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::*;