From 7aeac7271f2929d5c84c2858e7d08092912c9b34 Mon Sep 17 00:00:00 2001 From: Sam Demeulemeester <38105886+x86fr@users.noreply.github.com> Date: Fri, 12 May 2023 15:33:28 +0200 Subject: [PATCH] Add Memory Controller Registers polling to get current DRAM Timings/Frequency (#306) Read the memory controller configuration (instead of just relying on SPD data) to get the actual live settings. Currently supported platforms: * Intel SNB to RPL (Core 2nd Gen to Core 13th Gen) - Desktop only (no Server nor Mobile) * AMD SMR to RPL (Zen to Zen4) - Desktop only (no Server, Mobile nor APU). Individual commits below for archival: * First functions skeleton for reading IMC/ECC Registers * Change directory name from 'chipsets' to 'mch' (Memory Controller Hub) * Add Intel HSW and fix new files encoding * First Intel HSW IMC implementation * Add an option to disable MCH registers polling * Remove old include from Makefiles * Better Makefile and padding fixes * Statically init 'imc' struct to generate string relocation record * Small typos & code fixes * Add IMC support for Intel Core 6/7/8/9th Gen (SKL/KBL/CFL/CML) This is a bit more complex than Haswell and below because MMIO switched to 64-bit with Skylake (lot of) betatesting needed * Add IMC read support for Intel SNB/IVB (2nd/3rd gen Core) * Fix hard-lock on Intel SNB/IVB due to wrong access type on MCHBAR pointer * Move AMD SMN Registers & offsets to a specific header file * Add IMC Read support for AMD Zen/Zen2 CPUs * Change 'IMC' to 'MCH' in Makefiles to match actual mch/ directory * Add IMC Reading support for Intel ADL&RPL CPUs (Core Gen12&13) * Add support for Intel Rocket Lake (Core 11th Gen) and AMD Vermeer * Add IMC reading for AMD Zen4 'Raphael' AM5 CPUs * Various Cleanup #1 Change terminology from Intel-based 'MCH' (Memory Controller Hub) to more universal 'IMC' (Integrated Memory Controller) Integrate imc_type var into imc struct. Remove previously created AMD SNM header file * Various Cleanup 2 * Change DDR5 display format for IMC specs DDR5 Freq can be > 10000 and timings up to 63-127-127-127, which overwflow the available space. This commit remove the raw frequency on DDR5 (which may be incorrect due to Gear mechanism) and leave a bit of space to display the Gear engaged in the future --- README.md | 2 + app/config.c | 3 ++ app/config.h | 1 + app/display.c | 11 +++-- app/display.h | 4 ++ app/main.c | 3 ++ build32/Makefile | 11 ++++- build64/Makefile | 11 ++++- system/cpuinfo.c | 104 ++++++++++++++++++++-------------------- system/cpuinfo.h | 10 ++-- system/imc/amd_zen.c | 71 ++++++++++++++++++++++++++++ system/imc/imc.h | 24 ++++++++++ system/imc/intel_adl.c | 100 +++++++++++++++++++++++++++++++++++++++ system/imc/intel_hsw.c | 92 ++++++++++++++++++++++++++++++++++++ system/imc/intel_icl.c | 99 ++++++++++++++++++++++++++++++++++++++ system/imc/intel_skl.c | 105 +++++++++++++++++++++++++++++++++++++++++ system/imc/intel_snb.c | 93 ++++++++++++++++++++++++++++++++++++ system/memctrl.c | 65 +++++++++++++++++++++++++ system/memctrl.h | 57 ++++++++++++++++++++++ system/msr.h | 1 + system/pci.h | 8 +--- system/smbus.c | 3 +- system/temperature.h | 4 ++ 23 files changed, 809 insertions(+), 73 deletions(-) create mode 100644 system/imc/amd_zen.c create mode 100644 system/imc/imc.h create mode 100644 system/imc/intel_adl.c create mode 100644 system/imc/intel_hsw.c create mode 100644 system/imc/intel_icl.c create mode 100644 system/imc/intel_skl.c create mode 100644 system/imc/intel_snb.c create mode 100644 system/memctrl.c create mode 100644 system/memctrl.h diff --git a/README.md b/README.md index 896d6a2..454bb3d 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,8 @@ recognised: * disables the big PASS/FAIL pop-up status display * nosm * disables SMBUS/SPD parsing, DMI decoding and memory benchmark + * nomch + * disables memory controller configuration polling * nopause * skips the pause for configuration at startup * keyboard=*type* diff --git a/app/config.c b/app/config.c index 61a216c..6309c1e 100644 --- a/app/config.c +++ b/app/config.c @@ -97,6 +97,7 @@ bool enable_trace = false; bool enable_sm = true; bool enable_bench = true; +bool enable_mch_read = true; bool pause_at_start = true; @@ -209,6 +210,8 @@ static void parse_option(const char *option, const char *params) enable_big_status = false; } else if (strncmp(option, "noehci", 7) == 0) { usb_init_options |= USB_IGNORE_EHCI; + } else if (strncmp(option, "nomch", 6) == 0) { + enable_mch_read = false; } else if (strncmp(option, "nopause", 8) == 0) { pause_at_start = false; } else if (strncmp(option, "nosm", 5) == 0) { diff --git a/app/config.h b/app/config.h index 3d6b82a..d9eb5ba 100644 --- a/app/config.h +++ b/app/config.h @@ -58,6 +58,7 @@ extern bool enable_trace; extern bool enable_sm; extern bool enable_tty; extern bool enable_bench; +extern bool enable_mch_read; extern bool pause_at_start; diff --git a/app/display.c b/app/display.c index 27f0e1e..72bf6d2 100644 --- a/app/display.c +++ b/app/display.c @@ -10,6 +10,7 @@ #include "hwctrl.h" #include "io.h" #include "keyboard.h" +#include "memctrl.h" #include "serial.h" #include "pmem.h" #include "smbios.h" @@ -262,10 +263,14 @@ void post_display_init(void) print_smbios_startup_info(); print_smbus_startup_info(); - if (false) { - // Try to get RAM information from IMC (TODO) + if (imc.freq) { + // Try to get RAM information from IMC display_spec_mode("IMC: "); - display_spec_ddr(ram.freq, ram.type, ram.tCL, ram.tCL_dec, ram.tRCD, ram.tRP, ram.tRAS); + if (imc.type[3] == '5') { + display_spec_ddr5(imc.freq, imc.type, imc.tCL, imc.tCL_dec, imc.tRCD, imc.tRP, imc.tRAS); + } else { + display_spec_ddr(imc.freq, imc.type, imc.tCL, imc.tCL_dec, imc.tRCD, imc.tRP, imc.tRAS); + } display_mode = DISPLAY_MODE_IMC; } else if (ram.freq > 0 && ram.tCL > 0) { // If not available, grab max memory specs from SPD diff --git a/app/display.h b/app/display.h index 7d065f7..99a6c25 100644 --- a/app/display.h +++ b/app/display.h @@ -102,6 +102,10 @@ typedef enum { #define display_spec_mode(mode) \ prints(8,0, mode); +#define display_spec_ddr5(freq, type, cl, cl_dec, rcd, rp, ras) \ + printf(8,5, "%s-%u / CAS %u%s-%u-%u-%u", \ + type, freq, cl, cl_dec?".5":"", rcd, rp, ras); + #define display_spec_ddr(freq, type, cl, cl_dec, rcd, rp, ras) \ printf(8,5, "%uMHz (%s-%u) CAS %u%s-%u-%u-%u", \ freq / 2, type, freq, cl, cl_dec?".5":"", rcd, rp, ras); diff --git a/app/main.c b/app/main.c index 565ff78..3daedc2 100644 --- a/app/main.c +++ b/app/main.c @@ -28,6 +28,7 @@ #include "io.h" #include "keyboard.h" #include "pmem.h" +#include "memctrl.h" #include "memsize.h" #include "pci.h" #include "screen.h" @@ -235,6 +236,8 @@ static void global_init(void) config_init(); + memctrl_init(); + tty_init(); smp_init(smp_enabled); diff --git a/build32/Makefile b/build32/Makefile index 552c7b5..08f269d 100644 --- a/build32/Makefile +++ b/build32/Makefile @@ -25,6 +25,7 @@ SYS_OBJS = system/acpi.o \ system/hwquirks.o \ system/keyboard.o \ system/ohci.o \ + system/memctrl.o \ system/pci.o \ system/pmem.o \ system/reloc.o \ @@ -40,6 +41,9 @@ SYS_OBJS = system/acpi.o \ system/vmem.o \ system/xhci.o +IMC_SRCS = $(wildcard ../system/imc/*.c) +IMC_OBJS = $(subst ../,,$(IMC_SRCS:.c=.o)) + LIB_OBJS = lib/barrier.o \ lib/div64.o \ lib/print.o \ @@ -65,12 +69,13 @@ APP_OBJS = app/badram.o \ app/interrupt.o \ app/main.o -OBJS = boot/startup.o boot/efisetup.o $(SYS_OBJS) $(LIB_OBJS) $(TST_OBJS) $(APP_OBJS) +OBJS = boot/startup.o boot/efisetup.o $(SYS_OBJS) $(IMC_OBJS) $(LIB_OBJS) $(TST_OBJS) $(APP_OBJS) all: memtest.bin memtest.efi -include boot/efisetup.d -include $(subst .o,.d,$(SYS_OBJS)) +-include $(subst .o,.d,$(IMC_OBJS)) -include $(subst .o,.d,$(LIB_OBJS)) -include $(subst .o,.d,$(TST_OBJS)) -include $(subst .o,.d,$(APP_OBJS)) @@ -97,6 +102,10 @@ system/%.o: ../system/%.c @mkdir -p system $(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) +system/imc/%.o: ../system/imc/%.c + @mkdir -p system/imc + $(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) + lib/%.o: ../lib/%.c @mkdir -p lib $(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) diff --git a/build64/Makefile b/build64/Makefile index 799133e..4457013 100644 --- a/build64/Makefile +++ b/build64/Makefile @@ -25,6 +25,7 @@ SYS_OBJS = system/acpi.o \ system/hwquirks.o \ system/keyboard.o \ system/ohci.o \ + system/memctrl.o \ system/pci.o \ system/pmem.o \ system/reloc.o \ @@ -40,6 +41,9 @@ SYS_OBJS = system/acpi.o \ system/vmem.o \ system/xhci.o +IMC_SRCS = $(wildcard ../system/imc/*.c) +IMC_OBJS = $(subst ../,,$(IMC_SRCS:.c=.o)) + LIB_OBJS = lib/barrier.o \ lib/print.o \ lib/read.o \ @@ -64,12 +68,13 @@ APP_OBJS = app/badram.o \ app/interrupt.o \ app/main.o -OBJS = boot/startup.o boot/efisetup.o $(SYS_OBJS) $(LIB_OBJS) $(TST_OBJS) $(APP_OBJS) +OBJS = boot/startup.o boot/efisetup.o $(SYS_OBJS) $(IMC_OBJS) $(LIB_OBJS) $(TST_OBJS) $(APP_OBJS) all: memtest.bin memtest.efi -include boot/efisetup.d -include $(subst .o,.d,$(SYS_OBJS)) +-include $(subst .o,.d,$(IMC_OBJS)) -include $(subst .o,.d,$(LIB_OBJS)) -include $(subst .o,.d,$(TST_OBJS)) -include $(subst .o,.d,$(APP_OBJS)) @@ -96,6 +101,10 @@ system/%.o: ../system/%.c @mkdir -p system $(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) +system/imc/%.o: ../system/imc/%.c + @mkdir -p system/imc + $(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) + lib/%.o: ../lib/%.c @mkdir -p lib $(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) diff --git a/system/cpuinfo.c b/system/cpuinfo.c index cba579c..c01546c 100644 --- a/system/cpuinfo.c +++ b/system/cpuinfo.c @@ -1,11 +1,10 @@ // SPDX-License-Identifier: GPL-2.0 // Copyright (C) 2020-2022 Martin Whitaker. +// Copyright (C) 2004-2023 Sam Demeulemeester // // Derived from an extract of memtest86+ init.c: // // MemTest86+ V5 Specific code (GPL V2.0) -// By Samuel DEMEULEMEESTER, sdemeule@memtest.org -// http://www.canardpc.com - http://www.memtest.org // ------------------------------------------------ // init.c - MemTest-86 Version 3.6 // @@ -24,6 +23,7 @@ #include "config.h" #include "pmem.h" #include "vmem.h" +#include "memctrl.h" #include "memsize.h" #include "hwquirks.h" @@ -41,8 +41,6 @@ const char *cpu_model = NULL; -uint16_t imc_type = 0; - int l1_cache = 0; int l2_cache = 0; int l3_cache = 0; @@ -304,37 +302,37 @@ static void determine_imc(void) switch (cpuid_info.version.extendedFamily) { case 0x0: - imc_type = IMC_K8; // Old K8 + imc.family = IMC_K8; // Old K8 break; case 0x1: case 0x2: - imc_type = IMC_K10; // K10 (Family 10h & 11h) + imc.family = IMC_K10; // K10 (Family 10h & 11h) break; case 0x3: - imc_type = IMC_K12; // A-Series APU (Family 12h) + imc.family = IMC_K12; // A-Series APU (Family 12h) break; case 0x5: - imc_type = IMC_K14; // C- / E- / Z- Series APU (Family 14h) + imc.family = IMC_K14; // C- / E- / Z- Series APU (Family 14h) break; case 0x6: - imc_type = IMC_K15; // FX Series (Family 15h) + imc.family = IMC_K15; // FX Series (Family 15h) break; case 0x7: - imc_type = IMC_K16; // Kabini & related (Family 16h) + imc.family = IMC_K16; // Kabini & related (Family 16h) break; case 0x8: - imc_type = IMC_K17; // Zen & Zen2 (Family 17h) + imc.family = IMC_K17; // Zen & Zen2 (Family 17h) break; case 0x9: - imc_type = IMC_K18; // Hygon (Family 18h) + imc.family = IMC_K18; // Hygon (Family 18h) break; case 0xA: if (cpuid_info.version.extendedModel == 5) { - imc_type = IMC_K19_CZN; // AMD Cezanne APU (Model 0x50-5F - Family 19h) + imc.family = IMC_K19_CZN; // AMD Cezanne APU (Model 0x50-5F - Family 19h) } else if (cpuid_info.version.extendedModel >= 6) { - imc_type = IMC_K19_RPL; // Zen4 (Family 19h) + imc.family = IMC_K19_RPL; // Zen4 (Family 19h - Raphael AM5) } else { - imc_type = IMC_K19; // Zen3 (Family 19h) + imc.family = IMC_K19_VRM; // Zen3 (Family 19h - Vermeer AM4) } default: break; @@ -349,16 +347,16 @@ static void determine_imc(void) case 0x5: switch (cpuid_info.version.extendedModel) { case 2: - imc_type = IMC_NHM; // Core i3/i5 1st Gen 45 nm (Nehalem/Bloomfield) + imc.family = IMC_NHM; // Core i3/i5 1st Gen 45 nm (Nehalem/Bloomfield) break; case 3: - no_temperature = true; // Atom Clover Trail + no_temperature = true; // Atom Clover Trail break; case 4: - imc_type = IMC_HSW_ULT; // Core 4th Gen (Haswell-ULT) + imc.family = IMC_HSW_ULT; // Core 4th Gen (Haswell-ULT) break; case 5: - imc_type = IMC_SKL_SP; // Skylake/Cascade Lake/Cooper Lake (Server) + imc.family = IMC_SKL_SP; // Skylake/Cascade Lake/Cooper Lake (Server) break; default: break; @@ -368,17 +366,17 @@ static void determine_imc(void) case 0x6: switch (cpuid_info.version.extendedModel) { case 3: - imc_type = IMC_CDT; // Atom Cedar Trail + imc.family = IMC_CDT; // Atom Cedar Trail no_temperature = true; break; case 4: - imc_type = IMC_HSW; // Core 4th Gen (Haswell w/ GT3e) + imc.family = IMC_HSW; // Core 4th Gen (Haswell w/ GT3e) break; case 5: - imc_type = IMC_BDW_DE; // Broadwell-DE (Server) + imc.family = IMC_BDW_DE; // Broadwell-DE (Server) break; case 6: - imc_type = IMC_CNL; // Cannon Lake + imc.family = IMC_CNL; // Cannon Lake break; default: break; @@ -388,19 +386,19 @@ static void determine_imc(void) case 0x7: switch (cpuid_info.version.extendedModel) { case 0x3: - imc_type = IMC_BYT; // Atom Bay Trail + imc.family = IMC_BYT; // Atom Bay Trail break; case 0x4: - imc_type = IMC_BDW; // Core 5th Gen (Broadwell) + imc.family = IMC_BDW; // Core 5th Gen (Broadwell) break; case 0x9: - imc_type = IMC_ADL; // Core 12th Gen (Alder Lake-P) + imc.family = IMC_ADL; // Core 12th Gen (Alder Lake-P) break; case 0xA: - imc_type = IMC_RKL; // Core 11th Gen (Rocket Lake) + imc.family = IMC_RKL; // Core 11th Gen (Rocket Lake) break; case 0xB: - imc_type = IMC_RPL; // Core 13th Gen (Raptor Lake) + imc.family = IMC_RPL; // Core 13th Gen (Raptor Lake) break; default: break; @@ -410,19 +408,19 @@ static void determine_imc(void) case 0xA: switch (cpuid_info.version.extendedModel) { case 0x1: - imc_type = IMC_NHM_E; // Core i7 1st Gen 45 nm (NHME) + imc.family = IMC_NHM_E; // Core i7 1st Gen 45 nm (NHME) break; case 0x2: - imc_type = IMC_SNB; // Core 2nd Gen (Sandy Bridge) + imc.family = IMC_SNB; // Core 2nd Gen (Sandy Bridge) break; case 0x3: - imc_type = IMC_IVB; // Core 3rd Gen (Ivy Bridge) + imc.family = IMC_IVB; // Core 3rd Gen (Ivy Bridge) break; case 0x6: - imc_type = IMC_ICL_SP; // Ice Lake-SP/DE (Server) + imc.family = IMC_ICL_SP; // Ice Lake-SP/DE (Server) break; case 0x9: - imc_type = IMC_ADL; // Core 12th Gen (Alder Lake-S) + imc.family = IMC_ADL; // Core 12th Gen (Alder Lake-S) break; default: break; @@ -433,18 +431,18 @@ static void determine_imc(void) switch (cpuid_info.version.extendedModel) { case 0x1: if (cpuid_info.version.stepping > 9) { - imc_type = 0x0008; // Atom PineView + imc.family = 0x0008; // Atom PineView } no_temperature = true; break; case 0x2: - imc_type = IMC_WMR; // Core i7 1st Gen 32 nm (Westmere) + imc.family = IMC_WMR; // Core i7 1st Gen 32 nm (Westmere) break; case 0x3: - imc_type = IMC_HSW; // Core 4th Gen (Haswell) + imc.family = IMC_HSW; // Core 4th Gen (Haswell) break; case 0x8: - imc_type = IMC_TGL; // Core 11th Gen (Tiger Lake-U) + imc.family = IMC_TGL; // Core 11th Gen (Tiger Lake-U) break; default: break; @@ -454,13 +452,13 @@ static void determine_imc(void) case 0xD: switch (cpuid_info.version.extendedModel) { case 0x2: - imc_type = IMC_SNB_E; // Core 2nd Gen (Sandy Bridge-E) + imc.family = IMC_SNB_E; // Core 2nd Gen (Sandy Bridge-E) break; case 0x7: - imc_type = IMC_ICL; // Core 10th Gen (IceLake-Y) + imc.family = IMC_ICL; // Core 10th Gen (IceLake-Y) break; case 0x8: - imc_type = IMC_TGL; // Core 11th Gen (Tiger Lake-Y) + imc.family = IMC_TGL; // Core 11th Gen (Tiger Lake-Y) break; default: break; @@ -470,31 +468,31 @@ static void determine_imc(void) case 0xE: switch (cpuid_info.version.extendedModel) { case 0x1: - imc_type = IMC_NHM; // Core i7 1st Gen 45 nm (Nehalem/Bloomfield) + imc.family = IMC_NHM; // Core i7 1st Gen 45 nm (Nehalem/Bloomfield) break; case 0x2: - imc_type = IMC_SNB_E; // Core 2nd Gen (Sandy Bridge-E) + imc.family = IMC_SNB_E; // Core 2nd Gen (Sandy Bridge-E) break; case 0x3: - imc_type = IMC_IVB_E; // Core 3rd Gen (Ivy Bridge-E) + imc.family = IMC_IVB_E; // Core 3rd Gen (Ivy Bridge-E) break; case 0x4: - imc_type = IMC_SKL_UY; // Core 6th Gen (Sky Lake-U/Y) + imc.family = IMC_SKL_UY; // Core 6th Gen (Sky Lake-U/Y) break; case 0x5: - imc_type = IMC_SKL; // Core 6th Gen (Sky Lake-S/H/E) + imc.family = IMC_SKL; // Core 6th Gen (Sky Lake-S/H/E) break; case 0x7: - imc_type = IMC_ICL; // Core 10th Gen (IceLake-U) + imc.family = IMC_ICL; // Core 10th Gen (IceLake-U) break; case 0x8: - imc_type = IMC_KBL_UY; // Core 7/8/9th Gen (Kaby/Coffee/Comet/Amber Lake-U/Y) + imc.family = IMC_KBL_UY; // Core 7/8/9th Gen (Kaby/Coffee/Comet/Amber Lake-U/Y) break; case 0x9: - imc_type = IMC_KBL; // Core 7/8/9th Gen (Kaby/Coffee/Comet Lake) + imc.family = IMC_KBL; // Core 7/8/9th Gen (Kaby/Coffee/Comet Lake) break; case 0xB: - imc_type = IMC_ADL_N; // Core 12th Gen (Alder Lake-N - Gracemont E-Cores only) + imc.family = IMC_ADL_N; // Core 12th Gen (Alder Lake-N - Gracemont E-Cores only) break; default: break; @@ -504,13 +502,13 @@ static void determine_imc(void) case 0xF: switch (cpuid_info.version.extendedModel) { case 0x3: - imc_type = IMC_HSW_E; // Core 3rd Gen (Haswell-E) + imc.family = IMC_HSW_E; // Core 3rd Gen (Haswell-E) break; case 0x4: - imc_type = IMC_BDW_E; // Broadwell-E (Server) + imc.family = IMC_BDW_E; // Broadwell-E (Server) break; case 0x8: - imc_type = IMC_SPR; // Sapphire Rapids (Server) + imc.family = IMC_SPR; // Sapphire Rapids (Server) break; default: break; @@ -813,7 +811,7 @@ static void determine_cpu_model(void) // All VIA/Centaur family values >= 6 have brand string break; } - } else { /* CyrixInstead */ + } else { // CyrixInstead switch (cpuid_info.version.family) { case 4: switch (cpuid_info.version.model) { diff --git a/system/cpuinfo.h b/system/cpuinfo.h index 5975b0e..5653492 100644 --- a/system/cpuinfo.h +++ b/system/cpuinfo.h @@ -60,20 +60,16 @@ #define IMC_K16 0x8050 // Kabini & related (Family 16h) #define IMC_K17 0x8060 // Zen & Zen2 (Family 17h) #define IMC_K18 0x8070 // Hygon (Family 18h) -#define IMC_K19 0x8080 // Zen3 (Family 19h) +#define IMC_K19_VRM 0x8080 // Zen3 (Family 19h - Vermeer) #define IMC_K19_CZN 0x8081 // Cezanne APU -#define IMC_K19_RPL 0x8091 // Zen4 (Family 19h) + +#define IMC_K19_RPL 0x8100 // Zen4 (Family 19h - Raphael (AM5)) /** * A string identifying the CPU make and model. */ extern const char *cpu_model; -/** - * A number identifying the integrated memory controller type. - */ -extern uint16_t imc_type; - /** * The size of the L1 cache in KB. */ diff --git a/system/imc/amd_zen.c b/system/imc/amd_zen.c new file mode 100644 index 0000000..a4136ec --- /dev/null +++ b/system/imc/amd_zen.c @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2004-2023 Sam Demeulemeester +// +// ------------------------ +// +// Platform-specific code for AMD Zen CPUs +// + +#include "cpuinfo.h" +#include "memctrl.h" +#include "msr.h" +#include "pci.h" + +#include "imc.h" + +#define AMD_SMN_UMC_BAR 0x050000 +#define AMD_SMN_UMC_CHB_OFFSET 0x100000 +#define AMD_SMN_UMC_DRAM_CONFIG AMD_SMN_UMC_BAR + 0x200 +#define AMD_SMN_UMC_DRAM_TIMINGS1 AMD_SMN_UMC_BAR + 0x204 +#define AMD_SMN_UMC_DRAM_TIMINGS2 AMD_SMN_UMC_BAR + 0x208 + +void get_imc_config_amd_zen(void) +{ + uint32_t smn_reg, offset; + uint32_t reg_cha, reg_chb; + + imc.tCL_dec = 0; + + // Get Memory Mapped Register Base Address (Enable MMIO if needed) + reg_cha = amd_smn_read(AMD_SMN_UMC_DRAM_CONFIG) & 0x7F; + reg_chb = amd_smn_read(AMD_SMN_UMC_DRAM_CONFIG + AMD_SMN_UMC_CHB_OFFSET) & 0x7F; + + offset = reg_cha ? 0x0 : AMD_SMN_UMC_CHB_OFFSET; + + // Populate IMC width + imc.width = (reg_cha && reg_chb) ? 128 : 64; + + // Get DRAM Frequency + smn_reg = amd_smn_read(AMD_SMN_UMC_DRAM_CONFIG + offset); + if (imc.family >= IMC_K19_RPL) { + imc.type = "DDR5"; + imc.freq = smn_reg & 0xFFFF; + if ((smn_reg >> 18) & 1) imc.freq *= 2; // GearDown + } else { + imc.type = "DDR4"; + smn_reg = amd_smn_read(AMD_SMN_UMC_DRAM_CONFIG + offset) & 0x7F; + imc.freq = (float)smn_reg * 66.67f; + } + + if (imc.freq < 200 || imc.freq > 12000) { + imc.freq = 0; + return; + } + + // Get Timings + smn_reg = amd_smn_read(AMD_SMN_UMC_DRAM_TIMINGS1 + offset); + + // CAS Latency (tCAS) + imc.tCL = smn_reg & 0x3F; + + // RAS Active to precharge (tRAS) + imc.tRAS = (smn_reg >> 8) & 0x7F; + + // RAS-To-CAS (tRC) + imc.tRCD = (smn_reg >> 16) & 0x3F; + + smn_reg = amd_smn_read(AMD_SMN_UMC_DRAM_TIMINGS2 + offset); + + // RAS Precharge (tRP) + imc.tRP = (smn_reg >> 16) & 0x3F; +} diff --git a/system/imc/imc.h b/system/imc/imc.h new file mode 100644 index 0000000..3928f93 --- /dev/null +++ b/system/imc/imc.h @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2004-2023 Sam Demeulemeester +#ifndef _IMC_H_ +#define _IMC_H_ + +/* Memory configuration Detection for AMD Zen CPUs */ +void get_imc_config_amd_zen(void); + +/* Memory configuration Detection for Intel Sandy Bridge */ +void get_imc_config_intel_snb(void); + +/* Memory configuration Detection for Intel Haswell */ +void get_imc_config_intel_hsw(void); + +/* Memory configuration Detection for Intel Skylake */ +void get_imc_config_intel_skl(void); + +/* Memory configuration Detection for Intel Ice Lake */ +void get_imc_config_intel_icl(void); + +/* Memory configuration Detection for Intel Alder Lake */ +void get_imc_config_intel_adl(void); + +#endif /* _IMC_H_ */ diff --git a/system/imc/intel_adl.c b/system/imc/intel_adl.c new file mode 100644 index 0000000..89895a2 --- /dev/null +++ b/system/imc/intel_adl.c @@ -0,0 +1,100 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2004-2023 Sam Demeulemeester +// +// ------------------------ +// +// Platform-specific code for Intel Alder Lake CPUs (ADL-S) +// + +#include "cpuinfo.h" +#include "memctrl.h" +#include "msr.h" +#include "pci.h" +#include "vmem.h" + +#include "imc.h" + +#define ADL_MMR_BASE_REG_LOW 0x48 +#define ADL_MMR_BASE_REG_HIGH 0x4C + +#define ADL_MMR_WINDOW_RANGE (1UL << 17) +#define ADL_MMR_BASE_MASK 0x3FFFFFE0000 + +#define ADL_MMR_MC1_OFFSET 0x10000 +#define ADL_MMR_CH1_OFFSET 0x800 + +#define ADL_MMR_IC_DECODE 0xD800 +#define ADL_MMR_CH0_DIMM_REG 0xD80C +#define ADL_MMR_MC0_REG 0xE000 +#define ADL_MMR_ODT_TCL_REG 0xE070 +#define ADL_MMR_MC_INIT_REG 0xE454 + +#define ADL_MMR_SA_PERF_REG 0x5918 +#define ADL_MMR_MC_BIOS_REG 0x5E04 +#define ADL_MMR_BLCK_REG 0x5F60 + +void get_imc_config_intel_adl(void) +{ + uint64_t mmio_reg; + uint32_t cha, chb, offset; + float bclk; + uintptr_t *ptr; + uint32_t *ptr32; + + // Get Memory Mapped Register Base Address (Enable MMIO if needed) + mmio_reg = pci_config_read32(0, 0, 0, ADL_MMR_BASE_REG_LOW); + if (!(mmio_reg & 0x1)) { + pci_config_write32( 0, 0, 0, ADL_MMR_BASE_REG_LOW, mmio_reg | 1); + mmio_reg = pci_config_read32(0, 0, 0, ADL_MMR_BASE_REG_LOW); + if (!(mmio_reg & 0x1)) return; + } + + mmio_reg |= (uint64_t)pci_config_read32(0, 0, 0, ADL_MMR_BASE_REG_HIGH) << 32; + mmio_reg &= ADL_MMR_BASE_MASK; + +#ifndef __x86_64__ + if (mmio_reg >= (1ULL << 32)) return; // MMIO is outside reachable range (> 32bit) +#endif + + uintptr_t mchbar_addr = map_region(mmio_reg, ADL_MMR_WINDOW_RANGE, false); + + // Get channel configuration & IMC width + cha = *(uintptr_t*)(mchbar_addr + ADL_MMR_CH0_DIMM_REG); + cha = ~cha ? (((cha >> 16) & 0x7F) + (cha & 0x7F)) : 0; + + chb = *(uintptr_t*)(mchbar_addr + ADL_MMR_CH0_DIMM_REG + ADL_MMR_MC1_OFFSET); + chb = ~chb ? (((chb >> 16) & 0x7F) + (chb & 0x7F)) : 0; + + offset = cha ? 0x0 : ADL_MMR_MC1_OFFSET; + imc.width = (cha && chb) ? 64 : 128; + + // Get Memory Type (ADL supports DDR4 & DDR5) + cha = *(uintptr_t*)(mchbar_addr + offset + ADL_MMR_IC_DECODE) & 0x7; + imc.type = (cha == 1 || cha == 2) ? "DDR5" : "DDR4"; + + // Get SoC Base Clock + ptr = (uintptr_t*)(mchbar_addr + ADL_MMR_BLCK_REG); + bclk = (*ptr & 0xFFFFFFFF) / 1000.0f; + + // Get Memory Clock (QClk), apply Gear & clock ratio + ptr = (uintptr_t*)(mchbar_addr + ADL_MMR_SA_PERF_REG); + imc.freq = ((*ptr >> 2) & 0xFF) * bclk; + + ptr = (uintptr_t*)(mchbar_addr + ADL_MMR_MC_BIOS_REG); + imc.freq <<= (*ptr >> 12) & 0x3; + + if ((*ptr & 0xF00) == 0) { + imc.freq *= 133.34f / 100.0f; + } + + // Get DRAM Timings + ptr = (uintptr_t*)(mchbar_addr + offset + ADL_MMR_ODT_TCL_REG); + imc.tCL = (*ptr >> 16) & 0x7F; + + ptr = (uintptr_t*)(mchbar_addr + offset + ADL_MMR_MC0_REG); + imc.tRP = *ptr & 0xFF; + + ptr32 = (uint32_t*)((uintptr_t)mchbar_addr + offset + ADL_MMR_MC0_REG + 4); + imc.tRAS = (*ptr32 >> 10) & 0x1FF; + imc.tRCD = (*ptr32 >> 19) & 0xFF; +} diff --git a/system/imc/intel_hsw.c b/system/imc/intel_hsw.c new file mode 100644 index 0000000..62a518e --- /dev/null +++ b/system/imc/intel_hsw.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2004-2023 Sam Demeulemeester +// +// ------------------------ +// +// Platform-specific code for Intel Haswell CPUs (HSW) +// + +#include "cpuinfo.h" +#include "memctrl.h" +#include "msr.h" +#include "pci.h" + +#include "imc.h" + +#define HSW_MMR_BASE_REG 0x48 +#define HSW_REG_MAIN_CHAN0 0x5004 +#define HSW_REG_MAIN_CHAN1 0x5008 +#define HSW_REG_MCH_CFG 0x5E04 +#define HSW_REG_TIMING_CAS 0x4014 +#define HSW_REG_TIMING_RCD 0x4000 + +void get_imc_config_intel_hsw(void) +{ + uint32_t mmio_reg, mch_cfg, offset; + uint32_t reg0, reg1; + float cpu_ratio, dram_ratio; + uintptr_t *ptr; + + imc.type = "DDR3"; + imc.tCL_dec = 0; + + // Get Memory Mapped Register Base Address (Enable MMIO if needed) + mmio_reg = pci_config_read32(0, 0, 0, HSW_MMR_BASE_REG); + if (!(mmio_reg & 0x1)) { + pci_config_write32( 0, 0, 0, HSW_MMR_BASE_REG, mmio_reg | 1); + mmio_reg = pci_config_read32(0, 0, 0, HSW_MMR_BASE_REG); + if (!(mmio_reg & 0x1)) return; + } + mmio_reg &= 0xFFFFC000; + + // Get DRAM Ratio + ptr = (uintptr_t*)((uintptr_t)mmio_reg + HSW_REG_MCH_CFG); + mch_cfg = *ptr & 0xFFFF; + + if ((mch_cfg >> 8) & 1) { + dram_ratio = (float)(*ptr & 0x1F) * (100.0f / 100.0f); + } else { + dram_ratio = (float)(*ptr & 0x1F) * (133.34f / 100.0f); + } + + // Get CPU Ratio + rdmsr(MSR_IA32_PLATFORM_INFO, reg0, reg1); + cpu_ratio = (float)((reg0 >> 8) & 0xFF); + + if (!cpu_ratio) return; + + // Compute DRAM Frequency + imc.freq = ((clks_per_msec / 1000) / cpu_ratio) * dram_ratio * 2; + + if (imc.freq < 350 || imc.freq > 5000) { + imc.freq = 0; + return; + } + + // Get Main Memory Controller Register for both channels + ptr = (uintptr_t*)((uintptr_t)mmio_reg + HSW_REG_MAIN_CHAN0); + reg0 = *ptr & 0xFFFF; + + ptr = (uintptr_t*)((uintptr_t)mmio_reg + HSW_REG_MAIN_CHAN1); + reg1 = *ptr & 0xFFFF; + + // Populate IMC width + imc.width = (reg0 && reg1) ? 128 : 64; + + // Define offset (ie: which channel is really used) + offset = reg0 ? 0x0000 : 0x4000; + + // CAS Latency (tCAS) + ptr = (uintptr_t*)((uintptr_t)mmio_reg + offset + HSW_REG_TIMING_CAS); + imc.tCL = *ptr & 0x1F; + + // RAS-To-CAS (tRCD) + ptr = (uintptr_t*)((uintptr_t)mmio_reg + offset + HSW_REG_TIMING_RCD); + imc.tRCD = *ptr & 0x1F; + + // RAS Precharge (tRP) + imc.tRP = (*ptr >> 5) & 0x1F; + + // RAS Active to precharge (tRAS) + imc.tRAS = (*ptr >> 10) & 0x3F; +} diff --git a/system/imc/intel_icl.c b/system/imc/intel_icl.c new file mode 100644 index 0000000..bedc055 --- /dev/null +++ b/system/imc/intel_icl.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2004-2023 Sam Demeulemeester +// +// ------------------------ +// +// Platform-specific code for Intel IceLake CPUs (ICL) +// + +#include "cpuinfo.h" +#include "memctrl.h" +#include "msr.h" +#include "pci.h" +#include "vmem.h" + +#include "imc.h" + +#define ICL_MMR_BASE_REG_LOW 0x48 +#define ICL_MMR_BASE_REG_HIGH 0x4C +#define ICL_MMR_TIMINGS 0x4000 +#define ICL_MMR_TIMING_CAS 0x4070 +#define ICL_MMR_MAD_CHAN0 0x500C +#define ICL_MMR_MAD_CHAN1 0x5010 +#define ICL_MMR_DRAM_CLOCK 0x5E00 + +#define ICL_MMR_MC_BIOS_REG 0x5E04 +#define ICL_MMR_BLCK_REG 0x5F60 + +#define ICL_MMR_WINDOW_RANGE (1UL << 15) + +#define ICL_MMR_BASE_MASK 0x7FFFFF8000 +#define ICL_MMR_MAD_IN_USE_MASK 0x003F003F + +void get_imc_config_intel_icl(void) +{ + uint64_t mmio_reg; + uint32_t reg0, reg1, offset; + float bclk; + uintptr_t *ptr; + + // Get Memory Mapped Register Base Address (Enable MMIO if needed) + mmio_reg = pci_config_read32(0, 0, 0, ICL_MMR_BASE_REG_LOW); + if (!(mmio_reg & 0x1)) { + pci_config_write32( 0, 0, 0, ICL_MMR_BASE_REG_LOW, mmio_reg | 1); + mmio_reg = pci_config_read32(0, 0, 0, ICL_MMR_BASE_REG_LOW); + if (!(mmio_reg & 0x1)) return; + } + + mmio_reg |= (uint64_t)pci_config_read32(0, 0, 0, ICL_MMR_BASE_REG_HIGH) << 32; + mmio_reg &= ICL_MMR_BASE_MASK; + +#ifndef __x86_64__ + if (mmio_reg >= (1ULL << 32)) return; // MMIO is outside reachable range +#endif + + uintptr_t mchbar_addr = map_region(mmio_reg, ICL_MMR_WINDOW_RANGE, false); + + imc.type = "DDR4"; + + // Get SoC Base Clock + ptr = (uintptr_t*)(mchbar_addr + ICL_MMR_BLCK_REG); + bclk = (*ptr & 0xFFFFFFFF) / 1000.0f; + + // Get Memory Clock (QClk), apply Gear & clock ratio + ptr = (uintptr_t*)(mchbar_addr + ICL_MMR_MC_BIOS_REG); + imc.freq = (*ptr & 0xFF) * bclk; + + if (*ptr & 0x10000) { + imc.freq *= 2; + } + + if ((*ptr & 0xF00) == 0) { + imc.freq *= 133.34f / 100.0f; + } + + // Get Main Memory Controller Register for both channels + ptr = (uintptr_t*)(mchbar_addr + ICL_MMR_MAD_CHAN0); + reg0 = *ptr & ICL_MMR_MAD_IN_USE_MASK; + + ptr = (uintptr_t*)(mchbar_addr + ICL_MMR_MAD_CHAN1); + reg1 = *ptr & ICL_MMR_MAD_IN_USE_MASK; + + // Populate IMC width + imc.width = (reg0 && reg1) ? 128 : 64; + + // Define offset (ie: which channel is really used) + offset = reg0 ? 0x0000 : 0x0400; + + // CAS Latency (tCAS) + ptr = (uintptr_t*)(mchbar_addr + offset + ICL_MMR_TIMING_CAS); + imc.tCL = (*ptr >> 16) & 0x1F; + imc.tCL_dec = 0; + + // RAS-To-CAS (tRCD) & RAS Precharge (tRP) + ptr = (uintptr_t*)(mchbar_addr + offset + ICL_MMR_TIMINGS); + imc.tRP = imc.tRCD = *ptr & 0x3F; + + // RAS Active to precharge (tRAS) + imc.tRAS = (*ptr >> 9) & 0x7F; +} diff --git a/system/imc/intel_skl.c b/system/imc/intel_skl.c new file mode 100644 index 0000000..790d8f9 --- /dev/null +++ b/system/imc/intel_skl.c @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2004-2023 Sam Demeulemeester +// +// ------------------------ +// +// Platform-specific code for Intel Skylake CPUs (SKL) +// + +#include "cpuinfo.h" +#include "memctrl.h" +#include "msr.h" +#include "pci.h" +#include "vmem.h" + +#include "imc.h" + +#define SKL_MMR_BASE_REG_LOW 0x48 +#define SKL_MMR_BASE_REG_HIGH 0x4C +#define SKL_MMR_TIMINGS 0x4000 +#define SKL_MMR_SCHEDULER_CONF 0x401C +#define SKL_MMR_TIMING_CAS 0x4070 +#define SKL_MMR_MAD_CHAN0 0x500C +#define SKL_MMR_MAD_CHAN1 0x5010 +#define SKL_MMR_DRAM_CLOCK 0x5E00 + +#define SKL_MMR_WINDOW_RANGE (1UL << 15) + +#define SKL_MMR_BASE_MASK 0x7FFFFF8000 +#define SKL_MMR_MAD_IN_USE_MASK 0x003F003F + +void get_imc_config_intel_skl(void) +{ + uint64_t mmio_reg; + uint32_t reg0, reg1, offset; + float cpu_ratio, dram_ratio; + uintptr_t *ptr; + + // Get Memory Mapped Register Base Address (Enable MMIO if needed) + mmio_reg = pci_config_read32(0, 0, 0, SKL_MMR_BASE_REG_LOW); + if (!(mmio_reg & 0x1)) { + pci_config_write32( 0, 0, 0, SKL_MMR_BASE_REG_LOW, mmio_reg | 1); + mmio_reg = pci_config_read32(0, 0, 0, SKL_MMR_BASE_REG_LOW); + if (!(mmio_reg & 0x1)) return; + } + + mmio_reg |= (uint64_t)pci_config_read32(0, 0, 0, SKL_MMR_BASE_REG_HIGH) << 32; + mmio_reg &= SKL_MMR_BASE_MASK; + +#ifndef __x86_64__ + if (mmio_reg >= (1ULL << 32)) return; // MMIO is outside reachable range +#endif + + uintptr_t mchbar_addr = map_region(mmio_reg, SKL_MMR_WINDOW_RANGE, false); + + // Get DRAM Ratio + ptr = (uintptr_t*)(mchbar_addr + SKL_MMR_DRAM_CLOCK); + reg0 = *ptr & 0xF; + + if (reg0 < 3) return; + + dram_ratio = reg0 * (133.34f / 100.0f); + + // Get CPU Ratio + rdmsr(MSR_IA32_PLATFORM_INFO, reg0, reg1); + cpu_ratio = (float)((reg0 >> 8) & 0xFF); + + if (!cpu_ratio) return; + + // Compute DRAM Frequency + imc.freq = ((clks_per_msec / 1000) / cpu_ratio) * dram_ratio * 2; + + if (imc.freq < 150 || imc.freq > 8000) { + imc.freq = 0; + return; + } + + // Get Main Memory Controller Register for both channels + ptr = (uintptr_t*)(mchbar_addr + SKL_MMR_MAD_CHAN0); + reg0 = *ptr & SKL_MMR_MAD_IN_USE_MASK; + + ptr = (uintptr_t*)(mchbar_addr + SKL_MMR_MAD_CHAN1); + reg1 = *ptr & SKL_MMR_MAD_IN_USE_MASK; + + // Populate IMC width + imc.width = (reg0 && reg1) ? 128 : 64; + + // Define offset (ie: which channel is really used) + offset = reg0 ? 0x0000 : 0x0400; + + // SKL supports DDR3 & DDR4. Check DDR Type. + ptr = (uintptr_t*)(mchbar_addr + offset + SKL_MMR_SCHEDULER_CONF); + imc.type = (*ptr & 0x3) ? "DDR3" : "DDR4"; + + // CAS Latency (tCAS) + ptr = (uintptr_t*)(mchbar_addr + offset + SKL_MMR_TIMING_CAS); + imc.tCL = (*ptr >> 16) & 0x1F; + imc.tCL_dec = 0; + + // RAS-To-CAS (tRCD) & RAS Precharge (tRP) + ptr = (uintptr_t*)(mchbar_addr + offset + SKL_MMR_TIMINGS); + imc.tRP = imc.tRCD = *ptr & 0x3F; + + // RAS Active to precharge (tRAS) + imc.tRAS = (*ptr >> 8) & 0x7F; +} diff --git a/system/imc/intel_snb.c b/system/imc/intel_snb.c new file mode 100644 index 0000000..20a1e12 --- /dev/null +++ b/system/imc/intel_snb.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2004-2023 Sam Demeulemeester +// +// ------------------------ +// +// Platform-specific code for Intel Sandy Bridge CPUs (SNB) +// + +#include "cpuinfo.h" +#include "memctrl.h" +#include "msr.h" +#include "pci.h" + +#include "imc.h" + +#define SNB_MMR_BASE_REG 0x48 +#define SNB_REG_MAIN_CHAN0 0x5004 +#define SNB_REG_MAIN_CHAN1 0x5008 +#define SNB_REG_MCH_CFG 0x5E04 +#define SNB_REG_TIMING 0x4000 + +void get_imc_config_intel_snb(void) +{ + uint32_t mmio_reg, offset; + uint32_t mch_cfg, reg0, reg1; + float cpu_ratio, dram_ratio; + uint32_t *ptr; + + imc.type = "DDR3"; + imc.tCL_dec = 0; + + // Get Memory Mapped Register Base Address (Enable MMIO if needed) + mmio_reg = pci_config_read32(0, 0, 0, SNB_MMR_BASE_REG); + + if (!(mmio_reg & 0x1)) { + pci_config_write32( 0, 0, 0, SNB_MMR_BASE_REG, mmio_reg | 1); + mmio_reg = pci_config_read32(0, 0, 0, SNB_MMR_BASE_REG); + if (!(mmio_reg & 0x1)) return; + } + mmio_reg &= 0xFFFFC000; + + // Get DRAM Ratio + ptr = (uint32_t*)((uintptr_t)mmio_reg + SNB_REG_MCH_CFG); + mch_cfg = *ptr & 0xFFFF; + + if ((mch_cfg >> 8) & 1) { + dram_ratio = (float)(*ptr & 0x1F) * (100.0f / 100.0f); + } else { + dram_ratio = (float)(*ptr & 0x1F) * (133.34f / 100.0f); + } + + // Get CPU Ratio + rdmsr(MSR_IA32_PLATFORM_INFO, reg0, reg1); + cpu_ratio = (float)((reg0 >> 8) & 0xFF); + + if (!cpu_ratio) return; + + // Compute DRAM Frequency + imc.freq = ((clks_per_msec / 1000) / cpu_ratio) * dram_ratio * 2; + + if (imc.freq < 350 || imc.freq > 5000) { + imc.freq = 0; + return; + } + + // Get Main Memory Controller Register for both channels + ptr = (uint32_t*)((uintptr_t)mmio_reg + SNB_REG_MAIN_CHAN0); + reg0 = *ptr & 0xFFFF; + + ptr = (uint32_t*)((uintptr_t)mmio_reg + SNB_REG_MAIN_CHAN1); + reg1 = *ptr & 0xFFFF; + + // Populate IMC width + imc.width = (reg0 && reg1) ? 128 : 64; + + // Define offset (chan A or B used) + offset = reg0 ? 0x0 : 0x0400; + + // Get Main timing register + reg0 = *(uint32_t*)((uintptr_t)mmio_reg + offset + SNB_REG_TIMING); + + // CAS Latency (tCAS) + imc.tCL = (reg0 >> 8) & 0xF; + + // RAS-To-CAS (tRCD) + imc.tRCD = reg0 & 0xF; + + // RAS Precharge (tRP) + imc.tRP = (reg0 >> 4) & 0xF; + + // RAS Active to precharge (tRAS) + imc.tRAS = (reg0 >> 16) & 0xFF; +} diff --git a/system/memctrl.c b/system/memctrl.c new file mode 100644 index 0000000..75a9c0d --- /dev/null +++ b/system/memctrl.c @@ -0,0 +1,65 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (C) 2004-2023 Sam Demeulemeester +// +// ------------------------ +// +// Platform-specific code for IMC configuration, ECC support, etc. +// + +#include + +#include "config.h" +#include "cpuinfo.h" + +#include "memctrl.h" +#include "imc/imc.h" + +imc_info_t imc = {"UNDEF", 0, 0, 0, 0, 0, 0, 0, 0}; + +ecc_info_t ecc_status = {false, ECC_ERR_NONE, 0, 0, 0, 0, 0}; + +// --------------------- +// -- Public function -- +// --------------------- + +void memctrl_init(void) +{ + ecc_status.ecc_enabled = false; + + if (!enable_mch_read) { + return; + } + + switch(imc.family) { + case IMC_K17: + case IMC_K19_VRM: + case IMC_K19_RPL: + get_imc_config_amd_zen(); + break; + case IMC_SNB: + case IMC_IVB: + get_imc_config_intel_snb(); + break; + case IMC_HSW: + get_imc_config_intel_hsw(); + break; + case IMC_SKL: + case IMC_KBL: + get_imc_config_intel_skl(); + break; + case IMC_RKL: + get_imc_config_intel_icl(); + break; + case IMC_RPL: + case IMC_ADL: + get_imc_config_intel_adl(); + break; + default: + break; + } + + // Consistency check + if (imc.tCL == 0 || imc.tRCD == 0 || imc.tRP == 0 || imc.tRCD == 0) { + imc.freq = 0; + } +} diff --git a/system/memctrl.h b/system/memctrl.h new file mode 100644 index 0000000..b27e3d9 --- /dev/null +++ b/system/memctrl.h @@ -0,0 +1,57 @@ +// SPDX-License-Identifier: GPL-2.0 +#ifndef MEMCTRL_H +#define MEMCTRL_H +/** + * \file + * + * Provides information about the memory controller status + * (running DRAM configuration, ECC, ...) and other + * platform-specific data + * + *//* + * Copyright (C) 2004-2023 Sam Demeulemeester. + */ + +typedef struct __attribute__((packed)) imc_infos { + char *type; + uint16_t family; + uint16_t freq; + uint16_t width; + uint16_t tCL; + uint8_t tCL_dec; + uint16_t tRCD; + uint16_t tRP; + uint16_t tRAS; +} imc_info_t; + +typedef enum { + ECC_ERR_NONE, + ECC_ERR_CORRECTED, + ECC_ERR_UNCORRECTED +} ecc_error_type_t; + +typedef struct __attribute__((packed)) ecc_status { + bool ecc_enabled; + ecc_error_type_t err_type; + uint64_t err_adr; + uint32_t err_col; + uint32_t err_row; + uint32_t err_rank; + uint32_t err_bank; +} ecc_info_t; + +/** + * Current DRAM configuration of the Integrated Memory Controller + */ + +extern imc_info_t imc; + +/** + * Current ECC Status of the Integrated Memory Controller + */ + +extern ecc_info_t ecc_status; + +void memctrl_init(void); + +#endif // MEMCTRL_H diff --git a/system/msr.h b/system/msr.h index f4b98d8..4ef5a92 100644 --- a/system/msr.h +++ b/system/msr.h @@ -17,6 +17,7 @@ #define MSR_IA32_PLATFORM_ID 0x17 #define MSR_IA32_APIC_BASE 0x1b #define MSR_IA32_EBL_CR_POWERON 0x2a +#define MSR_IA32_PLATFORM_INFO 0xce #define MSR_IA32_MCG_CTL 0x17b #define MSR_IA32_PERF_STATUS 0x198 #define MSR_IA32_THERM_STATUS 0x19c diff --git a/system/pci.h b/system/pci.h index 3dccd89..1b6ecd4 100644 --- a/system/pci.h +++ b/system/pci.h @@ -86,15 +86,9 @@ void pci_config_write32(int bus, int dev, int func, int reg, uint32_t value); void lpc_outb(uint8_t cmd, uint8_t data); uint8_t lpc_inb(uint8_t reg); -/* - * Add some SNM related function (S.DEMEULEMEESTER) - */ - -#define SMN_SMUIO_THM 0x00059800 -#define SMN_THM_TCON_CUR_TMP (SMN_SMUIO_THM + 0x00) /** - * Read & Write to AMD Family 17h SNM + * Read & Write to AMD SNM */ uint32_t amd_smn_read(uint32_t adr); void amd_smn_write(uint32_t adr, uint32_t data); diff --git a/system/smbus.c b/system/smbus.c index a014ea2..0004595 100644 --- a/system/smbus.c +++ b/system/smbus.c @@ -10,6 +10,7 @@ #include "string.h" #include "cpuinfo.h" +#include "memctrl.h" #include "smbus.h" #include "smbios.h" #include "jedec_id.h" @@ -1407,7 +1408,7 @@ static bool fch_zen_get_smb(void) pm_reg |= __inb(AMD_DATA_IO_PORT); // Special case for AMD Family 19h & Extended Model > 4 (get smb address in memory) - if ((imc_type == IMC_K19_CZN || imc_type == IMC_K19_RPL) && pm_reg == 0xFFFF) { + if ((imc.family == IMC_K19_CZN || imc.family == IMC_K19_RPL) && pm_reg == 0xFFFF) { smbusbase = ((*(const uint32_t *)(0xFED80000 + 0x300) >> 8) & 0xFF) << 8; return true; } diff --git a/system/temperature.h b/system/temperature.h index f39c418..0677fa8 100644 --- a/system/temperature.h +++ b/system/temperature.h @@ -14,6 +14,10 @@ #define AMD_TEMP_REG_K8 0xE4 #define AMD_TEMP_REG_K10 0xA4 +// Temp Registers on AMD ZEN System Management Network +#define SMN_SMUIO_THM 0x00059800 +#define SMN_THM_TCON_CUR_TMP (SMN_SMUIO_THM + 0x00) + /** * Global CPU Temperature offset */