powerpc/b4860: Add workaround for errata A006384 and A006475

SerDes PLLs may not lock reliably at 5 G VCO configuration(A006384)
and at cold temperatures(A006475), workaround recalibrate the
PLLs with some SerDes configuration

Both these errata are only applicable for b4 rev1.
So, make workaround for these errata conditional,
depending upon soc version.

Signed-off-by: Shaveta Leekha <shaveta@freescale.com>
Reviewed-by: York Sun <yorksun@freescale.com>
diff --git a/arch/powerpc/cpu/mpc85xx/cmd_errata.c b/arch/powerpc/cpu/mpc85xx/cmd_errata.c
index 7693899..d0a1c51 100644
--- a/arch/powerpc/cpu/mpc85xx/cmd_errata.c
+++ b/arch/powerpc/cpu/mpc85xx/cmd_errata.c
@@ -229,6 +229,14 @@
 	if (IS_SVR_REV(svr, 1, 0))
 		puts("Work-around for Erratum A005871 enabled\n");
 #endif
+#ifdef CONFIG_SYS_FSL_ERRATUM_A006475
+	if (SVR_MAJ(get_svr()) == 1)
+		puts("Work-around for Erratum A006475 enabled\n");
+#endif
+#ifdef CONFIG_SYS_FSL_ERRATUM_A006384
+	if (SVR_MAJ(get_svr()) == 1)
+		puts("Work-around for Erratum A006384 enabled\n");
+#endif
 #ifdef CONFIG_SYS_FSL_ERRATUM_A004849
 	/* This work-around is implemented in PBI, so just check for it */
 	check_erratum_a4849(svr);
diff --git a/arch/powerpc/include/asm/config_mpc85xx.h b/arch/powerpc/include/asm/config_mpc85xx.h
index 0ec1417..2f47b3f 100644
--- a/arch/powerpc/include/asm/config_mpc85xx.h
+++ b/arch/powerpc/include/asm/config_mpc85xx.h
@@ -662,6 +662,8 @@
 #define CONFIG_SYS_FSL_ERRATUM_A005871
 #define CONFIG_SYS_FSL_ERRATUM_A006379
 #define CONFIG_SYS_FSL_ERRATUM_A006593
+#define CONFIG_SYS_FSL_ERRATUM_A006475
+#define CONFIG_SYS_FSL_ERRATUM_A006384
 #define CONFIG_SYS_CCSRBAR_DEFAULT	0xfe000000
 
 #ifdef CONFIG_PPC_B4860
diff --git a/arch/powerpc/include/asm/immap_85xx.h b/arch/powerpc/include/asm/immap_85xx.h
index e0efc7e..edd7888 100644
--- a/arch/powerpc/include/asm/immap_85xx.h
+++ b/arch/powerpc/include/asm/immap_85xx.h
@@ -2495,6 +2495,7 @@
 #define SRDS_RSTCTL_SDEN	0x00000020
 #define SRDS_RSTCTL_SDRST_B	0x00000040
 #define SRDS_RSTCTL_PLLRST_B	0x00000080
+#define SRDS_RSTCTL_RSTERR_SHIFT  29
 		u32	pllcr0; /* PLL Control Register 0 */
 #define SRDS_PLLCR0_POFF		0x80000000
 #define SRDS_PLLCR0_RFCK_SEL_MASK	0x70000000
@@ -2504,6 +2505,7 @@
 #define SRDS_PLLCR0_RFCK_SEL_150	0x30000000
 #define SRDS_PLLCR0_RFCK_SEL_161_13	0x40000000
 #define SRDS_PLLCR0_RFCK_SEL_122_88	0x50000000
+#define SRDS_PLLCR0_DCBIAS_OUT_EN      0x02000000
 #define SRDS_PLLCR0_FRATE_SEL_MASK	0x000f0000
 #define SRDS_PLLCR0_FRATE_SEL_5		0x00000000
 #define SRDS_PLLCR0_FRATE_SEL_3_75	0x00050000
@@ -2511,9 +2513,22 @@
 #define SRDS_PLLCR0_FRATE_SEL_4		0x00070000
 #define SRDS_PLLCR0_FRATE_SEL_3_12	0x00090000
 #define SRDS_PLLCR0_FRATE_SEL_3		0x000a0000
+#define SRDS_PLLCR0_DCBIAS_OVRD		0x000000F0
+#define SRDS_PLLCR0_DCBIAS_OVRD_SHIFT	4
 		u32	pllcr1; /* PLL Control Register 1 */
-#define SRDS_PLLCR1_PLL_BWSEL	0x08000000
-		u32	res_0c;	/* 0x00c */
+#define SRDS_PLLCR1_BCAP_EN		0x20000000
+#define SRDS_PLLCR1_BCAP_OVD		0x10000000
+#define SRDS_PLLCR1_PLL_FCAP		0x001F8000
+#define SRDS_PLLCR1_PLL_FCAP_SHIFT	15
+#define SRDS_PLLCR1_PLL_BWSEL		0x08000000
+#define SRDS_PLLCR1_BYP_CAL		0x02000000
+		u32	pllsr2;	/* At 0x00c, PLL Status Register 2 */
+#define SRDS_PLLSR2_BCAP_EN		0x00800000
+#define SRDS_PLLSR2_BCAP_EN_SHIFT	23
+#define SRDS_PLLSR2_FCAP		0x003F0000
+#define SRDS_PLLSR2_FCAP_SHIFT		16
+#define SRDS_PLLSR2_DCBIAS		0x000F0000
+#define SRDS_PLLSR2_DCBIAS_SHIFT	16
 		u32	pllcr3;
 		u32	pllcr4;
 		u8	res_18[0x20-0x18];
diff --git a/board/freescale/b4860qds/b4860qds.c b/board/freescale/b4860qds/b4860qds.c
index 24a709e..d9c88a0 100644
--- a/board/freescale/b4860qds/b4860qds.c
+++ b/board/freescale/b4860qds/b4860qds.c
@@ -288,6 +288,182 @@
 	return 0;
 }
 
+static int calibrate_pll(serdes_corenet_t *srds_regs, int pll_num)
+{
+	u32 rst_err;
+
+	/* Steps For SerDes PLLs reset and reconfiguration
+	 * or PLL power-up procedure
+	 */
+	debug("CALIBRATE PLL:%d\n", pll_num);
+	clrbits_be32(&srds_regs->bank[pll_num].rstctl,
+			SRDS_RSTCTL_SDRST_B);
+	udelay(10);
+	clrbits_be32(&srds_regs->bank[pll_num].rstctl,
+		(SRDS_RSTCTL_SDEN | SRDS_RSTCTL_PLLRST_B));
+	udelay(10);
+	setbits_be32(&srds_regs->bank[pll_num].rstctl,
+			SRDS_RSTCTL_RST);
+	setbits_be32(&srds_regs->bank[pll_num].rstctl,
+		(SRDS_RSTCTL_SDEN | SRDS_RSTCTL_PLLRST_B
+		| SRDS_RSTCTL_SDRST_B));
+
+	udelay(20);
+
+	/* Check whether PLL has been locked or not */
+	rst_err = in_be32(&srds_regs->bank[pll_num].rstctl) &
+				SRDS_RSTCTL_RSTERR;
+	rst_err >>= SRDS_RSTCTL_RSTERR_SHIFT;
+	debug("RST_ERR value for PLL %d is: 0x%x:\n", pll_num, rst_err);
+	if (rst_err)
+		return rst_err;
+
+	return rst_err;
+}
+
+static int check_pll_locks(serdes_corenet_t *srds_regs, int pll_num)
+{
+	int ret = 0;
+	u32 fcap, dcbias, bcap, pllcr1, pllcr0;
+
+	if (calibrate_pll(srds_regs, pll_num)) {
+		/* STEP 1 */
+		/* Read fcap, dcbias and bcap value */
+		clrbits_be32(&srds_regs->bank[pll_num].pllcr0,
+				SRDS_PLLCR0_DCBIAS_OUT_EN);
+		fcap = in_be32(&srds_regs->bank[pll_num].pllsr2) &
+					SRDS_PLLSR2_FCAP;
+		fcap >>= SRDS_PLLSR2_FCAP_SHIFT;
+		bcap = in_be32(&srds_regs->bank[pll_num].pllsr2) &
+					SRDS_PLLSR2_BCAP_EN;
+		bcap >>= SRDS_PLLSR2_BCAP_EN_SHIFT;
+		setbits_be32(&srds_regs->bank[pll_num].pllcr0,
+				SRDS_PLLCR0_DCBIAS_OUT_EN);
+		dcbias = in_be32(&srds_regs->bank[pll_num].pllsr2) &
+					SRDS_PLLSR2_DCBIAS;
+		dcbias >>= SRDS_PLLSR2_DCBIAS_SHIFT;
+		debug("values of bcap:%x, fcap:%x and dcbias:%x\n",
+					bcap, fcap, dcbias);
+		if (fcap == 0 && bcap == 1) {
+			/* Step 3 */
+			clrbits_be32(&srds_regs->bank[pll_num].rstctl,
+				(SRDS_RSTCTL_SDEN | SRDS_RSTCTL_PLLRST_B
+				 | SRDS_RSTCTL_SDRST_B));
+			clrbits_be32(&srds_regs->bank[pll_num].pllcr1,
+					SRDS_PLLCR1_BCAP_EN);
+			setbits_be32(&srds_regs->bank[pll_num].pllcr1,
+					SRDS_PLLCR1_BCAP_OVD);
+			if (calibrate_pll(srds_regs, pll_num)) {
+				/*save the fcap, dcbias and bcap values*/
+				clrbits_be32(&srds_regs->bank[pll_num].pllcr0,
+						SRDS_PLLCR0_DCBIAS_OUT_EN);
+				fcap = in_be32(&srds_regs->bank[pll_num].pllsr2)
+					& SRDS_PLLSR2_FCAP;
+				fcap >>= SRDS_PLLSR2_FCAP_SHIFT;
+				bcap = in_be32(&srds_regs->bank[pll_num].pllsr2)
+					& SRDS_PLLSR2_BCAP_EN;
+				bcap >>= SRDS_PLLSR2_BCAP_EN_SHIFT;
+				setbits_be32(&srds_regs->bank[pll_num].pllcr0,
+						SRDS_PLLCR0_DCBIAS_OUT_EN);
+				dcbias = in_be32
+					(&srds_regs->bank[pll_num].pllsr2) &
+							SRDS_PLLSR2_DCBIAS;
+				dcbias >>= SRDS_PLLSR2_DCBIAS_SHIFT;
+
+				/* Step 4*/
+				clrbits_be32(&srds_regs->bank[pll_num].rstctl,
+				(SRDS_RSTCTL_SDEN | SRDS_RSTCTL_PLLRST_B
+				 | SRDS_RSTCTL_SDRST_B));
+				setbits_be32(&srds_regs->bank[pll_num].pllcr1,
+						SRDS_PLLCR1_BYP_CAL);
+				clrbits_be32(&srds_regs->bank[pll_num].pllcr1,
+						SRDS_PLLCR1_BCAP_EN);
+				setbits_be32(&srds_regs->bank[pll_num].pllcr1,
+						SRDS_PLLCR1_BCAP_OVD);
+				/* change the fcap and dcbias to the saved
+				 * values from Step 3 */
+				clrbits_be32(&srds_regs->bank[pll_num].pllcr1,
+							SRDS_PLLCR1_PLL_FCAP);
+				pllcr1 = (in_be32
+					(&srds_regs->bank[pll_num].pllcr1)|
+					(fcap << SRDS_PLLCR1_PLL_FCAP_SHIFT));
+				out_be32(&srds_regs->bank[pll_num].pllcr1,
+							pllcr1);
+				clrbits_be32(&srds_regs->bank[pll_num].pllcr0,
+						SRDS_PLLCR0_DCBIAS_OVRD);
+				pllcr0 = (in_be32
+				(&srds_regs->bank[pll_num].pllcr0)|
+				(dcbias << SRDS_PLLCR0_DCBIAS_OVRD_SHIFT));
+				out_be32(&srds_regs->bank[pll_num].pllcr0,
+							pllcr0);
+				ret = calibrate_pll(srds_regs, pll_num);
+				if (ret)
+					return ret;
+			} else {
+				goto out;
+			}
+		} else { /* Step 5 */
+			clrbits_be32(&srds_regs->bank[pll_num].rstctl,
+				(SRDS_RSTCTL_SDEN | SRDS_RSTCTL_PLLRST_B
+				 | SRDS_RSTCTL_SDRST_B));
+			udelay(10);
+			/* Change the fcap, dcbias, and bcap to the
+			 * values from Step 1 */
+			setbits_be32(&srds_regs->bank[pll_num].pllcr1,
+					SRDS_PLLCR1_BYP_CAL);
+			clrbits_be32(&srds_regs->bank[pll_num].pllcr1,
+						SRDS_PLLCR1_PLL_FCAP);
+			pllcr1 = (in_be32(&srds_regs->bank[pll_num].pllcr1)|
+				(fcap << SRDS_PLLCR1_PLL_FCAP_SHIFT));
+			out_be32(&srds_regs->bank[pll_num].pllcr1,
+						pllcr1);
+			clrbits_be32(&srds_regs->bank[pll_num].pllcr0,
+						SRDS_PLLCR0_DCBIAS_OVRD);
+			pllcr0 = (in_be32(&srds_regs->bank[pll_num].pllcr0)|
+				(dcbias << SRDS_PLLCR0_DCBIAS_OVRD_SHIFT));
+			out_be32(&srds_regs->bank[pll_num].pllcr0,
+						pllcr0);
+			clrbits_be32(&srds_regs->bank[pll_num].pllcr1,
+					SRDS_PLLCR1_BCAP_EN);
+			setbits_be32(&srds_regs->bank[pll_num].pllcr1,
+					SRDS_PLLCR1_BCAP_OVD);
+			ret = calibrate_pll(srds_regs, pll_num);
+			if (ret)
+				return ret;
+		}
+	}
+out:
+	return 0;
+}
+
+static int check_serdes_pll_locks(void)
+{
+	serdes_corenet_t *srds1_regs =
+		(void *)CONFIG_SYS_FSL_CORENET_SERDES_ADDR;
+	serdes_corenet_t *srds2_regs =
+		(void *)CONFIG_SYS_FSL_CORENET_SERDES2_ADDR;
+	int i, ret1, ret2;
+
+	debug("\nSerDes1 Lock check\n");
+	for (i = 0; i < CONFIG_SYS_FSL_SRDS_NUM_PLLS; i++) {
+		ret1 = check_pll_locks(srds1_regs, i);
+		if (ret1) {
+			printf("SerDes1, PLL:%d didnt lock\n", i);
+			return ret1;
+		}
+	}
+	debug("\nSerDes2 Lock check\n");
+	for (i = 0; i < CONFIG_SYS_FSL_SRDS_NUM_PLLS; i++) {
+		ret2 = check_pll_locks(srds2_regs, i);
+		if (ret2) {
+			printf("SerDes2, PLL:%d didnt lock\n", i);
+			return ret2;
+		}
+	}
+
+	return 0;
+}
+
 int config_serdes1_refclks(void)
 {
 	ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
@@ -478,6 +654,8 @@
 			setbits_be32(&srds2_regs->bank[i].rstctl,
 				(SRDS_RSTCTL_SDEN | SRDS_RSTCTL_PLLRST_B
 				| SRDS_RSTCTL_SDRST_B));
+
+			udelay(10);
 		}
 		break;
 	default:
@@ -544,6 +722,21 @@
 	else
 		printf("SerDes2 Refclk reconfiguring failed.\n");
 
+#if defined(CONFIG_SYS_FSL_ERRATUM_A006384) || \
+			defined(CONFIG_SYS_FSL_ERRATUM_A006475)
+	/* Rechecking the SerDes locks after all SerDes configurations
+	 * are done, As SerDes PLLs may not lock reliably at 5 G VCO
+	 * and at cold temperatures.
+	 * Following sequence ensure the proper locking of SerDes PLLs.
+	 */
+	if (SVR_MAJ(get_svr()) == 1) {
+		if (check_serdes_pll_locks())
+			printf("SerDes plls still not locked properly.\n");
+		else
+			printf("SerDes plls have been locked well.\n");
+	}
+#endif
+
 	/* Configure VSC3316 and VSC3308 crossbar switches */
 	if (configure_vsc3316_3308())
 		printf("VSC:failed to configure VSC3316/3308.\n");