[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