void fsl_serdes_init()

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
}