Remove unnecessary instances of DECLARE_GLOBAL_DATA_PTR
[oweals/u-boot.git] / arch / arm / cpu / armv8 / fsl-layerscape / fsl_lsch3_serdes.c
index c2fc6468e1de6f3d376589ecc76caf5029ab3a5f..9ee0dd23e9c9f915d9449ffac3b5dac477efef4d 100644 (file)
@@ -18,7 +18,7 @@ static u8 serdes1_prtcl_map[SERDES_PRCTL_COUNT];
 static u8 serdes2_prtcl_map[SERDES_PRCTL_COUNT];
 #endif
 
-#ifdef CONFIG_FSL_MC_ENET
+#if defined(CONFIG_FSL_MC_ENET) && !defined(CONFIG_SPL_BUILD)
 int xfi_dpmac[XFI8 + 1];
 int sgmii_dpmac[SGMII16 + 1];
 #endif
@@ -28,6 +28,20 @@ __weak void wriop_init_dpmac_qsgmii(int sd, int lane_prtcl)
        return;
 }
 
+/*
+ *The return value of this func is the serdes protocol used.
+ *Typically this function is called number of times depending
+ *upon the number of serdes blocks in the Silicon.
+ *Zero is used to denote that no serdes was enabled,
+ *this is the case when golden RCW was used where DPAA2 bring was
+ *intentionally removed to achieve boot to prompt
+*/
+
+__weak int serdes_get_number(int serdes, int cfg)
+{
+       return cfg;
+}
+
 int is_serdes_configured(enum srds_prtcl device)
 {
        int ret = 0;
@@ -51,26 +65,31 @@ int is_serdes_configured(enum srds_prtcl device)
 int serdes_get_first_lane(u32 sd, enum srds_prtcl device)
 {
        struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);
-       u32 cfg = gur_in32(&gur->rcwsr[28]);
+       u32 cfg = 0;
        int i;
 
        switch (sd) {
 #ifdef CONFIG_SYS_FSL_SRDS_1
        case FSL_SRDS_1:
-               cfg &= FSL_CHASSIS3_RCWSR28_SRDS1_PRTCL_MASK;
-               cfg >>= FSL_CHASSIS3_RCWSR28_SRDS1_PRTCL_SHIFT;
+               cfg = gur_in32(&gur->rcwsr[FSL_CHASSIS3_SRDS1_REGSR - 1]);
+               cfg &= FSL_CHASSIS3_SRDS1_PRTCL_MASK;
+               cfg >>= FSL_CHASSIS3_SRDS1_PRTCL_SHIFT;
                break;
 #endif
 #ifdef CONFIG_SYS_FSL_SRDS_2
        case FSL_SRDS_2:
-               cfg &= FSL_CHASSIS3_RCWSR28_SRDS2_PRTCL_MASK;
-               cfg >>= FSL_CHASSIS3_RCWSR28_SRDS2_PRTCL_SHIFT;
+               cfg = gur_in32(&gur->rcwsr[FSL_CHASSIS3_SRDS2_REGSR - 1]);
+               cfg &= FSL_CHASSIS3_SRDS2_PRTCL_MASK;
+               cfg >>= FSL_CHASSIS3_SRDS2_PRTCL_SHIFT;
                break;
 #endif
        default:
                printf("invalid SerDes%d\n", sd);
                break;
        }
+
+       cfg = serdes_get_number(sd, cfg);
+
        /* Is serdes enabled at all? */
        if (cfg == 0)
                return -ENODEV;
@@ -83,8 +102,8 @@ int serdes_get_first_lane(u32 sd, enum srds_prtcl device)
        return -ENODEV;
 }
 
-void serdes_init(u32 sd, u32 sd_addr, u32 sd_prctl_mask, u32 sd_prctl_shift,
-               u8 serdes_prtcl_map[SERDES_PRCTL_COUNT])
+void serdes_init(u32 sd, u32 sd_addr, u32 rcwsr, u32 sd_prctl_mask,
+                u32 sd_prctl_shift, u8 serdes_prtcl_map[SERDES_PRCTL_COUNT])
 {
        struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);
        u32 cfg;
@@ -95,8 +114,10 @@ void serdes_init(u32 sd, u32 sd_addr, u32 sd_prctl_mask, u32 sd_prctl_shift,
 
        memset(serdes_prtcl_map, 0, sizeof(u8) * SERDES_PRCTL_COUNT);
 
-       cfg = gur_in32(&gur->rcwsr[28]) & sd_prctl_mask;
+       cfg = gur_in32(&gur->rcwsr[rcwsr - 1]) & sd_prctl_mask;
        cfg >>= sd_prctl_shift;
+
+       cfg = serdes_get_number(sd, cfg);
        printf("Using SERDES%d Protocol: %d (0x%x)\n", sd + 1, cfg, cfg);
 
        if (!is_serdes_prtcl_valid(sd, cfg))
@@ -108,7 +129,7 @@ void serdes_init(u32 sd, u32 sd_addr, u32 sd_prctl_mask, u32 sd_prctl_shift,
                        debug("Unknown SerDes lane protocol %d\n", lane_prtcl);
                else {
                        serdes_prtcl_map[lane_prtcl] = 1;
-#ifdef CONFIG_FSL_MC_ENET
+#if defined(CONFIG_FSL_MC_ENET) && !defined(CONFIG_SPL_BUILD)
                        switch (lane_prtcl) {
                        case QSGMII_A:
                        case QSGMII_B:
@@ -137,9 +158,296 @@ void serdes_init(u32 sd, u32 sd_addr, u32 sd_prctl_mask, u32 sd_prctl_shift,
        serdes_prtcl_map[NONE] = 1;
 }
 
+__weak int get_serdes_volt(void)
+{
+       return -1;
+}
+
+__weak int set_serdes_volt(int svdd)
+{
+       return -1;
+}
+
+#define LNAGCR0_RT_RSTB                0x00600000
+
+#define RSTCTL_RESET_MASK      0x000000E0
+
+#define RSTCTL_RSTREQ          0x80000000
+#define RSTCTL_RST_DONE                0x40000000
+#define RSTCTL_RSTERR          0x20000000
+
+#define RSTCTL_SDEN            0x00000020
+#define RSTCTL_SDRST_B         0x00000040
+#define RSTCTL_PLLRST_B                0x00000080
+
+#define TCALCR_CALRST_B                0x08000000
+
+struct serdes_prctl_info {
+       u32 id;
+       u32 mask;
+       u32 shift;
+};
+
+struct serdes_prctl_info srds_prctl_info[] = {
+#ifdef CONFIG_SYS_FSL_SRDS_1
+       {.id = 1,
+        .mask = FSL_CHASSIS3_SRDS1_PRTCL_MASK,
+        .shift = FSL_CHASSIS3_SRDS1_PRTCL_SHIFT
+       },
+
+#endif
+#ifdef CONFIG_SYS_FSL_SRDS_2
+       {.id = 2,
+        .mask = FSL_CHASSIS3_SRDS2_PRTCL_MASK,
+        .shift = FSL_CHASSIS3_SRDS2_PRTCL_SHIFT
+       },
+#endif
+       {} /* NULL ENTRY */
+};
+
+static int get_serdes_prctl_info_idx(u32 serdes_id)
+{
+       int pos = 0;
+       struct serdes_prctl_info *srds_info;
+
+       /* loop until NULL ENTRY defined by .id=0 */
+       for (srds_info = srds_prctl_info; srds_info->id != 0;
+            srds_info++, pos++) {
+               if (srds_info->id == serdes_id)
+                       return pos;
+       }
+
+       return -1;
+}
+
+static void do_enabled_lanes_reset(u32 serdes_id, u32 cfg,
+                                  struct ccsr_serdes __iomem *serdes_base,
+                                  bool cmplt)
+{
+       int i, pos;
+       u32 cfg_tmp;
+
+       pos = get_serdes_prctl_info_idx(serdes_id);
+       if (pos == -1) {
+               printf("invalid serdes_id %d\n", serdes_id);
+               return;
+       }
+
+       cfg_tmp = cfg & srds_prctl_info[pos].mask;
+       cfg_tmp >>= srds_prctl_info[pos].shift;
+
+       for (i = 0; i < 4 && cfg_tmp & (0xf << (3 - i)); i++) {
+               if (cmplt)
+                       setbits_le32(&serdes_base->lane[i].gcr0,
+                                    LNAGCR0_RT_RSTB);
+               else
+                       clrbits_le32(&serdes_base->lane[i].gcr0,
+                                    LNAGCR0_RT_RSTB);
+       }
+}
+
+static void do_pll_reset(u32 cfg,
+                        struct ccsr_serdes __iomem *serdes_base)
+{
+       int i;
+
+       for (i = 0; i < 2 && !(cfg & (0x1 << (1 - i))); i++) {
+               clrbits_le32(&serdes_base->bank[i].rstctl,
+                            RSTCTL_RESET_MASK);
+               udelay(1);
+
+               setbits_le32(&serdes_base->bank[i].rstctl,
+                            RSTCTL_RSTREQ);
+       }
+       udelay(1);
+}
+
+static void do_rx_tx_cal_reset(struct ccsr_serdes __iomem *serdes_base)
+{
+       clrbits_le32(&serdes_base->srdstcalcr, TCALCR_CALRST_B);
+       clrbits_le32(&serdes_base->srdstcalcr, TCALCR_CALRST_B);
+}
+
+static void do_rx_tx_cal_reset_comp(u32 cfg, int i,
+                                   struct ccsr_serdes __iomem *serdes_base)
+{
+       if (!(cfg == 0x3 && i == 1)) {
+               udelay(1);
+               setbits_le32(&serdes_base->srdstcalcr, TCALCR_CALRST_B);
+               setbits_le32(&serdes_base->srdstcalcr, TCALCR_CALRST_B);
+       }
+       udelay(1);
+}
+
+static void do_pll_reset_done(u32 cfg,
+                             struct ccsr_serdes __iomem *serdes_base)
+{
+       int i;
+       u32 reg = 0;
+
+       for (i = 0; i < 2; i++) {
+               reg = in_le32(&serdes_base->bank[i].pllcr0);
+               if (!(cfg & (0x1 << (1 - i))) && ((reg >> 23) & 0x1)) {
+                       setbits_le32(&serdes_base->bank[i].rstctl,
+                                    RSTCTL_RST_DONE);
+               }
+       }
+}
+
+static void do_serdes_enable(u32 cfg,
+                            struct ccsr_serdes __iomem *serdes_base)
+{
+       int i;
+
+       for (i = 0; i < 2 && !(cfg & (0x1 << (1 - i))); i++) {
+               setbits_le32(&serdes_base->bank[i].rstctl, RSTCTL_SDEN);
+               udelay(1);
+
+               setbits_le32(&serdes_base->bank[i].rstctl, RSTCTL_PLLRST_B);
+               udelay(1);
+               /* Take the Rx/Tx calibration out of reset */
+               do_rx_tx_cal_reset_comp(cfg, i, serdes_base);
+       }
+}
+
+static void do_pll_lock(u32 cfg,
+                       struct ccsr_serdes __iomem *serdes_base)
+{
+       int i;
+       u32 reg = 0;
+
+       for (i = 0; i < 2 && !(cfg & (0x1 << (1 - i))); i++) {
+               /* if the PLL is not locked, set RST_ERR */
+               reg = in_le32(&serdes_base->bank[i].pllcr0);
+               if (!((reg >> 23) & 0x1)) {
+                       setbits_le32(&serdes_base->bank[i].rstctl,
+                                    RSTCTL_RSTERR);
+               } else {
+                       udelay(1);
+                       setbits_le32(&serdes_base->bank[i].rstctl,
+                                    RSTCTL_SDRST_B);
+                       udelay(1);
+               }
+       }
+}
+
+int setup_serdes_volt(u32 svdd)
+{
+       struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);
+       struct ccsr_serdes __iomem *serdes1_base =
+                       (void *)CONFIG_SYS_FSL_LSCH3_SERDES_ADDR;
+       u32 cfg_rcwsrds1 = gur_in32(&gur->rcwsr[FSL_CHASSIS3_SRDS1_REGSR - 1]);
+#ifdef CONFIG_SYS_FSL_SRDS_2
+       struct ccsr_serdes __iomem *serdes2_base =
+                       (void *)(CONFIG_SYS_FSL_LSCH3_SERDES_ADDR + 0x10000);
+       u32 cfg_rcwsrds2 = gur_in32(&gur->rcwsr[FSL_CHASSIS3_SRDS2_REGSR - 1]);
+#endif
+       u32 cfg_tmp;
+       int svdd_cur, svdd_tar;
+       int ret = 1;
+
+       /* Only support switch SVDD to 900mV */
+       if (svdd != 900)
+               return -EINVAL;
+
+       /* Scale up to the LTC resolution is 1/4096V */
+       svdd = (svdd * 4096) / 1000;
+
+       svdd_tar = svdd;
+       svdd_cur = get_serdes_volt();
+       if (svdd_cur < 0)
+               return -EINVAL;
+
+       debug("%s: current SVDD: %x; target SVDD: %x\n",
+             __func__, svdd_cur, svdd_tar);
+       if (svdd_cur == svdd_tar)
+               return 0;
+
+       /* Put the all enabled lanes in reset */
+#ifdef CONFIG_SYS_FSL_SRDS_1
+       do_enabled_lanes_reset(1, cfg_rcwsrds1, serdes1_base, false);
+#endif
+
+#ifdef CONFIG_SYS_FSL_SRDS_2
+       do_enabled_lanes_reset(2, cfg_rcwsrds2, serdes2_base, false);
+#endif
+
+       /* Put the all enabled PLL in reset */
+#ifdef CONFIG_SYS_FSL_SRDS_1
+       cfg_tmp = cfg_rcwsrds1 & 0x3;
+       do_pll_reset(cfg_tmp, serdes1_base);
+#endif
+
+#ifdef CONFIG_SYS_FSL_SRDS_2
+       cfg_tmp = cfg_rcwsrds1 & 0xC;
+       cfg_tmp >>= 2;
+       do_pll_reset(cfg_tmp, serdes2_base);
+#endif
+
+       /* Put the Rx/Tx calibration into reset */
+#ifdef CONFIG_SYS_FSL_SRDS_1
+       do_rx_tx_cal_reset(serdes1_base);
+#endif
+
+#ifdef CONFIG_SYS_FSL_SRDS_2
+       do_rx_tx_cal_reset(serdes2_base);
+#endif
+
+       ret = set_serdes_volt(svdd);
+       if (ret < 0) {
+               printf("could not change SVDD\n");
+               ret = -1;
+       }
+
+       /* For each PLL that’s not disabled via RCW enable the SERDES */
+#ifdef CONFIG_SYS_FSL_SRDS_1
+       cfg_tmp = cfg_rcwsrds1 & 0x3;
+       do_serdes_enable(cfg_tmp, serdes1_base);
+#endif
+#ifdef CONFIG_SYS_FSL_SRDS_2
+       cfg_tmp = cfg_rcwsrds1 & 0xC;
+       cfg_tmp >>= 2;
+       do_serdes_enable(cfg_tmp, serdes2_base);
+#endif
+
+       /* Wait for at at least 625us, ensure the PLLs being reset are locked */
+       udelay(800);
+
+#ifdef CONFIG_SYS_FSL_SRDS_1
+       cfg_tmp = cfg_rcwsrds1 & 0x3;
+       do_pll_lock(cfg_tmp, serdes1_base);
+#endif
+
+#ifdef CONFIG_SYS_FSL_SRDS_2
+       cfg_tmp = cfg_rcwsrds1 & 0xC;
+       cfg_tmp >>= 2;
+       do_pll_lock(cfg_tmp, serdes2_base);
+#endif
+       /* Take the all enabled lanes out of reset */
+#ifdef CONFIG_SYS_FSL_SRDS_1
+       do_enabled_lanes_reset(1, cfg_rcwsrds1, serdes1_base, true);
+#endif
+#ifdef CONFIG_SYS_FSL_SRDS_2
+       do_enabled_lanes_reset(2, cfg_rcwsrds2, serdes2_base, true);
+#endif
+
+       /* For each PLL being reset, and achieved PLL lock set RST_DONE */
+#ifdef CONFIG_SYS_FSL_SRDS_1
+       cfg_tmp = cfg_rcwsrds1 & 0x3;
+       do_pll_reset_done(cfg_tmp, serdes1_base);
+#endif
+#ifdef CONFIG_SYS_FSL_SRDS_2
+       cfg_tmp = cfg_rcwsrds1 & 0xC;
+       cfg_tmp >>= 2;
+       do_pll_reset_done(cfg_tmp, serdes2_base);
+#endif
+
+       return ret;
+}
+
 void fsl_serdes_init(void)
 {
-#ifdef CONFIG_FSL_MC_ENET
+#if defined(CONFIG_FSL_MC_ENET) && !defined(CONFIG_SPL_BUILD)
        int i , j;
 
        for (i = XFI1, j = 1; i <= XFI8; i++, j++)
@@ -152,15 +460,17 @@ void fsl_serdes_init(void)
 #ifdef CONFIG_SYS_FSL_SRDS_1
        serdes_init(FSL_SRDS_1,
                    CONFIG_SYS_FSL_LSCH3_SERDES_ADDR,
-                   FSL_CHASSIS3_RCWSR28_SRDS1_PRTCL_MASK,
-                   FSL_CHASSIS3_RCWSR28_SRDS1_PRTCL_SHIFT,
+                   FSL_CHASSIS3_SRDS1_REGSR,
+                   FSL_CHASSIS3_SRDS1_PRTCL_MASK,
+                   FSL_CHASSIS3_SRDS1_PRTCL_SHIFT,
                    serdes1_prtcl_map);
 #endif
 #ifdef CONFIG_SYS_FSL_SRDS_2
        serdes_init(FSL_SRDS_2,
                    CONFIG_SYS_FSL_LSCH3_SERDES_ADDR + FSL_SRDS_2 * 0x10000,
-                   FSL_CHASSIS3_RCWSR28_SRDS2_PRTCL_MASK,
-                   FSL_CHASSIS3_RCWSR28_SRDS2_PRTCL_SHIFT,
+                   FSL_CHASSIS3_SRDS2_REGSR,
+                   FSL_CHASSIS3_SRDS2_PRTCL_MASK,
+                   FSL_CHASSIS3_SRDS2_PRTCL_SHIFT,
                    serdes2_prtcl_map);
 #endif
 }