Thread (9 messages) flat view 9 messages, 2 authors, 8h ago
HOTtoday

[PATCH net-next 2/3] net: phy: restore the interrupt after a generic-driver bind cycle

From: Aleksei Sviridkin <hidden>
Date: 2026-08-22 15:53:12
Also in: lkml
Subsystem: ethernet phy library, networking drivers, the rest · Maintainers: Andrew Lunn, Heiner Kallweit, Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds

fwnode_mdiobus_phy_device_register() resolves the interrupt declared
for a PHY once, at MDIO bus registration. If no specific driver is
available when the PHY is attached, the generic driver binds and
phy_probe() parks the device in polling mode, since the generic driver
has no interrupt callbacks. phy_detach() releases the generic driver
so a specific driver can bind later, but nothing brings the interrupt
back: the firmware node is never re-read after bus registration, so
the specific driver attaches with irq == PHY_POLL,
phy_request_interrupt() is never reached, and the PHY is polled for
the rest of the uptime with nothing in the logs but the "irq=POLL"
attach line.

A DSA switch probing before the rootfs is mounted produces exactly
that cycle for a PHY whose driver is a module: the generic driver
binds and fails validation during switch setup, and the real driver
binds at ifup. Observed on an MT7981B board with an Airoha EN8811H on
an MT7531 port: the device tree declares the INT_B line, yet the
attach says irq=POLL and the interrupt is never claimed.

Save the interrupt when the generic driver binds and give it back when
that driver is released. The restore runs before the device becomes
bindable again, so a concurrently arriving specific driver cannot
observe or overwrite the intermediate state. Only the value the
generic-driver cycle took is restored. A PHY already parked in polling
mode before that cycle, by a failed phy_request_interrupt() or by a
driver that chose PHY_POLL in its own probe, had PHY_POLL saved, so the
restore is skipped. The PHY_F_NO_IRQ and no-interrupt-support checks in
phy_attach_direct() still apply to whichever driver binds next.

Signed-off-by: Aleksei Sviridkin <redacted>
---
Testing

MT7981B board, mt7530 switch, Airoha EN8811H whose INT_B line is in
the device tree, driver in a module on the rootfs, together with the
next patch: the attach line reports irq=15 instead of irq=POLL, the
EINT is claimed, its counter advances on link changes forced from the
link partner, and there is no interrupt storm. The SoC's internal PHY
on the same board, which has no interrupt in its bus table, keeps
irq=POLL through the same boot, so the save-restore pair does not
resurrect an interrupt the device never had. Consistent across
reboots. Hardware testing was done on 6.18 with this exact
shape of the change; on net-next the files are compile-tested.

The saved value uses zero as "nothing saved"; no registration path
produces a valid interrupt number of zero, and non-positive values are
never restored.
 drivers/net/phy/phy_device.c | 13 +++++++++++++
 include/linux/phy.h          |  6 ++++++
 2 files changed, 19 insertions(+)
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 94b2e85e0..6047dce61 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -1780,6 +1780,7 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 		else
 			d->driver = &genphy_driver.mdiodrv.driver;
 
+		phydev->genphy_saved_irq = phydev->irq;
 		phydev->is_genphy_driven = 1;
 	}
 
@@ -1897,6 +1898,9 @@ int phy_attach_direct(struct net_device *dev, struct phy_device *phydev,
 error_module_put:
 	module_put(d->driver->owner);
 	phydev->is_genphy_driven = 0;
+	if (phydev->genphy_saved_irq > 0 && phydev->irq == PHY_POLL)
+		phydev->irq = phydev->genphy_saved_irq;
+	phydev->genphy_saved_irq = 0;
 	d->driver = NULL;
 error_put_device:
 	put_device(d);
@@ -1965,6 +1969,15 @@ void phy_detach(struct phy_device *phydev)
 	 * real driver could be loaded
 	 */
 	if (phydev->is_genphy_driven) {
+		/* Give back the interrupt phy_probe() parked when the generic
+		 * driver bound, before the device becomes bindable again. A
+		 * PHY that was in polling mode for any other reason had
+		 * PHY_POLL saved, and the restore is skipped.
+		 */
+		if (phydev->genphy_saved_irq > 0 && phydev->irq == PHY_POLL)
+			phydev->irq = phydev->genphy_saved_irq;
+		phydev->genphy_saved_irq = 0;
+
 		device_release_driver(&phydev->mdio.dev);
 		phydev->is_genphy_driven = 0;
 	}
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 5f8d65868..43e20b19e 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -591,6 +591,10 @@ struct phy_oatc14_sqi_capability {
  *      - Bits [31:24] are reserved for defining generic
  *        PHY driver behavior.
  * @irq: IRQ number of the PHY's interrupt (-1 if none)
+ * @genphy_saved_irq: value of @irq before the generic driver bound, given
+ *                    back when that driver is released; zero outside a
+ *                    generic bind cycle, and non-positive values are
+ *                    never restored
  * @phylink: Pointer to phylink instance for this PHY
  * @sfp_bus_attached: Flag indicating whether the SFP bus has been attached
  * @sfp_bus: SFP bus attached to this PHY's fiber port
@@ -762,6 +766,8 @@ struct phy_device {
 	 */
 	int irq;
 
+	int genphy_saved_irq;
+
 	/* private data pointer */
 	/* For use by PHYs to maintain extra state */
 	void *priv;
-- 
2.43.0
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help