mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-10-29 11:06:29 +08:00
rust: cpufreq: Extend abstractions for policy and driver ops
Extend the cpufreq abstractions to include support for policy handling and driver operations. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
This commit is contained in:
1 parent
2207856ff0
commit
6ebdd7c931
1 file changed
+461
-2
+461
-2
@@ -10,13 +10,25 @@
|
||||
|
||||
use crate::{
|
||||
clk::Hertz,
|
||||
error::{code::*, to_result, Result},
|
||||
cpumask,
|
||||
device::Device,
|
||||
error::{code::*, from_err_ptr, to_result, Result, VTABLE_DEFAULT_ERROR},
|
||||
ffi::c_ulong,
|
||||
prelude::*,
|
||||
types::ForeignOwnable,
|
||||
types::Opaque,
|
||||
};
|
||||
|
||||
use core::{ops::Deref, pin::Pin};
|
||||
#[cfg(CONFIG_COMMON_CLK)]
|
||||
use crate::clk::Clk;
|
||||
|
||||
use core::{
|
||||
ops::{Deref, DerefMut},
|
||||
pin::Pin,
|
||||
ptr,
|
||||
};
|
||||
|
||||
use macros::vtable;
|
||||
|
||||
/// Default transition latency value in nanoseconds.
|
||||
pub const ETERNAL_LATENCY_NS: u32 = bindings::CPUFREQ_ETERNAL as u32;
|
||||
@@ -363,3 +375,450 @@ impl TableBuilder {
|
||||
TableBox::new(self.entries)
|
||||
}
|
||||
}
|
||||
|
||||
/// CPU frequency policy.
|
||||
///
|
||||
/// Rust abstraction for the C `struct cpufreq_policy`.
|
||||
///
|
||||
/// # Invariants
|
||||
///
|
||||
/// A [`Policy`] instance always corresponds to a valid C `struct cpufreq_policy`.
|
||||
///
|
||||
/// The callers must ensure that the `struct cpufreq_policy` is valid for access and remains valid
|
||||
/// for the lifetime of the returned reference.
|
||||
///
|
||||
/// ## Examples
|
||||
///
|
||||
/// The following example demonstrates how to create a CPU frequency table.
|
||||
///
|
||||
/// ```
|
||||
/// use kernel::cpufreq::{ETERNAL_LATENCY_NS, Policy};
|
||||
///
|
||||
/// fn update_policy(policy: &mut Policy) {
|
||||
/// policy
|
||||
/// .set_dvfs_possible_from_any_cpu(true)
|
||||
/// .set_fast_switch_possible(true)
|
||||
/// .set_transition_latency_ns(ETERNAL_LATENCY_NS);
|
||||
///
|
||||
/// pr_info!("The policy details are: {:?}\n", (policy.cpu(), policy.cur()));
|
||||
/// }
|
||||
/// ```
|
||||
#[repr(transparent)]
|
||||
pub struct Policy(Opaque<bindings::cpufreq_policy>);
|
||||
|
||||
impl Policy {
|
||||
/// Creates a reference to an existing `struct cpufreq_policy` pointer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that `ptr` is valid for reading and remains valid for the lifetime
|
||||
/// of the returned reference.
|
||||
#[inline]
|
||||
pub unsafe fn from_raw<'a>(ptr: *const bindings::cpufreq_policy) -> &'a Self {
|
||||
// SAFETY: Guaranteed by the safety requirements of the function.
|
||||
//
|
||||
// INVARIANT: The caller ensures that `ptr` is valid for reading and remains valid for the
|
||||
// lifetime of the returned reference.
|
||||
unsafe { &*ptr.cast() }
|
||||
}
|
||||
|
||||
/// Creates a mutable reference to an existing `struct cpufreq_policy` pointer.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must ensure that `ptr` is valid for writing and remains valid for the lifetime
|
||||
/// of the returned reference.
|
||||
#[inline]
|
||||
pub unsafe fn from_raw_mut<'a>(ptr: *mut bindings::cpufreq_policy) -> &'a mut Self {
|
||||
// SAFETY: Guaranteed by the safety requirements of the function.
|
||||
//
|
||||
// INVARIANT: The caller ensures that `ptr` is valid for writing and remains valid for the
|
||||
// lifetime of the returned reference.
|
||||
unsafe { &mut *ptr.cast() }
|
||||
}
|
||||
|
||||
/// Returns a raw mutable pointer to the C `struct cpufreq_policy`.
|
||||
#[inline]
|
||||
fn as_raw(&self) -> *mut bindings::cpufreq_policy {
|
||||
let this: *const Self = self;
|
||||
this.cast_mut().cast()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_ref(&self) -> &bindings::cpufreq_policy {
|
||||
// SAFETY: By the type invariant, the pointer stored in `self` is valid.
|
||||
unsafe { &*self.as_raw() }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn as_mut_ref(&mut self) -> &mut bindings::cpufreq_policy {
|
||||
// SAFETY: By the type invariant, the pointer stored in `self` is valid.
|
||||
unsafe { &mut *self.as_raw() }
|
||||
}
|
||||
|
||||
/// Returns the primary CPU for the [`Policy`].
|
||||
#[inline]
|
||||
pub fn cpu(&self) -> u32 {
|
||||
self.as_ref().cpu
|
||||
}
|
||||
|
||||
/// Returns the minimum frequency for the [`Policy`].
|
||||
#[inline]
|
||||
pub fn min(&self) -> Hertz {
|
||||
Hertz::from_khz(self.as_ref().min as usize)
|
||||
}
|
||||
|
||||
/// Set the minimum frequency for the [`Policy`].
|
||||
#[inline]
|
||||
pub fn set_min(&mut self, min: Hertz) -> &mut Self {
|
||||
self.as_mut_ref().min = min.as_khz() as u32;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the maximum frequency for the [`Policy`].
|
||||
#[inline]
|
||||
pub fn max(&self) -> Hertz {
|
||||
Hertz::from_khz(self.as_ref().max as usize)
|
||||
}
|
||||
|
||||
/// Set the maximum frequency for the [`Policy`].
|
||||
#[inline]
|
||||
pub fn set_max(&mut self, max: Hertz) -> &mut Self {
|
||||
self.as_mut_ref().max = max.as_khz() as u32;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the current frequency for the [`Policy`].
|
||||
#[inline]
|
||||
pub fn cur(&self) -> Hertz {
|
||||
Hertz::from_khz(self.as_ref().cur as usize)
|
||||
}
|
||||
|
||||
/// Returns the suspend frequency for the [`Policy`].
|
||||
#[inline]
|
||||
pub fn suspend_freq(&self) -> Hertz {
|
||||
Hertz::from_khz(self.as_ref().suspend_freq as usize)
|
||||
}
|
||||
|
||||
/// Sets the suspend frequency for the [`Policy`].
|
||||
#[inline]
|
||||
pub fn set_suspend_freq(&mut self, freq: Hertz) -> &mut Self {
|
||||
self.as_mut_ref().suspend_freq = freq.as_khz() as u32;
|
||||
self
|
||||
}
|
||||
|
||||
/// Provides a wrapper to the generic suspend routine.
|
||||
#[inline]
|
||||
pub fn generic_suspend(&mut self) -> Result {
|
||||
// SAFETY: By the type invariant, the pointer stored in `self` is valid.
|
||||
to_result(unsafe { bindings::cpufreq_generic_suspend(self.as_mut_ref()) })
|
||||
}
|
||||
|
||||
/// Provides a wrapper to the generic get routine.
|
||||
#[inline]
|
||||
pub fn generic_get(&self) -> Result<u32> {
|
||||
// SAFETY: By the type invariant, the pointer stored in `self` is valid.
|
||||
Ok(unsafe { bindings::cpufreq_generic_get(self.cpu()) })
|
||||
}
|
||||
|
||||
/// Provides a wrapper to the register with energy model using the OPP core.
|
||||
#[cfg(CONFIG_PM_OPP)]
|
||||
#[inline]
|
||||
pub fn register_em_opp(&mut self) {
|
||||
// SAFETY: By the type invariant, the pointer stored in `self` is valid.
|
||||
unsafe { bindings::cpufreq_register_em_with_opp(self.as_mut_ref()) };
|
||||
}
|
||||
|
||||
/// Gets [`cpumask::Cpumask`] for a cpufreq [`Policy`].
|
||||
#[inline]
|
||||
pub fn cpus(&mut self) -> &mut cpumask::Cpumask {
|
||||
// SAFETY: The pointer to `cpus` is valid for writing and remains valid for the lifetime of
|
||||
// the returned reference.
|
||||
unsafe { cpumask::CpumaskVar::as_mut_ref(&mut self.as_mut_ref().cpus) }
|
||||
}
|
||||
|
||||
/// Sets clock for the [`Policy`].
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must guarantee that the returned [`Clk`] is not dropped while it is getting used
|
||||
/// by the C code.
|
||||
#[cfg(CONFIG_COMMON_CLK)]
|
||||
pub unsafe fn set_clk(&mut self, dev: &Device, name: Option<&CStr>) -> Result<Clk> {
|
||||
let clk = Clk::get(dev, name)?;
|
||||
self.as_mut_ref().clk = clk.as_raw();
|
||||
Ok(clk)
|
||||
}
|
||||
|
||||
/// Allows / disallows frequency switching code to run on any CPU.
|
||||
#[inline]
|
||||
pub fn set_dvfs_possible_from_any_cpu(&mut self, val: bool) -> &mut Self {
|
||||
self.as_mut_ref().dvfs_possible_from_any_cpu = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns if fast switching of frequencies is possible or not.
|
||||
#[inline]
|
||||
pub fn fast_switch_possible(&self) -> bool {
|
||||
self.as_ref().fast_switch_possible
|
||||
}
|
||||
|
||||
/// Enables / disables fast frequency switching.
|
||||
#[inline]
|
||||
pub fn set_fast_switch_possible(&mut self, val: bool) -> &mut Self {
|
||||
self.as_mut_ref().fast_switch_possible = val;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets transition latency (in nanoseconds) for the [`Policy`].
|
||||
#[inline]
|
||||
pub fn set_transition_latency_ns(&mut self, latency_ns: u32) -> &mut Self {
|
||||
self.as_mut_ref().cpuinfo.transition_latency = latency_ns;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets cpuinfo `min_freq`.
|
||||
#[inline]
|
||||
pub fn set_cpuinfo_min_freq(&mut self, min_freq: Hertz) -> &mut Self {
|
||||
self.as_mut_ref().cpuinfo.min_freq = min_freq.as_khz() as u32;
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets cpuinfo `max_freq`.
|
||||
#[inline]
|
||||
pub fn set_cpuinfo_max_freq(&mut self, max_freq: Hertz) -> &mut Self {
|
||||
self.as_mut_ref().cpuinfo.max_freq = max_freq.as_khz() as u32;
|
||||
self
|
||||
}
|
||||
|
||||
/// Set `transition_delay_us`, i.e. the minimum time between successive frequency change
|
||||
/// requests.
|
||||
#[inline]
|
||||
pub fn set_transition_delay_us(&mut self, transition_delay_us: u32) -> &mut Self {
|
||||
self.as_mut_ref().transition_delay_us = transition_delay_us;
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns reference to the CPU frequency [`Table`] for the [`Policy`].
|
||||
pub fn freq_table(&self) -> Result<&Table> {
|
||||
if self.as_ref().freq_table.is_null() {
|
||||
return Err(EINVAL);
|
||||
}
|
||||
|
||||
// SAFETY: The `freq_table` is guaranteed to be valid for reading and remains valid for the
|
||||
// lifetime of the returned reference.
|
||||
Ok(unsafe { Table::from_raw(self.as_ref().freq_table) })
|
||||
}
|
||||
|
||||
/// Sets the CPU frequency [`Table`] for the [`Policy`].
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The caller must guarantee that the [`Table`] is not dropped while it is getting used by the
|
||||
/// C code.
|
||||
#[inline]
|
||||
pub unsafe fn set_freq_table(&mut self, table: &Table) -> &mut Self {
|
||||
self.as_mut_ref().freq_table = table.as_raw();
|
||||
self
|
||||
}
|
||||
|
||||
/// Returns the [`Policy`]'s private data.
|
||||
pub fn data<T: ForeignOwnable>(&mut self) -> Option<<T>::Borrowed<'_>> {
|
||||
if self.as_ref().driver_data.is_null() {
|
||||
None
|
||||
} else {
|
||||
// SAFETY: The data is earlier set from [`set_data`].
|
||||
Some(unsafe { T::borrow(self.as_ref().driver_data) })
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the private data of the [`Policy`] using a foreign-ownable wrapper.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Returns `EBUSY` if private data is already set.
|
||||
fn set_data<T: ForeignOwnable>(&mut self, data: T) -> Result {
|
||||
if self.as_ref().driver_data.is_null() {
|
||||
// Transfer the ownership of the data to the foreign interface.
|
||||
self.as_mut_ref().driver_data = <T as ForeignOwnable>::into_foreign(data) as _;
|
||||
Ok(())
|
||||
} else {
|
||||
Err(EBUSY)
|
||||
}
|
||||
}
|
||||
|
||||
/// Clears and returns ownership of the private data.
|
||||
fn clear_data<T: ForeignOwnable>(&mut self) -> Option<T> {
|
||||
if self.as_ref().driver_data.is_null() {
|
||||
None
|
||||
} else {
|
||||
let data = Some(
|
||||
// SAFETY: The data is earlier set by us from [`set_data`]. It is safe to take
|
||||
// back the ownership of the data from the foreign interface.
|
||||
unsafe { <T as ForeignOwnable>::from_foreign(self.as_ref().driver_data) },
|
||||
);
|
||||
self.as_mut_ref().driver_data = ptr::null_mut();
|
||||
data
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// CPU frequency policy created from a CPU number.
|
||||
///
|
||||
/// This struct represents the CPU frequency policy obtained for a specific CPU, providing safe
|
||||
/// access to the underlying `cpufreq_policy` and ensuring proper cleanup when the `PolicyCpu` is
|
||||
/// dropped.
|
||||
struct PolicyCpu<'a>(&'a mut Policy);
|
||||
|
||||
impl<'a> PolicyCpu<'a> {
|
||||
fn from_cpu(cpu: u32) -> 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(cpu) })?;
|
||||
|
||||
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) },
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Deref for PolicyCpu<'a> {
|
||||
type Target = Policy;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> DerefMut for PolicyCpu<'a> {
|
||||
fn deref_mut(&mut self) -> &mut Policy {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Drop for PolicyCpu<'a> {
|
||||
fn drop(&mut self) {
|
||||
// SAFETY: The underlying pointer is guaranteed to be valid for the lifetime of `self`.
|
||||
unsafe { bindings::cpufreq_cpu_put(self.0.as_raw()) };
|
||||
}
|
||||
}
|
||||
|
||||
/// CPU frequency driver.
|
||||
///
|
||||
/// Implement this trait to provide a CPU frequency driver and its callbacks.
|
||||
///
|
||||
/// Reference: <https://docs.kernel.org/cpu-freq/cpu-drivers.html>
|
||||
#[vtable]
|
||||
pub trait Driver {
|
||||
/// Driver's name.
|
||||
const NAME: &'static CStr;
|
||||
|
||||
/// Driver's flags.
|
||||
const FLAGS: u16;
|
||||
|
||||
/// Boost support.
|
||||
const BOOST_ENABLED: bool;
|
||||
|
||||
/// Policy specific data.
|
||||
///
|
||||
/// Require that `PData` implements `ForeignOwnable`. We guarantee to never move the underlying
|
||||
/// wrapped data structure.
|
||||
type PData: ForeignOwnable;
|
||||
|
||||
/// Driver's `init` callback.
|
||||
fn init(policy: &mut Policy) -> Result<Self::PData>;
|
||||
|
||||
/// Driver's `exit` callback.
|
||||
fn exit(_policy: &mut Policy, _data: Option<Self::PData>) -> Result {
|
||||
build_error!(VTABLE_DEFAULT_ERROR)
|
||||
}
|
||||
|
||||
/// Driver's `online` callback.
|
||||
fn online(_policy: &mut Policy) -> Result {
|
||||
build_error!(VTABLE_DEFAULT_ERROR)
|
||||
}
|
||||
|
||||
/// Driver's `offline` callback.
|
||||
fn offline(_policy: &mut Policy) -> Result {
|
||||
build_error!(VTABLE_DEFAULT_ERROR)
|
||||
}
|
||||
|
||||
/// Driver's `suspend` callback.
|
||||
fn suspend(_policy: &mut Policy) -> Result {
|
||||
build_error!(VTABLE_DEFAULT_ERROR)
|
||||
}
|
||||
|
||||
/// Driver's `resume` callback.
|
||||
fn resume(_policy: &mut Policy) -> Result {
|
||||
build_error!(VTABLE_DEFAULT_ERROR)
|
||||
}
|
||||
|
||||
/// Driver's `ready` callback.
|
||||
fn ready(_policy: &mut Policy) {
|
||||
build_error!(VTABLE_DEFAULT_ERROR)
|
||||
}
|
||||
|
||||
/// Driver's `verify` callback.
|
||||
fn verify(data: &mut PolicyData) -> Result;
|
||||
|
||||
/// Driver's `setpolicy` callback.
|
||||
fn setpolicy(_policy: &mut Policy) -> Result {
|
||||
build_error!(VTABLE_DEFAULT_ERROR)
|
||||
}
|
||||
|
||||
/// Driver's `target` callback.
|
||||
fn target(_policy: &mut Policy, _target_freq: u32, _relation: Relation) -> Result {
|
||||
build_error!(VTABLE_DEFAULT_ERROR)
|
||||
}
|
||||
|
||||
/// Driver's `target_index` callback.
|
||||
fn target_index(_policy: &mut Policy, _index: TableIndex) -> Result {
|
||||
build_error!(VTABLE_DEFAULT_ERROR)
|
||||
}
|
||||
|
||||
/// Driver's `fast_switch` callback.
|
||||
fn fast_switch(_policy: &mut Policy, _target_freq: u32) -> u32 {
|
||||
build_error!(VTABLE_DEFAULT_ERROR)
|
||||
}
|
||||
|
||||
/// Driver's `adjust_perf` callback.
|
||||
fn adjust_perf(_policy: &mut Policy, _min_perf: usize, _target_perf: usize, _capacity: usize) {
|
||||
build_error!(VTABLE_DEFAULT_ERROR)
|
||||
}
|
||||
|
||||
/// Driver's `get_intermediate` callback.
|
||||
fn get_intermediate(_policy: &mut Policy, _index: TableIndex) -> u32 {
|
||||
build_error!(VTABLE_DEFAULT_ERROR)
|
||||
}
|
||||
|
||||
/// Driver's `target_intermediate` callback.
|
||||
fn target_intermediate(_policy: &mut Policy, _index: TableIndex) -> Result {
|
||||
build_error!(VTABLE_DEFAULT_ERROR)
|
||||
}
|
||||
|
||||
/// Driver's `get` callback.
|
||||
fn get(_policy: &mut Policy) -> Result<u32> {
|
||||
build_error!(VTABLE_DEFAULT_ERROR)
|
||||
}
|
||||
|
||||
/// Driver's `update_limits` callback.
|
||||
fn update_limits(_policy: &mut Policy) {
|
||||
build_error!(VTABLE_DEFAULT_ERROR)
|
||||
}
|
||||
|
||||
/// Driver's `bios_limit` callback.
|
||||
fn bios_limit(_policy: &mut Policy, _limit: &mut u32) -> Result {
|
||||
build_error!(VTABLE_DEFAULT_ERROR)
|
||||
}
|
||||
|
||||
/// Driver's `set_boost` callback.
|
||||
fn set_boost(_policy: &mut Policy, _state: i32) -> Result {
|
||||
build_error!(VTABLE_DEFAULT_ERROR)
|
||||
}
|
||||
|
||||
/// Driver's `register_em` callback.
|
||||
fn register_em(_policy: &mut Policy) {
|
||||
build_error!(VTABLE_DEFAULT_ERROR)
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user