From 9e6fec690a59f011e1664601369cf31f54f40c3d Mon Sep 17 00:00:00 2001 From: Maurice Hieronymus Date: Tue, 26 May 2026 19:04:57 +0200 Subject: [PATCH 1/2] rust: clk: Add ExclusiveClk wrapper for clk_rate_exclusive_get Add Rust bindings for clk_rate_exclusive_get() and clk_rate_exclusive_put(). Clk::rate_exclusive_get() consumes the Clk and returns an ExclusiveClk; the matching put is issued from its Drop impl. ExclusiveClk derefs to Clk so existing rate / prepare / enable APIs remain available on the locked handle. Signed-off-by: Maurice Hieronymus Signed-off-by: Linux RISC-V bot --- rust/kernel/clk.rs | 65 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/rust/kernel/clk.rs b/rust/kernel/clk.rs index 7abbd0767d8cff..8cda40cb01e400 100644 --- a/rust/kernel/clk.rs +++ b/rust/kernel/clk.rs @@ -249,6 +249,23 @@ mod common_clk { // [`clk_set_rate`]. to_result(unsafe { bindings::clk_set_rate(self.as_raw(), rate.as_hz()) }) } + + /// Acquire exclusive control over the clock's rate. + /// + /// Consumes the [`Clk`] and returns an [`ExclusiveClk`] that releases the exclusivity + /// when dropped. While held, no other consumer may change the clock's rate. + /// + /// Equivalent to the kernel's [`clk_rate_exclusive_get`] API. Must not be called from + /// atomic context. + /// + /// [`clk_rate_exclusive_get`]: + /// https://docs.kernel.org/core-api/kernel-api.html#c.clk_rate_exclusive_get + pub fn rate_exclusive_get(self) -> Result { + // SAFETY: By the type invariants, self.as_raw() is a valid argument for + // [`clk_rate_exclusive_get`]. + to_result(unsafe { bindings::clk_rate_exclusive_get(self.as_raw()) })?; + Ok(ExclusiveClk(self)) + } } impl Drop for Clk { @@ -329,6 +346,54 @@ mod common_clk { &self.0 } } + + /// A [`Clk`] with exclusive control over its rate. + /// + /// While an [`ExclusiveClk`] exists, no other consumer of the same clock may change its rate. + /// Obtained by calling [`Clk::rate_exclusive_get`]; the exclusivity is released automatically + /// when the value is dropped, after which the inner [`Clk`] is dropped as usual. + /// + /// # Invariants + /// + /// An [`ExclusiveClk`] instance owns a [`Clk`] for which `clk_rate_exclusive_get` has been + /// called and the matching `clk_rate_exclusive_put` has not yet been called. + /// + /// # Examples + /// + /// ``` + /// use kernel::clk::{Clk, ExclusiveClk}; + /// use kernel::device::Device; + /// use kernel::error::Result; + /// + /// fn lock_rate(dev: &Device) -> Result { + /// let clk = Clk::get(dev, None)?; + /// clk.prepare_enable()?; + /// clk.rate_exclusive_get() + /// } + /// ``` + /// + /// [`struct clk`]: https://docs.kernel.org/driver-api/clk.html + pub struct ExclusiveClk(Clk); + + // Make [`ExclusiveClk`] behave like [`Clk`]. + impl Deref for ExclusiveClk { + type Target = Clk; + + fn deref(&self) -> &Clk { + &self.0 + } + } + + impl Drop for ExclusiveClk { + fn drop(&mut self) { + // SAFETY: By the type invariants, self.as_raw() is a valid argument for + // [`clk_rate_exclusive_put`] and balances the [`clk_rate_exclusive_get`] call from + // [`Clk::rate_exclusive_get`]. + unsafe { + bindings::clk_rate_exclusive_put(self.as_raw()); + } + } + } } #[cfg(CONFIG_COMMON_CLK)] From 8c1f0da03fdf5e9bf4583beafd4c87b9bed72493 Mon Sep 17 00:00:00 2001 From: Maurice Hieronymus Date: Tue, 26 May 2026 19:04:58 +0200 Subject: [PATCH 2/2] pwm: th1520: Lock clock rate with clk_rate_exclusive_get The driver derives period and duty cycle from the clock rate read at probe, so a later rate change would silently miscompute waveforms. Switch to the new ExclusiveClk wrapper to hold the rate for the lifetime of the driver data and drop the corresponding TODO. Signed-off-by: Maurice Hieronymus Signed-off-by: Linux RISC-V bot --- drivers/pwm/pwm_th1520.rs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/pwm/pwm_th1520.rs b/drivers/pwm/pwm_th1520.rs index ddd44a5ce4979c..19e8621bc52c1e 100644 --- a/drivers/pwm/pwm_th1520.rs +++ b/drivers/pwm/pwm_th1520.rs @@ -22,7 +22,7 @@ use core::ops::Deref; use kernel::{ - clk::Clk, + clk::{Clk, ExclusiveClk}, device::{Bound, Core, Device}, devres, io::{ @@ -93,7 +93,7 @@ struct Th1520WfHw { struct Th1520PwmDriverData { #[pin] iomem: devres::Devres>, - clk: Clk, + clk: ExclusiveClk, } impl pwm::PwmOps for Th1520PwmDriverData { @@ -328,10 +328,8 @@ impl platform::Driver for Th1520PwmPlatformDriver { let clk = Clk::get(dev, None)?; clk.prepare_enable()?; + let clk = clk.rate_exclusive_get()?; - // TODO: Get exclusive ownership of the clock to prevent rate changes. - // The Rust equivalent of `clk_rate_exclusive_get()` is not yet available. - // This should be updated once it is implemented. let rate_hz = clk.rate().as_hz(); if rate_hz == 0 { dev_err!(dev, "Clock rate is zero\n");