summaryrefslogtreecommitdiff
path: root/drivers/dma
diff options
context:
space:
mode:
authorNicolin Chen <Guangyu.Chen@freescale.com>2014-07-16 12:16:31 +0800
committerNitin Garg <nitin.garg@nxp.com>2016-01-14 10:59:35 -0600
commit8e53d97721aa5ade07bc3bd0bd8ab3dc14a83c32 (patch)
tree32aeddde907a09b3b47349398f10035e64fe53ca /drivers/dma
parente6d9a9f1e1f86c77c89bd6455f3902074be5ca09 (diff)
MLK-11344-9: dma: imx-sdma: A bungle of work around for BUG ON issue
cherry-pick below patch from v3.14.y: ENGR00313512 dma: imx-sdma: A bungle of work around for BUG ON issue The BUG ON issue could be triggered by such scenarios: A) issue_pending(1) -> <- SDMA irq(1) <- SDMA tasklet(1) //Normal case issue_pending(2) -> terminate_all(2) -> ... system suspend/resume issue_pending(3) -> <- SDMA irq(2) <- SDMA irq(3) //might also happen after tasklet(2) <- SDMA tasklet(2) <- SDMA tasklet(3) //BUG ON B) issue_pending(1) -> <- SDMA irq(1) <- SDMA tasklet(1) //Normal case issue_pending(2) -> <- SDMA irq(2) //might also happen after terminate_all(2) terminate_all(2) -> ... system suspend/resume issue_pending(3) -> <- SDMA irq(3) //might also happen after tasklet(2) <- SDMA tasklet(2) <- SDMA tasklet(3) //BUG ON The best fix for this issue is to eradicate irq(2) or tasklet(2). However, currently we couldn't find an effective fix for both cases above. Thus this fix could be treated as a work around. It fixes this issue by Reduce the possiblity of irq(2) and tasklet(3). Signed-off-by: Nicolin Chen <Guangyu.Chen@freescale.com> Signed-off-by: Robin Gong <b38343@freescale.com> (cherry picked from commit 1880fc41df51450825c2b17bae5be9536e26b73f) (cherry picked from commit a744dfb31122b8393e717e8a6911b221d1d49052)
Diffstat (limited to 'drivers/dma')
-rw-r--r--drivers/dma/imx-sdma.c21
1 files changed, 18 insertions, 3 deletions
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index 095dae712c12..3bbb04dcf212 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -761,6 +761,14 @@ static void mxc_sdma_handle_channel_normal(struct sdma_channel *sdmac)
static void sdma_tasklet(unsigned long data)
{
struct sdma_channel *sdmac = (struct sdma_channel *) data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&sdmac->lock, flags);
+ if (sdmac->status != DMA_IN_PROGRESS) {
+ spin_unlock_irqrestore(&sdmac->lock, flags);
+ return;
+ }
+ spin_unlock_irqrestore(&sdmac->lock, flags);
if (sdmac->flags & IMX_DMA_SG_LOOP)
sdma_handle_channel_loop(sdmac);
@@ -771,7 +779,7 @@ static void sdma_tasklet(unsigned long data)
static irqreturn_t sdma_int_handler(int irq, void *dev_id)
{
struct sdma_engine *sdma = dev_id;
- unsigned long stat;
+ unsigned long stat, flags;
stat = readl_relaxed(sdma->regs + SDMA_H_INTR);
/* not interested in channel 0 interrupts */
@@ -786,7 +794,10 @@ static irqreturn_t sdma_int_handler(int irq, void *dev_id)
(sdmac->peripheral_type != IMX_DMATYPE_HDMI))
sdma_update_channel_loop(sdmac);
- tasklet_schedule(&sdmac->tasklet);
+ spin_lock_irqsave(&sdmac->lock, flags);
+ if (sdmac->status == DMA_IN_PROGRESS)
+ tasklet_schedule(&sdmac->tasklet);
+ spin_unlock_irqrestore(&sdmac->lock, flags);
__clear_bit(channel, &stat);
}
@@ -964,9 +975,13 @@ static int sdma_disable_channel(struct dma_chan *chan)
struct sdma_channel *sdmac = to_sdma_chan(chan);
struct sdma_engine *sdma = sdmac->sdma;
int channel = sdmac->channel;
+ unsigned long flags;
- writel_relaxed(BIT(channel), sdma->regs + SDMA_H_STATSTOP);
+ spin_lock_irqsave(&sdmac->lock, flags);
sdmac->status = DMA_ERROR;
+ spin_unlock_irqrestore(&sdmac->lock, flags);
+
+ writel_relaxed(BIT(channel), sdma->regs + SDMA_H_STATSTOP);
return 0;
}