Patrick Rudolph has uploaded this change for review. ( https://review.coreboot.org/c/coreboot/+/40721 )
Change subject: nb/intel/sandybridge/raminit: Fix ECC scrub ......................................................................
nb/intel/sandybridge/raminit: Fix ECC scrub
The scrubbing method was never correct nor tested. Fix that by observations made on mrc.bin.
* Add ECC test code when DEBUG_RAM_SETUP * Move ECC srubbing after set_scrambling_seed() to be able to observe what has been cleared. * ECC scrubbing must happen after dram_dimm_set_mapping() * Move method out of try_init_dram_ddr3() * Add comments with observations made while fixing the code
Tested on HPZ220 with ECC memory and XeonE3 CPU: The whole memory is now scrubbed.
Change-Id: Ia9fcc236fbf73f51fe944c6dda5d22ba9d334ec7 Signed-off-by: Patrick Rudolph patrick.rudolph@9elements.com --- M src/northbridge/intel/sandybridge/raminit.c M src/northbridge/intel/sandybridge/raminit_common.c M src/northbridge/intel/sandybridge/raminit_native.c 3 files changed, 82 insertions(+), 34 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/21/40721/1
diff --git a/src/northbridge/intel/sandybridge/raminit.c b/src/northbridge/intel/sandybridge/raminit.c index 6c8145d..2b0cb5e 100644 --- a/src/northbridge/intel/sandybridge/raminit.c +++ b/src/northbridge/intel/sandybridge/raminit.c @@ -366,10 +366,37 @@
set_scrambling_seed(&ctrl);
+ if (!s3resume && ctrl.ecc_enabled) + channel_scrub(&ctrl); + set_normal_operation(&ctrl);
final_registers(&ctrl);
+ if (CONFIG(DEBUG_RAM_SETUP) && !s3resume && ctrl.ecc_enabled) { + /* Test ECC in normal mode! */ + + uint32_t tseg = pci_read_config32(HOST_BRIDGE, TSEGMB); + int i; + + printk(BIOS_INFO, "RAMINIT: ECC scrub test on first channel up to 0x%x\n", + tseg); + + /* Skip A-seg and test up to TSEG */ + for (i = 1; i < tseg >> 20; i++) { + for (int j = 0; j < 1 * MiB; j += 4096) { + uintptr_t addr = (i * MiB) + j; + if (read32((u32 *)addr) == 0) + continue; + + printk(BIOS_ERR, "RAMINIT: DRAM not clear at addr 0x%lx\n", + addr); + break; + } + } + printk(BIOS_INFO, "RAMINIT: ECC scrub test done.\n"); + } + /* Zone config */ dram_zones(&ctrl, 0);
diff --git a/src/northbridge/intel/sandybridge/raminit_common.c b/src/northbridge/intel/sandybridge/raminit_common.c index 087ba2b..9cbc769 100644 --- a/src/northbridge/intel/sandybridge/raminit_common.c +++ b/src/northbridge/intel/sandybridge/raminit_common.c @@ -18,8 +18,9 @@
/* FIXME: no support for 3-channel chipsets */
-/* length: [1..4] */ -#define IOSAV_RUN_ONCE(length) ((((length) - 1) << 18) | 1) +/* length: [1..4], repeat: [1..255] */ +#define IOSAV_RUN_MULTIPLE(length, repeat) ((((length) - 1) << 18) | (repeat)) +#define IOSAV_RUN_ONCE(length) IOSAV_RUN_MULTIPLE(length, 1)
static void sfence(void) { @@ -297,8 +298,14 @@ reg |= (dimmB->width / 8 - 1) << 20; }
- reg |= 1 << 21; /* Rank interleave */ - reg |= 1 << 22; /* Enhanced interleave */ + /* + * Rank interleave: Bit 16 of the physical address space sets + * the rank to use in a dual single rank DIMM configuration. + * That results in every 64KiB being interleaved between two ranks. + */ + reg |= 1 << 21; + /* Enhanced interleave */ + reg |= 1 << 22;
if ((dimmA && (dimmA->ranks > 0)) || (dimmB && (dimmB->ranks > 0))) { ctrl->mad_dimm[channel] = reg; @@ -322,7 +329,7 @@ MCHBAR32(MAD_DIMM(channel)) = ctrl->mad_dimm[channel] | ecc; }
- //udelay(10); /* TODO: Might be needed for ECC configurations; so far works without. */ + udelay(10); }
void dram_zones(ramctr_timing *ctrl, int training) @@ -2948,39 +2955,56 @@ void channel_scrub(ramctr_timing *ctrl) { int channel, slotrank, row, rowsize; + u8 bank; + + + FOR_ALL_POPULATED_CHANNELS { + wait_for_iosav(channel); + fill_pattern0(ctrl, channel, 0, 0); + MCHBAR32(IOSAV_DATA_CTL_ch(channel)) = 0; + }
FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS { - rowsize = 1 << ctrl->info.dimm[channel][slotrank >> 1].row_bits; - for (row = 0; row < rowsize; row += 16) { + rowsize = 1 << ctrl->info.dimm[channel][!!(slotrank & 0xC)].row_bits; + /* Guessed based on oberservation: + * Writes 128 * 8 bytes + * Bank bits are located at 13:15 + * Rowbits start at bit 20 + */ + for (bank = 0; bank < 8; bank ++) { + for (row = 0; row < rowsize; row += 16) {
- wait_for_iosav(channel); + /* DRAM command ACT */ + MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_ACT; + MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = + (ctrl->tRCD << 16) | + (MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10) + | 1; + MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = + row | 0x60000 | (slotrank << 24) | (bank << 20); + MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x00000241;
- /* DRAM command ACT */ - MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 0)) = IOSAV_ACT; - MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 0)) = - (MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD) << 10) - | 1 | (ctrl->tRCD << 16); - MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 0)) = - row | 0x00060000 | (slotrank << 24); - MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 0)) = 0x00000241; + /* DRAM command WR */ + MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = IOSAV_WR; + MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x08000000 | + ((ctrl->tWTR + ctrl->CWL + 8) << 16) | (4 << 10) | 0x81; + MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = row | + (slotrank << 24) | (bank << 20); + MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x00000122;
- /* DRAM command WR */ - MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 1)) = IOSAV_WR; - MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 1)) = 0x08281081; - MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 1)) = row | (slotrank << 24); - MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 1)) = 0x00000242; + /* DRAM command PRE */ + MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = IOSAV_PRE; + MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x00000000 | + (ctrl->tRP << 16) | (4 << 10) | 1; + MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = 0x60000 | + (slotrank << 24) | (bank << 20) | 0x400; + MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x00000240;
- /* DRAM command PRE */ - MCHBAR32(IOSAV_n_SP_CMD_CTRL_ch(channel, 2)) = IOSAV_PRE; - MCHBAR32(IOSAV_n_SUBSEQ_CTRL_ch(channel, 2)) = 0x00280c01; - MCHBAR32(IOSAV_n_SP_CMD_ADDR_ch(channel, 2)) = - 0x00060400 | (slotrank << 24); - MCHBAR32(IOSAV_n_ADDR_UPDATE_ch(channel, 2)) = 0x00000240; + /* execute command queue */ + MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_MULTIPLE(3, 16);
- /* execute command queue */ - MCHBAR32(IOSAV_SEQ_CTL_ch(channel)) = IOSAV_RUN_ONCE(3); - - wait_for_iosav(channel); + wait_for_iosav(channel); + } } } } diff --git a/src/northbridge/intel/sandybridge/raminit_native.c b/src/northbridge/intel/sandybridge/raminit_native.c index 832391f..34299a3 100644 --- a/src/northbridge/intel/sandybridge/raminit_native.c +++ b/src/northbridge/intel/sandybridge/raminit_native.c @@ -685,9 +685,6 @@ err = channel_test(ctrl); if (err) return err; - - if (ctrl->ecc_enabled) - channel_scrub(ctrl); }
/* Set MAD-DIMM registers */