From: "Benno Lossin" <lossin@kernel.org>
To: "Daniel Almeida" <daniel.almeida@collabora.com>,
"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>,
"Boris Brezillon" <boris.brezillon@collabora.com>,
"Sebastian Reichel" <sebastian.reichel@collabora.com>,
"Liam Girdwood" <lgirdwood@gmail.com>,
"Mark Brown" <broonie@kernel.org>
Cc: <linux-kernel@vger.kernel.org>, <rust-for-linux@vger.kernel.org>
Subject: Re: [PATCH v3] rust: regulator: add a bare minimum regulator abstraction
Date: Tue, 13 May 2025 22:01:05 +0200 [thread overview]
Message-ID: <D9VATLUHDGU8.53I80TGVRV0J@kernel.org> (raw)
In-Reply-To: <20250513-topics-tyr-regulator-v3-1-4cc2704dfec6@collabora.com>
On Tue May 13, 2025 at 5:44 PM CEST, Daniel Almeida wrote:
> diff --git a/rust/kernel/regulator.rs b/rust/kernel/regulator.rs
> new file mode 100644
> index 0000000000000000000000000000000000000000..7b07b64f61fdd4a84ffb38e9b0f90830d5291ab9
> --- /dev/null
> +++ b/rust/kernel/regulator.rs
> @@ -0,0 +1,211 @@
> +// SPDX-License-Identifier: GPL-2.0
> +
> +//! Regulator abstractions, providing a standard kernel interface to control
> +//! voltage and current regulators.
> +//!
> +//! The intention is to allow systems to dynamically control regulator power
> +//! output in order to save power and prolong battery life. This applies to both
> +//! voltage regulators (where voltage output is controllable) and current sinks
> +//! (where current limit is controllable).
> +//!
> +//! C header: [`include/linux/regulator/consumer.h`](srctree/include/linux/regulator/consumer.h)
> +//!
> +//! Regulators are modeled in Rust with two types: [`Regulator`] and
> +//! [`EnabledRegulator`].
Would it make sense to store this in a generic variable acting as a type
state instead of using two different names? So:
pub struct Regulator<State: RegulatorState> { /* ... */ }
pub trait RegulatorState: private::Sealed {}
pub struct Enabled;
pub struct Disabled;
impl RegulatorState for Enabled {}
impl RegulatorState for Disabled {}
And then one would use `Regulator<Enabled>` and `Regulator<Disabled>`.
> +//!
> +//! 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.
> +//!
> +//! See [`Voltage and current regulator API`]("https://docs.kernel.org/driver-api/regulator.html")
> +//! for more information.
> +
> +use crate::{
> + bindings,
> + device::Device,
> + error::{from_err_ptr, to_result, Result},
> + prelude::*,
> +};
> +
> +use core::{mem::ManuallyDrop, ptr::NonNull};
> +
> +/// A `struct regulator` abstraction.
> +///
> +/// # Examples
> +///
> +/// Enabling a regulator:
> +///
> +/// ```
> +/// # use kernel::prelude::*;
> +/// # use kernel::c_str;
> +/// # use kernel::device::Device;
> +/// # use kernel::regulator::{Microvolt, Regulator, EnabledRegulator};
> +/// fn enable(dev: &Device, min_uv: Microvolt, max_uv: Microvolt) -> Result {
> +/// // Obtain a reference to a (fictitious) regulator.
> +/// let regulator: Regulator = Regulator::get(dev, c_str!("vcc"))?;
> +///
> +/// // The voltage can be set before enabling the regulator if needed, e.g.:
> +/// regulator.set_voltage(min_uv, max_uv)?;
> +///
> +/// // The same applies for `get_voltage()`, i.e.:
> +/// let voltage: Microvolt = regulator.get_voltage()?;
> +///
> +/// // Enables the regulator, consuming the previous value.
> +/// //
> +/// // From now on, the regulator is known to be enabled because of the type
> +/// // `EnabledRegulator`.
> +/// let regulator: EnabledRegulator = regulator.enable()?;
> +///
> +/// // The voltage can also be set after enabling the regulator, e.g.:
> +/// regulator.set_voltage(min_uv, max_uv)?;
> +///
> +/// // The same applies for `get_voltage()`, i.e.:
> +/// let voltage: Microvolt = regulator.get_voltage()?;
> +///
> +/// // Dropping an enabled regulator will disable it. The refcount will be
> +/// // decremented.
Where would you normally store an enabled regulator to keep it alive?
Maybe adjust the example to do just that?
> +/// drop(regulator);
> +/// // ...
> +/// # Ok::<(), Error>(())
> +/// }
> +///```
> +///
> +/// Disabling a regulator:
> +///
> +///```
> +/// # use kernel::prelude::*;
> +/// # use kernel::c_str;
> +/// # use kernel::device::Device;
> +/// # use kernel::regulator::{Microvolt, Regulator, EnabledRegulator};
> +/// fn disable(dev: &Device, regulator: EnabledRegulator) -> Result {
> +/// // We can also disable an enabled regulator without reliquinshing our
> +/// // refcount:
> +/// let regulator: Regulator = regulator.disable()?;
> +///
> +/// // The refcount will be decremented when `regulator` is dropped.
> +/// drop(regulator);
> +/// // ...
> +/// # Ok::<(), Error>(())
> +/// }
> +/// ```
> +///
> +/// # Invariants
> +///
> +/// - [`Regulator`] is a non-null wrapper over a pointer to a `struct
> +/// regulator` obtained from [`regulator_get()`](https://docs.kernel.org/driver-api/regulator.html#c.regulator_get).
This should be "`inner` is a pointer obtained from
[`regulator_get()`](...)".
> +/// - Each instance of [`Regulator`] is associated with a single count of
> +/// [`regulator_get()`](https://docs.kernel.org/driver-api/regulator.html#c.regulator_get).
This is redundant, so we should remove it.
> +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 })
> + }
> +
> + /// Sets the voltage for the regulator.
> + ///
> + /// This can be used to ensure that the device powers up cleanly.
> + 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)
> + })
> + }
> +
> + /// 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.inner.as_ptr()) };
> + if voltage < 0 {
> + Err(Error::from_errno(voltage))
> + } else {
> + Ok(Microvolt(voltage))
> + }
> + }
> +}
> +
> +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 [`Regulator`] that is known to be enabled.
> +///
> +/// # Invariants
> +///
> +/// - [`EnabledRegulator`] is a valid regulator that has been enabled.
This isn't fully clear what it's supposed to mean to me. Maybe mention
the `regulator_enable` function?
> +/// - Each instance of [`EnabledRegulator`] is associated with a single count
> +/// of [`regulator_enable()`](https://docs.kernel.org/driver-api/regulator.html#c.regulator_enable)
> +/// that was obtained from the [`Regulator`] instance once it was enabled.
Ah I see you mention it here, then what is the bullet point above about?
Also, please refer to `inner` instead of [`EnabledRegulator`].
> +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);
Why don't we drop the refcount if the `regulator_disable` call fails?
> +
> + // 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 {
> + self.inner.set_voltage(min_uv, max_uv)
> + }
> +
> + /// Gets the current voltage of the regulator.
> + pub fn get_voltage(&self) -> Result<Microvolt> {
> + self.inner.get_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()) };
Same here, what happens to the refcount?
---
Cheers,
Benno
> + }
> +}
> +
> +/// A voltage in microvolts.
> +///
> +/// The explicit type is used to avoid confusion with other multiples of the
> +/// volt, which can be desastrous.
> +#[repr(transparent)]
> +#[derive(Copy, Clone, PartialEq, Eq)]
> +pub struct Microvolt(pub i32);
>
> ---
> base-commit: edc5e6e019c99b529b3d1f2801d5cce9924ae79b
> change-id: 20250326-topics-tyr-regulator-e8b98f6860d7
>
> Best regards,
next prev parent reply other threads:[~2025-05-13 20:01 UTC|newest]
Thread overview: 52+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-05-13 15:44 Daniel Almeida
2025-05-13 20:01 ` Benno Lossin [this message]
2025-05-14 7:46 ` Mark Brown
2025-05-14 9:37 ` Benno Lossin
2025-05-14 10:16 ` Mark Brown
2025-05-14 10:31 ` Benno Lossin
2025-05-14 11:50 ` Mark Brown
2025-05-14 12:23 ` Benno Lossin
2025-05-14 12:48 ` Mark Brown
2025-05-14 14:06 ` Benno Lossin
2025-05-14 13:01 ` Daniel Almeida
2025-05-14 13:57 ` Benno Lossin
2025-05-14 14:40 ` Daniel Almeida
2025-05-14 15:38 ` Benno Lossin
2025-05-14 15:50 ` Mark Brown
2025-05-14 16:05 ` Benno Lossin
2025-05-14 16:08 ` Mark Brown
2025-05-14 16:19 ` Daniel Almeida
2025-05-14 17:41 ` Benno Lossin
2025-05-14 16:10 ` Daniel Almeida
2025-05-15 8:19 ` Mark Brown
2025-05-14 15:48 ` Mark Brown
2025-05-14 8:27 ` Mark Brown
2025-05-18 2:28 ` Alexandre Courbot
2025-05-18 7:19 ` Benno Lossin
2025-05-18 8:14 ` Alexandre Courbot
2025-05-18 8:30 ` Alexandre Courbot
2025-05-18 9:57 ` Benno Lossin
2025-05-18 11:12 ` Alexandre Courbot
2025-05-18 14:05 ` Benno Lossin
2025-05-19 0:29 ` Alexandre Courbot
2025-05-18 12:20 ` Mark Brown
2025-05-18 12:51 ` Alexandre Courbot
2025-05-19 9:55 ` Mark Brown
2025-05-18 14:04 ` Benno Lossin
2025-05-19 9:56 ` Mark Brown
2025-05-19 11:25 ` Benno Lossin
2025-05-19 11:46 ` Mark Brown
2025-05-19 12:30 ` Benno Lossin
2025-05-19 12:46 ` Mark Brown
2025-05-18 12:17 ` Mark Brown
2025-05-18 12:49 ` Alexandre Courbot
2025-05-19 9:54 ` Mark Brown
2025-05-18 15:11 ` Daniel Almeida
2025-05-19 1:25 ` Alexandre Courbot
2025-05-19 10:52 ` Daniel Almeida
2025-05-19 11:01 ` Daniel Almeida
2025-05-19 11:54 ` Benno Lossin
2025-05-19 11:59 ` Miguel Ojeda
2025-05-19 14:43 ` Alexandre Courbot
2025-05-20 18:09 ` Benno Lossin
2025-05-19 14:20 ` Alexandre Courbot
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=D9VATLUHDGU8.53I80TGVRV0J@kernel.org \
--to=lossin@kernel.org \
--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=boris.brezillon@collabora.com \
--cc=broonie@kernel.org \
--cc=dakr@kernel.org \
--cc=daniel.almeida@collabora.com \
--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=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
all inboxes | Powered by JetHome®