diff options
Diffstat (limited to 'drivers/pci/pcie/portdrv_pci.c')
-rw-r--r-- | drivers/pci/pcie/portdrv_pci.c | 211 |
1 files changed, 189 insertions, 22 deletions
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 478d0d28f7ad..037690e08f5f 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c | |||
@@ -14,8 +14,10 @@ | |||
14 | #include <linux/init.h> | 14 | #include <linux/init.h> |
15 | #include <linux/slab.h> | 15 | #include <linux/slab.h> |
16 | #include <linux/pcieport_if.h> | 16 | #include <linux/pcieport_if.h> |
17 | #include <linux/aer.h> | ||
17 | 18 | ||
18 | #include "portdrv.h" | 19 | #include "portdrv.h" |
20 | #include "aer/aerdrv.h" | ||
19 | 21 | ||
20 | /* | 22 | /* |
21 | * Version Information | 23 | * Version Information |
@@ -30,6 +32,43 @@ MODULE_LICENSE("GPL"); | |||
30 | /* global data */ | 32 | /* global data */ |
31 | static const char device_name[] = "pcieport-driver"; | 33 | static const char device_name[] = "pcieport-driver"; |
32 | 34 | ||
35 | static int pcie_portdrv_save_config(struct pci_dev *dev) | ||
36 | { | ||
37 | return pci_save_state(dev); | ||
38 | } | ||
39 | |||
40 | #ifdef CONFIG_PM | ||
41 | static int pcie_portdrv_restore_config(struct pci_dev *dev) | ||
42 | { | ||
43 | int retval; | ||
44 | |||
45 | pci_restore_state(dev); | ||
46 | retval = pci_enable_device(dev); | ||
47 | if (retval) | ||
48 | return retval; | ||
49 | pci_set_master(dev); | ||
50 | return 0; | ||
51 | } | ||
52 | |||
53 | static int pcie_portdrv_suspend(struct pci_dev *dev, pm_message_t state) | ||
54 | { | ||
55 | int ret = pcie_port_device_suspend(dev, state); | ||
56 | |||
57 | if (!ret) | ||
58 | ret = pcie_portdrv_save_config(dev); | ||
59 | return ret; | ||
60 | } | ||
61 | |||
62 | static int pcie_portdrv_resume(struct pci_dev *dev) | ||
63 | { | ||
64 | pcie_portdrv_restore_config(dev); | ||
65 | return pcie_port_device_resume(dev); | ||
66 | } | ||
67 | #else | ||
68 | #define pcie_portdrv_suspend NULL | ||
69 | #define pcie_portdrv_resume NULL | ||
70 | #endif | ||
71 | |||
33 | /* | 72 | /* |
34 | * pcie_portdrv_probe - Probe PCI-Express port devices | 73 | * pcie_portdrv_probe - Probe PCI-Express port devices |
35 | * @dev: PCI-Express port device being probed | 74 | * @dev: PCI-Express port device being probed |
@@ -61,6 +100,10 @@ static int __devinit pcie_portdrv_probe (struct pci_dev *dev, | |||
61 | return -ENOMEM; | 100 | return -ENOMEM; |
62 | } | 101 | } |
63 | 102 | ||
103 | pcie_portdrv_save_config(dev); | ||
104 | |||
105 | pci_enable_pcie_error_reporting(dev); | ||
106 | |||
64 | return 0; | 107 | return 0; |
65 | } | 108 | } |
66 | 109 | ||
@@ -70,39 +113,151 @@ static void pcie_portdrv_remove (struct pci_dev *dev) | |||
70 | kfree(pci_get_drvdata(dev)); | 113 | kfree(pci_get_drvdata(dev)); |
71 | } | 114 | } |
72 | 115 | ||
73 | #ifdef CONFIG_PM | 116 | static int error_detected_iter(struct device *device, void *data) |
74 | static int pcie_portdrv_save_config(struct pci_dev *dev) | ||
75 | { | 117 | { |
76 | return pci_save_state(dev); | 118 | struct pcie_device *pcie_device; |
119 | struct pcie_port_service_driver *driver; | ||
120 | struct aer_broadcast_data *result_data; | ||
121 | pci_ers_result_t status; | ||
122 | |||
123 | result_data = (struct aer_broadcast_data *) data; | ||
124 | |||
125 | if (device->bus == &pcie_port_bus_type && device->driver) { | ||
126 | driver = to_service_driver(device->driver); | ||
127 | if (!driver || | ||
128 | !driver->err_handler || | ||
129 | !driver->err_handler->error_detected) | ||
130 | return 0; | ||
131 | |||
132 | pcie_device = to_pcie_device(device); | ||
133 | |||
134 | /* Forward error detected message to service drivers */ | ||
135 | status = driver->err_handler->error_detected( | ||
136 | pcie_device->port, | ||
137 | result_data->state); | ||
138 | result_data->result = | ||
139 | merge_result(result_data->result, status); | ||
140 | } | ||
141 | |||
142 | return 0; | ||
77 | } | 143 | } |
78 | 144 | ||
79 | static int pcie_portdrv_restore_config(struct pci_dev *dev) | 145 | static pci_ers_result_t pcie_portdrv_error_detected(struct pci_dev *dev, |
146 | enum pci_channel_state error) | ||
80 | { | 147 | { |
148 | struct aer_broadcast_data result_data = | ||
149 | {error, PCI_ERS_RESULT_CAN_RECOVER}; | ||
81 | int retval; | 150 | int retval; |
82 | 151 | ||
83 | pci_restore_state(dev); | 152 | /* can not fail */ |
84 | retval = pci_enable_device(dev); | 153 | retval = device_for_each_child(&dev->dev, &result_data, error_detected_iter); |
85 | if (retval) | 154 | |
86 | return retval; | 155 | return result_data.result; |
87 | pci_set_master(dev); | 156 | } |
157 | |||
158 | static int mmio_enabled_iter(struct device *device, void *data) | ||
159 | { | ||
160 | struct pcie_device *pcie_device; | ||
161 | struct pcie_port_service_driver *driver; | ||
162 | pci_ers_result_t status, *result; | ||
163 | |||
164 | result = (pci_ers_result_t *) data; | ||
165 | |||
166 | if (device->bus == &pcie_port_bus_type && device->driver) { | ||
167 | driver = to_service_driver(device->driver); | ||
168 | if (driver && | ||
169 | driver->err_handler && | ||
170 | driver->err_handler->mmio_enabled) { | ||
171 | pcie_device = to_pcie_device(device); | ||
172 | |||
173 | /* Forward error message to service drivers */ | ||
174 | status = driver->err_handler->mmio_enabled( | ||
175 | pcie_device->port); | ||
176 | *result = merge_result(*result, status); | ||
177 | } | ||
178 | } | ||
179 | |||
88 | return 0; | 180 | return 0; |
89 | } | 181 | } |
90 | 182 | ||
91 | static int pcie_portdrv_suspend (struct pci_dev *dev, pm_message_t state) | 183 | static pci_ers_result_t pcie_portdrv_mmio_enabled(struct pci_dev *dev) |
92 | { | 184 | { |
93 | int ret = pcie_port_device_suspend(dev, state); | 185 | pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED; |
186 | int retval; | ||
94 | 187 | ||
95 | if (!ret) | 188 | /* get true return value from &status */ |
96 | ret = pcie_portdrv_save_config(dev); | 189 | retval = device_for_each_child(&dev->dev, &status, mmio_enabled_iter); |
97 | return ret; | 190 | return status; |
98 | } | 191 | } |
99 | 192 | ||
100 | static int pcie_portdrv_resume (struct pci_dev *dev) | 193 | static int slot_reset_iter(struct device *device, void *data) |
101 | { | 194 | { |
102 | pcie_portdrv_restore_config(dev); | 195 | struct pcie_device *pcie_device; |
103 | return pcie_port_device_resume(dev); | 196 | struct pcie_port_service_driver *driver; |
197 | pci_ers_result_t status, *result; | ||
198 | |||
199 | result = (pci_ers_result_t *) data; | ||
200 | |||
201 | if (device->bus == &pcie_port_bus_type && device->driver) { | ||
202 | driver = to_service_driver(device->driver); | ||
203 | if (driver && | ||
204 | driver->err_handler && | ||
205 | driver->err_handler->slot_reset) { | ||
206 | pcie_device = to_pcie_device(device); | ||
207 | |||
208 | /* Forward error message to service drivers */ | ||
209 | status = driver->err_handler->slot_reset( | ||
210 | pcie_device->port); | ||
211 | *result = merge_result(*result, status); | ||
212 | } | ||
213 | } | ||
214 | |||
215 | return 0; | ||
216 | } | ||
217 | |||
218 | static pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev) | ||
219 | { | ||
220 | pci_ers_result_t status; | ||
221 | int retval; | ||
222 | |||
223 | /* If fatal, restore cfg space for possible link reset at upstream */ | ||
224 | if (dev->error_state == pci_channel_io_frozen) { | ||
225 | pcie_portdrv_restore_config(dev); | ||
226 | pci_enable_pcie_error_reporting(dev); | ||
227 | } | ||
228 | |||
229 | /* get true return value from &status */ | ||
230 | retval = device_for_each_child(&dev->dev, &status, slot_reset_iter); | ||
231 | |||
232 | return status; | ||
233 | } | ||
234 | |||
235 | static int resume_iter(struct device *device, void *data) | ||
236 | { | ||
237 | struct pcie_device *pcie_device; | ||
238 | struct pcie_port_service_driver *driver; | ||
239 | |||
240 | if (device->bus == &pcie_port_bus_type && device->driver) { | ||
241 | driver = to_service_driver(device->driver); | ||
242 | if (driver && | ||
243 | driver->err_handler && | ||
244 | driver->err_handler->resume) { | ||
245 | pcie_device = to_pcie_device(device); | ||
246 | |||
247 | /* Forward error message to service drivers */ | ||
248 | driver->err_handler->resume(pcie_device->port); | ||
249 | } | ||
250 | } | ||
251 | |||
252 | return 0; | ||
253 | } | ||
254 | |||
255 | static void pcie_portdrv_err_resume(struct pci_dev *dev) | ||
256 | { | ||
257 | int retval; | ||
258 | /* nothing to do with error value, if it ever happens */ | ||
259 | retval = device_for_each_child(&dev->dev, NULL, resume_iter); | ||
104 | } | 260 | } |
105 | #endif | ||
106 | 261 | ||
107 | /* | 262 | /* |
108 | * LINUX Device Driver Model | 263 | * LINUX Device Driver Model |
@@ -114,6 +269,13 @@ static const struct pci_device_id port_pci_ids[] = { { | |||
114 | }; | 269 | }; |
115 | MODULE_DEVICE_TABLE(pci, port_pci_ids); | 270 | MODULE_DEVICE_TABLE(pci, port_pci_ids); |
116 | 271 | ||
272 | static struct pci_error_handlers pcie_portdrv_err_handler = { | ||
273 | .error_detected = pcie_portdrv_error_detected, | ||
274 | .mmio_enabled = pcie_portdrv_mmio_enabled, | ||
275 | .slot_reset = pcie_portdrv_slot_reset, | ||
276 | .resume = pcie_portdrv_err_resume, | ||
277 | }; | ||
278 | |||
117 | static struct pci_driver pcie_portdrv = { | 279 | static struct pci_driver pcie_portdrv = { |
118 | .name = (char *)device_name, | 280 | .name = (char *)device_name, |
119 | .id_table = &port_pci_ids[0], | 281 | .id_table = &port_pci_ids[0], |
@@ -121,20 +283,25 @@ static struct pci_driver pcie_portdrv = { | |||
121 | .probe = pcie_portdrv_probe, | 283 | .probe = pcie_portdrv_probe, |
122 | .remove = pcie_portdrv_remove, | 284 | .remove = pcie_portdrv_remove, |
123 | 285 | ||
124 | #ifdef CONFIG_PM | ||
125 | .suspend = pcie_portdrv_suspend, | 286 | .suspend = pcie_portdrv_suspend, |
126 | .resume = pcie_portdrv_resume, | 287 | .resume = pcie_portdrv_resume, |
127 | #endif /* PM */ | 288 | |
289 | .err_handler = &pcie_portdrv_err_handler, | ||
128 | }; | 290 | }; |
129 | 291 | ||
130 | static int __init pcie_portdrv_init(void) | 292 | static int __init pcie_portdrv_init(void) |
131 | { | 293 | { |
132 | int retval = 0; | 294 | int retval; |
133 | 295 | ||
134 | pcie_port_bus_register(); | 296 | retval = pcie_port_bus_register(); |
297 | if (retval) { | ||
298 | printk(KERN_WARNING "PCIE: bus_register error: %d\n", retval); | ||
299 | goto out; | ||
300 | } | ||
135 | retval = pci_register_driver(&pcie_portdrv); | 301 | retval = pci_register_driver(&pcie_portdrv); |
136 | if (retval) | 302 | if (retval) |
137 | pcie_port_bus_unregister(); | 303 | pcie_port_bus_unregister(); |
304 | out: | ||
138 | return retval; | 305 | return retval; |
139 | } | 306 | } |
140 | 307 | ||