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<T, I>(io: &T) -> Self where 373 T: ::core::ops::Deref<Target = I>, 374 I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 375 { 376 Self(io.read32($offset)) 377 } 378 379 /// Write the value contained in `self` to the register address in `io`. 380 #[inline(always)] 381 pub(crate) fn write<T, I>(self, io: &T) where 382 T: ::core::ops::Deref<Target = I>, 383 I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 384 { 385 io.write32(self.0, $offset) 386 } 387 388 /// Read the register from its address in `io` and run `f` on its value to obtain a new 389 /// value to write back. 390 #[inline(always)] 391 pub(crate) fn update<T, I, F>( 392 io: &T, 393 f: F, 394 ) where 395 T: ::core::ops::Deref<Target = I>, 396 I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 397 F: ::core::ops::FnOnce(Self) -> Self, 398 { 399 let reg = f(Self::read(io)); 400 reg.write(io); 401 } 402 } 403 }; 404 405 // Generates the IO accessors for a relative offset register. 406 (@io_relative $name:ident @ $base:ty [ $offset:expr ]) => { 407 #[allow(dead_code)] 408 impl $name { 409 pub(crate) const OFFSET: usize = $offset; 410 411 /// Read the register from `io`, using the base address provided by `base` and adding 412 /// the register's offset to it. 413 #[inline(always)] 414 pub(crate) fn read<T, I, B>( 415 io: &T, 416 #[allow(unused_variables)] 417 base: &B, 418 ) -> Self where 419 T: ::core::ops::Deref<Target = I>, 420 I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 421 B: crate::regs::macros::RegisterBase<$base>, 422 { 423 const OFFSET: usize = $name::OFFSET; 424 425 let value = io.read32( 426 <B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET 427 ); 428 429 Self(value) 430 } 431 432 /// Write the value contained in `self` to `io`, using the base address provided by 433 /// `base` and adding the register's offset to it. 434 #[inline(always)] 435 pub(crate) fn write<T, I, B>( 436 self, 437 io: &T, 438 #[allow(unused_variables)] 439 base: &B, 440 ) where 441 T: ::core::ops::Deref<Target = I>, 442 I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 443 B: crate::regs::macros::RegisterBase<$base>, 444 { 445 const OFFSET: usize = $name::OFFSET; 446 447 io.write32( 448 self.0, 449 <B as crate::regs::macros::RegisterBase<$base>>::BASE + OFFSET 450 ); 451 } 452 453 /// Read the register from `io`, using the base address provided by `base` and adding 454 /// the register's offset to it, then run `f` on its value to obtain a new value to 455 /// write back. 456 #[inline(always)] 457 pub(crate) fn update<T, I, B, F>( 458 io: &T, 459 base: &B, 460 f: F, 461 ) where 462 T: ::core::ops::Deref<Target = I>, 463 I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 464 B: crate::regs::macros::RegisterBase<$base>, 465 F: ::core::ops::FnOnce(Self) -> Self, 466 { 467 let reg = f(Self::read(io, base)); 468 reg.write(io, base); 469 } 470 } 471 }; 472 473 // Generates the IO accessors for an array of registers. 474 (@io_array $name:ident @ $offset:literal [ $size:expr ; $stride:expr ]) => { 475 #[allow(dead_code)] 476 impl $name { 477 pub(crate) const OFFSET: usize = $offset; 478 pub(crate) const SIZE: usize = $size; 479 pub(crate) const STRIDE: usize = $stride; 480 481 /// Read the array register at index `idx` from its address in `io`. 482 #[inline(always)] 483 pub(crate) fn read<T, I>( 484 io: &T, 485 idx: usize, 486 ) -> Self where 487 T: ::core::ops::Deref<Target = I>, 488 I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 489 { 490 build_assert!(idx < Self::SIZE); 491 492 let offset = Self::OFFSET + (idx * Self::STRIDE); 493 let value = io.read32(offset); 494 495 Self(value) 496 } 497 498 /// Write the value contained in `self` to the array register with index `idx` in `io`. 499 #[inline(always)] 500 pub(crate) fn write<T, I>( 501 self, 502 io: &T, 503 idx: usize 504 ) where 505 T: ::core::ops::Deref<Target = I>, 506 I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 507 { 508 build_assert!(idx < Self::SIZE); 509 510 let offset = Self::OFFSET + (idx * Self::STRIDE); 511 512 io.write32(self.0, offset); 513 } 514 515 /// Read the array register at index `idx` in `io` and run `f` on its value to obtain a 516 /// new value to write back. 517 #[inline(always)] 518 pub(crate) fn update<T, I, F>( 519 io: &T, 520 idx: usize, 521 f: F, 522 ) where 523 T: ::core::ops::Deref<Target = I>, 524 I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 525 F: ::core::ops::FnOnce(Self) -> Self, 526 { 527 let reg = f(Self::read(io, idx)); 528 reg.write(io, idx); 529 } 530 531 /// Read the array register at index `idx` from its address in `io`. 532 /// 533 /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the 534 /// access was out-of-bounds. 535 #[inline(always)] 536 pub(crate) fn try_read<T, I>( 537 io: &T, 538 idx: usize, 539 ) -> ::kernel::error::Result<Self> where 540 T: ::core::ops::Deref<Target = I>, 541 I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 542 { 543 if idx < Self::SIZE { 544 Ok(Self::read(io, idx)) 545 } else { 546 Err(EINVAL) 547 } 548 } 549 550 /// Write the value contained in `self` to the array register with index `idx` in `io`. 551 /// 552 /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the 553 /// access was out-of-bounds. 554 #[inline(always)] 555 pub(crate) fn try_write<T, I>( 556 self, 557 io: &T, 558 idx: usize, 559 ) -> ::kernel::error::Result where 560 T: ::core::ops::Deref<Target = I>, 561 I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 562 { 563 if idx < Self::SIZE { 564 Ok(self.write(io, idx)) 565 } else { 566 Err(EINVAL) 567 } 568 } 569 570 /// Read the array register at index `idx` in `io` and run `f` on its value to obtain a 571 /// new value to write back. 572 /// 573 /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the 574 /// access was out-of-bounds. 575 #[inline(always)] 576 pub(crate) fn try_update<T, I, F>( 577 io: &T, 578 idx: usize, 579 f: F, 580 ) -> ::kernel::error::Result where 581 T: ::core::ops::Deref<Target = I>, 582 I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 583 F: ::core::ops::FnOnce(Self) -> Self, 584 { 585 if idx < Self::SIZE { 586 Ok(Self::update(io, idx, f)) 587 } else { 588 Err(EINVAL) 589 } 590 } 591 } 592 }; 593 594 // Generates the IO accessors for an array of relative registers. 595 ( 596 @io_relative_array $name:ident @ $base:ty 597 [ $offset:literal [ $size:expr ; $stride:expr ] ] 598 ) => { 599 #[allow(dead_code)] 600 impl $name { 601 pub(crate) const OFFSET: usize = $offset; 602 pub(crate) const SIZE: usize = $size; 603 pub(crate) const STRIDE: usize = $stride; 604 605 /// Read the array register at index `idx` from `io`, using the base address provided 606 /// by `base` and adding the register's offset to it. 607 #[inline(always)] 608 pub(crate) fn read<T, I, B>( 609 io: &T, 610 #[allow(unused_variables)] 611 base: &B, 612 idx: usize, 613 ) -> Self where 614 T: ::core::ops::Deref<Target = I>, 615 I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 616 B: crate::regs::macros::RegisterBase<$base>, 617 { 618 build_assert!(idx < Self::SIZE); 619 620 let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE + 621 Self::OFFSET + (idx * Self::STRIDE); 622 let value = io.read32(offset); 623 624 Self(value) 625 } 626 627 /// Write the value contained in `self` to `io`, using the base address provided by 628 /// `base` and adding the offset of array register `idx` to it. 629 #[inline(always)] 630 pub(crate) fn write<T, I, B>( 631 self, 632 io: &T, 633 #[allow(unused_variables)] 634 base: &B, 635 idx: usize 636 ) where 637 T: ::core::ops::Deref<Target = I>, 638 I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 639 B: crate::regs::macros::RegisterBase<$base>, 640 { 641 build_assert!(idx < Self::SIZE); 642 643 let offset = <B as crate::regs::macros::RegisterBase<$base>>::BASE + 644 Self::OFFSET + (idx * Self::STRIDE); 645 646 io.write32(self.0, offset); 647 } 648 649 /// Read the array register at index `idx` from `io`, using the base address provided 650 /// by `base` and adding the register's offset to it, then run `f` on its value to 651 /// obtain a new value to write back. 652 #[inline(always)] 653 pub(crate) fn update<T, I, B, F>( 654 io: &T, 655 base: &B, 656 idx: usize, 657 f: F, 658 ) where 659 T: ::core::ops::Deref<Target = I>, 660 I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 661 B: crate::regs::macros::RegisterBase<$base>, 662 F: ::core::ops::FnOnce(Self) -> Self, 663 { 664 let reg = f(Self::read(io, base, idx)); 665 reg.write(io, base, idx); 666 } 667 668 /// Read the array register at index `idx` from `io`, using the base address provided 669 /// by `base` and adding the register's offset to it. 670 /// 671 /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the 672 /// access was out-of-bounds. 673 #[inline(always)] 674 pub(crate) fn try_read<T, I, B>( 675 io: &T, 676 base: &B, 677 idx: usize, 678 ) -> ::kernel::error::Result<Self> where 679 T: ::core::ops::Deref<Target = I>, 680 I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 681 B: crate::regs::macros::RegisterBase<$base>, 682 { 683 if idx < Self::SIZE { 684 Ok(Self::read(io, base, idx)) 685 } else { 686 Err(EINVAL) 687 } 688 } 689 690 /// Write the value contained in `self` to `io`, using the base address provided by 691 /// `base` and adding the offset of array register `idx` to it. 692 /// 693 /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the 694 /// access was out-of-bounds. 695 #[inline(always)] 696 pub(crate) fn try_write<T, I, B>( 697 self, 698 io: &T, 699 base: &B, 700 idx: usize, 701 ) -> ::kernel::error::Result where 702 T: ::core::ops::Deref<Target = I>, 703 I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 704 B: crate::regs::macros::RegisterBase<$base>, 705 { 706 if idx < Self::SIZE { 707 Ok(self.write(io, base, idx)) 708 } else { 709 Err(EINVAL) 710 } 711 } 712 713 /// Read the array register at index `idx` from `io`, using the base address provided 714 /// by `base` and adding the register's offset to it, then run `f` on its value to 715 /// obtain a new value to write back. 716 /// 717 /// The validity of `idx` is checked at run-time, and `EINVAL` is returned is the 718 /// access was out-of-bounds. 719 #[inline(always)] 720 pub(crate) fn try_update<T, I, B, F>( 721 io: &T, 722 base: &B, 723 idx: usize, 724 f: F, 725 ) -> ::kernel::error::Result where 726 T: ::core::ops::Deref<Target = I>, 727 I: ::kernel::io::IoKnownSize + ::kernel::io::IoCapable<u32>, 728 B: crate::regs::macros::RegisterBase<$base>, 729 F: ::core::ops::FnOnce(Self) -> Self, 730 { 731 if idx < Self::SIZE { 732 Ok(Self::update(io, base, idx, f)) 733 } else { 734 Err(EINVAL) 735 } 736 } 737 } 738 }; 739 } 740