Skip to content

rust: add initial common clock framework bindings #593

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
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
13 changes: 13 additions & 0 deletions rust/helpers.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <linux/bug.h>
#include <linux/build_bug.h>
#include <linux/clk.h>
#include <linux/uaccess.h>
#include <linux/sched/signal.h>
#include <linux/gfp.h>
Expand All @@ -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);
Expand Down
1 change: 1 addition & 0 deletions rust/kernel/bindings_helper.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0 */

#include <linux/cdev.h>
#include <linux/clk.h>
#include <linux/errname.h>
#include <linux/fs.h>
#include <linux/module.h>
Expand Down
75 changes: 75 additions & 0 deletions rust/kernel/clk.rs
Original file line number Diff line number Diff line change
@@ -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<EnabledClk> {
// 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) };
}
}
23 changes: 22 additions & 1 deletion rust/kernel/device.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand Down Expand Up @@ -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<Clk> {
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.
Expand All @@ -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.
Expand Down
2 changes: 2 additions & 0 deletions rust/kernel/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down