xref: /linux/drivers/gpu/nova-core/regs/macros.rs (revision ec2f6c81d2a16a93d882488062e8e09e2f1d6865)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 //! `register!` macro to define register layout and accessors.
4 //!
5 //! A single register typically includes several fields, which are accessed through a combination
6 //! of bit-shift and mask operations that introduce a class of potential mistakes, notably because
7 //! not all possible field values are necessarily valid.
8 //!
9 //! The `register!` macro in this module provides an intuitive and readable syntax for defining a
10 //! dedicated type for each register. Each such type comes with its own field accessors that can
11 //! return an error if a field's value is invalid.
12 
13 /// Trait providing a base address to be added to the offset of a relative register to obtain
14 /// its actual offset.
15 ///
16 /// The `T` generic argument is used to distinguish which base to use, in case a type provides
17 /// several bases. It is given to the `register!` macro to restrict the use of the register to
18 /// implementors of this particular variant.
19 pub(crate) trait RegisterBase<T> {
20     const BASE: usize;
21 }
22 
23 /// Defines a dedicated type for a register with an absolute offset, including getter and setter
24 /// methods for its fields and methods to read and write it from an `Io` region.
25 ///
26 /// Example:
27 ///
28 /// ```no_run
29 /// 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 least
38 /// significant bits of the register. Each field can be accessed and modified using accessor
39 /// methods:
40 ///
41 /// ```no_run
42 /// // 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 an
47 /// // 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::alter(&bar, |r| r.set_major_revision(3).set_minor_revision(10));
55 /// ```
56 ///
57 /// Fields are defined as follows:
58 ///
59 /// - `as <type>` simply returns the field value casted to <type>, typically `u32`, `u16`, `u8` or
60 ///   `bool`. Note that `bool` fields must have a range of 1 bit.
61 /// - `as <type> => <into_type>` calls `<into_type>`'s `From::<<type>>` implementation and returns
62 ///   the result.
63 /// - `as <type> ?=> <try_into_type>` calls `<try_into_type>`'s `TryFrom::<<type>>` implementation
64 ///   and returns the result. This is useful with fields for which not all values are valid.
65 ///
66 /// The documentation strings are optional. If present, they will be added to the type's
67 /// definition, or the field getter and setter methods they are attached to.
68 ///
69 /// It is also possible to create a alias register by using the `=> ALIAS` syntax. This is useful
70 /// for cases where a register's interpretation depends on the context:
71 ///
72 /// ```no_run
73 /// register!(SCRATCH @ 0x00000200, "Scratch register" {
74 ///    31:0     value as u32, "Raw value";
75 /// });
76 ///
77 /// register!(SCRATCH_BOOT_STATUS => SCRATCH, "Boot status of the firmware" {
78 ///     0:0     completed as bool, "Whether the firmware has completed booting";
79 /// });
80 /// ```
81 ///
82 /// In this example, `SCRATCH_0_BOOT_STATUS` uses the same I/O address as `SCRATCH`, while also
83 /// providing its own `completed` field.
84 ///
85 /// ## Relative registers
86 ///
87 /// A register can be defined as being accessible from a fixed offset of a provided base. For
88 /// instance, imagine the following I/O space:
89 ///
90 /// ```text
91 ///           +-----------------------------+
92 ///           |             ...             |
93 ///           |                             |
94 ///  0x100--->+------------CPU0-------------+
95 ///           |                             |
96 ///  0x110--->+-----------------------------+
97 ///           |           CPU_CTL           |
98 ///           +-----------------------------+
99 ///           |             ...             |
100 ///           |                             |
101 ///           |                             |
102 ///  0x200--->+------------CPU1-------------+
103 ///           |                             |
104 ///  0x210--->+-----------------------------+
105 ///           |           CPU_CTL           |
106 ///           +-----------------------------+
107 ///           |             ...             |
108 ///           +-----------------------------+
109 /// ```
110 ///
111 /// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset `0x10` of their I/O
112 /// space segment. Since both instances of `CPU_CTL` share the same layout, we don't want to define
113 /// them twice and would prefer a way to select which one to use from a single definition
114 ///
115 /// This can be done using the `Base[Offset]` syntax when specifying the register's address.
116 ///
117 /// `Base` is an arbitrary type (typically a ZST) to be used as a generic parameter of the
118 /// [`RegisterBase`] trait to provide the base as a constant, i.e. each type providing a base for
119 /// this register needs to implement `RegisterBase<Base>`. Here is the above example translated
120 /// into code:
121 ///
122 /// ```no_run
123 /// // Type used to identify the base.
124 /// pub(crate) struct CpuCtlBase;
125 ///
126 /// // ZST describing `CPU0`.
127 /// struct Cpu0;
128 /// impl RegisterBase<CpuCtlBase> for Cpu0 {
129 ///     const BASE: usize = 0x100;
130 /// }
131 /// // Singleton of `CPU0` used to identify it.
132 /// const CPU0: Cpu0 = Cpu0;
133 ///
134 /// // ZST describing `CPU1`.
135 /// struct Cpu1;
136 /// impl RegisterBase<CpuCtlBase> for Cpu1 {
137 ///     const BASE: usize = 0x200;
138 /// }
139 /// // Singleton of `CPU1` used to identify it.
140 /// const CPU1: Cpu1 = Cpu1;
141 ///
142 /// // This makes `CPU_CTL` accessible from all implementors of `RegisterBase<CpuCtlBase>`.
143 /// register!(CPU_CTL @ CpuCtlBase[0x10], "CPU core control" {
144 ///     0:0     start as bool, "Start the CPU core";
145 /// });
146 ///
147 /// // The `read`, `write` and `alter` methods of relative registers take an extra `base` argument
148 /// // that is used to resolve its final address by adding its `BASE` to the offset of the
149 /// // register.
150 ///
151 /// // Start `CPU0`.
152 /// CPU_CTL::alter(bar, &CPU0, |r| r.set_start(true));
153 ///
154 /// // Start `CPU1`.
155 /// CPU_CTL::alter(bar, &CPU1, |r| r.set_start(true));
156 ///
157 /// // Aliases can also be defined for relative register.
158 /// register!(CPU_CTL_ALIAS => CpuCtlBase[CPU_CTL], "Alias to CPU core control" {
159 ///     1:1     alias_start as bool, "Start the aliased CPU core";
160 /// });
161 ///
162 /// // Start the aliased `CPU0`.
163 /// CPU_CTL_ALIAS::alter(bar, &CPU0, |r| r.set_alias_start(true));
164 /// ```
165 ///
166 /// ## Arrays of registers
167 ///
168 /// Some I/O areas contain consecutive values that can be interpreted in the same way. These areas
169 /// can be defined as an array of identical registers, allowing them to be accessed by index with
170 /// compile-time or runtime bound checking. Simply define their address as `Address[Size]`, and add
171 /// an `idx` parameter to their `read`, `write` and `alter` methods:
172 ///
173 /// ```no_run
174 /// # fn no_run() -> Result<(), Error> {
175 /// # fn get_scratch_idx() -> usize {
176 /// #   0x15
177 /// # }
178 /// // Array of 64 consecutive registers with the same layout starting at offset `0x80`.
179 /// register!(SCRATCH @ 0x00000080[64], "Scratch registers" {
180 ///     31:0    value as u32;
181 /// });
182 ///
183 /// // Read scratch register 0, i.e. I/O address `0x80`.
184 /// let scratch_0 = SCRATCH::read(bar, 0).value();
185 /// // Read scratch register 15, i.e. I/O address `0x80 + (15 * 4)`.
186 /// let scratch_15 = SCRATCH::read(bar, 15).value();
187 ///
188 /// // This is out of bounds and won't build.
189 /// // let scratch_128 = SCRATCH::read(bar, 128).value();
190 ///
191 /// // Runtime-obtained array index.
192 /// let scratch_idx = get_scratch_idx();
193 /// // Access on a runtime index returns an error if it is out-of-bounds.
194 /// let some_scratch = SCRATCH::try_read(bar, scratch_idx)?.value();
195 ///
196 /// // Alias to a particular register in an array.
197 /// // Here `SCRATCH[8]` is used to convey the firmware exit code.
198 /// register!(FIRMWARE_STATUS => SCRATCH[8], "Firmware exit status code" {
199 ///     7:0     status as u8;
200 /// });
201 ///
202 /// let status = FIRMWARE_STATUS::read(bar).status();
203 ///
204 /// // Non-contiguous register arrays can be defined by adding a stride parameter.
205 /// // Here, each of the 16 registers of the array are separated by 8 bytes, meaning that the
206 /// // registers of the two declarations below are interleaved.
207 /// register!(SCRATCH_INTERLEAVED_0 @ 0x000000c0[16 ; 8], "Scratch registers bank 0" {
208 ///     31:0    value as u32;
209 /// });
210 /// register!(SCRATCH_INTERLEAVED_1 @ 0x000000c4[16 ; 8], "Scratch registers bank 1" {
211 ///     31:0    value as u32;
212 /// });
213 /// # Ok(())
214 /// # }
215 /// ```
216 macro_rules! register {
217     // Creates a register at a fixed offset of the MMIO space.
218     ($name:ident @ $offset:literal $(, $comment:literal)? { $($fields:tt)* } ) => {
219         register!(@core $name $(, $comment)? { $($fields)* } );
220         register!(@io_fixed $name @ $offset);
221     };
222 
223     // Creates an alias register of fixed offset register `alias` with its own fields.
224     ($name:ident => $alias:ident $(, $comment:literal)? { $($fields:tt)* } ) => {
225         register!(@core $name $(, $comment)? { $($fields)* } );
226         register!(@io_fixed $name @ $alias::OFFSET);
227     };
228 
229     // Creates a register at a relative offset from a base address provider.
230     ($name:ident @ $base:ty [ $offset:literal ] $(, $comment:literal)? { $($fields:tt)* } ) => {
231         register!(@core $name $(, $comment)? { $($fields)* } );
232         register!(@io_relative $name @ $base [ $offset ]);
233     };
234 
235     // Creates an alias register of relative offset register `alias` with its own fields.
236     ($name:ident => $base:ty [ $alias:ident ] $(, $comment:literal)? { $($fields:tt)* }) => {
237         register!(@core $name $(, $comment)? { $($fields)* } );
238         register!(@io_relative $name @ $base [ $alias::OFFSET ]);
239     };
240 
241     // Creates an array of registers at a fixed offset of the MMIO space.
242     (
243         $name:ident @ $offset:literal [ $size:expr ; $stride:expr ] $(, $comment:literal)? {
244             $($fields:tt)*
245         }
246     ) => {
247         static_assert!(::core::mem::size_of::<u32>() <= $stride);
248         register!(@core $name $(, $comment)? { $($fields)* } );
249         register!(@io_array $name @ $offset [ $size ; $stride ]);
250     };
251 
252     // Shortcut for contiguous array of registers (stride == size of element).
253     (
254         $name:ident @ $offset:literal [ $size:expr ] $(, $comment:literal)? {
255             $($fields:tt)*
256         }
257     ) => {
258         register!($name @ $offset [ $size ; ::core::mem::size_of::<u32>() ] $(, $comment)? {
259             $($fields)*
260         } );
261     };
262 
263     // Creates an alias of register `idx` of array of registers `alias` with its own fields.
264     ($name:ident => $alias:ident [ $idx:expr ] $(, $comment:literal)? { $($fields:tt)* }) => {
265         static_assert!($idx < $alias::SIZE);
266         register!(@core $name $(, $comment)? { $($fields)* } );
267         register!(@io_fixed $name @ $alias::OFFSET + $idx * $alias::STRIDE );
268     };
269 
270     // All rules below are helpers.
271 
272     // Defines the wrapper `$name` type, as well as its relevant implementations (`Debug`,
273     // `Default`, `BitOr`, and conversion to the value type) and field accessor methods.
274     (@core $name:ident $(, $comment:literal)? { $($fields:tt)* }) => {
275         $(
276         #[doc=$comment]
277         )?
278         #[repr(transparent)]
279         #[derive(Clone, Copy)]
280         pub(crate) struct $name(u32);
281 
282         impl ::core::ops::BitOr for $name {
283             type Output = Self;
284 
285             fn bitor(self, rhs: Self) -> Self::Output {
286                 Self(self.0 | rhs.0)
287             }
288         }
289 
290         impl ::core::convert::From<$name> for u32 {
291             fn from(reg: $name) -> u32 {
292                 reg.0
293             }
294         }
295 
296         register!(@fields_dispatcher $name { $($fields)* });
297     };
298 
299     // Captures the fields and passes them to all the implementers that require field information.
300     //
301     // Used to simplify the matching rules for implementers, so they don't need to match the entire
302     // complex fields rule even though they only make use of part of it.
303     (@fields_dispatcher $name:ident {
304         $($hi:tt:$lo:tt $field:ident as $type:tt
305             $(?=> $try_into_type:ty)?
306             $(=> $into_type:ty)?
307             $(, $comment:literal)?
308         ;
309         )*
310     }
311     ) => {
312         register!(@field_accessors $name {
313             $(
314                 $hi:$lo $field as $type
315                 $(?=> $try_into_type)?
316                 $(=> $into_type)?
317                 $(, $comment)?
318             ;
319             )*
320         });
321         register!(@debug $name { $($field;)* });
322         register!(@default $name { $($field;)* });
323     };
324 
325     // Defines all the field getter/methods methods for `$name`.
326     (
327         @field_accessors $name:ident {
328         $($hi:tt:$lo:tt $field:ident as $type:tt
329             $(?=> $try_into_type:ty)?
330             $(=> $into_type:ty)?
331             $(, $comment:literal)?
332         ;
333         )*
334         }
335     ) => {
336         $(
337             register!(@check_field_bounds $hi:$lo $field as $type);
338         )*
339 
340         #[allow(dead_code)]
341         impl $name {
342             $(
343             register!(@field_accessor $name $hi:$lo $field as $type
344                 $(?=> $try_into_type)?
345                 $(=> $into_type)?
346                 $(, $comment)?
347                 ;
348             );
349             )*
350         }
351     };
352 
353     // Boolean fields must have `$hi == $lo`.
354     (@check_field_bounds $hi:tt:$lo:tt $field:ident as bool) => {
355         #[allow(clippy::eq_op)]
356         const _: () = {
357             ::kernel::build_assert!(
358                 $hi == $lo,
359                 concat!("boolean field `", stringify!($field), "` covers more than one bit")
360             );
361         };
362     };
363 
364     // Non-boolean fields must have `$hi >= $lo`.
365     (@check_field_bounds $hi:tt:$lo:tt $field:ident as $type:tt) => {
366         #[allow(clippy::eq_op)]
367         const _: () = {
368             ::kernel::build_assert!(
369                 $hi >= $lo,
370                 concat!("field `", stringify!($field), "`'s MSB is smaller than its LSB")
371             );
372         };
373     };
374 
375     // Catches fields defined as `bool` and convert them into a boolean value.
376     (
377         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
378             $(, $comment:literal)?;
379     ) => {
380         register!(
381             @leaf_accessor $name $hi:$lo $field
382             { |f| <$into_type>::from(if f != 0 { true } else { false }) }
383             $into_type => $into_type $(, $comment)?;
384         );
385     };
386 
387     // Shortcut for fields defined as `bool` without the `=>` syntax.
388     (
389         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool $(, $comment:literal)?;
390     ) => {
391         register!(@field_accessor $name $hi:$lo $field as bool => bool $(, $comment)?;);
392     };
393 
394     // Catches the `?=>` syntax for non-boolean fields.
395     (
396         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt ?=> $try_into_type:ty
397             $(, $comment:literal)?;
398     ) => {
399         register!(@leaf_accessor $name $hi:$lo $field
400             { |f| <$try_into_type>::try_from(f as $type) } $try_into_type =>
401             ::core::result::Result<
402                 $try_into_type,
403                 <$try_into_type as ::core::convert::TryFrom<$type>>::Error
404             >
405             $(, $comment)?;);
406     };
407 
408     // Catches the `=>` syntax for non-boolean fields.
409     (
410         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt => $into_type:ty
411             $(, $comment:literal)?;
412     ) => {
413         register!(@leaf_accessor $name $hi:$lo $field
414             { |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;);
415     };
416 
417     // Shortcut for non-boolean fields defined without the `=>` or `?=>` syntax.
418     (
419         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt
420             $(, $comment:literal)?;
421     ) => {
422         register!(@field_accessor $name $hi:$lo $field as $type => $type $(, $comment)?;);
423     };
424 
425     // Generates the accessor methods for a single field.
426     (
427         @leaf_accessor $name:ident $hi:tt:$lo:tt $field:ident
428             { $process:expr } $to_type:ty => $res_type:ty $(, $comment:literal)?;
429     ) => {
430         ::kernel::macros::paste!(
431         const [<$field:upper _RANGE>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi;
432         const [<$field:upper _MASK>]: u32 = ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);
433         const [<$field:upper _SHIFT>]: u32 = Self::[<$field:upper _MASK>].trailing_zeros();
434         );
435 
436         $(
437         #[doc="Returns the value of this field:"]
438         #[doc=$comment]
439         )?
440         #[inline(always)]
441         pub(crate) fn $field(self) -> $res_type {
442             ::kernel::macros::paste!(
443             const MASK: u32 = $name::[<$field:upper _MASK>];
444             const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
445             );
446             let field = ((self.0 & MASK) >> SHIFT);
447 
448             $process(field)
449         }
450 
451         ::kernel::macros::paste!(
452         $(
453         #[doc="Sets the value of this field:"]
454         #[doc=$comment]
455         )?
456         #[inline(always)]
457         pub(crate) fn [<set_ $field>](mut self, value: $to_type) -> Self {
458             const MASK: u32 = $name::[<$field:upper _MASK>];
459             const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
460             let value = (u32::from(value) << SHIFT) & MASK;
461             self.0 = (self.0 & !MASK) | value;
462 
463             self
464         }
465         );
466     };
467 
468     // Generates the `Debug` implementation for `$name`.
469     (@debug $name:ident { $($field:ident;)* }) => {
470         impl ::core::fmt::Debug for $name {
471             fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
472                 f.debug_struct(stringify!($name))
473                     .field("<raw>", &format_args!("{:#x}", &self.0))
474                 $(
475                     .field(stringify!($field), &self.$field())
476                 )*
477                     .finish()
478             }
479         }
480     };
481 
482     // Generates the `Default` implementation for `$name`.
483     (@default $name:ident { $($field:ident;)* }) => {
484         /// Returns a value for the register where all fields are set to their default value.
485         impl ::core::default::Default for $name {
486             fn default() -> Self {
487                 #[allow(unused_mut)]
488                 let mut value = Self(Default::default());
489 
490                 ::kernel::macros::paste!(
491                 $(
492                 value.[<set_ $field>](Default::default());
493                 )*
494                 );
495 
496                 value
497             }
498         }
499     };
500 
501     // Generates the IO accessors for a fixed offset register.
502     (@io_fixed $name:ident @ $offset:expr) => {
503         #[allow(dead_code)]
504         impl $name {
505             pub(crate) const OFFSET: usize = $offset;
506 
507             /// Read the register from its address in `io`.
508             #[inline(always)]
509             pub(crate) fn read<const SIZE: usize, T>(io: &T) -> Self where
510                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
511             {
512                 Self(io.read32($offset))
513             }
514 
515             /// Write the value contained in `self` to the register address in `io`.
516             #[inline(always)]
517             pub(crate) fn write<const SIZE: usize, T>(self, io: &T) where
518                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
519             {
520                 io.write32(self.0, $offset)
521             }
522 
523             /// Read the register from its address in `io` and run `f` on its value to obtain a new
524             /// value to write back.
525             #[inline(always)]
526             pub(crate) fn alter<const SIZE: usize, T, F>(
527                 io: &T,
528                 f: F,
529             ) where
530                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
531                 F: ::core::ops::FnOnce(Self) -> Self,
532             {
533                 let reg = f(Self::read(io));
534                 reg.write(io);
535             }
536         }
537     };
538 
539     // Generates the IO accessors for a relative offset register.
540     (@io_relative $name:ident @ $base:ty [ $offset:expr ]) => {
541         #[allow(dead_code)]
542         impl $name {
543             pub(crate) const OFFSET: usize = $offset;
544 
545             /// Read the register from `io`, using the base address provided by `base` and adding
546             /// the register's offset to it.
547             #[inline(always)]
548             pub(crate) fn read<const SIZE: usize, T, B>(
549                 io: &T,
550                 #[allow(unused_variables)]
551                 base: &B,
552             ) -> Self where
553                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
554                 B: crate::regs::macros::RegisterBase<$base>,
555             {
556                 const OFFSET: usize = $name::OFFSET;
557 
558                 let value = io.read32(
559                     <B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET
560                 );
561 
562                 Self(value)
563             }
564 
565             /// Write the value contained in `self` to `io`, using the base address provided by
566             /// `base` and adding the register's offset to it.
567             #[inline(always)]
568             pub(crate) fn write<const SIZE: usize, T, B>(
569                 self,
570                 io: &T,
571                 #[allow(unused_variables)]
572                 base: &B,
573             ) where
574                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
575                 B: crate::regs::macros::RegisterBase<$base>,
576             {
577                 const OFFSET: usize = $name::OFFSET;
578 
579                 io.write32(
580                     self.0,
581                     <B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET
582                 );
583             }
584 
585             /// Read the register from `io`, using the base address provided by `base` and adding
586             /// the register's offset to it, then run `f` on its value to obtain a new value to
587             /// write back.
588             #[inline(always)]
589             pub(crate) fn alter<const SIZE: usize, T, B, F>(
590                 io: &T,
591                 base: &B,
592                 f: F,
593             ) where
594                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
595                 B: crate::regs::macros::RegisterBase<$base>,
596                 F: ::core::ops::FnOnce(Self) -> Self,
597             {
598                 let reg = f(Self::read(io, base));
599                 reg.write(io, base);
600             }
601         }
602     };
603 
604     // Generates the IO accessors for an array of registers.
605     (@io_array $name:ident @ $offset:literal [ $size:expr ; $stride:expr ]) => {
606         #[allow(dead_code)]
607         impl $name {
608             pub(crate) const OFFSET: usize = $offset;
609             pub(crate) const SIZE: usize = $size;
610             pub(crate) const STRIDE: usize = $stride;
611 
612             /// Read the array register at index `idx` from its address in `io`.
613             #[inline(always)]
614             pub(crate) fn read<const SIZE: usize, T>(
615                 io: &T,
616                 idx: usize,
617             ) -> Self where
618                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
619             {
620                 build_assert!(idx < Self::SIZE);
621 
622                 let offset = Self::OFFSET + (idx * Self::STRIDE);
623                 let value = io.read32(offset);
624 
625                 Self(value)
626             }
627 
628             /// Write the value contained in `self` to the array register with index `idx` in `io`.
629             #[inline(always)]
630             pub(crate) fn write<const SIZE: usize, T>(
631                 self,
632                 io: &T,
633                 idx: usize
634             ) where
635                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
636             {
637                 build_assert!(idx < Self::SIZE);
638 
639                 let offset = Self::OFFSET + (idx * Self::STRIDE);
640 
641                 io.write32(self.0, offset);
642             }
643 
644             /// Read the array register at index `idx` in `io` and run `f` on its value to obtain a
645             /// new value to write back.
646             #[inline(always)]
647             pub(crate) fn alter<const SIZE: usize, T, F>(
648                 io: &T,
649                 idx: usize,
650                 f: F,
651             ) where
652                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
653                 F: ::core::ops::FnOnce(Self) -> Self,
654             {
655                 let reg = f(Self::read(io, idx));
656                 reg.write(io, idx);
657             }
658 
659             /// Read the array register at index `idx` from its address in `io`.
660             ///
661             /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
662             /// access was out-of-bounds.
663             #[inline(always)]
664             pub(crate) fn try_read<const SIZE: usize, T>(
665                 io: &T,
666                 idx: usize,
667             ) -> ::kernel::error::Result<Self> where
668                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
669             {
670                 if idx < Self::SIZE {
671                     Ok(Self::read(io, idx))
672                 } else {
673                     Err(EINVAL)
674                 }
675             }
676 
677             /// Write the value contained in `self` to the array register with index `idx` in `io`.
678             ///
679             /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
680             /// access was out-of-bounds.
681             #[inline(always)]
682             pub(crate) fn try_write<const SIZE: usize, T>(
683                 self,
684                 io: &T,
685                 idx: usize,
686             ) -> ::kernel::error::Result where
687                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
688             {
689                 if idx < Self::SIZE {
690                     Ok(self.write(io, idx))
691                 } else {
692                     Err(EINVAL)
693                 }
694             }
695 
696             /// Read the array register at index `idx` in `io` and run `f` on its value to obtain a
697             /// new value to write back.
698             ///
699             /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
700             /// access was out-of-bounds.
701             #[inline(always)]
702             pub(crate) fn try_alter<const SIZE: usize, T, F>(
703                 io: &T,
704                 idx: usize,
705                 f: F,
706             ) -> ::kernel::error::Result where
707                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
708                 F: ::core::ops::FnOnce(Self) -> Self,
709             {
710                 if idx < Self::SIZE {
711                     Ok(Self::alter(io, idx, f))
712                 } else {
713                     Err(EINVAL)
714                 }
715             }
716         }
717     };
718 }
719