public inbox for rust-for-linux@vger.kernel.org
 help / color / mirror / Atom feed
From: Daniel Almeida <daniel.almeida@collabora.com>
To: lgirdwood@gmail.com, broonie@kernel.org,
	sebastian.reichel@collabora.com, sjoerd.simons@collabora.co.uk,
	ojeda@kernel.org, alex.gaynor@gmail.com, boqun.feng@gmail.com,
	gary@garyguo.net, bjorn3_gh@protonmail.com,
	a.hindborg@kernel.org, benno.lossin@proton.me,
	aliceryhl@google.com, tmgross@umich.edu, dakr@kernel.org
Cc: Daniel Almeida <daniel.almeida@collabora.com>,
	rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org
Subject: [PATCH] rust: regulator: add a bare minimum regulator abstraction
Date: Wed, 19 Feb 2025 13:25:16 -0300	[thread overview]
Message-ID: <20250219162517.278362-1-daniel.almeida@collabora.com> (raw)

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.

Note that each instance of [`Regulator`] obtained from
`Regulator::get()` can only be enabled once. This ensures that the calls
to enable and disable are perfectly balanced before `regulator_put()` is
called, as mandated by the C API.

Signed-off-by: Daniel Almeida <daniel.almeida@collabora.com>
---
 rust/bindings/bindings_helper.h |   1 +
 rust/kernel/lib.rs              |   2 +
 rust/kernel/regulator.rs        | 120 ++++++++++++++++++++++++++++++++
 3 files changed, 123 insertions(+)
 create mode 100644 rust/kernel/regulator.rs

diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index 55354e4dec14..92504f19655e 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -28,6 +28,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 496ed32b0911..0224f4c248c0 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -68,6 +68,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 000000000000..df6eb325d11a
--- /dev/null
+++ b/rust/kernel/regulator.rs
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Regulator abstractions.
+//!
+//! C header: [`include/linux/regulator/consumer.h`](srctree/include/linux/regulator/consumer.h)
+
+use crate::{
+    bindings,
+    device::Device,
+    error::{from_err_ptr, to_result, Result},
+    prelude::*,
+};
+
+use core::ptr::NonNull;
+
+/// A `struct regulator` abstraction.
+///
+/// Note that each instance of [`Regulator`] obtained from `Regulator::get()`
+/// can only be enabled once. This ensures that the calls to enable and disable
+/// are perfectly balanced before `regulator_put()` is called, as mandated by
+/// the C API.
+///
+/// # Invariants
+///
+/// - [`Regulator`] is a non-null wrapper over a pointer to a `struct regulator`
+///   obtained from `regulator_get()`.
+/// - Each instance of [`Regulator`] obtained from `Regulator::get()` can only
+///   be enabled once.
+pub struct Regulator {
+    inner: NonNull<bindings::regulator>,
+    enabled: bool,
+}
+
+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
+        // earlier 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,
+            enabled: false,
+        })
+    }
+
+    /// Enable the regulator.
+    pub fn enable(&mut self) -> Result {
+        if self.enabled {
+            return Ok(());
+        }
+
+        // SAFETY: Safe as per the type invariants of `Regulator`.
+        let res = to_result(unsafe { bindings::regulator_enable(self.inner.as_ptr()) });
+        if res.is_ok() {
+            self.enabled = true;
+        }
+
+        res
+    }
+
+    /// Disable the regulator.
+    pub fn disable(&mut self) -> Result {
+        if !self.enabled {
+            return Ok(());
+        }
+
+        // SAFETY: Safe as per the type invariants of `Regulator`.
+        let res = to_result(unsafe { bindings::regulator_disable(self.inner.as_ptr()) });
+        if res.is_ok() {
+            self.enabled = false;
+        }
+
+        res
+    }
+
+    /// Set 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.inner.as_ptr(), min_uv.0, max_uv.0)
+        })
+    }
+
+    /// Get 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.inner.as_ptr()) };
+        if voltage < 0 {
+            Err(Error::from_errno(voltage))
+        } else {
+            Ok(Microvolt(voltage))
+        }
+    }
+}
+
+impl Drop for Regulator {
+    fn drop(&mut self) {
+        if self.enabled {
+            // It is a requirement from the C API that the calls to enable and
+            // disabled are balanced before calling `regulator_put()`.
+            self.disable();
+        }
+
+        // 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 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);
-- 
2.48.1


             reply	other threads:[~2025-02-19 16:26 UTC|newest]

Thread overview: 13+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-02-19 16:25 Daniel Almeida [this message]
2025-02-19 16:28 ` [PATCH] rust: regulator: add a bare minimum regulator abstraction Alice Ryhl
2025-02-19 17:10   ` Daniel Almeida
2025-02-20 12:09     ` Mark Brown
2025-02-20 13:48       ` Daniel Almeida
2025-02-20 14:14         ` Mark Brown
2025-02-23  7:58     ` Boqun Feng
2025-03-04 15:36     ` Alice Ryhl
2025-02-19 23:05 ` Mark Brown
2025-02-19 23:35   ` Daniel Almeida
2025-02-20  1:37     ` Mark Brown
2025-02-20  9:42       ` Daniel Almeida
2025-02-20 11:58         ` Mark Brown

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=20250219162517.278362-1-daniel.almeida@collabora.com \
    --to=daniel.almeida@collabora.com \
    --cc=a.hindborg@kernel.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=broonie@kernel.org \
    --cc=dakr@kernel.org \
    --cc=gary@garyguo.net \
    --cc=lgirdwood@gmail.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=sebastian.reichel@collabora.com \
    --cc=sjoerd.simons@collabora.co.uk \
    --cc=tmgross@umich.edu \
    /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