armv8/fsl_lsch2: Add chip power supply voltage setup

Set up chip power supply voltage according to voltage ID.
The fuse status register provides the values from on-chip
voltage ID fuses programmed at the factory. These values
define the voltage requirements for the chip.

Main operations:
1. Set up the core voltage
2. Set up the SERDES voltage and reset SERDES lanes
3. Enable/disable DDR controller support 0.9V if needed

Signed-off-by: Hou Zhiqiang <Zhiqiang.Hou@nxp.com>
Reviewed-by: York Sun <york.sun@nxp.com>
This commit is contained in:
Hou Zhiqiang 2016-12-09 16:09:00 +08:00 committed by York Sun
parent 6424577b1b
commit 031acdbae8
5 changed files with 373 additions and 0 deletions

View File

@ -129,6 +129,278 @@ 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;
}
int setup_serdes_volt(u32 svdd)
{
struct ccsr_gur __iomem *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);
struct ccsr_serdes *serdes1_base;
#ifdef CONFIG_SYS_FSL_SRDS_2
struct ccsr_serdes *serdes2_base;
#endif
u32 cfg_rcw4 = gur_in32(&gur->rcwsr[4]);
u32 cfg_rcw5 = gur_in32(&gur->rcwsr[5]);
u32 cfg_tmp, reg = 0;
int svdd_cur, svdd_tar;
int ret;
int i;
/* Only support switch SVDD to 900mV/1000mV */
if (svdd != 900 && svdd != 1000)
return -EINVAL;
svdd_tar = svdd;
svdd_cur = get_serdes_volt();
if (svdd_cur < 0)
return -EINVAL;
debug("%s: current SVDD: %dmV; target SVDD: %dmV\n",
__func__, svdd_cur, svdd_tar);
if (svdd_cur == svdd_tar)
return 0;
serdes1_base = (void *)CONFIG_SYS_FSL_SERDES_ADDR;
#ifdef CONFIG_SYS_FSL_SRDS_2
serdes2_base = (void *)serdes1_base + 0x10000;
#endif
/* Put the all enabled lanes in reset */
#ifdef CONFIG_SYS_FSL_SRDS_1
cfg_tmp = cfg_rcw4 & FSL_CHASSIS2_RCWSR4_SRDS1_PRTCL_MASK;
cfg_tmp >>= FSL_CHASSIS2_RCWSR4_SRDS1_PRTCL_SHIFT;
for (i = 0; i < 4 && cfg_tmp & (0xf << (3 - i)); i++) {
reg = in_be32(&serdes1_base->lane[i].gcr0);
reg &= 0xFF9FFFFF;
out_be32(&serdes1_base->lane[i].gcr0, reg);
}
#endif
#ifdef CONFIG_SYS_FSL_SRDS_2
cfg_tmp = cfg_rcw4 & FSL_CHASSIS2_RCWSR4_SRDS2_PRTCL_MASK;
cfg_tmp >>= FSL_CHASSIS2_RCWSR4_SRDS2_PRTCL_SHIFT;
for (i = 0; i < 4 && cfg_tmp & (0xf << (3 - i)); i++) {
reg = in_be32(&serdes2_base->lane[i].gcr0);
reg &= 0xFF9FFFFF;
out_be32(&serdes2_base->lane[i].gcr0, reg);
}
#endif
/* Put the all enabled PLL in reset */
#ifdef CONFIG_SYS_FSL_SRDS_1
cfg_tmp = (cfg_rcw5 >> 22) & 0x3;
for (i = 0; i < 2 && !(cfg_tmp & (0x1 << (1 - i))); i++) {
reg = in_be32(&serdes1_base->bank[i].rstctl);
reg &= 0xFFFFFFBF;
reg |= 0x10000000;
out_be32(&serdes1_base->bank[i].rstctl, reg);
udelay(1);
reg = in_be32(&serdes1_base->bank[i].rstctl);
reg &= 0xFFFFFF1F;
out_be32(&serdes1_base->bank[i].rstctl, reg);
}
udelay(1);
#endif
#ifdef CONFIG_SYS_FSL_SRDS_2
cfg_tmp = (cfg_rcw5 >> 20) & 0x3;
for (i = 0; i < 2 && !(cfg_tmp & (0x1 << (1 - i))); i++) {
reg = in_be32(&serdes2_base->bank[i].rstctl);
reg &= 0xFFFFFFBF;
reg |= 0x10000000;
out_be32(&serdes2_base->bank[i].rstctl, reg);
udelay(1);
reg = in_be32(&serdes2_base->bank[i].rstctl);
reg &= 0xFFFFFF1F;
out_be32(&serdes2_base->bank[i].rstctl, reg);
}
udelay(1);
#endif
/* Put the Rx/Tx calibration into reset */
#ifdef CONFIG_SYS_FSL_SRDS_1
reg = in_be32(&serdes1_base->srdstcalcr);
reg &= 0xF7FFFFFF;
out_be32(&serdes1_base->srdstcalcr, reg);
reg = in_be32(&serdes1_base->srdsrcalcr);
reg &= 0xF7FFFFFF;
out_be32(&serdes1_base->srdsrcalcr, reg);
#endif
#ifdef CONFIG_SYS_FSL_SRDS_2
reg = in_be32(&serdes2_base->srdstcalcr);
reg &= 0xF7FFFFFF;
out_be32(&serdes2_base->srdstcalcr, reg);
reg = in_be32(&serdes2_base->srdsrcalcr);
reg &= 0xF7FFFFFF;
out_be32(&serdes2_base->srdsrcalcr, reg);
#endif
/*
* If SVDD set failed, will not return directly, so that the
* serdes lanes can complete reseting.
*/
ret = set_serdes_volt(svdd_tar);
if (ret)
printf("%s: Failed to set SVDD\n", __func__);
/* Wait for SVDD to stabilize */
udelay(100);
/* For each PLL thats not disabled via RCW */
#ifdef CONFIG_SYS_FSL_SRDS_1
cfg_tmp = (cfg_rcw5 >> 22) & 0x3;
for (i = 0; i < 2 && !(cfg_tmp & (0x1 << (1 - i))); i++) {
reg = in_be32(&serdes1_base->bank[i].rstctl);
reg |= 0x00000020;
out_be32(&serdes1_base->bank[i].rstctl, reg);
udelay(1);
reg = in_be32(&serdes1_base->bank[i].rstctl);
reg |= 0x00000080;
out_be32(&serdes1_base->bank[i].rstctl, reg);
/* Take the Rx/Tx calibration out of reset */
if (!(cfg_tmp == 0x3 && i == 1)) {
udelay(1);
reg = in_be32(&serdes1_base->srdstcalcr);
reg |= 0x08000000;
out_be32(&serdes1_base->srdstcalcr, reg);
reg = in_be32(&serdes1_base->srdsrcalcr);
reg |= 0x08000000;
out_be32(&serdes1_base->srdsrcalcr, reg);
}
}
udelay(1);
#endif
#ifdef CONFIG_SYS_FSL_SRDS_2
cfg_tmp = (cfg_rcw5 >> 20) & 0x3;
for (i = 0; i < 2 && !(cfg_tmp & (0x1 << (1 - i))); i++) {
reg = in_be32(&serdes2_base->bank[i].rstctl);
reg |= 0x00000020;
out_be32(&serdes2_base->bank[i].rstctl, reg);
udelay(1);
reg = in_be32(&serdes2_base->bank[i].rstctl);
reg |= 0x00000080;
out_be32(&serdes2_base->bank[i].rstctl, reg);
/* Take the Rx/Tx calibration out of reset */
if (!(cfg_tmp == 0x3 && i == 1)) {
udelay(1);
reg = in_be32(&serdes2_base->srdstcalcr);
reg |= 0x08000000;
out_be32(&serdes2_base->srdstcalcr, reg);
reg = in_be32(&serdes2_base->srdsrcalcr);
reg |= 0x08000000;
out_be32(&serdes2_base->srdsrcalcr, reg);
}
}
udelay(1);
#endif
/* Wait for at lesat 625us to ensure the PLLs being reset are locked */
udelay(800);
#ifdef CONFIG_SYS_FSL_SRDS_1
cfg_tmp = (cfg_rcw5 >> 22) & 0x3;
for (i = 0; i < 2 && !(cfg_tmp & (0x1 << (1 - i))); i++) {
/* if the PLL is not locked, set RST_ERR */
reg = in_be32(&serdes1_base->bank[i].pllcr0);
if (!((reg >> 23) & 0x1)) {
reg = in_be32(&serdes1_base->bank[i].rstctl);
reg |= 0x20000000;
out_be32(&serdes1_base->bank[i].rstctl, reg);
} else {
udelay(1);
reg = in_be32(&serdes1_base->bank[i].rstctl);
reg &= 0xFFFFFFEF;
reg |= 0x00000040;
out_be32(&serdes1_base->bank[i].rstctl, reg);
udelay(1);
}
}
#endif
#ifdef CONFIG_SYS_FSL_SRDS_2
cfg_tmp = (cfg_rcw5 >> 20) & 0x3;
for (i = 0; i < 2 && !(cfg_tmp & (0x1 << (1 - i))); i++) {
reg = in_be32(&serdes2_base->bank[i].pllcr0);
if (!((reg >> 23) & 0x1)) {
reg = in_be32(&serdes2_base->bank[i].rstctl);
reg |= 0x20000000;
out_be32(&serdes2_base->bank[i].rstctl, reg);
} else {
udelay(1);
reg = in_be32(&serdes2_base->bank[i].rstctl);
reg &= 0xFFFFFFEF;
reg |= 0x00000040;
out_be32(&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_rcw4 & FSL_CHASSIS2_RCWSR4_SRDS1_PRTCL_MASK;
cfg_tmp >>= FSL_CHASSIS2_RCWSR4_SRDS1_PRTCL_SHIFT;
for (i = 0; i < 4 && cfg_tmp & (0xf << (3 - i)); i++) {
reg = in_be32(&serdes1_base->lane[i].gcr0);
reg |= 0x00600000;
out_be32(&serdes1_base->lane[i].gcr0, reg);
}
#endif
#ifdef CONFIG_SYS_FSL_SRDS_2
cfg_tmp = cfg_rcw4 & FSL_CHASSIS2_RCWSR4_SRDS2_PRTCL_MASK;
cfg_tmp >>= FSL_CHASSIS2_RCWSR4_SRDS2_PRTCL_SHIFT;
for (i = 0; i < 4 && cfg_tmp & (0xf << (3 - i)); i++) {
reg = in_be32(&serdes2_base->lane[i].gcr0);
reg |= 0x00600000;
out_be32(&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_rcw5 >> 22) & 0x3;
for (i = 0; i < 2; i++) {
reg = in_be32(&serdes1_base->bank[i].pllcr0);
if (!(cfg_tmp & (0x1 << (1 - i))) && ((reg >> 23) & 0x1)) {
reg = in_be32(&serdes1_base->bank[i].rstctl);
reg |= 0x40000000;
out_be32(&serdes1_base->bank[i].rstctl, reg);
}
}
#endif
#ifdef CONFIG_SYS_FSL_SRDS_2
cfg_tmp = (cfg_rcw5 >> 20) & 0x3;
for (i = 0; i < 2; i++) {
reg = in_be32(&serdes2_base->bank[i].pllcr0);
if (!(cfg_tmp & (0x1 << (1 - i))) && ((reg >> 23) & 0x1)) {
reg = in_be32(&serdes2_base->bank[i].rstctl);
reg |= 0x40000000;
out_be32(&serdes2_base->bank[i].rstctl, reg);
}
}
#endif
return ret;
}
void fsl_serdes_init(void)
{
#ifdef CONFIG_SYS_FSL_SRDS_1

View File

@ -336,6 +336,95 @@ static void erratum_a010539(void)
#endif
}
/* Get VDD in the unit mV from voltage ID */
int get_core_volt_from_fuse(void)
{
struct ccsr_gur *gur = (void *)(CONFIG_SYS_FSL_GUTS_ADDR);
int vdd;
u32 fusesr;
u8 vid;
fusesr = in_be32(&gur->dcfg_fusesr);
debug("%s: fusesr = 0x%x\n", __func__, fusesr);
vid = (fusesr >> FSL_CHASSIS2_DCFG_FUSESR_ALTVID_SHIFT) &
FSL_CHASSIS2_DCFG_FUSESR_ALTVID_MASK;
if ((vid == 0) || (vid == FSL_CHASSIS2_DCFG_FUSESR_ALTVID_MASK)) {
vid = (fusesr >> FSL_CHASSIS2_DCFG_FUSESR_VID_SHIFT) &
FSL_CHASSIS2_DCFG_FUSESR_VID_MASK;
}
debug("%s: VID = 0x%x\n", __func__, vid);
switch (vid) {
case 0x00: /* VID isn't supported */
vdd = -EINVAL;
debug("%s: The VID feature is not supported\n", __func__);
break;
case 0x08: /* 0.9V silicon */
vdd = 900;
break;
case 0x10: /* 1.0V silicon */
vdd = 1000;
break;
default: /* Other core voltage */
vdd = -EINVAL;
printf("%s: The VID(%x) isn't supported\n", __func__, vid);
break;
}
debug("%s: The required minimum volt of CORE is %dmV\n", __func__, vdd);
return vdd;
}
__weak int board_switch_core_volt(u32 vdd)
{
return 0;
}
static int setup_core_volt(u32 vdd)
{
return board_setup_core_volt(vdd);
}
#ifdef CONFIG_SYS_FSL_DDR
static void ddr_enable_0v9_volt(bool en)
{
struct ccsr_ddr __iomem *ddr = (void *)CONFIG_SYS_FSL_DDR_ADDR;
u32 tmp;
tmp = ddr_in32(&ddr->ddr_cdr1);
if (en)
tmp |= DDR_CDR1_V0PT9_EN;
else
tmp &= ~DDR_CDR1_V0PT9_EN;
ddr_out32(&ddr->ddr_cdr1, tmp);
}
#endif
int setup_chip_volt(void)
{
int vdd;
vdd = get_core_volt_from_fuse();
/* Nothing to do for silicons doesn't support VID */
if (vdd < 0)
return vdd;
if (setup_core_volt(vdd))
printf("%s: Switch core VDD to %dmV failed\n", __func__, vdd);
#ifdef CONFIG_SYS_HAS_SERDES
if (setup_serdes_volt(vdd))
printf("%s: Switch SVDD to %dmV failed\n", __func__, vdd);
#endif
#ifdef CONFIG_SYS_FSL_DDR
if (vdd == 900)
ddr_enable_0v9_volt(true);
#endif
return 0;
}
void fsl_lsch2_early_init_f(void)
{
struct ccsr_cci400 *cci = (struct ccsr_cci400 *)CONFIG_SYS_CCI400_ADDR;

View File

@ -162,6 +162,14 @@ int is_serdes_prtcl_valid(int serdes, u32 prtcl);
#ifdef CONFIG_FSL_LSCH2
const char *serdes_clock_to_string(u32 clock);
int get_serdes_protocol(void);
#ifdef CONFIG_SYS_HAS_SERDES
/* Get the volt of SVDD in unit mV */
int get_serdes_volt(void);
/* Set the volt of SVDD in unit mV */
int set_serdes_volt(int svdd);
/* The target volt of SVDD in unit mV */
int setup_serdes_volt(u32 svdd);
#endif
#endif
#endif /* __FSL_SERDES_H__ */

View File

@ -99,6 +99,9 @@ struct ccsr_ahci {
void fsl_lsch3_early_init_f(void);
#elif defined(CONFIG_FSL_LSCH2)
void fsl_lsch2_early_init_f(void);
int setup_chip_volt(void);
/* Setup core vdd in unit mV */
int board_setup_core_volt(u32 vdd);
#endif
void cpu_name(char *name);

View File

@ -173,6 +173,7 @@ typedef struct ddr4_spd_eeprom_s generic_spd_eeprom_t;
/* DDR_CDR1 */
#define DDR_CDR1_DHC_EN 0x80000000
#define DDR_CDR1_V0PT9_EN 0x40000000
#define DDR_CDR1_ODT_SHIFT 17
#define DDR_CDR1_ODT_MASK 0x6
#define DDR_CDR2_ODT_MASK 0x1