[PATCH 10/10] rust: io: register: remove relative registers
Gary Guo <[email protected]> Tue, 21 Jul 2026 17:54:34 +0100
| Newsgroups | dev.linux.lists.nova-gpu,dev.linux.lists.driver-core,org.freedesktop.lists.dri-devel,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pci,org.kernel.vger.rust-for-linux |
|---|---|
| Message-ID | <[email protected]> |
Relative registers can be better served by projection to subregion instead of ad-hoc handling in register macro. Projection composes better (e.g. it natively allows relative registers of relative registers without needing additional support). Signed-off-by: Gary Guo <[email protected]> --- rust/kernel/io/register.rs | 469 +-------------------------------------------- 1 file changed, 3 insertions(+), 466 deletions(-) diff --git a/rust/kernel/io/register.rs b/rust/kernel/io/register.rs index df47c28ba4f4..88ead1c1874f 100644 --- a/rust/kernel/io/register.rs +++ b/rust/kernel/io/register.rs @@ -191,78 +191,6 @@ fn offset(self) -> usize { } } -/// Trait providing a base address to be added to the offset of a relative register to obtain -/// its actual offset. -/// -/// The `T` generic argument is used to distinguish which base to use, in case a type provides -/// several bases. It is given to the `register!` macro to restrict the use of the register to -/// implementors of this particular variant. -pub trait RegisterBase<T> { - /// Base address to which register offsets are added. - const BASE: usize; -} - -/// Trait implemented by all registers that are relative to a base. -pub trait WithBase { - /// Family of bases applicable to this register. - type BaseFamily; - - /// Returns the absolute location of this type when using `B` as its base. - #[inline(always)] - fn of<B: RegisterBase<Self::BaseFamily>>() -> RelativeRegisterLoc<Self, B> - where - Self: Register, - { - RelativeRegisterLoc::new() - } -} - -/// Trait implemented by relative registers. -pub trait RelativeRegister: Register + WithBase {} - -/// Location of a relative register. -/// -/// This can either be an immediately accessible regular [`RelativeRegister`], or a -/// [`RelativeRegisterArray`] that needs one additional resolution through -/// [`RelativeRegisterLoc::at`]. -pub struct RelativeRegisterLoc<T: WithBase, B: ?Sized>(PhantomData<T>, PhantomData<B>); - -impl<T, B> RelativeRegisterLoc<T, B> -where - T: Register + WithBase, - B: RegisterBase<T::BaseFamily> + ?Sized, -{ - /// Returns the location of a relative register or register array. - #[inline(always)] - // We do not implement `Default` so we can be const. - #[expect(clippy::new_without_default)] - pub const fn new() -> Self { - Self(PhantomData, PhantomData) - } - - // Returns the absolute offset of the relative register using base `B`. - // - // This is implemented as a private const method so it can be reused by the [`IoLoc`] - // implementations of both [`RelativeRegisterLoc`] and [`RelativeRegisterArrayLoc`]. - #[inline] - const fn offset(self) -> usize { - B::BASE + T::OFFSET - } -} - -impl<SuperBase: ?Sized, T, B> IoLoc<SuperBase, T> for RelativeRegisterLoc<T, B> -where - T: RelativeRegister<Base = SuperBase>, - B: RegisterBase<T::BaseFamily> + ?Sized, -{ - type IoType = T::Storage; - - #[inline(always)] - fn offset(self) -> usize { - RelativeRegisterLoc::offset(self) - } -} - /// Trait implemented by arrays of registers. pub trait RegisterArray: Register { /// Number of elements in the registers array. @@ -327,73 +255,6 @@ fn try_at(idx: usize) -> Option<RegisterArrayLoc<Self>> } } -/// Trait implemented by arrays of relative registers. -pub trait RelativeRegisterArray: RegisterArray + WithBase {} - -/// Location of a relative array register. -pub struct RelativeRegisterArrayLoc< - T: RelativeRegisterArray, - B: RegisterBase<T::BaseFamily> + ?Sized, ->(RelativeRegisterLoc<T, B>, usize); - -impl<T, B> RelativeRegisterArrayLoc<T, B> -where - T: RelativeRegisterArray, - B: RegisterBase<T::BaseFamily> + ?Sized, -{ - /// Returns the location of register `T` from the base `B` at index `idx`, with build-time - /// validation. - #[inline(always)] - pub fn new(idx: usize) -> Self { - build_assert!(idx < T::SIZE); - - Self(RelativeRegisterLoc::new(), idx) - } - - /// Attempts to return the location of register `T` from the base `B` at index `idx`, with - /// runtime validation. - #[inline(always)] - pub fn try_new(idx: usize) -> Option<Self> { - if idx < T::SIZE { - Some(Self(RelativeRegisterLoc::new(), idx)) - } else { - None - } - } -} - -/// Methods exclusive to [`RelativeRegisterLoc`]s created with a [`RelativeRegisterArray`]. -impl<T, B> RelativeRegisterLoc<T, B> -where - T: RelativeRegisterArray, - B: RegisterBase<T::BaseFamily> + ?Sized, -{ - /// Returns the location of the register at position `idx`, with build-time validation. - #[inline(always)] - pub fn at(self, idx: usize) -> RelativeRegisterArrayLoc<T, B> { - RelativeRegisterArrayLoc::new(idx) - } - - /// Returns the location of the register at position `idx`, with runtime validation. - #[inline(always)] - pub fn try_at(self, idx: usize) -> Option<RelativeRegisterArrayLoc<T, B>> { - RelativeRegisterArrayLoc::try_new(idx) - } -} - -impl<SuperBase: ?Sized, T, B> IoLoc<SuperBase, T> for RelativeRegisterArrayLoc<T, B> -where - T: RelativeRegisterArray<Base = SuperBase>, - B: RegisterBase<T::BaseFamily> + ?Sized, -{ - type IoType = T::Storage; - - #[inline(always)] - fn offset(self) -> usize { - self.0.offset() + self.1 * T::STRIDE - } -} - /// Trait implemented by items that contain both a register value and the absolute I/O location at /// which to write it. /// @@ -428,8 +289,7 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) { /// This documentation focuses on how to declare registers. See the [module-level /// documentation](mod@kernel::io::register) for examples of how to access them. /// -/// There are 4 possible kinds of registers: fixed offset registers, relative registers, arrays of -/// registers, and relative arrays of registers. +/// Registers can either be fixed offset registers or arrays of registers. /// /// ## Fixed offset registers /// @@ -514,122 +374,6 @@ 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. /// -/// ## Relative registers -/// -/// Relative registers can be instantiated several times at a relative offset of a group of bases. -/// For instance, imagine the following I/O space: -/// -/// ```text -/// +-----------------------------+ -/// | ... | -/// | | -/// 0x100--->+------------CPU0-------------+ -/// | | -/// 0x110--->+-----------------------------+ -/// | CPU_CTL | -/// +-----------------------------+ -/// | ... | -/// | | -/// | | -/// 0x200--->+------------CPU1-------------+ -/// | | -/// 0x210--->+-----------------------------+ -/// | CPU_CTL | -/// +-----------------------------+ -/// | ... | -/// +-----------------------------+ -/// ``` -/// -/// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset `0x10` of their I/O -/// space segment. Since both instances of `CPU_CTL` share the same layout, we don't want to define -/// them twice and would prefer a way to select which one to use from a single definition. -/// -/// This can be done using the `Base + Offset` syntax when specifying the register's address: -/// -/// ```ignore -/// register! { -/// ... -/// pub RELATIVE_REG(u32) @ Base + 0x80 { -/// ... -/// } -/// } -/// ``` -/// -/// This creates a register with an offset of `0x80` from a given base. -/// -/// `Base` is an arbitrary type (typically a ZST) to be used as a generic parameter of the -/// [`RegisterBase`] trait to provide the base as a constant, i.e. each type providing a base for -/// this register needs to implement `RegisterBase<Base>`. -/// -/// The location of relative registers can be built using the [`WithBase::of`] method to specify -/// its base. All relative registers implement [`WithBase`]. -/// -/// Here is the above layout translated into code: -/// -/// ```no_run -/// use kernel::{ -/// io::{ -/// register, -/// register::{ -/// RegisterBase, -/// WithBase, -/// }, -/// Io, -/// Region, -/// }, -/// }; -/// # use kernel::io::Mmio; -/// -/// // Type used to identify the base. -/// pub struct CpuCtlBase; -/// -/// // ZST describing `CPU0`. -/// struct Cpu0; -/// impl RegisterBase<CpuCtlBase> for Cpu0 { -/// const BASE: usize = 0x100; -/// } -/// -/// // ZST describing `CPU1`. -/// struct Cpu1; -/// impl RegisterBase<CpuCtlBase> for Cpu1 { -/// const BASE: usize = 0x200; -/// } -/// -/// // This makes `CPU_CTL` accessible from all implementors of `RegisterBase<CpuCtlBase>`. -/// register! { -/// base: Region<0x1000>; -/// -/// /// CPU core control. -/// pub CPU_CTL(u32) @ CpuCtlBase + 0x10 { -/// 0:0 start; -/// } -/// } -/// -/// # fn test(io: Mmio<'_, Region<0x1000>>) { -/// // Read the status of `Cpu0`. -/// let cpu0_started = io.read(CPU_CTL::of::<Cpu0>()); -/// -/// // Stop `Cpu0`. -/// io.write(WithBase::of::<Cpu0>(), CPU_CTL::zeroed()); -/// # } -/// -/// // Aliases can also be defined for relative register. -/// register! { -/// base: Region<0x1000>; -/// -/// /// Alias to CPU core control. -/// pub CPU_CTL_ALIAS(u32) => CpuCtlBase + CPU_CTL { -/// /// Start the aliased CPU core. -/// 1:1 alias_start; -/// } -/// } -/// -/// # fn test2(io: Mmio<'_, Region<0x1000>>) { -/// // Start the aliased `CPU0`, leaving its other fields untouched. -/// io.update(CPU_CTL_ALIAS::of::<Cpu0>(), |r| r.with_alias_start(true)); -/// # } -/// ``` -/// /// ## Arrays of registers /// /// Some I/O areas contain consecutive registers that share the same field layout. These areas can @@ -725,118 +469,6 @@ fn into_io_op(self) -> (FixedRegisterLoc<T>, T) { /// # Ok(()) /// # } /// ``` -/// -/// ## Relative arrays of registers -/// -/// Combining the two features described in the sections above, arrays of registers accessible from -/// a base can also be defined: -/// -/// ```ignore -/// register! { -/// ... -/// pub RELATIVE_REGISTER_ARRAY(u8)[10, stride = 4] @ Base + 0x100 { -/// ... -/// } -/// } -/// ``` -/// -/// Like relative registers, they implement the [`WithBase`] trait. However the return value of -/// [`WithBase::of`] cannot be used directly as a location and must be further specified using the -/// [`at`](RelativeRegisterLoc::at) method. -/// -/// ```no_run -/// use kernel::{ -/// io::{ -/// register, -/// register::{ -/// RegisterBase, -/// WithBase, -/// }, -/// Io, -/// Region, -/// }, -/// }; -/// # use kernel::io::Mmio; -/// # fn get_scratch_idx() -> usize { -/// # 0x15 -/// # } -/// -/// // Type used as parameter of `RegisterBase` to specify the base. -/// pub struct CpuCtlBase; -/// -/// // ZST describing `CPU0`. -/// struct Cpu0; -/// impl RegisterBase<CpuCtlBase> for Cpu0 { -/// const BASE: usize = 0x100; -/// } -/// -/// // ZST describing `CPU1`. -/// struct Cpu1; -/// impl RegisterBase<CpuCtlBase> for Cpu1 { -/// const BASE: usize = 0x200; -/// } -/// -/// // 64 per-cpu scratch registers, arranged as a contiguous array. -/// register! { -/// base: Region<0x1000>; -/// -/// /// Per-CPU scratch registers. -/// pub CPU_SCRATCH(u32)[64] @ CpuCtlBase + 0x00000080 { -/// 31:0 value; -/// } -/// } -/// -/// # fn test(io: Mmio<'_, Region<0x1000>>) -> Result<(), Error> { -/// // Read scratch register 0 of CPU0. -/// let scratch = io.read(CPU_SCRATCH::of::<Cpu0>().at(0)); -/// -/// // Write the retrieved value into scratch register 15 of CPU1. -/// io.write(WithBase::of::<Cpu1>().at(15), scratch); -/// -/// // This won't build. -/// // let cpu0_scratch_128 = io.read(CPU_SCRATCH::of::<Cpu0>().at(128)).value(); -/// -/// // Runtime-obtained array index. -/// let scratch_idx = get_scratch_idx(); -/// // Access on a runtime index returns an error if it is out-of-bounds. -/// let cpu0_scratch = io.read( -/// CPU_SCRATCH::of::<Cpu0>().try_at(scratch_idx).ok_or(EINVAL)? -/// ).value(); -/// # Ok(()) -/// # } -/// -/// // Alias to `SCRATCH[8]` used to convey the firmware exit code. -/// register! { -/// base: Region<0x1000>; -/// -/// /// Per-CPU firmware exit status code. -/// pub CPU_FIRMWARE_STATUS(u32) => CpuCtlBase + CPU_SCRATCH[8] { -/// 7:0 status; -/// } -/// } -/// -/// // Non-contiguous relative register arrays can be defined by adding a stride parameter. -/// // Here, each of the 16 registers of the array is separated by 8 bytes, meaning that the -/// // registers of the two declarations below are interleaved. -/// register! { -/// base: Region<0x1000>; -/// -/// /// Scratch registers bank 0. -/// pub CPU_SCRATCH_INTERLEAVED_0(u32)[16, stride = 8] @ CpuCtlBase + 0x00000d00 { -/// 31:0 value; -/// } -/// -/// /// Scratch registers bank 1. -/// pub CPU_SCRATCH_INTERLEAVED_1(u32)[16, stride = 8] @ CpuCtlBase + 0x00000d04 { -/// 31:0 value; -/// } -/// } -/// -/// # fn test2(io: Mmio<'_, Region<0x1000>>) -> Result<(), Error> { -/// let cpu0_status = io.read(CPU_FIRMWARE_STATUS::of::<Cpu0>()).status(); -/// # Ok(()) -/// # } -/// ``` #[macro_export] macro_rules! register { // Entry point for the macro, allowing multiple registers to be defined in one call. @@ -847,7 +479,7 @@ macro_rules! register { $( $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) $([ $size:expr $(, stride = $stride:expr)? ])? - $(@ $($base:ident +)? $offset:literal)? + $(@ $offset:literal)? $(=> $alias:ident $(+ $alias_offset:ident)? $([$alias_idx:expr])? )? { $($fields:tt)* } )* @@ -855,7 +487,7 @@ macro_rules! register { $( $crate::register!( @reg [$reg_base] $(#[$attr])* $vis $name ($storage) $([$size $(, stride = $stride)?])? - $(@ $($base +)? $offset)? + $(@ $offset)? $(=> $alias $(+ $alias_offset)? $([$alias_idx])? )? { $($fields)* } ); @@ -887,30 +519,6 @@ macro_rules! register { $crate::register!(@io_fixed $(#[$attr])* $vis $name($storage)); }; - // Creates a register at a relative offset from a base address provider. - ( - @reg [$reg_base:ty] - $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $base:ident + $offset:literal - { $($fields:tt)* } - ) => { - $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }); - $crate::register!(@io_base [$reg_base] $name($storage) @ $offset); - $crate::register!(@io_relative $vis $name($storage) @ $base); - }; - - // Creates an alias register of relative offset register `alias` with its own fields. - ( - @reg [$reg_base:ty] - $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $base:ident + $alias:ident - { $($fields:tt)* } - ) => { - $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }); - $crate::register!(@io_base [$reg_base] - $name($storage) @ <$alias as $crate::io::register::Register>::OFFSET - ); - $crate::register!(@io_relative $vis $name($storage) @ $base); - }; - // Creates an array of registers at a fixed offset of the MMIO space. ( @reg [$reg_base:ty] $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @@ -954,51 +562,6 @@ macro_rules! register { $crate::register!(@io_fixed $(#[$attr])* $vis $name($storage)); }; - // Creates an array of registers at a relative offset from a base address provider. - ( - @reg [$reg_base:ty] $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) - [ $size:expr, stride = $stride:expr ] - @ $base:ident + $offset:literal { $($fields:tt)* } - ) => { - $crate::build_assert::static_assert!(::core::mem::size_of::<$storage>() <= $stride); - - $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }); - $crate::register!(@io_base [$reg_base] $name($storage) @ $offset); - $crate::register!( - @io_relative_array $vis $name($storage) [ $size, stride = $stride ] @ $base + $offset - ); - }; - - // Shortcut for contiguous array of relative registers (stride == size of element). - ( - @reg [$reg_base:ty] $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ] - @ $base:ident + $offset:literal { $($fields:tt)* } - ) => { - $crate::register!(@reg [$reg_base] - $(#[$attr])* $vis $name($storage) [ $size, stride = ::core::mem::size_of::<$storage>() ] - @ $base + $offset { $($fields)* } - ); - }; - - // Creates an alias of register `idx` of relative array of registers `alias` with its own - // fields. - ( - @reg [$reg_base:ty] $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) - => $base:ident + $alias:ident [ $idx:expr ] { $($fields:tt)* } - ) => { - $crate::build_assert::static_assert!( - $idx < <$alias as $crate::io::register::RegisterArray>::SIZE - ); - - $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }); - $crate::register!( - @io_base [$reg_base] $name($storage) @ - <$alias as $crate::io::register::Register>::OFFSET + - $idx * <$alias as $crate::io::register::RegisterArray>::STRIDE - ); - $crate::register!(@io_relative $vis $name($storage) @ $base); - }; - // Generates the bitfield for the register. // // `#[allow(non_camel_case_types)]` is added since register names typically use @@ -1031,15 +594,6 @@ impl $crate::io::register::FixedRegister for $name {} $crate::io::register::FixedRegisterLoc::<$name>::new(); }; - // Implementations of relative registers. - (@io_relative $vis:vis $name:ident ($storage:ty) @ $base:ident) => { - impl $crate::io::register::WithBase for $name { - type BaseFamily = $base; - } - - impl $crate::io::register::RelativeRegister for $name {} - }; - // Implementations of register arrays. (@io_array $vis:vis $name:ident ($storage:ty) [ $size:expr, stride = $stride:expr ]) => { impl $crate::io::register::Array for $name {} @@ -1049,21 +603,4 @@ impl $crate::io::register::RegisterArray for $name { const STRIDE: usize = $stride; } }; - - // Implementations of relative array registers. - ( - @io_relative_array $vis:vis $name:ident ($storage:ty) [ $size:expr, stride = $stride:expr ] - @ $base:ident + $offset:literal - ) => { - impl $crate::io::register::WithBase for $name { - type BaseFamily = $base; - } - - impl $crate::io::register::RegisterArray for $name { - const SIZE: usize = $size; - const STRIDE: usize = $stride; - } - - impl $crate::io::register::RelativeRegisterArray for $name {} - }; } -- 2.54.0