[U-Boot] [RFC PATCH 00/12] imx: mx6: add virtual mx6memcal board

This patch set makes use of the dynamic DDR calibration routines added in commit d339f16 to define an alternative to the Freescale DDR stress tester tool.
The goals of this effort are to make the process of DDR validation easier and more robust: - The use of SPL simplifies the process of running DDR calibration through the imx_usb or SB_LOADER.exe tools (no need for JTAG)
- The use of Kconfig makes the set of board-specific parameters more obvious.
- The output of the SPL image can be directly pasted into either the DCD configuration file or U-Boot sources.
The notable feature of the DDR stress tool which isn't present in this virtual board file is the testing across CPU frequencies.
Patches 1-3 contain structural changes to those routines to return the calibration data and allow configuration of memory without a pre-defined set of calibration data.
Patches 4 and 5 add a struct mx6_ddr_sysinfo parameter to the calibration routines and use it to fix the use of the calibration routines on CPU variants which don't have two MMDC ports (tested on i.MX6SL).
Patches 6-7 simplify the selection of the DDR calibration routines and allow use on other CPU variants (again, tested on i.MX6SL).
Patch 8 is the meat of the patch set and defines the board itself and a single configuration sample that can be used on mx6sabresd or mx6qsabreauto.
Patches 9-12 define a set of other configurations to match other boards that I've tested during development. Note that the configurations with LPDDR (mx6slevk and WaRP) are not currently functional.
I believe that patches 1-7 are minor and can be applied after a short review, but the patch set is an RFC because of some fundamental questions:
- Is it okay with Freescale that I'm the maintainer for a board in the board/freescale tree? I don't particularly want to be the maintainer of this set of tools, but I want them, so I will try to maintain those portions that I'm in a position to test.
- What about the board name? Though I haven't looked deeply at it, but I think that some of this code could be shared with i.MX7.
- I'm including some configurations for boards that don't currently function (mx6slevk) or haven't been tested (warp) for reference and further updates. I'm also including a configuration for a board that isn't supported in main-line U-Boot (nitrogen6_max).
Eric Nelson (12): imx: mx6: ddr: return output of calibration routines novena: supply calibration parameter to DDR calibration routines imx: mx6: ddr: make calibration optional in config routines imx: mx6: ddr: pass sysinfo to calibration routines novena: pass mx6_ddr_sysinfo to calibration routines imx: mx6: ddr: use Kconfig for inclusion of DDR calibration novena_defconfig: select MX6_DDRCAL mx6: Add board mx6memcal for use in validating DDR mx6memcal: add configuration mx6memcal_mx6slevk_defconfig mx6memcal: add configuration mx6memcal_nitrogen6_max_defconfig mx6memcal: add configuration mx6memcal_sabrelite_defconfig mx6memcal: add configuration mx6memcal_warpboard_defconfig
arch/arm/cpu/armv7/mx6/Kconfig | 14 + arch/arm/cpu/armv7/mx6/ddr.c | 174 +++++++----- arch/arm/include/asm/arch-mx6/mx6-ddr.h | 8 +- board/freescale/mx6memcal/Kconfig | 135 +++++++++ board/freescale/mx6memcal/MAINTAINERS | 7 + board/freescale/mx6memcal/Makefile | 13 + board/freescale/mx6memcal/mx6memcal.c | 31 +++ board/freescale/mx6memcal/spl.c | 449 ++++++++++++++++++++++++++++++ board/kosagi/novena/novena_spl.c | 4 +- configs/mx6memcal_defconfig | 30 ++ configs/mx6memcal_mx6slevk_defconfig | 30 ++ configs/mx6memcal_nitrogen6_max_defconfig | 26 ++ configs/mx6memcal_sabrelite_defconfig | 27 ++ configs/mx6memcal_warpboard_defconfig | 32 +++ configs/novena_defconfig | 1 + include/configs/mx6memcal.h | 65 +++++ 16 files changed, 976 insertions(+), 70 deletions(-) create mode 100644 board/freescale/mx6memcal/Kconfig create mode 100644 board/freescale/mx6memcal/MAINTAINERS create mode 100644 board/freescale/mx6memcal/Makefile create mode 100644 board/freescale/mx6memcal/mx6memcal.c create mode 100644 board/freescale/mx6memcal/spl.c create mode 100644 configs/mx6memcal_defconfig create mode 100644 configs/mx6memcal_mx6slevk_defconfig create mode 100644 configs/mx6memcal_nitrogen6_max_defconfig create mode 100644 configs/mx6memcal_sabrelite_defconfig create mode 100644 configs/mx6memcal_warpboard_defconfig create mode 100644 include/configs/mx6memcal.h

Allow the calibration data from mmdc_do_write_level_calibration and mmdc_do_dqs_calibration to be returned to the caller for display.
Signed-off-by: Eric Nelson eric@nelint.com --- arch/arm/cpu/armv7/mx6/ddr.c | 29 +++++++++++++++++++++++------ arch/arm/include/asm/arch-mx6/mx6-ddr.h | 4 ++-- 2 files changed, 25 insertions(+), 8 deletions(-)
diff --git a/arch/arm/cpu/armv7/mx6/ddr.c b/arch/arm/cpu/armv7/mx6/ddr.c index 1e7ae28..bde6fe3 100644 --- a/arch/arm/cpu/armv7/mx6/ddr.c +++ b/arch/arm/cpu/armv7/mx6/ddr.c @@ -86,7 +86,7 @@ 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_mmdc_calibration *calib) { 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; @@ -195,10 +195,17 @@ int mmdc_do_write_level_calibration(void) 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 (calib) { + calib->p0_mpwldectrl0 = readl(&mmdc0->mpwldectrl0); + calib->p0_mpwldectrl1 = readl(&mmdc0->mpwldectrl1); + calib->p1_mpwldectrl0 = readl(&mmdc1->mpwldectrl0); + calib->p1_mpwldectrl1 = readl(&mmdc1->mpwldectrl1); + } else { + readl(&mmdc0->mpwldectrl0); + readl(&mmdc0->mpwldectrl1); + readl(&mmdc1->mpwldectrl0); + readl(&mmdc1->mpwldectrl1); + }
/* enable DDR logic power down timer: */ setbits_le32(&mmdc0->mdpdc, 0x00005500); @@ -212,7 +219,7 @@ int mmdc_do_write_level_calibration(void) return errors; }
-int mmdc_do_dqs_calibration(void) +int mmdc_do_dqs_calibration(struct mx6_mmdc_calibration *calib) { 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; @@ -548,6 +555,16 @@ int mmdc_do_dqs_calibration(void)
debug("Final do_dqs_calibration error mask: 0x%x\n", errors);
+ if (calib) { + calib->p0_mpdgctrl0 = readl(&mmdc0->mpdgctrl0); + calib->p0_mpdgctrl1 = readl(&mmdc0->mpdgctrl1); + calib->p1_mpdgctrl0 = readl(&mmdc1->mpdgctrl0); + calib->p1_mpdgctrl1 = readl(&mmdc1->mpdgctrl1); + calib->p0_mprddlctl = readl(&mmdc0->mprddlctl); + calib->p1_mprddlctl = readl(&mmdc1->mprddlctl); + calib->p0_mpwrdlctl = readl(&mmdc0->mpwrdlctl); + calib->p1_mpwrdlctl = readl(&mmdc1->mpwrdlctl); + } return errors; } #endif diff --git a/arch/arm/include/asm/arch-mx6/mx6-ddr.h b/arch/arm/include/asm/arch-mx6/mx6-ddr.h index 12c30d2..948862c 100644 --- a/arch/arm/include/asm/arch-mx6/mx6-ddr.h +++ b/arch/arm/include/asm/arch-mx6/mx6-ddr.h @@ -457,8 +457,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_mmdc_calibration *calib); +int mmdc_do_dqs_calibration(struct mx6_mmdc_calibration *calib); #endif
/* configure mx6 mmdc registers */

On 06/21/2016 08:41 PM, Eric Nelson wrote:
Allow the calibration data from mmdc_do_write_level_calibration and mmdc_do_dqs_calibration to be returned to the caller for display.
Signed-off-by: Eric Nelson eric@nelint.com
Why don't you create a separate function to read those params ?
arch/arm/cpu/armv7/mx6/ddr.c | 29 +++++++++++++++++++++++------ arch/arm/include/asm/arch-mx6/mx6-ddr.h | 4 ++-- 2 files changed, 25 insertions(+), 8 deletions(-)
diff --git a/arch/arm/cpu/armv7/mx6/ddr.c b/arch/arm/cpu/armv7/mx6/ddr.c index 1e7ae28..bde6fe3 100644 --- a/arch/arm/cpu/armv7/mx6/ddr.c +++ b/arch/arm/cpu/armv7/mx6/ddr.c @@ -86,7 +86,7 @@ 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_mmdc_calibration *calib) { 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; @@ -195,10 +195,17 @@ int mmdc_do_write_level_calibration(void) 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 (calib) {
calib->p0_mpwldectrl0 = readl(&mmdc0->mpwldectrl0);
calib->p0_mpwldectrl1 = readl(&mmdc0->mpwldectrl1);
calib->p1_mpwldectrl0 = readl(&mmdc1->mpwldectrl0);
calib->p1_mpwldectrl1 = readl(&mmdc1->mpwldectrl1);
} else {
readl(&mmdc0->mpwldectrl0);
readl(&mmdc0->mpwldectrl1);
readl(&mmdc1->mpwldectrl0);
readl(&mmdc1->mpwldectrl1);
}
/* enable DDR logic power down timer: */ setbits_le32(&mmdc0->mdpdc, 0x00005500);
@@ -212,7 +219,7 @@ int mmdc_do_write_level_calibration(void) return errors; }
-int mmdc_do_dqs_calibration(void) +int mmdc_do_dqs_calibration(struct mx6_mmdc_calibration *calib) { 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; @@ -548,6 +555,16 @@ int mmdc_do_dqs_calibration(void)
debug("Final do_dqs_calibration error mask: 0x%x\n", errors);
- if (calib) {
calib->p0_mpdgctrl0 = readl(&mmdc0->mpdgctrl0);
calib->p0_mpdgctrl1 = readl(&mmdc0->mpdgctrl1);
calib->p1_mpdgctrl0 = readl(&mmdc1->mpdgctrl0);
calib->p1_mpdgctrl1 = readl(&mmdc1->mpdgctrl1);
calib->p0_mprddlctl = readl(&mmdc0->mprddlctl);
calib->p1_mprddlctl = readl(&mmdc1->mprddlctl);
calib->p0_mpwrdlctl = readl(&mmdc0->mpwrdlctl);
calib->p1_mpwrdlctl = readl(&mmdc1->mpwrdlctl);
- } return errors;
} #endif diff --git a/arch/arm/include/asm/arch-mx6/mx6-ddr.h b/arch/arm/include/asm/arch-mx6/mx6-ddr.h index 12c30d2..948862c 100644 --- a/arch/arm/include/asm/arch-mx6/mx6-ddr.h +++ b/arch/arm/include/asm/arch-mx6/mx6-ddr.h @@ -457,8 +457,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_mmdc_calibration *calib); +int mmdc_do_dqs_calibration(struct mx6_mmdc_calibration *calib); #endif
/* configure mx6 mmdc registers */

Hi Marek,
On 06/22/2016 04:18 PM, Marek Vasut wrote:
On 06/21/2016 08:41 PM, Eric Nelson wrote:
Allow the calibration data from mmdc_do_write_level_calibration and mmdc_do_dqs_calibration to be returned to the caller for display.
Signed-off-by: Eric Nelson eric@nelint.com
Why don't you create a separate function to read those params ?
Probably because in my implementation, the calibration routine didn't actually write the registers. It was left to the caller to hand them to mx6_dram_cfg.
You're right though. A routine to gather calibration data would be simpler and prevent the "if (calibration)" junk in the two routines.
Regards,
Eric

On 06/23/2016 01:52 AM, Eric Nelson wrote:
Hi Marek,
On 06/22/2016 04:18 PM, Marek Vasut wrote:
On 06/21/2016 08:41 PM, Eric Nelson wrote:
Allow the calibration data from mmdc_do_write_level_calibration and mmdc_do_dqs_calibration to be returned to the caller for display.
Signed-off-by: Eric Nelson eric@nelint.com
Why don't you create a separate function to read those params ?
Probably because in my implementation, the calibration routine didn't actually write the registers. It was left to the caller to hand them to mx6_dram_cfg.
You're right though. A routine to gather calibration data would be simpler and prevent the "if (calibration)" junk in the two routines.
Try avoiding polluting the code with the if (calib) junk more :)

On 06/22/2016 04:57 PM, Marek Vasut wrote:
On 06/23/2016 01:52 AM, Eric Nelson wrote:
Hi Marek,
On 06/22/2016 04:18 PM, Marek Vasut wrote:
On 06/21/2016 08:41 PM, Eric Nelson wrote:
Allow the calibration data from mmdc_do_write_level_calibration and mmdc_do_dqs_calibration to be returned to the caller for display.
Signed-off-by: Eric Nelson eric@nelint.com
Why don't you create a separate function to read those params ?
Probably because in my implementation, the calibration routine didn't actually write the registers. It was left to the caller to hand them to mx6_dram_cfg.
You're right though. A routine to gather calibration data would be simpler and prevent the "if (calibration)" junk in the two routines.
Try avoiding polluting the code with the if (calib) junk more :)
Will fix in V2 with the addition of a routine to gather the calibration data.
This will also remove the need for patch 2/12, but the remainder could still use some review.

Signed-off-by: Eric Nelson eric@nelint.com --- board/kosagi/novena/novena_spl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/board/kosagi/novena/novena_spl.c b/board/kosagi/novena/novena_spl.c index f779bb4..9b18187 100644 --- a/board/kosagi/novena/novena_spl.c +++ b/board/kosagi/novena/novena_spl.c @@ -603,8 +603,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_mmdc_calib); + mmdc_do_dqs_calibration(&novena_mmdc_calib);
/* Clear the BSS. */ memset(__bss_start, 0, __bss_end - __bss_start);

Signed-off-by: Eric Nelson eric@nelint.com --- arch/arm/cpu/armv7/mx6/ddr.c | 44 ++++++++++++++++++++++++-------------------- 1 file changed, 24 insertions(+), 20 deletions(-)
diff --git a/arch/arm/cpu/armv7/mx6/ddr.c b/arch/arm/cpu/armv7/mx6/ddr.c index bde6fe3..eb2d000 100644 --- a/arch/arm/cpu/armv7/mx6/ddr.c +++ b/arch/arm/cpu/armv7/mx6/ddr.c @@ -1074,13 +1074,15 @@ void mx6_lpddr2_cfg(const struct mx6_ddr_sysinfo *sysinfo, * board-specific configuration: * These values are determined empirically and vary per board layout */ - mmdc0->mpwldectrl0 = calib->p0_mpwldectrl0; - mmdc0->mpwldectrl1 = calib->p0_mpwldectrl1; - mmdc0->mpdgctrl0 = calib->p0_mpdgctrl0; - mmdc0->mpdgctrl1 = calib->p0_mpdgctrl1; - mmdc0->mprddlctl = calib->p0_mprddlctl; - mmdc0->mpwrdlctl = calib->p0_mpwrdlctl; - mmdc0->mpzqlp2ctl = calib->mpzqlp2ctl; + if (calib) { + mmdc0->mpwldectrl0 = calib->p0_mpwldectrl0; + mmdc0->mpwldectrl1 = calib->p0_mpwldectrl1; + mmdc0->mpdgctrl0 = calib->p0_mpdgctrl0; + mmdc0->mpdgctrl1 = calib->p0_mpdgctrl1; + mmdc0->mprddlctl = calib->p0_mprddlctl; + mmdc0->mpwrdlctl = calib->p0_mpwrdlctl; + mmdc0->mpzqlp2ctl = calib->mpzqlp2ctl; + }
/* Read data DQ Byte0-3 delay */ mmdc0->mprddqby0dl = 0x33333333; @@ -1360,19 +1362,21 @@ void mx6_ddr3_cfg(const struct mx6_ddr_sysinfo *sysinfo, * see: * appnote, ddr3 spreadsheet */ - mmdc0->mpwldectrl0 = calib->p0_mpwldectrl0; - mmdc0->mpwldectrl1 = calib->p0_mpwldectrl1; - mmdc0->mpdgctrl0 = calib->p0_mpdgctrl0; - mmdc0->mpdgctrl1 = calib->p0_mpdgctrl1; - mmdc0->mprddlctl = calib->p0_mprddlctl; - mmdc0->mpwrdlctl = calib->p0_mpwrdlctl; - if (sysinfo->dsize > 1) { - MMDC1(mpwldectrl0, calib->p1_mpwldectrl0); - MMDC1(mpwldectrl1, calib->p1_mpwldectrl1); - MMDC1(mpdgctrl0, calib->p1_mpdgctrl0); - MMDC1(mpdgctrl1, calib->p1_mpdgctrl1); - MMDC1(mprddlctl, calib->p1_mprddlctl); - MMDC1(mpwrdlctl, calib->p1_mpwrdlctl); + if (calib) { + mmdc0->mpwldectrl0 = calib->p0_mpwldectrl0; + mmdc0->mpwldectrl1 = calib->p0_mpwldectrl1; + mmdc0->mpdgctrl0 = calib->p0_mpdgctrl0; + mmdc0->mpdgctrl1 = calib->p0_mpdgctrl1; + mmdc0->mprddlctl = calib->p0_mprddlctl; + mmdc0->mpwrdlctl = calib->p0_mpwrdlctl; + if (sysinfo->dsize > 1) { + MMDC1(mpwldectrl0, calib->p1_mpwldectrl0); + MMDC1(mpwldectrl1, calib->p1_mpwldectrl1); + MMDC1(mpdgctrl0, calib->p1_mpdgctrl0); + MMDC1(mpdgctrl1, calib->p1_mpdgctrl1); + MMDC1(mprddlctl, calib->p1_mprddlctl); + MMDC1(mpwrdlctl, calib->p1_mpwrdlctl); + } }
/* Read data DQ Byte0-3 delay */

Before calling the dynamic DDR calibration routines, board files must call mx6_dram_cfg, so they already have a struct mx6_ddr_sysinfo available for use in determining the bus width.
Use it for determining the DRAM bus width instead of a mix of dynamic determination in mmdc_do_dqs_calibration() and assumed 64-bit support in mmdc_do_write_level_calibration().
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 --- arch/arm/cpu/armv7/mx6/ddr.c | 119 +++++++++++++++++++------------- arch/arm/include/asm/arch-mx6/mx6-ddr.h | 6 +- 2 files changed, 75 insertions(+), 50 deletions(-)
diff --git a/arch/arm/cpu/armv7/mx6/ddr.c b/arch/arm/cpu/armv7/mx6/ddr.c index eb2d000..f9fb552 100644 --- a/arch/arm/cpu/armv7/mx6/ddr.c +++ b/arch/arm/cpu/armv7/mx6/ddr.c @@ -86,14 +86,16 @@ 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(struct mx6_mmdc_calibration *calib) +int mmdc_do_write_level_calibration(struct mx6_ddr_sysinfo const *sysinfo, + struct mx6_mmdc_calibration *calib) { 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 +103,10 @@ int mmdc_do_write_level_calibration(struct mx6_mmdc_calibration *calib) */ 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 +126,11 @@ int mmdc_do_write_level_calibration(struct mx6_mmdc_calibration *calib) 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 +157,25 @@ int mmdc_do_write_level_calibration(struct mx6_mmdc_calibration *calib) */ 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,22 +198,28 @@ int mmdc_do_write_level_calibration(struct mx6_mmdc_calibration *calib) 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 */ if (calib) { calib->p0_mpwldectrl0 = readl(&mmdc0->mpwldectrl0); calib->p0_mpwldectrl1 = readl(&mmdc0->mpwldectrl1); - calib->p1_mpwldectrl0 = readl(&mmdc1->mpwldectrl0); - calib->p1_mpwldectrl1 = readl(&mmdc1->mpwldectrl1); + if (sysinfo->dsize == 2) { + calib->p1_mpwldectrl0 = readl(&mmdc1->mpwldectrl0); + calib->p1_mpwldectrl1 = readl(&mmdc1->mpwldectrl1); + } } else { 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: */ @@ -219,7 +234,8 @@ int mmdc_do_write_level_calibration(struct mx6_mmdc_calibration *calib) return errors; }
-int mmdc_do_dqs_calibration(struct mx6_mmdc_calibration *calib) +int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo, + struct mx6_mmdc_calibration *calib) { 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; @@ -230,7 +246,6 @@ int mmdc_do_dqs_calibration(struct mx6_mmdc_calibration *calib) 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; @@ -299,10 +314,6 @@ int mmdc_do_dqs_calibration(struct mx6_mmdc_calibration *calib) 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 */ @@ -321,11 +332,11 @@ int mmdc_do_dqs_calibration(struct mx6_mmdc_calibration *calib) * 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);
/* * *************************** @@ -369,7 +380,7 @@ int mmdc_do_dqs_calibration(struct mx6_mmdc_calibration *calib) 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;
/* @@ -381,7 +392,7 @@ int mmdc_do_dqs_calibration(struct mx6_mmdc_calibration *calib) &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 +435,8 @@ int mmdc_do_dqs_calibration(struct mx6_mmdc_calibration *calib) 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 +462,14 @@ int mmdc_do_dqs_calibration(struct mx6_mmdc_calibration *calib) * 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 +489,8 @@ int mmdc_do_dqs_calibration(struct mx6_mmdc_calibration *calib) 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 +542,18 @@ int mmdc_do_dqs_calibration(struct mx6_mmdc_calibration *calib) 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,22 +565,28 @@ int mmdc_do_dqs_calibration(struct mx6_mmdc_calibration *calib) 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);
if (calib) { calib->p0_mpdgctrl0 = readl(&mmdc0->mpdgctrl0); calib->p0_mpdgctrl1 = readl(&mmdc0->mpdgctrl1); - calib->p1_mpdgctrl0 = readl(&mmdc1->mpdgctrl0); - calib->p1_mpdgctrl1 = readl(&mmdc1->mpdgctrl1); + if (sysinfo->dsize == 2) { + calib->p1_mpdgctrl0 = readl(&mmdc1->mpdgctrl0); + calib->p1_mpdgctrl1 = readl(&mmdc1->mpdgctrl1); + } calib->p0_mprddlctl = readl(&mmdc0->mprddlctl); - calib->p1_mprddlctl = readl(&mmdc1->mprddlctl); + if (sysinfo->dsize == 2) + calib->p1_mprddlctl = readl(&mmdc1->mprddlctl); calib->p0_mpwrdlctl = readl(&mmdc0->mpwrdlctl); - calib->p1_mpwrdlctl = readl(&mmdc1->mpwrdlctl); + if (sysinfo->dsize == 2) + calib->p1_mpwrdlctl = readl(&mmdc1->mpwrdlctl); } return errors; } diff --git a/arch/arm/include/asm/arch-mx6/mx6-ddr.h b/arch/arm/include/asm/arch-mx6/mx6-ddr.h index 948862c..41adacf 100644 --- a/arch/arm/include/asm/arch-mx6/mx6-ddr.h +++ b/arch/arm/include/asm/arch-mx6/mx6-ddr.h @@ -457,8 +457,10 @@ 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(struct mx6_mmdc_calibration *calib); -int mmdc_do_dqs_calibration(struct mx6_mmdc_calibration *calib); +int mmdc_do_write_level_calibration(struct mx6_ddr_sysinfo const *sysinfo, + struct mx6_mmdc_calibration *calib); +int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo, + struct mx6_mmdc_calibration *calib); #endif
/* configure mx6 mmdc registers */

Signed-off-by: Eric Nelson eric@nelint.com --- board/kosagi/novena/novena_spl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/board/kosagi/novena/novena_spl.c b/board/kosagi/novena/novena_spl.c index 9b18187..39b846f 100644 --- a/board/kosagi/novena/novena_spl.c +++ b/board/kosagi/novena/novena_spl.c @@ -603,8 +603,8 @@ void board_init_f(ulong dummy)
/* Perform DDR DRAM calibration */ udelay(100); - mmdc_do_write_level_calibration(&novena_mmdc_calib); - mmdc_do_dqs_calibration(&novena_mmdc_calib); + mmdc_do_write_level_calibration(&novena_ddr_info, &novena_mmdc_calib); + mmdc_do_dqs_calibration(&novena_ddr_info, &novena_mmdc_calib);
/* Clear the BSS. */ memset(__bss_start, 0, __bss_end - __bss_start);

Signed-off-by: Eric Nelson eric@nelint.com --- arch/arm/cpu/armv7/mx6/Kconfig | 5 +++++ arch/arm/cpu/armv7/mx6/ddr.c | 2 +- arch/arm/include/asm/arch-mx6/mx6-ddr.h | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/arch/arm/cpu/armv7/mx6/Kconfig b/arch/arm/cpu/armv7/mx6/Kconfig index 663f970..dc0bc7e 100644 --- a/arch/arm/cpu/armv7/mx6/Kconfig +++ b/arch/arm/cpu/armv7/mx6/Kconfig @@ -31,6 +31,11 @@ config MX6UL select ROM_UNIFIED_SECTIONS bool
+config MX6_DDRCAL + bool "Include dynamic DDR calibration routines" + depends on SPL + default n + choice prompt "MX6 board select" optional diff --git a/arch/arm/cpu/armv7/mx6/ddr.c b/arch/arm/cpu/armv7/mx6/ddr.c index f9fb552..c1534bc 100644 --- a/arch/arm/cpu/armv7/mx6/ddr.c +++ b/arch/arm/cpu/armv7/mx6/ddr.c @@ -14,7 +14,7 @@ #include <asm/types.h> #include <wait_bit.h>
-#if defined(CONFIG_MX6QDL) || defined(CONFIG_MX6Q) || defined(CONFIG_MX6D) +#if defined(CONFIG_MX6_DDRCAL)
static void reset_read_data_fifos(void) { diff --git a/arch/arm/include/asm/arch-mx6/mx6-ddr.h b/arch/arm/include/asm/arch-mx6/mx6-ddr.h index 41adacf..77f3b6e 100644 --- a/arch/arm/include/asm/arch-mx6/mx6-ddr.h +++ b/arch/arm/include/asm/arch-mx6/mx6-ddr.h @@ -456,7 +456,7 @@ void mx6sl_dram_iocfg(unsigned width, const struct mx6sl_iomux_ddr_regs *, const struct mx6sl_iomux_grp_regs *);
-#if defined(CONFIG_MX6QDL) || defined(CONFIG_MX6Q) || defined(CONFIG_MX6D) +#if defined(CONFIG_MX6_DDRCAL) int mmdc_do_write_level_calibration(struct mx6_ddr_sysinfo const *sysinfo, struct mx6_mmdc_calibration *calib); int mmdc_do_dqs_calibration(struct mx6_ddr_sysinfo const *sysinfo,

Signed-off-by: Eric Nelson eric@nelint.com --- configs/novena_defconfig | 1 + 1 file changed, 1 insertion(+)
diff --git a/configs/novena_defconfig b/configs/novena_defconfig index 69e5ea9..207e8eb 100644 --- a/configs/novena_defconfig +++ b/configs/novena_defconfig @@ -1,5 +1,6 @@ CONFIG_ARM=y CONFIG_ARCH_MX6=y +CONFIG_MX6_DDRCAL=y CONFIG_TARGET_KOSAGI_NOVENA=y CONFIG_SPL=y CONFIG_FIT=y

This is a virtual "board" that uses configuration files and Kconfig to define the memory layout used by a real board during the board bring-up process.
It generates an SPL image that can be loaded using imx_usb or SB_LOADER.exe.
When run, it will generate a set of calibration constants for use in either or both a DCD configuration file for boards that use u-boot.imx or struct mx6_mmdc_calibration for boards that boot via SPL.
In essence, it is a configurable, open-source variant of the Freescale ddr-stress tool.
https://community.nxp.com/docs/DOC-105652
File mx6memcal_defconfig configures the board for use with mx6sabresd or mx6qsabreauto.
Signed-off-by: Eric Nelson eric@nelint.com --- arch/arm/cpu/armv7/mx6/Kconfig | 9 + board/freescale/mx6memcal/Kconfig | 135 ++++++++++ board/freescale/mx6memcal/MAINTAINERS | 7 + board/freescale/mx6memcal/Makefile | 13 + board/freescale/mx6memcal/mx6memcal.c | 31 +++ board/freescale/mx6memcal/spl.c | 449 ++++++++++++++++++++++++++++++++++ configs/mx6memcal_defconfig | 30 +++ include/configs/mx6memcal.h | 65 +++++ 8 files changed, 739 insertions(+) create mode 100644 board/freescale/mx6memcal/Kconfig create mode 100644 board/freescale/mx6memcal/MAINTAINERS create mode 100644 board/freescale/mx6memcal/Makefile create mode 100644 board/freescale/mx6memcal/mx6memcal.c create mode 100644 board/freescale/mx6memcal/spl.c create mode 100644 configs/mx6memcal_defconfig create mode 100644 include/configs/mx6memcal.h
diff --git a/arch/arm/cpu/armv7/mx6/Kconfig b/arch/arm/cpu/armv7/mx6/Kconfig index dc0bc7e..c7241ca 100644 --- a/arch/arm/cpu/armv7/mx6/Kconfig +++ b/arch/arm/cpu/armv7/mx6/Kconfig @@ -92,6 +92,14 @@ config TARGET_MX6CUBOXI config TARGET_MX6QARM2 bool "mx6qarm2"
+config TARGET_MX6MEMCAL + bool "mx6memcal" + select SUPPORT_SPL + help + The mx6memcal board is a virtual board that can be used to validate + and characterize the memory layout of a new design during the initial + development and pre-production stages. + config TARGET_MX6QSABREAUTO bool "mx6qsabreauto" select DM @@ -199,6 +207,7 @@ source "board/compulab/cm_fx6/Kconfig" source "board/congatec/cgtqmx6eval/Kconfig" source "board/embest/mx6boards/Kconfig" source "board/freescale/mx6qarm2/Kconfig" +source "board/freescale/mx6memcal/Kconfig" source "board/freescale/mx6qsabreauto/Kconfig" source "board/freescale/mx6sabresd/Kconfig" source "board/freescale/mx6slevk/Kconfig" diff --git a/board/freescale/mx6memcal/Kconfig b/board/freescale/mx6memcal/Kconfig new file mode 100644 index 0000000..cfa5b6f --- /dev/null +++ b/board/freescale/mx6memcal/Kconfig @@ -0,0 +1,135 @@ +if TARGET_MX6MEMCAL + +config SYS_BOARD + default "mx6memcal" + +config SYS_VENDOR + default "freescale" + +config SYS_CONFIG_NAME + default "mx6memcal" + +config DDRWIDTH + int "DDR bus width" + default 64 + +config DDRCS + int "DDR chip selects" + default 2 + +choice + prompt "Memory type" + +config DDR3 + bool "DDR3" + help + Select this if your board design uses DDR3 + +config LPDDR2 + bool "LPDDR2" + help + Select this if your board design uses LPDDR2 + +endchoice + +choice + prompt "Memory type" + +config MT41K512M16TNA + bool "Micron MT41K512M16TNA 512Mx16 (1GiB/chip)" + depends on DDR3 + +config MT41K128M16JT + bool "Micron MT41K128M16JT 128Mx16 (256 MiB/chip)" + depends on DDR3 + +config H5TQ4G63AFR + bool "Hynix H5TQ4G63AFR 512Mx16 (1GiB/chip)" + depends on DDR3 + +config H5TQ2G63DFR + bool "Hynix H5TQ2G63DFR 128Mx16 (256 MiB/chip)" + depends on DDR3 + +config MT42L256M32D2LG + bool "Micron MT42L256M32D2LG LPDDR2 256Mx32 (1GiB/chip)" + depends on LPDDR2 + +config MT29PZZZ4D4BKESK + bool "Micron MT29PZZZ4D4BKESK multi-chip 512MiB LPDDR2/4GiB eMMC" + depends on LPDDR2 + +endchoice + +config DRAM_DRIVE_STRENGTH + int "DRAM Drive strength" + default 6 + range 0 7 + help + Enter drive strength as an index defined for IOMUX settings + for GRP_B1DS and others. + 0 == Hi Z + 6 == 40 Ohm (default) + 7 == 34 Ohm + Value will be applied to all clock and data lines + +config RALAT + int "Read additional latency" + default 5 + range 0 7 + help + Enter a latency in number of cycles. This will be added to + CAS and internal delays for which the MMDC will retrieve the + read data from the internal FIFO. + This is used to compensate for board/chip delays. + +config WALAT + int "Write additional latency" + default 0 + range 0 7 + help + Enter a latency in number of cycles. This will be added to + CAS and internal delays for which the MMDC will retrieve the + read data from the internal FIFO + This is used to compensate for board/chip delays. + +choice + prompt "Serial console" + +config SERIAL_CONSOLE_UART1 + bool "UART1" + +config SERIAL_CONSOLE_UART2 + bool "UART2" + +endchoice + +choice + prompt "UART pads" + + config UART2_EIM_D26_27 + bool "UART2 on EIM_D26/27 (SabreLite, Nitrogen6x)" + depends on SERIAL_CONSOLE_UART2 + + config UART1_CSI0_DAT10_11 + bool "UART1 on CSI0_DAT10/11 (Wand)" + depends on SERIAL_CONSOLE_UART1 + + config UART1_SD3_DAT6_7 + bool "UART1 on SD3_DAT6/7 (SabreSD)" + depends on SERIAL_CONSOLE_UART1 + + config UART1_UART1 + bool "UART1 on UART1 (i.MX6SL EVK, WaRP)" + depends on SERIAL_CONSOLE_UART1 + +endchoice + +config IMXIMAGE_OUTPUT + bool "Include output for imximage" + default y + help + Say "Y" if you want output formatted for use + in non-SPL builds + +endif diff --git a/board/freescale/mx6memcal/MAINTAINERS b/board/freescale/mx6memcal/MAINTAINERS new file mode 100644 index 0000000..5da38f7 --- /dev/null +++ b/board/freescale/mx6memcal/MAINTAINERS @@ -0,0 +1,7 @@ +MX6MEMCAL BOARD +M: Eric Nelson eric@nelint.com +S: Maintained +F: board/freescale/mx6memcal/ +F: include/configs/mx6memcal.h +F: configs/mx6memcal_defconfig + diff --git a/board/freescale/mx6memcal/Makefile b/board/freescale/mx6memcal/Makefile new file mode 100644 index 0000000..2d7a6fa --- /dev/null +++ b/board/freescale/mx6memcal/Makefile @@ -0,0 +1,13 @@ +# +# Copyright (C) 2007, Guennadi Liakhovetski lg@denx.de +# +# (C) Copyright 2011 Freescale Semiconductor, Inc. +# +# SPDX-License-Identifier: GPL-2.0+ +# + +ifdef CONFIG_SPL_BUILD +obj-y := spl.o +else +obj-y := mx6memcal.o +endif diff --git a/board/freescale/mx6memcal/mx6memcal.c b/board/freescale/mx6memcal/mx6memcal.c new file mode 100644 index 0000000..5aa13c1 --- /dev/null +++ b/board/freescale/mx6memcal/mx6memcal.c @@ -0,0 +1,31 @@ +/* + * mx6memcal board support - provides a minimal, UART-only + * U-Boot that's capable of running a memory test. + * + * Copyright (C) 2016, Nelson Integration (eric@nelint.com) + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/arch/sys_proto.h> + +DECLARE_GLOBAL_DATA_PTR; + +int board_init(void) +{ + return 0; +} + +int checkboard(void) +{ + puts("Board: mx6memcal\n"); + return 0; +} + +int dram_init(void) +{ + gd->ram_size = imx_ddr_size(); + return 0; +} + diff --git a/board/freescale/mx6memcal/spl.c b/board/freescale/mx6memcal/spl.c new file mode 100644 index 0000000..28402cf --- /dev/null +++ b/board/freescale/mx6memcal/spl.c @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2014 Gateworks Corporation + * Author: Tim Harvey tharvey@gateworks.com + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <asm/io.h> +#include <asm/arch/iomux.h> +#include <asm/arch/clock.h> +#include <asm/arch/crm_regs.h> +#include <asm/arch/mx6-ddr.h> +#include <asm/arch/mx6-pins.h> +#include <asm/arch/sys_proto.h> +#include <spl.h> + +DECLARE_GLOBAL_DATA_PTR; + +#define UART_PAD_CTRL (PAD_CTL_PUS_100K_UP | \ + PAD_CTL_SPEED_MED | PAD_CTL_DSE_40ohm | \ + PAD_CTL_SRE_FAST | PAD_CTL_HYS) + +static iomux_v3_cfg_t const uart_pads[] = { +#ifdef CONFIG_UART2_EIM_D26_27 + IOMUX_PADS(PAD_EIM_D26__UART2_TX_DATA | MUX_PAD_CTRL(UART_PAD_CTRL)), + IOMUX_PADS(PAD_EIM_D27__UART2_RX_DATA | MUX_PAD_CTRL(UART_PAD_CTRL)), +#elif defined(CONFIG_UART1_CSI0_DAT10_11) + IOMUX_PADS(PAD_CSI0_DAT10__UART1_TX_DATA | MUX_PAD_CTRL(UART_PAD_CTRL)), + IOMUX_PADS(PAD_CSI0_DAT11__UART1_RX_DATA | MUX_PAD_CTRL(UART_PAD_CTRL)), +#elif defined(CONFIG_UART1_SD3_DAT6_7) + IOMUX_PADS(PAD_SD3_DAT6__UART1_RX_DATA | MUX_PAD_CTRL(UART_PAD_CTRL)), + IOMUX_PADS(PAD_SD3_DAT7__UART1_TX_DATA | MUX_PAD_CTRL(UART_PAD_CTRL)), +#elif defined(CONFIG_UART1_UART1) + MX6_PAD_UART1_TXD__UART1_TXD | MUX_PAD_CTRL(UART_PAD_CTRL), + MX6_PAD_UART1_RXD__UART1_RXD | MUX_PAD_CTRL(UART_PAD_CTRL), +#else +#error select UART console pads +#endif +}; + +#ifdef CONFIG_DDR3 +#define GRP_DDRTYPE 0x000C0000 +#else +#define GRP_DDRTYPE 0x00080000 +#endif + +#define DDR_PKE 0 +#define DDR_MODE 0x00020000 + +#define DRAM_DRIVE_STRENGTH \ + (CONFIG_DRAM_DRIVE_STRENGTH << 3) + +/* configure MX6Q/DUAL mmdc DDR io registers */ +static struct mx6dq_iomux_ddr_regs const mx6dq_ddr_ioregs = { + /* SDCLK[0:1], CAS, RAS, Reset: Differential input, 40ohm */ + .dram_sdclk_0 = DDR_MODE+DRAM_DRIVE_STRENGTH, + .dram_sdclk_1 = DDR_MODE+DRAM_DRIVE_STRENGTH, + .dram_cas = DDR_MODE+DRAM_DRIVE_STRENGTH, + .dram_ras = DDR_MODE+DRAM_DRIVE_STRENGTH, + .dram_reset = DDR_MODE+DRAM_DRIVE_STRENGTH, + /* SDCKE[0:1]: 100k pull-up */ + .dram_sdcke0 = 0x00003000, + .dram_sdcke1 = 0x00003000, + /* SDBA2: pull-up disabled */ + .dram_sdba2 = 0x00000000, + /* SDODT[0:1]: 100k pull-up, 40 ohm */ + .dram_sdodt0 = 0x00003000+DRAM_DRIVE_STRENGTH, + .dram_sdodt1 = 0x00003000+DRAM_DRIVE_STRENGTH, + /* SDQS[0:7]: Differential input, 40 ohm */ + .dram_sdqs0 = DRAM_DRIVE_STRENGTH, + .dram_sdqs1 = DRAM_DRIVE_STRENGTH, + .dram_sdqs2 = DRAM_DRIVE_STRENGTH, + .dram_sdqs3 = DRAM_DRIVE_STRENGTH, + .dram_sdqs4 = DRAM_DRIVE_STRENGTH, + .dram_sdqs5 = DRAM_DRIVE_STRENGTH, + .dram_sdqs6 = DRAM_DRIVE_STRENGTH, + .dram_sdqs7 = DRAM_DRIVE_STRENGTH, + + /* DQM[0:7]: Differential input, 40 ohm */ + .dram_dqm0 = DDR_MODE+DRAM_DRIVE_STRENGTH, + .dram_dqm1 = DDR_MODE+DRAM_DRIVE_STRENGTH, + .dram_dqm2 = DDR_MODE+DRAM_DRIVE_STRENGTH, + .dram_dqm3 = DDR_MODE+DRAM_DRIVE_STRENGTH, + .dram_dqm4 = DDR_MODE+DRAM_DRIVE_STRENGTH, + .dram_dqm5 = DDR_MODE+DRAM_DRIVE_STRENGTH, + .dram_dqm6 = DDR_MODE+DRAM_DRIVE_STRENGTH, + .dram_dqm7 = DDR_MODE+DRAM_DRIVE_STRENGTH, +}; + +/* configure MX6Q/DUAL mmdc GRP io registers */ +static struct mx6dq_iomux_grp_regs const mx6dq_grp_ioregs = { + /* DDR3 */ + .grp_ddr_type = GRP_DDRTYPE, + .grp_ddrmode_ctl = DDR_MODE, + /* disable DDR pullups */ + .grp_ddrpke = DDR_PKE, + /* ADDR[00:16], SDBA[0:1]: 40 ohm */ + .grp_addds = DRAM_DRIVE_STRENGTH, + /* CS0/CS1/SDBA2/CKE0/CKE1/SDWE: 40 ohm */ + .grp_ctlds = DRAM_DRIVE_STRENGTH, + /* DATA[00:63]: Differential input, 40 ohm */ + .grp_ddrmode = DDR_MODE, + .grp_b0ds = DRAM_DRIVE_STRENGTH, + .grp_b1ds = DRAM_DRIVE_STRENGTH, + .grp_b2ds = DRAM_DRIVE_STRENGTH, + .grp_b3ds = DRAM_DRIVE_STRENGTH, + .grp_b4ds = DRAM_DRIVE_STRENGTH, + .grp_b5ds = DRAM_DRIVE_STRENGTH, + .grp_b6ds = DRAM_DRIVE_STRENGTH, + .grp_b7ds = DRAM_DRIVE_STRENGTH, +}; + +static struct mx6sdl_iomux_ddr_regs const mx6sdl_ddr_ioregs = { + /* SDCLK[0:1], CAS, RAS, Reset: Differential input, 40ohm */ + .dram_sdclk_0 = DDR_MODE+DRAM_DRIVE_STRENGTH, + .dram_sdclk_1 = DDR_MODE+DRAM_DRIVE_STRENGTH, + .dram_cas = DDR_MODE+DRAM_DRIVE_STRENGTH, + .dram_ras = DDR_MODE+DRAM_DRIVE_STRENGTH, + .dram_reset = DDR_MODE+DRAM_DRIVE_STRENGTH, + /* SDCKE[0:1]: 100k pull-up */ + .dram_sdcke0 = 0x00003000, + .dram_sdcke1 = 0x00003000, + /* SDBA2: pull-up disabled */ + .dram_sdba2 = 0x00000000, + /* SDODT[0:1]: 100k pull-up, 40 ohm */ + .dram_sdodt0 = 0x00003000+DRAM_DRIVE_STRENGTH, + .dram_sdodt1 = 0x00003000+DRAM_DRIVE_STRENGTH, + /* SDQS[0:7]: Differential input, 40 ohm */ + .dram_sdqs0 = DRAM_DRIVE_STRENGTH, + .dram_sdqs1 = DRAM_DRIVE_STRENGTH, + .dram_sdqs2 = DRAM_DRIVE_STRENGTH, + .dram_sdqs3 = DRAM_DRIVE_STRENGTH, + .dram_sdqs4 = DRAM_DRIVE_STRENGTH, + .dram_sdqs5 = DRAM_DRIVE_STRENGTH, + .dram_sdqs6 = DRAM_DRIVE_STRENGTH, + .dram_sdqs7 = DRAM_DRIVE_STRENGTH, + + /* DQM[0:7]: Differential input, 40 ohm */ + .dram_dqm0 = DDR_MODE+DRAM_DRIVE_STRENGTH, + .dram_dqm1 = DDR_MODE+DRAM_DRIVE_STRENGTH, + .dram_dqm2 = DDR_MODE+DRAM_DRIVE_STRENGTH, + .dram_dqm3 = DDR_MODE+DRAM_DRIVE_STRENGTH, + .dram_dqm4 = DDR_MODE+DRAM_DRIVE_STRENGTH, + .dram_dqm5 = DDR_MODE+DRAM_DRIVE_STRENGTH, + .dram_dqm6 = DDR_MODE+DRAM_DRIVE_STRENGTH, + .dram_dqm7 = DDR_MODE+DRAM_DRIVE_STRENGTH, +}; + +/* configure MX6SOLO/DUALLITE mmdc GRP io registers */ +static struct mx6sdl_iomux_grp_regs const mx6sdl_grp_ioregs = { + /* DDR3 */ + .grp_ddr_type = GRP_DDRTYPE, + /* SDQS[0:7]: Differential input, 40 ohm */ + .grp_ddrmode_ctl = DDR_MODE, + /* disable DDR pullups */ + .grp_ddrpke = DDR_PKE, + /* ADDR[00:16], SDBA[0:1]: 40 ohm */ + .grp_addds = DRAM_DRIVE_STRENGTH, + /* CS0/CS1/SDBA2/CKE0/CKE1/SDWE: 40 ohm */ + .grp_ctlds = DRAM_DRIVE_STRENGTH, + /* DATA[00:63]: Differential input, 40 ohm */ + .grp_ddrmode = DDR_MODE, + .grp_b0ds = DRAM_DRIVE_STRENGTH, + .grp_b1ds = DRAM_DRIVE_STRENGTH, + .grp_b2ds = DRAM_DRIVE_STRENGTH, + .grp_b3ds = DRAM_DRIVE_STRENGTH, + .grp_b4ds = DRAM_DRIVE_STRENGTH, + .grp_b5ds = DRAM_DRIVE_STRENGTH, + .grp_b6ds = DRAM_DRIVE_STRENGTH, + .grp_b7ds = DRAM_DRIVE_STRENGTH, +}; + +const struct mx6sl_iomux_ddr_regs mx6sl_ddr_ioregs = { + .dram_sdqs0 = DRAM_DRIVE_STRENGTH, + .dram_sdqs1 = DRAM_DRIVE_STRENGTH, + .dram_sdqs2 = DRAM_DRIVE_STRENGTH, + .dram_sdqs3 = DRAM_DRIVE_STRENGTH, + .dram_dqm0 = DRAM_DRIVE_STRENGTH, + .dram_dqm1 = DRAM_DRIVE_STRENGTH, + .dram_dqm2 = DRAM_DRIVE_STRENGTH, + .dram_dqm3 = DRAM_DRIVE_STRENGTH, + .dram_cas = DRAM_DRIVE_STRENGTH, + .dram_ras = DRAM_DRIVE_STRENGTH, + .dram_sdclk_0 = DRAM_DRIVE_STRENGTH, + .dram_reset = DRAM_DRIVE_STRENGTH, + .dram_sdba2 = 0x00020000, + .dram_odt0 = 0x00030000+DRAM_DRIVE_STRENGTH, + .dram_odt1 = 0x00030000+DRAM_DRIVE_STRENGTH, +}; + +const struct mx6sl_iomux_grp_regs mx6sl_grp_ioregs = { + .grp_b0ds = DRAM_DRIVE_STRENGTH, + .grp_b1ds = DRAM_DRIVE_STRENGTH, + .grp_b2ds = DRAM_DRIVE_STRENGTH, + .grp_b3ds = DRAM_DRIVE_STRENGTH, + .grp_addds = DRAM_DRIVE_STRENGTH, + .grp_ctlds = DRAM_DRIVE_STRENGTH, + .grp_ddrmode_ctl = DDR_MODE, + .grp_ddrpke = DDR_PKE, + .grp_ddrmode = DDR_MODE, + .grp_ddr_type = GRP_DDRTYPE, +}; + +static struct mx6_ddr_sysinfo const sysinfo = { + /* width of data bus:0=16,1=32,2=64 */ +#if CONFIG_DDRWIDTH == 32 + .dsize = 1, +#elif CONFIG_DDRWIDTH == 64 + .dsize = 2, +#else +#error missing CONFIG_DDRWIDTH +#endif + /* config for full 4GB range so that get_mem_size() works */ + .cs_density = 32, /* 32Gb per CS */ + + /* # of chip selects */ + .ncs = CONFIG_DDRCS, + .cs1_mirror = 0, + .rtt_wr = 1 /*DDR3_RTT_60_OHM*/, /* RTT_Wr = RZQ/4 */ +#ifdef RTT_NOM_120OHM + .rtt_nom = 2 /*DDR3_RTT_120_OHM*/, /* RTT_Nom = RZQ/2 */ +#else + .rtt_nom = 1 /*DDR3_RTT_60_OHM*/, /* RTT_Nom = RZQ/4 */ +#endif + .walat = CONFIG_WALAT, /* Write additional latency */ + .ralat = CONFIG_RALAT, /* Read additional latency */ + .mif3_mode = 3, /* Command prediction working mode */ + .bi_on = 1, /* Bank interleaving enabled */ +#ifdef CONFIG_DDR3 + .sde_to_rst = 0x10, /* JEDEC value for LPDDR2 - 200us */ + .rst_to_cke = 0x23, /* 33 cycles, 500us (JEDEC default) */ + .ddr_type = DDR_TYPE_DDR3, +#else + .sde_to_rst = 0, /* LPDDR2 does not need this field */ + .rst_to_cke = 0x10, /* JEDEC value for LPDDR2: 200us */ + .ddr_type = DDR_TYPE_LPDDR2, +#endif +}; + +#ifdef CONFIG_MT41K512M16TNA +/* MT41K512M16TNA-125 */ +static struct mx6_ddr3_cfg const ddrtype = { + .mem_speed = 1600, + .density = 8, + .width = 16, + .banks = 8, + .rowaddr = 15, + .coladdr = 10, + .pagesz = 1, + .trcd = 1375, + .trcmin = 5062, + .trasmin = 3750, +}; +#elif defined(CONFIG_MT41K128M16JT) +/* MT41K128M16JT-125 */ +static struct mx6_ddr3_cfg const ddrtype = { + .mem_speed = 1600, + .density = 2, + .width = 16, + .banks = 8, + .rowaddr = 14, + .coladdr = 10, + .pagesz = 2, + .trcd = 1375, + .trcmin = 4875, + .trasmin = 3500, +}; +#elif defined(CONFIG_H5TQ4G63AFR) +static struct mx6_ddr3_cfg const ddrtype = { + .mem_speed = 1600, + .density = 4, + .width = 16, + .banks = 8, + .rowaddr = 15, + .coladdr = 10, + .pagesz = 2, + .trcd = 1375, + .trcmin = 4875, + .trasmin = 3500, +}; +#elif defined CONFIG_H5TQ2G63DFR +static struct mx6_ddr3_cfg const ddrtype = { + .mem_speed = 1333, + .density = 2, + .width = 16, + .banks = 8, + .rowaddr = 14, + .coladdr = 10, + .pagesz = 2, + .trcd = 1350, + .trcmin = 4950, + .trasmin = 3600, +}; +#elif defined(CONFIG_MT42L256M32D2LG) +static struct mx6_lpddr2_cfg ddrtype = { + .mem_speed = 800, + .density = 4, + .width = 32, + .banks = 8, + .rowaddr = 14, + .coladdr = 10, + .trcd_lp = 2000, + .trppb_lp = 2000, + .trpab_lp = 2250, + .trasmin = 4200, +}; +#elif defined(CONFIG_MT29PZZZ4D4BKESK) +static struct mx6_lpddr2_cfg ddrtype = { + .mem_speed = 800, + .density = 4, + .width = 32, + .banks = 8, + .rowaddr = 14, + .coladdr = 10, + .trcd_lp = 2000, + .trppb_lp = 2000, + .trpab_lp = 2250, + .trasmin = 4200, +}; +#else +#error please select DDR type using menuconfig +#endif + +static void ccgr_init(void) +{ + struct mxc_ccm_reg *ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; + + writel(0x00C03F3F, &ccm->CCGR0); + writel(0x0030FC03, &ccm->CCGR1); + writel(0x0FFFC000, &ccm->CCGR2); + writel(0x3FF00000, &ccm->CCGR3); + writel(0x00FFF300, &ccm->CCGR4); + writel(0x0F0000C3, &ccm->CCGR5); + writel(0x000003FF, &ccm->CCGR6); +} + +static void display_calibration(struct mx6_mmdc_calibration *calib) +{ + printf(".p0_mpdgctrl0\t= 0x%08X\n", calib->p0_mpdgctrl0); + printf(".p0_mpdgctrl1\t= 0x%08X\n", calib->p0_mpdgctrl1); + printf(".p0_mprddlctl\t= 0x%08X\n", calib->p0_mprddlctl); + printf(".p0_mpwrdlctl\t= 0x%08X\n", calib->p0_mpwrdlctl); + printf(".p0_mpwldectrl0\t= 0x%08X\n", calib->p0_mpwldectrl0); + printf(".p0_mpwldectrl1\t= 0x%08X\n", calib->p0_mpwldectrl1); + if (sysinfo.dsize == 2) { + printf(".p1_mpdgctrl0\t= 0x%08X\n", calib->p1_mpdgctrl0); + printf(".p1_mpdgctrl1\t= 0x%08X\n", calib->p1_mpdgctrl1); + printf(".p1_mprddlctl\t= 0x%08X\n", calib->p1_mprddlctl); + printf(".p1_mpwrdlctl\t= 0x%08X\n", calib->p1_mpwrdlctl); + printf(".p1_mpwldectrl0\t= 0x%08X\n", calib->p1_mpwldectrl0); + printf(".p1_mpwldectrl1\t= 0x%08X\n", calib->p1_mpwldectrl1); + } +#ifdef CONFIG_IMXIMAGE_OUTPUT + printf("DATA 4 MX6_MMDC_P0_MPDGCTRL0\t= 0x%08X\n", calib->p0_mpdgctrl0); + printf("DATA 4 MX6_MMDC_P0_MPDGCTRL1\t= 0x%08X\n", calib->p0_mpdgctrl1); + printf("DATA 4 MX6_MMDC_P0_MPRDDLCTL\t= 0x%08X\n", calib->p0_mprddlctl); + printf("DATA 4 MX6_MMDC_P0_MPWRDLCTL\t= 0x%08X\n", calib->p0_mpwrdlctl); + printf("DATA 4 MX6_MMDC_P0_MPWLDECTRL0\t= 0x%08X\n", + calib->p0_mpwldectrl0); + printf("DATA 4 MX6_MMDC_P0_MPWLDECTRL1\t= 0x%08X\n", + calib->p0_mpwldectrl1); + if (sysinfo.dsize == 2) { + printf("DATA 4 MX6_MMDC_P1_mpdgctrl0\t= 0x%08X\n", + calib->p1_mpdgctrl0); + printf("DATA 4 MX6_MMDC_P1_mpdgctrl1\t= 0x%08X\n", + calib->p1_mpdgctrl1); + printf("DATA 4 MX6_MMDC_P1_mprddlctl\t= 0x%08X\n", + calib->p1_mprddlctl); + printf("DATA 4 MX6_MMDC_P1_mpwrdlctl\t= 0x%08X\n", + calib->p1_mpwrdlctl); + printf("DATA 4 MX6_MMDC_P1_mpwldectrl0\t= 0x%08X\n", + calib->p1_mpwldectrl0); + printf("DATA 4 MX6_MMDC_P1_mpwldectrl1\t= 0x%08X\n", + calib->p1_mpwldectrl1); + } +#endif +} + +/* + * called from C runtime startup code (arch/arm/lib/crt0.S:_main) + * - we have a stack and a place to store GD, both in SRAM + * - no variable global data is available + */ +void board_init_f(ulong dummy) +{ + int errs; + struct mx6_mmdc_calibration calibration; + u32 cpurev = get_cpu_rev(); + + memset((void *)gd, 0, sizeof(struct global_data)); + + /* setup AIPS and disable watchdog */ + arch_cpu_init(); + + ccgr_init(); + + SETUP_IOMUX_PADS(uart_pads); + + /* setup GP timer */ + timer_init(); + + /* UART clocks enabled and gd valid - init serial console */ + preloader_console_init(); + + if (sysinfo.dsize != 1) { + if (is_cpu_type(MXC_CPU_MX6SX) || + is_cpu_type(MXC_CPU_MX6UL) || + is_cpu_type(MXC_CPU_MX6SL)) { + printf("cpu type 0x%x doesn't support 64-bit bus\n", + get_cpu_type()); + reset_cpu(0); + } + } + printf("CPU: Freescale i.MX%s rev%d.%d at %d MHz\n", + get_imx_type((cpurev & 0xFF000) >> 12), + (cpurev & 0x000F0) >> 4, + (cpurev & 0x0000F) >> 0, + mxc_get_clock(MXC_ARM_CLK) / 1000000); +#ifdef CONFIG_MX6SL + mx6sl_dram_iocfg(CONFIG_DDRWIDTH, &mx6sl_ddr_ioregs, + &mx6sl_grp_ioregs); +#else + if (is_cpu_type(MXC_CPU_MX6Q)) { + mx6dq_dram_iocfg(CONFIG_DDRWIDTH, &mx6dq_ddr_ioregs, + &mx6dq_grp_ioregs); + } else { + mx6sdl_dram_iocfg(CONFIG_DDRWIDTH, &mx6sdl_ddr_ioregs, + &mx6sdl_grp_ioregs); + } +#endif + mx6_dram_cfg(&sysinfo, NULL, &ddrtype); + + errs = mmdc_do_write_level_calibration(&sysinfo, &calibration); + if (errs) { + printf("error %d from write level calibration\n", errs); + } else { + errs = mmdc_do_dqs_calibration(&sysinfo, &calibration); + if (errs) { + printf("error %d from write level calibration\n", errs); + } else { + printf("completed successfully\n"); + display_calibration(&calibration); + } + } + + reset_cpu(0); +} + diff --git a/configs/mx6memcal_defconfig b/configs/mx6memcal_defconfig new file mode 100644 index 0000000..826d316 --- /dev/null +++ b/configs/mx6memcal_defconfig @@ -0,0 +1,30 @@ +CONFIG_ARM=y +CONFIG_ARCH_MX6=y +CONFIG_MX6_DDRCAL=y +CONFIG_TARGET_MX6MEMCAL=y +CONFIG_SPL=y +CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=arch/arm/imx-common/spl_sd.cfg,SPL,MX6QDL" +CONFIG_DDRWIDTH=64 +CONFIG_DDRCS=2 +#CONFIG_MT41K512M16TNA is not set +CONFIG_MT41K128M16JT=y +#CONFIG_H5TQ4G63AFR is not set +# CONFIG_CMD_BOOTD is not set +# CONFIG_CMD_BOOTM is not set +# CONFIG_CMD_ELF is not set +# CONFIG_CMD_GO is not set +# CONFIG_CMD_RUN is not set +# CONFIG_CMD_IMI is not set +# CONFIG_CMD_IMLS is not set +# CONFIG_CMD_XIMG is not set +# CONFIG_CMD_EXPORTENV is not set +# CONFIG_CMD_IMPORTENV is not set +# CONFIG_CMD_EDITENV is not set +# CONFIG_CMD_SAVEENV is not set +# CONFIG_CMD_ENV_EXISTS is not set +# CONFIG_CMD_LOADB is not set +# CONFIG_CMD_LOADS is not set +# CONFIG_CMD_FLASH is not set +# CONFIG_CMD_FPGA is not set +# CONFIG_CMD_NET is not set +# CONFIG_CMD_NFS is not set diff --git a/include/configs/mx6memcal.h b/include/configs/mx6memcal.h new file mode 100644 index 0000000..8562360 --- /dev/null +++ b/include/configs/mx6memcal.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. + * + * Configuration settings for the Boundary Devices Nitrogen6X + * and Freescale i.MX6Q Sabre Lite boards. + * + * SPDX-License-Identifier: GPL-2.0+ + */ + +#ifndef __CONFIG_H +#define __CONFIG_H + +/* SPL */ +#define CONFIG_SPL_WATCHDOG_SUPPORT + +#include "mx6_common.h" +#include "imx6_spl.h" + +#undef CONFIG_CMD_BOOTM +#undef CONFIG_FSL_ESDHC +#undef CONFIG_MMC +#undef CONFIG_SPL_MMC_SUPPORT +#undef CONFIG_GENERIC_MMC +#undef CONFIG_CMD_FUSE + +#define CONFIG_SYS_MEMTEST_START 0x10000000 +#define CONFIG_SYS_MEMTEST_END 0x20000000 +#define CONFIG_SYS_MALLOC_LEN (64 * 1024 * 1024) + +#define CONFIG_MXC_UART +#ifdef CONFIG_SERIAL_CONSOLE_UART1 +#if defined(CONFIG_MX6SL) +#define CONFIG_MXC_UART_BASE UART1_IPS_BASE_ADDR +#else +#define CONFIG_MXC_UART_BASE UART1_BASE +#endif +#elif defined(CONFIG_SERIAL_CONSOLE_UART2) +#define CONFIG_MXC_UART_BASE UART2_BASE +#else +#error please define serial console (CONFIG_SERIAL_CONSOLE_UARTx) +#endif +#define CONFIG_BAUDRATE 115200 + +#define CONFIG_SYS_PBSIZE (CONFIG_SYS_CBSIZE + 16) + +/* Physical Memory Map */ +#define CONFIG_NR_DRAM_BANKS 1 +#define PHYS_SDRAM MMDC0_ARB_BASE_ADDR +#define CONFIG_RESET_CAUSE_ADDR (PHYS_SDRAM + 0x80) + +#define CONFIG_SYS_SDRAM_BASE PHYS_SDRAM +#define CONFIG_SYS_INIT_RAM_ADDR IRAM_BASE_ADDR +#define CONFIG_SYS_INIT_RAM_SIZE IRAM_SIZE + +#define CONFIG_SYS_INIT_SP_OFFSET \ + (CONFIG_SYS_INIT_RAM_SIZE - GENERATED_GBL_DATA_SIZE) +#define CONFIG_SYS_INIT_SP_ADDR \ + (CONFIG_SYS_INIT_RAM_ADDR + CONFIG_SYS_INIT_SP_OFFSET) + +#define CONFIG_SYS_NO_FLASH + +#define CONFIG_ENV_SIZE (8 * 1024) +#define CONFIG_ENV_IS_NOWHERE + +#endif /* __CONFIG_H */

Signed-off-by: Eric Nelson eric@nelint.com --- configs/mx6memcal_mx6slevk_defconfig | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 configs/mx6memcal_mx6slevk_defconfig
diff --git a/configs/mx6memcal_mx6slevk_defconfig b/configs/mx6memcal_mx6slevk_defconfig new file mode 100644 index 0000000..1bdf013 --- /dev/null +++ b/configs/mx6memcal_mx6slevk_defconfig @@ -0,0 +1,30 @@ +CONFIG_ARM=y +CONFIG_ARCH_MX6=y +CONFIG_MX6_DDRCAL=y +CONFIG_TARGET_MX6MEMCAL=y +CONFIG_DDRWIDTH=32 +CONFIG_DDRCS=1 +CONFIG_LPDDR2=y +CONFIG_RALAT=2 +CONFIG_UART1_UART1=y +CONFIG_SPL=y +CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=arch/arm/imx-common/spl_sd.cfg,SPL,MX6SL" +# CONFIG_CMD_BOOTD is not set +# CONFIG_CMD_BOOTM is not set +# CONFIG_CMD_ELF is not set +# CONFIG_CMD_GO is not set +# CONFIG_CMD_RUN is not set +# CONFIG_CMD_IMI is not set +# CONFIG_CMD_IMLS is not set +# CONFIG_CMD_XIMG is not set +# CONFIG_CMD_EXPORTENV is not set +# CONFIG_CMD_IMPORTENV is not set +# CONFIG_CMD_EDITENV is not set +# CONFIG_CMD_SAVEENV is not set +# CONFIG_CMD_ENV_EXISTS is not set +# CONFIG_CMD_LOADB is not set +# CONFIG_CMD_LOADS is not set +# CONFIG_CMD_FLASH is not set +# CONFIG_CMD_FPGA is not set +# CONFIG_CMD_NET is not set +# CONFIG_CMD_NFS is not set

Signed-off-by: Eric Nelson eric@nelint.com --- configs/mx6memcal_nitrogen6_max_defconfig | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 configs/mx6memcal_nitrogen6_max_defconfig
diff --git a/configs/mx6memcal_nitrogen6_max_defconfig b/configs/mx6memcal_nitrogen6_max_defconfig new file mode 100644 index 0000000..eb25cc5 --- /dev/null +++ b/configs/mx6memcal_nitrogen6_max_defconfig @@ -0,0 +1,26 @@ +CONFIG_ARM=y +CONFIG_ARCH_MX6=y +CONFIG_MX6_DDRCAL=y +CONFIG_TARGET_MX6MEMCAL=y +CONFIG_SERIAL_CONSOLE_UART2=y +CONFIG_SPL=y +CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=arch/arm/imx-common/spl_sd.cfg,SPL,MX6QDL" +# CONFIG_CMD_BOOTD is not set +# CONFIG_CMD_BOOTM is not set +# CONFIG_CMD_ELF is not set +# CONFIG_CMD_GO is not set +# CONFIG_CMD_RUN is not set +# CONFIG_CMD_IMI is not set +# CONFIG_CMD_IMLS is not set +# CONFIG_CMD_XIMG is not set +# CONFIG_CMD_EXPORTENV is not set +# CONFIG_CMD_IMPORTENV is not set +# CONFIG_CMD_EDITENV is not set +# CONFIG_CMD_SAVEENV is not set +# CONFIG_CMD_ENV_EXISTS is not set +# CONFIG_CMD_LOADB is not set +# CONFIG_CMD_LOADS is not set +# CONFIG_CMD_FLASH is not set +# CONFIG_CMD_FPGA is not set +# CONFIG_CMD_NET is not set +# CONFIG_CMD_NFS is not set

Signed-off-by: Eric Nelson eric@nelint.com --- configs/mx6memcal_sabrelite_defconfig | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 configs/mx6memcal_sabrelite_defconfig
diff --git a/configs/mx6memcal_sabrelite_defconfig b/configs/mx6memcal_sabrelite_defconfig new file mode 100644 index 0000000..e8372b6 --- /dev/null +++ b/configs/mx6memcal_sabrelite_defconfig @@ -0,0 +1,27 @@ +CONFIG_ARM=y +CONFIG_ARCH_MX6=y +CONFIG_MX6_DDRCAL=y +CONFIG_TARGET_MX6MEMCAL=y +CONFIG_MT41K128M16JT=y +CONFIG_SERIAL_CONSOLE_UART2=y +CONFIG_SPL=y +CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=arch/arm/imx-common/spl_sd.cfg,SPL,MX6QDL" +# CONFIG_CMD_BOOTD is not set +# CONFIG_CMD_BOOTM is not set +# CONFIG_CMD_ELF is not set +# CONFIG_CMD_GO is not set +# CONFIG_CMD_RUN is not set +# CONFIG_CMD_IMI is not set +# CONFIG_CMD_IMLS is not set +# CONFIG_CMD_XIMG is not set +# CONFIG_CMD_EXPORTENV is not set +# CONFIG_CMD_IMPORTENV is not set +# CONFIG_CMD_EDITENV is not set +# CONFIG_CMD_SAVEENV is not set +# CONFIG_CMD_ENV_EXISTS is not set +# CONFIG_CMD_LOADB is not set +# CONFIG_CMD_LOADS is not set +# CONFIG_CMD_FLASH is not set +# CONFIG_CMD_FPGA is not set +# CONFIG_CMD_NET is not set +# CONFIG_CMD_NFS is not set

Signed-off-by: Eric Nelson eric@nelint.com --- Note that I haven't tested this board because I don't currently have one.
configs/mx6memcal_warpboard_defconfig | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 configs/mx6memcal_warpboard_defconfig
diff --git a/configs/mx6memcal_warpboard_defconfig b/configs/mx6memcal_warpboard_defconfig new file mode 100644 index 0000000..0ec1b3c --- /dev/null +++ b/configs/mx6memcal_warpboard_defconfig @@ -0,0 +1,32 @@ +CONFIG_ARM=y +CONFIG_ARCH_MX6=y +CONFIG_MX6_DDRCAL=y +CONFIG_TARGET_MX6MEMCAL=y +CONFIG_DDRWIDTH=32 +CONFIG_DDRCS=1 +CONFIG_LPDDR2=y +CONFIG_MT29PZZZ4D4BKESK=y +CONFIG_DRAM_DRIVE_STRENGTH=4 +CONFIG_RALAT=2 +CONFIG_UART1_UART1=y +CONFIG_SPL=y +CONFIG_SYS_EXTRA_OPTIONS="IMX_CONFIG=arch/arm/imx-common/spl_sd.cfg,SPL,MX6SL" +# CONFIG_CMD_BOOTD is not set +# CONFIG_CMD_BOOTM is not set +# CONFIG_CMD_ELF is not set +# CONFIG_CMD_GO is not set +# CONFIG_CMD_RUN is not set +# CONFIG_CMD_IMI is not set +# CONFIG_CMD_IMLS is not set +# CONFIG_CMD_XIMG is not set +# CONFIG_CMD_EXPORTENV is not set +# CONFIG_CMD_IMPORTENV is not set +# CONFIG_CMD_EDITENV is not set +# CONFIG_CMD_SAVEENV is not set +# CONFIG_CMD_ENV_EXISTS is not set +# CONFIG_CMD_LOADB is not set +# CONFIG_CMD_LOADS is not set +# CONFIG_CMD_FLASH is not set +# CONFIG_CMD_FPGA is not set +# CONFIG_CMD_NET is not set +# CONFIG_CMD_NFS is not set

On 06/21/2016 11:41 AM, Eric Nelson wrote:
This patch set makes use of the dynamic DDR calibration routines added in commit d339f16 to define an alternative to the Freescale DDR stress tester tool.
Hi all,
While preparing this patch set, I thought again about the tools we have for managing SPL images, and especially "first boot" issues (when a full U-Boot isn't available or isn't programmed into the normal boot device).
Since the "mx6memcal" device is deliberately small, with only the DDR and UART defined, there's no way to load a full U-Boot and doing so would defeat the primary purpose.
We've discussed this at length on a number of occasions, (most recently in [5]) but I don't think we have a good solution.
I just looked at Troy's ancient patches ([1] and [2]) and it seems that these two patches are the bulk of the work needed to build a combined SPL+U-Boot image, and if I understand patch 2 correctly, SPL could tell at run-time that it was executed via a plugin and return control to the Boot ROM instead of trying to load U-Boot or a kernel.
If I recall correctly, Troy dropped these patches because of push-back that came from the sheer size of the 21-patch set, and not because of any particular objection to support for plugins.
It seems that a little work here would remove the need for things like Stefano's patch in [3] or Michael thoughts of adding DFU support in [4].
I'm interested in hearing your thoughts on the subject.
Regards,
Eric
[1] [U-Boot,V2,06/21] imximage: add plugin commands https://patchwork.ozlabs.org/patch/186051/ [2] [U-Boot,V2,08/21] mx6: add plugin file for use with imximage.cfg https://patchwork.ozlabs.org/patch/186054/ [3] [U-Boot] [PATCH v2] imx_common: check for Serial Downloader in spl_boot_device http://lists.denx.de/pipermail/u-boot/2015-December/237555.html [4] Discussion of SPU and DFU http://lists.denx.de/pipermail/u-boot/2013-October/thread.html#165805 [5] Discussion of booting Wand through USB http://lists.denx.de/pipermail/u-boot/2015-May/thread.html#215573

On Tue, Jun 21, 2016 at 12:48:45PM -0700, Eric Nelson wrote:
On 06/21/2016 11:41 AM, Eric Nelson wrote:
This patch set makes use of the dynamic DDR calibration routines added in commit d339f16 to define an alternative to the Freescale DDR stress tester tool.
Hi all,
While preparing this patch set, I thought again about the tools we have for managing SPL images, and especially "first boot" issues (when a full U-Boot isn't available or isn't programmed into the normal boot device).
Since the "mx6memcal" device is deliberately small, with only the DDR and UART defined, there's no way to load a full U-Boot and doing so would defeat the primary purpose.
We've discussed this at length on a number of occasions, (most recently in [5]) but I don't think we have a good solution.
I just looked at Troy's ancient patches ([1] and [2]) and it seems that these two patches are the bulk of the work needed to build a combined SPL+U-Boot image, and if I understand patch 2 correctly, SPL could tell at run-time that it was executed via a plugin and return control to the Boot ROM instead of trying to load U-Boot or a kernel.
If I recall correctly, Troy dropped these patches because of push-back that came from the sheer size of the 21-patch set, and not because of any particular objection to support for plugins.
It seems that a little work here would remove the need for things like Stefano's patch in [3] or Michael thoughts of adding DFU support in [4].
I'm interested in hearing your thoughts on the subject.
So, if I read the commentary right, "plugin" is how we can eventually get to the point where we can support, in a single binary, somewhat disparate board / SoC types because we can have enough smarts there to see if it's a Q or D or DL and so forth, right? That would be worthwhile to support, yes.

On Tue, Jun 21, 2016 at 11:41 AM, Eric Nelson eric@nelint.com wrote:
This patch set makes use of the dynamic DDR calibration routines added in commit d339f16 to define an alternative to the Freescale DDR stress tester tool.
The goals of this effort are to make the process of DDR validation easier and more robust: - The use of SPL simplifies the process of running DDR calibration through the imx_usb or SB_LOADER.exe tools (no need for JTAG)
- The use of Kconfig makes the set of board-specific parameters more obvious. - The output of the SPL image can be directly pasted into either the DCD configuration file or U-Boot sources.
The notable feature of the DDR stress tool which isn't present in this virtual board file is the testing across CPU frequencies.
Patches 1-3 contain structural changes to those routines to return the calibration data and allow configuration of memory without a pre-defined set of calibration data.
Patches 4 and 5 add a struct mx6_ddr_sysinfo parameter to the calibration routines and use it to fix the use of the calibration routines on CPU variants which don't have two MMDC ports (tested on i.MX6SL).
Patches 6-7 simplify the selection of the DDR calibration routines and allow use on other CPU variants (again, tested on i.MX6SL).
Patch 8 is the meat of the patch set and defines the board itself and a single configuration sample that can be used on mx6sabresd or mx6qsabreauto.
Patches 9-12 define a set of other configurations to match other boards that I've tested during development. Note that the configurations with LPDDR (mx6slevk and WaRP) are not currently functional.
I believe that patches 1-7 are minor and can be applied after a short review, but the patch set is an RFC because of some fundamental questions:
- Is it okay with Freescale that I'm the maintainer for a board in the board/freescale tree? I don't particularly want to be the maintainer of this set of tools, but I want them, so I will try to maintain those portions that I'm in a position to test. - What about the board name? Though I haven't looked deeply at it, but I think that some of this code could be shared with i.MX7. - I'm including some configurations for boards that don't currently function (mx6slevk) or haven't been tested (warp) for reference and further updates. I'm also including a configuration for a board that isn't supported in main-line U-Boot (nitrogen6_max).
Eric,
I currently have the time and resources to run some large-scale tests and prove out the reliability of auto-calibration.
It looks like you never got a whole lot of feedback on this series in the general sense - probably because there are only a few of us who need to deal with imx6 ddr calibration. Where do you stand with it?
I love the idea of providing an easier way of obtaining calibration values and removing the dependence on the Freescale tool. This SPL would use the auto-calibration features that Marek added to obtain the calibration instead of the 'brute force' method used by the Freescale tool - which I think is likely an improvement as well. I would love to see this re-submitted with Marek's suggestions and perhaps the addition of a stress test as well that could iterate over memtest loops (could be added later but I think I recall you saying you may have ported some of the memory tests from Freescale's tool to u-boot already).
Regarding Merak's imx6 auto-calibration features: I did run a test across a range of boards in a thermal chamber using nothing but auto-calibration and found many failures, but I need to go back and spend more time with that. I believe my failing there was that I the same thing you did in '03/12 imx: mx6: ddr: make calibration optional in config routines' and made passing in a null calib structure skip writing to the various registers completely and I'm thinking that if the power on default values of these registers are so off for your system that you can hang just trying to talk to the DDR in general. If I recall the boot failures I saw when testing this were all hangs in mx6_ddr3_cfg() before I even got to running mmdc_do_write_level_calibration() or mmdc_do_dqs_calibration(). I was also using an older u-boot fork that I rebased Marek's patches on top of, so its possible I was missing some relevant fix-ups in mx6_ddr3_cfg.
Regards,
Tim

Hi Tim,
On 08/26/2016 08:00 AM, Tim Harvey wrote:
On Tue, Jun 21, 2016 at 11:41 AM, Eric Nelson eric@nelint.com wrote:
This patch set makes use of the dynamic DDR calibration routines added in commit d339f16 to define an alternative to the Freescale DDR stress tester tool.
The goals of this effort are to make the process of DDR validation easier and more robust:
- The use of SPL simplifies the process of running DDR calibration through the imx_usb or SB_LOADER.exe tools (no need for JTAG) - The use of Kconfig makes the set of board-specific parameters more obvious. - The output of the SPL image can be directly pasted into either the DCD configuration file or U-Boot sources.
The notable feature of the DDR stress tool which isn't present in this virtual board file is the testing across CPU frequencies.
Patches 1-3 contain structural changes to those routines to return the calibration data and allow configuration of memory without a pre-defined set of calibration data.
Patches 4 and 5 add a struct mx6_ddr_sysinfo parameter to the calibration routines and use it to fix the use of the calibration routines on CPU variants which don't have two MMDC ports (tested on i.MX6SL).
Patches 6-7 simplify the selection of the DDR calibration routines and allow use on other CPU variants (again, tested on i.MX6SL).
Patch 8 is the meat of the patch set and defines the board itself and a single configuration sample that can be used on mx6sabresd or mx6qsabreauto.
Patches 9-12 define a set of other configurations to match other boards that I've tested during development. Note that the configurations with LPDDR (mx6slevk and WaRP) are not currently functional.
I believe that patches 1-7 are minor and can be applied after a short review, but the patch set is an RFC because of some fundamental questions:
- Is it okay with Freescale that I'm the maintainer for a board in the board/freescale tree? I don't particularly want to be the maintainer of this set of tools, but I want them, so I will try to maintain those portions that I'm in a position to test.
Fabio and Peng, do you have any comments?
- What about the board name? Though I haven't looked deeply at it, but I think that some of this code could be shared with i.MX7. - I'm including some configurations for boards that don't currently function (mx6slevk) or haven't been tested (warp) for reference and further updates. I'm also including a configuration for a board that isn't supported in main-line U-Boot (nitrogen6_max).
Eric,
I currently have the time and resources to run some large-scale tests and prove out the reliability of auto-calibration.
It looks like you never got a whole lot of feedback on this series in the general sense - probably because there are only a few of us who need to deal with imx6 ddr calibration. Where do you stand with it?
It's been lingering due to un-related client demands.
I was also hoping to get some feedback from Fabio and Peng regarding placement in the board/freescale tree and get some additional hardware to test against before V2.
I now have the hardware, but haven't put any cycles into it.
In particular, I now have a WaRP (i.MX6SL), mx6ulevk, and mx7dsabresd to test against, though the i.MX7 may require some code movement.
I love the idea of providing an easier way of obtaining calibration values and removing the dependence on the Freescale tool. This SPL would use the auto-calibration features that Marek added to obtain the calibration instead of the 'brute force' method used by the Freescale tool - which I think is likely an improvement as well. I would love to see this re-submitted with Marek's suggestions and perhaps the addition of a stress test as well that could iterate over memtest loops (could be added later but I think I recall you saying you may have ported some of the memory tests from Freescale's tool to u-boot already).
I'm glad to hear an ack of the premise. I have found the tool (virtual memory test board) useful for gathering calibration data on a handful of new board designs.
I haven't implemented a stress test, but think that could easily follow.
I think the V1 patch set also didn't define a "full" U-Boot (only SPL is buildable). Expanding that should allow U-Boot's mtest, but my experience has been that it rarely exposes issues.
The frequency-changing routines in the DDR stress tool and especially memory tests under a heavily loaded Linux have proven to be much better at detecting issues.
Regarding Marek's imx6 auto-calibration features: I did run a test across a range of boards in a thermal chamber using nothing but auto-calibration and found many failures, but I need to go back and spend more time with that. I believe my failing there was that I the same thing you did in '03/12 imx: mx6: ddr: make calibration optional in config routines' and made passing in a null calib structure skip writing to the various registers completely and I'm thinking that if the power on default values of these registers are so off for your system that you can hang just trying to talk to the DDR in general. If I recall the boot failures I saw when testing this were all hangs in mx6_ddr3_cfg() before I even got to running mmdc_do_write_level_calibration() or mmdc_do_dqs_calibration(). I was also using an older u-boot fork that I rebased Marek's patches on top of, so its possible I was missing some relevant fix-ups in mx6_ddr3_cfg.
It's hard to comment about what may be going on there.
I seem to recall having some issues if RALAT and/or WALAT are out of whack, but I don't think these should be affected by temperature.
Regards,
Eric

Hi Eric and Tim,
On Fri, Aug 26, 2016 at 12:33 PM, Eric Nelson eric@nelint.com wrote:
- Is it okay with Freescale that I'm the maintainer for a board in the board/freescale tree? I don't particularly want to be the maintainer of this set of tools, but I want them, so I will try to maintain those portions that I'm in a position to test.
Fabio and Peng, do you have any comments?
That's perfectly fine, thanks.

On Fri, Aug 26, 2016 at 8:33 AM, Eric Nelson eric@nelint.com wrote:
Hi Tim,
<snip>
It's been lingering due to un-related client demands.
I was also hoping to get some feedback from Fabio and Peng regarding placement in the board/freescale tree and get some additional hardware to test against before V2.
I now have the hardware, but haven't put any cycles into it.
In particular, I now have a WaRP (i.MX6SL), mx6ulevk, and mx7dsabresd to test against, though the i.MX7 may require some code movement.
and I have quite a variety of IMX6 base boards to test against with 16bit, 32bit, 64bit memory layouts on both IMX6DL and IMX6Q.
I love the idea of providing an easier way of obtaining calibration values and removing the dependence on the Freescale tool. This SPL would use the auto-calibration features that Marek added to obtain the calibration instead of the 'brute force' method used by the Freescale tool - which I think is likely an improvement as well. I would love to see this re-submitted with Marek's suggestions and perhaps the addition of a stress test as well that could iterate over memtest loops (could be added later but I think I recall you saying you may have ported some of the memory tests from Freescale's tool to u-boot already).
I'm glad to hear an ack of the premise. I have found the tool (virtual memory test board) useful for gathering calibration data on a handful of new board designs.
I haven't implemented a stress test, but think that could easily follow.
I think the V1 patch set also didn't define a "full" U-Boot (only SPL is buildable). Expanding that should allow U-Boot's mtest, but my experience has been that it rarely exposes issues.
I would rather see something in the SPL that ran out of IRAM.
The frequency-changing routines in the DDR stress tool and especially memory tests under a heavily loaded Linux have proven to be much better at detecting issues.
really? I've never bothered with testing at frequencies other than what gets typically configured per the CPU.
What do you use under Linux that you like? I always prefer the memory to be tested at a very low level with cache disabled.
Regarding Marek's imx6 auto-calibration features: I did run a test across a range of boards in a thermal chamber using nothing but auto-calibration and found many failures, but I need to go back and spend more time with that. I believe my failing there was that I the same thing you did in '03/12 imx: mx6: ddr: make calibration optional in config routines' and made passing in a null calib structure skip writing to the various registers completely and I'm thinking that if the power on default values of these registers are so off for your system that you can hang just trying to talk to the DDR in general. If I recall the boot failures I saw when testing this were all hangs in mx6_ddr3_cfg() before I even got to running mmdc_do_write_level_calibration() or mmdc_do_dqs_calibration(). I was also using an older u-boot fork that I rebased Marek's patches on top of, so its possible I was missing some relevant fix-ups in mx6_ddr3_cfg.
It's hard to comment about what may be going on there.
I seem to recall having some issues if RALAT and/or WALAT are out of whack, but I don't think these should be affected by temperature.
I'll hopefully start running some tests next week and won't think too hard about it unless I can show the same failures under the current mainline code.
Tim
participants (5)
-
Eric Nelson
-
Fabio Estevam
-
Marek Vasut
-
Tim Harvey
-
Tom Rini