diff options
author | Zhang, Yanmin <yanmin.zhang@intel.com> | 2006-07-31 03:26:16 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-09-26 20:43:53 -0400 |
commit | 4bf3392e0bf55e5aabbd7bbdbc52cc58eb63f837 (patch) | |
tree | 7ba7dbfec7dc8e32c48868fb9b9196e6c934c1eb /drivers/pci/pcie | |
parent | 6c2b374d74857e892080ee726184ec1d15e7d4e4 (diff) |
PCI-Express AER implemetation: pcie_portdrv error handler
Patch 4 implements error handlers for pcie_portdrv.
Signed-off-by: Zhang Yanmin <yanmin.zhang@intel.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/pci/pcie')
-rw-r--r-- | drivers/pci/pcie/portdrv_pci.c | 196 |
1 files changed, 175 insertions, 21 deletions
diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 3284199ce396..e4a2429986f0 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,143 @@ 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 | { |
81 | int retval; | 148 | struct aer_broadcast_data result_data = |
149 | {error, PCI_ERS_RESULT_CAN_RECOVER}; | ||
150 | |||
151 | device_for_each_child(&dev->dev, &result_data, error_detected_iter); | ||
152 | |||
153 | return result_data.result; | ||
154 | } | ||
155 | |||
156 | static int mmio_enabled_iter(struct device *device, void *data) | ||
157 | { | ||
158 | struct pcie_device *pcie_device; | ||
159 | struct pcie_port_service_driver *driver; | ||
160 | pci_ers_result_t status, *result; | ||
161 | |||
162 | result = (pci_ers_result_t *) data; | ||
163 | |||
164 | if (device->bus == &pcie_port_bus_type && device->driver) { | ||
165 | driver = to_service_driver(device->driver); | ||
166 | if (driver && | ||
167 | driver->err_handler && | ||
168 | driver->err_handler->mmio_enabled) { | ||
169 | pcie_device = to_pcie_device(device); | ||
170 | |||
171 | /* Forward error message to service drivers */ | ||
172 | status = driver->err_handler->mmio_enabled( | ||
173 | pcie_device->port); | ||
174 | *result = merge_result(*result, status); | ||
175 | } | ||
176 | } | ||
82 | 177 | ||
83 | pci_restore_state(dev); | ||
84 | retval = pci_enable_device(dev); | ||
85 | if (retval) | ||
86 | return retval; | ||
87 | pci_set_master(dev); | ||
88 | return 0; | 178 | return 0; |
89 | } | 179 | } |
90 | 180 | ||
91 | static int pcie_portdrv_suspend (struct pci_dev *dev, pm_message_t state) | 181 | static pci_ers_result_t pcie_portdrv_mmio_enabled(struct pci_dev *dev) |
92 | { | 182 | { |
93 | int ret = pcie_port_device_suspend(dev, state); | 183 | pci_ers_result_t status = PCI_ERS_RESULT_RECOVERED; |
94 | 184 | ||
95 | if (!ret) | 185 | device_for_each_child(&dev->dev, &status, mmio_enabled_iter); |
96 | ret = pcie_portdrv_save_config(dev); | 186 | return status; |
97 | return ret; | ||
98 | } | 187 | } |
99 | 188 | ||
100 | static int pcie_portdrv_resume (struct pci_dev *dev) | 189 | static int slot_reset_iter(struct device *device, void *data) |
101 | { | 190 | { |
102 | pcie_portdrv_restore_config(dev); | 191 | struct pcie_device *pcie_device; |
103 | return pcie_port_device_resume(dev); | 192 | struct pcie_port_service_driver *driver; |
193 | pci_ers_result_t status, *result; | ||
194 | |||
195 | result = (pci_ers_result_t *) data; | ||
196 | |||
197 | if (device->bus == &pcie_port_bus_type && device->driver) { | ||
198 | driver = to_service_driver(device->driver); | ||
199 | if (driver && | ||
200 | driver->err_handler && | ||
201 | driver->err_handler->slot_reset) { | ||
202 | pcie_device = to_pcie_device(device); | ||
203 | |||
204 | /* Forward error message to service drivers */ | ||
205 | status = driver->err_handler->slot_reset( | ||
206 | pcie_device->port); | ||
207 | *result = merge_result(*result, status); | ||
208 | } | ||
209 | } | ||
210 | |||
211 | return 0; | ||
212 | } | ||
213 | |||
214 | static pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev) | ||
215 | { | ||
216 | pci_ers_result_t status; | ||
217 | |||
218 | /* If fatal, restore cfg space for possible link reset at upstream */ | ||
219 | if (dev->error_state == pci_channel_io_frozen) { | ||
220 | pcie_portdrv_restore_config(dev); | ||
221 | pci_enable_pcie_error_reporting(dev); | ||
222 | } | ||
223 | |||
224 | device_for_each_child(&dev->dev, &status, slot_reset_iter); | ||
225 | |||
226 | return status; | ||
227 | } | ||
228 | |||
229 | static int resume_iter(struct device *device, void *data) | ||
230 | { | ||
231 | struct pcie_device *pcie_device; | ||
232 | struct pcie_port_service_driver *driver; | ||
233 | |||
234 | if (device->bus == &pcie_port_bus_type && device->driver) { | ||
235 | driver = to_service_driver(device->driver); | ||
236 | if (driver && | ||
237 | driver->err_handler && | ||
238 | driver->err_handler->resume) { | ||
239 | pcie_device = to_pcie_device(device); | ||
240 | |||
241 | /* Forward error message to service drivers */ | ||
242 | driver->err_handler->resume(pcie_device->port); | ||
243 | } | ||
244 | } | ||
245 | |||
246 | return 0; | ||
247 | } | ||
248 | |||
249 | static void pcie_portdrv_err_resume(struct pci_dev *dev) | ||
250 | { | ||
251 | device_for_each_child(&dev->dev, NULL, resume_iter); | ||
104 | } | 252 | } |
105 | #endif | ||
106 | 253 | ||
107 | /* | 254 | /* |
108 | * LINUX Device Driver Model | 255 | * LINUX Device Driver Model |
@@ -114,6 +261,13 @@ static const struct pci_device_id port_pci_ids[] = { { | |||
114 | }; | 261 | }; |
115 | MODULE_DEVICE_TABLE(pci, port_pci_ids); | 262 | MODULE_DEVICE_TABLE(pci, port_pci_ids); |
116 | 263 | ||
264 | static struct pci_error_handlers pcie_portdrv_err_handler = { | ||
265 | .error_detected = pcie_portdrv_error_detected, | ||
266 | .mmio_enabled = pcie_portdrv_mmio_enabled, | ||
267 | .slot_reset = pcie_portdrv_slot_reset, | ||
268 | .resume = pcie_portdrv_err_resume, | ||
269 | }; | ||
270 | |||
117 | static struct pci_driver pcie_portdrv = { | 271 | static struct pci_driver pcie_portdrv = { |
118 | .name = (char *)device_name, | 272 | .name = (char *)device_name, |
119 | .id_table = &port_pci_ids[0], | 273 | .id_table = &port_pci_ids[0], |
@@ -121,10 +275,10 @@ static struct pci_driver pcie_portdrv = { | |||
121 | .probe = pcie_portdrv_probe, | 275 | .probe = pcie_portdrv_probe, |
122 | .remove = pcie_portdrv_remove, | 276 | .remove = pcie_portdrv_remove, |
123 | 277 | ||
124 | #ifdef CONFIG_PM | ||
125 | .suspend = pcie_portdrv_suspend, | 278 | .suspend = pcie_portdrv_suspend, |
126 | .resume = pcie_portdrv_resume, | 279 | .resume = pcie_portdrv_resume, |
127 | #endif /* PM */ | 280 | |
281 | .err_handler = &pcie_portdrv_err_handler, | ||
128 | }; | 282 | }; |
129 | 283 | ||
130 | static int __init pcie_portdrv_init(void) | 284 | static int __init pcie_portdrv_init(void) |