From 8329ee73e00c6788759f430d275b7cd7f4542091 Mon Sep 17 00:00:00 2001 From: Luo Jie Date: Wed, 22 Mar 2023 18:28:55 +0800 Subject: [PATCH 200/281] net: mdio-ipq4019: Support PHY address program The qca8386 embedded PHY address supports to be edited, the PHY address configuration register can be specified according to DTS property below. phyaddr_fixup = <0xC90F018>; uniphyaddr_fixup = <0xC90F014>; Change-Id: Iad5eaac8b667bb0f09d6a90b569df19813be7219 Signed-off-by: Luo Jie --- drivers/net/mdio/mdio-ipq4019.c | 126 +++++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 1 deletion(-) diff --git a/drivers/net/mdio/mdio-ipq4019.c b/drivers/net/mdio/mdio-ipq4019.c index 7e78b002c87b..2a9869151cb9 100644 --- a/drivers/net/mdio/mdio-ipq4019.c +++ b/drivers/net/mdio/mdio-ipq4019.c @@ -40,6 +40,10 @@ #define IPQ_HIGH_ADDR_PREFIX 0x18 #define IPQ_LOW_ADDR_PREFIX 0x10 +#define PHY_ADDR_LENGTH 5 +#define PHY_ADDR_NUM 4 +#define UNIPHY_ADDR_NUM 3 + struct ipq4019_mdio_data { void __iomem *membase; void __iomem *eth_ldo_rdy; @@ -235,6 +239,122 @@ int ipq_mii_write(struct mii_bus *bus, unsigned int reg, unsigned int val) return 0; }; +static void ipq_phy_addr_fixup(struct mii_bus *bus, struct device_node *np) +{ + void __iomem *ephy_cfg_base; + struct device_node *child; + int phy_index, addr, len; + const __be32 *phy_cfg, *uniphy_cfg; + u32 val; + bool mdio_access = false; + unsigned long phyaddr_mask = 0; + + phy_cfg = of_get_property(np, "phyaddr_fixup", &len); + uniphy_cfg = of_get_property(np, "uniphyaddr_fixup", NULL); + + /* + * For MDIO access, phyaddr_fixup only provides the register address, + * as for local bus, the register length also needs to be provided + */ + if(!phy_cfg || (len != (2 * sizeof(__be32)) && len != sizeof(__be32))) + return; + + if (len == sizeof(__be32)) + mdio_access = true; + + if (!mdio_access) { + ephy_cfg_base = ioremap(be32_to_cpup(phy_cfg), be32_to_cpup(phy_cfg + 1)); + if (!ephy_cfg_base) + return; + val = readl(ephy_cfg_base); + } else + val = ipq_mii_read(bus, be32_to_cpup(phy_cfg)); + + phy_index = 0; + addr = 0; + for_each_available_child_of_node(np, child) { + if (phy_index >= PHY_ADDR_NUM) + break; + + addr = of_mdio_parse_addr(&bus->dev, child); + if (addr < 0) { + continue; + } + phyaddr_mask |= BIT(addr); + + if (!of_find_property(child, "fixup", NULL)) + continue; + + addr &= GENMASK(4, 0); + val &= ~(GENMASK(4, 0) << (phy_index * PHY_ADDR_LENGTH)); + val |= addr << (phy_index * PHY_ADDR_LENGTH); + phy_index++; + } + + /* Programe the PHY address */ + dev_info(bus->parent, "Program EPHY reg 0x%x with 0x%x\n", + be32_to_cpup(phy_cfg), val); + + if (!mdio_access) { + writel(val, ephy_cfg_base); + iounmap(ephy_cfg_base); + } else { + ipq_mii_write(bus, be32_to_cpup(phy_cfg), val); + + /* Programe the UNIPHY address if uniphyaddr_fixup specified. + * the UNIPHY address will select three MDIO address from + * unoccupied MDIO address space. */ + if (uniphy_cfg) { + val = ipq_mii_read(bus, be32_to_cpup(uniphy_cfg)); + + /* For qca8386, the switch occupies the other 16 MDIO address, + * for example, if the phy address is in the range of 0 to 15, + * the switch will occupy the MDIO address from 16 to 31. */ + if (addr > 15) + phyaddr_mask |= GENMASK(15, 0); + else + phyaddr_mask |= GENMASK(31, 16); + + phy_index = 0; + for_each_clear_bit_from(addr, &phyaddr_mask, PHY_MAX_ADDR) { + if (phy_index >= UNIPHY_ADDR_NUM) + break; + + val &= ~(GENMASK(4, 0) << (phy_index * PHY_ADDR_LENGTH)); + val |= addr << (phy_index * PHY_ADDR_LENGTH); + phy_index++; + } + + if (phy_index < UNIPHY_ADDR_NUM) { + for_each_clear_bit(addr, &phyaddr_mask, PHY_MAX_ADDR) { + if (phy_index >= UNIPHY_ADDR_NUM) + break; + + val &= ~(GENMASK(4, 0) << (phy_index * PHY_ADDR_LENGTH)); + val |= addr << (phy_index * PHY_ADDR_LENGTH); + phy_index++; + } + } + + dev_info(bus->parent, "Program UNIPHY reg 0x%x with 0x%x\n", + be32_to_cpup(uniphy_cfg), val); + + ipq_mii_write(bus, be32_to_cpup(uniphy_cfg), val); + } + } +} + +void ipq_mii_preinit(struct mii_bus *bus) +{ + struct device_node *np = bus->parent->of_node; + if (!np) + return; + + ipq_phy_addr_fixup(bus, np); + return; +} +EXPORT_SYMBOL_GPL(ipq_mii_preinit); + static int ipq_mdio_reset(struct mii_bus *bus) { struct ipq4019_mdio_data *priv = bus->priv; @@ -257,9 +377,13 @@ static int ipq_mdio_reset(struct mii_bus *bus) return ret; ret = clk_prepare_enable(priv->mdio_clk); - if (ret == 0) + if (ret == 0) { mdelay(10); + /* Configure the fixup PHY address and clocks for qca8386 chip if specified */ + ipq_mii_preinit(bus); + } + return ret; } -- 2.17.1