mirror of https://gitee.com/openkylin/linux.git
fsldma: implement support for scatterlist to scatterlist copy
Now that the DMAEngine API has support for scatterlist to scatterlist copy, implement support for the Freescale DMA controller. Signed-off-by: Ira W. Snyder <iws@ovro.caltech.edu> Signed-off-by: Dan Williams <dan.j.williams@intel.com>
This commit is contained in:
parent
a86ee03ce6
commit
c14330417e
|
@ -38,6 +38,8 @@
|
|||
#include <asm/fsldma.h>
|
||||
#include "fsldma.h"
|
||||
|
||||
static const char msg_ld_oom[] = "No free memory for link descriptor\n";
|
||||
|
||||
static void dma_init(struct fsldma_chan *chan)
|
||||
{
|
||||
/* Reset the channel */
|
||||
|
@ -499,7 +501,7 @@ fsl_dma_prep_interrupt(struct dma_chan *dchan, unsigned long flags)
|
|||
|
||||
new = fsl_dma_alloc_descriptor(chan);
|
||||
if (!new) {
|
||||
dev_err(chan->dev, "No free memory for link descriptor\n");
|
||||
dev_err(chan->dev, msg_ld_oom);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -536,8 +538,7 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy(
|
|||
/* Allocate the link descriptor from DMA pool */
|
||||
new = fsl_dma_alloc_descriptor(chan);
|
||||
if (!new) {
|
||||
dev_err(chan->dev,
|
||||
"No free memory for link descriptor\n");
|
||||
dev_err(chan->dev, msg_ld_oom);
|
||||
goto fail;
|
||||
}
|
||||
#ifdef FSL_DMA_LD_DEBUG
|
||||
|
@ -583,6 +584,125 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy(
|
|||
return NULL;
|
||||
}
|
||||
|
||||
static struct dma_async_tx_descriptor *fsl_dma_prep_sg(struct dma_chan *dchan,
|
||||
struct scatterlist *dst_sg, unsigned int dst_nents,
|
||||
struct scatterlist *src_sg, unsigned int src_nents,
|
||||
unsigned long flags)
|
||||
{
|
||||
struct fsl_desc_sw *first = NULL, *prev = NULL, *new = NULL;
|
||||
struct fsldma_chan *chan = to_fsl_chan(dchan);
|
||||
size_t dst_avail, src_avail;
|
||||
dma_addr_t dst, src;
|
||||
size_t len;
|
||||
|
||||
/* basic sanity checks */
|
||||
if (dst_nents == 0 || src_nents == 0)
|
||||
return NULL;
|
||||
|
||||
if (dst_sg == NULL || src_sg == NULL)
|
||||
return NULL;
|
||||
|
||||
/*
|
||||
* TODO: should we check that both scatterlists have the same
|
||||
* TODO: number of bytes in total? Is that really an error?
|
||||
*/
|
||||
|
||||
/* get prepared for the loop */
|
||||
dst_avail = sg_dma_len(dst_sg);
|
||||
src_avail = sg_dma_len(src_sg);
|
||||
|
||||
/* run until we are out of scatterlist entries */
|
||||
while (true) {
|
||||
|
||||
/* create the largest transaction possible */
|
||||
len = min_t(size_t, src_avail, dst_avail);
|
||||
len = min_t(size_t, len, FSL_DMA_BCR_MAX_CNT);
|
||||
if (len == 0)
|
||||
goto fetch;
|
||||
|
||||
dst = sg_dma_address(dst_sg) + sg_dma_len(dst_sg) - dst_avail;
|
||||
src = sg_dma_address(src_sg) + sg_dma_len(src_sg) - src_avail;
|
||||
|
||||
/* allocate and populate the descriptor */
|
||||
new = fsl_dma_alloc_descriptor(chan);
|
||||
if (!new) {
|
||||
dev_err(chan->dev, msg_ld_oom);
|
||||
goto fail;
|
||||
}
|
||||
#ifdef FSL_DMA_LD_DEBUG
|
||||
dev_dbg(chan->dev, "new link desc alloc %p\n", new);
|
||||
#endif
|
||||
|
||||
set_desc_cnt(chan, &new->hw, len);
|
||||
set_desc_src(chan, &new->hw, src);
|
||||
set_desc_dst(chan, &new->hw, dst);
|
||||
|
||||
if (!first)
|
||||
first = new;
|
||||
else
|
||||
set_desc_next(chan, &prev->hw, new->async_tx.phys);
|
||||
|
||||
new->async_tx.cookie = 0;
|
||||
async_tx_ack(&new->async_tx);
|
||||
prev = new;
|
||||
|
||||
/* Insert the link descriptor to the LD ring */
|
||||
list_add_tail(&new->node, &first->tx_list);
|
||||
|
||||
/* update metadata */
|
||||
dst_avail -= len;
|
||||
src_avail -= len;
|
||||
|
||||
fetch:
|
||||
/* fetch the next dst scatterlist entry */
|
||||
if (dst_avail == 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_avail = sg_dma_len(dst_sg);
|
||||
}
|
||||
|
||||
/* fetch the next src scatterlist entry */
|
||||
if (src_avail == 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_avail = sg_dma_len(src_sg);
|
||||
}
|
||||
}
|
||||
|
||||
new->async_tx.flags = flags; /* client is in control of this ack */
|
||||
new->async_tx.cookie = -EBUSY;
|
||||
|
||||
/* Set End-of-link to the last link descriptor of new list */
|
||||
set_ld_eol(chan, new);
|
||||
|
||||
return &first->async_tx;
|
||||
|
||||
fail:
|
||||
if (!first)
|
||||
return NULL;
|
||||
|
||||
fsldma_free_desc_list_reverse(chan, &first->tx_list);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* fsl_dma_prep_slave_sg - prepare descriptors for a DMA_SLAVE transaction
|
||||
* @chan: DMA channel
|
||||
|
@ -1327,11 +1447,13 @@ static int __devinit fsldma_of_probe(struct platform_device *op,
|
|||
|
||||
dma_cap_set(DMA_MEMCPY, fdev->common.cap_mask);
|
||||
dma_cap_set(DMA_INTERRUPT, fdev->common.cap_mask);
|
||||
dma_cap_set(DMA_SG, fdev->common.cap_mask);
|
||||
dma_cap_set(DMA_SLAVE, fdev->common.cap_mask);
|
||||
fdev->common.device_alloc_chan_resources = fsl_dma_alloc_chan_resources;
|
||||
fdev->common.device_free_chan_resources = fsl_dma_free_chan_resources;
|
||||
fdev->common.device_prep_dma_interrupt = fsl_dma_prep_interrupt;
|
||||
fdev->common.device_prep_dma_memcpy = fsl_dma_prep_memcpy;
|
||||
fdev->common.device_prep_dma_sg = fsl_dma_prep_sg;
|
||||
fdev->common.device_tx_status = fsl_tx_status;
|
||||
fdev->common.device_issue_pending = fsl_dma_memcpy_issue_pending;
|
||||
fdev->common.device_prep_slave_sg = fsl_dma_prep_slave_sg;
|
||||
|
|
Loading…
Reference in New Issue