1 // SPDX-License-Identifier: GPL-2.0 2 3 //! `register!` 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 `register!` macro in this module provides an intuitive and readable syntax for defining a 10 //! dedicated type for each register. Each such type comes with its own field accessors that can 11 //! return an error if a field's value is invalid. Please look at the [`bitfield`] macro for the 12 //! complete syntax of fields definitions. 13 14 /// Trait providing a base address to be added to the offset of a relative register to obtain 15 /// its actual offset. 16 /// 17 /// The `T` generic argument is used to distinguish which base to use, in case a type provides 18 /// several bases. It is given to the `register!` macro to restrict the use of the register to 19 /// implementors of this particular variant. 20 pub(crate) trait RegisterBase<T> { 21 const BASE: usize; 22 } 23 24 /// Defines a dedicated type for a register with an absolute offset, including getter and setter 25 /// methods for its fields and methods to read and write it from an `Io` region. 26 /// 27 /// Example: 28 /// 29 /// ```no_run 30 /// register!(BOOT_0 @ 0x00000100, "Basic revision information about the GPU" { 31 /// 3:0 minor_revision as u8, "Minor revision of the chip"; 32 /// 7:4 major_revision as u8, "Major revision of the chip"; 33 /// 28:20 chipset as u32 ?=> Chipset, "Chipset model"; 34 /// }); 35 /// ``` 36 /// 37 /// This defines a `BOOT_0` type which can be read or written from offset `0x100` of an `Io` 38 /// region. It is composed of 3 fields, for instance `minor_revision` is made of the 4 least 39 /// significant bits of the register. Each field can be accessed and modified using accessor 40 /// methods: 41 /// 42 /// ```no_run 43 /// // Read from the register's defined offset (0x100). 44 /// let boot0 = BOOT_0::read(&bar); 45 /// pr_info!("chip revision: {}.{}", boot0.major_revision(), boot0.minor_revision()); 46 /// 47 /// // `Chipset::try_from` is called with the value of the `chipset` field and returns an 48 /// // error if it is invalid. 49 /// let chipset = boot0.chipset()?; 50 /// 51 /// // Update some fields and write the value back. 52 /// boot0.set_major_revision(3).set_minor_revision(10).write(&bar); 53 /// 54 /// // Or, just read and update the register in a single step: 55 /// BOOT_0::update(&bar, |r| r.set_major_revision(3).set_minor_revision(10)); 56 /// ``` 57 /// 58 /// The documentation strings are optional. If present, they will be added to the type's 59 /// definition, or the field getter and setter methods they are attached to. 60 /// 61 /// It is also possible to create a alias register by using the `=> ALIAS` syntax. This is useful 62 /// for cases where a register's interpretation depends on the context: 63 /// 64 /// ```no_run 65 /// register!(SCRATCH @ 0x00000200, "Scratch register" { 66 /// 31:0 value as u32, "Raw value"; 67 /// }); 68 /// 69 /// register!(SCRATCH_BOOT_STATUS => SCRATCH, "Boot status of the firmware" { 70 /// 0:0 completed as bool, "Whether the firmware has completed booting"; 71 /// }); 72 /// ``` 73 /// 74 /// In this example, `SCRATCH_0_BOOT_STATUS` uses the same I/O address as `SCRATCH`, while also 75 /// providing its own `completed` field. 76 /// 77 /// ## Relative registers 78 /// 79 /// A register can be defined as being accessible from a fixed offset of a provided base. For 80 /// instance, imagine the following I/O space: 81 /// 82 /// ```text 83 /// +-----------------------------+ 84 /// | ... | 85 /// | | 86 /// 0x100--->+------------CPU0-------------+ 87 /// | | 88 /// 0x110--->+-----------------------------+ 89 /// | CPU_CTL | 90 /// +-----------------------------+ 91 /// | ... | 92 /// | | 93 /// | | 94 /// 0x200--->+------------CPU1-------------+ 95 /// | | 96 /// 0x210--->+-----------------------------+ 97 /// | CPU_CTL | 98 /// +-----------------------------+ 99 /// | ... | 100 /// +-----------------------------+ 101 /// ``` 102 /// 103 /// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset `0x10` of their I/O 104 /// space segment. Since both instances of `CPU_CTL` share the same layout, we don't want to define 105 /// them twice and would prefer a way to select which one to use from a single definition 106 /// 107 /// This can be done using the `Base[Offset]` syntax when specifying the register's address. 108 /// 109 /// `Base` is an arbitrary type (typically a ZST) to be used as a generic parameter of the 110 /// [`RegisterBase`] trait to provide the base as a constant, i.e. each type providing a base for 111 /// this register needs to implement `RegisterBase<Base>`. Here is the above example translated 112 /// into code: 113 /// 114 /// ```no_run 115 /// // Type used to identify the base. 116 /// pub(crate) struct CpuCtlBase; 117 /// 118 /// // ZST describing `CPU0`. 119 /// struct Cpu0; 120 /// impl RegisterBase<CpuCtlBase> for Cpu0 { 121 /// const BASE: usize = 0x100; 122 /// } 123 /// // Singleton of `CPU0` used to identify it. 124 /// const CPU0: Cpu0 = Cpu0; 125 /// 126 /// // ZST describing `CPU1`. 127 /// struct Cpu1; 128 /// impl RegisterBase<CpuCtlBase> for Cpu1 { 129 /// const BASE: usize = 0x200; 130 /// } 131 /// // Singleton of `CPU1` used to identify it. 132 /// const CPU1: Cpu1 = Cpu1; 133 /// 134 /// // This makes `CPU_CTL` accessible from all implementors of `RegisterBase<CpuCtlBase>`. 135 /// register!(CPU_CTL @ CpuCtlBase[0x10], "CPU core control" { 136 /// 0:0 start as bool, "Start the CPU core"; 137 /// }); 138 /// 139 /// // The `read`, `write` and `update` methods of relative registers take an extra `base` argument 140 /// // that is used to resolve its final address by adding its `BASE` to the offset of the 141 /// // register. 142 /// 143 /// // Start `CPU0`. 144 /// CPU_CTL::update(bar, &CPU0, |r| r.set_start(true)); 145 /// 146 /// // Start `CPU1`. 147 /// CPU_CTL::update(bar, &CPU1, |r| r.set_start(true)); 148 /// 149 /// // Aliases can also be defined for relative register. 150 /// register!(CPU_CTL_ALIAS => CpuCtlBase[CPU_CTL], "Alias to CPU core control" { 151 /// 1:1 alias_start as bool, "Start the aliased CPU core"; 152 /// }); 153 /// 154 /// // Start the aliased `CPU0`. 155 /// CPU_CTL_ALIAS::update(bar, &CPU0, |r| r.set_alias_start(true)); 156 /// ``` 157 /// 158 /// ## Arrays of registers 159 /// 160 /// Some I/O areas contain consecutive values that can be interpreted in the same way. These areas 161 /// can be defined as an array of identical registers, allowing them to be accessed by index with 162 /// compile-time or runtime bound checking. Simply define their address as `Address[Size]`, and add 163 /// an `idx` parameter to their `read`, `write` and `update` methods: 164 /// 165 /// ```no_run 166 /// # fn no_run() -> Result<(), Error> { 167 /// # fn get_scratch_idx() -> usize { 168 /// # 0x15 169 /// # } 170 /// // Array of 64 consecutive registers with the same layout starting at offset `0x80`. 171 /// register!(SCRATCH @ 0x00000080[64], "Scratch registers" { 172 /// 31:0 value as u32; 173 /// }); 174 /// 175 /// // Read scratch register 0, i.e. I/O address `0x80`. 176 /// let scratch_0 = SCRATCH::read(bar, 0).value(); 177 /// // Read scratch register 15, i.e. I/O address `0x80 + (15 * 4)`. 178 /// let scratch_15 = SCRATCH::read(bar, 15).value(); 179 /// 180 /// // This is out of bounds and won't build. 181 /// // let scratch_128 = SCRATCH::read(bar, 128).value(); 182 /// 183 /// // Runtime-obtained array index. 184 /// let scratch_idx = get_scratch_idx(); 185 /// // Access on a runtime index returns an error if it is out-of-bounds. 186 /// let some_scratch = SCRATCH::try_read(bar, scratch_idx)?.value(); 187 /// 188 /// // Alias to a particular register in an array. 189 /// // Here `SCRATCH[8]` is used to convey the firmware exit code. 190 /// register!(FIRMWARE_STATUS => SCRATCH[8], "Firmware exit status code" { 191 /// 7:0 status as u8; 192 /// }); 193 /// 194 /// let status = FIRMWARE_STATUS::read(bar).status(); 195 /// 196 /// // Non-contiguous register arrays can be defined by adding a stride parameter. 197 /// // Here, each of the 16 registers of the array are separated by 8 bytes, meaning that the 198 /// // registers of the two declarations below are interleaved. 199 /// register!(SCRATCH_INTERLEAVED_0 @ 0x000000c0[16 ; 8], "Scratch registers bank 0" { 200 /// 31:0 value as u32; 201 /// }); 202 /// register!(SCRATCH_INTERLEAVED_1 @ 0x000000c4[16 ; 8], "Scratch registers bank 1" { 203 /// 31:0 value as u32; 204 /// }); 205 /// # Ok(()) 206 /// # } 207 /// ``` 208 /// 209 /// ## Relative arrays of registers 210 /// 211 /// Combining the two features described in the sections above, arrays of registers accessible from 212 /// a base can also be defined: 213 /// 214 /// ```no_run 215 /// # fn no_run() -> Result<(), Error> { 216 /// # fn get_scratch_idx() -> usize { 217 /// # 0x15 218 /// # } 219 /// // Type used as parameter of `RegisterBase` to specify the base. 220 /// pub(crate) struct CpuCtlBase; 221 /// 222 /// // ZST describing `CPU0`. 223 /// struct Cpu0; 224 /// impl RegisterBase<CpuCtlBase> for Cpu0 { 225 /// const BASE: usize = 0x100; 226 /// } 227 /// // Singleton of `CPU0` used to identify it. 228 /// const CPU0: Cpu0 = Cpu0; 229 /// 230 /// // ZST describing `CPU1`. 231 /// struct Cpu1; 232 /// impl RegisterBase<CpuCtlBase> for Cpu1 { 233 /// const BASE: usize = 0x200; 234 /// } 235 /// // Singleton of `CPU1` used to identify it. 236 /// const CPU1: Cpu1 = Cpu1; 237 /// 238 /// // 64 per-cpu scratch registers, arranged as an contiguous array. 239 /// register!(CPU_SCRATCH @ CpuCtlBase[0x00000080[64]], "Per-CPU scratch registers" { 240 /// 31:0 value as u32; 241 /// }); 242 /// 243 /// let cpu0_scratch_0 = CPU_SCRATCH::read(bar, &Cpu0, 0).value(); 244 /// let cpu1_scratch_15 = CPU_SCRATCH::read(bar, &Cpu1, 15).value(); 245 /// 246 /// // This won't build. 247 /// // let cpu0_scratch_128 = CPU_SCRATCH::read(bar, &Cpu0, 128).value(); 248 /// 249 /// // Runtime-obtained array index. 250 /// let scratch_idx = get_scratch_idx(); 251 /// // Access on a runtime value returns an error if it is out-of-bounds. 252 /// let cpu0_some_scratch = CPU_SCRATCH::try_read(bar, &Cpu0, scratch_idx)?.value(); 253 /// 254 /// // `SCRATCH[8]` is used to convey the firmware exit code. 255 /// register!(CPU_FIRMWARE_STATUS => CpuCtlBase[CPU_SCRATCH[8]], 256 /// "Per-CPU firmware exit status code" { 257 /// 7:0 status as u8; 258 /// }); 259 /// 260 /// let cpu0_status = CPU_FIRMWARE_STATUS::read(bar, &Cpu0).status(); 261 /// 262 /// // Non-contiguous register arrays can be defined by adding a stride parameter. 263 /// // Here, each of the 16 registers of the array are separated by 8 bytes, meaning that the 264 /// // registers of the two declarations below are interleaved. 265 /// register!(CPU_SCRATCH_INTERLEAVED_0 @ CpuCtlBase[0x00000d00[16 ; 8]], 266 /// "Scratch registers bank 0" { 267 /// 31:0 value as u32; 268 /// }); 269 /// register!(CPU_SCRATCH_INTERLEAVED_1 @ CpuCtlBase[0x00000d04[16 ; 8]], 270 /// "Scratch registers bank 1" { 271 /// 31:0 value as u32; 272 /// }); 273 /// # Ok(()) 274 /// # } 275 /// ``` 276 macro_rules! register { 277 // Creates a register at a fixed offset of the MMIO space. 278 ($name:ident @ $offset:literal $(, $comment:literal)? { $($fields:tt)* } ) => { 279 bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); 280 register!(@io_fixed $name @ $offset); 281 }; 282 283 // Creates an alias register of fixed offset register `alias` with its own fields. 284 ($name:ident => $alias:ident $(, $comment:literal)? { $($fields:tt)* } ) => { 285 bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); 286 register!(@io_fixed $name @ $alias::OFFSET); 287 }; 288 289 // Creates a register at a relative offset from a base address provider. 290 ($name:ident @ $base:ty [ $offset:literal ] $(, $comment:literal)? { $($fields:tt)* } ) => { 291 bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); 292 register!(@io_relative $name @ $base [ $offset ]); 293 }; 294 295 // Creates an alias register of relative offset register `alias` with its own fields. 296 ($name:ident => $base:ty [ $alias:ident ] $(, $comment:literal)? { $($fields:tt)* }) => { 297 bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); 298 register!(@io_relative $name @ $base [ $alias::OFFSET ]); 299 }; 300 301 // Creates an array of registers at a fixed offset of the MMIO space. 302 ( 303 $name:ident @ $offset:literal [ $size:expr ; $stride:expr ] $(, $comment:literal)? { 304 $($fields:tt)* 305 } 306 ) => { 307 static_assert!(::core::mem::size_of::<u32>() <= $stride); 308 bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); 309 register!(@io_array $name @ $offset [ $size ; $stride ]); 310 }; 311 312 // Shortcut for contiguous array of registers (stride == size of element). 313 ( 314 $name:ident @ $offset:literal [ $size:expr ] $(, $comment:literal)? { 315 $($fields:tt)* 316 } 317 ) => { 318 register!($name @ $offset [ $size ; ::core::mem::size_of::<u32>() ] $(, $comment)? { 319 $($fields)* 320 } ); 321 }; 322 323 // Creates an array of registers at a relative offset from a base address provider. 324 ( 325 $name:ident @ $base:ty [ $offset:literal [ $size:expr ; $stride:expr ] ] 326 $(, $comment:literal)? { $($fields:tt)* } 327 ) => { 328 static_assert!(::core::mem::size_of::<u32>() <= $stride); 329 bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); 330 register!(@io_relative_array $name @ $base [ $offset [ $size ; $stride ] ]); 331 }; 332 333 // Shortcut for contiguous array of relative registers (stride == size of element). 334 ( 335 $name:ident @ $base:ty [ $offset:literal [ $size:expr ] ] $(, $comment:literal)? { 336 $($fields:tt)* 337 } 338 ) => { 339 register!($name @ $base [ $offset [ $size ; ::core::mem::size_of::<u32>() ] ] 340 $(, $comment)? { $($fields)* } ); 341 }; 342 343 // Creates an alias of register `idx` of relative array of registers `alias` with its own 344 // fields. 345 ( 346 $name:ident => $base:ty [ $alias:ident [ $idx:expr ] ] $(, $comment:literal)? { 347 $($fields:tt)* 348 } 349 ) => { 350 static_assert!($idx < $alias::SIZE); 351 bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); 352 register!(@io_relative $name @ $base [ $alias::OFFSET + $idx * $alias::STRIDE ] ); 353 }; 354 355 // Creates an alias of register `idx` of array of registers `alias` with its own fields. 356 // This rule belongs to the (non-relative) register arrays set, but needs to be put last 357 // to avoid it being interpreted in place of the relative register array alias rule. 358 ($name:ident => $alias:ident [ $idx:expr ] $(, $comment:literal)? { $($fields:tt)* }) => { 359 static_assert!($idx < $alias::SIZE); 360 bitfield!(pub(crate) struct $name(u32) $(, $comment)? { $($fields)* } ); 361 register!(@io_fixed $name @ $alias::OFFSET + $idx * $alias::STRIDE ); 362 }; 363 364 // Generates the IO accessors for a fixed offset register. 365 (@io_fixed $name:ident @ $offset:expr) => { 366 #[allow(dead_code)] 367 impl $name { 368 pub(crate) const OFFSET: usize = $offset; 369 370 /// Read the register from its address in `io`. 371 #[inline(always)] 372 pub(crate) fn read<const SIZE: usize, T>(io: &T) -> Self where 373 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 374 { 375 Self(io.read32($offset)) 376 } 377 378 /// Write the value contained in `self` to the register address in `io`. 379 #[inline(always)] 380 pub(crate) fn write<const SIZE: usize, T>(self, io: &T) where 381 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 382 { 383 io.write32(self.0, $offset) 384 } 385 386 /// Read the register from its address in `io` and run `f` on its value to obtain a new 387 /// value to write back. 388 #[inline(always)] 389 pub(crate) fn update<const SIZE: usize, T, F>( 390 io: &T, 391 f: F, 392 ) where 393 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 394 F: ::core::ops::FnOnce(Self) -> Self, 395 { 396 let reg = f(Self::read(io)); 397 reg.write(io); 398 } 399 } 400 }; 401 402 // Generates the IO accessors for a relative offset register. 403 (@io_relative $name:ident @ $base:ty [ $offset:expr ]) => { 404 #[allow(dead_code)] 405 impl $name { 406 pub(crate) const OFFSET: usize = $offset; 407 408 /// Read the register from `io`, using the base address provided by `base` and adding 409 /// the register's offset to it. 410 #[inline(always)] 411 pub(crate) fn read<const SIZE: usize, T, B>( 412 io: &T, 413 #[allow(unused_variables)] 414 base: &B, 415 ) -> Self where 416 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 417 B: crate::regs::macros::RegisterBase<$base>, 418 { 419 const OFFSET: usize = $name::OFFSET; 420 421 let value = io.read32( 422 <B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET 423 ); 424 425 Self(value) 426 } 427 428 /// Write the value contained in `self` to `io`, using the base address provided by 429 /// `base` and adding the register's offset to it. 430 #[inline(always)] 431 pub(crate) fn write<const SIZE: usize, T, B>( 432 self, 433 io: &T, 434 #[allow(unused_variables)] 435 base: &B, 436 ) where 437 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 438 B: crate::regs::macros::RegisterBase<$base>, 439 { 440 const OFFSET: usize = $name::OFFSET; 441 442 io.write32( 443 self.0, 444 <B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET 445 ); 446 } 447 448 /// Read the register from `io`, using the base address provided by `base` and adding 449 /// the register's offset to it, then run `f` on its value to obtain a new value to 450 /// write back. 451 #[inline(always)] 452 pub(crate) fn update<const SIZE: usize, T, B, F>( 453 io: &T, 454 base: &B, 455 f: F, 456 ) where 457 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 458 B: crate::regs::macros::RegisterBase<$base>, 459 F: ::core::ops::FnOnce(Self) -> Self, 460 { 461 let reg = f(Self::read(io, base)); 462 reg.write(io, base); 463 } 464 } 465 }; 466 467 // Generates the IO accessors for an array of registers. 468 (@io_array $name:ident @ $offset:literal [ $size:expr ; $stride:expr ]) => { 469 #[allow(dead_code)] 470 impl $name { 471 pub(crate) const OFFSET: usize = $offset; 472 pub(crate) const SIZE: usize = $size; 473 pub(crate) const STRIDE: usize = $stride; 474 475 /// Read the array register at index `idx` from its address in `io`. 476 #[inline(always)] 477 pub(crate) fn read<const SIZE: usize, T>( 478 io: &T, 479 idx: usize, 480 ) -> Self where 481 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 482 { 483 build_assert!(idx < Self::SIZE); 484 485 let offset = Self::OFFSET + (idx * Self::STRIDE); 486 let value = io.read32(offset); 487 488 Self(value) 489 } 490 491 /// Write the value contained in `self` to the array register with index `idx` in `io`. 492 #[inline(always)] 493 pub(crate) fn write<const SIZE: usize, T>( 494 self, 495 io: &T, 496 idx: usize 497 ) where 498 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 499 { 500 build_assert!(idx < Self::SIZE); 501 502 let offset = Self::OFFSET + (idx * Self::STRIDE); 503 504 io.write32(self.0, offset); 505 } 506 507 /// Read the array register at index `idx` in `io` and run `f` on its value to obtain a 508 /// new value to write back. 509 #[inline(always)] 510 pub(crate) fn update<const SIZE: usize, T, F>( 511 io: &T, 512 idx: usize, 513 f: F, 514 ) where 515 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 516 F: ::core::ops::FnOnce(Self) -> Self, 517 { 518 let reg = f(Self::read(io, idx)); 519 reg.write(io, idx); 520 } 521 522 /// Read the array register at index `idx` from its address in `io`. 523 /// 524 /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the 525 /// access was out-of-bounds. 526 #[inline(always)] 527 pub(crate) fn try_read<const SIZE: usize, T>( 528 io: &T, 529 idx: usize, 530 ) -> ::kernel::error::Result<Self> where 531 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 532 { 533 if idx < Self::SIZE { 534 Ok(Self::read(io, idx)) 535 } else { 536 Err(EINVAL) 537 } 538 } 539 540 /// Write the value contained in `self` to the array register with index `idx` in `io`. 541 /// 542 /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the 543 /// access was out-of-bounds. 544 #[inline(always)] 545 pub(crate) fn try_write<const SIZE: usize, T>( 546 self, 547 io: &T, 548 idx: usize, 549 ) -> ::kernel::error::Result where 550 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 551 { 552 if idx < Self::SIZE { 553 Ok(self.write(io, idx)) 554 } else { 555 Err(EINVAL) 556 } 557 } 558 559 /// Read the array register at index `idx` in `io` and run `f` on its value to obtain a 560 /// new value to write back. 561 /// 562 /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the 563 /// access was out-of-bounds. 564 #[inline(always)] 565 pub(crate) fn try_update<const SIZE: usize, T, F>( 566 io: &T, 567 idx: usize, 568 f: F, 569 ) -> ::kernel::error::Result where 570 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 571 F: ::core::ops::FnOnce(Self) -> Self, 572 { 573 if idx < Self::SIZE { 574 Ok(Self::update(io, idx, f)) 575 } else { 576 Err(EINVAL) 577 } 578 } 579 } 580 }; 581 582 // Generates the IO accessors for an array of relative registers. 583 ( 584 @io_relative_array $name:ident @ $base:ty 585 [ $offset:literal [ $size:expr ; $stride:expr ] ] 586 ) => { 587 #[allow(dead_code)] 588 impl $name { 589 pub(crate) const OFFSET: usize = $offset; 590 pub(crate) const SIZE: usize = $size; 591 pub(crate) const STRIDE: usize = $stride; 592 593 /// Read the array register at index `idx` from `io`, using the base address provided 594 /// by `base` and adding the register's offset to it. 595 #[inline(always)] 596 pub(crate) fn read<const SIZE: usize, T, B>( 597 io: &T, 598 #[allow(unused_variables)] 599 base: &B, 600 idx: usize, 601 ) -> Self where 602 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 603 B: crate::regs::macros::RegisterBase<$base>, 604 { 605 build_assert!(idx < Self::SIZE); 606 607 let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE + 608 Self::OFFSET + (idx * Self::STRIDE); 609 let value = io.read32(offset); 610 611 Self(value) 612 } 613 614 /// Write the value contained in `self` to `io`, using the base address provided by 615 /// `base` and adding the offset of array register `idx` to it. 616 #[inline(always)] 617 pub(crate) fn write<const SIZE: usize, T, B>( 618 self, 619 io: &T, 620 #[allow(unused_variables)] 621 base: &B, 622 idx: usize 623 ) where 624 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 625 B: crate::regs::macros::RegisterBase<$base>, 626 { 627 build_assert!(idx < Self::SIZE); 628 629 let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE + 630 Self::OFFSET + (idx * Self::STRIDE); 631 632 io.write32(self.0, offset); 633 } 634 635 /// Read the array register at index `idx` from `io`, using the base address provided 636 /// by `base` and adding the register's offset to it, then run `f` on its value to 637 /// obtain a new value to write back. 638 #[inline(always)] 639 pub(crate) fn update<const SIZE: usize, T, B, F>( 640 io: &T, 641 base: &B, 642 idx: usize, 643 f: F, 644 ) where 645 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 646 B: crate::regs::macros::RegisterBase<$base>, 647 F: ::core::ops::FnOnce(Self) -> Self, 648 { 649 let reg = f(Self::read(io, base, idx)); 650 reg.write(io, base, idx); 651 } 652 653 /// Read the array register at index `idx` from `io`, using the base address provided 654 /// by `base` and adding the register's offset to it. 655 /// 656 /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the 657 /// access was out-of-bounds. 658 #[inline(always)] 659 pub(crate) fn try_read<const SIZE: usize, T, B>( 660 io: &T, 661 base: &B, 662 idx: usize, 663 ) -> ::kernel::error::Result<Self> where 664 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 665 B: crate::regs::macros::RegisterBase<$base>, 666 { 667 if idx < Self::SIZE { 668 Ok(Self::read(io, base, idx)) 669 } else { 670 Err(EINVAL) 671 } 672 } 673 674 /// Write the value contained in `self` to `io`, using the base address provided by 675 /// `base` and adding the offset of array register `idx` to it. 676 /// 677 /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the 678 /// access was out-of-bounds. 679 #[inline(always)] 680 pub(crate) fn try_write<const SIZE: usize, T, B>( 681 self, 682 io: &T, 683 base: &B, 684 idx: usize, 685 ) -> ::kernel::error::Result where 686 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 687 B: crate::regs::macros::RegisterBase<$base>, 688 { 689 if idx < Self::SIZE { 690 Ok(self.write(io, base, idx)) 691 } else { 692 Err(EINVAL) 693 } 694 } 695 696 /// Read the array register at index `idx` from `io`, using the base address provided 697 /// by `base` and adding the register's offset to it, then run `f` on its value to 698 /// obtain a new value to write back. 699 /// 700 /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the 701 /// access was out-of-bounds. 702 #[inline(always)] 703 pub(crate) fn try_update<const SIZE: usize, T, B, F>( 704 io: &T, 705 base: &B, 706 idx: usize, 707 f: F, 708 ) -> ::kernel::error::Result where 709 T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>, 710 B: crate::regs::macros::RegisterBase<$base>, 711 F: ::core::ops::FnOnce(Self) -> Self, 712 { 713 if idx < Self::SIZE { 714 Ok(Self::update(io, base, idx, f)) 715 } else { 716 Err(EINVAL) 717 } 718 } 719 } 720 }; 721 } 722