[PATCH V4 5/8] rust: Extend cpufreq bindings for policy and driver ops
From: Viresh Kumar
Date: Thu Jul 11 2024 - 02:59:45 EST
This extends the cpufreq bindings with bindings for cpufreq policy and
driver operations.
Signed-off-by: Viresh Kumar <viresh.kumar@xxxxxxxxxx>
---
rust/kernel/cpufreq.rs | 315 ++++++++++++++++++++++++++++++++++++++++-
1 file changed, 313 insertions(+), 2 deletions(-)
diff --git a/rust/kernel/cpufreq.rs b/rust/kernel/cpufreq.rs
index 0751ad9459e2..d58bb0bbaad4 100644
--- a/rust/kernel/cpufreq.rs
+++ b/rust/kernel/cpufreq.rs
@@ -7,15 +7,20 @@
//! C header: [`include/linux/cpufreq.h`](srctree/include/linux/cpufreq.h)
use crate::{
- bindings,
- error::{code::*, to_result, Result},
+ bindings, clk, cpumask,
+ device::Device,
+ error::{code::*, from_err_ptr, to_result, Result, VTABLE_DEFAULT_ERROR},
prelude::*,
+ types::ForeignOwnable,
};
use core::{
pin::Pin,
+ ptr::self,
};
+use macros::vtable;
+
/// Default transition latency value.
pub const ETERNAL_LATENCY: u32 = bindings::CPUFREQ_ETERNAL as u32;
@@ -252,3 +257,309 @@ pub fn data(&self, index: usize) -> Result<u32> {
Ok(unsafe { (*self.ptr.add(index)).driver_data })
}
}
+
+/// Equivalent to `struct cpufreq_policy` in the C code.
+pub struct Policy {
+ ptr: *mut bindings::cpufreq_policy,
+ put_cpu: bool,
+ cpumask: cpumask::Cpumask,
+}
+
+impl Policy {
+ /// Creates a new instance of [`Policy`].
+ ///
+ /// # Safety
+ ///
+ /// Callers must ensure that `ptr` is valid and non-null.
+ pub unsafe fn from_raw_policy(ptr: *mut bindings::cpufreq_policy) -> Self {
+ Self {
+ ptr,
+ put_cpu: false,
+ // SAFETY: The pointer is guaranteed to be valid for the lifetime of `Self`. The `cpus`
+ // pointer is guaranteed to be valid by the C code.
+ cpumask: unsafe { cpumask::Cpumask::from_raw((*ptr).cpus) },
+ }
+ }
+
+ fn from_cpu(cpu: u32) -> Result<Self> {
+ // SAFETY: It is safe to call `cpufreq_cpu_get()` for any CPU.
+ let ptr = from_err_ptr(unsafe { bindings::cpufreq_cpu_get(cpu) })?;
+
+ // SAFETY: The pointer is guaranteed to be valid by the C code.
+ let mut policy = unsafe { Policy::from_raw_policy(ptr) };
+ policy.put_cpu = true;
+ Ok(policy)
+ }
+
+ /// Raw pointer to the underlying cpufreq policy.
+ #[inline]
+ pub fn as_raw(&self) -> *mut bindings::cpufreq_policy {
+ self.ptr
+ }
+
+ fn as_ref(&self) -> &bindings::cpufreq_policy {
+ // SAFETY: By the type invariants, we know that `self` owns a reference to the pointer.
+ unsafe { &(*self.ptr) }
+ }
+ fn as_mut_ref(&mut self) -> &mut bindings::cpufreq_policy {
+ // SAFETY: By the type invariants, we know that `self` owns a reference to the pointer.
+ unsafe { &mut (*self.ptr) }
+ }
+
+ /// Returns the primary CPU for a cpufreq policy.
+ pub fn cpu(&self) -> u32 {
+ self.as_ref().cpu
+ }
+
+ /// Returns the minimum frequency for a cpufreq policy.
+ pub fn min(&self) -> u32 {
+ self.as_ref().min
+ }
+
+ /// Returns the maximum frequency for a cpufreq policy.
+ pub fn max(&self) -> u32 {
+ self.as_ref().max
+ }
+
+ /// Returns the current frequency for a cpufreq policy.
+ pub fn cur(&self) -> u32 {
+ self.as_ref().cur
+ }
+
+ /// Sets the suspend frequency for a cpufreq policy.
+ pub fn set_suspend_freq(&mut self, freq: u32) -> &mut Self {
+ self.as_mut_ref().suspend_freq = freq;
+ self
+ }
+
+ /// Returns the suspend frequency for a cpufreq policy.
+ pub fn suspend_freq(&self) -> u32 {
+ self.as_ref().suspend_freq
+ }
+
+ /// Provides a wrapper to the generic suspend routine.
+ pub fn generic_suspend(&self) -> Result<()> {
+ // SAFETY: By the type invariants, we know that `self` owns a reference, so it is safe to
+ // use it now.
+ to_result(unsafe { bindings::cpufreq_generic_suspend(self.as_raw()) })
+ }
+
+ /// Provides a wrapper to the generic get routine.
+ pub fn generic_get(&self) -> Result<u32> {
+ // SAFETY: By the type invariants, we know that `self` owns a reference, so it is safe to
+ // use it now.
+ Ok(unsafe { bindings::cpufreq_generic_get(self.cpu()) })
+ }
+
+ /// Provides a wrapper to the register em with OPP routine.
+ pub fn register_em_opp(&self) {
+ // SAFETY: By the type invariants, we know that `self` owns a reference, so it is safe to
+ // use it now.
+ unsafe { bindings::cpufreq_register_em_with_opp(self.as_raw()) };
+ }
+
+ /// Gets raw pointer to cpufreq policy's CPUs mask.
+ pub fn cpus(&mut self) -> &mut cpumask::Cpumask {
+ &mut self.cpumask
+ }
+
+ /// Sets clock for a cpufreq policy.
+ pub fn set_clk(&mut self, dev: &Device, name: Option<&CStr>) -> Result<clk::Clk> {
+ let clk = clk::Clk::new(dev, name)?;
+ self.as_mut_ref().clk = clk.as_raw();
+ Ok(clk)
+ }
+
+ /// Allows frequency switching code to run on any CPU.
+ pub fn set_dvfs_possible_from_any_cpu(&mut self) -> &mut Self {
+ self.as_mut_ref().dvfs_possible_from_any_cpu = true;
+ self
+ }
+
+ /// Sets transition latency for a cpufreq policy.
+ pub fn set_transition_latency(&mut self, latency: u32) -> &mut Self {
+ self.as_mut_ref().cpuinfo.transition_latency = latency;
+ self
+ }
+
+ /// Returns the cpufreq table for a cpufreq policy. The cpufreq table is recreated in a
+ /// light-weight manner from the raw pointer. The table in C code is not freed once this table
+ /// is dropped.
+ 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.
+ Ok(unsafe { Table::from_raw(self.as_ref().freq_table) })
+ }
+
+ /// Sets the cpufreq table for a cpufreq policy.
+ ///
+ /// The cpufreq driver must guarantee that the frequency table does not get freed while it is
+ /// still being used by the C code.
+ pub fn set_freq_table(&mut self, table: &Table) -> &mut Self {
+ self.as_mut_ref().freq_table = table.as_raw();
+ self
+ }
+
+ /// Returns the data for a cpufreq policy.
+ 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 by us from [`set_data()`].
+ Some(unsafe { T::borrow(self.as_ref().driver_data) })
+ }
+ }
+
+ // Sets the data for a cpufreq policy.
+ fn set_data<T: ForeignOwnable>(&mut self, data: T) -> Result<()> {
+ if self.as_ref().driver_data.is_null() {
+ // Pass 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)
+ }
+ }
+
+ // Returns the data for a cpufreq policy.
+ fn clear_data<T: ForeignOwnable>(&mut self) -> Option<T> {
+ if self.as_ref().driver_data.is_null() {
+ None
+ } else {
+ // 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.
+ let data =
+ Some(unsafe { <T as ForeignOwnable>::from_foreign(self.as_ref().driver_data) });
+ self.as_mut_ref().driver_data = ptr::null_mut();
+ data
+ }
+ }
+}
+
+impl Drop for Policy {
+ fn drop(&mut self) {
+ if self.put_cpu {
+ // SAFETY: By the type invariants, we know that `self` owns a reference, so it is safe to
+ // relinquish it now.
+ unsafe { bindings::cpufreq_cpu_put(self.as_raw()) };
+ }
+ }
+}
+
+/// Operations to be implemented by a cpufreq driver.
+#[vtable]
+pub trait Driver {
+ /// Driver specific data.
+ ///
+ /// Corresponds to the data retrieved via the kernel's
+ /// `cpufreq_get_driver_data()` function.
+ ///
+ /// Require that `Data` implements `ForeignOwnable`. We guarantee to
+ /// never move the underlying wrapped data structure.
+ type Data: ForeignOwnable;
+
+ /// Policy specific data.
+ ///
+ /// Require that `PData` implements `ForeignOwnable`. We guarantee to
+ /// never move the underlying wrapped data structure.
+ type PData: ForeignOwnable;
+
+ /// Policy's init callback.
+ fn init(policy: &mut Policy) -> Result<Self::PData>;
+
+ /// Policy's exit callback.
+ fn exit(_policy: &mut Policy, _data: Option<Self::PData>) -> Result<()> {
+ kernel::build_error(VTABLE_DEFAULT_ERROR)
+ }
+
+ /// Policy's online callback.
+ fn online(_policy: &mut Policy) -> Result<()> {
+ kernel::build_error(VTABLE_DEFAULT_ERROR)
+ }
+
+ /// Policy's offline callback.
+ fn offline(_policy: &mut Policy) -> Result<()> {
+ kernel::build_error(VTABLE_DEFAULT_ERROR)
+ }
+
+ /// Policy's suspend callback.
+ fn suspend(_policy: &mut Policy) -> Result<()> {
+ kernel::build_error(VTABLE_DEFAULT_ERROR)
+ }
+
+ /// Policy's resume callback.
+ fn resume(_policy: &mut Policy) -> Result<()> {
+ kernel::build_error(VTABLE_DEFAULT_ERROR)
+ }
+
+ /// Policy's ready callback.
+ fn ready(_policy: &mut Policy) {
+ kernel::build_error(VTABLE_DEFAULT_ERROR)
+ }
+
+ /// Policy's verify callback.
+ fn verify(data: &mut PolicyData) -> Result<()>;
+
+ /// Policy's setpolicy callback.
+ fn setpolicy(_policy: &mut Policy) -> Result<()> {
+ kernel::build_error(VTABLE_DEFAULT_ERROR)
+ }
+
+ /// Policy's target callback.
+ fn target(_policy: &mut Policy, _target_freq: u32, _relation: Relation) -> Result<()> {
+ kernel::build_error(VTABLE_DEFAULT_ERROR)
+ }
+
+ /// Policy's target_index callback.
+ fn target_index(_policy: &mut Policy, _index: u32) -> Result<()> {
+ kernel::build_error(VTABLE_DEFAULT_ERROR)
+ }
+
+ /// Policy's fast_switch callback.
+ fn fast_switch(_policy: &mut Policy, _target_freq: u32) -> u32 {
+ kernel::build_error(VTABLE_DEFAULT_ERROR)
+ }
+
+ /// Policy's adjust_perf callback.
+ fn adjust_perf(_policy: &mut Policy, _min_perf: u64, _target_perf: u64, _capacity: u64) {
+ kernel::build_error(VTABLE_DEFAULT_ERROR)
+ }
+
+ /// Policy's get_intermediate callback.
+ fn get_intermediate(_policy: &mut Policy, _index: u32) -> u32 {
+ kernel::build_error(VTABLE_DEFAULT_ERROR)
+ }
+
+ /// Policy's target_intermediate callback.
+ fn target_intermediate(_policy: &mut Policy, _index: u32) -> Result<()> {
+ kernel::build_error(VTABLE_DEFAULT_ERROR)
+ }
+
+ /// Policy's get callback.
+ fn get(_policy: &mut Policy) -> Result<u32> {
+ kernel::build_error(VTABLE_DEFAULT_ERROR)
+ }
+
+ /// Policy's update_limits callback.
+ fn update_limits(_policy: &mut Policy) {
+ kernel::build_error(VTABLE_DEFAULT_ERROR)
+ }
+
+ /// Policy's bios_limit callback.
+ fn bios_limit(_policy: &mut Policy, _limit: &mut u32) -> Result<()> {
+ kernel::build_error(VTABLE_DEFAULT_ERROR)
+ }
+
+ /// Policy's set_boost callback.
+ fn set_boost(_policy: &mut Policy, _state: i32) -> Result<()> {
+ kernel::build_error(VTABLE_DEFAULT_ERROR)
+ }
+
+ /// Policy's register_em callback.
+ fn register_em(_policy: &mut Policy) {
+ kernel::build_error(VTABLE_DEFAULT_ERROR)
+ }
+}
--
2.31.1.272.g89b43f80a514