From 632ef868c289eebddfb2d8e60b2c11aad578e5e0 Mon Sep 17 00:00:00 2001 From: Maciej Falkowski Date: Fri, 29 Oct 2021 14:45:26 +0200 Subject: [PATCH] rust: add initial common clock framework bindings This patch adds initial abstractions including: - `Clk` wrapper around `struct clk *`. - Binding of clk_get() method implemented as a method of `device::RawDevice` trait. - `EnableClk` that is an invariant of the `Clk` type that manages usage of the disable_unprepare() function. - Routines get_rate() and prepare_enable() implemented as methods of `Clk` type. Signed-off-by: Maciej Falkowski --- rust/helpers.c | 13 ++++++ rust/kernel/bindings_helper.h | 1 + rust/kernel/clk.rs | 75 +++++++++++++++++++++++++++++++++++ rust/kernel/device.rs | 23 ++++++++++- rust/kernel/lib.rs | 2 + 5 files changed, 113 insertions(+), 1 deletion(-) create mode 100644 rust/kernel/clk.rs diff --git a/rust/helpers.c b/rust/helpers.c index f3e3fb34d3761e..d3a961ec384bd0 100644 --- a/rust/helpers.c +++ b/rust/helpers.c @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -22,6 +23,18 @@ __noreturn void rust_helper_BUG(void) BUG(); } +void rust_helper_clk_disable_unprepare(struct clk *clk) +{ + return clk_disable_unprepare(clk); +} +EXPORT_SYMBOL_GPL(rust_helper_clk_disable_unprepare); + +int rust_helper_clk_prepare_enable(struct clk *clk) +{ + return clk_prepare_enable(clk); +} +EXPORT_SYMBOL_GPL(rust_helper_clk_prepare_enable); + unsigned long rust_helper_copy_from_user(void *to, const void __user *from, unsigned long n) { return copy_from_user(to, from, n); diff --git a/rust/kernel/bindings_helper.h b/rust/kernel/bindings_helper.h index 55c3f25a54eb24..99a7d785ae01fe 100644 --- a/rust/kernel/bindings_helper.h +++ b/rust/kernel/bindings_helper.h @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0 */ #include +#include #include #include #include diff --git a/rust/kernel/clk.rs b/rust/kernel/clk.rs new file mode 100644 index 00000000000000..381f1ff3bd2358 --- /dev/null +++ b/rust/kernel/clk.rs @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Common clock framework. +//! +//! C header: [`include/linux/clk.h`](../../../../include/linux/clk.h) + +use crate::{bindings, error::Result, to_result}; +use core::mem::ManuallyDrop; + +/// Represents `struct clk *`. +/// +/// # Invariants +/// +/// The pointer is valid. +pub struct Clk(*mut bindings::clk); + +impl Clk { + /// Creates new clock structure from a raw pointer. + /// + /// # Safety + /// + /// The pointer must be valid. + pub unsafe fn new(clk: *mut bindings::clk) -> Self { + Self(clk) + } + + /// Returns value of the rate field of `struct clk`. + pub fn get_rate(&self) -> usize { + // SAFETY: the pointer is valid by the type invariant. + unsafe { bindings::clk_get_rate(self.0) as usize } + } + + /// Prepares and enables the underlying hardware clock. + /// + /// This function should not be called in atomic context. + pub fn prepare_enable(self) -> Result { + // SAFETY: the pointer is valid by the type invariant. + to_result(|| unsafe { bindings::clk_prepare_enable(self.0) })?; + Ok(EnabledClk(self)) + } +} + +impl Drop for Clk { + fn drop(&mut self) { + // SAFETY: the pointer is valid by the type invariant. + unsafe { bindings::clk_put(self.0) }; + } +} + +/// A clock variant that is prepared and enabled. +pub struct EnabledClk(Clk); + +impl EnabledClk { + /// Returns value of the rate field of `struct clk`. + pub fn get_rate(&self) -> usize { + self.0.get_rate() + } + + /// Disables and later unprepares the underlying hardware clock prematurely. + /// + /// This function should not be called in atomic context. + pub fn disable_unprepare(self) -> Clk { + let mut clk = ManuallyDrop::new(self); + // SAFETY: the pointer is valid by the type invariant. + unsafe { bindings::clk_disable_unprepare(clk.0 .0) }; + core::mem::replace(&mut clk.0, Clk(core::ptr::null_mut())) + } +} + +impl Drop for EnabledClk { + fn drop(&mut self) { + // SAFETY: the pointer is valid by the type invariant. + unsafe { bindings::clk_disable_unprepare(self.0 .0) }; + } +} diff --git a/rust/kernel/device.rs b/rust/kernel/device.rs index 417ad9d0ce7858..50aae46e042e88 100644 --- a/rust/kernel/device.rs +++ b/rust/kernel/device.rs @@ -4,6 +4,9 @@ //! //! C header: [`include/linux/device.h`](../../../../include/linux/device.h) +#[cfg(CONFIG_COMMON_CLK)] +use crate::{clk::Clk, error::from_kernel_err_ptr}; + use crate::{ bindings, revocable::{Revocable, RevocableGuard}, @@ -43,6 +46,24 @@ pub unsafe trait RawDevice { // by the compiler because of their lifetimes). unsafe { CStr::from_char_ptr(name) } } + + /// Lookups a clock producer consumed by this device. + /// + /// Returns a managed reference to the clock producer. + #[cfg(CONFIG_COMMON_CLK)] + fn clk_get(&self, id: Option<&CStr>) -> Result { + let id_ptr = match id { + Some(cstr) => cstr.as_char_ptr(), + None => core::ptr::null(), + }; + + // SAFETY: id_ptr is optional and may be either a valid pointer + // from the type invariant or NULL otherwise. + let clk_ptr = unsafe { from_kernel_err_ptr(bindings::clk_get(self.raw_device(), id_ptr)) }?; + + // SAFETY: clock is initialized with valid pointer returned from `bindings::clk_get` call. + unsafe { Ok(Clk::new(clk_ptr)) } + } } /// A ref-counted device. @@ -62,7 +83,7 @@ impl Device { /// /// Callers must ensure that `ptr` is valid, non-null, and has a non-zero reference count. pub unsafe fn new(ptr: *mut bindings::device) -> Self { - // SAFETY: By the safety requiments, ptr is valid and its refcounted will be incremented. + // SAFETY: By the safety requirements, ptr is valid and its refcounted will be incremented. unsafe { bindings::get_device(ptr) }; // INVARIANT: The safety requirements satisfy all but one invariant, which is that `self` // owns a reference. This is satisfied by the call to `get_device` above. diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index a3420273e03e76..774ef7a14063e7 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -45,6 +45,8 @@ pub mod amba; pub mod buffer; pub mod c_types; pub mod chrdev; +#[cfg(CONFIG_COMMON_CLK)] +pub mod clk; pub mod cred; pub mod device; pub mod driver;