]> Pileus Git - ~andy/linux/commitdiff
PCI/PM: add runtime PM support to PCIe port
authorZheng Yan <zheng.z.yan@intel.com>
Sat, 23 Jun 2012 02:23:49 +0000 (10:23 +0800)
committerBjorn Helgaas <bhelgaas@google.com>
Sat, 23 Jun 2012 16:47:47 +0000 (10:47 -0600)
This patch adds runtime PM support to PCIe port.  This is needed by
PCIe D3cold support, where PCIe device without ACPI node may be
powered on/off by PCIe port.

Because runtime suspend is broken for some chipsets, a black list is
used to disable runtime PM support for these chipsets.

Reviewed-by: Rafael J. Wysocki <rjw@sisk.pl>
Signed-off-by: Zheng Yan <zheng.z.yan@intel.com>
Signed-off-by: Huang Ying <ying.huang@intel.com>
Signed-off-by: Bjorn Helgaas <bhelgaas@google.com>
drivers/pci/pci.c
drivers/pci/pcie/portdrv_pci.c

index 447e83472c01558705d0685f7fd6af17a6f8deef..9eae64b17954b742b3d8291164cbf6af988c2eee 100644 (file)
@@ -1518,6 +1518,16 @@ static void pci_pme_list_scan(struct work_struct *work)
        if (!list_empty(&pci_pme_list)) {
                list_for_each_entry_safe(pme_dev, n, &pci_pme_list, list) {
                        if (pme_dev->dev->pme_poll) {
+                               struct pci_dev *bridge;
+
+                               bridge = pme_dev->dev->bus->self;
+                               /*
+                                * If bridge is in low power state, the
+                                * configuration space of subordinate devices
+                                * may be not accessible
+                                */
+                               if (bridge && bridge->current_state != PCI_D0)
+                                       continue;
                                pci_pme_wakeup(pme_dev->dev, NULL);
                        } else {
                                list_del(&pme_dev->list);
index e0610bda1dea8ace0c03c202ff52e33a278036ff..7c576b9aa01da142993e1c5dcbe0868b95d219dd 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/pm.h>
+#include <linux/pm_runtime.h>
 #include <linux/init.h>
 #include <linux/pcieport_if.h>
 #include <linux/aer.h>
@@ -99,6 +100,15 @@ static int pcie_port_resume_noirq(struct device *dev)
        return 0;
 }
 
+#ifdef CONFIG_PM_RUNTIME
+static int pcie_port_runtime_pm(struct device *dev)
+{
+       return 0;
+}
+#else
+#define pcie_port_runtime_pm   NULL
+#endif
+
 static const struct dev_pm_ops pcie_portdrv_pm_ops = {
        .suspend        = pcie_port_device_suspend,
        .resume         = pcie_port_device_resume,
@@ -107,6 +117,8 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = {
        .poweroff       = pcie_port_device_suspend,
        .restore        = pcie_port_device_resume,
        .resume_noirq   = pcie_port_resume_noirq,
+       .runtime_suspend = pcie_port_runtime_pm,
+       .runtime_resume = pcie_port_runtime_pm,
 };
 
 #define PCIE_PORTDRV_PM_OPS    (&pcie_portdrv_pm_ops)
@@ -116,6 +128,14 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = {
 #define PCIE_PORTDRV_PM_OPS    NULL
 #endif /* !PM */
 
+/*
+ * PCIe port runtime suspend is broken for some chipsets, so use a
+ * black list to disable runtime PM for these chipsets.
+ */
+static const struct pci_device_id port_runtime_pm_black_list[] = {
+       { /* end: all zeroes */ }
+};
+
 /*
  * pcie_portdrv_probe - Probe PCI-Express port devices
  * @dev: PCI-Express port device being probed
@@ -144,12 +164,16 @@ static int __devinit pcie_portdrv_probe(struct pci_dev *dev,
                return status;
 
        pci_save_state(dev);
+       if (!pci_match_id(port_runtime_pm_black_list, dev))
+               pm_runtime_put_noidle(&dev->dev);
 
        return 0;
 }
 
 static void pcie_portdrv_remove(struct pci_dev *dev)
 {
+       if (!pci_match_id(port_runtime_pm_black_list, dev))
+               pm_runtime_get_noresume(&dev->dev);
        pcie_port_device_remove(dev);
        pci_disable_device(dev);
 }