> Because I know it is common practice in the kernel, I attached 3 new > patches to inline these :-)) Grmbl, 1 wrong file attached. Here is the correct one. Regards, Remy
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: * atmel_serial_cleanup.patch Signed-off-by: Remy Bohmer <linux@xxxxxxxxxx> --- drivers/serial/atmel_serial.c | 150 +++++++++++++++++++++++++++++++++++++----- 1 file changed, 133 insertions(+), 17 deletions(-) Index: linux-2.6.23.1-rt5/drivers/serial/atmel_serial.c =================================================================== --- linux-2.6.23.1-rt5.orig/drivers/serial/atmel_serial.c 2007-12-13 21:33:08.000000000 +0100 +++ linux-2.6.23.1-rt5/drivers/serial/atmel_serial.c 2007-12-13 21:33:10.000000000 +0100 @@ -111,6 +111,22 @@ static int (*atmel_open_hook) (struct uart_port *); static void (*atmel_close_hook) (struct uart_port *); +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. */ @@ -119,6 +135,13 @@ struct atmel_uart_port { struct clk *clk; /* uart clock */ unsigned short suspended; /* is port suspended? */ int break_active; /* break being received */ + + 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]; @@ -249,12 +272,41 @@ static void atmel_break_ctl(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(&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(&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); @@ -315,13 +367,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); } /* @@ -381,7 +433,7 @@ atmel_handle_receive(struct uart_port *p } /* - * transmit interrupt handler. + * transmit interrupt handler. (Transmit is IRQF_NODELAY safe) */ static inline void atmel_handle_transmit(struct uart_port *port, unsigned int pending) @@ -398,19 +450,16 @@ static inline 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); + struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port; + + spin_lock(&port->lock); + + atmel_port->irq_pending = pending; + atmel_port->irq_status = status; + + spin_unlock(&port->lock); + + tasklet_schedule(&atmel_port->status_task); } /* @@ -438,6 +487,66 @@ 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) @@ -770,6 +879,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;