diff options
author | Graeme Gregory <graeme.gregory@linaro.org> | 2016-11-11 15:05:35 +0000 |
---|---|---|
committer | Graeme Gregory <graeme.gregory@linaro.org> | 2016-11-11 15:05:35 +0000 |
commit | 265af1bc73f3b7d1ec8ebf4035e7149bf5ea8953 (patch) | |
tree | a504d2ea57a0c67acdf29cd72300d25baeeeb6b3 | |
parent | b0db78c7d5775eb5ec1957768b989b35fd98b3a8 (diff) | |
parent | 322089477a34c3555ac86ff294b6dacd78b5b0ba (diff) |
Merge branch 'erp49/topic-qualcomm-workarounds-uart' into erp49/leg-kernelleg-erp-20161111.1leg-erp-10161111.1
-rw-r--r-- | drivers/tty/serial/amba-pl011.c | 38 |
1 files changed, 32 insertions, 6 deletions
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c index c00ab22afe9e..0704eaf533ad 100644 --- a/drivers/tty/serial/amba-pl011.c +++ b/drivers/tty/serial/amba-pl011.c @@ -73,6 +73,8 @@ #define UART_DR_ERROR (UART011_DR_OE|UART011_DR_BE|UART011_DR_PE|UART011_DR_FE) #define UART_DUMMY_DR_RX (1 << 16) +static bool pl011_workaround_busy; + static u16 pl011_std_offsets[REG_ARRAY_SIZE] = { [REG_DR] = UART01x_DR, [REG_FR] = UART01x_FR, @@ -1519,8 +1521,13 @@ static unsigned int pl011_tx_empty(struct uart_port *port) struct uart_amba_port *uap = container_of(port, struct uart_amba_port, port); unsigned int status = pl011_read(uap, REG_FR); - return status & (uap->vendor->fr_busy | UART01x_FR_TXFF) ? - 0 : TIOCSER_TEMT; + + if (unlikely(pl011_workaround_busy)) + return status & UART011_FR_TXFE ? TIOCSER_TEMT : 0; + else + return status & (uap->vendor->fr_busy | UART01x_FR_TXFF) ? + 0 : + TIOCSER_TEMT; } static unsigned int pl011_get_mctrl(struct uart_port *port) @@ -2218,8 +2225,14 @@ pl011_console_write(struct console *co, const char *s, unsigned int count) * Finally, wait for transmitter to become empty * and restore the TCR */ - while (pl011_read(uap, REG_FR) & uap->vendor->fr_busy) - cpu_relax(); + + if (unlikely(pl011_workaround_busy)) { + while (!(pl011_read(uap, REG_FR) & UART011_FR_TXFE)) + cpu_relax(); + } else { + while (pl011_read(uap, REG_FR) & uap->vendor->fr_busy) + cpu_relax(); + } if (!uap->vendor->always_enabled) pl011_write(old_cr, uap, REG_CR); @@ -2391,8 +2404,13 @@ static void pl011_putc(struct uart_port *port, int c) writel(c, port->membase + UART01x_DR); else writeb(c, port->membase + UART01x_DR); - while (readl(port->membase + UART01x_FR) & UART01x_FR_BUSY) - cpu_relax(); + if (unlikely(pl011_workaround_busy)) { + while (!(readl(port->membase + UART01x_FR) & UART011_FR_TXFE)) + cpu_relax(); + } else { + while (readl(port->membase + UART01x_FR) & UART01x_FR_BUSY) + cpu_relax(); + } } static void pl011_early_write(struct console *con, const char *s, unsigned n) @@ -2408,6 +2426,10 @@ static int __init pl011_early_console_setup(struct earlycon_device *device, if (!device->port.membase) return -ENODEV; + if (read_cpuid_implementor() == ARM_CPU_IMP_QCOM && + read_cpuid_part_number() == QCOM_CPU_PART_KRYO) + pl011_workaround_busy = true; + device->con->write = pl011_early_write; return 0; } @@ -2658,6 +2680,10 @@ static int sbsa_uart_probe(struct platform_device *pdev) if (ret) return ret; + if (read_cpuid_implementor() == ARM_CPU_IMP_QCOM && + read_cpuid_part_number() == QCOM_CPU_PART_KRYO) + pl011_workaround_busy = true; + platform_set_drvdata(pdev, uap); return pl011_register_port(uap); |