Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/rust/kernel/clk.rs
29266 views
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
9
use 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)]
30
pub struct Hertz(pub c_ulong);
31
32
impl 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
pub const fn from_khz(khz: c_ulong) -> Self {
39
Self(khz * Self::KHZ_TO_HZ)
40
}
41
42
/// Create a new instance from megahertz (MHz)
43
pub const fn from_mhz(mhz: c_ulong) -> Self {
44
Self(mhz * Self::MHZ_TO_HZ)
45
}
46
47
/// Create a new instance from gigahertz (GHz)
48
pub const fn from_ghz(ghz: c_ulong) -> Self {
49
Self(ghz * Self::GHZ_TO_HZ)
50
}
51
52
/// Get the frequency in hertz
53
pub const fn as_hz(&self) -> c_ulong {
54
self.0
55
}
56
57
/// Get the frequency in kilohertz
58
pub const fn as_khz(&self) -> c_ulong {
59
self.0 / Self::KHZ_TO_HZ
60
}
61
62
/// Get the frequency in megahertz
63
pub const fn as_mhz(&self) -> c_ulong {
64
self.0 / Self::MHZ_TO_HZ
65
}
66
67
/// Get the frequency in gigahertz
68
pub const fn as_ghz(&self) -> c_ulong {
69
self.0 / Self::GHZ_TO_HZ
70
}
71
}
72
73
impl From<Hertz> for c_ulong {
74
fn from(freq: Hertz) -> Self {
75
freq.0
76
}
77
}
78
79
#[cfg(CONFIG_COMMON_CLK)]
80
mod common_clk {
81
use super::Hertz;
82
use crate::{
83
device::Device,
84
error::{from_err_ptr, to_result, Result},
85
prelude::*,
86
};
87
88
use core::{ops::Deref, ptr};
89
90
/// A reference-counted clock.
91
///
92
/// Rust abstraction for the C [`struct clk`].
93
///
94
/// # Invariants
95
///
96
/// A [`Clk`] instance holds either a pointer to a valid [`struct clk`] created by the C
97
/// portion of the kernel or a NULL pointer.
98
///
99
/// Instances of this type are reference-counted. Calling [`Clk::get`] ensures that the
100
/// allocation remains valid for the lifetime of the [`Clk`].
101
///
102
/// # Examples
103
///
104
/// The following example demonstrates how to obtain and configure a clock for a device.
105
///
106
/// ```
107
/// use kernel::c_str;
108
/// use kernel::clk::{Clk, Hertz};
109
/// use kernel::device::Device;
110
/// use kernel::error::Result;
111
///
112
/// fn configure_clk(dev: &Device) -> Result {
113
/// let clk = Clk::get(dev, Some(c_str!("apb_clk")))?;
114
///
115
/// clk.prepare_enable()?;
116
///
117
/// let expected_rate = Hertz::from_ghz(1);
118
///
119
/// if clk.rate() != expected_rate {
120
/// clk.set_rate(expected_rate)?;
121
/// }
122
///
123
/// clk.disable_unprepare();
124
/// Ok(())
125
/// }
126
/// ```
127
///
128
/// [`struct clk`]: https://docs.kernel.org/driver-api/clk.html
129
#[repr(transparent)]
130
pub struct Clk(*mut bindings::clk);
131
132
impl Clk {
133
/// Gets [`Clk`] corresponding to a [`Device`] and a connection id.
134
///
135
/// Equivalent to the kernel's [`clk_get`] API.
136
///
137
/// [`clk_get`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_get
138
pub fn get(dev: &Device, name: Option<&CStr>) -> Result<Self> {
139
let con_id = name.map_or(ptr::null(), |n| n.as_ptr());
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 = name.map_or(ptr::null(), |n| n.as_ptr());
308
309
// SAFETY: It is safe to call [`clk_get_optional`] for a valid device pointer.
310
//
311
// INVARIANT: The reference-count is decremented when [`OptionalClk`] goes out of
312
// scope.
313
Ok(Self(Clk(from_err_ptr(unsafe {
314
bindings::clk_get_optional(dev.as_raw(), con_id)
315
})?)))
316
}
317
}
318
319
// Make [`OptionalClk`] behave like [`Clk`].
320
impl Deref for OptionalClk {
321
type Target = Clk;
322
323
fn deref(&self) -> &Clk {
324
&self.0
325
}
326
}
327
}
328
329
#[cfg(CONFIG_COMMON_CLK)]
330
pub use common_clk::*;
331
332