xref: /linux/drivers/gpu/nova-core/regs/macros.rs (revision a713222906e4f77b5fb1b5346d4f5de1adc639b4)
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 macro_rules! register {
75     // Creates a register at a fixed offset of the MMIO space.
76     (
77         $name:ident @ $offset:literal $(, $comment:literal)? {
78             $($fields:tt)*
79         }
80     ) => {
81         register!(@common $name $(, $comment)?);
82         register!(@field_accessors $name { $($fields)* });
83         register!(@io $name @ $offset);
84     };
85 
86     // Creates a register at a relative offset from a base address.
87     (
88         $name:ident @ + $offset:literal $(, $comment:literal)? {
89             $($fields:tt)*
90         }
91     ) => {
92         register!(@common $name $(, $comment)?);
93         register!(@field_accessors $name { $($fields)* });
94         register!(@io$name @ + $offset);
95     };
96 
97     // Defines the wrapper `$name` type, as well as its relevant implementations (`Debug`, `BitOr`,
98     // and conversion to regular `u32`).
99     (@common $name:ident $(, $comment:literal)?) => {
100         $(
101         #[doc=$comment]
102         )?
103         #[repr(transparent)]
104         #[derive(Clone, Copy, Default)]
105         pub(crate) struct $name(u32);
106 
107         // TODO: display the raw hex value, then the value of all the fields. This requires
108         // matching the fields, which will complexify the syntax considerably...
109         impl ::core::fmt::Debug for $name {
110             fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
111                 f.debug_tuple(stringify!($name))
112                     .field(&format_args!("0x{0:x}", &self.0))
113                     .finish()
114             }
115         }
116 
117         impl core::ops::BitOr for $name {
118             type Output = Self;
119 
120             fn bitor(self, rhs: Self) -> Self::Output {
121                 Self(self.0 | rhs.0)
122             }
123         }
124 
125         impl ::core::convert::From<$name> for u32 {
126             fn from(reg: $name) -> u32 {
127                 reg.0
128             }
129         }
130     };
131 
132     // Defines all the field getter/methods methods for `$name`.
133     (
134         @field_accessors $name:ident {
135         $($hi:tt:$lo:tt $field:ident as $type:tt
136             $(?=> $try_into_type:ty)?
137             $(=> $into_type:ty)?
138             $(, $comment:literal)?
139         ;
140         )*
141         }
142     ) => {
143         $(
144             register!(@check_field_bounds $hi:$lo $field as $type);
145         )*
146 
147         #[allow(dead_code)]
148         impl $name {
149             $(
150             register!(@field_accessor $name $hi:$lo $field as $type
151                 $(?=> $try_into_type)?
152                 $(=> $into_type)?
153                 $(, $comment)?
154                 ;
155             );
156             )*
157         }
158     };
159 
160     // Boolean fields must have `$hi == $lo`.
161     (@check_field_bounds $hi:tt:$lo:tt $field:ident as bool) => {
162         #[allow(clippy::eq_op)]
163         const _: () = {
164             kernel::build_assert!(
165                 $hi == $lo,
166                 concat!("boolean field `", stringify!($field), "` covers more than one bit")
167             );
168         };
169     };
170 
171     // Non-boolean fields must have `$hi >= $lo`.
172     (@check_field_bounds $hi:tt:$lo:tt $field:ident as $type:tt) => {
173         #[allow(clippy::eq_op)]
174         const _: () = {
175             kernel::build_assert!(
176                 $hi >= $lo,
177                 concat!("field `", stringify!($field), "`'s MSB is smaller than its LSB")
178             );
179         };
180     };
181 
182     // Catches fields defined as `bool` and convert them into a boolean value.
183     (
184         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool => $into_type:ty
185             $(, $comment:literal)?;
186     ) => {
187         register!(
188             @leaf_accessor $name $hi:$lo $field as bool
189             { |f| <$into_type>::from(if f != 0 { true } else { false }) }
190             $into_type => $into_type $(, $comment)?;
191         );
192     };
193 
194     // Shortcut for fields defined as `bool` without the `=>` syntax.
195     (
196         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool $(, $comment:literal)?;
197     ) => {
198         register!(@field_accessor $name $hi:$lo $field as bool => bool $(, $comment)?;);
199     };
200 
201     // Catches the `?=>` syntax for non-boolean fields.
202     (
203         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt ?=> $try_into_type:ty
204             $(, $comment:literal)?;
205     ) => {
206         register!(@leaf_accessor $name $hi:$lo $field as $type
207             { |f| <$try_into_type>::try_from(f as $type) } $try_into_type =>
208             ::core::result::Result<
209                 $try_into_type,
210                 <$try_into_type as ::core::convert::TryFrom<$type>>::Error
211             >
212             $(, $comment)?;);
213     };
214 
215     // Catches the `=>` syntax for non-boolean fields.
216     (
217         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt => $into_type:ty
218             $(, $comment:literal)?;
219     ) => {
220         register!(@leaf_accessor $name $hi:$lo $field as $type
221             { |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;);
222     };
223 
224     // Shortcut for fields defined as non-`bool` without the `=>` or `?=>` syntax.
225     (
226         @field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt
227             $(, $comment:literal)?;
228     ) => {
229         register!(@field_accessor $name $hi:$lo $field as $type => $type $(, $comment)?;);
230     };
231 
232     // Generates the accessor methods for a single field.
233     (
234         @leaf_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:ty
235             { $process:expr } $to_type:ty => $res_type:ty $(, $comment:literal)?;
236     ) => {
237         kernel::macros::paste!(
238         const [<$field:upper>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi;
239         const [<$field:upper _MASK>]: u32 = ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);
240         const [<$field:upper _SHIFT>]: u32 = Self::[<$field:upper _MASK>].trailing_zeros();
241         );
242 
243         $(
244         #[doc="Returns the value of this field:"]
245         #[doc=$comment]
246         )?
247         #[inline]
248         pub(crate) fn $field(self) -> $res_type {
249             kernel::macros::paste!(
250             const MASK: u32 = $name::[<$field:upper _MASK>];
251             const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
252             );
253             let field = ((self.0 & MASK) >> SHIFT);
254 
255             $process(field)
256         }
257 
258         kernel::macros::paste!(
259         $(
260         #[doc="Sets the value of this field:"]
261         #[doc=$comment]
262         )?
263         #[inline]
264         pub(crate) fn [<set_ $field>](mut self, value: $to_type) -> Self {
265             const MASK: u32 = $name::[<$field:upper _MASK>];
266             const SHIFT: u32 = $name::[<$field:upper _SHIFT>];
267             let value = ((value as u32) << SHIFT) & MASK;
268             self.0 = (self.0 & !MASK) | value;
269 
270             self
271         }
272         );
273     };
274 
275     // Creates the IO accessors for a fixed offset register.
276     (@io $name:ident @ $offset:literal) => {
277         #[allow(dead_code)]
278         impl $name {
279             #[inline]
280             pub(crate) fn read<const SIZE: usize, T>(io: &T) -> Self where
281                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
282             {
283                 Self(io.read32($offset))
284             }
285 
286             #[inline]
287             pub(crate) fn write<const SIZE: usize, T>(self, io: &T) where
288                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
289             {
290                 io.write32(self.0, $offset)
291             }
292 
293             #[inline]
294             pub(crate) fn alter<const SIZE: usize, T, F>(
295                 io: &T,
296                 f: F,
297             ) where
298                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
299                 F: ::core::ops::FnOnce(Self) -> Self,
300             {
301                 let reg = f(Self::read(io));
302                 reg.write(io);
303             }
304         }
305     };
306 
307     // Create the IO accessors for a relative offset register.
308     (@io $name:ident @ + $offset:literal) => {
309         #[allow(dead_code)]
310         impl $name {
311             #[inline]
312             pub(crate) fn read<const SIZE: usize, T>(
313                 io: &T,
314                 base: usize,
315             ) -> Self where
316                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
317             {
318                 Self(io.read32(base + $offset))
319             }
320 
321             #[inline]
322             pub(crate) fn write<const SIZE: usize, T>(
323                 self,
324                 io: &T,
325                 base: usize,
326             ) where
327                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
328             {
329                 io.write32(self.0, base + $offset)
330             }
331 
332             #[inline]
333             pub(crate) fn alter<const SIZE: usize, T, F>(
334                 io: &T,
335                 base: usize,
336                 f: F,
337             ) where
338                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
339                 F: ::core::ops::FnOnce(Self) -> Self,
340             {
341                 let reg = f(Self::read(io, base));
342                 reg.write(io, base);
343             }
344 
345             #[inline]
346             pub(crate) fn try_read<const SIZE: usize, T>(
347                 io: &T,
348                 base: usize,
349             ) -> ::kernel::error::Result<Self> where
350                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
351             {
352                 io.try_read32(base + $offset).map(Self)
353             }
354 
355             #[inline]
356             pub(crate) fn try_write<const SIZE: usize, T>(
357                 self,
358                 io: &T,
359                 base: usize,
360             ) -> ::kernel::error::Result<()> where
361                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
362             {
363                 io.try_write32(self.0, base + $offset)
364             }
365 
366             #[inline]
367             pub(crate) fn try_alter<const SIZE: usize, T, F>(
368                 io: &T,
369                 base: usize,
370                 f: F,
371             ) -> ::kernel::error::Result<()> where
372                 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,
373                 F: ::core::ops::FnOnce(Self) -> Self,
374             {
375                 let reg = f(Self::try_read(io, base)?);
376                 reg.try_write(io, base)
377             }
378         }
379     };
380 }
381