CPUFreq arm updates for 7.4

- Minor fixes to in various cpufreq drivers (Alexander Koskovich, Daniel
   Schwierzeck, Frank Zhang, Haotian Zhang, Kathiravan Thirumoorthy,
   Sumeet Pawnikar, and Xueqin Luo).
 
 - Fix wrong behaviors on multi-node servers in Loongson driver (Huacai Chen).
 
 - Reject NULL from cpufreq_cpu_get() in the Rust code (Mehmet Koseoglu).
 
 - Add suspend OPP to mediatek driver and DT (Ryan Brue).
 -----BEGIN PGP SIGNATURE-----
 
 iQIzBAABCgAdFiEEx73Crsp7f6M6scA70rkcPK6BEhwFAmrRv6UACgkQ0rkcPK6B
 EhzA1RAAh7mh2kSOkyyoUJV2fqvSP5fEzH1oX+FXZI/3Rmq8ycEuPIK6Xpz00Nj7
 XkX9MDi/hVgdqzM7CpuSR8KBdd73ZmMpUwxQI2efPlNLl7svLwUUkm/mWVIolP30
 ML1mP6qiLV41jKk9WtsCg05hNLt+jBdhyMXi6mHk6y8t7yNNLovEJVWry/f9MNSZ
 Rcr7vDjF99N95W9hCo77ERTpn2ILYajTlenzbLgakrzUkdHs8BzIi0chDFPq+OW0
 zvYaMxAiAPbzDBSzQ4XNwnZB7LBnqLOlnVZoJhD3yryWeesVgwMLeXmVpcqKuEBa
 aWUjSDKLKyyQEGtVD9GMQJ4SnG+5b8CT53R7NXNet2OMxW15UxrAkxUDtx0eyf8n
 FhHqOt3J5/qatOtw8lFm+AyOcrrWKd2p3Is1EJJ9f0q9F3H4KKth16wAlWTFYTTE
 LzZVyD6ZpARxnpC/g9y1Q4jhGIyHw7A61hrVxQmXueFubn0aKXaUO3bhPSbU9DEI
 rHDaujycOsLdaYCMTUZSfM9VtXpgWS7gaWNpRzKjVCd+0XSTe9sbmHFvtpliCohX
 qxgvVGQWajd9QP/JlDC7yJOUZEiZqVQE+NgHF8C2D7Acf9lj2NCkgb0hNKhbmPUo
 6KuwDgXrquMc5gcdicetb8gmgfGUdPv8HhrNT1WIAWxXhD7/onc=
 =vDid
 -----END PGP SIGNATURE-----

Merge tag 'cpufreq-arm-updates-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm

Pull CPUFreq Arm updates for 7.4 from Viresh Kumar:

"- Minor fixes to in various cpufreq drivers (Alexander Koskovich, Daniel
   Schwierzeck, Frank Zhang, Haotian Zhang, Kathiravan Thirumoorthy,
   Sumeet Pawnikar, and Xueqin Luo).

 - Fix wrong behaviors on multi-node servers in Loongson driver (Huacai Chen).

 - Reject NULL from cpufreq_cpu_get() in the Rust code (Mehmet Koseoglu).

 - Add suspend OPP to mediatek driver and DT (Ryan Brue)."

* tag 'cpufreq-arm-updates-7.4' of git://git.kernel.org/pub/scm/linux/kernel/git/vireshk/pm:
  cpufreq: loongson3: Replace IOCSR read/write with MMIO ones
  cpufreq: loongson3: Use global physical CPU ID in get/target callbacks
  cpufreq: loongson3: Replace per-package mutex with per-node
  cpufreq: loongson3: Adjust the width of id and val in smc_message
  cpufreq: loongson3: Make this driver depend on MACH_LOONGSON64
  arm64: dts: mediatek: mt8173: Mark the lowest CPU OPPs for suspend
  cpufreq: mediatek: Enter system sleep at the suspend OPP
  cpufreq: qcom-cpufreq-hw: fix device node refcount leak in phandle parsing
  cpufreq: qcom-nvmem: Add IPQ9650 support
  cpufreq: Add Milos to cpufreq-dt-platdev blocklist
  cpufreq: Use %pe to print error pointers symbolically
  rust: rcpufreq_dt: add module alias
  cpufreq: sti: avoid NULL dereference in dev_err()
  cpufreq: tegra194: fix double-pointer error in get_cpu_ndiv
  cpufreq: sparc-us2e: fix frequency table index copy-paste error
  rust: cpufreq: reject NULL from cpufreq_cpu_get()
  cpufreq: airoha: fix probe failure with correct error check
This commit is contained in:
Rafael J. Wysocki 2026-10-16 13:52:38 +02:00
commit c291ee973d
17 changed files with 100 additions and 50 deletions

View File

@ -56,6 +56,7 @@ cluster0_opp: opp-table-0 {
opp-507000000 {
opp-hz = /bits/ 64 <507000000>;
opp-microvolt = <859000>;
opp-suspend;
};
opp-702000000 {
opp-hz = /bits/ 64 <702000000>;
@ -93,6 +94,7 @@ cluster1_opp: opp-table-1 {
opp-507000000 {
opp-hz = /bits/ 64 <507000000>;
opp-microvolt = <828000>;
opp-suspend;
};
opp-702000000 {
opp-hz = /bits/ 64 <702000000>;

View File

@ -288,6 +288,7 @@ endif
if LOONGARCH
config LOONGSON3_CPUFREQ
tristate "Loongson3 CPUFreq Driver"
depends on MACH_LOONGSON64
help
This option adds a CPUFreq driver for Loongson processors which
support software configurable cpu frequency.

View File

@ -64,7 +64,7 @@ static int airoha_cpufreq_probe(struct platform_device *pdev)
/* Attach PM for OPP */
ret = dev_pm_domain_attach_list(cpu_dev, &attach_data,
&priv->pd_list);
if (ret)
if (ret < 0)
goto clear_opp_config;
cpufreq_dt = platform_device_register_simple("cpufreq-dt", -1, NULL, 0);

View File

@ -132,8 +132,8 @@ static int bmips_cpufreq_init(struct cpufreq_policy *policy)
freq_table = bmips_cpufreq_get_freq_table(policy);
if (IS_ERR(freq_table)) {
pr_err("%s: couldn't determine frequency table (%ld).\n",
BMIPS_CPUFREQ_NAME, PTR_ERR(freq_table));
pr_err("%s: couldn't determine frequency table (%pe).\n",
BMIPS_CPUFREQ_NAME, freq_table);
return PTR_ERR(freq_table);
}

View File

@ -230,8 +230,8 @@ static void cppc_fie_kworker_init(void)
kworker_fie = kthread_run_worker(0, "cppc_fie");
if (IS_ERR(kworker_fie)) {
pr_warn("%s: failed to create kworker_fie: %ld\n", __func__,
PTR_ERR(kworker_fie));
pr_warn("%s: failed to create kworker_fie: %pe\n", __func__,
kworker_fie);
fie_disabled = FIE_DISABLED;
kworker_fie = NULL;
return;

View File

@ -155,6 +155,7 @@ static const struct of_device_id blocklist[] __initconst = {
{ .compatible = "nvidia,tegra234", },
{ .compatible = "qcom,apq8096", },
{ .compatible = "qcom,milos", },
{ .compatible = "qcom,msm8909", },
{ .compatible = "qcom,msm8996", },
{ .compatible = "qcom,msm8998", },
@ -209,6 +210,7 @@ static const struct of_device_id blocklist[] __initconst = {
{ .compatible = "qcom,ipq8064", },
{ .compatible = "qcom,ipq8074", },
{ .compatible = "qcom,ipq9574", },
{ .compatible = "qcom,ipq9650", },
{ .compatible = "qcom,apq8064", },
{ .compatible = "qcom,msm8974", },
{ .compatible = "qcom,msm8960", },

View File

@ -16,14 +16,16 @@
#include <asm/idle.h>
#include <asm/loongarch.h>
#include <asm/loongson.h>
#include <asm/smp.h>
#include <asm/numa.h>
/* Message */
union smc_message {
u32 value;
struct {
u32 id : 4;
u32 id : 8;
u32 info : 4;
u32 val : 16;
u32 val : 12;
u32 cmd : 6;
u32 extra : 1;
u32 complete : 1;
@ -164,27 +166,47 @@ union smc_message {
#define FREQ_MAX_LEVEL 16
#define MMIO_SMCMBX(node) \
((void __iomem *)(nid_to_addrbase(node) | IO_BASE | LOONGSON_REG_BASE | LOONGARCH_IOCSR_SMCMBX))
#define MMIO_MISC_FUNC(node) \
((void __iomem *)(nid_to_addrbase(node) | IO_BASE | LOONGSON_REG_BASE | LOONGARCH_IOCSR_MISC_FUNC))
struct loongson3_freq_data {
unsigned int def_freq_level;
struct cpufreq_frequency_table table[];
};
static struct mutex cpufreq_mutex[MAX_PACKAGES];
static struct mutex cpufreq_mutex[MAX_NUMNODES];
static struct cpufreq_driver loongson3_cpufreq_driver;
static DEFINE_PER_CPU(struct loongson3_freq_data *, freq_data);
static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 extra)
{
int retries;
unsigned int cpu = raw_smp_processor_id();
unsigned int package = cpu_data[cpu].package;
unsigned int cpu = 0, nid;
union smc_message msg, last;
mutex_lock(&cpufreq_mutex[package]);
#ifdef CONFIG_SMP
switch (cmd) {
case CMD_GET_FREQ_INFO:
case CMD_SET_FREQ_INFO:
case CMD_GET_FREQ_LEVEL_NUM:
case CMD_GET_FREQ_LEVEL_INFO:
case CMD_GET_FREQ_BOOST_LEVEL:
cpu = cpu_number_map(id);
break;
default:
cpu = raw_smp_processor_id();
}
#endif
nid = cpu_to_node(cpu);
last.value = iocsr_read32(LOONGARCH_IOCSR_SMCMBX);
mutex_lock(&cpufreq_mutex[nid]);
last.value = readl(MMIO_SMCMBX(nid));
if (!last.complete) {
mutex_unlock(&cpufreq_mutex[package]);
mutex_unlock(&cpufreq_mutex[nid]);
return -EPERM;
}
@ -195,12 +217,11 @@ static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 ext
msg.extra = extra;
msg.complete = 0;
iocsr_write32(msg.value, LOONGARCH_IOCSR_SMCMBX);
iocsr_write32(iocsr_read32(LOONGARCH_IOCSR_MISC_FUNC) | IOCSR_MISC_FUNC_SOFT_INT,
LOONGARCH_IOCSR_MISC_FUNC);
writel(msg.value, MMIO_SMCMBX(nid));
writel(readl(MMIO_MISC_FUNC(nid)) | IOCSR_MISC_FUNC_SOFT_INT, MMIO_MISC_FUNC(nid));
for (retries = 0; retries < 10000; retries++) {
msg.value = iocsr_read32(LOONGARCH_IOCSR_SMCMBX);
msg.value = readl(MMIO_SMCMBX(nid));
if (msg.complete)
break;
@ -208,49 +229,51 @@ static inline int do_service_request(u32 id, u32 info, u32 cmd, u32 val, u32 ext
}
if (!msg.complete || msg.cmd != CMD_OK) {
mutex_unlock(&cpufreq_mutex[package]);
mutex_unlock(&cpufreq_mutex[nid]);
return -EPERM;
}
mutex_unlock(&cpufreq_mutex[package]);
mutex_unlock(&cpufreq_mutex[nid]);
return msg.val;
}
static unsigned int loongson3_cpufreq_get(unsigned int cpu)
{
int ret;
int ret, core = cpu_logical_map(cpu);
ret = do_service_request(cpu, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_INFO, 0, 0);
ret = do_service_request(core, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_INFO, 0, 0);
return ret * KILO;
return (ret < 0) ? 0 : ret * KILO;
}
static int loongson3_cpufreq_target(struct cpufreq_policy *policy, unsigned int index)
{
int ret;
int ret, core = cpu_logical_map(policy->cpu);
ret = do_service_request(cpu_data[policy->cpu].core,
FREQ_INFO_TYPE_LEVEL, CMD_SET_FREQ_INFO, index, 0);
ret = do_service_request(core, FREQ_INFO_TYPE_LEVEL, CMD_SET_FREQ_INFO,
index, 0);
return (ret >= 0) ? 0 : ret;
}
static int configure_freq_table(int cpu)
{
int i, ret, boost_level, max_level, freq_level;
int i, ret, core, boost_level, max_level, freq_level;
struct platform_device *pdev = cpufreq_get_driver_data();
struct loongson3_freq_data *data;
if (per_cpu(freq_data, cpu))
return 0;
ret = do_service_request(cpu, 0, CMD_GET_FREQ_LEVEL_NUM, 0, 0);
core = cpu_logical_map(cpu);
ret = do_service_request(core, 0, CMD_GET_FREQ_LEVEL_NUM, 0, 0);
if (ret < 0)
return ret;
max_level = ret;
ret = do_service_request(cpu, 0, CMD_GET_FREQ_BOOST_LEVEL, 0, 0);
ret = do_service_request(core, 0, CMD_GET_FREQ_BOOST_LEVEL, 0, 0);
if (ret < 0)
return ret;
boost_level = ret;
@ -263,7 +286,7 @@ static int configure_freq_table(int cpu)
data->def_freq_level = boost_level - 1;
for (i = 0; i < freq_level; i++) {
ret = do_service_request(cpu, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_LEVEL_INFO, i, 0);
ret = do_service_request(core, FREQ_INFO_TYPE_FREQ, CMD_GET_FREQ_LEVEL_INFO, i, 0);
if (ret < 0) {
devm_kfree(&pdev->dev, data);
return ret;
@ -337,7 +360,7 @@ static int loongson3_cpufreq_probe(struct platform_device *pdev)
{
int i, ret;
for (i = 0; i < MAX_PACKAGES; i++) {
for (i = 0; i < MAX_NUMNODES; i++) {
ret = devm_mutex_init(&pdev->dev, &cpufreq_mutex[i]);
if (ret)
return ret;

View File

@ -603,6 +603,7 @@ static int mtk_cpufreq_init(struct cpufreq_policy *policy)
policy->freq_table = freq_table;
policy->driver_data = info;
policy->clk = info->cpu_clk;
policy->suspend_freq = dev_pm_opp_get_suspend_opp_freq(info->cpu_dev) / 1000;
return 0;
}
@ -624,6 +625,7 @@ static struct cpufreq_driver mtk_cpufreq_driver = {
.init = mtk_cpufreq_init,
.exit = mtk_cpufreq_exit,
.register_em = cpufreq_register_em_with_opp,
.suspend = cpufreq_generic_suspend,
.name = "mtk-cpufreq",
};

View File

@ -322,6 +322,8 @@ static void qcom_get_related_cpus(int index, struct cpumask *m)
if (index == args.args[0])
cpumask_set_cpu(cpu, m);
of_node_put(args.np);
}
}
@ -554,6 +556,7 @@ static int qcom_cpufreq_hw_cpu_init(struct cpufreq_policy *policy)
return ret;
index = args.args[0];
of_node_put(args.np);
data = &qcom_cpufreq.data[index];
/* HW should be in enabled state to proceed */

View File

@ -211,6 +211,14 @@ static int qcom_cpufreq_kryo_name_version(struct device *cpu_dev,
case QCOM_ID_IPQ5404:
drv->versions = (*speedbin == 0x3b) ? BIT(1) : BIT(0);
break;
case QCOM_ID_IPQ9610:
case QCOM_ID_IPQ9620:
case QCOM_ID_IPQ9630:
case QCOM_ID_IPQ9640:
case QCOM_ID_IPQ9650:
case QCOM_ID_IPQ9670:
drv->versions = (*speedbin == 0x4b) ? BIT(3) : BIT(2);
break;
case QCOM_ID_MSM8996SG:
case QCOM_ID_APQ8096SG:
drv->versions = 1 << ((unsigned int)(*speedbin) + 4);
@ -633,6 +641,7 @@ static const struct of_device_id qcom_cpufreq_match_list[] __initconst __maybe_u
{ .compatible = "qcom,ipq8074", .data = &match_data_ipq8074 },
{ .compatible = "qcom,apq8064", .data = &match_data_krait },
{ .compatible = "qcom,ipq9574", .data = &match_data_kryo },
{ .compatible = "qcom,ipq9650", .data = &match_data_kryo },
{ .compatible = "qcom,msm8974", .data = &match_data_krait },
{ .compatible = "qcom,msm8960", .data = &match_data_krait },
{},

View File

@ -57,8 +57,7 @@ static u32 get_bus_freq(void)
/* get platform freq by its clock name */
pltclk = clk_get(NULL, "cg-pll0-div1");
if (IS_ERR(pltclk)) {
pr_err("%s: can't get bus frequency %ld\n",
__func__, PTR_ERR(pltclk));
pr_err("%s: can't get bus frequency %pe\n", __func__, pltclk);
return PTR_ERR(pltclk);
}

View File

@ -225,4 +225,5 @@ fn probe<'bound>(
authors: ["Viresh Kumar <viresh.kumar@linaro.org>"],
description: "Generic CPUFreq DT driver",
license: "GPL v2",
alias: ["platform:cpufreq-dt"],
}

View File

@ -152,15 +152,14 @@ static int s3c64xx_cpufreq_driver_init(struct cpufreq_policy *policy)
policy->clk = clk_get(NULL, "armclk");
if (IS_ERR(policy->clk)) {
pr_err("Unable to obtain ARMCLK: %ld\n",
PTR_ERR(policy->clk));
pr_err("Unable to obtain ARMCLK: %pe\n", policy->clk);
return PTR_ERR(policy->clk);
}
#ifdef CONFIG_REGULATOR
vddarm = regulator_get(NULL, "vddarm");
if (IS_ERR(vddarm)) {
pr_err("Failed to obtain VDDARM: %ld\n", PTR_ERR(vddarm));
pr_err("Failed to obtain VDDARM: %pe\n", vddarm);
pr_err("Only frequency scaling available\n");
vddarm = NULL;
} else {

View File

@ -282,12 +282,11 @@ static int us2e_freq_cpu_init(struct cpufreq_policy *policy)
table[1].frequency = clock_tick / 2;
table[2].driver_data = 2;
table[2].frequency = clock_tick / 4;
table[2].driver_data = 3;
table[2].frequency = clock_tick / 6;
table[2].driver_data = 4;
table[2].frequency = clock_tick / 8;
table[2].driver_data = 5;
table[3].frequency = CPUFREQ_TABLE_END;
table[3].driver_data = 3;
table[3].frequency = clock_tick / 6;
table[4].driver_data = 4;
table[4].frequency = clock_tick / 8;
table[5].frequency = CPUFREQ_TABLE_END;
policy->cpuinfo.transition_latency = 0;
policy->cur = clock_tick;

View File

@ -263,7 +263,7 @@ static int __init sti_cpufreq_init(void)
ddata.cpu = get_cpu_device(0);
if (!ddata.cpu) {
dev_err(ddata.cpu, "Failed to get device for CPU0\n");
pr_err("Failed to get device for CPU0\n");
goto skip_voltage_scaling;
}
@ -281,7 +281,7 @@ static int __init sti_cpufreq_init(void)
goto register_cpufreq_dt;
skip_voltage_scaling:
dev_err(ddata.cpu, "Not doing voltage scaling\n");
pr_err("Not doing voltage scaling\n");
register_cpufreq_dt:
platform_device_register_simple("cpufreq-dt", -1, NULL, 0);

View File

@ -367,7 +367,7 @@ static void tegra194_get_cpu_ndiv_sysreg(void *ndiv)
static int tegra194_get_cpu_ndiv(u32 cpu, u32 cpuid, u32 clusterid, u64 *ndiv)
{
return smp_call_function_single(cpu, tegra194_get_cpu_ndiv_sysreg, &ndiv, true);
return smp_call_function_single(cpu, tegra194_get_cpu_ndiv_sysreg, ndiv, true);
}
static void tegra194_set_cpu_ndiv_sysreg(void *data)

View File

@ -14,7 +14,13 @@
cpumask,
device::{Bound, Device},
devres,
error::{code::*, from_err_ptr, from_result, to_result, Result, VTABLE_DEFAULT_ERROR},
error::{
code::*,
from_result,
to_result,
Result,
VTABLE_DEFAULT_ERROR, //
},
ffi::{c_char, c_ulong},
prelude::*,
types::ForeignOwnable,
@ -29,7 +35,10 @@
marker::PhantomData,
ops::{Deref, DerefMut},
pin::Pin,
ptr,
ptr::{
self,
NonNull, //
},
};
use macros::vtable;
@ -687,12 +696,13 @@ fn clear_data<T: ForeignOwnable>(&mut self) -> Option<T> {
impl<'a> PolicyCpu<'a> {
fn from_cpu(cpu: CpuId) -> Result<Self> {
// SAFETY: It is safe to call `cpufreq_cpu_get` for any valid CPU.
let ptr = from_err_ptr(unsafe { bindings::cpufreq_cpu_get(u32::from(cpu)) })?;
let ptr =
NonNull::new(unsafe { bindings::cpufreq_cpu_get(u32::from(cpu)) }).ok_or(ENODEV)?;
Ok(Self(
// SAFETY: The `ptr` is guaranteed to be valid and remains valid for the lifetime of
// the returned reference.
unsafe { Policy::from_raw_mut(ptr) },
// SAFETY: `ptr` is non-NULL and `cpufreq_cpu_get()` took a reference on it, so it is
// valid for writing and remains valid for the lifetime of the returned reference.
unsafe { Policy::from_raw_mut(ptr.as_ptr()) },
))
}
}