1 /* $NetBSD: acpi_mcfg.c,v 1.6 2018/04/06 17:30:25 maxv Exp $ */ 2 3 /*- 4 * Copyright (C) 2015 NONAKA Kimihiro <nonaka@NetBSD.org> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __KERNEL_RCSID(0, "$NetBSD: acpi_mcfg.c,v 1.6 2018/04/06 17:30:25 maxv Exp $"); 30 31 #include <sys/param.h> 32 #include <sys/device.h> 33 #include <sys/kmem.h> 34 #include <sys/systm.h> 35 36 #include <dev/pci/pcireg.h> 37 #include <dev/pci/pcivar.h> 38 #include <dev/pci/pcidevs.h> 39 40 #include <dev/acpi/acpireg.h> 41 #include <dev/acpi/acpivar.h> 42 #include <dev/acpi/acpi_mcfg.h> 43 44 #include "locators.h" 45 46 #define _COMPONENT ACPI_RESOURCE_COMPONENT 47 ACPI_MODULE_NAME ("acpi_mcfg") 48 49 #define EXTCONF_OFFSET(d, f, r) ((((d) * 8 + (f)) * PCI_EXTCONF_SIZE) + (r)) 50 51 #define EXTCONF_SET_VALID(mb, d, f) ((mb)->valid_devs[(d)] |= __BIT((f))) 52 #define EXTCONF_SET_INVALID(mb, d, f) ((mb)->valid_devs[(d)] &= ~__BIT((f))) 53 #define EXTCONF_IS_VALID(mb, d, f) ((mb)->valid_devs[(d)] & __BIT((f))) 54 55 struct mcfg_segment { 56 uint64_t ms_address; /* Base address */ 57 int ms_segment; /* Segment # */ 58 int ms_bus_start; /* Start bus # */ 59 int ms_bus_end; /* End bus # */ 60 bus_space_tag_t ms_bst; 61 struct mcfg_bus { 62 bus_space_handle_t bsh[32][8]; 63 uint8_t valid_devs[32]; 64 int valid_ndevs; 65 pcitag_t last_probed; 66 } *ms_bus; 67 }; 68 69 static struct mcfg_segment *mcfg_segs; 70 static int mcfg_nsegs; 71 static ACPI_TABLE_MCFG *mcfg; 72 static int mcfg_inited; 73 static struct acpi_softc *acpi_sc; 74 75 static const struct acpimcfg_ops mcfg_default_ops = { 76 .ao_validate = acpimcfg_default_validate, 77 78 .ao_read = acpimcfg_default_read, 79 .ao_write = acpimcfg_default_write, 80 }; 81 static const struct acpimcfg_ops *mcfg_ops = &mcfg_default_ops; 82 83 /* 84 * default operations. 85 */ 86 bool 87 acpimcfg_default_validate(uint64_t address, int bus_start, int *bus_end) 88 { 89 90 /* Always Ok */ 91 return true; 92 } 93 94 uint32_t 95 acpimcfg_default_read(bus_space_tag_t bst, bus_space_handle_t bsh, 96 bus_addr_t addr) 97 { 98 99 return bus_space_read_4(bst, bsh, addr); 100 } 101 102 void 103 acpimcfg_default_write(bus_space_tag_t bst, bus_space_handle_t bsh, 104 bus_addr_t addr, uint32_t data) 105 { 106 107 bus_space_write_4(bst, bsh, addr, data); 108 } 109 110 111 /* 112 * Check MCFG memory region at system resource 113 */ 114 struct acpimcfg_memrange { 115 const char *hid; 116 uint64_t address; 117 int bus_start; 118 int bus_end; 119 bool found; 120 }; 121 122 static ACPI_STATUS 123 acpimcfg_parse_callback(ACPI_RESOURCE *res, void *ctx) 124 { 125 struct acpimcfg_memrange *mr = ctx; 126 const char *type; 127 uint64_t size, mapaddr, mapsize; 128 int n; 129 130 switch (res->Type) { 131 case ACPI_RESOURCE_TYPE_FIXED_MEMORY32: 132 type = "FIXED_MEMORY32"; 133 mapaddr = res->Data.FixedMemory32.Address; 134 mapsize = res->Data.FixedMemory32.AddressLength; 135 break; 136 137 case ACPI_RESOURCE_TYPE_ADDRESS32: 138 /* XXX Only fixed size supported for now */ 139 if (res->Data.Address32.Address.AddressLength == 0 || 140 res->Data.Address32.ProducerConsumer != ACPI_CONSUMER) 141 goto out; 142 143 if (res->Data.Address32.ResourceType != ACPI_MEMORY_RANGE) 144 goto out; 145 146 if (res->Data.Address32.MinAddressFixed != ACPI_ADDRESS_FIXED || 147 res->Data.Address32.MaxAddressFixed != ACPI_ADDRESS_FIXED) 148 goto out; 149 150 type = "ADDRESS32"; 151 mapaddr = res->Data.Address32.Address.Minimum; 152 mapsize = res->Data.Address32.Address.AddressLength; 153 break; 154 155 #ifdef _LP64 156 case ACPI_RESOURCE_TYPE_ADDRESS64: 157 /* XXX Only fixed size supported for now */ 158 if (res->Data.Address64.Address.AddressLength == 0 || 159 res->Data.Address64.ProducerConsumer != ACPI_CONSUMER) 160 goto out; 161 162 if (res->Data.Address64.ResourceType != ACPI_MEMORY_RANGE) 163 goto out; 164 165 if (res->Data.Address64.MinAddressFixed != ACPI_ADDRESS_FIXED || 166 res->Data.Address64.MaxAddressFixed != ACPI_ADDRESS_FIXED) 167 goto out; 168 169 type = "ADDRESS64"; 170 mapaddr = res->Data.Address64.Address.Minimum; 171 mapsize = res->Data.Address64.Address.AddressLength; 172 break; 173 #endif 174 175 default: 176 out: 177 aprint_debug_dev(acpi_sc->sc_dev, "MCFG: %s: Type=%d\n", 178 mr->hid, res->Type); 179 return_ACPI_STATUS(AE_OK); 180 } 181 182 aprint_debug_dev(acpi_sc->sc_dev, "MCFG: %s: Type=%d(%s), " 183 "Address=0x%016" PRIx64 ", Length=0x%016" PRIx64 "\n", 184 mr->hid, res->Type, type, mapaddr, mapsize); 185 186 if (mr->address < mapaddr || mr->address >= mapaddr + mapsize) 187 return_ACPI_STATUS(AE_OK); 188 189 size = (mr->bus_end - mr->bus_start + 1) * ACPIMCFG_SIZE_PER_BUS; 190 191 /* full map */ 192 if (mr->address + size <= mapaddr + mapsize) { 193 mr->found = true; 194 return_ACPI_STATUS(AE_CTRL_TERMINATE); 195 } 196 197 /* partial map */ 198 n = (mapsize - (mr->address - mapaddr)) / ACPIMCFG_SIZE_PER_BUS; 199 /* bus_start == bus_end is not allowed. */ 200 if (n > 1) { 201 mr->bus_end = mr->bus_start + n - 1; 202 mr->found = true; 203 return_ACPI_STATUS(AE_CTRL_TERMINATE); 204 } 205 206 aprint_debug_dev(acpi_sc->sc_dev, "MCFG: bus %d-%d, " 207 "address 0x%016" PRIx64 ": invalid size: request 0x%016" PRIx64 208 ", actual 0x%016" PRIx64 "\n", 209 mr->bus_start, mr->bus_end, mr->address, size, mapsize); 210 211 return_ACPI_STATUS(AE_OK); 212 } 213 214 static ACPI_STATUS 215 acpimcfg_check_system_resource(ACPI_HANDLE handle, UINT32 level, void *ctx, 216 void **retval) 217 { 218 struct acpimcfg_memrange *mr = ctx; 219 ACPI_STATUS status; 220 221 status = AcpiWalkResources(handle, "_CRS", acpimcfg_parse_callback, mr); 222 if (ACPI_FAILURE(status)) 223 return_ACPI_STATUS(status); 224 225 if (mr->found) 226 return_ACPI_STATUS(AE_CTRL_TERMINATE); 227 228 aprint_debug_dev(acpi_sc->sc_dev, "MCFG: %s: bus %d-%d, " 229 "address 0x%016" PRIx64 ": no valid region\n", mr->hid, 230 mr->bus_start, mr->bus_end, mr->address); 231 232 return_ACPI_STATUS(AE_OK); 233 } 234 235 static bool 236 acpimcfg_find_system_resource(uint64_t address, int bus_start, int *bus_end) 237 { 238 static const char *system_resource_hid[] = { 239 "PNP0C01", /* System Board */ 240 "PNP0C02" /* General ID for reserving resources */ 241 }; 242 struct acpimcfg_memrange mr; 243 ACPI_STATUS status; 244 int i; 245 246 mr.address = address; 247 mr.bus_start = bus_start; 248 mr.bus_end = *bus_end; 249 mr.found = false; 250 251 for (i = 0; i < __arraycount(system_resource_hid); i++) { 252 mr.hid = system_resource_hid[i]; 253 status = AcpiGetDevices(__UNCONST(system_resource_hid[i]), 254 acpimcfg_check_system_resource, &mr, NULL); 255 if (ACPI_FAILURE(status)) 256 continue; 257 if (mr.found) { 258 *bus_end = mr.bus_end; 259 return true; 260 } 261 } 262 return false; 263 } 264 265 266 /* 267 * ACPI MCFG 268 */ 269 void 270 acpimcfg_probe(struct acpi_softc *sc) 271 { 272 ACPI_MCFG_ALLOCATION *ama; 273 ACPI_STATUS status; 274 uint32_t offset; 275 int i, nsegs; 276 277 if (acpi_sc != NULL) 278 panic("acpi_sc != NULL"); 279 acpi_sc = sc; 280 281 status = AcpiGetTable(ACPI_SIG_MCFG, 0, (ACPI_TABLE_HEADER **)&mcfg); 282 if (ACPI_FAILURE(status)) { 283 mcfg = NULL; 284 return; 285 } 286 287 nsegs = 0; 288 offset = sizeof(ACPI_TABLE_MCFG); 289 ama = ACPI_ADD_PTR(ACPI_MCFG_ALLOCATION, mcfg, offset); 290 for (i = 0; offset + sizeof(ACPI_MCFG_ALLOCATION) <= 291 mcfg->Header.Length; i++) { 292 aprint_debug_dev(sc->sc_dev, 293 "MCFG: segment %d, bus %d-%d, address 0x%016" PRIx64 "\n", 294 ama->PciSegment, ama->StartBusNumber, ama->EndBusNumber, 295 ama->Address); 296 nsegs++; 297 offset += sizeof(ACPI_MCFG_ALLOCATION); 298 ama = ACPI_ADD_PTR(ACPI_MCFG_ALLOCATION, ama, offset); 299 } 300 if (nsegs == 0) { 301 mcfg = NULL; 302 return; 303 } 304 305 mcfg_segs = kmem_zalloc(sizeof(*mcfg_segs) * nsegs, KM_SLEEP); 306 mcfg_nsegs = nsegs; 307 } 308 309 int 310 acpimcfg_init(bus_space_tag_t memt, const struct acpimcfg_ops *ops) 311 { 312 ACPI_MCFG_ALLOCATION *ama; 313 struct mcfg_segment *seg; 314 uint32_t offset; 315 int i, n, nsegs, bus_end; 316 317 if (mcfg == NULL) 318 return ENXIO; 319 320 if (mcfg_inited) 321 return 0; 322 323 if (ops != NULL) 324 mcfg_ops = ops; 325 326 nsegs = 0; 327 offset = sizeof(ACPI_TABLE_MCFG); 328 ama = ACPI_ADD_PTR(ACPI_MCFG_ALLOCATION, mcfg, offset); 329 for (i = 0; offset < mcfg->Header.Length; i++) { 330 #ifndef _LP64 331 if (ama->Address >= 0x100000000ULL) { 332 aprint_debug_dev(acpi_sc->sc_dev, 333 "MCFG: segment %d, bus %d-%d, address 0x%016" PRIx64 334 ": ignore (64bit address)\n", ama->PciSegment, 335 ama->StartBusNumber, ama->EndBusNumber, 336 ama->Address); 337 goto next; 338 } 339 #endif 340 /* 341 * Some (broken?) BIOSen have an MCFG table for an empty 342 * bus range. Ignore those tables. 343 */ 344 if (ama->StartBusNumber == ama->EndBusNumber) { 345 aprint_debug_dev(acpi_sc->sc_dev, 346 "MCFG: segment %d, bus %d-%d, address 0x%016" PRIx64 347 ": ignore (bus %d == %d)\n", ama->PciSegment, 348 ama->StartBusNumber, ama->EndBusNumber, 349 ama->Address, ama->StartBusNumber, 350 ama->EndBusNumber); 351 goto next; 352 } 353 if (ama->StartBusNumber > ama->EndBusNumber) { 354 aprint_debug_dev(acpi_sc->sc_dev, 355 "MCFG: segment %d, bus %d-%d, address 0x%016" PRIx64 356 ": ignore (bus %d > %d)\n", ama->PciSegment, 357 ama->StartBusNumber, ama->EndBusNumber, 358 ama->Address, ama->StartBusNumber, 359 ama->EndBusNumber); 360 goto next; 361 } 362 363 /* Validate MCFG memory range */ 364 bus_end = ama->EndBusNumber; 365 if (mcfg_ops->ao_validate != NULL && 366 !mcfg_ops->ao_validate(ama->Address, ama->StartBusNumber, 367 &bus_end)) { 368 if (!acpimcfg_find_system_resource( ama->Address, 369 ama->StartBusNumber, &bus_end)) { 370 aprint_debug_dev(acpi_sc->sc_dev, 371 "MCFG: segment %d, bus %d-%d, " 372 "address 0x%016" PRIx64 373 ": ignore (invalid address)\n", 374 ama->PciSegment, 375 ama->StartBusNumber, ama->EndBusNumber, 376 ama->Address); 377 goto next; 378 } 379 } 380 if (ama->EndBusNumber != bus_end) { 381 aprint_debug_dev(acpi_sc->sc_dev, 382 "MCFG: segment %d, bus %d-%d, address 0x%016" PRIx64 383 " -> bus %d-%d\n", ama->PciSegment, 384 ama->StartBusNumber, ama->EndBusNumber, 385 ama->Address, ama->StartBusNumber, bus_end); 386 } 387 388 if (ama->PciSegment != 0) { 389 aprint_debug_dev(acpi_sc->sc_dev, 390 "MCFG: segment %d, bus %d-%d, address 0x%016" PRIx64 391 ": ignore (non PCI segment 0)\n", ama->PciSegment, 392 ama->StartBusNumber, bus_end, ama->Address); 393 goto next; 394 } 395 396 seg = &mcfg_segs[nsegs++]; 397 seg->ms_address = ama->Address; 398 seg->ms_segment = ama->PciSegment; 399 seg->ms_bus_start = ama->StartBusNumber; 400 seg->ms_bus_end = bus_end; 401 seg->ms_bst = memt; 402 n = seg->ms_bus_end - seg->ms_bus_start + 1; 403 seg->ms_bus = kmem_zalloc(sizeof(*seg->ms_bus) * n, KM_SLEEP); 404 405 next: 406 offset += sizeof(ACPI_MCFG_ALLOCATION); 407 ama = ACPI_ADD_PTR(ACPI_MCFG_ALLOCATION, ama, offset); 408 } 409 if (nsegs == 0) 410 return ENOENT; 411 412 for (i = 0; i < nsegs; i++) { 413 seg = &mcfg_segs[i]; 414 aprint_verbose_dev(acpi_sc->sc_dev, 415 "MCFG: segment %d, bus %d-%d, address 0x%016" PRIx64 "\n", 416 seg->ms_segment, seg->ms_bus_start, seg->ms_bus_end, 417 seg->ms_address); 418 } 419 420 /* Update # of segment */ 421 mcfg_nsegs = nsegs; 422 mcfg_inited = true; 423 424 return 0; 425 } 426 427 static int 428 acpimcfg_ext_conf_is_aliased(pci_chipset_tag_t pc, pcitag_t tag) 429 { 430 pcireg_t id; 431 int i; 432 433 id = pci_conf_read(pc, tag, PCI_ID_REG); 434 for (i = PCI_CONF_SIZE; i < PCI_EXTCONF_SIZE; i += PCI_CONF_SIZE) { 435 if (pci_conf_read(pc, tag, i) != id) 436 return false; 437 } 438 return true; 439 } 440 441 static struct mcfg_segment * 442 acpimcfg_get_segment(int bus) 443 { 444 struct mcfg_segment *seg; 445 int i; 446 447 for (i = 0; i < mcfg_nsegs; i++) { 448 seg = &mcfg_segs[i]; 449 if (bus >= seg->ms_bus_start && bus <= seg->ms_bus_end) 450 return seg; 451 } 452 return NULL; 453 } 454 455 static int 456 acpimcfg_device_probe(const struct pci_attach_args *pa) 457 { 458 pci_chipset_tag_t pc = pa->pa_pc; 459 struct mcfg_segment *seg; 460 struct mcfg_bus *mb; 461 pcitag_t tag; 462 pcireg_t reg; 463 int bus = pa->pa_bus; 464 int dev = pa->pa_device; 465 int func = pa->pa_function; 466 int last_dev, last_func, end_func; 467 int alias = 0; 468 const struct pci_quirkdata *qd; 469 bool force_hasextcnf = false; 470 bool force_noextcnf = false; 471 int i, j; 472 473 seg = acpimcfg_get_segment(bus); 474 if (seg == NULL) 475 return 0; 476 477 mb = &seg->ms_bus[bus - seg->ms_bus_start]; 478 tag = pci_make_tag(pc, bus, dev, func); 479 480 /* Mark invalid between last probed device to probed device. */ 481 pci_decompose_tag(pc, mb->last_probed, NULL, &last_dev, &last_func); 482 if (dev != 0 || func != 0) { 483 for (i = last_dev; i <= dev; i++) { 484 end_func = (i == dev) ? func : 8; 485 for (j = last_func; j < end_func; j++) { 486 if (i == last_dev && j == last_func) 487 continue; 488 EXTCONF_SET_INVALID(mb, i, j); 489 } 490 last_func = 0; 491 } 492 } 493 mb->last_probed = tag; 494 495 reg = pci_conf_read(pc, tag, PCI_ID_REG); 496 qd = pci_lookup_quirkdata(PCI_VENDOR(reg), PCI_PRODUCT(reg)); 497 if (qd != NULL && (qd->quirks & PCI_QUIRK_HASEXTCNF) != 0) 498 force_hasextcnf = true; 499 if (qd != NULL && (qd->quirks & PCI_QUIRK_NOEXTCNF) != 0) 500 force_noextcnf = true; 501 502 /* Probe extended configuration space. */ 503 if ((!force_hasextcnf) && ((force_noextcnf) || 504 ((reg = pci_conf_read(pc, tag, PCI_CONF_SIZE)) == (pcireg_t)-1) 505 || (reg == 0) 506 || (alias = acpimcfg_ext_conf_is_aliased(pc, tag)))) { 507 aprint_debug_dev(acpi_sc->sc_dev, 508 "MCFG: %03d:%02d:%d: invalid config space " 509 "(cfg[0x%03x]=0x%08x, alias=%s)\n", bus, dev, func, 510 PCI_CONF_SIZE, reg, alias ? "true" : "false"); 511 EXTCONF_SET_INVALID(mb, dev, func); 512 } else { 513 aprint_debug_dev(acpi_sc->sc_dev, 514 "MCFG: %03d:%02d:%d: Ok (cfg[0x%03x]=0x%08x)\n", 515 bus, dev, func, PCI_CONF_SIZE, reg); 516 mb->valid_ndevs++; 517 } 518 519 return 0; 520 } 521 522 static void 523 acpimcfg_scan_bus(struct pci_softc *sc, pci_chipset_tag_t pc, int bus) 524 { 525 static const int wildcard[PCICF_NLOCS] = { 526 PCICF_DEV_DEFAULT, PCICF_FUNCTION_DEFAULT 527 }; 528 529 sc->sc_bus = bus; /* XXX */ 530 531 pci_enumerate_bus(sc, wildcard, acpimcfg_device_probe, NULL); 532 } 533 534 int 535 acpimcfg_map_bus(device_t self, pci_chipset_tag_t pc, int bus) 536 { 537 struct pci_softc *sc = device_private(self); 538 struct mcfg_segment *seg = NULL; 539 struct mcfg_bus *mb; 540 bus_space_handle_t bsh; 541 bus_addr_t baddr; 542 pcitag_t tag; 543 pcireg_t reg; 544 bool is_e7520_mch; 545 int boff; 546 int last_dev, last_func; 547 int i, j; 548 int error; 549 550 if (!mcfg_inited) 551 return ENXIO; 552 553 seg = acpimcfg_get_segment(bus); 554 if (seg == NULL) 555 return ENOENT; 556 557 boff = bus - seg->ms_bus_start; 558 if (seg->ms_bus[boff].valid_ndevs > 0) 559 return 0; 560 561 mb = &seg->ms_bus[boff]; 562 baddr = seg->ms_address + (boff * ACPIMCFG_SIZE_PER_BUS); 563 564 /* Map extended configration space of all dev/func. */ 565 error = bus_space_map(seg->ms_bst, baddr, ACPIMCFG_SIZE_PER_BUS, 0, 566 &bsh); 567 if (error != 0) 568 return error; 569 for (i = 0; i < 32; i++) { 570 for (j = 0; j < 8; j++) { 571 error = bus_space_subregion(seg->ms_bst, bsh, 572 EXTCONF_OFFSET(i, j, 0), PCI_EXTCONF_SIZE, 573 &mb->bsh[i][j]); 574 if (error != 0) 575 break; 576 } 577 } 578 if (error != 0) 579 return error; 580 581 aprint_debug("\n"); 582 583 /* Probe extended configuration space of all devices. */ 584 memset(mb->valid_devs, 0xff, sizeof(mb->valid_devs)); 585 mb->valid_ndevs = 0; 586 mb->last_probed = pci_make_tag(pc, bus, 0, 0); 587 588 /* 589 * On an Intel E7520 we have to temporarily disable 590 * Enhanced Config Access error detection and reporting 591 * by setting the appropriate error mask in HI_ERRMASK register. 592 * 593 * See "Intel E7520 Memory Controller Hub (MCH) Datasheet", 594 * Document 303006-002, pg. 82 595 */ 596 tag = pci_make_tag(pc, 0, 0, 1); 597 reg = pci_conf_read(pc, tag, PCI_ID_REG); 598 is_e7520_mch = (reg == 599 PCI_ID_CODE(PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_E7525_MCHER)); 600 if (is_e7520_mch) { 601 reg = pci_conf_read(pc, tag, 0x54); 602 pci_conf_write(pc, tag, 0x54, reg | 0x20); 603 } 604 605 acpimcfg_scan_bus(sc, pc, bus); 606 607 if (is_e7520_mch) { 608 pci_conf_write(pc, tag, 0x54, reg); 609 } 610 611 /* Unmap extended configration space of all dev/func. */ 612 bus_space_unmap(seg->ms_bst, bsh, ACPIMCFG_SIZE_PER_BUS); 613 memset(mb->bsh, 0, sizeof(mb->bsh)); 614 615 if (mb->valid_ndevs == 0) { 616 aprint_debug_dev(acpi_sc->sc_dev, 617 "MCFG: bus %d: no valid devices.\n", bus); 618 memset(mb->valid_devs, 0, sizeof(mb->valid_devs)); 619 goto out; 620 } 621 622 /* Mark invalid on remaining all devices. */ 623 pci_decompose_tag(pc, mb->last_probed, NULL, &last_dev, &last_func); 624 for (i = last_dev; i < 32; i++) { 625 for (j = last_func; j < 8; j++) { 626 if (i == last_dev && j == last_func) { 627 /* Don't mark invalid to last probed device. */ 628 continue; 629 } 630 EXTCONF_SET_INVALID(mb, i, j); 631 } 632 last_func = 0; 633 } 634 635 /* Map extended configuration space per dev/func. */ 636 for (i = 0; i < 32; i++) { 637 for (j = 0; j < 8; j++) { 638 if (!EXTCONF_IS_VALID(mb, i, j)) 639 continue; 640 error = bus_space_map(seg->ms_bst, 641 baddr + EXTCONF_OFFSET(i, j, 0), PCI_EXTCONF_SIZE, 642 0, &mb->bsh[i][j]); 643 if (error != 0) { 644 /* Unmap all handles when map failed. */ 645 do { 646 while (--j >= 0) { 647 if (!EXTCONF_IS_VALID(mb, i, j)) 648 continue; 649 bus_space_unmap(seg->ms_bst, 650 mb->bsh[i][j], 651 PCI_EXTCONF_SIZE); 652 } 653 j = 8; 654 } while (--i >= 0); 655 memset(mb->valid_devs, 0, 656 sizeof(mb->valid_devs)); 657 goto out; 658 } 659 } 660 } 661 662 aprint_debug_dev(acpi_sc->sc_dev, "MCFG: bus %d: valid devices\n", bus); 663 for (i = 0; i < 32; i++) { 664 for (j = 0; j < 8; j++) { 665 if (EXTCONF_IS_VALID(mb, i, j)) { 666 aprint_debug_dev(acpi_sc->sc_dev, 667 "MCFG: %03d:%02d:%d\n", bus, i, j); 668 } 669 } 670 } 671 672 error = 0; 673 out: 674 aprint_debug_dev(acpi_sc->sc_dev, "%s done", __func__); 675 676 return error; 677 } 678 679 int 680 acpimcfg_conf_read(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t *data) 681 { 682 struct mcfg_segment *seg = NULL; 683 struct mcfg_bus *mb; 684 int bus, dev, func; 685 686 KASSERT(reg < PCI_EXTCONF_SIZE); 687 KASSERT((reg & 3) == 0); 688 689 if (!mcfg_inited) { 690 *data = -1; 691 return ENXIO; 692 } 693 694 pci_decompose_tag(pc, tag, &bus, &dev, &func); 695 696 seg = acpimcfg_get_segment(bus); 697 if (seg == NULL) { 698 *data = -1; 699 return ERANGE; 700 } 701 702 mb = &seg->ms_bus[bus - seg->ms_bus_start]; 703 if (!EXTCONF_IS_VALID(mb, dev, func)) { 704 *data = -1; 705 return EINVAL; 706 } 707 708 *data = mcfg_ops->ao_read(seg->ms_bst, mb->bsh[dev][func], reg); 709 return 0; 710 } 711 712 int 713 acpimcfg_conf_write(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t data) 714 { 715 struct mcfg_segment *seg = NULL; 716 struct mcfg_bus *mb; 717 int bus, dev, func; 718 719 KASSERT(reg < PCI_EXTCONF_SIZE); 720 KASSERT((reg & 3) == 0); 721 722 if (!mcfg_inited) 723 return ENXIO; 724 725 pci_decompose_tag(pc, tag, &bus, &dev, &func); 726 727 seg = acpimcfg_get_segment(bus); 728 if (seg == NULL) 729 return ERANGE; 730 731 mb = &seg->ms_bus[bus - seg->ms_bus_start]; 732 if (!EXTCONF_IS_VALID(mb, dev, func)) 733 return EINVAL; 734 735 mcfg_ops->ao_write(seg->ms_bst, mb->bsh[dev][func], reg, data); 736 return 0; 737 } 738