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