Re: [PATCH v2] serial: 8250_dw: Improve unwritable LCR workaround

Heikki Krogerus
2013-10-02T09:47:12Z

Hi,

On Tue, Oct 01, 2013 at 10:18:08AM -0700, Tim Kryger wrote:
> When configured with UART_16550_COMPATIBLE=NO or in versions prior to
> the introduction of this option, the Designware UART will ignore writes
> to the LCR if the UART is busy. The current workaround saves a copy of
> the last written LCR and re-writes it in the ISR for a special interrupt
> that is raised when a write was ignored.
>
> Unfortunately, interrupts are typically disabled prior to performing a
> sequence of register writes that include the LCR so the point at which
> the retry occurs is too late. An example is serial8250_do_set_termios()
> where an ignored LCR write results in the baud divisor not being set and
> instead a garbage character is sent out the transmitter.
>
> Furthermore, since serial_port_out() offers no way to indicate failure,
> a serious effort must be made to ensure that the LCR is actually updated
> before returning back to the caller. This is difficult, however, as a
> UART that was busy during the first attempt is likely to still be busy
> when a subsequent attempt is made unless some extra action is taken.
>
> This updated workaround reads back the LCR after each write to confirm
> that the new value was accepted by the hardware. Should the hardware
> ignore a write, the TX/RX FIFOs are cleared and the receive buffer read
> before attempting to rewrite the LCR out of the hope that doing so will
> force the UART into an idle state. While this may seem unnecessarily
> aggressive, writes to the LCR are used to change the baud rate, parity,
> stop bit, or data length so the data that may be lost is likely not
> important. Admittedly, this is far from ideal but it seems to be the
> best that can be done given the hardware limitations.
>
> Lastly, the revised workaround doesn't touch the LCR in the ISR, so it
> avoids the possibility of a "serial8250: too much work for irq" lock up.
> This problem is rare in real situations but can be reproduced easily by
> wiring up two UARTs and running the following commands.
>
> # stty -F /dev/ttyS1 echo
> # stty -F /dev/ttyS2 echo
> # cat /dev/ttyS1 &
> [1] 375
> # echo asdf > /dev/ttyS1
> asdf
>
> [ 27.700000] serial8250: too much work for irq96
> [ 27.700000] serial8250: too much work for irq96
> [ 27.710000] serial8250: too much work for irq96
> [ 27.710000] serial8250: too much work for irq96
> [ 27.720000] serial8250: too much work for irq96
> [ 27.720000] serial8250: too much work for irq96
> [ 27.730000] serial8250: too much work for irq96
> [ 27.730000] serial8250: too much work for irq96
> [ 27.740000] serial8250: too much work for irq96
>
> Signed-off-by: Tim Kryger <tim.kryger@linaro.org>
> Reviewed-by: Matt Porter <matt.porter@linaro.org>
> Reviewed-by: Markus Mayer <markus.mayer@linaro.org>
> ---
>
> Changes in v2:
> - Rebased on tty-next
> - Updated commit messsage to mention UART_16550_COMPATIBLE
> - Removed potentially unnecessary read of LSR and MSR
> - Only attempt workaround when LCR write is ignored

I'm OK with this.

Reviewed-by: Heikki Krogerus <heikki.krogerus@linux.intel.com>

Br,

--
heikki