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