diff options
Diffstat (limited to 'config/coreboot/haswell')
30 files changed, 0 insertions, 11298 deletions
| diff --git a/config/coreboot/haswell/patches/0001-haswell-NRI-Initialise-MPLL.patch b/config/coreboot/haswell/patches/0001-haswell-NRI-Initialise-MPLL.patch deleted file mode 100644 index 0de1a4ec..00000000 --- a/config/coreboot/haswell/patches/0001-haswell-NRI-Initialise-MPLL.patch +++ /dev/null @@ -1,348 +0,0 @@ -From cce5392f272b0acc493f47f9b5ca3cf90ce901e8 Mon Sep 17 00:00:00 2001 -From: Angel Pons <th3fanbus@gmail.com> -Date: Thu, 11 Apr 2024 17:25:07 +0200 -Subject: [PATCH 01/20] haswell NRI: Initialise MPLL - -Add code to initialise the MPLL (Memory PLL). The procedure is similar -to the one for Sandy/Ivy Bridge, but it is not worth factoring out. - -Change-Id: I978c352de68f6d8cecc76f4ae3c12daaf4be9ed6 -Signed-off-by: Angel Pons <th3fanbus@gmail.com> ---- - .../intel/haswell/native_raminit/Makefile.mk  |   2 + - .../intel/haswell/native_raminit/init_mpll.c  | 210 ++++++++++++++++++ - .../haswell/native_raminit/io_comp_control.c  |  22 ++ - .../haswell/native_raminit/raminit_main.c     |   3 +- - .../haswell/native_raminit/raminit_native.h   |  11 + - .../intel/haswell/registers/mchbar.h          |   3 + - 6 files changed, 250 insertions(+), 1 deletion(-) - create mode 100644 src/northbridge/intel/haswell/native_raminit/init_mpll.c - create mode 100644 src/northbridge/intel/haswell/native_raminit/io_comp_control.c - -diff --git a/src/northbridge/intel/haswell/native_raminit/Makefile.mk b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -index ebf7abc6ec..c125d84f0b 100644 ---- a/src/northbridge/intel/haswell/native_raminit/Makefile.mk -+++ b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -@@ -1,5 +1,7 @@ - ## SPDX-License-Identifier: GPL-2.0-or-later -  -+romstage-y += init_mpll.c -+romstage-y += io_comp_control.c - romstage-y += raminit_main.c - romstage-y += raminit_native.c - romstage-y += spd_bitmunching.c -diff --git a/src/northbridge/intel/haswell/native_raminit/init_mpll.c b/src/northbridge/intel/haswell/native_raminit/init_mpll.c -new file mode 100644 -index 0000000000..1f3f2c29a9 ---- /dev/null -+++ b/src/northbridge/intel/haswell/native_raminit/init_mpll.c -@@ -0,0 +1,210 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#include <commonlib/bsd/clamp.h> -+#include <console/console.h> -+#include <delay.h> -+#include <device/pci_ops.h> -+#include <northbridge/intel/haswell/haswell.h> -+#include <types.h> -+ -+#include "raminit_native.h" -+ -+static uint32_t get_mem_multiplier(const struct sysinfo *ctrl) -+{ -+	const uint32_t mult = NS2MHZ_DIV256 / (ctrl->tCK * ctrl->base_freq); -+ -+	if (ctrl->base_freq == 100) -+		return clamp_u32(7, mult, 12); -+ -+	if (ctrl->base_freq == 133) -+		return clamp_u32(3, mult, 10); -+ -+	die("Unsupported base frequency\n"); -+} -+ -+static void normalize_tck(struct sysinfo *ctrl, const bool pll_ref100) -+{ -+	/** TODO: Haswell supports up to DDR3-2600 **/ -+	if (ctrl->tCK <= TCK_1200MHZ) { -+		ctrl->tCK = TCK_1200MHZ; -+		ctrl->base_freq = 133; -+		ctrl->mem_clock_mhz = 1200; -+ -+	} else if (ctrl->tCK <= TCK_1100MHZ) { -+		ctrl->tCK = TCK_1100MHZ; -+		ctrl->base_freq = 100; -+		ctrl->mem_clock_mhz = 1100; -+ -+	} else if (ctrl->tCK <= TCK_1066MHZ) { -+		ctrl->tCK = TCK_1066MHZ; -+		ctrl->base_freq = 133; -+		ctrl->mem_clock_mhz = 1066; -+ -+	} else if (ctrl->tCK <= TCK_1000MHZ) { -+		ctrl->tCK = TCK_1000MHZ; -+		ctrl->base_freq = 100; -+		ctrl->mem_clock_mhz = 1000; -+ -+	} else if (ctrl->tCK <= TCK_933MHZ) { -+		ctrl->tCK = TCK_933MHZ; -+		ctrl->base_freq = 133; -+		ctrl->mem_clock_mhz = 933; -+ -+	} else if (ctrl->tCK <= TCK_900MHZ) { -+		ctrl->tCK = TCK_900MHZ; -+		ctrl->base_freq = 100; -+		ctrl->mem_clock_mhz = 900; -+ -+	} else if (ctrl->tCK <= TCK_800MHZ) { -+		ctrl->tCK = TCK_800MHZ; -+		ctrl->base_freq = 133; -+		ctrl->mem_clock_mhz = 800; -+ -+	} else if (ctrl->tCK <= TCK_700MHZ) { -+		ctrl->tCK = TCK_700MHZ; -+		ctrl->base_freq = 100; -+		ctrl->mem_clock_mhz = 700; -+ -+	} else if (ctrl->tCK <= TCK_666MHZ) { -+		ctrl->tCK = TCK_666MHZ; -+		ctrl->base_freq = 133; -+		ctrl->mem_clock_mhz = 666; -+ -+	} else if (ctrl->tCK <= TCK_533MHZ) { -+		ctrl->tCK = TCK_533MHZ; -+		ctrl->base_freq = 133; -+		ctrl->mem_clock_mhz = 533; -+ -+	} else if (ctrl->tCK <= TCK_400MHZ) { -+		ctrl->tCK = TCK_400MHZ; -+		ctrl->base_freq = 133; -+		ctrl->mem_clock_mhz = 400; -+ -+	} else { -+		ctrl->tCK = 0; -+		ctrl->base_freq = 1; -+		ctrl->mem_clock_mhz = 0; -+		return; -+	} -+	if (!pll_ref100 && ctrl->base_freq == 100) { -+		/* Skip unsupported frequency */ -+		ctrl->tCK++; -+		normalize_tck(ctrl, pll_ref100); -+	} -+} -+ -+#define MIN_CAS	4 -+#define MAX_CAS	24 -+ -+static uint8_t find_compatible_cas(struct sysinfo *ctrl) -+{ -+	printk(RAM_DEBUG, "With tCK %u, try CAS: ", ctrl->tCK); -+	const uint8_t cas_lower = MAX(MIN_CAS, DIV_ROUND_UP(ctrl->tAA, ctrl->tCK)); -+	const uint8_t cas_upper = MIN(MAX_CAS, 19); /* JEDEC MR0 limit */ -+ -+	if (!(ctrl->cas_supported >> (cas_lower - MIN_CAS))) { -+		printk(RAM_DEBUG, "DIMMs do not support CAS >= %u\n", cas_lower); -+		ctrl->tCK++; -+		return 0; -+	} -+	for (uint8_t cas = cas_lower; cas <= cas_upper; cas++) { -+		printk(RAM_DEBUG, "%u ", cas); -+		if (ctrl->cas_supported & BIT(cas - MIN_CAS)) { -+			printk(RAM_DEBUG, "OK\n"); -+			return cas; -+		} -+	} -+	return 0; -+} -+ -+static enum raminit_status find_cas_tck(struct sysinfo *ctrl) -+{ -+	/** TODO: Honor all possible PLL_REF100_CFG values **/ -+	uint8_t pll_ref100 = (pci_read_config32(HOST_BRIDGE, CAPID0_B) >> 21) & 0x7; -+	printk(RAM_DEBUG, "PLL_REF100_CFG value: 0x%x\n", pll_ref100); -+	printk(RAM_DEBUG, "100MHz reference clock support: %s\n", pll_ref100 ? "yes" : "no"); -+ -+	uint8_t selected_cas; -+	while (true) { -+		/* Round tCK up so that it is a multiple of either 133 or 100 MHz */ -+		normalize_tck(ctrl, pll_ref100); -+		if (!ctrl->tCK) { -+			printk(BIOS_ERR, "Couldn't find compatible clock / CAS settings\n"); -+			return RAMINIT_STATUS_MPLL_INIT_FAILURE; -+		} -+		selected_cas = find_compatible_cas(ctrl); -+		if (selected_cas) -+			break; -+ -+		ctrl->tCK++; -+	} -+	printk(BIOS_DEBUG, "Found compatible clock / CAS settings\n"); -+	printk(BIOS_DEBUG, "Selected DRAM frequency: %u MHz\n", NS2MHZ_DIV256 / ctrl->tCK); -+	printk(BIOS_DEBUG, "Selected CAS latency   : %uT\n", selected_cas); -+	ctrl->multiplier = get_mem_multiplier(ctrl); -+	return RAMINIT_STATUS_SUCCESS; -+} -+ -+enum raminit_status initialise_mpll(struct sysinfo *ctrl) -+{ -+	if (ctrl->tCK > TCK_400MHZ) { -+		printk(BIOS_ERR, "tCK is too slow. Increasing to 400 MHz as last resort\n"); -+		ctrl->tCK = TCK_400MHZ; -+	} -+	while (true) { -+		if (!ctrl->qclkps) { -+			const enum raminit_status status = find_cas_tck(ctrl); -+			if (status) -+				return status; -+		} -+ -+		/* -+		 * Unlike previous generations, Haswell's MPLL won't shut down if the -+		 * requested frequency isn't supported. But we cannot reinitialize it. -+		 * Another different thing: MPLL registers are 4-bit instead of 8-bit. -+		 */ -+ -+		/** FIXME: Obtain current clock frequency if we want to skip this **/ -+		//if (mchbar_read32(MC_BIOS_DATA) != 0) -+		//	break; -+ -+		uint32_t mc_bios_req = ctrl->multiplier; -+		if (ctrl->base_freq == 100) { -+			/* Use 100 MHz reference clock */ -+			mc_bios_req |= BIT(4); -+		} -+		mc_bios_req |= BIT(31); -+		printk(RAM_DEBUG, "MC_BIOS_REQ = 0x%08x\n", mc_bios_req); -+		printk(BIOS_DEBUG, "MPLL busy... "); -+		mchbar_write32(MC_BIOS_REQ, mc_bios_req); -+ -+		for (unsigned int i = 0; i <= 5000; i++) { -+			if (!(mchbar_read32(MC_BIOS_REQ) & BIT(31))) { -+				printk(BIOS_DEBUG, "done in %u us\n", i); -+				break; -+			} -+			udelay(1); -+		} -+		if (mchbar_read32(MC_BIOS_REQ) & BIT(31)) -+			printk(BIOS_DEBUG, "did not lock\n"); -+ -+		/* Verify locked frequency */ -+		const uint32_t mc_bios_data = mchbar_read32(MC_BIOS_DATA); -+		printk(RAM_DEBUG, "MC_BIOS_DATA = 0x%08x\n", mc_bios_data); -+		if ((mc_bios_data & 0xf) >= ctrl->multiplier) -+			break; -+ -+		printk(BIOS_DEBUG, "Retrying at a lower frequency\n\n"); -+		ctrl->tCK++; -+	} -+	if (!ctrl->mem_clock_mhz) { -+		printk(BIOS_ERR, "Could not program MPLL frequency\n"); -+		return RAMINIT_STATUS_MPLL_INIT_FAILURE; -+	} -+	printk(BIOS_DEBUG, "MPLL frequency is set to: %u MHz ", ctrl->mem_clock_mhz); -+	ctrl->mem_clock_fs = 1000000000 / ctrl->mem_clock_mhz; -+	printk(BIOS_DEBUG, "(period: %u femtoseconds)\n", ctrl->mem_clock_fs); -+	ctrl->qclkps = ctrl->mem_clock_fs / 2000; -+	printk(BIOS_DEBUG, "Quadrature clock period: %u picoseconds\n", ctrl->qclkps); -+	return wait_for_first_rcomp(); -+} -diff --git a/src/northbridge/intel/haswell/native_raminit/io_comp_control.c b/src/northbridge/intel/haswell/native_raminit/io_comp_control.c -new file mode 100644 -index 0000000000..d45b608dd3 ---- /dev/null -+++ b/src/northbridge/intel/haswell/native_raminit/io_comp_control.c -@@ -0,0 +1,22 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#include <commonlib/bsd/clamp.h> -+#include <console/console.h> -+#include <northbridge/intel/haswell/haswell.h> -+#include <timer.h> -+#include <types.h> -+ -+#include "raminit_native.h" -+ -+enum raminit_status wait_for_first_rcomp(void) -+{ -+	struct stopwatch timer; -+	stopwatch_init_msecs_expire(&timer, 2000); -+	do { -+		if (mchbar_read32(RCOMP_TIMER) & BIT(16)) -+			return RAMINIT_STATUS_SUCCESS; -+ -+	} while (!stopwatch_expired(&timer)); -+	printk(BIOS_ERR, "Timed out waiting for RCOMP to complete\n"); -+	return RAMINIT_STATUS_POLL_TIMEOUT; -+} -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_main.c b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -index 19ec5859ac..bf745e943f 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_main.c -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -@@ -19,7 +19,8 @@ struct task_entry { - }; -  - static const struct task_entry cold_boot[] = { --	{ collect_spd_info,           true, "PROCSPD",    }, -+	{ collect_spd_info,                                       true, "PROCSPD",    }, -+	{ initialise_mpll,                                        true, "INITMPLL",   }, - }; -  - /* Return a generic stepping value to make stepping checks simpler */ -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -index 1a0793947e..a54581abc7 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -@@ -23,6 +23,8 @@ enum raminit_status { - 	RAMINIT_STATUS_SUCCESS = 0, - 	RAMINIT_STATUS_NO_MEMORY_INSTALLED, - 	RAMINIT_STATUS_UNSUPPORTED_MEMORY, -+	RAMINIT_STATUS_MPLL_INIT_FAILURE, -+	RAMINIT_STATUS_POLL_TIMEOUT, - 	RAMINIT_STATUS_UNSPECIFIED_ERROR, /** TODO: Deprecated in favor of specific values **/ - }; -  -@@ -82,10 +84,19 @@ struct sysinfo { - 	uint8_t rankmap[NUM_CHANNELS]; - 	uint8_t rank_mirrored[NUM_CHANNELS]; - 	uint32_t channel_size_mb[NUM_CHANNELS]; -+ -+	uint8_t base_freq;		/* Memory base frequency, either 100 or 133 MHz */ -+	uint32_t multiplier; -+	uint32_t mem_clock_mhz; -+	uint32_t mem_clock_fs;		/* Memory clock period in femtoseconds */ -+	uint32_t qclkps;		/* Quadrature clock period in picoseconds */ - }; -  - void raminit_main(enum raminit_boot_mode bootmode); -  - enum raminit_status collect_spd_info(struct sysinfo *ctrl); -+enum raminit_status initialise_mpll(struct sysinfo *ctrl); -+ -+enum raminit_status wait_for_first_rcomp(void); -  - #endif -diff --git a/src/northbridge/intel/haswell/registers/mchbar.h b/src/northbridge/intel/haswell/registers/mchbar.h -index 5610e7089a..45f8174995 100644 ---- a/src/northbridge/intel/haswell/registers/mchbar.h -+++ b/src/northbridge/intel/haswell/registers/mchbar.h -@@ -13,6 +13,8 @@ - #define MC_INIT_STATE_G		0x5030 - #define MRC_REVISION		0x5034 /* MRC Revision */ -  -+#define RCOMP_TIMER		0x5084 -+ - #define MC_LOCK			0x50fc /* Memory Controller Lock register */ -  - #define GFXVTBAR		0x5400 /* Base address for IGD */ -@@ -61,6 +63,7 @@ -  - #define BIOS_RESET_CPL		0x5da8 /* 8-bit */ -  -+#define MC_BIOS_REQ		0x5e00 /* Memory frequency request register */ - #define MC_BIOS_DATA		0x5e04 /* Miscellaneous information for BIOS */ - #define SAPMCTL			0x5f00 -  ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0002-haswell-NRI-Post-process-selected-timings.patch b/config/coreboot/haswell/patches/0002-haswell-NRI-Post-process-selected-timings.patch deleted file mode 100644 index 0cc95cdd..00000000 --- a/config/coreboot/haswell/patches/0002-haswell-NRI-Post-process-selected-timings.patch +++ /dev/null @@ -1,249 +0,0 @@ -From 42b21fdce8c8bade53d9d86515f88b0665a4c1b1 Mon Sep 17 00:00:00 2001 -From: Angel Pons <th3fanbus@gmail.com> -Date: Sat, 7 May 2022 16:29:55 +0200 -Subject: [PATCH 02/20] haswell NRI: Post-process selected timings - -Once the MPLL has been initialised, convert the timings from the SPD to -be in DCLKs, which is what the hardware expects. In addition, calculate -the values for tREFI and tXP. - -Change-Id: Id02caf858f75b9e08016762b3aefda282b274386 -Signed-off-by: Angel Pons <th3fanbus@gmail.com> ---- - .../intel/haswell/native_raminit/Makefile.mk  |   1 + - .../haswell/native_raminit/lookup_timings.c   |  62 +++++++++++ - .../haswell/native_raminit/raminit_main.c     |   1 + - .../haswell/native_raminit/raminit_native.h   |   8 ++ - .../haswell/native_raminit/spd_bitmunching.c  | 100 ++++++++++++++++++ - 5 files changed, 172 insertions(+) - create mode 100644 src/northbridge/intel/haswell/native_raminit/lookup_timings.c - -diff --git a/src/northbridge/intel/haswell/native_raminit/Makefile.mk b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -index c125d84f0b..2769e0bbb4 100644 ---- a/src/northbridge/intel/haswell/native_raminit/Makefile.mk -+++ b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -@@ -1,5 +1,6 @@ - ## SPDX-License-Identifier: GPL-2.0-or-later -  -+romstage-y += lookup_timings.c - romstage-y += init_mpll.c - romstage-y += io_comp_control.c - romstage-y += raminit_main.c -diff --git a/src/northbridge/intel/haswell/native_raminit/lookup_timings.c b/src/northbridge/intel/haswell/native_raminit/lookup_timings.c -new file mode 100644 -index 0000000000..8b81c7c341 ---- /dev/null -+++ b/src/northbridge/intel/haswell/native_raminit/lookup_timings.c -@@ -0,0 +1,62 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#include <commonlib/bsd/clamp.h> -+#include <types.h> -+ -+#include "raminit_native.h" -+ -+struct timing_lookup { -+	uint32_t clock; -+	uint32_t value; -+}; -+ -+static uint32_t lookup_timing( -+	const uint32_t mem_clock_mhz, -+	const struct timing_lookup *const lookup, -+	const size_t length) -+{ -+	/* Fall back to the last index */ -+	size_t i; -+	for (i = 0; i < length - 1; i++) { -+		/* Account for imprecise frequency values */ -+		if ((mem_clock_mhz - 5) <= lookup[i].clock) -+			break; -+	} -+	return lookup[i].value; -+} -+ -+static const uint32_t fmax = UINT32_MAX; -+ -+uint8_t get_tCWL(const uint32_t mem_clock_mhz) -+{ -+	const struct timing_lookup lut[] = { -+		{  400,  5 }, -+		{  533,  6 }, -+		{  666,  7 }, -+		{  800,  8 }, -+		{  933,  9 }, -+		{ 1066, 10 }, -+		{ 1200, 11 }, -+		{ fmax, 12 }, -+	}; -+	return lookup_timing(mem_clock_mhz, lut, ARRAY_SIZE(lut)); -+} -+ -+/* tREFI = 7800 ns * DDR MHz */ -+uint32_t get_tREFI(const uint32_t mem_clock_mhz) -+{ -+	return (mem_clock_mhz * 7800) / 1000; -+} -+ -+uint32_t get_tXP(const uint32_t mem_clock_mhz) -+{ -+	const struct timing_lookup lut[] = { -+		{  400,  3 }, -+		{  666,  4 }, -+		{  800,  5 }, -+		{  933,  6 }, -+		{ 1066,  7 }, -+		{ fmax,  8 }, -+	}; -+	return lookup_timing(mem_clock_mhz, lut, ARRAY_SIZE(lut)); -+} -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_main.c b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -index bf745e943f..2fea658415 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_main.c -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -@@ -21,6 +21,7 @@ struct task_entry { - static const struct task_entry cold_boot[] = { - 	{ collect_spd_info,                                       true, "PROCSPD",    }, - 	{ initialise_mpll,                                        true, "INITMPLL",   }, -+	{ convert_timings,                                        true, "CONVTIM",    }, - }; -  - /* Return a generic stepping value to make stepping checks simpler */ -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -index a54581abc7..01e5ed1bd6 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -@@ -78,6 +78,9 @@ struct sysinfo { - 	uint32_t tCWL; - 	uint32_t tCMD; -  -+	uint32_t tREFI; -+	uint32_t tXP; -+ - 	uint8_t lanes;			/* 8 or 9 */ - 	uint8_t chanmap; - 	uint8_t dpc[NUM_CHANNELS];	/* DIMMs per channel */ -@@ -96,7 +99,12 @@ void raminit_main(enum raminit_boot_mode bootmode); -  - enum raminit_status collect_spd_info(struct sysinfo *ctrl); - enum raminit_status initialise_mpll(struct sysinfo *ctrl); -+enum raminit_status convert_timings(struct sysinfo *ctrl); -  - enum raminit_status wait_for_first_rcomp(void); -  -+uint8_t get_tCWL(uint32_t mem_clock_mhz); -+uint32_t get_tREFI(uint32_t mem_clock_mhz); -+uint32_t get_tXP(uint32_t mem_clock_mhz); -+ - #endif -diff --git a/src/northbridge/intel/haswell/native_raminit/spd_bitmunching.c b/src/northbridge/intel/haswell/native_raminit/spd_bitmunching.c -index 2dab8504c4..7d98341a7e 100644 ---- a/src/northbridge/intel/haswell/native_raminit/spd_bitmunching.c -+++ b/src/northbridge/intel/haswell/native_raminit/spd_bitmunching.c -@@ -204,3 +204,103 @@ enum raminit_status collect_spd_info(struct sysinfo *ctrl) - 	get_spd_data(ctrl); - 	return find_common_spd_parameters(ctrl); - } -+ -+#define MIN_CWL		5 -+#define MAX_CWL		12 -+ -+/* Except for tCK, hardware expects all timing values in DCLKs, not nanoseconds */ -+enum raminit_status convert_timings(struct sysinfo *ctrl) -+{ -+	/* -+	 * Obtain all required timing values, in DCLKs. -+	 */ -+ -+	/* Convert primary timings from nanoseconds to DCLKs */ -+	ctrl->tAA  = DIV_ROUND_UP(ctrl->tAA,  ctrl->tCK); -+	ctrl->tWR  = DIV_ROUND_UP(ctrl->tWR,  ctrl->tCK); -+	ctrl->tRCD = DIV_ROUND_UP(ctrl->tRCD, ctrl->tCK); -+	ctrl->tRRD = DIV_ROUND_UP(ctrl->tRRD, ctrl->tCK); -+	ctrl->tRP  = DIV_ROUND_UP(ctrl->tRP,  ctrl->tCK); -+	ctrl->tRAS = DIV_ROUND_UP(ctrl->tRAS, ctrl->tCK); -+	ctrl->tRC  = DIV_ROUND_UP(ctrl->tRC,  ctrl->tCK); -+	ctrl->tRFC = DIV_ROUND_UP(ctrl->tRFC, ctrl->tCK); -+	ctrl->tWTR = DIV_ROUND_UP(ctrl->tWTR, ctrl->tCK); -+	ctrl->tRTP = DIV_ROUND_UP(ctrl->tRTP, ctrl->tCK); -+	ctrl->tFAW = DIV_ROUND_UP(ctrl->tFAW, ctrl->tCK); -+	ctrl->tCWL = DIV_ROUND_UP(ctrl->tCWL, ctrl->tCK); -+	ctrl->tCMD = DIV_ROUND_UP(ctrl->tCMD, ctrl->tCK); -+ -+	/* Constrain primary timings to hardware limits */ -+	/** TODO: complain when clamping? **/ -+	ctrl->tAA  = clamp_u32(4,  ctrl->tAA,  24); -+	ctrl->tWR  = clamp_u32(5,  ctrl->tWR,  16); -+	ctrl->tRCD = clamp_u32(4,  ctrl->tRCD, 20); -+	ctrl->tRRD = clamp_u32(4,  ctrl->tRRD, 65535); -+	ctrl->tRP  = clamp_u32(4,  ctrl->tRP,  15); -+	ctrl->tRAS = clamp_u32(10, ctrl->tRAS, 40); -+	ctrl->tRC  = clamp_u32(1,  ctrl->tRC,  4095); -+	ctrl->tRFC = clamp_u32(1,  ctrl->tRFC, 511); -+	ctrl->tWTR = clamp_u32(4,  ctrl->tWTR, 10); -+	ctrl->tRTP = clamp_u32(4,  ctrl->tRTP, 15); -+	ctrl->tFAW = clamp_u32(10, ctrl->tFAW, 54); -+ -+	/** TODO: Honor tREFI from XMP **/ -+	ctrl->tREFI = get_tREFI(ctrl->mem_clock_mhz); -+	ctrl->tXP   =   get_tXP(ctrl->mem_clock_mhz); -+ -+	/* -+	 * Check some values, and adjust them if necessary. -+	 */ -+ -+	/* If tWR cannot be written into DDR3 MR0, adjust it */ -+	switch (ctrl->tWR) { -+	case  9: -+	case 11: -+	case 13: -+	case 15: -+		ctrl->tWR++; -+	} -+ -+	/* If tCWL is not supported or unspecified, look up a reasonable default */ -+	if (ctrl->tCWL < MIN_CWL || ctrl->tCWL > MAX_CWL) -+		ctrl->tCWL = get_tCWL(ctrl->mem_clock_mhz); -+ -+	/* This is needed to support ODT properly on 2DPC */ -+	if (ctrl->tAA - ctrl->tCWL > 4) -+		ctrl->tCWL = ctrl->tAA - 4; -+ -+	/* If tCMD is invalid, use a guesstimate default */ -+	if (!ctrl->tCMD) { -+		ctrl->tCMD = MAX(ctrl->dpc[0], ctrl->dpc[1]); -+		printk(RAM_DEBUG, "tCMD was zero, picking a guesstimate value\n"); -+	} -+	ctrl->tCMD = clamp_u32(1, ctrl->tCMD, 3); -+ -+	/* -+	 * Print final timings. -+	 */ -+ -+	/* tCK is special */ -+	printk(BIOS_DEBUG, "Selected tCK          : %u ps\n", ctrl->tCK * 1000 / 256); -+ -+	/* Primary timings */ -+	printk(BIOS_DEBUG, "Selected tAA          : %uT\n", ctrl->tAA); -+	printk(BIOS_DEBUG, "Selected tWR          : %uT\n", ctrl->tWR); -+	printk(BIOS_DEBUG, "Selected tRCD         : %uT\n", ctrl->tRCD); -+	printk(BIOS_DEBUG, "Selected tRRD         : %uT\n", ctrl->tRRD); -+	printk(BIOS_DEBUG, "Selected tRP          : %uT\n", ctrl->tRP); -+	printk(BIOS_DEBUG, "Selected tRAS         : %uT\n", ctrl->tRAS); -+	printk(BIOS_DEBUG, "Selected tRC          : %uT\n", ctrl->tRC); -+	printk(BIOS_DEBUG, "Selected tRFC         : %uT\n", ctrl->tRFC); -+	printk(BIOS_DEBUG, "Selected tWTR         : %uT\n", ctrl->tWTR); -+	printk(BIOS_DEBUG, "Selected tRTP         : %uT\n", ctrl->tRTP); -+	printk(BIOS_DEBUG, "Selected tFAW         : %uT\n", ctrl->tFAW); -+	printk(BIOS_DEBUG, "Selected tCWL         : %uT\n", ctrl->tCWL); -+	printk(BIOS_DEBUG, "Selected tCMD         : %uT\n", ctrl->tCMD); -+ -+	/* Derived timings */ -+	printk(BIOS_DEBUG, "Selected tREFI        : %uT\n", ctrl->tREFI); -+	printk(BIOS_DEBUG, "Selected tXP          : %uT\n", ctrl->tXP); -+ -+	return RAMINIT_STATUS_SUCCESS; -+} ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0003-haswell-NRI-Configure-initial-MC-settings.patch b/config/coreboot/haswell/patches/0003-haswell-NRI-Configure-initial-MC-settings.patch deleted file mode 100644 index f44eb029..00000000 --- a/config/coreboot/haswell/patches/0003-haswell-NRI-Configure-initial-MC-settings.patch +++ /dev/null @@ -1,1593 +0,0 @@ -From 574f4965976b56f98a825dea71e919fefb2c8547 Mon Sep 17 00:00:00 2001 -From: Angel Pons <th3fanbus@gmail.com> -Date: Sat, 7 May 2022 17:22:07 +0200 -Subject: [PATCH 03/20] haswell NRI: Configure initial MC settings - -Program initial memory controller settings. Many of these values will be -adjusted later during training. - -Change-Id: If33846b51cb1bab5d0458fe626e13afb1bdc900e -Signed-off-by: Angel Pons <th3fanbus@gmail.com> ---- - .../intel/haswell/native_raminit/Makefile.mk  |   2 + - .../haswell/native_raminit/configure_mc.c     | 822 ++++++++++++++++++ - .../haswell/native_raminit/raminit_main.c     |   2 + - .../haswell/native_raminit/raminit_native.h   | 101 +++ - .../haswell/native_raminit/reg_structs.h      | 405 +++++++++ - .../haswell/native_raminit/timings_refresh.c  |  13 + - .../intel/haswell/registers/mchbar.h          |  94 ++ - 7 files changed, 1439 insertions(+) - create mode 100644 src/northbridge/intel/haswell/native_raminit/configure_mc.c - create mode 100644 src/northbridge/intel/haswell/native_raminit/reg_structs.h - create mode 100644 src/northbridge/intel/haswell/native_raminit/timings_refresh.c - -diff --git a/src/northbridge/intel/haswell/native_raminit/Makefile.mk b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -index 2769e0bbb4..fc55277a65 100644 ---- a/src/northbridge/intel/haswell/native_raminit/Makefile.mk -+++ b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -@@ -1,8 +1,10 @@ - ## SPDX-License-Identifier: GPL-2.0-or-later -  -+romstage-y += configure_mc.c - romstage-y += lookup_timings.c - romstage-y += init_mpll.c - romstage-y += io_comp_control.c - romstage-y += raminit_main.c - romstage-y += raminit_native.c - romstage-y += spd_bitmunching.c -+romstage-y += timings_refresh.c -diff --git a/src/northbridge/intel/haswell/native_raminit/configure_mc.c b/src/northbridge/intel/haswell/native_raminit/configure_mc.c -new file mode 100644 -index 0000000000..88249725a7 ---- /dev/null -+++ b/src/northbridge/intel/haswell/native_raminit/configure_mc.c -@@ -0,0 +1,822 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#include <assert.h> -+#include <commonlib/bsd/clamp.h> -+#include <console/console.h> -+#include <delay.h> -+#include <lib.h> -+#include <northbridge/intel/haswell/haswell.h> -+#include <string.h> -+#include <types.h> -+ -+#include "raminit_native.h" -+ -+static void program_misc_control(struct sysinfo *ctrl) -+{ -+	if (!is_hsw_ult()) -+		return; -+ -+	const union ddr_scram_misc_control_reg ddr_scram_misc_ctrl = { -+		.ddr_no_ch_interleave = !ctrl->dq_pins_interleaved, -+		.lpddr_mode           = ctrl->lpddr, -+		.cke_mapping_ch0      = ctrl->lpddr ? ctrl->lpddr_cke_rank_map[0] : 0, -+		.cke_mapping_ch1      = ctrl->lpddr ? ctrl->lpddr_cke_rank_map[1] : 0, -+	}; -+	mchbar_write32(DDR_SCRAM_MISC_CONTROL, ddr_scram_misc_ctrl.raw); -+} -+ -+static void program_mrc_revision(void) -+{ -+	mchbar_write32(MRC_REVISION, 0x01090000);	/* MRC 1.9.0 Build 0 */ -+} -+ -+static void program_ranks_used(struct sysinfo *ctrl) -+{ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		mchbar_write8(MC_INIT_STATE_ch(channel), ctrl->rankmap[channel]); -+		if (!does_ch_exist(ctrl, channel)) { -+			mchbar_write32(DDR_CLK_ch_RANKS_USED(channel), 0); -+			mchbar_write32(DDR_CTL_ch_CTL_RANKS_USED(channel), 0); -+			mchbar_write32(DDR_CKE_ch_CTL_RANKS_USED(channel), 0); -+			continue; -+		} -+		uint32_t clk_ranks_used = ctrl->rankmap[channel]; -+		if (ctrl->lpddr) { -+			/* With LPDDR, the clock usage goes by group instead */ -+			clk_ranks_used = 0; -+			for (uint8_t group = 0; group < NUM_GROUPS; group++) { -+				if (ctrl->dq_byte_map[channel][CT_ITERATION_CLOCK][group]) -+					clk_ranks_used |= BIT(group); -+			} -+		} -+		mchbar_write32(DDR_CLK_ch_RANKS_USED(channel), clk_ranks_used); -+ -+		uint32_t ctl_ranks_used = ctrl->rankmap[channel]; -+		if (is_hsw_ult()) { -+			/* Set ODT disable bits */ -+			/** TODO: May need to do this after JEDEC reset/init **/ -+			if (ctrl->lpddr && ctrl->lpddr_dram_odt) -+				ctl_ranks_used |= 2 << 4;	/* ODT is used on rank 0 */ -+			else -+				ctl_ranks_used |= 3 << 4; -+		} -+		mchbar_write32(DDR_CTL_ch_CTL_RANKS_USED(channel), ctl_ranks_used); -+ -+		uint32_t cke_ranks_used = ctrl->rankmap[channel]; -+		if (ctrl->lpddr) { -+			/* Use CKE-to-rank mapping for LPDDR */ -+			const uint8_t cke_rank_map = ctrl->lpddr_cke_rank_map[channel]; -+			cke_ranks_used = 0; -+			for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { -+				/* ULT only has 2 ranks per channel */ -+				if (rank >= 2) -+					break; -+ -+				if (!rank_in_ch(ctrl, rank, channel)) -+					continue; -+ -+				for (uint8_t cke = 0; cke < 4; cke++) { -+					if (rank == ((cke_rank_map >> cke) & 1)) -+						cke_ranks_used |= BIT(cke); -+				} -+			} -+		} -+		mchbar_write32(DDR_CKE_ch_CTL_RANKS_USED(channel), cke_ranks_used); -+	} -+} -+ -+static const uint8_t rxb_trad[2][5][4] = { -+	{	/* Vdd low */ -+		/* 1067 MT/s,    1333 MT/s,    1600 MT/s,    1867 MT/s,    2133 MT/s, */ -+		{4, 3, 3, 2}, {4, 4, 3, 2}, {5, 4, 3, 3}, {5, 4, 4, 3}, {5, 4, 4, 3}, -+	}, -+	{	/* Vdd hi */ -+		/* 1067 MT/s,    1333 MT/s,    1600 MT/s,    1867 MT/s,    2133 MT/s, */ -+		{4, 3, 3, 2}, {4, 4, 3, 2}, {5, 4, 3, 3}, {5, 4, 4, 3}, {4, 4, 3, 3}, -+	}, -+}; -+ -+static const uint8_t rxb_ultx[2][3][4] = { -+	{	/* Vdd low */ -+		/* 1067 MT/s,    1333 MT/s,    1600 MT/s, */ -+		{5, 6, 6, 5}, {5, 6, 6, 5}, {4, 6, 6, 6}, -+	}, -+	{	/* Vdd hi */ -+		/* 1067 MT/s,    1333 MT/s,    1600 MT/s, */ -+		{7, 6, 6, 5}, {7, 6, 6, 5}, {7, 6, 6, 6}, -+	}, -+}; -+ -+uint8_t get_rx_bias(const struct sysinfo *ctrl) -+{ -+	const bool is_ult = is_hsw_ult(); -+	const bool vddhi  = ctrl->vdd_mv > 1350; -+	const uint8_t max_rxf = is_ult ? ARRAY_SIZE(rxb_ultx[0]) : ARRAY_SIZE(rxb_trad[0]); -+	const uint8_t ref_clk = ctrl->base_freq == 133 ? 4 : 6; -+	const uint8_t rx_f    = clamp_s8(0, ctrl->multiplier - ref_clk, max_rxf - 1); -+	const uint8_t rx_cb   = mchbar_read32(DDR_CLK_CB_STATUS) & 0x3; -+	if (is_ult) -+		return rxb_ultx[vddhi][rx_f][rx_cb]; -+	else -+		return rxb_trad[vddhi][rx_f][rx_cb]; -+} -+ -+static void program_ddr_data(struct sysinfo *ctrl, const bool dis_odt_static, const bool vddhi) -+{ -+	const bool is_ult = is_hsw_ult(); -+ -+	for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { -+		if (!does_rank_exist(ctrl, rank)) -+			continue; -+ -+		const union ddr_data_rx_train_rank_reg rx_train = { -+			.rcven = 64, -+			.dqs_p = 32, -+			.dqs_n = 32, -+		}; -+		mchbar_write32(DDR_DATA_RX_TRAIN_RANK(rank), rx_train.raw); -+		mchbar_write32(DDR_DATA_RX_PER_BIT_RANK(rank), 0x88888888); -+ -+		const union ddr_data_tx_train_rank_reg tx_train = { -+			.tx_eq     = TXEQFULLDRV | 11, -+			.dq_delay  = 96, -+			.dqs_delay = 64, -+		}; -+		mchbar_write32(DDR_DATA_TX_TRAIN_RANK(rank), tx_train.raw); -+		mchbar_write32(DDR_DATA_TX_PER_BIT_RANK(rank), 0x88888888); -+ -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+				ctrl->tx_dq[channel][rank][byte] = tx_train.dq_delay; -+				ctrl->txdqs[channel][rank][byte] = tx_train.dqs_delay; -+				ctrl->tx_eq[channel][rank][byte] = tx_train.tx_eq; -+ -+				ctrl->rcven[channel][rank][byte]  = rx_train.rcven; -+				ctrl->rxdqsp[channel][rank][byte] = rx_train.dqs_p; -+				ctrl->rxdqsn[channel][rank][byte] = rx_train.dqs_n; -+				ctrl->rx_eq[channel][rank][byte]  = rx_train.rx_eq; -+			} -+		} -+	} -+	mchbar_write32(DDR_DATA_TX_XTALK, 0); -+	mchbar_write32(DDR_DATA_RX_OFFSET_VDQ, 0x88888888); -+	mchbar_write32(DDR_DATA_OFFSET_TRAIN, 0); -+	mchbar_write32(DDR_DATA_OFFSET_COMP, 0); -+ -+	const union ddr_data_control_0_reg data_control_0 = { -+		.internal_clocks_on = !is_ult, -+		.data_vccddq_hi     = vddhi, -+		.disable_odt_static = dis_odt_static, -+		.lpddr_mode         = ctrl->lpddr, -+		.odt_samp_extend_en = ctrl->lpddr, -+		.early_rleak_en     = ctrl->lpddr && ctrl->stepping >= STEPPING_C0, -+	}; -+	mchbar_write32(DDR_DATA_CONTROL_0, data_control_0.raw); -+ -+	const union ddr_data_control_1_reg data_control_1 = { -+		.dll_mask             = 1, -+		.rx_bias_ctl          = get_rx_bias(ctrl), -+		.odt_delay            = -2, -+		.odt_duration         = 7, -+		.sense_amp_delay      = -2, -+		.sense_amp_duration   = 7, -+	}; -+	mchbar_write32(DDR_DATA_CONTROL_1, data_control_1.raw); -+ -+	clear_data_offset_train_all(ctrl); -+ -+	/* Stagger byte turn-on to reduce dI/dT */ -+	const uint8_t byte_stagger[] = { 0, 4, 1, 5, 2, 6, 3, 7, 8 }; -+	const uint8_t latency = 2 * ctrl->tAA - 6; -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		union ddr_data_control_2_reg data_control_2 = { -+			.raw = 0, -+		}; -+		if (is_ult) { -+			data_control_2.rx_dqs_amp_offset = 8; -+			data_control_2.rx_clk_stg_num    = 0x1f; -+			data_control_2.leaker_comp       = ctrl->lpddr ? 3 : 0; -+		} -+		for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+			const uint8_t stg = latency * byte_stagger[byte] / ctrl->lanes; -+			data_control_2.rx_stagger_ctl = stg & 0x1f; -+			mchbar_write32(DQ_CONTROL_2(channel, byte), data_control_2.raw); -+			ctrl->data_offset_comp[channel][byte] = 0; -+			ctrl->dq_control_1[channel][byte] = data_control_1.raw; -+			ctrl->dq_control_2[channel][byte] = data_control_2.raw; -+		} -+		ctrl->dq_control_0[channel] = data_control_0.raw; -+	} -+} -+ -+static void program_vsshi_control(struct sysinfo *ctrl, const uint16_t vsshi_mv) -+{ -+	const uint32_t vsshi_control_reg = is_hsw_ult() ? 0x366c : 0x306c; -+	const union ddr_comp_vsshi_control_reg ddr_vsshi_control = { -+		.vsshi_target    = (vsshi_mv * 192) / ctrl->vdd_mv - 20, -+		.hi_bw_divider   = 1, -+		.lo_bw_divider   = 1, -+		.bw_error        = 2, -+		.panic_driver_en = 1, -+		.panic_voltage   = 24 / 8, /* Voltage in 8mV steps */ -+		.gain_boost      = 1, -+	}; -+	mchbar_write32(vsshi_control_reg, ddr_vsshi_control.raw); -+	mchbar_write32(DDR_COMP_VSSHI_CONTROL, ddr_vsshi_control.raw); -+} -+ -+static void calc_vt_slope_code(const uint16_t slope, uint8_t *best_a, uint8_t *best_b) -+{ -+	const int16_t coding[] = {0, -125, -62, -31, 250, 125, 62, 31}; -+	*best_a = 0; -+	*best_b = 0; -+	int16_t best_err = slope; -+	for (uint8_t b = 0; b < ARRAY_SIZE(coding); b++) { -+		for (uint8_t a = b; a < ARRAY_SIZE(coding); a++) { -+			int16_t	error = slope - (coding[a] + coding[b]); -+			if (error < 0) -+				error = -error; -+ -+			if (error < best_err) { -+				best_err = error; -+				*best_a = a; -+				*best_b = b; -+			} -+		} -+	} -+} -+ -+static void program_dimm_vref(struct sysinfo *ctrl, const uint16_t vccio_mv, const bool vddhi) -+{ -+	const bool is_ult = is_hsw_ult(); -+ -+	/* Static values for ULT */ -+	uint8_t vt_slope_a = 4; -+	uint8_t vt_slope_b = 0; -+	if (!is_ult) { -+		/* On non-ULT, compute best slope code */ -+		const uint16_t vt_slope = 1500 * vccio_mv / ctrl->vdd_mv - 1000; -+		calc_vt_slope_code(vt_slope, &vt_slope_a, &vt_slope_b); -+	} -+	const union ddr_data_vref_control_reg ddr_vref_control = { -+		.hi_bw_divider  = is_ult ? 0 : 3, -+		.lo_bw_divider  = 3, -+		.sample_divider = is_ult ? 1 : 3, -+		.slow_bw_error  = 1, -+		.hi_bw_enable   = 1, -+		.vt_slope_b     = vt_slope_b, -+		.vt_slope_a     = vt_slope_a, -+		.vt_offset      = 0, -+	}; -+	mchbar_write32(is_ult ? 0xf68 : 0xf6c, ddr_vref_control.raw); /* Use CH1 byte 7 */ -+ -+	const union ddr_data_vref_adjust_reg ddr_vref_adjust = { -+		.en_dimm_vref_ca  = 1, -+		.en_dimm_vref_ch0 = 1, -+		.en_dimm_vref_ch1 = 1, -+		.vccddq_hi_qnnn_h = vddhi, -+		.hi_z_timer_ctrl  = 3, -+	}; -+	ctrl->dimm_vref = ddr_vref_adjust; -+	mchbar_write32(DDR_DATA_VREF_ADJUST, ddr_vref_adjust.raw); -+} -+ -+static uint32_t pi_code(const uint32_t code) -+{ -+	return code << 21 | code << 14 | code << 7 | code << 0; -+} -+ -+static void program_ddr_ca(struct sysinfo *ctrl, const bool vddhi) -+{ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		const union ddr_clk_controls_reg ddr_clk_controls = { -+			.dll_mask   = 1, -+			.vccddq_hi  = vddhi, -+			.lpddr_mode = ctrl->lpddr, -+		}; -+		mchbar_write32(DDR_CLK_ch_CONTROLS(channel), ddr_clk_controls.raw); -+ -+		const union ddr_cmd_controls_reg ddr_cmd_controls = { -+			.dll_mask         = 1, -+			.vccddq_hi        = vddhi, -+			.lpddr_mode       = ctrl->lpddr, -+			.early_weak_drive = 3, -+			.cmd_tx_eq        = 1, -+		}; -+		mchbar_write32(DDR_CMD_ch_CONTROLS(channel), ddr_cmd_controls.raw); -+ -+		const union ddr_cke_ctl_controls_reg ddr_cke_controls = { -+			.dll_mask         = 1, -+			.vccddq_hi        = vddhi, -+			.lpddr_mode       = ctrl->lpddr, -+			.early_weak_drive = 3, -+			.cmd_tx_eq        = 1, -+			.ctl_tx_eq        = 1, -+			.ctl_sr_drv       = 2, -+		}; -+		mchbar_write32(DDR_CKE_ch_CTL_CONTROLS(channel), ddr_cke_controls.raw); -+ -+		const union ddr_cke_ctl_controls_reg ddr_ctl_controls = { -+			.dll_mask       = 1, -+			.vccddq_hi      = vddhi, -+			.lpddr_mode     = ctrl->lpddr, -+			.ctl_tx_eq      = 1, -+			.ctl_sr_drv     = 2, -+			.la_drv_en_ovrd = 1,	/* Must be set on ULT */ -+		}; -+		mchbar_write32(DDR_CTL_ch_CTL_CONTROLS(channel), ddr_ctl_controls.raw); -+ -+		const uint8_t cmd_pi = ctrl->lpddr ? 96 : 64; -+		mchbar_write32(DDR_CMD_ch_PI_CODING(channel), pi_code(cmd_pi)); -+		mchbar_write32(DDR_CKE_ch_CMD_PI_CODING(channel), pi_code(cmd_pi)); -+		mchbar_write32(DDR_CKE_CTL_ch_CTL_PI_CODING(channel), pi_code(64)); -+		mchbar_write32(DDR_CLK_ch_PI_CODING(channel), pi_code(64)); -+ -+		mchbar_write32(DDR_CMD_ch_COMP_OFFSET(channel), 0); -+		mchbar_write32(DDR_CLK_ch_COMP_OFFSET(channel), 0); -+		mchbar_write32(DDR_CKE_CTL_ch_CTL_COMP_OFFSET(channel), 0); -+ -+		for (uint8_t group = 0; group < NUM_GROUPS; group++) { -+			ctrl->cke_cmd_pi_code[channel][group] = cmd_pi; -+			ctrl->cmd_north_pi_code[channel][group] = cmd_pi; -+			ctrl->cmd_south_pi_code[channel][group] = cmd_pi; -+		} -+		for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { -+			ctrl->clk_pi_code[channel][rank] = 64; -+			ctrl->ctl_pi_code[channel][rank] = 64; -+		} -+	} -+} -+ -+enum { -+	RCOMP_RD_ODT = 0, -+	RCOMP_WR_DS_DQ, -+	RCOMP_WR_DS_CMD, -+	RCOMP_WR_DS_CTL, -+	RCOMP_WR_DS_CLK, -+	RCOMP_MAX_CODES, -+}; -+ -+struct rcomp_info { -+	uint8_t resistor; -+	uint8_t sz_steps; -+	uint8_t target_r; -+	int8_t result; -+}; -+ -+static void program_rcomp_vref(struct sysinfo *ctrl, const bool dis_odt_static) -+{ -+	const bool is_ult = is_hsw_ult(); -+	/* -+	 * +-------------------------------+ -+	 * | Rcomp resistor values in ohms | -+	 * +-----------+------+------+-----+ -+	 * | Ball name | Trad | ULTX | Use | -+	 * +-----------+------+------+-----+ -+	 * | SM_RCOMP0 | 100  | 200  | CMD | -+	 * | SM_RCOMP1 |  75  | 120  | DQ  | -+	 * | SM_RCOMP2 | 100  | 100  | ODT | -+	 * +-----------+------+------+-----+ -+	 */ -+	struct rcomp_info rcomp_cfg[RCOMP_MAX_CODES] = { -+		[RCOMP_RD_ODT] = { -+			.resistor = 50, -+			.sz_steps = 96, -+			.target_r = 50, -+		}, -+		[RCOMP_WR_DS_DQ] = { -+			.resistor = 25, -+			.sz_steps = 64, -+			.target_r = 33, -+		}, -+		[RCOMP_WR_DS_CMD] = { -+			.resistor = 20, -+			.sz_steps = 64, -+			.target_r = 20, -+		}, -+		[RCOMP_WR_DS_CTL] = { -+			.resistor = 20, -+			.sz_steps = 64, -+			.target_r = 20, -+		}, -+		[RCOMP_WR_DS_CLK] = { -+			.resistor = 25, -+			.sz_steps = 64, -+			.target_r = 29, -+		}, -+	}; -+	if (is_ult) { -+		rcomp_cfg[RCOMP_WR_DS_DQ].resistor = 40; -+		rcomp_cfg[RCOMP_WR_DS_DQ].target_r = 40; -+		rcomp_cfg[RCOMP_WR_DS_CLK].resistor = 40; -+	} else if (ctrl->dpc[0] == 2 || ctrl->dpc[1] == 2) { -+		rcomp_cfg[RCOMP_RD_ODT].target_r = 60; -+	} -+	for (uint8_t i = 0; i < RCOMP_MAX_CODES; i++) { -+		struct rcomp_info *const r = &rcomp_cfg[i]; -+		const int32_t div = 2 * (r->resistor + r->target_r); -+		assert(div); -+		const int32_t vref = (r->sz_steps * (r->resistor - r->target_r)) / div; -+ -+		/* DqOdt is 5 bits wide, the other Rcomp targets are 4 bits wide */ -+		const int8_t comp_limit = i == RCOMP_RD_ODT ? 16 : 8; -+		r->result = clamp_s32(-comp_limit, vref, comp_limit - 1); -+	} -+	const union ddr_comp_ctl_0_reg ddr_comp_ctl_0 = { -+		.disable_odt_static = dis_odt_static, -+		.dq_drv_vref        = rcomp_cfg[RCOMP_WR_DS_DQ].result, -+		.dq_odt_vref        = rcomp_cfg[RCOMP_RD_ODT].result, -+		.cmd_drv_vref       = rcomp_cfg[RCOMP_WR_DS_CMD].result, -+		.ctl_drv_vref       = rcomp_cfg[RCOMP_WR_DS_CTL].result, -+		.clk_drv_vref       = rcomp_cfg[RCOMP_WR_DS_CLK].result, -+	}; -+	ctrl->comp_ctl_0 = ddr_comp_ctl_0; -+	mchbar_write32(DDR_COMP_CTL_0, ctrl->comp_ctl_0.raw); -+} -+ -+enum { -+	SCOMP_DQ = 0, -+	SCOMP_CMD, -+	SCOMP_CTL, -+	SCOMP_CLK, -+	SCOMP_MAX_CODES, -+}; -+ -+static void program_slew_rates(struct sysinfo *ctrl, const bool vddhi) -+{ -+	const uint8_t min_cycle_delay[SCOMP_MAX_CODES] = { 46, 70, 70, 46 }; -+	uint8_t buffer_stage_delay_ps[SCOMP_MAX_CODES] = { 59, 53, 53, 53 }; -+	uint16_t comp_slew_rate_codes[SCOMP_MAX_CODES]; -+ -+	/* CMD Slew Rate = 1.8 for 2N */ -+	if (ctrl->tCMD == 2) -+		buffer_stage_delay_ps[SCOMP_CMD] = 89; -+ -+	/* CMD Slew Rate = 4 V/ns for double-pumped CMD bus */ -+	if (ctrl->lpddr) -+		buffer_stage_delay_ps[SCOMP_CMD] = 63; -+ -+	for (uint8_t i = 0; i < SCOMP_MAX_CODES; i++) { -+		uint16_t stages = DIV_ROUND_CLOSEST(ctrl->qclkps, buffer_stage_delay_ps[i]); -+		if (stages < 5) -+			stages = 5; -+ -+		bool dll_pc = buffer_stage_delay_ps[i] < min_cycle_delay[i] || stages > 16; -+ -+		/* Lock DLL... */ -+		if (dll_pc) -+			comp_slew_rate_codes[i] = stages / 2 - 1;        /* to a phase */ -+		else -+			comp_slew_rate_codes[i] = (stages - 1) | BIT(4); /* to a cycle */ -+	} -+	union ddr_comp_ctl_1_reg ddr_comp_ctl_1 = { -+		.dq_scomp       = comp_slew_rate_codes[SCOMP_DQ], -+		.cmd_scomp      = comp_slew_rate_codes[SCOMP_CMD], -+		.ctl_scomp      = comp_slew_rate_codes[SCOMP_CTL], -+		.clk_scomp      = comp_slew_rate_codes[SCOMP_CLK], -+		.vccddq_hi      = vddhi, -+	}; -+	ctrl->comp_ctl_1 = ddr_comp_ctl_1; -+	mchbar_write32(DDR_COMP_CTL_1, ctrl->comp_ctl_1.raw); -+} -+ -+static uint32_t ln_x100(const uint32_t input_x100) -+{ -+	uint32_t val = input_x100; -+	uint32_t ret = 0; -+	while (val > 271) { -+		val = (val * 1000) / 2718; -+		ret += 100; -+	} -+	return ret + (-16 * val * val + 11578 * val - 978860) / 10000; -+} -+ -+static uint32_t compute_vsshi_vref(struct sysinfo *ctrl, const uint32_t vsshi_tgt, bool up) -+{ -+	const uint32_t delta = 15; -+	const uint32_t c_die_vsshi = 2000; -+	const uint32_t r_cmd_ref = 100 * 10; -+	const uint32_t offset = up ? 64 : 0; -+	const uint32_t ln_vsshi = ln_x100((100 * vsshi_tgt) / (vsshi_tgt - delta)); -+	const uint32_t r_target = (ctrl->qclkps * 2000) / (c_die_vsshi * ln_vsshi); -+	const uint32_t r_dividend = 128 * (up ? r_cmd_ref : r_target); -+	return r_dividend / (r_cmd_ref + r_target) - offset; -+} -+ -+static void program_vsshi(struct sysinfo *ctrl, const uint16_t vccio_mv, const uint16_t vsshi) -+{ -+	const uint16_t vsshi_down = vsshi + 24; /* Panic threshold of 24 mV */ -+	const uint16_t vsshi_up = vccio_mv - vsshi_down; -+	const union ddr_comp_vsshi_reg ddr_comp_vsshi = { -+		.panic_drv_down_vref = compute_vsshi_vref(ctrl, vsshi_down, false), -+		.panic_drv_up_vref   = compute_vsshi_vref(ctrl, vsshi_up, true), -+		.vt_offset           = 128 * 450 / vccio_mv / 2, -+		.vt_slope_a          = 4, -+	}; -+	mchbar_write32(DDR_COMP_VSSHI, ddr_comp_vsshi.raw); -+} -+ -+static void program_misc(struct sysinfo *ctrl) -+{ -+	ctrl->misc_control_0.raw = mchbar_read32(DDR_SCRAM_MISC_CONTROL); -+	ctrl->misc_control_0.weaklock_latency = 12; -+	ctrl->misc_control_0.wl_sleep_cycles  =  5; -+	ctrl->misc_control_0.wl_wake_cycles   =  2; -+	mchbar_write32(DDR_SCRAM_MISC_CONTROL, ctrl->misc_control_0.raw); -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		/* Keep scrambling disabled for training */ -+		mchbar_write32(DDR_SCRAMBLE_ch(channel), 0); -+	} -+} -+ -+/* Very weird, application-specific function */ -+static void override_comp(uint32_t value, uint32_t width, uint32_t shift, uint32_t offset) -+{ -+	const uint32_t mask = (1 << width) - 1; -+	uint32_t reg32 = mchbar_read32(offset); -+	reg32 &= ~(mask << shift); -+	reg32 |= (value << shift); -+	mchbar_write32(offset, reg32); -+} -+ -+static void program_ls_comp(struct sysinfo *ctrl) -+{ -+	/* Disable periodic COMP */ -+	const union pcu_comp_reg m_comp = { -+		.comp_disable  = 1, -+		.comp_interval = COMP_INT, -+		.comp_force    = 1, -+	}; -+	mchbar_write32(M_COMP, m_comp.raw); -+	udelay(10); -+ -+	/* Override level shifter compensation */ -+	const uint32_t ls_comp = 2; -+	override_comp(ls_comp, 3, 28, DDR_DATA_RCOMP_DATA_1); -+	override_comp(ls_comp, 3, 24, DDR_CMD_COMP); -+	override_comp(ls_comp, 3, 24, DDR_CKE_CTL_COMP); -+	override_comp(ls_comp, 3, 23, DDR_CLK_COMP); -+	override_comp(ls_comp, 3, 28, DDR_COMP_DATA_COMP_1); -+	override_comp(ls_comp, 3, 24, DDR_COMP_CMD_COMP); -+	override_comp(ls_comp, 4, 24, DDR_COMP_CTL_COMP); -+	override_comp(ls_comp, 4, 23, DDR_COMP_CLK_COMP); -+	override_comp(ls_comp, 3, 24, DDR_COMP_OVERRIDE); -+ -+	/* Manually update the COMP values */ -+	union ddr_scram_misc_control_reg ddr_scram_misc_ctrl = ctrl->misc_control_0; -+	ddr_scram_misc_ctrl.force_comp_update = 1; -+	mchbar_write32(DDR_SCRAM_MISC_CONTROL, ddr_scram_misc_ctrl.raw); -+ -+	/* Use a fixed offset between ODT Up/Dn */ -+	const union ddr_comp_data_comp_1_reg data_comp_1 = { -+		.raw = mchbar_read32(DDR_COMP_DATA_COMP_1), -+	}; -+	const uint32_t odt_offset = data_comp_1.rcomp_odt_down - data_comp_1.rcomp_odt_up; -+	ctrl->comp_ctl_0.odt_up_down_off  = odt_offset; -+	ctrl->comp_ctl_0.fixed_odt_offset = 1; -+	mchbar_write32(DDR_COMP_CTL_0, ctrl->comp_ctl_0.raw); -+} -+ -+/** TODO: Deduplicate PCODE stuff, it's already implemented in CPU code **/ -+static bool pcode_ready(void) -+{ -+	const unsigned int delay_step = 10; -+	for (unsigned int i = 0; i < 1000; i += delay_step) { -+		if (!(mchbar_read32(BIOS_MAILBOX_INTERFACE) & MAILBOX_RUN_BUSY)) -+			return true; -+ -+		udelay(delay_step); -+	}; -+	return false; -+} -+ -+static uint32_t pcode_mailbox_read(const uint32_t command) -+{ -+	if (!pcode_ready()) { -+		printk(BIOS_ERR, "PCODE: mailbox timeout on wait ready\n"); -+		return 0; -+	} -+	mchbar_write32(BIOS_MAILBOX_INTERFACE, command | MAILBOX_RUN_BUSY); -+	if (!pcode_ready()) { -+		printk(BIOS_ERR, "PCODE: mailbox timeout on completion\n"); -+		return 0; -+	} -+	return mchbar_read32(BIOS_MAILBOX_DATA); -+} -+ -+static int pcode_mailbox_write(const uint32_t command, const uint32_t data) -+{ -+	if (!pcode_ready()) { -+		printk(BIOS_ERR, "PCODE: mailbox timeout on wait ready\n"); -+		return -1; -+	} -+	mchbar_write32(BIOS_MAILBOX_DATA, data); -+	mchbar_write32(BIOS_MAILBOX_INTERFACE, command | MAILBOX_RUN_BUSY); -+	if (!pcode_ready()) { -+		printk(BIOS_ERR, "PCODE: mailbox timeout on completion\n"); -+		return -1; -+	} -+	return 0; -+} -+ -+static void enable_2x_refresh(struct sysinfo *ctrl) -+{ -+	if (!CONFIG(ENABLE_DDR_2X_REFRESH)) -+		return; -+ -+	printk(BIOS_DEBUG, "Enabling 2x Refresh\n"); -+	const bool asr = ctrl->flags.asr; -+	const bool lpddr = ctrl->lpddr; -+ -+	/* Mutually exclusive */ -+	assert(!asr || !lpddr); -+	if (!asr) { -+		uint32_t reg32 = pcode_mailbox_read(MAILBOX_BIOS_CMD_READ_DDR_2X_REFRESH); -+		if (!(reg32 & BIT(31))) {	/** TODO: What to do if this is locked? **/ -+			reg32 |= BIT(0);	/* Enable 2x refresh */ -+			reg32 |= BIT(31);	/* Lock */ -+ -+			if (lpddr) -+				reg32 |= 4 << 1;	/* LPDDR MR4 1/2 tREFI */ -+ -+			if (pcode_mailbox_write(MAILBOX_BIOS_CMD_WRITE_DDR_2X_REFRESH, reg32)) -+				printk(BIOS_ERR, "Could not enable Mailbox 2x Refresh\n"); -+		} -+		if (!lpddr) -+			return; -+	} -+	assert(asr || lpddr); -+	uint16_t refi_reduction = 50; -+	if (lpddr) { -+		refi_reduction = 97; -+		mchbar_clrbits32(PCU_DDR_PTM_CTL, 1 << 7); /* DISABLE_DRAM_TS */ -+	} -+	/** TODO: Remember why this is only done on cold boots **/ -+	if (ctrl->bootmode == BOOTMODE_COLD) { -+		ctrl->tREFI *= refi_reduction; -+		ctrl->tREFI /= 100; -+	} -+} -+ -+static void set_pcu_ddr_voltage(const uint16_t vdd_mv) -+{ -+	/** TODO: Handle other voltages? **/ -+	uint32_t pcu_ddr_voltage; -+	switch (vdd_mv) { -+	case 1200: -+		pcu_ddr_voltage = 3; -+		break; -+	case 1350: -+		pcu_ddr_voltage = 1; -+		break; -+	default: -+	case 1500: -+		pcu_ddr_voltage = 0; -+		break; -+	} -+	/* Set bits 0..2 */ -+	mchbar_write32(PCU_DDR_VOLTAGE, pcu_ddr_voltage); -+} -+ -+static void program_scheduler(struct sysinfo *ctrl) -+{ -+	/* -+	 * ZQ calibration needs to be serialized for LPDDR3. Otherwise, -+	 * the processor issues LPDDR3 ZQ calibration in parallel when -+	 * exiting Package C7 or deeper. This causes problems for dual -+	 * and quad die packages since all ranks share the same ZQ pin. -+	 * -+	 * Erratum HSM94: LPDDR3 ZQ Calibration Following Deep Package -+	 * C-state Exit May Lead to Unpredictable System Behavior -+	 */ -+	const union mcscheds_cbit_reg mcscheds_cbit = { -+		.dis_write_gap = 1, -+		.dis_odt       = is_hsw_ult() && !(ctrl->lpddr && ctrl->lpddr_dram_odt), -+		.serialize_zq  = ctrl->lpddr, -+	}; -+	mchbar_write32(MCSCHEDS_CBIT, mcscheds_cbit.raw); -+	mchbar_write32(MCMNTS_SC_WDBWM, 0x553c3038); -+	if (ctrl->lpddr) { -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!does_ch_exist(ctrl, channel)) -+				continue; -+ -+			union mcmain_command_rate_limit_reg cmd_rate_limit = { -+				.raw = mchbar_read32(COMMAND_RATE_LIMIT_ch(channel)), -+			}; -+			cmd_rate_limit.enable_cmd_limit = 1; -+			cmd_rate_limit.cmd_rate_limit   = 3; -+			mchbar_write32(COMMAND_RATE_LIMIT_ch(channel), cmd_rate_limit.raw); -+		} -+	} -+} -+ -+static uint8_t biggest_channel(const struct sysinfo *const ctrl) -+{ -+	_Static_assert(NUM_CHANNELS == 2, "Code assumes exactly two channels"); -+	return !!(ctrl->channel_size_mb[0] < ctrl->channel_size_mb[1]); -+} -+ -+static void dram_zones(struct sysinfo *ctrl) -+{ -+	/** TODO: Activate channel hash here, if enabled **/ -+	const uint8_t biggest = biggest_channel(ctrl); -+	const uint8_t smaller = !biggest; -+ -+	/** TODO: Use stacked mode if Memory Trace is enabled **/ -+	const union mad_chnl_reg mad_channel = { -+		.ch_a       = biggest, -+		.ch_b       = smaller, -+		.ch_c       = 2, -+		.lpddr_mode = ctrl->lpddr, -+	}; -+	mchbar_write32(MAD_CHNL, mad_channel.raw); -+ -+	const uint8_t channel_b_zone_size = ctrl->channel_size_mb[smaller] / 256; -+	const union mad_zr_reg mad_zr = { -+		.ch_b_double = channel_b_zone_size * 2, -+		.ch_b_single = channel_b_zone_size, -+	}; -+	mchbar_write32(MAD_ZR, mad_zr.raw); -+} -+ -+static uint8_t biggest_dimm(const struct raminit_dimm_info *dimms) -+{ -+	_Static_assert(NUM_SLOTS <= 2, "Code assumes at most two DIMMs per channel."); -+	if (NUM_SLOTS == 1) -+		return 0; -+ -+	return !!(dimms[0].data.size_mb < dimms[1].data.size_mb); -+} -+ -+static void dram_dimm_mapping(struct sysinfo *ctrl) -+{ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) { -+			const union mad_dimm_reg mad_dimm = { -+				.rank_interleave = 1, -+				.enh_interleave  = 1, -+			}; -+			mchbar_write32(MAD_DIMM(channel), mad_dimm.raw); -+			continue; -+		} -+		const uint8_t biggest = biggest_dimm(ctrl->dimms[channel]); -+		const uint8_t smaller = !biggest; -+		const struct dimm_attr_ddr3_st *dimm_a = &ctrl->dimms[channel][biggest].data; -+		const struct dimm_attr_ddr3_st *dimm_b = &ctrl->dimms[channel][smaller].data; -+		union mad_dimm_reg mad_dimm = { -+			.dimm_a_size     = dimm_a->size_mb / 256, -+			.dimm_b_size     = dimm_b->size_mb / 256, -+			.dimm_a_sel      = biggest, -+			.dimm_a_ranks    = dimm_a->ranks == 2, -+			.dimm_b_ranks    = dimm_b->ranks == 2, -+			.dimm_a_width    = dimm_a->width == 16, -+			.dimm_b_width    = dimm_b->width == 16, -+			.rank_interleave = 1, -+			.enh_interleave  = 1, -+			.ecc_mode        = 0,	/* Do not enable ECC yet */ -+		}; -+		if (is_hsw_ult()) -+			mad_dimm.dimm_b_width = mad_dimm.dimm_a_width; -+ -+		mchbar_write32(MAD_DIMM(channel), mad_dimm.raw); -+		if (ctrl->lpddr) -+			die("%s: Missing LPDDR support (LPDDR_MR_PARAMS)\n", __func__); -+	} -+} -+ -+enum raminit_status configure_mc(struct sysinfo *ctrl) -+{ -+	const uint16_t vccio_mv = 1000; -+	const uint16_t vsshi_mv = ctrl->vdd_mv - 950; -+	const bool dis_odt_static = is_hsw_ult(); /* Disable static ODT legs on ULT */ -+	const bool vddhi = ctrl->vdd_mv > 1350; -+ -+	program_misc_control(ctrl); -+	program_mrc_revision(); -+	program_ranks_used(ctrl); -+	program_ddr_data(ctrl, dis_odt_static, vddhi); -+	program_vsshi_control(ctrl, vsshi_mv); -+	program_dimm_vref(ctrl, vccio_mv, vddhi); -+	program_ddr_ca(ctrl, vddhi); -+	program_rcomp_vref(ctrl, dis_odt_static); -+	program_slew_rates(ctrl, vddhi); -+	program_vsshi(ctrl, vccio_mv, vsshi_mv); -+	program_misc(ctrl); -+	program_ls_comp(ctrl); -+	enable_2x_refresh(ctrl); -+	set_pcu_ddr_voltage(ctrl->vdd_mv); -+	configure_timings(ctrl); -+	configure_refresh(ctrl); -+	program_scheduler(ctrl); -+	dram_zones(ctrl); -+	dram_dimm_mapping(ctrl); -+ -+	return RAMINIT_STATUS_SUCCESS; -+} -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_main.c b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -index 2fea658415..fcc981ad04 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_main.c -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -@@ -22,6 +22,7 @@ static const struct task_entry cold_boot[] = { - 	{ collect_spd_info,                                       true, "PROCSPD",    }, - 	{ initialise_mpll,                                        true, "INITMPLL",   }, - 	{ convert_timings,                                        true, "CONVTIM",    }, -+	{ configure_mc,                                           true, "CONFMC",     }, - }; -  - /* Return a generic stepping value to make stepping checks simpler */ -@@ -53,6 +54,7 @@ static void initialize_ctrl(struct sysinfo *ctrl) -  - 	ctrl->cpu = cpu_get_cpuid(); - 	ctrl->stepping = get_stepping(ctrl->cpu); -+	ctrl->vdd_mv = is_hsw_ult() ? 1350 : 1500; /** FIXME: Hardcoded, does it matter? **/ - 	ctrl->dq_pins_interleaved = cfg->dq_pins_interleaved; - 	ctrl->bootmode = bootmode; - } -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -index 01e5ed1bd6..aa86b9aa39 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -@@ -3,15 +3,40 @@ - #ifndef HASWELL_RAMINIT_NATIVE_H - #define HASWELL_RAMINIT_NATIVE_H -  -+#include <assert.h> - #include <device/dram/ddr3.h> - #include <northbridge/intel/haswell/haswell.h> -+#include <string.h> -+#include <types.h> -+ -+#include "reg_structs.h" -  - #define SPD_LEN 256 -  -+/* Each channel has 4 ranks, spread across 2 slots */ -+#define NUM_SLOTRANKS		4 -+ -+#define NUM_GROUPS		2 -+ - /* 8 data lanes + 1 ECC lane */ - #define NUM_LANES		9 - #define NUM_LANES_NO_ECC	8 -  -+#define COMP_INT		10 -+ -+/* Always use 12 legs for emphasis (not trained) */ -+#define TXEQFULLDRV		(3 << 4) -+ -+enum command_training_iteration { -+	CT_ITERATION_CLOCK = 0, -+	CT_ITERATION_CMD_NORTH, -+	CT_ITERATION_CMD_SOUTH, -+	CT_ITERATION_CKE, -+	CT_ITERATION_CTL, -+	CT_ITERATION_CMD_VREF, -+	MAX_CT_ITERATION, -+}; -+ - enum raminit_boot_mode { - 	BOOTMODE_COLD, - 	BOOTMODE_WARM, -@@ -57,6 +82,9 @@ struct sysinfo { - 	 * LPDDR-specific functions have stubs which will halt upon execution. - 	 */ - 	bool lpddr; -+	bool lpddr_dram_odt; -+	uint8_t lpddr_cke_rank_map[NUM_CHANNELS]; -+	uint8_t dq_byte_map[NUM_CHANNELS][MAX_CT_ITERATION][2]; -  - 	struct raminit_dimm_info dimms[NUM_CHANNELS][NUM_SLOTS]; - 	union dimm_flags_ddr3_st flags; -@@ -93,16 +121,89 @@ struct sysinfo { - 	uint32_t mem_clock_mhz; - 	uint32_t mem_clock_fs;		/* Memory clock period in femtoseconds */ - 	uint32_t qclkps;		/* Quadrature clock period in picoseconds */ -+ -+	uint16_t vdd_mv; -+ -+	union ddr_scram_misc_control_reg misc_control_0; -+ -+	union ddr_comp_ctl_0_reg comp_ctl_0; -+	union ddr_comp_ctl_1_reg comp_ctl_1; -+ -+	union ddr_data_vref_adjust_reg dimm_vref; -+ -+	uint32_t data_offset_train[NUM_CHANNELS][NUM_LANES]; -+	uint32_t data_offset_comp[NUM_CHANNELS][NUM_LANES]; -+ -+	uint32_t dq_control_0[NUM_CHANNELS]; -+	uint32_t dq_control_1[NUM_CHANNELS][NUM_LANES]; -+	uint32_t dq_control_2[NUM_CHANNELS][NUM_LANES]; -+ -+	uint16_t tx_dq[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; -+	uint16_t txdqs[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; -+	uint8_t  tx_eq[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; -+ -+	uint16_t rcven[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; -+	uint8_t  rx_eq[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; -+	uint8_t rxdqsp[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; -+	uint8_t rxdqsn[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; -+	int8_t  rxvref[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; -+ -+	uint8_t clk_pi_code[NUM_CHANNELS][NUM_SLOTRANKS]; -+	uint8_t ctl_pi_code[NUM_CHANNELS][NUM_SLOTRANKS]; -+	uint8_t cke_pi_code[NUM_CHANNELS][NUM_SLOTRANKS]; -+ -+	uint8_t cke_cmd_pi_code[NUM_CHANNELS][NUM_GROUPS]; -+	uint8_t cmd_north_pi_code[NUM_CHANNELS][NUM_GROUPS]; -+	uint8_t cmd_south_pi_code[NUM_CHANNELS][NUM_GROUPS]; - }; -  -+static inline bool is_hsw_ult(void) -+{ -+	return CONFIG(INTEL_LYNXPOINT_LP); -+} -+ -+static inline bool rank_in_mask(uint8_t rank, uint8_t rankmask) -+{ -+	assert(rank < NUM_SLOTRANKS); -+	return !!(BIT(rank) & rankmask); -+} -+ -+static inline bool does_ch_exist(const struct sysinfo *ctrl, uint8_t channel) -+{ -+	return !!ctrl->dpc[channel]; -+} -+ -+static inline bool does_rank_exist(const struct sysinfo *ctrl, uint8_t rank) -+{ -+	return rank_in_mask(rank, ctrl->rankmap[0] | ctrl->rankmap[1]); -+} -+ -+static inline bool rank_in_ch(const struct sysinfo *ctrl, uint8_t rank, uint8_t channel) -+{ -+	assert(channel < NUM_CHANNELS); -+	return rank_in_mask(rank, ctrl->rankmap[channel]); -+} -+ -+/** TODO: Handling of data_offset_train could be improved, also coupled with reg updates **/ -+static inline void clear_data_offset_train_all(struct sysinfo *ctrl) -+{ -+	memset(ctrl->data_offset_train, 0, sizeof(ctrl->data_offset_train)); -+} -+ - void raminit_main(enum raminit_boot_mode bootmode); -  - enum raminit_status collect_spd_info(struct sysinfo *ctrl); - enum raminit_status initialise_mpll(struct sysinfo *ctrl); - enum raminit_status convert_timings(struct sysinfo *ctrl); -+enum raminit_status configure_mc(struct sysinfo *ctrl); -+ -+void configure_timings(struct sysinfo *ctrl); -+void configure_refresh(struct sysinfo *ctrl); -  - enum raminit_status wait_for_first_rcomp(void); -  -+uint8_t get_rx_bias(const struct sysinfo *ctrl); -+ - uint8_t get_tCWL(uint32_t mem_clock_mhz); - uint32_t get_tREFI(uint32_t mem_clock_mhz); - uint32_t get_tXP(uint32_t mem_clock_mhz); -diff --git a/src/northbridge/intel/haswell/native_raminit/reg_structs.h b/src/northbridge/intel/haswell/native_raminit/reg_structs.h -new file mode 100644 -index 0000000000..d11cda4b3d ---- /dev/null -+++ b/src/northbridge/intel/haswell/native_raminit/reg_structs.h -@@ -0,0 +1,405 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#ifndef HASWELL_RAMINIT_REG_STRUCTS_H -+#define HASWELL_RAMINIT_REG_STRUCTS_H -+ -+union ddr_data_rx_train_rank_reg { -+	struct __packed { -+		uint32_t rcven : 9; // Bits  8:0 -+		uint32_t dqs_p : 6; // Bits 14:9 -+		uint32_t rx_eq : 5; // Bits 19:15 -+		uint32_t dqs_n : 6; // Bits 25:20 -+		int32_t  vref  : 6; // Bits 31:26 -+	}; -+	uint32_t raw; -+}; -+ -+union ddr_data_tx_train_rank_reg { -+	struct __packed { -+		uint32_t dq_delay  : 9; // Bits  8:0 -+		uint32_t dqs_delay : 9; // Bits 17:9 -+		uint32_t           : 2; // Bits 19:18 -+		uint32_t tx_eq     : 6; // Bits 25:20 -+		uint32_t           : 6; // Bits 31:26 -+	}; -+	uint32_t raw; -+}; -+ -+union ddr_data_control_0_reg { -+	struct __packed { -+		uint32_t rx_training_mode      : 1; // Bits  0:0 -+		uint32_t wl_training_mode      : 1; // Bits  1:1 -+		uint32_t rl_training_mode      : 1; // Bits  2:2 -+		uint32_t samp_train_mode       : 1; // Bits  3:3 -+		uint32_t tx_on                 : 1; // Bits  4:4 -+		uint32_t rf_on                 : 1; // Bits  5:5 -+		uint32_t rx_pi_on              : 1; // Bits  6:6 -+		uint32_t tx_pi_on              : 1; // Bits  7:7 -+		uint32_t internal_clocks_on    : 1; // Bits  8:8 -+		uint32_t repeater_clocks_on    : 1; // Bits  9:9 -+		uint32_t tx_disable            : 1; // Bits 10:10 -+		uint32_t rx_disable            : 1; // Bits 11:11 -+		uint32_t tx_long               : 1; // Bits 12:12 -+		uint32_t rx_dqs_ctle           : 2; // Bits 14:13 -+		uint32_t rx_read_pointer       : 3; // Bits 17:15 -+		uint32_t driver_segment_enable : 1; // Bits 18:18 -+		uint32_t data_vccddq_hi        : 1; // Bits 19:19 -+		uint32_t read_rf_rd            : 1; // Bits 20:20 -+		uint32_t read_rf_wr            : 1; // Bits 21:21 -+		uint32_t read_rf_rank          : 2; // Bits 23:22 -+		uint32_t force_odt_on          : 1; // Bits 24:24 -+		uint32_t odt_samp_off          : 1; // Bits 25:25 -+		uint32_t disable_odt_static    : 1; // Bits 26:26 -+		uint32_t ddr_cr_force_odt_on   : 1; // Bits 27:27 -+		uint32_t lpddr_mode            : 1; // Bits 28:28 -+		uint32_t en_read_preamble      : 1; // Bits 29:29 -+		uint32_t odt_samp_extend_en    : 1; // Bits 30:30 -+		uint32_t early_rleak_en        : 1; // Bits 31:31 -+	}; -+	uint32_t raw; -+}; -+ -+union ddr_data_control_1_reg { -+	struct __packed { -+		int32_t  ref_pi               : 4; // Bits 3:0 -+		uint32_t dll_mask             : 2; // Bits 5:4 -+		uint32_t dll_weaklock         : 1; // Bits 6:6 -+		uint32_t sdll_segment_disable : 3; // Bits 9:7 -+		uint32_t rx_bias_ctl          : 3; // Bits 12:10 -+		int32_t  odt_delay            : 4; // Bits 16:13 -+		uint32_t odt_duration         : 3; // Bits 19:17 -+		int32_t  sense_amp_delay      : 4; // Bits 23:20 -+		uint32_t sense_amp_duration   : 3; // Bits 26:24 -+		uint32_t burst_end_odt_delay  : 3; // Bits 29:27   *** TODO: Check Broadwell *** -+		uint32_t lpddr_long_odt_en    : 1; // Bits 30:30 -+		uint32_t                      : 1; // Bits 31:31 -+	}; -+	uint32_t raw; -+}; -+ -+/* NOTE: Bits 31:19 are only valid for Broadwell onwards */ -+union ddr_data_control_2_reg { -+	struct __packed { -+		uint32_t rx_stagger_ctl    : 5; // Bits  4:0 -+		uint32_t force_bias_on     : 1; // Bits  5:5 -+		uint32_t force_rx_on       : 1; // Bits  6:6 -+		uint32_t leaker_comp       : 2; // Bits  8:7 -+		uint32_t rx_dqs_amp_offset : 4; // Bits 12:9 -+		uint32_t rx_clk_stg_num    : 5; // Bits 17:13 -+		uint32_t wl_long_delay     : 1; // Bits 18:18 -+		uint32_t enable_vref_pwrdn : 1; // Bits 19:19 -+		uint32_t ddr4_mode         : 1; // Bits 20:20 -+		uint32_t en_vddq_odt       : 1; // Bits 21:21 -+		uint32_t en_vtt_odt        : 1; // Bits 22:22 -+		uint32_t en_const_z_eq_tx  : 1; // Bits 23:23 -+		uint32_t tx_eq_dis         : 1; // Bits 24:24 -+		uint32_t rx_vref_prog_mfc  : 1; // Bits 25:25 -+		uint32_t cben              : 3; // Bits 28:26 -+		uint32_t tx_deskew_disable : 1; // Bits 29:29 -+		uint32_t rx_deskew_disable : 1; // Bits 30:30 -+		uint32_t dq_slew_dly_byp   : 1; // Bits 31:31 -+	}; -+	uint32_t raw; -+}; -+ -+union ddr_comp_data_comp_1_reg { -+	struct __packed { -+		uint32_t rcomp_odt_up   : 6; // Bits  5:0 -+		uint32_t                : 3; // Bits  8:6 -+		uint32_t rcomp_odt_down : 6; // Bits 14:9 -+		uint32_t                : 1; // Bits 15:15 -+		uint32_t panic_drv_down : 6; // Bits 21:16 -+		uint32_t panic_drv_up   : 6; // Bits 27:22 -+		uint32_t ls_comp        : 3; // Bits 30:28 -+		uint32_t                : 1; // Bits 31:31 -+	}; -+	uint32_t raw; -+}; -+ -+union ddr_comp_ctl_0_reg { -+	struct __packed { -+		uint32_t                    : 3; // Bits  2:0 -+		uint32_t disable_odt_static : 1; // Bits  3:3 -+		uint32_t odt_up_down_off    : 6; // Bits  9:4 -+		uint32_t fixed_odt_offset   : 1; // Bits 10:10 -+		int32_t  dq_drv_vref        : 4; // Bits 14:11 -+		int32_t  dq_odt_vref        : 5; // Bits 19:15 -+		int32_t  cmd_drv_vref       : 4; // Bits 23:20 -+		int32_t  ctl_drv_vref       : 4; // Bits 27:24 -+		int32_t  clk_drv_vref       : 4; // Bits 31:28 -+	}; -+	uint32_t raw; -+}; -+ -+union ddr_comp_ctl_1_reg { -+	struct __packed { -+		uint32_t dq_scomp       : 5; // Bits  4:0 -+		uint32_t cmd_scomp      : 5; // Bits  9:5 -+		uint32_t ctl_scomp      : 5; // Bits 14:10 -+		uint32_t clk_scomp      : 5; // Bits 19:15 -+		uint32_t tco_cmd_offset : 4; // Bits 23:20 -+		uint32_t comp_clk_on    : 1; // Bits 24:24 -+		uint32_t vccddq_hi      : 1; // Bits 25:25 -+		uint32_t                : 3; // Bits 28:26 -+		uint32_t dis_quick_comp : 1; // Bits 29:29 -+		uint32_t sin_step       : 1; // Bits 30:30 -+		uint32_t sin_step_adv   : 1; // Bits 31:31 -+	}; -+	uint32_t raw; -+}; -+ -+union ddr_data_vref_adjust_reg { -+	struct __packed { -+		int32_t  ca_vref_ctrl     : 7;// Bits  6:0 -+		int32_t  ch1_vref_ctrl    : 7;// Bits 13:7 -+		int32_t  ch0_vref_ctrl    : 7;// Bits 20:14 -+		uint32_t en_dimm_vref_ca  : 1;// Bits 21:21 -+		uint32_t en_dimm_vref_ch1 : 1;// Bits 22:22 -+		uint32_t en_dimm_vref_ch0 : 1;// Bits 23:23 -+		uint32_t hi_z_timer_ctrl  : 2;// Bits 25:24 -+		uint32_t vccddq_hi_qnnn_h : 1;// Bits 26:26 -+		uint32_t                  : 2;// Bits 28:27 -+		uint32_t ca_slow_bw       : 1;// Bits 29:29 -+		uint32_t ch0_slow_bw      : 1;// Bits 30:30 -+		uint32_t ch1_slow_bw      : 1;// Bits 31:31 -+	}; -+	uint32_t raw; -+}; -+ -+union ddr_data_vref_control_reg { -+	struct __packed { -+		uint32_t hi_bw_divider   : 2; // Bits  1:0 -+		uint32_t lo_bw_divider   : 2; // Bits  3:2 -+		uint32_t sample_divider  : 3; // Bits  6:4 -+		uint32_t open_loop       : 1; // Bits  7:7 -+		uint32_t slow_bw_error   : 2; // Bits  9:8 -+		uint32_t hi_bw_enable    : 1; // Bits 10:10 -+		uint32_t                 : 1; // Bits 11:11 -+		uint32_t vt_slope_b      : 3; // Bits 14:12 -+		uint32_t vt_slope_a      : 3; // Bits 17:15 -+		uint32_t vt_offset       : 3; // Bits 20:18 -+		uint32_t sel_code        : 3; // Bits 23:21 -+		uint32_t output_code     : 8; // Bits 31:24 -+	}; -+	uint32_t raw; -+}; -+ -+union ddr_comp_vsshi_reg { -+	struct __packed { -+		uint32_t panic_drv_down_vref : 6; // Bits  5:0 -+		uint32_t panic_drv_up_vref   : 6; // Bits 11:6 -+		uint32_t vt_offset           : 5; // Bits 16:12 -+		uint32_t vt_slope_a          : 3; // Bits 19:17 -+		uint32_t vt_slope_b          : 3; // Bits 22:20 -+		uint32_t                     : 9; // Bits 31:23 -+	}; -+	uint32_t raw; -+}; -+ -+union ddr_comp_vsshi_control_reg { -+	struct __packed { -+		uint32_t vsshi_target    : 6; // Bits  5:0 -+		uint32_t hi_bw_divider   : 2; // Bits  7:6 -+		uint32_t lo_bw_divider   : 2; // Bits  9:8 -+		uint32_t sample_divider  : 3; // Bits 12:10 -+		uint32_t open_loop       : 1; // Bits 13:13 -+		uint32_t bw_error        : 2; // Bits 15:14 -+		uint32_t panic_driver_en : 1; // Bits 16:16 -+		uint32_t                 : 1; // Bits 17:17 -+		uint32_t panic_voltage   : 4; // Bits 21:18 -+		uint32_t gain_boost      : 1; // Bits 22:22 -+		uint32_t sel_code        : 1; // Bits 23:23 -+		uint32_t output_code     : 8; // Bits 31:24 -+	}; -+	uint32_t raw; -+}; -+ -+union ddr_clk_controls_reg { -+	struct __packed { -+		uint32_t ref_pi             : 4; // Bits  3:0 -+		uint32_t dll_mask           : 2; // Bits  5:4 -+		uint32_t                    : 1; // Bits  6:6 -+		uint32_t tx_on              : 1; // Bits  7:7 -+		uint32_t internal_clocks_on : 1; // Bits  8:8 -+		uint32_t repeater_clocks_on : 1; // Bits  9:9 -+		uint32_t io_lb_ctl          : 2; // Bits 11:10 -+		uint32_t odt_mode           : 1; // Bits 12:12 -+		uint32_t                    : 8; // Bits 20:13 -+		uint32_t rx_vref            : 6; // Bits 26:21 -+		uint32_t vccddq_hi          : 1; // Bits 27:27 -+		uint32_t dll_weaklock       : 1; // Bits 28:28 -+		uint32_t lpddr_mode         : 1; // Bits 29:29 -+		uint32_t                    : 2; // Bits 31:30 -+	}; -+	uint32_t raw; -+}; -+ -+union ddr_cmd_controls_reg { -+	struct __packed { -+		int32_t  ref_pi             : 4; // Bits  3:0 -+		uint32_t dll_mask           : 2; // Bits  5:4 -+		uint32_t                    : 1; // Bits  6:6 -+		uint32_t tx_on              : 1; // Bits  7:7 -+		uint32_t internal_clocks_on : 1; // Bits  8:8 -+		uint32_t repeater_clocks_on : 1; // Bits  9:9 -+		uint32_t io_lb_ctl          : 2; // Bits 11:10 -+		uint32_t odt_mode           : 1; // Bits 12:12 -+		uint32_t cmd_tx_eq          : 2; // Bits 14:13 -+		uint32_t early_weak_drive   : 2; // Bits 16:15 -+		uint32_t                    : 4; // Bits 20:17 -+		int32_t  rx_vref            : 6; // Bits 26:21 -+		uint32_t vccddq_hi          : 1; // Bits 27:27 -+		uint32_t dll_weaklock       : 1; // Bits 28:28 -+		uint32_t lpddr_mode         : 1; // Bits 29:29 -+		uint32_t lpddr_ca_a_dis     : 1; // Bits 30:30 -+		uint32_t lpddr_ca_b_dis     : 1; // Bits 31:31 -+	}; -+	uint32_t raw; -+}; -+ -+/* Same register definition for CKE and CTL fubs */ -+union ddr_cke_ctl_controls_reg { -+	struct __packed { -+		int32_t  ref_pi             : 4; // Bits  3:0 -+		uint32_t dll_mask           : 2; // Bits  5:4 -+		uint32_t                    : 1; // Bits  6:6 -+		uint32_t tx_on              : 1; // Bits  7:7 -+		uint32_t internal_clocks_on : 1; // Bits  8:8 -+		uint32_t repeater_clocks_on : 1; // Bits  9:9 -+		uint32_t io_lb_ctl          : 2; // Bits 11:10 -+		uint32_t odt_mode           : 1; // Bits 12:12 -+		uint32_t cmd_tx_eq          : 2; // Bits 14:13 -+		uint32_t early_weak_drive   : 2; // Bits 16:15 -+		uint32_t ctl_tx_eq          : 2; // Bits 18:17 -+		uint32_t ctl_sr_drv         : 2; // Bits 20:19 -+		int32_t  rx_vref            : 6; // Bits 26:21 -+		uint32_t vccddq_hi          : 1; // Bits 27:27 -+		uint32_t dll_weaklock       : 1; // Bits 28:28 -+		uint32_t lpddr_mode         : 1; // Bits 29:29 -+		uint32_t la_drv_en_ovrd     : 1; // Bits 30:30 -+		uint32_t lpddr_ca_a_dis     : 1; // Bits 31:31 -+	}; -+	uint32_t raw; -+}; -+ -+union ddr_scram_misc_control_reg { -+	struct __packed { -+		uint32_t wl_wake_cycles       :  2; // Bits  1:0 -+		uint32_t wl_sleep_cycles      :  3; // Bits  4:2 -+		uint32_t force_comp_update    :  1; // Bits  5:5 -+		uint32_t weaklock_latency     :  4; // Bits  9:6 -+		uint32_t ddr_no_ch_interleave :  1; // Bits 10:10 -+		uint32_t lpddr_mode           :  1; // Bits 11:11 -+		uint32_t cke_mapping_ch0      :  4; // Bits 15:12 -+		uint32_t cke_mapping_ch1      :  4; // Bits 19:16 -+		uint32_t                      : 12; // Bits 31:20 -+	}; -+	uint32_t raw; -+}; -+ -+union mcscheds_cbit_reg { -+	struct __packed { -+		uint32_t dis_opp_cas    : 1; // Bits  0:0 -+		uint32_t dis_opp_is_cas : 1; // Bits  1:1 -+		uint32_t dis_opp_ras    : 1; // Bits  2:2 -+		uint32_t dis_opp_is_ras : 1; // Bits  3:3 -+		uint32_t dis_1c_byp     : 1; // Bits  4:4 -+		uint32_t dis_2c_byp     : 1; // Bits  5:5 -+		uint32_t dis_deprd_opt  : 1; // Bits  6:6 -+		uint32_t dis_pt_it      : 1; // Bits  7:7 -+		uint32_t dis_prcnt_ring : 1; // Bits  8:8 -+		uint32_t dis_prcnt_sa   : 1; // Bits  9:9 -+		uint32_t dis_blkr_ph    : 1; // Bits 10:10 -+		uint32_t dis_blkr_pe    : 1; // Bits 11:11 -+		uint32_t dis_blkr_pm    : 1; // Bits 12:12 -+		uint32_t dis_odt        : 1; // Bits 13:13 -+		uint32_t oe_always_off  : 1; // Bits 14:14 -+		uint32_t                : 1; // Bits 15:15 -+		uint32_t dis_aom        : 1; // Bits 16:16 -+		uint32_t block_rpq      : 1; // Bits 17:17 -+		uint32_t block_wpq      : 1; // Bits 18:18 -+		uint32_t invert_align   : 1; // Bits 19:19 -+		uint32_t dis_write_gap  : 1; // Bits 20:20 -+		uint32_t dis_zq         : 1; // Bits 21:21 -+		uint32_t dis_tt         : 1; // Bits 22:22 -+		uint32_t dis_opp_ref    : 1; // Bits 23:23 -+		uint32_t long_zq        : 1; // Bits 24:24 -+		uint32_t dis_srx_zq     : 1; // Bits 25:25 -+		uint32_t serialize_zq   : 1; // Bits 26:26 -+		uint32_t zq_fast_exec   : 1; // Bits 27:27 -+		uint32_t dis_drive_nop  : 1; // Bits 28:28 -+		uint32_t pres_wdb_ent   : 1; // Bits 29:29 -+		uint32_t dis_clk_gate   : 1; // Bits 30:30 -+		uint32_t                : 1; // Bits 31:31 -+	}; -+	uint32_t raw; -+}; -+ -+union mcmain_command_rate_limit_reg { -+	struct __packed { -+		uint32_t enable_cmd_limit :  1; // Bits  0:0 -+		uint32_t cmd_rate_limit   :  3; // Bits  3:1 -+		uint32_t reset_on_command :  4; // Bits  7:4 -+		uint32_t reset_delay      :  4; // Bits 11:8 -+		uint32_t ck_to_cke_delay  :  2; // Bits 13:12 -+		uint32_t                  : 17; // Bits 30:14 -+		uint32_t init_mrw_2n_cs   :  1; // Bits 31:31 -+	}; -+	uint32_t raw; -+}; -+ -+union mad_chnl_reg { -+	struct __packed { -+		uint32_t ch_a           :  2; // Bits  1:0 -+		uint32_t ch_b           :  2; // Bits  3:2 -+		uint32_t ch_c           :  2; // Bits  5:4 -+		uint32_t stacked_mode   :  1; // Bits  6:6 -+		uint32_t stkd_mode_bits :  3; // Bits  9:7 -+		uint32_t lpddr_mode     :  1; // Bits 10:10 -+		uint32_t                : 21; // Bits 31:11 -+	}; -+	uint32_t raw; -+}; -+ -+union mad_dimm_reg { -+	struct __packed { -+		uint32_t dimm_a_size     :  8;  // Bits  7:0 -+		uint32_t dimm_b_size     :  8;  // Bits 15:8 -+		uint32_t dimm_a_sel      :  1;  // Bits 16:16 -+		uint32_t dimm_a_ranks    :  1;  // Bits 17:17 -+		uint32_t dimm_b_ranks    :  1;  // Bits 18:18 -+		uint32_t dimm_a_width    :  1;  // Bits 19:19 -+		uint32_t dimm_b_width    :  1;  // Bits 20:20 -+		uint32_t rank_interleave :  1;  // Bits 21:21 -+		uint32_t enh_interleave  :  1;  // Bits 22:22 -+		uint32_t                 :  1;  // Bits 23:23 -+		uint32_t ecc_mode        :  2;  // Bits 25:24 -+		uint32_t hori_mode       :  1;  // Bits 26:26 -+		uint32_t hori_address    :  3;  // Bits 29:27 -+		uint32_t                 :  2;  // Bits 31:30 -+	}; -+	uint32_t raw; -+}; -+ -+union mad_zr_reg { -+	struct __packed { -+		uint32_t             : 16; // Bits 15:0 -+		uint32_t ch_b_double :  8; // Bits 23:16 -+		uint32_t ch_b_single :  8; // Bits 31:24 -+	}; -+	uint32_t raw; -+}; -+ -+/* Same definition for P_COMP, M_COMP, D_COMP */ -+union pcu_comp_reg { -+	struct __packed { -+		uint32_t comp_disable  :  1; // Bits  0:0 -+		uint32_t comp_interval :  4; // Bits  4:1 -+		uint32_t               :  3; // Bits  7:5 -+		uint32_t comp_force    :  1; // Bits  8:8 -+		uint32_t               : 23; // Bits 31:9 -+	}; -+	uint32_t raw; -+}; -+ -+#endif -diff --git a/src/northbridge/intel/haswell/native_raminit/timings_refresh.c b/src/northbridge/intel/haswell/native_raminit/timings_refresh.c -new file mode 100644 -index 0000000000..a9d960f31b ---- /dev/null -+++ b/src/northbridge/intel/haswell/native_raminit/timings_refresh.c -@@ -0,0 +1,13 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#include "raminit_native.h" -+ -+void configure_timings(struct sysinfo *ctrl) -+{ -+	/** TODO: Stub **/ -+} -+ -+void configure_refresh(struct sysinfo *ctrl) -+{ -+	/** TODO: Stub **/ -+} -diff --git a/src/northbridge/intel/haswell/registers/mchbar.h b/src/northbridge/intel/haswell/registers/mchbar.h -index 45f8174995..4c3f399b5d 100644 ---- a/src/northbridge/intel/haswell/registers/mchbar.h -+++ b/src/northbridge/intel/haswell/registers/mchbar.h -@@ -7,9 +7,98 @@ - #define NUM_CHANNELS	2 - #define NUM_SLOTS	2 -  -+/* Indexed register helper macros */ -+#define _DDRIO_C_R_B(r, ch, rank, byte)	((r) + 0x100 * (ch) + 0x4 * (rank) + 0x200 * (byte)) -+#define _MCMAIN_C_X(r, ch, x)		((r) + 0x400 * (ch) + 0x4 * (x)) -+#define _MCMAIN_C(r, ch)		((r) + 0x400 * (ch)) -+ - /* Register definitions */ -+ -+/* DDR DATA per-channel per-bytelane */ -+#define DQ_CONTROL_2(ch, byte)			_DDRIO_C_R_B(0x0064, ch, 0, byte) -+ -+/* DDR CKE per-channel */ -+#define DDR_CKE_ch_CMD_COMP_OFFSET(ch)		_DDRIO_C_R_B(0x1204, ch, 0, 0) -+#define DDR_CKE_ch_CMD_PI_CODING(ch)		_DDRIO_C_R_B(0x1208, ch, 0, 0) -+ -+#define DDR_CKE_ch_CTL_CONTROLS(ch)		_DDRIO_C_R_B(0x121c, ch, 0, 0) -+#define DDR_CKE_ch_CTL_RANKS_USED(ch)		_DDRIO_C_R_B(0x1220, ch, 0, 0) -+ -+/* DDR CTL per-channel */ -+#define DDR_CTL_ch_CTL_CONTROLS(ch)		_DDRIO_C_R_B(0x1c1c, ch, 0, 0) -+#define DDR_CTL_ch_CTL_RANKS_USED(ch)		_DDRIO_C_R_B(0x1c20, ch, 0, 0) -+ -+/* DDR CLK per-channel */ -+#define DDR_CLK_ch_RANKS_USED(ch)		_DDRIO_C_R_B(0x1800, ch, 0, 0) -+#define DDR_CLK_ch_COMP_OFFSET(ch)		_DDRIO_C_R_B(0x1808, ch, 0, 0) -+#define DDR_CLK_ch_PI_CODING(ch)		_DDRIO_C_R_B(0x180c, ch, 0, 0) -+#define DDR_CLK_ch_CONTROLS(ch)			_DDRIO_C_R_B(0x1810, ch, 0, 0) -+ -+/* DDR Scrambler */ -+#define DDR_SCRAMBLE_ch(ch)			(0x2000 + 4 * (ch)) -+#define DDR_SCRAM_MISC_CONTROL			0x2008 -+ -+/* DDR CMDN/CMDS per-channel (writes go to both CMDN and CMDS fubs) */ -+#define DDR_CMD_ch_COMP_OFFSET(ch)		_DDRIO_C_R_B(0x3204, ch, 0, 0) -+#define DDR_CMD_ch_PI_CODING(ch)		_DDRIO_C_R_B(0x3208, ch, 0, 0) -+#define DDR_CMD_ch_CONTROLS(ch)			_DDRIO_C_R_B(0x320c, ch, 0, 0) -+ -+/* DDR CKE/CTL per-channel (writes go to both CKE and CTL fubs) */ -+#define DDR_CKE_CTL_ch_CTL_COMP_OFFSET(ch)	_DDRIO_C_R_B(0x3414, ch, 0, 0) -+#define DDR_CKE_CTL_ch_CTL_PI_CODING(ch)	_DDRIO_C_R_B(0x3418, ch, 0, 0) -+ -+/* DDR DATA broadcast */ -+#define DDR_DATA_RX_TRAIN_RANK(rank)		_DDRIO_C_R_B(0x3600, 0, rank, 0) -+#define DDR_DATA_RX_PER_BIT_RANK(rank)		_DDRIO_C_R_B(0x3610, 0, rank, 0) -+#define DDR_DATA_TX_TRAIN_RANK(rank)		_DDRIO_C_R_B(0x3620, 0, rank, 0) -+#define DDR_DATA_TX_PER_BIT_RANK(rank)		_DDRIO_C_R_B(0x3630, 0, rank, 0) -+ -+#define DDR_DATA_RCOMP_DATA_1			0x3644 -+#define DDR_DATA_TX_XTALK			0x3648 -+#define DDR_DATA_RX_OFFSET_VDQ			0x364c -+#define DDR_DATA_OFFSET_COMP			0x365c -+#define DDR_DATA_CONTROL_1			0x3660 -+ -+#define DDR_DATA_OFFSET_TRAIN			0x3670 -+#define DDR_DATA_CONTROL_0			0x3674 -+#define DDR_DATA_VREF_ADJUST			0x3678 -+ -+/* DDR CMD broadcast */ -+#define DDR_CMD_COMP				0x3700 -+ -+/* DDR CKE/CTL broadcast */ -+#define DDR_CKE_CTL_COMP			0x3810 -+ -+/* DDR CLK broadcast */ -+#define DDR_CLK_COMP				0x3904 -+#define DDR_CLK_CONTROLS			0x3910 -+#define DDR_CLK_CB_STATUS			0x3918 -+ -+/* DDR COMP (global) */ -+#define DDR_COMP_DATA_COMP_1			0x3a04 -+#define DDR_COMP_CMD_COMP			0x3a08 -+#define DDR_COMP_CTL_COMP			0x3a0c -+#define DDR_COMP_CLK_COMP			0x3a10 -+#define DDR_COMP_CTL_0				0x3a14 -+#define DDR_COMP_CTL_1				0x3a18 -+#define DDR_COMP_VSSHI				0x3a1c -+#define DDR_COMP_OVERRIDE			0x3a20 -+#define DDR_COMP_VSSHI_CONTROL			0x3a24 -+ -+/* MCMAIN per-channel */ -+#define COMMAND_RATE_LIMIT_ch(ch)		_MCMAIN_C(0x4010, ch) -+ -+#define MC_INIT_STATE_ch(ch)			_MCMAIN_C(0x42a0, ch) -+ -+/* MCMAIN broadcast */ -+#define MCSCHEDS_CBIT		0x4c20 -+ -+#define MCMNTS_SC_WDBWM		0x4f8c -+ -+/* MCDECS */ - #define MAD_CHNL		0x5000 /* Address Decoder Channel Configuration */ - #define MAD_DIMM(ch)		(0x5004 + (ch) * 4) -+#define MAD_ZR			0x5014 - #define MC_INIT_STATE_G		0x5030 - #define MRC_REVISION		0x5034 /* MRC Revision */ -  -@@ -28,6 +117,8 @@ -  - #define PCU_DDR_PTM_CTL		0x5880 -  -+#define PCU_DDR_VOLTAGE		0x58a4 -+ - /* Some power MSRs are also represented in MCHBAR */ - #define MCH_PKG_POWER_LIMIT_LO	0x59a0 - #define MCH_PKG_POWER_LIMIT_HI	0x59a4 -@@ -48,6 +139,8 @@ - #define  MAILBOX_BIOS_CMD_FSM_MEASURE_INTVL	0x909 - #define  MAILBOX_BIOS_CMD_READ_PCH_POWER	0xa - #define  MAILBOX_BIOS_CMD_READ_PCH_POWER_EXT	0xb -+#define  MAILBOX_BIOS_CMD_READ_DDR_2X_REFRESH	0x17 -+#define  MAILBOX_BIOS_CMD_WRITE_DDR_2X_REFRESH	0x18 - #define  MAILBOX_BIOS_CMD_READ_C9C10_VOLTAGE	0x26 - #define  MAILBOX_BIOS_CMD_WRITE_C9C10_VOLTAGE	0x27 -  -@@ -66,6 +159,7 @@ - #define MC_BIOS_REQ		0x5e00 /* Memory frequency request register */ - #define MC_BIOS_DATA		0x5e04 /* Miscellaneous information for BIOS */ - #define SAPMCTL			0x5f00 -+#define M_COMP			0x5f08 -  - #define HDAUDRID		0x6008 - #define UMAGFXCTL		0x6020 ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0004-haswell-NRI-Add-timings-refresh-programming.patch b/config/coreboot/haswell/patches/0004-haswell-NRI-Add-timings-refresh-programming.patch deleted file mode 100644 index 74c21227..00000000 --- a/config/coreboot/haswell/patches/0004-haswell-NRI-Add-timings-refresh-programming.patch +++ /dev/null @@ -1,541 +0,0 @@ -From d94843c7c0e25cb6da4040b845556034fdb0e2c3 Mon Sep 17 00:00:00 2001 -From: Angel Pons <th3fanbus@gmail.com> -Date: Sat, 7 May 2022 20:59:58 +0200 -Subject: [PATCH 04/20] haswell NRI: Add timings/refresh programming - -Program the registers with timing and refresh parameters. - -Change-Id: Id2ea339d2c9ea8b56c71d6e88ec76949653ff5c2 -Signed-off-by: Angel Pons <th3fanbus@gmail.com> ---- - .../haswell/native_raminit/lookup_timings.c   | 102 ++++++++ - .../haswell/native_raminit/raminit_native.h   |  14 ++ - .../haswell/native_raminit/reg_structs.h      |  93 +++++++ - .../haswell/native_raminit/timings_refresh.c  | 233 +++++++++++++++++- - .../intel/haswell/registers/mchbar.h          |  12 + - 5 files changed, 452 insertions(+), 2 deletions(-) - -diff --git a/src/northbridge/intel/haswell/native_raminit/lookup_timings.c b/src/northbridge/intel/haswell/native_raminit/lookup_timings.c -index 8b81c7c341..b8d6c1ef40 100644 ---- a/src/northbridge/intel/haswell/native_raminit/lookup_timings.c -+++ b/src/northbridge/intel/haswell/native_raminit/lookup_timings.c -@@ -60,3 +60,105 @@ uint32_t get_tXP(const uint32_t mem_clock_mhz) - 	}; - 	return lookup_timing(mem_clock_mhz, lut, ARRAY_SIZE(lut)); - } -+ -+static uint32_t get_lpddr_tCKE(const uint32_t mem_clock_mhz) -+{ -+	const struct timing_lookup lut[] = { -+		{  533,  4 }, -+		{  666,  5 }, -+		{ fmax,  6 }, -+	}; -+	return lookup_timing(mem_clock_mhz, lut, ARRAY_SIZE(lut)); -+} -+ -+static uint32_t get_ddr_tCKE(const uint32_t mem_clock_mhz) -+{ -+	const struct timing_lookup lut[] = { -+		{  533,  3 }, -+		{  800,  4 }, -+		{  933,  5 }, -+		{ 1200,  6 }, -+		{ fmax,  7 }, -+	}; -+	return lookup_timing(mem_clock_mhz, lut, ARRAY_SIZE(lut)); -+} -+ -+uint32_t get_tCKE(const uint32_t mem_clock_mhz, const bool lpddr) -+{ -+	return lpddr ? get_lpddr_tCKE(mem_clock_mhz) : get_ddr_tCKE(mem_clock_mhz); -+} -+ -+uint32_t get_tXPDLL(const uint32_t mem_clock_mhz) -+{ -+	const struct timing_lookup lut[] = { -+		{  400, 10 }, -+		{  533, 13 }, -+		{  666, 16 }, -+		{  800, 20 }, -+		{  933, 23 }, -+		{ 1066, 26 }, -+		{ 1200, 29 }, -+		{ fmax, 32 }, -+	}; -+	return lookup_timing(mem_clock_mhz, lut, ARRAY_SIZE(lut)); -+} -+ -+uint32_t get_tAONPD(const uint32_t mem_clock_mhz) -+{ -+	const struct timing_lookup lut[] = { -+		{  400,  4 }, -+		{  533,  5 }, -+		{  666,  6 }, -+		{  800,  7 }, /* SNB had 8 */ -+		{  933,  8 }, -+		{ 1066, 10 }, -+		{ 1200, 11 }, -+		{ fmax, 12 }, -+	}; -+	return lookup_timing(mem_clock_mhz, lut, ARRAY_SIZE(lut)); -+} -+ -+uint32_t get_tMOD(const uint32_t mem_clock_mhz) -+{ -+	const struct timing_lookup lut[] = { -+		{  800, 12 }, -+		{  933, 14 }, -+		{ 1066, 16 }, -+		{ 1200, 18 }, -+		{ fmax, 20 }, -+	}; -+	return lookup_timing(mem_clock_mhz, lut, ARRAY_SIZE(lut)); -+} -+ -+uint32_t get_tXS_offset(const uint32_t mem_clock_mhz) -+{ -+	return DIV_ROUND_UP(mem_clock_mhz, 100); -+} -+ -+static uint32_t get_lpddr_tZQOPER(const uint32_t mem_clock_mhz) -+{ -+	return (mem_clock_mhz * 360) / 1000; -+} -+ -+static uint32_t get_ddr_tZQOPER(const uint32_t mem_clock_mhz) -+{ -+	const struct timing_lookup lut[] = { -+		{  800, 256 }, -+		{  933, 299 }, -+		{ 1066, 342 }, -+		{ 1200, 384 }, -+		{ fmax, 427 }, -+	}; -+	return lookup_timing(mem_clock_mhz, lut, ARRAY_SIZE(lut)); -+} -+ -+/* tZQOPER defines the period required for ZQCL after SR exit */ -+uint32_t get_tZQOPER(const uint32_t mem_clock_mhz, const bool lpddr) -+{ -+	return lpddr ? get_lpddr_tZQOPER(mem_clock_mhz) : get_ddr_tZQOPER(mem_clock_mhz); -+} -+ -+uint32_t get_tZQCS(const uint32_t mem_clock_mhz, const bool lpddr) -+{ -+	return DIV_ROUND_UP(get_tZQOPER(mem_clock_mhz, lpddr), 4); -+} -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -index aa86b9aa39..cd1f2eb2a5 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -@@ -155,6 +155,12 @@ struct sysinfo { - 	uint8_t cke_cmd_pi_code[NUM_CHANNELS][NUM_GROUPS]; - 	uint8_t cmd_north_pi_code[NUM_CHANNELS][NUM_GROUPS]; - 	uint8_t cmd_south_pi_code[NUM_CHANNELS][NUM_GROUPS]; -+ -+	union tc_bank_reg tc_bank[NUM_CHANNELS]; -+	union tc_bank_rank_a_reg tc_bankrank_a[NUM_CHANNELS]; -+	union tc_bank_rank_b_reg tc_bankrank_b[NUM_CHANNELS]; -+	union tc_bank_rank_c_reg tc_bankrank_c[NUM_CHANNELS]; -+	union tc_bank_rank_d_reg tc_bankrank_d[NUM_CHANNELS]; - }; -  - static inline bool is_hsw_ult(void) -@@ -200,6 +206,14 @@ enum raminit_status configure_mc(struct sysinfo *ctrl); - void configure_timings(struct sysinfo *ctrl); - void configure_refresh(struct sysinfo *ctrl); -  -+uint32_t get_tCKE(uint32_t mem_clock_mhz, bool lpddr); -+uint32_t get_tXPDLL(uint32_t mem_clock_mhz); -+uint32_t get_tAONPD(uint32_t mem_clock_mhz); -+uint32_t get_tMOD(uint32_t mem_clock_mhz); -+uint32_t get_tXS_offset(uint32_t mem_clock_mhz); -+uint32_t get_tZQOPER(uint32_t mem_clock_mhz, bool lpddr); -+uint32_t get_tZQCS(uint32_t mem_clock_mhz, bool lpddr); -+ - enum raminit_status wait_for_first_rcomp(void); -  - uint8_t get_rx_bias(const struct sysinfo *ctrl); -diff --git a/src/northbridge/intel/haswell/native_raminit/reg_structs.h b/src/northbridge/intel/haswell/native_raminit/reg_structs.h -index d11cda4b3d..70487e1640 100644 ---- a/src/northbridge/intel/haswell/native_raminit/reg_structs.h -+++ b/src/northbridge/intel/haswell/native_raminit/reg_structs.h -@@ -335,6 +335,99 @@ union mcscheds_cbit_reg { - 	uint32_t raw; - }; -  -+union tc_bank_reg { -+	struct __packed { -+		uint32_t tRCD      : 5; // Bits  4:0 -+		uint32_t tRP       : 5; // Bits  9:5 -+		uint32_t tRAS      : 6; // Bits 15:10 -+		uint32_t tRDPRE    : 4; // Bits 19:16 -+		uint32_t tWRPRE    : 6; // Bits 25:20 -+		uint32_t tRRD      : 4; // Bits 29:26 -+		uint32_t tRPab_ext : 2; // Bits 31:30 -+	}; -+	uint32_t raw; -+}; -+ -+union tc_bank_rank_a_reg { -+	struct __packed { -+		uint32_t tCKE        : 4; // Bits  3:0 -+		uint32_t tFAW        : 8; // Bits 11:4 -+		uint32_t tRDRD_sr    : 3; // Bits 14:12 -+		uint32_t tRDRD_dr    : 4; // Bits 18:15 -+		uint32_t tRDRD_dd    : 4; // Bits 22:19 -+		uint32_t tRDPDEN     : 5; // Bits 27:23 -+		uint32_t             : 1; // Bits 28:28 -+		uint32_t cmd_3st_dis : 1; // Bits 29:29 -+		uint32_t cmd_stretch : 2; // Bits 31:30 -+	}; -+	uint32_t raw; -+}; -+ -+union tc_bank_rank_b_reg { -+	struct __packed { -+		uint32_t tWRRD_sr : 6; // Bits  5:0 -+		uint32_t tWRRD_dr : 4; // Bits  9:6 -+		uint32_t tWRRD_dd : 4; // Bits 13:10 -+		uint32_t tWRWR_sr : 3; // Bits 16:14 -+		uint32_t tWRWR_dr : 4; // Bits 20:17 -+		uint32_t tWRWR_dd : 4; // Bits 24:21 -+		uint32_t tWRPDEN  : 6; // Bits 30:25 -+		uint32_t dec_wrd  : 1; // Bits 31:31 -+	}; -+	uint32_t raw; -+}; -+ -+union tc_bank_rank_c_reg { -+	struct __packed { -+		uint32_t tXPDLL   : 6; // Bits  5:0 -+		uint32_t tXP      : 4; // Bits  9:6 -+		uint32_t tAONPD   : 4; // Bits 13:10 -+		uint32_t tRDWR_sr : 5; // Bits 18:14 -+		uint32_t tRDWR_dr : 5; // Bits 23:19 -+		uint32_t tRDWR_dd : 5; // Bits 28:24 -+		uint32_t          : 3; // Bits 31:29 -+	}; -+	uint32_t raw; -+}; -+ -+/* NOTE: Non-ULT only implements the lower 21 bits (odt_write_delay is 2 bits) */ -+union tc_bank_rank_d_reg { -+	struct __packed { -+		uint32_t tAA                : 5; // Bits  4:0 -+		uint32_t tCWL               : 5; // Bits  9:5 -+		uint32_t tCPDED             : 2; // Bits 11:10 -+		uint32_t tPRPDEN            : 2; // Bits 13:12 -+		uint32_t odt_read_delay     : 3; // Bits 16:14 -+		uint32_t odt_read_duration  : 2; // Bits 18:17 -+		uint32_t odt_write_duration : 3; // Bits 21:19 -+		uint32_t odt_write_delay    : 3; // Bits 24:22 -+		uint32_t odt_always_rank_0  : 1; // Bits 25:25 -+		uint32_t cmd_delay          : 2; // Bits 27:26 -+		uint32_t                    : 4; // Bits 31:28 -+	}; -+	uint32_t raw; -+}; -+ -+union tc_rftp_reg { -+	struct __packed { -+		uint32_t tREFI   : 16; // Bits 15:0 -+		uint32_t tRFC    :  9; // Bits 24:16 -+		uint32_t tREFIx9 :  7; // Bits 31:25 -+	}; -+	uint32_t raw; -+}; -+ -+union tc_srftp_reg { -+	struct __packed { -+		uint32_t tXSDLL     : 12; // Bits 11:0 -+		uint32_t tXS_offset :  4; // Bits 15:12 -+		uint32_t tZQOPER    : 10; // Bits 25:16 -+		uint32_t            :  2; // Bits 27:26 -+		uint32_t tMOD       :  4; // Bits 31:28 -+	}; -+	uint32_t raw; -+}; -+ - union mcmain_command_rate_limit_reg { - 	struct __packed { - 		uint32_t enable_cmd_limit :  1; // Bits  0:0 -diff --git a/src/northbridge/intel/haswell/native_raminit/timings_refresh.c b/src/northbridge/intel/haswell/native_raminit/timings_refresh.c -index a9d960f31b..54fee0121d 100644 ---- a/src/northbridge/intel/haswell/native_raminit/timings_refresh.c -+++ b/src/northbridge/intel/haswell/native_raminit/timings_refresh.c -@@ -1,13 +1,242 @@ - /* SPDX-License-Identifier: GPL-2.0-or-later */ -  -+#include <assert.h> -+#include <commonlib/bsd/clamp.h> -+#include <console/console.h> -+#include <delay.h> -+#include <device/pci_ops.h> -+#include <northbridge/intel/haswell/haswell.h> -+ - #include "raminit_native.h" -  -+#define BL		8	/* Burst length */ -+#define tCCD		4 -+#define tRPRE		1 -+#define tWPRE		1 -+#define tDLLK		512 -+ -+static bool is_sodimm(const enum spd_dimm_type_ddr3 type) -+{ -+	return type == SPD_DDR3_DIMM_TYPE_SO_DIMM || type == SPD_DDR3_DIMM_TYPE_72B_SO_UDIMM; -+} -+ -+static uint8_t get_odt_stretch(const struct sysinfo *const ctrl) -+{ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		/* Only stretch with 2 DIMMs per channel */ -+		if (ctrl->dpc[channel] != 2) -+			continue; -+ -+		const struct raminit_dimm_info *dimms = ctrl->dimms[channel]; -+ -+		/* Only stretch when using SO-DIMMs */ -+		if (!is_sodimm(dimms[0].data.dimm_type) || !is_sodimm(dimms[1].data.dimm_type)) -+			continue; -+ -+		/* Only stretch with mismatched card types */ -+		if (dimms[0].data.reference_card == dimms[1].data.reference_card) -+			continue; -+ -+		/* Stretch if one SO-DIMM is card F */ -+		for (uint8_t slot = 0; slot < NUM_SLOTS; slot++) { -+			if (dimms[slot].data.reference_card == 5) -+				return 1; -+		} -+	} -+	return 0; -+} -+ -+static union tc_bank_reg make_tc_bank(struct sysinfo *const ctrl) -+{ -+	return (union tc_bank_reg) { -+		.tRCD      = ctrl->tRCD, -+		.tRP       = ctrl->tRP, -+		.tRAS      = ctrl->tRAS, -+		.tRDPRE    = ctrl->tRTP, -+		.tWRPRE    = 4 + ctrl->tCWL + ctrl->tWR, -+		.tRRD      = ctrl->tRRD, -+		.tRPab_ext = 0,	/** TODO: For LPDDR, this is ctrl->tRPab - ctrl->tRP **/ -+	}; -+} -+ -+static union tc_bank_rank_a_reg make_tc_bankrank_a(struct sysinfo *ctrl, uint8_t odt_stretch) -+{ -+	/* Use 3N mode for DDR during training, but always use 1N mode for LPDDR */ -+	const uint32_t tCMD = ctrl->lpddr ? 0 : 3; -+	const uint32_t tRDRD_drdd = BL / 2 + 1 + tRPRE + odt_stretch + !!ctrl->lpddr; -+ -+	return (union tc_bank_rank_a_reg) { -+		.tCKE        = get_tCKE(ctrl->mem_clock_mhz, ctrl->lpddr), -+		.tFAW        = ctrl->tFAW, -+		.tRDRD_sr    = tCCD, -+		.tRDRD_dr    = tRDRD_drdd, -+		.tRDRD_dd    = tRDRD_drdd, -+		.tRDPDEN     = ctrl->tAA + BL / 2 + 1, -+		.cmd_3st_dis = 1,	/* Disable command tri-state before training */ -+		.cmd_stretch = tCMD, -+	}; -+} -+ -+static union tc_bank_rank_b_reg make_tc_bankrank_b(struct sysinfo *const ctrl) -+{ -+	const uint8_t tWRRD_drdd = ctrl->tCWL - ctrl->tAA + BL / 2 + 2 + tRPRE; -+	const uint8_t tWRWR_drdd = BL / 2 + 2 + tWPRE; -+ -+	return (union tc_bank_rank_b_reg) { -+		.tWRRD_sr = tCCD + ctrl->tCWL + ctrl->tWTR + 2, -+		.tWRRD_dr = ctrl->lpddr ? 8 : tWRRD_drdd, -+		.tWRRD_dd = ctrl->lpddr ? 8 : tWRRD_drdd, -+		.tWRWR_sr = tCCD, -+		.tWRWR_dr = tWRWR_drdd, -+		.tWRWR_dd = tWRWR_drdd, -+		.tWRPDEN  = ctrl->tWR + ctrl->tCWL + BL / 2, -+		.dec_wrd  = ctrl->tCWL >= 6, -+	}; -+} -+ -+static uint32_t get_tRDWR_sr(const struct sysinfo *ctrl) -+{ -+	if (ctrl->lpddr) { -+		const uint32_t tdqsck_max = DIV_ROUND_UP(5500, ctrl->qclkps * 2); -+		return ctrl->tAA - ctrl->tCWL + tCCD + tWPRE + tdqsck_max + 1; -+	} else { -+		const bool fast_clock = ctrl->mem_clock_mhz > 666; -+		return ctrl->tAA - ctrl->tCWL + tCCD + tWPRE + 2 + fast_clock; -+	} -+} -+ -+static union tc_bank_rank_c_reg make_tc_bankrank_c(struct sysinfo *ctrl, uint8_t odt_stretch) -+{ -+	const uint32_t tRDWR_sr = get_tRDWR_sr(ctrl); -+	const uint32_t tRDWR_drdd = tRDWR_sr + odt_stretch; -+ -+	return (union tc_bank_rank_c_reg) { -+		.tXPDLL   = get_tXPDLL(ctrl->mem_clock_mhz), -+		.tXP      = MAX(ctrl->tXP, 7),	/* Use a higher tXP for training */ -+		.tAONPD   = get_tAONPD(ctrl->mem_clock_mhz), -+		.tRDWR_sr = tRDWR_sr, -+		.tRDWR_dr = tRDWR_drdd, -+		.tRDWR_dd = tRDWR_drdd, -+	}; -+} -+ -+static union tc_bank_rank_d_reg make_tc_bankrank_d(struct sysinfo *ctrl, uint8_t odt_stretch) -+{ -+	const uint32_t odt_rd_delay = ctrl->tAA - ctrl->tCWL; -+	if (!ctrl->lpddr) { -+		return (union tc_bank_rank_d_reg) { -+			.tAA               = ctrl->tAA, -+			.tCWL              = ctrl->tCWL, -+			.tCPDED            = 1, -+			.tPRPDEN           = 1, -+			.odt_read_delay    = odt_rd_delay, -+			.odt_read_duration = odt_stretch, -+		}; -+	} -+ -+	/* tCWL has 1 extra clock because of tDQSS, subtract it here */ -+	const uint32_t tCWL_lpddr = ctrl->tCWL - 1; -+	const uint32_t odt_wr_delay = tCWL_lpddr + DIV_ROUND_UP(3500, ctrl->qclkps * 2); -+	const uint32_t odt_wr_duration = DIV_ROUND_UP(3500 - 1750, ctrl->qclkps * 2) + 1; -+ -+	return (union tc_bank_rank_d_reg) { -+		.tAA                = ctrl->tAA, -+		.tCWL               = tCWL_lpddr, -+		.tCPDED             = 2,	/* Required by JEDEC LPDDR3 spec */ -+		.tPRPDEN            = 1, -+		.odt_read_delay     = odt_rd_delay, -+		.odt_read_duration  = odt_stretch, -+		.odt_write_delay    = odt_wr_delay, -+		.odt_write_duration = odt_wr_duration, -+		.odt_always_rank_0  = ctrl->lpddr_dram_odt -+	}; -+} -+ -+/* ZQCS period values, in (tREFI * 128) units */ -+#define ZQCS_PERIOD_DDR3	128	/* tREFI * 128 = 7.8 us * 128 = 1ms */ -+#define ZQCS_PERIOD_LPDDR3	256	/* tREFI * 128 = 3.9 us * 128 = 0.5ms */ -+ -+static uint32_t make_tc_zqcal(const struct sysinfo *const ctrl) -+{ -+	const uint32_t zqcs_period = ctrl->lpddr ? ZQCS_PERIOD_LPDDR3 : ZQCS_PERIOD_DDR3; -+	const uint32_t tZQCS = get_tZQCS(ctrl->mem_clock_mhz, ctrl->lpddr); -+	return tZQCS << (is_hsw_ult() ? 10 : 8) | zqcs_period; -+} -+ -+static union tc_rftp_reg make_tc_rftp(const struct sysinfo *const ctrl) -+{ -+	/* -+	 * The tREFIx9 field should be programmed to minimum of 8.9 * tREFI (to allow -+	 * for possible delays from ZQ or isoc) and tRASmax (70us) divided by 1024. -+	 */ -+	return (union tc_rftp_reg) { -+		.tREFI   = ctrl->tREFI, -+		.tRFC    = ctrl->tRFC, -+		.tREFIx9 = ctrl->tREFI * 89 / 10240, -+	}; -+} -+ -+static union tc_srftp_reg make_tc_srftp(const struct sysinfo *const ctrl) -+{ -+	return (union tc_srftp_reg) { -+		.tXSDLL     = tDLLK, -+		.tXS_offset = get_tXS_offset(ctrl->mem_clock_mhz), -+		.tZQOPER    = get_tZQOPER(ctrl->mem_clock_mhz, ctrl->lpddr), -+		.tMOD       = get_tMOD(ctrl->mem_clock_mhz) - 8, -+	}; -+} -+ - void configure_timings(struct sysinfo *ctrl) - { --	/** TODO: Stub **/ -+	if (ctrl->lpddr) -+		die("%s: Missing support for LPDDR\n", __func__); -+ -+	const uint8_t odt_stretch = get_odt_stretch(ctrl); -+	const union tc_bank_reg tc_bank = make_tc_bank(ctrl); -+	const union tc_bank_rank_a_reg tc_bank_rank_a = make_tc_bankrank_a(ctrl, odt_stretch); -+	const union tc_bank_rank_b_reg tc_bank_rank_b = make_tc_bankrank_b(ctrl); -+	const union tc_bank_rank_c_reg tc_bank_rank_c = make_tc_bankrank_c(ctrl, odt_stretch); -+	const union tc_bank_rank_d_reg tc_bank_rank_d = make_tc_bankrank_d(ctrl, odt_stretch); -+ -+	const uint8_t wr_delay = tc_bank_rank_b.dec_wrd + 1; -+	uint8_t sc_wr_add_delay = 0; -+	sc_wr_add_delay |= wr_delay << 0; -+	sc_wr_add_delay |= wr_delay << 2; -+	sc_wr_add_delay |= wr_delay << 4; -+	sc_wr_add_delay |= wr_delay << 6; -+ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		ctrl->tc_bank[channel] = tc_bank; -+		ctrl->tc_bankrank_a[channel] = tc_bank_rank_a; -+		ctrl->tc_bankrank_b[channel] = tc_bank_rank_b; -+		ctrl->tc_bankrank_c[channel] = tc_bank_rank_c; -+		ctrl->tc_bankrank_d[channel] = tc_bank_rank_d; -+ -+		mchbar_write32(TC_BANK_ch(channel), ctrl->tc_bank[channel].raw); -+		mchbar_write32(TC_BANK_RANK_A_ch(channel), ctrl->tc_bankrank_a[channel].raw); -+		mchbar_write32(TC_BANK_RANK_B_ch(channel), ctrl->tc_bankrank_b[channel].raw); -+		mchbar_write32(TC_BANK_RANK_C_ch(channel), ctrl->tc_bankrank_c[channel].raw); -+		mchbar_write32(TC_BANK_RANK_D_ch(channel), ctrl->tc_bankrank_d[channel].raw); -+		mchbar_write8(SC_WR_ADD_DELAY_ch(channel), sc_wr_add_delay); -+	} - } -  - void configure_refresh(struct sysinfo *ctrl) - { --	/** TODO: Stub **/ -+	const union tc_srftp_reg tc_srftp = make_tc_srftp(ctrl); -+	const union tc_rftp_reg  tc_rftp  = make_tc_rftp(ctrl); -+	const uint32_t tc_zqcal = make_tc_zqcal(ctrl); -+ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		mchbar_setbits32(TC_RFP_ch(channel), 0xff); -+		mchbar_write32(TC_RFTP_ch(channel),  tc_rftp.raw); -+		mchbar_write32(TC_SRFTP_ch(channel), tc_srftp.raw); -+		mchbar_write32(TC_ZQCAL_ch(channel), tc_zqcal); -+	} - } -diff --git a/src/northbridge/intel/haswell/registers/mchbar.h b/src/northbridge/intel/haswell/registers/mchbar.h -index 4c3f399b5d..2acc5cbbc8 100644 ---- a/src/northbridge/intel/haswell/registers/mchbar.h -+++ b/src/northbridge/intel/haswell/registers/mchbar.h -@@ -86,9 +86,21 @@ - #define DDR_COMP_VSSHI_CONTROL			0x3a24 -  - /* MCMAIN per-channel */ -+#define TC_BANK_ch(ch)				_MCMAIN_C(0x4000, ch) -+#define TC_BANK_RANK_A_ch(ch)			_MCMAIN_C(0x4004, ch) -+#define TC_BANK_RANK_B_ch(ch)			_MCMAIN_C(0x4008, ch) -+#define TC_BANK_RANK_C_ch(ch)			_MCMAIN_C(0x400c, ch) - #define COMMAND_RATE_LIMIT_ch(ch)		_MCMAIN_C(0x4010, ch) -+#define TC_BANK_RANK_D_ch(ch)			_MCMAIN_C(0x4014, ch) -+#define SC_ROUNDT_LAT_ch(ch)			_MCMAIN_C(0x4024, ch) -  -+#define SC_WR_ADD_DELAY_ch(ch)			_MCMAIN_C(0x40d0, ch) -+ -+#define TC_ZQCAL_ch(ch)				_MCMAIN_C(0x4290, ch) -+#define TC_RFP_ch(ch)				_MCMAIN_C(0x4294, ch) -+#define TC_RFTP_ch(ch)				_MCMAIN_C(0x4298, ch) - #define MC_INIT_STATE_ch(ch)			_MCMAIN_C(0x42a0, ch) -+#define TC_SRFTP_ch(ch)				_MCMAIN_C(0x42a4, ch) -  - /* MCMAIN broadcast */ - #define MCSCHEDS_CBIT		0x4c20 ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0005-haswell-NRI-Program-memory-map.patch b/config/coreboot/haswell/patches/0005-haswell-NRI-Program-memory-map.patch deleted file mode 100644 index e095417c..00000000 --- a/config/coreboot/haswell/patches/0005-haswell-NRI-Program-memory-map.patch +++ /dev/null @@ -1,263 +0,0 @@ -From b872fb9fc10d1789989072b8533b797152e6cb54 Mon Sep 17 00:00:00 2001 -From: Angel Pons <th3fanbus@gmail.com> -Date: Sat, 7 May 2022 21:24:50 +0200 -Subject: [PATCH 05/20] haswell NRI: Program memory map - -This is very similar to Sandy/Ivy Bridge, except that there's several -registers to program in GDXCBAR. One of these GDXCBAR registers has a -lock bit that must be set in order for the memory controller to allow -normal access to DRAM. And it took me four months to realize this one -bit was the only reason why native raminit did not work. - -Change-Id: I3af73a018a7ba948701a542e661e7fefd57591fe -Signed-off-by: Angel Pons <th3fanbus@gmail.com> ---- - .../intel/haswell/native_raminit/Makefile.mk  |   1 + - .../intel/haswell/native_raminit/memory_map.c | 183 ++++++++++++++++++ - .../haswell/native_raminit/raminit_main.c     |   1 + - .../haswell/native_raminit/raminit_native.h   |   1 + - .../intel/haswell/registers/host_bridge.h     |   2 + - 5 files changed, 188 insertions(+) - create mode 100644 src/northbridge/intel/haswell/native_raminit/memory_map.c - -diff --git a/src/northbridge/intel/haswell/native_raminit/Makefile.mk b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -index fc55277a65..37d527e972 100644 ---- a/src/northbridge/intel/haswell/native_raminit/Makefile.mk -+++ b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -@@ -4,6 +4,7 @@ romstage-y += configure_mc.c - romstage-y += lookup_timings.c - romstage-y += init_mpll.c - romstage-y += io_comp_control.c -+romstage-y += memory_map.c - romstage-y += raminit_main.c - romstage-y += raminit_native.c - romstage-y += spd_bitmunching.c -diff --git a/src/northbridge/intel/haswell/native_raminit/memory_map.c b/src/northbridge/intel/haswell/native_raminit/memory_map.c -new file mode 100644 -index 0000000000..e3aded2b37 ---- /dev/null -+++ b/src/northbridge/intel/haswell/native_raminit/memory_map.c -@@ -0,0 +1,183 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#include <device/pci_ops.h> -+#include <northbridge/intel/haswell/haswell.h> -+#include <southbridge/intel/lynxpoint/me.h> -+#include <types.h> -+ -+#include "raminit_native.h" -+ -+/* GDXCBAR */ -+#define MPCOHTRK_GDXC_MOT_ADDRESS_LO		0x10 -+#define MPCOHTRK_GDXC_MOT_ADDRESS_HI		0x14 -+#define MPCOHTRK_GDXC_MOT_REGION		0x18 -+ -+#define MPCOHTRK_GDXC_OCLA_ADDRESS_LO		0x20 -+#define MPCOHTRK_GDXC_OCLA_ADDRESS_HI		0x24 -+#define MPCOHTRK_GDXC_OCLA_REGION		0x28 -+ -+/* This lock bit made me lose what little sanity I had left. - Angel Pons */ -+#define MPCOHTRK_GDXC_OCLA_ADDRESS_HI_LOCK	BIT(2) -+ -+static inline uint32_t gdxcbar_read32(const uintptr_t offset) -+{ -+	return read32p((mchbar_read32(GDXCBAR) & ~1) + offset); -+} -+ -+static inline void gdxcbar_write32(const uintptr_t offset, const uint32_t value) -+{ -+	write32p((mchbar_read32(GDXCBAR) & ~1) + offset, value); -+} -+ -+static inline void gdxcbar_clrsetbits32(const uintptr_t offset, uint32_t clear, uint32_t set) -+{ -+	const uintptr_t address = (mchbar_read32(GDXCBAR) & ~1) + offset; -+	clrsetbits32((void *)address, clear, set); -+} -+ -+#define gdxcbar_setbits32(offset, set)		gdxcbar_clrsetbits32(offset, 0, set) -+#define gdxcbar_clrbits32(offset, clear)	gdxcbar_clrsetbits32(offset, clear, 0) -+ -+/* All values stored in here (except the bool) are specified in MiB */ -+struct memory_map_data { -+	uint32_t dpr_size; -+	uint32_t tseg_size; -+	uint32_t gtt_size; -+	uint32_t gms_size; -+	uint32_t me_stolen_size; -+	uint32_t mmio_size; -+	uint32_t touud; -+	uint32_t remaplimit; -+	uint32_t remapbase; -+	uint32_t tom; -+	uint32_t tom_minus_me; -+	uint32_t tolud; -+	uint32_t bdsm_base; -+	uint32_t gtt_base; -+	uint32_t tseg_base; -+	bool reclaim_possible; -+}; -+ -+static void compute_memory_map(struct memory_map_data *map) -+{ -+	map->tom_minus_me = map->tom - map->me_stolen_size; -+ -+	/* -+	 * MMIO size will actually be slightly smaller than computed, -+	 * but matches what MRC does and is more MTRR-friendly given -+	 * that TSEG is treated as WB, but SMRR makes TSEG UC anyway. -+	 */ -+	const uint32_t mmio_size = MIN(map->tom_minus_me, 4096) / 2; -+	map->gtt_base = ALIGN_DOWN(mmio_size, map->tseg_size); -+	map->tseg_base = map->gtt_base - map->tseg_size; -+	map->bdsm_base = map->gtt_base + map->gtt_size; -+	map->tolud = map->bdsm_base + map->gms_size; -+	map->reclaim_possible = map->tom_minus_me > map->tolud; -+ -+	if (map->reclaim_possible) { -+		map->remapbase  = MAX(4096, map->tom_minus_me); -+		map->touud      = MIN(4096, map->tom_minus_me) + map->remapbase - map->tolud; -+		map->remaplimit = map->touud - 1; -+	} else { -+		map->remapbase  = 0; -+		map->remaplimit = 0; -+		map->touud = map->tom_minus_me; -+	} -+} -+ -+static void display_memory_map(const struct memory_map_data *map) -+{ -+	if (!CONFIG(DEBUG_RAM_SETUP)) -+		return; -+ -+	printk(BIOS_DEBUG, "============ MEMORY MAP ============\n"); -+	printk(BIOS_DEBUG, "\n"); -+	printk(BIOS_DEBUG, "dpr_size       = %u MiB\n", map->dpr_size); -+	printk(BIOS_DEBUG, "tseg_size      = %u MiB\n", map->tseg_size); -+	printk(BIOS_DEBUG, "gtt_size       = %u MiB\n", map->gtt_size); -+	printk(BIOS_DEBUG, "gms_size       = %u MiB\n", map->gms_size); -+	printk(BIOS_DEBUG, "me_stolen_size = %u MiB\n", map->me_stolen_size); -+	printk(BIOS_DEBUG, "\n"); -+	printk(BIOS_DEBUG, "touud          = %u MiB\n", map->touud); -+	printk(BIOS_DEBUG, "remaplimit     = %u MiB\n", map->remaplimit); -+	printk(BIOS_DEBUG, "remapbase      = %u MiB\n", map->remapbase); -+	printk(BIOS_DEBUG, "tom            = %u MiB\n", map->tom); -+	printk(BIOS_DEBUG, "tom_minus_me   = %u MiB\n", map->tom_minus_me); -+	printk(BIOS_DEBUG, "tolud          = %u MiB\n", map->tolud); -+	printk(BIOS_DEBUG, "bdsm_base      = %u MiB\n", map->bdsm_base); -+	printk(BIOS_DEBUG, "gtt_base       = %u MiB\n", map->gtt_base); -+	printk(BIOS_DEBUG, "tseg_base      = %u MiB\n", map->tseg_base); -+	printk(BIOS_DEBUG, "\n"); -+	printk(BIOS_DEBUG, "reclaim_possible = %s\n", map->reclaim_possible ? "Yes" : "No"); -+} -+ -+static void map_write_reg64(const uint16_t reg, const uint64_t size) -+{ -+	const uint64_t value = size << 20; -+	pci_write_config32(HOST_BRIDGE, reg + 4, value >> 32); -+	pci_write_config32(HOST_BRIDGE, reg + 0, value >>  0); -+} -+ -+static void map_write_reg32(const uint16_t reg, const uint32_t size) -+{ -+	const uint32_t value = size << 20; -+	pci_write_config32(HOST_BRIDGE, reg, value); -+} -+ -+static void program_memory_map(const struct memory_map_data *map) -+{ -+	map_write_reg64(TOUUD, map->touud); -+	map_write_reg64(TOM,   map->tom); -+	if (map->reclaim_possible) { -+		map_write_reg64(REMAPBASE,  map->remapbase); -+		map_write_reg64(REMAPLIMIT, map->remaplimit); -+	} -+	if (map->me_stolen_size) { -+		map_write_reg64(MESEG_LIMIT, 0x80000 - map->me_stolen_size); -+		map_write_reg64(MESEG_BASE, map->tom_minus_me); -+		pci_or_config32(HOST_BRIDGE, MESEG_LIMIT, ME_STLEN_EN); -+	} -+	map_write_reg32(TOLUD, map->tolud); -+	map_write_reg32(BDSM,  map->bdsm_base); -+	map_write_reg32(BGSM,  map->gtt_base); -+	map_write_reg32(TSEG,  map->tseg_base); -+ -+	const uint32_t dpr_reg = map->tseg_base << 20 | map->dpr_size << 4; -+	pci_write_config32(HOST_BRIDGE, DPR, dpr_reg); -+ -+	const uint16_t gfx_stolen_size = GGC_IGD_MEM_IN_32MB_UNITS(map->gms_size / 32); -+	const uint16_t ggc = map->gtt_size << 8 | gfx_stolen_size; -+	pci_write_config16(HOST_BRIDGE, GGC, ggc); -+ -+	/** TODO: Do not hardcode these? GDXC has weird alignment requirements, though. **/ -+	gdxcbar_write32(MPCOHTRK_GDXC_MOT_ADDRESS_LO, 0); -+	gdxcbar_write32(MPCOHTRK_GDXC_MOT_ADDRESS_HI, 0); -+	gdxcbar_write32(MPCOHTRK_GDXC_MOT_REGION, 0); -+ -+	gdxcbar_write32(MPCOHTRK_GDXC_OCLA_ADDRESS_LO, 0); -+	gdxcbar_write32(MPCOHTRK_GDXC_OCLA_ADDRESS_HI, 0); -+	gdxcbar_write32(MPCOHTRK_GDXC_OCLA_REGION, 0); -+ -+	gdxcbar_setbits32(MPCOHTRK_GDXC_OCLA_ADDRESS_HI, MPCOHTRK_GDXC_OCLA_ADDRESS_HI_LOCK); -+} -+ -+enum raminit_status configure_memory_map(struct sysinfo *ctrl) -+{ -+	struct memory_map_data memory_map = { -+		.tom            = ctrl->channel_size_mb[0] + ctrl->channel_size_mb[1], -+		.dpr_size       = CONFIG_INTEL_TXT_DPR_SIZE, -+		.tseg_size      = CONFIG_SMM_TSEG_SIZE >> 20, -+		.me_stolen_size = intel_early_me_uma_size(), -+	}; -+	/** FIXME: MRC hardcodes iGPU parameters, but we should not **/ -+	const bool igpu_on = pci_read_config32(HOST_BRIDGE, DEVEN) & DEVEN_D2EN; -+	if (CONFIG(ONBOARD_VGA_IS_PRIMARY) || igpu_on) { -+		memory_map.gtt_size = 2; -+		memory_map.gms_size = 64; -+		pci_or_config32(HOST_BRIDGE, DEVEN, DEVEN_D2EN); -+	} -+	compute_memory_map(&memory_map); -+	display_memory_map(&memory_map); -+	program_memory_map(&memory_map); -+	return 0; -+} -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_main.c b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -index fcc981ad04..559dfc3a4e 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_main.c -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -@@ -23,6 +23,7 @@ static const struct task_entry cold_boot[] = { - 	{ initialise_mpll,                                        true, "INITMPLL",   }, - 	{ convert_timings,                                        true, "CONVTIM",    }, - 	{ configure_mc,                                           true, "CONFMC",     }, -+	{ configure_memory_map,                                   true, "MEMMAP",     }, - }; -  - /* Return a generic stepping value to make stepping checks simpler */ -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -index cd1f2eb2a5..4763b25e8d 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -@@ -202,6 +202,7 @@ enum raminit_status collect_spd_info(struct sysinfo *ctrl); - enum raminit_status initialise_mpll(struct sysinfo *ctrl); - enum raminit_status convert_timings(struct sysinfo *ctrl); - enum raminit_status configure_mc(struct sysinfo *ctrl); -+enum raminit_status configure_memory_map(struct sysinfo *ctrl); -  - void configure_timings(struct sysinfo *ctrl); - void configure_refresh(struct sysinfo *ctrl); -diff --git a/src/northbridge/intel/haswell/registers/host_bridge.h b/src/northbridge/intel/haswell/registers/host_bridge.h -index 1ee0ab2890..0228cf6bb9 100644 ---- a/src/northbridge/intel/haswell/registers/host_bridge.h -+++ b/src/northbridge/intel/haswell/registers/host_bridge.h -@@ -34,6 +34,8 @@ -  - #define MESEG_BASE	0x70	/* Management Engine Base */ - #define MESEG_LIMIT	0x78	/* Management Engine Limit */ -+#define  MELCK		(1 << 10)	/* ME Range Lock */ -+#define  ME_STLEN_EN	(1 << 11)	/* ME Stolen Memory Enable */ -  - #define PAM0		0x80 - #define PAM1		0x81 ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0006-haswell-NRI-Add-DDR3-JEDEC-reset-and-init.patch b/config/coreboot/haswell/patches/0006-haswell-NRI-Add-DDR3-JEDEC-reset-and-init.patch deleted file mode 100644 index ea46364f..00000000 --- a/config/coreboot/haswell/patches/0006-haswell-NRI-Add-DDR3-JEDEC-reset-and-init.patch +++ /dev/null @@ -1,1036 +0,0 @@ -From 1ea9b05694da7ee61d49d9cd2b7e533a98e42321 Mon Sep 17 00:00:00 2001 -From: Angel Pons <th3fanbus@gmail.com> -Date: Sat, 7 May 2022 21:49:40 +0200 -Subject: [PATCH 06/20] haswell NRI: Add DDR3 JEDEC reset and init - -Implement JEDEC reset and init sequence for DDR3. The MRS commands are -issued through the REUT (Robust Electrical Unified Testing) hardware. - -Change-Id: I2a0c066537021b587599228086727cb1e041bff5 -Signed-off-by: Angel Pons <th3fanbus@gmail.com> ---- - .../intel/haswell/native_raminit/Makefile.mk  |   3 + - .../intel/haswell/native_raminit/ddr3.c       | 217 ++++++++++++++++++ - .../haswell/native_raminit/io_comp_control.c  |  19 ++ - .../haswell/native_raminit/jedec_reset.c      | 120 ++++++++++ - .../haswell/native_raminit/raminit_main.c     |   2 + - .../haswell/native_raminit/raminit_native.h   |  99 ++++++++ - .../haswell/native_raminit/reg_structs.h      | 154 +++++++++++++ - .../intel/haswell/native_raminit/reut.c       | 196 ++++++++++++++++ - .../intel/haswell/registers/mchbar.h          |  21 ++ - src/southbridge/intel/lynxpoint/pch.h         |   2 + - 10 files changed, 833 insertions(+) - create mode 100644 src/northbridge/intel/haswell/native_raminit/ddr3.c - create mode 100644 src/northbridge/intel/haswell/native_raminit/jedec_reset.c - create mode 100644 src/northbridge/intel/haswell/native_raminit/reut.c - -diff --git a/src/northbridge/intel/haswell/native_raminit/Makefile.mk b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -index 37d527e972..e9212df9e6 100644 ---- a/src/northbridge/intel/haswell/native_raminit/Makefile.mk -+++ b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -@@ -1,11 +1,14 @@ - ## SPDX-License-Identifier: GPL-2.0-or-later -  - romstage-y += configure_mc.c -+romstage-y += ddr3.c -+romstage-y += jedec_reset.c - romstage-y += lookup_timings.c - romstage-y += init_mpll.c - romstage-y += io_comp_control.c - romstage-y += memory_map.c - romstage-y += raminit_main.c - romstage-y += raminit_native.c -+romstage-y += reut.c - romstage-y += spd_bitmunching.c - romstage-y += timings_refresh.c -diff --git a/src/northbridge/intel/haswell/native_raminit/ddr3.c b/src/northbridge/intel/haswell/native_raminit/ddr3.c -new file mode 100644 -index 0000000000..6ddb11488b ---- /dev/null -+++ b/src/northbridge/intel/haswell/native_raminit/ddr3.c -@@ -0,0 +1,217 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#include <assert.h> -+#include <console/console.h> -+#include <northbridge/intel/haswell/haswell.h> -+#include <types.h> -+ -+#include "raminit_native.h" -+ -+#define DDR3_RTTNOM(a, b, c) (((a) << 9) | ((b) << 6) | ((c) << 2)) -+ -+uint16_t encode_ddr3_rttnom(const uint32_t rttnom) -+{ -+	switch (rttnom) { -+	case 0:		return DDR3_RTTNOM(0, 0, 0);	/* RttNom is disabled */ -+	case 20:	return DDR3_RTTNOM(1, 0, 0);	/* RZQ/12 */ -+	case 30:	return DDR3_RTTNOM(1, 0, 1);	/* RZQ/8 */ -+	case 40:	return DDR3_RTTNOM(0, 1, 1);	/* RZQ/6 */ -+	case 60:	return DDR3_RTTNOM(0, 0, 1);	/* RZQ/4 */ -+	case 120:	return DDR3_RTTNOM(0, 1, 0);	/* RZQ/2 */ -+	} -+	printk(BIOS_ERR, "%s: Invalid rtt_nom value %u\n", __func__, rttnom); -+	return 0; -+} -+ -+static const uint8_t jedec_wr_t[12] = { 1, 2, 3, 4, 5, 5, 6, 6, 7, 7, 0, 0 }; -+ -+static void ddr3_program_mr0(struct sysinfo *ctrl, const uint8_t dll_reset) -+{ -+	assert(ctrl->tWR >= 5 && ctrl->tWR <= 16); -+	assert(ctrl->tAA >= 4); -+	const uint8_t jedec_cas = ctrl->tAA - 4; -+	const union { -+		struct __packed { -+			uint16_t burst_length     : 2; // Bits  1:0 -+			uint16_t cas_latency_msb  : 1; // Bits  2:2 -+			uint16_t read_burst_type  : 1; // Bits  3:3 -+			uint16_t cas_latency_low  : 3; // Bits  6:4 -+			uint16_t test_mode        : 1; // Bits  7:7 -+			uint16_t dll_reset        : 1; // Bits  8:8 -+			uint16_t write_recovery   : 3; // Bits 11:9 -+			uint16_t precharge_pd_dll : 1; // Bits 12:12 -+			uint16_t                  : 3; // Bits 15:13 -+		}; -+		uint16_t raw; -+	} mr0reg = { -+		.burst_length     = 0, -+		.cas_latency_msb  = !!(jedec_cas & BIT(3)), -+		.read_burst_type  = 0, -+		.cas_latency_low  = jedec_cas & 0x7, -+		.dll_reset        = 1, -+		.write_recovery   = jedec_wr_t[ctrl->tWR - 5], -+		.precharge_pd_dll = 0, -+	}; -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		for (uint8_t slot = 0; slot < NUM_SLOTS; slot++) { -+			if (!rank_in_ch(ctrl, slot + slot, channel)) -+				continue; -+ -+			if (!ctrl->restore_mrs) -+				ctrl->mr0[channel][slot] = mr0reg.raw; -+		} -+		reut_issue_mrs_all(ctrl, channel, 0, ctrl->mr0[channel]); -+	} -+} -+ -+void ddr3_program_mr1(struct sysinfo *ctrl, const uint8_t wl_mode, const uint8_t q_off) -+{ -+	/* -+	 * JESD79-3F (JEDEC DDR3 spec) refers to bit 0 of MR1 as 'DLL Enable'. -+	 * However, its encoding is weird, and 'DLL Disable' makes more sense. -+	 * -+	 * Moreover, bit 5 is part of ODIC (Output Driver Impedance Control), -+	 * but all encodings where MR1 bit 5 is 1 are reserved. Thus, omit it. -+	 */ -+	union { -+		struct __packed { -+			uint16_t dll_disable      : 1; // Bits  0:0 -+			uint16_t od_impedance_ctl : 1; // Bits  1:1 -+			uint16_t odt_rtt_nom_low  : 1; // Bits  2:2 -+			uint16_t additive_latency : 2; // Bits  4:3 -+			uint16_t                  : 1; // Bits  5:5 -+			uint16_t odt_rtt_nom_mid  : 1; // Bits  6:6 -+			uint16_t write_level_mode : 1; // Bits  7:7 -+			uint16_t                  : 1; // Bits  8:8 -+			uint16_t odt_rtt_nom_high : 1; // Bits  9:9 -+			uint16_t                  : 1; // Bits 10:10 -+			uint16_t t_dqs            : 1; // Bits 11:11 -+			uint16_t q_off            : 1; // Bits 12:12 -+			uint16_t                  : 3; // Bits 15:13 -+		}; -+		uint16_t raw; -+	} mr1reg = { -+		.dll_disable      = 0, -+		.od_impedance_ctl = 1,	/* RZQ/7 */ -+		.additive_latency = 0, -+		.write_level_mode = wl_mode, -+		.t_dqs            = 0, -+		.q_off            = q_off, -+	}; -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		mr1reg.raw &= ~RTTNOM_MASK; -+		mr1reg.raw |= encode_ddr3_rttnom(ctrl->dpc[channel] == 2 ? 60 : 0); -+		for (uint8_t slot = 0; slot < NUM_SLOTS; slot++) { -+			if (!rank_in_ch(ctrl, slot + slot, channel)) -+				continue; -+ -+			if (!ctrl->restore_mrs) -+				ctrl->mr1[channel][slot] = mr1reg.raw; -+		} -+		reut_issue_mrs_all(ctrl, channel, 1, ctrl->mr1[channel]); -+	} -+} -+ -+enum { -+	RTT_WR_OFF = 0, -+	RTT_WR_60  = 1, -+	RTT_WR_120 = 2, -+}; -+ -+static void ddr3_program_mr2(struct sysinfo *ctrl) -+{ -+	assert(ctrl->tCWL >= 5); -+	const bool dimm_srt = ctrl->flags.ext_temp_refresh && !ctrl->flags.asr; -+ -+	const union { -+		struct __packed { -+			uint16_t partial_array_sr  : 3; // Bits  0:2 -+			uint16_t cas_write_latency : 3; // Bits  5:3 -+			uint16_t auto_self_refresh : 1; // Bits  6:6 -+			uint16_t self_refresh_temp : 1; // Bits  7:7 -+			uint16_t                   : 1; // Bits  8:8 -+			uint16_t odt_rtt_wr        : 2; // Bits 10:9 -+			uint16_t                   : 5; // Bits 15:11 -+		}; -+		uint16_t raw; -+	} mr2reg = { -+		.partial_array_sr  = 0, -+		.cas_write_latency = ctrl->tCWL - 5, -+		.auto_self_refresh = ctrl->flags.asr, -+		.self_refresh_temp = dimm_srt, -+		.odt_rtt_wr        = is_hsw_ult() ? RTT_WR_120 : RTT_WR_60, -+	}; -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		for (uint8_t slot = 0; slot < NUM_SLOTS; slot++) { -+			if (!rank_in_ch(ctrl, slot + slot, channel)) -+				continue; -+ -+			if (!ctrl->restore_mrs) -+				ctrl->mr2[channel][slot] = mr2reg.raw; -+		} -+		/* MR2 shadow register is similar but not identical to MR2 */ -+		if (!ctrl->restore_mrs) { -+			union tc_mr2_shadow_reg tc_mr2_shadow = { -+				.raw = mr2reg.raw & 0x073f, -+			}; -+			for (uint8_t slot = 0; slot < NUM_SLOTS; slot++) { -+				if (!rank_in_ch(ctrl, slot + slot, channel)) -+					continue; -+ -+				if (dimm_srt) -+					tc_mr2_shadow.srt_available |= BIT(slot); -+ -+				if (ctrl->rank_mirrored[channel] & BIT(slot + slot + 1)) -+					tc_mr2_shadow.addr_bit_swizzle |= BIT(slot); -+			} -+			mchbar_write32(TC_MR2_SHADOW_ch(channel), tc_mr2_shadow.raw); -+		} -+		reut_issue_mrs_all(ctrl, channel, 2, ctrl->mr2[channel]); -+	} -+} -+ -+static void ddr3_program_mr3(struct sysinfo *ctrl, const uint8_t mpr_mode) -+{ -+	const union { -+		struct __packed { -+			uint16_t mpr_loc  :  2; // Bits  1:0 -+			uint16_t mpr_mode :  1; // Bits  2:2 -+			uint16_t          : 13; // Bits 15:3 -+		}; -+		uint16_t raw; -+	} mr3reg = { -+		.mpr_loc  = 0, -+		.mpr_mode = mpr_mode, -+	}; -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		for (uint8_t slot = 0; slot < NUM_SLOTS; slot++) { -+			if (!rank_in_ch(ctrl, slot + slot, channel)) -+				continue; -+ -+			if (!ctrl->restore_mrs) -+				ctrl->mr3[channel][slot] = mr3reg.raw; -+		} -+		reut_issue_mrs_all(ctrl, channel, 3, ctrl->mr3[channel]); -+	} -+} -+ -+enum raminit_status ddr3_jedec_init(struct sysinfo *ctrl) -+{ -+	ddr3_program_mr2(ctrl); -+	ddr3_program_mr3(ctrl, 0); -+	ddr3_program_mr1(ctrl, 0, 0); -+	ddr3_program_mr0(ctrl, 1); -+	return reut_issue_zq(ctrl, ctrl->chanmap, ZQ_INIT); -+} -diff --git a/src/northbridge/intel/haswell/native_raminit/io_comp_control.c b/src/northbridge/intel/haswell/native_raminit/io_comp_control.c -index d45b608dd3..8a55fd81b2 100644 ---- a/src/northbridge/intel/haswell/native_raminit/io_comp_control.c -+++ b/src/northbridge/intel/haswell/native_raminit/io_comp_control.c -@@ -8,6 +8,25 @@ -  - #include "raminit_native.h" -  -+enum raminit_status io_reset(void) -+{ -+	union mc_init_state_g_reg mc_init_state_g = { -+		.raw = mchbar_read32(MC_INIT_STATE_G), -+	}; -+	mc_init_state_g.reset_io = 1; -+	mchbar_write32(MC_INIT_STATE_G, mc_init_state_g.raw); -+	struct stopwatch timer; -+	stopwatch_init_msecs_expire(&timer, 2000); -+	do { -+		mc_init_state_g.raw = mchbar_read32(MC_INIT_STATE_G); -+		if (mc_init_state_g.reset_io == 0) -+			return RAMINIT_STATUS_SUCCESS; -+ -+	} while (!stopwatch_expired(&timer)); -+	printk(BIOS_ERR, "Timed out waiting for DDR I/O reset to complete\n"); -+	return RAMINIT_STATUS_POLL_TIMEOUT; -+} -+ - enum raminit_status wait_for_first_rcomp(void) - { - 	struct stopwatch timer; -diff --git a/src/northbridge/intel/haswell/native_raminit/jedec_reset.c b/src/northbridge/intel/haswell/native_raminit/jedec_reset.c -new file mode 100644 -index 0000000000..de0f676758 ---- /dev/null -+++ b/src/northbridge/intel/haswell/native_raminit/jedec_reset.c -@@ -0,0 +1,120 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#include <console/console.h> -+#include <delay.h> -+#include <northbridge/intel/haswell/haswell.h> -+#include <southbridge/intel/lynxpoint/pch.h> -+#include <types.h> -+#include <timer.h> -+ -+#include "raminit_native.h" -+ -+static void assert_reset(const bool do_reset) -+{ -+	if (is_hsw_ult()) { -+		uint32_t pm_cfg2 = RCBA32(PM_CFG2); -+		if (do_reset) -+			pm_cfg2 &= ~PM_CFG2_DRAM_RESET_CTL; -+		else -+			pm_cfg2 |= PM_CFG2_DRAM_RESET_CTL; -+		RCBA32(PM_CFG2) = pm_cfg2; -+	} else { -+		union mc_init_state_g_reg mc_init_state_g = { -+			.raw = mchbar_read32(MC_INIT_STATE_G), -+		}; -+		mc_init_state_g.ddr_not_reset = !do_reset; -+		mchbar_write32(MC_INIT_STATE_G, mc_init_state_g.raw); -+	} -+} -+ -+/* -+ * Perform JEDEC reset. -+ * -+ * If RTT_NOM is to be enabled in MR1, the ODT input signal must be -+ * statically held low in our system since RTT_NOM is always enabled. -+ */ -+static void jedec_reset(struct sysinfo *ctrl) -+{ -+	if (is_hsw_ult()) -+		assert_reset(false); -+ -+	union mc_init_state_g_reg mc_init_state_g = { -+		.ddr_not_reset     = 1, -+		.safe_self_refresh = 1, -+	}; -+	mchbar_write32(MC_INIT_STATE_G, mc_init_state_g.raw); -+ -+	union reut_misc_cke_ctrl_reg reut_misc_cke_ctrl = { -+		.cke_override = 0xf, -+		.cke_on       = 0, -+	}; -+	mchbar_write32(REUT_MISC_CKE_CTRL, reut_misc_cke_ctrl.raw); -+ -+	assert_reset(true); -+ -+	/** TODO: check and switch DDR3 voltage here (mainboard-specific) **/ -+ -+	udelay(200); -+ -+	assert_reset(false); -+ -+	udelay(500); -+ -+	mc_init_state_g.dclk_enable = 1; -+	mchbar_write32(MC_INIT_STATE_G, mc_init_state_g.raw); -+ -+	/* Delay at least 20 nanoseconds for tCKSRX */ -+	tick_delay(1); -+ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		reut_misc_cke_ctrl.cke_on = ctrl->rankmap[channel]; -+		mchbar_write32(REUT_ch_MISC_CKE_CTRL(channel), reut_misc_cke_ctrl.raw); -+	} -+ -+	/* -+	 * Wait minimum of reset CKE exit time, tXPR. -+	 * Spec says MAX(tXS, 5 tCK). 5 tCK is 10 ns. -+	 */ -+	tick_delay(1); -+} -+ -+enum raminit_status do_jedec_init(struct sysinfo *ctrl) -+{ -+	/* Never do a JEDEC reset in S3 resume */ -+	if (ctrl->bootmode == BOOTMODE_S3) -+		return RAMINIT_STATUS_SUCCESS; -+ -+	enum raminit_status status = io_reset(); -+	if (status) -+		return status; -+ -+	status = wait_for_first_rcomp(); -+	if (status) -+		return status; -+ -+	/* Force ODT low (JEDEC spec) */ -+	const union reut_misc_odt_ctrl_reg reut_misc_odt_ctrl = { -+		.odt_override = 0xf, -+		.odt_on       = 0, -+	}; -+	mchbar_write32(REUT_MISC_ODT_CTRL, reut_misc_odt_ctrl.raw); -+ -+	/* -+	 * Note: Haswell MRC does not clear ODT override for LPDDR3. However, -+	 * Broadwell MRC does. Hell suspects this difference is important, as -+	 * there is an erratum in the specification update for Broadwell: -+	 * -+	 * Erratum BDM74: LPDDR3 Memory Training May Cause Platform Boot Failure -+	 */ -+	if (ctrl->lpddr) -+		die("%s: LPDDR-specific JEDEC init not implemented\n", __func__); -+ -+	jedec_reset(ctrl); -+	status = ddr3_jedec_init(ctrl); -+	if (!status) -+		ctrl->restore_mrs = true; -+ -+	/* Release ODT override */ -+	mchbar_write32(REUT_MISC_ODT_CTRL, 0); -+	return status; -+} -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_main.c b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -index 559dfc3a4e..94b268468c 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_main.c -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -@@ -24,6 +24,7 @@ static const struct task_entry cold_boot[] = { - 	{ convert_timings,                                        true, "CONVTIM",    }, - 	{ configure_mc,                                           true, "CONFMC",     }, - 	{ configure_memory_map,                                   true, "MEMMAP",     }, -+	{ do_jedec_init,                                          true, "JEDECINIT",  }, - }; -  - /* Return a generic stepping value to make stepping checks simpler */ -@@ -57,6 +58,7 @@ static void initialize_ctrl(struct sysinfo *ctrl) - 	ctrl->stepping = get_stepping(ctrl->cpu); - 	ctrl->vdd_mv = is_hsw_ult() ? 1350 : 1500; /** FIXME: Hardcoded, does it matter? **/ - 	ctrl->dq_pins_interleaved = cfg->dq_pins_interleaved; -+	ctrl->restore_mrs = false; - 	ctrl->bootmode = bootmode; - } -  -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -index 4763b25e8d..4bc2a4955f 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -@@ -27,6 +27,30 @@ - /* Always use 12 legs for emphasis (not trained) */ - #define TXEQFULLDRV		(3 << 4) -  -+/* DDR3 mode register bits */ -+#define MR0_DLL_RESET		BIT(8) -+ -+#define MR1_WL_ENABLE		BIT(7) -+#define MR1_QOFF_ENABLE		BIT(12) /* If set, output buffers disabled */ -+ -+#define RTTNOM_MASK		(BIT(9) | BIT(6) | BIT(2)) -+ -+/* ZQ calibration types */ -+enum { -+	ZQ_INIT,	/* DDR3: ZQCL with tZQinit, LPDDR3: ZQ Init  with tZQinit  */ -+	ZQ_LONG,	/* DDR3: ZQCL with tZQoper, LPDDR3: ZQ Long  with tZQCL    */ -+	ZQ_SHORT,	/* DDR3: ZQCS with tZQCS,   LPDDR3: ZQ Short with tZQCS    */ -+	ZQ_RESET,	/* DDR3: not used,          LPDDR3: ZQ Reset with tZQreset */ -+}; -+ -+/* REUT initialisation modes */ -+enum { -+	REUT_MODE_IDLE = 0, -+	REUT_MODE_TEST = 1, -+	REUT_MODE_MRS  = 2, -+	REUT_MODE_NOP  = 3, /* Normal operation mode */ -+}; -+ - enum command_training_iteration { - 	CT_ITERATION_CLOCK = 0, - 	CT_ITERATION_CMD_NORTH, -@@ -50,6 +74,7 @@ enum raminit_status { - 	RAMINIT_STATUS_UNSUPPORTED_MEMORY, - 	RAMINIT_STATUS_MPLL_INIT_FAILURE, - 	RAMINIT_STATUS_POLL_TIMEOUT, -+	RAMINIT_STATUS_REUT_ERROR, - 	RAMINIT_STATUS_UNSPECIFIED_ERROR, /** TODO: Deprecated in favor of specific values **/ - }; -  -@@ -72,6 +97,7 @@ struct sysinfo { - 	uint32_t cpu;		/* CPUID value */ -  - 	bool dq_pins_interleaved; -+	bool restore_mrs; -  - 	/** TODO: ECC support untested **/ - 	bool is_ecc; -@@ -161,6 +187,11 @@ struct sysinfo { - 	union tc_bank_rank_b_reg tc_bankrank_b[NUM_CHANNELS]; - 	union tc_bank_rank_c_reg tc_bankrank_c[NUM_CHANNELS]; - 	union tc_bank_rank_d_reg tc_bankrank_d[NUM_CHANNELS]; -+ -+	uint16_t mr0[NUM_CHANNELS][NUM_SLOTS]; -+	uint16_t mr1[NUM_CHANNELS][NUM_SLOTS]; -+	uint16_t mr2[NUM_CHANNELS][NUM_SLOTS]; -+	uint16_t mr3[NUM_CHANNELS][NUM_SLOTS]; - }; -  - static inline bool is_hsw_ult(void) -@@ -196,6 +227,53 @@ static inline void clear_data_offset_train_all(struct sysinfo *ctrl) - 	memset(ctrl->data_offset_train, 0, sizeof(ctrl->data_offset_train)); - } -  -+/* Number of ticks to wait in units of 69.841279 ns (citation needed) */ -+static inline void tick_delay(const uint32_t delay) -+{ -+	/* Just perform reads to a random register */ -+	for (uint32_t start = 0; start <= delay; start++) -+		mchbar_read32(REUT_ERR_DATA_STATUS); -+} -+ -+/* -+ * 64-bit MCHBAR registers need to be accessed atomically. If one uses -+ * two 32-bit ops instead, there will be problems with the REUT's CADB -+ * (Command Address Data Buffer): hardware automatically advances the -+ * pointer into the register file after a write to the input register. -+ */ -+static inline uint64_t mchbar_read64(const uintptr_t x) -+{ -+	const uint64_t *offset = (uint64_t *)(CONFIG_FIXED_MCHBAR_MMIO_BASE + x); -+	uint64_t mmxsave, v; -+	asm volatile ( -+		"\n\t movq %%mm0, %0" -+		"\n\t movq %2, %%mm0" -+		"\n\t movq %%mm0, %1" -+		"\n\t movq %3, %%mm0" -+		"\n\t emms" -+		: "=m"(mmxsave), -+		  "=m"(v) -+		: "m"(offset[0]), -+		  "m"(mmxsave)); -+	return v; -+} -+ -+static inline void mchbar_write64(const uintptr_t x, const uint64_t v) -+{ -+	const uint64_t *offset = (uint64_t *)(CONFIG_FIXED_MCHBAR_MMIO_BASE + x); -+	uint64_t mmxsave; -+	asm volatile ( -+		"\n\t movq %%mm0, %0" -+		"\n\t movq %2, %%mm0" -+		"\n\t movq %%mm0, %1" -+		"\n\t movq %3, %%mm0" -+		"\n\t emms" -+		: "=m"(mmxsave) -+		: "m"(offset[0]), -+		  "m"(v), -+		  "m"(mmxsave)); -+} -+ - void raminit_main(enum raminit_boot_mode bootmode); -  - enum raminit_status collect_spd_info(struct sysinfo *ctrl); -@@ -203,6 +281,7 @@ enum raminit_status initialise_mpll(struct sysinfo *ctrl); - enum raminit_status convert_timings(struct sysinfo *ctrl); - enum raminit_status configure_mc(struct sysinfo *ctrl); - enum raminit_status configure_memory_map(struct sysinfo *ctrl); -+enum raminit_status do_jedec_init(struct sysinfo *ctrl); -  - void configure_timings(struct sysinfo *ctrl); - void configure_refresh(struct sysinfo *ctrl); -@@ -215,8 +294,28 @@ uint32_t get_tXS_offset(uint32_t mem_clock_mhz); - uint32_t get_tZQOPER(uint32_t mem_clock_mhz, bool lpddr); - uint32_t get_tZQCS(uint32_t mem_clock_mhz, bool lpddr); -  -+enum raminit_status io_reset(void); - enum raminit_status wait_for_first_rcomp(void); -  -+uint16_t encode_ddr3_rttnom(uint32_t rttnom); -+void ddr3_program_mr1(struct sysinfo *ctrl, uint8_t wl_mode, uint8_t q_off); -+enum raminit_status ddr3_jedec_init(struct sysinfo *ctrl); -+ -+void reut_issue_mrs( -+	struct sysinfo *ctrl, -+	uint8_t channel, -+	uint8_t rankmask, -+	uint8_t mr, -+	uint16_t val); -+ -+void reut_issue_mrs_all( -+	struct sysinfo *ctrl, -+	uint8_t channel, -+	uint8_t mr, -+	const uint16_t val[NUM_SLOTS]); -+ -+enum raminit_status reut_issue_zq(struct sysinfo *ctrl, uint8_t chanmask, uint8_t zq_type); -+ - uint8_t get_rx_bias(const struct sysinfo *ctrl); -  - uint8_t get_tCWL(uint32_t mem_clock_mhz); -diff --git a/src/northbridge/intel/haswell/native_raminit/reg_structs.h b/src/northbridge/intel/haswell/native_raminit/reg_structs.h -index 70487e1640..9929f617fe 100644 ---- a/src/northbridge/intel/haswell/native_raminit/reg_structs.h -+++ b/src/northbridge/intel/haswell/native_raminit/reg_structs.h -@@ -335,6 +335,127 @@ union mcscheds_cbit_reg { - 	uint32_t raw; - }; -  -+union reut_pat_cadb_prog_reg { -+	struct __packed { -+		uint32_t addr : 16; // Bits 15:0 -+		uint32_t      :  8; // Bits 23:16 -+		uint32_t bank :  3; // Bits 26:24 -+		uint32_t      :  5; // Bits 31:27 -+		uint32_t cs   :  4; // Bits 35:32 -+		uint32_t      :  4; // Bits 39:36 -+		uint32_t cmd  :  3; // Bits 42:40 -+		uint32_t      :  5; // Bits 47:43 -+		uint32_t odt  :  4; // Bits 51:48 -+		uint32_t      :  4; // Bits 55:52 -+		uint32_t cke  :  4; // Bits 59:56 -+		uint32_t      :  4; // Bits 63:60 -+	}; -+	uint64_t raw; -+	uint32_t raw32[2]; -+}; -+ -+union reut_pat_cadb_mrs_reg { -+	struct __packed { -+		uint32_t delay_gap : 3; // Bits  2:0 -+		uint32_t           : 5; // Bits  7:3 -+		uint32_t start_ptr : 3; // Bits 10:8 -+		uint32_t           : 5; // Bits 15:11 -+		uint32_t end_ptr   : 3; // Bits 18:16 -+		uint32_t           : 5; // Bits 23:19 -+		uint32_t curr_ptr  : 3; // Bits 26:24 -+		uint32_t           : 5; // Bits 31:27 -+	}; -+	uint32_t raw; -+}; -+ -+union reut_seq_cfg_reg { -+	struct __packed { -+		uint32_t                               :  3; // Bits  2:0 -+		uint32_t stop_base_seq_on_wrap_trigger :  1; // Bits  3:3 -+		uint32_t                               :  1; // Bits  4:4 -+		uint32_t address_update_rate_mode      :  1; // Bits  5:5 -+		uint32_t                               :  1; // Bits  6:6 -+		uint32_t enable_dummy_reads            :  1; // Bits  7:7 -+		uint32_t                               :  2; // Bits  9:8 -+		uint32_t enable_constant_write_strobe  :  1; // Bits 10:10 -+		uint32_t global_control                :  1; // Bits 11:11 -+		uint32_t initialization_mode           :  2; // Bits 13:12 -+		uint32_t                               :  2; // Bits 15:14 -+		uint32_t early_steppings_loop_count    :  5; // Bits 20:16   *** Not on C0 *** -+		uint32_t                               :  3; // Bits 23:21 -+		uint32_t subsequence_start_pointer     :  3; // Bits 26:24 -+		uint32_t                               :  1; // Bits 27:27 -+		uint32_t subsequence_end_pointer       :  3; // Bits 30:28 -+		uint32_t                               :  1; // Bits 31:31 -+		uint32_t start_test_delay              : 10; // Bits 41:32 -+		uint32_t                               : 22; // Bits 63:42 -+	}; -+	uint64_t raw; -+	uint32_t raw32[2]; -+}; -+ -+union reut_seq_ctl_reg { -+	struct __packed { -+		uint32_t start_test    :  1; // Bits  0:0 -+		uint32_t stop_test     :  1; // Bits  1:1 -+		uint32_t clear_errors  :  1; // Bits  2:2 -+		uint32_t               :  1; // Bits  3:3 -+		uint32_t stop_on_error :  1; // Bits  4:4 -+		uint32_t               : 27; // Bits 31:5 -+	}; -+	uint32_t raw; -+}; -+ -+union reut_global_err_reg { -+	struct __packed { -+		uint32_t ch_error     :  2; // Bits  1:0 -+		uint32_t              : 14; // Bits 15:2 -+		uint32_t ch_test_done :  2; // Bits 17:16 -+		uint32_t              : 14; // Bits 31:18 -+	}; -+	uint32_t raw; -+}; -+ -+union reut_misc_cke_ctrl_reg { -+	struct __packed { -+		uint32_t cke_override           :  4; // Bits  3:0 -+		uint32_t                        :  4; // Bits  7:4 -+		uint32_t cke_en_start_test_sync :  1; // Bits  8:8 -+		uint32_t                        :  7; // Bits 15:9 -+		uint32_t cke_on                 :  4; // Bits 19:16 -+		uint32_t                        : 12; // Bits 31:20 -+	}; -+	uint32_t raw; -+}; -+ -+union reut_misc_odt_ctrl_reg { -+	struct __packed { -+		uint32_t odt_override     :  4; // Bits  3:0 -+		uint32_t                  : 12; // Bits 15:4 -+		uint32_t odt_on           :  4; // Bits 19:16 -+		uint32_t                  : 11; // Bits 30:20 -+		uint32_t mpr_train_ddr_on :  1; // Bits 31:31 -+	}; -+	uint32_t raw; -+}; -+ -+union mcscheds_dft_misc_reg { -+	struct __packed { -+		uint32_t wdar                 :  1; // Bits  0:0 -+		uint32_t safe_mask_sel        :  3; // Bits  3:1 -+		uint32_t force_rcv_en         :  1; // Bits  4:4 -+		uint32_t                      :  3; // Bits  7:5 -+		uint32_t ddr_qualifier        :  2; // Bits  9:8 -+		uint32_t qualifier_length     :  2; // Bits 11:10 -+		uint32_t wdb_block_en         :  1; // Bits 12:12 -+		uint32_t rt_dft_read_ptr      :  4; // Bits 16:13 -+		uint32_t rt_dft_read_enable   :  1; // Bits 17:17 -+		uint32_t rt_dft_read_sel_addr :  1; // Bits 18:18 -+		uint32_t                      : 13; // Bits 31:19 -+	}; -+	uint32_t raw; -+}; -+ - union tc_bank_reg { - 	struct __packed { - 		uint32_t tRCD      : 5; // Bits  4:0 -@@ -428,6 +549,18 @@ union tc_srftp_reg { - 	uint32_t raw; - }; -  -+union tc_mr2_shadow_reg { -+	struct __packed { -+		uint32_t mr2_shadow_low   :  6; // Bits  5:0 -+		uint32_t srt_available    :  2; // Bits  7:6 -+		uint32_t mr2_shadow_high  :  3; // Bits 10:8 -+		uint32_t                  :  3; // Bits 13:11 -+		uint32_t addr_bit_swizzle :  2; // Bits 15:14 -+		uint32_t                  : 16; // Bits 31:16 -+	}; -+	uint32_t raw; -+}; -+ - union mcmain_command_rate_limit_reg { - 	struct __packed { - 		uint32_t enable_cmd_limit :  1; // Bits  0:0 -@@ -483,6 +616,27 @@ union mad_zr_reg { - 	uint32_t raw; - }; -  -+union mc_init_state_g_reg { -+	struct __packed { -+		uint32_t pu_mrc_done        :  1; // Bits  0:0 -+		uint32_t ddr_not_reset      :  1; // Bits  1:1 -+		uint32_t                    :  1; // Bits  2:2 -+		uint32_t refresh_enable     :  1; // Bits  3:3 -+		uint32_t                    :  1; // Bits  4:4 -+		uint32_t mc_init_done_ack   :  1; // Bits  5:5 -+		uint32_t                    :  1; // Bits  6:6 -+		uint32_t mrc_done           :  1; // Bits  7:7 -+		uint32_t safe_self_refresh  :  1; // Bits  8:8 -+		uint32_t                    :  1; // Bits  9:9 -+		uint32_t hvm_gate_ddr_reset :  1; // Bits 10:10 -+		uint32_t                    : 11; // Bits 21:11 -+		uint32_t dclk_enable        :  1; // Bits 22:22 -+		uint32_t reset_io           :  1; // Bits 23:23 -+		uint32_t                    :  8; // Bits 31:24 -+	}; -+	uint32_t raw; -+}; -+ - /* Same definition for P_COMP, M_COMP, D_COMP */ - union pcu_comp_reg { - 	struct __packed { -diff --git a/src/northbridge/intel/haswell/native_raminit/reut.c b/src/northbridge/intel/haswell/native_raminit/reut.c -new file mode 100644 -index 0000000000..31019f74a1 ---- /dev/null -+++ b/src/northbridge/intel/haswell/native_raminit/reut.c -@@ -0,0 +1,196 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#include <console/console.h> -+#include <delay.h> -+#include <northbridge/intel/haswell/haswell.h> -+#include <timer.h> -+#include <types.h> -+ -+#include "raminit_native.h" -+ -+enum { -+	CADB_CMD_MRS = 0, -+	CADB_CMD_REF = 1, -+	CADB_CMD_PRE = 2, -+	CADB_CMD_ACT = 3, -+	CADB_CMD_WR  = 4, -+	CADB_CMD_RD  = 5, -+	CADB_CMD_ZQ  = 6, -+	CADB_CMD_NOP = 7, -+}; -+ -+/* -+ * DDR3 rank mirror swaps the following pins: A3<->A4, A5<->A6, A7<->A8, BA0<->BA1 -+ * -+ * Note that the swapped bits are contiguous. We can use some XOR magic to swap the bits. -+ * Address lanes are at bits 0..15 and bank selects are at bits 24..26 on the REUT register. -+ */ -+#define MIRROR_BITS	(BIT(24) | BIT(7) | BIT(5) | BIT(3)) -+static uint64_t cadb_prog_rank_mirror(const uint64_t cadb_prog) -+{ -+	/* First XOR: find which pairs of bits are different (need swapping) */ -+	const uint64_t tmp64 = (cadb_prog ^ (cadb_prog >> 1)) & MIRROR_BITS; -+ -+	/* Second XOR: invert the pairs of bits that have different values */ -+	return cadb_prog ^ (tmp64 | tmp64 << 1); -+} -+ -+static enum raminit_status reut_write_cadb_cmd( -+	struct sysinfo *ctrl, -+	const uint8_t channel, -+	const uint8_t rankmask, -+	const uint8_t cmd, -+	const uint8_t bank, -+	const uint16_t valarr[NUM_SLOTRANKS], -+	const uint8_t delay) -+{ -+	union mcscheds_dft_misc_reg dft_misc = { -+		.raw = mchbar_read32(MCSCHEDS_DFT_MISC), -+	}; -+	dft_misc.ddr_qualifier = 0; -+	mchbar_write32(MCSCHEDS_DFT_MISC, dft_misc.raw); -+ -+	/* Pointer will be dynamically incremented after a write to CADB_PROG register */ -+	mchbar_write8(REUT_ch_PAT_CADB_WRITE_PTR(channel), 0); -+ -+	uint8_t count = 0; -+	for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { -+		if (!(ctrl->rankmap[channel] & BIT(rank) & rankmask)) -+			continue; -+ -+		union reut_pat_cadb_prog_reg reut_cadb_prog = { -+			.addr = valarr[rank], -+			.bank = bank, -+			.cs   = ~BIT(rank), /* CS is active low */ -+			.cmd  = cmd, -+			.cke  = 0xf, -+		}; -+		if (ctrl->rank_mirrored[channel] & BIT(rank)) -+			reut_cadb_prog.raw = cadb_prog_rank_mirror(reut_cadb_prog.raw); -+ -+		mchbar_write64(REUT_ch_PAT_CADB_PROG(channel), reut_cadb_prog.raw); -+		count++; -+	} -+	if (!count) { -+		printk(BIOS_ERR, "%s: rankmask is invalid\n", __func__); -+		return RAMINIT_STATUS_UNSPECIFIED_ERROR;	/** FIXME: Is this needed? **/ -+	} -+	const union reut_pat_cadb_mrs_reg reut_cadb_mrs = { -+		.delay_gap = delay ? delay : 3, -+		.end_ptr   = count - 1, -+	}; -+	mchbar_write32(REUT_ch_PAT_CADB_MRS(channel), reut_cadb_mrs.raw); -+ -+	const uint32_t reut_seq_cfg_save = mchbar_read32(REUT_ch_SEQ_CFG(channel)); -+	union reut_seq_cfg_reg reut_seq_cfg = { -+		.raw = reut_seq_cfg_save, -+	}; -+	reut_seq_cfg.global_control = 0; -+	reut_seq_cfg.initialization_mode = REUT_MODE_MRS; -+	mchbar_write32(REUT_ch_SEQ_CFG(channel), reut_seq_cfg.raw); -+	mchbar_write32(REUT_ch_SEQ_CTL(channel), (union reut_seq_ctl_reg) { -+		.start_test   = 1, -+		.clear_errors = 1, -+	}.raw); -+	enum raminit_status status = RAMINIT_STATUS_SUCCESS; -+	union reut_global_err_reg reut_global_err; -+	struct stopwatch timer; -+	stopwatch_init_msecs_expire(&timer, 100); -+	do { -+		reut_global_err.raw = mchbar_read32(REUT_GLOBAL_ERR); -+		if (reut_global_err.ch_error & BIT(channel)) { -+			printk(BIOS_ERR, "Unexpected REUT error for channel %u\n", channel); -+			status = RAMINIT_STATUS_REUT_ERROR; -+			break; -+		} -+		if (stopwatch_expired(&timer)) { -+			printk(BIOS_ERR, "%s: REUT timed out!\n", __func__); -+			status = RAMINIT_STATUS_POLL_TIMEOUT; -+			break; -+		} -+	} while (!(reut_global_err.ch_test_done & BIT(channel))); -+	mchbar_write32(REUT_ch_SEQ_CTL(channel), (union reut_seq_ctl_reg) { -+		.clear_errors = 1, -+	}.raw); -+	mchbar_write32(REUT_ch_SEQ_CFG(channel), reut_seq_cfg_save); -+	return status; -+} -+ -+static enum raminit_status reut_write_cadb_cmd_all( -+	struct sysinfo *ctrl, -+	const uint8_t channel, -+	const uint8_t rankmask, -+	const uint8_t cmd, -+	const uint8_t bank, -+	const uint16_t val, -+	const uint8_t delay) -+{ -+	const uint16_t valarr[NUM_SLOTRANKS] = { val, val, val, val }; -+	return reut_write_cadb_cmd(ctrl, channel, rankmask, cmd, bank, valarr, delay); -+} -+ -+void reut_issue_mrs( -+	struct sysinfo *ctrl, -+	const uint8_t channel, -+	const uint8_t rankmask, -+	const uint8_t mr, -+	const uint16_t val) -+{ -+	reut_write_cadb_cmd_all(ctrl, channel, rankmask, CADB_CMD_MRS, mr, val, 0); -+} -+ -+void reut_issue_mrs_all( -+	struct sysinfo *ctrl, -+	const uint8_t channel, -+	const uint8_t mr, -+	const uint16_t val[NUM_SLOTS]) -+{ -+	const uint16_t valarr[NUM_SLOTRANKS] = { val[0], val[0], val[1], val[1] }; -+	reut_write_cadb_cmd(ctrl, channel, 0xf, CADB_CMD_MRS, mr, valarr, 0); -+} -+ -+enum raminit_status reut_issue_zq(struct sysinfo *ctrl, uint8_t chanmask, uint8_t zq_type) -+{ -+	/** TODO: Issuing ZQ commands differs for LPDDR **/ -+	if (ctrl->lpddr) -+		die("%s: LPDDR not yet supported in ZQ calibration\n", __func__); -+ -+	__maybe_unused uint8_t opcode; /* NOTE: Only used for LPDDR */ -+	uint16_t zq = 0; -+	switch (zq_type) { -+	case ZQ_INIT: -+		zq = BIT(10); -+		opcode = 0xff; -+		break; -+	case ZQ_LONG: -+		zq = BIT(10); -+		opcode = 0xab; -+		break; -+	case ZQ_SHORT: -+		opcode = 0x56; -+		break; -+	case ZQ_RESET: -+		opcode = 0xc3; -+		break; -+	default: -+		die("%s: ZQ type %u is invalid\n", __func__, zq_type); -+	} -+ -+	/* ZQCS on single-channel needs a longer delay */ -+	const uint8_t delay = zq_type == ZQ_SHORT && (!ctrl->dpc[0] || !ctrl->dpc[1]) ? 7 : 1; -+	enum raminit_status status = RAMINIT_STATUS_SUCCESS; -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!(BIT(channel) & chanmask) || !does_ch_exist(ctrl, channel)) -+			continue; -+ -+		status = reut_write_cadb_cmd_all(ctrl, channel, 0xf, CADB_CMD_ZQ, 0, zq, delay); -+		if (status) -+			break; -+	} -+ -+	/* Wait a bit after ZQ INIT and ZQCL commands */ -+	if (zq) -+		udelay(1); -+ -+	return status; -+} -diff --git a/src/northbridge/intel/haswell/registers/mchbar.h b/src/northbridge/intel/haswell/registers/mchbar.h -index 2acc5cbbc8..4fc78a7f43 100644 ---- a/src/northbridge/intel/haswell/registers/mchbar.h -+++ b/src/northbridge/intel/haswell/registers/mchbar.h -@@ -96,15 +96,36 @@ -  - #define SC_WR_ADD_DELAY_ch(ch)			_MCMAIN_C(0x40d0, ch) -  -+#define REUT_ch_MISC_CKE_CTRL(ch)		_MCMAIN_C(0x4190, ch) -+ -+#define REUT_ch_PAT_CADB_MRS(ch)		_MCMAIN_C(0x419c, ch) -+ -+#define REUT_ch_PAT_CADB_WRITE_PTR(ch)		_MCMAIN_C(0x41bc, ch) -+#define REUT_ch_PAT_CADB_PROG(ch)		_MCMAIN_C(0x41c0, ch) -+ - #define TC_ZQCAL_ch(ch)				_MCMAIN_C(0x4290, ch) - #define TC_RFP_ch(ch)				_MCMAIN_C(0x4294, ch) - #define TC_RFTP_ch(ch)				_MCMAIN_C(0x4298, ch) -+#define TC_MR2_SHADOW_ch(ch)			_MCMAIN_C(0x429c, ch) - #define MC_INIT_STATE_ch(ch)			_MCMAIN_C(0x42a0, ch) - #define TC_SRFTP_ch(ch)				_MCMAIN_C(0x42a4, ch) -  -+#define REUT_GLOBAL_ERR				0x4804 -+ -+#define REUT_ch_SEQ_CFG(ch)			(0x48a8 + 8 * (ch)) -+ -+#define REUT_ch_SEQ_CTL(ch)			(0x48b8 + 4 * (ch)) -+ - /* MCMAIN broadcast */ - #define MCSCHEDS_CBIT		0x4c20 -  -+#define MCSCHEDS_DFT_MISC	0x4c30 -+ -+#define REUT_ERR_DATA_STATUS	0x4ce0 -+ -+#define REUT_MISC_CKE_CTRL	0x4d90 -+#define REUT_MISC_ODT_CTRL	0x4d94 -+ - #define MCMNTS_SC_WDBWM		0x4f8c -  - /* MCDECS */ -diff --git a/src/southbridge/intel/lynxpoint/pch.h b/src/southbridge/intel/lynxpoint/pch.h -index 07f4b9dc16..5b3696347c 100644 ---- a/src/southbridge/intel/lynxpoint/pch.h -+++ b/src/southbridge/intel/lynxpoint/pch.h -@@ -586,6 +586,8 @@ void mainboard_config_rcba(void); - #define ACPIIRQEN	0x31e0	/* 32bit */ - #define OIC		0x31fe	/* 16bit */ - #define PRSTS		0x3310	/* 32bit */ -+#define PM_CFG2		0x333c	/* 32bit */ -+#define  PM_CFG2_DRAM_RESET_CTL	(1 << 26)	/* ULT only */ - #define PMSYNC_CONFIG	0x33c4	/* 32bit */ - #define PMSYNC_CONFIG2	0x33cc	/* 32bit */ - #define SOFT_RESET_CTRL 0x38f4 ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0007-haswell-NRI-Add-pre-training-steps.patch b/config/coreboot/haswell/patches/0007-haswell-NRI-Add-pre-training-steps.patch deleted file mode 100644 index 8b73df88..00000000 --- a/config/coreboot/haswell/patches/0007-haswell-NRI-Add-pre-training-steps.patch +++ /dev/null @@ -1,392 +0,0 @@ -From 936d432822fcd9aa2f018444cdc89e48e6d257d5 Mon Sep 17 00:00:00 2001 -From: Angel Pons <th3fanbus@gmail.com> -Date: Sat, 7 May 2022 23:12:18 +0200 -Subject: [PATCH 07/20] haswell NRI: Add pre-training steps - -Implement pre-training steps, which consist of enabling ECC I/O and -filling the WDB (Write Data Buffer, stores test patterns) through a -magic LDAT port. - -Change-Id: Ie2e09e3b218c4569ed8de5c5e1b05d491032e0f1 -Signed-off-by: Angel Pons <th3fanbus@gmail.com> ---- - .../intel/haswell/native_raminit/Makefile.mk  |   1 + - .../haswell/native_raminit/raminit_main.c     |  35 ++++ - .../haswell/native_raminit/raminit_native.h   |  24 +++ - .../haswell/native_raminit/reg_structs.h      |  45 +++++ - .../intel/haswell/native_raminit/setup_wdb.c  | 159 ++++++++++++++++++ - .../intel/haswell/registers/mchbar.h          |   9 + - 6 files changed, 273 insertions(+) - create mode 100644 src/northbridge/intel/haswell/native_raminit/setup_wdb.c - -diff --git a/src/northbridge/intel/haswell/native_raminit/Makefile.mk b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -index e9212df9e6..8d7d4e4db0 100644 ---- a/src/northbridge/intel/haswell/native_raminit/Makefile.mk -+++ b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -@@ -10,5 +10,6 @@ romstage-y += memory_map.c - romstage-y += raminit_main.c - romstage-y += raminit_native.c - romstage-y += reut.c -+romstage-y += setup_wdb.c - romstage-y += spd_bitmunching.c - romstage-y += timings_refresh.c -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_main.c b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -index 94b268468c..5e4674957d 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_main.c -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -@@ -3,6 +3,7 @@ - #include <assert.h> - #include <console/console.h> - #include <cpu/intel/haswell/haswell.h> -+#include <delay.h> - #include <device/pci_ops.h> - #include <northbridge/intel/haswell/chip.h> - #include <northbridge/intel/haswell/haswell.h> -@@ -12,6 +13,39 @@ -  - #include "raminit_native.h" -  -+static enum raminit_status pre_training(struct sysinfo *ctrl) -+{ -+	/* Skip on S3 resume */ -+	if (ctrl->bootmode == BOOTMODE_S3) -+		return RAMINIT_STATUS_SUCCESS; -+ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		for (uint8_t slot = 0; slot < NUM_SLOTS; slot++) { -+			if (!rank_in_ch(ctrl, slot + slot, channel)) -+				continue; -+ -+			printk(RAM_DEBUG, "C%uS%u:\n", channel, slot); -+			printk(RAM_DEBUG, "\tMR0: 0x%04x\n", ctrl->mr0[channel][slot]); -+			printk(RAM_DEBUG, "\tMR1: 0x%04x\n", ctrl->mr1[channel][slot]); -+			printk(RAM_DEBUG, "\tMR2: 0x%04x\n", ctrl->mr2[channel][slot]); -+			printk(RAM_DEBUG, "\tMR3: 0x%04x\n", ctrl->mr3[channel][slot]); -+			printk(RAM_DEBUG, "\n"); -+		} -+		if (ctrl->is_ecc) { -+			union mad_dimm_reg mad_dimm = { -+				.raw = mchbar_read32(MAD_DIMM(channel)), -+			}; -+			/* Enable ECC I/O */ -+			mad_dimm.ecc_mode = 1; -+			mchbar_write32(MAD_DIMM(channel), mad_dimm.raw); -+			/* Wait 4 usec after enabling the ECC I/O, needed by HW */ -+			udelay(4); -+		} -+	} -+	setup_wdb(ctrl); -+	return RAMINIT_STATUS_SUCCESS; -+} -+ - struct task_entry { - 	enum raminit_status (*task)(struct sysinfo *); - 	bool is_enabled; -@@ -25,6 +59,7 @@ static const struct task_entry cold_boot[] = { - 	{ configure_mc,                                           true, "CONFMC",     }, - 	{ configure_memory_map,                                   true, "MEMMAP",     }, - 	{ do_jedec_init,                                          true, "JEDECINIT",  }, -+	{ pre_training,                                           true, "PRETRAIN",   }, - }; -  - /* Return a generic stepping value to make stepping checks simpler */ -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -index 4bc2a4955f..1971b44b66 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -@@ -35,6 +35,13 @@ -  - #define RTTNOM_MASK		(BIT(9) | BIT(6) | BIT(2)) -  -+#define BASIC_VA_PAT_SPREAD_8	0x01010101 -+ -+#define WDB_CACHE_LINE_SIZE	8 -+ -+#define NUM_WDB_CL_MUX_SEEDS	3 -+#define NUM_CADB_MUX_SEEDS	3 -+ - /* ZQ calibration types */ - enum { - 	ZQ_INIT,	/* DDR3: ZQCL with tZQinit, LPDDR3: ZQ Init  with tZQinit  */ -@@ -316,6 +323,23 @@ void reut_issue_mrs_all( -  - enum raminit_status reut_issue_zq(struct sysinfo *ctrl, uint8_t chanmask, uint8_t zq_type); -  -+void write_wdb_fixed_pat( -+	const struct sysinfo *ctrl, -+	const uint8_t patterns[], -+	const uint8_t pat_mask[], -+	uint8_t spread, -+	uint16_t start); -+ -+void write_wdb_va_pat( -+	const struct sysinfo *ctrl, -+	uint32_t agg_mask, -+	uint32_t vic_mask, -+	uint8_t vic_rot, -+	uint16_t start); -+ -+void program_wdb_lfsr(const struct sysinfo *ctrl, bool cleanup); -+void setup_wdb(const struct sysinfo *ctrl); -+ - uint8_t get_rx_bias(const struct sysinfo *ctrl); -  - uint8_t get_tCWL(uint32_t mem_clock_mhz); -diff --git a/src/northbridge/intel/haswell/native_raminit/reg_structs.h b/src/northbridge/intel/haswell/native_raminit/reg_structs.h -index 9929f617fe..7aa8d8c8b2 100644 ---- a/src/northbridge/intel/haswell/native_raminit/reg_structs.h -+++ b/src/northbridge/intel/haswell/native_raminit/reg_structs.h -@@ -335,6 +335,18 @@ union mcscheds_cbit_reg { - 	uint32_t raw; - }; -  -+union reut_pat_cl_mux_lmn_reg { -+	struct __packed { -+		uint32_t l_data_select : 1; // Bits  0:0 -+		uint32_t en_sweep_freq : 1; // Bits  1:1 -+		uint32_t               : 6; // Bits  7:2 -+		uint32_t l_counter     : 8; // Bits 15:8 -+		uint32_t m_counter     : 8; // Bits 23:16 -+		uint32_t n_counter     : 8; // Bits 31:24 -+	}; -+	uint32_t raw; -+}; -+ - union reut_pat_cadb_prog_reg { - 	struct __packed { - 		uint32_t addr : 16; // Bits 15:0 -@@ -439,6 +451,39 @@ union reut_misc_odt_ctrl_reg { - 	uint32_t raw; - }; -  -+union ldat_pdat_reg { -+	struct __packed { -+		uint32_t fast_addr : 12; // Bits 11:0 -+		uint32_t           :  4; // Bits 15:12 -+		uint32_t addr_en   :  1; // Bits 16:16 -+		uint32_t seq_en    :  1; // Bits 17:17 -+		uint32_t pol_0     :  1; // Bits 18:18 -+		uint32_t pol_1     :  1; // Bits 19:19 -+		uint32_t cmd_a     :  4; // Bits 23:20 -+		uint32_t cmd_b     :  4; // Bits 27:24 -+		uint32_t cmd_c     :  4; // Bits 31:28 -+	}; -+	uint32_t raw; -+}; -+ -+union ldat_sdat_reg { -+	struct __packed { -+		uint32_t bank_sel   : 4; // Bits  3:0 -+		uint32_t            : 1; // Bits  4:4 -+		uint32_t array_sel  : 5; // Bits  9:5 -+		uint32_t cmp        : 1; // Bits 10:10 -+		uint32_t replicate  : 1; // Bits 11:11 -+		uint32_t dword      : 4; // Bits 15:12 -+		uint32_t mode       : 2; // Bits 17:16 -+		uint32_t mpmap      : 6; // Bits 23:18 -+		uint32_t mpb_offset : 4; // Bits 27:24 -+		uint32_t stage_en   : 1; // Bits 28:28 -+		uint32_t shadow     : 2; // Bits 30:29 -+		uint32_t            : 1; // Bits 31:31 -+	}; -+	uint32_t raw; -+}; -+ - union mcscheds_dft_misc_reg { - 	struct __packed { - 		uint32_t wdar                 :  1; // Bits  0:0 -diff --git a/src/northbridge/intel/haswell/native_raminit/setup_wdb.c b/src/northbridge/intel/haswell/native_raminit/setup_wdb.c -new file mode 100644 -index 0000000000..ec37c48415 ---- /dev/null -+++ b/src/northbridge/intel/haswell/native_raminit/setup_wdb.c -@@ -0,0 +1,159 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#include <northbridge/intel/haswell/haswell.h> -+#include <types.h> -+ -+#include "raminit_native.h" -+ -+static void ldat_write_cacheline( -+	const struct sysinfo *const ctrl, -+	const uint8_t chunk, -+	const uint16_t start, -+	const uint64_t data) -+{ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		/* -+		 * Do not do a 64-bit write here. The register is not aligned -+		 * to a 64-bit boundary, which could potentially cause issues. -+		 */ -+		mchbar_write32(QCLK_ch_LDAT_DATA_IN_x(channel, 0), data & UINT32_MAX); -+		mchbar_write32(QCLK_ch_LDAT_DATA_IN_x(channel, 1), data >> 32); -+		/* -+		 * Set REPLICATE = 0 as you don't want to replicate the data. -+		 * Set BANK_SEL to the chunk you want to write the 64 bits to. -+		 * Set ARRAY_SEL = 0 (the MC WDB) and MODE = 1. -+		 */ -+		const union ldat_sdat_reg ldat_sdat = { -+			.bank_sel = chunk, -+			.mode     = 1, -+		}; -+		mchbar_write32(QCLK_ch_LDAT_SDAT(channel), ldat_sdat.raw); -+		/* -+		 * Finally, write the PDAT register indicating which cacheline -+		 * of the WDB you want to write to by setting FAST_ADDR field -+		 * to one of the 64 cache lines. Also set CMD_B in the PDAT -+		 * register to 4'b1000, indicating that this is a LDAT write. -+		 */ -+		const union ldat_pdat_reg ldat_pdat = { -+			.fast_addr = MIN(start, 0xfff), -+			.cmd_b     = 8, -+		}; -+		mchbar_write32(QCLK_ch_LDAT_PDAT(channel), ldat_pdat.raw); -+	} -+} -+ -+static void clear_ldat_mode(const struct sysinfo *const ctrl) -+{ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) -+		mchbar_write32(QCLK_ch_LDAT_SDAT(channel), 0); -+} -+ -+void write_wdb_fixed_pat( -+	const struct sysinfo *const ctrl, -+	const uint8_t patterns[], -+	const uint8_t pat_mask[], -+	const uint8_t spread, -+	const uint16_t start) -+{ -+	for (uint8_t chunk = 0; chunk < WDB_CACHE_LINE_SIZE; chunk++) { -+		uint64_t data = 0; -+		for (uint8_t b = 0; b < 64; b++) { -+			const uint8_t beff  = b % spread; -+			const uint8_t burst = patterns[pat_mask[beff]]; -+			if (burst & BIT(chunk)) -+				data |= 1ULL << b; -+		} -+		ldat_write_cacheline(ctrl, chunk, start, data); -+	} -+	clear_ldat_mode(ctrl); -+} -+ -+static inline uint32_t rol_u32(const uint32_t val) -+{ -+	return (val << 1) | ((val >> 31) & 1); -+} -+ -+void write_wdb_va_pat( -+	const struct sysinfo *const ctrl, -+	const uint32_t agg_mask, -+	const uint32_t vic_mask, -+	const uint8_t vic_rot, -+	const uint16_t start) -+{ -+	static const uint8_t va_mask_to_compressed[4] = {0xaa, 0xc0, 0xcc, 0xf0}; -+	uint32_t v_mask = vic_mask; -+	uint32_t a_mask = agg_mask; -+	for (uint8_t v = 0; v < vic_rot; v++) { -+		uint8_t compressed[32] = {0}; -+		/* Iterate through all 32 bits and create a compressed version of cacheline */ -+		for (uint8_t b = 0; b < ARRAY_SIZE(compressed); b++) { -+			const uint8_t vic = !!(v_mask & BIT(b)); -+			const uint8_t agg = !!(a_mask & BIT(b)); -+			const uint8_t index = !vic << 1 | agg << 0; -+			compressed[b] = va_mask_to_compressed[index]; -+		} -+		for (uint8_t chunk = 0; chunk < WDB_CACHE_LINE_SIZE; chunk++) { -+			uint32_t data = 0; -+			for (uint8_t b = 0; b < ARRAY_SIZE(compressed); b++) -+				data |= !!(compressed[b] & BIT(chunk)) << b; -+ -+			const uint64_t data64 = (uint64_t)data << 32 | data; -+			ldat_write_cacheline(ctrl, chunk, start + v, data64); -+		} -+		v_mask = rol_u32(v_mask); -+		a_mask = rol_u32(a_mask); -+	} -+	clear_ldat_mode(ctrl); -+} -+ -+void program_wdb_lfsr(const struct sysinfo *ctrl, const bool cleanup) -+{ -+	/* Cleanup LFSR seeds are sequential */ -+	const uint32_t cleanup_seeds[NUM_WDB_CL_MUX_SEEDS] = { 0xaaaaaa, 0xcccccc, 0xf0f0f0 }; -+	const uint32_t regular_seeds[NUM_WDB_CL_MUX_SEEDS] = { 0xa10ca1, 0xef0d08, 0xad0a1e }; -+	const uint32_t *seeds = cleanup ? cleanup_seeds : regular_seeds; -+ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		for (uint8_t i = 0; i < NUM_WDB_CL_MUX_SEEDS; i++) { -+			mchbar_write32(REUT_ch_PAT_WDB_CL_MUX_RD_x(channel, i), seeds[i]); -+			mchbar_write32(REUT_ch_PAT_WDB_CL_MUX_WR_x(channel, i), seeds[i]); -+		} -+	} -+} -+ -+void setup_wdb(const struct sysinfo *ctrl) -+{ -+	const uint32_t amask[9] = { -+		0x86186186, 0x18618618, 0x30c30c30, -+		0xa28a28a2, 0x8a28a28a, 0x14514514, -+		0x28a28a28, 0x92492492, 0x24924924, -+	}; -+	const uint32_t vmask = 0x41041041; -+ -+	/* Fill first 8 entries with simple 2-LFSR VA pattern */ -+	write_wdb_va_pat(ctrl, 0, BASIC_VA_PAT_SPREAD_8, 8, 0); -+ -+	/* Fill next 54 entries with 3-LFSR VA pattern */ -+	for (uint8_t a = 0; a < ARRAY_SIZE(amask); a++) -+		write_wdb_va_pat(ctrl, amask[a], vmask, 6, 8 + a * 6); -+ -+	program_wdb_lfsr(ctrl, false); -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		const union reut_pat_cl_mux_lmn_reg wdb_cl_mux_lmn = { -+			.en_sweep_freq = 1, -+			.l_counter     = 1, -+			.m_counter     = 1, -+			.n_counter     = 10, -+		}; -+		mchbar_write32(REUT_ch_PAT_WDB_CL_MUX_LMN(channel), wdb_cl_mux_lmn.raw); -+	} -+} -diff --git a/src/northbridge/intel/haswell/registers/mchbar.h b/src/northbridge/intel/haswell/registers/mchbar.h -index 4fc78a7f43..f8408e51a0 100644 ---- a/src/northbridge/intel/haswell/registers/mchbar.h -+++ b/src/northbridge/intel/haswell/registers/mchbar.h -@@ -94,6 +94,11 @@ - #define TC_BANK_RANK_D_ch(ch)			_MCMAIN_C(0x4014, ch) - #define SC_ROUNDT_LAT_ch(ch)			_MCMAIN_C(0x4024, ch) -  -+#define REUT_ch_PAT_WDB_CL_MUX_WR_x(ch, x)	_MCMAIN_C_X(0x4048, ch, x) /* x in 0 .. 2 */ -+#define REUT_ch_PAT_WDB_CL_MUX_RD_x(ch, x)	_MCMAIN_C_X(0x4054, ch, x) /* x in 0 .. 2 */ -+ -+#define REUT_ch_PAT_WDB_CL_MUX_LMN(ch)		_MCMAIN_C(0x4078, ch) -+ - #define SC_WR_ADD_DELAY_ch(ch)			_MCMAIN_C(0x40d0, ch) -  - #define REUT_ch_MISC_CKE_CTRL(ch)		_MCMAIN_C(0x4190, ch) -@@ -110,6 +115,10 @@ - #define MC_INIT_STATE_ch(ch)			_MCMAIN_C(0x42a0, ch) - #define TC_SRFTP_ch(ch)				_MCMAIN_C(0x42a4, ch) -  -+#define QCLK_ch_LDAT_PDAT(ch)			_MCMAIN_C(0x42d0, ch) -+#define QCLK_ch_LDAT_SDAT(ch)			_MCMAIN_C(0x42d4, ch) -+#define QCLK_ch_LDAT_DATA_IN_x(ch, x)		_MCMAIN_C_X(0x42dc, ch, x) /* x in 0 .. 1 */ -+ - #define REUT_GLOBAL_ERR				0x4804 -  - #define REUT_ch_SEQ_CFG(ch)			(0x48a8 + 8 * (ch)) ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0008-haswell-NRI-Add-REUT-I-O-test-library.patch b/config/coreboot/haswell/patches/0008-haswell-NRI-Add-REUT-I-O-test-library.patch deleted file mode 100644 index 2225f18c..00000000 --- a/config/coreboot/haswell/patches/0008-haswell-NRI-Add-REUT-I-O-test-library.patch +++ /dev/null @@ -1,1130 +0,0 @@ -From 49a7ef2401922a8492ba577a43235bcfba7ea822 Mon Sep 17 00:00:00 2001 -From: Angel Pons <th3fanbus@gmail.com> -Date: Sun, 8 May 2022 00:11:29 +0200 -Subject: [PATCH 08/20] haswell NRI: Add REUT I/O test library - -Implement a library to run I/O tests using the REUT hardware. - -Change-Id: Id7b207cd0a3989ddd23c88c6b1f0cfa79d2c861f -Signed-off-by: Angel Pons <th3fanbus@gmail.com> ---- - .../intel/haswell/native_raminit/Makefile.mk  |   1 + - .../haswell/native_raminit/raminit_native.h   | 110 +++ - .../haswell/native_raminit/reg_structs.h      | 121 +++ - .../intel/haswell/native_raminit/testing_io.c | 744 ++++++++++++++++++ - .../intel/haswell/registers/mchbar.h          |  30 + - 5 files changed, 1006 insertions(+) - create mode 100644 src/northbridge/intel/haswell/native_raminit/testing_io.c - -diff --git a/src/northbridge/intel/haswell/native_raminit/Makefile.mk b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -index 8d7d4e4db0..6e1b365602 100644 ---- a/src/northbridge/intel/haswell/native_raminit/Makefile.mk -+++ b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -@@ -12,4 +12,5 @@ romstage-y += raminit_native.c - romstage-y += reut.c - romstage-y += setup_wdb.c - romstage-y += spd_bitmunching.c -+romstage-y += testing_io.c - romstage-y += timings_refresh.c -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -index 1971b44b66..7f19fde4cc 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -@@ -58,6 +58,88 @@ enum { - 	REUT_MODE_NOP  = 3, /* Normal operation mode */ - }; -  -+/* REUT error counter control */ -+enum { -+	COUNT_ERRORS_PER_CHANNEL	= 0, -+	COUNT_ERRORS_PER_LANE		= 1, -+	COUNT_ERRORS_PER_BYTE_GROUP	= 2, -+	COUNT_ERRORS_PER_CHUNK		= 3, -+}; -+ -+enum wdb_dq_pattern { -+	BASIC_VA = 0, -+	SEGMENT_WDB, -+	CADB, -+	TURN_AROUND, -+	LMN_VA, -+	TURN_AROUND_WR, -+	TURN_AROUND_ODT, -+	RD_RD_TA, -+	RD_RD_TA_ALL, -+}; -+ -+enum reut_cmd_pat { -+	PAT_WR_RD, -+	PAT_WR, -+	PAT_RD, -+	PAT_RD_WR_TA, -+	PAT_WR_RD_TA, -+	PAT_ODT_TA, -+}; -+ -+/* REUT subsequence types (B = Base, O = Offset) */ -+enum { -+	SUBSEQ_B_RD	= 0 << 22, -+	SUBSEQ_B_WR	= 1 << 22, -+	SUBSEQ_B_RD_WR	= 2 << 22, -+	SUBSEQ_B_WR_RD	= 3 << 22, -+	SUBSEQ_O_RD	= 4 << 22, -+	SUBSEQ_O_WR	= 5 << 22, -+}; -+ -+/* REUT mux control */ -+enum { -+	REUT_MUX_LMN      = 0, -+	REUT_MUX_BTBUFFER = 1, -+	REUT_MUX_LFSR     = 2, -+}; -+ -+/* Increment scale */ -+enum { -+	SCALE_LOGARITHM	= 0, -+	SCALE_LINEAR	= 1, -+}; -+ -+enum test_stop { -+	NSOE   = 0,	/* Never stop on error */ -+	NTHSOE = 1,	/* Stop on the nth error (we use n = 1) */ -+	ABGSOE = 2,	/* Stop on all byte groups error */ -+	ALSOE  = 3,	/* Stop on all lanes error */ -+}; -+ -+struct wdb_pat { -+	uint32_t start_ptr;	/* Starting pointer in WDB */ -+	uint32_t stop_ptr;	/* Stopping pointer in WDB */ -+	uint16_t inc_rate;	/* How quickly the WDB walks through cachelines */ -+	uint8_t  dq_pattern;	/* DQ pattern to use (see enum wdb_dq_pattern above) */ -+}; -+ -+struct reut_pole { -+	uint16_t start; -+	uint16_t stop; -+	uint16_t order; -+	uint32_t inc_rate; -+	uint16_t inc_val; -+	bool wrap_trigger; -+}; -+ -+struct reut_box { -+	struct reut_pole rank; -+	struct reut_pole bank; -+	struct reut_pole row; -+	struct reut_pole col; -+}; -+ - enum command_training_iteration { - 	CT_ITERATION_CLOCK = 0, - 	CT_ITERATION_CMD_NORTH, -@@ -199,6 +281,10 @@ struct sysinfo { - 	uint16_t mr1[NUM_CHANNELS][NUM_SLOTS]; - 	uint16_t mr2[NUM_CHANNELS][NUM_SLOTS]; - 	uint16_t mr3[NUM_CHANNELS][NUM_SLOTS]; -+ -+	uint8_t dq_pat; -+ -+	uint8_t dq_pat_lc; - }; -  - static inline bool is_hsw_ult(void) -@@ -340,6 +426,30 @@ void write_wdb_va_pat( - void program_wdb_lfsr(const struct sysinfo *ctrl, bool cleanup); - void setup_wdb(const struct sysinfo *ctrl); -  -+void program_seq_addr(uint8_t channel, const struct reut_box *reut_addr, bool log_seq_addr); -+void program_loop_count(const struct sysinfo *ctrl, uint8_t channel, uint8_t lc_exp); -+ -+void setup_io_test( -+	struct sysinfo *ctrl, -+	uint8_t chanmask, -+	enum reut_cmd_pat cmd_pat, -+	uint16_t num_cl, -+	uint8_t lc, -+	const struct reut_box *reut_addr, -+	enum test_stop soe, -+	const struct wdb_pat *pat, -+	uint8_t en_cadb, -+	uint8_t subseq_wait); -+ -+void setup_io_test_cadb(struct sysinfo *ctrl, uint8_t chanmask, uint8_t lc, enum test_stop soe); -+void setup_io_test_basic_va(struct sysinfo *ctrl, uint8_t chm, uint8_t lc, enum test_stop soe); -+void setup_io_test_mpr(struct sysinfo *ctrl, uint8_t chanmask, uint8_t lc, enum test_stop soe); -+ -+uint8_t select_reut_ranks(struct sysinfo *ctrl, uint8_t channel, uint8_t rankmask); -+ -+void run_mpr_io_test(bool clear_errors); -+uint8_t run_io_test(struct sysinfo *ctrl, uint8_t chanmask, uint8_t dq_pat, bool clear_errors); -+ - uint8_t get_rx_bias(const struct sysinfo *ctrl); -  - uint8_t get_tCWL(uint32_t mem_clock_mhz); -diff --git a/src/northbridge/intel/haswell/native_raminit/reg_structs.h b/src/northbridge/intel/haswell/native_raminit/reg_structs.h -index 7aa8d8c8b2..b943259b91 100644 ---- a/src/northbridge/intel/haswell/native_raminit/reg_structs.h -+++ b/src/northbridge/intel/haswell/native_raminit/reg_structs.h -@@ -347,6 +347,54 @@ union reut_pat_cl_mux_lmn_reg { - 	uint32_t raw; - }; -  -+union reut_err_ctl_reg { -+	struct __packed { -+		uint32_t stop_on_nth_error              : 6; // Bits  5:0 -+		uint32_t                                : 6; // Bits 11:6 -+		uint32_t stop_on_error_control          : 2; // Bits 13:12 -+		uint32_t                                : 2; // Bits 15:14 -+		uint32_t selective_err_enable_chunk     : 8; // Bits 23:16 -+		uint32_t selective_err_enable_cacheline : 8; // Bits 31:24 -+	}; -+	uint32_t raw; -+}; -+ -+union reut_pat_cadb_mux_ctrl_reg { -+	struct __packed { -+		uint32_t mux_0_ctrl     : 2; // Bits  1:0 -+		uint32_t                : 2; // Bits  3:2 -+		uint32_t mux_1_ctrl     : 2; // Bits  5:4 -+		uint32_t                : 2; // Bits  7:6 -+		uint32_t mux_2_ctrl     : 2; // Bits  9:8 -+		uint32_t                : 6; // Bits 15:10 -+		uint32_t sel_mux_0_ctrl : 2; // Bits 17:16 -+		uint32_t                : 2; // Bits 19:18 -+		uint32_t sel_mux_1_ctrl : 2; // Bits 21:20 -+		uint32_t                : 2; // Bits 23:22 -+		uint32_t sel_mux_2_ctrl : 2; // Bits 25:24 -+		uint32_t                : 6; // Bits 31:26 -+	}; -+	uint32_t raw; -+}; -+ -+union reut_pat_wdb_cl_mux_cfg_reg { -+	struct __packed { -+		uint32_t mux_0_control         : 2; // Bits  1:0 -+		uint32_t                       : 1; // Bits  2:2 -+		uint32_t mux_1_control         : 2; // Bits  4:3 -+		uint32_t                       : 1; // Bits  5:5 -+		uint32_t mux_2_control         : 2; // Bits  7:6 -+		uint32_t                       : 6; // Bits 13:8 -+		uint32_t ecc_replace_byte_ctl  : 1; // Bits 14:14 -+		uint32_t ecc_data_source_sel   : 1; // Bits 15:15 -+		uint32_t save_lfsr_seed_rate   : 6; // Bits 21:16 -+		uint32_t                       : 2; // Bits 23:22 -+		uint32_t reload_lfsr_seed_rate : 3; // Bits 26:24 -+		uint32_t                       : 5; // Bits 31:27 -+	}; -+	uint32_t raw; -+}; -+ - union reut_pat_cadb_prog_reg { - 	struct __packed { - 		uint32_t addr : 16; // Bits 15:0 -@@ -366,6 +414,19 @@ union reut_pat_cadb_prog_reg { - 	uint32_t raw32[2]; - }; -  -+union reut_pat_wdb_cl_ctrl_reg { -+	struct __packed { -+		uint32_t inc_rate  :  5; // Bits 4:0 -+		uint32_t inc_scale :  1; // Bits 5:5 -+		uint32_t           :  2; // Bits 7:6 -+		uint32_t start_ptr :  6; // Bits 13:8 -+		uint32_t           :  2; // Bits 15:14 -+		uint32_t end_ptr   :  6; // Bits 21:16 -+		uint32_t           : 10; // Bits 31:22 -+	}; -+	uint32_t raw; -+}; -+ - union reut_pat_cadb_mrs_reg { - 	struct __packed { - 		uint32_t delay_gap : 3; // Bits  2:0 -@@ -406,6 +467,66 @@ union reut_seq_cfg_reg { - 	uint32_t raw32[2]; - }; -  -+union reut_seq_base_addr_reg { -+	struct __packed { -+		uint32_t           :  3; // Bits  2:0 -+		uint32_t col_addr  :  8; // Bits 10:3 -+		uint32_t           : 13; // Bits 23:11 -+		uint32_t row_addr  : 16; // Bits 39:24 -+		uint32_t           :  8; // Bits 47:40 -+		uint32_t bank_addr :  3; // Bits 50:48 -+		uint32_t           :  5; // Bits 55:51 -+		uint32_t rank_addr :  3; // Bits 58:56 -+		uint32_t           :  5; // Bits 63:59 -+	}; -+	uint32_t raw32[2]; -+	uint64_t raw; -+}; -+ -+union reut_seq_misc_ctl_reg { -+	struct __packed { -+		uint32_t col_addr_order       : 2; // Bits  1:0 -+		uint32_t row_addr_order       : 2; // Bits  3:2 -+		uint32_t bank_addr_order      : 2; // Bits  5:4 -+		uint32_t rank_addr_order      : 2; // Bits  7:6 -+		uint32_t                      : 5; // Bits 12:8 -+		uint32_t addr_invert_rate     : 3; // Bits 15:13 -+		uint32_t                      : 4; // Bits 19:16 -+		uint32_t col_addr_invert_en   : 1; // Bits 20:20 -+		uint32_t row_addr_invert_en   : 1; // Bits 21:21 -+		uint32_t bank_addr_invert_en  : 1; // Bits 22:22 -+		uint32_t rank_addr_invert_en  : 1; // Bits 23:23 -+		uint32_t col_wrap_trigger_en  : 1; // Bits 24:24 -+		uint32_t row_wrap_trigger_en  : 1; // Bits 25:25 -+		uint32_t bank_wrap_trigger_en : 1; // Bits 26:26 -+		uint32_t rank_wrap_trigger_en : 1; // Bits 27:27 -+		uint32_t col_wrap_carry_en    : 1; // Bits 28:28 -+		uint32_t row_wrap_carry_en    : 1; // Bits 29:29 -+		uint32_t bank_wrap_carry_en   : 1; // Bits 30:30 -+		uint32_t rank_wrap_carry_en   : 1; // Bits 31:31 -+	}; -+	uint32_t raw; -+}; -+ -+union reut_seq_addr_inc_ctl_reg { -+	struct __packed { -+		uint32_t                     :  3; // Bits  2:0 -+		uint32_t col_addr_increment  :  8; // Bits 10:3 -+		uint32_t                     :  1; // Bits 11:11 -+		uint32_t col_addr_update     :  8; // Bits 19:12 -+		uint32_t row_addr_increment  : 12; // Bits 31:20 -+		uint32_t row_addr_update     :  6; // Bits 37:32 -+		uint32_t bank_addr_increment :  3; // Bits 40:38 -+		uint32_t                     :  3; // Bits 43:41 -+		uint32_t bank_addr_update    :  8; // Bits 53:44 -+		uint32_t rank_addr_increment :  3; // Bits 54:52 -+		uint32_t                     :  1; // Bits 55:55 -+		uint32_t rank_addr_update    :  8; // Bits 63:56 -+	}; -+	uint64_t raw; -+	uint32_t raw32[2]; -+}; -+ - union reut_seq_ctl_reg { - 	struct __packed { - 		uint32_t start_test    :  1; // Bits  0:0 -diff --git a/src/northbridge/intel/haswell/native_raminit/testing_io.c b/src/northbridge/intel/haswell/native_raminit/testing_io.c -new file mode 100644 -index 0000000000..2632c238f8 ---- /dev/null -+++ b/src/northbridge/intel/haswell/native_raminit/testing_io.c -@@ -0,0 +1,744 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#include <console/console.h> -+#include <delay.h> -+#include <lib.h> -+#include <northbridge/intel/haswell/haswell.h> -+#include <timer.h> -+#include <types.h> -+ -+#include "raminit_native.h" -+ -+static void set_cadb_patterns(const uint8_t channel, const uint16_t seeds[NUM_CADB_MUX_SEEDS]) -+{ -+	for (uint8_t i = 0; i < NUM_CADB_MUX_SEEDS; i++) -+		mchbar_write32(REUT_ch_PAT_CADB_MUX_x(channel, i), seeds[i]); -+} -+ -+static void setup_cadb( -+	struct sysinfo *ctrl, -+	const uint8_t channel, -+	const uint8_t vic_spread, -+	const uint8_t vic_bit) -+{ -+	const bool lmn_en = false; -+ -+	/* -+	 * Currently, always start writing at CADB row 0. -+	 * Could add a start point parameter in the future. -+	 */ -+	mchbar_write8(REUT_ch_PAT_CADB_WRITE_PTR(channel), 0); -+	const uint8_t num_cadb_rows = 8; -+	for (uint8_t row = 0; row < num_cadb_rows; row++) { -+		const uint8_t lfsr0 = (row >> 0) & 1; -+		const uint8_t lfsr1 = (row >> 1) & 1; -+		uint64_t reg64 = 0; -+		for (uint8_t bit = 0; bit < 22; bit++) { -+			uint8_t bremap; -+			if (bit >= 19) { -+				/* (bremap in 40 .. 42) => CADB data control */ -+				bremap = bit + 21; -+			} else if (bit >= 16) { -+				/* (bremap in 24 .. 26) => CADB data bank */ -+				bremap = bit + 8; -+			} else { -+				/* (bremap in  0 .. 15) => CADB data address */ -+				bremap = bit; -+			} -+			const uint8_t fine = bit % vic_spread; -+			reg64 |= ((uint64_t)(fine == vic_bit ? lfsr0 : lfsr1)) << bremap; -+		} -+		/* -+		 * Write row. CADB pointer is auto incremented after every write. This must be -+		 * a single 64-bit write, otherwise the CADB pointer will auto-increment twice. -+		 */ -+		mchbar_write64(REUT_ch_PAT_CADB_PROG(channel), reg64); -+	} -+	const union reut_pat_cadb_mux_ctrl_reg cadb_mux_ctrl = { -+		.mux_0_ctrl = lmn_en ? REUT_MUX_LMN : REUT_MUX_LFSR, -+		.mux_1_ctrl = REUT_MUX_LFSR, -+		.mux_2_ctrl = REUT_MUX_LFSR, -+	}; -+	mchbar_write32(REUT_ch_PAT_CADB_MUX_CTRL(channel), cadb_mux_ctrl.raw); -+	const union reut_pat_cl_mux_lmn_reg cadb_cl_mux_lmn = { -+		.en_sweep_freq = 1, -+		.l_counter     = 1, -+		.m_counter     = 1, -+		.n_counter     = 6, -+	}; -+	mchbar_write32(REUT_ch_PAT_CADB_CL_MUX_LMN(channel), cadb_cl_mux_lmn.raw); -+	const uint16_t cadb_mux_seeds[NUM_CADB_MUX_SEEDS] = { 0x0ea1, 0xbeef, 0xdead }; -+	set_cadb_patterns(channel, cadb_mux_seeds); -+} -+ -+static uint32_t calc_rate(const uint32_t rate, const uint32_t lim, const uint8_t scale_bit) -+{ -+	return rate > lim ? log2_ceil(rate - 1) : BIT(scale_bit) | rate; -+} -+ -+void program_seq_addr( -+	const uint8_t channel, -+	const struct reut_box *reut_addr, -+	const bool log_seq_addr) -+{ -+	const int loglevel = log_seq_addr ? BIOS_ERR : BIOS_NEVER; -+	const uint32_t div = 8; -+	union reut_seq_base_addr_reg reut_seq_addr_start = { -+		.col_addr  = reut_addr->col.start / div, -+		.row_addr  = reut_addr->row.start, -+		.bank_addr = reut_addr->bank.start, -+		.rank_addr = reut_addr->rank.start, -+	}; -+	mchbar_write64(REUT_ch_SEQ_ADDR_START(channel), reut_seq_addr_start.raw); -+	reut_seq_addr_start.raw = mchbar_read64(REUT_ch_SEQ_ADDR_START(channel)); -+	printk(loglevel, "\tStart column:     %u\n", reut_seq_addr_start.col_addr); -+	printk(loglevel, "\tStart row:        %u\n", reut_seq_addr_start.row_addr); -+	printk(loglevel, "\tStart bank:       %u\n", reut_seq_addr_start.bank_addr); -+	printk(loglevel, "\tStart rank:       %u\n", reut_seq_addr_start.rank_addr); -+	printk(loglevel, "\n"); -+ -+	union reut_seq_base_addr_reg reut_seq_addr_stop = { -+		.col_addr  = reut_addr->col.stop / div, -+		.row_addr  = reut_addr->row.stop, -+		.bank_addr = reut_addr->bank.stop, -+		.rank_addr = reut_addr->rank.stop, -+	}; -+	mchbar_write64(REUT_ch_SEQ_ADDR_WRAP(channel), reut_seq_addr_stop.raw); -+	reut_seq_addr_stop.raw = mchbar_read64(REUT_ch_SEQ_ADDR_WRAP(channel)); -+	printk(loglevel, "\tStop column:      %u\n", reut_seq_addr_stop.col_addr); -+	printk(loglevel, "\tStop row:         %u\n", reut_seq_addr_stop.row_addr); -+	printk(loglevel, "\tStop bank:        %u\n", reut_seq_addr_stop.bank_addr); -+	printk(loglevel, "\tStop rank:        %u\n", reut_seq_addr_stop.rank_addr); -+	printk(loglevel, "\n"); -+ -+	union reut_seq_misc_ctl_reg reut_seq_misc_ctl = { -+		.col_wrap_trigger_en  = reut_addr->col.wrap_trigger, -+		.row_wrap_trigger_en  = reut_addr->row.wrap_trigger, -+		.bank_wrap_trigger_en = reut_addr->bank.wrap_trigger, -+		.rank_wrap_trigger_en = reut_addr->rank.wrap_trigger, -+	}; -+	mchbar_write32(REUT_ch_SEQ_MISC_CTL(channel), reut_seq_misc_ctl.raw); -+	printk(loglevel, "\tWrap column:      %u\n", reut_addr->col.wrap_trigger); -+	printk(loglevel, "\tWrap row:         %u\n", reut_addr->row.wrap_trigger); -+	printk(loglevel, "\tWrap bank:        %u\n", reut_addr->bank.wrap_trigger); -+	printk(loglevel, "\tWrap rank:        %u\n", reut_addr->rank.wrap_trigger); -+	printk(loglevel, "\n"); -+ -+	union reut_seq_addr_inc_ctl_reg reut_seq_addr_inc_ctl = { -+		.col_addr_update  = calc_rate(reut_addr->col.inc_rate,  31, 7), -+		.row_addr_update  = calc_rate(reut_addr->row.inc_rate,  15, 5), -+		.bank_addr_update = calc_rate(reut_addr->bank.inc_rate, 31, 7), -+		.rank_addr_update = calc_rate(reut_addr->rank.inc_rate, 31, 7), -+		.col_addr_increment  = reut_addr->col.inc_val, -+		.row_addr_increment  = reut_addr->row.inc_val, -+		.bank_addr_increment = reut_addr->bank.inc_val, -+		.rank_addr_increment = reut_addr->rank.inc_val, -+	}; -+	printk(loglevel, "\tUpdRate column:   %u\n", reut_addr->col.inc_rate); -+	printk(loglevel, "\tUpdRate row:      %u\n", reut_addr->row.inc_rate); -+	printk(loglevel, "\tUpdRate bank:     %u\n", reut_addr->bank.inc_rate); -+	printk(loglevel, "\tUpdRate rank:     %u\n", reut_addr->rank.inc_rate); -+	printk(loglevel, "\n"); -+	printk(loglevel, "\tUpdRateCR column: %u\n", reut_seq_addr_inc_ctl.col_addr_update); -+	printk(loglevel, "\tUpdRateCR row:    %u\n", reut_seq_addr_inc_ctl.row_addr_update); -+	printk(loglevel, "\tUpdRateCR bank:   %u\n", reut_seq_addr_inc_ctl.bank_addr_update); -+	printk(loglevel, "\tUpdRateCR rank:   %u\n", reut_seq_addr_inc_ctl.rank_addr_update); -+	printk(loglevel, "\n"); -+	printk(loglevel, "\tUpdInc column:    %u\n", reut_seq_addr_inc_ctl.col_addr_increment); -+	printk(loglevel, "\tUpdInc row:       %u\n", reut_seq_addr_inc_ctl.row_addr_increment); -+	printk(loglevel, "\tUpdInc bank:      %u\n", reut_seq_addr_inc_ctl.bank_addr_increment); -+	printk(loglevel, "\tUpdInc rank:      %u\n", reut_seq_addr_inc_ctl.rank_addr_increment); -+	printk(loglevel, "\n"); -+	mchbar_write64(REUT_ch_SEQ_ADDR_INC_CTL(channel), reut_seq_addr_inc_ctl.raw); -+} -+ -+/* -+ * Early steppings take exponential (base 2) loopcount values, -+ * but later steppings take linear loopcount values elsewhere. -+ * Address the differences in register offset and format here. -+ */ -+void program_loop_count(const struct sysinfo *ctrl, const uint8_t channel, const uint8_t lc_exp) -+{ -+	if (ctrl->stepping >= STEPPING_C0) { -+		const uint32_t loopcount = lc_exp >= 32 ? 0 : BIT(lc_exp); -+		mchbar_write32(HSW_REUT_ch_SEQ_LOOP_COUNT(channel), loopcount); -+	} else { -+		const uint8_t loopcount = lc_exp >= 32 ? 0 : lc_exp + 1; -+		union reut_seq_cfg_reg reut_seq_cfg = { -+			.raw = mchbar_read64(REUT_ch_SEQ_CFG(channel)), -+		}; -+		reut_seq_cfg.early_steppings_loop_count = loopcount; -+		mchbar_write64(REUT_ch_SEQ_CFG(channel), reut_seq_cfg.raw); -+	} -+} -+ -+static inline void write_subseq(const uint8_t channel, const uint8_t idx, const uint32_t ssq) -+{ -+	mchbar_write32(REUT_ch_SUBSEQ_x_CTL(channel, idx), ssq); -+} -+ -+static void program_subseq( -+	struct sysinfo *const ctrl, -+	const uint8_t channel, -+	const enum reut_cmd_pat cmd_pat, -+	const uint32_t ss_a, -+	const uint32_t ss_b) -+{ -+	switch (cmd_pat) { -+	case PAT_WR_RD_TA: -+		write_subseq(channel, 0, ss_a | SUBSEQ_B_WR); -+		for (uint8_t i = 1; i < 7; i++) -+			write_subseq(channel, i, ss_b | SUBSEQ_B_RD_WR); -+ -+		write_subseq(channel, 7, ss_a | SUBSEQ_B_RD); -+		break; -+	case PAT_RD_WR_TA: -+		write_subseq(channel, 0, ss_b | SUBSEQ_B_WR_RD); -+		break; -+	case PAT_ODT_TA: -+		write_subseq(channel, 0, ss_a | SUBSEQ_B_WR); -+		write_subseq(channel, 1, ss_b | SUBSEQ_B_RD_WR); -+		write_subseq(channel, 2, ss_a | SUBSEQ_B_RD); -+		write_subseq(channel, 3, ss_b | SUBSEQ_B_WR_RD); -+		break; -+	default: -+		write_subseq(channel, 0, ss_a | SUBSEQ_B_WR); -+		write_subseq(channel, 1, ss_a | SUBSEQ_B_RD); -+		break; -+	} -+} -+ -+void setup_io_test( -+	struct sysinfo *ctrl, -+	const uint8_t chanmask, -+	const enum reut_cmd_pat cmd_pat, -+	const uint16_t num_cl, -+	const uint8_t lc, -+	const struct reut_box *const reut_addr, -+	const enum test_stop soe, -+	const struct wdb_pat *const pat, -+	const uint8_t en_cadb, -+	const uint8_t subseq_wait) -+{ -+	if (!chanmask) { -+		printk(BIOS_ERR, "\n%s: chanmask is invalid\n", __func__); -+		return; -+	} -+ -+	/* -+	 * Prepare variables needed for both channels. -+	 * Check for the cases where this MUST be 1: when -+	 * we manually walk through subseq ODT and TA Wr. -+	 */ -+	uint8_t lc_exp = MAX(lc - log2_ceil(num_cl), 0); -+	if (cmd_pat == PAT_WR_RD_TA || cmd_pat == PAT_ODT_TA) -+		lc_exp = 0; -+ -+	uint8_t num_clcr; -+	if (num_cl > 127) { -+		/* Assume exponential number */ -+		num_clcr = log2_ceil(num_cl); -+	} else { -+		/* Set number of cache lines as linear number */ -+		num_clcr = num_cl | BIT(7); -+	} -+ -+	const uint16_t num_cl2 = 2 * num_cl; -+	uint8_t num_cl2cr; -+	if (num_cl2 > 127) { -+		/* Assume exponential number */ -+		num_cl2cr = log2_ceil(num_cl2); -+	} else { -+		/* Set number of cache lines as linear number */ -+		num_cl2cr = num_cl2 | BIT(7); -+	} -+ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!(chanmask & BIT(channel))) { -+			union reut_seq_cfg_reg reut_seq_cfg = { -+				.raw = mchbar_read64(REUT_ch_SEQ_CFG(channel)), -+			}; -+			reut_seq_cfg.global_control = 0; -+			mchbar_write64(REUT_ch_SEQ_CFG(channel), reut_seq_cfg.raw); -+			continue; -+		} -+ -+		/* -+		 * Program CADB -+		 */ -+		mchbar_write8(REUT_ch_MISC_PAT_CADB_CTRL(channel), !!en_cadb); -+		if (en_cadb) -+			setup_cadb(ctrl, channel, 7, 8); -+ -+		/* -+		 * Program sequence -+		 */ -+		uint8_t subseq_start = 0; -+		uint8_t subseq_end   = 0; -+		switch (cmd_pat) { -+		case PAT_WR_RD: -+			subseq_end = 1; -+			break; -+		case PAT_WR: -+			break; -+		case PAT_RD: -+			subseq_start = 1; -+			subseq_end   = 1; -+			break; -+		case PAT_RD_WR_TA: -+			break; -+		case PAT_WR_RD_TA: -+			subseq_end = 7; -+			break; -+		case PAT_ODT_TA: -+			subseq_end = 3; -+			break; -+		default: -+			die("\n%s: Pattern type %u is invalid\n", __func__, cmd_pat); -+		} -+		const union reut_seq_cfg_reg reut_seq_cfg = { -+			.global_control            = 1, -+			.initialization_mode       = REUT_MODE_TEST, -+			.subsequence_start_pointer = subseq_start, -+			.subsequence_end_pointer   = subseq_end, -+			.start_test_delay          = 2, -+		}; -+		mchbar_write64(REUT_ch_SEQ_CFG(channel), reut_seq_cfg.raw); -+		program_loop_count(ctrl, channel, lc_exp); -+		mchbar_write32(REUT_ch_SEQ_CTL(channel), (union reut_seq_ctl_reg) { -+			.clear_errors = 1, -+		}.raw); -+ -+		/* -+		 * Program subsequences -+		 */ -+		uint32_t subseq_a = 0; -+ -+		/* Number of cachelines and scale */ -+		subseq_a |= (num_clcr    & 0x00ff) << 0; -+		subseq_a |= (subseq_wait & 0x3fff) << 8; -+ -+		/* Reset current base address to start */ -+		subseq_a |= BIT(27); -+ -+		uint32_t subseq_b = 0; -+ -+		/* Number of cachelines and scale */ -+		subseq_b |= (num_cl2cr   & 0x00ff) << 0; -+		subseq_b |= (subseq_wait & 0x3fff) << 8; -+ -+		/* Reset current base address to start */ -+		subseq_b |= BIT(27); -+ -+		program_subseq(ctrl, channel, cmd_pat, subseq_a, subseq_b); -+ -+		/* Program sequence address */ -+		program_seq_addr(channel, reut_addr, false); -+ -+		/* Program WDB */ -+		const bool is_linear = pat->inc_rate < 32; -+		mchbar_write32(REUT_ch_WDB_CL_CTRL(channel), (union reut_pat_wdb_cl_ctrl_reg) { -+			.start_ptr = pat->start_ptr, -+			.end_ptr   = pat->stop_ptr, -+			.inc_rate  = is_linear ? pat->inc_rate : log2_ceil(pat->inc_rate), -+			.inc_scale = is_linear, -+		}.raw); -+ -+		/* Enable LMN in LMN or CADB modes, used to create lots of supply noise */ -+		const bool use_lmn = pat->dq_pattern == LMN_VA || pat->dq_pattern == CADB; -+		union reut_pat_wdb_cl_mux_cfg_reg pat_wdb_cl_mux_cfg = { -+			.mux_0_control = use_lmn ? REUT_MUX_LMN : REUT_MUX_LFSR, -+			.mux_1_control = REUT_MUX_LFSR, -+			.mux_2_control = REUT_MUX_LFSR, -+			.ecc_data_source_sel = 1, -+		}; -+ -+		/* Program LFSR save/restore, too complex unless everything is power of 2 */ -+		if (cmd_pat == PAT_ODT_TA || cmd_pat == PAT_WR_RD_TA) { -+			pat_wdb_cl_mux_cfg.reload_lfsr_seed_rate = log2_ceil(num_cl) + 1; -+			pat_wdb_cl_mux_cfg.save_lfsr_seed_rate   = 1; -+		} -+		mchbar_write32(REUT_ch_PAT_WDB_CL_MUX_CFG(channel), pat_wdb_cl_mux_cfg.raw); -+ -+		/* Inversion mask is not used */ -+		mchbar_write32(REUT_ch_PAT_WDB_INV(channel), 0); -+ -+		/* Program error checking */ -+		const union reut_err_ctl_reg reut_err_ctl = { -+			.selective_err_enable_cacheline = 0xff, -+			.selective_err_enable_chunk     = 0xff, -+			.stop_on_error_control          = soe, -+			.stop_on_nth_error              = 1, -+		}; -+		mchbar_write32(REUT_ch_ERR_CONTROL(channel), reut_err_ctl.raw); -+		mchbar_write64(REUT_ch_ERR_DATA_MASK(channel), 0); -+		mchbar_write8(REUT_ch_ERR_ECC_MASK(channel), 0); -+	} -+ -+	/* Always do a ZQ short before the beginning of a test */ -+	reut_issue_zq(ctrl, chanmask, ZQ_SHORT); -+} -+ -+void setup_io_test_cadb( -+	struct sysinfo *ctrl, -+	const uint8_t chanmask, -+	const uint8_t lc, -+	const enum test_stop soe) -+{ -+	const struct reut_box reut_addr = { -+		.rank = { -+			.start    = 0, -+			.stop     = 0, -+			.inc_rate = 32, -+			.inc_val  = 1, -+		}, -+		.bank = { -+			.start    = 0, -+			.stop     = 7, -+			.inc_rate = 3, -+			.inc_val  = 1, -+		}, -+		.row = { -+			.start    = 0, -+			.stop     = 2047, -+			.inc_rate = 3, -+			.inc_val  = 73, -+		}, -+		.col = { -+			.start    = 0, -+			.stop     = 1023, -+			.inc_rate = 0, -+			.inc_val  = 53, -+		}, -+	}; -+	const struct wdb_pat pattern = { -+		.start_ptr  = 0, -+		.stop_ptr   = 9, -+		.inc_rate   = 4, -+		.dq_pattern = CADB, -+	}; -+	setup_io_test( -+		ctrl, -+		chanmask, -+		PAT_WR_RD, -+		128, -+		lc, -+		&reut_addr, -+		soe, -+		&pattern, -+		1, -+		0); -+ -+	ctrl->dq_pat_lc = MAX(lc - 2 - 3, 0) + 1; -+	ctrl->dq_pat = CADB; -+} -+ -+void setup_io_test_basic_va( -+	struct sysinfo *ctrl, -+	const uint8_t chanmask, -+	const uint8_t lc, -+	const enum test_stop soe) -+{ -+	const uint32_t spread = 8; -+	const struct reut_box reut_addr = { -+		.rank = { -+			.start    = 0, -+			.stop     = 0, -+			.inc_rate = 32, -+			.inc_val  = 1, -+		}, -+		.col = { -+			.start    = 0, -+			.stop     = 1023, -+			.inc_rate = 0, -+			.inc_val  = 1, -+		}, -+	}; -+	const struct wdb_pat pattern = { -+		.start_ptr  = 0, -+		.stop_ptr   = spread - 1, -+		.inc_rate   = 4, -+		.dq_pattern = BASIC_VA, -+	}; -+	setup_io_test( -+		ctrl, -+		chanmask, -+		PAT_WR_RD, -+		128, -+		lc, -+		&reut_addr, -+		soe, -+		&pattern, -+		0, -+		0); -+ -+	ctrl->dq_pat_lc = MAX(lc - 8, 0) + 1; -+	ctrl->dq_pat = BASIC_VA; -+} -+ -+void setup_io_test_mpr( -+	struct sysinfo *ctrl, -+	const uint8_t chanmask, -+	const uint8_t lc, -+	const enum test_stop soe) -+{ -+	const struct reut_box reut_addr_ddr = { -+		.rank = { -+			.start    = 0, -+			.stop     = 0, -+			.inc_rate = 32, -+			.inc_val  = 1, -+		}, -+		.col = { -+			.start    = 0, -+			.stop     = 1023, -+			.inc_rate = 0, -+			.inc_val  = 1, -+		}, -+	}; -+	const struct reut_box reut_addr_lpddr = { -+		.bank = { -+			.start    = 4, -+			.stop     = 4, -+			.inc_rate = 0, -+			.inc_val  = 0, -+		}, -+	}; -+	const struct wdb_pat pattern = { -+		.start_ptr  = 0, -+		.stop_ptr   = 9, -+		.inc_rate   = 4, -+		.dq_pattern = BASIC_VA, -+	}; -+	setup_io_test( -+		ctrl, -+		chanmask, -+		PAT_RD, -+		128, -+		lc, -+		ctrl->lpddr ? &reut_addr_lpddr : &reut_addr_ddr, -+		soe, -+		&pattern, -+		0, -+		0); -+ -+	ctrl->dq_pat_lc = 1; -+	ctrl->dq_pat = BASIC_VA; -+} -+ -+uint8_t select_reut_ranks(struct sysinfo *ctrl, const uint8_t channel, uint8_t rankmask) -+{ -+	rankmask &= ctrl->rankmap[channel]; -+ -+	uint8_t rank_count = 0; -+	uint32_t rank_log_to_phys = 0; -+	for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { -+		if (!rank_in_mask(rank, rankmask)) -+			continue; -+ -+		rank_log_to_phys |= rank << (4 * rank_count); -+		rank_count++; -+	} -+	mchbar_write32(REUT_ch_RANK_LOG_TO_PHYS(channel), rank_log_to_phys); -+ -+	union reut_seq_cfg_reg reut_seq_cfg = { -+		.raw = mchbar_read64(REUT_ch_SEQ_CFG(channel)), -+	}; -+	if (!rank_count) { -+		reut_seq_cfg.global_control = 0; -+		mchbar_write64(REUT_ch_SEQ_CFG(channel), reut_seq_cfg.raw); -+		return 0; -+	} -+	union reut_seq_base_addr_reg reut_seq_addr_stop = { -+		.raw = mchbar_read64(REUT_ch_SEQ_ADDR_WRAP(channel)), -+	}; -+	reut_seq_addr_stop.rank_addr = rank_count - 1; -+	mchbar_write64(REUT_ch_SEQ_ADDR_WRAP(channel), reut_seq_addr_stop.raw); -+ -+	reut_seq_cfg.global_control = 1; -+	mchbar_write64(REUT_ch_SEQ_CFG(channel), reut_seq_cfg.raw); -+	return BIT(channel); -+} -+ -+void run_mpr_io_test(const bool clear_errors) -+{ -+	io_reset(); -+	mchbar_write32(REUT_GLOBAL_CTL, (union reut_seq_ctl_reg) { -+		.start_test   = 1, -+		.clear_errors = clear_errors, -+	}.raw); -+	tick_delay(2); -+	io_reset(); -+	tick_delay(2); -+	mchbar_write32(REUT_GLOBAL_CTL, (union reut_seq_ctl_reg) { -+		.stop_test = 1, -+	}.raw); -+} -+ -+static uint8_t get_num_tests(const uint8_t dq_pat) -+{ -+	switch (dq_pat) { -+	case SEGMENT_WDB:	return 4; -+	case CADB:		return 7; -+	case TURN_AROUND_WR:	return 8; -+	case TURN_AROUND_ODT:	return 4; -+	case RD_RD_TA:		return 2; -+	case RD_RD_TA_ALL:	return 8; -+	default:		return 1; -+	} -+} -+ -+uint8_t run_io_test( -+	struct sysinfo *const ctrl, -+	const uint8_t chanmask, -+	const uint8_t dq_pat, -+	const bool clear_errors) -+{ -+	/* SEGMENT_WDB only runs 4 tests */ -+	const uint8_t segment_wdb_lc[4] = { 0, 0, 4, 2 }; -+	const union reut_pat_wdb_cl_ctrl_reg pat_wdb_cl[4] = { -+		[0] = { -+			.start_ptr =  0, -+			.end_ptr   =  9, -+			.inc_rate  = 25, -+			.inc_scale = SCALE_LINEAR, -+		}, -+		[1] = { -+			.start_ptr =  0, -+			.end_ptr   =  9, -+			.inc_rate  = 25, -+			.inc_scale = SCALE_LINEAR, -+		}, -+		[2] = { -+			.start_ptr = 10, -+			.end_ptr   = 63, -+			.inc_rate  = 19, -+			.inc_scale = SCALE_LINEAR, -+		}, -+		[3] = { -+			.start_ptr = 10, -+			.end_ptr   = 63, -+			.inc_rate  = 10, -+			.inc_scale = SCALE_LINEAR, -+		}, -+	}; -+	const bool is_turnaround = dq_pat == RD_RD_TA || dq_pat == RD_RD_TA_ALL; -+	const uint8_t num_tests = get_num_tests(dq_pat); -+	union tc_bank_rank_a_reg tc_bank_rank_a[NUM_CHANNELS] = { 0 }; -+	if (is_turnaround) { -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!(chanmask & BIT(channel))) -+				continue; -+ -+			tc_bank_rank_a[channel].raw = ctrl->tc_bankrank_a[channel].raw; -+		} -+	} -+	for (uint8_t t = 0; t < num_tests; t++) { -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!(chanmask & BIT(channel))) -+				continue; -+ -+			if (dq_pat == SEGMENT_WDB) { -+				mchbar_write32(REUT_ch_WDB_CL_CTRL(channel), pat_wdb_cl[t].raw); -+				/* -+				 * Skip programming LFSR save/restore. Too complex -+				 * unless power of 2. Program desired loopcount. -+				 */ -+				const uint8_t pat_lc = ctrl->dq_pat_lc + segment_wdb_lc[t]; -+				program_loop_count(ctrl, channel, pat_lc); -+			} else if (dq_pat == CADB) { -+				setup_cadb(ctrl, channel, num_tests, t); -+			} else if (dq_pat == TURN_AROUND_WR || dq_pat == TURN_AROUND_ODT) { -+				union reut_seq_cfg_reg reut_seq_cfg = { -+					.raw = mchbar_read64(REUT_ch_SEQ_CFG(channel)), -+				}; -+				reut_seq_cfg.subsequence_start_pointer = t; -+				reut_seq_cfg.subsequence_end_pointer   = t; -+				mchbar_write64(REUT_ch_SEQ_CFG(channel), reut_seq_cfg.raw); -+				union reut_seq_addr_inc_ctl_reg addr_inc_ctl = { -+					.raw = mchbar_read64(REUT_ch_SEQ_ADDR_INC_CTL(channel)), -+				}; -+				uint8_t ta_inc_rate = 1; -+				if (dq_pat == TURN_AROUND_WR && (t == 0 || t == 7)) -+					ta_inc_rate = 0; -+				else if (dq_pat == TURN_AROUND_ODT && (t == 0 || t == 2)) -+					ta_inc_rate = 0; -+ -+				/* Program increment rate as linear value */ -+				addr_inc_ctl.rank_addr_update = BIT(7) | ta_inc_rate; -+				addr_inc_ctl.col_addr_update  = BIT(7) | ta_inc_rate; -+				mchbar_write64(REUT_ch_SEQ_ADDR_INC_CTL(channel), -+						addr_inc_ctl.raw); -+			} else if (dq_pat == RD_RD_TA) { -+				tc_bank_rank_a[channel].tRDRD_sr = (t == 0) ? 4 : 5; -+				mchbar_write32(TC_BANK_RANK_A_ch(channel), -+						tc_bank_rank_a[channel].raw); -+			} else if (dq_pat == RD_RD_TA_ALL) { -+				/* -+				 * Program tRDRD for SR and DR. Run 8 tests, covering -+				 * tRDRD_sr = 4, 5, 6, 7 and tRDRD_dr = min, +1, +2, +3 -+				 */ -+				const uint32_t tRDRD_dr = ctrl->tc_bankrank_a[channel].tRDRD_dr; -+				tc_bank_rank_a[channel].tRDRD_sr = (t % 4) + 4; -+				tc_bank_rank_a[channel].tRDRD_dr = (t % 4) + tRDRD_dr; -+				mchbar_write32(TC_BANK_RANK_A_ch(channel), -+						tc_bank_rank_a[channel].raw); -+ -+				/* Program linear rank increment rate */ -+				union reut_seq_addr_inc_ctl_reg addr_inc_ctl = { -+					.raw = mchbar_read64(REUT_ch_SEQ_ADDR_INC_CTL(channel)), -+				}; -+				addr_inc_ctl.rank_addr_update = BIT(7) | (t / 4) ? 0 : 31; -+				mchbar_write64(REUT_ch_SEQ_ADDR_INC_CTL(channel), -+						addr_inc_ctl.raw); -+			} -+		} -+		bool test_soe = false; -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!(chanmask & BIT(channel))) -+				continue; -+ -+			const union reut_err_ctl_reg reut_err_ctl = { -+				.raw = mchbar_read32(REUT_ch_ERR_CONTROL(channel)), -+			}; -+			const uint8_t soe = reut_err_ctl.stop_on_error_control; -+			if (soe != NSOE) { -+				test_soe = true; -+				break; -+			} -+		} -+		io_reset(); -+		mchbar_write32(REUT_GLOBAL_CTL, (union reut_seq_ctl_reg) { -+			.start_test   = 1, -+			.clear_errors = clear_errors && t == 0, -+		}.raw); -+		struct mono_time prev, curr; -+		timer_monotonic_get(&prev); -+		union reut_global_err_reg global_err; -+		do { -+			global_err.raw = mchbar_read32(REUT_GLOBAL_ERR); -+			/** TODO: Clean up this mess **/ -+			timer_monotonic_get(&curr); -+			if (mono_time_diff_microseconds(&prev, &curr) > 1000 * 1000) { -+				mchbar_write32(REUT_GLOBAL_CTL, (union reut_seq_ctl_reg) { -+					.stop_test = 1, -+				}.raw); -+				printk(BIOS_ERR, "REUT timed out, ch_done: %x\n", -+					global_err.ch_test_done); -+				break; -+			} -+		} while ((global_err.ch_test_done & chanmask) != chanmask); -+		if (test_soe && global_err.ch_error & chanmask) -+			break; -+	} -+	if (is_turnaround) { -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!(chanmask & BIT(channel))) -+				continue; -+ -+			mchbar_write32(TC_BANK_RANK_A_ch(channel), -+				ctrl->tc_bankrank_a[channel].raw); -+		} -+	} -+	return ((union reut_global_err_reg)mchbar_read32(REUT_GLOBAL_ERR)).ch_error; -+} -diff --git a/src/northbridge/intel/haswell/registers/mchbar.h b/src/northbridge/intel/haswell/registers/mchbar.h -index f8408e51a0..817a9f8bf8 100644 ---- a/src/northbridge/intel/haswell/registers/mchbar.h -+++ b/src/northbridge/intel/haswell/registers/mchbar.h -@@ -94,20 +94,35 @@ - #define TC_BANK_RANK_D_ch(ch)			_MCMAIN_C(0x4014, ch) - #define SC_ROUNDT_LAT_ch(ch)			_MCMAIN_C(0x4024, ch) -  -+#define REUT_ch_PAT_WDB_CL_MUX_CFG(ch)		_MCMAIN_C(0x4040, ch) -+ - #define REUT_ch_PAT_WDB_CL_MUX_WR_x(ch, x)	_MCMAIN_C_X(0x4048, ch, x) /* x in 0 .. 2 */ - #define REUT_ch_PAT_WDB_CL_MUX_RD_x(ch, x)	_MCMAIN_C_X(0x4054, ch, x) /* x in 0 .. 2 */ -  - #define REUT_ch_PAT_WDB_CL_MUX_LMN(ch)		_MCMAIN_C(0x4078, ch) -  -+#define REUT_ch_PAT_WDB_INV(ch)			_MCMAIN_C(0x4084, ch) -+ -+#define REUT_ch_ERR_CONTROL(ch)			_MCMAIN_C(0x4098, ch) -+#define REUT_ch_ERR_ECC_MASK(ch)		_MCMAIN_C(0x409c, ch) -+ - #define SC_WR_ADD_DELAY_ch(ch)			_MCMAIN_C(0x40d0, ch) -  -+#define REUT_ch_ERR_DATA_MASK(ch)		_MCMAIN_C(0x40d8, ch) -+ - #define REUT_ch_MISC_CKE_CTRL(ch)		_MCMAIN_C(0x4190, ch) -  -+#define REUT_ch_MISC_PAT_CADB_CTRL(ch)		_MCMAIN_C(0x4198, ch) - #define REUT_ch_PAT_CADB_MRS(ch)		_MCMAIN_C(0x419c, ch) -+#define REUT_ch_PAT_CADB_MUX_CTRL(ch)		_MCMAIN_C(0x41a0, ch) -+#define REUT_ch_PAT_CADB_MUX_x(ch, x)		_MCMAIN_C_X(0x41a4, ch, x) /* x in 0 .. 2 */ -  -+#define REUT_ch_PAT_CADB_CL_MUX_LMN(ch)		_MCMAIN_C(0x41b0, ch) - #define REUT_ch_PAT_CADB_WRITE_PTR(ch)		_MCMAIN_C(0x41bc, ch) - #define REUT_ch_PAT_CADB_PROG(ch)		_MCMAIN_C(0x41c0, ch) -  -+#define REUT_ch_WDB_CL_CTRL(ch)			_MCMAIN_C(0x4200, ch) -+ - #define TC_ZQCAL_ch(ch)				_MCMAIN_C(0x4290, ch) - #define TC_RFP_ch(ch)				_MCMAIN_C(0x4294, ch) - #define TC_RFTP_ch(ch)				_MCMAIN_C(0x4298, ch) -@@ -119,12 +134,27 @@ - #define QCLK_ch_LDAT_SDAT(ch)			_MCMAIN_C(0x42d4, ch) - #define QCLK_ch_LDAT_DATA_IN_x(ch, x)		_MCMAIN_C_X(0x42dc, ch, x) /* x in 0 .. 1 */ -  -+#define REUT_GLOBAL_CTL				0x4800 - #define REUT_GLOBAL_ERR				0x4804 -  -+#define REUT_ch_SUBSEQ_x_CTL(ch, x)		(0x4808 + 40 * (ch) + 4 * (x)) -+ - #define REUT_ch_SEQ_CFG(ch)			(0x48a8 + 8 * (ch)) -  - #define REUT_ch_SEQ_CTL(ch)			(0x48b8 + 4 * (ch)) -  -+#define REUT_ch_SEQ_ADDR_START(ch)		(0x48d8 + 8 * (ch)) -+ -+#define REUT_ch_SEQ_ADDR_WRAP(ch)		(0x48e8 + 8 * (ch)) -+ -+#define REUT_ch_SEQ_MISC_CTL(ch)		(0x4908 + 4 * (ch)) -+ -+#define REUT_ch_SEQ_ADDR_INC_CTL(ch)		(0x4910 + 8 * (ch)) -+ -+#define REUT_ch_RANK_LOG_TO_PHYS(ch)		(0x4930 + 4 * (ch)) /* 4 bits per rank */ -+ -+#define HSW_REUT_ch_SEQ_LOOP_COUNT(ch)		(0x4980 + 4 * (ch)) /* *** only on C0 *** */ -+ - /* MCMAIN broadcast */ - #define MCSCHEDS_CBIT		0x4c20 -  ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0009-haswell-NRI-Add-range-tracking-library.patch b/config/coreboot/haswell/patches/0009-haswell-NRI-Add-range-tracking-library.patch deleted file mode 100644 index 9c3fe1c9..00000000 --- a/config/coreboot/haswell/patches/0009-haswell-NRI-Add-range-tracking-library.patch +++ /dev/null @@ -1,222 +0,0 @@ -From 7f5c3f8c6c8960d1c374b9c95821c19f230fa34f Mon Sep 17 00:00:00 2001 -From: Angel Pons <th3fanbus@gmail.com> -Date: Sun, 8 May 2022 00:56:00 +0200 -Subject: [PATCH 09/20] haswell NRI: Add range tracking library - -Implement a small library used to keep track of passing ranges. This -will be used by 1D training algorithms when margining some parameter. - -Change-Id: I8718e85165160afd7c0c8e730b5ce6c9c00f8a60 -Signed-off-by: Angel Pons <th3fanbus@gmail.com> ---- - .../intel/haswell/native_raminit/Makefile.mk  |   1 + - .../intel/haswell/native_raminit/ranges.c     | 109 ++++++++++++++++++ - .../intel/haswell/native_raminit/ranges.h     |  68 +++++++++++ - 3 files changed, 178 insertions(+) - create mode 100644 src/northbridge/intel/haswell/native_raminit/ranges.c - create mode 100644 src/northbridge/intel/haswell/native_raminit/ranges.h - -diff --git a/src/northbridge/intel/haswell/native_raminit/Makefile.mk b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -index 6e1b365602..2da950771d 100644 ---- a/src/northbridge/intel/haswell/native_raminit/Makefile.mk -+++ b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -@@ -9,6 +9,7 @@ romstage-y += io_comp_control.c - romstage-y += memory_map.c - romstage-y += raminit_main.c - romstage-y += raminit_native.c -+romstage-y += ranges.c - romstage-y += reut.c - romstage-y += setup_wdb.c - romstage-y += spd_bitmunching.c -diff --git a/src/northbridge/intel/haswell/native_raminit/ranges.c b/src/northbridge/intel/haswell/native_raminit/ranges.c -new file mode 100644 -index 0000000000..cdebc1fa66 ---- /dev/null -+++ b/src/northbridge/intel/haswell/native_raminit/ranges.c -@@ -0,0 +1,109 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#include <types.h> -+ -+#include "ranges.h" -+ -+void linear_record_pass( -+	struct linear_train_data *const data, -+	const bool pass, -+	const int32_t value, -+	const int32_t start, -+	const int32_t step) -+{ -+	/* If this is the first time, initialize all values */ -+	if (value == start) { -+		/* -+		 * If value passed, create a zero-length region for the current value, -+		 * which may be extended as long as the successive values are passing. -+		 * -+		 * Otherwise, create a zero-length range for the preceding value. This -+		 * range cannot be extended by other passing values, which is desired. -+		 */ -+		data->current.start = start - (pass ? 0 : step); -+		data->current.end   = data->current.start; -+		data->largest       = data->current; -+	} else if (pass) { -+		/* If this pass is not contiguous, it belongs to a new region */ -+		if (data->current.end != (value - step)) -+			data->current.start = value; -+ -+		/* Update end of current region */ -+		data->current.end = value; -+ -+		/* Update largest region */ -+		if (range_width(data->current) > range_width(data->largest)) -+			data->largest = data->current; -+	} -+} -+ -+void phase_record_pass( -+	struct phase_train_data *const data, -+	const bool pass, -+	const int32_t value, -+	const int32_t start, -+	const int32_t step) -+{ -+	/* If this is the first time, initialize all values */ -+	if (value == start) { -+		/* -+		 * If value passed, create a zero-length region for the current value, -+		 * which may be extended as long as the successive values are passing. -+		 * -+		 * Otherwise, create a zero-length range for the preceding value. This -+		 * range cannot be extended by other passing values, which is desired. -+		 */ -+		data->current.start = start - (pass ? 0 : step); -+		data->current.end   = data->current.start; -+		data->largest       = data->current; -+		data->initial       = data->current; -+		return; -+	} -+	if (!pass) -+		return; -+ -+	/* Update initial region */ -+	if (data->initial.end == (value - step)) -+		data->initial.end = value; -+ -+	/* If this pass is not contiguous, it belongs to a new region */ -+	if (data->current.end != (value - step)) -+		data->current.start = value; -+ -+	/* Update end of current region */ -+	data->current.end = value; -+ -+	/* Update largest region */ -+	if (range_width(data->current) > range_width(data->largest)) -+		data->largest = data->current; -+} -+ -+void phase_append_initial_to_current( -+	struct phase_train_data *const data, -+	const int32_t start, -+	const int32_t step) -+{ -+	/* If initial region is valid and does not overlap, append it */ -+	if (data->initial.start == start && data->initial.end != data->current.end) -+		data->current.end += step + range_width(data->initial); -+ -+	/* Update largest region */ -+	if (range_width(data->current) > range_width(data->largest)) -+		data->largest = data->current; -+} -+ -+void phase_append_current_to_initial( -+	struct phase_train_data *const data, -+	const int32_t start, -+	const int32_t step) -+{ -+	/* If initial region is valid and does not overlap, append it */ -+	if (data->initial.start == start && data->initial.end != data->current.end) { -+		data->initial.start -= (step + range_width(data->current)); -+		data->current = data->initial; -+	} -+ -+	/* Update largest region */ -+	if (range_width(data->current) > range_width(data->largest)) -+		data->largest = data->current; -+} -diff --git a/src/northbridge/intel/haswell/native_raminit/ranges.h b/src/northbridge/intel/haswell/native_raminit/ranges.h -new file mode 100644 -index 0000000000..235392df96 ---- /dev/null -+++ b/src/northbridge/intel/haswell/native_raminit/ranges.h -@@ -0,0 +1,68 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#ifndef HASWELL_RAMINIT_RANGES_H -+#define HASWELL_RAMINIT_RANGES_H -+ -+#include <types.h> -+ -+/* -+ * Many algorithms shmoo some parameter to determine the largest passing -+ * range. Provide a common implementation to avoid redundant boilerplate. -+ */ -+struct passing_range { -+	int32_t start; -+	int32_t end; -+}; -+ -+/* Structure for linear parameters, such as roundtrip delays */ -+struct linear_train_data { -+	struct passing_range current; -+	struct passing_range largest; -+}; -+ -+/* -+ * Phase ranges are "circular": the first and last indices are contiguous. -+ * To correctly determine the largest passing range, one has to combine -+ * the initial range and the current range when processing the last index. -+ */ -+struct phase_train_data { -+	struct passing_range initial; -+	struct passing_range current; -+	struct passing_range largest; -+}; -+ -+static inline int32_t range_width(const struct passing_range range) -+{ -+	return range.end - range.start; -+} -+ -+static inline int32_t range_center(const struct passing_range range) -+{ -+	return range.start + range_width(range) / 2; -+} -+ -+void linear_record_pass( -+	struct linear_train_data *data, -+	bool pass, -+	int32_t value, -+	int32_t start, -+	int32_t step); -+ -+void phase_record_pass( -+	struct phase_train_data *data, -+	bool pass, -+	int32_t value, -+	int32_t start, -+	int32_t step); -+ -+void phase_append_initial_to_current( -+	struct phase_train_data *data, -+	int32_t start, -+	int32_t step); -+ -+void phase_append_current_to_initial( -+	struct phase_train_data *data, -+	int32_t start, -+	int32_t step); -+ -+#endif ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0010-haswell-NRI-Add-library-to-change-margins.patch b/config/coreboot/haswell/patches/0010-haswell-NRI-Add-library-to-change-margins.patch deleted file mode 100644 index 622fac5a..00000000 --- a/config/coreboot/haswell/patches/0010-haswell-NRI-Add-library-to-change-margins.patch +++ /dev/null @@ -1,294 +0,0 @@ -From 8ad18cc335f60a78f47ab9e5a7994f6075b6a176 Mon Sep 17 00:00:00 2001 -From: Angel Pons <th3fanbus@gmail.com> -Date: Sun, 8 May 2022 01:11:03 +0200 -Subject: [PATCH 10/20] haswell NRI: Add library to change margins - -Implement a library to change Rx/Tx margins. It will be expanded later. - -Change-Id: I0b55aba428d8b4d4e16d2fbdec57235ce3ce8adf -Signed-off-by: Angel Pons <th3fanbus@gmail.com> ---- - .../intel/haswell/native_raminit/Makefile.mk  |   1 + - .../haswell/native_raminit/change_margin.c    | 154 ++++++++++++++++++ - .../haswell/native_raminit/raminit_native.h   |  50 ++++++ - .../intel/haswell/registers/mchbar.h          |   9 + - 4 files changed, 214 insertions(+) - create mode 100644 src/northbridge/intel/haswell/native_raminit/change_margin.c - -diff --git a/src/northbridge/intel/haswell/native_raminit/Makefile.mk b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -index 2da950771d..ebe9e9b762 100644 ---- a/src/northbridge/intel/haswell/native_raminit/Makefile.mk -+++ b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -@@ -1,5 +1,6 @@ - ## SPDX-License-Identifier: GPL-2.0-or-later -  -+romstage-y += change_margin.c - romstage-y += configure_mc.c - romstage-y += ddr3.c - romstage-y += jedec_reset.c -diff --git a/src/northbridge/intel/haswell/native_raminit/change_margin.c b/src/northbridge/intel/haswell/native_raminit/change_margin.c -new file mode 100644 -index 0000000000..055c666eee ---- /dev/null -+++ b/src/northbridge/intel/haswell/native_raminit/change_margin.c -@@ -0,0 +1,154 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#include <commonlib/bsd/clamp.h> -+#include <console/console.h> -+#include <delay.h> -+#include <northbridge/intel/haswell/haswell.h> -+#include <timer.h> -+ -+#include "raminit_native.h" -+ -+void update_rxt( -+	struct sysinfo *ctrl, -+	const uint8_t channel, -+	const uint8_t rank, -+	const uint8_t byte, -+	const enum rxt_subfield subfield, -+	const int32_t value) -+{ -+	union ddr_data_rx_train_rank_reg rxt = { -+		.rcven =  ctrl->rcven[channel][rank][byte], -+		.dqs_p = ctrl->rxdqsp[channel][rank][byte], -+		.rx_eq =  ctrl->rx_eq[channel][rank][byte], -+		.dqs_n = ctrl->rxdqsn[channel][rank][byte], -+		.vref  = ctrl->rxvref[channel][rank][byte], -+	}; -+	int32_t new_value; -+	switch (subfield) { -+	case RXT_RCVEN: -+		new_value = clamp_s32(0, value, 511); -+		rxt.rcven = new_value; -+		break; -+	case RXT_RXDQS_P: -+		new_value = clamp_s32(0, value, 63); -+		rxt.dqs_p = new_value; -+		break; -+	case RXT_RX_EQ: -+		new_value = clamp_s32(0, value, 31); -+		rxt.rx_eq = new_value; -+		break; -+	case RXT_RXDQS_N: -+		new_value = clamp_s32(0, value, 63); -+		rxt.dqs_n = new_value; -+		break; -+	case RXT_RX_VREF: -+		new_value = clamp_s32(-32, value, 31); -+		rxt.vref = new_value; -+		break; -+	case RXT_RXDQS_BOTH: -+		new_value = clamp_s32(0, value, 63); -+		rxt.dqs_p = new_value; -+		rxt.dqs_n = new_value; -+		break; -+	case RXT_RESTORE: -+		new_value = value; -+		break; -+	default: -+		die("%s: Unhandled subfield index %u\n", __func__, subfield); -+	} -+ -+	if (new_value != value) { -+		printk(BIOS_ERR, "%s: Overflow for subfield %u: %d ---> %d\n", -+			__func__, subfield, value, new_value); -+	} -+	mchbar_write32(RX_TRAIN_ch_r_b(channel, rank, byte), rxt.raw); -+	download_regfile(ctrl, channel, false, rank, REG_FILE_USE_RANK, byte, true, false); -+} -+ -+void update_txt( -+	struct sysinfo *ctrl, -+	const uint8_t channel, -+	const uint8_t rank, -+	const uint8_t byte, -+	const enum txt_subfield subfield, -+	const int32_t value) -+{ -+	union ddr_data_tx_train_rank_reg txt = { -+		.dq_delay  = ctrl->tx_dq[channel][rank][byte], -+		.dqs_delay = ctrl->txdqs[channel][rank][byte], -+		.tx_eq     = ctrl->tx_eq[channel][rank][byte], -+	}; -+	int32_t new_value; -+	switch (subfield) { -+	case TXT_TX_DQ: -+		new_value = clamp_s32(0, value, 511); -+		txt.dq_delay = new_value; -+		break; -+	case TXT_TXDQS: -+		new_value = clamp_s32(0, value, 511); -+		txt.dqs_delay = new_value; -+		break; -+	case TXT_TX_EQ: -+		new_value = clamp_s32(0, value, 63); -+		txt.tx_eq = new_value; -+		break; -+	case TXT_DQDQS_OFF: -+		new_value = value; -+		txt.dqs_delay += new_value; -+		txt.dq_delay  += new_value; -+		break; -+	case TXT_RESTORE: -+		new_value = value; -+		break; -+	default: -+		die("%s: Unhandled subfield index %u\n", __func__, subfield); -+	} -+	if (new_value != value) { -+		printk(BIOS_ERR, "%s: Overflow for subfield %u: %d ---> %d\n", -+			__func__, subfield, value, new_value); -+	} -+	mchbar_write32(TX_TRAIN_ch_r_b(channel, rank, byte), txt.raw); -+	download_regfile(ctrl, channel, false, rank, REG_FILE_USE_RANK, byte, false, true); -+} -+ -+void download_regfile( -+	struct sysinfo *ctrl, -+	const uint8_t channel, -+	const bool multicast, -+	const uint8_t rank, -+	const enum regfile_mode regfile, -+	const uint8_t byte, -+	const bool read_rf_rd, -+	const bool read_rf_wr) -+{ -+	union reut_seq_base_addr_reg reut_seq_base_addr; -+	switch (regfile) { -+	case REG_FILE_USE_START: -+		reut_seq_base_addr.raw = mchbar_read64(REUT_ch_SEQ_ADDR_START(channel)); -+		break; -+	case REG_FILE_USE_CURRENT: -+		reut_seq_base_addr.raw = mchbar_read64(REUT_ch_SEQ_ADDR_CURRENT(channel)); -+		break; -+	case REG_FILE_USE_RANK: -+		reut_seq_base_addr.raw = 0; -+		if (rank >= NUM_SLOTRANKS) -+			die("%s: bad rank %u\n", __func__, rank); -+		break; -+	default: -+		die("%s: Invalid regfile param %u\n", __func__, regfile); -+	} -+	uint8_t phys_rank = rank; -+	if (reut_seq_base_addr.raw != 0) { -+		/* Map REUT logical rank to physical rank */ -+		const uint32_t log_to_phys = mchbar_read32(REUT_ch_RANK_LOG_TO_PHYS(channel)); -+		phys_rank = log_to_phys >> (reut_seq_base_addr.rank_addr * 4) & 0x3; -+	} -+	uint32_t reg = multicast ? DDR_DATA_ch_CONTROL_0(channel) : DQ_CONTROL_0(channel, byte); -+	union ddr_data_control_0_reg ddr_data_control_0 = { -+		.raw = mchbar_read32(reg), -+	}; -+	ddr_data_control_0.read_rf_rd   = read_rf_rd; -+	ddr_data_control_0.read_rf_wr   = read_rf_wr; -+	ddr_data_control_0.read_rf_rank = phys_rank; -+	mchbar_write32(reg, ddr_data_control_0.raw); -+} -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -index 7f19fde4cc..906b3143b9 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -@@ -117,6 +117,30 @@ enum test_stop { - 	ALSOE  = 3,	/* Stop on all lanes error */ - }; -  -+enum rxt_subfield { -+	RXT_RCVEN	= 0, -+	RXT_RXDQS_P	= 1, -+	RXT_RX_EQ	= 2, -+	RXT_RXDQS_N	= 3, -+	RXT_RX_VREF	= 4, -+	RXT_RXDQS_BOTH	= 5, -+	RXT_RESTORE	= 255, -+}; -+ -+enum txt_subfield { -+	TXT_TX_DQ	= 0, -+	TXT_TXDQS	= 1, -+	TXT_TX_EQ	= 2, -+	TXT_DQDQS_OFF	= 3, -+	TXT_RESTORE	= 255, -+}; -+ -+enum regfile_mode { -+	REG_FILE_USE_RANK,	/* Used when changing parameters for each rank */ -+	REG_FILE_USE_START,	/* Used when changing parameters before the test */ -+	REG_FILE_USE_CURRENT,	/* Used when changing parameters after the test */ -+}; -+ - struct wdb_pat { - 	uint32_t start_ptr;	/* Starting pointer in WDB */ - 	uint32_t stop_ptr;	/* Stopping pointer in WDB */ -@@ -450,6 +474,32 @@ uint8_t select_reut_ranks(struct sysinfo *ctrl, uint8_t channel, uint8_t rankmas - void run_mpr_io_test(bool clear_errors); - uint8_t run_io_test(struct sysinfo *ctrl, uint8_t chanmask, uint8_t dq_pat, bool clear_errors); -  -+void update_rxt( -+	struct sysinfo *ctrl, -+	uint8_t channel, -+	uint8_t rank, -+	uint8_t byte, -+	enum rxt_subfield subfield, -+	int32_t value); -+ -+void update_txt( -+	struct sysinfo *ctrl, -+	uint8_t channel, -+	uint8_t rank, -+	uint8_t byte, -+	enum txt_subfield subfield, -+	int32_t value); -+ -+void download_regfile( -+	struct sysinfo *ctrl, -+	uint8_t channel, -+	bool multicast, -+	uint8_t rank, -+	enum regfile_mode regfile, -+	uint8_t byte, -+	bool read_rf_rd, -+	bool read_rf_wr); -+ - uint8_t get_rx_bias(const struct sysinfo *ctrl); -  - uint8_t get_tCWL(uint32_t mem_clock_mhz); -diff --git a/src/northbridge/intel/haswell/registers/mchbar.h b/src/northbridge/intel/haswell/registers/mchbar.h -index 817a9f8bf8..a81559bb1e 100644 ---- a/src/northbridge/intel/haswell/registers/mchbar.h -+++ b/src/northbridge/intel/haswell/registers/mchbar.h -@@ -15,7 +15,11 @@ - /* Register definitions */ -  - /* DDR DATA per-channel per-bytelane */ -+#define RX_TRAIN_ch_r_b(ch, rank, byte)		_DDRIO_C_R_B(0x0000, ch, rank, byte) -+#define TX_TRAIN_ch_r_b(ch, rank, byte)		_DDRIO_C_R_B(0x0020, ch, rank, byte) -+ - #define DQ_CONTROL_2(ch, byte)			_DDRIO_C_R_B(0x0064, ch, 0, byte) -+#define DQ_CONTROL_0(ch, byte)			_DDRIO_C_R_B(0x0074, ch, 0, byte) -  - /* DDR CKE per-channel */ - #define DDR_CKE_ch_CMD_COMP_OFFSET(ch)		_DDRIO_C_R_B(0x1204, ch, 0, 0) -@@ -38,6 +42,9 @@ - #define DDR_SCRAMBLE_ch(ch)			(0x2000 + 4 * (ch)) - #define DDR_SCRAM_MISC_CONTROL			0x2008 -  -+/* DDR DATA per-channel multicast */ -+#define DDR_DATA_ch_CONTROL_0(ch)		_DDRIO_C_R_B(0x3074, ch, 0, 0) -+ - /* DDR CMDN/CMDS per-channel (writes go to both CMDN and CMDS fubs) */ - #define DDR_CMD_ch_COMP_OFFSET(ch)		_DDRIO_C_R_B(0x3204, ch, 0, 0) - #define DDR_CMD_ch_PI_CODING(ch)		_DDRIO_C_R_B(0x3208, ch, 0, 0) -@@ -147,6 +154,8 @@ -  - #define REUT_ch_SEQ_ADDR_WRAP(ch)		(0x48e8 + 8 * (ch)) -  -+#define REUT_ch_SEQ_ADDR_CURRENT(ch)		(0x48f8 + 8 * (ch)) -+ - #define REUT_ch_SEQ_MISC_CTL(ch)		(0x4908 + 4 * (ch)) -  - #define REUT_ch_SEQ_ADDR_INC_CTL(ch)		(0x4910 + 8 * (ch)) ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0011-haswell-NRI-Add-RcvEn-training.patch b/config/coreboot/haswell/patches/0011-haswell-NRI-Add-RcvEn-training.patch deleted file mode 100644 index 4815be9a..00000000 --- a/config/coreboot/haswell/patches/0011-haswell-NRI-Add-RcvEn-training.patch +++ /dev/null @@ -1,708 +0,0 @@ -From 4254a9ff03658d7a6f1a4e32cfe4c65dbfc072f8 Mon Sep 17 00:00:00 2001 -From: Angel Pons <th3fanbus@gmail.com> -Date: Sun, 8 May 2022 00:05:41 +0200 -Subject: [PATCH 11/20] haswell NRI: Add RcvEn training - -Implement the RcvEn (Receive Enable) calibration procedure. - -Change-Id: Ifbfa520f3e0486c56d0988ce67af2ddb9cf29888 -Signed-off-by: Angel Pons <th3fanbus@gmail.com> ---- - .../intel/haswell/native_raminit/Makefile.mk  |   1 + - .../haswell/native_raminit/raminit_main.c     |   1 + - .../haswell/native_raminit/raminit_native.h   |  14 + - .../haswell/native_raminit/reg_structs.h      |  13 + - .../native_raminit/train_receive_enable.c     | 561 ++++++++++++++++++ - .../intel/haswell/registers/mchbar.h          |   3 + - 6 files changed, 593 insertions(+) - create mode 100644 src/northbridge/intel/haswell/native_raminit/train_receive_enable.c - -diff --git a/src/northbridge/intel/haswell/native_raminit/Makefile.mk b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -index ebe9e9b762..e2fbfb4211 100644 ---- a/src/northbridge/intel/haswell/native_raminit/Makefile.mk -+++ b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -@@ -16,3 +16,4 @@ romstage-y += setup_wdb.c - romstage-y += spd_bitmunching.c - romstage-y += testing_io.c - romstage-y += timings_refresh.c -+romstage-y += train_receive_enable.c -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_main.c b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -index 5e4674957d..7d444659c3 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_main.c -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -@@ -60,6 +60,7 @@ static const struct task_entry cold_boot[] = { - 	{ configure_memory_map,                                   true, "MEMMAP",     }, - 	{ do_jedec_init,                                          true, "JEDECINIT",  }, - 	{ pre_training,                                           true, "PRETRAIN",   }, -+	{ train_receive_enable,                                   true, "RCVET",      }, - }; -  - /* Return a generic stepping value to make stepping checks simpler */ -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -index 906b3143b9..b4e8c7de5a 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -@@ -42,6 +42,9 @@ - #define NUM_WDB_CL_MUX_SEEDS	3 - #define NUM_CADB_MUX_SEEDS	3 -  -+/* Specified in PI ticks. 64 PI ticks == 1 qclk */ -+#define tDQSCK_DRIFT		64 -+ - /* ZQ calibration types */ - enum { - 	ZQ_INIT,	/* DDR3: ZQCL with tZQinit, LPDDR3: ZQ Init  with tZQinit  */ -@@ -188,6 +191,7 @@ enum raminit_status { - 	RAMINIT_STATUS_MPLL_INIT_FAILURE, - 	RAMINIT_STATUS_POLL_TIMEOUT, - 	RAMINIT_STATUS_REUT_ERROR, -+	RAMINIT_STATUS_RCVEN_FAILURE, - 	RAMINIT_STATUS_UNSPECIFIED_ERROR, /** TODO: Deprecated in favor of specific values **/ - }; -  -@@ -270,6 +274,10 @@ struct sysinfo { -  - 	union ddr_data_vref_adjust_reg dimm_vref; -  -+	uint8_t io_latency[NUM_CHANNELS][NUM_SLOTRANKS]; -+	uint8_t rt_latency[NUM_CHANNELS][NUM_SLOTRANKS]; -+	uint32_t rt_io_comp[NUM_CHANNELS]; -+ - 	uint32_t data_offset_train[NUM_CHANNELS][NUM_LANES]; - 	uint32_t data_offset_comp[NUM_CHANNELS][NUM_LANES]; -  -@@ -344,6 +352,11 @@ static inline void clear_data_offset_train_all(struct sysinfo *ctrl) - 	memset(ctrl->data_offset_train, 0, sizeof(ctrl->data_offset_train)); - } -  -+static inline uint32_t get_data_train_feedback(const uint8_t channel, const uint8_t byte) -+{ -+	return mchbar_read32(DDR_DATA_TRAIN_FEEDBACK(channel, byte)); -+} -+ - /* Number of ticks to wait in units of 69.841279 ns (citation needed) */ - static inline void tick_delay(const uint32_t delay) - { -@@ -399,6 +412,7 @@ enum raminit_status convert_timings(struct sysinfo *ctrl); - enum raminit_status configure_mc(struct sysinfo *ctrl); - enum raminit_status configure_memory_map(struct sysinfo *ctrl); - enum raminit_status do_jedec_init(struct sysinfo *ctrl); -+enum raminit_status train_receive_enable(struct sysinfo *ctrl); -  - void configure_timings(struct sysinfo *ctrl); - void configure_refresh(struct sysinfo *ctrl); -diff --git a/src/northbridge/intel/haswell/native_raminit/reg_structs.h b/src/northbridge/intel/haswell/native_raminit/reg_structs.h -index b943259b91..b099f4bb82 100644 ---- a/src/northbridge/intel/haswell/native_raminit/reg_structs.h -+++ b/src/northbridge/intel/haswell/native_raminit/reg_structs.h -@@ -297,6 +297,19 @@ union ddr_scram_misc_control_reg { - 	uint32_t raw; - }; -  -+union sc_io_latency_reg { -+	struct __packed { -+		uint32_t iolat_rank0     : 4; // Bits  3:0 -+		uint32_t iolat_rank1     : 4; // Bits  7:4 -+		uint32_t iolat_rank2     : 4; // Bits 11:8 -+		uint32_t iolat_rank3     : 4; // Bits 15:12 -+		uint32_t rt_iocomp       : 6; // Bits 21:16 -+		uint32_t                 : 9; // Bits 30:22 -+		uint32_t dis_rt_clk_gate : 1; // Bits 31:31 -+	}; -+	uint32_t raw; -+}; -+ - union mcscheds_cbit_reg { - 	struct __packed { - 		uint32_t dis_opp_cas    : 1; // Bits  0:0 -diff --git a/src/northbridge/intel/haswell/native_raminit/train_receive_enable.c b/src/northbridge/intel/haswell/native_raminit/train_receive_enable.c -new file mode 100644 -index 0000000000..576c6bc21e ---- /dev/null -+++ b/src/northbridge/intel/haswell/native_raminit/train_receive_enable.c -@@ -0,0 +1,561 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#include <console/console.h> -+#include <northbridge/intel/haswell/haswell.h> -+#include <types.h> -+ -+#include "raminit_native.h" -+#include "ranges.h" -+ -+#define RCVEN_PLOT	RAM_DEBUG -+ -+static enum raminit_status change_rcven_timing(struct sysinfo *ctrl, const uint8_t channel) -+{ -+	int16_t max_rcven = -4096; -+	int16_t min_rcven = 4096; -+	int16_t max_rcven_rank[NUM_SLOTRANKS]; -+	int16_t min_rcven_rank[NUM_SLOTRANKS]; -+	for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { -+		max_rcven_rank[rank] = max_rcven; -+		min_rcven_rank[rank] = min_rcven; -+	} -+	for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { -+		if (!rank_in_ch(ctrl, rank, channel)) -+			continue; -+ -+		for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+			int16_t new_rcven = ctrl->rcven[channel][rank][byte]; -+			new_rcven -= ctrl->io_latency[channel][rank] * 64; -+			if (max_rcven_rank[rank] < new_rcven) -+				max_rcven_rank[rank] = new_rcven; -+ -+			if (min_rcven_rank[rank] > new_rcven) -+				min_rcven_rank[rank] = new_rcven; -+		} -+		if (max_rcven < max_rcven_rank[rank]) -+			max_rcven = max_rcven_rank[rank]; -+ -+		if (min_rcven > min_rcven_rank[rank]) -+			min_rcven = min_rcven_rank[rank]; -+	} -+ -+	/* -+	 * Determine how far we are from the ideal center point for RcvEn timing. -+	 * (PiIdeal - AveRcvEn) / 64 is the ideal number of cycles we should have -+	 * for IO latency. command training will reduce this by 64, so plan for -+	 * that now in the ideal value. Round to closest integer. -+	 */ -+	const int16_t rre_pi_ideal = 256 + 64; -+	const int16_t pi_reserve = 64; -+	const int16_t rcven_center = (max_rcven + min_rcven) / 2; -+	const int8_t iolat_target = DIV_ROUND_CLOSEST(rre_pi_ideal - rcven_center, 64); -+ -+	int8_t io_g_offset = 0; -+	int8_t io_lat[NUM_SLOTRANKS] = { 0 }; -+	for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { -+		if (!rank_in_ch(ctrl, rank, channel)) -+			continue; -+ -+		io_lat[rank] = iolat_target; -+ -+		/* Check for RcvEn underflow/overflow */ -+		const int16_t rcven_lower = 64 * io_lat[rank] + min_rcven_rank[rank]; -+		if (rcven_lower < pi_reserve) -+			io_lat[rank] += DIV_ROUND_UP(pi_reserve - rcven_lower, 64); -+ -+		const int16_t rcven_upper = 64 * io_lat[rank] + max_rcven_rank[rank]; -+		if (rcven_upper > 511 - pi_reserve) -+			io_lat[rank] -= DIV_ROUND_UP(rcven_upper - (511 - pi_reserve), 64); -+ -+		/* Check for IO latency over/underflow */ -+		if (io_lat[rank] - io_g_offset > 14) -+			io_g_offset = io_lat[rank] - 14; -+ -+		if (io_lat[rank] - io_g_offset < 1) -+			io_g_offset = io_lat[rank] - 1; -+ -+		const int8_t cycle_offset = io_lat[rank] - ctrl->io_latency[channel][rank]; -+		for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+			ctrl->rcven[channel][rank][byte] += 64 * cycle_offset; -+			update_rxt(ctrl, channel, rank, byte, RXT_RESTORE, 0); -+		} -+	} -+ -+	/* Calculate new IO comp latency */ -+	union sc_io_latency_reg sc_io_lat = { -+		.raw = mchbar_read32(SC_IO_LATENCY_ch(channel)), -+	}; -+ -+	/* Check if we are underflowing or overflowing this field */ -+	if (io_g_offset < 0 && sc_io_lat.rt_iocomp < -io_g_offset) { -+		printk(BIOS_ERR, "%s: IO COMP underflow\n", __func__); -+		printk(BIOS_ERR, "io_g_offset: %d\n", io_g_offset); -+		printk(BIOS_ERR, "rt_iocomp: %u\n", sc_io_lat.rt_iocomp); -+		return RAMINIT_STATUS_RCVEN_FAILURE; -+	} -+	if (io_g_offset > 0 && io_g_offset > 0x3f - sc_io_lat.rt_iocomp) { -+		printk(BIOS_ERR, "%s: IO COMP overflow\n", __func__); -+		printk(BIOS_ERR, "io_g_offset: %d\n", io_g_offset); -+		printk(BIOS_ERR, "rt_iocomp: %u\n", sc_io_lat.rt_iocomp); -+		return RAMINIT_STATUS_RCVEN_FAILURE; -+	} -+	sc_io_lat.rt_iocomp += io_g_offset; -+	ctrl->rt_io_comp[channel] = sc_io_lat.rt_iocomp; -+	for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { -+		if (ctrl->rankmap[channel] & BIT(rank)) -+			ctrl->io_latency[channel][rank] = io_lat[rank] - io_g_offset; -+ -+		const uint8_t shift = rank * 4; -+		sc_io_lat.raw &= ~(0xf << shift); -+		sc_io_lat.raw |= ctrl->io_latency[channel][rank] << shift; -+	} -+	mchbar_write32(SC_IO_LATENCY_ch(channel), sc_io_lat.raw); -+	return RAMINIT_STATUS_SUCCESS; -+} -+ -+#define RL_START (256 + 24) -+#define RL_STOP  (384 + 24) -+#define RL_STEP  8 -+ -+#define RE_NUM_SAMPLES	6 -+ -+static enum raminit_status verify_high_region(const int32_t center, const int32_t lwidth) -+{ -+	if (center > RL_STOP) { -+		/* Check if center of high was found where it should be */ -+		printk(BIOS_ERR, "RcvEn: Center of high (%d) higher than expected\n", center); -+		return RAMINIT_STATUS_RCVEN_FAILURE; -+	} -+	if (lwidth <= 32) { -+		/* Check if width is large enough */ -+		printk(BIOS_ERR, "RcvEn: Width of high region (%d) too small\n", lwidth); -+		return RAMINIT_STATUS_RCVEN_FAILURE; -+	} -+	if (lwidth >= 96) { -+		/* Since we're calibrating a phase, a too large region is a problem */ -+		printk(BIOS_ERR, "RcvEn: Width of high region (%d) too large\n", lwidth); -+		return RAMINIT_STATUS_RCVEN_FAILURE; -+	} -+	return RAMINIT_STATUS_SUCCESS; -+} -+ -+static void program_io_latency(struct sysinfo *ctrl, const uint8_t channel, const uint8_t rank) -+{ -+	const uint8_t shift = rank * 4; -+	const uint8_t iolat = ctrl->io_latency[channel][rank]; -+	mchbar_clrsetbits32(SC_IO_LATENCY_ch(channel), 0xf << shift, iolat << shift); -+} -+ -+static void program_rl_delays(struct sysinfo *ctrl, const uint8_t rank, const uint16_t rl_delay) -+{ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!rank_in_ch(ctrl, rank, channel)) -+			continue; -+ -+		for (uint8_t byte = 0; byte < ctrl->lanes; byte++) -+			update_rxt(ctrl, channel, rank, byte, RXT_RCVEN, rl_delay); -+	} -+} -+ -+static bool sample_dqs(const uint8_t channel, const uint8_t byte) -+{ -+	return (get_data_train_feedback(channel, byte) & 0x1ff) >= BIT(RE_NUM_SAMPLES - 1); -+} -+ -+enum raminit_status train_receive_enable(struct sysinfo *ctrl) -+{ -+	const struct reut_box reut_addr = { -+		.col = { -+			.start    = 0, -+			.stop     = 1023, -+			.inc_rate = 0, -+			.inc_val  = 1, -+		}, -+	}; -+	const struct wdb_pat wdb_pattern = { -+		.start_ptr  = 0, -+		.stop_ptr   = 9, -+		.inc_rate   = 32, -+		.dq_pattern = BASIC_VA, -+	}; -+ -+	const uint16_t bytemask = BIT(ctrl->lanes) - 1; -+	const uint8_t fine_step = 1; -+ -+	const uint8_t rt_delta = is_hsw_ult() ? 4 : 2; -+	const uint8_t rt_io_comp = 21 + rt_delta; -+	const uint8_t rt_latency = 16 + rt_delta; -+	setup_io_test( -+		ctrl, -+		ctrl->chanmap, -+		PAT_RD, -+		2, -+		RE_NUM_SAMPLES + 1, -+		&reut_addr, -+		0, -+		&wdb_pattern, -+		0, -+		8); -+ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+			union ddr_data_control_2_reg data_control_2 = { -+				.raw = ctrl->dq_control_2[channel][byte], -+			}; -+			data_control_2.force_rx_on = 1; -+			mchbar_write32(DQ_CONTROL_2(channel, byte), data_control_2.raw); -+		} -+		union ddr_data_control_0_reg data_control_0 = { -+			.raw = ctrl->dq_control_0[channel], -+		}; -+		if (ctrl->lpddr) { -+			/** -+			 * W/A for b4618574 - @todo: remove for HSW ULT C0 -+			 * Can't have force_odt_on together with leaker, disable LPDDR -+			 * mode during this training step. lpddr_mode is restored -+			 * at the end of this function from the host structure. -+			 */ -+			data_control_0.lpddr_mode = 0; -+			mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), data_control_0.raw); -+		} -+		data_control_0.force_odt_on     = 1; -+		data_control_0.rl_training_mode = 1; -+		mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), data_control_0.raw); -+		mchbar_write32(SC_IO_LATENCY_ch(channel), (union sc_io_latency_reg) { -+			.rt_iocomp = rt_io_comp, -+		}.raw); -+	} -+	enum raminit_status status = RAMINIT_STATUS_SUCCESS; -+	for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { -+		if (!does_rank_exist(ctrl, rank)) -+			continue; -+ -+		/* -+		 * Set initial roundtrip latency values. Assume -4 QCLK for worst board -+		 * layout. This is calculated as HW_ROUNDT_LAT_DEFAULT_VALUE plus: -+		 * -+		 *   DDR3: Default + (2 * tAA) + 4 QCLK + PI_CLK + N-mode value * 2 -+		 * LPDDR3: Default + (2 * tAA) + 4 QCLK + PI_CLK + tDQSCK_max -+		 * -+		 * N-mode is 3 during training mode. Both channels use the same timings. -+		 */ -+		/** TODO: differs for LPDDR **/ -+		const uint32_t tmp = MAX(ctrl->multiplier, 4) + 5 + 2 * ctrl->tAA; -+		const uint32_t initial_rt_latency = MIN(rt_latency + tmp, 0x3f); -+ -+		uint8_t chanmask = 0; -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			chanmask |= select_reut_ranks(ctrl, channel, BIT(rank)); -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			ctrl->io_latency[channel][rank] = 0; -+			mchbar_write8(SC_ROUNDT_LAT_ch(channel) + rank, initial_rt_latency); -+			ctrl->rt_latency[channel][rank] = initial_rt_latency; -+		} -+ -+		printk(BIOS_DEBUG, "Rank %u\n", rank); -+		printk(BIOS_DEBUG, "Steps 1 and 2: Find middle of high region\n"); -+		printk(RCVEN_PLOT, "Byte"); -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			printk(RCVEN_PLOT, "\t"); -+			for (uint8_t byte = 0; byte < ctrl->lanes; byte++) -+				printk(RCVEN_PLOT, "%u ", byte); -+		} -+		printk(RCVEN_PLOT, "\nRcvEn\n"); -+		struct phase_train_data region_data[NUM_CHANNELS][NUM_LANES] = { 0 }; -+		for (uint16_t rl_delay = RL_START; rl_delay < RL_STOP; rl_delay += RL_STEP) { -+			printk(RCVEN_PLOT, " % 3d", rl_delay); -+			program_rl_delays(ctrl, rank, rl_delay); -+			run_io_test(ctrl, chanmask, BASIC_VA, true); -+			for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+				if (!rank_in_ch(ctrl, rank, channel)) -+					continue; -+ -+				printk(RCVEN_PLOT, "\t"); -+				for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+					const bool high = sample_dqs(channel, byte); -+					printk(RCVEN_PLOT, high ? ". " : "# "); -+					phase_record_pass( -+						®ion_data[channel][byte], -+						high, -+						rl_delay, -+						RL_START, -+						RL_STEP); -+				} -+			} -+			printk(RCVEN_PLOT, "\n"); -+		} -+		printk(RCVEN_PLOT, "\n"); -+		printk(BIOS_DEBUG, "Update RcvEn timing to be in the center of high region\n"); -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			printk(BIOS_DEBUG, "C%u.R%u: \tLeft\tRight\tWidth\tCenter\n", -+				channel, rank); -+			for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+				struct phase_train_data *const curr_data = -+						®ion_data[channel][byte]; -+				phase_append_current_to_initial(curr_data, RL_START, RL_STEP); -+				const int32_t lwidth = range_width(curr_data->largest); -+				const int32_t center = range_center(curr_data->largest); -+				printk(BIOS_DEBUG, "   B%u: \t%d\t%d\t%d\t%d\n", -+					byte, -+					curr_data->largest.start, -+					curr_data->largest.end, -+					lwidth, -+					center); -+ -+				status = verify_high_region(center, lwidth); -+				if (status) { -+					printk(BIOS_ERR, -+						"RcvEn problems on channel %u, byte %u\n", -+						channel, byte); -+					goto clean_up; -+				} -+				ctrl->rcven[channel][rank][byte] = center; -+				update_rxt(ctrl, channel, rank, byte, RXT_RESTORE, 0); -+			} -+			printk(BIOS_DEBUG, "\n"); -+		} -+ -+		printk(BIOS_DEBUG, "Step 3: Quarter preamble - Walk backwards\n"); -+		printk(RCVEN_PLOT, "Byte"); -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			printk(RCVEN_PLOT, "\t"); -+			for (uint8_t byte = 0; byte < ctrl->lanes; byte++) -+				printk(RCVEN_PLOT, "%u ", byte); -+		} -+		printk(RCVEN_PLOT, "\nIOLAT\n"); -+		bool done = false; -+		while (!done) { -+			run_io_test(ctrl, chanmask, BASIC_VA, true); -+			done = true; -+			for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+				if (!rank_in_ch(ctrl, rank, channel)) -+					continue; -+ -+				printk(RCVEN_PLOT, "  %2u\t", ctrl->io_latency[channel][rank]); -+				uint16_t highs = 0; -+				for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+					const bool high = sample_dqs(channel, byte); -+					printk(RCVEN_PLOT, high ? "H " : "L "); -+					if (high) -+						highs |= BIT(byte); -+				} -+				if (!highs) -+					continue; -+ -+				done = false; -+ -+				/* If all bytes sample high, adjust timing globally */ -+				if (highs == bytemask && ctrl->io_latency[channel][rank] < 14) { -+					ctrl->io_latency[channel][rank] += 2; -+					ctrl->io_latency[channel][rank] %= 16; -+					program_io_latency(ctrl, channel, rank); -+					continue; -+				} -+ -+				/* Otherwise, adjust individual bytes */ -+				for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+					if (!(highs & BIT(byte))) -+						continue; -+ -+					if (ctrl->rcven[channel][rank][byte] < 128) { -+						printk(BIOS_ERR, -+							"RcvEn underflow: walking backwards\n"); -+						printk(BIOS_ERR, -+							"For channel %u, rank %u, byte %u\n", -+							channel, rank, byte); -+						status = RAMINIT_STATUS_RCVEN_FAILURE; -+						goto clean_up; -+					} -+					ctrl->rcven[channel][rank][byte] -= 128; -+					update_rxt(ctrl, channel, rank, byte, RXT_RESTORE, 0); -+				} -+			} -+			printk(RCVEN_PLOT, "\n"); -+		} -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			printk(BIOS_DEBUG, "\nC%u:  Preamble\n", channel); -+			for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+				printk(BIOS_DEBUG, -+					" B%u: %u\n", byte, ctrl->rcven[channel][rank][byte]); -+			} -+		} -+		printk(BIOS_DEBUG, "\n"); -+ -+		printk(BIOS_DEBUG, "Step 4: Add 1 qclk\n"); -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+				ctrl->rcven[channel][rank][byte] += 64; -+				update_rxt(ctrl, channel, rank, byte, RXT_RESTORE, 0); -+			} -+		} -+		printk(BIOS_DEBUG, "\n"); -+ -+		printk(BIOS_DEBUG, "Step 5: Walk forward to find rising edge\n"); -+		printk(RCVEN_PLOT, "Byte"); -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			printk(RCVEN_PLOT, "\t"); -+			for (uint8_t byte = 0; byte < ctrl->lanes; byte++) -+				printk(RCVEN_PLOT, "%u ", byte); -+		} -+		printk(RCVEN_PLOT, "\n inc\n"); -+		uint16_t ch_result[NUM_CHANNELS] = { 0 }; -+		uint8_t inc_preamble[NUM_CHANNELS][NUM_LANES] = { 0 }; -+		for (uint8_t inc = 0; inc < 64; inc += fine_step) { -+			printk(RCVEN_PLOT, " %2u\t", inc); -+			run_io_test(ctrl, chanmask, BASIC_VA, true); -+			done = true; -+			for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+				if (!rank_in_ch(ctrl, rank, channel)) -+					continue; -+ -+				for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+					if (ch_result[channel] & BIT(byte)) { -+						/* Skip bytes that are already done */ -+						printk(RCVEN_PLOT, ". "); -+						continue; -+					} -+					const bool pass = sample_dqs(channel, byte); -+					printk(RCVEN_PLOT, pass ? ". " : "# "); -+					if (pass) { -+						ch_result[channel] |= BIT(byte); -+						continue; -+					} -+					ctrl->rcven[channel][rank][byte] += fine_step; -+					update_rxt(ctrl, channel, rank, byte, RXT_RESTORE, 0); -+					inc_preamble[channel][byte] = inc; -+				} -+				printk(RCVEN_PLOT, "\t"); -+				if (ch_result[channel] != bytemask) -+					done = false; -+			} -+			printk(RCVEN_PLOT, "\n"); -+			if (done) -+				break; -+		} -+		printk(BIOS_DEBUG, "\n"); -+		if (!done) { -+			printk(BIOS_ERR, "Error: Preamble edge not found for all bytes\n"); -+			printk(BIOS_ERR, "The final RcvEn results are as follows:\n"); -+			for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+				if (!rank_in_ch(ctrl, rank, channel)) -+					continue; -+ -+				printk(BIOS_ERR, "Channel %u Rank %u:  preamble\n", -+					channel, rank); -+				for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+					printk(BIOS_ERR, " Byte %u: %u%s\n", byte, -+						ctrl->rcven[channel][rank][byte], -+						(ch_result[channel] ^ bytemask) & BIT(byte) -+							? "" -+							: " *** Check this byte! ***"); -+				} -+			} -+			status = RAMINIT_STATUS_RCVEN_FAILURE; -+			goto clean_up; -+		} -+ -+		printk(BIOS_DEBUG, "Step 6: center on preamble and clean up rank\n"); -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			printk(BIOS_DEBUG, "C%u:  Preamble increment\n", channel); -+			for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+				/* -+				 * For Traditional, pull in RcvEn by 64. For ULT, take the DQS -+				 * drift into account to the specified guardband: tDQSCK_DRIFT. -+				 */ -+				ctrl->rcven[channel][rank][byte] -= tDQSCK_DRIFT; -+				update_rxt(ctrl, channel, rank, byte, RXT_RESTORE, 0); -+				printk(BIOS_DEBUG, " B%u: %u      %u\n", byte, -+					ctrl->rcven[channel][rank][byte], -+					inc_preamble[channel][byte]); -+			} -+			printk(BIOS_DEBUG, "\n"); -+		} -+		printk(BIOS_DEBUG, "\n"); -+	} -+ -+clean_up: -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		if (ctrl->lpddr) { -+			/** -+			 * W/A for b4618574 - @todo: remove for HSW ULT C0 -+			 * Can't have force_odt_on together with leaker, disable LPDDR mode for -+			 * this training step. This write will disable force_odt_on while still -+			 * keeping LPDDR mode disabled. Second write will restore LPDDR mode. -+			 */ -+			union ddr_data_control_0_reg data_control_0 = { -+				.raw = ctrl->dq_control_0[channel], -+			}; -+			data_control_0.lpddr_mode = 0; -+			mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), data_control_0.raw); -+		} -+		mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), ctrl->dq_control_0[channel]); -+		for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+			mchbar_write32(DQ_CONTROL_2(channel, byte), -+					ctrl->dq_control_2[channel][byte]); -+		} -+	} -+	io_reset(); -+	if (status) -+		return status; -+ -+	printk(BIOS_DEBUG, "Step 7: Sync IO latency across all ranks\n"); -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		status = change_rcven_timing(ctrl, channel); -+		if (status) -+			return status; -+	} -+	printk(BIOS_DEBUG, "\nFinal Receive Enable and IO latency settings:\n"); -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			const union sc_io_latency_reg sc_io_latency = { -+				.raw = mchbar_read32(SC_IO_LATENCY_ch(channel)), -+			}; -+			printk(BIOS_DEBUG, "  C%u.R%u: IOLAT = %u  rt_iocomp = %u\n", channel, -+				rank, ctrl->io_latency[channel][rank], sc_io_latency.rt_iocomp); -+			for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+				printk(BIOS_DEBUG, "   B%u:   %u\n", byte, -+					ctrl->rcven[channel][rank][byte]); -+			} -+			printk(BIOS_DEBUG, "\n"); -+		} -+	} -+	return status; -+} -diff --git a/src/northbridge/intel/haswell/registers/mchbar.h b/src/northbridge/intel/haswell/registers/mchbar.h -index a81559bb1e..9172d4f2b0 100644 ---- a/src/northbridge/intel/haswell/registers/mchbar.h -+++ b/src/northbridge/intel/haswell/registers/mchbar.h -@@ -18,6 +18,8 @@ - #define RX_TRAIN_ch_r_b(ch, rank, byte)		_DDRIO_C_R_B(0x0000, ch, rank, byte) - #define TX_TRAIN_ch_r_b(ch, rank, byte)		_DDRIO_C_R_B(0x0020, ch, rank, byte) -  -+#define DDR_DATA_TRAIN_FEEDBACK(ch, byte)	_DDRIO_C_R_B(0x0054, ch, 0, byte) -+ - #define DQ_CONTROL_2(ch, byte)			_DDRIO_C_R_B(0x0064, ch, 0, byte) - #define DQ_CONTROL_0(ch, byte)			_DDRIO_C_R_B(0x0074, ch, 0, byte) -  -@@ -100,6 +102,7 @@ - #define COMMAND_RATE_LIMIT_ch(ch)		_MCMAIN_C(0x4010, ch) - #define TC_BANK_RANK_D_ch(ch)			_MCMAIN_C(0x4014, ch) - #define SC_ROUNDT_LAT_ch(ch)			_MCMAIN_C(0x4024, ch) -+#define SC_IO_LATENCY_ch(ch)			_MCMAIN_C(0x4028, ch) -  - #define REUT_ch_PAT_WDB_CL_MUX_CFG(ch)		_MCMAIN_C(0x4040, ch) -  ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0012-haswell-NRI-Add-function-to-change-margins.patch b/config/coreboot/haswell/patches/0012-haswell-NRI-Add-function-to-change-margins.patch deleted file mode 100644 index f4f5161e..00000000 --- a/config/coreboot/haswell/patches/0012-haswell-NRI-Add-function-to-change-margins.patch +++ /dev/null @@ -1,272 +0,0 @@ -From c24b26594bfab47a8709ed7fb5cb77307fb73a53 Mon Sep 17 00:00:00 2001 -From: Angel Pons <th3fanbus@gmail.com> -Date: Sun, 8 May 2022 11:58:59 +0200 -Subject: [PATCH 12/20] haswell NRI: Add function to change margins - -Implement a function to change margin parameters. Haswell provides a -register to apply an offset to margin parameters during training, so -make use of it. There are other margin parameters that have not been -implemented yet, as they are not needed for now and special handling -is needed to provide offset training functionality. - -Change-Id: I5392380e13de3c44e77b7bc9f3b819e2661d1e2d -Signed-off-by: Angel Pons <th3fanbus@gmail.com> ---- - .../haswell/native_raminit/change_margin.c    | 136 ++++++++++++++++++ - .../haswell/native_raminit/raminit_native.h   |  39 +++++ - .../haswell/native_raminit/reg_structs.h      |  12 ++ - .../intel/haswell/registers/mchbar.h          |   1 + - 4 files changed, 188 insertions(+) - -diff --git a/src/northbridge/intel/haswell/native_raminit/change_margin.c b/src/northbridge/intel/haswell/native_raminit/change_margin.c -index 055c666eee..299c44a6b0 100644 ---- a/src/northbridge/intel/haswell/native_raminit/change_margin.c -+++ b/src/northbridge/intel/haswell/native_raminit/change_margin.c -@@ -1,5 +1,6 @@ - /* SPDX-License-Identifier: GPL-2.0-or-later */ -  -+#include <assert.h> - #include <commonlib/bsd/clamp.h> - #include <console/console.h> - #include <delay.h> -@@ -152,3 +153,138 @@ void download_regfile( - 	ddr_data_control_0.read_rf_rank = phys_rank; - 	mchbar_write32(reg, ddr_data_control_0.raw); - } -+ -+static void update_data_offset_train( -+	struct sysinfo *ctrl, -+	const uint8_t param, -+	const uint8_t en_multicast, -+	const uint8_t channel_in, -+	const uint8_t rank, -+	const uint8_t byte_in, -+	const bool update_ctrl, -+	const enum regfile_mode regfile, -+	const uint32_t value) -+{ -+	bool is_rd = false; -+	bool is_wr = false; -+	switch (param) { -+	case RdT: -+	case RdV: -+	case RcvEna: -+		is_rd = true; -+		break; -+	case WrT: -+	case WrDqsT: -+		is_wr = true; -+		break; -+	default: -+		die("%s: Invalid margin parameter %u\n", __func__, param); -+	} -+	if (en_multicast) { -+		mchbar_write32(DDR_DATA_OFFSET_TRAIN, value); -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!does_ch_exist(ctrl, channel)) -+				continue; -+ -+			download_regfile(ctrl, channel, true, rank, regfile, 0, is_rd, is_wr); -+			if (update_ctrl) { -+				for (uint8_t byte = 0; byte < ctrl->lanes; byte++) -+					ctrl->data_offset_train[channel][byte] = value; -+			} -+		} -+	} else { -+		mchbar_write32(DDR_DATA_OFFSET_TRAIN_ch_b(channel_in, byte_in), value); -+		download_regfile(ctrl, channel_in, false, rank, regfile, byte_in, is_rd, is_wr); -+		if (update_ctrl) -+			ctrl->data_offset_train[channel_in][byte_in] = value; -+	} -+} -+ -+static uint32_t get_max_margin(const enum margin_parameter param) -+{ -+	switch (param) { -+	case RcvEna: -+	case RdT: -+	case WrT: -+	case WrDqsT: -+		return MAX_POSSIBLE_TIME; -+	case RdV: -+		return MAX_POSSIBLE_VREF; -+	default: -+		die("%s: Invalid margin parameter %u\n", __func__, param); -+	} -+} -+ -+void change_margin( -+	struct sysinfo *ctrl, -+	const enum margin_parameter param, -+	const int32_t value0, -+	const bool en_multicast, -+	const uint8_t channel, -+	const uint8_t rank, -+	const uint8_t byte, -+	const bool update_ctrl, -+	const enum regfile_mode regfile) -+{ -+	/** FIXME: Remove this **/ -+	if (rank == 0xff) -+		die("%s: rank is 0xff\n", __func__); -+ -+	if (!en_multicast && !does_ch_exist(ctrl, channel)) -+		die("%s: Tried to change margin of empty channel %u\n", __func__, channel); -+ -+	const uint32_t max_value = get_max_margin(param); -+	const int32_t v0 = clamp_s32(-max_value, value0, max_value); -+ -+	union ddr_data_offset_train_reg ddr_data_offset_train = { -+		.raw = en_multicast ? 0 : ctrl->data_offset_train[channel][byte], -+	}; -+	bool update_offset_train = false; -+	switch (param) { -+	case RcvEna: -+		ddr_data_offset_train.rcven = v0; -+		update_offset_train = true; -+		break; -+	case RdT: -+		ddr_data_offset_train.rx_dqs = v0; -+		update_offset_train = true; -+		break; -+	case WrT: -+		ddr_data_offset_train.tx_dq = v0; -+		update_offset_train = true; -+		break; -+	case WrDqsT: -+		ddr_data_offset_train.tx_dqs = v0; -+		update_offset_train = true; -+		break; -+	case RdV: -+		ddr_data_offset_train.vref = v0; -+		update_offset_train = true; -+		break; -+	default: -+		die("%s: Invalid margin parameter %u\n", __func__, param); -+	} -+	if (update_offset_train) { -+		update_data_offset_train( -+			ctrl, -+			param, -+			en_multicast, -+			channel, -+			rank, -+			byte, -+			update_ctrl, -+			regfile, -+			ddr_data_offset_train.raw); -+	} -+} -+ -+void change_1d_margin_multicast( -+	struct sysinfo *ctrl, -+	const enum margin_parameter param, -+	const int32_t value0, -+	const uint8_t rank, -+	const bool update_ctrl, -+	const enum regfile_mode regfile) -+{ -+	change_margin(ctrl, param, value0, true, 0, rank, 0, update_ctrl, regfile); -+} -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -index b4e8c7de5a..5242b16f28 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -@@ -35,6 +35,18 @@ -  - #define RTTNOM_MASK		(BIT(9) | BIT(6) | BIT(2)) -  -+/* Margin parameter limits */ -+#define MAX_POSSIBLE_TIME	31 -+#define MAX_POSSIBLE_VREF	54 -+ -+#define MAX_POSSIBLE_BOTH	MAX_POSSIBLE_VREF -+ -+#define MIN_TIME		(-MAX_POSSIBLE_TIME) -+#define MAX_TIME		(MAX_POSSIBLE_TIME) -+ -+#define MIN_VREF		(-MAX_POSSIBLE_VREF) -+#define MAX_VREF		(MAX_POSSIBLE_VREF) -+ - #define BASIC_VA_PAT_SPREAD_8	0x01010101 -  - #define WDB_CACHE_LINE_SIZE	8 -@@ -45,6 +57,14 @@ - /* Specified in PI ticks. 64 PI ticks == 1 qclk */ - #define tDQSCK_DRIFT		64 -  -+enum margin_parameter { -+	RcvEna, -+	RdT, -+	WrT, -+	WrDqsT, -+	RdV, -+}; -+ - /* ZQ calibration types */ - enum { - 	ZQ_INIT,	/* DDR3: ZQCL with tZQinit, LPDDR3: ZQ Init  with tZQinit  */ -@@ -514,6 +534,25 @@ void download_regfile( - 	bool read_rf_rd, - 	bool read_rf_wr); -  -+void change_margin( -+	struct sysinfo *ctrl, -+	const enum margin_parameter param, -+	const int32_t value0, -+	const bool en_multicast, -+	const uint8_t channel, -+	const uint8_t rank, -+	const uint8_t byte, -+	const bool update_ctrl, -+	const enum regfile_mode regfile); -+ -+void change_1d_margin_multicast( -+	struct sysinfo *ctrl, -+	const enum margin_parameter param, -+	const int32_t value0, -+	const uint8_t rank, -+	const bool update_ctrl, -+	const enum regfile_mode regfile); -+ - uint8_t get_rx_bias(const struct sysinfo *ctrl); -  - uint8_t get_tCWL(uint32_t mem_clock_mhz); -diff --git a/src/northbridge/intel/haswell/native_raminit/reg_structs.h b/src/northbridge/intel/haswell/native_raminit/reg_structs.h -index b099f4bb82..a0e36ed082 100644 ---- a/src/northbridge/intel/haswell/native_raminit/reg_structs.h -+++ b/src/northbridge/intel/haswell/native_raminit/reg_structs.h -@@ -25,6 +25,18 @@ union ddr_data_tx_train_rank_reg { - 	uint32_t raw; - }; -  -+union ddr_data_offset_train_reg { -+	struct __packed { -+		int32_t rcven  : 6; // Bits  5:0 -+		int32_t rx_dqs : 6; // Bits 11:6 -+		int32_t tx_dq  : 6; // Bits 17:12 -+		int32_t tx_dqs : 6; // Bits 23:18 -+		int32_t vref   : 7; // Bits 30:24 -+		int32_t        : 1; // Bits 31:31 -+	}; -+	uint32_t raw; -+}; -+ - union ddr_data_control_0_reg { - 	struct __packed { - 		uint32_t rx_training_mode      : 1; // Bits  0:0 -diff --git a/src/northbridge/intel/haswell/registers/mchbar.h b/src/northbridge/intel/haswell/registers/mchbar.h -index 9172d4f2b0..0acafbc826 100644 ---- a/src/northbridge/intel/haswell/registers/mchbar.h -+++ b/src/northbridge/intel/haswell/registers/mchbar.h -@@ -21,6 +21,7 @@ - #define DDR_DATA_TRAIN_FEEDBACK(ch, byte)	_DDRIO_C_R_B(0x0054, ch, 0, byte) -  - #define DQ_CONTROL_2(ch, byte)			_DDRIO_C_R_B(0x0064, ch, 0, byte) -+#define DDR_DATA_OFFSET_TRAIN_ch_b(ch, byte)	_DDRIO_C_R_B(0x0070, ch, 0, byte) - #define DQ_CONTROL_0(ch, byte)			_DDRIO_C_R_B(0x0074, ch, 0, byte) -  - /* DDR CKE per-channel */ ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0013-haswell-NRI-Add-read-MPR-training.patch b/config/coreboot/haswell/patches/0013-haswell-NRI-Add-read-MPR-training.patch deleted file mode 100644 index 5f154bcc..00000000 --- a/config/coreboot/haswell/patches/0013-haswell-NRI-Add-read-MPR-training.patch +++ /dev/null @@ -1,332 +0,0 @@ -From e263f0d2e9d6d016d603342651da261bbcb6af1f Mon Sep 17 00:00:00 2001 -From: Angel Pons <th3fanbus@gmail.com> -Date: Sun, 8 May 2022 11:35:49 +0200 -Subject: [PATCH 13/20] haswell NRI: Add read MPR training - -Implement read training using DDR3 MPR (Multi-Purpose Register). - -Change-Id: Id17cb2c4c399ac9bcc937b595b58f863c152461b -Signed-off-by: Angel Pons <th3fanbus@gmail.com> ---- - .../intel/haswell/native_raminit/Makefile.mk  |   1 + - .../haswell/native_raminit/raminit_main.c     |   1 + - .../haswell/native_raminit/raminit_native.h   |   4 + - .../haswell/native_raminit/train_read_mpr.c   | 241 ++++++++++++++++++ - .../intel/haswell/registers/mchbar.h          |   2 +- - 5 files changed, 248 insertions(+), 1 deletion(-) - create mode 100644 src/northbridge/intel/haswell/native_raminit/train_read_mpr.c - -diff --git a/src/northbridge/intel/haswell/native_raminit/Makefile.mk b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -index e2fbfb4211..c442be0728 100644 ---- a/src/northbridge/intel/haswell/native_raminit/Makefile.mk -+++ b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -@@ -16,4 +16,5 @@ romstage-y += setup_wdb.c - romstage-y += spd_bitmunching.c - romstage-y += testing_io.c - romstage-y += timings_refresh.c -+romstage-y += train_read_mpr.c - romstage-y += train_receive_enable.c -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_main.c b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -index 7d444659c3..264d1468f5 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_main.c -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -@@ -61,6 +61,7 @@ static const struct task_entry cold_boot[] = { - 	{ do_jedec_init,                                          true, "JEDECINIT",  }, - 	{ pre_training,                                           true, "PRETRAIN",   }, - 	{ train_receive_enable,                                   true, "RCVET",      }, -+	{ train_read_mpr,                                         true, "RDMPRT",     }, - }; -  - /* Return a generic stepping value to make stepping checks simpler */ -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -index 5242b16f28..49e9214656 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -@@ -27,6 +27,8 @@ - /* Always use 12 legs for emphasis (not trained) */ - #define TXEQFULLDRV		(3 << 4) -  -+#define LOOPCOUNT_INFINITE	0xff -+ - /* DDR3 mode register bits */ - #define MR0_DLL_RESET		BIT(8) -  -@@ -212,6 +214,7 @@ enum raminit_status { - 	RAMINIT_STATUS_POLL_TIMEOUT, - 	RAMINIT_STATUS_REUT_ERROR, - 	RAMINIT_STATUS_RCVEN_FAILURE, -+	RAMINIT_STATUS_RMPR_FAILURE, - 	RAMINIT_STATUS_UNSPECIFIED_ERROR, /** TODO: Deprecated in favor of specific values **/ - }; -  -@@ -433,6 +436,7 @@ enum raminit_status configure_mc(struct sysinfo *ctrl); - enum raminit_status configure_memory_map(struct sysinfo *ctrl); - enum raminit_status do_jedec_init(struct sysinfo *ctrl); - enum raminit_status train_receive_enable(struct sysinfo *ctrl); -+enum raminit_status train_read_mpr(struct sysinfo *ctrl); -  - void configure_timings(struct sysinfo *ctrl); - void configure_refresh(struct sysinfo *ctrl); -diff --git a/src/northbridge/intel/haswell/native_raminit/train_read_mpr.c b/src/northbridge/intel/haswell/native_raminit/train_read_mpr.c -new file mode 100644 -index 0000000000..ade1e36148 ---- /dev/null -+++ b/src/northbridge/intel/haswell/native_raminit/train_read_mpr.c -@@ -0,0 +1,241 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#include <commonlib/bsd/clamp.h> -+#include <console/console.h> -+#include <delay.h> -+#include <northbridge/intel/haswell/haswell.h> -+#include <types.h> -+ -+#include "raminit_native.h" -+#include "ranges.h" -+ -+#define RMPR_START	(-32) -+#define RMPR_STOP	(32) -+#define RMPR_STEP	1 -+ -+#define RMPR_MIN_WIDTH	12 -+ -+#define RMPR_PLOT	RAM_DEBUG -+ -+/* -+ * Clear rx_training_mode. For LPDDR, we first need to disable odt_samp_extend_en, -+ * then disable rx_training_mode, and finally re-enable odt_samp_extend_en. -+ */ -+static void clear_rx_training_mode(struct sysinfo *ctrl, const uint8_t channel) -+{ -+	for (uint8_t byte = 0; byte < ctrl->lanes; byte++) -+		mchbar_write32(DQ_CONTROL_2(channel, byte), ctrl->dq_control_2[channel][byte]); -+ -+	if (ctrl->lpddr) { -+		union ddr_data_control_0_reg data_control_0 = { -+			.raw = mchbar_read32(DDR_DATA_ch_CONTROL_0(channel)), -+		}; -+		data_control_0.odt_samp_extend_en = 0; -+		mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), data_control_0.raw); -+		tick_delay(1); -+		data_control_0.rx_training_mode = 0; -+		mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), data_control_0.raw); -+		tick_delay(1); -+	} -+	mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), ctrl->dq_control_0[channel]); -+} -+ -+static void set_rxdqs_edges_to_midpoint(struct sysinfo *ctrl) -+{ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			for (uint8_t byte = 0; byte < ctrl->lanes; byte++) -+				update_rxt(ctrl, channel, rank, byte, RXT_RXDQS_BOTH, 32); -+		} -+	} -+} -+ -+static void enter_mpr_train_ddr_mode(struct sysinfo *ctrl, const uint8_t rank) -+{ -+	/* Program MR3 and mask RAS/WE to prevent scheduler from issuing non-read commands */ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!rank_in_ch(ctrl, rank, channel)) -+			continue; -+ -+		if (!ctrl->lpddr) -+			reut_issue_mrs(ctrl, channel, BIT(rank), 3, 1 << 2); -+ -+		union reut_misc_odt_ctrl_reg reut_misc_odt_ctrl = { -+			.raw = mchbar_read32(REUT_ch_MISC_ODT_CTRL(channel)), -+		}; -+		reut_misc_odt_ctrl.mpr_train_ddr_on = 1; -+		mchbar_write32(REUT_ch_MISC_ODT_CTRL(channel), reut_misc_odt_ctrl.raw); -+	} -+} -+ -+static void leave_mpr_train_ddr_mode(struct sysinfo *ctrl, const uint8_t rank) -+{ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!rank_in_ch(ctrl, rank, channel)) -+			continue; -+ -+		/* -+		 * The mpr_train_ddr_on bit will force a special command. -+		 * Therefore, clear it before issuing the MRS command. -+		 */ -+		union reut_misc_odt_ctrl_reg reut_misc_odt_ctrl = { -+			.raw = mchbar_read32(REUT_ch_MISC_ODT_CTRL(channel)), -+		}; -+		reut_misc_odt_ctrl.mpr_train_ddr_on = 0; -+		mchbar_write32(REUT_ch_MISC_ODT_CTRL(channel), reut_misc_odt_ctrl.raw); -+		if (!ctrl->lpddr) -+			reut_issue_mrs(ctrl, channel, BIT(rank), 3, 0 << 2); -+	} -+} -+ -+enum raminit_status train_read_mpr(struct sysinfo *ctrl) -+{ -+	set_rxdqs_edges_to_midpoint(ctrl); -+	clear_data_offset_train_all(ctrl); -+	setup_io_test_mpr(ctrl, ctrl->chanmap, LOOPCOUNT_INFINITE, NSOE); -+	enum raminit_status status = RAMINIT_STATUS_SUCCESS; -+	for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { -+		if (!does_rank_exist(ctrl, rank)) -+			continue; -+ -+		printk(BIOS_DEBUG, "Rank %u\n", rank); -+		printk(RMPR_PLOT, "Channel"); -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			printk(RMPR_PLOT, "\t%u\t\t", channel); -+		} -+		printk(RMPR_PLOT, "\nByte"); -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			printk(RMPR_PLOT, "\t"); -+			for (uint8_t byte = 0; byte < ctrl->lanes; byte++) -+				printk(RMPR_PLOT, "%u ", byte); -+		} -+		enter_mpr_train_ddr_mode(ctrl, rank); -+		struct linear_train_data region_data[NUM_CHANNELS][NUM_LANES] = { 0 }; -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) -+			select_reut_ranks(ctrl, channel, BIT(rank)); -+ -+		printk(RMPR_PLOT, "\nDqsDelay\n"); -+		int8_t dqs_delay; -+		for (dqs_delay = RMPR_START; dqs_delay < RMPR_STOP; dqs_delay += RMPR_STEP) { -+			printk(RMPR_PLOT, "% 5d", dqs_delay); -+			const enum regfile_mode regfile = REG_FILE_USE_START; -+			/* Looks like MRC uses rank 0 here, but it feels wrong */ -+			change_1d_margin_multicast(ctrl, RdT, dqs_delay, rank, false, regfile); -+			for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+				if (!rank_in_ch(ctrl, rank, channel)) -+					continue; -+ -+				for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+					union ddr_data_control_2_reg data_control_2 = { -+						.raw = ctrl->dq_control_2[channel][byte], -+					}; -+					data_control_2.force_bias_on = 1; -+					data_control_2.force_rx_on   = 1; -+					data_control_2.leaker_comp   = 0; -+					mchbar_write32(DQ_CONTROL_2(channel, byte), -+						data_control_2.raw); -+				} -+				union ddr_data_control_0_reg data_control_0 = { -+					.raw = ctrl->dq_control_0[channel], -+				}; -+				data_control_0.rx_training_mode   = 1; -+				data_control_0.force_odt_on       = !ctrl->lpddr; -+				data_control_0.en_read_preamble   = 0; -+				data_control_0.odt_samp_extend_en = ctrl->lpddr; -+				const uint32_t reg_offset = DDR_DATA_ch_CONTROL_0(channel); -+				mchbar_write32(reg_offset, data_control_0.raw); -+			} -+			run_mpr_io_test(false); -+			for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+				if (!rank_in_ch(ctrl, rank, channel)) -+					continue; -+ -+				printk(RMPR_PLOT, "\t"); -+				for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+					uint32_t fb = get_data_train_feedback(channel, byte); -+					const bool pass = fb == 1; -+					printk(RMPR_PLOT, pass ? ". " : "# "); -+					linear_record_pass( -+						®ion_data[channel][byte], -+						pass, -+						dqs_delay, -+						RMPR_START, -+						RMPR_STEP); -+				} -+			} -+			printk(RMPR_PLOT, "\n"); -+			for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+				if (!rank_in_ch(ctrl, rank, channel)) -+					continue; -+ -+				clear_rx_training_mode(ctrl, channel); -+			} -+			io_reset(); -+		} -+		printk(RMPR_PLOT, "\n"); -+		leave_mpr_train_ddr_mode(ctrl, rank); -+		clear_data_offset_train_all(ctrl); -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			printk(BIOS_DEBUG, "C%u.R%u: \tLeft\tRight\tWidth\tCenter\tRxDqsPN\n", -+				channel, rank); -+			for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+				struct linear_train_data *data = ®ion_data[channel][byte]; -+				const int32_t lwidth = range_width(data->largest); -+				if (lwidth <= RMPR_MIN_WIDTH) { -+					printk(BIOS_ERR, -+						"Bad eye (lwidth %d <= min %d) for byte %u\n", -+						lwidth, RMPR_MIN_WIDTH, byte); -+					status = RAMINIT_STATUS_RMPR_FAILURE; -+				} -+				/* -+				 * The MPR center may not be ideal on certain platforms for -+				 * unknown reasons. If so, adjust it with a magical number. -+				 * For Haswell, the magical number is zero. Hell knows why. -+				 */ -+				const int32_t center = range_center(data->largest); -+				ctrl->rxdqsp[channel][rank][byte] = center - RMPR_START; -+				ctrl->rxdqsn[channel][rank][byte] = center - RMPR_START; -+				printk(BIOS_DEBUG, "  B%u: \t%d\t%d\t%d\t%d\t%u\n", byte, -+					data->largest.start, data->largest.end, lwidth, -+					center, ctrl->rxdqsp[channel][rank][byte]); -+			} -+			printk(BIOS_DEBUG, "\n"); -+		} -+	} -+ -+	/* -+	 * Now program the DQS center values on populated ranks. data is taken from -+	 * the host struct. We need to do it after all ranks are trained, because we -+	 * need to keep the same DQS value on all ranks during the training procedure. -+	 */ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			for (uint8_t byte = 0; byte < ctrl->lanes; byte++) -+				update_rxt(ctrl, channel, rank, byte, RXT_RESTORE, 0); -+		} -+	} -+	change_1d_margin_multicast(ctrl, RdT, 0, 0, false, REG_FILE_USE_CURRENT); -+	io_reset(); -+	return status; -+} -diff --git a/src/northbridge/intel/haswell/registers/mchbar.h b/src/northbridge/intel/haswell/registers/mchbar.h -index 0acafbc826..6a31d3a32c 100644 ---- a/src/northbridge/intel/haswell/registers/mchbar.h -+++ b/src/northbridge/intel/haswell/registers/mchbar.h -@@ -122,7 +122,7 @@ - #define REUT_ch_ERR_DATA_MASK(ch)		_MCMAIN_C(0x40d8, ch) -  - #define REUT_ch_MISC_CKE_CTRL(ch)		_MCMAIN_C(0x4190, ch) -- -+#define REUT_ch_MISC_ODT_CTRL(ch)		_MCMAIN_C(0x4194, ch) - #define REUT_ch_MISC_PAT_CADB_CTRL(ch)		_MCMAIN_C(0x4198, ch) - #define REUT_ch_PAT_CADB_MRS(ch)		_MCMAIN_C(0x419c, ch) - #define REUT_ch_PAT_CADB_MUX_CTRL(ch)		_MCMAIN_C(0x41a0, ch) ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0014-haswell-NRI-Add-write-leveling.patch b/config/coreboot/haswell/patches/0014-haswell-NRI-Add-write-leveling.patch deleted file mode 100644 index 5a96cd1f..00000000 --- a/config/coreboot/haswell/patches/0014-haswell-NRI-Add-write-leveling.patch +++ /dev/null @@ -1,689 +0,0 @@ -From bebe0b74bede64b03aa1e3781310ef539465627b Mon Sep 17 00:00:00 2001 -From: Angel Pons <th3fanbus@gmail.com> -Date: Sun, 8 May 2022 12:56:04 +0200 -Subject: [PATCH 14/20] haswell NRI: Add write leveling - -Implement JEDEC write leveling, which is done in two steps. The first -step uses the JEDEC procedure to do "fine" write leveling, i.e. align -the DQS phase to the clock signal. The second step performs a regular -read-write test to correct "coarse" cycle errors. - -Change-Id: I27678523fe22c38173a688e2a4751c259a20f009 -Signed-off-by: Angel Pons <th3fanbus@gmail.com> ---- - .../intel/haswell/native_raminit/Makefile.mk  |   1 + - .../haswell/native_raminit/raminit_main.c     |   1 + - .../haswell/native_raminit/raminit_native.h   |  10 + - .../train_jedec_write_leveling.c              | 581 ++++++++++++++++++ - .../intel/haswell/registers/mchbar.h          |   2 + - 5 files changed, 595 insertions(+) - create mode 100644 src/northbridge/intel/haswell/native_raminit/train_jedec_write_leveling.c - -diff --git a/src/northbridge/intel/haswell/native_raminit/Makefile.mk b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -index c442be0728..40c2f5e014 100644 ---- a/src/northbridge/intel/haswell/native_raminit/Makefile.mk -+++ b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -@@ -16,5 +16,6 @@ romstage-y += setup_wdb.c - romstage-y += spd_bitmunching.c - romstage-y += testing_io.c - romstage-y += timings_refresh.c -+romstage-y += train_jedec_write_leveling.c - romstage-y += train_read_mpr.c - romstage-y += train_receive_enable.c -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_main.c b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -index 264d1468f5..1ff23be615 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_main.c -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -@@ -62,6 +62,7 @@ static const struct task_entry cold_boot[] = { - 	{ pre_training,                                           true, "PRETRAIN",   }, - 	{ train_receive_enable,                                   true, "RCVET",      }, - 	{ train_read_mpr,                                         true, "RDMPRT",     }, -+	{ train_jedec_write_leveling,                             true, "JWRL",       }, - }; -  - /* Return a generic stepping value to make stepping checks simpler */ -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -index 49e9214656..86d89f2120 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -@@ -59,6 +59,9 @@ - /* Specified in PI ticks. 64 PI ticks == 1 qclk */ - #define tDQSCK_DRIFT		64 -  -+/* Maximum additional latency */ -+#define MAX_ADD_DELAY		2 -+ - enum margin_parameter { - 	RcvEna, - 	RdT, -@@ -215,6 +218,7 @@ enum raminit_status { - 	RAMINIT_STATUS_REUT_ERROR, - 	RAMINIT_STATUS_RCVEN_FAILURE, - 	RAMINIT_STATUS_RMPR_FAILURE, -+	RAMINIT_STATUS_JWRL_FAILURE, - 	RAMINIT_STATUS_UNSPECIFIED_ERROR, /** TODO: Deprecated in favor of specific values **/ - }; -  -@@ -380,6 +384,11 @@ static inline uint32_t get_data_train_feedback(const uint8_t channel, const uint - 	return mchbar_read32(DDR_DATA_TRAIN_FEEDBACK(channel, byte)); - } -  -+static inline uint16_t get_byte_group_errors(const uint8_t channel) -+{ -+	return mchbar_read32(4 + REUT_ch_ERR_MISC_STATUS(channel)) & 0x1ff; -+} -+ - /* Number of ticks to wait in units of 69.841279 ns (citation needed) */ - static inline void tick_delay(const uint32_t delay) - { -@@ -437,6 +446,7 @@ enum raminit_status configure_memory_map(struct sysinfo *ctrl); - enum raminit_status do_jedec_init(struct sysinfo *ctrl); - enum raminit_status train_receive_enable(struct sysinfo *ctrl); - enum raminit_status train_read_mpr(struct sysinfo *ctrl); -+enum raminit_status train_jedec_write_leveling(struct sysinfo *ctrl); -  - void configure_timings(struct sysinfo *ctrl); - void configure_refresh(struct sysinfo *ctrl); -diff --git a/src/northbridge/intel/haswell/native_raminit/train_jedec_write_leveling.c b/src/northbridge/intel/haswell/native_raminit/train_jedec_write_leveling.c -new file mode 100644 -index 0000000000..ef6483e2bd ---- /dev/null -+++ b/src/northbridge/intel/haswell/native_raminit/train_jedec_write_leveling.c -@@ -0,0 +1,581 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#include <assert.h> -+#include <console/console.h> -+#include <delay.h> -+#include <northbridge/intel/haswell/haswell.h> -+#include <types.h> -+ -+#include "raminit_native.h" -+#include "ranges.h" -+ -+#define JWLC_PLOT	RAM_DEBUG -+#define JWRL_PLOT	RAM_DEBUG -+ -+static void reset_dram_dll(struct sysinfo *ctrl, const uint8_t channel, const uint8_t rank) -+{ -+	const uint16_t mr0reg = ctrl->mr0[channel][rank / 2]; -+	reut_issue_mrs(ctrl, channel, BIT(rank), 0, mr0reg | MR0_DLL_RESET); -+} -+ -+static void program_wdb_pattern(struct sysinfo *ctrl, const bool invert) -+{ -+	/* Pattern to keep DQ-DQS simple but detect any failures. Same as NHM/WSM. */ -+	const uint8_t pat[4][2] = { -+		{ 0x00, 0xff }, -+		{ 0xff, 0x00 }, -+		{ 0xc3, 0x3c }, -+		{ 0x3c, 0xc3 }, -+	}; -+	const uint8_t pmask[2][8] = { -+		{ 0, 0, 1, 1, 1, 1, 0, 0 }, -+		{ 1, 1, 0, 0, 0, 0, 1, 1 }, -+	}; -+	for (uint8_t s = 0; s < ARRAY_SIZE(pat); s++) -+		write_wdb_fixed_pat(ctrl, pat[s], pmask[invert], ARRAY_SIZE(pmask[invert]), s); -+} -+ -+static int16_t set_add_delay(uint32_t *add_delay, uint8_t rank, int8_t target_off) -+{ -+	const uint8_t shift = rank * 2; -+	if (target_off > MAX_ADD_DELAY) { -+		*add_delay &= ~(3 << shift); -+		*add_delay |= MAX_ADD_DELAY << shift; -+		return 128 * (target_off - MAX_ADD_DELAY); -+	} else if (target_off < 0) { -+		*add_delay &= ~(3 << shift); -+		*add_delay |= 0 << shift; -+		return 128 * target_off; -+	} else { -+		*add_delay &= ~(3 << shift); -+		*add_delay |= target_off << shift; -+		return 0; -+	} -+} -+ -+static enum raminit_status train_jedec_write_leveling_cleanup(struct sysinfo *ctrl) -+{ -+	const struct reut_box reut_addr = { -+		.col = { -+			.start   = 0, -+			.stop    = 1023, -+			.inc_val = 1, -+		}, -+	}; -+	const struct wdb_pat wdb_pattern = { -+		.start_ptr  = 0, -+		.stop_ptr   = 3, -+		.inc_rate   = 1, -+		.dq_pattern = BASIC_VA, -+	}; -+	const int8_t offsets[] = { 0, 1, -1, 2, 3 }; -+	const int8_t dq_offsets[] = { 0, -10, 10, -5, 5, -15, 15 }; -+	const uint8_t dq_offset_max = ARRAY_SIZE(dq_offsets); -+ -+	/* Set LFSR seeds to be sequential */ -+	program_wdb_lfsr(ctrl, true); -+	setup_io_test( -+		ctrl, -+		ctrl->chanmap, -+		PAT_WR_RD, -+		2, -+		4, -+		&reut_addr, -+		NSOE, -+		&wdb_pattern, -+		0, -+		0); -+ -+	const union reut_pat_wdb_cl_mux_cfg_reg reut_wdb_cl_mux_cfg = { -+		.mux_0_control       = REUT_MUX_BTBUFFER, -+		.mux_1_control       = REUT_MUX_BTBUFFER, -+		.mux_2_control       = REUT_MUX_BTBUFFER, -+		.ecc_data_source_sel = 1, -+	}; -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		mchbar_write32(REUT_ch_PAT_WDB_CL_MUX_CFG(channel), reut_wdb_cl_mux_cfg.raw); -+	} -+ -+	int8_t byte_off[NUM_CHANNELS][NUM_LANES] = { 0 }; -+	uint32_t add_delay[NUM_CHANNELS] = { 0 }; -+	enum raminit_status status = RAMINIT_STATUS_SUCCESS; -+	bool invert = false; -+	const uint16_t valid_byte_mask = BIT(ctrl->lanes) - 1; -+	for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { -+		uint8_t chanmask = 0; -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) -+			chanmask |= select_reut_ranks(ctrl, channel, BIT(rank)); -+ -+		if (!chanmask) -+			continue; -+ -+		printk(BIOS_DEBUG, "Rank %u\n", rank); -+		printk(JWLC_PLOT, "Channel"); -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			printk(JWLC_PLOT, "\t\t%u\t", channel); -+		} -+		printk(JWLC_PLOT, "\nByte\t"); -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			printk(JWLC_PLOT, "\t"); -+			for (uint8_t byte = 0; byte < ctrl->lanes; byte++) -+				printk(JWLC_PLOT, "%u ", byte); -+		} -+		printk(JWLC_PLOT, "\nDelay DqOffset"); -+		bool done = false; -+		int8_t byte_sum[NUM_CHANNELS] = { 0 }; -+		uint16_t byte_pass[NUM_CHANNELS] = { 0 }; -+		for (uint8_t off = 0; off < ARRAY_SIZE(offsets); off++) { -+			for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+				if (!rank_in_ch(ctrl, rank, channel)) -+					continue; -+ -+				const int16_t global_byte_off = -+					set_add_delay(&add_delay[channel], rank, offsets[off]); -+				for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+					update_txt(ctrl, channel, rank, byte, TXT_DQDQS_OFF, -+						global_byte_off); -+				} -+				mchbar_write32(SC_WR_ADD_DELAY_ch(channel), -+						add_delay[channel]); -+			} -+			/* Reset FIFOs and DRAM DLL (Micron workaround) */ -+			if (!ctrl->lpddr) { -+				io_reset(); -+				for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+					if (!rank_in_ch(ctrl, rank, channel)) -+						continue; -+ -+					reset_dram_dll(ctrl, channel, rank); -+				} -+				udelay(1); -+			} -+			for (uint8_t dq_offset = 0; dq_offset < dq_offset_max; dq_offset++) { -+				printk(JWLC_PLOT, "\n% 3d\t% 3d", -+					offsets[off], dq_offsets[dq_offset]); -+				change_1d_margin_multicast( -+					ctrl, -+					WrT, -+					dq_offsets[dq_offset], -+					rank, -+					false, -+					REG_FILE_USE_RANK); -+ -+				/* -+				 * Re-program the WDB pattern. Change the pattern -+				 * for the next test to avoid false pass issues. -+				 */ -+				program_wdb_pattern(ctrl, invert); -+				invert = !invert; -+				run_io_test(ctrl, chanmask, BASIC_VA, true); -+				done = true; -+				for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+					if (!rank_in_ch(ctrl, rank, channel)) -+						continue; -+ -+					printk(JWLC_PLOT, "\t"); -+					uint16_t result = get_byte_group_errors(channel); -+					result &= valid_byte_mask; -+ -+					/* Skip bytes that have failed or already passed */ -+					const uint16_t skip_me = result | byte_pass[channel]; -+					for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+						const bool pass = result & BIT(byte); -+						printk(JWLC_PLOT, pass ? "# " : ". "); -+						if (skip_me & BIT(byte)) -+							continue; -+ -+						byte_pass[channel] |= BIT(byte); -+						byte_off[channel][byte] = offsets[off]; -+						byte_sum[channel] += offsets[off]; -+					} -+					if (byte_pass[channel] != valid_byte_mask) -+						done = false; -+				} -+				if (done) -+					break; -+			} -+			if (done) -+				break; -+		} -+		printk(BIOS_DEBUG, "\n\n"); -+		if (!done) { -+			printk(BIOS_ERR, "JWLC: Could not find a pass for all bytes\n"); -+			for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+				if (!rank_in_ch(ctrl, rank, channel)) -+					continue; -+ -+				printk(BIOS_ERR, "Channel %u, rank %u fail:", channel, rank); -+				const uint16_t passing_mask = byte_pass[channel]; -+				for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+					if (BIT(byte) & passing_mask) -+						continue; -+ -+					printk(BIOS_ERR, " %u", byte); -+				} -+				printk(BIOS_ERR, "\n"); -+			} -+			status = RAMINIT_STATUS_JWRL_FAILURE; -+			break; -+		} -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			/* Refine target offset to make sure it works for all bytes */ -+			int8_t target_off = DIV_ROUND_CLOSEST(byte_sum[channel], ctrl->lanes); -+			int16_t global_byte_off = 0; -+			uint8_t all_good_loops = 0; -+			bool all_good = 0; -+			while (!all_good) { -+				global_byte_off = -+					set_add_delay(&add_delay[channel], rank, target_off); -+				all_good = true; -+				for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+					int16_t local_offset; -+					local_offset = byte_off[channel][byte] - target_off; -+					local_offset = local_offset * 128 + global_byte_off; -+					const uint16_t tx_dq = ctrl->tx_dq[channel][rank][byte]; -+					if (tx_dq + local_offset >= (512 - 64)) { -+						all_good = false; -+						all_good_loops++; -+						target_off++; -+						break; -+					} -+					const uint16_t txdqs = ctrl->tx_dq[channel][rank][byte]; -+					if (txdqs + local_offset < 96) { -+						all_good = false; -+						all_good_loops++; -+						target_off--; -+						break; -+					} -+				} -+				/* Avoid an infinite loop */ -+				if (all_good_loops > 3) -+					break; -+			} -+			if (!all_good) { -+				printk(BIOS_ERR, "JWLC: Target offset refining failed\n"); -+				status = RAMINIT_STATUS_JWRL_FAILURE; -+				break; -+			} -+			printk(BIOS_DEBUG, "C%u.R%u:  Offset\tFinalEdge\n", channel, rank); -+			for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+				int16_t local_offset; -+				local_offset = byte_off[channel][byte] - target_off; -+				local_offset = local_offset * 128 + global_byte_off; -+				ctrl->tx_dq[channel][rank][byte] += local_offset; -+				ctrl->txdqs[channel][rank][byte] += local_offset; -+				update_txt(ctrl, channel, rank, byte, TXT_RESTORE, 0); -+				printk(BIOS_DEBUG, "  B%u:   %d\t%d\n", byte, local_offset, -+					ctrl->txdqs[channel][rank][byte]); -+			} -+			mchbar_write32(SC_WR_ADD_DELAY_ch(channel), add_delay[channel]); -+			if (!ctrl->lpddr) { -+				reset_dram_dll(ctrl, channel, rank); -+				udelay(1); -+			} -+			printk(BIOS_DEBUG, "\n"); -+		} -+		printk(BIOS_DEBUG, "\n"); -+	} -+ -+	/* Restore WDB after test */ -+	write_wdb_va_pat(ctrl, 0, BASIC_VA_PAT_SPREAD_8, 8, 0); -+	program_wdb_lfsr(ctrl, false); -+	mchbar_write32(DDR_DATA_OFFSET_TRAIN, 0); -+ -+	/** TODO: Do full JEDEC init instead? **/ -+	io_reset(); -+	return status; -+} -+ -+static enum raminit_status verify_wl_width(const int32_t lwidth) -+{ -+	if (lwidth <= 32) { -+		/* Check if width is valid */ -+		printk(BIOS_ERR, "WrLevel: Width region (%d) too small\n", lwidth); -+		return RAMINIT_STATUS_JWRL_FAILURE; -+	} -+	if (lwidth >= 96) { -+		/* Since we're calibrating a phase, a too large region is a problem */ -+		printk(BIOS_ERR, "WrLevel: Width region (%d) too large\n", lwidth); -+		return RAMINIT_STATUS_JWRL_FAILURE; -+	} -+	return 0; -+} -+ -+enum raminit_status train_jedec_write_leveling(struct sysinfo *ctrl) -+{ -+	/* -+	 * Enabling WL mode causes DQS to toggle for 1024 QCLK. -+	 * Wait for this to stop. Round up to nearest microsecond. -+	 */ -+	const bool wl_long_delay = ctrl->lpddr; -+	const uint32_t dqs_toggle_time = wl_long_delay ? 2048 : 1024; -+	const uint32_t wait_time_us = DIV_ROUND_UP(ctrl->qclkps * dqs_toggle_time, 1000 * 1000); -+ -+	const uint16_t wl_start = 192; -+	const uint16_t wl_stop  = 192 + 128; -+	const uint16_t wl_step  = 2; -+ -+	/* Do not use cached MR values */ -+	const bool save_restore_mrs = ctrl->restore_mrs; -+	ctrl->restore_mrs = 0; -+ -+	/* Propagate delay values (without a write command) */ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		/* Propagate delay values from rank 0 to prevent assertion failures in RTL */ -+		union ddr_data_control_0_reg data_control_0 = { -+			.raw = ctrl->dq_control_0[channel], -+		}; -+		data_control_0.read_rf_rd   = 0; -+		data_control_0.read_rf_wr   = 1; -+		data_control_0.read_rf_rank = 0; -+		mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), data_control_0.raw); -+		for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+			union ddr_data_control_2_reg data_control_2 = { -+				.raw = ctrl->dq_control_2[channel][byte], -+			}; -+			data_control_2.force_bias_on = 1; -+			data_control_2.force_rx_on   = 0; -+			data_control_2.wl_long_delay = wl_long_delay; -+			mchbar_write32(DQ_CONTROL_2(channel, byte), data_control_2.raw); -+		} -+	} -+ -+	if (ctrl->lpddr) -+		die("%s: Missing LPDDR support\n", __func__); -+ -+	if (!ctrl->lpddr) -+		ddr3_program_mr1(ctrl, 0, 1); -+ -+	enum raminit_status status = RAMINIT_STATUS_SUCCESS; -+	struct phase_train_data region_data[NUM_CHANNELS][NUM_LANES] = { 0 }; -+	for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { -+		if (!does_rank_exist(ctrl, rank)) -+			continue; -+ -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			/** TODO: Differs for LPDDR **/ -+			uint16_t mr1reg = ctrl->mr1[channel][rank / 2]; -+			mr1reg &= ~MR1_QOFF_ENABLE; -+			mr1reg |= MR1_WL_ENABLE; -+			if (is_hsw_ult()) { -+				mr1reg &= ~RTTNOM_MASK; -+				mr1reg |= encode_ddr3_rttnom(120); -+			} else if (ctrl->dpc[channel] == 2) { -+				mr1reg &= ~RTTNOM_MASK; -+				mr1reg |= encode_ddr3_rttnom(60); -+			} -+			reut_issue_mrs(ctrl, channel, BIT(rank), 1, mr1reg); -+ -+			/* Assert ODT for myself */ -+			uint8_t odt_matrix = BIT(rank); -+			if (ctrl->dpc[channel] == 2) { -+				/* Assert ODT for non-target DIMM */ -+				const uint8_t other_dimm = ((rank + 2) / 2) & 1; -+				odt_matrix |= BIT(2 * other_dimm); -+			} -+ -+			union reut_misc_odt_ctrl_reg reut_misc_odt_ctrl = { -+				.raw = 0, -+			}; -+			if (ctrl->lpddr) { -+				/* Only one ODT pin for ULT */ -+				reut_misc_odt_ctrl.odt_on       = 1; -+				reut_misc_odt_ctrl.odt_override = 1; -+			} else if (!is_hsw_ult()) { -+				reut_misc_odt_ctrl.odt_on       = odt_matrix; -+				reut_misc_odt_ctrl.odt_override = 0xf; -+			} -+			mchbar_write32(REUT_ch_MISC_ODT_CTRL(channel), reut_misc_odt_ctrl.raw); -+		} -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			/* -+			 * Enable write leveling mode in DDR and propagate delay -+			 * values (without a write command). Stay in WL mode. -+			 */ -+			union ddr_data_control_0_reg data_control_0 = { -+				.raw = ctrl->dq_control_0[channel], -+			}; -+			data_control_0.wl_training_mode = 1; -+			data_control_0.tx_pi_on         = 1; -+			data_control_0.read_rf_rd       = 0; -+			data_control_0.read_rf_wr       = 1; -+			data_control_0.read_rf_rank     = rank; -+			mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), data_control_0.raw); -+		} -+		printk(BIOS_DEBUG, "\nRank %u\n", rank); -+		printk(JWRL_PLOT, "Channel\t"); -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			printk(JWRL_PLOT, "%u", channel); -+			if (channel > 0) -+				continue; -+ -+			for (uint8_t byte = 0; byte < ctrl->lanes; byte++) -+				printk(JWRL_PLOT, "\t"); -+		} -+		printk(JWRL_PLOT, "\nByte"); -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			for (uint8_t byte = 0; byte < ctrl->lanes; byte++) -+				printk(JWRL_PLOT, "\t%u", byte); -+		} -+		printk(JWRL_PLOT, "\nWlDelay"); -+		for (uint16_t wl_delay = wl_start; wl_delay < wl_stop; wl_delay += wl_step) { -+			printk(JWRL_PLOT, "\n %3u:", wl_delay); -+			for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+				if (!rank_in_ch(ctrl, rank, channel)) -+					continue; -+ -+				for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+					update_txt(ctrl, channel, rank, byte, TXT_TXDQS, -+						wl_delay); -+				} -+			} -+			/* Wait for the first burst to finish */ -+			if (wl_delay == wl_start) -+				udelay(wait_time_us); -+ -+			io_reset(); -+			udelay(wait_time_us); -+			for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+				if (!rank_in_ch(ctrl, rank, channel)) -+					continue; -+ -+				for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+					const uint32_t feedback = -+						get_data_train_feedback(channel, byte); -+					const bool pass = (feedback & 0x1ff) >= 16; -+					printk(JWRL_PLOT, "\t%c%u", pass ? '.' : '#', feedback); -+					phase_record_pass( -+						®ion_data[channel][byte], -+						pass, -+						wl_delay, -+						wl_start, -+						wl_step); -+				} -+			} -+		} -+		printk(JWRL_PLOT, "\n"); -+		printk(BIOS_DEBUG, "\n\tInitSt\tInitEn\tCurrSt\tCurrEn\tLargSt\tLargEn\n"); -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			printk(BIOS_DEBUG, "C%u\n", channel); -+			for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+				struct phase_train_data *data = ®ion_data[channel][byte]; -+ -+				phase_append_initial_to_current(data, wl_start, wl_step); -+				printk(BIOS_DEBUG, "   B%u:\t%d\t%d\t%d\t%d\t%d\t%d\n", -+					byte, -+					data->initial.start, -+					data->initial.end, -+					data->current.start, -+					data->current.end, -+					data->largest.start, -+					data->largest.end); -+			} -+		} -+ -+		/* -+		 * Clean up after test. Very coarsely adjust for -+		 * any cycle errors. Program values for TxDQS. -+		 */ -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			/* Clear ODT before MRS (JEDEC spec) */ -+			mchbar_write32(REUT_ch_MISC_ODT_CTRL(channel), 0); -+ -+			/** TODO: Differs for LPDDR **/ -+			const uint16_t mr1reg = ctrl->mr1[channel][rank / 2] | MR1_QOFF_ENABLE; -+			reut_issue_mrs(ctrl, channel, BIT(rank), 1, mr1reg); -+ -+			printk(BIOS_DEBUG, "\nC%u.R%u:  LftEdge Width\n", channel, rank); -+			const bool rank_x16 = ctrl->dimms[channel][rank / 2].data.width == 16; -+			for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+				struct phase_train_data *data = ®ion_data[channel][byte]; -+				const int32_t lwidth = range_width(data->largest); -+				int32_t tx_start = data->largest.start; -+				printk(BIOS_DEBUG, "  B%u:   %d\t%d\n", byte, tx_start, lwidth); -+				status = verify_wl_width(lwidth); -+				if (status) { -+					printk(BIOS_ERR, -+						"WrLevel problems on channel %u, byte %u\n", -+						channel, byte); -+					goto clean_up; -+				} -+ -+				/* Align byte pairs if DIMM is x16 */ -+				if (rank_x16 && (byte & 1)) { -+					const struct phase_train_data *const ref_data = -+							®ion_data[channel][byte - 1]; -+ -+					if (tx_start > ref_data->largest.start + 64) -+						tx_start -= 128; -+ -+					if (tx_start < ref_data->largest.start - 64) -+						tx_start += 128; -+				} -+ -+				/* Fix for b4618067 - need to add 1 QCLK to DQS PI */ -+				if (is_hsw_ult()) -+					tx_start += 64; -+ -+				assert(tx_start >= 0); -+				ctrl->txdqs[channel][rank][byte] = tx_start; -+				ctrl->tx_dq[channel][rank][byte] = tx_start + 32; -+				update_txt(ctrl, channel, rank, byte, TXT_RESTORE, 0); -+			} -+		} -+		printk(BIOS_DEBUG, "\n"); -+	} -+ -+clean_up: -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), ctrl->dq_control_0[channel]); -+		for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+			mchbar_write32(DQ_CONTROL_2(channel, byte), -+				ctrl->dq_control_2[channel][byte]); -+		} -+	} -+	if (!ctrl->lpddr) -+		ddr3_program_mr1(ctrl, 0, 0); -+ -+	ctrl->restore_mrs = save_restore_mrs; -+ -+	if (status) -+		return status; -+ -+	/** TODO: If this step fails and dec_wrd is set, clear it and try again **/ -+	return train_jedec_write_leveling_cleanup(ctrl); -+} -diff --git a/src/northbridge/intel/haswell/registers/mchbar.h b/src/northbridge/intel/haswell/registers/mchbar.h -index 6a31d3a32c..7c0b5a49de 100644 ---- a/src/northbridge/intel/haswell/registers/mchbar.h -+++ b/src/northbridge/intel/haswell/registers/mchbar.h -@@ -121,6 +121,8 @@ -  - #define REUT_ch_ERR_DATA_MASK(ch)		_MCMAIN_C(0x40d8, ch) -  -+#define REUT_ch_ERR_MISC_STATUS(ch)		_MCMAIN_C(0x40e8, ch) -+ - #define REUT_ch_MISC_CKE_CTRL(ch)		_MCMAIN_C(0x4190, ch) - #define REUT_ch_MISC_ODT_CTRL(ch)		_MCMAIN_C(0x4194, ch) - #define REUT_ch_MISC_PAT_CADB_CTRL(ch)		_MCMAIN_C(0x4198, ch) ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0015-haswell-NRI-Add-final-raminit-steps.patch b/config/coreboot/haswell/patches/0015-haswell-NRI-Add-final-raminit-steps.patch deleted file mode 100644 index 3626bf6d..00000000 --- a/config/coreboot/haswell/patches/0015-haswell-NRI-Add-final-raminit-steps.patch +++ /dev/null @@ -1,570 +0,0 @@ -From eba8680d618db95028e3f984f25881df0e67abf7 Mon Sep 17 00:00:00 2001 -From: Angel Pons <th3fanbus@gmail.com> -Date: Sun, 8 May 2022 14:29:05 +0200 -Subject: [PATCH 15/20] haswell NRI: Add final raminit steps - -Implement the remaining raminit steps. Although many training steps are -missing, this is enough to boot on the Asrock B85M Pro4. - -Change-Id: I94f3b65f0218d4da4fda4d84592dfd91f77f8f21 -Signed-off-by: Angel Pons <th3fanbus@gmail.com> ---- - src/northbridge/intel/haswell/Kconfig         |   4 +- - .../intel/haswell/native_raminit/Makefile.mk  |   1 + - .../haswell/native_raminit/activate_mc.c      | 388 ++++++++++++++++++ - .../haswell/native_raminit/raminit_main.c     |   5 +- - .../haswell/native_raminit/raminit_native.c   |   5 +- - .../haswell/native_raminit/raminit_native.h   |   2 + - .../haswell/native_raminit/reg_structs.h      |  12 + - .../intel/haswell/registers/mchbar.h          |   7 + - 8 files changed, 416 insertions(+), 8 deletions(-) - create mode 100644 src/northbridge/intel/haswell/native_raminit/activate_mc.c - -diff --git a/src/northbridge/intel/haswell/Kconfig b/src/northbridge/intel/haswell/Kconfig -index 4b83a25bc1..c6ab27184e 100644 ---- a/src/northbridge/intel/haswell/Kconfig -+++ b/src/northbridge/intel/haswell/Kconfig -@@ -11,12 +11,12 @@ config NORTHBRIDGE_INTEL_HASWELL - if NORTHBRIDGE_INTEL_HASWELL -  - config USE_NATIVE_RAMINIT --	bool "[NOT WORKING] Use native raminit" -+	bool "[NOT COMPLETE] Use native raminit" - 	default n - 	select HAVE_DEBUG_RAM_SETUP - 	help - 	  Select if you want to use coreboot implementation of raminit rather than --	  MRC.bin. Currently incomplete and does not boot. -+	  MRC.bin. Currently incomplete and does not support S3 resume. -  - config HASWELL_VBOOT_IN_BOOTBLOCK - 	depends on VBOOT -diff --git a/src/northbridge/intel/haswell/native_raminit/Makefile.mk b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -index 40c2f5e014..d97da72890 100644 ---- a/src/northbridge/intel/haswell/native_raminit/Makefile.mk -+++ b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -@@ -1,5 +1,6 @@ - ## SPDX-License-Identifier: GPL-2.0-or-later -  -+romstage-y += activate_mc.c - romstage-y += change_margin.c - romstage-y += configure_mc.c - romstage-y += ddr3.c -diff --git a/src/northbridge/intel/haswell/native_raminit/activate_mc.c b/src/northbridge/intel/haswell/native_raminit/activate_mc.c -new file mode 100644 -index 0000000000..78a7ad27ef ---- /dev/null -+++ b/src/northbridge/intel/haswell/native_raminit/activate_mc.c -@@ -0,0 +1,388 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#include <console/console.h> -+#include <delay.h> -+#include <device/pci_ops.h> -+#include <northbridge/intel/haswell/haswell.h> -+#include <timer.h> -+#include <types.h> -+ -+#include "raminit_native.h" -+ -+static void update_internal_clocks_on(struct sysinfo *ctrl) -+{ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		bool clocks_on = false; -+		for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+			const union ddr_data_control_1_reg data_control_1 = { -+				.raw = ctrl->dq_control_1[channel][byte], -+			}; -+			const int8_t o_on = data_control_1.odt_delay; -+			const int8_t s_on = data_control_1.sense_amp_delay; -+			const int8_t o_off = data_control_1.odt_duration; -+			const int8_t s_off = data_control_1.sense_amp_duration; -+			if (o_on + o_off >= 7 || s_on + s_off >= 7) { -+				clocks_on = true; -+				break; -+			} -+		} -+		union ddr_data_control_0_reg data_control_0 = { -+			.raw = ctrl->dq_control_0[channel], -+		}; -+		data_control_0.internal_clocks_on = clocks_on; -+		ctrl->dq_control_0[channel] = data_control_0.raw; -+		mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), data_control_0.raw); -+	} -+} -+ -+/* Switch off unused segments of the SDLL to save power */ -+static void update_sdll_length(struct sysinfo *ctrl) -+{ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+			uint8_t max_pi = 0; -+			for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { -+				if (!rank_in_ch(ctrl, rank, channel)) -+					continue; -+ -+				const uint8_t rx_dqs_p = ctrl->rxdqsp[channel][rank][byte]; -+				const uint8_t rx_dqs_n = ctrl->rxdqsn[channel][rank][byte]; -+				max_pi = MAX(max_pi, MAX(rx_dqs_p, rx_dqs_n)); -+			} -+			/* Update SDLL length for power savings */ -+			union ddr_data_control_1_reg data_control_1 = { -+				.raw = ctrl->dq_control_1[channel][byte], -+			}; -+			/* Calculate which segments to turn off */ -+			data_control_1.sdll_segment_disable = (7 - (max_pi >> 3)) & ~1; -+			ctrl->dq_control_1[channel][byte] = data_control_1.raw; -+			mchbar_write32(DQ_CONTROL_1(channel, byte), data_control_1.raw); -+		} -+	} -+} -+ -+static void set_rx_clk_stg_num(struct sysinfo *ctrl, const uint8_t channel) -+{ -+	const uint8_t rcven_drift = ctrl->lpddr ? DIV_ROUND_UP(tDQSCK_DRIFT, ctrl->qclkps) : 1; -+	uint8_t max_rcven = 0; -+	for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { -+		if (!rank_in_ch(ctrl, rank, channel)) -+			continue; -+ -+		for (uint8_t byte = 0; byte < ctrl->lanes; byte++) -+			max_rcven = MAX(max_rcven, ctrl->rcven[channel][rank][byte] / 64); -+	} -+	const union ddr_data_control_1_reg ddr_data_control_1 = { -+		.raw = ctrl->dq_control_1[channel][0], -+	}; -+	const bool lpddr_long_odt = ddr_data_control_1.lpddr_long_odt_en; -+	const uint8_t rcven_turnoff = max_rcven + 18 + 2 * rcven_drift + lpddr_long_odt; -+	const union ddr_data_control_0_reg ddr_data_control_0 = { -+		.raw = ctrl->dq_control_0[channel], -+	}; -+	for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+		union ddr_data_control_2_reg ddr_data_control_2 = { -+			.raw = ctrl->dq_control_2[channel][byte], -+		}; -+		if (ddr_data_control_0.odt_samp_extend_en) { -+			if (ddr_data_control_2.rx_clk_stg_num < rcven_turnoff) -+				ddr_data_control_2.rx_clk_stg_num = rcven_turnoff; -+		} else { -+			const int8_t o_on = ddr_data_control_1.odt_delay; -+			const int8_t o_off = ddr_data_control_1.odt_duration; -+			ddr_data_control_2.rx_clk_stg_num = MAX(17, o_on + o_off + 14); -+		} -+		ctrl->dq_control_2[channel][byte] = ddr_data_control_2.raw; -+		mchbar_write32(DQ_CONTROL_2(channel, byte), ddr_data_control_2.raw); -+	} -+} -+ -+#define SELF_REFRESH_IDLE_COUNT 0x200 -+ -+static void enter_sr(void) -+{ -+	mchbar_write32(PM_SREF_CONFIG, SELF_REFRESH_IDLE_COUNT | BIT(16)); -+	udelay(1); -+} -+ -+enum power_down_mode { -+	PDM_NO_PD	= 0, -+	PDM_APD		= 1, -+	PDM_PPD		= 2, -+	PDM_PPD_DLL_OFF	= 6, -+}; -+ -+static void power_down_config(struct sysinfo *ctrl) -+{ -+	const enum power_down_mode pd_mode = ctrl->lpddr ? PDM_PPD : PDM_PPD_DLL_OFF; -+	mchbar_write32(PM_PDWN_CONFIG, pd_mode << 12 | 0x40); -+} -+ -+static void train_power_modes_post(struct sysinfo *ctrl) -+{ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		/* Adjust tCPDED and tPRPDEN */ -+		if (ctrl->mem_clock_mhz >= 933) -+			ctrl->tc_bankrank_d[channel].tCPDED = 2; -+ -+		if (ctrl->mem_clock_mhz >= 1066) -+			ctrl->tc_bankrank_d[channel].tPRPDEN = 2; -+ -+		mchbar_write32(TC_BANK_RANK_D_ch(channel), ctrl->tc_bankrank_d[channel].raw); -+	} -+	power_down_config(ctrl); -+	mchbar_write32(MCDECS_CBIT, BIT(30));	/* dis_msg_clk_gate */ -+} -+ -+static uint8_t compute_burst_end_odt_delay(const struct sysinfo *const ctrl) -+{ -+	/* Must be disabled for LPDDR */ -+	if (ctrl->lpddr) -+		return 0; -+ -+	const uint8_t beod = MIN(7, DIV_ROUND_CLOSEST(14300 * 20 / 100, ctrl->qclkps)); -+	if (beod < 3) -+		return 0; -+ -+	if (beod < 4) -+		return 4; -+ -+	return beod; -+} -+ -+static void program_burst_end_odt_delay(struct sysinfo *ctrl) -+{ -+	/* Program burst_end_odt_delay - it should be zero during training steps */ -+	const uint8_t beod = compute_burst_end_odt_delay(ctrl); -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+			union ddr_data_control_1_reg ddr_data_control_1 = { -+				.raw = ctrl->dq_control_1[channel][byte], -+			}; -+			ddr_data_control_1.burst_end_odt_delay = beod; -+			ctrl->dq_control_1[channel][byte] = ddr_data_control_1.raw; -+			mchbar_write32(DQ_CONTROL_1(channel, byte), ddr_data_control_1.raw); -+		} -+	} -+} -+ -+/* -+ * Return a random value to use for scrambler seeds. Try to use RDRAND -+ * first and fall back to hardcoded values if RDRAND does not succeed. -+ */ -+static uint16_t get_random_number(const uint8_t channel) -+{ -+	/* The RDRAND instruction is only available 100k cycles after reset */ -+	for (size_t i = 0; i < 100000; i++) { -+		uint32_t status; -+		uint32_t random; -+		/** TODO: Clean up asm **/ -+		__asm__ __volatile__( -+			"\n\t .byte 0x0F, 0xC7, 0xF0" -+			"\n\t movl %%eax, %0" -+			"\n\t pushf" -+			"\n\t pop %%eax" -+			"\n\t movl %%eax, %1" -+			: "=m"(random), -+			  "=m"(status) -+			: /* No inputs */ -+			: "eax", "cc"); -+ -+		/* Only consider non-zero random values as valid */ -+		if (status & 1 && random) -+			return random; -+	} -+ -+	/* https://xkcd.com/221 */ -+	if (channel) -+		return 0x28f4; -+	else -+		return 0x893e; -+} -+ -+/* Work around "error: 'typeof' applied to a bit-field" */ -+static inline uint32_t max(const uint32_t a, const uint32_t b) -+{ -+	return MAX(a, b); -+} -+ -+enum raminit_status activate_mc(struct sysinfo *ctrl) -+{ -+	const bool enable_scrambling = true; -+	const bool enable_cmd_tristate = true; -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		if (enable_scrambling && ctrl->stepping < STEPPING_C0) { -+			/* Make sure tRDRD_(sr, dr, dd) are at least 6 for scrambler W/A */ -+			union tc_bank_rank_a_reg tc_bank_rank_a = { -+				.raw = mchbar_read32(TC_BANK_RANK_A_ch(channel)), -+			}; -+			tc_bank_rank_a.tRDRD_sr = max(tc_bank_rank_a.tRDRD_sr, 6); -+			tc_bank_rank_a.tRDRD_dr = max(tc_bank_rank_a.tRDRD_dr, 6); -+			tc_bank_rank_a.tRDRD_dd = max(tc_bank_rank_a.tRDRD_dd, 6); -+			mchbar_write32(TC_BANK_RANK_A_ch(channel), tc_bank_rank_a.raw); -+		} -+		if (enable_scrambling) { -+			const union ddr_scramble_reg ddr_scramble = { -+				.scram_key = get_random_number(channel), -+				.scram_en  = 1, -+			}; -+			mchbar_write32(DDR_SCRAMBLE_ch(channel), ddr_scramble.raw); -+		} -+		if (ctrl->tCMD == 1) { -+			/* If we are in 1N mode, enable and set command rate limit to 3 */ -+			union mcmain_command_rate_limit_reg cmd_rate_limit = { -+				.raw = mchbar_read32(COMMAND_RATE_LIMIT_ch(channel)), -+			}; -+			cmd_rate_limit.enable_cmd_limit = 1; -+			cmd_rate_limit.cmd_rate_limit   = 3; -+			mchbar_write32(COMMAND_RATE_LIMIT_ch(channel), cmd_rate_limit.raw); -+		} -+		if (enable_cmd_tristate) { -+			/* Enable command tri-state at the end of training */ -+			union tc_bank_rank_a_reg tc_bank_rank_a = { -+				.raw = mchbar_read32(TC_BANK_RANK_A_ch(channel)), -+			}; -+			tc_bank_rank_a.cmd_3st_dis = 0; -+			mchbar_write32(TC_BANK_RANK_A_ch(channel), tc_bank_rank_a.raw); -+		} -+		/* Set MC to normal mode and clean the ODT and CKE */ -+		mchbar_write32(REUT_ch_SEQ_CFG(channel), REUT_MODE_NOP << 12); -+		/* Set again the rank occupancy */ -+		mchbar_write8(MC_INIT_STATE_ch(channel), ctrl->rankmap[channel]); -+		if (ctrl->is_ecc) { -+			/* Enable ECC I/O and logic */ -+			union mad_dimm_reg mad_dimm = { -+				.raw = mchbar_read32(MAD_DIMM(channel)), -+			}; -+			mad_dimm.ecc_mode = 3; -+			mchbar_write32(MAD_DIMM(channel), mad_dimm.raw); -+		} -+	} -+ -+	if (!is_hsw_ult()) -+		update_internal_clocks_on(ctrl); -+ -+	update_sdll_length(ctrl); -+ -+	program_burst_end_odt_delay(ctrl); -+ -+	if (is_hsw_ult()) { -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!does_ch_exist(ctrl, channel)) -+				continue; -+ -+			set_rx_clk_stg_num(ctrl, channel); -+		} -+		/** TODO: Program DDRPL_CR_DDR_TX_DELAY if Memory Trace is enabled **/ -+	} -+ -+	/* Enable periodic COMP */ -+	mchbar_write32(M_COMP, (union pcu_comp_reg) { -+		.comp_interval = COMP_INT, -+	}.raw); -+ -+	/* Enable the power mode before PCU starts working */ -+	train_power_modes_post(ctrl); -+ -+	/* Set idle timer and self refresh enable bits */ -+	enter_sr(); -+ -+	/** FIXME: Do not hardcode power weights and RAPL settings **/ -+	mchbar_write32(0x5888, 0x00000d0d); -+	mchbar_write32(0x5884, 0x00000004);	/* 58.2 pJ */ -+ -+	mchbar_write32(0x58e0, 0); -+	mchbar_write32(0x58e4, 0); -+ -+	mchbar_write32(0x5890, 0xffff); -+	mchbar_write32(0x5894, 0xffff); -+	mchbar_write32(0x5898, 0xffff); -+	mchbar_write32(0x589c, 0xffff); -+	mchbar_write32(0x58d0, 0xffff); -+	mchbar_write32(0x58d4, 0xffff); -+	mchbar_write32(0x58d8, 0xffff); -+	mchbar_write32(0x58dc, 0xffff); -+ -+	/* Overwrite thermal parameters */ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		mchbar_write32(_MCMAIN_C(0x42ec, channel), 0x0000000f); -+		mchbar_write32(_MCMAIN_C(0x42f0, channel), 0x00000009); -+		mchbar_write32(_MCMAIN_C(0x42f4, channel), 0x00000093); -+		mchbar_write32(_MCMAIN_C(0x42f8, channel), 0x00000087); -+		mchbar_write32(_MCMAIN_C(0x42fc, channel), 0x000000de); -+ -+		/** TODO: Differs for LPDDR **/ -+		mchbar_write32(PM_THRT_CKE_MIN_ch(channel), 0x30); -+	} -+	mchbar_write32(PCU_DDR_PTM_CTL, 0x40); -+	return RAMINIT_STATUS_SUCCESS; -+} -+ -+static void mc_lockdown(void) -+{ -+	/* Lock memory controller registers */ -+	mchbar_write32(MC_LOCK, 0x8f); -+ -+	/* MPCOHTRK_GDXC_OCLA_ADDRESS_HI_LOCK is set when programming the memory map */ -+ -+	/* Lock memory map registers */ -+	pci_or_config16(HOST_BRIDGE, GGC,         1 <<  0); -+	pci_or_config32(HOST_BRIDGE, DPR,         1 <<  0); -+	pci_or_config32(HOST_BRIDGE, MESEG_LIMIT, 1 << 10); -+	pci_or_config32(HOST_BRIDGE, REMAPBASE,   1 <<  0); -+	pci_or_config32(HOST_BRIDGE, REMAPLIMIT,  1 <<  0); -+	pci_or_config32(HOST_BRIDGE, TOM,         1 <<  0); -+	pci_or_config32(HOST_BRIDGE, TOUUD,       1 <<  0); -+	pci_or_config32(HOST_BRIDGE, BDSM,        1 <<  0); -+	pci_or_config32(HOST_BRIDGE, BGSM,        1 <<  0); -+	pci_or_config32(HOST_BRIDGE, TOLUD,       1 <<  0); -+} -+ -+enum raminit_status raminit_done(struct sysinfo *ctrl) -+{ -+	union mc_init_state_g_reg mc_init_state_g = { -+		.raw = mchbar_read32(MC_INIT_STATE_G), -+	}; -+	mc_init_state_g.refresh_enable = 1; -+	mc_init_state_g.pu_mrc_done    = 1; -+	mc_init_state_g.mrc_done       = 1; -+	mchbar_write32(MC_INIT_STATE_G, mc_init_state_g.raw); -+ -+	/* Lock the memory controller to enable normal operation */ -+	mc_lockdown(); -+ -+	/* Poll for mc_init_done_ack to make sure memory initialization is complete */ -+	printk(BIOS_DEBUG, "Waiting for mc_init_done acknowledgement... "); -+ -+	struct stopwatch timer; -+	stopwatch_init_msecs_expire(&timer, 2000); -+	do { -+		mc_init_state_g.raw = mchbar_read32(MC_INIT_STATE_G); -+ -+		/* DRAM will NOT work without the acknowledgement. There is no hope. */ -+		if (stopwatch_expired(&timer)) -+			die("\nTimed out waiting for mc_init_done acknowledgement\n"); -+ -+	} while (mc_init_state_g.mc_init_done_ack == 0); -+	printk(BIOS_DEBUG, "DONE!\n"); -+ -+	/* Provide some data for the graphics driver. Yes, it's hardcoded. */ -+	mchbar_write32(SSKPD + 0, 0x05a2404f); -+	mchbar_write32(SSKPD + 4, 0x140000a0); -+	return RAMINIT_STATUS_SUCCESS; -+} -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_main.c b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -index 1ff23be615..3a65fb01fb 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_main.c -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -@@ -63,6 +63,8 @@ static const struct task_entry cold_boot[] = { - 	{ train_receive_enable,                                   true, "RCVET",      }, - 	{ train_read_mpr,                                         true, "RDMPRT",     }, - 	{ train_jedec_write_leveling,                             true, "JWRL",       }, -+	{ activate_mc,                                            true, "ACTIVATE",   }, -+	{ raminit_done,                                           true, "RAMINITEND", }, - }; -  - /* Return a generic stepping value to make stepping checks simpler */ -@@ -143,7 +145,4 @@ void raminit_main(const enum raminit_boot_mode bootmode) -  - 	if (status != RAMINIT_STATUS_SUCCESS) - 		die("Memory initialization was met with utmost failure and misery\n"); -- --	/** TODO: Implement the required magic **/ --	die("NATIVE RAMINIT: More Magic (tm) required.\n"); - } -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.c b/src/northbridge/intel/haswell/native_raminit/raminit_native.c -index 2fed93de5b..5f7ceec222 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_native.c -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.c -@@ -199,8 +199,6 @@ void perform_raminit(const int s3resume) - 		else - 			me_status = ME_INIT_STATUS_SUCCESS; -  --		/** TODO: Remove this once raminit is implemented **/ --		me_status = ME_INIT_STATUS_ERROR; - 		intel_early_me_init_done(me_status); - 	} -  -@@ -214,7 +212,8 @@ void perform_raminit(const int s3resume) - 	} -  - 	/* Save training data on non-S3 resumes */ --	if (!s3resume) -+	/** TODO: Enable this once training data is populated **/ -+	if (0 && !s3resume) - 		save_mrc_data(&md); -  - 	/** TODO: setup_sdram_meminfo **/ -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -index 86d89f2120..9bab57b518 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -@@ -447,6 +447,8 @@ enum raminit_status do_jedec_init(struct sysinfo *ctrl); - enum raminit_status train_receive_enable(struct sysinfo *ctrl); - enum raminit_status train_read_mpr(struct sysinfo *ctrl); - enum raminit_status train_jedec_write_leveling(struct sysinfo *ctrl); -+enum raminit_status activate_mc(struct sysinfo *ctrl); -+enum raminit_status raminit_done(struct sysinfo *ctrl); -  - void configure_timings(struct sysinfo *ctrl); - void configure_refresh(struct sysinfo *ctrl); -diff --git a/src/northbridge/intel/haswell/native_raminit/reg_structs.h b/src/northbridge/intel/haswell/native_raminit/reg_structs.h -index a0e36ed082..0d9aaa1f7c 100644 ---- a/src/northbridge/intel/haswell/native_raminit/reg_structs.h -+++ b/src/northbridge/intel/haswell/native_raminit/reg_structs.h -@@ -294,6 +294,18 @@ union ddr_cke_ctl_controls_reg { - 	uint32_t raw; - }; -  -+union ddr_scramble_reg { -+	struct __packed { -+		uint32_t scram_en    :  1; // Bits  0:0 -+		uint32_t scram_key   : 16; // Bits 16:1 -+		uint32_t clk_gate_ab :  2; // Bits 18:17 -+		uint32_t clk_gate_c  :  2; // Bits 20:19 -+		uint32_t en_dbi_ab   :  1; // Bits 21:21 -+		uint32_t             : 10; // Bits 31:17 -+	}; -+	uint32_t raw; -+}; -+ - union ddr_scram_misc_control_reg { - 	struct __packed { - 		uint32_t wl_wake_cycles       :  2; // Bits  1:0 -diff --git a/src/northbridge/intel/haswell/registers/mchbar.h b/src/northbridge/intel/haswell/registers/mchbar.h -index 7c0b5a49de..49a215aa71 100644 ---- a/src/northbridge/intel/haswell/registers/mchbar.h -+++ b/src/northbridge/intel/haswell/registers/mchbar.h -@@ -20,6 +20,7 @@ -  - #define DDR_DATA_TRAIN_FEEDBACK(ch, byte)	_DDRIO_C_R_B(0x0054, ch, 0, byte) -  -+#define DQ_CONTROL_1(ch, byte)			_DDRIO_C_R_B(0x0060, ch, 0, byte) - #define DQ_CONTROL_2(ch, byte)			_DDRIO_C_R_B(0x0064, ch, 0, byte) - #define DDR_DATA_OFFSET_TRAIN_ch_b(ch, byte)	_DDRIO_C_R_B(0x0070, ch, 0, byte) - #define DQ_CONTROL_0(ch, byte)			_DDRIO_C_R_B(0x0074, ch, 0, byte) -@@ -147,6 +148,8 @@ - #define QCLK_ch_LDAT_SDAT(ch)			_MCMAIN_C(0x42d4, ch) - #define QCLK_ch_LDAT_DATA_IN_x(ch, x)		_MCMAIN_C_X(0x42dc, ch, x) /* x in 0 .. 1 */ -  -+#define PM_THRT_CKE_MIN_ch(ch)			_MCMAIN_C(0x4328, ch) -+ - #define REUT_GLOBAL_CTL				0x4800 - #define REUT_GLOBAL_ERR				0x4804 -  -@@ -175,6 +178,8 @@ -  - #define MCSCHEDS_DFT_MISC	0x4c30 -  -+#define PM_PDWN_CONFIG		0x4cb0 -+ - #define REUT_ERR_DATA_STATUS	0x4ce0 -  - #define REUT_MISC_CKE_CTRL	0x4d90 -@@ -186,8 +191,10 @@ - #define MAD_CHNL		0x5000 /* Address Decoder Channel Configuration */ - #define MAD_DIMM(ch)		(0x5004 + (ch) * 4) - #define MAD_ZR			0x5014 -+#define MCDECS_CBIT		0x501c - #define MC_INIT_STATE_G		0x5030 - #define MRC_REVISION		0x5034 /* MRC Revision */ -+#define PM_SREF_CONFIG		0x5060 -  - #define RCOMP_TIMER		0x5084 -  ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0016-Haswell-NRI-Implement-fast-boot-path.patch b/config/coreboot/haswell/patches/0016-Haswell-NRI-Implement-fast-boot-path.patch deleted file mode 100644 index c2fd8b60..00000000 --- a/config/coreboot/haswell/patches/0016-Haswell-NRI-Implement-fast-boot-path.patch +++ /dev/null @@ -1,722 +0,0 @@ -From c7d6a901edf648f0f02dd2053337bcf3a319e49b Mon Sep 17 00:00:00 2001 -From: Angel Pons <th3fanbus@gmail.com> -Date: Sat, 13 Apr 2024 01:16:30 +0200 -Subject: [PATCH 16/20] Haswell NRI: Implement fast boot path - -When the memory configuration hasn't changed, there is no need to do -full memory training. Instead, boot firmware can use saved training -data to reinitialise the memory controller and memory. - -Unlike native RAM init for other platforms, Haswell does not save the -main structure (the "mighty ctrl" struct) to flash. Instead, separate -structures define the data to be saved, which can be smaller than the -main structure. - -This makes S3 suspend and resume work: RAM contents MUST be preserved -for a S3 resume to succeed, but RAM training destroys RAM contents. - -Change-Id: I06f6cd39ceecdca104fae89159f28e85cf7ff4e6 -Signed-off-by: Angel Pons <th3fanbus@gmail.com> ---- - .../intel/haswell/native_raminit/Makefile.mk  |   1 + - .../haswell/native_raminit/activate_mc.c      |  17 + - .../intel/haswell/native_raminit/ddr3.c       |  41 ++ - .../haswell/native_raminit/raminit_main.c     |  34 +- - .../haswell/native_raminit/raminit_native.c   |  30 +- - .../haswell/native_raminit/raminit_native.h   |  18 + - .../haswell/native_raminit/save_restore.c     | 387 ++++++++++++++++++ - 7 files changed, 504 insertions(+), 24 deletions(-) - create mode 100644 src/northbridge/intel/haswell/native_raminit/save_restore.c - -diff --git a/src/northbridge/intel/haswell/native_raminit/Makefile.mk b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -index d97da72890..8fdd17c542 100644 ---- a/src/northbridge/intel/haswell/native_raminit/Makefile.mk -+++ b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -@@ -13,6 +13,7 @@ romstage-y += raminit_main.c - romstage-y += raminit_native.c - romstage-y += ranges.c - romstage-y += reut.c -+romstage-y += save_restore.c - romstage-y += setup_wdb.c - romstage-y += spd_bitmunching.c - romstage-y += testing_io.c -diff --git a/src/northbridge/intel/haswell/native_raminit/activate_mc.c b/src/northbridge/intel/haswell/native_raminit/activate_mc.c -index 78a7ad27ef..0b3eb917da 100644 ---- a/src/northbridge/intel/haswell/native_raminit/activate_mc.c -+++ b/src/northbridge/intel/haswell/native_raminit/activate_mc.c -@@ -333,6 +333,23 @@ enum raminit_status activate_mc(struct sysinfo *ctrl) - 	return RAMINIT_STATUS_SUCCESS; - } -  -+enum raminit_status normal_state(struct sysinfo *ctrl) -+{ -+	/* Enable periodic COMP */ -+	mchbar_write32(M_COMP, (union pcu_comp_reg) { -+		.comp_interval = COMP_INT, -+	}.raw); -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		/* Set MC to normal mode and clean the ODT and CKE */ -+		mchbar_write32(REUT_ch_SEQ_CFG(channel), REUT_MODE_NOP << 12); -+	} -+	power_down_config(ctrl); -+	return RAMINIT_STATUS_SUCCESS; -+} -+ - static void mc_lockdown(void) - { - 	/* Lock memory controller registers */ -diff --git a/src/northbridge/intel/haswell/native_raminit/ddr3.c b/src/northbridge/intel/haswell/native_raminit/ddr3.c -index 6ddb11488b..9b6368edb1 100644 ---- a/src/northbridge/intel/haswell/native_raminit/ddr3.c -+++ b/src/northbridge/intel/haswell/native_raminit/ddr3.c -@@ -2,6 +2,7 @@ -  - #include <assert.h> - #include <console/console.h> -+#include <delay.h> - #include <northbridge/intel/haswell/haswell.h> - #include <types.h> -  -@@ -215,3 +216,43 @@ enum raminit_status ddr3_jedec_init(struct sysinfo *ctrl) - 	ddr3_program_mr0(ctrl, 1); - 	return reut_issue_zq(ctrl, ctrl->chanmap, ZQ_INIT); - } -+ -+enum raminit_status exit_selfrefresh(struct sysinfo *ctrl) -+{ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		/* Fields in ctrl aren't populated on a warm boot */ -+		union ddr_data_control_0_reg data_control_0 = { -+			.raw = mchbar_read32(DQ_CONTROL_0(channel, 0)), -+		}; -+		data_control_0.read_rf_rd = 1; -+		for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { -+			if (!rank_in_ch(ctrl, rank, channel)) -+				continue; -+ -+			data_control_0.read_rf_rank = rank; -+			mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), data_control_0.raw); -+		} -+	} -+ -+	/* Time needed to stabilize the DCLK (~6 us) */ -+	udelay(6); -+ -+	/* Pull the DIMMs out of self refresh by asserting CKE high */ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		const union reut_misc_cke_ctrl_reg reut_misc_cke_ctrl = { -+			.cke_on = ctrl->rankmap[channel], -+		}; -+		mchbar_write32(REUT_ch_MISC_CKE_CTRL(channel), reut_misc_cke_ctrl.raw); -+	} -+	mchbar_write32(REUT_MISC_ODT_CTRL, 0); -+ -+	const enum raminit_status status = reut_issue_zq(ctrl, ctrl->chanmap, ZQ_LONG); -+	if (status) { -+		/* ZQCL errors don't seem to be a fatal problem here */ -+		printk(BIOS_ERR, "ZQ Long failed during S3 resume or warm reset flow\n"); -+	} -+	return RAMINIT_STATUS_SUCCESS; -+} -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_main.c b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -index 3a65fb01fb..056dde1adc 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_main.c -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -@@ -64,6 +64,22 @@ static const struct task_entry cold_boot[] = { - 	{ train_read_mpr,                                         true, "RDMPRT",     }, - 	{ train_jedec_write_leveling,                             true, "JWRL",       }, - 	{ activate_mc,                                            true, "ACTIVATE",   }, -+	{ save_training_values,                                   true, "SAVE_TRAIN", }, -+	{ save_non_training,                                      true, "SAVE_NONT",  }, -+	{ raminit_done,                                           true, "RAMINITEND", }, -+}; -+ -+static const struct task_entry fast_boot[] = { -+	{ collect_spd_info,                                       true, "PROCSPD",    }, -+	{ restore_non_training,                                   true, "RST_NONT",   }, -+	{ initialise_mpll,                                        true, "INITMPLL",   }, -+	{ configure_mc,                                           true, "CONFMC",     }, -+	{ configure_memory_map,                                   true, "MEMMAP",     }, -+	{ do_jedec_init,                                          true, "JEDECINIT",  }, -+	{ pre_training,                                           true, "PRETRAIN",   }, -+	{ restore_training_values,                                true, "RST_TRAIN",  }, -+	{ exit_selfrefresh,                                       true, "EXIT_SR",    }, -+	{ normal_state,                                           true, "NORMALMODE", }, - 	{ raminit_done,                                           true, "RAMINITEND", }, - }; -  -@@ -102,11 +118,11 @@ static void initialize_ctrl(struct sysinfo *ctrl) - 	ctrl->bootmode = bootmode; - } -  --static enum raminit_status try_raminit(struct sysinfo *ctrl) -+static enum raminit_status try_raminit( -+	struct sysinfo *ctrl, -+	const struct task_entry *const schedule, -+	const size_t length) - { --	const struct task_entry *const schedule = cold_boot; --	const size_t length = ARRAY_SIZE(cold_boot); -- - 	enum raminit_status status = RAMINIT_STATUS_UNSPECIFIED_ERROR; -  - 	for (size_t i = 0; i < length; i++) { -@@ -140,8 +156,16 @@ void raminit_main(const enum raminit_boot_mode bootmode) - 	mighty_ctrl.bootmode = bootmode; - 	initialize_ctrl(&mighty_ctrl); -  -+	enum raminit_status status = RAMINIT_STATUS_UNSPECIFIED_ERROR; -+ -+	if (bootmode != BOOTMODE_COLD) { -+		status = try_raminit(&mighty_ctrl, fast_boot, ARRAY_SIZE(fast_boot)); -+		if (status == RAMINIT_STATUS_SUCCESS) -+			return; -+	} -+ - 	/** TODO: Try more than once **/ --	enum raminit_status status = try_raminit(&mighty_ctrl); -+	status = try_raminit(&mighty_ctrl, cold_boot, ARRAY_SIZE(cold_boot)); -  - 	if (status != RAMINIT_STATUS_SUCCESS) - 		die("Memory initialization was met with utmost failure and misery\n"); -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.c b/src/northbridge/intel/haswell/native_raminit/raminit_native.c -index 5f7ceec222..3ad8ce29e7 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_native.c -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.c -@@ -54,23 +54,17 @@ static bool early_init_native(enum raminit_boot_mode bootmode) - 	return cpu_replaced; - } -  --#define MRC_CACHE_VERSION 1 -- --struct mrc_data { --	const void *buffer; --	size_t buffer_len; --}; -- --static void save_mrc_data(struct mrc_data *md) -+static void save_mrc_data(void) - { --	mrc_cache_stash_data(MRC_TRAINING_DATA, MRC_CACHE_VERSION, md->buffer, md->buffer_len); -+	mrc_cache_stash_data(MRC_TRAINING_DATA, reg_frame_rev(), -+			     reg_frame_ptr(), reg_frame_size()); - } -  - static struct mrc_data prepare_mrc_cache(void) - { - 	struct mrc_data md = {0}; - 	md.buffer = mrc_cache_current_mmap_leak(MRC_TRAINING_DATA, --						MRC_CACHE_VERSION, -+						reg_frame_rev(), - 						&md.buffer_len); - 	return md; - } -@@ -94,14 +88,15 @@ static void raminit_reset(void) - } -  - static enum raminit_boot_mode do_actual_raminit( --	struct mrc_data *md, - 	const bool s3resume, - 	const bool cpu_replaced, - 	const enum raminit_boot_mode orig_bootmode) - { -+	struct mrc_data md = prepare_mrc_cache(); -+ - 	enum raminit_boot_mode bootmode = orig_bootmode; -  --	bool save_data_valid = md->buffer && md->buffer_len == USHRT_MAX; /** TODO: sizeof() **/ -+	bool save_data_valid = md.buffer && md.buffer_len == reg_frame_size(); -  - 	if (s3resume) { - 		if (bootmode == BOOTMODE_COLD) { -@@ -154,7 +149,7 @@ static enum raminit_boot_mode do_actual_raminit( - 	assert(save_data_valid != (bootmode == BOOTMODE_COLD)); - 	if (save_data_valid) { - 		printk(BIOS_INFO, "Using cached memory parameters\n"); --		die("RAMINIT: Fast boot is not yet implemented\n"); -+		memcpy(reg_frame_ptr(), md.buffer, reg_frame_size()); - 	} - 	printk(RAM_DEBUG, "Initial bootmode: %s\n", bm_names[orig_bootmode]); - 	printk(RAM_DEBUG, "Current bootmode: %s\n", bm_names[bootmode]); -@@ -181,10 +176,8 @@ void perform_raminit(const int s3resume) - 	wait_txt_clear(); - 	wrmsr(0x2e6, (msr_t) {.lo = 0, .hi = 0}); -  --	struct mrc_data md = prepare_mrc_cache(); -- - 	const enum raminit_boot_mode bootmode = --			do_actual_raminit(&md, s3resume, cpu_replaced, orig_bootmode); -+			do_actual_raminit(s3resume, cpu_replaced, orig_bootmode); -  - 	/** TODO: report_memory_config **/ -  -@@ -212,9 +205,8 @@ void perform_raminit(const int s3resume) - 	} -  - 	/* Save training data on non-S3 resumes */ --	/** TODO: Enable this once training data is populated **/ --	if (0 && !s3resume) --		save_mrc_data(&md); -+	if (!s3resume) -+		save_mrc_data(); -  - 	/** TODO: setup_sdram_meminfo **/ - } -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -index 9bab57b518..0750904aec 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -@@ -169,6 +169,8 @@ enum regfile_mode { - 	REG_FILE_USE_CURRENT,	/* Used when changing parameters after the test */ - }; -  -+struct register_save_frame; -+ - struct wdb_pat { - 	uint32_t start_ptr;	/* Starting pointer in WDB */ - 	uint32_t stop_ptr;	/* Stopping pointer in WDB */ -@@ -219,6 +221,7 @@ enum raminit_status { - 	RAMINIT_STATUS_RCVEN_FAILURE, - 	RAMINIT_STATUS_RMPR_FAILURE, - 	RAMINIT_STATUS_JWRL_FAILURE, -+	RAMINIT_STATUS_INVALID_CACHE, - 	RAMINIT_STATUS_UNSPECIFIED_ERROR, /** TODO: Deprecated in favor of specific values **/ - }; -  -@@ -228,6 +231,11 @@ enum generic_stepping { - 	STEPPING_C0 = 3, - }; -  -+struct mrc_data { -+	const void *buffer; -+	size_t buffer_len; -+}; -+ - struct raminit_dimm_info { - 	spd_raw_data raw_spd; - 	struct dimm_attr_ddr3_st data; -@@ -447,12 +455,22 @@ enum raminit_status do_jedec_init(struct sysinfo *ctrl); - enum raminit_status train_receive_enable(struct sysinfo *ctrl); - enum raminit_status train_read_mpr(struct sysinfo *ctrl); - enum raminit_status train_jedec_write_leveling(struct sysinfo *ctrl); -+enum raminit_status save_training_values(struct sysinfo *ctrl); -+enum raminit_status restore_training_values(struct sysinfo *ctrl); -+enum raminit_status save_non_training(struct sysinfo *ctrl); -+enum raminit_status restore_non_training(struct sysinfo *ctrl); -+enum raminit_status exit_selfrefresh(struct sysinfo *ctrl); -+enum raminit_status normal_state(struct sysinfo *ctrl); - enum raminit_status activate_mc(struct sysinfo *ctrl); - enum raminit_status raminit_done(struct sysinfo *ctrl); -  - void configure_timings(struct sysinfo *ctrl); - void configure_refresh(struct sysinfo *ctrl); -  -+struct register_save_frame *reg_frame_ptr(void); -+size_t reg_frame_size(void); -+uint32_t reg_frame_rev(void); -+ - uint32_t get_tCKE(uint32_t mem_clock_mhz, bool lpddr); - uint32_t get_tXPDLL(uint32_t mem_clock_mhz); - uint32_t get_tAONPD(uint32_t mem_clock_mhz); -diff --git a/src/northbridge/intel/haswell/native_raminit/save_restore.c b/src/northbridge/intel/haswell/native_raminit/save_restore.c -new file mode 100644 -index 0000000000..f1f50e3ff8 ---- /dev/null -+++ b/src/northbridge/intel/haswell/native_raminit/save_restore.c -@@ -0,0 +1,387 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#include <assert.h> -+#include <console/console.h> -+#include <northbridge/intel/haswell/haswell.h> -+#include <types.h> -+ -+#include "raminit_native.h" -+ -+uint32_t reg_frame_rev(void) -+{ -+	/* -+	 * Equivalent to MRC_CACHE_REVISION, but hidden via abstraction. -+	 * The structures that get saved to flash are contained within -+	 * this translation unit, so changes outside this file shouldn't -+	 * require invalidating the cache. -+	 */ -+	return 1; -+} -+ -+struct register_save { -+	uint16_t lower; -+	uint16_t upper; -+}; -+ -+/** TODO: Haswell DDRIO aliases writes: 0x80 .. 0xff => 0x00 .. 0x7f **/ -+static const struct register_save ddrio_per_byte_list[] = { -+	{0x0000, 0x003c}, /* 16 registers */ -+//	{0x0048, 0x0084}, /* 16 registers */	/** TODO: BDW support **/ -+	{0x0048, 0x004c}, /*  2 registers */ -+	{0x005c, 0x0078}, /*  8 registers */ -+}; -+#define DDRIO_PER_BYTE_REGISTER_COUNT	(16 + 2 + 8) -+ -+static const struct register_save ddrio_per_ch_list[] = { -+	/* CKE */ -+	{0x1204, 0x1208}, /* 2 registers */ -+	{0x1214, 0x121c}, /* 3 registers */ -+	/* CMD North */ -+	{0x1404, 0x140c}, /* 3 registers */ -+	/* CLK */ -+	{0x1808, 0x1810}, /* 3 registers */ -+	/* CMD South */ -+	{0x1a04, 0x1a0c}, /* 3 registers */ -+	/* CTL */ -+	{0x1c14, 0x1c1c}, /* 3 registers */ -+}; -+#define DDRIO_PER_CH_REGISTER_COUNT	(2 + 3 * 5) -+ -+static const struct register_save ddrio_common_list[] = { -+	{0x2000, 0x2008}, /* 3 registers */ -+	{0x3a14, 0x3a1c}, /* 3 registers */ -+	{0x3a24, 0x3a24}, /* 1 registers */ -+}; -+ -+#define DDRIO_COMMON_REGISTER_COUNT	(3 + 3 + 1) -+ -+static const struct register_save mcmain_per_ch_list[] = { -+	{0x4000, 0x4014}, /* 6 registers */ -+	{0x4024, 0x4028}, /* 2 registers */ -+	{0x40d0, 0x40d0}, /* 1 registers */ -+	{0x4220, 0x4224}, /* 2 registers */ -+	{0x4294, 0x4294}, /* 1 registers */ -+	{0x429c, 0x42a0}, /* 2 registers */ -+	{0x42ec, 0x42fc}, /* 5 registers */ -+	{0x4328, 0x4328}, /* 1 registers */ -+	{0x438c, 0x4390}, /* 2 registers */ -+}; -+#define MCMAIN_PER_CH_REGISTER_COUNT	(6 + 2 + 1 + 2 + 1 + 2 + 5 + 1 + 2) -+ -+static const struct register_save misc_common_list[] = { -+	{0x5884, 0x5888}, /*  2 registers */ -+	{0x5890, 0x589c}, /*  4 registers */ -+	{0x58a4, 0x58a4}, /*  1 registers */ -+	{0x58d0, 0x58e4}, /*  6 registers */ -+	{0x5880, 0x5880}, /*  1 registers */ -+	{0x5000, 0x50dc}, /* 56 registers */ -+	{0x59b8, 0x59b8}  /*  1 registers */ -+}; -+#define MISC_COMMON_REGISTER_COUNT	(2 + 4 + 1 + 6 + 1 + 56 + 1) -+ -+struct save_params { -+	bool is_initialised; -+ -+	/* Memory base frequency, either 100 or 133 MHz */ -+	uint8_t base_freq; -+ -+	/* Multiplier */ -+	uint32_t multiplier; -+ -+	/* Memory clock in MHz */ -+	uint32_t mem_clock_mhz; -+ -+	/* Memory clock in femtoseconds */ -+	uint32_t mem_clock_fs; -+ -+	/* Quadrature clock in picoseconds */ -+	uint16_t qclkps; -+ -+	/* Bitfield of supported CAS latencies */ -+	uint16_t cas_supported; -+ -+	/* CPUID value */ -+	uint32_t cpu; -+ -+	/* Cached CPU stepping value */ -+	uint8_t stepping; -+ -+	uint16_t vdd_mv; -+ -+	union dimm_flags_ddr3_st flags; -+ -+	/* Except for tCK, everything is stored in DCLKs */ -+	uint32_t tCK; -+	uint32_t tAA; -+	uint32_t tWR; -+	uint32_t tRCD; -+	uint32_t tRRD; -+	uint32_t tRP; -+	uint32_t tRAS; -+	uint32_t tRC; -+	uint32_t tRFC; -+	uint32_t tWTR; -+	uint32_t tRTP; -+	uint32_t tFAW; -+	uint32_t tCWL; -+	uint32_t tCMD; -+ -+	uint32_t tREFI; -+	uint32_t tXP; -+ -+	uint8_t lpddr_cke_rank_map[NUM_CHANNELS]; -+ -+	struct raminit_dimm_info dimms[NUM_CHANNELS][NUM_SLOTS]; -+ -+	uint8_t chanmap; -+ -+	uint32_t channel_size_mb[NUM_CHANNELS]; -+ -+	/* DIMMs per channel */ -+	uint8_t dpc[NUM_CHANNELS]; -+ -+	uint8_t rankmap[NUM_CHANNELS]; -+ -+	/* Whether a rank is mirrored or not (only rank 1 of each DIMM can be) */ -+	uint8_t rank_mirrored[NUM_CHANNELS]; -+ -+	/* -+	 * FIXME: LPDDR support is incomplete. The largest chunks are missing, -+	 * but some LPDDR-specific variations in algorithms have been handled. -+	 * LPDDR-specific functions have stubs which will halt upon execution. -+	 */ -+	bool lpddr; -+ -+	uint8_t lanes; -+ -+	/* FIXME: ECC support missing */ -+	bool is_ecc; -+}; -+ -+struct register_save_frame { -+	uint32_t ddrio_per_byte[NUM_CHANNELS][NUM_LANES][DDRIO_PER_BYTE_REGISTER_COUNT]; -+	uint32_t ddrio_per_ch[NUM_CHANNELS][DDRIO_PER_CH_REGISTER_COUNT]; -+	uint32_t ddrio_common[DDRIO_COMMON_REGISTER_COUNT]; -+	uint32_t mcmain_per_ch[NUM_CHANNELS][MCMAIN_PER_CH_REGISTER_COUNT]; -+	uint32_t misc_common[MISC_COMMON_REGISTER_COUNT]; -+	struct save_params params; -+}; -+ -+struct register_save_frame *reg_frame_ptr(void) -+{ -+	/* The chonky register save frame struct, used for fast boot and S3 resume */ -+	static struct register_save_frame register_frame = { 0 }; -+	return ®ister_frame; -+} -+ -+size_t reg_frame_size(void) -+{ -+	return sizeof(struct register_save_frame); -+} -+ -+typedef void (*reg_func_t)(const uint16_t offset, uint32_t *const value); -+ -+static void save_value(const uint16_t offset, uint32_t *const value) -+{ -+	*value = mchbar_read32(offset); -+} -+ -+static void restore_value(const uint16_t offset, uint32_t *const value) -+{ -+	mchbar_write32(offset, *value); -+} -+ -+static void save_restore( -+	uint32_t *reg_frame, -+	const uint16_t g_offset, -+	const struct register_save *reg_save_list, -+	const size_t reg_save_length, -+	reg_func_t handle_reg) -+{ -+	for (size_t i = 0; i < reg_save_length; i++) { -+		const struct register_save *entry = ®_save_list[i]; -+		for (uint16_t offset = entry->lower; offset <= entry->upper; offset += 4) { -+			handle_reg(offset + g_offset, reg_frame++); -+		} -+	} -+} -+ -+static void save_restore_all(struct register_save_frame *reg_frame, reg_func_t handle_reg) -+{ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		for (uint8_t byte = 0; byte < NUM_LANES; byte++) { -+			const uint16_t g_offset = _DDRIO_C_R_B(0, channel, 0, byte); -+			save_restore( -+				reg_frame->ddrio_per_byte[channel][byte], -+				g_offset, -+				ddrio_per_byte_list, -+				ARRAY_SIZE(ddrio_per_byte_list), -+				handle_reg); -+		} -+	} -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		const uint16_t g_offset = _DDRIO_C_R_B(0, channel, 0, 0); -+		save_restore( -+			reg_frame->ddrio_per_ch[channel], -+			g_offset, -+			ddrio_per_ch_list, -+			ARRAY_SIZE(ddrio_per_ch_list), -+			handle_reg); -+	} -+	save_restore( -+		reg_frame->ddrio_common, -+		0, -+		ddrio_common_list, -+		ARRAY_SIZE(ddrio_common_list), -+		handle_reg); -+ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		const uint16_t g_offset = _MCMAIN_C(0, channel); -+		save_restore( -+			reg_frame->mcmain_per_ch[channel], -+			g_offset, -+			mcmain_per_ch_list, -+			ARRAY_SIZE(mcmain_per_ch_list), -+			handle_reg); -+	} -+	save_restore( -+		reg_frame->misc_common, -+		0, -+		misc_common_list, -+		ARRAY_SIZE(misc_common_list), -+		handle_reg); -+} -+ -+enum raminit_status save_training_values(struct sysinfo *ctrl) -+{ -+	save_restore_all(reg_frame_ptr(), save_value); -+	return RAMINIT_STATUS_SUCCESS; -+} -+ -+enum raminit_status restore_training_values(struct sysinfo *ctrl) -+{ -+	save_restore_all(reg_frame_ptr(), restore_value); -+	return RAMINIT_STATUS_SUCCESS; -+} -+ -+enum raminit_status save_non_training(struct sysinfo *ctrl) -+{ -+	struct register_save_frame *reg_frame = reg_frame_ptr(); -+	struct save_params *params = ®_frame->params; -+ -+	params->is_initialised = true; -+ -+	params->base_freq     = ctrl->base_freq; -+	params->multiplier    = ctrl->multiplier; -+	params->mem_clock_mhz = ctrl->mem_clock_mhz; -+	params->mem_clock_fs  = ctrl->mem_clock_fs; -+	params->qclkps        = ctrl->qclkps; -+	params->cas_supported = ctrl->cas_supported; -+	params->cpu           = ctrl->cpu; -+	params->stepping      = ctrl->stepping; -+	params->vdd_mv        = ctrl->vdd_mv; -+	params->flags         = ctrl->flags; -+ -+	params->tCK       = ctrl->tCK; -+	params->tAA       = ctrl->tAA; -+	params->tWR       = ctrl->tWR; -+	params->tRCD      = ctrl->tRCD; -+	params->tRRD      = ctrl->tRRD; -+	params->tRP       = ctrl->tRP; -+	params->tRAS      = ctrl->tRAS; -+	params->tRC       = ctrl->tRC; -+	params->tRFC      = ctrl->tRFC; -+	params->tWTR      = ctrl->tWTR; -+	params->tRTP      = ctrl->tRTP; -+	params->tFAW      = ctrl->tFAW; -+	params->tCWL      = ctrl->tCWL; -+	params->tCMD      = ctrl->tCMD; -+	params->tREFI     = ctrl->tREFI; -+	params->tXP       = ctrl->tXP; -+ -+	params->chanmap = ctrl->chanmap; -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		params->lpddr_cke_rank_map[channel] = ctrl->lpddr_cke_rank_map[channel]; -+		for (uint8_t slot = 0; slot < NUM_SLOTS; slot++) -+			params->dimms[channel][slot] = ctrl->dimms[channel][slot]; -+	} -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		params->dpc[channel] = ctrl->dpc[channel]; -+		params->rankmap[channel] = ctrl->rankmap[channel]; -+		params->rank_mirrored[channel] = ctrl->rank_mirrored[channel]; -+		params->channel_size_mb[channel] = ctrl->channel_size_mb[channel]; -+	} -+	params->lpddr = ctrl->lpddr; -+	params->lanes = ctrl->lanes; -+	params->is_ecc = ctrl->is_ecc; -+	return RAMINIT_STATUS_SUCCESS; -+} -+ -+#define RAMINIT_COMPARE(_s1, _s2) \ -+	((sizeof(_s1) == sizeof(_s2)) && !memcmp(_s1, _s2, sizeof(_s1))) -+ -+enum raminit_status restore_non_training(struct sysinfo *ctrl) -+{ -+	struct register_save_frame *reg_frame = reg_frame_ptr(); -+	struct save_params *params = ®_frame->params; -+ -+	if (!params->is_initialised) { -+		printk(BIOS_WARNING, "Cannot fast boot: saved data is invalid\n"); -+		return RAMINIT_STATUS_INVALID_CACHE; -+	} -+ -+	if (!RAMINIT_COMPARE(ctrl->dimms, params->dimms)) { -+		printk(BIOS_WARNING, "Cannot fast boot: DIMMs have changed\n"); -+		return RAMINIT_STATUS_INVALID_CACHE; -+	} -+ -+	if (ctrl->cpu != params->cpu) { -+		printk(BIOS_WARNING, "Cannot fast boot: CPU has changed\n"); -+		return RAMINIT_STATUS_INVALID_CACHE; -+	} -+ -+	ctrl->base_freq     = params->base_freq; -+	ctrl->multiplier    = params->multiplier; -+	ctrl->mem_clock_mhz = params->mem_clock_mhz; -+	ctrl->mem_clock_fs  = params->mem_clock_fs; -+	ctrl->qclkps        = params->qclkps; -+	ctrl->cas_supported = params->cas_supported; -+	ctrl->cpu           = params->cpu; -+	ctrl->stepping      = params->stepping; -+	ctrl->vdd_mv        = params->vdd_mv; -+	ctrl->flags         = params->flags; -+ -+	ctrl->tCK       = params->tCK; -+	ctrl->tAA       = params->tAA; -+	ctrl->tWR       = params->tWR; -+	ctrl->tRCD      = params->tRCD; -+	ctrl->tRRD      = params->tRRD; -+	ctrl->tRP       = params->tRP; -+	ctrl->tRAS      = params->tRAS; -+	ctrl->tRC       = params->tRC; -+	ctrl->tRFC      = params->tRFC; -+	ctrl->tWTR      = params->tWTR; -+	ctrl->tRTP      = params->tRTP; -+	ctrl->tFAW      = params->tFAW; -+	ctrl->tCWL      = params->tCWL; -+	ctrl->tCMD      = params->tCMD; -+	ctrl->tREFI     = params->tREFI; -+	ctrl->tXP       = params->tXP; -+ -+	ctrl->chanmap = params->chanmap; -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		ctrl->lpddr_cke_rank_map[channel] = params->lpddr_cke_rank_map[channel]; -+		for (uint8_t slot = 0; slot < NUM_SLOTS; slot++) -+			ctrl->dimms[channel][slot] = params->dimms[channel][slot]; -+	} -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		ctrl->dpc[channel] = params->dpc[channel]; -+		ctrl->rankmap[channel] = params->rankmap[channel]; -+		ctrl->rank_mirrored[channel] = params->rank_mirrored[channel]; -+		ctrl->channel_size_mb[channel] = params->channel_size_mb[channel]; -+	} -+	ctrl->lpddr = params->lpddr; -+	ctrl->lanes = params->lanes; -+	ctrl->is_ecc = params->is_ecc; -+	return RAMINIT_STATUS_SUCCESS; -+} ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0017-haswell-NRI-Do-sense-amplifier-offset-training.patch b/config/coreboot/haswell/patches/0017-haswell-NRI-Do-sense-amplifier-offset-training.patch deleted file mode 100644 index 846fe9a3..00000000 --- a/config/coreboot/haswell/patches/0017-haswell-NRI-Do-sense-amplifier-offset-training.patch +++ /dev/null @@ -1,476 +0,0 @@ -From be58501141aa97aa544b670e566cd6cf6797c18e Mon Sep 17 00:00:00 2001 -From: Angel Pons <th3fanbus@gmail.com> -Date: Wed, 17 Apr 2024 13:20:32 +0200 -Subject: [PATCH 17/20] haswell NRI: Do sense amplifier offset training - -Quoting Wikipedia: - -  A sense amplifier is a circuit that is used to amplify and detect -  small signals in electronic systems. It is commonly used in memory -  circuits, such as dynamic random access memory (DRAM), to read and -  amplify the weak signals stored in memory cells. - -In this case, we're calibrating the sense amplifiers in the memory -controller. This training procedure uses a magic "sense amp offset -cancel" mode of the DDRIO to observe the sampled logic levels, and -sweeps Vref to find the low-high transition for each bit lane. The -procedure consists of two stages: the first stage centers per-byte -Vref (to ensure per-bit Vref offsets are as small as possible) and -the second stage centers per-bit Vref. - -Because this procedure uses the "sense amp offset cancel" mode, it -does not rely on DRAM being trained. It is assumed that the memory -controller simply makes sense amp output levels observable via the -`DDR_DATA_TRAIN_FEEDBACK` register and that the memory bus is idle -during this training step (so the lane voltage is Vdd / 2). - -Note: This procedure will need to be adapted for Broadwell because -it has per-rank per-bit RxVref registers, whereas Haswell only has -a single per-bit RxVref register for all ranks. - -Change-Id: Ia07db68763f90e9701c8a376e01279ada8dbbe07 -Signed-off-by: Angel Pons <th3fanbus@gmail.com> ---- - .../intel/haswell/native_raminit/Makefile.mk  |   1 + - .../haswell/native_raminit/raminit_main.c     |   1 + - .../haswell/native_raminit/raminit_native.h   |  12 + - .../native_raminit/train_sense_amp_offset.c   | 341 ++++++++++++++++++ - .../intel/haswell/registers/mchbar.h          |   2 + - 5 files changed, 357 insertions(+) - create mode 100644 src/northbridge/intel/haswell/native_raminit/train_sense_amp_offset.c - -diff --git a/src/northbridge/intel/haswell/native_raminit/Makefile.mk b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -index 8fdd17c542..4bd668a2d6 100644 ---- a/src/northbridge/intel/haswell/native_raminit/Makefile.mk -+++ b/src/northbridge/intel/haswell/native_raminit/Makefile.mk -@@ -21,3 +21,4 @@ romstage-y += timings_refresh.c - romstage-y += train_jedec_write_leveling.c - romstage-y += train_read_mpr.c - romstage-y += train_receive_enable.c -+romstage-y += train_sense_amp_offset.c -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_main.c b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -index 056dde1adc..ce637e2d03 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_main.c -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_main.c -@@ -60,6 +60,7 @@ static const struct task_entry cold_boot[] = { - 	{ configure_memory_map,                                   true, "MEMMAP",     }, - 	{ do_jedec_init,                                          true, "JEDECINIT",  }, - 	{ pre_training,                                           true, "PRETRAIN",   }, -+	{ train_sense_amp_offset,                                 true, "SOT",        }, - 	{ train_receive_enable,                                   true, "RCVET",      }, - 	{ train_read_mpr,                                         true, "RDMPRT",     }, - 	{ train_jedec_write_leveling,                             true, "JWRL",       }, -diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -index 0750904aec..95ccd0a8b3 100644 ---- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h -+++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h -@@ -22,6 +22,8 @@ - #define NUM_LANES		9 - #define NUM_LANES_NO_ECC	8 -  -+#define NUM_BITS		8 -+ - #define COMP_INT		10 -  - /* Always use 12 legs for emphasis (not trained) */ -@@ -218,6 +220,7 @@ enum raminit_status { - 	RAMINIT_STATUS_MPLL_INIT_FAILURE, - 	RAMINIT_STATUS_POLL_TIMEOUT, - 	RAMINIT_STATUS_REUT_ERROR, -+	RAMINIT_STATUS_SAMP_OFFSET_FAILURE, - 	RAMINIT_STATUS_RCVEN_FAILURE, - 	RAMINIT_STATUS_RMPR_FAILURE, - 	RAMINIT_STATUS_JWRL_FAILURE, -@@ -243,6 +246,12 @@ struct raminit_dimm_info { - 	bool valid; - }; -  -+struct vref_margin { -+	uint8_t low; -+	uint8_t center; -+	uint8_t high; -+}; -+ - struct sysinfo { - 	enum raminit_boot_mode bootmode; - 	enum generic_stepping stepping; -@@ -330,6 +339,8 @@ struct sysinfo { - 	uint8_t rxdqsn[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; - 	int8_t  rxvref[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES]; -  -+	struct vref_margin rxdqvrefpb[NUM_CHANNELS][NUM_SLOTRANKS][NUM_LANES][NUM_BITS]; -+ - 	uint8_t clk_pi_code[NUM_CHANNELS][NUM_SLOTRANKS]; - 	uint8_t ctl_pi_code[NUM_CHANNELS][NUM_SLOTRANKS]; - 	uint8_t cke_pi_code[NUM_CHANNELS][NUM_SLOTRANKS]; -@@ -452,6 +463,7 @@ enum raminit_status convert_timings(struct sysinfo *ctrl); - enum raminit_status configure_mc(struct sysinfo *ctrl); - enum raminit_status configure_memory_map(struct sysinfo *ctrl); - enum raminit_status do_jedec_init(struct sysinfo *ctrl); -+enum raminit_status train_sense_amp_offset(struct sysinfo *ctrl); - enum raminit_status train_receive_enable(struct sysinfo *ctrl); - enum raminit_status train_read_mpr(struct sysinfo *ctrl); - enum raminit_status train_jedec_write_leveling(struct sysinfo *ctrl); -diff --git a/src/northbridge/intel/haswell/native_raminit/train_sense_amp_offset.c b/src/northbridge/intel/haswell/native_raminit/train_sense_amp_offset.c -new file mode 100644 -index 0000000000..d4f199fefb ---- /dev/null -+++ b/src/northbridge/intel/haswell/native_raminit/train_sense_amp_offset.c -@@ -0,0 +1,341 @@ -+/* SPDX-License-Identifier: GPL-2.0-or-later */ -+ -+#include <assert.h> -+#include <commonlib/bsd/clamp.h> -+#include <console/console.h> -+#include <delay.h> -+#include <lib.h> -+#include <types.h> -+ -+#include "raminit_native.h" -+ -+#define VREF_OFFSET_PLOT	RAM_DEBUG -+#define SAMP_OFFSET_PLOT	RAM_DEBUG -+ -+struct vref_train_data { -+	int8_t best_sum; -+	int8_t best_vref; -+	int8_t sum_bits; -+	uint8_t high_mask; -+	uint8_t low_mask; -+}; -+ -+static enum raminit_status train_vref_offset(struct sysinfo *ctrl) -+{ -+	const int8_t vref_start = -15; -+	const int8_t vref_stop  = 15; -+	const struct vref_train_data initial_vref_values = { -+		.best_sum  = -NUM_LANES, -+		.best_vref = 0, -+		.high_mask = 0, -+		.low_mask  = 0xff, -+	}; -+	struct vref_train_data vref_data[NUM_CHANNELS][NUM_LANES]; -+ -+	printk(VREF_OFFSET_PLOT, "Plot of sum_bits across Vref settings\nChannel"); -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		printk(VREF_OFFSET_PLOT, "\t%u\t\t", channel); -+	} -+ -+	printk(VREF_OFFSET_PLOT, "\nByte"); -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		printk(VREF_OFFSET_PLOT, "\t"); -+		for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+			printk(VREF_OFFSET_PLOT, "%u ", byte); -+			vref_data[channel][byte] = initial_vref_values; -+			union ddr_data_control_2_reg data_control_2 = { -+				.raw = ctrl->dq_control_2[channel][byte], -+			}; -+			data_control_2.force_bias_on = 1; -+			data_control_2.force_rx_on   = 1; -+			mchbar_write32(DQ_CONTROL_2(channel, byte), data_control_2.raw); -+		} -+	} -+ -+	/* Sweep through Vref settings and find point SampOffset of +/- 7 passes */ -+	printk(VREF_OFFSET_PLOT, "\n1/2 Vref"); -+	for (int8_t vref = vref_start; vref <= vref_stop; vref++) { -+		printk(VREF_OFFSET_PLOT, "\n% 3d", vref); -+ -+		/* -+		 * To perform this test, enable offset cancel mode and enable ODT. -+		 * Check results and update variables. Ideal result is all zeroes. -+		 * Clear offset cancel mode at end of test to write RX_OFFSET_VDQ. -+		 */ -+		change_1d_margin_multicast(ctrl, RdV, vref, 0, false, REG_FILE_USE_RANK); -+ -+		/* Program settings for Vref and SampOffset = 7 (8 + 7) */ -+		mchbar_write32(DDR_DATA_RX_OFFSET_VDQ, 0xffffffff); -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!does_ch_exist(ctrl, channel)) -+				continue; -+ -+			/* Propagate delay values (without a read command) */ -+			union ddr_data_control_0_reg data_control_0 = { -+				.raw = ctrl->dq_control_0[channel], -+			}; -+			data_control_0.read_rf_rd      = 1; -+			data_control_0.read_rf_wr      = 0; -+			data_control_0.read_rf_rank    = 0; -+			data_control_0.force_odt_on    = 1; -+			data_control_0.samp_train_mode = 1; -+			mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), data_control_0.raw); -+			udelay(1); -+			data_control_0.samp_train_mode = 0; -+			mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), data_control_0.raw); -+			for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+				const uint8_t feedback = get_data_train_feedback(channel, byte); -+				struct vref_train_data *curr_data = &vref_data[channel][byte]; -+				curr_data->low_mask &= feedback; -+				curr_data->sum_bits = -popcnt(feedback); -+			} -+		} -+ -+		/* Program settings for Vref and SampOffset = -7 (8 - 7) */ -+		mchbar_write32(DDR_DATA_RX_OFFSET_VDQ, 0x11111111); -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!does_ch_exist(ctrl, channel)) -+				continue; -+ -+			/* Propagate delay values (without a read command) */ -+			union ddr_data_control_0_reg data_control_0 = { -+				.raw = ctrl->dq_control_0[channel], -+			}; -+			data_control_0.read_rf_rd      = 1; -+			data_control_0.read_rf_wr      = 0; -+			data_control_0.read_rf_rank    = 0; -+			data_control_0.force_odt_on    = 1; -+			data_control_0.samp_train_mode = 1; -+			mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), data_control_0.raw); -+			udelay(1); -+			data_control_0.samp_train_mode = 0; -+			mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), data_control_0.raw); -+			printk(VREF_OFFSET_PLOT, "\t"); -+			for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+				const uint8_t feedback = get_data_train_feedback(channel, byte); -+				struct vref_train_data *curr_data = &vref_data[channel][byte]; -+				curr_data->high_mask |= feedback; -+				curr_data->sum_bits += popcnt(feedback); -+				printk(VREF_OFFSET_PLOT, "%d ", curr_data->sum_bits); -+				if (curr_data->sum_bits > curr_data->best_sum) { -+					curr_data->best_sum  = curr_data->sum_bits; -+					curr_data->best_vref = vref; -+					ctrl->rxvref[channel][0][byte] = vref; -+				} else if (curr_data->sum_bits == curr_data->best_sum) { -+					curr_data->best_vref = vref; -+				} -+			} -+		} -+	} -+	printk(BIOS_DEBUG, "\n\nHi-Lo (XOR):"); -+	enum raminit_status status = RAMINIT_STATUS_SUCCESS; -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		printk(BIOS_DEBUG, "\n  C%u:", channel); -+		for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+			struct vref_train_data *const curr_data = &vref_data[channel][byte]; -+			const uint8_t bit_xor = curr_data->high_mask ^ curr_data->low_mask; -+			printk(BIOS_DEBUG, "\t0x%02x", bit_xor); -+			if (bit_xor == 0xff) -+				continue; -+ -+			/* Report an error if any bit did not change */ -+			status = RAMINIT_STATUS_SAMP_OFFSET_FAILURE; -+		} -+	} -+	if (status) -+		printk(BIOS_ERR, "\nUnexpected bit error in Vref offset training\n"); -+ -+	printk(BIOS_DEBUG, "\n\nRdVref:"); -+	change_1d_margin_multicast(ctrl, RdV, 0, 0, false, REG_FILE_USE_RANK); -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		printk(BIOS_DEBUG, "\n  C%u:", channel); -+		for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+			struct vref_train_data *const curr_data = &vref_data[channel][byte]; -+			const int8_t vref_width = -+				curr_data->best_vref - ctrl->rxvref[channel][0][byte]; -+ -+			/* -+			 * Step size for Rx Vref in DATA_OFFSET_TRAIN is about 3.9 mV -+			 * whereas Rx Vref step size in RX_TRAIN_RANK is about 7.8 mV -+			 */ -+			int8_t vref = ctrl->rxvref[channel][0][byte] + vref_width / 2; -+			if (vref < 0) -+				vref--; -+			else -+				vref++; -+ -+			for (uint8_t rank = 0; rank < NUM_SLOTRANKS; rank++) { -+				if (!rank_in_ch(ctrl, rank, channel)) -+					continue; -+ -+				ctrl->rxvref[channel][rank][byte] = vref / 2; -+				update_rxt(ctrl, channel, rank, byte, RXT_RESTORE, 0); -+			} -+			printk(BIOS_DEBUG, "\t% 4d", ctrl->rxvref[channel][0][byte]); -+		} -+	} -+	printk(BIOS_DEBUG, "\n\n"); -+	return status; -+} -+ -+/** -+ * LPDDR has an additional bit for DQS per each byte. -+ * -+ * TODO: The DQS value must be written into Data Control 2. -+ */ -+#define NUM_OFFSET_TRAIN_BITS	(NUM_BITS + 1) -+ -+#define PLOT_CH_SPACE		"  " -+ -+struct samp_train_data { -+	uint8_t first_zero; -+	uint8_t last_one; -+}; -+ -+static void train_samp_offset(struct sysinfo *ctrl) -+{ -+	const uint8_t max_train_bits = ctrl->lpddr ? NUM_OFFSET_TRAIN_BITS : NUM_BITS; -+ -+	struct samp_train_data samp_data[NUM_CHANNELS][NUM_LANES][NUM_OFFSET_TRAIN_BITS] = {0}; -+ -+	printk(BIOS_DEBUG, "Channel "); -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		printk(BIOS_DEBUG, "%u ", channel); /* Same length as PLOT_CH_SPACE */ -+		for (uint8_t byte = 0; byte < ctrl->lanes; byte++) -+			printk(BIOS_DEBUG, "        %s ", ctrl->lpddr ? " " : ""); -+	} -+	printk(BIOS_DEBUG, "\nByte    "); -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		for (uint8_t byte = 0; byte < ctrl->lanes; byte++) -+			printk(BIOS_DEBUG, "%u       %s ", byte, ctrl->lpddr ? " " : ""); -+ -+		printk(BIOS_DEBUG, PLOT_CH_SPACE); -+	} -+	printk(SAMP_OFFSET_PLOT, "\nBits    "); -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		for (uint8_t byte = 0; byte < ctrl->lanes; byte++) -+			printk(SAMP_OFFSET_PLOT, "01234567%s ", ctrl->lpddr ? "S" : ""); -+ -+		printk(SAMP_OFFSET_PLOT, PLOT_CH_SPACE); -+	} -+	printk(SAMP_OFFSET_PLOT, "\n SAmp\n"); -+	for (uint8_t samp_offset = 1; samp_offset <= 15; samp_offset++) { -+		printk(SAMP_OFFSET_PLOT, "% 5d\t", samp_offset); -+ -+		uint32_t rx_offset_vdq = 0; -+		for (uint8_t bit = 0; bit < NUM_BITS; bit++) { -+			rx_offset_vdq += samp_offset << (4 * bit); -+		} -+		mchbar_write32(DDR_DATA_RX_OFFSET_VDQ, rx_offset_vdq); -+		for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+			if (!does_ch_exist(ctrl, channel)) -+				continue; -+ -+			/* Propagate delay values (without a read command) */ -+			union ddr_data_control_0_reg data_control_0 = { -+				.raw = ctrl->dq_control_0[channel], -+			}; -+			data_control_0.read_rf_rd      = 1; -+			data_control_0.read_rf_wr      = 0; -+			data_control_0.read_rf_rank    = 0; -+			data_control_0.force_odt_on    = 1; -+			data_control_0.samp_train_mode = 1; -+			mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), data_control_0.raw); -+			udelay(1); -+			for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+				const uint32_t feedback = -+					get_data_train_feedback(channel, byte); -+ -+				for (uint8_t bit = 0; bit < max_train_bits; bit++) { -+					struct samp_train_data *const curr_data = -+							&samp_data[channel][byte][bit]; -+					const bool result = feedback & BIT(bit); -+					if (result) { -+						curr_data->last_one = samp_offset; -+					} else if (curr_data->first_zero == 0) { -+						curr_data->first_zero = samp_offset; -+					} -+					printk(SAMP_OFFSET_PLOT, result ? "." : "#"); -+				} -+				printk(SAMP_OFFSET_PLOT, " "); -+			} -+			printk(SAMP_OFFSET_PLOT, PLOT_CH_SPACE); -+			data_control_0.samp_train_mode = 0; -+			mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), data_control_0.raw); -+		} -+		printk(SAMP_OFFSET_PLOT, "\n"); -+	} -+	printk(BIOS_DEBUG, "\nBitSAmp "); -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		for (uint8_t byte = 0; byte < ctrl->lanes; byte++) { -+			uint32_t rx_offset_vdq = 0; -+			for (uint8_t bit = 0; bit < max_train_bits; bit++) { -+				struct samp_train_data *const curr_data = -+						&samp_data[channel][byte][bit]; -+ -+				uint8_t vref = curr_data->first_zero + curr_data->last_one; -+				vref = clamp_u8(0, vref / 2, 15); -+				/* -+				 * Check for saturation conditions to make sure -+				 * we are as close as possible to Vdd/2 (750 mV). -+				 */ -+				if (curr_data->first_zero == 0) -+					vref = 15; -+				if (curr_data->last_one == 0) -+					vref = 0; -+ -+				ctrl->rxdqvrefpb[channel][0][byte][bit].center = vref; -+				rx_offset_vdq += vref & 0xf << (4 * bit); -+				printk(BIOS_DEBUG, "%x", vref); -+			} -+			mchbar_write32(RX_OFFSET_VDQ(channel, byte), rx_offset_vdq); -+			printk(BIOS_DEBUG, " "); -+			download_regfile(ctrl, channel, 1, 0, REG_FILE_USE_RANK, 0, 1, 0); -+		} -+		printk(BIOS_DEBUG, PLOT_CH_SPACE); -+	} -+	printk(BIOS_DEBUG, "\n"); -+} -+ -+enum raminit_status train_sense_amp_offset(struct sysinfo *ctrl) -+{ -+	printk(BIOS_DEBUG, "Stage 1: Vref offset training\n"); -+	const enum raminit_status status = train_vref_offset(ctrl); -+ -+	printk(BIOS_DEBUG, "Stage 2: Samp offset training\n"); -+	train_samp_offset(ctrl); -+ -+	/* Clean up after test */ -+	for (uint8_t channel = 0; channel < NUM_CHANNELS; channel++) { -+		if (!does_ch_exist(ctrl, channel)) -+			continue; -+ -+		mchbar_write32(DDR_DATA_ch_CONTROL_0(channel), ctrl->dq_control_0[channel]); -+		for (uint8_t byte = 0; byte < ctrl->lanes; byte++) -+			mchbar_write32(DQ_CONTROL_2(channel, byte), -+				ctrl->dq_control_2[channel][byte]); -+	} -+	io_reset(); -+	return status; -+} -diff --git a/src/northbridge/intel/haswell/registers/mchbar.h b/src/northbridge/intel/haswell/registers/mchbar.h -index 49a215aa71..1a168a3fc8 100644 ---- a/src/northbridge/intel/haswell/registers/mchbar.h -+++ b/src/northbridge/intel/haswell/registers/mchbar.h -@@ -18,6 +18,8 @@ - #define RX_TRAIN_ch_r_b(ch, rank, byte)		_DDRIO_C_R_B(0x0000, ch, rank, byte) - #define TX_TRAIN_ch_r_b(ch, rank, byte)		_DDRIO_C_R_B(0x0020, ch, rank, byte) -  -+#define RX_OFFSET_VDQ(ch, byte)			_DDRIO_C_R_B(0x004c, ch, 0, byte) -+ - #define DDR_DATA_TRAIN_FEEDBACK(ch, byte)	_DDRIO_C_R_B(0x0054, ch, 0, byte) -  - #define DQ_CONTROL_1(ch, byte)			_DDRIO_C_R_B(0x0060, ch, 0, byte) ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0018-Remove-warning-for-coreboot-images-built-without-a-p.patch b/config/coreboot/haswell/patches/0018-Remove-warning-for-coreboot-images-built-without-a-p.patch deleted file mode 100644 index 34309242..00000000 --- a/config/coreboot/haswell/patches/0018-Remove-warning-for-coreboot-images-built-without-a-p.patch +++ /dev/null @@ -1,39 +0,0 @@ -From c25dcd8ac80598939edffd011df0fd9ba3d8a1a8 Mon Sep 17 00:00:00 2001 -From: Nicholas Chin <nic.c3.14@gmail.com> -Date: Fri, 12 May 2023 19:55:15 -0600 -Subject: [PATCH 18/20] Remove warning for coreboot images built without a - payload - -I added this in upstream to prevent people from accidentally flashing -roms without a payload resulting in a no boot situation, but in -libreboot lbmk handles the payload and thus this warning always comes -up. This has caused confusion and concern so just patch it out. ---- - payloads/Makefile.mk | 13 +------------ - 1 file changed, 1 insertion(+), 12 deletions(-) - -diff --git a/payloads/Makefile.mk b/payloads/Makefile.mk -index a2336aa876..4f1692a873 100644 ---- a/payloads/Makefile.mk -+++ b/payloads/Makefile.mk -@@ -49,16 +49,5 @@ distclean-payloads: - print-repo-info-payloads: - 	-$(foreach payload, $(PAYLOADS_LIST), $(MAKE) -C $(payload) print-repo-info 2>/dev/null; ) -  --ifeq ($(CONFIG_PAYLOAD_NONE),y) --show_notices:: warn_no_payload --endif -- --warn_no_payload: --	printf "\n\t** WARNING **\n" --	printf "coreboot has been built without a payload. Writing\n" --	printf "a coreboot image without a payload to your board's\n" --	printf "flash chip will result in a non-booting system. You\n" --	printf "can use cbfstool to add a payload to the image.\n\n" -- - .PHONY: force-payload coreinfo nvramcui --.PHONY: clean-payloads distclean-payloads print-repo-info-payloads warn_no_payload -+.PHONY: clean-payloads distclean-payloads print-repo-info-payloads ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0019-use-mirrorservice.org-for-gcc-downloads.patch b/config/coreboot/haswell/patches/0019-use-mirrorservice.org-for-gcc-downloads.patch deleted file mode 100644 index e197b3f3..00000000 --- a/config/coreboot/haswell/patches/0019-use-mirrorservice.org-for-gcc-downloads.patch +++ /dev/null @@ -1,36 +0,0 @@ -From 081890bab8d454247b6f7e9cb209f46159c45c8b Mon Sep 17 00:00:00 2001 -From: Leah Rowe <leah@libreboot.org> -Date: Sun, 5 Nov 2023 23:19:42 +0000 -Subject: [PATCH 19/20] use mirrorservice.org for gcc downloads - -the gnu.org 302 redirect often fails - -Signed-off-by: Leah Rowe <leah@libreboot.org> ---- - util/crossgcc/buildgcc | 10 +++++----- - 1 file changed, 5 insertions(+), 5 deletions(-) - -diff --git a/util/crossgcc/buildgcc b/util/crossgcc/buildgcc -index 0a0462e2f6..6ae201239d 100755 ---- a/util/crossgcc/buildgcc -+++ b/util/crossgcc/buildgcc -@@ -69,11 +69,11 @@ NASM_ARCHIVE="nasm-${NASM_VERSION}.tar.bz2" - # to the jenkins build as well, or the builder won't download it. -  - # GCC toolchain archive locations --GMP_BASE_URL="https://ftpmirror.gnu.org/gmp" --MPFR_BASE_URL="https://ftpmirror.gnu.org/mpfr" --MPC_BASE_URL="https://ftpmirror.gnu.org/mpc" --GCC_BASE_URL="https://ftpmirror.gnu.org/gcc/gcc-${GCC_VERSION}" --BINUTILS_BASE_URL="https://ftpmirror.gnu.org/binutils" -+GMP_BASE_URL="https://www.mirrorservice.org/sites/ftp.gnu.org/gnu/gmp" -+MPFR_BASE_URL="https://www.mirrorservice.org/sites/ftp.gnu.org/gnu/mpfr" -+MPC_BASE_URL="https://www.mirrorservice.org/sites/ftp.gnu.org/gnu/mpc" -+GCC_BASE_URL="https://www.mirrorservice.org/sites/ftp.gnu.org/gnu/gcc/gcc-${GCC_VERSION}" -+BINUTILS_BASE_URL="https://www.mirrorservice.org/sites/ftp.gnu.org/gnu/binutils" - IASL_BASE_URL="https://github.com/acpica/acpica/archive/refs/tags" - # CLANG toolchain archive locations - LLVM_BASE_URL="https://github.com/llvm/llvm-project/releases/download/llvmorg-${CLANG_VERSION}" ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0020-util-ifdtool-add-nuke-flag-all-0xFF-on-region.patch b/config/coreboot/haswell/patches/0020-util-ifdtool-add-nuke-flag-all-0xFF-on-region.patch deleted file mode 100644 index 60490608..00000000 --- a/config/coreboot/haswell/patches/0020-util-ifdtool-add-nuke-flag-all-0xFF-on-region.patch +++ /dev/null @@ -1,204 +0,0 @@ -From 1a4f454e05b613cb080cdd063dd3efb1fdbb748b Mon Sep 17 00:00:00 2001 -From: Leah Rowe <leah@libreboot.org> -Date: Sun, 19 Feb 2023 18:21:43 +0000 -Subject: [PATCH 20/20] util/ifdtool: add --nuke flag (all 0xFF on region) - -When this option is used, the region's contents are overwritten -with all ones (0xFF). - -Example: - -./ifdtool --nuke gbe coreboot.rom -./ifdtool --nuke bios coreboot.com -./ifdtool --nuke me coreboot.com - -Rebased since the last revision update in lbmk. - -Signed-off-by: Leah Rowe <leah@libreboot.org> ---- - util/ifdtool/ifdtool.c | 113 ++++++++++++++++++++++++++++++----------- - 1 file changed, 82 insertions(+), 31 deletions(-) - -diff --git a/util/ifdtool/ifdtool.c b/util/ifdtool/ifdtool.c -index 516570e0a3..1638e1710e 100644 ---- a/util/ifdtool/ifdtool.c -+++ b/util/ifdtool/ifdtool.c -@@ -2143,6 +2143,7 @@ static void print_usage(const char *name) - 	       "                                         tgl    - Tiger Lake\n" - 	       "                                         wbg    - Wellsburg\n" - 	       "   -S | --setpchstrap                    Write a PCH strap\n" -+	       "   -N | --nuke <region>                  Overwrite the specified region with 0xFF (all ones)\n" - 	       "   -V | --newvalue                       The new value to write into PCH strap specified by -S\n" - 	       "   -v | --version:                       print the version\n" - 	       "   -h | --help:                          print this help\n\n" -@@ -2151,6 +2152,60 @@ static void print_usage(const char *name) - 	       "\n"); - } -  -+static int -+get_region_type_string(const char *region_type_string) -+{ -+       if (!strcasecmp("Descriptor", region_type_string)) -+               return 0; -+       else if (!strcasecmp("BIOS", region_type_string)) -+               return 1; -+       else if (!strcasecmp("ME", region_type_string)) -+               return 2; -+       else if (!strcasecmp("GbE", region_type_string)) -+               return 3; -+       else if (!strcasecmp("Platform Data", region_type_string)) -+               return 4; -+       else if (!strcasecmp("Device Exp1", region_type_string)) -+               return 5; -+       else if (!strcasecmp("Secondary BIOS", region_type_string)) -+               return 6; -+       else if (!strcasecmp("Reserved", region_type_string)) -+               return 7; -+       else if (!strcasecmp("EC", region_type_string)) -+               return 8; -+       else if (!strcasecmp("Device Exp2", region_type_string)) -+               return 9; -+       else if (!strcasecmp("IE", region_type_string)) -+               return 10; -+       else if (!strcasecmp("10GbE_0", region_type_string)) -+               return 11; -+       else if (!strcasecmp("10GbE_1", region_type_string)) -+               return 12; -+       else if (!strcasecmp("PTT", region_type_string)) -+               return 15; -+       return -1; -+} -+ -+static void -+nuke(const char *filename, char *image, int size, int region_type) -+{ -+       int i; -+       struct region region; -+       const struct frba *frba = find_frba(image, size); -+       if (!frba) -+               exit(EXIT_FAILURE); -+ -+       region = get_region(frba, region_type); -+       if (region.size > 0) { -+               for (i = region.base; i <= region.limit; i++) { -+                       if ((i + 1) > (size)) -+                               break; -+                       image[i] = 0xFF; -+               } -+               write_image(filename, image, size); -+       } -+} -+ - int main(int argc, char *argv[]) - { - 	int opt, option_index = 0; -@@ -2158,6 +2213,7 @@ int main(int argc, char *argv[]) - 	int mode_em100 = 0, mode_locked = 0, mode_unlocked = 0, mode_validate = 0; - 	int mode_layout = 0, mode_newlayout = 0, mode_density = 0, mode_setstrap = 0; - 	int mode_read = 0, mode_altmedisable = 0, altmedisable = 0, mode_fmap_template = 0; -+	int mode_nuke = 0; - 	int mode_gpr0_disable = 0, mode_gpr0_enable = 0; - 	char *region_type_string = NULL, *region_fname = NULL; - 	const char *layout_fname = NULL; -@@ -2192,6 +2248,7 @@ int main(int argc, char *argv[]) - 		{"validate", 0, NULL, 't'}, - 		{"setpchstrap", 1, NULL, 'S'}, - 		{"newvalue", 1, NULL, 'V'}, -+		{"nuke", 1, NULL, 'N'}, - 		{0, 0, 0, 0} - 	}; -  -@@ -2241,35 +2298,8 @@ int main(int argc, char *argv[]) - 			region_fname++; - 			// Descriptor, BIOS, ME, GbE, Platform - 			// valid type? --			if (!strcasecmp("Descriptor", region_type_string)) --				region_type = 0; --			else if (!strcasecmp("BIOS", region_type_string)) --				region_type = 1; --			else if (!strcasecmp("ME", region_type_string)) --				region_type = 2; --			else if (!strcasecmp("GbE", region_type_string)) --				region_type = 3; --			else if (!strcasecmp("Platform Data", region_type_string)) --				region_type = 4; --			else if (!strcasecmp("Device Exp1", region_type_string)) --				region_type = 5; --			else if (!strcasecmp("Secondary BIOS", region_type_string)) --				region_type = 6; --			else if (!strcasecmp("Reserved", region_type_string)) --				region_type = 7; --			else if (!strcasecmp("EC", region_type_string)) --				region_type = 8; --			else if (!strcasecmp("Device Exp2", region_type_string)) --				region_type = 9; --			else if (!strcasecmp("IE", region_type_string)) --				region_type = 10; --			else if (!strcasecmp("10GbE_0", region_type_string)) --				region_type = 11; --			else if (!strcasecmp("10GbE_1", region_type_string)) --				region_type = 12; --			else if (!strcasecmp("PTT", region_type_string)) --				region_type = 15; --			if (region_type == -1) { -+			if ((region_type = -+			    get_region_type_string(region_type_string)) == -1) { - 				fprintf(stderr, "No such region type: '%s'\n\n", - 					region_type_string); - 				fprintf(stderr, "run '%s -h' for usage\n", argv[0]); -@@ -2441,6 +2471,22 @@ int main(int argc, char *argv[]) - 		case 't': - 			mode_validate = 1; - 			break; -+               case 'N': -+                       region_type_string = strdup(optarg); -+                       if (!region_type_string) { -+                               fprintf(stderr, "No region specified\n"); -+                               print_usage(argv[0]); -+                               exit(EXIT_FAILURE); -+                       } -+                       if ((region_type = -+                           get_region_type_string(region_type_string)) == -1) { -+                               fprintf(stderr, "No such region type: '%s'\n\n", -+                                       region_type_string); -+                               print_usage(argv[0]); -+                               exit(EXIT_FAILURE); -+                       } -+                       mode_nuke = 1; -+                       break; - 		case 'v': - 			print_version(); - 			exit(EXIT_SUCCESS); -@@ -2457,7 +2503,7 @@ int main(int argc, char *argv[]) - 	if ((mode_dump + mode_layout + mode_fmap_template + mode_extract + mode_inject + - 			mode_setstrap + mode_newlayout + (mode_spifreq | mode_em100 | - 			mode_unlocked | mode_locked) + mode_altmedisable + mode_validate + --			(mode_gpr0_disable | mode_gpr0_enable)) > 1) { -+			(mode_gpr0_disable | mode_gpr0_enable) + mode_nuke) > 1) { - 		fprintf(stderr, "You may not specify more than one mode.\n\n"); - 		fprintf(stderr, "run '%s -h' for usage\n", argv[0]); - 		exit(EXIT_FAILURE); -@@ -2466,7 +2512,8 @@ int main(int argc, char *argv[]) - 	if ((mode_dump + mode_layout + mode_fmap_template + mode_extract + mode_inject + - 			mode_setstrap + mode_newlayout + mode_spifreq + mode_em100 + - 			mode_locked + mode_unlocked + mode_density + mode_altmedisable + --			mode_validate + (mode_gpr0_disable | mode_gpr0_enable)) == 0) { -+			mode_validate + (mode_gpr0_disable | mode_gpr0_enable) + -+			mode_nuke) == 0) { - 		fprintf(stderr, "You need to specify a mode.\n\n"); - 		fprintf(stderr, "run '%s -h' for usage\n", argv[0]); - 		exit(EXIT_FAILURE); -@@ -2576,6 +2623,10 @@ int main(int argc, char *argv[]) - 		write_image(new_filename, image, size); - 	} -  -+	if (mode_nuke) { -+		nuke(new_filename, image, size, region_type); -+	} -+ - 	if (mode_altmedisable) { - 		struct fpsba *fpsba = find_fpsba(image, size); - 		struct fmsba *fmsba = find_fmsba(image, size); ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0021-nb-intel-haswell-make-IOMMU-a-runtime-option.patch b/config/coreboot/haswell/patches/0021-nb-intel-haswell-make-IOMMU-a-runtime-option.patch deleted file mode 100644 index 81b8e839..00000000 --- a/config/coreboot/haswell/patches/0021-nb-intel-haswell-make-IOMMU-a-runtime-option.patch +++ /dev/null @@ -1,292 +0,0 @@ -From d97b865a2210e70583e8bf5ee3a73d3c131b29c1 Mon Sep 17 00:00:00 2001 -From: Leah Rowe <info@minifree.org> -Date: Sat, 2 Mar 2024 22:51:09 +0000 -Subject: [PATCH 1/4] nb/intel/haswell: make IOMMU a runtime option - -When I tested graphics cards on a coreboot port for Dell -OptiPlex 9020 SFF, I could not use a graphics card unless -I set iommu=off on the Linux cmdline. - -Coreboot's current behaviour is to check whether the CPU -has vt-d support and, if it does, initialise the IOMMU. - -This patch maintains the current behaviour by default, but -allows the user to turn *off* the IOMMU, even if vt-d is -supported by the host CPU. - -If iommu=Disable is specified, the check will not be -performed, and the IOMMU will be left disabled. This option -has been added to all current Haswell boards, though it is -recommended to leave the IOMMU turned on in most setups. - -Signed-off-by: Leah Rowe <info@minifree.org> ---- - src/mainboard/asrock/b85m_pro4/cmos.default    | 1 + - src/mainboard/asrock/b85m_pro4/cmos.layout     | 3 +++ - src/mainboard/asrock/h81m-hds/cmos.default     | 1 + - src/mainboard/asrock/h81m-hds/cmos.layout      | 6 ++++++ - src/mainboard/dell/optiplex_9020/cmos.default  | 1 + - src/mainboard/dell/optiplex_9020/cmos.layout   | 6 ++++++ - src/mainboard/google/beltino/cmos.layout       | 5 +++++ - src/mainboard/google/slippy/cmos.layout        | 5 +++++ - src/mainboard/intel/baskingridge/cmos.layout   | 4 ++++ - src/mainboard/lenovo/haswell/cmos.default      | 1 + - src/mainboard/lenovo/haswell/cmos.layout       | 3 +++ - src/mainboard/supermicro/x10slm-f/cmos.default | 1 + - src/mainboard/supermicro/x10slm-f/cmos.layout  | 6 ++++++ - src/northbridge/intel/haswell/early_init.c     | 5 +++++ - 14 files changed, 48 insertions(+) - -diff --git a/src/mainboard/asrock/b85m_pro4/cmos.default b/src/mainboard/asrock/b85m_pro4/cmos.default -index 01bf20ad16..dfc8b80fb0 100644 ---- a/src/mainboard/asrock/b85m_pro4/cmos.default -+++ b/src/mainboard/asrock/b85m_pro4/cmos.default -@@ -4,3 +4,4 @@ boot_option=Fallback - debug_level=Debug - nmi=Enable - power_on_after_fail=Disable -+iommu=Enable -diff --git a/src/mainboard/asrock/b85m_pro4/cmos.layout b/src/mainboard/asrock/b85m_pro4/cmos.layout -index efdc333fc2..c9883ea71d 100644 ---- a/src/mainboard/asrock/b85m_pro4/cmos.layout -+++ b/src/mainboard/asrock/b85m_pro4/cmos.layout -@@ -11,6 +11,7 @@ - 	395	4	e	4	debug_level - 	408	1	e	1	nmi - 	409	2	e	5	power_on_after_fail -+	412	1	e	6	iommu - 	984	16	h	0	check_sum - # ----------------------------------------------------------------- -  -@@ -38,6 +39,8 @@ - 	5	0	Disable - 	5	1	Enable - 	5	2	Keep -+	6	0	Disable -+	6	1	Enable - # ----------------------------------------------------------------- -  - # ----------------------------------------------------------------- -diff --git a/src/mainboard/asrock/h81m-hds/cmos.default b/src/mainboard/asrock/h81m-hds/cmos.default -index 01bf20ad16..dfc8b80fb0 100644 ---- a/src/mainboard/asrock/h81m-hds/cmos.default -+++ b/src/mainboard/asrock/h81m-hds/cmos.default -@@ -4,3 +4,4 @@ boot_option=Fallback - debug_level=Debug - nmi=Enable - power_on_after_fail=Disable -+iommu=Enable -diff --git a/src/mainboard/asrock/h81m-hds/cmos.layout b/src/mainboard/asrock/h81m-hds/cmos.layout -index c9ba76c78f..95ee3d36fb 100644 ---- a/src/mainboard/asrock/h81m-hds/cmos.layout -+++ b/src/mainboard/asrock/h81m-hds/cmos.layout -@@ -21,6 +21,9 @@ entries - 408	1	e	1	nmi - 409	2	e	5	power_on_after_fail -  -+# enable or disable iommu -+412	1	e	6	iommu -+ - # coreboot config options: check sums - 984	16	h	0	check_sum -  -@@ -52,6 +55,9 @@ enumerations - 5	1	Enable - 5	2	Keep -  -+6	0	Disable -+6	1	Enable -+ - # ----------------------------------------------------------------- - checksums -  -diff --git a/src/mainboard/dell/optiplex_9020/cmos.default b/src/mainboard/dell/optiplex_9020/cmos.default -index cd4046f1ab..c974022472 100644 ---- a/src/mainboard/dell/optiplex_9020/cmos.default -+++ b/src/mainboard/dell/optiplex_9020/cmos.default -@@ -3,3 +3,4 @@ boot_option=Fallback - debug_level=Debug - nmi=Disable - power_on_after_fail=Disable -+iommu=Enable -diff --git a/src/mainboard/dell/optiplex_9020/cmos.layout b/src/mainboard/dell/optiplex_9020/cmos.layout -index c9ba76c78f..72ff9c4bee 100644 ---- a/src/mainboard/dell/optiplex_9020/cmos.layout -+++ b/src/mainboard/dell/optiplex_9020/cmos.layout -@@ -21,6 +21,9 @@ entries - 408	1	e	1	nmi - 409	2	e	5	power_on_after_fail -  -+# turn iommu on or off -+412	1	e	6	iommu -+ - # coreboot config options: check sums - 984	16	h	0	check_sum -  -@@ -52,6 +55,9 @@ enumerations - 5	1	Enable - 5	2	Keep -  -+6	0	Disable -+6	1	Enable -+ - # ----------------------------------------------------------------- - checksums -  -diff --git a/src/mainboard/google/beltino/cmos.layout b/src/mainboard/google/beltino/cmos.layout -index 78d44c1415..c143979ae1 100644 ---- a/src/mainboard/google/beltino/cmos.layout -+++ b/src/mainboard/google/beltino/cmos.layout -@@ -19,6 +19,9 @@ entries - 408	1	e	1	nmi - 409	2	e	7	power_on_after_fail -  -+# enable or disable iommu -+412	1	e	8	iommu -+ - # coreboot config options: bootloader - #Used by ChromeOS: - 416	128	r	0	vbnv -@@ -47,6 +50,8 @@ enumerations - 7	0	Disable - 7	1	Enable - 7	2	Keep -+8	0	Disable -+8	1	Enable - # ----------------------------------------------------------------- - checksums -  -diff --git a/src/mainboard/google/slippy/cmos.layout b/src/mainboard/google/slippy/cmos.layout -index 78d44c1415..c143979ae1 100644 ---- a/src/mainboard/google/slippy/cmos.layout -+++ b/src/mainboard/google/slippy/cmos.layout -@@ -19,6 +19,9 @@ entries - 408	1	e	1	nmi - 409	2	e	7	power_on_after_fail -  -+# enable or disable iommu -+412	1	e	8	iommu -+ - # coreboot config options: bootloader - #Used by ChromeOS: - 416	128	r	0	vbnv -@@ -47,6 +50,8 @@ enumerations - 7	0	Disable - 7	1	Enable - 7	2	Keep -+8	0	Disable -+8	1	Enable - # ----------------------------------------------------------------- - checksums -  -diff --git a/src/mainboard/intel/baskingridge/cmos.layout b/src/mainboard/intel/baskingridge/cmos.layout -index 78d44c1415..f2c602f541 100644 ---- a/src/mainboard/intel/baskingridge/cmos.layout -+++ b/src/mainboard/intel/baskingridge/cmos.layout -@@ -19,6 +19,8 @@ entries - 408	1	e	1	nmi - 409	2	e	7	power_on_after_fail -  -+412	1	e	8	iommu -+ - # coreboot config options: bootloader - #Used by ChromeOS: - 416	128	r	0	vbnv -@@ -47,6 +49,8 @@ enumerations - 7	0	Disable - 7	1	Enable - 7	2	Keep -+8	0	Disable -+8	1	Enable - # ----------------------------------------------------------------- - checksums -  -diff --git a/src/mainboard/lenovo/haswell/cmos.default b/src/mainboard/lenovo/haswell/cmos.default -index 08db97c5a9..cc6b363cd9 100644 ---- a/src/mainboard/lenovo/haswell/cmos.default -+++ b/src/mainboard/lenovo/haswell/cmos.default -@@ -14,3 +14,4 @@ trackpoint=Enable - backlight=Keyboard - enable_dual_graphics=Disable - usb_always_on=Disable -+iommu=Enable -diff --git a/src/mainboard/lenovo/haswell/cmos.layout b/src/mainboard/lenovo/haswell/cmos.layout -index 27915d3ab7..59df76b64c 100644 ---- a/src/mainboard/lenovo/haswell/cmos.layout -+++ b/src/mainboard/lenovo/haswell/cmos.layout -@@ -23,6 +23,7 @@ entries -  - # coreboot config options: EC - 411	1	e	8	first_battery -+413	1	e	14	iommu - 415	1	e	1	wlan - 416	1	e	1	trackpoint - 417	1	e	1	fn_ctrl_swap -@@ -72,6 +73,8 @@ enumerations - 13	0	Disable - 13	1	AC and battery - 13	2	AC only -+14	0	Disable -+14	1	Enable -  - # ----------------------------------------------------------------- - checksums -diff --git a/src/mainboard/supermicro/x10slm-f/cmos.default b/src/mainboard/supermicro/x10slm-f/cmos.default -index 7ce38fb5d7..6049e7938a 100644 ---- a/src/mainboard/supermicro/x10slm-f/cmos.default -+++ b/src/mainboard/supermicro/x10slm-f/cmos.default -@@ -5,3 +5,4 @@ debug_level=Debug - nmi=Enable - power_on_after_fail=Keep - hide_ast2400=Disable -+iommu=Enable -diff --git a/src/mainboard/supermicro/x10slm-f/cmos.layout b/src/mainboard/supermicro/x10slm-f/cmos.layout -index 38ba87aa45..24d39e97ee 100644 ---- a/src/mainboard/supermicro/x10slm-f/cmos.layout -+++ b/src/mainboard/supermicro/x10slm-f/cmos.layout -@@ -21,6 +21,9 @@ entries - 408	1	e	1	nmi - 409	2	e	5	power_on_after_fail -  -+# enable or disable iommu -+412	1	e	6	iommu -+ - # coreboot config options: mainboard - 416	1	e	1	hide_ast2400 -  -@@ -55,6 +58,9 @@ enumerations - 5	1	Enable - 5	2	Keep -  -+6	0	Disable -+6	1	Enable -+ - # ----------------------------------------------------------------- - checksums -  -diff --git a/src/northbridge/intel/haswell/early_init.c b/src/northbridge/intel/haswell/early_init.c -index e47deb5da6..1a7e0b1076 100644 ---- a/src/northbridge/intel/haswell/early_init.c -+++ b/src/northbridge/intel/haswell/early_init.c -@@ -5,6 +5,7 @@ - #include <device/mmio.h> - #include <device/pci_def.h> - #include <device/pci_ops.h> -+#include <option.h> -  - #include "haswell.h" -  -@@ -157,6 +158,10 @@ static void haswell_setup_misc(void) - static void haswell_setup_iommu(void) - { - 	const u32 capid0_a = pci_read_config32(HOST_BRIDGE, CAPID0_A); -+	u8 enable_iommu = get_uint_option("iommu", 1); -+ -+	if (!enable_iommu) -+		return; -  - 	if (capid0_a & VTD_DISABLE) - 		return; ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0022-dell-optiplex_9020-Disable-IOMMU-by-default.patch b/config/coreboot/haswell/patches/0022-dell-optiplex_9020-Disable-IOMMU-by-default.patch deleted file mode 100644 index fbb40293..00000000 --- a/config/coreboot/haswell/patches/0022-dell-optiplex_9020-Disable-IOMMU-by-default.patch +++ /dev/null @@ -1,29 +0,0 @@ -From 153ca1a43c2c978fa2b2b82d988b0f838953cfb9 Mon Sep 17 00:00:00 2001 -From: Leah Rowe <info@minifree.org> -Date: Sat, 2 Mar 2024 23:00:09 +0000 -Subject: [PATCH 2/4] dell/optiplex_9020: Disable IOMMU by default - -Needed to make graphics cards work. Turning it on is -recommended if only using iGPU, otherwise leave it off -by default. The IOMMU is extremely buggy when a graphics -card is used. Leaving it off by default will ensure that -the default ROM images in Libreboot will work on any setup. - -Signed-off-by: Leah Rowe <info@minifree.org> ---- - src/mainboard/dell/optiplex_9020/cmos.default | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/src/mainboard/dell/optiplex_9020/cmos.default b/src/mainboard/dell/optiplex_9020/cmos.default -index c974022472..a0acd7b6bb 100644 ---- a/src/mainboard/dell/optiplex_9020/cmos.default -+++ b/src/mainboard/dell/optiplex_9020/cmos.default -@@ -3,4 +3,4 @@ boot_option=Fallback - debug_level=Debug - nmi=Disable - power_on_after_fail=Disable --iommu=Enable -+iommu=Disable ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0023-mb-dell-optiplex_9020-Implement-late-HWM-initializat.patch b/config/coreboot/haswell/patches/0023-mb-dell-optiplex_9020-Implement-late-HWM-initializat.patch deleted file mode 100644 index e2db6a17..00000000 --- a/config/coreboot/haswell/patches/0023-mb-dell-optiplex_9020-Implement-late-HWM-initializat.patch +++ /dev/null @@ -1,602 +0,0 @@ -From 05cc767d1398f91533e87db5ceaa0aabb7918425 Mon Sep 17 00:00:00 2001 -From: Mate Kukri <kukri.mate@gmail.com> -Date: Thu, 18 Apr 2024 20:28:45 +0100 -Subject: [PATCH 1/1] mb/dell/optiplex_9020: Implement late HWM initialization - -There are 4 different chassis types specified by vendor firmware, each -with a slightly different HWM configuration. - -The chassis type to use is determined at runtime by reading a set of -4 PCH GPIOs: 70, 38, 17, and 1. - -Additionally vendor firmware also provides an option to run the fans at -full speed. This is substituted with a coreboot nvram option in this -implementation. - -This was tested to make fan control work on my OptiPlex 7020 SFF. - -NOTE: This is superficially similar to the OptiPlex 9010's SCH5545 -however the OptiPlex 9020's SCH5555 does not use externally -programmed EC firmware. - -Change-Id: Ibdccd3fc7364e03e84ca606592928410624eed43 -Signed-off-by: Mate Kukri <kukri.mate@gmail.com> ---- - src/mainboard/dell/optiplex_9020/Makefile.mk  |   3 +- - src/mainboard/dell/optiplex_9020/bootblock.c  |  25 +- - src/mainboard/dell/optiplex_9020/cmos.default |   1 + - src/mainboard/dell/optiplex_9020/cmos.layout  |   5 +- - src/mainboard/dell/optiplex_9020/mainboard.c  | 387 ++++++++++++++++++ - src/mainboard/dell/optiplex_9020/sch5555_ec.c |  54 +++ - src/mainboard/dell/optiplex_9020/sch5555_ec.h |  10 + - 7 files changed, 463 insertions(+), 22 deletions(-) - create mode 100644 src/mainboard/dell/optiplex_9020/sch5555_ec.c - create mode 100644 src/mainboard/dell/optiplex_9020/sch5555_ec.h - -diff --git a/src/mainboard/dell/optiplex_9020/Makefile.mk b/src/mainboard/dell/optiplex_9020/Makefile.mk -index 6ca2f2afaa..08e2e53577 100644 ---- a/src/mainboard/dell/optiplex_9020/Makefile.mk -+++ b/src/mainboard/dell/optiplex_9020/Makefile.mk -@@ -2,4 +2,5 @@ -  - romstage-y += gpio.c - ramstage-$(CONFIG_MAINBOARD_USE_LIBGFXINIT) += gma-mainboard.ads --bootblock-y += bootblock.c -+ramstage-y += sch5555_ec.c -+bootblock-y += bootblock.c sch5555_ec.c -diff --git a/src/mainboard/dell/optiplex_9020/bootblock.c b/src/mainboard/dell/optiplex_9020/bootblock.c -index 2837cf9cf1..e5e759273e 100644 ---- a/src/mainboard/dell/optiplex_9020/bootblock.c -+++ b/src/mainboard/dell/optiplex_9020/bootblock.c -@@ -4,29 +4,14 @@ - #include <device/pnp_ops.h> - #include <superio/smsc/sch555x/sch555x.h> - #include <southbridge/intel/lynxpoint/pch.h> -- --static void ec_write(uint8_t addr1, uint16_t addr2, uint8_t val) --{ --	// Clear EC-to-Host mailbox --	uint8_t tmp = inb(SCH555x_EMI_IOBASE + 1); --	outb(tmp, SCH555x_EMI_IOBASE + 1); -- --	// Send address and value to the EC --	sch555x_emi_write16(0, (addr1 * 2) | 0x101); --	sch555x_emi_write32(4, val | (addr2 << 16)); -- --	// Wait for acknowledgement message from EC --	outb(1, SCH555x_EMI_IOBASE); --	size_t timeout = 0; --	do {} while (++timeout < 0xfff && (inb(SCH555x_EMI_IOBASE + 1) & 1) == 0); --} -+#include "sch5555_ec.h" -  - struct ec_init_entry { - 	uint16_t addr; - 	uint8_t val; - }; -  --static void ec_init(void) -+static void bootblock_ec_init(void) - { - 	/* - 	 * Tables from CORE_PEI -@@ -108,9 +93,9 @@ void mainboard_config_superio(void) - 	outb(0x01, SCH555x_RUNTIME_IOBASE + SCH555x_RUNTIME_UNK1); - 	outb(0x0f, SCH555x_RUNTIME_IOBASE + SCH555x_RUNTIME_LED); -  --	// Magic EC init --	ec_init(); -+	// Perform bootblock EC initialization -+	bootblock_ec_init(); -  --	// Magic EC init is needed for UART1 initialization to work -+	// Bootblock EC initialization is required for UART1 to work - 	sch555x_enable_serial(SERIAL_DEV, CONFIG_TTYS0_BASE); - } -diff --git a/src/mainboard/dell/optiplex_9020/cmos.default b/src/mainboard/dell/optiplex_9020/cmos.default -index a0acd7b6bb..9e02534c16 100644 ---- a/src/mainboard/dell/optiplex_9020/cmos.default -+++ b/src/mainboard/dell/optiplex_9020/cmos.default -@@ -4,3 +4,4 @@ debug_level=Debug - nmi=Disable - power_on_after_fail=Disable - iommu=Disable -+fan_full_speed=Disable -diff --git a/src/mainboard/dell/optiplex_9020/cmos.layout b/src/mainboard/dell/optiplex_9020/cmos.layout -index 72ff9c4bee..4a1496a878 100644 ---- a/src/mainboard/dell/optiplex_9020/cmos.layout -+++ b/src/mainboard/dell/optiplex_9020/cmos.layout -@@ -22,7 +22,10 @@ entries - 409	2	e	5	power_on_after_fail -  - # turn iommu on or off --412	1	e	6	iommu -+411	1	e	6	iommu -+ -+# coreboot config options: EC -+412	1	e	1	fan_full_speed -  - # coreboot config options: check sums - 984	16	h	0	check_sum -diff --git a/src/mainboard/dell/optiplex_9020/mainboard.c b/src/mainboard/dell/optiplex_9020/mainboard.c -index c834fea5d3..0b7829c736 100644 ---- a/src/mainboard/dell/optiplex_9020/mainboard.c -+++ b/src/mainboard/dell/optiplex_9020/mainboard.c -@@ -1,7 +1,12 @@ - /* SPDX-License-Identifier: GPL-2.0-only */ -  -+#include <bootstate.h> -+#include <cpu/x86/msr.h> - #include <device/device.h> - #include <drivers/intel/gma/int15.h> -+#include <option.h> -+#include <southbridge/intel/common/gpio.h> -+#include "sch5555_ec.h" -  - static void mainboard_enable(struct device *dev) - { -@@ -13,3 +18,385 @@ static void mainboard_enable(struct device *dev) - struct chip_operations mainboard_ops = { - 	.enable_dev = mainboard_enable, - }; -+ -+#define HWM_TAB_ADD_TEMP_TARGET	1 -+#define HWM_TAB_PKG_POWER_ANY	0xffff -+#define CHASSIS_TYPE_UNKNOWN	0xff -+ -+struct hwm_tab_entry { -+	uint16_t addr; -+	uint8_t val; -+	uint8_t flags; -+	uint16_t pkg_power; -+}; -+ -+struct hwm_tab_entry HWM_TAB3[] = { -+	{ 0x005, 0x33, 0, 0xffff }, -+	{ 0x018, 0x2f, 0, 0xffff }, -+	{ 0x019, 0x2f, 0, 0xffff }, -+	{ 0x01a, 0x2f, 0, 0xffff }, -+	{ 0x080, 0x00, 0, 0xffff }, -+	{ 0x081, 0x00, 0, 0xffff }, -+	{ 0x083, 0xbb, 0, 0xffff }, -+	{ 0x085, 0x8a, 0, 0x0010 }, -+	{ 0x086, 0x4c, 0, 0x0010 }, -+	{ 0x08a, 0x66, 0, 0x0010 }, -+	{ 0x08b, 0x5b, 0, 0x0010 }, -+	{ 0x090, 0x65, 0, 0xffff }, -+	{ 0x091, 0x70, 0, 0xffff }, -+	{ 0x092, 0x86, 0, 0xffff }, -+	{ 0x096, 0xa4, 0, 0xffff }, -+	{ 0x097, 0xa4, 0, 0xffff }, -+	{ 0x098, 0xa4, 0, 0xffff }, -+	{ 0x09b, 0xa4, 0, 0xffff }, -+	{ 0x0a0, 0x0e, 0, 0xffff }, -+	{ 0x0a1, 0x0e, 0, 0xffff }, -+	{ 0x0ae, 0x7c, 0, 0xffff }, -+	{ 0x0af, 0x86, 0, 0xffff }, -+	{ 0x0b0, 0x9a, 0, 0xffff }, -+	{ 0x0b3, 0x9a, 0, 0xffff }, -+	{ 0x0b6, 0x08, 0, 0xffff }, -+	{ 0x0b7, 0x08, 0, 0xffff }, -+	{ 0x0ea, 0x64, 0, 0x0020 }, -+	{ 0x0ea, 0x5c, 0, 0x0010 }, -+	{ 0x0ef, 0xff, 0, 0xffff }, -+	{ 0x0f8, 0x15, 0, 0xffff }, -+	{ 0x0f9, 0x00, 0, 0xffff }, -+	{ 0x0f0, 0x30, 0, 0xffff }, -+	{ 0x0fd, 0x01, 0, 0xffff }, -+	{ 0x1a1, 0x00, 0, 0xffff }, -+	{ 0x1a2, 0x00, 0, 0xffff }, -+	{ 0x1b1, 0x08, 0, 0xffff }, -+	{ 0x1be, 0x99, 0, 0xffff }, -+	{ 0x280, 0xa0, 0, 0x0010 }, -+	{ 0x281, 0x0f, 0, 0x0010 }, -+	{ 0x282, 0x03, 0, 0xffff }, -+	{ 0x283, 0x0a, 0, 0xffff }, -+	{ 0x284, 0x80, 0, 0xffff }, -+	{ 0x285, 0x03, 0, 0xffff }, -+	{ 0x288, 0x68, 0, 0x0010 }, -+	{ 0x289, 0x10, 0, 0x0010 }, -+	{ 0x28a, 0x03, 0, 0xffff }, -+	{ 0x28b, 0x0a, 0, 0xffff }, -+	{ 0x28c, 0x80, 0, 0xffff }, -+	{ 0x28d, 0x03, 0, 0xffff }, -+}; -+ -+struct hwm_tab_entry HWM_TAB4[] = { -+	{ 0x005, 0x33, 0, 0xffff }, -+	{ 0x018, 0x2f, 0, 0xffff }, -+	{ 0x019, 0x2f, 0, 0xffff }, -+	{ 0x01a, 0x2f, 0, 0xffff }, -+	{ 0x080, 0x00, 0, 0xffff }, -+	{ 0x081, 0x00, 0, 0xffff }, -+	{ 0x083, 0xbb, 0, 0xffff }, -+	{ 0x085, 0x99, 0, 0x0020 }, -+	{ 0x085, 0xad, 0, 0x0010 }, -+	{ 0x086, 0x1c, 0, 0xffff }, -+	{ 0x08a, 0x39, 0, 0x0020 }, -+	{ 0x08a, 0x41, 0, 0x0010 }, -+	{ 0x08b, 0x76, 0, 0x0020 }, -+	{ 0x08b, 0x8b, 0, 0x0010 }, -+	{ 0x090, 0x5e, 0, 0xffff }, -+	{ 0x091, 0x5e, 0, 0xffff }, -+	{ 0x092, 0x86, 0, 0xffff }, -+	{ 0x096, 0xa4, 0, 0xffff }, -+	{ 0x097, 0xa4, 0, 0xffff }, -+	{ 0x098, 0xa4, 0, 0xffff }, -+	{ 0x09b, 0xa4, 0, 0xffff }, -+	{ 0x0a0, 0x0a, 0, 0xffff }, -+	{ 0x0a1, 0x0a, 0, 0xffff }, -+	{ 0x0ae, 0x7c, 0, 0xffff }, -+	{ 0x0af, 0x7c, 0, 0xffff }, -+	{ 0x0b0, 0x9a, 0, 0xffff }, -+	{ 0x0b3, 0x7c, 0, 0xffff }, -+	{ 0x0b6, 0x08, 0, 0xffff }, -+	{ 0x0b7, 0x08, 0, 0xffff }, -+	{ 0x0ea, 0x64, 0, 0x0020 }, -+	{ 0x0ea, 0x5c, 0, 0x0010 }, -+	{ 0x0ef, 0xff, 0, 0xffff }, -+	{ 0x0f8, 0x15, 0, 0xffff }, -+	{ 0x0f9, 0x00, 0, 0xffff }, -+	{ 0x0f0, 0x30, 0, 0xffff }, -+	{ 0x0fd, 0x01, 0, 0xffff }, -+	{ 0x1a1, 0x00, 0, 0xffff }, -+	{ 0x1a2, 0x00, 0, 0xffff }, -+	{ 0x1b1, 0x08, 0, 0xffff }, -+	{ 0x1be, 0x90, 0, 0xffff }, -+	{ 0x280, 0x94, 0, 0x0020 }, -+	{ 0x281, 0x11, 0, 0x0020 }, -+	{ 0x280, 0x94, 0, 0x0010 }, -+	{ 0x281, 0x11, 0, 0x0010 }, -+	{ 0x282, 0x03, 0, 0xffff }, -+	{ 0x283, 0x0a, 0, 0xffff }, -+	{ 0x284, 0x80, 0, 0xffff }, -+	{ 0x285, 0x03, 0, 0xffff }, -+	{ 0x288, 0x28, 0, 0x0020 }, -+	{ 0x289, 0x0a, 0, 0x0020 }, -+	{ 0x288, 0x28, 0, 0x0010 }, -+	{ 0x289, 0x0a, 0, 0x0010 }, -+	{ 0x28a, 0x03, 0, 0xffff }, -+	{ 0x28b, 0x0a, 0, 0xffff }, -+	{ 0x28c, 0x80, 0, 0xffff }, -+	{ 0x28d, 0x03, 0, 0xffff }, -+}; -+ -+struct hwm_tab_entry HWM_TAB5[] = { -+	{ 0x005, 0x33, 0, 0xffff }, -+	{ 0x018, 0x2f, 0, 0xffff }, -+	{ 0x019, 0x2f, 0, 0xffff }, -+	{ 0x01a, 0x2f, 0, 0xffff }, -+	{ 0x080, 0x00, 0, 0xffff }, -+	{ 0x081, 0x00, 0, 0xffff }, -+	{ 0x083, 0xbb, 0, 0xffff }, -+	{ 0x085, 0x66, 0, 0x0020 }, -+	{ 0x085, 0x5d, 0, 0x0010 }, -+	{ 0x086, 0x1c, 0, 0xffff }, -+	{ 0x08a, 0x39, 0, 0x0020 }, -+	{ 0x08a, 0x41, 0, 0x0010 }, -+	{ 0x08b, 0x76, 0, 0x0020 }, -+	{ 0x08b, 0x80, 0, 0x0010 }, -+	{ 0x090, 0x5d, 0, 0x0020 }, -+	{ 0x090, 0x5e, 0, 0x0010 }, -+	{ 0x091, 0x5e, 0, 0xffff }, -+	{ 0x092, 0x86, 0, 0xffff }, -+	{ 0x096, 0xa4, 0, 0xffff }, -+	{ 0x097, 0xa4, 0, 0xffff }, -+	{ 0x098, 0xa3, 0, 0x0020 }, -+	{ 0x098, 0xa4, 0, 0x0010 }, -+	{ 0x09b, 0xa4, 0, 0xffff }, -+	{ 0x0a0, 0x08, 0, 0xffff }, -+	{ 0x0a1, 0x0a, 0, 0xffff }, -+	{ 0x0ae, 0x7c, 0, 0xffff }, -+	{ 0x0af, 0x7c, 0, 0xffff }, -+	{ 0x0b0, 0x9a, 0, 0xffff }, -+	{ 0x0b3, 0x7c, 0, 0xffff }, -+	{ 0x0b6, 0x08, 0, 0xffff }, -+	{ 0x0b7, 0x08, 0, 0xffff }, -+	{ 0x0ea, 0x64, 0, 0x0020 }, -+	{ 0x0ea, 0x5c, 0, 0x0010 }, -+	{ 0x0ef, 0xff, 0, 0xffff }, -+	{ 0x0f8, 0x15, 0, 0xffff }, -+	{ 0x0f9, 0x00, 0, 0xffff }, -+	{ 0x0f0, 0x30, 0, 0xffff }, -+	{ 0x0fd, 0x01, 0, 0xffff }, -+	{ 0x1a1, 0x00, 0, 0xffff }, -+	{ 0x1a2, 0x00, 0, 0xffff }, -+	{ 0x1b1, 0x08, 0, 0xffff }, -+	{ 0x1be, 0x98, 0, 0x0020 }, -+	{ 0x1be, 0x90, 0, 0x0010 }, -+	{ 0x280, 0x94, 0, 0x0020 }, -+	{ 0x281, 0x11, 0, 0x0020 }, -+	{ 0x280, 0x94, 0, 0x0010 }, -+	{ 0x281, 0x11, 0, 0x0010 }, -+	{ 0x282, 0x03, 0, 0xffff }, -+	{ 0x283, 0x0a, 0, 0xffff }, -+	{ 0x284, 0x80, 0, 0xffff }, -+	{ 0x285, 0x03, 0, 0xffff }, -+	{ 0x288, 0x28, 0, 0x0020 }, -+	{ 0x289, 0x0a, 0, 0x0020 }, -+	{ 0x288, 0x28, 0, 0x0010 }, -+	{ 0x289, 0x0a, 0, 0x0010 }, -+	{ 0x28a, 0x03, 0, 0xffff }, -+	{ 0x28b, 0x0a, 0, 0xffff }, -+	{ 0x28c, 0x80, 0, 0xffff }, -+	{ 0x28d, 0x03, 0, 0xffff }, -+}; -+ -+struct hwm_tab_entry HWM_TAB6[] = { -+	{ 0x005, 0x33, 0, 0xffff }, -+	{ 0x018, 0x2f, 0, 0xffff }, -+	{ 0x019, 0x2f, 0, 0xffff }, -+	{ 0x01a, 0x2f, 0, 0xffff }, -+	{ 0x080, 0x00, 0, 0xffff }, -+	{ 0x081, 0x00, 0, 0xffff }, -+	{ 0x083, 0xbb, 0, 0xffff }, -+	{ 0x085, 0x98, 0, 0xffff }, -+	{ 0x086, 0x3c, 0, 0xffff }, -+	{ 0x08a, 0x39, 0, 0x0020 }, -+	{ 0x08a, 0x3d, 0, 0x0010 }, -+	{ 0x08b, 0x44, 0, 0x0020 }, -+	{ 0x08b, 0x51, 0, 0x0010 }, -+	{ 0x090, 0x61, 0, 0xffff }, -+	{ 0x091, 0x6d, 0, 0xffff }, -+	{ 0x092, 0x86, 0, 0xffff }, -+	{ 0x096, 0xa4, 0, 0xffff }, -+	{ 0x097, 0xa4, 0, 0xffff }, -+	{ 0x098, 0x9f, 0, 0x0020 }, -+	{ 0x098, 0xa4, 0, 0x0010 }, -+	{ 0x09b, 0xa4, 0, 0xffff }, -+	{ 0x0a0, 0x0e, 0, 0xffff }, -+	{ 0x0a1, 0x0e, 0, 0xffff }, -+	{ 0x0ae, 0x7c, 0, 0xffff }, -+	{ 0x0af, 0x7c, 0, 0xffff }, -+	{ 0x0b0, 0x9b, 0, 0x0020 }, -+	{ 0x0b0, 0x98, 0, 0x0010 }, -+	{ 0x0b3, 0x9a, 0, 0xffff }, -+	{ 0x0b6, 0x08, 0, 0xffff }, -+	{ 0x0b7, 0x08, 0, 0xffff }, -+	{ 0x0ea, 0x64, 0, 0x0020 }, -+	{ 0x0ea, 0x5c, 0, 0x0010 }, -+	{ 0x0ef, 0xff, 0, 0xffff }, -+	{ 0x0f8, 0x15, 0, 0xffff }, -+	{ 0x0f9, 0x00, 0, 0xffff }, -+	{ 0x0f0, 0x30, 0, 0xffff }, -+	{ 0x0fd, 0x01, 0, 0xffff }, -+	{ 0x1a1, 0x00, 0, 0xffff }, -+	{ 0x1a2, 0x00, 0, 0xffff }, -+	{ 0x1b1, 0x08, 0, 0xffff }, -+	{ 0x1be, 0x9a, 0, 0x0020 }, -+	{ 0x1be, 0x96, 0, 0x0010 }, -+	{ 0x280, 0x94, 0, 0x0020 }, -+	{ 0x281, 0x11, 0, 0x0020 }, -+	{ 0x280, 0x94, 0, 0x0010 }, -+	{ 0x281, 0x11, 0, 0x0010 }, -+	{ 0x282, 0x03, 0, 0xffff }, -+	{ 0x283, 0x0a, 0, 0xffff }, -+	{ 0x284, 0x80, 0, 0xffff }, -+	{ 0x285, 0x03, 0, 0xffff }, -+	{ 0x288, 0x94, 0, 0x0020 }, -+	{ 0x289, 0x11, 0, 0x0020 }, -+	{ 0x288, 0x94, 0, 0x0010 }, -+	{ 0x289, 0x11, 0, 0x0010 }, -+	{ 0x28a, 0x03, 0, 0xffff }, -+	{ 0x28b, 0x0a, 0, 0xffff }, -+	{ 0x28c, 0x80, 0, 0xffff }, -+	{ 0x28d, 0x03, 0, 0xffff }, -+}; -+ -+static uint8_t get_chassis_type(void) -+{ -+	uint8_t gpio_chassis_type; -+ -+	// Read chassis type from GPIO -+	gpio_chassis_type = get_gpio(70) << 3 | get_gpio(38) << 2 | -+			    get_gpio(17) << 1 | get_gpio(1); -+ -+	printk(BIOS_DEBUG, "GPIO chassis type = %#x\n", gpio_chassis_type); -+ -+	// Turn it into internal chassis index -+	switch (gpio_chassis_type) { -+	case 0x08: -+	case 0x0a: -+		return 4; -+	case 0x0b: -+		return 3; -+	case 0x0c: -+		return 5; -+	case 0x0d: // SFF -+	case 0x0e: -+	case 0x0f: -+		return 6; -+	default: -+		return CHASSIS_TYPE_UNKNOWN; -+	} -+ -+} -+ -+static uint8_t get_temp_target(void) -+{ -+	uint8_t val = rdmsr(0x1a2).lo >> 8 & 0xff; -+	if (!val) -+		val = 20; -+	return 0x95 - val; -+} -+ -+static uint16_t get_pkg_power(void) -+{ -+	uint8_t rapl_power_unit = rdmsr(0x606).lo & 0xf; -+	if (rapl_power_unit) -+		rapl_power_unit = 2 << (rapl_power_unit - 1); -+	uint16_t pkg_power_info = rdmsr(0x614).lo & 0x7fff; -+	if (pkg_power_info / rapl_power_unit > 0x41) -+		return 32; -+	else -+		return 16; -+} -+ -+static void apply_hwm_tab(struct hwm_tab_entry *arr, size_t size) -+{ -+	uint8_t temp_target = get_temp_target(); -+	uint16_t pkg_power = get_pkg_power(); -+ -+	printk(BIOS_DEBUG, "Temp target = %#x\n", temp_target); -+	printk(BIOS_DEBUG, "Package power = %#x\n", pkg_power); -+ -+	for (size_t i = 0; i < size; ++i) { -+		// Skip entry if it doesn't apply for this package power -+		if (arr[i].pkg_power != pkg_power && -+		    arr[i].pkg_power != HWM_TAB_PKG_POWER_ANY) -+			continue; -+ -+		uint8_t val = arr[i].val; -+ -+		// Add temp target to value if requested (current tables never do) -+		if (arr[i].flags & HWM_TAB_ADD_TEMP_TARGET) -+			val += temp_target; -+ -+		// Perform write -+		ec_write(1, arr[i].addr, val); -+ -+	} -+} -+ -+static void sch5555_ec_hwm_init(void *arg) -+{ -+	uint8_t chassis_type, saved_2fc; -+ -+	printk(BIOS_DEBUG, "OptiPlex 9020 late HWM init\n"); -+ -+	saved_2fc = ec_read(1, 0x2fc); -+	ec_write(1, 0x2fc, 0xa0); -+	ec_write(1, 0x2fd, 0x32); -+ -+	chassis_type = get_chassis_type(); -+ -+	if (chassis_type != CHASSIS_TYPE_UNKNOWN) { -+		printk(BIOS_DEBUG, "Chassis type = %#x\n", chassis_type); -+	} else { -+		printk(BIOS_DEBUG, "WARNING: Unknown chassis type\n"); -+	} -+ -+	// Apply HWM table based on chassis type -+	switch (chassis_type) { -+	case 3: -+		apply_hwm_tab(HWM_TAB3, ARRAY_SIZE(HWM_TAB3)); -+		break; -+	case 4: -+		apply_hwm_tab(HWM_TAB4, ARRAY_SIZE(HWM_TAB4)); -+		break; -+	case 5: -+		apply_hwm_tab(HWM_TAB5, ARRAY_SIZE(HWM_TAB5)); -+		break; -+	case 6: -+		apply_hwm_tab(HWM_TAB6, ARRAY_SIZE(HWM_TAB6)); -+		break; -+	} -+ -+	// NOTE: vendor firmware applies these when "max core address" > 2 -+	// i think this is always the case -+	ec_write(1, 0x9e, 0x30); -+	ec_write(1, 0xeb, ec_read(1, 0xea)); -+ -+	ec_write(1, 0x2fc, saved_2fc); -+ -+	// Apply full speed fan config if requested or if the chassis type is unknown -+	if (chassis_type == CHASSIS_TYPE_UNKNOWN || get_uint_option("fan_full_speed", 0)) { -+		printk(BIOS_DEBUG, "Setting full fan speed\n"); -+		ec_write(1, 0x80, 0x60 | ec_read(1, 0x80)); -+		ec_write(1, 0x81, 0x60 | ec_read(1, 0x81)); -+	} -+ -+	ec_read(1, 0xb8); -+ -+	if ((chassis_type == 4 || chassis_type == 5) && ec_read(1, 0x26) == 0) { -+		ec_write(1, 0xa0, ec_read(1, 0xa0) & 0xfb); -+		ec_write(1, 0xa1, ec_read(1, 0xa1) & 0xfb); -+		ec_write(1, 0xa2, ec_read(1, 0xa2) & 0xfb); -+		ec_write(1, 0x8a, 0x99); -+		ec_write(1, 0x8b, 0x47); -+		ec_write(1, 0x8c, 0x91); -+	} -+} -+ -+BOOT_STATE_INIT_ENTRY(BS_POST_DEVICE, BS_ON_EXIT, sch5555_ec_hwm_init, NULL); -diff --git a/src/mainboard/dell/optiplex_9020/sch5555_ec.c b/src/mainboard/dell/optiplex_9020/sch5555_ec.c -new file mode 100644 -index 0000000000..a1067ac063 ---- /dev/null -+++ b/src/mainboard/dell/optiplex_9020/sch5555_ec.c -@@ -0,0 +1,54 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+ -+#include <arch/io.h> -+#include <device/pnp_ops.h> -+#include <superio/smsc/sch555x/sch555x.h> -+#include "sch5555_ec.h" -+ -+uint8_t ec_read(uint8_t addr1, uint16_t addr2) -+{ -+	// clear ec-to-host mailbox -+	uint8_t tmp = inb(SCH555x_EMI_IOBASE + 1); -+	outb(tmp, SCH555x_EMI_IOBASE + 1); -+ -+	// send address -+	outw(0 | 0x8001, SCH555x_EMI_IOBASE + 2); -+	outw((addr1 * 2) | 0x100, SCH555x_EMI_IOBASE + 4); -+ -+	outw(4 | 0x8002, SCH555x_EMI_IOBASE + 2); -+	outl(addr2 << 16, SCH555x_EMI_IOBASE + 4); -+ -+	// send message to ec -+	outb(1, SCH555x_EMI_IOBASE); -+ -+	// wait for ack -+	for (size_t retry = 0; retry < 0xfff; ++retry) -+		if (inb(SCH555x_EMI_IOBASE + 1) & 1) -+			break; -+ -+	// read result -+	outw(4 | 0x8000, SCH555x_EMI_IOBASE + 2); -+	return inb(SCH555x_EMI_IOBASE + 4); -+} -+ -+void ec_write(uint8_t addr1, uint16_t addr2, uint8_t val) -+{ -+	// clear ec-to-host mailbox -+	uint8_t tmp = inb(SCH555x_EMI_IOBASE + 1); -+	outb(tmp, SCH555x_EMI_IOBASE + 1); -+ -+	// send address and value -+	outw(0 | 0x8001, SCH555x_EMI_IOBASE + 2); -+	outw((addr1 * 2) | 0x101, SCH555x_EMI_IOBASE + 4); -+ -+	outw(4 | 0x8002, SCH555x_EMI_IOBASE + 2); -+	outl(val | (addr2 << 16), SCH555x_EMI_IOBASE + 4); -+ -+	// send message to ec -+	outb(1, SCH555x_EMI_IOBASE); -+ -+	// wait for ack -+	for (size_t retry = 0; retry < 0xfff; ++retry) -+		if (inb(SCH555x_EMI_IOBASE + 1) & 1) -+			break; -+} -diff --git a/src/mainboard/dell/optiplex_9020/sch5555_ec.h b/src/mainboard/dell/optiplex_9020/sch5555_ec.h -new file mode 100644 -index 0000000000..7e399e8e74 ---- /dev/null -+++ b/src/mainboard/dell/optiplex_9020/sch5555_ec.h -@@ -0,0 +1,10 @@ -+/* SPDX-License-Identifier: GPL-2.0-only */ -+ -+#ifndef __SCH5555_EC_H__ -+#define __SCH5555_EC_H__ -+ -+uint8_t ec_read(uint8_t addr1, uint16_t addr2); -+ -+void ec_write(uint8_t addr1, uint16_t addr2, uint8_t val); -+ -+#endif ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0024-nb-haswell-Fully-disable-iGPU-when-dGPU-is-used.patch b/config/coreboot/haswell/patches/0024-nb-haswell-Fully-disable-iGPU-when-dGPU-is-used.patch deleted file mode 100644 index fb112f8c..00000000 --- a/config/coreboot/haswell/patches/0024-nb-haswell-Fully-disable-iGPU-when-dGPU-is-used.patch +++ /dev/null @@ -1,51 +0,0 @@ -From ae494dc1b1dde92ec42390b85ced0ffe816f5110 Mon Sep 17 00:00:00 2001 -From: Leah Rowe <info@minifree.org> -Date: Sat, 6 Apr 2024 01:22:47 +0100 -Subject: [PATCH 4/4] nb/haswell: Fully disable iGPU when dGPU is used - -My earlier patch disabled decode *and* disabled the iGPU itself, but -a subsequent revision disabled only VGA decode. Upon revisiting, I -found that, actually, yes, you also need to disable the iGPU entirely. - -Tested on Dell 9020 SFF using broadwell MRC, with both iGPU and dGPU. -With this patch, the iGPU is completely disabled when you install a -graphics card, but the iGPU is available to use when no graphics card -is present. - -For more context, see: - -Author: Leah Rowe <info@minifree.org> -Date:   Fri Feb 23 13:33:31 2024 +0000 - -    nb/haswell: Disable iGPU when dGPU is used - -And look at the Gerrit comments: - -https://review.coreboot.org/c/coreboot/+/80717/ - -So, my original submission on change 80717 was actually correct. -This patch fixes the issue. I tested on iGPU and dGPU, with both -broadwell and haswell mrc.bin. - -Signed-off-by: Leah Rowe <info@minifree.org> ---- - src/northbridge/intel/haswell/gma.c | 3 +++ - 1 file changed, 3 insertions(+) - -diff --git a/src/northbridge/intel/haswell/gma.c b/src/northbridge/intel/haswell/gma.c -index 9e9f9804f5..526a51aff0 100644 ---- a/src/northbridge/intel/haswell/gma.c -+++ b/src/northbridge/intel/haswell/gma.c -@@ -464,6 +464,9 @@ static void gma_func0_disable(struct device *dev) - { - 	/* Disable VGA decode */ - 	pci_or_config16(pcidev_on_root(0, 0), GGC, 1 << 1); -+ -+	/* Required or else the graphics card doesn't work */ -+	dev->enabled = 0; - } -  - static struct device_operations gma_func0_ops = { ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0025-mb-dell-optiplex_9020-Add-support-for-TPM1.2-device.patch b/config/coreboot/haswell/patches/0025-mb-dell-optiplex_9020-Add-support-for-TPM1.2-device.patch deleted file mode 100644 index 52147426..00000000 --- a/config/coreboot/haswell/patches/0025-mb-dell-optiplex_9020-Add-support-for-TPM1.2-device.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 355536155898e649fa50277136ccd2df53a52bb1 Mon Sep 17 00:00:00 2001 -From: Mate Kukri <kukri.mate@gmail.com> -Date: Wed, 10 Apr 2024 20:31:35 +0100 -Subject: [PATCH 1/1] mb/dell/optiplex_9020: Add support for TPM1.2 device - -These machines come with a TPM1.2 device by default. It is somewhat -obsolete these days, but there is no harm in enabling it. - -Change-Id: Iec05321862aed58695c256b00494e5953219786d -Signed-off-by: Mate Kukri <kukri.mate@gmail.com> -Reviewed-on: https://review.coreboot.org/c/coreboot/+/81827 -Reviewed-by: Angel Pons <th3fanbus@gmail.com> -Tested-by: build bot (Jenkins) <no-reply@coreboot.org> ---- - src/mainboard/dell/optiplex_9020/Kconfig       | 2 ++ - src/mainboard/dell/optiplex_9020/devicetree.cb | 3 +++ - 2 files changed, 5 insertions(+) - -diff --git a/src/mainboard/dell/optiplex_9020/Kconfig b/src/mainboard/dell/optiplex_9020/Kconfig -index 2de4a9abd6..38c3281e70 100644 ---- a/src/mainboard/dell/optiplex_9020/Kconfig -+++ b/src/mainboard/dell/optiplex_9020/Kconfig -@@ -12,7 +12,9 @@ config BOARD_SPECIFIC_OPTIONS - 	select INTEL_GMA_HAVE_VBT - 	select INTEL_INT15 - 	select MAINBOARD_HAS_LIBGFXINIT -+	select MAINBOARD_HAS_TPM1 - 	select MAINBOARD_USES_IFD_GBE_REGION -+	select MEMORY_MAPPED_TPM - 	select NORTHBRIDGE_INTEL_HASWELL - 	select SERIRQ_CONTINUOUS_MODE - 	select SOUTHBRIDGE_INTEL_LYNXPOINT -diff --git a/src/mainboard/dell/optiplex_9020/devicetree.cb b/src/mainboard/dell/optiplex_9020/devicetree.cb -index dce5869478..841285bb9c 100644 ---- a/src/mainboard/dell/optiplex_9020/devicetree.cb -+++ b/src/mainboard/dell/optiplex_9020/devicetree.cb -@@ -70,6 +70,9 @@ chip northbridge/intel/haswell - 					device pnp 2e.b  off end	# Floppy Controller - 					device pnp 2e.11 off end	# Parallel Port - 				end -+				chip drivers/pc80/tpm -+					device pnp 0c31.0 on end -+				end - 			end - 			device pci 1f.2 on  end				# SATA controller 1 - 			device pci 1f.3 on  end				# SMBus ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0026-use-mirrorservice.org-for-iasl-downloads.patch b/config/coreboot/haswell/patches/0026-use-mirrorservice.org-for-iasl-downloads.patch deleted file mode 100644 index f9981abd..00000000 --- a/config/coreboot/haswell/patches/0026-use-mirrorservice.org-for-iasl-downloads.patch +++ /dev/null @@ -1,28 +0,0 @@ -From ed30cca924fa576dd5b69ce4a348b5a1466a8db1 Mon Sep 17 00:00:00 2001 -From: Leah Rowe <info@minifree.org> -Date: Sun, 28 Apr 2024 01:57:46 +0100 -Subject: [PATCH 1/1] use mirrorservice.org for iasl downloads - -github is unreliable. i mirror these files myself. - -Signed-off-by: Leah Rowe <info@minifree.org> ---- - util/crossgcc/buildgcc | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/util/crossgcc/buildgcc b/util/crossgcc/buildgcc -index 6ae201239d..a8433a25e5 100755 ---- a/util/crossgcc/buildgcc -+++ b/util/crossgcc/buildgcc -@@ -74,7 +74,7 @@ MPFR_BASE_URL="https://www.mirrorservice.org/sites/ftp.gnu.org/gnu/mpfr" - MPC_BASE_URL="https://www.mirrorservice.org/sites/ftp.gnu.org/gnu/mpc" - GCC_BASE_URL="https://www.mirrorservice.org/sites/ftp.gnu.org/gnu/gcc/gcc-${GCC_VERSION}" - BINUTILS_BASE_URL="https://www.mirrorservice.org/sites/ftp.gnu.org/gnu/binutils" --IASL_BASE_URL="https://github.com/acpica/acpica/archive/refs/tags" -+IASL_BASE_URL="https://www.mirrorservice.org/sites/libreboot.org/release/misc/acpica" - # CLANG toolchain archive locations - LLVM_BASE_URL="https://github.com/llvm/llvm-project/releases/download/llvmorg-${CLANG_VERSION}" - CLANG_BASE_URL="https://github.com/llvm/llvm-project/releases/download/llvmorg-${CLANG_VERSION}" ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0027-nb-haswell-lock-policy-regs-when-disabling-IOMMU.patch b/config/coreboot/haswell/patches/0027-nb-haswell-lock-policy-regs-when-disabling-IOMMU.patch deleted file mode 100644 index 9b6020a2..00000000 --- a/config/coreboot/haswell/patches/0027-nb-haswell-lock-policy-regs-when-disabling-IOMMU.patch +++ /dev/null @@ -1,55 +0,0 @@ -From b75d9e385137b3b561fc7220c04f742817d319b2 Mon Sep 17 00:00:00 2001 -From: Leah Rowe <info@minifree.org> -Date: Sat, 4 May 2024 02:00:53 +0100 -Subject: [PATCH 1/1] nb/haswell: lock policy regs when disabling IOMMU - -Angel Pons told me I should do it. See comments here: -https://review.coreboot.org/c/coreboot/+/81016 - -I see no harm in complying with the request. I'll merge -this into the main patch at a later date and try to -get this upstreamed. - -Just a reminder: on Optiplex 9020 variants, Xorg locks up -under Linux when tested with a graphics card; disabling -IOMMU works around the issue. Intel graphics work just fine -with IOMMU turned on. Libreboot disables IOMMU by default, -on the 9020, so that users can install graphics cards easily. - -Signed-off-by: Leah Rowe <info@minifree.org> ---- - src/northbridge/intel/haswell/early_init.c | 15 +++++++-------- - 1 file changed, 7 insertions(+), 8 deletions(-) - -diff --git a/src/northbridge/intel/haswell/early_init.c b/src/northbridge/intel/haswell/early_init.c -index 1a7e0b1076..e9506ee830 100644 ---- a/src/northbridge/intel/haswell/early_init.c -+++ b/src/northbridge/intel/haswell/early_init.c -@@ -160,17 +160,16 @@ static void haswell_setup_iommu(void) - 	const u32 capid0_a = pci_read_config32(HOST_BRIDGE, CAPID0_A); - 	u8 enable_iommu = get_uint_option("iommu", 1); -  --	if (!enable_iommu) --		return; -- - 	if (capid0_a & VTD_DISABLE) - 		return; -  --	/* Setup BARs: zeroize top 32 bits; set enable bit */ --	mchbar_write32(GFXVTBAR + 4, GFXVT_BASE_ADDRESS >> 32); --	mchbar_write32(GFXVTBAR + 0, GFXVT_BASE_ADDRESS | 1); --	mchbar_write32(VTVC0BAR + 4, VTVC0_BASE_ADDRESS >> 32); --	mchbar_write32(VTVC0BAR + 0, VTVC0_BASE_ADDRESS | 1); -+	if (enable_iommu) { -+		/* Setup BARs: zeroize top 32 bits; set enable bit */ -+		mchbar_write32(GFXVTBAR + 4, GFXVT_BASE_ADDRESS >> 32); -+		mchbar_write32(GFXVTBAR + 0, GFXVT_BASE_ADDRESS | 1); -+		mchbar_write32(VTVC0BAR + 4, VTVC0_BASE_ADDRESS >> 32); -+		mchbar_write32(VTVC0BAR + 0, VTVC0_BASE_ADDRESS | 1); -+	} -  - 	/* Set L3HIT2PEND_DIS, lock GFXVTBAR policy config registers */ - 	u32 reg32; ---  -2.39.2 - diff --git a/config/coreboot/haswell/patches/0028-nb-intel-haswell-Synchronize-lists-of-graphics-PCI-I.patch b/config/coreboot/haswell/patches/0028-nb-intel-haswell-Synchronize-lists-of-graphics-PCI-I.patch deleted file mode 100644 index 422b291e..00000000 --- a/config/coreboot/haswell/patches/0028-nb-intel-haswell-Synchronize-lists-of-graphics-PCI-I.patch +++ /dev/null @@ -1,44 +0,0 @@ -From bfb39806c9edbfee7383c99a73e228a5314ee2c2 Mon Sep 17 00:00:00 2001 -From: Nico Huber <nico.h@gmx.de> -Date: Mon, 03 Jun 2024 20:08:26 +0200 -Subject: [PATCH] nb/intel/haswell: Synchronize lists of graphics PCI IDs - -Both, the list of IDs that we hooked our driver up to and the list -that we use for VBIOS mapping, had gaps. Fill those. - -Change-Id: I97c09bb113cf0f35ae158abbd0ba2632dbad7cad -Signed-off-by: Nico Huber <nico.h@gmx.de> -Reviewed-on: https://review.coreboot.org/c/coreboot/+/82787 -Tested-by: build bot (Jenkins) <no-reply@coreboot.org> -Reviewed-by: Angel Pons <th3fanbus@gmail.com> ---- - -diff --git a/src/northbridge/intel/haswell/gma.c b/src/northbridge/intel/haswell/gma.c -index 9e9f980..f7fad31 100644 ---- a/src/northbridge/intel/haswell/gma.c -+++ b/src/northbridge/intel/haswell/gma.c -@@ -93,12 +93,14 @@ - 	case 0x80860406:		/* GT1 Mobile */ - 	case 0x8086040a:		/* GT1 Server */ - 	case 0x80860a06:		/* GT1 ULT */ -+	case 0x80860a0e:		/* GT1 ULX */ -  - 	case 0x80860412:		/* GT2 Desktop */ - 	case 0x80860416:		/* GT2 Mobile */ - 	case 0x8086041a:		/* GT2 Server */ - 	case 0x8086041e:		/* GT1.5 Desktop */ - 	case 0x80860a16:		/* GT2 ULT */ -+	case 0x80860a1e:		/* GT2 ULX */ -  - 	case 0x80860422:		/* GT3 Desktop */ - 	case 0x80860426:		/* GT3 Mobile */ -@@ -485,6 +487,9 @@ - 	0x0406, /* Mobile GT1 */ - 	0x0416, /* Mobile GT2 */ - 	0x0426, /* Mobile GT3 */ -+	0x040a, /* Server GT1 */ -+	0x041a, /* Server GT2 */ -+	0x042a, /* Server GT3 */ - 	0x0d16, /* Mobile 4+3 GT1 */ - 	0x0d26, /* Mobile 4+3 GT2, Mobile GT3e */ - 	0x0d36, /* Mobile 4+3 GT3 */ diff --git a/config/coreboot/haswell/patches/0029-mb-dell-optiplex_9020-Fix-integrated-video-port-list.patch b/config/coreboot/haswell/patches/0029-mb-dell-optiplex_9020-Fix-integrated-video-port-list.patch deleted file mode 100644 index b599e236..00000000 --- a/config/coreboot/haswell/patches/0029-mb-dell-optiplex_9020-Fix-integrated-video-port-list.patch +++ /dev/null @@ -1,30 +0,0 @@ -From c8929f5fb43b6e9b491ae00885be507d01b2a896 Mon Sep 17 00:00:00 2001 -From: Mate Kukri <kukri.mate@gmail.com> -Date: Tue, 11 Jun 2024 20:26:16 +0100 -Subject: [PATCH] mb/dell/optiplex_9020: Fix integrated video port list - -- Physical DP ports are DP2/DP3 (HDMI2/HDMI3 for DP++) -- VGA port is Analog -- DP1 is not connected - -Signed-off-by: Mate Kukri <kukri.mate@gmail.com> -Change-Id: I8ed79167d5445d607acbee491c3382ff2585583f ---- - -diff --git a/src/mainboard/dell/optiplex_9020/gma-mainboard.ads b/src/mainboard/dell/optiplex_9020/gma-mainboard.ads -index 173f2f1..7d95061 100644 ---- a/src/mainboard/dell/optiplex_9020/gma-mainboard.ads -+++ b/src/mainboard/dell/optiplex_9020/gma-mainboard.ads -@@ -9,9 +9,10 @@ - private package GMA.Mainboard is -  -    ports : constant Port_List := --     (DP1, --      DP2, -+     (DP2, -       DP3, -+      HDMI2, -+      HDMI3, -       Analog, -       others => Disabled); -  diff --git a/config/coreboot/haswell/target.cfg b/config/coreboot/haswell/target.cfg deleted file mode 100644 index 69f9bd39..00000000 --- a/config/coreboot/haswell/target.cfg +++ /dev/null @@ -1,2 +0,0 @@ -tree="haswell" -rev="b7341da19133991efd29880849bdaab29a6e243d" | 
