The SD1 lanes can be set in more cases than the 1x100GE + 4x25G
split. Describe each case as a mode, an entry of a table:
  - its hwconfig name
  - how to tell SD1 is in it
  - the mode it is entered from
  - the sequence that enters it

The two existing states become the first two modes, under their
serdes1 names:
  - the "split" state, 1x100GE + 4x25G, is 100g4x25g
  - the "unsplit" state, 2x100GE, is 2x100g

A mode is entered only when SD1 is in the mode it starts from, as
the split required the unsplit state. The serdes1 value is a single
mode name, read with hwconfig_arg().

Suggested-by: Vladimir Oltean <[email protected]>
Signed-off-by: Vincent Jardin <[email protected]>
Reviewed-by: Vladimir Oltean <[email protected]>

---

(no changes since v5)

Changes in v5:
- SPLIT_POLL_US and the halt and reset helpers are separate patches
- Keep the order of the state checks, tell old state is which mode
- sd1_mode_find(): use a name and its length from the start
- The serdes1 value is one mode name, read with hwconfig_arg()
- No skip logic yet, entering a mode through another one is another
  topic

Changes in v4:
- New patch

 board/nxp/lx2160a/serdes_split.c | 127 ++++++++++++++++++++++---------
 board/nxp/lx2160a/serdes_split.h |  16 +---
 2 files changed, 92 insertions(+), 51 deletions(-)

diff --git a/board/nxp/lx2160a/serdes_split.c b/board/nxp/lx2160a/serdes_split.c
index 5dcdeff5898..1cc9c7e5509 100644
--- a/board/nxp/lx2160a/serdes_split.c
+++ b/board/nxp/lx2160a/serdes_split.c
@@ -246,8 +246,16 @@ static void print_rcw_view(int sd)
 
 #if defined(CONFIG_ARCH_LX2160A)
 
+struct sd1_mode {
+       const char *name;                       /* hwconfig serdes1:<name> */
+       const char *desc;
+       const char *from;
+       bool (*is_active)(unsigned long base);
+       int (*enter)(unsigned long base);
+};
+
 /*
- * The split itself.
+ * 1x100GE + 4x25G
  *
  * This is NXP's e100g1_split.rcw sequence, moved from the PBI into
  * U-Boot.
@@ -266,7 +274,7 @@ static void print_rcw_view(int sd)
 
 #define SRDS_POLL_US           10000           /* NXP waits ~100 cycles   */
 
-static bool srds1_split_state(unsigned long base)
+static bool sd1_is_100g4x25g(unsigned long base)
 {
        u32 pccd = in_le32((void __iomem *)SRDS_PCCD(base));
        u32 pcce = in_le32((void __iomem *)SRDS_PCCE(base));
@@ -276,12 +284,7 @@ static bool srds1_split_state(unsigned long base)
               PCCD_E25G_CFG(pccd, 2) && PCCD_E25G_CFG(pccd, 3);
 }
 
-bool lx2160a_serdes1_is_split(void)
-{
-       return srds1_split_state(srds_base(FSL_SRDS_1));
-}
-
-static bool srds1_is_unsplit(unsigned long base)
+static bool sd1_is_2x100g(unsigned long base)
 {
        u32 pccd = in_le32((void __iomem *)SRDS_PCCD(base));
        u32 pcce = in_le32((void __iomem *)SRDS_PCCE(base));
@@ -399,24 +402,11 @@ static int sd1_reset_wait(unsigned long base, unsigned 
long lanes)
        return ret;
 }
 
-int lx2160a_serdes1_split(void)
+static int sd1_enter_100g4x25g(unsigned long base)
 {
-       unsigned long base = srds_base(FSL_SRDS_1);
        unsigned long lanes = SD1_LNE_LNH;
        int m, ret, err;
 
-       if (srds1_split_state(base)) {
-               debug("lx2160a serdes: SD1 already split, nothing to do\n");
-               return 0;
-       }
-
-       if (!srds1_is_unsplit(base)) {
-               printf("lx2160a serdes: SD1 is in neither the split nor the 
unsplit state (PCCD=%08x PCCE=%08x), refusing\n",
-                      in_le32((void __iomem *)SRDS_PCCD(base)),
-                      in_le32((void __iomem *)SRDS_PCCE(base)));
-               return -EINVAL;
-       }
-
        /* Lanes E-H are one 100G port at this point. */
        ret = sd1_halt_port(base, lanes);
        if (ret == -EINVAL)
@@ -436,36 +426,99 @@ int lx2160a_serdes1_split(void)
        /* Reset. Every lane is its own master now, so all four count. */
        sd1_reset_req(base, lanes);
        err = sd1_reset_wait(base, lanes);
-       if (err)
-               ret = err;
-
-       if (!srds1_split_state(base)) {
-               printf("lx2160a serdes: split did not take (PCCD=%08x 
PCCE=%08x)\n",
-                      in_le32((void __iomem *)SRDS_PCCD(base)),
-                      in_le32((void __iomem *)SRDS_PCCE(base)));
-               return -EIO;
+
+       return err ? err : ret;
+}
+
+static const struct sd1_mode sd1_modes[] = {
+       {
+               .name = "2x100g",
+               .desc = "2x 100GE",
+               .is_active = sd1_is_2x100g,
+       },
+       {
+               .name = "100g4x25g",
+               .desc = "1x 100GE + 4x 25G",
+               .from = "2x100g",
+               .is_active = sd1_is_100g4x25g,
+               .enter = sd1_enter_100g4x25g,
+       },
+};
+
+static const struct sd1_mode *sd1_mode_find(const char *name, size_t len)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(sd1_modes); i++)
+               if (strlen(sd1_modes[i].name) == len &&
+                   !strncmp(sd1_modes[i].name, name, len))
+                       return &sd1_modes[i];
+
+       return NULL;
+}
+
+static void sd1_print_pcc(unsigned long base)
+{
+       printf("PCCC=%08x PCCD=%08x PCCE=%08x",
+              in_le32((void __iomem *)SRDS_PCCC(base)),
+              in_le32((void __iomem *)SRDS_PCCD(base)),
+              in_le32((void __iomem *)SRDS_PCCE(base)));
+}
+
+static int sd1_enter(const struct sd1_mode *mode)
+{
+       unsigned long base = srds_base(FSL_SRDS_1);
+       const struct sd1_mode *from = NULL;
+       int ret;
+
+       if (mode->is_active(base))
+               return 0;
+
+       if (mode->from)
+               from = sd1_mode_find(mode->from, strlen(mode->from));
+
+       if (!mode->enter || !from || !from->is_active(base)) {
+               printf("lx2160a serdes: serdes1:%s needs SD1 to be %s, it is 
not (",
+                      mode->name, from ? from->desc : mode->desc);
+               sd1_print_pcc(base);
+               printf("), refusing\n");
+               return -EINVAL;
+       }
+
+       ret = mode->enter(base);
+       if (!mode->is_active(base)) {
+               printf("lx2160a serdes: SD1 is not %s (", mode->desc);
+               sd1_print_pcc(base);
+               printf("), stopping\n");
+               return ret ? ret : -EIO;
        }
 
-       printf("lx2160a serdes: SD1 100GE.1 split into 4x 25G%s\n",
+       printf("lx2160a serdes: SD1 now %s%s\n", mode->desc,
               ret ? " (with warnings above)" : "");
 
-       return ret;
+       return 0;
 }
 
 /*
- *   setenv hwconfig serdes1:100g4x25g
+ *   setenv hwconfig serdes1:<mode>
  *
- * Idempotent: a board whose PBI already split SD1 skip it too.
+ * Idempotent: a board whose PBI already configured SD1 skips it.
  */
 void lx2160a_serdes_apply_hwconfig(void)
 {
-       if (!hwconfig_sub("serdes1", "100g4x25g"))
+       const struct sd1_mode *mode;
+       const char *arg;
+       size_t len;
+
+       arg = hwconfig_arg("serdes1", &len);
+       if (!arg)
                return;
 
-       if (lx2160a_serdes1_is_split())
+       mode = sd1_mode_find(arg, len);
+       if (!mode || mode->is_active(srds_base(FSL_SRDS_1)))
                return;
 
-       lx2160a_serdes1_split();
+       sd1_enter(mode);
 }
 
 #endif /* CONFIG_ARCH_LX2160A */
diff --git a/board/nxp/lx2160a/serdes_split.h b/board/nxp/lx2160a/serdes_split.h
index 8dce9ddf97a..5a820c8c5d8 100644
--- a/board/nxp/lx2160a/serdes_split.h
+++ b/board/nxp/lx2160a/serdes_split.h
@@ -7,25 +7,13 @@
 #define __LX2160A_SERDES_SPLIT_H
 
 /*
- * The split is LX2160A only: LX2162A's SerDes 1 is four lanes with no
+ * SD1 modes are for LX2160A only: LX2162A's SD 1 is four lanes with no
  * 100GE in its protocol table.
  */
 #if defined(CONFIG_ARCH_LX2160A)
 
-/* Is SerDes 1 already 1x 100G + 4x 25G? */
-bool lx2160a_serdes1_is_split(void);
-
-/*
- * Split SerDes 1's 100GE.1 (lanes LNE-LNH) into 4x 25G.
- *
- * Refuses unless SD1 is in the unsplit state, and must be called before
- * the Management Complex starts: MC latches the live protocol-converter
- * configuration when it boots.
- */
-int lx2160a_serdes1_split(void);
-
 /*
- * Apply `hwconfig=serdes1:100g4x25g` if the board's environment asks for it.
+ * Apply `hwconfig=serdes1:<mode>` if the board's environment asks for it.
  * Boards call this from reset_phy(), before mc_env_boot().
  */
 void lx2160a_serdes_apply_hwconfig(void);
-- 
2.43.0

Reply via email to