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 <vladimir.oltean@nxp.com>
---
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 8e38acd30224..fef2b2d7d170 100644
--- a/drivers/phy/freescale/phy-fsl-lynx-28g.c
+++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c
@@ -1295,8 +1295,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