summaryrefslogtreecommitdiff
path: root/config/coreboot/haswell/patches
diff options
context:
space:
mode:
authorLeah Rowe <leah@libreboot.org>2024-08-10 14:43:19 +0100
committerLeah Rowe <leah@libreboot.org>2024-08-10 14:48:01 +0100
commit877f5d6aeb6a1a62f09c95cc214c874d057310d6 (patch)
tree4a40b0d1a0ec5edb5f501087e5ed881262b896b1 /config/coreboot/haswell/patches
parenta15347ef1e677ca711ce706877db2416ddfd451a (diff)
coreboot/default: merge coreboot/haswell
Signed-off-by: Leah Rowe <leah@libreboot.org>
Diffstat (limited to 'config/coreboot/haswell/patches')
-rw-r--r--config/coreboot/haswell/patches/0001-haswell-NRI-Initialise-MPLL.patch348
-rw-r--r--config/coreboot/haswell/patches/0002-haswell-NRI-Post-process-selected-timings.patch249
-rw-r--r--config/coreboot/haswell/patches/0003-haswell-NRI-Configure-initial-MC-settings.patch1593
-rw-r--r--config/coreboot/haswell/patches/0004-haswell-NRI-Add-timings-refresh-programming.patch541
-rw-r--r--config/coreboot/haswell/patches/0005-haswell-NRI-Program-memory-map.patch263
-rw-r--r--config/coreboot/haswell/patches/0006-haswell-NRI-Add-DDR3-JEDEC-reset-and-init.patch1036
-rw-r--r--config/coreboot/haswell/patches/0007-haswell-NRI-Add-pre-training-steps.patch392
-rw-r--r--config/coreboot/haswell/patches/0008-haswell-NRI-Add-REUT-I-O-test-library.patch1130
-rw-r--r--config/coreboot/haswell/patches/0009-haswell-NRI-Add-range-tracking-library.patch222
-rw-r--r--config/coreboot/haswell/patches/0010-haswell-NRI-Add-library-to-change-margins.patch294
-rw-r--r--config/coreboot/haswell/patches/0011-haswell-NRI-Add-RcvEn-training.patch708
-rw-r--r--config/coreboot/haswell/patches/0012-haswell-NRI-Add-function-to-change-margins.patch272
-rw-r--r--config/coreboot/haswell/patches/0013-haswell-NRI-Add-read-MPR-training.patch332
-rw-r--r--config/coreboot/haswell/patches/0014-haswell-NRI-Add-write-leveling.patch689
-rw-r--r--config/coreboot/haswell/patches/0015-haswell-NRI-Add-final-raminit-steps.patch570
-rw-r--r--config/coreboot/haswell/patches/0016-Haswell-NRI-Implement-fast-boot-path.patch722
-rw-r--r--config/coreboot/haswell/patches/0017-haswell-NRI-Do-sense-amplifier-offset-training.patch476
-rw-r--r--config/coreboot/haswell/patches/0018-Remove-warning-for-coreboot-images-built-without-a-p.patch39
-rw-r--r--config/coreboot/haswell/patches/0019-use-mirrorservice.org-for-gcc-downloads.patch36
-rw-r--r--config/coreboot/haswell/patches/0020-util-ifdtool-add-nuke-flag-all-0xFF-on-region.patch204
-rw-r--r--config/coreboot/haswell/patches/0021-nb-intel-haswell-make-IOMMU-a-runtime-option.patch292
-rw-r--r--config/coreboot/haswell/patches/0022-dell-optiplex_9020-Disable-IOMMU-by-default.patch29
-rw-r--r--config/coreboot/haswell/patches/0023-mb-dell-optiplex_9020-Implement-late-HWM-initializat.patch602
-rw-r--r--config/coreboot/haswell/patches/0024-nb-haswell-Fully-disable-iGPU-when-dGPU-is-used.patch51
-rw-r--r--config/coreboot/haswell/patches/0025-mb-dell-optiplex_9020-Add-support-for-TPM1.2-device.patch49
-rw-r--r--config/coreboot/haswell/patches/0026-use-mirrorservice.org-for-iasl-downloads.patch28
-rw-r--r--config/coreboot/haswell/patches/0027-nb-haswell-lock-policy-regs-when-disabling-IOMMU.patch55
-rw-r--r--config/coreboot/haswell/patches/0028-nb-intel-haswell-Synchronize-lists-of-graphics-PCI-I.patch44
-rw-r--r--config/coreboot/haswell/patches/0029-mb-dell-optiplex_9020-Fix-integrated-video-port-list.patch30
29 files changed, 0 insertions, 11296 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(
-+ &region_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 =
-+ &region_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(
-+ &region_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 = &region_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(
-+ &region_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 = &region_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 = &region_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 =
-+ &region_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 &register_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 = &reg_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 = &reg_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 = &reg_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);
-