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:
parent
d058131586
commit
b5fa9c8200
|
@ -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)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in New Issue