PCI AER: support Multiple Error Received and no error source id
[linux-2.6.git] / drivers / pci / pcie / aer / aerdrv_core.c
index fef159a..a7a3919 100644 (file)
 static int forceload;
 module_param(forceload, bool, 0);
 
-#define PCI_CFG_SPACE_SIZE     (0x100)
-int pci_find_aer_capability(struct pci_dev *dev)
-{
-       int pos;
-       u32 reg32 = 0;
-
-       /* Check if it's a pci-express device */
-       pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
-       if (!pos)
-               return 0;
-
-       /* Check if it supports pci-express AER */
-       pos = PCI_CFG_SPACE_SIZE;
-       while (pos) {
-               if (pci_read_config_dword(dev, pos, &reg32))
-                       return 0;
-
-               /* some broken boards return ~0 */
-               if (reg32 == 0xffffffff)
-                       return 0;
-
-               if (PCI_EXT_CAP_ID(reg32) == PCI_EXT_CAP_ID_ERR)
-                       break;
-
-               pos = reg32 >> 20;
-       }
-
-       return pos;
-}
-
 int pci_enable_pcie_error_reporting(struct pci_dev *dev)
 {
        u16 reg16 = 0;
        int pos;
 
+       pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+       if (!pos)
+               return -EIO;
+
        pos = pci_find_capability(dev, PCI_CAP_ID_EXP);
        if (!pos)
                return -EIO;
@@ -102,7 +76,7 @@ int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev)
        int pos;
        u32 status, mask;
 
-       pos = pci_find_aer_capability(dev);
+       pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
        if (!pos)
                return -EIO;
 
@@ -117,6 +91,58 @@ int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev)
        return 0;
 }
 
+#if 0
+int pci_cleanup_aer_correct_error_status(struct pci_dev *dev)
+{
+       int pos;
+       u32 status;
+
+       pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
+       if (!pos)
+               return -EIO;
+
+       pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status);
+       pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS, status);
+
+       return 0;
+}
+#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;
@@ -153,11 +179,11 @@ static int find_device_iter(struct device *device, void *data)
 
 /**
  * find_source_device - search through device hierarchy for source device
- * @p_dev: pointer to Root Port pci_dev data structure
+ * @parent: pointer to Root Port pci_dev data structure
  * @id: device ID of agent who sends an error message to this Root Port
  *
  * Invoked when error is detected at the Root Port.
- **/
+ */
 static struct device* find_source_device(struct pci_dev *parent, u16 id)
 {
        struct pci_dev *dev = parent;
@@ -183,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;
@@ -204,20 +230,20 @@ static void report_error_detected(struct pci_dev *dev, void *data)
                         * of a driver for this device is unaware of
                         * its hw state.
                         */
-                       printk(KERN_DEBUG "Device ID[%s] has %s\n",
-                                       dev->dev.bus_id, (dev->driver) ?
-                                       "no AER-aware driver" : "no driver");
+                       dev_printk(KERN_DEBUG, &dev->dev, "device has %s\n",
+                                  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;
@@ -227,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;
@@ -245,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;
 
@@ -261,32 +287,33 @@ 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;
 }
 
 /**
  * broadcast_error_message - handle message broadcast to downstream drivers
- * @device: pointer to from where in a hierarchy message is broadcasted down
- * @api: callback to be broadcasted
+ * @dev: pointer to from where in a hierarchy message is broadcasted down
  * @state: error state
+ * @error_mesg: message to print
+ * @cb: callback to be broadcasted
  *
  * Invoked during error recovery process. Once being invoked, the content
  * of error severity will be broadcasted to all downstream drivers in a
  * hierarchy in question.
- **/
+ */
 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;
 
-       printk(KERN_DEBUG "Broadcast %s message\n", error_mesg);
+       dev_printk(KERN_DEBUG, &dev->dev, "broadcast %s message\n", error_mesg);
        result_data.state = state;
        if (cb == report_error_detected)
                result_data.result = PCI_ERS_RESULT_CAN_RECOVER;
@@ -328,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;
                        }
@@ -386,18 +413,16 @@ static pci_ers_result_t reset_link(struct pcie_device *aerdev,
                        data.aer_driver =
                                to_service_driver(aerdev->device.driver);
                } else {
-                       printk(KERN_DEBUG "No link-reset support to Device ID"
-                               "[%s]\n",
-                               dev->dev.bus_id);
+                       dev_printk(KERN_DEBUG, &dev->dev, "no link-reset "
+                                  "support\n");
                        return PCI_ERS_RESULT_DISCONNECT;
                }
        }
 
        status = data.aer_driver->reset_link(udev);
        if (status != PCI_ERS_RESULT_RECOVERED) {
-               printk(KERN_DEBUG "Link reset at upstream Device ID"
-                       "[%s] failed\n",
-                       udev->dev.bus_id);
+               dev_printk(KERN_DEBUG, &dev->dev, "link reset at upstream "
+                          "device %s failed\n", pci_name(udev));
                return PCI_ERS_RESULT_DISCONNECT;
        }
 
@@ -413,7 +438,7 @@ static pci_ers_result_t reset_link(struct pcie_device *aerdev,
  * Invoked when an error is nonfatal/fatal. Once being invoked, broadcast
  * error detected message to all downstream drivers within a hierarchy in
  * question and return the returned code.
- **/
+ */
 static pci_ers_result_t do_recovery(struct pcie_device *aerdev,
                struct pci_dev *dev,
                int severity)
@@ -473,7 +498,7 @@ static pci_ers_result_t do_recovery(struct pcie_device *aerdev,
  * @info: comprehensive error information
  *
  * Invoked when an error being detected by Root Port.
- **/
+ */
 static void handle_error_source(struct pcie_device * aerdev,
        struct pci_dev *dev,
        struct aer_err_info info)
@@ -486,17 +511,19 @@ static void handle_error_source(struct pcie_device * aerdev,
                 * Correctable error does not need software intevention.
                 * No need to go through error recovery process.
                 */
-               pos = pci_find_aer_capability(dev);
+               pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
                if (pos)
                        pci_write_config_dword(dev, pos + PCI_ERR_COR_STATUS,
                                        info.status);
        } else {
                status = do_recovery(aerdev, dev, info.severity);
                if (status == PCI_ERS_RESULT_RECOVERED) {
-                       printk(KERN_DEBUG "AER driver successfully recovered\n");
+                       dev_printk(KERN_DEBUG, &dev->dev, "AER driver "
+                                  "successfully recovered\n");
                } else {
                        /* TODO: Should kernel panic here? */
-                       printk(KERN_DEBUG "AER driver didn't recover\n");
+                       dev_printk(KERN_DEBUG, &dev->dev, "AER driver didn't "
+                                  "recover\n");
                }
        }
 }
@@ -506,7 +533,7 @@ static void handle_error_source(struct pcie_device * aerdev,
  * @rpc: pointer to a Root Port data structure
  *
  * Invoked when PCIE bus loads AER service driver.
- **/
+ */
 void aer_enable_rootport(struct aer_rpc *rpc)
 {
        struct pci_dev *pdev = rpc->rpd->port;
@@ -524,7 +551,7 @@ void aer_enable_rootport(struct aer_rpc *rpc)
        reg16 &= ~(SYSTEM_ERROR_INTR_ON_MESG_MASK);
        pci_write_config_word(pdev, pos + PCI_EXP_RTCTL, reg16);
 
-       aer_pos = pci_find_aer_capability(pdev);
+       aer_pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_ERR);
        /* Clear error status */
        pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, &reg32);
        pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32);
@@ -533,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,
@@ -554,14 +577,20 @@ void aer_enable_rootport(struct aer_rpc *rpc)
  * @rpc: pointer to a Root Port data structure
  *
  * Invoked when PCIE bus unloads AER service driver.
- **/
+ */
 static void disable_root_aer(struct aer_rpc *rpc)
 {
        struct pci_dev *pdev = rpc->rpd->port;
        u32 reg32;
        int pos;
 
-       pos = pci_find_aer_capability(pdev);
+       /*
+        * 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);
 
@@ -575,7 +604,7 @@ static void disable_root_aer(struct aer_rpc *rpc)
  * @rpc: pointer to the root port which holds an error
  *
  * Invoked by DPC handler to consume an error.
- **/
+ */
 static struct aer_err_source* get_e_source(struct aer_rpc *rpc)
 {
        struct aer_err_source *e_source;
@@ -600,7 +629,7 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
 {
        int pos;
 
-       pos = pci_find_aer_capability(dev);
+       pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
 
        /* The device might not support AER */
        if (!pos)
@@ -640,7 +669,7 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info)
  * aer_isr_one_error - consume an error detected by root port
  * @p_device: pointer to error root port service device
  * @e_src: pointer to an error source
- **/
+ */
 static void aer_isr_one_error(struct pcie_device *p_device,
                struct aer_err_source *e_src)
 {
@@ -673,7 +702,7 @@ static void aer_isr_one_error(struct pcie_device *p_device,
                        e_info.flags |= AER_MULTI_ERROR_VALID_FLAG;
                if (!(s_device = find_source_device(p_device->port, id))) {
                        printk(KERN_DEBUG "%s->can't find device of ID%04x\n",
-                               __FUNCTION__, id);
+                               __func__, id);
                        continue;
                }
                if (get_device_error_info(to_pci_dev(s_device), &e_info) ==
@@ -691,7 +720,7 @@ static void aer_isr_one_error(struct pcie_device *p_device,
  * @work: definition of this work item
  *
  * Invoked, as DPC, when root port records new detected error
- **/
+ */
 void aer_isr(struct work_struct *work)
 {
        struct aer_rpc *rpc = container_of(work, struct aer_rpc, dpc_handler);
@@ -714,7 +743,7 @@ void aer_isr(struct work_struct *work)
  * @rpc: pointer to a root port device being deleted
  *
  * Invoked when AER service unloaded on a specific Root Port
- **/
+ */
 void aer_delete_rootport(struct aer_rpc *rpc)
 {
        /* Disable root port AER itself */
@@ -728,7 +757,7 @@ void aer_delete_rootport(struct aer_rpc *rpc)
  * @dev: pointer to AER pcie device
  *
  * Invoked when AER service driver is loaded.
- **/
+ */
 int aer_init(struct pcie_device *dev)
 {
        if (aer_osc_setup(dev) && !forceload)
@@ -737,7 +766,6 @@ int aer_init(struct pcie_device *dev)
        return AER_SUCCESS;
 }
 
-EXPORT_SYMBOL_GPL(pci_find_aer_capability);
 EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting);
 EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting);
 EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status);