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