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

Reply via email to