1/* $NetBSD: acpi_machdep.c,v 1.26 2022/10/15 11:07:38 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@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 "pci.h" 33 34#include <sys/cdefs.h> 35__KERNEL_RCSID(0, "$NetBSD: acpi_machdep.c,v 1.26 2022/10/15 11:07:38 jmcneill Exp $"); 36 37#include <sys/param.h> 38#include <sys/systm.h> 39#include <sys/bus.h> 40#include <sys/cpu.h> 41#include <sys/device.h> 42#include <sys/kmem.h> 43 44#include <uvm/uvm_extern.h> 45 46#include <dev/fdt/fdtvar.h> 47 48#include <dev/acpi/acpica.h> 49#include <dev/acpi/acpivar.h> 50#if NPCI > 0 51#include <dev/acpi/acpi_mcfg.h> 52#endif 53 54#include <arm/arm/efi_runtime.h> 55 56#include <arm/pic/picvar.h> 57 58#include <arm/locore.h> 59 60#include <machine/acpi_machdep.h> 61 62extern struct bus_space arm_generic_bs_tag; 63extern struct arm32_bus_dma_tag acpi_coherent_dma_tag; 64extern struct arm32_bus_dma_tag arm_generic_dma_tag; 65 66struct acpi_intrhandler { 67 int (*ah_fn)(void *); 68 void *ah_arg; 69 TAILQ_ENTRY(acpi_intrhandler) ah_list; 70}; 71 72struct acpi_intrvec { 73 int ai_irq; 74 int ai_ipl; 75 int ai_type; 76 bool ai_mpsafe; 77 int ai_refcnt; 78 void *ai_arg; 79 void *ai_ih; 80 TAILQ_HEAD(, acpi_intrhandler) ai_handlers; 81 TAILQ_ENTRY(acpi_intrvec) ai_list; 82}; 83 84static TAILQ_HEAD(, acpi_intrvec) acpi_intrvecs = 85 TAILQ_HEAD_INITIALIZER(acpi_intrvecs); 86 87bus_dma_tag_t arm_acpi_dma32_tag(struct acpi_softc *, struct acpi_devnode *); 88bus_dma_tag_t arm_acpi_dma64_tag(struct acpi_softc *, struct acpi_devnode *); 89 90static int 91acpi_md_pmapflags(paddr_t pa) 92{ 93 int len; 94 95 const int chosen = OF_finddevice("/chosen"); 96 if (chosen == -1) 97 return 0; 98 99 const uint32_t *map = fdtbus_get_prop(chosen, "netbsd,uefi-memmap", &len); 100 if (map == NULL) 101 return 0; 102 103 while (len >= 28) { 104 const uint32_t type = be32dec(&map[0]); 105 const uint64_t phys_start = be64dec(&map[1]); 106 const uint64_t num_pages = be64dec(&map[3]); 107 const uint64_t attr = be64dec(&map[5]); 108 109 if (pa >= phys_start && pa < phys_start + (num_pages * EFI_PAGE_SIZE)) { 110 switch (type) { 111 case EFI_MD_TYPE_RECLAIM: 112 /* ACPI table memory */ 113 return PMAP_WRITE_BACK; 114 115 case EFI_MD_TYPE_IOMEM: 116 case EFI_MD_TYPE_IOPORT: 117 return PMAP_DEV; 118 119 default: 120 if ((attr & EFI_MD_ATTR_WB) != 0) 121 return PMAP_WRITE_BACK; 122 else if ((attr & EFI_MD_ATTR_WC) != 0) 123 return PMAP_WRITE_COMBINE; 124 else if ((attr & EFI_MD_ATTR_WT) != 0) 125 return 0; /* XXX */ 126 127 return PMAP_DEV; 128 } 129 } 130 131 map += 7; 132 len -= 28; 133 } 134 135 /* Not found; assume device memory */ 136 return PMAP_DEV; 137} 138 139ACPI_STATUS 140acpi_md_OsInitialize(void) 141{ 142 return AE_OK; 143} 144 145ACPI_PHYSICAL_ADDRESS 146acpi_md_OsGetRootPointer(void) 147{ 148 uint64_t pa; 149 150 const int chosen = OF_finddevice("/chosen"); 151 if (chosen == -1) 152 return 0; 153 154 if (of_getprop_uint64(chosen, "netbsd,acpi-root-table", &pa) != 0) 155 return 0; 156 157 return (ACPI_PHYSICAL_ADDRESS)pa; 158} 159 160ACPI_STATUS 161acpi_md_OsInstallInterruptHandler(UINT32 irq, ACPI_OSD_HANDLER handler, void *context, 162 void **cookiep, const char *xname) 163{ 164 return AE_NOT_IMPLEMENTED; 165} 166 167void 168acpi_md_OsRemoveInterruptHandler(void *cookie) 169{ 170 intr_disestablish(cookie); 171} 172 173ACPI_STATUS 174acpi_md_OsMapMemory(ACPI_PHYSICAL_ADDRESS pa, UINT32 size, void **vap) 175{ 176 paddr_t spa, epa, curpa; 177 vaddr_t va, curva; 178 179 spa = trunc_page(pa); 180 epa = round_page(pa + size); 181 182 va = uvm_km_alloc(kernel_map, epa - spa, 0, UVM_KMF_VAONLY); 183 if (va == 0) 184 return AE_NO_MEMORY; 185 186 const int pmapflags = acpi_md_pmapflags(spa); 187 188 aprint_debug("%s: 0x%lx 0x%x flags = %#x\n", __func__, pa, size, pmapflags); 189 190 for (curpa = spa, curva = va; curpa < epa; curpa += PAGE_SIZE, curva += PAGE_SIZE) 191 pmap_kenter_pa(curva, curpa, VM_PROT_READ | VM_PROT_WRITE, pmapflags); 192 pmap_update(pmap_kernel()); 193 194 *vap = (void *)(va + (pa - spa)); 195 196 return AE_OK; 197} 198 199void 200acpi_md_OsUnmapMemory(void *va, UINT32 size) 201{ 202 vaddr_t ova; 203 vsize_t osz; 204 205 ova = trunc_page((vaddr_t)va); 206 osz = round_page((vaddr_t)va + size) - ova; 207 208 pmap_kremove(ova, osz); 209 pmap_update(pmap_kernel()); 210 uvm_km_free(kernel_map, ova, osz, UVM_KMF_VAONLY); 211} 212 213ACPI_STATUS 214acpi_md_OsGetPhysicalAddress(void *va, ACPI_PHYSICAL_ADDRESS *pap) 215{ 216 paddr_t pa; 217 218 if (!pmap_extract(pmap_kernel(), (vaddr_t)va, &pa)) 219 return AE_ERROR; 220 221 *pap = pa; 222 223 return AE_OK; 224} 225 226BOOLEAN 227acpi_md_OsReadable(void *va, UINT32 len) 228{ 229 vaddr_t sva, eva; 230 pt_entry_t *pte; 231 232 sva = trunc_page((vaddr_t)va); 233 eva = round_page((vaddr_t)va + len); 234 235 if (sva < VM_MIN_KERNEL_ADDRESS) 236 return FALSE; 237 238 for (; sva < eva; sva += PAGE_SIZE) { 239 pte = kvtopte(sva); 240 if ((*pte & (LX_BLKPAG_AF|LX_BLKPAG_AP)) != (LX_BLKPAG_AF|LX_BLKPAG_AP_RO)) 241 return FALSE; 242 } 243 244 return TRUE; 245} 246 247BOOLEAN 248acpi_md_OsWritable(void *va, UINT32 len) 249{ 250 vaddr_t sva, eva; 251 pt_entry_t *pte; 252 253 sva = trunc_page((vaddr_t)va); 254 eva = round_page((vaddr_t)va + len); 255 256 if (sva < VM_MIN_KERNEL_ADDRESS) 257 return FALSE; 258 259 for (; sva < eva; sva += PAGE_SIZE) { 260 pte = kvtopte(sva); 261 if ((*pte & (LX_BLKPAG_AF|LX_BLKPAG_AP)) != (LX_BLKPAG_AF|LX_BLKPAG_AP_RW)) 262 return FALSE; 263 } 264 265 return TRUE; 266} 267 268void 269acpi_md_OsEnableInterrupt(void) 270{ 271 cpsie(I32_bit); 272} 273 274void 275acpi_md_OsDisableInterrupt(void) 276{ 277 cpsid(I32_bit); 278} 279 280static struct acpi_intrvec * 281acpi_md_intr_lookup(int irq) 282{ 283 struct acpi_intrvec *ai; 284 285 TAILQ_FOREACH(ai, &acpi_intrvecs, ai_list) { 286 if (ai->ai_irq == irq) { 287 return ai; 288 } 289 } 290 291 return NULL; 292} 293 294static int 295acpi_md_intr(void *arg) 296{ 297 struct acpi_intrvec *ai = arg; 298 struct acpi_intrhandler *ah; 299 int rv = 0; 300 301 TAILQ_FOREACH(ah, &ai->ai_handlers, ah_list) { 302 rv += ah->ah_fn(ah->ah_arg); 303 } 304 305 return rv; 306} 307 308void * 309acpi_md_intr_establish(uint32_t irq, int ipl, int type, int (*handler)(void *), void *arg, bool mpsafe, const char *xname) 310{ 311 struct acpi_intrvec *ai; 312 struct acpi_intrhandler *ah; 313 314 ai = acpi_md_intr_lookup(irq); 315 if (ai == NULL) { 316 ai = kmem_zalloc(sizeof(*ai), KM_SLEEP); 317 ai->ai_refcnt = 0; 318 ai->ai_irq = irq; 319 ai->ai_ipl = ipl; 320 ai->ai_type = type; 321 ai->ai_mpsafe = mpsafe; 322 ai->ai_arg = arg; 323 TAILQ_INIT(&ai->ai_handlers); 324 if (arg == NULL) { 325 ai->ai_ih = intr_establish_xname(irq, ipl, 326 type | (mpsafe ? IST_MPSAFE : 0), handler, NULL, 327 xname); 328 } else { 329 ai->ai_ih = intr_establish_xname(irq, ipl, 330 type | (mpsafe ? IST_MPSAFE : 0), acpi_md_intr, ai, 331 xname); 332 } 333 if (ai->ai_ih == NULL) { 334 kmem_free(ai, sizeof(*ai)); 335 return NULL; 336 } 337 TAILQ_INSERT_TAIL(&acpi_intrvecs, ai, ai_list); 338 } else { 339 if (ai->ai_arg == NULL) { 340 printf("ACPI: cannot share irq with NULL arg\n"); 341 return NULL; 342 } 343 if (ai->ai_ipl != ipl) { 344 printf("ACPI: cannot share irq with different ipl\n"); 345 return NULL; 346 } 347 if (ai->ai_type != type) { 348 printf("ACPI: cannot share edge and level interrupts\n"); 349 return NULL; 350 } 351 if (ai->ai_mpsafe != mpsafe) { 352 printf("ACPI: cannot share between mpsafe/non-mpsafe\n"); 353 return NULL; 354 } 355 } 356 357 ai->ai_refcnt++; 358 359 ah = kmem_zalloc(sizeof(*ah), KM_SLEEP); 360 ah->ah_fn = handler; 361 ah->ah_arg = arg; 362 TAILQ_INSERT_TAIL(&ai->ai_handlers, ah, ah_list); 363 364 return ai->ai_ih; 365} 366 367void 368acpi_md_intr_disestablish(void *ih) 369{ 370 struct acpi_intrvec *ai; 371 struct acpi_intrhandler *ah; 372 373 TAILQ_FOREACH(ai, &acpi_intrvecs, ai_list) { 374 if (ai->ai_ih == ih) { 375 KASSERT(ai->ai_refcnt > 0); 376 if (ai->ai_refcnt > 1) { 377 panic("%s: cannot disestablish shared irq", __func__); 378 } 379 380 TAILQ_REMOVE(&acpi_intrvecs, ai, ai_list); 381 ah = TAILQ_FIRST(&ai->ai_handlers); 382 kmem_free(ah, sizeof(*ah)); 383 intr_disestablish(ai->ai_ih); 384 kmem_free(ai, sizeof(*ai)); 385 return; 386 } 387 } 388 389 panic("%s: interrupt not established", __func__); 390} 391 392void 393acpi_md_intr_mask(void *ih) 394{ 395 intr_mask(ih); 396} 397 398void 399acpi_md_intr_unmask(void *ih) 400{ 401 intr_unmask(ih); 402} 403 404int 405acpi_md_sleep(int state) 406{ 407 printf("ERROR: ACPI sleep not implemented on this platform\n"); 408 return -1; 409} 410 411uint32_t 412acpi_md_pdc(void) 413{ 414 return 0; 415} 416 417uint32_t 418acpi_md_ncpus(void) 419{ 420 return kcpuset_countset(kcpuset_attached); 421} 422 423static ACPI_STATUS 424acpi_md_madt_probe_cpu(ACPI_SUBTABLE_HEADER *hdrp, void *aux) 425{ 426 struct acpi_softc * const sc = aux; 427 428 if (hdrp->Type == ACPI_MADT_TYPE_GENERIC_INTERRUPT) 429 config_found(sc->sc_dev, hdrp, NULL, 430 CFARGS(.iattr = "acpimadtbus")); 431 432 return AE_OK; 433} 434 435static ACPI_STATUS 436acpi_md_madt_probe_gic(ACPI_SUBTABLE_HEADER *hdrp, void *aux) 437{ 438 struct acpi_softc * const sc = aux; 439 440 if (hdrp->Type == ACPI_MADT_TYPE_GENERIC_DISTRIBUTOR) 441 config_found(sc->sc_dev, hdrp, NULL, 442 CFARGS(.iattr = "acpimadtbus")); 443 444 return AE_OK; 445} 446 447static ACPI_STATUS 448acpi_md_gtdt_probe(ACPI_GTDT_HEADER *hdrp, void *aux) 449{ 450 struct acpi_softc * const sc = aux; 451 452 config_found(sc->sc_dev, hdrp, NULL, 453 CFARGS(.iattr = "acpigtdtbus")); 454 455 return AE_OK; 456} 457 458#if NPCI > 0 459static struct bus_space acpi_md_mcfg_bs_tag; 460 461static int 462acpi_md_mcfg_bs_map(void *t, bus_addr_t bpa, bus_size_t size, int flag, 463 bus_space_handle_t *bshp) 464{ 465 return arm_generic_bs_tag.bs_map(t, bpa, size, 466 flag | BUS_SPACE_MAP_NONPOSTED, bshp); 467} 468#endif 469 470void 471acpi_md_callback(struct acpi_softc *sc) 472{ 473#if NPCI > 0 474 acpi_md_mcfg_bs_tag = arm_generic_bs_tag; 475 acpi_md_mcfg_bs_tag.bs_map = acpi_md_mcfg_bs_map; 476 acpimcfg_init(&acpi_md_mcfg_bs_tag, NULL); 477#endif 478 479 if (acpi_madt_map() != AE_OK) 480 panic("Failed to map MADT"); 481 acpi_madt_walk(acpi_md_madt_probe_cpu, sc); 482 acpi_madt_walk(acpi_md_madt_probe_gic, sc); 483 acpi_madt_unmap(); 484 485 if (acpi_gtdt_map() != AE_OK) 486 panic("Failed to map GTDT"); 487 acpi_gtdt_walk(acpi_md_gtdt_probe, sc); 488 acpi_gtdt_unmap(); 489} 490 491static const char * const module_hid[] = { 492 "ACPI0004", /* Module device */ 493 NULL 494}; 495 496static ACPI_HANDLE 497arm_acpi_dma_module(struct acpi_softc *sc, struct acpi_devnode *ad) 498{ 499 ACPI_HANDLE tmp; 500 ACPI_STATUS rv; 501 502 /* 503 * Search up the tree for a module device with a _DMA method. 504 */ 505 for (; ad != NULL; ad = ad->ad_parent) { 506 if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE) 507 continue; 508 if (!acpi_match_hid(ad->ad_devinfo, module_hid)) 509 continue; 510 rv = AcpiGetHandle(ad->ad_handle, "_DMA", &tmp); 511 if (ACPI_SUCCESS(rv)) 512 return ad->ad_handle; 513 } 514 515 return NULL; 516} 517 518static void 519arm_acpi_dma_init_ranges(struct acpi_softc *sc, struct acpi_devnode *ad, 520 struct arm32_bus_dma_tag *dmat, uint32_t flags) 521{ 522 struct acpi_resources res; 523 struct acpi_mem *mem; 524 ACPI_HANDLE module; 525 ACPI_STATUS rv; 526 int n; 527 528 module = arm_acpi_dma_module(sc, ad->ad_parent); 529 if (module == NULL) { 530default_tag: 531 /* No translation required */ 532 dmat->_nranges = 1; 533 dmat->_ranges = kmem_zalloc(sizeof(*dmat->_ranges), KM_SLEEP); 534 dmat->_ranges[0].dr_sysbase = 0; 535 dmat->_ranges[0].dr_busbase = 0; 536 dmat->_ranges[0].dr_len = UINTPTR_MAX; 537 dmat->_ranges[0].dr_flags = flags; 538 return; 539 } 540 541 rv = acpi_resource_parse_any(sc->sc_dev, module, "_DMA", &res, 542 &acpi_resource_parse_ops_quiet); 543 if (ACPI_FAILURE(rv)) { 544 aprint_error_dev(sc->sc_dev, 545 "failed to parse _DMA on %s: %s\n", 546 acpi_name(module), AcpiFormatException(rv)); 547 goto default_tag; 548 } 549 if (res.ar_nmem == 0) { 550 acpi_resource_cleanup(&res); 551 goto default_tag; 552 } 553 554 dmat->_nranges = res.ar_nmem; 555 dmat->_ranges = kmem_zalloc(sizeof(*dmat->_ranges) * res.ar_nmem, 556 KM_SLEEP); 557 558 for (n = 0; n < res.ar_nmem; n++) { 559 mem = acpi_res_mem(&res, n); 560 dmat->_ranges[n].dr_busbase = mem->ar_base; 561 dmat->_ranges[n].dr_sysbase = mem->ar_xbase; 562 dmat->_ranges[n].dr_len = mem->ar_length; 563 dmat->_ranges[n].dr_flags = flags; 564 565 aprint_debug_dev(sc->sc_dev, 566 "%s: DMA sys %#lx-%#lx bus %#lx-%#lx%s\n", 567 acpi_name(ad->ad_handle), 568 dmat->_ranges[n].dr_sysbase, 569 dmat->_ranges[n].dr_sysbase + dmat->_ranges[n].dr_len - 1, 570 dmat->_ranges[n].dr_busbase, 571 dmat->_ranges[n].dr_busbase + dmat->_ranges[n].dr_len - 1, 572 flags ? " (coherent)" : ""); 573 } 574 575 acpi_resource_cleanup(&res); 576} 577 578static uint32_t 579arm_acpi_dma_flags(struct acpi_softc *sc, struct acpi_devnode *ad) 580{ 581 ACPI_INTEGER cca = 1; /* default cache coherent */ 582 ACPI_STATUS rv; 583 584 for (; ad != NULL; ad = ad->ad_parent) { 585 if (ad->ad_devinfo->Type != ACPI_TYPE_DEVICE) 586 continue; 587 588 rv = acpi_eval_integer(ad->ad_handle, "_CCA", &cca); 589 if (ACPI_SUCCESS(rv)) 590 break; 591 } 592 593 return cca ? _BUS_DMAMAP_COHERENT : 0; 594} 595 596bus_dma_tag_t 597arm_acpi_dma32_tag(struct acpi_softc *sc, struct acpi_devnode *ad) 598{ 599 bus_dma_tag_t dmat64, dmat32; 600 int error; 601 602 if (ad->ad_dmat != NULL) 603 return ad->ad_dmat; 604 605 dmat64 = arm_acpi_dma64_tag(sc, ad); 606 607 const uint32_t flags = arm_acpi_dma_flags(sc, ad); 608 error = bus_dmatag_subregion(dmat64, 0, UINT32_MAX, &dmat32, flags); 609 if (error != 0) 610 panic("arm_acpi_dma32_tag: bus_dmatag_subregion returned %d", 611 error); 612 613 return dmat32; 614} 615__strong_alias(acpi_get_dma_tag,arm_acpi_dma32_tag); 616 617bus_dma_tag_t 618arm_acpi_dma64_tag(struct acpi_softc *sc, struct acpi_devnode *ad) 619{ 620 struct arm32_bus_dma_tag *dmat; 621 622 if (ad->ad_dmat64 != NULL) 623 return ad->ad_dmat64; 624 625 dmat = kmem_alloc(sizeof(*dmat), KM_SLEEP); 626 *dmat = arm_generic_dma_tag; 627 628 const uint32_t flags = arm_acpi_dma_flags(sc, ad); 629 arm_acpi_dma_init_ranges(sc, ad, dmat, flags); 630 631 return dmat; 632} 633__strong_alias(acpi_get_dma64_tag,arm_acpi_dma64_tag); 634