[SCSI] qla2xxx: Added AER support for ISP82xx.
authorLalit Chandivade <lalit.chandivade@qlogic.com>
Fri, 3 Sep 2010 22:20:50 +0000 (15:20 -0700)
committerJames Bottomley <James.Bottomley@suse.de>
Sun, 5 Sep 2010 18:33:07 +0000 (15:33 -0300)
Signed-off-by: Giridhar Malavali <giridhar.malavali@qlogic.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
drivers/scsi/qla2xxx/qla_gbl.h
drivers/scsi/qla2xxx/qla_nx.c
drivers/scsi/qla2xxx/qla_os.c

index 1a1b281cea332c38ec71ca8f7d03be363c6efd85..4edfc731ea96abca0ab70434b6cfcb6b09a1e51a 100644 (file)
@@ -511,6 +511,7 @@ extern void qla82xx_reset_chip(struct scsi_qla_host *);
 extern void qla82xx_config_rings(struct scsi_qla_host *);
 extern int qla82xx_pinit_from_rom(scsi_qla_host_t *);
 extern void qla82xx_watchdog(scsi_qla_host_t *);
+extern int qla82xx_start_firmware(scsi_qla_host_t *);
 
 /* Firmware and flash related functions */
 extern int qla82xx_load_risc(scsi_qla_host_t *, uint32_t *);
@@ -538,6 +539,7 @@ extern void qla82xx_poll(int, void *);
 extern void qla82xx_init_flags(struct qla_hw_data *);
 
 /* ISP 8021 hardware related */
+extern void qla82xx_set_drv_active(scsi_qla_host_t *);
 extern int qla82xx_crb_win_lock(struct qla_hw_data *);
 extern void qla82xx_crb_win_unlock(struct qla_hw_data *);
 extern int qla82xx_pci_get_crb_addr_2M(struct qla_hw_data *, ulong *);
index 915b77a6e19390026134ff697cceaa89e9dd8c99..2ad91100a7e450019d89e5f8474fb7241f8eea81 100644 (file)
@@ -2257,7 +2257,7 @@ void qla82xx_init_flags(struct qla_hw_data *ha)
        ha->nx_legacy_intr.pci_int_reg = nx_legacy_intr->pci_int_reg;
 }
 
-static inline void
+inline void
 qla82xx_set_drv_active(scsi_qla_host_t *vha)
 {
        uint32_t drv_active;
@@ -2411,7 +2411,7 @@ fw_load_failed:
        return QLA_FUNCTION_FAILED;
 }
 
-static int
+int
 qla82xx_start_firmware(scsi_qla_host_t *vha)
 {
        int           pcie_cap;
@@ -3291,6 +3291,9 @@ qla82xx_check_fw_alive(scsi_qla_host_t *vha)
        struct qla_hw_data *ha = vha->hw;
 
        fw_heartbeat_counter = qla82xx_rd_32(ha, QLA82XX_PEG_ALIVE_COUNTER);
+       /* all 0xff, assume AER/EEH in progress, ignore */
+       if (fw_heartbeat_counter == 0xffffffff)
+               return;
        if (vha->fw_heartbeat_counter == fw_heartbeat_counter) {
                vha->seconds_since_last_heartbeat++;
                /* FW not alive after 2 seconds */
index 30b05229af26e5ad11d57c3d448e643d9cc0a896..cfa55827a101bedda4ac9a0a65b9c58289139b3d 100644 (file)
@@ -3526,6 +3526,11 @@ qla2x00_timer(scsi_qla_host_t *vha)
        struct qla_hw_data *ha = vha->hw;
        struct req_que *req;
 
+       if (ha->flags.eeh_busy) {
+               qla2x00_restart_timer(vha, WATCH_INTERVAL);
+               return;
+       }
+
        if (IS_QLA82XX(ha))
                qla82xx_watchdog(vha);
 
@@ -3755,6 +3760,17 @@ qla2xxx_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
                return PCI_ERS_RESULT_CAN_RECOVER;
        case pci_channel_io_frozen:
                ha->flags.eeh_busy = 1;
+               /* For ISP82XX complete any pending mailbox cmd */
+               if (IS_QLA82XX(ha)) {
+                       ha->flags.fw_hung = 1;
+                       if (ha->flags.mbox_busy) {
+                               ha->flags.mbox_int = 1;
+                               DEBUG2(qla_printk(KERN_ERR, ha,
+                                       "Due to pci channel io frozen, doing premature "
+                                       "completion of mbx command\n"));
+                               complete(&ha->mbx_intr_comp);
+                       }
+               }
                qla2x00_free_irqs(vha);
                pci_disable_device(pdev);
                return PCI_ERS_RESULT_NEED_RESET;
@@ -3803,6 +3819,109 @@ qla2xxx_pci_mmio_enabled(struct pci_dev *pdev)
                return PCI_ERS_RESULT_RECOVERED;
 }
 
+uint32_t qla82xx_error_recovery(scsi_qla_host_t *base_vha)
+{
+       uint32_t rval = QLA_FUNCTION_FAILED;
+       uint32_t drv_active = 0;
+       struct qla_hw_data *ha = base_vha->hw;
+       int fn;
+       struct pci_dev *other_pdev = NULL;
+
+       DEBUG17(qla_printk(KERN_INFO, ha,
+           "scsi(%ld): In qla82xx_error_recovery\n", base_vha->host_no));
+
+       set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+
+       if (base_vha->flags.online) {
+               /* Abort all outstanding commands,
+                * so as to be requeued later */
+               qla2x00_abort_isp_cleanup(base_vha);
+       }
+
+
+       fn = PCI_FUNC(ha->pdev->devfn);
+       while (fn > 0) {
+               fn--;
+               DEBUG17(qla_printk(KERN_INFO, ha,
+                   "Finding pci device at function = 0x%x\n", fn));
+               other_pdev =
+                   pci_get_domain_bus_and_slot(pci_domain_nr(ha->pdev->bus),
+                   ha->pdev->bus->number, PCI_DEVFN(PCI_SLOT(ha->pdev->devfn),
+                   fn));
+
+               if (!other_pdev)
+                       continue;
+               if (atomic_read(&other_pdev->enable_cnt)) {
+                       DEBUG17(qla_printk(KERN_INFO, ha,
+                           "Found PCI func availabe and enabled at 0x%x\n",
+                           fn));
+                       pci_dev_put(other_pdev);
+                       break;
+               }
+               pci_dev_put(other_pdev);
+       }
+
+       if (!fn) {
+               /* Reset owner */
+               DEBUG17(qla_printk(KERN_INFO, ha,
+                   "This devfn is reset owner = 0x%x\n", ha->pdev->devfn));
+               qla82xx_idc_lock(ha);
+
+               qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
+                   QLA82XX_DEV_INITIALIZING);
+
+               qla82xx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION,
+                   QLA82XX_IDC_VERSION);
+
+               drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
+               DEBUG17(qla_printk(KERN_INFO, ha,
+                   "drv_active = 0x%x\n", drv_active));
+
+               qla82xx_idc_unlock(ha);
+               /* Reset if device is not already reset
+                * drv_active would be 0 if a reset has already been done
+                */
+               if (drv_active)
+                       rval = qla82xx_start_firmware(base_vha);
+               else
+                       rval = QLA_SUCCESS;
+               qla82xx_idc_lock(ha);
+
+               if (rval != QLA_SUCCESS) {
+                       qla_printk(KERN_INFO, ha, "HW State: FAILED\n");
+                       qla82xx_clear_drv_active(ha);
+                       qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
+                           QLA82XX_DEV_FAILED);
+               } else {
+                       qla_printk(KERN_INFO, ha, "HW State: READY\n");
+                       qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
+                           QLA82XX_DEV_READY);
+                       qla82xx_idc_unlock(ha);
+                       ha->flags.fw_hung = 0;
+                       rval = qla82xx_restart_isp(base_vha);
+                       qla82xx_idc_lock(ha);
+                       /* Clear driver state register */
+                       qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, 0);
+                       qla82xx_set_drv_active(base_vha);
+               }
+               qla82xx_idc_unlock(ha);
+       } else {
+               DEBUG17(qla_printk(KERN_INFO, ha,
+                   "This devfn is not reset owner = 0x%x\n", ha->pdev->devfn));
+               if ((qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE) ==
+                   QLA82XX_DEV_READY)) {
+                       ha->flags.fw_hung = 0;
+                       rval = qla82xx_restart_isp(base_vha);
+                       qla82xx_idc_lock(ha);
+                       qla82xx_set_drv_active(base_vha);
+                       qla82xx_idc_unlock(ha);
+               }
+       }
+       clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+
+       return rval;
+}
+
 static pci_ers_result_t
 qla2xxx_pci_slot_reset(struct pci_dev *pdev)
 {
@@ -3835,15 +3954,23 @@ qla2xxx_pci_slot_reset(struct pci_dev *pdev)
        if (rc) {
                qla_printk(KERN_WARNING, ha,
                    "Can't re-enable PCI device after reset.\n");
-               return ret;
+               goto exit_slot_reset;
        }
 
        rsp = ha->rsp_q_map[0];
        if (qla2x00_request_irqs(ha, rsp))
-               return ret;
+               goto exit_slot_reset;
 
        if (ha->isp_ops->pci_config(base_vha))
-               return ret;
+               goto exit_slot_reset;
+
+       if (IS_QLA82XX(ha)) {
+               if (qla82xx_error_recovery(base_vha) == QLA_SUCCESS) {
+                       ret = PCI_ERS_RESULT_RECOVERED;
+                       goto exit_slot_reset;
+               } else
+                       goto exit_slot_reset;
+       }
 
        while (ha->flags.mbox_busy && retries--)
                msleep(1000);
@@ -3854,6 +3981,7 @@ qla2xxx_pci_slot_reset(struct pci_dev *pdev)
        clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
 
 
+exit_slot_reset:
        DEBUG17(qla_printk(KERN_WARNING, ha,
            "slot_reset-return:ret=%x\n", ret));