Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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}