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