Skip to content

Commit 5775b84

Browse files
rafaeljwl1k
authored andcommitted
PCI: Restore config space on runtime resume despite being unbound
We leave PCI devices not bound to a driver in D0 during runtime suspend. But they may have a parent which is bound and can be transitioned to D3cold at runtime. Once the parent goes to D3cold, the unbound child may go to D3cold as well. When the child goes to D3cold, its internal state, including configuration of BARs, MSI, ASPM, MPS, etc., is lost. One example are recent hybrid graphics laptops which cut power to the discrete GPU when the root port above it goes to ACPI power state D3. Users may provoke this by unbinding the GPU driver and allowing runtime PM on the GPU via sysfs: The PM core will then treat the GPU as "suspended", which in turn allows the root port to runtime suspend, causing the power resources listed in its _PR3 object to be powered off. The GPU's BARs will be uninitialized when a driver later probes it. Another example are hybrid graphics laptops where the GPU itself (rather than the root port) is capable of runtime suspending to D3cold. If the GPU's integrated HDA controller is not bound and the GPU's driver decides to runtime suspend to D3cold, the HDA controller's BARs will be uninitialized when a driver later probes it. Fix by saving and restoring config space over a runtime suspend cycle even if the device is not bound. Acked-by: Bjorn Helgaas <[email protected]> Tested-by: Peter Wu <[email protected]> # Nvidia Optimus Tested-by: Lukas Wunner <[email protected]> # MacBook Pro Signed-off-by: Rafael J. Wysocki <[email protected]> [lukas: add commit message, bikeshed code comments for clarity] Signed-off-by: Lukas Wunner <[email protected]> Link: https://patchwork.freedesktop.org/patch/msgid/92fb6e6ae2730915eb733c08e2f76c6a313e3860.1520068884.git.lukas@wunner.de
1 parent 9c936b1 commit 5775b84

File tree

1 file changed

+11
-6
lines changed

1 file changed

+11
-6
lines changed

drivers/pci/pci-driver.c

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1224,11 +1224,14 @@ static int pci_pm_runtime_suspend(struct device *dev)
12241224
int error;
12251225

12261226
/*
1227-
* If pci_dev->driver is not set (unbound), the device should
1228-
* always remain in D0 regardless of the runtime PM status
1227+
* If pci_dev->driver is not set (unbound), we leave the device in D0,
1228+
* but it may go to D3cold when the bridge above it runtime suspends.
1229+
* Save its config space in case that happens.
12291230
*/
1230-
if (!pci_dev->driver)
1231+
if (!pci_dev->driver) {
1232+
pci_save_state(pci_dev);
12311233
return 0;
1234+
}
12321235

12331236
if (!pm || !pm->runtime_suspend)
12341237
return -ENOSYS;
@@ -1276,16 +1279,18 @@ static int pci_pm_runtime_resume(struct device *dev)
12761279
const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL;
12771280

12781281
/*
1279-
* If pci_dev->driver is not set (unbound), the device should
1280-
* always remain in D0 regardless of the runtime PM status
1282+
* Restoring config space is necessary even if the device is not bound
1283+
* to a driver because although we left it in D0, it may have gone to
1284+
* D3cold when the bridge above it runtime suspended.
12811285
*/
1286+
pci_restore_standard_config(pci_dev);
1287+
12821288
if (!pci_dev->driver)
12831289
return 0;
12841290

12851291
if (!pm || !pm->runtime_resume)
12861292
return -ENOSYS;
12871293

1288-
pci_restore_standard_config(pci_dev);
12891294
pci_fixup_device(pci_fixup_resume_early, pci_dev);
12901295
pci_enable_wake(pci_dev, PCI_D0, false);
12911296
pci_fixup_device(pci_fixup_resume, pci_dev);

0 commit comments

Comments
 (0)