Skip to content

Commit 25961ae

Browse files
committed
Merge branch 'pm-cpufreq'
Merge Rust support for cpufreq and OPP, a new Rust-based cpufreq-dt driver, an SCMI cpufreq driver cleanup, and an ACPI cpufreq driver regression fix: - Add Rust abstractions for CPUFreq framework (Viresh Kumar). - Add Rust abstractions for OPP framework (Viresh Kumar). - Add basic Rust abstractions for Clk and Cpumask frameworks (Viresh Kumar). - Clean up the SCMI cpufreq driver somewhat (Mike Tipton). - Use KHz as the nominal_freq units in get_max_boost_ratio() in the ACPI cpufreq driver (iGautham Shenoy). * pm-cpufreq: acpi-cpufreq: Fix nominal_freq units to KHz in get_max_boost_ratio() rust: opp: Move `cfg(CONFIG_OF)` attribute to the top of doc test rust: opp: Make the doctest example depend on CONFIG_OF cpufreq: scmi: Skip SCMI devices that aren't used by the CPUs cpufreq: Add Rust-based cpufreq-dt driver rust: opp: Extend OPP abstractions with cpufreq support rust: cpufreq: Extend abstractions for driver registration rust: cpufreq: Extend abstractions for policy and driver ops rust: cpufreq: Add initial abstractions for cpufreq framework rust: opp: Add abstractions for the configuration options rust: opp: Add abstractions for the OPP table rust: opp: Add initial abstractions for OPP framework rust: cpu: Add from_cpu() rust: macros: enable use of hyphens in module names rust: clk: Add initial abstractions rust: clk: Add helpers for Rust code MAINTAINERS: Add entry for Rust cpumask API rust: cpumask: Add initial abstractions rust: cpumask: Add few more helpers
2 parents 9d230d5 + cb6a85f commit 25961ae

File tree

18 files changed

+3573
-10
lines changed

18 files changed

+3573
-10
lines changed

MAINTAINERS

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5949,6 +5949,8 @@ F: include/dt-bindings/clock/
59495949
F: include/linux/clk-pr*
59505950
F: include/linux/clk/
59515951
F: include/linux/of_clk.h
5952+
F: rust/helpers/clk.c
5953+
F: rust/kernel/clk.rs
59525954
X: drivers/clk/clkdev.c
59535955

59545956
COMMON INTERNET FILE SYSTEM CLIENT (CIFS and SMB3)
@@ -6208,6 +6210,7 @@ F: drivers/cpufreq/
62086210
F: include/linux/cpufreq.h
62096211
F: include/linux/sched/cpufreq.h
62106212
F: kernel/sched/cpufreq*.c
6213+
F: rust/kernel/cpufreq.rs
62116214
F: tools/testing/selftests/cpufreq/
62126215

62136216
CPU HOTPLUG
@@ -6221,6 +6224,7 @@ F: include/linux/cpuhotplug.h
62216224
F: include/linux/smpboot.h
62226225
F: kernel/cpu.c
62236226
F: kernel/smpboot.*
6227+
F: rust/kernel/cpu.rs
62246228

62256229
CPU IDLE TIME MANAGEMENT FRAMEWORK
62266230
M: "Rafael J. Wysocki" <rafael@kernel.org>
@@ -6305,6 +6309,12 @@ L: linux-riscv@lists.infradead.org
63056309
S: Maintained
63066310
F: drivers/cpuidle/cpuidle-riscv-sbi.c
63076311

6312+
CPUMASK API [RUST]
6313+
M: Viresh Kumar <viresh.kumar@linaro.org>
6314+
R: Yury Norov <yury.norov@gmail.com>
6315+
S: Maintained
6316+
F: rust/kernel/cpumask.rs
6317+
63086318
CRAMFS FILESYSTEM
63096319
M: Nicolas Pitre <nico@fluxnic.net>
63106320
S: Maintained
@@ -18504,6 +18514,7 @@ F: Documentation/devicetree/bindings/opp/
1850418514
F: Documentation/power/opp.rst
1850518515
F: drivers/opp/
1850618516
F: include/linux/pm_opp.h
18517+
F: rust/kernel/opp.rs
1850718518

1850818519
OPL4 DRIVER
1850918520
M: Clemens Ladisch <clemens@ladisch.de>

drivers/cpufreq/Kconfig

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,18 @@ config CPUFREQ_DT
217217

218218
If in doubt, say N.
219219

220+
config CPUFREQ_DT_RUST
221+
tristate "Rust based Generic DT based cpufreq driver"
222+
depends on HAVE_CLK && OF && RUST
223+
select CPUFREQ_DT_PLATDEV
224+
select PM_OPP
225+
help
226+
This adds a Rust based generic DT based cpufreq driver for frequency
227+
management. It supports both uniprocessor (UP) and symmetric
228+
multiprocessor (SMP) systems.
229+
230+
If in doubt, say N.
231+
220232
config CPUFREQ_VIRT
221233
tristate "Virtual cpufreq driver"
222234
depends on GENERIC_ARCH_TOPOLOGY

drivers/cpufreq/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o
1515
obj-$(CONFIG_CPU_FREQ_GOV_ATTR_SET) += cpufreq_governor_attr_set.o
1616

1717
obj-$(CONFIG_CPUFREQ_DT) += cpufreq-dt.o
18+
obj-$(CONFIG_CPUFREQ_DT_RUST) += rcpufreq_dt.o
1819
obj-$(CONFIG_CPUFREQ_DT_PLATDEV) += cpufreq-dt-platdev.o
1920
obj-$(CONFIG_CPUFREQ_VIRT) += virtual-cpufreq.o
2021

drivers/cpufreq/acpi-cpufreq.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -660,7 +660,7 @@ static u64 get_max_boost_ratio(unsigned int cpu, u64 *nominal_freq)
660660
nominal_perf = perf_caps.nominal_perf;
661661

662662
if (nominal_freq)
663-
*nominal_freq = perf_caps.nominal_freq;
663+
*nominal_freq = perf_caps.nominal_freq * 1000;
664664

665665
if (!highest_perf || !nominal_perf) {
666666
pr_debug("CPU%d: highest or nominal performance missing\n", cpu);

drivers/cpufreq/rcpufreq_dt.rs

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Rust based implementation of the cpufreq-dt driver.
4+
5+
use kernel::{
6+
c_str,
7+
clk::Clk,
8+
cpu, cpufreq,
9+
cpumask::CpumaskVar,
10+
device::{Core, Device},
11+
error::code::*,
12+
fmt,
13+
macros::vtable,
14+
module_platform_driver, of, opp, platform,
15+
prelude::*,
16+
str::CString,
17+
sync::Arc,
18+
};
19+
20+
/// Finds exact supply name from the OF node.
21+
fn find_supply_name_exact(dev: &Device, name: &str) -> Option<CString> {
22+
let prop_name = CString::try_from_fmt(fmt!("{}-supply", name)).ok()?;
23+
dev.property_present(&prop_name)
24+
.then(|| CString::try_from_fmt(fmt!("{name}")).ok())
25+
.flatten()
26+
}
27+
28+
/// Finds supply name for the CPU from DT.
29+
fn find_supply_names(dev: &Device, cpu: u32) -> Option<KVec<CString>> {
30+
// Try "cpu0" for older DTs, fallback to "cpu".
31+
let name = (cpu == 0)
32+
.then(|| find_supply_name_exact(dev, "cpu0"))
33+
.flatten()
34+
.or_else(|| find_supply_name_exact(dev, "cpu"))?;
35+
36+
let mut list = KVec::with_capacity(1, GFP_KERNEL).ok()?;
37+
list.push(name, GFP_KERNEL).ok()?;
38+
39+
Some(list)
40+
}
41+
42+
/// Represents the cpufreq dt device.
43+
struct CPUFreqDTDevice {
44+
opp_table: opp::Table,
45+
freq_table: opp::FreqTable,
46+
_mask: CpumaskVar,
47+
_token: Option<opp::ConfigToken>,
48+
_clk: Clk,
49+
}
50+
51+
#[derive(Default)]
52+
struct CPUFreqDTDriver;
53+
54+
#[vtable]
55+
impl opp::ConfigOps for CPUFreqDTDriver {}
56+
57+
#[vtable]
58+
impl cpufreq::Driver for CPUFreqDTDriver {
59+
const NAME: &'static CStr = c_str!("cpufreq-dt");
60+
const FLAGS: u16 = cpufreq::flags::NEED_INITIAL_FREQ_CHECK | cpufreq::flags::IS_COOLING_DEV;
61+
const BOOST_ENABLED: bool = true;
62+
63+
type PData = Arc<CPUFreqDTDevice>;
64+
65+
fn init(policy: &mut cpufreq::Policy) -> Result<Self::PData> {
66+
let cpu = policy.cpu();
67+
// SAFETY: The CPU device is only used during init; it won't get hot-unplugged. The cpufreq
68+
// core registers with CPU notifiers and the cpufreq core/driver won't use the CPU device,
69+
// once the CPU is hot-unplugged.
70+
let dev = unsafe { cpu::from_cpu(cpu)? };
71+
let mut mask = CpumaskVar::new_zero(GFP_KERNEL)?;
72+
73+
mask.set(cpu);
74+
75+
let token = find_supply_names(dev, cpu)
76+
.map(|names| {
77+
opp::Config::<Self>::new()
78+
.set_regulator_names(names)?
79+
.set(dev)
80+
})
81+
.transpose()?;
82+
83+
// Get OPP-sharing information from "operating-points-v2" bindings.
84+
let fallback = match opp::Table::of_sharing_cpus(dev, &mut mask) {
85+
Ok(()) => false,
86+
Err(e) if e == ENOENT => {
87+
// "operating-points-v2" not supported. If the platform hasn't
88+
// set sharing CPUs, fallback to all CPUs share the `Policy`
89+
// for backward compatibility.
90+
opp::Table::sharing_cpus(dev, &mut mask).is_err()
91+
}
92+
Err(e) => return Err(e),
93+
};
94+
95+
// Initialize OPP tables for all policy cpus.
96+
//
97+
// For platforms not using "operating-points-v2" bindings, we do this
98+
// before updating policy cpus. Otherwise, we will end up creating
99+
// duplicate OPPs for the CPUs.
100+
//
101+
// OPPs might be populated at runtime, don't fail for error here unless
102+
// it is -EPROBE_DEFER.
103+
let mut opp_table = match opp::Table::from_of_cpumask(dev, &mut mask) {
104+
Ok(table) => table,
105+
Err(e) => {
106+
if e == EPROBE_DEFER {
107+
return Err(e);
108+
}
109+
110+
// The table is added dynamically ?
111+
opp::Table::from_dev(dev)?
112+
}
113+
};
114+
115+
// The OPP table must be initialized, statically or dynamically, by this point.
116+
opp_table.opp_count()?;
117+
118+
// Set sharing cpus for fallback scenario.
119+
if fallback {
120+
mask.setall();
121+
opp_table.set_sharing_cpus(&mut mask)?;
122+
}
123+
124+
let mut transition_latency = opp_table.max_transition_latency_ns() as u32;
125+
if transition_latency == 0 {
126+
transition_latency = cpufreq::ETERNAL_LATENCY_NS;
127+
}
128+
129+
policy
130+
.set_dvfs_possible_from_any_cpu(true)
131+
.set_suspend_freq(opp_table.suspend_freq())
132+
.set_transition_latency_ns(transition_latency);
133+
134+
let freq_table = opp_table.cpufreq_table()?;
135+
// SAFETY: The `freq_table` is not dropped while it is getting used by the C code.
136+
unsafe { policy.set_freq_table(&freq_table) };
137+
138+
// SAFETY: The returned `clk` is not dropped while it is getting used by the C code.
139+
let clk = unsafe { policy.set_clk(dev, None)? };
140+
141+
mask.copy(policy.cpus());
142+
143+
Ok(Arc::new(
144+
CPUFreqDTDevice {
145+
opp_table,
146+
freq_table,
147+
_mask: mask,
148+
_token: token,
149+
_clk: clk,
150+
},
151+
GFP_KERNEL,
152+
)?)
153+
}
154+
155+
fn exit(_policy: &mut cpufreq::Policy, _data: Option<Self::PData>) -> Result {
156+
Ok(())
157+
}
158+
159+
fn online(_policy: &mut cpufreq::Policy) -> Result {
160+
// We did light-weight tear down earlier, nothing to do here.
161+
Ok(())
162+
}
163+
164+
fn offline(_policy: &mut cpufreq::Policy) -> Result {
165+
// Preserve policy->data and don't free resources on light-weight
166+
// tear down.
167+
Ok(())
168+
}
169+
170+
fn suspend(policy: &mut cpufreq::Policy) -> Result {
171+
policy.generic_suspend()
172+
}
173+
174+
fn verify(data: &mut cpufreq::PolicyData) -> Result {
175+
data.generic_verify()
176+
}
177+
178+
fn target_index(policy: &mut cpufreq::Policy, index: cpufreq::TableIndex) -> Result {
179+
let Some(data) = policy.data::<Self::PData>() else {
180+
return Err(ENOENT);
181+
};
182+
183+
let freq = data.freq_table.freq(index)?;
184+
data.opp_table.set_rate(freq)
185+
}
186+
187+
fn get(policy: &mut cpufreq::Policy) -> Result<u32> {
188+
policy.generic_get()
189+
}
190+
191+
fn set_boost(_policy: &mut cpufreq::Policy, _state: i32) -> Result {
192+
Ok(())
193+
}
194+
195+
fn register_em(policy: &mut cpufreq::Policy) {
196+
policy.register_em_opp()
197+
}
198+
}
199+
200+
kernel::of_device_table!(
201+
OF_TABLE,
202+
MODULE_OF_TABLE,
203+
<CPUFreqDTDriver as platform::Driver>::IdInfo,
204+
[(of::DeviceId::new(c_str!("operating-points-v2")), ())]
205+
);
206+
207+
impl platform::Driver for CPUFreqDTDriver {
208+
type IdInfo = ();
209+
const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
210+
211+
fn probe(
212+
pdev: &platform::Device<Core>,
213+
_id_info: Option<&Self::IdInfo>,
214+
) -> Result<Pin<KBox<Self>>> {
215+
cpufreq::Registration::<CPUFreqDTDriver>::new_foreign_owned(pdev.as_ref())?;
216+
Ok(KBox::new(Self {}, GFP_KERNEL)?.into())
217+
}
218+
}
219+
220+
module_platform_driver! {
221+
type: CPUFreqDTDriver,
222+
name: "cpufreq-dt",
223+
author: "Viresh Kumar <viresh.kumar@linaro.org>",
224+
description: "Generic CPUFreq DT driver",
225+
license: "GPL v2",
226+
}

drivers/cpufreq/scmi-cpufreq.c

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,40 @@ static struct cpufreq_driver scmi_cpufreq_driver = {
393393
.set_boost = cpufreq_boost_set_sw,
394394
};
395395

396+
static bool scmi_dev_used_by_cpus(struct device *scmi_dev)
397+
{
398+
struct device_node *scmi_np = dev_of_node(scmi_dev);
399+
struct device_node *cpu_np, *np;
400+
struct device *cpu_dev;
401+
int cpu, idx;
402+
403+
if (!scmi_np)
404+
return false;
405+
406+
for_each_possible_cpu(cpu) {
407+
cpu_dev = get_cpu_device(cpu);
408+
if (!cpu_dev)
409+
continue;
410+
411+
cpu_np = dev_of_node(cpu_dev);
412+
413+
np = of_parse_phandle(cpu_np, "clocks", 0);
414+
of_node_put(np);
415+
416+
if (np == scmi_np)
417+
return true;
418+
419+
idx = of_property_match_string(cpu_np, "power-domain-names", "perf");
420+
np = of_parse_phandle(cpu_np, "power-domains", idx);
421+
of_node_put(np);
422+
423+
if (np == scmi_np)
424+
return true;
425+
}
426+
427+
return false;
428+
}
429+
396430
static int scmi_cpufreq_probe(struct scmi_device *sdev)
397431
{
398432
int ret;
@@ -401,7 +435,7 @@ static int scmi_cpufreq_probe(struct scmi_device *sdev)
401435

402436
handle = sdev->handle;
403437

404-
if (!handle)
438+
if (!handle || !scmi_dev_used_by_cpus(dev))
405439
return -ENODEV;
406440

407441
scmi_cpufreq_driver.driver_data = sdev;

rust/bindings/bindings_helper.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,10 @@
1616
#include <linux/blk-mq.h>
1717
#include <linux/blk_types.h>
1818
#include <linux/blkdev.h>
19+
#include <linux/clk.h>
1920
#include <linux/configfs.h>
21+
#include <linux/cpu.h>
22+
#include <linux/cpufreq.h>
2023
#include <linux/cpumask.h>
2124
#include <linux/cred.h>
2225
#include <linux/device/faux.h>
@@ -35,6 +38,7 @@
3538
#include <linux/phy.h>
3639
#include <linux/pid_namespace.h>
3740
#include <linux/platform_device.h>
41+
#include <linux/pm_opp.h>
3842
#include <linux/poll.h>
3943
#include <linux/property.h>
4044
#include <linux/refcount.h>

0 commit comments

Comments
 (0)