summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorStefan Agner <stefan.agner@toradex.com>2018-04-05 15:32:37 +0200
committerMax Krummenacher <max.krummenacher@toradex.com>2018-06-21 17:35:59 +0200
commit183cfed9e76b4b045b3243ad4ac730aec02c16c1 (patch)
treeda5e015c1f4bf14877305c5eb2f6548d62b882a0
parent47133a000cd65fc17873e562e2f72b368b615760 (diff)
tty: serial: fsl_lpuart: fix framing error handling when using DMA
When using DMA framing error get cleared properly. However, due to the additional read from the data register, an underflow in the receive FIFO buffer occures (the FIFO pointer gets out of sync). Clear the FIFO in case an underflow has occured. Also disable the receiver during this operation and when reading the data register to minimize potential interference. 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.c22
1 files changed, 22 insertions, 0 deletions
diff --git a/drivers/tty/serial/fsl_lpuart.c b/drivers/tty/serial/fsl_lpuart.c
index 96ba3a2a9e37..36acc98cea71 100644
--- a/drivers/tty/serial/fsl_lpuart.c
+++ b/drivers/tty/serial/fsl_lpuart.c
@@ -803,6 +803,13 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport)
sr = readb(sport->port.membase + UARTSR1);
if (sr & (UARTSR1_PE | UARTSR1_FE)) {
+ unsigned char cr2;
+
+ /* Disable receiver during this operation... */
+ cr2 = readb(sport->port.membase + UARTCR2);
+ cr2 &= ~(UARTCR2_RE);
+ writeb(cr2, sport->port.membase + UARTCR2);
+
/* Read DR to clear the error flags */
readb(sport->port.membase + UARTDR);
@@ -810,6 +817,21 @@ static void lpuart_copy_rx_to_tty(struct lpuart_port *sport)
sport->port.icount.parity++;
else if (sr & UARTSR1_FE)
sport->port.icount.frame++;
+
+ /*
+ * At this point parity/framing error is cleared
+ * However, since the DMA already read the data register
+ * and we had to read it again after reading the status
+ * register to properly clear the flags, the FIFO actually
+ * underflowed... This requires a clearing of the FIFO...
+ */
+ if (readb(sport->port.membase + UARTSFIFO) & UARTSFIFO_RXUF) {
+ writeb(UARTSFIFO_RXUF, sport->port.membase + UARTSFIFO);
+ writeb(UARTCFIFO_RXFLUSH, sport->port.membase + UARTCFIFO);
+ }
+
+ cr2 |= UARTCR2_RE;
+ writeb(cr2, sport->port.membase + UARTCR2);
}
async_tx_ack(sport->dma_rx_desc);