powerpc/85xx: Implement work-around for P4080 erratum SERDES-A001
authorTimur Tabi <timur@freescale.com>
Mon, 18 Apr 2011 22:16:00 +0000 (17:16 -0500)
committerKumar Gala <galak@kernel.crashing.org>
Fri, 29 Apr 2011 03:09:23 +0000 (22:09 -0500)
Bank powerdown through RCW[SRDS_LPD_Bn] for XAUI on FM2 and SGMII on FM1
are swapped.

Erratum SERDES-A001 says that if bank two is kept disabled and after bank
three is enabled, then the PLL for bank three won't lock properly.  The
work-around is to enable and then disable bank two after bank three is
enabled.

Signed-off-by: Timur Tabi <timur@freescale.com>
Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
arch/powerpc/cpu/mpc85xx/fsl_corenet_serdes.c
arch/powerpc/include/asm/config_mpc85xx.h

index b44a81edea893cbcabf846fd64d79d07c082f671..741a0f84aace038b425bc8ca0c5d179ac51edd42 100644 (file)
@@ -432,6 +432,28 @@ static void p4080_erratum_serdes_a005(serdes_corenet_t *regs, unsigned int cfg)
 }
 #endif
 
+/*
+ * Wait for the RSTDONE bit to get set, or a one-second timeout.
+ */
+static void wait_for_rstdone(unsigned int bank)
+{
+       serdes_corenet_t *srds_regs =
+               (void *)CONFIG_SYS_FSL_CORENET_SERDES_ADDR;
+       unsigned long long end_tick;
+       u32 rstctl;
+
+       /* wait for reset complete or 1-second timeout */
+       end_tick = usec2ticks(1000000) + get_ticks();
+       do {
+               rstctl = in_be32(&srds_regs->bank[bank].rstctl);
+               if (rstctl & SRDS_RSTCTL_RSTDONE)
+                       break;
+       } while (end_tick > get_ticks());
+
+       if (!(rstctl & SRDS_RSTCTL_RSTDONE))
+               printf("SERDES: timeout resetting bank %u\n", bank);
+}
+
 void fsl_serdes_init(void)
 {
        ccsr_gur_t *gur = (void *)(CONFIG_SYS_MPC85xx_GUTS_ADDR);
@@ -439,7 +461,6 @@ void fsl_serdes_init(void)
        serdes_corenet_t *srds_regs;
        int lane, bank, idx;
        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;
@@ -450,6 +471,9 @@ void fsl_serdes_init(void)
 #endif
 #ifdef CONFIG_SYS_P4080_ERRATUM_SERDES9
        enum srds_prtcl device;
+#endif
+#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES_A001
+       int need_serdes_a001;   /* TRUE == need work-around for SERDES A001 */
 #endif
        char buffer[HWCONFIG_BUFFER_SIZE];
        char *buf = NULL;
@@ -519,11 +543,32 @@ void fsl_serdes_init(void)
                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.
+        */
+       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
                }
        }
 
@@ -649,8 +694,6 @@ void fsl_serdes_init(void)
 #endif
 
        for (idx = 0; idx < SRDS_MAX_BANK; idx++) {
-               u32 rstctl;
-
                bank = idx;
 
 #ifdef CONFIG_SYS_P4080_ERRATUM_SERDES8
@@ -689,20 +732,25 @@ void fsl_serdes_init(void)
                /* reset banks for errata */
                setbits_be32(&srds_regs->bank[bank].rstctl, SRDS_RSTCTL_RST);
 
-               /* wait for reset complete or 1-second timeout */
-               end_tick = usec2ticks(1000000) + get_ticks();
-               do {
-                       rstctl = in_be32(&srds_regs->bank[bank].rstctl);
-                       if (rstctl & SRDS_RSTCTL_RSTDONE)
-                               break;
-               } while (end_tick > get_ticks());
-
-               if (!(rstctl & SRDS_RSTCTL_RSTDONE)) {
-                       printf("SERDES: timeout resetting bank %d\n",
-                              bank + 1);
-                       continue;
-               }
+               wait_for_rstdone(bank);
+       }
+
+#ifdef CONFIG_SYS_P4080_ERRATUM_SERDES_A001
+       if (need_serdes_a001) {
+               /*
+                * Bank three has been enabled, so enable bank two and then
+                * disable it.
+                */
+               srds_lpd_b[FSL_SRDS_BANK_2] = 0;
+               enable_bank(gur, FSL_SRDS_BANK_2);
+
+               wait_for_rstdone(FSL_SRDS_BANK_2);
+
+               /* Disable bank 2 */
+               setbits_be32(&srds_regs->bank[FSL_SRDS_BANK_2].rstctl,
+                            SRDS_RSTCTL_SDPD);
        }
+#endif
 
 #ifdef CONFIG_SYS_P4080_ERRATUM_SERDES9
        for (device = XAUI_FM1; device <= XAUI_FM2; device++) {
index b8b8914c44284241a3e81e6713bd12f94f876fd5..ccf703b2430eb87a4805a2f5fc5cad18f45e9dca 100644 (file)
 #define CONFIG_SYS_P4080_ERRATUM_CPU22
 #define CONFIG_SYS_P4080_ERRATUM_SERDES8
 #define CONFIG_SYS_P4080_ERRATUM_SERDES9
+#define CONFIG_SYS_P4080_ERRATUM_SERDES_A001
 #define CONFIG_SYS_P4080_ERRATUM_SERDES_A005
 
 /* P5010 is single core version of P5020 */