in arch/powerpc/cpu/mpc85xx/fsl_corenet_serdes.c [492:871]
void fsl_serdes_init(void)
{
ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
int cfg;
serdes_corenet_t *srds_regs;
#ifdef CONFIG_ARCH_P5040
serdes_corenet_t *srds2_regs;
#endif
int lane, bank, idx;
int have_bank[SRDS_MAX_BANK] = {};
#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES8
u32 serdes8_devdisr = 0;
u32 serdes8_devdisr2 = 0;
char srds_lpd_opt[16];
const char *srds_lpd_arg;
size_t arglen;
#endif
#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES_A001
int need_serdes_a001; /* true == need work-around for SERDES A001 */
#endif
#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES8
char buffer[HWCONFIG_BUFFER_SIZE];
char *buf = NULL;
/*
* Extract hwconfig from environment since we have not properly setup
* the environment but need it for ddr config params
*/
if (env_get_f("hwconfig", buffer, sizeof(buffer)) > 0)
buf = buffer;
#endif
if (serdes_prtcl_map & (1 << NONE))
return;
/* Is serdes enabled at all? */
if (!(in_be32(&gur->rcwsr[5]) & FSL_CORENET_RCWSR5_SRDS_EN))
return;
srds_regs = (void *)(CONFIG_SYS_FSL_CORENET_SERDES_ADDR);
cfg = (in_be32(&gur->rcwsr[4]) & FSL_CORENET_RCWSR4_SRDS_PRTCL) >> 26;
debug("Using SERDES configuration 0x%x, lane settings:\n", cfg);
if (!is_serdes_prtcl_valid(cfg)) {
printf("SERDES[PRTCL] = 0x%x is not valid\n", cfg);
return;
}
#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.
*/
for (bank = 1; bank < ARRAY_SIZE(srds_lpd_b); bank++) {
sprintf(srds_lpd_opt, "fsl_srds_lpd_b%u", bank + 1);
srds_lpd_arg =
hwconfig_subarg_f("serdes", srds_lpd_opt, &arglen, buf);
if (srds_lpd_arg)
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_by_lane(lane)] = 1;
serdes_prtcl_map |= (1 << lane_prtcl);
}
}
#ifdef CONFIG_ARCH_P5040
/*
* Lanes on bank 4 on P5040 are commented-out, but for some SERDES
* protocols, these lanes are routed to SATA. We use serdes_prtcl_map
* to decide whether a protocol is supported on a given lane, so SATA
* will be identified as not supported, and therefore not initialized.
* So for protocols which use SATA on bank4, we add SATA support in
* serdes_prtcl_map.
*/
switch (cfg) {
case 0x0:
case 0x1:
case 0x2:
case 0x3:
case 0x4:
case 0x5:
case 0x6:
case 0x7:
serdes_prtcl_map |= 1 << SATA1 | 1 << SATA2;
break;
default:
srds2_regs = (void *)CONFIG_SYS_FSL_CORENET_SERDES2_ADDR;
/* We don't need bank 4, so power it down */
setbits_be32(&srds2_regs->bank[0].rstctl, SRDS_RSTCTL_SDPD);
}
#endif
soc_serdes_init();
/* Set the first bit to indicate serdes has been initialized */
serdes_prtcl_map |= (1 << NONE);
#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES8
/*
* Bank two uses the clock from bank three, so if bank two is enabled,
* then bank three must also be enabled.
*/
if (have_bank[FSL_SRDS_BANK_2])
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
}
}
#ifdef CONFIG_SYS_FSL_ERRATUM_A004699
/*
* To avoid the situation that resulted in the P4080 erratum
* SERDES-8, a given SerDes bank will use the PLLs from the previous
* bank if one of the PLL frequencies is a multiple of the other. For
* instance, if bank 3 is running at 2.5GHz and bank 2 is at 1.25GHz,
* then bank 3 will use bank 2's PLL. P5040 Erratum A-004699 says
* that, in this situation, lane synchronization is not initiated. So
* when we detect a bank with a "borrowed" PLL, we have to manually
* initiate lane synchronization.
*/
for (bank = FSL_SRDS_BANK_2; bank <= FSL_SRDS_BANK_3; bank++) {
/* Determine the first lane for this bank */
unsigned int lane;
for (lane = 0; lane < SRDS_MAX_LANES; lane++)
if (lanes[lane].bank == bank)
break;
idx = lanes[lane].idx;
/*
* Check if the PLL for the bank is borrowed. The UOTHL
* bit of the first lane will tell us that.
*/
if (in_be32(&srds_regs->lane[idx].gcr0) & SRDS_GCR0_UOTHL) {
/* Manually start lane synchronization */
setbits_be32(&srds_regs->bank[bank].pllcr0,
SRDS_PLLCR0_PVCOCNT_EN);
}
}
#endif
#if defined(CONFIG_SYS_P4080_ERRATUM_SERDES8) || defined (CONFIG_SYS_P4080_ERRATUM_SERDES9)
for (lane = 0; lane < SRDS_MAX_LANES; lane++) {
enum srds_prtcl lane_prtcl;
idx = serdes_get_lane_idx(lane);
lane_prtcl = serdes_get_prtcl(cfg, lane);
#ifdef DEBUG
switch (lane) {
case 0:
puts("Bank1: ");
break;
case 10:
puts("\nBank2: ");
break;
case 14:
puts("\nBank3: ");
break;
default:
break;
}
printf("%s ", serdes_prtcl_str[lane_prtcl]);
#endif
#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES9
/*
* Set BnTTLCRy0[FLT_SEL] = 011011 and set BnTTLCRy0[31] = 1
* for each of the SerDes lanes selected as SGMII, XAUI, SRIO,
* or AURORA before the device is initialized.
*
* Note that this part of the SERDES-9 work-around is
* redundant if the work-around for A-4580 has already been
* applied via PBI.
*/
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 SGMII_FM2_DTSEC5:
case XAUI_FM1:
case XAUI_FM2:
case SRIO1:
case SRIO2:
case AURORA:
out_be32(&srds_regs->lane[idx].ttlcr0,
SRDS_TTLCR0_FLT_SEL_KFR_26 |
SRDS_TTLCR0_FLT_SEL_KPH_28 |
SRDS_TTLCR0_FLT_SEL_750PPM |
SRDS_TTLCR0_FREQOVD_EN);
break;
default:
break;
}
#endif
#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES8
switch (lane_prtcl) {
case PCIE1:
case PCIE2:
case PCIE3:
serdes8_devdisr |= FSL_CORENET_DEVDISR_PCIE1 >>
(lane_prtcl - PCIE1);
break;
case SRIO1:
case SRIO2:
serdes8_devdisr |= FSL_CORENET_DEVDISR_SRIO1 >>
(lane_prtcl - SRIO1);
break;
case SGMII_FM1_DTSEC1:
serdes8_devdisr2 |= FSL_CORENET_DEVDISR2_FM1 |
FSL_CORENET_DEVDISR2_DTSEC1_1;
break;
case SGMII_FM1_DTSEC2:
serdes8_devdisr2 |= FSL_CORENET_DEVDISR2_FM1 |
FSL_CORENET_DEVDISR2_DTSEC1_2;
break;
case SGMII_FM1_DTSEC3:
serdes8_devdisr2 |= FSL_CORENET_DEVDISR2_FM1 |
FSL_CORENET_DEVDISR2_DTSEC1_3;
break;
case SGMII_FM1_DTSEC4:
serdes8_devdisr2 |= FSL_CORENET_DEVDISR2_FM1 |
FSL_CORENET_DEVDISR2_DTSEC1_4;
break;
case SGMII_FM2_DTSEC1:
serdes8_devdisr2 |= FSL_CORENET_DEVDISR2_FM2 |
FSL_CORENET_DEVDISR2_DTSEC2_1;
break;
case SGMII_FM2_DTSEC2:
serdes8_devdisr2 |= FSL_CORENET_DEVDISR2_FM2 |
FSL_CORENET_DEVDISR2_DTSEC2_2;
break;
case SGMII_FM2_DTSEC3:
serdes8_devdisr2 |= FSL_CORENET_DEVDISR2_FM2 |
FSL_CORENET_DEVDISR2_DTSEC2_3;
break;
case SGMII_FM2_DTSEC4:
serdes8_devdisr2 |= FSL_CORENET_DEVDISR2_FM2 |
FSL_CORENET_DEVDISR2_DTSEC2_4;
break;
case SGMII_FM2_DTSEC5:
serdes8_devdisr2 |= FSL_CORENET_DEVDISR2_FM2 |
FSL_CORENET_DEVDISR2_DTSEC2_5;
break;
case XAUI_FM1:
serdes8_devdisr2 |= FSL_CORENET_DEVDISR2_FM1 |
FSL_CORENET_DEVDISR2_10GEC1;
break;
case XAUI_FM2:
serdes8_devdisr2 |= FSL_CORENET_DEVDISR2_FM2 |
FSL_CORENET_DEVDISR2_10GEC2;
break;
case AURORA:
break;
default:
break;
}
#endif
}
#endif
#ifdef DEBUG
puts("\n");
#endif
#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
/*
* Change bank init order to 0, 2, 1, so that the third bank's
* PLL is established before we start the second bank. The
* second bank uses the third bank's PLL.
*/
if (idx == 1)
bank = FSL_SRDS_BANK_3;
else if (idx == 2)
bank = FSL_SRDS_BANK_2;
#endif
/* Skip disabled banks */
if (!have_bank[bank])
continue;
#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES8
if (idx == 1) {
/*
* Re-enable devices on banks two and three that were
* disabled by the RCW, and then enable bank three. The
* devices need to be enabled before either bank is
* powered up.
*/
p4080_erratum_serdes8(srds_regs, gur, serdes8_devdisr,
serdes8_devdisr2, cfg);
} else if (idx == 2) {
/* Enable bank two now that bank three is enabled. */
enable_bank(gur, FSL_SRDS_BANK_2);
}
#endif
wait_for_rstdone(bank);
}
#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
}