// SPDX-License-Identifier: GPL-2.0
//! Operating performance points.
//!
//! This module provides rust abstractions for interacting with the OPP subsystem.
//!
//! C header: [`include/linux/pm_opp.h`](srctree/include/linux/pm_opp.h)
//!
//! Reference: <https://docs.kernel.org/power/opp.html>
use crate::{
clk::Hertz,
cpumask::{Cpumask, CpumaskVar},
device::Device,
error::{code::*, from_err_ptr, from_result, to_result, Result, VTABLE_DEFAULT_ERROR},
ffi::c_ulong,
prelude::*,
str::CString,
sync::aref::{ARef, AlwaysRefCounted},
types::Opaque,
};
#[cfg(CONFIG_CPU_FREQ)]
/// Frequency table implementation.
mod freq {
use super::*;
use crate::cpufreq;
use core::ops::Deref;
/// OPP frequency table.
///
/// A [`cpufreq::Table`] created from [`Table`].
pub struct FreqTable {
dev: ARef<Device>,
ptr: *mut bindings::cpufreq_frequency_table,
}
impl FreqTable {
/// Creates a new instance of [`FreqTable`] from [`Table`].
pub(crate) fn new(table: &Table) -> Result<Self> {
let mut ptr: *mut bindings::cpufreq_frequency_table = ptr::null_mut();
// SAFETY: The requirements are satisfied by the existence of [`Device`] and its safety
// requirements.
to_result(unsafe {
bindings::dev_pm_opp_init_cpufreq_table(table.dev.as_raw(), &mut ptr)
})?;
Ok(Self {
dev: table.dev.clone(),
ptr,
})
}
/// Returns a reference to the underlying [`cpufreq::Table`].
#[inline]
fn table(&self) -> &cpufreq::Table {
// SAFETY: The `ptr` is guaranteed by the C code to be valid.
unsafe { cpufreq::Table::from_raw(self.ptr) }
}
}
impl Deref for FreqTable {
type Target = cpufreq::Table;
#[inline]
fn deref(&self) -> &Self::Target {
self.table()
}
}
impl Drop for FreqTable {
fn drop(&mut self) {
// SAFETY: The pointer was created via `dev_pm_opp_init_cpufreq_table`, and is only
// freed here.
unsafe {
bindings::dev_pm_opp_free_cpufreq_table(self.dev.as_raw(), &mut self.as_raw())
};
}
}
}
#[cfg(CONFIG_CPU_FREQ)]
pub use freq::FreqTable;
use core::{marker::PhantomData, ptr};
use macros::vtable;
/// Creates a null-terminated slice of pointers to [`Cstring`]s.
fn to_c_str_array(names: &[CString]) -> Result<KVec<*const u8>> {
// Allocated a null-terminated vector of pointers.
let mut list = KVec::with_capacity(names.len() + 1, GFP_KERNEL)?;
for name in names.iter() {
list.push(name.as_ptr().cast(), GFP_KERNEL)?;
}
list.push(ptr::null(), GFP_KERNEL)?;
Ok(list)
}
/// The voltage unit.
///
/// Represents voltage in microvolts, wrapping a [`c_ulong`] value.
///
/// # Examples
///
/// ```
/// use kernel::opp::MicroVolt;
///
/// let raw = 90500;
/// let volt = MicroVolt(raw);
///
/// assert_eq!(usize::from(volt), raw);
/// assert_eq!(volt, MicroVolt(raw));
/// ```
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct MicroVolt(pub c_ulong);
impl From<MicroVolt> for c_ulong {
#[inline]
fn from(volt: MicroVolt) -> Self {