From: Viresh Kumar <viresh.kumar@linaro.org>
To: "Rafael J. Wysocki" <rafael@kernel.org>,
"Miguel Ojeda" <miguel.ojeda.sandonis@gmail.com>,
"Danilo Krummrich" <dakr@redhat.com>,
"Michael Turquette" <mturquette@baylibre.com>,
"Stephen Boyd" <sboyd@kernel.org>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Alex Gaynor" <alex.gaynor@gmail.com>,
"Boqun Feng" <boqun.feng@gmail.com>,
"Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <benno.lossin@proton.me>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>,
"Danilo Krummrich" <dakr@kernel.org>
Cc: "Viresh Kumar" <viresh.kumar@linaro.org>,
linux-pm@vger.kernel.org,
"Vincent Guittot" <vincent.guittot@linaro.org>,
"Nishanth Menon" <nm@ti.com>,
rust-for-linux@vger.kernel.org,
"Manos Pitsidianakis" <manos.pitsidianakis@linaro.org>,
"Alex Bennée" <alex.bennee@linaro.org>,
"Joakim Bech" <joakim.bech@linaro.org>,
"Rob Herring" <robh@kernel.org>,
"Yury Norov" <yury.norov@gmail.com>,
"Burak Emir" <bqe@google.com>,
"Rasmus Villemoes" <linux@rasmusvillemoes.dk>,
"Russell King" <linux@armlinux.org.uk>,
linux-clk@vger.kernel.org,
"Daniel Almeida" <daniel.almeida@collabora.com>,
linux-kernel@vger.kernel.org
Subject: [PATCH V11 05/15] rust: clk: Add initial abstractions
Date: Mon, 21 Apr 2025 12:52:12 +0530 [thread overview]
Message-ID: <4207f5bf540de148e1bd31a88dc75eaac1aaf897.1745218975.git.viresh.kumar@linaro.org> (raw)
In-Reply-To: <cover.1745218975.git.viresh.kumar@linaro.org>
Add initial abstractions for the clk APIs. These provide the minimal
functionality needed for common use cases, making them straightforward
to introduce in the first iteration.
These will be used by Rust based cpufreq / OPP layers to begin with.
Tested-by: Daniel Almeida <daniel.almeida@collabora.com>
Reviewed-by: Daniel Almeida <daniel.almeida@collabora.com>
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
MAINTAINERS | 1 +
rust/kernel/clk.rs | 318 +++++++++++++++++++++++++++++++++++++++++++++
rust/kernel/lib.rs | 2 +
3 files changed, 321 insertions(+)
create mode 100644 rust/kernel/clk.rs
diff --git a/MAINTAINERS b/MAINTAINERS
index 608689342aaf..12cde55579a0 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5884,6 +5884,7 @@ F: include/linux/clk-pr*
F: include/linux/clk/
F: include/linux/of_clk.h
F: rust/helpers/clk.c
+F: rust/kernel/clk.rs
X: drivers/clk/clkdev.c
COMMON INTERNET FILE SYSTEM CLIENT (CIFS and SMB3)
diff --git a/rust/kernel/clk.rs b/rust/kernel/clk.rs
new file mode 100644
index 000000000000..698e05cd41f8
--- /dev/null
+++ b/rust/kernel/clk.rs
@@ -0,0 +1,318 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Clock abstractions.
+//!
+//! C header: [`include/linux/clk.h`](srctree/include/linux/clk.h)
+//!
+//! Reference: <https://docs.kernel.org/driver-api/clk.html>
+
+use crate::{
+ device::Device,
+ error::{from_err_ptr, to_result, Result},
+ ffi::c_ulong,
+ prelude::*,
+};
+
+use core::{ops::Deref, ptr};
+
+/// The frequency unit.
+///
+/// Represents a frequency in hertz, wrapping a [`c_ulong`] value.
+///
+/// ## Examples
+///
+/// ```
+/// use kernel::clk::Hertz;
+///
+/// let hz = 1_000_000_000;
+/// let rate = Hertz(hz);
+///
+/// assert_eq!(rate.as_hz(), hz);
+/// assert_eq!(rate, Hertz(hz));
+/// assert_eq!(rate, Hertz::from_khz(hz / 1_000));
+/// assert_eq!(rate, Hertz::from_mhz(hz / 1_000_000));
+/// assert_eq!(rate, Hertz::from_ghz(hz / 1_000_000_000));
+/// ```
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+pub struct Hertz(pub c_ulong);
+
+impl Hertz {
+ /// Create a new instance from kilohertz (kHz)
+ pub fn from_khz(khz: c_ulong) -> Self {
+ Self(khz * 1_000)
+ }
+
+ /// Create a new instance from megahertz (MHz)
+ pub fn from_mhz(mhz: c_ulong) -> Self {
+ Self(mhz * 1_000_000)
+ }
+
+ /// Create a new instance from gigahertz (GHz)
+ pub fn from_ghz(ghz: c_ulong) -> Self {
+ Self(ghz * 1_000_000_000)
+ }
+
+ /// Get the frequency in hertz
+ pub fn as_hz(&self) -> c_ulong {
+ self.0
+ }
+
+ /// Get the frequency in kilohertz
+ pub fn as_khz(&self) -> c_ulong {
+ self.0 / 1_000
+ }
+
+ /// Get the frequency in megahertz
+ pub fn as_mhz(&self) -> c_ulong {
+ self.0 / 1_000_000
+ }
+
+ /// Get the frequency in gigahertz
+ pub fn as_ghz(&self) -> c_ulong {
+ self.0 / 1_000_000_000
+ }
+}
+
+impl From<Hertz> for c_ulong {
+ fn from(freq: Hertz) -> Self {
+ freq.0
+ }
+}
+
+/// A reference-counted clock.
+///
+/// Rust abstraction for the C [`struct clk`].
+///
+/// # Invariants
+///
+/// A [`Clk`] instance holds either a pointer to a valid [`struct clk`] created by the C portion of
+/// the kernel or a NULL pointer.
+///
+/// Instances of this type are reference-counted. Calling [`Clk::get`] ensures that the allocation
+/// remains valid for the lifetime of the [`Clk`].
+///
+/// ## Examples
+///
+/// The following example demonstrates how to obtain and configure a clock for a device.
+///
+/// ```
+/// use kernel::c_str;
+/// use kernel::clk::{Clk, Hertz};
+/// use kernel::device::Device;
+/// use kernel::error::Result;
+///
+/// fn configure_clk(dev: &Device) -> Result {
+/// let clk = Clk::get(dev, Some(c_str!("apb_clk")))?;
+///
+/// clk.prepare_enable()?;
+///
+/// let expected_rate = Hertz::from_ghz(1);
+///
+/// if clk.rate() != expected_rate {
+/// clk.set_rate(expected_rate)?;
+/// }
+///
+/// clk.disable_unprepare();
+/// Ok(())
+/// }
+/// ```
+///
+/// [`struct clk`]: https://docs.kernel.org/driver-api/clk.html
+#[repr(transparent)]
+pub struct Clk(*mut bindings::clk);
+
+impl Clk {
+ /// Gets [`Clk`] corresponding to a [`Device`] and a connection id.
+ ///
+ /// Equivalent to the kernel's [`clk_get`] API.
+ ///
+ /// [`clk_get`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_get
+ pub fn get(dev: &Device, name: Option<&CStr>) -> Result<Self> {
+ let con_id = if let Some(name) = name {
+ name.as_ptr()
+ } else {
+ ptr::null()
+ };
+
+ // SAFETY: It is safe to call [`clk_get`] for a valid device pointer.
+ //
+ // INVARIANT: The reference-count is decremented when [`Clk`] goes out of scope.
+ Ok(Self(from_err_ptr(unsafe {
+ bindings::clk_get(dev.as_raw(), con_id)
+ })?))
+ }
+
+ /// Obtain the raw [`struct clk`] pointer.
+ #[inline]
+ pub fn as_raw(&self) -> *mut bindings::clk {
+ self.0
+ }
+
+ /// Enable the clock.
+ ///
+ /// Equivalent to the kernel's [`clk_enable`] API.
+ ///
+ /// [`clk_enable`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_enable
+ #[inline]
+ pub fn enable(&self) -> Result {
+ // SAFETY: By the type invariants, self.as_raw() is a valid argument for [`clk_enable`].
+ to_result(unsafe { bindings::clk_enable(self.as_raw()) })
+ }
+
+ /// Disable the clock.
+ ///
+ /// Equivalent to the kernel's [`clk_disable`] API.
+ ///
+ /// [`clk_disable`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_disable
+ #[inline]
+ pub fn disable(&self) {
+ // SAFETY: By the type invariants, self.as_raw() is a valid argument for [`clk_disable`].
+ unsafe { bindings::clk_disable(self.as_raw()) };
+ }
+
+ /// Prepare the clock.
+ ///
+ /// Equivalent to the kernel's [`clk_prepare`] API.
+ ///
+ /// [`clk_prepare`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_prepare
+ #[inline]
+ pub fn prepare(&self) -> Result {
+ // SAFETY: By the type invariants, self.as_raw() is a valid argument for [`clk_prepare`].
+ to_result(unsafe { bindings::clk_prepare(self.as_raw()) })
+ }
+
+ /// Unprepare the clock.
+ ///
+ /// Equivalent to the kernel's [`clk_unprepare`] API.
+ ///
+ /// [`clk_unprepare`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_unprepare
+ #[inline]
+ pub fn unprepare(&self) {
+ // SAFETY: By the type invariants, self.as_raw() is a valid argument for [`clk_unprepare`].
+ unsafe { bindings::clk_unprepare(self.as_raw()) };
+ }
+
+ /// Prepare and enable the clock.
+ ///
+ /// Equivalent to calling [`Clk::prepare`] followed by [`Clk::enable`].
+ #[inline]
+ pub fn prepare_enable(&self) -> Result {
+ // SAFETY: By the type invariants, self.as_raw() is a valid argument for
+ // [`clk_prepare_enable`].
+ to_result(unsafe { bindings::clk_prepare_enable(self.as_raw()) })
+ }
+
+ /// Disable and unprepare the clock.
+ ///
+ /// Equivalent to calling [`Clk::disable`] followed by [`Clk::unprepare`].
+ #[inline]
+ pub fn disable_unprepare(&self) {
+ // SAFETY: By the type invariants, self.as_raw() is a valid argument for
+ // [`clk_disable_unprepare`].
+ unsafe { bindings::clk_disable_unprepare(self.as_raw()) };
+ }
+
+ /// Get clock's rate.
+ ///
+ /// Equivalent to the kernel's [`clk_get_rate`] API.
+ ///
+ /// [`clk_get_rate`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_get_rate
+ #[inline]
+ pub fn rate(&self) -> Hertz {
+ // SAFETY: By the type invariants, self.as_raw() is a valid argument for [`clk_get_rate`].
+ Hertz(unsafe { bindings::clk_get_rate(self.as_raw()) })
+ }
+
+ /// Set clock's rate.
+ ///
+ /// Equivalent to the kernel's [`clk_set_rate`] API.
+ ///
+ /// [`clk_set_rate`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_set_rate
+ #[inline]
+ pub fn set_rate(&self, rate: Hertz) -> Result {
+ // SAFETY: By the type invariants, self.as_raw() is a valid argument for [`clk_set_rate`].
+ to_result(unsafe { bindings::clk_set_rate(self.as_raw(), rate.as_hz()) })
+ }
+}
+
+impl Drop for Clk {
+ fn drop(&mut self) {
+ // SAFETY: By the type invariants, self.as_raw() is a valid argument for [`clk_put`].
+ unsafe { bindings::clk_put(self.as_raw()) };
+ }
+}
+
+/// A reference-counted optional clock.
+///
+/// A lightweight wrapper around an optional [`Clk`]. An [`OptionalClk`] represents a [`Clk`] that
+/// a driver can function without but may improve performance or enable additional features when
+/// available.
+///
+/// # Invariants
+///
+/// An [`OptionalClk`] instance encapsulates a [`Clk`] with either a valid [`struct clk`] or `NULL`
+/// pointer.
+///
+/// Instances of this type are reference-counted. Calling [`OptionalClk::get`] ensures that the
+/// allocation remains valid for the lifetime of the [`OptionalClk`].
+///
+/// ## Examples
+///
+/// The following example demonstrates how to obtain and configure an optional clock for a device.
+/// The code functions correctly whether or not the clock is available.
+///
+/// ```
+/// use kernel::c_str;
+/// use kernel::clk::{OptionalClk, Hertz};
+/// use kernel::device::Device;
+/// use kernel::error::Result;
+///
+/// fn configure_clk(dev: &Device) -> Result {
+/// let clk = OptionalClk::get(dev, Some(c_str!("apb_clk")))?;
+///
+/// clk.prepare_enable()?;
+///
+/// let expected_rate = Hertz::from_ghz(1);
+///
+/// if clk.rate() != expected_rate {
+/// clk.set_rate(expected_rate)?;
+/// }
+///
+/// clk.disable_unprepare();
+/// Ok(())
+/// }
+/// ```
+///
+/// [`struct clk`]: https://docs.kernel.org/driver-api/clk.html
+pub struct OptionalClk(Clk);
+
+impl OptionalClk {
+ /// Gets [`OptionalClk`] corresponding to a [`Device`] and a connection id.
+ ///
+ /// Equivalent to the kernel's [`clk_get_optional`] API.
+ ///
+ /// [`clk_get_optional`]: https://docs.kernel.org/core-api/kernel-api.html#c.clk_get_optional
+ pub fn get(dev: &Device, name: Option<&CStr>) -> Result<Self> {
+ let con_id = if let Some(name) = name {
+ name.as_ptr()
+ } else {
+ ptr::null()
+ };
+
+ // SAFETY: It is safe to call [`clk_get_optional`] for a valid device pointer.
+ //
+ // INVARIANT: The reference-count is decremented when [`OptionalClk`] goes out of scope.
+ Ok(Self(Clk(from_err_ptr(unsafe {
+ bindings::clk_get_optional(dev.as_raw(), con_id)
+ })?)))
+ }
+}
+
+// Make [`OptionalClk`] behave like [`Clk`].
+impl Deref for OptionalClk {
+ type Target = Clk;
+
+ fn deref(&self) -> &Clk {
+ &self.0
+ }
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 75f78f6bfaa6..f4dcfefe94be 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -42,6 +42,8 @@
pub mod block;
#[doc(hidden)]
pub mod build_assert;
+#[cfg(CONFIG_COMMON_CLK)]
+pub mod clk;
pub mod cpumask;
pub mod cred;
pub mod device;
--
2.31.1.272.g89b43f80a514
next prev parent reply other threads:[~2025-04-21 7:23 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-04-21 7:22 [PATCH V11 00/15] Rust abstractions for clk, cpumask, cpufreq, OPP Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 01/15] rust: cpumask: Add few more helpers Viresh Kumar
2025-05-02 16:56 ` Yury Norov
2025-04-21 7:22 ` [PATCH V11 02/15] rust: cpumask: Add initial abstractions Viresh Kumar
2025-05-02 17:01 ` Yury Norov
2025-04-21 7:22 ` [PATCH V11 03/15] MAINTAINERS: Add entry for Rust cpumask API Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 04/15] rust: clk: Add helpers for Rust code Viresh Kumar
2025-04-21 7:22 ` Viresh Kumar [this message]
2025-04-21 7:22 ` [PATCH V11 06/15] rust: macros: enable use of hyphens in module names Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 07/15] rust: cpu: Add from_cpu() Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 08/15] rust: opp: Add initial abstractions for OPP framework Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 09/15] rust: opp: Add abstractions for the OPP table Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 10/15] rust: opp: Add abstractions for the configuration options Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 11/15] rust: cpufreq: Add initial abstractions for cpufreq framework Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 12/15] rust: cpufreq: Extend abstractions for policy and driver ops Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 13/15] rust: cpufreq: Extend abstractions for driver registration Viresh Kumar
2025-04-23 12:05 ` Danilo Krummrich
2025-04-24 6:01 ` Viresh Kumar
2025-04-23 12:08 ` Danilo Krummrich
2025-04-24 6:29 ` Viresh Kumar
2025-04-24 8:03 ` Danilo Krummrich
2025-04-24 10:51 ` Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 14/15] rust: opp: Extend OPP abstractions with cpufreq support Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 15/15] cpufreq: Add Rust-based cpufreq-dt driver Viresh Kumar
2025-05-02 7:01 ` [PATCH V11 00/15] Rust abstractions for clk, cpumask, cpufreq, OPP Viresh Kumar
2025-05-06 10:13 ` Andrew Ballance
2025-05-06 10:25 ` Miguel Ojeda
2025-05-08 2:10 ` Viresh Kumar
2025-05-08 2:52 ` Philip Li
2025-05-19 5:12 ` Viresh Kumar
2025-05-09 8:43 ` Miguel Ojeda
2025-05-13 11:15 ` Viresh Kumar
2025-05-19 5:00 ` Viresh Kumar
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4207f5bf540de148e1bd31a88dc75eaac1aaf897.1745218975.git.viresh.kumar@linaro.org \
--to=viresh.kumar@linaro.org \
--cc=a.hindborg@kernel.org \
--cc=alex.bennee@linaro.org \
--cc=alex.gaynor@gmail.com \
--cc=aliceryhl@google.com \
--cc=benno.lossin@proton.me \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=bqe@google.com \
--cc=dakr@kernel.org \
--cc=dakr@redhat.com \
--cc=daniel.almeida@collabora.com \
--cc=gary@garyguo.net \
--cc=joakim.bech@linaro.org \
--cc=linux-clk@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=linux@armlinux.org.uk \
--cc=linux@rasmusvillemoes.dk \
--cc=manos.pitsidianakis@linaro.org \
--cc=miguel.ojeda.sandonis@gmail.com \
--cc=mturquette@baylibre.com \
--cc=nm@ti.com \
--cc=ojeda@kernel.org \
--cc=rafael@kernel.org \
--cc=robh@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=sboyd@kernel.org \
--cc=tmgross@umich.edu \
--cc=vincent.guittot@linaro.org \
--cc=yury.norov@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).