123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- // SPDX-License-Identifier: GPL-2.0+
- /*
- * QE UEC ethernet phy controller driver
- *
- * based on phy parts of drivers/qe/uec.c and drivers/qe/uec_phy.c
- * from NXP
- *
- * Copyright (C) 2020 Heiko Schocher <hs@denx.de>
- */
- #include <common.h>
- #include <dm.h>
- #include <errno.h>
- #include <miiphy.h>
- #include <phy.h>
- #include <asm/io.h>
- #include <linux/ioport.h>
- #include "dm_qe_uec.h"
- struct qe_uec_mdio_priv {
- struct ucc_mii_mng *base;
- };
- static int
- qe_uec_mdio_read(struct udevice *dev, int addr, int devad, int reg)
- {
- struct qe_uec_mdio_priv *priv = dev_get_priv(dev);
- struct ucc_mii_mng *regs = priv->base;
- u32 tmp_reg;
- u16 value;
- debug("%s: regs: %p addr: %x devad: %x reg: %x\n", __func__, regs,
- addr, devad, reg);
- /* Setting up the MII management Address Register */
- tmp_reg = ((u32)addr << MIIMADD_PHY_ADDRESS_SHIFT) | reg;
- out_be32(®s->miimadd, tmp_reg);
- /* clear MII management command cycle */
- out_be32(®s->miimcom, 0);
- sync();
- /* Perform an MII management read cycle */
- out_be32(®s->miimcom, MIIMCOM_READ_CYCLE);
- /* Wait till MII management write is complete */
- while ((in_be32(®s->miimind)) &
- (MIIMIND_NOT_VALID | MIIMIND_BUSY))
- ;
- /* Read MII management status */
- value = (u16)in_be32(®s->miimstat);
- if (value == 0xffff)
- return -EINVAL;
- return value;
- };
- static int
- qe_uec_mdio_write(struct udevice *dev, int addr, int devad, int reg,
- u16 value)
- {
- struct qe_uec_mdio_priv *priv = dev_get_priv(dev);
- struct ucc_mii_mng *regs = priv->base;
- u32 tmp_reg;
- debug("%s: regs: %p addr: %x devad: %x reg: %x val: %x\n", __func__,
- regs, addr, devad, reg, value);
- /* Stop the MII management read cycle */
- out_be32(®s->miimcom, 0);
- /* Setting up the MII management Address Register */
- tmp_reg = ((u32)addr << MIIMADD_PHY_ADDRESS_SHIFT) | reg;
- out_be32(®s->miimadd, tmp_reg);
- /* Setting up the MII management Control Register with the value */
- out_be32(®s->miimcon, (u32)value);
- sync();
- /* Wait till MII management write is complete */
- while ((in_be32(®s->miimind)) & MIIMIND_BUSY)
- ;
- return 0;
- };
- static const struct mdio_ops qe_uec_mdio_ops = {
- .read = qe_uec_mdio_read,
- .write = qe_uec_mdio_write,
- };
- static int qe_uec_mdio_probe(struct udevice *dev)
- {
- struct qe_uec_mdio_priv *priv = dev_get_priv(dev);
- fdt_size_t base;
- ofnode node;
- u32 num = 0;
- int ret = -ENODEV;
- priv->base = (struct ucc_mii_mng *)dev_read_addr(dev);
- base = (fdt_size_t)priv->base;
- /*
- * idea from linux:
- * drivers/net/ethernet/freescale/fsl_pq_mdio.c
- *
- * Find the UCC node that controls the given MDIO node
- *
- * For some reason, the QE MDIO nodes are not children of the UCC
- * devices that control them. Therefore, we need to scan all UCC
- * nodes looking for the one that encompases the given MDIO node.
- * We do this by comparing physical addresses. The 'start' and
- * 'end' addresses of the MDIO node are passed, and the correct
- * UCC node will cover the entire address range.
- */
- node = ofnode_by_compatible(ofnode_null(), "ucc_geth");
- while (ofnode_valid(node)) {
- fdt_size_t size;
- fdt_addr_t addr;
- addr = ofnode_get_addr_index(node, 0);
- ret = ofnode_get_addr_size_index(node, 0, &size);
- if (addr == FDT_ADDR_T_NONE) {
- node = ofnode_by_compatible(node, "ucc_geth");
- continue;
- }
- /* check if priv->base in start end */
- if (base > addr && base < (addr + size)) {
- ret = ofnode_read_u32(node, "cell-index", &num);
- if (ret)
- ret = ofnode_read_u32(node, "device-id",
- &num);
- break;
- }
- node = ofnode_by_compatible(node, "ucc_geth");
- }
- if (ret) {
- printf("%s: no cell-index nor device-id found!", __func__);
- return ret;
- }
- /* Setup MII master clock source */
- qe_set_mii_clk_src(num - 1);
- return 0;
- }
- static const struct udevice_id qe_uec_mdio_ids[] = {
- { .compatible = "fsl,ucc-mdio" },
- { }
- };
- U_BOOT_DRIVER(mvmdio) = {
- .name = "qe_uec_mdio",
- .id = UCLASS_MDIO,
- .of_match = qe_uec_mdio_ids,
- .probe = qe_uec_mdio_probe,
- .ops = &qe_uec_mdio_ops,
- .priv_auto_alloc_size = sizeof(struct qe_uec_mdio_priv),
- };
|