250 lines
8.7 KiB
Diff
250 lines
8.7 KiB
Diff
|
From 42b21fdce8c8bade53d9d86515f88b0665a4c1b1 Mon Sep 17 00:00:00 2001
|
||
|
From: Angel Pons <th3fanbus@gmail.com>
|
||
|
Date: Sat, 7 May 2022 16:29:55 +0200
|
||
|
Subject: [PATCH 02/20] haswell NRI: Post-process selected timings
|
||
|
|
||
|
Once the MPLL has been initialised, convert the timings from the SPD to
|
||
|
be in DCLKs, which is what the hardware expects. In addition, calculate
|
||
|
the values for tREFI and tXP.
|
||
|
|
||
|
Change-Id: Id02caf858f75b9e08016762b3aefda282b274386
|
||
|
Signed-off-by: Angel Pons <th3fanbus@gmail.com>
|
||
|
---
|
||
|
.../intel/haswell/native_raminit/Makefile.mk | 1 +
|
||
|
.../haswell/native_raminit/lookup_timings.c | 62 +++++++++++
|
||
|
.../haswell/native_raminit/raminit_main.c | 1 +
|
||
|
.../haswell/native_raminit/raminit_native.h | 8 ++
|
||
|
.../haswell/native_raminit/spd_bitmunching.c | 100 ++++++++++++++++++
|
||
|
5 files changed, 172 insertions(+)
|
||
|
create mode 100644 src/northbridge/intel/haswell/native_raminit/lookup_timings.c
|
||
|
|
||
|
diff --git a/src/northbridge/intel/haswell/native_raminit/Makefile.mk b/src/northbridge/intel/haswell/native_raminit/Makefile.mk
|
||
|
index c125d84f0b..2769e0bbb4 100644
|
||
|
--- a/src/northbridge/intel/haswell/native_raminit/Makefile.mk
|
||
|
+++ b/src/northbridge/intel/haswell/native_raminit/Makefile.mk
|
||
|
@@ -1,5 +1,6 @@
|
||
|
## SPDX-License-Identifier: GPL-2.0-or-later
|
||
|
|
||
|
+romstage-y += lookup_timings.c
|
||
|
romstage-y += init_mpll.c
|
||
|
romstage-y += io_comp_control.c
|
||
|
romstage-y += raminit_main.c
|
||
|
diff --git a/src/northbridge/intel/haswell/native_raminit/lookup_timings.c b/src/northbridge/intel/haswell/native_raminit/lookup_timings.c
|
||
|
new file mode 100644
|
||
|
index 0000000000..8b81c7c341
|
||
|
--- /dev/null
|
||
|
+++ b/src/northbridge/intel/haswell/native_raminit/lookup_timings.c
|
||
|
@@ -0,0 +1,62 @@
|
||
|
+/* SPDX-License-Identifier: GPL-2.0-or-later */
|
||
|
+
|
||
|
+#include <commonlib/bsd/clamp.h>
|
||
|
+#include <types.h>
|
||
|
+
|
||
|
+#include "raminit_native.h"
|
||
|
+
|
||
|
+struct timing_lookup {
|
||
|
+ uint32_t clock;
|
||
|
+ uint32_t value;
|
||
|
+};
|
||
|
+
|
||
|
+static uint32_t lookup_timing(
|
||
|
+ const uint32_t mem_clock_mhz,
|
||
|
+ const struct timing_lookup *const lookup,
|
||
|
+ const size_t length)
|
||
|
+{
|
||
|
+ /* Fall back to the last index */
|
||
|
+ size_t i;
|
||
|
+ for (i = 0; i < length - 1; i++) {
|
||
|
+ /* Account for imprecise frequency values */
|
||
|
+ if ((mem_clock_mhz - 5) <= lookup[i].clock)
|
||
|
+ break;
|
||
|
+ }
|
||
|
+ return lookup[i].value;
|
||
|
+}
|
||
|
+
|
||
|
+static const uint32_t fmax = UINT32_MAX;
|
||
|
+
|
||
|
+uint8_t get_tCWL(const uint32_t mem_clock_mhz)
|
||
|
+{
|
||
|
+ const struct timing_lookup lut[] = {
|
||
|
+ { 400, 5 },
|
||
|
+ { 533, 6 },
|
||
|
+ { 666, 7 },
|
||
|
+ { 800, 8 },
|
||
|
+ { 933, 9 },
|
||
|
+ { 1066, 10 },
|
||
|
+ { 1200, 11 },
|
||
|
+ { fmax, 12 },
|
||
|
+ };
|
||
|
+ return lookup_timing(mem_clock_mhz, lut, ARRAY_SIZE(lut));
|
||
|
+}
|
||
|
+
|
||
|
+/* tREFI = 7800 ns * DDR MHz */
|
||
|
+uint32_t get_tREFI(const uint32_t mem_clock_mhz)
|
||
|
+{
|
||
|
+ return (mem_clock_mhz * 7800) / 1000;
|
||
|
+}
|
||
|
+
|
||
|
+uint32_t get_tXP(const uint32_t mem_clock_mhz)
|
||
|
+{
|
||
|
+ const struct timing_lookup lut[] = {
|
||
|
+ { 400, 3 },
|
||
|
+ { 666, 4 },
|
||
|
+ { 800, 5 },
|
||
|
+ { 933, 6 },
|
||
|
+ { 1066, 7 },
|
||
|
+ { fmax, 8 },
|
||
|
+ };
|
||
|
+ return lookup_timing(mem_clock_mhz, lut, ARRAY_SIZE(lut));
|
||
|
+}
|
||
|
diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_main.c b/src/northbridge/intel/haswell/native_raminit/raminit_main.c
|
||
|
index bf745e943f..2fea658415 100644
|
||
|
--- a/src/northbridge/intel/haswell/native_raminit/raminit_main.c
|
||
|
+++ b/src/northbridge/intel/haswell/native_raminit/raminit_main.c
|
||
|
@@ -21,6 +21,7 @@ struct task_entry {
|
||
|
static const struct task_entry cold_boot[] = {
|
||
|
{ collect_spd_info, true, "PROCSPD", },
|
||
|
{ initialise_mpll, true, "INITMPLL", },
|
||
|
+ { convert_timings, true, "CONVTIM", },
|
||
|
};
|
||
|
|
||
|
/* Return a generic stepping value to make stepping checks simpler */
|
||
|
diff --git a/src/northbridge/intel/haswell/native_raminit/raminit_native.h b/src/northbridge/intel/haswell/native_raminit/raminit_native.h
|
||
|
index a54581abc7..01e5ed1bd6 100644
|
||
|
--- a/src/northbridge/intel/haswell/native_raminit/raminit_native.h
|
||
|
+++ b/src/northbridge/intel/haswell/native_raminit/raminit_native.h
|
||
|
@@ -78,6 +78,9 @@ struct sysinfo {
|
||
|
uint32_t tCWL;
|
||
|
uint32_t tCMD;
|
||
|
|
||
|
+ uint32_t tREFI;
|
||
|
+ uint32_t tXP;
|
||
|
+
|
||
|
uint8_t lanes; /* 8 or 9 */
|
||
|
uint8_t chanmap;
|
||
|
uint8_t dpc[NUM_CHANNELS]; /* DIMMs per channel */
|
||
|
@@ -96,7 +99,12 @@ void raminit_main(enum raminit_boot_mode bootmode);
|
||
|
|
||
|
enum raminit_status collect_spd_info(struct sysinfo *ctrl);
|
||
|
enum raminit_status initialise_mpll(struct sysinfo *ctrl);
|
||
|
+enum raminit_status convert_timings(struct sysinfo *ctrl);
|
||
|
|
||
|
enum raminit_status wait_for_first_rcomp(void);
|
||
|
|
||
|
+uint8_t get_tCWL(uint32_t mem_clock_mhz);
|
||
|
+uint32_t get_tREFI(uint32_t mem_clock_mhz);
|
||
|
+uint32_t get_tXP(uint32_t mem_clock_mhz);
|
||
|
+
|
||
|
#endif
|
||
|
diff --git a/src/northbridge/intel/haswell/native_raminit/spd_bitmunching.c b/src/northbridge/intel/haswell/native_raminit/spd_bitmunching.c
|
||
|
index 2dab8504c4..7d98341a7e 100644
|
||
|
--- a/src/northbridge/intel/haswell/native_raminit/spd_bitmunching.c
|
||
|
+++ b/src/northbridge/intel/haswell/native_raminit/spd_bitmunching.c
|
||
|
@@ -204,3 +204,103 @@ enum raminit_status collect_spd_info(struct sysinfo *ctrl)
|
||
|
get_spd_data(ctrl);
|
||
|
return find_common_spd_parameters(ctrl);
|
||
|
}
|
||
|
+
|
||
|
+#define MIN_CWL 5
|
||
|
+#define MAX_CWL 12
|
||
|
+
|
||
|
+/* Except for tCK, hardware expects all timing values in DCLKs, not nanoseconds */
|
||
|
+enum raminit_status convert_timings(struct sysinfo *ctrl)
|
||
|
+{
|
||
|
+ /*
|
||
|
+ * Obtain all required timing values, in DCLKs.
|
||
|
+ */
|
||
|
+
|
||
|
+ /* Convert primary timings from nanoseconds to DCLKs */
|
||
|
+ ctrl->tAA = DIV_ROUND_UP(ctrl->tAA, ctrl->tCK);
|
||
|
+ ctrl->tWR = DIV_ROUND_UP(ctrl->tWR, ctrl->tCK);
|
||
|
+ ctrl->tRCD = DIV_ROUND_UP(ctrl->tRCD, ctrl->tCK);
|
||
|
+ ctrl->tRRD = DIV_ROUND_UP(ctrl->tRRD, ctrl->tCK);
|
||
|
+ ctrl->tRP = DIV_ROUND_UP(ctrl->tRP, ctrl->tCK);
|
||
|
+ ctrl->tRAS = DIV_ROUND_UP(ctrl->tRAS, ctrl->tCK);
|
||
|
+ ctrl->tRC = DIV_ROUND_UP(ctrl->tRC, ctrl->tCK);
|
||
|
+ ctrl->tRFC = DIV_ROUND_UP(ctrl->tRFC, ctrl->tCK);
|
||
|
+ ctrl->tWTR = DIV_ROUND_UP(ctrl->tWTR, ctrl->tCK);
|
||
|
+ ctrl->tRTP = DIV_ROUND_UP(ctrl->tRTP, ctrl->tCK);
|
||
|
+ ctrl->tFAW = DIV_ROUND_UP(ctrl->tFAW, ctrl->tCK);
|
||
|
+ ctrl->tCWL = DIV_ROUND_UP(ctrl->tCWL, ctrl->tCK);
|
||
|
+ ctrl->tCMD = DIV_ROUND_UP(ctrl->tCMD, ctrl->tCK);
|
||
|
+
|
||
|
+ /* Constrain primary timings to hardware limits */
|
||
|
+ /** TODO: complain when clamping? **/
|
||
|
+ ctrl->tAA = clamp_u32(4, ctrl->tAA, 24);
|
||
|
+ ctrl->tWR = clamp_u32(5, ctrl->tWR, 16);
|
||
|
+ ctrl->tRCD = clamp_u32(4, ctrl->tRCD, 20);
|
||
|
+ ctrl->tRRD = clamp_u32(4, ctrl->tRRD, 65535);
|
||
|
+ ctrl->tRP = clamp_u32(4, ctrl->tRP, 15);
|
||
|
+ ctrl->tRAS = clamp_u32(10, ctrl->tRAS, 40);
|
||
|
+ ctrl->tRC = clamp_u32(1, ctrl->tRC, 4095);
|
||
|
+ ctrl->tRFC = clamp_u32(1, ctrl->tRFC, 511);
|
||
|
+ ctrl->tWTR = clamp_u32(4, ctrl->tWTR, 10);
|
||
|
+ ctrl->tRTP = clamp_u32(4, ctrl->tRTP, 15);
|
||
|
+ ctrl->tFAW = clamp_u32(10, ctrl->tFAW, 54);
|
||
|
+
|
||
|
+ /** TODO: Honor tREFI from XMP **/
|
||
|
+ ctrl->tREFI = get_tREFI(ctrl->mem_clock_mhz);
|
||
|
+ ctrl->tXP = get_tXP(ctrl->mem_clock_mhz);
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Check some values, and adjust them if necessary.
|
||
|
+ */
|
||
|
+
|
||
|
+ /* If tWR cannot be written into DDR3 MR0, adjust it */
|
||
|
+ switch (ctrl->tWR) {
|
||
|
+ case 9:
|
||
|
+ case 11:
|
||
|
+ case 13:
|
||
|
+ case 15:
|
||
|
+ ctrl->tWR++;
|
||
|
+ }
|
||
|
+
|
||
|
+ /* If tCWL is not supported or unspecified, look up a reasonable default */
|
||
|
+ if (ctrl->tCWL < MIN_CWL || ctrl->tCWL > MAX_CWL)
|
||
|
+ ctrl->tCWL = get_tCWL(ctrl->mem_clock_mhz);
|
||
|
+
|
||
|
+ /* This is needed to support ODT properly on 2DPC */
|
||
|
+ if (ctrl->tAA - ctrl->tCWL > 4)
|
||
|
+ ctrl->tCWL = ctrl->tAA - 4;
|
||
|
+
|
||
|
+ /* If tCMD is invalid, use a guesstimate default */
|
||
|
+ if (!ctrl->tCMD) {
|
||
|
+ ctrl->tCMD = MAX(ctrl->dpc[0], ctrl->dpc[1]);
|
||
|
+ printk(RAM_DEBUG, "tCMD was zero, picking a guesstimate value\n");
|
||
|
+ }
|
||
|
+ ctrl->tCMD = clamp_u32(1, ctrl->tCMD, 3);
|
||
|
+
|
||
|
+ /*
|
||
|
+ * Print final timings.
|
||
|
+ */
|
||
|
+
|
||
|
+ /* tCK is special */
|
||
|
+ printk(BIOS_DEBUG, "Selected tCK : %u ps\n", ctrl->tCK * 1000 / 256);
|
||
|
+
|
||
|
+ /* Primary timings */
|
||
|
+ printk(BIOS_DEBUG, "Selected tAA : %uT\n", ctrl->tAA);
|
||
|
+ printk(BIOS_DEBUG, "Selected tWR : %uT\n", ctrl->tWR);
|
||
|
+ printk(BIOS_DEBUG, "Selected tRCD : %uT\n", ctrl->tRCD);
|
||
|
+ printk(BIOS_DEBUG, "Selected tRRD : %uT\n", ctrl->tRRD);
|
||
|
+ printk(BIOS_DEBUG, "Selected tRP : %uT\n", ctrl->tRP);
|
||
|
+ printk(BIOS_DEBUG, "Selected tRAS : %uT\n", ctrl->tRAS);
|
||
|
+ printk(BIOS_DEBUG, "Selected tRC : %uT\n", ctrl->tRC);
|
||
|
+ printk(BIOS_DEBUG, "Selected tRFC : %uT\n", ctrl->tRFC);
|
||
|
+ printk(BIOS_DEBUG, "Selected tWTR : %uT\n", ctrl->tWTR);
|
||
|
+ printk(BIOS_DEBUG, "Selected tRTP : %uT\n", ctrl->tRTP);
|
||
|
+ printk(BIOS_DEBUG, "Selected tFAW : %uT\n", ctrl->tFAW);
|
||
|
+ printk(BIOS_DEBUG, "Selected tCWL : %uT\n", ctrl->tCWL);
|
||
|
+ printk(BIOS_DEBUG, "Selected tCMD : %uT\n", ctrl->tCMD);
|
||
|
+
|
||
|
+ /* Derived timings */
|
||
|
+ printk(BIOS_DEBUG, "Selected tREFI : %uT\n", ctrl->tREFI);
|
||
|
+ printk(BIOS_DEBUG, "Selected tXP : %uT\n", ctrl->tXP);
|
||
|
+
|
||
|
+ return RAMINIT_STATUS_SUCCESS;
|
||
|
+}
|
||
|
--
|
||
|
2.39.2
|
||
|
|