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