1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2016 The FreeBSD Foundation 5 * Copyright (c) 2019 Colin Percival 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 19 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 20 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 21 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 22 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 23 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 27 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 */ 29 30 #include <sys/param.h> 31 #include <sys/systm.h> 32 #include <sys/bus.h> 33 34 #include <machine/bus.h> 35 36 #include <dev/pci/pcireg.h> 37 38 #include <dev/uart/uart.h> 39 #include <dev/uart/uart_bus.h> 40 #include <dev/uart/uart_cpu.h> 41 #include <dev/uart/uart_cpu_acpi.h> 42 43 #include <contrib/dev/acpica/include/acpi.h> 44 #include <contrib/dev/acpica/include/accommon.h> 45 #include <contrib/dev/acpica/include/actables.h> 46 47 static struct acpi_uart_compat_data * 48 uart_cpu_acpi_scan(uint8_t interface_type) 49 { 50 struct acpi_uart_compat_data **cd, *curcd; 51 int i; 52 53 SET_FOREACH(cd, uart_acpi_class_and_device_set) { 54 curcd = *cd; 55 for (i = 0; curcd[i].cd_hid != NULL; i++) { 56 if (curcd[i].cd_port_subtype == interface_type) 57 return (&curcd[i]); 58 } 59 } 60 61 SET_FOREACH(cd, uart_acpi_class_set) { 62 curcd = *cd; 63 for (i = 0; curcd[i].cd_hid != NULL; i++) { 64 if (curcd[i].cd_port_subtype == interface_type) 65 return (&curcd[i]); 66 } 67 } 68 69 return (NULL); 70 } 71 72 int 73 uart_cpu_acpi_spcr(int devtype, struct uart_devinfo *di) 74 { 75 vm_paddr_t spcr_physaddr; 76 ACPI_TABLE_SPCR *spcr; 77 struct acpi_uart_compat_data *cd; 78 struct uart_class *class; 79 int error = ENXIO; 80 81 /* SPCR only tells us about consoles. */ 82 if (devtype != UART_DEV_CONSOLE) 83 return (error); 84 85 /* Look for the SPCR table. */ 86 spcr_physaddr = acpi_find_table(ACPI_SIG_SPCR); 87 if (spcr_physaddr == 0) 88 return (error); 89 spcr = acpi_map_table(spcr_physaddr, ACPI_SIG_SPCR); 90 if (spcr == NULL) { 91 printf("Unable to map the SPCR table!\n"); 92 return (error); 93 } 94 95 /* Search for information about this SPCR interface type. */ 96 cd = uart_cpu_acpi_scan(spcr->InterfaceType); 97 if (cd == NULL) 98 goto out; 99 class = cd->cd_class; 100 101 /* Fill in some fixed details. */ 102 di->bas.chan = 0; 103 di->bas.rclk = 0; 104 di->databits = 8; 105 di->stopbits = 1; 106 di->parity = UART_PARITY_NONE; 107 di->ops = uart_getops(class); 108 109 /* Fill in details from SPCR table. */ 110 switch (spcr->SerialPort.SpaceId) { 111 case 0: 112 di->bas.bst = uart_bus_space_mem; 113 break; 114 case 1: 115 di->bas.bst = uart_bus_space_io; 116 break; 117 default: 118 printf("UART in unrecognized address space: %d!\n", 119 (int)spcr->SerialPort.SpaceId); 120 goto out; 121 } 122 switch (spcr->SerialPort.AccessWidth) { 123 case 0: /* EFI_ACPI_6_0_UNDEFINED */ 124 /* FALLTHROUGH */ 125 case 1: /* EFI_ACPI_6_0_BYTE */ 126 di->bas.regiowidth = 1; 127 break; 128 case 2: /* EFI_ACPI_6_0_WORD */ 129 di->bas.regiowidth = 2; 130 break; 131 case 3: /* EFI_ACPI_6_0_DWORD */ 132 di->bas.regiowidth = 4; 133 break; 134 case 4: /* EFI_ACPI_6_0_QWORD */ 135 di->bas.regiowidth = 8; 136 break; 137 default: 138 printf("UART unsupported access width: %d!\n", 139 (int)spcr->SerialPort.AccessWidth); 140 goto out; 141 } 142 switch (spcr->SerialPort.BitWidth) { 143 case 0: 144 /* FALLTHROUGH */ 145 case 8: 146 di->bas.regshft = 0; 147 break; 148 case 16: 149 di->bas.regshft = 1; 150 break; 151 case 32: 152 di->bas.regshft = 2; 153 break; 154 case 64: 155 di->bas.regshft = 3; 156 break; 157 default: 158 printf("UART unsupported bit width: %d!\n", 159 (int)spcr->SerialPort.BitWidth); 160 goto out; 161 } 162 switch (spcr->BaudRate) { 163 case 0: 164 /* Special value; means "keep current value unchanged". */ 165 di->baudrate = 0; 166 break; 167 case 3: 168 di->baudrate = 9600; 169 break; 170 case 4: 171 di->baudrate = 19200; 172 break; 173 case 6: 174 di->baudrate = 57600; 175 break; 176 case 7: 177 di->baudrate = 115200; 178 break; 179 default: 180 printf("SPCR has reserved BaudRate value: %d!\n", 181 (int)spcr->BaudRate); 182 goto out; 183 } 184 if (spcr->PciVendorId != PCIV_INVALID && 185 spcr->PciDeviceId != PCIV_INVALID) { 186 di->pci_info.vendor = spcr->PciVendorId; 187 di->pci_info.device = spcr->PciDeviceId; 188 } 189 190 /* Apply device tweaks. */ 191 if ((cd->cd_quirks & UART_F_IGNORE_SPCR_REGSHFT) == 192 UART_F_IGNORE_SPCR_REGSHFT) { 193 di->bas.regshft = cd->cd_regshft; 194 } 195 196 /* Create a bus space handle. */ 197 error = bus_space_map(di->bas.bst, spcr->SerialPort.Address, 198 uart_getrange(class), 0, &di->bas.bsh); 199 200 out: 201 acpi_unmap_table(spcr); 202 return (error); 203 } 204