// SPDX-License-Identifier: GPL-2.012//! `register!` macro to define register layout and accessors.3//!4//! A single register typically includes several fields, which are accessed through a combination5//! of bit-shift and mask operations that introduce a class of potential mistakes, notably because6//! not all possible field values are necessarily valid.7//!8//! The `register!` macro in this module provides an intuitive and readable syntax for defining a9//! dedicated type for each register. Each such type comes with its own field accessors that can10//! return an error if a field's value is invalid. Please look at the [`bitfield`] macro for the11//! complete syntax of fields definitions.1213/// Trait providing a base address to be added to the offset of a relative register to obtain14/// its actual offset.15///16/// The `T` generic argument is used to distinguish which base to use, in case a type provides17/// several bases. It is given to the `register!` macro to restrict the use of the register to18/// implementors of this particular variant.19pub(crate) trait RegisterBase<T> {20const BASE: usize;21}2223/// Defines a dedicated type for a register with an absolute offset, including getter and setter24/// methods for its fields and methods to read and write it from an `Io` region.25///26/// Example:27///28/// ```no_run29/// register!(BOOT_0 @ 0x00000100, "Basic revision information about the GPU" {30/// 3:0 minor_revision as u8, "Minor revision of the chip";31/// 7:4 major_revision as u8, "Major revision of the chip";32/// 28:20 chipset as u32 ?=> Chipset, "Chipset model";33/// });34/// ```35///36/// This defines a `BOOT_0` type which can be read or written from offset `0x100` of an `Io`37/// region. It is composed of 3 fields, for instance `minor_revision` is made of the 4 least38/// significant bits of the register. Each field can be accessed and modified using accessor39/// methods:40///41/// ```no_run42/// // Read from the register's defined offset (0x100).43/// let boot0 = BOOT_0::read(&bar);44/// pr_info!("chip revision: {}.{}", boot0.major_revision(), boot0.minor_revision());45///46/// // `Chipset::try_from` is called with the value of the `chipset` field and returns an47/// // error if it is invalid.48/// let chipset = boot0.chipset()?;49///50/// // Update some fields and write the value back.51/// boot0.set_major_revision(3).set_minor_revision(10).write(&bar);52///53/// // Or, just read and update the register in a single step:54/// BOOT_0::update(&bar, |r| r.set_major_revision(3).set_minor_revision(10));55/// ```56///57/// The documentation strings are optional. If present, they will be added to the type's58/// definition, or the field getter and setter methods they are attached to.59///60/// It is also possible to create a alias register by using the `=> ALIAS` syntax. This is useful61/// for cases where a register's interpretation depends on the context:62///63/// ```no_run64/// register!(SCRATCH @ 0x00000200, "Scratch register" {65/// 31:0 value as u32, "Raw value";66/// });67///68/// register!(SCRATCH_BOOT_STATUS => SCRATCH, "Boot status of the firmware" {69/// 0:0 completed as bool, "Whether the firmware has completed booting";70/// });71/// ```72///73/// In this example, `SCRATCH_0_BOOT_STATUS` uses the same I/O address as `SCRATCH`, while also74/// providing its own `completed` field.75///76/// ## Relative registers77///78/// A register can be defined as being accessible from a fixed offset of a provided base. For79/// instance, imagine the following I/O space:80///81/// ```text82/// +-----------------------------+83/// | ... |84/// | |85/// 0x100--->+------------CPU0-------------+86/// | |87/// 0x110--->+-----------------------------+88/// | CPU_CTL |89/// +-----------------------------+90/// | ... |91/// | |92/// | |93/// 0x200--->+------------CPU1-------------+94/// | |95/// 0x210--->+-----------------------------+96/// | CPU_CTL |97/// +-----------------------------+98/// | ... |99/// +-----------------------------+100/// ```101///102/// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset `0x10` of their I/O103/// space segment. Since both instances of `CPU_CTL` share the same layout, we don't want to define104/// them twice and would prefer a way to select which one to use from a single definition105///106/// This can be done using the `Base[Offset]` syntax when specifying the register's address.107///108/// `Base` is an arbitrary type (typically a ZST) to be used as a generic parameter of the109/// [`RegisterBase`] trait to provide the base as a constant, i.e. each type providing a base for110/// this register needs to implement `RegisterBase<Base>`. Here is the above example translated111/// into code:112///113/// ```no_run114/// // Type used to identify the base.115/// pub(crate) struct CpuCtlBase;116///117/// // ZST describing `CPU0`.118/// struct Cpu0;119/// impl RegisterBase<CpuCtlBase> for Cpu0 {120/// const BASE: usize = 0x100;121/// }122/// // Singleton of `CPU0` used to identify it.123/// const CPU0: Cpu0 = Cpu0;124///125/// // ZST describing `CPU1`.126/// struct Cpu1;127/// impl RegisterBase<CpuCtlBase> for Cpu1 {128/// const BASE: usize = 0x200;129/// }130/// // Singleton of `CPU1` used to identify it.131/// const CPU1: Cpu1 = Cpu1;132///133/// // This makes `CPU_CTL` accessible from all implementors of `RegisterBase<CpuCtlBase>`.134/// register!(CPU_CTL @ CpuCtlBase[0x10], "CPU core control" {135/// 0:0 start as bool, "Start the CPU core";136/// });137///138/// // The `read`, `write` and `update` methods of relative registers take an extra `base` argument139/// // that is used to resolve its final address by adding its `BASE` to the offset of the140/// // register.141///142/// // Start `CPU0`.143/// CPU_CTL::update(bar, &CPU0, |r| r.set_start(true));144///145/// // Start `CPU1`.146/// CPU_CTL::update(bar, &CPU1, |r| r.set_start(true));147///148/// // Aliases can also be defined for relative register.149/// register!(CPU_CTL_ALIAS => CpuCtlBase[CPU_CTL], "Alias to CPU core control" {150/// 1:1 alias_start as bool, "Start the aliased CPU core";151/// });152///153/// // Start the aliased `CPU0`.154/// CPU_CTL_ALIAS::update(bar, &CPU0, |r| r.set_alias_start(true));155/// ```156///157/// ## Arrays of registers158///159/// Some I/O areas contain consecutive values that can be interpreted in the same way. These areas160/// can be defined as an array of identical registers, allowing them to be accessed by index with161/// compile-time or runtime bound checking. Simply define their address as `Address[Size]`, and add162/// an `idx` parameter to their `read`, `write` and `update` methods:163///164/// ```no_run165/// # fn no_run() -> Result<(), Error> {166/// # fn get_scratch_idx() -> usize {167/// # 0x15168/// # }169/// // Array of 64 consecutive registers with the same layout starting at offset `0x80`.170/// register!(SCRATCH @ 0x00000080[64], "Scratch registers" {171/// 31:0 value as u32;172/// });173///174/// // Read scratch register 0, i.e. I/O address `0x80`.175/// let scratch_0 = SCRATCH::read(bar, 0).value();176/// // Read scratch register 15, i.e. I/O address `0x80 + (15 * 4)`.177/// let scratch_15 = SCRATCH::read(bar, 15).value();178///179/// // This is out of bounds and won't build.180/// // let scratch_128 = SCRATCH::read(bar, 128).value();181///182/// // Runtime-obtained array index.183/// let scratch_idx = get_scratch_idx();184/// // Access on a runtime index returns an error if it is out-of-bounds.185/// let some_scratch = SCRATCH::try_read(bar, scratch_idx)?.value();186///187/// // Alias to a particular register in an array.188/// // Here `SCRATCH[8]` is used to convey the firmware exit code.189/// register!(FIRMWARE_STATUS => SCRATCH[8], "Firmware exit status code" {190/// 7:0 status as u8;191/// });192///193/// let status = FIRMWARE_STATUS::read(bar).status();194///195/// // Non-contiguous register arrays can be defined by adding a stride parameter.196/// // Here, each of the 16 registers of the array are separated by 8 bytes, meaning that the197/// // registers of the two declarations below are interleaved.198/// register!(SCRATCH_INTERLEAVED_0 @ 0x000000c0[16 ; 8], "Scratch registers bank 0" {199/// 31:0 value as u32;200/// });201/// register!(SCRATCH_INTERLEAVED_1 @ 0x000000c4[16 ; 8], "Scratch registers bank 1" {202/// 31:0 value as u32;203/// });204/// # Ok(())205/// # }206/// ```207///208/// ## Relative arrays of registers209///210/// Combining the two features described in the sections above, arrays of registers accessible from211/// a base can also be defined:212///213/// ```no_run214/// # fn no_run() -> Result<(), Error> {215/// # fn get_scratch_idx() -> usize {216/// # 0x15217/// # }218/// // Type used as parameter of `RegisterBase` to specify the base.219/// pub(crate) struct CpuCtlBase;220///221/// // ZST describing `CPU0`.222/// struct Cpu0;223/// impl RegisterBase<CpuCtlBase> for Cpu0 {224/// const BASE: usize = 0x100;225/// }226/// // Singleton of `CPU0` used to identify it.227/// const CPU0: Cpu0 = Cpu0;228///229/// // ZST describing `CPU1`.230/// struct Cpu1;231/// impl RegisterBase<CpuCtlBase> for Cpu1 {232/// const BASE: usize = 0x200;233/// }234/// // Singleton of `CPU1` used to identify it.235/// const CPU1: Cpu1 = Cpu1;236///237/// // 64 per-cpu scratch registers, arranged as an contiguous array.238/// register!(CPU_SCRATCH @ CpuCtlBase[0x00000080[64]], "Per-CPU scratch registers" {239/// 31:0 value as u32;240/// });241///242/// let cpu0_scratch_0 = CPU_SCRATCH::read(bar, &Cpu0, 0).value();243/// let cpu1_scratch_15 = CPU_SCRATCH::read(bar, &Cpu1, 15).value();244///245/// // This won't build.246/// // let cpu0_scratch_128 = CPU_SCRATCH::read(bar, &Cpu0, 128).value();247///248/// // Runtime-obtained array index.249/// let scratch_idx = get_scratch_idx();250/// // Access on a runtime value returns an error if it is out-of-bounds.251/// let cpu0_some_scratch = CPU_SCRATCH::try_read(bar, &Cpu0, scratch_idx)?.value();252///253/// // `SCRATCH[8]` is used to convey the firmware exit code.254/// register!(CPU_FIRMWARE_STATUS => CpuCtlBase[CPU_SCRATCH[8]],255/// "Per-CPU firmware exit status code" {256/// 7:0 status as u8;257/// });258///259/// let cpu0_status = CPU_FIRMWARE_STATUS::read(bar, &Cpu0).status();260///261/// // Non-contiguous register arrays can be defined by adding a stride parameter.262/// // Here, each of the 16 registers of the array are separated by 8 bytes, meaning that the263/// // registers of the two declarations below are interleaved.264/// register!(CPU_SCRATCH_INTERLEAVED_0 @ CpuCtlBase[0x00000d00[16 ; 8]],265/// "Scratch registers bank 0" {266/// 31:0 value as u32;267/// });268/// register!(CPU_SCRATCH_INTERLEAVED_1 @ CpuCtlBase[0x00000d04[16 ; 8]],269/// "Scratch registers bank 1" {270/// 31:0 value as u32;271/// });272/// # Ok(())273/// # }274/// ```275macro_rules! register {276// Creates a register at a fixed offset of the MMIO space.277($name:ident @ $offset:literal $(, $comment:literal)? { $($fields:tt)* } ) => {278bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );279register!(@io_fixed $name @ $offset);280};281282// Creates an alias register of fixed offset register `alias` with its own fields.283($name:ident => $alias:ident $(, $comment:literal)? { $($fields:tt)* } ) => {284bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );285register!(@io_fixed $name @ $alias::OFFSET);286};287288// Creates a register at a relative offset from a base address provider.289($name:ident @ $base:ty [ $offset:literal ] $(, $comment:literal)? { $($fields:tt)* } ) => {290bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );291register!(@io_relative $name @ $base [ $offset ]);292};293294// Creates an alias register of relative offset register `alias` with its own fields.295($name:ident => $base:ty [ $alias:ident ] $(, $comment:literal)? { $($fields:tt)* }) => {296bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );297register!(@io_relative $name @ $base [ $alias::OFFSET ]);298};299300// Creates an array of registers at a fixed offset of the MMIO space.301(302$name:ident @ $offset:literal [ $size:expr ; $stride:expr ] $(, $comment:literal)? {303$($fields:tt)*304}305) => {306static_assert!(::core::mem::size_of::<u32>() <= $stride);307bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );308register!(@io_array $name @ $offset [ $size ; $stride ]);309};310311// Shortcut for contiguous array of registers (stride == size of element).312(313$name:ident @ $offset:literal [ $size:expr ] $(, $comment:literal)? {314$($fields:tt)*315}316) => {317register!($name @ $offset [ $size ; ::core::mem::size_of::<u32>() ] $(, $comment)? {318$($fields)*319} );320};321322// Creates an array of registers at a relative offset from a base address provider.323(324$name:ident @ $base:ty [ $offset:literal [ $size:expr ; $stride:expr ] ]325$(, $comment:literal)? { $($fields:tt)* }326) => {327static_assert!(::core::mem::size_of::<u32>() <= $stride);328bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );329register!(@io_relative_array $name @ $base [ $offset [ $size ; $stride ] ]);330};331332// Shortcut for contiguous array of relative registers (stride == size of element).333(334$name:ident @ $base:ty [ $offset:literal [ $size:expr ] ] $(, $comment:literal)? {335$($fields:tt)*336}337) => {338register!($name @ $base [ $offset [ $size ; ::core::mem::size_of::<u32>() ] ]339$(, $comment)? { $($fields)* } );340};341342// Creates an alias of register `idx` of relative array of registers `alias` with its own343// fields.344(345$name:ident => $base:ty [ $alias:ident [ $idx:expr ] ] $(, $comment:literal)? {346$($fields:tt)*347}348) => {349static_assert!($idx < $alias::SIZE);350bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );351register!(@io_relative $name @ $base [ $alias::OFFSET + $idx * $alias::STRIDE ] );352};353354// Creates an alias of register `idx` of array of registers `alias` with its own fields.355// This rule belongs to the (non-relative) register arrays set, but needs to be put last356// to avoid it being interpreted in place of the relative register array alias rule.357($name:ident => $alias:ident [ $idx:expr ] $(, $comment:literal)? { $($fields:tt)* }) => {358static_assert!($idx < $alias::SIZE);359bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } );360register!(@io_fixed $name @ $alias::OFFSET + $idx * $alias::STRIDE );361};362363// Generates the IO accessors for a fixed offset register.364(@io_fixed $name:ident @ $offset:expr) => {365#[allow(dead_code)]366impl $name {367pub(crate) const OFFSET: usize = $offset;368369/// Read the register from its address in `io`.370#[inline(always)]371pub(crate) fn read<const SIZE: usize, T>(io: &T) -> Self where372T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,373{374Self(io.read32($offset))375}376377/// Write the value contained in `self` to the register address in `io`.378#[inline(always)]379pub(crate) fn write<const SIZE: usize, T>(self, io: &T) where380T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,381{382io.write32(self.0, $offset)383}384385/// Read the register from its address in `io` and run `f` on its value to obtain a new386/// value to write back.387#[inline(always)]388pub(crate) fn update<const SIZE: usize, T, F>(389io: &T,390f: F,391) where392T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,393F: ::core::ops::FnOnce(Self) -> Self,394{395let reg = f(Self::read(io));396reg.write(io);397}398}399};400401// Generates the IO accessors for a relative offset register.402(@io_relative $name:ident @ $base:ty [ $offset:expr ]) => {403#[allow(dead_code)]404impl $name {405pub(crate) const OFFSET: usize = $offset;406407/// Read the register from `io`, using the base address provided by `base` and adding408/// the register's offset to it.409#[inline(always)]410pub(crate) fn read<const SIZE: usize, T, B>(411io: &T,412#[allow(unused_variables)]413base: &B,414) -> Self where415T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,416B: crate::regs::macros::RegisterBase<$base>,417{418const OFFSET: usize = $name::OFFSET;419420let value = io.read32(421<B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET422);423424Self(value)425}426427/// Write the value contained in `self` to `io`, using the base address provided by428/// `base` and adding the register's offset to it.429#[inline(always)]430pub(crate) fn write<const SIZE: usize, T, B>(431self,432io: &T,433#[allow(unused_variables)]434base: &B,435) where436T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,437B: crate::regs::macros::RegisterBase<$base>,438{439const OFFSET: usize = $name::OFFSET;440441io.write32(442self.0,443<B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET444);445}446447/// Read the register from `io`, using the base address provided by `base` and adding448/// the register's offset to it, then run `f` on its value to obtain a new value to449/// write back.450#[inline(always)]451pub(crate) fn update<const SIZE: usize, T, B, F>(452io: &T,453base: &B,454f: F,455) where456T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,457B: crate::regs::macros::RegisterBase<$base>,458F: ::core::ops::FnOnce(Self) -> Self,459{460let reg = f(Self::read(io, base));461reg.write(io, base);462}463}464};465466// Generates the IO accessors for an array of registers.467(@io_array $name:ident @ $offset:literal [ $size:expr ; $stride:expr ]) => {468#[allow(dead_code)]469impl $name {470pub(crate) const OFFSET: usize = $offset;471pub(crate) const SIZE: usize = $size;472pub(crate) const STRIDE: usize = $stride;473474/// Read the array register at index `idx` from its address in `io`.475#[inline(always)]476pub(crate) fn read<const SIZE: usize, T>(477io: &T,478idx: usize,479) -> Self where480T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,481{482build_assert!(idx < Self::SIZE);483484let offset = Self::OFFSET + (idx * Self::STRIDE);485let value = io.read32(offset);486487Self(value)488}489490/// Write the value contained in `self` to the array register with index `idx` in `io`.491#[inline(always)]492pub(crate) fn write<const SIZE: usize, T>(493self,494io: &T,495idx: usize496) where497T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,498{499build_assert!(idx < Self::SIZE);500501let offset = Self::OFFSET + (idx * Self::STRIDE);502503io.write32(self.0, offset);504}505506/// Read the array register at index `idx` in `io` and run `f` on its value to obtain a507/// new value to write back.508#[inline(always)]509pub(crate) fn update<const SIZE: usize, T, F>(510io: &T,511idx: usize,512f: F,513) where514T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,515F: ::core::ops::FnOnce(Self) -> Self,516{517let reg = f(Self::read(io, idx));518reg.write(io, idx);519}520521/// Read the array register at index `idx` from its address in `io`.522///523/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the524/// access was out-of-bounds.525#[inline(always)]526pub(crate) fn try_read<const SIZE: usize, T>(527io: &T,528idx: usize,529) -> ::kernel::error::Result<Self> where530T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,531{532if idx < Self::SIZE {533Ok(Self::read(io, idx))534} else {535Err(EINVAL)536}537}538539/// Write the value contained in `self` to the array register with index `idx` in `io`.540///541/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the542/// access was out-of-bounds.543#[inline(always)]544pub(crate) fn try_write<const SIZE: usize, T>(545self,546io: &T,547idx: usize,548) -> ::kernel::error::Result where549T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,550{551if idx < Self::SIZE {552Ok(self.write(io, idx))553} else {554Err(EINVAL)555}556}557558/// Read the array register at index `idx` in `io` and run `f` on its value to obtain a559/// new value to write back.560///561/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the562/// access was out-of-bounds.563#[inline(always)]564pub(crate) fn try_update<const SIZE: usize, T, F>(565io: &T,566idx: usize,567f: F,568) -> ::kernel::error::Result where569T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,570F: ::core::ops::FnOnce(Self) -> Self,571{572if idx < Self::SIZE {573Ok(Self::update(io, idx, f))574} else {575Err(EINVAL)576}577}578}579};580581// Generates the IO accessors for an array of relative registers.582(583@io_relative_array $name:ident @ $base:ty584[ $offset:literal [ $size:expr ; $stride:expr ] ]585) => {586#[allow(dead_code)]587impl $name {588pub(crate) const OFFSET: usize = $offset;589pub(crate) const SIZE: usize = $size;590pub(crate) const STRIDE: usize = $stride;591592/// Read the array register at index `idx` from `io`, using the base address provided593/// by `base` and adding the register's offset to it.594#[inline(always)]595pub(crate) fn read<const SIZE: usize, T, B>(596io: &T,597#[allow(unused_variables)]598base: &B,599idx: usize,600) -> Self where601T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,602B: crate::regs::macros::RegisterBase<$base>,603{604build_assert!(idx < Self::SIZE);605606let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE +607Self::OFFSET + (idx * Self::STRIDE);608let value = io.read32(offset);609610Self(value)611}612613/// Write the value contained in `self` to `io`, using the base address provided by614/// `base` and adding the offset of array register `idx` to it.615#[inline(always)]616pub(crate) fn write<const SIZE: usize, T, B>(617self,618io: &T,619#[allow(unused_variables)]620base: &B,621idx: usize622) where623T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,624B: crate::regs::macros::RegisterBase<$base>,625{626build_assert!(idx < Self::SIZE);627628let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE +629Self::OFFSET + (idx * Self::STRIDE);630631io.write32(self.0, offset);632}633634/// Read the array register at index `idx` from `io`, using the base address provided635/// by `base` and adding the register's offset to it, then run `f` on its value to636/// obtain a new value to write back.637#[inline(always)]638pub(crate) fn update<const SIZE: usize, T, B, F>(639io: &T,640base: &B,641idx: usize,642f: F,643) where644T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,645B: crate::regs::macros::RegisterBase<$base>,646F: ::core::ops::FnOnce(Self) -> Self,647{648let reg = f(Self::read(io, base, idx));649reg.write(io, base, idx);650}651652/// Read the array register at index `idx` from `io`, using the base address provided653/// by `base` and adding the register's offset to it.654///655/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the656/// access was out-of-bounds.657#[inline(always)]658pub(crate) fn try_read<const SIZE: usize, T, B>(659io: &T,660base: &B,661idx: usize,662) -> ::kernel::error::Result<Self> where663T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,664B: crate::regs::macros::RegisterBase<$base>,665{666if idx < Self::SIZE {667Ok(Self::read(io, base, idx))668} else {669Err(EINVAL)670}671}672673/// Write the value contained in `self` to `io`, using the base address provided by674/// `base` and adding the offset of array register `idx` to it.675///676/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the677/// access was out-of-bounds.678#[inline(always)]679pub(crate) fn try_write<const SIZE: usize, T, B>(680self,681io: &T,682base: &B,683idx: usize,684) -> ::kernel::error::Result where685T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,686B: crate::regs::macros::RegisterBase<$base>,687{688if idx < Self::SIZE {689Ok(self.write(io, base, idx))690} else {691Err(EINVAL)692}693}694695/// Read the array register at index `idx` from `io`, using the base address provided696/// by `base` and adding the register's offset to it, then run `f` on its value to697/// obtain a new value to write back.698///699/// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the700/// access was out-of-bounds.701#[inline(always)]702pub(crate) fn try_update<const SIZE: usize, T, B, F>(703io: &T,704base: &B,705idx: usize,706f: F,707) -> ::kernel::error::Result where708T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,709B: crate::regs::macros::RegisterBase<$base>,710F: ::core::ops::FnOnce(Self) -> Self,711{712if idx < Self::SIZE {713Ok(Self::update(io, base, idx, f))714} else {715Err(EINVAL)716}717}718}719};720}721722723