From fc7855d6f24591c39c1c87d1043dbb13752f76d5 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 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 4651a0a66103..7f24617474f0 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -33,6 +33,7 @@ /* Offsets for the DesignWare specific registers */ #define DW_UART_USR 0x1f /* UART Status Register */ +#define DW_UART_RFL 0x21 /* UART Receive Fifo Level Register */ /* DesignWare specific register fields */ #define DW_UART_MCR_SIRE BIT(6) @@ -242,7 +243,7 @@ static int dw8250_handle_irq(struct uart_port *p) { struct dw8250_data *d = to_dw8250_data(p->private_data); unsigned int iir = p->serial_in(p, UART_IIR); - unsigned int status; + unsigned int status, usr, rfl; unsigned long flags; /* @@ -254,9 +255,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);