summaryrefslogtreecommitdiff
path: root/drivers/dma/fsl-edma-common.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/dma/fsl-edma-common.c')
-rw-r--r--drivers/dma/fsl-edma-common.c32
1 files changed, 30 insertions, 2 deletions
diff --git a/drivers/dma/fsl-edma-common.c b/drivers/dma/fsl-edma-common.c
index d6010486ee50..108ffd26b13e 100644
--- a/drivers/dma/fsl-edma-common.c
+++ b/drivers/dma/fsl-edma-common.c
@@ -4,6 +4,7 @@
// Copyright (c) 2017 Sysam, Angelo Dureghello <angelo@sysam.it>
#include <linux/dmapool.h>
+#include <linux/delay.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
@@ -109,10 +110,15 @@ void fsl_edma_chan_mux(struct fsl_edma_chan *fsl_chan,
u32 ch = fsl_chan->vchan.chan.chan_id;
void __iomem *muxaddr;
unsigned int chans_per_mux, ch_off;
+ int endian_diff[4] = {3, 1, -1, -3};
u32 dmamux_nr = fsl_chan->edma->drvdata->dmamuxs;
chans_per_mux = fsl_chan->edma->n_chans / dmamux_nr;
ch_off = fsl_chan->vchan.chan.chan_id % chans_per_mux;
+
+ if (fsl_chan->edma->drvdata->mux_swap)
+ ch_off += endian_diff[ch_off % 4];
+
muxaddr = fsl_chan->edma->muxbase[ch / chans_per_mux];
slot = EDMAMUX_CHCFG_SOURCE(slot);
@@ -155,11 +161,24 @@ EXPORT_SYMBOL_GPL(fsl_edma_free_desc);
int fsl_edma_terminate_all(struct dma_chan *chan)
{
struct fsl_edma_chan *fsl_chan = to_fsl_edma_chan(chan);
+ struct edma_regs *regs = &fsl_chan->edma->regs;
+ u32 ch = fsl_chan->vchan.chan.chan_id;
unsigned long flags;
+ int count = 0;
LIST_HEAD(head);
- spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
fsl_edma_disable_request(fsl_chan);
+
+ /*
+ * Checking ACTIVE to ensure minor loop stop indeed to prevent the
+ * potential illegal memory write if channel not stopped with buffer
+ * freed.
+ */
+ while (count++ < EDMA_MINOR_LOOP_TIMEOUT && (EDMA_TCD_CSR_ACTIVE &
+ edma_readw(fsl_chan->edma, &regs->tcd[ch].csr)))
+ udelay(1);
+
+ spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
fsl_chan->edesc = NULL;
fsl_chan->idle = true;
vchan_get_all_descriptors(&fsl_chan->vchan, &head);
@@ -305,6 +324,11 @@ static size_t fsl_edma_desc_residue(struct fsl_edma_chan *fsl_chan,
return len;
}
+void fsl_edma_get_realcnt(struct fsl_edma_chan *fsl_chan)
+{
+ fsl_chan->chn_real_count = fsl_edma_desc_residue(fsl_chan, NULL, true);
+}
+
enum dma_status fsl_edma_tx_status(struct dma_chan *chan,
dma_cookie_t cookie, struct dma_tx_state *txstate)
{
@@ -314,8 +338,12 @@ enum dma_status fsl_edma_tx_status(struct dma_chan *chan,
unsigned long flags;
status = dma_cookie_status(chan, cookie, txstate);
- if (status == DMA_COMPLETE)
+ if (status == DMA_COMPLETE) {
+ spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
+ txstate->residue = fsl_chan->chn_real_count;
+ spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
return status;
+ }
if (!txstate)
return fsl_chan->status;