]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
net: phy: add a PHY write barrier when disabling interrupts
authorCharles Perry <charles.perry@microchip.com>
Wed, 8 Apr 2026 13:18:16 +0000 (06:18 -0700)
committerJakub Kicinski <kuba@kernel.org>
Sun, 12 Apr 2026 21:19:19 +0000 (14:19 -0700)
MDIO bus controllers are not required to wait for write transactions to
complete before returning as synchronization is often achieved by polling
status bits.

This can cause issues when disabling interrupts since an interrupt could
fire before the interrupt handler is unregistered and there's no status
bit to poll.

Add a phy_write_barrier() function and use it in phy_disable_interrupts()
to fix this issue. The write barrier just reads an MII register and
discards the value, which is enough to guarantee that previous writes have
completed.

Signed-off-by: Charles Perry <charles.perry@microchip.com>
Link: https://patch.msgid.link/20260408131821.1145334-4-charles.perry@microchip.com
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
drivers/net/phy/phy.c

index 1f1039084f514a1b47254a0a68a665624dac45b7..fce9bc7be3304b408c9f3ec9b5b95c76d3680a88 100644 (file)
@@ -1368,14 +1368,37 @@ void phy_error(struct phy_device *phydev)
 }
 EXPORT_SYMBOL(phy_error);
 
+/**
+ * phy_write_barrier - ensure the last write completed for this PHY device
+ * @phydev: target phy_device struct
+ *
+ * MDIO bus controllers are not required to wait for write transactions to
+ * complete before returning. Calling this function ensures that the previous
+ * write has completed.
+ */
+static void phy_write_barrier(struct phy_device *phydev)
+{
+       if (mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, MII_PHYSID1) ==
+           -EOPNOTSUPP)
+               mdiobus_c45_read(phydev->mdio.bus, phydev->mdio.addr,
+                                MDIO_MMD_PMAPMD, MII_PHYSID1);
+}
+
 /**
  * phy_disable_interrupts - Disable the PHY interrupts from the PHY side
  * @phydev: target phy_device struct
  */
 int phy_disable_interrupts(struct phy_device *phydev)
 {
+       int err;
+
        /* Disable PHY interrupts */
-       return phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED);
+       err = phy_config_interrupt(phydev, PHY_INTERRUPT_DISABLED);
+       if (err)
+               return err;
+
+       phy_write_barrier(phydev);
+       return 0;
 }
 
 /**