Commit 37ef38f3 authored by Timur Tabi's avatar Timur Tabi Committed by Greg Kroah-Hartman

tty: pl011: fix initialization order of QDF2400 E44

The work-around for Qualcomm Technologies QDF2400 Erratum 44 hinges on a
global variable defined in the pl011 driver.  The ACPI SPCR parsing code
determines whether the work-around is needed, and if so, it changes the
console name from "pl011" to "qdf2400_e44".  The expectation is that
the pl011 driver will implement the work-around when it sees the console
name.  The global variable qdf2400_e44_present is set when that happens.

The problem is that work-around needs to be enabled when the pl011
driver probes, not when the console name is queried.  However, sbsa_probe()
is called before pl011_console_match().  The work-around appeared to work
previously because the default console on QDF2400 platforms was always
ttyAMA1.  The first time sbsa_probe() is called (for ttyAMA0),
qdf2400_e44_present is still false.  Then pl011_console_match() is called,
and it sets qdf2400_e44_present to true.  All subsequent calls to
sbsa_probe() enable the work-around.

The solution is to move the global variable into spcr.c and let the
pl011 driver query it during probe time.  This works because all QDF2400
platforms require SPCR, so parse_spcr() will always be called.
pl011_console_match still checks for the "qdf2400_e44" console name,
but it doesn't do anything else special.

Fixes: 5a0722b8 ("tty: pl011: use "qdf2400_e44" as the earlycon name for QDF2400 E44")
Tested-by: default avatarJeffrey Hugo <jhugo@codeaurora.org>
Signed-off-by: default avatarTimur Tabi <timur@codeaurora.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 520eccdf
...@@ -16,6 +16,16 @@ ...@@ -16,6 +16,16 @@
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/serial_core.h> #include <linux/serial_core.h>
/*
* 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().
*/
bool qdf2400_e44_present;
EXPORT_SYMBOL(qdf2400_e44_present);
/* /*
* Some Qualcomm Datacenter Technologies SoCs have a defective UART BUSY bit. * Some Qualcomm Datacenter Technologies SoCs have a defective UART BUSY bit.
* Detect them by examining the OEM fields in the SPCR header, similiar to PCI * Detect them by examining the OEM fields in the SPCR header, similiar to PCI
...@@ -147,8 +157,30 @@ int __init parse_spcr(bool earlycon) ...@@ -147,8 +157,30 @@ int __init parse_spcr(bool earlycon)
goto done; goto done;
} }
if (qdf2400_erratum_44_present(&table->header)) /*
uart = "qdf2400_e44"; * If the E44 erratum is required, then we need to tell the pl011
* driver to implement the work-around.
*
* The global variable is used by the probe function when it
* creates the UARTs, whether or not they're used as a console.
*
* If the user specifies "traditional" earlycon, the qdf2400_e44
* console name matches the EARLYCON_DECLARE() statement, and
* SPCR is not used. Parameter "earlycon" is false.
*
* If the user specifies "SPCR" earlycon, then we need to update
* the console name so that it also says "qdf2400_e44". Parameter
* "earlycon" is true.
*
* For consistency, if we change the console name, then we do it
* for everyone, not just earlycon.
*/
if (qdf2400_erratum_44_present(&table->header)) {
qdf2400_e44_present = true;
if (earlycon)
uart = "qdf2400_e44";
}
if (xgene_8250_erratum_present(table)) if (xgene_8250_erratum_present(table))
iotype = "mmio32"; iotype = "mmio32";
......
...@@ -142,15 +142,7 @@ static struct vendor_data vendor_sbsa = { ...@@ -142,15 +142,7 @@ static struct vendor_data vendor_sbsa = {
.fixed_options = true, .fixed_options = true,
}; };
/* #ifdef CONFIG_ACPI_SPCR_TABLE
* 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 = { static struct vendor_data vendor_qdt_qdf2400_e44 = {
.reg_offset = pl011_std_offsets, .reg_offset = pl011_std_offsets,
.fr_busy = UART011_FR_TXFE, .fr_busy = UART011_FR_TXFE,
...@@ -165,6 +157,7 @@ static struct vendor_data vendor_qdt_qdf2400_e44 = { ...@@ -165,6 +157,7 @@ static struct vendor_data vendor_qdt_qdf2400_e44 = {
.always_enabled = true, .always_enabled = true,
.fixed_options = true, .fixed_options = true,
}; };
#endif
static u16 pl011_st_offsets[REG_ARRAY_SIZE] = { static u16 pl011_st_offsets[REG_ARRAY_SIZE] = {
[REG_DR] = UART01x_DR, [REG_DR] = UART01x_DR,
...@@ -2375,12 +2368,14 @@ static int __init pl011_console_match(struct console *co, char *name, int idx, ...@@ -2375,12 +2368,14 @@ static int __init pl011_console_match(struct console *co, char *name, int idx,
resource_size_t addr; resource_size_t addr;
int i; int i;
if (strcmp(name, "qdf2400_e44") == 0) { /*
pr_info_once("UART: Working around QDF2400 SoC erratum 44"); * Systems affected by the Qualcomm Technologies QDF2400 E44 erratum
qdf2400_e44_present = true; * have a distinct console name, so make sure we check for that.
} else if (strcmp(name, "pl011") != 0) { * The actual implementation of the erratum occurs in the probe
* function.
*/
if ((strcmp(name, "qdf2400_e44") != 0) && (strcmp(name, "pl011") != 0))
return -ENODEV; return -ENODEV;
}
if (uart_parse_earlycon(options, &iotype, &addr, &options)) if (uart_parse_earlycon(options, &iotype, &addr, &options))
return -ENODEV; return -ENODEV;
...@@ -2734,11 +2729,17 @@ static int sbsa_uart_probe(struct platform_device *pdev) ...@@ -2734,11 +2729,17 @@ static int sbsa_uart_probe(struct platform_device *pdev)
} }
uap->port.irq = ret; uap->port.irq = ret;
uap->reg_offset = vendor_sbsa.reg_offset; #ifdef CONFIG_ACPI_SPCR_TABLE
uap->vendor = qdf2400_e44_present ? if (qdf2400_e44_present) {
&vendor_qdt_qdf2400_e44 : &vendor_sbsa; dev_info(&pdev->dev, "working around QDF2400 SoC erratum 44\n");
uap->vendor = &vendor_qdt_qdf2400_e44;
} else
#endif
uap->vendor = &vendor_sbsa;
uap->reg_offset = uap->vendor->reg_offset;
uap->fifosize = 32; uap->fifosize = 32;
uap->port.iotype = vendor_sbsa.access_32b ? UPIO_MEM32 : UPIO_MEM; uap->port.iotype = uap->vendor->access_32b ? UPIO_MEM32 : UPIO_MEM;
uap->port.ops = &sbsa_uart_pops; uap->port.ops = &sbsa_uart_pops;
uap->fixed_baud = baudrate; uap->fixed_baud = baudrate;
......
...@@ -1209,6 +1209,7 @@ static inline bool acpi_has_watchdog(void) { return false; } ...@@ -1209,6 +1209,7 @@ static inline bool acpi_has_watchdog(void) { return false; }
#endif #endif
#ifdef CONFIG_ACPI_SPCR_TABLE #ifdef CONFIG_ACPI_SPCR_TABLE
extern bool qdf2400_e44_present;
int parse_spcr(bool earlycon); int parse_spcr(bool earlycon);
#else #else
static inline int parse_spcr(bool earlycon) { return 0; } static inline int parse_spcr(bool earlycon) { return 0; }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment