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 if (spcr->SerialPort.AccessWidth == 0) 124 di->bas.regshft = 0; 125 else 126 di->bas.regshft = spcr->SerialPort.AccessWidth - 1; 127 di->bas.regiowidth = spcr->SerialPort.BitWidth / 8; 128 switch (spcr->BaudRate) { 129 case 0: 130 /* Special value; means "keep current value unchanged". */ 131 di->baudrate = 0; 132 break; 133 case 3: 134 di->baudrate = 9600; 135 break; 136 case 4: 137 di->baudrate = 19200; 138 break; 139 case 6: 140 di->baudrate = 57600; 141 break; 142 case 7: 143 di->baudrate = 115200; 144 break; 145 default: 146 printf("SPCR has reserved BaudRate value: %d!\n", 147 (int)spcr->BaudRate); 148 goto out; 149 } 150 151 /* Apply device tweaks. */ 152 if ((cd->cd_quirks & UART_F_IGNORE_SPCR_REGSHFT) == 153 UART_F_IGNORE_SPCR_REGSHFT) { 154 di->bas.regshft = cd->cd_regshft; 155 } 156 157 /* Create a bus space handle. */ 158 error = bus_space_map(di->bas.bst, spcr->SerialPort.Address, 159 uart_getrange(class), 0, &di->bas.bsh); 160 161 out: 162 acpi_unmap_table(spcr); 163 return (error); 164 } 165