#include <asm/io.h>
#include <asm/processor.h>
#include <asm/fsl_law.h>
+#include <asm/errno.h>
#include "fsl_corenet_serdes.h"
+/*
+ * The work-arounds for erratum SERDES8 and SERDES-A001 are linked together.
+ * The code is already very complicated as it is, and separating the two
+ * completely would just make things worse. We try to keep them as separate
+ * as possible, but for now we require SERDES8 if SERDES_A001 is defined.
+ */
+#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES_A001
+#ifndef CONFIG_SYS_P4080_ERRATUM_SERDES8
+#error "CONFIG_SYS_P4080_ERRATUM_SERDES_A001 requires CONFIG_SYS_P4080_ERRATUM_SERDES8"
+#endif
+#endif
+
static u32 serdes_prtcl_map;
#define HWCONFIG_BUFFER_SIZE 128
return lanes[lane].idx;
}
-int serdes_get_bank(int lane)
+int serdes_get_bank_by_lane(int lane)
{
return lanes[lane].bank;
}
return (1 << device) & serdes_prtcl_map;
}
+static int __serdes_get_first_lane(uint32_t prtcl, enum srds_prtcl device)
+{
+ int i;
+
+ for (i = 0; i < SRDS_MAX_LANES; i++) {
+ if (serdes_get_prtcl(prtcl, i) == device)
+ return i;
+ }
+
+ return -ENODEV;
+}
+
+/*
+ * Returns the SERDES lane (0..SRDS_MAX_LANES-1) that routes to the given
+ * device. This depends on the current SERDES protocol, as defined in the RCW.
+ *
+ * Returns a negative error code if SERDES is disabled or the given device is
+ * not supported in the current SERDES protocol.
+ */
+int serdes_get_first_lane(enum srds_prtcl device)
+{
+ u32 prtcl;
+ const ccsr_gur_t *gur;
+
+ gur = (typeof(gur))CONFIG_SYS_MPC85xx_GUTS_ADDR;
+
+ /* Is serdes enabled at all? */
+ if (unlikely((in_be32(&gur->rcwsr[5]) & 0x2000) == 0))
+ return -ENODEV;
+
+ prtcl = (in_be32(&gur->rcwsr[4]) & FSL_CORENET_RCWSR4_SRDS_PRTCL) >> 26;
+
+ 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
#endif
#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES8
+/*
+ * Enable a SERDES bank that was disabled via the RCW
+ *
+ * We only call this function for SERDES8 and SERDES-A001 in cases we really
+ * want to enable the bank, whether we actually want to use the lanes or not,
+ * so make sure at least one lane is enabled. We're only enabling this one
+ * lane to satisfy errata requirements that the bank be enabled.
+ *
+ * We use a local variable instead of srds_lpd_b[] because we want drivers to
+ * think that the lanes actually are disabled.
+ */
static void enable_bank(ccsr_gur_t *gur, int bank)
{
u32 rcw5;
+ u32 temp_lpd_b = srds_lpd_b[bank];
+
+ /*
+ * If we're asked to disable all lanes, just pretend we're doing
+ * that.
+ */
+ if (temp_lpd_b == 0xF)
+ temp_lpd_b = 0xE;
/*
* Enable the lanes SRDS_LPD_Bn. The RCW bits are read-only in
rcw5 = in_be32(gur->rcwsr + 5);
if (bank == FSL_SRDS_BANK_2) {
rcw5 &= ~FSL_CORENET_RCWSRn_SRDS_LPD_B2;
- rcw5 |= srds_lpd_b[bank] << 26;
+ rcw5 |= temp_lpd_b << 26;
} else if (bank == FSL_SRDS_BANK_3) {
rcw5 &= ~FSL_CORENET_RCWSRn_SRDS_LPD_B3;
- rcw5 |= srds_lpd_b[bank] << 18;
+ rcw5 |= temp_lpd_b << 18;
} else {
printf("SERDES: enable_bank: bad bank %d\n", bank + 1);
return;
*/
setbits_be32(®s->bank[FSL_SRDS_BANK_3].pllcr1,
SRDS_PLLCR1_PLL_BWSEL);
-
- enable_bank(gur, FSL_SRDS_BANK_3);
break;
case 0x0f:
SRDS_PLLCR0_FRATE_SEL_MASK,
SRDS_PLLCR0_FRATE_SEL_6_25);
break;
- default:
- enable_bank(gur, FSL_SRDS_BANK_3);
}
+ enable_bank(gur, FSL_SRDS_BANK_3);
+}
+#endif
+
+#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES_A005
+/*
+ * If PCIe is not selected as a protocol for any lanes driven by a given PLL,
+ * that PLL should have SRDSBnPLLCR1[PLLBW_SEL] = 0.
+ */
+static void p4080_erratum_serdes_a005(serdes_corenet_t *regs, unsigned int cfg)
+{
+ enum srds_prtcl device;
+
+ switch (cfg) {
+ case 0x13:
+ case 0x16:
+ /*
+ * If SRDS_PRTCL = 0x13 or 0x16, set SRDSB1PLLCR1[PLLBW_SEL]
+ * to 0.
+ */
+ clrbits_be32(®s->bank[FSL_SRDS_BANK_1].pllcr1,
+ SRDS_PLLCR1_PLL_BWSEL);
+ break;
+ case 0x19:
+ /*
+ * If SRDS_PRTCL = 0x19, set SRDSB1PLLCR1[PLLBW_SEL] to 0 and
+ * SRDSB3PLLCR1[PLLBW_SEL] to 1.
+ */
+ clrbits_be32(®s->bank[FSL_SRDS_BANK_1].pllcr1,
+ SRDS_PLLCR1_PLL_BWSEL);
+ setbits_be32(®s->bank[FSL_SRDS_BANK_3].pllcr1,
+ SRDS_PLLCR1_PLL_BWSEL);
+ break;
+ }
+
+ /*
+ * Set SRDSBnPLLCR1[PLLBW_SEL] to 0 for each bank that selects XAUI
+ * before XAUI is initialized.
+ */
+ for (device = XAUI_FM1; device <= XAUI_FM2; device++) {
+ if (is_serdes_configured(device)) {
+ int bank = serdes_get_bank_by_device(cfg, device);
+
+ clrbits_be32(®s->bank[bank].pllcr1,
+ SRDS_PLLCR1_PLL_BWSEL);
+ }
+ }
}
#endif
+/*
+ * Wait for the RSTDONE bit to get set, or a one-second timeout.
+ */
+static void wait_for_rstdone(unsigned int bank)
+{
+ serdes_corenet_t *srds_regs =
+ (void *)CONFIG_SYS_FSL_CORENET_SERDES_ADDR;
+ unsigned long long end_tick;
+ u32 rstctl;
+
+ /* wait for reset complete or 1-second timeout */
+ end_tick = usec2ticks(1000000) + get_ticks();
+ do {
+ rstctl = in_be32(&srds_regs->bank[bank].rstctl);
+ if (rstctl & SRDS_RSTCTL_RSTDONE)
+ break;
+ } while (end_tick > get_ticks());
+
+ if (!(rstctl & SRDS_RSTCTL_RSTDONE))
+ printf("SERDES: timeout resetting bank %u\n", bank + 1);
+}
+
+
+void __soc_serdes_init(void)
+{
+ /* Allow for SoC-specific initialization in <SOC>_serdes.c */
+};
+void soc_serdes_init(void) __attribute__((weak, alias("__soc_serdes_init")));
+
void fsl_serdes_init(void)
{
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
serdes_corenet_t *srds_regs;
int lane, bank, idx;
enum srds_prtcl lane_prtcl;
- long long end_tick;
int have_bank[SRDS_MAX_BANK] = {};
#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES8
u32 serdes8_devdisr = 0;
const char *srds_lpd_arg;
size_t arglen;
#endif
+#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES9
+ enum srds_prtcl device;
+#endif
+#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES_A001
+ int need_serdes_a001; /* TRUE == need work-around for SERDES A001 */
+#endif
char buffer[HWCONFIG_BUFFER_SIZE];
char *buf = NULL;
#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES8
/*
+ * Display a warning if banks two and three are not disabled in the RCW,
+ * since our work-around for SERDES8 depends on these banks being
+ * disabled at power-on.
+ */
+#define B2_B3 (FSL_CORENET_RCWSRn_SRDS_LPD_B2 | FSL_CORENET_RCWSRn_SRDS_LPD_B3)
+ if ((in_be32(&gur->rcwsr[5]) & B2_B3) != B2_B3) {
+ printf("Warning: SERDES8 requires banks two and "
+ "three to be disabled in the RCW\n");
+ }
+
+ /*
* Store the values of the fsl_srds_lpd_b2 and fsl_srds_lpd_b3
* hwconfig options into the srds_lpd_b[] array. See README.p4080ds
* for a description of these options.
srds_lpd_b[bank] =
simple_strtoul(srds_lpd_arg, NULL, 0) & 0xf;
}
+
+ if ((cfg == 0xf) || (cfg == 0x10)) {
+ /*
+ * For SERDES protocols 0xF and 0x10, force bank 3 to be
+ * disabled, because it is not supported.
+ */
+ srds_lpd_b[FSL_SRDS_BANK_3] = 0xF;
+ }
#endif
/* Look for banks with all lanes disabled, and power down the bank. */
for (lane = 0; lane < SRDS_MAX_LANES; lane++) {
enum srds_prtcl lane_prtcl = serdes_get_prtcl(cfg, lane);
if (serdes_lane_enabled(lane)) {
- have_bank[serdes_get_bank(lane)] = 1;
+ have_bank[serdes_get_bank_by_lane(lane)] = 1;
serdes_prtcl_map |= (1 << lane_prtcl);
}
}
+ soc_serdes_init();
+
#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES8
/*
* Bank two uses the clock from bank three, so if bank two is enabled,
have_bank[FSL_SRDS_BANK_3] = 1;
#endif
+#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES_A001
+ /*
+ * The work-aroud for erratum SERDES-A001 is needed only if bank two
+ * is disabled and bank three is enabled. The converse is also true,
+ * but SERDES8 ensures that bank 3 is always enabled if bank 2 is
+ * enabled, so there's no point in complicating the code to handle
+ * that situation.
+ */
+ need_serdes_a001 =
+ !have_bank[FSL_SRDS_BANK_2] && have_bank[FSL_SRDS_BANK_3];
+#endif
+
+ /* Power down the banks we're not interested in */
for (bank = 0; bank < SRDS_MAX_BANK; bank++) {
if (!have_bank[bank]) {
printf("SERDES: bank %d disabled\n", bank + 1);
+#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES_A001
+ /*
+ * Erratum SERDES-A001 says bank two needs to be powered
+ * down after bank three is powered up, so don't power
+ * down bank two here.
+ */
+ if (!need_serdes_a001 || (bank != FSL_SRDS_BANK_2))
+ setbits_be32(&srds_regs->bank[bank].rstctl,
+ SRDS_RSTCTL_SDPD);
+#else
setbits_be32(&srds_regs->bank[bank].rstctl,
SRDS_RSTCTL_SDPD);
+#endif
}
}
printf("%s ", serdes_prtcl_str[lane_prtcl]);
#endif
+#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES9
+ /*
+ * Set BnTTLCRy0[FLT_SEL] = 000011 and set BnTTLCRy0[17] = 1 for
+ * each of the SerDes lanes selected as SGMII, XAUI, SRIO, or
+ * AURORA before the device is initialized.
+ */
+ switch (lane_prtcl) {
+ case SGMII_FM1_DTSEC1:
+ case SGMII_FM1_DTSEC2:
+ case SGMII_FM1_DTSEC3:
+ case SGMII_FM1_DTSEC4:
+ case SGMII_FM2_DTSEC1:
+ case SGMII_FM2_DTSEC2:
+ case SGMII_FM2_DTSEC3:
+ case SGMII_FM2_DTSEC4:
+ case XAUI_FM1:
+ case XAUI_FM2:
+ case SRIO1:
+ case SRIO2:
+ case AURORA:
+ clrsetbits_be32(&srds_regs->lane[idx].ttlcr0,
+ SRDS_TTLCR0_FLT_SEL_MASK,
+ SRDS_TTLCR0_FLT_SEL_750PPM |
+ SRDS_TTLCR0_PM_DIS);
+ default:
+ break;
+ }
+#endif
+
#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES8
switch (lane_prtcl) {
case PCIE1:
FSL_CORENET_DEVDISR2_DTSEC2_4;
break;
case XAUI_FM1:
+ serdes8_devdisr2 |= FSL_CORENET_DEVDISR2_FM1 |
+ FSL_CORENET_DEVDISR2_10GEC1;
+ break;
case XAUI_FM2:
- if (lane_prtcl == XAUI_FM1)
- serdes8_devdisr2 |= FSL_CORENET_DEVDISR2_FM1 |
- FSL_CORENET_DEVDISR2_10GEC1;
- else
- serdes8_devdisr2 |= FSL_CORENET_DEVDISR2_FM2 |
- FSL_CORENET_DEVDISR2_10GEC2;
+ serdes8_devdisr2 |= FSL_CORENET_DEVDISR2_FM2 |
+ FSL_CORENET_DEVDISR2_10GEC2;
break;
case AURORA:
break;
puts("\n");
#endif
- for (idx = 0; idx < SRDS_MAX_BANK; idx++) {
- u32 rstctl;
+#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES_A005
+ p4080_erratum_serdes_a005(srds_regs, cfg);
+#endif
+ for (idx = 0; idx < SRDS_MAX_BANK; idx++) {
bank = idx;
#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES8
p4080_erratum_serdes8(srds_regs, gur, serdes8_devdisr,
serdes8_devdisr2, cfg);
} else if (idx == 2) {
- /* Eable bank two now that bank three is enabled. */
+ /* Enable bank two now that bank three is enabled. */
enable_bank(gur, FSL_SRDS_BANK_2);
}
#endif
- /* reset banks for errata */
- setbits_be32(&srds_regs->bank[bank].rstctl, SRDS_RSTCTL_RST);
+ wait_for_rstdone(bank);
+ }
- /* wait for reset complete or 1-second timeout */
- end_tick = usec2ticks(1000000) + get_ticks();
- do {
- rstctl = in_be32(&srds_regs->bank[bank].rstctl);
- if (rstctl & SRDS_RSTCTL_RSTDONE)
- break;
- } while (end_tick > get_ticks());
+#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES_A001
+ if (need_serdes_a001) {
+ /* Bank 3 has been enabled, so now we can disable bank 2 */
+ setbits_be32(&srds_regs->bank[FSL_SRDS_BANK_2].rstctl,
+ SRDS_RSTCTL_SDPD);
+ }
+#endif
- if (!(rstctl & SRDS_RSTCTL_RSTDONE)) {
- printf("SERDES: timeout resetting bank %d\n",
- bank + 1);
- 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
}