public inbox for netdev@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/3] net: pse-pd: support module-based PSE controller drivers
@ 2026-03-29 16:10 Carlo Szelinsky
  2026-03-29 16:10 ` [PATCH 1/3] net: mdio: treat PSE EPROBE_DEFER as non-fatal during PHY registration Carlo Szelinsky
                   ` (2 more replies)
  0 siblings, 3 replies; 4+ messages in thread
From: Carlo Szelinsky @ 2026-03-29 16:10 UTC (permalink / raw)
  To: Kory Maincent, Oleksij Rempel, Andrew Lunn
  Cc: Heiner Kallweit, Russell King, Jakub Kicinski, David S . Miller,
	Eric Dumazet, Paolo Abeni, Simon Horman, netdev, linux-kernel,
	Carlo Szelinsky

When a PSE controller driver is built as a module, it may not be probed
yet when PHYs are registered on the MDIO bus. This causes
of_pse_control_get() to return -EPROBE_DEFER, destroying the PHY device.
Later, regulator_late_cleanup disables the unclaimed PSE regulators,
permanently killing PoE.

This series fixes the issue in three steps:

  1. Treat -EPROBE_DEFER as non-fatal during PHY registration, allowing
     the PHY to register with psec=NULL.

  2. Add an admin_state_synced flag to pse_pi so that pse_pi_is_enabled()
     reports unclaimed PIs as disabled, preventing regulator_late_cleanup
     from shutting them down. The existing dual-path behavior (software-
     tracked vs. hardware-queried state) is preserved for claimed PIs.

  3. Add pse_control_try_resolve() for lazy PSE control resolution on
     first ethtool access, serialized by RTNL.

This is tested on my setup, but I am not fully sure if this is the right
approach to solve this problem. I would love to get feedback from the
maintainers on whether the overall design direction makes sense, or if
there is a better way to handle the deferred PSE control acquisition.

Carlo Szelinsky (3):
  net: mdio: treat PSE EPROBE_DEFER as non-fatal during PHY registration
  net: pse-pd: prevent regulator cleanup from disabling unclaimed PSE
    PIs
  net: pse-pd: add lazy PSE control resolution for modular drivers

 drivers/net/mdio/fwnode_mdio.c |  8 ++++--
 drivers/net/pse-pd/pse_core.c  | 49 ++++++++++++++++++++++++++++++++++
 include/linux/pse-pd/pse.h     |  6 +++++
 net/ethtool/pse-pd.c           |  4 +++
 4 files changed, 65 insertions(+), 2 deletions(-)

--
2.43.0


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

* [PATCH 1/3] net: mdio: treat PSE EPROBE_DEFER as non-fatal during PHY registration
  2026-03-29 16:10 [PATCH 0/3] net: pse-pd: support module-based PSE controller drivers Carlo Szelinsky
@ 2026-03-29 16:10 ` Carlo Szelinsky
  2026-03-29 16:10 ` [PATCH 2/3] net: pse-pd: prevent regulator cleanup from disabling unclaimed PSE PIs Carlo Szelinsky
  2026-03-29 16:10 ` [PATCH 3/3] net: pse-pd: add lazy PSE control resolution for modular drivers Carlo Szelinsky
  2 siblings, 0 replies; 4+ messages in thread
From: Carlo Szelinsky @ 2026-03-29 16:10 UTC (permalink / raw)
  To: Kory Maincent, Oleksij Rempel, Andrew Lunn
  Cc: Heiner Kallweit, Russell King, Jakub Kicinski, David S . Miller,
	Eric Dumazet, Paolo Abeni, Simon Horman, netdev, linux-kernel,
	Carlo Szelinsky

When a PSE controller driver is built as a module, it may not be probed
yet when PHYs are registered on the MDIO bus. This causes
fwnode_find_pse_control() -> of_pse_control_get() to return
-EPROBE_DEFER, which currently propagates up and destroys the PHY
device.

Treat -EPROBE_DEFER as non-fatal, allowing the PHY to register
successfully with psec=NULL. The PSE control can be resolved lazily
when first needed.

Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
---
 drivers/net/mdio/fwnode_mdio.c | 8 ++++++--
 1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/drivers/net/mdio/fwnode_mdio.c b/drivers/net/mdio/fwnode_mdio.c
index ba7091518265..2a03b3fc41e6 100644
--- a/drivers/net/mdio/fwnode_mdio.c
+++ b/drivers/net/mdio/fwnode_mdio.c
@@ -161,8 +161,12 @@ int fwnode_mdiobus_register_phy(struct mii_bus *bus,
 
 	psec = fwnode_find_pse_control(child, phy);
 	if (IS_ERR(psec)) {
-		rc = PTR_ERR(psec);
-		goto unregister_phy;
+		if (PTR_ERR(psec) == -EPROBE_DEFER) {
+			psec = NULL;
+		} else {
+			rc = PTR_ERR(psec);
+			goto unregister_phy;
+		}
 	}
 
 	phy->psec = psec;
-- 
2.43.0


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

* [PATCH 2/3] net: pse-pd: prevent regulator cleanup from disabling unclaimed PSE PIs
  2026-03-29 16:10 [PATCH 0/3] net: pse-pd: support module-based PSE controller drivers Carlo Szelinsky
  2026-03-29 16:10 ` [PATCH 1/3] net: mdio: treat PSE EPROBE_DEFER as non-fatal during PHY registration Carlo Szelinsky
@ 2026-03-29 16:10 ` Carlo Szelinsky
  2026-03-29 16:10 ` [PATCH 3/3] net: pse-pd: add lazy PSE control resolution for modular drivers Carlo Szelinsky
  2 siblings, 0 replies; 4+ messages in thread
From: Carlo Szelinsky @ 2026-03-29 16:10 UTC (permalink / raw)
  To: Kory Maincent, Oleksij Rempel, Andrew Lunn
  Cc: Heiner Kallweit, Russell King, Jakub Kicinski, David S . Miller,
	Eric Dumazet, Paolo Abeni, Simon Horman, netdev, linux-kernel,
	Carlo Szelinsky

When a PSE controller driver is loaded as a module, its PI regulators
are registered before any consumer (PHY) acquires the corresponding PSE
control via of_pse_control_get(). The regulator framework's
regulator_late_cleanup then calls pse_pi_is_enabled(), which queries
hardware and sees the PI is enabled. Since no consumer holds it
(use_count == 0), regulator_late_cleanup disables it, killing PoE.

Add an admin_state_synced flag to struct pse_pi that is set when a
consumer first acquires the PSE control and syncs admin_state_enabled
from hardware. In pse_pi_is_enabled(), report unsynchronized PIs as
disabled so regulator_late_cleanup skips them.

This preserves the existing dual-path behavior: software-tracked state
for software-controlled power domains, and hardware queries for
hardware-controlled domains. The admin_state_synced flag is only false
before the first consumer acquisition, which is the exact window where
regulator_late_cleanup could incorrectly disable the PI.

Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
---
 drivers/net/pse-pd/pse_core.c | 13 +++++++++++++
 include/linux/pse-pd/pse.h    |  1 +
 2 files changed, 14 insertions(+)

diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c
index 3beaaaeec9e1..566b07c336bf 100644
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -421,6 +421,18 @@ static int pse_pi_is_enabled(struct regulator_dev *rdev)
 
 	id = rdev_get_id(rdev);
 	mutex_lock(&pcdev->lock);
+
+	/*
+	 * Report the PI as disabled until a consumer has acquired it
+	 * and synced admin_state_enabled from hardware. This prevents
+	 * regulator_late_cleanup from disabling unclaimed PSE PIs
+	 * when the PSE controller driver loads as a module.
+	 */
+	if (!pcdev->pi[id].admin_state_synced) {
+		ret = 0;
+		goto out;
+	}
+
 	if (pse_pw_d_is_sw_pw_control(pcdev, pcdev->pi[id].pw_d)) {
 		ret = pcdev->pi[id].admin_state_enabled;
 		goto out;
@@ -1431,6 +1443,7 @@ pse_control_get_internal(struct pse_controller_dev *pcdev, unsigned int index,
 		goto free_psec;
 
 	pcdev->pi[index].admin_state_enabled = ret;
+	pcdev->pi[index].admin_state_synced = true;
 	psec->ps = devm_regulator_get_exclusive(pcdev->dev,
 						rdev_get_name(pcdev->pi[index].rdev));
 	if (IS_ERR(psec->ps)) {
diff --git a/include/linux/pse-pd/pse.h b/include/linux/pse-pd/pse.h
index 4e5696cfade7..b86cce740551 100644
--- a/include/linux/pse-pd/pse.h
+++ b/include/linux/pse-pd/pse.h
@@ -260,6 +260,7 @@ struct pse_pi {
 	struct device_node *np;
 	struct regulator_dev *rdev;
 	bool admin_state_enabled;
+	bool admin_state_synced;
 	struct pse_power_domain *pw_d;
 	int prio;
 	bool isr_pd_detected;
-- 
2.43.0


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

* [PATCH 3/3] net: pse-pd: add lazy PSE control resolution for modular drivers
  2026-03-29 16:10 [PATCH 0/3] net: pse-pd: support module-based PSE controller drivers Carlo Szelinsky
  2026-03-29 16:10 ` [PATCH 1/3] net: mdio: treat PSE EPROBE_DEFER as non-fatal during PHY registration Carlo Szelinsky
  2026-03-29 16:10 ` [PATCH 2/3] net: pse-pd: prevent regulator cleanup from disabling unclaimed PSE PIs Carlo Szelinsky
@ 2026-03-29 16:10 ` Carlo Szelinsky
  2 siblings, 0 replies; 4+ messages in thread
From: Carlo Szelinsky @ 2026-03-29 16:10 UTC (permalink / raw)
  To: Kory Maincent, Oleksij Rempel, Andrew Lunn
  Cc: Heiner Kallweit, Russell King, Jakub Kicinski, David S . Miller,
	Eric Dumazet, Paolo Abeni, Simon Horman, netdev, linux-kernel,
	Carlo Szelinsky

When a PSE controller driver is built as a module and was not probed
during PHY registration, the PHY ends up with psec=NULL. Add
pse_control_try_resolve() to lazily resolve the PSE control on first
ethtool access.

Call pse_control_try_resolve() in both the GET and SET ethtool PSE
handlers, before checking phydev->psec. The function is serialized by
RTNL (enforced via ASSERT_RTNL), preventing concurrent callers from
double-allocating a PSE control.

If resolution fails (e.g. the module still has not loaded), a debug
message is emitted via phydev_dbg() and the handler falls through to
the existing "No PSE is attached" error path.

Signed-off-by: Carlo Szelinsky <github@szelinsky.de>
---
 drivers/net/pse-pd/pse_core.c | 36 +++++++++++++++++++++++++++++++++++
 include/linux/pse-pd/pse.h    |  5 +++++
 net/ethtool/pse-pd.c          |  4 ++++
 3 files changed, 45 insertions(+)

diff --git a/drivers/net/pse-pd/pse_core.c b/drivers/net/pse-pd/pse_core.c
index 566b07c336bf..390df23a991c 100644
--- a/drivers/net/pse-pd/pse_core.c
+++ b/drivers/net/pse-pd/pse_core.c
@@ -1569,6 +1569,42 @@ struct pse_control *of_pse_control_get(struct device_node *node,
 }
 EXPORT_SYMBOL_GPL(of_pse_control_get);
 
+/**
+ * pse_control_try_resolve - attempt to resolve a deferred PSE control
+ * @phydev: the PHY device whose PSE control may need resolution
+ *
+ * When a PSE controller driver is built as a module, it may not have
+ * probed when PHYs were registered on the MDIO bus. This function
+ * retries PSE control acquisition and should be called before
+ * accessing phydev->psec in ethtool handlers.
+ *
+ * Context: Caller must hold RTNL.
+ */
+void pse_control_try_resolve(struct phy_device *phydev)
+{
+	struct device_node *np;
+	struct pse_control *psec;
+
+	ASSERT_RTNL();
+
+	if (phydev->psec)
+		return;
+
+	np = phydev->mdio.dev.of_node;
+	if (!np || !of_property_present(np, "pses"))
+		return;
+
+	psec = of_pse_control_get(np, phydev);
+	if (IS_ERR(psec)) {
+		phydev_dbg(phydev, "failed to resolve PSE control: %pe\n",
+			   psec);
+		return;
+	}
+
+	phydev->psec = psec;
+}
+EXPORT_SYMBOL_GPL(pse_control_try_resolve);
+
 /**
  * pse_get_sw_admin_state - Convert the software admin state to c33 or podl
  *			    admin state value used in the standard
diff --git a/include/linux/pse-pd/pse.h b/include/linux/pse-pd/pse.h
index b86cce740551..d2f9b7c1acdf 100644
--- a/include/linux/pse-pd/pse.h
+++ b/include/linux/pse-pd/pse.h
@@ -350,6 +350,7 @@ int devm_pse_irq_helper(struct pse_controller_dev *pcdev, int irq,
 struct pse_control *of_pse_control_get(struct device_node *node,
 				       struct phy_device *phydev);
 void pse_control_put(struct pse_control *psec);
+void pse_control_try_resolve(struct phy_device *phydev);
 
 int pse_ethtool_get_status(struct pse_control *psec,
 			   struct netlink_ext_ack *extack,
@@ -379,6 +380,10 @@ static inline void pse_control_put(struct pse_control *psec)
 {
 }
 
+static inline void pse_control_try_resolve(struct phy_device *phydev)
+{
+}
+
 static inline int pse_ethtool_get_status(struct pse_control *psec,
 					 struct netlink_ext_ack *extack,
 					 struct ethtool_pse_control_status *status)
diff --git a/net/ethtool/pse-pd.c b/net/ethtool/pse-pd.c
index 2eb9bdc2dcb9..adffc230acd6 100644
--- a/net/ethtool/pse-pd.c
+++ b/net/ethtool/pse-pd.c
@@ -42,6 +42,8 @@ static int pse_get_pse_attributes(struct phy_device *phydev,
 		return -EOPNOTSUPP;
 	}
 
+	pse_control_try_resolve(phydev);
+
 	if (!phydev->psec) {
 		NL_SET_ERR_MSG(extack, "No PSE is attached");
 		return -EOPNOTSUPP;
@@ -249,6 +251,8 @@ ethnl_set_pse_validate(struct phy_device *phydev, struct genl_info *info)
 		return -EOPNOTSUPP;
 	}
 
+	pse_control_try_resolve(phydev);
+
 	if (!phydev->psec) {
 		NL_SET_ERR_MSG(info->extack, "No PSE is attached");
 		return -EOPNOTSUPP;
-- 
2.43.0


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

end of thread, other threads:[~2026-03-29 16:10 UTC | newest]

Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-29 16:10 [PATCH 0/3] net: pse-pd: support module-based PSE controller drivers Carlo Szelinsky
2026-03-29 16:10 ` [PATCH 1/3] net: mdio: treat PSE EPROBE_DEFER as non-fatal during PHY registration Carlo Szelinsky
2026-03-29 16:10 ` [PATCH 2/3] net: pse-pd: prevent regulator cleanup from disabling unclaimed PSE PIs Carlo Szelinsky
2026-03-29 16:10 ` [PATCH 3/3] net: pse-pd: add lazy PSE control resolution for modular drivers Carlo Szelinsky

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