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@freescale.com>2015-09-17 09:20:29 -0500
commitcb56e700f80f99395ddf54adf588297c6576cb21 (patch)
tree3ece4c18e9a40d6dedea53a4d6ef20ffc9ac5b41 /drivers/dma
parent45048822a5f62e45da189fba1dcbb6dcc4df39d1 (diff)
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)
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 551d3ab43db0..253d68b1d4a2 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -704,6 +704,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);
@@ -714,7 +722,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 */
@@ -729,7 +737,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);
}
@@ -901,9 +912,13 @@ static void sdma_disable_channel(struct sdma_channel *sdmac)
{
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);
}
static void sdma_set_watermarklevel_for_p2p(struct sdma_channel *sdmac)