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
This commit is contained in:
Sam Demeulemeester 2023-05-12 15:33:28 +02:00 committed by GitHub
parent 5dcd424ea7
commit 7aeac7271f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 809 additions and 73 deletions

View File

@ -133,6 +133,8 @@ recognised:
* disables the big PASS/FAIL pop-up status display * disables the big PASS/FAIL pop-up status display
* nosm * nosm
* disables SMBUS/SPD parsing, DMI decoding and memory benchmark * disables SMBUS/SPD parsing, DMI decoding and memory benchmark
* nomch
* disables memory controller configuration polling
* nopause * nopause
* skips the pause for configuration at startup * skips the pause for configuration at startup
* keyboard=*type* * keyboard=*type*

View File

@ -97,6 +97,7 @@ bool enable_trace = false;
bool enable_sm = true; bool enable_sm = true;
bool enable_bench = true; bool enable_bench = true;
bool enable_mch_read = true;
bool pause_at_start = true; bool pause_at_start = true;
@ -209,6 +210,8 @@ static void parse_option(const char *option, const char *params)
enable_big_status = false; enable_big_status = false;
} else if (strncmp(option, "noehci", 7) == 0) { } else if (strncmp(option, "noehci", 7) == 0) {
usb_init_options |= USB_IGNORE_EHCI; usb_init_options |= USB_IGNORE_EHCI;
} else if (strncmp(option, "nomch", 6) == 0) {
enable_mch_read = false;
} else if (strncmp(option, "nopause", 8) == 0) { } else if (strncmp(option, "nopause", 8) == 0) {
pause_at_start = false; pause_at_start = false;
} else if (strncmp(option, "nosm", 5) == 0) { } else if (strncmp(option, "nosm", 5) == 0) {

View File

@ -58,6 +58,7 @@ extern bool enable_trace;
extern bool enable_sm; extern bool enable_sm;
extern bool enable_tty; extern bool enable_tty;
extern bool enable_bench; extern bool enable_bench;
extern bool enable_mch_read;
extern bool pause_at_start; extern bool pause_at_start;

View File

@ -10,6 +10,7 @@
#include "hwctrl.h" #include "hwctrl.h"
#include "io.h" #include "io.h"
#include "keyboard.h" #include "keyboard.h"
#include "memctrl.h"
#include "serial.h" #include "serial.h"
#include "pmem.h" #include "pmem.h"
#include "smbios.h" #include "smbios.h"
@ -262,10 +263,14 @@ void post_display_init(void)
print_smbios_startup_info(); print_smbios_startup_info();
print_smbus_startup_info(); print_smbus_startup_info();
if (false) { if (imc.freq) {
// Try to get RAM information from IMC (TODO) // Try to get RAM information from IMC
display_spec_mode("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; display_mode = DISPLAY_MODE_IMC;
} else if (ram.freq > 0 && ram.tCL > 0) { } else if (ram.freq > 0 && ram.tCL > 0) {
// If not available, grab max memory specs from SPD // If not available, grab max memory specs from SPD

View File

@ -102,6 +102,10 @@ typedef enum {
#define display_spec_mode(mode) \ #define display_spec_mode(mode) \
prints(8,0, 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) \ #define display_spec_ddr(freq, type, cl, cl_dec, rcd, rp, ras) \
printf(8,5, "%uMHz (%s-%u) CAS %u%s-%u-%u-%u", \ printf(8,5, "%uMHz (%s-%u) CAS %u%s-%u-%u-%u", \
freq / 2, type, freq, cl, cl_dec?".5":"", rcd, rp, ras); freq / 2, type, freq, cl, cl_dec?".5":"", rcd, rp, ras);

View File

@ -28,6 +28,7 @@
#include "io.h" #include "io.h"
#include "keyboard.h" #include "keyboard.h"
#include "pmem.h" #include "pmem.h"
#include "memctrl.h"
#include "memsize.h" #include "memsize.h"
#include "pci.h" #include "pci.h"
#include "screen.h" #include "screen.h"
@ -235,6 +236,8 @@ static void global_init(void)
config_init(); config_init();
memctrl_init();
tty_init(); tty_init();
smp_init(smp_enabled); smp_init(smp_enabled);

View File

@ -25,6 +25,7 @@ SYS_OBJS = system/acpi.o \
system/hwquirks.o \ system/hwquirks.o \
system/keyboard.o \ system/keyboard.o \
system/ohci.o \ system/ohci.o \
system/memctrl.o \
system/pci.o \ system/pci.o \
system/pmem.o \ system/pmem.o \
system/reloc.o \ system/reloc.o \
@ -40,6 +41,9 @@ SYS_OBJS = system/acpi.o \
system/vmem.o \ system/vmem.o \
system/xhci.o system/xhci.o
IMC_SRCS = $(wildcard ../system/imc/*.c)
IMC_OBJS = $(subst ../,,$(IMC_SRCS:.c=.o))
LIB_OBJS = lib/barrier.o \ LIB_OBJS = lib/barrier.o \
lib/div64.o \ lib/div64.o \
lib/print.o \ lib/print.o \
@ -65,12 +69,13 @@ APP_OBJS = app/badram.o \
app/interrupt.o \ app/interrupt.o \
app/main.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 all: memtest.bin memtest.efi
-include boot/efisetup.d -include boot/efisetup.d
-include $(subst .o,.d,$(SYS_OBJS)) -include $(subst .o,.d,$(SYS_OBJS))
-include $(subst .o,.d,$(IMC_OBJS))
-include $(subst .o,.d,$(LIB_OBJS)) -include $(subst .o,.d,$(LIB_OBJS))
-include $(subst .o,.d,$(TST_OBJS)) -include $(subst .o,.d,$(TST_OBJS))
-include $(subst .o,.d,$(APP_OBJS)) -include $(subst .o,.d,$(APP_OBJS))
@ -97,6 +102,10 @@ system/%.o: ../system/%.c
@mkdir -p system @mkdir -p system
$(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) $(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 lib/%.o: ../lib/%.c
@mkdir -p lib @mkdir -p lib
$(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) $(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)

View File

@ -25,6 +25,7 @@ SYS_OBJS = system/acpi.o \
system/hwquirks.o \ system/hwquirks.o \
system/keyboard.o \ system/keyboard.o \
system/ohci.o \ system/ohci.o \
system/memctrl.o \
system/pci.o \ system/pci.o \
system/pmem.o \ system/pmem.o \
system/reloc.o \ system/reloc.o \
@ -40,6 +41,9 @@ SYS_OBJS = system/acpi.o \
system/vmem.o \ system/vmem.o \
system/xhci.o system/xhci.o
IMC_SRCS = $(wildcard ../system/imc/*.c)
IMC_OBJS = $(subst ../,,$(IMC_SRCS:.c=.o))
LIB_OBJS = lib/barrier.o \ LIB_OBJS = lib/barrier.o \
lib/print.o \ lib/print.o \
lib/read.o \ lib/read.o \
@ -64,12 +68,13 @@ APP_OBJS = app/badram.o \
app/interrupt.o \ app/interrupt.o \
app/main.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 all: memtest.bin memtest.efi
-include boot/efisetup.d -include boot/efisetup.d
-include $(subst .o,.d,$(SYS_OBJS)) -include $(subst .o,.d,$(SYS_OBJS))
-include $(subst .o,.d,$(IMC_OBJS))
-include $(subst .o,.d,$(LIB_OBJS)) -include $(subst .o,.d,$(LIB_OBJS))
-include $(subst .o,.d,$(TST_OBJS)) -include $(subst .o,.d,$(TST_OBJS))
-include $(subst .o,.d,$(APP_OBJS)) -include $(subst .o,.d,$(APP_OBJS))
@ -96,6 +101,10 @@ system/%.o: ../system/%.c
@mkdir -p system @mkdir -p system
$(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) $(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 lib/%.o: ../lib/%.c
@mkdir -p lib @mkdir -p lib
$(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d) $(CC) -c $(CFLAGS) -Os $(INC_DIRS) -o $@ $< -MMD -MP -MT $@ -MF $(@:.o=.d)

View File

@ -1,11 +1,10 @@
// SPDX-License-Identifier: GPL-2.0 // SPDX-License-Identifier: GPL-2.0
// Copyright (C) 2020-2022 Martin Whitaker. // Copyright (C) 2020-2022 Martin Whitaker.
// Copyright (C) 2004-2023 Sam Demeulemeester
// //
// Derived from an extract of memtest86+ init.c: // Derived from an extract of memtest86+ init.c:
// //
// MemTest86+ V5 Specific code (GPL V2.0) // 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 // init.c - MemTest-86 Version 3.6
// //
@ -24,6 +23,7 @@
#include "config.h" #include "config.h"
#include "pmem.h" #include "pmem.h"
#include "vmem.h" #include "vmem.h"
#include "memctrl.h"
#include "memsize.h" #include "memsize.h"
#include "hwquirks.h" #include "hwquirks.h"
@ -41,8 +41,6 @@
const char *cpu_model = NULL; const char *cpu_model = NULL;
uint16_t imc_type = 0;
int l1_cache = 0; int l1_cache = 0;
int l2_cache = 0; int l2_cache = 0;
int l3_cache = 0; int l3_cache = 0;
@ -304,37 +302,37 @@ static void determine_imc(void)
switch (cpuid_info.version.extendedFamily) switch (cpuid_info.version.extendedFamily)
{ {
case 0x0: case 0x0:
imc_type = IMC_K8; // Old K8 imc.family = IMC_K8; // Old K8
break; break;
case 0x1: case 0x1:
case 0x2: case 0x2:
imc_type = IMC_K10; // K10 (Family 10h & 11h) imc.family = IMC_K10; // K10 (Family 10h & 11h)
break; break;
case 0x3: case 0x3:
imc_type = IMC_K12; // A-Series APU (Family 12h) imc.family = IMC_K12; // A-Series APU (Family 12h)
break; break;
case 0x5: 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; break;
case 0x6: case 0x6:
imc_type = IMC_K15; // FX Series (Family 15h) imc.family = IMC_K15; // FX Series (Family 15h)
break; break;
case 0x7: case 0x7:
imc_type = IMC_K16; // Kabini & related (Family 16h) imc.family = IMC_K16; // Kabini & related (Family 16h)
break; break;
case 0x8: case 0x8:
imc_type = IMC_K17; // Zen & Zen2 (Family 17h) imc.family = IMC_K17; // Zen & Zen2 (Family 17h)
break; break;
case 0x9: case 0x9:
imc_type = IMC_K18; // Hygon (Family 18h) imc.family = IMC_K18; // Hygon (Family 18h)
break; break;
case 0xA: case 0xA:
if (cpuid_info.version.extendedModel == 5) { 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) { } 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 { } else {
imc_type = IMC_K19; // Zen3 (Family 19h) imc.family = IMC_K19_VRM; // Zen3 (Family 19h - Vermeer AM4)
} }
default: default:
break; break;
@ -349,16 +347,16 @@ static void determine_imc(void)
case 0x5: case 0x5:
switch (cpuid_info.version.extendedModel) { switch (cpuid_info.version.extendedModel) {
case 2: 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; break;
case 3: case 3:
no_temperature = true; // Atom Clover Trail no_temperature = true; // Atom Clover Trail
break; break;
case 4: case 4:
imc_type = IMC_HSW_ULT; // Core 4th Gen (Haswell-ULT) imc.family = IMC_HSW_ULT; // Core 4th Gen (Haswell-ULT)
break; break;
case 5: 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; break;
default: default:
break; break;
@ -368,17 +366,17 @@ static void determine_imc(void)
case 0x6: case 0x6:
switch (cpuid_info.version.extendedModel) { switch (cpuid_info.version.extendedModel) {
case 3: case 3:
imc_type = IMC_CDT; // Atom Cedar Trail imc.family = IMC_CDT; // Atom Cedar Trail
no_temperature = true; no_temperature = true;
break; break;
case 4: case 4:
imc_type = IMC_HSW; // Core 4th Gen (Haswell w/ GT3e) imc.family = IMC_HSW; // Core 4th Gen (Haswell w/ GT3e)
break; break;
case 5: case 5:
imc_type = IMC_BDW_DE; // Broadwell-DE (Server) imc.family = IMC_BDW_DE; // Broadwell-DE (Server)
break; break;
case 6: case 6:
imc_type = IMC_CNL; // Cannon Lake imc.family = IMC_CNL; // Cannon Lake
break; break;
default: default:
break; break;
@ -388,19 +386,19 @@ static void determine_imc(void)
case 0x7: case 0x7:
switch (cpuid_info.version.extendedModel) { switch (cpuid_info.version.extendedModel) {
case 0x3: case 0x3:
imc_type = IMC_BYT; // Atom Bay Trail imc.family = IMC_BYT; // Atom Bay Trail
break; break;
case 0x4: case 0x4:
imc_type = IMC_BDW; // Core 5th Gen (Broadwell) imc.family = IMC_BDW; // Core 5th Gen (Broadwell)
break; break;
case 0x9: case 0x9:
imc_type = IMC_ADL; // Core 12th Gen (Alder Lake-P) imc.family = IMC_ADL; // Core 12th Gen (Alder Lake-P)
break; break;
case 0xA: case 0xA:
imc_type = IMC_RKL; // Core 11th Gen (Rocket Lake) imc.family = IMC_RKL; // Core 11th Gen (Rocket Lake)
break; break;
case 0xB: case 0xB:
imc_type = IMC_RPL; // Core 13th Gen (Raptor Lake) imc.family = IMC_RPL; // Core 13th Gen (Raptor Lake)
break; break;
default: default:
break; break;
@ -410,19 +408,19 @@ static void determine_imc(void)
case 0xA: case 0xA:
switch (cpuid_info.version.extendedModel) { switch (cpuid_info.version.extendedModel) {
case 0x1: 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; break;
case 0x2: case 0x2:
imc_type = IMC_SNB; // Core 2nd Gen (Sandy Bridge) imc.family = IMC_SNB; // Core 2nd Gen (Sandy Bridge)
break; break;
case 0x3: case 0x3:
imc_type = IMC_IVB; // Core 3rd Gen (Ivy Bridge) imc.family = IMC_IVB; // Core 3rd Gen (Ivy Bridge)
break; break;
case 0x6: case 0x6:
imc_type = IMC_ICL_SP; // Ice Lake-SP/DE (Server) imc.family = IMC_ICL_SP; // Ice Lake-SP/DE (Server)
break; break;
case 0x9: case 0x9:
imc_type = IMC_ADL; // Core 12th Gen (Alder Lake-S) imc.family = IMC_ADL; // Core 12th Gen (Alder Lake-S)
break; break;
default: default:
break; break;
@ -433,18 +431,18 @@ static void determine_imc(void)
switch (cpuid_info.version.extendedModel) { switch (cpuid_info.version.extendedModel) {
case 0x1: case 0x1:
if (cpuid_info.version.stepping > 9) { if (cpuid_info.version.stepping > 9) {
imc_type = 0x0008; // Atom PineView imc.family = 0x0008; // Atom PineView
} }
no_temperature = true; no_temperature = true;
break; break;
case 0x2: 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; break;
case 0x3: case 0x3:
imc_type = IMC_HSW; // Core 4th Gen (Haswell) imc.family = IMC_HSW; // Core 4th Gen (Haswell)
break; break;
case 0x8: case 0x8:
imc_type = IMC_TGL; // Core 11th Gen (Tiger Lake-U) imc.family = IMC_TGL; // Core 11th Gen (Tiger Lake-U)
break; break;
default: default:
break; break;
@ -454,13 +452,13 @@ static void determine_imc(void)
case 0xD: case 0xD:
switch (cpuid_info.version.extendedModel) { switch (cpuid_info.version.extendedModel) {
case 0x2: 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; break;
case 0x7: case 0x7:
imc_type = IMC_ICL; // Core 10th Gen (IceLake-Y) imc.family = IMC_ICL; // Core 10th Gen (IceLake-Y)
break; break;
case 0x8: case 0x8:
imc_type = IMC_TGL; // Core 11th Gen (Tiger Lake-Y) imc.family = IMC_TGL; // Core 11th Gen (Tiger Lake-Y)
break; break;
default: default:
break; break;
@ -470,31 +468,31 @@ static void determine_imc(void)
case 0xE: case 0xE:
switch (cpuid_info.version.extendedModel) { switch (cpuid_info.version.extendedModel) {
case 0x1: 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; break;
case 0x2: 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; break;
case 0x3: 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; break;
case 0x4: 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; break;
case 0x5: 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; break;
case 0x7: case 0x7:
imc_type = IMC_ICL; // Core 10th Gen (IceLake-U) imc.family = IMC_ICL; // Core 10th Gen (IceLake-U)
break; break;
case 0x8: 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; break;
case 0x9: 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; break;
case 0xB: 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; break;
default: default:
break; break;
@ -504,13 +502,13 @@ static void determine_imc(void)
case 0xF: case 0xF:
switch (cpuid_info.version.extendedModel) { switch (cpuid_info.version.extendedModel) {
case 0x3: case 0x3:
imc_type = IMC_HSW_E; // Core 3rd Gen (Haswell-E) imc.family = IMC_HSW_E; // Core 3rd Gen (Haswell-E)
break; break;
case 0x4: case 0x4:
imc_type = IMC_BDW_E; // Broadwell-E (Server) imc.family = IMC_BDW_E; // Broadwell-E (Server)
break; break;
case 0x8: case 0x8:
imc_type = IMC_SPR; // Sapphire Rapids (Server) imc.family = IMC_SPR; // Sapphire Rapids (Server)
break; break;
default: default:
break; break;
@ -813,7 +811,7 @@ static void determine_cpu_model(void)
// All VIA/Centaur family values >= 6 have brand string // All VIA/Centaur family values >= 6 have brand string
break; break;
} }
} else { /* CyrixInstead */ } else { // CyrixInstead
switch (cpuid_info.version.family) { switch (cpuid_info.version.family) {
case 4: case 4:
switch (cpuid_info.version.model) { switch (cpuid_info.version.model) {

View File

@ -60,20 +60,16 @@
#define IMC_K16 0x8050 // Kabini & related (Family 16h) #define IMC_K16 0x8050 // Kabini & related (Family 16h)
#define IMC_K17 0x8060 // Zen & Zen2 (Family 17h) #define IMC_K17 0x8060 // Zen & Zen2 (Family 17h)
#define IMC_K18 0x8070 // Hygon (Family 18h) #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_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. * A string identifying the CPU make and model.
*/ */
extern const char *cpu_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. * The size of the L1 cache in KB.
*/ */

71
system/imc/amd_zen.c Normal file
View File

@ -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;
}

24
system/imc/imc.h Normal file
View File

@ -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_ */

100
system/imc/intel_adl.c Normal file
View File

@ -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;
}

92
system/imc/intel_hsw.c Normal file
View File

@ -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;
}

99
system/imc/intel_icl.c Normal file
View File

@ -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;
}

105
system/imc/intel_skl.c Normal file
View File

@ -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;
}

93
system/imc/intel_snb.c Normal file
View File

@ -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;
}

65
system/memctrl.c Normal file
View File

@ -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 <stdbool.h>
#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;
}
}

57
system/memctrl.h Normal file
View File

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

View File

@ -17,6 +17,7 @@
#define MSR_IA32_PLATFORM_ID 0x17 #define MSR_IA32_PLATFORM_ID 0x17
#define MSR_IA32_APIC_BASE 0x1b #define MSR_IA32_APIC_BASE 0x1b
#define MSR_IA32_EBL_CR_POWERON 0x2a #define MSR_IA32_EBL_CR_POWERON 0x2a
#define MSR_IA32_PLATFORM_INFO 0xce
#define MSR_IA32_MCG_CTL 0x17b #define MSR_IA32_MCG_CTL 0x17b
#define MSR_IA32_PERF_STATUS 0x198 #define MSR_IA32_PERF_STATUS 0x198
#define MSR_IA32_THERM_STATUS 0x19c #define MSR_IA32_THERM_STATUS 0x19c

View File

@ -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); void lpc_outb(uint8_t cmd, uint8_t data);
uint8_t lpc_inb(uint8_t reg); 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); uint32_t amd_smn_read(uint32_t adr);
void amd_smn_write(uint32_t adr, uint32_t data); void amd_smn_write(uint32_t adr, uint32_t data);

View File

@ -10,6 +10,7 @@
#include "string.h" #include "string.h"
#include "cpuinfo.h" #include "cpuinfo.h"
#include "memctrl.h"
#include "smbus.h" #include "smbus.h"
#include "smbios.h" #include "smbios.h"
#include "jedec_id.h" #include "jedec_id.h"
@ -1407,7 +1408,7 @@ static bool fch_zen_get_smb(void)
pm_reg |= __inb(AMD_DATA_IO_PORT); pm_reg |= __inb(AMD_DATA_IO_PORT);
// Special case for AMD Family 19h & Extended Model > 4 (get smb address in memory) // 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; smbusbase = ((*(const uint32_t *)(0xFED80000 + 0x300) >> 8) & 0xFF) << 8;
return true; return true;
} }

View File

@ -14,6 +14,10 @@
#define AMD_TEMP_REG_K8 0xE4 #define AMD_TEMP_REG_K8 0xE4
#define AMD_TEMP_REG_K10 0xA4 #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 * Global CPU Temperature offset
*/ */