---
 drivers/spi/atmel_spi.c |  140 ++++++++++++++++++++++++++++++++++--------------
 1 file changed, 100 insertions(+), 40 deletions(-)

Index: linux-2.6.18-avr32/drivers/spi/atmel_spi.c
===================================================================
--- linux-2.6.18-avr32.orig/drivers/spi/atmel_spi.c	2007-01-15 15:35:38.000000000 +0100
+++ linux-2.6.18-avr32/drivers/spi/atmel_spi.c	2007-01-16 13:26:32.000000000 +0100
@@ -156,7 +156,7 @@ static void atmel_spi_next_xfer(struct s
 	 */
 	spi_writel(as, TNCR, 0);
 	spi_writel(as, RNCR, 0);
-	imr = SPI_BIT(ENDRX);
+	imr = SPI_BIT(ENDRX) | SPI_BIT(OVRES);
 
 	dev_dbg(&msg->spi->dev,
 		"start xfer %p: len %u tx %p/%08x rx %p/%08x imr %08x\n",
@@ -209,6 +209,43 @@ static void atmel_spi_dma_map_xfer(struc
 	}
 }
 
+static void atmel_spi_dma_unmap_xfer(struct spi_master *master,
+				     struct spi_transfer *xfer)
+{
+	if (xfer->tx_dma != INVALID_DMA_ADDRESS)
+		dma_unmap_single(master->cdev.dev, xfer->tx_dma,
+				 xfer->len, DMA_TO_DEVICE);
+	if (xfer->rx_dma != INVALID_DMA_ADDRESS)
+		dma_unmap_single(master->cdev.dev, xfer->rx_dma,
+				 xfer->len, DMA_FROM_DEVICE);
+}
+
+static void atmel_spi_msg_done(struct spi_master *master,
+			       struct atmel_spi *as,
+			       struct spi_message *msg,
+			       int status)
+{
+	cs_deactivate(msg->spi);
+	list_del(&msg->queue);
+	msg->status = status;
+
+	dev_dbg(master->cdev.dev,
+		"xfer complete: %u bytes transferred\n",
+		msg->actual_length);
+
+	spin_unlock(&as->lock);
+	msg->complete(msg->context);
+	spin_lock(&as->lock);
+
+	as->current_transfer = NULL;
+
+	/* continue; complete() may have queued requests */
+	if (list_empty(&as->queue) || as->stopping)
+		spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
+	else
+		atmel_spi_next_message(master);
+}
+
 static irqreturn_t
 atmel_spi_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
@@ -219,19 +256,71 @@ atmel_spi_interrupt(int irq, void *dev_i
 	u32			status, pending, imr;
 	int			ret = IRQ_NONE;
 
+	spin_lock(&as->lock);
+
+	xfer = as->current_transfer;
+	msg = list_entry(as->queue.next, struct spi_message, queue);
+
 	imr = spi_readl(as, IMR);
 	status = spi_readl(as, SR);
 	pending = status & imr;
 pr_debug("spi irq: stat %05x imr %05x pend %05x\n", status, imr, pending);
 
-	if (pending & (SPI_BIT(ENDTX) | SPI_BIT(ENDRX))) {
+	if (pending & SPI_BIT(OVRES)) {
+		int timeout;
+
 		ret = IRQ_HANDLED;
 
-		spi_writel(as, IDR, pending);
-		spin_lock(&as->lock);
+		spi_writel(as, IDR, (SPI_BIT(ENDTX) | SPI_BIT(ENDRX)
+				     | SPI_BIT(OVRES)));
+
+		/*
+		 * When we get an overrun, we disregard the current
+		 * transfer. Data will not be copied back from any
+		 * bounce buffer and msg->actual_len will not be
+		 * updated with the last xfer.
+		 *
+		 * We will also not process any remaning transfers in
+		 * the message.
+		 *
+		 * First, stop the transfer and unmap the DMA buffers.
+		 */
+		spi_writel(as, PTCR, SPI_BIT(RXTDIS) | SPI_BIT(TXTDIS));
+		if (!msg->is_dma_mapped)
+			atmel_spi_dma_unmap_xfer(master, xfer);
+
+		/* REVISIT: udelay in irq is unfriendly */
+		if (xfer->delay_usecs)
+			udelay(xfer->delay_usecs);
 
-		xfer = as->current_transfer;
-		msg = list_entry(as->queue.next, struct spi_message, queue);
+		dev_warn(master->cdev.dev, "fifo overrun (%u/%u remaining)\n",
+			 spi_readl(as, TCR), spi_readl(as, RCR));
+
+		/*
+		 * Clean up DMA registers and make sure the data
+		 * registers are empty.
+		 */
+		spi_writel(as, RNCR, 0);
+		spi_writel(as, TNCR, 0);
+		spi_writel(as, RCR, 0);
+		spi_writel(as, TCR, 0);
+		for (timeout = 1000; timeout; timeout--)
+			if (spi_readl(as, SR) & SPI_BIT(TXEMPTY))
+				break;
+		if (!timeout)
+			dev_warn(master->cdev.dev,
+				 "timeout waiting for TXEMPTY");
+		while (spi_readl(as, SR) & SPI_BIT(RDRF))
+			spi_readl(as, RDR);
+
+		/* Clear any overrun happening while cleaning up */
+		spi_readl(as, SR);
+
+		atmel_spi_msg_done(master, as, msg, -EIO);
+	} else if (pending & (SPI_BIT(ENDTX) | SPI_BIT(ENDRX))) {
+		ret = IRQ_HANDLED;
+
+		spi_writel(as, IDR, pending);
 
 		/*
 		 * If the rx buffer wasn't aligned, we used a bounce
@@ -254,46 +343,16 @@ pr_debug("spi irq: stat %05x imr %05x pe
 		if (as->remaining_bytes == 0) {
 			msg->actual_length += xfer->len;
 
-			if (!msg->is_dma_mapped) {
-				if (xfer->tx_dma != INVALID_DMA_ADDRESS)
-					dma_unmap_single(master->cdev.dev,
-							 xfer->tx_dma,
-							 xfer->len,
-							 DMA_TO_DEVICE);
-				if (xfer->rx_dma != INVALID_DMA_ADDRESS)
-					dma_unmap_single(master->cdev.dev,
-							 xfer->rx_dma,
-							 xfer->len,
-							 DMA_FROM_DEVICE);
-			}
+			if (!msg->is_dma_mapped)
+				atmel_spi_dma_unmap_xfer(master, xfer);
 
 			/* REVISIT: udelay in irq is unfriendly */
 			if (xfer->delay_usecs)
 				udelay(xfer->delay_usecs);
 
 			if (msg->transfers.prev == &xfer->transfer_list) {
-
 				/* report completed message */
-				cs_deactivate(msg->spi);
-				list_del(&msg->queue);
-				msg->status = 0;
-
-				dev_dbg(master->cdev.dev,
-					"xfer complete: %u bytes transferred\n",
-					msg->actual_length);
-
-				spin_unlock(&as->lock);
-				msg->complete(msg->context);
-				spin_lock(&as->lock);
-
-				as->current_transfer = NULL;
-
-				/* continue; complete() may have queued requests */
-				if (list_empty(&as->queue) || as->stopping)
-					spi_writel(as, PTCR, SPI_BIT(RXTDIS)
-						   | SPI_BIT(TXTDIS));
-				else
-					atmel_spi_next_message(master);
+				atmel_spi_msg_done(master, as, msg, 0);
 			} else {
 				if (xfer->cs_change) {
 					cs_deactivate(msg->spi);
@@ -315,9 +374,10 @@ pr_debug("spi irq: stat %05x imr %05x pe
 			 */
 			atmel_spi_next_xfer(master, msg);
 		}
-		spin_unlock(&as->lock);
 	}
 
+	spin_unlock(&as->lock);
+
 	return ret;
 }