mirror of
https://github.com/torvalds/linux.git
synced 2026-11-06 21:58:01 +01:00
cpufreq: Add Rust-based cpufreq-dt driver
Introduce a Rust-based implementation of the cpufreq-dt driver, covering most of the functionality provided by the existing C version. Some features, such as retrieving platform data from `cpufreq-dt-platdev.c`, are still pending. The driver has been tested with QEMU, and frequency scaling works as expected. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
This commit is contained in:
parent
14f47156cf
commit
06149d8f22
|
|
@ -217,6 +217,18 @@ config CPUFREQ_DT
|
||||||
|
|
||||||
If in doubt, say N.
|
If in doubt, say N.
|
||||||
|
|
||||||
|
config CPUFREQ_DT_RUST
|
||||||
|
tristate "Rust based Generic DT based cpufreq driver"
|
||||||
|
depends on HAVE_CLK && OF && RUST
|
||||||
|
select CPUFREQ_DT_PLATDEV
|
||||||
|
select PM_OPP
|
||||||
|
help
|
||||||
|
This adds a Rust based generic DT based cpufreq driver for frequency
|
||||||
|
management. It supports both uniprocessor (UP) and symmetric
|
||||||
|
multiprocessor (SMP) systems.
|
||||||
|
|
||||||
|
If in doubt, say N.
|
||||||
|
|
||||||
config CPUFREQ_VIRT
|
config CPUFREQ_VIRT
|
||||||
tristate "Virtual cpufreq driver"
|
tristate "Virtual cpufreq driver"
|
||||||
depends on GENERIC_ARCH_TOPOLOGY
|
depends on GENERIC_ARCH_TOPOLOGY
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ obj-$(CONFIG_CPU_FREQ_GOV_COMMON) += cpufreq_governor.o
|
||||||
obj-$(CONFIG_CPU_FREQ_GOV_ATTR_SET) += cpufreq_governor_attr_set.o
|
obj-$(CONFIG_CPU_FREQ_GOV_ATTR_SET) += cpufreq_governor_attr_set.o
|
||||||
|
|
||||||
obj-$(CONFIG_CPUFREQ_DT) += cpufreq-dt.o
|
obj-$(CONFIG_CPUFREQ_DT) += cpufreq-dt.o
|
||||||
|
obj-$(CONFIG_CPUFREQ_DT_RUST) += rcpufreq_dt.o
|
||||||
obj-$(CONFIG_CPUFREQ_DT_PLATDEV) += cpufreq-dt-platdev.o
|
obj-$(CONFIG_CPUFREQ_DT_PLATDEV) += cpufreq-dt-platdev.o
|
||||||
obj-$(CONFIG_CPUFREQ_VIRT) += virtual-cpufreq.o
|
obj-$(CONFIG_CPUFREQ_VIRT) += virtual-cpufreq.o
|
||||||
|
|
||||||
|
|
|
||||||
226
drivers/cpufreq/rcpufreq_dt.rs
Normal file
226
drivers/cpufreq/rcpufreq_dt.rs
Normal file
|
|
@ -0,0 +1,226 @@
|
||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
|
||||||
|
//! Rust based implementation of the cpufreq-dt driver.
|
||||||
|
|
||||||
|
use kernel::{
|
||||||
|
c_str,
|
||||||
|
clk::Clk,
|
||||||
|
cpu, cpufreq,
|
||||||
|
cpumask::CpumaskVar,
|
||||||
|
device::{Core, Device},
|
||||||
|
error::code::*,
|
||||||
|
fmt,
|
||||||
|
macros::vtable,
|
||||||
|
module_platform_driver, of, opp, platform,
|
||||||
|
prelude::*,
|
||||||
|
str::CString,
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Finds exact supply name from the OF node.
|
||||||
|
fn find_supply_name_exact(dev: &Device, name: &str) -> Option<CString> {
|
||||||
|
let prop_name = CString::try_from_fmt(fmt!("{}-supply", name)).ok()?;
|
||||||
|
dev.property_present(&prop_name)
|
||||||
|
.then(|| CString::try_from_fmt(fmt!("{name}")).ok())
|
||||||
|
.flatten()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Finds supply name for the CPU from DT.
|
||||||
|
fn find_supply_names(dev: &Device, cpu: u32) -> Option<KVec<CString>> {
|
||||||
|
// Try "cpu0" for older DTs, fallback to "cpu".
|
||||||
|
let name = (cpu == 0)
|
||||||
|
.then(|| find_supply_name_exact(dev, "cpu0"))
|
||||||
|
.flatten()
|
||||||
|
.or_else(|| find_supply_name_exact(dev, "cpu"))?;
|
||||||
|
|
||||||
|
let mut list = KVec::with_capacity(1, GFP_KERNEL).ok()?;
|
||||||
|
list.push(name, GFP_KERNEL).ok()?;
|
||||||
|
|
||||||
|
Some(list)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Represents the cpufreq dt device.
|
||||||
|
struct CPUFreqDTDevice {
|
||||||
|
opp_table: opp::Table,
|
||||||
|
freq_table: opp::FreqTable,
|
||||||
|
_mask: CpumaskVar,
|
||||||
|
_token: Option<opp::ConfigToken>,
|
||||||
|
_clk: Clk,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
struct CPUFreqDTDriver;
|
||||||
|
|
||||||
|
#[vtable]
|
||||||
|
impl opp::ConfigOps for CPUFreqDTDriver {}
|
||||||
|
|
||||||
|
#[vtable]
|
||||||
|
impl cpufreq::Driver for CPUFreqDTDriver {
|
||||||
|
const NAME: &'static CStr = c_str!("cpufreq-dt");
|
||||||
|
const FLAGS: u16 = cpufreq::flags::NEED_INITIAL_FREQ_CHECK | cpufreq::flags::IS_COOLING_DEV;
|
||||||
|
const BOOST_ENABLED: bool = true;
|
||||||
|
|
||||||
|
type PData = Arc<CPUFreqDTDevice>;
|
||||||
|
|
||||||
|
fn init(policy: &mut cpufreq::Policy) -> Result<Self::PData> {
|
||||||
|
let cpu = policy.cpu();
|
||||||
|
// SAFETY: The CPU device is only used during init; it won't get hot-unplugged. The cpufreq
|
||||||
|
// core registers with CPU notifiers and the cpufreq core/driver won't use the CPU device,
|
||||||
|
// once the CPU is hot-unplugged.
|
||||||
|
let dev = unsafe { cpu::from_cpu(cpu)? };
|
||||||
|
let mut mask = CpumaskVar::new_zero(GFP_KERNEL)?;
|
||||||
|
|
||||||
|
mask.set(cpu);
|
||||||
|
|
||||||
|
let token = find_supply_names(dev, cpu)
|
||||||
|
.map(|names| {
|
||||||
|
opp::Config::<Self>::new()
|
||||||
|
.set_regulator_names(names)?
|
||||||
|
.set(dev)
|
||||||
|
})
|
||||||
|
.transpose()?;
|
||||||
|
|
||||||
|
// Get OPP-sharing information from "operating-points-v2" bindings.
|
||||||
|
let fallback = match opp::Table::of_sharing_cpus(dev, &mut mask) {
|
||||||
|
Ok(()) => false,
|
||||||
|
Err(e) if e == ENOENT => {
|
||||||
|
// "operating-points-v2" not supported. If the platform hasn't
|
||||||
|
// set sharing CPUs, fallback to all CPUs share the `Policy`
|
||||||
|
// for backward compatibility.
|
||||||
|
opp::Table::sharing_cpus(dev, &mut mask).is_err()
|
||||||
|
}
|
||||||
|
Err(e) => return Err(e),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Initialize OPP tables for all policy cpus.
|
||||||
|
//
|
||||||
|
// For platforms not using "operating-points-v2" bindings, we do this
|
||||||
|
// before updating policy cpus. Otherwise, we will end up creating
|
||||||
|
// duplicate OPPs for the CPUs.
|
||||||
|
//
|
||||||
|
// OPPs might be populated at runtime, don't fail for error here unless
|
||||||
|
// it is -EPROBE_DEFER.
|
||||||
|
let mut opp_table = match opp::Table::from_of_cpumask(dev, &mut mask) {
|
||||||
|
Ok(table) => table,
|
||||||
|
Err(e) => {
|
||||||
|
if e == EPROBE_DEFER {
|
||||||
|
return Err(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
// The table is added dynamically ?
|
||||||
|
opp::Table::from_dev(dev)?
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// The OPP table must be initialized, statically or dynamically, by this point.
|
||||||
|
opp_table.opp_count()?;
|
||||||
|
|
||||||
|
// Set sharing cpus for fallback scenario.
|
||||||
|
if fallback {
|
||||||
|
mask.setall();
|
||||||
|
opp_table.set_sharing_cpus(&mut mask)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut transition_latency = opp_table.max_transition_latency_ns() as u32;
|
||||||
|
if transition_latency == 0 {
|
||||||
|
transition_latency = cpufreq::ETERNAL_LATENCY_NS;
|
||||||
|
}
|
||||||
|
|
||||||
|
policy
|
||||||
|
.set_dvfs_possible_from_any_cpu(true)
|
||||||
|
.set_suspend_freq(opp_table.suspend_freq())
|
||||||
|
.set_transition_latency_ns(transition_latency);
|
||||||
|
|
||||||
|
let freq_table = opp_table.cpufreq_table()?;
|
||||||
|
// SAFETY: The `freq_table` is not dropped while it is getting used by the C code.
|
||||||
|
unsafe { policy.set_freq_table(&freq_table) };
|
||||||
|
|
||||||
|
// SAFETY: The returned `clk` is not dropped while it is getting used by the C code.
|
||||||
|
let clk = unsafe { policy.set_clk(dev, None)? };
|
||||||
|
|
||||||
|
mask.copy(policy.cpus());
|
||||||
|
|
||||||
|
Ok(Arc::new(
|
||||||
|
CPUFreqDTDevice {
|
||||||
|
opp_table,
|
||||||
|
freq_table,
|
||||||
|
_mask: mask,
|
||||||
|
_token: token,
|
||||||
|
_clk: clk,
|
||||||
|
},
|
||||||
|
GFP_KERNEL,
|
||||||
|
)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit(_policy: &mut cpufreq::Policy, _data: Option<Self::PData>) -> Result {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn online(_policy: &mut cpufreq::Policy) -> Result {
|
||||||
|
// We did light-weight tear down earlier, nothing to do here.
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn offline(_policy: &mut cpufreq::Policy) -> Result {
|
||||||
|
// Preserve policy->data and don't free resources on light-weight
|
||||||
|
// tear down.
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn suspend(policy: &mut cpufreq::Policy) -> Result {
|
||||||
|
policy.generic_suspend()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify(data: &mut cpufreq::PolicyData) -> Result {
|
||||||
|
data.generic_verify()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn target_index(policy: &mut cpufreq::Policy, index: cpufreq::TableIndex) -> Result {
|
||||||
|
let Some(data) = policy.data::<Self::PData>() else {
|
||||||
|
return Err(ENOENT);
|
||||||
|
};
|
||||||
|
|
||||||
|
let freq = data.freq_table.freq(index)?;
|
||||||
|
data.opp_table.set_rate(freq)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get(policy: &mut cpufreq::Policy) -> Result<u32> {
|
||||||
|
policy.generic_get()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_boost(_policy: &mut cpufreq::Policy, _state: i32) -> Result {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_em(policy: &mut cpufreq::Policy) {
|
||||||
|
policy.register_em_opp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
kernel::of_device_table!(
|
||||||
|
OF_TABLE,
|
||||||
|
MODULE_OF_TABLE,
|
||||||
|
<CPUFreqDTDriver as platform::Driver>::IdInfo,
|
||||||
|
[(of::DeviceId::new(c_str!("operating-points-v2")), ())]
|
||||||
|
);
|
||||||
|
|
||||||
|
impl platform::Driver for CPUFreqDTDriver {
|
||||||
|
type IdInfo = ();
|
||||||
|
const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
|
||||||
|
|
||||||
|
fn probe(
|
||||||
|
pdev: &platform::Device<Core>,
|
||||||
|
_id_info: Option<&Self::IdInfo>,
|
||||||
|
) -> Result<Pin<KBox<Self>>> {
|
||||||
|
cpufreq::Registration::<CPUFreqDTDriver>::new_foreign_owned(pdev.as_ref())?;
|
||||||
|
Ok(KBox::new(Self {}, GFP_KERNEL)?.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module_platform_driver! {
|
||||||
|
type: CPUFreqDTDriver,
|
||||||
|
name: "cpufreq-dt",
|
||||||
|
author: "Viresh Kumar <viresh.kumar@linaro.org>",
|
||||||
|
description: "Generic CPUFreq DT driver",
|
||||||
|
license: "GPL v2",
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user