powerpc/serdes: Add the workaround for erratum A-007186

SerDes PLL is calibrated at reset. When the junction temperature
delta from the time the PLL is calibrated exceeds +56C/-66C,
jitter may increase and can cause PLL to unlock.

This workaround overwrite the SerDes registers with new values,
to calibrate SerDes registers.
These values are known to work fine for all temperature ranges.

This workaround is valid for B4, T4 and T2 platforms, so
added in their config.

Signed-off-by: Shaveta Leekha <shaveta@freescale.com>
Signed-off-by: Poonam Aggrwal <Poonam.Aggrwal@freescale.com>
[York Sun: replaced typedef ccsr_sfp_regs_t with struct ccsr_sfp_regs]
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 f69c834..3a04a89 100644
--- a/arch/powerpc/cpu/mpc85xx/cmd_errata.c
+++ b/arch/powerpc/cpu/mpc85xx/cmd_errata.c
@@ -269,6 +269,9 @@
 #ifdef CONFIG_SYS_FSL_ERRATUM_USB14
 	puts("Work-around for Erratum USB14 enabled\n");
 #endif
+#ifdef CONFIG_SYS_FSL_ERRATUM_A007186
+	puts("Work-around for Erratum A007186 enabled\n");
+#endif
 #ifdef CONFIG_SYS_FSL_ERRATUM_A006593
 	puts("Work-around for Erratum A006593 enabled\n");
 #endif
diff --git a/arch/powerpc/cpu/mpc85xx/fsl_corenet2_serdes.c b/arch/powerpc/cpu/mpc85xx/fsl_corenet2_serdes.c
index 70e09ea..d1fc76a 100644
--- a/arch/powerpc/cpu/mpc85xx/fsl_corenet2_serdes.c
+++ b/arch/powerpc/cpu/mpc85xx/fsl_corenet2_serdes.c
@@ -147,12 +147,43 @@
 	return -ENODEV;
 }
 
+#define BC3_SHIFT	9
+#define DC3_SHIFT	6
+#define FC3_SHIFT	0
+#define BC2_SHIFT	19
+#define DC2_SHIFT	16
+#define FC2_SHIFT	10
+#define BC1_SHIFT	29
+#define DC1_SHIFT	26
+#define FC1_SHIFT	20
+#define BC_MASK		0x1
+#define DC_MASK		0x7
+#define FC_MASK		0x3F
+
+#define FUSE_VAL_MASK		0x00000003
+#define FUSE_VAL_SHIFT		30
+#define CR0_DCBIAS_SHIFT	5
+#define CR1_FCAP_SHIFT		15
+#define CR1_BCAP_SHIFT		29
+#define FCAP_MASK		0x001F8000
+#define BCAP_MASK		0x20000000
+#define BCAP_OVD_MASK		0x10000000
+#define BYP_CAL_MASK		0x02000000
+
 u64 serdes_init(u32 sd, u32 sd_addr, u32 sd_prctl_mask, u32 sd_prctl_shift)
 {
 	ccsr_gur_t *gur = (void __iomem *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
 	u64 serdes_prtcl_map = 0;
 	u32 cfg;
 	int lane;
+#ifdef CONFIG_SYS_FSL_ERRATUM_A007186
+	struct ccsr_sfp_regs  __iomem *sfp_regs =
+			(struct ccsr_sfp_regs __iomem *)(CONFIG_SYS_SFP_ADDR);
+	u32 pll_num, pll_status, bc, dc, fc, pll_cr_upd, pll_cr0, pll_cr1;
+	u32 bc_status, fc_status, dc_status, pll_sr2;
+	serdes_corenet_t  __iomem *srds_regs = (void *)sd_addr;
+	u32 sfp_spfr0, sel;
+#endif
 
 	cfg = in_be32(&gur->rcwsr[4]) & sd_prctl_mask;
 	/* Is serdes enabled at all? */
@@ -160,6 +191,123 @@
 		printf("SERDES%d is not enabled\n", sd + 1);
 		return 0;
 	}
+
+/* Erratum A-007186
+ * Freescale Scratch Pad Fuse Register n (SFP_FSPFR0)
+ * The workaround requires factory pre-set SerDes calibration values to be
+ * read from a fuse block(Freescale Scratch Pad Fuse Register SFP_FSPFR0)
+ * These values have been shown to work across the
+ * entire temperature range for all SerDes. These values are then written into
+ * the SerDes registers to calibrate the SerDes PLL.
+ *
+ * This workaround for the protocols and rates that only have the Ring VCO.
+ */
+#ifdef CONFIG_SYS_FSL_ERRATUM_A007186
+	sfp_spfr0 = in_be32(&sfp_regs->fsl_spfr0);
+	debug("A007186: sfp_spfr0= %x\n", sfp_spfr0);
+
+	sel = (sfp_spfr0 >> FUSE_VAL_SHIFT) & FUSE_VAL_MASK;
+
+	if (sel == 0x01 || sel == 0x02) {
+		for (pll_num = 0; pll_num < SRDS_MAX_BANK; pll_num++) {
+			pll_status = in_be32(&srds_regs->bank[pll_num].pllcr0);
+			debug("A007186: pll_num=%x pllcr0=%x\n",
+			      pll_num, pll_status);
+			/* STEP 1 */
+			/* Read factory pre-set SerDes calibration values
+			 * from fuse block(SFP scratch register-sfp_spfr0)
+			 */
+			switch (pll_status & SRDS_PLLCR0_FRATE_SEL_MASK) {
+			case SRDS_PLLCR0_FRATE_SEL_3_0:
+			case SRDS_PLLCR0_FRATE_SEL_3_072:
+				debug("A007186: 3.0/3.072 protocol rate\n");
+				bc = (sfp_spfr0 >> BC1_SHIFT) & BC_MASK;
+				dc = (sfp_spfr0 >> DC1_SHIFT) & DC_MASK;
+				fc = (sfp_spfr0 >> FC1_SHIFT) & FC_MASK;
+				break;
+			case SRDS_PLLCR0_FRATE_SEL_3_125:
+				debug("A007186: 3.125 protocol rate\n");
+				bc = (sfp_spfr0 >> BC2_SHIFT) & BC_MASK;
+				dc = (sfp_spfr0 >> DC2_SHIFT) & DC_MASK;
+				fc = (sfp_spfr0 >> FC2_SHIFT) & FC_MASK;
+				break;
+			case SRDS_PLLCR0_FRATE_SEL_3_75:
+				debug("A007186: 3.75 protocol rate\n");
+				bc = (sfp_spfr0 >> BC1_SHIFT) & BC_MASK;
+				dc = (sfp_spfr0 >> DC1_SHIFT) & DC_MASK;
+				fc = (sfp_spfr0 >> FC1_SHIFT) & FC_MASK;
+				break;
+			default:
+				continue;
+			}
+
+			/* STEP 2 */
+			/* Write SRDSxPLLnCR1[11:16] = FC
+			 * Write SRDSxPLLnCR1[2] = BC
+			 */
+			pll_cr1 = in_be32(&srds_regs->bank[pll_num].pllcr1);
+			pll_cr_upd = (((bc << CR1_BCAP_SHIFT) & BCAP_MASK) |
+				      ((fc << CR1_FCAP_SHIFT) & FCAP_MASK));
+			out_be32(&srds_regs->bank[pll_num].pllcr1,
+				 (pll_cr_upd | pll_cr1));
+			debug("A007186: pll_num=%x Updated PLLCR1=%x\n",
+			      pll_num, (pll_cr_upd | pll_cr1));
+			/* Write SRDSxPLLnCR0[24:26] = DC
+			 */
+			pll_cr0 = in_be32(&srds_regs->bank[pll_num].pllcr0);
+			out_be32(&srds_regs->bank[pll_num].pllcr0,
+				 pll_cr0 | (dc << CR0_DCBIAS_SHIFT));
+			debug("A007186: pll_num=%x, Updated PLLCR0=%x\n",
+			      pll_num, (pll_cr0 | (dc << CR0_DCBIAS_SHIFT)));
+			/* Write SRDSxPLLnCR1[3] = 1
+			 * Write SRDSxPLLnCR1[6] = 1
+			 */
+			pll_cr1 = in_be32(&srds_regs->bank[pll_num].pllcr1);
+			pll_cr_upd = (BCAP_OVD_MASK | BYP_CAL_MASK);
+			out_be32(&srds_regs->bank[pll_num].pllcr1,
+				 (pll_cr_upd | pll_cr1));
+			debug("A007186: pll_num=%x Updated PLLCR1=%x\n",
+			      pll_num, (pll_cr_upd | pll_cr1));
+
+			/* STEP 3 */
+			/* Read the status Registers */
+			/* Verify SRDSxPLLnSR2[8] = BC */
+			pll_sr2 = in_be32(&srds_regs->bank[pll_num].pllsr2);
+			debug("A007186: pll_num=%x pllsr2=%x\n",
+			      pll_num, pll_sr2);
+			bc_status = (pll_sr2 >> 23) & BC_MASK;
+			if (bc_status != bc)
+				debug("BC mismatch\n");
+			fc_status = (pll_sr2 >> 16) & FC_MASK;
+			if (fc_status != fc)
+				debug("FC mismatch\n");
+			pll_cr0 = in_be32(&srds_regs->bank[pll_num].pllcr0);
+			out_be32(&srds_regs->bank[pll_num].pllcr0, pll_cr0 |
+								0x02000000);
+			pll_sr2 = in_be32(&srds_regs->bank[pll_num].pllsr2);
+			dc_status = (pll_sr2 >> 17) & DC_MASK;
+			if (dc_status != dc)
+				debug("DC mismatch\n");
+			pll_cr0 = in_be32(&srds_regs->bank[pll_num].pllcr0);
+			out_be32(&srds_regs->bank[pll_num].pllcr0, pll_cr0 &
+								0xfdffffff);
+
+			/* STEP 4 */
+			/* Wait 750us to verify the PLL is locked
+			 * by checking SRDSxPLLnCR0[8] = 1.
+			 */
+			udelay(750);
+			pll_status = in_be32(&srds_regs->bank[pll_num].pllcr0);
+			debug("A007186: pll_num=%x pllcr0=%x\n",
+			      pll_num, pll_status);
+
+			if ((pll_status & SRDS_PLLCR0_PLL_LCK) == 0)
+				printf("A007186 Serdes PLL not locked\n");
+			else
+				debug("A007186 Serdes PLL locked\n");
+		}
+	}
+#endif
 
 	cfg >>= sd_prctl_shift;
 	printf("Using SERDES%d Protocol: %d (0x%x)\n", sd + 1, cfg, cfg);
diff --git a/arch/powerpc/include/asm/config_mpc85xx.h b/arch/powerpc/include/asm/config_mpc85xx.h
index c9fd2a5..712f2ef 100644
--- a/arch/powerpc/include/asm/config_mpc85xx.h
+++ b/arch/powerpc/include/asm/config_mpc85xx.h
@@ -675,8 +675,10 @@
 #define CONFIG_SYS_FSL_ERRATUM_A005871
 #define CONFIG_SYS_FSL_ERRATUM_A006261
 #define CONFIG_SYS_FSL_ERRATUM_A006379
+#define CONFIG_SYS_FSL_ERRATUM_A007186
 #define CONFIG_SYS_FSL_ERRATUM_A006593
 #define CONFIG_SYS_CCSRBAR_DEFAULT	0xfe000000
+#define CONFIG_SYS_FSL_SFP_VER_3_0
 #define CONFIG_SYS_FSL_PCI_VER_3_X
 
 #elif defined(CONFIG_PPC_B4860) || defined(CONFIG_PPC_B4420)
@@ -702,12 +704,14 @@
 #define CONFIG_SYS_FSL_ERRATUM_A_004934
 #define CONFIG_SYS_FSL_ERRATUM_A005871
 #define CONFIG_SYS_FSL_ERRATUM_A006379
+#define CONFIG_SYS_FSL_ERRATUM_A007186
 #define CONFIG_SYS_FSL_ERRATUM_A006593
 #define CONFIG_SYS_FSL_ERRATUM_A007075
 #define CONFIG_SYS_FSL_ERRATUM_A006475
 #define CONFIG_SYS_FSL_ERRATUM_A006384
 #define CONFIG_SYS_FSL_ERRATUM_A007212
 #define CONFIG_SYS_CCSRBAR_DEFAULT	0xfe000000
+#define CONFIG_SYS_FSL_SFP_VER_3_0
 
 #ifdef CONFIG_PPC_B4860
 #define CONFIG_SYS_FSL_CORES_PER_CLUSTER 4
@@ -827,8 +831,10 @@
 #define CONFIG_SYS_FSL_ERRATUM_ESDHC111
 #define CONFIG_SYS_FSL_ERRATUM_A006261
 #define CONFIG_SYS_FSL_ERRATUM_A006593
+#define CONFIG_SYS_FSL_ERRATUM_A007186
 #define CONFIG_SYS_FSL_ERRATUM_A006379
 #define ESDHCI_QUIRK_BROKEN_TIMEOUT_VALUE
+#define CONFIG_SYS_FSL_SFP_VER_3_0
 
 
 #elif defined(CONFIG_PPC_C29X)
diff --git a/arch/powerpc/include/asm/immap_85xx.h b/arch/powerpc/include/asm/immap_85xx.h
index eff573b..fe1dcc2 100644
--- a/arch/powerpc/include/asm/immap_85xx.h
+++ b/arch/powerpc/include/asm/immap_85xx.h
@@ -2521,14 +2521,17 @@
 #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_PLL_LCK		0x00800000
 #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_4_9152	0x00030000
 #define SRDS_PLLCR0_FRATE_SEL_3_75	0x00050000
 #define SRDS_PLLCR0_FRATE_SEL_5_15	0x00060000
 #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_FRATE_SEL_3_125	0x00090000
+#define SRDS_PLLCR0_FRATE_SEL_3_0	0x000a0000
+#define SRDS_PLLCR0_FRATE_SEL_3_072	0x000c0000
 #define SRDS_PLLCR0_DCBIAS_OVRD		0x000000F0
 #define SRDS_PLLCR0_DCBIAS_OVRD_SHIFT	4
 		u32	pllcr1; /* PLL Control Register 1 */
@@ -2863,6 +2866,21 @@
 	u8	res_f4[0xf0c];
 };
 #endif
+#ifdef CONFIG_SYS_FSL_SFP_VER_3_0
+struct ccsr_sfp_regs {
+	u32 ospr;		/* 0x200 */
+	u32 reserved0[14];
+	u32 srk_hash[8];	/* 0x23c Super Root Key Hash */
+	u32 oem_uid;		/* 0x9c OEM Unique ID */
+	u8 reserved2[0x04];
+	u32 ovpr;			/* 0xA4  Intent To Secure */
+	u8 reserved4[0x08];
+	u32 fsl_uid;		/* 0xB0  FSL Unique ID */
+	u8 reserved5[0x04];
+	u32 fsl_spfr0;		/* Scratch Pad Fuse Register 0 */
+	u32 fsl_spfr1;		/* Scratch Pad Fuse Register 1 */
+};
+#endif
 
 #ifdef CONFIG_FSL_CORENET
 #define CONFIG_SYS_FSL_CORENET_CCM_OFFSET	0x0000
@@ -2876,6 +2894,14 @@
 #define CONFIG_SYS_MPC8xxx_DDR3_OFFSET		0xA000
 #define CONFIG_SYS_FSL_CORENET_CLK_OFFSET	0xE1000
 #define CONFIG_SYS_FSL_CORENET_RCPM_OFFSET	0xE2000
+#ifdef CONFIG_SYS_FSL_SFP_VER_3_0
+/* In SFPv3, OSPR register is now at offset 0x200.
+ *  * So directly mapping sfp register map to this address */
+#define CONFIG_SYS_OSPR_OFFSET                  0x200
+#define CONFIG_SYS_SFP_OFFSET            (0xE8000 + CONFIG_SYS_OSPR_OFFSET)
+#else
+#define CONFIG_SYS_SFP_OFFSET                   0xE8000
+#endif
 #define CONFIG_SYS_FSL_CORENET_SERDES_OFFSET	0xEA000
 #define CONFIG_SYS_FSL_CORENET_SERDES2_OFFSET	0xEB000
 #define CONFIG_SYS_FSL_CPC_OFFSET		0x10000
@@ -3094,6 +3120,9 @@
 #define CONFIG_SYS_PCIE4_ADDR \
 	(CONFIG_SYS_IMMR + CONFIG_SYS_MPC85xx_PCIE4_OFFSET)
 
+#define CONFIG_SYS_SFP_ADDR  \
+	(CONFIG_SYS_IMMR + CONFIG_SYS_SFP_OFFSET)
+
 #define TSEC_BASE_ADDR		(CONFIG_SYS_IMMR + CONFIG_SYS_TSEC1_OFFSET)
 #define MDIO_BASE_ADDR		(CONFIG_SYS_IMMR + CONFIG_SYS_MDIO1_OFFSET)