mirror of
https://github.com/AsahiLinux/u-boot
synced 2024-11-10 15:14:43 +00:00
mx6: ddr: pass mx6_ddr_sysinfo to calibration routines
The DDR calibration routines have scattered support for bus widths other than 64-bits: -- The mmdc_do_write_level_calibration() routine assumes the presence of PHY1, and -- The mmdc_do_dqs_calibration() routine tries to determine whether one or two DDR PHYs are active by reading MDCTL. Since a caller of these routines must have a valid struct mx6_ddr_sysinfo for use in calling mx6_dram_cfg(), and the bus width is available in the "dsize" field, use this structure to inform the calibration routines which PHYs are active. This allows the use of the DDR calibration routines on CPU variants like i.MX6SL that only have a single MMDC port. Signed-off-by: Eric Nelson <eric@nelint.com> Reviewed-by: Marek Vasut <marex@denx.de>
This commit is contained in:
parent
b33f74ead4
commit
7f17fb7400
3 changed files with 60 additions and 46 deletions
|
@ -86,14 +86,15 @@ static void modify_dg_result(u32 *reg_st0, u32 *reg_st1, u32 *reg_ctrl)
|
|||
writel(val_ctrl, reg_ctrl);
|
||||
}
|
||||
|
||||
int mmdc_do_write_level_calibration(void)
|
||||
int mmdc_do_write_level_calibration(struct mx6_ddr_sysinfo const *sysinfo)
|
||||
{
|
||||
struct mmdc_p_regs *mmdc0 = (struct mmdc_p_regs *)MMDC_P0_BASE_ADDR;
|
||||
struct mmdc_p_regs *mmdc1 = (struct mmdc_p_regs *)MMDC_P1_BASE_ADDR;
|
||||
u32 esdmisc_val, zq_val;
|
||||
u32 errors = 0;
|
||||
u32 ldectrl[4];
|
||||
u32 ldectrl[4] = {0};
|
||||
u32 ddr_mr1 = 0x4;
|
||||
u32 rwalat_max;
|
||||
|
||||
/*
|
||||
* Stash old values in case calibration fails,
|
||||
|
@ -101,8 +102,10 @@ int mmdc_do_write_level_calibration(void)
|
|||
*/
|
||||
ldectrl[0] = readl(&mmdc0->mpwldectrl0);
|
||||
ldectrl[1] = readl(&mmdc0->mpwldectrl1);
|
||||
ldectrl[2] = readl(&mmdc1->mpwldectrl0);
|
||||
ldectrl[3] = readl(&mmdc1->mpwldectrl1);
|
||||
if (sysinfo->dsize == 2) {
|
||||
ldectrl[2] = readl(&mmdc1->mpwldectrl0);
|
||||
ldectrl[3] = readl(&mmdc1->mpwldectrl1);
|
||||
}
|
||||
|
||||
/* disable DDR logic power down timer */
|
||||
clrbits_le32(&mmdc0->mdpdc, 0xff00);
|
||||
|
@ -122,10 +125,10 @@ int mmdc_do_write_level_calibration(void)
|
|||
writel(zq_val & ~0x3, &mmdc0->mpzqhwctrl);
|
||||
|
||||
/* 3. increase walat and ralat to maximum */
|
||||
setbits_le32(&mmdc0->mdmisc,
|
||||
(1 << 6) | (1 << 7) | (1 << 8) | (1 << 16) | (1 << 17));
|
||||
setbits_le32(&mmdc1->mdmisc,
|
||||
(1 << 6) | (1 << 7) | (1 << 8) | (1 << 16) | (1 << 17));
|
||||
rwalat_max = (1 << 6) | (1 << 7) | (1 << 8) | (1 << 16) | (1 << 17);
|
||||
setbits_le32(&mmdc0->mdmisc, rwalat_max);
|
||||
if (sysinfo->dsize == 2)
|
||||
setbits_le32(&mmdc1->mdmisc, rwalat_max);
|
||||
/*
|
||||
* 4 & 5. Configure the external DDR device to enter write-leveling
|
||||
* mode through Load Mode Register command.
|
||||
|
@ -152,21 +155,25 @@ int mmdc_do_write_level_calibration(void)
|
|||
*/
|
||||
if (readl(&mmdc0->mpwlgcr) & 0x00000F00)
|
||||
errors |= 1;
|
||||
if (readl(&mmdc1->mpwlgcr) & 0x00000F00)
|
||||
errors |= 2;
|
||||
if (sysinfo->dsize == 2)
|
||||
if (readl(&mmdc1->mpwlgcr) & 0x00000F00)
|
||||
errors |= 2;
|
||||
|
||||
debug("Ending write leveling calibration. Error mask: 0x%x\n", errors);
|
||||
|
||||
/* check to see if cal failed */
|
||||
if ((readl(&mmdc0->mpwldectrl0) == 0x001F001F) &&
|
||||
(readl(&mmdc0->mpwldectrl1) == 0x001F001F) &&
|
||||
(readl(&mmdc1->mpwldectrl0) == 0x001F001F) &&
|
||||
(readl(&mmdc1->mpwldectrl1) == 0x001F001F)) {
|
||||
((sysinfo->dsize < 2) ||
|
||||
((readl(&mmdc1->mpwldectrl0) == 0x001F001F) &&
|
||||
(readl(&mmdc1->mpwldectrl1) == 0x001F001F)))) {
|
||||
debug("Cal seems to have soft-failed due to memory not supporting write leveling on all channels. Restoring original write leveling values.\n");
|
||||
writel(ldectrl[0], &mmdc0->mpwldectrl0);
|
||||
writel(ldectrl[1], &mmdc0->mpwldectrl1);
|
||||
writel(ldectrl[2], &mmdc1->mpwldectrl0);
|
||||
writel(ldectrl[3], &mmdc1->mpwldectrl1);
|
||||
if (sysinfo->dsize == 2) {
|
||||
writel(ldectrl[2], &mmdc1->mpwldectrl0);
|
||||
writel(ldectrl[3], &mmdc1->mpwldectrl1);
|
||||
}
|
||||
errors |= 4;
|
||||
}
|
||||
|
||||
|
@ -189,16 +196,20 @@ int mmdc_do_write_level_calibration(void)
|
|||
readl(&mmdc0->mpwldectrl0));
|
||||
debug("\tMMDC_MPWLDECTRL1 after write level cal: 0x%08X\n",
|
||||
readl(&mmdc0->mpwldectrl1));
|
||||
debug("\tMMDC_MPWLDECTRL0 after write level cal: 0x%08X\n",
|
||||
readl(&mmdc1->mpwldectrl0));
|
||||
debug("\tMMDC_MPWLDECTRL1 after write level cal: 0x%08X\n",
|
||||
readl(&mmdc1->mpwldectrl1));
|
||||
if (sysinfo->dsize == 2) {
|
||||
debug("\tMMDC_MPWLDECTRL0 after write level cal: 0x%08X\n",
|
||||
readl(&mmdc1->mpwldectrl0));
|
||||
debug("\tMMDC_MPWLDECTRL1 after write level cal: 0x%08X\n",
|
||||
readl(&mmdc1->mpwldectrl1));
|
||||
}
|
||||
|
||||
/* We must force a readback of these values, to get them to stick */
|
||||
readl(&mmdc0->mpwldectrl0);
|
||||
readl(&mmdc0->mpwldectrl1);
|
||||
readl(&mmdc1->mpwldectrl0);
|
||||
readl(&mmdc1->mpwldectrl1);
|
||||
if (sysinfo->dsize == 2) {
|
||||
readl(&mmdc1->mpwldectrl0);
|
||||
readl(&mmdc1->mpwldectrl1);
|
||||
}
|
||||
|
||||
/* enable DDR logic power down timer: */
|
||||
setbits_le32(&mmdc0->mdpdc, 0x00005500);
|
||||
|
@ -212,7 +223,7 @@ int mmdc_do_write_level_calibration(void)
|
|||
return errors;
|
||||
}
|
||||
|
||||
int mmdc_do_dqs_calibration(void)
|
||||
int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo)
|
||||
{
|
||||
struct mmdc_p_regs *mmdc0 = (struct mmdc_p_regs *)MMDC_P0_BASE_ADDR;
|
||||
struct mmdc_p_regs *mmdc1 = (struct mmdc_p_regs *)MMDC_P1_BASE_ADDR;
|
||||
|
@ -223,7 +234,6 @@ int mmdc_do_dqs_calibration(void)
|
|||
bool cs0_enable_initial;
|
||||
bool cs1_enable_initial;
|
||||
u32 esdmisc_val;
|
||||
u32 bus_size;
|
||||
u32 temp_ref;
|
||||
u32 pddword = 0x00ffff00; /* best so far, place into MPPDCMPR1 */
|
||||
u32 errors = 0;
|
||||
|
@ -292,10 +302,6 @@ int mmdc_do_dqs_calibration(void)
|
|||
cs0_enable = readl(&mmdc0->mdctl) & 0x80000000;
|
||||
cs1_enable = readl(&mmdc0->mdctl) & 0x40000000;
|
||||
|
||||
/* Check to see what the data bus size is */
|
||||
bus_size = (readl(&mmdc0->mdctl) & 0x30000) >> 16;
|
||||
debug("Data bus size: %d (%d bits)\n", bus_size, 1 << (bus_size + 4));
|
||||
|
||||
precharge_all(cs0_enable, cs1_enable);
|
||||
|
||||
/* Write the pre-defined value into MPPDCMPR1 */
|
||||
|
@ -314,11 +320,11 @@ int mmdc_do_dqs_calibration(void)
|
|||
* Both PHYs for x64 configuration, if x32, do only PHY0.
|
||||
*/
|
||||
writel(initdelay, &mmdc0->mprddlctl);
|
||||
if (bus_size == 0x2)
|
||||
if (sysinfo->dsize == 0x2)
|
||||
writel(initdelay, &mmdc1->mprddlctl);
|
||||
|
||||
/* Force a measurment, for previous delay setup to take effect. */
|
||||
force_delay_measurement(bus_size);
|
||||
force_delay_measurement(sysinfo->dsize);
|
||||
|
||||
/*
|
||||
* ***************************
|
||||
|
@ -364,7 +370,7 @@ int mmdc_do_dqs_calibration(void)
|
|||
if (readl(&mmdc0->mpdgctrl0) & 0x00001000)
|
||||
errors |= 1;
|
||||
|
||||
if ((bus_size == 0x2) && (readl(&mmdc1->mpdgctrl0) & 0x00001000))
|
||||
if ((sysinfo->dsize == 0x2) && (readl(&mmdc1->mpdgctrl0) & 0x00001000))
|
||||
errors |= 2;
|
||||
|
||||
/* now disable mpdgctrl0[DG_CMP_CYC] */
|
||||
|
@ -381,7 +387,7 @@ int mmdc_do_dqs_calibration(void)
|
|||
&mmdc0->mpdgctrl0);
|
||||
modify_dg_result(&mmdc0->mpdghwst2, &mmdc0->mpdghwst3,
|
||||
&mmdc0->mpdgctrl1);
|
||||
if (bus_size == 0x2) {
|
||||
if (sysinfo->dsize == 0x2) {
|
||||
modify_dg_result(&mmdc1->mpdghwst0, &mmdc1->mpdghwst1,
|
||||
&mmdc1->mpdgctrl0);
|
||||
modify_dg_result(&mmdc1->mpdghwst2, &mmdc1->mpdghwst3,
|
||||
|
@ -424,7 +430,8 @@ int mmdc_do_dqs_calibration(void)
|
|||
if (readl(&mmdc0->mprddlhwctl) & 0x0000000f)
|
||||
errors |= 4;
|
||||
|
||||
if ((bus_size == 0x2) && (readl(&mmdc1->mprddlhwctl) & 0x0000000f))
|
||||
if ((sysinfo->dsize == 0x2) &&
|
||||
(readl(&mmdc1->mprddlhwctl) & 0x0000000f))
|
||||
errors |= 8;
|
||||
|
||||
debug("Ending Read Delay calibration. Error mask: 0x%x\n", errors);
|
||||
|
@ -450,14 +457,14 @@ int mmdc_do_dqs_calibration(void)
|
|||
* Both PHYs for x64 configuration, if x32, do only PHY0.
|
||||
*/
|
||||
writel(initdelay, &mmdc0->mpwrdlctl);
|
||||
if (bus_size == 0x2)
|
||||
if (sysinfo->dsize == 0x2)
|
||||
writel(initdelay, &mmdc1->mpwrdlctl);
|
||||
|
||||
/*
|
||||
* XXX This isn't in the manual. Force a measurement,
|
||||
* for previous delay setup to effect.
|
||||
*/
|
||||
force_delay_measurement(bus_size);
|
||||
force_delay_measurement(sysinfo->dsize);
|
||||
|
||||
/*
|
||||
* 9. 10. Start the automatic write calibration process
|
||||
|
@ -477,7 +484,8 @@ int mmdc_do_dqs_calibration(void)
|
|||
if (readl(&mmdc0->mpwrdlhwctl) & 0x0000000f)
|
||||
errors |= 16;
|
||||
|
||||
if ((bus_size == 0x2) && (readl(&mmdc1->mpwrdlhwctl) & 0x0000000f))
|
||||
if ((sysinfo->dsize == 0x2) &&
|
||||
(readl(&mmdc1->mpwrdlhwctl) & 0x0000000f))
|
||||
errors |= 32;
|
||||
|
||||
debug("Ending Write Delay calibration. Error mask: 0x%x\n", errors);
|
||||
|
@ -529,14 +537,18 @@ int mmdc_do_dqs_calibration(void)
|
|||
debug("Read DQS gating calibration:\n");
|
||||
debug("\tMPDGCTRL0 PHY0 = 0x%08X\n", readl(&mmdc0->mpdgctrl0));
|
||||
debug("\tMPDGCTRL1 PHY0 = 0x%08X\n", readl(&mmdc0->mpdgctrl1));
|
||||
debug("\tMPDGCTRL0 PHY1 = 0x%08X\n", readl(&mmdc1->mpdgctrl0));
|
||||
debug("\tMPDGCTRL1 PHY1 = 0x%08X\n", readl(&mmdc1->mpdgctrl1));
|
||||
if (sysinfo->dsize == 2) {
|
||||
debug("\tMPDGCTRL0 PHY1 = 0x%08X\n", readl(&mmdc1->mpdgctrl0));
|
||||
debug("\tMPDGCTRL1 PHY1 = 0x%08X\n", readl(&mmdc1->mpdgctrl1));
|
||||
}
|
||||
debug("Read calibration:\n");
|
||||
debug("\tMPRDDLCTL PHY0 = 0x%08X\n", readl(&mmdc0->mprddlctl));
|
||||
debug("\tMPRDDLCTL PHY1 = 0x%08X\n", readl(&mmdc1->mprddlctl));
|
||||
if (sysinfo->dsize == 2)
|
||||
debug("\tMPRDDLCTL PHY1 = 0x%08X\n", readl(&mmdc1->mprddlctl));
|
||||
debug("Write calibration:\n");
|
||||
debug("\tMPWRDLCTL PHY0 = 0x%08X\n", readl(&mmdc0->mpwrdlctl));
|
||||
debug("\tMPWRDLCTL PHY1 = 0x%08X\n", readl(&mmdc1->mpwrdlctl));
|
||||
if (sysinfo->dsize == 2)
|
||||
debug("\tMPWRDLCTL PHY1 = 0x%08X\n", readl(&mmdc1->mpwrdlctl));
|
||||
|
||||
/*
|
||||
* Registers below are for debugging purposes. These print out
|
||||
|
@ -548,10 +560,12 @@ int mmdc_do_dqs_calibration(void)
|
|||
debug("\tMPDGHWST1 PHY0 = 0x%08x\n", readl(&mmdc0->mpdghwst1));
|
||||
debug("\tMPDGHWST2 PHY0 = 0x%08x\n", readl(&mmdc0->mpdghwst2));
|
||||
debug("\tMPDGHWST3 PHY0 = 0x%08x\n", readl(&mmdc0->mpdghwst3));
|
||||
debug("\tMPDGHWST0 PHY1 = 0x%08x\n", readl(&mmdc1->mpdghwst0));
|
||||
debug("\tMPDGHWST1 PHY1 = 0x%08x\n", readl(&mmdc1->mpdghwst1));
|
||||
debug("\tMPDGHWST2 PHY1 = 0x%08x\n", readl(&mmdc1->mpdghwst2));
|
||||
debug("\tMPDGHWST3 PHY1 = 0x%08x\n", readl(&mmdc1->mpdghwst3));
|
||||
if (sysinfo->dsize == 2) {
|
||||
debug("\tMPDGHWST0 PHY1 = 0x%08x\n", readl(&mmdc1->mpdghwst0));
|
||||
debug("\tMPDGHWST1 PHY1 = 0x%08x\n", readl(&mmdc1->mpdghwst1));
|
||||
debug("\tMPDGHWST2 PHY1 = 0x%08x\n", readl(&mmdc1->mpdghwst2));
|
||||
debug("\tMPDGHWST3 PHY1 = 0x%08x\n", readl(&mmdc1->mpdghwst3));
|
||||
}
|
||||
|
||||
debug("Final do_dqs_calibration error mask: 0x%x\n", errors);
|
||||
|
||||
|
|
|
@ -459,8 +459,8 @@ void mx6sl_dram_iocfg(unsigned width,
|
|||
const struct mx6sl_iomux_grp_regs *);
|
||||
|
||||
#if defined(CONFIG_MX6QDL) || defined(CONFIG_MX6Q) || defined(CONFIG_MX6D)
|
||||
int mmdc_do_write_level_calibration(void);
|
||||
int mmdc_do_dqs_calibration(void);
|
||||
int mmdc_do_write_level_calibration(struct mx6_ddr_sysinfo const *sysinfo);
|
||||
int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo);
|
||||
#endif
|
||||
|
||||
/* configure mx6 mmdc registers */
|
||||
|
|
|
@ -605,8 +605,8 @@ void board_init_f(ulong dummy)
|
|||
|
||||
/* Perform DDR DRAM calibration */
|
||||
udelay(100);
|
||||
mmdc_do_write_level_calibration();
|
||||
mmdc_do_dqs_calibration();
|
||||
mmdc_do_write_level_calibration(&novena_ddr_info);
|
||||
mmdc_do_dqs_calibration(&novena_ddr_info);
|
||||
|
||||
/* Clear the BSS. */
|
||||
memset(__bss_start, 0, __bss_end - __bss_start);
|
||||
|
|
Loading…
Reference in a new issue