From badcf8a57cfdeb30cab16b41ea9b65ba2d679ab3 Mon Sep 17 00:00:00 2001 From: Sam Demeulemeester <38105886+x86fr@users.noreply.github.com> Date: Mon, 25 May 2026 20:57:38 +0200 Subject: [PATCH] Add temperature quirks for AMD EPYCs (#616) Closes #411 --- system/cpuinfo.h | 4 ++- system/hwquirks.c | 56 +++++++++++++++++++++++++++++++++++++++- system/hwquirks.h | 3 ++- system/x86/cpuinfo.c | 3 +++ system/x86/temperature.c | 16 +++++++++--- 5 files changed, 75 insertions(+), 7 deletions(-) diff --git a/system/cpuinfo.h b/system/cpuinfo.h index f136dad..a783acf 100644 --- a/system/cpuinfo.h +++ b/system/cpuinfo.h @@ -83,7 +83,9 @@ #define IMC_K1A_GRG 0x8210 // Zen5 (Granite Ridge) #define IMC_K1A_KRN 0x8220 // Zen5 (Krackan) #define IMC_K1A_STH 0x8230 // Zen5 (Strix Halo) -#define IMC_K1A_MDS 0x8240 // Zen6 (Medusa) +#define IMC_K1A_TUR 0x8240 // Zen5 (Turin) + +#define IMC_K1A_MDS 0x8300 // Zen6 (Medusa) #define IMC_LSLA 0xC000 // Loongson LoongArch family #define IMC_LA464 0xC010 // LA464 (Loongson 3th Gen) diff --git a/system/hwquirks.c b/system/hwquirks.c index 43c7ecc..fa13b35 100644 --- a/system/hwquirks.c +++ b/system/hwquirks.c @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -// Copyright (C) 2004-2025 Sam Demeulemeester +// Copyright (C) 2004-2026 Sam Demeulemeester // // ------------------------ // This file is used to detect quirks on specific hardware @@ -11,6 +11,7 @@ #include "hwquirks.h" #include "io.h" #include "pci.h" +#include "string.h" #include "unistd.h" #include "cpuinfo.h" #include "cpuid.h" @@ -158,6 +159,48 @@ static void amd_k8_revfg_temp(void) cpu_temp_offset = 21.0f; } +// AMD Zen Tctl -> Tdie offset table. +typedef struct { + uint8_t ext_family; + uint8_t ext_model; + float tctl_offset; + char brand_prefix[CPUID_BRAND_STR_LENGTH]; +} amd_tctl_offset_t; + +static const amd_tctl_offset_t amd_tctl_offset_table[] = { + { 0x8, 0x0, -20.0f, "AMD Ryzen 5 1600X" }, // Summit Ridge + { 0x8, 0x0, -20.0f, "AMD Ryzen 7 1700X" }, // Summit Ridge + { 0x8, 0x0, -20.0f, "AMD Ryzen 7 1800X" }, // Summit Ridge + { 0x8, 0x0, -10.0f, "AMD Ryzen 7 2700X" }, // Pinnacle Ridge + { 0x8, 0x0, -27.0f, "AMD Ryzen Threadripper 19" }, // Whitehaven (1900X/1920X/1950X) + { 0x8, 0x0, -27.0f, "AMD Ryzen Threadripper 29" }, // Colfax (29x0X/29x0WX) + { 0x8, 0x0, -27.0f, "AMD EPYC 7" }, // Naples (Family 17h, Model 01h) + //{ 0xA, 0xA, -49.0f, "AMD EPYC 8" }, // Siena (Maybe needed) + //{ 0xA, 0x1, -49.0f, "AMD EPYC 9" }, // Genoa (Maybe needed) + { 0xB, 0x0, -49.0f, "AMD EPYC 9" }, // Turin (Family 19h, Model 11h) + // Other EPYC parts report Tdie directly using the bit-19 (T_OFFSET_PRESENT) path. +}; + +static void amd_zen_apply_tctl_offset(void) +{ + const char *brand = cpuid_info.brand_id.str; + uint8_t ext_family = cpuid_info.version.extendedFamily; + uint8_t ext_model = cpuid_info.version.extendedModel; + + for (size_t i = 0; i < sizeof(amd_tctl_offset_table) / sizeof(amd_tctl_offset_table[0]); i++) { + const amd_tctl_offset_t *e = &amd_tctl_offset_table[i]; + + if (e->ext_family != ext_family || e->ext_model != ext_model) { + continue; + } + + if (strncmp(brand, e->brand_prefix, strlen(e->brand_prefix)) == 0) { + cpu_temp_offset += e->tctl_offset; + return; + } + } +} + static void loongson_7a00_ehci_workaround(void) { uintptr_t reg_addr = 0x10010000; @@ -327,6 +370,17 @@ void quirks_init(void) } } + // ----------------------------------------------------------- + // -- AMD Zen-class Tctl -> Tdie offset for affected SKUs -- + // ----------------------------------------------------------- + if (cpuid_info.vendor_id.str[0] == 'A' && cpuid_info.version.family == 0xF + && cpuid_info.version.extendedFamily >= 8) { + + quirk.id = QUIRK_AMD_ZEN_TCTL_OFFSET; + quirk.type |= QUIRK_TYPE_TEMP; + quirk.process = amd_zen_apply_tctl_offset; + } + // ----------------------------------------------------------- // -- Loongson 7A1000 and 7A2000 chipset USB 2.0 workaround -- // ----------------------------------------------------------- diff --git a/system/hwquirks.h b/system/hwquirks.h index a1bcb29..45e005c 100644 --- a/system/hwquirks.h +++ b/system/hwquirks.h @@ -1,5 +1,5 @@ // SPDX-License-Identifier: GPL-2.0 -// Copyright (C) 2004-2025 Sam Demeulemeester +// Copyright (C) 2004-2026 Sam Demeulemeester #ifndef _QUIRK_H_ #define _QUIRK_H_ /** @@ -29,6 +29,7 @@ typedef enum { QUIRK_K8_BSTEP_NOTEMP, QUIRK_K8_REVFG_TEMP, QUIRK_AMD_ERRATA_319, + QUIRK_AMD_ZEN_TCTL_OFFSET, QUIRK_VIA_VP, QUIRK_SIS_530, QUIRK_LOONGSON7A00_EHCI_WORKARD, diff --git a/system/x86/cpuinfo.c b/system/x86/cpuinfo.c index 60de38b..20cedf2 100644 --- a/system/x86/cpuinfo.c +++ b/system/x86/cpuinfo.c @@ -356,6 +356,9 @@ static void determine_imc(void) break; case 0xB: switch(cpuid_info.version.extendedModel) { + case 0: + imc.family = IMC_K1A_TUR; // Zen5 EPYC (Family 1Ah - Turin Classic, SP5) + break; case 2: imc.family = IMC_K1A_STP; // Zen5 APU (Family 1Ah - Strix Point) break; diff --git a/system/x86/temperature.c b/system/x86/temperature.c index 964ba7c..7280d40 100644 --- a/system/x86/temperature.c +++ b/system/x86/temperature.c @@ -78,6 +78,18 @@ void cpu_temp_init(void) } } } + + // On Zen and newer, check once whether firmware reports the generic + // -49 °C Tctl bias via T_OFFSET_PRESENT (bit 19). The result is folded + // into cpu_temp_offset so per-SKU quirks set above can still apply. + if (cpuid_info.vendor_id.str[0] == 'A' && cpuid_info.version.family == 0xF + && cpuid_info.version.extendedFamily >= 8) { + + regl = amd_smn_read(SMN_THM_TCON_CUR_TMP); + if ((regl >> 19) & 0x01) { + cpu_temp_offset += -49.0f; + } + } } int get_cpu_temp(void) @@ -100,10 +112,6 @@ int get_cpu_temp(void) regl = amd_smn_read(SMN_THM_TCON_CUR_TMP); - if ((regl >> 19) & 0x01) { - cpu_temp_offset = -49.0f; - } - return cpu_temp_offset + 0.125f * (float)((regl >> 21) & 0x7FF); } else if (cpuid_info.version.extendedFamily == 6 && (cpuid_info.version.extendedModel == 6 || cpuid_info.version.extendedModel == 7)) { // Target family 15h (Excavator)