summaryrefslogtreecommitdiffstats
path: root/drivers/pci/pcie/aer/aerdrv.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/pci/pcie/aer/aerdrv.c')
-rw-r--r--drivers/pci/pcie/aer/aerdrv.c173
1 files changed, 133 insertions, 40 deletions
diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c
index aa495ad9bbd4..484cc55194b8 100644
--- a/drivers/pci/pcie/aer/aerdrv.c
+++ b/drivers/pci/pcie/aer/aerdrv.c
@@ -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,41 +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);
+ 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);
- /* Assert Secondary Bus Reset */
- pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &p2p_ctrl);
- p2p_ctrl |= PCI_CB_BRIDGE_CTL_CB_RESET;
- pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
-
- /* De-assert Secondary Bus Reset */
- p2p_ctrl &= ~PCI_CB_BRIDGE_CTL_CB_RESET;
- pci_write_config_word(dev, PCI_BRIDGE_CONTROL, p2p_ctrl);
-
- /*
- * 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;
}
@@ -275,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)
{
@@ -288,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;
@@ -315,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)
@@ -329,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);