summaryrefslogtreecommitdiff
path: root/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c')
-rw-r--r--drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c334
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)