[PATCHv2] dma: pl330: add pl330_prep_dma_sg to transfer from sglist to sglist

Chanho Park chanho61.park at samsung.com
Fri Sep 13 22:50:19 EDT 2013


This patch adds prep_dma_sg function to transfer memory to memory which
mapped in scatter/gather list. The patch move get_burst_len to upwards to
call in the __pl330_prep_dma_mecpy. Some duplicated code was splitted off
from prep_dma_memcpy.
This patch also included previous Boojon Kim's patch[1] which fixes burst_size
calculation. The burst_size should be aligned not only length but also source
and destionation addresses.

[1] : http://lkml.indiana.edu/hypermail/linux/kernel/1201.1/00246.html

Cc: Boojin Kim <boojin.kim at samsung.com>
Signed-off-by: Chanho Park <chanho61.park at samsung.com>
Acked-by: Jassi Brar <jassisinghbrar at gmail.com>
Signed-off-by: Kyungmin Park <kyungmin.park at samsung.com>
---
 drivers/dma/pl330.c |  179 +++++++++++++++++++++++++++++++++++++++------------
 1 file changed, 138 insertions(+), 41 deletions(-)

diff --git a/drivers/dma/pl330.c b/drivers/dma/pl330.c
index a562d24..b272ee6 100644
--- a/drivers/dma/pl330.c
+++ b/drivers/dma/pl330.c
@@ -2602,9 +2602,33 @@ static inline void fill_px(struct pl330_xfer *px,
 	px->src_addr = src;
 }
 
+/* Call after fixing burst size */
+static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len)
+{
+	struct dma_pl330_chan *pch = desc->pchan;
+	struct pl330_info *pi = &pch->dmac->pif;
+	int burst_len;
+
+	burst_len = pi->pcfg.data_bus_width / 8;
+	burst_len *= pi->pcfg.data_buf_dep;
+	burst_len >>= desc->rqcfg.brst_size;
+
+	/* src/dst_burst_len can't be more than 16 */
+	if (burst_len > 16)
+		burst_len = 16;
+
+	while (burst_len > 1) {
+		if (!(len % (burst_len << desc->rqcfg.brst_size)))
+			break;
+		burst_len--;
+	}
+
+	return burst_len;
+}
+
 static struct dma_pl330_desc *
 __pl330_prep_dma_memcpy(struct dma_pl330_chan *pch, dma_addr_t dst,
-		dma_addr_t src, size_t len)
+		dma_addr_t src, size_t len, int burst)
 {
 	struct dma_pl330_desc *desc = pl330_get_desc(pch);
 
@@ -2626,31 +2650,23 @@ __pl330_prep_dma_memcpy(struct dma_pl330_chan *pch, dma_addr_t dst,
 	 */
 	fill_px(&desc->px, dst, src, len);
 
-	return desc;
-}
-
-/* Call after fixing burst size */
-static inline int get_burst_len(struct dma_pl330_desc *desc, size_t len)
-{
-	struct dma_pl330_chan *pch = desc->pchan;
-	struct pl330_info *pi = &pch->dmac->pif;
-	int burst_len;
-
-	burst_len = pi->pcfg.data_bus_width / 8;
-	burst_len *= pi->pcfg.data_buf_dep;
-	burst_len >>= desc->rqcfg.brst_size;
-
-	/* src/dst_burst_len can't be more than 16 */
-	if (burst_len > 16)
-		burst_len = 16;
+	desc->rqcfg.src_inc = 1;
+	desc->rqcfg.dst_inc = 1;
+	desc->req.rqtype = MEMTOMEM;
 
-	while (burst_len > 1) {
-		if (!(len % (burst_len << desc->rqcfg.brst_size)))
+	while (burst > 1) {
+		if (!(len % burst) && !(len % dst) && !(len % src))
 			break;
-		burst_len--;
+		burst /= 2;
 	}
 
-	return burst_len;
+	desc->rqcfg.brst_size = 0;
+	while (burst != (1 << desc->rqcfg.brst_size))
+		desc->rqcfg.brst_size++;
+
+	desc->rqcfg.brst_len = get_burst_len(desc, len);
+
+	return desc;
 }
 
 static struct dma_async_tx_descriptor *pl330_prep_dma_cyclic(
@@ -2752,28 +2768,12 @@ pl330_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dst,
 
 	pi = &pch->dmac->pif;
 
-	desc = __pl330_prep_dma_memcpy(pch, dst, src, len);
-	if (!desc)
-		return NULL;
-
-	desc->rqcfg.src_inc = 1;
-	desc->rqcfg.dst_inc = 1;
-	desc->req.rqtype = MEMTOMEM;
-
 	/* Select max possible burst size */
 	burst = pi->pcfg.data_bus_width / 8;
 
-	while (burst > 1) {
-		if (!(len % burst))
-			break;
-		burst /= 2;
-	}
-
-	desc->rqcfg.brst_size = 0;
-	while (burst != (1 << desc->rqcfg.brst_size))
-		desc->rqcfg.brst_size++;
-
-	desc->rqcfg.brst_len = get_burst_len(desc, len);
+	desc = __pl330_prep_dma_memcpy(pch, dst, src, len, burst);
+	if (!desc)
+		return NULL;
 
 	desc->txd.flags = flags;
 
@@ -2803,6 +2803,102 @@ static void __pl330_giveback_desc(struct dma_pl330_dmac *pdmac,
 }
 
 static struct dma_async_tx_descriptor *
+pl330_prep_dma_sg(struct dma_chan *chan,
+	struct scatterlist *dst_sg, unsigned int dst_nents,
+	struct scatterlist *src_sg, unsigned int src_nents,
+	unsigned long flags)
+{
+	struct dma_pl330_desc *first, *desc = NULL;
+	struct dma_pl330_chan *pch = to_pchan(chan);
+	struct pl330_info *pi;
+	dma_addr_t src, dst;
+	size_t len, dst_len = 0, src_len = 0;
+	int burst;
+
+	if (unlikely(!pch))
+		return NULL;
+
+	pi = &pch->dmac->pif;
+
+	/* basic sanity checks */
+	if (dst_nents == 0 || src_nents == 0)
+		return NULL;
+
+	if (dst_sg == NULL || src_sg == NULL)
+		return NULL;
+
+	first = NULL;
+
+	/* Select max possible burst size */
+	burst = pi->pcfg.data_bus_width / 8;
+
+	/* get prepared for the loop */
+	dst_len = sg_dma_len(dst_sg);
+	src_len = sg_dma_len(src_sg);
+
+	while (true) {
+		len = min_t(size_t, src_len, dst_len);
+
+		if (len == 0)
+			goto fetch;
+
+		dst = sg_dma_address(dst_sg) + sg_dma_len(dst_sg) - dst_len;
+		src = sg_dma_address(src_sg) + sg_dma_len(src_sg) - src_len;
+
+		desc = __pl330_prep_dma_memcpy(pch, dst, src, len, burst);
+		if (!desc) {
+			struct dma_pl330_dmac *pdmac = pch->dmac;
+
+			dev_err(pdmac->pif.dev,
+				"%s:%d Unable to fetch desc\n",
+				__func__, __LINE__);
+
+			__pl330_giveback_desc(pdmac, first);
+
+			return NULL;
+		}
+		if (!first)
+			first = desc;
+		else
+			list_add_tail(&desc->node, &first->node);
+
+		desc->txd.flags = flags;
+
+		dst_len -= len;
+		src_len -= len;
+
+fetch:
+		/* fetch the next dst scatterlist entry */
+		if (dst_len == 0) {
+			/* no more entries: we're done */
+			if (dst_nents == 0)
+				break;
+			/* fetch the next entry: if there are no more: done */
+			dst_sg = sg_next(dst_sg);
+			if (dst_sg == NULL)
+				break;
+			dst_nents--;
+			dst_len = sg_dma_len(dst_sg);
+		}
+
+		/* fetch the next src scatterlist entry */
+		if (src_len == 0) {
+			/* no more entries: we're done */
+			if (src_nents == 0)
+				break;
+			/* fetch the next entry: if there are no more: done */
+			src_sg = sg_next(src_sg);
+			if (src_sg == NULL)
+				break;
+			src_nents--;
+			src_len = sg_dma_len(src_sg);
+		}
+	}
+
+	return &first->txd;
+}
+
+static struct dma_async_tx_descriptor *
 pl330_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
 		unsigned int sg_len, enum dma_transfer_direction direction,
 		unsigned long flg, void *context)
@@ -2989,6 +3085,7 @@ pl330_probe(struct amba_device *adev, const struct amba_id *id)
 	pd->device_alloc_chan_resources = pl330_alloc_chan_resources;
 	pd->device_free_chan_resources = pl330_free_chan_resources;
 	pd->device_prep_dma_memcpy = pl330_prep_dma_memcpy;
+	pd->device_prep_dma_sg = pl330_prep_dma_sg;
 	pd->device_prep_dma_cyclic = pl330_prep_dma_cyclic;
 	pd->device_tx_status = pl330_tx_status;
 	pd->device_prep_slave_sg = pl330_prep_slave_sg;
-- 
1.7.9.5




More information about the linux-arm-kernel mailing list