diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-11 10:57:18 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2016-03-11 10:57:18 -0800 |
commit | 20698c922f81047c4d45c7793b7ddce8ff58d10e (patch) | |
tree | a24d1fe48615fcf2e33c4136c92b208d624d42f1 | |
parent | 7ae9c768e19ec8ce1b397a5c4abf88f9ee053e09 (diff) | |
parent | 25c5e9626ca4d40928dc9c44f009ce2ed0a739e7 (diff) | |
download | linux-20698c922f81047c4d45c7793b7ddce8ff58d10e.tar.bz2 |
Merge tag 'dmaengine-fix-4.5' of git://git.infradead.org/users/vkoul/slave-dma
Pull dmaengine fixes from Vinod Koul:
"Two fixes showed up in last few days, and they should be included in
4.5. Summary:
Two more late fixes to drivers, nothing major here:
- A memory leak fix in fsdma unmap the dma descriptors on freeup
- A fix in xdmac driver for residue calculation of dma descriptor"
* tag 'dmaengine-fix-4.5' of git://git.infradead.org/users/vkoul/slave-dma:
dmaengine: at_xdmac: fix residue computation
dmaengine: fsldma: fix memory leak
-rw-r--r-- | drivers/dma/at_xdmac.c | 42 | ||||
-rw-r--r-- | drivers/dma/fsldma.c | 2 |
2 files changed, 41 insertions, 3 deletions
diff --git a/drivers/dma/at_xdmac.c b/drivers/dma/at_xdmac.c index 64f5d1bdbb48..8e304b1befc5 100644 --- a/drivers/dma/at_xdmac.c +++ b/drivers/dma/at_xdmac.c @@ -176,6 +176,7 @@ #define AT_XDMAC_MAX_CHAN 0x20 #define AT_XDMAC_MAX_CSIZE 16 /* 16 data */ #define AT_XDMAC_MAX_DWIDTH 8 /* 64 bits */ +#define AT_XDMAC_RESIDUE_MAX_RETRIES 5 #define AT_XDMAC_DMA_BUSWIDTHS\ (BIT(DMA_SLAVE_BUSWIDTH_UNDEFINED) |\ @@ -1395,8 +1396,8 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie, struct at_xdmac_desc *desc, *_desc; struct list_head *descs_list; enum dma_status ret; - int residue; - u32 cur_nda, mask, value; + int residue, retry; + u32 cur_nda, check_nda, cur_ubc, mask, value; u8 dwidth = 0; unsigned long flags; @@ -1433,7 +1434,42 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie, cpu_relax(); } + /* + * When processing the residue, we need to read two registers but we + * can't do it in an atomic way. AT_XDMAC_CNDA is used to find where + * we stand in the descriptor list and AT_XDMAC_CUBC is used + * to know how many data are remaining for the current descriptor. + * Since the dma channel is not paused to not loose data, between the + * AT_XDMAC_CNDA and AT_XDMAC_CUBC read, we may have change of + * descriptor. + * For that reason, after reading AT_XDMAC_CUBC, we check if we are + * still using the same descriptor by reading a second time + * AT_XDMAC_CNDA. If AT_XDMAC_CNDA has changed, it means we have to + * read again AT_XDMAC_CUBC. + * Memory barriers are used to ensure the read order of the registers. + * A max number of retries is set because unlikely it can never ends if + * we are transferring a lot of data with small buffers. + */ cur_nda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA) & 0xfffffffc; + rmb(); + cur_ubc = at_xdmac_chan_read(atchan, AT_XDMAC_CUBC); + for (retry = 0; retry < AT_XDMAC_RESIDUE_MAX_RETRIES; retry++) { + rmb(); + check_nda = at_xdmac_chan_read(atchan, AT_XDMAC_CNDA) & 0xfffffffc; + + if (likely(cur_nda == check_nda)) + break; + + cur_nda = check_nda; + rmb(); + cur_ubc = at_xdmac_chan_read(atchan, AT_XDMAC_CUBC); + } + + if (unlikely(retry >= AT_XDMAC_RESIDUE_MAX_RETRIES)) { + ret = DMA_ERROR; + goto spin_unlock; + } + /* * Remove size of all microblocks already transferred and the current * one. Then add the remaining size to transfer of the current @@ -1446,7 +1482,7 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie, if ((desc->lld.mbr_nda & 0xfffffffc) == cur_nda) break; } - residue += at_xdmac_chan_read(atchan, AT_XDMAC_CUBC) << dwidth; + residue += cur_ubc << dwidth; dma_set_residue(txstate, residue); diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c index 2209f75fdf05..aac85c30c2cf 100644 --- a/drivers/dma/fsldma.c +++ b/drivers/dma/fsldma.c @@ -522,6 +522,8 @@ static dma_cookie_t fsldma_run_tx_complete_actions(struct fsldma_chan *chan, chan_dbg(chan, "LD %p callback\n", desc); txd->callback(txd->callback_param); } + + dma_descriptor_unmap(txd); } /* Run any dependencies */ |