xref: /linux/drivers/gpu/nova-core/regs/macros.rs (revision af10924fc471d1c693b8689249f53ea10b0519b7)
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.
12 
13 /// Trait providing a base address to be added to the offset of a relative register to obtain
14 /// its actual offset.
15 ///
16 /// The `T` generic argument is used to distinguish which base to use, in case a type provides
17 /// several bases. It is given to the `register!` macro to restrict the use of the register to
18 /// implementors of this particular variant.
19 pub(crate) trait RegisterBase<T> {
20     const BASE: usize;
21 }
22 
23 /// Defines a dedicated type for a register with an absolute offset, including getter and setter
24 /// methods for its fields and methods to read and write it from an `Io` region.
25 ///
26 /// Example:
27 ///
28 /// ```no_run
29 /// register!(BOOT_0 @ 0x00000100, "Basic revision information about the GPU" {
30 ///    3:0     minor_revision as u8, "Minor revision of the chip";
31 ///    7:4     major_revision as u8, "Major revision of the chip";
32 ///    28:20   chipset as u32 ?=> Chipset, "Chipset model";
33 /// });
34 /// ```
35 ///
36 /// This defines a `BOOT_0` type which can be read or written from offset `0x100` of an `Io`
37 /// region. It is composed of 3 fields, for instance `minor_revision` is made of the 4 least
38 /// significant bits of the register. Each field can be accessed and modified using accessor
39 /// methods:
40 ///
41 /// ```no_run
42 /// // Read from the register's defined offset (0x100).
43 /// let boot0 = BOOT_0::read(&bar);
44 /// pr_info!("chip revision: {}.{}", boot0.major_revision(), boot0.minor_revision());
45 ///
46 /// // `Chipset::try_from` is called with the value of the `chipset` field and returns an
47 /// // error if it is invalid.
48 /// let chipset = boot0.chipset()?;
49 ///
50 /// // Update some fields and write the value back.
51 /// boot0.set_major_revision(3).set_minor_revision(10).write(&bar);
52 ///
53 /// // Or, just read and update the register in a single step:
54 /// BOOT_0::alter(&bar, |r| r.set_major_revision(3).set_minor_revision(10));
55 /// ```
56 ///
57 /// Fields are defined as follows:
58 ///
59 /// - `as <type>` simply returns the field value casted to <type>, typically `u32`, `u16`, `u8` or
60 ///   `bool`. Note that `bool` fields must have a range of 1 bit.
61 /// - `as <type> => <into_type>` calls `<into_type>`'s `From::<<type>>` implementation and returns
62 ///   the result.
63 /// - `as <type> ?=> <try_into_type>` calls `<try_into_type>`'s `TryFrom::<<type>>` implementation
64 ///   and returns the result. This is useful with fields for which not all values are valid.
65 ///
66 /// The documentation strings are optional. If present, they will be added to the type's
67 /// definition, or the field getter and setter methods they are attached to.
68 ///
69 /// It is also possible to create a alias register by using the `=> ALIAS` syntax. This is useful
70 /// for cases where a register's interpretation depends on the context:
71 ///
72 /// ```no_run
73 /// register!(SCRATCH @ 0x00000200, "Scratch register" {
74 ///    31:0     value as u32, "Raw value";
75 /// });
76 ///
77 /// register!(SCRATCH_BOOT_STATUS => SCRATCH, "Boot status of the firmware" {
78 ///     0:0     completed as bool, "Whether the firmware has completed booting";
79 /// });
80 /// ```
81 ///
82 /// In this example, `SCRATCH_0_BOOT_STATUS` uses the same I/O address as `SCRATCH`, while also
83 /// providing its own `completed` field.
84 ///
85 /// ## Relative registers
86 ///
87 /// A register can be defined as being accessible from a fixed offset of a provided base. For
88 /// instance, imagine the following I/O space:
89 ///
90 /// ```text
91 ///           +-----------------------------+
92 ///           |             ...             |
93 ///           |                             |
94 ///  0x100--->+------------CPU0-------------+
95 ///           |                             |
96 ///  0x110--->+-----------------------------+
97 ///           |           CPU_CTL           |
98 ///           +-----------------------------+
99 ///           |             ...             |
100 ///           |                             |
101 ///           |                             |
102 ///  0x200--->+------------CPU1-------------+
103 ///           |                             |
104 ///  0x210--->+-----------------------------+
105 ///           |           CPU_CTL           |
106 ///           +-----------------------------+
107 ///           |             ...             |
108 ///           +-----------------------------+
109 /// ```
110 ///
111 /// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset `0x10` of their I/O
112 /// space segment. Since both instances of `CPU_CTL` share the same layout, we don't want to define
113 /// them twice and would prefer a way to select which one to use from a single definition
114 ///
115 /// This can be done using the `Base[Offset]` syntax when specifying the register's address.
116 ///
117 /// `Base` is an arbitrary type (typically a ZST) to be used as a generic parameter of the
118 /// [`RegisterBase`] trait to provide the base as a constant, i.e. each type providing a base for
119 /// this register needs to implement `RegisterBase<Base>`. Here is the above example translated
120 /// into code:
121 ///
122 /// ```no_run
123 /// // Type used to identify the base.
124 /// pub(crate) struct CpuCtlBase;
125 ///
126 /// // ZST describing `CPU0`.
127 /// struct Cpu0;
128 /// impl RegisterBase<CpuCtlBase> for Cpu0 {
129 ///     const BASE: usize = 0x100;
130 /// }
131 /// // Singleton of `CPU0` used to identify it.
132 /// const CPU0: Cpu0 = Cpu0;
133 ///
134 /// // ZST describing `CPU1`.
135 /// struct Cpu1;
136 /// impl RegisterBase<CpuCtlBase> for Cpu1 {
137 ///     const BASE: usize = 0x200;
138 /// }
139 /// // Singleton of `CPU1` used to identify it.
140 /// const CPU1: Cpu1 = Cpu1;
141 ///
142 /// // This makes `CPU_CTL` accessible from all implementors of `RegisterBase<CpuCtlBase>`.
143 /// register!(CPU_CTL @ CpuCtlBase[0x10], "CPU core control" {
144 ///     0:0     start as bool, "Start the CPU core";
145 /// });
146 ///
147 /// // The `read`, `write` and `alter` methods of relative registers take an extra `base` argument
148 /// // that is used to resolve its final address by adding its `BASE` to the offset of the
149 /// // register.
150 ///
151 /// // Start `CPU0`.
152 /// CPU_CTL::alter(bar, &CPU0, |r| r.set_start(true));
153 ///
154 /// // Start `CPU1`.
155 /// CPU_CTL::alter(bar, &CPU1, |r| r.set_start(true));
156 ///
157 /// // Aliases can also be defined for relative register.
158 /// register!(CPU_CTL_ALIAS => CpuCtlBase[CPU_CTL], "Alias to CPU core control" {
159 ///     1:1     alias_start as bool, "Start the aliased CPU core";
160 /// });
161 ///
162 /// // Start the aliased `CPU0`.
163 /// CPU_CTL_ALIAS::alter(bar, &CPU0, |r| r.set_alias_start(true));
164 /// ```
165 macro_rules! register {
166     // Creates a register at a fixed offset of the MMIO space.
167     ($name:ident @ $offset:literal $(, $comment:literal)? { $($fields:tt)* } ) => {
168         register!(@core $name $(, $comment)? { $($fields)* } );
169         register!(@io_fixed $name @ $offset);
170     };
171 
172     // Creates an alias register of fixed offset register `alias` with its own fields.
173     ($name:ident => $alias:ident $(, $comment:literal)? { $($fields:tt)* } ) => {
174         register!(@core $name $(, $comment)? { $($fields)* } );
175         register!(@io_fixed $name @ $alias::OFFSET);
176     };
177 
178     // Creates a register at a relative offset from a base address provider.
179     ($name:ident @ $base:ty [ $offset:literal ] $(, $comment:literal)? { $($fields:tt)* } ) => {
180         register!(@core $name $(, $comment)? { $($fields)* } );
181         register!(@io_relative $name @ $base [ $offset ]);
182     };
183 
184     // Creates an alias register of relative offset register `alias` with its own fields.
185     ($name:ident => $base:ty [ $alias:ident ] $(, $comment:literal)? { $($fields:tt)* }) => {
186         register!(@core $name $(, $comment)? { $($fields)* } );
187         register!(@io_relative $name @ $base [ $alias::OFFSET ]);
188     };
189 
190     // All rules below are helpers.
191 
192     // Defines the wrapper `$name` type, as well as its relevant implementations (`Debug`,
193     // `Default`, `BitOr`, and conversion to the value type) and field accessor methods.
194     (@core $name:ident $(, $comment:literal)? { $($fields:tt)* }) => {
195         $(
196         #[doc=$comment]
197         )?
198         #[repr(transparent)]
199         #[derive(Clone, Copy)]
200         pub(crate) struct $name(u32);
201 
202         impl ::core::ops::BitOr for $name {
203             type Output = Self;
204 
205             fn bitor(self, rhs: Self) -> Self::Output {
206                 Self(self.0 | rhs.0)
207             }
208         }
209 
210         impl ::core::convert::From<$name> for u32 {
211             fn from(reg: $name) -> u32 {
212                 reg.0
213             }
214         }
215 
216         register!(@fields_dispatcher $name { $($fields)* });
217     };
218 
219     // Captures the fields and passes them to all the implementers that require field information.
220     //
221     // Used to simplify the matching rules for implementers, so they don't need to match the entire
222     // complex fields rule even though they only make use of part of it.
223     (@fields_dispatcher $name:ident {
224         $($hi:tt:$lo:tt $field:ident as $type:tt
225             $(?=> $try_into_type:ty)?
226             $(=> $into_type:ty)?
227             $(, $comment:literal)?
228         ;
229         )*
230     }
231     ) => {
232         register!(@field_accessors $name {
233             $(
234                 $hi:$lo $field as $type
235                 $(?=> $try_into_type)?
236                 $(=> $into_type)?
237                 $(, $comment)?
238             ;
239             )*
240         });
241         register!(@debug $name { $($field;)* });
242         register!(@default $name { $($field;)* });
243     };
244 
245     // Defines all the field getter/methods methods for `$name`.
246     (
247         @field_accessors $name:ident {
248         $($hi:tt:$lo:tt $field:ident as $type:tt
249             $(?=> $try_into_type:ty)?
250             $(=> $into_type:ty)?
251             $(, $comment:literal)?
252         ;
253         )*
254         }
255     ) => {
256         $(
257             register!(@check_field_bounds $hi:$lo $field as $type);
258         )*
259 
260         #[allow(dead_code)]
261         impl $name {
262             $(
263             register!(@field_accessor $name $hi:$lo $field as $type
264                 $(?=> $try_into_type)?
265                 $(=> $into_type)?
266                 $(, $comment)?
267                 ;
268             );
269             )*
270         }
271     };
272 
273     // Boolean fields must have `$hi == $lo`.
274     (@check_field_bounds $hi:tt:$lo:tt $field:ident as bool) => {
275         #[allow(clippy::eq_op)]
276         const _: () = {
277             ::kernel::build_assert!(
278                 $hi == $lo,
279                 concat!("boolean field `", stringify!($field), "` covers more than one bit")
280             );
281         };
282     };
283 
284     // Non-boolean fields must have `$hi >= $lo`.
285     (@check_field_bounds $hi:tt:$lo:tt $field:ident as $type:tt) => {
286         #[allow(clippy::eq_op)]
287         const _: () = {
288             ::kernel::build_assert!(
289                 $hi >= $lo,
290                 concat!("field `", stringify!($field), "`'s MSB is smaller than its LSB")
291             );
292         };
293     };
294 
295     // Catches fields defined as `bool` and convert them into a boolean value.
296     (
297         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
298             $(, $comment:literal)?;
299     ) => {
300         register!(
301             @leaf_accessor $name $hi:$lo $field
302             { |f| <$into_type>::from(if f != 0 { true } else { false }) }
303             $into_type => $into_type $(, $comment)?;
304         );
305     };
306 
307     // Shortcut for fields defined as `bool` without the `=>` syntax.
308     (
309         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool $(, $comment:literal)?;
310     ) => {
311         register!(@field_accessor $name $hi:$lo $field as bool => bool $(, $comment)?;);
312     };
313 
314     // Catches the `?=>` syntax for non-boolean fields.
315     (
316         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt ?=> $try_into_type:ty
317             $(, $comment:literal)?;
318     ) => {
319         register!(@leaf_accessor $name $hi:$lo $field
320             { |f| <$try_into_type>::try_from(f as $type) } $try_into_type =>
321             ::core::result::Result<
322                 $try_into_type,
323                 <$try_into_type as ::core::convert::TryFrom<$type>>::Error
324             >
325             $(, $comment)?;);
326     };
327 
328     // Catches the `=>` syntax for non-boolean fields.
329     (
330         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt => $into_type:ty
331             $(, $comment:literal)?;
332     ) => {
333         register!(@leaf_accessor $name $hi:$lo $field
334             { |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;);
335     };
336 
337     // Shortcut for non-boolean fields defined without the `=>` or `?=>` syntax.
338     (
339         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt
340             $(, $comment:literal)?;
341     ) => {
342         register!(@field_accessor $name $hi:$lo $field as $type => $type $(, $comment)?;);
343     };
344 
345     // Generates the accessor methods for a single field.
346     (
347         @leaf_accessor $name:ident $hi:tt:$lo:tt $field:ident
348             { $process:expr } $to_type:ty => $res_type:ty $(, $comment:literal)?;
349     ) => {
350         ::kernel::macros::paste!(
351         const [<$field:upper _RANGE>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi;
352         const [<$field:upper _MASK>]: u32 = ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);
353         const [<$field:upper _SHIFT>]: u32 = Self::[<$field:upper _MASK>].trailing_zeros();
354         );
355 
356         $(
357         #[doc="Returns the value of this field:"]
358         #[doc=$comment]
359         )?
360         #[inline(always)]
361         pub(crate) fn $field(self) -> $res_type {
362             ::kernel::macros::paste!(
363             const MASK: u32 = $name::[<$field:upper _MASK>];
364             const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
365             );
366             let field = ((self.0 & MASK) >> SHIFT);
367 
368             $process(field)
369         }
370 
371         ::kernel::macros::paste!(
372         $(
373         #[doc="Sets the value of this field:"]
374         #[doc=$comment]
375         )?
376         #[inline(always)]
377         pub(crate) fn [<set_ $field>](mut self, value: $to_type) -> Self {
378             const MASK: u32 = $name::[<$field:upper _MASK>];
379             const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
380             let value = (u32::from(value) << SHIFT) & MASK;
381             self.0 = (self.0 & !MASK) | value;
382 
383             self
384         }
385         );
386     };
387 
388     // Generates the `Debug` implementation for `$name`.
389     (@debug $name:ident { $($field:ident;)* }) => {
390         impl ::core::fmt::Debug for $name {
391             fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
392                 f.debug_struct(stringify!($name))
393                     .field("<raw>", &format_args!("{:#x}", &self.0))
394                 $(
395                     .field(stringify!($field), &self.$field())
396                 )*
397                     .finish()
398             }
399         }
400     };
401 
402     // Generates the `Default` implementation for `$name`.
403     (@default $name:ident { $($field:ident;)* }) => {
404         /// Returns a value for the register where all fields are set to their default value.
405         impl ::core::default::Default for $name {
406             fn default() -> Self {
407                 #[allow(unused_mut)]
408                 let mut value = Self(Default::default());
409 
410                 ::kernel::macros::paste!(
411                 $(
412                 value.[<set_ $field>](Default::default());
413                 )*
414                 );
415 
416                 value
417             }
418         }
419     };
420 
421     // Generates the IO accessors for a fixed offset register.
422     (@io_fixed $name:ident @ $offset:expr) => {
423         #[allow(dead_code)]
424         impl $name {
425             pub(crate) const OFFSET: usize = $offset;
426 
427             /// Read the register from its address in `io`.
428             #[inline(always)]
429             pub(crate) fn read<const SIZE: usize, T>(io: &T) -> Self where
430                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
431             {
432                 Self(io.read32($offset))
433             }
434 
435             /// Write the value contained in `self` to the register address in `io`.
436             #[inline(always)]
437             pub(crate) fn write<const SIZE: usize, T>(self, io: &T) where
438                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
439             {
440                 io.write32(self.0, $offset)
441             }
442 
443             /// Read the register from its address in `io` and run `f` on its value to obtain a new
444             /// value to write back.
445             #[inline(always)]
446             pub(crate) fn alter<const SIZE: usize, T, F>(
447                 io: &T,
448                 f: F,
449             ) where
450                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
451                 F: ::core::ops::FnOnce(Self) -> Self,
452             {
453                 let reg = f(Self::read(io));
454                 reg.write(io);
455             }
456         }
457     };
458 
459     // Generates the IO accessors for a relative offset register.
460     (@io_relative $name:ident @ $base:ty [ $offset:expr ]) => {
461         #[allow(dead_code)]
462         impl $name {
463             pub(crate) const OFFSET: usize = $offset;
464 
465             /// Read the register from `io`, using the base address provided by `base` and adding
466             /// the register's offset to it.
467             #[inline(always)]
468             pub(crate) fn read<const SIZE: usize, T, B>(
469                 io: &T,
470                 #[allow(unused_variables)]
471                 base: &B,
472             ) -> Self where
473                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
474                 B: crate::regs::macros::RegisterBase<$base>,
475             {
476                 const OFFSET: usize = $name::OFFSET;
477 
478                 let value = io.read32(
479                     <B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET
480                 );
481 
482                 Self(value)
483             }
484 
485             /// Write the value contained in `self` to `io`, using the base address provided by
486             /// `base` and adding the register's offset to it.
487             #[inline(always)]
488             pub(crate) fn write<const SIZE: usize, T, B>(
489                 self,
490                 io: &T,
491                 #[allow(unused_variables)]
492                 base: &B,
493             ) where
494                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
495                 B: crate::regs::macros::RegisterBase<$base>,
496             {
497                 const OFFSET: usize = $name::OFFSET;
498 
499                 io.write32(
500                     self.0,
501                     <B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET
502                 );
503             }
504 
505             /// Read the register from `io`, using the base address provided by `base` and adding
506             /// the register's offset to it, then run `f` on its value to obtain a new value to
507             /// write back.
508             #[inline(always)]
509             pub(crate) fn alter<const SIZE: usize, T, B, F>(
510                 io: &T,
511                 base: &B,
512                 f: F,
513             ) where
514                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
515                 B: crate::regs::macros::RegisterBase<$base>,
516                 F: ::core::ops::FnOnce(Self) -> Self,
517             {
518                 let reg = f(Self::read(io, base));
519                 reg.write(io, base);
520             }
521         }
522     };
523 }
524