From: Vladimir Oltean Date: Mon, 11 May 2026 15:00:22 +0000 (+0300) Subject: phy: lynx-28g: probe on per-SoC and per-instance compatible strings X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8c57247f5d9734d617862d320bd3894eec898298;p=thirdparty%2Fkernel%2Flinux.git phy: lynx-28g: probe on per-SoC and per-instance compatible strings Add driver support for probing on the new, per-instance and per-SoC bindings, which provide the main benefit that they allow rejecting unsupported protocols per lane (10GbE on SerDes 2 lanes 0-5), but they also allow avoiding the creation of PHYs for lanes that don't exist (LX2162A lanes 0-3). For old device trees with just "fsl,lynx-28g", the only things that change are: - a probe time warning/encouragement to update the device tree. This is warranted by the fact that using "fsl,lynx-28g" may already provide incorrect behaviour (undetected absent 10GbE support on LX2160A SerDes 2 lanes 0-5). But we retain bug compatibility nonetheless. - the feature set is frozen in time (e.g. no 25GbE). Since we cannot guarantee that this protocol will work on a lane, just err on the safe side and don't offer it (and require a device tree update to get it). In terms of code, the lynx_28g_supports_lane_mode() function prototype changes. It was a SerDes-global function and now becomes per lane, to reflect the specific capabilities each instance may have. The implementation goes through priv->info->lane_supports_mode(). Signed-off-by: Vladimir Oltean Reviewed-by: Ioana Ciornei Tested-by: Josua Mayer Link: https://patch.msgid.link/20260511150023.1903577-5-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index 6d0c395d20e50..5eddc2723e782 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -446,9 +446,15 @@ struct lynx_28g_lane { enum lynx_lane_mode mode; }; +struct lynx_info { + bool (*lane_supports_mode)(int lane, enum lynx_lane_mode mode); + int first_lane; +}; + struct lynx_28g_priv { void __iomem *base; struct device *dev; + const struct lynx_info *info; /* Serialize concurrent access to registers shared between lanes, * like PCCn */ @@ -513,11 +519,18 @@ static enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf) } } -static bool lynx_28g_supports_lane_mode(struct lynx_28g_priv *priv, +/* A lane mode is supported if we have a PLL that can provide its required + * clock net, and if there is a protocol converter for that mode on that lane. + */ +static bool lynx_28g_supports_lane_mode(struct lynx_28g_lane *lane, enum lynx_lane_mode mode) { + struct lynx_28g_priv *priv = lane->priv; int i; + if (!priv->info->lane_supports_mode(lane->id, mode)) + return false; + for (i = 0; i < LYNX_28G_NUM_PLL; i++) { if (PLLnRSTCTL_DIS(priv->pll[i].rstctl)) continue; @@ -783,6 +796,87 @@ static int lynx_28g_get_pcvt_offset(int lane, enum lynx_lane_mode lane_mode) } } +static bool lx2160a_serdes1_lane_supports_mode(int lane, + enum lynx_lane_mode mode) +{ + return true; +} + +static bool lx2160a_serdes2_lane_supports_mode(int lane, + enum lynx_lane_mode mode) +{ + switch (mode) { + case LANE_MODE_1000BASEX_SGMII: + return true; + case LANE_MODE_USXGMII: + case LANE_MODE_10GBASER: + return lane == 6 || lane == 7; + default: + return false; + } +} + +static bool lx2160a_serdes3_lane_supports_mode(int lane, + enum lynx_lane_mode mode) +{ + /* + * Non-networking SerDes, and this driver supports only + * networking protocols + */ + return false; +} + +static bool lx2162a_serdes1_lane_supports_mode(int lane, + enum lynx_lane_mode mode) +{ + return true; +} + +static bool lx2162a_serdes2_lane_supports_mode(int lane, + enum lynx_lane_mode mode) +{ + return lx2160a_serdes2_lane_supports_mode(lane, mode); +} + +/* Feature set is not expected to grow for the deprecated compatible string */ +static bool lynx_28g_compat_lane_supports_mode(int lane, + enum lynx_lane_mode mode) +{ + switch (mode) { + case LANE_MODE_1000BASEX_SGMII: + case LANE_MODE_USXGMII: + case LANE_MODE_10GBASER: + return true; + default: + return false; + } +} + +static const struct lynx_info lynx_info_compat = { + .lane_supports_mode = lynx_28g_compat_lane_supports_mode, +}; + +static const struct lynx_info lynx_info_lx2160a_serdes1 = { + .lane_supports_mode = lx2160a_serdes1_lane_supports_mode, +}; + +static const struct lynx_info lynx_info_lx2160a_serdes2 = { + .lane_supports_mode = lx2160a_serdes2_lane_supports_mode, +}; + +static const struct lynx_info lynx_info_lx2160a_serdes3 = { + .lane_supports_mode = lx2160a_serdes3_lane_supports_mode, +}; + +static const struct lynx_info lynx_info_lx2162a_serdes1 = { + .lane_supports_mode = lx2162a_serdes1_lane_supports_mode, + .first_lane = 4, +}; + +static const struct lynx_info lynx_info_lx2162a_serdes2 = { + .lane_supports_mode = lx2162a_serdes2_lane_supports_mode, +}; + static int lynx_pccr_read(struct lynx_28g_lane *lane, enum lynx_lane_mode mode, u32 *val) { @@ -1035,7 +1129,6 @@ static int lynx_28g_lane_enable_pcvt(struct lynx_28g_lane *lane, 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_28g_priv *priv = lane->priv; int powered_up = lane->powered_up; enum lynx_lane_mode lane_mode; int err = 0; @@ -1047,7 +1140,7 @@ static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode) return -EOPNOTSUPP; lane_mode = phy_interface_to_lane_mode(submode); - if (!lynx_28g_supports_lane_mode(priv, lane_mode)) + if (!lynx_28g_supports_lane_mode(lane, lane_mode)) return -EOPNOTSUPP; if (lane_mode == lane->mode) @@ -1083,14 +1176,13 @@ 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); - struct lynx_28g_priv *priv = lane->priv; enum lynx_lane_mode lane_mode; if (mode != PHY_MODE_ETHERNET) return -EOPNOTSUPP; lane_mode = phy_interface_to_lane_mode(submode); - if (!lynx_28g_supports_lane_mode(priv, lane_mode)) + if (!lynx_28g_supports_lane_mode(lane, lane_mode)) return -EOPNOTSUPP; return 0; @@ -1183,7 +1275,7 @@ static void lynx_28g_cdr_lock_check(struct work_struct *work) u32 rrstctl; int err, i; - for (i = 0; i < LYNX_28G_NUM_LANE; i++) { + for (i = priv->info->first_lane; i < LYNX_28G_NUM_LANE; i++) { lane = &priv->lane[i]; if (!lane->phy) continue; @@ -1253,7 +1345,8 @@ static struct phy *lynx_28g_xlate(struct device *dev, idx = args->args[0]; - if (WARN_ON(idx >= LYNX_28G_NUM_LANE)) + if (WARN_ON(idx >= LYNX_28G_NUM_LANE || + idx < priv->info->first_lane)) return ERR_PTR(-EINVAL); return priv->lane[idx].phy; @@ -1297,10 +1390,18 @@ static int lynx_28g_probe(struct platform_device *pdev) return -ENOMEM; priv->dev = dev; + priv->info = of_device_get_match_data(dev); dev_set_drvdata(dev, priv); spin_lock_init(&priv->pcc_lock); INIT_DELAYED_WORK(&priv->cdr_check, lynx_28g_cdr_lock_check); + /* + * If we get here it means we probed on a device tree where + * "fsl,lynx-28g" wasn't the fallback, but the sole compatible string. + */ + if (priv->info == &lynx_info_compat) + dev_warn(dev, "Please update device tree to use per-device compatible strings\n"); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); @@ -1323,7 +1424,7 @@ static int lynx_28g_probe(struct platform_device *pdev) return -EINVAL; } - if (reg >= LYNX_28G_NUM_LANE) { + if (reg < priv->info->first_lane || reg >= LYNX_28G_NUM_LANE) { dev_err(dev, "\"reg\" property out of range for %pOF\n", child); of_node_put(child); return -EINVAL; @@ -1336,7 +1437,7 @@ static int lynx_28g_probe(struct platform_device *pdev) } } } else { - for (int i = 0; i < LYNX_28G_NUM_LANE; i++) { + for (int i = priv->info->first_lane; i < LYNX_28G_NUM_LANE; i++) { err = lynx_28g_probe_lane(priv, i, NULL); if (err) return err; @@ -1362,7 +1463,12 @@ static void lynx_28g_remove(struct platform_device *pdev) } static const struct of_device_id lynx_28g_of_match_table[] = { - { .compatible = "fsl,lynx-28g" }, + { .compatible = "fsl,lx2160a-serdes1", .data = &lynx_info_lx2160a_serdes1 }, + { .compatible = "fsl,lx2160a-serdes2", .data = &lynx_info_lx2160a_serdes2 }, + { .compatible = "fsl,lx2160a-serdes3", .data = &lynx_info_lx2160a_serdes3 }, + { .compatible = "fsl,lx2162a-serdes1", .data = &lynx_info_lx2162a_serdes1 }, + { .compatible = "fsl,lx2162a-serdes2", .data = &lynx_info_lx2162a_serdes2 }, + { .compatible = "fsl,lynx-28g", .data = &lynx_info_compat }, /* fallback, keep last */ { }, }; MODULE_DEVICE_TABLE(of, lynx_28g_of_match_table);