This fixes issues of wrong DRAM size calculation for imx8mq-evk, phytec-som-imx8mq, zii-imx8mq-dev, i.MX8MN-EVK with LPDDR4 as well as an out-of-tree i.MX8MM board I have. Ahmad Fatoum (5): ARM: i.MX8M: refactor to prepare i.MX8MN LPDDR4 support ARM: i.MX8M: esdctl: fix LPDDR4 size calculation for nano ARM: i.MX8M: esdctl: ignore ADDRMAP8 for non-DDR4 ARM: i.MX8MQ: initialize ADDRMAP7 ddr: imx8m: workaround old spreadsheets not initializing ADDRMAP7 arch/arm/boards/nxp-imx8mq-evk/ddr_init.c | 3 +- arch/arm/boards/phytec-som-imx8mq/ddr_init.c | 1 + arch/arm/boards/zii-imx8mq-dev/ddr_init.c | 3 +- arch/arm/mach-imx/esdctl.c | 71 ++++++++++++++------ drivers/ddr/imx8m/ddr_init.c | 18 +++++ drivers/ddr/imx8m/helper.c | 6 ++ include/soc/imx8m/ddr.h | 1 + 7 files changed, 82 insertions(+), 21 deletions(-) -- 2.30.2
Device config has no effect on i.MX7 and (i.MX8M && LPDDR4). In these configuration, it's assumed to be 4, which isn't correct for the i.MX8MN, which has a 16-bit bus. Instead of complicating the function further, allow it to assume device config is always correct and have the callers worry about fixing it up as necessary. No functional change. Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de> --- v1 -> v2: - new patch --- arch/arm/mach-imx/esdctl.c | 50 +++++++++++++++++++++++++------------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/arch/arm/mach-imx/esdctl.c b/arch/arm/mach-imx/esdctl.c index b070ebc62a45..69f4f8df9391 100644 --- a/arch/arm/mach-imx/esdctl.c +++ b/arch/arm/mach-imx/esdctl.c @@ -370,9 +370,8 @@ static resource_size_t imx_ddrc_sdram_size(void __iomem *ddrc, const u32 addrmap[DDRC_ADDRMAP_LENGTH], u8 col_max, const u8 col_b[], unsigned int col_b_num, u8 row_max, const u8 row_b[], unsigned int row_b_num, - bool reduced_adress_space, bool is_imx8) + bool reduced_adress_space, unsigned int mstr) { - const u32 mstr = readl(ddrc + DDRC_MSTR); unsigned int banks, ranks, columns, rows, active_ranks, width; resource_size_t size; @@ -393,11 +392,13 @@ imx_ddrc_sdram_size(void __iomem *ddrc, const u32 addrmap[DDRC_ADDRMAP_LENGTH], BUG(); } - /* Bus width in bytes, 0 means half byte or 4-bit mode */ - if (is_imx8 && !(mstr & DDRC_MSTR_LPDDR4)) - width = (1 << FIELD_GET(DDRC_MSTR_DEVICE_CONFIG, mstr)) >> 1; - else - width = 4; + /* + * mstr is ignored for some SoCs/RAM types and may yield wrong + * results when used for calculation. Callers of this function + * are expected to fix it up as necessary. + * Bus width in bytes, 0 means half byte or 4-bit mode + */ + width = (1 << FIELD_GET(DDRC_MSTR_DEVICE_CONFIG, mstr)) >> 1; switch (FIELD_GET(DDRC_MSTR_DATA_BUS_WIDTH, mstr)) { case 0b00: /* Full DQ bus */ @@ -446,7 +447,13 @@ imx_ddrc_sdram_size(void __iomem *ddrc, const u32 addrmap[DDRC_ADDRMAP_LENGTH], return reduced_adress_space ? size * 3 / 4 : size; } -static resource_size_t imx8m_ddrc_sdram_size(void __iomem *ddrc) +static void imx_ddrc_set_mstr_device_config(u32 *mstr, unsigned bits) +{ + *mstr &= ~DDRC_MSTR_DEVICE_CONFIG; + *mstr |= FIELD_PREP(DDRC_MSTR_DEVICE_CONFIG, fls(bits / 8)); +} + +static resource_size_t imx8m_ddrc_sdram_size(void __iomem *ddrc, unsigned buswidth) { const u32 addrmap[DDRC_ADDRMAP_LENGTH] = { readl(ddrc + DDRC_ADDRMAP(0)), @@ -485,17 +492,22 @@ static resource_size_t imx8m_ddrc_sdram_size(void __iomem *ddrc) }; const bool reduced_adress_space = FIELD_GET(DDRC_ADDRMAP6_LPDDR4_6GB_12GB_24GB, addrmap[6]); + u32 mstr = readl(ddrc + DDRC_MSTR); + + /* Device config is ignored and taken as 32-bit for LPDDR4 */ + if (mstr & DDRC_MSTR_LPDDR4) + imx_ddrc_set_mstr_device_config(&mstr, buswidth); return imx_ddrc_sdram_size(ddrc, addrmap, 12, ARRAY_AND_SIZE(col_b), 18, ARRAY_AND_SIZE(row_b), - reduced_adress_space, true); + reduced_adress_space, mstr); } static int imx8m_ddrc_add_mem(void *mmdcbase, struct imx_esdctl_data *data) { return arm_add_mem_device("ram0", data->base0, - imx8m_ddrc_sdram_size(mmdcbase)); + imx8m_ddrc_sdram_size(mmdcbase, 32)); } static resource_size_t imx7d_ddrc_sdram_size(void __iomem *ddrc) @@ -527,11 +539,15 @@ static resource_size_t imx7d_ddrc_sdram_size(void __iomem *ddrc) }; const bool reduced_adress_space = FIELD_GET(DDRC_ADDRMAP6_LPDDR3_6GB_12GB, addrmap[6]); + u32 mstr = readl(ddrc + DDRC_MSTR); + + /* Device config is unused on i.MX7, so rewrite it as 32-bit wide */ + imx_ddrc_set_mstr_device_config(&mstr, 32); return imx_ddrc_sdram_size(ddrc, addrmap, 11, ARRAY_AND_SIZE(col_b), 15, ARRAY_AND_SIZE(row_b), - reduced_adress_space, false); + reduced_adress_space, mstr); } static int imx7d_ddrc_add_mem(void *mmdcbase, struct imx_esdctl_data *data) @@ -890,11 +906,11 @@ void __noreturn vf610_barebox_entry(void *boarddata) boarddata); } -static void __noreturn imx8m_barebox_entry(void *boarddata) +static void __noreturn imx8m_barebox_entry(void *boarddata, unsigned buswidth) { resource_size_t size; - size = imx8m_ddrc_sdram_size(IOMEM(MX8M_DDRC_CTL_BASE_ADDR)); + size = imx8m_ddrc_sdram_size(IOMEM(MX8M_DDRC_CTL_BASE_ADDR), buswidth); /* * We artificially limit detected memory size to force malloc * pool placement to be within 4GiB address space, so as to @@ -910,22 +926,22 @@ static void __noreturn imx8m_barebox_entry(void *boarddata) void __noreturn imx8mm_barebox_entry(void *boarddata) { - imx8m_barebox_entry(boarddata); + imx8m_barebox_entry(boarddata, 32); } void __noreturn imx8mn_barebox_entry(void *boarddata) { - imx8m_barebox_entry(boarddata); + imx8m_barebox_entry(boarddata, 32); } void __noreturn imx8mp_barebox_entry(void *boarddata) { - imx8m_barebox_entry(boarddata); + imx8m_barebox_entry(boarddata, 32); } void __noreturn imx8mq_barebox_entry(void *boarddata) { - imx8m_barebox_entry(boarddata); + imx8m_barebox_entry(boarddata, 32); } void __noreturn imx7d_barebox_entry(void *boarddata) -- 2.30.2
i.MX8MN has a bus width of 16-bit, not 32-bit, so adjust accordingly. This fixes RAM size computation of i.MX8MN boards with LPDDR4 memory. Suggested-by: Lucas Stach <l.stach@pengutronix.de> Fixes: 7a05a7626d7f ("ARM: i.MX: add i.MX8MN (Nano) SoC support boilerplate") Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de> --- v1 -> v2: - new patch, suggested by Lucas. Replaces hacky last patch from v1 --- arch/arm/mach-imx/esdctl.c | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/arch/arm/mach-imx/esdctl.c b/arch/arm/mach-imx/esdctl.c index 69f4f8df9391..35c9fa9a4248 100644 --- a/arch/arm/mach-imx/esdctl.c +++ b/arch/arm/mach-imx/esdctl.c @@ -510,6 +510,12 @@ static int imx8m_ddrc_add_mem(void *mmdcbase, struct imx_esdctl_data *data) imx8m_ddrc_sdram_size(mmdcbase, 32)); } +static int imx8mn_ddrc_add_mem(void *mmdcbase, struct imx_esdctl_data *data) +{ + return arm_add_mem_device("ram0", data->base0, + imx8m_ddrc_sdram_size(mmdcbase, 16)); +} + static resource_size_t imx7d_ddrc_sdram_size(void __iomem *ddrc) { const u32 addrmap[DDRC_ADDRMAP_LENGTH] = { @@ -645,6 +651,11 @@ static __maybe_unused struct imx_esdctl_data imx8mq_data = { .add_mem = imx8m_ddrc_add_mem, }; +static __maybe_unused struct imx_esdctl_data imx8mn_data = { + .base0 = MX8M_DDR_CSD1_BASE_ADDR, + .add_mem = imx8mn_ddrc_add_mem, +}; + static __maybe_unused struct imx_esdctl_data imx7d_data = { .base0 = MX7_DDR_BASE_ADDR, .add_mem = imx7d_ddrc_add_mem, @@ -719,7 +730,7 @@ static __maybe_unused struct of_device_id imx_esdctl_dt_ids[] = { .data = &imx8mq_data }, { .compatible = "fsl,imx8mn-ddrc", - .data = &imx8mq_data + .data = &imx8mn_data }, { .compatible = "fsl,imx8mq-ddrc", .data = &imx8mq_data @@ -931,7 +942,7 @@ void __noreturn imx8mm_barebox_entry(void *boarddata) void __noreturn imx8mn_barebox_entry(void *boarddata) { - imx8m_barebox_entry(boarddata, 32); + imx8m_barebox_entry(boarddata, 16); } void __noreturn imx8mp_barebox_entry(void *boarddata) -- 2.30.2
ADDRMAP8 handling was added for i.MX8MN DDR4 handling, but as done currently has a few issues: - Bank groups are a DDR4 feature (well, borrowed from GDDR5), so we should just skip it for other RAM types - addrmap[8] == 0 is actually a valid value according to both reference manual and spreadsheet - Spreadsheet claims DDRC_ADDRMAP8_BG_B0 to be 6-bit, while reference manual claims 5-bit - Spreadsheet claims DDRC_ADDRMAP8_BG_B0 == 63 to be the neutral value. The code assumes 31 and the reference manual describes all values 0-31 to have an effect. This commit fixes the first two issues. The calculation may still be wrong, but at least for the i.MX8MN-DDR4-EVK it seems to return a correct value of 2G. Fixes: 42d45ef380c5 ("ARM: imx: Add imx8 support for SDRAM with two or more bank groups") Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de> --- v1 -> v2: - remove is_imx8 && in condition. The bit is reserved on i.MX7 and 0 by default and the variable has been replaced in a previous patch --- arch/arm/mach-imx/esdctl.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/arch/arm/mach-imx/esdctl.c b/arch/arm/mach-imx/esdctl.c index 35c9fa9a4248..a55ee06b8346 100644 --- a/arch/arm/mach-imx/esdctl.c +++ b/arch/arm/mach-imx/esdctl.c @@ -317,6 +317,7 @@ static int vf610_ddrmc_add_mem(void *mmdcbase, struct imx_esdctl_data *data) #define DDRC_ADDRMAP0_CS_BIT0 GENMASK(4, 0) #define DDRC_MSTR 0x0000 +#define DDRC_MSTR_DDR4 BIT(4) #define DDRC_MSTR_LPDDR4 BIT(5) #define DDRC_MSTR_DATA_BUS_WIDTH GENMASK(13, 12) #define DDRC_MSTR_ACTIVE_RANKS GENMASK(27, 24) @@ -424,7 +425,12 @@ imx_ddrc_sdram_size(void __iomem *ddrc, const u32 addrmap[DDRC_ADDRMAP_LENGTH], if (FIELD_GET(DDRC_ADDRMAP1_BANK_B2, addrmap[1]) != 0b11111) banks++; - if (addrmap[8]) { + if (mstr & DDRC_MSTR_DDR4) { + /* FIXME: DDR register spreasheet claims this to be + * 6-bit and 63 meaning bank group address bit 0 is 0, + * but reference manual claims 5-bit without 'neutral' value + * See MX8M_Mini_DDR4_RPA_v17, MX8M_Nano_DDR4_RPA_v8 + */ if (FIELD_GET(DDRC_ADDRMAP8_BG_B0, addrmap[8]) != 0b11111) banks++; if (FIELD_GET(DDRC_ADDRMAP8_BG_B1, addrmap[8]) != 0b111111) -- 2.30.2
Older NXP DDR spreadsheets don't initialize ADDRMAP7, leaving it at its POR default of zero. Now that barebox looks at ADDRMAP7 to be able to correctly detect bigger memory sizes, barebox proper on boards with older spreadsheets may read back 4x times as much RAM as actually fitted. MNT Reform LPDDR4 setup already writes 0xf0f (the neutral ignore-me value for the register) into ADDRMAP7. Follow suit for the other i.MX8MQ boards that don't. In-tree Non-i.MX8MQ boards aren't affected. Out of tree boards might and will get a common workaround in a follow-up commit. No workaround for out of tree i.MX8MQ boards. Tested on i.MX8M-EVK (i.MX8MQuad), where now 3G are correctly detected instead of 12G. Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de> --- v1 -> v2: - no change --- arch/arm/boards/nxp-imx8mq-evk/ddr_init.c | 3 ++- arch/arm/boards/phytec-som-imx8mq/ddr_init.c | 1 + arch/arm/boards/zii-imx8mq-dev/ddr_init.c | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/arch/arm/boards/nxp-imx8mq-evk/ddr_init.c b/arch/arm/boards/nxp-imx8mq-evk/ddr_init.c index 39addea97320..b1f752c4cb20 100644 --- a/arch/arm/boards/nxp-imx8mq-evk/ddr_init.c +++ b/arch/arm/boards/nxp-imx8mq-evk/ddr_init.c @@ -81,6 +81,7 @@ void ddr_init(void) reg32_write(0x3d400200,0x15); reg32_write(0x3d40020c,0x0); reg32_write(0x3d400210,0x1f1f); + reg32_write(0x3d40021c,0xf0f); reg32_write(0x3d400204,0x80808); reg32_write(0x3d400214,0x7070707); reg32_write(0x3d400218,0x48080707); @@ -222,4 +223,4 @@ void ddr_init(void) /* enable DDR auto-refresh mode */ tmp = reg32_read(DDRC_RFSHCTL3(0)) & ~0x1; reg32_write(DDRC_RFSHCTL3(0), tmp); -} \ No newline at end of file +} diff --git a/arch/arm/boards/phytec-som-imx8mq/ddr_init.c b/arch/arm/boards/phytec-som-imx8mq/ddr_init.c index aa327d3fb0cb..c6812e3efaec 100644 --- a/arch/arm/boards/phytec-som-imx8mq/ddr_init.c +++ b/arch/arm/boards/phytec-som-imx8mq/ddr_init.c @@ -84,6 +84,7 @@ void ddr_init(void) reg32_write(0x3d400204,0x80808); reg32_write(0x3d400214,0x7070707); reg32_write(0x3d400218,0xf070707); + reg32_write(0x3d40021c,0xf0f); reg32_write(0x3d402020,0x1); reg32_write(0x3d402024,0x518b00); reg32_write(0x3d402050,0x20d040); diff --git a/arch/arm/boards/zii-imx8mq-dev/ddr_init.c b/arch/arm/boards/zii-imx8mq-dev/ddr_init.c index 7a955193fd7c..902d0ee3cd6e 100644 --- a/arch/arm/boards/zii-imx8mq-dev/ddr_init.c +++ b/arch/arm/boards/zii-imx8mq-dev/ddr_init.c @@ -81,6 +81,7 @@ void ddr_init(void) reg32_write(0x3d400200,0x17); reg32_write(0x3d40020c,0x0); reg32_write(0x3d400210,0x1f1f); + reg32_write(0x3d40021c,0xf0f); reg32_write(0x3d400204,0x80808); reg32_write(0x3d400214,0x7070707); reg32_write(0x3d400218,0x7070707); @@ -222,4 +223,4 @@ void ddr_init(void) /* enable DDR auto-refresh mode */ tmp = reg32_read(DDRC_RFSHCTL3(0)) & ~0x1; reg32_write(DDRC_RFSHCTL3(0), tmp); -} \ No newline at end of file +} -- 2.30.2
Older NXP DDR spreadsheets don't initialize ADDRMAP7, leaving it at its POR default of zero. Now that barebox looks at ADDRMAP7 to be able to correctly detect bigger memory sizes, barebox proper on out-of-tree boards with older spreadsheets may read back 4x times as much RAM as actually fitted. Work around this by writing a trailing 0xf0f (the neutral ignore-me value for the register) if the register wasn't written through dram_timing_info::ddrc_cfg. We consider this safe to do, because the DDRC is held in reset while these values are programmed. Fixes: dad2b5636bd8 ("ARM: imx: Add imx8 support for 18 bit SDRAM row size handle") Fixes: 6cf197fa61f9 ("arm: imx: mmdc_size: Increase row_max for imx8m") Tested-by: Teresa Remmet <t.remmet@phytec.de> Signed-off-by: Ahmad Fatoum <a.fatoum@pengutronix.de> --- v1 -> v2: - added Teresa's Tested-by --- drivers/ddr/imx8m/ddr_init.c | 18 ++++++++++++++++++ drivers/ddr/imx8m/helper.c | 6 ++++++ include/soc/imx8m/ddr.h | 1 + 3 files changed, 25 insertions(+) diff --git a/drivers/ddr/imx8m/ddr_init.c b/drivers/ddr/imx8m/ddr_init.c index ae05b136229c..9a4b4e2ca88a 100644 --- a/drivers/ddr/imx8m/ddr_init.c +++ b/drivers/ddr/imx8m/ddr_init.c @@ -13,14 +13,32 @@ #include <mach/imx8m-regs.h> #include <mach/imx8m-ccm-regs.h> +bool imx8m_ddr_old_spreadsheet = true; + static void ddr_cfg_umctl2(struct dram_cfg_param *ddrc_cfg, int num) { int i = 0; for (i = 0; i < num; i++) { + if (ddrc_cfg->reg == DDRC_ADDRMAP7(0)) + imx8m_ddr_old_spreadsheet = false; reg32_write((unsigned long)ddrc_cfg->reg, ddrc_cfg->val); ddrc_cfg++; } + + /* + * Older NXP DDR configuration spreadsheets don't initialize ADDRMAP7, + * which falsifies the memory size read back from the controller + * in barebox proper. + */ + if (imx8m_ddr_old_spreadsheet) { + pr_warn("Working around old spreadsheet. Please regenerate\n"); + /* + * Alternatively, stick { DDRC_ADDRMAP7(0), 0xf0f } into + * struct dram_timing_info::ddrc_cfg of your old timing file + */ + reg32_write(DDRC_ADDRMAP7(0), 0xf0f); + } } /* diff --git a/drivers/ddr/imx8m/helper.c b/drivers/ddr/imx8m/helper.c index 94bbb811576d..98e40849584b 100644 --- a/drivers/ddr/imx8m/helper.c +++ b/drivers/ddr/imx8m/helper.c @@ -62,6 +62,12 @@ void dram_config_save(struct dram_timing_info *timing_info, cfg++; } + if (imx8m_ddr_old_spreadsheet) { + cfg->reg = DDRC_ADDRMAP7(0); + cfg->val = 0xf0f; + cfg++; + } + /* save ddrphy config */ saved_timing->ddrphy_cfg = cfg; for (i = 0; i < timing_info->ddrphy_cfg_num; i++) { diff --git a/include/soc/imx8m/ddr.h b/include/soc/imx8m/ddr.h index 9ae7cb877686..147a7d499aaf 100644 --- a/include/soc/imx8m/ddr.h +++ b/include/soc/imx8m/ddr.h @@ -407,6 +407,7 @@ static inline void reg32setbit(unsigned long addr, u32 bit) #define dwc_ddrphy_apb_rd(addr) \ reg32_read(IOMEM(IP2APB_DDRPHY_IPS_BASE_ADDR(0)) + 4 * (addr)) +extern bool imx8m_ddr_old_spreadsheet; extern struct dram_cfg_param ddrphy_trained_csr[]; extern uint32_t ddrphy_trained_csr_num; -- 2.30.2
On Thu, Jun 23, 2022 at 03:07:12PM +0200, Ahmad Fatoum wrote: > This fixes issues of wrong DRAM size calculation for imx8mq-evk, > phytec-som-imx8mq, zii-imx8mq-dev, i.MX8MN-EVK with LPDDR4 as well > as an out-of-tree i.MX8MM board I have. > > Ahmad Fatoum (5): > ARM: i.MX8M: refactor to prepare i.MX8MN LPDDR4 support > ARM: i.MX8M: esdctl: fix LPDDR4 size calculation for nano > ARM: i.MX8M: esdctl: ignore ADDRMAP8 for non-DDR4 > ARM: i.MX8MQ: initialize ADDRMAP7 > ddr: imx8m: workaround old spreadsheets not initializing ADDRMAP7 Applied, thanks Sascha > > arch/arm/boards/nxp-imx8mq-evk/ddr_init.c | 3 +- > arch/arm/boards/phytec-som-imx8mq/ddr_init.c | 1 + > arch/arm/boards/zii-imx8mq-dev/ddr_init.c | 3 +- > arch/arm/mach-imx/esdctl.c | 71 ++++++++++++++------ > drivers/ddr/imx8m/ddr_init.c | 18 +++++ > drivers/ddr/imx8m/helper.c | 6 ++ > include/soc/imx8m/ddr.h | 1 + > 7 files changed, 82 insertions(+), 21 deletions(-) > > -- > 2.30.2 > > > -- Pengutronix e.K. | | Steuerwalder Str. 21 | http://www.pengutronix.de/ | 31137 Hildesheim, Germany | Phone: +49-5121-206917-0 | Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |