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