[PATCH v2 1/4] i3c: master: Add helpers for DMA mapping and bounce buffer handling
Jarkko Nikula
jarkko.nikula at linux.intel.com
Mon Aug 18 23:08:36 PDT 2025
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?
More information about the linux-i3c
mailing list