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 | |