|
|
|
@ -158,6 +158,293 @@ 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; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#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) |
|
|
|
|
{ |
|
|
|
|
#if defined(CONFIG_FSL_MC_ENET) && !defined(CONFIG_SPL_BUILD) |
|
|
|
|