Netdev List
 help / color / mirror / Atom feed
From: Aleksei Sviridkin <f@lex.la>
To: Andrew Lunn <andrew@lunn.ch>, Vladimir Oltean <olteanv@gmail.com>,
	Heiner Kallweit <hkallweit1@gmail.com>,
	Russell King <linux@armlinux.org.uk>
Cc: "David S . Miller" <davem@davemloft.net>,
	Eric Dumazet <edumazet@google.com>,
	Jakub Kicinski <kuba@kernel.org>, Paolo Abeni <pabeni@redhat.com>,
	Simon Horman <horms@kernel.org>,
	netdev@vger.kernel.org, linux-kernel@vger.kernel.org,
	Aleksei Sviridkin <f@lex.la>
Subject: [PATCH net-next 2/3] net: phy: restore the interrupt after a generic-driver bind cycle
Date: Sat, 22 Aug 2026 18:52:58 +0300	[thread overview]
Message-ID: <20260822155259.87146-3-f@lex.la> (raw)
In-Reply-To: <20260822155259.87146-1-f@lex.la>

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 <f@lex.la>
---
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


  parent reply	other threads:[~2026-08-22 15:53 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-22 15:52 [PATCH net-next 0/3] net: survive a PHY whose driver arrives after the switch probes Aleksei Sviridkin
2026-08-22 15:52 ` [PATCH net-next 1/3] net: phylink: unwind the PHY binding when bringup fails late Aleksei Sviridkin
2026-08-22 17:30   ` Andrew Lunn
2026-08-22 15:52 ` Aleksei Sviridkin [this message]
2026-08-22 19:28   ` [PATCH net-next 2/3] net: phy: restore the interrupt after a generic-driver bind cycle Andrew Lunn
     [not found] ` <20260822155259.87146-4-f@lex.la>
2026-08-22 19:38   ` [PATCH net-next 3/3] net: dsa: connect a late-arriving PHY at ifup Andrew Lunn
2026-08-23  0:05     ` Aleksei Sviridkin
2026-08-23  1:24       ` Andrew Lunn
2026-08-23 12:37         ` Aleksei Sviridkin
2026-08-23 15:20           ` Andrew Lunn
2026-08-24  2:40         ` Aleksei Sviridkin
2026-08-24 16:25 ` [PATCH net-next 0/3] net: survive a PHY whose driver arrives after the switch probes Andrew Lunn
2026-08-25  8:25   ` Aleksei Sviridkin
2026-08-28 13:30     ` Andrew Lunn

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260822155259.87146-3-f@lex.la \
    --to=f@lex.la \
    --cc=andrew@lunn.ch \
    --cc=davem@davemloft.net \
    --cc=edumazet@google.com \
    --cc=hkallweit1@gmail.com \
    --cc=horms@kernel.org \
    --cc=kuba@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux@armlinux.org.uk \
    --cc=netdev@vger.kernel.org \
    --cc=olteanv@gmail.com \
    --cc=pabeni@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox