xref: /linux/rust/kernel/io/register.rs (revision 59e6295fac26b8e85c1ea859cdd89fa1e47519d7)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 //! Macro to define register layout and accessors.
4 //!
5 //! The [`register!`](kernel::io::register!) macro provides an intuitive and readable syntax for
6 //! defining a dedicated type for each register and accessing it using [`Io`](super::Io). Each such
7 //! type comes with its own field accessors that can return an error if a field's value is invalid.
8 //!
9 //! Note: most of the items in this module are public so they can be referenced by the macro, but
10 //! most are not to be used directly by users. Outside of the `register!` macro itself, the only
11 //! items you might want to import from this module are [`WithBase`] and [`Array`].
12 //!
13 //! # Simple example
14 //!
15 //! ```no_run
16 //! use kernel::io::register;
17 //!
18 //! register! {
19 //!     /// Basic information about the chip.
20 //!     pub BOOT_0(u32) @ 0x00000100 {
21 //!         /// Vendor ID.
22 //!         15:8 vendor_id;
23 //!         /// Major revision of the chip.
24 //!         7:4 major_revision;
25 //!         /// Minor revision of the chip.
26 //!         3:0 minor_revision;
27 //!     }
28 //! }
29 //! ```
30 //!
31 //! This defines a 32-bit `BOOT_0` type which can be read from or written to offset `0x100` of an
32 //! `Io` region, with the described bitfields. For instance, `minor_revision` consists of the 4
33 //! least significant bits of the type.
34 //!
35 //! Fields are instances of [`Bounded`](kernel::num::Bounded) and can be read by calling their
36 //! getter method, which is named after them. They also have setter methods prefixed with `with_`
37 //! for runtime values and `with_const_` for constant values. All setters return the updated
38 //! register value.
39 //!
40 //! Fields can also be transparently converted from/to an arbitrary type by using the `=>` and
41 //! `?=>` syntaxes.
42 //!
43 //! If present, doc comments above register or fields definitions are added to the relevant item
44 //! they document (the register type itself, or the field's setter and getter methods).
45 //!
46 //! Note that multiple registers can be defined in a single `register!` invocation. This can be
47 //! useful to group related registers together.
48 //!
49 //! Here is how the register defined above can be used in code:
50 //!
51 //!
52 //! ```no_run
53 //! use kernel::{
54 //!     io::{
55 //!         register,
56 //!         Io,
57 //!         IoLoc,
58 //!     },
59 //!     num::Bounded,
60 //! };
61 //! # use kernel::io::{Mmio, Region};
62 //! # register! {
63 //! #     pub BOOT_0(u32) @ 0x00000100 {
64 //! #         15:8 vendor_id;
65 //! #         7:4 major_revision;
66 //! #         3:0 minor_revision;
67 //! #     }
68 //! # }
69 //! # fn test(io: Mmio<'_, Region<0x1000>>) {
70 //! # fn obtain_vendor_id() -> u8 { 0xff }
71 //!
72 //! // Read from the register's defined offset (0x100).
73 //! let boot0 = io.read(BOOT_0);
74 //! pr_info!("chip revision: {}.{}", boot0.major_revision().get(), boot0.minor_revision().get());
75 //!
76 //! // Update some fields and write the new value back.
77 //! let new_boot0 = boot0
78 //!     // Constant values.
79 //!     .with_const_major_revision::<3>()
80 //!     .with_const_minor_revision::<10>()
81 //!     // Runtime value.
82 //!     .with_vendor_id(obtain_vendor_id());
83 //! io.write_reg(new_boot0);
84 //!
85 //! // Or, build a new value from zero and write it:
86 //! io.write_reg(BOOT_0::zeroed()
87 //!     .with_const_major_revision::<3>()
88 //!     .with_const_minor_revision::<10>()
89 //!     .with_vendor_id(obtain_vendor_id())
90 //! );
91 //!
92 //! // Or, read and update the register in a single step.
93 //! io.update(BOOT_0, |r| r
94 //!     .with_const_major_revision::<3>()
95 //!     .with_const_minor_revision::<10>()
96 //!     .with_vendor_id(obtain_vendor_id())
97 //! );
98 //!
99 //! // Constant values can also be built using the const setters.
100 //! const V: BOOT_0 = pin_init::zeroed::<BOOT_0>()
101 //!     .with_const_major_revision::<3>()
102 //!     .with_const_minor_revision::<10>();
103 //! # }
104 //! ```
105 //!
106 //! For more extensive documentation about how to define registers, see the
107 //! [`register!`](kernel::io::register!) macro.
108 
109 use core::marker::PhantomData;
110 
111 use crate::{
112     build_assert::build_assert,
113     io::IoLoc, //
114 };
115 
116 use super::Region;
117 
118 /// Trait implemented by all registers.
119 pub trait Register: Sized {
120     /// Backing primitive type of the register.
121     type Storage: Into<Self> + From<Self>;
122 
123     /// Start offset of the register.
124     ///
125     /// The interpretation of this offset depends on the type of the register.
126     const OFFSET: usize;
127 }
128 
129 /// Trait implemented by registers with a fixed offset.
130 pub trait FixedRegister: Register {}
131 
132 /// Allows `()` to be used as the `location` parameter of [`Io::write`](super::Io::write) when
133 /// passing a [`FixedRegister`] value.
134 impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for ()
135 where
136     T: FixedRegister,
137 {
138     type IoType = T::Storage;
139 
140     #[inline(always)]
141     fn offset(self) -> usize {
142         T::OFFSET
143     }
144 }
145 
146 /// A [`FixedRegister`] carries its location in its type. Thus `FixedRegister` values can be used
147 /// as an [`IoLoc`].
148 impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for T
149 where
150     T: FixedRegister,
151 {
152     type IoType = T::Storage;
153 
154     #[inline(always)]
155     fn offset(self) -> usize {
156         T::OFFSET
157     }
158 }
159 
160 /// Location of a fixed register.
161 pub struct FixedRegisterLoc<T: FixedRegister>(PhantomData<T>);
162 
163 impl<T: FixedRegister> FixedRegisterLoc<T> {
164     /// Returns the location of `T`.
165     #[inline(always)]
166     // We do not implement `Default` so we can be const.
167     #[expect(clippy::new_without_default)]
168     pub const fn new() -> Self {
169         Self(PhantomData)
170     }
171 }
172 
173 impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for FixedRegisterLoc<T>
174 where
175     T: FixedRegister,
176 {
177     type IoType = T::Storage;
178 
179     #[inline(always)]
180     fn offset(self) -> usize {
181         T::OFFSET
182     }
183 }
184 
185 /// Trait providing a base address to be added to the offset of a relative register to obtain
186 /// its actual offset.
187 ///
188 /// The `T` generic argument is used to distinguish which base to use, in case a type provides
189 /// several bases. It is given to the `register!` macro to restrict the use of the register to
190 /// implementors of this particular variant.
191 pub trait RegisterBase<T> {
192     /// Base address to which register offsets are added.
193     const BASE: usize;
194 }
195 
196 /// Trait implemented by all registers that are relative to a base.
197 pub trait WithBase {
198     /// Family of bases applicable to this register.
199     type BaseFamily;
200 
201     /// Returns the absolute location of this type when using `B` as its base.
202     #[inline(always)]
203     fn of<B: RegisterBase<Self::BaseFamily>>() -> RelativeRegisterLoc<Self, B>
204     where
205         Self: Register,
206     {
207         RelativeRegisterLoc::new()
208     }
209 }
210 
211 /// Trait implemented by relative registers.
212 pub trait RelativeRegister: Register + WithBase {}
213 
214 /// Location of a relative register.
215 ///
216 /// This can either be an immediately accessible regular [`RelativeRegister`], or a
217 /// [`RelativeRegisterArray`] that needs one additional resolution through
218 /// [`RelativeRegisterLoc::at`].
219 pub struct RelativeRegisterLoc<T: WithBase, B: ?Sized>(PhantomData<T>, PhantomData<B>);
220 
221 impl<T, B> RelativeRegisterLoc<T, B>
222 where
223     T: Register + WithBase,
224     B: RegisterBase<T::BaseFamily> + ?Sized,
225 {
226     /// Returns the location of a relative register or register array.
227     #[inline(always)]
228     // We do not implement `Default` so we can be const.
229     #[expect(clippy::new_without_default)]
230     pub const fn new() -> Self {
231         Self(PhantomData, PhantomData)
232     }
233 
234     // Returns the absolute offset of the relative register using base `B`.
235     //
236     // This is implemented as a private const method so it can be reused by the [`IoLoc`]
237     // implementations of both [`RelativeRegisterLoc`] and [`RelativeRegisterArrayLoc`].
238     #[inline]
239     const fn offset(self) -> usize {
240         B::BASE + T::OFFSET
241     }
242 }
243 
244 impl<const SIZE: usize, T, B> IoLoc<Region<SIZE>, T> for RelativeRegisterLoc<T, B>
245 where
246     T: RelativeRegister,
247     B: RegisterBase<T::BaseFamily> + ?Sized,
248 {
249     type IoType = T::Storage;
250 
251     #[inline(always)]
252     fn offset(self) -> usize {
253         RelativeRegisterLoc::offset(self)
254     }
255 }
256 
257 /// Trait implemented by arrays of registers.
258 pub trait RegisterArray: Register {
259     /// Number of elements in the registers array.
260     const SIZE: usize;
261     /// Number of bytes between the start of elements in the registers array.
262     const STRIDE: usize;
263 }
264 
265 /// Location of an array register.
266 pub struct RegisterArrayLoc<T: RegisterArray>(usize, PhantomData<T>);
267 
268 impl<T: RegisterArray> RegisterArrayLoc<T> {
269     /// Returns the location of register `T` at position `idx`, with build-time validation.
270     #[inline(always)]
271     pub fn new(idx: usize) -> Self {
272         build_assert!(idx < T::SIZE);
273 
274         Self(idx, PhantomData)
275     }
276 
277     /// Attempts to return the location of register `T` at position `idx`, with runtime validation.
278     #[inline(always)]
279     pub fn try_new(idx: usize) -> Option<Self> {
280         if idx < T::SIZE {
281             Some(Self(idx, PhantomData))
282         } else {
283             None
284         }
285     }
286 }
287 
288 impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for RegisterArrayLoc<T>
289 where
290     T: RegisterArray,
291 {
292     type IoType = T::Storage;
293 
294     #[inline(always)]
295     fn offset(self) -> usize {
296         T::OFFSET + self.0 * T::STRIDE
297     }
298 }
299 
300 /// Trait providing location builders for [`RegisterArray`]s.
301 pub trait Array {
302     /// Returns the location of the register at position `idx`, with build-time validation.
303     #[inline(always)]
304     fn at(idx: usize) -> RegisterArrayLoc<Self>
305     where
306         Self: RegisterArray,
307     {
308         RegisterArrayLoc::new(idx)
309     }
310 
311     /// Returns the location of the register at position `idx`, with runtime validation.
312     #[inline(always)]
313     fn try_at(idx: usize) -> Option<RegisterArrayLoc<Self>>
314     where
315         Self: RegisterArray,
316     {
317         RegisterArrayLoc::try_new(idx)
318     }
319 }
320 
321 /// Trait implemented by arrays of relative registers.
322 pub trait RelativeRegisterArray: RegisterArray + WithBase {}
323 
324 /// Location of a relative array register.
325 pub struct RelativeRegisterArrayLoc<
326     T: RelativeRegisterArray,
327     B: RegisterBase<T::BaseFamily> + ?Sized,
328 >(RelativeRegisterLoc<T, B>, usize);
329 
330 impl<T, B> RelativeRegisterArrayLoc<T, B>
331 where
332     T: RelativeRegisterArray,
333     B: RegisterBase<T::BaseFamily> + ?Sized,
334 {
335     /// Returns the location of register `T` from the base `B` at index `idx`, with build-time
336     /// validation.
337     #[inline(always)]
338     pub fn new(idx: usize) -> Self {
339         build_assert!(idx < T::SIZE);
340 
341         Self(RelativeRegisterLoc::new(), idx)
342     }
343 
344     /// Attempts to return the location of register `T` from the base `B` at index `idx`, with
345     /// runtime validation.
346     #[inline(always)]
347     pub fn try_new(idx: usize) -> Option<Self> {
348         if idx < T::SIZE {
349             Some(Self(RelativeRegisterLoc::new(), idx))
350         } else {
351             None
352         }
353     }
354 }
355 
356 /// Methods exclusive to [`RelativeRegisterLoc`]s created with a [`RelativeRegisterArray`].
357 impl<T, B> RelativeRegisterLoc<T, B>
358 where
359     T: RelativeRegisterArray,
360     B: RegisterBase<T::BaseFamily> + ?Sized,
361 {
362     /// Returns the location of the register at position `idx`, with build-time validation.
363     #[inline(always)]
364     pub fn at(self, idx: usize) -> RelativeRegisterArrayLoc<T, B> {
365         RelativeRegisterArrayLoc::new(idx)
366     }
367 
368     /// Returns the location of the register at position `idx`, with runtime validation.
369     #[inline(always)]
370     pub fn try_at(self, idx: usize) -> Option<RelativeRegisterArrayLoc<T, B>> {
371         RelativeRegisterArrayLoc::try_new(idx)
372     }
373 }
374 
375 impl<const SIZE: usize, T, B> IoLoc<Region<SIZE>, T> for RelativeRegisterArrayLoc<T, B>
376 where
377     T: RelativeRegisterArray,
378     B: RegisterBase<T::BaseFamily> + ?Sized,
379 {
380     type IoType = T::Storage;
381 
382     #[inline(always)]
383     fn offset(self) -> usize {
384         self.0.offset() + self.1 * T::STRIDE
385     }
386 }
387 
388 /// Trait implemented by items that contain both a register value and the absolute I/O location at
389 /// which to write it.
390 ///
391 /// Implementors can be used with [`Io::write_reg`](super::Io::write_reg).
392 pub trait LocatedRegister<Base: ?Sized> {
393     /// Register value to write.
394     type Value: Register;
395     /// Full location information at which to write the value.
396     type Location: IoLoc<Base, Self::Value>;
397 
398     /// Consumes `self` and returns a `(location, value)` tuple describing a valid I/O write
399     /// operation.
400     fn into_io_op(self) -> (Self::Location, Self::Value);
401 }
402 
403 impl<const SIZE: usize, T> LocatedRegister<Region<SIZE>> for T
404 where
405     T: FixedRegister,
406 {
407     type Location = FixedRegisterLoc<Self::Value>;
408     type Value = T;
409 
410     #[inline(always)]
411     fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {
412         (FixedRegisterLoc::new(), self)
413     }
414 }
415 
416 /// Defines a dedicated type for a register, including getter and setter methods for its fields and
417 /// methods to read and write it from an [`Io`](kernel::io::Io) region.
418 ///
419 /// This documentation focuses on how to declare registers. See the [module-level
420 /// documentation](mod@kernel::io::register) for examples of how to access them.
421 ///
422 /// There are 4 possible kinds of registers: fixed offset registers, relative registers, arrays of
423 /// registers, and relative arrays of registers.
424 ///
425 /// ## Fixed offset registers
426 ///
427 /// These are the simplest kind of registers. Their location is simply an offset inside the I/O
428 /// region. For instance:
429 ///
430 /// ```ignore
431 /// register! {
432 ///     pub FIXED_REG(u16) @ 0x80 {
433 ///         ...
434 ///     }
435 /// }
436 /// ```
437 ///
438 /// This creates a 16-bit register named `FIXED_REG` located at offset `0x80` of an I/O region.
439 ///
440 /// These registers' location can be built simply by referencing their name:
441 ///
442 /// ```no_run
443 /// use kernel::{
444 ///     io::{
445 ///         register,
446 ///         Io,
447 ///     },
448 /// };
449 /// # use kernel::io::{Mmio, Region};
450 ///
451 /// register! {
452 ///     FIXED_REG(u32) @ 0x100 {
453 ///         15:8 high_byte;
454 ///         7:0  low_byte;
455 ///     }
456 /// }
457 ///
458 /// # fn test(io: Mmio<'_, Region<0x1000>>) {
459 /// let val = io.read(FIXED_REG);
460 ///
461 /// // Write from an already-existing value.
462 /// io.write(FIXED_REG, val.with_low_byte(0xff));
463 ///
464 /// // Create a register value from scratch.
465 /// let val2 = FIXED_REG::zeroed().with_high_byte(0x80);
466 ///
467 /// // The location of fixed offset registers is already contained in their type. Thus, the
468 /// // `location` argument of `Io::write` is technically redundant and can be replaced by `()`.
469 /// io.write((), val2);
470 ///
471 /// // Or, the single-argument `Io::write_reg` can be used.
472 /// io.write_reg(val2);
473 /// # }
474 ///
475 /// ```
476 ///
477 /// It is possible to create an alias of an existing register with new field definitions by using
478 /// the `=> ALIAS` syntax. This is useful for cases where a register's interpretation depends on
479 /// the context:
480 ///
481 /// ```no_run
482 /// use kernel::io::register;
483 ///
484 /// register! {
485 ///     /// Scratch register.
486 ///     pub SCRATCH(u32) @ 0x00000200 {
487 ///         31:0 value;
488 ///     }
489 ///
490 ///     /// Boot status of the firmware.
491 ///     pub SCRATCH_BOOT_STATUS(u32) => SCRATCH {
492 ///         0:0 completed;
493 ///     }
494 /// }
495 /// ```
496 ///
497 /// In this example, `SCRATCH_BOOT_STATUS` uses the same I/O address as `SCRATCH`, while providing
498 /// its own `completed` field.
499 ///
500 /// ## Relative registers
501 ///
502 /// Relative registers can be instantiated several times at a relative offset of a group of bases.
503 /// For instance, imagine the following I/O space:
504 ///
505 /// ```text
506 ///           +-----------------------------+
507 ///           |             ...             |
508 ///           |                             |
509 ///  0x100--->+------------CPU0-------------+
510 ///           |                             |
511 ///  0x110--->+-----------------------------+
512 ///           |           CPU_CTL           |
513 ///           +-----------------------------+
514 ///           |             ...             |
515 ///           |                             |
516 ///           |                             |
517 ///  0x200--->+------------CPU1-------------+
518 ///           |                             |
519 ///  0x210--->+-----------------------------+
520 ///           |           CPU_CTL           |
521 ///           +-----------------------------+
522 ///           |             ...             |
523 ///           +-----------------------------+
524 /// ```
525 ///
526 /// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset `0x10` of their I/O
527 /// space segment. Since both instances of `CPU_CTL` share the same layout, we don't want to define
528 /// them twice and would prefer a way to select which one to use from a single definition.
529 ///
530 /// This can be done using the `Base + Offset` syntax when specifying the register's address:
531 ///
532 /// ```ignore
533 /// register! {
534 ///     pub RELATIVE_REG(u32) @ Base + 0x80 {
535 ///         ...
536 ///     }
537 /// }
538 /// ```
539 ///
540 /// This creates a register with an offset of `0x80` from a given base.
541 ///
542 /// `Base` is an arbitrary type (typically a ZST) to be used as a generic parameter of the
543 /// [`RegisterBase`] trait to provide the base as a constant, i.e. each type providing a base for
544 /// this register needs to implement `RegisterBase<Base>`.
545 ///
546 /// The location of relative registers can be built using the [`WithBase::of`] method to specify
547 /// its base. All relative registers implement [`WithBase`].
548 ///
549 /// Here is the above layout translated into code:
550 ///
551 /// ```no_run
552 /// use kernel::{
553 ///     io::{
554 ///         register,
555 ///         register::{
556 ///             RegisterBase,
557 ///             WithBase,
558 ///         },
559 ///         Io,
560 ///     },
561 /// };
562 /// # use kernel::io::{Mmio, Region};
563 ///
564 /// // Type used to identify the base.
565 /// pub struct CpuCtlBase;
566 ///
567 /// // ZST describing `CPU0`.
568 /// struct Cpu0;
569 /// impl RegisterBase<CpuCtlBase> for Cpu0 {
570 ///     const BASE: usize = 0x100;
571 /// }
572 ///
573 /// // ZST describing `CPU1`.
574 /// struct Cpu1;
575 /// impl RegisterBase<CpuCtlBase> for Cpu1 {
576 ///     const BASE: usize = 0x200;
577 /// }
578 ///
579 /// // This makes `CPU_CTL` accessible from all implementors of `RegisterBase<CpuCtlBase>`.
580 /// register! {
581 ///     /// CPU core control.
582 ///     pub CPU_CTL(u32) @ CpuCtlBase + 0x10 {
583 ///         0:0 start;
584 ///     }
585 /// }
586 ///
587 /// # fn test(io: Mmio<'_, Region<0x1000>>) {
588 /// // Read the status of `Cpu0`.
589 /// let cpu0_started = io.read(CPU_CTL::of::<Cpu0>());
590 ///
591 /// // Stop `Cpu0`.
592 /// io.write(WithBase::of::<Cpu0>(), CPU_CTL::zeroed());
593 /// # }
594 ///
595 /// // Aliases can also be defined for relative register.
596 /// register! {
597 ///     /// Alias to CPU core control.
598 ///     pub CPU_CTL_ALIAS(u32) => CpuCtlBase + CPU_CTL {
599 ///         /// Start the aliased CPU core.
600 ///         1:1 alias_start;
601 ///     }
602 /// }
603 ///
604 /// # fn test2(io: Mmio<'_, Region<0x1000>>) {
605 /// // Start the aliased `CPU0`, leaving its other fields untouched.
606 /// io.update(CPU_CTL_ALIAS::of::<Cpu0>(), |r| r.with_alias_start(true));
607 /// # }
608 /// ```
609 ///
610 /// ## Arrays of registers
611 ///
612 /// Some I/O areas contain consecutive registers that share the same field layout. These areas can
613 /// be defined as an array of identical registers, allowing them to be accessed by index with
614 /// compile-time or runtime bound checking:
615 ///
616 /// ```ignore
617 /// register! {
618 ///     pub REGISTER_ARRAY(u8)[10, stride = 4] @ 0x100 {
619 ///         ...
620 ///     }
621 /// }
622 /// ```
623 ///
624 /// This defines `REGISTER_ARRAY`, an array of 10 byte registers starting at offset `0x100`. Each
625 /// register is separated from its neighbor by 4 bytes.
626 ///
627 /// The `stride` parameter is optional; if unspecified, the registers are placed consecutively from
628 /// each other.
629 ///
630 /// A location for a register in a register array is built using the [`Array::at`] trait method.
631 /// All arrays of registers implement [`Array`].
632 ///
633 /// ```no_run
634 /// use kernel::{
635 ///     io::{
636 ///         register,
637 ///         register::Array,
638 ///         Io,
639 ///     },
640 /// };
641 /// # use kernel::io::{Mmio, Region};
642 /// # fn get_scratch_idx() -> usize {
643 /// #   0x15
644 /// # }
645 ///
646 /// // Array of 64 consecutive registers with the same layout starting at offset `0x80`.
647 /// register! {
648 ///     /// Scratch registers.
649 ///     pub SCRATCH(u32)[64] @ 0x00000080 {
650 ///         31:0 value;
651 ///     }
652 /// }
653 ///
654 /// # fn test(io: Mmio<'_, Region<0x1000>>)
655 /// #     -> Result<(), Error>{
656 /// // Read scratch register 0, i.e. I/O address `0x80`.
657 /// let scratch_0 = io.read(SCRATCH::at(0)).value();
658 ///
659 /// // Write scratch register 15, i.e. I/O address `0x80 + (15 * 4)`.
660 /// io.write(Array::at(15), SCRATCH::from(0xffeeaabb));
661 ///
662 /// // This is out of bounds and won't build.
663 /// // let scratch_128 = io.read(SCRATCH::at(128)).value();
664 ///
665 /// // Runtime-obtained array index.
666 /// let idx = get_scratch_idx();
667 /// // Access on a runtime index returns an error if it is out-of-bounds.
668 /// let some_scratch = io.read(SCRATCH::try_at(idx).ok_or(EINVAL)?).value();
669 ///
670 /// // Alias to a specific register in an array.
671 /// // Here `SCRATCH[8]` is used to convey the firmware exit code.
672 /// register! {
673 ///     /// Firmware exit status code.
674 ///     pub FIRMWARE_STATUS(u32) => SCRATCH[8] {
675 ///         7:0 status;
676 ///     }
677 /// }
678 ///
679 /// let status = io.read(FIRMWARE_STATUS).status();
680 ///
681 /// // Non-contiguous register arrays can be defined by adding a stride parameter.
682 /// // Here, each of the 16 registers of the array is separated by 8 bytes, meaning that the
683 /// // registers of the two declarations below are interleaved.
684 /// register! {
685 ///     /// Scratch registers bank 0.
686 ///     pub SCRATCH_INTERLEAVED_0(u32)[16, stride = 8] @ 0x000000c0 {
687 ///         31:0 value;
688 ///     }
689 ///
690 ///     /// Scratch registers bank 1.
691 ///     pub SCRATCH_INTERLEAVED_1(u32)[16, stride = 8] @ 0x000000c4 {
692 ///         31:0 value;
693 ///     }
694 /// }
695 /// # Ok(())
696 /// # }
697 /// ```
698 ///
699 /// ## Relative arrays of registers
700 ///
701 /// Combining the two features described in the sections above, arrays of registers accessible from
702 /// a base can also be defined:
703 ///
704 /// ```ignore
705 /// register! {
706 ///     pub RELATIVE_REGISTER_ARRAY(u8)[10, stride = 4] @ Base + 0x100 {
707 ///         ...
708 ///     }
709 /// }
710 /// ```
711 ///
712 /// Like relative registers, they implement the [`WithBase`] trait. However the return value of
713 /// [`WithBase::of`] cannot be used directly as a location and must be further specified using the
714 /// [`at`](RelativeRegisterLoc::at) method.
715 ///
716 /// ```no_run
717 /// use kernel::{
718 ///     io::{
719 ///         register,
720 ///         register::{
721 ///             RegisterBase,
722 ///             WithBase,
723 ///         },
724 ///         Io,
725 ///     },
726 /// };
727 /// # use kernel::io::{Mmio, Region};
728 /// # fn get_scratch_idx() -> usize {
729 /// #   0x15
730 /// # }
731 ///
732 /// // Type used as parameter of `RegisterBase` to specify the base.
733 /// pub struct CpuCtlBase;
734 ///
735 /// // ZST describing `CPU0`.
736 /// struct Cpu0;
737 /// impl RegisterBase<CpuCtlBase> for Cpu0 {
738 ///     const BASE: usize = 0x100;
739 /// }
740 ///
741 /// // ZST describing `CPU1`.
742 /// struct Cpu1;
743 /// impl RegisterBase<CpuCtlBase> for Cpu1 {
744 ///     const BASE: usize = 0x200;
745 /// }
746 ///
747 /// // 64 per-cpu scratch registers, arranged as a contiguous array.
748 /// register! {
749 ///     /// Per-CPU scratch registers.
750 ///     pub CPU_SCRATCH(u32)[64] @ CpuCtlBase + 0x00000080 {
751 ///         31:0 value;
752 ///     }
753 /// }
754 ///
755 /// # fn test(io: Mmio<'_, Region<0x1000>>) -> Result<(), Error> {
756 /// // Read scratch register 0 of CPU0.
757 /// let scratch = io.read(CPU_SCRATCH::of::<Cpu0>().at(0));
758 ///
759 /// // Write the retrieved value into scratch register 15 of CPU1.
760 /// io.write(WithBase::of::<Cpu1>().at(15), scratch);
761 ///
762 /// // This won't build.
763 /// // let cpu0_scratch_128 = io.read(CPU_SCRATCH::of::<Cpu0>().at(128)).value();
764 ///
765 /// // Runtime-obtained array index.
766 /// let scratch_idx = get_scratch_idx();
767 /// // Access on a runtime index returns an error if it is out-of-bounds.
768 /// let cpu0_scratch = io.read(
769 ///     CPU_SCRATCH::of::<Cpu0>().try_at(scratch_idx).ok_or(EINVAL)?
770 /// ).value();
771 /// # Ok(())
772 /// # }
773 ///
774 /// // Alias to `SCRATCH[8]` used to convey the firmware exit code.
775 /// register! {
776 ///     /// Per-CPU firmware exit status code.
777 ///     pub CPU_FIRMWARE_STATUS(u32) => CpuCtlBase + CPU_SCRATCH[8] {
778 ///         7:0 status;
779 ///     }
780 /// }
781 ///
782 /// // Non-contiguous relative register arrays can be defined by adding a stride parameter.
783 /// // Here, each of the 16 registers of the array is separated by 8 bytes, meaning that the
784 /// // registers of the two declarations below are interleaved.
785 /// register! {
786 ///     /// Scratch registers bank 0.
787 ///     pub CPU_SCRATCH_INTERLEAVED_0(u32)[16, stride = 8] @ CpuCtlBase + 0x00000d00 {
788 ///         31:0 value;
789 ///     }
790 ///
791 ///     /// Scratch registers bank 1.
792 ///     pub CPU_SCRATCH_INTERLEAVED_1(u32)[16, stride = 8] @ CpuCtlBase + 0x00000d04 {
793 ///         31:0 value;
794 ///     }
795 /// }
796 ///
797 /// # fn test2(io: Mmio<'_, Region<0x1000>>) -> Result<(), Error> {
798 /// let cpu0_status = io.read(CPU_FIRMWARE_STATUS::of::<Cpu0>()).status();
799 /// # Ok(())
800 /// # }
801 /// ```
802 #[macro_export]
803 macro_rules! register {
804     // Entry point for the macro, allowing multiple registers to be defined in one call.
805     // It matches all possible register declaration patterns to dispatch them to corresponding
806     // `@reg` rule that defines a single register.
807     //
808     // TODO: change `alias:ident` to `alias:path` once relative registers are replaced by I/O
809     // projections.
810     (
811         $(
812             $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
813                 $([ $size:expr $(, stride = $stride:expr)? ])?
814                 $(@ $($base:ident +)? $offset:literal)?
815                 $(=> $alias:ident $(+ $alias_offset:ident)? $([$alias_idx:expr])? )?
816             { $($fields:tt)* }
817         )*
818     ) => {
819         $(
820         $crate::register!(
821             @reg $(#[$attr])* $vis $name ($storage) $([$size $(, stride = $stride)?])?
822                 $(@ $($base +)? $offset)?
823                 $(=> $alias $(+ $alias_offset)? $([$alias_idx])? )?
824             { $($fields)* }
825         );
826         )*
827     };
828 
829     // All the rules below are private helpers.
830 
831     // Creates a register at a fixed offset of the MMIO space.
832     (
833         @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $offset:literal
834             { $($fields:tt)* }
835     ) => {
836         $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
837         $crate::register!(@io_base $name($storage) @ $offset);
838         $crate::register!(@io_fixed $(#[$attr])* $vis $name);
839     };
840 
841     // Creates an alias register of fixed offset register `alias` with its own fields.
842     (
843         @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:path
844             { $($fields:tt)* }
845     ) => {
846         $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
847         $crate::register!(
848             @io_base $name($storage) @
849             <$alias as $crate::io::register::Register>::OFFSET
850         );
851         $crate::register!(@io_fixed $(#[$attr])* $vis $name);
852     };
853 
854     // Creates a register at a relative offset from a base address provider.
855     (
856         @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $base:ident + $offset:literal
857             { $($fields:tt)* }
858     ) => {
859         $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
860         $crate::register!(@io_base $name($storage) @ $offset);
861         $crate::register!(@io_relative $name @ $base);
862     };
863 
864     // Creates an alias register of relative offset register `alias` with its own fields.
865     (
866         @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $base:ident + $alias:ident
867             { $($fields:tt)* }
868     ) => {
869         $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
870         $crate::register!(
871             @io_base $name($storage) @ <$alias as $crate::io::register::Register>::OFFSET
872         );
873         $crate::register!(@io_relative $name @ $base);
874     };
875 
876     // Creates an array of registers at a fixed offset of the MMIO space.
877     (
878         @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
879             [ $size:expr, stride = $stride:expr ] @ $offset:literal { $($fields:tt)* }
880     ) => {
881         $crate::build_assert::static_assert!(::core::mem::size_of::<$storage>() <= $stride);
882 
883         $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
884         $crate::register!(@io_base $name($storage) @ $offset);
885         $crate::register!(@io_array $name [ $size, stride = $stride ]);
886     };
887 
888     // Shortcut for contiguous array of registers (stride == size of element).
889     (
890         @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ] @ $offset:literal
891             { $($fields:tt)* }
892     ) => {
893         $crate::register!(
894             @reg $(#[$attr])* $vis $name($storage)
895                 [ $size, stride = ::core::mem::size_of::<$storage>() ]
896                 @ $offset { $($fields)* }
897         );
898     };
899 
900     // Creates an alias of register `idx` of array of registers `alias` with its own fields.
901     (
902         @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:path [ $idx:expr ]
903             { $($fields:tt)* }
904     ) => {
905         $crate::build_assert::static_assert!(
906             $idx < <$alias as $crate::io::register::RegisterArray>::SIZE
907         );
908 
909         $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
910         $crate::register!(
911             @io_base $name($storage) @
912             <$alias as $crate::io::register::Register>::OFFSET
913                 + $idx * <$alias as $crate::io::register::RegisterArray>::STRIDE
914         );
915         $crate::register!(@io_fixed $(#[$attr])* $vis $name);
916     };
917 
918     // Creates an array of registers at a relative offset from a base address provider.
919     (
920         @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
921             [ $size:expr, stride = $stride:expr ]
922             @ $base:ident + $offset:literal { $($fields:tt)* }
923     ) => {
924         $crate::build_assert::static_assert!(::core::mem::size_of::<$storage>() <= $stride);
925 
926         $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
927         $crate::register!(@io_base $name($storage) @ $offset);
928         $crate::register!(@io_relative_array $name [ $size, stride = $stride ] @ $base);
929     };
930 
931     // Shortcut for contiguous array of relative registers (stride == size of element).
932     (
933         @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ]
934             @ $base:ident + $offset:literal { $($fields:tt)* }
935     ) => {
936         $crate::register!(
937             @reg $(#[$attr])* $vis $name($storage)
938                 [ $size, stride = ::core::mem::size_of::<$storage>() ]
939                 @ $base + $offset { $($fields)* }
940         );
941     };
942 
943     // Creates an alias of register `idx` of relative array of registers `alias` with its own
944     // fields.
945     (
946         @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty)
947             => $base:ident + $alias:ident [ $idx:expr ] { $($fields:tt)* }
948     ) => {
949         $crate::build_assert::static_assert!(
950             $idx < <$alias as $crate::io::register::RegisterArray>::SIZE
951         );
952 
953         $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* });
954         $crate::register!(
955             @io_base $name($storage) @
956                 <$alias as $crate::io::register::Register>::OFFSET +
957                 $idx * <$alias as $crate::io::register::RegisterArray>::STRIDE
958         );
959         $crate::register!(@io_relative $name @ $base);
960     };
961 
962     // Generates the bitfield for the register.
963     //
964     // `#[allow(non_camel_case_types)]` is added since register names typically use
965     // `SCREAMING_CASE`.
966     (
967         @bitfield $(#[$attr:meta])* $vis:vis struct $name:ident($storage:ty) { $($fields:tt)* }
968     ) => {
969         $crate::bitfield!(
970             #[allow(non_camel_case_types)]
971             $(#[$attr])* $vis struct $name($storage) { $($fields)* }
972         );
973     };
974 
975     // Implementations shared by all registers types.
976     (@io_base $name:ident($storage:ty) @ $offset:expr) => {
977         impl $crate::io::register::Register for $name {
978             type Storage = $storage;
979 
980             const OFFSET: usize = $offset;
981         }
982     };
983 
984     // Implementations of fixed registers.
985     (@io_fixed $(#[$attr:meta])* $vis:vis $name:ident) => {
986         impl $crate::io::register::FixedRegister for $name {}
987 
988         $(#[$attr])*
989         $vis const $name: $crate::io::register::FixedRegisterLoc<$name> =
990             $crate::io::register::FixedRegisterLoc::<$name>::new();
991     };
992 
993     // Implementations of relative registers.
994     (@io_relative $name:ident @ $base:ident) => {
995         impl $crate::io::register::WithBase for $name {
996             type BaseFamily = $base;
997         }
998 
999         impl $crate::io::register::RelativeRegister for $name {}
1000     };
1001 
1002     // Implementations of register arrays.
1003     (@io_array $name:ident [ $size:expr, stride = $stride:expr ]) => {
1004         impl $crate::io::register::Array for $name {}
1005 
1006         impl $crate::io::register::RegisterArray for $name {
1007             const SIZE: usize = $size;
1008             const STRIDE: usize = $stride;
1009         }
1010     };
1011 
1012     // Implementations of relative array registers.
1013     (
1014         @io_relative_array $name:ident [ $size:expr, stride = $stride:expr ] @ $base:ident
1015     ) => {
1016         impl $crate::io::register::WithBase for $name {
1017             type BaseFamily = $base;
1018         }
1019 
1020         impl $crate::io::register::RegisterArray for $name {
1021             const SIZE: usize = $size;
1022             const STRIDE: usize = $stride;
1023         }
1024 
1025         impl $crate::io::register::RelativeRegisterArray for $name {}
1026     };
1027 }
1028