Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

at v6.16-rc3 380 lines 14 kB view raw
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}