Skip to content

Commit 26ad34d

Browse files
westeribjorn-helgaas
authored andcommitted
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>
1 parent 5f5e489 commit 26ad34d

File tree

4 files changed

+56
-0
lines changed

4 files changed

+56
-0
lines changed

drivers/acpi/property.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ static const guid_t prp_guids[] = {
2828
/* ACPI _DSD device properties GUID: daffd814-6eba-4d8c-8a91-bc9bbf4aa301 */
2929
GUID_INIT(0xdaffd814, 0x6eba, 0x4d8c,
3030
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),
3134
};
3235

3336
static const guid_t ads_guid =

drivers/pci/pci-acpi.c

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -519,6 +519,46 @@ static pci_power_t acpi_pci_choose_state(struct pci_dev *pdev)
519519
return PCI_POWER_ERROR;
520520
}
521521

522+
static struct acpi_device *acpi_pci_find_companion(struct device *dev);
523+
524+
static 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+
522562
static bool acpi_pci_power_manageable(struct pci_dev *dev)
523563
{
524564
struct acpi_device *adev = ACPI_COMPANION(&dev->dev);
@@ -635,6 +675,7 @@ static bool acpi_pci_need_resume(struct pci_dev *dev)
635675
}
636676

637677
static const struct pci_platform_pm_ops acpi_pci_platform_pm = {
678+
.bridge_d3 = acpi_pci_bridge_d3,
638679
.is_manageable = acpi_pci_power_manageable,
639680
.set_state = acpi_pci_set_power_state,
640681
.get_state = acpi_pci_get_power_state,

drivers/pci/pci.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -793,6 +793,11 @@ static inline bool platform_pci_need_resume(struct pci_dev *dev)
793793
return pci_platform_pm ? pci_platform_pm->need_resume(dev) : false;
794794
}
795795

796+
static 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+
796801
/**
797802
* pci_raw_set_power_state - Use PCI PM registers to set the power state of
798803
* given PCI device
@@ -2518,6 +2523,10 @@ bool pci_bridge_d3_possible(struct pci_dev *bridge)
25182523
if (bridge->is_thunderbolt)
25192524
return true;
25202525

2526+
/* Platform might know better if the bridge supports D3 */
2527+
if (platform_pci_bridge_d3(bridge))
2528+
return true;
2529+
25212530
/*
25222531
* Hotplug ports handled natively by the OS were not validated
25232532
* by vendors for runtime D3 at least until 2018 because there

drivers/pci/pci.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ int pci_bus_error_reset(struct pci_dev *dev);
4040
/**
4141
* struct pci_platform_pm_ops - Firmware PM callbacks
4242
*
43+
* @bridge_d3: Does the bridge allow entering into D3
44+
*
4345
* @is_manageable: returns 'true' if given device is power manageable by the
4446
* platform firmware
4547
*
@@ -61,6 +63,7 @@ int pci_bus_error_reset(struct pci_dev *dev);
6163
* these callbacks are mandatory.
6264
*/
6365
struct pci_platform_pm_ops {
66+
bool (*bridge_d3)(struct pci_dev *dev);
6467
bool (*is_manageable)(struct pci_dev *dev);
6568
int (*set_state)(struct pci_dev *dev, pci_power_t state);
6669
pci_power_t (*get_state)(struct pci_dev *dev);

0 commit comments

Comments
 (0)