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

gpu: nova-core: define registers layout using helper macro

Add the register!() macro, which defines a given register's layout and
provide bit-field accessors with a way to convert them to a given type.
This macro will allow us to make clear definitions of the registers and
manipulate their fields safely.

The long-term goal is to eventually move it to the kernel crate so it
can be used by other drivers as well, but it was agreed to first land it
into nova-core and make it mature there.

To illustrate its usage, use it to define the layout for the Boot0
(renamed to NV_PMC_BOOT_0 to match OpenRM's naming scheme) and take
advantage of its accessors.

Suggested-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
Link: https://lore.kernel.org/r/20250507-nova-frts-v3-5-fcb02749754d@nvidia.com
[ Fix typo in commit message. - Danilo ]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>

authored by

Alexandre Courbot and committed by
Danilo Krummrich
c3f22262 a2a637ff

+403 -54
+6
Documentation/gpu/nova/core/todo.rst
··· 102 102 let boot0 = Boot0::read(&bar); 103 103 pr_info!("Revision: {}\n", boot0.revision()); 104 104 105 + Note: a work-in-progress implementation currently resides in 106 + `drivers/gpu/nova-core/regs/macros.rs` and is used in nova-core. It would be 107 + nice to improve it (possibly using proc macros) and move it to the `kernel` 108 + crate so it can be used by other components as well. 109 + 105 110 | Complexity: Advanced 111 + | Contact: Alexandre Courbot 106 112 107 113 Delay / Sleep abstractions 108 114 --------------------------
+5 -5
drivers/gpu/nova-core/gpu.rs
··· 112 112 } 113 113 114 114 impl Revision { 115 - fn from_boot0(boot0: regs::Boot0) -> Self { 115 + fn from_boot0(boot0: regs::NV_PMC_BOOT_0) -> Self { 116 116 Self { 117 - major: boot0.major_rev(), 118 - minor: boot0.minor_rev(), 117 + major: boot0.major_revision(), 118 + minor: boot0.minor_revision(), 119 119 } 120 120 } 121 121 } ··· 135 135 136 136 impl Spec { 137 137 fn new(bar: &Bar0) -> Result<Spec> { 138 - let boot0 = regs::Boot0::read(bar); 138 + let boot0 = regs::NV_PMC_BOOT_0::read(bar); 139 139 140 140 Ok(Self { 141 - chipset: boot0.chipset().try_into()?, 141 + chipset: boot0.chipset()?, 142 142 revision: Revision::from_boot0(boot0), 143 143 }) 144 144 }
+12 -49
drivers/gpu/nova-core/regs.rs
··· 1 1 // SPDX-License-Identifier: GPL-2.0 2 2 3 - use crate::driver::Bar0; 3 + // Required to retain the original register names used by OpenRM, which are all capital snake case 4 + // but are mapped to types. 5 + #![allow(non_camel_case_types)] 4 6 5 - // TODO 6 - // 7 - // Create register definitions via generic macros. See task "Generic register 8 - // abstraction" in Documentation/gpu/nova/core/todo.rst. 7 + #[macro_use] 8 + mod macros; 9 9 10 - const BOOT0_OFFSET: usize = 0x00000000; 10 + use crate::gpu::Chipset; 11 11 12 - // 3:0 - chipset minor revision 13 - const BOOT0_MINOR_REV_SHIFT: u8 = 0; 14 - const BOOT0_MINOR_REV_MASK: u32 = 0x0000000f; 12 + /* PMC */ 15 13 16 - // 7:4 - chipset major revision 17 - const BOOT0_MAJOR_REV_SHIFT: u8 = 4; 18 - const BOOT0_MAJOR_REV_MASK: u32 = 0x000000f0; 19 - 20 - // 23:20 - chipset implementation Identifier (depends on architecture) 21 - const BOOT0_IMPL_SHIFT: u8 = 20; 22 - const BOOT0_IMPL_MASK: u32 = 0x00f00000; 23 - 24 - // 28:24 - chipset architecture identifier 25 - const BOOT0_ARCH_MASK: u32 = 0x1f000000; 26 - 27 - // 28:20 - chipset identifier (virtual register field combining BOOT0_IMPL and 28 - // BOOT0_ARCH) 29 - const BOOT0_CHIPSET_SHIFT: u8 = BOOT0_IMPL_SHIFT; 30 - const BOOT0_CHIPSET_MASK: u32 = BOOT0_IMPL_MASK | BOOT0_ARCH_MASK; 31 - 32 - #[derive(Copy, Clone)] 33 - pub(crate) struct Boot0(u32); 34 - 35 - impl Boot0 { 36 - #[inline] 37 - pub(crate) fn read(bar: &Bar0) -> Self { 38 - Self(bar.read32(BOOT0_OFFSET)) 39 - } 40 - 41 - #[inline] 42 - pub(crate) fn chipset(&self) -> u32 { 43 - (self.0 & BOOT0_CHIPSET_MASK) >> BOOT0_CHIPSET_SHIFT 44 - } 45 - 46 - #[inline] 47 - pub(crate) fn minor_rev(&self) -> u8 { 48 - ((self.0 & BOOT0_MINOR_REV_MASK) >> BOOT0_MINOR_REV_SHIFT) as u8 49 - } 50 - 51 - #[inline] 52 - pub(crate) fn major_rev(&self) -> u8 { 53 - ((self.0 & BOOT0_MAJOR_REV_MASK) >> BOOT0_MAJOR_REV_SHIFT) as u8 54 - } 55 - } 14 + register!(NV_PMC_BOOT_0 @ 0x00000000, "Basic revision information about the GPU" { 15 + 3:0 minor_revision as u8, "Minor revision of the chip"; 16 + 7:4 major_revision as u8, "Major revision of the chip"; 17 + 28:20 chipset as u32 ?=> Chipset, "Chipset model"; 18 + });
+380
drivers/gpu/nova-core/regs/macros.rs
··· 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 + }