powerpc/b4860: Add workaround for errata A006384 and A006475
authorShaveta Leekha <shaveta@freescale.com>
Wed, 26 Feb 2014 10:38:22 +0000 (16:08 +0530)
committerYork Sun <yorksun@freescale.com>
Fri, 7 Mar 2014 22:52:01 +0000 (14:52 -0800)
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>
arch/powerpc/cpu/mpc85xx/cmd_errata.c
arch/powerpc/include/asm/config_mpc85xx.h
arch/powerpc/include/asm/immap_85xx.h
board/freescale/b4860qds/b4860qds.c

index 7693899..d0a1c51 100644 (file)
@@ -229,6 +229,14 @@ static int do_errata(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[])
        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);
index 0ec1417..2f47b3f 100644 (file)
 #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
index e0efc7e..edd7888 100644 (file)
@@ -2495,6 +2495,7 @@ typedef struct serdes_corenet {
 #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 @@ typedef struct serdes_corenet {
 #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 @@ typedef struct serdes_corenet {
 #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];
index 24a709e..d9c88a0 100644 (file)
@@ -288,6 +288,182 @@ int configure_vsc3316_3308(void)
        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 @@ int config_serdes2_refclks(void)
                        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 @@ int board_early_init_r(void)
        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");