static int cdns_i3c_master_priv_xfers()

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;
}