File Index
Files and directories
Sign in
FILE

macros.rs

File preview with syntax highlighting.

linux-6.19.14/drivers/gpu/nova-core/regs/macros.rs RUST
  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.
 20pub(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/// ```
276macro_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}
~/ Read-only file preview.