1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2016, Anish Gupta (anish@freebsd.org) 5 * Copyright (c) 2021 The FreeBSD Foundation 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 * 1. Redistributions of source code must retain the above copyright 12 * notice unmodified, this list of conditions, and the following 13 * 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 "opt_acpi.h" 34 #include <sys/param.h> 35 #include <sys/bus.h> 36 #include <sys/kernel.h> 37 #include <sys/module.h> 38 #include <sys/malloc.h> 39 40 #include <machine/vmparam.h> 41 42 #include <contrib/dev/acpica/include/acpi.h> 43 #include <contrib/dev/acpica/include/accommon.h> 44 #include <dev/acpica/acpivar.h> 45 #include <dev/pci/pcireg.h> 46 #include <dev/pci/pcivar.h> 47 48 #include "io/iommu.h" 49 #include "amdvi_priv.h" 50 51 device_t *ivhd_devs; /* IVHD or AMD-Vi device list. */ 52 int ivhd_count; /* Number of IVHD header. */ 53 /* 54 * Cached IVHD header list. 55 * Single entry for each IVHD, filtered the legacy one. 56 */ 57 ACPI_IVRS_HARDWARE1 **ivhd_hdrs; 58 59 extern int amdvi_ptp_level; /* Page table levels. */ 60 61 typedef int (*ivhd_iter_t)(ACPI_IVRS_HEADER *ptr, void *arg); 62 /* 63 * Iterate IVRS table for IVHD and IVMD device type. 64 */ 65 static void 66 ivrs_hdr_iterate_tbl(ivhd_iter_t iter, void *arg) 67 { 68 ACPI_TABLE_IVRS *ivrs; 69 ACPI_IVRS_HEADER *ivrs_hdr, *end; 70 ACPI_STATUS status; 71 72 status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs); 73 if (ACPI_FAILURE(status)) 74 return; 75 76 if (ivrs->Header.Length == 0) { 77 return; 78 } 79 80 ivrs_hdr = (ACPI_IVRS_HEADER *)(ivrs + 1); 81 end = (ACPI_IVRS_HEADER *)((char *)ivrs + ivrs->Header.Length); 82 83 while (ivrs_hdr < end) { 84 if ((uint8_t *)ivrs_hdr + ivrs_hdr->Length > (uint8_t *)end) { 85 printf("AMD-Vi:IVHD/IVMD is corrupted, length : %d\n", 86 ivrs_hdr->Length); 87 break; 88 } 89 90 switch (ivrs_hdr->Type) { 91 case IVRS_TYPE_HARDWARE_LEGACY: /* Legacy */ 92 case IVRS_TYPE_HARDWARE_EFR: 93 case IVRS_TYPE_HARDWARE_MIXED: 94 if (!iter(ivrs_hdr, arg)) 95 return; 96 break; 97 98 case ACPI_IVRS_TYPE_MEMORY1: 99 case ACPI_IVRS_TYPE_MEMORY2: 100 case ACPI_IVRS_TYPE_MEMORY3: 101 if (!iter(ivrs_hdr, arg)) 102 return; 103 104 break; 105 106 default: 107 printf("AMD-Vi:Not IVHD/IVMD type(%d)", ivrs_hdr->Type); 108 } 109 110 ivrs_hdr = (ACPI_IVRS_HEADER *)((uint8_t *)ivrs_hdr + 111 ivrs_hdr->Length); 112 } 113 } 114 115 static bool 116 ivrs_is_ivhd(UINT8 type) 117 { 118 119 switch(type) { 120 case IVRS_TYPE_HARDWARE_LEGACY: 121 case IVRS_TYPE_HARDWARE_EFR: 122 case IVRS_TYPE_HARDWARE_MIXED: 123 return (true); 124 125 default: 126 return (false); 127 } 128 } 129 130 /* Count the number of AMD-Vi devices in the system. */ 131 static int 132 ivhd_count_iter(ACPI_IVRS_HEADER * ivrs_he, void *arg) 133 { 134 int *count; 135 136 count = (int *)arg; 137 if (ivrs_is_ivhd(ivrs_he->Type)) 138 (*count)++; 139 140 return (1); 141 } 142 143 struct find_ivrs_hdr_args { 144 int i; 145 ACPI_IVRS_HEADER *ptr; 146 }; 147 148 static int 149 ivrs_hdr_find_iter(ACPI_IVRS_HEADER * ivrs_hdr, void *args) 150 { 151 struct find_ivrs_hdr_args *fi; 152 153 fi = (struct find_ivrs_hdr_args *)args; 154 if (ivrs_is_ivhd(ivrs_hdr->Type)) { 155 if (fi->i == 0) { 156 fi->ptr = ivrs_hdr; 157 return (0); 158 } 159 fi->i--; 160 } 161 162 return (1); 163 } 164 165 static ACPI_IVRS_HARDWARE1 * 166 ivhd_find_by_index(int idx) 167 { 168 struct find_ivrs_hdr_args fi; 169 170 fi.i = idx; 171 fi.ptr = NULL; 172 173 ivrs_hdr_iterate_tbl(ivrs_hdr_find_iter, &fi); 174 175 return ((ACPI_IVRS_HARDWARE1 *)fi.ptr); 176 } 177 178 static void 179 ivhd_dev_add_entry(struct amdvi_softc *softc, uint32_t start_id, 180 uint32_t end_id, uint8_t cfg, bool ats) 181 { 182 struct ivhd_dev_cfg *dev_cfg; 183 184 KASSERT(softc->dev_cfg_cap >= softc->dev_cfg_cnt, 185 ("Impossible case: number of dev_cfg exceeding capacity")); 186 if (softc->dev_cfg_cap == softc->dev_cfg_cnt) { 187 if (softc->dev_cfg_cap == 0) 188 softc->dev_cfg_cap = 1; 189 else 190 softc->dev_cfg_cap <<= 2; 191 softc->dev_cfg = realloc(softc->dev_cfg, 192 sizeof(*softc->dev_cfg) * softc->dev_cfg_cap, M_DEVBUF, 193 M_WAITOK); 194 } 195 196 dev_cfg = &softc->dev_cfg[softc->dev_cfg_cnt++]; 197 dev_cfg->start_id = start_id; 198 dev_cfg->end_id = end_id; 199 dev_cfg->data = cfg; 200 dev_cfg->enable_ats = ats; 201 } 202 203 /* 204 * Record device attributes as suggested by BIOS. 205 */ 206 static int 207 ivhd_dev_parse(ACPI_IVRS_HARDWARE1 *ivhd, struct amdvi_softc *softc) 208 { 209 ACPI_IVRS_DE_HEADER *de; 210 uint8_t *p, *end; 211 int range_start_id = -1, range_end_id = -1, i; 212 uint32_t *extended; 213 uint8_t all_data = 0, range_data = 0; 214 bool range_enable_ats = false, enable_ats; 215 216 switch (ivhd->Header.Type) { 217 case IVRS_TYPE_HARDWARE_LEGACY: 218 p = (uint8_t *)ivhd + sizeof(ACPI_IVRS_HARDWARE1); 219 break; 220 221 case IVRS_TYPE_HARDWARE_EFR: 222 case IVRS_TYPE_HARDWARE_MIXED: 223 p = (uint8_t *)ivhd + sizeof(ACPI_IVRS_HARDWARE2); 224 break; 225 226 default: 227 device_printf(softc->dev, 228 "unknown type: 0x%x\n", ivhd->Header.Type); 229 return (-1); 230 } 231 232 end = (uint8_t *)ivhd + ivhd->Header.Length; 233 234 while (p < end) { 235 de = (ACPI_IVRS_DE_HEADER *)p; 236 switch (de->Type) { 237 case ACPI_IVRS_TYPE_ALL: 238 all_data = de->DataSetting; 239 for (i = 0; i < softc->dev_cfg_cnt; i++) 240 softc->dev_cfg[i].data |= all_data; 241 break; 242 243 case ACPI_IVRS_TYPE_SELECT: 244 case ACPI_IVRS_TYPE_ALIAS_SELECT: 245 case ACPI_IVRS_TYPE_EXT_SELECT: 246 enable_ats = false; 247 if (de->Type == ACPI_IVRS_TYPE_EXT_SELECT) { 248 extended = (uint32_t *)(de + 1); 249 enable_ats = 250 (*extended & IVHD_DEV_EXT_ATS_DISABLE) ? 251 false : true; 252 } 253 ivhd_dev_add_entry(softc, de->Id, de->Id, 254 de->DataSetting | all_data, enable_ats); 255 break; 256 257 case ACPI_IVRS_TYPE_START: 258 case ACPI_IVRS_TYPE_ALIAS_START: 259 case ACPI_IVRS_TYPE_EXT_START: 260 if (range_start_id != -1) { 261 device_printf(softc->dev, 262 "Unexpected start-of-range device entry\n"); 263 return (EINVAL); 264 } 265 range_start_id = de->Id; 266 range_data = de->DataSetting; 267 if (de->Type == ACPI_IVRS_TYPE_EXT_START) { 268 extended = (uint32_t *)(de + 1); 269 range_enable_ats = 270 (*extended & IVHD_DEV_EXT_ATS_DISABLE) ? 271 false : true; 272 } 273 break; 274 275 case ACPI_IVRS_TYPE_END: 276 if (range_start_id == -1) { 277 device_printf(softc->dev, 278 "Unexpected end-of-range device entry\n"); 279 return (EINVAL); 280 } 281 range_end_id = de->Id; 282 if (range_end_id < range_start_id) { 283 device_printf(softc->dev, 284 "Device entry range going backward\n"); 285 return (EINVAL); 286 } 287 ivhd_dev_add_entry(softc, range_start_id, range_end_id, 288 range_data | all_data, range_enable_ats); 289 range_start_id = range_end_id = -1; 290 range_data = 0; 291 all_data = 0; 292 break; 293 294 case ACPI_IVRS_TYPE_PAD4: 295 break; 296 297 case ACPI_IVRS_TYPE_SPECIAL: 298 /* HPET or IOAPIC */ 299 break; 300 default: 301 if ((de->Type < 5) || 302 (de->Type >= ACPI_IVRS_TYPE_PAD8)) 303 device_printf(softc->dev, 304 "Unknown dev entry:0x%x\n", de->Type); 305 } 306 307 if (de->Type < 0x40) 308 p += sizeof(ACPI_IVRS_DEVICE4); 309 else if (de->Type < 0x80) 310 p += sizeof(ACPI_IVRS_DEVICE8A); 311 else { 312 printf("Variable size IVHD type 0x%x not supported\n", 313 de->Type); 314 break; 315 } 316 } 317 318 return (0); 319 } 320 321 static bool 322 ivhd_is_newer(ACPI_IVRS_HEADER *old, ACPI_IVRS_HEADER *new) 323 { 324 if (old->DeviceId == new->DeviceId) { 325 /* 326 * Newer IVRS header type take precedence. 327 */ 328 if (old->Type == IVRS_TYPE_HARDWARE_LEGACY && 329 ((new->Type == IVRS_TYPE_HARDWARE_EFR) || 330 (new->Type == IVRS_TYPE_HARDWARE_MIXED))) 331 return (true); 332 333 /* 334 * Mixed format IVHD header type take precedence 335 * over fixed format IVHD header types. 336 */ 337 if (old->Type == IVRS_TYPE_HARDWARE_EFR && 338 new->Type == IVRS_TYPE_HARDWARE_MIXED) 339 return (true); 340 } 341 342 return (false); 343 } 344 345 static void 346 ivhd_identify(driver_t *driver, device_t parent) 347 { 348 ACPI_TABLE_IVRS *ivrs; 349 ACPI_IVRS_HARDWARE1 *ivhd; 350 ACPI_STATUS status; 351 int i, j, count = 0; 352 uint32_t ivrs_ivinfo; 353 354 if (acpi_disabled("ivhd")) 355 return; 356 357 status = AcpiGetTable(ACPI_SIG_IVRS, 1, (ACPI_TABLE_HEADER **)&ivrs); 358 if (ACPI_FAILURE(status)) 359 return; 360 361 if (ivrs->Header.Length == 0) { 362 return; 363 } 364 365 ivrs_ivinfo = ivrs->Info; 366 printf("AMD-Vi: IVRS Info VAsize = %d PAsize = %d GVAsize = %d" 367 " flags:%b\n", 368 REG_BITS(ivrs_ivinfo, 21, 15), REG_BITS(ivrs_ivinfo, 14, 8), 369 REG_BITS(ivrs_ivinfo, 7, 5), REG_BITS(ivrs_ivinfo, 22, 22), 370 "\020\001EFRSup"); 371 372 ivrs_hdr_iterate_tbl(ivhd_count_iter, &count); 373 if (!count) 374 return; 375 376 ivhd_hdrs = kmem_zalloc(sizeof(void *) * count, KM_SLEEP); 377 for (i = 0; i < count; i++) { 378 ivhd = ivhd_find_by_index(i); 379 KASSERT(ivhd, ("ivhd%d is NULL\n", i)); 380 381 /* 382 * Scan for presence of legacy and non-legacy device type 383 * for same IOMMU device and override the old one. 384 * 385 * If there is no existing IVHD to the same IOMMU device, 386 * the IVHD header pointer is appended. 387 */ 388 for (j = 0; j < ivhd_count; j++) { 389 if (ivhd_is_newer(&ivhd_hdrs[j]->Header, &ivhd->Header)) 390 break; 391 } 392 ivhd_hdrs[j] = ivhd; 393 if (j == ivhd_count) 394 ivhd_count++; 395 } 396 397 ivhd_devs = kmem_zalloc(sizeof(device_t) * ivhd_count, KM_SLEEP); 398 for (i = 0, j = 0; i < ivhd_count; i++) { 399 ivhd = ivhd_hdrs[i]; 400 KASSERT(ivhd, ("ivhd%d is NULL\n", i)); 401 402 /* 403 * Use a high order to ensure that this driver is probed after 404 * the Host-PCI bridge and the root PCI bus. 405 */ 406 ivhd_devs[i] = BUS_ADD_CHILD(parent, 407 ACPI_DEV_BASE_ORDER + 10 * 10, "ivhd", i); 408 409 /* 410 * XXX: In case device was not destroyed before, add will fail. 411 * locate the old device instance. 412 */ 413 if (ivhd_devs[i] == NULL) { 414 ivhd_devs[i] = device_find_child(parent, "ivhd", i); 415 if (ivhd_devs[i] == NULL) { 416 printf("AMD-Vi: cant find ivhd%d\n", i); 417 break; 418 } 419 } 420 j++; 421 } 422 423 /* 424 * Update device count in case failed to attach. 425 */ 426 ivhd_count = j; 427 } 428 429 static int 430 ivhd_probe(device_t dev) 431 { 432 ACPI_IVRS_HARDWARE1 *ivhd; 433 int unit; 434 435 if (acpi_get_handle(dev) != NULL) 436 return (ENXIO); 437 438 unit = device_get_unit(dev); 439 KASSERT((unit < ivhd_count), 440 ("ivhd unit %d > count %d", unit, ivhd_count)); 441 ivhd = ivhd_hdrs[unit]; 442 KASSERT(ivhd, ("ivhd is NULL")); 443 444 switch (ivhd->Header.Type) { 445 case IVRS_TYPE_HARDWARE_EFR: 446 device_set_desc(dev, "AMD-Vi/IOMMU ivhd with EFR"); 447 break; 448 449 case IVRS_TYPE_HARDWARE_MIXED: 450 device_set_desc(dev, "AMD-Vi/IOMMU ivhd in mixed format"); 451 break; 452 453 case IVRS_TYPE_HARDWARE_LEGACY: 454 default: 455 device_set_desc(dev, "AMD-Vi/IOMMU ivhd"); 456 break; 457 } 458 459 return (BUS_PROBE_NOWILDCARD); 460 } 461 462 static void 463 ivhd_print_flag(device_t dev, enum IvrsType ivhd_type, uint8_t flag) 464 { 465 /* 466 * IVHD lgeacy type has two extra high bits in flag which has 467 * been moved to EFR for non-legacy device. 468 */ 469 switch (ivhd_type) { 470 case IVRS_TYPE_HARDWARE_LEGACY: 471 device_printf(dev, "Flag:%b\n", flag, 472 "\020" 473 "\001HtTunEn" 474 "\002PassPW" 475 "\003ResPassPW" 476 "\004Isoc" 477 "\005IotlbSup" 478 "\006Coherent" 479 "\007PreFSup" 480 "\010PPRSup"); 481 break; 482 483 case IVRS_TYPE_HARDWARE_EFR: 484 case IVRS_TYPE_HARDWARE_MIXED: 485 device_printf(dev, "Flag:%b\n", flag, 486 "\020" 487 "\001HtTunEn" 488 "\002PassPW" 489 "\003ResPassPW" 490 "\004Isoc" 491 "\005IotlbSup" 492 "\006Coherent"); 493 break; 494 495 default: 496 device_printf(dev, "Can't decode flag of ivhd type :0x%x\n", 497 ivhd_type); 498 break; 499 } 500 } 501 502 /* 503 * Feature in legacy IVHD type(0x10) and attribute in newer type(0x11 and 0x40). 504 */ 505 static void 506 ivhd_print_feature(device_t dev, enum IvrsType ivhd_type, uint32_t feature) 507 { 508 switch (ivhd_type) { 509 case IVRS_TYPE_HARDWARE_LEGACY: 510 device_printf(dev, "Features(type:0x%x) HATS = %d GATS = %d" 511 " MsiNumPPR = %d PNBanks= %d PNCounters= %d\n", 512 ivhd_type, 513 REG_BITS(feature, 31, 30), 514 REG_BITS(feature, 29, 28), 515 REG_BITS(feature, 27, 23), 516 REG_BITS(feature, 22, 17), 517 REG_BITS(feature, 16, 13)); 518 device_printf(dev, "max PASID = %d GLXSup = %d Feature:%b\n", 519 REG_BITS(feature, 12, 8), 520 REG_BITS(feature, 4, 3), 521 feature, 522 "\020" 523 "\002NXSup" 524 "\003GTSup" 525 "\004<b4>" 526 "\005IASup" 527 "\006GASup" 528 "\007HESup"); 529 break; 530 531 /* Fewer features or attributes are reported in non-legacy type. */ 532 case IVRS_TYPE_HARDWARE_EFR: 533 case IVRS_TYPE_HARDWARE_MIXED: 534 device_printf(dev, "Features(type:0x%x) MsiNumPPR = %d" 535 " PNBanks= %d PNCounters= %d\n", 536 ivhd_type, 537 REG_BITS(feature, 27, 23), 538 REG_BITS(feature, 22, 17), 539 REG_BITS(feature, 16, 13)); 540 break; 541 542 default: /* Other ivhd type features are not decoded. */ 543 device_printf(dev, "Can't decode ivhd type :0x%x\n", ivhd_type); 544 } 545 } 546 547 /* Print extended features of IOMMU. */ 548 static void 549 ivhd_print_ext_feature(device_t dev, uint64_t ext_feature) 550 { 551 uint32_t ext_low, ext_high; 552 553 if (!ext_feature) 554 return; 555 556 ext_low = ext_feature; 557 device_printf(dev, "Extended features[31:0]:%b " 558 "HATS = 0x%x GATS = 0x%x " 559 "GLXSup = 0x%x SmiFSup = 0x%x SmiFRC = 0x%x " 560 "GAMSup = 0x%x DualPortLogSup = 0x%x DualEventLogSup = 0x%x\n", 561 (int)ext_low, 562 "\020" 563 "\001PreFSup" 564 "\002PPRSup" 565 "\003<b2>" 566 "\004NXSup" 567 "\005GTSup" 568 "\006<b5>" 569 "\007IASup" 570 "\010GASup" 571 "\011HESup" 572 "\012PCSup", 573 REG_BITS(ext_low, 11, 10), 574 REG_BITS(ext_low, 13, 12), 575 REG_BITS(ext_low, 15, 14), 576 REG_BITS(ext_low, 17, 16), 577 REG_BITS(ext_low, 20, 18), 578 REG_BITS(ext_low, 23, 21), 579 REG_BITS(ext_low, 25, 24), 580 REG_BITS(ext_low, 29, 28)); 581 582 ext_high = ext_feature >> 32; 583 device_printf(dev, "Extended features[62:32]:%b " 584 "Max PASID: 0x%x DevTblSegSup = 0x%x " 585 "MarcSup = 0x%x\n", 586 (int)(ext_high), 587 "\020" 588 "\006USSup" 589 "\011PprOvrflwEarlySup" 590 "\012PPRAutoRspSup" 591 "\015BlKStopMrkSup" 592 "\016PerfOptSup" 593 "\017MsiCapMmioSup" 594 "\021GIOSup" 595 "\022HASup" 596 "\023EPHSup" 597 "\024AttrFWSup" 598 "\025HDSup" 599 "\027InvIotlbSup", 600 REG_BITS(ext_high, 5, 0), 601 REG_BITS(ext_high, 8, 7), 602 REG_BITS(ext_high, 11, 10)); 603 } 604 605 static int 606 ivhd_print_cap(struct amdvi_softc *softc, ACPI_IVRS_HARDWARE1 * ivhd) 607 { 608 device_t dev; 609 int max_ptp_level; 610 611 dev = softc->dev; 612 613 ivhd_print_flag(dev, softc->ivhd_type, softc->ivhd_flag); 614 ivhd_print_feature(dev, softc->ivhd_type, softc->ivhd_feature); 615 ivhd_print_ext_feature(dev, softc->ext_feature); 616 max_ptp_level = 7; 617 /* Make sure device support minimum page level as requested by user. */ 618 if (max_ptp_level < amdvi_ptp_level) { 619 device_printf(dev, "insufficient PTP level:%d\n", 620 max_ptp_level); 621 return (EINVAL); 622 } else { 623 device_printf(softc->dev, "supported paging level:%d, will use only: %d\n", 624 max_ptp_level, amdvi_ptp_level); 625 } 626 627 return (0); 628 } 629 630 static int 631 ivhd_attach(device_t dev) 632 { 633 ACPI_IVRS_HARDWARE1 *ivhd; 634 ACPI_IVRS_HARDWARE2 *ivhd_efr; 635 struct amdvi_softc *softc; 636 int status, unit; 637 638 unit = device_get_unit(dev); 639 KASSERT((unit < ivhd_count), 640 ("ivhd unit %d > count %d", unit, ivhd_count)); 641 /* Make sure its same device for which attach is called. */ 642 KASSERT((ivhd_devs[unit] == dev), 643 ("Not same device old %p new %p", ivhd_devs[unit], dev)); 644 645 softc = device_get_softc(dev); 646 softc->dev = dev; 647 ivhd = ivhd_hdrs[unit]; 648 KASSERT(ivhd, ("ivhd is NULL")); 649 softc->pci_dev = pci_find_bsf(PCI_RID2BUS(ivhd->Header.DeviceId), 650 PCI_RID2SLOT(ivhd->Header.DeviceId), 651 PCI_RID2FUNC(ivhd->Header.DeviceId)); 652 653 softc->ivhd_type = ivhd->Header.Type; 654 softc->pci_seg = ivhd->PciSegmentGroup; 655 softc->pci_rid = ivhd->Header.DeviceId; 656 softc->ivhd_flag = ivhd->Header.Flags; 657 /* 658 * On lgeacy IVHD type(0x10), it is documented as feature 659 * but in newer type it is attribute. 660 */ 661 softc->ivhd_feature = ivhd->FeatureReporting; 662 /* 663 * PCI capability has more capabilities that are not part of IVRS. 664 */ 665 softc->cap_off = ivhd->CapabilityOffset; 666 667 #ifdef notyet 668 /* IVHD Info bit[4:0] is event MSI/X number. */ 669 softc->event_msix = ivhd->Info & 0x1F; 670 #endif 671 switch (ivhd->Header.Type) { 672 case IVRS_TYPE_HARDWARE_EFR: 673 case IVRS_TYPE_HARDWARE_MIXED: 674 ivhd_efr = (ACPI_IVRS_HARDWARE2 *)ivhd; 675 softc->ext_feature = ivhd_efr->EfrRegisterImage; 676 break; 677 } 678 679 softc->ctrl = (struct amdvi_ctrl *) PHYS_TO_DMAP(ivhd->BaseAddress); 680 status = ivhd_dev_parse(ivhd, softc); 681 if (status != 0) { 682 device_printf(dev, 683 "endpoint device parsing error=%d\n", status); 684 goto fail; 685 } 686 687 status = ivhd_print_cap(softc, ivhd); 688 if (status != 0) 689 goto fail; 690 691 status = amdvi_setup_hw(softc); 692 if (status != 0) { 693 device_printf(dev, "couldn't be initialised, error=%d\n", 694 status); 695 goto fail; 696 } 697 698 return (0); 699 700 fail: 701 free(softc->dev_cfg, M_DEVBUF); 702 return (status); 703 } 704 705 static int 706 ivhd_detach(device_t dev) 707 { 708 struct amdvi_softc *softc; 709 710 softc = device_get_softc(dev); 711 712 amdvi_teardown_hw(softc); 713 free(softc->dev_cfg, M_DEVBUF); 714 715 /* 716 * XXX: delete the device. 717 * don't allow detach, return EBUSY. 718 */ 719 return (0); 720 } 721 722 static int 723 ivhd_suspend(device_t dev) 724 { 725 726 return (0); 727 } 728 729 static int 730 ivhd_resume(device_t dev) 731 { 732 733 return (0); 734 } 735 736 static device_method_t ivhd_methods[] = { 737 DEVMETHOD(device_identify, ivhd_identify), 738 DEVMETHOD(device_probe, ivhd_probe), 739 DEVMETHOD(device_attach, ivhd_attach), 740 DEVMETHOD(device_detach, ivhd_detach), 741 DEVMETHOD(device_suspend, ivhd_suspend), 742 DEVMETHOD(device_resume, ivhd_resume), 743 DEVMETHOD_END 744 }; 745 746 static driver_t ivhd_driver = { 747 "ivhd", 748 ivhd_methods, 749 sizeof(struct amdvi_softc), 750 }; 751 752 static devclass_t ivhd_devclass; 753 754 /* 755 * Load this module at the end after PCI re-probing to configure interrupt. 756 */ 757 DRIVER_MODULE_ORDERED(ivhd, acpi, ivhd_driver, ivhd_devclass, 0, 0, 758 SI_ORDER_ANY); 759 MODULE_DEPEND(ivhd, acpi, 1, 1, 1); 760 MODULE_DEPEND(ivhd, pci, 1, 1, 1); 761