1 /* 2 * Copyright 2007, Hugo Santos. All Rights Reserved. 3 * Copyright 2004, Marcus Overhagen. All Rights Reserved. 4 * Distributed under the terms of the MIT License. 5 */ 6 7 8 extern "C" { 9 #include "device.h" 10 } 11 12 #include <stdlib.h> 13 14 #include <algorithm> 15 16 #include <arch/cpu.h> 17 18 extern "C" { 19 #include <compat/dev/pci/pcireg.h> 20 #include <compat/dev/pci/pcivar.h> 21 #include <compat/machine/resource.h> 22 #include <compat/sys/mutex.h> 23 #include <compat/machine/bus.h> 24 #include <compat/sys/rman.h> 25 #include <compat/sys/bus.h> 26 } 27 28 // private kernel header to get B_NO_HANDLED_INFO 29 #include <int.h> 30 31 #include <PCI_x86.h> 32 33 34 //#define DEBUG_BUS_SPACE_RW 35 #ifdef DEBUG_BUS_SPACE_RW 36 # define TRACE_BUS_SPACE_RW(x) driver_printf x 37 #else 38 # define TRACE_BUS_SPACE_RW(x) 39 #endif 40 41 //#define DEBUG_PCI 42 #ifdef DEBUG_PCI 43 # define TRACE_PCI(dev, format, args...) device_printf(dev, format , ##args) 44 #else 45 # define TRACE_PCI(dev, format, args...) do { } while (0) 46 #endif 47 48 49 struct internal_intr { 50 device_t dev; 51 driver_filter_t filter; 52 driver_intr_t *handler; 53 void *arg; 54 int irq; 55 uint32 flags; 56 57 thread_id thread; 58 sem_id sem; 59 int32 handling; 60 }; 61 62 static int32 intr_wrapper(void *data); 63 64 65 static int 66 fls(int mask) 67 { 68 int bit; 69 if (mask == 0) 70 return (0); 71 for (bit = 1; mask != 1; bit++) 72 mask = (unsigned int)mask >> 1; 73 return (bit); 74 } 75 76 77 static area_id 78 map_mem(void **virtualAddr, phys_addr_t _phy, size_t size, uint32 protection, 79 const char *name) 80 { 81 uint32 offset = _phy & (B_PAGE_SIZE - 1); 82 phys_addr_t physicalAddr = _phy - offset; 83 area_id area; 84 85 size = roundup(size + offset, B_PAGE_SIZE); 86 area = map_physical_memory(name, physicalAddr, size, B_ANY_KERNEL_ADDRESS, 87 protection, virtualAddr); 88 if (area < B_OK) 89 return area; 90 91 *virtualAddr = (uint8 *)(*virtualAddr) + offset; 92 93 return area; 94 } 95 96 97 static int 98 bus_alloc_irq_resource(device_t dev, struct resource *res) 99 { 100 uint8 irq = pci_read_config(dev, PCI_interrupt_line, 1); 101 if (irq == 0 || irq == 0xff) 102 return -1; 103 104 /* TODO: IRQ resources! */ 105 res->r_bustag = 0; 106 res->r_bushandle = irq; 107 108 return 0; 109 } 110 111 112 static int 113 bus_alloc_mem_resource(device_t dev, struct resource *res, int regid) 114 { 115 /* TODO: check the offset really is of a BAR */ 116 uint32 bar = pci_read_config(dev, regid, 4); 117 uint32 addr = bar & PCI_address_memory_32_mask; 118 pci_write_config(dev, regid, ~0, 4); 119 uint32 size = pci_read_config(dev, regid, 4) & PCI_address_memory_32_mask; 120 size = (~size) + 1; 121 pci_write_config(dev, regid, bar, 4); 122 void *virtualAddr; 123 124 res->r_mapped_area = map_mem(&virtualAddr, addr, size, 0, 125 "bus_alloc_resource(MEMORY)"); 126 if (res->r_mapped_area < B_OK) 127 return -1; 128 129 res->r_bustag = X86_BUS_SPACE_MEM; 130 res->r_bushandle = (bus_space_handle_t)virtualAddr; 131 return 0; 132 } 133 134 135 static int 136 bus_alloc_ioport_resource(device_t dev, struct resource *res, int regid) 137 { 138 res->r_bustag = X86_BUS_SPACE_IO; 139 res->r_bushandle = pci_read_config(dev, regid, 4) & PCI_address_io_mask; 140 return 0; 141 } 142 143 144 struct resource * 145 bus_alloc_resource(device_t dev, int type, int *rid, unsigned long start, 146 unsigned long end, unsigned long count, uint32 flags) 147 { 148 struct resource *res; 149 int result = -1; 150 151 if (type != SYS_RES_IRQ && type != SYS_RES_MEMORY 152 && type != SYS_RES_IOPORT) 153 return NULL; 154 155 device_printf(dev, "bus_alloc_resource(%i, [%i], 0x%lx, 0x%lx, 0x%lx," 156 "0x%" B_PRIx32 ")\n", type, *rid, start, end, count, flags); 157 158 // maybe a local array of resources is enough 159 res = (struct resource *)malloc(sizeof(struct resource)); 160 if (res == NULL) 161 return NULL; 162 163 if (type == SYS_RES_IRQ) { 164 if (*rid == 0) { 165 // pinned interrupt 166 result = bus_alloc_irq_resource(dev, res); 167 } else { 168 // msi or msi-x interrupt at index *rid - 1 169 pci_info *info; 170 info = &((struct root_device_softc *)dev->root->softc)->pci_info; 171 res->r_bustag = 1; 172 res->r_bushandle = info->u.h0.interrupt_line + *rid - 1; 173 result = 0; 174 } 175 } else if (type == SYS_RES_MEMORY) 176 result = bus_alloc_mem_resource(dev, res, *rid); 177 else if (type == SYS_RES_IOPORT) 178 result = bus_alloc_ioport_resource(dev, res, *rid); 179 180 if (result < 0) { 181 free(res); 182 return NULL; 183 } 184 185 res->r_type = type; 186 return res; 187 } 188 189 190 int 191 bus_release_resource(device_t dev, int type, int rid, struct resource *res) 192 { 193 if (res->r_type != type) 194 panic("bus_release_resource: mismatch"); 195 196 if (type == SYS_RES_MEMORY) 197 delete_area(res->r_mapped_area); 198 199 free(res); 200 return 0; 201 } 202 203 204 int 205 bus_alloc_resources(device_t dev, struct resource_spec *resourceSpec, 206 struct resource **resources) 207 { 208 int i; 209 210 for (i = 0; resourceSpec[i].type != -1; i++) { 211 resources[i] = bus_alloc_resource_any(dev, 212 resourceSpec[i].type, &resourceSpec[i].rid, resourceSpec[i].flags); 213 if (resources[i] == NULL 214 && (resourceSpec[i].flags & RF_OPTIONAL) == 0) { 215 for (++i; resourceSpec[i].type != -1; i++) { 216 resources[i] = NULL; 217 } 218 219 bus_release_resources(dev, resourceSpec, resources); 220 return ENXIO; 221 } 222 } 223 return 0; 224 } 225 226 227 void 228 bus_release_resources(device_t dev, const struct resource_spec *resourceSpec, 229 struct resource **resources) 230 { 231 int i; 232 233 for (i = 0; resourceSpec[i].type != -1; i++) { 234 if (resources[i] == NULL) 235 continue; 236 237 bus_release_resource(dev, resourceSpec[i].type, resourceSpec[i].rid, 238 resources[i]); 239 resources[i] = NULL; 240 } 241 } 242 243 244 bus_space_handle_t 245 rman_get_bushandle(struct resource *res) 246 { 247 return res->r_bushandle; 248 } 249 250 251 bus_space_tag_t 252 rman_get_bustag(struct resource *res) 253 { 254 return res->r_bustag; 255 } 256 257 258 int 259 rman_get_rid(struct resource *res) 260 { 261 return 0; 262 } 263 264 265 void* 266 rman_get_virtual(struct resource *res) 267 { 268 return NULL; 269 } 270 271 272 // #pragma mark - Interrupt handling 273 274 275 static int32 276 intr_wrapper(void *data) 277 { 278 struct internal_intr *intr = (struct internal_intr *)data; 279 280 //device_printf(intr->dev, "in interrupt handler.\n"); 281 282 if (!HAIKU_CHECK_DISABLE_INTERRUPTS(intr->dev)) 283 return B_UNHANDLED_INTERRUPT; 284 285 release_sem_etc(intr->sem, 1, B_DO_NOT_RESCHEDULE); 286 return intr->handling ? B_HANDLED_INTERRUPT : B_INVOKE_SCHEDULER; 287 } 288 289 290 static int32 291 intr_handler(void *data) 292 { 293 struct internal_intr *intr = (struct internal_intr *)data; 294 status_t status; 295 296 while (1) { 297 status = acquire_sem(intr->sem); 298 if (status < B_OK) 299 break; 300 301 //device_printf(intr->dev, "in soft interrupt handler.\n"); 302 303 atomic_or(&intr->handling, 1); 304 intr->handler(intr->arg); 305 atomic_and(&intr->handling, 0); 306 HAIKU_REENABLE_INTERRUPTS(intr->dev); 307 } 308 309 return 0; 310 } 311 312 313 static void 314 free_internal_intr(struct internal_intr *intr) 315 { 316 if (intr->sem >= B_OK) { 317 status_t status; 318 delete_sem(intr->sem); 319 wait_for_thread(intr->thread, &status); 320 } 321 322 free(intr); 323 } 324 325 326 int 327 bus_setup_intr(device_t dev, struct resource *res, int flags, 328 driver_filter_t filter, driver_intr_t handler, void *arg, void **_cookie) 329 { 330 /* TODO check MPSAFE etc */ 331 332 struct internal_intr *intr = (struct internal_intr *)malloc( 333 sizeof(struct internal_intr)); 334 char semName[64]; 335 status_t status; 336 337 if (intr == NULL) 338 return B_NO_MEMORY; 339 340 intr->dev = dev; 341 intr->filter = filter; 342 intr->handler = handler; 343 intr->arg = arg; 344 intr->irq = res->r_bushandle; 345 intr->flags = flags; 346 intr->sem = -1; 347 intr->thread = -1; 348 349 if (filter != NULL) { 350 status = install_io_interrupt_handler(intr->irq, 351 (interrupt_handler)intr->filter, intr->arg, 0); 352 } else { 353 snprintf(semName, sizeof(semName), "%s intr", dev->device_name); 354 355 intr->sem = create_sem(0, semName); 356 if (intr->sem < B_OK) { 357 free(intr); 358 return B_NO_MEMORY; 359 } 360 361 snprintf(semName, sizeof(semName), "%s intr handler", dev->device_name); 362 363 intr->thread = spawn_kernel_thread(intr_handler, semName, 364 B_REAL_TIME_DISPLAY_PRIORITY, intr); 365 if (intr->thread < B_OK) { 366 delete_sem(intr->sem); 367 free(intr); 368 return B_NO_MEMORY; 369 } 370 371 status = install_io_interrupt_handler(intr->irq, 372 intr_wrapper, intr, 0); 373 } 374 375 if (status == B_OK && res->r_bustag == 1 && gPCIx86 != NULL) { 376 // this is an msi, enable it 377 pci_info *info 378 = &((struct root_device_softc *)dev->root->softc)->pci_info; 379 if (((struct root_device_softc *)dev->root->softc)->is_msi) { 380 if (gPCIx86->enable_msi(info->bus, info->device, 381 info->function) != B_OK) { 382 device_printf(dev, "enabling msi failed\n"); 383 bus_teardown_intr(dev, res, intr); 384 return ENODEV; 385 } 386 } else if (((struct root_device_softc *)dev->root->softc)->is_msix) { 387 if (gPCIx86->enable_msix(info->bus, info->device, 388 info->function) != B_OK) { 389 device_printf(dev, "enabling msix failed\n"); 390 bus_teardown_intr(dev, res, intr); 391 return ENODEV; 392 } 393 } 394 } 395 396 if (status < B_OK) { 397 free_internal_intr(intr); 398 return status; 399 } 400 401 resume_thread(intr->thread); 402 403 *_cookie = intr; 404 return 0; 405 } 406 407 408 int 409 bus_teardown_intr(device_t dev, struct resource *res, void *arg) 410 { 411 struct internal_intr *intr = (struct internal_intr *)arg; 412 if (intr == NULL) 413 return -1; 414 415 struct root_device_softc *root = (struct root_device_softc *)dev->root->softc; 416 417 if ((root->is_msi || root->is_msix) && gPCIx86 != NULL) { 418 // disable msi generation 419 pci_info *info = &root->pci_info; 420 gPCIx86->disable_msi(info->bus, info->device, info->function); 421 } 422 423 if (intr->filter != NULL) { 424 remove_io_interrupt_handler(intr->irq, (interrupt_handler)intr->filter, 425 intr->arg); 426 } else { 427 remove_io_interrupt_handler(intr->irq, intr_wrapper, intr); 428 } 429 430 free_internal_intr(intr); 431 return 0; 432 } 433 434 435 int 436 bus_bind_intr(device_t dev, struct resource *res, int cpu) 437 { 438 if (dev->parent == NULL) 439 return EINVAL; 440 441 // TODO 442 return 0; 443 } 444 445 446 int bus_describe_intr(device_t dev, struct resource *irq, void *cookie, 447 const char* fmt, ...) 448 { 449 if (dev->parent == NULL) 450 return EINVAL; 451 452 // we don't really support names for interrupts 453 return 0; 454 } 455 456 457 // #pragma mark - bus functions 458 459 460 bus_dma_tag_t 461 bus_get_dma_tag(device_t dev) 462 { 463 return NULL; 464 } 465 466 467 int 468 bus_generic_suspend(device_t dev) 469 { 470 UNIMPLEMENTED(); 471 return B_ERROR; 472 } 473 474 475 int 476 bus_generic_resume(device_t dev) 477 { 478 UNIMPLEMENTED(); 479 return B_ERROR; 480 } 481 482 483 void 484 bus_generic_shutdown(device_t dev) 485 { 486 UNIMPLEMENTED(); 487 } 488 489 490 int 491 bus_print_child_header(device_t dev, device_t child) 492 { 493 UNIMPLEMENTED(); 494 return B_ERROR; 495 } 496 497 498 int 499 bus_print_child_footer(device_t dev, device_t child) 500 { 501 UNIMPLEMENTED(); 502 return B_ERROR; 503 } 504 505 506 int 507 bus_generic_print_child(device_t dev, device_t child) 508 { 509 UNIMPLEMENTED(); 510 return B_ERROR; 511 } 512 513 514 void 515 bus_generic_driver_added(device_t dev, driver_t *driver) 516 { 517 UNIMPLEMENTED(); 518 } 519 520 521 int 522 bus_child_present(device_t child) 523 { 524 device_t parent = device_get_parent(child); 525 if (parent == NULL) 526 return 0; 527 528 return bus_child_present(parent); 529 } 530 531 532 void 533 bus_enumerate_hinted_children(device_t bus) 534 { 535 #if 0 536 UNIMPLEMENTED(); 537 #endif 538 } 539 540 541 542 // #pragma mark - PCI functions 543 544 545 uint32_t 546 pci_read_config(device_t dev, int offset, int size) 547 { 548 pci_info *info = &((struct root_device_softc *)dev->root->softc)->pci_info; 549 550 uint32_t value = gPci->read_pci_config(info->bus, info->device, 551 info->function, offset, size); 552 TRACE_PCI(dev, "pci_read_config(%i, %i) = 0x%x\n", offset, size, value); 553 return value; 554 } 555 556 557 void 558 pci_write_config(device_t dev, int offset, uint32_t value, int size) 559 { 560 pci_info *info = &((struct root_device_softc *)dev->root->softc)->pci_info; 561 562 TRACE_PCI(dev, "pci_write_config(%i, 0x%x, %i)\n", offset, value, size); 563 564 gPci->write_pci_config(info->bus, info->device, info->function, offset, 565 size, value); 566 } 567 568 569 uint16_t 570 pci_get_vendor(device_t dev) 571 { 572 return pci_read_config(dev, PCI_vendor_id, 2); 573 } 574 575 576 uint16_t 577 pci_get_device(device_t dev) 578 { 579 return pci_read_config(dev, PCI_device_id, 2); 580 } 581 582 583 uint16_t 584 pci_get_subvendor(device_t dev) 585 { 586 return pci_read_config(dev, PCI_subsystem_vendor_id, 2); 587 } 588 589 590 uint16_t 591 pci_get_subdevice(device_t dev) 592 { 593 return pci_read_config(dev, PCI_subsystem_id, 2); 594 } 595 596 597 uint8_t 598 pci_get_revid(device_t dev) 599 { 600 return pci_read_config(dev, PCI_revision, 1); 601 } 602 603 604 uint32_t 605 pci_get_domain(device_t dev) 606 { 607 return 0; 608 } 609 610 uint32_t 611 pci_get_devid(device_t dev) 612 { 613 return pci_read_config(dev, PCI_device_id, 2) << 16 | 614 pci_read_config(dev, PCI_vendor_id, 2); 615 } 616 617 uint8_t 618 pci_get_cachelnsz(device_t dev) 619 { 620 return pci_read_config(dev, PCI_line_size, 1); 621 } 622 623 uint8_t * 624 pci_get_ether(device_t dev) 625 { 626 /* used in if_dc to get the MAC from CardBus CIS for Xircom card */ 627 return NULL; /* NULL is handled in the caller correctly */ 628 } 629 630 uint8_t 631 pci_get_bus(device_t dev) 632 { 633 pci_info *info 634 = &((struct root_device_softc *)dev->root->softc)->pci_info; 635 return info->bus; 636 } 637 638 639 uint8_t 640 pci_get_slot(device_t dev) 641 { 642 pci_info *info 643 = &((struct root_device_softc *)dev->root->softc)->pci_info; 644 return info->device; 645 } 646 647 648 uint8_t 649 pci_get_function(device_t dev) 650 { 651 pci_info *info 652 = &((struct root_device_softc *)dev->root->softc)->pci_info; 653 return info->function; 654 } 655 656 657 device_t 658 pci_find_dbsf(uint32_t domain, uint8_t bus, uint8_t slot, uint8_t func) 659 { 660 // We don't support that yet - if we want to support the multi port 661 // feature of the Broadcom BCM 570x driver, we would have to change 662 // that. 663 return NULL; 664 } 665 666 667 static void 668 pci_set_command_bit(device_t dev, uint16_t bit) 669 { 670 uint16_t command = pci_read_config(dev, PCI_command, 2); 671 pci_write_config(dev, PCI_command, command | bit, 2); 672 } 673 674 675 int 676 pci_enable_busmaster(device_t dev) 677 { 678 pci_set_command_bit(dev, PCI_command_master); 679 return 0; 680 } 681 682 683 int 684 pci_enable_io(device_t dev, int space) 685 { 686 /* adapted from FreeBSD's pci_enable_io_method */ 687 int bit = 0; 688 689 switch (space) { 690 case SYS_RES_IOPORT: 691 bit = PCI_command_io; 692 break; 693 case SYS_RES_MEMORY: 694 bit = PCI_command_memory; 695 break; 696 default: 697 return EINVAL; 698 } 699 700 pci_set_command_bit(dev, bit); 701 if (pci_read_config(dev, PCI_command, 2) & bit) 702 return 0; 703 704 device_printf(dev, "pci_enable_io(%d) failed.\n", space); 705 706 return ENXIO; 707 } 708 709 710 int 711 pci_find_cap(device_t dev, int capability, int *capreg) 712 { 713 return pci_find_extcap(dev, capability, capreg); 714 } 715 716 717 int 718 pci_find_extcap(device_t child, int capability, int *_capabilityRegister) 719 { 720 uint8 capabilityPointer; 721 uint8 headerType; 722 uint16 status; 723 724 status = pci_read_config(child, PCIR_STATUS, 2); 725 if ((status & PCIM_STATUS_CAPPRESENT) == 0) 726 return ENXIO; 727 728 headerType = pci_read_config(child, PCI_header_type, 1); 729 switch (headerType & PCIM_HDRTYPE) { 730 case 0: 731 case 1: 732 capabilityPointer = PCIR_CAP_PTR; 733 break; 734 case 2: 735 capabilityPointer = PCIR_CAP_PTR_2; 736 break; 737 default: 738 return ENXIO; 739 } 740 capabilityPointer = pci_read_config(child, capabilityPointer, 1); 741 742 while (capabilityPointer != 0) { 743 if (pci_read_config(child, capabilityPointer + PCICAP_ID, 1) 744 == capability) { 745 if (_capabilityRegister != NULL) 746 *_capabilityRegister = capabilityPointer; 747 return 0; 748 } 749 capabilityPointer = pci_read_config(child, 750 capabilityPointer + PCICAP_NEXTPTR, 1); 751 } 752 753 return ENOENT; 754 } 755 756 757 int 758 pci_msi_count(device_t dev) 759 { 760 pci_info *info; 761 if (gPCIx86 == NULL) 762 return 0; 763 764 info = &((struct root_device_softc *)dev->root->softc)->pci_info; 765 return gPCIx86->get_msi_count(info->bus, info->device, info->function); 766 } 767 768 769 int 770 pci_alloc_msi(device_t dev, int *count) 771 { 772 pci_info *info; 773 uint8 startVector = 0; 774 if (gPCIx86 == NULL) 775 return ENODEV; 776 777 info = &((struct root_device_softc *)dev->root->softc)->pci_info; 778 779 if (gPCIx86->configure_msi(info->bus, info->device, info->function, *count, 780 &startVector) != B_OK) { 781 return ENODEV; 782 } 783 784 ((struct root_device_softc *)dev->root->softc)->is_msi = true; 785 info->u.h0.interrupt_line = startVector; 786 return EOK; 787 } 788 789 790 int 791 pci_release_msi(device_t dev) 792 { 793 pci_info *info; 794 if (gPCIx86 == NULL) 795 return ENODEV; 796 797 info = &((struct root_device_softc *)dev->root->softc)->pci_info; 798 gPCIx86->unconfigure_msi(info->bus, info->device, info->function); 799 ((struct root_device_softc *)dev->root->softc)->is_msi = false; 800 ((struct root_device_softc *)dev->root->softc)->is_msix = false; 801 return EOK; 802 } 803 804 805 int 806 pci_msix_count(device_t dev) 807 { 808 pci_info *info; 809 if (gPCIx86 == NULL) 810 return 0; 811 812 info = &((struct root_device_softc *)dev->root->softc)->pci_info; 813 return gPCIx86->get_msix_count(info->bus, info->device, info->function); 814 } 815 816 817 int 818 pci_alloc_msix(device_t dev, int *count) 819 { 820 pci_info *info; 821 uint8 startVector = 0; 822 if (gPCIx86 == NULL) 823 return ENODEV; 824 825 info = &((struct root_device_softc *)dev->root->softc)->pci_info; 826 827 if (gPCIx86->configure_msix(info->bus, info->device, info->function, *count, 828 &startVector) != B_OK) { 829 return ENODEV; 830 } 831 832 ((struct root_device_softc *)dev->root->softc)->is_msix = true; 833 info->u.h0.interrupt_line = startVector; 834 return EOK; 835 } 836 837 838 int 839 pci_get_max_read_req(device_t dev) 840 { 841 int cap; 842 uint16_t val; 843 844 if (pci_find_extcap(dev, PCIY_EXPRESS, &cap) != 0) 845 return (0); 846 val = pci_read_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, 2); 847 val &= PCIM_EXP_CTL_MAX_READ_REQUEST; 848 val >>= 12; 849 return (1 << (val + 7)); 850 } 851 852 853 int 854 pci_set_max_read_req(device_t dev, int size) 855 { 856 int cap; 857 uint16_t val; 858 859 if (pci_find_extcap(dev, PCIY_EXPRESS, &cap) != 0) 860 return (0); 861 if (size < 128) 862 size = 128; 863 if (size > 4096) 864 size = 4096; 865 size = (1 << (fls(size) - 1)); 866 val = pci_read_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, 2); 867 val &= ~PCIM_EXP_CTL_MAX_READ_REQUEST; 868 val |= (fls(size) - 8) << 12; 869 pci_write_config(dev, cap + PCIR_EXPRESS_DEVICE_CTL, val, 2); 870 return (size); 871 } 872 873 874 int 875 pci_get_powerstate(device_t dev) 876 { 877 int capabilityRegister; 878 uint16 status; 879 int powerState = PCI_POWERSTATE_D0; 880 881 if (pci_find_extcap(dev, PCIY_PMG, &capabilityRegister) != EOK) 882 return powerState; 883 884 status = pci_read_config(dev, capabilityRegister + PCIR_POWER_STATUS, 2); 885 switch (status & PCI_pm_mask) { 886 case PCI_pm_state_d0: 887 break; 888 case PCI_pm_state_d1: 889 powerState = PCI_POWERSTATE_D1; 890 break; 891 case PCI_pm_state_d2: 892 powerState = PCI_POWERSTATE_D2; 893 break; 894 case PCI_pm_state_d3: 895 powerState = PCI_POWERSTATE_D3; 896 break; 897 default: 898 powerState = PCI_POWERSTATE_UNKNOWN; 899 break; 900 } 901 902 TRACE_PCI(dev, "%s: D%i\n", __func__, powerState); 903 return powerState; 904 } 905 906 907 int 908 pci_set_powerstate(device_t dev, int newPowerState) 909 { 910 int capabilityRegister; 911 int oldPowerState; 912 uint8 currentPowerManagementStatus; 913 uint8 newPowerManagementStatus; 914 uint16 powerManagementCapabilities; 915 bigtime_t stateTransitionDelayInUs = 0; 916 917 if (pci_find_extcap(dev, PCIY_PMG, &capabilityRegister) != EOK) 918 return EOPNOTSUPP; 919 920 oldPowerState = pci_get_powerstate(dev); 921 if (oldPowerState == newPowerState) 922 return EOK; 923 924 switch (std::max(oldPowerState, newPowerState)) { 925 case PCI_POWERSTATE_D2: 926 stateTransitionDelayInUs = 200; 927 break; 928 case PCI_POWERSTATE_D3: 929 stateTransitionDelayInUs = 10000; 930 break; 931 } 932 933 currentPowerManagementStatus = pci_read_config(dev, capabilityRegister 934 + PCIR_POWER_STATUS, 2); 935 newPowerManagementStatus = currentPowerManagementStatus & ~PCI_pm_mask; 936 powerManagementCapabilities = pci_read_config(dev, capabilityRegister 937 + PCIR_POWER_CAP, 2); 938 939 switch (newPowerState) { 940 case PCI_POWERSTATE_D0: 941 newPowerManagementStatus |= PCIM_PSTAT_D0; 942 break; 943 case PCI_POWERSTATE_D1: 944 if ((powerManagementCapabilities & PCI_pm_d1supp) == 0) 945 return EOPNOTSUPP; 946 newPowerManagementStatus |= PCIM_PSTAT_D1; 947 break; 948 case PCI_POWERSTATE_D2: 949 if ((powerManagementCapabilities & PCI_pm_d2supp) == 0) 950 return EOPNOTSUPP; 951 newPowerManagementStatus |= PCIM_PSTAT_D2; 952 break; 953 case PCI_POWERSTATE_D3: 954 newPowerManagementStatus |= PCIM_PSTAT_D3; 955 break; 956 default: 957 return EINVAL; 958 } 959 960 TRACE_PCI(dev, "%s: D%i -> D%i\n", __func__, oldPowerState, newPowerState); 961 pci_write_config(dev, capabilityRegister + PCIR_POWER_STATUS, newPowerState, 962 2); 963 if (stateTransitionDelayInUs != 0) 964 snooze(stateTransitionDelayInUs); 965 966 return EOK; 967 } 968