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(+)
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;