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 <asm/fsldma.h>
|
||||||
#include "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)
|
static void dma_init(struct fsldma_chan *chan)
|
||||||
{
|
{
|
||||||
/* Reset the channel */
|
/* Reset the channel */
|
||||||
|
@ -499,7 +501,7 @@ fsl_dma_prep_interrupt(struct dma_chan *dchan, unsigned long flags)
|
||||||
|
|
||||||
new = fsl_dma_alloc_descriptor(chan);
|
new = fsl_dma_alloc_descriptor(chan);
|
||||||
if (!new) {
|
if (!new) {
|
||||||
dev_err(chan->dev, "No free memory for link descriptor\n");
|
dev_err(chan->dev, msg_ld_oom);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -536,8 +538,7 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy(
|
||||||
/* Allocate the link descriptor from DMA pool */
|
/* Allocate the link descriptor from DMA pool */
|
||||||
new = fsl_dma_alloc_descriptor(chan);
|
new = fsl_dma_alloc_descriptor(chan);
|
||||||
if (!new) {
|
if (!new) {
|
||||||
dev_err(chan->dev,
|
dev_err(chan->dev, msg_ld_oom);
|
||||||
"No free memory for link descriptor\n");
|
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
#ifdef FSL_DMA_LD_DEBUG
|
#ifdef FSL_DMA_LD_DEBUG
|
||||||
|
@ -583,6 +584,125 @@ static struct dma_async_tx_descriptor *fsl_dma_prep_memcpy(
|
||||||
return NULL;
|
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
|
* fsl_dma_prep_slave_sg - prepare descriptors for a DMA_SLAVE transaction
|
||||||
* @chan: DMA channel
|
* @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_MEMCPY, fdev->common.cap_mask);
|
||||||
dma_cap_set(DMA_INTERRUPT, 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);
|
dma_cap_set(DMA_SLAVE, fdev->common.cap_mask);
|
||||||
fdev->common.device_alloc_chan_resources = fsl_dma_alloc_chan_resources;
|
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_free_chan_resources = fsl_dma_free_chan_resources;
|
||||||
fdev->common.device_prep_dma_interrupt = fsl_dma_prep_interrupt;
|
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_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_tx_status = fsl_tx_status;
|
||||||
fdev->common.device_issue_pending = fsl_dma_memcpy_issue_pending;
|
fdev->common.device_issue_pending = fsl_dma_memcpy_issue_pending;
|
||||||
fdev->common.device_prep_slave_sg = fsl_dma_prep_slave_sg;
|
fdev->common.device_prep_slave_sg = fsl_dma_prep_slave_sg;
|
||||||
|
|
Loading…
Reference in New Issue