in master/i3c-master-cdns.c [734:813]
static int cdns_i3c_master_priv_xfers(struct i3c_dev_desc *dev,
struct i3c_priv_xfer *xfers,
int nxfers)
{
struct i3c_master_controller *m = i3c_dev_get_master(dev);
struct cdns_i3c_master *master = to_cdns_i3c_master(m);
int txslots = 0, rxslots = 0, i, ret;
struct cdns_i3c_xfer *cdns_xfer;
for (i = 0; i < nxfers; i++) {
if (xfers[i].len > CMD0_FIFO_PL_LEN_MAX)
return -ENOTSUPP;
}
if (!nxfers)
return 0;
if (nxfers > master->caps.cmdfifodepth ||
nxfers > master->caps.cmdrfifodepth)
return -ENOTSUPP;
/*
* First make sure that all transactions (block of transfers separated
* by a STOP marker) fit in the FIFOs.
*/
for (i = 0; i < nxfers; i++) {
if (xfers[i].rnw)
rxslots += DIV_ROUND_UP(xfers[i].len, 4);
else
txslots += DIV_ROUND_UP(xfers[i].len, 4);
}
if (rxslots > master->caps.rxfifodepth ||
txslots > master->caps.txfifodepth)
return -ENOTSUPP;
cdns_xfer = cdns_i3c_master_alloc_xfer(master, nxfers);
if (!cdns_xfer)
return -ENOMEM;
for (i = 0; i < nxfers; i++) {
struct cdns_i3c_cmd *ccmd = &cdns_xfer->cmds[i];
u32 pl_len = xfers[i].len;
ccmd->cmd0 = CMD0_FIFO_DEV_ADDR(dev->info.dyn_addr) |
CMD0_FIFO_PRIV_XMIT_MODE(XMIT_BURST_WITHOUT_SUBADDR);
if (xfers[i].rnw) {
ccmd->cmd0 |= CMD0_FIFO_RNW;
ccmd->rx_buf = xfers[i].data.in;
ccmd->rx_len = xfers[i].len;
pl_len++;
} else {
ccmd->tx_buf = xfers[i].data.out;
ccmd->tx_len = xfers[i].len;
}
ccmd->cmd0 |= CMD0_FIFO_PL_LEN(pl_len);
if (i < nxfers - 1)
ccmd->cmd0 |= CMD0_FIFO_RSBC;
if (!i)
ccmd->cmd0 |= CMD0_FIFO_BCH;
}
cdns_i3c_master_queue_xfer(master, cdns_xfer);
if (!wait_for_completion_timeout(&cdns_xfer->comp,
msecs_to_jiffies(1000)))
cdns_i3c_master_unqueue_xfer(master, cdns_xfer);
ret = cdns_xfer->ret;
for (i = 0; i < nxfers; i++)
xfers[i].err = cdns_i3c_cmd_get_err(&cdns_xfer->cmds[i]);
cdns_i3c_master_free_xfer(cdns_xfer);
return ret;
}