From 58d0379d29db10cf382d0b368c8da183f43e612b Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Thu, 23 Apr 2026 19:36:08 +0200 Subject: [PATCH 01/25] cpufreq: qcom: Unify user-visible "Qualcomm" name Various names for Qualcomm as a company are used in user-visible config options. Switch to unified "Qualcomm" so it will be easier for users to identify the options when for example running menuconfig. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Viresh Kumar --- drivers/cpufreq/Kconfig.arm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/Kconfig.arm b/drivers/cpufreq/Kconfig.arm index 47c9b031f1b3..a441668f9e0c 100644 --- a/drivers/cpufreq/Kconfig.arm +++ b/drivers/cpufreq/Kconfig.arm @@ -153,7 +153,7 @@ config ARM_QCOM_CPUFREQ_NVMEM If in doubt, say N. config ARM_QCOM_CPUFREQ_HW - tristate "QCOM CPUFreq HW driver" + tristate "Qualcomm CPUFreq HW driver" depends on ARCH_QCOM || COMPILE_TEST depends on COMMON_CLK help From 08a64b9bfc1c099ede2c881adfd7a49fd285e79f Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Fri, 1 May 2026 00:32:12 +0800 Subject: [PATCH 02/25] cpufreq/amd-pstate: Use FIELD_MODIFY() Use FIELD_MODIFY() to remove open-coded bit manipulation. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Reviewed-by: K Prateek Nayak Signed-off-by: Viresh Kumar --- drivers/cpufreq/amd-pstate.c | 26 ++++++++++---------------- 1 file changed, 10 insertions(+), 16 deletions(-) diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index 453084c67327..1037d1722263 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -246,12 +246,10 @@ static int msr_update_perf(struct cpufreq_policy *policy, u8 min_perf, value = prev = READ_ONCE(cpudata->cppc_req_cached); - value &= ~(AMD_CPPC_MAX_PERF_MASK | AMD_CPPC_MIN_PERF_MASK | - AMD_CPPC_DES_PERF_MASK | AMD_CPPC_EPP_PERF_MASK); - value |= FIELD_PREP(AMD_CPPC_MAX_PERF_MASK, max_perf); - value |= FIELD_PREP(AMD_CPPC_DES_PERF_MASK, des_perf); - value |= FIELD_PREP(AMD_CPPC_MIN_PERF_MASK, min_perf); - value |= FIELD_PREP(AMD_CPPC_EPP_PERF_MASK, epp); + FIELD_MODIFY(AMD_CPPC_MAX_PERF_MASK, &value, max_perf); + FIELD_MODIFY(AMD_CPPC_DES_PERF_MASK, &value, des_perf); + FIELD_MODIFY(AMD_CPPC_MIN_PERF_MASK, &value, min_perf); + FIELD_MODIFY(AMD_CPPC_EPP_PERF_MASK, &value, epp); if (trace_amd_pstate_epp_perf_enabled()) { union perf_cached perf = READ_ONCE(cpudata->perf); @@ -300,8 +298,7 @@ static int msr_set_epp(struct cpufreq_policy *policy, u8 epp) int ret; value = prev = READ_ONCE(cpudata->cppc_req_cached); - value &= ~AMD_CPPC_EPP_PERF_MASK; - value |= FIELD_PREP(AMD_CPPC_EPP_PERF_MASK, epp); + FIELD_MODIFY(AMD_CPPC_EPP_PERF_MASK, &value, epp); if (trace_amd_pstate_epp_perf_enabled()) { union perf_cached perf = cpudata->perf; @@ -441,8 +438,7 @@ static int shmem_set_epp(struct cpufreq_policy *policy, u8 epp) } value = READ_ONCE(cpudata->cppc_req_cached); - value &= ~AMD_CPPC_EPP_PERF_MASK; - value |= FIELD_PREP(AMD_CPPC_EPP_PERF_MASK, epp); + FIELD_MODIFY(AMD_CPPC_EPP_PERF_MASK, &value, epp); WRITE_ONCE(cpudata->cppc_req_cached, value); return ret; @@ -575,12 +571,10 @@ static int shmem_update_perf(struct cpufreq_policy *policy, u8 min_perf, value = prev = READ_ONCE(cpudata->cppc_req_cached); - value &= ~(AMD_CPPC_MAX_PERF_MASK | AMD_CPPC_MIN_PERF_MASK | - AMD_CPPC_DES_PERF_MASK | AMD_CPPC_EPP_PERF_MASK); - value |= FIELD_PREP(AMD_CPPC_MAX_PERF_MASK, max_perf); - value |= FIELD_PREP(AMD_CPPC_DES_PERF_MASK, des_perf); - value |= FIELD_PREP(AMD_CPPC_MIN_PERF_MASK, min_perf); - value |= FIELD_PREP(AMD_CPPC_EPP_PERF_MASK, epp); + FIELD_MODIFY(AMD_CPPC_MAX_PERF_MASK, &value, max_perf); + FIELD_MODIFY(AMD_CPPC_DES_PERF_MASK, &value, des_perf); + FIELD_MODIFY(AMD_CPPC_MIN_PERF_MASK, &value, min_perf); + FIELD_MODIFY(AMD_CPPC_EPP_PERF_MASK, &value, epp); if (trace_amd_pstate_epp_perf_enabled()) { union perf_cached perf = READ_ONCE(cpudata->perf); From 88e8df5904007ea53232237acf9ad02aeb992ece Mon Sep 17 00:00:00 2001 From: Hans Zhang <18255117159@163.com> Date: Fri, 1 May 2026 00:32:13 +0800 Subject: [PATCH 03/25] cpufreq: apple-soc: Use FIELD_MODIFY() Use FIELD_MODIFY() to remove open-coded bit manipulation. No functional change intended. Signed-off-by: Hans Zhang <18255117159@163.com> Reviewed-by: Joshua Peisach Signed-off-by: Viresh Kumar --- drivers/cpufreq/apple-soc-cpufreq.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/cpufreq/apple-soc-cpufreq.c b/drivers/cpufreq/apple-soc-cpufreq.c index 9396034167e5..638e5bf72185 100644 --- a/drivers/cpufreq/apple-soc-cpufreq.c +++ b/drivers/cpufreq/apple-soc-cpufreq.c @@ -187,10 +187,8 @@ static int apple_soc_cpufreq_set_target(struct cpufreq_policy *policy, reg &= ~priv->info->ps1_mask; reg |= pstate << priv->info->ps1_shift; - if (priv->info->has_ps2) { - reg &= ~APPLE_DVFS_CMD_PS2; - reg |= FIELD_PREP(APPLE_DVFS_CMD_PS2, pstate); - } + if (priv->info->has_ps2) + FIELD_MODIFY(APPLE_DVFS_CMD_PS2, ®, pstate); reg |= APPLE_DVFS_CMD_SET; writeq_relaxed(reg, priv->reg_base + APPLE_DVFS_CMD); From bcb8889c4981fdde42d4fd2c29a77d510fe21da2 Mon Sep 17 00:00:00 2001 From: Guangshuo Li Date: Sat, 2 May 2026 03:00:05 +0800 Subject: [PATCH 04/25] cpufreq: qcom-cpufreq-hw: Fix possible double free qcom_cpufreq.data is allocated with devm_kzalloc() in probe() as an array of per-domain data. qcom_cpufreq_hw_cpu_init() stores a pointer to one element of this array in policy->driver_data. qcom_cpufreq_hw_cpu_exit() currently calls kfree() on policy->driver_data. This is not valid because the memory is devm-managed. For the first domain, this can free the devm-managed allocation while the devres entry is still active, leading to a possible double free when the platform device is later detached. For other domains, the pointer may refer to an element inside the array rather than the allocation base. Remove the kfree(data) call and let devres release qcom_cpufreq.data. This issue was found by a static analysis tool I am developing. Fixes: 054a3ef683a1 ("cpufreq: qcom-hw: Allocate qcom_cpufreq_data during probe") Cc: stable@vger.kernel.org Signed-off-by: Guangshuo Li Reviewed-by: Zhongqiu Han Signed-off-by: Viresh Kumar --- drivers/cpufreq/qcom-cpufreq-hw.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index ea9a20d27b8f..ef19faedbfec 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -578,7 +578,6 @@ static void qcom_cpufreq_hw_cpu_exit(struct cpufreq_policy *policy) dev_pm_opp_of_cpumask_remove_table(policy->related_cpus); qcom_cpufreq_hw_lmh_exit(data); kfree(policy->freq_table); - kfree(data); } static void qcom_cpufreq_ready(struct cpufreq_policy *policy) From 9a303892acac58e77704d336418fa9ae92e3f70f Mon Sep 17 00:00:00 2001 From: Xueqin Luo Date: Fri, 15 May 2026 10:42:42 +0800 Subject: [PATCH 05/25] cpufreq: cppc: mask Desired_Excursion when autonomous selection is enabled According to the ACPI 6.6 specification, the Desired_Excursion field is not utilized when autonomous selection is enabled. In this mode, the bit is architecturally ignored and does not carry meaningful information. Currently, the kernel exposes the raw Performance Limited register value to userspace through the cpufreq sysfs interface. This may lead to misinterpretation, as userspace may assume Desired_Excursion is valid even when autonomous selection is active. To provide a stable and semantically correct ABI, mask out the Desired_Excursion bit when autonomous selection is enabled, so that userspace does not observe undefined or misleading values. Writes are left unchanged, as the field is architecturally ignored in this mode and write attempts are harmless. Signed-off-by: Xueqin Luo Reviewed-by: Pierre Gondois Reviewed-by: Sumit Gupta Signed-off-by: Viresh Kumar --- drivers/cpufreq/cppc_cpufreq.c | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index 7e7f9dfb7a24..bf5175f7551c 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -982,7 +982,34 @@ store_energy_performance_preference_val(struct cpufreq_policy *policy, return count; } -CPPC_CPUFREQ_ATTR_RW_U64(perf_limited, cppc_get_perf_limited, +static int cppc_get_perf_limited_filtered(int cpu, u64 *perf_limited) +{ + struct cpufreq_policy *policy; + struct cppc_cpudata *cpu_data; + int ret; + + ret = cppc_get_perf_limited(cpu, perf_limited); + if (ret) + return ret; + + policy = cpufreq_cpu_get_raw(cpu); + if (!policy) + return -EINVAL; + + cpu_data = policy->driver_data; + + /* + * Desired Excursion is ignored when autonomous selection is + * enabled. Clear the bit to avoid exposing meaningless state + * to userspace. + */ + if (cpu_data && cpu_data->perf_ctrls.auto_sel) + *perf_limited &= ~CPPC_PERF_LIMITED_DESIRED_EXCURSION; + + return 0; +} + +CPPC_CPUFREQ_ATTR_RW_U64(perf_limited, cppc_get_perf_limited_filtered, cppc_set_perf_limited) cpufreq_freq_attr_ro(freqdomain_cpus); From 266d3dd8b757b48a576e90f018b51f7b7563cc32 Mon Sep 17 00:00:00 2001 From: Yuho Choi Date: Thu, 16 Apr 2026 10:46:21 -0400 Subject: [PATCH 06/25] cpufreq: pcc: fix use-after-free and double free in _OSC evaluation pcc_cpufreq_do_osc() calls acpi_evaluate_object() twice for the two-phase _OSC negotiation. Between the two calls it freed output.pointer but left output.length unchanged. Since acpi_evaluate_object() treats a non-zero length with a non-NULL pointer as an existing buffer to write into, the second call wrote into freed memory (use-after-free). The subsequent kfree(output.pointer) at out_free then freed the same pointer a second time (double free). Reset output.pointer to NULL and output.length to ACPI_ALLOCATE_BUFFER after freeing the first result, so ACPICA allocates a fresh buffer for each phase independently. Fixes: 0f1d683fb35d ("[CPUFREQ] Processor Clocking Control interface driver") Signed-off-by: Yuho Choi Reviewed-by: Zhongqiu Han Cc: All applicable Link: https://patch.msgid.link/20260416144621.93964-1-dbgh9129@gmail.com Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/pcc-cpufreq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/cpufreq/pcc-cpufreq.c b/drivers/cpufreq/pcc-cpufreq.c index ac2e90a65f0c..a355ec4f3dd4 100644 --- a/drivers/cpufreq/pcc-cpufreq.c +++ b/drivers/cpufreq/pcc-cpufreq.c @@ -352,6 +352,8 @@ static int __init pcc_cpufreq_do_osc(acpi_handle *handle) } kfree(output.pointer); + output.pointer = NULL; + output.length = ACPI_ALLOCATE_BUFFER; capabilities[0] = 0x0; capabilities[1] = 0x1; From a9029dd55696c651ee46912afa2a166fa456bb3e Mon Sep 17 00:00:00 2001 From: Tianxiang Chen Date: Wed, 8 Apr 2026 22:19:14 +0800 Subject: [PATCH 07/25] cpufreq: Fix hotplug-suspend race during reboot During system reboot, cpufreq_suspend() is called via the kernel_restart() -> device_shutdown() path. Unlike the normal system suspend path, the reboot path does not call freeze_processes(), so userspace processes and kernel threads remain active. This allows CPU hotplug operations to run concurrently with cpufreq_suspend(). The original code has no synchronization with CPU hotplug, leading to a race condition where governor_data can be freed by the hotplug path while cpufreq_suspend() is still accessing it, resulting in a null pointer dereference: Unable to handle kernel NULL pointer dereference Call Trace: do_kernel_fault+0x28/0x3c cpufreq_suspend+0xdc/0x160 device_shutdown+0x18/0x200 kernel_restart+0x40/0x80 arm64_sys_reboot+0x1b0/0x200 Fix this by adding cpus_read_lock()/cpus_read_unlock() to cpufreq_suspend() to block CPU hotplug operations while suspend is in progress. Fixes: 65650b35133f ("cpufreq: Avoid cpufreq_suspend() deadlock on system shutdown") Signed-off-by: Tianxiang Chen Reviewed-by: Zhongqiu Han Cc: All applicable [ rjw: Changelog edits ] Link: https://patch.msgid.link/20260408141914.35281-1-nanmu@xiaomi.com Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 44eb1b7e7fc1..d41067cd1f1b 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1972,6 +1972,7 @@ void cpufreq_suspend(void) if (!cpufreq_driver) return; + cpus_read_lock(); if (!has_target() && !cpufreq_driver->suspend) goto suspend; @@ -1991,6 +1992,7 @@ void cpufreq_suspend(void) suspend: cpufreq_suspended = true; + cpus_read_unlock(); } /** From 85524e651d20944399322d46fb97960337831d43 Mon Sep 17 00:00:00 2001 From: Pengjie Zhang Date: Mon, 18 May 2026 21:34:57 +0800 Subject: [PATCH 08/25] cpufreq: Documentation: fix sampling_down_factor range The ondemand governor implementation accepts sampling_down_factor values from 1 to 100000 via MAX_SAMPLING_DOWN_FACTOR, but the documentation in admin-guide/pm/cpufreq.rst still says the valid range is 1 to 100. Update the documentation to match the actual code. Fixes: 2a0e49279850 ("cpufreq: User/admin documentation update and consolidation") Reviewed-by: Zhongqiu Han Signed-off-by: Pengjie Zhang Link: https://patch.msgid.link/20260518133457.2408463-1-zhangpengjie2@huawei.com Signed-off-by: Rafael J. Wysocki --- Documentation/admin-guide/pm/cpufreq.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/admin-guide/pm/cpufreq.rst b/Documentation/admin-guide/pm/cpufreq.rst index dbe6d23a5d67..fdca59c955dc 100644 --- a/Documentation/admin-guide/pm/cpufreq.rst +++ b/Documentation/admin-guide/pm/cpufreq.rst @@ -516,7 +516,7 @@ This governor exposes the following tunables: of those tasks above 0 and set this attribute to 1. ``sampling_down_factor`` - Temporary multiplier, between 1 (default) and 100 inclusive, to apply to + Temporary multiplier, between 1 (default) and 100000 inclusive, to apply to the ``sampling_rate`` value if the CPU load goes above ``up_threshold``. This causes the next execution of the governor's worker routine (after From bcbdaa1086c25a8a5d48e04e1b82fdfb0682b681 Mon Sep 17 00:00:00 2001 From: Fushuai Wang Date: Wed, 20 May 2026 11:21:19 +0800 Subject: [PATCH 09/25] cpufreq: intel_pstate: Sync policy->cur during CPU offline When a CPU goes offline with HWP disabled, intel_pstate_set_min_pstate() sets the MSR_IA32_PERF_CTL to minimum frequency to prevent SMT siblings from being restricted. However, the policy->cur value was not updated, leaving it at the previous value. When the CPU comes back online, governor->limits() checks if target_freq equals policy->cur and skips the frequency adjustment if they match. Since policy->cur still holds the previous value, the governor does not call cpufreq_driver->target to update MSR_IA32_PERF_CTL. Fix this by synchronizing policy->cur with the hardware state when setting minimum pstate during CPU offline. Fixes: bb18008f8086 ("intel_pstate: Set core to min P state during core offline") Cc: stable@vger.kernel.org # 3.15+ Suggested-by: Srinivas Pandruvada Signed-off-by: Fushuai Wang [ rjw: Subject refinement ] Link: https://patch.msgid.link/20260520032119.30615-1-fushuai.wang@linux.dev Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/intel_pstate.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 1f093e346430..0fbbdbd5765c 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -2984,10 +2984,12 @@ static int intel_cpufreq_cpu_offline(struct cpufreq_policy *policy) * from getting to lower performance levels, so force the minimum * performance on CPU offline to prevent that from happening. */ - if (hwp_active) + if (hwp_active) { intel_pstate_hwp_offline(cpu); - else + } else { intel_pstate_set_min_pstate(cpu); + policy->cur = cpu->pstate.min_freq; + } intel_pstate_exit_perf_limits(policy); From aa70232cc7431d517e0402d90c38e11b67414049 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 22 May 2026 09:49:08 +0530 Subject: [PATCH 10/25] cpufreq: Fix typo in comment Replace "diver" with "driver" in the comment describing CPUFREQ_NEED_UPDATE_LIMITS. Signed-off-by: Viresh Kumar Reviewed-by: Zhongqiu Han Reviewed-by: Lifeng Zheng Link: https://patch.msgid.link/396f64411431ffbb5b4f07d1f2e0bbf9763d468f.1779423281.git.viresh.kumar@linaro.org Signed-off-by: Rafael J. Wysocki --- include/linux/cpufreq.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 2ab691828e48..4d4b4ed24b30 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -434,7 +434,7 @@ struct cpufreq_driver { /* * Set by drivers that need to update internal upper and lower boundaries along * with the target frequency and so the core and governors should also invoke - * the diver if the target frequency does not change, but the policy min or max + * the driver if the target frequency does not change, but the policy min or max * may have changed. */ #define CPUFREQ_NEED_UPDATE_LIMITS BIT(0) From 9801be8bef65dbcbc40c1a9c2f03d70ed67a5d30 Mon Sep 17 00:00:00 2001 From: Viresh Kumar Date: Fri, 22 May 2026 09:49:09 +0530 Subject: [PATCH 11/25] cpufreq: Avoid redundant target() calls for unchanged limits Drivers setting CPUFREQ_NEED_UPDATE_LIMITS expect target() to be invoked even if the target frequency remains unchanged, so they can update their internal policy limits state. Currently the core invokes target() unconditionally whenever the requested frequency matches policy->cur for such drivers, even if policy->min and policy->max haven't changed since the previous update. Track pending policy limit updates explicitly and skip redundant target() invocations when neither the target frequency nor the effective limits changed. Signed-off-by: Viresh Kumar Reviewed-by: Lifeng Zheng Reviewed-by: Zhongqiu Han Link: https://patch.msgid.link/d0107c364b709abca21acf88072220bc05478594.1779423281.git.viresh.kumar@linaro.org Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 31 ++++++++++++++++++++++--------- include/linux/cpufreq.h | 3 +++ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index d41067cd1f1b..4e78f4caab3a 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -2368,9 +2368,13 @@ int __cpufreq_driver_target(struct cpufreq_policy *policy, * exactly same freq is called again and so we can save on few function * calls. */ - if (target_freq == policy->cur && - !(cpufreq_driver->flags & CPUFREQ_NEED_UPDATE_LIMITS)) - return 0; + if (target_freq == policy->cur) { + if (!(cpufreq_driver->flags & CPUFREQ_NEED_UPDATE_LIMITS) || + !policy->update_limits) + return 0; + + policy->update_limits = false; + } if (cpufreq_driver->target) { /* @@ -2622,6 +2626,7 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy, { struct cpufreq_policy_data new_data; struct cpufreq_governor *old_gov; + unsigned int freq; int ret; memcpy(&new_data.cpuinfo, &policy->cpuinfo, sizeof(policy->cpuinfo)); @@ -2654,12 +2659,20 @@ static int cpufreq_set_policy(struct cpufreq_policy *policy, * compiler optimizations around them because they may be accessed * concurrently by cpufreq_driver_resolve_freq() during the update. */ - WRITE_ONCE(policy->max, __resolve_freq(policy, new_data.max, - new_data.min, new_data.max, - CPUFREQ_RELATION_H)); - new_data.min = __resolve_freq(policy, new_data.min, new_data.min, - new_data.max, CPUFREQ_RELATION_L); - WRITE_ONCE(policy->min, new_data.min > policy->max ? policy->max : new_data.min); + freq = __resolve_freq(policy, new_data.max, new_data.min, new_data.max, + CPUFREQ_RELATION_H); + if (freq != policy->max) { + WRITE_ONCE(policy->max, freq); + policy->update_limits = true; + } + + freq = __resolve_freq(policy, new_data.min, new_data.min, new_data.max, + CPUFREQ_RELATION_L); + freq = min(freq, policy->max); + if (freq != policy->min) { + WRITE_ONCE(policy->min, freq); + policy->update_limits = true; + } trace_cpu_frequency_limits(policy); diff --git a/include/linux/cpufreq.h b/include/linux/cpufreq.h index 4d4b4ed24b30..ae9d1ce4f49c 100644 --- a/include/linux/cpufreq.h +++ b/include/linux/cpufreq.h @@ -146,6 +146,9 @@ struct cpufreq_policy { /* Per policy boost supported flag. */ bool boost_supported; + /* Pending policy->min/max update for the driver */ + bool update_limits; + /* Cached frequency lookup from cpufreq_driver_resolve_freq. */ unsigned int cached_target_freq; unsigned int cached_resolved_idx; From 3494dff89779b73a6c70481c982e0e96d336454a Mon Sep 17 00:00:00 2001 From: Lifeng Zheng Date: Fri, 22 May 2026 09:49:10 +0530 Subject: [PATCH 12/25] cpufreq: conservative: Simplify frequency limit handling cs_dbs_update() performs explicit checks against policy->min/max before updating the target frequency. These checks are redundant as __cpufreq_driver_target() already clamps the requested frequency to the valid policy limits. Remove the unnecessary boundary checks and simplify the update logic. This also fixes an issue introduced by commit 00bfe05889e9 ("cpufreq: conservative: Decrease frequency faster for deferred updates"), where stale target comparisons could cause frequency updates to be skipped entirely after deferred adjustments. Closes: https://lore.kernel.org/all/20260421123545.1745998-1-zhenglifeng1@huawei.com/ Fixes: 00bfe05889e9 ("cpufreq: conservative: Decrease frequency faster for deferred updates") Signed-off-by: Lifeng Zheng Co-developed-by: Viresh Kumar Signed-off-by: Viresh Kumar Reviewed-by: Zhongqiu Han Link: https://patch.msgid.link/292e6d937890f135e30ec0d2107eaad47cb9a976.1779423281.git.viresh.kumar@linaro.org Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq_conservative.c | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/drivers/cpufreq/cpufreq_conservative.c b/drivers/cpufreq/cpufreq_conservative.c index df01d33993d8..0b32ae28ec85 100644 --- a/drivers/cpufreq/cpufreq_conservative.c +++ b/drivers/cpufreq/cpufreq_conservative.c @@ -103,10 +103,6 @@ static unsigned int cs_dbs_update(struct cpufreq_policy *policy) if (load > dbs_data->up_threshold) { dbs_info->down_skip = 0; - /* if we are already at full speed then break out early */ - if (requested_freq == policy->max) - goto out; - requested_freq += freq_step; if (requested_freq > policy->max) requested_freq = policy->max; @@ -124,13 +120,7 @@ static unsigned int cs_dbs_update(struct cpufreq_policy *policy) /* Check for frequency decrease */ if (load < cs_tuners->down_threshold) { - /* - * if we cannot reduce the frequency anymore, break out early - */ - if (requested_freq == policy->min) - goto out; - - if (requested_freq > freq_step) + if (requested_freq > policy->min + freq_step) requested_freq -= freq_step; else requested_freq = policy->min; From 7dd383dc6693654d013b3c15e322ec1534ccd86e Mon Sep 17 00:00:00 2001 From: Julian Braha Date: Sat, 18 Apr 2026 00:06:52 +0100 Subject: [PATCH 13/25] cpufreq: clean up dead dependencies on X86 in Kconfig The Kconfig in the parent directory already has an 'if X86' condition wrapping the inclusion of this file, meaning that each of the individual 'depends on' statements in this file is a duplicate dependency (dead code). Leave the outer 'if X86...endif' and remove the individual 'depends on X86' statement from each option. This dead code was found by kconfirm, a static analysis tool for Kconfig. Signed-off-by: Julian Braha Reviewed-by: Mario Limonciello (AMD) [ rjw: Changelog edits ] Link: https://patch.msgid.link/20260417230652.305414-1-julianbraha@gmail.com Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/Kconfig.x86 | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/Kconfig.x86 b/drivers/cpufreq/Kconfig.x86 index a9093cd5e5d1..865099926577 100644 --- a/drivers/cpufreq/Kconfig.x86 +++ b/drivers/cpufreq/Kconfig.x86 @@ -5,7 +5,6 @@ config X86_INTEL_PSTATE bool "Intel P state control" - depends on X86 select ACPI_PROCESSOR if ACPI select ACPI_CPPC_LIB if X86_64 && ACPI && SCHED_MC_PRIO select CPU_FREQ_GOV_PERFORMANCE @@ -36,7 +35,7 @@ config X86_PCC_CPUFREQ config X86_AMD_PSTATE bool "AMD Processor P-State driver" - depends on X86 && ACPI + depends on ACPI select ACPI_PROCESSOR select ACPI_CPPC_LIB if X86_64 select CPU_FREQ_GOV_SCHEDUTIL if SMP @@ -72,7 +71,7 @@ config X86_AMD_PSTATE_DEFAULT_MODE config X86_AMD_PSTATE_UT tristate "selftest for AMD Processor P-State driver" - depends on X86 && ACPI_PROCESSOR + depends on ACPI_PROCESSOR depends on X86_AMD_PSTATE default n help From 6fb302d2109c84d33e988d5b6e803dc8eae6e21e Mon Sep 17 00:00:00 2001 From: Sean Young Date: Thu, 7 May 2026 10:01:04 +0100 Subject: [PATCH 14/25] cpufreq: elanfreq: Drop support for AMD Elan SC4* Since commit 8b793a92d862 ("x86/cpu: Remove M486/M486SX/ELAN support"), the AMD Elan SC4* is no longer supported, so the CPU frequency driver is no longer needed. Signed-off-by: Sean Young Reviewed-by: Zhongqiu Han Acked-by: Viresh Kumar [ rjw: Changelog tweaks ] Link: https://patch.msgid.link/20260507090107.10113-1-sean@mess.org Signed-off-by: Rafael J. Wysocki --- .../admin-guide/kernel-parameters.txt | 4 - drivers/cpufreq/Kconfig.x86 | 15 -- drivers/cpufreq/Makefile | 1 - drivers/cpufreq/elanfreq.c | 226 ------------------ 4 files changed, 246 deletions(-) delete mode 100644 drivers/cpufreq/elanfreq.c diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index 4d0f545fb3ec..7ab0e58c4aa9 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -1669,10 +1669,6 @@ Kernel parameters very early in the boot process. For early debugging via a serial port see kgdboc_earlycon instead. - elanfreq= [X86-32] - See comment before function elanfreq_setup() in - arch/x86/kernel/cpu/cpufreq/elanfreq.c. - elfcorehdr=[size[KMG]@]offset[KMG] [PPC,SH,X86,S390,EARLY] Specifies physical address of start of kernel core image elf header and optionally the size. Generally diff --git a/drivers/cpufreq/Kconfig.x86 b/drivers/cpufreq/Kconfig.x86 index 865099926577..46b9742a1688 100644 --- a/drivers/cpufreq/Kconfig.x86 +++ b/drivers/cpufreq/Kconfig.x86 @@ -113,21 +113,6 @@ config X86_ACPI_CPUFREQ_CPB By enabling this option the acpi_cpufreq driver provides the old entry in addition to the new boost ones, for compatibility reasons. -config ELAN_CPUFREQ - tristate "AMD Elan SC400 and SC410" - depends on MELAN - help - This adds the CPUFreq driver for AMD Elan SC400 and SC410 - processors. - - You need to specify the processor maximum speed as boot - parameter: elanfreq=maxspeed (in kHz) or as module - parameter "max_freq". - - For details, take a look at . - - If in doubt, say N. - config SC520_CPUFREQ tristate "AMD Elan SC520" depends on MELAN diff --git a/drivers/cpufreq/Makefile b/drivers/cpufreq/Makefile index 385c9fcc65c6..8056b8f2200b 100644 --- a/drivers/cpufreq/Makefile +++ b/drivers/cpufreq/Makefile @@ -40,7 +40,6 @@ obj-$(CONFIG_X86_POWERNOW_K6) += powernow-k6.o obj-$(CONFIG_X86_POWERNOW_K7) += powernow-k7.o obj-$(CONFIG_X86_LONGHAUL) += longhaul.o obj-$(CONFIG_X86_E_POWERSAVER) += e_powersaver.o -obj-$(CONFIG_ELAN_CPUFREQ) += elanfreq.o obj-$(CONFIG_SC520_CPUFREQ) += sc520_freq.o obj-$(CONFIG_X86_LONGRUN) += longrun.o obj-$(CONFIG_X86_GX_SUSPMOD) += gx-suspmod.o diff --git a/drivers/cpufreq/elanfreq.c b/drivers/cpufreq/elanfreq.c deleted file mode 100644 index fc5a58088b35..000000000000 --- a/drivers/cpufreq/elanfreq.c +++ /dev/null @@ -1,226 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -/* - * elanfreq: cpufreq driver for the AMD ELAN family - * - * (c) Copyright 2002 Robert Schwebel - * - * Parts of this code are (c) Sven Geggus - * - * All Rights Reserved. - * - * 2002-02-13: - initial revision for 2.4.18-pre9 by Robert Schwebel - */ - -#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt - -#include -#include -#include - -#include -#include - -#include -#include -#include - -#define REG_CSCIR 0x22 /* Chip Setup and Control Index Register */ -#define REG_CSCDR 0x23 /* Chip Setup and Control Data Register */ - -/* Module parameter */ -static int max_freq; - -struct s_elan_multiplier { - int clock; /* frequency in kHz */ - int val40h; /* PMU Force Mode register */ - int val80h; /* CPU Clock Speed Register */ -}; - -/* - * It is important that the frequencies - * are listed in ascending order here! - */ -static struct s_elan_multiplier elan_multiplier[] = { - {1000, 0x02, 0x18}, - {2000, 0x02, 0x10}, - {4000, 0x02, 0x08}, - {8000, 0x00, 0x00}, - {16000, 0x00, 0x02}, - {33000, 0x00, 0x04}, - {66000, 0x01, 0x04}, - {99000, 0x01, 0x05} -}; - -static struct cpufreq_frequency_table elanfreq_table[] = { - {0, 0, 1000}, - {0, 1, 2000}, - {0, 2, 4000}, - {0, 3, 8000}, - {0, 4, 16000}, - {0, 5, 33000}, - {0, 6, 66000}, - {0, 7, 99000}, - {0, 0, CPUFREQ_TABLE_END}, -}; - - -/** - * elanfreq_get_cpu_frequency: determine current cpu speed - * - * Finds out at which frequency the CPU of the Elan SOC runs - * at the moment. Frequencies from 1 to 33 MHz are generated - * the normal way, 66 and 99 MHz are called "Hyperspeed Mode" - * and have the rest of the chip running with 33 MHz. - */ - -static unsigned int elanfreq_get_cpu_frequency(unsigned int cpu) -{ - u8 clockspeed_reg; /* Clock Speed Register */ - - local_irq_disable(); - outb_p(0x80, REG_CSCIR); - clockspeed_reg = inb_p(REG_CSCDR); - local_irq_enable(); - - if ((clockspeed_reg & 0xE0) == 0xE0) - return 0; - - /* Are we in CPU clock multiplied mode (66/99 MHz)? */ - if ((clockspeed_reg & 0xE0) == 0xC0) { - if ((clockspeed_reg & 0x01) == 0) - return 66000; - else - return 99000; - } - - /* 33 MHz is not 32 MHz... */ - if ((clockspeed_reg & 0xE0) == 0xA0) - return 33000; - - return (1<<((clockspeed_reg & 0xE0) >> 5)) * 1000; -} - - -static int elanfreq_target(struct cpufreq_policy *policy, - unsigned int state) -{ - /* - * Access to the Elan's internal registers is indexed via - * 0x22: Chip Setup & Control Register Index Register (CSCI) - * 0x23: Chip Setup & Control Register Data Register (CSCD) - * - */ - - /* - * 0x40 is the Power Management Unit's Force Mode Register. - * Bit 6 enables Hyperspeed Mode (66/100 MHz core frequency) - */ - - local_irq_disable(); - outb_p(0x40, REG_CSCIR); /* Disable hyperspeed mode */ - outb_p(0x00, REG_CSCDR); - local_irq_enable(); /* wait till internal pipelines and */ - udelay(1000); /* buffers have cleaned up */ - - local_irq_disable(); - - /* now, set the CPU clock speed register (0x80) */ - outb_p(0x80, REG_CSCIR); - outb_p(elan_multiplier[state].val80h, REG_CSCDR); - - /* now, the hyperspeed bit in PMU Force Mode Register (0x40) */ - outb_p(0x40, REG_CSCIR); - outb_p(elan_multiplier[state].val40h, REG_CSCDR); - udelay(10000); - local_irq_enable(); - - return 0; -} -/* - * Module init and exit code - */ - -static int elanfreq_cpu_init(struct cpufreq_policy *policy) -{ - struct cpuinfo_x86 *c = &cpu_data(0); - struct cpufreq_frequency_table *pos; - - /* capability check */ - if ((c->x86_vendor != X86_VENDOR_AMD) || - (c->x86 != 4) || (c->x86_model != 10)) - return -ENODEV; - - /* max freq */ - if (!max_freq) - max_freq = elanfreq_get_cpu_frequency(0); - - /* table init */ - cpufreq_for_each_entry(pos, elanfreq_table) - if (pos->frequency > max_freq) - pos->frequency = CPUFREQ_ENTRY_INVALID; - - policy->freq_table = elanfreq_table; - return 0; -} - - -#ifndef MODULE -/** - * elanfreq_setup - elanfreq command line parameter parsing - * - * elanfreq command line parameter. Use: - * elanfreq=66000 - * to set the maximum CPU frequency to 66 MHz. Note that in - * case you do not give this boot parameter, the maximum - * frequency will fall back to _current_ CPU frequency which - * might be lower. If you build this as a module, use the - * max_freq module parameter instead. - */ -static int __init elanfreq_setup(char *str) -{ - max_freq = simple_strtoul(str, &str, 0); - pr_warn("You're using the deprecated elanfreq command line option. Use elanfreq.max_freq instead, please!\n"); - return 1; -} -__setup("elanfreq=", elanfreq_setup); -#endif - - -static struct cpufreq_driver elanfreq_driver = { - .get = elanfreq_get_cpu_frequency, - .flags = CPUFREQ_NO_AUTO_DYNAMIC_SWITCHING, - .verify = cpufreq_generic_frequency_table_verify, - .target_index = elanfreq_target, - .init = elanfreq_cpu_init, - .name = "elanfreq", -}; - -static const struct x86_cpu_id elan_id[] = { - X86_MATCH_VENDOR_FAM_MODEL(AMD, 4, 10, NULL), - {} -}; -MODULE_DEVICE_TABLE(x86cpu, elan_id); - -static int __init elanfreq_init(void) -{ - if (!x86_match_cpu(elan_id)) - return -ENODEV; - return cpufreq_register_driver(&elanfreq_driver); -} - - -static void __exit elanfreq_exit(void) -{ - cpufreq_unregister_driver(&elanfreq_driver); -} - - -module_param(max_freq, int, 0444); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Robert Schwebel , " - "Sven Geggus "); -MODULE_DESCRIPTION("cpufreq driver for AMD's Elan CPUs"); - -module_init(elanfreq_init); -module_exit(elanfreq_exit); From 08a859927e0c62e9a03a75f508baa6cbfbbef3c9 Mon Sep 17 00:00:00 2001 From: Yohei Kojima Date: Fri, 22 May 2026 09:15:58 +0900 Subject: [PATCH 15/25] cpufreq: intel_pstate: Improve warning message on HWP-disabled hybrid CPUs Improve warning message on HWP-disabled hybrid processors to state that intel_pstate requires HWP to be enabled on such processors [1]. Previously it warned that "intel_pstate: CPU model not supported", but it was misleading. Link: https://docs.kernel.org/admin-guide/pm/intel_pstate.html [1] Signed-off-by: Yohei Kojima [ rjw: Changelog tweaks ] Link: https://patch.msgid.link/87f69971a9bf89fb4b51f128e8ae519cbaf5894e.1779406085.git.yohei.kojima@sony.com Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/intel_pstate.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 0fbbdbd5765c..8dc22a65e025 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -3826,6 +3826,12 @@ static int __init intel_pstate_init(void) if (no_load) return -ENODEV; + id = x86_match_cpu(intel_hybrid_scaling_factor); + if (id) { + pr_info("HWP-disabled hybrid CPU is not supported\n"); + return -ENODEV; + } + id = x86_match_cpu(intel_pstate_cpu_ids); if (!id) { pr_info("CPU model not supported\n"); From 91f5d698478f3d07230cf9ca4dfaf67e0316a53d Mon Sep 17 00:00:00 2001 From: Zhongqiu Han Date: Sun, 19 Apr 2026 21:26:53 +0800 Subject: [PATCH 16/25] cpufreq: governor: Fix data races on per-CPU idle/nice baselines gov_update_cpu_data() resets per-CPU prev_cpu_idle for every CPU in the governed domain, and conditionally resets prev_cpu_nice when ignore_nice_load is set. It is called from sysfs store callbacks (e.g. ignore_nice_load_store) which run under attr_set->update_lock, held by the surrounding governor_store(). Concurrently, dbs_work_handler() calls gov->gov_dbs_update() (which calls dbs_update()) under policy_dbs->update_mutex. dbs_update() both reads and writes the same prev_cpu_idle / prev_cpu_nice fields. The potential race path is: Path A (sysfs write, holds attr_set->update_lock only): governor_store() mutex_lock(&attr_set->update_lock) ignore_nice_load_store() dbs_data->ignore_nice_load = input gov_update_cpu_data(dbs_data) list_for_each_entry(policy_dbs, ...) for_each_cpu(j, ...) j_cdbs->prev_cpu_idle = get_cpu_idle_time(...) /* write */ j_cdbs->prev_cpu_nice = kcpustat_field(...) /* write */ mutex_unlock(&attr_set->update_lock) Path B (work queue, holds policy_dbs->update_mutex only): dbs_work_handler() mutex_lock(&policy_dbs->update_mutex) gov->gov_dbs_update(policy) dbs_update() for_each_cpu(j, policy->cpus) idle_time = cur - j_cdbs->prev_cpu_idle /* read */ j_cdbs->prev_cpu_idle = cur_idle_time /* write */ idle_time += cur_nice - j_cdbs->prev_cpu_nice /* read */ j_cdbs->prev_cpu_nice = cur_nice /* write */ mutex_unlock(&policy_dbs->update_mutex) Because attr_set->update_lock and policy_dbs->update_mutex are two completely independent locks, the two paths are not mutually exclusive. This results in a data race on cpu_dbs_info.prev_cpu_idle and cpu_dbs_info.prev_cpu_nice. Fix this by also acquiring policy_dbs->update_mutex in gov_update_cpu_data() for each policy, so that path A participates in the mutual exclusion already established by dbs_work_handler(). Also update the function comment to accurately reflect the two-level locking contract. Additionally, cpufreq_dbs_governor_start() initializes prev_cpu_idle using io_busy read from dbs_data->io_is_busy without holding policy_dbs->update_mutex. A concurrent io_is_busy_store() can update io_is_busy and call gov_update_cpu_data(), which writes prev_cpu_idle with the new value under the mutex. cpufreq_dbs_governor_start() then overwrites prev_cpu_idle with the stale io_busy value, leaving the baseline inconsistent with the tunable. Fix this by reading io_busy inside the mutex. The root of this race dates back to the original ondemand/conservative governors. Before commit ee88415caf73 ("[CPUFREQ] Cleanup locking in conservative governor") and commit 5a75c82828e7 ("[CPUFREQ] Cleanup locking in ondemand governor"), all accesses to prev_cpu_idle and prev_cpu_nice in cpufreq_governor_dbs() (path X), store_ignore_nice_load() /io_is_busy_store() (path Y), and do_dbs_timer() (path Z) were serialised by the same dbs_mutex, so no race existed. Those two commits switched do_dbs_timer() from dbs_mutex to a per-policy/per-cpu timer_mutex to reduce lock contention, but left path Y (store) still holding dbs_mutex. As a result, path Y (store) and path Z (do_dbs_timer) no longer shared a common lock, introducing a potential race on prev_cpu_idle/prev_cpu_nice between path Y (store) and dbs_check_cpu(). Commit 326c86deaed54a ("[CPUFREQ] Remove unneeded locks") then removed dbs_mutex from store_ignore_nice_load()/io_is_busy_store() entirely, introducing an additional potential race between path Y (now lockless) and cpufreq_governor_dbs() (path X, still holding dbs_mutex), while the race between path Y and path Z remained. Fixes: ee88415caf736b ("[CPUFREQ] Cleanup locking in conservative governor") Fixes: 5a75c82828e7c0 ("[CPUFREQ] Cleanup locking in ondemand governor") Fixes: 326c86deaed54a ("[CPUFREQ] Remove unneeded locks") Signed-off-by: Zhongqiu Han Link: https://patch.msgid.link/20260419132655.3800673-2-zhongqiu.han@oss.qualcomm.com Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq_governor.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index 86f35e451914..fc6f705c5a9c 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -90,7 +90,8 @@ EXPORT_SYMBOL_GPL(sampling_rate_store); * (that may be a single policy or a bunch of them if governor tunables are * system-wide). * - * Call under the @dbs_data mutex. + * Call under the @dbs_data->attr_set.update_lock. The per-policy + * update_mutex is acquired and released internally for each policy. */ void gov_update_cpu_data(struct dbs_data *dbs_data) { @@ -99,6 +100,7 @@ void gov_update_cpu_data(struct dbs_data *dbs_data) list_for_each_entry(policy_dbs, &dbs_data->attr_set.policy_list, list) { unsigned int j; + mutex_lock(&policy_dbs->update_mutex); for_each_cpu(j, policy_dbs->policy->cpus) { struct cpu_dbs_info *j_cdbs = &per_cpu(cpu_dbs, j); @@ -107,6 +109,7 @@ void gov_update_cpu_data(struct dbs_data *dbs_data) if (dbs_data->ignore_nice_load) j_cdbs->prev_cpu_nice = kcpustat_field(&kcpustat_cpu(j), CPUTIME_NICE, j); } + mutex_unlock(&policy_dbs->update_mutex); } } EXPORT_SYMBOL_GPL(gov_update_cpu_data); @@ -527,8 +530,9 @@ int cpufreq_dbs_governor_start(struct cpufreq_policy *policy) sampling_rate = dbs_data->sampling_rate; ignore_nice = dbs_data->ignore_nice_load; - io_busy = dbs_data->io_is_busy; + mutex_lock(&policy_dbs->update_mutex); + io_busy = dbs_data->io_is_busy; for_each_cpu(j, policy->cpus) { struct cpu_dbs_info *j_cdbs = &per_cpu(cpu_dbs, j); @@ -541,6 +545,7 @@ int cpufreq_dbs_governor_start(struct cpufreq_policy *policy) if (ignore_nice) j_cdbs->prev_cpu_nice = kcpustat_field(&kcpustat_cpu(j), CPUTIME_NICE, j); } + mutex_unlock(&policy_dbs->update_mutex); gov->start(policy); From 24fc5870808dea4290e9563746cb5f2146043c6c Mon Sep 17 00:00:00 2001 From: Zhongqiu Han Date: Sun, 19 Apr 2026 21:26:54 +0800 Subject: [PATCH 17/25] cpufreq: governor: Fix stale prev_cpu_nice spike when enabling ignore_nice_load When ignore_nice_load is toggled from 0 to 1 via sysfs, dbs_update() may run concurrently and observe the new tunable value while prev_cpu_nice still holds a stale baseline, producing a spurious massive idle_time that results in an incorrect CPU load value. The race can be illustrated with two concurrent paths: Path A (sysfs write, holds attr_set->update_lock): governor_store() mutex_lock(&attr_set->update_lock) ignore_nice_load_store() dbs_data->ignore_nice_load = 1 /* (A1) */ gov_update_cpu_data(dbs_data) mutex_lock(&policy_dbs->update_mutex) /* (A2) */ j_cdbs->prev_cpu_nice = kcpustat_field(...) mutex_unlock(&policy_dbs->update_mutex) mutex_unlock(&attr_set->update_lock) Path B (work queue, wins the race between A1 and A2): dbs_work_handler() mutex_lock(&policy_dbs->update_mutex) /* acquired before A2 */ dbs_update() ignore_nice = dbs_data->ignore_nice_load /* sees new value: 1 */ cur_nice = kcpustat_field(...) idle_time += div_u64(cur_nice - j_cdbs->prev_cpu_nice, ..) /* stale */ j_cdbs->prev_cpu_nice = cur_nice mutex_unlock(&policy_dbs->update_mutex) Fix this by unconditionally sampling cur_nice and advancing prev_cpu_nice in dbs_update() on every call, regardless of ignore_nice. With prev_cpu_nice always reflecting the most recent sample, enabling ignore_nice_load can never produce a stale-baseline spike: the delta will always be the nice time accumulated in the last sampling interval, not since boot. The additional kcpustat_field() call per CPU per sample is negligible given that the sampling path already reads idle and load accounting. To keep prev_cpu_nice handling consistent with the always-tracking semantics introduced above: - gov_update_cpu_data() unconditionally resets prev_cpu_nice alongside prev_cpu_idle, so both baselines share the same timestamp when io_is_busy changes. This prevents an interval mismatch between idle_time and nice_delta on the next dbs_update() when ignore_nice_load is enabled. - cpufreq_dbs_governor_start() unconditionally initializes prev_cpu_nice so the baseline is always valid from the first dbs_update() call; remove the ignore_nice guard and the now-unused ignore_nice variable. Fixes: ee88415caf736b ("[CPUFREQ] Cleanup locking in conservative governor") Fixes: 5a75c82828e7c0 ("[CPUFREQ] Cleanup locking in ondemand governor") Fixes: 326c86deaed54a ("[CPUFREQ] Remove unneeded locks") Signed-off-by: Zhongqiu Han Link: https://patch.msgid.link/20260419132655.3800673-3-zhongqiu.han@oss.qualcomm.com Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq_governor.c | 33 +++++++++++++++++++----------- 1 file changed, 21 insertions(+), 12 deletions(-) diff --git a/drivers/cpufreq/cpufreq_governor.c b/drivers/cpufreq/cpufreq_governor.c index fc6f705c5a9c..8a85bd32defe 100644 --- a/drivers/cpufreq/cpufreq_governor.c +++ b/drivers/cpufreq/cpufreq_governor.c @@ -92,6 +92,12 @@ EXPORT_SYMBOL_GPL(sampling_rate_store); * * Call under the @dbs_data->attr_set.update_lock. The per-policy * update_mutex is acquired and released internally for each policy. + * + * Note: prev_cpu_nice is reset here unconditionally alongside prev_cpu_idle. + * When io_is_busy changes, both baselines must be advanced to the same + * timestamp so that the next dbs_update() computes idle_time and nice_delta + * over the same interval, preventing an artificially inflated idle_time when + * ignore_nice_load is enabled. */ void gov_update_cpu_data(struct dbs_data *dbs_data) { @@ -106,8 +112,7 @@ void gov_update_cpu_data(struct dbs_data *dbs_data) j_cdbs->prev_cpu_idle = get_cpu_idle_time(j, &j_cdbs->prev_update_time, dbs_data->io_is_busy); - if (dbs_data->ignore_nice_load) - j_cdbs->prev_cpu_nice = kcpustat_field(&kcpustat_cpu(j), CPUTIME_NICE, j); + j_cdbs->prev_cpu_nice = kcpustat_field(&kcpustat_cpu(j), CPUTIME_NICE, j); } mutex_unlock(&policy_dbs->update_mutex); } @@ -121,6 +126,7 @@ unsigned int dbs_update(struct cpufreq_policy *policy) unsigned int ignore_nice = dbs_data->ignore_nice_load; unsigned int max_load = 0, idle_periods = UINT_MAX; unsigned int sampling_rate, io_busy, j; + u64 cur_nice; /* * Sometimes governors may use an additional multiplier to increase @@ -167,12 +173,18 @@ unsigned int dbs_update(struct cpufreq_policy *policy) j_cdbs->prev_cpu_idle = cur_idle_time; - if (ignore_nice) { - u64 cur_nice = kcpustat_field(&kcpustat_cpu(j), CPUTIME_NICE, j); - + /* + * Always sample cur_nice and advance prev_cpu_nice, regardless + * of ignore_nice. This keeps prev_cpu_nice current so that + * enabling ignore_nice_load via sysfs never produces a + * stale-baseline spike (the delta will be at most one sampling + * interval of accumulated nice time, not since boot). + */ + cur_nice = kcpustat_field(&kcpustat_cpu(j), CPUTIME_NICE, j); + if (ignore_nice) idle_time += div_u64(cur_nice - j_cdbs->prev_cpu_nice, NSEC_PER_USEC); - j_cdbs->prev_cpu_nice = cur_nice; - } + + j_cdbs->prev_cpu_nice = cur_nice; if (unlikely(!time_elapsed)) { /* @@ -519,7 +531,7 @@ int cpufreq_dbs_governor_start(struct cpufreq_policy *policy) struct dbs_governor *gov = dbs_governor_of(policy); struct policy_dbs_info *policy_dbs = policy->governor_data; struct dbs_data *dbs_data = policy_dbs->dbs_data; - unsigned int sampling_rate, ignore_nice, j; + unsigned int sampling_rate, j; unsigned int io_busy; if (!policy->cur) @@ -529,7 +541,6 @@ int cpufreq_dbs_governor_start(struct cpufreq_policy *policy) policy_dbs->rate_mult = 1; sampling_rate = dbs_data->sampling_rate; - ignore_nice = dbs_data->ignore_nice_load; mutex_lock(&policy_dbs->update_mutex); io_busy = dbs_data->io_is_busy; @@ -541,9 +552,7 @@ int cpufreq_dbs_governor_start(struct cpufreq_policy *policy) * Make the first invocation of dbs_update() compute the load. */ j_cdbs->prev_load = 0; - - if (ignore_nice) - j_cdbs->prev_cpu_nice = kcpustat_field(&kcpustat_cpu(j), CPUTIME_NICE, j); + j_cdbs->prev_cpu_nice = kcpustat_field(&kcpustat_cpu(j), CPUTIME_NICE, j); } mutex_unlock(&policy_dbs->update_mutex); From ef92354d0e6261e0ed792666972ef0b257cc0d8f Mon Sep 17 00:00:00 2001 From: Imran Shaik Date: Fri, 22 May 2026 20:46:22 +0530 Subject: [PATCH 18/25] dt-bindings: cpufreq: Document Qualcomm Shikra SoC EPSS The Qualcomm Shikra cpufreq hardware is functionally identical to EPSS, but supports only up to 12 frequency lookup table (LUT) entries. Introduce Shikra specific bindings to represent this constrained EPSS variant. Reviewed-by: Krzysztof Kozlowski Signed-off-by: Imran Shaik Signed-off-by: Viresh Kumar --- .../bindings/cpufreq/qcom,shikra-epss.yaml | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 Documentation/devicetree/bindings/cpufreq/qcom,shikra-epss.yaml diff --git a/Documentation/devicetree/bindings/cpufreq/qcom,shikra-epss.yaml b/Documentation/devicetree/bindings/cpufreq/qcom,shikra-epss.yaml new file mode 100644 index 000000000000..1a3105e86980 --- /dev/null +++ b/Documentation/devicetree/bindings/cpufreq/qcom,shikra-epss.yaml @@ -0,0 +1,96 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/cpufreq/qcom,shikra-epss.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Qualcomm Shikra SoC EPSS + +maintainers: + - Imran Shaik + - Taniya Das + +description: | + EPSS is a hardware engine used by some Qualcomm SoCs to manage + frequency in hardware. It is capable of controlling frequency for + multiple clusters. + + The Qualcomm Shikra SoC EPSS supports up to 12 frequency lookup table + (LUT) entries. + +properties: + compatible: + enum: + - qcom,shikra-epss + + reg: + items: + - description: Frequency domain 0 register region + - description: Frequency domain 1 register region + + reg-names: + items: + - const: freq-domain0 + - const: freq-domain1 + + clocks: + items: + - description: XO Clock + - description: GPLL0 Clock + + clock-names: + items: + - const: xo + - const: alternate + + interrupts: + items: + - description: IRQ line for DCVSH 0 + - description: IRQ line for DCVSH 1 + + interrupt-names: + items: + - const: dcvsh-irq-0 + - const: dcvsh-irq-1 + + '#freq-domain-cells': + const: 1 + + '#clock-cells': + const: 1 + +required: + - compatible + - reg + - clocks + - clock-names + - interrupts + - interrupt-names + - '#freq-domain-cells' + - '#clock-cells' + +additionalProperties: false + +examples: + - | + #include + #include + + soc { + #address-cells = <1>; + #size-cells = <1>; + + cpufreq@fd91000 { + compatible = "qcom,shikra-epss"; + reg = <0x0fd91000 0x1000>, <0x0fd92000 0x1000>; + reg-names = "freq-domain0", "freq-domain1"; + clocks = <&rpmcc RPM_SMD_XO_CLK_SRC>, <&gpll0>; + clock-names = "xo", "alternate"; + interrupts = , + ; + interrupt-names = "dcvsh-irq-0", "dcvsh-irq-1"; + #freq-domain-cells = <1>; + #clock-cells = <1>; + }; + }; +... From 4b5d3b847218b466527fb98990e193874081e0c7 Mon Sep 17 00:00:00 2001 From: Taniya Das Date: Fri, 22 May 2026 20:46:23 +0530 Subject: [PATCH 19/25] cpufreq: qcom: Add cpufreq scaling support for Qualcomm Shikra SoC The Qualcomm Shikra cpufreq hardware is functionally identical to EPSS, but supports only up to 12 frequency lookup table (LUT) entries. When all 12 entries are populated, the existing repetitive LUT entry check may read beyond valid entries and expose incorrect frequencies. Hence, introduce shikra_epss_soc_data that reuses EPSS configuration with appropriate LUT entries limit. Signed-off-by: Taniya Das Reviewed-by: Konrad Dybcio Signed-off-by: Imran Shaik Signed-off-by: Viresh Kumar --- drivers/cpufreq/qcom-cpufreq-hw.c | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/drivers/cpufreq/qcom-cpufreq-hw.c b/drivers/cpufreq/qcom-cpufreq-hw.c index ef19faedbfec..874ff3fb9d97 100644 --- a/drivers/cpufreq/qcom-cpufreq-hw.c +++ b/drivers/cpufreq/qcom-cpufreq-hw.c @@ -1,6 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* * Copyright (c) 2018, The Linux Foundation. All rights reserved. + * Copyright (c) Qualcomm Technologies, Inc. and/or its subsidiaries. */ #include @@ -40,6 +41,7 @@ struct qcom_cpufreq_soc_data { u32 reg_intr_clr; u32 reg_current_vote; u32 reg_perf_state; + u32 lut_max_entries; u8 lut_row_size; }; @@ -156,7 +158,7 @@ static unsigned int qcom_cpufreq_get_freq(struct cpufreq_policy *policy) soc_data = qcom_cpufreq.soc_data; index = readl_relaxed(data->base + soc_data->reg_perf_state); - index = min(index, LUT_MAX_ENTRIES - 1); + index = min(index, soc_data->lut_max_entries - 1); return policy->freq_table[index].frequency; } @@ -211,7 +213,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, struct qcom_cpufreq_data *drv_data = policy->driver_data; const struct qcom_cpufreq_soc_data *soc_data = qcom_cpufreq.soc_data; - table = kzalloc_objs(*table, LUT_MAX_ENTRIES + 1); + table = kzalloc_objs(*table, soc_data->lut_max_entries + 1); if (!table) return -ENOMEM; @@ -236,7 +238,7 @@ static int qcom_cpufreq_hw_read_lut(struct device *cpu_dev, icc_scaling_enabled = false; } - for (i = 0; i < LUT_MAX_ENTRIES; i++) { + for (i = 0; i < soc_data->lut_max_entries; i++) { data = readl_relaxed(drv_data->base + soc_data->reg_freq_lut + i * soc_data->lut_row_size); src = FIELD_GET(LUT_SRC, data); @@ -405,6 +407,7 @@ static const struct qcom_cpufreq_soc_data qcom_soc_data = { .reg_current_vote = 0x704, .reg_perf_state = 0x920, .lut_row_size = 32, + .lut_max_entries = LUT_MAX_ENTRIES, }; static const struct qcom_cpufreq_soc_data epss_soc_data = { @@ -416,11 +419,25 @@ static const struct qcom_cpufreq_soc_data epss_soc_data = { .reg_intr_clr = 0x308, .reg_perf_state = 0x320, .lut_row_size = 4, + .lut_max_entries = LUT_MAX_ENTRIES, +}; + +static const struct qcom_cpufreq_soc_data shikra_epss_soc_data = { + .reg_enable = 0x0, + .reg_domain_state = 0x20, + .reg_dcvs_ctrl = 0xb0, + .reg_freq_lut = 0x100, + .reg_volt_lut = 0x200, + .reg_intr_clr = 0x308, + .reg_perf_state = 0x320, + .lut_row_size = 4, + .lut_max_entries = 12, }; static const struct of_device_id qcom_cpufreq_hw_match[] = { { .compatible = "qcom,cpufreq-hw", .data = &qcom_soc_data }, { .compatible = "qcom,cpufreq-epss", .data = &epss_soc_data }, + { .compatible = "qcom,shikra-epss", .data = &shikra_epss_soc_data }, {} }; MODULE_DEVICE_TABLE(of, qcom_cpufreq_hw_match); From b7a76f38ef3500d53ec87489c445fec8100c7a6b Mon Sep 17 00:00:00 2001 From: Akashdeep Kaur Date: Wed, 3 Jun 2026 12:54:38 +0530 Subject: [PATCH 20/25] cpufreq: ti: Add EPROBE_DEFER for K3 SoCs On K3 SoCs, ti-cpufreq relies on k3-socinfo to register the SoC device before soc_device_match() can return valid revision information. If ti-cpufreq probes before k3-socinfo, soc_device_match() returns NULL, leading to incorrect CPU frequency scaling behavior. Add a needs_k3_socinfo flag to ti_cpufreq_soc_data (similar to the existing multi_regulator pattern) to defer probe when k3-socinfo hasn't registered the SoC device yet. Signed-off-by: Akashdeep Kaur Reviewed-by: Zhongqiu Han Signed-off-by: Viresh Kumar --- drivers/cpufreq/ti-cpufreq.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/drivers/cpufreq/ti-cpufreq.c b/drivers/cpufreq/ti-cpufreq.c index a01abc1622eb..0f96492341f2 100644 --- a/drivers/cpufreq/ti-cpufreq.c +++ b/drivers/cpufreq/ti-cpufreq.c @@ -99,6 +99,7 @@ struct ti_cpufreq_soc_data { unsigned long efuse_shift; unsigned long rev_offset; bool multi_regulator; + bool needs_k3_socinfo; /* Backward compatibility hack: Might have missing syscon */ #define TI_QUIRK_SYSCON_MAY_BE_MISSING 0x1 /* Backward compatibility hack: new syscon size is 1 register wide */ @@ -347,6 +348,7 @@ static struct ti_cpufreq_soc_data am625_soc_data = { .efuse_mask = 0x07c0, .efuse_shift = 0x6, .multi_regulator = false, + .needs_k3_socinfo = true, .quirks = TI_QUIRK_SYSCON_IS_SINGLE_REG, }; @@ -356,6 +358,7 @@ static struct ti_cpufreq_soc_data am62a7_soc_data = { .efuse_mask = 0x07c0, .efuse_shift = 0x6, .multi_regulator = false, + .needs_k3_socinfo = true, }; static struct ti_cpufreq_soc_data am62l3_soc_data = { @@ -364,6 +367,7 @@ static struct ti_cpufreq_soc_data am62l3_soc_data = { .efuse_mask = 0x07c0, .efuse_shift = 0x6, .multi_regulator = false, + .needs_k3_socinfo = true, }; static struct ti_cpufreq_soc_data am62p5_soc_data = { @@ -372,6 +376,7 @@ static struct ti_cpufreq_soc_data am62p5_soc_data = { .efuse_mask = 0x07c0, .efuse_shift = 0x6, .multi_regulator = false, + .needs_k3_socinfo = true, }; /** @@ -443,6 +448,11 @@ static int ti_cpufreq_get_rev(struct ti_cpufreq_data *opp_data, goto done; } + /* Defer if k3-socinfo hasn't registered the SoC device yet */ + if (opp_data->soc_data->needs_k3_socinfo) + return dev_err_probe(opp_data->cpu_dev, -EPROBE_DEFER, + "SoC device not registered by k3-socinfo\n"); + ret = regmap_read(opp_data->syscon, opp_data->soc_data->rev_offset, &revision); if (opp_data->soc_data->quirks & TI_QUIRK_SYSCON_MAY_BE_MISSING && ret == -EIO) { From 03120e1425262c7d11f93362a88e579a1720390b Mon Sep 17 00:00:00 2001 From: Pengjie Zhang Date: Wed, 3 Jun 2026 13:56:35 +0800 Subject: [PATCH 21/25] cpufreq: Documentation: fix conservative governor freq_step description The conservative governor documentation incorrectly states that setting freq_step to 0 will use the default 5% frequency step. In reality, since at least commit 8e677ce83bf4 ("[CPUFREQ] conservative: fixup governor to function more like ondemand logic"), freq_step=0 has always caused the governor to skip frequency updates entirely. Correct the documentation to reflect the actual behavior: freq_step=0 disables frequency changes by the governor entirely. Fixes: 2a0e49279850 ("cpufreq: User/admin documentation update and consolidation") Signed-off-by: Pengjie Zhang Reviewed-by: Zhongqiu Han [ rjw: Subject adjustment ] Link: https://patch.msgid.link/20260603055635.1549943-1-zhangpengjie2@huawei.com Signed-off-by: Rafael J. Wysocki --- Documentation/admin-guide/pm/cpufreq.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/admin-guide/pm/cpufreq.rst b/Documentation/admin-guide/pm/cpufreq.rst index fdca59c955dc..8831cface585 100644 --- a/Documentation/admin-guide/pm/cpufreq.rst +++ b/Documentation/admin-guide/pm/cpufreq.rst @@ -586,8 +586,8 @@ This governor exposes the following tunables: 100 (5 by default). This is how much the frequency is allowed to change in one go. Setting - it to 0 will cause the default frequency step (5 percent) to be used - and setting it to 100 effectively causes the governor to periodically + it to 0 disables frequency changes by the governor entirely and setting + it to 100 effectively causes the governor to periodically switch the frequency between the ``scaling_min_freq`` and ``scaling_max_freq`` policy limits. From 70fa8a1860edc31190864cacfa6b964e19038109 Mon Sep 17 00:00:00 2001 From: Pierre Gondois Date: Thu, 28 May 2026 11:09:03 +0200 Subject: [PATCH 22/25] cpufreq: Extract cpufreq_policy_init_qos() function Extract the QoS-related logic from cpufreq_policy_online() to make that function shorter/simpler. The logic is placed in cpufreq_policy_init_qos() and is now executed right after the following calls: - cpufreq_driver->init() - cpufreq_table_validate_and_sort() This facilitats subsequent changes that will, in cpufreq_policy_init_qos(): - Set a default policy->min/max value for all policies. - Use the policy->min/max values set by drivers as initial request values for policy frequency QoS requests. No functional change. Signed-off-by: Pierre Gondois Reviewed-by: Zhongqiu Han Reviewed-by: Jie Zhan [ rjw: Changelog edits ] Link: https://patch.msgid.link/20260528090913.2759118-2-pierre.gondois@arm.com Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 53 +++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 4e78f4caab3a..584e4fcbf044 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1397,6 +1397,32 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy) kfree(policy); } +static int cpufreq_policy_init_qos(struct cpufreq_policy *policy) +{ + int ret; + + if (policy->boost_supported) { + ret = freq_qos_add_request(&policy->constraints, + &policy->boost_freq_req, + FREQ_QOS_MAX, + policy->cpuinfo.max_freq); + if (ret < 0) + return ret; + } + + ret = freq_qos_add_request(&policy->constraints, &policy->min_freq_req, + FREQ_QOS_MIN, FREQ_QOS_MIN_DEFAULT_VALUE); + if (ret < 0) + return ret; + + ret = freq_qos_add_request(&policy->constraints, &policy->max_freq_req, + FREQ_QOS_MAX, FREQ_QOS_MAX_DEFAULT_VALUE); + if (ret < 0) + return ret; + + return 0; +} + static int cpufreq_policy_online(struct cpufreq_policy *policy, unsigned int cpu, bool new_policy) { @@ -1442,6 +1468,12 @@ static int cpufreq_policy_online(struct cpufreq_policy *policy, if (ret) goto out_offline_policy; + if (new_policy) { + ret = cpufreq_policy_init_qos(policy); + if (ret < 0) + goto out_offline_policy; + } + /* related_cpus should at least include policy->cpus. */ cpumask_copy(policy->related_cpus, policy->cpus); } @@ -1458,27 +1490,6 @@ static int cpufreq_policy_online(struct cpufreq_policy *policy, add_cpu_dev_symlink(policy, j, get_cpu_device(j)); } - if (policy->boost_supported) { - ret = freq_qos_add_request(&policy->constraints, - &policy->boost_freq_req, - FREQ_QOS_MAX, - policy->cpuinfo.max_freq); - if (ret < 0) - goto out_destroy_policy; - } - - ret = freq_qos_add_request(&policy->constraints, - &policy->min_freq_req, FREQ_QOS_MIN, - FREQ_QOS_MIN_DEFAULT_VALUE); - if (ret < 0) - goto out_destroy_policy; - - ret = freq_qos_add_request(&policy->constraints, - &policy->max_freq_req, FREQ_QOS_MAX, - FREQ_QOS_MAX_DEFAULT_VALUE); - if (ret < 0) - goto out_destroy_policy; - blocking_notifier_call_chain(&cpufreq_policy_notifier_list, CPUFREQ_CREATE_POLICY, policy); } From 743a3cddb1f0aa45f0a277a494de653097c7a378 Mon Sep 17 00:00:00 2001 From: Pierre Gondois Date: Thu, 28 May 2026 11:09:04 +0200 Subject: [PATCH 23/25] cpufreq: Set default policy->min/max values for all drivers Some drivers set policy->min/max in their .init() callback, but cpufreq_set_policy() will ultimately override them through: cpufreq_policy_online() \-cpufreq_init_policy() \-cpufreq_set_policy() \-/* Set policy->min/max */ Thus the policy min/max values set by the drivers are only temporary. There is an exception if CPUFREQ_NEED_INITIAL_FREQ_CHECK is set and cpufreq_policy_online() calls __cpufreq_driver_target() which invokes cpufreq_driver->target(). To prepare for a subsequent change that will remove all initialization of policy->min/max in driver .init() callbacks if the min/max value is equal to the corresponding cpuinfo.min/max_freq, set default policy->min/max values in the core for all drivers. Signed-off-by: Pierre Gondois Reviewed-by: Jie Zhan [ rjw: Edits of the new comment and changelog ] Link: https://patch.msgid.link/20260528090913.2759118-3-pierre.gondois@arm.com Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/cpufreq.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 584e4fcbf044..253d3ba4217c 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1474,6 +1474,13 @@ static int cpufreq_policy_online(struct cpufreq_policy *policy, goto out_offline_policy; } + /* + * If the driver hasn't set policy->min/max, set them as they + * are used for clamping frequency requests. + */ + policy->min = policy->min ? policy->min : policy->cpuinfo.min_freq; + policy->max = policy->max ? policy->max : policy->cpuinfo.max_freq; + /* related_cpus should at least include policy->cpus. */ cpumask_copy(policy->related_cpus, policy->cpus); } From db80ad776cd216e833c410569ffc1359c1abc8bf Mon Sep 17 00:00:00 2001 From: Pierre Gondois Date: Thu, 28 May 2026 11:09:05 +0200 Subject: [PATCH 24/25] cpufreq: Remove driver default policy->min/max init Prior to commit 521223d8b3ec ("cpufreq: Fix initialization of min and max frequency QoS requests"), drivers were setting policy->min/max and these values were used as initial policy QoS constraints. After the above commit, these values are only used temporarily, as cpufreq_set_policy() ultimately overrides them through: cpufreq_policy_online() \-cpufreq_init_policy() \-cpufreq_set_policy() \-/* Set policy->min/max */ A subsequent change will restore the previous behavior allowing drivers to request special min/max QoS frequencies instead of FREQ_QOS_MIN_DEFAULT_VALUE and FREQ_QOS_MAX_DEFAULT_VALUE, respectively, if desired. For instance, the CPPC driver wants to advertise the lowest non-linear frequency that should be used as the initial minimum frequency QoS request. However, for this purpose, all drivers setting policy->min/max to policy->cpuinfo.min/max_freq, respectively, need to be updated so their initial policy->min/max settings don't limit the frequency scaling unnecessarily going forward (which would defeat the purpose of commit 521223d8b3ec), so do that. This does not actually alter the observed behavior of all of the drivers in question because setting policy->min/max to policy->cpuinfo.min/max_freq, respectively, is not necessary or even useful any more after a previous change ("cpufreq: Set default policy->min/max values for all drivers"). Signed-off-by: Pierre Gondois Acked-by: Jie Zhan [ rjw: Changelog rewrite ] Link: https://patch.msgid.link/20260528090913.2759118-4-pierre.gondois@arm.com Signed-off-by: Rafael J. Wysocki --- drivers/cpufreq/amd-pstate.c | 14 ++++++-------- drivers/cpufreq/cppc_cpufreq.c | 5 ++--- drivers/cpufreq/cpufreq-nforce2.c | 4 ++-- drivers/cpufreq/freq_table.c | 7 +++---- drivers/cpufreq/gx-suspmod.c | 2 +- drivers/cpufreq/intel_pstate.c | 3 --- drivers/cpufreq/pcc-cpufreq.c | 10 ++++------ drivers/cpufreq/pxa3xx-cpufreq.c | 5 ++--- drivers/cpufreq/sh-cpufreq.c | 6 ++---- drivers/cpufreq/virtual-cpufreq.c | 5 +---- 10 files changed, 23 insertions(+), 38 deletions(-) diff --git a/drivers/cpufreq/amd-pstate.c b/drivers/cpufreq/amd-pstate.c index 6f415860bd2a..07fc6ac3daf8 100644 --- a/drivers/cpufreq/amd-pstate.c +++ b/drivers/cpufreq/amd-pstate.c @@ -1080,10 +1080,9 @@ static int amd_pstate_cpu_init(struct cpufreq_policy *policy) perf = READ_ONCE(cpudata->perf); - policy->cpuinfo.min_freq = policy->min = perf_to_freq(perf, - cpudata->nominal_freq, - perf.lowest_perf); - policy->cpuinfo.max_freq = policy->max = cpudata->max_freq; + policy->cpuinfo.min_freq = perf_to_freq(perf, cpudata->nominal_freq, + perf.lowest_perf); + policy->cpuinfo.max_freq = cpudata->max_freq; policy->driver_data = cpudata; ret = amd_pstate_cppc_enable(policy); @@ -1909,10 +1908,9 @@ static int amd_pstate_epp_cpu_init(struct cpufreq_policy *policy) perf = READ_ONCE(cpudata->perf); - policy->cpuinfo.min_freq = policy->min = perf_to_freq(perf, - cpudata->nominal_freq, - perf.lowest_perf); - policy->cpuinfo.max_freq = policy->max = cpudata->max_freq; + policy->cpuinfo.min_freq = perf_to_freq(perf, cpudata->nominal_freq, + perf.lowest_perf); + policy->cpuinfo.max_freq = cpudata->max_freq; policy->driver_data = cpudata; ret = amd_pstate_cppc_enable(policy); diff --git a/drivers/cpufreq/cppc_cpufreq.c b/drivers/cpufreq/cppc_cpufreq.c index bf5175f7551c..f6cea0c54dd9 100644 --- a/drivers/cpufreq/cppc_cpufreq.c +++ b/drivers/cpufreq/cppc_cpufreq.c @@ -660,8 +660,6 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) * Section 8.4.7.1.1.5 of ACPI 6.1 spec) */ policy->min = cppc_perf_to_khz(caps, caps->lowest_nonlinear_perf); - policy->max = cppc_perf_to_khz(caps, policy->boost_enabled ? - caps->highest_perf : caps->nominal_perf); /* * Set cpuinfo.min_freq to Lowest to make the full range of performance @@ -669,7 +667,8 @@ static int cppc_cpufreq_cpu_init(struct cpufreq_policy *policy) * nonlinear perf */ policy->cpuinfo.min_freq = cppc_perf_to_khz(caps, caps->lowest_perf); - policy->cpuinfo.max_freq = policy->max; + policy->cpuinfo.max_freq = cppc_perf_to_khz(caps, policy->boost_enabled ? + caps->highest_perf : caps->nominal_perf); policy->transition_delay_us = cppc_cpufreq_get_transition_delay_us(cpu); policy->shared_type = cpu_data->shared_type; diff --git a/drivers/cpufreq/cpufreq-nforce2.c b/drivers/cpufreq/cpufreq-nforce2.c index fbbbe501cf2d..831102522ad6 100644 --- a/drivers/cpufreq/cpufreq-nforce2.c +++ b/drivers/cpufreq/cpufreq-nforce2.c @@ -355,8 +355,8 @@ static int nforce2_cpu_init(struct cpufreq_policy *policy) min_fsb = NFORCE2_MIN_FSB; /* cpuinfo and default policy values */ - policy->min = policy->cpuinfo.min_freq = min_fsb * fid * 100; - policy->max = policy->cpuinfo.max_freq = max_fsb * fid * 100; + policy->cpuinfo.min_freq = min_fsb * fid * 100; + policy->cpuinfo.max_freq = max_fsb * fid * 100; return 0; } diff --git a/drivers/cpufreq/freq_table.c b/drivers/cpufreq/freq_table.c index 5b364d8da4f9..ea994647abc8 100644 --- a/drivers/cpufreq/freq_table.c +++ b/drivers/cpufreq/freq_table.c @@ -49,16 +49,15 @@ int cpufreq_frequency_table_cpuinfo(struct cpufreq_policy *policy) max_freq = freq; } - policy->min = policy->cpuinfo.min_freq = min_freq; - policy->max = max_freq; + policy->cpuinfo.min_freq = min_freq; /* * If the driver has set its own cpuinfo.max_freq above max_freq, leave * it as is. */ if (policy->cpuinfo.max_freq < max_freq) - policy->max = policy->cpuinfo.max_freq = max_freq; + policy->cpuinfo.max_freq = max_freq; - if (policy->min == ~0) + if (min_freq == ~0) return -EINVAL; else return 0; diff --git a/drivers/cpufreq/gx-suspmod.c b/drivers/cpufreq/gx-suspmod.c index d269a4f26f98..d40c9e0bbb74 100644 --- a/drivers/cpufreq/gx-suspmod.c +++ b/drivers/cpufreq/gx-suspmod.c @@ -421,7 +421,7 @@ static int cpufreq_gx_cpu_init(struct cpufreq_policy *policy) policy->min = maxfreq / max_duration; else policy->min = maxfreq / POLICY_MIN_DIV; - policy->max = maxfreq; + policy->cpuinfo.min_freq = maxfreq / max_duration; policy->cpuinfo.max_freq = maxfreq; diff --git a/drivers/cpufreq/intel_pstate.c b/drivers/cpufreq/intel_pstate.c index 8dc22a65e025..5a0eeb84d382 100644 --- a/drivers/cpufreq/intel_pstate.c +++ b/drivers/cpufreq/intel_pstate.c @@ -3051,9 +3051,6 @@ static int __intel_pstate_cpu_init(struct cpufreq_policy *policy) policy->cpuinfo.max_freq = READ_ONCE(global.no_turbo) ? cpu->pstate.max_freq : cpu->pstate.turbo_freq; - policy->min = policy->cpuinfo.min_freq; - policy->max = policy->cpuinfo.max_freq; - intel_pstate_init_acpi_perf_limits(policy); policy->fast_switch_possible = true; diff --git a/drivers/cpufreq/pcc-cpufreq.c b/drivers/cpufreq/pcc-cpufreq.c index a355ec4f3dd4..83e6f73dc9a4 100644 --- a/drivers/cpufreq/pcc-cpufreq.c +++ b/drivers/cpufreq/pcc-cpufreq.c @@ -553,13 +553,11 @@ static int pcc_cpufreq_cpu_init(struct cpufreq_policy *policy) goto out; } - policy->max = policy->cpuinfo.max_freq = - ioread32(&pcch_hdr->nominal) * 1000; - policy->min = policy->cpuinfo.min_freq = - ioread32(&pcch_hdr->minimum_frequency) * 1000; + policy->cpuinfo.max_freq = ioread32(&pcch_hdr->nominal) * 1000; + policy->cpuinfo.min_freq = ioread32(&pcch_hdr->minimum_frequency) * 1000; - pr_debug("init: policy->max is %d, policy->min is %d\n", - policy->max, policy->min); + pr_debug("init: max_freq is %d, min_freq is %d\n", + policy->cpuinfo.max_freq, policy->cpuinfo.min_freq); out: return result; } diff --git a/drivers/cpufreq/pxa3xx-cpufreq.c b/drivers/cpufreq/pxa3xx-cpufreq.c index 50ff3b6a6900..06b27cbc59d6 100644 --- a/drivers/cpufreq/pxa3xx-cpufreq.c +++ b/drivers/cpufreq/pxa3xx-cpufreq.c @@ -185,9 +185,8 @@ static int pxa3xx_cpufreq_init(struct cpufreq_policy *policy) int ret = -EINVAL; /* set default policy and cpuinfo */ - policy->min = policy->cpuinfo.min_freq = 104000; - policy->max = policy->cpuinfo.max_freq = - (cpu_is_pxa320()) ? 806000 : 624000; + policy->cpuinfo.min_freq = 104000; + policy->cpuinfo.max_freq = (cpu_is_pxa320()) ? 806000 : 624000; policy->cpuinfo.transition_latency = 1000; /* FIXME: 1 ms, assumed */ if (cpu_is_pxa300() || cpu_is_pxa310()) diff --git a/drivers/cpufreq/sh-cpufreq.c b/drivers/cpufreq/sh-cpufreq.c index 642ddb9ea217..3c99d7009cbe 100644 --- a/drivers/cpufreq/sh-cpufreq.c +++ b/drivers/cpufreq/sh-cpufreq.c @@ -124,10 +124,8 @@ static int sh_cpufreq_cpu_init(struct cpufreq_policy *policy) dev_notice(dev, "no frequency table found, falling back " "to rate rounding.\n"); - policy->min = policy->cpuinfo.min_freq = - (clk_round_rate(cpuclk, 1) + 500) / 1000; - policy->max = policy->cpuinfo.max_freq = - (clk_round_rate(cpuclk, ~0UL) + 500) / 1000; + policy->cpuinfo.min_freq = (clk_round_rate(cpuclk, 1) + 500) / 1000; + policy->cpuinfo.max_freq = (clk_round_rate(cpuclk, ~0UL) + 500) / 1000; } return 0; diff --git a/drivers/cpufreq/virtual-cpufreq.c b/drivers/cpufreq/virtual-cpufreq.c index 4159f31349b1..dc78b74409af 100644 --- a/drivers/cpufreq/virtual-cpufreq.c +++ b/drivers/cpufreq/virtual-cpufreq.c @@ -164,10 +164,7 @@ static int virt_cpufreq_get_freq_info(struct cpufreq_policy *policy) policy->cpuinfo.min_freq = 1; policy->cpuinfo.max_freq = virt_cpufreq_get_perftbl_entry(policy->cpu, 0); - policy->min = policy->cpuinfo.min_freq; - policy->max = policy->cpuinfo.max_freq; - - policy->cur = policy->max; + policy->cur = policy->cpuinfo.max_freq; return 0; } From 8c83947c5dbbd49b36d08bb99e344327c6278781 Mon Sep 17 00:00:00 2001 From: Pierre Gondois Date: Thu, 28 May 2026 11:09:06 +0200 Subject: [PATCH 25/25] cpufreq: Use policy->min/max init as QoS request Modify cpufreq_policy_init_qos() introduced previously to use policy->min/max set in the driver .init() callback as the initial values for the policy min/max frequency QoS requests, respectively, so long as they are different from 0 (which means that they have been updated by the driver). Update the documentation in accordance with that code change. This only affects the following drivers: - gx-suspmod (min) - cppc-cpufreq (min) - longrun (min/max) Signed-off-by: Pierre Gondois [ rjw: Changelog rewrite ] Link: https://patch.msgid.link/20260528090913.2759118-5-pierre.gondois@arm.com Signed-off-by: Rafael J. Wysocki --- Documentation/cpu-freq/cpu-drivers.rst | 9 +++++++-- drivers/cpufreq/cpufreq.c | 12 ++++++++++-- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/Documentation/cpu-freq/cpu-drivers.rst b/Documentation/cpu-freq/cpu-drivers.rst index c5635ac3de54..17c69f83691e 100644 --- a/Documentation/cpu-freq/cpu-drivers.rst +++ b/Documentation/cpu-freq/cpu-drivers.rst @@ -114,8 +114,13 @@ Then, the driver must fill in the following values: |policy->cur | The current operating frequency of | | | this CPU (if appropriate) | +-----------------------------------+--------------------------------------+ -|policy->min, | | -|policy->max, | | +|policy->min, | The min/max scaling frequency. | +|policy->max | If set by the driver in ->init(), | +| | used as the lower/upper bound for | +| | policy frequency QoS requests; | +| | otherwise, reflects the min/max | +| | frequency the driver can set | ++-----------------------------------+--------------------------------------+ |policy->policy and, if necessary, | | |policy->governor | must contain the "default policy" for| | | this CPU. A few moments later, | diff --git a/drivers/cpufreq/cpufreq.c b/drivers/cpufreq/cpufreq.c index 253d3ba4217c..a81d40f12029 100644 --- a/drivers/cpufreq/cpufreq.c +++ b/drivers/cpufreq/cpufreq.c @@ -1399,8 +1399,16 @@ static void cpufreq_policy_free(struct cpufreq_policy *policy) static int cpufreq_policy_init_qos(struct cpufreq_policy *policy) { + unsigned int min_freq, max_freq; int ret; + /* Use policy->min/max set by the driver as QoS requests. */ + min_freq = max(FREQ_QOS_MIN_DEFAULT_VALUE, policy->min); + if (policy->max) + max_freq = min(FREQ_QOS_MAX_DEFAULT_VALUE, policy->max); + else + max_freq = FREQ_QOS_MAX_DEFAULT_VALUE; + if (policy->boost_supported) { ret = freq_qos_add_request(&policy->constraints, &policy->boost_freq_req, @@ -1411,12 +1419,12 @@ static int cpufreq_policy_init_qos(struct cpufreq_policy *policy) } ret = freq_qos_add_request(&policy->constraints, &policy->min_freq_req, - FREQ_QOS_MIN, FREQ_QOS_MIN_DEFAULT_VALUE); + FREQ_QOS_MIN, min_freq); if (ret < 0) return ret; ret = freq_qos_add_request(&policy->constraints, &policy->max_freq_req, - FREQ_QOS_MAX, FREQ_QOS_MAX_DEFAULT_VALUE); + FREQ_QOS_MAX, max_freq); if (ret < 0) return ret;