[PATCH v2 11/16] rust: io: register: support fixed offset register without bitfield

Gary Guo <[email protected]> Wed, 05 Aug 2026 17:35:54 +0100
Newsgroups org.kernel.vger.rust-for-linux,dev.linux.lists.driver-core,dev.linux.lists.nova-gpu,org.freedesktop.lists.dri-devel,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pci
Message-ID <[email protected]>
Add a rule to allow creating `IoLoc` in `regiser!()` using an existing type
and not create a bitfield. Add an example to demonstrate this for FIFO
registers.

This rule is also going to be used to create subregions for registers; the
example of doing so will be added later when relative registers are
removed.

Signed-off-by: Gary Guo <[email protected]>
---
 rust/kernel/io/register.rs | 47 ++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 47 insertions(+)

diff --git a/rust/kernel/io/register.rs b/rust/kernel/io/register.rs
index dc800fc71172..49a61da106c7 100644
--- a/rust/kernel/io/register.rs
+++ b/rust/kernel/io/register.rs
@@ -182,6 +182,23 @@ fn offset(self) -> usize {
     }
 }
 
+#[doc(hidden)]
+pub struct OffsetLoc<Base: ?Sized, T>(usize, PhantomData<(T, Base)>);
+
+impl<Base: ?Sized, T> OffsetLoc<Base, T> {
+    #[inline]
+    pub const fn new(offset: usize) -> Self {
+        Self(offset, PhantomData)
+    }
+}
+
+impl<Base: ?Sized, T> IoLoc<Base, T> for OffsetLoc<Base, T> {
+    #[inline(always)]
+    fn offset(self) -> usize {
+        self.0
+    }
+}
+
 /// Trait providing a base address to be added to the offset of a relative register to obtain
 /// its actual offset.
 ///
@@ -499,6 +516,19 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
 /// In this example, `SCRATCH_BOOT_STATUS` uses the same I/O address as `SCRATCH`, while providing
 /// its own `completed` field.
 ///
+/// If you do not wish to have a bitfield defined, you can also create a register using an existing
+/// type.
+///
+/// ```no_run
+/// # use kernel::io::*;
+/// register! {
+///     base: Region<0x1000>;
+///
+///     /// TX FIFO register.
+///     pub TX_FIFO: u32 @ 0x00001000;
+/// }
+/// ```
+///
 /// ## Relative registers
 ///
 /// Relative registers can be instantiated several times at a relative offset of a group of bases.
@@ -826,6 +856,23 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
 macro_rules! register {
     (base: $reg_base:ty;) => {};
 
+    // Creates a register at a fixed offset of the MMIO space with provided type.
+    (
+        base: $reg_base:ty;
+        // `$ty` cannot be `:ty` due to follow-set restrictions.
+        $(#[$attr:meta])* $vis:vis $name:ident: $ty: ident $(:: $path_frag:ident)*
+            $(@ $offset:literal)?
+            $(=> $alias:path $([$alias_idx:expr])? )?;
+        $($rest:tt)*
+    ) => {
+        $(#[$attr])* $vis
+        const $name: $crate::io::register::OffsetLoc<$reg_base, $ty $(:: $path_frag)*> =
+            $crate::io::register::OffsetLoc::new(
+                $crate::register!(@offset $(@ $offset)? $(=> $alias $([$alias_idx])?)?)
+            );
+        $crate::register!(base: $reg_base; $($rest)*);
+    };
+
     // Creates a register at a fixed offset of the MMIO space.
     //
     // This handles all of the fixed offset `@ offset`, alias of register `=> alias` and alias of

-- 
2.54.0