[PATCH v3 2/7] rust: add basic mfd abstractions

Markus Probst posted 7 patches 3 weeks, 3 days ago
Only 3 patches received!
There is a newer version of this series
[PATCH v3 2/7] rust: add basic mfd abstractions
Posted by Markus Probst 3 weeks, 3 days ago
Implement the basic mfd rust abstractions required to register mfd
sub-devices, including:
- `mfd::Cell` - a safe wrapper for `struct mfd_cell`
- `mfd::CellAcpiMatch` - a safe wrapper for `struct mfd_cell_acpi_match`
- `const MFD_CELLS: Option<&'static [mfd::Cell]>` in each bus device,
  except auxiliary

The mfd sub-device registration will be done after a successful call to
probe in each bus device, except auxiliary. This guarantees that
`mfd_add_devices` will only be run at most once per device. It also
ensures that the sub-devices will be probed after the drvdata of the
device has been set.

In order to register mfd sub-devices for a device, the driver needs to
set `const MFD_CELLS` in their Driver trait implementation to Some.
A build_assert guarantees that this can only be set to Some, if
CONFIG_MFD_CORE is enabled.

Signed-off-by: Markus Probst <markus.probst@posteo.de>
---
 MAINTAINERS                     |   6 +++
 rust/bindings/bindings_helper.h |   1 +
 rust/kernel/i2c.rs              |   7 +++
 rust/kernel/lib.rs              |   1 +
 rust/kernel/mfd.rs              | 114 ++++++++++++++++++++++++++++++++++++++++
 rust/kernel/pci.rs              |   7 +++
 rust/kernel/platform.rs         |   7 +++
 rust/kernel/serdev.rs           |   6 +++
 rust/kernel/usb.rs              |   7 +++
 9 files changed, 156 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 749d63ca18fa..fa49e40836ab 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -18082,6 +18082,12 @@ F:	drivers/mfd/
 F:	include/dt-bindings/mfd/
 F:	include/linux/mfd/
 
+MULTIFUNCTION DEVICES (MFD) [RUST]
+M:	Markus Probst <markus.probst@posteo.de>
+S:	Maintained
+T:	git git://git.kernel.org/pub/scm/linux/kernel/git/lee/mfd.git
+F:	rust/kernel/mfd.rs
+
 MULTIMEDIA CARD (MMC) ETC. OVER SPI
 S:	Orphan
 F:	drivers/mmc/host/mmc_spi.c
diff --git a/rust/bindings/bindings_helper.h b/rust/bindings/bindings_helper.h
index f597fe3352f5..b7c17d1d9ece 100644
--- a/rust/bindings/bindings_helper.h
+++ b/rust/bindings/bindings_helper.h
@@ -65,6 +65,7 @@
 #include <linux/jump_label.h>
 #include <linux/led-class-multicolor.h>
 #include <linux/mdio.h>
+#include <linux/mfd/core.h>
 #include <linux/mm.h>
 #include <linux/miscdevice.h>
 #include <linux/of_device.h>
diff --git a/rust/kernel/i2c.rs b/rust/kernel/i2c.rs
index bb5b830f48c3..e733b651d878 100644
--- a/rust/kernel/i2c.rs
+++ b/rust/kernel/i2c.rs
@@ -14,6 +14,7 @@
     devres::Devres,
     driver,
     error::*,
+    mfd,
     of,
     prelude::*,
     types::{
@@ -167,6 +168,9 @@ extern "C" fn probe_callback(idev: *mut bindings::i2c_client) -> kernel::ffi::c_
             let data = T::probe(idev, info);
 
             idev.as_ref().set_drvdata(data)?;
+
+            idev.as_ref().mfd_add_devices(T::MFD_CELLS)?;
+
             Ok(0)
         })
     }
@@ -328,6 +332,9 @@ pub trait Driver: Send {
     /// The table of ACPI device ids supported by the driver.
     const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = None;
 
+    /// The mfd cells for mfd devices.
+    const MFD_CELLS: Option<&'static [mfd::Cell]> = None;
+
     /// I2C driver probe.
     ///
     /// Called when a new i2c client is added or discovered.
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 311fdf984b87..bacc54ca6aea 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -114,6 +114,7 @@
 pub mod led;
 pub mod list;
 pub mod maple_tree;
+pub mod mfd;
 pub mod miscdevice;
 pub mod mm;
 pub mod module_param;
diff --git a/rust/kernel/mfd.rs b/rust/kernel/mfd.rs
new file mode 100644
index 000000000000..6c47d9211bf2
--- /dev/null
+++ b/rust/kernel/mfd.rs
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Abstractions for the mfd subsystem.
+//!
+//! C header: [`include/linux/mfd/core.h`](srctree/include/linux/mfd/core.h)
+
+use core::{mem::MaybeUninit, ptr};
+
+use crate::{
+    device::{
+        CoreInternal,
+        Device, //
+    },
+    error::to_result,
+    prelude::*, //
+};
+
+/// A mfd cell.
+///
+/// # Invariants
+///
+/// A [`Cell`] instance represents a valid `struct mfd_cell`.
+#[repr(transparent)]
+pub struct Cell(bindings::mfd_cell);
+
+impl Cell {
+    /// Creates a new mfd cell.
+    pub const fn new(name: &'static CStr) -> Self {
+        Self(bindings::mfd_cell {
+            name: name.as_ptr().cast::<u8>(),
+
+            // SAFETY: Always safe to call.
+            // This is the const equivalent to `bindings::mfd_cell::default()`.
+            ..unsafe { MaybeUninit::zeroed().assume_init() }
+        })
+    }
+
+    /// Sets `of_compatible` and optionally `of_reg` and `use_of_reg` on the mfd cell.
+    pub const fn of(self, compatible: &'static CStr, reg: Option<u64>) -> Self {
+        Self(bindings::mfd_cell {
+            of_compatible: compatible.as_ptr().cast::<u8>(),
+            // TODO: Use `unwrap_or` once stabilized in const fn.
+            of_reg: if let Some(reg) = reg { reg } else { 0 },
+            use_of_reg: reg.is_some(),
+
+            ..self.0
+        })
+    }
+
+    /// Sets `acpi_match` on the mfd cell.
+    pub const fn acpi(self, acpi_match: &'static CellAcpiMatch) -> Self {
+        Self(bindings::mfd_cell {
+            acpi_match: &raw const acpi_match.0,
+
+            ..self.0
+        })
+    }
+}
+
+/// A mfd cell acpi match entry.
+///
+/// # Invariants
+///
+/// A [`CellAcpiMatch`] instance represents a valid `struct mfd_cell_acpi_match`.
+#[repr(transparent)]
+pub struct CellAcpiMatch(bindings::mfd_cell_acpi_match);
+
+impl CellAcpiMatch {
+    /// Creates a new mfd cell acpi match entry, using a ACPI PNP ID.
+    pub const fn pnpid(pnpid: &'static CStr) -> Self {
+        Self(bindings::mfd_cell_acpi_match {
+            pnpid: pnpid.as_ptr().cast::<u8>(),
+            adr: 0,
+        })
+    }
+
+    /// Creates a new mfd cell acpi match entry, using a ACPI ADR.
+    pub const fn adr(adr: u64) -> Self {
+        Self(bindings::mfd_cell_acpi_match {
+            pnpid: ptr::null(),
+            adr,
+        })
+    }
+}
+
+impl Device<CoreInternal> {
+    /// Registers child mfd devices.
+    // Always inline to optimize out error path of `build_assert`.
+    #[inline(always)]
+    pub(crate) fn mfd_add_devices(&self, cells: Option<&'static [Cell]>) -> Result {
+        if let Some(cells) = cells {
+            build_assert!(cfg!(CONFIG_MFD_CORE));
+
+            // SAFETY:
+            // - `self.as_raw()` is guaranteed to be a pointer to a valid `device`.
+            // - `cells.as_ptr()` is a guaranteed to be a pointer to a valid `mfd_cell` array
+            //   with the length of `cells.len()`.
+            to_result(unsafe {
+                bindings::devm_mfd_add_devices(
+                    self.as_raw(),
+                    bindings::PLATFORM_DEVID_AUTO,
+                    // CAST: `Cell` is a transparent wrapper of `mfd_cell`.
+                    cells.as_ptr().cast::<bindings::mfd_cell>(),
+                    i32::try_from(cells.len())?,
+                    ptr::null_mut(),
+                    0,
+                    ptr::null_mut(),
+                )
+            })?;
+        }
+
+        Ok(())
+    }
+}
diff --git a/rust/kernel/pci.rs b/rust/kernel/pci.rs
index af74ddff6114..6c4cf6cf970b 100644
--- a/rust/kernel/pci.rs
+++ b/rust/kernel/pci.rs
@@ -17,6 +17,7 @@
         from_result,
         to_result, //
     },
+    mfd,
     prelude::*,
     str::CStr,
     types::Opaque,
@@ -116,6 +117,9 @@ extern "C" fn probe_callback(
             let data = T::probe(pdev, info);
 
             pdev.as_ref().set_drvdata(data)?;
+
+            pdev.as_ref().mfd_add_devices(T::MFD_CELLS)?;
+
             Ok(0)
         })
     }
@@ -303,6 +307,9 @@ pub trait Driver: Send {
     /// The table of device ids supported by the driver.
     const ID_TABLE: IdTable<Self::IdInfo>;
 
+    /// The mfd cells for mfd devices.
+    const MFD_CELLS: Option<&'static [mfd::Cell]> = None;
+
     /// PCI driver probe.
     ///
     /// Called when a new pci device is added or discovered. Implementers should
diff --git a/rust/kernel/platform.rs b/rust/kernel/platform.rs
index 8917d4ee499f..e2bcf8ef093c 100644
--- a/rust/kernel/platform.rs
+++ b/rust/kernel/platform.rs
@@ -25,6 +25,7 @@
         self,
         IrqRequest, //
     },
+    mfd,
     of,
     prelude::*,
     types::Opaque,
@@ -104,6 +105,9 @@ extern "C" fn probe_callback(pdev: *mut bindings::platform_device) -> kernel::ff
             let data = T::probe(pdev, info);
 
             pdev.as_ref().set_drvdata(data)?;
+
+            pdev.as_ref().mfd_add_devices(T::MFD_CELLS)?;
+
             Ok(0)
         })
     }
@@ -218,6 +222,9 @@ pub trait Driver: Send {
     /// The table of ACPI device ids supported by the driver.
     const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = None;
 
+    /// The mfd cells for mfd devices.
+    const MFD_CELLS: Option<&'static [mfd::Cell]> = None;
+
     /// Platform driver probe.
     ///
     /// Called when a new platform device is added or discovered.
diff --git a/rust/kernel/serdev.rs b/rust/kernel/serdev.rs
index d9fea4bd4439..6e702c734ded 100644
--- a/rust/kernel/serdev.rs
+++ b/rust/kernel/serdev.rs
@@ -14,6 +14,7 @@
         to_result,
         VTABLE_DEFAULT_ERROR, //
     },
+    mfd,
     of,
     prelude::*,
     sync::Completion,
@@ -180,6 +181,8 @@ extern "C" fn probe_callback(sdev: *mut bindings::serdev_device) -> kernel::ffi:
 
             private_data.probe_complete.complete_all();
 
+            sdev.as_ref().mfd_add_devices(T::MFD_CELLS)?;
+
             result.map(|()| 0)
         })
     }
@@ -339,6 +342,9 @@ pub trait Driver: Send {
     /// The table of ACPI device ids supported by the driver.
     const ACPI_ID_TABLE: Option<acpi::IdTable<Self::IdInfo>> = None;
 
+    /// The mfd cells for mfd devices.
+    const MFD_CELLS: Option<&'static [mfd::Cell]> = None;
+
     /// Serial device bus device driver probe.
     ///
     /// Called when a new serial device bus device is added or discovered.
diff --git a/rust/kernel/usb.rs b/rust/kernel/usb.rs
index 0e1b9a88f4f1..a64ed6a530f1 100644
--- a/rust/kernel/usb.rs
+++ b/rust/kernel/usb.rs
@@ -17,6 +17,7 @@
         from_result,
         to_result, //
     },
+    mfd,
     prelude::*,
     types::{
         AlwaysRefCounted,
@@ -96,6 +97,9 @@ extern "C" fn probe_callback(
 
             let dev: &device::Device<device::CoreInternal> = intf.as_ref();
             dev.set_drvdata(data)?;
+
+            dev.mfd_add_devices(T::MFD_CELLS)?;
+
             Ok(0)
         })
     }
@@ -309,6 +313,9 @@ pub trait Driver {
     /// The table of device ids supported by the driver.
     const ID_TABLE: IdTable<Self::IdInfo>;
 
+    /// The mfd cells for mfd devices.
+    const MFD_CELLS: Option<&'static [mfd::Cell]> = None;
+
     /// USB driver probe.
     ///
     /// Called when a new USB interface is bound to this driver.

-- 
2.52.0