[PATCH] dmaengine: sirf: add dmaengine_prep_slave_single/sg support

Barry Song 21cnbao at gmail.com
Sun Aug 25 08:57:13 EDT 2013


the dma engine of sirfsoc supports interleaved mode, but if we set
xlen=width instead xlen<width, it will work as non-interleaved. as
most clients of sirf dma driver still don't need interleaved mode,
so here we still need to implement prep_slave_sg entry so that users
like uart, spi can use these APIs instead of interleaved API.

the dma engine of sirfsoc doesn't support hardware s/g, so here we
are using the list of desc nodes to do SW s/g. when dma operations
finish, driver will re-start the next desc automatically.

Signed-off-by: Barry Song <Baohua.Song at csr.com>
---
 drivers/dma/sirf-dma.c | 57 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 57 insertions(+)

diff --git a/drivers/dma/sirf-dma.c b/drivers/dma/sirf-dma.c
index 6aec3ad..c226c79 100644
--- a/drivers/dma/sirf-dma.c
+++ b/drivers/dma/sirf-dma.c
@@ -577,6 +577,62 @@ err_dir:
 }
 
 static struct dma_async_tx_descriptor *
+sirfsoc_dma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
+	unsigned int sg_len, enum dma_transfer_direction direction,
+	unsigned long flags, void *context)
+{
+	struct sirfsoc_dma_chan *schan = dma_chan_to_sirfsoc_dma_chan(chan);
+	struct sirfsoc_dma_desc *first_sdesc;
+	struct sirfsoc_dma_desc *sdesc;
+	struct list_head *l;
+	unsigned long iflags;
+	struct scatterlist *sg;
+	int desc_cnt = 0;
+	int i;
+	int ret;
+
+	/*
+	 * the hardware doesn't support sg, here we use software list
+	 * to simulate sg, so make sure we have enough desc nodes
+	 */
+	spin_lock_irqsave(&schan->lock, iflags);
+	list_for_each(l, &schan->free)
+		desc_cnt++;
+	if (desc_cnt < sg_len) {
+		spin_unlock_irqrestore(&schan->lock, iflags);
+		pr_err("sirfsoc DMA channel busy\n");
+		ret = -EBUSY;
+		goto err;
+	}
+
+	first_sdesc = list_first_entry(&schan->free, struct sirfsoc_dma_desc,
+			node);
+
+	for_each_sg(sgl, sg, sg_len, i) {
+		dma_addr_t addr = sg_dma_address(sg);
+		unsigned int len = sg_dma_len(sg);
+
+		sdesc = list_first_entry(&schan->free, struct sirfsoc_dma_desc,
+			node);
+		list_del(&sdesc->node);
+
+		sdesc->addr = addr;
+		sdesc->dir = (direction == DMA_MEM_TO_DEV ? 1 : 0);
+		sdesc->cyclic = 0;
+		sdesc->xlen = len / SIRFSOC_DMA_WORD_LEN;
+		sdesc->width = sdesc->xlen;
+		sdesc->ylen = 0;
+
+		list_add_tail(&sdesc->node, &schan->prepared);
+	}
+	spin_unlock_irqrestore(&schan->lock, iflags);
+
+	return &first_sdesc->desc;
+err:
+	return ERR_PTR(ret);
+}
+
+static struct dma_async_tx_descriptor *
 sirfsoc_dma_prep_cyclic(struct dma_chan *chan, dma_addr_t addr,
 	size_t buf_len, size_t period_len,
 	enum dma_transfer_direction direction, unsigned long flags, void *context)
@@ -711,6 +767,7 @@ static int sirfsoc_dma_probe(struct platform_device *op)
 	dma->device_control = sirfsoc_dma_control;
 	dma->device_tx_status = sirfsoc_dma_tx_status;
 	dma->device_prep_interleaved_dma = sirfsoc_dma_prep_interleaved;
+	dma->device_prep_slave_sg = sirfsoc_dma_prep_slave_sg;
 	dma->device_prep_dma_cyclic = sirfsoc_dma_prep_cyclic;
 
 	INIT_LIST_HEAD(&dma->channels);
-- 
1.8.2.3




More information about the linux-arm-kernel mailing list