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