Merge branch 'pm-cpufreq'

Merge cpufreq updates for 7.3-rc1:

 - Minor fixes and cleanups in assorted cpufreq drivers (Dan Carpenter,
   Guru Das Srinagesh, Haoxiang Li, Karl Mehltretter, Sasha Finkelstein,
   and Pan Chuang)

 - Fix cpufreq table creation and bios_limits() callback in the Rust
   bindings (Priya Bala Govindasamy)

 - Add IPQ5210 support to qcom-nvmem driver (Varadarajan Narayanan)

 - Adjust the .adjust_perf() cpufreq driver callback to allow the
   maximum performance value to be passed to drivers and update the
   intel_pstate driver to use it (Rafael Wysocki)

 - Set policy->cur to the actual requested frequency in the intel_pstate
   driver when the performance policy is used (Rafael Wysocki)

 - Simplify HWP handling on Broadwell processors in intel_pstate (Rafael
   Wysocki)

 - Fix setting minimum P-state at init time in intel_pstate (Rafael
   Wysocki)

 - Consolidate frequency values computation in intel_pstate and clean up
   code in that driver (Rafael Wysocki)

 - Add missing kernel-doc desciptions for structure and union members in
   the amd-pstate driver (David Vernet)

 - Handle missing policy in dynamic EPP callbacks in the amd-pstate
   driver (EDAMAMEX)

 - Introduce EXPORT_SYMBOL_FOR_PSTATE_UT() to export amd-pstate driver
   symbols to the amd-pstate-ut subdriver (K Prateek Nayak)

 - Add dynamic EPP as an "energy_performance_preference" mode in
   amd-pstate, remove the "amd_dynamic_epp" kernel command line option
   and the "dynamic_epp" sysfs attribute, and update the dynamic_epp
   documentation accordingly (K Prateek Nayak)

 - Add unit tests for CPPC Performance Priority and the "dynamic" EPP
   mode in the amd-pstate driver (K Prateek Nayak)

 - Set min_limit_freq based on bios_min_perf in amd-pstate and remove
   the defensive check for bios_min_perf from it (K Prateek Nayak)

 - Fix EPP return type and handle errors in amd-pstate during
   initialization, toggle auto_sel in active mode on shared memory
   systems, and cache the firmware programmed EPP value (Marco Scardovi)

 - Skip tests in amd-pstate-ut if the amd-pstate driver is not in active
   use (Qianheng Peng)

 - Replace sprintf() with sysfs_emit() in sysfs show in the cpufreq
   schedutil governor and fix a self-contradictory comment in
   sugov_iowait_apply() (Zhongqiu Han)

 - Fix the usage example for the sampling_rate tunable of the ondemand
   cpufreq governor in admin-guide (wangxiaodong)

* pm-cpufreq: (40 commits)
  cpufreq: imx6q: fix out-of-bounds write when probed more than once
  cpufreq: imx6q: fix devres accumulation across driver rebind
  rust: cpufreq: Fix temporary write in Registration::bios_limit_callback
  rust: cpufreq: Add CPUFREQ_TABLE_END as last table entry in TableBuilder::to_table
  cpufreq: intel_pstate: Adjust policy->cur in active mode to policy
  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
  cpufreq: apple-soc: Calculate frequency as a 64-bit value
  kselftest: cpufreq: Backup and restore governor for sptests
  selftests/cpufreq: Remove unnecessary sudo from quick_shuffle()
  selftests/cpufreq: Remove unused local variables from switch_show_governor()
  cpufreq/amd-pstate: handle missing policy in dynamic EPP callbacks
  ...
This commit is contained in:
Rafael J. Wysocki
2026-08-07 20:48:50 +02:00
19 changed files with 498 additions and 314 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

@@ -497,7 +497,7 @@ This governor exposes the following tunables:
represented by it to be 1.5 times as high as the transition latency
(the default)::
# echo `$(($(cat cpuinfo_transition_latency) * 3 / 2))` > ondemand/sampling_rate
# echo $(($(cat cpuinfo_transition_latency) * 3 / 2)) > ondemand/sampling_rate
``up_threshold``
If the estimated CPU load is above this value (in percent), the governor

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,9 +562,88 @@ 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);
enum amd_pstate_mode mode = amd_pstate_get_status();
/* don't test if no running amd-pstate driver */
if (mode == AMD_PSTATE_UNDEFINED || mode == AMD_PSTATE_DISABLE)
return -EOPNOTSUPP;
for (i = 0; i < arr_size; i++) {
int ret;
@@ -570,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);
@@ -199,7 +201,7 @@ static inline int get_mode_idx_from_str(const char *str, size_t size)
static DEFINE_MUTEX(amd_pstate_driver_lock);
static u8 msr_get_epp(struct amd_cpudata *cpudata)
static int msr_get_epp(struct amd_cpudata *cpudata)
{
u64 value;
int ret;
@@ -215,12 +217,12 @@ static u8 msr_get_epp(struct amd_cpudata *cpudata)
DEFINE_STATIC_CALL(amd_pstate_get_epp, msr_get_epp);
static inline s16 amd_pstate_get_epp(struct amd_cpudata *cpudata)
static inline int amd_pstate_get_epp(struct amd_cpudata *cpudata)
{
return static_call(amd_pstate_get_epp)(cpudata);
}
static u8 shmem_get_epp(struct amd_cpudata *cpudata)
static int shmem_get_epp(struct amd_cpudata *cpudata)
{
u64 epp;
int ret;
@@ -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));
@@ -526,9 +518,6 @@ static int shmem_init_perf(struct amd_cpudata *cpudata)
WRITE_ONCE(cpudata->perf, perf);
WRITE_ONCE(cpudata->prefcore_ranking, cppc_perf.highest_perf);
if (cppc_state == AMD_PSTATE_ACTIVE)
return 0;
ret = cppc_get_auto_sel(cpudata->cpu, &auto_sel);
if (ret) {
pr_warn("failed to get auto_sel, ret: %d\n", ret);
@@ -702,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
@@ -712,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);
@@ -782,6 +781,7 @@ static unsigned int amd_pstate_fast_switch(struct cpufreq_policy *policy,
static void amd_pstate_adjust_perf(struct cpufreq_policy *policy,
unsigned long _min_perf,
unsigned long target_perf,
unsigned long _max_perf,
unsigned long capacity)
{
u8 max_perf, min_perf, des_perf, cap_perf;
@@ -1037,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;
}
@@ -1173,6 +1180,9 @@ static int amd_pstate_power_supply_notifier(struct notifier_block *nb,
if (cpudata->current_profile != PLATFORM_PROFILE_BALANCED)
return 0;
if (!policy)
return NOTIFY_OK;
epp = amd_pstate_get_balanced_epp(policy);
ret = amd_pstate_set_epp(policy, epp);
@@ -1182,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);
@@ -1207,28 +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;
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;
}
if (!policy)
return -ENODEV;
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;
@@ -1254,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;
@@ -1308,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;
}
@@ -1378,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)
@@ -1401,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
@@ -1416,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
@@ -1427,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;
@@ -1435,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)
{
@@ -1444,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) {
@@ -1464,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);
@@ -1492,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)
{
@@ -1566,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)
{
@@ -1751,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)
{
@@ -1771,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)
@@ -1799,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
};
@@ -1876,6 +1877,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
struct amd_cpudata *cpudata;
union perf_cached perf;
struct device *dev;
int default_epp;
int ret;
/*
@@ -1924,6 +1926,14 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
policy->boost_supported = READ_ONCE(cpudata->boost_supported);
/* Cache the firmware programmed EPP */
default_epp = amd_pstate_get_epp(cpudata);
if (default_epp < 0) {
ret = default_epp;
goto free_cpudata1;
}
FIELD_MODIFY(AMD_CPPC_EPP_PERF_MASK, &cpudata->cppc_req_cached, default_epp);
/*
* Set the policy to provide a valid fallback value in case
* the default cpufreq governor is neither powersave nor performance.
@@ -1931,7 +1941,7 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy)
if (amd_pstate_acpi_pm_profile_server() ||
amd_pstate_acpi_pm_profile_undefined()) {
policy->policy = CPUFREQ_POLICY_PERFORMANCE;
cpudata->epp_default_ac = cpudata->epp_default_dc = amd_pstate_get_epp(cpudata);
cpudata->epp_default_ac = cpudata->epp_default_dc = default_epp;
cpudata->current_profile = PLATFORM_PROFILE_PERFORMANCE;
} else {
policy->policy = CPUFREQ_POLICY_POWERSAVE;
@@ -1940,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;
@@ -2015,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);
@@ -2342,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;

View File

@@ -249,21 +249,19 @@ static int apple_soc_cpufreq_init(struct cpufreq_policy *policy)
return -ENODEV;
}
ret = dev_pm_opp_of_add_table(cpu_dev);
if (ret < 0) {
dev_err(cpu_dev, "%s: failed to add OPP table: %d\n", __func__, ret);
return ret;
}
priv = kzalloc_obj(*priv);
if (!priv)
return -ENOMEM;
ret = apple_soc_cpufreq_find_cluster(policy, &reg_base, &info);
if (ret) {
dev_err(cpu_dev, "%s: failed to get cluster info: %d\n", __func__, ret);
return ret;
goto out_free_priv;
}
ret = dev_pm_opp_set_sharing_cpus(cpu_dev, policy->cpus);
if (ret) {
dev_err(cpu_dev, "%s: failed to mark OPPs as shared: %d\n", __func__, ret);
ret = dev_pm_opp_of_cpumask_add_table(policy->cpus);
if (ret < 0) {
dev_err(cpu_dev, "%s: failed to add OPP table: %d\n", __func__, ret);
goto out_iounmap;
}
@@ -271,24 +269,18 @@ static int apple_soc_cpufreq_init(struct cpufreq_policy *policy)
if (ret <= 0) {
dev_dbg(cpu_dev, "OPP table is not ready, deferring probe\n");
ret = -EPROBE_DEFER;
goto out_free_opp;
}
priv = kzalloc_obj(*priv);
if (!priv) {
ret = -ENOMEM;
goto out_free_opp;
goto out_free_table;
}
ret = dev_pm_opp_init_cpufreq_table(cpu_dev, &freq_table);
if (ret) {
dev_err(cpu_dev, "failed to init cpufreq table: %d\n", ret);
goto out_free_priv;
goto out_free_table;
}
/* Get OPP levels (p-state indexes) and stash them in driver_data */
for (i = 0; freq_table[i].frequency != CPUFREQ_TABLE_END; i++) {
unsigned long rate = freq_table[i].frequency * 1000 + 999;
unsigned long rate = freq_table[i].frequency * 1000UL + 999;
struct dev_pm_opp *opp = dev_pm_opp_find_freq_floor(cpu_dev, &rate);
if (IS_ERR(opp)) {
@@ -318,12 +310,12 @@ static int apple_soc_cpufreq_init(struct cpufreq_policy *policy)
out_free_cpufreq_table:
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
out_free_priv:
kfree(priv);
out_free_opp:
dev_pm_opp_remove_all_dynamic(cpu_dev);
out_free_table:
dev_pm_opp_of_cpumask_remove_table(policy->cpus);
out_iounmap:
iounmap(reg_base);
out_free_priv:
kfree(priv);
return ret;
}
@@ -332,7 +324,7 @@ static void apple_soc_cpufreq_exit(struct cpufreq_policy *policy)
struct apple_cpu_priv *priv = policy->driver_data;
dev_pm_opp_free_cpufreq_table(priv->cpu_dev, &policy->freq_table);
dev_pm_opp_remove_all_dynamic(priv->cpu_dev);
dev_pm_opp_of_cpumask_remove_table(policy->cpus);
iounmap(priv->reg_base);
kfree(priv);
}

View File

@@ -584,11 +584,8 @@ static int brcm_avs_prepare_init(struct platform_device *pdev)
ret = devm_request_irq(dev, priv->host_irq, irq_handler,
IRQF_TRIGGER_RISING,
BRCM_AVS_HOST_INTR, priv);
if (ret && priv->host_irq >= 0) {
dev_err(dev, "IRQ request failed: %s (%d) -- %d\n",
BRCM_AVS_HOST_INTR, priv->host_irq, ret);
if (ret && priv->host_irq >= 0)
goto unmap_intr_base;
}
if (brcm_avs_is_firmware_loaded(priv))
return 0;

View File

@@ -200,6 +200,7 @@ static const struct of_device_id blocklist[] __initconst = {
{ .compatible = "ti,am62l3", },
{ .compatible = "ti,am62p5", },
{ .compatible = "qcom,ipq5210", },
{ .compatible = "qcom,ipq5332", },
{ .compatible = "qcom,ipq5424", },
{ .compatible = "qcom,ipq6018", },

View File

@@ -2225,14 +2225,17 @@ EXPORT_SYMBOL_GPL(cpufreq_driver_fast_switch);
* @policy: cpufreq policy object of the target CPU.
* @min_perf: Minimum (required) performance level (units of @capacity).
* @target_perf: Target (desired) performance level (units of @capacity).
* @max_perf: Maximum (allowed) performance level (units of @capacity).
* @capacity: Capacity of the target CPU.
*
* Carry out a fast performance level switch of @cpu without sleeping.
* Carry out a fast performance level adjustment for the CPU represented by
* @policy without sleeping.
*
* The driver's ->adjust_perf() callback invoked by this function must be
* suitable for being called from within RCU-sched read-side critical sections
* and it is expected to select a suitable performance level equal to or above
* @min_perf and preferably equal to or below @target_perf.
* suitable for calling from within RCU-sched read-side critical sections and
* it is expected to program the processor to select suitable performance
* levels between @min_perf and @max_perf inclusive and preferably close to
* @target_perf going forward for the CPU represented by @policy.
*
* This function must not be called if policy->fast_switch_enabled is unset.
*
@@ -2244,9 +2247,10 @@ EXPORT_SYMBOL_GPL(cpufreq_driver_fast_switch);
void cpufreq_driver_adjust_perf(struct cpufreq_policy *policy,
unsigned long min_perf,
unsigned long target_perf,
unsigned long max_perf,
unsigned long capacity)
{
cpufreq_driver->adjust_perf(policy, min_perf, target_perf, capacity);
cpufreq_driver->adjust_perf(policy, min_perf, target_perf, max_perf, capacity);
}
/**

View File

@@ -55,7 +55,6 @@ static unsigned int max_freq;
static unsigned int transition_latency;
static u32 *imx6_soc_volt;
static u32 soc_opp_count;
static int imx6q_set_target(struct cpufreq_policy *policy, unsigned int index)
{
@@ -330,6 +329,7 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
const struct property *prop;
const __be32 *val;
u32 nr, i, j;
u32 soc_opp_count = 0;
cpu_dev = get_cpu_device(0);
if (!cpu_dev) {
@@ -400,7 +400,7 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
}
/* Make imx6_soc_volt array's size same as arm opp number */
imx6_soc_volt = devm_kcalloc(cpu_dev, num, sizeof(*imx6_soc_volt),
imx6_soc_volt = devm_kcalloc(&pdev->dev, num, sizeof(*imx6_soc_volt),
GFP_KERNEL);
if (imx6_soc_volt == NULL) {
ret = -ENOMEM;
@@ -485,6 +485,7 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
return 0;
free_freq_table:
imx6_soc_volt = NULL;
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
out_free_opp:
dev_pm_opp_of_remove_table(cpu_dev);
@@ -506,6 +507,7 @@ static int imx6q_cpufreq_probe(struct platform_device *pdev)
static void imx6q_cpufreq_remove(struct platform_device *pdev)
{
cpufreq_unregister_driver(&imx6q_cpufreq_driver);
imx6_soc_volt = NULL;
dev_pm_opp_free_cpufreq_table(cpu_dev, &freq_table);
dev_pm_opp_of_remove_table(cpu_dev);
regulator_put(arm_reg);

View File

@@ -299,7 +299,6 @@ struct pstate_funcs {
static struct pstate_funcs pstate_funcs __read_mostly;
static bool hwp_active __ro_after_init;
static int hwp_mode_bdw __ro_after_init;
static bool per_cpu_limits __ro_after_init;
static bool hwp_forced __ro_after_init;
static bool hwp_boost __read_mostly;
@@ -587,21 +586,14 @@ static void intel_pstate_hybrid_hwp_adjust(struct cpudata *cpu)
hwp_is_hybrid = true;
cpu->pstate.turbo_freq = rounddown(cpu->pstate.turbo_pstate * scaling,
perf_ctl_scaling);
cpu->pstate.max_freq = rounddown(cpu->pstate.max_pstate * scaling,
perf_ctl_scaling);
freq = perf_ctl_max_phys * perf_ctl_scaling;
cpu->pstate.max_pstate_physical = intel_pstate_freq_to_hwp(cpu, freq);
freq = cpu->pstate.min_pstate * perf_ctl_scaling;
cpu->pstate.min_freq = freq;
/*
* Cast the min P-state value retrieved via pstate_funcs.get_min() to
* the effective range of HWP performance levels.
*/
cpu->pstate.min_pstate = intel_pstate_freq_to_hwp(cpu, freq);
cpu->pstate.min_pstate = intel_pstate_freq_to_hwp(cpu, cpu->pstate.min_freq);
}
static bool turbo_is_disabled(void)
@@ -979,12 +971,10 @@ static int hybrid_get_cost(struct device *dev, unsigned long freq,
* capacity. Similarly, P-cores start to be populated when E-cores are
* utilized above 60% of the capacity.
*/
if (hybrid_get_cpu_type(dev->id) == INTEL_CPU_TYPE_ATOM) {
if (hybrid_has_l3(dev->id)) /* E-core */
*cost += 1;
} else { /* P-core */
if (hybrid_get_cpu_type(dev->id) == INTEL_CPU_TYPE_CORE) /* P-core */
*cost += 2;
}
else if (hybrid_has_l3(dev->id)) /* E-core */
*cost += 1;
return 0;
}
@@ -1185,6 +1175,22 @@ static bool hybrid_clear_max_perf_cpu(void)
return ret;
}
static void intel_pstate_update_freq_limits(struct cpudata *cpu)
{
int scaling = cpu->pstate.scaling;
unsigned int turbo_freq = cpu->pstate.turbo_pstate * scaling;
unsigned int max_freq = cpu->pstate.max_pstate * scaling;
int perf_ctl_scaling = cpu->pstate.perf_ctl_scaling;
if (scaling != perf_ctl_scaling) {
turbo_freq = rounddown(turbo_freq, perf_ctl_scaling);
max_freq = rounddown(max_freq, perf_ctl_scaling);
}
cpu->pstate.turbo_freq = turbo_freq;
cpu->pstate.max_freq = max_freq;
}
static void __intel_pstate_get_hwp_cap(struct cpudata *cpu)
{
u64 cap;
@@ -1197,20 +1203,8 @@ static void __intel_pstate_get_hwp_cap(struct cpudata *cpu)
static void intel_pstate_get_hwp_cap(struct cpudata *cpu)
{
int scaling = cpu->pstate.scaling;
__intel_pstate_get_hwp_cap(cpu);
cpu->pstate.max_freq = cpu->pstate.max_pstate * scaling;
cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * scaling;
if (scaling != cpu->pstate.perf_ctl_scaling) {
int perf_ctl_scaling = cpu->pstate.perf_ctl_scaling;
cpu->pstate.max_freq = rounddown(cpu->pstate.max_freq,
perf_ctl_scaling);
cpu->pstate.turbo_freq = rounddown(cpu->pstate.turbo_freq,
perf_ctl_scaling);
}
intel_pstate_update_freq_limits(cpu);
}
static void hybrid_update_capacity(struct cpudata *cpu)
@@ -2299,33 +2293,16 @@ static int hwp_get_cpu_scaling(int cpu)
return intel_pstate_cppc_get_scaling(cpu);
}
static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate)
{
trace_cpu_frequency(pstate * cpu->pstate.scaling, cpu->cpu);
cpu->pstate.current_pstate = pstate;
/*
* Generally, there is no guarantee that this code will always run on
* the CPU being updated, so force the register update to run on the
* right CPU.
*/
wrmsrq_on_cpu(cpu->cpu, MSR_IA32_PERF_CTL,
pstate_funcs.get_val(cpu, pstate));
}
static void intel_pstate_set_min_pstate(struct cpudata *cpu)
{
intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate);
}
static void intel_pstate_get_cpu_pstates(struct cpudata *cpu)
{
int perf_ctl_scaling = pstate_funcs.get_scaling();
cpu->pstate.max_pstate_physical = pstate_funcs.get_max_physical(cpu->cpu);
cpu->pstate.min_pstate = pstate_funcs.get_min(cpu->cpu);
cpu->pstate.min_freq = cpu->pstate.min_pstate * perf_ctl_scaling;
cpu->pstate.perf_ctl_scaling = perf_ctl_scaling;
if (hwp_active && !hwp_mode_bdw) {
if (hwp_active) {
__intel_pstate_get_hwp_cap(cpu);
if (pstate_funcs.get_cpu_scaling) {
@@ -2345,19 +2322,13 @@ static void intel_pstate_get_cpu_pstates(struct cpudata *cpu)
cpu->pstate.turbo_pstate = pstate_funcs.get_turbo(cpu->cpu);
}
if (cpu->pstate.scaling == perf_ctl_scaling) {
cpu->pstate.min_freq = cpu->pstate.min_pstate * perf_ctl_scaling;
cpu->pstate.max_freq = cpu->pstate.max_pstate * perf_ctl_scaling;
cpu->pstate.turbo_freq = cpu->pstate.turbo_pstate * perf_ctl_scaling;
}
intel_pstate_update_freq_limits(cpu);
if (pstate_funcs.get_aperf_mperf_shift)
cpu->aperf_mperf_shift = pstate_funcs.get_aperf_mperf_shift();
if (pstate_funcs.get_vid)
pstate_funcs.get_vid(cpu);
intel_pstate_set_min_pstate(cpu);
}
/*
@@ -2884,8 +2855,22 @@ static void intel_pstate_update_perf_limits(struct cpudata *cpu,
cpu->min_perf_ratio);
}
static void intel_pstate_set_pstate(struct cpudata *cpu, int pstate)
{
trace_cpu_frequency(pstate * cpu->pstate.scaling, cpu->cpu);
cpu->pstate.current_pstate = pstate;
/*
* Generally, there is no guarantee that this code will always run on
* the CPU being updated, so force the register update to run on the
* right CPU.
*/
wrmsrq_on_cpu(cpu->cpu, MSR_IA32_PERF_CTL,
pstate_funcs.get_val(cpu, pstate));
}
static int intel_pstate_set_policy(struct cpufreq_policy *policy)
{
unsigned int freq = policy->min;
struct cpudata *cpu;
if (!policy->cpuinfo.max_freq)
@@ -2901,7 +2886,23 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy)
intel_pstate_update_perf_limits(cpu, policy->min, policy->max);
if (cpu->policy == CPUFREQ_POLICY_PERFORMANCE) {
if (hwp_active) {
/*
* The active mode only requires an update util hook if HWP
* boost is used and the policy is not "performance".
*/
if (hwp_boost && cpu->policy != CPUFREQ_POLICY_PERFORMANCE) {
intel_pstate_set_update_util_hook(policy->cpu);
} else {
intel_pstate_clear_update_util_hook(policy->cpu);
if (cpu->policy == CPUFREQ_POLICY_PERFORMANCE) {
freq = cpu->max_perf_ratio * cpu->pstate.scaling;
if (cpu->pstate.scaling != cpu->pstate.perf_ctl_scaling)
freq = rounddown(freq, cpu->pstate.perf_ctl_scaling);
}
}
intel_pstate_hwp_set(policy->cpu);
} else if (cpu->policy == CPUFREQ_POLICY_PERFORMANCE) {
int pstate = max(cpu->pstate.min_pstate, cpu->max_perf_ratio);
/*
@@ -2910,25 +2911,17 @@ static int intel_pstate_set_policy(struct cpufreq_policy *policy)
*/
intel_pstate_clear_update_util_hook(policy->cpu);
intel_pstate_set_pstate(cpu, pstate);
freq = pstate * cpu->pstate.scaling;
} else {
intel_pstate_set_update_util_hook(policy->cpu);
}
if (hwp_active) {
/*
* When hwp_boost was active before and dynamically it
* was turned off, in that case we need to clear the
* update util hook.
*/
if (!hwp_boost)
intel_pstate_clear_update_util_hook(policy->cpu);
intel_pstate_hwp_set(policy->cpu);
}
/*
* policy->cur is never updated with the intel_pstate driver, but it
* is used as a stale frequency value. So, keep it within limits.
* policy->cur is never updated in the intel_pstate driver, but it is
* used as a stale frequency value, so set it to reflect the actual
* requested P-state in the "performance" policy case and to the min
* otherwise.
*/
policy->cur = policy->min;
policy->cur = freq;
mutex_unlock(&intel_pstate_limits_lock);
@@ -2971,6 +2964,11 @@ static int intel_pstate_verify_policy(struct cpufreq_policy_data *policy)
return 0;
}
static void intel_pstate_set_min_pstate(struct cpudata *cpu)
{
intel_pstate_set_pstate(cpu, cpu->pstate.min_pstate);
}
static int intel_cpufreq_cpu_offline(struct cpufreq_policy *policy)
{
struct cpudata *cpu = all_cpu_data[policy->cpu];
@@ -3063,6 +3061,7 @@ static int __intel_pstate_cpu_init(struct cpufreq_policy *policy)
static int intel_pstate_cpu_init(struct cpufreq_policy *policy)
{
int ret = __intel_pstate_cpu_init(policy);
struct cpudata *cpu;
if (ret)
return ret;
@@ -3073,11 +3072,11 @@ static int intel_pstate_cpu_init(struct cpufreq_policy *policy)
*/
policy->policy = CPUFREQ_POLICY_POWERSAVE;
if (hwp_active) {
struct cpudata *cpu = all_cpu_data[policy->cpu];
cpu = all_cpu_data[policy->cpu];
if (hwp_active)
cpu->epp_cached = intel_pstate_get_epp(cpu, 0);
}
else
intel_pstate_set_min_pstate(cpu);
return 0;
}
@@ -3243,6 +3242,7 @@ static unsigned int intel_cpufreq_fast_switch(struct cpufreq_policy *policy,
static void intel_cpufreq_adjust_perf(struct cpufreq_policy *policy,
unsigned long min_perf,
unsigned long target_perf,
unsigned long max_perf,
unsigned long capacity)
{
struct cpudata *cpu = all_cpu_data[policy->cpu];
@@ -3273,7 +3273,13 @@ static void intel_cpufreq_adjust_perf(struct cpufreq_policy *policy,
if (min_pstate > cpu->max_perf_ratio)
min_pstate = cpu->max_perf_ratio;
max_pstate = min(cap_pstate, cpu->max_perf_ratio);
max_pstate = cap_pstate;
if (max_perf < capacity)
max_pstate = DIV_ROUND_UP(cap_pstate * max_perf, capacity);
if (max_pstate > cpu->max_perf_ratio)
max_pstate = cpu->max_perf_ratio;
if (max_pstate < min_pstate)
max_pstate = min_pstate;
@@ -3301,8 +3307,6 @@ static int intel_cpufreq_cpu_init(struct cpufreq_policy *policy)
return ret;
policy->cpuinfo.transition_latency = INTEL_CPUFREQ_TRANSITION_LATENCY;
/* This reflects the intel_pstate_get_cpu_pstates() setting. */
policy->cur = policy->cpuinfo.min_freq;
req = kzalloc_objs(*req, 2);
if (!req) {
@@ -3323,9 +3327,15 @@ static int intel_cpufreq_cpu_init(struct cpufreq_policy *policy)
WRITE_ONCE(cpu->hwp_req_cached, value);
cpu->epp_cached = intel_pstate_get_epp(cpu, value);
intel_cpufreq_hwp_update(cpu, cpu->pstate.min_pstate,
cpu->pstate.max_pstate,
cpu->pstate.min_pstate, false);
} else {
policy->transition_delay_us = INTEL_CPUFREQ_TRANSITION_DELAY;
intel_pstate_set_min_pstate(cpu);
}
policy->cur = policy->cpuinfo.min_freq;
freq = DIV_ROUND_UP(cpu->pstate.turbo_freq * global.min_perf_pct, 100);
@@ -3676,14 +3686,14 @@ static inline bool intel_pstate_has_acpi_ppc(void) { return false; }
static inline void intel_pstate_request_control_from_smm(void) {}
#endif /* CONFIG_ACPI */
#define INTEL_PSTATE_HWP_BROADWELL 0x01
#define INTEL_PSTATE_HWP_NOT_HYBRID 0x01
#define X86_MATCH_HWP(vfm, hwp_mode) \
X86_MATCH_VFM_FEATURE(vfm, X86_FEATURE_HWP, hwp_mode)
static const struct x86_cpu_id hwp_support_ids[] __initconst = {
X86_MATCH_HWP(INTEL_BROADWELL_X, INTEL_PSTATE_HWP_BROADWELL),
X86_MATCH_HWP(INTEL_BROADWELL_D, INTEL_PSTATE_HWP_BROADWELL),
X86_MATCH_HWP(INTEL_BROADWELL_X, INTEL_PSTATE_HWP_NOT_HYBRID),
X86_MATCH_HWP(INTEL_BROADWELL_D, INTEL_PSTATE_HWP_NOT_HYBRID),
X86_MATCH_HWP(INTEL_ANY, 0),
{}
};
@@ -3808,7 +3818,6 @@ static int __init intel_pstate_init(void)
if (!no_hwp) {
hwp_active = true;
hwp_mode_bdw = id->driver_data;
intel_pstate.attr = hwp_cpufreq_attrs;
intel_cpufreq.attr = hwp_cpufreq_attrs;
intel_cpufreq.flags |= CPUFREQ_NEED_UPDATE_LIMITS;
@@ -3816,7 +3825,8 @@ static int __init intel_pstate_init(void)
if (!default_driver)
default_driver = &intel_pstate;
pstate_funcs.get_cpu_scaling = hwp_get_cpu_scaling;
if (!id->driver_data)
pstate_funcs.get_cpu_scaling = hwp_get_cpu_scaling;
goto hwp_cpu_matched;
}

View File

@@ -200,6 +200,13 @@ static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev,
case QCOM_ID_IPQ9574:
drv->versions = 1 << (unsigned int)(*speedbin);
break;
case QCOM_ID_IPQ5200:
case QCOM_ID_IPQ5210:
case QCOM_ID_QCF2200:
case QCOM_ID_QCF3200:
case QCOM_ID_QCF3210:
drv->versions = (*speedbin == 0xcd) ? BIT(1) : BIT(0);
break;
case QCOM_ID_IPQ5424:
case QCOM_ID_IPQ5404:
drv->versions = (*speedbin == 0x3b) ? BIT(1) : BIT(0);
@@ -618,6 +625,7 @@ static const struct of_device_id qcom_cpufreq_match_list[] __initconst __maybe_u
{ .compatible = "qcom,msm8909", .data = &match_data_msm8909 },
{ .compatible = "qcom,msm8996", .data = &match_data_kryo },
{ .compatible = "qcom,qcs404", .data = &match_data_qcs404 },
{ .compatible = "qcom,ipq5210", .data = &match_data_kryo },
{ .compatible = "qcom,ipq5332", .data = &match_data_kryo },
{ .compatible = "qcom,ipq5424", .data = &match_data_kryo },
{ .compatible = "qcom,ipq6018", .data = &match_data_ipq6018 },

View File

@@ -4,12 +4,19 @@
use kernel::{
clk::Clk,
cpu, cpufreq,
cpu,
cpufreq, //
cpumask::CpumaskVar,
device::{Core, Device},
device::{
Core,
Device, //
},
error::code::*,
macros::vtable,
module_platform_driver, of, opp, platform,
module_platform_driver,
of,
opp,
platform, //
prelude::*,
str::CString,
sync::Arc,

View File

@@ -79,9 +79,9 @@ static int spear1340_set_cpu_rate(struct clk *sys_pclk, unsigned long newfreq)
int ret = 0;
sys_clk = clk_get_parent(spear_cpufreq.clk);
if (IS_ERR(sys_clk)) {
if (!sys_clk) {
pr_err("failed to get cpu's parent (sys) clock\n");
return PTR_ERR(sys_clk);
return -EINVAL;
}
/* Set the rate of the source clock before changing the parent */

View File

@@ -379,6 +379,7 @@ struct cpufreq_driver {
void (*adjust_perf)(struct cpufreq_policy *policy,
unsigned long min_perf,
unsigned long target_perf,
unsigned long max_perf,
unsigned long capacity);
/*
@@ -624,6 +625,7 @@ unsigned int cpufreq_driver_fast_switch(struct cpufreq_policy *policy,
void cpufreq_driver_adjust_perf(struct cpufreq_policy *policy,
unsigned long min_perf,
unsigned long target_perf,
unsigned long max_perf,
unsigned long capacity);
bool cpufreq_driver_has_adjust_perf(void);
int cpufreq_driver_target(struct cpufreq_policy *policy,

View File

@@ -50,6 +50,7 @@ struct sugov_cpu {
unsigned long util;
unsigned long bw_min;
unsigned long bw_max;
/* The field below is for single-CPU policies only: */
#ifdef CONFIG_NO_HZ_COMMON
@@ -232,6 +233,7 @@ static void sugov_get_util(struct sugov_cpu *sg_cpu, unsigned long boost)
util = effective_cpu_util(sg_cpu->cpu, util, &min, &max);
util = max(util, boost);
sg_cpu->bw_min = min;
sg_cpu->bw_max = max;
sg_cpu->util = sugov_effective_cpu_perf(sg_cpu->cpu, util, min, max);
}
@@ -314,7 +316,7 @@ static void sugov_iowait_boost(struct sugov_cpu *sg_cpu, u64 time,
* A CPU running a task which woken up after an IO operation can have its
* utilization boosted to speed up the completion of those IO operations.
* The IO boost value is increased each time a task wakes up from IO, in
* sugov_iowait_apply(), and it's instead decreased by this function,
* sugov_iowait_boost(), and it's instead decreased by this function,
* each time an increase has not been requested (!iowait_boost_pending).
*
* A CPU which also appears to have been idle for at least one tick has also
@@ -484,7 +486,7 @@ static void sugov_update_single_perf(struct update_util_data *hook, u64 time,
sg_cpu->util = prev_util;
cpufreq_driver_adjust_perf(sg_policy->policy, sg_cpu->bw_min,
sg_cpu->util, max_cap);
sg_cpu->util, sg_cpu->bw_max, max_cap);
sg_policy->need_freq_update = false;
sg_policy->last_freq_update_time = time;
@@ -590,7 +592,7 @@ static ssize_t rate_limit_us_show(struct gov_attr_set *attr_set, char *buf)
{
struct sugov_tunables *tunables = to_sugov_tunables(attr_set);
return sprintf(buf, "%u\n", tunables->rate_limit_us);
return sysfs_emit(buf, "%u\n", tunables->rate_limit_us);
}
static ssize_t

View File

@@ -361,23 +361,28 @@ pub fn new() -> Self {
}
}
/// Adds a new entry to the table.
pub fn add(&mut self, freq: Hertz, flags: u32, driver_data: u32) -> Result {
/// Adds a raw frequency-table entry.
fn push(&mut self, frequency: u32, flags: u32, driver_data: u32) -> Result {
// Adds the new entry at the end of the vector.
Ok(self.entries.push(
bindings::cpufreq_frequency_table {
flags,
driver_data,
frequency: freq.as_khz() as u32,
frequency,
},
GFP_KERNEL,
)?)
}
/// Adds a new entry to the table.
pub fn add(&mut self, freq: Hertz, flags: u32, driver_data: u32) -> Result {
self.push(freq.as_khz() as u32, flags, driver_data)
}
/// Consumes the [`TableBuilder`] and returns [`TableBox`].
pub fn to_table(mut self) -> Result<TableBox> {
// Add last entry to the table.
self.add(Hertz(c_ulong::MAX), 0, 0)?;
self.push(bindings::CPUFREQ_TABLE_END as u32, 0, 0)?;
TableBox::new(self.entries)
}
@@ -792,7 +797,13 @@ fn fast_switch(_policy: &mut Policy, _target_freq: u32) -> u32 {
}
/// Driver's `adjust_perf` callback.
fn adjust_perf(_policy: &mut Policy, _min_perf: usize, _target_perf: usize, _capacity: usize) {
fn adjust_perf(
_policy: &mut Policy,
_min_perf: usize,
_target_perf: usize,
_max_perf: usize,
_capacity: usize,
) {
build_error!(VTABLE_DEFAULT_ERROR)
}
@@ -817,7 +828,9 @@ fn update_limits(_policy: &mut Policy) {
}
/// Driver's `bios_limit` callback.
fn bios_limit(_policy: &mut Policy, _limit: &mut u32) -> Result {
///
/// Returns HW/BIOS max frequency limitations for the CPU.
fn bios_limit(_policy: &mut Policy) -> Result<u32> {
build_error!(VTABLE_DEFAULT_ERROR)
}
@@ -1263,12 +1276,13 @@ impl<T: Driver> Registration<T> {
ptr: *mut bindings::cpufreq_policy,
min_perf: c_ulong,
target_perf: c_ulong,
max_perf: c_ulong,
capacity: c_ulong,
) {
// SAFETY: The `ptr` is guaranteed to be valid by the contract with the C code for the
// lifetime of `policy`.
let policy = unsafe { Policy::from_raw_mut(ptr) };
T::adjust_perf(policy, min_perf, target_perf, capacity);
T::adjust_perf(policy, min_perf, target_perf, max_perf, capacity);
}
/// Driver's `get_intermediate` callback.
@@ -1352,9 +1366,12 @@ impl<T: Driver> Registration<T> {
from_result(|| {
let mut policy = PolicyCpu::from_cpu(cpu_id)?;
let val = T::bios_limit(&mut policy)?;
// SAFETY: `limit` is guaranteed by the C code to be valid.
T::bios_limit(&mut policy, &mut (unsafe { *limit })).map(|()| 0)
unsafe {
*limit = val;
}
Ok(0)
})
}

View File

@@ -16,6 +16,12 @@ source cpufreq.sh
CUR_GOV=
CUR_FREQ=
# Per-policy backup, keyed by policy so multiple policies can be saved at once
# (backup_governor/restore_governor also keep CUR_GOV/CUR_FREQ for callers that
# read them directly).
declare -A SAVED_GOVERNORS
declare -A SAVED_FREQS
# Find governor's directory path
# $1: policy, $2: governor
find_gov_directory()
@@ -39,11 +45,13 @@ find_current_governor()
backup_governor()
{
CUR_GOV=$(find_current_governor $1)
SAVED_GOVERNORS[$1]=$CUR_GOV
printf "Governor backup done for $1: $CUR_GOV\n"
if [ $CUR_GOV == "userspace" ]; then
CUR_FREQ=$(find_current_freq $1)
SAVED_FREQS[$1]=$CUR_FREQ
printf "Governor frequency backup done for $1: $CUR_FREQ\n"
fi
@@ -53,11 +61,13 @@ backup_governor()
# $1: policy
restore_governor()
{
CUR_GOV=${SAVED_GOVERNORS[$1]}
__switch_governor $1 $CUR_GOV
printf "Governor restored for $1 to $CUR_GOV\n"
if [ $CUR_GOV == "userspace" ]; then
CUR_FREQ=${SAVED_FREQS[$1]}
set_cpu_frequency $1 $CUR_FREQ
printf "Governor frequency restored for $1: $CUR_FREQ\n"
fi
@@ -65,6 +75,17 @@ restore_governor()
printf "\n"
}
# Save/restore governors for every policy at once
save_all_governors()
{
for_each_policy backup_governor
}
restore_all_governors()
{
for_each_policy restore_governor
}
# param:
# $1: policy, $2: governor
__switch_governor()
@@ -100,11 +121,6 @@ switch_governor()
# $1: policy, $2: governor
switch_show_governor()
{
cur_gov=find_current_governor
if [ $cur_gov == "userspace" ]; then
cur_freq=find_current_freq
fi
# switch governor
__switch_governor $1 $2

View File

@@ -40,7 +40,9 @@ simple_lockdep()
{
printf "** Test: Running ${FUNCNAME[0]} **\n"
save_all_governors
for_each_policy __simple_lockdep
restore_all_governors
}
# Test 2
@@ -56,7 +58,10 @@ concurrent_lockdep()
{
printf "** Test: Running ${FUNCNAME[0]} **\n"
save_all_governors
for_each_policy_concurrent __concurrent_lockdep
wait
restore_all_governors
}
# Test 3
@@ -65,20 +70,26 @@ quick_shuffle()
# this is called concurrently from governor_race
for I in `seq 1000`
do
echo ondemand | sudo tee $CPUFREQROOT/policy*/scaling_governor &
echo userspace | sudo tee $CPUFREQROOT/policy*/scaling_governor &
echo ondemand | tee $CPUFREQROOT/policy*/scaling_governor &
echo userspace | tee $CPUFREQROOT/policy*/scaling_governor &
done
wait
}
governor_race()
{
printf "** Test: Running ${FUNCNAME[0]} **\n"
save_all_governors
# run 8 concurrent instances
for I in `seq 8`
do
quick_shuffle &
done
wait
restore_all_governors
}
# Test 4
@@ -112,5 +123,8 @@ hotplug_with_updates_cpu()
hotplug_with_updates()
{
save_all_governors
for_each_non_boot_cpu hotplug_with_updates_cpu
wait
restore_all_governors
}