p4080/serdes: Implement the XAUI workaround for SERDES9 erratum
Signed-off-by: Emil Medve <Emilian.Medve@Freescale.com>
Signed-off-by: Timur Tabi <timur@freescale.com>
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
diff --git a/arch/powerpc/cpu/mpc85xx/cmd_errata.c b/arch/powerpc/cpu/mpc85xx/cmd_errata.c
index e94975a..0564816 100644
--- a/arch/powerpc/cpu/mpc85xx/cmd_errata.c
+++ b/arch/powerpc/cpu/mpc85xx/cmd_errata.c
@@ -44,6 +44,9 @@
#if defined(CONFIG_SYS_P4080_ERRATUM_SERDES8)
puts("Work-around for Erratum SERDES8 enabled\n");
#endif
+#if defined(CONFIG_SYS_P4080_ERRATUM_SERDES9)
+ puts("Work-around for Erratum SERDES9 enabled\n");
+#endif
#if defined(CONFIG_SYS_P4080_ERRATUM_CPU22)
puts("Work-around for Erratum CPU22 enabled\n");
#endif
diff --git a/arch/powerpc/cpu/mpc85xx/fsl_corenet_serdes.c b/arch/powerpc/cpu/mpc85xx/fsl_corenet_serdes.c
index 1177247..d39f963 100644
--- a/arch/powerpc/cpu/mpc85xx/fsl_corenet_serdes.c
+++ b/arch/powerpc/cpu/mpc85xx/fsl_corenet_serdes.c
@@ -168,6 +168,90 @@
return __serdes_get_first_lane(prtcl, device);
}
+#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES9
+/*
+ * Returns the SERDES bank (1, 2, or 3) that a given device is on for a given
+ * SERDES protocol.
+ *
+ * Returns a negative error code if the given device is not supported for the
+ * given SERDES protocol.
+ */
+static int serdes_get_bank_by_device(uint32_t prtcl, enum srds_prtcl device)
+{
+ int lane;
+
+ lane = __serdes_get_first_lane(prtcl, device);
+ if (unlikely(lane < 0))
+ return lane;
+
+ return serdes_get_bank_by_lane(lane);
+}
+
+static uint32_t __serdes_get_lane_count(uint32_t prtcl, enum srds_prtcl device,
+ int first)
+{
+ int lane;
+
+ for (lane = first; lane < SRDS_MAX_LANES; lane++) {
+ if (serdes_get_prtcl(prtcl, lane) != device)
+ break;
+ }
+
+ return lane - first;
+}
+
+static void __serdes_reset_rx(serdes_corenet_t *regs,
+ uint32_t prtcl,
+ enum srds_prtcl device)
+{
+ int lane, idx, first, last;
+
+ lane = __serdes_get_first_lane(prtcl, device);
+ if (unlikely(lane < 0))
+ return;
+ first = serdes_get_lane_idx(lane);
+ last = first + __serdes_get_lane_count(prtcl, device, lane);
+
+ /*
+ * Set BnGCRy0[RRST] = 0 for each lane in the each bank that is
+ * selected as XAUI to place the lane into reset.
+ */
+ for (idx = first; idx < last; idx++)
+ clrbits_be32(®s->lane[idx].gcr0, SRDS_GCR0_RRST);
+
+ /* Wait at least 250 ns */
+ udelay(1);
+
+ /*
+ * Set BnGCRy0[RRST] = 1 for each lane in the each bank that is
+ * selected as XAUI to bring the lane out of reset.
+ */
+ for (idx = first; idx < last; idx++)
+ setbits_be32(®s->lane[idx].gcr0, SRDS_GCR0_RRST);
+}
+
+void serdes_reset_rx(enum srds_prtcl device)
+{
+ u32 prtcl;
+ const ccsr_gur_t *gur;
+ serdes_corenet_t *regs;
+
+ if (unlikely(device == NONE))
+ return;
+
+ gur = (typeof(gur))CONFIG_SYS_MPC85xx_GUTS_ADDR;
+
+ /* Is serdes enabled at all? */
+ if (unlikely((in_be32(&gur->rcwsr[5]) & 0x2000) == 0))
+ return;
+
+ regs = (typeof(regs))CONFIG_SYS_FSL_CORENET_SERDES_ADDR;
+ prtcl = (in_be32(&gur->rcwsr[4]) & FSL_CORENET_RCWSR4_SRDS_PRTCL) >> 26;
+
+ __serdes_reset_rx(regs, prtcl, device);
+}
+#endif
+
#ifndef CONFIG_SYS_DCSRBAR_PHYS
#define CONFIG_SYS_DCSRBAR_PHYS 0x80000000 /* Must be 1GB-aligned for rev1.0 */
#define CONFIG_SYS_DCSRBAR 0x80000000
@@ -318,6 +402,9 @@
const char *srds_lpd_arg;
size_t arglen;
#endif
+#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES9
+ enum srds_prtcl device;
+#endif
char buffer[HWCONFIG_BUFFER_SIZE];
char *buf = NULL;
@@ -452,6 +539,17 @@
break;
case XAUI_FM1:
case XAUI_FM2:
+#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES9
+ /*
+ * Set BnTTLCRy0[FLT_SEL] = 000011 and set
+ * BnTTLCRy0[17] = 1 for each of the SerDes lanes
+ * selected as XAUI on each bank before XAUI is
+ * initialized.
+ */
+ clrsetbits_be32(&srds_regs->lane[idx].ttlcr0,
+ SRDS_TTLCR0_FLT_SEL_MASK,
+ 0x03000000 | SRDS_TTLCR0_PM_DIS);
+#endif
if (lane_prtcl == XAUI_FM1)
serdes8_devdisr2 |= FSL_CORENET_DEVDISR2_FM1 |
FSL_CORENET_DEVDISR2_10GEC1;
@@ -472,6 +570,8 @@
puts("\n");
#endif
+#endif
+
for (idx = 0; idx < SRDS_MAX_BANK; idx++) {
u32 rstctl;
@@ -527,4 +627,11 @@
continue;
}
}
+
+#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES9
+ for (device = XAUI_FM1; device <= XAUI_FM2; device++) {
+ if (is_serdes_configured(device))
+ __serdes_reset_rx(srds_regs, cfg, device);
+ }
+#endif
}
diff --git a/arch/powerpc/include/asm/config_mpc85xx.h b/arch/powerpc/include/asm/config_mpc85xx.h
index 41fd86c..88bc030 100644
--- a/arch/powerpc/include/asm/config_mpc85xx.h
+++ b/arch/powerpc/include/asm/config_mpc85xx.h
@@ -304,6 +304,7 @@
#define CONFIG_SYS_FSL_ERRATUM_ESDHC136
#define CONFIG_SYS_P4080_ERRATUM_CPU22
#define CONFIG_SYS_P4080_ERRATUM_SERDES8
+#define CONFIG_SYS_P4080_ERRATUM_SERDES9
/* P5010 is single core version of P5020 */
#elif defined(CONFIG_PPC_P5010)
diff --git a/arch/powerpc/include/asm/fsl_serdes.h b/arch/powerpc/include/asm/fsl_serdes.h
index 9d9f2e4..0f31af1 100644
--- a/arch/powerpc/include/asm/fsl_serdes.h
+++ b/arch/powerpc/include/asm/fsl_serdes.h
@@ -55,6 +55,9 @@
#ifdef CONFIG_FSL_CORENET
int serdes_get_first_lane(enum srds_prtcl device);
+#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES9
+void serdes_reset_rx(enum srds_prtcl device);
+#endif
#endif
#endif /* __FSL_SERDES_H */
diff --git a/arch/powerpc/include/asm/immap_85xx.h b/arch/powerpc/include/asm/immap_85xx.h
index d6ac61a..286ecb9 100644
--- a/arch/powerpc/include/asm/immap_85xx.h
+++ b/arch/powerpc/include/asm/immap_85xx.h
@@ -2137,6 +2137,8 @@
#define SRDS_TECR0_TEQ_TYPE_2LVL 0x10000000
u32 res3;
u32 ttlcr0; /* Transition Tracking Loop Ctrl 0 */
+#define SRDS_TTLCR0_FLT_SEL_MASK 0x3f000000
+#define SRDS_TTLCR0_PM_DIS 0x00004000
u32 res4[7];
} lane[24];
u32 res6[384];