Skip to main content

kernel/io/
register.rs

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//! item you might want to import from this module is [`Array`].
12//!
13//! # Simple example
14//!
15//! ```no_run
16//! use kernel::io::{
17//!     register,
18//!     Region,
19//! };
20//!
21//! register! {
22//!     base: Region<0x1000>;
23//!
24//!     /// Basic information about the chip.
25//!     pub BOOT_0(u32) @ 0x00000100 {
26//!         /// Vendor ID.
27//!         15:8 vendor_id;
28//!         /// Major revision of the chip.
29//!         7:4 major_revision;
30//!         /// Minor revision of the chip.
31//!         3:0 minor_revision;
32//!     }
33//! }
34//! ```
35//!
36//! This defines a 32-bit `BOOT_0` type which can be read from or written to offset `0x100` of an
37//! `Io` region, with the described bitfields. For instance, `minor_revision` consists of the 4
38//! least significant bits of the type.
39//!
40//! Fields are instances of [`Bounded`](kernel::num::Bounded) and can be read by calling their
41//! getter method, which is named after them. They also have setter methods prefixed with `with_`
42//! for runtime values and `with_const_` for constant values. All setters return the updated
43//! register value.
44//!
45//! Fields can also be transparently converted from/to an arbitrary type by using the `=>` and
46//! `?=>` syntaxes.
47//!
48//! If present, doc comments above register or fields definitions are added to the relevant item
49//! they document (the register type itself, or the field's setter and getter methods).
50//!
51//! Note that multiple registers can be defined in a single `register!` invocation. This can be
52//! useful to group related registers together.
53//!
54//! Here is how the register defined above can be used in code:
55//!
56//!
57//! ```no_run
58//! use kernel::{
59//!     io::{
60//!         register,
61//!         Io,
62//!         IoLoc,
63//!         Region,
64//!     },
65//!     num::Bounded,
66//! };
67//! # use kernel::io::Mmio;
68//! # register! {
69//! #     base: Region<0x1000>;
70//! #
71//! #     pub BOOT_0(u32) @ 0x00000100 {
72//! #         15:8 vendor_id;
73//! #         7:4 major_revision;
74//! #         3:0 minor_revision;
75//! #     }
76//! # }
77//! # fn test(io: Mmio<'_, Region<0x1000>>) {
78//! # fn obtain_vendor_id() -> u8 { 0xff }
79//!
80//! // Read from the register's defined offset (0x100).
81//! let boot0 = io.read(BOOT_0);
82//! pr_info!("chip revision: {}.{}", boot0.major_revision().get(), boot0.minor_revision().get());
83//!
84//! // Update some fields and write the new value back.
85//! let new_boot0 = boot0
86//!     // Constant values.
87//!     .with_const_major_revision::<3>()
88//!     .with_const_minor_revision::<10>()
89//!     // Runtime value.
90//!     .with_vendor_id(obtain_vendor_id());
91//! io.write_reg(new_boot0);
92//!
93//! // Or, build a new value from zero and write it:
94//! io.write_reg(BOOT_0::zeroed()
95//!     .with_const_major_revision::<3>()
96//!     .with_const_minor_revision::<10>()
97//!     .with_vendor_id(obtain_vendor_id())
98//! );
99//!
100//! // Or, read and update the register in a single step.
101//! io.update(BOOT_0, |r| r
102//!     .with_const_major_revision::<3>()
103//!     .with_const_minor_revision::<10>()
104//!     .with_vendor_id(obtain_vendor_id())
105//! );
106//!
107//! // Constant values can also be built using the const setters.
108//! const V: BOOT_0 = pin_init::zeroed::<BOOT_0>()
109//!     .with_const_major_revision::<3>()
110//!     .with_const_minor_revision::<10>();
111//! # }
112//! ```
113//!
114//! For more extensive documentation about how to define registers, see the
115//! [`register!`](kernel::io::register!) macro.
116
117use core::marker::PhantomData;
118
119use crate::{
120    build_assert::build_assert,
121    io::IoLoc, //
122};
123
124/// Allows `()` to be used as the `location` parameter of [`Io::write`](super::Io::write) when
125/// passing a [`FixedIoLoc`] value.
126impl<Base: ?Sized, T> IoLoc<Base, T> for ()
127where
128    T: FixedIoLoc<Base>,
129{
130    #[inline(always)]
131    fn offset(self) -> usize {
132        T::LOCATION.offset()
133    }
134}
135
136// Provides a `IoLoc` impl that for a fixed offset.
137#[doc(hidden)]
138pub struct OffsetLoc<Base: ?Sized, T>(usize, PhantomData<(T, Base)>);
139
140impl<Base: ?Sized, T> OffsetLoc<Base, T> {
141    #[inline]
142    pub const fn new(offset: usize) -> Self {
143        Self(offset, PhantomData)
144    }
145
146    #[inline]
147    pub const fn const_offset(self) -> usize {
148        self.0
149    }
150}
151
152impl<Base: ?Sized, T> IoLoc<Base, T> for OffsetLoc<Base, T> {
153    #[inline(always)]
154    fn offset(self) -> usize {
155        self.0
156    }
157}
158
159/// Trait implemented by arrays of registers.
160pub trait RegisterArray: Sized {
161    /// Base type for this register.
162    type Base: ?Sized;
163
164    /// Start offset of the register.
165    ///
166    /// The interpretation of this offset depends on the type of the register.
167    const OFFSET: usize;
168    /// Number of elements in the registers array.
169    const SIZE: usize;
170    /// Number of bytes between the start of elements in the registers array.
171    const STRIDE: usize;
172}
173
174/// Location of an array register.
175pub struct RegisterArrayLoc<T: RegisterArray>(usize, PhantomData<T>);
176
177impl<T: RegisterArray> RegisterArrayLoc<T> {
178    /// Returns the location of register `T` at position `idx`, with build-time validation.
179    #[inline(always)]
180    pub fn new(idx: usize) -> Self {
181        build_assert!(idx < T::SIZE);
182
183        Self(idx, PhantomData)
184    }
185
186    /// Attempts to return the location of register `T` at position `idx`, with runtime validation.
187    #[inline(always)]
188    pub fn try_new(idx: usize) -> Option<Self> {
189        if idx < T::SIZE {
190            Some(Self(idx, PhantomData))
191        } else {
192            None
193        }
194    }
195}
196
197impl<Base: ?Sized, T> IoLoc<Base, T> for RegisterArrayLoc<T>
198where
199    T: RegisterArray<Base = Base>,
200{
201    #[inline(always)]
202    fn offset(self) -> usize {
203        T::OFFSET + self.0 * T::STRIDE
204    }
205}
206
207/// Trait providing location builders for [`RegisterArray`]s.
208pub trait Array {
209    /// Returns the location of the register at position `idx`, with build-time validation.
210    #[inline(always)]
211    fn at(idx: usize) -> RegisterArrayLoc<Self>
212    where
213        Self: RegisterArray,
214    {
215        RegisterArrayLoc::new(idx)
216    }
217
218    /// Returns the location of the register at position `idx`, with runtime validation.
219    #[inline(always)]
220    fn try_at(idx: usize) -> Option<RegisterArrayLoc<Self>>
221    where
222        Self: RegisterArray,
223    {
224        RegisterArrayLoc::try_new(idx)
225    }
226}
227
228/// Trait implemented by types that indicate there is a fixed I/O location for this given type.
229///
230/// Implementors can be used with [`Io::write_reg`](super::Io::write_reg).
231pub trait FixedIoLoc<Base: ?Sized>: Sized {
232    /// Type of [`FixedIoLoc::LOCATION`].
233    type Location: IoLoc<Base, Self>;
234
235    /// Location of this type within given base.
236    const LOCATION: Self::Location;
237}
238
239/// Trait implemented by items that contain both a register value and the absolute I/O location at
240/// which to write it.
241///
242/// Implementors can be used with [`Io::write_reg`](super::Io::write_reg).
243pub trait LocatedRegister<Base: ?Sized> {
244    /// Value to write.
245    type Value;
246    /// Full location information at which to write the value.
247    type Location: IoLoc<Base, Self::Value>;
248
249    /// Consumes `self` and returns a `(location, value)` tuple describing a valid I/O write
250    /// operation.
251    fn into_io_op(self) -> (Self::Location, Self::Value);
252}
253
254impl<Base: ?Sized, T> LocatedRegister<Base> for T
255where
256    T: FixedIoLoc<Base>,
257{
258    type Location = T::Location;
259    type Value = T;
260
261    #[inline(always)]
262    fn into_io_op(self) -> (T::Location, T) {
263        (T::LOCATION, self)
264    }
265}
266
267/// Helper function for register element alias implementation.
268///
269/// This is used to enforce base matching and provide bounds checking.
270#[doc(hidden)]
271#[inline(always)] // for const eval only
272pub const fn element_alias_offset<Base: ?Sized, Alias: RegisterArray<Base = Base>>(
273    idx: usize,
274) -> usize {
275    assert!(idx < Alias::SIZE);
276    Alias::OFFSET + idx * Alias::STRIDE
277}
278
279/// Defines a dedicated type for a register, including getter and setter methods for its fields and
280/// methods to read and write it from an [`Io`](kernel::io::Io) region.
281///
282/// This documentation focuses on how to declare registers. See the [module-level
283/// documentation](mod@kernel::io::register) for examples of how to access them.
284///
285/// Registers can either be fixed offset registers or arrays of registers.
286///
287/// ## Fixed offset registers
288///
289/// These are the simplest kind of registers. Their location is simply an offset inside the I/O
290/// region. For instance:
291///
292/// ```ignore
293/// register! {
294///     pub FIXED_REG(u16) @ 0x80 {
295///         ...
296///     }
297/// }
298/// ```
299///
300/// This creates a 16-bit register named `FIXED_REG` located at offset `0x80` of an I/O region.
301///
302/// These registers' location can be built simply by referencing their name:
303///
304/// ```no_run
305/// use kernel::{
306///     io::{
307///         register,
308///         Io,
309///         Region,
310///     },
311/// };
312/// # use kernel::io::Mmio;
313///
314/// register! {
315///     base: Region<0x1000>;
316///
317///     FIXED_REG(u32) @ 0x100 {
318///         15:8 high_byte;
319///         7:0  low_byte;
320///     }
321/// }
322///
323/// # fn test(io: Mmio<'_, Region<0x1000>>) {
324/// let val = io.read(FIXED_REG);
325///
326/// // Write from an already-existing value.
327/// io.write(FIXED_REG, val.with_low_byte(0xff));
328///
329/// // Create a register value from scratch.
330/// let val2 = FIXED_REG::zeroed().with_high_byte(0x80);
331///
332/// // The location of fixed offset registers is already contained in their type. Thus, the
333/// // `location` argument of `Io::write` is technically redundant and can be replaced by `()`.
334/// io.write((), val2);
335///
336/// // Or, the single-argument `Io::write_reg` can be used.
337/// io.write_reg(val2);
338/// # }
339///
340/// ```
341///
342/// It is possible to create an alias of an existing register with new field definitions by using
343/// the `=> ALIAS` syntax. This is useful for cases where a register's interpretation depends on
344/// the context:
345///
346/// ```no_run
347/// use kernel::io::{
348///     register,
349///     Region,
350/// };
351///
352/// register! {
353///     base: Region<0x1000>;
354///
355///     /// Scratch register.
356///     pub SCRATCH(u32) @ 0x00000200 {
357///         31:0 value;
358///     }
359///
360///     /// Boot status of the firmware.
361///     pub SCRATCH_BOOT_STATUS(u32) => SCRATCH {
362///         0:0 completed;
363///     }
364/// }
365/// ```
366///
367/// In this example, `SCRATCH_BOOT_STATUS` uses the same I/O address as `SCRATCH`, while providing
368/// its own `completed` field.
369///
370/// If you do not wish to have a bitfield defined, you can also create a register using an existing
371/// type.
372///
373/// ```no_run
374/// # use kernel::io::*;
375/// register! {
376///     base: Region<0x1000>;
377///
378///     /// UART RX register.
379///     pub UART_RX: u8 @ 0x100;
380/// }
381/// ```
382///
383/// In case there is a fixed register associated with a specific type in the base, you can apply
384/// `#[unique]` attribute which enables `write_reg` shorthand. This is automatically applied to
385/// bitfields instantiated via the `register!` macro.
386///
387/// This should only be used when types meaningfully represent a register. For example, in the
388/// previous `UART_RX` example, even if only a single register is defined with `u8` type, it is a
389/// bad idea to annotate it with `#[unique]`.
390///
391/// ```no_run
392/// # use kernel::{bitfield, io::*};
393///
394/// bitfield! {
395///     pub struct Reset(u32) {
396///         0:0 reset;
397///     }
398/// }
399///
400/// register! {
401///     base: Region<0x1000>;
402///
403///     pub RESET: #[unique] Reset @ 0x100;
404/// }
405///
406/// # fn test(mmio: Mmio<'_, Region<0x1000>>) {
407/// // let mmio: Mmio<'_, Region<0x1000>>;
408/// mmio.write_reg(Reset::zeroed().with_const_reset::<1>());
409/// # }
410/// ```
411///
412/// ## Arrays of registers
413///
414/// Some I/O areas contain consecutive registers that share the same field layout. These areas can
415/// be defined as an array of identical registers, allowing them to be accessed by index with
416/// compile-time or runtime bound checking:
417///
418/// ```ignore
419/// register! {
420///     pub REGISTER_ARRAY(u8)[10, stride = 4] @ 0x100 {
421///         ...
422///     }
423/// }
424/// ```
425///
426/// This defines `REGISTER_ARRAY`, an array of 10 byte registers starting at offset `0x100`. Each
427/// register is separated from its neighbor by 4 bytes.
428///
429/// The `stride` parameter is optional; if unspecified, the registers are placed consecutively from
430/// each other.
431///
432/// A location for a register in a register array is built using the [`Array::at`] trait method.
433/// All arrays of registers implement [`Array`].
434///
435/// ```no_run
436/// use kernel::{
437///     io::{
438///         register,
439///         register::Array,
440///         Io,
441///         Region,
442///     },
443/// };
444/// # use kernel::io::Mmio;
445/// # fn get_scratch_idx() -> usize {
446/// #   0x15
447/// # }
448///
449/// // Array of 64 consecutive registers with the same layout starting at offset `0x80`.
450/// register! {
451///     base: Region<0x1000>;
452///
453///     /// Scratch registers.
454///     pub SCRATCH(u32)[64] @ 0x00000080 {
455///         31:0 value;
456///     }
457/// }
458///
459/// # fn test(io: Mmio<'_, Region<0x1000>>)
460/// #     -> Result<(), Error>{
461/// // Read scratch register 0, i.e. I/O address `0x80`.
462/// let scratch_0 = io.read(SCRATCH::at(0)).value();
463///
464/// // Write scratch register 15, i.e. I/O address `0x80 + (15 * 4)`.
465/// io.write(Array::at(15), SCRATCH::from(0xffeeaabb));
466///
467/// // This is out of bounds and won't build.
468/// // let scratch_128 = io.read(SCRATCH::at(128)).value();
469///
470/// // Runtime-obtained array index.
471/// let idx = get_scratch_idx();
472/// // Access on a runtime index returns an error if it is out-of-bounds.
473/// let some_scratch = io.read(SCRATCH::try_at(idx).ok_or(EINVAL)?).value();
474///
475/// // Alias to a specific register in an array.
476/// // Here `SCRATCH[8]` is used to convey the firmware exit code.
477/// register! {
478///     base: Region<0x1000>;
479///
480///     /// Firmware exit status code.
481///     pub FIRMWARE_STATUS(u32) => SCRATCH[8] {
482///         7:0 status;
483///     }
484/// }
485///
486/// let status = io.read(FIRMWARE_STATUS).status();
487///
488/// // Non-contiguous register arrays can be defined by adding a stride parameter.
489/// // Here, each of the 16 registers of the array is separated by 8 bytes, meaning that the
490/// // registers of the two declarations below are interleaved.
491/// register! {
492///     base: Region<0x1000>;
493///
494///     /// Scratch registers bank 0.
495///     pub SCRATCH_INTERLEAVED_0(u32)[16, stride = 8] @ 0x000000c0 {
496///         31:0 value;
497///     }
498///
499///     /// Scratch registers bank 1.
500///     pub SCRATCH_INTERLEAVED_1(u32)[16, stride = 8] @ 0x000000c4 {
501///         31:0 value;
502///     }
503/// }
504/// # Ok(())
505/// # }
506/// ```
507///
508/// ## Relative registers
509///
510/// There are cases where a register region is subdivided into small subregions, and you may wish to
511/// have your register definition be relative to these subregions. This may be needed, for example,
512/// if these subregions are instantiated several times, or you just want it for encapsulation
513/// purpose.
514///
515/// For instance, imagine the following I/O space:
516///
517/// ```text
518///           +-----------------------------+
519///           |             ...             |
520///           |                             |
521///  0x100--->+------------CPU0-------------+
522///           |                             |
523///  0x110--->+-----------------------------+
524///           |           CPU_CTL           |
525///           +-----------------------------+
526///           |             ...             |
527///           |                             |
528///           |                             |
529///  0x200--->+------------CPU1-------------+
530///           |                             |
531///  0x210--->+-----------------------------+
532///           |           CPU_CTL           |
533///           +-----------------------------+
534///           |             ...             |
535///           +-----------------------------+
536/// ```
537///
538/// `CPU0` and `CPU1` both have a `CPU_CTL` register that starts at offset `0x10` of their I/O
539/// space segment. Since both instances of `CPU_CTL` share the same layout, we don't want to define
540/// them twice and would prefer a way to select which one to use from a single definition.
541///
542/// This can be done by defining a new type for the subregion, and then defining registers that use
543/// the new type as the base:
544///
545/// ```no_run
546/// use kernel::{
547///     io::{
548///         io_project,
549///         register,
550///         Io,
551///         Region,
552///     },
553/// };
554/// # use kernel::io::Mmio;
555///
556/// // Subregion type. Make sure it has adequate size and alignment.
557/// #[repr(align(4))]
558/// #[derive(FromBytes, IntoBytes)]
559/// pub struct CpuCtl([u8; 0x100]);
560///
561/// register! {
562///     base: Region<0x1000>;
563///
564///     // Subregions can just be defined like normal registers.
565///     CPU0: CpuCtl @ 0x100;
566///     CPU1: CpuCtl @ 0x200;
567/// }
568///
569/// // Then you can define new registers on the subregion.
570/// register! {
571///     base: CpuCtl;
572///
573///     /// CPU core control.
574///     pub CPU_CTL(u32) @ 0x10 {
575///         0:0 start;
576///     }
577/// }
578///
579/// # fn test(io: Mmio<'_, Region<0x1000>>) {
580/// // Read the status of `Cpu0`.
581/// let cpu0_started = io_project!(io, build: CPU0).read(CPU_CTL);
582///
583/// // Stop `Cpu0`.
584/// io_project!(io, build: CPU0).write_reg(CPU_CTL::zeroed());
585/// # }
586/// ```
587#[macro_export]
588macro_rules! register {
589    ($($tt:tt)*) => {
590        $crate::macros::register!($($tt)*);
591    };
592}