mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 12:52:29 -04:00
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:
@@ -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``
|
||||
======================================
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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, ®_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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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", },
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user