Something went wrong. Try again.
Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024// SPDX-License-Identifier: GPL-2.0
//! Macro to define register layout and accessors.//!//! The [`register!`](kernel::io::register!) macro provides an intuitive and readable syntax for//! defining a dedicated type for each register and accessing it using [`Io`](super::Io). Each such//! type comes with its own field accessors that can return an error if a field's value is invalid.//!//! Note: most of the items in this module are public so they can be referenced by the macro, but//! most are not to be used directly by users. Outside of the `register!` macro itself, the only//! items you might want to import from this module are [`WithBase`] and [`Array`].//!//! # Simple example//!//! ```no_run//! use kernel::io::register;//!//! register! {//! /// Basic information about the chip.//! pub BOOT_0(u32) @ 0x00000100 {//! /// Vendor ID.//! 15:8 vendor_id;//! /// Major revision of the chip.//! 7:4 major_revision;//! /// Minor revision of the chip.//! 3:0 minor_revision;//! }//! }//! ```//!//! This defines a 32-bit `BOOT_0` type which can be read from or written to offset `0x100` of an//! `Io` region, with the described bitfields. For instance, `minor_revision` consists of the 4//! least significant bits of the type.//!//! Fields are instances of [`Bounded`](kernel::num::Bounded) and can be read by calling their//! getter method, which is named after them. They also have setter methods prefixed with `with_`//! for runtime values and `with_const_` for constant values. All setters return the updated//! register value.//!//! Fields can also be transparently converted from/to an arbitrary type by using the `=>` and//! `?=>` syntaxes.//!//! If present, doc comments above register or fields definitions are added to the relevant item//! they document (the register type itself, or the field's setter and getter methods).//!//! Note that multiple registers can be defined in a single `register!` invocation. This can be//! useful to group related registers together.//!//! Here is how the register defined above can be used in code://!//!//! ```no_run//! use kernel::{//! io::{//! register,//! Io,//! IoLoc,//! },//! num::Bounded,//! };//! # use kernel::io::Mmio;//! # register! {//! # pub BOOT_0(u32) @ 0x00000100 {//! # 15:8 vendor_id;//! # 7:4 major_revision;//! # 3:0 minor_revision;//! # }//! # }//! # fn test(io: &Mmio<0x1000>) {//! # fn obtain_vendor_id() -> u8 { 0xff }//!//! // Read from the register's defined offset (0x100).//! let boot0 = io.read(BOOT_0);//! pr_info!("chip revision: {}.{}", boot0.major_revision().get(), boot0.minor_revision().get());//!//! // Update some fields and write the new value back.//! let new_boot0 = boot0//! // Constant values.//! .with_const_major_revision::<3>()//! .with_const_minor_revision::<10>()//! // Runtime value.//! .with_vendor_id(obtain_vendor_id());//! io.write_reg(new_boot0);//!//! // Or, build a new value from zero and write it://! io.write_reg(BOOT_0::zeroed()//! .with_const_major_revision::<3>()//! .with_const_minor_revision::<10>()//! .with_vendor_id(obtain_vendor_id())//! );//!//! // Or, read and update the register in a single step.//! io.update(BOOT_0, |r| r//! .with_const_major_revision::<3>()//! .with_const_minor_revision::<10>()//! .with_vendor_id(obtain_vendor_id())//! );//!//! // Constant values can also be built using the const setters.//! const V: BOOT_0 = pin_init::zeroed::<BOOT_0>()//! .with_const_major_revision::<3>()//! .with_const_minor_revision::<10>();//! # }//! ```//!//! For more extensive documentation about how to define registers, see the//! [`register!`](kernel::io::register!) macro.
use core::marker::PhantomData;
use crate::{ build_assert::build_assert, io::IoLoc, //};
/// Trait implemented by all registers.pub trait Register: Sized { /// Backing primitive type of the register. type Storage: Into<Self> + From<Self>;
/// Start offset of the register. /// /// The interpretation of this offset depends on the type of the register. const OFFSET: usize;}
/// Trait implemented by registers with a fixed offset.pub trait FixedRegister: Register {}
/// Allows `()` to be used as the `location` parameter of [`Io::write`](super::Io::write) when/// passing a [`FixedRegister`] value.impl<T> IoLoc<T> for ()where T: FixedRegister,{ type IoType = T::Storage;
#[inline(always)] fn offset(self) -> usize { T::OFFSET }}
/// A [`FixedRegister`] carries its location in its type. Thus `FixedRegister` values can be used/// as an [`IoLoc`].impl<T> IoLoc<T> for Twhere T: FixedRegister,{ type IoType = T::Storage;
#[inline(always)] fn offset(self) -> usize { T::OFFSET }}
/// Location of a fixed register.pub struct FixedRegisterLoc<T: FixedRegister>(PhantomData<T>);
impl<T: FixedRegister> FixedRegisterLoc<T> { /// Returns the location of `T`. #[inline(always)] // We do not implement `Default` so we can be const. #[expect(clippy::new_without_default)] pub const fn new() -> Self { Self(PhantomData) }}
impl<T> IoLoc<T> for FixedRegisterLoc<T>where T: FixedRegister,{ type IoType = T::Storage;
#[inline(always)] fn offset(self) -> usize { T::OFFSET }}
/// 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<T, B> IoLoc<T> for RelativeRegisterLoc<T, B>where T: RelativeRegister, 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. const SIZE: usize; /// Number of bytes between the start of elements in the registers array. const STRIDE: usize;}
/// Location of an array register.pub struct RegisterArrayLoc<T: RegisterArray>(usize, PhantomData<T>);
impl<T: RegisterArray> RegisterArrayLoc<T> { /// Returns the location of register `T` at position `idx`, with build-time validation. #[inline(always)] pub fn new(idx: usize) -> Self { build_assert!(idx < T::SIZE);
Self(idx, PhantomData) }
/// Attempts to return the location of register `T` at position `idx`, with runtime validation. #[inline(always)] pub fn try_new(idx: usize) -> Option<Self> { if idx < T::SIZE { Some(Self(idx, PhantomData)) } else { None } }}
impl<T> IoLoc<T> for RegisterArrayLoc<T>where T: RegisterArray,{ type IoType = T::Storage;
#[inline(always)] fn offset(self) -> usize { T::OFFSET + self.0 * T::STRIDE }}
/// Trait providing location builders for [`RegisterArray`]s.pub trait Array { /// Returns the location of the register at position `idx`, with build-time validation. #[inline(always)] fn at(idx: usize) -> RegisterArrayLoc<Self> where Self: RegisterArray, { RegisterArrayLoc::new(idx) }
/// Returns the location of the register at position `idx`, with runtime validation. #[inline(always)] fn try_at(idx: usize) -> Option<RegisterArrayLoc<Self>> where Self: RegisterArray, { RegisterArrayLoc::try_new(idx) }}
/// 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<T, B> IoLoc<T> for RelativeRegisterArrayLoc<T, B>where T: RelativeRegisterArray, 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.////// 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 Twhere 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.////// 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.////// ## Fixed offset registers////// These are the simplest kind of registers. Their location is simply an offset inside the I/O/// region. For instance:////// ```ignore/// register! {/// pub FIXED_REG(u16) @ 0x80 {/// .../// }/// }/// ```////// This creates a 16-bit register named `FIXED_REG` located at offset `0x80` of an I/O region.////// These registers' location can be built simply by referencing their name:////// ```no_run/// use kernel::{/// io::{/// register,/// Io,/// },/// };/// # use kernel::io::Mmio;////// register! {/// FIXED_REG(u32) @ 0x100 {/// 16:8 high_byte;/// 7:0 low_byte;/// }/// }////// # fn test(io: &Mmio<0x1000>) {/// let val = io.read(FIXED_REG);////// // Write from an already-existing value./// io.write(FIXED_REG, val.with_low_byte(0xff));////// // Create a register value from scratch./// let val2 = FIXED_REG::zeroed().with_high_byte(0x80);////// // 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);/// # }////// ```////// It is possible to create an alias of an existing register with new field definitions by using/// the `=> ALIAS` syntax. This is useful for cases where a register's interpretation depends on/// the context:////// ```no_run/// use kernel::io::register;////// register! {/// /// Scratch register./// pub SCRATCH(u32) @ 0x00000200 {/// 31:0 value;/// }////// /// Boot status of the firmware./// pub SCRATCH_BOOT_STATUS(u32) => SCRATCH {/// 0:0 completed;/// }/// }/// ```////// 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,/// },/// };/// # 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! {/// /// CPU core control./// pub CPU_CTL(u32) @ CpuCtlBase + 0x10 {/// 0:0 start;/// }/// }////// # fn test(io: Mmio<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! {/// /// 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<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/// be defined as an array of identical registers, allowing them to be accessed by index with/// compile-time or runtime bound checking:////// ```ignore/// register! {/// pub REGISTER_ARRAY(u8)[10, stride = 4] @ 0x100 {/// .../// }/// }/// ```////// This defines `REGISTER_ARRAY`, an array of 10 byte registers starting at offset `0x100`. Each/// register is separated from its neighbor by 4 bytes.////// The `stride` parameter is optional; if unspecified, the registers are placed consecutively from/// each other.////// A location for a register in a register array is built using the [`Array::at`] trait method./// All arrays of registers implement [`Array`].////// ```no_run/// use kernel::{/// io::{/// register,/// register::Array,/// Io,/// },/// };/// # use kernel::io::Mmio;/// # fn get_scratch_idx() -> usize {/// # 0x15/// # }////// // Array of 64 consecutive registers with the same layout starting at offset `0x80`./// register! {/// /// Scratch registers./// pub SCRATCH(u32)[64] @ 0x00000080 {/// 31:0 value;/// }/// }////// # fn test(io: &Mmio<0x1000>)/// # -> Result<(), Error>{/// // Read scratch register 0, i.e. I/O address `0x80`./// let scratch_0 = io.read(SCRATCH::at(0)).value();////// // Write scratch register 15, i.e. I/O address `0x80 + (15 * 4)`./// io.write(Array::at(15), SCRATCH::from(0xffeeaabb));////// // This is out of bounds and won't build./// // let scratch_128 = io.read(SCRATCH::at(128)).value();////// // Runtime-obtained array index./// let idx = get_scratch_idx();/// // Access on a runtime index returns an error if it is out-of-bounds./// let some_scratch = io.read(SCRATCH::try_at(idx).ok_or(EINVAL)?).value();////// // Alias to a specific register in an array./// // Here `SCRATCH[8]` is used to convey the firmware exit code./// register! {/// /// Firmware exit status code./// pub FIRMWARE_STATUS(u32) => SCRATCH[8] {/// 7:0 status;/// }/// }////// let status = io.read(FIRMWARE_STATUS).status();////// // Non-contiguous 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! {/// /// Scratch registers bank 0./// pub SCRATCH_INTERLEAVED_0(u32)[16, stride = 8] @ 0x000000c0 {/// 31:0 value;/// }////// /// Scratch registers bank 1./// pub SCRATCH_INTERLEAVED_1(u32)[16, stride = 8] @ 0x000000c4 {/// 31:0 value;/// }/// }/// # 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,/// },/// };/// # 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! {/// /// Per-CPU scratch registers./// pub CPU_SCRATCH(u32)[64] @ CpuCtlBase + 0x00000080 {/// 31:0 value;/// }/// }////// # fn test(io: &Mmio<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! {/// /// 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! {/// /// 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<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. // It matches all possible register declaration patterns to dispatch them to corresponding // `@reg` rule that defines a single register. ( $( $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) $([ $size:expr $(, stride = $stride:expr)? ])? $(@ $($base:ident +)? $offset:literal)? $(=> $alias:ident $(+ $alias_offset:ident)? $([$alias_idx:expr])? )? { $($fields:tt)* } )* ) => { $( $crate::register!( @reg $(#[$attr])* $vis $name ($storage) $([$size $(, stride = $stride)?])? $(@ $($base +)? $offset)? $(=> $alias $(+ $alias_offset)? $([$alias_idx])? )? { $($fields)* } ); )* };
// All the rules below are private helpers.
// Creates a register at a fixed offset of the MMIO space. ( @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $offset:literal { $($fields:tt)* } ) => { $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }); $crate::register!(@io_base $name($storage) @ $offset); $crate::register!(@io_fixed $(#[$attr])* $vis $name($storage)); };
// Creates an alias register of fixed offset register `alias` with its own fields. ( @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:ident { $($fields:tt)* } ) => { $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }); $crate::register!( @io_base $name($storage) @ <$alias as $crate::io::register::Register>::OFFSET ); $crate::register!(@io_fixed $(#[$attr])* $vis $name($storage)); };
// Creates a register at a relative offset from a base address provider. ( @reg $(#[$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 $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 $(#[$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 $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 $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr, stride = $stride:expr ] @ $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 $name($storage) @ $offset); $crate::register!(@io_array $vis $name($storage) [ $size, stride = $stride ]); };
// Shortcut for contiguous array of registers (stride == size of element). ( @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ] @ $offset:literal { $($fields:tt)* } ) => { $crate::register!( $(#[$attr])* $vis $name($storage) [ $size, stride = ::core::mem::size_of::<$storage>() ] @ $offset { $($fields)* } ); };
// Creates an alias of register `idx` of array of registers `alias` with its own fields. ( @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $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 $name($storage) @ <$alias as $crate::io::register::Register>::OFFSET + $idx * <$alias as $crate::io::register::RegisterArray>::STRIDE ); $crate::register!(@io_fixed $(#[$attr])* $vis $name($storage)); };
// Creates an array of registers at a relative offset from a base address provider. ( @reg $(#[$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 $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 $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ] @ $base:ident + $offset:literal { $($fields:tt)* } ) => { $crate::register!( $(#[$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 $(#[$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 $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 // `SCREAMING_CASE`. ( @bitfield $(#[$attr:meta])* $vis:vis struct $name:ident($storage:ty) { $($fields:tt)* } ) => { $crate::bitfield!( #[allow(non_camel_case_types)] $(#[$attr])* $vis struct $name($storage) { $($fields)* } ); };
// Implementations shared by all registers types. (@io_base $name:ident($storage:ty) @ $offset:expr) => { impl $crate::io::register::Register for $name { type Storage = $storage;
const OFFSET: usize = $offset; } };
// Implementations of fixed registers. (@io_fixed $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)) => { impl $crate::io::register::FixedRegister for $name {}
$(#[$attr])* $vis const $name: $crate::io::register::FixedRegisterLoc<$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 {}
impl $crate::io::register::RegisterArray for $name { const SIZE: usize = $size; 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 {} };}