|
|
@@ -97,6 +97,7 @@ struct vendor_data {
|
|
|
unsigned int fr_dsr;
|
|
|
unsigned int fr_cts;
|
|
|
unsigned int fr_ri;
|
|
|
+ unsigned int inv_fr;
|
|
|
bool access_32b;
|
|
|
bool oversampling;
|
|
|
bool dma_threshold;
|
|
|
@@ -141,6 +142,30 @@ static struct vendor_data vendor_sbsa = {
|
|
|
.fixed_options = true,
|
|
|
};
|
|
|
|
|
|
+/*
|
|
|
+ * Erratum 44 for QDF2432v1 and QDF2400v1 SoCs describes the BUSY bit as
|
|
|
+ * occasionally getting stuck as 1. To avoid the potential for a hang, check
|
|
|
+ * TXFE == 0 instead of BUSY == 1. This may not be suitable for all UART
|
|
|
+ * implementations, so only do so if an affected platform is detected in
|
|
|
+ * parse_spcr().
|
|
|
+ */
|
|
|
+static bool qdf2400_e44_present = false;
|
|
|
+
|
|
|
+static struct vendor_data vendor_qdt_qdf2400_e44 = {
|
|
|
+ .reg_offset = pl011_std_offsets,
|
|
|
+ .fr_busy = UART011_FR_TXFE,
|
|
|
+ .fr_dsr = UART01x_FR_DSR,
|
|
|
+ .fr_cts = UART01x_FR_CTS,
|
|
|
+ .fr_ri = UART011_FR_RI,
|
|
|
+ .inv_fr = UART011_FR_TXFE,
|
|
|
+ .access_32b = true,
|
|
|
+ .oversampling = false,
|
|
|
+ .dma_threshold = false,
|
|
|
+ .cts_event_workaround = false,
|
|
|
+ .always_enabled = true,
|
|
|
+ .fixed_options = true,
|
|
|
+};
|
|
|
+
|
|
|
static u16 pl011_st_offsets[REG_ARRAY_SIZE] = {
|
|
|
[REG_DR] = UART01x_DR,
|
|
|
[REG_ST_DMAWM] = ST_UART011_DMAWM,
|
|
|
@@ -1518,7 +1543,10 @@ 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);
|
|
|
+
|
|
|
+ /* Allow feature register bits to be inverted to work around errata */
|
|
|
+ unsigned int status = pl011_read(uap, REG_FR) ^ uap->vendor->inv_fr;
|
|
|
+
|
|
|
return status & (uap->vendor->fr_busy | UART01x_FR_TXFF) ?
|
|
|
0 : TIOCSER_TEMT;
|
|
|
}
|
|
|
@@ -2215,10 +2243,12 @@ pl011_console_write(struct console *co, const char *s, unsigned int count)
|
|
|
uart_console_write(&uap->port, s, count, pl011_console_putchar);
|
|
|
|
|
|
/*
|
|
|
- * Finally, wait for transmitter to become empty
|
|
|
- * and restore the TCR
|
|
|
+ * Finally, wait for transmitter to become empty and restore the
|
|
|
+ * TCR. Allow feature register bits to be inverted to work around
|
|
|
+ * errata.
|
|
|
*/
|
|
|
- while (pl011_read(uap, REG_FR) & uap->vendor->fr_busy)
|
|
|
+ while ((pl011_read(uap, REG_FR) ^ uap->vendor->inv_fr)
|
|
|
+ & uap->vendor->fr_busy)
|
|
|
cpu_relax();
|
|
|
if (!uap->vendor->always_enabled)
|
|
|
pl011_write(old_cr, uap, REG_CR);
|
|
|
@@ -2340,8 +2370,12 @@ static int __init pl011_console_match(struct console *co, char *name, int idx,
|
|
|
resource_size_t addr;
|
|
|
int i;
|
|
|
|
|
|
- if (strcmp(name, "pl011") != 0 || strcmp(name, "ttyAMA") != 0)
|
|
|
+ if (strcmp(name, "qdf2400_e44") == 0) {
|
|
|
+ pr_info_once("UART: Working around QDF2400 SoC erratum 44");
|
|
|
+ qdf2400_e44_present = true;
|
|
|
+ } else if (strcmp(name, "pl011") != 0 || strcmp(name, "ttyAMA") != 0) {
|
|
|
return -ENODEV;
|
|
|
+ }
|
|
|
|
|
|
if (uart_parse_earlycon(options, &iotype, &addr, &options))
|
|
|
return -ENODEV;
|
|
|
@@ -2383,6 +2417,22 @@ static struct console amba_console = {
|
|
|
|
|
|
#define AMBA_CONSOLE (&amba_console)
|
|
|
|
|
|
+static void qdf2400_e44_putc(struct uart_port *port, int c)
|
|
|
+{
|
|
|
+ while (readl(port->membase + UART01x_FR) & UART01x_FR_TXFF)
|
|
|
+ cpu_relax();
|
|
|
+ writel(c, port->membase + UART01x_DR);
|
|
|
+ while (!(readl(port->membase + UART01x_FR) & UART011_FR_TXFE))
|
|
|
+ cpu_relax();
|
|
|
+}
|
|
|
+
|
|
|
+static void qdf2400_e44_early_write(struct console *con, const char *s, unsigned n)
|
|
|
+{
|
|
|
+ struct earlycon_device *dev = con->data;
|
|
|
+
|
|
|
+ uart_console_write(&dev->port, s, n, qdf2400_e44_putc);
|
|
|
+}
|
|
|
+
|
|
|
static void pl011_putc(struct uart_port *port, int c)
|
|
|
{
|
|
|
while (readl(port->membase + UART01x_FR) & UART01x_FR_TXFF)
|
|
|
@@ -2408,7 +2458,8 @@ static int __init pl011_early_console_setup(struct earlycon_device *device,
|
|
|
if (!device->port.membase)
|
|
|
return -ENODEV;
|
|
|
|
|
|
- device->con->write = pl011_early_write;
|
|
|
+ device->con->write = qdf2400_e44_present ?
|
|
|
+ qdf2400_e44_early_write : pl011_early_write;
|
|
|
return 0;
|
|
|
}
|
|
|
OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup);
|
|
|
@@ -2645,7 +2696,8 @@ static int sbsa_uart_probe(struct platform_device *pdev)
|
|
|
uap->port.irq = ret;
|
|
|
|
|
|
uap->reg_offset = vendor_sbsa.reg_offset;
|
|
|
- uap->vendor = &vendor_sbsa;
|
|
|
+ uap->vendor = qdf2400_e44_present ?
|
|
|
+ &vendor_qdt_qdf2400_e44 : &vendor_sbsa;
|
|
|
uap->fifosize = 32;
|
|
|
uap->port.iotype = vendor_sbsa.access_32b ? UPIO_MEM32 : UPIO_MEM;
|
|
|
uap->port.ops = &sbsa_uart_pops;
|