[PATCH net-next v8 6/7] of: property: do not let "pses" block a consumer's probe
From: Carlo Szelinsky
Date: Sun Oct 04 2026 - 12:47:58 EST
fw_devlink treats "pses" as a supplier binding, so a PHY that
references a PSE PI has its driver probe held in
device_links_check_suppliers() until the PSE controller binds: by a
fwnode link to the PI node until then, and by a device link to the
controller once it has bound.
That is harmless while the PSE lookup itself defers the PHY:
fwnode_mdio registers it, fails the lookup and removes it again on
every retry, so it never stays registered long enough to be bound
early. Once phylib stops deferring on PSE and picks the handle up from
a notifier instead, the PHY is registered while its own driver is
still blocked, and a MAC or DSA switch attaching in that window binds
the generic driver:
phy_attach_direct()
if (!d->driver)
d->driver = &genphy_driver.mdiodrv.driver;
device_bind_driver()
device_links_force_bind()
device_bind_driver() does not wait for suppliers:
device_links_force_bind() drops any managed supplier link that is not
available yet, and driver_bound() purges the PHY's remaining fwnode
supplier links. Nothing re-probes the PHY once the PSE controller shows
up, so the port keeps running on genphy until a rebind or a reboot.
The link is not needed for correctness. PSE is not a resource the
consumer must have before it probes: phylib looks a PI up when its
controller becomes available and releases it when the controller goes
away, and of_pse_control_get() is the only reader of the property.
Mark it FWLINK_FLAG_IGNORE, as post-init-providers already is. That
drops the dependency entirely: fw_devlink_create_devlink() returns
early, so no device link is made, the fwnode link is deleted at the
consumer's device_add() like any other that has been handled, and
fw_devlink neither gates probe on it nor uses it for cycle detection.
Two things ride on the link today, and both go with it, because it is
managed. of_link_property() passes no get_con_dev for parse_pses, so
the fwnode link sits on the PHY's own node, which is the PHY device's
fwnode by the time it is registered, and fw_devlink_create_devlink()
takes its first branch:
if (con->fwnode == link->consumer)
flags = fw_devlink_get_flags(link->flags);
else
flags = FW_DEVLINK_FLAGS_PERMISSIVE;
With link->flags clear that returns fw_devlink_flags, by default
FW_DEVLINK_FLAGS_RPM, which carries neither DL_FLAG_STATELESS nor
DL_FLAG_SYNC_STATE_ONLY, so device_link_add() makes it DL_FLAG_MANAGED.
Unbinding the PSE controller therefore releases the PHY's driver today,
and DL_FLAG_AUTOPROBE_CONSUMER probes the PHY once the controller
binds. Both are given up on purpose: phylib's notifier takes the PI
away on unbind and hands it back on bind without tearing the PHY driver
down, and with no deferral left there is nothing for an autoprobe to
wait for. DL_FLAG_PM_RUNTIME and the dpm_list reordering go without
consequence, since no PSE driver implements PM ops, .shutdown,
sync_state or runtime PM.
This change is not a no-op while fwnode_mdio still does the lookup.
fwnode_mdiobus_register_phy() registers the PHY before it looks the PI
up, so the device link is created regardless and becomes active as
soon as the controller is bound; the unbind cascade above goes away
from here on. The link also held the PHY's driver back on each of those
retries while the controller was unbound. Without it the driver probes
inside device_add() - phy drivers are PROBE_FORCE_SYNCHRONOUS - and is
removed again when the lookup defers, once per retry of the MDIO bus
owner, until the PSE controller binds or fwnode_mdio stops doing the
lookup.
Signed-off-by: Carlo Szelinsky <github@xxxxxxxxxxxx>
---
drivers/of/property.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/of/property.c b/drivers/of/property.c
index 72cf12907de0..5ec0f05b87ac 100644
--- a/drivers/of/property.c
+++ b/drivers/of/property.c
@@ -1564,7 +1564,10 @@ static const struct supplier_bindings of_supplier_bindings[] = {
{ .parse_prop = parse_backlight, },
{ .parse_prop = parse_panel, },
{ .parse_prop = parse_msi_parent, },
- { .parse_prop = parse_pses, },
+ {
+ .parse_prop = parse_pses,
+ .fwlink_flags = FWLINK_FLAG_IGNORE,
+ },
{ .parse_prop = parse_power_supplies, },
{ .parse_prop = parse_mmc_pwrseq, },
{ .parse_prop = parse_gpio_compat, },
--
2.43.0