From: Jonas Jelonek Date: Sun, 3 May 2026 20:43:57 +0000 (+0000) Subject: generic: pse-pd: add patches for module-based PSE drivers X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=96ecf8dd248cbdd4bcabcc198f9170abda1ab0ea;p=thirdparty%2Fopenwrt.git generic: pse-pd: add patches for module-based PSE drivers Add a backport patch and pending patches needed by upcoming PSE drivers which are built as modules and probed after the MAC/PHY. The net effect for module-built PSE controllers: attachment to PHYs happens via the lifecycle notifier rather than via probe-time -EPROBE_DEFER coupling, so the MDIO/DSA probe no longer sees any PSE-originated -EPROBE_DEFER and the probe-retry storm is gone. Link: https://github.com/openwrt/openwrt/pull/23222 Signed-off-by: Jonas Jelonek --- diff --git a/target/linux/airoha/patches-6.18/801-01-net-phy-add-PHY_DETACH_NO_HW_RESET-PHY-flag.patch b/target/linux/airoha/patches-6.18/801-01-net-phy-add-PHY_DETACH_NO_HW_RESET-PHY-flag.patch index cc710daead4..4637e79b3b3 100644 --- a/target/linux/airoha/patches-6.18/801-01-net-phy-add-PHY_DETACH_NO_HW_RESET-PHY-flag.patch +++ b/target/linux/airoha/patches-6.18/801-01-net-phy-add-PHY_DETACH_NO_HW_RESET-PHY-flag.patch @@ -107,7 +107,7 @@ Signed-off-by: Christian Marangi module_phy_driver(as21xxx_drivers); --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c -@@ -1885,7 +1885,8 @@ void phy_detach(struct phy_device *phyde +@@ -2030,7 +2030,8 @@ void phy_detach(struct phy_device *phyde } /* Assert the reset signal */ diff --git a/target/linux/generic/backport-6.18/627-v7.2-net-pse-pd-scope-pse_control-regulator-handle-to-kre.patch b/target/linux/generic/backport-6.18/627-v7.2-net-pse-pd-scope-pse_control-regulator-handle-to-kre.patch new file mode 100644 index 00000000000..e41033c6e16 --- /dev/null +++ b/target/linux/generic/backport-6.18/627-v7.2-net-pse-pd-scope-pse_control-regulator-handle-to-kre.patch @@ -0,0 +1,73 @@ +From 16759757c4d28e958fd5a5a1fe0f86828872f28d Mon Sep 17 00:00:00 2001 +From: Corey Leavitt +Date: Wed, 24 Jun 2026 22:40:16 +0200 +Subject: net: pse-pd: scope pse_control regulator handle to kref lifetime + +__pse_control_release() drops psec->ps via devm_regulator_put(), which +only succeeds if the devres entry added by the matching +devm_regulator_get_exclusive() is still present on pcdev->dev at the +time the pse_control's kref hits zero. + +That assumption does not hold when the controller is unbound while a +pse_control still has consumers: pcdev->dev's devres list is released +LIFO, so every per-attach regulator-GET devres runs (and +regulator_put()s the underlying regulator) before +pse_controller_unregister() itself is invoked. Any later +pse_control_put() from that unbind path then reads psec->ps as a +dangling pointer inside devm_regulator_put() and WARNs at +drivers/regulator/devres.c:232 (devres_release() fails to find the +already-released match). + +The pse_control's consumer handle is logically scoped to the +pse_control's refcount, not to pcdev->dev's devres lifetime. Switch to +the plain regulator_get_exclusive() / regulator_put() pair so the +regulator put in __pse_control_release() no longer depends on the +controller's devres still being present. No change to the +regulator-framework-visible refcount or lifetime of the underlying +regulator: a single get paired with a single put. The existing +devm_regulator_register() for the per-PI rails is unchanged (those ARE +correctly scoped to the controller's lifetime). + +This addresses only the regulator handle. The same unbind-while-held +scenario also leaves __pse_control_release() reading psec->pcdev->pi[] +and psec->pcdev->owner after pse_controller_unregister() has freed +pcdev->pi, because the controller does not drain its outstanding +pse_control references on unregister. That wider pse_control vs +pcdev lifetime problem pre-dates this change and is addressed by the +PSE controller notifier series, which drains phydev->psec on +PSE_UNREGISTERED before pcdev->pi is freed. + +Link: https://lore.kernel.org/netdev/20260620112440.1734404-1-github@szelinsky.de/ +Fixes: d83e13761d5b ("net: pse-pd: Use regulator framework within PSE framework") +Signed-off-by: Corey Leavitt +Acked-by: Kory Maincent +Signed-off-by: Carlo Szelinsky +Reviewed-by: Simon Horman +Link: https://patch.msgid.link/20260624204017.2752934-1-github@szelinsky.de +Signed-off-by: Jakub Kicinski +--- + drivers/net/pse-pd/pse_core.c | 6 +++--- + 1 file changed, 3 insertions(+), 3 deletions(-) + +--- a/drivers/net/pse-pd/pse_core.c ++++ b/drivers/net/pse-pd/pse_core.c +@@ -1362,7 +1362,7 @@ static void __pse_control_release(struct + + if (psec->pcdev->pi[psec->id].admin_state_enabled) + regulator_disable(psec->ps); +- devm_regulator_put(psec->ps); ++ regulator_put(psec->ps); + + module_put(psec->pcdev->owner); + +@@ -1431,8 +1431,8 @@ pse_control_get_internal(struct pse_cont + goto free_psec; + + pcdev->pi[index].admin_state_enabled = ret; +- psec->ps = devm_regulator_get_exclusive(pcdev->dev, +- rdev_get_name(pcdev->pi[index].rdev)); ++ psec->ps = regulator_get_exclusive(pcdev->dev, ++ rdev_get_name(pcdev->pi[index].rdev)); + if (IS_ERR(psec->ps)) { + ret = PTR_ERR(psec->ps); + goto put_module; diff --git a/target/linux/generic/pending-6.18/896-01-net-pse-pd-add-notifier-chain-for-controller-lifecyc.patch b/target/linux/generic/pending-6.18/896-01-net-pse-pd-add-notifier-chain-for-controller-lifecyc.patch new file mode 100644 index 00000000000..4855829863e --- /dev/null +++ b/target/linux/generic/pending-6.18/896-01-net-pse-pd-add-notifier-chain-for-controller-lifecyc.patch @@ -0,0 +1,144 @@ +From: Corey Leavitt + +Introduce a blocking notifier chain that allows other subsystems to be +informed when a PSE controller is registered or unregistered, and +provide pse_register_notifier() / pse_unregister_notifier() as the +subscriber interface. + +Subsequent patches will use this to let the phy subsystem own the +phydev->psec lifecycle directly, decoupling PSE lookup from +fwnode_mdiobus_register_phy() and removing the probe-time +-EPROBE_DEFER coupling that currently exists between mdio, phy and +pse-pd when the PSE controller driver is modular. + +A blocking chain (rather than atomic) is used because callbacks will +take rtnl_lock and call back into pse_core via of_pse_control_get(). + +The enum pse_controller_event is placed outside the +IS_ENABLED(CONFIG_PSE_CONTROLLER) guard so that subscribers compiled +into a kernel without PSE support can still reference the event +values in dead-code paths without breaking the build. + +This patch is pure infrastructure: nothing fires events yet, and +nothing subscribes. No observable behavior change. + +Signed-off-by: Corey Leavitt +Signed-off-by: Carlo Szelinsky +Tested-by: Jonas Jelonek +--- + drivers/net/pse-pd/pse_core.c | 34 ++++++++++++++++++++++++++++++++++ + include/linux/pse-pd/pse.h | 32 ++++++++++++++++++++++++++++++++ + 2 files changed, 66 insertions(+) + +--- a/drivers/net/pse-pd/pse_core.c ++++ b/drivers/net/pse-pd/pse_core.c +@@ -8,6 +8,7 @@ + #include + #include + #include ++#include + #include + #include + #include +@@ -23,6 +24,39 @@ static LIST_HEAD(pse_controller_list); + static DEFINE_XARRAY_ALLOC(pse_pw_d_map); + static DEFINE_MUTEX(pse_pw_d_mutex); + ++static BLOCKING_NOTIFIER_HEAD(pse_controller_notifier); ++ ++/** ++ * pse_register_notifier - register a callback for PSE controller events ++ * @nb: notifier block to register ++ * ++ * See enum pse_controller_event for events fired and their subscriber ++ * contract. Callbacks run in process context; they may sleep, take ++ * rtnl, and call of_pse_control_get(). The chain fires synchronously, ++ * so a PSE controller driver's probe/unbind path must not hold any ++ * such lock when calling pse_controller_register() or ++ * pse_controller_unregister(). ++ * ++ * Return: 0 on success, negative error code otherwise. ++ */ ++int pse_register_notifier(struct notifier_block *nb) ++{ ++ return blocking_notifier_chain_register(&pse_controller_notifier, nb); ++} ++EXPORT_SYMBOL_GPL(pse_register_notifier); ++ ++/** ++ * pse_unregister_notifier - unregister a previously registered callback ++ * @nb: notifier block previously passed to pse_register_notifier() ++ * ++ * Return: 0 on success, negative error code otherwise. ++ */ ++int pse_unregister_notifier(struct notifier_block *nb) ++{ ++ return blocking_notifier_chain_unregister(&pse_controller_notifier, nb); ++} ++EXPORT_SYMBOL_GPL(pse_unregister_notifier); ++ + /** + * struct pse_control - a PSE control + * @pcdev: a pointer to the PSE controller device +--- a/include/linux/pse-pd/pse.h ++++ b/include/linux/pse-pd/pse.h +@@ -21,6 +21,7 @@ struct net_device; + struct phy_device; + struct pse_controller_dev; + struct netlink_ext_ack; ++struct notifier_block; + + /* C33 PSE extended state and substate. */ + struct ethtool_c33_pse_ext_state_info { +@@ -337,6 +338,24 @@ enum pse_budget_eval_strategies { + PSE_BUDGET_EVAL_STRAT_DYNAMIC = 1 << 2, + }; + ++/** ++ * enum pse_controller_event - PSE controller lifecycle events ++ * ++ * Event data in callbacks is always a pointer to the struct ++ * pse_controller_dev firing the event. ++ * ++ * @PSE_REGISTERED: controller added to pse_controller_list and ++ * resolvable by of_pse_control_get(). ++ * @PSE_UNREGISTERED: controller about to be removed from ++ * pse_controller_list. Subscribers holding pse_control references ++ * targeting it must drop them before returning and must not ++ * acquire new references for it. ++ */ ++enum pse_controller_event { ++ PSE_REGISTERED, ++ PSE_UNREGISTERED, ++}; ++ + #if IS_ENABLED(CONFIG_PSE_CONTROLLER) + int pse_controller_register(struct pse_controller_dev *pcdev); + void pse_controller_unregister(struct pse_controller_dev *pcdev); +@@ -366,6 +385,9 @@ int pse_ethtool_set_prio(struct pse_cont + bool pse_has_podl(struct pse_control *psec); + bool pse_has_c33(struct pse_control *psec); + ++int pse_register_notifier(struct notifier_block *nb); ++int pse_unregister_notifier(struct notifier_block *nb); ++ + #else + + static inline struct pse_control *of_pse_control_get(struct device_node *node, +@@ -416,6 +438,16 @@ static inline bool pse_has_c33(struct ps + return false; + } + ++static inline int pse_register_notifier(struct notifier_block *nb) ++{ ++ return 0; ++} ++ ++static inline int pse_unregister_notifier(struct notifier_block *nb) ++{ ++ return 0; ++} ++ + #endif + + #endif diff --git a/target/linux/generic/pending-6.18/896-02-net-pse-pd-fire-lifecycle-events-on-controller-regis.patch b/target/linux/generic/pending-6.18/896-02-net-pse-pd-fire-lifecycle-events-on-controller-regis.patch new file mode 100644 index 00000000000..0a6cbc9ee6e --- /dev/null +++ b/target/linux/generic/pending-6.18/896-02-net-pse-pd-fire-lifecycle-events-on-controller-regis.patch @@ -0,0 +1,42 @@ +From: Corey Leavitt + +Hook the newly-introduced pse_controller_notifier chain so that +pse_controller_register() fires PSE_REGISTERED after the controller +has been added to pse_controller_list (i.e. is now resolvable by +of_pse_control_get()), and pse_controller_unregister() fires +PSE_UNREGISTERED before the controller is removed from the list +(while it is still valid to dereference from a subscriber's +pse_control pointer targeting it). + +With no subscribers yet, this is observably a no-op. A later change +wires the phy subsystem in as the first subscriber. + +Signed-off-by: Corey Leavitt +Signed-off-by: Carlo Szelinsky +Tested-by: Jonas Jelonek +--- + drivers/net/pse-pd/pse_core.c | 6 ++++++ + 1 file changed, 6 insertions(+) + +--- a/drivers/net/pse-pd/pse_core.c ++++ b/drivers/net/pse-pd/pse_core.c +@@ -1138,6 +1138,9 @@ int pse_controller_register(struct pse_c + list_add(&pcdev->list, &pse_controller_list); + mutex_unlock(&pse_list_mutex); + ++ blocking_notifier_call_chain(&pse_controller_notifier, ++ PSE_REGISTERED, pcdev); ++ + return 0; + } + EXPORT_SYMBOL_GPL(pse_controller_register); +@@ -1148,6 +1151,9 @@ EXPORT_SYMBOL_GPL(pse_controller_registe + */ + void pse_controller_unregister(struct pse_controller_dev *pcdev) + { ++ blocking_notifier_call_chain(&pse_controller_notifier, ++ PSE_UNREGISTERED, pcdev); ++ + pse_flush_pw_ds(pcdev); + pse_release_pis(pcdev); + if (pcdev->irq) diff --git a/target/linux/generic/pending-6.18/896-03-net-phy-own-phydev-psec-via-PSE-notifier-and-remove-.patch b/target/linux/generic/pending-6.18/896-03-net-phy-own-phydev-psec-via-PSE-notifier-and-remove-.patch new file mode 100644 index 00000000000..b5a5aa2009f --- /dev/null +++ b/target/linux/generic/pending-6.18/896-03-net-phy-own-phydev-psec-via-PSE-notifier-and-remove-.patch @@ -0,0 +1,436 @@ +From: Corey Leavitt + +Transfer ownership of phydev->psec from fwnode_mdio to the phy +subsystem itself. The phy subsystem now subscribes to the pse-pd +notifier chain and manages psec attach/detach in response to PSE +controller lifecycle events, while fwnode_mdio loses its PSE awareness +entirely. + +phydev->psec is attached after device_add() has made the phy visible +on mdio_bus_type, under a narrow rtnl_lock() that covers only +phy_try_attach_pse(). Ordering the attach after registration closes +the race that would otherwise leave a phy unattached: a PSE_REGISTERED +event firing during registration walks mdio_bus_type and either finds +the phy already added (and attaches it) or runs before device_add(), +in which case the post-add attach resolves it. The phydev->psec check +in phy_try_attach_pse() makes the two paths idempotent. Holding rtnl +across of_pse_control_get() is safe because pse_list_mutex is never +taken in the opposite order. + +device_add() is deliberately left outside rtnl. Binding a phy that +itself provides an SFP cage reaches sfp_bus_add_upstream() through +phy_probe() -> phy_setup_ports() -> phy_sfp_probe(), and +sfp_bus_add_upstream() takes rtnl_lock(); holding rtnl across +device_add() would deadlock such phys (reported on RTL8214FC). + +phy_device_register() is split into the public form, which takes the +narrow rtnl_lock() around the attach, and a phy_device_register_locked() +form for callers that already hold rtnl (the SFP module state machine +via __sfp_sm_event). This pair mirrors the register_netdevice() / +register_netdev() split convention already established in the core +networking stack. The _locked form runs device_add() under the +caller's rtnl, which is safe because a phy resident on an SFP module +does not itself provide a downstream cage, so phy_sfp_probe() is a +no-op there. + + - On PSE_REGISTERED: an rtnl-guarded bus walk retries the attach for + every registered phy whose psec is still NULL. This is the "phy + was enumerated before the PSE controller loaded" case, the root + cause of the boot-time probe-retry storm on systems with a modular + PSE controller driver. + + - On PSE_UNREGISTERED: an rtnl-guarded bus walk releases every + phydev->psec that targets the departing controller before + pse_release_pis() frees pcdev->pi. Without this, a phy still + holding a pse_control reference would cause a use-after-free in + __pse_control_release()'s pcdev->pi[psec->id] access, and the PSE + driver module could not finish unloading while any phy still held a + reference. + +A bad `pses` binding -- an error from of_pse_control_get() other than +-ENOENT (no phandle) or -EPROBE_DEFER (controller not yet registered) +-- is reported with phydev_warn() rather than silently dropped, +preserving the diagnostic that the removed fwnode_mdio lookup used to +provide. + +The final pse_control_put() of phydev->psec moves from +phy_device_remove() to phy_device_release(), so it runs only after +every reference on the device -- including the bus-iterator references +taken by bus_for_each_dev() in the notifier walk -- has been dropped. + +Finally, delete fwnode_find_pse_control() and its call site in +fwnode_mdiobus_register_phy(), and drop the PSE header from +fwnode_mdio.c. The MDIO/DSA probe no longer sees any PSE-originated +-EPROBE_DEFER, so the probe-retry storm is gone and fwnode_mdio is +now PSE-agnostic. + +Reported-by: Jonas Jelonek +Closes: https://lore.kernel.org/netdev/e00048dd-1ed3-40c3-9912-59bccf015ad5@gmail.com/ +Signed-off-by: Corey Leavitt +Co-developed-by: Carlo Szelinsky +Signed-off-by: Carlo Szelinsky +Tested-by: Jonas Jelonek +--- + drivers/net/mdio/fwnode_mdio.c | 34 ------- + drivers/net/phy/phy_device.c | 168 +++++++++++++++++++++++++++++++-- + drivers/net/phy/sfp.c | 2 +- + drivers/net/pse-pd/pse_core.c | 14 +++ + include/linux/phy.h | 2 + + include/linux/pse-pd/pse.h | 9 ++ + 6 files changed, 186 insertions(+), 43 deletions(-) + +--- a/drivers/net/mdio/fwnode_mdio.c ++++ b/drivers/net/mdio/fwnode_mdio.c +@@ -11,33 +11,11 @@ + #include + #include + #include +-#include + + MODULE_AUTHOR("Calvin Johnson "); + MODULE_LICENSE("GPL"); + MODULE_DESCRIPTION("FWNODE MDIO bus (Ethernet PHY) accessors"); + +-static struct pse_control * +-fwnode_find_pse_control(struct fwnode_handle *fwnode, +- struct phy_device *phydev) +-{ +- struct pse_control *psec; +- struct device_node *np; +- +- if (!IS_ENABLED(CONFIG_PSE_CONTROLLER)) +- return NULL; +- +- np = to_of_node(fwnode); +- if (!np) +- return NULL; +- +- psec = of_pse_control_get(np, phydev); +- if (PTR_ERR(psec) == -ENOENT) +- return NULL; +- +- return psec; +-} +- + static struct mii_timestamper * + fwnode_find_mii_timestamper(struct fwnode_handle *fwnode) + { +@@ -123,7 +101,6 @@ int fwnode_mdiobus_register_phy(struct m + struct fwnode_handle *child, u32 addr) + { + struct mii_timestamper *mii_ts = NULL; +- struct pse_control *psec = NULL; + struct phy_device *phy; + bool is_c45; + u32 phy_id; +@@ -164,14 +141,6 @@ int fwnode_mdiobus_register_phy(struct m + goto clean_phy; + } + +- psec = fwnode_find_pse_control(child, phy); +- if (IS_ERR(psec)) { +- rc = PTR_ERR(psec); +- goto unregister_phy; +- } +- +- phy->psec = psec; +- + /* phy->mii_ts may already be defined by the PHY driver. A + * mii_timestamper probed via the device tree will still have + * precedence. +@@ -181,9 +150,6 @@ int fwnode_mdiobus_register_phy(struct m + + return 0; + +-unregister_phy: +- if (is_acpi_node(child) || is_of_node(child)) +- phy_device_remove(phy); + clean_phy: + phy_device_free(phy); + clean_mii_ts: +--- a/drivers/net/phy/phy_device.c ++++ b/drivers/net/phy/phy_device.c +@@ -225,8 +225,19 @@ static void phy_mdio_device_free(struct + + static void phy_device_release(struct device *dev) + { ++ struct phy_device *phydev = to_phy_device(dev); ++ ++ /* bus_for_each_dev() holds get_device() across each iteration ++ * step, deferring this release callback until any in-flight PSE ++ * notifier walk has advanced past this phy. pse_control_put() ++ * takes pse_list_mutex, so this path must run in sleepable ++ * context. ++ */ ++ might_sleep(); ++ pse_control_put(phydev->psec); ++ + fwnode_handle_put(dev->fwnode); +- kfree(to_phy_device(dev)); ++ kfree(phydev); + } + + static void phy_mdio_device_remove(struct mdio_device *mdiodev) +@@ -1138,11 +1149,103 @@ struct phy_device *get_phy_device(struct + } + EXPORT_SYMBOL(get_phy_device); + +-/** +- * phy_device_register - Register the phy device on the MDIO bus +- * @phydev: phy_device structure to be added to the MDIO bus ++/* Best-effort attach of phydev->psec from a DT `pses = <&...>` phandle. ++ * Caller must hold rtnl. A missing phandle (-ENOENT) or a not-yet-registered ++ * controller (-EPROBE_DEFER) is silent; the notifier retries the latter at ++ * PSE_REGISTERED time. Any other error means a broken binding and is warned ++ * about, but left non-fatal so the phy still registers. + */ +-int phy_device_register(struct phy_device *phydev) ++static void phy_try_attach_pse(struct phy_device *phydev) ++{ ++ struct pse_control *psec; ++ struct device_node *np; ++ ++ ASSERT_RTNL(); ++ ++ np = phydev->mdio.dev.of_node; ++ if (!np) ++ return; ++ ++ if (phydev->psec) ++ return; ++ ++ psec = of_pse_control_get(np, phydev); ++ if (IS_ERR(psec)) { ++ if (PTR_ERR(psec) != -EPROBE_DEFER && PTR_ERR(psec) != -ENOENT) ++ phydev_warn(phydev, "failed to get PSE control: %pe\n", ++ psec); ++ return; ++ } ++ ++ phydev->psec = psec; ++} ++ ++static int phy_pse_attach_one(struct device *dev, void *data __maybe_unused) ++{ ++ ASSERT_RTNL(); ++ ++ if (dev->type != &mdio_bus_phy_type) ++ return 0; ++ ++ phy_try_attach_pse(to_phy_device(dev)); ++ return 0; ++} ++ ++static int phy_pse_detach_one(struct device *dev, void *data) ++{ ++ struct pse_controller_dev *pcdev = data; ++ struct phy_device *phydev; ++ struct pse_control *psec; ++ ++ ASSERT_RTNL(); ++ ++ if (dev->type != &mdio_bus_phy_type) ++ return 0; ++ ++ phydev = to_phy_device(dev); ++ psec = phydev->psec; ++ if (!psec || !pse_control_matches_pcdev(psec, pcdev)) ++ return 0; ++ ++ phydev->psec = NULL; ++ pse_control_put(psec); ++ return 0; ++} ++ ++static int phy_pse_notifier_event(struct notifier_block *nb, ++ unsigned long event, void *data) ++{ ++ switch (event) { ++ case PSE_REGISTERED: ++ rtnl_lock(); ++ bus_for_each_dev(&mdio_bus_type, NULL, NULL, ++ phy_pse_attach_one); ++ rtnl_unlock(); ++ return NOTIFY_OK; ++ case PSE_UNREGISTERED: ++ rtnl_lock(); ++ bus_for_each_dev(&mdio_bus_type, NULL, data, ++ phy_pse_detach_one); ++ rtnl_unlock(); ++ return NOTIFY_OK; ++ default: ++ return NOTIFY_DONE; ++ } ++} ++ ++static struct notifier_block phy_pse_notifier __read_mostly = { ++ .notifier_call = phy_pse_notifier_event, ++}; ++ ++/* Core registration: add the phy to the MDIO bus. Does not touch rtnl or ++ * PSE. phydev->psec is attached by the callers below, after device_add() ++ * has made the phy visible on mdio_bus_type, so that a concurrent PSE ++ * notifier walk and the attach can never leave the phy unattached. Keeping ++ * device_add() out of rtnl also avoids deadlocking when binding a phy that ++ * itself provides an SFP cage (phy_probe() -> phy_sfp_probe() -> ++ * sfp_bus_add_upstream() takes rtnl). ++ */ ++static int __phy_device_register(struct phy_device *phydev) + { + int err; + +@@ -1171,10 +1274,54 @@ int phy_device_register(struct phy_devic + out: + /* Assert the reset signal */ + phy_device_reset(phydev, 1); +- + mdiobus_unregister_device(&phydev->mdio); + return err; + } ++ ++/** ++ * phy_device_register_locked - Register the phy device on the MDIO bus ++ * @phydev: phy_device structure to be added to the MDIO bus ++ * ++ * Same as phy_device_register() but caller must already hold rtnl_lock(). ++ * ++ * Return: 0 on success, negative error code on failure. ++ */ ++int phy_device_register_locked(struct phy_device *phydev) ++{ ++ int err; ++ ++ ASSERT_RTNL(); ++ ++ err = __phy_device_register(phydev); ++ if (err) ++ return err; ++ ++ phy_try_attach_pse(phydev); ++ ++ return 0; ++} ++EXPORT_SYMBOL(phy_device_register_locked); ++ ++/** ++ * phy_device_register - Register the phy device on the MDIO bus ++ * @phydev: phy_device structure to be added to the MDIO bus ++ * ++ * Return: 0 on success, negative error code on failure. ++ */ ++int phy_device_register(struct phy_device *phydev) ++{ ++ int err; ++ ++ err = __phy_device_register(phydev); ++ if (err) ++ return err; ++ ++ rtnl_lock(); ++ phy_try_attach_pse(phydev); ++ rtnl_unlock(); ++ ++ return 0; ++} + EXPORT_SYMBOL(phy_device_register); + + /** +@@ -1188,8 +1335,6 @@ EXPORT_SYMBOL(phy_device_register); + void phy_device_remove(struct phy_device *phydev) + { + unregister_mii_timestamper(phydev->mii_ts); +- pse_control_put(phydev->psec); +- + device_del(&phydev->mdio.dev); + + /* Assert the reset signal */ +@@ -3701,8 +3846,14 @@ static int __init phy_init(void) + if (rc) + goto err_c45; + ++ rc = pse_register_notifier(&phy_pse_notifier); ++ if (rc) ++ goto err_genphy; ++ + return 0; + ++err_genphy: ++ phy_driver_unregister(&genphy_driver); + err_c45: + phy_driver_unregister(&genphy_c45_driver); + err_ethtool_phy_ops: +@@ -3716,6 +3867,7 @@ err_ethtool_phy_ops: + + static void __exit phy_exit(void) + { ++ pse_unregister_notifier(&phy_pse_notifier); + phy_driver_unregister(&genphy_c45_driver); + phy_driver_unregister(&genphy_driver); + rtnl_lock(); +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -1991,7 +1991,7 @@ static int sfp_sm_probe_phy(struct sfp * + /* Mark this PHY as being on a SFP module */ + phy->is_on_sfp_module = true; + +- err = phy_device_register(phy); ++ err = phy_device_register_locked(phy); + if (err) { + phy_device_free(phy); + dev_err(sfp->dev, "phy_device_register failed: %pe\n", +--- a/drivers/net/pse-pd/pse_core.c ++++ b/drivers/net/pse-pd/pse_core.c +@@ -2016,3 +2016,17 @@ bool pse_has_c33(struct pse_control *pse + return psec->pcdev->types & ETHTOOL_PSE_C33; + } + EXPORT_SYMBOL_GPL(pse_has_c33); ++ ++/** ++ * pse_control_matches_pcdev - Test whether a pse_control targets a controller ++ * @psec: pse_control obtained from of_pse_control_get() ++ * @pcdev: PSE controller to compare against ++ * ++ * Return: %true if @psec was obtained from @pcdev, %false otherwise. ++ */ ++bool pse_control_matches_pcdev(struct pse_control *psec, ++ struct pse_controller_dev *pcdev) ++{ ++ return psec->pcdev == pcdev; ++} ++EXPORT_SYMBOL_GPL(pse_control_matches_pcdev); +--- a/include/linux/phy.h ++++ b/include/linux/phy.h +@@ -1841,6 +1841,8 @@ struct phy_device *fwnode_phy_find_devic + struct fwnode_handle *fwnode_get_phy_node(const struct fwnode_handle *fwnode); + struct phy_device *get_phy_device(struct mii_bus *bus, int addr, bool is_c45); + int phy_device_register(struct phy_device *phy); ++/* Caller must hold rtnl_lock(); see phy_device_register() for the public form. */ ++int phy_device_register_locked(struct phy_device *phy); + void phy_device_free(struct phy_device *phydev); + void phy_device_remove(struct phy_device *phydev); + int phy_get_c45_ids(struct phy_device *phydev); +--- a/include/linux/pse-pd/pse.h ++++ b/include/linux/pse-pd/pse.h +@@ -385,6 +385,9 @@ int pse_ethtool_set_prio(struct pse_cont + bool pse_has_podl(struct pse_control *psec); + bool pse_has_c33(struct pse_control *psec); + ++bool pse_control_matches_pcdev(struct pse_control *psec, ++ struct pse_controller_dev *pcdev); ++ + int pse_register_notifier(struct notifier_block *nb); + int pse_unregister_notifier(struct notifier_block *nb); + +@@ -437,6 +440,12 @@ static inline bool pse_has_c33(struct ps + { + return false; + } ++ ++static inline bool pse_control_matches_pcdev(struct pse_control *psec, ++ struct pse_controller_dev *pcdev) ++{ ++ return false; ++} + + static inline int pse_register_notifier(struct notifier_block *nb) + { diff --git a/target/linux/ramips/patches-6.18/720-NET-no-auto-carrier-off-support.patch b/target/linux/ramips/patches-6.18/720-NET-no-auto-carrier-off-support.patch index 752a1ec9449..7d97f03aa80 100644 --- a/target/linux/ramips/patches-6.18/720-NET-no-auto-carrier-off-support.patch +++ b/target/linux/ramips/patches-6.18/720-NET-no-auto-carrier-off-support.patch @@ -10,7 +10,7 @@ Signed-off-by: John Crispin --- a/drivers/net/phy/phy_device.c +++ b/drivers/net/phy/phy_device.c -@@ -257,7 +257,7 @@ static void phy_link_change(struct phy_d +@@ -268,7 +268,7 @@ static void phy_link_change(struct phy_d if (up) netif_carrier_on(netdev);