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