linux/drivers/acpi/spcr.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2012, Intel Corporation
   4 * Copyright (c) 2015, Red Hat, Inc.
   5 * Copyright (c) 2015, 2016 Linaro Ltd.
   6 */
   7
   8#define pr_fmt(fmt) "ACPI: SPCR: " fmt
   9
  10#include <linux/acpi.h>
  11#include <linux/console.h>
  12#include <linux/kernel.h>
  13#include <linux/serial_core.h>
  14
  15/*
  16 * Erratum 44 for QDF2432v1 and QDF2400v1 SoCs describes the BUSY bit as
  17 * occasionally getting stuck as 1. To avoid the potential for a hang, check
  18 * TXFE == 0 instead of BUSY == 1. This may not be suitable for all UART
  19 * implementations, so only do so if an affected platform is detected in
  20 * acpi_parse_spcr().
  21 */
  22bool qdf2400_e44_present;
  23EXPORT_SYMBOL(qdf2400_e44_present);
  24
  25/*
  26 * Some Qualcomm Datacenter Technologies SoCs have a defective UART BUSY bit.
  27 * Detect them by examining the OEM fields in the SPCR header, similar to PCI
  28 * quirk detection in pci_mcfg.c.
  29 */
  30static bool qdf2400_erratum_44_present(struct acpi_table_header *h)
  31{
  32        if (memcmp(h->oem_id, "QCOM  ", ACPI_OEM_ID_SIZE))
  33                return false;
  34
  35        if (!memcmp(h->oem_table_id, "QDF2432 ", ACPI_OEM_TABLE_ID_SIZE))
  36                return true;
  37
  38        if (!memcmp(h->oem_table_id, "QDF2400 ", ACPI_OEM_TABLE_ID_SIZE) &&
  39                        h->oem_revision == 1)
  40                return true;
  41
  42        return false;
  43}
  44
  45/*
  46 * APM X-Gene v1 and v2 UART hardware is an 16550 like device but has its
  47 * register aligned to 32-bit. In addition, the BIOS also encoded the
  48 * access width to be 8 bits. This function detects this errata condition.
  49 */
  50static bool xgene_8250_erratum_present(struct acpi_table_spcr *tb)
  51{
  52        bool xgene_8250 = false;
  53
  54        if (tb->interface_type != ACPI_DBG2_16550_COMPATIBLE)
  55                return false;
  56
  57        if (memcmp(tb->header.oem_id, "APMC0D", ACPI_OEM_ID_SIZE) &&
  58            memcmp(tb->header.oem_id, "HPE   ", ACPI_OEM_ID_SIZE))
  59                return false;
  60
  61        if (!memcmp(tb->header.oem_table_id, "XGENESPC",
  62            ACPI_OEM_TABLE_ID_SIZE) && tb->header.oem_revision == 0)
  63                xgene_8250 = true;
  64
  65        if (!memcmp(tb->header.oem_table_id, "ProLiant",
  66            ACPI_OEM_TABLE_ID_SIZE) && tb->header.oem_revision == 1)
  67                xgene_8250 = true;
  68
  69        return xgene_8250;
  70}
  71
  72/**
  73 * acpi_parse_spcr() - parse ACPI SPCR table and add preferred console
  74 *
  75 * @enable_earlycon: set up earlycon for the console specified by the table
  76 * @enable_console: setup the console specified by the table.
  77 *
  78 * For the architectures with support for ACPI, CONFIG_ACPI_SPCR_TABLE may be
  79 * defined to parse ACPI SPCR table.  As a result of the parsing preferred
  80 * console is registered and if @enable_earlycon is true, earlycon is set up.
  81 * If @enable_console is true the system console is also configured.
  82 *
  83 * When CONFIG_ACPI_SPCR_TABLE is defined, this function should be called
  84 * from arch initialization code as soon as the DT/ACPI decision is made.
  85 *
  86 */
  87int __init acpi_parse_spcr(bool enable_earlycon, bool enable_console)
  88{
  89        static char opts[64];
  90        struct acpi_table_spcr *table;
  91        acpi_status status;
  92        char *uart;
  93        char *iotype;
  94        int baud_rate;
  95        int err;
  96
  97        if (acpi_disabled)
  98                return -ENODEV;
  99
 100        status = acpi_get_table(ACPI_SIG_SPCR, 0,
 101                                (struct acpi_table_header **)&table);
 102
 103        if (ACPI_FAILURE(status))
 104                return -ENOENT;
 105
 106        if (table->header.revision < 2)
 107                pr_info("SPCR table version %d\n", table->header.revision);
 108
 109        if (table->serial_port.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
 110                switch (ACPI_ACCESS_BIT_WIDTH((
 111                        table->serial_port.access_width))) {
 112                default:
 113                        pr_err("Unexpected SPCR Access Width.  Defaulting to byte size\n");
 114                        fallthrough;
 115                case 8:
 116                        iotype = "mmio";
 117                        break;
 118                case 16:
 119                        iotype = "mmio16";
 120                        break;
 121                case 32:
 122                        iotype = "mmio32";
 123                        break;
 124                }
 125        } else
 126                iotype = "io";
 127
 128        switch (table->interface_type) {
 129        case ACPI_DBG2_ARM_SBSA_32BIT:
 130                iotype = "mmio32";
 131                fallthrough;
 132        case ACPI_DBG2_ARM_PL011:
 133        case ACPI_DBG2_ARM_SBSA_GENERIC:
 134        case ACPI_DBG2_BCM2835:
 135                uart = "pl011";
 136                break;
 137        case ACPI_DBG2_16550_COMPATIBLE:
 138        case ACPI_DBG2_16550_SUBSET:
 139        case ACPI_DBG2_16550_WITH_GAS:
 140                uart = "uart";
 141                break;
 142        default:
 143                err = -ENOENT;
 144                goto done;
 145        }
 146
 147        switch (table->baud_rate) {
 148        case 0:
 149                /*
 150                 * SPCR 1.04 defines 0 as a preconfigured state of UART.
 151                 * Assume firmware or bootloader configures console correctly.
 152                 */
 153                baud_rate = 0;
 154                break;
 155        case 3:
 156                baud_rate = 9600;
 157                break;
 158        case 4:
 159                baud_rate = 19200;
 160                break;
 161        case 6:
 162                baud_rate = 57600;
 163                break;
 164        case 7:
 165                baud_rate = 115200;
 166                break;
 167        default:
 168                err = -ENOENT;
 169                goto done;
 170        }
 171
 172        /*
 173         * If the E44 erratum is required, then we need to tell the pl011
 174         * driver to implement the work-around.
 175         *
 176         * The global variable is used by the probe function when it
 177         * creates the UARTs, whether or not they're used as a console.
 178         *
 179         * If the user specifies "traditional" earlycon, the qdf2400_e44
 180         * console name matches the EARLYCON_DECLARE() statement, and
 181         * SPCR is not used.  Parameter "earlycon" is false.
 182         *
 183         * If the user specifies "SPCR" earlycon, then we need to update
 184         * the console name so that it also says "qdf2400_e44".  Parameter
 185         * "earlycon" is true.
 186         *
 187         * For consistency, if we change the console name, then we do it
 188         * for everyone, not just earlycon.
 189         */
 190        if (qdf2400_erratum_44_present(&table->header)) {
 191                qdf2400_e44_present = true;
 192                if (enable_earlycon)
 193                        uart = "qdf2400_e44";
 194        }
 195
 196        if (xgene_8250_erratum_present(table)) {
 197                iotype = "mmio32";
 198
 199                /* for xgene v1 and v2 we don't know the clock rate of the
 200                 * UART so don't attempt to change to the baud rate state
 201                 * in the table because driver cannot calculate the dividers
 202                 */
 203                baud_rate = 0;
 204        }
 205
 206        if (!baud_rate) {
 207                snprintf(opts, sizeof(opts), "%s,%s,0x%llx", uart, iotype,
 208                         table->serial_port.address);
 209        } else {
 210                snprintf(opts, sizeof(opts), "%s,%s,0x%llx,%d", uart, iotype,
 211                         table->serial_port.address, baud_rate);
 212        }
 213
 214        pr_info("console: %s\n", opts);
 215
 216        if (enable_earlycon)
 217                setup_earlycon(opts);
 218
 219        if (enable_console)
 220                err = add_preferred_console(uart, 0, opts + strlen(uart) + 1);
 221        else
 222                err = 0;
 223done:
 224        acpi_put_table((struct acpi_table_header *)table);
 225        return err;
 226}
 227