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