use crate::{
clk::Hertz,
cpu::CpuId,
cpumask,
device::{Bound, Device},
devres,
error::{code::*, from_err_ptr, from_result, to_result, Result, VTABLE_DEFAULT_ERROR},
ffi::{c_char, c_ulong},
prelude::*,
types::ForeignOwnable,
types::Opaque,
};
#[cfg(CONFIG_COMMON_CLK)]
use crate::clk::Clk;
use core::{
cell::UnsafeCell,
marker::PhantomData,
ops::{Deref, DerefMut},
pin::Pin,
ptr,
};
use macros::vtable;
const CPUFREQ_NAME_LEN: usize = bindings::CPUFREQ_NAME_LEN as usize;
pub const DEFAULT_TRANSITION_LATENCY_NS: u32 = bindings::CPUFREQ_DEFAULT_TRANSITION_LATENCY_NS;
pub mod flags {
pub const NEED_UPDATE_LIMITS: u16 = 1 << 0;
pub const CONST_LOOPS: u16 = 1 << 1;
pub const IS_COOLING_DEV: u16 = 1 << 2;
pub const HAVE_GOVERNOR_PER_POLICY: u16 = 1 << 3;
pub const ASYNC_NOTIFICATION: u16 = 1 << 4;
pub const NEED_INITIAL_FREQ_CHECK: u16 = 1 << 5;
pub const NO_AUTO_DYNAMIC_SWITCHING: u16 = 1 << 6;
}
const CPUFREQ_RELATION_L: u32 = 0;
const CPUFREQ_RELATION_H: u32 = 1;
const CPUFREQ_RELATION_C: u32 = 2;
const CPUFREQ_RELATION_E: u32 = 1 << 2;
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum Relation {
Low(bool),
High(bool),
Close(bool),
}
impl Relation {
fn new(val: u32) -> Result<Self> {
let efficient = val & CPUFREQ_RELATION_E != 0;
Ok(match val & !CPUFREQ_RELATION_E {
CPUFREQ_RELATION_L => Self::Low(efficient),
CPUFREQ_RELATION_H => Self::High(efficient),
CPUFREQ_RELATION_C => Self::Close(efficient),
_ => return Err(EINVAL),
})
}
}
impl From<Relation> for u32 {
fn from(rel: Relation) -> Self {
let (mut val, efficient) = match rel {
Relation::Low(e) => (CPUFREQ_RELATION_L, e),
Relation::High(e) => (CPUFREQ_RELATION_H, e),
Relation::Close(e) => (CPUFREQ_RELATION_C, e),
};
if efficient {
val |= CPUFREQ_RELATION_E;
}
val
}
}
#[repr(transparent)]
pub struct PolicyData(Opaque<bindings::cpufreq_policy_data>);
impl PolicyData {
#[inline]
pub unsafe fn from_raw_mut<'a>(ptr: *mut bindings::cpufreq_policy_data) -> &'a mut Self {
unsafe { &mut *ptr.cast() }
}
#[inline]
pub fn as_raw(&self) -> *mut bindings::cpufreq_policy_data {
let this: *const Self = self;
this.cast_mut().cast()
}
#[inline]
pub fn generic_verify(&self) -> Result {
to_result(unsafe { bindings::cpufreq_generic_frequency_table_verify(self.as_raw()) })
}
}
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct TableIndex(usize);
impl TableIndex {
pub unsafe fn new(index: usize) -> Self {
Self(index)
}
}
impl From<TableIndex> for usize {
#[inline]
fn from(index: TableIndex) -> Self {
index.0
}
}
#[repr(transparent)]
pub struct Table(Opaque<bindings::cpufreq_frequency_table>);
impl Table {
#[inline]
pub unsafe fn from_raw<'a>(ptr: *const bindings::cpufreq_frequency_table) -> &'a Self {
unsafe { &*ptr.cast() }
}
#[inline]
pub fn as_raw(&self) -> *mut bindings::cpufreq_frequency_table {
let this: *const Self = self;
this.cast_mut().cast()
}
#[inline]
pub fn freq(&self, index: TableIndex) -> Result<Hertz> {
Ok(Hertz::from_khz(unsafe {
(*self.as_raw().add(index.into())).frequency.try_into()?
}))
}
#[inline]
pub fn flags(&self, index: TableIndex) -> u32 {
unsafe { (*self.as_raw().add(index.into())).flags }
}
#[inline]
pub fn data(&self, index: TableIndex) -> u32 {
unsafe { (*self.as_raw().add(index.into())).driver_data }
}
}
pub struct TableBox {
entries: Pin<KVec<bindings::cpufreq_frequency_table>>,
}
impl TableBox {
#[inline]
fn new(entries: KVec<bindings::cpufreq_frequency_table>) -> Result<Self> {
if entries.is_empty() {
return Err(EINVAL);
}
Ok(Self {
entries: Pin::new(entries),
})
}
#[inline]
fn as_raw(&self) -> *const bindings::cpufreq_frequency_table {
self.entries.as_ptr()
}
}
impl Deref for TableBox {
type Target = Table;
fn deref(&self) -> &Self::Target {
unsafe { Self::Target::from_raw(self.as_raw()) }
}
}
#[derive(Default)]
#[repr(transparent)]
pub struct TableBuilder {
entries: KVec<bindings::cpufreq_frequency_table>,
}
impl TableBuilder {
#[inline]
pub fn new() -> Self {
Self {
entries: KVec::new(),
}
}
pub fn add(&mut self, freq: Hertz, flags: u32, driver_data: u32) -> Result {
Ok(self.entries.push(
bindings::cpufreq_frequency_table {
flags,
driver_data,
frequency: freq.as_khz() as u32,
},
GFP_KERNEL,
)?)
}
pub fn to_table(mut self) -> Result<TableBox> {
self.add(Hertz(c_ulong::MAX), 0, 0)?;
TableBox::new(self.entries)
}
}
#[repr(transparent)]
pub struct Policy(Opaque<bindings::cpufreq_policy>);
impl Policy {
#[inline]
pub unsafe fn from_raw<'a>(ptr: *const bindings::cpufreq_policy) -> &'a Self {
unsafe { &*ptr.cast() }
}
#[inline]
pub unsafe fn from_raw_mut<'a>(ptr: *mut bindings::cpufreq_policy) -> &'a mut Self {
unsafe { &mut *ptr.cast() }
}
#[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 {
unsafe { &*self.as_raw() }
}
#[inline]
fn as_mut_ref(&mut self) -> &mut bindings::cpufreq_policy {
unsafe { &mut *self.as_raw() }
}
#[inline]
pub fn cpu(&self) -> CpuId {
unsafe { CpuId::from_u32_unchecked(self.as_ref().cpu) }
}
#[inline]
pub fn min(&self) -> Hertz {
Hertz::from_khz(self.as_ref().min as usize)
}
#[inline]
pub fn set_min(&mut self, min: Hertz) -> &mut Self {
self.as_mut_ref().min = min.as_khz() as u32;
self
}
#[inline]
pub fn max(&self) -> Hertz {
Hertz::from_khz(self.as_ref().max as usize)
}
#[inline]
pub fn set_max(&mut self, max: Hertz) -> &mut Self {
self.as_mut_ref().max = max.as_khz() as u32;
self
}
#[inline]
pub fn cur(&self) -> Hertz {
Hertz::from_khz(self.as_ref().cur as usize)
}
#[inline]
pub fn suspend_freq(&self) -> Hertz {
Hertz::from_khz(self.as_ref().suspend_freq as usize)
}
#[inline]
pub fn set_suspend_freq(&mut self, freq: Hertz) -> &mut Self {
self.as_mut_ref().suspend_freq = freq.as_khz() as u32;
self
}
#[inline]
pub fn generic_suspend(&mut self) -> Result {
to_result(unsafe { bindings::cpufreq_generic_suspend(self.as_mut_ref()) })
}
#[inline]
pub fn generic_get(&self) -> Result<u32> {
Ok(unsafe { bindings::cpufreq_generic_get(u32::from(self.cpu())) })
}
#[cfg(CONFIG_PM_OPP)]
#[inline]
pub fn register_em_opp(&mut self) {
unsafe { bindings::cpufreq_register_em_with_opp(self.as_mut_ref()) };
}
#[inline]
pub fn cpus(&mut self) -> &mut cpumask::Cpumask {
unsafe { cpumask::CpumaskVar::from_raw_mut(&mut self.as_mut_ref().cpus) }
}
#[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)
}
#[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
}
#[inline]
pub fn fast_switch_possible(&self) -> bool {
self.as_ref().fast_switch_possible
}
#[inline]
pub fn set_fast_switch_possible(&mut self, val: bool) -> &mut Self {
self.as_mut_ref().fast_switch_possible = val;
self
}
#[inline]
pub fn set_transition_latency_ns(&mut self, latency_ns: u32) -> &mut Self {
self.as_mut_ref().cpuinfo.transition_latency = latency_ns;
self
}
#[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
}
#[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
}
#[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
}
pub fn freq_table(&self) -> Result<&Table> {
if self.as_ref().freq_table.is_null() {
return Err(EINVAL);
}
Ok(unsafe { Table::from_raw(self.as_ref().freq_table) })
}
#[inline]
pub unsafe fn set_freq_table(&mut self, table: &Table) -> &mut Self {
self.as_mut_ref().freq_table = table.as_raw();
self
}
pub fn data<T: ForeignOwnable>(&mut self) -> Option<<T>::Borrowed<'_>> {
if self.as_ref().driver_data.is_null() {
None
} else {
Some(unsafe { T::borrow(self.as_ref().driver_data.cast()) })
}
}
fn set_data<T: ForeignOwnable>(&mut self, data: T) -> Result {
if self.as_ref().driver_data.is_null() {
self.as_mut_ref().driver_data = <T as ForeignOwnable>::into_foreign(data).cast();
Ok(())
} else {
Err(EBUSY)
}
}
fn clear_data<T: ForeignOwnable>(&mut self) -> Option<T> {
if self.as_ref().driver_data.is_null() {
None
} else {
let data = Some(
unsafe { <T as ForeignOwnable>::from_foreign(self.as_ref().driver_data.cast()) },
);
self.as_mut_ref().driver_data = ptr::null_mut();
data
}
}
}
struct PolicyCpu<'a>(&'a mut Policy);
impl<'a> PolicyCpu<'a> {
fn from_cpu(cpu: CpuId) -> Result<Self> {
let ptr = from_err_ptr(unsafe { bindings::cpufreq_cpu_get(u32::from(cpu)) })?;
Ok(Self(
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) {
unsafe { bindings::cpufreq_cpu_put(self.0.as_raw()) };
}
}
#[vtable]
pub trait Driver {
const NAME: &'static CStr;
const FLAGS: u16;
const BOOST_ENABLED: bool;
type PData: ForeignOwnable;
fn init(policy: &mut Policy) -> Result<Self::PData>;
fn exit(_policy: &mut Policy, _data: Option<Self::PData>) -> Result {
build_error!(VTABLE_DEFAULT_ERROR)
}
fn online(_policy: &mut Policy) -> Result {
build_error!(VTABLE_DEFAULT_ERROR)
}
fn offline(_policy: &mut Policy) -> Result {
build_error!(VTABLE_DEFAULT_ERROR)
}
fn suspend(_policy: &mut Policy) -> Result {
build_error!(VTABLE_DEFAULT_ERROR)
}
fn resume(_policy: &mut Policy) -> Result {
build_error!(VTABLE_DEFAULT_ERROR)
}
fn ready(_policy: &mut Policy) {
build_error!(VTABLE_DEFAULT_ERROR)
}
fn verify(data: &mut PolicyData) -> Result;
fn setpolicy(_policy: &mut Policy) -> Result {
build_error!(VTABLE_DEFAULT_ERROR)
}
fn target(_policy: &mut Policy, _target_freq: u32, _relation: Relation) -> Result {
build_error!(VTABLE_DEFAULT_ERROR)
}
fn target_index(_policy: &mut Policy, _index: TableIndex) -> Result {
build_error!(VTABLE_DEFAULT_ERROR)
}
fn fast_switch(_policy: &mut Policy, _target_freq: u32) -> u32 {
build_error!(VTABLE_DEFAULT_ERROR)
}
fn adjust_perf(_policy: &mut Policy, _min_perf: usize, _target_perf: usize, _capacity: usize) {
build_error!(VTABLE_DEFAULT_ERROR)
}
fn get_intermediate(_policy: &mut Policy, _index: TableIndex) -> u32 {
build_error!(VTABLE_DEFAULT_ERROR)
}
fn target_intermediate(_policy: &mut Policy, _index: TableIndex) -> Result {
build_error!(VTABLE_DEFAULT_ERROR)
}
fn get(_policy: &mut Policy) -> Result<u32> {
build_error!(VTABLE_DEFAULT_ERROR)
}
fn update_limits(_policy: &mut Policy) {
build_error!(VTABLE_DEFAULT_ERROR)
}
fn bios_limit(_policy: &mut Policy, _limit: &mut u32) -> Result {
build_error!(VTABLE_DEFAULT_ERROR)
}
fn set_boost(_policy: &mut Policy, _state: i32) -> Result {
build_error!(VTABLE_DEFAULT_ERROR)
}
fn register_em(_policy: &mut Policy) {
build_error!(VTABLE_DEFAULT_ERROR)
}
}
#[repr(transparent)]
pub struct Registration<T: Driver>(KBox<UnsafeCell<bindings::cpufreq_driver>>, PhantomData<T>);
unsafe impl<T: Driver> Sync for Registration<T> {}
#[allow(clippy::non_send_fields_in_send_ty)]
unsafe impl<T: Driver> Send for Registration<T> {}
impl<T: Driver> Registration<T> {
const VTABLE: bindings::cpufreq_driver = bindings::cpufreq_driver {
name: Self::copy_name(T::NAME),
boost_enabled: T::BOOST_ENABLED,
flags: T::FLAGS,
init: Some(Self::init_callback),
verify: Some(Self::verify_callback),
setpolicy: if T::HAS_SETPOLICY {
Some(Self::setpolicy_callback)
} else {
None
},
target: if T::HAS_TARGET {
Some(Self::target_callback)
} else {
None
},
target_index: if T::HAS_TARGET_INDEX {
Some(Self::target_index_callback)
} else {
None
},
fast_switch: if T::HAS_FAST_SWITCH {
Some(Self::fast_switch_callback)
} else {
None
},
adjust_perf: if T::HAS_ADJUST_PERF {
Some(Self::adjust_perf_callback)
} else {
None
},
get_intermediate: if T::HAS_GET_INTERMEDIATE {
Some(Self::get_intermediate_callback)
} else {
None
},
target_intermediate: if T::HAS_TARGET_INTERMEDIATE {
Some(Self::target_intermediate_callback)
} else {
None
},
get: if T::HAS_GET {
Some(Self::get_callback)
} else {
None
},
update_limits: if T::HAS_UPDATE_LIMITS {
Some(Self::update_limits_callback)
} else {
None
},
bios_limit: if T::HAS_BIOS_LIMIT {
Some(Self::bios_limit_callback)
} else {
None
},
online: if T::HAS_ONLINE {
Some(Self::online_callback)
} else {
None
},
offline: if T::HAS_OFFLINE {
Some(Self::offline_callback)
} else {
None
},
exit: if T::HAS_EXIT {
Some(Self::exit_callback)
} else {
None
},
suspend: if T::HAS_SUSPEND {
Some(Self::suspend_callback)
} else {
None
},
resume: if T::HAS_RESUME {
Some(Self::resume_callback)
} else {
None
},
ready: if T::HAS_READY {
Some(Self::ready_callback)
} else {
None
},
set_boost: if T::HAS_SET_BOOST {
Some(Self::set_boost_callback)
} else {
None
},
register_em: if T::HAS_REGISTER_EM {
Some(Self::register_em_callback)
} else {
None
},
..pin_init::zeroed()
};
#[inline(always)]
const fn copy_name(name: &'static CStr) -> [c_char; CPUFREQ_NAME_LEN] {
let src = name.to_bytes_with_nul();
let mut dst = [0; CPUFREQ_NAME_LEN];
build_assert!(src.len() <= CPUFREQ_NAME_LEN);
let mut i = 0;
while i < src.len() {
dst[i] = src[i];
i += 1;
}
dst
}
pub fn new() -> Result<Self> {
let mut drv = KBox::new(UnsafeCell::new(Self::VTABLE), GFP_KERNEL)?;
to_result(unsafe { bindings::cpufreq_register_driver(drv.get_mut()) })?;
Ok(Self(drv, PhantomData))
}
pub fn new_foreign_owned(dev: &Device<Bound>) -> Result
where
T: 'static,
{
devres::register(dev, Self::new()?, GFP_KERNEL)
}
}
impl<T: Driver> Registration<T> {
unsafe extern "C" fn init_callback(ptr: *mut bindings::cpufreq_policy) -> c_int {
from_result(|| {
let policy = unsafe { Policy::from_raw_mut(ptr) };
let data = T::init(policy)?;
policy.set_data(data)?;
Ok(0)
})
}
unsafe extern "C" fn exit_callback(ptr: *mut bindings::cpufreq_policy) {
let policy = unsafe { Policy::from_raw_mut(ptr) };
let data = policy.clear_data();
let _ = T::exit(policy, data);
}
unsafe extern "C" fn online_callback(ptr: *mut bindings::cpufreq_policy) -> c_int {
from_result(|| {
let policy = unsafe { Policy::from_raw_mut(ptr) };
T::online(policy).map(|()| 0)
})
}
unsafe extern "C" fn offline_callback(ptr: *mut bindings::cpufreq_policy) -> c_int {
from_result(|| {
let policy = unsafe { Policy::from_raw_mut(ptr) };
T::offline(policy).map(|()| 0)
})
}
unsafe extern "C" fn suspend_callback(ptr: *mut bindings::cpufreq_policy) -> c_int {
from_result(|| {
let policy = unsafe { Policy::from_raw_mut(ptr) };
T::suspend(policy).map(|()| 0)
})
}
unsafe extern "C" fn resume_callback(ptr: *mut bindings::cpufreq_policy) -> c_int {
from_result(|| {
let policy = unsafe { Policy::from_raw_mut(ptr) };
T::resume(policy).map(|()| 0)
})
}
unsafe extern "C" fn ready_callback(ptr: *mut bindings::cpufreq_policy) {
let policy = unsafe { Policy::from_raw_mut(ptr) };
T::ready(policy);
}
unsafe extern "C" fn verify_callback(ptr: *mut bindings::cpufreq_policy_data) -> c_int {
from_result(|| {
let data = unsafe { PolicyData::from_raw_mut(ptr) };
T::verify(data).map(|()| 0)
})
}
unsafe extern "C" fn setpolicy_callback(ptr: *mut bindings::cpufreq_policy) -> c_int {
from_result(|| {
let policy = unsafe { Policy::from_raw_mut(ptr) };
T::setpolicy(policy).map(|()| 0)
})
}
unsafe extern "C" fn target_callback(
ptr: *mut bindings::cpufreq_policy,
target_freq: c_uint,
relation: c_uint,
) -> c_int {
from_result(|| {
let policy = unsafe { Policy::from_raw_mut(ptr) };
T::target(policy, target_freq, Relation::new(relation)?).map(|()| 0)
})
}
unsafe extern "C" fn target_index_callback(
ptr: *mut bindings::cpufreq_policy,
index: c_uint,
) -> c_int {
from_result(|| {
let policy = unsafe { Policy::from_raw_mut(ptr) };
let index = unsafe { TableIndex::new(index as usize) };
T::target_index(policy, index).map(|()| 0)
})
}
unsafe extern "C" fn fast_switch_callback(
ptr: *mut bindings::cpufreq_policy,
target_freq: c_uint,
) -> c_uint {
let policy = unsafe { Policy::from_raw_mut(ptr) };
T::fast_switch(policy, target_freq)
}
unsafe extern "C" fn adjust_perf_callback(
ptr: *mut bindings::cpufreq_policy,
min_perf: c_ulong,
target_perf: c_ulong,
capacity: c_ulong,
) {
let policy = unsafe { Policy::from_raw_mut(ptr) };
T::adjust_perf(policy, min_perf, target_perf, capacity);
}
unsafe extern "C" fn get_intermediate_callback(
ptr: *mut bindings::cpufreq_policy,
index: c_uint,
) -> c_uint {
let policy = unsafe { Policy::from_raw_mut(ptr) };
let index = unsafe { TableIndex::new(index as usize) };
T::get_intermediate(policy, index)
}
unsafe extern "C" fn target_intermediate_callback(
ptr: *mut bindings::cpufreq_policy,
index: c_uint,
) -> c_int {
from_result(|| {
let policy = unsafe { Policy::from_raw_mut(ptr) };
let index = unsafe { TableIndex::new(index as usize) };
T::target_intermediate(policy, index).map(|()| 0)
})
}
unsafe extern "C" fn get_callback(cpu: c_uint) -> c_uint {
let cpu_id = unsafe { CpuId::from_u32_unchecked(cpu) };
PolicyCpu::from_cpu(cpu_id).map_or(0, |mut policy| T::get(&mut policy).map_or(0, |f| f))
}
unsafe extern "C" fn update_limits_callback(ptr: *mut bindings::cpufreq_policy) {
let policy = unsafe { Policy::from_raw_mut(ptr) };
T::update_limits(policy);
}
unsafe extern "C" fn bios_limit_callback(cpu: c_int, limit: *mut c_uint) -> c_int {
let cpu_id = unsafe { CpuId::from_i32_unchecked(cpu) };
from_result(|| {
let mut policy = PolicyCpu::from_cpu(cpu_id)?;
T::bios_limit(&mut policy, &mut (unsafe { *limit })).map(|()| 0)
})
}
unsafe extern "C" fn set_boost_callback(
ptr: *mut bindings::cpufreq_policy,
state: c_int,
) -> c_int {
from_result(|| {
let policy = unsafe { Policy::from_raw_mut(ptr) };
T::set_boost(policy, state).map(|()| 0)
})
}
unsafe extern "C" fn register_em_callback(ptr: *mut bindings::cpufreq_policy) {
let policy = unsafe { Policy::from_raw_mut(ptr) };
T::register_em(policy);
}
}
impl<T: Driver> Drop for Registration<T> {
fn drop(&mut self) {
unsafe { bindings::cpufreq_unregister_driver(self.0.get_mut()) };
}
}