mirror of
https://github.com/torvalds/linux.git
synced 2026-04-18 14:53:58 -04:00
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>
1261 lines
40 KiB
Rust
1261 lines
40 KiB
Rust
// 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::io::IoLoc;
|
|
|
|
use kernel::build_assert;
|
|
|
|
/// 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 T
|
|
where
|
|
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 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.
|
|
///
|
|
/// 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)* }
|
|
) => {
|
|
::kernel::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)* }
|
|
) => {
|
|
::kernel::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)* }
|
|
) => {
|
|
::kernel::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)* }
|
|
) => {
|
|
::kernel::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::register!(@bitfield_core
|
|
#[allow(non_camel_case_types)]
|
|
$(#[$attr])* $vis $name $storage
|
|
);
|
|
$crate::register!(@bitfield_fields $vis $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 {}
|
|
};
|
|
|
|
// Defines the wrapper `$name` type and its conversions from/to the storage type.
|
|
(@bitfield_core $(#[$attr:meta])* $vis:vis $name:ident $storage:ty) => {
|
|
$(#[$attr])*
|
|
#[repr(transparent)]
|
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
|
$vis struct $name {
|
|
inner: $storage,
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
impl $name {
|
|
/// Creates a bitfield from a raw value.
|
|
#[inline(always)]
|
|
$vis const fn from_raw(value: $storage) -> Self {
|
|
Self{ inner: value }
|
|
}
|
|
|
|
/// Turns this bitfield into its raw value.
|
|
///
|
|
/// This is similar to the [`From`] implementation, but is shorter to invoke in
|
|
/// most cases.
|
|
#[inline(always)]
|
|
$vis const fn into_raw(self) -> $storage {
|
|
self.inner
|
|
}
|
|
}
|
|
|
|
// SAFETY: `$storage` is `Zeroable` and `$name` is transparent.
|
|
unsafe impl ::pin_init::Zeroable for $name {}
|
|
|
|
impl ::core::convert::From<$name> for $storage {
|
|
#[inline(always)]
|
|
fn from(val: $name) -> $storage {
|
|
val.into_raw()
|
|
}
|
|
}
|
|
|
|
impl ::core::convert::From<$storage> for $name {
|
|
#[inline(always)]
|
|
fn from(val: $storage) -> $name {
|
|
Self::from_raw(val)
|
|
}
|
|
}
|
|
};
|
|
|
|
// Definitions requiring knowledge of individual fields: private and public field accessors,
|
|
// and `Debug` implementation.
|
|
(@bitfield_fields $vis:vis $name:ident $storage:ty {
|
|
$($(#[doc = $doc:expr])* $hi:literal:$lo:literal $field:ident
|
|
$(?=> $try_into_type:ty)?
|
|
$(=> $into_type:ty)?
|
|
;
|
|
)*
|
|
}
|
|
) => {
|
|
#[allow(dead_code)]
|
|
impl $name {
|
|
$(
|
|
$crate::register!(@private_field_accessors $vis $name $storage : $hi:$lo $field);
|
|
$crate::register!(
|
|
@public_field_accessors $(#[doc = $doc])* $vis $name $storage : $hi:$lo $field
|
|
$(?=> $try_into_type)?
|
|
$(=> $into_type)?
|
|
);
|
|
)*
|
|
}
|
|
|
|
$crate::register!(@debug $name { $($field;)* });
|
|
};
|
|
|
|
// Private field accessors working with the exact `Bounded` type for the field.
|
|
(
|
|
@private_field_accessors $vis:vis $name:ident $storage:ty : $hi:tt:$lo:tt $field:ident
|
|
) => {
|
|
::kernel::macros::paste!(
|
|
$vis const [<$field:upper _RANGE>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi;
|
|
$vis const [<$field:upper _MASK>]: $storage =
|
|
((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);
|
|
$vis const [<$field:upper _SHIFT>]: u32 = $lo;
|
|
);
|
|
|
|
::kernel::macros::paste!(
|
|
fn [<__ $field>](self) ->
|
|
::kernel::num::Bounded<$storage, { $hi + 1 - $lo }> {
|
|
// Left shift to align the field's MSB with the storage MSB.
|
|
const ALIGN_TOP: u32 = $storage::BITS - ($hi + 1);
|
|
// Right shift to move the top-aligned field to bit 0 of the storage.
|
|
const ALIGN_BOTTOM: u32 = ALIGN_TOP + $lo;
|
|
|
|
// Extract the field using two shifts. `Bounded::shr` produces the correctly-sized
|
|
// output type.
|
|
let val = ::kernel::num::Bounded::<$storage, { $storage::BITS }>::from(
|
|
self.inner << ALIGN_TOP
|
|
);
|
|
val.shr::<ALIGN_BOTTOM, { $hi + 1 - $lo } >()
|
|
}
|
|
|
|
const fn [<__with_ $field>](
|
|
mut self,
|
|
value: ::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>,
|
|
) -> Self
|
|
{
|
|
const MASK: $storage = <$name>::[<$field:upper _MASK>];
|
|
const SHIFT: u32 = <$name>::[<$field:upper _SHIFT>];
|
|
|
|
let value = value.get() << SHIFT;
|
|
self.inner = (self.inner & !MASK) | value;
|
|
|
|
self
|
|
}
|
|
);
|
|
};
|
|
|
|
// Public accessors for fields infallibly (`=>`) converted to a type.
|
|
(
|
|
@public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty :
|
|
$hi:literal:$lo:literal $field:ident => $into_type:ty
|
|
) => {
|
|
::kernel::macros::paste!(
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Returns the value of this field."]
|
|
#[inline(always)]
|
|
$vis fn $field(self) -> $into_type
|
|
{
|
|
self.[<__ $field>]().into()
|
|
}
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Sets this field to the given `value`."]
|
|
#[inline(always)]
|
|
$vis fn [<with_ $field>](self, value: $into_type) -> Self
|
|
{
|
|
self.[<__with_ $field>](value.into())
|
|
}
|
|
|
|
);
|
|
};
|
|
|
|
// Public accessors for fields fallibly (`?=>`) converted to a type.
|
|
(
|
|
@public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty :
|
|
$hi:tt:$lo:tt $field:ident ?=> $try_into_type:ty
|
|
) => {
|
|
::kernel::macros::paste!(
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Returns the value of this field."]
|
|
#[inline(always)]
|
|
$vis fn $field(self) ->
|
|
Result<
|
|
$try_into_type,
|
|
<$try_into_type as ::core::convert::TryFrom<
|
|
::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>
|
|
>>::Error
|
|
>
|
|
{
|
|
self.[<__ $field>]().try_into()
|
|
}
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Sets this field to the given `value`."]
|
|
#[inline(always)]
|
|
$vis fn [<with_ $field>](self, value: $try_into_type) -> Self
|
|
{
|
|
self.[<__with_ $field>](value.into())
|
|
}
|
|
|
|
);
|
|
};
|
|
|
|
// Public accessors for fields not converted to a type.
|
|
(
|
|
@public_field_accessors $(#[doc = $doc:expr])* $vis:vis $name:ident $storage:ty :
|
|
$hi:tt:$lo:tt $field:ident
|
|
) => {
|
|
::kernel::macros::paste!(
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Returns the value of this field."]
|
|
#[inline(always)]
|
|
$vis fn $field(self) ->
|
|
::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>
|
|
{
|
|
self.[<__ $field>]()
|
|
}
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Sets this field to the compile-time constant `VALUE`."]
|
|
#[inline(always)]
|
|
$vis const fn [<with_const_ $field>]<const VALUE: $storage>(self) -> Self {
|
|
self.[<__with_ $field>](
|
|
::kernel::num::Bounded::<$storage, { $hi + 1 - $lo }>::new::<VALUE>()
|
|
)
|
|
}
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Sets this field to the given `value`."]
|
|
#[inline(always)]
|
|
$vis fn [<with_ $field>]<T>(
|
|
self,
|
|
value: T,
|
|
) -> Self
|
|
where T: Into<::kernel::num::Bounded<$storage, { $hi + 1 - $lo }>>,
|
|
{
|
|
self.[<__with_ $field>](value.into())
|
|
}
|
|
|
|
$(#[doc = $doc])*
|
|
#[doc = "Tries to set this field to `value`, returning an error if it is out of range."]
|
|
#[inline(always)]
|
|
$vis fn [<try_with_ $field>]<T>(
|
|
self,
|
|
value: T,
|
|
) -> ::kernel::error::Result<Self>
|
|
where T: ::kernel::num::TryIntoBounded<$storage, { $hi + 1 - $lo }>,
|
|
{
|
|
Ok(
|
|
self.[<__with_ $field>](
|
|
value.try_into_bounded().ok_or(::kernel::error::code::EOVERFLOW)?
|
|
)
|
|
)
|
|
}
|
|
|
|
);
|
|
};
|
|
|
|
// `Debug` implementation.
|
|
(@debug $name:ident { $($field:ident;)* }) => {
|
|
impl ::kernel::fmt::Debug for $name {
|
|
fn fmt(&self, f: &mut ::kernel::fmt::Formatter<'_>) -> ::kernel::fmt::Result {
|
|
f.debug_struct(stringify!($name))
|
|
.field("<raw>", &::kernel::prelude::fmt!("{:#x}", self.inner))
|
|
$(
|
|
.field(stringify!($field), &self.$field())
|
|
)*
|
|
.finish()
|
|
}
|
|
}
|
|
};
|
|
}
|