Skip to content

Commit ce32e2d

Browse files
committed
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>
1 parent d52c7e8 commit ce32e2d

File tree

1 file changed

+293
-2
lines changed

1 file changed

+293
-2
lines changed

rust/kernel/opp.rs

Lines changed: 293 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,29 @@ use crate::{
1212
clk::Hertz,
1313
cpumask::{Cpumask, CpumaskVar},
1414
device::Device,
15-
error::{code::*, from_err_ptr, to_result, Error, Result},
15+
error::{code::*, from_err_ptr, from_result, to_result, Error, Result, VTABLE_DEFAULT_ERROR},
1616
ffi::c_ulong,
17+
prelude::*,
18+
str::CString,
1719
types::{ARef, AlwaysRefCounted, Opaque},
1820
};
1921

20-
use core::ptr;
22+
use core::{marker::PhantomData, ptr};
23+
24+
use macros::vtable;
25+
26+
/// Creates a null-terminated slice of pointers to [`Cstring`]s.
27+
fn to_c_str_array(names: &[CString]) -> Result<KVec<*const u8>> {
28+
// Allocated a null-terminated vector of pointers.
29+
let mut list = KVec::with_capacity(names.len() + 1, GFP_KERNEL)?;
30+
31+
for name in names.iter() {
32+
list.push(name.as_ptr() as _, GFP_KERNEL)?;
33+
}
34+
35+
list.push(ptr::null(), GFP_KERNEL)?;
36+
Ok(list)
37+
}
2138

2239
/// The voltage unit.
2340
///
@@ -205,6 +222,280 @@ pub enum SearchType {
205222
Ceil,
206223
}
207224

225+
/// OPP configuration callbacks.
226+
///
227+
/// Implement this trait to customize OPP clock and regulator setup for your device.
228+
#[vtable]
229+
pub trait ConfigOps {
230+
/// This is typically used to scale clocks when transitioning between OPPs.
231+
#[inline]
232+
fn config_clks(_dev: &Device, _table: &Table, _opp: &OPP, _scaling_down: bool) -> Result {
233+
build_error!(VTABLE_DEFAULT_ERROR)
234+
}
235+
236+
/// This provides access to the old and new OPPs, allowing for safe regulator adjustments.
237+
#[inline]
238+
fn config_regulators(
239+
_dev: &Device,
240+
_opp_old: &OPP,
241+
_opp_new: &OPP,
242+
_data: *mut *mut bindings::regulator,
243+
_count: u32,
244+
) -> Result {
245+
build_error!(VTABLE_DEFAULT_ERROR)
246+
}
247+
}
248+
249+
/// OPP configuration token.
250+
///
251+
/// Returned by the OPP core when configuration is applied to a [`Device`]. The associated
252+
/// configuration is automatically cleared when the token is dropped.
253+
pub struct ConfigToken(i32);
254+
255+
impl Drop for ConfigToken {
256+
fn drop(&mut self) {
257+
// SAFETY: This is the same token value returned by the C code via `dev_pm_opp_set_config`.
258+
unsafe { bindings::dev_pm_opp_clear_config(self.0) };
259+
}
260+
}
261+
262+
/// OPP configurations.
263+
///
264+
/// Rust abstraction for the C `struct dev_pm_opp_config`.
265+
///
266+
/// ## Examples
267+
///
268+
/// The following example demonstrates how to set OPP property-name configuration for a [`Device`].
269+
///
270+
/// ```
271+
/// use kernel::device::Device;
272+
/// use kernel::error::Result;
273+
/// use kernel::opp::{Config, ConfigOps, ConfigToken};
274+
/// use kernel::str::CString;
275+
/// use kernel::types::ARef;
276+
/// use kernel::macros::vtable;
277+
///
278+
/// #[derive(Default)]
279+
/// struct Driver;
280+
///
281+
/// #[vtable]
282+
/// impl ConfigOps for Driver {}
283+
///
284+
/// fn configure(dev: &ARef<Device>) -> Result<ConfigToken> {
285+
/// let name = CString::try_from_fmt(fmt!("{}", "slow"))?;
286+
///
287+
/// // The OPP configuration is cleared once the [`ConfigToken`] goes out of scope.
288+
/// Config::<Driver>::new()
289+
/// .set_prop_name(name)?
290+
/// .set(dev)
291+
/// }
292+
/// ```
293+
#[derive(Default)]
294+
pub struct Config<T: ConfigOps>
295+
where
296+
T: Default,
297+
{
298+
clk_names: Option<KVec<CString>>,
299+
prop_name: Option<CString>,
300+
regulator_names: Option<KVec<CString>>,
301+
supported_hw: Option<KVec<u32>>,
302+
303+
// Tuple containing (required device, index)
304+
required_dev: Option<(ARef<Device>, u32)>,
305+
_data: PhantomData<T>,
306+
}
307+
308+
impl<T: ConfigOps + Default> Config<T> {
309+
/// Creates a new instance of [`Config`].
310+
#[inline]
311+
pub fn new() -> Self {
312+
Self::default()
313+
}
314+
315+
/// Initializes clock names.
316+
pub fn set_clk_names(mut self, names: KVec<CString>) -> Result<Self> {
317+
if self.clk_names.is_some() {
318+
return Err(EBUSY);
319+
}
320+
321+
if names.is_empty() {
322+
return Err(EINVAL);
323+
}
324+
325+
self.clk_names = Some(names);
326+
Ok(self)
327+
}
328+
329+
/// Initializes property name.
330+
pub fn set_prop_name(mut self, name: CString) -> Result<Self> {
331+
if self.prop_name.is_some() {
332+
return Err(EBUSY);
333+
}
334+
335+
self.prop_name = Some(name);
336+
Ok(self)
337+
}
338+
339+
/// Initializes regulator names.
340+
pub fn set_regulator_names(mut self, names: KVec<CString>) -> Result<Self> {
341+
if self.regulator_names.is_some() {
342+
return Err(EBUSY);
343+
}
344+
345+
if names.is_empty() {
346+
return Err(EINVAL);
347+
}
348+
349+
self.regulator_names = Some(names);
350+
351+
Ok(self)
352+
}
353+
354+
/// Initializes required devices.
355+
pub fn set_required_dev(mut self, dev: ARef<Device>, index: u32) -> Result<Self> {
356+
if self.required_dev.is_some() {
357+
return Err(EBUSY);
358+
}
359+
360+
self.required_dev = Some((dev, index));
361+
Ok(self)
362+
}
363+
364+
/// Initializes supported hardware.
365+
pub fn set_supported_hw(mut self, hw: KVec<u32>) -> Result<Self> {
366+
if self.supported_hw.is_some() {
367+
return Err(EBUSY);
368+
}
369+
370+
if hw.is_empty() {
371+
return Err(EINVAL);
372+
}
373+
374+
self.supported_hw = Some(hw);
375+
Ok(self)
376+
}
377+
378+
/// Sets the configuration with the OPP core.
379+
///
380+
/// The returned [`ConfigToken`] will remove the configuration when dropped.
381+
pub fn set(self, dev: &Device) -> Result<ConfigToken> {
382+
let (_clk_list, clk_names) = match &self.clk_names {
383+
Some(x) => {
384+
let list = to_c_str_array(x)?;
385+
let ptr = list.as_ptr();
386+
(Some(list), ptr)
387+
}
388+
None => (None, ptr::null()),
389+
};
390+
391+
let (_regulator_list, regulator_names) = match &self.regulator_names {
392+
Some(x) => {
393+
let list = to_c_str_array(x)?;
394+
let ptr = list.as_ptr();
395+
(Some(list), ptr)
396+
}
397+
None => (None, ptr::null()),
398+
};
399+
400+
let prop_name = self
401+
.prop_name
402+
.as_ref()
403+
.map_or(ptr::null(), |p| p.as_char_ptr());
404+
405+
let (supported_hw, supported_hw_count) = self
406+
.supported_hw
407+
.as_ref()
408+
.map_or((ptr::null(), 0), |hw| (hw.as_ptr(), hw.len() as u32));
409+
410+
let (required_dev, required_dev_index) = self
411+
.required_dev
412+
.as_ref()
413+
.map_or((ptr::null_mut(), 0), |(dev, idx)| (dev.as_raw(), *idx));
414+
415+
let mut config = bindings::dev_pm_opp_config {
416+
clk_names,
417+
config_clks: if T::HAS_CONFIG_CLKS {
418+
Some(Self::config_clks)
419+
} else {
420+
None
421+
},
422+
prop_name,
423+
regulator_names,
424+
config_regulators: if T::HAS_CONFIG_REGULATORS {
425+
Some(Self::config_regulators)
426+
} else {
427+
None
428+
},
429+
supported_hw,
430+
supported_hw_count,
431+
432+
required_dev,
433+
required_dev_index,
434+
};
435+
436+
// SAFETY: The requirements are satisfied by the existence of [`Device`] and its safety
437+
// requirements. The OPP core guarantees not to access fields of [`Config`] after this call
438+
// and so we don't need to save a copy of them for future use.
439+
let ret = unsafe { bindings::dev_pm_opp_set_config(dev.as_raw(), &mut config) };
440+
if ret < 0 {
441+
Err(Error::from_errno(ret))
442+
} else {
443+
Ok(ConfigToken(ret))
444+
}
445+
}
446+
447+
/// Config's clk callback.
448+
///
449+
/// SAFETY: Called from C. Inputs must be valid pointers.
450+
extern "C" fn config_clks(
451+
dev: *mut bindings::device,
452+
opp_table: *mut bindings::opp_table,
453+
opp: *mut bindings::dev_pm_opp,
454+
_data: *mut kernel::ffi::c_void,
455+
scaling_down: bool,
456+
) -> kernel::ffi::c_int {
457+
from_result(|| {
458+
// SAFETY: 'dev' is guaranteed by the C code to be valid.
459+
let dev = unsafe { Device::get_device(dev) };
460+
T::config_clks(
461+
&dev,
462+
// SAFETY: 'opp_table' is guaranteed by the C code to be valid.
463+
&unsafe { Table::from_raw_table(opp_table, &dev) },
464+
// SAFETY: 'opp' is guaranteed by the C code to be valid.
465+
unsafe { OPP::from_raw_opp(opp)? },
466+
scaling_down,
467+
)
468+
.map(|()| 0)
469+
})
470+
}
471+
472+
/// Config's regulator callback.
473+
///
474+
/// SAFETY: Called from C. Inputs must be valid pointers.
475+
extern "C" fn config_regulators(
476+
dev: *mut bindings::device,
477+
old_opp: *mut bindings::dev_pm_opp,
478+
new_opp: *mut bindings::dev_pm_opp,
479+
regulators: *mut *mut bindings::regulator,
480+
count: kernel::ffi::c_uint,
481+
) -> kernel::ffi::c_int {
482+
from_result(|| {
483+
// SAFETY: 'dev' is guaranteed by the C code to be valid.
484+
let dev = unsafe { Device::get_device(dev) };
485+
T::config_regulators(
486+
&dev,
487+
// SAFETY: 'old_opp' is guaranteed by the C code to be valid.
488+
unsafe { OPP::from_raw_opp(old_opp)? },
489+
// SAFETY: 'new_opp' is guaranteed by the C code to be valid.
490+
unsafe { OPP::from_raw_opp(new_opp)? },
491+
regulators,
492+
count,
493+
)
494+
.map(|()| 0)
495+
})
496+
}
497+
}
498+
208499
/// A reference-counted OPP table.
209500
///
210501
/// Rust abstraction for the C `struct opp_table`.

0 commit comments

Comments
 (0)