diff options
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.patch | 249 |
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 + |