[U-Boot] [PATCH 1/5] arm: mvebu: sdram: Enable ECC support on Armada XP

This is tested on the DB-MV784MP-GP eval board. To really enable ECC support on this board the I2C EEPROM needs to get changed. As it saves the enabling of ECC support internally. For this the following commands can be used to enable ECC support on this board:
Its recommended for first save (print) the value(s) in this EEPROM address:
=> i2c md 4e 0.1 2 0000: 05 00 ..
To enable ECC support you need to set bit 1 in the 2nd byte:
Marvell>> i2c mw 4e 1.1 02 Marvell>> i2c md 4e 0.1 2 0000: 05 02 ..
To disable ECC support again, please use this command:
Marvell>> i2c mw 4e 1.1 00 Marvell>> i2c md 4e 0.1 2 0000: 05 00 ..
On other AXP boards, simply plugging an ECC DIMM should be enough to enable ECC support.
Signed-off-by: Stefan Roese sr@denx.de Cc: Luka Perkov luka.perkov@sartura.hr --- drivers/ddr/marvell/axp/ddr3_axp_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/ddr/marvell/axp/ddr3_axp_config.h b/drivers/ddr/marvell/axp/ddr3_axp_config.h index 800d2d1..a672044 100644 --- a/drivers/ddr/marvell/axp/ddr3_axp_config.h +++ b/drivers/ddr/marvell/axp/ddr3_axp_config.h @@ -44,7 +44,7 @@ * DDR3_TRAINING_DEBUG - Debug prints of internal code */ #define DDR_TARGET_FABRIC 5 -#define DRAM_ECC 0 +#define DRAM_ECC 1
#ifdef MV_DDR_32BIT #define BUS_WIDTH 32

This patch adds "(ECC enabled)" or "(ECC disabled)" to the DRAM bootup text. Making it easier for board with SPD DIMM's to see, if ECC is enabled or not.
Signed-off-by: Stefan Roese sr@denx.de Cc: Luka Perkov luka.perkov@sartura.hr --- arch/arm/mach-mvebu/dram.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+)
diff --git a/arch/arm/mach-mvebu/dram.c b/arch/arm/mach-mvebu/dram.c index 7a5840c..600dc09 100644 --- a/arch/arm/mach-mvebu/dram.c +++ b/arch/arm/mach-mvebu/dram.c @@ -12,6 +12,13 @@ #include <asm/arch/cpu.h> #include <asm/arch/soc.h>
+#ifdef CONFIG_SYS_MVEBU_DDR_A38X +#include "../../../drivers/ddr/marvell/a38x/ddr3_init.h" +#endif +#ifdef CONFIG_SYS_MVEBU_DDR_AXP +#include "../../../drivers/ddr/marvell/axp/ddr3_init.h" +#endif + DECLARE_GLOBAL_DATA_PTR;
struct sdram_bank { @@ -140,3 +147,15 @@ void dram_init_banksize(void) { dram_init(); } + +void board_add_ram_info(int use_default) +{ + u32 reg; + + reg = reg_read(REG_SDRAM_CONFIG_ADDR); + if (reg & (1 << REG_SDRAM_CONFIG_ECC_OFFS)) + printf(" (ECC"); + else + printf(" (ECC not"); + printf(" enabled)"); +}

This patch moves CONFIG_SYS_TEXT_BASE to 0x00800000 for all Armada XP / 38x boards in mainline U-Boot. This is done in preparation for the ECC SDRAM scrubbing that needs to be done in the main U-Boot. The SPL (previously bin_hdr) has already scrubbed the area: 0x0000.0000 - 0x0100.0000
In this area this main U-Boot needs to get loaded. The main U-Boot then can scrub the remaining SDRAM area while running from this location.
Signed-off-by: Stefan Roese sr@denx.de Cc: Luka Perkov luka.perkov@sartura.hr --- include/configs/db-88f6820-gp.h | 7 ++++++- include/configs/db-mv784mp-gp.h | 7 ++++++- include/configs/maxbcm.h | 7 ++++++- 3 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/include/configs/db-88f6820-gp.h b/include/configs/db-88f6820-gp.h index 739c2bf..7c32edb 100644 --- a/include/configs/db-88f6820-gp.h +++ b/include/configs/db-88f6820-gp.h @@ -20,7 +20,12 @@ #define CONFIG_SYS_GENERIC_BOARD #define CONFIG_DISPLAY_BOARDINFO_LATE
-#define CONFIG_SYS_TEXT_BASE 0x04000000 +/* + * TEXT_BASE needs to be below 16MiB, since this area is scrubbed + * for DDR ECC byte filling in the SPL before loading the main + * U-Boot into it. + */ +#define CONFIG_SYS_TEXT_BASE 0x00800000 #define CONFIG_SYS_TCLK 250000000 /* 250MHz */
/* diff --git a/include/configs/db-mv784mp-gp.h b/include/configs/db-mv784mp-gp.h index 9ea8cff..2b177d7 100644 --- a/include/configs/db-mv784mp-gp.h +++ b/include/configs/db-mv784mp-gp.h @@ -17,7 +17,12 @@ #define CONFIG_SYS_GENERIC_BOARD #define CONFIG_DISPLAY_BOARDINFO_LATE
-#define CONFIG_SYS_TEXT_BASE 0x04000000 +/* + * TEXT_BASE needs to be below 16MiB, since this area is scrubbed + * for DDR ECC byte filling in the SPL before loading the main + * U-Boot into it. + */ +#define CONFIG_SYS_TEXT_BASE 0x00800000 #define CONFIG_SYS_TCLK 250000000 /* 250MHz */
/* diff --git a/include/configs/maxbcm.h b/include/configs/maxbcm.h index 0fb117f..3530a26 100644 --- a/include/configs/maxbcm.h +++ b/include/configs/maxbcm.h @@ -15,7 +15,12 @@ #define CONFIG_SYS_GENERIC_BOARD #define CONFIG_DISPLAY_BOARDINFO_LATE
-#define CONFIG_SYS_TEXT_BASE 0x04000000 +/* + * TEXT_BASE needs to be below 16MiB, since this area is scrubbed + * for DDR ECC byte filling in the SPL before loading the main + * U-Boot into it. + */ +#define CONFIG_SYS_TEXT_BASE 0x00800000 #define CONFIG_SYS_TCLK 250000000 /* 250MHz */
/*

Rework these functions so that dram_init_banksize() does not call dram_init() again. It only needs to set the banksize values in the bdinfo struct.
Make sure to also clip the size of the last bank if it exceeds the maximum allowed value of 3 GiB (0xc000.0000). Otherwise other address windows (e.g. PCIe) will overlap with this memory window.
Signed-off-by: Stefan Roese sr@denx.de Cc: Luka Perkov luka.perkov@sartura.hr --- arch/arm/mach-mvebu/dram.c | 30 +++++++++++++++++++++--------- 1 file changed, 21 insertions(+), 9 deletions(-)
diff --git a/arch/arm/mach-mvebu/dram.c b/arch/arm/mach-mvebu/dram.c index 600dc09..a75ada3 100644 --- a/arch/arm/mach-mvebu/dram.c +++ b/arch/arm/mach-mvebu/dram.c @@ -35,6 +35,8 @@ struct sdram_addr_dec { #define REG_CPUCS_WIN_WIN0_CS(x) (((x) & 0x3) << 2) #define REG_CPUCS_WIN_SIZE(x) (((x) & 0xff) << 24)
+#define SDRAM_SIZE_MAX 0xc0000000 + /* * mvebu_sdram_bar - reads SDRAM Base Address Register */ @@ -102,29 +104,26 @@ void mvebu_sdram_size_adjust(enum memory_bank bank)
int dram_init(void) { + u64 size = 0; int i;
- gd->ram_size = 0; for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { - gd->bd->bi_dram[i].start = mvebu_sdram_bar(i); - gd->bd->bi_dram[i].size = mvebu_sdram_bs(i); /* * It is assumed that all memory banks are consecutive * and without gaps. * If the gap is found, ram_size will be reported for * consecutive memory only */ - if (gd->bd->bi_dram[i].start != gd->ram_size) + if (mvebu_sdram_bar(i) != size) break;
/* * Don't report more than 3GiB of SDRAM, otherwise there is no * address space left for the internal registers etc. */ - if ((gd->ram_size + gd->bd->bi_dram[i].size != 0) && - (gd->ram_size + gd->bd->bi_dram[i].size <= (3 << 30))) - gd->ram_size += gd->bd->bi_dram[i].size; - + size += mvebu_sdram_bs(i); + if (size > SDRAM_SIZE_MAX) + size = SDRAM_SIZE_MAX; }
for (; i < CONFIG_NR_DRAM_BANKS; i++) { @@ -136,6 +135,8 @@ int dram_init(void) gd->bd->bi_dram[i].size = 0; }
+ gd->ram_size = size; + return 0; }
@@ -145,7 +146,18 @@ int dram_init(void) */ void dram_init_banksize(void) { - dram_init(); + u64 size = 0; + int i; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + gd->bd->bi_dram[i].start = mvebu_sdram_bar(i); + gd->bd->bi_dram[i].size = mvebu_sdram_bs(i); + + /* Clip the banksize to 1GiB if it exceeds the max size */ + size += gd->bd->bi_dram[i].size; + if (size > SDRAM_SIZE_MAX) + mvebu_sdram_bs_set(i, 0x40000000); + } }
void board_add_ram_info(int use_default)

This patch introduces the SDRAM scrubbing for ECC enabled board to fill/initialize the ECC bytes. This is done via the XOR engine to speed up the process. The scrubbing is a 2-stage process:
1) SPL scrubs the area 0 - 0x100.0000 (16MiB) for the main U-Boot 2) U-Boot scrubs the remaining SDRAM area(s)
Signed-off-by: Stefan Roese sr@denx.de Cc: Luka Perkov luka.perkov@sartura.hr --- arch/arm/mach-mvebu/Makefile | 4 ++ arch/arm/mach-mvebu/dram.c | 130 ++++++++++++++++++++++++++++++++++++++++-- drivers/ddr/marvell/axp/xor.c | 3 +- drivers/ddr/marvell/axp/xor.h | 1 + 4 files changed, 130 insertions(+), 8 deletions(-)
diff --git a/arch/arm/mach-mvebu/Makefile b/arch/arm/mach-mvebu/Makefile index 446ce04..21c56a4 100644 --- a/arch/arm/mach-mvebu/Makefile +++ b/arch/arm/mach-mvebu/Makefile @@ -14,6 +14,10 @@ else
obj-y = cpu.o obj-y += dram.o +ifndef CONFIG_SPL_BUILD +obj-$(CONFIG_SYS_MVEBU_DDR_A38X) += ../../../drivers/ddr/marvell/a38x/xor.o +obj-$(CONFIG_SYS_MVEBU_DDR_AXP) += ../../../drivers/ddr/marvell/axp/xor.o +endif obj-y += gpio.o obj-y += mbus.o obj-y += timer.o diff --git a/arch/arm/mach-mvebu/dram.c b/arch/arm/mach-mvebu/dram.c index a75ada3..1daffce 100644 --- a/arch/arm/mach-mvebu/dram.c +++ b/arch/arm/mach-mvebu/dram.c @@ -13,10 +13,12 @@ #include <asm/arch/soc.h>
#ifdef CONFIG_SYS_MVEBU_DDR_A38X -#include "../../../drivers/ddr/marvell/a38x/ddr3_init.h" +#include "../../../drivers/ddr/marvell/axp/xor.h" +#include "../../../drivers/ddr/marvell/axp/xor_regs.h" #endif #ifdef CONFIG_SYS_MVEBU_DDR_AXP -#include "../../../drivers/ddr/marvell/axp/ddr3_init.h" +#include "../../../drivers/ddr/marvell/axp/xor.h" +#include "../../../drivers/ddr/marvell/axp/xor_regs.h" #endif
DECLARE_GLOBAL_DATA_PTR; @@ -37,6 +39,18 @@ struct sdram_addr_dec {
#define SDRAM_SIZE_MAX 0xc0000000
+#define SCRUB_MAGIC 0xbeefdead + +#define SCRB_XOR_UNIT 0 +#define SCRB_XOR_CHAN 1 +#define SCRB_XOR_WIN 0 + +#define XEBARX_BASE_OFFS 16 + +static u32 xor_ctrl_save; +static u32 xor_base_save; +static u32 xor_mask_save; + /* * mvebu_sdram_bar - reads SDRAM Base Address Register */ @@ -102,6 +116,109 @@ void mvebu_sdram_size_adjust(enum memory_bank bank) mvebu_sdram_bs_set(bank, size); }
+static void mv_xor_init2(u32 cs) +{ + u32 reg, base, size, base2; + u32 bank_attr[4] = { 0xe00, 0xd00, 0xb00, 0x700 }; + + xor_ctrl_save = reg_read(XOR_WINDOW_CTRL_REG(SCRB_XOR_UNIT, + SCRB_XOR_CHAN)); + xor_base_save = reg_read(XOR_BASE_ADDR_REG(SCRB_XOR_UNIT, + SCRB_XOR_WIN)); + xor_mask_save = reg_read(XOR_SIZE_MASK_REG(SCRB_XOR_UNIT, + SCRB_XOR_WIN)); + + /* Enable Window x for each CS */ + reg = 0x1; + reg |= (0x3 << 16); + reg_write(XOR_WINDOW_CTRL_REG(SCRB_XOR_UNIT, SCRB_XOR_CHAN), reg); + + base = 0; + size = mvebu_sdram_bs(cs) - 1; + if (size) { + base2 = ((base / (64 << 10)) << XEBARX_BASE_OFFS) | + bank_attr[cs]; + reg_write(XOR_BASE_ADDR_REG(SCRB_XOR_UNIT, SCRB_XOR_WIN), + base2); + + base += size + 1; + size = (size / (64 << 10)) << 16; + /* Window x - size - 256 MB */ + reg_write(XOR_SIZE_MASK_REG(SCRB_XOR_UNIT, SCRB_XOR_WIN), size); + } + + mv_xor_hal_init(0); + + return; +} + +static void mv_xor_finish2(void) +{ + reg_write(XOR_WINDOW_CTRL_REG(SCRB_XOR_UNIT, SCRB_XOR_CHAN), + xor_ctrl_save); + reg_write(XOR_BASE_ADDR_REG(SCRB_XOR_UNIT, SCRB_XOR_WIN), + xor_base_save); + reg_write(XOR_SIZE_MASK_REG(SCRB_XOR_UNIT, SCRB_XOR_WIN), + xor_mask_save); +} + +static void dram_ecc_scrubbing(void) +{ + int cs; + u32 size, temp; + u32 total_mem = 0; + u64 total; + u32 start_addr; + + /* + * The DDR training code from the bin_hdr / SPL already + * scrubbed the DDR till 0x1000000. And the main U-Boot + * is loaded to an address < 0x1000000. So we need to + * skip this range to not re-scrub this area again. + */ + temp = reg_read(REG_SDRAM_CONFIG_ADDR); + temp |= (1 << REG_SDRAM_CONFIG_IERR_OFFS); + reg_write(REG_SDRAM_CONFIG_ADDR, temp); + + for (cs = 0; cs < CONFIG_NR_DRAM_BANKS; cs++) { + size = mvebu_sdram_bs(cs) - 1; + if (size == 0) + continue; + + total = (u64)size + 1; + total_mem += (u32)(total / (1 << 30)); + start_addr = 0; + mv_xor_init2(cs); + + /* Skip first 16 MiB */ + if (0 == cs) { + start_addr = 0x1000000; + size -= start_addr; + } + + mv_xor_mem_init(SCRB_XOR_CHAN, start_addr, size, + SCRUB_MAGIC, SCRUB_MAGIC); + + /* Wait for previous transfer completion */ + while (mv_xor_state_get(SCRB_XOR_CHAN) != MV_IDLE) + ; + + mv_xor_finish2(); + } + + temp = reg_read(REG_SDRAM_CONFIG_ADDR); + temp &= ~(1 << REG_SDRAM_CONFIG_IERR_OFFS); + reg_write(REG_SDRAM_CONFIG_ADDR, temp); +} + +static int ecc_enabled(void) +{ + if (reg_read(REG_SDRAM_CONFIG_ADDR) & (1 << REG_SDRAM_CONFIG_ECC_OFFS)) + return 1; + + return 0; +} + int dram_init(void) { u64 size = 0; @@ -135,6 +252,10 @@ int dram_init(void) gd->bd->bi_dram[i].size = 0; }
+ + if (ecc_enabled()) + dram_ecc_scrubbing(); + gd->ram_size = size;
return 0; @@ -162,10 +283,7 @@ void dram_init_banksize(void)
void board_add_ram_info(int use_default) { - u32 reg; - - reg = reg_read(REG_SDRAM_CONFIG_ADDR); - if (reg & (1 << REG_SDRAM_CONFIG_ECC_OFFS)) + if (ecc_enabled()) printf(" (ECC"); else printf(" (ECC not"); diff --git a/drivers/ddr/marvell/axp/xor.c b/drivers/ddr/marvell/axp/xor.c index 66c96ae..54924ca 100644 --- a/drivers/ddr/marvell/axp/xor.c +++ b/drivers/ddr/marvell/axp/xor.c @@ -18,7 +18,6 @@ static u32 xor_regs_ctrl_backup; static u32 xor_regs_base_backup[MAX_CS]; static u32 xor_regs_mask_backup[MAX_CS];
-static void mv_xor_hal_init(u32 chan_num); static int mv_xor_cmd_set(u32 chan, int command); static int mv_xor_ctrl_set(u32 chan, u32 xor_ctrl);
@@ -110,7 +109,7 @@ void mv_sys_xor_finish(void) * RETURN: * MV_BAD_PARAM if parameters to function invalid, MV_OK otherwise. */ -static void mv_xor_hal_init(u32 chan_num) +void mv_xor_hal_init(u32 chan_num) { u32 i;
diff --git a/drivers/ddr/marvell/axp/xor.h b/drivers/ddr/marvell/axp/xor.h index 3536487..3ff784d 100644 --- a/drivers/ddr/marvell/axp/xor.h +++ b/drivers/ddr/marvell/axp/xor.h @@ -60,6 +60,7 @@ struct crc_dma_desc { u32 src_addr1; /* Mode: Source Block address pointer */ } __packed;
+void mv_xor_hal_init(u32 chan_num); int mv_xor_state_get(u32 chan); void mv_sys_xor_init(MV_DRAM_INFO *dram_info); void mv_sys_xor_finish(void);
participants (1)
-
Stefan Roese