diff --git a/Documentation/admin-guide/pm/amd-pstate.rst b/Documentation/admin-guide/pm/amd-pstate.rst index a95e2ebce005..c7bf7cc2db87 100644 --- a/Documentation/admin-guide/pm/amd-pstate.rst +++ b/Documentation/admin-guide/pm/amd-pstate.rst @@ -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`` ====================================== diff --git a/Documentation/admin-guide/pm/cpufreq.rst b/Documentation/admin-guide/pm/cpufreq.rst index 8831cface585..34baf20cc202 100644 --- a/Documentation/admin-guide/pm/cpufreq.rst +++ b/Documentation/admin-guide/pm/cpufreq.rst @@ -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 diff --git a/drivers/cpufreq/amd-pstate-ut.c b/drivers/cpufreq/amd-pstate-ut.c index 735b29f76438..e23773680e05 100644 --- a/drivers/cpufreq/amd-pstate-ut.c +++ b/drivers/cpufreq/amd-pstate-ut.c @@ -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; diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index 3a6b4b224a66..d4ff8b228f86 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -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 "); MODULE_DESCRIPTION("AMD Processor P-state Frequency Driver"); diff --git a/drivers/cpufreq/amd-pstate.h b/drivers/cpufreq/amd-pstate.h index 23e8baa05849..9f5a81976eae 100644 --- a/drivers/cpufreq/amd-pstate.h +++ b/drivers/cpufreq/amd-pstate.h @@ -11,6 +11,13 @@ #include #include +#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; diff --git a/drivers/cpufreq/apple-soc-cpufreq.c b/drivers/cpufreq/apple-soc-cpufreq.c index 638e5bf72185..7c50ce74f911 100644 --- a/drivers/cpufreq/apple-soc-cpufreq.c +++ b/drivers/cpufreq/apple-soc-cpufreq.c @@ -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); } diff --git a/drivers/cpufreq/brcmstb-avs-cpufreq.c b/drivers/cpufreq/brcmstb-avs-cpufreq.c index 71450cca8e9f..7d902856ac01 100644 --- a/drivers/cpufreq/brcmstb-avs-cpufreq.c +++ b/drivers/cpufreq/brcmstb-avs-cpufreq.c @@ -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; diff --git a/drivers/cpufreq/cpufreq-dt-platdev.c b/drivers/cpufreq/cpufreq-dt-platdev.c index ff1204c666b1..284eece9e230 100644 --- a/drivers/cpufreq/cpufreq-dt-platdev.c +++ b/drivers/cpufreq/cpufreq-dt-platdev.c @@ -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", }, diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index b898b6544069..0d0df986fa3d 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -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); } /** diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index e93697d3edfd..731f5721ff1e 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -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); diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 6e984c114d96..7ce5981cbae5 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -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; } diff --git a/drivers/cpufreq/qcom-cpufreq-nvmem.c b/drivers/cpufreq/qcom-cpufreq-nvmem.c index e6d28d162442..efa766e98d86 100644 --- a/drivers/cpufreq/qcom-cpufreq-nvmem.c +++ b/drivers/cpufreq/qcom-cpufreq-nvmem.c @@ -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 }, diff --git a/drivers/cpufreq/rcpufreq_dt.rs b/drivers/cpufreq/rcpufreq_dt.rs index 10106fa13095..5d27857261da 100644 --- a/drivers/cpufreq/rcpufreq_dt.rs +++ b/drivers/cpufreq/rcpufreq_dt.rs @@ -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, diff --git a/drivers/cpufreq/spear-cpufreq.c b/drivers/cpufreq/spear-cpufreq.c index 81a0780b2ebf..ffe5eda82f0b 100644 --- a/drivers/cpufreq/spear-cpufreq.c +++ b/drivers/cpufreq/spear-cpufreq.c @@ -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 */ diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index ae9d1ce4f49c..35ce665edfd8 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -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, diff --git a/kernel/sched/cpufreq_schedutil.c b/kernel/sched/cpufreq_schedutil.c index dff4ee04694c..a1782755efcc 100644 --- a/kernel/sched/cpufreq_schedutil.c +++ b/kernel/sched/cpufreq_schedutil.c @@ -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 diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs index 58ac04c650a1..affa2b9490ef 100644 --- a/rust/kernel/cpufreq.rs +++ b/rust/kernel/cpufreq.rs @@ -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 { // 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 { build_error!(VTABLE_DEFAULT_ERROR) } @@ -1263,12 +1276,13 @@ impl Registration { 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 Registration { 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) }) } diff --git a/tools/testing/selftests/cpufreq/governor.sh b/tools/testing/selftests/cpufreq/governor.sh index fe37df79c087..cf59e63f8e14 100755 --- a/tools/testing/selftests/cpufreq/governor.sh +++ b/tools/testing/selftests/cpufreq/governor.sh @@ -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 diff --git a/tools/testing/selftests/cpufreq/special-tests.sh b/tools/testing/selftests/cpufreq/special-tests.sh index 8d40505dc468..e87ed7c8e5e5 100755 --- a/tools/testing/selftests/cpufreq/special-tests.sh +++ b/tools/testing/selftests/cpufreq/special-tests.sh @@ -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 }