Skip to content

Commit 4afd728

Browse files
jhnikulaalexandrebelloni
authored andcommitted
i3c: mipi-i3c-hci: Add DMA bounce buffer for private transfers
Implement a local bounce buffer for private I3C SDR and I2C transfers when using DMA and the buffer attached to the transfer is not DMA safe. Otherwise the DMA transfer will fail and with following warning: [ 11.411059] i3c mipi-i3c-hci.0: rejecting DMA map of vmalloc memory [ 11.417313] WARNING: CPU: 3 PID: 357 at include/linux/dma-mapping.h:332 hci_dma_queue_xfer+0x2e2/0x300 [mipi_i3c_hci] Strictly speaking private I3C SDR transfers are expected to pass a DMA-able buffer. However I fear this requirement may easily be slipped or go unnoticed when I3C interface support is added into a existing device driver that use regmap API to read/write stack variables. For example this is the case with the commit 2660b00 ("iio: imu: st_lsm6dsx: add i3c basic support for LSM6DSO and LSM6DSR"). Buffer of an I2C message is not required to be DMA safe and the I2C core provides i2c_(get|put)_dma_safe_msg_buf() helpers for the host controllers that do DMA and that is also recommendation for the i2c_xfers() callback from the I3C core. However due to above I3C private transfers reason I decided to implement a bounce buffer for them and reuse the same code for the I2C transfers too. Since this driver is currently the only I3C host controller driver that can do DMA the implementation is done here and not in I3C core. Signed-off-by: Jarkko Nikula <jarkko.nikula@linux.intel.com> Link: https://lore.kernel.org/r/20231109133708.653950-5-jarkko.nikula@linux.intel.com Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
1 parent f83f86e commit 4afd728

File tree

3 files changed

+44
-1
lines changed

3 files changed

+44
-1
lines changed

drivers/i3c/master/mipi-i3c-hci/core.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -276,6 +276,34 @@ static int i3c_hci_daa(struct i3c_master_controller *m)
276276
return hci->cmd->perform_daa(hci);
277277
}
278278

279+
static int i3c_hci_alloc_safe_xfer_buf(struct i3c_hci *hci,
280+
struct hci_xfer *xfer)
281+
{
282+
if (hci->io != &mipi_i3c_hci_dma ||
283+
xfer->data == NULL || !is_vmalloc_addr(xfer->data))
284+
return 0;
285+
286+
if (xfer->rnw)
287+
xfer->bounce_buf = kzalloc(xfer->data_len, GFP_KERNEL);
288+
else
289+
xfer->bounce_buf = kmemdup(xfer->data,
290+
xfer->data_len, GFP_KERNEL);
291+
292+
return xfer->bounce_buf == NULL ? -ENOMEM : 0;
293+
}
294+
295+
static void i3c_hci_free_safe_xfer_buf(struct i3c_hci *hci,
296+
struct hci_xfer *xfer)
297+
{
298+
if (hci->io != &mipi_i3c_hci_dma || xfer->bounce_buf == NULL)
299+
return;
300+
301+
if (xfer->rnw)
302+
memcpy(xfer->data, xfer->bounce_buf, xfer->data_len);
303+
304+
kfree(xfer->bounce_buf);
305+
}
306+
279307
static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev,
280308
struct i3c_priv_xfer *i3c_xfers,
281309
int nxfers)
@@ -309,6 +337,9 @@ static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev,
309337
}
310338
hci->cmd->prep_i3c_xfer(hci, dev, &xfer[i]);
311339
xfer[i].cmd_desc[0] |= CMD_0_ROC;
340+
ret = i3c_hci_alloc_safe_xfer_buf(hci, &xfer[i]);
341+
if (ret)
342+
goto out;
312343
}
313344
last = i - 1;
314345
xfer[last].cmd_desc[0] |= CMD_0_TOC;
@@ -332,6 +363,9 @@ static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev,
332363
}
333364

334365
out:
366+
for (i = 0; i < nxfers; i++)
367+
i3c_hci_free_safe_xfer_buf(hci, &xfer[i]);
368+
335369
hci_free_xfer(xfer, nxfers);
336370
return ret;
337371
}
@@ -357,6 +391,9 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev,
357391
xfer[i].rnw = i2c_xfers[i].flags & I2C_M_RD;
358392
hci->cmd->prep_i2c_xfer(hci, dev, &xfer[i]);
359393
xfer[i].cmd_desc[0] |= CMD_0_ROC;
394+
ret = i3c_hci_alloc_safe_xfer_buf(hci, &xfer[i]);
395+
if (ret)
396+
goto out;
360397
}
361398
last = i - 1;
362399
xfer[last].cmd_desc[0] |= CMD_0_TOC;
@@ -378,6 +415,9 @@ static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev,
378415
}
379416

380417
out:
418+
for (i = 0; i < nxfers; i++)
419+
i3c_hci_free_safe_xfer_buf(hci, &xfer[i]);
420+
381421
hci_free_xfer(xfer, nxfers);
382422
return ret;
383423
}

drivers/i3c/master/mipi-i3c-hci/dma.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,7 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
362362
struct hci_rh_data *rh;
363363
unsigned int i, ring, enqueue_ptr;
364364
u32 op1_val, op2_val;
365+
void *buf;
365366

366367
/* For now we only use ring 0 */
367368
ring = 0;
@@ -390,9 +391,10 @@ static int hci_dma_queue_xfer(struct i3c_hci *hci,
390391

391392
/* 2nd and 3rd words of Data Buffer Descriptor Structure */
392393
if (xfer->data) {
394+
buf = xfer->bounce_buf ? xfer->bounce_buf : xfer->data;
393395
xfer->data_dma =
394396
dma_map_single(&hci->master.dev,
395-
xfer->data,
397+
buf,
396398
xfer->data_len,
397399
xfer->rnw ?
398400
DMA_FROM_DEVICE :

drivers/i3c/master/mipi-i3c-hci/hci.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ struct hci_xfer {
9090
struct {
9191
/* DMA specific */
9292
dma_addr_t data_dma;
93+
void *bounce_buf;
9394
int ring_number;
9495
int ring_entry;
9596
};

0 commit comments

Comments
 (0)