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 static int 73 uart_cpu_acpi_init_devinfo(struct uart_devinfo *di, struct uart_class *class, 74 ACPI_GENERIC_ADDRESS *addr) 75 { 76 /* Fill in some fixed details. */ 77 di->bas.chan = 0; 78 di->bas.rclk = 0; 79 di->databits = 8; 80 di->stopbits = 1; 81 di->parity = UART_PARITY_NONE; 82 di->ops = uart_getops(class); 83 84 /* Fill in details from SPCR table. */ 85 switch (addr->SpaceId) { 86 case 0: 87 di->bas.bst = uart_bus_space_mem; 88 break; 89 case 1: 90 di->bas.bst = uart_bus_space_io; 91 break; 92 default: 93 printf("UART in unrecognized address space: %d!\n", 94 (int)addr->SpaceId); 95 return (ENXIO); 96 } 97 switch (addr->AccessWidth) { 98 case 0: /* EFI_ACPI_6_0_UNDEFINED */ 99 /* FALLTHROUGH */ 100 case 1: /* EFI_ACPI_6_0_BYTE */ 101 di->bas.regiowidth = 1; 102 break; 103 case 2: /* EFI_ACPI_6_0_WORD */ 104 di->bas.regiowidth = 2; 105 break; 106 case 3: /* EFI_ACPI_6_0_DWORD */ 107 di->bas.regiowidth = 4; 108 break; 109 case 4: /* EFI_ACPI_6_0_QWORD */ 110 di->bas.regiowidth = 8; 111 break; 112 default: 113 printf("UART unsupported access width: %d!\n", 114 (int)addr->AccessWidth); 115 return (ENXIO); 116 } 117 switch (addr->BitWidth) { 118 case 0: 119 /* FALLTHROUGH */ 120 case 8: 121 di->bas.regshft = 0; 122 break; 123 case 16: 124 di->bas.regshft = 1; 125 break; 126 case 32: 127 di->bas.regshft = 2; 128 break; 129 case 64: 130 di->bas.regshft = 3; 131 break; 132 default: 133 printf("UART unsupported bit width: %d!\n", 134 (int)addr->BitWidth); 135 return (ENXIO); 136 } 137 138 return (0); 139 } 140 141 static int 142 uart_cpu_acpi_spcr(int devtype, struct uart_devinfo *di) 143 { 144 vm_paddr_t spcr_physaddr; 145 ACPI_TABLE_SPCR *spcr; 146 struct acpi_uart_compat_data *cd; 147 struct uart_class *class; 148 int error = ENXIO; 149 150 /* Look for the SPCR table. */ 151 spcr_physaddr = acpi_find_table(ACPI_SIG_SPCR); 152 if (spcr_physaddr == 0) 153 return (error); 154 spcr = acpi_map_table(spcr_physaddr, ACPI_SIG_SPCR); 155 if (spcr == NULL) { 156 printf("Unable to map the SPCR table!\n"); 157 return (error); 158 } 159 160 /* Search for information about this SPCR interface type. */ 161 cd = uart_cpu_acpi_scan(spcr->InterfaceType); 162 if (cd == NULL) 163 goto out; 164 class = cd->cd_class; 165 166 error = uart_cpu_acpi_init_devinfo(di, class, &spcr->SerialPort); 167 if (error != 0) 168 goto out; 169 170 switch (spcr->BaudRate) { 171 case 0: 172 /* Special value; means "keep current value unchanged". */ 173 di->baudrate = 0; 174 break; 175 case 3: 176 di->baudrate = 9600; 177 break; 178 case 4: 179 di->baudrate = 19200; 180 break; 181 case 6: 182 di->baudrate = 57600; 183 break; 184 case 7: 185 di->baudrate = 115200; 186 break; 187 default: 188 printf("SPCR has reserved BaudRate value: %d!\n", 189 (int)spcr->BaudRate); 190 goto out; 191 } 192 if (spcr->PciVendorId != PCIV_INVALID && 193 spcr->PciDeviceId != PCIV_INVALID) { 194 di->pci_info.vendor = spcr->PciVendorId; 195 di->pci_info.device = spcr->PciDeviceId; 196 } 197 198 /* Apply device tweaks. */ 199 if ((cd->cd_quirks & UART_F_IGNORE_SPCR_REGSHFT) == 200 UART_F_IGNORE_SPCR_REGSHFT) { 201 di->bas.regshft = cd->cd_regshft; 202 } 203 204 /* Create a bus space handle. */ 205 error = bus_space_map(di->bas.bst, spcr->SerialPort.Address, 206 uart_getrange(class), 0, &di->bas.bsh); 207 208 out: 209 acpi_unmap_table(spcr); 210 return (error); 211 } 212 213 int 214 uart_cpu_acpi_setup(int devtype, struct uart_devinfo *di) 215 { 216 switch(devtype) { 217 case UART_DEV_CONSOLE: 218 return (uart_cpu_acpi_spcr(devtype, di)); 219 } 220 return (ENXIO); 221 } 222