+---
+ src/mainboard/dell/snb_ivb_latitude/Kconfig | 9 +
+ .../dell/snb_ivb_latitude/Kconfig.name | 3 +
+ .../snb_ivb_latitude/variants/e6220/data.vbt | Bin 0 -> 3985 bytes
+ .../variants/e6220/early_init.c | 14 ++
+ .../snb_ivb_latitude/variants/e6220/gpio.c | 192 ++++++++++++++++++
+ .../variants/e6220/hda_verb.c | 32 +++
+ .../variants/e6220/overridetree.cb | 37 ++++
+ 7 files changed, 287 insertions(+)
+ create mode 100644 src/mainboard/dell/snb_ivb_latitude/variants/e6220/data.vbt
+ create mode 100644 src/mainboard/dell/snb_ivb_latitude/variants/e6220/early_init.c
+ create mode 100644 src/mainboard/dell/snb_ivb_latitude/variants/e6220/gpio.c
+ create mode 100644 src/mainboard/dell/snb_ivb_latitude/variants/e6220/hda_verb.c
+ create mode 100644 src/mainboard/dell/snb_ivb_latitude/variants/e6220/overridetree.cb
+
+diff --git a/src/mainboard/dell/snb_ivb_latitude/Kconfig b/src/mainboard/dell/snb_ivb_latitude/Kconfig
+index 84ffe1d33a..baa83baa41 100644
+--- a/src/mainboard/dell/snb_ivb_latitude/Kconfig
++++ b/src/mainboard/dell/snb_ivb_latitude/Kconfig
+@@ -27,6 +27,12 @@ config BOARD_DELL_LATITUDE_E5520
+ select BOARD_ROMSIZE_KB_6144
+ select SOUTHBRIDGE_INTEL_BD82X6X
+
++config BOARD_DELL_LATITUDE_E6220
++ select BOARD_DELL_SNB_IVB_LATITUDE_COMMON
++ select BOARD_ROMSIZE_KB_10240
++ select MAINBOARD_USES_IFD_GBE_REGION
++ select SOUTHBRIDGE_INTEL_BD82X6X
++
+ config BOARD_DELL_LATITUDE_E6320
+ select BOARD_DELL_SNB_IVB_LATITUDE_COMMON
+ select BOARD_ROMSIZE_KB_10240
+@@ -73,6 +79,7 @@ config MAINBOARD_DIR
+ config MAINBOARD_PART_NUMBER
+ default "Latitude E5420" if BOARD_DELL_LATITUDE_E5420
+ default "Latitude E5520" if BOARD_DELL_LATITUDE_E5520
++ default "Latitude E6220" if BOARD_DELL_LATITUDE_E6220
+ default "Latitude E6320" if BOARD_DELL_LATITUDE_E6320
+ default "Latitude E6420" if BOARD_DELL_LATITUDE_E6420
+ default "Latitude E6520" if BOARD_DELL_LATITUDE_E6520
+@@ -89,6 +96,7 @@ config USBDEBUG_HCD_INDEX
+ config VARIANT_DIR
+ default "e5420" if BOARD_DELL_LATITUDE_E5420
+ default "e5520" if BOARD_DELL_LATITUDE_E5520
++ default "e6220" if BOARD_DELL_LATITUDE_E6220
+ default "e6320" if BOARD_DELL_LATITUDE_E6320
+ default "e6420" if BOARD_DELL_LATITUDE_E6420
+ default "e6520" if BOARD_DELL_LATITUDE_E6520
+@@ -102,6 +110,7 @@ config VGA_BIOS_ID
+ default "8086,0166" if BOARD_DELL_LATITUDE_E5530
+ default "8086,0126" if BOARD_DELL_LATITUDE_E6420 \
+ || BOARD_DELL_LATITUDE_E5520 \
++ || BOARD_DELL_LATITUDE_E6220 \
+ || BOARD_DELL_LATITUDE_E6320
+ default "8086,0166" if BOARD_DELL_LATITUDE_E6430 \
+ || BOARD_DELL_LATITUDE_E6530
+diff --git a/src/mainboard/dell/snb_ivb_latitude/Kconfig.name b/src/mainboard/dell/snb_ivb_latitude/Kconfig.name
+index ef6a1329a9..349ee7f79e 100644
+--- a/src/mainboard/dell/snb_ivb_latitude/Kconfig.name
++++ b/src/mainboard/dell/snb_ivb_latitude/Kconfig.name
+@@ -6,6 +6,9 @@ config BOARD_DELL_LATITUDE_E5420
+ config BOARD_DELL_LATITUDE_E5520
+ bool "Latitude E5520"
+
++config BOARD_DELL_LATITUDE_E6220
++ bool "Latitude E6220"
++
+ config BOARD_DELL_LATITUDE_E6320
+ bool "Latitude E6320"
+
+diff --git a/src/mainboard/dell/snb_ivb_latitude/variants/e6220/data.vbt b/src/mainboard/dell/snb_ivb_latitude/variants/e6220/data.vbt
+new file mode 100644
+index 0000000000000000000000000000000000000000..548075a74500b5d159108089ee29cff802d07db7
+GIT binary patch
+literal 3985
+zcmdT{eP|p-6#wn*-rZ(yH@R-odPzTgZQ6LXjonL|7&YQ0xu#d`$C=h}21|4GP8*0x
+zyjE@hv0Dv(P?c0g{G(_DMJZ@22r8oZ0U9lcR8a)~s33wOSg|T<^?b8?XBzL?#)6`A
+z{N~Nfd-LYan>TOv7WZ{+rcIq264!S1u1&02-MpSC3mf}u-r~BvbgkXEX=|c$bLZBs
+zbsM{{q9-s1nVR3f2C|A`nJsqvC7UwC+1=angV`H%w4saofx<^KL=Lc`x
+zzLTQeOW7vdZsuwwtsUOU>vxajM=zqzp&{y(GCQa@weIb%fzF6`uwy)UhaB+ynus
+zBRr-~^|__t=m5fD9tR81r@XLV3UEc-2I6>o`;@@MXS$rj)&)r+pA?|K2vh+9SHM=N
+zw3d{Uh1~iK)juV`E`v4?cFU@^_DehBU5TFLmFrTyoBPuJ*ExIdxO1!lC!eceSG8i}
+z&Aivl*T2}Cf;*vEJvsN-nR!WWDm8M^Zmcltu96wMRudvHXLxn;xh~EqEM^Gr}m&=vHbwRKjl{%)fM2d8tOI4MM{l!dK
+z4$)%2P!LDJaqX2t;s4$Wy@Q1gZ=x97KlhsvF9`g6&TYocZ_JQN=A1hUE#+kAD@E9jJd7%}~
+zMLIYMDVoel8h1}$+_YJF%DJ&-O)X~`ZoroouO-yDsj)OrPU{{+ph4LJKdD;Bi3a3T
+zbe?Tf8&elW+H+t;5$y~|nhq{o@?k1^-Hg%jhcu{xJyzvgk`0m*Te#GQe$
+z2IjOP{U&oF$`&WsuJN2!=fTnT^W)PwhnW-Ya3)3%H!`OUfy6?#V9r%+wCY}TU0!Cl
+z*kjeex}MZl_aWVoxhXfp&Ur~>>k;onlO4II%~KY!FT|r)!;agdwcf~rXIAVwc6CEj
+zJpE{CBzZ;L-gdYp9)GC8f@wZ%=YBfkLb0_gZP%us?_tgG3TXJ7BDbWb~V23Mt{QT(?mOc#ihbo#YtY#rD
+z7PLiJBSP#J^tiB|7vdE`p9|}IA$}9o7_wSJ;))RELe^~|u{T6dhpd-F;;j&U6|#N}
+z3BN(h4C``3tTE^&!`fqrdks2dSZ^5Oh(X^Omdc+rCapBB)uz~J(k-TSw<-3U^rC6K
+zYl;s|`q{KX)nazFdEs%*@f}l~SsY?~kb2(WgGl=fm!43>4I&2(k1$iaK?FYVZ}~h~1{1T|;>=%b4`yk3XF>siEVHyC@HS
+z8OvVW%DeB`K&~H7>f?&^gQU_A0oM;p0b*k61j!x*S5X(-unS`9rZ
+zG}=vb+R*x})DSq-Q7;uJwKLPuG`Ej6G}#nch4dSqhHo0B2Gq%HbgCyS+pwZ3{?fph
+z!Jo*Dxcw7v7a#rIU2IRmVn4KE$x~88+a7J4zd|_!%xo9z$+P;Q6qA*AQ5FvzlPW^f
+zY&aJUhO1#_o~&$x>1qJKGpC+K<(u_&1197VVWxipk10ERAEpLG3^|JWI~DYG*_Qk)fwt)g^KZ*f*K5tEj9C7Ea`HGyPe8U4wd
+nX2Iz@%Q6UTm;}-X%j^D0i1fiT)I6)4TdrsMY}`L(
++#include
++#include
++#include
++
++void bootblock_mainboard_early_init(void)
++{
++ pci_write_config16(PCH_LPC_DEV, LPC_EN, CNF1_LPC_EN | MC_LPC_EN
++ | KBC_LPC_EN | FDD_LPC_EN | LPT_LPC_EN
++ | COMB_LPC_EN | COMA_LPC_EN);
++ mec5035_early_init();
++}
+diff --git a/src/mainboard/dell/snb_ivb_latitude/variants/e6220/gpio.c b/src/mainboard/dell/snb_ivb_latitude/variants/e6220/gpio.c
+new file mode 100644
+index 0000000000..2306e4cf0a
+--- /dev/null
++++ b/src/mainboard/dell/snb_ivb_latitude/variants/e6220/gpio.c
+@@ -0,0 +1,192 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++
++#include
++
++static const struct pch_gpio_set1 pch_gpio_set1_mode = {
++ .gpio0 = GPIO_MODE_GPIO,
++ .gpio1 = GPIO_MODE_GPIO,
++ .gpio2 = GPIO_MODE_GPIO,
++ .gpio3 = GPIO_MODE_NATIVE,
++ .gpio4 = GPIO_MODE_GPIO,
++ .gpio5 = GPIO_MODE_NATIVE,
++ .gpio6 = GPIO_MODE_GPIO,
++ .gpio7 = GPIO_MODE_GPIO,
++ .gpio8 = GPIO_MODE_GPIO,
++ .gpio9 = GPIO_MODE_NATIVE,
++ .gpio10 = GPIO_MODE_NATIVE,
++ .gpio11 = GPIO_MODE_NATIVE,
++ .gpio12 = GPIO_MODE_NATIVE,
++ .gpio13 = GPIO_MODE_GPIO,
++ .gpio14 = GPIO_MODE_GPIO,
++ .gpio15 = GPIO_MODE_GPIO,
++ .gpio16 = GPIO_MODE_GPIO,
++ .gpio17 = GPIO_MODE_GPIO,
++ .gpio18 = GPIO_MODE_NATIVE,
++ .gpio19 = GPIO_MODE_GPIO,
++ .gpio20 = GPIO_MODE_NATIVE,
++ .gpio21 = GPIO_MODE_GPIO,
++ .gpio22 = GPIO_MODE_GPIO,
++ .gpio23 = GPIO_MODE_NATIVE,
++ .gpio24 = GPIO_MODE_GPIO,
++ .gpio25 = GPIO_MODE_NATIVE,
++ .gpio26 = GPIO_MODE_NATIVE,
++ .gpio27 = GPIO_MODE_GPIO,
++ .gpio28 = GPIO_MODE_GPIO,
++ .gpio29 = GPIO_MODE_GPIO,
++ .gpio30 = GPIO_MODE_GPIO,
++ .gpio31 = GPIO_MODE_NATIVE,
++};
++
++static const struct pch_gpio_set1 pch_gpio_set1_direction = {
++ .gpio0 = GPIO_DIR_INPUT,
++ .gpio1 = GPIO_DIR_INPUT,
++ .gpio2 = GPIO_DIR_INPUT,
++ .gpio4 = GPIO_DIR_INPUT,
++ .gpio6 = GPIO_DIR_INPUT,
++ .gpio7 = GPIO_DIR_INPUT,
++ .gpio8 = GPIO_DIR_INPUT,
++ .gpio13 = GPIO_DIR_INPUT,
++ .gpio14 = GPIO_DIR_INPUT,
++ .gpio15 = GPIO_DIR_INPUT,
++ .gpio16 = GPIO_DIR_INPUT,
++ .gpio17 = GPIO_DIR_INPUT,
++ .gpio19 = GPIO_DIR_INPUT,
++ .gpio21 = GPIO_DIR_INPUT,
++ .gpio22 = GPIO_DIR_INPUT,
++ .gpio24 = GPIO_DIR_INPUT,
++ .gpio27 = GPIO_DIR_INPUT,
++ .gpio28 = GPIO_DIR_INPUT,
++ .gpio29 = GPIO_DIR_INPUT,
++ .gpio30 = GPIO_DIR_OUTPUT,
++};
++
++static const struct pch_gpio_set1 pch_gpio_set1_level = {
++ .gpio30 = GPIO_LEVEL_HIGH,
++};
++
++static const struct pch_gpio_set1 pch_gpio_set1_reset = {
++};
++
++static const struct pch_gpio_set1 pch_gpio_set1_invert = {
++ .gpio0 = GPIO_INVERT,
++ .gpio1 = GPIO_INVERT,
++ .gpio8 = GPIO_INVERT,
++ .gpio14 = GPIO_INVERT,
++};
++
++static const struct pch_gpio_set1 pch_gpio_set1_blink = {
++};
++
++static const struct pch_gpio_set2 pch_gpio_set2_mode = {
++ .gpio32 = GPIO_MODE_NATIVE,
++ .gpio33 = GPIO_MODE_GPIO,
++ .gpio34 = GPIO_MODE_GPIO,
++ .gpio35 = GPIO_MODE_GPIO,
++ .gpio36 = GPIO_MODE_GPIO,
++ .gpio37 = GPIO_MODE_GPIO,
++ .gpio38 = GPIO_MODE_GPIO,
++ .gpio39 = GPIO_MODE_GPIO,
++ .gpio40 = GPIO_MODE_NATIVE,
++ .gpio41 = GPIO_MODE_NATIVE,
++ .gpio42 = GPIO_MODE_NATIVE,
++ .gpio43 = GPIO_MODE_NATIVE,
++ .gpio44 = GPIO_MODE_NATIVE,
++ .gpio45 = GPIO_MODE_GPIO,
++ .gpio46 = GPIO_MODE_NATIVE,
++ .gpio47 = GPIO_MODE_NATIVE,
++ .gpio48 = GPIO_MODE_GPIO,
++ .gpio49 = GPIO_MODE_GPIO,
++ .gpio50 = GPIO_MODE_NATIVE,
++ .gpio51 = GPIO_MODE_GPIO,
++ .gpio52 = GPIO_MODE_GPIO,
++ .gpio53 = GPIO_MODE_NATIVE,
++ .gpio54 = GPIO_MODE_GPIO,
++ .gpio55 = GPIO_MODE_NATIVE,
++ .gpio56 = GPIO_MODE_NATIVE,
++ .gpio57 = GPIO_MODE_GPIO,
++ .gpio58 = GPIO_MODE_NATIVE,
++ .gpio59 = GPIO_MODE_NATIVE,
++ .gpio60 = GPIO_MODE_GPIO,
++ .gpio61 = GPIO_MODE_NATIVE,
++ .gpio62 = GPIO_MODE_NATIVE,
++ .gpio63 = GPIO_MODE_NATIVE,
++};
++
++static const struct pch_gpio_set2 pch_gpio_set2_direction = {
++ .gpio33 = GPIO_DIR_INPUT,
++ .gpio34 = GPIO_DIR_OUTPUT,
++ .gpio35 = GPIO_DIR_INPUT,
++ .gpio36 = GPIO_DIR_INPUT,
++ .gpio37 = GPIO_DIR_INPUT,
++ .gpio38 = GPIO_DIR_INPUT,
++ .gpio39 = GPIO_DIR_INPUT,
++ .gpio45 = GPIO_DIR_OUTPUT,
++ .gpio48 = GPIO_DIR_INPUT,
++ .gpio49 = GPIO_DIR_OUTPUT,
++ .gpio51 = GPIO_DIR_INPUT,
++ .gpio52 = GPIO_DIR_INPUT,
++ .gpio54 = GPIO_DIR_INPUT,
++ .gpio57 = GPIO_DIR_INPUT,
++ .gpio60 = GPIO_DIR_OUTPUT,
++};
++
++static const struct pch_gpio_set2 pch_gpio_set2_level = {
++ .gpio34 = GPIO_LEVEL_HIGH,
++ .gpio45 = GPIO_LEVEL_LOW,
++ .gpio49 = GPIO_LEVEL_LOW,
++ .gpio60 = GPIO_LEVEL_HIGH,
++};
++
++static const struct pch_gpio_set2 pch_gpio_set2_reset = {
++};
++
++static const struct pch_gpio_set3 pch_gpio_set3_mode = {
++ .gpio64 = GPIO_MODE_NATIVE,
++ .gpio65 = GPIO_MODE_NATIVE,
++ .gpio66 = GPIO_MODE_NATIVE,
++ .gpio67 = GPIO_MODE_NATIVE,
++ .gpio68 = GPIO_MODE_GPIO,
++ .gpio69 = GPIO_MODE_GPIO,
++ .gpio70 = GPIO_MODE_GPIO,
++ .gpio71 = GPIO_MODE_GPIO,
++ .gpio72 = GPIO_MODE_NATIVE,
++ .gpio73 = GPIO_MODE_NATIVE,
++ .gpio74 = GPIO_MODE_NATIVE,
++ .gpio75 = GPIO_MODE_NATIVE,
++};
++
++static const struct pch_gpio_set3 pch_gpio_set3_direction = {
++ .gpio68 = GPIO_DIR_INPUT,
++ .gpio69 = GPIO_DIR_INPUT,
++ .gpio70 = GPIO_DIR_INPUT,
++ .gpio71 = GPIO_DIR_INPUT,
++};
++
++static const struct pch_gpio_set3 pch_gpio_set3_level = {
++};
++
++static const struct pch_gpio_set3 pch_gpio_set3_reset = {
++};
++
++const struct pch_gpio_map mainboard_gpio_map = {
++ .set1 = {
++ .mode = &pch_gpio_set1_mode,
++ .direction = &pch_gpio_set1_direction,
++ .level = &pch_gpio_set1_level,
++ .blink = &pch_gpio_set1_blink,
++ .invert = &pch_gpio_set1_invert,
++ .reset = &pch_gpio_set1_reset,
++ },
++ .set2 = {
++ .mode = &pch_gpio_set2_mode,
++ .direction = &pch_gpio_set2_direction,
++ .level = &pch_gpio_set2_level,
++ .reset = &pch_gpio_set2_reset,
++ },
++ .set3 = {
++ .mode = &pch_gpio_set3_mode,
++ .direction = &pch_gpio_set3_direction,
++ .level = &pch_gpio_set3_level,
++ .reset = &pch_gpio_set3_reset,
++ },
++};
+diff --git a/src/mainboard/dell/snb_ivb_latitude/variants/e6220/hda_verb.c b/src/mainboard/dell/snb_ivb_latitude/variants/e6220/hda_verb.c
+new file mode 100644
+index 0000000000..0c69f0bd0e
+--- /dev/null
++++ b/src/mainboard/dell/snb_ivb_latitude/variants/e6220/hda_verb.c
+@@ -0,0 +1,32 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++
++#include
++
++const u32 cim_verb_data[] = {
++ 0x111d76e7, /* Codec Vendor / Device ID: IDT */
++ 0x102804a9, /* Subsystem ID */
++ 11, /* Number of 4 dword sets */
++ AZALIA_SUBVENDOR(0, 0x102804a9),
++ AZALIA_PIN_CFG(0, 0x0a, 0x03a11020),
++ AZALIA_PIN_CFG(0, 0x0b, 0x0321101f),
++ AZALIA_PIN_CFG(0, 0x0c, 0x400000f0),
++ AZALIA_PIN_CFG(0, 0x0d, 0x90170110),
++ AZALIA_PIN_CFG(0, 0x0e, 0x23011050),
++ AZALIA_PIN_CFG(0, 0x0f, 0x23a1102e),
++ AZALIA_PIN_CFG(0, 0x10, 0x400000f3),
++ AZALIA_PIN_CFG(0, 0x11, 0xd5a30130),
++ AZALIA_PIN_CFG(0, 0x1f, 0x400000f0),
++ AZALIA_PIN_CFG(0, 0x20, 0x400000f0),
++
++ 0x80862805, /* Codec Vendor / Device ID: Intel */
++ 0x80860101, /* Subsystem ID */
++ 4, /* Number of 4 dword sets */
++ AZALIA_SUBVENDOR(3, 0x80860101),
++ AZALIA_PIN_CFG(3, 0x05, 0x18560010),
++ AZALIA_PIN_CFG(3, 0x06, 0x18560020),
++ AZALIA_PIN_CFG(3, 0x07, 0x18560030),
++};
++
++const u32 pc_beep_verbs[0] = {};
++
++AZALIA_ARRAY_SIZES;
+diff --git a/src/mainboard/dell/snb_ivb_latitude/variants/e6220/overridetree.cb b/src/mainboard/dell/snb_ivb_latitude/variants/e6220/overridetree.cb
+new file mode 100644
+index 0000000000..9faf27e27b
+--- /dev/null
++++ b/src/mainboard/dell/snb_ivb_latitude/variants/e6220/overridetree.cb
+@@ -0,0 +1,37 @@
++## SPDX-License-Identifier: GPL-2.0-or-later
++
++chip northbridge/intel/sandybridge
++ device domain 0 on
++ subsystemid 0x1028 0x04a9 inherit
++
++ device ref igd on
++ register "gpu_cpu_backlight" = "0x0000046a"
++ register "gpu_pch_backlight" = "0x13121312"
++ end
++
++ chip southbridge/intel/bd82x6x
++ register "pcie_hotplug_map" = "{ 0, 0, 1, 0, 0, 0, 0, 0 }"
++ register "usb_port_config" = "{
++ { 1, 1, 0 },
++ { 1, 0, 0 },
++ { 1, 1, 1 },
++ { 1, 0, 1 },
++ { 1, 1, 2 },
++ { 1, 1, 2 },
++ { 1, 1, 3 },
++ { 1, 1, 3 },
++ { 1, 0, 5 },
++ { 1, 0, 5 },
++ { 1, 1, 7 },
++ { 1, 1, 6 },
++ { 1, 0, 6 },
++ { 1, 0, 7 },
++ }"
++
++ device ref pcie_rp4 off end
++ device ref sata1 on
++ register "sata_port_map" = "0x3b"
++ end
++ end
++ end
++end
+--
+2.39.5
+
diff --git a/config/coreboot/default/patches/0016-mb-dell-Add-Latitude-E6330-Ivy-Bridge.patch b/config/coreboot/default/patches/0016-mb-dell-Add-Latitude-E6330-Ivy-Bridge.patch
new file mode 100644
index 00000000..7d2133ef
--- /dev/null
+++ b/config/coreboot/default/patches/0016-mb-dell-Add-Latitude-E6330-Ivy-Bridge.patch
@@ -0,0 +1,436 @@
+From 0889cc6b6f62cba616feff5ae8558be31f298069 Mon Sep 17 00:00:00 2001
+From: Nicholas Chin
+Date: Fri, 8 Mar 2024 09:33:03 -0700
+Subject: [PATCH 16/51] mb/dell: Add Latitude E6330 (Ivy Bridge)
+
+Mainboard is QAL70/LA-7741P. I do not physically have this system;
+someone with physical access to one sent me the output of autoport which
+I then modified to produce this port. I was also sent the VBT binary,
+which was obtained from `/sys/kernel/debug/dri/0/i915_vbt` while running
+version A21 of the vendor firmware. This port has not been tested.
+
+The EC is the SMSC MEC5055, which seems to be compatible with the
+existing MEC5035 code. As with the other Dell systems with this EC, this
+board is assumed to be internally flashable using an EC command that
+tells it to pull the FDO pin low on the next boot, which also tells the
+vendor firmware to disable all write protections to the flash [1].
+
+[1] https://gitlab.com/nic3-14159/dell-flash-unlock
+
+Change-Id: I827826e9ff8a9a534c50250458b399104478e06c
+Signed-off-by: Nicholas Chin
+---
+ src/mainboard/dell/snb_ivb_latitude/Kconfig | 11 +-
+ .../dell/snb_ivb_latitude/Kconfig.name | 3 +
+ .../snb_ivb_latitude/variants/e6330/data.vbt | Bin 0 -> 6144 bytes
+ .../variants/e6330/early_init.c | 14 ++
+ .../snb_ivb_latitude/variants/e6330/gpio.c | 192 ++++++++++++++++++
+ .../variants/e6330/hda_verb.c | 32 +++
+ .../variants/e6330/overridetree.cb | 37 ++++
+ 7 files changed, 288 insertions(+), 1 deletion(-)
+ create mode 100644 src/mainboard/dell/snb_ivb_latitude/variants/e6330/data.vbt
+ create mode 100644 src/mainboard/dell/snb_ivb_latitude/variants/e6330/early_init.c
+ create mode 100644 src/mainboard/dell/snb_ivb_latitude/variants/e6330/gpio.c
+ create mode 100644 src/mainboard/dell/snb_ivb_latitude/variants/e6330/hda_verb.c
+ create mode 100644 src/mainboard/dell/snb_ivb_latitude/variants/e6330/overridetree.cb
+
+diff --git a/src/mainboard/dell/snb_ivb_latitude/Kconfig b/src/mainboard/dell/snb_ivb_latitude/Kconfig
+index baa83baa41..49bf225fe2 100644
+--- a/src/mainboard/dell/snb_ivb_latitude/Kconfig
++++ b/src/mainboard/dell/snb_ivb_latitude/Kconfig
+@@ -56,6 +56,12 @@ config BOARD_DELL_LATITUDE_E5530
+ select BOARD_ROMSIZE_KB_12288
+ select SOUTHBRIDGE_INTEL_C216
+
++config BOARD_DELL_LATITUDE_E6330
++ select BOARD_DELL_SNB_IVB_LATITUDE_COMMON
++ select BOARD_ROMSIZE_KB_12288
++ select MAINBOARD_USES_IFD_GBE_REGION
++ select SOUTHBRIDGE_INTEL_C216
++
+ config BOARD_DELL_LATITUDE_E6430
+ select BOARD_DELL_SNB_IVB_LATITUDE_COMMON
+ select BOARD_ROMSIZE_KB_12288
+@@ -84,6 +90,7 @@ config MAINBOARD_PART_NUMBER
+ default "Latitude E6420" if BOARD_DELL_LATITUDE_E6420
+ default "Latitude E6520" if BOARD_DELL_LATITUDE_E6520
+ default "Latitude E5530" if BOARD_DELL_LATITUDE_E5530
++ default "Latitude E6330" if BOARD_DELL_LATITUDE_E6330
+ default "Latitude E6430" if BOARD_DELL_LATITUDE_E6430
+ default "Latitude E6530" if BOARD_DELL_LATITUDE_E6530
+
+@@ -101,13 +108,15 @@ config VARIANT_DIR
+ default "e6420" if BOARD_DELL_LATITUDE_E6420
+ default "e6520" if BOARD_DELL_LATITUDE_E6520
+ default "e5530" if BOARD_DELL_LATITUDE_E5530
++ default "e6330" if BOARD_DELL_LATITUDE_E6330
+ default "e6430" if BOARD_DELL_LATITUDE_E6430
+ default "e6530" if BOARD_DELL_LATITUDE_E6530
+
+ config VGA_BIOS_ID
+ default "8086,0116" if BOARD_DELL_LATITUDE_E6520 \
+ || BOARD_DELL_LATITUDE_E5420
+- default "8086,0166" if BOARD_DELL_LATITUDE_E5530
++ default "8086,0166" if BOARD_DELL_LATITUDE_E5530 \
++ || BOARD_DELL_LATITUDE_E6330
+ default "8086,0126" if BOARD_DELL_LATITUDE_E6420 \
+ || BOARD_DELL_LATITUDE_E5520 \
+ || BOARD_DELL_LATITUDE_E6220 \
+diff --git a/src/mainboard/dell/snb_ivb_latitude/Kconfig.name b/src/mainboard/dell/snb_ivb_latitude/Kconfig.name
+index 349ee7f79e..d6fc8eb224 100644
+--- a/src/mainboard/dell/snb_ivb_latitude/Kconfig.name
++++ b/src/mainboard/dell/snb_ivb_latitude/Kconfig.name
+@@ -21,6 +21,9 @@ config BOARD_DELL_LATITUDE_E6520
+ config BOARD_DELL_LATITUDE_E5530
+ bool "Latitude E5530"
+
++config BOARD_DELL_LATITUDE_E6330
++ bool "Latitude E6330"
++
+ config BOARD_DELL_LATITUDE_E6430
+ bool "Latitude E6430"
+
+diff --git a/src/mainboard/dell/snb_ivb_latitude/variants/e6330/data.vbt b/src/mainboard/dell/snb_ivb_latitude/variants/e6330/data.vbt
+new file mode 100644
+index 0000000000000000000000000000000000000000..18856746656058651c571ecbb3708e0543b19d62
+GIT binary patch
+literal 6144
+zcmeHKU2GiH75-*tc6WAmW_LYygMSkDB*E^Q*zv5f7dLg)@$NQV2a{}!yImnfyvQ4D
+z;n-$v0!RpNi<_o@ktI-@2a590stC0zRi%iRR%stvi&hAs3RvNXNl&Dsr*z_6HAP5GJeST
+znX2JD;{xLa#;c6KGychVn-L6YXkv6Qx)}Y8&ok1DI~ZSM6dCt39%QUAzRh@o@gqjy
+zL0qZ&DhN8ZR3xu$a$Cd{oasU3DNp{CCl6f~PYlq!Hte;Ia0^wn8Vut7>Wl1)s`^E-
+z1DhGxzr8&7h}dMJ3~YBe;)!vVf-T&?{PoMvvRR{!67;Xhz^g^loX
+zja+*c-KJJoxbsm3pTE4THs`cgD{Pt+ga3en-i$P#9Wsra(oqRMr;H$4{gxr)9eF(x
+zg0v@a7aj}rA^Kf#sNb*>at^>P)5li%ycOq*4e;3~RUj$i1e8=rHi&Y_gP4=
+zxz9^%q0dLXqC&Bq<&sDScZwvatjRxB=rcJJiYb?w#4Iy2KTk1F6T>T}E@(DNGa>5R
+z7&Yv)JdHrRI};pfsKLVj=FE=U*=*T4#ncVktknoGelT||SDY`+9WI_IZE`_>@wb<-RI-lu(_~Oy_Zo6={Cdq!uw(fmyz_u^cB&~5IS7g`U
+zdUC}N$J5-C)|`CfUO+?xptr@*hJW$ZhBZk%JaMh_<8!ZGj)z*WU9fcg2`>dT##_?q
+z=Ksx}uxo3jTHTq%E1}97UECE@r}nt3I=3R(HOL7jNg>teSM-g$aU#`3jk}#qh;D?6
+zw=CYuA2#mS+vU%0P&u8RCn4)$8VH-2uy#01%VG0WSX&Fz`LMYj)?NgH_zzWS)G2Pn=k3wYdU|DnWmxbG$>`5lZJMWL92%O14BD!(C-cNFNXFH
+zgVc!G9?@=&(4mNVcSJiLq3=b^rz6@k5qdFVUW{m$A{2|7d!kxz)VSrcQt@4sDoq^f
+z98hXm=YS~qbfiR|LbzHLa*mLXlH${^b4c9%>9%)HO-?LA1gT0mlz!M}8&;(;^x|
+z*H22;juB|7F+k?y(_2~3@STqQ@f^sqD2c8g3x>ciM%siMq~;pKwfE57kw2K@
+z!-ZN0QTVOP@aA5@fEGKjy2+D`t?2KzpPyRQ`JcmHJoc(<#hvyF
+zewtgef*II~y;k>*B!+(8*blXsY-~kcJa9zG2yfcMCt+|-0ex$pY`h1<*#rEv=~*<+
+ztV``Um!q33-Aap9fUshX^N~GS2@X3^U9+MwgYQ74^?~6&yU^#oY#cvC9R_}P2d
++#include
++#include
++#include
++
++void bootblock_mainboard_early_init(void)
++{
++ pci_write_config16(PCH_LPC_DEV, LPC_EN, CNF1_LPC_EN | MC_LPC_EN
++ | KBC_LPC_EN | FDD_LPC_EN | LPT_LPC_EN
++ | COMB_LPC_EN | COMA_LPC_EN);
++ mec5035_early_init();
++}
+diff --git a/src/mainboard/dell/snb_ivb_latitude/variants/e6330/gpio.c b/src/mainboard/dell/snb_ivb_latitude/variants/e6330/gpio.c
+new file mode 100644
+index 0000000000..777570765a
+--- /dev/null
++++ b/src/mainboard/dell/snb_ivb_latitude/variants/e6330/gpio.c
+@@ -0,0 +1,192 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++
++#include
++
++static const struct pch_gpio_set1 pch_gpio_set1_mode = {
++ .gpio0 = GPIO_MODE_GPIO,
++ .gpio1 = GPIO_MODE_GPIO,
++ .gpio2 = GPIO_MODE_GPIO,
++ .gpio3 = GPIO_MODE_GPIO,
++ .gpio4 = GPIO_MODE_GPIO,
++ .gpio5 = GPIO_MODE_NATIVE,
++ .gpio6 = GPIO_MODE_GPIO,
++ .gpio7 = GPIO_MODE_GPIO,
++ .gpio8 = GPIO_MODE_GPIO,
++ .gpio9 = GPIO_MODE_NATIVE,
++ .gpio10 = GPIO_MODE_NATIVE,
++ .gpio11 = GPIO_MODE_NATIVE,
++ .gpio12 = GPIO_MODE_NATIVE,
++ .gpio13 = GPIO_MODE_GPIO,
++ .gpio14 = GPIO_MODE_GPIO,
++ .gpio15 = GPIO_MODE_GPIO,
++ .gpio16 = GPIO_MODE_GPIO,
++ .gpio17 = GPIO_MODE_GPIO,
++ .gpio18 = GPIO_MODE_NATIVE,
++ .gpio19 = GPIO_MODE_GPIO,
++ .gpio20 = GPIO_MODE_NATIVE,
++ .gpio21 = GPIO_MODE_GPIO,
++ .gpio22 = GPIO_MODE_GPIO,
++ .gpio23 = GPIO_MODE_NATIVE,
++ .gpio24 = GPIO_MODE_GPIO,
++ .gpio25 = GPIO_MODE_NATIVE,
++ .gpio26 = GPIO_MODE_NATIVE,
++ .gpio27 = GPIO_MODE_GPIO,
++ .gpio28 = GPIO_MODE_GPIO,
++ .gpio29 = GPIO_MODE_GPIO,
++ .gpio30 = GPIO_MODE_NATIVE,
++ .gpio31 = GPIO_MODE_NATIVE,
++};
++
++static const struct pch_gpio_set1 pch_gpio_set1_direction = {
++ .gpio0 = GPIO_DIR_INPUT,
++ .gpio1 = GPIO_DIR_INPUT,
++ .gpio2 = GPIO_DIR_INPUT,
++ .gpio3 = GPIO_DIR_INPUT,
++ .gpio4 = GPIO_DIR_INPUT,
++ .gpio6 = GPIO_DIR_INPUT,
++ .gpio7 = GPIO_DIR_INPUT,
++ .gpio8 = GPIO_DIR_INPUT,
++ .gpio13 = GPIO_DIR_INPUT,
++ .gpio14 = GPIO_DIR_INPUT,
++ .gpio15 = GPIO_DIR_INPUT,
++ .gpio16 = GPIO_DIR_INPUT,
++ .gpio17 = GPIO_DIR_INPUT,
++ .gpio19 = GPIO_DIR_INPUT,
++ .gpio21 = GPIO_DIR_INPUT,
++ .gpio22 = GPIO_DIR_INPUT,
++ .gpio24 = GPIO_DIR_INPUT,
++ .gpio27 = GPIO_DIR_INPUT,
++ .gpio28 = GPIO_DIR_OUTPUT,
++ .gpio29 = GPIO_DIR_INPUT,
++};
++
++static const struct pch_gpio_set1 pch_gpio_set1_level = {
++ .gpio28 = GPIO_LEVEL_LOW,
++};
++
++static const struct pch_gpio_set1 pch_gpio_set1_reset = {
++ .gpio30 = GPIO_RESET_RSMRST,
++};
++
++static const struct pch_gpio_set1 pch_gpio_set1_invert = {
++ .gpio0 = GPIO_INVERT,
++ .gpio8 = GPIO_INVERT,
++ .gpio13 = GPIO_INVERT,
++ .gpio14 = GPIO_INVERT,
++};
++
++static const struct pch_gpio_set1 pch_gpio_set1_blink = {
++};
++
++static const struct pch_gpio_set2 pch_gpio_set2_mode = {
++ .gpio32 = GPIO_MODE_NATIVE,
++ .gpio33 = GPIO_MODE_GPIO,
++ .gpio34 = GPIO_MODE_GPIO,
++ .gpio35 = GPIO_MODE_GPIO,
++ .gpio36 = GPIO_MODE_GPIO,
++ .gpio37 = GPIO_MODE_GPIO,
++ .gpio38 = GPIO_MODE_GPIO,
++ .gpio39 = GPIO_MODE_GPIO,
++ .gpio40 = GPIO_MODE_NATIVE,
++ .gpio41 = GPIO_MODE_NATIVE,
++ .gpio42 = GPIO_MODE_NATIVE,
++ .gpio43 = GPIO_MODE_NATIVE,
++ .gpio44 = GPIO_MODE_NATIVE,
++ .gpio45 = GPIO_MODE_GPIO,
++ .gpio46 = GPIO_MODE_NATIVE,
++ .gpio47 = GPIO_MODE_NATIVE,
++ .gpio48 = GPIO_MODE_GPIO,
++ .gpio49 = GPIO_MODE_GPIO,
++ .gpio50 = GPIO_MODE_NATIVE,
++ .gpio51 = GPIO_MODE_GPIO,
++ .gpio52 = GPIO_MODE_GPIO,
++ .gpio53 = GPIO_MODE_NATIVE,
++ .gpio54 = GPIO_MODE_GPIO,
++ .gpio55 = GPIO_MODE_NATIVE,
++ .gpio56 = GPIO_MODE_NATIVE,
++ .gpio57 = GPIO_MODE_GPIO,
++ .gpio58 = GPIO_MODE_NATIVE,
++ .gpio59 = GPIO_MODE_NATIVE,
++ .gpio60 = GPIO_MODE_GPIO,
++ .gpio61 = GPIO_MODE_NATIVE,
++ .gpio62 = GPIO_MODE_NATIVE,
++ .gpio63 = GPIO_MODE_NATIVE,
++};
++
++static const struct pch_gpio_set2 pch_gpio_set2_direction = {
++ .gpio33 = GPIO_DIR_INPUT,
++ .gpio34 = GPIO_DIR_OUTPUT,
++ .gpio35 = GPIO_DIR_INPUT,
++ .gpio36 = GPIO_DIR_INPUT,
++ .gpio37 = GPIO_DIR_INPUT,
++ .gpio38 = GPIO_DIR_INPUT,
++ .gpio39 = GPIO_DIR_INPUT,
++ .gpio45 = GPIO_DIR_OUTPUT,
++ .gpio48 = GPIO_DIR_INPUT,
++ .gpio49 = GPIO_DIR_INPUT,
++ .gpio51 = GPIO_DIR_INPUT,
++ .gpio52 = GPIO_DIR_INPUT,
++ .gpio54 = GPIO_DIR_INPUT,
++ .gpio57 = GPIO_DIR_INPUT,
++ .gpio60 = GPIO_DIR_OUTPUT,
++};
++
++static const struct pch_gpio_set2 pch_gpio_set2_level = {
++ .gpio34 = GPIO_LEVEL_HIGH,
++ .gpio45 = GPIO_LEVEL_LOW,
++ .gpio60 = GPIO_LEVEL_HIGH,
++};
++
++static const struct pch_gpio_set2 pch_gpio_set2_reset = {
++};
++
++static const struct pch_gpio_set3 pch_gpio_set3_mode = {
++ .gpio64 = GPIO_MODE_NATIVE,
++ .gpio65 = GPIO_MODE_NATIVE,
++ .gpio66 = GPIO_MODE_NATIVE,
++ .gpio67 = GPIO_MODE_NATIVE,
++ .gpio68 = GPIO_MODE_GPIO,
++ .gpio69 = GPIO_MODE_GPIO,
++ .gpio70 = GPIO_MODE_GPIO,
++ .gpio71 = GPIO_MODE_GPIO,
++ .gpio72 = GPIO_MODE_NATIVE,
++ .gpio73 = GPIO_MODE_NATIVE,
++ .gpio74 = GPIO_MODE_NATIVE,
++ .gpio75 = GPIO_MODE_NATIVE,
++};
++
++static const struct pch_gpio_set3 pch_gpio_set3_direction = {
++ .gpio68 = GPIO_DIR_INPUT,
++ .gpio69 = GPIO_DIR_INPUT,
++ .gpio70 = GPIO_DIR_INPUT,
++ .gpio71 = GPIO_DIR_INPUT,
++};
++
++static const struct pch_gpio_set3 pch_gpio_set3_level = {
++};
++
++static const struct pch_gpio_set3 pch_gpio_set3_reset = {
++};
++
++const struct pch_gpio_map mainboard_gpio_map = {
++ .set1 = {
++ .mode = &pch_gpio_set1_mode,
++ .direction = &pch_gpio_set1_direction,
++ .level = &pch_gpio_set1_level,
++ .blink = &pch_gpio_set1_blink,
++ .invert = &pch_gpio_set1_invert,
++ .reset = &pch_gpio_set1_reset,
++ },
++ .set2 = {
++ .mode = &pch_gpio_set2_mode,
++ .direction = &pch_gpio_set2_direction,
++ .level = &pch_gpio_set2_level,
++ .reset = &pch_gpio_set2_reset,
++ },
++ .set3 = {
++ .mode = &pch_gpio_set3_mode,
++ .direction = &pch_gpio_set3_direction,
++ .level = &pch_gpio_set3_level,
++ .reset = &pch_gpio_set3_reset,
++ },
++};
+diff --git a/src/mainboard/dell/snb_ivb_latitude/variants/e6330/hda_verb.c b/src/mainboard/dell/snb_ivb_latitude/variants/e6330/hda_verb.c
+new file mode 100644
+index 0000000000..804733b172
+--- /dev/null
++++ b/src/mainboard/dell/snb_ivb_latitude/variants/e6330/hda_verb.c
+@@ -0,0 +1,32 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++
++#include
++
++const u32 cim_verb_data[] = {
++ 0x111d76df, /* Codec Vendor / Device ID: IDT */
++ 0x10280533, /* Subsystem ID */
++ 11, /* Number of 4 dword sets */
++ AZALIA_SUBVENDOR(0, 0x10280533),
++ AZALIA_PIN_CFG(0, 0x0a, 0x03a11020),
++ AZALIA_PIN_CFG(0, 0x0b, 0x0321101f),
++ AZALIA_PIN_CFG(0, 0x0c, 0x400000f0),
++ AZALIA_PIN_CFG(0, 0x0d, 0x90170110),
++ AZALIA_PIN_CFG(0, 0x0e, 0x23011050),
++ AZALIA_PIN_CFG(0, 0x0f, 0x23a1102e),
++ AZALIA_PIN_CFG(0, 0x10, 0x400000f3),
++ AZALIA_PIN_CFG(0, 0x11, 0xd5a30130),
++ AZALIA_PIN_CFG(0, 0x1f, 0x400000f0),
++ AZALIA_PIN_CFG(0, 0x20, 0x400000f0),
++
++ 0x80862806, /* Codec Vendor / Device ID: Intel */
++ 0x80860101, /* Subsystem ID */
++ 4, /* Number of 4 dword sets */
++ AZALIA_SUBVENDOR(3, 0x80860101),
++ AZALIA_PIN_CFG(3, 0x05, 0x18560010),
++ AZALIA_PIN_CFG(3, 0x06, 0x18560020),
++ AZALIA_PIN_CFG(3, 0x07, 0x18560030),
++};
++
++const u32 pc_beep_verbs[0] = {};
++
++AZALIA_ARRAY_SIZES;
+diff --git a/src/mainboard/dell/snb_ivb_latitude/variants/e6330/overridetree.cb b/src/mainboard/dell/snb_ivb_latitude/variants/e6330/overridetree.cb
+new file mode 100644
+index 0000000000..4125159367
+--- /dev/null
++++ b/src/mainboard/dell/snb_ivb_latitude/variants/e6330/overridetree.cb
+@@ -0,0 +1,37 @@
++## SPDX-License-Identifier: GPL-2.0-or-later
++
++chip northbridge/intel/sandybridge
++ device domain 0 on
++ subsystemid 0x1028 0x0533 inherit
++
++ device ref igd on
++ register "gpu_cpu_backlight" = "0x00001312"
++ register "gpu_pch_backlight" = "0x13121312"
++ end
++
++ chip southbridge/intel/bd82x6x
++ register "usb_port_config" = "{
++ { 1, 2, 0 },
++ { 1, 0, 0 },
++ { 1, 0, 1 },
++ { 1, 1, 1 },
++ { 1, 1, 2 },
++ { 1, 1, 2 },
++ { 1, 2, 3 },
++ { 1, 2, 3 },
++ { 1, 2, 4 },
++ { 1, 1, 4 },
++ { 1, 1, 5 },
++ { 1, 1, 5 },
++ { 1, 2, 6 },
++ { 1, 0, 6 },
++ }"
++
++ device ref xhci on
++ register "superspeed_capable_ports" = "0x0000000f"
++ register "xhci_overcurrent_mapping" = "0x00000c03"
++ register "xhci_switchable_ports" = "0x0000000f"
++ end
++ end
++ end
++end
+--
+2.39.5
+
diff --git a/config/coreboot/default/patches/0017-mb-dell-Add-Latitude-E6230-Ivy-Bridge.patch b/config/coreboot/default/patches/0017-mb-dell-Add-Latitude-E6230-Ivy-Bridge.patch
new file mode 100644
index 00000000..412b8471
--- /dev/null
+++ b/config/coreboot/default/patches/0017-mb-dell-Add-Latitude-E6230-Ivy-Bridge.patch
@@ -0,0 +1,440 @@
+From 84d7f3201eb4492acd7d290a02d19c4850c85791 Mon Sep 17 00:00:00 2001
+From: Nicholas Chin
+Date: Thu, 26 Oct 2017 21:26:43 +0800
+Subject: [PATCH 17/51] mb/dell: Add Latitude E6230 (Ivy Bridge)
+
+This was adapted from CB:22693 from Iru Cai, which was based on
+autoport. I do not physically have this system. Someone with physical
+access to an E6230 running version A11 of the vendor firmware sent me
+the VBT after running the command `intelvbttool --inlegacy --outvbt
+data.vbt`. This new version of the port has not yet been tested.
+
+The EC is the SMSC MEC5055, which seems to be compatible with the
+existing MEC5035 code. As with the other Dell systems with this EC, this
+board is assumed to be internally flashable using an EC command that
+tells it to pull the FDO pin low on the next boot, which also tells the
+vendor firmware to disable all write protections to the flash [1].
+
+[1] https://gitlab.com/nic3-14159/dell-flash-unlock
+
+Original-Change-Id: I8cdc01e902e670310628809416290045c2102340
+Change-Id: I32927beea7c29b96a851ab77ed15b0160f16d369
+Signed-off-by: Nicholas Chin
+---
+ src/mainboard/dell/snb_ivb_latitude/Kconfig | 11 +-
+ .../dell/snb_ivb_latitude/Kconfig.name | 3 +
+ .../snb_ivb_latitude/variants/e6230/data.vbt | Bin 0 -> 4280 bytes
+ .../variants/e6230/early_init.c | 12 ++
+ .../snb_ivb_latitude/variants/e6230/gpio.c | 193 ++++++++++++++++++
+ .../variants/e6230/hda_verb.c | 32 +++
+ .../variants/e6230/overridetree.cb | 40 ++++
+ 7 files changed, 290 insertions(+), 1 deletion(-)
+ create mode 100644 src/mainboard/dell/snb_ivb_latitude/variants/e6230/data.vbt
+ create mode 100644 src/mainboard/dell/snb_ivb_latitude/variants/e6230/early_init.c
+ create mode 100644 src/mainboard/dell/snb_ivb_latitude/variants/e6230/gpio.c
+ create mode 100644 src/mainboard/dell/snb_ivb_latitude/variants/e6230/hda_verb.c
+ create mode 100644 src/mainboard/dell/snb_ivb_latitude/variants/e6230/overridetree.cb
+
+diff --git a/src/mainboard/dell/snb_ivb_latitude/Kconfig b/src/mainboard/dell/snb_ivb_latitude/Kconfig
+index 49bf225fe2..f6e097930b 100644
+--- a/src/mainboard/dell/snb_ivb_latitude/Kconfig
++++ b/src/mainboard/dell/snb_ivb_latitude/Kconfig
+@@ -56,6 +56,12 @@ config BOARD_DELL_LATITUDE_E5530
+ select BOARD_ROMSIZE_KB_12288
+ select SOUTHBRIDGE_INTEL_C216
+
++config BOARD_DELL_LATITUDE_E6230
++ select BOARD_DELL_SNB_IVB_LATITUDE_COMMON
++ select BOARD_ROMSIZE_KB_12288
++ select MAINBOARD_USES_IFD_GBE_REGION
++ select SOUTHBRIDGE_INTEL_C216
++
+ config BOARD_DELL_LATITUDE_E6330
+ select BOARD_DELL_SNB_IVB_LATITUDE_COMMON
+ select BOARD_ROMSIZE_KB_12288
+@@ -90,6 +96,7 @@ config MAINBOARD_PART_NUMBER
+ default "Latitude E6420" if BOARD_DELL_LATITUDE_E6420
+ default "Latitude E6520" if BOARD_DELL_LATITUDE_E6520
+ default "Latitude E5530" if BOARD_DELL_LATITUDE_E5530
++ default "Latitude E6230" if BOARD_DELL_LATITUDE_E6230
+ default "Latitude E6330" if BOARD_DELL_LATITUDE_E6330
+ default "Latitude E6430" if BOARD_DELL_LATITUDE_E6430
+ default "Latitude E6530" if BOARD_DELL_LATITUDE_E6530
+@@ -108,6 +115,7 @@ config VARIANT_DIR
+ default "e6420" if BOARD_DELL_LATITUDE_E6420
+ default "e6520" if BOARD_DELL_LATITUDE_E6520
+ default "e5530" if BOARD_DELL_LATITUDE_E5530
++ default "e6230" if BOARD_DELL_LATITUDE_E6230
+ default "e6330" if BOARD_DELL_LATITUDE_E6330
+ default "e6430" if BOARD_DELL_LATITUDE_E6430
+ default "e6530" if BOARD_DELL_LATITUDE_E6530
+@@ -121,7 +129,8 @@ config VGA_BIOS_ID
+ || BOARD_DELL_LATITUDE_E5520 \
+ || BOARD_DELL_LATITUDE_E6220 \
+ || BOARD_DELL_LATITUDE_E6320
+- default "8086,0166" if BOARD_DELL_LATITUDE_E6430 \
++ default "8086,0166" if BOARD_DELL_LATITUDE_E6230 \
++ || BOARD_DELL_LATITUDE_E6430 \
+ || BOARD_DELL_LATITUDE_E6530
+
+ endif
+diff --git a/src/mainboard/dell/snb_ivb_latitude/Kconfig.name b/src/mainboard/dell/snb_ivb_latitude/Kconfig.name
+index d6fc8eb224..cb7bbd5cdb 100644
+--- a/src/mainboard/dell/snb_ivb_latitude/Kconfig.name
++++ b/src/mainboard/dell/snb_ivb_latitude/Kconfig.name
+@@ -21,6 +21,9 @@ config BOARD_DELL_LATITUDE_E6520
+ config BOARD_DELL_LATITUDE_E5530
+ bool "Latitude E5530"
+
++config BOARD_DELL_LATITUDE_E6230
++ bool "Latitude E6230"
++
+ config BOARD_DELL_LATITUDE_E6330
+ bool "Latitude E6330"
+
+diff --git a/src/mainboard/dell/snb_ivb_latitude/variants/e6230/data.vbt b/src/mainboard/dell/snb_ivb_latitude/variants/e6230/data.vbt
+new file mode 100644
+index 0000000000000000000000000000000000000000..45ce8f435eea647a0bddaab3fd1e9282c87afc66
+GIT binary patch
+literal 4280
+zcmdT{Yiu0V75-*tAG5PFyX&zDekA7P<*tbx&o1`j23L%CmvkLWvN7(mLa6alZ?J`9
+zo3#m4YVlG`2;w12Ajppt~|mMp8L%`XU@4ZyCa_(r`z|Z`bP4p-rEkOMn-R;Ntk#o`b)0sOKRl6
+z?T0eMZS*@LZWP#hD{>
+zSLslxYH@j~%H#VLx+<8~!;a@$n+>Q%xHrQ8KGI21_iL4sI
+zF*$}m$R7Mr9z@Z*ir@Q9eClsSmC+t(g`q~VQ&nIxZenav_^Buc78s8*o@e|t}g
+z1Z-O>lG`>pEuvmL-HpmgSANo!2hWQq2B>Zua$8%tfvQ>!1n@=m9ri_4`H|Rx#SH9n
+zDdRF_-FDP&WjW`L$GK%a6x?yO8l2!^g0HJrtA7TknNzCO?|U!wCv2^-5pJ%LW6!+P
+z)anX%E>`gP%3Er4c6+J9x=Atk1{Abrr1|WSY3P`SO5j!R5F*vbbQ%AaSHnR_+x&Op
+zA%8C-Pk=-Hs+FL90B)E*y3FUTIA1J)&pxRF$tzAkNr7a6_-8v$@j~G~3keqYd5I;y8g^P<
+zfWO_D2@EyVVBxnpv*}hgTeeRzWz9BoISh>M%^k`WXG=5ti$_Wu99)~lWE-qubeXNk
+zla9Tu=Jhyn5T<3$H#?Hfm-`+(d$7IBDx9cEvNv1i-LEDr>r7438bfkPcKod+mwd22
+z%{^(w&NuG)MKl0fTMTXZFJEm~k;KCj*D60g=j!2jsP){tzvOjAJ2nB}At~#%f4?FGT+d8LFnXXtYN&Mm06B
+z(JwUPX-z$$(d(M=uBLvh@h6#K=;~&jQo1p&t3TCgSvQ{3)l)jXr5hjW>fd!z!bW>o
+z-4UjJVdJi_dN@o^hK(1(>dRqzCv2PztLMTLjTqY^YEMMJ{=B#1IV)9~IMg|yl(NPF
+zQSfMX`?(b5)))B!zjy0B$ua20CCLTPl^IS&2=T&Zid9-1*K{VAJP?rxjYC+zGDCe*
+ziQI7VfF17@3`3W-qCN>lPC5CL_c?p0FTGXaGB3i~{ZE
+zr=QtIprytDnU7C*Wj%x0k)O{Y%nUnl%}K%F|J_iVaD&ubW4Qbtx;pZEb9}f^Yd;Ea
+zI1Ha{7Yt~z{LAY++1QG{F6*_4WsUziY{x?%I9B}i5-Tphhk8FGm%Jf!3d-ysG_9>uk%#)4xpxb+ZkJdel#+h}l9j9`1
+zt*M!5u?i4YtZ+WECo6$LJF06|G-mMZskGiV*lQJf-ItB+hltI{5E<^P=0rLsId@e&
+z-cNvr(fgI)K*zkikg6TDJi?^}ghc*U*%A%EGg
+Y$$8Z}9a~<{Q@y10T!W`-d%n2+Kj)*Kg#Z8m
+
+literal 0
+HcmV?d00001
+
+diff --git a/src/mainboard/dell/snb_ivb_latitude/variants/e6230/early_init.c b/src/mainboard/dell/snb_ivb_latitude/variants/e6230/early_init.c
+new file mode 100644
+index 0000000000..24c1b32467
+--- /dev/null
++++ b/src/mainboard/dell/snb_ivb_latitude/variants/e6230/early_init.c
+@@ -0,0 +1,12 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++
++#include
++#include
++#include
++#include
++
++void bootblock_mainboard_early_init(void)
++{
++ pci_write_config16(PCH_LPC_DEV, LPC_EN, CNF1_LPC_EN | MC_LPC_EN | KBC_LPC_EN);
++ mec5035_early_init();
++}
+diff --git a/src/mainboard/dell/snb_ivb_latitude/variants/e6230/gpio.c b/src/mainboard/dell/snb_ivb_latitude/variants/e6230/gpio.c
+new file mode 100644
+index 0000000000..c07e4b1c56
+--- /dev/null
++++ b/src/mainboard/dell/snb_ivb_latitude/variants/e6230/gpio.c
+@@ -0,0 +1,193 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++
++#include
++
++static const struct pch_gpio_set1 pch_gpio_set1_mode = {
++ .gpio0 = GPIO_MODE_GPIO,
++ .gpio1 = GPIO_MODE_GPIO,
++ .gpio2 = GPIO_MODE_GPIO,
++ .gpio3 = GPIO_MODE_GPIO,
++ .gpio4 = GPIO_MODE_GPIO,
++ .gpio5 = GPIO_MODE_NATIVE,
++ .gpio6 = GPIO_MODE_GPIO,
++ .gpio7 = GPIO_MODE_GPIO,
++ .gpio8 = GPIO_MODE_GPIO,
++ .gpio9 = GPIO_MODE_NATIVE,
++ .gpio10 = GPIO_MODE_NATIVE,
++ .gpio11 = GPIO_MODE_NATIVE,
++ .gpio12 = GPIO_MODE_NATIVE,
++ .gpio13 = GPIO_MODE_GPIO,
++ .gpio14 = GPIO_MODE_GPIO,
++ .gpio15 = GPIO_MODE_GPIO,
++ .gpio16 = GPIO_MODE_GPIO,
++ .gpio17 = GPIO_MODE_GPIO,
++ .gpio18 = GPIO_MODE_NATIVE,
++ .gpio19 = GPIO_MODE_GPIO,
++ .gpio20 = GPIO_MODE_NATIVE,
++ .gpio21 = GPIO_MODE_GPIO,
++ .gpio22 = GPIO_MODE_GPIO,
++ .gpio23 = GPIO_MODE_NATIVE,
++ .gpio24 = GPIO_MODE_GPIO,
++ .gpio25 = GPIO_MODE_NATIVE,
++ .gpio26 = GPIO_MODE_NATIVE,
++ .gpio27 = GPIO_MODE_GPIO,
++ .gpio28 = GPIO_MODE_GPIO,
++ .gpio29 = GPIO_MODE_GPIO,
++ .gpio30 = GPIO_MODE_NATIVE,
++ .gpio31 = GPIO_MODE_NATIVE,
++};
++
++static const struct pch_gpio_set1 pch_gpio_set1_direction = {
++ .gpio0 = GPIO_DIR_INPUT,
++ .gpio1 = GPIO_DIR_INPUT,
++ .gpio2 = GPIO_DIR_INPUT,
++ .gpio3 = GPIO_DIR_INPUT,
++ .gpio4 = GPIO_DIR_INPUT,
++ .gpio6 = GPIO_DIR_INPUT,
++ .gpio7 = GPIO_DIR_INPUT,
++ .gpio8 = GPIO_DIR_INPUT,
++ .gpio13 = GPIO_DIR_INPUT,
++ .gpio14 = GPIO_DIR_INPUT,
++ .gpio15 = GPIO_DIR_INPUT,
++ .gpio16 = GPIO_DIR_INPUT,
++ .gpio17 = GPIO_DIR_OUTPUT,
++ .gpio19 = GPIO_DIR_INPUT,
++ .gpio21 = GPIO_DIR_INPUT,
++ .gpio22 = GPIO_DIR_INPUT,
++ .gpio24 = GPIO_DIR_INPUT,
++ .gpio27 = GPIO_DIR_INPUT,
++ .gpio28 = GPIO_DIR_OUTPUT,
++ .gpio29 = GPIO_DIR_INPUT,
++};
++
++static const struct pch_gpio_set1 pch_gpio_set1_level = {
++ .gpio17 = GPIO_LEVEL_HIGH,
++ .gpio28 = GPIO_LEVEL_LOW,
++};
++
++static const struct pch_gpio_set1 pch_gpio_set1_reset = {
++ .gpio30 = GPIO_RESET_RSMRST,
++};
++
++static const struct pch_gpio_set1 pch_gpio_set1_invert = {
++ .gpio0 = GPIO_INVERT,
++ .gpio8 = GPIO_INVERT,
++ .gpio13 = GPIO_INVERT,
++ .gpio14 = GPIO_INVERT,
++};
++
++static const struct pch_gpio_set1 pch_gpio_set1_blink = {
++};
++
++static const struct pch_gpio_set2 pch_gpio_set2_mode = {
++ .gpio32 = GPIO_MODE_NATIVE,
++ .gpio33 = GPIO_MODE_GPIO,
++ .gpio34 = GPIO_MODE_GPIO,
++ .gpio35 = GPIO_MODE_GPIO,
++ .gpio36 = GPIO_MODE_GPIO,
++ .gpio37 = GPIO_MODE_GPIO,
++ .gpio38 = GPIO_MODE_GPIO,
++ .gpio39 = GPIO_MODE_GPIO,
++ .gpio40 = GPIO_MODE_NATIVE,
++ .gpio41 = GPIO_MODE_NATIVE,
++ .gpio42 = GPIO_MODE_NATIVE,
++ .gpio43 = GPIO_MODE_NATIVE,
++ .gpio44 = GPIO_MODE_NATIVE,
++ .gpio45 = GPIO_MODE_GPIO,
++ .gpio46 = GPIO_MODE_NATIVE,
++ .gpio47 = GPIO_MODE_NATIVE,
++ .gpio48 = GPIO_MODE_GPIO,
++ .gpio49 = GPIO_MODE_GPIO,
++ .gpio50 = GPIO_MODE_NATIVE,
++ .gpio51 = GPIO_MODE_GPIO,
++ .gpio52 = GPIO_MODE_GPIO,
++ .gpio53 = GPIO_MODE_NATIVE,
++ .gpio54 = GPIO_MODE_GPIO,
++ .gpio55 = GPIO_MODE_NATIVE,
++ .gpio56 = GPIO_MODE_NATIVE,
++ .gpio57 = GPIO_MODE_GPIO,
++ .gpio58 = GPIO_MODE_NATIVE,
++ .gpio59 = GPIO_MODE_NATIVE,
++ .gpio60 = GPIO_MODE_GPIO,
++ .gpio61 = GPIO_MODE_NATIVE,
++ .gpio62 = GPIO_MODE_NATIVE,
++ .gpio63 = GPIO_MODE_NATIVE,
++};
++
++static const struct pch_gpio_set2 pch_gpio_set2_direction = {
++ .gpio33 = GPIO_DIR_INPUT,
++ .gpio34 = GPIO_DIR_OUTPUT,
++ .gpio35 = GPIO_DIR_INPUT,
++ .gpio36 = GPIO_DIR_INPUT,
++ .gpio37 = GPIO_DIR_INPUT,
++ .gpio38 = GPIO_DIR_INPUT,
++ .gpio39 = GPIO_DIR_INPUT,
++ .gpio45 = GPIO_DIR_OUTPUT,
++ .gpio48 = GPIO_DIR_INPUT,
++ .gpio49 = GPIO_DIR_INPUT,
++ .gpio51 = GPIO_DIR_INPUT,
++ .gpio52 = GPIO_DIR_INPUT,
++ .gpio54 = GPIO_DIR_INPUT,
++ .gpio57 = GPIO_DIR_INPUT,
++ .gpio60 = GPIO_DIR_OUTPUT,
++};
++
++static const struct pch_gpio_set2 pch_gpio_set2_level = {
++ .gpio34 = GPIO_LEVEL_HIGH,
++ .gpio45 = GPIO_LEVEL_LOW,
++ .gpio60 = GPIO_LEVEL_HIGH,
++};
++
++static const struct pch_gpio_set2 pch_gpio_set2_reset = {
++};
++
++static const struct pch_gpio_set3 pch_gpio_set3_mode = {
++ .gpio64 = GPIO_MODE_NATIVE,
++ .gpio65 = GPIO_MODE_NATIVE,
++ .gpio66 = GPIO_MODE_NATIVE,
++ .gpio67 = GPIO_MODE_NATIVE,
++ .gpio68 = GPIO_MODE_GPIO,
++ .gpio69 = GPIO_MODE_GPIO,
++ .gpio70 = GPIO_MODE_GPIO,
++ .gpio71 = GPIO_MODE_GPIO,
++ .gpio72 = GPIO_MODE_NATIVE,
++ .gpio73 = GPIO_MODE_NATIVE,
++ .gpio74 = GPIO_MODE_NATIVE,
++ .gpio75 = GPIO_MODE_NATIVE,
++};
++
++static const struct pch_gpio_set3 pch_gpio_set3_direction = {
++ .gpio68 = GPIO_DIR_INPUT,
++ .gpio69 = GPIO_DIR_INPUT,
++ .gpio70 = GPIO_DIR_INPUT,
++ .gpio71 = GPIO_DIR_INPUT,
++};
++
++static const struct pch_gpio_set3 pch_gpio_set3_level = {
++};
++
++static const struct pch_gpio_set3 pch_gpio_set3_reset = {
++};
++
++const struct pch_gpio_map mainboard_gpio_map = {
++ .set1 = {
++ .mode = &pch_gpio_set1_mode,
++ .direction = &pch_gpio_set1_direction,
++ .level = &pch_gpio_set1_level,
++ .blink = &pch_gpio_set1_blink,
++ .invert = &pch_gpio_set1_invert,
++ .reset = &pch_gpio_set1_reset,
++ },
++ .set2 = {
++ .mode = &pch_gpio_set2_mode,
++ .direction = &pch_gpio_set2_direction,
++ .level = &pch_gpio_set2_level,
++ .reset = &pch_gpio_set2_reset,
++ },
++ .set3 = {
++ .mode = &pch_gpio_set3_mode,
++ .direction = &pch_gpio_set3_direction,
++ .level = &pch_gpio_set3_level,
++ .reset = &pch_gpio_set3_reset,
++ },
++};
+diff --git a/src/mainboard/dell/snb_ivb_latitude/variants/e6230/hda_verb.c b/src/mainboard/dell/snb_ivb_latitude/variants/e6230/hda_verb.c
+new file mode 100644
+index 0000000000..f6876f9e09
+--- /dev/null
++++ b/src/mainboard/dell/snb_ivb_latitude/variants/e6230/hda_verb.c
+@@ -0,0 +1,32 @@
++/* SPDX-License-Identifier: GPL-2.0-only */
++
++#include
++
++const u32 cim_verb_data[] = {
++ 0x111d76df, /* Codec Vendor / Device ID: IDT */
++ 0x10280532, /* Subsystem ID */
++ 11, /* Number of 4 dword sets */
++ AZALIA_SUBVENDOR(0, 0x10280532),
++ AZALIA_PIN_CFG(0, 0x0a, 0x03a11020),
++ AZALIA_PIN_CFG(0, 0x0b, 0x0321101f),
++ AZALIA_PIN_CFG(0, 0x0c, 0x400000f0),
++ AZALIA_PIN_CFG(0, 0x0d, 0x90170110),
++ AZALIA_PIN_CFG(0, 0x0e, 0x23011050),
++ AZALIA_PIN_CFG(0, 0x0f, 0x23a1102e),
++ AZALIA_PIN_CFG(0, 0x10, 0x400000f3),
++ AZALIA_PIN_CFG(0, 0x11, 0xd5a30130),
++ AZALIA_PIN_CFG(0, 0x1f, 0x400000f0),
++ AZALIA_PIN_CFG(0, 0x20, 0x400000f0),
++
++ 0x80862806, /* Codec Vendor / Device ID: Intel */
++ 0x80860101, /* Subsystem ID */
++ 4, /* Number of 4 dword sets */
++ AZALIA_SUBVENDOR(3, 0x80860101),
++ AZALIA_PIN_CFG(3, 0x05, 0x18560010),
++ AZALIA_PIN_CFG(3, 0x06, 0x18560020),
++ AZALIA_PIN_CFG(3, 0x07, 0x18560030),
++};
++
++const u32 pc_beep_verbs[0] = {};
++
++AZALIA_ARRAY_SIZES;
+diff --git a/src/mainboard/dell/snb_ivb_latitude/variants/e6230/overridetree.cb b/src/mainboard/dell/snb_ivb_latitude/variants/e6230/overridetree.cb
+new file mode 100644
+index 0000000000..3a0fa720da
+--- /dev/null
++++ b/src/mainboard/dell/snb_ivb_latitude/variants/e6230/overridetree.cb
+@@ -0,0 +1,40 @@
++## SPDX-License-Identifier: GPL-2.0-or-later
++
++chip northbridge/intel/sandybridge
++ device domain 0 on
++ subsystemid 0x1028 0x0532 inherit
++
++ device ref igd on
++ register "gpu_cpu_backlight" = "0x000009e9"
++ register "gpu_pch_backlight" = "0x13121312"
++ end
++
++ chip southbridge/intel/bd82x6x
++ register "usb_port_config" = "{
++ { 1, 1, 0 },
++ { 1, 1, 0 },
++ { 1, 0, 1 },
++ { 1, 2, 1 },
++ { 1, 0, 2 },
++ { 1, 0, 2 },
++ { 1, 0, 3 },
++ { 1, 1, 3 },
++ { 1, 2, 4 },
++ { 1, 1, 4 },
++ { 1, 1, 5 },
++ { 1, 1, 5 },
++ { 1, 2, 6 },
++ { 1, 0, 6 },
++ }"
++
++ device ref xhci on
++ register "superspeed_capable_ports" = "0x0000000f"
++ register "xhci_overcurrent_mapping" = "0x00000c03"
++ register "xhci_switchable_ports" = "0x0000000f"
++ end
++ device ref sata1 on
++ register "sata_port_map" = "0x31"
++ end
++ end
++ end
++end
+--
+2.39.5
+
diff --git a/config/coreboot/default/patches/0008-HACK-Disable-coreboot-related-BL31-features.patch b/config/coreboot/default/patches/0018-HACK-Disable-coreboot-related-BL31-features.patch
similarity index 78%
rename from config/coreboot/default/patches/0008-HACK-Disable-coreboot-related-BL31-features.patch
rename to config/coreboot/default/patches/0018-HACK-Disable-coreboot-related-BL31-features.patch
index bd2c56bd..9592215d 100644
--- a/config/coreboot/default/patches/0008-HACK-Disable-coreboot-related-BL31-features.patch
+++ b/config/coreboot/default/patches/0018-HACK-Disable-coreboot-related-BL31-features.patch
@@ -1,7 +1,7 @@
-From 7f650a19d30fe6157b150c5248d6086007323d72 Mon Sep 17 00:00:00 2001
+From ec27f5414c78d493ec7be4cd055ac877ce9ea178 Mon Sep 17 00:00:00 2001
From: Alper Nebi Yasak
Date: Thu, 22 Jun 2023 16:44:27 +0300
-Subject: [PATCH 08/37] HACK: Disable coreboot related BL31 features
+Subject: [PATCH 18/51] HACK: Disable coreboot related BL31 features
I don't know why, but removing this BL31 make argument lets gru-kevin
power off properly when shut down from Linux. Needs investigation.
@@ -10,10 +10,10 @@ power off properly when shut down from Linux. Needs investigation.
1 file changed, 3 deletions(-)
diff --git a/src/arch/arm64/Makefile.mk b/src/arch/arm64/Makefile.mk
-index f54c6d22fc..b075abfd42 100644
+index cb43897efd..a9e5ff399a 100644
--- a/src/arch/arm64/Makefile.mk
+++ b/src/arch/arm64/Makefile.mk
-@@ -162,9 +162,6 @@ BL31_MAKEARGS += LOG_LEVEL=40
+@@ -173,9 +173,6 @@ BL31_MAKEARGS += LOG_LEVEL=40
# Always enable crash reporting, even on a release build
BL31_MAKEARGS += CRASH_REPORTING=1
diff --git a/config/coreboot/default/patches/0009-dell-e6430-use-ME-Soft-Temporary-Disable.patch b/config/coreboot/default/patches/0019-dell-e6430-use-ME-Soft-Temporary-Disable.patch
similarity index 87%
rename from config/coreboot/default/patches/0009-dell-e6430-use-ME-Soft-Temporary-Disable.patch
rename to config/coreboot/default/patches/0019-dell-e6430-use-ME-Soft-Temporary-Disable.patch
index ca3b6264..b3d2d12c 100644
--- a/config/coreboot/default/patches/0009-dell-e6430-use-ME-Soft-Temporary-Disable.patch
+++ b/config/coreboot/default/patches/0019-dell-e6430-use-ME-Soft-Temporary-Disable.patch
@@ -1,7 +1,7 @@
-From 3f6f65ed6a435fe49534c8a0b5cb98c3eac71150 Mon Sep 17 00:00:00 2001
+From a15b59616e00c43c05d7853080859d4aefe26c5d Mon Sep 17 00:00:00 2001
From: Leah Rowe
Date: Sun, 5 Nov 2023 11:41:41 +0000
-Subject: [PATCH 09/37] dell/e6430: use ME Soft Temporary Disable
+Subject: [PATCH 19/51] dell/e6430: use ME Soft Temporary Disable
i overlooked this. it's set on other boards.
diff --git a/config/coreboot/default/patches/0010-mb-hp-Add-Compaq-Elite-8300-CMT-port.patch b/config/coreboot/default/patches/0020-mb-hp-Add-Compaq-Elite-8300-CMT-port.patch
similarity index 99%
rename from config/coreboot/default/patches/0010-mb-hp-Add-Compaq-Elite-8300-CMT-port.patch
rename to config/coreboot/default/patches/0020-mb-hp-Add-Compaq-Elite-8300-CMT-port.patch
index 2a7d5c17..46e38925 100644
--- a/config/coreboot/default/patches/0010-mb-hp-Add-Compaq-Elite-8300-CMT-port.patch
+++ b/config/coreboot/default/patches/0020-mb-hp-Add-Compaq-Elite-8300-CMT-port.patch
@@ -1,7 +1,7 @@
-From b4d48233a8d829d7285501f662d999aad898be21 Mon Sep 17 00:00:00 2001
+From 440ebbe1e10911dc3d8c53cf9eecb5519c2ecd67 Mon Sep 17 00:00:00 2001
From: Riku Viitanen
Date: Sat, 23 Dec 2023 19:02:10 +0200
-Subject: [PATCH 10/37] mb/hp: Add Compaq Elite 8300 CMT port
+Subject: [PATCH 20/51] mb/hp: Add Compaq Elite 8300 CMT port
Based on autoport and Z220 SuperIO code.
diff --git a/config/coreboot/default/patches/0011-nb-intel-haswell-make-IOMMU-a-runtime-option.patch b/config/coreboot/default/patches/0021-nb-intel-haswell-make-IOMMU-a-runtime-option.patch
similarity index 98%
rename from config/coreboot/default/patches/0011-nb-intel-haswell-make-IOMMU-a-runtime-option.patch
rename to config/coreboot/default/patches/0021-nb-intel-haswell-make-IOMMU-a-runtime-option.patch
index f8c56155..b3305e58 100644
--- a/config/coreboot/default/patches/0011-nb-intel-haswell-make-IOMMU-a-runtime-option.patch
+++ b/config/coreboot/default/patches/0021-nb-intel-haswell-make-IOMMU-a-runtime-option.patch
@@ -1,7 +1,7 @@
-From a16ff494adb1f706d402a2e167d0d53c775d0897 Mon Sep 17 00:00:00 2001
+From 4c7577314f19e934d690c4cce3642fe693400c07 Mon Sep 17 00:00:00 2001
From: Leah Rowe
Date: Sat, 2 Mar 2024 22:51:09 +0000
-Subject: [PATCH 11/37] nb/intel/haswell: make IOMMU a runtime option
+Subject: [PATCH 21/51] 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
diff --git a/config/coreboot/default/patches/0012-dell-optiplex_9020-Disable-IOMMU-by-default.patch b/config/coreboot/default/patches/0022-dell-optiplex_9020-Disable-IOMMU-by-default.patch
similarity index 87%
rename from config/coreboot/default/patches/0012-dell-optiplex_9020-Disable-IOMMU-by-default.patch
rename to config/coreboot/default/patches/0022-dell-optiplex_9020-Disable-IOMMU-by-default.patch
index e578d22a..f18f119e 100644
--- a/config/coreboot/default/patches/0012-dell-optiplex_9020-Disable-IOMMU-by-default.patch
+++ b/config/coreboot/default/patches/0022-dell-optiplex_9020-Disable-IOMMU-by-default.patch
@@ -1,7 +1,7 @@
-From 4b0536ce7cd55eedc52d13497bea59d91e8924d8 Mon Sep 17 00:00:00 2001
+From b5695d0f0dc44ed1eb1feac008e601040feda55d Mon Sep 17 00:00:00 2001
From: Leah Rowe
Date: Sat, 2 Mar 2024 23:00:09 +0000
-Subject: [PATCH 12/37] dell/optiplex_9020: Disable IOMMU by default
+Subject: [PATCH 22/51] 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
diff --git a/config/coreboot/default/patches/0013-nb-haswell-Fully-disable-iGPU-when-dGPU-is-used.patch b/config/coreboot/default/patches/0023-nb-haswell-Fully-disable-iGPU-when-dGPU-is-used.patch
similarity index 92%
rename from config/coreboot/default/patches/0013-nb-haswell-Fully-disable-iGPU-when-dGPU-is-used.patch
rename to config/coreboot/default/patches/0023-nb-haswell-Fully-disable-iGPU-when-dGPU-is-used.patch
index 89584d95..f9d80b9f 100644
--- a/config/coreboot/default/patches/0013-nb-haswell-Fully-disable-iGPU-when-dGPU-is-used.patch
+++ b/config/coreboot/default/patches/0023-nb-haswell-Fully-disable-iGPU-when-dGPU-is-used.patch
@@ -1,7 +1,7 @@
-From c8329f84b2d06581dcbeecedc38b7c4715a9cba7 Mon Sep 17 00:00:00 2001
+From d86824305f11bc684f1e91e3826158b8c7d7e0ee Mon Sep 17 00:00:00 2001
From: Leah Rowe
Date: Sat, 6 Apr 2024 01:22:47 +0100
-Subject: [PATCH 13/37] nb/haswell: Fully disable iGPU when dGPU is used
+Subject: [PATCH 23/51] 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
diff --git a/config/coreboot/default/patches/0024-Disable-compression-on-refcode-insertion.patch b/config/coreboot/default/patches/0024-Disable-compression-on-refcode-insertion.patch
deleted file mode 100644
index c557e9d7..00000000
--- a/config/coreboot/default/patches/0024-Disable-compression-on-refcode-insertion.patch
+++ /dev/null
@@ -1,31 +0,0 @@
-From b6bd33b0430f72c2fce16a3b1e41927ef540923b Mon Sep 17 00:00:00 2001
-From: Leah Rowe
-Date: Tue, 31 Dec 2024 14:42:24 +0000
-Subject: [PATCH 24/37] Disable compression on refcode insertion
-
-Compression is not reliably reproducible. In an lbmk release
-context, this means we cannot rely on vendorfile insertion.
-
-Therefore, use uncompressed refcode.
-
-Signed-off-by: Leah Rowe
----
- Makefile.mk | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/Makefile.mk b/Makefile.mk
-index 3969bfbd05..15346569f8 100644
---- a/Makefile.mk
-+++ b/Makefile.mk
-@@ -1392,7 +1392,7 @@ endif
- cbfs-files-$(CONFIG_HAVE_REFCODE_BLOB) += $(CONFIG_CBFS_PREFIX)/refcode
- $(CONFIG_CBFS_PREFIX)/refcode-file := $(REFCODE_BLOB)
- $(CONFIG_CBFS_PREFIX)/refcode-type := stage
--$(CONFIG_CBFS_PREFIX)/refcode-compression := $(CBFS_COMPRESS_FLAG)
-+$(CONFIG_CBFS_PREFIX)/refcode-compression := none
-
- cbfs-files-$(CONFIG_SEABIOS_VGA_COREBOOT) += vgaroms/seavgabios.bin
- vgaroms/seavgabios.bin-file := $(CONFIG_PAYLOAD_VGABIOS_FILE)
---
-2.39.5
-
diff --git a/config/coreboot/default/patches/0024-ec-dell-mec5035-Replace-defines-with-enums.patch b/config/coreboot/default/patches/0024-ec-dell-mec5035-Replace-defines-with-enums.patch
new file mode 100644
index 00000000..6c1118bb
--- /dev/null
+++ b/config/coreboot/default/patches/0024-ec-dell-mec5035-Replace-defines-with-enums.patch
@@ -0,0 +1,91 @@
+From a1566875789469ebd91e472301be4b359aac0a4c Mon Sep 17 00:00:00 2001
+From: Nicholas Chin
+Date: Tue, 28 May 2024 17:23:21 -0600
+Subject: [PATCH 24/51] ec/dell/mec5035: Replace defines with enums
+
+Instead of using defines for command IDs and argument values, use enums
+to provide more type safety. This also has the effect of moving the
+command IDs to a more central location instead of defines spread out
+throughout the header.
+
+Change-Id: I788531e8b70e79541213853f177326d217235ef2
+Signed-off-by: Nicholas Chin
+Reviewed-on: https://review.coreboot.org/c/coreboot/+/82998
+Tested-by: build bot (Jenkins)
+Reviewed-by: Felix Singer
+---
+ src/ec/dell/mec5035/mec5035.c | 10 +++++-----
+ src/ec/dell/mec5035/mec5035.h | 20 ++++++++++++--------
+ 2 files changed, 17 insertions(+), 13 deletions(-)
+
+diff --git a/src/ec/dell/mec5035/mec5035.c b/src/ec/dell/mec5035/mec5035.c
+index 68b6b2f7fb..dffbb7960c 100644
+--- a/src/ec/dell/mec5035/mec5035.c
++++ b/src/ec/dell/mec5035/mec5035.c
+@@ -66,17 +66,17 @@ static enum cb_err write_mailbox_regs(const u8 *data, u8 start, u8 count)
+ return CB_SUCCESS;
+ }
+
+-static void ec_command(u8 cmd)
++static void ec_command(enum mec5035_cmd cmd)
+ {
+ outb(0, MAILBOX_INDEX);
+- outb(cmd, MAILBOX_DATA);
++ outb((u8)cmd, MAILBOX_DATA);
+ wait_ec();
+ }
+
+-u8 mec5035_mouse_touchpad(u8 setting)
++u8 mec5035_mouse_touchpad(enum ec_mouse_setting setting)
+ {
+- u8 buf[15] = {0};
+- write_mailbox_regs(&setting, 2, 1);
++ u8 buf[15] = {(u8)setting};
++ write_mailbox_regs(buf, 2, 1);
+ ec_command(CMD_MOUSE_TP);
+ /* The vendor firmware reads 15 bytes starting at index 1, presumably
+ to get some sort of return code. Though I don't know for sure if
+diff --git a/src/ec/dell/mec5035/mec5035.h b/src/ec/dell/mec5035/mec5035.h
+index fa15a9d621..32f791cb01 100644
+--- a/src/ec/dell/mec5035/mec5035.h
++++ b/src/ec/dell/mec5035/mec5035.h
+@@ -7,16 +7,20 @@
+
+ #define NUM_REGISTERS 32
+
++enum mec5035_cmd {
++ CMD_MOUSE_TP = 0x1a,
++ CMD_RADIO_CTRL = 0x2b,
++ CMD_CPU_OK = 0xc2,
++};
++
+ /* Touchpad (TP) and mouse related. The EC seems to
+ default to 0 which results in the TP not working. */
+-#define CMD_MOUSE_TP 0x1a
+-#define SERIAL_MOUSE 0 /* Disable TP, force use of a serial mouse */
+-#define PS2_MOUSE 1 /* Disable TP when using a PS/2 mouse */
+-#define TP_PS2_MOUSE 2 /* Leave TP enabled when using a PS/2 mouse */
+-
+-#define CMD_CPU_OK 0xc2
++enum ec_mouse_setting {
++ SERIAL_MOUSE = 0, /* Disable TP, force use of a serial mouse */
++ PS2_MOUSE, /* Disable TP when using a PS/2 mouse */
++ TP_PS2_MOUSE /* Leave TP enabled when using a PS/2 mouse */
++};
+
+-#define CMD_RADIO_CTRL 0x2b
+ #define RADIO_CTRL_NUM_ARGS 3
+ enum ec_radio_dev {
+ RADIO_WLAN = 0,
+@@ -29,7 +33,7 @@ enum ec_radio_state {
+ RADIO_ON
+ };
+
+-u8 mec5035_mouse_touchpad(u8 setting);
++u8 mec5035_mouse_touchpad(enum ec_mouse_setting setting);
+ void mec5035_cpu_ok(void);
+ void mec5035_early_init(void);
+ void mec5035_control_radio(enum ec_radio_dev device, enum ec_radio_state state);
+--
+2.39.5
+
diff --git a/config/coreboot/default/patches/0014-ec-dell-mec5035-Add-S3-suspend-SMI-handler.patch b/config/coreboot/default/patches/0025-ec-dell-mec5035-Add-S3-suspend-SMI-handler.patch
similarity index 97%
rename from config/coreboot/default/patches/0014-ec-dell-mec5035-Add-S3-suspend-SMI-handler.patch
rename to config/coreboot/default/patches/0025-ec-dell-mec5035-Add-S3-suspend-SMI-handler.patch
index 70556090..ed620a3e 100644
--- a/config/coreboot/default/patches/0014-ec-dell-mec5035-Add-S3-suspend-SMI-handler.patch
+++ b/config/coreboot/default/patches/0025-ec-dell-mec5035-Add-S3-suspend-SMI-handler.patch
@@ -1,7 +1,7 @@
-From 73dbf291631fdbae2d8e8a761c147523c8d9e65c Mon Sep 17 00:00:00 2001
+From 2fdd5bbb2bbec76c3c2238c4cd471b9b63073942 Mon Sep 17 00:00:00 2001
From: Nicholas Chin
Date: Fri, 3 May 2024 11:03:32 -0600
-Subject: [PATCH 14/37] ec/dell/mec5035: Add S3 suspend SMI handler
+Subject: [PATCH 25/51] ec/dell/mec5035: Add S3 suspend SMI handler
This is necessary for S3 resume to work on SNB and newer Dell Latitude
laptops. If a command isn't sent, the EC cuts power to the DIMMs,
diff --git a/config/coreboot/default/patches/0025-nb-intel-Disable-stack-overflow-debug-options.patch b/config/coreboot/default/patches/0025-nb-intel-Disable-stack-overflow-debug-options.patch
deleted file mode 100644
index 696be518..00000000
--- a/config/coreboot/default/patches/0025-nb-intel-Disable-stack-overflow-debug-options.patch
+++ /dev/null
@@ -1,187 +0,0 @@
-From fc4c65f3bb807b9fc766745a70f92729b0b8d99e Mon Sep 17 00:00:00 2001
-From: Leah Rowe
-Date: Mon, 21 Apr 2025 02:58:47 +0100
-Subject: [PATCH 25/37] nb/intel/*: Disable stack overflow debug options
-
-Signed-off-by: Leah Rowe
----
- src/northbridge/intel/e7505/Kconfig | 9 +++++++++
- src/northbridge/intel/gm45/Kconfig | 9 +++++++++
- src/northbridge/intel/haswell/Kconfig | 9 +++++++++
- src/northbridge/intel/i440bx/Kconfig | 13 +++++++++++++
- src/northbridge/intel/i945/Kconfig | 9 +++++++++
- src/northbridge/intel/ironlake/Kconfig | 9 +++++++++
- src/northbridge/intel/pineview/Kconfig | 9 +++++++++
- src/northbridge/intel/sandybridge/Kconfig | 9 +++++++++
- src/northbridge/intel/x4x/Kconfig | 9 +++++++++
- 9 files changed, 85 insertions(+)
-
-diff --git a/src/northbridge/intel/e7505/Kconfig b/src/northbridge/intel/e7505/Kconfig
-index 039a7396f8..ddcb986f10 100644
---- a/src/northbridge/intel/e7505/Kconfig
-+++ b/src/northbridge/intel/e7505/Kconfig
-@@ -7,3 +7,12 @@ config NORTHBRIDGE_INTEL_E7505
- select NO_CBFS_MCACHE
- select SMM_TSEG
- select NEED_SMALL_2MB_PAGE_TABLES
-+
-+# Override DEBUG Kconfig to avoid false alarm about stack overflow.
-+config DEBUG_STACK_OVERFLOW_BREAKPOINTS
-+ bool
-+ default n
-+
-+config DEBUG_STACK_OVERFLOW_BREAKPOINTS_IN_ALL_STAGES
-+ bool
-+ default n
-diff --git a/src/northbridge/intel/gm45/Kconfig b/src/northbridge/intel/gm45/Kconfig
-index fc5df8b11a..95e3644b73 100644
---- a/src/northbridge/intel/gm45/Kconfig
-+++ b/src/northbridge/intel/gm45/Kconfig
-@@ -58,4 +58,13 @@ config FIXED_DMIBAR_MMIO_BASE
- config FIXED_EPBAR_MMIO_BASE
- default 0xfed19000
-
-+# Override DEBUG Kconfig to avoid false alarm about stack overflow.
-+config DEBUG_STACK_OVERFLOW_BREAKPOINTS
-+ bool
-+ default n
-+
-+config DEBUG_STACK_OVERFLOW_BREAKPOINTS_IN_ALL_STAGES
-+ bool
-+ default n
-+
- endif
-diff --git a/src/northbridge/intel/haswell/Kconfig b/src/northbridge/intel/haswell/Kconfig
-index 6191cb6ccf..0f5b5c7241 100644
---- a/src/northbridge/intel/haswell/Kconfig
-+++ b/src/northbridge/intel/haswell/Kconfig
-@@ -10,6 +10,15 @@ config NORTHBRIDGE_INTEL_HASWELL
-
- if NORTHBRIDGE_INTEL_HASWELL
-
-+# Override DEBUG Kconfig to avoid false alarm about stack overflow.
-+config DEBUG_STACK_OVERFLOW_BREAKPOINTS
-+ bool
-+ default n
-+
-+config DEBUG_STACK_OVERFLOW_BREAKPOINTS_IN_ALL_STAGES
-+ bool
-+ default n
-+
- config USE_NATIVE_RAMINIT
- bool "[NOT COMPLETE] Use native raminit"
- default n
-diff --git a/src/northbridge/intel/i440bx/Kconfig b/src/northbridge/intel/i440bx/Kconfig
-index dbb2d7436b..5e9418b6a9 100644
---- a/src/northbridge/intel/i440bx/Kconfig
-+++ b/src/northbridge/intel/i440bx/Kconfig
-@@ -19,3 +19,16 @@ config SDRAMPWR_4DIMM
- If your board has 4 DIMM slots, you must use select this option, in
- your Kconfig file of the board. On boards with 3 DIMM slots,
- do _not_ select this option.
-+
-+if NORTHBRIDGE_INTEL_I440BX
-+
-+# Override DEBUG Kconfig to avoid false alarm about stack overflow.
-+config DEBUG_STACK_OVERFLOW_BREAKPOINTS
-+ bool
-+ default n
-+
-+config DEBUG_STACK_OVERFLOW_BREAKPOINTS_IN_ALL_STAGES
-+ bool
-+ default n
-+
-+endif
-diff --git a/src/northbridge/intel/i945/Kconfig b/src/northbridge/intel/i945/Kconfig
-index 32eff1a611..9479d75c07 100644
---- a/src/northbridge/intel/i945/Kconfig
-+++ b/src/northbridge/intel/i945/Kconfig
-@@ -89,4 +89,13 @@ config FIXED_DMIBAR_MMIO_BASE
- config FIXED_EPBAR_MMIO_BASE
- default 0xfed19000
-
-+# Override DEBUG Kconfig to avoid false alarm about stack overflow.
-+config DEBUG_STACK_OVERFLOW_BREAKPOINTS
-+ bool
-+ default n
-+
-+config DEBUG_STACK_OVERFLOW_BREAKPOINTS_IN_ALL_STAGES
-+ bool
-+ default n
-+
- endif
-diff --git a/src/northbridge/intel/ironlake/Kconfig b/src/northbridge/intel/ironlake/Kconfig
-index 2bafebf92e..16b81705bb 100644
---- a/src/northbridge/intel/ironlake/Kconfig
-+++ b/src/northbridge/intel/ironlake/Kconfig
-@@ -63,4 +63,13 @@ config FIXED_DMIBAR_MMIO_BASE
- config FIXED_EPBAR_MMIO_BASE
- default 0xfed19000
-
-+# Override DEBUG Kconfig to avoid false alarm about stack overflow.
-+config DEBUG_STACK_OVERFLOW_BREAKPOINTS
-+ bool
-+ default n
-+
-+config DEBUG_STACK_OVERFLOW_BREAKPOINTS_IN_ALL_STAGES
-+ bool
-+ default n
-+
- endif
-diff --git a/src/northbridge/intel/pineview/Kconfig b/src/northbridge/intel/pineview/Kconfig
-index 59cfcd5e0a..a3ad8d3425 100644
---- a/src/northbridge/intel/pineview/Kconfig
-+++ b/src/northbridge/intel/pineview/Kconfig
-@@ -42,4 +42,13 @@ config FIXED_EPBAR_MMIO_BASE
- config DOMAIN_RESOURCE_32BIT_LIMIT
- default 0xfec00000
-
-+# Override DEBUG Kconfig to avoid false alarm about stack overflow.
-+config DEBUG_STACK_OVERFLOW_BREAKPOINTS
-+ bool
-+ default n
-+
-+config DEBUG_STACK_OVERFLOW_BREAKPOINTS_IN_ALL_STAGES
-+ bool
-+ default n
-+
- endif
-diff --git a/src/northbridge/intel/sandybridge/Kconfig b/src/northbridge/intel/sandybridge/Kconfig
-index 973eed8bbd..6387cf926d 100644
---- a/src/northbridge/intel/sandybridge/Kconfig
-+++ b/src/northbridge/intel/sandybridge/Kconfig
-@@ -208,4 +208,13 @@ config IGD_DEFAULT_UMA_INDEX
- default 2 if IGD_DEFAULT_UMA_SIZE_96MB
- default 3 if IGD_DEFAULT_UMA_SIZE_128MB
-
-+# Override DEBUG Kconfig to avoid false alarm about stack overflow.
-+config DEBUG_STACK_OVERFLOW_BREAKPOINTS
-+ bool
-+ default n
-+
-+config DEBUG_STACK_OVERFLOW_BREAKPOINTS_IN_ALL_STAGES
-+ bool
-+ default n
-+
- endif
-diff --git a/src/northbridge/intel/x4x/Kconfig b/src/northbridge/intel/x4x/Kconfig
-index 6430319f6a..1803ef5733 100644
---- a/src/northbridge/intel/x4x/Kconfig
-+++ b/src/northbridge/intel/x4x/Kconfig
-@@ -53,4 +53,13 @@ config FIXED_DMIBAR_MMIO_BASE
- config FIXED_EPBAR_MMIO_BASE
- default 0xfed19000
-
-+# Override DEBUG Kconfig to avoid false alarm about stack overflow.
-+config DEBUG_STACK_OVERFLOW_BREAKPOINTS
-+ bool
-+ default n
-+
-+config DEBUG_STACK_OVERFLOW_BREAKPOINTS_IN_ALL_STAGES
-+ bool
-+ default n
-+
- endif
---
-2.39.5
-
diff --git a/config/coreboot/default/patches/0015-nb-haswell-lock-policy-regs-when-disabling-IOMMU.patch b/config/coreboot/default/patches/0026-nb-haswell-lock-policy-regs-when-disabling-IOMMU.patch
similarity index 93%
rename from config/coreboot/default/patches/0015-nb-haswell-lock-policy-regs-when-disabling-IOMMU.patch
rename to config/coreboot/default/patches/0026-nb-haswell-lock-policy-regs-when-disabling-IOMMU.patch
index 9525b8ce..fe9034b0 100644
--- a/config/coreboot/default/patches/0015-nb-haswell-lock-policy-regs-when-disabling-IOMMU.patch
+++ b/config/coreboot/default/patches/0026-nb-haswell-lock-policy-regs-when-disabling-IOMMU.patch
@@ -1,7 +1,7 @@
-From a507fe609a2e99c95218ec430916eaf4c3cb61d9 Mon Sep 17 00:00:00 2001
+From ce7d65790b9b8656ebbaa0ca715adff6a9c25588 Mon Sep 17 00:00:00 2001
From: Leah Rowe
Date: Sat, 4 May 2024 02:00:53 +0100
-Subject: [PATCH 15/37] nb/haswell: lock policy regs when disabling IOMMU
+Subject: [PATCH 26/51] 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
diff --git a/config/coreboot/default/patches/0016-nb-intel-gm45-Make-DDR2-raminit-work.patch b/config/coreboot/default/patches/0027-nb-intel-gm45-Make-DDR2-raminit-work.patch
similarity index 98%
rename from config/coreboot/default/patches/0016-nb-intel-gm45-Make-DDR2-raminit-work.patch
rename to config/coreboot/default/patches/0027-nb-intel-gm45-Make-DDR2-raminit-work.patch
index 091a15c4..28fc679f 100644
--- a/config/coreboot/default/patches/0016-nb-intel-gm45-Make-DDR2-raminit-work.patch
+++ b/config/coreboot/default/patches/0027-nb-intel-gm45-Make-DDR2-raminit-work.patch
@@ -1,7 +1,7 @@
-From 9e0a6aa376db81f9409eda92b6783a8262c1fedb Mon Sep 17 00:00:00 2001
+From c6181fe0c8b58cb5a4523d5763fc5fcdf61b3f10 Mon Sep 17 00:00:00 2001
From: Angel Pons
Date: Mon, 10 May 2021 22:40:59 +0200
-Subject: [PATCH 16/37] nb/intel/gm45: Make DDR2 raminit work
+Subject: [PATCH 27/51] nb/intel/gm45: Make DDR2 raminit work
List of changes:
- Update some timing and ODT values
diff --git a/config/coreboot/default/patches/0017-nb-gm45-Fix-Angel-s-DDR2-RCOMP-fix-on-DDR3-boards.patch b/config/coreboot/default/patches/0028-nb-gm45-Fix-Angel-s-DDR2-RCOMP-fix-on-DDR3-boards.patch
similarity index 98%
rename from config/coreboot/default/patches/0017-nb-gm45-Fix-Angel-s-DDR2-RCOMP-fix-on-DDR3-boards.patch
rename to config/coreboot/default/patches/0028-nb-gm45-Fix-Angel-s-DDR2-RCOMP-fix-on-DDR3-boards.patch
index 4ba74757..92e59129 100644
--- a/config/coreboot/default/patches/0017-nb-gm45-Fix-Angel-s-DDR2-RCOMP-fix-on-DDR3-boards.patch
+++ b/config/coreboot/default/patches/0028-nb-gm45-Fix-Angel-s-DDR2-RCOMP-fix-on-DDR3-boards.patch
@@ -1,7 +1,7 @@
-From 6acc310c1d695d47c148296da9da189de21d58be Mon Sep 17 00:00:00 2001
+From b6f75374fa38e0b097c9eadb4916112707cb6747 Mon Sep 17 00:00:00 2001
From: Leah Rowe
Date: Tue, 6 Aug 2024 00:50:24 +0100
-Subject: [PATCH 17/37] nb/gm45: Fix Angel's DDR2 RCOMP fix on DDR3 boards
+Subject: [PATCH 28/51] nb/gm45: Fix Angel's DDR2 RCOMP fix on DDR3 boards
We add this patch:
diff --git a/config/coreboot/default/patches/0018-mb-dell-e6400-Use-100-MHz-reference-clock-for-displa.patch b/config/coreboot/default/patches/0029-mb-dell-e6400-Use-100-MHz-reference-clock-for-displa.patch
similarity index 90%
rename from config/coreboot/default/patches/0018-mb-dell-e6400-Use-100-MHz-reference-clock-for-displa.patch
rename to config/coreboot/default/patches/0029-mb-dell-e6400-Use-100-MHz-reference-clock-for-displa.patch
index 1cf7c0ac..e31cb64c 100644
--- a/config/coreboot/default/patches/0018-mb-dell-e6400-Use-100-MHz-reference-clock-for-displa.patch
+++ b/config/coreboot/default/patches/0029-mb-dell-e6400-Use-100-MHz-reference-clock-for-displa.patch
@@ -1,7 +1,7 @@
-From 7461210ecc7c8e41f3f941bd5ce7943e5f66c711 Mon Sep 17 00:00:00 2001
+From d3045b3dcebd94b78df2129cd81a20adf215e46a Mon Sep 17 00:00:00 2001
From: Nicholas Chin
Date: Mon, 20 May 2024 10:24:16 -0600
-Subject: [PATCH 18/37] mb/dell/e6400: Use 100 MHz reference clock for display
+Subject: [PATCH 29/51] mb/dell/e6400: Use 100 MHz reference clock for display
The E6400 uses a 100 MHz reference clock for spread spectrum support on
LVDS, whereas libgfxinit previously assumed a 96 MHz input clock. For
@@ -33,7 +33,7 @@ index 417d95fd5d..6fe1b1c456 100644
default "dell/e6400"
diff --git a/src/northbridge/intel/gm45/Kconfig b/src/northbridge/intel/gm45/Kconfig
-index fef0d735b3..fc5df8b11a 100644
+index 8059e7ee80..5df5a93296 100644
--- a/src/northbridge/intel/gm45/Kconfig
+++ b/src/northbridge/intel/gm45/Kconfig
@@ -14,6 +14,10 @@ config NORTHBRIDGE_INTEL_GM45
diff --git a/config/coreboot/default/patches/0030-haswell-NRI-Initialise-MPLL.patch b/config/coreboot/default/patches/0030-haswell-NRI-Initialise-MPLL.patch
new file mode 100644
index 00000000..a1cf9b75
--- /dev/null
+++ b/config/coreboot/default/patches/0030-haswell-NRI-Initialise-MPLL.patch
@@ -0,0 +1,348 @@
+From 0966980e52286985fcd0fac6325bdd99f35ebcb8 Mon Sep 17 00:00:00 2001
+From: Angel Pons
+Date: Thu, 11 Apr 2024 17:25:07 +0200
+Subject: [PATCH 30/51] 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
+---
+ .../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
++#include
++#include
++#include
++#include
++#include
++
++#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
++#include
++#include
++#include
++#include
++
++#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 8078c9c386..15a1550424 100644
+--- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h
++++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h
+@@ -24,6 +24,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 **/
+ };
+
+@@ -83,10 +85,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.5
+
diff --git a/config/coreboot/default/patches/0031-haswell-NRI-Post-process-selected-timings.patch b/config/coreboot/default/patches/0031-haswell-NRI-Post-process-selected-timings.patch
new file mode 100644
index 00000000..426cef35
--- /dev/null
+++ b/config/coreboot/default/patches/0031-haswell-NRI-Post-process-selected-timings.patch
@@ -0,0 +1,249 @@
+From 1dc22174b9b28b9ea9af59183ffd5d86d19a2721 Mon Sep 17 00:00:00 2001
+From: Angel Pons
+Date: Sat, 7 May 2022 16:29:55 +0200
+Subject: [PATCH 31/51] 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
+---
+ .../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
++#include
++
++#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 15a1550424..e0ebd3a2a7 100644
+--- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h
++++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h
+@@ -79,6 +79,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 */
+@@ -97,7 +100,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 eff993800b..4f7fe46494 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.5
+
diff --git a/config/coreboot/default/patches/0032-haswell-NRI-Configure-initial-MC-settings.patch b/config/coreboot/default/patches/0032-haswell-NRI-Configure-initial-MC-settings.patch
new file mode 100644
index 00000000..e16f4e3d
--- /dev/null
+++ b/config/coreboot/default/patches/0032-haswell-NRI-Configure-initial-MC-settings.patch
@@ -0,0 +1,1594 @@
+From a4f5deb78c2d4132bf857c57ffd53684f942ba62 Mon Sep 17 00:00:00 2001
+From: Angel Pons
+Date: Sat, 7 May 2022 17:22:07 +0200
+Subject: [PATCH 32/51] 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
+---
+ .../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
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++
++#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 e0ebd3a2a7..fffa6d5450 100644
+--- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h
++++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h
+@@ -3,16 +3,41 @@
+ #ifndef HASWELL_RAMINIT_NATIVE_H
+ #define HASWELL_RAMINIT_NATIVE_H
+
++#include
+ #include
+ #include
++#include
++#include
++
++#include "reg_structs.h"
+
+ /** TODO (Angel): Remove this after in-review patches are submitted **/
+ #define SPD_LEN SPD_SIZE_MAX_DDR3
+
++/* 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,
+@@ -58,6 +83,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;
+@@ -94,16 +122,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.5
+
diff --git a/config/coreboot/default/patches/0032-lenovo-Add-Kconfig-option-CONFIG_LENOVO_TBFW_BIN.patch b/config/coreboot/default/patches/0032-lenovo-Add-Kconfig-option-CONFIG_LENOVO_TBFW_BIN.patch
deleted file mode 100644
index e60c102f..00000000
--- a/config/coreboot/default/patches/0032-lenovo-Add-Kconfig-option-CONFIG_LENOVO_TBFW_BIN.patch
+++ /dev/null
@@ -1,78 +0,0 @@
-From 5d8930edfa1d9537ba80e24c0cf8f0c9e4e9ec72 Mon Sep 17 00:00:00 2001
-From: Leah Rowe
-Date: Wed, 18 Dec 2024 02:06:18 +0000
-Subject: [PATCH 32/37] lenovo: Add Kconfig option CONFIG_LENOVO_TBFW_BIN
-
-This is used by lbmk to know where a tb.bin file goes,
-when extracting and padding TBT.bin from Lenovo ThunderBolt
-firmware updates on T480/T480s and other machines, grabbing
-Lenovo update files.
-
-Not used in any builds, so it's not relevant for ./mk inject
-
-However, the ThunderBolt firmware is now auto-downloaded on
-T480/T480s. This is not inserted, because it doesn't go in
-the main flash, but the resulting ROM image can be flashed
-on the TB controller's separate flash chip.
-
-Locations are as follows:
-
-vendorfiles/t480s/tb.bin
-vendorfiles/t480/tb.bin
-
-This can be used for other affected ThinkPads when they're
-added to Libreboot, but note that Lenovo provides different
-TB firmware files for each machine.
-
-Since I assume it's the same TB controller on all of those
-machines, I have to wonder: what difference is there between
-the various TBT.bin files provided by Lenovo, and how do they
-differ in terms of actual flashed configuration?
-
-We simply flash the padded TBT.bin when updating the firmware,
-flashing externally. That's what this patch is for, so that
-lbmk can auto-download them.
-
-Signed-off-by: Leah Rowe
----
- src/mainboard/lenovo/Kconfig | 26 ++++++++++++++++++++++++++
- 1 file changed, 26 insertions(+)
-
-diff --git a/src/mainboard/lenovo/Kconfig b/src/mainboard/lenovo/Kconfig
-index 2ffbaab85f..512b326381 100644
---- a/src/mainboard/lenovo/Kconfig
-+++ b/src/mainboard/lenovo/Kconfig
-@@ -18,4 +18,30 @@ config MAINBOARD_FAMILY
- string
- default MAINBOARD_PART_NUMBER
-
-+config LENOVO_TBFW_BIN
-+ string "Lenovo ThunderBolt firmware bin file"
-+ default ""
-+ help
-+ ThunderBolt firmware for certain ThinkPad models e.g. T480.
-+ Not used in the actual build. Libreboot's build system uses this
-+ along with config/vendor/*/pkg.cfg entries defining a URL to the
-+ Lenovo download link and hash. The resulting file when processed by
-+ lbmk can be flashed to the ThunderBolt firmware's 25XX NOR device.
-+ Earlier versions of this firmware had debug commands enabled that
-+ sent logs to said flash IC, and it would quickly fill up, bricking
-+ the ThunderBolt controller. With these updates, flashed externally,
-+ you can fix the issue if present or otherwise prevent it. The benefit
-+ here is that you then don't need to use Windows or a boot disk. You
-+ can flash the TB firmware while flashing Libreboot firmware. Easy!
-+ Look for these variables in lbmk:
-+ TBFW_url TBFW_url_bkup TBFW_hash and look at how it handles that and
-+ CONFIG_LENOVO_TBFW_BIN, in lbmk's include/vendor.sh file.
-+ The path set by CONFIG_LENOVO_TBFW_BIN is used by lbmk when extracting
-+ the firmware, putting it at that desired location. In this way, lbmk
-+ can auto-download such firmware. E.g. ./mk -d coreboot t480_fsp_16mb
-+ and it appears at vendorfiles/t480/tb.bin fully padded and everything!
-+
-+ Just leave this blank if you don't care about this option. It's not
-+ useful for every ThinkPad, only certain models.
-+
- endif # VENDOR_LENOVO
---
-2.39.5
-
diff --git a/config/coreboot/default/patches/0033-haswell-NRI-Add-timings-refresh-programming.patch b/config/coreboot/default/patches/0033-haswell-NRI-Add-timings-refresh-programming.patch
new file mode 100644
index 00000000..3ec3b57b
--- /dev/null
+++ b/config/coreboot/default/patches/0033-haswell-NRI-Add-timings-refresh-programming.patch
@@ -0,0 +1,541 @@
+From 8f94c0428eea2145a97de943b093dee29001c4f9 Mon Sep 17 00:00:00 2001
+From: Angel Pons
+Date: Sat, 7 May 2022 20:59:58 +0200
+Subject: [PATCH 33/51] haswell NRI: Add timings/refresh programming
+
+Program the registers with timing and refresh parameters.
+
+Change-Id: Id2ea339d2c9ea8b56c71d6e88ec76949653ff5c2
+Signed-off-by: Angel Pons
+---
+ .../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 fffa6d5450..5915a2bab0 100644
+--- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h
++++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h
+@@ -156,6 +156,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)
+@@ -201,6 +207,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
++#include
++#include
++#include
++#include
++#include
++
+ #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.5
+
diff --git a/config/coreboot/default/patches/0033-soc-intel-skylake-Don-t-compress-FSP-S.patch b/config/coreboot/default/patches/0033-soc-intel-skylake-Don-t-compress-FSP-S.patch
deleted file mode 100644
index a7ab5a96..00000000
--- a/config/coreboot/default/patches/0033-soc-intel-skylake-Don-t-compress-FSP-S.patch
+++ /dev/null
@@ -1,36 +0,0 @@
-From 49cee334bc7fe9a78b9355b5256a37984bac385a Mon Sep 17 00:00:00 2001
-From: Leah Rowe
-Date: Thu, 26 Dec 2024 19:45:20 +0000
-Subject: [PATCH 33/37] soc/intel/skylake: Don't compress FSP-S
-
-Build systems like lbmk need to reproducibly insert
-certain vendor files on release images.
-
-Compression isn't always reproducible, and making it
-so costs a lot more time than simply disabling compression.
-
-With this change, the FSP-S module will now be inserted
-without compression, which means that there will now be
-about 40KB of extra space used in the flash.
-
-Signed-off-by: Leah Rowe
----
- src/soc/intel/skylake/Kconfig | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
-diff --git a/src/soc/intel/skylake/Kconfig b/src/soc/intel/skylake/Kconfig
-index 9191ed0ff8..d51ffaef7b 100644
---- a/src/soc/intel/skylake/Kconfig
-+++ b/src/soc/intel/skylake/Kconfig
-@@ -12,7 +12,7 @@ config SOC_INTEL_COMMON_SKYLAKE_BASE
- select CPU_SUPPORTS_PM_TIMER_EMULATION
- select DRIVERS_USB_ACPI
- select EDK2_CPU_TIMER_LIB if PAYLOAD_EDK2
-- select FSP_COMPRESS_FSP_S_LZ4
-+# select FSP_COMPRESS_FSP_S_LZ4
- select FSP_M_XIP
- select GENERIC_GPIO_LIB
- select HAVE_FSP_GOP
---
-2.39.5
-
diff --git a/config/coreboot/default/patches/0034-haswell-NRI-Program-memory-map.patch b/config/coreboot/default/patches/0034-haswell-NRI-Program-memory-map.patch
new file mode 100644
index 00000000..bb3ed03d
--- /dev/null
+++ b/config/coreboot/default/patches/0034-haswell-NRI-Program-memory-map.patch
@@ -0,0 +1,263 @@
+From ded914f236f76715aa43cb439a3de7df9a3dfa11 Mon Sep 17 00:00:00 2001
+From: Angel Pons
+Date: Sat, 7 May 2022 21:24:50 +0200
+Subject: [PATCH 34/51] 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
+---
+ .../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
++#include
++#include
++#include
++
++#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 5915a2bab0..8f937c4ccd 100644
+--- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h
++++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h
+@@ -203,6 +203,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.5
+
diff --git a/config/coreboot/default/patches/0034-soc-intel-pmc-Hardcoded-poweroff-after-power-fail.patch b/config/coreboot/default/patches/0034-soc-intel-pmc-Hardcoded-poweroff-after-power-fail.patch
deleted file mode 100644
index ceee75c4..00000000
--- a/config/coreboot/default/patches/0034-soc-intel-pmc-Hardcoded-poweroff-after-power-fail.patch
+++ /dev/null
@@ -1,82 +0,0 @@
-From 09740dc9d43a8dc24b7416b70476796515af6581 Mon Sep 17 00:00:00 2001
-From: Leah Rowe
-Date: Tue, 31 Dec 2024 01:40:42 +0000
-Subject: [PATCH 34/37] soc/intel/pmc: Hardcoded poweroff after power fail
-
-Coreboot can set the power state for power on after previous
-power failure, based on the option table. On the ThinkPad T480,
-we have no nvram and, due to coreboot's design, we therefore
-have no option table, so the default setting is enabled.
-
-In my testing, this seems to be that the system will turn on
-after a power failure. If your ThinkPad was previously in a state
-where it wouldn't turn on when plugging in the power, it'd be fine.
-
-If your battery ran out later on, this would be triggered and
-your ThinkPad would permanently turn on, when plugging in a charger,
-and there is currently no way to configure this behaviour.
-
-We currently only use the common SoC PMC code on the ThinkPad
-T480, T480s and the Dell OptiPlex 3050 Micro, at the time of
-this patch, and it is desirable that the system be set to power
-off after power fail anyway.
-
-In some cases, you might want the opposite, for example if you're
-running a server. This will be documented on the website, for that
-reason.
-
-Signed-off-by: Leah Rowe
----
- src/soc/intel/common/block/pmc/pmclib.c | 36 +++----------------------
- 1 file changed, 4 insertions(+), 32 deletions(-)
-
-diff --git a/src/soc/intel/common/block/pmc/pmclib.c b/src/soc/intel/common/block/pmc/pmclib.c
-index 64b9bb997c..7823775bcb 100644
---- a/src/soc/intel/common/block/pmc/pmclib.c
-+++ b/src/soc/intel/common/block/pmc/pmclib.c
-@@ -776,38 +776,10 @@ void pmc_clear_pmcon_sts(void)
-
- void pmc_set_power_failure_state(const bool target_on)
- {
-- const unsigned int state = get_uint_option("power_on_after_fail",
-- CONFIG_MAINBOARD_POWER_FAILURE_STATE);
--
-- /*
-- * On the shutdown path (target_on == false), we only need to
-- * update the register for MAINBOARD_POWER_STATE_PREVIOUS. For
-- * all other cases, we don't write the register to avoid clob-
-- * bering the value set on the boot path. This is necessary,
-- * for instance, when we can't access the option backend in SMM.
-- */
--
-- switch (state) {
-- case MAINBOARD_POWER_STATE_OFF:
-- if (!target_on)
-- break;
-- printk(BIOS_INFO, "Set power off after power failure.\n");
-- pmc_soc_set_afterg3_en(false);
-- break;
-- case MAINBOARD_POWER_STATE_ON:
-- if (!target_on)
-- break;
-- printk(BIOS_INFO, "Set power on after power failure.\n");
-- pmc_soc_set_afterg3_en(true);
-- break;
-- case MAINBOARD_POWER_STATE_PREVIOUS:
-- printk(BIOS_INFO, "Keep power state after power failure.\n");
-- pmc_soc_set_afterg3_en(target_on);
-- break;
-- default:
-- printk(BIOS_WARNING, "Unknown power-failure state: %d\n", state);
-- break;
-- }
-+ if (!target_on)
-+ return;
-+ printk(BIOS_INFO, "Set power off after power failure.\n");
-+ pmc_soc_set_afterg3_en(false);
- }
-
- /* This function returns the highest assertion duration of the SLP_Sx assertion widths */
---
-2.39.5
-
diff --git a/config/coreboot/default/patches/0035-haswell-NRI-Add-DDR3-JEDEC-reset-and-init.patch b/config/coreboot/default/patches/0035-haswell-NRI-Add-DDR3-JEDEC-reset-and-init.patch
new file mode 100644
index 00000000..29bdec9f
--- /dev/null
+++ b/config/coreboot/default/patches/0035-haswell-NRI-Add-DDR3-JEDEC-reset-and-init.patch
@@ -0,0 +1,1036 @@
+From db2b383a8ee5a4fc45c9ce0003ae45f25ed51f86 Mon Sep 17 00:00:00 2001
+From: Angel Pons
+Date: Sat, 7 May 2022 21:49:40 +0200
+Subject: [PATCH 35/51] 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
+---
+ .../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