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/// ```
74macro_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

source code of linux/drivers/gpu/nova-core/regs/macros.rs