[RFC PATCH 0/3] Fix iMX6 DDR configuration issues and update toradex apalis-imx6

Dear all, we do recently have debugged some spurious boot failures and crashes, about 2% of times on specific "bad" modules", on the toradex apalis imx6 [0].
We found a couple of issues in the generic imx6 ddr configuration code and we updated the apalis-imx6 memory timing to fix those. With the following patches applied we were able to run a complete validation cycle (weeks of testing for multiple SKUs) over the whole industrial temperature range (-40 to +85) without any issues.
I'm sending this patch as RFC since we plan to run some additional validation testing SKUs from different batches before asking for this to be merged.
[0] https://lore.kernel.org/all/20211202161428.GA104937@francesco-nb.int.toradex...
Francesco Dolcini (3): mx6: ddr: Restore ralat/walat in write level calibration mx6: ddr: Wait before issuing the first MRS cmd board: apalis_imx6: DDR init using mx6_dram_cfg()
arch/arm/mach-imx/mx6/ddr.c | 10 +- board/toradex/apalis_imx6/apalis_imx6.c | 439 ++++++++++-------------- 2 files changed, 193 insertions(+), 256 deletions(-)

The current DDR write level calibration routine always overwrite the ralat/walat fields to their maximum value, just save the existing values at the beginning of the calibration routine and restore it at the end.
In case the delay is estimated by the user to be more than one cycle the walat should be configured according to that, this is not automatically done. From the i.MX6 RM:
The user should read the results of the associated delay-line at MPWLDECTRL#[WL_DL_ABS_OFFSET#] and in case the user estimates that the reasonable delay may be above 1 cycle then the user should indicate it at MPWLDECTRL#[WL_CYC_DEL#]. Moreover the user should indicate it in MDMISC[WALAT] field. For example, if the result of the write leveling calibration is 100/256 parts of a cycle, but the user estimates that the delay is above 2 cycles then MPWLDECTRL#[WL_CYC_DEL#] should be configured to 2, so the total delay will be 2 and 100/256 parts of a cycle
Probably it would just possible to not overwrite the mdmisc register in the first place, since this is not present in the write_level_calib() example in NXP AN4467 [1] nor in the i.MX6 RM (44.11.6.1 Hardware Write Leveling Calibration).
Fixes: d339f16911c7 ("arm: imx6: Add DDR3 calibration code for MX6 Q/D/DL") Signed-off-by: Francesco Dolcini francesco.dolcini@toradex.com --- arch/arm/mach-imx/mx6/ddr.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mach-imx/mx6/ddr.c b/arch/arm/mach-imx/mx6/ddr.c index f872bfdab315..08e2f0f130a6 100644 --- a/arch/arm/mach-imx/mx6/ddr.c +++ b/arch/arm/mach-imx/mx6/ddr.c @@ -108,7 +108,7 @@ 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 esdmisc_val, zq_val, mdmisc_val; u32 errors = 0; u32 ldectrl[4] = {0}; u32 ddr_mr1 = 0x4; @@ -131,6 +131,9 @@ int mmdc_do_write_level_calibration(struct mx6_ddr_sysinfo const *sysinfo) /* disable Adopt power down timer */ setbits_le32(&mmdc0->mapsr, 0x1);
+ /* Save old RALAT and WALAT values */ + mdmisc_val = readl(&mmdc0->mdmisc); + debug("Starting write leveling calibration.\n");
/* @@ -217,6 +220,9 @@ int mmdc_do_write_level_calibration(struct mx6_ddr_sysinfo const *sysinfo) writel(esdmisc_val, &mmdc0->mdref); writel(zq_val, &mmdc0->mpzqhwctrl);
+ /* restore walat/ralat */ + writel(mdmisc_val, &mmdc0->mdmisc); + debug("\tMMDC_MPWLDECTRL0 after write level cal: 0x%08x\n", readl(&mmdc0->mpwldectrl0)); debug("\tMMDC_MPWLDECTRL1 after write level cal: 0x%08x\n",

On 4/4/22 10:51, Francesco Dolcini wrote:
The current DDR write level calibration routine always overwrite the ralat/walat fields to their maximum value, just save the existing values at the beginning of the calibration routine and restore it at the end.
In case the delay is estimated by the user to be more than one cycle the walat should be configured according to that, this is not automatically done. From the i.MX6 RM:
The user should read the results of the associated delay-line at MPWLDECTRL#[WL_DL_ABS_OFFSET#] and in case the user estimates that the reasonable delay may be above 1 cycle then the user should indicate it at MPWLDECTRL#[WL_CYC_DEL#]. Moreover the user should indicate it in MDMISC[WALAT] field. For example, if the result of the write leveling calibration is 100/256 parts of a cycle, but the user estimates that the delay is above 2 cycles then MPWLDECTRL#[WL_CYC_DEL#] should be configured to 2, so the total delay will be 2 and 100/256 parts of a cycle
Probably it would just possible to not overwrite the mdmisc register in the first place, since this is not present in the write_level_calib() example in NXP AN4467 [1] nor in the i.MX6 RM (44.11.6.1 Hardware Write Leveling Calibration).
Fixes: d339f16911c7 ("arm: imx6: Add DDR3 calibration code for MX6 Q/D/DL") Signed-off-by: Francesco Dolcini francesco.dolcini@toradex.com
arch/arm/mach-imx/mx6/ddr.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/arch/arm/mach-imx/mx6/ddr.c b/arch/arm/mach-imx/mx6/ddr.c index f872bfdab315..08e2f0f130a6 100644 --- a/arch/arm/mach-imx/mx6/ddr.c +++ b/arch/arm/mach-imx/mx6/ddr.c @@ -108,7 +108,7 @@ 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 esdmisc_val, zq_val, mdmisc_val; u32 errors = 0; u32 ldectrl[4] = {0}; u32 ddr_mr1 = 0x4;
@@ -131,6 +131,9 @@ int mmdc_do_write_level_calibration(struct mx6_ddr_sysinfo const *sysinfo) /* disable Adopt power down timer */ setbits_le32(&mmdc0->mapsr, 0x1);
/* Save old RALAT and WALAT values */
mdmisc_val = readl(&mmdc0->mdmisc);
debug("Starting write leveling calibration.\n");
/*
@@ -217,6 +220,9 @@ int mmdc_do_write_level_calibration(struct mx6_ddr_sysinfo const *sysinfo) writel(esdmisc_val, &mmdc0->mdref); writel(zq_val, &mmdc0->mpzqhwctrl);
- /* restore walat/ralat */
- writel(mdmisc_val, &mmdc0->mdmisc);
- debug("\tMMDC_MPWLDECTRL0 after write level cal: 0x%08x\n", readl(&mmdc0->mpwldectrl0)); debug("\tMMDC_MPWLDECTRL1 after write level cal: 0x%08x\n",
Reviewed-by: Marek Vasut marex@denx.de

Wait 1ms before issuing the first MRS command to write DDR3 Mode registers.
There is a requirement to wait minimum of Reset CKE Exit time, tXPR, with tXPR = max(tXS, 5tCK) and to wait 500 useconds after reset is de-asserted. It seems that for some reason this is not enforced by the MMDC controller, despite MMDCx_MDOR RST_to_CKE and tXPR being correctly configured.
Without this change we experienced random memory initialization failures with about 2% boot failure rate on specific problematic boards, after this change we were able to do more than 10.000 power-cycle without a single failure.
Fixes: fe0f7f7842e1 ("mx6: add mmdc configuration for MX6Q/MX6DL") Signed-off-by: Francesco Dolcini francesco.dolcini@toradex.com --- arch/arm/mach-imx/mx6/ddr.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/arch/arm/mach-imx/mx6/ddr.c b/arch/arm/mach-imx/mx6/ddr.c index 08e2f0f130a6..7b3d620094c4 100644 --- a/arch/arm/mach-imx/mx6/ddr.c +++ b/arch/arm/mach-imx/mx6/ddr.c @@ -1526,6 +1526,8 @@ void mx6_ddr3_cfg(const struct mx6_ddr_sysinfo *sysinfo, ((sysinfo->ncs == 2) ? 1 : 0) << 30; /* SDE_1 for CS1 */
/* Step 8: Write Mode Registers to Init DDR3 devices */ + mdelay(1); /* Wait before issuing the first MRS command + (tXPR / 500us CKE delay after reset deassertion) */ for (cs = 0; cs < sysinfo->ncs; cs++) { /* MR2 */ val = (sysinfo->rtt_wr & 3) << 9 | (ddr3_cfg->SRT & 1) << 7 |

On 4/4/22 10:51, Francesco Dolcini wrote:
Wait 1ms before issuing the first MRS command to write DDR3 Mode registers.
There is a requirement to wait minimum of Reset CKE Exit time, tXPR, with tXPR = max(tXS, 5tCK) and to wait 500 useconds after reset is de-asserted. It seems that for some reason this is not enforced by the MMDC controller, despite MMDCx_MDOR RST_to_CKE and tXPR being correctly configured.
Without this change we experienced random memory initialization failures with about 2% boot failure rate on specific problematic boards, after this change we were able to do more than 10.000 power-cycle without a single failure.
Fixes: fe0f7f7842e1 ("mx6: add mmdc configuration for MX6Q/MX6DL") Signed-off-by: Francesco Dolcini francesco.dolcini@toradex.com
arch/arm/mach-imx/mx6/ddr.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/arch/arm/mach-imx/mx6/ddr.c b/arch/arm/mach-imx/mx6/ddr.c index 08e2f0f130a6..7b3d620094c4 100644 --- a/arch/arm/mach-imx/mx6/ddr.c +++ b/arch/arm/mach-imx/mx6/ddr.c @@ -1526,6 +1526,8 @@ void mx6_ddr3_cfg(const struct mx6_ddr_sysinfo *sysinfo, ((sysinfo->ncs == 2) ? 1 : 0) << 30; /* SDE_1 for CS1 */
/* Step 8: Write Mode Registers to Init DDR3 devices */
- mdelay(1); /* Wait before issuing the first MRS command
(tXPR / 500us CKE delay after reset deassertion) */
Should we infer this delay from tXPR instead ?

Hello Marek, thanks for your review.
On Mon, Apr 04, 2022 at 03:39:35PM +0200, Marek Vasut wrote:
On 4/4/22 10:51, Francesco Dolcini wrote:
Wait 1ms before issuing the first MRS command to write DDR3 Mode registers.
There is a requirement to wait minimum of Reset CKE Exit time, tXPR, with tXPR = max(tXS, 5tCK) and to wait 500 useconds after reset is de-asserted. It seems that for some reason this is not enforced by the MMDC controller, despite MMDCx_MDOR RST_to_CKE and tXPR being correctly configured.
Without this change we experienced random memory initialization failures with about 2% boot failure rate on specific problematic boards, after this change we were able to do more than 10.000 power-cycle without a single failure.
Fixes: fe0f7f7842e1 ("mx6: add mmdc configuration for MX6Q/MX6DL") Signed-off-by: Francesco Dolcini francesco.dolcini@toradex.com
arch/arm/mach-imx/mx6/ddr.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/arch/arm/mach-imx/mx6/ddr.c b/arch/arm/mach-imx/mx6/ddr.c index 08e2f0f130a6..7b3d620094c4 100644 --- a/arch/arm/mach-imx/mx6/ddr.c +++ b/arch/arm/mach-imx/mx6/ddr.c @@ -1526,6 +1526,8 @@ void mx6_ddr3_cfg(const struct mx6_ddr_sysinfo *sysinfo, ((sysinfo->ncs == 2) ? 1 : 0) << 30; /* SDE_1 for CS1 */ /* Step 8: Write Mode Registers to Init DDR3 devices */
- mdelay(1); /* Wait before issuing the first MRS command
(tXPR / 500us CKE delay after reset deassertion) */
Should we infer this delay from tXPR instead ?
I could just delay(tXPR + 500us) and do the exact worst case delay.
However I wonder if it is worth doing it, the 1ms delay works in practice, it is big enough to be correct in any case, but small enough not to be a concern on the boot time.
Please note that I do not know which timing is violated here (tXPR, the 500us after reset de-assertion or both of them).
Francesco

On 4/4/22 16:53, Francesco Dolcini wrote:
Hello Marek, thanks for your review.
On Mon, Apr 04, 2022 at 03:39:35PM +0200, Marek Vasut wrote:
On 4/4/22 10:51, Francesco Dolcini wrote:
Wait 1ms before issuing the first MRS command to write DDR3 Mode registers.
There is a requirement to wait minimum of Reset CKE Exit time, tXPR, with tXPR = max(tXS, 5tCK) and to wait 500 useconds after reset is de-asserted. It seems that for some reason this is not enforced by the MMDC controller, despite MMDCx_MDOR RST_to_CKE and tXPR being correctly configured.
Without this change we experienced random memory initialization failures with about 2% boot failure rate on specific problematic boards, after this change we were able to do more than 10.000 power-cycle without a single failure.
Fixes: fe0f7f7842e1 ("mx6: add mmdc configuration for MX6Q/MX6DL") Signed-off-by: Francesco Dolcini francesco.dolcini@toradex.com
arch/arm/mach-imx/mx6/ddr.c | 2 ++ 1 file changed, 2 insertions(+)
diff --git a/arch/arm/mach-imx/mx6/ddr.c b/arch/arm/mach-imx/mx6/ddr.c index 08e2f0f130a6..7b3d620094c4 100644 --- a/arch/arm/mach-imx/mx6/ddr.c +++ b/arch/arm/mach-imx/mx6/ddr.c @@ -1526,6 +1526,8 @@ void mx6_ddr3_cfg(const struct mx6_ddr_sysinfo *sysinfo, ((sysinfo->ncs == 2) ? 1 : 0) << 30; /* SDE_1 for CS1 */ /* Step 8: Write Mode Registers to Init DDR3 devices */
- mdelay(1); /* Wait before issuing the first MRS command
(tXPR / 500us CKE delay after reset deassertion) */
Should we infer this delay from tXPR instead ?
I could just delay(tXPR + 500us) and do the exact worst case delay.
However I wonder if it is worth doing it, the 1ms delay works in practice, it is big enough to be correct in any case, but small enough not to be a concern on the boot time.
Please note that I do not know which timing is violated here (tXPR, the 500us after reset de-assertion or both of them).
Can the tXPR ever be larger than 500us ?

On Mon, Apr 04, 2022 at 09:56:50PM +0200, Marek Vasut wrote:
On 4/4/22 16:53, Francesco Dolcini wrote:
On Mon, Apr 04, 2022 at 03:39:35PM +0200, Marek Vasut wrote:
--- a/arch/arm/mach-imx/mx6/ddr.c +++ b/arch/arm/mach-imx/mx6/ddr.c @@ -1526,6 +1526,8 @@ void mx6_ddr3_cfg(const struct mx6_ddr_sysinfo *sysinfo, ((sysinfo->ncs == 2) ? 1 : 0) << 30; /* SDE_1 for CS1 */ /* Step 8: Write Mode Registers to Init DDR3 devices */
- mdelay(1); /* Wait before issuing the first MRS command
(tXPR / 500us CKE delay after reset deassertion) */
Should we infer this delay from tXPR instead ?
I could just delay(tXPR + 500us) and do the exact worst case delay.
However I wonder if it is worth doing it, the 1ms delay works in practice, it is big enough to be correct in any case, but small enough not to be a concern on the boot time.
Please note that I do not know which timing is violated here (tXPR, the 500us after reset de-assertion or both of them).
Can the tXPR ever be larger than 500us ?
No, it can't. Max value for 8GB density is 360ns, min value 120ns for 1GB density (see JEDEC standard, but also mx6/ddr.c).
Would be fine for you to improve the commit message and code comment to make this discussion we just had transparent, while keeping the 1ms delay?
Francesco

On 4/5/22 11:09, Francesco Dolcini wrote:
On Mon, Apr 04, 2022 at 09:56:50PM +0200, Marek Vasut wrote:
On 4/4/22 16:53, Francesco Dolcini wrote:
On Mon, Apr 04, 2022 at 03:39:35PM +0200, Marek Vasut wrote:
--- a/arch/arm/mach-imx/mx6/ddr.c +++ b/arch/arm/mach-imx/mx6/ddr.c @@ -1526,6 +1526,8 @@ void mx6_ddr3_cfg(const struct mx6_ddr_sysinfo *sysinfo, ((sysinfo->ncs == 2) ? 1 : 0) << 30; /* SDE_1 for CS1 */ /* Step 8: Write Mode Registers to Init DDR3 devices */
- mdelay(1); /* Wait before issuing the first MRS command
(tXPR / 500us CKE delay after reset deassertion) */
Should we infer this delay from tXPR instead ?
I could just delay(tXPR + 500us) and do the exact worst case delay.
However I wonder if it is worth doing it, the 1ms delay works in practice, it is big enough to be correct in any case, but small enough not to be a concern on the boot time.
Please note that I do not know which timing is violated here (tXPR, the 500us after reset de-assertion or both of them).
Can the tXPR ever be larger than 500us ?
No, it can't. Max value for 8GB density is 360ns, min value 120ns for 1GB density (see JEDEC standard, but also mx6/ddr.c).
Would be fine for you to improve the commit message and code comment to make this discussion we just had transparent, while keeping the 1ms delay?
Yeah, 1ms is fine. If you do V2, add my:
Reviewed-by: Marek Vasut marex@denx.de

Do DDR initialization using the procedural mx6_dram_cfg() instead of programming the MMDC using a raw list of register/value pairs, this solves some rare boot failures on specific "bad" modules.
Calibration values, DDR geometry are unchanged, memory timings are updated according to the relevant memory datasheet.
For IT temperature range SKUs CL is decreased from 8 to 7 and tFAW value is increased, for commercial temperature range SKUs some minor changes on ODT parameters.
Signed-off-by: Francesco Dolcini francesco.dolcini@toradex.com --- board/toradex/apalis_imx6/apalis_imx6.c | 439 ++++++++++-------------- 1 file changed, 184 insertions(+), 255 deletions(-)
diff --git a/board/toradex/apalis_imx6/apalis_imx6.c b/board/toradex/apalis_imx6/apalis_imx6.c index 25a4cd9f38bb..0438f0701435 100644 --- a/board/toradex/apalis_imx6/apalis_imx6.c +++ b/board/toradex/apalis_imx6/apalis_imx6.c @@ -806,244 +806,6 @@ void ldo_mode_set(int ldo_bypass) #include "asm/arch/iomux.h" #include "asm/arch/crm_regs.h"
-static int mx6_com_dcd_table[] = { -/* ddr-setup.cfg */ -MX6_IOM_DRAM_SDQS0, 0x00000030, -MX6_IOM_DRAM_SDQS1, 0x00000030, -MX6_IOM_DRAM_SDQS2, 0x00000030, -MX6_IOM_DRAM_SDQS3, 0x00000030, -MX6_IOM_DRAM_SDQS4, 0x00000030, -MX6_IOM_DRAM_SDQS5, 0x00000030, -MX6_IOM_DRAM_SDQS6, 0x00000030, -MX6_IOM_DRAM_SDQS7, 0x00000030, - -MX6_IOM_GRP_B0DS, 0x00000030, -MX6_IOM_GRP_B1DS, 0x00000030, -MX6_IOM_GRP_B2DS, 0x00000030, -MX6_IOM_GRP_B3DS, 0x00000030, -MX6_IOM_GRP_B4DS, 0x00000030, -MX6_IOM_GRP_B5DS, 0x00000030, -MX6_IOM_GRP_B6DS, 0x00000030, -MX6_IOM_GRP_B7DS, 0x00000030, -MX6_IOM_GRP_ADDDS, 0x00000030, -/* 40 Ohm drive strength for cs0/1,sdba2,cke0/1,sdwe */ -MX6_IOM_GRP_CTLDS, 0x00000030, - -MX6_IOM_DRAM_DQM0, 0x00020030, -MX6_IOM_DRAM_DQM1, 0x00020030, -MX6_IOM_DRAM_DQM2, 0x00020030, -MX6_IOM_DRAM_DQM3, 0x00020030, -MX6_IOM_DRAM_DQM4, 0x00020030, -MX6_IOM_DRAM_DQM5, 0x00020030, -MX6_IOM_DRAM_DQM6, 0x00020030, -MX6_IOM_DRAM_DQM7, 0x00020030, - -MX6_IOM_DRAM_CAS, 0x00020030, -MX6_IOM_DRAM_RAS, 0x00020030, -MX6_IOM_DRAM_SDCLK_0, 0x00020030, -MX6_IOM_DRAM_SDCLK_1, 0x00020030, - -MX6_IOM_DRAM_RESET, 0x00020030, -MX6_IOM_DRAM_SDCKE0, 0x00003000, -MX6_IOM_DRAM_SDCKE1, 0x00003000, - -MX6_IOM_DRAM_SDODT0, 0x00003030, -MX6_IOM_DRAM_SDODT1, 0x00003030, - -/* (differential input) */ -MX6_IOM_DDRMODE_CTL, 0x00020000, -/* (differential input) */ -MX6_IOM_GRP_DDRMODE, 0x00020000, -/* disable ddr pullups */ -MX6_IOM_GRP_DDRPKE, 0x00000000, -MX6_IOM_DRAM_SDBA2, 0x00000000, -/* 40 Ohm drive strength for cs0/1,sdba2,cke0/1,sdwe */ -MX6_IOM_GRP_DDR_TYPE, 0x000C0000, - -/* Read data DQ Byte0-3 delay */ -MX6_MMDC_P0_MPRDDQBY0DL, 0x33333333, -MX6_MMDC_P0_MPRDDQBY1DL, 0x33333333, -MX6_MMDC_P0_MPRDDQBY2DL, 0x33333333, -MX6_MMDC_P0_MPRDDQBY3DL, 0x33333333, -MX6_MMDC_P1_MPRDDQBY0DL, 0x33333333, -MX6_MMDC_P1_MPRDDQBY1DL, 0x33333333, -MX6_MMDC_P1_MPRDDQBY2DL, 0x33333333, -MX6_MMDC_P1_MPRDDQBY3DL, 0x33333333, - -/* - * MDMISC mirroring interleaved (row/bank/col) - */ -MX6_MMDC_P0_MDMISC, 0x00081740, - -/* - * MDSCR con_req - */ -MX6_MMDC_P0_MDSCR, 0x00008000, - -/* 1066mhz_4x128mx16.cfg */ - -MX6_MMDC_P0_MDPDC, 0x00020036, -MX6_MMDC_P0_MDCFG0, 0x555A7954, -MX6_MMDC_P0_MDCFG1, 0xDB328F64, -MX6_MMDC_P0_MDCFG2, 0x01FF00DB, -MX6_MMDC_P0_MDRWD, 0x000026D2, -MX6_MMDC_P0_MDOR, 0x005A1023, -MX6_MMDC_P0_MDOTC, 0x09555050, -MX6_MMDC_P0_MDPDC, 0x00025576, -MX6_MMDC_P0_MDASP, 0x00000027, -MX6_MMDC_P0_MDCTL, 0x831A0000, -MX6_MMDC_P0_MDSCR, 0x04088032, -MX6_MMDC_P0_MDSCR, 0x00008033, -MX6_MMDC_P0_MDSCR, 0x00428031, -MX6_MMDC_P0_MDSCR, 0x19308030, -MX6_MMDC_P0_MDSCR, 0x04008040, -MX6_MMDC_P0_MPZQHWCTRL, 0xA1390003, -MX6_MMDC_P1_MPZQHWCTRL, 0xA1390003, -MX6_MMDC_P0_MDREF, 0x00005800, -MX6_MMDC_P0_MPODTCTRL, 0x00000000, -MX6_MMDC_P1_MPODTCTRL, 0x00000000, - -MX6_MMDC_P0_MPDGCTRL0, 0x432A0338, -MX6_MMDC_P0_MPDGCTRL1, 0x03260324, -MX6_MMDC_P1_MPDGCTRL0, 0x43340344, -MX6_MMDC_P1_MPDGCTRL1, 0x031E027C, - -MX6_MMDC_P0_MPRDDLCTL, 0x33272D2E, -MX6_MMDC_P1_MPRDDLCTL, 0x2F312B37, - -MX6_MMDC_P0_MPWRDLCTL, 0x3A35433C, -MX6_MMDC_P1_MPWRDLCTL, 0x4336453F, - -MX6_MMDC_P0_MPWLDECTRL0, 0x0009000E, -MX6_MMDC_P0_MPWLDECTRL1, 0x0018000B, -MX6_MMDC_P1_MPWLDECTRL0, 0x00060015, -MX6_MMDC_P1_MPWLDECTRL1, 0x0006000E, - -MX6_MMDC_P0_MPMUR0, 0x00000800, -MX6_MMDC_P1_MPMUR0, 0x00000800, -MX6_MMDC_P0_MDSCR, 0x00000000, -MX6_MMDC_P0_MAPSR, 0x00011006, -}; - -static int mx6_it_dcd_table[] = { -/* ddr-setup.cfg */ -MX6_IOM_DRAM_SDQS0, 0x00000030, -MX6_IOM_DRAM_SDQS1, 0x00000030, -MX6_IOM_DRAM_SDQS2, 0x00000030, -MX6_IOM_DRAM_SDQS3, 0x00000030, -MX6_IOM_DRAM_SDQS4, 0x00000030, -MX6_IOM_DRAM_SDQS5, 0x00000030, -MX6_IOM_DRAM_SDQS6, 0x00000030, -MX6_IOM_DRAM_SDQS7, 0x00000030, - -MX6_IOM_GRP_B0DS, 0x00000030, -MX6_IOM_GRP_B1DS, 0x00000030, -MX6_IOM_GRP_B2DS, 0x00000030, -MX6_IOM_GRP_B3DS, 0x00000030, -MX6_IOM_GRP_B4DS, 0x00000030, -MX6_IOM_GRP_B5DS, 0x00000030, -MX6_IOM_GRP_B6DS, 0x00000030, -MX6_IOM_GRP_B7DS, 0x00000030, -MX6_IOM_GRP_ADDDS, 0x00000030, -/* 40 Ohm drive strength for cs0/1,sdba2,cke0/1,sdwe */ -MX6_IOM_GRP_CTLDS, 0x00000030, - -MX6_IOM_DRAM_DQM0, 0x00020030, -MX6_IOM_DRAM_DQM1, 0x00020030, -MX6_IOM_DRAM_DQM2, 0x00020030, -MX6_IOM_DRAM_DQM3, 0x00020030, -MX6_IOM_DRAM_DQM4, 0x00020030, -MX6_IOM_DRAM_DQM5, 0x00020030, -MX6_IOM_DRAM_DQM6, 0x00020030, -MX6_IOM_DRAM_DQM7, 0x00020030, - -MX6_IOM_DRAM_CAS, 0x00020030, -MX6_IOM_DRAM_RAS, 0x00020030, -MX6_IOM_DRAM_SDCLK_0, 0x00020030, -MX6_IOM_DRAM_SDCLK_1, 0x00020030, - -MX6_IOM_DRAM_RESET, 0x00020030, -MX6_IOM_DRAM_SDCKE0, 0x00003000, -MX6_IOM_DRAM_SDCKE1, 0x00003000, - -MX6_IOM_DRAM_SDODT0, 0x00003030, -MX6_IOM_DRAM_SDODT1, 0x00003030, - -/* (differential input) */ -MX6_IOM_DDRMODE_CTL, 0x00020000, -/* (differential input) */ -MX6_IOM_GRP_DDRMODE, 0x00020000, -/* disable ddr pullups */ -MX6_IOM_GRP_DDRPKE, 0x00000000, -MX6_IOM_DRAM_SDBA2, 0x00000000, -/* 40 Ohm drive strength for cs0/1,sdba2,cke0/1,sdwe */ -MX6_IOM_GRP_DDR_TYPE, 0x000C0000, - -/* Read data DQ Byte0-3 delay */ -MX6_MMDC_P0_MPRDDQBY0DL, 0x33333333, -MX6_MMDC_P0_MPRDDQBY1DL, 0x33333333, -MX6_MMDC_P0_MPRDDQBY2DL, 0x33333333, -MX6_MMDC_P0_MPRDDQBY3DL, 0x33333333, -MX6_MMDC_P1_MPRDDQBY0DL, 0x33333333, -MX6_MMDC_P1_MPRDDQBY1DL, 0x33333333, -MX6_MMDC_P1_MPRDDQBY2DL, 0x33333333, -MX6_MMDC_P1_MPRDDQBY3DL, 0x33333333, - -/* - * MDMISC mirroring interleaved (row/bank/col) - */ -MX6_MMDC_P0_MDMISC, 0x00081740, - -/* - * MDSCR con_req - */ -MX6_MMDC_P0_MDSCR, 0x00008000, - -/* 1066mhz_4x256mx16.cfg */ - -MX6_MMDC_P0_MDPDC, 0x00020036, -MX6_MMDC_P0_MDCFG0, 0x898E78f5, -MX6_MMDC_P0_MDCFG1, 0xff328f64, -MX6_MMDC_P0_MDCFG2, 0x01FF00DB, -MX6_MMDC_P0_MDRWD, 0x000026D2, -MX6_MMDC_P0_MDOR, 0x008E1023, -MX6_MMDC_P0_MDOTC, 0x09444040, -MX6_MMDC_P0_MDPDC, 0x00025576, -MX6_MMDC_P0_MDASP, 0x00000047, -MX6_MMDC_P0_MDCTL, 0x841A0000, -MX6_MMDC_P0_MDSCR, 0x02888032, -MX6_MMDC_P0_MDSCR, 0x00008033, -MX6_MMDC_P0_MDSCR, 0x00048031, -MX6_MMDC_P0_MDSCR, 0x19408030, -MX6_MMDC_P0_MDSCR, 0x04008040, -MX6_MMDC_P0_MPZQHWCTRL, 0xA1390003, -MX6_MMDC_P1_MPZQHWCTRL, 0xA1390003, -MX6_MMDC_P0_MDREF, 0x00007800, -MX6_MMDC_P0_MPODTCTRL, 0x00022227, -MX6_MMDC_P1_MPODTCTRL, 0x00022227, - -MX6_MMDC_P0_MPDGCTRL0, 0x03300338, -MX6_MMDC_P0_MPDGCTRL1, 0x03240324, -MX6_MMDC_P1_MPDGCTRL0, 0x03440350, -MX6_MMDC_P1_MPDGCTRL1, 0x032C0308, - -MX6_MMDC_P0_MPRDDLCTL, 0x40363C3E, -MX6_MMDC_P1_MPRDDLCTL, 0x3C3E3C46, - -MX6_MMDC_P0_MPWRDLCTL, 0x403E463E, -MX6_MMDC_P1_MPWRDLCTL, 0x4A384C46, - -MX6_MMDC_P0_MPWLDECTRL0, 0x0009000E, -MX6_MMDC_P0_MPWLDECTRL1, 0x0018000B, -MX6_MMDC_P1_MPWLDECTRL0, 0x00060015, -MX6_MMDC_P1_MPWLDECTRL1, 0x0006000E, - -MX6_MMDC_P0_MPMUR0, 0x00000800, -MX6_MMDC_P1_MPMUR0, 0x00000800, -MX6_MMDC_P0_MDSCR, 0x00000000, -MX6_MMDC_P0_MAPSR, 0x00011006, -}; - static void ccgr_init(void) { struct mxc_ccm_reg *ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR; @@ -1068,51 +830,218 @@ static void ccgr_init(void) writel(0x000000FB, &ccm->ccosr); }
-static void ddr_init(int *table, int size) -{ - int i;
- for (i = 0; i < size / 2 ; i++) - writel(table[2 * i + 1], table[2 * i]); -} +#define PAD_CTL_INPUT_DDR BIT(17) + +struct mx6dq_iomux_ddr_regs mx6_ddr_ioregs = { + /* Differential input, 40 ohm DSE */ + .dram_sdclk_0 = PAD_CTL_DSE_40ohm | PAD_CTL_INPUT_DDR, + .dram_sdclk_1 = PAD_CTL_DSE_40ohm | PAD_CTL_INPUT_DDR, + .dram_cas = PAD_CTL_DSE_40ohm | PAD_CTL_INPUT_DDR, + .dram_ras = PAD_CTL_DSE_40ohm | PAD_CTL_INPUT_DDR, + .dram_reset = PAD_CTL_DSE_40ohm | PAD_CTL_INPUT_DDR, + + /* SDKE[0:1]: BIT(12) and BIT(13) are reserved and set at reset */ + .dram_sdcke0 = 0x00003000, + .dram_sdcke1 = 0x00003000, + + .dram_sdba2 = 0x00000000, + + /* ODT[0:1]: 40 ohm DSE, BIT(12) and BIT(13) are reserved and set at reset */ + .dram_sdodt0 = PAD_CTL_DSE_40ohm | 0x00003000, + .dram_sdodt1 = PAD_CTL_DSE_40ohm | 0x00003000, + + /* SDQS[0:7]: 40 ohm DSE, Pull/Keeper Disabled, ODT Disabled */ + .dram_sdqs0 = PAD_CTL_DSE_40ohm, + .dram_sdqs1 = PAD_CTL_DSE_40ohm, + .dram_sdqs2 = PAD_CTL_DSE_40ohm, + .dram_sdqs3 = PAD_CTL_DSE_40ohm, + .dram_sdqs4 = PAD_CTL_DSE_40ohm, + .dram_sdqs5 = PAD_CTL_DSE_40ohm, + .dram_sdqs6 = PAD_CTL_DSE_40ohm, + .dram_sdqs7 = PAD_CTL_DSE_40ohm, + + /* DQM[0:7]: Differential input, 40 ohm DSE, Pull/Keeper Disabled, ODT Disabled */ + .dram_dqm0 = PAD_CTL_DSE_40ohm | PAD_CTL_INPUT_DDR, + .dram_dqm1 = PAD_CTL_DSE_40ohm | PAD_CTL_INPUT_DDR, + .dram_dqm2 = PAD_CTL_DSE_40ohm | PAD_CTL_INPUT_DDR, + .dram_dqm3 = PAD_CTL_DSE_40ohm | PAD_CTL_INPUT_DDR, + .dram_dqm4 = PAD_CTL_DSE_40ohm | PAD_CTL_INPUT_DDR, + .dram_dqm5 = PAD_CTL_DSE_40ohm | PAD_CTL_INPUT_DDR, + .dram_dqm6 = PAD_CTL_DSE_40ohm | PAD_CTL_INPUT_DDR, + .dram_dqm7 = PAD_CTL_DSE_40ohm | PAD_CTL_INPUT_DDR, +}; + +struct mx6dq_iomux_grp_regs mx6_grp_ioregs = { + /* DDR3 */ + .grp_ddr_type = 0x000C0000, + + /* SDQS[0:7]: Differential input */ + .grp_ddrmode_ctl = PAD_CTL_INPUT_DDR, + + /* DATA[0:63]: Pull/Keeper disabled */ + .grp_ddrpke = 0, + + /* ADDR[0:16], SDBA[0:1]: 40 ohm DSE */ + .grp_addds = PAD_CTL_DSE_40ohm, + + /* CS0/CS1/SDBA2/CKE0/CKE1/SDWE: 40 ohm DSE */ + .grp_ctlds = PAD_CTL_DSE_40ohm, + + /* DATA[0:63]: Differential input */ + .grp_ddrmode = PAD_CTL_INPUT_DDR, + + /* DATA[0:63]: 40 ohm DSE */ + .grp_b0ds = PAD_CTL_DSE_40ohm, + .grp_b1ds = PAD_CTL_DSE_40ohm, + .grp_b2ds = PAD_CTL_DSE_40ohm, + .grp_b3ds = PAD_CTL_DSE_40ohm, + .grp_b4ds = PAD_CTL_DSE_40ohm, + .grp_b5ds = PAD_CTL_DSE_40ohm, + .grp_b6ds = PAD_CTL_DSE_40ohm, + .grp_b7ds = PAD_CTL_DSE_40ohm, +}; + +struct mx6_ddr_sysinfo sysinfo = { + .dsize = 2, /* width of data bus: 2=64 */ + .cs_density = 32, /* full range so that get_mem_size() works, 32Gb per CS */ + .ncs = 1, + .cs1_mirror = 0, + .rtt_wr = 2, /* Dynamic ODT, RZQ/2 */ + .rtt_nom = 2, /* RZQ/2 */ + .walat = 0, /* Write additional latency */ + .ralat = 5, /* Read additional latency */ + .mif3_mode = 3, /* Command prediction working mode */ + .bi_on = 1, /* Bank interleaving enabled */ + .sde_to_rst = 0x10, /* 14 cycles, 200us (JEDEC default) */ + .rst_to_cke = 0x23, /* 33 cycles, 500us (JEDEC default) */ + .pd_fast_exit = 1, /* enable precharge power-down fast exit */ + .ddr_type = DDR_TYPE_DDR3, + .refsel = 1, /* Refresh cycles at 32KHz */ + .refr = 3, /* 4 refresh commands per refresh cycle */ +}; + +static const struct mx6_mmdc_calibration mx6_mmdc_calib = { + .p0_mpwldectrl0 = 0x0009000E, + .p0_mpwldectrl1 = 0x0018000B, + .p1_mpwldectrl0 = 0x00060015, + .p1_mpwldectrl1 = 0x0006000E, + .p0_mpdgctrl0 = 0x432A0338, + .p0_mpdgctrl1 = 0x03260324, + .p1_mpdgctrl0 = 0x43340344, + .p1_mpdgctrl1 = 0x031E027C, + .p0_mprddlctl = 0x33272D2E, + .p1_mprddlctl = 0x2F312B37, + .p0_mpwrdlctl = 0x3A35433C, + .p1_mpwrdlctl = 0x4336453F, +}; + +static const struct mx6_ddr3_cfg ddr3_cfg = { + .mem_speed = 1066, + .density = 2, + .width = 16, + .banks = 8, + .rowaddr = 14, + .coladdr = 10, + .pagesz = 2, + .trcd = 1312, + .trcmin = 4812, + .trasmin = 3500, + .SRT = 0, +}; + +struct mx6_ddr_sysinfo sysinfo_it = { + .dsize = 2, /* width of data bus: 2=64 */ + .cs_density = 32, /* full range so that get_mem_size() works, 32Gb per CS */ + .ncs = 1, + .cs1_mirror = 0, + .rtt_wr = 1, /* Dynamic ODT, RZQ/4 */ + .rtt_nom = 1, /* RZQ/4 */ + .walat = 0, /* Write additional latency */ + .ralat = 5, /* Read additional latency */ + .mif3_mode = 3, /* Command prediction working mode */ + .bi_on = 1, /* Bank interleaving enabled */ + .sde_to_rst = 0x10, /* 14 cycles, 200us (JEDEC default) */ + .rst_to_cke = 0x23, /* 33 cycles, 500us (JEDEC default) */ + .pd_fast_exit = 1, /* enable precharge power-down fast exit */ + .ddr_type = DDR_TYPE_DDR3, + .refsel = 1, /* Refresh cycles at 32KHz */ + .refr = 7, /* 8 refresh commands per refresh cycle */ +}; + +static const struct mx6_mmdc_calibration mx6_mmdc_calib_it = { + .p0_mpwldectrl0 = 0x0009000E, + .p0_mpwldectrl1 = 0x0018000B, + .p1_mpwldectrl0 = 0x00060015, + .p1_mpwldectrl1 = 0x0006000E, + .p0_mpdgctrl0 = 0x03300338, + .p0_mpdgctrl1 = 0x03240324, + .p1_mpdgctrl0 = 0x03440350, + .p1_mpdgctrl1 = 0x032C0308, + .p0_mprddlctl = 0x40363C3E, + .p1_mprddlctl = 0x3C3E3C46, + .p0_mpwrdlctl = 0x403E463E, + .p1_mpwrdlctl = 0x4A384C46, +}; + +static const struct mx6_ddr3_cfg ddr3_cfg_it = { + .mem_speed = 1066, + .density = 4, + .width = 16, + .banks = 8, + .rowaddr = 15, + .coladdr = 10, + .pagesz = 2, + .trcd = 1312, + .trcmin = 4812, + .trasmin = 3500, + .SRT = 1, +}; +
/* Perform DDR DRAM calibration */ -static void spl_dram_perform_cal(void) +static void spl_dram_perform_cal(const struct mx6_ddr_sysinfo *ddr_sysinfo) { #ifdef CONFIG_MX6_DDRCAL int err; - struct mx6_ddr_sysinfo ddr_sysinfo = { - .dsize = 2, - };
- err = mmdc_do_write_level_calibration(&ddr_sysinfo); + err = mmdc_do_write_level_calibration(ddr_sysinfo); if (err) printf("error %d from write level calibration\n", err); - err = mmdc_do_dqs_calibration(&ddr_sysinfo); + err = mmdc_do_dqs_calibration(ddr_sysinfo); if (err) printf("error %d from dqs calibration\n", err); #endif }
+/* MMDC initialization */ static void spl_dram_init(void) { - int minc, maxc; + bool temp_grade_it;
- switch (get_cpu_temp_grade(&minc, &maxc)) { + switch (get_cpu_temp_grade(NULL, NULL)) { case TEMP_COMMERCIAL: case TEMP_EXTCOMMERCIAL: puts("Commercial temperature grade DDR3 timings.\n"); - ddr_init(mx6_com_dcd_table, ARRAY_SIZE(mx6_com_dcd_table)); + temp_grade_it = false; break; case TEMP_INDUSTRIAL: case TEMP_AUTOMOTIVE: default: puts("Industrial temperature grade DDR3 timings.\n"); - ddr_init(mx6_it_dcd_table, ARRAY_SIZE(mx6_it_dcd_table)); + temp_grade_it = true; break; }; + + mx6dq_dram_iocfg(64, &mx6_ddr_ioregs, &mx6_grp_ioregs); + + if (temp_grade_it) + mx6_dram_cfg(&sysinfo_it, &mx6_mmdc_calib_it, &ddr3_cfg_it); + else + mx6_dram_cfg(&sysinfo, &mx6_mmdc_calib, &ddr3_cfg); + udelay(100); - spl_dram_perform_cal(); + spl_dram_perform_cal(&sysinfo); }
void board_init_f(ulong dummy)
participants (2)
-
Francesco Dolcini
-
Marek Vasut