The split halts the 100GE port on LNE-LNH, reconfigures its lanes,
then resets. The following patches do the same halt and reset on
other lanes, for the other SD1 modes.

Move them into helpers.

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

---

(no changes since v5)

Changes in v5:
- New patch, split out of "set SD1 with a table"

 board/nxp/lx2160a/serdes_split.c | 132 ++++++++++++++++++++-----------
 1 file changed, 88 insertions(+), 44 deletions(-)

diff --git a/board/nxp/lx2160a/serdes_split.c b/board/nxp/lx2160a/serdes_split.c
index aeae8fbb340..5dcdeff5898 100644
--- a/board/nxp/lx2160a/serdes_split.c
+++ b/board/nxp/lx2160a/serdes_split.c
@@ -12,6 +12,7 @@
 #include <asm/arch/fsl_serdes.h>
 #include <asm/arch/immap_lsch3.h>
 #include <asm/arch/soc.h>              /* gur_in32() picks the GUR endianness 
*/
+#include <linux/bitmap.h>
 #include <linux/kernel.h>
 #include <linux/string.h>
 #include <linux/delay.h>
@@ -251,8 +252,7 @@ static void print_rcw_view(int sd)
  * This is NXP's e100g1_split.rcw sequence, moved from the PBI into
  * U-Boot.
  */
-#define SPLIT_FIRST_LANE       4       /* LNE */
-#define SPLIT_LAST_LANE                7       /* LNH */
+#define SD1_LNE_LNH            GENMASK(7, 4)
 
 /*
  * PROTO_SEL=11010b (25G/50G/100G, shared) + IF_WIDTH=100b (40-bit),
@@ -309,73 +309,74 @@ static int poll_clear(unsigned long reg, u32 bit)
        return -ETIMEDOUT;
 }
 
-int lx2160a_serdes1_split(void)
+static int sd1_port_master(unsigned long base, unsigned long lanes)
 {
-       unsigned long base = srds_base(FSL_SRDS_1);
-       int m, master = -1, ret = 0;
-
-       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;
-       }
+       int m, master = -1;
 
-       /*
-        * Halt. "Halt only has meaning on the master source
-        * clock lane of a port (LNmGCR0[PORT_LN0_B]=0)". Lanes E-H are one
-        * 100G port at this point, so exactly one of them is the master and
-        * the other three requests are ignored. Issue all four anyway
-        * (that is what NXP's sequence does) but only wait on the lane
-        * where the bit can actually clear.
-        */
-       for (m = SPLIT_FIRST_LANE; m <= SPLIT_LAST_LANE; m++) {
+       for_each_set_bit(m, &lanes, SRDS_LANES)
                if (!(in_le32((void __iomem *)SRDS_LNmGCR0(base, m)) &
                      GCR0_PORT_LN0_B))
                        master = m;
+
+       if (master < 0)
+               printf("lx2160a serdes: no master lane among LN%c-LN%c, 
refusing\n",
+                      'A' + (int)__ffs(lanes), 'A' + (int)__fls(lanes));
+
+       return master;
+}
+
+/*
+ * Halt the port made of @lanes.
+ *
+ * "Halt only has meaning on the master source clock lane of a port
+ * (LNmGCR0[PORT_LN0_B]=0)", so the request goes to every lane, as
+ * NXP's sequence does, and only the master is waited on:
+ * it is the one lane where the bit can clear.
+ *
+ *  -EINVAL means nothing was touched
+ *  -ETIMEDOUT is only a warning
+ */
+static int sd1_halt_port(unsigned long base, unsigned long lanes)
+{
+       int m, master = sd1_port_master(base, lanes);
+
+       if (master < 0)
+               return -EINVAL;
+
+       for_each_set_bit(m, &lanes, SRDS_LANES) {
                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 among LNE-LNH, 
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: LN%c did not halt within %d us, 
continuing\n",
                       'A' + master, SRDS_POLL_US);
-               ret = -ETIMEDOUT;
+               return -ETIMEDOUT;
        }
 
-       /*
-        * Reconfigure while halted: four one-lane ports instead of one
-        * four-lane port. Everything else about the lane is unchanged.
-        */
-       for (m = SPLIT_FIRST_LANE; m <= SPLIT_LAST_LANE; m++)
-               out_le32((void __iomem *)SRDS_LNmGCR0(base, m), SPLIT_GCR0);
+       return 0;
+}
 
-       /* Re-point the protocol converters. */
-       out_le32((void __iomem *)SRDS_PCCE(base), SPLIT_PCCE);
-       out_le32((void __iomem *)SRDS_PCCD(base), SPLIT_PCCD);
+static void sd1_reset_req(unsigned long base, unsigned long lanes)
+{
+       int m;
 
-       /* Reset. Every lane is its own master now, so all four count. */
-       for (m = SPLIT_FIRST_LANE; m <= SPLIT_LAST_LANE; m++) {
+       for_each_set_bit(m, &lanes, SRDS_LANES) {
                out_le32((void __iomem *)SRDS_LNmTRSTCTL(base, m),
                         RSTCTL_RST_REQ);
                out_le32((void __iomem *)SRDS_LNmRRSTCTL(base, m),
                         RSTCTL_RST_REQ);
        }
+}
 
-       for (m = SPLIT_FIRST_LANE; m <= SPLIT_LAST_LANE; m++) {
+static int sd1_reset_wait(unsigned long base, unsigned long lanes)
+{
+       int m, ret = 0;
+
+       for_each_set_bit(m, &lanes, SRDS_LANES) {
                u32 t, r;
 
                if (poll_clear(SRDS_LNmTRSTCTL(base, m), RSTCTL_RST_REQ) ||
@@ -395,6 +396,49 @@ int lx2160a_serdes1_split(void)
                }
        }
 
+       return ret;
+}
+
+int lx2160a_serdes1_split(void)
+{
+       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)
+               return ret;
+
+       /*
+        * Reconfigure while halted: four one-lane ports instead of one
+        * four-lane port. Everything else about the lane is unchanged.
+        */
+       for_each_set_bit(m, &lanes, SRDS_LANES)
+               out_le32((void __iomem *)SRDS_LNmGCR0(base, m), SPLIT_GCR0);
+
+       /* Re-point the protocol converters. */
+       out_le32((void __iomem *)SRDS_PCCE(base), SPLIT_PCCE);
+       out_le32((void __iomem *)SRDS_PCCD(base), SPLIT_PCCD);
+
+       /* 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)),
-- 
2.43.0

Reply via email to