aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/dma
diff options
context:
space:
mode:
authorMarkus Pargmann <mpa@pengutronix.de>2013-10-29 03:47:45 -0400
committerVinod Koul <vinod.koul@intel.com>2013-11-13 05:08:30 -0500
commitb2d639890b0d3379e844f6bb3c90d67ab292c80b (patch)
treead8d1df7fc51000a581bb88f540d088f3df7511b /drivers/dma
parent8de7a7d95049bdbe454ade7add08d893efe5a456 (diff)
dma: mxs-dma: Cleanup interrupt handler
The DMA interrupt handler uses its controll registers to handle all available channel interrupts it can find. This patch changes it to handle only one interrupt by directly mapping irq number to channel. It also includes a cleanup of the ctrl-register usage. Signed-off-by: Markus Pargmann <mpa@pengutronix.de> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Diffstat (limited to 'drivers/dma')
-rw-r--r--drivers/dma/mxs-dma.c96
1 files changed, 60 insertions, 36 deletions
diff --git a/drivers/dma/mxs-dma.c b/drivers/dma/mxs-dma.c
index 7ab7cecc48a4..1d1b6e99993d 100644
--- a/drivers/dma/mxs-dma.c
+++ b/drivers/dma/mxs-dma.c
@@ -27,6 +27,7 @@
27#include <linux/of.h> 27#include <linux/of.h>
28#include <linux/of_device.h> 28#include <linux/of_device.h>
29#include <linux/of_dma.h> 29#include <linux/of_dma.h>
30#include <linux/list.h>
30 31
31#include <asm/irq.h> 32#include <asm/irq.h>
32 33
@@ -272,58 +273,81 @@ static void mxs_dma_tasklet(unsigned long data)
272 mxs_chan->desc.callback(mxs_chan->desc.callback_param); 273 mxs_chan->desc.callback(mxs_chan->desc.callback_param);
273} 274}
274 275
276static int mxs_dma_irq_to_chan(struct mxs_dma_engine *mxs_dma, int irq)
277{
278 int i;
279
280 for (i = 0; i != mxs_dma->nr_channels; ++i)
281 if (mxs_dma->mxs_chans[i].chan_irq == irq)
282 return i;
283
284 return -EINVAL;
285}
286
275static irqreturn_t mxs_dma_int_handler(int irq, void *dev_id) 287static irqreturn_t mxs_dma_int_handler(int irq, void *dev_id)
276{ 288{
277 struct mxs_dma_engine *mxs_dma = dev_id; 289 struct mxs_dma_engine *mxs_dma = dev_id;
278 u32 stat1, stat2; 290 struct mxs_dma_chan *mxs_chan;
291 u32 completed;
292 u32 err;
293 int chan = mxs_dma_irq_to_chan(mxs_dma, irq);
294
295 if (chan < 0)
296 return IRQ_NONE;
279 297
280 /* completion status */ 298 /* completion status */
281 stat1 = readl(mxs_dma->base + HW_APBHX_CTRL1); 299 completed = readl(mxs_dma->base + HW_APBHX_CTRL1);
282 stat1 &= MXS_DMA_CHANNELS_MASK; 300 completed = (completed >> chan) & 0x1;
283 writel(stat1, mxs_dma->base + HW_APBHX_CTRL1 + STMP_OFFSET_REG_CLR); 301
302 /* Clear interrupt */
303 writel((1 << chan),
304 mxs_dma->base + HW_APBHX_CTRL1 + STMP_OFFSET_REG_CLR);
284 305
285 /* error status */ 306 /* error status */
286 stat2 = readl(mxs_dma->base + HW_APBHX_CTRL2); 307 err = readl(mxs_dma->base + HW_APBHX_CTRL2);
287 writel(stat2, mxs_dma->base + HW_APBHX_CTRL2 + STMP_OFFSET_REG_CLR); 308 err &= (1 << (MXS_DMA_CHANNELS + chan)) | (1 << chan);
309
310 /*
311 * error status bit is in the upper 16 bits, error irq bit in the lower
312 * 16 bits. We transform it into a simpler error code:
313 * err: 0x00 = no error, 0x01 = TERMINATION, 0x02 = BUS_ERROR
314 */
315 err = (err >> (MXS_DMA_CHANNELS + chan)) + (err >> chan);
316
317 /* Clear error irq */
318 writel((1 << chan),
319 mxs_dma->base + HW_APBHX_CTRL2 + STMP_OFFSET_REG_CLR);
288 320
289 /* 321 /*
290 * When both completion and error of termination bits set at the 322 * When both completion and error of termination bits set at the
291 * same time, we do not take it as an error. IOW, it only becomes 323 * same time, we do not take it as an error. IOW, it only becomes
292 * an error we need to handle here in case of either it's (1) a bus 324 * an error we need to handle here in case of either it's a bus
293 * error or (2) a termination error with no completion. 325 * error or a termination error with no completion. 0x01 is termination
326 * error, so we can subtract err & completed to get the real error case.
294 */ 327 */
295 stat2 = ((stat2 >> MXS_DMA_CHANNELS) & stat2) | /* (1) */ 328 err -= err & completed;
296 (~(stat2 >> MXS_DMA_CHANNELS) & stat2 & ~stat1); /* (2) */
297
298 /* combine error and completion status for checking */
299 stat1 = (stat2 << MXS_DMA_CHANNELS) | stat1;
300 while (stat1) {
301 int channel = fls(stat1) - 1;
302 struct mxs_dma_chan *mxs_chan =
303 &mxs_dma->mxs_chans[channel % MXS_DMA_CHANNELS];
304
305 if (channel >= MXS_DMA_CHANNELS) {
306 dev_dbg(mxs_dma->dma_device.dev,
307 "%s: error in channel %d\n", __func__,
308 channel - MXS_DMA_CHANNELS);
309 mxs_chan->status = DMA_ERROR;
310 mxs_dma_reset_chan(mxs_chan);
311 } else {
312 if (mxs_chan->flags & MXS_DMA_SG_LOOP)
313 mxs_chan->status = DMA_IN_PROGRESS;
314 else
315 mxs_chan->status = DMA_COMPLETE;
316 }
317 329
318 stat1 &= ~(1 << channel); 330 mxs_chan = &mxs_dma->mxs_chans[chan];
319 331
320 if (mxs_chan->status == DMA_COMPLETE) 332 if (err) {
321 dma_cookie_complete(&mxs_chan->desc); 333 dev_dbg(mxs_dma->dma_device.dev,
322 334 "%s: error in channel %d\n", __func__,
323 /* schedule tasklet on this channel */ 335 chan);
324 tasklet_schedule(&mxs_chan->tasklet); 336 mxs_chan->status = DMA_ERROR;
337 mxs_dma_reset_chan(mxs_chan);
338 } else {
339 if (mxs_chan->flags & MXS_DMA_SG_LOOP)
340 mxs_chan->status = DMA_IN_PROGRESS;
341 else
342 mxs_chan->status = DMA_COMPLETE;
325 } 343 }
326 344
345 if (mxs_chan->status == DMA_COMPLETE)
346 dma_cookie_complete(&mxs_chan->desc);
347
348 /* schedule tasklet on this channel */
349 tasklet_schedule(&mxs_chan->tasklet);
350
327 return IRQ_HANDLED; 351 return IRQ_HANDLED;
328} 352}
329 353