xref: /linux/drivers/gpu/nova-core/regs/macros.rs (revision 352af6a011d586ff042db4b2d1f7421875eb8a14)
1 // SPDX-License-Identifier: GPL-2.0
2 
3 //! 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 macro in this module allow to define, using an intruitive and readable syntax, a dedicated
10 //! type for each register with its own field accessors that can return an error is a field's value
11 //! is invalid.
12 
13 /// Defines a dedicated type for a register with an absolute offset, alongside with getter and
14 /// setter 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 less
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` will be called with the value of the field and returns an error if the
37 /// // value 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 can be defined as follows:
48 ///
49 /// - `as <type>` simply returns the field value casted as the requested integer type, typically
50 ///   `u32`, `u16`, `u8` or `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 on fields for which not all values are value.
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 /// and `try_read` and `try_write` methods are also created, allowing access with offsets unknown
62 /// at compile-time:
63 ///
64 /// ```no_run
65 /// register!(CPU_CTL @ +0x0000010, "CPU core control" {
66 ///    0:0     start as bool, "Start the CPU core";
67 /// });
68 ///
69 /// // Flip the `start` switch for the CPU core which base address is at `CPU_BASE`.
70 /// let cpuctl = CPU_CTL::read(&bar, CPU_BASE);
71 /// pr_info!("CPU CTL: {:#x}", cpuctl);
72 /// cpuctl.set_start(true).write(&bar, CPU_BASE);
73 /// ```
74 ///
75 /// It is also possible to create a alias register by using the `=> ALIAS` syntax. This is useful
76 /// for cases where a register's interpretation depends on the context:
77 ///
78 /// ```no_run
79 /// register!(SCRATCH_0 @ 0x0000100, "Scratch register 0" {
80 ///    31:0     value as u32, "Raw value";
81 ///
82 /// register!(SCRATCH_0_BOOT_STATUS => SCRATCH_0, "Boot status of the firmware" {
83 ///     0:0     completed as bool, "Whether the firmware has completed booting";
84 /// ```
85 ///
86 /// In this example, `SCRATCH_0_BOOT_STATUS` uses the same I/O address as `SCRATCH_0`, while also
87 /// providing its own `completed` method.
88 macro_rules! register {
89     // Creates a register at a fixed offset of the MMIO space.
90     (
91         $name:ident @ $offset:literal $(, $comment:literal)? {
92             $($fields:tt)*
93         }
94     ) => {
95         register!(@common $name @ $offset $(, $comment)?);
96         register!(@field_accessors $name { $($fields)* });
97         register!(@io $name @ $offset);
98     };
99 
100     // Creates a alias register of fixed offset register `alias` with its own fields.
101     (
102         $name:ident => $alias:ident $(, $comment:literal)? {
103             $($fields:tt)*
104         }
105     ) => {
106         register!(@common $name @ $alias::OFFSET $(, $comment)?);
107         register!(@field_accessors $name { $($fields)* });
108         register!(@io $name @ $alias::OFFSET);
109     };
110 
111     // Creates a register at a relative offset from a base address.
112     (
113         $name:ident @ + $offset:literal $(, $comment:literal)? {
114             $($fields:tt)*
115         }
116     ) => {
117         register!(@common $name @ $offset $(, $comment)?);
118         register!(@field_accessors $name { $($fields)* });
119         register!(@io$name @ + $offset);
120     };
121 
122     // Creates a alias register of relative offset register `alias` with its own fields.
123     (
124         $name:ident => + $alias:ident $(, $comment:literal)? {
125             $($fields:tt)*
126         }
127     ) => {
128         register!(@common $name @ $alias::OFFSET $(, $comment)?);
129         register!(@field_accessors $name { $($fields)* });
130         register!(@io $name @ + $alias::OFFSET);
131     };
132 
133     // All rules below are helpers.
134 
135     // Defines the wrapper `$name` type, as well as its relevant implementations (`Debug`, `BitOr`,
136     // and conversion to regular `u32`).
137     (@common $name:ident @ $offset:expr $(, $comment:literal)?) => {
138         $(
139         #[doc=$comment]
140         )?
141         #[repr(transparent)]
142         #[derive(Clone, Copy, Default)]
143         pub(crate) struct $name(u32);
144 
145         #[allow(dead_code)]
146         impl $name {
147             pub(crate) const OFFSET: usize = $offset;
148         }
149 
150         // TODO[REGA]: display the raw hex value, then the value of all the fields. This requires
151         // matching the fields, which will complexify the syntax considerably...
152         impl ::core::fmt::Debug for $name {
153             fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
154                 f.debug_tuple(stringify!($name))
155                     .field(&format_args!("0x{0:x}", &self.0))
156                     .finish()
157             }
158         }
159 
160         impl ::core::ops::BitOr for $name {
161             type Output = Self;
162 
163             fn bitor(self, rhs: Self) -> Self::Output {
164                 Self(self.0 | rhs.0)
165             }
166         }
167 
168         impl ::core::convert::From<$name> for u32 {
169             fn from(reg: $name) -> u32 {
170                 reg.0
171             }
172         }
173     };
174 
175     // Defines all the field getter/methods methods for `$name`.
176     (
177         @field_accessors $name:ident {
178         $($hi:tt:$lo:tt $field:ident as $type:tt
179             $(?=> $try_into_type:ty)?
180             $(=> $into_type:ty)?
181             $(, $comment:literal)?
182         ;
183         )*
184         }
185     ) => {
186         $(
187             register!(@check_field_bounds $hi:$lo $field as $type);
188         )*
189 
190         #[allow(dead_code)]
191         impl $name {
192             $(
193             register!(@field_accessor $name $hi:$lo $field as $type
194                 $(?=> $try_into_type)?
195                 $(=> $into_type)?
196                 $(, $comment)?
197                 ;
198             );
199             )*
200         }
201     };
202 
203     // Boolean fields must have `$hi == $lo`.
204     (@check_field_bounds $hi:tt:$lo:tt $field:ident as bool) => {
205         #[allow(clippy::eq_op)]
206         const _: () = {
207             ::kernel::build_assert!(
208                 $hi == $lo,
209                 concat!("boolean field `", stringify!($field), "` covers more than one bit")
210             );
211         };
212     };
213 
214     // Non-boolean fields must have `$hi >= $lo`.
215     (@check_field_bounds $hi:tt:$lo:tt $field:ident as $type:tt) => {
216         #[allow(clippy::eq_op)]
217         const _: () = {
218             ::kernel::build_assert!(
219                 $hi >= $lo,
220                 concat!("field `", stringify!($field), "`'s MSB is smaller than its LSB")
221             );
222         };
223     };
224 
225     // Catches fields defined as `bool` and convert them into a boolean value.
226     (
227         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
228             $(, $comment:literal)?;
229     ) => {
230         register!(
231             @leaf_accessor $name $hi:$lo $field as bool
232             { |f| <$into_type>::from(if f != 0 { true } else { false }) }
233             $into_type => $into_type $(, $comment)?;
234         );
235     };
236 
237     // Shortcut for fields defined as `bool` without the `=>` syntax.
238     (
239         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool $(, $comment:literal)?;
240     ) => {
241         register!(@field_accessor $name $hi:$lo $field as bool => bool $(, $comment)?;);
242     };
243 
244     // Catches the `?=>` syntax for non-boolean fields.
245     (
246         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt ?=> $try_into_type:ty
247             $(, $comment:literal)?;
248     ) => {
249         register!(@leaf_accessor $name $hi:$lo $field as $type
250             { |f| <$try_into_type>::try_from(f as $type) } $try_into_type =>
251             ::core::result::Result<
252                 $try_into_type,
253                 <$try_into_type as ::core::convert::TryFrom<$type>>::Error
254             >
255             $(, $comment)?;);
256     };
257 
258     // Catches the `=>` syntax for non-boolean fields.
259     (
260         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt => $into_type:ty
261             $(, $comment:literal)?;
262     ) => {
263         register!(@leaf_accessor $name $hi:$lo $field as $type
264             { |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;);
265     };
266 
267     // Shortcut for fields defined as non-`bool` without the `=>` or `?=>` syntax.
268     (
269         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt
270             $(, $comment:literal)?;
271     ) => {
272         register!(@field_accessor $name $hi:$lo $field as $type => $type $(, $comment)?;);
273     };
274 
275     // Generates the accessor methods for a single field.
276     (
277         @leaf_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:ty
278             { $process:expr } $to_type:ty => $res_type:ty $(, $comment:literal)?;
279     ) => {
280         ::kernel::macros::paste!(
281         const [<$field:upper>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi;
282         const [<$field:upper _MASK>]: u32 = ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);
283         const [<$field:upper _SHIFT>]: u32 = Self::[<$field:upper _MASK>].trailing_zeros();
284         );
285 
286         $(
287         #[doc="Returns the value of this field:"]
288         #[doc=$comment]
289         )?
290         #[inline]
291         pub(crate) fn $field(self) -> $res_type {
292             ::kernel::macros::paste!(
293             const MASK: u32 = $name::[<$field:upper _MASK>];
294             const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
295             );
296             let field = ((self.0 & MASK) >> SHIFT);
297 
298             $process(field)
299         }
300 
301         ::kernel::macros::paste!(
302         $(
303         #[doc="Sets the value of this field:"]
304         #[doc=$comment]
305         )?
306         #[inline]
307         pub(crate) fn [<set_ $field>](mut self, value: $to_type) -> Self {
308             const MASK: u32 = $name::[<$field:upper _MASK>];
309             const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
310             let value = (u32::from(value) << SHIFT) & MASK;
311             self.0 = (self.0 & !MASK) | value;
312 
313             self
314         }
315         );
316     };
317 
318     // Creates the IO accessors for a fixed offset register.
319     (@io $name:ident @ $offset:expr) => {
320         #[allow(dead_code)]
321         impl $name {
322             #[inline]
323             pub(crate) fn read<const SIZE: usize, T>(io: &T) -> Self where
324                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
325             {
326                 Self(io.read32($offset))
327             }
328 
329             #[inline]
330             pub(crate) fn write<const SIZE: usize, T>(self, io: &T) where
331                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
332             {
333                 io.write32(self.0, $offset)
334             }
335 
336             #[inline]
337             pub(crate) fn alter<const SIZE: usize, T, F>(
338                 io: &T,
339                 f: F,
340             ) where
341                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
342                 F: ::core::ops::FnOnce(Self) -> Self,
343             {
344                 let reg = f(Self::read(io));
345                 reg.write(io);
346             }
347         }
348     };
349 
350     // Create the IO accessors for a relative offset register.
351     (@io $name:ident @ + $offset:literal) => {
352         #[allow(dead_code)]
353         impl $name {
354             #[inline]
355             pub(crate) fn read<const SIZE: usize, T>(
356                 io: &T,
357                 base: usize,
358             ) -> Self where
359                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
360             {
361                 Self(io.read32(base + $offset))
362             }
363 
364             #[inline]
365             pub(crate) fn write<const SIZE: usize, T>(
366                 self,
367                 io: &T,
368                 base: usize,
369             ) where
370                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
371             {
372                 io.write32(self.0, base + $offset)
373             }
374 
375             #[inline]
376             pub(crate) fn alter<const SIZE: usize, T, F>(
377                 io: &T,
378                 base: usize,
379                 f: F,
380             ) where
381                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
382                 F: ::core::ops::FnOnce(Self) -> Self,
383             {
384                 let reg = f(Self::read(io, base));
385                 reg.write(io, base);
386             }
387 
388             #[inline]
389             pub(crate) fn try_read<const SIZE: usize, T>(
390                 io: &T,
391                 base: usize,
392             ) -> ::kernel::error::Result<Self> where
393                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
394             {
395                 io.try_read32(base + $offset).map(Self)
396             }
397 
398             #[inline]
399             pub(crate) fn try_write<const SIZE: usize, T>(
400                 self,
401                 io: &T,
402                 base: usize,
403             ) -> ::kernel::error::Result<()> where
404                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
405             {
406                 io.try_write32(self.0, base + $offset)
407             }
408 
409             #[inline]
410             pub(crate) fn try_alter<const SIZE: usize, T, F>(
411                 io: &T,
412                 base: usize,
413                 f: F,
414             ) -> ::kernel::error::Result<()> where
415                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
416                 F: ::core::ops::FnOnce(Self) -> Self,
417             {
418                 let reg = f(Self::try_read(io, base)?);
419                 reg.try_write(io, base)
420             }
421         }
422     };
423 }
424