1023 lines · 32.5 KB · Rust
RawDownload
// SPDX-License-Identifier: GPL-2.0 //! Macro to define register layout and accessors.//!//! The [`register!`](kernel::io::register!) macro provides an intuitive and readable syntax for//! defining a dedicated type for each register and accessing it using [`Io`](super::Io). Each such//! type comes with its own field accessors that can return an error if a field's value is invalid.//!//! Note: most of the items in this module are public so they can be referenced by the macro, but//! most are not to be used directly by users. Outside of the `register!` macro itself, the only//! items you might want to import from this module are [`WithBase`] and [`Array`].//!//! # Simple example//!//! ```no_run//! use kernel::io::register;//!//! register! {//!     /// Basic information about the chip.//!     pub BOOT_0(u32) @ 0x00000100 {//!         /// Vendor ID.//!         15:8 vendor_id;//!         /// Major revision of the chip.//!         7:4 major_revision;//!         /// Minor revision of the chip.//!         3:0 minor_revision;//!     }//! }//! ```//!//! This defines a 32-bit `BOOT_0` type which can be read from or written to offset `0x100` of an//! `Io` region, with the described bitfields. For instance, `minor_revision` consists of the 4//! least significant bits of the type.//!//! Fields are instances of [`Bounded`](kernel::num::Bounded) and can be read by calling their//! getter method, which is named after them. They also have setter methods prefixed with `with_`//! for runtime values and `with_const_` for constant values. All setters return the updated//! register value.//!//! Fields can also be transparently converted from/to an arbitrary type by using the `=>` and//! `?=>` syntaxes.//!//! If present, doc comments above register or fields definitions are added to the relevant item//! they document (the register type itself, or the field's setter and getter methods).//!//! Note that multiple registers can be defined in a single `register!` invocation. This can be//! useful to group related registers together.//!//! Here is how the register defined above can be used in code://!//!//! ```no_run//! use kernel::{//!     io::{//!         register,//!         Io,//!         IoLoc,//!     },//!     num::Bounded,//! };//! # use kernel::io::Mmio;//! # register! {//! #     pub BOOT_0(u32) @ 0x00000100 {//! #         15:8 vendor_id;//! #         7:4 major_revision;//! #         3:0 minor_revision;//! #     }//! # }//! # fn test(io: &Mmio<0x1000>) {//! # fn obtain_vendor_id() -> u8 { 0xff }//!//! // Read from the register's defined offset (0x100).//! let boot0 = io.read(BOOT_0);//! pr_info!("chip revision: {}.{}", boot0.major_revision().get(), boot0.minor_revision().get());//!//! // Update some fields and write the new value back.//! let new_boot0 = boot0//!     // Constant values.//!     .with_const_major_revision::<3>()//!     .with_const_minor_revision::<10>()//!     // Runtime value.//!     .with_vendor_id(obtain_vendor_id());//! io.write_reg(new_boot0);//!//! // Or, build a new value from zero and write it://! io.write_reg(BOOT_0::zeroed()//!     .with_const_major_revision::<3>()//!     .with_const_minor_revision::<10>()//!     .with_vendor_id(obtain_vendor_id())//! );//!//! // Or, read and update the register in a single step.//! io.update(BOOT_0, |r| r//!     .with_const_major_revision::<3>()//!     .with_const_minor_revision::<10>()//!     .with_vendor_id(obtain_vendor_id())//! );//!//! // Constant values can also be built using the const setters.//! const V: BOOT_0 = pin_init::zeroed::<BOOT_0>()//!     .with_const_major_revision::<3>()//!     .with_const_minor_revision::<10>();//! # }//! ```//!//! For more extensive documentation about how to define registers, see the//! [`register!`](kernel::io::register!) macro. use core::marker::PhantomData; use crate::{    build_assert::build_assert,    io::IoLoc, //}; /// Trait implemented by all registers.pub trait Register: Sized {    /// Backing primitive type of the register.    type Storage: Into<Self> + From<Self>;     /// Start offset of the register.    ///    /// The interpretation of this offset depends on the type of the register.    const OFFSET: usize;} /// Trait implemented by registers with a fixed offset.pub trait FixedRegister: Register {} /// Allows `()` to be used as the `location` parameter of [`Io::write`](super::Io::write) when/// passing a [`FixedRegister`] value.impl<T> IoLoc<T> for ()where    T: FixedRegister,{    type IoType = T::Storage;     #[inline(always)]    fn offset(self) -> usize {        T::OFFSET    }} /// A [`FixedRegister`] carries its location in its type. Thus `FixedRegister` values can be used/// as an [`IoLoc`].impl<T> IoLoc<T> for Twhere    T: FixedRegister,{    type IoType = T::Storage;     #[inline(always)]    fn offset(self) -> usize {        T::OFFSET    }} /// Location of a fixed register.pub struct FixedRegisterLoc<T: FixedRegister>(PhantomData<T>); impl<T: FixedRegister> FixedRegisterLoc<T> {    /// Returns the location of `T`.    #[inline(always)]    // We do not implement `Default` so we can be const.    #[expect(clippy::new_without_default)]    pub const fn new() -> Self {        Self(PhantomData)    }} impl<T> IoLoc<T> for FixedRegisterLoc<T>where    T: FixedRegister,{    type IoType = T::Storage;     #[inline(always)]    fn offset(self) -> usize {        T::OFFSET    }} /// Trait providing a base address to be added to the offset of a relative register to obtain/// its actual offset.////// The `T` generic argument is used to distinguish which base to use, in case a type provides/// several bases. It is given to the `register!` macro to restrict the use of the register to/// implementors of this particular variant.pub trait RegisterBase<T> {    /// Base address to which register offsets are added.    const BASE: usize;} /// Trait implemented by all registers that are relative to a base.pub trait WithBase {    /// Family of bases applicable to this register.    type BaseFamily;     /// Returns the absolute location of this type when using `B` as its base.    #[inline(always)]    fn of<B: RegisterBase<Self::BaseFamily>>() -> RelativeRegisterLoc<Self, B>    where        Self: Register,    {        RelativeRegisterLoc::new()    }} /// Trait implemented by relative registers.pub trait RelativeRegister: Register + WithBase {} /// Location of a relative register.////// This can either be an immediately accessible regular [`RelativeRegister`], or a/// [`RelativeRegisterArray`] that needs one additional resolution through/// [`RelativeRegisterLoc::at`].pub struct RelativeRegisterLoc<T: WithBase, B: ?Sized>(PhantomData<T>, PhantomData<B>); impl<T, B> RelativeRegisterLoc<T, B>where    T: Register + WithBase,    B: RegisterBase<T::BaseFamily> + ?Sized,{    /// Returns the location of a relative register or register array.    #[inline(always)]    // We do not implement `Default` so we can be const.    #[expect(clippy::new_without_default)]    pub const fn new() -> Self {        Self(PhantomData, PhantomData)    }     // Returns the absolute offset of the relative register using base `B`.    //    // This is implemented as a private const method so it can be reused by the [`IoLoc`]    // implementations of both [`RelativeRegisterLoc`] and [`RelativeRegisterArrayLoc`].    #[inline]    const fn offset(self) -> usize {        B::BASE + T::OFFSET    }} impl<T, B> IoLoc<T> for RelativeRegisterLoc<T, B>where    T: RelativeRegister,    B: RegisterBase<T::BaseFamily> + ?Sized,{    type IoType = T::Storage;     #[inline(always)]    fn offset(self) -> usize {        RelativeRegisterLoc::offset(self)    }} /// Trait implemented by arrays of registers.pub trait RegisterArray: Register {    /// Number of elements in the registers array.    const SIZE: usize;    /// Number of bytes between the start of elements in the registers array.    const STRIDE: usize;} /// Location of an array register.pub struct RegisterArrayLoc<T: RegisterArray>(usize, PhantomData<T>); impl<T: RegisterArray> RegisterArrayLoc<T> {    /// Returns the location of register `T` at position `idx`, with build-time validation.    #[inline(always)]    pub fn new(idx: usize) -> Self {        build_assert!(idx < T::SIZE);         Self(idx, PhantomData)    }     /// Attempts to return the location of register `T` at position `idx`, with runtime validation.    #[inline(always)]    pub fn try_new(idx: usize) -> Option<Self> {        if idx < T::SIZE {            Some(Self(idx, PhantomData))        } else {            None        }    }} impl<T> IoLoc<T> for RegisterArrayLoc<T>where    T: RegisterArray,{    type IoType = T::Storage;     #[inline(always)]    fn offset(self) -> usize {        T::OFFSET + self.0 * T::STRIDE    }} /// Trait providing location builders for [`RegisterArray`]s.pub trait Array {    /// Returns the location of the register at position `idx`, with build-time validation.    #[inline(always)]    fn at(idx: usize) -> RegisterArrayLoc<Self>    where        Self: RegisterArray,    {        RegisterArrayLoc::new(idx)    }     /// Returns the location of the register at position `idx`, with runtime validation.    #[inline(always)]    fn try_at(idx: usize) -> Option<RegisterArrayLoc<Self>>    where        Self: RegisterArray,    {        RegisterArrayLoc::try_new(idx)    }} /// Trait implemented by arrays of relative registers.pub trait RelativeRegisterArray: RegisterArray + WithBase {} /// Location of a relative array register.pub struct RelativeRegisterArrayLoc<    T: RelativeRegisterArray,    B: RegisterBase<T::BaseFamily> + ?Sized,>(RelativeRegisterLoc<T, B>, usize); impl<T, B> RelativeRegisterArrayLoc<T, B>where    T: RelativeRegisterArray,    B: RegisterBase<T::BaseFamily> + ?Sized,{    /// Returns the location of register `T` from the base `B` at index `idx`, with build-time    /// validation.    #[inline(always)]    pub fn new(idx: usize) -> Self {        build_assert!(idx < T::SIZE);         Self(RelativeRegisterLoc::new(), idx)    }     /// Attempts to return the location of register `T` from the base `B` at index `idx`, with    /// runtime validation.    #[inline(always)]    pub fn try_new(idx: usize) -> Option<Self> {        if idx < T::SIZE {            Some(Self(RelativeRegisterLoc::new(), idx))        } else {            None        }    }} /// Methods exclusive to [`RelativeRegisterLoc`]s created with a [`RelativeRegisterArray`].impl<T, B> RelativeRegisterLoc<T, B>where    T: RelativeRegisterArray,    B: RegisterBase<T::BaseFamily> + ?Sized,{    /// Returns the location of the register at position `idx`, with build-time validation.    #[inline(always)]    pub fn at(self, idx: usize) -> RelativeRegisterArrayLoc<T, B> {        RelativeRegisterArrayLoc::new(idx)    }     /// Returns the location of the register at position `idx`, with runtime validation.    #[inline(always)]    pub fn try_at(self, idx: usize) -> Option<RelativeRegisterArrayLoc<T, B>> {        RelativeRegisterArrayLoc::try_new(idx)    }} impl<T, B> IoLoc<T> for RelativeRegisterArrayLoc<T, B>where    T: RelativeRegisterArray,    B: RegisterBase<T::BaseFamily> + ?Sized,{    type IoType = T::Storage;     #[inline(always)]    fn offset(self) -> usize {        self.0.offset() + self.1 * T::STRIDE    }} /// Trait implemented by items that contain both a register value and the absolute I/O location at/// which to write it.////// Implementors can be used with [`Io::write_reg`](super::Io::write_reg).pub trait LocatedRegister {    /// Register value to write.    type Value: Register;    /// Full location information at which to write the value.    type Location: IoLoc<Self::Value>;     /// Consumes `self` and returns a `(location, value)` tuple describing a valid I/O write    /// operation.    fn into_io_op(self) -> (Self::Location, Self::Value);} impl<T> LocatedRegister for Twhere    T: FixedRegister,{    type Location = FixedRegisterLoc<Self::Value>;    type Value = T;     #[inline(always)]    fn into_io_op(self) -> (FixedRegisterLoc<T>, T) {        (FixedRegisterLoc::new(), self)    }} /// Defines a dedicated type for a register, including getter and setter methods for its fields and/// methods to read and write it from an [`Io`](kernel::io::Io) region.////// This documentation focuses on how to declare registers. See the [module-level/// documentation](mod@kernel::io::register) for examples of how to access them.////// There are 4 possible kinds of registers: fixed offset registers, relative registers, arrays of/// registers, and relative arrays of registers.////// ## Fixed offset registers////// These are the simplest kind of registers. Their location is simply an offset inside the I/O/// region. For instance:////// ```ignore/// register! {///     pub FIXED_REG(u16) @ 0x80 {///         ...///     }/// }/// ```////// This creates a 16-bit register named `FIXED_REG` located at offset `0x80` of an I/O region.////// These registers' location can be built simply by referencing their name:////// ```no_run/// use kernel::{///     io::{///         register,///         Io,///     },/// };/// # use kernel::io::Mmio;////// register! {///     FIXED_REG(u32) @ 0x100 {///         16:8 high_byte;///         7:0  low_byte;///     }/// }////// # fn test(io: &Mmio<0x1000>) {/// let val = io.read(FIXED_REG);////// // Write from an already-existing value./// io.write(FIXED_REG, val.with_low_byte(0xff));////// // Create a register value from scratch./// let val2 = FIXED_REG::zeroed().with_high_byte(0x80);////// // The location of fixed offset registers is already contained in their type. Thus, the/// // `location` argument of `Io::write` is technically redundant and can be replaced by `()`./// io.write((), val2);////// // Or, the single-argument `Io::write_reg` can be used./// io.write_reg(val2);/// # }////// ```////// It is possible to create an alias of an existing register with new field definitions by using/// the `=> ALIAS` syntax. This is useful for cases where a register's interpretation depends on/// the context:////// ```no_run/// use kernel::io::register;////// register! {///     /// Scratch register.///     pub SCRATCH(u32) @ 0x00000200 {///         31:0 value;///     }//////     /// Boot status of the firmware.///     pub SCRATCH_BOOT_STATUS(u32) => SCRATCH {///         0:0 completed;///     }/// }/// ```////// In this example, `SCRATCH_BOOT_STATUS` uses the same I/O address as `SCRATCH`, while providing/// its own `completed` field.////// ## Relative registers////// Relative registers can be instantiated several times at a relative offset of a group of bases.

Lines 1–500 of 1023.