lx2160_sd1 40g4x10g, hwconfig=serdes1:40g4x10g.

  XFI   LNmGCR0 = 0x00000052   LNmTGCR0 = 0x10000000
  40GE  LNmGCR0 = 0x00010052   LNmTGCR0 = 0x10000000
                       ^ PORT_LN0_B

same PROTO_SEL (0x0a spans XFI and 40GBase-R), same 20-bit width, same
USE_SLOW_PLL, same BY_N_RATE_SEL.

It is inspired from the 100G+4x25G split, but using PLLS.

Signed-off-by: Vincent Jardin <[email protected]>
---

(no changes since v1)

 board/nxp/lx2160a/serdes_split.c | 191 ++++++++++++++++++++++++++++++-
 board/nxp/lx2160a/serdes_split.h |  11 ++
 2 files changed, 199 insertions(+), 3 deletions(-)

diff --git a/board/nxp/lx2160a/serdes_split.c b/board/nxp/lx2160a/serdes_split.c
index 0f30019e20a..f48c882aa22 100644
--- a/board/nxp/lx2160a/serdes_split.c
+++ b/board/nxp/lx2160a/serdes_split.c
@@ -585,6 +585,154 @@ int lx2160a_serdes1_to_40g(void)
        return ret;
 }
 
+/*
+ * 2x 40GE -> 1x 40GE + 4x XFI (10G), keeping the QSFP group intact.
+ *
+ *   XFI  lane   LNmGCR0 = 0x00000052   LNmTGCR0 = 0x10000000
+ *   40GE lane   LNmGCR0 = 0x00010052   LNmTGCR0 = 0x10000000
+ *                            ^ PORT_LN0_B
+ */
+#define X10G_FIRST_LANE                4
+#define X10G_LAST_LANE         7
+#define X10G_GCR0              0x00000052      /* as 40GE, but own port master 
*/
+#define X10G_PCCE              0x01000000      /* E40Gb kept (lanes 0-3) */
+#define X10G_PCCC              0x88889999      /* XFI converters for lanes 4-7 
  */
+
+static bool srds1_is_40g_4x10g(unsigned long base)
+{
+       u32 pccc = in_le32((void __iomem *)SRDS_PCCC(base));
+       u32 pcce = in_le32((void __iomem *)SRDS_PCCE(base));
+
+       return PCCE_E40GB_CFG(pcce) != 0 &&
+              PCCE_E40GA_CFG(pcce) == 0 &&
+              (pccc & 0xffff) == 0x9999;
+}
+
+bool lx2160a_serdes1_is_40g_4x10g(void)
+{
+       return srds1_is_40g_4x10g(srds_base(FSL_SRDS_1));
+}
+
+int lx2160a_serdes1_40g_4x10g(void)
+{
+       unsigned long base = srds_base(FSL_SRDS_1);
+       int m, master = -1, keep_master = -1, ret = 0;
+
+       if (srds1_is_40g_4x10g(base)) {
+               debug("lx2160a serdes: SD1 already 1x 40GE + 4x 10G\n");
+               return 0;
+       }
+
+       if (!srds1_is_40g(base)) {
+               printf("lx2160a serdes: SD1 is not in the 2x 40GE state 
(PCCC=%08x PCCE=%08x), refusing\n",
+                      in_le32((void __iomem *)SRDS_PCCC(base)),
+                      in_le32((void __iomem *)SRDS_PCCE(base)));
+               return -EINVAL;
+       }
+
+       /*
+        * Halt. As in the 25G split, only the port master's request can
+        * clear, so issue all four and wait on that one.
+        */
+       /* The kept group's master, recorded before anything is halted. */
+       for (m = 0; m < SRDS_LANES; m++) {
+               if (m >= X10G_FIRST_LANE && m <= X10G_LAST_LANE)
+                       continue;
+               if (!(in_le32((void __iomem *)SRDS_LNmGCR0(base, m)) &
+                     GCR0_PORT_LN0_B))
+                       keep_master = m;
+       }
+
+       for (m = X10G_FIRST_LANE; m <= X10G_LAST_LANE; m++) {
+               if (!(in_le32((void __iomem *)SRDS_LNmGCR0(base, m)) &
+                     GCR0_PORT_LN0_B))
+                       master = m;
+               out_le32((void __iomem *)SRDS_LNmTRSTCTL(base, m),
+                        RSTCTL_HLT_REQ);
+               out_le32((void __iomem *)SRDS_LNmRRSTCTL(base, m),
+                        RSTCTL_HLT_REQ);
+       }
+
+       if (master < 0) {
+               printf("lx2160a serdes: no master lane in the dpmac.1 group, 
refusing\n");
+               return -EINVAL;
+       }
+
+       if (poll_clear(SRDS_LNmTRSTCTL(base, master), RSTCTL_HLT_REQ) ||
+           poll_clear(SRDS_LNmRRSTCTL(base, master), RSTCTL_HLT_REQ)) {
+               printf("lx2160a serdes: lane %d did not halt within %d us, 
continuing\n",
+                      master, SPLIT_POLL_US);
+               ret = -ETIMEDOUT;
+       }
+
+       for (m = X10G_FIRST_LANE; m <= X10G_LAST_LANE; m++)
+               out_le32((void __iomem *)SRDS_LNmGCR0(base, m), X10G_GCR0);
+
+       out_le32((void __iomem *)SRDS_PCCE(base), X10G_PCCE);
+       out_le32((void __iomem *)SRDS_PCCC(base), X10G_PCCC);
+
+       /* Reset BOTH halves, not just the four lanes whose GCR0 changed */
+       for (m = X10G_FIRST_LANE; m <= X10G_LAST_LANE; m++) {
+               out_le32((void __iomem *)SRDS_LNmTRSTCTL(base, m),
+                        RSTCTL_RST_REQ);
+               out_le32((void __iomem *)SRDS_LNmRRSTCTL(base, m),
+                        RSTCTL_RST_REQ);
+       }
+
+       for (m = 0; m < SRDS_LANES; m++) {
+               if (m >= X10G_FIRST_LANE && m <= X10G_LAST_LANE)
+                       continue;
+               if (m == keep_master)
+                       continue;
+               out_le32((void __iomem *)SRDS_LNmTRSTCTL(base, m),
+                        RSTCTL_RST_REQ);
+               out_le32((void __iomem *)SRDS_LNmRRSTCTL(base, m),
+                        RSTCTL_RST_REQ);
+       }
+
+       if (keep_master >= 0) {
+               out_le32((void __iomem *)SRDS_LNmTRSTCTL(base, keep_master),
+                        RSTCTL_RST_REQ);
+               out_le32((void __iomem *)SRDS_LNmRRSTCTL(base, keep_master),
+                        RSTCTL_RST_REQ);
+       } else {
+               printf("lx2160a serdes: no master lane in the kept 40GE group, 
it was not reset\n");
+               ret = -EINVAL;
+       }
+
+       for (m = 0; m < SRDS_LANES; m++) {
+               u32 t, r;
+
+               if (poll_clear(SRDS_LNmTRSTCTL(base, m), RSTCTL_RST_REQ) ||
+                   poll_clear(SRDS_LNmRRSTCTL(base, m), RSTCTL_RST_REQ)) {
+                       printf("lx2160a serdes: lane %d reset did not complete 
within %d us\n",
+                              m, SPLIT_POLL_US);
+                       ret = -ETIMEDOUT;
+                       continue;
+               }
+
+               t = in_le32((void __iomem *)SRDS_LNmTRSTCTL(base, m));
+               r = in_le32((void __iomem *)SRDS_LNmRRSTCTL(base, m));
+               if (!(t & RSTCTL_RST_DONE) || !(r & RSTCTL_RST_DONE)) {
+                       printf("lx2160a serdes: lane %d reset finished without 
RST_DONE (TRSTCTL=%08x RRSTCTL=%08x)\n",
+                              m, t, r);
+                       ret = -EIO;
+               }
+       }
+
+       if (!srds1_is_40g_4x10g(base)) {
+               printf("lx2160a serdes: 4x 10G split did not take (PCCC=%08x 
PCCE=%08x)\n",
+                      in_le32((void __iomem *)SRDS_PCCC(base)),
+                      in_le32((void __iomem *)SRDS_PCCE(base)));
+               return -EIO;
+       }
+
+       printf("lx2160a serdes: SD1 now 1x 40GE (dpmac.2) + 4x 10G 
(dpmac.3-6)%s\n",
+              ret ? " (with warnings above)" : "");
+
+       return ret;
+}
+
 /*
  *   setenv hwconfig serdes1:100g4x25g
  *   setenv hwconfig serdes1:2x40g
@@ -598,8 +746,9 @@ int lx2160a_serdes1_to_40g(void)
 void lx2160a_serdes_apply_hwconfig(void)
 {
        if (hwconfig_sub("serdes1", "100g4x25g")) {
-               if (hwconfig_sub("serdes1", "2x40g"))
-                       printf("lx2160a serdes: hwconfig asks for both 
serdes1:100g4x25g and serdes1:2x40g, doing the split only\n");
+               if (hwconfig_sub("serdes1", "2x40g") ||
+                   hwconfig_sub("serdes1", "40g4x10g"))
+                       printf("lx2160a serdes: hwconfig asks for 
serdes1:100g4x25g alongside a 40GE mode, doing the split only\n");
                if (lx2160a_serdes1_is_split())
                        return;
 
@@ -607,6 +756,23 @@ void lx2160a_serdes_apply_hwconfig(void)
                return;
        }
 
+       /*
+        * serdes1:40g4x10g is a target MODE, not a second step the user has
+        * to sequence by hand: it implies the 40GE conversion first,
+        * because 1x 40GE + 4x 10G is reached by splitting one of the two
+        * 40GE ports.
+        */
+       if (hwconfig_sub("serdes1", "40g4x10g")) {
+               if (lx2160a_serdes1_is_40g_4x10g())
+                       return;
+
+               if (!lx2160a_serdes1_is_40g() && lx2160a_serdes1_to_40g())
+                       return;
+
+               lx2160a_serdes1_40g_4x10g();
+               return;
+       }
+
        if (!hwconfig_sub("serdes1", "2x40g"))
                return;
 
@@ -671,6 +837,22 @@ static int do_lx2160_sd1(struct cmd_tbl *cmdtp, int flag,
                return CMD_RET_SUCCESS;
        }
 
+       if (!strcmp(argv[1], "40g4x10g")) {
+               if (!IS_ENABLED(CONFIG_ARCH_LX2160A)) {
+                       printf("no 40GE on SerDes 1 to split on this SoC\n");
+                       return CMD_RET_FAILURE;
+               }
+               if (argc != 2)
+                       return CMD_RET_USAGE;
+               if (lx2160a_serdes1_is_40g_4x10g()) {
+                       printf("SD1 is already 1x 40GE + 4x 10G, nothing to 
do\n");
+                       return CMD_RET_SUCCESS;
+               }
+               if (lx2160a_serdes1_40g_4x10g())
+                       return CMD_RET_FAILURE;
+               return CMD_RET_SUCCESS;
+       }
+
        return CMD_RET_USAGE;
 }
 
@@ -714,7 +896,10 @@ static int do_lx2160_serdes(struct cmd_tbl *cmdtp, int 
flag,
        "    - convert SD1 2x 100GE into 2x 40GE without touching the RCW.\n"   
\
        "      Must run before the MC starts, and the DPC must carry\n"         
\
        "      board_info/serdes/follow_hw_pssr: RCWSR still reads 13.\n"       
\
-       "      Undo with `reset`. Permanent: setenv hwconfig serdes1:2x40g"
+       "      Undo with `reset`. Permanent: setenv hwconfig serdes1:2x40g\n"   
\
+       "lx2160_sd1 40g4x10g\n"                                                 
\
+       "    - split SD1's dpmac.1 40GE port into 4x 10G,\n"                    
\
+       "      Permanent: setenv hwconfig serdes1:40g4x10g (implies 2x40g)"
 #else
 #define SD1_CMD_HELP                                                   \
        "    - no SerDes 1 Ethernet modes on this SoC"
diff --git a/board/nxp/lx2160a/serdes_split.h b/board/nxp/lx2160a/serdes_split.h
index 36d486d7301..1b65848db2f 100644
--- a/board/nxp/lx2160a/serdes_split.h
+++ b/board/nxp/lx2160a/serdes_split.h
@@ -34,6 +34,17 @@ bool lx2160a_serdes1_is_40g(void);
  */
 int lx2160a_serdes1_to_40g(void);
 
+/* Is SerDes 1 already 1x 40GE + 4x 10G? */
+bool lx2160a_serdes1_is_40g_4x10g(void);
+
+/*
+ * Split SerDes 1's dpmac.1 40GE port into 4x 10G (dpmac.3-6),
+ * keeping dpmac.2 as one 40GE port.
+ *
+ * Refuses unless SD1 is in the 2x 40GE state.
+ */
+int lx2160a_serdes1_40g_4x10g(void);
+
 void lx2160a_serdes_apply_hwconfig(void);
 
 #endif /* __LX2160A_SERDES_SPLIT_H */
-- 
2.43.0

Reply via email to