linux-i3c.lists.infradead.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/4] i3c: master: Add helpers for DMA mapping and bounce buffer handling
@ 2025-07-31 14:14 Jarkko Nikula
  2025-07-31 14:14 ` [PATCH 2/4] i3c: mipi-i3c-hci: Use core helpers for DMA mapping and bounce buffering Jarkko Nikula
                   ` (3 more replies)
  0 siblings, 4 replies; 11+ messages in thread
From: Jarkko Nikula @ 2025-07-31 14:14 UTC (permalink / raw)
  To: linux-i3c; +Cc: Alexandre Belloni, Frank Li, Jarkko Nikula

Add helpers for I3C host controller drivers for DMA mapping/unmapping
and bounce buffer handling. A bounce buffer is allocated if the buffer
is not DMA'able or when the driver requires it for a transfer.

Signed-off-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
---
 drivers/i3c/master.c       | 74 ++++++++++++++++++++++++++++++++++++++
 include/linux/i3c/master.h | 20 +++++++++++
 2 files changed, 94 insertions(+)

diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
index 2ef898a8fd80..a1daf18ea707 100644
--- a/drivers/i3c/master.c
+++ b/drivers/i3c/master.c
@@ -8,6 +8,7 @@
 #include <linux/atomic.h>
 #include <linux/bug.h>
 #include <linux/device.h>
+#include <linux/dma-mapping.h>
 #include <linux/err.h>
 #include <linux/export.h>
 #include <linux/kernel.h>
@@ -1727,6 +1728,79 @@ int i3c_master_do_daa(struct i3c_master_controller *master)
 }
 EXPORT_SYMBOL_GPL(i3c_master_do_daa);
 
+/**
+ * i3c_master_dma_map_single() - Map buffer for single DMA transfer
+ * @dev: device object of a device doing DMA
+ * @buf: destination/source buffer for DMA
+ * @len: length of transfer
+ * @need_bounce: true if buffer is not DMA safe and need a bounce buffer
+ * @dir: DMA direction
+ *
+ * Map buffer for a DMA transfer and allocate a bounce buffer if required.
+ *
+ * Return: I3C DMA transfer descriptor or NULL in case of error.
+ */
+struct i3c_dma *i3c_master_dma_map_single(struct device *dev, void *buf,
+	size_t len, bool need_bounce, enum dma_data_direction dir)
+{
+	struct i3c_dma *dma_xfer;
+	void *dma_buf = buf;
+
+	dma_xfer = kzalloc(sizeof(*dma_xfer), GFP_KERNEL);
+	if (!dma_xfer)
+		return NULL;
+
+	dma_xfer->buf = buf;
+	dma_xfer->dir = dir;
+	dma_xfer->len = len;
+	if (is_vmalloc_addr(buf))
+		need_bounce = true;
+
+	if (need_bounce) {
+		if (dir == DMA_FROM_DEVICE)
+			dma_buf = kzalloc(ALIGN(len, cache_line_size()),
+						GFP_KERNEL);
+		else
+			dma_buf = kmemdup(buf, len, GFP_KERNEL);
+		if (!dma_buf)
+			goto err_alloc;
+
+		dma_xfer->bounce_buf = dma_buf;
+	}
+
+	dma_xfer->addr = dma_map_single(dev, dma_buf, len, dir);
+	if (dma_mapping_error(dev, dma_xfer->addr))
+		goto err_map;
+
+	return dma_xfer;
+err_map:
+	kfree(dma_xfer->bounce_buf);
+err_alloc:
+	kfree(dma_xfer);
+	return NULL;
+}
+EXPORT_SYMBOL_GPL(i3c_master_dma_map_single);
+
+/**
+ * i3c_master_dma_unmap_single() - Unmap buffer after DMA
+ * @dev: device object of a device doing DMA
+ * @dma_xfer: DMA transfer and mapping descriptor
+ *
+ * Unmap buffer and cleanup DMA transfer descriptor.
+ */
+void i3c_master_dma_unmap_single(struct device *dev, struct i3c_dma *dma_xfer)
+{
+	dma_unmap_single(dev, dma_xfer->addr, dma_xfer->len, dma_xfer->dir);
+	if (dma_xfer->bounce_buf) {
+		if (dma_xfer->dir == DMA_FROM_DEVICE)
+			memcpy(dma_xfer->buf, dma_xfer->bounce_buf,
+			       dma_xfer->len);
+		kfree(dma_xfer->bounce_buf);
+	}
+	kfree(dma_xfer);
+}
+EXPORT_SYMBOL_GPL(i3c_master_dma_unmap_single);
+
 /**
  * i3c_master_set_info() - set master device information
  * @master: master used to send frames on the bus
diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
index 043f5c7ff398..e39350b1038e 100644
--- a/include/linux/i3c/master.h
+++ b/include/linux/i3c/master.h
@@ -558,6 +558,22 @@ struct i3c_master_controller {
 #define i3c_bus_for_each_i3cdev(bus, dev)				\
 	list_for_each_entry(dev, &(bus)->devs.i3c, common.node)
 
+/**
+ * struct i3c_dma - DMA transfer and mapping descriptor
+ * @buf: destination/source buffer for DMA
+ * @len: length of transfer
+ * @addr: mapped DMA address for a Host Controller Driver
+ * @dir: DMA direction
+ * @bounce_buf: an allocated bounce buffer if transfer needs it or NULL
+ */
+struct i3c_dma {
+	void *buf;
+	size_t len;
+	dma_addr_t addr;
+	enum dma_data_direction dir;
+	void *bounce_buf;
+};
+
 int i3c_master_do_i2c_xfers(struct i3c_master_controller *master,
 			    const struct i2c_msg *xfers,
 			    int nxfers);
@@ -575,6 +591,10 @@ int i3c_master_get_free_addr(struct i3c_master_controller *master,
 int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
 				  u8 addr);
 int i3c_master_do_daa(struct i3c_master_controller *master);
+struct i3c_dma *i3c_master_dma_map_single(struct device *dev, void *ptr,
+					  size_t len, bool dma_safe,
+					  enum dma_data_direction dir);
+void i3c_master_dma_unmap_single(struct device *dev, struct i3c_dma *dma_xfer);
 
 int i3c_master_set_info(struct i3c_master_controller *master,
 			const struct i3c_device_info *info);
-- 
2.47.2


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 2/4] i3c: mipi-i3c-hci: Use core helpers for DMA mapping and bounce buffering
  2025-07-31 14:14 [PATCH 1/4] i3c: master: Add helpers for DMA mapping and bounce buffer handling Jarkko Nikula
@ 2025-07-31 14:14 ` Jarkko Nikula
  2025-08-01 14:41   ` Frank Li
  2025-07-31 14:14 ` [PATCH 3/4] i3c: mipi-i3c-hci: Use physical device pointer with DMA API Jarkko Nikula
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 11+ messages in thread
From: Jarkko Nikula @ 2025-07-31 14:14 UTC (permalink / raw)
  To: linux-i3c; +Cc: Alexandre Belloni, Frank Li, Jarkko Nikula

So far only I3C private and I2C transfers have required a bounce buffer
for DMA transfers when buffer is not DMA'able.

It was observed that when the device DMA is IOMMU mapped and the receive
length is not a multiple of DWORDs, the last DWORD is padded with stale
data from the RX FIFO, corrupting 1-3 bytes beyond the expected data.

A similar issue, though less severe, occurs when an I3C target returns
less data than requested. In this case, the padding does not exceed the
requested number of bytes, assuming the device DMA is not IOMMU mapped.

Therefore, all I3C private transfer, CCC command payload and I2C
transfer receive buffers must be properly sized for the DMA being IOMMU
mapped. Even if those buffers are already DMA safe, their size may not
be, and I don't have a clear idea how to guarantee this other than
using a bounce buffer.

To prepare for the device DMA being IOMMU mapped and to address the
above issue, use helpers from I3C core for DMA mapping/unmapping and
bounce buffering for all DMA transfers.

For now, require bounce buffer only when the buffer is in the
vmalloc() area to avoid unnecessary copying with CCC commands and
DMA-safe I2C transfers.

Signed-off-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
---
 drivers/i3c/master/mipi-i3c-hci/core.c | 34 --------------------------
 drivers/i3c/master/mipi-i3c-hci/dma.c  | 27 +++++++++-----------
 drivers/i3c/master/mipi-i3c-hci/hci.h  |  3 +--
 3 files changed, 12 insertions(+), 52 deletions(-)

diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c
index 60f1175f1f37..b2977b6ac9f7 100644
--- a/drivers/i3c/master/mipi-i3c-hci/core.c
+++ b/drivers/i3c/master/mipi-i3c-hci/core.c
@@ -272,34 +272,6 @@ static int i3c_hci_daa(struct i3c_master_controller *m)
 	return hci->cmd->perform_daa(hci);
 }
 
-static int i3c_hci_alloc_safe_xfer_buf(struct i3c_hci *hci,
-				       struct hci_xfer *xfer)
-{
-	if (hci->io != &mipi_i3c_hci_dma ||
-	    xfer->data == NULL || !is_vmalloc_addr(xfer->data))
-		return 0;
-
-	if (xfer->rnw)
-		xfer->bounce_buf = kzalloc(xfer->data_len, GFP_KERNEL);
-	else
-		xfer->bounce_buf = kmemdup(xfer->data,
-					   xfer->data_len, GFP_KERNEL);
-
-	return xfer->bounce_buf == NULL ? -ENOMEM : 0;
-}
-
-static void i3c_hci_free_safe_xfer_buf(struct i3c_hci *hci,
-				       struct hci_xfer *xfer)
-{
-	if (hci->io != &mipi_i3c_hci_dma || xfer->bounce_buf == NULL)
-		return;
-
-	if (xfer->rnw)
-		memcpy(xfer->data, xfer->bounce_buf, xfer->data_len);
-
-	kfree(xfer->bounce_buf);
-}
-
 static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev,
 			      struct i3c_priv_xfer *i3c_xfers,
 			      int nxfers)
@@ -333,9 +305,6 @@ static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev,
 		}
 		hci->cmd->prep_i3c_xfer(hci, dev, &xfer[i]);
 		xfer[i].cmd_desc[0] |= CMD_0_ROC;
-		ret = i3c_hci_alloc_safe_xfer_buf(hci, &xfer[i]);
-		if (ret)
-			goto out;
 	}
 	last = i - 1;
 	xfer[last].cmd_desc[0] |= CMD_0_TOC;
@@ -359,9 +328,6 @@ static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev,
 	}
 
 out:
-	for (i = 0; i < nxfers; i++)
-		i3c_hci_free_safe_xfer_buf(hci, &xfer[i]);
-
 	hci_free_xfer(xfer, nxfers);
 	return ret;
 }
diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c
index 491dfe70b660..669d026646b4 100644
--- a/drivers/i3c/master/mipi-i3c-hci/dma.c
+++ b/drivers/i3c/master/mipi-i3c-hci/dma.c
@@ -349,9 +349,7 @@ static void hci_dma_unmap_xfer(struct i3c_hci *hci,
 		xfer = xfer_list + i;
 		if (!xfer->data)
 			continue;
-		dma_unmap_single(&hci->master.dev,
-				 xfer->data_dma, xfer->data_len,
-				 xfer->rnw ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
+		i3c_master_dma_unmap_single(&hci->master.dev, xfer->dma);
 	}
 }
 
@@ -362,7 +360,6 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
 	struct hci_rh_data *rh;
 	unsigned int i, ring, enqueue_ptr;
 	u32 op1_val, op2_val;
-	void *buf;
 
 	/* For now we only use ring 0 */
 	ring = 0;
@@ -373,6 +370,8 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
 	for (i = 0; i < n; i++) {
 		struct hci_xfer *xfer = xfer_list + i;
 		u32 *ring_data = rh->xfer + rh->xfer_struct_sz * enqueue_ptr;
+		enum dma_data_direction dir = xfer->rnw ? DMA_FROM_DEVICE :
+							  DMA_TO_DEVICE;
 
 		/* store cmd descriptor */
 		*ring_data++ = xfer->cmd_desc[0];
@@ -391,21 +390,17 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
 
 		/* 2nd and 3rd words of Data Buffer Descriptor Structure */
 		if (xfer->data) {
-			buf = xfer->bounce_buf ? xfer->bounce_buf : xfer->data;
-			xfer->data_dma =
-				dma_map_single(&hci->master.dev,
-					       buf,
-					       xfer->data_len,
-					       xfer->rnw ?
-						  DMA_FROM_DEVICE :
-						  DMA_TO_DEVICE);
-			if (dma_mapping_error(&hci->master.dev,
-					      xfer->data_dma)) {
+			xfer->dma = i3c_master_dma_map_single(&hci->master.dev,
+							      xfer->data,
+							      xfer->data_len,
+							      false,
+							      dir);
+			if (!xfer->dma) {
 				hci_dma_unmap_xfer(hci, xfer_list, i);
 				return -ENOMEM;
 			}
-			*ring_data++ = lower_32_bits(xfer->data_dma);
-			*ring_data++ = upper_32_bits(xfer->data_dma);
+			*ring_data++ = lower_32_bits(xfer->dma->addr);
+			*ring_data++ = upper_32_bits(xfer->dma->addr);
 		} else {
 			*ring_data++ = 0;
 			*ring_data++ = 0;
diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h
index 69ea1d10414b..33bc4906df1f 100644
--- a/drivers/i3c/master/mipi-i3c-hci/hci.h
+++ b/drivers/i3c/master/mipi-i3c-hci/hci.h
@@ -94,8 +94,7 @@ struct hci_xfer {
 		};
 		struct {
 			/* DMA specific */
-			dma_addr_t data_dma;
-			void *bounce_buf;
+			struct i3c_dma *dma;
 			int ring_number;
 			int ring_entry;
 		};
-- 
2.47.2


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 3/4] i3c: mipi-i3c-hci: Use physical device pointer with DMA API
  2025-07-31 14:14 [PATCH 1/4] i3c: master: Add helpers for DMA mapping and bounce buffer handling Jarkko Nikula
  2025-07-31 14:14 ` [PATCH 2/4] i3c: mipi-i3c-hci: Use core helpers for DMA mapping and bounce buffering Jarkko Nikula
@ 2025-07-31 14:14 ` Jarkko Nikula
  2025-08-01 16:03   ` Frank Li
  2025-07-31 14:14 ` [PATCH 4/4] i3c: mipi-i3c-hci: Use own DMA bounce buffer management for I2C transfers Jarkko Nikula
  2025-08-01 14:54 ` [PATCH 1/4] i3c: master: Add helpers for DMA mapping and bounce buffer handling Frank Li
  3 siblings, 1 reply; 11+ messages in thread
From: Jarkko Nikula @ 2025-07-31 14:14 UTC (permalink / raw)
  To: linux-i3c
  Cc: Alexandre Belloni, Frank Li, Jarkko Nikula, Prabhakaran, Krishna

DMA transfer faults on Intel hardware when the IOMMU is enabled and
driver initialization will fail when attempting to do the first transfer:

	DMAR: DRHD: handling fault status reg 2
	DMAR: [DMA Read NO_PASID] Request device [00:11.0] fault addr 0x676e3000 [fault reason 0x71] SM: Present bit in first-level paging entry is clear
 	i3c mipi-i3c-hci.0: ring 0: Transfer Aborted
	mipi-i3c-hci mipi-i3c-hci.0: probe with driver mipi-i3c-hci failed with error -62

Reason for this is that the IOMMU setup is done for the physical devices
only and not for the virtual I3C Controller device object.

Therefore use the pointer to a physical device object with the DMA API.

Due to a data corruption observation when the device DMA is IOMMU
mapped, a properly sized receive bounce buffer is required if transfer
length is not a multiple of DWORDs.

Reported-by: Prabhakaran, Krishna <krishna.prabhakaran@intel.com>
Signed-off-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
---
 drivers/i3c/master/mipi-i3c-hci/dma.c | 49 +++++++++++++++++++--------
 1 file changed, 35 insertions(+), 14 deletions(-)

diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c
index 669d026646b4..af1c9c97fb3d 100644
--- a/drivers/i3c/master/mipi-i3c-hci/dma.c
+++ b/drivers/i3c/master/mipi-i3c-hci/dma.c
@@ -14,6 +14,7 @@
 #include <linux/errno.h>
 #include <linux/i3c/master.h>
 #include <linux/io.h>
+#include <linux/pci.h>
 
 #include "hci.h"
 #include "cmd.h"
@@ -138,6 +139,7 @@ struct hci_rh_data {
 };
 
 struct hci_rings_data {
+	struct device *sysdev;
 	unsigned int total;
 	struct hci_rh_data headers[] __counted_by(total);
 };
@@ -165,20 +167,20 @@ static void hci_dma_cleanup(struct i3c_hci *hci)
 		rh_reg_write(IBI_SETUP, 0);
 
 		if (rh->xfer)
-			dma_free_coherent(&hci->master.dev,
+			dma_free_coherent(rings->sysdev,
 					  rh->xfer_struct_sz * rh->xfer_entries,
 					  rh->xfer, rh->xfer_dma);
 		if (rh->resp)
-			dma_free_coherent(&hci->master.dev,
+			dma_free_coherent(rings->sysdev,
 					  rh->resp_struct_sz * rh->xfer_entries,
 					  rh->resp, rh->resp_dma);
 		kfree(rh->src_xfers);
 		if (rh->ibi_status)
-			dma_free_coherent(&hci->master.dev,
+			dma_free_coherent(rings->sysdev,
 					  rh->ibi_status_sz * rh->ibi_status_entries,
 					  rh->ibi_status, rh->ibi_status_dma);
 		if (rh->ibi_data_dma)
-			dma_unmap_single(&hci->master.dev, rh->ibi_data_dma,
+			dma_unmap_single(rings->sysdev, rh->ibi_data_dma,
 					 rh->ibi_chunk_sz * rh->ibi_chunks_total,
 					 DMA_FROM_DEVICE);
 		kfree(rh->ibi_data);
@@ -194,11 +196,23 @@ static int hci_dma_init(struct i3c_hci *hci)
 {
 	struct hci_rings_data *rings;
 	struct hci_rh_data *rh;
+	struct device *sysdev;
 	u32 regval;
 	unsigned int i, nr_rings, xfers_sz, resps_sz;
 	unsigned int ibi_status_ring_sz, ibi_data_ring_sz;
 	int ret;
 
+	/*
+	 * Set pointer to a physical device that does DMA and has IOMMU setup
+	 * done for it in case of enabled IOMMU and use it with the DMA API.
+	 * Here such device is either
+	 * "mipi-i3c-hci" platform device (OF/ACPI enumeration) parent or
+	 * grandparent (PCI enumeration).
+	 */
+	sysdev = hci->master.dev.parent;
+	if (sysdev->parent && dev_is_pci(sysdev->parent))
+		sysdev = sysdev->parent;
+
 	regval = rhs_reg_read(CONTROL);
 	nr_rings = FIELD_GET(MAX_HEADER_COUNT_CAP, regval);
 	dev_info(&hci->master.dev, "%d DMA rings available\n", nr_rings);
@@ -213,6 +227,7 @@ static int hci_dma_init(struct i3c_hci *hci)
 		return -ENOMEM;
 	hci->io_data = rings;
 	rings->total = nr_rings;
+	rings->sysdev = sysdev;
 
 	regval = FIELD_PREP(MAX_HEADER_COUNT, rings->total);
 	rhs_reg_write(CONTROL, regval);
@@ -239,9 +254,9 @@ static int hci_dma_init(struct i3c_hci *hci)
 		xfers_sz = rh->xfer_struct_sz * rh->xfer_entries;
 		resps_sz = rh->resp_struct_sz * rh->xfer_entries;
 
-		rh->xfer = dma_alloc_coherent(&hci->master.dev, xfers_sz,
+		rh->xfer = dma_alloc_coherent(rings->sysdev, xfers_sz,
 					      &rh->xfer_dma, GFP_KERNEL);
-		rh->resp = dma_alloc_coherent(&hci->master.dev, resps_sz,
+		rh->resp = dma_alloc_coherent(rings->sysdev, resps_sz,
 					      &rh->resp_dma, GFP_KERNEL);
 		rh->src_xfers =
 			kmalloc_array(rh->xfer_entries, sizeof(*rh->src_xfers),
@@ -295,16 +310,16 @@ static int hci_dma_init(struct i3c_hci *hci)
 		ibi_data_ring_sz = rh->ibi_chunk_sz * rh->ibi_chunks_total;
 
 		rh->ibi_status =
-			dma_alloc_coherent(&hci->master.dev, ibi_status_ring_sz,
+			dma_alloc_coherent(rings->sysdev, ibi_status_ring_sz,
 					   &rh->ibi_status_dma, GFP_KERNEL);
 		rh->ibi_data = kmalloc(ibi_data_ring_sz, GFP_KERNEL);
 		ret = -ENOMEM;
 		if (!rh->ibi_status || !rh->ibi_data)
 			goto err_out;
 		rh->ibi_data_dma =
-			dma_map_single(&hci->master.dev, rh->ibi_data,
+			dma_map_single(rings->sysdev, rh->ibi_data,
 				       ibi_data_ring_sz, DMA_FROM_DEVICE);
-		if (dma_mapping_error(&hci->master.dev, rh->ibi_data_dma)) {
+		if (dma_mapping_error(rings->sysdev, rh->ibi_data_dma)) {
 			rh->ibi_data_dma = 0;
 			ret = -ENOMEM;
 			goto err_out;
@@ -342,6 +357,7 @@ static int hci_dma_init(struct i3c_hci *hci)
 static void hci_dma_unmap_xfer(struct i3c_hci *hci,
 			       struct hci_xfer *xfer_list, unsigned int n)
 {
+	struct hci_rings_data *rings = hci->io_data;
 	struct hci_xfer *xfer;
 	unsigned int i;
 
@@ -349,7 +365,7 @@ static void hci_dma_unmap_xfer(struct i3c_hci *hci,
 		xfer = xfer_list + i;
 		if (!xfer->data)
 			continue;
-		i3c_master_dma_unmap_single(&hci->master.dev, xfer->dma);
+		i3c_master_dma_unmap_single(rings->sysdev, xfer->dma);
 	}
 }
 
@@ -372,6 +388,7 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
 		u32 *ring_data = rh->xfer + rh->xfer_struct_sz * enqueue_ptr;
 		enum dma_data_direction dir = xfer->rnw ? DMA_FROM_DEVICE :
 							  DMA_TO_DEVICE;
+		bool need_bounce;
 
 		/* store cmd descriptor */
 		*ring_data++ = xfer->cmd_desc[0];
@@ -390,10 +407,13 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
 
 		/* 2nd and 3rd words of Data Buffer Descriptor Structure */
 		if (xfer->data) {
-			xfer->dma = i3c_master_dma_map_single(&hci->master.dev,
+			need_bounce = device_iommu_mapped(rings->sysdev) &&
+				      xfer->rnw &&
+				      xfer->data_len != ALIGN(xfer->data_len, 4);
+			xfer->dma = i3c_master_dma_map_single(rings->sysdev,
 							      xfer->data,
 							      xfer->data_len,
-							      false,
+							      need_bounce,
 							      dir);
 			if (!xfer->dma) {
 				hci_dma_unmap_xfer(hci, xfer_list, i);
@@ -581,6 +601,7 @@ static void hci_dma_recycle_ibi_slot(struct i3c_hci *hci,
 
 static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh)
 {
+	struct hci_rings_data *rings = hci->io_data;
 	struct i3c_dev_desc *dev;
 	struct i3c_hci_dev_data *dev_data;
 	struct hci_dma_dev_ibi_data *dev_ibi;
@@ -691,7 +712,7 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh)
 			* rh->ibi_chunk_sz;
 	if (first_part > ibi_size)
 		first_part = ibi_size;
-	dma_sync_single_for_cpu(&hci->master.dev, ring_ibi_data_dma,
+	dma_sync_single_for_cpu(rings->sysdev, ring_ibi_data_dma,
 				first_part, DMA_FROM_DEVICE);
 	memcpy(slot->data, ring_ibi_data, first_part);
 
@@ -700,7 +721,7 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh)
 		/* we wrap back to the start and copy remaining data */
 		ring_ibi_data = rh->ibi_data;
 		ring_ibi_data_dma = rh->ibi_data_dma;
-		dma_sync_single_for_cpu(&hci->master.dev, ring_ibi_data_dma,
+		dma_sync_single_for_cpu(rings->sysdev, ring_ibi_data_dma,
 					ibi_size - first_part, DMA_FROM_DEVICE);
 		memcpy(slot->data + first_part, ring_ibi_data,
 		       ibi_size - first_part);
-- 
2.47.2


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH 4/4] i3c: mipi-i3c-hci: Use own DMA bounce buffer management for I2C transfers
  2025-07-31 14:14 [PATCH 1/4] i3c: master: Add helpers for DMA mapping and bounce buffer handling Jarkko Nikula
  2025-07-31 14:14 ` [PATCH 2/4] i3c: mipi-i3c-hci: Use core helpers for DMA mapping and bounce buffering Jarkko Nikula
  2025-07-31 14:14 ` [PATCH 3/4] i3c: mipi-i3c-hci: Use physical device pointer with DMA API Jarkko Nikula
@ 2025-07-31 14:14 ` Jarkko Nikula
  2025-08-01 14:59   ` Frank Li
  2025-08-01 14:54 ` [PATCH 1/4] i3c: master: Add helpers for DMA mapping and bounce buffer handling Frank Li
  3 siblings, 1 reply; 11+ messages in thread
From: Jarkko Nikula @ 2025-07-31 14:14 UTC (permalink / raw)
  To: linux-i3c; +Cc: Alexandre Belloni, Frank Li, Jarkko Nikula, Billy Tsai

Stop using I2C DMA-safe API for two reasons:
- Not needed if driver is using PIO mode.
- DMA transfers needs a properly sized receive bounce buffer when the
  device DMA is IOMMU mapped causing needless double bounce buffering in
  that case.

Cc: Billy Tsai <billy_tsai@aspeedtech.com>
Signed-off-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
---
 drivers/i3c/master/mipi-i3c-hci/core.c | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c
index b2977b6ac9f7..7a467ef65787 100644
--- a/drivers/i3c/master/mipi-i3c-hci/core.c
+++ b/drivers/i3c/master/mipi-i3c-hci/core.c
@@ -348,7 +348,7 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev,
 		return -ENOMEM;
 
 	for (i = 0; i < nxfers; i++) {
-		xfer[i].data = i2c_get_dma_safe_msg_buf(&i2c_xfers[i], 1);
+		xfer[i].data = i2c_xfers[i].buf;
 		xfer[i].data_len = i2c_xfers[i].len;
 		xfer[i].rnw = i2c_xfers[i].flags & I2C_M_RD;
 		hci->cmd->prep_i2c_xfer(hci, dev, &xfer[i]);
@@ -374,10 +374,6 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev,
 	}
 
 out:
-	for (i = 0; i < nxfers; i++)
-		i2c_put_dma_safe_msg_buf(xfer[i].data, &i2c_xfers[i],
-					 ret ? false : true);
-
 	hci_free_xfer(xfer, nxfers);
 	return ret;
 }
-- 
2.47.2


-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH 2/4] i3c: mipi-i3c-hci: Use core helpers for DMA mapping and bounce buffering
  2025-07-31 14:14 ` [PATCH 2/4] i3c: mipi-i3c-hci: Use core helpers for DMA mapping and bounce buffering Jarkko Nikula
@ 2025-08-01 14:41   ` Frank Li
  0 siblings, 0 replies; 11+ messages in thread
From: Frank Li @ 2025-08-01 14:41 UTC (permalink / raw)
  To: Jarkko Nikula; +Cc: linux-i3c, Alexandre Belloni

On Thu, Jul 31, 2025 at 05:14:18PM +0300, Jarkko Nikula wrote:
> So far only I3C private and I2C transfers have required a bounce buffer
> for DMA transfers when buffer is not DMA'able.
>
> It was observed that when the device DMA is IOMMU mapped and the receive
> length is not a multiple of DWORDs, the last DWORD is padded with stale
> data from the RX FIFO, corrupting 1-3 bytes beyond the expected data.

Some I3C controller such as ..., pad with stale data from the RX FIFO if
the receive length is not align with DWORD (32bit). [when use IOMMU] (I
suppose it is same behavor without IOMMU, you find it just because IOMMU
capture this problem).

>
> A similar issue, though less severe, occurs when an I3C target returns
> less data than requested. In this case, the padding does not exceed the
> requested number of bytes, assuming the device DMA is not IOMMU mapped.
>
> Therefore, all I3C private transfer, CCC command payload and I2C
> transfer receive buffers must be properly sized for the DMA being IOMMU
> mapped. Even if those buffers are already DMA safe, their size may not
> be, and I don't have a clear idea how to guarantee this other than
> using a bounce buffer.

their size may not be WORD aligned. (Actually, it is require cache line
size aligned.)

remove "I don't ..."

>
> To prepare for the device DMA being IOMMU mapped and to address the
> above issue, use helpers from I3C core for DMA mapping/unmapping and
> bounce buffering for all DMA transfers.
>
> For now, require bounce buffer only when the buffer is in the
> vmalloc() area to avoid unnecessary copying with CCC commands and
> DMA-safe I2C transfers.


This patch major purpose should be use new common helper function
i3c_master_dma_map(unmap)_single() to simple mipi-i3c-hci code.

>
> Signed-off-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
> ---
>  drivers/i3c/master/mipi-i3c-hci/core.c | 34 --------------------------
>  drivers/i3c/master/mipi-i3c-hci/dma.c  | 27 +++++++++-----------
>  drivers/i3c/master/mipi-i3c-hci/hci.h  |  3 +--
>  3 files changed, 12 insertions(+), 52 deletions(-)
>
> diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c
> index 60f1175f1f37..b2977b6ac9f7 100644
> --- a/drivers/i3c/master/mipi-i3c-hci/core.c
> +++ b/drivers/i3c/master/mipi-i3c-hci/core.c
> @@ -272,34 +272,6 @@ static int i3c_hci_daa(struct i3c_master_controller *m)
>  	return hci->cmd->perform_daa(hci);
>  }
>
> -static int i3c_hci_alloc_safe_xfer_buf(struct i3c_hci *hci,
> -				       struct hci_xfer *xfer)
> -{
> -	if (hci->io != &mipi_i3c_hci_dma ||
> -	    xfer->data == NULL || !is_vmalloc_addr(xfer->data))
> -		return 0;
> -
> -	if (xfer->rnw)
> -		xfer->bounce_buf = kzalloc(xfer->data_len, GFP_KERNEL);
> -	else
> -		xfer->bounce_buf = kmemdup(xfer->data,
> -					   xfer->data_len, GFP_KERNEL);
> -
> -	return xfer->bounce_buf == NULL ? -ENOMEM : 0;
> -}
> -
> -static void i3c_hci_free_safe_xfer_buf(struct i3c_hci *hci,
> -				       struct hci_xfer *xfer)
> -{
> -	if (hci->io != &mipi_i3c_hci_dma || xfer->bounce_buf == NULL)
> -		return;
> -
> -	if (xfer->rnw)
> -		memcpy(xfer->data, xfer->bounce_buf, xfer->data_len);
> -
> -	kfree(xfer->bounce_buf);
> -}
> -
>  static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev,
>  			      struct i3c_priv_xfer *i3c_xfers,
>  			      int nxfers)
> @@ -333,9 +305,6 @@ static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev,
>  		}
>  		hci->cmd->prep_i3c_xfer(hci, dev, &xfer[i]);
>  		xfer[i].cmd_desc[0] |= CMD_0_ROC;
> -		ret = i3c_hci_alloc_safe_xfer_buf(hci, &xfer[i]);
> -		if (ret)
> -			goto out;
>  	}
>  	last = i - 1;
>  	xfer[last].cmd_desc[0] |= CMD_0_TOC;
> @@ -359,9 +328,6 @@ static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev,
>  	}
>
>  out:
> -	for (i = 0; i < nxfers; i++)
> -		i3c_hci_free_safe_xfer_buf(hci, &xfer[i]);
> -
>  	hci_free_xfer(xfer, nxfers);
>  	return ret;
>  }
> diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c
> index 491dfe70b660..669d026646b4 100644
> --- a/drivers/i3c/master/mipi-i3c-hci/dma.c
> +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c
> @@ -349,9 +349,7 @@ static void hci_dma_unmap_xfer(struct i3c_hci *hci,
>  		xfer = xfer_list + i;
>  		if (!xfer->data)
>  			continue;
> -		dma_unmap_single(&hci->master.dev,
> -				 xfer->data_dma, xfer->data_len,
> -				 xfer->rnw ? DMA_FROM_DEVICE : DMA_TO_DEVICE);
> +		i3c_master_dma_unmap_single(&hci->master.dev, xfer->dma);
>  	}
>  }
>
> @@ -362,7 +360,6 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
>  	struct hci_rh_data *rh;
>  	unsigned int i, ring, enqueue_ptr;
>  	u32 op1_val, op2_val;
> -	void *buf;
>
>  	/* For now we only use ring 0 */
>  	ring = 0;
> @@ -373,6 +370,8 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
>  	for (i = 0; i < n; i++) {
>  		struct hci_xfer *xfer = xfer_list + i;
>  		u32 *ring_data = rh->xfer + rh->xfer_struct_sz * enqueue_ptr;
> +		enum dma_data_direction dir = xfer->rnw ? DMA_FROM_DEVICE :
> +							  DMA_TO_DEVICE;
>
>  		/* store cmd descriptor */
>  		*ring_data++ = xfer->cmd_desc[0];
> @@ -391,21 +390,17 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
>
>  		/* 2nd and 3rd words of Data Buffer Descriptor Structure */
>  		if (xfer->data) {
> -			buf = xfer->bounce_buf ? xfer->bounce_buf : xfer->data;
> -			xfer->data_dma =
> -				dma_map_single(&hci->master.dev,
> -					       buf,
> -					       xfer->data_len,
> -					       xfer->rnw ?
> -						  DMA_FROM_DEVICE :
> -						  DMA_TO_DEVICE);
> -			if (dma_mapping_error(&hci->master.dev,
> -					      xfer->data_dma)) {
> +			xfer->dma = i3c_master_dma_map_single(&hci->master.dev,
> +							      xfer->data,
> +							      xfer->data_len,
> +							      false,
> +							      dir);
> +			if (!xfer->dma) {
>  				hci_dma_unmap_xfer(hci, xfer_list, i);
>  				return -ENOMEM;
>  			}
> -			*ring_data++ = lower_32_bits(xfer->data_dma);
> -			*ring_data++ = upper_32_bits(xfer->data_dma);
> +			*ring_data++ = lower_32_bits(xfer->dma->addr);
> +			*ring_data++ = upper_32_bits(xfer->dma->addr);
>  		} else {
>  			*ring_data++ = 0;
>  			*ring_data++ = 0;
> diff --git a/drivers/i3c/master/mipi-i3c-hci/hci.h b/drivers/i3c/master/mipi-i3c-hci/hci.h
> index 69ea1d10414b..33bc4906df1f 100644
> --- a/drivers/i3c/master/mipi-i3c-hci/hci.h
> +++ b/drivers/i3c/master/mipi-i3c-hci/hci.h
> @@ -94,8 +94,7 @@ struct hci_xfer {
>  		};
>  		struct {
>  			/* DMA specific */
> -			dma_addr_t data_dma;
> -			void *bounce_buf;
> +			struct i3c_dma *dma;
>  			int ring_number;
>  			int ring_entry;
>  		};
> --
> 2.47.2
>
>
> --
> linux-i3c mailing list
> linux-i3c@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-i3c

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/4] i3c: master: Add helpers for DMA mapping and bounce buffer handling
  2025-07-31 14:14 [PATCH 1/4] i3c: master: Add helpers for DMA mapping and bounce buffer handling Jarkko Nikula
                   ` (2 preceding siblings ...)
  2025-07-31 14:14 ` [PATCH 4/4] i3c: mipi-i3c-hci: Use own DMA bounce buffer management for I2C transfers Jarkko Nikula
@ 2025-08-01 14:54 ` Frank Li
  2025-08-04 13:35   ` Jarkko Nikula
  3 siblings, 1 reply; 11+ messages in thread
From: Frank Li @ 2025-08-01 14:54 UTC (permalink / raw)
  To: Jarkko Nikula; +Cc: linux-i3c, Alexandre Belloni

On Thu, Jul 31, 2025 at 05:14:17PM +0300, Jarkko Nikula wrote:
> Add helpers for I3C host controller drivers for DMA mapping/unmapping
> and bounce buffer handling. A bounce buffer is allocated if the buffer
> is not DMA'able or when the driver requires it for a transfer.

You'd better move some patch 2's description to this patch, to descriptor
why need bounce buffer.

>
> Signed-off-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
> ---
>  drivers/i3c/master.c       | 74 ++++++++++++++++++++++++++++++++++++++
>  include/linux/i3c/master.h | 20 +++++++++++
>  2 files changed, 94 insertions(+)
>
> diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
> index 2ef898a8fd80..a1daf18ea707 100644
> --- a/drivers/i3c/master.c
> +++ b/drivers/i3c/master.c
> @@ -8,6 +8,7 @@
>  #include <linux/atomic.h>
>  #include <linux/bug.h>
>  #include <linux/device.h>
> +#include <linux/dma-mapping.h>
>  #include <linux/err.h>
>  #include <linux/export.h>
>  #include <linux/kernel.h>
> @@ -1727,6 +1728,79 @@ int i3c_master_do_daa(struct i3c_master_controller *master)
>  }
>  EXPORT_SYMBOL_GPL(i3c_master_do_daa);
>
> +/**
> + * i3c_master_dma_map_single() - Map buffer for single DMA transfer
> + * @dev: device object of a device doing DMA
> + * @buf: destination/source buffer for DMA
> + * @len: length of transfer
> + * @need_bounce: true if buffer is not DMA safe and need a bounce buffer

Is it possible auto check if 'buf' dma-able?

> + * @dir: DMA direction
> + *
> + * Map buffer for a DMA transfer and allocate a bounce buffer if required.
> + *
> + * Return: I3C DMA transfer descriptor or NULL in case of error.
> + */
> +struct i3c_dma *i3c_master_dma_map_single(struct device *dev, void *buf,
> +	size_t len, bool need_bounce, enum dma_data_direction dir)
> +{
> +	struct i3c_dma *dma_xfer;
> +	void *dma_buf = buf;
> +
> +	dma_xfer = kzalloc(sizeof(*dma_xfer), GFP_KERNEL);

If use struct i3c_dma __free(kfree) *dma_xfer = NULL; it simple error handle

> +	if (!dma_xfer)
> +		return NULL;
> +
> +	dma_xfer->buf = buf;
> +	dma_xfer->dir = dir;
> +	dma_xfer->len = len;
> +	if (is_vmalloc_addr(buf))
> +		need_bounce = true;
> +
> +	if (need_bounce) {
> +		if (dir == DMA_FROM_DEVICE)
> +			dma_buf = kzalloc(ALIGN(len, cache_line_size()),
> +						GFP_KERNEL);
> +		else
> +			dma_buf = kmemdup(buf, len, GFP_KERNEL);
> +		if (!dma_buf)
> +			goto err_alloc;
> +
> +		dma_xfer->bounce_buf = dma_buf;
> +	}
> +
> +	dma_xfer->addr = dma_map_single(dev, dma_buf, len, dir);

len should be ALIGN(len, cache_line_size(), otherwise swiotlb will bounce
again if need_bounce is true

> +	if (dma_mapping_error(dev, dma_xfer->addr))
> +		goto err_map;
> +
> +	return dma_xfer;

return no_free_ptr(dma_xfer) if use __free.

> +err_map:
> +	kfree(dma_xfer->bounce_buf);
> +err_alloc:
> +	kfree(dma_xfer);
> +	return NULL;
> +}
> +EXPORT_SYMBOL_GPL(i3c_master_dma_map_single);
> +
> +/**
> + * i3c_master_dma_unmap_single() - Unmap buffer after DMA
> + * @dev: device object of a device doing DMA
> + * @dma_xfer: DMA transfer and mapping descriptor
> + *
> + * Unmap buffer and cleanup DMA transfer descriptor.
> + */
> +void i3c_master_dma_unmap_single(struct device *dev, struct i3c_dma *dma_xfer)
> +{
> +	dma_unmap_single(dev, dma_xfer->addr, dma_xfer->len, dma_xfer->dir);
> +	if (dma_xfer->bounce_buf) {
> +		if (dma_xfer->dir == DMA_FROM_DEVICE)
> +			memcpy(dma_xfer->buf, dma_xfer->bounce_buf,
> +			       dma_xfer->len);
> +		kfree(dma_xfer->bounce_buf);
> +	}
> +	kfree(dma_xfer);
> +}
> +EXPORT_SYMBOL_GPL(i3c_master_dma_unmap_single);
> +
>  /**
>   * i3c_master_set_info() - set master device information
>   * @master: master used to send frames on the bus
> diff --git a/include/linux/i3c/master.h b/include/linux/i3c/master.h
> index 043f5c7ff398..e39350b1038e 100644
> --- a/include/linux/i3c/master.h
> +++ b/include/linux/i3c/master.h
> @@ -558,6 +558,22 @@ struct i3c_master_controller {
>  #define i3c_bus_for_each_i3cdev(bus, dev)				\
>  	list_for_each_entry(dev, &(bus)->devs.i3c, common.node)
>
> +/**
> + * struct i3c_dma - DMA transfer and mapping descriptor
> + * @buf: destination/source buffer for DMA
> + * @len: length of transfer
> + * @addr: mapped DMA address for a Host Controller Driver
> + * @dir: DMA direction
> + * @bounce_buf: an allocated bounce buffer if transfer needs it or NULL
> + */
> +struct i3c_dma {
> +	void *buf;
> +	size_t len;

need dma_map_len to avoid swiotlb bounce again.

> +	dma_addr_t addr;
> +	enum dma_data_direction dir;
> +	void *bounce_buf;
> +};
> +
>  int i3c_master_do_i2c_xfers(struct i3c_master_controller *master,
>  			    const struct i2c_msg *xfers,
>  			    int nxfers);
> @@ -575,6 +591,10 @@ int i3c_master_get_free_addr(struct i3c_master_controller *master,
>  int i3c_master_add_i3c_dev_locked(struct i3c_master_controller *master,
>  				  u8 addr);
>  int i3c_master_do_daa(struct i3c_master_controller *master);
> +struct i3c_dma *i3c_master_dma_map_single(struct device *dev, void *ptr,
> +					  size_t len, bool dma_safe,
> +					  enum dma_data_direction dir);
> +void i3c_master_dma_unmap_single(struct device *dev, struct i3c_dma *dma_xfer);

needn't dev, save it into struct i3c_dma, so we can use cleanup easily


#define DEFINE_FREE(i3c_master_dma_unmap_single, struct i3c_dma *, if (_T) i3c_master_dma_map_single(_T))

Frank
>
>  int i3c_master_set_info(struct i3c_master_controller *master,
>  			const struct i3c_device_info *info);
> --
> 2.47.2
>
>
> --
> linux-i3c mailing list
> linux-i3c@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-i3c

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 4/4] i3c: mipi-i3c-hci: Use own DMA bounce buffer management for I2C transfers
  2025-07-31 14:14 ` [PATCH 4/4] i3c: mipi-i3c-hci: Use own DMA bounce buffer management for I2C transfers Jarkko Nikula
@ 2025-08-01 14:59   ` Frank Li
  0 siblings, 0 replies; 11+ messages in thread
From: Frank Li @ 2025-08-01 14:59 UTC (permalink / raw)
  To: Jarkko Nikula; +Cc: linux-i3c, Alexandre Belloni, Billy Tsai

On Thu, Jul 31, 2025 at 05:14:20PM +0300, Jarkko Nikula wrote:
> Stop using I2C DMA-safe API for two reasons:
> - Not needed if driver is using PIO mode.
> - DMA transfers needs a properly sized receive bounce buffer when the

s/a properly sized/word align sized/

>   device DMA is IOMMU mapped causing needless double bounce buffering in

when use IOMMU, which causing ...

Frank
>   that case.
>
> Cc: Billy Tsai <billy_tsai@aspeedtech.com>
> Signed-off-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
> ---
>  drivers/i3c/master/mipi-i3c-hci/core.c | 6 +-----
>  1 file changed, 1 insertion(+), 5 deletions(-)
>
> diff --git a/drivers/i3c/master/mipi-i3c-hci/core.c b/drivers/i3c/master/mipi-i3c-hci/core.c
> index b2977b6ac9f7..7a467ef65787 100644
> --- a/drivers/i3c/master/mipi-i3c-hci/core.c
> +++ b/drivers/i3c/master/mipi-i3c-hci/core.c
> @@ -348,7 +348,7 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev,
>  		return -ENOMEM;
>
>  	for (i = 0; i < nxfers; i++) {
> -		xfer[i].data = i2c_get_dma_safe_msg_buf(&i2c_xfers[i], 1);
> +		xfer[i].data = i2c_xfers[i].buf;
>  		xfer[i].data_len = i2c_xfers[i].len;
>  		xfer[i].rnw = i2c_xfers[i].flags & I2C_M_RD;
>  		hci->cmd->prep_i2c_xfer(hci, dev, &xfer[i]);
> @@ -374,10 +374,6 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev,
>  	}
>
>  out:
> -	for (i = 0; i < nxfers; i++)
> -		i2c_put_dma_safe_msg_buf(xfer[i].data, &i2c_xfers[i],
> -					 ret ? false : true);
> -
>  	hci_free_xfer(xfer, nxfers);
>  	return ret;
>  }
> --
> 2.47.2
>
>
> --
> linux-i3c mailing list
> linux-i3c@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-i3c

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 3/4] i3c: mipi-i3c-hci: Use physical device pointer with DMA API
  2025-07-31 14:14 ` [PATCH 3/4] i3c: mipi-i3c-hci: Use physical device pointer with DMA API Jarkko Nikula
@ 2025-08-01 16:03   ` Frank Li
  2025-08-04 14:34     ` Jarkko Nikula
  0 siblings, 1 reply; 11+ messages in thread
From: Frank Li @ 2025-08-01 16:03 UTC (permalink / raw)
  To: Jarkko Nikula; +Cc: linux-i3c, Alexandre Belloni, Prabhakaran, Krishna

On Thu, Jul 31, 2025 at 05:14:19PM +0300, Jarkko Nikula wrote:
> DMA transfer faults on Intel hardware when the IOMMU is enabled and
> driver initialization will fail when attempting to do the first transfer:
>
> 	DMAR: DRHD: handling fault status reg 2
> 	DMAR: [DMA Read NO_PASID] Request device [00:11.0] fault addr 0x676e3000 [fault reason 0x71] SM: Present bit in first-level paging entry is clear
>  	i3c mipi-i3c-hci.0: ring 0: Transfer Aborted
> 	mipi-i3c-hci mipi-i3c-hci.0: probe with driver mipi-i3c-hci failed with error -62
>
> Reason for this is that the IOMMU setup is done for the physical devices
> only and not for the virtual I3C Controller device object.
>
> Therefore use the pointer to a physical device object with the DMA API.
>
> Due to a data corruption observation when the device DMA is IOMMU
> mapped, a properly sized receive bounce buffer is required if transfer
> length is not a multiple of DWORDs.
>
> Reported-by: Prabhakaran, Krishna <krishna.prabhakaran@intel.com>
> Signed-off-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
> ---
>  drivers/i3c/master/mipi-i3c-hci/dma.c | 49 +++++++++++++++++++--------
>  1 file changed, 35 insertions(+), 14 deletions(-)
>
> diff --git a/drivers/i3c/master/mipi-i3c-hci/dma.c b/drivers/i3c/master/mipi-i3c-hci/dma.c
> index 669d026646b4..af1c9c97fb3d 100644
> --- a/drivers/i3c/master/mipi-i3c-hci/dma.c
> +++ b/drivers/i3c/master/mipi-i3c-hci/dma.c
> @@ -14,6 +14,7 @@
>  #include <linux/errno.h>
>  #include <linux/i3c/master.h>
>  #include <linux/io.h>
> +#include <linux/pci.h>
>
>  #include "hci.h"
>  #include "cmd.h"
> @@ -138,6 +139,7 @@ struct hci_rh_data {
>  };
>
>  struct hci_rings_data {
> +	struct device *sysdev;
>  	unsigned int total;
>  	struct hci_rh_data headers[] __counted_by(total);
>  };
> @@ -165,20 +167,20 @@ static void hci_dma_cleanup(struct i3c_hci *hci)
>  		rh_reg_write(IBI_SETUP, 0);
>
>  		if (rh->xfer)
> -			dma_free_coherent(&hci->master.dev,
> +			dma_free_coherent(rings->sysdev,
>  					  rh->xfer_struct_sz * rh->xfer_entries,
>  					  rh->xfer, rh->xfer_dma);
>  		if (rh->resp)
> -			dma_free_coherent(&hci->master.dev,
> +			dma_free_coherent(rings->sysdev,
>  					  rh->resp_struct_sz * rh->xfer_entries,
>  					  rh->resp, rh->resp_dma);
>  		kfree(rh->src_xfers);
>  		if (rh->ibi_status)
> -			dma_free_coherent(&hci->master.dev,
> +			dma_free_coherent(rings->sysdev,
>  					  rh->ibi_status_sz * rh->ibi_status_entries,
>  					  rh->ibi_status, rh->ibi_status_dma);
>  		if (rh->ibi_data_dma)
> -			dma_unmap_single(&hci->master.dev, rh->ibi_data_dma,
> +			dma_unmap_single(rings->sysdev, rh->ibi_data_dma,
>  					 rh->ibi_chunk_sz * rh->ibi_chunks_total,
>  					 DMA_FROM_DEVICE);
>  		kfree(rh->ibi_data);
> @@ -194,11 +196,23 @@ static int hci_dma_init(struct i3c_hci *hci)
>  {
>  	struct hci_rings_data *rings;
>  	struct hci_rh_data *rh;
> +	struct device *sysdev;
>  	u32 regval;
>  	unsigned int i, nr_rings, xfers_sz, resps_sz;
>  	unsigned int ibi_status_ring_sz, ibi_data_ring_sz;
>  	int ret;
>
> +	/*
> +	 * Set pointer to a physical device that does DMA and has IOMMU setup
> +	 * done for it in case of enabled IOMMU and use it with the DMA API.
> +	 * Here such device is either
> +	 * "mipi-i3c-hci" platform device (OF/ACPI enumeration) parent or
> +	 * grandparent (PCI enumeration).
> +	 */
> +	sysdev = hci->master.dev.parent;
> +	if (sysdev->parent && dev_is_pci(sysdev->parent))
> +		sysdev = sysdev->parent;
> +

I am not clear about how i3c master controller device create in your
platform. If use DT, i3c master controller should direct refer to a iommu

i3c-master@123456 {
	...
	iommu = <&smmu id>;
}

Is it children device of a PCIe devices?

Frank

>  	regval = rhs_reg_read(CONTROL);
>  	nr_rings = FIELD_GET(MAX_HEADER_COUNT_CAP, regval);
>  	dev_info(&hci->master.dev, "%d DMA rings available\n", nr_rings);
> @@ -213,6 +227,7 @@ static int hci_dma_init(struct i3c_hci *hci)
>  		return -ENOMEM;
>  	hci->io_data = rings;
>  	rings->total = nr_rings;
> +	rings->sysdev = sysdev;
>
>  	regval = FIELD_PREP(MAX_HEADER_COUNT, rings->total);
>  	rhs_reg_write(CONTROL, regval);
> @@ -239,9 +254,9 @@ static int hci_dma_init(struct i3c_hci *hci)
>  		xfers_sz = rh->xfer_struct_sz * rh->xfer_entries;
>  		resps_sz = rh->resp_struct_sz * rh->xfer_entries;
>
> -		rh->xfer = dma_alloc_coherent(&hci->master.dev, xfers_sz,
> +		rh->xfer = dma_alloc_coherent(rings->sysdev, xfers_sz,
>  					      &rh->xfer_dma, GFP_KERNEL);
> -		rh->resp = dma_alloc_coherent(&hci->master.dev, resps_sz,
> +		rh->resp = dma_alloc_coherent(rings->sysdev, resps_sz,
>  					      &rh->resp_dma, GFP_KERNEL);
>  		rh->src_xfers =
>  			kmalloc_array(rh->xfer_entries, sizeof(*rh->src_xfers),
> @@ -295,16 +310,16 @@ static int hci_dma_init(struct i3c_hci *hci)
>  		ibi_data_ring_sz = rh->ibi_chunk_sz * rh->ibi_chunks_total;
>
>  		rh->ibi_status =
> -			dma_alloc_coherent(&hci->master.dev, ibi_status_ring_sz,
> +			dma_alloc_coherent(rings->sysdev, ibi_status_ring_sz,
>  					   &rh->ibi_status_dma, GFP_KERNEL);
>  		rh->ibi_data = kmalloc(ibi_data_ring_sz, GFP_KERNEL);
>  		ret = -ENOMEM;
>  		if (!rh->ibi_status || !rh->ibi_data)
>  			goto err_out;
>  		rh->ibi_data_dma =
> -			dma_map_single(&hci->master.dev, rh->ibi_data,
> +			dma_map_single(rings->sysdev, rh->ibi_data,
>  				       ibi_data_ring_sz, DMA_FROM_DEVICE);
> -		if (dma_mapping_error(&hci->master.dev, rh->ibi_data_dma)) {
> +		if (dma_mapping_error(rings->sysdev, rh->ibi_data_dma)) {
>  			rh->ibi_data_dma = 0;
>  			ret = -ENOMEM;
>  			goto err_out;
> @@ -342,6 +357,7 @@ static int hci_dma_init(struct i3c_hci *hci)
>  static void hci_dma_unmap_xfer(struct i3c_hci *hci,
>  			       struct hci_xfer *xfer_list, unsigned int n)
>  {
> +	struct hci_rings_data *rings = hci->io_data;
>  	struct hci_xfer *xfer;
>  	unsigned int i;
>
> @@ -349,7 +365,7 @@ static void hci_dma_unmap_xfer(struct i3c_hci *hci,
>  		xfer = xfer_list + i;
>  		if (!xfer->data)
>  			continue;
> -		i3c_master_dma_unmap_single(&hci->master.dev, xfer->dma);
> +		i3c_master_dma_unmap_single(rings->sysdev, xfer->dma);
>  	}
>  }
>
> @@ -372,6 +388,7 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
>  		u32 *ring_data = rh->xfer + rh->xfer_struct_sz * enqueue_ptr;
>  		enum dma_data_direction dir = xfer->rnw ? DMA_FROM_DEVICE :
>  							  DMA_TO_DEVICE;
> +		bool need_bounce;
>
>  		/* store cmd descriptor */
>  		*ring_data++ = xfer->cmd_desc[0];
> @@ -390,10 +407,13 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
>
>  		/* 2nd and 3rd words of Data Buffer Descriptor Structure */
>  		if (xfer->data) {
> -			xfer->dma = i3c_master_dma_map_single(&hci->master.dev,
> +			need_bounce = device_iommu_mapped(rings->sysdev) &&
> +				      xfer->rnw &&
> +				      xfer->data_len != ALIGN(xfer->data_len, 4);
> +			xfer->dma = i3c_master_dma_map_single(rings->sysdev,
>  							      xfer->data,
>  							      xfer->data_len,
> -							      false,
> +							      need_bounce,
>  							      dir);
>  			if (!xfer->dma) {
>  				hci_dma_unmap_xfer(hci, xfer_list, i);
> @@ -581,6 +601,7 @@ static void hci_dma_recycle_ibi_slot(struct i3c_hci *hci,
>
>  static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh)
>  {
> +	struct hci_rings_data *rings = hci->io_data;
>  	struct i3c_dev_desc *dev;
>  	struct i3c_hci_dev_data *dev_data;
>  	struct hci_dma_dev_ibi_data *dev_ibi;
> @@ -691,7 +712,7 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh)
>  			* rh->ibi_chunk_sz;
>  	if (first_part > ibi_size)
>  		first_part = ibi_size;
> -	dma_sync_single_for_cpu(&hci->master.dev, ring_ibi_data_dma,
> +	dma_sync_single_for_cpu(rings->sysdev, ring_ibi_data_dma,
>  				first_part, DMA_FROM_DEVICE);
>  	memcpy(slot->data, ring_ibi_data, first_part);
>
> @@ -700,7 +721,7 @@ static void hci_dma_process_ibi(struct i3c_hci *hci, struct hci_rh_data *rh)
>  		/* we wrap back to the start and copy remaining data */
>  		ring_ibi_data = rh->ibi_data;
>  		ring_ibi_data_dma = rh->ibi_data_dma;
> -		dma_sync_single_for_cpu(&hci->master.dev, ring_ibi_data_dma,
> +		dma_sync_single_for_cpu(rings->sysdev, ring_ibi_data_dma,
>  					ibi_size - first_part, DMA_FROM_DEVICE);
>  		memcpy(slot->data + first_part, ring_ibi_data,
>  		       ibi_size - first_part);
> --
> 2.47.2
>
>
> --
> linux-i3c mailing list
> linux-i3c@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-i3c

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 1/4] i3c: master: Add helpers for DMA mapping and bounce buffer handling
  2025-08-01 14:54 ` [PATCH 1/4] i3c: master: Add helpers for DMA mapping and bounce buffer handling Frank Li
@ 2025-08-04 13:35   ` Jarkko Nikula
  0 siblings, 0 replies; 11+ messages in thread
From: Jarkko Nikula @ 2025-08-04 13:35 UTC (permalink / raw)
  To: Frank Li; +Cc: linux-i3c, Alexandre Belloni

On 8/1/25 5:54 PM, Frank Li wrote:
> On Thu, Jul 31, 2025 at 05:14:17PM +0300, Jarkko Nikula wrote:
>> Add helpers for I3C host controller drivers for DMA mapping/unmapping
>> and bounce buffer handling. A bounce buffer is allocated if the buffer
>> is not DMA'able or when the driver requires it for a transfer.
> 
> You'd better move some patch 2's description to this patch, to descriptor
> why need bounce buffer.
> 
>>
>> Signed-off-by: Jarkko Nikula <jarkko.nikula@linux.intel.com>
>> ---
>>   drivers/i3c/master.c       | 74 ++++++++++++++++++++++++++++++++++++++
>>   include/linux/i3c/master.h | 20 +++++++++++
>>   2 files changed, 94 insertions(+)
>>
>> diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
>> index 2ef898a8fd80..a1daf18ea707 100644
>> --- a/drivers/i3c/master.c
>> +++ b/drivers/i3c/master.c
>> @@ -8,6 +8,7 @@
>>   #include <linux/atomic.h>
>>   #include <linux/bug.h>
>>   #include <linux/device.h>
>> +#include <linux/dma-mapping.h>
>>   #include <linux/err.h>
>>   #include <linux/export.h>
>>   #include <linux/kernel.h>
>> @@ -1727,6 +1728,79 @@ int i3c_master_do_daa(struct i3c_master_controller *master)
>>   }
>>   EXPORT_SYMBOL_GPL(i3c_master_do_daa);
>>
>> +/**
>> + * i3c_master_dma_map_single() - Map buffer for single DMA transfer
>> + * @dev: device object of a device doing DMA
>> + * @buf: destination/source buffer for DMA
>> + * @len: length of transfer
>> + * @need_bounce: true if buffer is not DMA safe and need a bounce buffer
> 
> Is it possible auto check if 'buf' dma-able?
> 
In some sense yes and I think since I3C being new, and probably 
integrated only to a modern HW, it will simplify things a bit and we 
don't need to deal with e.g. highmem etc. legacy stuff what SPI core 
needs to check for instance.

Idea is to do common checks (currently only the is_vmalloc_addr()) here 
but let drivers to tell if they have their quirk reason for a bounce buffer.

> If use struct i3c_dma __free(kfree) *dma_xfer = NULL; it simple error handle
> 
Will do.

>> +	dma_xfer->addr = dma_map_single(dev, dma_buf, len, dir);
> 
> len should be ALIGN(len, cache_line_size(), otherwise swiotlb will bounce
> again if need_bounce is true
> 
Ah, good to know.

>> +struct i3c_dma *i3c_master_dma_map_single(struct device *dev, void *ptr,
>> +					  size_t len, bool dma_safe,
>> +					  enum dma_data_direction dir);
>> +void i3c_master_dma_unmap_single(struct device *dev, struct i3c_dma *dma_xfer);
> 
> needn't dev, save it into struct i3c_dma, so we can use cleanup easily
> 
Ok.

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 3/4] i3c: mipi-i3c-hci: Use physical device pointer with DMA API
  2025-08-01 16:03   ` Frank Li
@ 2025-08-04 14:34     ` Jarkko Nikula
  2025-08-05 15:57       ` Frank Li
  0 siblings, 1 reply; 11+ messages in thread
From: Jarkko Nikula @ 2025-08-04 14:34 UTC (permalink / raw)
  To: Frank Li; +Cc: linux-i3c, Alexandre Belloni, Prabhakaran, Krishna

On 8/1/25 7:03 PM, Frank Li wrote:
> On Thu, Jul 31, 2025 at 05:14:19PM +0300, Jarkko Nikula wrote:
>> @@ -194,11 +196,23 @@ static int hci_dma_init(struct i3c_hci *hci)
>>   {
>>   	struct hci_rings_data *rings;
>>   	struct hci_rh_data *rh;
>> +	struct device *sysdev;
>>   	u32 regval;
>>   	unsigned int i, nr_rings, xfers_sz, resps_sz;
>>   	unsigned int ibi_status_ring_sz, ibi_data_ring_sz;
>>   	int ret;
>>
>> +	/*
>> +	 * Set pointer to a physical device that does DMA and has IOMMU setup
>> +	 * done for it in case of enabled IOMMU and use it with the DMA API.
>> +	 * Here such device is either
>> +	 * "mipi-i3c-hci" platform device (OF/ACPI enumeration) parent or
>> +	 * grandparent (PCI enumeration).
>> +	 */
>> +	sysdev = hci->master.dev.parent;
>> +	if (sysdev->parent && dev_is_pci(sysdev->parent))
>> +		sysdev = sysdev->parent;
>> +
> 
> I am not clear about how i3c master controller device create in your
> platform. If use DT, i3c master controller should direct refer to a iommu
> 
> i3c-master@123456 {
> 	...
> 	iommu = <&smmu id>;
> }
> 
> Is it children device of a PCIe devices?
> 
The master.dev is a "virtual" device object created by 
i3c_master_register() and for that there is no IOMMU link obviously. 
Only this IOMMU exercise revealed we were wrongly using the master.dev 
with DMA API.

For the OF/ACPI enumeration the physical device is in pdev->dev in 
drivers/i3c/master/mipi-i3c-hci/core.c: i3c_hci_probe() and that is 
passed to the i3c_master_register() and can be references via 
hci->master.dev.parent here.

PCI code needs it's own module mipi-i3c-hci-pci.c due to each module can 
have only one module_driver(), i.e. module_platform_driver() and 
module_pci_driver() cannot reside in the same module.

mipi_i3c_hci_pci_probe() creates a "mipi-i3c-hci" platform device and 
sets physical device pointer pci->dev as its parent. So in PCI case the 
i3c_hci_probe() don't have the physical device pointer in its pdev->dev 
like OF/ACPI cases have but as a parent of it.

Not very clear but that's how drivers with multiple enumeration need to 
cope with.

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH 3/4] i3c: mipi-i3c-hci: Use physical device pointer with DMA API
  2025-08-04 14:34     ` Jarkko Nikula
@ 2025-08-05 15:57       ` Frank Li
  0 siblings, 0 replies; 11+ messages in thread
From: Frank Li @ 2025-08-05 15:57 UTC (permalink / raw)
  To: Jarkko Nikula; +Cc: linux-i3c, Alexandre Belloni, Prabhakaran, Krishna

On Mon, Aug 04, 2025 at 05:34:39PM +0300, Jarkko Nikula wrote:
> On 8/1/25 7:03 PM, Frank Li wrote:
> > On Thu, Jul 31, 2025 at 05:14:19PM +0300, Jarkko Nikula wrote:
> > > @@ -194,11 +196,23 @@ static int hci_dma_init(struct i3c_hci *hci)
> > >   {
> > >   	struct hci_rings_data *rings;
> > >   	struct hci_rh_data *rh;
> > > +	struct device *sysdev;
> > >   	u32 regval;
> > >   	unsigned int i, nr_rings, xfers_sz, resps_sz;
> > >   	unsigned int ibi_status_ring_sz, ibi_data_ring_sz;
> > >   	int ret;
> > >
> > > +	/*
> > > +	 * Set pointer to a physical device that does DMA and has IOMMU setup
> > > +	 * done for it in case of enabled IOMMU and use it with the DMA API.
> > > +	 * Here such device is either
> > > +	 * "mipi-i3c-hci" platform device (OF/ACPI enumeration) parent or
> > > +	 * grandparent (PCI enumeration).
> > > +	 */
> > > +	sysdev = hci->master.dev.parent;
> > > +	if (sysdev->parent && dev_is_pci(sysdev->parent))
> > > +		sysdev = sysdev->parent;
> > > +
> >
> > I am not clear about how i3c master controller device create in your
> > platform. If use DT, i3c master controller should direct refer to a iommu
> >
> > i3c-master@123456 {
> > 	...
> > 	iommu = <&smmu id>;
> > }
> >
> > Is it children device of a PCIe devices?
> >
> The master.dev is a "virtual" device object created by i3c_master_register()
> and for that there is no IOMMU link obviously. Only this IOMMU exercise
> revealed we were wrongly using the master.dev with DMA API.
>
> For the OF/ACPI enumeration the physical device is in pdev->dev in
> drivers/i3c/master/mipi-i3c-hci/core.c: i3c_hci_probe() and that is passed
> to the i3c_master_register() and can be references via
> hci->master.dev.parent here.
>
> PCI code needs it's own module mipi-i3c-hci-pci.c due to each module can
> have only one module_driver(), i.e. module_platform_driver() and
> module_pci_driver() cannot reside in the same module.
>
> mipi_i3c_hci_pci_probe() creates a "mipi-i3c-hci" platform device and sets
> physical device pointer pci->dev as its parent. So in PCI case the
> i3c_hci_probe() don't have the physical device pointer in its pdev->dev like
> OF/ACPI cases have but as a parent of it.
>
> Not very clear but that's how drivers with multiple enumeration need to cope
> with.

Okay for here, idealy framework should auto search parent for suitable iommu.

Frank

>
> --
> linux-i3c mailing list
> linux-i3c@lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-i3c

-- 
linux-i3c mailing list
linux-i3c@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-i3c

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2025-08-05 17:10 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-07-31 14:14 [PATCH 1/4] i3c: master: Add helpers for DMA mapping and bounce buffer handling Jarkko Nikula
2025-07-31 14:14 ` [PATCH 2/4] i3c: mipi-i3c-hci: Use core helpers for DMA mapping and bounce buffering Jarkko Nikula
2025-08-01 14:41   ` Frank Li
2025-07-31 14:14 ` [PATCH 3/4] i3c: mipi-i3c-hci: Use physical device pointer with DMA API Jarkko Nikula
2025-08-01 16:03   ` Frank Li
2025-08-04 14:34     ` Jarkko Nikula
2025-08-05 15:57       ` Frank Li
2025-07-31 14:14 ` [PATCH 4/4] i3c: mipi-i3c-hci: Use own DMA bounce buffer management for I2C transfers Jarkko Nikula
2025-08-01 14:59   ` Frank Li
2025-08-01 14:54 ` [PATCH 1/4] i3c: master: Add helpers for DMA mapping and bounce buffer handling Frank Li
2025-08-04 13:35   ` Jarkko Nikula

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).