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}