xref: /openbmc/linux/drivers/acpi/spcr.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
2ad1696f6SAleksey Makarov /*
3ad1696f6SAleksey Makarov  * Copyright (c) 2012, Intel Corporation
4ad1696f6SAleksey Makarov  * Copyright (c) 2015, Red Hat, Inc.
5ad1696f6SAleksey Makarov  * Copyright (c) 2015, 2016 Linaro Ltd.
6ad1696f6SAleksey Makarov  */
7ad1696f6SAleksey Makarov 
8ad1696f6SAleksey Makarov #define pr_fmt(fmt) "ACPI: SPCR: " fmt
9ad1696f6SAleksey Makarov 
10ad1696f6SAleksey Makarov #include <linux/acpi.h>
11ad1696f6SAleksey Makarov #include <linux/console.h>
12ad1696f6SAleksey Makarov #include <linux/kernel.h>
13ad1696f6SAleksey Makarov #include <linux/serial_core.h>
14ad1696f6SAleksey Makarov 
15d8a4995bSChristopher Covington /*
1637ef38f3STimur Tabi  * Erratum 44 for QDF2432v1 and QDF2400v1 SoCs describes the BUSY bit as
1737ef38f3STimur Tabi  * occasionally getting stuck as 1. To avoid the potential for a hang, check
1837ef38f3STimur Tabi  * TXFE == 0 instead of BUSY == 1. This may not be suitable for all UART
1937ef38f3STimur Tabi  * implementations, so only do so if an affected platform is detected in
200231d000SPrarit Bhargava  * acpi_parse_spcr().
2137ef38f3STimur Tabi  */
2237ef38f3STimur Tabi bool qdf2400_e44_present;
2337ef38f3STimur Tabi EXPORT_SYMBOL(qdf2400_e44_present);
2437ef38f3STimur Tabi 
2537ef38f3STimur Tabi /*
26d8a4995bSChristopher Covington  * Some Qualcomm Datacenter Technologies SoCs have a defective UART BUSY bit.
27603fadf3SBjorn Helgaas  * Detect them by examining the OEM fields in the SPCR header, similar to PCI
28d8a4995bSChristopher Covington  * quirk detection in pci_mcfg.c.
29d8a4995bSChristopher Covington  */
qdf2400_erratum_44_present(struct acpi_table_header * h)30d8a4995bSChristopher Covington static bool qdf2400_erratum_44_present(struct acpi_table_header *h)
31d8a4995bSChristopher Covington {
32d8a4995bSChristopher Covington 	if (memcmp(h->oem_id, "QCOM  ", ACPI_OEM_ID_SIZE))
33d8a4995bSChristopher Covington 		return false;
34d8a4995bSChristopher Covington 
35d8a4995bSChristopher Covington 	if (!memcmp(h->oem_table_id, "QDF2432 ", ACPI_OEM_TABLE_ID_SIZE))
36d8a4995bSChristopher Covington 		return true;
37d8a4995bSChristopher Covington 
38d8a4995bSChristopher Covington 	if (!memcmp(h->oem_table_id, "QDF2400 ", ACPI_OEM_TABLE_ID_SIZE) &&
39542ed784STimur Tabi 			h->oem_revision == 1)
40d8a4995bSChristopher Covington 		return true;
41d8a4995bSChristopher Covington 
42d8a4995bSChristopher Covington 	return false;
43d8a4995bSChristopher Covington }
44d8a4995bSChristopher Covington 
4579a64832SLoc Ho /*
4679a64832SLoc Ho  * APM X-Gene v1 and v2 UART hardware is an 16550 like device but has its
4779a64832SLoc Ho  * register aligned to 32-bit. In addition, the BIOS also encoded the
4879a64832SLoc Ho  * access width to be 8 bits. This function detects this errata condition.
4979a64832SLoc Ho  */
xgene_8250_erratum_present(struct acpi_table_spcr * tb)5079a64832SLoc Ho static bool xgene_8250_erratum_present(struct acpi_table_spcr *tb)
5179a64832SLoc Ho {
52dee82bc1SGraeme Gregory 	bool xgene_8250 = false;
53dee82bc1SGraeme Gregory 
5479a64832SLoc Ho 	if (tb->interface_type != ACPI_DBG2_16550_COMPATIBLE)
5579a64832SLoc Ho 		return false;
5679a64832SLoc Ho 
57dee82bc1SGraeme Gregory 	if (memcmp(tb->header.oem_id, "APMC0D", ACPI_OEM_ID_SIZE) &&
58dee82bc1SGraeme Gregory 	    memcmp(tb->header.oem_id, "HPE   ", ACPI_OEM_ID_SIZE))
5979a64832SLoc Ho 		return false;
6079a64832SLoc Ho 
6179a64832SLoc Ho 	if (!memcmp(tb->header.oem_table_id, "XGENESPC",
6279a64832SLoc Ho 	    ACPI_OEM_TABLE_ID_SIZE) && tb->header.oem_revision == 0)
63dee82bc1SGraeme Gregory 		xgene_8250 = true;
6479a64832SLoc Ho 
65dee82bc1SGraeme Gregory 	if (!memcmp(tb->header.oem_table_id, "ProLiant",
66dee82bc1SGraeme Gregory 	    ACPI_OEM_TABLE_ID_SIZE) && tb->header.oem_revision == 1)
67dee82bc1SGraeme Gregory 		xgene_8250 = true;
68dee82bc1SGraeme Gregory 
69dee82bc1SGraeme Gregory 	return xgene_8250;
7079a64832SLoc Ho }
7179a64832SLoc Ho 
72ad1696f6SAleksey Makarov /**
730231d000SPrarit Bhargava  * acpi_parse_spcr() - parse ACPI SPCR table and add preferred console
740231d000SPrarit Bhargava  * @enable_earlycon: set up earlycon for the console specified by the table
750231d000SPrarit Bhargava  * @enable_console: setup the console specified by the table.
76ad1696f6SAleksey Makarov  *
77ad1696f6SAleksey Makarov  * For the architectures with support for ACPI, CONFIG_ACPI_SPCR_TABLE may be
78ad1696f6SAleksey Makarov  * defined to parse ACPI SPCR table.  As a result of the parsing preferred
790231d000SPrarit Bhargava  * console is registered and if @enable_earlycon is true, earlycon is set up.
800231d000SPrarit Bhargava  * If @enable_console is true the system console is also configured.
81ad1696f6SAleksey Makarov  *
82ad1696f6SAleksey Makarov  * When CONFIG_ACPI_SPCR_TABLE is defined, this function should be called
83183b8021SMasahiro Yamada  * from arch initialization code as soon as the DT/ACPI decision is made.
84ad1696f6SAleksey Makarov  */
acpi_parse_spcr(bool enable_earlycon,bool enable_console)850231d000SPrarit Bhargava int __init acpi_parse_spcr(bool enable_earlycon, bool enable_console)
86ad1696f6SAleksey Makarov {
87ad1696f6SAleksey Makarov 	static char opts[64];
88ad1696f6SAleksey Makarov 	struct acpi_table_spcr *table;
89ad1696f6SAleksey Makarov 	acpi_status status;
90ad1696f6SAleksey Makarov 	char *uart;
91ad1696f6SAleksey Makarov 	char *iotype;
92ad1696f6SAleksey Makarov 	int baud_rate;
93ad1696f6SAleksey Makarov 	int err;
94ad1696f6SAleksey Makarov 
95ad1696f6SAleksey Makarov 	if (acpi_disabled)
96ad1696f6SAleksey Makarov 		return -ENODEV;
97ad1696f6SAleksey Makarov 
98*dcf0c2e0SAndy Shevchenko 	status = acpi_get_table(ACPI_SIG_SPCR, 0, (struct acpi_table_header **)&table);
99ad1696f6SAleksey Makarov 	if (ACPI_FAILURE(status))
100ad1696f6SAleksey Makarov 		return -ENOENT;
101ad1696f6SAleksey Makarov 
1020231d000SPrarit Bhargava 	if (table->header.revision < 2)
1030231d000SPrarit Bhargava 		pr_info("SPCR table version %d\n", table->header.revision);
104ad1696f6SAleksey Makarov 
1052bece493SLoc Ho 	if (table->serial_port.space_id == ACPI_ADR_SPACE_SYSTEM_MEMORY) {
106ee3fe99fSMark Langsdorf 		u32 bit_width = table->serial_port.access_width;
107ee3fe99fSMark Langsdorf 
108ee3fe99fSMark Langsdorf 		if (bit_width > ACPI_ACCESS_BIT_MAX) {
109da30a34aSAndy Shevchenko 			pr_err(FW_BUG "Unacceptable wide SPCR Access Width. Defaulting to byte size\n");
110ee3fe99fSMark Langsdorf 			bit_width = ACPI_ACCESS_BIT_DEFAULT;
111ee3fe99fSMark Langsdorf 		}
112ee3fe99fSMark Langsdorf 		switch (ACPI_ACCESS_BIT_WIDTH((bit_width))) {
1132bece493SLoc Ho 		default:
114da30a34aSAndy Shevchenko 			pr_err(FW_BUG "Unexpected SPCR Access Width. Defaulting to byte size\n");
11557d2dd4bSGustavo A. R. Silva 			fallthrough;
1164eebedd8SLv Zheng 		case 8:
1172bece493SLoc Ho 			iotype = "mmio";
1182bece493SLoc Ho 			break;
1194eebedd8SLv Zheng 		case 16:
1202bece493SLoc Ho 			iotype = "mmio16";
1212bece493SLoc Ho 			break;
1224eebedd8SLv Zheng 		case 32:
1232bece493SLoc Ho 			iotype = "mmio32";
1242bece493SLoc Ho 			break;
1252bece493SLoc Ho 		}
1262bece493SLoc Ho 	} else
1272bece493SLoc Ho 		iotype = "io";
128ad1696f6SAleksey Makarov 
129ad1696f6SAleksey Makarov 	switch (table->interface_type) {
130ad1696f6SAleksey Makarov 	case ACPI_DBG2_ARM_SBSA_32BIT:
131ad1696f6SAleksey Makarov 		iotype = "mmio32";
13257d2dd4bSGustavo A. R. Silva 		fallthrough;
133ad1696f6SAleksey Makarov 	case ACPI_DBG2_ARM_PL011:
134ad1696f6SAleksey Makarov 	case ACPI_DBG2_ARM_SBSA_GENERIC:
135ad1696f6SAleksey Makarov 	case ACPI_DBG2_BCM2835:
136ad1696f6SAleksey Makarov 		uart = "pl011";
137ad1696f6SAleksey Makarov 		break;
138ad1696f6SAleksey Makarov 	case ACPI_DBG2_16550_COMPATIBLE:
139ad1696f6SAleksey Makarov 	case ACPI_DBG2_16550_SUBSET:
1402aaea6a1SMarcin Wojtas 	case ACPI_DBG2_16550_WITH_GAS:
1413a506ca2SJeff Brasen 	case ACPI_DBG2_16550_NVIDIA:
142ad1696f6SAleksey Makarov 		uart = "uart";
143ad1696f6SAleksey Makarov 		break;
144ad1696f6SAleksey Makarov 	default:
145ad1696f6SAleksey Makarov 		err = -ENOENT;
146ad1696f6SAleksey Makarov 		goto done;
147ad1696f6SAleksey Makarov 	}
148ad1696f6SAleksey Makarov 
149ad1696f6SAleksey Makarov 	switch (table->baud_rate) {
150b413b1abSAndy Shevchenko 	case 0:
151b413b1abSAndy Shevchenko 		/*
152b413b1abSAndy Shevchenko 		 * SPCR 1.04 defines 0 as a preconfigured state of UART.
153b413b1abSAndy Shevchenko 		 * Assume firmware or bootloader configures console correctly.
154b413b1abSAndy Shevchenko 		 */
155b413b1abSAndy Shevchenko 		baud_rate = 0;
156b413b1abSAndy Shevchenko 		break;
157ad1696f6SAleksey Makarov 	case 3:
158ad1696f6SAleksey Makarov 		baud_rate = 9600;
159ad1696f6SAleksey Makarov 		break;
160ad1696f6SAleksey Makarov 	case 4:
161ad1696f6SAleksey Makarov 		baud_rate = 19200;
162ad1696f6SAleksey Makarov 		break;
163ad1696f6SAleksey Makarov 	case 6:
164ad1696f6SAleksey Makarov 		baud_rate = 57600;
165ad1696f6SAleksey Makarov 		break;
166ad1696f6SAleksey Makarov 	case 7:
167ad1696f6SAleksey Makarov 		baud_rate = 115200;
168ad1696f6SAleksey Makarov 		break;
169ad1696f6SAleksey Makarov 	default:
170ad1696f6SAleksey Makarov 		err = -ENOENT;
171ad1696f6SAleksey Makarov 		goto done;
172ad1696f6SAleksey Makarov 	}
173ad1696f6SAleksey Makarov 
17437ef38f3STimur Tabi 	/*
17537ef38f3STimur Tabi 	 * If the E44 erratum is required, then we need to tell the pl011
17637ef38f3STimur Tabi 	 * driver to implement the work-around.
17737ef38f3STimur Tabi 	 *
17837ef38f3STimur Tabi 	 * The global variable is used by the probe function when it
17937ef38f3STimur Tabi 	 * creates the UARTs, whether or not they're used as a console.
18037ef38f3STimur Tabi 	 *
18137ef38f3STimur Tabi 	 * If the user specifies "traditional" earlycon, the qdf2400_e44
18237ef38f3STimur Tabi 	 * console name matches the EARLYCON_DECLARE() statement, and
18337ef38f3STimur Tabi 	 * SPCR is not used.  Parameter "earlycon" is false.
18437ef38f3STimur Tabi 	 *
18537ef38f3STimur Tabi 	 * If the user specifies "SPCR" earlycon, then we need to update
18637ef38f3STimur Tabi 	 * the console name so that it also says "qdf2400_e44".  Parameter
18737ef38f3STimur Tabi 	 * "earlycon" is true.
18837ef38f3STimur Tabi 	 *
18937ef38f3STimur Tabi 	 * For consistency, if we change the console name, then we do it
19037ef38f3STimur Tabi 	 * for everyone, not just earlycon.
19137ef38f3STimur Tabi 	 */
19237ef38f3STimur Tabi 	if (qdf2400_erratum_44_present(&table->header)) {
19337ef38f3STimur Tabi 		qdf2400_e44_present = true;
1940231d000SPrarit Bhargava 		if (enable_earlycon)
195d8a4995bSChristopher Covington 			uart = "qdf2400_e44";
19637ef38f3STimur Tabi 	}
19737ef38f3STimur Tabi 
19803c3876fSGraeme Gregory 	if (xgene_8250_erratum_present(table)) {
19979a64832SLoc Ho 		iotype = "mmio32";
200d8a4995bSChristopher Covington 
201*dcf0c2e0SAndy Shevchenko 		/*
202*dcf0c2e0SAndy Shevchenko 		 * For xgene v1 and v2 we don't know the clock rate of the
20303c3876fSGraeme Gregory 		 * UART so don't attempt to change to the baud rate state
20403c3876fSGraeme Gregory 		 * in the table because driver cannot calculate the dividers
20503c3876fSGraeme Gregory 		 */
206b413b1abSAndy Shevchenko 		baud_rate = 0;
207b413b1abSAndy Shevchenko 	}
208b413b1abSAndy Shevchenko 
209b413b1abSAndy Shevchenko 	if (!baud_rate) {
21003c3876fSGraeme Gregory 		snprintf(opts, sizeof(opts), "%s,%s,0x%llx", uart, iotype,
21103c3876fSGraeme Gregory 			 table->serial_port.address);
21203c3876fSGraeme Gregory 	} else {
213ad1696f6SAleksey Makarov 		snprintf(opts, sizeof(opts), "%s,%s,0x%llx,%d", uart, iotype,
214ad1696f6SAleksey Makarov 			 table->serial_port.address, baud_rate);
21503c3876fSGraeme Gregory 	}
216ad1696f6SAleksey Makarov 
217ad1696f6SAleksey Makarov 	pr_info("console: %s\n", opts);
218ad1696f6SAleksey Makarov 
2190231d000SPrarit Bhargava 	if (enable_earlycon)
220ad1696f6SAleksey Makarov 		setup_earlycon(opts);
221ad1696f6SAleksey Makarov 
2220231d000SPrarit Bhargava 	if (enable_console)
223ad1696f6SAleksey Makarov 		err = add_preferred_console(uart, 0, opts + strlen(uart) + 1);
2240231d000SPrarit Bhargava 	else
2250231d000SPrarit Bhargava 		err = 0;
226ad1696f6SAleksey Makarov done:
2276b11d1d6SLv Zheng 	acpi_put_table((struct acpi_table_header *)table);
228ad1696f6SAleksey Makarov 	return err;
229ad1696f6SAleksey Makarov }
230