[PATCH v2 phy-next 12/15] phy: lynx-28g: improve phy_validate() procedure
From: Vladimir Oltean
Date: Fri May 29 2026 - 13:20:38 EST
lynx_28g_validate() suffers from the following shortcomings:
- Changing the protocol should not be possible if the source protocol of
the lane is unsupported. This is because lynx_28g_proto_conf[] only
covers the register deltas between any pair of supported lane modes,
but that delta is probably incomplete if the source protocol is, say,
PCIe (which is currently assimilated by the driver to
LANE_MODE_UNKNOWN).
lynx_28g_proto_conf() does refuse changing the protocol if the current
one is unsupported, but we shouldn't advertise it via phy_validate()
at all.
The phy_set_mode_ext() call should perform the exact same
verifications as phy_validate() did, in case the caller bypassed
phy_validate(). So we need to centralize the logic into a common
validation. But lynx_28g_set_mode() later needs the lane_mode that
this validation needs to compute anyway, so name the common helper
lynx_phy_mode_to_lane_mode() and let it return that lane_mode.
- Future core sanity checks on phy_validate() will want to differentiate
the case where this optional method is not implemented from the case
where the mode/submode is really not supported. So we shouldn't return
-EOPNOTSUPP from lynx_28g_validate(), but -EINVAL to signal that we do
implement the operation:
https://lore.kernel.org/linux-phy/aY2lFTIALH7qEJmM@xxxxxxxxxxxxxxxxxxxxx/
Signed-off-by: Vladimir Oltean <vladimir.oltean@xxxxxxx>
---
v1->v2: patch is new
---
drivers/phy/freescale/phy-fsl-lynx-28g.c | 38 +++++++----------------
drivers/phy/freescale/phy-fsl-lynx-core.c | 30 ++++++++++++++++++
drivers/phy/freescale/phy-fsl-lynx-core.h | 2 ++
3 files changed, 43 insertions(+), 27 deletions(-)
diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c
index 50b991870edb..38afcd081a2a 100644
--- a/drivers/phy/freescale/phy-fsl-lynx-28g.c
+++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c
@@ -968,22 +968,22 @@ static int lynx_28g_lane_enable_pcvt(struct lynx_28g_lane *lane,
return err;
}
+static int lynx_28g_validate(struct phy *phy, enum phy_mode mode, int submode,
+ union phy_configure_opts *opts)
+{
+ return lynx_phy_mode_to_lane_mode(phy, mode, submode, NULL);
+}
+
static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode)
{
- struct lynx_28g_lane *lane = phy_get_drvdata(phy);
+ struct lynx_lane *lane = phy_get_drvdata(phy);
int powered_up = lane->powered_up;
enum lynx_lane_mode lane_mode;
- int err = 0;
-
- if (mode != PHY_MODE_ETHERNET)
- return -EOPNOTSUPP;
-
- if (lane->mode == LANE_MODE_UNKNOWN)
- return -EOPNOTSUPP;
+ int err;
- lane_mode = phy_interface_to_lane_mode(submode);
- if (!lynx_lane_supports_mode(lane, lane_mode))
- return -EOPNOTSUPP;
+ err = lynx_phy_mode_to_lane_mode(phy, mode, submode, &lane_mode);
+ if (err)
+ return err;
if (lane_mode == lane->mode)
return 0;
@@ -1014,22 +1014,6 @@ static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode)
return err;
}
-static int lynx_28g_validate(struct phy *phy, enum phy_mode mode, int submode,
- union phy_configure_opts *opts __always_unused)
-{
- struct lynx_28g_lane *lane = phy_get_drvdata(phy);
- enum lynx_lane_mode lane_mode;
-
- if (mode != PHY_MODE_ETHERNET)
- return -EOPNOTSUPP;
-
- lane_mode = phy_interface_to_lane_mode(submode);
- if (!lynx_lane_supports_mode(lane, lane_mode))
- return -EOPNOTSUPP;
-
- return 0;
-}
-
static int lynx_28g_init(struct phy *phy)
{
struct lynx_28g_lane *lane = phy_get_drvdata(phy);
diff --git a/drivers/phy/freescale/phy-fsl-lynx-core.c b/drivers/phy/freescale/phy-fsl-lynx-core.c
index 92f8c82f5b8d..a9fda85a7783 100644
--- a/drivers/phy/freescale/phy-fsl-lynx-core.c
+++ b/drivers/phy/freescale/phy-fsl-lynx-core.c
@@ -89,6 +89,36 @@ bool lynx_lane_supports_mode(struct lynx_lane *lane, enum lynx_lane_mode mode)
}
EXPORT_SYMBOL_NS_GPL(lynx_lane_supports_mode, "PHY_FSL_LYNX");
+/* Translate the mode/submode from phy_validate() and phy_set_mode_ext() to a
+ * lane_mode and return 0 if it is supported and we can transition to it from
+ * the current lane mode, or return negative error otherwise.
+ */
+int lynx_phy_mode_to_lane_mode(struct phy *phy, enum phy_mode mode,
+ int submode, enum lynx_lane_mode *lane_mode)
+{
+ struct lynx_lane *lane = phy_get_drvdata(phy);
+ enum lynx_lane_mode tmp_lane_mode;
+
+ /* The protocol configuration tables are incomplete for full lane
+ * reconfiguration from an arbitrary protocol.
+ */
+ if (lane->mode == LANE_MODE_UNKNOWN)
+ return -EINVAL;
+
+ if (mode != PHY_MODE_ETHERNET)
+ return -EINVAL;
+
+ tmp_lane_mode = phy_interface_to_lane_mode(submode);
+ if (!lynx_lane_supports_mode(lane, tmp_lane_mode))
+ return -EINVAL;
+
+ if (lane_mode)
+ *lane_mode = tmp_lane_mode;
+
+ return 0;
+}
+EXPORT_SYMBOL_NS_GPL(lynx_phy_mode_to_lane_mode, "PHY_FSL_LYNX");
+
struct lynx_pll *lynx_pll_get(struct lynx_priv *priv, enum lynx_lane_mode mode)
{
struct lynx_pll *pll;
diff --git a/drivers/phy/freescale/phy-fsl-lynx-core.h b/drivers/phy/freescale/phy-fsl-lynx-core.h
index 3d9508dfb2c1..37fa4b544faa 100644
--- a/drivers/phy/freescale/phy-fsl-lynx-core.h
+++ b/drivers/phy/freescale/phy-fsl-lynx-core.h
@@ -113,6 +113,8 @@ void lynx_remove(struct platform_device *pdev);
const char *lynx_lane_mode_str(enum lynx_lane_mode lane_mode);
enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf);
bool lynx_lane_supports_mode(struct lynx_lane *lane, enum lynx_lane_mode mode);
+int lynx_phy_mode_to_lane_mode(struct phy *phy, enum phy_mode mode,
+ int submode, enum lynx_lane_mode *lane_mode);
struct lynx_pll *lynx_pll_get(struct lynx_priv *priv, enum lynx_lane_mode mode);
--
2.34.1