[PATCH v2 1/4] board: bsh: imx6ulz_smm_m2: Match SPL DDR settings to DCD table
Michael Nazzareno Trimarchi
michael at amarulasolutions.com
Tue May 20 19:07:13 CEST 2025
Hi Dario
On Tue, May 20, 2025 at 5:07 PM Dario Binacchi
<dario.binacchi at amarulasolutions.com> wrote:
>
> From: Michael Trimarchi <michael at amarulasolutions.com>
>
> When using SPL on i.mx6 we frequently notice some DDR initialization
> mismatches between the SPL code and the non-SPL code.
>
> As the non-SPL code have been tested for long time and proves to be
> reliable, let's configure the DDR in the exact same way as the non-SPL
> case.
>
> The idea is simple: just use the DCD table and write directly to the DDR
> registers.
>
> Signed-off-by: Michael Trimarchi <michael at amarulasolutions.com>
> Signed-off-by: Dario Binacchi <dario.binacchi at amarulasolutions.com>
>
> ---
>
> Changes in v2:
> - Add 'static struct' globally in the module where the definitions
> can mabe static.
> - Use standard C comment style
>
> arch/arm/include/asm/arch-mx6/mx6-ddr.h | 2 +
> arch/arm/mach-imx/mx6/ddr.c | 3 +
> board/bsh/imx6ulz_smm_m2/spl.c | 260 ++++++++++++++++++------
> 3 files changed, 203 insertions(+), 62 deletions(-)
>
> diff --git a/arch/arm/include/asm/arch-mx6/mx6-ddr.h b/arch/arm/include/asm/arch-mx6/mx6-ddr.h
> index ad9c1ac906a3..bd3ff65bcd96 100644
> --- a/arch/arm/include/asm/arch-mx6/mx6-ddr.h
> +++ b/arch/arm/include/asm/arch-mx6/mx6-ddr.h
> @@ -457,6 +457,8 @@ struct mx6_mmdc_calibration {
> u32 p1_mpwrdlctl;
> /* lpddr2 zq hw calibration */
> u32 mpzqlp2ctl;
> + /* MDC Duty Cycle Control Register */
> + u32 mpdccr;
> };
>
> /* configure iomux (pinctl/padctl) */
> diff --git a/arch/arm/mach-imx/mx6/ddr.c b/arch/arm/mach-imx/mx6/ddr.c
> index 5a1258e002d2..749ceee0cdbf 100644
> --- a/arch/arm/mach-imx/mx6/ddr.c
> +++ b/arch/arm/mach-imx/mx6/ddr.c
> @@ -1444,6 +1444,9 @@ void mx6_ddr3_cfg(const struct mx6_ddr_sysinfo *sysinfo,
> mmdc0->mpdgctrl1 = calib->p0_mpdgctrl1;
> mmdc0->mprddlctl = calib->p0_mprddlctl;
> mmdc0->mpwrdlctl = calib->p0_mpwrdlctl;
> + if (calib->mpdccr)
> + mmdc0->mpdccr = calib->mpdccr;
> +
This should be in this patch? Don't think so
Michael
> if (sysinfo->dsize > 1) {
> MMDC1(mpwldectrl0, calib->p1_mpwldectrl0);
> MMDC1(mpwldectrl1, calib->p1_mpwldectrl1);
> diff --git a/board/bsh/imx6ulz_smm_m2/spl.c b/board/bsh/imx6ulz_smm_m2/spl.c
> index 724841b57456..936e54662e98 100644
> --- a/board/bsh/imx6ulz_smm_m2/spl.c
> +++ b/board/bsh/imx6ulz_smm_m2/spl.c
> @@ -31,70 +31,209 @@ static void setup_iomux_uart(void)
> imx_iomux_v3_setup_multiple_pads(uart4_pads, ARRAY_SIZE(uart4_pads));
> }
>
> -static struct mx6ul_iomux_grp_regs mx6_grp_ioregs = {
> - .grp_addds = 0x00000028,
> - .grp_ddrmode_ctl = 0x00020000,
> - .grp_b0ds = 0x00000028,
> - .grp_ctlds = 0x00000028,
> - .grp_b1ds = 0x00000028,
> - .grp_ddrpke = 0x00000000,
> - .grp_ddrmode = 0x00020000,
> - .grp_ddr_type = 0x000c0000,
> +struct dram_cfg_param {
> + unsigned int reg;
> + unsigned int val;
> };
>
> -static struct mx6ul_iomux_ddr_regs mx6_ddr_ioregs = {
> - .dram_dqm0 = 0x00000028,
> - .dram_dqm1 = 0x00000028,
> - .dram_ras = 0x00000028,
> - .dram_cas = 0x00000028,
> - .dram_odt0 = 0x00000028,
> - .dram_odt1 = 0x00000028,
> - .dram_sdba2 = 0x00000000,
> - .dram_sdclk_0 = 0x00000028,
> - .dram_sdqs0 = 0x00000028,
> - .dram_sdqs1 = 0x00000028,
> - .dram_reset = 0x000c0028,
> +struct dram_timing_info {
> + const struct dram_cfg_param *ddrc_cfg;
> + unsigned int ddrc_cfg_num;
> };
>
> -static struct mx6_mmdc_calibration mx6_mmcd_calib = {
> - .p0_mpwldectrl0 = 0x00000000,
> - .p0_mpwldectrl1 = 0x00100010,
> - .p0_mpdgctrl0 = 0x414c014c,
> - .p0_mpdgctrl1 = 0x00000000,
> - .p0_mprddlctl = 0x40403a42,
> - .p0_mpwrdlctl = 0x4040342e,
> -};
> +static const struct dram_cfg_param ddr_ddrc_cfg_128mb[] = {
> + /* IOMUX */
> +
> + /* DDR IO Type: */
> + {0x020e04b4, 0x000C0000}, /* IOMUXC_SW_PAD_CTL_GRP_DDR_TYPE */
> + {0x020e04ac, 0x00000000}, /* IOMUXC_SW_PAD_CTL_GRP_DDRPKE */
> +
> + /* Clock: */
> + {0x020e027c, 0x00000028}, /* IOMUXC_SW_PAD_CTL_PAD_DRAM_SDCLK_0 */
> +
> + /* Address: */
> + {0x020e0250, 0x00000028}, /* IOMUXC_SW_PAD_CTL_PAD_DRAM_CAS */
> + {0x020e024c, 0x00000028}, /* IOMUXC_SW_PAD_CTL_PAD_DRAM_RAS */
> + {0x020e0490, 0x00000028}, /* IOMUXC_SW_PAD_CTL_GRP_ADDDS */
> +
> + /* Control: */
> + {0x020e0288, 0x000C0028}, /* IOMUXC_SW_PAD_CTL_PAD_DRAM_RESET */
> +
> + {0x020e0270, 0x00000000}, /*
> + * IOMUXC_SW_PAD_CTL_PAD_DRAM_SDBA2 - DSE can be configured
> + * using Group Control Register: IOMUXC_SW_PAD_CTL_GRP_CTLDS
> + */
> +
> + {0x020e0260, 0x00000028}, /* IOMUXC_SW_PAD_CTL_PAD_DRAM_SDODT0 */
> + {0x020e0264, 0x00000028}, /* IOMUXC_SW_PAD_CTL_PAD_DRAM_SDODT1 */
> + {0x020e04a0, 0x00000028}, /* IOMUXC_SW_PAD_CTL_GRP_CTLDS */
> +
> + /* Data Strobes: */
> + {0x020e0494, 0x00020000}, /* IOMUXC_SW_PAD_CTL_GRP_DDRMODE_CTL */
> + {0x020e0280, 0x00000028}, /* IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS0 */
> + {0x020e0284, 0x00000028}, /* IOMUXC_SW_PAD_CTL_PAD_DRAM_SDQS1 */
> +
> + /* Data: */
> + {0x020e04b0, 0x00020000}, /* IOMUXC_SW_PAD_CTL_GRP_DDRMODE */
> + {0x020e0498, 0x00000028}, /* IOMUXC_SW_PAD_CTL_GRP_B0DS */
> + {0x020e04a4, 0x00000028}, /* IOMUXC_SW_PAD_CTL_GRP_B1DS */
> +
> + {0x020e0244, 0x00000028}, /* IOMUXC_SW_PAD_CTL_PAD_DRAM_DQM0 */
> + {0x020e0248, 0x00000028}, /* IOMUXC_SW_PAD_CTL_PAD_DRAM_DQM1 */
> +
> + /*
> + * =============================================================================
> + * DDR Controller Registers
> + * =============================================================================
> + * Manufacturer:ISSI
> + * Device Part Number:IS43TR16640BL-125JBLI
> + * Clock Freq.: 400MHz
> + * Density per CS in Gb: 1
> + * Chip Selects used:1
> + * Number of Banks:8
> + * Row address: 13
> + * Column address: 10
> + * Data bus width16
> + * =============================================================================
> + */
> +
> + {0x021b001c, 0x00008000}, /*
> + * MMDC0_MDSCR, set the Configuration request bit during
> + * MMDC set up
> + */
> +
> + /*
> + * =============================================================================
> + * Calibration setup
> + * =============================================================================
> + */
> +
> + {0x021b0800, 0xA1390003}, /*
> + * DDR_PHY_P0_MPZQHWCTRL, enable both one-time & periodic
> + * HW ZQ calibration.
> + */
> +
> + /*
> + * For target board, may need to run write leveling calibration to fine tune these
> + * settings.
> + */
> + {0x021b080c, 0x00000000},
> +
> + /* Read DQS Gating calibration */
> + {0x021b083c, 0x41480148}, /* MPDGCTRL0 PHY0 */
> +
> + /* Read calibration */
> + {0x021b0848, 0x40403A3E}, /* MPRDDLCTL PHY0 */
> +
> + /* Write calibration */
> + {0x021b0850, 0x4040362E}, /* MPWRDLCTL PHY0 */
> +
> + /*
> + * Read data bit delay: 3 is the recommended default value, although out of reset
> + * value is 0
> + */
> + {0x021b081c, 0x33333333}, /* MMDC_MPRDDQBY0DL */
> + {0x021b0820, 0x33333333}, /* MMDC_MPRDDQBY1DL */
> +
> + /* Write data bit delay: */
> + {0x021b082c, 0xF3333333}, /* MMDC_MPWRDQBY0DL */
> + {0x021b0830, 0xF3333333}, /* MMDC_MPWRDQBY1DL */
> +
> + /* DQS&CLK Duty Cycle */
> + {0x021b08c0, 0x00944009}, /* [MMDC_MPDCCR] MMDC Duty Cycle Control Register */
> +
> + /* Complete calibration by forced measurement: */
> + {0x021b08b8, 0x00000800}, /* DDR_PHY_P0_MPMUR0, frc_msr */
>
> -static struct mx6_ddr_sysinfo ddr_sysinfo = {
> - .dsize = 0,
> - .cs1_mirror = 0,
> - .cs_density = 32,
> - .ncs = 1,
> - .bi_on = 1,
> - .rtt_nom = 1,
> - .rtt_wr = 0,
> - .ralat = 5,
> - .walat = 1,
> - .mif3_mode = 3,
> - .rst_to_cke = 0x23, /* 33 cycles (JEDEC value for DDR3) - total of 500 us */
> - .sde_to_rst = 0x10, /* 14 cycles (JEDEC value for DDR3) - total of 200 us */
> - .refsel = 1,
> - .refr = 3,
> + /*
> + * =============================================================================
> + * Calibration setup end
> + * =============================================================================
> + */
> +
> + /* MMDC init: */
> + {0x021b0004, 0x0002002D}, /* MMDC0_MDPDC */
> + {0x021b0008, 0x1B333030}, /* MMDC0_MDOTC */
> + {0x021b000c, 0x2B2F52F3}, /* MMDC0_MDCFG0 */
> + {0x021b0010, 0xB66D0B63}, /* MMDC0_MDCFG1 */
> + {0x021b0014, 0x01FF00DB}, /* MMDC0_MDCFG2 */
> +
> + /*
> + * MDMISC: RALAT kept to the high level of 5.
> + * MDMISC: consider reducing RALAT if your 528MHz board design allow that.
> + * Lower RALAT benefits:
> + * a. better operation at low frequency, for LPDDR2 freq < 100MHz, change RALAT to 3
> + * b. Small performance improvement
> + */
> + {0x021b0018, 0x00211740}, /* MMDC0_MDMISC */
> + {0x021b001c, 0x00008000}, /*
> + * MMDC0_MDSCR, set the Configuration request
> + * bit during MMDC set up
> + */
> + {0x021b002c, 0x000026D2}, /* MMDC0_MDRWD */
> + {0x021b0030, 0x002F1023}, /* MMDC0_MDOR */
> + {0x021b0040, 0x00000043}, /* Chan0 CS0_END */
> + {0x021b0000, 0x82180000}, /* MMDC0_MDCTL */
> +
> + {0x021b0890, 0x00400000}, /* MPPDCMPR2 */
> +
> + /* Mode register writes */
> + {0x021b001c, 0x02808032}, /* MMDC0_MDSCR, MR2 write, CS0 */
> + {0x021b001c, 0x00008033}, /* MMDC0_MDSCR, MR3 write, CS0 */
> + {0x021b001c, 0x00048031}, /* MMDC0_MDSCR, MR1 write, CS0 */
> + {0x021b001c, 0x15208030}, /* MMDC0_MDSCR, MR0write, CS0 */
> + {0x021b001c, 0x04008040}, /*
> + * MMDC0_MDSCR, ZQ calibration command sent to
> + * device on CS0
> + */
> + {0x021b0020, 0x00007800}, /* MMDC0_MDREF */
> +
> + {0x021b0818, 0x00000227}, /* DDR_PHY_P0_MPODTCTRL */
> +
> + {0x021b0004, 0x0002552D}, /* MMDC0_MDPDC now SDCTL power down enabled */
> +
> + {0x021b0404, 0x00011006}, /*
> + * MMDC0_MAPSR ADOPT power down enabled,
> + * MMDC will enter automatically to self-refresh
> + * while the number of idle cycle reached.
> + */
> +
> + {0x021b001c, 0x00000000}, /*
> + * MMDC0_MDSCR, clear this register
> + * (especially the configuration bit as initialization
> + * is complete)
> + */
> };
>
> -static struct mx6_ddr3_cfg mem_ddr = {
> - .mem_speed = 1333,
> - .density = 2,
> - .width = 16,
> - .banks = 8,
> - .rowaddr = 13,
> - .coladdr = 10,
> - .pagesz = 2,
> - .trcd = 1350,
> - .trcmin = 4950,
> - .trasmin = 3600,
> +static struct dram_timing_info dram_timing_128mb = {
> + .ddrc_cfg = ddr_ddrc_cfg_128mb,
> + .ddrc_cfg_num = ARRAY_SIZE(ddr_ddrc_cfg_128mb),
> };
>
> +static void ddr_cfg_write(const struct dram_timing_info *dram_timing_info)
> +{
> + int i;
> + const struct dram_cfg_param *ddrc_cfg = dram_timing_info->ddrc_cfg;
> + const int ddrc_cfg_num = dram_timing_info->ddrc_cfg_num;
> +
> + for (i = 0; i < ddrc_cfg_num; i++) {
> + debug("Writing 0x%x to register 0x%x\n", ddrc_cfg->val,
> + ddrc_cfg->reg);
> + writel(ddrc_cfg->val, ddrc_cfg->reg);
> + ddrc_cfg++;
> + }
> +}
> +
> +static void spl_dram_init(void)
> +{
> + struct mmdc_p_regs *mmdc0 = (struct mmdc_p_regs *)MMDC_P0_BASE_ADDR;
> +
> + clrbits_le32(&mmdc0->mdctl, 1 << 31); /* clear SDE_0 */
> + clrbits_le32(&mmdc0->mdctl, 1 << 30); /* clear SDE_1 */
> +
> + ddr_cfg_write(&dram_timing_128mb);
> +}
> +
> static void ccgr_init(void)
> {
> struct mxc_ccm_reg *ccm = (struct mxc_ccm_reg *)CCM_BASE_ADDR;
> @@ -108,20 +247,17 @@ static void ccgr_init(void)
> writel(0xFFFFFFFF, &ccm->CCGR6);
> }
>
> -static void imx6ul_spl_dram_cfg(void)
> -{
> - mx6ul_dram_iocfg(mem_ddr.width, &mx6_ddr_ioregs, &mx6_grp_ioregs);
> - mx6_dram_cfg(&ddr_sysinfo, &mx6_mmcd_calib, &mem_ddr);
> -}
> -
> void board_init_f(ulong dummy)
> {
> ccgr_init();
> +
> + /* DDR initialization */
> + spl_dram_init();
> +
> arch_cpu_init();
> timer_init();
> setup_iomux_uart();
> preloader_console_init();
> - imx6ul_spl_dram_cfg();
> }
>
> void reset_cpu(void)
> --
> 2.43.0
>
> base-commit: eeb5ff1a468b2303efa9deb2483b5edbebb568c6
> branch: bsh-202505-RAM
--
Michael Nazzareno Trimarchi
Co-Founder & Chief Executive Officer
M. +39 347 913 2170
michael at amarulasolutions.com
__________________________________
Amarula Solutions BV
Joop Geesinkweg 125, 1114 AB, Amsterdam, NL
T. +31 (0)85 111 9172
info at amarulasolutions.com
www.amarulasolutions.com
More information about the U-Boot
mailing list