diff options
author | Zheng Yan <zheng.z.yan@intel.com> | 2012-06-22 22:23:49 -0400 |
---|---|---|
committer | Bjorn Helgaas <bhelgaas@google.com> | 2012-06-23 12:47:47 -0400 |
commit | 71a83bd727cc31c5fe960c3758cb396267ff710e (patch) | |
tree | c4655a683b386c98d7c85bddd019e67711310c71 | |
parent | ee85f543710dd56ce526cb44e39191f32972e5ad (diff) |
PCI/PM: add runtime PM support to PCIe port
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>
-rw-r--r-- | drivers/pci/pci.c | 10 | ||||
-rw-r--r-- | drivers/pci/pcie/portdrv_pci.c | 24 |
2 files changed, 34 insertions, 0 deletions
diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 447e83472c01..9eae64b17954 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c | |||
@@ -1518,6 +1518,16 @@ static void pci_pme_list_scan(struct work_struct *work) | |||
1518 | if (!list_empty(&pci_pme_list)) { | 1518 | if (!list_empty(&pci_pme_list)) { |
1519 | list_for_each_entry_safe(pme_dev, n, &pci_pme_list, list) { | 1519 | list_for_each_entry_safe(pme_dev, n, &pci_pme_list, list) { |
1520 | if (pme_dev->dev->pme_poll) { | 1520 | if (pme_dev->dev->pme_poll) { |
1521 | struct pci_dev *bridge; | ||
1522 | |||
1523 | bridge = pme_dev->dev->bus->self; | ||
1524 | /* | ||
1525 | * If bridge is in low power state, the | ||
1526 | * configuration space of subordinate devices | ||
1527 | * may be not accessible | ||
1528 | */ | ||
1529 | if (bridge && bridge->current_state != PCI_D0) | ||
1530 | continue; | ||
1521 | pci_pme_wakeup(pme_dev->dev, NULL); | 1531 | pci_pme_wakeup(pme_dev->dev, NULL); |
1522 | } else { | 1532 | } else { |
1523 | list_del(&pme_dev->list); | 1533 | list_del(&pme_dev->list); |
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index e0610bda1dea..7c576b9aa01d 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c | |||
@@ -11,6 +11,7 @@ | |||
11 | #include <linux/kernel.h> | 11 | #include <linux/kernel.h> |
12 | #include <linux/errno.h> | 12 | #include <linux/errno.h> |
13 | #include <linux/pm.h> | 13 | #include <linux/pm.h> |
14 | #include <linux/pm_runtime.h> | ||
14 | #include <linux/init.h> | 15 | #include <linux/init.h> |
15 | #include <linux/pcieport_if.h> | 16 | #include <linux/pcieport_if.h> |
16 | #include <linux/aer.h> | 17 | #include <linux/aer.h> |
@@ -99,6 +100,15 @@ static int pcie_port_resume_noirq(struct device *dev) | |||
99 | return 0; | 100 | return 0; |
100 | } | 101 | } |
101 | 102 | ||
103 | #ifdef CONFIG_PM_RUNTIME | ||
104 | static int pcie_port_runtime_pm(struct device *dev) | ||
105 | { | ||
106 | return 0; | ||
107 | } | ||
108 | #else | ||
109 | #define pcie_port_runtime_pm NULL | ||
110 | #endif | ||
111 | |||
102 | static const struct dev_pm_ops pcie_portdrv_pm_ops = { | 112 | static const struct dev_pm_ops pcie_portdrv_pm_ops = { |
103 | .suspend = pcie_port_device_suspend, | 113 | .suspend = pcie_port_device_suspend, |
104 | .resume = pcie_port_device_resume, | 114 | .resume = pcie_port_device_resume, |
@@ -107,6 +117,8 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = { | |||
107 | .poweroff = pcie_port_device_suspend, | 117 | .poweroff = pcie_port_device_suspend, |
108 | .restore = pcie_port_device_resume, | 118 | .restore = pcie_port_device_resume, |
109 | .resume_noirq = pcie_port_resume_noirq, | 119 | .resume_noirq = pcie_port_resume_noirq, |
120 | .runtime_suspend = pcie_port_runtime_pm, | ||
121 | .runtime_resume = pcie_port_runtime_pm, | ||
110 | }; | 122 | }; |
111 | 123 | ||
112 | #define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops) | 124 | #define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops) |
@@ -117,6 +129,14 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = { | |||
117 | #endif /* !PM */ | 129 | #endif /* !PM */ |
118 | 130 | ||
119 | /* | 131 | /* |
132 | * PCIe port runtime suspend is broken for some chipsets, so use a | ||
133 | * black list to disable runtime PM for these chipsets. | ||
134 | */ | ||
135 | static const struct pci_device_id port_runtime_pm_black_list[] = { | ||
136 | { /* end: all zeroes */ } | ||
137 | }; | ||
138 | |||
139 | /* | ||
120 | * pcie_portdrv_probe - Probe PCI-Express port devices | 140 | * pcie_portdrv_probe - Probe PCI-Express port devices |
121 | * @dev: PCI-Express port device being probed | 141 | * @dev: PCI-Express port device being probed |
122 | * | 142 | * |
@@ -144,12 +164,16 @@ static int __devinit pcie_portdrv_probe(struct pci_dev *dev, | |||
144 | return status; | 164 | return status; |
145 | 165 | ||
146 | pci_save_state(dev); | 166 | pci_save_state(dev); |
167 | if (!pci_match_id(port_runtime_pm_black_list, dev)) | ||
168 | pm_runtime_put_noidle(&dev->dev); | ||
147 | 169 | ||
148 | return 0; | 170 | return 0; |
149 | } | 171 | } |
150 | 172 | ||
151 | static void pcie_portdrv_remove(struct pci_dev *dev) | 173 | static void pcie_portdrv_remove(struct pci_dev *dev) |
152 | { | 174 | { |
175 | if (!pci_match_id(port_runtime_pm_black_list, dev)) | ||
176 | pm_runtime_get_noresume(&dev->dev); | ||
153 | pcie_port_device_remove(dev); | 177 | pcie_port_device_remove(dev); |
154 | pci_disable_device(dev); | 178 | pci_disable_device(dev); |
155 | } | 179 | } |