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"); 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)]