]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
phy: lynx-28g: improve phy_validate() procedure
authorVladimir Oltean <vladimir.oltean@nxp.com>
Wed, 10 Jun 2026 15:19:49 +0000 (18:19 +0300)
committerVinod Koul <vkoul@kernel.org>
Thu, 11 Jun 2026 07:09:47 +0000 (12:39 +0530)
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@shell.armlinux.org.uk/

Signed-off-by: Vladimir Oltean <vladimir.oltean@nxp.com>
Link: https://patch.msgid.link/20260610151952.2141019-14-vladimir.oltean@nxp.com
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/phy/freescale/phy-fsl-lynx-28g.c
drivers/phy/freescale/phy-fsl-lynx-core.c
drivers/phy/freescale/phy-fsl-lynx-core.h

index 50b991870edba3dcfa6be939f8bebab10007ebdd..38afcd081a2a75d7b418dc0dcd2e381201a032f2 100644 (file)
@@ -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 @@ out:
        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);
index 226b2af2b599f6e10e5a6ea7b9be85136aac3fe9..1e411bfab404167e8b4a745dc0335d28c3b012c5 100644 (file)
@@ -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;
index 3d9508dfb2c1c6e89b7f10a6f43147becad6c8c8..37fa4b544faa31d4337477e2c948ab94d98e1f4d 100644 (file)
@@ -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);