#define LYNX_28G_NUM_LANE 8
#define LYNX_28G_NUM_PLL 2
-#define LNa_PCC_OFFSET(lane) (4 * (LYNX_28G_NUM_LANE - (lane->id) - 1))
-
-/* General registers per SerDes block */
+/* SoC IP wrapper for protocol converters */
#define PCC8 0x10a0
-#define PCC8_SGMIInCFG(lane, x) (((x) & GENMASK(2, 0)) << LNa_PCC_OFFSET(lane))
-#define PCC8_SGMIInCFG_EN(lane) PCC8_SGMIInCFG(lane, 1)
-#define PCC8_SGMIInCFG_MSK(lane) PCC8_SGMIInCFG(lane, GENMASK(2, 0))
-#define PCC8_SGMIIn_KX(lane, x) ((((x) << 3) & BIT(3)) << LNa_PCC_OFFSET(lane))
-#define PCC8_SGMIIn_KX_MSK(lane) PCC8_SGMIIn_KX(lane, 1)
-#define PCC8_MSK(lane) PCC8_SGMIInCFG_MSK(lane) | \
- PCC8_SGMIIn_KX_MSK(lane)
+#define PCC8_SGMIIa_KX BIT(3)
+#define PCC8_SGMIIa_CFG BIT(0)
#define PCCC 0x10b0
-#define PCCC_SXGMIInCFG(lane, x) (((x) & GENMASK(2, 0)) << LNa_PCC_OFFSET(lane))
-#define PCCC_SXGMIInCFG_EN(lane) PCCC_SXGMIInCFG(lane, 1)
-#define PCCC_SXGMIInCFG_MSK(lane) PCCC_SXGMIInCFG(lane, GENMASK(2, 0))
-#define PCCC_SXGMIInCFG_XFI(lane, x) ((((x) << 3) & BIT(3)) << LNa_PCC_OFFSET(lane))
-#define PCCC_SXGMIInCFG_XFI_MSK(lane) PCCC_SXGMIInCFG_XFI(lane, 1)
-#define PCCC_MSK(lane) PCCC_SXGMIInCFG_MSK(lane) | \
- PCCC_SXGMIInCFG_XFI_MSK(lane)
+#define PCCC_SXGMIIn_XFI BIT(3)
+#define PCCC_SXGMIIn_CFG BIT(0)
#define PCCD 0x10b4
-#define PCCD_E25GnCFG(lane, x) (((x) & GENMASK(2, 0)) << LNa_PCCD_OFFSET(lane))
-#define PCCD_E25GnCFG_EN(lane) PCCD_E25GnCFG(lane, 1)
-#define PCCD_E25GnCFG_MSK(lane) PCCD_E25GnCFG(lane, GENMASK(2, 0))
-#define PCCD_MSK(lane) PCCD_E25GnCFG_MSK(lane)
+#define PCCD_E25Gn_CFG BIT(0)
+
+#define PCCE 0x10b8
+#define PCCE_E40Gn_LRV BIT(3)
+#define PCCE_E40Gn_CFG BIT(0)
+#define PCCE_E50Gn_LRV BIT(3)
+#define PCCE_E50GnCFG BIT(0)
+#define PCCE_E100Gn_LRV BIT(3)
+#define PCCE_E100Gn_CFG BIT(0)
+
+#define SGMII_CFG(id) (28 - (id) * 4) /* Offset into PCC8 */
+#define SXGMII_CFG(id) (28 - (id) * 4) /* Offset into PCCC */
+#define E25G_CFG(id) (28 - (id) * 4) /* Offset into PCCD */
+#define E40G_CFG(id) (28 - (id) * 4) /* Offset into PCCE */
+#define E50G_CFG(id) (20 - (id) * 4) /* Offset into PCCE */
+#define E100G_CFG(id) (12 - (id) * 4) /* Offset into PCCE */
/* Per PLL registers */
#define PLLnRSTCTL(pll) (0x400 + (pll) * 0x100 + 0x0)
#define LNaTECR0_EQ_POST1Q GENMASK(12, 8)
#define LNaTECR0_EQ_AMP_RED GENMASK(5, 0)
+#define LNaTECR1(lane) (0x800 + (lane) * 0x100 + 0x34)
+#define LNaTECR1_EQ_ADPT_EQ_DRVR_DIS BIT(31)
+#define LNaTECR1_EQ_ADPT_EQ GENMASK(29, 24)
+
/* Lane a Rx Reset Control Register */
#define LNaRRSTCTL(lane) (0x800 + (lane) * 0x100 + 0x40)
#define LNaRRSTCTL_HLT_REQ BIT(27)
#define LNaRECR2_EQ_BIN_DATA_AVG_TC GENMASK(5, 4)
#define LNaRECR2_SPARE_IN GENMASK(1, 0)
+#define LNaRECR3(lane) (0x800 + (lane) * 0x100 + 0x5c)
+#define LNaRECR3_EQ_SNAP_START BIT(31)
+#define LNaRECR3_EQ_SNAP_DONE BIT(30)
+#define LNaRECR3_EQ_GAINK2_HF_STAT GENMASK(28, 24)
+#define LNaRECR3_EQ_GAINK3_MF_STAT GENMASK(20, 16)
+#define LNaRECR3_SPARE_OUT GENMASK(13, 12)
+#define LNaRECR3_EQ_GAINK4_LF_STAT GENMASK(4, 0)
+
+#define LNaRECR4(lane) (0x800 + (lane) * 0x100 + 0x60)
+#define LNaRECR4_BLW_STAT GENMASK(28, 24)
+#define LNaRECR4_EQ_OFFSET_STAT GENMASK(21, 16)
+#define LNaRECR4_EQ_BIN_DATA_SEL GENMASK(15, 12)
+#define LNaRECR4_EQ_BIN_DATA GENMASK(8, 0) /* bit 9 is reserved */
+#define LNaRECR4_EQ_BIN_DATA_SGN BIT(8)
+
#define LNaRSCCR0(lane) (0x800 + (lane) * 0x100 + 0x74)
#define LNaRSCCR0_SMP_OFF_EN BIT(31)
#define LNaRSCCR0_SMP_OFF_OV_EN BIT(30)
#define LNaRSCCR0_SMP_AUTOZ_EG1R GENMASK(5, 4)
#define LNaRSCCR0_SMP_AUTOZ_EG1F GENMASK(1, 0)
+#define LNaTCSR0(lane) (0x800 + (lane) * 0x100 + 0xa0)
+#define LNaTCSR0_SD_STAT_OBS_EN BIT(31)
+#define LNaTCSR0_SD_LPBK_SEL GENMASK(29, 28)
+
#define LNaPSS(lane) (0x1000 + (lane) * 0x4)
#define LNaPSS_TYPE GENMASK(30, 24)
-#define LNaPSS_TYPE_SGMII 0x4
-#define LNaPSS_TYPE_XFI 0x28
+#define LNaPSS_TYPE_SGMII (PROTO_SEL_SGMII_BASEX_KX << 2)
+#define LNaPSS_TYPE_XFI (PROTO_SEL_XFI_10GBASER_KR_SXGMII << 2)
+#define LNaPSS_TYPE_40G ((PROTO_SEL_XFI_10GBASER_KR_SXGMII << 2) | 3)
+#define LNaPSS_TYPE_25G (PROTO_SEL_25G_50G_100G << 2)
+#define LNaPSS_TYPE_100G ((PROTO_SEL_25G_50G_100G << 2) | 2)
+/* MDEV_PORT is at the same bitfield address for all protocol converters */
+#define MDEV_PORT GENMASK(31, 27)
+
+#define SGMIIaCR0(lane) (0x1800 + (lane) * 0x10)
#define SGMIIaCR1(lane) (0x1804 + (lane) * 0x10)
#define SGMIIaCR1_SGPCS_EN BIT(11)
+#define ANLTaCR0(lane) (0x1a00 + (lane) * 0x10)
+#define ANLTaCR1(lane) (0x1a04 + (lane) * 0x10)
+
+#define SXGMIIaCR0(lane) (0x1a80 + (lane) * 0x10)
+#define SXGMIIaCR0_RST BIT(31)
+#define SXGMIIaCR0_PD BIT(30)
+
+#define SXGMIIaCR1(lane) (0x1a84 + (lane) * 0x10)
+
+#define E25GaCR0(lane) (0x1b00 + (lane) * 0x10)
+#define E25GaCR0_RST BIT(31)
+#define E25GaCR0_PD BIT(30)
+
+#define E25GaCR1(lane) (0x1b04 + (lane) * 0x10)
+
+#define E25GaCR2(lane) (0x1b08 + (lane) * 0x10)
+#define E25GaCR2_FEC_ENA BIT(23)
+#define E25GaCR2_FEC_ERR_ENA BIT(22)
+#define E25GaCR2_FEC91_ENA BIT(20)
+
+#define E40GaCR0(pcvt) (0x1b40 + (pcvt) * 0x20)
+#define E40GaCR1(pcvt) (0x1b44 + (pcvt) * 0x20)
+
+#define E50GaCR1(pcvt) (0x1b84 + (pcvt) * 0x10)
+
+#define E100GaCR1(pcvt) (0x1c04 + (pcvt) * 0x20)
+
+#define CR(x) ((x) * 4)
+
enum lynx_28g_eq_type {
EQ_TYPE_NO_EQ = 0,
EQ_TYPE_2TAP = 1,
EQ_TYPE_3TAP = 2,
};
+enum lynx_28g_proto_sel {
+ PROTO_SEL_PCIE = 0,
+ PROTO_SEL_SGMII_BASEX_KX = 1,
+ PROTO_SEL_SATA = 2,
+ PROTO_SEL_XAUI = 4,
+ PROTO_SEL_XFI_10GBASER_KR_SXGMII = 0xa,
+ PROTO_SEL_25G_50G_100G = 0x1a,
+};
+
+struct lynx_28g_proto_conf {
+ /* LNaGCR0 */
+ int proto_sel;
+ int if_width;
+ /* LNaTECR0 */
+ int teq_type;
+ int sgn_preq;
+ int ratio_preq;
+ int sgn_post1q;
+ int ratio_post1q;
+ int amp_red;
+ /* LNaTECR1 */
+ int adpt_eq;
+ /* LNaRGCR1 */
+ int enter_idle_flt_sel;
+ int exit_idle_flt_sel;
+ int data_lost_th_sel;
+ /* LNaRECR0 */
+ int gk2ovd;
+ int gk3ovd;
+ int gk4ovd;
+ int gk2ovd_en;
+ int gk3ovd_en;
+ int gk4ovd_en;
+ /* LNaRECR1 ? */
+ int eq_offset_ovd;
+ int eq_offset_ovd_en;
+ /* LNaRECR2 */
+ int eq_offset_rng_dbl;
+ int eq_blw_sel;
+ int eq_boost;
+ int spare_in;
+ /* LNaRSCCR0 */
+ int smp_autoz_d1r;
+ int smp_autoz_eg1r;
+};
+
+static const struct lynx_28g_proto_conf lynx_28g_proto_conf[PHY_INTERFACE_MODE_MAX] = {
+ [PHY_INTERFACE_MODE_SGMII] = {
+ .proto_sel = LNaGCR0_PROTO_SEL_SGMII,
+ .if_width = LNaGCR0_IF_WIDTH_10_BIT,
+ .teq_type = EQ_TYPE_NO_EQ,
+ .sgn_preq = 1,
+ .ratio_preq = 0,
+ .sgn_post1q = 1,
+ .ratio_post1q = 0,
+ .amp_red = 6,
+ .adpt_eq = 48,
+ .enter_idle_flt_sel = 4,
+ .exit_idle_flt_sel = 3,
+ .data_lost_th_sel = 1,
+ .gk2ovd = 0x1f,
+ .gk3ovd = 0,
+ .gk4ovd = 0,
+ .gk2ovd_en = 1,
+ .gk3ovd_en = 1,
+ .gk4ovd_en = 0,
+ .eq_offset_ovd = 0x1f,
+ .eq_offset_ovd_en = 0,
+ .eq_offset_rng_dbl = 0,
+ .eq_blw_sel = 0,
+ .eq_boost = 0,
+ .spare_in = 0,
+ .smp_autoz_d1r = 0,
+ .smp_autoz_eg1r = 0,
+ },
+ [PHY_INTERFACE_MODE_1000BASEX] = {
+ .proto_sel = LNaGCR0_PROTO_SEL_SGMII,
+ .if_width = LNaGCR0_IF_WIDTH_10_BIT,
+ .teq_type = EQ_TYPE_NO_EQ,
+ .sgn_preq = 1,
+ .ratio_preq = 0,
+ .sgn_post1q = 1,
+ .ratio_post1q = 0,
+ .amp_red = 6,
+ .adpt_eq = 48,
+ .enter_idle_flt_sel = 4,
+ .exit_idle_flt_sel = 3,
+ .data_lost_th_sel = 1,
+ .gk2ovd = 0x1f,
+ .gk3ovd = 0,
+ .gk4ovd = 0,
+ .gk2ovd_en = 1,
+ .gk3ovd_en = 1,
+ .gk4ovd_en = 0,
+ .eq_offset_ovd = 0x1f,
+ .eq_offset_ovd_en = 0,
+ .eq_offset_rng_dbl = 0,
+ .eq_blw_sel = 0,
+ .eq_boost = 0,
+ .spare_in = 0,
+ .smp_autoz_d1r = 0,
+ .smp_autoz_eg1r = 0,
+ },
+ [PHY_INTERFACE_MODE_10GBASER] = {
+ .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,
+ },
+};
+
+struct lynx_pccr {
+ int offset;
+ int width;
+ int shift;
+};
+
struct lynx_28g_priv;
struct lynx_28g_pll {
iowrite32(tmp, reg);
}
+#define lynx_28g_read(priv, off) \
+ ioread32((priv)->base + (off))
+#define lynx_28g_write(priv, off, val) \
+ iowrite32(val, (priv)->base + (off))
#define lynx_28g_lane_rmw(lane, reg, val, mask) \
lynx_28g_rmw((lane)->priv, reg(lane->id), val, mask)
#define lynx_28g_lane_read(lane, reg) \
}
}
-static void lynx_28g_cleanup_lane(struct lynx_28g_lane *lane)
-{
- struct lynx_28g_priv *priv = lane->priv;
-
- /* Cleanup the protocol configuration registers of the current protocol */
- switch (lane->interface) {
- case PHY_INTERFACE_MODE_10GBASER:
- /* Cleanup the protocol configuration registers */
- lynx_28g_rmw(priv, PCCC, 0, PCCC_MSK(lane));
- break;
- case PHY_INTERFACE_MODE_SGMII:
- case PHY_INTERFACE_MODE_1000BASEX:
- /* Cleanup the protocol configuration registers */
- lynx_28g_rmw(priv, PCC8, 0, PCC8_MSK(lane));
-
- /* Disable the SGMII PCS */
- lynx_28g_lane_rmw(lane, SGMIIaCR1, 0, SGMIIaCR1_SGPCS_EN);
-
- break;
- default:
- break;
- }
-}
-
-static void lynx_28g_lane_set_sgmii(struct lynx_28g_lane *lane)
-{
- struct lynx_28g_priv *priv = lane->priv;
- struct lynx_28g_pll *pll;
-
- lynx_28g_cleanup_lane(lane);
-
- /* Setup the lane to run in SGMII */
- lynx_28g_rmw(priv, PCC8, PCC8_SGMIInCFG_EN(lane), PCC8_MSK(lane));
-
- /* Setup the protocol select and SerDes parallel interface width */
- lynx_28g_lane_rmw(lane, LNaGCR0,
- FIELD_PREP(LNaGCR0_PROTO_SEL, LNaGCR0_PROTO_SEL_SGMII) |
- FIELD_PREP(LNaGCR0_IF_WIDTH, LNaGCR0_IF_WIDTH_10_BIT),
- LNaGCR0_PROTO_SEL | LNaGCR0_IF_WIDTH);
-
- /* Find the PLL that works with this interface type */
- pll = lynx_28g_pll_get(priv, PHY_INTERFACE_MODE_SGMII);
- if (unlikely(pll == NULL))
- return;
-
- /* Switch to the PLL that works with this interface type */
- lynx_28g_lane_set_pll(lane, pll);
-
- /* Choose the portion of clock net to be used on this lane */
- lynx_28g_lane_set_nrate(lane, pll, PHY_INTERFACE_MODE_SGMII);
-
- /* Enable the SGMII PCS */
- lynx_28g_lane_rmw(lane, SGMIIaCR1, SGMIIaCR1_SGPCS_EN,
- SGMIIaCR1_SGPCS_EN);
-
- /* Configure the appropriate equalization parameters for the protocol */
- lynx_28g_lane_write(lane, LNaTECR0,
- LNaTECR0_EQ_SGN_PREQ | LNaTECR0_EQ_SGN_POST1Q |
- FIELD_PREP(LNaTECR0_EQ_AMP_RED, 6));
- lynx_28g_lane_write(lane, LNaRGCR1,
- FIELD_PREP(LNaRGCR1_ENTER_IDLE_FLT_SEL, 4) |
- FIELD_PREP(LNaRGCR1_EXIT_IDLE_FLT_SEL, 3) |
- LNaRGCR1_DATA_LOST_FLT);
- lynx_28g_lane_write(lane, LNaRECR0,
- LNaRECR0_EQ_GAINK2_HF_OV_EN |
- FIELD_PREP(LNaRECR0_EQ_GAINK2_HF_OV, 31) |
- LNaRECR0_EQ_GAINK3_MF_OV_EN |
- FIELD_PREP(LNaRECR0_EQ_GAINK3_MF_OV, 0));
- lynx_28g_lane_write(lane, LNaRECR1,
- FIELD_PREP(LNaRECR1_EQ_OFFSET_OV, 31));
- lynx_28g_lane_write(lane, LNaRECR2, 0);
- lynx_28g_lane_write(lane, LNaRSCCR0, 0);
-}
-
-static void lynx_28g_lane_set_10gbaser(struct lynx_28g_lane *lane)
-{
- struct lynx_28g_priv *priv = lane->priv;
- struct lynx_28g_pll *pll;
-
- lynx_28g_cleanup_lane(lane);
-
- /* Enable the SXGMII lane */
- lynx_28g_rmw(priv, PCCC, PCCC_SXGMIInCFG_EN(lane) |
- PCCC_SXGMIInCFG_XFI(lane, 1), PCCC_MSK(lane));
-
- /* Setup the protocol select and SerDes parallel interface width */
- lynx_28g_lane_rmw(lane, LNaGCR0,
- FIELD_PREP(LNaGCR0_PROTO_SEL, LNaGCR0_PROTO_SEL_XFI) |
- FIELD_PREP(LNaGCR0_IF_WIDTH, LNaGCR0_IF_WIDTH_20_BIT),
- LNaGCR0_PROTO_SEL | LNaGCR0_IF_WIDTH);
-
- /* Find the PLL that works with this interface type */
- pll = lynx_28g_pll_get(priv, PHY_INTERFACE_MODE_10GBASER);
- if (unlikely(pll == NULL))
- return;
-
- /* Switch to the PLL that works with this interface type */
- lynx_28g_lane_set_pll(lane, pll);
-
- /* Choose the portion of clock net to be used on this lane */
- lynx_28g_lane_set_nrate(lane, pll, PHY_INTERFACE_MODE_10GBASER);
-
- /* Disable the SGMII PCS */
- lynx_28g_lane_rmw(lane, SGMIIaCR1, 0, SGMIIaCR1_SGPCS_EN);
-
- /* Configure the appropriate equalization parameters for the protocol */
- lynx_28g_lane_write(lane, LNaTECR0,
- FIELD_PREP(LNaTECR0_EQ_TYPE, EQ_TYPE_2TAP) |
- LNaTECR0_EQ_SGN_PREQ |
- FIELD_PREP(LNaTECR0_EQ_PREQ, 0) |
- LNaTECR0_EQ_SGN_POST1Q |
- FIELD_PREP(LNaTECR0_EQ_POST1Q, 3) |
- FIELD_PREP(LNaTECR0_EQ_AMP_RED, 7));
- lynx_28g_lane_write(lane, LNaRGCR1, LNaRGCR1_IDLE_CONFIG);
- lynx_28g_lane_write(lane, LNaRECR0, 0);
- lynx_28g_lane_write(lane, LNaRECR1, FIELD_PREP(LNaRECR1_EQ_OFFSET_OV, 31));
- lynx_28g_lane_write(lane, LNaRECR2,
- LNaRECR2_EQ_OFFSET_RNG_DBL |
- FIELD_PREP(LNaRECR2_EQ_BLW_SEL, 1) |
- FIELD_PREP(LNaRECR2_EQ_BIN_DATA_AVG_TC, 2));
- lynx_28g_lane_write(lane, LNaRSCCR0,
- FIELD_PREP(LNaRSCCR0_SMP_AUTOZ_D1R, 2));
-}
-
static int lynx_28g_power_off(struct phy *phy)
{
struct lynx_28g_lane *lane = phy_get_drvdata(phy);
return 0;
}
+static int lynx_28g_get_pccr(phy_interface_t interface, int lane,
+ struct lynx_pccr *pccr)
+{
+ switch (interface) {
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ pccr->offset = PCC8;
+ pccr->width = 4;
+ pccr->shift = SGMII_CFG(lane);
+ break;
+ case PHY_INTERFACE_MODE_10GBASER:
+ pccr->offset = PCCC;
+ pccr->width = 4;
+ pccr->shift = SXGMII_CFG(lane);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return 0;
+}
+
+static int lynx_28g_get_pcvt_offset(int lane, phy_interface_t interface)
+{
+ switch (interface) {
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ return SGMIIaCR0(lane);
+ case PHY_INTERFACE_MODE_10GBASER:
+ return SXGMIIaCR0(lane);
+ default:
+ return -EOPNOTSUPP;
+ }
+}
+
+static int lynx_pccr_write(struct lynx_28g_lane *lane,
+ phy_interface_t interface, u32 val)
+{
+ struct lynx_28g_priv *priv = lane->priv;
+ struct lynx_pccr pccr;
+ u32 old, tmp, mask;
+ int err;
+
+ err = lynx_28g_get_pccr(interface, lane->id, &pccr);
+ if (err)
+ return err;
+
+ old = lynx_28g_read(priv, pccr.offset);
+ mask = GENMASK(pccr.width - 1, 0) << pccr.shift;
+ tmp = (old & ~mask) | (val << pccr.shift);
+ lynx_28g_write(priv, pccr.offset, tmp);
+
+ dev_dbg(&lane->phy->dev, "PCCR@0x%x: 0x%x -> 0x%x\n",
+ pccr.offset, old, tmp);
+
+ return 0;
+}
+
+static int lynx_pcvt_read(struct lynx_28g_lane *lane, phy_interface_t interface,
+ int cr, u32 *val)
+{
+ struct lynx_28g_priv *priv = lane->priv;
+ int offset;
+
+ offset = lynx_28g_get_pcvt_offset(lane->id, interface);
+ if (offset < 0)
+ return offset;
+
+ *val = lynx_28g_read(priv, offset + cr);
+
+ return 0;
+}
+
+static int lynx_pcvt_write(struct lynx_28g_lane *lane, phy_interface_t interface,
+ int cr, u32 val)
+{
+ struct lynx_28g_priv *priv = lane->priv;
+ int offset;
+
+ offset = lynx_28g_get_pcvt_offset(lane->id, interface);
+ if (offset < 0)
+ return offset;
+
+ lynx_28g_write(priv, offset + cr, val);
+
+ return 0;
+}
+
+static int lynx_pcvt_rmw(struct lynx_28g_lane *lane, phy_interface_t interface,
+ int cr, u32 val, u32 mask)
+{
+ int err;
+ u32 tmp;
+
+ err = lynx_pcvt_read(lane, interface, cr, &tmp);
+ if (err)
+ return err;
+
+ tmp &= ~mask;
+ tmp |= val;
+
+ return lynx_pcvt_write(lane, interface, cr, tmp);
+}
+
+static void lynx_28g_lane_remap_pll(struct lynx_28g_lane *lane,
+ phy_interface_t interface)
+{
+ struct lynx_28g_priv *priv = lane->priv;
+ struct lynx_28g_pll *pll;
+
+ /* Switch to the PLL that works with this interface type */
+ pll = lynx_28g_pll_get(priv, interface);
+ if (unlikely(pll == NULL))
+ return;
+
+ lynx_28g_lane_set_pll(lane, pll);
+
+ /* Choose the portion of clock net to be used on this lane */
+ lynx_28g_lane_set_nrate(lane, pll, interface);
+}
+
+static void lynx_28g_lane_change_proto_conf(struct lynx_28g_lane *lane,
+ phy_interface_t interface)
+{
+ const struct lynx_28g_proto_conf *conf = &lynx_28g_proto_conf[interface];
+
+ lynx_28g_lane_rmw(lane, LNaGCR0,
+ FIELD_PREP(LNaGCR0_PROTO_SEL, conf->proto_sel) |
+ FIELD_PREP(LNaGCR0_IF_WIDTH, conf->if_width),
+ LNaGCR0_PROTO_SEL | LNaGCR0_IF_WIDTH);
+
+ lynx_28g_lane_rmw(lane, LNaTECR0,
+ FIELD_PREP(LNaTECR0_EQ_TYPE, conf->teq_type) |
+ FIELD_PREP(LNaTECR0_EQ_SGN_PREQ, conf->sgn_preq) |
+ FIELD_PREP(LNaTECR0_EQ_PREQ, conf->ratio_preq) |
+ FIELD_PREP(LNaTECR0_EQ_SGN_POST1Q, conf->sgn_post1q) |
+ FIELD_PREP(LNaTECR0_EQ_POST1Q, conf->ratio_post1q) |
+ FIELD_PREP(LNaTECR0_EQ_AMP_RED, conf->amp_red),
+ LNaTECR0_EQ_TYPE |
+ LNaTECR0_EQ_SGN_PREQ |
+ LNaTECR0_EQ_PREQ |
+ LNaTECR0_EQ_SGN_POST1Q |
+ LNaTECR0_EQ_POST1Q |
+ LNaTECR0_EQ_AMP_RED);
+
+ lynx_28g_lane_rmw(lane, LNaTECR1,
+ FIELD_PREP(LNaTECR1_EQ_ADPT_EQ, conf->adpt_eq),
+ LNaTECR1_EQ_ADPT_EQ);
+
+ lynx_28g_lane_rmw(lane, LNaRGCR1,
+ FIELD_PREP(LNaRGCR1_ENTER_IDLE_FLT_SEL, conf->enter_idle_flt_sel) |
+ FIELD_PREP(LNaRGCR1_EXIT_IDLE_FLT_SEL, conf->exit_idle_flt_sel) |
+ FIELD_PREP(LNaRGCR1_DATA_LOST_TH_SEL, conf->data_lost_th_sel),
+ LNaRGCR1_ENTER_IDLE_FLT_SEL |
+ LNaRGCR1_EXIT_IDLE_FLT_SEL |
+ LNaRGCR1_DATA_LOST_TH_SEL);
+
+ lynx_28g_lane_rmw(lane, LNaRECR0,
+ FIELD_PREP(LNaRECR0_EQ_GAINK2_HF_OV_EN, conf->gk2ovd_en) |
+ FIELD_PREP(LNaRECR0_EQ_GAINK3_MF_OV_EN, conf->gk3ovd_en) |
+ FIELD_PREP(LNaRECR0_EQ_GAINK4_LF_OV_EN, conf->gk4ovd_en) |
+ FIELD_PREP(LNaRECR0_EQ_GAINK2_HF_OV, conf->gk2ovd) |
+ FIELD_PREP(LNaRECR0_EQ_GAINK3_MF_OV, conf->gk3ovd) |
+ FIELD_PREP(LNaRECR0_EQ_GAINK4_LF_OV, conf->gk4ovd),
+ LNaRECR0_EQ_GAINK2_HF_OV |
+ LNaRECR0_EQ_GAINK3_MF_OV |
+ LNaRECR0_EQ_GAINK4_LF_OV |
+ LNaRECR0_EQ_GAINK2_HF_OV_EN |
+ LNaRECR0_EQ_GAINK3_MF_OV_EN |
+ LNaRECR0_EQ_GAINK4_LF_OV_EN);
+
+ lynx_28g_lane_rmw(lane, LNaRECR1,
+ FIELD_PREP(LNaRECR1_EQ_OFFSET_OV, conf->eq_offset_ovd) |
+ FIELD_PREP(LNaRECR1_EQ_OFFSET_OV_EN, conf->eq_offset_ovd_en),
+ LNaRECR1_EQ_OFFSET_OV |
+ LNaRECR1_EQ_OFFSET_OV_EN);
+
+ lynx_28g_lane_rmw(lane, LNaRECR2,
+ FIELD_PREP(LNaRECR2_EQ_OFFSET_RNG_DBL, conf->eq_offset_rng_dbl) |
+ FIELD_PREP(LNaRECR2_EQ_BLW_SEL, conf->eq_blw_sel) |
+ FIELD_PREP(LNaRECR2_EQ_BOOST, conf->eq_boost) |
+ FIELD_PREP(LNaRECR2_SPARE_IN, conf->spare_in),
+ LNaRECR2_EQ_OFFSET_RNG_DBL |
+ LNaRECR2_EQ_BLW_SEL |
+ LNaRECR2_EQ_BOOST |
+ LNaRECR2_SPARE_IN);
+
+ lynx_28g_lane_rmw(lane, LNaRSCCR0,
+ FIELD_PREP(LNaRSCCR0_SMP_AUTOZ_D1R, conf->smp_autoz_d1r) |
+ FIELD_PREP(LNaRSCCR0_SMP_AUTOZ_EG1R, conf->smp_autoz_eg1r),
+ LNaRSCCR0_SMP_AUTOZ_D1R |
+ LNaRSCCR0_SMP_AUTOZ_EG1R);
+}
+
+static int lynx_28g_lane_disable_pcvt(struct lynx_28g_lane *lane,
+ phy_interface_t interface)
+{
+ struct lynx_28g_priv *priv = lane->priv;
+ int err;
+
+ spin_lock(&priv->pcc_lock);
+
+ err = lynx_pccr_write(lane, interface, 0);
+ if (err)
+ goto out;
+
+ switch (interface) {
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ err = lynx_pcvt_rmw(lane, interface, CR(1), 0,
+ SGMIIaCR1_SGPCS_EN);
+ break;
+ default:
+ err = 0;
+ }
+
+out:
+ spin_unlock(&priv->pcc_lock);
+
+ return err;
+}
+
+static int lynx_28g_lane_enable_pcvt(struct lynx_28g_lane *lane,
+ phy_interface_t interface)
+{
+ struct lynx_28g_priv *priv = lane->priv;
+ u32 val;
+ int err;
+
+ spin_lock(&priv->pcc_lock);
+
+ switch (interface) {
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ err = lynx_pcvt_rmw(lane, interface, CR(1), SGMIIaCR1_SGPCS_EN,
+ SGMIIaCR1_SGPCS_EN);
+ break;
+ default:
+ err = 0;
+ }
+
+ val = 0;
+
+ switch (interface) {
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ val |= PCC8_SGMIIa_CFG;
+ break;
+ case PHY_INTERFACE_MODE_10GBASER:
+ val |= PCCC_SXGMIIn_CFG | PCCC_SXGMIIn_XFI;
+ break;
+ default:
+ break;
+ }
+
+ err = lynx_pccr_write(lane, interface, val);
+
+ spin_unlock(&priv->pcc_lock);
+
+ return err;
+}
+
static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode)
{
struct lynx_28g_lane *lane = phy_get_drvdata(phy);
if (!lynx_28g_supports_interface(priv, submode))
return -EOPNOTSUPP;
+ if (submode == lane->interface)
+ return 0;
+
/* If the lane is powered up, put the lane into the halt state while
* the reconfiguration is being done.
*/
if (powered_up)
lynx_28g_power_off(phy);
- spin_lock(&priv->pcc_lock);
-
- switch (submode) {
- case PHY_INTERFACE_MODE_SGMII:
- case PHY_INTERFACE_MODE_1000BASEX:
- lynx_28g_lane_set_sgmii(lane);
- break;
- case PHY_INTERFACE_MODE_10GBASER:
- lynx_28g_lane_set_10gbaser(lane);
- break;
- default:
- err = -EOPNOTSUPP;
+ err = lynx_28g_lane_disable_pcvt(lane, lane->interface);
+ if (err)
goto out;
- }
+
+ lynx_28g_lane_change_proto_conf(lane, submode);
+ lynx_28g_lane_remap_pll(lane, submode);
+ WARN_ON(lynx_28g_lane_enable_pcvt(lane, submode));
lane->interface = submode;
out:
- spin_unlock(&priv->pcc_lock);
-
- /* Power up the lane if necessary */
if (powered_up)
lynx_28g_power_on(phy);