[coreboot-gerrit] Change in coreboot[master]: [WIP]nb/intel/x4x/raminit: Add write leveling
Arthur Heymans (Code Review)
gerrit at coreboot.org
Wed May 24 22:10:38 CEST 2017
Arthur Heymans has uploaded a new change for review. ( https://review.coreboot.org/19878 )
Change subject: [WIP]nb/intel/x4x/raminit: Add write leveling
......................................................................
[WIP]nb/intel/x4x/raminit: Add write leveling
Does not work!!
Change-Id: Ibfbaa235bc4eb08e9345321b851e880390a624e8
Signed-off-by: Arthur Heymans <arthur at aheymans.xyz>
---
M src/northbridge/intel/x4x/Makefile.inc
A src/northbridge/intel/x4x/dq_dqsl_dll.c
M src/northbridge/intel/x4x/raminit_ddr23.c
M src/northbridge/intel/x4x/raminit_tables.c
M src/northbridge/intel/x4x/spd_ddr3_decode.c
5 files changed, 507 insertions(+), 2 deletions(-)
git pull ssh://review.coreboot.org:29418/coreboot refs/changes/78/19878/1
diff --git a/src/northbridge/intel/x4x/Makefile.inc b/src/northbridge/intel/x4x/Makefile.inc
index 8b59dbf..72f2c0e 100644
--- a/src/northbridge/intel/x4x/Makefile.inc
+++ b/src/northbridge/intel/x4x/Makefile.inc
@@ -22,6 +22,7 @@
romstage-y += ram_calc.c
romstage-y += spd_ddr2_decode.c
romstage-y += spd_ddr3_decode.c
+romstage-y += write_level.c
romstage-y += raminit_tables.c
ramstage-y += acpi.c
diff --git a/src/northbridge/intel/x4x/dq_dqsl_dll.c b/src/northbridge/intel/x4x/dq_dqsl_dll.c
new file mode 100644
index 0000000..a1dea68
--- /dev/null
+++ b/src/northbridge/intel/x4x/dq_dqsl_dll.c
@@ -0,0 +1,439 @@
+/*
+ * This file is part of the coreboot project.
+ *
+ * Copyright (C) 2017 Arthur Heymans <arthur at aheymans.xyz>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <stdint.h>
+#include <string.h>
+#include <console/console.h>
+#include <arch/io.h>
+#include <halt.h>
+#include <delay.h>
+#include "x4x.h"
+#include "iomap.h"
+
+static void do_write_level(struct sysinfo *s, u8 channel, u8 config, u8 rank1,
+ u8 rank0, int wl_enable)
+{
+ u32 emrs1;
+
+ /* Is shifted by bits 2 later so u8 can be used to reduce size */
+ const static u8 emrs1_lut[8][4][4]={ /* [Config][Leveling Rank][Rank] */
+ { /* Config 0: 2R2R */
+ {0x11, 0x00, 0x91, 0x00},
+ {0x00, 0x11, 0x91, 0x00},
+ {0x91, 0x00, 0x11, 0x00},
+ {0x91, 0x00, 0x00, 0x11}
+ },
+ { // Config 1: 2R1R
+ {0x11, 0x00, 0x91, 0x00},
+ {0x00, 0x11, 0x91, 0x00},
+ {0x91, 0x00, 0x11, 0x00},
+ {0x00, 0x00, 0x00, 0x00}
+ },
+ { // Config 2: 1R2R
+ {0x11, 0x00, 0x91, 0x00},
+ {0x00, 0x00, 0x00, 0x00},
+ {0x91, 0x00, 0x11, 0x00},
+ {0x91, 0x00, 0x00, 0x11}
+ },
+ { // Config 3: 1R1R
+ {0x11, 0x00, 0x91, 0x00},
+ {0x00, 0x00, 0x00, 0x00},
+ {0x91, 0x00, 0x11, 0x00},
+ {0x00, 0x00, 0x00, 0x00}
+ },
+ { // Config 4: 2R0R
+ {0x11, 0x00, 0x00, 0x00},
+ {0x00, 0x11, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00}
+ },
+ { // Config 5: 0R2R
+ {0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x11, 0x00},
+ {0x00, 0x00, 0x00, 0x11}
+ },
+ { // Config 6: 1R0R
+ {0x11, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00}
+ },
+ { // Config 7: 0R1R
+ {0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x00, 0x00},
+ {0x00, 0x00, 0x11, 0x00},
+ {0x00, 0x00, 0x00, 0x00}
+ }
+ };
+
+ if (wl_enable) {
+ printk(RAM_DEBUG, "Entering WL mode\n");
+ printk(RAM_DEBUG, "Using WL ODT values\n");
+ emrs1 = emrs1_lut[config][rank0][rank1];
+ } else {
+ printk(RAM_DEBUG, "Exiting WL mode\n");
+ emrs1 = ddr3_emrs1_config[s->dimm_config[channel]][rank1];
+ }
+ printk(RAM_DEBUG, "Setting ODT for rank%d to ", rank1);
+ switch (emrs1) {
+ case 0:
+ printk(RAM_DEBUG, "0 Ohm\n");
+ break;
+ case 0x11:
+ printk(RAM_DEBUG, "40 Ohm\n");
+ break;
+ case 0x81:
+ printk(RAM_DEBUG, "30 Ohm\n");
+ break;
+ case 0x80:
+ printk(RAM_DEBUG, "20 Ohm\n");
+ break;
+ case 0x10:
+ printk(RAM_DEBUG, "120 Ohm\n");
+ break;
+ case 0x01:
+ printk(RAM_DEBUG, "60 Ohm\n");
+ break;
+ default:
+ printk(BIOS_WARNING, "ODT value Undefined!\n");
+ break;
+ }
+
+ emrs1 <<= 2;
+ emrs1 |= (1 << 1);
+
+ if (wl_enable && (rank0 != rank1))
+ printk(RAM_DEBUG, "Disabling output for rank%d\n", rank1);
+ emrs1 |= (1 << 12);
+ if (wl_enable && (rank0 == rank1)) {
+ printk(RAM_DEBUG, "Enabling WL for rank%d\n", rank1);
+ emrs1 |= (1 << 7);
+ }
+ send_jedec_cmd(s, rank1, channel, EMRS1_CMD, emrs1);
+}
+
+static void set_db(struct sysinfo *s, struct dll_setting *dqs_setting)
+{
+ u8 db_tap, db_pi;
+
+ switch (s->selected_timings.mem_clk) {
+ default:
+ case MEM_CLOCK_800MHz:
+ db_tap = 0xa3;
+ db_pi = 0x32;
+ break;
+ case MEM_CLOCK_1066MHz:
+ db_tap = 0x82;
+ db_pi = 0x76;
+ break;
+ case MEM_CLOCK_1333MHz:
+ db_tap = 0xb3;
+ db_pi = 0x46;
+ break;
+ }
+
+ if (dqs_setting->tap < (db_tap & 0xf)) {
+ dqs_setting->db_en = 1;
+ dqs_setting->db_sel = 1;
+ } else if ((dqs_setting->tap == (db_tap & 0xf))
+ && (dqs_setting->pi < (db_pi & 0xf))) {
+ dqs_setting->db_en = 1;
+ dqs_setting->db_sel = 1;
+ } else if (dqs_setting->tap < (db_tap >> 4)) {
+ dqs_setting->db_en = 0;
+ dqs_setting->db_sel = 0;
+ } else if ((dqs_setting->tap == (db_tap >> 4))
+ && (dqs_setting->pi < (db_pi >> 4))) {
+ dqs_setting->db_en = 0;
+ dqs_setting->db_sel = 0;
+ } else {
+ dqs_setting->db_en = 1;
+ dqs_setting->db_sel = 0;
+ }
+}
+
+const static u8 max_tap[3] = {12, 10, 13};
+
+static int increment_dqs(struct sysinfo *s, struct dll_setting *dqs_setting)
+{
+ u8 max_tap_val = max_tap[s->selected_timings.mem_clk
+ - MEM_CLOCK_800MHz];
+ /* PI */
+ if (dqs_setting->pi < 6) {
+ dqs_setting->pi += 1;
+ } else if (dqs_setting->tap < max_tap_val) {
+ dqs_setting->pi = 0;
+ dqs_setting->tap += 1;
+ } else if (dqs_setting->clk_delay < 2) {
+ dqs_setting->pi = 0;
+ dqs_setting->tap = 0;
+ dqs_setting->clk_delay += 1;
+ } else if (dqs_setting->coarse < 1) {
+ dqs_setting->pi = 0;
+ dqs_setting->tap = 0;
+ dqs_setting->clk_delay = 0;
+ dqs_setting->coarse += 1;
+ } else {
+ return 1;
+ }
+ set_db(s, dqs_setting);
+ return 0;
+}
+
+static int decrement_dqs(struct sysinfo *s, struct dll_setting *dqs_setting)
+{
+ u8 max_tap_val = max_tap[s->selected_timings.mem_clk
+ - MEM_CLOCK_800MHz];
+ if (dqs_setting->pi > 0) {
+ dqs_setting->pi -= 1;
+ } else if (dqs_setting->tap > 0) {
+ dqs_setting->pi = 6;
+ dqs_setting->tap -= 1;
+ } else if (dqs_setting->clk_delay > 0) {
+ dqs_setting->pi = 6;
+ dqs_setting->tap = max_tap_val;
+ dqs_setting->clk_delay -= 1;
+ } else if (dqs_setting->coarse > 0) {
+ dqs_setting->pi = 6;
+ dqs_setting->tap = max_tap_val;
+ dqs_setting->clk_delay += 1;
+ dqs_setting->coarse -= 1;
+ } else {
+ return 1;
+ }
+ set_db(s, dqs_setting);
+ return 0;
+}
+
+#define N_LOOPS 2 /* Most likely needs to be even */
+#define N_SAMPLES 5
+
+static u8 sample_dq(struct sysinfo *s, u8 channel, u8 rank, u8 pass) {
+ u32 address = (channel << 29) | rank * 128 * MiB;
+ u8 all_found = 0xff;
+ int samples, lane;
+
+ for (samples = 0; samples < N_SAMPLES; samples++) {
+ write32((u32 *)address, 0x12341234);
+ write32((u32 *)address + 4, 0x12341234);
+ udelay(10);
+ for (lane = 0; lane < 8; lane++) {
+ if (((MCHBAR8(0x561 + 0x400 * channel + (lane * 4))
+ & 0x80) >> 7) != (pass % 2))
+ all_found &= ~(1 << lane);
+ }
+ }
+ return all_found;
+}
+
+static void increment_to_dqs_edge(struct sysinfo *s, u8 channel, u8 rank)
+{
+ int loop, lane, status, temp0, temp1 = 0;
+ u8 saved_24d;
+ struct dll_setting dqs_setting[8];
+ u8 all_found;
+ u8 bytelane = 0xff;
+
+ for (lane = 0; lane < 8; lane++) {
+ dqs_setting[lane].pi = 0;
+ dqs_setting[lane].tap = 0;
+ dqs_setting[lane].clk_delay = 0;
+ dqs_setting[lane].db_en = 0;
+ dqs_setting[lane].db_sel = 0;
+ dqs_setting[lane].coarse = 0;
+ }
+
+ for (lane = 0; lane < 8; lane++) {
+ printk(RAM_DEBUG, "Lane %d\n", lane);
+ while (!increment_dqs(s, &dqs_setting[lane])) {
+ temp0 = (sample_dq(s, 0, 0, 1) >> lane) & 1 ;
+ if (temp0 != temp1) {
+ temp1 = temp0;
+ printk(RAM_DEBUG, "Found EDGE for lane %d at: ",
+ lane);
+ print_dll_setting(&dqs_setting[lane], 1);
+ }
+
+ printk(RAM_DEBUG,"\tDQ: %s ", (temp0 ?
+ "LOW" : "HIGH"));
+ dqsset(channel, lane, &dqs_setting[lane]);
+ }
+ }
+
+ switch (s->selected_timings.mem_clk) {
+ default:
+ case MEM_CLOCK_800MHz:
+ for (lane = 0; lane < 8; lane++)
+ dqs_setting[lane] = ddr3_dll_setting_800[s->nmode - 1][DQS1 + lane];
+ break;
+ case MEM_CLOCK_1066MHz:
+ for (lane = 0; lane < 8; lane++)
+ dqs_setting[lane] = ddr3_dll_setting_1066[s->nmode - 1][DQS1 + lane];
+ break;
+ case MEM_CLOCK_1333MHz:
+ for (lane = 0; lane < 8; lane++)
+ dqs_setting[lane] = ddr3_dll_setting_1333[s->nmode - 1][DQS1 + lane];
+ break;
+ }
+
+ saved_24d = MCHBAR8(0x24d + 0x400 * channel);
+
+ for (loop = 0; loop < N_LOOPS; loop++) {
+ bytelane = 0xff;
+ do {
+ all_found = sample_dq(s, channel, rank, loop);
+ for (lane = 0; lane < 8; lane++) {
+ if (!(bytelane & (1 << lane)))
+ continue;
+ if (all_found & (1 << lane)) {
+ bytelane &= ~(1 << lane);
+ continue;
+ } else {
+ if ((loop % 2) == 0) {
+ status = decrement_dqs(s, &dqs_setting[lane]);
+ } else {
+ status = increment_dqs(s, &dqs_setting[lane]);
+ if (status)
+ printk(BIOS_WARNING,
+ " DQS DLL at MAX at lane %d,"
+ " we might have a problem here...\n"
+ , lane);
+ }
+ if (status) {
+ /* limits are not an invalid settings */
+ printk(RAM_SPEW, " lane %d reached limit. Continuing\n", lane);
+ bytelane &= ~(1 << lane);
+ status = 0;
+ }
+ }
+ dqsset(channel, lane, &dqs_setting[lane]);
+ }
+ } while (bytelane);
+ printk(BIOS_DEBUG, "DQS settings on PAS #%d:\n", loop);
+ for (lane = 0; lane < 8; lane++) {
+ printk(BIOS_DEBUG, "lane %d: ", lane);
+ print_dll_setting(&dqs_setting[lane], 1);
+ }
+ }
+
+ for (lane = 0; lane < 8; lane++)
+ s->dqs_settings[channel][lane] = dqs_setting[lane];
+
+ MCHBAR8(0x24d + 0x400 * channel) = saved_24d;
+}
+
+void search_write_leveling(struct sysinfo *s)
+{
+ int ch, count;
+ u8 config, rank0, rank1, lane;
+ u16 saved_x5c4;
+ struct dll_setting dqs_setting;
+
+ return; /* Sampling DQ does not work :( */
+
+ u8 chanconfig_lut[16]={ 0, 6, 4, 6, 7, 3, 1, 3, 5, 2, 0, 2, 7, 3, 1, 3
+ };
+
+ u8 odt_force[8][4] = { /* [Config][leveling rank] */
+ {0x5, 0x6, 0x5, 0x9},
+ {0x5, 0x6, 0x5, 0x0},
+ {0x5, 0x0, 0x5, 0x9},
+ {0x5, 0x0, 0x5, 0x0},
+ {0x1, 0x2, 0x0, 0x0},
+ {0x0, 0x0, 0x4, 0x8},
+ {0x1, 0x0, 0x0, 0x0},
+ {0x0, 0x0, 0x4, 0x0}
+ };
+
+ printk(BIOS_DEBUG, "Starting write levelling.\n");
+
+ MCHBAR8(0x5dc) = MCHBAR8(0x5dc) & ~0x80;
+ MCHBAR16(0x127) = (MCHBAR16(0x127) & ~0x7ff) | 0x540;
+ FOR_EACH_POPULATED_CHANNEL(s->dimms, ch) {
+ printk(BIOS_DEBUG, "\tCH%d\n", ch);
+ config = chanconfig_lut[s->dimm_config[ch]];
+ saved_x5c4 = MCHBAR16(0x5c4 + ch * 0x400);
+
+ MCHBAR8(0x5d8 + 0x400 * ch) = MCHBAR8(0x5d8 + 0x400 * ch) & ~0x0e;
+ MCHBAR16(0x5c4 + 0x400 * ch) = (MCHBAR16(0x5c4 + 0x400 * ch) &
+ ~0x3fff) | 0x3fff;
+ MCHBAR8(0x265 + 0x400 * ch) = MCHBAR8(0x265 + 0x400 * ch) & ~0x1f;
+ FOR_EACH_POPULATED_RANK_IN_CHANNEL(s->dimms, ch, rank0) {
+ FOR_EACH_POPULATED_RANK_IN_CHANNEL(s->dimms, ch, rank1) {
+ do_write_level(s, ch, config, rank1, rank0, 1);
+ }
+
+ MCHBAR8(0x298 + 2 + 0x400 * ch) = (MCHBAR8(0x298 + 2 + 0x400 * ch) & ~0x0f)
+ | odt_force[config][rank0];
+ MCHBAR8(0x271 + 0x400 * ch) = (MCHBAR8(0x271 + 0x400 * ch) & ~0x7e)
+ | 0x4e;
+ MCHBAR8(0x5d9 + 0x400 * ch) = (MCHBAR8(0x5d9 + 0x400 * ch) & ~0x04)
+ | 0x04;
+ MCHBAR32(0x1a0) = (MCHBAR32(0x1a0) & ~0x07ffffff) | 0x00014000;
+
+ increment_to_dqs_edge(s, ch, rank0);
+
+ MCHBAR8(0x298 + 2 + 0x400 * ch) = MCHBAR8(0x298 + 2 + 0x400 * ch) & ~0x0f;
+ MCHBAR8(0x271 + 0x400 * ch) = (MCHBAR8(0x271 + 0x400 * ch) & ~0x7e)
+ | 0x0e;
+ MCHBAR8(0x5d9 + 0x400 * ch) = (MCHBAR8(0x5d9 + 0x400 * ch) & ~0x04);
+ MCHBAR32(0x1a0) = (MCHBAR32(0x1a0) & ~0x07ffffff) | 0x00555801;
+ FOR_EACH_POPULATED_RANK_IN_CHANNEL(s->dimms, ch, rank1) {
+ if (rank0 == rank1) {
+ do_write_level(s, ch, config, rank1, rank0, 0);
+ send_jedec_cmd(s, rank1 + 4 * ch, ch, NORMALOP_CMD, 1 << 12);
+ }
+ }
+ break; /* Break on first populated rank */
+ }
+ MCHBAR8(0x5d8 + 0x400 * ch) = (MCHBAR8(0x5d8 + 0x400 * ch) & ~0x0e) | 0x0e;
+ MCHBAR16(0x5c4 + 0x400 * ch) = (MCHBAR16(0x5c4 + 0x400 * ch) &
+ ~0x3fff) | 0x1807;
+ MCHBAR8(0x265 + 0x400 * ch) = MCHBAR8(0x265 + 0x400 * ch) & ~0x1f;
+ FOR_EACH_POPULATED_RANK_IN_CHANNEL(s->dimms, ch, rank0) {
+ do_write_level(s, ch, config, rank0, rank0, 0);
+ }
+
+ }
+ MCHBAR8(0x5dc) = (MCHBAR8(0x5dc) & ~0x80) | 0x80;
+ MCHBAR16(0x127) = (MCHBAR16(0x127) & ~0x7ff) | 0x2bf;
+
+ switch (s->selected_timings.mem_clk) {
+ default:
+ case MEM_CLOCK_800MHz:
+ count = 39;
+ break;
+ case MEM_CLOCK_1066MHz:
+ count = 32;
+ break;
+ case MEM_CLOCK_1333MHz:
+ count = 42;
+ break;
+ }
+ /* TODO: restore on S3 */
+ FOR_EACH_POPULATED_CHANNEL(s->dimms, ch) {
+ for (lane = 0; lane < 8; lane++) {
+ dqs_setting = s->dqs_settings[ch][lane];
+ dqsset(ch, lane, &dqs_setting);
+ while (count--)
+ increment_dqs(s, &dqs_setting);
+ dqset(ch, lane, &dqs_setting);
+ }
+ }
+
+ printk(BIOS_DEBUG, "Done write levelling.\n");
+}
diff --git a/src/northbridge/intel/x4x/raminit_ddr23.c b/src/northbridge/intel/x4x/raminit_ddr23.c
index a289788..9e7583c 100644
--- a/src/northbridge/intel/x4x/raminit_ddr23.c
+++ b/src/northbridge/intel/x4x/raminit_ddr23.c
@@ -369,6 +369,8 @@
void dqsset(u8 ch, u8 lane, const struct dll_setting *setting)
{
+ printk(RAM_SPEW, "DQS setting: ch%d, l%d ", ch, lane);
+ print_dll_setting(setting, 0);
MCHBAR32(0x400*ch + 0x5fc) = (MCHBAR32(0x400*ch + 0x5fc) & ~(2 << (lane*4)))
| (setting->coarse << ((lane * 4) + 1));
@@ -414,6 +416,10 @@
void dqset(u8 ch, u8 lane, const struct dll_setting *setting)
{
+
+ printk(RAM_SPEW, "DQ setting: ch%d, l%d ", ch, lane);
+ print_dll_setting(setting, 0);
+
MCHBAR32(0x400*ch + 0x5fc) = (MCHBAR32(0x400*ch + 0x5fc) & ~(1 << (lane*4))) |
(setting->coarse << (lane * 4));
MCHBAR32(0x400*ch + 0x5a4) = (MCHBAR32(0x400*ch + 0x5a4) & ~(0x201 << lane)) |
@@ -765,6 +771,20 @@
for (lane = 0; lane < 8; lane++) {
dqset(channel, lane, &dll_setting[DQ1 + lane]);
}
+}
+
+void print_dll_setting(const struct dll_setting *dll_setting, u8 default_verbose)
+{
+ u8 debug_level;
+ if (default_verbose)
+ debug_level = BIOS_DEBUG;
+ else
+ debug_level = RAM_DEBUG;
+
+ printk(debug_level, "%d.%d.%d.%d:%d.%d\n", dll_setting->coarse,
+ dll_setting->clk_delay, dll_setting->tap,
+ dll_setting->pi, dll_setting->db_en,
+ dll_setting->db_sel);
}
static void program_dll(struct sysinfo *s)
@@ -1411,6 +1431,33 @@
}
printk(BIOS_DEBUG, "MRS done\n");
}
+
+static void write_leveling(struct sysinfo *s)
+{
+ if (s->boot_path == BOOT_PATH_NORMAL) {
+ search_write_leveling(s);
+ } else {
+ /* TODO restore write leveling */
+ }
+}
+
+static void software_ddr3_reset(struct sysinfo *s)
+{
+ printk(BIOS_DEBUG, "Software initiated DDR3 reset.\n");
+ MCHBAR8(0x1a8) = MCHBAR8(0x1a8) | 0x02;
+ MCHBAR8(0x5da) = MCHBAR8(0x5da) & ~0x80;
+ MCHBAR8(0x1a8) = MCHBAR8(0x1a8) & ~0x02;
+ MCHBAR8(0x5da) = (MCHBAR8(0x5da) & ~0x03) | 1;
+ udelay(200);
+ MCHBAR8(0x1a8) = MCHBAR8(0x1a8) & ~0x02;
+ MCHBAR8(0x5da) = MCHBAR8(0x5da) | 0x80;
+ MCHBAR8(0x5da) = MCHBAR8(0x5da) & ~0x80;
+ udelay(500);
+ MCHBAR8(0x5da) = MCHBAR8(0x5da) | 0x03;
+ MCHBAR8(0x5da) = MCHBAR8(0x5da) & ~0x03;
+ jedec_ddr3(s);
+}
+
static u8 sampledqs(u16 mchloc, u32 addr, u8 hilow, u8 repeat)
{
u8 dqsmatch = 1;
@@ -2216,6 +2263,17 @@
MCHBAR8(0x9d8) = MCHBAR8(0x9d8) | 0x7;
}
+ /* DDR3 reset */
+ if ((s->spd_type == DDR3) && (s->boot_path != BOOT_PATH_RESUME)) {
+ printk(BIOS_DEBUG, "DDR3 Reset.\n");
+ MCHBAR8(0x1a8) = MCHBAR8(0x1a8) & ~0x2;
+ MCHBAR8(0x5da) = MCHBAR8(0x5da) | 0x80;
+ udelay(500);
+ MCHBAR8(0x1a8) = MCHBAR8(0x1a8) & ~0x2;
+ MCHBAR8(0x5da) = MCHBAR8(0x5da) & ~0x80;
+ udelay(500);
+ }
+
// Pre jedec
MCHBAR8(0x40) = MCHBAR8(0x40) | 0x2;
FOR_EACH_POPULATED_CHANNEL(s->dimms, ch) {
@@ -2232,6 +2290,12 @@
jedec_ddr3(s);
}
+ if (s->spd_type == DDR3) {
+ write_leveling(s);
+ if (s->boot_path == BOOT_PATH_NORMAL)
+ software_ddr3_reset(s);
+ }
+
printk(BIOS_DEBUG, "Done jedec steps\n");
// After JEDEC reset
diff --git a/src/northbridge/intel/x4x/raminit_tables.c b/src/northbridge/intel/x4x/raminit_tables.c
index 3349f62..e3001f2 100644
--- a/src/northbridge/intel/x4x/raminit_tables.c
+++ b/src/northbridge/intel/x4x/raminit_tables.c
@@ -134,7 +134,7 @@
{7, 6, 0, 0, 0, 0},
{9, 2, 1, 0, 0, 0},
{9, 2, 1, 0, 0, 0},
- {2, 5, 1, 1, 1, 0},
+ {2, 5, 1, 1, 1, 0}, /* DQS1 */
{5, 1, 0, 0, 1, 0},
{6, 6, 0, 0, 1, 0},
{8, 0, 0, 0, 1, 0},
@@ -158,7 +158,7 @@
{0, 6, 1, 1, 0, 0},
{2, 2, 1, 1, 0, 0},
{2, 2, 1, 1, 0, 0},
- {6, 4, 0, 0, 0, 0},
+ {6, 4, 0, 0, 0, 0}, /* DQS1 */
{9, 1, 1, 0, 0, 0},
{10, 6, 1, 0, 0, 0},
{1, 0, 1, 1, 1, 0},
diff --git a/src/northbridge/intel/x4x/spd_ddr3_decode.c b/src/northbridge/intel/x4x/spd_ddr3_decode.c
index eab0548..857f6e7 100644
--- a/src/northbridge/intel/x4x/spd_ddr3_decode.c
+++ b/src/northbridge/intel/x4x/spd_ddr3_decode.c
@@ -82,6 +82,7 @@
}
min_tCLK = MAX(min_tCLK, saved_timings->min_tclk);
+ min_tCLK = TCK_400MHZ; /* Hardcode since write leveling does not work */
normalize_tCLK(&min_tCLK);
if (min_tCLK == 0) {
printk(BIOS_ERR, "DRAM frequency is under lowest supported "
--
To view, visit https://review.coreboot.org/19878
To unsubscribe, visit https://review.coreboot.org/settings
Gerrit-MessageType: newchange
Gerrit-Change-Id: Ibfbaa235bc4eb08e9345321b851e880390a624e8
Gerrit-PatchSet: 1
Gerrit-Project: coreboot
Gerrit-Branch: master
Gerrit-Owner: Arthur Heymans <arthur at aheymans.xyz>
More information about the coreboot-gerrit
mailing list