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