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/cdefs.h> 31 __FBSDID("$FreeBSD$"); 32 33 #include <sys/param.h> 34 #include <sys/systm.h> 35 #include <sys/bus.h> 36 37 #include <machine/bus.h> 38 39 #include <dev/pci/pcireg.h> 40 41 #include <dev/uart/uart.h> 42 #include <dev/uart/uart_bus.h> 43 #include <dev/uart/uart_cpu.h> 44 #include <dev/uart/uart_cpu_acpi.h> 45 46 #include <contrib/dev/acpica/include/acpi.h> 47 #include <contrib/dev/acpica/include/accommon.h> 48 #include <contrib/dev/acpica/include/actables.h> 49 50 static struct acpi_uart_compat_data * 51 uart_cpu_acpi_scan(uint8_t interface_type) 52 { 53 struct acpi_uart_compat_data **cd, *curcd; 54 int i; 55 56 SET_FOREACH(cd, uart_acpi_class_and_device_set) { 57 curcd = *cd; 58 for (i = 0; curcd[i].cd_hid != NULL; i++) { 59 if (curcd[i].cd_port_subtype == interface_type) 60 return (&curcd[i]); 61 } 62 } 63 64 SET_FOREACH(cd, uart_acpi_class_set) { 65 curcd = *cd; 66 for (i = 0; curcd[i].cd_hid != NULL; i++) { 67 if (curcd[i].cd_port_subtype == interface_type) 68 return (&curcd[i]); 69 } 70 } 71 72 return (NULL); 73 } 74 75 int 76 uart_cpu_acpi_spcr(int devtype, struct uart_devinfo *di) 77 { 78 vm_paddr_t spcr_physaddr; 79 ACPI_TABLE_SPCR *spcr; 80 struct acpi_uart_compat_data *cd; 81 struct uart_class *class; 82 int error = ENXIO; 83 84 /* SPCR only tells us about consoles. */ 85 if (devtype != UART_DEV_CONSOLE) 86 return (error); 87 88 /* Look for the SPCR table. */ 89 spcr_physaddr = acpi_find_table(ACPI_SIG_SPCR); 90 if (spcr_physaddr == 0) 91 return (error); 92 spcr = acpi_map_table(spcr_physaddr, ACPI_SIG_SPCR); 93 if (spcr == NULL) { 94 printf("Unable to map the SPCR table!\n"); 95 return (error); 96 } 97 98 /* Search for information about this SPCR interface type. */ 99 cd = uart_cpu_acpi_scan(spcr->InterfaceType); 100 if (cd == NULL) 101 goto out; 102 class = cd->cd_class; 103 104 /* Fill in some fixed details. */ 105 di->bas.chan = 0; 106 di->bas.rclk = 0; 107 di->databits = 8; 108 di->stopbits = 1; 109 di->parity = UART_PARITY_NONE; 110 di->ops = uart_getops(class); 111 112 /* Fill in details from SPCR table. */ 113 switch (spcr->SerialPort.SpaceId) { 114 case 0: 115 di->bas.bst = uart_bus_space_mem; 116 break; 117 case 1: 118 di->bas.bst = uart_bus_space_io; 119 break; 120 default: 121 printf("UART in unrecognized address space: %d!\n", 122 (int)spcr->SerialPort.SpaceId); 123 goto out; 124 } 125 switch (spcr->SerialPort.AccessWidth) { 126 case 0: /* EFI_ACPI_6_0_UNDEFINED */ 127 /* FALLTHROUGH */ 128 case 1: /* EFI_ACPI_6_0_BYTE */ 129 di->bas.regiowidth = 1; 130 break; 131 case 2: /* EFI_ACPI_6_0_WORD */ 132 di->bas.regiowidth = 2; 133 break; 134 case 3: /* EFI_ACPI_6_0_DWORD */ 135 di->bas.regiowidth = 4; 136 break; 137 case 4: /* EFI_ACPI_6_0_QWORD */ 138 di->bas.regiowidth = 8; 139 break; 140 default: 141 printf("UART unsupported access width: %d!\n", 142 (int)spcr->SerialPort.AccessWidth); 143 goto out; 144 } 145 switch (spcr->SerialPort.BitWidth) { 146 case 0: 147 /* FALLTHROUGH */ 148 case 8: 149 di->bas.regshft = 0; 150 break; 151 case 16: 152 di->bas.regshft = 1; 153 break; 154 case 32: 155 di->bas.regshft = 2; 156 break; 157 case 64: 158 di->bas.regshft = 3; 159 break; 160 default: 161 printf("UART unsupported bit width: %d!\n", 162 (int)spcr->SerialPort.BitWidth); 163 goto out; 164 } 165 switch (spcr->BaudRate) { 166 case 0: 167 /* Special value; means "keep current value unchanged". */ 168 di->baudrate = 0; 169 break; 170 case 3: 171 di->baudrate = 9600; 172 break; 173 case 4: 174 di->baudrate = 19200; 175 break; 176 case 6: 177 di->baudrate = 57600; 178 break; 179 case 7: 180 di->baudrate = 115200; 181 break; 182 default: 183 printf("SPCR has reserved BaudRate value: %d!\n", 184 (int)spcr->BaudRate); 185 goto out; 186 } 187 if (spcr->PciVendorId != PCIV_INVALID && 188 spcr->PciDeviceId != PCIV_INVALID) { 189 di->pci_info.vendor = spcr->PciVendorId; 190 di->pci_info.device = spcr->PciDeviceId; 191 } 192 193 /* Apply device tweaks. */ 194 if ((cd->cd_quirks & UART_F_IGNORE_SPCR_REGSHFT) == 195 UART_F_IGNORE_SPCR_REGSHFT) { 196 di->bas.regshft = cd->cd_regshft; 197 } 198 199 /* Create a bus space handle. */ 200 error = bus_space_map(di->bas.bst, spcr->SerialPort.Address, 201 uart_getrange(class), 0, &di->bas.bsh); 202 203 out: 204 acpi_unmap_table(spcr); 205 return (error); 206 } 207