1 // SPDX-License-Identifier: GPL-2.0 2 3 //! Macro to define register layout and accessors. 4 //! 5 //! The [`register!`](kernel::io::register!) macro provides an intuitive and readable syntax for 6 //! defining a dedicated type for each register and accessing it using [`Io`](super::Io). Each such 7 //! type comes with its own field accessors that can return an error if a field's value is invalid. 8 //! 9 //! Note: most of the items in this module are public so they can be referenced by the macro, but 10 //! most are not to be used directly by users. Outside of the `register!` macro itself, the only 11 //! items you might want to import from this module are [`WithBase`] and [`Array`]. 12 //! 13 //! # Simple example 14 //! 15 //! ```no_run 16 //! use kernel::io::register; 17 //! 18 //! register! { 19 //! /// Basic information about the chip. 20 //! pub BOOT_0(u32) @ 0x00000100 { 21 //! /// Vendor ID. 22 //! 15:8 vendor_id; 23 //! /// Major revision of the chip. 24 //! 7:4 major_revision; 25 //! /// Minor revision of the chip. 26 //! 3:0 minor_revision; 27 //! } 28 //! } 29 //! ``` 30 //! 31 //! This defines a 32-bit `BOOT_0` type which can be read from or written to offset `0x100` of an 32 //! `Io` region, with the described bitfields. For instance, `minor_revision` consists of the 4 33 //! least significant bits of the type. 34 //! 35 //! Fields are instances of [`Bounded`](kernel::num::Bounded) and can be read by calling their 36 //! getter method, which is named after them. They also have setter methods prefixed with `with_` 37 //! for runtime values and `with_const_` for constant values. All setters return the updated 38 //! register value. 39 //! 40 //! Fields can also be transparently converted from/to an arbitrary type by using the `=>` and 41 //! `?=>` syntaxes. 42 //! 43 //! If present, doc comments above register or fields definitions are added to the relevant item 44 //! they document (the register type itself, or the field's setter and getter methods). 45 //! 46 //! Note that multiple registers can be defined in a single `register!` invocation. This can be 47 //! useful to group related registers together. 48 //! 49 //! Here is how the register defined above can be used in code: 50 //! 51 //! 52 //! ```no_run 53 //! use kernel::{ 54 //! io::{ 55 //! register, 56 //! Io, 57 //! IoLoc, 58 //! }, 59 //! num::Bounded, 60 //! }; 61 //! # use kernel::io::{Mmio, Region}; 62 //! # register! { 63 //! # pub BOOT_0(u32) @ 0x00000100 { 64 //! # 15:8 vendor_id; 65 //! # 7:4 major_revision; 66 //! # 3:0 minor_revision; 67 //! # } 68 //! # } 69 //! # fn test(io: Mmio<'_, Region<0x1000>>) { 70 //! # fn obtain_vendor_id() -> u8 { 0xff } 71 //! 72 //! // Read from the register's defined offset (0x100). 73 //! let boot0 = io.read(BOOT_0); 74 //! pr_info!("chip revision: {}.{}", boot0.major_revision().get(), boot0.minor_revision().get()); 75 //! 76 //! // Update some fields and write the new value back. 77 //! let new_boot0 = boot0 78 //! // Constant values. 79 //! .with_const_major_revision::<3>() 80 //! .with_const_minor_revision::<10>() 81 //! // Runtime value. 82 //! .with_vendor_id(obtain_vendor_id()); 83 //! io.write_reg(new_boot0); 84 //! 85 //! // Or, build a new value from zero and write it: 86 //! io.write_reg(BOOT_0::zeroed() 87 //! .with_const_major_revision::<3>() 88 //! .with_const_minor_revision::<10>() 89 //! .with_vendor_id(obtain_vendor_id()) 90 //! ); 91 //! 92 //! // Or, read and update the register in a single step. 93 //! io.update(BOOT_0, |r| r 94 //! .with_const_major_revision::<3>() 95 //! .with_const_minor_revision::<10>() 96 //! .with_vendor_id(obtain_vendor_id()) 97 //! ); 98 //! 99 //! // Constant values can also be built using the const setters. 100 //! const V: BOOT_0 = pin_init::zeroed::<BOOT_0>() 101 //! .with_const_major_revision::<3>() 102 //! .with_const_minor_revision::<10>(); 103 //! # } 104 //! ``` 105 //! 106 //! For more extensive documentation about how to define registers, see the 107 //! [`register!`](kernel::io::register!) macro. 108 109 use core::marker::PhantomData; 110 111 use crate::{ 112 build_assert::build_assert, 113 io::IoLoc, // 114 }; 115 116 use super::Region; 117 118 /// Trait implemented by all registers. 119 pub trait Register: Sized { 120 /// Backing primitive type of the register. 121 type Storage: Into<Self> + From<Self>; 122 123 /// Start offset of the register. 124 /// 125 /// The interpretation of this offset depends on the type of the register. 126 const OFFSET: usize; 127 } 128 129 /// Trait implemented by registers with a fixed offset. 130 pub trait FixedRegister: Register {} 131 132 /// Allows `()` to be used as the `location` parameter of [`Io::write`](super::Io::write) when 133 /// passing a [`FixedRegister`] value. 134 impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for () 135 where 136 T: FixedRegister, 137 { 138 type IoType = T::Storage; 139 140 #[inline(always)] 141 fn offset(self) -> usize { 142 T::OFFSET 143 } 144 } 145 146 /// A [`FixedRegister`] carries its location in its type. Thus `FixedRegister` values can be used 147 /// as an [`IoLoc`]. 148 impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for T 149 where 150 T: FixedRegister, 151 { 152 type IoType = T::Storage; 153 154 #[inline(always)] 155 fn offset(self) -> usize { 156 T::OFFSET 157 } 158 } 159 160 /// Location of a fixed register. 161 pub struct FixedRegisterLoc<T: FixedRegister>(PhantomData<T>); 162 163 impl<T: FixedRegister> FixedRegisterLoc<T> { 164 /// Returns the location of `T`. 165 #[inline(always)] 166 // We do not implement `Default` so we can be const. 167 #[expect(clippy::new_without_default)] 168 pub const fn new() -> Self { 169 Self(PhantomData) 170 } 171 } 172 173 impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for FixedRegisterLoc<T> 174 where 175 T: FixedRegister, 176 { 177 type IoType = T::Storage; 178 179 #[inline(always)] 180 fn offset(self) -> usize { 181 T::OFFSET 182 } 183 } 184 185 /// Trait providing a base address to be added to the offset of a relative register to obtain 186 /// its actual offset. 187 /// 188 /// The `T` generic argument is used to distinguish which base to use, in case a type provides 189 /// several bases. It is given to the `register!` macro to restrict the use of the register to 190 /// implementors of this particular variant. 191 pub trait RegisterBase<T> { 192 /// Base address to which register offsets are added. 193 const BASE: usize; 194 } 195 196 /// Trait implemented by all registers that are relative to a base. 197 pub trait WithBase { 198 /// Family of bases applicable to this register. 199 type BaseFamily; 200 201 /// Returns the absolute location of this type when using `B` as its base. 202 #[inline(always)] 203 fn of<B: RegisterBase<Self::BaseFamily>>() -> RelativeRegisterLoc<Self, B> 204 where 205 Self: Register, 206 { 207 RelativeRegisterLoc::new() 208 } 209 } 210 211 /// Trait implemented by relative registers. 212 pub trait RelativeRegister: Register + WithBase {} 213 214 /// Location of a relative register. 215 /// 216 /// This can either be an immediately accessible regular [`RelativeRegister`], or a 217 /// [`RelativeRegisterArray`] that needs one additional resolution through 218 /// [`RelativeRegisterLoc::at`]. 219 pub struct RelativeRegisterLoc<T: WithBase, B: ?Sized>(PhantomData<T>, PhantomData<B>); 220 221 impl<T, B> RelativeRegisterLoc<T, B> 222 where 223 T: Register + WithBase, 224 B: RegisterBase<T::BaseFamily> + ?Sized, 225 { 226 /// Returns the location of a relative register or register array. 227 #[inline(always)] 228 // We do not implement `Default` so we can be const. 229 #[expect(clippy::new_without_default)] 230 pub const fn new() -> Self { 231 Self(PhantomData, PhantomData) 232 } 233 234 // Returns the absolute offset of the relative register using base `B`. 235 // 236 // This is implemented as a private const method so it can be reused by the [`IoLoc`] 237 // implementations of both [`RelativeRegisterLoc`] and [`RelativeRegisterArrayLoc`]. 238 #[inline] 239 const fn offset(self) -> usize { 240 B::BASE + T::OFFSET 241 } 242 } 243 244 impl<const SIZE: usize, T, B> IoLoc<Region<SIZE>, T> for RelativeRegisterLoc<T, B> 245 where 246 T: RelativeRegister, 247 B: RegisterBase<T::BaseFamily> + ?Sized, 248 { 249 type IoType = T::Storage; 250 251 #[inline(always)] 252 fn offset(self) -> usize { 253 RelativeRegisterLoc::offset(self) 254 } 255 } 256 257 /// Trait implemented by arrays of registers. 258 pub trait RegisterArray: Register { 259 /// Number of elements in the registers array. 260 const SIZE: usize; 261 /// Number of bytes between the start of elements in the registers array. 262 const STRIDE: usize; 263 } 264 265 /// Location of an array register. 266 pub struct RegisterArrayLoc<T: RegisterArray>(usize, PhantomData<T>); 267 268 impl<T: RegisterArray> RegisterArrayLoc<T> { 269 /// Returns the location of register `T` at position `idx`, with build-time validation. 270 #[inline(always)] 271 pub fn new(idx: usize) -> Self { 272 build_assert!(idx < T::SIZE); 273 274 Self(idx, PhantomData) 275 } 276 277 /// Attempts to return the location of register `T` at position `idx`, with runtime validation. 278 #[inline(always)] 279 pub fn try_new(idx: usize) -> Option<Self> { 280 if idx < T::SIZE { 281 Some(Self(idx, PhantomData)) 282 } else { 283 None 284 } 285 } 286 } 287 288 impl<const SIZE: usize, T> IoLoc<Region<SIZE>, T> for RegisterArrayLoc<T> 289 where 290 T: RegisterArray, 291 { 292 type IoType = T::Storage; 293 294 #[inline(always)] 295 fn offset(self) -> usize { 296 T::OFFSET + self.0 * T::STRIDE 297 } 298 } 299 300 /// Trait providing location builders for [`RegisterArray`]s. 301 pub trait Array { 302 /// Returns the location of the register at position `idx`, with build-time validation. 303 #[inline(always)] 304 fn at(idx: usize) -> RegisterArrayLoc<Self> 305 where 306 Self: RegisterArray, 307 { 308 RegisterArrayLoc::new(idx) 309 } 310 311 /// Returns the location of the register at position `idx`, with runtime validation. 312 #[inline(always)] 313 fn try_at(idx: usize) -> Option<RegisterArrayLoc<Self>> 314 where 315 Self: RegisterArray, 316 { 317 RegisterArrayLoc::try_new(idx) 318 } 319 } 320 321 /// Trait implemented by arrays of relative registers. 322 pub trait RelativeRegisterArray: RegisterArray + WithBase {} 323 324 /// Location of a relative array register. 325 pub struct RelativeRegisterArrayLoc< 326 T: RelativeRegisterArray, 327 B: RegisterBase<T::BaseFamily> + ?Sized, 328 >(RelativeRegisterLoc<T, B>, usize); 329 330 impl<T, B> RelativeRegisterArrayLoc<T, B> 331 where 332 T: RelativeRegisterArray, 333 B: RegisterBase<T::BaseFamily> + ?Sized, 334 { 335 /// Returns the location of register `T` from the base `B` at index `idx`, with build-time 336 /// validation. 337 #[inline(always)] 338 pub fn new(idx: usize) -> Self { 339 build_assert!(idx < T::SIZE); 340 341 Self(RelativeRegisterLoc::new(), idx) 342 } 343 344 /// Attempts to return the location of register `T` from the base `B` at index `idx`, with 345 /// runtime validation. 346 #[inline(always)] 347 pub fn try_new(idx: usize) -> Option<Self> { 348 if idx < T::SIZE { 349 Some(Self(RelativeRegisterLoc::new(), idx)) 350 } else { 351 None 352 } 353 } 354 } 355 356 /// Methods exclusive to [`RelativeRegisterLoc`]s created with a [`RelativeRegisterArray`]. 357 impl<T, B> RelativeRegisterLoc<T, B> 358 where 359 T: RelativeRegisterArray, 360 B: RegisterBase<T::BaseFamily> + ?Sized, 361 { 362 /// Returns the location of the register at position `idx`, with build-time validation. 363 #[inline(always)] 364 pub fn at(self, idx: usize) -> RelativeRegisterArrayLoc<T, B> { 365 RelativeRegisterArrayLoc::new(idx) 366 } 367 368 /// Returns the location of the register at position `idx`, with runtime validation. 369 #[inline(always)] 370 pub fn try_at(self, idx: usize) -> Option<RelativeRegisterArrayLoc<T, B>> { 371 RelativeRegisterArrayLoc::try_new(idx) 372 } 373 } 374 375 impl<const SIZE: usize, T, B> IoLoc<Region<SIZE>, T> for RelativeRegisterArrayLoc<T, B> 376 where 377 T: RelativeRegisterArray, 378 B: RegisterBase<T::BaseFamily> + ?Sized, 379 { 380 type IoType = T::Storage; 381 382 #[inline(always)] 383 fn offset(self) -> usize { 384 self.0.offset() + self.1 * T::STRIDE 385 } 386 } 387 388 /// Trait implemented by items that contain both a register value and the absolute I/O location at 389 /// which to write it. 390 /// 391 /// Implementors can be used with [`Io::write_reg`](super::Io::write_reg). 392 pub trait LocatedRegister<Base: ?Sized> { 393 /// Register value to write. 394 type Value: Register; 395 /// Full location information at which to write the value. 396 type Location: IoLoc<Base, Self::Value>; 397 398 /// Consumes `self` and returns a `(location, value)` tuple describing a valid I/O write 399 /// operation. 400 fn into_io_op(self) -> (Self::Location, Self::Value); 401 } 402 403 impl<const SIZE: usize, T> LocatedRegister<Region<SIZE>> for T 404 where 405 T: FixedRegister, 406 { 407 type Location = FixedRegisterLoc<Self::Value>; 408 type Value = T; 409 410 #[inline(always)] 411 fn into_io_op(self) -> (FixedRegisterLoc<T>, T) { 412 (FixedRegisterLoc::new(), self) 413 } 414 } 415 416 /// Defines a dedicated type for a register, including getter and setter methods for its fields and 417 /// methods to read and write it from an [`Io`](kernel::io::Io) region. 418 /// 419 /// This documentation focuses on how to declare registers. See the [module-level 420 /// documentation](mod@kernel::io::register) for examples of how to access them. 421 /// 422 /// There are 4 possible kinds of registers: fixed offset registers, relative registers, arrays of 423 /// registers, and relative arrays of registers. 424 /// 425 /// ## Fixed offset registers 426 /// 427 /// These are the simplest kind of registers. Their location is simply an offset inside the I/O 428 /// region. For instance: 429 /// 430 /// ```ignore 431 /// register! { 432 /// pub FIXED_REG(u16) @ 0x80 { 433 /// ... 434 /// } 435 /// } 436 /// ``` 437 /// 438 /// This creates a 16-bit register named `FIXED_REG` located at offset `0x80` of an I/O region. 439 /// 440 /// These registers' location can be built simply by referencing their name: 441 /// 442 /// ```no_run 443 /// use kernel::{ 444 /// io::{ 445 /// register, 446 /// Io, 447 /// }, 448 /// }; 449 /// # use kernel::io::{Mmio, Region}; 450 /// 451 /// register! { 452 /// FIXED_REG(u32) @ 0x100 { 453 /// 15:8 high_byte; 454 /// 7:0 low_byte; 455 /// } 456 /// } 457 /// 458 /// # fn test(io: Mmio<'_, Region<0x1000>>) { 459 /// let val = io.read(FIXED_REG); 460 /// 461 /// // Write from an already-existing value. 462 /// io.write(FIXED_REG, val.with_low_byte(0xff)); 463 /// 464 /// // Create a register value from scratch. 465 /// let val2 = FIXED_REG::zeroed().with_high_byte(0x80); 466 /// 467 /// // The location of fixed offset registers is already contained in their type. Thus, the 468 /// // `location` argument of `Io::write` is technically redundant and can be replaced by `()`. 469 /// io.write((), val2); 470 /// 471 /// // Or, the single-argument `Io::write_reg` can be used. 472 /// io.write_reg(val2); 473 /// # } 474 /// 475 /// ``` 476 /// 477 /// It is possible to create an alias of an existing register with new field definitions by using 478 /// the `=> ALIAS` syntax. This is useful for cases where a register's interpretation depends on 479 /// the context: 480 /// 481 /// ```no_run 482 /// use kernel::io::register; 483 /// 484 /// register! { 485 /// /// Scratch register. 486 /// pub SCRATCH(u32) @ 0x00000200 { 487 /// 31:0 value; 488 /// } 489 /// 490 /// /// Boot status of the firmware. 491 /// pub SCRATCH_BOOT_STATUS(u32) => SCRATCH { 492 /// 0:0 completed; 493 /// } 494 /// } 495 /// ``` 496 /// 497 /// In this example, `SCRATCH_BOOT_STATUS` uses the same I/O address as `SCRATCH`, while providing 498 /// its own `completed` field. 499 /// 500 /// ## Relative registers 501 /// 502 /// Relative registers can be instantiated several times at a relative offset of a group of bases. 503 /// For instance, imagine the following I/O space: 504 /// 505 /// ```text 506 /// +-----------------------------+ 507 /// | ... | 508 /// | | 509 /// 0x100--->+------------CPU0-------------+ 510 /// | | 511 /// 0x110--->+-----------------------------+ 512 /// | CPU_CTL | 513 /// +-----------------------------+ 514 /// | ... | 515 /// | | 516 /// | | 517 /// 0x200--->+------------CPU1-------------+ 518 /// | | 519 /// 0x210--->+-----------------------------+ 520 /// | CPU_CTL | 521 /// +-----------------------------+ 522 /// | ... | 523 /// +-----------------------------+ 524 /// ``` 525 /// 526 /// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset `0x10` of their I/O 527 /// space segment. Since both instances of `CPU_CTL` share the same layout, we don't want to define 528 /// them twice and would prefer a way to select which one to use from a single definition. 529 /// 530 /// This can be done using the `Base + Offset` syntax when specifying the register's address: 531 /// 532 /// ```ignore 533 /// register! { 534 /// pub RELATIVE_REG(u32) @ Base + 0x80 { 535 /// ... 536 /// } 537 /// } 538 /// ``` 539 /// 540 /// This creates a register with an offset of `0x80` from a given base. 541 /// 542 /// `Base` is an arbitrary type (typically a ZST) to be used as a generic parameter of the 543 /// [`RegisterBase`] trait to provide the base as a constant, i.e. each type providing a base for 544 /// this register needs to implement `RegisterBase<Base>`. 545 /// 546 /// The location of relative registers can be built using the [`WithBase::of`] method to specify 547 /// its base. All relative registers implement [`WithBase`]. 548 /// 549 /// Here is the above layout translated into code: 550 /// 551 /// ```no_run 552 /// use kernel::{ 553 /// io::{ 554 /// register, 555 /// register::{ 556 /// RegisterBase, 557 /// WithBase, 558 /// }, 559 /// Io, 560 /// }, 561 /// }; 562 /// # use kernel::io::{Mmio, Region}; 563 /// 564 /// // Type used to identify the base. 565 /// pub struct CpuCtlBase; 566 /// 567 /// // ZST describing `CPU0`. 568 /// struct Cpu0; 569 /// impl RegisterBase<CpuCtlBase> for Cpu0 { 570 /// const BASE: usize = 0x100; 571 /// } 572 /// 573 /// // ZST describing `CPU1`. 574 /// struct Cpu1; 575 /// impl RegisterBase<CpuCtlBase> for Cpu1 { 576 /// const BASE: usize = 0x200; 577 /// } 578 /// 579 /// // This makes `CPU_CTL` accessible from all implementors of `RegisterBase<CpuCtlBase>`. 580 /// register! { 581 /// /// CPU core control. 582 /// pub CPU_CTL(u32) @ CpuCtlBase + 0x10 { 583 /// 0:0 start; 584 /// } 585 /// } 586 /// 587 /// # fn test(io: Mmio<'_, Region<0x1000>>) { 588 /// // Read the status of `Cpu0`. 589 /// let cpu0_started = io.read(CPU_CTL::of::<Cpu0>()); 590 /// 591 /// // Stop `Cpu0`. 592 /// io.write(WithBase::of::<Cpu0>(), CPU_CTL::zeroed()); 593 /// # } 594 /// 595 /// // Aliases can also be defined for relative register. 596 /// register! { 597 /// /// Alias to CPU core control. 598 /// pub CPU_CTL_ALIAS(u32) => CpuCtlBase + CPU_CTL { 599 /// /// Start the aliased CPU core. 600 /// 1:1 alias_start; 601 /// } 602 /// } 603 /// 604 /// # fn test2(io: Mmio<'_, Region<0x1000>>) { 605 /// // Start the aliased `CPU0`, leaving its other fields untouched. 606 /// io.update(CPU_CTL_ALIAS::of::<Cpu0>(), |r| r.with_alias_start(true)); 607 /// # } 608 /// ``` 609 /// 610 /// ## Arrays of registers 611 /// 612 /// Some I/O areas contain consecutive registers that share the same field layout. These areas can 613 /// be defined as an array of identical registers, allowing them to be accessed by index with 614 /// compile-time or runtime bound checking: 615 /// 616 /// ```ignore 617 /// register! { 618 /// pub REGISTER_ARRAY(u8)[10, stride = 4] @ 0x100 { 619 /// ... 620 /// } 621 /// } 622 /// ``` 623 /// 624 /// This defines `REGISTER_ARRAY`, an array of 10 byte registers starting at offset `0x100`. Each 625 /// register is separated from its neighbor by 4 bytes. 626 /// 627 /// The `stride` parameter is optional; if unspecified, the registers are placed consecutively from 628 /// each other. 629 /// 630 /// A location for a register in a register array is built using the [`Array::at`] trait method. 631 /// All arrays of registers implement [`Array`]. 632 /// 633 /// ```no_run 634 /// use kernel::{ 635 /// io::{ 636 /// register, 637 /// register::Array, 638 /// Io, 639 /// }, 640 /// }; 641 /// # use kernel::io::{Mmio, Region}; 642 /// # fn get_scratch_idx() -> usize { 643 /// # 0x15 644 /// # } 645 /// 646 /// // Array of 64 consecutive registers with the same layout starting at offset `0x80`. 647 /// register! { 648 /// /// Scratch registers. 649 /// pub SCRATCH(u32)[64] @ 0x00000080 { 650 /// 31:0 value; 651 /// } 652 /// } 653 /// 654 /// # fn test(io: Mmio<'_, Region<0x1000>>) 655 /// # -> Result<(), Error>{ 656 /// // Read scratch register 0, i.e. I/O address `0x80`. 657 /// let scratch_0 = io.read(SCRATCH::at(0)).value(); 658 /// 659 /// // Write scratch register 15, i.e. I/O address `0x80 + (15 * 4)`. 660 /// io.write(Array::at(15), SCRATCH::from(0xffeeaabb)); 661 /// 662 /// // This is out of bounds and won't build. 663 /// // let scratch_128 = io.read(SCRATCH::at(128)).value(); 664 /// 665 /// // Runtime-obtained array index. 666 /// let idx = get_scratch_idx(); 667 /// // Access on a runtime index returns an error if it is out-of-bounds. 668 /// let some_scratch = io.read(SCRATCH::try_at(idx).ok_or(EINVAL)?).value(); 669 /// 670 /// // Alias to a specific register in an array. 671 /// // Here `SCRATCH[8]` is used to convey the firmware exit code. 672 /// register! { 673 /// /// Firmware exit status code. 674 /// pub FIRMWARE_STATUS(u32) => SCRATCH[8] { 675 /// 7:0 status; 676 /// } 677 /// } 678 /// 679 /// let status = io.read(FIRMWARE_STATUS).status(); 680 /// 681 /// // Non-contiguous register arrays can be defined by adding a stride parameter. 682 /// // Here, each of the 16 registers of the array is separated by 8 bytes, meaning that the 683 /// // registers of the two declarations below are interleaved. 684 /// register! { 685 /// /// Scratch registers bank 0. 686 /// pub SCRATCH_INTERLEAVED_0(u32)[16, stride = 8] @ 0x000000c0 { 687 /// 31:0 value; 688 /// } 689 /// 690 /// /// Scratch registers bank 1. 691 /// pub SCRATCH_INTERLEAVED_1(u32)[16, stride = 8] @ 0x000000c4 { 692 /// 31:0 value; 693 /// } 694 /// } 695 /// # Ok(()) 696 /// # } 697 /// ``` 698 /// 699 /// ## Relative arrays of registers 700 /// 701 /// Combining the two features described in the sections above, arrays of registers accessible from 702 /// a base can also be defined: 703 /// 704 /// ```ignore 705 /// register! { 706 /// pub RELATIVE_REGISTER_ARRAY(u8)[10, stride = 4] @ Base + 0x100 { 707 /// ... 708 /// } 709 /// } 710 /// ``` 711 /// 712 /// Like relative registers, they implement the [`WithBase`] trait. However the return value of 713 /// [`WithBase::of`] cannot be used directly as a location and must be further specified using the 714 /// [`at`](RelativeRegisterLoc::at) method. 715 /// 716 /// ```no_run 717 /// use kernel::{ 718 /// io::{ 719 /// register, 720 /// register::{ 721 /// RegisterBase, 722 /// WithBase, 723 /// }, 724 /// Io, 725 /// }, 726 /// }; 727 /// # use kernel::io::{Mmio, Region}; 728 /// # fn get_scratch_idx() -> usize { 729 /// # 0x15 730 /// # } 731 /// 732 /// // Type used as parameter of `RegisterBase` to specify the base. 733 /// pub struct CpuCtlBase; 734 /// 735 /// // ZST describing `CPU0`. 736 /// struct Cpu0; 737 /// impl RegisterBase<CpuCtlBase> for Cpu0 { 738 /// const BASE: usize = 0x100; 739 /// } 740 /// 741 /// // ZST describing `CPU1`. 742 /// struct Cpu1; 743 /// impl RegisterBase<CpuCtlBase> for Cpu1 { 744 /// const BASE: usize = 0x200; 745 /// } 746 /// 747 /// // 64 per-cpu scratch registers, arranged as a contiguous array. 748 /// register! { 749 /// /// Per-CPU scratch registers. 750 /// pub CPU_SCRATCH(u32)[64] @ CpuCtlBase + 0x00000080 { 751 /// 31:0 value; 752 /// } 753 /// } 754 /// 755 /// # fn test(io: Mmio<'_, Region<0x1000>>) -> Result<(), Error> { 756 /// // Read scratch register 0 of CPU0. 757 /// let scratch = io.read(CPU_SCRATCH::of::<Cpu0>().at(0)); 758 /// 759 /// // Write the retrieved value into scratch register 15 of CPU1. 760 /// io.write(WithBase::of::<Cpu1>().at(15), scratch); 761 /// 762 /// // This won't build. 763 /// // let cpu0_scratch_128 = io.read(CPU_SCRATCH::of::<Cpu0>().at(128)).value(); 764 /// 765 /// // Runtime-obtained array index. 766 /// let scratch_idx = get_scratch_idx(); 767 /// // Access on a runtime index returns an error if it is out-of-bounds. 768 /// let cpu0_scratch = io.read( 769 /// CPU_SCRATCH::of::<Cpu0>().try_at(scratch_idx).ok_or(EINVAL)? 770 /// ).value(); 771 /// # Ok(()) 772 /// # } 773 /// 774 /// // Alias to `SCRATCH[8]` used to convey the firmware exit code. 775 /// register! { 776 /// /// Per-CPU firmware exit status code. 777 /// pub CPU_FIRMWARE_STATUS(u32) => CpuCtlBase + CPU_SCRATCH[8] { 778 /// 7:0 status; 779 /// } 780 /// } 781 /// 782 /// // Non-contiguous relative register arrays can be defined by adding a stride parameter. 783 /// // Here, each of the 16 registers of the array is separated by 8 bytes, meaning that the 784 /// // registers of the two declarations below are interleaved. 785 /// register! { 786 /// /// Scratch registers bank 0. 787 /// pub CPU_SCRATCH_INTERLEAVED_0(u32)[16, stride = 8] @ CpuCtlBase + 0x00000d00 { 788 /// 31:0 value; 789 /// } 790 /// 791 /// /// Scratch registers bank 1. 792 /// pub CPU_SCRATCH_INTERLEAVED_1(u32)[16, stride = 8] @ CpuCtlBase + 0x00000d04 { 793 /// 31:0 value; 794 /// } 795 /// } 796 /// 797 /// # fn test2(io: Mmio<'_, Region<0x1000>>) -> Result<(), Error> { 798 /// let cpu0_status = io.read(CPU_FIRMWARE_STATUS::of::<Cpu0>()).status(); 799 /// # Ok(()) 800 /// # } 801 /// ``` 802 #[macro_export] 803 macro_rules! register { 804 // Entry point for the macro, allowing multiple registers to be defined in one call. 805 // It matches all possible register declaration patterns to dispatch them to corresponding 806 // `@reg` rule that defines a single register. 807 // 808 // TODO: change `alias:ident` to `alias:path` once relative registers are replaced by I/O 809 // projections. 810 ( 811 $( 812 $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) 813 $([ $size:expr $(, stride = $stride:expr)? ])? 814 $(@ $($base:ident +)? $offset:literal)? 815 $(=> $alias:ident $(+ $alias_offset:ident)? $([$alias_idx:expr])? )? 816 { $($fields:tt)* } 817 )* 818 ) => { 819 $( 820 $crate::register!( 821 @reg $(#[$attr])* $vis $name ($storage) $([$size $(, stride = $stride)?])? 822 $(@ $($base +)? $offset)? 823 $(=> $alias $(+ $alias_offset)? $([$alias_idx])? )? 824 { $($fields)* } 825 ); 826 )* 827 }; 828 829 // All the rules below are private helpers. 830 831 // Creates a register at a fixed offset of the MMIO space. 832 ( 833 @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $offset:literal 834 { $($fields:tt)* } 835 ) => { 836 $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }); 837 $crate::register!(@io_base $name($storage) @ $offset); 838 $crate::register!(@io_fixed $(#[$attr])* $vis $name); 839 }; 840 841 // Creates an alias register of fixed offset register `alias` with its own fields. 842 ( 843 @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:path 844 { $($fields:tt)* } 845 ) => { 846 $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }); 847 $crate::register!( 848 @io_base $name($storage) @ 849 <$alias as $crate::io::register::Register>::OFFSET 850 ); 851 $crate::register!(@io_fixed $(#[$attr])* $vis $name); 852 }; 853 854 // Creates a register at a relative offset from a base address provider. 855 ( 856 @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) @ $base:ident + $offset:literal 857 { $($fields:tt)* } 858 ) => { 859 $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }); 860 $crate::register!(@io_base $name($storage) @ $offset); 861 $crate::register!(@io_relative $name @ $base); 862 }; 863 864 // Creates an alias register of relative offset register `alias` with its own fields. 865 ( 866 @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $base:ident + $alias:ident 867 { $($fields:tt)* } 868 ) => { 869 $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }); 870 $crate::register!( 871 @io_base $name($storage) @ <$alias as $crate::io::register::Register>::OFFSET 872 ); 873 $crate::register!(@io_relative $name @ $base); 874 }; 875 876 // Creates an array of registers at a fixed offset of the MMIO space. 877 ( 878 @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) 879 [ $size:expr, stride = $stride:expr ] @ $offset:literal { $($fields:tt)* } 880 ) => { 881 $crate::build_assert::static_assert!(::core::mem::size_of::<$storage>() <= $stride); 882 883 $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }); 884 $crate::register!(@io_base $name($storage) @ $offset); 885 $crate::register!(@io_array $name [ $size, stride = $stride ]); 886 }; 887 888 // Shortcut for contiguous array of registers (stride == size of element). 889 ( 890 @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ] @ $offset:literal 891 { $($fields:tt)* } 892 ) => { 893 $crate::register!( 894 @reg $(#[$attr])* $vis $name($storage) 895 [ $size, stride = ::core::mem::size_of::<$storage>() ] 896 @ $offset { $($fields)* } 897 ); 898 }; 899 900 // Creates an alias of register `idx` of array of registers `alias` with its own fields. 901 ( 902 @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) => $alias:path [ $idx:expr ] 903 { $($fields:tt)* } 904 ) => { 905 $crate::build_assert::static_assert!( 906 $idx < <$alias as $crate::io::register::RegisterArray>::SIZE 907 ); 908 909 $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }); 910 $crate::register!( 911 @io_base $name($storage) @ 912 <$alias as $crate::io::register::Register>::OFFSET 913 + $idx * <$alias as $crate::io::register::RegisterArray>::STRIDE 914 ); 915 $crate::register!(@io_fixed $(#[$attr])* $vis $name); 916 }; 917 918 // Creates an array of registers at a relative offset from a base address provider. 919 ( 920 @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) 921 [ $size:expr, stride = $stride:expr ] 922 @ $base:ident + $offset:literal { $($fields:tt)* } 923 ) => { 924 $crate::build_assert::static_assert!(::core::mem::size_of::<$storage>() <= $stride); 925 926 $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }); 927 $crate::register!(@io_base $name($storage) @ $offset); 928 $crate::register!(@io_relative_array $name [ $size, stride = $stride ] @ $base); 929 }; 930 931 // Shortcut for contiguous array of relative registers (stride == size of element). 932 ( 933 @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) [ $size:expr ] 934 @ $base:ident + $offset:literal { $($fields:tt)* } 935 ) => { 936 $crate::register!( 937 @reg $(#[$attr])* $vis $name($storage) 938 [ $size, stride = ::core::mem::size_of::<$storage>() ] 939 @ $base + $offset { $($fields)* } 940 ); 941 }; 942 943 // Creates an alias of register `idx` of relative array of registers `alias` with its own 944 // fields. 945 ( 946 @reg $(#[$attr:meta])* $vis:vis $name:ident ($storage:ty) 947 => $base:ident + $alias:ident [ $idx:expr ] { $($fields:tt)* } 948 ) => { 949 $crate::build_assert::static_assert!( 950 $idx < <$alias as $crate::io::register::RegisterArray>::SIZE 951 ); 952 953 $crate::register!(@bitfield $(#[$attr])* $vis struct $name($storage) { $($fields)* }); 954 $crate::register!( 955 @io_base $name($storage) @ 956 <$alias as $crate::io::register::Register>::OFFSET + 957 $idx * <$alias as $crate::io::register::RegisterArray>::STRIDE 958 ); 959 $crate::register!(@io_relative $name @ $base); 960 }; 961 962 // Generates the bitfield for the register. 963 // 964 // `#[allow(non_camel_case_types)]` is added since register names typically use 965 // `SCREAMING_CASE`. 966 ( 967 @bitfield $(#[$attr:meta])* $vis:vis struct $name:ident($storage:ty) { $($fields:tt)* } 968 ) => { 969 $crate::bitfield!( 970 #[allow(non_camel_case_types)] 971 $(#[$attr])* $vis struct $name($storage) { $($fields)* } 972 ); 973 }; 974 975 // Implementations shared by all registers types. 976 (@io_base $name:ident($storage:ty) @ $offset:expr) => { 977 impl $crate::io::register::Register for $name { 978 type Storage = $storage; 979 980 const OFFSET: usize = $offset; 981 } 982 }; 983 984 // Implementations of fixed registers. 985 (@io_fixed $(#[$attr:meta])* $vis:vis $name:ident) => { 986 impl $crate::io::register::FixedRegister for $name {} 987 988 $(#[$attr])* 989 $vis const $name: $crate::io::register::FixedRegisterLoc<$name> = 990 $crate::io::register::FixedRegisterLoc::<$name>::new(); 991 }; 992 993 // Implementations of relative registers. 994 (@io_relative $name:ident @ $base:ident) => { 995 impl $crate::io::register::WithBase for $name { 996 type BaseFamily = $base; 997 } 998 999 impl $crate::io::register::RelativeRegister for $name {} 1000 }; 1001 1002 // Implementations of register arrays. 1003 (@io_array $name:ident [ $size:expr, stride = $stride:expr ]) => { 1004 impl $crate::io::register::Array for $name {} 1005 1006 impl $crate::io::register::RegisterArray for $name { 1007 const SIZE: usize = $size; 1008 const STRIDE: usize = $stride; 1009 } 1010 }; 1011 1012 // Implementations of relative array registers. 1013 ( 1014 @io_relative_array $name:ident [ $size:expr, stride = $stride:expr ] @ $base:ident 1015 ) => { 1016 impl $crate::io::register::WithBase for $name { 1017 type BaseFamily = $base; 1018 } 1019 1020 impl $crate::io::register::RegisterArray for $name { 1021 const SIZE: usize = $size; 1022 const STRIDE: usize = $stride; 1023 } 1024 1025 impl $crate::io::register::RelativeRegisterArray for $name {} 1026 }; 1027 } 1028