diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c index d04a037d6b6f..4658e3e0ec42 100644 --- a/drivers/tty/serial/8250/8250_dw.c +++ b/drivers/tty/serial/8250/8250_dw.c @@ -57,7 +57,6 @@ struct dw8250_data { u8 usr_reg; - int last_lcr; int last_mcr; int line; struct clk *clk; @@ -77,17 +76,33 @@ static inline int dw8250_modify_msr(struct uart_port *p, int offset, int value) return value; } +static void dw8250_force_idle(struct uart_port *p) +{ + serial8250_clear_and_reinit_fifos(container_of + (p, struct uart_8250_port, port)); + (void)p->serial_in(p, UART_RX); +} + static void dw8250_serial_out(struct uart_port *p, int offset, int value) { struct dw8250_data *d = p->private_data; - if (offset == UART_LCR) - d->last_lcr = value; - if (offset == UART_MCR) d->last_mcr = value; writeb(value, p->membase + (offset << p->regshift)); + + /* Make sure LCR write wasn't ignored */ + if (offset == UART_LCR) { + int tries = 1000; + while (tries--) { + if (value == p->serial_in(p, UART_LCR)) + return; + dw8250_force_idle(p); + writeb(value, p->membase + (UART_LCR << p->regshift)); + } + dev_err(p->dev, "Couldn't set LCR to %d\n", value); + } } static unsigned int dw8250_serial_in(struct uart_port *p, int offset) @@ -108,13 +123,22 @@ static void dw8250_serial_out32(struct uart_port *p, int offset, int value) { struct dw8250_data *d = p->private_data; - if (offset == UART_LCR) - d->last_lcr = value; - if (offset == UART_MCR) d->last_mcr = value; writel(value, p->membase + (offset << p->regshift)); + + /* Make sure LCR write wasn't ignored */ + if (offset == UART_LCR) { + int tries = 1000; + while (tries--) { + if (value == p->serial_in(p, UART_LCR)) + return; + dw8250_force_idle(p); + writel(value, p->membase + (UART_LCR << p->regshift)); + } + dev_err(p->dev, "Couldn't set LCR to %d\n", value); + } } static unsigned int dw8250_serial_in32(struct uart_port *p, int offset) @@ -132,9 +156,8 @@ static int dw8250_handle_irq(struct uart_port *p) if (serial8250_handle_irq(p, iir)) { return 1; } else if ((iir & UART_IIR_BUSY) == UART_IIR_BUSY) { - /* Clear the USR and write the LCR again. */ + /* Clear the USR */ (void)p->serial_in(p, d->usr_reg); - p->serial_out(p, UART_LCR, d->last_lcr); return 1; }