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 *
uart_cpu_acpi_scan(uint8_t interface_type)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
uart_cpu_acpi_init_devinfo(struct uart_devinfo * di,struct uart_class * class,ACPI_GENERIC_ADDRESS * addr)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
uart_cpu_acpi_spcr(int devtype,struct uart_devinfo * di)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
170*987dc68eSWarner Losh /*
171*987dc68eSWarner Losh * SPCR Rev 4 and newer allow a precise baudrate to be passed in for
172*987dc68eSWarner Losh * things like 1.5M or 2.0M. If we have that, then use that value,
173*987dc68eSWarner Losh * otherwise try to decode the older enumeration.
174*987dc68eSWarner Losh */
175*987dc68eSWarner Losh if (spcr->Header.Revision >= 4 && spcr->PreciseBaudrate != 0) {
176*987dc68eSWarner Losh di->baudrate = spcr->PreciseBaudrate;
177*987dc68eSWarner Losh } else {
1787f166c93SColin Percival switch (spcr->BaudRate) {
1797f166c93SColin Percival case 0:
1807f166c93SColin Percival /* Special value; means "keep current value unchanged". */
1817f166c93SColin Percival di->baudrate = 0;
1827f166c93SColin Percival break;
1837f166c93SColin Percival case 3:
1847f166c93SColin Percival di->baudrate = 9600;
1857f166c93SColin Percival break;
1867f166c93SColin Percival case 4:
1877f166c93SColin Percival di->baudrate = 19200;
1887f166c93SColin Percival break;
1897f166c93SColin Percival case 6:
1907f166c93SColin Percival di->baudrate = 57600;
1917f166c93SColin Percival break;
1927f166c93SColin Percival case 7:
1937f166c93SColin Percival di->baudrate = 115200;
1947f166c93SColin Percival break;
1957f166c93SColin Percival default:
1967f166c93SColin Percival printf("SPCR has reserved BaudRate value: %d!\n",
1977f166c93SColin Percival (int)spcr->BaudRate);
1987f166c93SColin Percival goto out;
1997f166c93SColin Percival }
200*987dc68eSWarner Losh }
201*987dc68eSWarner Losh
202*987dc68eSWarner Losh /*
203*987dc68eSWarner Losh * Rev 3 and newer can specify a rclk, use it if it's there. It's
204*987dc68eSWarner Losh * defined to be 0 when it's not known, and we've initialized rclk to 0
205*987dc68eSWarner Losh * in uart_cpu_acpi_init_devinfo, so we don't have to test for it.
206*987dc68eSWarner Losh */
207*987dc68eSWarner Losh if (spcr->Header.Revision >= 3)
208*987dc68eSWarner Losh di->bas.rclk = spcr->UartClkFreq;
209*987dc68eSWarner Losh
210b2fd259eSWarner Losh /*
211b2fd259eSWarner Losh * If no rclk is set, then we will assume the BIOS has configured the
212b2fd259eSWarner Losh * hardware at the stated baudrate, so we can use it to guess the rclk
213b2fd259eSWarner Losh * relatively accurately, so make a note for later.
214b2fd259eSWarner Losh */
215b2fd259eSWarner Losh if (di->bas.rclk == 0)
216b2fd259eSWarner Losh di->bas.rclk_guess = 1;
217b2fd259eSWarner Losh
218ad93649dSColin Percival if (spcr->PciVendorId != PCIV_INVALID &&
219ad93649dSColin Percival spcr->PciDeviceId != PCIV_INVALID) {
220ad93649dSColin Percival di->pci_info.vendor = spcr->PciVendorId;
221ad93649dSColin Percival di->pci_info.device = spcr->PciDeviceId;
222ad93649dSColin Percival }
2237f166c93SColin Percival
2247f166c93SColin Percival /* Create a bus space handle. */
2257f166c93SColin Percival error = bus_space_map(di->bas.bst, spcr->SerialPort.Address,
2267f166c93SColin Percival uart_getrange(class), 0, &di->bas.bsh);
2277f166c93SColin Percival
2287f166c93SColin Percival out:
2297f166c93SColin Percival acpi_unmap_table(spcr);
2307f166c93SColin Percival return (error);
2317f166c93SColin Percival }
232a931b85aSAndrew Turner
233097bd33dSAndrew Turner static int
uart_cpu_acpi_dbg2(struct uart_devinfo * di)234097bd33dSAndrew Turner uart_cpu_acpi_dbg2(struct uart_devinfo *di)
235097bd33dSAndrew Turner {
236097bd33dSAndrew Turner vm_paddr_t dbg2_physaddr;
237097bd33dSAndrew Turner ACPI_TABLE_DBG2 *dbg2;
238097bd33dSAndrew Turner ACPI_DBG2_DEVICE *dbg2_dev;
239097bd33dSAndrew Turner ACPI_GENERIC_ADDRESS *base_address;
240097bd33dSAndrew Turner struct acpi_uart_compat_data *cd;
241097bd33dSAndrew Turner struct uart_class *class;
242097bd33dSAndrew Turner int error;
243097bd33dSAndrew Turner bool found;
244097bd33dSAndrew Turner
2450bd23ca0SWarner Losh /* Look for the DBG2 table. */
246097bd33dSAndrew Turner dbg2_physaddr = acpi_find_table(ACPI_SIG_DBG2);
247097bd33dSAndrew Turner if (dbg2_physaddr == 0)
248097bd33dSAndrew Turner return (ENXIO);
249097bd33dSAndrew Turner
250097bd33dSAndrew Turner dbg2 = acpi_map_table(dbg2_physaddr, ACPI_SIG_DBG2);
251097bd33dSAndrew Turner if (dbg2 == NULL) {
252097bd33dSAndrew Turner printf("Unable to map the DBG2 table!\n");
253097bd33dSAndrew Turner return (ENXIO);
254097bd33dSAndrew Turner }
255097bd33dSAndrew Turner
256097bd33dSAndrew Turner error = ENXIO;
257097bd33dSAndrew Turner
258fcef3592SJohn Baldwin dbg2_dev = (ACPI_DBG2_DEVICE *)((uintptr_t)dbg2 + dbg2->InfoOffset);
259097bd33dSAndrew Turner found = false;
260fcef3592SJohn Baldwin while ((uintptr_t)dbg2_dev + dbg2_dev->Length <=
261fcef3592SJohn Baldwin (uintptr_t)dbg2 + dbg2->Header.Length) {
262097bd33dSAndrew Turner if (dbg2_dev->PortType != ACPI_DBG2_SERIAL_PORT)
263097bd33dSAndrew Turner goto next;
264097bd33dSAndrew Turner
265097bd33dSAndrew Turner /* XXX: Too restrictive? */
266097bd33dSAndrew Turner if (dbg2_dev->RegisterCount != 1)
267097bd33dSAndrew Turner goto next;
268097bd33dSAndrew Turner
269097bd33dSAndrew Turner cd = uart_cpu_acpi_scan(dbg2_dev->PortSubtype);
270097bd33dSAndrew Turner if (cd == NULL)
271097bd33dSAndrew Turner goto next;
272097bd33dSAndrew Turner
273097bd33dSAndrew Turner class = cd->cd_class;
274097bd33dSAndrew Turner base_address = (ACPI_GENERIC_ADDRESS *)
275fcef3592SJohn Baldwin ((uintptr_t)dbg2_dev + dbg2_dev->BaseAddressOffset);
276097bd33dSAndrew Turner
277097bd33dSAndrew Turner error = uart_cpu_acpi_init_devinfo(di, class, base_address);
278097bd33dSAndrew Turner if (error == 0) {
279097bd33dSAndrew Turner found = true;
280097bd33dSAndrew Turner break;
281097bd33dSAndrew Turner }
282097bd33dSAndrew Turner
283097bd33dSAndrew Turner next:
284097bd33dSAndrew Turner dbg2_dev = (ACPI_DBG2_DEVICE *)
285fcef3592SJohn Baldwin ((uintptr_t)dbg2_dev + dbg2_dev->Length);
286097bd33dSAndrew Turner }
287097bd33dSAndrew Turner if (!found)
288097bd33dSAndrew Turner goto out;
289097bd33dSAndrew Turner
290097bd33dSAndrew Turner /* XXX: Find the correct value */
291097bd33dSAndrew Turner di->baudrate = 115200;
292097bd33dSAndrew Turner
293097bd33dSAndrew Turner /* Create a bus space handle. */
294097bd33dSAndrew Turner error = bus_space_map(di->bas.bst, base_address->Address,
295097bd33dSAndrew Turner uart_getrange(class), 0, &di->bas.bsh);
296097bd33dSAndrew Turner
297097bd33dSAndrew Turner out:
298097bd33dSAndrew Turner acpi_unmap_table(dbg2);
299097bd33dSAndrew Turner return (error);
300097bd33dSAndrew Turner }
301097bd33dSAndrew Turner
302a931b85aSAndrew Turner int
uart_cpu_acpi_setup(int devtype,struct uart_devinfo * di)303a931b85aSAndrew Turner uart_cpu_acpi_setup(int devtype, struct uart_devinfo *di)
304a931b85aSAndrew Turner {
305097bd33dSAndrew Turner char *cp;
306097bd33dSAndrew Turner
307a931b85aSAndrew Turner switch(devtype) {
308a931b85aSAndrew Turner case UART_DEV_CONSOLE:
309a931b85aSAndrew Turner return (uart_cpu_acpi_spcr(devtype, di));
310097bd33dSAndrew Turner case UART_DEV_DBGPORT:
311097bd33dSAndrew Turner /* Use the Debug Port Table 2 (DBG2) to find a debug uart */
312097bd33dSAndrew Turner cp = kern_getenv("hw.acpi.enable_dbg2");
313097bd33dSAndrew Turner if (cp != NULL && strcasecmp(cp, "yes") == 0)
314097bd33dSAndrew Turner return (uart_cpu_acpi_dbg2(di));
315097bd33dSAndrew Turner break;
316a931b85aSAndrew Turner }
317a931b85aSAndrew Turner return (ENXIO);
318a931b85aSAndrew Turner }
319