From fa3e686a34f4c33de31a128cc36def0b466bfe1a Mon Sep 17 00:00:00 2001
From: Pavel Pisa <ppisa@pikron.com>
Date: Thu, 22 Jun 2006 22:21:03 +0100
Subject: [PATCH] [ARM] 3601/1: i.MX/MX1 DMA error handling for signaled
 channels only

Patch from Pavel Pisa

There has been bug, that dma_err_handler() touches even
channels not signaling error condition.

Problem noticed by Andrea Paterniani.

Signed-off-by: Pavel Pisa <pisa@cmp.felk.cvut.cz>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
---
 arch/arm/mach-imx/dma.c            | 65 ++++++++++++++++++------------
 include/asm-arm/arch-imx/imx-dma.h |  8 +++-
 2 files changed, 45 insertions(+), 28 deletions(-)

diff --git a/arch/arm/mach-imx/dma.c b/arch/arm/mach-imx/dma.c
index 4ca51dcf13a..36578871ecc 100644
--- a/arch/arm/mach-imx/dma.c
+++ b/arch/arm/mach-imx/dma.c
@@ -15,6 +15,9 @@
  *             Changed to support scatter gather DMA
  *             by taking Russell's code from RiscPC
  *
+ *  2006-05-31 Pavel Pisa <pisa@cmp.felk.cvut.cz>
+ *             Corrected error handling code.
+ *
  */
 
 #undef DEBUG
@@ -277,7 +280,7 @@ imx_dma_setup_sg(imx_dmach_t dma_ch,
 int
 imx_dma_setup_handlers(imx_dmach_t dma_ch,
 		       void (*irq_handler) (int, void *, struct pt_regs *),
-		       void (*err_handler) (int, void *, struct pt_regs *),
+		       void (*err_handler) (int, void *, struct pt_regs *, int),
 		       void *data)
 {
 	struct imx_dma_channel *imxdma = &imx_dma_channels[dma_ch];
@@ -463,43 +466,53 @@ static irqreturn_t dma_err_handler(int irq, void *dev_id, struct pt_regs *regs)
 	int i, disr = DISR;
 	struct imx_dma_channel *channel;
 	unsigned int err_mask = DBTOSR | DRTOSR | DSESR | DBOSR;
+	int errcode;
 
-	DISR = disr;
+	DISR = disr & err_mask;
 	for (i = 0; i < IMX_DMA_CHANNELS; i++) {
-		channel = &imx_dma_channels[i];
-
-		if ((err_mask & 1 << i) && channel->name
-		    && channel->err_handler) {
-			channel->err_handler(i, channel->data, regs);
+		if(!(err_mask & (1 << i)))
 			continue;
-		}
-
-		imx_dma_channels[i].sg = NULL;
+		channel = &imx_dma_channels[i];
+		errcode = 0;
 
 		if (DBTOSR & (1 << i)) {
-			printk(KERN_WARNING
-			       "Burst timeout on channel %d (%s)\n",
-			       i, channel->name);
-			DBTOSR |= (1 << i);
+			DBTOSR = (1 << i);
+			errcode |= IMX_DMA_ERR_BURST;
 		}
 		if (DRTOSR & (1 << i)) {
-			printk(KERN_WARNING
-			       "Request timeout on channel %d (%s)\n",
-			       i, channel->name);
-			DRTOSR |= (1 << i);
+			DRTOSR = (1 << i);
+			errcode |= IMX_DMA_ERR_REQUEST;
 		}
 		if (DSESR & (1 << i)) {
-			printk(KERN_WARNING
-			       "Transfer timeout on channel %d (%s)\n",
-			       i, channel->name);
-			DSESR |= (1 << i);
+			DSESR = (1 << i);
+			errcode |= IMX_DMA_ERR_TRANSFER;
 		}
 		if (DBOSR & (1 << i)) {
-			printk(KERN_WARNING
-			       "Buffer overflow timeout on channel %d (%s)\n",
-			       i, channel->name);
-			DBOSR |= (1 << i);
+			DBOSR = (1 << i);
+			errcode |= IMX_DMA_ERR_BUFFER;
 		}
+
+		/*
+		 * The cleaning of @sg field would be questionable
+		 * there, because its value can help to compute
+		 * remaining/transfered bytes count in the handler
+		 */
+		/*imx_dma_channels[i].sg = NULL;*/
+
+		if (channel->name && channel->err_handler) {
+			channel->err_handler(i, channel->data, regs, errcode);
+			continue;
+		}
+
+		imx_dma_channels[i].sg = NULL;
+
+		printk(KERN_WARNING
+		       "DMA timeout on channel %d (%s) -%s%s%s%s\n",
+		       i, channel->name,
+		       errcode&IMX_DMA_ERR_BURST?    " burst":"",
+		       errcode&IMX_DMA_ERR_REQUEST?  " request":"",
+		       errcode&IMX_DMA_ERR_TRANSFER? " transfer":"",
+		       errcode&IMX_DMA_ERR_BUFFER?   " buffer":"");
 	}
 	return IRQ_HANDLED;
 }
diff --git a/include/asm-arm/arch-imx/imx-dma.h b/include/asm-arm/arch-imx/imx-dma.h
index f2063c1d610..599f03e5a9e 100644
--- a/include/asm-arm/arch-imx/imx-dma.h
+++ b/include/asm-arm/arch-imx/imx-dma.h
@@ -46,7 +46,7 @@
 struct imx_dma_channel {
 	const char *name;
 	void (*irq_handler) (int, void *, struct pt_regs *);
-	void (*err_handler) (int, void *, struct pt_regs *);
+	void (*err_handler) (int, void *, struct pt_regs *, int errcode);
 	void *data;
 	dmamode_t  dma_mode;
 	struct scatterlist *sg;
@@ -58,6 +58,10 @@ struct imx_dma_channel {
 
 extern struct imx_dma_channel imx_dma_channels[IMX_DMA_CHANNELS];
 
+#define IMX_DMA_ERR_BURST     1
+#define IMX_DMA_ERR_REQUEST   2
+#define IMX_DMA_ERR_TRANSFER  4
+#define IMX_DMA_ERR_BUFFER    8
 
 /* The type to distinguish channel numbers parameter from ordinal int type */
 typedef int imx_dmach_t;
@@ -74,7 +78,7 @@ imx_dma_setup_sg(imx_dmach_t dma_ch,
 int
 imx_dma_setup_handlers(imx_dmach_t dma_ch,
 		void (*irq_handler) (int, void *, struct pt_regs *),
-		void (*err_handler) (int, void *, struct pt_regs *), void *data);
+		void (*err_handler) (int, void *, struct pt_regs *, int), void *data);
 
 void imx_dma_enable(imx_dmach_t dma_ch);
 
-- 
2.41.3