[PATCH v6 6/7] rust: clk: add `ExclusiveClk`
Daniel Almeida
daniel.almeida at collabora.com
Tue Oct 13 10:07:45 PDT 2026
Some drivers derive their configuration from a clock rate and cannot
tolerate the rate changing underneath them. In C, they claim rate
exclusivity with clk_rate_exclusive_get(), after which the clock
framework refuses rate changes from other consumers until
clk_rate_exclusive_put() is called.
Add ExclusiveClk<T>, which takes a Clk<T> in any state and holds rate
exclusivity until it is dropped. ExclusiveClk::new() calls
clk_rate_exclusive_get(), and dropping the ExclusiveClk or calling
ExclusiveClk::into_inner() calls clk_rate_exclusive_put().
ExclusiveClk dereferences to the Clk<T>, so the rate can still be read
and changed through it. Since a Clk cannot be cloned, the ExclusiveClk
is the only handle to the consumer that holds the exclusivity.
pwm-th1520 is the first user, in the next patch.
Co-developed-by: Maurice Hieronymus <mhi at mailbox.org>
Signed-off-by: Maurice Hieronymus <mhi at mailbox.org>
Signed-off-by: Daniel Almeida <daniel.almeida at collabora.com>
---
rust/kernel/clk.rs | 100 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 100 insertions(+)
diff --git a/rust/kernel/clk.rs b/rust/kernel/clk.rs
index e2587c613182..8d7ba0fae459 100644
--- a/rust/kernel/clk.rs
+++ b/rust/kernel/clk.rs
@@ -846,6 +846,106 @@ fn drop(&mut self) {
unsafe { bindings::clk_disable_unprepare(self.clk.as_raw()) };
}
}
+
+ /// A [`Clk`] with exclusive control over its rate.
+ ///
+ /// While an [`ExclusiveClk`] exists, no other consumer of the same clock
+ /// may change its rate. The rate can still be changed through the
+ /// [`ExclusiveClk`] itself, which dereferences to the [`Clk`] in its
+ /// current state.
+ ///
+ /// The [`Clk`] is moved into the [`ExclusiveClk`], and a [`Clk`] cannot be
+ /// cloned, so only the [`ExclusiveClk`] refers to the consumer handle that
+ /// holds the exclusivity.
+ ///
+ /// # Invariants
+ ///
+ /// `clk_rate_exclusive_get()` has been called on the wrapped [`Clk`], and
+ /// the matching `clk_rate_exclusive_put()` has not been called yet.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::clk::{
+ /// Clk,
+ /// Enabled,
+ /// ExclusiveClk,
+ /// Hertz,
+ /// };
+ /// use kernel::device::{
+ /// Bound,
+ /// Device,
+ /// };
+ ///
+ /// fn lock_rate(dev: &Device<Bound>) -> Result<ExclusiveClk<Enabled>> {
+ /// let clk = Clk::<Enabled>::get(dev, None)?;
+ /// clk.set_rate(Hertz::from_mhz(24))?;
+ ///
+ /// ExclusiveClk::new(clk)
+ /// }
+ /// ```
+ pub struct ExclusiveClk<T: ClkState>(Clk<T>);
+
+ impl<T: ClkState> ExclusiveClk<T> {
+ /// Takes exclusive control over the rate of `clk`.
+ ///
+ /// No other consumer of the clock may change its rate until the
+ /// returned [`ExclusiveClk`] is dropped or [`ExclusiveClk::into_inner`]
+ /// is called. This may sleep.
+ ///
+ /// Equivalent to the kernel's [`clk_rate_exclusive_get`] API.
+ ///
+ /// [`clk_rate_exclusive_get`]:
+ /// https://docs.kernel.org/core-api/kernel-api.html#c.clk_rate_exclusive_get
+ #[inline]
+ pub fn new(clk: Clk<T>) -> Result<Self> {
+ // SAFETY: By the type invariants of `Clk`, `clk.as_raw()` is a valid
+ // argument for `clk_rate_exclusive_get()`.
+ to_result(unsafe { bindings::clk_rate_exclusive_get(clk.as_raw()) })?;
+
+ // INVARIANT: `clk_rate_exclusive_get()` succeeded, and the matching
+ // `clk_rate_exclusive_put()` is called when the `ExclusiveClk` is
+ // dropped or consumed by `into_inner()`.
+ Ok(Self(clk))
+ }
+
+ /// Releases the exclusive control over the rate and returns the [`Clk`].
+ ///
+ /// Equivalent to the kernel's [`clk_rate_exclusive_put`] API.
+ ///
+ /// [`clk_rate_exclusive_put`]:
+ /// https://docs.kernel.org/core-api/kernel-api.html#c.clk_rate_exclusive_put
+ #[inline]
+ pub fn into_inner(self) -> Clk<T> {
+ let this = ManuallyDrop::new(self);
+
+ // SAFETY: By the type invariants, `clk_rate_exclusive_get()` was
+ // called on `this.0` and not yet balanced, so this balances it.
+ unsafe { bindings::clk_rate_exclusive_put(this.0.as_raw()) };
+
+ // SAFETY: `this` is wrapped in `ManuallyDrop`, so the `Clk` is moved
+ // out exactly once and the `Drop` of `ExclusiveClk` does not run.
+ unsafe { ptr::read(&this.0) }
+ }
+ }
+
+ impl<T: ClkState> Deref for ExclusiveClk<T> {
+ type Target = Clk<T>;
+
+ #[inline]
+ fn deref(&self) -> &Clk<T> {
+ &self.0
+ }
+ }
+
+ impl<T: ClkState> Drop for ExclusiveClk<T> {
+ #[inline]
+ fn drop(&mut self) {
+ // SAFETY: By the type invariants, `clk_rate_exclusive_get()` was
+ // called on `self.0` and not yet balanced, so this balances it.
+ unsafe { bindings::clk_rate_exclusive_put(self.0.as_raw()) };
+ }
+ }
}
#[cfg(CONFIG_COMMON_CLK)]
--
2.56.0
More information about the linux-riscv
mailing list