Commit 8f9b87b3dcb18783389dcacca5073f1f3ab872dd

Authored by Stefan Agner
Committed by Greg Kroah-Hartman
1 parent 5716a78103

serial: fsl_lpuart: avoid new transfer while DMA is running

commit 5f1437f61a0b351d25b528c159360da3d5e8c77b upstream.

When the UART is in DMA receive mode (RDMAS set) and one character
just arrived while another interrupt is handled (e.g. TX), the RDRF
(receiver data register full flag) is set due to the water level of
1. But since the DMA will take care of this character, there is no
need to handle it by calling lpuart_prepare_rx. Handling it leads to
adding the RX timeout timer twice:

[   74.336698] Kernel BUG at 80053070 [verbose debug info unavailable]
[   74.342999] Internal error: Oops - BUG: 0 [#1] ARM0:00.00 khungtaskd
[   74.347817] Modules linked in:    0 S  0.0  0.0   0:00.00 writeback
[   74.350926] CPU: 0 PID: 0 Comm: swapper Not tainted 3.19.0-rc3-00001-g39d78e2 #1788
[   74.358617] Hardware name: Freescale Vybrid VF610 (Device Tree)t
[   74.364563] task: 807a7678 ti: 8079c000 task.ti: 8079c000 kblockd
[   74.370002] PC is at add_timer+0x24/0x28.0  0.0   0:00.09 kworker/u2:1
[   74.373960] LR is at lpuart_int+0x15c/0x3d8
[   74.378171] pc : [<80053070>]    lr : [<802e0d88>]    psr: a0010193
[   74.378171] sp : 8079de10  ip : 8079de20  fp : 8079de1c
[   74.389694] r10: 807d44c0  r9 : 8688c300  r8 : 00000013
[   74.394943] r7 : 20010193  r6 : 00000000  r5 : 000000a0  r4 : 86997210
[   74.401498] r3 : ffffa7da  r2 : 80817868  r1 : 86997210  r0 : 86997344
[   74.408052] Flags: NzCv  IRQs off  FIQs on  Mode SVC_32  ISA ARM  Segment kernel
[   74.415489] Control: 10c5387d  Table: 8611c059  DAC: 00000015
[   74.421265] Process swapper (pid: 0, stack limit = 0x8079c230)
...

Solve this by only execute the receiver path (lpuart_prepare_rx) if
the DMA receive mode (RDMAS) is not set. Also, make sure the flag is
cleared on initialization, in case it has been left set.

This can be best reproduced using UART as a serial console, then
running top while dd'ing data into the terminal.

Signed-off-by: Stefan Agner <stefan@agner.ch>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

Showing 1 changed file with 5 additions and 4 deletions Side-by-side Diff

drivers/tty/serial/fsl_lpuart.c
... ... @@ -755,18 +755,18 @@
755 755 static irqreturn_t lpuart_int(int irq, void *dev_id)
756 756 {
757 757 struct lpuart_port *sport = dev_id;
758   - unsigned char sts;
  758 + unsigned char sts, crdma;
759 759  
760 760 sts = readb(sport->port.membase + UARTSR1);
  761 + crdma = readb(sport->port.membase + UARTCR5);
761 762  
762   - if (sts & UARTSR1_RDRF) {
  763 + if (sts & UARTSR1_RDRF && !(crdma & UARTCR5_RDMAS)) {
763 764 if (sport->lpuart_dma_use)
764 765 lpuart_prepare_rx(sport);
765 766 else
766 767 lpuart_rxint(irq, dev_id);
767 768 }
768   - if (sts & UARTSR1_TDRE &&
769   - !(readb(sport->port.membase + UARTCR5) & UARTCR5_TDMAS)) {
  769 + if (sts & UARTSR1_TDRE && !(crdma & UARTCR5_TDMAS)) {
770 770 if (sport->lpuart_dma_use)
771 771 lpuart_pio_tx(sport);
772 772 else
... ... @@ -1106,6 +1106,7 @@
1106 1106 setup_timer(&sport->lpuart_timer, lpuart_timer_func,
1107 1107 (unsigned long)sport);
1108 1108 temp = readb(port->membase + UARTCR5);
  1109 + temp &= ~UARTCR5_RDMAS;
1109 1110 writeb(temp | UARTCR5_TDMAS, port->membase + UARTCR5);
1110 1111 }
1111 1112