mirror of
https://github.com/torvalds/linux.git
synced 2026-09-22 04:34:03 +02:00
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:
commit
eb49643e66
|
|
@ -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``
|
||||
======================================
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user