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