summaryrefslogtreecommitdiffstats
path: root/drivers/tty
diff options
context:
space:
mode:
authorJoshua Scott <joshua.scott@alliedtelesis.co.nz>2018-02-08 22:59:56 +0100
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-02-28 13:40:22 +0100
commit914eaf935ec78d3a3ce03751b1e0f8395035d94a (patch)
tree753dc7a4933800b87c127c4b908fa7d00cf0f2b4 /drivers/tty
parentserial: sh-sci: use hrtimer for receive timeout (diff)
downloadlinux-914eaf935ec78d3a3ce03751b1e0f8395035d94a.tar.xz
linux-914eaf935ec78d3a3ce03751b1e0f8395035d94a.zip
serial: 8250_dw: Allow TX FIFO to drain before writing to UART_LCR
An issue has been observed on the Marvell Armada 38x serial port. Writes to UART_LCR can result in characters that are currently held in the TX FIFO being lost rather than sent, even if the userspace process has attempted to flush them. This is most visible when using the "resize" command (tested on Busybox), where we have observed the escape code for restoring cursor position becoming mangled. Signed-off-by: Joshua Scott <joshua.scott@alliedtelesis.co.nz> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty')
-rw-r--r--drivers/tty/serial/8250/8250_dw.c18
1 files changed, 18 insertions, 0 deletions
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index cd1b94a0f451..41618b780146 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -9,6 +9,7 @@
* LCR is written whilst busy. If it is, then a busy detect interrupt is
* raised, the LCR needs to be rewritten and the uart status register read.
*/
+#include <linux/delay.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/module.h>
@@ -119,10 +120,27 @@ static void dw8250_check_lcr(struct uart_port *p, int value)
*/
}
+/* Returns once the transmitter is empty or we run out of retries */
+static void dw8250_tx_wait_empty(struct uart_port *p, int tries)
+{
+ unsigned int lsr;
+
+ while (tries--) {
+ lsr = readb (p->membase + (UART_LSR << p->regshift));
+ if (lsr & UART_LSR_TEMT)
+ break;
+ udelay (10);
+ }
+}
+
static void dw8250_serial_out(struct uart_port *p, int offset, int value)
{
struct dw8250_data *d = p->private_data;
+ /* Allow the TX to drain before we reconfigure */
+ if (offset == UART_LCR)
+ dw8250_tx_wait_empty(p, 1000);
+
writeb(value, p->membase + (offset << p->regshift));
if (offset == UART_LCR && !d->uart_16550_compatible)