xref: /freebsd/sys/dev/uart/uart_cpu_acpi.c (revision 097bd33dd7a687cd3ad7fd4bc7ededa3b9e00e28)
17f166c93SColin Percival /*-
24d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
37f166c93SColin Percival  *
47f166c93SColin Percival  * Copyright (c) 2016 The FreeBSD Foundation
57f166c93SColin Percival  * Copyright (c) 2019 Colin Percival
67f166c93SColin Percival  * All rights reserved.
77f166c93SColin Percival  *
87f166c93SColin Percival  * Redistribution and use in source and binary forms, with or without
97f166c93SColin Percival  * modification, are permitted provided that the following conditions
107f166c93SColin Percival  * are met:
117f166c93SColin Percival  *
127f166c93SColin Percival  * 1. Redistributions of source code must retain the above copyright
137f166c93SColin Percival  *    notice, this list of conditions and the following disclaimer.
147f166c93SColin Percival  * 2. Redistributions in binary form must reproduce the above copyright
157f166c93SColin Percival  *    notice, this list of conditions and the following disclaimer in the
167f166c93SColin Percival  *    documentation and/or other materials provided with the distribution.
177f166c93SColin Percival  *
187f166c93SColin Percival  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
197f166c93SColin Percival  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
207f166c93SColin Percival  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
217f166c93SColin Percival  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
227f166c93SColin Percival  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
237f166c93SColin Percival  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
247f166c93SColin Percival  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
257f166c93SColin Percival  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
267f166c93SColin Percival  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
277f166c93SColin Percival  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
287f166c93SColin Percival  */
297f166c93SColin Percival 
307f166c93SColin Percival #include <sys/param.h>
317f166c93SColin Percival #include <sys/systm.h>
327f166c93SColin Percival #include <sys/bus.h>
337f166c93SColin Percival 
347f166c93SColin Percival #include <machine/bus.h>
357f166c93SColin Percival 
36ad93649dSColin Percival #include <dev/pci/pcireg.h>
37ad93649dSColin Percival 
387f166c93SColin Percival #include <dev/uart/uart.h>
397f166c93SColin Percival #include <dev/uart/uart_bus.h>
407f166c93SColin Percival #include <dev/uart/uart_cpu.h>
417f166c93SColin Percival #include <dev/uart/uart_cpu_acpi.h>
427f166c93SColin Percival 
437f166c93SColin Percival #include <contrib/dev/acpica/include/acpi.h>
447f166c93SColin Percival #include <contrib/dev/acpica/include/accommon.h>
457f166c93SColin Percival #include <contrib/dev/acpica/include/actables.h>
467f166c93SColin Percival 
477f166c93SColin Percival static struct acpi_uart_compat_data *
487f166c93SColin Percival uart_cpu_acpi_scan(uint8_t interface_type)
497f166c93SColin Percival {
507f166c93SColin Percival 	struct acpi_uart_compat_data **cd, *curcd;
517f166c93SColin Percival 	int i;
527f166c93SColin Percival 
537f166c93SColin Percival 	SET_FOREACH(cd, uart_acpi_class_and_device_set) {
547f166c93SColin Percival 		curcd = *cd;
557f166c93SColin Percival 		for (i = 0; curcd[i].cd_hid != NULL; i++) {
567f166c93SColin Percival 			if (curcd[i].cd_port_subtype == interface_type)
577f166c93SColin Percival 				return (&curcd[i]);
587f166c93SColin Percival 		}
597f166c93SColin Percival 	}
607f166c93SColin Percival 
617f166c93SColin Percival 	SET_FOREACH(cd, uart_acpi_class_set) {
627f166c93SColin Percival 		curcd = *cd;
637f166c93SColin Percival 		for (i = 0; curcd[i].cd_hid != NULL; i++) {
647f166c93SColin Percival 			if (curcd[i].cd_port_subtype == interface_type)
657f166c93SColin Percival 				return (&curcd[i]);
667f166c93SColin Percival 		}
677f166c93SColin Percival 	}
687f166c93SColin Percival 
697f166c93SColin Percival 	return (NULL);
707f166c93SColin Percival }
717f166c93SColin Percival 
72473c0b44SAndrew Turner static int
73473c0b44SAndrew Turner uart_cpu_acpi_init_devinfo(struct uart_devinfo *di, struct uart_class *class,
74473c0b44SAndrew Turner     ACPI_GENERIC_ADDRESS *addr)
75473c0b44SAndrew Turner {
76473c0b44SAndrew Turner 	/* Fill in some fixed details. */
77473c0b44SAndrew Turner 	di->bas.chan = 0;
78473c0b44SAndrew Turner 	di->bas.rclk = 0;
79473c0b44SAndrew Turner 	di->databits = 8;
80473c0b44SAndrew Turner 	di->stopbits = 1;
81473c0b44SAndrew Turner 	di->parity = UART_PARITY_NONE;
82473c0b44SAndrew Turner 	di->ops = uart_getops(class);
83473c0b44SAndrew Turner 
84473c0b44SAndrew Turner 	/* Fill in details from SPCR table. */
85473c0b44SAndrew Turner 	switch (addr->SpaceId) {
86473c0b44SAndrew Turner 	case 0:
87473c0b44SAndrew Turner 		di->bas.bst = uart_bus_space_mem;
88473c0b44SAndrew Turner 		break;
89473c0b44SAndrew Turner 	case 1:
90473c0b44SAndrew Turner 		di->bas.bst = uart_bus_space_io;
91473c0b44SAndrew Turner 		break;
92473c0b44SAndrew Turner 	default:
93473c0b44SAndrew Turner 		printf("UART in unrecognized address space: %d!\n",
94473c0b44SAndrew Turner 		    (int)addr->SpaceId);
95473c0b44SAndrew Turner 		return (ENXIO);
96473c0b44SAndrew Turner 	}
97473c0b44SAndrew Turner 	switch (addr->AccessWidth) {
98473c0b44SAndrew Turner 	case 0: /* EFI_ACPI_6_0_UNDEFINED */
99473c0b44SAndrew Turner 		/* FALLTHROUGH */
100473c0b44SAndrew Turner 	case 1: /* EFI_ACPI_6_0_BYTE */
101473c0b44SAndrew Turner 		di->bas.regiowidth = 1;
102473c0b44SAndrew Turner 		break;
103473c0b44SAndrew Turner 	case 2: /* EFI_ACPI_6_0_WORD */
104473c0b44SAndrew Turner 		di->bas.regiowidth = 2;
105473c0b44SAndrew Turner 		break;
106473c0b44SAndrew Turner 	case 3: /* EFI_ACPI_6_0_DWORD */
107473c0b44SAndrew Turner 		di->bas.regiowidth = 4;
108473c0b44SAndrew Turner 		break;
109473c0b44SAndrew Turner 	case 4: /* EFI_ACPI_6_0_QWORD */
110473c0b44SAndrew Turner 		di->bas.regiowidth = 8;
111473c0b44SAndrew Turner 		break;
112473c0b44SAndrew Turner 	default:
113473c0b44SAndrew Turner 		printf("UART unsupported access width: %d!\n",
114473c0b44SAndrew Turner 		    (int)addr->AccessWidth);
115473c0b44SAndrew Turner 		return (ENXIO);
116473c0b44SAndrew Turner 	}
117473c0b44SAndrew Turner 	switch (addr->BitWidth) {
118473c0b44SAndrew Turner 	case 0:
119473c0b44SAndrew Turner 		/* FALLTHROUGH */
120473c0b44SAndrew Turner 	case 8:
121473c0b44SAndrew Turner 		di->bas.regshft = 0;
122473c0b44SAndrew Turner 		break;
123473c0b44SAndrew Turner 	case 16:
124473c0b44SAndrew Turner 		di->bas.regshft = 1;
125473c0b44SAndrew Turner 		break;
126473c0b44SAndrew Turner 	case 32:
127473c0b44SAndrew Turner 		di->bas.regshft = 2;
128473c0b44SAndrew Turner 		break;
129473c0b44SAndrew Turner 	case 64:
130473c0b44SAndrew Turner 		di->bas.regshft = 3;
131473c0b44SAndrew Turner 		break;
132473c0b44SAndrew Turner 	default:
133473c0b44SAndrew Turner 		printf("UART unsupported bit width: %d!\n",
134473c0b44SAndrew Turner 		    (int)addr->BitWidth);
135473c0b44SAndrew Turner 		return (ENXIO);
136473c0b44SAndrew Turner 	}
137473c0b44SAndrew Turner 
138473c0b44SAndrew Turner 	return (0);
139473c0b44SAndrew Turner }
140473c0b44SAndrew Turner 
141a931b85aSAndrew Turner static int
1427f166c93SColin Percival uart_cpu_acpi_spcr(int devtype, struct uart_devinfo *di)
1437f166c93SColin Percival {
1447f166c93SColin Percival 	vm_paddr_t spcr_physaddr;
1457f166c93SColin Percival 	ACPI_TABLE_SPCR *spcr;
1467f166c93SColin Percival 	struct acpi_uart_compat_data *cd;
1477f166c93SColin Percival 	struct uart_class *class;
1487f166c93SColin Percival 	int error = ENXIO;
1497f166c93SColin Percival 
1507f166c93SColin Percival 	/* Look for the SPCR table. */
1517f166c93SColin Percival 	spcr_physaddr = acpi_find_table(ACPI_SIG_SPCR);
1527f166c93SColin Percival 	if (spcr_physaddr == 0)
1537f166c93SColin Percival 		return (error);
1547f166c93SColin Percival 	spcr = acpi_map_table(spcr_physaddr, ACPI_SIG_SPCR);
1557f166c93SColin Percival 	if (spcr == NULL) {
1567f166c93SColin Percival 		printf("Unable to map the SPCR table!\n");
1577f166c93SColin Percival 		return (error);
1587f166c93SColin Percival 	}
1597f166c93SColin Percival 
1607f166c93SColin Percival 	/* Search for information about this SPCR interface type. */
1617f166c93SColin Percival 	cd = uart_cpu_acpi_scan(spcr->InterfaceType);
1627f166c93SColin Percival 	if (cd == NULL)
1637f166c93SColin Percival 		goto out;
1647f166c93SColin Percival 	class = cd->cd_class;
1657f166c93SColin Percival 
166473c0b44SAndrew Turner 	error = uart_cpu_acpi_init_devinfo(di, class, &spcr->SerialPort);
167473c0b44SAndrew Turner 	if (error != 0)
168473c0b44SAndrew Turner 		goto out;
1697f166c93SColin Percival 
1707f166c93SColin Percival 	switch (spcr->BaudRate) {
1717f166c93SColin Percival 	case 0:
1727f166c93SColin Percival 		/* Special value; means "keep current value unchanged". */
1737f166c93SColin Percival 		di->baudrate = 0;
1747f166c93SColin Percival 		break;
1757f166c93SColin Percival 	case 3:
1767f166c93SColin Percival 		di->baudrate = 9600;
1777f166c93SColin Percival 		break;
1787f166c93SColin Percival 	case 4:
1797f166c93SColin Percival 		di->baudrate = 19200;
1807f166c93SColin Percival 		break;
1817f166c93SColin Percival 	case 6:
1827f166c93SColin Percival 		di->baudrate = 57600;
1837f166c93SColin Percival 		break;
1847f166c93SColin Percival 	case 7:
1857f166c93SColin Percival 		di->baudrate = 115200;
1867f166c93SColin Percival 		break;
1877f166c93SColin Percival 	default:
1887f166c93SColin Percival 		printf("SPCR has reserved BaudRate value: %d!\n",
1897f166c93SColin Percival 		    (int)spcr->BaudRate);
1907f166c93SColin Percival 		goto out;
1917f166c93SColin Percival 	}
192ad93649dSColin Percival 	if (spcr->PciVendorId != PCIV_INVALID &&
193ad93649dSColin Percival 	    spcr->PciDeviceId != PCIV_INVALID) {
194ad93649dSColin Percival 		di->pci_info.vendor = spcr->PciVendorId;
195ad93649dSColin Percival 		di->pci_info.device = spcr->PciDeviceId;
196ad93649dSColin Percival 	}
1977f166c93SColin Percival 
1987f166c93SColin Percival 	/* Apply device tweaks. */
1997f166c93SColin Percival 	if ((cd->cd_quirks & UART_F_IGNORE_SPCR_REGSHFT) ==
2007f166c93SColin Percival 	    UART_F_IGNORE_SPCR_REGSHFT) {
2017f166c93SColin Percival 		di->bas.regshft = cd->cd_regshft;
2027f166c93SColin Percival 	}
2037f166c93SColin Percival 
2047f166c93SColin Percival 	/* Create a bus space handle. */
2057f166c93SColin Percival 	error = bus_space_map(di->bas.bst, spcr->SerialPort.Address,
2067f166c93SColin Percival 	    uart_getrange(class), 0, &di->bas.bsh);
2077f166c93SColin Percival 
2087f166c93SColin Percival out:
2097f166c93SColin Percival 	acpi_unmap_table(spcr);
2107f166c93SColin Percival 	return (error);
2117f166c93SColin Percival }
212a931b85aSAndrew Turner 
213*097bd33dSAndrew Turner static int
214*097bd33dSAndrew Turner uart_cpu_acpi_dbg2(struct uart_devinfo *di)
215*097bd33dSAndrew Turner {
216*097bd33dSAndrew Turner 	vm_paddr_t dbg2_physaddr;
217*097bd33dSAndrew Turner 	ACPI_TABLE_DBG2 *dbg2;
218*097bd33dSAndrew Turner 	ACPI_DBG2_DEVICE *dbg2_dev;
219*097bd33dSAndrew Turner 	ACPI_GENERIC_ADDRESS *base_address;
220*097bd33dSAndrew Turner 	struct acpi_uart_compat_data *cd;
221*097bd33dSAndrew Turner 	struct uart_class *class;
222*097bd33dSAndrew Turner 	int error;
223*097bd33dSAndrew Turner 	bool found;
224*097bd33dSAndrew Turner 
225*097bd33dSAndrew Turner 	/* Look for the SPCR table. */
226*097bd33dSAndrew Turner 	dbg2_physaddr = acpi_find_table(ACPI_SIG_DBG2);
227*097bd33dSAndrew Turner 	if (dbg2_physaddr == 0)
228*097bd33dSAndrew Turner 		return (ENXIO);
229*097bd33dSAndrew Turner 
230*097bd33dSAndrew Turner 	dbg2 = acpi_map_table(dbg2_physaddr, ACPI_SIG_DBG2);
231*097bd33dSAndrew Turner 	if (dbg2 == NULL) {
232*097bd33dSAndrew Turner 		printf("Unable to map the DBG2 table!\n");
233*097bd33dSAndrew Turner 		return (ENXIO);
234*097bd33dSAndrew Turner 	}
235*097bd33dSAndrew Turner 
236*097bd33dSAndrew Turner 	error = ENXIO;
237*097bd33dSAndrew Turner 
238*097bd33dSAndrew Turner 	dbg2_dev = (ACPI_DBG2_DEVICE *)((vm_offset_t)dbg2 + dbg2->InfoOffset);
239*097bd33dSAndrew Turner 	found = false;
240*097bd33dSAndrew Turner 	while ((vm_offset_t)dbg2_dev + dbg2_dev->Length <=
241*097bd33dSAndrew Turner 	    (vm_offset_t)dbg2 + dbg2->Header.Length) {
242*097bd33dSAndrew Turner 		if (dbg2_dev->PortType != ACPI_DBG2_SERIAL_PORT)
243*097bd33dSAndrew Turner 			goto next;
244*097bd33dSAndrew Turner 
245*097bd33dSAndrew Turner 		/* XXX: Too restrictive? */
246*097bd33dSAndrew Turner 		if (dbg2_dev->RegisterCount != 1)
247*097bd33dSAndrew Turner 			goto next;
248*097bd33dSAndrew Turner 
249*097bd33dSAndrew Turner 		cd = uart_cpu_acpi_scan(dbg2_dev->PortSubtype);
250*097bd33dSAndrew Turner 		if (cd == NULL)
251*097bd33dSAndrew Turner 			goto next;
252*097bd33dSAndrew Turner 
253*097bd33dSAndrew Turner 		class = cd->cd_class;
254*097bd33dSAndrew Turner 		base_address = (ACPI_GENERIC_ADDRESS *)
255*097bd33dSAndrew Turner 		    ((vm_offset_t)dbg2_dev + dbg2_dev->BaseAddressOffset);
256*097bd33dSAndrew Turner 
257*097bd33dSAndrew Turner 		error = uart_cpu_acpi_init_devinfo(di, class, base_address);
258*097bd33dSAndrew Turner 		if (error == 0) {
259*097bd33dSAndrew Turner 			found = true;
260*097bd33dSAndrew Turner 			break;
261*097bd33dSAndrew Turner 		}
262*097bd33dSAndrew Turner 
263*097bd33dSAndrew Turner next:
264*097bd33dSAndrew Turner 		dbg2_dev = (ACPI_DBG2_DEVICE *)
265*097bd33dSAndrew Turner 		    ((vm_offset_t)dbg2_dev + dbg2_dev->Length);
266*097bd33dSAndrew Turner 	}
267*097bd33dSAndrew Turner 	if (!found)
268*097bd33dSAndrew Turner 		goto out;
269*097bd33dSAndrew Turner 
270*097bd33dSAndrew Turner 	/* XXX: Find the correct value */
271*097bd33dSAndrew Turner 	di->baudrate = 115200;
272*097bd33dSAndrew Turner 
273*097bd33dSAndrew Turner 	/* Apply device tweaks. */
274*097bd33dSAndrew Turner 	if ((cd->cd_quirks & UART_F_IGNORE_SPCR_REGSHFT) ==
275*097bd33dSAndrew Turner 	    UART_F_IGNORE_SPCR_REGSHFT) {
276*097bd33dSAndrew Turner 		di->bas.regshft = cd->cd_regshft;
277*097bd33dSAndrew Turner 	}
278*097bd33dSAndrew Turner 
279*097bd33dSAndrew Turner 	/* Create a bus space handle. */
280*097bd33dSAndrew Turner 	error = bus_space_map(di->bas.bst, base_address->Address,
281*097bd33dSAndrew Turner 	    uart_getrange(class), 0, &di->bas.bsh);
282*097bd33dSAndrew Turner 
283*097bd33dSAndrew Turner out:
284*097bd33dSAndrew Turner 	acpi_unmap_table(dbg2);
285*097bd33dSAndrew Turner 	return (error);
286*097bd33dSAndrew Turner }
287*097bd33dSAndrew Turner 
288a931b85aSAndrew Turner int
289a931b85aSAndrew Turner uart_cpu_acpi_setup(int devtype, struct uart_devinfo *di)
290a931b85aSAndrew Turner {
291*097bd33dSAndrew Turner 	char *cp;
292*097bd33dSAndrew Turner 
293a931b85aSAndrew Turner 	switch(devtype) {
294a931b85aSAndrew Turner 	case UART_DEV_CONSOLE:
295a931b85aSAndrew Turner 		return (uart_cpu_acpi_spcr(devtype, di));
296*097bd33dSAndrew Turner 	case UART_DEV_DBGPORT:
297*097bd33dSAndrew Turner 		/* Use the Debug Port Table 2 (DBG2) to find a debug uart */
298*097bd33dSAndrew Turner 		cp = kern_getenv("hw.acpi.enable_dbg2");
299*097bd33dSAndrew Turner 		if (cp != NULL && strcasecmp(cp, "yes") == 0)
300*097bd33dSAndrew Turner 			return (uart_cpu_acpi_dbg2(di));
301*097bd33dSAndrew Turner 		break;
302a931b85aSAndrew Turner 	}
303a931b85aSAndrew Turner 	return (ENXIO);
304a931b85aSAndrew Turner }
305