aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMika Westerberg <mika.westerberg@linux.intel.com>2018-09-27 17:57:14 -0400
committerBjorn Helgaas <bhelgaas@google.com>2018-10-02 17:04:40 -0400
commit26ad34d510a87fc65caeb48fa85cce58d2477a88 (patch)
tree875312464f915fc088e0d258e97fc4504e249582
parent5f5e4890d57a8af5da72c9d73a4efa9bad43a7a3 (diff)
PCI / ACPI: Whitelist D3 for more PCIe hotplug ports
In order to have better power management for Thunderbolt PCIe chains, Windows enables power management for native PCIe hotplug ports if there is the following ACPI _DSD attached to the root port: Name (_DSD, Package () { ToUUID ("6211e2c0-58a3-4af3-90e1-927a4e0c55a4"), Package () { Package () {"HotPlugSupportInD3", 1} } }) This is also documented in: https://docs.microsoft.com/en-us/windows-hardware/drivers/pci/dsd-for-pcie-root-ports#identifying-pcie-root-ports-supporting-hot-plug-in-d3 Do the same in Linux by introducing new firmware PM callback (->bridge_d3()) and then implement it for ACPI based systems so that the above property is checked. There is one catch, though. The initial pci_dev->bridge_d3 is set before the root port has ACPI companion bound (the device is not added to the PCI bus either) so we need to look up the ACPI companion manually in that case in acpi_pci_bridge_d3(). Signed-off-by: Mika Westerberg <mika.westerberg@linux.intel.com> Signed-off-by: Bjorn Helgaas <bhelgaas@google.com> Reviewed-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
-rw-r--r--drivers/acpi/property.c3
-rw-r--r--drivers/pci/pci-acpi.c41
-rw-r--r--drivers/pci/pci.c9
-rw-r--r--drivers/pci/pci.h3
4 files changed, 56 insertions, 0 deletions
diff --git a/drivers/acpi/property.c b/drivers/acpi/property.c
index 90ba9371bae6..8c7c4583b52d 100644
--- a/drivers/acpi/property.c
+++ b/drivers/acpi/property.c
@@ -28,6 +28,9 @@ static const guid_t prp_guids[] = {
28 /* ACPI _DSD device properties GUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */ 28 /* ACPI _DSD device properties GUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */
29 GUID_INIT(0xdaffd814, 0x6eba, 0x4d8c, 29 GUID_INIT(0xdaffd814, 0x6eba, 0x4d8c,
30 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01), 30 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01),
31 /* Hotplug in D3 GUID: 6211e2c0-58a3-4af3-90e1-927a4e0c55a4 */
32 GUID_INIT(0x6211e2c0, 0x58a3, 0x4af3,
33 0x90, 0xe1, 0x92, 0x7a, 0x4e, 0x0c, 0x55, 0xa4),
31}; 34};
32 35
33static const guid_t ads_guid = 36static const guid_t ads_guid =
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index f8436d1c4d45..c8d0549580f4 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -519,6 +519,46 @@ static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev)
519 return PCI_POWER_ERROR; 519 return PCI_POWER_ERROR;
520} 520}
521 521
522static struct acpi_device *acpi_pci_find_companion(struct device *dev);
523
524static bool acpi_pci_bridge_d3(struct pci_dev *dev)
525{
526 const struct fwnode_handle *fwnode;
527 struct acpi_device *adev;
528 struct pci_dev *root;
529 u8 val;
530
531 if (!dev->is_hotplug_bridge)
532 return false;
533
534 /*
535 * Look for a special _DSD property for the root port and if it
536 * is set we know the hierarchy behind it supports D3 just fine.
537 */
538 root = pci_find_pcie_root_port(dev);
539 if (!root)
540 return false;
541
542 adev = ACPI_COMPANION(&root->dev);
543 if (root == dev) {
544 /*
545 * It is possible that the ACPI companion is not yet bound
546 * for the root port so look it up manually here.
547 */
548 if (!adev && !pci_dev_is_added(root))
549 adev = acpi_pci_find_companion(&root->dev);
550 }
551
552 if (!adev)
553 return false;
554
555 fwnode = acpi_fwnode_handle(adev);
556 if (fwnode_property_read_u8(fwnode, "HotPlugSupportInD3", &val))
557 return false;
558
559 return val == 1;
560}
561
522static bool acpi_pci_power_manageable(struct pci_dev *dev) 562static bool acpi_pci_power_manageable(struct pci_dev *dev)
523{ 563{
524 struct acpi_device *adev = ACPI_COMPANION(&dev->dev); 564 struct acpi_device *adev = ACPI_COMPANION(&dev->dev);
@@ -635,6 +675,7 @@ static bool acpi_pci_need_resume(struct pci_dev *dev)
635} 675}
636 676
637static const struct pci_platform_pm_ops acpi_pci_platform_pm = { 677static const struct pci_platform_pm_ops acpi_pci_platform_pm = {
678 .bridge_d3 = acpi_pci_bridge_d3,
638 .is_manageable = acpi_pci_power_manageable, 679 .is_manageable = acpi_pci_power_manageable,
639 .set_state = acpi_pci_set_power_state, 680 .set_state = acpi_pci_set_power_state,
640 .get_state = acpi_pci_get_power_state, 681 .get_state = acpi_pci_get_power_state,
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c
index 4a1b1f76dc92..e6fcf11f5dcc 100644
--- a/drivers/pci/pci.c
+++ b/drivers/pci/pci.c
@@ -793,6 +793,11 @@ static inline bool platform_pci_need_resume(struct pci_dev *dev)
793 return pci_platform_pm ? pci_platform_pm->need_resume(dev) : false; 793 return pci_platform_pm ? pci_platform_pm->need_resume(dev) : false;
794} 794}
795 795
796static inline bool platform_pci_bridge_d3(struct pci_dev *dev)
797{
798 return pci_platform_pm ? pci_platform_pm->bridge_d3(dev) : false;
799}
800
796/** 801/**
797 * pci_raw_set_power_state - Use PCI PM registers to set the power state of 802 * pci_raw_set_power_state - Use PCI PM registers to set the power state of
798 * given PCI device 803 * given PCI device
@@ -2518,6 +2523,10 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge)
2518 if (bridge->is_thunderbolt) 2523 if (bridge->is_thunderbolt)
2519 return true; 2524 return true;
2520 2525
2526 /* Platform might know better if the bridge supports D3 */
2527 if (platform_pci_bridge_d3(bridge))
2528 return true;
2529
2521 /* 2530 /*
2522 * Hotplug ports handled natively by the OS were not validated 2531 * Hotplug ports handled natively by the OS were not validated
2523 * by vendors for runtime D3 at least until 2018 because there 2532 * by vendors for runtime D3 at least until 2018 because there
diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h
index eb3125decffe..672ba4d1659e 100644
--- a/drivers/pci/pci.h
+++ b/drivers/pci/pci.h
@@ -40,6 +40,8 @@ int pci_bus_error_reset(struct pci_dev *dev);
40/** 40/**
41 * struct pci_platform_pm_ops - Firmware PM callbacks 41 * struct pci_platform_pm_ops - Firmware PM callbacks
42 * 42 *
43 * @bridge_d3: Does the bridge allow entering into D3
44 *
43 * @is_manageable: returns 'true' if given device is power manageable by the 45 * @is_manageable: returns 'true' if given device is power manageable by the
44 * platform firmware 46 * platform firmware
45 * 47 *
@@ -61,6 +63,7 @@ int pci_bus_error_reset(struct pci_dev *dev);
61 * these callbacks are mandatory. 63 * these callbacks are mandatory.
62 */ 64 */
63struct pci_platform_pm_ops { 65struct pci_platform_pm_ops {
66 bool (*bridge_d3)(struct pci_dev *dev);
64 bool (*is_manageable)(struct pci_dev *dev); 67 bool (*is_manageable)(struct pci_dev *dev);
65 int (*set_state)(struct pci_dev *dev, pci_power_t state); 68 int (*set_state)(struct pci_dev *dev, pci_power_t state);
66 pci_power_t (*get_state)(struct pci_dev *dev); 69 pci_power_t (*get_state)(struct pci_dev *dev);