diff mbox series

[2/9] rust: add abstraction for regmap

Message ID 20241218-ncv6336-v1-2-b8d973747f7a@gmail.com
State New
Headers show
Series Regulator driver with I2C/Regmap Rust abstractions | expand

Commit Message

Fabien Parent Dec. 18, 2024, 11:36 p.m. UTC
From: Fabien Parent <fabien.parent@linaro.org>

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@linaro.org>
---
 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(+)

Comments

Mark Brown Dec. 20, 2024, 1:16 p.m. UTC | #1
On Wed, Dec 18, 2024 at 03:36:32PM -0800, Fabien Parent wrote:

> 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`.

I'm not so sure about that...

> +int rust_helper_regmap_field_write(struct regmap_field *field, unsigned int val)
> +{
> +	return regmap_field_write(field, val);
> +}

...this is wrapping the field API.  That's not very widely used at all,
and all the usages are in leaf drivers rather than frameworks.  Given
this and that the code is basically simple syntactic sugar rather than
any substantial logic I would suggest not wrapping this for Rust but
instead writing native Rust abstractions that do the same thing.  That
seems likely to be less work and give something that is more pleasant to
use in the Rust environment.

Indeed, it looks like the actual core regmap APIs aren't wrapped at all,
only the field ones?

> +//! ```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,
> +//!         }),
> +//!     })
> +//! });

You might want to include some explanation as to what this does because
it's quite unclear.

> +//! 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);

New code should use maple tree unless it's got a good reason.

> +    /// 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,

Maple Tree.

> +impl Regmap {
> +    #[cfg(CONFIG_REGMAP_I2C = "y")]

Built in only?

> +/// `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)`

Given how many register fields are just a number I'm not sure calling
them "raw" fields really conveys the right message, it sounds like this
is bypassing things that should be there rather than a perfectly normal
thing to do.
diff mbox series

Patch

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@kernel.org>
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;