From 55ce1d64aa51baecdd26d56e3efb250c9671e988 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Tue, 25 Nov 2025 13:48:44 +0200 Subject: [PATCH] phy: lynx-28g: distinguish between 10GBASE-R and USXGMII The driver does not handle well protocol switching to or from USXGMII, because it conflates it with 10GBase-R. In the expected USXGMII use case, that isn't a problem, because SerDes protocol switching performed by the lynx-28g driver is not necessary, because USXGMII natively supports multiple speeds, as opposed to SFP modules using 1000Base-X or 10GBase-R which require switching between the 2. That being said, let's be explicit, and in case someone requests a protocol change which involves USXGMII, let's do the right thing. Signed-off-by: Vladimir Oltean Link: https://patch.msgid.link/20251125114847.804961-13-vladimir.oltean@nxp.com Signed-off-by: Vinod Koul --- drivers/phy/freescale/phy-fsl-lynx-28g.c | 87 ++++++++++++++++++++---- 1 file changed, 74 insertions(+), 13 deletions(-) diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index 29ab7213a35d..01589a2bfe74 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -246,7 +246,8 @@ enum lynx_28g_proto_sel { enum lynx_lane_mode { LANE_MODE_UNKNOWN, LANE_MODE_1000BASEX_SGMII, - LANE_MODE_10GBASER_USXGMII, + LANE_MODE_10GBASER, + LANE_MODE_USXGMII, LANE_MODE_MAX, }; @@ -316,7 +317,35 @@ static const struct lynx_28g_proto_conf lynx_28g_proto_conf[LANE_MODE_MAX] = { .smp_autoz_d1r = 0, .smp_autoz_eg1r = 0, }, - [LANE_MODE_10GBASER_USXGMII] = { + [LANE_MODE_USXGMII] = { + .proto_sel = LNaGCR0_PROTO_SEL_XFI, + .if_width = LNaGCR0_IF_WIDTH_20_BIT, + .teq_type = EQ_TYPE_2TAP, + .sgn_preq = 1, + .ratio_preq = 0, + .sgn_post1q = 1, + .ratio_post1q = 3, + .amp_red = 7, + .adpt_eq = 48, + .enter_idle_flt_sel = 0, + .exit_idle_flt_sel = 0, + .data_lost_th_sel = 0, + .gk2ovd = 0, + .gk3ovd = 0, + .gk4ovd = 0, + .gk2ovd_en = 0, + .gk3ovd_en = 0, + .gk4ovd_en = 0, + .eq_offset_ovd = 0x1f, + .eq_offset_ovd_en = 0, + .eq_offset_rng_dbl = 1, + .eq_blw_sel = 1, + .eq_boost = 0, + .spare_in = 0, + .smp_autoz_d1r = 2, + .smp_autoz_eg1r = 0, + }, + [LANE_MODE_10GBASER] = { .proto_sel = LNaGCR0_PROTO_SEL_XFI, .if_width = LNaGCR0_IF_WIDTH_20_BIT, .teq_type = EQ_TYPE_2TAP, @@ -413,8 +442,10 @@ static const char *lynx_lane_mode_str(enum lynx_lane_mode lane_mode) switch (lane_mode) { case LANE_MODE_1000BASEX_SGMII: return "1000Base-X/SGMII"; - case LANE_MODE_10GBASER_USXGMII: - return "10GBase-R/USXGMII"; + case LANE_MODE_10GBASER: + return "10GBase-R"; + case LANE_MODE_USXGMII: + return "USXGMII"; default: return "unknown"; } @@ -427,8 +458,9 @@ static enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf) case PHY_INTERFACE_MODE_1000BASEX: return LANE_MODE_1000BASEX_SGMII; case PHY_INTERFACE_MODE_10GBASER: + return LANE_MODE_10GBASER; case PHY_INTERFACE_MODE_USXGMII: - return LANE_MODE_10GBASER_USXGMII; + return LANE_MODE_USXGMII; default: return LANE_MODE_UNKNOWN; } @@ -496,7 +528,8 @@ static void lynx_28g_lane_set_nrate(struct lynx_28g_lane *lane, break; case PLLnCR1_FRATE_10G_20GVCO: switch (lane_mode) { - case LANE_MODE_10GBASER_USXGMII: + case LANE_MODE_10GBASER: + case LANE_MODE_USXGMII: lynx_28g_lane_rmw(lane, LNaTGCR0, FIELD_PREP(LNaTGCR0_N_RATE, LNaTGCR0_N_RATE_FULL), LNaTGCR0_N_RATE); @@ -594,7 +627,8 @@ static int lynx_28g_get_pccr(enum lynx_lane_mode lane_mode, int lane, pccr->width = 4; pccr->shift = SGMII_CFG(lane); break; - case LANE_MODE_10GBASER_USXGMII: + case LANE_MODE_USXGMII: + case LANE_MODE_10GBASER: pccr->offset = PCCC; pccr->width = 4; pccr->shift = SXGMII_CFG(lane); @@ -611,13 +645,32 @@ static int lynx_28g_get_pcvt_offset(int lane, enum lynx_lane_mode lane_mode) switch (lane_mode) { case LANE_MODE_1000BASEX_SGMII: return SGMIIaCR0(lane); - case LANE_MODE_10GBASER_USXGMII: + case LANE_MODE_USXGMII: + case LANE_MODE_10GBASER: return SXGMIIaCR0(lane); default: return -EOPNOTSUPP; } } +static int lynx_pccr_read(struct lynx_28g_lane *lane, enum lynx_lane_mode mode, + u32 *val) +{ + struct lynx_28g_priv *priv = lane->priv; + struct lynx_pccr pccr; + u32 tmp; + int err; + + err = lynx_28g_get_pccr(mode, lane->id, &pccr); + if (err) + return err; + + tmp = lynx_28g_read(priv, pccr.offset); + *val = (tmp >> pccr.shift) & GENMASK(pccr.width - 1, 0); + + return 0; +} + static int lynx_pccr_write(struct lynx_28g_lane *lane, enum lynx_lane_mode lane_mode, u32 val) { @@ -829,8 +882,11 @@ static int lynx_28g_lane_enable_pcvt(struct lynx_28g_lane *lane, case LANE_MODE_1000BASEX_SGMII: val |= PCC8_SGMIIa_CFG; break; - case LANE_MODE_10GBASER_USXGMII: - val |= PCCC_SXGMIIn_CFG | PCCC_SXGMIIn_XFI; + case LANE_MODE_10GBASER: + val |= PCCC_SXGMIIn_XFI; + fallthrough; + case LANE_MODE_USXGMII: + val |= PCCC_SXGMIIn_CFG; break; default: break; @@ -955,7 +1011,8 @@ static void lynx_28g_pll_read_configuration(struct lynx_28g_priv *priv) break; case PLLnCR1_FRATE_10G_20GVCO: /* 10.3125GHz clock net */ - __set_bit(LANE_MODE_10GBASER_USXGMII, pll->supported); + __set_bit(LANE_MODE_10GBASER, pll->supported); + __set_bit(LANE_MODE_USXGMII, pll->supported); break; default: /* 6GHz, 12.890625GHz, 8GHz */ @@ -1000,7 +1057,7 @@ static void lynx_28g_cdr_lock_check(struct work_struct *work) static void lynx_28g_lane_read_configuration(struct lynx_28g_lane *lane) { - u32 pss, protocol; + u32 pccr, pss, protocol; pss = lynx_28g_lane_read(lane, LNaPSS); protocol = FIELD_GET(LNaPSS_TYPE, pss); @@ -1009,7 +1066,11 @@ static void lynx_28g_lane_read_configuration(struct lynx_28g_lane *lane) lane->mode = LANE_MODE_1000BASEX_SGMII; break; case LNaPSS_TYPE_XFI: - lane->mode = LANE_MODE_10GBASER_USXGMII; + lynx_pccr_read(lane, LANE_MODE_10GBASER, &pccr); + if (pccr & PCCC_SXGMIIn_XFI) + lane->mode = LANE_MODE_10GBASER; + else + lane->mode = LANE_MODE_USXGMII; break; default: lane->mode = LANE_MODE_UNKNOWN; -- 2.47.3