From 064129e2236f174d9f0a97ff273a73117a001668 Mon Sep 17 00:00:00 2001 From: Huibin Hong Date: Thu, 12 Jul 2018 15:07:46 +0800 Subject: [PATCH] serial: 8250_dw: lost one byte sometime when receive To avoid "too much work for irq" issue, cherry pick the the patch. It reads the RBR to clear the time out interrupt, but sometime the rx fifo may be not empty while cpu reads the RBR. Which would cause the data lost. patch for "too much work":06451e93ab59e5b1843c29cbb468a274f4919563 By the way, current patch can't get rid of the risk entirely, so I try a lot to solve it. Unfortunately, I only got the phenomenon that lower pclk can reduce the probability. And I check the dw data sheet, it has pclk and sclk, so there is synchronization problem. But it only requires (slck < 4*pclk). Change-Id: I01a36c689b43310294c45294abcf4982f5ddf2af Signed-off-by: Huibin Hong --- drivers/tty/serial/8250/8250_dw.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index cdea151a32eb..b519c21a6fd2 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -32,6 +32,7 @@ /* Offsets for the DesignWare specific registers */ #define DW_UART_USR 0x1f /* UART Status Register */ #define DW_UART_DLF 0xc0 /* Divisor Latch Fraction Register */ +#define DW_UART_RFL 0x21 /* UART Receive Fifo Level Register */ #define DW_UART_CPR 0xf4 /* Component Parameter Register */ #define DW_UART_UCV 0xf8 /* UART Component Version */ @@ -257,7 +258,7 @@ static int dw8250_handle_irq(struct uart_port *p) { struct dw8250_data *d = p->private_data; unsigned int iir = p->serial_in(p, UART_IIR); - unsigned int status; + unsigned int status, usr, rfl; unsigned long flags; /* @@ -269,9 +270,10 @@ static int dw8250_handle_irq(struct uart_port *p) */ if ((iir & 0x3f) == UART_IIR_RX_TIMEOUT) { spin_lock_irqsave(&p->lock, flags); + usr = p->serial_in(p, d->usr_reg); status = p->serial_in(p, UART_LSR); - - if (!(status & (UART_LSR_DR | UART_LSR_BI))) + rfl = p->serial_in(p, DW_UART_RFL); + if (!(status & (UART_LSR_DR | UART_LSR_BI)) && !(usr & 0x1) && (rfl == 0)) (void) p->serial_in(p, UART_RX); spin_unlock_irqrestore(&p->lock, flags);