PCI AER: support Multiple Error Received and no error source id
[linux-2.6.git] / drivers / pci / pcie / aer / aerdrv_core.c
index 1ff21f6..a7a3919 100644 (file)
@@ -33,11 +33,11 @@ int pci_enable_pcie_error_reporting(struct pci_dev *dev)
        u16 reg16 = 0;
        int pos;
 
-       pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
+       pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
        if (!pos)
                return -EIO;
 
-       pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+       pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
        if (!pos)
                return -EIO;
 
@@ -108,6 +108,41 @@ int pci_cleanup_aer_correct_error_status(struct pci_dev *dev)
 }
 #endif  /*  0  */
 
+
+static int set_device_error_reporting(struct pci_dev *dev, void *data)
+{
+       bool enable = *((bool *)data);
+
+       if (dev->pcie_type == PCIE_RC_PORT ||
+           dev->pcie_type == PCIE_SW_UPSTREAM_PORT ||
+           dev->pcie_type == PCIE_SW_DOWNSTREAM_PORT) {
+               if (enable)
+                       pci_enable_pcie_error_reporting(dev);
+               else
+                       pci_disable_pcie_error_reporting(dev);
+       }
+
+       if (enable)
+               pcie_set_ecrc_checking(dev);
+
+       return 0;
+}
+
+/**
+ * set_downstream_devices_error_reporting - enable/disable the error reporting  bits on the root port and its downstream ports.
+ * @dev: pointer to root port's pci_dev data structure
+ * @enable: true = enable error reporting, false = disable error reporting.
+ */
+static void set_downstream_devices_error_reporting(struct pci_dev *dev,
+                                                  bool enable)
+{
+       set_device_error_reporting(dev, &enable);
+
+       if (!dev->subordinate)
+               return;
+       pci_walk_bus(dev->subordinate, set_device_error_reporting, &enable);
+}
+
 static int find_device_iter(struct device *device, void *data)
 {
        struct pci_dev *dev;
@@ -174,7 +209,7 @@ static struct device* find_source_device(struct pci_dev *parent, u16 id)
        return NULL;
 }
 
-static void report_error_detected(struct pci_dev *dev, void *data)
+static int report_error_detected(struct pci_dev *dev, void *data)
 {
        pci_ers_result_t vote;
        struct pci_error_handlers *err_handler;
@@ -199,16 +234,16 @@ static void report_error_detected(struct pci_dev *dev, void *data)
                                   dev->driver ?
                                   "no AER-aware driver" : "no driver");
                }
-               return;
+               return 0;
        }
 
        err_handler = dev->driver->err_handler;
        vote = err_handler->error_detected(dev, result_data->state);
        result_data->result = merge_result(result_data->result, vote);
-       return;
+       return 0;
 }
 
-static void report_mmio_enabled(struct pci_dev *dev, void *data)
+static int report_mmio_enabled(struct pci_dev *dev, void *data)
 {
        pci_ers_result_t vote;
        struct pci_error_handlers *err_handler;
@@ -218,15 +253,15 @@ static void report_mmio_enabled(struct pci_dev *dev, void *data)
        if (!dev->driver ||
                !dev->driver->err_handler ||
                !dev->driver->err_handler->mmio_enabled)
-               return;
+               return 0;
 
        err_handler = dev->driver->err_handler;
        vote = err_handler->mmio_enabled(dev);
        result_data->result = merge_result(result_data->result, vote);
-       return;
+       return 0;
 }
 
-static void report_slot_reset(struct pci_dev *dev, void *data)
+static int report_slot_reset(struct pci_dev *dev, void *data)
 {
        pci_ers_result_t vote;
        struct pci_error_handlers *err_handler;
@@ -236,15 +271,15 @@ static void report_slot_reset(struct pci_dev *dev, void *data)
        if (!dev->driver ||
                !dev->driver->err_handler ||
                !dev->driver->err_handler->slot_reset)
-               return;
+               return 0;
 
        err_handler = dev->driver->err_handler;
        vote = err_handler->slot_reset(dev);
        result_data->result = merge_result(result_data->result, vote);
-       return;
+       return 0;
 }
 
-static void report_resume(struct pci_dev *dev, void *data)
+static int report_resume(struct pci_dev *dev, void *data)
 {
        struct pci_error_handlers *err_handler;
 
@@ -252,12 +287,12 @@ static void report_resume(struct pci_dev *dev, void *data)
 
        if (!dev->driver ||
                !dev->driver->err_handler ||
-               !dev->driver->err_handler->slot_reset)
-               return;
+               !dev->driver->err_handler->resume)
+               return 0;
 
        err_handler = dev->driver->err_handler;
        err_handler->resume(dev);
-       return;
+       return 0;
 }
 
 /**
@@ -274,7 +309,7 @@ static void report_resume(struct pci_dev *dev, void *data)
 static pci_ers_result_t broadcast_error_message(struct pci_dev *dev,
        enum pci_channel_state state,
        char *error_mesg,
-       void (*cb)(struct pci_dev *, void *))
+       int (*cb)(struct pci_dev *, void *))
 {
        struct aer_broadcast_data result_data;
 
@@ -320,21 +355,21 @@ static int find_aer_service_iter(struct device *device, void *data)
 {
        struct device_driver *driver;
        struct pcie_port_service_driver *service_driver;
-       struct pcie_device *pcie_dev;
        struct find_aer_service_data *result;
 
        result = (struct find_aer_service_data *) data;
 
        if (device->bus == &pcie_port_bus_type) {
-               pcie_dev = to_pcie_device(device);
-               if (pcie_dev->id.port_type == PCIE_SW_DOWNSTREAM_PORT)
+               struct pcie_port_data *port_data;
+
+               port_data = pci_get_drvdata(to_pcie_device(device)->port);
+               if (port_data->port_type == PCIE_SW_DOWNSTREAM_PORT)
                        result->is_downstream = 1;
 
                driver = device->driver;
                if (driver) {
                        service_driver = to_service_driver(driver);
-                       if (service_driver->id_table->service_type ==
-                                       PCIE_PORT_SERVICE_AER) {
+                       if (service_driver->service == PCIE_PORT_SERVICE_AER) {
                                result->aer_driver = service_driver;
                                return 1;
                        }
@@ -525,15 +560,11 @@ void aer_enable_rootport(struct aer_rpc *rpc)
        pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, &reg32);
        pci_write_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, reg32);
 
-       /* Enable Root Port device reporting error itself */
-       pci_read_config_word(pdev, pos+PCI_EXP_DEVCTL, &reg16);
-       reg16 = reg16 |
-               PCI_EXP_DEVCTL_CERE |
-               PCI_EXP_DEVCTL_NFERE |
-               PCI_EXP_DEVCTL_FERE |
-               PCI_EXP_DEVCTL_URRE;
-       pci_write_config_word(pdev, pos+PCI_EXP_DEVCTL,
-               reg16);
+       /*
+        * Enable error reporting for the root port device and downstream port
+        * devices.
+        */
+       set_downstream_devices_error_reporting(pdev, true);
 
        /* Enable Root Port's interrupt in response to error messages */
        pci_write_config_dword(pdev,
@@ -553,6 +584,12 @@ static void disable_root_aer(struct aer_rpc *rpc)
        u32 reg32;
        int pos;
 
+       /*
+        * Disable error reporting for the root port device and downstream port
+        * devices.
+        */
+       set_downstream_devices_error_reporting(pdev, false);
+
        pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
        /* Disable Root's interrupt in response to error messages */
        pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, 0);