diff options
author | Nicolin Chen <Guangyu.Chen@freescale.com> | 2014-07-16 12:16:31 +0800 |
---|---|---|
committer | Nitin Garg <nitin.garg@nxp.com> | 2016-01-14 10:59:35 -0600 |
commit | 8e53d97721aa5ade07bc3bd0bd8ab3dc14a83c32 (patch) | |
tree | 32aeddde907a09b3b47349398f10035e64fe53ca /drivers/dma | |
parent | e6d9a9f1e1f86c77c89bd6455f3902074be5ca09 (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.c | 21 |
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; } |