/** * handle_error_source - handle logging error into an event log * @aerdev: pointer to pcie_device data structure of the root port * @dev: pointer to pci_dev data structure of error source device * @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) { pci_ers_result_t status = 0; int pos; if (info.severity == AER_CORRECTABLE) { /* * Correctable error does not need software intevention. * No need to go through error recovery process. */ pos = pci_find_aer_capability(dev); 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"); } else { /* TODO: Should kernel panic here? */ printk(KERN_DEBUG "AER driver didn't recover\n"); } } }
/** * disable_root_aer - 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 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 Root's interrupt in response to error messages */ pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_COMMAND, 0); /* Clear Root's error status reg */ pci_read_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, ®32); pci_write_config_dword(pdev, pos + PCI_ERR_ROOT_STATUS, reg32); }
int pci_cleanup_aer_correct_error_status(struct pci_dev *dev) { int pos; u32 status; pos = pci_find_aer_capability(dev); 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; }
/** * 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. **/ static irqreturn_t aer_irq(int irq, void *context) { unsigned int status, id; struct pcie_device *pdev = (struct pcie_device *)context; struct aer_rpc *rpc = get_service_data(pdev); int next_prod_idx; unsigned long flags; int pos; pos = pci_find_aer_capability(pdev->port); /* * Must lock access to Root Error Status Reg, Root Error ID Reg, * and Root error producer/consumer index */ spin_lock_irqsave(&rpc->e_lock, flags); /* Read error status */ pci_read_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, &status); if (!(status & ROOT_ERR_STATUS_MASKS)) { 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_write_config_dword(pdev->port, pos + PCI_ERR_ROOT_STATUS, status); /* Store error source for later DPC handler */ next_prod_idx = rpc->prod_idx + 1; if (next_prod_idx == AER_ERROR_SOURCES_MAX) next_prod_idx = 0; if (next_prod_idx == rpc->cons_idx) { /* * Error Storm Condition - possibly the same error occurred. * Drop the error. */ spin_unlock_irqrestore(&rpc->e_lock, flags); return IRQ_HANDLED; } rpc->e_sources[rpc->prod_idx].status = status; rpc->e_sources[rpc->prod_idx].id = id; rpc->prod_idx = next_prod_idx; spin_unlock_irqrestore(&rpc->e_lock, flags); /* Invoke DPC handler */ schedule_work(&rpc->dpc_handler); return IRQ_HANDLED; }
int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) { int pos; u32 status, mask; pos = pci_find_aer_capability(dev); if (!pos) return -EIO; pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask); if (dev->error_state == pci_channel_io_normal) status &= ~mask; /* Clear corresponding nonfatal bits */ else status &= mask; /* Clear corresponding fatal bits */ pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status); return 0; }
/** * 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. **/ 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_find_capability(pdev, PCI_CAP_ID_EXP); /* Clear PCIE Capability's Device Status */ pci_read_config_word(pdev, pos+PCI_EXP_DEVSTA, ®16); 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, ®16); reg16 &= ~(SYSTEM_ERROR_INTR_ON_MESG_MASK); pci_write_config_word(pdev, pos + PCI_EXP_RTCTL, reg16); aer_pos = pci_find_aer_capability(pdev); /* Clear error status */ pci_read_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, ®32); pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_STATUS, reg32); pci_read_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, ®32); pci_write_config_dword(pdev, aer_pos + PCI_ERR_COR_STATUS, reg32); pci_read_config_dword(pdev, aer_pos + PCI_ERR_UNCOR_STATUS, ®32); 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, ®16); 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 Root Port's interrupt in response to error messages */ pci_write_config_dword(pdev, aer_pos + PCI_ERR_ROOT_COMMAND, ROOT_PORT_INTR_ON_MESG_MASK); }
/** * aer_error_resume - clean up corresponding error status bits * @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; u32 status, mask; u16 reg16; /* Clean up Root device status */ pos = pci_find_capability(dev, PCI_CAP_ID_EXP); pci_read_config_word(dev, pos + PCI_EXP_DEVSTA, ®16); pci_write_config_word(dev, pos + PCI_EXP_DEVSTA, reg16); /* Clean AER Root Error Status */ pos = pci_find_aer_capability(dev); pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, &mask); if (dev->error_state == pci_channel_io_normal) status &= ~mask; /* Clear corresponding nonfatal bits */ else status &= mask; /* Clear corresponding fatal bits */ pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, status); }
static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) { int pos; pos = pci_find_aer_capability(dev); /* The device might not support AER */ if (!pos) return AER_SUCCESS; if (info->severity == AER_CORRECTABLE) { pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &info->status); if (!(info->status & ERR_CORRECTABLE_ERROR_MASK)) return AER_UNSUCCESS; } else if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE || info->severity == AER_NONFATAL) { /* Link is still healthy for IO reads */ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &info->status); if (!(info->status & ERR_UNCORRECTABLE_ERROR_MASK)) return AER_UNSUCCESS; if (info->status & AER_LOG_TLP_MASKS) { info->flags |= AER_TLP_HEADER_VALID_FLAG; pci_read_config_dword(dev, pos + PCI_ERR_HEADER_LOG, &info->tlp.dw0); pci_read_config_dword(dev, pos + PCI_ERR_HEADER_LOG + 4, &info->tlp.dw1); pci_read_config_dword(dev, pos + PCI_ERR_HEADER_LOG + 8, &info->tlp.dw2); pci_read_config_dword(dev, pos + PCI_ERR_HEADER_LOG + 12, &info->tlp.dw3); } } return AER_SUCCESS; }
/** * aer_root_reset - reset link on Root Port * @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; int pos; pos = pci_find_aer_capability(dev); /* 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_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); printk(KERN_DEBUG "Complete link reset at Root[%s]\n", dev->dev.bus_id); /* 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); return PCI_ERS_RESULT_RECOVERED; }