aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/pci/pcie/portdrv_pci.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/pcie/portdrv_pci.c')
-rw-r--r--drivers/pci/pcie/portdrv_pci.c60
1 files changed, 60 insertions, 0 deletions
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c
index e0610bda1dea..3a7eefcb270a 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,51 @@ static int pcie_port_resume_noirq(struct device *dev)
99 return 0; 100 return 0;
100} 101}
101 102
103#ifdef CONFIG_PM_RUNTIME
104struct d3cold_info {
105 bool no_d3cold;
106 unsigned int d3cold_delay;
107};
108
109static int pci_dev_d3cold_info(struct pci_dev *pdev, void *data)
110{
111 struct d3cold_info *info = data;
112
113 info->d3cold_delay = max_t(unsigned int, pdev->d3cold_delay,
114 info->d3cold_delay);
115 if (pdev->no_d3cold)
116 info->no_d3cold = true;
117 return 0;
118}
119
120static int pcie_port_runtime_suspend(struct device *dev)
121{
122 struct pci_dev *pdev = to_pci_dev(dev);
123 struct d3cold_info d3cold_info = {
124 .no_d3cold = false,
125 .d3cold_delay = PCI_PM_D3_WAIT,
126 };
127
128 /*
129 * If any subordinate device disable D3cold, we should not put
130 * the port into D3cold. The D3cold delay of port should be
131 * the max of that of all subordinate devices.
132 */
133 pci_walk_bus(pdev->subordinate, pci_dev_d3cold_info, &d3cold_info);
134 pdev->no_d3cold = d3cold_info.no_d3cold;
135 pdev->d3cold_delay = d3cold_info.d3cold_delay;
136 return 0;
137}
138
139static int pcie_port_runtime_resume(struct device *dev)
140{
141 return 0;
142}
143#else
144#define pcie_port_runtime_suspend NULL
145#define pcie_port_runtime_resume NULL
146#endif
147
102static const struct dev_pm_ops pcie_portdrv_pm_ops = { 148static const struct dev_pm_ops pcie_portdrv_pm_ops = {
103 .suspend = pcie_port_device_suspend, 149 .suspend = pcie_port_device_suspend,
104 .resume = pcie_port_device_resume, 150 .resume = pcie_port_device_resume,
@@ -107,6 +153,8 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = {
107 .poweroff = pcie_port_device_suspend, 153 .poweroff = pcie_port_device_suspend,
108 .restore = pcie_port_device_resume, 154 .restore = pcie_port_device_resume,
109 .resume_noirq = pcie_port_resume_noirq, 155 .resume_noirq = pcie_port_resume_noirq,
156 .runtime_suspend = pcie_port_runtime_suspend,
157 .runtime_resume = pcie_port_runtime_resume,
110}; 158};
111 159
112#define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops) 160#define PCIE_PORTDRV_PM_OPS (&pcie_portdrv_pm_ops)
@@ -117,6 +165,14 @@ static const struct dev_pm_ops pcie_portdrv_pm_ops = {
117#endif /* !PM */ 165#endif /* !PM */
118 166
119/* 167/*
168 * PCIe port runtime suspend is broken for some chipsets, so use a
169 * black list to disable runtime PM for these chipsets.
170 */
171static const struct pci_device_id port_runtime_pm_black_list[] = {
172 { /* end: all zeroes */ }
173};
174
175/*
120 * pcie_portdrv_probe - Probe PCI-Express port devices 176 * pcie_portdrv_probe - Probe PCI-Express port devices
121 * @dev: PCI-Express port device being probed 177 * @dev: PCI-Express port device being probed
122 * 178 *
@@ -144,12 +200,16 @@ static int __devinit pcie_portdrv_probe(struct pci_dev *dev,
144 return status; 200 return status;
145 201
146 pci_save_state(dev); 202 pci_save_state(dev);
203 if (!pci_match_id(port_runtime_pm_black_list, dev))
204 pm_runtime_put_noidle(&dev->dev);
147 205
148 return 0; 206 return 0;
149} 207}
150 208
151static void pcie_portdrv_remove(struct pci_dev *dev) 209static void pcie_portdrv_remove(struct pci_dev *dev)
152{ 210{
211 if (!pci_match_id(port_runtime_pm_black_list, dev))
212 pm_runtime_get_noresume(&dev->dev);
153 pcie_port_device_remove(dev); 213 pcie_port_device_remove(dev);
154 pci_disable_device(dev); 214 pci_disable_device(dev);
155} 215}