diff options
author | Bjorn Helgaas <bhelgaas@google.com> | 2012-12-05 00:13:03 +0100 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2012-12-05 00:13:03 +0100 |
commit | edb1daab8e91338b7e2a6c41faec695891ccda35 (patch) | |
tree | 2f5ce32e53fee349e7f0ffb384a5d952a0a94f2f /drivers/pci | |
parent | Merge branch 'pci/misc' into next (diff) | |
parent | PCI/PM: Keep runtime PM enabled for unbound PCI devices (diff) | |
download | linux-edb1daab8e91338b7e2a6c41faec695891ccda35.tar.xz linux-edb1daab8e91338b7e2a6c41faec695891ccda35.zip |
Merge branch 'pci/huang-d3cold-fixes' into next
* pci/huang-d3cold-fixes:
PCI/PM: Keep runtime PM enabled for unbound PCI devices
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/pci-driver.c | 67 | ||||
-rw-r--r-- | drivers/pci/pci.c | 2 |
2 files changed, 42 insertions, 27 deletions
diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 24aa44c6ed02..c3088e54fc43 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -256,31 +256,26 @@ struct drv_dev_and_id { static long local_pci_probe(void *_ddi) { struct drv_dev_and_id *ddi = _ddi; - struct device *dev = &ddi->dev->dev; - struct device *parent = dev->parent; + struct pci_dev *pci_dev = ddi->dev; + struct pci_driver *pci_drv = ddi->drv; + struct device *dev = &pci_dev->dev; int rc; - /* The parent bridge must be in active state when probing */ - if (parent) - pm_runtime_get_sync(parent); - /* Unbound PCI devices are always set to disabled and suspended. - * During probe, the device is set to enabled and active and the - * usage count is incremented. If the driver supports runtime PM, - * it should call pm_runtime_put_noidle() in its probe routine and - * pm_runtime_get_noresume() in its remove routine. + /* + * Unbound PCI devices are always put in D0, regardless of + * runtime PM status. During probe, the device is set to + * active and the usage count is incremented. If the driver + * supports runtime PM, it should call pm_runtime_put_noidle() + * in its probe routine and pm_runtime_get_noresume() in its + * remove routine. */ - pm_runtime_get_noresume(dev); - pm_runtime_set_active(dev); - pm_runtime_enable(dev); - - rc = ddi->drv->probe(ddi->dev, ddi->id); + pm_runtime_get_sync(dev); + pci_dev->driver = pci_drv; + rc = pci_drv->probe(pci_dev, ddi->id); if (rc) { - pm_runtime_disable(dev); - pm_runtime_set_suspended(dev); - pm_runtime_put_noidle(dev); + pci_dev->driver = NULL; + pm_runtime_put_sync(dev); } - if (parent) - pm_runtime_put(parent); return rc; } @@ -330,10 +325,8 @@ __pci_device_probe(struct pci_driver *drv, struct pci_dev *pci_dev) id = pci_match_device(drv, pci_dev); if (id) error = pci_call_probe(drv, pci_dev, id); - if (error >= 0) { - pci_dev->driver = drv; + if (error >= 0) error = 0; - } } return error; } @@ -369,9 +362,7 @@ static int pci_device_remove(struct device * dev) } /* Undo the runtime PM settings in local_pci_probe() */ - pm_runtime_disable(dev); - pm_runtime_set_suspended(dev); - pm_runtime_put_noidle(dev); + pm_runtime_put_sync(dev); /* * If the device is still on, set the power state as "unknown", @@ -994,6 +985,13 @@ static int pci_pm_runtime_suspend(struct device *dev) pci_power_t prev = pci_dev->current_state; int error; + /* + * If pci_dev->driver is not set (unbound), the device should + * always remain in D0 regardless of the runtime PM status + */ + if (!pci_dev->driver) + return 0; + if (!pm || !pm->runtime_suspend) return -ENOSYS; @@ -1029,6 +1027,13 @@ static int pci_pm_runtime_resume(struct device *dev) struct pci_dev *pci_dev = to_pci_dev(dev); const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + /* + * If pci_dev->driver is not set (unbound), the device should + * always remain in D0 regardless of the runtime PM status + */ + if (!pci_dev->driver) + return 0; + if (!pm || !pm->runtime_resume) return -ENOSYS; @@ -1046,8 +1051,16 @@ static int pci_pm_runtime_resume(struct device *dev) static int pci_pm_runtime_idle(struct device *dev) { + struct pci_dev *pci_dev = to_pci_dev(dev); const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + /* + * If pci_dev->driver is not set (unbound), the device should + * always remain in D0 regardless of the runtime PM status + */ + if (!pci_dev->driver) + goto out; + if (!pm) return -ENOSYS; @@ -1057,8 +1070,8 @@ static int pci_pm_runtime_idle(struct device *dev) return ret; } +out: pm_runtime_suspend(dev); - return 0; } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index adffc6f621e5..5b862b1c93c6 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1910,6 +1910,8 @@ void pci_pm_init(struct pci_dev *dev) u16 pmc; pm_runtime_forbid(&dev->dev); + pm_runtime_set_active(&dev->dev); + pm_runtime_enable(&dev->dev); device_enable_async_suspend(&dev->dev); dev->wakeup_prepared = false; |