diff options
author | Chip Coldwell <coldwell@redhat.com> | 2008-02-08 07:21:06 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@woody.linux-foundation.org> | 2008-02-08 12:22:37 -0500 |
commit | a66706158d6bc4d9eb29c37852001f78f4c8989c (patch) | |
tree | d2d593dc2902c1db1ea6d0ef417dc3426e0b3bae | |
parent | 1ecc26bd2789ddb253f61e182a61c776663fe44c (diff) |
atmel_serial: add DMA support
This patch is based on the DMA-patch by Chip Coldwell for the AT91/AT32 serial
USARTS, with some tweaks to make it apply neatly on top of the other patches
in this series.
The RX and TX code has been moved to a tasklet and reworked a bit. Instead of
depending on the ENDRX and TIMEOUT bits in CSR, we simply grab as much data as
we can from the DMA buffers. I think this closes a race where the ENDRX bit
is set after we read CSR but before we read RPR, although I haven't confirmed
this.
Similarly, the two TX handlers (ENDTX and TXBUFE) have been combined into one.
Since the current code only uses a single TX buffer, there's no point in
handling those interrupts separately.
This also fixes a DMA sync bug in the original patch.
[linux@bohmer.net: rebased onto irq-splitup patch]
[hskinnemoen@atmel.com: moved to tasklet, fixed dma bug, misc cleanups]
[hskinnemoen@atmel.com: atmel_serial dma: Misc fixes and cleanups]
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>
-rw-r--r-- | drivers/serial/Kconfig | 15 | ||||
-rw-r--r-- | drivers/serial/atmel_serial.c | 392 |
2 files changed, 383 insertions, 24 deletions
diff --git a/drivers/serial/Kconfig b/drivers/serial/Kconfig index 84a054d7e986..b82595cf13e8 100644 --- a/drivers/serial/Kconfig +++ b/drivers/serial/Kconfig | |||
@@ -380,6 +380,21 @@ config SERIAL_ATMEL_CONSOLE | |||
380 | console is the device which receives all kernel messages and | 380 | console is the device which receives all kernel messages and |
381 | warnings and which allows logins in single user mode). | 381 | warnings and which allows logins in single user mode). |
382 | 382 | ||
383 | config SERIAL_ATMEL_PDC | ||
384 | bool "Support DMA transfers on AT91 / AT32 serial port" | ||
385 | depends on SERIAL_ATMEL | ||
386 | default y | ||
387 | help | ||
388 | Say Y here if you wish to use the PDC to do DMA transfers to | ||
389 | and from the Atmel AT91 / AT32 serial port. In order to | ||
390 | actually use DMA transfers, make sure that the use_dma_tx | ||
391 | and use_dma_rx members in the atmel_uart_data struct is set | ||
392 | appropriately for each port. | ||
393 | |||
394 | Note that break and error handling currently doesn't work | ||
395 | properly when DMA is enabled. Make sure that ports where | ||
396 | this matters don't use DMA. | ||
397 | |||
383 | config SERIAL_ATMEL_TTYAT | 398 | config SERIAL_ATMEL_TTYAT |
384 | bool "Install as device ttyATn instead of ttySn" | 399 | bool "Install as device ttyATn instead of ttySn" |
385 | depends on SERIAL_ATMEL=y | 400 | depends on SERIAL_ATMEL=y |
diff --git a/drivers/serial/atmel_serial.c b/drivers/serial/atmel_serial.c index f0f6ea3a9eed..d15ab2243289 100644 --- a/drivers/serial/atmel_serial.c +++ b/drivers/serial/atmel_serial.c | |||
@@ -7,6 +7,8 @@ | |||
7 | * Based on drivers/char/serial_sa1100.c, by Deep Blue Solutions Ltd. | 7 | * Based on drivers/char/serial_sa1100.c, by Deep Blue Solutions Ltd. |
8 | * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. | 8 | * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. |
9 | * | 9 | * |
10 | * DMA support added by Chip Coldwell. | ||
11 | * | ||
10 | * This program is free software; you can redistribute it and/or modify | 12 | * This program is free software; you can redistribute it and/or modify |
11 | * it under the terms of the GNU General Public License as published by | 13 | * it under the terms of the GNU General Public License as published by |
12 | * the Free Software Foundation; either version 2 of the License, or | 14 | * the Free Software Foundation; either version 2 of the License, or |
@@ -33,6 +35,7 @@ | |||
33 | #include <linux/sysrq.h> | 35 | #include <linux/sysrq.h> |
34 | #include <linux/tty_flip.h> | 36 | #include <linux/tty_flip.h> |
35 | #include <linux/platform_device.h> | 37 | #include <linux/platform_device.h> |
38 | #include <linux/dma-mapping.h> | ||
36 | #include <linux/atmel_pdc.h> | 39 | #include <linux/atmel_pdc.h> |
37 | #include <linux/atmel_serial.h> | 40 | #include <linux/atmel_serial.h> |
38 | 41 | ||
@@ -46,6 +49,10 @@ | |||
46 | #include <asm/arch/gpio.h> | 49 | #include <asm/arch/gpio.h> |
47 | #endif | 50 | #endif |
48 | 51 | ||
52 | #define PDC_BUFFER_SIZE 512 | ||
53 | /* Revisit: We should calculate this based on the actual port settings */ | ||
54 | #define PDC_RX_TIMEOUT (3 * 10) /* 3 bytes */ | ||
55 | |||
49 | #if defined(CONFIG_SERIAL_ATMEL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) | 56 | #if defined(CONFIG_SERIAL_ATMEL_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) |
50 | #define SUPPORT_SYSRQ | 57 | #define SUPPORT_SYSRQ |
51 | #endif | 58 | #endif |
@@ -103,6 +110,13 @@ | |||
103 | static int (*atmel_open_hook)(struct uart_port *); | 110 | static int (*atmel_open_hook)(struct uart_port *); |
104 | static void (*atmel_close_hook)(struct uart_port *); | 111 | static void (*atmel_close_hook)(struct uart_port *); |
105 | 112 | ||
113 | struct atmel_dma_buffer { | ||
114 | unsigned char *buf; | ||
115 | dma_addr_t dma_addr; | ||
116 | unsigned int dma_size; | ||
117 | unsigned int ofs; | ||
118 | }; | ||
119 | |||
106 | struct atmel_uart_char { | 120 | struct atmel_uart_char { |
107 | u16 status; | 121 | u16 status; |
108 | u16 ch; | 122 | u16 ch; |
@@ -119,6 +133,13 @@ struct atmel_uart_port { | |||
119 | unsigned short suspended; /* is port suspended? */ | 133 | unsigned short suspended; /* is port suspended? */ |
120 | int break_active; /* break being received */ | 134 | int break_active; /* break being received */ |
121 | 135 | ||
136 | short use_dma_rx; /* enable PDC receiver */ | ||
137 | short pdc_rx_idx; /* current PDC RX buffer */ | ||
138 | struct atmel_dma_buffer pdc_rx[2]; /* PDC receier */ | ||
139 | |||
140 | short use_dma_tx; /* enable PDC transmitter */ | ||
141 | struct atmel_dma_buffer pdc_tx; /* PDC transmitter */ | ||
142 | |||
122 | struct tasklet_struct tasklet; | 143 | struct tasklet_struct tasklet; |
123 | unsigned int irq_status; | 144 | unsigned int irq_status; |
124 | unsigned int irq_status_prev; | 145 | unsigned int irq_status_prev; |
@@ -132,6 +153,32 @@ static struct atmel_uart_port atmel_ports[ATMEL_MAX_UART]; | |||
132 | static struct console atmel_console; | 153 | static struct console atmel_console; |
133 | #endif | 154 | #endif |
134 | 155 | ||
156 | #ifdef CONFIG_SERIAL_ATMEL_PDC | ||
157 | static bool atmel_use_dma_rx(struct uart_port *port) | ||
158 | { | ||
159 | struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port; | ||
160 | |||
161 | return atmel_port->use_dma_rx; | ||
162 | } | ||
163 | |||
164 | static bool atmel_use_dma_tx(struct uart_port *port) | ||
165 | { | ||
166 | struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port; | ||
167 | |||
168 | return atmel_port->use_dma_tx; | ||
169 | } | ||
170 | #else | ||
171 | static bool atmel_use_dma_rx(struct uart_port *port) | ||
172 | { | ||
173 | return false; | ||
174 | } | ||
175 | |||
176 | static bool atmel_use_dma_tx(struct uart_port *port) | ||
177 | { | ||
178 | return false; | ||
179 | } | ||
180 | #endif | ||
181 | |||
135 | /* | 182 | /* |
136 | * Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty. | 183 | * Return TIOCSER_TEMT when transmitter FIFO and Shift register is empty. |
137 | */ | 184 | */ |
@@ -213,7 +260,12 @@ static u_int atmel_get_mctrl(struct uart_port *port) | |||
213 | */ | 260 | */ |
214 | static void atmel_stop_tx(struct uart_port *port) | 261 | static void atmel_stop_tx(struct uart_port *port) |
215 | { | 262 | { |
216 | UART_PUT_IDR(port, ATMEL_US_TXRDY); | 263 | if (atmel_use_dma_tx(port)) { |
264 | /* disable PDC transmit */ | ||
265 | UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS); | ||
266 | UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); | ||
267 | } else | ||
268 | UART_PUT_IDR(port, ATMEL_US_TXRDY); | ||
217 | } | 269 | } |
218 | 270 | ||
219 | /* | 271 | /* |
@@ -221,7 +273,17 @@ static void atmel_stop_tx(struct uart_port *port) | |||
221 | */ | 273 | */ |
222 | static void atmel_start_tx(struct uart_port *port) | 274 | static void atmel_start_tx(struct uart_port *port) |
223 | { | 275 | { |
224 | UART_PUT_IER(port, ATMEL_US_TXRDY); | 276 | if (atmel_use_dma_tx(port)) { |
277 | if (UART_GET_PTSR(port) & ATMEL_PDC_TXTEN) | ||
278 | /* The transmitter is already running. Yes, we | ||
279 | really need this.*/ | ||
280 | return; | ||
281 | |||
282 | UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); | ||
283 | /* re-enable PDC transmit */ | ||
284 | UART_PUT_PTCR(port, ATMEL_PDC_TXTEN); | ||
285 | } else | ||
286 | UART_PUT_IER(port, ATMEL_US_TXRDY); | ||
225 | } | 287 | } |
226 | 288 | ||
227 | /* | 289 | /* |
@@ -229,7 +291,12 @@ static void atmel_start_tx(struct uart_port *port) | |||
229 | */ | 291 | */ |
230 | static void atmel_stop_rx(struct uart_port *port) | 292 | static void atmel_stop_rx(struct uart_port *port) |
231 | { | 293 | { |
232 | UART_PUT_IDR(port, ATMEL_US_RXRDY); | 294 | if (atmel_use_dma_rx(port)) { |
295 | /* disable PDC receive */ | ||
296 | UART_PUT_PTCR(port, ATMEL_PDC_RXTDIS); | ||
297 | UART_PUT_IDR(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); | ||
298 | } else | ||
299 | UART_PUT_IDR(port, ATMEL_US_RXRDY); | ||
233 | } | 300 | } |
234 | 301 | ||
235 | /* | 302 | /* |
@@ -278,6 +345,27 @@ atmel_buffer_rx_char(struct uart_port *port, unsigned int status, | |||
278 | } | 345 | } |
279 | 346 | ||
280 | /* | 347 | /* |
348 | * Deal with parity, framing and overrun errors. | ||
349 | */ | ||
350 | static void atmel_pdc_rxerr(struct uart_port *port, unsigned int status) | ||
351 | { | ||
352 | /* clear error */ | ||
353 | UART_PUT_CR(port, ATMEL_US_RSTSTA); | ||
354 | |||
355 | if (status & ATMEL_US_RXBRK) { | ||
356 | /* ignore side-effect */ | ||
357 | status &= ~(ATMEL_US_PARE | ATMEL_US_FRAME); | ||
358 | port->icount.brk++; | ||
359 | } | ||
360 | if (status & ATMEL_US_PARE) | ||
361 | port->icount.parity++; | ||
362 | if (status & ATMEL_US_FRAME) | ||
363 | port->icount.frame++; | ||
364 | if (status & ATMEL_US_OVRE) | ||
365 | port->icount.overrun++; | ||
366 | } | ||
367 | |||
368 | /* | ||
281 | * Characters received (called from interrupt handler) | 369 | * Characters received (called from interrupt handler) |
282 | */ | 370 | */ |
283 | static void atmel_rx_chars(struct uart_port *port) | 371 | static void atmel_rx_chars(struct uart_port *port) |
@@ -364,6 +452,25 @@ atmel_handle_receive(struct uart_port *port, unsigned int pending) | |||
364 | { | 452 | { |
365 | struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port; | 453 | struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port; |
366 | 454 | ||
455 | if (atmel_use_dma_rx(port)) { | ||
456 | /* | ||
457 | * PDC receive. Just schedule the tasklet and let it | ||
458 | * figure out the details. | ||
459 | * | ||
460 | * TODO: We're not handling error flags correctly at | ||
461 | * the moment. | ||
462 | */ | ||
463 | if (pending & (ATMEL_US_ENDRX | ATMEL_US_TIMEOUT)) { | ||
464 | UART_PUT_IDR(port, (ATMEL_US_ENDRX | ||
465 | | ATMEL_US_TIMEOUT)); | ||
466 | tasklet_schedule(&atmel_port->tasklet); | ||
467 | } | ||
468 | |||
469 | if (pending & (ATMEL_US_RXBRK | ATMEL_US_OVRE | | ||
470 | ATMEL_US_FRAME | ATMEL_US_PARE)) | ||
471 | atmel_pdc_rxerr(port, pending); | ||
472 | } | ||
473 | |||
367 | /* Interrupt receive */ | 474 | /* Interrupt receive */ |
368 | if (pending & ATMEL_US_RXRDY) | 475 | if (pending & ATMEL_US_RXRDY) |
369 | atmel_rx_chars(port); | 476 | atmel_rx_chars(port); |
@@ -386,10 +493,18 @@ atmel_handle_transmit(struct uart_port *port, unsigned int pending) | |||
386 | { | 493 | { |
387 | struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port; | 494 | struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port; |
388 | 495 | ||
389 | /* Interrupt transmit */ | 496 | if (atmel_use_dma_tx(port)) { |
390 | if (pending & ATMEL_US_TXRDY) { | 497 | /* PDC transmit */ |
391 | UART_PUT_IDR(port, ATMEL_US_TXRDY); | 498 | if (pending & (ATMEL_US_ENDTX | ATMEL_US_TXBUFE)) { |
392 | tasklet_schedule(&atmel_port->tasklet); | 499 | UART_PUT_IDR(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); |
500 | tasklet_schedule(&atmel_port->tasklet); | ||
501 | } | ||
502 | } else { | ||
503 | /* Interrupt transmit */ | ||
504 | if (pending & ATMEL_US_TXRDY) { | ||
505 | UART_PUT_IDR(port, ATMEL_US_TXRDY); | ||
506 | tasklet_schedule(&atmel_port->tasklet); | ||
507 | } | ||
393 | } | 508 | } |
394 | } | 509 | } |
395 | 510 | ||
@@ -417,20 +532,63 @@ static irqreturn_t atmel_interrupt(int irq, void *dev_id) | |||
417 | struct uart_port *port = dev_id; | 532 | struct uart_port *port = dev_id; |
418 | unsigned int status, pending, pass_counter = 0; | 533 | unsigned int status, pending, pass_counter = 0; |
419 | 534 | ||
420 | status = UART_GET_CSR(port); | 535 | do { |
421 | pending = status & UART_GET_IMR(port); | 536 | status = UART_GET_CSR(port); |
422 | while (pending) { | 537 | pending = status & UART_GET_IMR(port); |
538 | if (!pending) | ||
539 | break; | ||
540 | |||
423 | atmel_handle_receive(port, pending); | 541 | atmel_handle_receive(port, pending); |
424 | atmel_handle_status(port, pending, status); | 542 | atmel_handle_status(port, pending, status); |
425 | atmel_handle_transmit(port, pending); | 543 | atmel_handle_transmit(port, pending); |
544 | } while (pass_counter++ < ATMEL_ISR_PASS_LIMIT); | ||
426 | 545 | ||
427 | if (pass_counter++ > ATMEL_ISR_PASS_LIMIT) | 546 | return IRQ_HANDLED; |
428 | break; | 547 | } |
429 | 548 | ||
430 | status = UART_GET_CSR(port); | 549 | /* |
431 | pending = status & UART_GET_IMR(port); | 550 | * Called from tasklet with ENDTX and TXBUFE interrupts disabled. |
551 | */ | ||
552 | static void atmel_tx_dma(struct uart_port *port) | ||
553 | { | ||
554 | struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port; | ||
555 | struct circ_buf *xmit = &port->info->xmit; | ||
556 | struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; | ||
557 | int count; | ||
558 | |||
559 | xmit->tail += pdc->ofs; | ||
560 | xmit->tail &= UART_XMIT_SIZE - 1; | ||
561 | |||
562 | port->icount.tx += pdc->ofs; | ||
563 | pdc->ofs = 0; | ||
564 | |||
565 | if (!uart_circ_empty(xmit)) { | ||
566 | /* more to transmit - setup next transfer */ | ||
567 | |||
568 | /* disable PDC transmit */ | ||
569 | UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS); | ||
570 | dma_sync_single_for_device(port->dev, | ||
571 | pdc->dma_addr, | ||
572 | pdc->dma_size, | ||
573 | DMA_TO_DEVICE); | ||
574 | |||
575 | count = CIRC_CNT_TO_END(xmit->head, xmit->tail, UART_XMIT_SIZE); | ||
576 | pdc->ofs = count; | ||
577 | |||
578 | UART_PUT_TPR(port, pdc->dma_addr + xmit->tail); | ||
579 | UART_PUT_TCR(port, count); | ||
580 | /* re-enable PDC transmit and interrupts */ | ||
581 | UART_PUT_PTCR(port, ATMEL_PDC_TXTEN); | ||
582 | UART_PUT_IER(port, ATMEL_US_ENDTX | ATMEL_US_TXBUFE); | ||
583 | } else { | ||
584 | /* nothing left to transmit - disable the transmitter */ | ||
585 | |||
586 | /* disable PDC transmit */ | ||
587 | UART_PUT_PTCR(port, ATMEL_PDC_TXTDIS); | ||
432 | } | 588 | } |
433 | return IRQ_HANDLED; | 589 | |
590 | if (uart_circ_chars_pending(xmit) < WAKEUP_CHARS) | ||
591 | uart_write_wakeup(port); | ||
434 | } | 592 | } |
435 | 593 | ||
436 | static void atmel_rx_from_ring(struct uart_port *port) | 594 | static void atmel_rx_from_ring(struct uart_port *port) |
@@ -501,6 +659,82 @@ static void atmel_rx_from_ring(struct uart_port *port) | |||
501 | spin_lock(&port->lock); | 659 | spin_lock(&port->lock); |
502 | } | 660 | } |
503 | 661 | ||
662 | static void atmel_rx_from_dma(struct uart_port *port) | ||
663 | { | ||
664 | struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port; | ||
665 | struct tty_struct *tty = port->info->tty; | ||
666 | struct atmel_dma_buffer *pdc; | ||
667 | int rx_idx = atmel_port->pdc_rx_idx; | ||
668 | unsigned int head; | ||
669 | unsigned int tail; | ||
670 | unsigned int count; | ||
671 | |||
672 | do { | ||
673 | /* Reset the UART timeout early so that we don't miss one */ | ||
674 | UART_PUT_CR(port, ATMEL_US_STTTO); | ||
675 | |||
676 | pdc = &atmel_port->pdc_rx[rx_idx]; | ||
677 | head = UART_GET_RPR(port) - pdc->dma_addr; | ||
678 | tail = pdc->ofs; | ||
679 | |||
680 | /* If the PDC has switched buffers, RPR won't contain | ||
681 | * any address within the current buffer. Since head | ||
682 | * is unsigned, we just need a one-way comparison to | ||
683 | * find out. | ||
684 | * | ||
685 | * In this case, we just need to consume the entire | ||
686 | * buffer and resubmit it for DMA. This will clear the | ||
687 | * ENDRX bit as well, so that we can safely re-enable | ||
688 | * all interrupts below. | ||
689 | */ | ||
690 | head = min(head, pdc->dma_size); | ||
691 | |||
692 | if (likely(head != tail)) { | ||
693 | dma_sync_single_for_cpu(port->dev, pdc->dma_addr, | ||
694 | pdc->dma_size, DMA_FROM_DEVICE); | ||
695 | |||
696 | /* | ||
697 | * head will only wrap around when we recycle | ||
698 | * the DMA buffer, and when that happens, we | ||
699 | * explicitly set tail to 0. So head will | ||
700 | * always be greater than tail. | ||
701 | */ | ||
702 | count = head - tail; | ||
703 | |||
704 | tty_insert_flip_string(tty, pdc->buf + pdc->ofs, count); | ||
705 | |||
706 | dma_sync_single_for_device(port->dev, pdc->dma_addr, | ||
707 | pdc->dma_size, DMA_FROM_DEVICE); | ||
708 | |||
709 | port->icount.rx += count; | ||
710 | pdc->ofs = head; | ||
711 | } | ||
712 | |||
713 | /* | ||
714 | * If the current buffer is full, we need to check if | ||
715 | * the next one contains any additional data. | ||
716 | */ | ||
717 | if (head >= pdc->dma_size) { | ||
718 | pdc->ofs = 0; | ||
719 | UART_PUT_RNPR(port, pdc->dma_addr); | ||
720 | UART_PUT_RNCR(port, pdc->dma_size); | ||
721 | |||
722 | rx_idx = !rx_idx; | ||
723 | atmel_port->pdc_rx_idx = rx_idx; | ||
724 | } | ||
725 | } while (head >= pdc->dma_size); | ||
726 | |||
727 | /* | ||
728 | * Drop the lock here since it might end up calling | ||
729 | * uart_start(), which takes the lock. | ||
730 | */ | ||
731 | spin_unlock(&port->lock); | ||
732 | tty_flip_buffer_push(tty); | ||
733 | spin_lock(&port->lock); | ||
734 | |||
735 | UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); | ||
736 | } | ||
737 | |||
504 | /* | 738 | /* |
505 | * tasklet handling tty stuff outside the interrupt handler. | 739 | * tasklet handling tty stuff outside the interrupt handler. |
506 | */ | 740 | */ |
@@ -514,7 +748,10 @@ static void atmel_tasklet_func(unsigned long data) | |||
514 | /* The interrupt handler does not take the lock */ | 748 | /* The interrupt handler does not take the lock */ |
515 | spin_lock(&port->lock); | 749 | spin_lock(&port->lock); |
516 | 750 | ||
517 | atmel_tx_chars(port); | 751 | if (atmel_use_dma_tx(port)) |
752 | atmel_tx_dma(port); | ||
753 | else | ||
754 | atmel_tx_chars(port); | ||
518 | 755 | ||
519 | status = atmel_port->irq_status; | 756 | status = atmel_port->irq_status; |
520 | status_change = status ^ atmel_port->irq_status_prev; | 757 | status_change = status ^ atmel_port->irq_status_prev; |
@@ -536,7 +773,10 @@ static void atmel_tasklet_func(unsigned long data) | |||
536 | atmel_port->irq_status_prev = status; | 773 | atmel_port->irq_status_prev = status; |
537 | } | 774 | } |
538 | 775 | ||
539 | atmel_rx_from_ring(port); | 776 | if (atmel_use_dma_rx(port)) |
777 | atmel_rx_from_dma(port); | ||
778 | else | ||
779 | atmel_rx_from_ring(port); | ||
540 | 780 | ||
541 | spin_unlock(&port->lock); | 781 | spin_unlock(&port->lock); |
542 | } | 782 | } |
@@ -546,6 +786,7 @@ static void atmel_tasklet_func(unsigned long data) | |||
546 | */ | 786 | */ |
547 | static int atmel_startup(struct uart_port *port) | 787 | static int atmel_startup(struct uart_port *port) |
548 | { | 788 | { |
789 | struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port; | ||
549 | int retval; | 790 | int retval; |
550 | 791 | ||
551 | /* | 792 | /* |
@@ -566,6 +807,56 @@ static int atmel_startup(struct uart_port *port) | |||
566 | } | 807 | } |
567 | 808 | ||
568 | /* | 809 | /* |
810 | * Initialize DMA (if necessary) | ||
811 | */ | ||
812 | if (atmel_use_dma_rx(port)) { | ||
813 | int i; | ||
814 | |||
815 | for (i = 0; i < 2; i++) { | ||
816 | struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i]; | ||
817 | |||
818 | pdc->buf = kmalloc(PDC_BUFFER_SIZE, GFP_KERNEL); | ||
819 | if (pdc->buf == NULL) { | ||
820 | if (i != 0) { | ||
821 | dma_unmap_single(port->dev, | ||
822 | atmel_port->pdc_rx[0].dma_addr, | ||
823 | PDC_BUFFER_SIZE, | ||
824 | DMA_FROM_DEVICE); | ||
825 | kfree(atmel_port->pdc_rx[0].buf); | ||
826 | } | ||
827 | free_irq(port->irq, port); | ||
828 | return -ENOMEM; | ||
829 | } | ||
830 | pdc->dma_addr = dma_map_single(port->dev, | ||
831 | pdc->buf, | ||
832 | PDC_BUFFER_SIZE, | ||
833 | DMA_FROM_DEVICE); | ||
834 | pdc->dma_size = PDC_BUFFER_SIZE; | ||
835 | pdc->ofs = 0; | ||
836 | } | ||
837 | |||
838 | atmel_port->pdc_rx_idx = 0; | ||
839 | |||
840 | UART_PUT_RPR(port, atmel_port->pdc_rx[0].dma_addr); | ||
841 | UART_PUT_RCR(port, PDC_BUFFER_SIZE); | ||
842 | |||
843 | UART_PUT_RNPR(port, atmel_port->pdc_rx[1].dma_addr); | ||
844 | UART_PUT_RNCR(port, PDC_BUFFER_SIZE); | ||
845 | } | ||
846 | if (atmel_use_dma_tx(port)) { | ||
847 | struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; | ||
848 | struct circ_buf *xmit = &port->info->xmit; | ||
849 | |||
850 | pdc->buf = xmit->buf; | ||
851 | pdc->dma_addr = dma_map_single(port->dev, | ||
852 | pdc->buf, | ||
853 | UART_XMIT_SIZE, | ||
854 | DMA_TO_DEVICE); | ||
855 | pdc->dma_size = UART_XMIT_SIZE; | ||
856 | pdc->ofs = 0; | ||
857 | } | ||
858 | |||
859 | /* | ||
569 | * If there is a specific "open" function (to register | 860 | * If there is a specific "open" function (to register |
570 | * control line interrupts) | 861 | * control line interrupts) |
571 | */ | 862 | */ |
@@ -584,8 +875,18 @@ static int atmel_startup(struct uart_port *port) | |||
584 | /* enable xmit & rcvr */ | 875 | /* enable xmit & rcvr */ |
585 | UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); | 876 | UART_PUT_CR(port, ATMEL_US_TXEN | ATMEL_US_RXEN); |
586 | 877 | ||
587 | /* enable receive only */ | 878 | if (atmel_use_dma_rx(port)) { |
588 | UART_PUT_IER(port, ATMEL_US_RXRDY); | 879 | /* set UART timeout */ |
880 | UART_PUT_RTOR(port, PDC_RX_TIMEOUT); | ||
881 | UART_PUT_CR(port, ATMEL_US_STTTO); | ||
882 | |||
883 | UART_PUT_IER(port, ATMEL_US_ENDRX | ATMEL_US_TIMEOUT); | ||
884 | /* enable PDC controller */ | ||
885 | UART_PUT_PTCR(port, ATMEL_PDC_RXTEN); | ||
886 | } else { | ||
887 | /* enable receive only */ | ||
888 | UART_PUT_IER(port, ATMEL_US_RXRDY); | ||
889 | } | ||
589 | 890 | ||
590 | return 0; | 891 | return 0; |
591 | } | 892 | } |
@@ -595,6 +896,38 @@ static int atmel_startup(struct uart_port *port) | |||
595 | */ | 896 | */ |
596 | static void atmel_shutdown(struct uart_port *port) | 897 | static void atmel_shutdown(struct uart_port *port) |
597 | { | 898 | { |
899 | struct atmel_uart_port *atmel_port = (struct atmel_uart_port *)port; | ||
900 | /* | ||
901 | * Ensure everything is stopped. | ||
902 | */ | ||
903 | atmel_stop_rx(port); | ||
904 | atmel_stop_tx(port); | ||
905 | |||
906 | /* | ||
907 | * Shut-down the DMA. | ||
908 | */ | ||
909 | if (atmel_use_dma_rx(port)) { | ||
910 | int i; | ||
911 | |||
912 | for (i = 0; i < 2; i++) { | ||
913 | struct atmel_dma_buffer *pdc = &atmel_port->pdc_rx[i]; | ||
914 | |||
915 | dma_unmap_single(port->dev, | ||
916 | pdc->dma_addr, | ||
917 | pdc->dma_size, | ||
918 | DMA_FROM_DEVICE); | ||
919 | kfree(pdc->buf); | ||
920 | } | ||
921 | } | ||
922 | if (atmel_use_dma_tx(port)) { | ||
923 | struct atmel_dma_buffer *pdc = &atmel_port->pdc_tx; | ||
924 | |||
925 | dma_unmap_single(port->dev, | ||
926 | pdc->dma_addr, | ||
927 | pdc->dma_size, | ||
928 | DMA_TO_DEVICE); | ||
929 | } | ||
930 | |||
598 | /* | 931 | /* |
599 | * Disable all interrupts, port and break condition. | 932 | * Disable all interrupts, port and break condition. |
600 | */ | 933 | */ |
@@ -706,6 +1039,10 @@ static void atmel_set_termios(struct uart_port *port, struct ktermios *termios, | |||
706 | if (termios->c_iflag & (BRKINT | PARMRK)) | 1039 | if (termios->c_iflag & (BRKINT | PARMRK)) |
707 | port->read_status_mask |= ATMEL_US_RXBRK; | 1040 | port->read_status_mask |= ATMEL_US_RXBRK; |
708 | 1041 | ||
1042 | if (atmel_use_dma_rx(port)) | ||
1043 | /* need to enable error interrupts */ | ||
1044 | UART_PUT_IER(port, port->read_status_mask); | ||
1045 | |||
709 | /* | 1046 | /* |
710 | * Characters to ignore | 1047 | * Characters to ignore |
711 | */ | 1048 | */ |
@@ -891,6 +1228,11 @@ static void __devinit atmel_init_port(struct atmel_uart_port *atmel_port, | |||
891 | clk_enable(atmel_port->clk); | 1228 | clk_enable(atmel_port->clk); |
892 | port->uartclk = clk_get_rate(atmel_port->clk); | 1229 | port->uartclk = clk_get_rate(atmel_port->clk); |
893 | } | 1230 | } |
1231 | |||
1232 | atmel_port->use_dma_rx = data->use_dma_rx; | ||
1233 | atmel_port->use_dma_tx = data->use_dma_tx; | ||
1234 | if (atmel_use_dma_tx(port)) | ||
1235 | port->fifosize = PDC_BUFFER_SIZE; | ||
894 | } | 1236 | } |
895 | 1237 | ||
896 | /* | 1238 | /* |
@@ -1125,11 +1467,13 @@ static int __devinit atmel_serial_probe(struct platform_device *pdev) | |||
1125 | port = &atmel_ports[pdev->id]; | 1467 | port = &atmel_ports[pdev->id]; |
1126 | atmel_init_port(port, pdev); | 1468 | atmel_init_port(port, pdev); |
1127 | 1469 | ||
1128 | ret = -ENOMEM; | 1470 | if (!atmel_use_dma_rx(&port->uart)) { |
1129 | data = kmalloc(ATMEL_SERIAL_RINGSIZE, GFP_KERNEL); | 1471 | ret = -ENOMEM; |
1130 | if (!data) | 1472 | data = kmalloc(ATMEL_SERIAL_RINGSIZE, GFP_KERNEL); |
1131 | goto err_alloc_ring; | 1473 | if (!data) |
1132 | port->rx_ring.buf = data; | 1474 | goto err_alloc_ring; |
1475 | port->rx_ring.buf = data; | ||
1476 | } | ||
1133 | 1477 | ||
1134 | ret = uart_add_one_port(&atmel_uart, &port->uart); | 1478 | ret = uart_add_one_port(&atmel_uart, &port->uart); |
1135 | if (ret) | 1479 | if (ret) |