]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
rust: io: introduce `write_reg` and `LocatedRegister`
authorAlexandre Courbot <acourbot@nvidia.com>
Sat, 14 Mar 2026 01:06:18 +0000 (10:06 +0900)
committerDanilo Krummrich <dakr@kernel.org>
Tue, 17 Mar 2026 19:04:11 +0000 (20:04 +0100)
Some I/O types, like fixed address registers, carry their location
alongside their values. For these types, the regular `Io::write` method
can lead into repeating the location information twice: once to provide
the location itself, another time to build the value.

We are also considering supporting making all register values carry
their full location information for convenience and safety.

Add a new `Io::write_reg` method that takes a single argument
implementing `LocatedRegister`, a trait that decomposes implementors
into a `(location, value)` tuple. This allows write operations on fixed
offset registers to be done while specifying their name only once.

Suggested-by: Danilo Krummrich <dakr@kernel.org>
Link: https://lore.kernel.org/all/DH0XBLXZD81K.22SWIZ1ZAOW1@kernel.org/
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Link: https://patch.msgid.link/20260314-register-v9-8-86805b2f7e9d@nvidia.com
[ Replace FIFO with VERSION register in the examples. - Danilo ]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
rust/kernel/io.rs
rust/kernel/io/register.rs

index 4950cecf30ca7abc7b794b7d83d001100eff5bfc..fcc7678fd9e3c192bcd71964f6e4a6c37863fac6 100644 (file)
@@ -17,6 +17,8 @@ pub mod resource;
 pub use crate::register;
 pub use resource::Resource;
 
+use register::LocatedRegister;
+
 /// Physical address type.
 ///
 /// This is a type alias to either `u32` or `u64` depending on the config option
@@ -473,6 +475,49 @@ pub trait Io {
         Ok(())
     }
 
+    /// Generic fallible write of a fully-located register value.
+    ///
+    /// # Examples
+    ///
+    /// Tuples carrying a location and a value can be used with this method:
+    ///
+    /// ```no_run
+    /// use kernel::io::{
+    ///     register,
+    ///     Io,
+    ///     Mmio,
+    /// };
+    ///
+    /// register! {
+    ///     VERSION(u32) @ 0x100 {
+    ///         15:8 major;
+    ///         7:0  minor;
+    ///     }
+    /// }
+    ///
+    /// impl VERSION {
+    ///     fn new(major: u8, minor: u8) -> Self {
+    ///         VERSION::zeroed().with_major(major).with_minor(minor)
+    ///     }
+    /// }
+    ///
+    /// fn do_write_reg(io: &Mmio) -> Result {
+    ///
+    ///     io.try_write_reg(VERSION::new(1, 0))
+    /// }
+    /// ```
+    #[inline(always)]
+    fn try_write_reg<T, L, V>(&self, value: V) -> Result
+    where
+        L: IoLoc<T>,
+        V: LocatedRegister<Location = L, Value = T>,
+        Self: IoCapable<L::IoType>,
+    {
+        let (location, value) = value.into_io_op();
+
+        self.try_write(location, value)
+    }
+
     /// Generic fallible update with runtime bounds check.
     ///
     /// Note: this does not perform any synchronization. The caller is responsible for ensuring
@@ -578,6 +623,48 @@ pub trait Io {
         unsafe { self.io_write(io_value, address) }
     }
 
+    /// Generic infallible write of a fully-located register value.
+    ///
+    /// # Examples
+    ///
+    /// Tuples carrying a location and a value can be used with this method:
+    ///
+    /// ```no_run
+    /// use kernel::io::{
+    ///     register,
+    ///     Io,
+    ///     Mmio,
+    /// };
+    ///
+    /// register! {
+    ///     VERSION(u32) @ 0x100 {
+    ///         15:8 major;
+    ///         7:0  minor;
+    ///     }
+    /// }
+    ///
+    /// impl VERSION {
+    ///     fn new(major: u8, minor: u8) -> Self {
+    ///         VERSION::zeroed().with_major(major).with_minor(minor)
+    ///     }
+    /// }
+    ///
+    /// fn do_write_reg(io: &Mmio<0x1000>) {
+    ///     io.write_reg(VERSION::new(1, 0));
+    /// }
+    /// ```
+    #[inline(always)]
+    fn write_reg<T, L, V>(&self, value: V)
+    where
+        L: IoLoc<T>,
+        V: LocatedRegister<Location = L, Value = T>,
+        Self: IoKnownSize + IoCapable<L::IoType>,
+    {
+        let (location, value) = value.into_io_op();
+
+        self.write(location, value)
+    }
+
     /// Generic infallible update with compile-time bounds check.
     ///
     /// Note: this does not perform any synchronization. The caller is responsible for ensuring
index dbd458aaa76120fd564466917b82f62ebbf02c74..abc49926abfe7dc598e68d35dc1eb03d9fca463c 100644 (file)
 //!     .with_const_minor_revision::<10>()
 //!     // Runtime value.
 //!     .with_vendor_id(obtain_vendor_id());
-//! io.write((), new_boot0);
+//! io.write_reg(new_boot0);
 //!
 //! // Or, build a new value from zero and write it:
-//! io.write((), BOOT_0::zeroed()
+//! io.write_reg(BOOT_0::zeroed()
 //!     .with_const_major_revision::<3>()
 //!     .with_const_minor_revision::<10>()
 //!     .with_vendor_id(obtain_vendor_id())
@@ -382,6 +382,34 @@ where
     }
 }
 
+/// Trait implemented by items that contain both a register value and the absolute I/O location at
+/// which to write it.
+///
+/// Implementors can be used with [`Io::write_reg`](super::Io::write_reg).
+pub trait LocatedRegister {
+    /// Register value to write.
+    type Value: Register;
+    /// Full location information at which to write the value.
+    type Location: IoLoc<Self::Value>;
+
+    /// Consumes `self` and returns a `(location, value)` tuple describing a valid I/O write
+    /// operation.
+    fn into_io_op(self) -> (Self::Location, Self::Value);
+}
+
+impl<T> LocatedRegister for T
+where
+    T: FixedRegister,
+{
+    type Location = FixedRegisterLoc<Self::Value>;
+    type Value = T;
+
+    #[inline(always)]
+    fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
+        (FixedRegisterLoc::new(), self)
+    }
+}
+
 /// Defines a dedicated type for a register, including getter and setter methods for its fields and
 /// methods to read and write it from an [`Io`](kernel::io::Io) region.
 ///
@@ -436,6 +464,9 @@ where
 /// // The location of fixed offset registers is already contained in their type. Thus, the
 /// // `location` argument of `Io::write` is technically redundant and can be replaced by `()`.
 /// io.write((), val2);
+///
+/// // Or, the single-argument `Io::write_reg` can be used.
+/// io.write_reg(val2);
 /// # }
 ///
 /// ```