nb/intel/sandybridge/raminit: Fix ECC scrub

The scrubbing method was never correct nor tested.
Fix that by observations made on mrc.bin.

Tested on HP Z220 with ECC memory and Xeon E3 CPU:
The whole memory is now scrubbed.

Change-Id: Ia9fcc236fbf73f51fe944c6dda5d22ba9d334ec7
Signed-off-by: Patrick Rudolph <patrick.rudolph@9elements.com>
Reviewed-on: https://review.coreboot.org/c/coreboot/+/40721
Reviewed-by: Angel Pons <th3fanbus@gmail.com>
Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
This commit is contained in:
Patrick Rudolph 2020-05-01 18:35:05 +02:00 committed by Patrick Rudolph
parent d058131586
commit b5fa9c8200
1 changed files with 116 additions and 87 deletions

View File

@ -344,7 +344,8 @@ void dram_dimm_set_mapping(ramctr_timing *ctrl, int training)
MCHBAR32(MAD_DIMM(channel)) = ctrl->mad_dimm[channel] | ecc; MCHBAR32(MAD_DIMM(channel)) = ctrl->mad_dimm[channel] | ecc;
} }
//udelay(10); /* TODO: Might be needed for ECC configurations; so far works without. */ if (ctrl->ecc_enabled)
udelay(10);
} }
void dram_zones(ramctr_timing *ctrl, int training) void dram_zones(ramctr_timing *ctrl, int training)
@ -4260,15 +4261,35 @@ int channel_test(ramctr_timing *ctrl)
void channel_scrub(ramctr_timing *ctrl) void channel_scrub(ramctr_timing *ctrl)
{ {
int channel, slotrank, row, rowsize; 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;
}
/*
* During runtime the "scrubber" will periodically scan through the memory in the
* physical address space, to identify and fix CRC errors.
* The following loops writes to every DRAM address, setting the ECC bits to the
* correct value. A read from this location will no longer return a CRC error,
* except when a bit has toggled due to external events.
* The same could be accieved by writing to the physical memory map, but it's
* much more difficult due to SMM remapping, ME stolen memory, GFX stolen memory,
* and firmware running in x86_32.
*/
FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS { FOR_ALL_POPULATED_CHANNELS FOR_ALL_POPULATED_RANKS {
rowsize = 1 << ctrl->info.dimm[channel][slotrank >> 1].row_bits; rowsize = 1 << ctrl->info.dimm[channel][slotrank >> 1].row_bits;
for (bank = 0; bank < 8; bank++) {
for (row = 0; row < rowsize; row += 16) { for (row = 0; row < rowsize; row += 16) {
wait_for_iosav(channel); /*
* DRAM command ACT
/* DRAM command ACT */ * Opens the row for writing.
*/
{ {
u8 gap = MAX((ctrl->tFAW >> 2) + 1, ctrl->tRRD);
const struct iosav_ssq ssq = { const struct iosav_ssq ssq = {
.sp_cmd_ctrl = { .sp_cmd_ctrl = {
.command = IOSAV_ACT, .command = IOSAV_ACT,
@ -4276,15 +4297,14 @@ void channel_scrub(ramctr_timing *ctrl)
}, },
.subseq_ctrl = { .subseq_ctrl = {
.cmd_executions = 1, .cmd_executions = 1,
.cmd_delay_gap = MAX((ctrl->tFAW >> 2) + 1, .cmd_delay_gap = gap,
ctrl->tRRD),
.post_ssq_wait = ctrl->tRCD, .post_ssq_wait = ctrl->tRCD,
.data_direction = SSQ_NA, .data_direction = SSQ_NA,
}, },
.sp_cmd_addr = { .sp_cmd_addr = {
.address = row, .address = row,
.rowbits = 6, .rowbits = 6,
.bank = 0, .bank = bank,
.rank = slotrank, .rank = slotrank,
}, },
.addr_update = { .addr_update = {
@ -4295,7 +4315,11 @@ void channel_scrub(ramctr_timing *ctrl)
iosav_write_ssq(channel, &ssq); iosav_write_ssq(channel, &ssq);
} }
/* DRAM command WR */ /*
* DRAM command WR
* Writes (128 + 1) * 8 (burst length) * 8 (bus width)
* bytes.
*/
{ {
const struct iosav_ssq ssq = { const struct iosav_ssq ssq = {
.sp_cmd_ctrl = { .sp_cmd_ctrl = {
@ -4305,24 +4329,28 @@ void channel_scrub(ramctr_timing *ctrl)
.subseq_ctrl = { .subseq_ctrl = {
.cmd_executions = 129, .cmd_executions = 129,
.cmd_delay_gap = 4, .cmd_delay_gap = 4,
.post_ssq_wait = 40, .post_ssq_wait = ctrl->tWTR +
ctrl->CWL + 8,
.data_direction = SSQ_WR, .data_direction = SSQ_WR,
}, },
.sp_cmd_addr = { .sp_cmd_addr = {
.address = row, .address = row,
.rowbits = 0, .rowbits = 0,
.bank = 0, .bank = bank,
.rank = slotrank, .rank = slotrank,
}, },
.addr_update = { .addr_update = {
.inc_addr_8 = 1, .inc_addr_8 = 1,
.addr_wrap = 18, .addr_wrap = 9,
}, },
}; };
iosav_write_ssq(channel, &ssq); iosav_write_ssq(channel, &ssq);
} }
/* DRAM command PRE */ /*
* DRAM command PRE
* Closes the row.
*/
{ {
const struct iosav_ssq ssq = { const struct iosav_ssq ssq = {
.sp_cmd_ctrl = { .sp_cmd_ctrl = {
@ -4331,14 +4359,14 @@ void channel_scrub(ramctr_timing *ctrl)
}, },
.subseq_ctrl = { .subseq_ctrl = {
.cmd_executions = 1, .cmd_executions = 1,
.cmd_delay_gap = 3, .cmd_delay_gap = 4,
.post_ssq_wait = 40, .post_ssq_wait = ctrl->tRP,
.data_direction = SSQ_NA, .data_direction = SSQ_NA,
}, },
.sp_cmd_addr = { .sp_cmd_addr = {
.address = 1024, .address = 0,
.rowbits = 6, .rowbits = 6,
.bank = 0, .bank = bank,
.rank = slotrank, .rank = slotrank,
}, },
.addr_update = { .addr_update = {
@ -4348,13 +4376,14 @@ void channel_scrub(ramctr_timing *ctrl)
iosav_write_ssq(channel, &ssq); iosav_write_ssq(channel, &ssq);
} }
/* execute command queue */ /* Execute command queue */
iosav_run_once(channel); iosav_run_queue(channel, 16, 0);
wait_for_iosav(channel); wait_for_iosav(channel);
} }
} }
} }
}
void set_scrambling_seed(ramctr_timing *ctrl) void set_scrambling_seed(ramctr_timing *ctrl)
{ {