// SPDX-License-Identifier: GPL-2.0 //! Clock abstractions. //! //! C header: [`include/linux/clk.h`](srctree/include/linux/clk.h) //! //! Reference: 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 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 { 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 { 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::*;