Skip to main content

kernel/
clk.rs

1// SPDX-License-Identifier: GPL-2.0
2
3//! Clock abstractions.
4//!
5//! C header: [`include/linux/clk.h`](srctree/include/linux/clk.h)
6//!
7//! Reference: <https://docs.kernel.org/driver-api/clk.html>
8
9use crate::ffi::c_ulong;
10
11/// The frequency unit.
12///
13/// Represents a frequency in hertz, wrapping a [`c_ulong`] value.
14///
15/// # Examples
16///
17/// ```
18/// use kernel::clk::Hertz;
19///
20/// let hz = 1_000_000_000;
21/// let rate = Hertz(hz);
22///
23/// assert_eq!(rate.as_hz(), hz);
24/// assert_eq!(rate, Hertz(hz));
25/// assert_eq!(rate, Hertz::from_khz(hz / 1_000));
26/// assert_eq!(rate, Hertz::from_mhz(hz / 1_000_000));
27/// assert_eq!(rate, Hertz::from_ghz(hz / 1_000_000_000));
28/// ```
29#[derive(Copy, Clone, PartialEq, Eq, Debug)]
30pub struct Hertz(pub c_ulong);
31
32impl Hertz {
33    const KHZ_TO_HZ: c_ulong = 1_000;
34    const MHZ_TO_HZ: c_ulong = 1_000_000;
35    const GHZ_TO_HZ: c_ulong = 1_000_000_000;
36
37    /// Create a new instance from kilohertz (kHz)
38    ///
39    /// # Panics
40    ///
41    /// Panics if `CONFIG_RUST_OVERFLOW_CHECKS` is enabled and `khz` is greater
42    /// than `c_ulong::MAX / 1_000`.
43    pub const fn from_khz(khz: c_ulong) -> Self {
44        Self(khz * Self::KHZ_TO_HZ)
45    }
46
47    /// Create a new instance from megahertz (MHz)
48    ///
49    /// # Panics
50    ///
51    /// Panics if `CONFIG_RUST_OVERFLOW_CHECKS` is enabled and `mhz` is greater
52    /// than `c_ulong::MAX / 1_000_000`.
53    pub const fn from_mhz(mhz: c_ulong) -> Self {
54        Self(mhz * Self::MHZ_TO_HZ)
55    }
56
57    /// Create a new instance from gigahertz (GHz)
58    ///
59    /// # Panics
60    ///
61    /// Panics if `CONFIG_RUST_OVERFLOW_CHECKS` is enabled and `ghz` is greater
62    /// than `c_ulong::MAX / 1_000_000_000`.
63    pub const fn from_ghz(ghz: c_ulong) -> Self {
64        Self(ghz * Self::GHZ_TO_HZ)
65    }
66
67    /// Get the frequency in hertz
68    pub const fn as_hz(&self) -> c_ulong {
69        self.0
70    }
71
72    /// Get the frequency in kilohertz
73    pub const fn as_khz(&self) -> c_ulong {
74        self.0 / Self::KHZ_TO_HZ
75    }
76
77    /// Get the frequency in megahertz
78    pub const fn as_mhz(&self) -> c_ulong {
79        self.0 / Self::MHZ_TO_HZ
80    }
81
82    /// Get the frequency in gigahertz
83    pub const fn as_ghz(&self) -> c_ulong {
84        self.0 / Self::GHZ_TO_HZ
85    }
86}
87
88impl From<Hertz> for c_ulong {
89    fn from(freq: Hertz) -> Self {
90        freq.0
91    }
92}
93
94#[cfg(CONFIG_COMMON_CLK)]
95mod common_clk {
96    use super::Hertz;
97    use crate::{
98        device::Device,
99        error::{from_err_ptr, to_result, Result},
100        prelude::*,
101    };
102
103    use core::{ops::Deref, ptr};
104
105    /// A reference-counted clock.
106    ///
107    /// Rust abstraction for the C [`struct clk`].
108    ///
109    /// # Invariants
110    ///
111    /// A [`Clk`] instance holds either a pointer to a valid [`struct clk`] created by the C
112    /// portion of the kernel or a `NULL` pointer.
113    ///
114    /// Instances of this type are reference-counted. Calling [`Clk::get`] ensures that the
115    /// allocation remains valid for the lifetime of the [`Clk`].
116    ///
117    /// # Examples
118    ///
119    /// The following example demonstrates how to obtain and configure a clock for a device.
120    ///
121    /// ```
122    /// use kernel::clk::{Clk, Hertz};
123    /// use kernel::device::Device;
124    /// use kernel::error::Result;
125    ///
126    /// fn configure_clk(dev: &Device) -> Result {
127    ///     let clk = Clk::get(dev, Some(c"apb_clk"))?;
128    ///
129    ///     clk.prepare_enable()?;
130    ///
131    ///     let expected_rate = Hertz::from_ghz(1);
132    ///
133    ///     if clk.rate() != expected_rate {
134    ///         clk.set_rate(expected_rate)?;
135    ///     }
136    ///
137    ///     clk.disable_unprepare();
138    ///     Ok(())
139    /// }
140    /// ```
141    ///
142    /// [`struct clk`]: https://docs.kernel.org/driver-api/clk.html
143    #[repr(transparent)]
144    pub struct Clk(*mut bindings::clk);
145
146    // SAFETY: It is safe to call `clk_put` on another thread than where `clk_get` was called.
147    unsafe impl Send for Clk {}
148
149    // SAFETY: It is safe to call any combination of the `&self` methods in parallel, as the
150    // methods are synchronized internally.
151    unsafe impl Sync for Clk {}
152
153    impl Clk {
154        /// Gets [`Clk`] corresponding to a [`Device`] and a connection id.
155        ///
156        /// Equivalent to the kernel's [`clk_get`] API.
157        ///
158        /// [`clk_get`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_get
159        pub fn get(dev: &Device, name: Option<&CStr>) -> Result<Self> {
160            let con_id = name.map_or(ptr::null(), |n| n.as_char_ptr());
161
162            // SAFETY: It is safe to call [`clk_get`] for a valid device pointer.
163            //
164            // INVARIANT: The reference-count is decremented when [`Clk`] goes out of scope.
165            Ok(Self(from_err_ptr(unsafe {
166                bindings::clk_get(dev.as_raw(), con_id)
167            })?))
168        }
169
170        /// Obtain the raw [`struct clk`] pointer.
171        #[inline]
172        pub fn as_raw(&self) -> *mut bindings::clk {
173            self.0
174        }
175
176        /// Enable the clock.
177        ///
178        /// Equivalent to the kernel's [`clk_enable`] API.
179        ///
180        /// [`clk_enable`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_enable
181        #[inline]
182        pub fn enable(&self) -> Result {
183            // SAFETY: By the type invariants, self.as_raw() is a valid argument for
184            // [`clk_enable`].
185            to_result(unsafe { bindings::clk_enable(self.as_raw()) })
186        }
187
188        /// Disable the clock.
189        ///
190        /// Equivalent to the kernel's [`clk_disable`] API.
191        ///
192        /// [`clk_disable`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_disable
193        #[inline]
194        pub fn disable(&self) {
195            // SAFETY: By the type invariants, self.as_raw() is a valid argument for
196            // [`clk_disable`].
197            unsafe { bindings::clk_disable(self.as_raw()) };
198        }
199
200        /// Prepare the clock.
201        ///
202        /// Equivalent to the kernel's [`clk_prepare`] API.
203        ///
204        /// [`clk_prepare`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_prepare
205        #[inline]
206        pub fn prepare(&self) -> Result {
207            // SAFETY: By the type invariants, self.as_raw() is a valid argument for
208            // [`clk_prepare`].
209            to_result(unsafe { bindings::clk_prepare(self.as_raw()) })
210        }
211
212        /// Unprepare the clock.
213        ///
214        /// Equivalent to the kernel's [`clk_unprepare`] API.
215        ///
216        /// [`clk_unprepare`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_unprepare
217        #[inline]
218        pub fn unprepare(&self) {
219            // SAFETY: By the type invariants, self.as_raw() is a valid argument for
220            // [`clk_unprepare`].
221            unsafe { bindings::clk_unprepare(self.as_raw()) };
222        }
223
224        /// Prepare and enable the clock.
225        ///
226        /// Equivalent to calling [`Clk::prepare`] followed by [`Clk::enable`].
227        #[inline]
228        pub fn prepare_enable(&self) -> Result {
229            // SAFETY: By the type invariants, self.as_raw() is a valid argument for
230            // [`clk_prepare_enable`].
231            to_result(unsafe { bindings::clk_prepare_enable(self.as_raw()) })
232        }
233
234        /// Disable and unprepare the clock.
235        ///
236        /// Equivalent to calling [`Clk::disable`] followed by [`Clk::unprepare`].
237        #[inline]
238        pub fn disable_unprepare(&self) {
239            // SAFETY: By the type invariants, self.as_raw() is a valid argument for
240            // [`clk_disable_unprepare`].
241            unsafe { bindings::clk_disable_unprepare(self.as_raw()) };
242        }
243
244        /// Get clock's rate.
245        ///
246        /// Equivalent to the kernel's [`clk_get_rate`] API.
247        ///
248        /// [`clk_get_rate`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_get_rate
249        #[inline]
250        pub fn rate(&self) -> Hertz {
251            // SAFETY: By the type invariants, self.as_raw() is a valid argument for
252            // [`clk_get_rate`].
253            Hertz(unsafe { bindings::clk_get_rate(self.as_raw()) })
254        }
255
256        /// Set clock's rate.
257        ///
258        /// Equivalent to the kernel's [`clk_set_rate`] API.
259        ///
260        /// [`clk_set_rate`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_set_rate
261        #[inline]
262        pub fn set_rate(&self, rate: Hertz) -> Result {
263            // SAFETY: By the type invariants, self.as_raw() is a valid argument for
264            // [`clk_set_rate`].
265            to_result(unsafe { bindings::clk_set_rate(self.as_raw(), rate.as_hz()) })
266        }
267    }
268
269    impl Drop for Clk {
270        fn drop(&mut self) {
271            // SAFETY: By the type invariants, self.as_raw() is a valid argument for [`clk_put`].
272            unsafe { bindings::clk_put(self.as_raw()) };
273        }
274    }
275
276    /// A reference-counted optional clock.
277    ///
278    /// A lightweight wrapper around an optional [`Clk`]. An [`OptionalClk`] represents a [`Clk`]
279    /// that a driver can function without but may improve performance or enable additional
280    /// features when available.
281    ///
282    /// # Invariants
283    ///
284    /// An [`OptionalClk`] instance encapsulates a [`Clk`] with either a valid [`struct clk`] or
285    /// `NULL` pointer.
286    ///
287    /// Instances of this type are reference-counted. Calling [`OptionalClk::get`] ensures that the
288    /// allocation remains valid for the lifetime of the [`OptionalClk`].
289    ///
290    /// # Examples
291    ///
292    /// The following example demonstrates how to obtain and configure an optional clock for a
293    /// device. The code functions correctly whether or not the clock is available.
294    ///
295    /// ```
296    /// use kernel::clk::{OptionalClk, Hertz};
297    /// use kernel::device::Device;
298    /// use kernel::error::Result;
299    ///
300    /// fn configure_clk(dev: &Device) -> Result {
301    ///     let clk = OptionalClk::get(dev, Some(c"apb_clk"))?;
302    ///
303    ///     clk.prepare_enable()?;
304    ///
305    ///     let expected_rate = Hertz::from_ghz(1);
306    ///
307    ///     if clk.rate() != expected_rate {
308    ///         clk.set_rate(expected_rate)?;
309    ///     }
310    ///
311    ///     clk.disable_unprepare();
312    ///     Ok(())
313    /// }
314    /// ```
315    ///
316    /// [`struct clk`]: https://docs.kernel.org/driver-api/clk.html
317    pub struct OptionalClk(Clk);
318
319    impl OptionalClk {
320        /// Gets [`OptionalClk`] corresponding to a [`Device`] and a connection id.
321        ///
322        /// Equivalent to the kernel's [`clk_get_optional`] API.
323        ///
324        /// [`clk_get_optional`]:
325        /// https://docs.kernel.org/core-api/kernel-api.html#c.clk_get_optional
326        pub fn get(dev: &Device, name: Option<&CStr>) -> Result<Self> {
327            let con_id = name.map_or(ptr::null(), |n| n.as_char_ptr());
328
329            // SAFETY: It is safe to call [`clk_get_optional`] for a valid device pointer.
330            //
331            // INVARIANT: The reference-count is decremented when [`OptionalClk`] goes out of
332            // scope.
333            Ok(Self(Clk(from_err_ptr(unsafe {
334                bindings::clk_get_optional(dev.as_raw(), con_id)
335            })?)))
336        }
337    }
338
339    // Make [`OptionalClk`] behave like [`Clk`].
340    impl Deref for OptionalClk {
341        type Target = Clk;
342
343        fn deref(&self) -> &Clk {
344            &self.0
345        }
346    }
347}
348
349#[cfg(CONFIG_COMMON_CLK)]
350pub use common_clk::*;