Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

PCI/ACPI: Optimize device state transition delays

The PCI "ACPI additions for FW latency optimizations" ECN (link below)
defines two functions in the PCI _DSM:

Function 8, "Reset Delay," applies to the entire hierarchy below a PCI
host bridge. If it returns one, the OS may assume that all devices in
the hierarchy have already completed power-on reset delays.

Function 9, "Device Readiness Durations," applies only to the object
where it is located. It returns delay durations required after various
events if the device requires less time than the spec requires. Delays
from this function take precedence over the Reset Delay function.

Add support for Reset Delay and part of Device Readiness Durations.

[bhelgaas: changelog, comments]
Link: https://www.pcisig.com/specifications/conventional/pci_firmware/ECN_fw_latency_optimization_final.pdf
Signed-off-by: Aaron Lu <aaron.lu@intel.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>

authored by

Aaron Lu and committed by
Bjorn Helgaas
e33caa82 3390e085

+77
+74
drivers/pci/pci-acpi.c
··· 537 537 538 538 void acpi_pci_add_bus(struct pci_bus *bus) 539 539 { 540 + union acpi_object *obj; 541 + struct pci_host_bridge *bridge; 542 + 540 543 if (acpi_pci_disabled || !bus->bridge) 541 544 return; 542 545 543 546 acpi_pci_slot_enumerate(bus); 544 547 acpiphp_enumerate_slots(bus); 548 + 549 + /* 550 + * For a host bridge, check its _DSM for function 8 and if 551 + * that is available, mark it in pci_host_bridge. 552 + */ 553 + if (!pci_is_root_bus(bus)) 554 + return; 555 + 556 + obj = acpi_evaluate_dsm(ACPI_HANDLE(bus->bridge), pci_acpi_dsm_uuid, 3, 557 + RESET_DELAY_DSM, NULL); 558 + if (!obj) 559 + return; 560 + 561 + if (obj->type == ACPI_TYPE_INTEGER && obj->integer.value == 1) { 562 + bridge = pci_find_host_bridge(bus); 563 + bridge->ignore_reset_delay = 1; 564 + } 565 + ACPI_FREE(obj); 545 566 } 546 567 547 568 void acpi_pci_remove_bus(struct pci_bus *bus) ··· 588 567 check_children); 589 568 } 590 569 570 + /** 571 + * pci_acpi_optimize_delay - optimize PCI D3 and D3cold delay from ACPI 572 + * @pdev: the PCI device whose delay is to be updated 573 + * @adev: the companion ACPI device of this PCI device 574 + * 575 + * Update the d3_delay and d3cold_delay of a PCI device from the ACPI _DSM 576 + * control method of either the device itself or the PCI host bridge. 577 + * 578 + * Function 8, "Reset Delay," applies to the entire hierarchy below a PCI 579 + * host bridge. If it returns one, the OS may assume that all devices in 580 + * the hierarchy have already completed power-on reset delays. 581 + * 582 + * Function 9, "Device Readiness Durations," applies only to the object 583 + * where it is located. It returns delay durations required after various 584 + * events if the device requires less time than the spec requires. Delays 585 + * from this function take precedence over the Reset Delay function. 586 + * 587 + * These _DSM functions are defined by the draft ECN of January 28, 2014, 588 + * titled "ACPI additions for FW latency optimizations." 589 + */ 590 + static void pci_acpi_optimize_delay(struct pci_dev *pdev, 591 + acpi_handle handle) 592 + { 593 + struct pci_host_bridge *bridge = pci_find_host_bridge(pdev->bus); 594 + int value; 595 + union acpi_object *obj, *elements; 596 + 597 + if (bridge->ignore_reset_delay) 598 + pdev->d3cold_delay = 0; 599 + 600 + obj = acpi_evaluate_dsm(handle, pci_acpi_dsm_uuid, 3, 601 + FUNCTION_DELAY_DSM, NULL); 602 + if (!obj) 603 + return; 604 + 605 + if (obj->type == ACPI_TYPE_PACKAGE && obj->package.count == 5) { 606 + elements = obj->package.elements; 607 + if (elements[0].type == ACPI_TYPE_INTEGER) { 608 + value = (int)elements[0].integer.value / 1000; 609 + if (value < PCI_PM_D3COLD_WAIT) 610 + pdev->d3cold_delay = value; 611 + } 612 + if (elements[3].type == ACPI_TYPE_INTEGER) { 613 + value = (int)elements[3].integer.value / 1000; 614 + if (value < PCI_PM_D3_WAIT) 615 + pdev->d3_delay = value; 616 + } 617 + } 618 + ACPI_FREE(obj); 619 + } 620 + 591 621 static void pci_acpi_setup(struct device *dev) 592 622 { 593 623 struct pci_dev *pci_dev = to_pci_dev(dev); ··· 646 574 647 575 if (!adev) 648 576 return; 577 + 578 + pci_acpi_optimize_delay(pci_dev, adev->handle); 649 579 650 580 pci_acpi_add_pm_notifier(adev, pci_dev); 651 581 if (!adev->wakeup.flags.valid)
+2
include/linux/pci-acpi.h
··· 79 79 80 80 extern const u8 pci_acpi_dsm_uuid[]; 81 81 #define DEVICE_LABEL_DSM 0x07 82 + #define RESET_DELAY_DSM 0x08 83 + #define FUNCTION_DELAY_DSM 0x09 82 84 83 85 #else /* CONFIG_ACPI */ 84 86 static inline void acpi_pci_add_bus(struct pci_bus *bus) { }
+1
include/linux/pci.h
··· 406 406 struct list_head windows; /* resource_entry */ 407 407 void (*release_fn)(struct pci_host_bridge *); 408 408 void *release_data; 409 + unsigned int ignore_reset_delay:1; /* for entire hierarchy */ 409 410 }; 410 411 411 412 #define to_pci_host_bridge(n) container_of(n, struct pci_host_bridge, dev)