mirror of
https://github.com/torvalds/linux.git
synced 2025-08-16 06:31:34 +02:00

Use a consistent `# Examples` heading in rustdoc across the codebase. Some modules previously used `## Examples` (even when they should be available as top-level headers), while others used `# Example`, which deviates from the preferred `# Examples` style. Suggested-by: Miguel Ojeda <ojeda@kernel.org> Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org> Acked-by: Benno Lossin <lossin@kernel.org> Link: https://lore.kernel.org/r/ddd5ce0ac20c99a72a4f1e4322d3de3911056922.1749545815.git.viresh.kumar@linaro.org Signed-off-by: Miguel Ojeda <ojeda@kernel.org>
334 lines
10 KiB
Rust
334 lines
10 KiB
Rust
// SPDX-License-Identifier: GPL-2.0
|
|
|
|
//! Clock abstractions.
|
|
//!
|
|
//! C header: [`include/linux/clk.h`](srctree/include/linux/clk.h)
|
|
//!
|
|
//! Reference: <https://docs.kernel.org/driver-api/clk.html>
|
|
|
|
use crate::ffi::c_ulong;
|
|
|
|
/// The frequency unit.
|
|
///
|
|
/// Represents a frequency in hertz, wrapping a [`c_ulong`] value.
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// ```
|
|
/// use kernel::clk::Hertz;
|
|
///
|
|
/// let hz = 1_000_000_000;
|
|
/// let rate = Hertz(hz);
|
|
///
|
|
/// assert_eq!(rate.as_hz(), hz);
|
|
/// assert_eq!(rate, Hertz(hz));
|
|
/// assert_eq!(rate, Hertz::from_khz(hz / 1_000));
|
|
/// assert_eq!(rate, Hertz::from_mhz(hz / 1_000_000));
|
|
/// assert_eq!(rate, Hertz::from_ghz(hz / 1_000_000_000));
|
|
/// ```
|
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
|
pub struct Hertz(pub c_ulong);
|
|
|
|
impl Hertz {
|
|
/// Create a new instance from kilohertz (kHz)
|
|
pub fn from_khz(khz: c_ulong) -> Self {
|
|
Self(khz * 1_000)
|
|
}
|
|
|
|
/// Create a new instance from megahertz (MHz)
|
|
pub fn from_mhz(mhz: c_ulong) -> Self {
|
|
Self(mhz * 1_000_000)
|
|
}
|
|
|
|
/// Create a new instance from gigahertz (GHz)
|
|
pub fn from_ghz(ghz: c_ulong) -> Self {
|
|
Self(ghz * 1_000_000_000)
|
|
}
|
|
|
|
/// Get the frequency in hertz
|
|
pub fn as_hz(&self) -> c_ulong {
|
|
self.0
|
|
}
|
|
|
|
/// Get the frequency in kilohertz
|
|
pub fn as_khz(&self) -> c_ulong {
|
|
self.0 / 1_000
|
|
}
|
|
|
|
/// Get the frequency in megahertz
|
|
pub fn as_mhz(&self) -> c_ulong {
|
|
self.0 / 1_000_000
|
|
}
|
|
|
|
/// Get the frequency in gigahertz
|
|
pub fn as_ghz(&self) -> c_ulong {
|
|
self.0 / 1_000_000_000
|
|
}
|
|
}
|
|
|
|
impl From<Hertz> for c_ulong {
|
|
fn from(freq: Hertz) -> Self {
|
|
freq.0
|
|
}
|
|
}
|
|
|
|
#[cfg(CONFIG_COMMON_CLK)]
|
|
mod common_clk {
|
|
use super::Hertz;
|
|
use crate::{
|
|
device::Device,
|
|
error::{from_err_ptr, to_result, Result},
|
|
prelude::*,
|
|
};
|
|
|
|
use core::{ops::Deref, ptr};
|
|
|
|
/// A reference-counted clock.
|
|
///
|
|
/// Rust abstraction for the C [`struct clk`].
|
|
///
|
|
/// # Invariants
|
|
///
|
|
/// A [`Clk`] instance holds either a pointer to a valid [`struct clk`] created by the C
|
|
/// portion of the kernel or a NULL pointer.
|
|
///
|
|
/// Instances of this type are reference-counted. Calling [`Clk::get`] ensures that the
|
|
/// allocation remains valid for the lifetime of the [`Clk`].
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// The following example demonstrates how to obtain and configure a clock for a device.
|
|
///
|
|
/// ```
|
|
/// use kernel::c_str;
|
|
/// use kernel::clk::{Clk, Hertz};
|
|
/// use kernel::device::Device;
|
|
/// use kernel::error::Result;
|
|
///
|
|
/// fn configure_clk(dev: &Device) -> Result {
|
|
/// let clk = Clk::get(dev, Some(c_str!("apb_clk")))?;
|
|
///
|
|
/// clk.prepare_enable()?;
|
|
///
|
|
/// let expected_rate = Hertz::from_ghz(1);
|
|
///
|
|
/// if clk.rate() != expected_rate {
|
|
/// clk.set_rate(expected_rate)?;
|
|
/// }
|
|
///
|
|
/// clk.disable_unprepare();
|
|
/// Ok(())
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// [`struct clk`]: https://docs.kernel.org/driver-api/clk.html
|
|
#[repr(transparent)]
|
|
pub struct Clk(*mut bindings::clk);
|
|
|
|
impl Clk {
|
|
/// Gets [`Clk`] corresponding to a [`Device`] and a connection id.
|
|
///
|
|
/// Equivalent to the kernel's [`clk_get`] API.
|
|
///
|
|
/// [`clk_get`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_get
|
|
pub fn get(dev: &Device, name: Option<&CStr>) -> Result<Self> {
|
|
let con_id = if let Some(name) = name {
|
|
name.as_ptr()
|
|
} else {
|
|
ptr::null()
|
|
};
|
|
|
|
// SAFETY: It is safe to call [`clk_get`] for a valid device pointer.
|
|
//
|
|
// INVARIANT: The reference-count is decremented when [`Clk`] goes out of scope.
|
|
Ok(Self(from_err_ptr(unsafe {
|
|
bindings::clk_get(dev.as_raw(), con_id)
|
|
})?))
|
|
}
|
|
|
|
/// Obtain the raw [`struct clk`] pointer.
|
|
#[inline]
|
|
pub fn as_raw(&self) -> *mut bindings::clk {
|
|
self.0
|
|
}
|
|
|
|
/// Enable the clock.
|
|
///
|
|
/// Equivalent to the kernel's [`clk_enable`] API.
|
|
///
|
|
/// [`clk_enable`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_enable
|
|
#[inline]
|
|
pub fn enable(&self) -> Result {
|
|
// SAFETY: By the type invariants, self.as_raw() is a valid argument for
|
|
// [`clk_enable`].
|
|
to_result(unsafe { bindings::clk_enable(self.as_raw()) })
|
|
}
|
|
|
|
/// Disable the clock.
|
|
///
|
|
/// Equivalent to the kernel's [`clk_disable`] API.
|
|
///
|
|
/// [`clk_disable`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_disable
|
|
#[inline]
|
|
pub fn disable(&self) {
|
|
// SAFETY: By the type invariants, self.as_raw() is a valid argument for
|
|
// [`clk_disable`].
|
|
unsafe { bindings::clk_disable(self.as_raw()) };
|
|
}
|
|
|
|
/// Prepare the clock.
|
|
///
|
|
/// Equivalent to the kernel's [`clk_prepare`] API.
|
|
///
|
|
/// [`clk_prepare`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_prepare
|
|
#[inline]
|
|
pub fn prepare(&self) -> Result {
|
|
// SAFETY: By the type invariants, self.as_raw() is a valid argument for
|
|
// [`clk_prepare`].
|
|
to_result(unsafe { bindings::clk_prepare(self.as_raw()) })
|
|
}
|
|
|
|
/// Unprepare the clock.
|
|
///
|
|
/// Equivalent to the kernel's [`clk_unprepare`] API.
|
|
///
|
|
/// [`clk_unprepare`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_unprepare
|
|
#[inline]
|
|
pub fn unprepare(&self) {
|
|
// SAFETY: By the type invariants, self.as_raw() is a valid argument for
|
|
// [`clk_unprepare`].
|
|
unsafe { bindings::clk_unprepare(self.as_raw()) };
|
|
}
|
|
|
|
/// Prepare and enable the clock.
|
|
///
|
|
/// Equivalent to calling [`Clk::prepare`] followed by [`Clk::enable`].
|
|
#[inline]
|
|
pub fn prepare_enable(&self) -> Result {
|
|
// SAFETY: By the type invariants, self.as_raw() is a valid argument for
|
|
// [`clk_prepare_enable`].
|
|
to_result(unsafe { bindings::clk_prepare_enable(self.as_raw()) })
|
|
}
|
|
|
|
/// Disable and unprepare the clock.
|
|
///
|
|
/// Equivalent to calling [`Clk::disable`] followed by [`Clk::unprepare`].
|
|
#[inline]
|
|
pub fn disable_unprepare(&self) {
|
|
// SAFETY: By the type invariants, self.as_raw() is a valid argument for
|
|
// [`clk_disable_unprepare`].
|
|
unsafe { bindings::clk_disable_unprepare(self.as_raw()) };
|
|
}
|
|
|
|
/// Get clock's rate.
|
|
///
|
|
/// Equivalent to the kernel's [`clk_get_rate`] API.
|
|
///
|
|
/// [`clk_get_rate`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_get_rate
|
|
#[inline]
|
|
pub fn rate(&self) -> Hertz {
|
|
// SAFETY: By the type invariants, self.as_raw() is a valid argument for
|
|
// [`clk_get_rate`].
|
|
Hertz(unsafe { bindings::clk_get_rate(self.as_raw()) })
|
|
}
|
|
|
|
/// Set clock's rate.
|
|
///
|
|
/// Equivalent to the kernel's [`clk_set_rate`] API.
|
|
///
|
|
/// [`clk_set_rate`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_set_rate
|
|
#[inline]
|
|
pub fn set_rate(&self, rate: Hertz) -> Result {
|
|
// SAFETY: By the type invariants, self.as_raw() is a valid argument for
|
|
// [`clk_set_rate`].
|
|
to_result(unsafe { bindings::clk_set_rate(self.as_raw(), rate.as_hz()) })
|
|
}
|
|
}
|
|
|
|
impl Drop for Clk {
|
|
fn drop(&mut self) {
|
|
// SAFETY: By the type invariants, self.as_raw() is a valid argument for [`clk_put`].
|
|
unsafe { bindings::clk_put(self.as_raw()) };
|
|
}
|
|
}
|
|
|
|
/// A reference-counted optional clock.
|
|
///
|
|
/// A lightweight wrapper around an optional [`Clk`]. An [`OptionalClk`] represents a [`Clk`]
|
|
/// that a driver can function without but may improve performance or enable additional
|
|
/// features when available.
|
|
///
|
|
/// # Invariants
|
|
///
|
|
/// An [`OptionalClk`] instance encapsulates a [`Clk`] with either a valid [`struct clk`] or
|
|
/// `NULL` pointer.
|
|
///
|
|
/// Instances of this type are reference-counted. Calling [`OptionalClk::get`] ensures that the
|
|
/// allocation remains valid for the lifetime of the [`OptionalClk`].
|
|
///
|
|
/// # Examples
|
|
///
|
|
/// The following example demonstrates how to obtain and configure an optional clock for a
|
|
/// device. The code functions correctly whether or not the clock is available.
|
|
///
|
|
/// ```
|
|
/// use kernel::c_str;
|
|
/// use kernel::clk::{OptionalClk, Hertz};
|
|
/// use kernel::device::Device;
|
|
/// use kernel::error::Result;
|
|
///
|
|
/// fn configure_clk(dev: &Device) -> Result {
|
|
/// let clk = OptionalClk::get(dev, Some(c_str!("apb_clk")))?;
|
|
///
|
|
/// clk.prepare_enable()?;
|
|
///
|
|
/// let expected_rate = Hertz::from_ghz(1);
|
|
///
|
|
/// if clk.rate() != expected_rate {
|
|
/// clk.set_rate(expected_rate)?;
|
|
/// }
|
|
///
|
|
/// clk.disable_unprepare();
|
|
/// Ok(())
|
|
/// }
|
|
/// ```
|
|
///
|
|
/// [`struct clk`]: https://docs.kernel.org/driver-api/clk.html
|
|
pub struct OptionalClk(Clk);
|
|
|
|
impl OptionalClk {
|
|
/// Gets [`OptionalClk`] corresponding to a [`Device`] and a connection id.
|
|
///
|
|
/// Equivalent to the kernel's [`clk_get_optional`] API.
|
|
///
|
|
/// [`clk_get_optional`]:
|
|
/// https://docs.kernel.org/core-api/kernel-api.html#c.clk_get_optional
|
|
pub fn get(dev: &Device, name: Option<&CStr>) -> Result<Self> {
|
|
let con_id = if let Some(name) = name {
|
|
name.as_ptr()
|
|
} else {
|
|
ptr::null()
|
|
};
|
|
|
|
// SAFETY: It is safe to call [`clk_get_optional`] for a valid device pointer.
|
|
//
|
|
// INVARIANT: The reference-count is decremented when [`OptionalClk`] goes out of
|
|
// scope.
|
|
Ok(Self(Clk(from_err_ptr(unsafe {
|
|
bindings::clk_get_optional(dev.as_raw(), con_id)
|
|
})?)))
|
|
}
|
|
}
|
|
|
|
// Make [`OptionalClk`] behave like [`Clk`].
|
|
impl Deref for OptionalClk {
|
|
type Target = Clk;
|
|
|
|
fn deref(&self) -> &Clk {
|
|
&self.0
|
|
}
|
|
}
|
|
}
|
|
|
|
#[cfg(CONFIG_COMMON_CLK)]
|
|
pub use common_clk::*;
|