aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorRemy Bohmer <linux@bohmer.net>2008-02-08 07:21:05 -0500
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2008-02-08 12:22:37 -0500
commit1ecc26bd2789ddb253f61e182a61c776663fe44c (patch)
tree1ad6d8bdc5e9afdc09182af840319ceaa3802ae8 /drivers
parentdfa7f343e526f3595d8f1d99807d141ae0c08601 (diff)
atmel_serial: split the interrupt handler
Split up the interrupt handler of the serial port into a interrupt top-half and a tasklet. 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. The tasklet takes care of handling control status changes, pushing incoming characters to the tty layer, handling break and other errors. It also handles pushing TX data into the data register. Reading the complete receive queue is still done in the top-half because we never want to miss any incoming character. [hskinnemoen@atmel.com: misc cleanups and simplifications] Signed-off-by: Remy Bohmer <linux@bohmer.net> Signed-off-by: Haavard Skinnemoen <hskinnemoen@atmel.com> Cc: Andrew Victor <linux@maxim.org.za> Tested-by: Marc Pignat <marc.pignat@hevs.ch> Signed-off-by: Andrew Morton <akpm@linux-foundation.org> Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/serial/atmel_serial.c245
1 files changed, 190 insertions, 55 deletions
diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c
index e06c6c8f4dd8..f0f6ea3a9eed 100644
--- a/drivers/serial/atmel_serial.c
+++ b/drivers/serial/atmel_serial.c
@@ -103,6 +103,13 @@
103static int (*atmel_open_hook)(struct uart_port *); 103static int (*atmel_open_hook)(struct uart_port *);
104static void (*atmel_close_hook)(struct uart_port *); 104static void (*atmel_close_hook)(struct uart_port *);
105 105
106struct atmel_uart_char {
107 u16 status;
108 u16 ch;
109};
110
111#define ATMEL_SERIAL_RINGSIZE 1024
112
106/* 113/*
107 * We wrap our port structure around the generic uart_port. 114 * We wrap our port structure around the generic uart_port.
108 */ 115 */
@@ -111,6 +118,12 @@ struct atmel_uart_port {
111 struct clk *clk; /* uart clock */ 118 struct clk *clk; /* uart clock */
112 unsigned short suspended; /* is port suspended? */ 119 unsigned short suspended; /* is port suspended? */
113 int break_active; /* break being received */ 120 int break_active; /* break being received */
121
122 struct tasklet_struct tasklet;
123 unsigned int irq_status;
124 unsigned int irq_status_prev;
125
126 struct circ_buf rx_ring;
114}; 127};
115 128
116static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART]; 129static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART];
@@ -240,22 +253,42 @@ static void atmel_break_ctl(struct uart_port *port, int break_state)
240} 253}
241 254
242/* 255/*
256 * Stores the incoming character in the ring buffer
257 */
258static void
259atmel_buffer_rx_char(struct uart_port *port, unsigned int status,
260 unsigned int ch)
261{
262 struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
263 struct circ_buf *ring = &atmel_port->rx_ring;
264 struct atmel_uart_char *c;
265
266 if (!CIRC_SPACE(ring->head, ring->tail, ATMEL_SERIAL_RINGSIZE))
267 /* Buffer overflow, ignore char */
268 return;
269
270 c = &((struct atmel_uart_char *)ring->buf)[ring->head];
271 c->status = status;
272 c->ch = ch;
273
274 /* Make sure the character is stored before we update head. */
275 smp_wmb();
276
277 ring->head = (ring->head + 1) & (ATMEL_SERIAL_RINGSIZE - 1);
278}
279
280/*
243 * Characters received (called from interrupt handler) 281 * Characters received (called from interrupt handler)
244 */ 282 */
245static void atmel_rx_chars(struct uart_port *port) 283static void atmel_rx_chars(struct uart_port *port)
246{ 284{
247 struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port; 285 struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
248 struct tty_struct *tty = port->info->tty; 286 unsigned int status, ch;
249 unsigned int status, ch, flg;
250 287
251 status = UART_GET_CSR(port); 288 status = UART_GET_CSR(port);
252 while (status & ATMEL_US_RXRDY) { 289 while (status & ATMEL_US_RXRDY) {
253 ch = UART_GET_CHAR(port); 290 ch = UART_GET_CHAR(port);
254 291
255 port->icount.rx++;
256
257 flg = TTY_NORMAL;
258
259 /* 292 /*
260 * note that the error handling code is 293 * note that the error handling code is
261 * out of the main execution path 294 * out of the main execution path
@@ -263,17 +296,14 @@ static void atmel_rx_chars(struct uart_port *port)
263 if (unlikely(status & (ATMEL_US_PARE | ATMEL_US_FRAME 296 if (unlikely(status & (ATMEL_US_PARE | ATMEL_US_FRAME
264 | ATMEL_US_OVRE | ATMEL_US_RXBRK) 297 | ATMEL_US_OVRE | ATMEL_US_RXBRK)
265 || atmel_port->break_active)) { 298 || atmel_port->break_active)) {
299
266 /* clear error */ 300 /* clear error */
267 UART_PUT_CR(port, ATMEL_US_RSTSTA); 301 UART_PUT_CR(port, ATMEL_US_RSTSTA);
302
268 if (status & ATMEL_US_RXBRK 303 if (status & ATMEL_US_RXBRK
269 && !atmel_port->break_active) { 304 && !atmel_port->break_active) {
270 /* ignore side-effect */
271 status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME);
272 port->icount.brk++;
273 atmel_port->break_active = 1; 305 atmel_port->break_active = 1;
274 UART_PUT_IER(port, ATMEL_US_RXBRK); 306 UART_PUT_IER(port, ATMEL_US_RXBRK);
275 if (uart_handle_break(port))
276 goto ignore_char;
277 } else { 307 } else {
278 /* 308 /*
279 * This is either the end-of-break 309 * This is either the end-of-break
@@ -286,52 +316,30 @@ static void atmel_rx_chars(struct uart_port *port)
286 status &= ~ATMEL_US_RXBRK; 316 status &= ~ATMEL_US_RXBRK;
287 atmel_port->break_active = 0; 317 atmel_port->break_active = 0;
288 } 318 }
289 if (status & ATMEL_US_PARE)
290 port->icount.parity++;
291 if (status & ATMEL_US_FRAME)
292 port->icount.frame++;
293 if (status & ATMEL_US_OVRE)
294 port->icount.overrun++;
295
296 status &= port->read_status_mask;
297
298 if (status & ATMEL_US_RXBRK)
299 flg = TTY_BREAK;
300 else if (status & ATMEL_US_PARE)
301 flg = TTY_PARITY;
302 else if (status & ATMEL_US_FRAME)
303 flg = TTY_FRAME;
304 } 319 }
305 320
306 if (uart_handle_sysrq_char(port, ch)) 321 atmel_buffer_rx_char(port, status, ch);
307 goto ignore_char;
308
309 uart_insert_char(port, status, ATMEL_US_OVRE, ch, flg);
310
311ignore_char:
312 status = UART_GET_CSR(port); 322 status = UART_GET_CSR(port);
313 } 323 }
314 324
315 tty_flip_buffer_push(tty); 325 tasklet_schedule(&atmel_port->tasklet);
316} 326}
317 327
318/* 328/*
319 * Transmit characters (called from interrupt handler) 329 * Transmit characters (called from tasklet with TXRDY interrupt
330 * disabled)
320 */ 331 */
321static void atmel_tx_chars(struct uart_port *port) 332static void atmel_tx_chars(struct uart_port *port)
322{ 333{
323 struct circ_buf *xmit = &port->info->xmit; 334 struct circ_buf *xmit = &port->info->xmit;
324 335
325 if (port->x_char) { 336 if (port->x_char && UART_GET_CSR(port) & ATMEL_US_TXRDY) {
326 UART_PUT_CHAR(port, port->x_char); 337 UART_PUT_CHAR(port, port->x_char);
327 port->icount.tx++; 338 port->icount.tx++;
328 port->x_char = 0; 339 port->x_char = 0;
329 return;
330 } 340 }
331 if (uart_circ_empty(xmit) || uart_tx_stopped(port)) { 341 if (uart_circ_empty(xmit) || uart_tx_stopped(port))
332 atmel_stop_tx(port);
333 return; 342 return;
334 }
335 343
336 while (UART_GET_CSR(port) & ATMEL_US_TXRDY) { 344 while (UART_GET_CSR(port) & ATMEL_US_TXRDY) {
337 UART_PUT_CHAR(port, xmit->buf[xmit->tail]); 345 UART_PUT_CHAR(port, xmit->buf[xmit->tail]);
@@ -344,8 +352,8 @@ static void atmel_tx_chars(struct uart_port *port)
344 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) 352 if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS)
345 uart_write_wakeup(port); 353 uart_write_wakeup(port);
346 354
347 if (uart_circ_empty(xmit)) 355 if (!uart_circ_empty(xmit))
348 atmel_stop_tx(port); 356 UART_PUT_IER(port, ATMEL_US_TXRDY);
349} 357}
350 358
351/* 359/*
@@ -371,14 +379,18 @@ atmel_handle_receive(struct uart_port *port, unsigned int pending)
371} 379}
372 380
373/* 381/*
374 * transmit interrupt handler. 382 * transmit interrupt handler. (Transmit is IRQF_NODELAY safe)
375 */ 383 */
376static void 384static void
377atmel_handle_transmit(struct uart_port *port, unsigned int pending) 385atmel_handle_transmit(struct uart_port *port, unsigned int pending)
378{ 386{
387 struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
388
379 /* Interrupt transmit */ 389 /* Interrupt transmit */
380 if (pending & ATMEL_US_TXRDY) 390 if (pending & ATMEL_US_TXRDY) {
381 atmel_tx_chars(port); 391 UART_PUT_IDR(port, ATMEL_US_TXRDY);
392 tasklet_schedule(&atmel_port->tasklet);
393 }
382} 394}
383 395
384/* 396/*
@@ -388,18 +400,13 @@ static void
388atmel_handle_status(struct uart_port *port, unsigned int pending, 400atmel_handle_status(struct uart_port *port, unsigned int pending,
389 unsigned int status) 401 unsigned int status)
390{ 402{
391 /* TODO: All reads to CSR will clear these interrupts! */ 403 struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
392 if (pending & ATMEL_US_RIIC) 404
393 port->icount.rng++;
394 if (pending & ATMEL_US_DSRIC)
395 port->icount.dsr++;
396 if (pending & ATMEL_US_DCDIC)
397 uart_handle_dcd_change(port, !(status & ATMEL_US_DCD));
398 if (pending & ATMEL_US_CTSIC)
399 uart_handle_cts_change(port, !(status & ATMEL_US_CTS));
400 if (pending & (ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC 405 if (pending & (ATMEL_US_RIIC | ATMEL_US_DSRIC | ATMEL_US_DCDIC
401 | ATMEL_US_CTSIC)) 406 | ATMEL_US_CTSIC)) {
402 wake_up_interruptible(&port->info->delta_msr_wait); 407 atmel_port->irq_status = status;
408 tasklet_schedule(&atmel_port->tasklet);
409 }
403} 410}
404 411
405/* 412/*
@@ -426,6 +433,114 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id)
426 return IRQ_HANDLED; 433 return IRQ_HANDLED;
427} 434}
428 435
436static void atmel_rx_from_ring(struct uart_port *port)
437{
438 struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
439 struct circ_buf *ring = &atmel_port->rx_ring;
440 unsigned int flg;
441 unsigned int status;
442
443 while (ring->head != ring->tail) {
444 struct atmel_uart_char c;
445
446 /* Make sure c is loaded after head. */
447 smp_rmb();
448
449 c = ((struct atmel_uart_char *)ring->buf)[ring->tail];
450
451 ring->tail = (ring->tail + 1) & (ATMEL_SERIAL_RINGSIZE - 1);
452
453 port->icount.rx++;
454 status = c.status;
455 flg = TTY_NORMAL;
456
457 /*
458 * note that the error handling code is
459 * out of the main execution path
460 */
461 if (unlikely(status & (ATMEL_US_PARE | ATMEL_US_FRAME
462 | ATMEL_US_OVRE | ATMEL_US_RXBRK))) {
463 if (status & ATMEL_US_RXBRK) {
464 /* ignore side-effect */
465 status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME);
466
467 port->icount.brk++;
468 if (uart_handle_break(port))
469 continue;
470 }
471 if (status & ATMEL_US_PARE)
472 port->icount.parity++;
473 if (status & ATMEL_US_FRAME)
474 port->icount.frame++;
475 if (status & ATMEL_US_OVRE)
476 port->icount.overrun++;
477
478 status &= port->read_status_mask;
479
480 if (status & ATMEL_US_RXBRK)
481 flg = TTY_BREAK;
482 else if (status & ATMEL_US_PARE)
483 flg = TTY_PARITY;
484 else if (status & ATMEL_US_FRAME)
485 flg = TTY_FRAME;
486 }
487
488
489 if (uart_handle_sysrq_char(port, c.ch))
490 continue;
491
492 uart_insert_char(port, status, ATMEL_US_OVRE, c.ch, flg);
493 }
494
495 /*
496 * Drop the lock here since it might end up calling
497 * uart_start(), which takes the lock.
498 */
499 spin_unlock(&port->lock);
500 tty_flip_buffer_push(port->info->tty);
501 spin_lock(&port->lock);
502}
503
504/*
505 * tasklet handling tty stuff outside the interrupt handler.
506 */
507static void atmel_tasklet_func(unsigned long data)
508{
509 struct uart_port *port = (struct uart_port *)data;
510 struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port;
511 unsigned int status;
512 unsigned int status_change;
513
514 /* The interrupt handler does not take the lock */
515 spin_lock(&port->lock);
516
517 atmel_tx_chars(port);
518
519 status = atmel_port->irq_status;
520 status_change = status ^ atmel_port->irq_status_prev;
521
522 if (status_change & (ATMEL_US_RI | ATMEL_US_DSR
523 | ATMEL_US_DCD | ATMEL_US_CTS)) {
524 /* TODO: All reads to CSR will clear these interrupts! */
525 if (status_change & ATMEL_US_RI)
526 port->icount.rng++;
527 if (status_change & ATMEL_US_DSR)
528 port->icount.dsr++;
529 if (status_change & ATMEL_US_DCD)
530 uart_handle_dcd_change(port, !(status & ATMEL_US_DCD));
531 if (status_change & ATMEL_US_CTS)
532 uart_handle_cts_change(port, !(status & ATMEL_US_CTS));
533
534 wake_up_interruptible(&port->info->delta_msr_wait);
535
536 atmel_port->irq_status_prev = status;
537 }
538
539 atmel_rx_from_ring(port);
540
541 spin_unlock(&port->lock);
542}
543
429/* 544/*
430 * Perform initialization and enable port for reception 545 * Perform initialization and enable port for reception
431 */ 546 */
@@ -757,6 +872,11 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port,
757 port->mapbase = pdev->resource[0].start; 872 port->mapbase = pdev->resource[0].start;
758 port->irq = pdev->resource[1].start; 873 port->irq = pdev->resource[1].start;
759 874
875 tasklet_init(&atmel_port->tasklet, atmel_tasklet_func,
876 (unsigned long)port);
877
878 memset(&atmel_port->rx_ring, 0, sizeof(atmel_port->rx_ring));
879
760 if (data->regs) 880 if (data->regs)
761 /* Already mapped by setup code */ 881 /* Already mapped by setup code */
762 port->membase = data->regs; 882 port->membase = data->regs;
@@ -997,11 +1117,20 @@ static int atmel_serial_resume(struct platform_device *pdev)
997static int __devinit atmel_serial_probe(struct platform_device *pdev) 1117static int __devinit atmel_serial_probe(struct platform_device *pdev)
998{ 1118{
999 struct atmel_uart_port *port; 1119 struct atmel_uart_port *port;
1120 void *data;
1000 int ret; 1121 int ret;
1001 1122
1123 BUILD_BUG_ON(!is_power_of_2(ATMEL_SERIAL_RINGSIZE));
1124
1002 port = &atmel_ports[pdev->id]; 1125 port = &atmel_ports[pdev->id];
1003 atmel_init_port(port, pdev); 1126 atmel_init_port(port, pdev);
1004 1127
1128 ret = -ENOMEM;
1129 data = kmalloc(ATMEL_SERIAL_RINGSIZE, GFP_KERNEL);
1130 if (!data)
1131 goto err_alloc_ring;
1132 port->rx_ring.buf = data;
1133
1005 ret = uart_add_one_port(&atmel_uart, &port->uart); 1134 ret = uart_add_one_port(&atmel_uart, &port->uart);
1006 if (ret) 1135 if (ret)
1007 goto err_add_port; 1136 goto err_add_port;
@@ -1012,6 +1141,9 @@ static int __devinit atmel_serial_probe(struct platform_device *pdev)
1012 return 0; 1141 return 0;
1013 1142
1014err_add_port: 1143err_add_port:
1144 kfree(port->rx_ring.buf);
1145 port->rx_ring.buf = NULL;
1146err_alloc_ring:
1015 if (!atmel_is_console_port(&port->uart)) { 1147 if (!atmel_is_console_port(&port->uart)) {
1016 clk_disable(port->clk); 1148 clk_disable(port->clk);
1017 clk_put(port->clk); 1149 clk_put(port->clk);
@@ -1032,6 +1164,9 @@ static int __devexit atmel_serial_remove(struct platform_device *pdev)
1032 1164
1033 ret = uart_remove_one_port(&atmel_uart, port); 1165 ret = uart_remove_one_port(&atmel_uart, port);
1034 1166
1167 tasklet_kill(&atmel_port->tasklet);
1168 kfree(atmel_port->rx_ring.buf);
1169
1035 /* "port" is allocated statically, so we shouldn't free it */ 1170 /* "port" is allocated statically, so we shouldn't free it */
1036 1171
1037 clk_disable(atmel_port->clk); 1172 clk_disable(atmel_port->clk);