diff options
Diffstat (limited to 'drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c')
-rw-r--r-- | drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 334 |
1 files changed, 219 insertions, 115 deletions
diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index 5f177ea80725..21aaa2730ac8 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -45,8 +45,8 @@ #define MII_XGMAC_PA_SHIFT 16 #define MII_XGMAC_DA_SHIFT 21 -static int stmmac_xgmac2_c45_format(struct stmmac_priv *priv, int phyaddr, - int phyreg, u32 *hw_addr) +static void stmmac_xgmac2_c45_format(struct stmmac_priv *priv, int phyaddr, + int devad, int phyreg, u32 *hw_addr) { u32 tmp; @@ -56,19 +56,14 @@ static int stmmac_xgmac2_c45_format(struct stmmac_priv *priv, int phyaddr, writel(tmp, priv->ioaddr + XGMAC_MDIO_C22P); *hw_addr = (phyaddr << MII_XGMAC_PA_SHIFT) | (phyreg & 0xffff); - *hw_addr |= (phyreg >> MII_DEVADDR_C45_SHIFT) << MII_XGMAC_DA_SHIFT; - return 0; + *hw_addr |= devad << MII_XGMAC_DA_SHIFT; } -static int stmmac_xgmac2_c22_format(struct stmmac_priv *priv, int phyaddr, - int phyreg, u32 *hw_addr) +static void stmmac_xgmac2_c22_format(struct stmmac_priv *priv, int phyaddr, + int phyreg, u32 *hw_addr) { u32 tmp; - /* HW does not support C22 addr >= 4 */ - if (phyaddr > MII_XGMAC_MAX_C22ADDR) - return -ENODEV; - /* Set port as Clause 22 */ tmp = readl(priv->ioaddr + XGMAC_MDIO_C22P); tmp &= ~MII_XGMAC_C22P_MASK; @@ -76,16 +71,14 @@ static int stmmac_xgmac2_c22_format(struct stmmac_priv *priv, int phyaddr, writel(tmp, priv->ioaddr + XGMAC_MDIO_C22P); *hw_addr = (phyaddr << MII_XGMAC_PA_SHIFT) | (phyreg & 0x1f); - return 0; } -static int stmmac_xgmac2_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg) +static int stmmac_xgmac2_mdio_read(struct stmmac_priv *priv, u32 addr, + u32 value) { - struct net_device *ndev = bus->priv; - struct stmmac_priv *priv = netdev_priv(ndev); unsigned int mii_address = priv->hw->mii.addr; unsigned int mii_data = priv->hw->mii.data; - u32 tmp, addr, value = MII_XGMAC_BUSY; + u32 tmp; int ret; ret = pm_runtime_resume_and_get(priv->device); @@ -99,20 +92,6 @@ static int stmmac_xgmac2_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg) goto err_disable_clks; } - if (phyreg & MII_ADDR_C45) { - phyreg &= ~MII_ADDR_C45; - - ret = stmmac_xgmac2_c45_format(priv, phyaddr, phyreg, &addr); - if (ret) - goto err_disable_clks; - } else { - ret = stmmac_xgmac2_c22_format(priv, phyaddr, phyreg, &addr); - if (ret) - goto err_disable_clks; - - value |= MII_XGMAC_SADDR; - } - value |= (priv->clk_csr << priv->hw->mii.clk_csr_shift) & priv->hw->mii.clk_csr_mask; value |= MII_XGMAC_READ; @@ -144,14 +123,44 @@ err_disable_clks: return ret; } -static int stmmac_xgmac2_mdio_write(struct mii_bus *bus, int phyaddr, - int phyreg, u16 phydata) +static int stmmac_xgmac2_mdio_read_c22(struct mii_bus *bus, int phyaddr, + int phyreg) { struct net_device *ndev = bus->priv; - struct stmmac_priv *priv = netdev_priv(ndev); + struct stmmac_priv *priv; + u32 addr; + + priv = netdev_priv(ndev); + + /* HW does not support C22 addr >= 4 */ + if (phyaddr > MII_XGMAC_MAX_C22ADDR) + return -ENODEV; + + stmmac_xgmac2_c22_format(priv, phyaddr, phyreg, &addr); + + return stmmac_xgmac2_mdio_read(priv, addr, MII_XGMAC_BUSY); +} + +static int stmmac_xgmac2_mdio_read_c45(struct mii_bus *bus, int phyaddr, + int devad, int phyreg) +{ + struct net_device *ndev = bus->priv; + struct stmmac_priv *priv; + u32 addr; + + priv = netdev_priv(ndev); + + stmmac_xgmac2_c45_format(priv, phyaddr, devad, phyreg, &addr); + + return stmmac_xgmac2_mdio_read(priv, addr, MII_XGMAC_BUSY); +} + +static int stmmac_xgmac2_mdio_write(struct stmmac_priv *priv, u32 addr, + u32 value, u16 phydata) +{ unsigned int mii_address = priv->hw->mii.addr; unsigned int mii_data = priv->hw->mii.data; - u32 addr, tmp, value = MII_XGMAC_BUSY; + u32 tmp; int ret; ret = pm_runtime_resume_and_get(priv->device); @@ -165,20 +174,6 @@ static int stmmac_xgmac2_mdio_write(struct mii_bus *bus, int phyaddr, goto err_disable_clks; } - if (phyreg & MII_ADDR_C45) { - phyreg &= ~MII_ADDR_C45; - - ret = stmmac_xgmac2_c45_format(priv, phyaddr, phyreg, &addr); - if (ret) - goto err_disable_clks; - } else { - ret = stmmac_xgmac2_c22_format(priv, phyaddr, phyreg, &addr); - if (ret) - goto err_disable_clks; - - value |= MII_XGMAC_SADDR; - } - value |= (priv->clk_csr << priv->hw->mii.clk_csr_shift) & priv->hw->mii.clk_csr_mask; value |= phydata; @@ -205,8 +200,63 @@ err_disable_clks: return ret; } +static int stmmac_xgmac2_mdio_write_c22(struct mii_bus *bus, int phyaddr, + int phyreg, u16 phydata) +{ + struct net_device *ndev = bus->priv; + struct stmmac_priv *priv; + u32 addr; + + priv = netdev_priv(ndev); + + /* HW does not support C22 addr >= 4 */ + if (phyaddr > MII_XGMAC_MAX_C22ADDR) + return -ENODEV; + + stmmac_xgmac2_c22_format(priv, phyaddr, phyreg, &addr); + + return stmmac_xgmac2_mdio_write(priv, addr, + MII_XGMAC_BUSY | MII_XGMAC_SADDR, phydata); +} + +static int stmmac_xgmac2_mdio_write_c45(struct mii_bus *bus, int phyaddr, + int devad, int phyreg, u16 phydata) +{ + struct net_device *ndev = bus->priv; + struct stmmac_priv *priv; + u32 addr; + + priv = netdev_priv(ndev); + + stmmac_xgmac2_c45_format(priv, phyaddr, devad, phyreg, &addr); + + return stmmac_xgmac2_mdio_write(priv, addr, MII_XGMAC_BUSY, + phydata); +} + +static int stmmac_mdio_read(struct stmmac_priv *priv, int data, u32 value) +{ + unsigned int mii_address = priv->hw->mii.addr; + unsigned int mii_data = priv->hw->mii.data; + u32 v; + + if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY), + 100, 10000)) + return -EBUSY; + + writel(data, priv->ioaddr + mii_data); + writel(value, priv->ioaddr + mii_address); + + if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY), + 100, 10000)) + return -EBUSY; + + /* Read the data from the MII data register */ + return readl(priv->ioaddr + mii_data) & MII_DATA_MASK; +} + /** - * stmmac_mdio_read + * stmmac_mdio_read_c22 * @bus: points to the mii_bus structure * @phyaddr: MII addr * @phyreg: MII reg @@ -215,15 +265,12 @@ err_disable_clks: * accessing the PHY registers. * Fortunately, it seems this has no drawback for the 7109 MAC. */ -static int stmmac_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg) +static int stmmac_mdio_read_c22(struct mii_bus *bus, int phyaddr, int phyreg) { struct net_device *ndev = bus->priv; struct stmmac_priv *priv = netdev_priv(ndev); - unsigned int mii_address = priv->hw->mii.addr; - unsigned int mii_data = priv->hw->mii.data; u32 value = MII_BUSY; int data = 0; - u32 v; data = pm_runtime_resume_and_get(priv->device); if (data < 0) @@ -236,60 +283,94 @@ static int stmmac_mdio_read(struct mii_bus *bus, int phyaddr, int phyreg) & priv->hw->mii.clk_csr_mask; if (priv->plat->has_gmac4) { value |= MII_GMAC4_READ; - if (phyreg & MII_ADDR_C45) { - value |= MII_GMAC4_C45E; - value &= ~priv->hw->mii.reg_mask; - value |= ((phyreg >> MII_DEVADDR_C45_SHIFT) << - priv->hw->mii.reg_shift) & - priv->hw->mii.reg_mask; - - data |= (phyreg & MII_REGADDR_C45_MASK) << - MII_GMAC4_REG_ADDR_SHIFT; - } } - if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY), - 100, 10000)) { - data = -EBUSY; - goto err_disable_clks; - } + data = stmmac_mdio_read(priv, data, value); - writel(data, priv->ioaddr + mii_data); - writel(value, priv->ioaddr + mii_address); + pm_runtime_put(priv->device); - if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY), - 100, 10000)) { - data = -EBUSY; - goto err_disable_clks; + return data; +} + +/** + * stmmac_mdio_read_c45 + * @bus: points to the mii_bus structure + * @phyaddr: MII addr + * @devad: device address to read + * @phyreg: MII reg + * Description: it reads data from the MII register from within the phy device. + * For the 7111 GMAC, we must set the bit 0 in the MII address register while + * accessing the PHY registers. + * Fortunately, it seems this has no drawback for the 7109 MAC. + */ +static int stmmac_mdio_read_c45(struct mii_bus *bus, int phyaddr, int devad, + int phyreg) +{ + struct net_device *ndev = bus->priv; + struct stmmac_priv *priv = netdev_priv(ndev); + u32 value = MII_BUSY; + int data = 0; + + data = pm_runtime_get_sync(priv->device); + if (data < 0) { + pm_runtime_put_noidle(priv->device); + return data; } - /* Read the data from the MII data register */ - data = (int)readl(priv->ioaddr + mii_data) & MII_DATA_MASK; + value |= (phyaddr << priv->hw->mii.addr_shift) + & priv->hw->mii.addr_mask; + value |= (phyreg << priv->hw->mii.reg_shift) & priv->hw->mii.reg_mask; + value |= (priv->clk_csr << priv->hw->mii.clk_csr_shift) + & priv->hw->mii.clk_csr_mask; + value |= MII_GMAC4_READ; + value |= MII_GMAC4_C45E; + value &= ~priv->hw->mii.reg_mask; + value |= (devad << priv->hw->mii.reg_shift) & priv->hw->mii.reg_mask; + + data |= phyreg << MII_GMAC4_REG_ADDR_SHIFT; + + data = stmmac_mdio_read(priv, data, value); -err_disable_clks: pm_runtime_put(priv->device); return data; } +static int stmmac_mdio_write(struct stmmac_priv *priv, int data, u32 value) +{ + unsigned int mii_address = priv->hw->mii.addr; + unsigned int mii_data = priv->hw->mii.data; + u32 v; + + /* Wait until any existing MII operation is complete */ + if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY), + 100, 10000)) + return -EBUSY; + + /* Set the MII address register to write */ + writel(data, priv->ioaddr + mii_data); + writel(value, priv->ioaddr + mii_address); + + /* Wait until any existing MII operation is complete */ + return readl_poll_timeout(priv->ioaddr + mii_address, v, + !(v & MII_BUSY), 100, 10000); +} + /** - * stmmac_mdio_write + * stmmac_mdio_write_c22 * @bus: points to the mii_bus structure * @phyaddr: MII addr * @phyreg: MII reg * @phydata: phy data * Description: it writes the data into the MII register from within the device. */ -static int stmmac_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg, - u16 phydata) +static int stmmac_mdio_write_c22(struct mii_bus *bus, int phyaddr, int phyreg, + u16 phydata) { struct net_device *ndev = bus->priv; struct stmmac_priv *priv = netdev_priv(ndev); - unsigned int mii_address = priv->hw->mii.addr; - unsigned int mii_data = priv->hw->mii.data; int ret, data = phydata; u32 value = MII_BUSY; - u32 v; ret = pm_runtime_resume_and_get(priv->device); if (ret < 0) @@ -301,38 +382,57 @@ static int stmmac_mdio_write(struct mii_bus *bus, int phyaddr, int phyreg, value |= (priv->clk_csr << priv->hw->mii.clk_csr_shift) & priv->hw->mii.clk_csr_mask; - if (priv->plat->has_gmac4) { + if (priv->plat->has_gmac4) value |= MII_GMAC4_WRITE; - if (phyreg & MII_ADDR_C45) { - value |= MII_GMAC4_C45E; - value &= ~priv->hw->mii.reg_mask; - value |= ((phyreg >> MII_DEVADDR_C45_SHIFT) << - priv->hw->mii.reg_shift) & - priv->hw->mii.reg_mask; - - data |= (phyreg & MII_REGADDR_C45_MASK) << - MII_GMAC4_REG_ADDR_SHIFT; - } - } else { + else value |= MII_WRITE; - } - /* Wait until any existing MII operation is complete */ - if (readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY), - 100, 10000)) { - ret = -EBUSY; - goto err_disable_clks; + ret = stmmac_mdio_write(priv, data, value); + + pm_runtime_put(priv->device); + + return ret; +} + +/** + * stmmac_mdio_write_c45 + * @bus: points to the mii_bus structure + * @phyaddr: MII addr + * @phyreg: MII reg + * @devad: device address to read + * @phydata: phy data + * Description: it writes the data into the MII register from within the device. + */ +static int stmmac_mdio_write_c45(struct mii_bus *bus, int phyaddr, + int devad, int phyreg, u16 phydata) +{ + struct net_device *ndev = bus->priv; + struct stmmac_priv *priv = netdev_priv(ndev); + int ret, data = phydata; + u32 value = MII_BUSY; + + ret = pm_runtime_get_sync(priv->device); + if (ret < 0) { + pm_runtime_put_noidle(priv->device); + return ret; } - /* Set the MII address register to write */ - writel(data, priv->ioaddr + mii_data); - writel(value, priv->ioaddr + mii_address); + value |= (phyaddr << priv->hw->mii.addr_shift) + & priv->hw->mii.addr_mask; + value |= (phyreg << priv->hw->mii.reg_shift) & priv->hw->mii.reg_mask; - /* Wait until any existing MII operation is complete */ - ret = readl_poll_timeout(priv->ioaddr + mii_address, v, !(v & MII_BUSY), - 100, 10000); + value |= (priv->clk_csr << priv->hw->mii.clk_csr_shift) + & priv->hw->mii.clk_csr_mask; + + value |= MII_GMAC4_WRITE; + value |= MII_GMAC4_C45E; + value &= ~priv->hw->mii.reg_mask; + value |= (devad << priv->hw->mii.reg_shift) & priv->hw->mii.reg_mask; + + data |= phyreg << MII_GMAC4_REG_ADDR_SHIFT; + + ret = stmmac_mdio_write(priv, data, value); -err_disable_clks: pm_runtime_put(priv->device); return ret; @@ -453,12 +553,11 @@ int stmmac_mdio_register(struct net_device *ndev) new_bus->name = "stmmac"; - if (priv->plat->has_gmac4) - new_bus->probe_capabilities = MDIOBUS_C22_C45; - if (priv->plat->has_xgmac) { - new_bus->read = &stmmac_xgmac2_mdio_read; - new_bus->write = &stmmac_xgmac2_mdio_write; + new_bus->read = &stmmac_xgmac2_mdio_read_c22; + new_bus->write = &stmmac_xgmac2_mdio_write_c22; + new_bus->read_c45 = &stmmac_xgmac2_mdio_read_c45; + new_bus->write_c45 = &stmmac_xgmac2_mdio_write_c45; /* Right now only C22 phys are supported */ max_addr = MII_XGMAC_MAX_C22ADDR + 1; @@ -468,8 +567,13 @@ int stmmac_mdio_register(struct net_device *ndev) dev_err(dev, "Unsupported phy_addr (max=%d)\n", MII_XGMAC_MAX_C22ADDR); } else { - new_bus->read = &stmmac_mdio_read; - new_bus->write = &stmmac_mdio_write; + new_bus->read = &stmmac_mdio_read_c22; + new_bus->write = &stmmac_mdio_write_c22; + if (priv->plat->has_gmac4) { + new_bus->read_c45 = &stmmac_mdio_read_c45; + new_bus->write_c45 = &stmmac_mdio_write_c45; + } + max_addr = PHY_MAX_ADDR; } @@ -490,7 +594,7 @@ int stmmac_mdio_register(struct net_device *ndev) /* Looks like we need a dummy read for XGMAC only and C45 PHYs */ if (priv->plat->has_xgmac) - stmmac_xgmac2_mdio_read(new_bus, 0, MII_ADDR_C45); + stmmac_xgmac2_mdio_read_c45(new_bus, 0, 0, 0); /* If fixed-link is set, skip PHY scanning */ if (!fwnode) |