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