1146 lines · 37.3 KB · Rust
RawDownload
// 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, Error, Result, VTABLE_DEFAULT_ERROR},    ffi::c_ulong,    prelude::*,    str::CString,    types::{ARef, AlwaysRefCounted, 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() as _, 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 {        volt.0    }} /// The power unit.////// Represents power in microwatts, wrapping a [`c_ulong`] value.////// ## Examples////// ```/// use kernel::opp::MicroWatt;////// let raw = 1000000;/// let power = MicroWatt(raw);////// assert_eq!(usize::from(power), raw);/// assert_eq!(power, MicroWatt(raw));/// ```#[derive(Copy, Clone, PartialEq, Eq, Debug)]pub struct MicroWatt(pub c_ulong); impl From<MicroWatt> for c_ulong {    #[inline]    fn from(power: MicroWatt) -> Self {        power.0    }} /// Handle for a dynamically created [`OPP`].////// The associated [`OPP`] is automatically removed when the [`Token`] is dropped.////// ## Examples////// The following example demonstrates how to create an [`OPP`] dynamically.////// ```/// use kernel::clk::Hertz;/// use kernel::device::Device;/// use kernel::error::Result;/// use kernel::opp::{Data, MicroVolt, Token};/// use kernel::types::ARef;////// fn create_opp(dev: &ARef<Device>, freq: Hertz, volt: MicroVolt, level: u32) -> Result<Token> {///     let data = Data::new(freq, volt, level, false);//////     // OPP is removed once token goes out of scope.///     data.add_opp(dev)/// }/// ```pub struct Token {    dev: ARef<Device>,    freq: Hertz,} impl Token {    /// Dynamically adds an [`OPP`] and returns a [`Token`] that removes it on drop.    fn new(dev: &ARef<Device>, mut data: Data) -> Result<Self> {        // SAFETY: The requirements are satisfied by the existence of [`Device`] and its safety        // requirements.        to_result(unsafe { bindings::dev_pm_opp_add_dynamic(dev.as_raw(), &mut data.0) })?;        Ok(Self {            dev: dev.clone(),            freq: data.freq(),        })    }} impl Drop for Token {    fn drop(&mut self) {        // SAFETY: The requirements are satisfied by the existence of [`Device`] and its safety        // requirements.        unsafe { bindings::dev_pm_opp_remove(self.dev.as_raw(), self.freq.into()) };    }} /// OPP data.////// Rust abstraction for the C `struct dev_pm_opp_data`, used to define operating performance/// points (OPPs) dynamically.////// ## Examples////// The following example demonstrates how to create an [`OPP`] with [`Data`].////// ```/// use kernel::clk::Hertz;/// use kernel::device::Device;/// use kernel::error::Result;/// use kernel::opp::{Data, MicroVolt, Token};/// use kernel::types::ARef;////// fn create_opp(dev: &ARef<Device>, freq: Hertz, volt: MicroVolt, level: u32) -> Result<Token> {///     let data = Data::new(freq, volt, level, false);//////     // OPP is removed once token goes out of scope.///     data.add_opp(dev)/// }/// ```#[repr(transparent)]pub struct Data(bindings::dev_pm_opp_data); impl Data {    /// Creates a new instance of [`Data`].    ///    /// This can be used to define a dynamic OPP to be added to a device.    pub fn new(freq: Hertz, volt: MicroVolt, level: u32, turbo: bool) -> Self {        Self(bindings::dev_pm_opp_data {            turbo,            freq: freq.into(),            u_volt: volt.into(),            level,        })    }     /// Adds an [`OPP`] dynamically.    ///    /// Returns a [`Token`] that ensures the OPP is automatically removed    /// when it goes out of scope.    #[inline]    pub fn add_opp(self, dev: &ARef<Device>) -> Result<Token> {        Token::new(dev, self)    }     /// Returns the frequency associated with this OPP data.    #[inline]    fn freq(&self) -> Hertz {        Hertz(self.0.freq)    }} /// [`OPP`] search options.////// ## Examples////// Defines how to search for an [`OPP`] in a [`Table`] relative to a frequency.////// ```/// use kernel::clk::Hertz;/// use kernel::error::Result;/// use kernel::opp::{OPP, SearchType, Table};/// use kernel::types::ARef;////// fn find_opp(table: &Table, freq: Hertz) -> Result<ARef<OPP>> {///     let opp = table.opp_from_freq(freq, Some(true), None, SearchType::Exact)?;//////     pr_info!("OPP frequency is: {:?}\n", opp.freq(None));///     pr_info!("OPP voltage is: {:?}\n", opp.voltage());///     pr_info!("OPP level is: {}\n", opp.level());///     pr_info!("OPP power is: {:?}\n", opp.power());//////     Ok(opp)/// }/// ```#[derive(Copy, Clone, Debug, Eq, PartialEq)]pub enum SearchType {    /// Match the exact frequency.    Exact,    /// Find the highest frequency less than or equal to the given value.    Floor,    /// Find the lowest frequency greater than or equal to the given value.    Ceil,} /// OPP configuration callbacks.////// Implement this trait to customize OPP clock and regulator setup for your device.#[vtable]pub trait ConfigOps {    /// This is typically used to scale clocks when transitioning between OPPs.    #[inline]    fn config_clks(_dev: &Device, _table: &Table, _opp: &OPP, _scaling_down: bool) -> Result {        build_error!(VTABLE_DEFAULT_ERROR)    }     /// This provides access to the old and new OPPs, allowing for safe regulator adjustments.    #[inline]    fn config_regulators(        _dev: &Device,        _opp_old: &OPP,        _opp_new: &OPP,        _data: *mut *mut bindings::regulator,        _count: u32,    ) -> Result {        build_error!(VTABLE_DEFAULT_ERROR)    }} /// OPP configuration token.////// Returned by the OPP core when configuration is applied to a [`Device`]. The associated/// configuration is automatically cleared when the token is dropped.pub struct ConfigToken(i32); impl Drop for ConfigToken {    fn drop(&mut self) {        // SAFETY: This is the same token value returned by the C code via `dev_pm_opp_set_config`.        unsafe { bindings::dev_pm_opp_clear_config(self.0) };    }} /// OPP configurations.////// Rust abstraction for the C `struct dev_pm_opp_config`.////// ## Examples////// The following example demonstrates how to set OPP property-name configuration for a [`Device`].////// ```/// use kernel::device::Device;/// use kernel::error::Result;/// use kernel::opp::{Config, ConfigOps, ConfigToken};/// use kernel::str::CString;/// use kernel::types::ARef;/// use kernel::macros::vtable;////// #[derive(Default)]/// struct Driver;////// #[vtable]/// impl ConfigOps for Driver {}////// fn configure(dev: &ARef<Device>) -> Result<ConfigToken> {///     let name = CString::try_from_fmt(fmt!("{}", "slow"))?;//////     // The OPP configuration is cleared once the [`ConfigToken`] goes out of scope.///     Config::<Driver>::new()///         .set_prop_name(name)?///         .set(dev)/// }/// ```#[derive(Default)]pub struct Config<T: ConfigOps>where    T: Default,{    clk_names: Option<KVec<CString>>,    prop_name: Option<CString>,    regulator_names: Option<KVec<CString>>,    supported_hw: Option<KVec<u32>>,     // Tuple containing (required device, index)    required_dev: Option<(ARef<Device>, u32)>,    _data: PhantomData<T>,} impl<T: ConfigOps + Default> Config<T> {    /// Creates a new instance of [`Config`].    #[inline]    pub fn new() -> Self {        Self::default()    }     /// Initializes clock names.    pub fn set_clk_names(mut self, names: KVec<CString>) -> Result<Self> {        if self.clk_names.is_some() {            return Err(EBUSY);        }         if names.is_empty() {            return Err(EINVAL);        }         self.clk_names = Some(names);        Ok(self)    }     /// Initializes property name.    pub fn set_prop_name(mut self, name: CString) -> Result<Self> {        if self.prop_name.is_some() {            return Err(EBUSY);        }         self.prop_name = Some(name);        Ok(self)    }     /// Initializes regulator names.    pub fn set_regulator_names(mut self, names: KVec<CString>) -> Result<Self> {        if self.regulator_names.is_some() {            return Err(EBUSY);        }         if names.is_empty() {            return Err(EINVAL);        }         self.regulator_names = Some(names);         Ok(self)    }     /// Initializes required devices.    pub fn set_required_dev(mut self, dev: ARef<Device>, index: u32) -> Result<Self> {        if self.required_dev.is_some() {            return Err(EBUSY);        }         self.required_dev = Some((dev, index));        Ok(self)    }     /// Initializes supported hardware.    pub fn set_supported_hw(mut self, hw: KVec<u32>) -> Result<Self> {        if self.supported_hw.is_some() {            return Err(EBUSY);        }         if hw.is_empty() {            return Err(EINVAL);        }         self.supported_hw = Some(hw);        Ok(self)    }     /// Sets the configuration with the OPP core.    ///    /// The returned [`ConfigToken`] will remove the configuration when dropped.    pub fn set(self, dev: &Device) -> Result<ConfigToken> {        let (_clk_list, clk_names) = match &self.clk_names {            Some(x) => {                let list = to_c_str_array(x)?;                let ptr = list.as_ptr();                (Some(list), ptr)            }            None => (None, ptr::null()),        };         let (_regulator_list, regulator_names) = match &self.regulator_names {            Some(x) => {                let list = to_c_str_array(x)?;                let ptr = list.as_ptr();                (Some(list), ptr)            }            None => (None, ptr::null()),        };         let prop_name = self            .prop_name            .as_ref()            .map_or(ptr::null(), |p| p.as_char_ptr());         let (supported_hw, supported_hw_count) = self            .supported_hw            .as_ref()            .map_or((ptr::null(), 0), |hw| (hw.as_ptr(), hw.len() as u32));         let (required_dev, required_dev_index) = self            .required_dev            .as_ref()            .map_or((ptr::null_mut(), 0), |(dev, idx)| (dev.as_raw(), *idx));         let mut config = bindings::dev_pm_opp_config {            clk_names,            config_clks: if T::HAS_CONFIG_CLKS {                Some(Self::config_clks)            } else {                None            },            prop_name,            regulator_names,            config_regulators: if T::HAS_CONFIG_REGULATORS {                Some(Self::config_regulators)            } else {                None            },            supported_hw,            supported_hw_count,             required_dev,            required_dev_index,        };         // SAFETY: The requirements are satisfied by the existence of [`Device`] and its safety        // requirements. The OPP core guarantees not to access fields of [`Config`] after this call

Lines 1–500 of 1146.