xref: /linux/drivers/gpu/nova-core/regs/macros.rs (revision fcdce54d645a0779892faaed1209105350e15d92)
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 /// Defines a dedicated type for a register with an absolute offset, including getter and setter
14 /// methods for its fields and methods to read and write it from an `Io` region.
15 ///
16 /// Example:
17 ///
18 /// ```no_run
19 /// register!(BOOT_0 @ 0x00000100, "Basic revision information about the GPU" {
20 ///    3:0     minor_revision as u8, "Minor revision of the chip";
21 ///    7:4     major_revision as u8, "Major revision of the chip";
22 ///    28:20   chipset as u32 ?=> Chipset, "Chipset model";
23 /// });
24 /// ```
25 ///
26 /// This defines a `BOOT_0` type which can be read or written from offset `0x100` of an `Io`
27 /// region. It is composed of 3 fields, for instance `minor_revision` is made of the 4 least
28 /// significant bits of the register. Each field can be accessed and modified using accessor
29 /// methods:
30 ///
31 /// ```no_run
32 /// // Read from the register's defined offset (0x100).
33 /// let boot0 = BOOT_0::read(&bar);
34 /// pr_info!("chip revision: {}.{}", boot0.major_revision(), boot0.minor_revision());
35 ///
36 /// // `Chipset::try_from` is called with the value of the `chipset` field and returns an
37 /// // error if it is invalid.
38 /// let chipset = boot0.chipset()?;
39 ///
40 /// // Update some fields and write the value back.
41 /// boot0.set_major_revision(3).set_minor_revision(10).write(&bar);
42 ///
43 /// // Or, just read and update the register in a single step:
44 /// BOOT_0::alter(&bar, |r| r.set_major_revision(3).set_minor_revision(10));
45 /// ```
46 ///
47 /// Fields are defined as follows:
48 ///
49 /// - `as <type>` simply returns the field value casted to <type>, typically `u32`, `u16`, `u8` or
50 ///   `bool`. Note that `bool` fields must have a range of 1 bit.
51 /// - `as <type> => <into_type>` calls `<into_type>`'s `From::<<type>>` implementation and returns
52 ///   the result.
53 /// - `as <type> ?=> <try_into_type>` calls `<try_into_type>`'s `TryFrom::<<type>>` implementation
54 ///   and returns the result. This is useful with fields for which not all values are valid.
55 ///
56 /// The documentation strings are optional. If present, they will be added to the type's
57 /// definition, or the field getter and setter methods they are attached to.
58 ///
59 /// Putting a `+` before the address of the register makes it relative to a base: the `read` and
60 /// `write` methods take a `base` argument that is added to the specified address before access:
61 ///
62 /// ```no_run
63 /// register!(CPU_CTL @ +0x0000010, "CPU core control" {
64 ///    0:0     start as bool, "Start the CPU core";
65 /// });
66 ///
67 /// // Flip the `start` switch for the CPU core which base address is at `CPU_BASE`.
68 /// let cpuctl = CPU_CTL::read(&bar, CPU_BASE);
69 /// pr_info!("CPU CTL: {:#x}", cpuctl);
70 /// cpuctl.set_start(true).write(&bar, CPU_BASE);
71 /// ```
72 ///
73 /// It is also possible to create a alias register by using the `=> ALIAS` syntax. This is useful
74 /// for cases where a register's interpretation depends on the context:
75 ///
76 /// ```no_run
77 /// register!(SCRATCH @ 0x00000200, "Scratch register" {
78 ///    31:0     value as u32, "Raw value";
79 /// });
80 ///
81 /// register!(SCRATCH_BOOT_STATUS => SCRATCH, "Boot status of the firmware" {
82 ///     0:0     completed as bool, "Whether the firmware has completed booting";
83 /// });
84 /// ```
85 ///
86 /// In this example, `SCRATCH_0_BOOT_STATUS` uses the same I/O address as `SCRATCH`, while also
87 /// providing its own `completed` field.
88 macro_rules! register {
89     // Creates a register at a fixed offset of the MMIO space.
90     ($name:ident @ $offset:literal $(, $comment:literal)? { $($fields:tt)* } ) => {
91         register!(@core $name $(, $comment)? { $($fields)* } );
92         register!(@io_fixed $name @ $offset);
93     };
94 
95     // Creates an alias register of fixed offset register `alias` with its own fields.
96     ($name:ident => $alias:ident $(, $comment:literal)? { $($fields:tt)* } ) => {
97         register!(@core $name $(, $comment)? { $($fields)* } );
98         register!(@io_fixed $name @ $alias::OFFSET);
99     };
100 
101     // Creates a register at a relative offset from a base address.
102     ($name:ident @ + $offset:literal $(, $comment:literal)? { $($fields:tt)* } ) => {
103         register!(@core $name $(, $comment)? { $($fields)* } );
104         register!(@io_relative $name @ + $offset);
105     };
106 
107     // Creates an alias register of relative offset register `alias` with its own fields.
108     ($name:ident => + $alias:ident $(, $comment:literal)? { $($fields:tt)* } ) => {
109         register!(@core $name $(, $comment)? { $($fields)* } );
110         register!(@io_relative $name @ + $alias::OFFSET);
111     };
112 
113     // All rules below are helpers.
114 
115     // Defines the wrapper `$name` type, as well as its relevant implementations (`Debug`,
116     // `Default`, `BitOr`, and conversion to the value type) and field accessor methods.
117     (@core $name:ident $(, $comment:literal)? { $($fields:tt)* }) => {
118         $(
119         #[doc=$comment]
120         )?
121         #[repr(transparent)]
122         #[derive(Clone, Copy)]
123         pub(crate) struct $name(u32);
124 
125         impl ::core::ops::BitOr for $name {
126             type Output = Self;
127 
128             fn bitor(self, rhs: Self) -> Self::Output {
129                 Self(self.0 | rhs.0)
130             }
131         }
132 
133         impl ::core::convert::From<$name> for u32 {
134             fn from(reg: $name) -> u32 {
135                 reg.0
136             }
137         }
138 
139         register!(@fields_dispatcher $name { $($fields)* });
140     };
141 
142     // Captures the fields and passes them to all the implementers that require field information.
143     //
144     // Used to simplify the matching rules for implementers, so they don't need to match the entire
145     // complex fields rule even though they only make use of part of it.
146     (@fields_dispatcher $name:ident {
147         $($hi:tt:$lo:tt $field:ident as $type:tt
148             $(?=> $try_into_type:ty)?
149             $(=> $into_type:ty)?
150             $(, $comment:literal)?
151         ;
152         )*
153     }
154     ) => {
155         register!(@field_accessors $name {
156             $(
157                 $hi:$lo $field as $type
158                 $(?=> $try_into_type)?
159                 $(=> $into_type)?
160                 $(, $comment)?
161             ;
162             )*
163         });
164         register!(@debug $name { $($field;)* });
165         register!(@default $name { $($field;)* });
166     };
167 
168     // Defines all the field getter/methods methods for `$name`.
169     (
170         @field_accessors $name:ident {
171         $($hi:tt:$lo:tt $field:ident as $type:tt
172             $(?=> $try_into_type:ty)?
173             $(=> $into_type:ty)?
174             $(, $comment:literal)?
175         ;
176         )*
177         }
178     ) => {
179         $(
180             register!(@check_field_bounds $hi:$lo $field as $type);
181         )*
182 
183         #[allow(dead_code)]
184         impl $name {
185             $(
186             register!(@field_accessor $name $hi:$lo $field as $type
187                 $(?=> $try_into_type)?
188                 $(=> $into_type)?
189                 $(, $comment)?
190                 ;
191             );
192             )*
193         }
194     };
195 
196     // Boolean fields must have `$hi == $lo`.
197     (@check_field_bounds $hi:tt:$lo:tt $field:ident as bool) => {
198         #[allow(clippy::eq_op)]
199         const _: () = {
200             ::kernel::build_assert!(
201                 $hi == $lo,
202                 concat!("boolean field `", stringify!($field), "` covers more than one bit")
203             );
204         };
205     };
206 
207     // Non-boolean fields must have `$hi >= $lo`.
208     (@check_field_bounds $hi:tt:$lo:tt $field:ident as $type:tt) => {
209         #[allow(clippy::eq_op)]
210         const _: () = {
211             ::kernel::build_assert!(
212                 $hi >= $lo,
213                 concat!("field `", stringify!($field), "`'s MSB is smaller than its LSB")
214             );
215         };
216     };
217 
218     // Catches fields defined as `bool` and convert them into a boolean value.
219     (
220         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
221             $(, $comment:literal)?;
222     ) => {
223         register!(
224             @leaf_accessor $name $hi:$lo $field
225             { |f| <$into_type>::from(if f != 0 { true } else { false }) }
226             $into_type => $into_type $(, $comment)?;
227         );
228     };
229 
230     // Shortcut for fields defined as `bool` without the `=>` syntax.
231     (
232         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool $(, $comment:literal)?;
233     ) => {
234         register!(@field_accessor $name $hi:$lo $field as bool => bool $(, $comment)?;);
235     };
236 
237     // Catches the `?=>` syntax for non-boolean fields.
238     (
239         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt ?=> $try_into_type:ty
240             $(, $comment:literal)?;
241     ) => {
242         register!(@leaf_accessor $name $hi:$lo $field
243             { |f| <$try_into_type>::try_from(f as $type) } $try_into_type =>
244             ::core::result::Result<
245                 $try_into_type,
246                 <$try_into_type as ::core::convert::TryFrom<$type>>::Error
247             >
248             $(, $comment)?;);
249     };
250 
251     // Catches the `=>` syntax for non-boolean fields.
252     (
253         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt => $into_type:ty
254             $(, $comment:literal)?;
255     ) => {
256         register!(@leaf_accessor $name $hi:$lo $field
257             { |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;);
258     };
259 
260     // Shortcut for non-boolean fields defined without the `=>` or `?=>` syntax.
261     (
262         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt
263             $(, $comment:literal)?;
264     ) => {
265         register!(@field_accessor $name $hi:$lo $field as $type => $type $(, $comment)?;);
266     };
267 
268     // Generates the accessor methods for a single field.
269     (
270         @leaf_accessor $name:ident $hi:tt:$lo:tt $field:ident
271             { $process:expr } $to_type:ty => $res_type:ty $(, $comment:literal)?;
272     ) => {
273         ::kernel::macros::paste!(
274         const [<$field:upper _RANGE>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi;
275         const [<$field:upper _MASK>]: u32 = ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);
276         const [<$field:upper _SHIFT>]: u32 = Self::[<$field:upper _MASK>].trailing_zeros();
277         );
278 
279         $(
280         #[doc="Returns the value of this field:"]
281         #[doc=$comment]
282         )?
283         #[inline]
284         pub(crate) fn $field(self) -> $res_type {
285             ::kernel::macros::paste!(
286             const MASK: u32 = $name::[<$field:upper _MASK>];
287             const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
288             );
289             let field = ((self.0 & MASK) >> SHIFT);
290 
291             $process(field)
292         }
293 
294         ::kernel::macros::paste!(
295         $(
296         #[doc="Sets the value of this field:"]
297         #[doc=$comment]
298         )?
299         #[inline]
300         pub(crate) fn [<set_ $field>](mut self, value: $to_type) -> Self {
301             const MASK: u32 = $name::[<$field:upper _MASK>];
302             const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
303             let value = (u32::from(value) << SHIFT) & MASK;
304             self.0 = (self.0 & !MASK) | value;
305 
306             self
307         }
308         );
309     };
310 
311     // Generates the `Debug` implementation for `$name`.
312     (@debug $name:ident { $($field:ident;)* }) => {
313         impl ::core::fmt::Debug for $name {
314             fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
315                 f.debug_struct(stringify!($name))
316                     .field("<raw>", &format_args!("{:#x}", &self.0))
317                 $(
318                     .field(stringify!($field), &self.$field())
319                 )*
320                     .finish()
321             }
322         }
323     };
324 
325     // Generates the `Default` implementation for `$name`.
326     (@default $name:ident { $($field:ident;)* }) => {
327         /// Returns a value for the register where all fields are set to their default value.
328         impl ::core::default::Default for $name {
329             fn default() -> Self {
330                 #[allow(unused_mut)]
331                 let mut value = Self(Default::default());
332 
333                 ::kernel::macros::paste!(
334                 $(
335                 value.[<set_ $field>](Default::default());
336                 )*
337                 );
338 
339                 value
340             }
341         }
342     };
343 
344     // Generates the IO accessors for a fixed offset register.
345     (@io_fixed $name:ident @ $offset:expr) => {
346         #[allow(dead_code)]
347         impl $name {
348             pub(crate) const OFFSET: usize = $offset;
349 
350             /// Read the register from its address in `io`.
351             #[inline]
352             pub(crate) fn read<const SIZE: usize, T>(io: &T) -> Self where
353                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
354             {
355                 Self(io.read32($offset))
356             }
357 
358             /// Write the value contained in `self` to the register address in `io`.
359             #[inline]
360             pub(crate) fn write<const SIZE: usize, T>(self, io: &T) where
361                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
362             {
363                 io.write32(self.0, $offset)
364             }
365 
366             /// Read the register from its address in `io` and run `f` on its value to obtain a new
367             /// value to write back.
368             #[inline]
369             pub(crate) fn alter<const SIZE: usize, T, F>(
370                 io: &T,
371                 f: F,
372             ) where
373                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
374                 F: ::core::ops::FnOnce(Self) -> Self,
375             {
376                 let reg = f(Self::read(io));
377                 reg.write(io);
378             }
379         }
380     };
381 
382     // Generates the IO accessors for a relative offset register.
383     (@io_relative $name:ident @ + $offset:literal) => {
384         #[allow(dead_code)]
385         impl $name {
386             pub(crate) const OFFSET: usize = $offset;
387 
388             #[inline]
389             pub(crate) fn read<const SIZE: usize, T>(
390                 io: &T,
391                 base: usize,
392             ) -> Self where
393                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
394             {
395                 Self(io.read32(base + $offset))
396             }
397 
398             #[inline]
399             pub(crate) fn write<const SIZE: usize, T>(
400                 self,
401                 io: &T,
402                 base: usize,
403             ) where
404                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
405             {
406                 io.write32(self.0, base + $offset)
407             }
408 
409             #[inline]
410             pub(crate) fn alter<const SIZE: usize, T, F>(
411                 io: &T,
412                 base: usize,
413                 f: F,
414             ) where
415                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
416                 F: ::core::ops::FnOnce(Self) -> Self,
417             {
418                 let reg = f(Self::read(io, base));
419                 reg.write(io, base);
420             }
421         }
422     };
423 }
424