mirror of
git://git.yoctoproject.org/linux-yocto.git
synced 2025-10-23 07:23:12 +02:00
rust: opp: Add abstractions for the configuration options
Introduce Rust abstractions for the OPP core configuration options, enabling safe access to various configurable aspects of the OPP framework. Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
This commit is contained in:
parent
d52c7e868f
commit
ce32e2d47c
|
@ -12,12 +12,29 @@ use crate::{
|
|||
clk::Hertz,
|
||||
cpumask::{Cpumask, CpumaskVar},
|
||||
device::Device,
|
||||
error::{code::*, from_err_ptr, to_result, Error, Result},
|
||||
error::{code::*, from_err_ptr, from_result, to_result, Error, Result, VTABLE_DEFAULT_ERROR},
|
||||
ffi::c_ulong,
|
||||
prelude::*,
|
||||
str::CString,
|
||||
types::{ARef, AlwaysRefCounted, Opaque},
|
||||
};
|
||||
|
||||
use core::ptr;
|
||||
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.
|
||||
///
|
||||
|
@ -205,6 +222,280 @@ pub enum SearchType {
|
|||
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
|
||||
// and so we don't need to save a copy of them for future use.
|
||||
let ret = unsafe { bindings::dev_pm_opp_set_config(dev.as_raw(), &mut config) };
|
||||
if ret < 0 {
|
||||
Err(Error::from_errno(ret))
|
||||
} else {
|
||||
Ok(ConfigToken(ret))
|
||||
}
|
||||
}
|
||||
|
||||
/// Config's clk callback.
|
||||
///
|
||||
/// SAFETY: Called from C. Inputs must be valid pointers.
|
||||
extern "C" fn config_clks(
|
||||
dev: *mut bindings::device,
|
||||
opp_table: *mut bindings::opp_table,
|
||||
opp: *mut bindings::dev_pm_opp,
|
||||
_data: *mut kernel::ffi::c_void,
|
||||
scaling_down: bool,
|
||||
) -> kernel::ffi::c_int {
|
||||
from_result(|| {
|
||||
// SAFETY: 'dev' is guaranteed by the C code to be valid.
|
||||
let dev = unsafe { Device::get_device(dev) };
|
||||
T::config_clks(
|
||||
&dev,
|
||||
// SAFETY: 'opp_table' is guaranteed by the C code to be valid.
|
||||
&unsafe { Table::from_raw_table(opp_table, &dev) },
|
||||
// SAFETY: 'opp' is guaranteed by the C code to be valid.
|
||||
unsafe { OPP::from_raw_opp(opp)? },
|
||||
scaling_down,
|
||||
)
|
||||
.map(|()| 0)
|
||||
})
|
||||
}
|
||||
|
||||
/// Config's regulator callback.
|
||||
///
|
||||
/// SAFETY: Called from C. Inputs must be valid pointers.
|
||||
extern "C" fn config_regulators(
|
||||
dev: *mut bindings::device,
|
||||
old_opp: *mut bindings::dev_pm_opp,
|
||||
new_opp: *mut bindings::dev_pm_opp,
|
||||
regulators: *mut *mut bindings::regulator,
|
||||
count: kernel::ffi::c_uint,
|
||||
) -> kernel::ffi::c_int {
|
||||
from_result(|| {
|
||||
// SAFETY: 'dev' is guaranteed by the C code to be valid.
|
||||
let dev = unsafe { Device::get_device(dev) };
|
||||
T::config_regulators(
|
||||
&dev,
|
||||
// SAFETY: 'old_opp' is guaranteed by the C code to be valid.
|
||||
unsafe { OPP::from_raw_opp(old_opp)? },
|
||||
// SAFETY: 'new_opp' is guaranteed by the C code to be valid.
|
||||
unsafe { OPP::from_raw_opp(new_opp)? },
|
||||
regulators,
|
||||
count,
|
||||
)
|
||||
.map(|()| 0)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A reference-counted OPP table.
|
||||
///
|
||||
/// Rust abstraction for the C `struct opp_table`.
|
||||
|
|
Loading…
Reference in New Issue
Block a user