Managed lanes are supposed to have power management through phy_power_on() and phy_power_off(). Unmanaged lanes are supposed to be always powered on, because they might have a consumer which doesn't use this SerDes driver, and we don't want to break it. A lane is initially unmanaged, and becomes managed when phy_init() is called on it. It is normal for consumer drivers to call both phy_init() and phy_exit(), in a balanced way. This ensures the phy->init_count from the phy core is brought back to zero, for example during -EPROBE_DEFER in the consumer, the lane temporarily becomes unmanaged and then managed again. Given the above requirement for consumers, it also imposes a requirement for the SerDes driver to implement the exit() operation. Otherwise, a balanced set of phy_init() and phy_exit() calls from the consumer will effectively result in multiple lynx_28g_init() calls as seen by the SerDes and nothing else. That actually doesn't work - the driver can't power down a SerDes lane which is actually powered down, so such a call sequence would hang the kernel. No consumer driver currently uses phy_exit(), so the above problem does not yet trigger, but in preparation for its introduction without any regressions, it is necessary to add lynx_28g_exit() as the mirror of lynx_28g_init(). Signed-off-by: Vladimir Oltean --- part 1 -> part 2: none Patch last made its appearance in v3 from part 1: https://lore.kernel.org/linux-phy/20250926180505.760089-18-vladimir.oltean@nxp.com/ (old) part 1 change log: v2->v3: propagate the potential -ETIMEDOUT error code from lynx_28g_power_on() to the caller v1->v2: slight commit message reword drivers/phy/freescale/phy-fsl-lynx-28g.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index 4c20d5d42983..f1d0e0f29fcf 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -1296,8 +1296,23 @@ static int lynx_28g_init(struct phy *phy) return lynx_28g_power_off(phy); } +static int lynx_28g_exit(struct phy *phy) +{ + struct lynx_28g_lane *lane = phy_get_drvdata(phy); + + /* The lane returns to the state where it isn't managed by the + * consumer, so we must treat is as if it isn't initialized, and always + * powered on. + */ + lane->init = false; + lane->powered_up = false; + + return lynx_28g_power_on(phy); +} + static const struct phy_ops lynx_28g_ops = { .init = lynx_28g_init, + .exit = lynx_28g_exit, .power_on = lynx_28g_power_on, .power_off = lynx_28g_power_off, .set_mode = lynx_28g_set_mode, -- 2.34.1