Add a SD1 mode that makes the two 100GE ports of protocol 13 two 40GE (XLAUI-4) ports, dpmac.2 on LNA-LND and dpmac.1 on LNE-LNH, as protocol 20 would have built them.
Sequence: - bring PLLS up and wait for its lock - halt PLLF, which puts all eight lanes in reset - make them 2x 4-lane ports on PLLS with their master lanes kept - switch PCCE from E100G to E40G - reset PLLS which restarts every lane Signed-off-by: Vincent Jardin <[email protected]> Reviewed-by: Vladimir Oltean <[email protected]> --- (no changes since v6) Changes in v6: - Better explain the move to PLLS by the clock net, not BY_N_RATE_SEL (Vladimir) - Only 161.1328125 MHz PLLS reference clock is supported, from the PLLS input or through SRDS_INTRA_REF_CLK_S1 Changes in v4: - was on v3 2/5, now it is an entry of the mode table board/nxp/lx2160a/serdes_split.c | 148 +++++++++++++++++++++++++++++++ 1 file changed, 148 insertions(+) diff --git a/board/nxp/lx2160a/serdes_split.c b/board/nxp/lx2160a/serdes_split.c index ba3be4bc0d7..0107e8636a6 100644 --- a/board/nxp/lx2160a/serdes_split.c +++ b/board/nxp/lx2160a/serdes_split.c @@ -29,10 +29,17 @@ #define SRDS_LANES 8 #define SRDS_BLOCK_STRIDE 0x10000 #define SRDS_LANE_STRIDE 0x100 +#define SRDS_PLL_STRIDE 0x100 +#define SRDS_PLLnRSTCTL(b, n) ((b) + 0x400 + (n) * SRDS_PLL_STRIDE) +#define SRDS_PLLnCR0(b, n) ((b) + 0x404 + (n) * SRDS_PLL_STRIDE) +#define SRDS_PLLnCR1(b, n) ((b) + 0x408 + (n) * SRDS_PLL_STRIDE) +#define SRDS_PLLnCR5(b, n) ((b) + 0x414 + (n) * SRDS_PLL_STRIDE) #define SRDS_LNmGCR0(b, m) ((b) + 0x800 + (m) * SRDS_LANE_STRIDE) #define SRDS_LNmTRSTCTL(b, m) ((b) + 0x820 + (m) * SRDS_LANE_STRIDE) +#define SRDS_LNmTGCR0(b, m) ((b) + 0x824 + (m) * SRDS_LANE_STRIDE) #define SRDS_LNmRRSTCTL(b, m) ((b) + 0x840 + (m) * SRDS_LANE_STRIDE) +#define SRDS_LNmRGCR0(b, m) ((b) + 0x844 + (m) * SRDS_LANE_STRIDE) #define SRDS_PCCC(b) ((b) + 0x10B0) #define SRDS_PCCD(b) ((b) + 0x10B4) #define SRDS_PCCE(b) ((b) + 0x10B8) @@ -51,6 +58,14 @@ #define RSTCTL_STP_REQ BIT(26) #define RSTCTL_DIS BIT(24) +/* PLLnRSTCTL adds two status bits the lane RSTCTLs do not have */ +#define PLLRSTCTL_RST_ERR BIT(29) /* no lock before time-out */ +#define PLLRSTCTL_LOCK BIT(23) /* PLL calibrated + locked */ + +/* PCCE: E40Ga at 30-28 (LRV 31), E40Gb at 26-24 (LRV 27) */ +#define PCCE_E40GA_CFG(v) (((v) >> 28) & 0x7) +#define PCCE_E40GB_CFG(v) (((v) >> 24) & 0x7) + /* PCCE: E100Ga at 14-12 (LRV 15), E100Gb at 10-8 (LRV 11) */ #define PCCE_E100GA_CFG(v) (((v) >> 12) & 0x7) #define PCCE_E100GA_LRV(v) (((v) >> 15) & 0x1) @@ -261,6 +276,7 @@ struct sd1_mode { * This is NXP's e100g1_split.rcw sequence, moved from the PBI into * U-Boot. */ +#define SD1_LNA_LND GENMASK(3, 0) #define SD1_LNE_LNH GENMASK(7, 4) /* @@ -431,6 +447,131 @@ static int sd1_enter_100g4x25g(unsigned long base) return err ? err : ret; } +/* + * 2x40GE (XLAUI-4), dpmac.2 on LNA-LND and dpmac.1 on LNE-LNH, + * protocol 20 builds the same. + * + * A PLL makes one clock net, and each lane takes its rate from the + * clock net of one PLL, through its own BY_N_RATE_SEL. 25GE and 100GE + * lanes run at 25.78125 Gbaud, twice the 12.890625 GHz clock net of + * PLLF. 40GE lanes run at 10.3125 Gbaud, which no ratio of 12.890625 + * GHz gives: they need the 10.3125 GHz clock net, which only PLLS + * makes (PLLnCR1[FRATE_SEL]=00110b). So the lanes move to PLLS. + * + * Protocol 13 does not use PLLS, and NXP's RCWs for it power it down + * (SRDS_PLL_PD_PLL2=1), which leaves PLLSRSTCTL[DIS] set: software can + * clear it. PLLS then gets the PLLSCR0/CR1/CR5 values the SoC loads + * for protocol 20 when SRDS_PLL_REF_CLK_SEL_S1 selects 161.1328125 MHz + * for PLLS. + * + * Only a 161.1328125 MHz PLLS reference clock is supported. It comes + * from the SD1 PLLS reference clock input, or from the PLLF one when + * the RCW sets SRDS_INTRA_REF_CLK_S1=1, as 25GE and 100GE already need + * 161.1328125 MHz on PLLF. 156.25 MHz, also valid for a 10.3125 GHz + * clock net, would need PLLSCR0[REFCLK_SEL]=00010b and is not handled. + * Nothing here can check the reference clock: PLLSRSTCTL[LOCK] does + * not tell its frequency. + */ +#define SRDS_PLLF 0 +#define SRDS_PLLS 1 +#define SLOW_PLLS_CR0 0x00040000 /* REFCLK_SEL=00100b, 161.1328125 MHz */ +#define SLOW_PLLS_CR1 0x86100008 /* 10.3125 GHz clock net */ +#define SLOW_PLLS_CR5 0x00001000 +/* PROTO_SEL=01010b (XFI/10GBase-R/40GBase-R) + IF_WIDTH=010b (20-bit) */ +#define F40G_GCR0_MASTER 0x00000052 +#define F40G_GCR0_SLAVE 0x00010052 +/* LNmTGCR0/LNmRGCR0: USE_SLOW_PLL=1 (bit 28), BY_N_RATE_SEL=000b full */ +#define SLOW_LANE_GCR 0x10000000 +#define F40G_PCCE 0x11000000 /* E40Ga+E40Gb on, E100G off */ + +static bool sd1_is_2x40g(unsigned long base) +{ + u32 pcce = in_le32((void __iomem *)SRDS_PCCE(base)); + + return PCCE_E40GA_CFG(pcce) != 0 && PCCE_E40GB_CFG(pcce) != 0 && + PCCE_E100GA_CFG(pcce) == 0 && PCCE_E100GB_CFG(pcce) == 0; +} + +/* Program PLLS (Slow) for 10.3125 GHz, and wait for its lock */ +static int sd1_plls_up(unsigned long base) +{ + unsigned long rstctl = SRDS_PLLnRSTCTL(base, SRDS_PLLS); + u32 v; + + out_le32((void __iomem *)SRDS_PLLnCR0(base, SRDS_PLLS), SLOW_PLLS_CR0); + out_le32((void __iomem *)SRDS_PLLnCR1(base, SRDS_PLLS), SLOW_PLLS_CR1); + out_le32((void __iomem *)SRDS_PLLnCR5(base, SRDS_PLLS), SLOW_PLLS_CR5); + + out_le32((void __iomem *)rstctl, 0); /* clears DIS */ + out_le32((void __iomem *)rstctl, RSTCTL_RST_REQ); + + if (poll_clear(rstctl, RSTCTL_RST_REQ)) { + printf("lx2160a serdes: SD1 PLLS reset did not complete within %d us\n", + SRDS_POLL_US); + return -ETIMEDOUT; + } + + v = in_le32((void __iomem *)rstctl); + if ((v & PLLRSTCTL_RST_ERR) || !(v & PLLRSTCTL_LOCK) || + !(v & RSTCTL_RST_DONE)) { + printf("lx2160a serdes: SD1 PLLS did not lock (PLLSRSTCTL=%08x)\n", + v); + return -EIO; + } + + return 0; +} + +static int sd1_enter_2x40g(unsigned long base) +{ + unsigned long pllf = SRDS_PLLnRSTCTL(base, SRDS_PLLF); + unsigned long plls = SRDS_PLLnRSTCTL(base, SRDS_PLLS); + unsigned long lanes = SD1_LNA_LND | SD1_LNE_LNH; + int m, lo, hi, ret, err; + + /* The two ports keep their master lanes. */ + lo = sd1_port_master(base, SD1_LNA_LND); + hi = sd1_port_master(base, SD1_LNE_LNH); + if (lo < 0 || hi < 0) + return -EINVAL; + + ret = sd1_plls_up(base); + if (ret) + return ret; + + /* + * Halt PLLF, which puts every lane using it, all eight, in reset + * "to allow reconfiguration". + */ + out_le32((void __iomem *)pllf, RSTCTL_HLT_REQ); + if (poll_clear(pllf, RSTCTL_HLT_REQ)) { + printf("lx2160a serdes: SD1 PLLF did not halt within %d us, continuing\n", + SRDS_POLL_US); + ret = -ETIMEDOUT; + } + + for_each_set_bit(m, &lanes, SRDS_LANES) { + out_le32((void __iomem *)SRDS_LNmGCR0(base, m), + m == lo || m == hi ? F40G_GCR0_MASTER : F40G_GCR0_SLAVE); + out_le32((void __iomem *)SRDS_LNmTGCR0(base, m), SLOW_LANE_GCR); + out_le32((void __iomem *)SRDS_LNmRGCR0(base, m), SLOW_LANE_GCR); + } + + out_le32((void __iomem *)SRDS_PCCE(base), F40G_PCCE); + + /* Every lane now selects PLLS, so one PLL reset restarts them all. */ + out_le32((void __iomem *)plls, RSTCTL_RST_REQ); + if (poll_clear(plls, RSTCTL_RST_REQ)) { + printf("lx2160a serdes: SD1 PLLS reset did not complete within %d us\n", + SRDS_POLL_US); + ret = -ETIMEDOUT; + } + + err = sd1_reset_wait(base, lanes); + + return err ? err : ret; +} + static const struct sd1_mode sd1_modes[] = { { .name = "2x100g", @@ -444,6 +585,13 @@ static const struct sd1_mode sd1_modes[] = { .is_active = sd1_is_100g4x25g, .enter = sd1_enter_100g4x25g, }, + { + .name = "2x40g", + .desc = "2x 40GE", + .from = "2x100g", + .is_active = sd1_is_2x40g, + .enter = sd1_enter_2x40g, + }, }; static const struct sd1_mode *sd1_mode_find(const char *name, size_t len) -- 2.43.0
