| // 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 { | |
| const KHZ_TO_HZ: c_ulong = 1_000; | |
| const MHZ_TO_HZ: c_ulong = 1_000_000; | |
| const GHZ_TO_HZ: c_ulong = 1_000_000_000; | |
| /// Create a new instance from kilohertz (kHz) | |
| pub const fn from_khz(khz: c_ulong) -> Self { | |
| Self(khz * Self::KHZ_TO_HZ) | |
| } | |
| /// Create a new instance from megahertz (MHz) | |
| pub const fn from_mhz(mhz: c_ulong) -> Self { | |
| Self(mhz * Self::MHZ_TO_HZ) | |
| } | |
| /// Create a new instance from gigahertz (GHz) | |
| pub const fn from_ghz(ghz: c_ulong) -> Self { | |
| Self(ghz * Self::GHZ_TO_HZ) | |
| } | |
| /// Get the frequency in hertz | |
| pub const fn as_hz(&self) -> c_ulong { | |
| self.0 | |
| } | |
| /// Get the frequency in kilohertz | |
| pub const fn as_khz(&self) -> c_ulong { | |
| self.0 / Self::KHZ_TO_HZ | |
| } | |
| /// Get the frequency in megahertz | |
| pub const fn as_mhz(&self) -> c_ulong { | |
| self.0 / Self::MHZ_TO_HZ | |
| } | |
| /// Get the frequency in gigahertz | |
| pub const fn as_ghz(&self) -> c_ulong { | |
| self.0 / Self::GHZ_TO_HZ | |
| } | |
| } | |
| 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::clk::{Clk, Hertz}; | |
| /// use kernel::device::Device; | |
| /// use kernel::error::Result; | |
| /// | |
| /// fn configure_clk(dev: &Device) -> Result { | |
| /// let clk = Clk::get(dev, Some(c"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); | |
| // SAFETY: It is safe to call `clk_put` on another thread than where `clk_get` was called. | |
| unsafe impl Send for Clk {} | |
| // SAFETY: It is safe to call any combination of the `&self` methods in parallel, as the | |
| // methods are synchronized internally. | |
| unsafe impl Sync for 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 = name.map_or(ptr::null(), |n| n.as_char_ptr()); | |
| // 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::clk::{OptionalClk, Hertz}; | |
| /// use kernel::device::Device; | |
| /// use kernel::error::Result; | |
| /// | |
| /// fn configure_clk(dev: &Device) -> Result { | |
| /// let clk = OptionalClk::get(dev, Some(c"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 = name.map_or(ptr::null(), |n| n.as_char_ptr()); | |
| // 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::*; |