acpi_platform.c revision 1.38 1 /* $NetBSD: acpi_platform.c,v 1.38 2024/12/08 20:55:18 jmcneill Exp $ */
2
3 /*-
4 * Copyright (c) 2018 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Jared McNeill <jmcneill (at) invisible.ca>.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31
32 #include "com.h"
33 #include "plcom.h"
34 #include "opt_efi.h"
35 #include "opt_multiprocessor.h"
36
37 #include <sys/cdefs.h>
38 __KERNEL_RCSID(0, "$NetBSD: acpi_platform.c,v 1.38 2024/12/08 20:55:18 jmcneill Exp $");
39
40 #include <sys/param.h>
41 #include <sys/bus.h>
42 #include <sys/cpu.h>
43 #include <sys/device.h>
44 #include <sys/termios.h>
45 #include <sys/kprintf.h>
46
47 #include <dev/fdt/fdtvar.h>
48
49 #include <uvm/uvm_extern.h>
50
51 #include <machine/bootconfig.h>
52 #include <arm/cpufunc.h>
53 #include <arm/locore.h>
54
55 #include <arm/cortex/gtmr_var.h>
56
57 #include <arm/arm/smccc.h>
58 #include <arm/arm/psci.h>
59 #include <arm/fdt/psci_fdtvar.h>
60
61 #include <evbarm/fdt/platform.h>
62
63 #include <evbarm/dev/plcomreg.h>
64 #include <evbarm/dev/plcomvar.h>
65 #include <dev/ic/ns16550reg.h>
66 #include <dev/ic/comreg.h>
67 #include <dev/ic/comvar.h>
68
69 #if NCOM > 0
70 #include <dev/pci/pcireg.h>
71 #include <dev/pci/pcivar.h>
72 #include <dev/pci/pucvar.h>
73 #endif
74
75 #ifdef EFI_RUNTIME
76 #include <arm/arm/efi_runtime.h>
77 #endif
78
79 #include <dev/acpi/acpireg.h>
80 #include <dev/acpi/acpivar.h>
81 #include <arm/acpi/acpi_table.h>
82 #include <dev/acpi/acpi_srat.h>
83
84 #include <libfdt.h>
85
86 #define SPCR_BAUD_DEFAULT 0
87 #define SPCR_BAUD_9600 3
88 #define SPCR_BAUD_19200 4
89 #define SPCR_BAUD_57600 6
90 #define SPCR_BAUD_115200 7
91
92 static const struct acpi_spcr_baud_rate {
93 uint8_t id;
94 int baud_rate;
95 } acpi_spcr_baud_rates[] = {
96 /*
97 * SPCR_BAUD_DEFAULT means:
98 * "As is, operating system relies on the current configuration
99 * of serial port until the full featured driver will be
100 * initialized."
101 *
102 * We don't currently have a good way of telling the UART driver
103 * to detect the currently configured baud rate, so just pick
104 * something sensible here.
105 *
106 * In the past we have tried baud_rate values of 0 and -1, but
107 * these cause problems with the com(4) driver.
108 */
109 { SPCR_BAUD_DEFAULT, 115200 },
110 { SPCR_BAUD_9600, 9600 },
111 { SPCR_BAUD_19200, 19200 },
112 { SPCR_BAUD_57600, 57600 },
113 { SPCR_BAUD_115200, 115200 },
114 };
115
116 extern struct bus_space arm_generic_bs_tag;
117
118 #if NPLCOM > 0
119 static struct plcom_instance plcom_console;
120 #endif
121
122 struct arm32_bus_dma_tag acpi_coherent_dma_tag;
123 static struct arm32_dma_range acpi_coherent_ranges[] = {
124 [0] = {
125 .dr_sysbase = 0,
126 .dr_busbase = 0,
127 .dr_len = UINTPTR_MAX,
128 .dr_flags = _BUS_DMAMAP_COHERENT,
129 }
130 };
131
132 static const struct pmap_devmap *
133 acpi_platform_devmap(void)
134 {
135 static const struct pmap_devmap devmap[] = {
136 DEVMAP_ENTRY_END
137 };
138
139 return devmap;
140 }
141
142 static void
143 acpi_platform_bootstrap(void)
144 {
145 extern struct arm32_bus_dma_tag arm_generic_dma_tag;
146
147 acpi_coherent_dma_tag = arm_generic_dma_tag;
148 acpi_coherent_dma_tag._ranges = acpi_coherent_ranges;
149 acpi_coherent_dma_tag._nranges = __arraycount(acpi_coherent_ranges);
150 }
151
152 static void
153 acpi_platform_attach_uart(ACPI_TABLE_SPCR *spcr)
154 {
155 #if NCOM > 0
156 struct com_regs regs;
157 bus_space_handle_t dummy_bsh;
158 u_int reg_shift;
159 #endif
160 int baud_rate, n;
161
162 /*
163 * Only MMIO access is supported today.
164 */
165 if (spcr->SerialPort.SpaceId != ACPI_ADR_SPACE_SYSTEM_MEMORY) {
166 return;
167 }
168 if (le64toh(spcr->SerialPort.Address) == 0) {
169 return;
170 }
171
172 /*
173 * Lookup SPCR baud rate.
174 */
175 baud_rate = 0;
176 for (n = 0; n < __arraycount(acpi_spcr_baud_rates); n++) {
177 if (acpi_spcr_baud_rates[n].id == spcr->BaudRate) {
178 baud_rate = acpi_spcr_baud_rates[n].baud_rate;
179 break;
180 }
181 }
182
183 /*
184 * Attach console device.
185 */
186 switch (spcr->InterfaceType) {
187 #if NPLCOM > 0
188 case ACPI_DBG2_ARM_PL011:
189 case ACPI_DBG2_ARM_SBSA_32BIT:
190 case ACPI_DBG2_ARM_SBSA_GENERIC:
191 if (spcr->InterfaceType == ACPI_DBG2_ARM_PL011)
192 plcom_console.pi_type = PLCOM_TYPE_PL011;
193 else
194 plcom_console.pi_type = PLCOM_TYPE_GENERIC_UART;
195 plcom_console.pi_iot = &arm_generic_bs_tag;
196 plcom_console.pi_iobase = le64toh(spcr->SerialPort.Address);
197 plcom_console.pi_size = PL011COM_UART_SIZE;
198 plcom_console.pi_flags = PLC_FLAG_32BIT_ACCESS;
199
200 plcomcnattach(&plcom_console, baud_rate, 0, TTYDEF_CFLAG, -1);
201 break;
202 #endif
203
204 #if NCOM > 0
205 case ACPI_DBG2_16550_COMPATIBLE:
206 case ACPI_DBG2_16550_SUBSET:
207 case ACPI_DBG2_16550_WITH_GAS:
208 memset(&dummy_bsh, 0, sizeof(dummy_bsh));
209 switch (spcr->SerialPort.BitWidth) {
210 case 8:
211 reg_shift = 0;
212 break;
213 case 16:
214 reg_shift = 1;
215 break;
216 case 32:
217 reg_shift = 2;
218 break;
219 default:
220 /*
221 * Bit width 0 is possible for types 0 and 1. Otherwise,
222 * possibly buggy firmware.
223 */
224 if (spcr->InterfaceType == ACPI_DBG2_16550_COMPATIBLE) {
225 reg_shift = 0;
226 } else {
227 reg_shift = 2;
228 }
229 break;
230 }
231 com_init_regs_stride(®s, &arm_generic_bs_tag, dummy_bsh,
232 le64toh(spcr->SerialPort.Address), reg_shift);
233 comcnattach1(®s, baud_rate, -1, COM_TYPE_NORMAL,
234 TTYDEF_CFLAG);
235 break;
236
237 case ACPI_DBG2_BCM2835:
238 memset(&dummy_bsh, 0, sizeof(dummy_bsh));
239 com_init_regs_stride(®s, &arm_generic_bs_tag, dummy_bsh,
240 le64toh(spcr->SerialPort.Address) + 0x40, 2);
241 comcnattach1(®s, baud_rate, -1, COM_TYPE_BCMAUXUART,
242 TTYDEF_CFLAG);
243 cn_set_magic("+++++");
244 break;
245 #endif
246
247 default:
248 printf("SPCR: kernel does not support interface type %#x\n",
249 spcr->InterfaceType);
250 break;
251 }
252
253 /*
254 * UEFI firmware may leave the console in an undesireable state (wrong
255 * foreground/background colour, etc). Reset the terminal and clear
256 * text from the cursor to the end of the screen.
257 */
258 printf_flags(TOCONS|NOTSTAMP, "\033[0m");
259 printf_flags(TOCONS|NOTSTAMP, "\033[0J");
260 }
261
262 static void
263 acpi_platform_startup(void)
264 {
265 ACPI_TABLE_SPCR *spcr;
266 ACPI_TABLE_FADT *fadt;
267 #ifdef MULTIPROCESSOR
268 ACPI_TABLE_MADT *madt;
269 #endif
270
271 /*
272 * Setup serial console device
273 */
274 if (ACPI_SUCCESS(acpi_table_find(ACPI_SIG_SPCR, (void **)&spcr))) {
275 acpi_platform_attach_uart(spcr);
276 acpi_table_unmap((ACPI_TABLE_HEADER *)spcr);
277 }
278
279 /*
280 * Initialize PSCI 0.2+ if implemented
281 */
282 if (ACPI_SUCCESS(acpi_table_find(ACPI_SIG_FADT, (void **)&fadt))) {
283 const uint16_t boot_flags = le16toh(fadt->ArmBootFlags);
284 if ((boot_flags & ACPI_FADT_PSCI_COMPLIANT) != 0) {
285 if ((boot_flags & ACPI_FADT_PSCI_USE_HVC) != 0) {
286 psci_init(psci_call_hvc);
287 } else {
288 psci_init(psci_call_smc);
289 }
290 smccc_probe();
291 }
292 acpi_table_unmap((ACPI_TABLE_HEADER *)fadt);
293 }
294
295 #ifdef MULTIPROCESSOR
296 /*
297 * Count CPUs
298 */
299 if (ACPI_SUCCESS(acpi_table_find(ACPI_SIG_MADT, (void **)&madt))) {
300 char *end = (char *)madt + le32toh(madt->Header.Length);
301 char *where = (char *)madt + sizeof(ACPI_TABLE_MADT);
302 while (where < end) {
303 ACPI_SUBTABLE_HEADER *subtable =
304 (ACPI_SUBTABLE_HEADER *)where;
305 if (subtable->Type == ACPI_MADT_TYPE_GENERIC_INTERRUPT)
306 arm_cpu_max++;
307 where += subtable->Length;
308 }
309 acpi_table_unmap((ACPI_TABLE_HEADER *)madt);
310 }
311 #endif /* MULTIPROCESSOR */
312 }
313
314 static void
315 acpi_platform_init_attach_args(struct fdt_attach_args *faa)
316 {
317 extern struct bus_space arm_generic_bs_tag;
318
319 faa->faa_bst = &arm_generic_bs_tag;
320 faa->faa_dmat = &acpi_coherent_dma_tag;
321 }
322
323 static void
324 acpi_platform_device_register(device_t self, void *aux)
325 {
326 #if NCOM > 0
327 prop_dictionary_t prop = device_properties(self);
328 ACPI_STATUS rv;
329
330 if (device_is_a(self, "com")) {
331 ACPI_TABLE_SPCR *spcr;
332
333 rv = acpi_table_find(ACPI_SIG_SPCR, (void **)&spcr);
334 if (ACPI_FAILURE(rv)) {
335 return;
336 }
337
338 if (spcr->SerialPort.SpaceId != ACPI_ADR_SPACE_SYSTEM_MEMORY) {
339 goto spcr_unmap;
340 }
341 if (le64toh(spcr->SerialPort.Address) == 0) {
342 goto spcr_unmap;
343 }
344 if (spcr->InterfaceType != ACPI_DBG2_16550_COMPATIBLE &&
345 spcr->InterfaceType != ACPI_DBG2_16550_SUBSET) {
346 goto spcr_unmap;
347 }
348
349 if (device_is_a(device_parent(self), "puc")) {
350 const struct puc_attach_args * const paa = aux;
351 int b, d, f;
352
353 const int s = pci_get_segment(paa->pc);
354 pci_decompose_tag(paa->pc, paa->tag, &b, &d, &f);
355
356 if (spcr->PciSegment == s && spcr->PciBus == b &&
357 spcr->PciDevice == d && spcr->PciFunction == f) {
358 prop_dictionary_set_bool(prop,
359 "force_console", true);
360 }
361 }
362
363 if (device_is_a(device_parent(self), "acpi")) {
364 struct acpi_attach_args * const aa = aux;
365 struct acpi_resources res;
366 struct acpi_mem *mem;
367
368 if (ACPI_FAILURE(acpi_resource_parse(self,
369 aa->aa_node->ad_handle, "_CRS", &res,
370 &acpi_resource_parse_ops_quiet))) {
371 goto spcr_unmap;
372 }
373
374 mem = acpi_res_mem(&res, 0);
375 if (mem == NULL) {
376 goto crs_cleanup;
377 }
378
379 if (mem->ar_base == le64toh(spcr->SerialPort.Address)) {
380 prop_dictionary_set_bool(prop,
381 "force_console", true);
382 }
383
384 crs_cleanup:
385 acpi_resource_cleanup(&res);
386 }
387
388 spcr_unmap:
389 acpi_table_unmap((ACPI_TABLE_HEADER *)spcr);
390 }
391 #endif
392 }
393
394 static void
395 acpi_platform_device_register_post_config(device_t self, void *aux)
396 {
397 if (device_is_a(self, "acpi")) {
398 acpisrat_load_uvm();
399 }
400 }
401
402 static void
403 acpi_platform_reset(void)
404 {
405 if (psci_available()) {
406 psci_system_reset();
407 }
408
409 #ifdef EFI_RUNTIME
410 arm_efirt_reset(EFI_RESET_COLD);
411 #endif
412 }
413
414 static u_int
415 acpi_platform_uart_freq(void)
416 {
417 return 0;
418 }
419
420 static const struct fdt_platform acpi_platform = {
421 .fp_devmap = acpi_platform_devmap,
422 .fp_bootstrap = acpi_platform_bootstrap,
423 .fp_startup = acpi_platform_startup,
424 .fp_init_attach_args = acpi_platform_init_attach_args,
425 .fp_device_register = acpi_platform_device_register,
426 .fp_device_register_post_config = acpi_platform_device_register_post_config,
427 .fp_reset = acpi_platform_reset,
428 .fp_delay = gtmr_delay,
429 .fp_uart_freq = acpi_platform_uart_freq,
430 };
431
432 FDT_PLATFORM(acpi, "netbsd,generic-acpi", &acpi_platform);
433