diff options
Diffstat (limited to 'drivers/pci/pcie/portdrv_pci.c')
-rw-r--r-- | drivers/pci/pcie/portdrv_pci.c | 60 |
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 | ||
104 | struct d3cold_info { | ||
105 | bool no_d3cold; | ||
106 | unsigned int d3cold_delay; | ||
107 | }; | ||
108 | |||
109 | static 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 | |||
120 | static 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 | |||
139 | static 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 | |||
102 | static const struct dev_pm_ops pcie_portdrv_pm_ops = { | 148 | static 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 | */ | ||
171 | static 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 | ||
151 | static void pcie_portdrv_remove(struct pci_dev *dev) | 209 | static 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 | } |