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 <[email protected]>
Signed-off-by: Chanho Park <[email protected]>
Acked-by: Jassi Brar <[email protected]>
Signed-off-by: Kyungmin Park <[email protected]>
---
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