From: Fabien Parent <fabien.parent@xxxxxxxxxx> This regmap abstraction is implemented using only the regmap_field APIs. This abstraction tries to bring some type-safety features and ease of use by enhancing the regmap API through extensive macro use to generate code. The abstraction is bringing only a small subset of all the features provided by regmap by only supporting the most vital field from `struct regmap_config`. This abstraction is used by the Regulator abstraction as well as the following driver: https://github.com/Fabo/linux/blob/b4/ncv6336/drivers/regulator/ncv6336_regulator.rs Signed-off-by: Fabien Parent <fabien.parent@xxxxxxxxxx> --- MAINTAINERS | 1 + rust/bindings/bindings_helper.h | 1 + rust/helpers/helpers.c | 1 + rust/helpers/regmap.c | 48 ++ rust/kernel/lib.rs | 2 + rust/kernel/regmap.rs | 1043 +++++++++++++++++++++++++++++++++++++++ 6 files changed, 1096 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 961fe4ed39605bf489d1d9e473f47bccb692ff14..acb3942eb1b66ec2bc09ac50f51c2054b7b45355 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -19790,6 +19790,7 @@ T: git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regmap.git F: Documentation/devicetree/bindings/regmap/ F: drivers/base/regmap/ F: include/linux/regmap.h +F: rust/kernel/regmap.rs REMOTE PROCESSOR (REMOTEPROC) SUBSYSTEM M: Bjorn Andersson <andersson@xxxxxxxxxx> diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h index a882efb90bfc27960ef1fd5f2dc8cc40533a1c27..48d2b91b34067e7e9ee9c64c2e42681e988e9aad 100644 --- a/rust/bindings/bindings_helper.h +++ b/rust/bindings/bindings_helper.h @@ -28,6 +28,7 @@ #include <linux/platform_device.h> #include <linux/poll.h> #include <linux/refcount.h> +#include <linux/regmap.h> #include <linux/sched.h> #include <linux/security.h> #include <linux/slab.h> diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c index 630e903f516ee14a51f46ff0bcc68e8f9a64021a..f78371d1932939821ecc5f57b065bd63b8bc9dee 100644 --- a/rust/helpers/helpers.c +++ b/rust/helpers/helpers.c @@ -27,6 +27,7 @@ #include "rbtree.c" #include "rcu.c" #include "refcount.c" +#include "regmap.c" #include "security.c" #include "signal.c" #include "slab.c" diff --git a/rust/helpers/regmap.c b/rust/helpers/regmap.c new file mode 100644 index 0000000000000000000000000000000000000000..2426e563df339a9c7c9c52a72f9982eea5a0ed29 --- /dev/null +++ b/rust/helpers/regmap.c @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/i2c.h> +#include <linux/regmap.h> + +#if IS_BUILTIN(CONFIG_REGMAP_I2C) +struct regmap *rust_helper_regmap_init_i2c(struct i2c_client *i2c, + const struct regmap_config *config) +{ + return regmap_init_i2c(i2c, config); +} +#endif + +int rust_helper_regmap_field_write(struct regmap_field *field, unsigned int val) +{ + return regmap_field_write(field, val); +} + +int rust_helper_regmap_field_force_write(struct regmap_field *field, + unsigned int val) +{ + return regmap_field_force_write(field, val); +} + +int rust_helper_regmap_field_update_bits(struct regmap_field *field, + unsigned int mask, unsigned int val) +{ + return regmap_field_update_bits(field, mask, val); +} + +int rust_helper_regmap_field_set_bits(struct regmap_field *field, + unsigned int bits) +{ + return regmap_field_set_bits(field, bits); +} + +int rust_helper_regmap_field_clear_bits(struct regmap_field *field, + unsigned int bits) +{ + return regmap_field_clear_bits(field, bits); +} + +int rust_helper_regmap_field_force_update_bits(struct regmap_field *field, + unsigned int mask, + unsigned int val) +{ + return regmap_field_force_update_bits(field, mask, val); +} diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs index 71ef7df94302b689be665676a36bd5c2e6effff3..456e979724d1079045cb157086ff2b2ed0fcca3b 100644 --- a/rust/kernel/lib.rs +++ b/rust/kernel/lib.rs @@ -66,6 +66,8 @@ pub mod prelude; pub mod print; pub mod rbtree; +#[cfg(CONFIG_REGMAP)] +pub mod regmap; pub mod revocable; pub mod security; pub mod seq_file; diff --git a/rust/kernel/regmap.rs b/rust/kernel/regmap.rs new file mode 100644 index 0000000000000000000000000000000000000000..232fe93df769eee97966703e0ba92c969b8f506e --- /dev/null +++ b/rust/kernel/regmap.rs @@ -0,0 +1,1043 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Register map access API. +//! +//! C header: [`include/linux/regmap.h`](srctree/include/linux/regmap.h) +//! +//! # Examples +//! +//! ```ignore +//! regmap::define_regmap_field_descs!(FIELD_DESCS, { +//! (pid, 0x3, READ, { value => raw([7:0], ro) }), +//! (limconf, 0x16, RW, { +//! rearm => bit(0, rw), +//! rststatus => bit(1, rw), +//! tpwth => enum([5:4], rw, { +//! Temp83C = 0x0, +//! Temp94C = 0x1, +//! Temp105C = 0x2, +//! Temp116C = 0x3, +//! }), +//! }) +//! }); +//! +//! fn probe(client: &mut i2c::Client) -> Result { +//! let config = regmap::Config::<AccessOps>::new(8, 8) +//! .with_max_register(0x16) +//! .with_cache_type(regmap::CacheType::RbTree); +//! let regmap = regmap::Regmap::init_i2c(client, &config); +//! let mut fields = regmap.alloc_fields(&FIELD_DESCS)?; +//! +//! dev_info!(client.as_ref(), "PID: {:#x}", pid::value::read(&mut fields)?); +//! } +//! ``` + +use crate::{ + bindings, + error::{code::*, to_result, Error, Result}, + macros::paste, + sync::Arc, +}; +#[cfg(CONFIG_REGMAP_I2C = "y")] +use crate::{error::from_err_ptr, i2c}; +use core::{marker::PhantomData, ptr::NonNull}; + +/// Type of caching +#[repr(u32)] +pub enum CacheType { + /// Don't cache anything + None = bindings::regcache_type_REGCACHE_NONE, + /// Use RbTree caching + RbTree = bindings::regcache_type_REGCACHE_RBTREE, + /// Use Flat caching + Flat = bindings::regcache_type_REGCACHE_FLAT, + /// Use Maple caching + Maple = bindings::regcache_type_REGCACHE_MAPLE, +} + +/// Register map +/// +/// Note for Rust abstractions using Regmap: +/// Regmap C structure does not implement reference count, so in order to keep the abstractions +/// safe it is essential to keep a `Arc<Regmap>` instance whenever the associated C API is holding +/// on the `struct regmap` pointer. +/// +/// # Invariants +/// +/// * `self.0` is valid, non-zero, and the memory is owned by `self`. +/// * This abstraction does not allow to disable regmap locking. +pub struct Regmap(NonNull<bindings::regmap>); + +impl Regmap { + #[cfg(CONFIG_REGMAP_I2C = "y")] + /// Initialize a [`Regmap`] instance for an `i2c` client. + pub fn init_i2c<T: ConfigOps>(i2c: &i2c::Client, config: &Config<T>) -> Result<Self> { + // SAFETY: Type invariants guarantee that `i2c.as_raw` is valid and non-null and + // the Config type invariant guarantee that `config.raw` always contains valid data. + let regmap = from_err_ptr(unsafe { bindings::regmap_init_i2c(i2c.as_raw(), &config.raw) })?; + + Ok(Regmap(NonNull::new(regmap).ok_or(EINVAL)?)) + } + + /// Return the raw pointer of this regmap. + pub fn as_raw(&self) -> *mut bindings::regmap { + self.0.as_ptr() + } +} + +impl Drop for Regmap { + fn drop(&mut self) { + // SAFETY: By the type invariant, `self.as_raw` is a valid pointer and it can be freed + // because we own the memory. + unsafe { bindings::regmap_exit(self.as_raw()) } + } +} + +// SAFETY: The type invariants guarantee that the memory of `bindings::regmap` is owned and +// guarantee that the C API is using locked accesses. +unsafe impl Send for Regmap {} + +/// Field Descriptors +/// +/// FieldDescriptors can be created by calling the [`define_regmap_field_descs`] macro. +/// +/// # Examples +/// +/// ```ignore +/// use kernel::regmap::{define_regmap_field_descs, Fields}; +/// +/// define_regmap_field_descs!(DESCS, { +/// (pid, 0x3, READ, { value => raw([7:0], ro) }) +/// }); +/// +/// struct Registrations { +/// fields: Fields<{ DESCS.len() }>, +/// } +/// ``` +pub struct FieldDescs<const N: usize>([bindings::reg_field; N]); + +impl<const N: usize> FieldDescs<N> { + // macro use only + #[doc(hidden)] + pub const fn new(fields: [bindings::reg_field; N]) -> Self { + Self(fields) + } + + /// Number of fields being held by `FieldDescs<N>` + /// + /// This function can be used to retrieve the number of fields that were + /// created when calling [`define_regmap_field_descs`]. + #[allow(clippy::len_without_is_empty)] + pub const fn len(&self) -> usize { + N + } +} + +/// Regmap fields +/// +/// # Invariants +/// +/// `self.fields` array is garanteed to contains valid and non-null pointers. +/// `self.fields[0]` memory is owned by `Fields`. +/// `self.fields[*]` values cannot be modified. +pub struct Fields<const N: usize> { + fields: [NonNull<bindings::regmap_field>; N], + + // Each regmap_field hold a pointer to the `struct regmap` instance, so we need to keep a copy + // of the wrapper around. + _regmap: Arc<Regmap>, +} +impl<const N: usize> Fields<N> { + /// Allocate regmap [`Fields`] + /// + /// This function allocate regmap fields from the `reg_fields` descriptors + pub fn new(regmap: &Arc<Regmap>, descs: &'static FieldDescs<N>) -> Result<Self> { + let mut fields = [NonNull::<bindings::regmap_field>::dangling(); N]; + // SAFETY: + // * [`Regmap`] type invariants guarantee that `Regmap::as_raw` returns a valid pointer. + // * `FieldDescs::<N>` is guaranteed to hold a valid array of size N. + to_result(unsafe { + bindings::regmap_field_bulk_alloc( + regmap.as_raw(), + fields.as_mut_ptr().cast(), + descs.0.as_ptr().cast(), + descs.0.len() as i32, + ) + })?; + + Ok(Fields { + fields, + _regmap: regmap.clone(), + }) + } + + /// Get field `index` + pub fn index(&mut self, index: usize) -> *mut bindings::regmap_field { + self.fields[index].as_ptr() + } + + // macro use only + #[doc(hidden)] + pub fn read(&mut self, index: usize) -> Result<core::ffi::c_uint> { + let mut val = 0; + + // Make sure we don't panic if the index is out of bound. + if index >= N { + return Err(EINVAL); + } + + // SAFETY: By the type invariants, we are garanteed that all fields entries point + // to valid and initialized values, hence it is safe to make this FFI call. + let ret = unsafe { bindings::regmap_field_read(self.fields[index].as_ptr(), &mut val) }; + if ret < 0 { + return Err(Error::from_errno(ret)); + } + + Ok(val) + } +} + +impl<const N: usize> Drop for Fields<N> { + fn drop(&mut self) { + // SAFETY: Per type invariant, `self.fields[0].as_mut` is garanteed to be valid and + // are owned by `Fields`. + unsafe { bindings::regmap_field_bulk_free(core::ptr::from_mut(self.fields[0].as_mut())) } + } +} + +// SAFETY: The type invariants guarantee that we own the `struct regmap_field` data and that they +// cannot be modified after allocation, and _regmap is Send, so it is safe for `Fields` to be Send. +unsafe impl<const N: usize> Send for Fields<N> {} + +macro_rules! config_with { + ($(#[$meta:meta])* $name:ident: $type:ty) => { + config_with!($(#[$meta])* $name: $type, $name); + }; + + ($(#[$meta:meta])* $name:ident: $type:ty, $e:expr) => { + paste! { + $(#[$meta])* + pub const fn [<with_$name>](mut self, $name: $type) -> Self { + self.raw.$name = $e; + self + } + } + }; +} + +// macro use only +#[doc(hidden)] +pub trait ConfigOps { + fn is_readable_reg(reg: u32) -> bool; + fn is_writeable_reg(reg: u32) -> bool; + fn is_volatile_reg(reg: u32) -> bool; + fn is_precious_reg(reg: u32) -> bool; +} + +/// Regmap Configuration +/// +/// # Invariants +/// +/// `self.raw` always contain valid data. +pub struct Config<T: ConfigOps> { + raw: bindings::regmap_config, + _phantom: PhantomData<T>, +} +impl<T: ConfigOps> Config<T> { + /// Create a new regmap Config + pub const fn new(reg_bits: i32, val_bits: i32) -> Self { + // SAFETY: FFI type is valid to be zero-initialized. + let mut cfg: bindings::regmap_config = unsafe { core::mem::zeroed() }; + + cfg.reg_bits = reg_bits; + cfg.val_bits = val_bits; + cfg.writeable_reg = Some(Self::writeable_reg_callback); + cfg.readable_reg = Some(Self::readable_reg_callback); + cfg.volatile_reg = Some(Self::volatile_reg_callback); + cfg.precious_reg = Some(Self::precious_reg_callback); + + Self { + raw: cfg, + _phantom: PhantomData, + } + } + + config_with!( + /// Specifies the maximum valid register address. + max_register: u32 + ); + + config_with!( + /// Type of caching being performed. + cache_type: CacheType, cache_type as _ + ); + + /// # Safety + /// + /// `_dev` must be a non-null and valid `struct device` pointer. + unsafe extern "C" fn writeable_reg_callback(_dev: *mut bindings::device, reg: u32) -> bool { + T::is_writeable_reg(reg) + } + + /// # Safety + /// + /// `_dev` must be a non-null and valid `struct device` pointer. + unsafe extern "C" fn readable_reg_callback(_dev: *mut bindings::device, reg: u32) -> bool { + T::is_readable_reg(reg) + } + + /// # Safety + /// + /// `_dev` must be a non-null and valid `struct device` pointer. + unsafe extern "C" fn volatile_reg_callback(_dev: *mut bindings::device, reg: u32) -> bool { + T::is_volatile_reg(reg) + } + + /// # Safety + /// + /// `_dev` must be a non-null and valid `struct device` pointer. + unsafe extern "C" fn precious_reg_callback(_dev: *mut bindings::device, reg: u32) -> bool { + T::is_precious_reg(reg) + } +} + +/// Definitions describing how registers can be accessed. +pub mod access { + /// Register can be read from. + pub const READ: u32 = 0b000001; + /// Register can be written to. + pub const WRITE: u32 = 0b000010; + /// Register should not be read outside of a call from the driver. + pub const PRECIOUS: u32 = 0b000100; + /// Register value can't be cached. + pub const VOLATILE: u32 = 0b001000; + + /// Register can be read from and written to. + pub const RW: u32 = READ | WRITE; +} + +// macro use only +#[doc(hidden)] +#[macro_export] +macro_rules! regmap_check_access { + ($type:ident, $access:expr, $reg:ident, $addr:literal) => { + if kernel::regmap::access::$type & $access > 0 && $reg == $addr { + return true; + } + }; +} +// macro use only +#[doc(hidden)] +pub use regmap_check_access; + +/// Common operations for all field types +pub trait FieldCommonOps { + /// Get the Mask for the field + fn mask() -> u32; +} + +/// Read operations for fields with `bit` type +pub trait BitFieldReadOps { + /// Returns whether the bit is set + fn is_set<const N: usize>(fields: &mut Fields<N>) -> Result<bool>; +} + +/// Write operations for fields with `bit` type +pub trait BitFieldWriteOps { + /// Set the bit + fn set<const N: usize>(fields: &mut Fields<N>) -> Result; + + /// Force set the bit + fn force_set<const N: usize>(fields: &mut Fields<N>) -> Result; + + /// Clear the bit + fn clear<const N: usize>(fields: &mut Fields<N>) -> Result; + + /// Force clear the bit + fn force_clear<const N: usize>(fields: &mut Fields<N>) -> Result; +} + +/// Read operations for fields with `enum` type +pub trait EnumFieldReadOps { + #[doc(hidden)] + /// Underlying enum type reprensenting the field values + type EnumType; + + /// Read the field + fn read<const N: usize>(fields: &mut Fields<N>) -> Result<Self::EnumType>; +} + +/// Write operations for fields with `enum` type +pub trait EnumFieldWriteOps { + #[doc(hidden)] + /// Underlying enum type reprensenting the field values + type EnumType; + + /// Write the field + fn write<const N: usize>(fields: &mut Fields<N>, val: Self::EnumType) -> Result; + + /// Force write the field + fn force_write<const N: usize>(fields: &mut Fields<N>, val: Self::EnumType) -> Result; +} + +/// Read operations for fields with `raw` type +pub trait RawFieldReadOps { + /// Read the field + fn read<const N: usize>(fields: &mut Fields<N>) -> Result<core::ffi::c_uint>; + + /// Test the field bits + fn test_bits<const N: usize>(fields: &mut Fields<N>, bits: core::ffi::c_uint) -> Result; +} + +/// Write operations for fields with `raw` type +pub trait RawFieldWriteOps { + /// Write the field + fn write<const N: usize>(fields: &mut Fields<N>, val: core::ffi::c_uint) -> Result; + + /// Force write the field + fn force_write<const N: usize>(fields: &mut Fields<N>, val: core::ffi::c_uint) -> Result; + + /// Update the field using a mask + fn update_bits<const N: usize>( + fields: &mut Fields<N>, + mask: core::ffi::c_uint, + val: core::ffi::c_uint, + ) -> Result; + + /// Force update the field using a mask + fn force_update_bits<const N: usize>( + fields: &mut Fields<N>, + mask: core::ffi::c_uint, + val: core::ffi::c_uint, + ) -> Result; + + /// Set field bits + fn set_bits<const N: usize>(fields: &mut Fields<N>, bits: core::ffi::c_uint) -> Result; + + /// Clear the field bits + fn clear_bits<const N: usize>(fields: &mut Fields<N>, bits: core::ffi::c_uint) -> Result; +} + +/// Bit field +/// +/// `bit` should be use when a feature is implemented through reading or writing a single bit of +/// a register. +/// +/// See [`BitFieldReadOps`] and [`BitFieldWriteOps`] for operations available.. +/// +/// # Syntax +/// +/// `bit(index, access)` +/// +/// where +/// * `index`: bit index starting from 0 +/// * `access`: access of the bit with the following possible values: +/// - `ro`: read-only ([`BitFieldReadOps`] gets implemented) +/// - `wo`: write-only ([`BitFieldWriteOps`] gets implemented) +/// - `rw`: read and write (both [`BitFieldReadOps`] and [`BitFieldWriteOps`] gets +/// implemented) +/// +/// # Examples +/// +/// ```ignore +/// regmap::define_regmap_field_descs!(FIELD_DESCS, { +/// (command, 0x14, RW, { +/// vselgt => bit(0, rw), +/// pwmvsel1 => bit(6, rw), +/// pwmvsel0 => bit(7, rw), +/// }) +/// }); +/// +/// command::pwmvsel0::set(&mut fields); +/// command::pwmvsel0::is_set(&mut fields); +/// command::pwmvsel0::clear(&mut fields); +/// ``` +#[macro_export] +macro_rules! regmap_field_bit { + ($field_name:ident, $access: expr, $reg:literal, $pos:literal, rw) => { + kernel::static_assert!($access & kernel::regmap::access::RW == kernel::regmap::access::RW); + + $crate::regmap_field_bit!($field_name, $reg, $pos, reserved); + $crate::regmap_field_bit!($field_name, _ro); + $crate::regmap_field_bit!($field_name, _wo); + }; + + ($field_name:ident, $access: expr, $reg:literal, $pos:literal, ro) => { + kernel::static_assert!( + $access & kernel::regmap::access::READ == kernel::regmap::access::READ + ); + + $crate::regmap_field_bit!($field_name, $reg, $pos, reserved); + $crate::regmap_field_bit!($field_name, _ro); + }; + + ($field_name:ident, $access: expr, $reg:literal, $pos:literal, wo) => { + kernel::static_assert!( + $access & kernel::regmap::access::WRITE == kernel::regmap::access::WRITE + ); + + $crate::regmap_field_bit!($field_name, $reg, $pos, reserved); + $crate::regmap_field_bit!($field_name, _wo); + }; + + ($field_name:ident, $reg:literal, $pos:literal, reserved) => { + kernel::macros::paste! { + struct [<_Bit $pos >]; + } + + impl $field_name { + pub(crate) const fn reg_field() -> bindings::reg_field { + bindings::reg_field { + reg: $reg, + lsb: $pos, + msb: $pos + 1, + id_offset: 0, + id_size: 0, + } + } + + #[allow(dead_code)] + pub(crate) const fn mask() -> u32 { + kernel::genmask!($pos, $pos) as _ + } + } + }; + + ($field_name:ident, _ro) => { + impl super::BitFieldReadOps for $field_name { + fn is_set<const N: usize>(fields: &mut regmap::Fields<N>) -> Result<bool> { + let field = fields.index(Self::id() as usize); + let mut val: core::ffi::c_uint = 0; + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { bindings::regmap_field_read(field, &mut val) })?; + Ok(val == 1) + } + } + }; + + ($field_name:ident, _wo) => { + impl super::BitFieldWriteOps for $field_name { + fn set<const N: usize>(fields: &mut regmap::Fields<N>) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { bindings::regmap_field_write(field, 1) }) + } + + fn force_set<const N: usize>(fields: &mut regmap::Fields<N>) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { bindings::regmap_field_force_write(field, 1) }) + } + + fn clear<const N: usize>(fields: &mut regmap::Fields<N>) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { bindings::regmap_field_write(field, 0) }) + } + + fn force_clear<const N: usize>(fields: &mut regmap::Fields<N>) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { bindings::regmap_field_force_write(field, 0) }) + } + } + }; +} + +/// Enum field +/// +/// `enum` should be used when a series of contineous bits represent possible values that can be +/// enumerated. +/// `enum` fields provide type-safety and preventing to write into the fields incorrect values. +/// +/// See [`EnumFieldReadOps`] and [`EnumFieldWriteOps`] for operations available.. +/// +/// # Syntax +/// +/// `enum(bits_range, access, { variant_definitions })` +/// +/// where +/// * `bits_range`: bit used to store the data. +/// * `access`: access of the bits with the following possible values: +/// - `ro`: read-only ([`EnumFieldReadOps`] gets implemented) +/// - `wo`: write-only ([`EnumFieldWriteOps`] gets implemented) +/// - `rw`: read and write (both [`EnumFieldReadOps`] and [`EnumFieldWriteOps`] gets +/// implemented) +/// * `variant_definitions`: list of all the enum variants using the syntax: `VariantName = Value,`. +/// +/// # Examples +/// +/// ```ignore +/// regmap::define_regmap_field_descs!(FIELD_DESCS, { +/// (limconf, 0x16, RW, { +/// ipeak => enum([7:6], rw, { +/// Peak3p5A = 0x0, +/// Peak4p0A = 0x1, +/// Peak4p5A = 0x2, +/// Peak5p0A = 0x3, +/// }), +/// }) +/// }); +/// +/// limconf::ipeak::write(&mut fields, limconf::ipeak::Peak4p0A); +/// limconf::ipeak::read(&mut fields); +/// ``` +#[macro_export] +macro_rules! regmap_field_enum { + ($field_name:ident, $access: expr, $reg:literal, [$msb:literal:$lsb:literal], ro, { + $($k:ident = $v:literal,)+ }) => { + kernel::static_assert!( + $access & kernel::regmap::access::READ == kernel::regmap::access::READ + ); + + $crate::regmap_field_enum!($field_name, $reg, [$msb:$lsb], reserved, { $($k = $v,)+ }); + $crate::regmap_field_enum!($field_name, _ro); + }; + + ($field_name:ident, $access: expr, $reg:literal, [$msb:literal:$lsb:literal], rw, { + $($k:ident = $v:literal,)+ }) => { + kernel::static_assert!($access & kernel::regmap::access::RW == kernel::regmap::access::RW); + + $crate::regmap_field_enum!($field_name, $reg, [$msb:$lsb], reserved, { $($k = $v,)+ }); + $crate::regmap_field_enum!($field_name, _ro); + $crate::regmap_field_enum!($field_name, _wo); + }; + + ($field_name:ident, $access: expr, $reg:literal, [$msb:literal:$lsb:literal], wo, { + $($k:ident = $v:literal,)+ }) => { + kernel::static_assert!( + $access & kernel::regmap::access::WRITE == kernel::regmap::access::WRITE + ); + + $crate::regmap_field_enum!($field_name, $reg, [$msb:$lsb], reserved, { $($k = $v,)+ }); + $crate::regmap_field_enum!($field_name, _wo); + }; + + ($field_name:ident, $reg:literal, [$msb:literal:$lsb:literal], reserved, { + $($k:ident = $v:literal,)+ }) => { + kernel::macros::paste! { + #[repr(u32)] + #[allow(non_camel_case_types)] + pub(crate) enum [<$field_name _enum>] { + $($k = $v,)+ + } + + impl TryFrom<core::ffi::c_uint> for [<$field_name _enum>] { + type Error = kernel::error::Error; + + fn try_from(raw_value: core::ffi::c_uint) -> Result<Self> { + match raw_value { + $($v => Ok(Self::$k),)+ + _ => Err(kernel::error::code::EINVAL), + } + } + } + + impl $field_name { + pub(crate) const fn reg_field() -> bindings::reg_field { + bindings::reg_field { + reg: $reg, + lsb: $lsb, + msb: $msb, + id_offset: 0, + id_size: 0, + } + } + + #[allow(dead_code)] + pub(crate) const fn mask() -> u32 { + kernel::genmask!($msb, $lsb) as _ + } + } + } + }; + + ($field_name:ident, _ro) => { + impl super::EnumFieldReadOps for $field_name { + type EnumType = kernel::macros::paste! {[<$field_name _enum>]}; + + fn read<const N: usize>(fields: &mut regmap::Fields<N>) -> Result<Self::EnumType> { + Self::EnumType::try_from(fields.read(Self::id() as usize)?) + } + } + }; + + ($field_name:ident, _wo) => { + impl super::EnumFieldWriteOps for $field_name { + type EnumType = kernel::macros::paste! {[<$field_name _enum>]}; + + fn write<const N: usize>( + fields: &mut regmap::Fields<N>, + val: Self::EnumType + ) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + let ret = unsafe { bindings::regmap_field_write(field, val as _) }; + kernel::error::to_result(ret) + } + + fn force_write<const N: usize>( + fields: &mut regmap::Fields<N>, + val: Self::EnumType + ) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + let ret = unsafe { bindings::regmap_field_force_write(field, val as _) }; + kernel::error::to_result(ret) + } + } + }; +} + +/// Raw field +/// +/// `raw` should be used when bits cannot be represented by any other field types. It provides +/// raw access to the register bits. +/// +/// # Syntax +/// +/// `raw(bits_range, access)` +/// +/// where +/// * `bits_range`: bits used to store the data. +/// * `access`: access of the bit with the following possible values: +/// - `ro`: read-only ([`RawFieldReadOps`] gets implemented) +/// - `wo`: write-only ([`RawFieldWriteOps`] gets implemented) +/// - `rw`: read and write (both [`RawFieldReadOps`] and [`RawFieldWriteOps`] gets +/// implemented) +/// +/// # Examples +/// +/// ```ignore +/// regmap::define_regmap_field_descs!(FIELD_DESCS, { +/// (pid, 0x3, READ, { value => raw([7:0], ro) }), +/// (progvsel1, 0x10, RW, { +/// voutvsel1 => raw([6:0], rw), +/// }) +/// }); +/// +/// pid::value::read(&mut fields); +/// progvsel1::voutvsel1::write(&mut fields, 0x42); +/// ``` +#[macro_export] +macro_rules! regmap_field_raw { + ($field_name:ident, $access: expr, $reg:literal, [$msb:literal:$lsb:literal], rw) => { + kernel::static_assert!($access & kernel::regmap::access::RW == kernel::regmap::access::RW); + + $crate::regmap_field_raw!($field_name, $reg, [$msb:$lsb], reserved); + $crate::regmap_field_raw!($field_name, $reg, [$msb:$lsb], _ro); + $crate::regmap_field_raw!($field_name, $reg, [$msb:$lsb], _wo); + }; + + ($field_name:ident, $access: expr, $reg:literal, [$msb:literal:$lsb:literal], ro) => { + kernel::static_assert!( + $access & kernel::regmap::access::READ == kernel::regmap::access::READ + ); + + $crate::regmap_field_raw!($field_name, $reg, [$msb:$lsb], reserved); + $crate::regmap_field_raw!($field_name, $reg, [$msb:$lsb], _ro); + }; + + ($field_name:ident, $access: expr, $reg:literal, [$msb:literal:$lsb:literal], wo) => { + kernel::static_assert!( + $access & kernel::regmap::access::WRITE == kernel::regmap::access::WRITE + ); + + $crate::regmap_field_raw!($field_name, $reg, [$msb:$lsb], reserved); + $crate::regmap_field_raw!($field_name, $reg, [$msb:$lsb], _wo); + }; + + ($field_name:ident, $reg:literal, [$msb:literal:$lsb:literal], reserved) => { + impl $field_name { + pub(crate) const fn reg_field() -> bindings::reg_field { + bindings::reg_field { + reg: $reg, + lsb: $lsb, + msb: $msb, + id_offset: 0, + id_size: 0, + } + } + + #[allow(dead_code)] + pub(crate) const fn mask() -> u32 { + kernel::genmask!($msb, $lsb) as _ + } + } + }; + + ($field_name:ident, $reg:literal, [$msb:literal:$lsb:literal], _ro) => { + impl super::RawFieldReadOps for $field_name { + fn read<const N: usize>(fields: &mut regmap::Fields<N>) -> Result<core::ffi::c_uint> { + fields.read(Self::id() as usize) + } + + fn test_bits<const N: usize>( + fields: &mut regmap::Fields<N>, + bits: core::ffi::c_uint, + ) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { bindings::regmap_field_test_bits(field, bits) }) + } + } + }; + + ($field_name:ident, $reg:literal, [$msb:literal:$lsb:literal], _wo) => { + impl super::RawFieldWriteOps for $field_name { + fn write<const N: usize>( + fields: &mut regmap::Fields<N>, + val: core::ffi::c_uint, + ) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { bindings::regmap_field_write(field, val as _) }) + } + + fn force_write<const N: usize>( + fields: &mut regmap::Fields<N>, + val: core::ffi::c_uint, + ) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { + bindings::regmap_field_force_write(field, val as _) + }) + } + + fn update_bits<const N: usize>( + fields: &mut regmap::Fields<N>, + mask: core::ffi::c_uint, + val: core::ffi::c_uint, + ) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { + bindings::regmap_field_update_bits(field, mask, val) + }) + } + + fn force_update_bits<const N: usize>( + fields: &mut regmap::Fields<N>, + mask: core::ffi::c_uint, + val: core::ffi::c_uint, + ) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { + bindings::regmap_field_force_update_bits(field, mask, val) + }) + } + + fn set_bits<const N: usize>( + fields: &mut regmap::Fields<N>, + bits: core::ffi::c_uint, + ) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { bindings::regmap_field_set_bits(field, bits) }) + } + + fn clear_bits<const N: usize>( + fields: &mut regmap::Fields<N>, + bits: core::ffi::c_uint, + ) -> Result { + let field = fields.index(Self::id() as usize); + // SAFETY: `Fields` guarantee that anything returned from `Fields::index` is valid + // and non-null, hence it is safe to perform the FFI function call. + kernel::error::to_result(unsafe { bindings::regmap_field_clear_bits(field, bits) }) + } + } + }; +} + +// macro use only +#[doc(hidden)] +#[macro_export] +macro_rules! regmap_fields { + ($type:ident, $reg:ident, $access:expr, $name:ident, $($t:tt)*) => { + kernel::macros::paste! { + #[allow(non_camel_case_types)] + pub(crate) struct $name; + + impl $name { + #[allow(dead_code)] + pub(crate) const fn id() -> super::Fields { + super::Fields::[<$reg _ $name>] + } + } + + $crate::[<regmap_field_ $type>]!($name, $access, $($t)*); + } + }; +} + +// macro use only +#[doc(hidden)] +#[macro_export] +macro_rules! regmap_reg_field { + ($reg_name:ident, $field_name:ident) => { + register::$reg_name::$field_name::reg_field() + }; +} + +// macro use only +#[doc(hidden)] +#[macro_export] +macro_rules! regmap_count_fields { + () => { 0usize }; + ($type:ident $($rhs:ident)*) => { 1 + $crate::regmap_count_fields!($($rhs)*) }; +} + +/// Define regmap field descriptors +/// +/// # Syntax +/// +/// ```ignore +/// define_regmap_field_desc!(VAR_NAME, { <register_definition>, [<register_definition>, ...] }); +/// ``` +/// +/// where `VAR_NAME`: symbol under which the regmap [`Fields`] are available. +/// +/// register_definition: +/// ```ignore +/// (name, address, access_permission, { <field_definition>, [<field_definition>, ...] }) +/// ``` +/// where +/// +/// * name: symbol under which this field will be available +/// * address: register address +/// * access_permission: [`access`] permission of the register +/// +/// field_definition: +/// ```ignore +/// field_name => <field_type>(...), +/// ``` +/// +/// where `field_name` is the symbol under which the field will be accessible. +/// +/// The following `<field_type>`s are available: +/// * [bit](`regmap_field_bit`) +/// * [enum](`regmap_field_enum`) +/// * [raw](`regmap_field_raw`) +/// +/// # Examples +/// +/// ```ignore +/// regmap::define_regmap_field_descs!(FIELD_DESCS, { +/// (pid, 0x3, READ, { value => raw([7:0], ro) }), +/// (limconf, 0x16, RW, { +/// rearm => bit(0, rw), +/// rststatus => bit(1, rw), +/// tpwth => enum([5:4], rw, { +/// Temp83C = 0x0, +/// Temp94C = 0x1, +/// Temp105C = 0x2, +/// Temp116C = 0x3, +/// }), +/// }) +/// }); +/// ``` +#[macro_export] +macro_rules! define_regmap_field_descs { + ($name:ident, { + $(( + $reg_name:ident, $reg_addr:literal, $access:expr, { + $($field_name:ident => $type:ident($($x:tt),*)),* $(,)? + } + )),+ + }) => { + mod register { + use kernel::regmap::{ + access::*, + BitFieldReadOps, BitFieldWriteOps, + ConfigOps, + EnumFieldReadOps, EnumFieldWriteOps, + RawFieldReadOps, RawFieldWriteOps + }; + + kernel::macros::paste! { + $( + pub(crate) mod $reg_name { + use kernel::{bindings, error::{Result}, regmap::{self, access::*}}; + $( + $crate::regmap_fields!($type, $reg_name, $access, $field_name, + $reg_addr, $($x),*); + )* + + #[allow(dead_code)] + pub(crate) const fn addr() -> u32 { + $reg_addr + } + } + )+ + + #[repr(u32)] + #[allow(non_camel_case_types)] + pub(crate) enum Fields { + $($( + [<$reg_name _ $field_name>], + )*)+ + } + + pub(crate) struct AccessOps; + impl ConfigOps for AccessOps { + fn is_readable_reg(reg: u32) -> bool { + $( + kernel::regmap::regmap_check_access!(READ, $access, reg, $reg_addr); + )+ + + false + } + + fn is_writeable_reg(reg: u32) -> bool { + $( + kernel::regmap::regmap_check_access!(WRITE, $access, reg, $reg_addr); + )+ + + false + } + + fn is_volatile_reg(reg: u32) -> bool { + $( + kernel::regmap::regmap_check_access!(VOLATILE, $access, reg, $reg_addr); + )+ + + false + } + + fn is_precious_reg(reg: u32) -> bool { + $( + kernel::regmap::regmap_check_access!(PRECIOUS, $access, reg, $reg_addr); + )+ + + false + } + } + } + } + + const $name: regmap::FieldDescs<{$crate::regmap_count_fields!($($($type)*)+)}> = + regmap::FieldDescs::new([ + $( + $( + $crate::regmap_reg_field!($reg_name, $field_name) + ),* + ),+ + ]); + }; +} +pub use define_regmap_field_descs; -- 2.45.2