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 <huibin.hong@rock-chips.com>
This commit is contained in:
Huibin Hong
2018-07-12 15:07:46 +08:00
committed by Tao Huang
parent b03cab30e2
commit fc7855d6f2

View File

@@ -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);