// SPDX-License-Identifier: GPL-2.012//! Macro to define register layout and accessors.3//!4//! A single register typically includes several fields, which are accessed through a combination5//! of bit-shift and mask operations that introduce a class of potential mistakes, notably because6//! not all possible field values are necessarily valid.7//!8//! The macro in this module allow to define, using an intruitive and readable syntax, a dedicated9//! type for each register with its own field accessors that can return an error is a field's value10//! is invalid.1112/// Defines a dedicated type for a register with an absolute offset, alongside with getter and13/// setter methods for its fields and methods to read and write it from an `Io` region.14///15/// Example:16///17/// ```no_run18/// register!(BOOT_0 @ 0x00000100, "Basic revision information about the GPU" {19/// 3:0 minor_revision as u8, "Minor revision of the chip";20/// 7:4 major_revision as u8, "Major revision of the chip";21/// 28:20 chipset as u32 ?=> Chipset, "Chipset model";22/// });23/// ```24///25/// This defines a `BOOT_0` type which can be read or written from offset `0x100` of an `Io`26/// region. It is composed of 3 fields, for instance `minor_revision` is made of the 4 less27/// significant bits of the register. Each field can be accessed and modified using accessor28/// methods:29///30/// ```no_run31/// // Read from the register's defined offset (0x100).32/// let boot0 = BOOT_0::read(&bar);33/// pr_info!("chip revision: {}.{}", boot0.major_revision(), boot0.minor_revision());34///35/// // `Chipset::try_from` will be called with the value of the field and returns an error if the36/// // value is invalid.37/// let chipset = boot0.chipset()?;38///39/// // Update some fields and write the value back.40/// boot0.set_major_revision(3).set_minor_revision(10).write(&bar);41///42/// // Or just read and update the register in a single step:43/// BOOT_0::alter(&bar, |r| r.set_major_revision(3).set_minor_revision(10));44/// ```45///46/// Fields can be defined as follows:47///48/// - `as <type>` simply returns the field value casted as the requested integer type, typically49/// `u32`, `u16`, `u8` or `bool`. Note that `bool` fields must have a range of 1 bit.50/// - `as <type> => <into_type>` calls `<into_type>`'s `From::<<type>>` implementation and returns51/// the result.52/// - `as <type> ?=> <try_into_type>` calls `<try_into_type>`'s `TryFrom::<<type>>` implementation53/// and returns the result. This is useful on fields for which not all values are value.54///55/// The documentation strings are optional. If present, they will be added to the type's56/// definition, or the field getter and setter methods they are attached to.57///58/// Putting a `+` before the address of the register makes it relative to a base: the `read` and59/// `write` methods take a `base` argument that is added to the specified address before access,60/// and `try_read` and `try_write` methods are also created, allowing access with offsets unknown61/// at compile-time:62///63/// ```no_run64/// register!(CPU_CTL @ +0x0000010, "CPU core control" {65/// 0:0 start as bool, "Start the CPU core";66/// });67///68/// // Flip the `start` switch for the CPU core which base address is at `CPU_BASE`.69/// let cpuctl = CPU_CTL::read(&bar, CPU_BASE);70/// pr_info!("CPU CTL: {:#x}", cpuctl);71/// cpuctl.set_start(true).write(&bar, CPU_BASE);72/// ```73///74/// It is also possible to create a alias register by using the `=> ALIAS` syntax. This is useful75/// for cases where a register's interpretation depends on the context:76///77/// ```no_run78/// register!(SCRATCH_0 @ 0x0000100, "Scratch register 0" {79/// 31:0 value as u32, "Raw value";80///81/// register!(SCRATCH_0_BOOT_STATUS => SCRATCH_0, "Boot status of the firmware" {82/// 0:0 completed as bool, "Whether the firmware has completed booting";83/// ```84///85/// In this example, `SCRATCH_0_BOOT_STATUS` uses the same I/O address as `SCRATCH_0`, while also86/// providing its own `completed` method.87macro_rules! register {88// Creates a register at a fixed offset of the MMIO space.89(90$name:ident @ $offset:literal $(, $comment:literal)? {91$($fields:tt)*92}93) => {94register!(@common $name @ $offset $(, $comment)?);95register!(@field_accessors $name { $($fields)* });96register!(@io $name @ $offset);97};9899// Creates a alias register of fixed offset register `alias` with its own fields.100(101$name:ident => $alias:ident $(, $comment:literal)? {102$($fields:tt)*103}104) => {105register!(@common $name @ $alias::OFFSET $(, $comment)?);106register!(@field_accessors $name { $($fields)* });107register!(@io $name @ $alias::OFFSET);108};109110// Creates a register at a relative offset from a base address.111(112$name:ident @ + $offset:literal $(, $comment:literal)? {113$($fields:tt)*114}115) => {116register!(@common $name @ $offset $(, $comment)?);117register!(@field_accessors $name { $($fields)* });118register!(@io$name @ + $offset);119};120121// Creates a alias register of relative offset register `alias` with its own fields.122(123$name:ident => + $alias:ident $(, $comment:literal)? {124$($fields:tt)*125}126) => {127register!(@common $name @ $alias::OFFSET $(, $comment)?);128register!(@field_accessors $name { $($fields)* });129register!(@io $name @ + $alias::OFFSET);130};131132// All rules below are helpers.133134// Defines the wrapper `$name` type, as well as its relevant implementations (`Debug`, `BitOr`,135// and conversion to regular `u32`).136(@common $name:ident @ $offset:expr $(, $comment:literal)?) => {137$(138#[doc=$comment]139)?140#[repr(transparent)]141#[derive(Clone, Copy, Default)]142pub(crate) struct $name(u32);143144#[allow(dead_code)]145impl $name {146pub(crate) const OFFSET: usize = $offset;147}148149// TODO[REGA]: display the raw hex value, then the value of all the fields. This requires150// matching the fields, which will complexify the syntax considerably...151impl ::core::fmt::Debug for $name {152fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {153f.debug_tuple(stringify!($name))154.field(&format_args!("0x{0:x}", &self.0))155.finish()156}157}158159impl ::core::ops::BitOr for $name {160type Output = Self;161162fn bitor(self, rhs: Self) -> Self::Output {163Self(self.0 | rhs.0)164}165}166167impl ::core::convert::From<$name> for u32 {168fn from(reg: $name) -> u32 {169reg.0170}171}172};173174// Defines all the field getter/methods methods for `$name`.175(176@field_accessors $name:ident {177$($hi:tt:$lo:tt $field:ident as $type:tt178$(?=> $try_into_type:ty)?179$(=> $into_type:ty)?180$(, $comment:literal)?181;182)*183}184) => {185$(186register!(@check_field_bounds $hi:$lo $field as $type);187)*188189#[allow(dead_code)]190impl $name {191$(192register!(@field_accessor $name $hi:$lo $field as $type193$(?=> $try_into_type)?194$(=> $into_type)?195$(, $comment)?196;197);198)*199}200};201202// Boolean fields must have `$hi == $lo`.203(@check_field_bounds $hi:tt:$lo:tt $field:ident as bool) => {204#[allow(clippy::eq_op)]205const _: () = {206::kernel::build_assert!(207$hi == $lo,208concat!("boolean field `", stringify!($field), "` covers more than one bit")209);210};211};212213// Non-boolean fields must have `$hi >= $lo`.214(@check_field_bounds $hi:tt:$lo:tt $field:ident as $type:tt) => {215#[allow(clippy::eq_op)]216const _: () = {217::kernel::build_assert!(218$hi >= $lo,219concat!("field `", stringify!($field), "`'s MSB is smaller than its LSB")220);221};222};223224// Catches fields defined as `bool` and convert them into a boolean value.225(226@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool => $into_type:ty227$(, $comment:literal)?;228) => {229register!(230@leaf_accessor $name $hi:$lo $field as bool231{ |f| <$into_type>::from(if f != 0 { true } else { false }) }232$into_type => $into_type $(, $comment)?;233);234};235236// Shortcut for fields defined as `bool` without the `=>` syntax.237(238@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as bool $(, $comment:literal)?;239) => {240register!(@field_accessor $name $hi:$lo $field as bool => bool $(, $comment)?;);241};242243// Catches the `?=>` syntax for non-boolean fields.244(245@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt ?=> $try_into_type:ty246$(, $comment:literal)?;247) => {248register!(@leaf_accessor $name $hi:$lo $field as $type249{ |f| <$try_into_type>::try_from(f as $type) } $try_into_type =>250::core::result::Result<251$try_into_type,252<$try_into_type as ::core::convert::TryFrom<$type>>::Error253>254$(, $comment)?;);255};256257// Catches the `=>` syntax for non-boolean fields.258(259@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt => $into_type:ty260$(, $comment:literal)?;261) => {262register!(@leaf_accessor $name $hi:$lo $field as $type263{ |f| <$into_type>::from(f as $type) } $into_type => $into_type $(, $comment)?;);264};265266// Shortcut for fields defined as non-`bool` without the `=>` or `?=>` syntax.267(268@field_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:tt269$(, $comment:literal)?;270) => {271register!(@field_accessor $name $hi:$lo $field as $type => $type $(, $comment)?;);272};273274// Generates the accessor methods for a single field.275(276@leaf_accessor $name:ident $hi:tt:$lo:tt $field:ident as $type:ty277{ $process:expr } $to_type:ty => $res_type:ty $(, $comment:literal)?;278) => {279::kernel::macros::paste!(280const [<$field:upper>]: ::core::ops::RangeInclusive<u8> = $lo..=$hi;281const [<$field:upper _MASK>]: u32 = ((((1 << $hi) - 1) << 1) + 1) - ((1 << $lo) - 1);282const [<$field:upper _SHIFT>]: u32 = Self::[<$field:upper _MASK>].trailing_zeros();283);284285$(286#[doc="Returns the value of this field:"]287#[doc=$comment]288)?289#[inline]290pub(crate) fn $field(self) -> $res_type {291::kernel::macros::paste!(292const MASK: u32 = $name::[<$field:upper _MASK>];293const SHIFT: u32 = $name::[<$field:upper _SHIFT>];294);295let field = ((self.0 & MASK) >> SHIFT);296297$process(field)298}299300::kernel::macros::paste!(301$(302#[doc="Sets the value of this field:"]303#[doc=$comment]304)?305#[inline]306pub(crate) fn [<set_ $field>](mut self, value: $to_type) -> Self {307const MASK: u32 = $name::[<$field:upper _MASK>];308const SHIFT: u32 = $name::[<$field:upper _SHIFT>];309let value = (u32::from(value) << SHIFT) & MASK;310self.0 = (self.0 & !MASK) | value;311312self313}314);315};316317// Creates the IO accessors for a fixed offset register.318(@io $name:ident @ $offset:expr) => {319#[allow(dead_code)]320impl $name {321#[inline]322pub(crate) fn read<const SIZE: usize, T>(io: &T) -> Self where323T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,324{325Self(io.read32($offset))326}327328#[inline]329pub(crate) fn write<const SIZE: usize, T>(self, io: &T) where330T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,331{332io.write32(self.0, $offset)333}334335#[inline]336pub(crate) fn alter<const SIZE: usize, T, F>(337io: &T,338f: F,339) where340T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,341F: ::core::ops::FnOnce(Self) -> Self,342{343let reg = f(Self::read(io));344reg.write(io);345}346}347};348349// Create the IO accessors for a relative offset register.350(@io $name:ident @ + $offset:literal) => {351#[allow(dead_code)]352impl $name {353#[inline]354pub(crate) fn read<const SIZE: usize, T>(355io: &T,356base: usize,357) -> Self where358T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,359{360Self(io.read32(base + $offset))361}362363#[inline]364pub(crate) fn write<const SIZE: usize, T>(365self,366io: &T,367base: usize,368) where369T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,370{371io.write32(self.0, base + $offset)372}373374#[inline]375pub(crate) fn alter<const SIZE: usize, T, F>(376io: &T,377base: usize,378f: F,379) where380T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,381F: ::core::ops::FnOnce(Self) -> Self,382{383let reg = f(Self::read(io, base));384reg.write(io, base);385}386387#[inline]388pub(crate) fn try_read<const SIZE: usize, T>(389io: &T,390base: usize,391) -> ::kernel::error::Result<Self> where392T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,393{394io.try_read32(base + $offset).map(Self)395}396397#[inline]398pub(crate) fn try_write<const SIZE: usize, T>(399self,400io: &T,401base: usize,402) -> ::kernel::error::Result<()> where403T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,404{405io.try_write32(self.0, base + $offset)406}407408#[inline]409pub(crate) fn try_alter<const SIZE: usize, T, F>(410io: &T,411base: usize,412f: F,413) -> ::kernel::error::Result<()> where414T: ::core::ops::Deref<Target = ::kernel::io::Io<SIZE>>,415F: ::core::ops::FnOnce(Self) -> Self,416{417let reg = f(Self::try_read(io, base)?);418reg.try_write(io, base)419}420}421};422}423424425