Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/ericvh...
[pandora-kernel.git] / drivers / pci / pcie / aer / aerdrv.c
index 7a711ee..484cc55 100644 (file)
@@ -72,13 +72,120 @@ void pci_no_aer(void)
        pcie_aer_disable = 1;   /* has priority over 'forceload' */
 }
 
+static int set_device_error_reporting(struct pci_dev *dev, void *data)
+{
+       bool enable = *((bool *)data);
+
+       if ((dev->pcie_type == PCI_EXP_TYPE_ROOT_PORT) ||
+           (dev->pcie_type == PCI_EXP_TYPE_UPSTREAM) ||
+           (dev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM)) {
+               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);
+}
+
+/**
+ * aer_enable_rootport - enable Root Port's interrupts when receiving messages
+ * @rpc: pointer to a Root Port data structure
+ *
+ * Invoked when PCIe bus loads AER service driver.
+ */
+static void aer_enable_rootport(struct aer_rpc *rpc)
+{
+       struct pci_dev *pdev = rpc->rpd->port;
+       int pos, aer_pos;
+       u16 reg16;
+       u32 reg32;
+
+       pos = pci_pcie_cap(pdev);
+       /* Clear PCIe Capability's Device Status */
+       pci_read_config_word(pdev, pos+PCI_EXP_DEVSTA, &reg16);
+       pci_write_config_word(pdev, pos+PCI_EXP_DEVSTA, reg16);
+
+       /* Disable system error generation in response to error messages */
+       pci_read_config_word(pdev, pos + PCI_EXP_RTCTL, &reg16);
+       reg16 &= ~(SYSTEM_ERROR_INTR_ON_MESG_MASK);
+       pci_write_config_word(pdev, pos + PCI_EXP_RTCTL, reg16);
+
+       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);
+       pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, &reg32);
+       pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32);
+       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 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_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, &reg32);
+       reg32 |= ROOT_PORT_INTR_ON_MESG_MASK;
+       pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, reg32);
+}
+
+/**
+ * aer_disable_rootport - disable Root Port's interrupts when receiving messages
+ * @rpc: pointer to a Root Port data structure
+ *
+ * Invoked when PCIe bus unloads AER service driver.
+ */
+static void aer_disable_rootport(struct aer_rpc *rpc)
+{
+       struct pci_dev *pdev = rpc->rpd->port;
+       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_read_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, &reg32);
+       reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
+       pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, reg32);
+
+       /* Clear Root's error status reg */
+       pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, &reg32);
+       pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32);
+}
+
 /**
  * aer_irq - Root Port's ISR
  * @irq: IRQ assigned to Root Port
  * @context: pointer to Root Port data structure
  *
  * Invoked when Root Port detects AER messages.
- **/
+ */
 irqreturn_t aer_irq(int irq, void *context)
 {
        unsigned int status, id;
@@ -97,13 +204,13 @@ irqreturn_t aer_irq(int irq, void *context)
 
        /* Read error status */
        pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status);
-       if (!(status & ROOT_ERR_STATUS_MASKS)) {
+       if (!(status & (PCI_ERR_ROOT_UNCOR_RCV|PCI_ERR_ROOT_COR_RCV))) {
                spin_unlock_irqrestore(&rpc->e_lock, flags);
                return IRQ_NONE;
        }
 
        /* Read error source and clear error status */
-       pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_COR_SRC, &id);
+       pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_ERR_SRC, &id);
        pci_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status);
 
        /* Store error source for later DPC handler */
@@ -135,7 +242,7 @@ EXPORT_SYMBOL_GPL(aer_irq);
  * @dev: pointer to the pcie_dev data structure
  *
  * Invoked when Root Port's AER service is loaded.
- **/
+ */
 static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev)
 {
        struct aer_rpc *rpc;
@@ -144,15 +251,11 @@ static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev)
        if (!rpc)
                return NULL;
 
-       /*
-        * Initialize Root lock access, e_lock, to Root Error Status Reg,
-        * Root Error ID Reg, and Root error producer/consumer index.
-        */
+       /* Initialize Root lock access, e_lock, to Root Error Status Reg */
        spin_lock_init(&rpc->e_lock);
 
        rpc->rpd = dev;
        INIT_WORK(&rpc->dpc_handler, aer_isr);
-       rpc->prod_idx = rpc->cons_idx = 0;
        mutex_init(&rpc->rpc_mutex);
        init_waitqueue_head(&rpc->wait_release);
 
@@ -167,7 +270,7 @@ static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev)
  * @dev: pointer to the pcie_dev data structure
  *
  * Invoked when PCI Express bus unloads or AER probe fails.
- **/
+ */
 static void aer_remove(struct pcie_device *dev)
 {
        struct aer_rpc *rpc = get_service_data(dev);
@@ -179,7 +282,8 @@ static void aer_remove(struct pcie_device *dev)
 
                wait_event(rpc->wait_release, rpc->prod_idx == rpc->cons_idx);
 
-               aer_delete_rootport(rpc);
+               aer_disable_rootport(rpc);
+               kfree(rpc);
                set_service_data(dev, NULL);
        }
 }
@@ -190,7 +294,7 @@ static void aer_remove(struct pcie_device *dev)
  * @id: pointer to the service id data structure
  *
  * Invoked when PCI Express bus loads AER service driver.
- **/
+ */
 static int __devinit aer_probe(struct pcie_device *dev)
 {
        int status;
@@ -230,47 +334,30 @@ static int __devinit aer_probe(struct pcie_device *dev)
  * @dev: pointer to Root Port's pci_dev data structure
  *
  * Invoked by Port Bus driver when performing link reset at Root Port.
- **/
+ */
 static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
 {
-       u16 p2p_ctrl;
-       u32 status;
+       u32 reg32;
        int pos;
 
        pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR);
 
        /* Disable Root's interrupt in response to error messages */
-       pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, 0);
-
-       /* Assert Secondary Bus Reset */
-       pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl);
-       p2p_ctrl |= PCI_BRIDGE_CTL_BUS_RESET;
-       pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
-
-       /*
-        * we should send hot reset message for 2ms to allow it time to
-        * propogate to all downstream ports
-        */
-       msleep(2);
-
-       /* De-assert Secondary Bus Reset */
-       p2p_ctrl &= ~PCI_BRIDGE_CTL_BUS_RESET;
-       pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
+       pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, &reg32);
+       reg32 &= ~ROOT_PORT_INTR_ON_MESG_MASK;
+       pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
 
-       /*
-        * System software must wait for at least 100ms from the end
-        * of a reset of one or more device before it is permitted
-        * to issue Configuration Requests to those devices.
-        */
-       msleep(200);
+       aer_do_secondary_bus_reset(dev);
        dev_printk(KERN_DEBUG, &dev->dev, "Root Port link has been reset\n");
 
+       /* Clear Root Error Status */
+       pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &reg32);
+       pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, reg32);
+
        /* Enable Root Port's interrupt in response to error messages */
-       pci_read_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, &status);
-       pci_write_config_dword(dev, pos + PCI_ERR_ROOT_STATUS, status);
-       pci_write_config_dword(dev,
-               pos + PCI_ERR_ROOT_COMMAND,
-               ROOT_PORT_INTR_ON_MESG_MASK);
+       pci_read_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, &reg32);
+       reg32 |= ROOT_PORT_INTR_ON_MESG_MASK;
+       pci_write_config_dword(dev, pos + PCI_ERR_ROOT_COMMAND, reg32);
 
        return PCI_ERS_RESULT_RECOVERED;
 }
@@ -281,7 +368,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev)
  * @error: error severity being notified by port bus
  *
  * Invoked by Port Bus driver during error recovery.
- **/
+ */
 static pci_ers_result_t aer_error_detected(struct pci_dev *dev,
                        enum pci_channel_state error)
 {
@@ -294,7 +381,7 @@ static pci_ers_result_t aer_error_detected(struct pci_dev *dev,
  * @dev: pointer to Root Port's pci_dev data structure
  *
  * Invoked by Port Bus driver during nonfatal recovery.
- **/
+ */
 static void aer_error_resume(struct pci_dev *dev)
 {
        int pos;
@@ -321,7 +408,7 @@ static void aer_error_resume(struct pci_dev *dev)
  * aer_service_init - register AER root service driver
  *
  * Invoked when AER root service driver is loaded.
- **/
+ */
 static int __init aer_service_init(void)
 {
        if (pcie_aer_disable)
@@ -335,7 +422,7 @@ static int __init aer_service_init(void)
  * aer_service_exit - unregister AER root service driver
  *
  * Invoked when AER root service driver is unloaded.
- **/
+ */
 static void __exit aer_service_exit(void)
 {
        pcie_port_service_unregister(&aerdriver);