xref: /linux/drivers/gpu/nova-core/regs/macros.rs (revision e54ad0cd3673c93cdafda58505eaa81610fe3aef)
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<const SIZE: usize, T>(io: &T) -> Self where
373                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
374             {
375                 Self(io.read32($offset))
376             }
377 
378             /// Write the value contained in `self` to the register address in `io`.
379             #[inline(always)]
380             pub(crate) fn write<const SIZE: usize, T>(self, io: &T) where
381                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
382             {
383                 io.write32(self.0, $offset)
384             }
385 
386             /// Read the register from its address in `io` and run `f` on its value to obtain a new
387             /// value to write back.
388             #[inline(always)]
389             pub(crate) fn update<const SIZE: usize, T, F>(
390                 io: &T,
391                 f: F,
392             ) where
393                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
394                 F: ::core::ops::FnOnce(Self) -> Self,
395             {
396                 let reg = f(Self::read(io));
397                 reg.write(io);
398             }
399         }
400     };
401 
402     // Generates the IO accessors for a relative offset register.
403     (@io_relative $name:ident @ $base:ty [ $offset:expr ]) => {
404         #[allow(dead_code)]
405         impl $name {
406             pub(crate) const OFFSET: usize = $offset;
407 
408             /// Read the register from `io`, using the base address provided by `base` and adding
409             /// the register's offset to it.
410             #[inline(always)]
411             pub(crate) fn read<const SIZE: usize, T, B>(
412                 io: &T,
413                 #[allow(unused_variables)]
414                 base: &B,
415             ) -> Self where
416                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
417                 B: crate::regs::macros::RegisterBase<$base>,
418             {
419                 const OFFSET: usize = $name::OFFSET;
420 
421                 let value = io.read32(
422                     <B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET
423                 );
424 
425                 Self(value)
426             }
427 
428             /// Write the value contained in `self` to `io`, using the base address provided by
429             /// `base` and adding the register's offset to it.
430             #[inline(always)]
431             pub(crate) fn write<const SIZE: usize, T, B>(
432                 self,
433                 io: &T,
434                 #[allow(unused_variables)]
435                 base: &B,
436             ) where
437                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
438                 B: crate::regs::macros::RegisterBase<$base>,
439             {
440                 const OFFSET: usize = $name::OFFSET;
441 
442                 io.write32(
443                     self.0,
444                     <B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET
445                 );
446             }
447 
448             /// Read the register from `io`, using the base address provided by `base` and adding
449             /// the register's offset to it, then run `f` on its value to obtain a new value to
450             /// write back.
451             #[inline(always)]
452             pub(crate) fn update<const SIZE: usize, T, B, F>(
453                 io: &T,
454                 base: &B,
455                 f: F,
456             ) where
457                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
458                 B: crate::regs::macros::RegisterBase<$base>,
459                 F: ::core::ops::FnOnce(Self) -> Self,
460             {
461                 let reg = f(Self::read(io, base));
462                 reg.write(io, base);
463             }
464         }
465     };
466 
467     // Generates the IO accessors for an array of registers.
468     (@io_array $name:ident @ $offset:literal [ $size:expr ; $stride:expr ]) => {
469         #[allow(dead_code)]
470         impl $name {
471             pub(crate) const OFFSET: usize = $offset;
472             pub(crate) const SIZE: usize = $size;
473             pub(crate) const STRIDE: usize = $stride;
474 
475             /// Read the array register at index `idx` from its address in `io`.
476             #[inline(always)]
477             pub(crate) fn read<const SIZE: usize, T>(
478                 io: &T,
479                 idx: usize,
480             ) -> Self where
481                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
482             {
483                 build_assert!(idx < Self::SIZE);
484 
485                 let offset = Self::OFFSET + (idx * Self::STRIDE);
486                 let value = io.read32(offset);
487 
488                 Self(value)
489             }
490 
491             /// Write the value contained in `self` to the array register with index `idx` in `io`.
492             #[inline(always)]
493             pub(crate) fn write<const SIZE: usize, T>(
494                 self,
495                 io: &T,
496                 idx: usize
497             ) where
498                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
499             {
500                 build_assert!(idx < Self::SIZE);
501 
502                 let offset = Self::OFFSET + (idx * Self::STRIDE);
503 
504                 io.write32(self.0, offset);
505             }
506 
507             /// Read the array register at index `idx` in `io` and run `f` on its value to obtain a
508             /// new value to write back.
509             #[inline(always)]
510             pub(crate) fn update<const SIZE: usize, T, F>(
511                 io: &T,
512                 idx: usize,
513                 f: F,
514             ) where
515                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
516                 F: ::core::ops::FnOnce(Self) -> Self,
517             {
518                 let reg = f(Self::read(io, idx));
519                 reg.write(io, idx);
520             }
521 
522             /// Read the array register at index `idx` from its address in `io`.
523             ///
524             /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
525             /// access was out-of-bounds.
526             #[inline(always)]
527             pub(crate) fn try_read<const SIZE: usize, T>(
528                 io: &T,
529                 idx: usize,
530             ) -> ::kernel::error::Result<Self> where
531                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
532             {
533                 if idx < Self::SIZE {
534                     Ok(Self::read(io, idx))
535                 } else {
536                     Err(EINVAL)
537                 }
538             }
539 
540             /// Write the value contained in `self` to the array register with index `idx` in `io`.
541             ///
542             /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
543             /// access was out-of-bounds.
544             #[inline(always)]
545             pub(crate) fn try_write<const SIZE: usize, T>(
546                 self,
547                 io: &T,
548                 idx: usize,
549             ) -> ::kernel::error::Result where
550                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
551             {
552                 if idx < Self::SIZE {
553                     Ok(self.write(io, idx))
554                 } else {
555                     Err(EINVAL)
556                 }
557             }
558 
559             /// Read the array register at index `idx` in `io` and run `f` on its value to obtain a
560             /// new value to write back.
561             ///
562             /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
563             /// access was out-of-bounds.
564             #[inline(always)]
565             pub(crate) fn try_update<const SIZE: usize, T, F>(
566                 io: &T,
567                 idx: usize,
568                 f: F,
569             ) -> ::kernel::error::Result where
570                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
571                 F: ::core::ops::FnOnce(Self) -> Self,
572             {
573                 if idx < Self::SIZE {
574                     Ok(Self::update(io, idx, f))
575                 } else {
576                     Err(EINVAL)
577                 }
578             }
579         }
580     };
581 
582     // Generates the IO accessors for an array of relative registers.
583     (
584         @io_relative_array $name:ident @ $base:ty
585             [ $offset:literal [ $size:expr ; $stride:expr ] ]
586     ) => {
587         #[allow(dead_code)]
588         impl $name {
589             pub(crate) const OFFSET: usize = $offset;
590             pub(crate) const SIZE: usize = $size;
591             pub(crate) const STRIDE: usize = $stride;
592 
593             /// Read the array register at index `idx` from `io`, using the base address provided
594             /// by `base` and adding the register's offset to it.
595             #[inline(always)]
596             pub(crate) fn read<const SIZE: usize, T, B>(
597                 io: &T,
598                 #[allow(unused_variables)]
599                 base: &B,
600                 idx: usize,
601             ) -> Self where
602                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
603                 B: crate::regs::macros::RegisterBase<$base>,
604             {
605                 build_assert!(idx < Self::SIZE);
606 
607                 let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE +
608                     Self::OFFSET + (idx * Self::STRIDE);
609                 let value = io.read32(offset);
610 
611                 Self(value)
612             }
613 
614             /// Write the value contained in `self` to `io`, using the base address provided by
615             /// `base` and adding the offset of array register `idx` to it.
616             #[inline(always)]
617             pub(crate) fn write<const SIZE: usize, T, B>(
618                 self,
619                 io: &T,
620                 #[allow(unused_variables)]
621                 base: &B,
622                 idx: usize
623             ) where
624                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
625                 B: crate::regs::macros::RegisterBase<$base>,
626             {
627                 build_assert!(idx < Self::SIZE);
628 
629                 let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE +
630                     Self::OFFSET + (idx * Self::STRIDE);
631 
632                 io.write32(self.0, offset);
633             }
634 
635             /// Read the array register at index `idx` from `io`, using the base address provided
636             /// by `base` and adding the register's offset to it, then run `f` on its value to
637             /// obtain a new value to write back.
638             #[inline(always)]
639             pub(crate) fn update<const SIZE: usize, T, B, F>(
640                 io: &T,
641                 base: &B,
642                 idx: usize,
643                 f: F,
644             ) where
645                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
646                 B: crate::regs::macros::RegisterBase<$base>,
647                 F: ::core::ops::FnOnce(Self) -> Self,
648             {
649                 let reg = f(Self::read(io, base, idx));
650                 reg.write(io, base, idx);
651             }
652 
653             /// Read the array register at index `idx` from `io`, using the base address provided
654             /// by `base` and adding the register's offset to it.
655             ///
656             /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
657             /// access was out-of-bounds.
658             #[inline(always)]
659             pub(crate) fn try_read<const SIZE: usize, T, B>(
660                 io: &T,
661                 base: &B,
662                 idx: usize,
663             ) -> ::kernel::error::Result<Self> where
664                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
665                 B: crate::regs::macros::RegisterBase<$base>,
666             {
667                 if idx < Self::SIZE {
668                     Ok(Self::read(io, base, idx))
669                 } else {
670                     Err(EINVAL)
671                 }
672             }
673 
674             /// Write the value contained in `self` to `io`, using the base address provided by
675             /// `base` and adding the offset of array register `idx` to it.
676             ///
677             /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
678             /// access was out-of-bounds.
679             #[inline(always)]
680             pub(crate) fn try_write<const SIZE: usize, T, B>(
681                 self,
682                 io: &T,
683                 base: &B,
684                 idx: usize,
685             ) -> ::kernel::error::Result where
686                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
687                 B: crate::regs::macros::RegisterBase<$base>,
688             {
689                 if idx < Self::SIZE {
690                     Ok(self.write(io, base, idx))
691                 } else {
692                     Err(EINVAL)
693                 }
694             }
695 
696             /// Read the array register at index `idx` from `io`, using the base address provided
697             /// by `base` and adding the register's offset to it, then run `f` on its value to
698             /// obtain a new value to write back.
699             ///
700             /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the
701             /// access was out-of-bounds.
702             #[inline(always)]
703             pub(crate) fn try_update<const SIZE: usize, T, B, F>(
704                 io: &T,
705                 base: &B,
706                 idx: usize,
707                 f: F,
708             ) -> ::kernel::error::Result where
709                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
710                 B: crate::regs::macros::RegisterBase<$base>,
711                 F: ::core::ops::FnOnce(Self) -> Self,
712             {
713                 if idx < Self::SIZE {
714                     Ok(Self::update(io, base, idx, f))
715                 } else {
716                     Err(EINVAL)
717                 }
718             }
719         }
720     };
721 }
722