amd-pstate 7.3 content (07/30/26)

* Changes for dynamic EPP
 * Adjustments to the bios min perf feature
 * Fixes to kernel doc
 -----BEGIN PGP SIGNATURE-----
 
 iQJHBAABCgAxFiEECwtuSU6dXvs5GA2aLRkspiR3AnYFAmprwUITHHN1cGVybTFA
 a2VybmVsLm9yZwAKCRAtGSymJHcCdobfD/9dkDCf6Go6vAt31Z1PnnJfPKtl7+2n
 CzwKu5+Fx+/4nsJ/MiiDtQ5gJ6q9Pn0NA05vm6fuLhXznzAoWYExi+b3YRNWttyh
 0om7PZfrLWS6oCYl+DC/zu6cL8TBZJweqAEYcNBxRKAKXP5Icgpefrpj+lOW5asr
 YSl9b1cU2NboUksfWqfRGDmDCwKMKsBuZGlEMV8DgpnVJo0A2t8UmXCesUmd+hzP
 LOupRKZlVUkYHeHOGuRnwSkKMoSyzcJjW7oCbkZfLOTjM2KXZ3AEadyNG257H4U5
 PqGSA56EkDqycBuQzZKE6Ayz/mCWmNrB+jd/RjViARVNkUdNy+S8qafVuHEwG0RB
 L5rWef76BKvhK1dAVZ512cidWEfp4Rw2iDaF7o4LUDysCmThAI2VZ+e6uxs6We4R
 2Qe8l6KIxXt9bmf704q9jo62DKzdIve9PV5Y/WRcBft3NqvNmIu/VReIm29LqdDq
 3cYWODH3Lg0kVwaB1jY5n8XAEThhEnB1T4iVWJyfeAX+F4WuSRslM/8sJgdZG3uf
 kIkCbzMa3z5y7cASQCKdfQDzaxf6Vw7oX8c30UzZE5+PYcTuWuLQl6fclddXj1W7
 TEYG1NskvtgETTLA1QXb5JTJYNcGHvUwd+Ng5O7robQ7QSaUGvIw4uHUsLQXG9DI
 JvjxN/NNdJTRQg==
 =8o8O
 -----END PGP SIGNATURE-----

Merge tag 'amd-pstate-v7.3-2026-07-30' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/superm1/linux

Pull amd-pstate 7.3 content (07/30/26) from Mario Limonciello:

"* Changes for dynamic EPP
 * Adjustments to the bios min perf feature
 * Fixes to kernel doc"

* tag 'amd-pstate-v7.3-2026-07-30' of ssh://gitolite.kernel.org/pub/scm/linux/kernel/git/superm1/linux:
  cpufreq/amd-pstate: Document missing kernel-doc members
  cpufreq/amd-pstate-ut: Add unit test for CPPC Performance Priority
  cpufreq/amd-pstate-ut: Add unit test for "dynamic" EPP mode
  cpufreq/amd-pstate: Reduce the scope of exported symbols
  Documentation/amd-pstate: Update dynamic_epp documentation with new behavior
  cpufreq/amd-pstate: Remove "amd_dynamic_epp" cmdline and "dynamic_epp" sysfs
  cpufreq/amd-pstate: Add dynamic EPP as an "energy_performance_preference" mode
  cpufreq/amd-pstate: Extract platform profile to EPP conversion into a helper
  cpufreq/amd-pstate: Remove the defensive check for bios_min_perf
  cpufreq/amd-pstate: Set min_limit_freq based on bios_min_perf
This commit is contained in:
Rafael J. Wysocki 2026-07-31 19:36:04 +02:00
commit eb49643e66
4 changed files with 262 additions and 168 deletions

View File

@ -317,7 +317,9 @@ These profiles represent different hints that are provided
to the low-level firmware about the user's desired energy vs efficiency
tradeoff. ``default`` represents the epp value is set by platform
firmware. ``custom`` designates that integer values 0-255 may be written
as well. This attribute is read-only.
as well. ``dynamic`` designates that the EPP is modified dynamically
on kernel events. See ``Dynamic energy performance profile`` section below to
know more about the ``dynamic`` mode. This attribute is read-only.
``energy_performance_preference``
@ -326,13 +328,11 @@ and user can change current preference according to energy or performance needs
Coarse named profiles are available in the attribute
``energy_performance_available_preferences``.
Users can also write individual integer values between 0 to 255.
When dynamic EPP is enabled, writes to energy_performance_preference are blocked
even when EPP feature is enabled by platform firmware. Lower epp values shift the bias
towards improved performance while a higher epp value shifts the bias towards
power-savings. The exact impact can change from one platform to the other.
If a valid integer was last written, then a number will be returned on future reads.
If a valid string was last written then a string will be returned on future reads.
This attribute is read-write.
Lower epp values shift the bias towards improved performance while a higher epp
value shifts the bias towards power-savings. The exact impact can change from
one platform to the other. If a valid integer was last written, then a number
will be returned on future reads. If a valid string was last written then a
string will be returned on future reads. This attribute is read-write.
``boost``
The `boost` sysfs attribute provides control over the CPU core
@ -356,21 +356,20 @@ Other performance and frequency values can be read back from
Dynamic energy performance profile
==================================
The amd-pstate driver supports dynamically selecting the energy performance
profile based on whether the machine is running on AC or DC power.
profile based on the system profile and the current power source in active mode.
Whether this behavior is enabled by default depends on the kernel command line option
``amd_dynamic_epp`` is set. This behavior can also be overridden
at runtime by the sysfs file ``/sys/devices/system/cpu/amd_pstate/dynamic_epp``.
The ``dynamic`` mode is listed in
``/sys/devices/system/cpu/cpuX/cpufreq/energy_performance_available_preferences``
when available while running under the ``powersave`` governor. The ``dynamic``
mode can be toggled on by writing the same to the sysfs file
``/sys/devices/system/cpu/cpuX/cpufreq/energy_performance_preference`` when
available.
When set to enabled, the driver will select a different energy performance
profile when the machine is running on battery or AC power. The driver will
also register with the platform profile handler to receive notifications of
user desired power state and react to those.
When set to disabled, the driver will not change the energy performance profile
based on the power source and will not react to user desired power state.
Attempting to manually write to the ``energy_performance_preference`` sysfs
file will fail when ``dynamic_epp`` is enabled.
When ``energy_performance_preference`` is set to ``dynamic``, the driver will
select a different energy performance profile when the machine is running on
battery or AC power. The driver will also register with the platform profile
handler to receive notifications of user desired power state and react to
those.
``amd-pstate`` vs ``acpi-cpufreq``
======================================

View File

@ -59,6 +59,7 @@ static int amd_pstate_ut_check_freq(u32 index);
static int amd_pstate_ut_epp(u32 index);
static int amd_pstate_ut_check_driver(u32 index);
static int amd_pstate_ut_check_freq_attrs(u32 index);
static int amd_pstate_ut_check_floor_freq(u32 index);
static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = {
{"amd_pstate_ut_acpi_cpc_valid", amd_pstate_ut_acpi_cpc_valid },
@ -68,6 +69,7 @@ static struct amd_pstate_ut_struct amd_pstate_ut_cases[] = {
{"amd_pstate_ut_epp", amd_pstate_ut_epp },
{"amd_pstate_ut_check_driver", amd_pstate_ut_check_driver },
{"amd_pstate_ut_check_freq_attrs", amd_pstate_ut_check_freq_attrs },
{"amd_pstate_ut_check_floor_freq", amd_pstate_ut_check_floor_freq },
};
static bool test_in_list(const char *list, const char *name)
@ -275,6 +277,7 @@ static int amd_pstate_set_mode(enum amd_pstate_mode mode)
static int amd_pstate_ut_epp(u32 index)
{
static const char * const epp_strings[] = {
"dynamic",
"power",
"balance_power",
"balance_performance",
@ -282,10 +285,10 @@ static int amd_pstate_ut_epp(u32 index)
};
char *buf __free(cleanup_page) = NULL;
struct cpufreq_policy *policy = NULL;
unsigned long orig_dynamic_epp = 0;
enum amd_pstate_mode orig_mode;
struct amd_cpudata *cpudata;
unsigned long orig_policy;
bool orig_dynamic_epp;
int ret, cpu = 0;
u16 epp;
int i;
@ -294,9 +297,11 @@ static int amd_pstate_ut_epp(u32 index)
if (!policy)
return -ENODEV;
cpudata = policy->driver_data;
orig_mode = amd_pstate_get_status();
orig_dynamic_epp = cpudata->dynamic_epp;
if (policy->driver_data) {
cpudata = policy->driver_data;
orig_dynamic_epp = cpudata->dynamic_epp;
}
/* Drop reference before potential driver change. */
cpufreq_cpu_put(policy);
@ -321,16 +326,6 @@ static int amd_pstate_ut_epp(u32 index)
orig_policy = cpudata->policy;
cpudata->policy = CPUFREQ_POLICY_POWERSAVE;
/*
* Disable dynamic EPP before running test. If "orig_dynamic_epp" is
* true, the driver will do a redundant switch at the end and there
* is no need for enabling it again at the end of the test.
*/
if (cpudata->dynamic_epp) {
pr_debug("Dynamic EPP is enabled, disabling it\n");
amd_pstate_clear_dynamic_epp(policy);
}
for (epp = 0; epp <= U8_MAX; epp++) {
u8 val;
@ -367,6 +362,11 @@ static int amd_pstate_ut_epp(u32 index)
if (ret < 0)
goto out;
strreplace(buf, '\n', '\0');
/*
* "dynamic" mode reports the EPP as "dynamic(profile:X)"
* Trim at "(" and just compare tie the epp string.
*/
strreplace(buf, '(', '\0');
if (strcmp(buf, epp_strings[i])) {
pr_err("String EPP value mismatch: %s != %s\n", buf, epp_strings[i]);
@ -380,18 +380,23 @@ static int amd_pstate_ut_epp(u32 index)
out:
if (policy) {
cpudata->policy = orig_policy;
/*
* If the driver had enabled dynamic_epp to begin with,
* restore it here before dropping policy reference.
*/
if (orig_dynamic_epp) {
int ret2;
ret2 = store_energy_performance_preference(policy,
epp_strings[0],
strlen(epp_strings[0]));
if (!ret && (ret2 < 0))
ret = ret2;
}
up_write(&policy->rwsem);
cpufreq_cpu_put(policy);
}
if (orig_dynamic_epp) {
int ret2;
ret2 = amd_pstate_set_mode(AMD_PSTATE_DISABLE);
if (!ret && ret2)
ret = ret2;
}
if (orig_mode != amd_pstate_get_status()) {
int ret2;
@ -557,6 +562,80 @@ static int amd_pstate_ut_check_freq_attrs(u32 index)
return ret;
}
static int amd_pstate_ut_check_floor_freq(u32 index)
{
struct cpufreq_policy *policy __free(put_cpufreq_policy) = NULL;
char *buf __free(cleanup_page) = NULL;
unsigned int orig_floor_freq;
unsigned int floor_freq;
int ret, cpu = 0;
if (!cpu_feature_enabled(X86_FEATURE_CPPC_PERF_PRIO))
return -EOPNOTSUPP;
policy = cpufreq_cpu_get(cpu);
if (!policy)
return -ENODEV;
buf = (char *)__get_free_page(GFP_KERNEL);
if (!buf)
return -ENOMEM;
guard(rwsem_write)(&policy->rwsem);
if (!policy->driver_data)
return -ENODEV;
/* Retrieve original floor frequency */
memset(buf, 0, PAGE_SIZE);
ret = show_amd_pstate_floor_freq(policy, buf);
if (ret < 0)
return ret;
ret = kstrtou32(buf, 0, &orig_floor_freq);
if (ret)
return ret;
memset(buf, 0, PAGE_SIZE);
snprintf(buf, PAGE_SIZE, "%u", policy->cpuinfo.min_freq);
/* Set floor frequency to cpuinfo.min_freq */
ret = store_amd_pstate_floor_freq(policy, buf, strlen(buf));
if (ret < 0) {
pr_err("Failed to set floor frequency to %s\n", buf);
return ret;
}
memset(buf, 0, PAGE_SIZE);
ret = show_amd_pstate_floor_freq(policy, buf);
if (ret < 0)
return ret;
strreplace(buf, '\n', '\0');
ret = kstrtou32(buf, 0, &floor_freq);
if (ret)
return ret;
/* Confirm sysfs reflects the change correctly. */
if (floor_freq != policy->cpuinfo.min_freq) {
pr_err("Floor frequency value mismatch: %u != %u\n",
floor_freq, policy->cpuinfo.min_freq);
return -EINVAL;
}
memset(buf, 0, PAGE_SIZE);
snprintf(buf, PAGE_SIZE, "%u", orig_floor_freq);
/* Restore the original value. */
ret = store_amd_pstate_floor_freq(policy, buf, strlen(buf));
if (ret < 0) {
pr_err("Failed to restore floor frequency to %s\n", buf);
return ret;
}
return 0;
}
static int __init amd_pstate_ut_init(void)
{
u32 i = 0, arr_size = ARRAY_SIZE(amd_pstate_ut_cases);
@ -575,10 +654,16 @@ static int __init amd_pstate_ut_init(void)
ret = amd_pstate_ut_cases[i].func(i);
if (ret)
if (ret) {
/* Platform does not support the feature being tested. */
if (ret == -EOPNOTSUPP) {
pr_err("%-4d %-20s\t skipped!\n", i+1, amd_pstate_ut_cases[i].name);
continue;
}
pr_err("%-4d %-20s\t fail: %d!\n", i+1, amd_pstate_ut_cases[i].name, ret);
else
} else {
pr_info("%-4d %-20s\t success!\n", i+1, amd_pstate_ut_cases[i].name);
}
}
return 0;

View File

@ -75,7 +75,7 @@ const char *amd_pstate_get_mode_string(enum amd_pstate_mode mode)
mode = AMD_PSTATE_UNDEFINED;
return amd_pstate_mode_string[mode];
}
EXPORT_SYMBOL_GPL(amd_pstate_get_mode_string);
EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_get_mode_string);
struct quirk_entry {
u32 nominal_freq;
@ -87,7 +87,6 @@ static struct cpufreq_driver amd_pstate_driver;
static struct cpufreq_driver amd_pstate_epp_driver;
static int cppc_state = AMD_PSTATE_UNDEFINED;
static bool amd_pstate_prefcore = true;
static bool dynamic_epp;
static struct quirk_entry *quirks;
/*
@ -106,6 +105,7 @@ static struct quirk_entry *quirks;
* 3 balance_power
* 4 power
* 5 custom (for raw EPP values)
* 6 dynamic (platform profile driven selection)
*/
enum energy_perf_value_index {
EPP_INDEX_DEFAULT = 0,
@ -114,6 +114,7 @@ enum energy_perf_value_index {
EPP_INDEX_BALANCE_POWERSAVE,
EPP_INDEX_POWERSAVE,
EPP_INDEX_CUSTOM,
EPP_INDEX_DYNAMIC,
EPP_INDEX_MAX,
};
@ -124,6 +125,7 @@ static const char * const energy_perf_strings[] = {
[EPP_INDEX_BALANCE_POWERSAVE] = "balance_power",
[EPP_INDEX_POWERSAVE] = "power",
[EPP_INDEX_CUSTOM] = "custom",
[EPP_INDEX_DYNAMIC] = "dynamic",
};
static_assert(ARRAY_SIZE(energy_perf_strings) == EPP_INDEX_MAX);
@ -134,7 +136,7 @@ static unsigned int epp_values[] = {
[EPP_INDEX_BALANCE_POWERSAVE] = AMD_CPPC_EPP_BALANCE_POWERSAVE,
[EPP_INDEX_POWERSAVE] = AMD_CPPC_EPP_POWERSAVE,
};
static_assert(ARRAY_SIZE(epp_values) == EPP_INDEX_MAX - 1);
static_assert(ARRAY_SIZE(epp_values) == EPP_INDEX_MAX - 2);
typedef int (*cppc_mode_transition_fn)(int);
@ -462,7 +464,6 @@ static int msr_init_perf(struct amd_cpudata *cpudata)
{
union perf_cached perf = READ_ONCE(cpudata->perf);
u64 cap1, numerator, cppc_req;
u8 min_perf;
int ret = rdmsrq_safe_on_cpu(cpudata->cpu, MSR_AMD_CPPC_CAP1,
&cap1);
@ -478,16 +479,6 @@ static int msr_init_perf(struct amd_cpudata *cpudata)
return ret;
WRITE_ONCE(cpudata->cppc_req_cached, cppc_req);
min_perf = FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cppc_req);
/*
* Clear out the min_perf part to check if the rest of the MSR is 0, if yes, this is an
* indication that the min_perf value is the one specified through the BIOS option
*/
cppc_req &= ~(AMD_CPPC_MIN_PERF_MASK);
if (!cppc_req)
perf.bios_min_perf = min_perf;
perf.highest_perf = numerator;
perf.max_limit_perf = numerator;
@ -495,6 +486,7 @@ static int msr_init_perf(struct amd_cpudata *cpudata)
perf.nominal_perf = FIELD_GET(AMD_CPPC_NOMINAL_PERF_MASK, cap1);
perf.lowest_nonlinear_perf = FIELD_GET(AMD_CPPC_LOWNONLIN_PERF_MASK, cap1);
perf.lowest_perf = FIELD_GET(AMD_CPPC_LOWEST_PERF_MASK, cap1);
perf.bios_min_perf = FIELD_GET(AMD_CPPC_MIN_PERF_MASK, cppc_req);
WRITE_ONCE(cpudata->perf, perf);
WRITE_ONCE(cpudata->prefcore_ranking, FIELD_GET(AMD_CPPC_HIGHEST_PERF_MASK, cap1));
WRITE_ONCE(cpudata->floor_perf_cnt, FIELD_GET(AMD_CPPC_FLOOR_PERF_CNT_MASK, cap1));
@ -699,9 +691,12 @@ static void amd_pstate_update_min_max_limit(struct cpufreq_policy *policy)
WRITE_ONCE(cpudata->max_limit_freq, policy->max);
if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE) {
u8 min_limit_perf = perf.bios_min_perf ?: perf.nominal_perf;
u32 min_limit_freq;
/*
* For performance policy, set MinPerf to nominal_perf rather than
* highest_perf or lowest_nonlinear_perf.
* For performance policy, set MinPerf to nominal_perf / bios_min_perf
* rather than highest_perf or lowest_nonlinear_perf.
*
* Per commit 0c411b39e4f4c, using highest_perf was observed
* to cause frequency throttling on power-limited platforms, leading to
@ -709,11 +704,18 @@ static void amd_pstate_update_min_max_limit(struct cpufreq_policy *policy)
* performance too much for HPC workloads requiring high frequency
* operation and minimal wakeup latency from idle states.
*
* nominal_perf therefore provides a balance by avoiding throttling
* while still maintaining enough performance for HPC workloads.
* nominal_perf therefore provides a balanced default by avoiding
* throttling while still maintaining enough performance for HPC
* workloads when bios_min_perf is not available.
*
* When bios_min_perf is available, users have profiled their workloads
* to understand the best idling frequency. Use that instead.
*/
perf.min_limit_perf = min(perf.nominal_perf, perf.max_limit_perf);
WRITE_ONCE(cpudata->min_limit_freq, min(cpudata->nominal_freq, cpudata->max_limit_freq));
min_limit_perf = min(min_limit_perf, perf.max_limit_perf);
min_limit_freq = perf_to_freq(perf, cpudata->nominal_freq, min_limit_perf);
perf.min_limit_perf = min_limit_perf;
WRITE_ONCE(cpudata->min_limit_freq, min(min_limit_freq, cpudata->max_limit_freq));
} else {
perf.min_limit_perf = freq_to_perf(perf, cpudata->nominal_freq, policy->min);
WRITE_ONCE(cpudata->min_limit_freq, policy->min);
@ -1035,6 +1037,13 @@ static int amd_pstate_init_freq(struct amd_cpudata *cpudata)
return -EINVAL;
}
if (perf.bios_min_perf) {
u32 bios_min_freq = perf_to_freq(perf, cpudata->nominal_freq, perf.bios_min_perf);
pr_debug("Found Requested CPU Min Frequency of %uKHz on CPU%d\n",
bios_min_freq, cpudata->cpu);
}
return 0;
}
@ -1183,6 +1192,24 @@ static int amd_pstate_power_supply_notifier(struct notifier_block *nb,
return NOTIFY_OK;
}
static int amd_pstate_get_epp_from_platform_profile(struct cpufreq_policy *policy,
enum platform_profile_option profile)
{
switch (profile) {
case PLATFORM_PROFILE_PERFORMANCE:
return AMD_CPPC_EPP_PERFORMANCE;
case PLATFORM_PROFILE_BALANCED:
return amd_pstate_get_balanced_epp(policy);
case PLATFORM_PROFILE_LOW_POWER:
return AMD_CPPC_EPP_POWERSAVE;
default:
break;
}
pr_err("Unknown Platform Profile %d\n", profile);
return -EOPNOTSUPP;
}
static int amd_pstate_profile_probe(void *drvdata, unsigned long *choices)
{
set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
@ -1208,31 +1235,19 @@ static int amd_pstate_profile_set(struct device *dev,
struct amd_cpudata *cpudata = dev_get_drvdata(dev);
struct cpufreq_policy *policy __free(put_cpufreq_policy) = cpufreq_cpu_get(cpudata->cpu);
int ret;
u8 epp;
if (!policy)
return -ENODEV;
switch (profile) {
case PLATFORM_PROFILE_LOW_POWER:
ret = amd_pstate_set_epp(policy, AMD_CPPC_EPP_POWERSAVE);
if (ret)
return ret;
break;
case PLATFORM_PROFILE_BALANCED:
ret = amd_pstate_set_epp(policy,
amd_pstate_get_balanced_epp(policy));
if (ret)
return ret;
break;
case PLATFORM_PROFILE_PERFORMANCE:
ret = amd_pstate_set_epp(policy, AMD_CPPC_EPP_PERFORMANCE);
if (ret)
return ret;
break;
default:
pr_err("Unknown Platform Profile %d\n", profile);
return -EOPNOTSUPP;
}
ret = amd_pstate_get_epp_from_platform_profile(policy, profile);
if (ret < 0)
return ret;
epp = (u8)ret;
ret = amd_pstate_set_epp(policy, epp);
if (ret)
return ret;
cpudata->current_profile = profile;
@ -1258,28 +1273,20 @@ void amd_pstate_clear_dynamic_epp(struct cpufreq_policy *policy)
kfree(cpudata->profile_name);
cpudata->dynamic_epp = false;
}
EXPORT_SYMBOL_GPL(amd_pstate_clear_dynamic_epp);
EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_clear_dynamic_epp);
static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy)
{
struct amd_cpudata *cpudata = policy->driver_data;
u64 prev = READ_ONCE(cpudata->cppc_req_cached);
int ret;
u8 epp;
switch (cpudata->current_profile) {
case PLATFORM_PROFILE_PERFORMANCE:
epp = AMD_CPPC_EPP_PERFORMANCE;
break;
case PLATFORM_PROFILE_LOW_POWER:
epp = AMD_CPPC_EPP_POWERSAVE;
break;
case PLATFORM_PROFILE_BALANCED:
epp = amd_pstate_get_balanced_epp(policy);
break;
default:
pr_err("Unknown Platform Profile %d\n", cpudata->current_profile);
return -EOPNOTSUPP;
}
ret = amd_pstate_get_epp_from_platform_profile(policy, cpudata->current_profile);
if (ret < 0)
return ret;
epp = (u8)ret;
ret = amd_pstate_set_epp(policy, epp);
if (ret)
return ret;
@ -1312,6 +1319,9 @@ static int amd_pstate_set_dynamic_epp(struct cpufreq_policy *policy)
cleanup:
amd_pstate_clear_dynamic_epp(policy);
epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, prev);
/* Restore previous EPP if toggling Dynamic EPP failed. */
amd_pstate_set_epp(policy, epp);
return ret;
}
@ -1382,7 +1392,7 @@ static ssize_t show_amd_pstate_hw_prefcore(struct cpufreq_policy *policy,
static ssize_t show_energy_performance_available_preferences(
struct cpufreq_policy *policy, char *buf)
{
int offset = 0, i;
int i, offset = 0;
struct amd_cpudata *cpudata = policy->driver_data;
if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE)
@ -1405,11 +1415,6 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
bool raw_epp = false;
u8 epp;
if (cpudata->dynamic_epp) {
pr_debug("EPP cannot be set when dynamic EPP is enabled\n");
return -EBUSY;
}
/*
* if the value matches a number, use that, otherwise see if
* matches an index in the energy_perf_strings array
@ -1420,6 +1425,25 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
ret = sysfs_match_string(energy_perf_strings, buf);
if (ret < 0 || ret == EPP_INDEX_CUSTOM)
return -EINVAL;
if (ret == EPP_INDEX_DYNAMIC) {
if (cpudata->policy == CPUFREQ_POLICY_PERFORMANCE)
return -EBUSY;
/*
* Dynamic EPP was already enabled for this CPU.
* Nothing to do.
*/
if (cpudata->dynamic_epp)
return count;
cpudata->current_profile = PLATFORM_PROFILE_BALANCED;
ret = amd_pstate_set_dynamic_epp(policy);
if (ret)
return ret;
return count;
}
if (ret)
epp = epp_values[ret];
else
@ -1431,6 +1455,13 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
return -EBUSY;
}
/*
* Dynamic EPP was enabled previously!
* Switch back to the static EPP mode.
*/
if (cpudata->dynamic_epp)
amd_pstate_clear_dynamic_epp(policy);
ret = amd_pstate_set_epp(policy, epp);
if (ret)
return ret;
@ -1439,7 +1470,7 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
return count;
}
EXPORT_SYMBOL_GPL(store_energy_performance_preference);
EXPORT_SYMBOL_FOR_PSTATE_UT(store_energy_performance_preference);
ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *buf)
{
@ -1448,7 +1479,7 @@ ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *
epp = FIELD_GET(AMD_CPPC_EPP_PERF_MASK, cpudata->cppc_req_cached);
if (cpudata->raw_epp)
if (!cpudata->dynamic_epp && cpudata->raw_epp)
return sysfs_emit(buf, "%u\n", epp);
switch (epp) {
@ -1468,12 +1499,14 @@ ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *
return -EINVAL;
}
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]);
}
EXPORT_SYMBOL_GPL(show_energy_performance_preference);
EXPORT_SYMBOL_FOR_PSTATE_UT(show_energy_performance_preference);
static ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy,
const char *buf, size_t count)
ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy, const char *buf, size_t count)
{
struct amd_cpudata *cpudata = policy->driver_data;
union perf_cached perf = READ_ONCE(cpudata->perf);
@ -1496,13 +1529,15 @@ static ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy,
return ret ?: count;
}
EXPORT_SYMBOL_FOR_PSTATE_UT(store_amd_pstate_floor_freq);
static ssize_t show_amd_pstate_floor_freq(struct cpufreq_policy *policy, char *buf)
ssize_t show_amd_pstate_floor_freq(struct cpufreq_policy *policy, char *buf)
{
struct amd_cpudata *cpudata = policy->driver_data;
return sysfs_emit(buf, "%u\n", cpudata->floor_freq);
}
EXPORT_SYMBOL_FOR_PSTATE_UT(show_amd_pstate_floor_freq);
static ssize_t show_amd_pstate_floor_count(struct cpufreq_policy *policy, char *buf)
{
@ -1570,7 +1605,7 @@ struct freq_attr **amd_pstate_get_current_attrs(void)
return NULL;
return current_pstate_driver->attr;
}
EXPORT_SYMBOL_GPL(amd_pstate_get_current_attrs);
EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_get_current_attrs);
static struct freq_attr **get_freq_attrs(void)
{
@ -1755,7 +1790,7 @@ int amd_pstate_get_status(void)
{
return cppc_state;
}
EXPORT_SYMBOL_GPL(amd_pstate_get_status);
EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_get_status);
int amd_pstate_update_status(const char *buf, size_t size)
{
@ -1775,7 +1810,7 @@ int amd_pstate_update_status(const char *buf, size_t size)
return 0;
}
EXPORT_SYMBOL_GPL(amd_pstate_update_status);
EXPORT_SYMBOL_FOR_PSTATE_UT(amd_pstate_update_status);
static ssize_t status_show(struct device *dev,
struct device_attribute *attr, char *buf)
@ -1803,50 +1838,12 @@ static ssize_t prefcore_show(struct device *dev,
return sysfs_emit(buf, "%s\n", str_enabled_disabled(amd_pstate_prefcore));
}
static ssize_t dynamic_epp_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sysfs_emit(buf, "%s\n", str_enabled_disabled(dynamic_epp));
}
static ssize_t dynamic_epp_store(struct device *a, struct device_attribute *b,
const char *buf, size_t count)
{
bool enabled;
int ret;
ret = kstrtobool(buf, &enabled);
if (ret)
return ret;
guard(mutex)(&amd_pstate_driver_lock);
if (cppc_state != AMD_PSTATE_ACTIVE) {
pr_debug("dynamic_epp can only be toggled in active mode\n");
return -EINVAL;
}
/* Nothing to do */
if (dynamic_epp == enabled)
return count;
/* reinitialize with desired dynamic EPP value */
dynamic_epp = enabled;
ret = amd_pstate_change_driver_mode(cppc_state);
if (ret)
dynamic_epp = false;
return ret ? ret : count;
}
static DEVICE_ATTR_RW(status);
static DEVICE_ATTR_RO(prefcore);
static DEVICE_ATTR_RW(dynamic_epp);
static struct attribute *pstate_global_attributes[] = {
&dev_attr_status.attr,
&dev_attr_prefcore.attr,
&dev_attr_dynamic_epp.attr,
NULL
};
@ -1953,10 +1950,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
cpudata->current_profile = PLATFORM_PROFILE_BALANCED;
}
if (dynamic_epp)
ret = amd_pstate_set_dynamic_epp(policy);
else
ret = amd_pstate_set_epp(policy, cpudata->epp_default_dc);
ret = amd_pstate_set_epp(policy, cpudata->epp_default_dc);
if (ret)
goto free_cpudata1;
@ -2028,6 +2022,18 @@ static int amd_pstate_epp_set_policy(struct cpufreq_policy *policy)
if (!policy->cpuinfo.max_freq)
return -ENODEV;
/* Must be a switch between PERFORMANCE and POWERSAVE */
if (cpudata->policy != policy->policy) {
/*
* Disable dynamic_epp when switching
* out of CPUFREQ_POLICY_POWERSAVE.
*/
if (cpudata->dynamic_epp) {
WARN_ON_ONCE(cpudata->policy != CPUFREQ_POLICY_POWERSAVE);
amd_pstate_clear_dynamic_epp(policy);
}
}
cpudata->policy = policy->policy;
ret = amd_pstate_epp_update_limit(policy, true);
@ -2355,19 +2361,8 @@ static int __init amd_prefcore_param(char *str)
return 0;
}
static int __init amd_dynamic_epp_param(char *str)
{
if (!strcmp(str, "disable"))
dynamic_epp = false;
if (!strcmp(str, "enable"))
dynamic_epp = true;
return 0;
}
early_param("amd_pstate", amd_pstate_param);
early_param("amd_prefcore", amd_prefcore_param);
early_param("amd_dynamic_epp", amd_dynamic_epp_param);
MODULE_AUTHOR("Huang Rui <ray.huang@amd.com>");
MODULE_DESCRIPTION("AMD Processor P-state Frequency Driver");

View File

@ -11,6 +11,13 @@
#include <linux/pm_qos.h>
#include <linux/platform_profile.h>
#if IS_MODULE(CONFIG_X86_AMD_PSTATE_UT)
#define EXPORT_SYMBOL_FOR_PSTATE_UT(symbol) \
EXPORT_SYMBOL_FOR_MODULES(symbol, "amd-pstate-ut")
#else
#define EXPORT_SYMBOL_FOR_PSTATE_UT(symbol)
#endif
/*********************************************************************
* AMD P-state INTERFACE *
*********************************************************************/
@ -32,6 +39,7 @@
* @min_limit_perf: Cached value of the performance corresponding to policy->min
* @max_limit_perf: Cached value of the performance corresponding to policy->max
* @bios_min_perf: Cached perf value corresponding to the "Requested CPU Min Frequency" BIOS option
* @val: Raw 64-bit value for atomic access via READ_ONCE()/WRITE_ONCE()
*/
union perf_cached {
struct {
@ -89,7 +97,12 @@ struct amd_aperf_mperf {
* @epp_default_ac: Default EPP value for AC power source
* @epp_default_dc: Default EPP value for DC power source
* @dynamic_epp: Whether dynamic EPP is enabled
* @raw_epp: Whether the last EPP write was a raw numeric value rather than a
* named preference
* @power_nb: Notifier block for power events
* @current_profile: Currently selected platform profile option
* @ppdev: Device registered with the platform profile handler
* @profile_name: Name under which @ppdev is registered
*
* The amd_cpudata is key private data for each CPU thread in AMD P-State, and
* represents all the attributes and goals that AMD P-State requests at runtime.
@ -153,6 +166,8 @@ ssize_t store_energy_performance_preference(struct cpufreq_policy *policy,
const char *buf, size_t count);
ssize_t show_energy_performance_preference(struct cpufreq_policy *policy, char *buf);
void amd_pstate_clear_dynamic_epp(struct cpufreq_policy *policy);
ssize_t store_amd_pstate_floor_freq(struct cpufreq_policy *policy, const char *buf, size_t count);
ssize_t show_amd_pstate_floor_freq(struct cpufreq_policy *policy, char *buf);
struct freq_attr;