Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions drivers/pwm/pwm_th1520.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

use core::ops::Deref;
use kernel::{
clk::Clk,
clk::{Clk, ExclusiveClk},
device::{Bound, Core, Device},
devres,
io::{
Expand Down Expand Up @@ -93,7 +93,7 @@ struct Th1520WfHw {
struct Th1520PwmDriverData {
#[pin]
iomem: devres::Devres<IoMem<TH1520_PWM_REG_SIZE>>,
clk: Clk,
clk: ExclusiveClk,
}

impl pwm::PwmOps for Th1520PwmDriverData {
Expand Down Expand Up @@ -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");
Expand Down
65 changes: 65 additions & 0 deletions rust/kernel/clk.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<ExclusiveClk> {
// 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 {
Expand Down Expand Up @@ -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<ExclusiveClk> {
/// 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)]
Expand Down