net/fsl: xgmac_mdio: Add workaround for erratum A-009885
authorTobias Waldekranz <tobias@waldekranz.com>
Tue, 18 Jan 2022 21:50:50 +0000 (22:50 +0100)
committerJakub Kicinski <kuba@kernel.org>
Wed, 19 Jan 2022 16:14:17 +0000 (08:14 -0800)
Once an MDIO read transaction is initiated, we must read back the data
register within 16 MDC cycles after the transaction completes. Outside
of this window, reads may return corrupt data.

Therefore, disable local interrupts in the critical section, to
maximize the probability that we can satisfy this requirement.

Fixes: d55ad2967d89 ("powerpc/mpc85xx: Create dts components for the FSL QorIQ DPAA FMan")
Signed-off-by: Tobias Waldekranz <tobias@waldekranz.com>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/ethernet/freescale/xgmac_mdio.c

index 5b8b9bc..bf566ac 100644 (file)
@@ -51,6 +51,7 @@ struct tgec_mdio_controller {
 struct mdio_fsl_priv {
        struct  tgec_mdio_controller __iomem *mdio_base;
        bool    is_little_endian;
+       bool    has_a009885;
        bool    has_a011043;
 };
 
@@ -186,10 +187,10 @@ static int xgmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
 {
        struct mdio_fsl_priv *priv = (struct mdio_fsl_priv *)bus->priv;
        struct tgec_mdio_controller __iomem *regs = priv->mdio_base;
+       unsigned long flags;
        uint16_t dev_addr;
        uint32_t mdio_stat;
        uint32_t mdio_ctl;
-       uint16_t value;
        int ret;
        bool endian = priv->is_little_endian;
 
@@ -221,12 +222,18 @@ static int xgmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
                        return ret;
        }
 
+       if (priv->has_a009885)
+               /* Once the operation completes, i.e. MDIO_STAT_BSY clears, we
+                * must read back the data register within 16 MDC cycles.
+                */
+               local_irq_save(flags);
+
        /* Initiate the read */
        xgmac_write32(mdio_ctl | MDIO_CTL_READ, &regs->mdio_ctl, endian);
 
        ret = xgmac_wait_until_done(&bus->dev, regs, endian);
        if (ret)
-               return ret;
+               goto irq_restore;
 
        /* Return all Fs if nothing was there */
        if ((xgmac_read32(&regs->mdio_stat, endian) & MDIO_STAT_RD_ER) &&
@@ -234,13 +241,17 @@ static int xgmac_mdio_read(struct mii_bus *bus, int phy_id, int regnum)
                dev_dbg(&bus->dev,
                        "Error while reading PHY%d reg at %d.%hhu\n",
                        phy_id, dev_addr, regnum);
-               return 0xffff;
+               ret = 0xffff;
+       } else {
+               ret = xgmac_read32(&regs->mdio_data, endian) & 0xffff;
+               dev_dbg(&bus->dev, "read %04x\n", ret);
        }
 
-       value = xgmac_read32(&regs->mdio_data, endian) & 0xffff;
-       dev_dbg(&bus->dev, "read %04x\n", value);
+irq_restore:
+       if (priv->has_a009885)
+               local_irq_restore(flags);
 
-       return value;
+       return ret;
 }
 
 static int xgmac_mdio_probe(struct platform_device *pdev)
@@ -287,6 +298,8 @@ static int xgmac_mdio_probe(struct platform_device *pdev)
        priv->is_little_endian = device_property_read_bool(&pdev->dev,
                                                           "little-endian");
 
+       priv->has_a009885 = device_property_read_bool(&pdev->dev,
+                                                     "fsl,erratum-a009885");
        priv->has_a011043 = device_property_read_bool(&pdev->dev,
                                                      "fsl,erratum-a011043");