123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250 |
- 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
|