Commit cc584ab8 authored by Stefan Agner's avatar Stefan Agner Committed by Greg Kroah-Hartman

tty: serial: fsl_lpuart: flush receive FIFO after overruns

After overruns the FIFO pointers become misaligned. This
typically shows by characters still being stuck in the FIFO
despite the empty flag being asserted. After the first
assertion of the overrun flag the empty flag still seems to
indicate FIFO state correctly and all data can be read.
However, after another overrun assertion the FIFO seems to
be off by one such that the last received character is still
in the FIFO (despite the empty flag being asserted).

Flushing the receive FIFO reinitializes pointers. Hence it
is recommended to flush the FIFO after overruns, see also:
https://community.nxp.com/thread/321175

Hence, on assertion of the overrun flag read the remaining
data from the FIFO and flush buffers.
Signed-off-by: default avatarStefan Agner <stefan.agner@toradex.com>
Acked-by: default avatarMax Krummenacher <max.krummenacher@toradex.com>
Cc: Stefan Agner <stefan@agner.ch>
Cc: Bhuvanchandra DV <bhuvanchandra.dv@toradex.com>
Cc: Chris Healy <cphealy@gmail.com>
Cc: Cory Tusar <cory.tusar@zii.aero>
Cc: Lucas Stach <l.stach@pengutronix.de>
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Jiri Slaby <jslaby@suse.com>
Cc: linux-imx@nxp.com
Cc: linux-serial@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Link: https://lore.kernel.org/r/20190729195226.8862-3-andrew.smirnov@gmail.comSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 65632179
...@@ -799,7 +799,7 @@ static irqreturn_t lpuart_txint(int irq, void *dev_id) ...@@ -799,7 +799,7 @@ static irqreturn_t lpuart_txint(int irq, void *dev_id)
static irqreturn_t lpuart_rxint(int irq, void *dev_id) static irqreturn_t lpuart_rxint(int irq, void *dev_id)
{ {
struct lpuart_port *sport = dev_id; struct lpuart_port *sport = dev_id;
unsigned int flg, ignored = 0; unsigned int flg, ignored = 0, overrun = 0;
struct tty_port *port = &sport->port.state->port; struct tty_port *port = &sport->port.state->port;
unsigned long flags; unsigned long flags;
unsigned char rx, sr; unsigned char rx, sr;
...@@ -826,7 +826,7 @@ static irqreturn_t lpuart_rxint(int irq, void *dev_id) ...@@ -826,7 +826,7 @@ static irqreturn_t lpuart_rxint(int irq, void *dev_id)
sport->port.icount.frame++; sport->port.icount.frame++;
if (sr & UARTSR1_OR) if (sr & UARTSR1_OR)
sport->port.icount.overrun++; overrun++;
if (sr & sport->port.ignore_status_mask) { if (sr & sport->port.ignore_status_mask) {
if (++ignored > 100) if (++ignored > 100)
...@@ -853,6 +853,17 @@ static irqreturn_t lpuart_rxint(int irq, void *dev_id) ...@@ -853,6 +853,17 @@ static irqreturn_t lpuart_rxint(int irq, void *dev_id)
} }
out: out:
if (overrun) {
sport->port.icount.overrun += overrun;
/*
* Overruns cause FIFO pointers to become missaligned.
* Flushing the receive FIFO reinitializes the pointers.
*/
writeb(UARTCFIFO_RXFLUSH, sport->port.membase + UARTCFIFO);
writeb(UARTSFIFO_RXOF, sport->port.membase + UARTSFIFO);
}
spin_unlock_irqrestore(&sport->port.lock, flags); spin_unlock_irqrestore(&sport->port.lock, flags);
tty_flip_buffer_push(port); tty_flip_buffer_push(port);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment