diff options
| -rw-r--r-- | arch/powerpc/cpu/mpc85xx/cmd_errata.c | 4 | ||||
| -rw-r--r-- | arch/powerpc/cpu/mpc85xx/fsl_corenet_serdes.c | 283 | ||||
| -rw-r--r-- | arch/powerpc/cpu/mpc85xx/fsl_corenet_serdes.h | 4 | ||||
| -rw-r--r-- | arch/powerpc/cpu/mpc85xx/p4080_serdes.c | 4 | 
4 files changed, 295 insertions, 0 deletions
| diff --git a/arch/powerpc/cpu/mpc85xx/cmd_errata.c b/arch/powerpc/cpu/mpc85xx/cmd_errata.c index d7835c8d6..01c462c4b 100644 --- a/arch/powerpc/cpu/mpc85xx/cmd_errata.c +++ b/arch/powerpc/cpu/mpc85xx/cmd_errata.c @@ -41,6 +41,10 @@ static int do_errata(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])  	}  #endif +#if defined(CONFIG_SYS_P4080_ERRATUM_SERDES8) +	puts("Work-around for Erratum SERDES8 enabled\n"); +#endif +  	return 0;  } diff --git a/arch/powerpc/cpu/mpc85xx/fsl_corenet_serdes.c b/arch/powerpc/cpu/mpc85xx/fsl_corenet_serdes.c index 1c03061f7..5bcf91abf 100644 --- a/arch/powerpc/cpu/mpc85xx/fsl_corenet_serdes.c +++ b/arch/powerpc/cpu/mpc85xx/fsl_corenet_serdes.c @@ -21,10 +21,14 @@   */  #include <common.h> +#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES8 +#include <hwconfig.h> +#endif  #include <asm/fsl_serdes.h>  #include <asm/immap_85xx.h>  #include <asm/io.h>  #include <asm/processor.h> +#include <asm/fsl_law.h>  #include "fsl_corenet_serdes.h"  static u32 serdes_prtcl_map; @@ -102,6 +106,13 @@ int serdes_lane_enabled(int lane)  	if (in_be32(®s->bank[bank].rstctl) & SRDS_RSTCTL_SDPD)  		return 0; +#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES8 +	if (!IS_SVR_REV(get_svr(), 1, 0)) +		if (bank > 0) +			return !(srds_lpd_b[bank] & +					(8 >> (lane - (6 + 4 * bank)))); +#endif +  	return !(in_be32(&gur->rcwsr[word]) & (0x80000000 >> bit));  } @@ -116,6 +127,140 @@ int is_serdes_configured(enum srds_prtcl device)  	return (1 << device) & serdes_prtcl_map;  } +#ifndef CONFIG_SYS_DCSRBAR_PHYS +#define CONFIG_SYS_DCSRBAR_PHYS	0x80000000 /* Must be 1GB-aligned for rev1.0 */ +#define CONFIG_SYS_DCSRBAR	0x80000000 +#define __DCSR_NOT_DEFINED_BY_CONFIG +#endif + +#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES8 +static void enable_bank(ccsr_gur_t *gur, int bank) +{ +	u32 rcw5; + +	/* +	 * Enable the lanes SRDS_LPD_Bn.  The RCW bits are read-only in +	 * CCSR, and read/write in DSCR. +	 */ +	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; +	} else if (bank == FSL_SRDS_BANK_3) { +		rcw5 &= ~FSL_CORENET_RCWSRn_SRDS_LPD_B3; +		rcw5 |= srds_lpd_b[bank] << 18; +	} else { +		printf("SERDES: enable_bank: bad bank %d\n", bank + 1); +		return; +	} + +	/* See similar code in cpu/mpc85xx/cpu_init.c for an explanation +	 * of the DCSR mapping. +	 */ +	{ +#ifdef __DCSR_NOT_DEFINED_BY_CONFIG +		struct law_entry law = find_law(CONFIG_SYS_DCSRBAR_PHYS); +		int law_index; +		if (law.index == -1) +			law_index = set_next_law(CONFIG_SYS_DCSRBAR_PHYS, +						 LAW_SIZE_1M, LAW_TRGT_IF_DCSR); +		else +			set_law(law.index, CONFIG_SYS_DCSRBAR_PHYS, LAW_SIZE_1M, +				LAW_TRGT_IF_DCSR); +#endif +		u32 *p = (void *)CONFIG_SYS_DCSRBAR + 0x20114; +		out_be32(p, rcw5); +#ifdef __DCSR_NOT_DEFINED_BY_CONFIG +		if (law.index == -1) +			disable_law(law_index); +		else +			set_law(law.index, law.addr, law.size, law.trgt_id); +#endif +	} +} + +/* + * To avoid problems with clock jitter, rev 2 p4080 uses the pll from + * bank 3 to clock banks 2 and 3, as well as a limited selection of + * protocol configurations.  This requires that banks 2 and 3's lanes be + * disabled in the RCW, and enabled with some fixup here to re-enable + * them, and to configure bank 2's clock parameters in bank 3's pll in + * cases where they differ. + */ +static void p4080_erratum_serdes8(serdes_corenet_t *regs, ccsr_gur_t *gur, +				  u32 devdisr, u32 devdisr2, int cfg) +{ +	int srds_ratio_b2; +	int rfck_sel; + +	/* +	 * The disabled lanes of bank 2 will cause the associated +	 * logic blocks to be disabled in DEVDISR.  We reverse that here. +	 * +	 * Note that normally it is not permitted to clear DEVDISR bits +	 * once the device has been disabled, but the hardware people +	 * say that this special case is OK. +	 */ +	clrbits_be32(&gur->devdisr, devdisr); +	clrbits_be32(&gur->devdisr2, devdisr2); + +	/* +	 * Some protocols require special handling.  There are a few +	 * additional protocol configurations that can be used, which are +	 * not listed here.  See app note 4065 for supported protocol +	 * configurations. +	 */ +	switch (cfg) { +	case 0x19: +		/* +		 * Bank 2 has PCIe which wants BWSEL -- tell bank 3's PLL. +		 * SGMII on bank 3 should still be usable. +		 */ +		setbits_be32(®s->bank[FSL_SRDS_BANK_3].pllcr1, +			     SRDS_PLLCR1_PLL_BWSEL); + +		enable_bank(gur, FSL_SRDS_BANK_3); +		break; + +	case 0x0f: +	case 0x10: +		/* +		 * Banks 2 (XAUI) and 3 (SGMII) have different clocking +		 * requirements in these configurations.  Bank 3 cannot +		 * be used and should have its lanes (but not the bank +		 * itself) disabled in the RCW.  We set up bank 3's pll +		 * for bank 2's needs here. +		 */ +		srds_ratio_b2 = (in_be32(&gur->rcwsr[4]) >> 13) & 7; + +		/* Determine refclock from XAUI ratio */ +		switch (srds_ratio_b2) { +		case 1: /* 20:1 */ +			rfck_sel = SRDS_PLLCR0_RFCK_SEL_156_25; +			break; +		case 2: /* 25:1 */ +			rfck_sel = SRDS_PLLCR0_RFCK_SEL_125; +			break; +		default: +			printf("SERDES: bad SRDS_RATIO_B2 %d\n", +			       srds_ratio_b2); +			return; +		} + +		clrsetbits_be32(®s->bank[FSL_SRDS_BANK_3].pllcr0, +				SRDS_PLLCR0_RFCK_SEL_MASK, rfck_sel); + +		clrsetbits_be32(®s->bank[FSL_SRDS_BANK_3].pllcr0, +				SRDS_PLLCR0_FRATE_SEL_MASK, +				SRDS_PLLCR0_FRATE_SEL_6_25); +		break; +	default: +		enable_bank(gur, FSL_SRDS_BANK_3); +	} + +} +#endif +  void fsl_serdes_init(void)  {  	ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR); @@ -125,6 +270,13 @@ void fsl_serdes_init(void)  	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; +	u32 serdes8_devdisr2 = 0; +	char srds_lpd_opt[16]; +	const char *srds_lpd_arg; +	size_t arglen; +#endif  	/* Is serdes enabled at all? */  	if (!(in_be32(&gur->rcwsr[5]) & FSL_CORENET_RCWSR5_SRDS_EN)) @@ -139,6 +291,18 @@ void fsl_serdes_init(void)  		return;  	} +#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES8 +	if (!IS_SVR_REV(get_svr(), 1, 0)) +		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("serdes", srds_lpd_opt, +						       &arglen); +			if (srds_lpd_arg) +				srds_lpd_b[bank] = simple_strtoul(srds_lpd_arg, +								  NULL, 0); +		} +#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); @@ -148,6 +312,35 @@ void fsl_serdes_init(void)  		}  	} +#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES8 +	if (IS_SVR_REV(get_svr(), 1, 0)) { +		/* At least one bank must be disabled due to SERDES8.  If +		 * no bank is found to be disabled based on lane +		 * disables, disable bank 3 because we can't turn off its +		 * lanes in the RCW without disabling MDIO due to erratum +		 * GEN8. +		 * +		 * This means that if you are relying on bank 3 being +		 * disabled to avoid SERDES8, in some cases you cannot +		 * also disable all lanes of another bank, or else bank +		 * 3 won't be disabled, leaving you with a configuration +		 * that isn't valid according to SERDES8 (e.g. if banks +		 * 2 and 3 have the same clock, and bank 1 is disabled +		 * instead of 3). +		 */ +		for (bank = 0; bank < SRDS_MAX_BANK; bank++) { +			if (!have_bank[bank]) +				break; +		} + +		if (bank == SRDS_MAX_BANK) +			have_bank[FSL_SRDS_BANK_3] = 0; +	} else { +		if (have_bank[FSL_SRDS_BANK_2]) +			have_bank[FSL_SRDS_BANK_3] = 1; +	} +#endif +  	for (bank = 0; bank < SRDS_MAX_BANK; bank++) {  		if (!have_bank[bank]) {  			printf("SERDES: bank %d disabled\n", bank + 1); @@ -177,6 +370,68 @@ void fsl_serdes_init(void)  		printf("%s ", serdes_prtcl_str[lane_prtcl]);  #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 XAUI_FM1: +		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; +			break; +		case AURORA: +			break; +		default: +			break; +		} + +#endif  	}  #ifdef DEBUG @@ -188,10 +443,38 @@ void fsl_serdes_init(void)  		bank = idx; +#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES8 +		if (!IS_SVR_REV(get_svr(), 1, 0)) { +			/* +			 * Change bank init order to 0, 2, 1, so that the +			 * third bank's PLL is established before we +			 * start the second bank which shares 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 (!IS_SVR_REV(get_svr(), 1, 0)) { +			if (idx == 1) { +				p4080_erratum_serdes8(srds_regs, gur, +						      serdes8_devdisr, +						      serdes8_devdisr2, cfg); +			} else if (idx == 2) { +				enable_bank(gur, FSL_SRDS_BANK_2); +			} +		} +#endif +  		/* reset banks for errata */  		setbits_be32(&srds_regs->bank[bank].rstctl, SRDS_RSTCTL_RST); diff --git a/arch/powerpc/cpu/mpc85xx/fsl_corenet_serdes.h b/arch/powerpc/cpu/mpc85xx/fsl_corenet_serdes.h index 4e1f33144..42d771e09 100644 --- a/arch/powerpc/cpu/mpc85xx/fsl_corenet_serdes.h +++ b/arch/powerpc/cpu/mpc85xx/fsl_corenet_serdes.h @@ -37,4 +37,8 @@ int serdes_get_bank(int lane);  int serdes_lane_enabled(int lane);  enum srds_prtcl serdes_get_prtcl(int cfg, int lane); +#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES8 +extern uint16_t srds_lpd_b[SRDS_MAX_BANK]; +#endif +  #endif /* __FSL_CORENET_SERDES_H */ diff --git a/arch/powerpc/cpu/mpc85xx/p4080_serdes.c b/arch/powerpc/cpu/mpc85xx/p4080_serdes.c index eb6223cc9..87bd79529 100644 --- a/arch/powerpc/cpu/mpc85xx/p4080_serdes.c +++ b/arch/powerpc/cpu/mpc85xx/p4080_serdes.c @@ -71,6 +71,10 @@ static u8 serdes_cfg_tbl[][SRDS_MAX_LANES] = {  		XAUI_FM1, XAUI_FM1, XAUI_FM1, XAUI_FM1},  }; +#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES8 +uint16_t srds_lpd_b[SRDS_MAX_BANK]; +#endif +  enum srds_prtcl serdes_get_prtcl(int cfg, int lane)  {  	if (!serdes_lane_enabled(lane)) |