summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Agner <stefan.agner@toradex.com>2018-04-06 11:43:37 +0200
committerMax Krummenacher <max.krummenacher@toradex.com>2018-06-21 17:36:27 +0200
commit52657351d56eb8b655682daa57dd2139eba71be0 (patch)
treec4fe87e7c533bba5184129836794a4266ad910dc
parent183cfed9e76b4b045b3243ad4ac730aec02c16c1 (diff)
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: Stefan Agner <stefan.agner@toradex.com> Acked-by: Max Krummenacher <max.krummenacher@toradex.com>
-rw-r--r--drivers/tty/serial/fsl_lpuart.c15
1 files changed, 13 insertions, 2 deletions
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 36acc98cea71..3b2690d6896a 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -627,7 +627,7 @@ out:
static irqreturn_t lpuart_rxint(int irq, void *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;
unsigned long flags;
unsigned char rx, sr;
@@ -657,7 +657,7 @@ static irqreturn_t lpuart_rxint(int irq, void *dev_id)
sport->port.icount.frame++;
if (sr & UARTSR1_OR)
- sport->port.icount.overrun++;
+ overrun++;
if (sr & sport->port.ignore_status_mask) {
if (++ignored > 100)
@@ -684,6 +684,17 @@ static irqreturn_t lpuart_rxint(int irq, void *dev_id)
}
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);
tty_flip_buffer_push(port);