|
@@ -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,
|