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
