summaryrefslogtreecommitdiff
path: root/arch/arm/cpu/armv8/fsl-layerscape/fsl_lsch3_serdes.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/cpu/armv8/fsl-layerscape/fsl_lsch3_serdes.c')
-rw-r--r--arch/arm/cpu/armv8/fsl-layerscape/fsl_lsch3_serdes.c274
1 files changed, 274 insertions, 0 deletions
diff --git a/arch/arm/cpu/armv8/fsl-layerscape/fsl_lsch3_serdes.c b/arch/arm/cpu/armv8/fsl-layerscape/fsl_lsch3_serdes.c
index 179cac6..39f2cdf 100644
--- a/arch/arm/cpu/armv8/fsl-layerscape/fsl_lsch3_serdes.c
+++ b/arch/arm/cpu/armv8/fsl-layerscape/fsl_lsch3_serdes.c
@@ -158,6 +158,280 @@ void serdes_init(u32 sd, u32 sd_addr, u32 rcwsr, u32 sd_prctl_mask,
serdes_prtcl_map[NONE] = 1;
}
+__weak int get_serdes_volt(void)
+{
+ return -1;
+}
+
+__weak int set_serdes_volt(int svdd)
+{
+ return -1;
+}
+
+int setup_serdes_volt(u32 svdd)
+{
+ struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);
+ struct ccsr_serdes __iomem *serdes1_base;
+ u32 cfg_rcwsrds1 = gur_in32(&gur->rcwsr[FSL_CHASSIS3_SRDS1_REGSR - 1]);
+#ifdef CONFIG_SYS_FSL_SRDS_2
+ struct ccsr_serdes __iomem *serdes2_base;
+ u32 cfg_rcwsrds2 = gur_in32(&gur->rcwsr[FSL_CHASSIS3_SRDS2_REGSR - 1]);
+#endif
+ u32 cfg_tmp, reg = 0;
+ int svdd_cur, svdd_tar;
+ int ret = 1;
+ int i;
+
+ /* Only support switch SVDD to 900mV */
+ if (svdd != 900)
+ return -1;
+
+ /* 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;
+
+ serdes1_base = (void *)CONFIG_SYS_FSL_LSCH3_SERDES_ADDR;
+#ifdef CONFIG_SYS_FSL_SRDS_2
+ serdes2_base = (void *)(CONFIG_SYS_FSL_LSCH3_SERDES_ADDR + 0x10000);
+#endif
+
+ /* Put the all enabled lanes in reset */
+#ifdef CONFIG_SYS_FSL_SRDS_1
+ cfg_tmp = cfg_rcwsrds1 & FSL_CHASSIS3_SRDS1_PRTCL_MASK;
+ cfg_tmp >>= FSL_CHASSIS3_SRDS1_PRTCL_SHIFT;
+
+ for (i = 0; i < 4 && cfg_tmp & (0xf << (3 - i)); i++) {
+ reg = in_le32(&serdes1_base->lane[i].gcr0);
+ reg &= 0xFF9FFFFF;
+ out_le32(&serdes1_base->lane[i].gcr0, reg);
+ }
+#endif
+
+#ifdef CONFIG_SYS_FSL_SRDS_2
+ cfg_tmp = cfg_rcwsrds2 & FSL_CHASSIS3_SRDS2_PRTCL_MASK;
+ cfg_tmp >>= FSL_CHASSIS3_SRDS2_PRTCL_SHIFT;
+
+ for (i = 0; i < 4 && cfg_tmp & (0xf << (3 - i)); i++) {
+ reg = in_le32(&serdes2_base->lane[i].gcr0);
+ reg &= 0xFF9FFFFF;
+ out_le32(&serdes2_base->lane[i].gcr0, reg);
+ }
+#endif
+
+ /* Put the all enabled PLL in reset */
+#ifdef CONFIG_SYS_FSL_SRDS_1
+ cfg_tmp = cfg_rcwsrds1 & 0x3;
+ for (i = 0; i < 2 && !(cfg_tmp & (0x1 << (1 - i))); i++) {
+ reg = in_le32(&serdes1_base->bank[i].rstctl);
+ reg &= 0xFFFFFFBF;
+ reg |= 0x10000000;
+ out_le32(&serdes1_base->bank[i].rstctl, reg);
+ }
+ udelay(1);
+
+ reg = in_le32(&serdes1_base->bank[i].rstctl);
+ reg &= 0xFFFFFF1F;
+ out_le32(&serdes1_base->bank[i].rstctl, reg);
+#endif
+
+#ifdef CONFIG_SYS_FSL_SRDS_2
+ cfg_tmp = cfg_rcwsrds1 & 0xC;
+ cfg_tmp >>= 2;
+ for (i = 0; i < 2 && !(cfg_tmp & (0x1 << (1 - i))); i++) {
+ reg = in_le32(&serdes2_base->bank[i].rstctl);
+ reg &= 0xFFFFFFBF;
+ reg |= 0x10000000;
+ out_le32(&serdes2_base->bank[i].rstctl, reg);
+ }
+ udelay(1);
+
+ reg = in_le32(&serdes2_base->bank[i].rstctl);
+ reg &= 0xFFFFFF1F;
+ out_le32(&serdes2_base->bank[i].rstctl, reg);
+#endif
+
+ /* Put the Rx/Tx calibration into reset */
+#ifdef CONFIG_SYS_FSL_SRDS_1
+ reg = in_le32(&serdes1_base->srdstcalcr);
+ reg &= 0xF7FFFFFF;
+ out_le32(&serdes1_base->srdstcalcr, reg);
+ reg = in_le32(&serdes1_base->srdsrcalcr);
+ reg &= 0xF7FFFFFF;
+ out_le32(&serdes1_base->srdsrcalcr, reg);
+#endif
+
+#ifdef CONFIG_SYS_FSL_SRDS_2
+ reg = in_le32(&serdes2_base->srdstcalcr);
+ reg &= 0xF7FFFFFF;
+ out_le32(&serdes2_base->srdstcalcr, reg);
+ reg = in_le32(&serdes2_base->srdsrcalcr);
+ reg &= 0xF7FFFFFF;
+ out_le32(&serdes2_base->srdsrcalcr, reg);
+#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;
+ for (i = 0; i < 2 && !(cfg_tmp & (0x1 << (1 - i))); i++) {
+ reg = in_le32(&serdes1_base->bank[i].rstctl);
+ reg |= 0x00000020;
+ out_le32(&serdes1_base->bank[i].rstctl, reg);
+ udelay(1);
+
+ reg = in_le32(&serdes1_base->bank[i].rstctl);
+ reg |= 0x00000080;
+ out_le32(&serdes1_base->bank[i].rstctl, reg);
+ udelay(1);
+ /* Take the Rx/Tx calibration out of reset */
+ if (!(cfg_tmp == 0x3 && i == 1)) {
+ udelay(1);
+ reg = in_le32(&serdes1_base->srdstcalcr);
+ reg |= 0x08000000;
+ out_le32(&serdes1_base->srdstcalcr, reg);
+ reg = in_le32(&serdes1_base->srdsrcalcr);
+ reg |= 0x08000000;
+ out_le32(&serdes1_base->srdsrcalcr, reg);
+ }
+ udelay(1);
+ }
+#endif
+#ifdef CONFIG_SYS_FSL_SRDS_2
+ cfg_tmp = cfg_rcwsrds1 & 0xC;
+ cfg_tmp >>= 2;
+ for (i = 0; i < 2 && !(cfg_tmp & (0x1 << (1 - i))); i++) {
+ reg = in_le32(&serdes2_base->bank[i].rstctl);
+ reg |= 0x00000020;
+ out_le32(&serdes2_base->bank[i].rstctl, reg);
+ udelay(1);
+
+ reg = in_le32(&serdes2_base->bank[i].rstctl);
+ reg |= 0x00000080;
+ out_le32(&serdes2_base->bank[i].rstctl, reg);
+ udelay(1);
+
+ /* Take the Rx/Tx calibration out of reset */
+ if (!(cfg_tmp == 0x3 && i == 1)) {
+ udelay(1);
+ reg = in_le32(&serdes2_base->srdstcalcr);
+ reg |= 0x08000000;
+ out_le32(&serdes2_base->srdstcalcr, reg);
+ reg = in_le32(&serdes2_base->srdsrcalcr);
+ reg |= 0x08000000;
+ out_le32(&serdes2_base->srdsrcalcr, reg);
+ }
+ udelay(1);
+ }
+#endif
+
+ /* Wait for at atleast 625us, ensure the PLLs being reset are locked */
+ udelay(800);
+
+#ifdef CONFIG_SYS_FSL_SRDS_1
+ cfg_tmp = cfg_rcwsrds1 & 0x3;
+ for (i = 0; i < 2 && !(cfg_tmp & (0x1 << (1 - i))); i++) {
+ /* if the PLL is not locked, set RST_ERR */
+ reg = in_le32(&serdes1_base->bank[i].pllcr0);
+ if (!((reg >> 23) & 0x1)) {
+ reg = in_le32(&serdes1_base->bank[i].rstctl);
+ reg |= 0x20000000;
+ out_le32(&serdes1_base->bank[i].rstctl, reg);
+ } else {
+ udelay(1);
+ reg = in_le32(&serdes1_base->bank[i].rstctl);
+ reg &= 0xFFFFFFEF;
+ reg |= 0x00000040;
+ out_le32(&serdes1_base->bank[i].rstctl, reg);
+ udelay(1);
+ }
+ }
+#endif
+
+#ifdef CONFIG_SYS_FSL_SRDS_2
+ cfg_tmp = cfg_rcwsrds1 & 0xC;
+ cfg_tmp >>= 2;
+
+ for (i = 0; i < 2 && !(cfg_tmp & (0x1 << (1 - i))); i++) {
+ reg = in_le32(&serdes2_base->bank[i].pllcr0);
+ if (!((reg >> 23) & 0x1)) {
+ reg = in_le32(&serdes2_base->bank[i].rstctl);
+ reg |= 0x20000000;
+ out_le32(&serdes2_base->bank[i].rstctl, reg);
+ } else {
+ udelay(1);
+ reg = in_le32(&serdes2_base->bank[i].rstctl);
+ reg &= 0xFFFFFFEF;
+ reg |= 0x00000040;
+ out_le32(&serdes2_base->bank[i].rstctl, reg);
+ udelay(1);
+ }
+ }
+#endif
+ /* Take the all enabled lanes out of reset */
+#ifdef CONFIG_SYS_FSL_SRDS_1
+ cfg_tmp = cfg_rcwsrds1 & FSL_CHASSIS3_SRDS1_PRTCL_MASK;
+ cfg_tmp >>= FSL_CHASSIS3_SRDS1_PRTCL_SHIFT;
+
+ for (i = 0; i < 4 && cfg_tmp & (0xf << (3 - i)); i++) {
+ reg = in_le32(&serdes1_base->lane[i].gcr0);
+ reg |= 0x00600000;
+ out_le32(&serdes1_base->lane[i].gcr0, reg);
+ }
+#endif
+#ifdef CONFIG_SYS_FSL_SRDS_2
+ cfg_tmp = cfg_rcwsrds2 & FSL_CHASSIS3_SRDS2_PRTCL_MASK;
+ cfg_tmp >>= FSL_CHASSIS3_SRDS2_PRTCL_SHIFT;
+
+ for (i = 0; i < 4 && cfg_tmp & (0xf << (3 - i)); i++) {
+ reg = in_le32(&serdes2_base->lane[i].gcr0);
+ reg |= 0x00600000;
+ out_le32(&serdes2_base->lane[i].gcr0, reg);
+ }
+#endif
+
+ /* For each PLL being reset, and achieved PLL lock set RST_DONE */
+#ifdef CONFIG_SYS_FSL_SRDS_1
+ cfg_tmp = cfg_rcwsrds1 & 0x3;
+ for (i = 0; i < 2; i++) {
+ reg = in_le32(&serdes1_base->bank[i].pllcr0);
+ if (!(cfg_tmp & (0x1 << (1 - i))) && ((reg >> 23) & 0x1)) {
+ reg = in_le32(&serdes1_base->bank[i].rstctl);
+ reg |= 0x40000000;
+ out_le32(&serdes1_base->bank[i].rstctl, reg);
+ }
+ }
+#endif
+#ifdef CONFIG_SYS_FSL_SRDS_2
+ cfg_tmp = cfg_rcwsrds1 & 0xC;
+ cfg_tmp >>= 2;
+
+ for (i = 0; i < 2; i++) {
+ reg = in_le32(&serdes2_base->bank[i].pllcr0);
+ if (!(cfg_tmp & (0x1 << (1 - i))) && ((reg >> 23) & 0x1)) {
+ reg = in_le32(&serdes2_base->bank[i].rstctl);
+ reg |= 0x40000000;
+ out_le32(&serdes2_base->bank[i].rstctl, reg);
+ }
+ }
+#endif
+
+ return ret;
+}
+
void fsl_serdes_init(void)
{
#if defined(CONFIG_FSL_MC_ENET) && !defined(CONFIG_SPL_BUILD)