Skip to content

Commit 632ef86

Browse files
author
Maciej Falkowski
committed
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 <[email protected]>
1 parent cd969b5 commit 632ef86

File tree

5 files changed

+113
-1
lines changed

5 files changed

+113
-1
lines changed

rust/helpers.c

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
#include <linux/bug.h>
44
#include <linux/build_bug.h>
5+
#include <linux/clk.h>
56
#include <linux/uaccess.h>
67
#include <linux/sched/signal.h>
78
#include <linux/gfp.h>
@@ -22,6 +23,18 @@ __noreturn void rust_helper_BUG(void)
2223
BUG();
2324
}
2425

26+
void rust_helper_clk_disable_unprepare(struct clk *clk)
27+
{
28+
return clk_disable_unprepare(clk);
29+
}
30+
EXPORT_SYMBOL_GPL(rust_helper_clk_disable_unprepare);
31+
32+
int rust_helper_clk_prepare_enable(struct clk *clk)
33+
{
34+
return clk_prepare_enable(clk);
35+
}
36+
EXPORT_SYMBOL_GPL(rust_helper_clk_prepare_enable);
37+
2538
unsigned long rust_helper_copy_from_user(void *to, const void __user *from, unsigned long n)
2639
{
2740
return copy_from_user(to, from, n);

rust/kernel/bindings_helper.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/* SPDX-License-Identifier: GPL-2.0 */
22

33
#include <linux/cdev.h>
4+
#include <linux/clk.h>
45
#include <linux/errname.h>
56
#include <linux/fs.h>
67
#include <linux/module.h>

rust/kernel/clk.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
3+
//! Common clock framework.
4+
//!
5+
//! C header: [`include/linux/clk.h`](../../../../include/linux/clk.h)
6+
7+
use crate::{bindings, error::Result, to_result};
8+
use core::mem::ManuallyDrop;
9+
10+
/// Represents `struct clk *`.
11+
///
12+
/// # Invariants
13+
///
14+
/// The pointer is valid.
15+
pub struct Clk(*mut bindings::clk);
16+
17+
impl Clk {
18+
/// Creates new clock structure from a raw pointer.
19+
///
20+
/// # Safety
21+
///
22+
/// The pointer must be valid.
23+
pub unsafe fn new(clk: *mut bindings::clk) -> Self {
24+
Self(clk)
25+
}
26+
27+
/// Returns value of the rate field of `struct clk`.
28+
pub fn get_rate(&self) -> usize {
29+
// SAFETY: the pointer is valid by the type invariant.
30+
unsafe { bindings::clk_get_rate(self.0) as usize }
31+
}
32+
33+
/// Prepares and enables the underlying hardware clock.
34+
///
35+
/// This function should not be called in atomic context.
36+
pub fn prepare_enable(self) -> Result<EnabledClk> {
37+
// SAFETY: the pointer is valid by the type invariant.
38+
to_result(|| unsafe { bindings::clk_prepare_enable(self.0) })?;
39+
Ok(EnabledClk(self))
40+
}
41+
}
42+
43+
impl Drop for Clk {
44+
fn drop(&mut self) {
45+
// SAFETY: the pointer is valid by the type invariant.
46+
unsafe { bindings::clk_put(self.0) };
47+
}
48+
}
49+
50+
/// A clock variant that is prepared and enabled.
51+
pub struct EnabledClk(Clk);
52+
53+
impl EnabledClk {
54+
/// Returns value of the rate field of `struct clk`.
55+
pub fn get_rate(&self) -> usize {
56+
self.0.get_rate()
57+
}
58+
59+
/// Disables and later unprepares the underlying hardware clock prematurely.
60+
///
61+
/// This function should not be called in atomic context.
62+
pub fn disable_unprepare(self) -> Clk {
63+
let mut clk = ManuallyDrop::new(self);
64+
// SAFETY: the pointer is valid by the type invariant.
65+
unsafe { bindings::clk_disable_unprepare(clk.0 .0) };
66+
core::mem::replace(&mut clk.0, Clk(core::ptr::null_mut()))
67+
}
68+
}
69+
70+
impl Drop for EnabledClk {
71+
fn drop(&mut self) {
72+
// SAFETY: the pointer is valid by the type invariant.
73+
unsafe { bindings::clk_disable_unprepare(self.0 .0) };
74+
}
75+
}

rust/kernel/device.rs

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
//!
55
//! C header: [`include/linux/device.h`](../../../../include/linux/device.h)
66
7+
#[cfg(CONFIG_COMMON_CLK)]
8+
use crate::{clk::Clk, error::from_kernel_err_ptr};
9+
710
use crate::{
811
bindings,
912
revocable::{Revocable, RevocableGuard},
@@ -43,6 +46,24 @@ pub unsafe trait RawDevice {
4346
// by the compiler because of their lifetimes).
4447
unsafe { CStr::from_char_ptr(name) }
4548
}
49+
50+
/// Lookups a clock producer consumed by this device.
51+
///
52+
/// Returns a managed reference to the clock producer.
53+
#[cfg(CONFIG_COMMON_CLK)]
54+
fn clk_get(&self, id: Option<&CStr>) -> Result<Clk> {
55+
let id_ptr = match id {
56+
Some(cstr) => cstr.as_char_ptr(),
57+
None => core::ptr::null(),
58+
};
59+
60+
// SAFETY: id_ptr is optional and may be either a valid pointer
61+
// from the type invariant or NULL otherwise.
62+
let clk_ptr = unsafe { from_kernel_err_ptr(bindings::clk_get(self.raw_device(), id_ptr)) }?;
63+
64+
// SAFETY: clock is initialized with valid pointer returned from `bindings::clk_get` call.
65+
unsafe { Ok(Clk::new(clk_ptr)) }
66+
}
4667
}
4768

4869
/// A ref-counted device.
@@ -62,7 +83,7 @@ impl Device {
6283
///
6384
/// Callers must ensure that `ptr` is valid, non-null, and has a non-zero reference count.
6485
pub unsafe fn new(ptr: *mut bindings::device) -> Self {
65-
// SAFETY: By the safety requiments, ptr is valid and its refcounted will be incremented.
86+
// SAFETY: By the safety requirements, ptr is valid and its refcounted will be incremented.
6687
unsafe { bindings::get_device(ptr) };
6788
// INVARIANT: The safety requirements satisfy all but one invariant, which is that `self`
6889
// owns a reference. This is satisfied by the call to `get_device` above.

rust/kernel/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ pub mod amba;
4545
pub mod buffer;
4646
pub mod c_types;
4747
pub mod chrdev;
48+
#[cfg(CONFIG_COMMON_CLK)]
49+
pub mod clk;
4850
pub mod cred;
4951
pub mod device;
5052
pub mod driver;

0 commit comments

Comments
 (0)