[PATCH v2 1/4] i3c: master: Add helpers for DMA mapping and bounce buffer handling

Frank Li Frank.li at nxp.com
Tue Aug 19 07:26:17 PDT 2025


On Tue, Aug 19, 2025 at 09:08:36AM +0300, Jarkko Nikula wrote:
> On 8/18/25 7:07 PM, Frank Li wrote:
> > On Fri, Aug 15, 2025 at 05:12:39PM +0300, Jarkko Nikula wrote:
> > > Some I3C controllers such as MIPI I3C HCI may pad the last DWORD (32-bit)
> > > with stale data from the RX FIFO in DMA transfers if the receive length
> > > is not DWORD aligned and when the device DMA is IOMMU mapped.
> > >
> > > In such a case, a properly sized bounce buffer is required in order to
> > > avoid possible data corruption. In a review discussion, proposal was to
> > > have a common helpers in I3C core for DMA mapping and bounce buffer
> > > handling.
> > >
> > > Drivers may use the helper i3c_master_dma_map_single() to map a buffer
> > > for a DMA transfer. It internally allocates a bounce buffer if buffer is
> > > not DMA'able or when the driver requires it for a transfer.
> > >
> > > Helper i3c_master_dma_unmap_single() does the needed cleanups and
> > > data copying from the bounce buffer.
> > >
> > > Signed-off-by: Jarkko Nikula <jarkko.nikula at linux.intel.com>
> > > ---
> > >   drivers/i3c/master.c       | 69 ++++++++++++++++++++++++++++++++++++++
> > >   include/linux/i3c/master.h | 26 ++++++++++++++
> > >   2 files changed, 95 insertions(+)
> > >
> > > diff --git a/drivers/i3c/master.c b/drivers/i3c/master.c
> > > index 2ef898a8fd80..497bff57248a 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,74 @@ 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 __free(kfree) = NULL;
> > > +	void *bounce __free(kfree) = NULL;
> > > +	void *dma_buf = buf;
> > > +
> > > +	dma_xfer = kzalloc(sizeof(*dma_xfer), GFP_KERNEL);
> > > +	if (!dma_xfer)
> > > +		return NULL;
> >
> > nit: add empty line here
> >
> > > +	dma_xfer->dev = dev;
> > > +	dma_xfer->buf = buf;
> > > +	dma_xfer->dir = dir;
> > > +	dma_xfer->len = len;
> > > +	dma_xfer->map_len = len;
> > > +
> > > +	if (is_vmalloc_addr(buf))
> > > +		need_bounce = true;
> > > +
> > > +	if (need_bounce) {
> > > +		dma_xfer->map_len = ALIGN(len, cache_line_size());
> > > +		if (dir == DMA_FROM_DEVICE)
> > > +			bounce = kzalloc(dma_xfer->map_len, GFP_KERNEL);
> > > +		else
> > > +			bounce = kmemdup(buf, dma_xfer->map_len, GFP_KERNEL);
> > > +		if (!bounce)
> > > +			return NULL;
> > > +		dma_buf = bounce;
> > > +	}
> > > +
> > > +	dma_xfer->addr = dma_map_single(dev, dma_buf, dma_xfer->map_len, dir);
> > > +	if (dma_mapping_error(dev, dma_xfer->addr))
> > > +		return NULL;
> > > +
> > > +	dma_xfer->bounce_buf = no_free_ptr(bounce);
> > > +	return no_free_ptr(dma_xfer);
> > > +}
> > > +EXPORT_SYMBOL_GPL(i3c_master_dma_map_single);
> > > +
> > > +/**
> > > + * i3c_master_dma_unmap_single() - Unmap buffer after DMA
> > > + * @dma_xfer: DMA transfer and mapping descriptor
> > > + *
> > > + * Unmap buffer and cleanup DMA transfer descriptor.
> > > + */
> > > +void i3c_master_dma_unmap_single(struct i3c_dma *dma_xfer)
> > > +{
> > > +	struct i3c_dma *d __free(kfree) = dma_xfer;
> > > +	void *bounce __free(kfree) = d->bounce_buf;
> >
> > This simple free, call free(dma_xfer) after memcpy directly.
> >
> I'm confused. So should I use __free() infra only in
> i3c_master_dma_map_single() and use explicit kfree() for dma_xfer and
> bounce_buf here?

See previous comments. __free() help reduce error handle. here just add
complex instead of simplify code.

Becuase mipi use build in dma, __free() have not help much, but it will
help much if use dmaegine because may dmaegine need check return value.

Frank

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



More information about the linux-i3c mailing list