linux-rt-users.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH]: Atmel Serial Console interrupt handler splitup
@ 2007-12-07 15:24 Remy Bohmer
  2007-12-07 18:31 ` David Brownell
                   ` (2 more replies)
  0 siblings, 3 replies; 20+ messages in thread
From: Remy Bohmer @ 2007-12-07 15:24 UTC (permalink / raw)
  To: Andrew Victor; +Cc: RT, ARM Linux Mailing List, Steven Rostedt, David Brownell

[-- Attachment #1: Type: text/plain, Size: 2843 bytes --]

Hello Andrew,

Attached I have put 3 patches for the AT91-series for the serial port
driver on AT91.
The interrupt handler of the serial console is quite long, and if
Preempt-RT is used on AT91 the interrupt handler is even doing illegal
things. These illegal things are related to the DBGU port of which the
interrupt handler is shared with the system_interrupt which
automagically runs in IRQF_NODELAY context due to the timer interrupt.
The old interrupt handler calls some kernel interfaces that can block
on a mutex.

To solve this, I have split up the interrupt handler in a IRQF_NODELAY
safe/unsafe part, with as result that the code running in real
interrupt context is shortened a lot (the tty driver part is now
called from a tasklet). On AT91 David Brownell noticed several months
ago that the DBGU can miss some characters on NO_HZ. I would expect
that this would be better now due to the shorter interrupt handler,
although it was not my goal to solve it with these patches. (David can
you verify if this is better now?)

So, I have here 3 patches:
* atmel_serial_cleanup -> This patch adapts the driver to the coding
rules, splits the interrupt handler into 3 routines (cuts the routine
in more readable pieces) , but there is no functional change involved.
* atmel_serial_irq_splitup -> This patch splits up the interrupt handler.
* atmel_serial_irqf_nodelay -> This patch is additionally required to
get it properly working on Preempt-RT. (This patch should thus go into
the RT-patch, AFTER integration of the other 2 patches into mainline)

BUT: I based the patch on the 2.6.23.1 + your patch collection at
http://maxim.org.za/AT91RM9200/2.6/2.6.23-at91.patch.gz

I did this because this driver conflicts with the DMA(PDC) patches
that are in the patchset on  maxim.org.za. I found out that these DMA
patches are still not integrated into mainline, although it is in your
patchset for several kernel releases. I can make a series based on
kernel mainline, but that would harden the integration for you in your
patchset.
The patchset itself is not dependant on the DMA changes, so I can
split it up, but the DMA changes itself are quite heavy.

But there is also a relation with Preempt-RT. To get the patch in
preempt RT the other patches has to be in mainline, so things are
stacking up now.

What is wise here? should I create a new patchset for mainline? Or can
you push the DMA patch also to mainline together with this set? I have
it working here for months, so I see no reason not to, maybe you have
a reason?

I tested it on AT91rm9200-EK (+proprietary boards) + AT91SAM9261-EK,
in combination with: 2.6.23.1 and 2.6.23.1-rt5 up to serial speed to
115200 (also with 99% CPU load on prio 99 on RT, no missing characters
detected.)

Note: Preempt-RT CANNOT run without these patches on AT91.


Kind Regards,

Remy Bohmer

[-- Attachment #2: patch_atmel_serial_cleanup --]
[-- Type: application/octet-stream, Size: 31627 bytes --]

This patch cleans up the atmel_serial driver to conform the coding rules.

This patch demands the patch http://maxim.org.za/AT91RM9200/2.6/2.6.23-at91.patch.gz
to be installed first.

Signed-off-by: Remy Bohmer <linux@bohmer.net>
---
 drivers/serial/atmel_serial.c |  459 ++++++++++++++++++++++++------------------
 1 file changed, 263 insertions(+), 196 deletions(-)

Index: linux-2.6.23/drivers/serial/atmel_serial.c
===================================================================
--- linux-2.6.23.orig/drivers/serial/atmel_serial.c	2007-12-05 14:48:28.000000000 +0100
+++ linux-2.6.23/drivers/serial/atmel_serial.c	2007-12-05 15:08:24.000000000 +0100
@@ -53,7 +53,7 @@
 #define SUPPORT_PDC
 #define PDC_BUFFER_SIZE		(L1_CACHE_BYTES << 3)
 #warning "Revisit"
-#define PDC_RX_TIMEOUT		(3 * 10)		/* 3 bytes */
+#define PDC_RX_TIMEOUT		(3 * 10)	/* 3 bytes */
 
 #if defined(CONFIG_SERIAL_ATMEL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ)
 #define SUPPORT_SYSRQ
@@ -82,44 +82,50 @@
 
 #define ATMEL_ISR_PASS_LIMIT	256
 
-#define UART_PUT_CR(port,v)	__raw_writel(v, (port)->membase + ATMEL_US_CR)
+#define UART_PUT_CR(port, v)	__raw_writel(v, (port)->membase + ATMEL_US_CR)
 #define UART_GET_MR(port)	__raw_readl((port)->membase + ATMEL_US_MR)
-#define UART_PUT_MR(port,v)	__raw_writel(v, (port)->membase + ATMEL_US_MR)
-#define UART_PUT_IER(port,v)	__raw_writel(v, (port)->membase + ATMEL_US_IER)
-#define UART_PUT_IDR(port,v)	__raw_writel(v, (port)->membase + ATMEL_US_IDR)
+#define UART_PUT_MR(port, v)	__raw_writel(v, (port)->membase + ATMEL_US_MR)
+#define UART_PUT_IER(port, v)	__raw_writel(v, (port)->membase + ATMEL_US_IER)
+#define UART_PUT_IDR(port, v)	__raw_writel(v, (port)->membase + ATMEL_US_IDR)
 #define UART_GET_IMR(port)	__raw_readl((port)->membase + ATMEL_US_IMR)
 #define UART_GET_CSR(port)	__raw_readl((port)->membase + ATMEL_US_CSR)
 #define UART_GET_CHAR(port)	__raw_readl((port)->membase + ATMEL_US_RHR)
-#define UART_PUT_CHAR(port,v)	__raw_writel(v, (port)->membase + ATMEL_US_THR)
+#define UART_PUT_CHAR(port, v)	__raw_writel(v, (port)->membase + ATMEL_US_THR)
 #define UART_GET_BRGR(port)	__raw_readl((port)->membase + ATMEL_US_BRGR)
-#define UART_PUT_BRGR(port,v)	__raw_writel(v, (port)->membase + ATMEL_US_BRGR)
-#define UART_PUT_RTOR(port,v)	__raw_writel(v, (port)->membase + ATMEL_US_RTOR)
+#define UART_PUT_BRGR(port, v)	__raw_writel(v, (port)->membase + ATMEL_US_BRGR)
+#define UART_PUT_RTOR(port, v)	__raw_writel(v, (port)->membase + ATMEL_US_RTOR)
 
-// #define UART_GET_CR(port)	__raw_readl((port)->membase + ATMEL_US_CR)		// is write-only
+/* write-only: */
+/* #define UART_GET_CR(port)    __raw_readl((port)->membase + ATMEL_US_CR) */
 
  /* PDC registers */
-#define UART_PUT_PTCR(port,v)	__raw_writel(v, (port)->membase + ATMEL_PDC_PTCR)
+#define UART_PUT_PTCR(port, v)	__raw_writel(v, \
+					     (port)->membase + ATMEL_PDC_PTCR)
 #define UART_GET_PTSR(port)	__raw_readl((port)->membase + ATMEL_PDC_PTSR)
 
-#define UART_PUT_RPR(port,v)	__raw_writel(v, (port)->membase + ATMEL_PDC_RPR)
+#define UART_PUT_RPR(port, v)	__raw_writel(v, (port)->membase + ATMEL_PDC_RPR)
 #define UART_GET_RPR(port)	__raw_readl((port)->membase + ATMEL_PDC_RPR)
-#define UART_PUT_RCR(port,v)	__raw_writel(v, (port)->membase + ATMEL_PDC_RCR)
-#define UART_PUT_RNPR(port,v)	__raw_writel(v, (port)->membase + ATMEL_PDC_RNPR)
-#define UART_PUT_RNCR(port,v)	__raw_writel(v, (port)->membase + ATMEL_PDC_RNCR)
-
-#define UART_PUT_TPR(port,v)	__raw_writel(v, (port)->membase + ATMEL_PDC_TPR)
-#define UART_PUT_TCR(port,v)	__raw_writel(v, (port)->membase + ATMEL_PDC_TCR)
-//#define UART_PUT_TNPR(port,v)	__raw_writel(v, (port)->membase + ATMEL_PDC_TNPR)
-//#define UART_PUT_TNCR(port,v)	__raw_writel(v, (port)->membase + ATMEL_PDC_TNCR)
+#define UART_PUT_RCR(port, v)	__raw_writel(v, (port)->membase + ATMEL_PDC_RCR)
+#define UART_PUT_RNPR(port, v)	__raw_writel(v, \
+					       (port)->membase + ATMEL_PDC_RNPR)
+#define UART_PUT_RNCR(port, v)	__raw_writel(v, \
+					       (port)->membase + ATMEL_PDC_RNCR)
+
+#define UART_PUT_TPR(port, v)	__raw_writel(v, (port)->membase + ATMEL_PDC_TPR)
+#define UART_PUT_TCR(port, v)	__raw_writel(v, (port)->membase + ATMEL_PDC_TCR)
+/*#define UART_PUT_TNPR(port, v) __raw_writel(v, \
+					    (port)->membase + ATMEL_PDC_TNPR)*/
+/*#define UART_PUT_TNCR(port, v) __raw_writel(v, \
+					    (port)->membase + ATMEL_PDC_TNCR)*/
 
-static int (*atmel_open_hook)(struct uart_port *);
-static void (*atmel_close_hook)(struct uart_port *);
+static int (*atmel_open_hook) (struct uart_port *);
+static void (*atmel_close_hook) (struct uart_port *);
 
 struct atmel_dma_buffer {
-	unsigned char	*buf;
-	dma_addr_t	dma_addr;
-	size_t		dma_size;
-	unsigned int	ofs;
+	unsigned char   *buf;
+	dma_addr_t      dma_addr;
+	size_t          dma_size;
+	unsigned        int ofs;
 };
 
 /*
@@ -127,16 +133,16 @@ struct atmel_dma_buffer {
  */
 struct atmel_uart_port {
 	struct uart_port	uart;		/* uart */
-	struct clk		*clk;		/* uart clock */
-	unsigned short		suspended;	/* is port suspended? */
-	int			break_active;	/* break being received */
-
-	short			use_dma_rx;	/* enable PDC receiver */
-	short			pdc_rx_idx;	/* current PDC RX buffer */
-	struct atmel_dma_buffer	pdc_rx[2];	/* PDC receier */
+	struct clk 		*clk;		/* uart clock */
+	unsigned short 		suspended;	/* is port suspended? */
+	int 			break_active;	/* break being received */
+
+	short 			use_dma_rx;	/* enable PDC receiver */
+	short 			pdc_rx_idx;	/* current PDC RX buffer */
+	struct atmel_dma_buffer pdc_rx[2];	/* PDC receiver */
 
-	short			use_dma_tx;	/* enable PDC transmitter */
-	struct atmel_dma_buffer	pdc_tx;		/* PDC transmitter */
+	short 			use_dma_tx;	/* enable PDC transmitter */
+	struct atmel_dma_buffer pdc_tx;		/* PDC transmitter */
 };
 
 static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART];
@@ -167,8 +173,8 @@ static void atmel_set_mctrl(struct uart_
 #ifdef CONFIG_ARCH_AT91RM9200
 	if (cpu_is_at91rm9200()) {
 		/*
-		 * AT91RM9200 Errata #39: RTS0 is not internally connected to PA21.
-		 *  We need to drive the pin manually.
+		 * AT91RM9200 Errata #39: RTS0 is not internally
+		 * connected to PA21. We need to drive the pin manually.
 		 */
 		if (port->mapbase == AT91RM9200_BASE_US0) {
 			if (mctrl & TIOCM_RTS)
@@ -229,13 +235,13 @@ static u_int atmel_get_mctrl(struct uart
  */
 static void atmel_stop_tx(struct uart_port *port)
 {
-	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
 
 	if (atmel_port->use_dma_tx) {
-		UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);		/* disable PDC transmit */
+		/* disable PDC transmit */
+		UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);
 		UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
-	}
-	else
+	} else
 		UART_PUT_IDR(port, ATMEL_US_TXRDY);
 }
 
@@ -244,18 +250,18 @@ static void atmel_stop_tx(struct uart_po
  */
 static void atmel_start_tx(struct uart_port *port)
 {
-	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
 
 	if (atmel_port->use_dma_tx) {
 		if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN)
 			/* The transmitter is already running.  Yes, we
-			   really need this.*/
+			   really need this. */
 			return;
 
 		UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
-		UART_PUT_PTCR(port, ATMEL_PDC_TXTEN);		/* re-enable PDC transmit */
-	}
-	else
+		/* re-enable PDC transmit */
+		UART_PUT_PTCR(port, ATMEL_PDC_TXTEN);
+	} else
 		UART_PUT_IER(port, ATMEL_US_TXRDY);
 }
 
@@ -264,13 +270,13 @@ static void atmel_start_tx(struct uart_p
  */
 static void atmel_stop_rx(struct uart_port *port)
 {
-	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
 
 	if (atmel_port->use_dma_rx) {
-		UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS);		/* disable PDC receive */
+		/* disable PDC receive */
+		UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS);
 		UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT);
-	}
-	else
+	} else
 		UART_PUT_IDR(port, ATMEL_US_RXRDY);
 }
 
@@ -279,7 +285,9 @@ static void atmel_stop_rx(struct uart_po
  */
 static void atmel_enable_ms(struct uart_port *port)
 {
-	UART_PUT_IER(port, ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC | ATMEL_US_CTSIC);
+	UART_PUT_IER(port,
+		     ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC |
+		     ATMEL_US_CTSIC);
 }
 
 /*
@@ -298,14 +306,15 @@ static void atmel_break_ctl(struct uart_
  */
 static void atmel_pdc_endrx(struct uart_port *port)
 {
-	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
 	struct tty_struct *tty = port->info->tty;
 	struct atmel_dma_buffer *pdc = PDC_RX_BUF(atmel_port);
 	unsigned int count;
 
 	count = pdc->dma_size - pdc->ofs;
 	if (likely(count > 0)) {
-		dma_sync_single_for_cpu(port->dev, pdc->dma_addr, pdc->dma_size, DMA_FROM_DEVICE);
+		dma_sync_single_for_cpu(port->dev, pdc->dma_addr,
+					pdc->dma_size, DMA_FROM_DEVICE);
 		tty_insert_flip_string(tty, pdc->buf + pdc->ofs, count);
 		tty_flip_buffer_push(tty);
 
@@ -318,7 +327,7 @@ static void atmel_pdc_endrx(struct uart_
 	UART_PUT_RNCR(port, pdc->dma_size);
 
 	/* Switch to next buffer */
-	PDC_RX_SWITCH(atmel_port);		/* next PDC buffer */
+	PDC_RX_SWITCH(atmel_port);	/* next PDC buffer */
 }
 
 /*
@@ -327,16 +336,17 @@ static void atmel_pdc_endrx(struct uart_
  */
 static void atmel_pdc_timeout(struct uart_port *port)
 {
-	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
 	struct tty_struct *tty = port->info->tty;
 	struct atmel_dma_buffer *pdc = PDC_RX_BUF(atmel_port);
 	/* unsigned */ int ofs, count;
 
-	ofs = UART_GET_RPR(port) - pdc->dma_addr;	/* current DMA adress */
+	ofs = UART_GET_RPR(port) - pdc->dma_addr; /* current DMA adress */
 	count = ofs - pdc->ofs;
 
 	if (likely(count > 0)) {
-		dma_sync_single_for_cpu(port->dev, pdc->dma_addr, pdc->dma_size, DMA_FROM_DEVICE);
+		dma_sync_single_for_cpu(port->dev, pdc->dma_addr, pdc->dma_size,
+					DMA_FROM_DEVICE);
 		tty_insert_flip_string(tty, pdc->buf + pdc->ofs, count);
 		tty_flip_buffer_push(tty);
 
@@ -357,7 +367,8 @@ static void atmel_pdc_rxerr(struct uart_
 	UART_PUT_CR(port, ATMEL_US_RSTSTA);
 
 	if (status & ATMEL_US_RXBRK) {
-		status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME);	/* ignore side-effect */
+		/* ignore side-effect */
+		status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME);
 		port->icount.brk++;
 	}
 	if (status & ATMEL_US_PARE)
@@ -373,7 +384,7 @@ static void atmel_pdc_rxerr(struct uart_
  */
 static void atmel_pdc_endtx(struct uart_port *port)
 {
-	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
 	struct circ_buf *xmit = &port->info->xmit;
 	struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
 
@@ -394,15 +405,16 @@ static void atmel_pdc_endtx(struct uart_
  */
 static void atmel_pdc_txbufe(struct uart_port *port)
 {
-	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
 	struct circ_buf *xmit = &port->info->xmit;
 	struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
 	int count;
 
 	if (!uart_circ_empty(xmit)) {
 		/* more to transmit - setup next transfer */
-		UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);			/* disable PDC transmit */
-		dma_sync_single_for_device(port->dev, pdc->dma_addr, pdc->dma_size, DMA_TO_DEVICE);
+		UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);/* disable PDC transmit */
+		dma_sync_single_for_device(port->dev, pdc->dma_addr,
+					   pdc->dma_size, DMA_TO_DEVICE);
 
 		if (xmit->tail < xmit->head)
 			count = xmit->head - xmit->tail;
@@ -412,11 +424,11 @@ static void atmel_pdc_txbufe(struct uart
 
 		UART_PUT_TPR(port, pdc->dma_addr + xmit->tail);
 		UART_PUT_TCR(port, count);
-		UART_PUT_PTCR(port, ATMEL_PDC_TXTEN);			/* re-enable PDC transmit */
-	}
-	else {
+		/* re-enable PDC transmit */
+		UART_PUT_PTCR(port, ATMEL_PDC_TXTEN);
+	} else {
 		/* nothing left to transmit - disable the transmitter */
-		UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);			/* disable PDC transmit */
+		UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS);/* disable PDC transmit */
 		UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE);
 	}
 }
@@ -426,7 +438,7 @@ static void atmel_pdc_txbufe(struct uart
  */
 static void atmel_rx_chars(struct uart_port *port)
 {
-	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
 	struct tty_struct *tty = port->info->tty;
 	unsigned int status, ch, flg;
 
@@ -445,10 +457,11 @@ static void atmel_rx_chars(struct uart_p
 		if (unlikely(status & (ATMEL_US_PARE | ATMEL_US_FRAME
 				       | ATMEL_US_OVRE | ATMEL_US_RXBRK)
 			     || atmel_port->break_active)) {
-			UART_PUT_CR(port, ATMEL_US_RSTSTA);	/* clear error */
+			UART_PUT_CR(port, ATMEL_US_RSTSTA); /* clear error */
 			if (status & ATMEL_US_RXBRK
 			    && !atmel_port->break_active) {
-				status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME);	/* ignore side-effect */
+				/* ignore side-effect */
+				status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME);
 				port->icount.brk++;
 				atmel_port->break_active = 1;
 				UART_PUT_IER(port, ATMEL_US_RXBRK);
@@ -529,58 +542,88 @@ static void atmel_tx_chars(struct uart_p
 }
 
 /*
+ * receive interrupt handler.
+ */
+static void atmel_handle_receive(struct uart_port *port, unsigned int pending)
+{
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
+
+	/* PDC receive */
+	if (pending & ATMEL_US_ENDRX)
+		atmel_pdc_endrx(port);
+	if (pending & ATMEL_US_TIMEOUT)
+		atmel_pdc_timeout(port);
+	if (atmel_port->use_dma_rx
+	    && pending & (ATMEL_US_RXBRK | ATMEL_US_OVRE | ATMEL_US_FRAME |
+			  ATMEL_US_PARE))
+		atmel_pdc_rxerr(port, pending);
+
+	/* Interrupt receive */
+	if (pending & ATMEL_US_RXRDY)
+		atmel_rx_chars(port);
+	else if (pending & ATMEL_US_RXBRK) {
+		/*
+		 * End of break detected. If it came along
+		 * with a character, atmel_rx_chars will
+		 * handle it.
+		 */
+		UART_PUT_CR(port, ATMEL_US_RSTSTA);
+		UART_PUT_IDR(port, ATMEL_US_RXBRK);
+		atmel_port->break_active = 0;
+	}
+}
+
+/*
+ * transmit interrupt handler.
+ */
+static void atmel_handle_transmit(struct uart_port *port, unsigned int pending)
+{
+	/* PDC transmit */
+	if (pending & ATMEL_US_ENDTX)
+		atmel_pdc_endtx(port);
+	if (pending & ATMEL_US_TXBUFE)
+		atmel_pdc_txbufe(port);
+
+	/* Interrupt transmit */
+	if (pending & ATMEL_US_TXRDY)
+		atmel_tx_chars(port);
+}
+
+/*
+ * status flags interrupt handler.
+ */
+static void atmel_handle_status(struct uart_port *port, unsigned int pending,
+				unsigned int status)
+{
+	/* TODO: All reads to CSR will clear these interrupts! */
+	if (pending & ATMEL_US_RIIC)
+		port->icount.rng++;
+	if (pending & ATMEL_US_DSRIC)
+		port->icount.dsr++;
+
+	if (pending & ATMEL_US_DCDIC)
+		uart_handle_dcd_change(port, !(status & ATMEL_US_DCD));
+	if (pending & ATMEL_US_CTSIC)
+		uart_handle_cts_change(port, !(status & ATMEL_US_CTS));
+	if (pending &
+	    (ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC | ATMEL_US_CTSIC))
+		wake_up_interruptible(&port->info->delta_msr_wait);
+}
+
+/*
  * Interrupt handler
  */
 static irqreturn_t atmel_interrupt(int irq, void *dev_id)
 {
 	struct uart_port *port = dev_id;
-	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
 	unsigned int status, pending, pass_counter = 0;
 
 	status = UART_GET_CSR(port);
 	pending = status & UART_GET_IMR(port);
 	while (pending) {
-		/* PDC receive */
-		if (pending & ATMEL_US_ENDRX)
-			atmel_pdc_endrx(port);
-		if (pending & ATMEL_US_TIMEOUT)
-			atmel_pdc_timeout(port);
-		if (atmel_port->use_dma_rx && pending & (ATMEL_US_RXBRK | ATMEL_US_OVRE | ATMEL_US_FRAME | ATMEL_US_PARE))
-			atmel_pdc_rxerr(port, pending);
-
-		/* Interrupt receive */
-		if (pending & ATMEL_US_RXRDY)
-			atmel_rx_chars(port);
-		else if (pending & ATMEL_US_RXBRK) {
-			/*
-			 * End of break detected. If it came along
-			 * with a character, atmel_rx_chars will
-			 * handle it.
-			 */
-			UART_PUT_CR(port, ATMEL_US_RSTSTA);
-			UART_PUT_IDR(port, ATMEL_US_RXBRK);
-			atmel_port->break_active = 0;
-		}
-
-		// TODO: All reads to CSR will clear these interrupts!
-		if (pending & ATMEL_US_RIIC) port->icount.rng++;
-		if (pending & ATMEL_US_DSRIC) port->icount.dsr++;
-		if (pending & ATMEL_US_DCDIC)
-			uart_handle_dcd_change(port, !(status & ATMEL_US_DCD));
-		if (pending & ATMEL_US_CTSIC)
-			uart_handle_cts_change(port, !(status & ATMEL_US_CTS));
-		if (pending & (ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC | ATMEL_US_CTSIC))
-			wake_up_interruptible(&port->info->delta_msr_wait);
-
-		/* PDC transmit */
-		if (pending & ATMEL_US_ENDTX)
-			atmel_pdc_endtx(port);
-		if (pending & ATMEL_US_TXBUFE)
-			atmel_pdc_txbufe(port);
-
-		/* Interrupt transmit */
-		if (pending & ATMEL_US_TXRDY)
-			atmel_tx_chars(port);
+		atmel_handle_receive(port, pending);
+		atmel_handle_status(port, pending, status);
+		atmel_handle_transmit(port, pending);
 
 		if (pass_counter++ > ATMEL_ISR_PASS_LIMIT)
 			break;
@@ -596,7 +639,7 @@ static irqreturn_t atmel_interrupt(int i
  */
 static int atmel_startup(struct uart_port *port)
 {
-	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
 	int retval;
 
 	/*
@@ -609,7 +652,9 @@ static int atmel_startup(struct uart_por
 	/*
 	 * Allocate the IRQ
 	 */
-	retval = request_irq(port->irq, atmel_interrupt, IRQF_SHARED, "atmel_serial", port);
+	retval =
+	    request_irq(port->irq, atmel_interrupt, IRQF_SHARED, "atmel_serial",
+			port);
 	if (retval) {
 		printk("atmel_serial: atmel_startup - Can't get irq\n");
 		return retval;
@@ -627,13 +672,19 @@ static int atmel_startup(struct uart_por
 			pdc->buf = kmalloc(PDC_BUFFER_SIZE, GFP_KERNEL);
 			if (pdc->buf == NULL) {
 				if (i != 0) {
-					dma_unmap_single(port->dev, atmel_port->pdc_rx[0].dma_addr, PDC_BUFFER_SIZE, DMA_FROM_DEVICE);
+					dma_unmap_single(port->dev,
+							 atmel_port->pdc_rx[0].
+							 dma_addr,
+							 PDC_BUFFER_SIZE,
+							 DMA_FROM_DEVICE);
 					kfree(atmel_port->pdc_rx[0].buf);
 				}
 				free_irq(port->irq, port);
 				return -ENOMEM;
 			}
-			pdc->dma_addr = dma_map_single(port->dev, pdc->buf, PDC_BUFFER_SIZE, DMA_FROM_DEVICE);
+			pdc->dma_addr =
+			    dma_map_single(port->dev, pdc->buf, PDC_BUFFER_SIZE,
+					   DMA_FROM_DEVICE);
 			pdc->dma_size = PDC_BUFFER_SIZE;
 			pdc->ofs = 0;
 		}
@@ -651,7 +702,9 @@ static int atmel_startup(struct uart_por
 		struct circ_buf *xmit = &port->info->xmit;
 
 		pdc->buf = xmit->buf;
-		pdc->dma_addr = dma_map_single(port->dev, pdc->buf, SERIAL_XMIT_SIZE, DMA_TO_DEVICE);
+		pdc->dma_addr =
+		    dma_map_single(port->dev, pdc->buf, SERIAL_XMIT_SIZE,
+				   DMA_TO_DEVICE);
 		pdc->dma_size = SERIAL_XMIT_SIZE;
 		pdc->ofs = 0;
 	}
@@ -672,18 +725,21 @@ static int atmel_startup(struct uart_por
 	 * Finally, enable the serial port
 	 */
 	UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
-	UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN);		/* enable xmit & rcvr */
+	/* enable xmit & rcvr */
+	UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN);
 
 	if (atmel_port->use_dma_rx) {
-		UART_PUT_RTOR(port, PDC_RX_TIMEOUT);		/* set UART timeout */
+		/* set UART timeout */
+		UART_PUT_RTOR(port, PDC_RX_TIMEOUT);
 		UART_PUT_CR(port, ATMEL_US_STTTO);
 
 		UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT);
-		UART_PUT_PTCR(port, ATMEL_PDC_RXTEN);		/* enable PDC controller */
+		/* enable PDC controller */
+		UART_PUT_PTCR(port, ATMEL_PDC_RXTEN);
+	} else {
+		/* enable receive only */
+		UART_PUT_IER(port, ATMEL_US_RXRDY);
 	}
-	else
-		UART_PUT_IER(port, ATMEL_US_RXRDY);		/* enable receive only */
-
 	return 0;
 }
 
@@ -692,7 +748,7 @@ static int atmel_startup(struct uart_por
  */
 static void atmel_shutdown(struct uart_port *port)
 {
-	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
 
 	/*
 	 * Ensure everything is stopped.
@@ -709,14 +765,16 @@ static void atmel_shutdown(struct uart_p
 		for (i = 0; i < 2; i++) {
 			struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i];
 
-			dma_unmap_single(port->dev, pdc->dma_addr, pdc->dma_size, DMA_FROM_DEVICE);
+			dma_unmap_single(port->dev, pdc->dma_addr,
+					 pdc->dma_size, DMA_FROM_DEVICE);
 			kfree(pdc->buf);
 		}
 	}
 	if (atmel_port->use_dma_tx) {
 		struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx;
 
-		dma_unmap_single(port->dev, pdc->dma_addr, pdc->dma_size, DMA_TO_DEVICE);
+		dma_unmap_single(port->dev, pdc->dma_addr, pdc->dma_size,
+				 DMA_TO_DEVICE);
 	}
 
 	/*
@@ -741,43 +799,47 @@ static void atmel_shutdown(struct uart_p
 /*
  * Power / Clock management.
  */
-static void atmel_serial_pm(struct uart_port *port, unsigned int state, unsigned int oldstate)
+static void atmel_serial_pm(struct uart_port *port, unsigned int state,
+			    unsigned int oldstate)
 {
-	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
 
 	switch (state) {
-		case 0:
-			/*
-			 * Enable the peripheral clock for this serial port.
-			 * This is called on uart_open() or a resume event.
-			 */
-			clk_enable(atmel_port->clk);
-			break;
-		case 3:
-			/*
-			 * Disable the peripheral clock for this serial port.
-			 * This is called on uart_close() or a suspend event.
-			 */
-			clk_disable(atmel_port->clk);
-			break;
-		default:
-			printk(KERN_ERR "atmel_serial: unknown pm %d\n", state);
+	case 0:
+		/*
+		 * Enable the peripheral clock for this serial port.
+		 * This is called on uart_open() or a resume event.
+		 */
+		clk_enable(atmel_port->clk);
+		break;
+	case 3:
+		/*
+		 * Disable the peripheral clock for this serial port.
+		 * This is called on uart_close() or a suspend event.
+		 */
+		clk_disable(atmel_port->clk);
+		break;
+	default:
+		printk(KERN_ERR "atmel_serial: unknown pm %d\n", state);
 	}
 }
 
 /*
  * Change the port parameters
  */
-static void atmel_set_termios(struct uart_port *port, struct ktermios * termios, struct ktermios * old)
+static void atmel_set_termios(struct uart_port *port, struct ktermios *termios,
+			      struct ktermios *old)
 {
-	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
 	unsigned long flags;
 	unsigned int mode, imr, quot, baud;
 
 	/* Get current mode register */
-	mode = UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL | ATMEL_US_NBSTOP | ATMEL_US_PAR);
+	mode =
+	    UART_GET_MR(port) & ~(ATMEL_US_USCLKS | ATMEL_US_CHRL |
+				  ATMEL_US_NBSTOP | ATMEL_US_PAR);
 
-	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk/16);
+	baud = uart_get_baud_rate(port, termios, old, 0, port->uartclk / 16);
 	quot = uart_get_divisor(port, baud);
 
 	if (quot > 65535) {	/* BRGR is 16-bit, so switch to slower clock */
@@ -807,18 +869,16 @@ static void atmel_set_termios(struct uar
 
 	/* parity */
 	if (termios->c_cflag & PARENB) {
-		if (termios->c_cflag & CMSPAR) {			/* Mark or Space parity */
+		if (termios->c_cflag & CMSPAR) { /* Mark or Space parity */
 			if (termios->c_cflag & PARODD)
 				mode |= ATMEL_US_PAR_MARK;
 			else
 				mode |= ATMEL_US_PAR_SPACE;
-		}
-		else if (termios->c_cflag & PARODD)
+		} else if (termios->c_cflag & PARODD)
 			mode |= ATMEL_US_PAR_ODD;
 		else
 			mode |= ATMEL_US_PAR_EVEN;
-	}
-	else
+	} else
 		mode |= ATMEL_US_PAR_NONE;
 
 	spin_lock_irqsave(&port->lock, flags);
@@ -847,16 +907,16 @@ static void atmel_set_termios(struct uar
 		if (termios->c_iflag & IGNPAR)
 			port->ignore_status_mask |= ATMEL_US_OVRE;
 	}
-
-	// TODO: Ignore all characters if CREAD is set.
+	/* TODO: Ignore all characters if CREAD is set.*/
 
 	/* update the per-port timeout */
 	uart_update_timeout(port, termios->c_cflag, baud);
 
 	/* disable interrupts and drain transmitter */
 	imr = UART_GET_IMR(port);	/* get interrupt mask */
-	UART_PUT_IDR(port, -1);		/* disable all interrupts */
-	while (!(UART_GET_CSR(port) & ATMEL_US_TXEMPTY)) { barrier(); }
+	UART_PUT_IDR(port, -1);	/* disable all interrupts */
+	while (!(UART_GET_CSR(port) & ATMEL_US_TXEMPTY))
+		barrier();
 
 	/* disable receiver and transmitter */
 	UART_PUT_CR(port, ATMEL_US_TXDIS | ATMEL_US_RXDIS);
@@ -982,7 +1042,8 @@ static struct uart_ops atmel_pops = {
 /*
  * Configure the port from the platform device resource info.
  */
-static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, struct platform_device *pdev)
+static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port,
+				      struct platform_device *pdev)
 {
 	struct uart_port *port = &atmel_port->uart;
 	struct atmel_uart_data *data = pdev->dev.platform_data;
@@ -1001,16 +1062,16 @@ static void __devinit atmel_init_port(st
 		/* Already mapped by setup code */
 		port->membase = data->regs;
 	else {
-		port->flags	|= UPF_IOREMAP;
-		port->membase	= NULL;
+		port->flags |= UPF_IOREMAP;
+		port->membase = NULL;
 	}
 
-	if (!atmel_port->clk) {		/* for console, the clock could already be configured */
+	/* for console, the clock could already be configured */
+	if (!atmel_port->clk) {
 		atmel_port->clk = clk_get(&pdev->dev, "usart");
 		clk_enable(atmel_port->clk);
 		port->uartclk = clk_get_rate(atmel_port->clk);
 	}
-
 #ifdef SUPPORT_PDC
 	atmel_port->use_dma_rx = data->use_dma_rx;
 	atmel_port->use_dma_tx = data->use_dma_tx;
@@ -1036,7 +1097,6 @@ void __init atmel_register_uart_fns(stru
 	atmel_pops.set_wake	= fns->set_wake;
 }
 
-
 #ifdef CONFIG_SERIAL_ATMEL_CONSOLE
 static void atmel_console_putchar(struct uart_port *port, int ch)
 {
@@ -1054,7 +1114,7 @@ static void atmel_console_write(struct c
 	unsigned int status, imr;
 
 	/*
-	 *	First, save IMR and then disable interrupts
+	 *      First, save IMR and then disable interrupts
 	 */
 	imr = UART_GET_IMR(port);	/* get interrupt mask */
 	UART_PUT_IDR(port, ATMEL_US_RXRDY | ATMEL_US_TXRDY);
@@ -1062,30 +1122,31 @@ static void atmel_console_write(struct c
 	uart_console_write(port, s, count, atmel_console_putchar);
 
 	/*
-	 *	Finally, wait for transmitter to become empty
-	 *	and restore IMR
+	 *      Finally, wait for transmitter to become empty
+	 *      and restore IMR
 	 */
 	do {
 		status = UART_GET_CSR(port);
 	} while (!(status & ATMEL_US_TXRDY));
-	UART_PUT_IER(port, imr);	/* set interrupts back the way they were */
+	UART_PUT_IER(port, imr); /* set interrupts back the way they were */
 }
 
 /*
- * If the port was already initialised (eg, by a boot loader), try to determine
- * the current setup.
+ * If the port was already initialised (eg, by a boot loader),
+ * try to determine the current setup.
  */
-static void __init atmel_console_get_options(struct uart_port *port, int *baud, int *parity, int *bits)
+static void __init atmel_console_get_options(struct uart_port *port, int *baud,
+					     int *parity, int *bits)
 {
 	unsigned int mr, quot;
 
-// TODO: CR is a write-only register
-//	unsigned int cr;
+/* TODO: CR is a write-only register
+//      unsigned int cr;
 //
-//	cr = UART_GET_CR(port) & (ATMEL_US_RXEN | ATMEL_US_TXEN);
-//	if (cr == (ATMEL_US_RXEN | ATMEL_US_TXEN)) {
-//		/* ok, the port was enabled */
-//	}
+//      cr = UART_GET_CR(port) & (ATMEL_US_RXEN | ATMEL_US_TXEN);
+//      if (cr == (ATMEL_US_RXEN | ATMEL_US_TXEN)) {
+//              / * ok, the port was enabled * /
+//      } */
 
 	mr = UART_GET_MR(port) & ATMEL_US_CHRL;
 	if (mr == ATMEL_US_CHRL_8)
@@ -1117,10 +1178,10 @@ static int __init atmel_console_setup(st
 	int parity = 'n';
 	int flow = 'n';
 
-	if (port->membase == 0)		/* Port not initialized yet - delay setup */
+	if (port->membase == 0)	/* Port not initialized yet - delay setup */
 		return -ENODEV;
 
-	UART_PUT_IDR(port, -1);				/* disable interrupts */
+	UART_PUT_IDR(port, -1);	/* disable interrupts */
 	UART_PUT_CR(port, ATMEL_US_RSTSTA | ATMEL_US_RSTRX);
 	UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN);
 
@@ -1152,13 +1213,17 @@ static struct console atmel_console = {
 static int __init atmel_console_init(void)
 {
 	if (atmel_default_console_device) {
-		add_preferred_console(ATMEL_DEVICENAME, atmel_default_console_device->id, NULL);
-		atmel_init_port(&(atmel_ports[atmel_default_console_device->id]), atmel_default_console_device);
+		add_preferred_console(ATMEL_DEVICENAME,
+				      atmel_default_console_device->id, NULL);
+		atmel_init_port(
+			      &(atmel_ports[atmel_default_console_device->id]),
+			      atmel_default_console_device);
 		register_console(&atmel_console);
 	}
 
 	return 0;
 }
+
 console_initcall(atmel_console_init);
 
 /*
@@ -1166,11 +1231,13 @@ console_initcall(atmel_console_init);
  */
 static int __init atmel_late_console_init(void)
 {
-	if (atmel_default_console_device && !(atmel_console.flags & CON_ENABLED))
+	if (atmel_default_console_device
+	    && !(atmel_console.flags & CON_ENABLED))
 		register_console(&atmel_console);
 
 	return 0;
 }
+
 core_initcall(atmel_late_console_init);
 
 #else
@@ -1178,23 +1245,24 @@ core_initcall(atmel_late_console_init);
 #endif
 
 static struct uart_driver atmel_uart = {
-	.owner			= THIS_MODULE,
-	.driver_name		= "atmel_serial",
-	.dev_name		= ATMEL_DEVICENAME,
-	.major			= SERIAL_ATMEL_MAJOR,
-	.minor			= MINOR_START,
-	.nr			= ATMEL_MAX_UART,
-	.cons			= ATMEL_CONSOLE_DEVICE,
+	.owner		= THIS_MODULE,
+	.driver_name	= "atmel_serial",
+	.dev_name	= ATMEL_DEVICENAME,
+	.major		= SERIAL_ATMEL_MAJOR,
+	.minor		= MINOR_START,
+	.nr		= ATMEL_MAX_UART,
+	.cons		= ATMEL_CONSOLE_DEVICE,
 };
 
 #ifdef CONFIG_PM
-static int atmel_serial_suspend(struct platform_device *pdev, pm_message_t state)
+static int atmel_serial_suspend(struct platform_device *pdev,
+				pm_message_t state)
 {
 	struct uart_port *port = platform_get_drvdata(pdev);
-	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
 
 	if (device_may_wakeup(&pdev->dev)
-			&& !clk_must_disable(atmel_port->clk))
+	    && !clk_must_disable(atmel_port->clk))
 		enable_irq_wake(port->irq);
 	else {
 		uart_suspend_port(&atmel_uart, port);
@@ -1207,13 +1275,12 @@ static int atmel_serial_suspend(struct p
 static int atmel_serial_resume(struct platform_device *pdev)
 {
 	struct uart_port *port = platform_get_drvdata(pdev);
-	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
 
 	if (atmel_port->suspended) {
 		uart_resume_port(&atmel_uart, port);
 		atmel_port->suspended = 0;
-	}
-	else
+	} else
 		disable_irq_wake(port->irq);
 
 	return 0;
@@ -1243,7 +1310,7 @@ static int __devinit atmel_serial_probe(
 static int __devexit atmel_serial_remove(struct platform_device *pdev)
 {
 	struct uart_port *port = platform_get_drvdata(pdev);
-	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *) port;
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
 	int ret = 0;
 
 	clk_disable(atmel_port->clk);
@@ -1266,9 +1333,9 @@ static struct platform_driver atmel_seri
 	.suspend	= atmel_serial_suspend,
 	.resume		= atmel_serial_resume,
 	.driver		= {
-		.name	= "atmel_usart",
-		.owner	= THIS_MODULE,
-	},
+			  .name = "atmel_usart",
+			  .owner = THIS_MODULE,
+			  },
 };
 
 static int __init atmel_serial_init(void)

[-- Attachment #3: patch_atmel_serial_irq_splitup --]
[-- Type: application/octet-stream, Size: 8012 bytes --]

This patch splits up the interrupt handler of the serial port
into a interrupt top-half and some tasklets.

The goal is to get the interrupt top-half as short as possible to
minimize latencies on interrupts. But the old code also does some
calls in the interrupt handler that are not allowed on preempt-RT
in IRQF_NODELAY context. This handler is executed in this context
because of the interrupt sharing with the timer interrupt.
The timer interrupt on Preempt-RT runs in IRQF_NODELAY context.

2 tasklets are used:
* one for handling the error statuses
* one for pushing the incoming characters into the tty-layer.

The Transmit path was IRQF_NODELAY safe by itself, and is not adapted.
The read path for DMA(PDC) is also not adapted, because that code
does not run in IRQF_NODELAY context due to irq-sharing. The DBGU 
which is shared with the timer-irq does not work with DMA, so 
therefor this is no problem.

Reading the complete receive queue is still done in the top-half
because we never want to miss any incoming character.

This patch demands the following patches to be installed first:
* http://maxim.org.za/AT91RM9200/2.6/2.6.23-at91.patch.gz
* atmel_serial_cleanup.patch

Signed-off-by: Remy Bohmer <linux@bohmer.net>
---
 drivers/serial/atmel_serial.c |  154 +++++++++++++++++++++++++++++++++++++-----
 1 file changed, 136 insertions(+), 18 deletions(-)

Index: linux-2.6.23/drivers/serial/atmel_serial.c
===================================================================
--- linux-2.6.23.orig/drivers/serial/atmel_serial.c	2007-12-07 15:31:48.000000000 +0100
+++ linux-2.6.23/drivers/serial/atmel_serial.c	2007-12-07 15:33:34.000000000 +0100
@@ -128,6 +128,22 @@ struct atmel_dma_buffer {
 	unsigned        int ofs;
 };
 
+struct atmel_uart_char {
+	unsigned int status;
+	unsigned int overrun;
+	unsigned int ch;
+	unsigned int flg;
+};
+
+#define ATMEL_SERIAL_RINGSIZE 1024
+
+struct atmel_uart_ring {
+	unsigned int          head;
+	unsigned int          tail;
+	unsigned int          count;
+	struct atmel_uart_char data[ATMEL_SERIAL_RINGSIZE];
+};
+
 /*
  * We wrap our port structure around the generic uart_port.
  */
@@ -143,6 +159,13 @@ struct atmel_uart_port {
 
 	short 			use_dma_tx;	/* enable PDC transmitter */
 	struct atmel_dma_buffer pdc_tx;		/* PDC transmitter */
+
+	struct tasklet_struct   rx_task;
+	struct tasklet_struct   status_task;
+	unsigned int 		irq_pending;
+	unsigned int 		irq_status;
+
+	struct atmel_uart_ring  rx_ring;
 };
 
 static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART];
@@ -434,12 +457,42 @@ static void atmel_pdc_txbufe(struct uart
 }
 
 /*
+ * Stores the incoming character in the ring buffer
+ */
+static void
+atmel_buffer_rx_char(struct uart_port *port, unsigned int status,
+		     unsigned int overrun, unsigned int ch,
+		     unsigned int flg)
+{
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
+	struct atmel_uart_ring *ring = &atmel_port->rx_ring;
+	struct atmel_uart_char *c;
+
+	spin_lock_bh(&port->lock);
+
+	if (ring->count == ATMEL_SERIAL_RINGSIZE)
+		goto out; /* Buffer overflow, ignore char */
+
+	c = &ring->data[ring->head];
+	c->status  = status;
+	c->overrun = overrun;
+	c->ch      = ch;
+	c->flg     = flg;
+
+	ring->head++;
+	ring->head %= ATMEL_SERIAL_RINGSIZE;
+	ring->count++;
+
+ out:
+	spin_unlock_bh(&port->lock);
+}
+
+/*
  * Characters received (called from interrupt handler)
  */
 static void atmel_rx_chars(struct uart_port *port)
 {
 	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
-	struct tty_struct *tty = port->info->tty;
 	unsigned int status, ch, flg;
 
 	status = UART_GET_CSR(port);
@@ -499,13 +552,13 @@ static void atmel_rx_chars(struct uart_p
 		if (uart_handle_sysrq_char(port, ch))
 			goto ignore_char;
 
-		uart_insert_char(port, status, ATMEL_US_OVRE, ch, flg);
+		atmel_buffer_rx_char(port, status, ATMEL_US_OVRE, ch, flg);
 
 	ignore_char:
 		status = UART_GET_CSR(port);
 	}
 
-	tty_flip_buffer_push(tty);
+	tasklet_schedule(&atmel_port->rx_task);
 }
 
 /*
@@ -550,9 +603,9 @@ static void atmel_handle_receive(struct 
 
 	/* PDC receive */
 	if (pending & ATMEL_US_ENDRX)
-		atmel_pdc_endrx(port);
+		atmel_pdc_endrx(port); 	/*Not IRQF_NODELAY Safe*/
 	if (pending & ATMEL_US_TIMEOUT)
-		atmel_pdc_timeout(port);
+		atmel_pdc_timeout(port);/*Not IRQF_NODELAY Safe*/
 	if (atmel_port->use_dma_rx
 	    && pending & (ATMEL_US_RXBRK | ATMEL_US_OVRE | ATMEL_US_FRAME |
 			  ATMEL_US_PARE))
@@ -574,7 +627,7 @@ static void atmel_handle_receive(struct 
 }
 
 /*
- * transmit interrupt handler.
+ * transmit interrupt handler. (Transmit is IRQF_NODELAY safe)
  */
 static void atmel_handle_transmit(struct uart_port *port, unsigned int pending)
 {
@@ -595,19 +648,16 @@ static void atmel_handle_transmit(struct
 static void atmel_handle_status(struct uart_port *port, unsigned int pending,
 				unsigned int status)
 {
-	/* TODO: All reads to CSR will clear these interrupts! */
-	if (pending & ATMEL_US_RIIC)
-		port->icount.rng++;
-	if (pending & ATMEL_US_DSRIC)
-		port->icount.dsr++;
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
 
-	if (pending & ATMEL_US_DCDIC)
-		uart_handle_dcd_change(port, !(status & ATMEL_US_DCD));
-	if (pending & ATMEL_US_CTSIC)
-		uart_handle_cts_change(port, !(status & ATMEL_US_CTS));
-	if (pending &
-	    (ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC | ATMEL_US_CTSIC))
-		wake_up_interruptible(&port->info->delta_msr_wait);
+	spin_lock_bh(&port->lock);
+
+	atmel_port->irq_pending = pending;
+	atmel_port->irq_status  = status;
+
+	spin_unlock_bh(&port->lock);
+
+	tasklet_schedule(&atmel_port->status_task);
 }
 
 /*
@@ -635,6 +685,67 @@ static irqreturn_t atmel_interrupt(int i
 }
 
 /*
+ * tasklet handling tty stuff outside the interrupt handler.
+ */
+static void atmel_rx_handler_task(unsigned long data)
+{
+	struct uart_port *port = (struct uart_port *)data;
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
+	struct atmel_uart_ring *ring = &atmel_port->rx_ring;
+	unsigned long flags;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	while (ring->count) {
+		struct atmel_uart_char c = ring->data[ring->tail];
+
+		ring->tail++;
+		ring->tail %= ATMEL_SERIAL_RINGSIZE;
+		ring->count--;
+
+		spin_unlock_irqrestore(&port->lock, flags);
+
+		uart_insert_char(port, c.status, c.overrun, c.ch, c.flg);
+
+		spin_lock_irqsave(&port->lock, flags);
+	}
+
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	tty_flip_buffer_push(port->info->tty);
+}
+
+static void atmel_status_handler_task(unsigned long data)
+{
+	struct uart_port *port = (struct uart_port *)data;
+	struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
+	unsigned long flags;
+	unsigned int pending, status;
+
+	spin_lock_irqsave(&port->lock, flags);
+
+	pending = atmel_port->irq_pending;
+	status = atmel_port->irq_status;
+
+	spin_unlock_irqrestore(&port->lock, flags);
+
+	/* TODO: All reads to CSR will clear these interrupts! */
+	if (pending & ATMEL_US_RIIC)
+		port->icount.rng++;
+	if (pending & ATMEL_US_DSRIC)
+		port->icount.dsr++;
+
+	if (pending & ATMEL_US_DCDIC)
+		uart_handle_dcd_change(port, !(status & ATMEL_US_DCD));
+	if (pending & ATMEL_US_CTSIC)
+		uart_handle_cts_change(port, !(status & ATMEL_US_CTS));
+	if (pending &
+	    (ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC | ATMEL_US_CTSIC))
+		wake_up_interruptible(&port->info->delta_msr_wait);
+}
+
+
+/*
  * Perform initialization and enable port for reception
  */
 static int atmel_startup(struct uart_port *port)
@@ -1058,6 +1169,13 @@ static void __devinit atmel_init_port(st
 	port->mapbase	= pdev->resource[0].start;
 	port->irq	= pdev->resource[1].start;
 
+	tasklet_init(&atmel_port->rx_task, atmel_rx_handler_task,
+			(unsigned long)port);
+	tasklet_init(&atmel_port->status_task, atmel_status_handler_task,
+			(unsigned long)port);
+
+	memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring));
+
 	if (data->regs)
 		/* Already mapped by setup code */
 		port->membase = data->regs;

[-- Attachment #4: patch_atmel_serial_irqf_nodelay --]
[-- Type: application/octet-stream, Size: 1234 bytes --]

On PREEMPT-RT we may not block on a normal spinlock in IRQ/IRQF_NODELAY-context.
This patch fixes this by making the lock a raw_spinlock_t for the
Atmel serial console.

This patch demands the following patches to be installed first:
* http://maxim.org.za/AT91RM9200/2.6/2.6.23-at91.patch.gz
* atmel_serial_cleanup.patch
* atmel_serial_irq_splitup.patch

Signed-off-by: Remy Bohmer <linux@bohmer.net>
---
 include/linux/serial_core.h |    8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

Index: linux-2.6.23/include/linux/serial_core.h
===================================================================
--- linux-2.6.23.orig/include/linux/serial_core.h	2007-12-07 14:40:35.000000000 +0100
+++ linux-2.6.23/include/linux/serial_core.h	2007-12-07 15:08:33.000000000 +0100
@@ -225,8 +225,14 @@ struct uart_icount {
 
 typedef unsigned int __bitwise__ upf_t;
 
+#ifdef CONFIG_SERIAL_ATMEL
+typedef raw_spinlock_t uart_spinlock_t;
+#else
+typedef spinlock_t uart_spinlock_t;
+#endif
+
 struct uart_port {
-	spinlock_t		lock;			/* port lock */
+	uart_spinlock_t		lock;			/* port lock */
 	unsigned int		iobase;			/* in/out[bwl] */
 	unsigned char __iomem	*membase;		/* read/write[bwl] */
 	unsigned int		irq;			/* irq number */

^ permalink raw reply	[flat|nested] 20+ messages in thread
[parent not found: <AANLkTi=TyvXEtoZGatP5pxymLSna9g6ULV46i72bYgc4@mail.gmail.com>]

end of thread, other threads:[~2010-10-29 13:52 UTC | newest]

Thread overview: 20+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-12-07 15:24 [PATCH]: Atmel Serial Console interrupt handler splitup Remy Bohmer
2007-12-07 18:31 ` David Brownell
2007-12-07 19:57 ` Andrew Victor
2007-12-07 20:38   ` Remy Bohmer
2007-12-07 21:16     ` Remy Bohmer
2007-12-12 21:10 ` Steven Rostedt
2007-12-12 22:29   ` Remy Bohmer
2007-12-13 16:40     ` Remy Bohmer
2007-12-13 17:33       ` Andrew Victor
2007-12-13 20:32         ` Remy Bohmer
2007-12-13 20:35           ` Remy Bohmer
2007-12-14 11:46             ` Remy Bohmer
2007-12-17 12:17               ` Haavard Skinnemoen
2007-12-17 18:13                 ` Haavard Skinnemoen
2007-12-17 20:56                 ` Remy Bohmer
2007-12-17 23:12                   ` Haavard Skinnemoen
2007-12-18  7:32                     ` Remy Bohmer
2007-12-17 23:49                   ` Russell King - ARM Linux
2007-12-18  9:07                     ` Haavard Skinnemoen
     [not found] <AANLkTi=TyvXEtoZGatP5pxymLSna9g6ULV46i72bYgc4@mail.gmail.com>
2010-10-29 13:52 ` Remy Bohmer

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).