]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - drivers/pci/pcie/aer/aerdrv_core.c
PCI: Add support for turning PCIe ECRC on or off
[mirror_ubuntu-artful-kernel.git] / drivers / pci / pcie / aer / aerdrv_core.c
CommitLineData
6c2b374d
ZY
1/*
2 * drivers/pci/pcie/aer/aerdrv_core.c
3 *
4 * This file is subject to the terms and conditions of the GNU General Public
5 * License. See the file "COPYING" in the main directory of this archive
6 * for more details.
7 *
8 * This file implements the core part of PCI-Express AER. When an pci-express
9 * error is delivered, an error message will be collected and printed to
10 * console, then, an error recovery procedure will be executed by following
11 * the pci error recovery rules.
12 *
13 * Copyright (C) 2006 Intel Corp.
14 * Tom Long Nguyen (tom.l.nguyen@intel.com)
15 * Zhang Yanmin (yanmin.zhang@intel.com)
16 *
17 */
18
19#include <linux/module.h>
20#include <linux/pci.h>
21#include <linux/kernel.h>
22#include <linux/errno.h>
23#include <linux/pm.h>
24#include <linux/suspend.h>
6c2b374d
ZY
25#include <linux/delay.h>
26#include "aerdrv.h"
27
28static int forceload;
29module_param(forceload, bool, 0);
30
6c2b374d
ZY
31int pci_enable_pcie_error_reporting(struct pci_dev *dev)
32{
33 u16 reg16 = 0;
34 int pos;
35
270c66be 36 pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
6c2b374d
ZY
37 if (!pos)
38 return -EIO;
39
270c66be 40 pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
0927678f
JB
41 if (!pos)
42 return -EIO;
43
6c2b374d
ZY
44 pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, &reg16);
45 reg16 = reg16 |
46 PCI_EXP_DEVCTL_CERE |
47 PCI_EXP_DEVCTL_NFERE |
48 PCI_EXP_DEVCTL_FERE |
49 PCI_EXP_DEVCTL_URRE;
50 pci_write_config_word(dev, pos+PCI_EXP_DEVCTL,
51 reg16);
52 return 0;
53}
54
55int pci_disable_pcie_error_reporting(struct pci_dev *dev)
56{
57 u16 reg16 = 0;
58 int pos;
59
60 pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
61 if (!pos)
62 return -EIO;
63
64 pci_read_config_word(dev, pos+PCI_EXP_DEVCTL, &reg16);
65 reg16 = reg16 & ~(PCI_EXP_DEVCTL_CERE |
66 PCI_EXP_DEVCTL_NFERE |
67 PCI_EXP_DEVCTL_FERE |
68 PCI_EXP_DEVCTL_URRE);
69 pci_write_config_word(dev, pos+PCI_EXP_DEVCTL,
70 reg16);
71 return 0;
72}
73
74int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev)
75{
76 int pos;
77 u32 status, mask;
78
0927678f 79 pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
6c2b374d
ZY
80 if (!pos)
81 return -EIO;
82
83 pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status);
84 pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask);
85 if (dev->error_state == pci_channel_io_normal)
86 status &= ~mask; /* Clear corresponding nonfatal bits */
87 else
88 status &= mask; /* Clear corresponding fatal bits */
89 pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status);
90
91 return 0;
92}
93
21c68474 94#if 0
f0dce411
SH
95int pci_cleanup_aer_correct_error_status(struct pci_dev *dev)
96{
97 int pos;
98 u32 status;
99
0927678f 100 pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
f0dce411
SH
101 if (!pos)
102 return -EIO;
103
104 pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status);
105 pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, status);
106
107 return 0;
108}
21c68474 109#endif /* 0 */
f0dce411 110
1f9f13c8
AP
111
112static void set_device_error_reporting(struct pci_dev *dev, void *data)
113{
114 bool enable = *((bool *)data);
115
43c16408
AP
116 if (dev->pcie_type == PCIE_RC_PORT ||
117 dev->pcie_type == PCIE_SW_UPSTREAM_PORT ||
118 dev->pcie_type == PCIE_SW_DOWNSTREAM_PORT) {
119 if (enable)
120 pci_enable_pcie_error_reporting(dev);
121 else
122 pci_disable_pcie_error_reporting(dev);
123 }
1f9f13c8
AP
124
125 if (enable)
43c16408 126 pcie_set_ecrc_checking(dev);
1f9f13c8
AP
127}
128
129/**
130 * set_downstream_devices_error_reporting - enable/disable the error reporting bits on the root port and its downstream ports.
131 * @dev: pointer to root port's pci_dev data structure
132 * @enable: true = enable error reporting, false = disable error reporting.
133 */
134static void set_downstream_devices_error_reporting(struct pci_dev *dev,
135 bool enable)
136{
137 set_device_error_reporting(dev, &enable);
cb4cb4ac
AC
138
139 if (!dev->subordinate)
140 return;
1f9f13c8
AP
141 pci_walk_bus(dev->subordinate, set_device_error_reporting, &enable);
142}
143
6c2b374d
ZY
144static int find_device_iter(struct device *device, void *data)
145{
146 struct pci_dev *dev;
147 u16 id = *(unsigned long *)data;
148 u8 secondary, subordinate, d_bus = id >> 8;
149
150 if (device->bus == &pci_bus_type) {
151 dev = to_pci_dev(device);
152 if (id == ((dev->bus->number << 8) | dev->devfn)) {
153 /*
154 * Device ID match
155 */
156 *(unsigned long*)data = (unsigned long)device;
157 return 1;
158 }
159
160 /*
161 * If device is P2P, check if it is an upstream?
162 */
163 if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) {
164 pci_read_config_byte(dev, PCI_SECONDARY_BUS,
165 &secondary);
166 pci_read_config_byte(dev, PCI_SUBORDINATE_BUS,
167 &subordinate);
168 if (d_bus >= secondary && d_bus <= subordinate) {
169 *(unsigned long*)data = (unsigned long)device;
170 return 1;
171 }
172 }
173 }
174
175 return 0;
176}
177
178/**
179 * find_source_device - search through device hierarchy for source device
d885c6b7 180 * @parent: pointer to Root Port pci_dev data structure
6c2b374d
ZY
181 * @id: device ID of agent who sends an error message to this Root Port
182 *
183 * Invoked when error is detected at the Root Port.
d885c6b7 184 */
6c2b374d
ZY
185static struct device* find_source_device(struct pci_dev *parent, u16 id)
186{
187 struct pci_dev *dev = parent;
188 struct device *device;
189 unsigned long device_addr;
190 int status;
191
192 /* Is Root Port an agent that sends error message? */
193 if (id == ((dev->bus->number << 8) | dev->devfn))
194 return &dev->dev;
195
196 do {
197 device_addr = id;
198 if ((status = device_for_each_child(&dev->dev,
199 &device_addr, find_device_iter))) {
200 device = (struct device*)device_addr;
201 dev = to_pci_dev(device);
202 if (id == ((dev->bus->number << 8) | dev->devfn))
203 return device;
204 }
205 }while (status);
206
207 return NULL;
208}
209
210static void report_error_detected(struct pci_dev *dev, void *data)
211{
212 pci_ers_result_t vote;
213 struct pci_error_handlers *err_handler;
214 struct aer_broadcast_data *result_data;
215 result_data = (struct aer_broadcast_data *) data;
216
217 dev->error_state = result_data->state;
218
219 if (!dev->driver ||
220 !dev->driver->err_handler ||
221 !dev->driver->err_handler->error_detected) {
222 if (result_data->state == pci_channel_io_frozen &&
223 !(dev->hdr_type & PCI_HEADER_TYPE_BRIDGE)) {
224 /*
225 * In case of fatal recovery, if one of down-
226 * stream device has no driver. We might be
227 * unable to recover because a later insmod
228 * of a driver for this device is unaware of
229 * its hw state.
230 */
531f254e
BH
231 dev_printk(KERN_DEBUG, &dev->dev, "device has %s\n",
232 dev->driver ?
233 "no AER-aware driver" : "no driver");
6c2b374d
ZY
234 }
235 return;
236 }
237
238 err_handler = dev->driver->err_handler;
239 vote = err_handler->error_detected(dev, result_data->state);
240 result_data->result = merge_result(result_data->result, vote);
241 return;
242}
243
244static void report_mmio_enabled(struct pci_dev *dev, void *data)
245{
246 pci_ers_result_t vote;
247 struct pci_error_handlers *err_handler;
248 struct aer_broadcast_data *result_data;
249 result_data = (struct aer_broadcast_data *) data;
250
251 if (!dev->driver ||
252 !dev->driver->err_handler ||
253 !dev->driver->err_handler->mmio_enabled)
254 return;
255
256 err_handler = dev->driver->err_handler;
257 vote = err_handler->mmio_enabled(dev);
258 result_data->result = merge_result(result_data->result, vote);
259 return;
260}
261
262static void report_slot_reset(struct pci_dev *dev, void *data)
263{
264 pci_ers_result_t vote;
265 struct pci_error_handlers *err_handler;
266 struct aer_broadcast_data *result_data;
267 result_data = (struct aer_broadcast_data *) data;
268
269 if (!dev->driver ||
270 !dev->driver->err_handler ||
271 !dev->driver->err_handler->slot_reset)
272 return;
273
274 err_handler = dev->driver->err_handler;
275 vote = err_handler->slot_reset(dev);
276 result_data->result = merge_result(result_data->result, vote);
277 return;
278}
279
280static void report_resume(struct pci_dev *dev, void *data)
281{
282 struct pci_error_handlers *err_handler;
283
284 dev->error_state = pci_channel_io_normal;
285
286 if (!dev->driver ||
287 !dev->driver->err_handler ||
b0b801dd 288 !dev->driver->err_handler->resume)
6c2b374d
ZY
289 return;
290
291 err_handler = dev->driver->err_handler;
292 err_handler->resume(dev);
293 return;
294}
295
296/**
297 * broadcast_error_message - handle message broadcast to downstream drivers
d885c6b7 298 * @dev: pointer to from where in a hierarchy message is broadcasted down
6c2b374d 299 * @state: error state
d885c6b7
RD
300 * @error_mesg: message to print
301 * @cb: callback to be broadcasted
6c2b374d
ZY
302 *
303 * Invoked during error recovery process. Once being invoked, the content
304 * of error severity will be broadcasted to all downstream drivers in a
305 * hierarchy in question.
d885c6b7 306 */
6c2b374d
ZY
307static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
308 enum pci_channel_state state,
309 char *error_mesg,
310 void (*cb)(struct pci_dev *, void *))
311{
312 struct aer_broadcast_data result_data;
313
531f254e 314 dev_printk(KERN_DEBUG, &dev->dev, "broadcast %s message\n", error_mesg);
6c2b374d
ZY
315 result_data.state = state;
316 if (cb == report_error_detected)
317 result_data.result = PCI_ERS_RESULT_CAN_RECOVER;
318 else
319 result_data.result = PCI_ERS_RESULT_RECOVERED;
320
321 if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) {
322 /*
323 * If the error is reported by a bridge, we think this error
324 * is related to the downstream link of the bridge, so we
325 * do error recovery on all subordinates of the bridge instead
326 * of the bridge and clear the error status of the bridge.
327 */
328 if (cb == report_error_detected)
329 dev->error_state = state;
330 pci_walk_bus(dev->subordinate, cb, &result_data);
331 if (cb == report_resume) {
332 pci_cleanup_aer_uncorrect_error_status(dev);
333 dev->error_state = pci_channel_io_normal;
334 }
335 }
336 else {
337 /*
338 * If the error is reported by an end point, we think this
339 * error is related to the upstream link of the end point.
340 */
341 pci_walk_bus(dev->bus, cb, &result_data);
342 }
343
344 return result_data.result;
345}
346
347struct find_aer_service_data {
348 struct pcie_port_service_driver *aer_driver;
349 int is_downstream;
350};
351
352static int find_aer_service_iter(struct device *device, void *data)
353{
354 struct device_driver *driver;
355 struct pcie_port_service_driver *service_driver;
6c2b374d
ZY
356 struct find_aer_service_data *result;
357
358 result = (struct find_aer_service_data *) data;
359
360 if (device->bus == &pcie_port_bus_type) {
22106368
RW
361 struct pcie_port_data *port_data;
362
363 port_data = pci_get_drvdata(to_pcie_device(device)->port);
364 if (port_data->port_type == PCIE_SW_DOWNSTREAM_PORT)
6c2b374d
ZY
365 result->is_downstream = 1;
366
367 driver = device->driver;
368 if (driver) {
369 service_driver = to_service_driver(driver);
22106368 370 if (service_driver->service == PCIE_PORT_SERVICE_AER) {
6c2b374d
ZY
371 result->aer_driver = service_driver;
372 return 1;
373 }
374 }
375 }
376
377 return 0;
378}
379
380static void find_aer_service(struct pci_dev *dev,
381 struct find_aer_service_data *data)
382{
b19441af
GKH
383 int retval;
384 retval = device_for_each_child(&dev->dev, data, find_aer_service_iter);
6c2b374d
ZY
385}
386
387static pci_ers_result_t reset_link(struct pcie_device *aerdev,
388 struct pci_dev *dev)
389{
390 struct pci_dev *udev;
391 pci_ers_result_t status;
392 struct find_aer_service_data data;
393
394 if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE)
395 udev = dev;
396 else
397 udev= dev->bus->self;
398
399 data.is_downstream = 0;
400 data.aer_driver = NULL;
401 find_aer_service(udev, &data);
402
403 /*
404 * Use the aer driver of the error agent firstly.
405 * If it hasn't the aer driver, use the root port's
406 */
407 if (!data.aer_driver || !data.aer_driver->reset_link) {
408 if (data.is_downstream &&
409 aerdev->device.driver &&
410 to_service_driver(aerdev->device.driver)->reset_link) {
411 data.aer_driver =
412 to_service_driver(aerdev->device.driver);
413 } else {
531f254e
BH
414 dev_printk(KERN_DEBUG, &dev->dev, "no link-reset "
415 "support\n");
6c2b374d
ZY
416 return PCI_ERS_RESULT_DISCONNECT;
417 }
418 }
419
420 status = data.aer_driver->reset_link(udev);
421 if (status != PCI_ERS_RESULT_RECOVERED) {
531f254e
BH
422 dev_printk(KERN_DEBUG, &dev->dev, "link reset at upstream "
423 "device %s failed\n", pci_name(udev));
6c2b374d
ZY
424 return PCI_ERS_RESULT_DISCONNECT;
425 }
426
427 return status;
428}
429
430/**
431 * do_recovery - handle nonfatal/fatal error recovery process
432 * @aerdev: pointer to a pcie_device data structure of root port
433 * @dev: pointer to a pci_dev data structure of agent detecting an error
434 * @severity: error severity type
435 *
436 * Invoked when an error is nonfatal/fatal. Once being invoked, broadcast
437 * error detected message to all downstream drivers within a hierarchy in
438 * question and return the returned code.
d885c6b7 439 */
6c2b374d
ZY
440static pci_ers_result_t do_recovery(struct pcie_device *aerdev,
441 struct pci_dev *dev,
442 int severity)
443{
444 pci_ers_result_t status, result = PCI_ERS_RESULT_RECOVERED;
445 enum pci_channel_state state;
446
447 if (severity == AER_FATAL)
448 state = pci_channel_io_frozen;
449 else
450 state = pci_channel_io_normal;
451
452 status = broadcast_error_message(dev,
453 state,
454 "error_detected",
455 report_error_detected);
456
457 if (severity == AER_FATAL) {
458 result = reset_link(aerdev, dev);
459 if (result != PCI_ERS_RESULT_RECOVERED) {
460 /* TODO: Should panic here? */
461 return result;
462 }
463 }
464
465 if (status == PCI_ERS_RESULT_CAN_RECOVER)
466 status = broadcast_error_message(dev,
467 state,
468 "mmio_enabled",
469 report_mmio_enabled);
470
471 if (status == PCI_ERS_RESULT_NEED_RESET) {
472 /*
473 * TODO: Should call platform-specific
474 * functions to reset slot before calling
475 * drivers' slot_reset callbacks?
476 */
477 status = broadcast_error_message(dev,
478 state,
479 "slot_reset",
480 report_slot_reset);
481 }
482
483 if (status == PCI_ERS_RESULT_RECOVERED)
484 broadcast_error_message(dev,
485 state,
486 "resume",
487 report_resume);
488
489 return status;
490}
491
492/**
493 * handle_error_source - handle logging error into an event log
494 * @aerdev: pointer to pcie_device data structure of the root port
495 * @dev: pointer to pci_dev data structure of error source device
496 * @info: comprehensive error information
497 *
498 * Invoked when an error being detected by Root Port.
d885c6b7 499 */
6c2b374d
ZY
500static void handle_error_source(struct pcie_device * aerdev,
501 struct pci_dev *dev,
502 struct aer_err_info info)
503{
504 pci_ers_result_t status = 0;
505 int pos;
506
507 if (info.severity == AER_CORRECTABLE) {
508 /*
509 * Correctable error does not need software intevention.
510 * No need to go through error recovery process.
511 */
0927678f 512 pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
6c2b374d
ZY
513 if (pos)
514 pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS,
515 info.status);
516 } else {
517 status = do_recovery(aerdev, dev, info.severity);
518 if (status == PCI_ERS_RESULT_RECOVERED) {
531f254e
BH
519 dev_printk(KERN_DEBUG, &dev->dev, "AER driver "
520 "successfully recovered\n");
6c2b374d
ZY
521 } else {
522 /* TODO: Should kernel panic here? */
531f254e
BH
523 dev_printk(KERN_DEBUG, &dev->dev, "AER driver didn't "
524 "recover\n");
6c2b374d
ZY
525 }
526 }
527}
528
529/**
530 * aer_enable_rootport - enable Root Port's interrupts when receiving messages
531 * @rpc: pointer to a Root Port data structure
532 *
533 * Invoked when PCIE bus loads AER service driver.
d885c6b7 534 */
6c2b374d
ZY
535void aer_enable_rootport(struct aer_rpc *rpc)
536{
537 struct pci_dev *pdev = rpc->rpd->port;
538 int pos, aer_pos;
539 u16 reg16;
540 u32 reg32;
541
542 pos = pci_find_capability(pdev, PCI_CAP_ID_EXP);
543 /* Clear PCIE Capability's Device Status */
544 pci_read_config_word(pdev, pos+PCI_EXP_DEVSTA, &reg16);
545 pci_write_config_word(pdev, pos+PCI_EXP_DEVSTA, reg16);
546
547 /* Disable system error generation in response to error messages */
548 pci_read_config_word(pdev, pos + PCI_EXP_RTCTL, &reg16);
549 reg16 &= ~(SYSTEM_ERROR_INTR_ON_MESG_MASK);
550 pci_write_config_word(pdev, pos + PCI_EXP_RTCTL, reg16);
551
0927678f 552 aer_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
6c2b374d
ZY
553 /* Clear error status */
554 pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, &reg32);
555 pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32);
556 pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, &reg32);
557 pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32);
558 pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, &reg32);
559 pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32);
560
1f9f13c8
AP
561 /*
562 * Enable error reporting for the root port device and downstream port
563 * devices.
564 */
565 set_downstream_devices_error_reporting(pdev, true);
6c2b374d
ZY
566
567 /* Enable Root Port's interrupt in response to error messages */
568 pci_write_config_dword(pdev,
569 aer_pos + PCI_ERR_ROOT_COMMAND,
570 ROOT_PORT_INTR_ON_MESG_MASK);
571}
572
573/**
574 * disable_root_aer - disable Root Port's interrupts when receiving messages
575 * @rpc: pointer to a Root Port data structure
576 *
577 * Invoked when PCIE bus unloads AER service driver.
d885c6b7 578 */
6c2b374d
ZY
579static void disable_root_aer(struct aer_rpc *rpc)
580{
581 struct pci_dev *pdev = rpc->rpd->port;
582 u32 reg32;
583 int pos;
584
1f9f13c8
AP
585 /*
586 * Disable error reporting for the root port device and downstream port
587 * devices.
588 */
589 set_downstream_devices_error_reporting(pdev, false);
590
0927678f 591 pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
6c2b374d
ZY
592 /* Disable Root's interrupt in response to error messages */
593 pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, 0);
594
595 /* Clear Root's error status reg */
596 pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, &reg32);
597 pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32);
598}
599
600/**
601 * get_e_source - retrieve an error source
602 * @rpc: pointer to the root port which holds an error
603 *
604 * Invoked by DPC handler to consume an error.
d885c6b7 605 */
6c2b374d
ZY
606static struct aer_err_source* get_e_source(struct aer_rpc *rpc)
607{
608 struct aer_err_source *e_source;
609 unsigned long flags;
610
611 /* Lock access to Root error producer/consumer index */
612 spin_lock_irqsave(&rpc->e_lock, flags);
613 if (rpc->prod_idx == rpc->cons_idx) {
614 spin_unlock_irqrestore(&rpc->e_lock, flags);
615 return NULL;
616 }
617 e_source = &rpc->e_sources[rpc->cons_idx];
618 rpc->cons_idx++;
619 if (rpc->cons_idx == AER_ERROR_SOURCES_MAX)
620 rpc->cons_idx = 0;
621 spin_unlock_irqrestore(&rpc->e_lock, flags);
622
623 return e_source;
624}
625
626static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
627{
628 int pos;
629
0927678f 630 pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
6c2b374d
ZY
631
632 /* The device might not support AER */
633 if (!pos)
634 return AER_SUCCESS;
635
636 if (info->severity == AER_CORRECTABLE) {
637 pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS,
638 &info->status);
639 if (!(info->status & ERR_CORRECTABLE_ERROR_MASK))
640 return AER_UNSUCCESS;
641 } else if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE ||
642 info->severity == AER_NONFATAL) {
643
644 /* Link is still healthy for IO reads */
645 pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS,
646 &info->status);
647 if (!(info->status & ERR_UNCORRECTABLE_ERROR_MASK))
648 return AER_UNSUCCESS;
649
650 if (info->status & AER_LOG_TLP_MASKS) {
651 info->flags |= AER_TLP_HEADER_VALID_FLAG;
652 pci_read_config_dword(dev,
653 pos + PCI_ERR_HEADER_LOG, &info->tlp.dw0);
654 pci_read_config_dword(dev,
655 pos + PCI_ERR_HEADER_LOG + 4, &info->tlp.dw1);
656 pci_read_config_dword(dev,
657 pos + PCI_ERR_HEADER_LOG + 8, &info->tlp.dw2);
658 pci_read_config_dword(dev,
659 pos + PCI_ERR_HEADER_LOG + 12, &info->tlp.dw3);
660 }
661 }
662
663 return AER_SUCCESS;
664}
665
666/**
667 * aer_isr_one_error - consume an error detected by root port
668 * @p_device: pointer to error root port service device
669 * @e_src: pointer to an error source
d885c6b7 670 */
6c2b374d
ZY
671static void aer_isr_one_error(struct pcie_device *p_device,
672 struct aer_err_source *e_src)
673{
674 struct device *s_device;
675 struct aer_err_info e_info = {0, 0, 0,};
676 int i;
677 u16 id;
678
679 /*
680 * There is a possibility that both correctable error and
681 * uncorrectable error being logged. Report correctable error first.
682 */
683 for (i = 1; i & ROOT_ERR_STATUS_MASKS ; i <<= 2) {
684 if (i > 4)
685 break;
686 if (!(e_src->status & i))
687 continue;
688
689 /* Init comprehensive error information */
690 if (i & PCI_ERR_ROOT_COR_RCV) {
691 id = ERR_COR_ID(e_src->id);
692 e_info.severity = AER_CORRECTABLE;
693 } else {
694 id = ERR_UNCOR_ID(e_src->id);
695 e_info.severity = ((e_src->status >> 6) & 1);
696 }
697 if (e_src->status &
698 (PCI_ERR_ROOT_MULTI_COR_RCV |
699 PCI_ERR_ROOT_MULTI_UNCOR_RCV))
700 e_info.flags |= AER_MULTI_ERROR_VALID_FLAG;
701 if (!(s_device = find_source_device(p_device->port, id))) {
702 printk(KERN_DEBUG "%s->can't find device of ID%04x\n",
66bef8c0 703 __func__, id);
6c2b374d
ZY
704 continue;
705 }
706 if (get_device_error_info(to_pci_dev(s_device), &e_info) ==
707 AER_SUCCESS) {
708 aer_print_error(to_pci_dev(s_device), &e_info);
709 handle_error_source(p_device,
710 to_pci_dev(s_device),
711 e_info);
712 }
713 }
714}
715
716/**
717 * aer_isr - consume errors detected by root port
65f27f38 718 * @work: definition of this work item
6c2b374d
ZY
719 *
720 * Invoked, as DPC, when root port records new detected error
d885c6b7 721 */
65f27f38 722void aer_isr(struct work_struct *work)
6c2b374d 723{
65f27f38
DH
724 struct aer_rpc *rpc = container_of(work, struct aer_rpc, dpc_handler);
725 struct pcie_device *p_device = rpc->rpd;
6c2b374d
ZY
726 struct aer_err_source *e_src;
727
728 mutex_lock(&rpc->rpc_mutex);
729 e_src = get_e_source(rpc);
730 while (e_src) {
731 aer_isr_one_error(p_device, e_src);
732 e_src = get_e_source(rpc);
733 }
734 mutex_unlock(&rpc->rpc_mutex);
735
736 wake_up(&rpc->wait_release);
737}
738
739/**
740 * aer_delete_rootport - disable root port aer and delete service data
741 * @rpc: pointer to a root port device being deleted
742 *
743 * Invoked when AER service unloaded on a specific Root Port
d885c6b7 744 */
6c2b374d
ZY
745void aer_delete_rootport(struct aer_rpc *rpc)
746{
747 /* Disable root port AER itself */
748 disable_root_aer(rpc);
749
750 kfree(rpc);
751}
752
753/**
754 * aer_init - provide AER initialization
755 * @dev: pointer to AER pcie device
756 *
757 * Invoked when AER service driver is loaded.
d885c6b7 758 */
6c2b374d
ZY
759int aer_init(struct pcie_device *dev)
760{
8d29bfb7
ZY
761 if (aer_osc_setup(dev) && !forceload)
762 return -ENXIO;
6c2b374d
ZY
763
764 return AER_SUCCESS;
765}
766
6c2b374d
ZY
767EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting);
768EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting);
769EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status);
770