amd-pstate 7.4 content (2026-09-14)

* Fix for amd-pstate-ut with 'Requested CPU Min frequency' BIOS option
 * New Zen6 tuning values for EPP
 * Documentation fix
 -----BEGIN PGP SIGNATURE-----
 
 iQJHBAABCgAxFiEECwtuSU6dXvs5GA2aLRkspiR3AnYFAmqoEmQTHHN1cGVybTFA
 a2VybmVsLm9yZwAKCRAtGSymJHcCdsTGD/4vBrpgo0hptJxRHuIL49JTizEZKMoC
 TQ3PD9P9shJvGX8CaRCUQaDfd6c5ARKlo+Sz3PFMc8Xwa8oyG+L519+1f2P6YieI
 mxf1wi4fZMynKWZFxemEd52z7HZTufYNrWkYWdHr9K7BxnzwIX2j2jPjVXB8GfjI
 fYmu0jL9gA7sMHFXwWXiTcPgghv/w1nTInHX3vIfSFGKa5L3BG02Fq3iQxl55qB+
 VV0f0Mlp5hM2wkuAYtTStFhY2IP9RDpHar6umHeYfjEVsVMe8z5V2Y2aQNYnfN47
 pvb8kVAr916eYxhmQPS+HrKRld4cPs7dVfOOti3CTENtcX663npNtMGOd5XIBPtP
 8xxaVIb2c1oD6XKFM3iRUJKN33eYFTUi6LNWejiN4SpPe/zh9Mz6/6+dlb2VxfK0
 cLDvuDC5v5fZ3Xx2tyG18UfZD50E++BefajUVF4HDRkHB3oHa1x+2SW2jl1aC79x
 V4xwguaOroSJiueM98yticlS/VjtR3c9ZGYcgPWUCuSTe43Vc6PF6o8hhhCOBDZB
 VpBifHE2fx+UEf6dsF0AIsH/tlcHlaqb74xfhx/5ysp+VOj0n4IgCxESkcL6Wsao
 VI2IBAH/w6fHkCaVwU5W9jklCvjpdWUO+g9avQEGgx0nO+INIA72IQpipuM5PVYg
 QVIqK4MPU5Lcrg==
 =A9lG
 -----END PGP SIGNATURE-----

Merge tag 'amd-pstate-v7.4-2026-09-14' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/superm1/linux

Pull amd-pstate 7.4 content (2026-09-14) from Mario Limonciello:

"* Fix for amd-pstate-ut with 'Requested CPU Min frequency' BIOS option
 * New Zen6 tuning values for EPP
 * Documentation fix"

* tag 'amd-pstate-v7.4-2026-09-14' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/superm1/linux:
  cpufreq/amd-pstate: Remove obsolete amd_dynamic_epp documentation
  cpufreq/amd-pstate: Show a warning if missing EPP tunings
  cpufreq/amd-pstate: Add EPP tunings for Zen6 client platforms
  cpufreq/amd-pstate: Add per SoC and per core type EPP tuning values
  cpufreq/amd-pstate-ut: Fix amd_pstate_ut_check_freq failure with 'Requested CPU Min frequency' BIOS option
This commit is contained in:
Rafael J. Wysocki 2026-09-14 20:25:51 +02:00
commit 9e2055b872
5 changed files with 214 additions and 64 deletions

View File

@ -501,13 +501,6 @@ Kernel parameters
disable
Disable amd-pstate preferred core.
amd_dynamic_epp=
[X86]
disable
Disable amd-pstate dynamic EPP.
enable
Enable amd-pstate dynamic EPP.
amijoy.map= [HW,JOY] Amiga joystick support
Map of devices attached to JOY0DAT and JOY1DAT
Format: <a>,<b>

View File

@ -481,12 +481,6 @@ For systems that support ``amd-pstate`` preferred core, the core rankings will
always be advertised by the platform. But OS can choose to ignore that via the
kernel parameter ``amd_prefcore=disable``.
``amd_dynamic_epp``
When AMD pstate is in auto mode, dynamic EPP will control whether the kernel
autonomously changes the EPP mode. The default is disabled. It can be enabled
with the kernel parameter ``amd_dynamic_epp=enable``.
User Space Interface in ``sysfs`` - General
===========================================

View File

@ -226,11 +226,14 @@ static int amd_pstate_ut_check_freq(u32 index)
for_each_online_cpu(cpu) {
struct cpufreq_policy *policy __free(put_cpufreq_policy) = NULL;
struct amd_cpudata *cpudata;
union perf_cached perf;
policy = cpufreq_cpu_get(cpu);
if (!policy)
continue;
cpudata = policy->driver_data;
perf = READ_ONCE(cpudata->perf);
if (!((policy->cpuinfo.max_freq >= cpudata->nominal_freq) &&
(cpudata->nominal_freq > cpudata->lowest_nonlinear_freq) &&
@ -242,7 +245,25 @@ static int amd_pstate_ut_check_freq(u32 index)
return -EINVAL;
}
if (cpudata->lowest_nonlinear_freq != policy->min) {
if (perf.bios_min_perf) {
u32 bios_min_freq = perf_to_freq(perf, cpudata->nominal_freq,
perf.bios_min_perf);
/*
* User set bios_min_freq cannot be trusted to
* be within the driver limits. Clamp it similar
* to cpufreq_verify_within_cpu_limits().
*/
bios_min_freq = clamp_t(u32, bios_min_freq,
policy->cpuinfo.min_freq,
policy->cpuinfo.max_freq);
if (bios_min_freq != policy->min) {
pr_err("%s cpu%d bios_min_freq=%d policy_min=%d, they should be equal!\n",
__func__, cpu, bios_min_freq, policy->min);
return -EINVAL;
}
} else if (cpudata->lowest_nonlinear_freq != policy->min) {
pr_err("%s cpu%d cpudata_lowest_nonlinear_freq=%d policy_min=%d, they should be equal!\n",
__func__, cpu, cpudata->lowest_nonlinear_freq, policy->min);
return -EINVAL;

View File

@ -55,10 +55,10 @@
#define AMD_PSTATE_TRANSITION_DELAY 1000
#define AMD_PSTATE_FAST_CPPC_TRANSITION_DELAY 600
#define AMD_CPPC_EPP_PERFORMANCE 0x00
#define AMD_CPPC_EPP_BALANCE_PERFORMANCE 0x80
#define AMD_CPPC_EPP_BALANCE_POWERSAVE 0xBF
#define AMD_CPPC_EPP_POWERSAVE 0xFF
#define AMD_CPPC_EPP_LEGACY_PERFORMANCE 0x00
#define AMD_CPPC_EPP_LEGACY_BALANCE_PERFORMANCE 0x80
#define AMD_CPPC_EPP_LEGACY_BALANCE_POWERSAVE 0xBF
#define AMD_CPPC_EPP_LEGACY_POWERSAVE 0xFF
static const char * const amd_pstate_mode_string[] = {
[AMD_PSTATE_UNDEFINED] = "undefined",
@ -129,14 +129,112 @@ static const char * const energy_perf_strings[] = {
};
static_assert(ARRAY_SIZE(energy_perf_strings) == EPP_INDEX_MAX);
static unsigned int epp_values[] = {
[EPP_INDEX_DEFAULT] = 0,
[EPP_INDEX_PERFORMANCE] = AMD_CPPC_EPP_PERFORMANCE,
[EPP_INDEX_BALANCE_PERFORMANCE] = AMD_CPPC_EPP_BALANCE_PERFORMANCE,
[EPP_INDEX_BALANCE_POWERSAVE] = AMD_CPPC_EPP_BALANCE_POWERSAVE,
[EPP_INDEX_POWERSAVE] = AMD_CPPC_EPP_POWERSAVE,
/*
* The numeric EPP value programmed for each named preference. First dimension
* is CPU type (TOPO_CPU_TYPE_ANY for non-hybrid, TOPO_CPU_TYPE_PERFORMANCE/
* EFFICIENCY/LOW_POWER for hybrid). The initializer holds the legacy values
* used as the fallback on any platform not listed in amd_pstate_epp_soc_ids[];
* amd_pstate_init_epp_values() overwrites slots at boot when the running SoC
* has a per-SoC (and potentially per-CPU-type) override.
*/
static u8 epp_values[][EPP_INDEX_MAX] = {
/*
* Initialize all CPU types to legacy defaults.
* amd_pstate_init_epp_values() will fix these up
* based on the platform during boot.
*/
[TOPO_CPU_TYPE_ANY ... TOPO_CPU_TYPE_LOW_POWER] = {
[EPP_INDEX_DEFAULT] = 0,
[EPP_INDEX_PERFORMANCE] = AMD_CPPC_EPP_LEGACY_PERFORMANCE,
[EPP_INDEX_BALANCE_PERFORMANCE] = AMD_CPPC_EPP_LEGACY_BALANCE_PERFORMANCE,
[EPP_INDEX_BALANCE_POWERSAVE] = AMD_CPPC_EPP_LEGACY_BALANCE_POWERSAVE,
[EPP_INDEX_POWERSAVE] = AMD_CPPC_EPP_LEGACY_POWERSAVE,
},
};
static_assert(ARRAY_SIZE(epp_values) == TOPO_CPU_TYPE_LOW_POWER + 1,
"epp_values must have entries for all CPU types up to TOPO_CPU_TYPE_LOW_POWER");
/*
* Get the EPP value row for a given CPU, accounting for hybrid CPU types.
* Non-hybrid systems use TOPO_CPU_TYPE_ANY; hybrid systems use the CPU's
* actual type (PERFORMANCE/EFFICIENCY/LOW_POWER).
*/
static inline u8 *amd_pstate_cpu_epp_values(enum x86_topology_cpu_type cpu_type)
{
switch (cpu_type) {
case TOPO_CPU_TYPE_PERFORMANCE:
case TOPO_CPU_TYPE_EFFICIENCY:
case TOPO_CPU_TYPE_LOW_POWER:
return epp_values[cpu_type];
default:
return epp_values[TOPO_CPU_TYPE_ANY];
}
}
/**
* struct amd_pstate_epp_values - EPP values for the four named preferences
* @performance: value for the "performance" preference
* @balance_performance: value for the "balance_performance" preference
* @balance_power: value for the "balance_power" preference
* @power: value for the "power" preference
*/
struct amd_pstate_epp_values {
u8 performance;
u8 balance_performance;
u8 balance_power;
u8 power;
};
/**
* struct amd_pstate_epp_soc - per-CPU-type EPP overrides for hybrid systems
* @performance_core: values for TOPO_CPU_TYPE_PERFORMANCE cores
* @efficiency_core: values for TOPO_CPU_TYPE_EFFICIENCY cores
* @low_power_core: values for TOPO_CPU_TYPE_LOW_POWER cores
*
* Referenced from amd_pstate_epp_soc_ids[] to give a hybrid platform its own
* numeric EPP values for the four named preferences, with distinct values per
* CPU type. Non-hybrid systems are not listed in the table and always use the
* legacy defaults.
*/
struct amd_pstate_epp_soc {
struct amd_pstate_epp_values performance_core;
struct amd_pstate_epp_values efficiency_core;
struct amd_pstate_epp_values low_power_core;
};
/*
* Per-CPU-type EPP overrides for hybrid systems. Only hybrid SoCs should be
* listed here; non-hybrid systems always use the legacy defaults.
*/
static const struct amd_pstate_epp_soc epp_soc_zen6_client __initconst = {
.performance_core = {
.performance = 25,
.balance_performance = 51,
.balance_power = 64,
.power = 64,
},
.efficiency_core = {
.performance = 25,
.balance_performance = 51,
.balance_power = 64,
.power = 115,
},
.low_power_core = {
.performance = 25,
.balance_performance = 51,
.balance_power = 64,
.power = 115,
},
};
static const struct x86_cpu_id amd_pstate_epp_soc_ids[] __initconst = {
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x1A, 0x80, &epp_soc_zen6_client),
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x1A, 0x81, &epp_soc_zen6_client),
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x1A, 0x84, &epp_soc_zen6_client),
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x1A, 0x85, &epp_soc_zen6_client),
X86_MATCH_VENDOR_FAM_MODEL(AMD, 0x1A, 0xe0, &epp_soc_zen6_client),
{}
};
static_assert(ARRAY_SIZE(epp_values) == EPP_INDEX_MAX - 2);
typedef int (*cppc_mode_transition_fn)(int);
@ -145,19 +243,6 @@ static struct quirk_entry quirk_amd_7k62 = {
.lowest_freq = 550,
};
static inline u8 freq_to_perf(union perf_cached perf, u32 nominal_freq, unsigned int freq_val)
{
u32 perf_val = DIV_ROUND_UP_ULL((u64)freq_val * perf.nominal_perf, nominal_freq);
return (u8)clamp(perf_val, perf.lowest_perf, perf.highest_perf);
}
static inline u32 perf_to_freq(union perf_cached perf, u32 nominal_freq, u8 perf_val)
{
return DIV_ROUND_UP_ULL((u64)nominal_freq * perf_val,
perf.nominal_perf);
}
static int __init dmi_matched_7k62_bios_bug(const struct dmi_system_id *dmi)
{
/**
@ -1068,6 +1153,7 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy)
return -ENOMEM;
cpudata->cpu = policy->cpu;
cpudata->cpu_type = cpu_data(policy->cpu).topo.cpu_type;
ret = amd_pstate_init_perf(cpudata);
if (ret)
@ -1195,13 +1281,16 @@ static int amd_pstate_power_supply_notifier(struct notifier_block *nb,
static int amd_pstate_get_epp_from_platform_profile(struct cpufreq_policy *policy,
enum platform_profile_option profile)
{
struct amd_cpudata *cpudata = policy->driver_data;
u8 *values = amd_pstate_cpu_epp_values(cpudata->cpu_type);
switch (profile) {
case PLATFORM_PROFILE_PERFORMANCE:
return AMD_CPPC_EPP_PERFORMANCE;
return values[EPP_INDEX_PERFORMANCE];
case PLATFORM_PROFILE_BALANCED:
return amd_pstate_get_balanced_epp(policy);
case PLATFORM_PROFILE_LOW_POWER:
return AMD_CPPC_EPP_POWERSAVE;
return values[EPP_INDEX_POWERSAVE];
default:
break;
}
@ -1411,6 +1500,7 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
const char *buf, size_t count)
{
struct amd_cpudata *cpudata = policy->driver_data;
u8 *values = amd_pstate_cpu_epp_values(cpudata->cpu_type);
ssize_t ret;
bool raw_epp = false;
u8 epp;
@ -1445,12 +1535,13 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
}
if (ret)
epp = epp_values[ret];
epp = values[ret];
else
epp = cpudata->epp_default_dc;
}
if (epp > 0 && cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) {
if (epp > 0 && epp != values[EPP_INDEX_PERFORMANCE] &&
cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) {
pr_debug("EPP cannot be set under performance policy\n");
return -EBUSY;
}
@ -1475,34 +1566,31 @@ EXPORT_SYMBOL_FOR_PSTATE_UT(store_energy_performance_preference);
ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *buf)
{
struct amd_cpudata *cpudata = policy->driver_data;
u8 preference, epp;
u8 *values = amd_pstate_cpu_epp_values(cpudata->cpu_type);
u8 epp;
int i;
epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached);
if (!cpudata->dynamic_epp && cpudata->raw_epp)
return sysfs_emit(buf, "%u\n", epp);
switch (epp) {
case AMD_CPPC_EPP_PERFORMANCE:
preference = EPP_INDEX_PERFORMANCE;
break;
case AMD_CPPC_EPP_BALANCE_PERFORMANCE:
preference = EPP_INDEX_BALANCE_PERFORMANCE;
break;
case AMD_CPPC_EPP_BALANCE_POWERSAVE:
preference = EPP_INDEX_BALANCE_POWERSAVE;
break;
case AMD_CPPC_EPP_POWERSAVE:
preference = EPP_INDEX_POWERSAVE;
break;
default:
return -EINVAL;
/*
* Map the cached EPP value back to a named preference. Skip the
* "default" slot (index 0) so an EPP of 0 reports as "performance".
* Stop at POWERSAVE; CUSTOM and DYNAMIC are not initialized in epp_values.
*/
for (i = EPP_INDEX_PERFORMANCE; i <= EPP_INDEX_POWERSAVE; i++) {
const char *name = energy_perf_strings[i];
if (epp == values[i]) {
if (cpudata->dynamic_epp)
return sysfs_emit(buf, "dynamic(profile:%s)\n", name);
return sysfs_emit(buf, "%s\n", name);
}
}
if (cpudata->dynamic_epp)
return sysfs_emit(buf, "dynamic(profile:%s)\n", energy_perf_strings[preference]);
return sysfs_emit(buf, "%s\n", energy_perf_strings[preference]);
return sysfs_emit(buf, "%u\n", epp);
}
EXPORT_SYMBOL_FOR_PSTATE_UT(show_energy_performance_preference);
@ -1894,6 +1982,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
return -ENOMEM;
cpudata->cpu = policy->cpu;
cpudata->cpu_type = cpu_data(policy->cpu).topo.cpu_type;
ret = amd_pstate_init_perf(cpudata);
if (ret)
@ -1944,9 +2033,11 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
cpudata->epp_default_ac = cpudata->epp_default_dc = default_epp;
cpudata->current_profile = PLATFORM_PROFILE_PERFORMANCE;
} else {
u8 *values = amd_pstate_cpu_epp_values(cpudata->cpu_type);
policy->policy = CPUFREQ_POLICY_POWERSAVE;
cpudata->epp_default_ac = AMD_CPPC_EPP_PERFORMANCE;
cpudata->epp_default_dc = AMD_CPPC_EPP_BALANCE_PERFORMANCE;
cpudata->epp_default_ac = values[EPP_INDEX_PERFORMANCE];
cpudata->epp_default_dc = values[EPP_INDEX_BALANCE_PERFORMANCE];
cpudata->current_profile = PLATFORM_PROFILE_BALANCED;
}
@ -2245,6 +2336,38 @@ static bool amd_cppc_supported(void)
return true;
}
/*
* Resolve the numeric EPP values for hybrid systems. Only hybrid SoCs are listed
* in amd_pstate_epp_soc_ids[]; non-hybrid systems always use the legacy defaults.
*/
static inline void __init amd_pstate_set_epp_values(enum x86_topology_cpu_type type,
const struct amd_pstate_epp_values *core)
{
epp_values[type][EPP_INDEX_PERFORMANCE] = core->performance;
epp_values[type][EPP_INDEX_BALANCE_PERFORMANCE] = core->balance_performance;
epp_values[type][EPP_INDEX_BALANCE_POWERSAVE] = core->balance_power;
epp_values[type][EPP_INDEX_POWERSAVE] = core->power;
}
static void __init amd_pstate_init_epp_values(void)
{
const struct x86_cpu_id *id = x86_match_cpu(amd_pstate_epp_soc_ids);
const struct amd_pstate_epp_soc *soc;
if (!id || !id->driver_data) {
if (cpu_feature_enabled(X86_FEATURE_ZEN6) &&
cpu_feature_enabled(X86_FEATURE_AMD_HTR_CORES))
pr_warn_once("No EPP tunings found for platform\n");
return;
}
soc = (const struct amd_pstate_epp_soc *)id->driver_data;
amd_pstate_set_epp_values(TOPO_CPU_TYPE_PERFORMANCE, &soc->performance_core);
amd_pstate_set_epp_values(TOPO_CPU_TYPE_EFFICIENCY, &soc->efficiency_core);
amd_pstate_set_epp_values(TOPO_CPU_TYPE_LOW_POWER, &soc->low_power_core);
}
static int __init amd_pstate_init(void)
{
struct device *dev_root;
@ -2272,6 +2395,9 @@ static int __init amd_pstate_init(void)
/* check if this machine need CPPC quirks */
dmi_check_system(amd_pstate_quirks_table);
/* resolve per-SoC EPP values for the named preferences */
amd_pstate_init_epp_values();
/*
* determine the driver mode from the command line or kernel config.
* If no command line input is provided, cppc_state will be AMD_PSTATE_UNDEFINED.

View File

@ -146,6 +146,8 @@ struct amd_cpudata {
enum platform_profile_option current_profile;
struct device *ppdev;
char *profile_name;
enum x86_topology_cpu_type cpu_type;
};
/*
@ -159,6 +161,20 @@ enum amd_pstate_mode {
AMD_PSTATE_GUIDED,
AMD_PSTATE_MAX,
};
static inline u8 freq_to_perf(union perf_cached perf, u32 nominal_freq, unsigned int freq_val)
{
u32 perf_val = DIV_ROUND_UP_ULL((u64)freq_val * perf.nominal_perf, nominal_freq);
return (u8)clamp(perf_val, perf.lowest_perf, perf.highest_perf);
}
static inline u32 perf_to_freq(union perf_cached perf, u32 nominal_freq, u8 perf_val)
{
return DIV_ROUND_UP_ULL((u64)nominal_freq * perf_val,
perf.nominal_perf);
}
const char *amd_pstate_get_mode_string(enum amd_pstate_mode mode);
int amd_pstate_get_status(void);
int amd_pstate_update_status(const char *buf, size_t size);