From 6299cf9ec3985cac70bede8a855b5087b81a6640 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 27 Sep 2018 16:54:13 -0500 Subject: PCI / ACPI: Enable wake automatically for power managed bridges We enable power management automatically for bridges where pci_bridge_d3_possible() returns true. However, these bridges may have ACPI methods such as _DSW that need to be called before D3 entry. For example in Lenovo Thinkpad X1 Carbon 6th _DSW method is used to prepare D3cold for the PCIe root port hosting Thunderbolt chain. Because wake is not enabled _DSW method is never called and the port does not enter D3cold properly consuming more power than necessary. Users can work this around by writing "enabled" to "wakeup" sysfs file under the device in question but that is not something an ordinary user is expected to do. Since we already automatically enable power management for PCIe ports with ->bridge_d3 set extend that to enable wake for them as well, assuming the port has any ACPI wakeup related objects implemented in the namespace (adev->wakeup.flags.valid is true). This ensures the necessary ACPI methods get called at appropriate times and allows the root port in Thinkpad X1 Carbon 6th to go into D3cold. Signed-off-by: Mika Westerberg Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/pci/pci-acpi.c | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers/pci/pci-acpi.c') diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index c2ab57705043..f8436d1c4d45 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -762,19 +762,33 @@ static void pci_acpi_setup(struct device *dev) return; device_set_wakeup_capable(dev, true); + /* + * For bridges that can do D3 we enable wake automatically (as + * we do for the power management itself in that case). The + * reason is that the bridge may have additional methods such as + * _DSW that need to be called. + */ + if (pci_dev->bridge_d3) + device_wakeup_enable(dev); + acpi_pci_wakeup(pci_dev, false); } static void pci_acpi_cleanup(struct device *dev) { struct acpi_device *adev = ACPI_COMPANION(dev); + struct pci_dev *pci_dev = to_pci_dev(dev); if (!adev) return; pci_acpi_remove_pm_notifier(adev); - if (adev->wakeup.flags.valid) + if (adev->wakeup.flags.valid) { + if (pci_dev->bridge_d3) + device_wakeup_disable(dev); + device_set_wakeup_capable(dev, false); + } } static bool pci_acpi_bus_match(struct device *dev) -- cgit 1.4.1 From 26ad34d510a87fc65caeb48fa85cce58d2477a88 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Thu, 27 Sep 2018 16:57:14 -0500 Subject: 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 Signed-off-by: Bjorn Helgaas Reviewed-by: Rafael J. Wysocki --- drivers/acpi/property.c | 3 +++ drivers/pci/pci-acpi.c | 41 +++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.c | 9 +++++++++ drivers/pci/pci.h | 3 +++ 4 files changed, 56 insertions(+) (limited to 'drivers/pci/pci-acpi.c') 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[] = { /* ACPI _DSD device properties GUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */ GUID_INIT(0xdaffd814, 0x6eba, 0x4d8c, 0x8a, 0x91, 0xbc, 0x9b, 0xbf, 0x4a, 0xa3, 0x01), + /* Hotplug in D3 GUID: 6211e2c0-58a3-4af3-90e1-927a4e0c55a4 */ + GUID_INIT(0x6211e2c0, 0x58a3, 0x4af3, + 0x90, 0xe1, 0x92, 0x7a, 0x4e, 0x0c, 0x55, 0xa4), }; static 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) return PCI_POWER_ERROR; } +static struct acpi_device *acpi_pci_find_companion(struct device *dev); + +static bool acpi_pci_bridge_d3(struct pci_dev *dev) +{ + const struct fwnode_handle *fwnode; + struct acpi_device *adev; + struct pci_dev *root; + u8 val; + + if (!dev->is_hotplug_bridge) + return false; + + /* + * Look for a special _DSD property for the root port and if it + * is set we know the hierarchy behind it supports D3 just fine. + */ + root = pci_find_pcie_root_port(dev); + if (!root) + return false; + + adev = ACPI_COMPANION(&root->dev); + if (root == dev) { + /* + * It is possible that the ACPI companion is not yet bound + * for the root port so look it up manually here. + */ + if (!adev && !pci_dev_is_added(root)) + adev = acpi_pci_find_companion(&root->dev); + } + + if (!adev) + return false; + + fwnode = acpi_fwnode_handle(adev); + if (fwnode_property_read_u8(fwnode, "HotPlugSupportInD3", &val)) + return false; + + return val == 1; +} + static bool acpi_pci_power_manageable(struct pci_dev *dev) { struct acpi_device *adev = ACPI_COMPANION(&dev->dev); @@ -635,6 +675,7 @@ static bool acpi_pci_need_resume(struct pci_dev *dev) } static const struct pci_platform_pm_ops acpi_pci_platform_pm = { + .bridge_d3 = acpi_pci_bridge_d3, .is_manageable = acpi_pci_power_manageable, .set_state = acpi_pci_set_power_state, .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) return pci_platform_pm ? pci_platform_pm->need_resume(dev) : false; } +static inline bool platform_pci_bridge_d3(struct pci_dev *dev) +{ + return pci_platform_pm ? pci_platform_pm->bridge_d3(dev) : false; +} + /** * pci_raw_set_power_state - Use PCI PM registers to set the power state of * given PCI device @@ -2518,6 +2523,10 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge) if (bridge->is_thunderbolt) return true; + /* Platform might know better if the bridge supports D3 */ + if (platform_pci_bridge_d3(bridge)) + return true; + /* * Hotplug ports handled natively by the OS were not validated * 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); /** * struct pci_platform_pm_ops - Firmware PM callbacks * + * @bridge_d3: Does the bridge allow entering into D3 + * * @is_manageable: returns 'true' if given device is power manageable by the * platform firmware * @@ -61,6 +63,7 @@ int pci_bus_error_reset(struct pci_dev *dev); * these callbacks are mandatory. */ struct pci_platform_pm_ops { + bool (*bridge_d3)(struct pci_dev *dev); bool (*is_manageable)(struct pci_dev *dev); int (*set_state)(struct pci_dev *dev, pci_power_t state); pci_power_t (*get_state)(struct pci_dev *dev); -- cgit 1.4.1