summaryrefslogtreecommitdiff
path: root/resources/coreboot/haswell/patches/0012-haswell-NRI-Post-process-selected-timings.patch
diff options
context:
space:
mode:
Diffstat (limited to 'resources/coreboot/haswell/patches/0012-haswell-NRI-Post-process-selected-timings.patch')
-rw-r--r--resources/coreboot/haswell/patches/0012-haswell-NRI-Post-process-selected-timings.patch249
1 files changed, 249 insertions, 0 deletions
diff --git a/resources/coreboot/haswell/patches/0012-haswell-NRI-Post-process-selected-timings.patch b/resources/coreboot/haswell/patches/0012-haswell-NRI-Post-process-selected-timings.patch
new file mode 100644
index 00000000..e38f8e57
--- /dev/null
+++ b/resources/coreboot/haswell/patches/0012-haswell-NRI-Post-process-selected-timings.patch
@@ -0,0 +1,249 @@
+From faabed9ca8974b2e7192c55b59a9d28d75e72df6 Mon Sep 17 00:00:00 2001
+From: Angel Pons <th3fanbus@gmail.com>
+Date: Sat, 7 May 2022 16:29:55 +0200
+Subject: [PATCH 12/26] haswell NRI: Post-process selected timings
+
+Once the MPLL has been initialised, convert the timings from the SPD to
+be in DCLKs, which is what the hardware expects. In addition, calculate
+the values for tREFI and tXP.
+
+Change-Id: Id02caf858f75b9e08016762b3aefda282b274386
+Signed-off-by: Angel Pons <th3fanbus@gmail.com>
+---
+ .../intel/haswell/native_raminit/Makefile.inc | 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.inc b/src/northbridge/intel/haswell/native_raminit/Makefile.inc
+index c125d84f0b..2769e0bbb4 100644
+--- a/src/northbridge/intel/haswell/native_raminit/Makefile.inc
++++ b/src/northbridge/intel/haswell/native_raminit/Makefile.inc
+@@ -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..038686c844
+--- /dev/null
++++ b/src/northbridge/intel/haswell/native_raminit/lookup_timings.c
+@@ -0,0 +1,62 @@
++/* SPDX-License-Identifier: GPL-2.0-or-later */
++
++#include <commonlib/clamp.h>
++#include <types.h>
++
++#include "raminit_native.h"
++
++struct timing_lookup {
++ uint32_t clock;
++ uint32_t value;
++};
++
++static uint32_t lookup_timing(
++ const uint32_t mem_clock_mhz,
++ const struct timing_lookup *const lookup,
++ const size_t length)
++{
++ /* Fall back to the last index */
++ size_t i;
++ for (i = 0; i < length - 1; i++) {
++ /* Account for imprecise frequency values */
++ if ((mem_clock_mhz - 5) <= lookup[i].clock)
++ break;
++ }
++ return lookup[i].value;
++}
++
++static const uint32_t fmax = UINT32_MAX;
++
++uint8_t get_tCWL(const uint32_t mem_clock_mhz)
++{
++ const struct timing_lookup lut[] = {
++ { 400, 5 },
++ { 533, 6 },
++ { 666, 7 },
++ { 800, 8 },
++ { 933, 9 },
++ { 1066, 10 },
++ { 1200, 11 },
++ { fmax, 12 },
++ };
++ return lookup_timing(mem_clock_mhz, lut, ARRAY_SIZE(lut));
++}
++
++/* tREFI = 7800 ns * DDR MHz */
++uint32_t get_tREFI(const uint32_t mem_clock_mhz)
++{
++ return (mem_clock_mhz * 7800) / 1000;
++}
++
++uint32_t get_tXP(const uint32_t mem_clock_mhz)
++{
++ const struct timing_lookup lut[] = {
++ { 400, 3 },
++ { 666, 4 },
++ { 800, 5 },
++ { 933, 6 },
++ { 1066, 7 },
++ { fmax, 8 },
++ };
++ return lookup_timing(mem_clock_mhz, lut, ARRAY_SIZE(lut));
++}
+diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_main.c b/src/northbridge/intel/haswell/native_raminit/raminit_main.c
+index 09545422c0..5f2be980d4 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 @@ struct task_entry {
+ static const struct task_entry cold_boot[] = {
+ { collect_spd_info, true, "PROCSPD", },
+ { initialise_mpll, true, "INITMPLL", },
++ { convert_timings, true, "CONVTIM", },
+ };
+
+ /* Return a generic stepping value to make stepping checks simpler */
+diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h
+index a54581abc7..01e5ed1bd6 100644
+--- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h
++++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h
+@@ -78,6 +78,9 @@ struct sysinfo {
+ uint32_t tCWL;
+ uint32_t tCMD;
+
++ uint32_t tREFI;
++ uint32_t tXP;
++
+ uint8_t lanes; /* 8 or 9 */
+ uint8_t chanmap;
+ uint8_t dpc[NUM_CHANNELS]; /* DIMMs per channel */
+@@ -96,7 +99,12 @@ void raminit_main(enum raminit_boot_mode bootmode);
+
+ enum raminit_status collect_spd_info(struct sysinfo *ctrl);
+ enum raminit_status initialise_mpll(struct sysinfo *ctrl);
++enum raminit_status convert_timings(struct sysinfo *ctrl);
+
+ enum raminit_status wait_for_first_rcomp(void);
+
++uint8_t get_tCWL(uint32_t mem_clock_mhz);
++uint32_t get_tREFI(uint32_t mem_clock_mhz);
++uint32_t get_tXP(uint32_t mem_clock_mhz);
++
+ #endif
+diff --git a/src/northbridge/intel/haswell/native_raminit/spd_bitmunching.c b/src/northbridge/intel/haswell/native_raminit/spd_bitmunching.c
+index dbe02c72d0..becbea0725 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 ns\n", ctrl->tCK / 256);
++
++ /* Primary timings */
++ printk(BIOS_DEBUG, "Selected tAA : %uT\n", ctrl->tAA);
++ printk(BIOS_DEBUG, "Selected tWR : %uT\n", ctrl->tWR);
++ printk(BIOS_DEBUG, "Selected tRCD : %uT\n", ctrl->tRCD);
++ printk(BIOS_DEBUG, "Selected tRRD : %uT\n", ctrl->tRRD);
++ printk(BIOS_DEBUG, "Selected tRP : %uT\n", ctrl->tRP);
++ printk(BIOS_DEBUG, "Selected tRAS : %uT\n", ctrl->tRAS);
++ printk(BIOS_DEBUG, "Selected tRC : %uT\n", ctrl->tRC);
++ printk(BIOS_DEBUG, "Selected tRFC : %uT\n", ctrl->tRFC);
++ printk(BIOS_DEBUG, "Selected tWTR : %uT\n", ctrl->tWTR);
++ printk(BIOS_DEBUG, "Selected tRTP : %uT\n", ctrl->tRTP);
++ printk(BIOS_DEBUG, "Selected tFAW : %uT\n", ctrl->tFAW);
++ printk(BIOS_DEBUG, "Selected tCWL : %uT\n", ctrl->tCWL);
++ printk(BIOS_DEBUG, "Selected tCMD : %uT\n", ctrl->tCMD);
++
++ /* Derived timings */
++ printk(BIOS_DEBUG, "Selected tREFI : %uT\n", ctrl->tREFI);
++ printk(BIOS_DEBUG, "Selected tXP : %uT\n", ctrl->tXP);
++
++ return RAMINIT_STATUS_SUCCESS;
++}
+--
+2.39.2
+