[PATCH v2] rust: regulator: add a bare minimum regulator abstraction
From: Daniel Almeida
Date: Wed Mar 26 2025 - 09:48:02 EST
Add a bare minimum regulator abstraction to be used by Rust drivers.
This abstraction adds a small subset of the regulator API, which is
thought to be sufficient for the drivers we have now.
Regulators provide the power needed by many hardware blocks and thus are
likely to be needed by a lot of drivers.
It was tested on rk3588, where it was used to power up the "mali"
regulator in order to power up the GPU.
Signed-off-by: Daniel Almeida <daniel.almeida@xxxxxxxxxxxxx>
---
Changes from v1:
- Rebased on rust-next
- Split the design into two types as suggested by Alice Ryhl.
- Modify the docs to highlight how users can use kernel::types::Either
or an enum to enable and disable the regulator at runtime.
---
rust/bindings/bindings_helper.h | 1 +
rust/kernel/lib.rs | 2 +
rust/kernel/regulator.rs | 127 ++++++++++++++++++++++++++++++++++++++++
3 files changed, 130 insertions(+)
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index ccb988340df69c84a702fe39a09addcc2663aebe..374f48b5ce2a602b4d1a5791201514ed8a535844 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -30,6 +30,7 @@
#include <linux/poll.h>
#include <linux/property.h>
#include <linux/refcount.h>
+#include <linux/regulator/consumer.h>
#include <linux/sched.h>
#include <linux/security.h>
#include <linux/slab.h>
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index ba0f3b0297b27dbda6a7b5d9ef8fdb8b7e6463dc..5b3228e8c80b1eb33bf36929ce3671b982efaf4a 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -72,6 +72,8 @@
pub mod prelude;
pub mod print;
pub mod rbtree;
+#[cfg(CONFIG_REGULATOR)]
+pub mod regulator;
pub mod revocable;
pub mod security;
pub mod seq_file;
diff --git a/rust/kernel/regulator.rs b/rust/kernel/regulator.rs
new file mode 100644
index 0000000000000000000000000000000000000000..4ac9b6c537dff4cfc7f2f99d48aec3cecc3151e8
--- /dev/null
+++ b/rust/kernel/regulator.rs
@@ -0,0 +1,127 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Regulator abstractions.
+//!
+//! C header: [`include/linux/regulator/consumer.h`](srctree/include/linux/regulator/consumer.h)
+//!
+//! Regulators are modeled with two types: [`Regulator`] and
+//! [`EnabledRegulator`].
+//!
+//! The transition between these types is done by calling
+//! [`Regulator::enable()`] and [`EnabledRegulator::disable()`] respectively.
+//!
+//! Use an enum or [`kernel::types::Either`] to gracefully transition between
+//! the two states at runtime if needed. Store [`EnabledRegulator`] directly
+//! otherwise.
+
+use crate::{
+ bindings,
+ device::Device,
+ error::{from_err_ptr, to_result, Result},
+ prelude::*,
+};
+
+use core::{mem::ManuallyDrop, ptr::NonNull};
+
+/// A `struct regulator` abstraction.
+///
+/// # Invariants
+///
+/// - [`Regulator`] is a non-null wrapper over a pointer to a `struct
+/// regulator` obtained from `regulator_get()`.
+/// - Each instance of [`Regulator`] is associated with a single count of `regulator_get()`.
+pub struct Regulator {
+ inner: NonNull<bindings::regulator>,
+}
+
+impl Regulator {
+ /// Obtains a [`Regulator`] instance from the system.
+ pub fn get(dev: &Device, name: &CStr) -> Result<Self> {
+ // SAFETY: It is safe to call `regulator_get()`, on a device pointer
+ // received from the C code.
+ let inner = from_err_ptr(unsafe { bindings::regulator_get(dev.as_raw(), name.as_ptr()) })?;
+
+ // SAFETY: We can safely trust `inner` to be a pointer to a valid
+ // regulator if `ERR_PTR` was not returned.
+ let inner = unsafe { NonNull::new_unchecked(inner) };
+
+ Ok(Self { inner })
+ }
+
+ /// Enables the regulator.
+ pub fn enable(self) -> Result<EnabledRegulator> {
+ // SAFETY: Safe as per the type invariants of `Regulator`.
+ let res = to_result(unsafe { bindings::regulator_enable(self.inner.as_ptr()) });
+ res.map(|()| EnabledRegulator { inner: self })
+ }
+}
+
+impl Drop for Regulator {
+ fn drop(&mut self) {
+ // SAFETY: By the type invariants, we know that `self` owns a reference,
+ // so it is safe to relinquish it now.
+ unsafe { bindings::regulator_put(self.inner.as_ptr()) };
+ }
+}
+
+/// A `struct regulator` abstraction that is known to be enabled.
+///
+/// # Invariants
+///
+/// - [`EnabledRegulator`] is a valid regulator that has been enabled.
+/// - Each instance of [`EnabledRegulator`] is associated with a single count
+/// of `regulator_enable()`.
+pub struct EnabledRegulator {
+ inner: Regulator,
+}
+
+impl EnabledRegulator {
+ fn as_ptr(&self) -> *mut bindings::regulator {
+ self.inner.inner.as_ptr()
+ }
+
+ /// Disables the regulator.
+ pub fn disable(self) -> Result<Regulator> {
+ // Keep the count on `regulator_get()`.
+ let regulator = ManuallyDrop::new(self);
+
+ // SAFETY: Safe as per the type invariants of `Self`.
+ let res = to_result(unsafe { bindings::regulator_disable(regulator.as_ptr()) });
+
+ res.map(|()| Regulator {
+ inner: regulator.inner.inner,
+ })
+ }
+
+ /// Sets the voltage for the regulator.
+ pub fn set_voltage(&self, min_uv: Microvolt, max_uv: Microvolt) -> Result {
+ // SAFETY: Safe as per the type invariants of `Regulator`.
+ to_result(unsafe { bindings::regulator_set_voltage(self.as_ptr(), min_uv.0, max_uv.0) })
+ }
+
+ /// Gets the current voltage of the regulator.
+ pub fn get_voltage(&self) -> Result<Microvolt> {
+ // SAFETY: Safe as per the type invariants of `Regulator`.
+ let voltage = unsafe { bindings::regulator_get_voltage(self.as_ptr()) };
+ if voltage < 0 {
+ Err(Error::from_errno(voltage))
+ } else {
+ Ok(Microvolt(voltage))
+ }
+ }
+}
+
+impl Drop for EnabledRegulator {
+ fn drop(&mut self) {
+ // SAFETY: By the type invariants, we know that `self` owns a reference,
+ // so it is safe to relinquish it now.
+ unsafe { bindings::regulator_disable(self.as_ptr()) };
+ }
+}
+
+/// A voltage in microvolts.
+///
+/// The explicit type is used to avoid confusion with other multiples of the
+/// volt, which can be desastrous.
+#[repr(transparent)]
+pub struct Microvolt(pub i32);
---
base-commit: e6ea10d5dbe082c54add289b44f08c9fcfe658af
change-id: 20250326-topics-tyr-regulator-e8b98f6860d7
Best regards,
--
Daniel Almeida <daniel.almeida@xxxxxxxxxxxxx>