mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH net] net: phy: postpone PHY interrupts during sleep with MAC-managed PM
@ 2026-10-02  4:35 Igor Velkov
  2026-10-02  5:03 ` Igor Velkov
                   ` (4 more replies)
  0 siblings, 5 replies; 7+ messages in thread
From: Igor Velkov @ 2026-10-02  4:35 UTC (permalink / raw)
  To: Andrew Lunn, Heiner Kallweit
  Cc: Russell King, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Lukas Wunner, netdev, linux-kernel

mdio_bus_phy_suspend() and mdio_bus_phy_resume() return early when the
MAC driver manages PHY PM, so the PHY never gets irq_suspended. A PHY
interrupt that wakes the system fires as soon as resume_device_irqs()
re-enables the line, before the MAC driver resumes, and phy_interrupt()
runs the PHY driver's handler at once. If the MDIO bus was powered down
in suspend, the MDIO access in that handler stalls the CPU.

On Helios64 (dwmac-rk, RTL8211F with an interrupt line, Wake-on-LAN in
the PHY) the wake interrupt reads INSR while the GMAC clocks are off,
and the board hangs within a few suspend cycles.

Set irq_suspended for these PHYs too. The PHY device is a child of its
MDIO bus, so mdio_bus_phy_resume() runs after the bus is back (with
stmmac, after the MAC) and replays the postponed interrupt then.
phy_suspend(), phy_resume() and the state machine stay with the MAC
driver.

Drop a pending rerun when the interrupt is freed: MAC drivers that close
the interface in suspend free and request it again before the PHY
resumes.

Tested on Helios64 with 7.3-rc5, the GMAC powered down in suspend and
Wake-on-LAN in the PHY: 20 of 20 magic-packet wakes. Without it the
same setup hung within 1 to 7 cycles in 14 of 16 runs, some on
instrumented builds; one run passed 25 cycles. ODROID-HC4
(dwmac-meson8b, RTL8211F with an interrupt line): 10 of 10.

Fixes: 1758bde2e4aa ("net: phy: Don't trigger state machine while in suspend")
Assisted-by: LLM
Signed-off-by: Igor Velkov <iav@iav.lv>
---
This replaces the dwmac-rk change that kept the GMAC powered instead:
https://lore.kernel.org/r/20260930-dwmac-rk-phy-wol-v1-1-9fdc50bd9ae4@iav.lv

Build-tested on net: allmodconfig and allyesconfig with W=1, no new
warnings.

 drivers/net/phy/phy.c        |  2 ++
 drivers/net/phy/phy_device.c | 43 ++++++++++++++++++++++--------------
 2 files changed, 28 insertions(+), 17 deletions(-)

diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index fce9bc7be330..cd189ecee55e 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -1507,6 +1507,8 @@ EXPORT_SYMBOL(phy_request_interrupt);
 void phy_free_interrupt(struct phy_device *phydev)
 {
 	phy_disable_interrupts(phydev);
+	/* A freed interrupt has nothing left to rerun on resume. */
+	phydev->irq_rerun = 0;
 	free_irq(phydev->irq, phydev);
 }
 EXPORT_SYMBOL(phy_free_interrupt);
diff --git a/drivers/net/phy/phy_device.c b/drivers/net/phy/phy_device.c
index 94b2e85e00a3..948818964841 100644
--- a/drivers/net/phy/phy_device.c
+++ b/drivers/net/phy/phy_device.c
@@ -347,18 +347,19 @@ static __maybe_unused int mdio_bus_phy_suspend(struct device *dev)
 {
 	struct phy_device *phydev = to_phy_device(dev);
 
-	if (phydev->mac_managed_pm)
-		return 0;
-
 	/* Wakeup interrupts may occur during the system sleep transition when
 	 * the PHY is inaccessible. Set flag to postpone handling until the PHY
 	 * has resumed. Wait for concurrent interrupt handler to complete.
+	 * The MDIO bus may be powered down even when the MAC manages PHY PM.
 	 */
 	if (phy_interrupt_is_valid(phydev)) {
 		phydev->irq_suspended = 1;
 		synchronize_irq(phydev->irq);
 	}
 
+	if (phydev->mac_managed_pm)
+		return 0;
+
 	/* We must stop the state machine manually, otherwise it stops out of
 	 * control, possibly with the phydev->lock held. Upon resume, netdev
 	 * may call phy routines that try to grab the same lock, and that may
@@ -375,13 +376,33 @@ static __maybe_unused int mdio_bus_phy_suspend(struct device *dev)
 	return phy_suspend(phydev);
 }
 
+static __maybe_unused void mdio_bus_phy_resume_irq(struct phy_device *phydev)
+{
+	if (!phy_interrupt_is_valid(phydev))
+		return;
+
+	phydev->irq_suspended = 0;
+	synchronize_irq(phydev->irq);
+
+	/* Rerun interrupts which were postponed by phy_interrupt()
+	 * because they occurred during the system sleep transition.
+	 */
+	if (phydev->irq_rerun) {
+		phydev->irq_rerun = 0;
+		enable_irq(phydev->irq);
+		irq_wake_thread(phydev->irq, phydev);
+	}
+}
+
 static __maybe_unused int mdio_bus_phy_resume(struct device *dev)
 {
 	struct phy_device *phydev = to_phy_device(dev);
 	int ret;
 
-	if (phydev->mac_managed_pm)
+	if (phydev->mac_managed_pm) {
+		mdio_bus_phy_resume_irq(phydev);
 		return 0;
+	}
 
 	if (!phydev->suspended_by_mdio_bus)
 		goto no_resume;
@@ -404,19 +425,7 @@ static __maybe_unused int mdio_bus_phy_resume(struct device *dev)
 	if (ret < 0)
 		return ret;
 no_resume:
-	if (phy_interrupt_is_valid(phydev)) {
-		phydev->irq_suspended = 0;
-		synchronize_irq(phydev->irq);
-
-		/* Rerun interrupts which were postponed by phy_interrupt()
-		 * because they occurred during the system sleep transition.
-		 */
-		if (phydev->irq_rerun) {
-			phydev->irq_rerun = 0;
-			enable_irq(phydev->irq);
-			irq_wake_thread(phydev->irq, phydev);
-		}
-	}
+	mdio_bus_phy_resume_irq(phydev);
 
 	if (phy_uses_state_machine(phydev))
 		phy_start_machine(phydev);
-- 
2.43.0


^ permalink raw reply	[flat|nested] 7+ messages in thread

end of thread, other threads:[~2026-10-06  4:38 UTC | newest]

Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-10-02  4:35 [PATCH net] net: phy: postpone PHY interrupts during sleep with MAC-managed PM Igor Velkov
2026-10-02  5:03 ` Igor Velkov
2026-10-03 20:35 ` Igor Velkov
2026-10-04 14:28 ` Andrew Lunn
2026-10-04 18:21   ` Igor Velkov
2026-10-04 14:35 ` Andrew Lunn
2026-10-06  4:38 ` netdev-bot+sashiko

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®