summaryrefslogtreecommitdiff
path: root/src/northbridge
diff options
context:
space:
mode:
authorAngel Pons <th3fanbus@gmail.com>2020-11-14 16:34:35 +0100
committerFelix Held <felix-coreboot@felixheld.de>2020-11-22 19:18:57 +0000
commit59996e03774e5430bd94a8a0eb51a927a1ef4eb1 (patch)
tree306b981a5220095e5c0bacaea92e65192afed894 /src/northbridge
parentd029a579badd2e71e87b84f66e5fbe87c1651bee (diff)
downloadcoreboot-59996e03774e5430bd94a8a0eb51a927a1ef4eb1.tar.xz
nb/intel/sandybridge: Use one sequence for write leveling
In order to run a write leveling test, one needs to unset the Qoff bit in MR1, then run the test, and finally set Qoff again. The current IOSAV sequence uses two subsequences to perform the test, while the other two are unused. It is possible to perform the two necessary MR1 updates in the same sequence, which can potentially improve runtime (not measured). Since `write_mrreg` is no longer used, it is necessary to handle address mirroring explicitly. This can be accomplished with the recently-added `ddr3_mirror_mrreg` function, which is also used in `write_mrreg`. Tested on Asus P8H61-M PRO, still boots. Change-Id: I65ca1aa32cdb177d2a9e27c3b02e74ac0c882794 Signed-off-by: Angel Pons <th3fanbus@gmail.com> Reviewed-on: https://review.coreboot.org/c/coreboot/+/47614 Reviewed-by: Arthur Heymans <arthur@aheymans.xyz> Tested-by: build bot (Jenkins) <no-reply@coreboot.org>
Diffstat (limited to 'src/northbridge')
-rw-r--r--src/northbridge/intel/sandybridge/raminit_common.c56
1 files changed, 48 insertions, 8 deletions
diff --git a/src/northbridge/intel/sandybridge/raminit_common.c b/src/northbridge/intel/sandybridge/raminit_common.c
index 885689c94f..92d0c4f47d 100644
--- a/src/northbridge/intel/sandybridge/raminit_common.c
+++ b/src/northbridge/intel/sandybridge/raminit_common.c
@@ -1696,15 +1696,40 @@ static void precharge(ramctr_timing *ctrl)
static void test_timB(ramctr_timing *ctrl, int channel, int slotrank)
{
- /* enable DQs on this slotrank */
- write_mrreg(ctrl, channel, slotrank, 1, make_mr1(ctrl, slotrank, channel) | 1 << 7);
+ /* First DQS/DQS# rising edge after write leveling mode is programmed */
+ const u32 tWLMRD = 40;
+
+ u32 mr1reg = make_mr1(ctrl, slotrank, channel) | 1 << 7;
+ int bank = 1;
+
+ if (ctrl->rank_mirror[channel][slotrank])
+ ddr3_mirror_mrreg(&bank, &mr1reg);
wait_for_iosav(channel);
const struct iosav_ssq sequence[] = {
- /* DRAM command NOP */
+ /* DRAM command MRS: enable DQs on this slotrank */
[0] = {
.sp_cmd_ctrl = {
+ .command = IOSAV_MRS,
+ .ranksel_ap = 1,
+ },
+ .subseq_ctrl = {
+ .cmd_executions = 1,
+ .cmd_delay_gap = 3,
+ .post_ssq_wait = tWLMRD,
+ .data_direction = SSQ_NA,
+ },
+ .sp_cmd_addr = {
+ .address = mr1reg,
+ .rowbits = 6,
+ .bank = bank,
+ .rank = slotrank,
+ },
+ },
+ /* DRAM command NOP */
+ [1] = {
+ .sp_cmd_ctrl = {
.command = IOSAV_NOP,
.ranksel_ap = 1,
},
@@ -1722,7 +1747,7 @@ static void test_timB(ramctr_timing *ctrl, int channel, int slotrank)
},
},
/* DRAM command NOP */
- [1] = {
+ [2] = {
.sp_cmd_ctrl = {
.command = IOSAV_NOP_ALT,
.ranksel_ap = 1,
@@ -1740,6 +1765,25 @@ static void test_timB(ramctr_timing *ctrl, int channel, int slotrank)
.rank = slotrank,
},
},
+ /* DRAM command MRS: disable DQs on this slotrank */
+ [3] = {
+ .sp_cmd_ctrl = {
+ .command = IOSAV_MRS,
+ .ranksel_ap = 1,
+ },
+ .subseq_ctrl = {
+ .cmd_executions = 1,
+ .cmd_delay_gap = 3,
+ .post_ssq_wait = ctrl->tMOD,
+ .data_direction = SSQ_NA,
+ },
+ .sp_cmd_addr = {
+ .address = mr1reg | 1 << 12,
+ .rowbits = 6,
+ .bank = bank,
+ .rank = slotrank,
+ },
+ },
};
iosav_write_sequence(channel, sequence, ARRAY_SIZE(sequence));
@@ -1747,10 +1791,6 @@ static void test_timB(ramctr_timing *ctrl, int channel, int slotrank)
iosav_run_once(channel);
wait_for_iosav(channel);
-
- /* disable DQs on this slotrank */
- write_mrreg(ctrl, channel, slotrank, 1,
- make_mr1(ctrl, slotrank, channel) | 1 << 12 | 1 << 7);
}
static int discover_timB(ramctr_timing *ctrl, int channel, int slotrank)