On Thu, Feb 01, 2024 at 06:01:12PM +0100, Konrad Dybcio wrote:
> The original commit assumed this hardware is long rotting in the
> junkyards, but apparently Qualcomm is still using some of these old
> servers internally and the thing can still run upstream.
>
> Adding insult to injury, I apparently managed to overdelete code and broke
> non-QDF2xxx platforms [1].
Thanks, as expected this fixes the issues on at least the Arm FVP for me:
Tested-by: Mark Brown <broonie@kernel.org>
@@ -152,6 +152,23 @@ static const struct vendor_data vendor_sbsa = {
.fixed_options = true,
};
+#ifdef CONFIG_ACPI_SPCR_TABLE
+static const 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,
+};
+#endif
+
static u16 pl011_st_offsets[REG_ARRAY_SIZE] = {
[REG_DR] = UART01x_DR,
[REG_ST_DMAWM] = ST_UART011_DMAWM,
@@ -2451,6 +2468,15 @@ static int pl011_console_match(struct console *co, char *name, int idx,
resource_size_t addr;
int i;
+ /*
+ * Systems affected by the Qualcomm Technologies QDF2400 E44 erratum
+ * have a distinct console name, so make sure we check for that.
+ * The actual implementation of the erratum occurs in the probe
+ * function.
+ */
+ if ((strcmp(name, "qdf2400_e44") != 0) && (strcmp(name, "pl011") != 0))
+ return -ENODEV;
+
if (uart_parse_earlycon(options, &iotype, &addr, &options))
return -ENODEV;
@@ -2491,6 +2517,22 @@ static struct console amba_console = {
#define AMBA_CONSOLE (&amba_console)
+static void qdf2400_e44_putc(struct uart_port *port, unsigned char 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 int 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, unsigned char c)
{
while (readl(port->membase + UART01x_FR) & UART01x_FR_TXFF)
@@ -2569,6 +2611,29 @@ OF_EARLYCON_DECLARE(pl011, "arm,pl011", pl011_early_console_setup);
OF_EARLYCON_DECLARE(pl011, "arm,sbsa-uart", pl011_early_console_setup);
+/*
+ * On Qualcomm Datacenter Technologies QDF2400 SOCs affected by
+ * Erratum 44, traditional earlycon can be enabled by specifying
+ * "earlycon=qdf2400_e44,<address>". Any options are ignored.
+ *
+ * Alternatively, you can just specify "earlycon", and the early console
+ * will be enabled with the information from the SPCR table. In this
+ * case, the SPCR code will detect the need for the E44 work-around,
+ * and set the console name to "qdf2400_e44".
+ */
+static int __init
+qdf2400_e44_early_console_setup(struct earlycon_device *device,
+ const char *opt)
+{
+ if (!device->port.membase)
+ return -ENODEV;
+
+ device->con->write = qdf2400_e44_early_write;
+ return 0;
+}
+
+EARLYCON_DECLARE(qdf2400_e44, qdf2400_e44_early_console_setup);
+
#else
#define AMBA_CONSOLE NULL
#endif
@@ -2804,6 +2869,22 @@ static int pl011_resume(struct device *dev)
static SIMPLE_DEV_PM_OPS(pl011_dev_pm_ops, pl011_suspend, pl011_resume);
+#ifdef CONFIG_ACPI_SPCR_TABLE
+static void qpdf2400_erratum44_workaround(struct device *dev,
+ struct uart_amba_port *uap)
+{
+ if (!qdf2400_e44_present)
+ return;
+
+ dev_info(dev, "working around QDF2400 SoC erratum 44\n");
+ uap->vendor = &vendor_qdt_qdf2400_e44;
+}
+#else
+static void qpdf2400_erratum44_workaround(struct device *dev,
+ struct uart_amba_port *uap)
+{ /* empty */ }
+#endif
+
static int sbsa_uart_probe(struct platform_device *pdev)
{
struct uart_amba_port *uap;
@@ -2840,6 +2921,7 @@ static int sbsa_uart_probe(struct platform_device *pdev)
uap->port.irq = ret;
uap->vendor = &vendor_sbsa;
+ qpdf2400_erratum44_workaround(&pdev->dev, uap);
uap->reg_offset = uap->vendor->reg_offset;
uap->fifosize = 32;