static int tsi721_submit_sg()

in devices/tsi721_dma.c [409:530]


static int tsi721_submit_sg(struct tsi721_tx_desc *desc)
{
	struct dma_chan *dchan = desc->txd.chan;
	struct tsi721_bdma_chan *bdma_chan = to_tsi721_chan(dchan);
	u32 sys_size;
	u64 rio_addr;
	dma_addr_t next_addr;
	u32 bcount;
	struct scatterlist *sg;
	unsigned int i;
	int err = 0;
	struct tsi721_dma_desc *bd_ptr = NULL;
	u32 idx, rd_idx;
	u32 add_count = 0;
	struct device *ch_dev = &dchan->dev->device;

	if (!tsi721_dma_is_idle(bdma_chan)) {
		tsi_err(ch_dev, "DMAC%d ERR: Attempt to use non-idle channel",
			bdma_chan->id);
		return -EIO;
	}

	/*
	 * Fill DMA channel's hardware buffer descriptors.
	 * (NOTE: RapidIO destination address is limited to 64 bits for now)
	 */
	rio_addr = desc->rio_addr;
	next_addr = -1;
	bcount = 0;
	sys_size = dma_to_mport(dchan->device)->sys_size;

	rd_idx = ioread32(bdma_chan->regs + TSI721_DMAC_DRDCNT);
	rd_idx %= (bdma_chan->bd_num + 1);

	idx = bdma_chan->wr_count_next % (bdma_chan->bd_num + 1);
	if (idx == bdma_chan->bd_num) {
		/* wrap around link descriptor */
		idx = 0;
		add_count++;
	}

	tsi_debug(DMA, ch_dev, "DMAC%d BD ring status: rdi=%d wri=%d",
		  bdma_chan->id, rd_idx, idx);

	for_each_sg(desc->sg, sg, desc->sg_len, i) {

		tsi_debug(DMAV, ch_dev, "DMAC%d sg%d/%d addr: 0x%llx len: %d",
			bdma_chan->id, i, desc->sg_len,
			(unsigned long long)sg_dma_address(sg), sg_dma_len(sg));

		if (sg_dma_len(sg) > TSI721_BDMA_MAX_BCOUNT) {
			tsi_err(ch_dev, "DMAC%d SG entry %d is too large",
				bdma_chan->id, i);
			err = -EINVAL;
			break;
		}

		/*
		 * If this sg entry forms contiguous block with previous one,
		 * try to merge it into existing DMA descriptor
		 */
		if (next_addr == sg_dma_address(sg) &&
		    bcount + sg_dma_len(sg) <= TSI721_BDMA_MAX_BCOUNT) {
			/* Adjust byte count of the descriptor */
			bcount += sg_dma_len(sg);
			goto entry_done;
		} else if (next_addr != -1) {
			/* Finalize descriptor using total byte count value */
			tsi721_desc_fill_end(bd_ptr, bcount, 0);
			tsi_debug(DMAV, ch_dev,	"DMAC%d prev desc final len: %d",
				  bdma_chan->id, bcount);
		}

		desc->rio_addr = rio_addr;

		if (i && idx == rd_idx) {
			tsi_debug(DMAV, ch_dev,
				  "DMAC%d HW descriptor ring is full @ %d",
				  bdma_chan->id, i);
			desc->sg = sg;
			desc->sg_len -= i;
			break;
		}

		bd_ptr = &((struct tsi721_dma_desc *)bdma_chan->bd_base)[idx];
		err = tsi721_desc_fill_init(desc, bd_ptr, sg, sys_size);
		if (err) {
			tsi_err(ch_dev, "Failed to build desc: err=%d", err);
			break;
		}

		tsi_debug(DMAV, ch_dev, "DMAC%d bd_ptr = %p did=%d raddr=0x%llx",
			  bdma_chan->id, bd_ptr, desc->destid, desc->rio_addr);

		next_addr = sg_dma_address(sg);
		bcount = sg_dma_len(sg);

		add_count++;
		if (++idx == bdma_chan->bd_num) {
			/* wrap around link descriptor */
			idx = 0;
			add_count++;
		}

entry_done:
		if (sg_is_last(sg)) {
			tsi721_desc_fill_end(bd_ptr, bcount, 0);
			tsi_debug(DMAV, ch_dev,
				  "DMAC%d last desc final len: %d",
				  bdma_chan->id, bcount);
			desc->sg_len = 0;
		} else {
			rio_addr += sg_dma_len(sg);
			next_addr += sg_dma_len(sg);
		}
	}

	if (!err)
		bdma_chan->wr_count_next += add_count;

	return err;
}