mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
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,


  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®