From 80652b0de2570a89e6ce7443a51e46e3c10bcd14 Mon Sep 17 00:00:00 2001 From: Varadarajan Narayanan Date: Wed, 1 Jul 2026 14:16:25 +0530 Subject: [PATCH 01/10] cpufreq: qcom-nvmem: Add IPQ5210 support IPQ5210 SoCs expose CPU frequency limits through an eFuse speed bin, and the valid CPU OPPs depend on the SoC variant. Add IPQ5210 support to the Qualcomm NVMEM cpufreq driver so the supported OPPs can be selected at runtime using the eFuse value and the opp- supported-hw OPP property. Also block the generic cpufreq-dt platform device for IPQ5210 so the NVMEM-based driver is used. Signed-off-by: Varadarajan Narayanan Reviewed-by: Konrad Dybcio [ Viresh: Converted `!=` to `==` ] Signed-off-by: Viresh Kumar --- drivers/cpufreq/cpufreq-dt-platdev.c | 1 + drivers/cpufreq/qcom-cpufreq-nvmem.c | 8 ++++++++ 2 files changed, 9 insertions(+) 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/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 }, From d87cb889dc7ab1f2deecadf2a5e9023184bd7900 Mon Sep 17 00:00:00 2001 From: Haoxiang Li Date: Fri, 3 Jul 2026 14:20:49 +0800 Subject: [PATCH 02/10] cpufreq: apple-soc: Fix OPP table cleanup apple_soc_cpufreq_init() adds OPP tables from firmware, but some failure paths do not remove them. The driver also uses dev_pm_opp_remove_all_dynamic(), which is not the right cleanup helper for OPP tables loaded from firmware. Use the cpumask OPP helper after the policy CPU mask has been populated. Pair it with the matching cpumask remove helper on failure paths and in apple_soc_cpufreq_exit(). This also removes the separate dev_pm_opp_set_sharing_cpus() call, as the cpumask helper loads the DT OPP tables for all CPUs in the policy. Fixes: 6286bbb40576 ("cpufreq: apple-soc: Add new driver to control Apple SoC CPU P-states") Cc: stable@vger.kernel.org Signed-off-by: Haoxiang Li Signed-off-by: Viresh Kumar --- drivers/cpufreq/apple-soc-cpufreq.c | 36 +++++++++++------------------ 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/drivers/cpufreq/apple-soc-cpufreq.c b/drivers/cpufreq/apple-soc-cpufreq.c index 638e5bf72185..3f64f266e695 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,19 +269,13 @@ 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 */ @@ -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); } From f456178290356167c7349d51432889113565d1ba Mon Sep 17 00:00:00 2001 From: Guru Das Srinagesh Date: Sun, 5 Jul 2026 20:08:15 -0700 Subject: [PATCH 03/10] rust: rcpufreq_dt: use vertical import style Convert `use` imports to vertical layout for better readability and maintainability. Signed-off-by: Guru Das Srinagesh Signed-off-by: Viresh Kumar --- drivers/cpufreq/rcpufreq_dt.rs | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) 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, From 5a83170c8795056a401d7bfb9529af22a53fd9a4 Mon Sep 17 00:00:00 2001 From: Pan Chuang Date: Mon, 13 Jul 2026 21:11:31 +0800 Subject: [PATCH 04/10] cpufreq: brcmstb-avs: Remove redundant dev_err() Since commit 55b48e23f5c4 ("genirq/devres: Add error handling in devm_request_*_irq()"), devm_request_irq() automatically logs detailed error messages on failure. Remove the now-redundant driver-specific dev_err() calls. Signed-off-by: Pan Chuang Reviewed-by: Zhongqiu Han [ Viresh: Fixed Subject ] Signed-off-by: Viresh Kumar --- drivers/cpufreq/brcmstb-avs-cpufreq.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) 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; From 6a9e0e0f7592313ace66303cf5eca68e04c10f30 Mon Sep 17 00:00:00 2001 From: Dan Carpenter Date: Tue, 14 Jul 2026 18:46:22 +0300 Subject: [PATCH 05/10] cpufreq: spear: Fix an IS_ERR() vs NULL bug in spear1340_set_cpu_rate() The clk_get_parent() function doesn't return error pointers, it returns NULL on error. Update the error checking to match. Fixes: 420993221175 ("cpufreq: SPEAr: Add CPUFreq driver") Signed-off-by: Dan Carpenter Reviewed-by: Zhongqiu Han Signed-off-by: Viresh Kumar --- drivers/cpufreq/spear-cpufreq.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 */ From 391b4b1d5476a39058bdd24d9c69430386f79659 Mon Sep 17 00:00:00 2001 From: Sasha Finkelstein Date: Mon, 20 Jul 2026 09:25:10 +0200 Subject: [PATCH 06/10] cpufreq: apple-soc: Calculate frequency as a 64-bit value The current frequency calculation is done in 32 bit, causing problems if run on a future SoC that can boost higher than 4.2GHz. Ideally, we should use a true u64 instead of unsigned long and "knowning" that this only runs on 64 bit machines, but the core code uses ulong everywhere, so this should be good enough. Signed-off-by: Sasha Finkelstein Reviewed-by: Joshua Peisach Reviewed-by: Zhongqiu Han Reviewed-by: Janne Grunau Signed-off-by: Viresh Kumar --- drivers/cpufreq/apple-soc-cpufreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/apple-soc-cpufreq.c b/drivers/cpufreq/apple-soc-cpufreq.c index 3f64f266e695..7c50ce74f911 100644 --- a/drivers/cpufreq/apple-soc-cpufreq.c +++ b/drivers/cpufreq/apple-soc-cpufreq.c @@ -280,7 +280,7 @@ static int apple_soc_cpufreq_init(struct cpufreq_policy *policy) /* 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)) { From b5e4771f20a37fdf19eac0824bf062dffb4e291f Mon Sep 17 00:00:00 2001 From: Priya Bala Govindasamy Date: Mon, 20 Jul 2026 20:50:35 +0000 Subject: [PATCH 07/10] rust: cpufreq: Add CPUFREQ_TABLE_END as last table entry in TableBuilder::to_table The `TableBuilder::to_table` function adds `Hertz(c_ulong::MAX).as_khz()` as the last frequency entry in the frequency table. But the C API expects the last entry to have frequency set to `CPUFREQ_TABLE_END` which is `~1u` as per include/linux/cpufreq.h. Fix this by setting the last frequency entry to `CPUFREQ_TABLE_END` instead of `Hertz(c_ulong::MAX).as_khz()`. Fixes: 2207856ff0bc8d953d6e89bda70b8978c2de8bab ("rust: cpufreq: Add initial abstractions for cpufreq framework") Reported-by: Dylan Zueck Reported-by: Yuan Tan Assisted-by: ChatGPT:gpt-5.6-terra Signed-off-by: Priya Bala Govindasamy Signed-off-by: Viresh Kumar --- rust/kernel/cpufreq.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs index 58ac04c650a1..f360eb9578f3 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) } From 19c76bdd3fc02475c73c8576f6f4a55ff07886f1 Mon Sep 17 00:00:00 2001 From: Priya Bala Govindasamy Date: Mon, 20 Jul 2026 18:00:58 +0000 Subject: [PATCH 08/10] rust: cpufreq: Fix temporary write in Registration::bios_limit_callback In `Registration::bios_limit_callback`, the expression `&mut (unsafe { *limit })` creates a reference to a temporary copy of the value pointed to by `limit` on the stack. Therefore, writes made by `T::bios_limit` go to this temporary instead of the memory location pointed to by `limit`. Additionally, `limit` may be uninitialized, such as when `Registration::bios_limit_callback` is invoked by `show_bios_limit` in drivers/cpufreq/cpufreq.c. Therefore creating a reference to `limit` is unsound. Fix this by changing the signature of `T::bios_limit` to return the limit value. `Registration::bios_limit_callback` can then update `limit` directly. Fixes: c6af9a1191d042839e56abff69e8b0302d117988 ("rust: cpufreq: Extend abstractions for driver registration") Reported-by: Dylan Zueck Reported-by: Yuan Tan Assisted-by: ChatGPT:gpt-5.4 Signed-off-by: Priya Bala Govindasamy [ Viresh: Fix rustfmtcheck warning ] Signed-off-by: Viresh Kumar --- rust/kernel/cpufreq.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs index f360eb9578f3..1158ef3a85f2 100644 --- a/rust/kernel/cpufreq.rs +++ b/rust/kernel/cpufreq.rs @@ -822,7 +822,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) } @@ -1357,9 +1359,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) }) } From 22c23c72c3b21fa3ec3db5070dfc0582794e0ef9 Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Thu, 6 Aug 2026 07:09:02 +0200 Subject: [PATCH 09/10] cpufreq: imx6q: fix devres accumulation across driver rebind imx6_soc_volt is allocated with devm_kcalloc(cpu_dev, ...), where cpu_dev is the CPU device from get_cpu_device(0). That device is never unbound, so its devres list is never released, and imx6q_cpufreq_remove() does not free the array either. Every probe therefore adds an allocation that stays for the lifetime of the system. Allocate against the platform device instead. Its devres is released when the driver is unbound, which is exactly the lifetime the array wants: imx6q_set_target() reads it, and nothing may reach that after cpufreq_unregister_driver(). That makes the array actually go away on unbind, so also clear the file-scope pointer in remove and on the failed-probe path, rather than leave it pointing at memory devres is about to release. Tested by rebinding the driver on qemu's mcimx6ul-evk. Fixes: b4573d1d657a ("cpufreq: imx6q: correct VDDSOC/PU voltage scaling when cpufreq is changed") Assisted-by: Claude:claude-opus-5 Signed-off-by: Karl Mehltretter Signed-off-by: Viresh Kumar --- drivers/cpufreq/imx6q-cpufreq.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index e93697d3edfd..8110c95059e0 100644 --- a/drivers/cpufreq/imx6q-cpufreq.c +++ b/drivers/cpufreq/imx6q-cpufreq.c @@ -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); From 8c3afcf27fa4582c1ab912503dc8a4ebb8dc0f82 Mon Sep 17 00:00:00 2001 From: Karl Mehltretter Date: Thu, 6 Aug 2026 07:02:39 +0200 Subject: [PATCH 10/10] cpufreq: imx6q: fix out-of-bounds write when probed more than once imx6_soc_volt is allocated fresh on every probe, sized to the number of ARM OPPs: imx6_soc_volt = devm_kcalloc(cpu_dev, num, sizeof(*imx6_soc_volt), GFP_KERNEL); but it is filled through soc_opp_count, which has static storage and is never reset. A second bind after an unbind keeps indexing from where the first one stopped, and writes past the end of the new array. Unbinding and rebinding the driver on qemu's mcimx6ul-evk, under KASAN: BUG: KASAN: slab-out-of-bounds in imx6q_cpufreq_probe+0x3b0/0xa34 Write of size 4 at addr c5e90480 by task binder/73 imx6q_cpufreq_probe from platform_probe+0x88/0xe4 platform_probe from really_probe+0x108/0x384 bind_store from kernfs_fop_write_iter+0x1b4/0x28c The write lands one u32 past the end of the allocation. soc_opp_count is only read a few lines below the loop that fills it, so it never needed static storage. Make it a local. Fixes: b4573d1d657a ("cpufreq: imx6q: correct VDDSOC/PU voltage scaling when cpufreq is changed") Assisted-by: Claude:claude-opus-5 Signed-off-by: Karl Mehltretter Signed-off-by: Viresh Kumar --- drivers/cpufreq/imx6q-cpufreq.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/cpufreq/imx6q-cpufreq.c b/drivers/cpufreq/imx6q-cpufreq.c index 8110c95059e0..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) {