> 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 <[EMAIL PROTECTED]>
---
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;