OSDN Git Service

serial: 8250_dw: Limit dw8250_tx_wait_empty quirk to armada-38x devices
authorJoshua Scott <joshua.scott@alliedtelesis.co.nz>
Fri, 16 Mar 2018 00:42:00 +0000 (13:42 +1300)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Mon, 23 Apr 2018 08:11:13 +0000 (10:11 +0200)
The previous implementation has had a detrimental effect on devices using
high bitrates (bluetooth), as the fifo being non-empty for a single check
would result in a 10 µs delay.

Limit the change to devices with the new "marvell,armada-38x-uart"
compatible string. Also update the code to allow the first 1000 retries
to not perform a delay.

The maximum duration of retries has been increased to cover a worst-case
seen on the Armada 385 SoC. "dmesg ; resize", will fill the buffer with
text to output before doing a resize. At 9600 baud this took up to 13 ms
to flush all characters and avoid some getting lost.

Signed-off-by: Joshua Scott <joshua.scott@alliedtelesis.co.nz>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@linux.intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
arch/arm/boot/dts/armada-38x.dtsi
drivers/tty/serial/8250/8250_dw.c

index 4cc09e4..6916d75 100644 (file)
                        };
 
                        uart0: serial@12000 {
-                               compatible = "snps,dw-apb-uart";
+                               compatible = "marvell,armada-38x-uart";
                                reg = <0x12000 0x100>;
                                reg-shift = <2>;
                                interrupts = <GIC_SPI 12 IRQ_TYPE_LEVEL_HIGH>;
                        };
 
                        uart1: serial@12100 {
-                               compatible = "snps,dw-apb-uart";
+                               compatible = "marvell,armada-38x-uart";
                                reg = <0x12100 0x100>;
                                reg-shift = <2>;
                                interrupts = <GIC_SPI 13 IRQ_TYPE_LEVEL_HIGH>;
index 6fcdb90..0529b5c 100644 (file)
@@ -121,25 +121,44 @@ 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)
+static void dw8250_tx_wait_empty(struct uart_port *p)
 {
+       unsigned int tries = 20000;
+       unsigned int delay_threshold = tries - 1000;
        unsigned int lsr;
 
        while (tries--) {
                lsr = readb (p->membase + (UART_LSR << p->regshift));
                if (lsr & UART_LSR_TEMT)
                        break;
-               udelay (10);
+
+               /* The device is first given a chance to empty without delay,
+                * to avoid slowdowns at high bitrates. If after 1000 tries
+                * the buffer has still not emptied, allow more time for low-
+                * speed links. */
+               if (tries < delay_threshold)
+                       udelay (1);
        }
 }
 
-static void dw8250_serial_out(struct uart_port *p, int offset, int value)
+static void dw8250_serial_out38x(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);
+               dw8250_tx_wait_empty(p);
+
+       writeb(value, p->membase + (offset << p->regshift));
+
+       if (offset == UART_LCR && !d->uart_16550_compatible)
+               dw8250_check_lcr(p, value);
+}
+
+
+static void dw8250_serial_out(struct uart_port *p, int offset, int value)
+{
+       struct dw8250_data *d = p->private_data;
 
        writeb(value, p->membase + (offset << p->regshift));
 
@@ -357,6 +376,9 @@ static void dw8250_quirks(struct uart_port *p, struct dw8250_data *data)
                        p->serial_in = dw8250_serial_in32be;
                        p->serial_out = dw8250_serial_out32be;
                }
+               if (of_device_is_compatible(np, "marvell,armada-38x-uart"))
+                       p->serial_out = dw8250_serial_out38x;
+
        } else if (acpi_dev_present("APMC0D08", NULL, -1)) {
                p->iotype = UPIO_MEM32;
                p->regshift = 2;
@@ -666,6 +688,7 @@ static const struct dev_pm_ops dw8250_pm_ops = {
 static const struct of_device_id dw8250_of_match[] = {
        { .compatible = "snps,dw-apb-uart" },
        { .compatible = "cavium,octeon-3860-uart" },
+       { .compatible = "marvell,armada-38x-uart" },
        { /* Sentinel */ }
 };
 MODULE_DEVICE_TABLE(of, dw8250_of_match);