[SCSI] iscsi_transport: wait on session in error handler path
[linux-2.6.git] / drivers / scsi / qla4xxx / ql4_os.c
index 40e3caf..5529b2a 100644 (file)
@@ -5,6 +5,7 @@
  * See LICENSE.qla4xxx for copyright and licensing details.
  */
 #include <linux/moduleparam.h>
+#include <linux/slab.h>
 
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsicam.h>
@@ -29,22 +30,29 @@ static struct kmem_cache *srb_cachep;
  * Module parameter information and variables
  */
 int ql4xdiscoverywait = 60;
-module_param(ql4xdiscoverywait, int, S_IRUGO | S_IRUSR);
+module_param(ql4xdiscoverywait, int, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(ql4xdiscoverywait, "Discovery wait time");
+
 int ql4xdontresethba = 0;
-module_param(ql4xdontresethba, int, S_IRUGO | S_IRUSR);
+module_param(ql4xdontresethba, int, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(ql4xdontresethba,
-                "Dont reset the HBA when the driver gets 0x8002 AEN "
-                " default it will reset hba :0"
-                " set to 1 to avoid resetting HBA");
+               "Don't reset the HBA for driver recovery \n"
+               " 0 - It will reset HBA (Default)\n"
+               " 1 - It will NOT reset HBA");
 
 int ql4xextended_error_logging = 0; /* 0 = off, 1 = log errors */
-module_param(ql4xextended_error_logging, int, S_IRUGO | S_IRUSR);
+module_param(ql4xextended_error_logging, int, S_IRUGO | S_IWUSR);
 MODULE_PARM_DESC(ql4xextended_error_logging,
                 "Option to enable extended error logging, "
                 "Default is 0 - no logging, 1 - debug logging");
 
-int ql4_mod_unload = 0;
+int ql4xenablemsix = 1;
+module_param(ql4xenablemsix, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(ql4xenablemsix,
+               "Set to enable MSI or MSI-X interrupt mechanism.\n"
+               " 0 = enable INTx interrupt mechanism.\n"
+               " 1 = enable MSI-X interrupt mechanism (Default).\n"
+               " 2 = enable MSI interrupt mechanism.");
 
 #define QL4_DEF_QDEPTH 32
 
@@ -73,6 +81,7 @@ static enum blk_eh_timer_return qla4xxx_eh_cmd_timed_out(struct scsi_cmnd *sc);
  */
 static int qla4xxx_queuecommand(struct scsi_cmnd *cmd,
                                void (*done) (struct scsi_cmnd *));
+static int qla4xxx_eh_abort(struct scsi_cmnd *cmd);
 static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd);
 static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd);
 static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd);
@@ -81,12 +90,16 @@ static int qla4xxx_slave_configure(struct scsi_device *device);
 static void qla4xxx_slave_destroy(struct scsi_device *sdev);
 static void qla4xxx_scan_start(struct Scsi_Host *shost);
 
+static struct qla4_8xxx_legacy_intr_set legacy_intr[] =
+    QLA82XX_LEGACY_INTR_CONFIG;
+
 static struct scsi_host_template qla4xxx_driver_template = {
        .module                 = THIS_MODULE,
        .name                   = DRIVER_NAME,
        .proc_name              = DRIVER_NAME,
        .queuecommand           = qla4xxx_queuecommand,
 
+       .eh_abort_handler       = qla4xxx_eh_abort,
        .eh_device_reset_handler = qla4xxx_eh_device_reset,
        .eh_target_reset_handler = qla4xxx_eh_target_reset,
        .eh_host_reset_handler  = qla4xxx_eh_host_reset,
@@ -113,7 +126,8 @@ static struct iscsi_transport qla4xxx_iscsi_transport = {
        .caps                   = CAP_FW_DB | CAP_SENDTARGETS_OFFLOAD |
                                  CAP_DATA_PATH_OFFLOAD,
        .param_mask             = ISCSI_CONN_PORT | ISCSI_CONN_ADDRESS |
-                                 ISCSI_TARGET_NAME | ISCSI_TPGT,
+                                 ISCSI_TARGET_NAME | ISCSI_TPGT |
+                                 ISCSI_TARGET_ALIAS,
        .host_param_mask        = ISCSI_HOST_HWADDRESS |
                                  ISCSI_HOST_IPADDRESS |
                                  ISCSI_HOST_INITIATOR_NAME,
@@ -149,15 +163,12 @@ static void qla4xxx_recovery_timedout(struct iscsi_cls_session *session)
        if (atomic_read(&ddb_entry->state) != DDB_STATE_ONLINE) {
                atomic_set(&ddb_entry->state, DDB_STATE_DEAD);
 
-               DEBUG2(printk("scsi%ld: %s: index [%d] port down retry count "
+               DEBUG2(printk("scsi%ld: %s: ddb [%d] port down retry count "
                              "of (%d) secs exhausted, marking device DEAD.\n",
                              ha->host_no, __func__, ddb_entry->fw_ddb_index,
                              ha->port_down_retry_count));
 
-               DEBUG2(printk("scsi%ld: %s: scheduling dpc routine - dpc "
-                             "flags = 0x%lx\n",
-                             ha->host_no, __func__, ha->dpc_flags));
-               queue_work(ha->dpc_thread, &ha->dpc_work);
+               qla4xxx_wake_dpc(ha);
        }
 }
 
@@ -200,6 +211,10 @@ static int qla4xxx_sess_get_param(struct iscsi_cls_session *sess,
        case ISCSI_PARAM_TPGT:
                len = sprintf(buf, "%u\n", ddb_entry->tpgt);
                break;
+       case ISCSI_PARAM_TARGET_ALIAS:
+               len = snprintf(buf, PAGE_SIZE - 1, "%s\n",
+                   ddb_entry->iscsi_alias);
+               break;
        default:
                return -ENOSYS;
        }
@@ -359,19 +374,37 @@ static void qla4xxx_stop_timer(struct scsi_qla_host *ha)
  * @ha: Pointer to host adapter structure.
  * @ddb_entry: Pointer to device database entry
  *
- * This routine marks a device missing and resets the relogin retry count.
+ * This routine marks a device missing and close connection.
  **/
 void qla4xxx_mark_device_missing(struct scsi_qla_host *ha,
                                 struct ddb_entry *ddb_entry)
 {
-       atomic_set(&ddb_entry->state, DDB_STATE_MISSING);
-       DEBUG3(printk("scsi%d:%d:%d: index [%d] marked MISSING\n",
-                     ha->host_no, ddb_entry->bus, ddb_entry->target,
-                     ddb_entry->fw_ddb_index));
+       if ((atomic_read(&ddb_entry->state) != DDB_STATE_DEAD)) {
+               atomic_set(&ddb_entry->state, DDB_STATE_MISSING);
+               DEBUG2(printk("scsi%ld: ddb [%d] marked MISSING\n",
+                   ha->host_no, ddb_entry->fw_ddb_index));
+       } else
+               DEBUG2(printk("scsi%ld: ddb [%d] DEAD\n", ha->host_no,
+                   ddb_entry->fw_ddb_index))
+
        iscsi_block_session(ddb_entry->sess);
        iscsi_conn_error_event(ddb_entry->conn, ISCSI_ERR_CONN_FAILED);
 }
 
+/**
+ * qla4xxx_mark_all_devices_missing - mark all devices as missing.
+ * @ha: Pointer to host adapter structure.
+ *
+ * This routine marks a device missing and resets the relogin retry count.
+ **/
+void qla4xxx_mark_all_devices_missing(struct scsi_qla_host *ha)
+{
+       struct ddb_entry *ddb_entry, *ddbtemp;
+       list_for_each_entry_safe(ddb_entry, ddbtemp, &ha->ddb_list, list) {
+               qla4xxx_mark_device_missing(ha, ddb_entry);
+       }
+}
+
 static struct srb* qla4xxx_get_new_srb(struct scsi_qla_host *ha,
                                       struct ddb_entry *ddb_entry,
                                       struct scsi_cmnd *cmd,
@@ -383,12 +416,12 @@ static struct srb* qla4xxx_get_new_srb(struct scsi_qla_host *ha,
        if (!srb)
                return srb;
 
-       atomic_set(&srb->ref_count, 1);
+       kref_init(&srb->srb_ref);
        srb->ha = ha;
        srb->ddb = ddb_entry;
        srb->cmd = cmd;
        srb->flags = 0;
-       cmd->SCp.ptr = (void *)srb;
+       CMD_SP(cmd) = (void *)srb;
        cmd->scsi_done = done;
 
        return srb;
@@ -402,12 +435,14 @@ static void qla4xxx_srb_free_dma(struct scsi_qla_host *ha, struct srb *srb)
                scsi_dma_unmap(cmd);
                srb->flags &= ~SRB_DMA_VALID;
        }
-       cmd->SCp.ptr = NULL;
+       CMD_SP(cmd) = NULL;
 }
 
-void qla4xxx_srb_compl(struct scsi_qla_host *ha, struct srb *srb)
+void qla4xxx_srb_compl(struct kref *ref)
 {
+       struct srb *srb = container_of(ref, struct srb, srb_ref);
        struct scsi_cmnd *cmd = srb->cmd;
+       struct scsi_qla_host *ha = srb->ha;
 
        qla4xxx_srb_free_dma(ha, srb);
 
@@ -458,7 +493,13 @@ static int qla4xxx_queuecommand(struct scsi_cmnd *cmd,
                return SCSI_MLQUEUE_TARGET_BUSY;
        }
 
-       if (test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags))
+       if (test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags) ||
+           test_bit(DPC_RESET_ACTIVE, &ha->dpc_flags) ||
+           test_bit(DPC_RESET_HA, &ha->dpc_flags) ||
+           test_bit(DPC_HA_UNRECOVERABLE, &ha->dpc_flags) ||
+           test_bit(DPC_HA_NEED_QUIESCENT, &ha->dpc_flags) ||
+           !test_bit(AF_ONLINE, &ha->flags) ||
+           test_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags))
                goto qc_host_busy;
 
        spin_unlock_irq(ha->host->host_lock);
@@ -519,7 +560,15 @@ static void qla4xxx_mem_free(struct scsi_qla_host *ha)
        ha->srb_mempool = NULL;
 
        /* release io space registers  */
-       if (ha->reg)
+       if (is_qla8022(ha)) {
+               if (ha->nx_pcibase)
+                       iounmap(
+                           (struct device_reg_82xx __iomem *)ha->nx_pcibase);
+
+               if (ha->nx_db_wr_ptr)
+                       iounmap(
+                           (struct device_reg_82xx __iomem *)ha->nx_db_wr_ptr);
+       } else if (ha->reg)
                iounmap(ha->reg);
        pci_release_regions(ha->pdev);
 }
@@ -544,8 +593,8 @@ static int qla4xxx_mem_alloc(struct scsi_qla_host *ha)
        ha->queues = dma_alloc_coherent(&ha->pdev->dev, ha->queues_len,
                                        &ha->queues_dma, GFP_KERNEL);
        if (ha->queues == NULL) {
-               dev_warn(&ha->pdev->dev,
-                       "Memory Allocation failed - queues.\n");
+               ql4_printk(KERN_WARNING, ha,
+                   "Memory Allocation failed - queues.\n");
 
                goto mem_alloc_error_exit;
        }
@@ -581,8 +630,8 @@ static int qla4xxx_mem_alloc(struct scsi_qla_host *ha)
        ha->srb_mempool = mempool_create(SRB_MIN_REQ, mempool_alloc_slab,
                                         mempool_free_slab, srb_cachep);
        if (ha->srb_mempool == NULL) {
-               dev_warn(&ha->pdev->dev,
-                       "Memory Allocation failed - SRB Pool.\n");
+               ql4_printk(KERN_WARNING, ha,
+                   "Memory Allocation failed - SRB Pool.\n");
 
                goto mem_alloc_error_exit;
        }
@@ -595,6 +644,74 @@ mem_alloc_error_exit:
 }
 
 /**
+ * qla4_8xxx_check_fw_alive  - Check firmware health
+ * @ha: Pointer to host adapter structure.
+ *
+ * Context: Interrupt
+ **/
+static void qla4_8xxx_check_fw_alive(struct scsi_qla_host *ha)
+{
+       uint32_t fw_heartbeat_counter, halt_status;
+
+       fw_heartbeat_counter = qla4_8xxx_rd_32(ha, QLA82XX_PEG_ALIVE_COUNTER);
+
+       if (ha->fw_heartbeat_counter == fw_heartbeat_counter) {
+               ha->seconds_since_last_heartbeat++;
+               /* FW not alive after 2 seconds */
+               if (ha->seconds_since_last_heartbeat == 2) {
+                       ha->seconds_since_last_heartbeat = 0;
+                       halt_status = qla4_8xxx_rd_32(ha,
+                           QLA82XX_PEG_HALT_STATUS1);
+                       /* Since we cannot change dev_state in interrupt
+                        * context, set appropriate DPC flag then wakeup
+                        * DPC */
+                       if (halt_status & HALT_STATUS_UNRECOVERABLE)
+                               set_bit(DPC_HA_UNRECOVERABLE, &ha->dpc_flags);
+                       else {
+                               printk("scsi%ld: %s: detect abort needed!\n",
+                                   ha->host_no, __func__);
+                               set_bit(DPC_RESET_HA, &ha->dpc_flags);
+                       }
+                       qla4xxx_wake_dpc(ha);
+               }
+       }
+       ha->fw_heartbeat_counter = fw_heartbeat_counter;
+}
+
+/**
+ * qla4_8xxx_watchdog - Poll dev state
+ * @ha: Pointer to host adapter structure.
+ *
+ * Context: Interrupt
+ **/
+void qla4_8xxx_watchdog(struct scsi_qla_host *ha)
+{
+       uint32_t dev_state;
+
+       dev_state = qla4_8xxx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
+
+       /* don't poll if reset is going on */
+       if (!test_bit(DPC_RESET_ACTIVE, &ha->dpc_flags)) {
+               if (dev_state == QLA82XX_DEV_NEED_RESET &&
+                   !test_bit(DPC_RESET_HA, &ha->dpc_flags)) {
+                       printk("scsi%ld: %s: HW State: NEED RESET!\n",
+                           ha->host_no, __func__);
+                       set_bit(DPC_RESET_HA, &ha->dpc_flags);
+                       qla4xxx_wake_dpc(ha);
+               } else if (dev_state == QLA82XX_DEV_NEED_QUIESCENT &&
+                   !test_bit(DPC_HA_NEED_QUIESCENT, &ha->dpc_flags)) {
+                       printk("scsi%ld: %s: HW State: NEED QUIES!\n",
+                           ha->host_no, __func__);
+                       set_bit(DPC_HA_NEED_QUIESCENT, &ha->dpc_flags);
+                       qla4xxx_wake_dpc(ha);
+               } else  {
+                       /* Check firmware health */
+                       qla4_8xxx_check_fw_alive(ha);
+               }
+       }
+}
+
+/**
  * qla4xxx_timer - checks every second for work to do.
  * @ha: Pointer to host adapter structure.
  **/
@@ -603,6 +720,16 @@ static void qla4xxx_timer(struct scsi_qla_host *ha)
        struct ddb_entry *ddb_entry, *dtemp;
        int start_dpc = 0;
 
+       if (test_bit(AF_HBA_GOING_AWAY, &ha->flags)) {
+               DEBUG2(ql4_printk(KERN_INFO, ha, "%s exited. HBA GOING AWAY\n",
+                   __func__));
+               return;
+       }
+
+       if (is_qla8022(ha)) {
+               qla4_8xxx_watchdog(ha);
+       }
+
        /* Search for relogin's to time-out and port down retry. */
        list_for_each_entry_safe(ddb_entry, dtemp, &ha->ddb_list, list) {
                /* Count down time between sending relogins */
@@ -619,7 +746,7 @@ static void qla4xxx_timer(struct scsi_qla_host *ha)
                                        set_bit(DPC_RELOGIN_DEVICE,
                                                &ha->dpc_flags);
                                        set_bit(DF_RELOGIN, &ddb_entry->flags);
-                                       DEBUG2(printk("scsi%ld: %s: index [%d]"
+                                       DEBUG2(printk("scsi%ld: %s: ddb [%d]"
                                                      " login device\n",
                                                      ha->host_no, __func__,
                                                      ddb_entry->fw_ddb_index));
@@ -642,7 +769,7 @@ static void qla4xxx_timer(struct scsi_qla_host *ha)
                            DDB_DS_SESSION_FAILED) {
                                /* Reset retry relogin timer */
                                atomic_inc(&ddb_entry->relogin_retry_count);
-                               DEBUG2(printk("scsi%ld: index[%d] relogin"
+                               DEBUG2(printk("scsi%ld: ddb [%d] relogin"
                                              " timed out-retrying"
                                              " relogin (%d)\n",
                                              ha->host_no,
@@ -651,7 +778,7 @@ static void qla4xxx_timer(struct scsi_qla_host *ha)
                                                          relogin_retry_count))
                                        );
                                start_dpc++;
-                               DEBUG(printk("scsi%ld:%d:%d: index [%d] "
+                               DEBUG(printk("scsi%ld:%d:%d: ddb [%d] "
                                             "initate relogin after"
                                             " %d seconds\n",
                                             ha->host_no, ddb_entry->bus,
@@ -666,30 +793,35 @@ static void qla4xxx_timer(struct scsi_qla_host *ha)
                }
        }
 
-       /* Check for heartbeat interval. */
-       if (ha->firmware_options & FWOPT_HEARTBEAT_ENABLE &&
-           ha->heartbeat_interval != 0) {
-               ha->seconds_since_last_heartbeat++;
-               if (ha->seconds_since_last_heartbeat >
-                   ha->heartbeat_interval + 2)
-                       set_bit(DPC_RESET_HA, &ha->dpc_flags);
+       if (!is_qla8022(ha)) {
+               /* Check for heartbeat interval. */
+               if (ha->firmware_options & FWOPT_HEARTBEAT_ENABLE &&
+                   ha->heartbeat_interval != 0) {
+                       ha->seconds_since_last_heartbeat++;
+                       if (ha->seconds_since_last_heartbeat >
+                           ha->heartbeat_interval + 2)
+                               set_bit(DPC_RESET_HA, &ha->dpc_flags);
+               }
        }
 
-
        /* Wakeup the dpc routine for this adapter, if needed. */
        if ((start_dpc ||
             test_bit(DPC_RESET_HA, &ha->dpc_flags) ||
             test_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags) ||
             test_bit(DPC_RELOGIN_DEVICE, &ha->dpc_flags) ||
-            test_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags) ||
+            test_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags) ||
             test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags) ||
             test_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags) ||
+            test_bit(DPC_LINK_CHANGED, &ha->dpc_flags) ||
+            test_bit(DPC_HA_UNRECOVERABLE, &ha->dpc_flags) ||
+            test_bit(DPC_HA_NEED_QUIESCENT, &ha->dpc_flags) ||
             test_bit(DPC_AEN, &ha->dpc_flags)) &&
+            !test_bit(AF_DPC_SCHEDULED, &ha->flags) &&
             ha->dpc_thread) {
                DEBUG2(printk("scsi%ld: %s: scheduling dpc routine"
                              " - dpc flags = 0x%lx\n",
                              ha->host_no, __func__, ha->dpc_flags));
-               queue_work(ha->dpc_thread, &ha->dpc_work);
+               qla4xxx_wake_dpc(ha);
        }
 
        /* Reschedule timer thread to call us back in one second */
@@ -708,16 +840,15 @@ static void qla4xxx_timer(struct scsi_qla_host *ha)
 static int qla4xxx_cmd_wait(struct scsi_qla_host *ha)
 {
        uint32_t index = 0;
-       int stat = QLA_SUCCESS;
        unsigned long flags;
        struct scsi_cmnd *cmd;
-       int wait_cnt = WAIT_CMD_TOV;    /*
-                                        * Initialized for 30 seconds as we
-                                        * expect all commands to retuned
-                                        * ASAP.
-                                        */
 
-       while (wait_cnt) {
+       unsigned long wtime = jiffies + (WAIT_CMD_TOV * HZ);
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "Wait up to %d seconds for cmds to "
+           "complete\n", WAIT_CMD_TOV));
+
+       while (!time_after_eq(jiffies, wtime)) {
                spin_lock_irqsave(&ha->hardware_lock, flags);
                /* Find a command that hasn't completed. */
                for (index = 0; index < ha->host->can_queue; index++) {
@@ -728,31 +859,26 @@ static int qla4xxx_cmd_wait(struct scsi_qla_host *ha)
                spin_unlock_irqrestore(&ha->hardware_lock, flags);
 
                /* If No Commands are pending, wait is complete */
-               if (index == ha->host->can_queue) {
-                       break;
-               }
-
-               /* If we timed out on waiting for commands to come back
-                * return ERROR.
-                */
-               wait_cnt--;
-               if (wait_cnt == 0)
-                       stat = QLA_ERROR;
-               else {
-                       msleep(1000);
-               }
-       }                       /* End of While (wait_cnt) */
+               if (index == ha->host->can_queue)
+                       return QLA_SUCCESS;
 
-       return stat;
+               msleep(1000);
+       }
+       /* If we timed out on waiting for commands to come back
+        * return ERROR. */
+       return QLA_ERROR;
 }
 
-void qla4xxx_hw_reset(struct scsi_qla_host *ha)
+int qla4xxx_hw_reset(struct scsi_qla_host *ha)
 {
        uint32_t ctrl_status;
        unsigned long flags = 0;
 
        DEBUG2(printk(KERN_ERR "scsi%ld: %s\n", ha->host_no, __func__));
 
+       if (ql4xxx_lock_drvr_wait(ha) != QLA_SUCCESS)
+               return QLA_ERROR;
+
        spin_lock_irqsave(&ha->hardware_lock, flags);
 
        /*
@@ -768,6 +894,7 @@ void qla4xxx_hw_reset(struct scsi_qla_host *ha)
        readl(&ha->reg->ctrl_status);
 
        spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       return QLA_SUCCESS;
 }
 
 /**
@@ -866,15 +993,16 @@ int qla4xxx_soft_reset(struct scsi_qla_host *ha)
 }
 
 /**
- * qla4xxx_flush_active_srbs - returns all outstanding i/o requests to O.S.
+ * qla4xxx_abort_active_cmds - returns all outstanding i/o requests to O.S.
  * @ha: Pointer to host adapter structure.
+ * @res: returned scsi status
  *
  * This routine is called just prior to a HARD RESET to return all
  * outstanding commands back to the Operating System.
  * Caller should make sure that the following locks are released
  * before this calling routine: Hardware lock, and io_request_lock.
  **/
-static void qla4xxx_flush_active_srbs(struct scsi_qla_host *ha)
+static void qla4xxx_abort_active_cmds(struct scsi_qla_host *ha, int res)
 {
        struct srb *srb;
        int i;
@@ -884,75 +1012,116 @@ static void qla4xxx_flush_active_srbs(struct scsi_qla_host *ha)
        for (i = 0; i < ha->host->can_queue; i++) {
                srb = qla4xxx_del_from_active_array(ha, i);
                if (srb != NULL) {
-                       srb->cmd->result = DID_RESET << 16;
-                       qla4xxx_srb_compl(ha, srb);
+                       srb->cmd->result = res;
+                       kref_put(&srb->srb_ref, qla4xxx_srb_compl);
                }
        }
        spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+void qla4xxx_dead_adapter_cleanup(struct scsi_qla_host *ha)
+{
+       clear_bit(AF_ONLINE, &ha->flags);
 
+       /* Disable the board */
+       ql4_printk(KERN_INFO, ha, "Disabling the board\n");
+       set_bit(AF_HBA_GOING_AWAY, &ha->flags);
+
+       qla4xxx_abort_active_cmds(ha, DID_NO_CONNECT << 16);
+       qla4xxx_mark_all_devices_missing(ha);
+       clear_bit(AF_INIT_DONE, &ha->flags);
 }
 
 /**
  * qla4xxx_recover_adapter - recovers adapter after a fatal error
  * @ha: Pointer to host adapter structure.
- * @renew_ddb_list: Indicates what to do with the adapter's ddb list
- *
- * renew_ddb_list value can be 0=preserve ddb list, 1=destroy and rebuild
- * ddb list.
  **/
-static int qla4xxx_recover_adapter(struct scsi_qla_host *ha,
-                               uint8_t renew_ddb_list)
+static int qla4xxx_recover_adapter(struct scsi_qla_host *ha)
 {
-       int status;
+       int status = QLA_ERROR;
+       uint8_t reset_chip = 0;
 
        /* Stall incoming I/O until we are done */
+       scsi_block_requests(ha->host);
        clear_bit(AF_ONLINE, &ha->flags);
 
-       DEBUG2(printk("scsi%ld: %s calling qla4xxx_cmd_wait\n", ha->host_no,
-                     __func__));
+       DEBUG2(ql4_printk(KERN_INFO, ha, "%s: adapter OFFLINE\n", __func__));
 
-       /* Wait for outstanding commands to complete.
-        * Stalls the driver for max 30 secs
-        */
-       status = qla4xxx_cmd_wait(ha);
+       set_bit(DPC_RESET_ACTIVE, &ha->dpc_flags);
 
-       qla4xxx_disable_intrs(ha);
+       if (test_bit(DPC_RESET_HA, &ha->dpc_flags))
+               reset_chip = 1;
 
-       /* Flush any pending ddb changed AENs */
-       qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS);
+       /* For the DPC_RESET_HA_INTR case (ISP-4xxx specific)
+        * do not reset adapter, jump to initialize_adapter */
+       if (test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags)) {
+               status = QLA_SUCCESS;
+               goto recover_ha_init_adapter;
+       }
 
-       qla4xxx_flush_active_srbs(ha);
+       /* For the ISP-82xx adapter, issue a stop_firmware if invoked
+        * from eh_host_reset or ioctl module */
+       if (is_qla8022(ha) && !reset_chip &&
+           test_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags)) {
+
+               DEBUG2(ql4_printk(KERN_INFO, ha,
+                   "scsi%ld: %s - Performing stop_firmware...\n",
+                   ha->host_no, __func__));
+               status = ha->isp_ops->reset_firmware(ha);
+               if (status == QLA_SUCCESS) {
+                       qla4xxx_cmd_wait(ha);
+                       ha->isp_ops->disable_intrs(ha);
+                       qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS);
+                       qla4xxx_abort_active_cmds(ha, DID_RESET << 16);
+               } else {
+                       /* If the stop_firmware fails then
+                        * reset the entire chip */
+                       reset_chip = 1;
+                       clear_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags);
+                       set_bit(DPC_RESET_HA, &ha->dpc_flags);
+               }
+       }
 
-       /* Reset the firmware.  If successful, function
-        * returns with ISP interrupts enabled.
-        */
-       DEBUG2(printk("scsi%ld: %s - Performing soft reset..\n",
-                     ha->host_no, __func__));
-       if (ql4xxx_lock_drvr_wait(ha) == QLA_SUCCESS)
-               status = qla4xxx_soft_reset(ha);
-       else
-               status = QLA_ERROR;
+       /* Issue full chip reset if recovering from a catastrophic error,
+        * or if stop_firmware fails for ISP-82xx.
+        * This is the default case for ISP-4xxx */
+       if (!is_qla8022(ha) || reset_chip) {
+               qla4xxx_cmd_wait(ha);
+               qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS);
+               qla4xxx_abort_active_cmds(ha, DID_RESET << 16);
+               DEBUG2(ql4_printk(KERN_INFO, ha,
+                   "scsi%ld: %s - Performing chip reset..\n",
+                   ha->host_no, __func__));
+               status = ha->isp_ops->reset_chip(ha);
+       }
 
        /* Flush any pending ddb changed AENs */
        qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS);
 
-       /* Re-initialize firmware. If successful, function returns
-        * with ISP interrupts enabled */
+recover_ha_init_adapter:
+       /* Upon successful firmware/chip reset, re-initialize the adapter */
        if (status == QLA_SUCCESS) {
-               DEBUG2(printk("scsi%ld: %s - Initializing adapter..\n",
-                             ha->host_no, __func__));
-
-               /* If successful, AF_ONLINE flag set in
-                * qla4xxx_initialize_adapter */
-               status = qla4xxx_initialize_adapter(ha, renew_ddb_list);
+               /* For ISP-4xxx, force function 1 to always initialize
+                * before function 3 to prevent both funcions from
+                * stepping on top of the other */
+               if (!is_qla8022(ha) && (ha->mac_index == 3))
+                       ssleep(6);
+
+               /* NOTE: AF_ONLINE flag set upon successful completion of
+                *       qla4xxx_initialize_adapter */
+               status = qla4xxx_initialize_adapter(ha, PRESERVE_DDB_LIST);
        }
 
-       /* Failed adapter initialization?
-        * Retry reset_ha only if invoked via DPC (DPC_RESET_HA) */
-       if ((test_bit(AF_ONLINE, &ha->flags) == 0) &&
-           (test_bit(DPC_RESET_HA, &ha->dpc_flags))) {
+       /* Retry failed adapter initialization, if necessary
+        * Do not retry initialize_adapter for RESET_HA_INTR (ISP-4xxx specific)
+        * case to prevent ping-pong resets between functions */
+       if (!test_bit(AF_ONLINE, &ha->flags) &&
+           !test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags)) {
                /* Adapter initialization failed, see if we can retry
-                * resetting the ha */
+                * resetting the ha.
+                * Since we don't want to block the DPC for too long
+                * with multiple resets in the same thread,
+                * utilize DPC to retry */
                if (!test_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags)) {
                        ha->retry_reset_ha_cnt = MAX_RESET_HA_RETRIES;
                        DEBUG2(printk("scsi%ld: recover adapter - retrying "
@@ -977,29 +1146,43 @@ static int qla4xxx_recover_adapter(struct scsi_qla_host *ha,
                                DEBUG2(printk("scsi%ld: recover adapter "
                                              "failed - board disabled\n",
                                              ha->host_no));
-                               qla4xxx_flush_active_srbs(ha);
+                               qla4xxx_dead_adapter_cleanup(ha);
                                clear_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags);
                                clear_bit(DPC_RESET_HA, &ha->dpc_flags);
-                               clear_bit(DPC_RESET_HA_DESTROY_DDB_LIST,
+                               clear_bit(DPC_RESET_HA_FW_CONTEXT,
                                          &ha->dpc_flags);
                                status = QLA_ERROR;
                        }
                }
        } else {
                clear_bit(DPC_RESET_HA, &ha->dpc_flags);
-               clear_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags);
+               clear_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags);
                clear_bit(DPC_RETRY_RESET_HA, &ha->dpc_flags);
        }
 
        ha->adapter_error_count++;
 
-       if (status == QLA_SUCCESS)
-               qla4xxx_enable_intrs(ha);
+       if (test_bit(AF_ONLINE, &ha->flags))
+               ha->isp_ops->enable_intrs(ha);
+
+       scsi_unblock_requests(ha->host);
+
+       clear_bit(DPC_RESET_ACTIVE, &ha->dpc_flags);
+       DEBUG2(printk("scsi%ld: recover adapter: %s\n", ha->host_no,
+           status == QLA_ERROR ? "FAILED" : "SUCCEDED"));
 
-       DEBUG2(printk("scsi%ld: recover adapter .. DONE\n", ha->host_no));
        return status;
 }
 
+void qla4xxx_wake_dpc(struct scsi_qla_host *ha)
+{
+       if (ha->dpc_thread &&
+           !test_bit(AF_DPC_SCHEDULED, &ha->flags)) {
+               set_bit(AF_DPC_SCHEDULED, &ha->flags);
+               queue_work(ha->dpc_thread, &ha->dpc_work);
+       }
+}
+
 /**
  * qla4xxx_do_dpc - dpc routine
  * @data: in our case pointer to adapter structure
@@ -1019,21 +1202,47 @@ static void qla4xxx_do_dpc(struct work_struct *work)
        int status = QLA_ERROR;
 
        DEBUG2(printk("scsi%ld: %s: DPC handler waking up."
-               "flags = 0x%08lx, dpc_flags = 0x%08lx ctrl_stat = 0x%08x\n",
-               ha->host_no, __func__, ha->flags, ha->dpc_flags,
-               readw(&ha->reg->ctrl_status)));
+           "flags = 0x%08lx, dpc_flags = 0x%08lx\n",
+           ha->host_no, __func__, ha->flags, ha->dpc_flags))
 
        /* Initialization not yet finished. Don't do anything yet. */
        if (!test_bit(AF_INIT_DONE, &ha->flags))
                return;
 
-       if (adapter_up(ha) ||
-           test_bit(DPC_RESET_HA, &ha->dpc_flags) ||
+       /* HBA is in the process of being permanently disabled.
+        * Don't process anything */
+       if (test_bit(AF_HBA_GOING_AWAY, &ha->flags))
+               return;
+
+       if (is_qla8022(ha)) {
+               if (test_bit(DPC_HA_UNRECOVERABLE, &ha->dpc_flags)) {
+                       qla4_8xxx_idc_lock(ha);
+                       qla4_8xxx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
+                           QLA82XX_DEV_FAILED);
+                       qla4_8xxx_idc_unlock(ha);
+                       ql4_printk(KERN_INFO, ha, "HW State: FAILED\n");
+                       qla4_8xxx_device_state_handler(ha);
+               }
+               if (test_and_clear_bit(DPC_HA_NEED_QUIESCENT, &ha->dpc_flags)) {
+                       qla4_8xxx_need_qsnt_handler(ha);
+               }
+       }
+
+       if (!test_bit(DPC_RESET_ACTIVE, &ha->dpc_flags) &&
+           (test_bit(DPC_RESET_HA, &ha->dpc_flags) ||
            test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags) ||
-           test_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags)) {
-               if (test_bit(DPC_RESET_HA_DESTROY_DDB_LIST, &ha->dpc_flags) ||
-                       test_bit(DPC_RESET_HA, &ha->dpc_flags))
-                       qla4xxx_recover_adapter(ha, PRESERVE_DDB_LIST);
+           test_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags))) {
+               if (ql4xdontresethba) {
+                       DEBUG2(printk("scsi%ld: %s: Don't Reset HBA\n",
+                           ha->host_no, __func__));
+                       clear_bit(DPC_RESET_HA, &ha->dpc_flags);
+                       clear_bit(DPC_RESET_HA_INTR, &ha->dpc_flags);
+                       clear_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags);
+                       goto dpc_post_reset_ha;
+               }
+               if (test_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags) ||
+                   test_bit(DPC_RESET_HA, &ha->dpc_flags))
+                       qla4xxx_recover_adapter(ha);
 
                if (test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags)) {
                        uint8_t wait_time = RESET_INTR_TOV;
@@ -1048,18 +1257,18 @@ static void qla4xxx_do_dpc(struct work_struct *work)
                                DEBUG2(printk("scsi%ld: %s: SR|FSR "
                                              "bit not cleared-- resetting\n",
                                              ha->host_no, __func__));
-                       qla4xxx_flush_active_srbs(ha);
+                       qla4xxx_abort_active_cmds(ha, DID_RESET << 16);
                        if (ql4xxx_lock_drvr_wait(ha) == QLA_SUCCESS) {
                                qla4xxx_process_aen(ha, FLUSH_DDB_CHANGED_AENS);
-                               status = qla4xxx_initialize_adapter(ha,
-                                               PRESERVE_DDB_LIST);
+                               status = qla4xxx_recover_adapter(ha);
                        }
                        clear_bit(DPC_RESET_HA_INTR, &ha->dpc_flags);
                        if (status == QLA_SUCCESS)
-                               qla4xxx_enable_intrs(ha);
+                               ha->isp_ops->enable_intrs(ha);
                }
        }
 
+dpc_post_reset_ha:
        /* ---- process AEN? --- */
        if (test_and_clear_bit(DPC_AEN, &ha->dpc_flags))
                qla4xxx_process_aen(ha, PROCESS_ALL_AENS);
@@ -1068,6 +1277,52 @@ static void qla4xxx_do_dpc(struct work_struct *work)
        if (test_and_clear_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags))
                qla4xxx_get_dhcp_ip_address(ha);
 
+       /* ---- link change? --- */
+       if (test_and_clear_bit(DPC_LINK_CHANGED, &ha->dpc_flags)) {
+               if (!test_bit(AF_LINK_UP, &ha->flags)) {
+                       /* ---- link down? --- */
+                       list_for_each_entry_safe(ddb_entry, dtemp,
+                                                &ha->ddb_list, list) {
+                               if (atomic_read(&ddb_entry->state) ==
+                                               DDB_STATE_ONLINE)
+                                       qla4xxx_mark_device_missing(ha,
+                                                       ddb_entry);
+                       }
+               } else {
+                       /* ---- link up? --- *
+                        * F/W will auto login to all devices ONLY ONCE after
+                        * link up during driver initialization and runtime
+                        * fatal error recovery.  Therefore, the driver must
+                        * manually relogin to devices when recovering from
+                        * connection failures, logouts, expired KATO, etc. */
+
+                       list_for_each_entry_safe(ddb_entry, dtemp,
+                                                       &ha->ddb_list, list) {
+                               if ((atomic_read(&ddb_entry->state) ==
+                                                DDB_STATE_MISSING) ||
+                                   (atomic_read(&ddb_entry->state) ==
+                                                DDB_STATE_DEAD)) {
+                                       if (ddb_entry->fw_ddb_device_state ==
+                                           DDB_DS_SESSION_ACTIVE) {
+                                               atomic_set(&ddb_entry->state,
+                                                          DDB_STATE_ONLINE);
+                                               ql4_printk(KERN_INFO, ha,
+                                                   "scsi%ld: %s: ddb[%d]"
+                                                   " marked ONLINE\n",
+                                                   ha->host_no, __func__,
+                                                   ddb_entry->fw_ddb_index);
+
+                                               iscsi_unblock_session(
+                                                   ddb_entry->sess);
+                                       } else
+                                               qla4xxx_relogin_device(
+                                                   ha, ddb_entry);
+                               }
+
+                       }
+               }
+       }
+
        /* ---- relogin device? --- */
        if (adapter_up(ha) &&
            test_and_clear_bit(DPC_RELOGIN_DEVICE, &ha->dpc_flags)) {
@@ -1091,6 +1346,7 @@ static void qla4xxx_do_dpc(struct work_struct *work)
                        }
                }
        }
+       clear_bit(AF_DPC_SCHEDULED, &ha->flags);
 }
 
 /**
@@ -1102,30 +1358,99 @@ static void qla4xxx_free_adapter(struct scsi_qla_host *ha)
 
        if (test_bit(AF_INTERRUPTS_ON, &ha->flags)) {
                /* Turn-off interrupts on the card. */
-               qla4xxx_disable_intrs(ha);
+               ha->isp_ops->disable_intrs(ha);
        }
 
+       /* Remove timer thread, if present */
+       if (ha->timer_active)
+               qla4xxx_stop_timer(ha);
+
        /* Kill the kernel thread for this host */
        if (ha->dpc_thread)
                destroy_workqueue(ha->dpc_thread);
 
-       /* Issue Soft Reset to put firmware in unknown state */
-       if (ql4xxx_lock_drvr_wait(ha) == QLA_SUCCESS)
-               qla4xxx_hw_reset(ha);
+       /* Put firmware in known state */
+       ha->isp_ops->reset_firmware(ha);
 
-       /* Remove timer thread, if present */
-       if (ha->timer_active)
-               qla4xxx_stop_timer(ha);
+       if (is_qla8022(ha)) {
+               qla4_8xxx_idc_lock(ha);
+               qla4_8xxx_clear_drv_active(ha);
+               qla4_8xxx_idc_unlock(ha);
+       }
 
        /* Detach interrupts */
        if (test_and_clear_bit(AF_IRQ_ATTACHED, &ha->flags))
-               free_irq(ha->pdev->irq, ha);
+               qla4xxx_free_irqs(ha);
 
        /* free extra memory */
        qla4xxx_mem_free(ha);
+}
+
+int qla4_8xxx_iospace_config(struct scsi_qla_host *ha)
+{
+       int status = 0;
+       uint8_t revision_id;
+       unsigned long mem_base, mem_len, db_base, db_len;
+       struct pci_dev *pdev = ha->pdev;
+
+       status = pci_request_regions(pdev, DRIVER_NAME);
+       if (status) {
+               printk(KERN_WARNING
+                   "scsi(%ld) Failed to reserve PIO regions (%s) "
+                   "status=%d\n", ha->host_no, pci_name(pdev), status);
+               goto iospace_error_exit;
+       }
+
+       pci_read_config_byte(pdev, PCI_REVISION_ID, &revision_id);
+       DEBUG2(printk(KERN_INFO "%s: revision-id=%d\n",
+           __func__, revision_id));
+       ha->revision_id = revision_id;
+
+       /* remap phys address */
+       mem_base = pci_resource_start(pdev, 0); /* 0 is for BAR 0 */
+       mem_len = pci_resource_len(pdev, 0);
+       DEBUG2(printk(KERN_INFO "%s: ioremap from %lx a size of %lx\n",
+           __func__, mem_base, mem_len));
+
+       /* mapping of pcibase pointer */
+       ha->nx_pcibase = (unsigned long)ioremap(mem_base, mem_len);
+       if (!ha->nx_pcibase) {
+               printk(KERN_ERR
+                   "cannot remap MMIO (%s), aborting\n", pci_name(pdev));
+               pci_release_regions(ha->pdev);
+               goto iospace_error_exit;
+       }
+
+       /* Mapping of IO base pointer, door bell read and write pointer */
+
+       /* mapping of IO base pointer */
+       ha->qla4_8xxx_reg =
+           (struct device_reg_82xx  __iomem *)((uint8_t *)ha->nx_pcibase +
+           0xbc000 + (ha->pdev->devfn << 11));
+
+       db_base = pci_resource_start(pdev, 4);  /* doorbell is on bar 4 */
+       db_len = pci_resource_len(pdev, 4);
 
-       pci_disable_device(ha->pdev);
+       /* mapping of doorbell write pointer */
+       ha->nx_db_wr_ptr = (unsigned long)ioremap(db_base +
+           (ha->pdev->devfn << 12), 4);
+       if (!ha->nx_db_wr_ptr) {
+               printk(KERN_ERR
+                   "cannot remap MMIO doorbell-write (%s), aborting\n",
+                   pci_name(pdev));
+               goto iospace_error_exit;
+       }
+       /* mapping of doorbell read pointer */
+       ha->nx_db_rd_ptr = (uint8_t *) ha->nx_pcibase + (512 * 1024) +
+           (ha->pdev->devfn * 8);
+       if (!ha->nx_db_rd_ptr)
+               printk(KERN_ERR
+                   "cannot remap MMIO doorbell-read (%s), aborting\n",
+                   pci_name(pdev));
+       return 0;
 
+iospace_error_exit:
+       return -ENOMEM;
 }
 
 /***
@@ -1135,7 +1460,7 @@ static void qla4xxx_free_adapter(struct scsi_qla_host *ha)
  * This routines maps HBA's registers from the pci address space
  * into the kernel virtual address space for memory mapped i/o.
  **/
-static int qla4xxx_iospace_config(struct scsi_qla_host *ha)
+int qla4xxx_iospace_config(struct scsi_qla_host *ha)
 {
        unsigned long pio, pio_len, pio_flags;
        unsigned long mmio, mmio_len, mmio_flags;
@@ -1145,12 +1470,12 @@ static int qla4xxx_iospace_config(struct scsi_qla_host *ha)
        pio_flags = pci_resource_flags(ha->pdev, 0);
        if (pio_flags & IORESOURCE_IO) {
                if (pio_len < MIN_IOBASE_LEN) {
-                       dev_warn(&ha->pdev->dev,
+                       ql4_printk(KERN_WARNING, ha,
                                "Invalid PCI I/O region size\n");
                        pio = 0;
                }
        } else {
-               dev_warn(&ha->pdev->dev, "region #0 not a PIO resource\n");
+               ql4_printk(KERN_WARNING, ha, "region #0 not a PIO resource\n");
                pio = 0;
        }
 
@@ -1160,20 +1485,21 @@ static int qla4xxx_iospace_config(struct scsi_qla_host *ha)
        mmio_flags = pci_resource_flags(ha->pdev, 1);
 
        if (!(mmio_flags & IORESOURCE_MEM)) {
-               dev_err(&ha->pdev->dev,
-                       "region #0 not an MMIO resource, aborting\n");
+               ql4_printk(KERN_ERR, ha,
+                   "region #0 not an MMIO resource, aborting\n");
 
                goto iospace_error_exit;
        }
+
        if (mmio_len < MIN_IOBASE_LEN) {
-               dev_err(&ha->pdev->dev,
-                       "Invalid PCI mem region size, aborting\n");
+               ql4_printk(KERN_ERR, ha,
+                   "Invalid PCI mem region size, aborting\n");
                goto iospace_error_exit;
        }
 
        if (pci_request_regions(ha->pdev, DRIVER_NAME)) {
-               dev_warn(&ha->pdev->dev,
-                       "Failed to reserve PIO/MMIO regions\n");
+               ql4_printk(KERN_WARNING, ha,
+                   "Failed to reserve PIO/MMIO regions\n");
 
                goto iospace_error_exit;
        }
@@ -1182,8 +1508,8 @@ static int qla4xxx_iospace_config(struct scsi_qla_host *ha)
        ha->pio_length = pio_len;
        ha->reg = ioremap(mmio, MIN_IOBASE_LEN);
        if (!ha->reg) {
-               dev_err(&ha->pdev->dev,
-                       "cannot remap MMIO, aborting\n");
+               ql4_printk(KERN_ERR, ha,
+                   "cannot remap MMIO, aborting\n");
 
                goto iospace_error_exit;
        }
@@ -1194,6 +1520,60 @@ iospace_error_exit:
        return -ENOMEM;
 }
 
+static struct isp_operations qla4xxx_isp_ops = {
+       .iospace_config         = qla4xxx_iospace_config,
+       .pci_config             = qla4xxx_pci_config,
+       .disable_intrs          = qla4xxx_disable_intrs,
+       .enable_intrs           = qla4xxx_enable_intrs,
+       .start_firmware         = qla4xxx_start_firmware,
+       .intr_handler           = qla4xxx_intr_handler,
+       .interrupt_service_routine = qla4xxx_interrupt_service_routine,
+       .reset_chip             = qla4xxx_soft_reset,
+       .reset_firmware         = qla4xxx_hw_reset,
+       .queue_iocb             = qla4xxx_queue_iocb,
+       .complete_iocb          = qla4xxx_complete_iocb,
+       .rd_shdw_req_q_out      = qla4xxx_rd_shdw_req_q_out,
+       .rd_shdw_rsp_q_in       = qla4xxx_rd_shdw_rsp_q_in,
+       .get_sys_info           = qla4xxx_get_sys_info,
+};
+
+static struct isp_operations qla4_8xxx_isp_ops = {
+       .iospace_config         = qla4_8xxx_iospace_config,
+       .pci_config             = qla4_8xxx_pci_config,
+       .disable_intrs          = qla4_8xxx_disable_intrs,
+       .enable_intrs           = qla4_8xxx_enable_intrs,
+       .start_firmware         = qla4_8xxx_load_risc,
+       .intr_handler           = qla4_8xxx_intr_handler,
+       .interrupt_service_routine = qla4_8xxx_interrupt_service_routine,
+       .reset_chip             = qla4_8xxx_isp_reset,
+       .reset_firmware         = qla4_8xxx_stop_firmware,
+       .queue_iocb             = qla4_8xxx_queue_iocb,
+       .complete_iocb          = qla4_8xxx_complete_iocb,
+       .rd_shdw_req_q_out      = qla4_8xxx_rd_shdw_req_q_out,
+       .rd_shdw_rsp_q_in       = qla4_8xxx_rd_shdw_rsp_q_in,
+       .get_sys_info           = qla4_8xxx_get_sys_info,
+};
+
+uint16_t qla4xxx_rd_shdw_req_q_out(struct scsi_qla_host *ha)
+{
+       return (uint16_t)le32_to_cpu(ha->shadow_regs->req_q_out);
+}
+
+uint16_t qla4_8xxx_rd_shdw_req_q_out(struct scsi_qla_host *ha)
+{
+       return (uint16_t)le32_to_cpu(readl(&ha->qla4_8xxx_reg->req_q_out));
+}
+
+uint16_t qla4xxx_rd_shdw_rsp_q_in(struct scsi_qla_host *ha)
+{
+       return (uint16_t)le32_to_cpu(ha->shadow_regs->rsp_q_in);
+}
+
+uint16_t qla4_8xxx_rd_shdw_rsp_q_in(struct scsi_qla_host *ha)
+{
+       return (uint16_t)le32_to_cpu(readl(&ha->qla4_8xxx_reg->rsp_q_in));
+}
+
 /**
  * qla4xxx_probe_adapter - callback function to probe HBA
  * @pdev: pointer to pci_dev structure
@@ -1211,6 +1591,7 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
        struct scsi_qla_host *ha;
        uint8_t init_retry_count = 0;
        char buf[34];
+       struct qla4_8xxx_legacy_intr_set *nx_legacy_intr;
 
        if (pci_enable_device(pdev))
                return -1;
@@ -1231,12 +1612,30 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
        ha->host = host;
        ha->host_no = host->host_no;
 
+       /* Setup Runtime configurable options */
+       if (is_qla8022(ha)) {
+               ha->isp_ops = &qla4_8xxx_isp_ops;
+               rwlock_init(&ha->hw_lock);
+               ha->qdr_sn_window = -1;
+               ha->ddr_mn_window = -1;
+               ha->curr_window = 255;
+               ha->func_num = PCI_FUNC(ha->pdev->devfn);
+               nx_legacy_intr = &legacy_intr[ha->func_num];
+               ha->nx_legacy_intr.int_vec_bit = nx_legacy_intr->int_vec_bit;
+               ha->nx_legacy_intr.tgt_status_reg =
+                       nx_legacy_intr->tgt_status_reg;
+               ha->nx_legacy_intr.tgt_mask_reg = nx_legacy_intr->tgt_mask_reg;
+               ha->nx_legacy_intr.pci_int_reg = nx_legacy_intr->pci_int_reg;
+       } else {
+               ha->isp_ops = &qla4xxx_isp_ops;
+       }
+
        /* Configure PCI I/O space. */
-       ret = qla4xxx_iospace_config(ha);
+       ret = ha->isp_ops->iospace_config(ha);
        if (ret)
-               goto probe_failed;
+               goto probe_failed_ioconfig;
 
-       dev_info(&ha->pdev->dev, "Found an ISP%04x, irq %d, iobase 0x%p\n",
+       ql4_printk(KERN_INFO, ha, "Found an ISP%04x, irq %d, iobase 0x%p\n",
                   pdev->device, pdev->irq, ha->reg);
 
        qla4xxx_config_dma_addressing(ha);
@@ -1246,32 +1645,41 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
        INIT_LIST_HEAD(&ha->free_srb_q);
 
        mutex_init(&ha->mbox_sem);
+       init_completion(&ha->mbx_intr_comp);
 
        spin_lock_init(&ha->hardware_lock);
 
        /* Allocate dma buffers */
        if (qla4xxx_mem_alloc(ha)) {
-               dev_warn(&ha->pdev->dev,
-                          "[ERROR] Failed to allocate memory for adapter\n");
+               ql4_printk(KERN_WARNING, ha,
+                   "[ERROR] Failed to allocate memory for adapter\n");
 
                ret = -ENOMEM;
                goto probe_failed;
        }
 
+       if (is_qla8022(ha))
+               (void) qla4_8xxx_get_flash_info(ha);
+
        /*
         * Initialize the Host adapter request/response queues and
         * firmware
         * NOTE: interrupts enabled upon successful completion
         */
        status = qla4xxx_initialize_adapter(ha, REBUILD_DDB_LIST);
-       while (status == QLA_ERROR && init_retry_count++ < MAX_INIT_RETRIES) {
+       while ((!test_bit(AF_ONLINE, &ha->flags)) &&
+           init_retry_count++ < MAX_INIT_RETRIES) {
                DEBUG2(printk("scsi: %s: retrying adapter initialization "
                              "(%d)\n", __func__, init_retry_count));
-               qla4xxx_soft_reset(ha);
+
+               if (ha->isp_ops->reset_chip(ha) == QLA_ERROR)
+                       continue;
+
                status = qla4xxx_initialize_adapter(ha, REBUILD_DDB_LIST);
        }
-       if (status == QLA_ERROR) {
-               dev_warn(&ha->pdev->dev, "Failed to initialize adapter\n");
+
+       if (!test_bit(AF_ONLINE, &ha->flags)) {
+               ql4_printk(KERN_WARNING, ha, "Failed to initialize adapter\n");
 
                ret = -ENODEV;
                goto probe_failed;
@@ -1287,8 +1695,9 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
 
         ret = scsi_init_shared_tag_map(host, MAX_SRBS);
         if (ret) {
-                dev_warn(&ha->pdev->dev, "scsi_init_shared_tag_map failed\n");
-                goto probe_failed;
+               ql4_printk(KERN_WARNING, ha,
+                   "scsi_init_shared_tag_map failed\n");
+               goto probe_failed;
         }
 
        /* Startup the kernel thread for this host adapter. */
@@ -1297,24 +1706,27 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
        sprintf(buf, "qla4xxx_%lu_dpc", ha->host_no);
        ha->dpc_thread = create_singlethread_workqueue(buf);
        if (!ha->dpc_thread) {
-               dev_warn(&ha->pdev->dev, "Unable to start DPC thread!\n");
+               ql4_printk(KERN_WARNING, ha, "Unable to start DPC thread!\n");
                ret = -ENODEV;
                goto probe_failed;
        }
        INIT_WORK(&ha->dpc_work, qla4xxx_do_dpc);
 
-       ret = request_irq(pdev->irq, qla4xxx_intr_handler,
-                         IRQF_DISABLED | IRQF_SHARED, "qla4xxx", ha);
-       if (ret) {
-               dev_warn(&ha->pdev->dev, "Failed to reserve interrupt %d"
-                       " already in use.\n", pdev->irq);
-               goto probe_failed;
+       /* For ISP-82XX, request_irqs is called in qla4_8xxx_load_risc
+        * (which is called indirectly by qla4xxx_initialize_adapter),
+        * so that irqs will be registered after crbinit but before
+        * mbx_intr_enable.
+        */
+       if (!is_qla8022(ha)) {
+               ret = qla4xxx_request_irqs(ha);
+               if (ret) {
+                       ql4_printk(KERN_WARNING, ha, "Failed to reserve "
+                           "interrupt %d already in use.\n", pdev->irq);
+                       goto probe_failed;
+               }
        }
-       set_bit(AF_IRQ_ATTACHED, &ha->flags);
-       host->irq = pdev->irq;
-       DEBUG(printk("scsi%d: irq %d attached\n", ha->host_no, ha->pdev->irq));
 
-       qla4xxx_enable_intrs(ha);
+       ha->isp_ops->enable_intrs(ha);
 
        /* Start timer thread. */
        qla4xxx_start_timer(ha, qla4xxx_timer, 1);
@@ -1338,6 +1750,8 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
 
 probe_failed:
        qla4xxx_free_adapter(ha);
+
+probe_failed_ioconfig:
        scsi_host_put(ha->host);
 
 probe_disable_device:
@@ -1356,10 +1770,7 @@ static void __devexit qla4xxx_remove_adapter(struct pci_dev *pdev)
 
        ha = pci_get_drvdata(pdev);
 
-       qla4xxx_disable_intrs(ha);
-
-       while (test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags))
-               ssleep(1);
+       set_bit(AF_HBA_GOING_AWAY, &ha->flags);
 
        /* remove devs from iscsi_sessions to scsi_devices */
        qla4xxx_free_ddb_list(ha);
@@ -1370,6 +1781,7 @@ static void __devexit qla4xxx_remove_adapter(struct pci_dev *pdev)
 
        scsi_host_put(ha->host);
 
+       pci_disable_device(pdev);
        pci_set_drvdata(pdev, NULL);
 }
 
@@ -1422,19 +1834,22 @@ static void qla4xxx_slave_destroy(struct scsi_device *sdev)
 /**
  * qla4xxx_del_from_active_array - returns an active srb
  * @ha: Pointer to host adapter structure.
- * @index: index into to the active_array
+ * @index: index into the active_array
  *
  * This routine removes and returns the srb at the specified index
  **/
-struct srb * qla4xxx_del_from_active_array(struct scsi_qla_host *ha, uint32_t index)
+struct srb *qla4xxx_del_from_active_array(struct scsi_qla_host *ha,
+    uint32_t index)
 {
        struct srb *srb = NULL;
-       struct scsi_cmnd *cmd;
+       struct scsi_cmnd *cmd = NULL;
 
-       if (!(cmd = scsi_host_find_tag(ha->host, index)))
+       cmd = scsi_host_find_tag(ha->host, index);
+       if (!cmd)
                return srb;
 
-       if (!(srb = (struct srb *)cmd->host_scribble))
+       srb = (struct srb *)CMD_SP(cmd);
+       if (!srb)
                return srb;
 
        /* update counters */
@@ -1442,14 +1857,15 @@ struct srb * qla4xxx_del_from_active_array(struct scsi_qla_host *ha, uint32_t in
                ha->req_q_count += srb->iocb_cnt;
                ha->iocb_cnt -= srb->iocb_cnt;
                if (srb->cmd)
-                       srb->cmd->host_scribble = NULL;
+                       srb->cmd->host_scribble =
+                               (unsigned char *)(unsigned long) MAX_SRBS;
        }
        return srb;
 }
 
 /**
  * qla4xxx_eh_wait_on_command - waits for command to be returned by firmware
- * @ha: actual ha whose done queue will contain the comd returned by firmware.
+ * @ha: Pointer to host adapter structure.
  * @cmd: Scsi Command to wait on.
  *
  * This routine waits for the command to be returned by the Firmware
@@ -1464,7 +1880,7 @@ static int qla4xxx_eh_wait_on_command(struct scsi_qla_host *ha,
 
        do {
                /* Checking to see if its returned to OS */
-               rp = (struct srb *) cmd->SCp.ptr;
+               rp = (struct srb *) CMD_SP(cmd);
                if (rp == NULL) {
                        done++;
                        break;
@@ -1500,7 +1916,7 @@ static int qla4xxx_wait_for_hba_online(struct scsi_qla_host *ha)
 
 /**
  * qla4xxx_eh_wait_for_commands - wait for active cmds to finish.
- * @ha: pointer to to HBA
+ * @ha: pointer to HBA
  * @t: target id
  * @l: lun id
  *
@@ -1533,6 +1949,62 @@ static int qla4xxx_eh_wait_for_commands(struct scsi_qla_host *ha,
 }
 
 /**
+ * qla4xxx_eh_abort - callback for abort task.
+ * @cmd: Pointer to Linux's SCSI command structure
+ *
+ * This routine is called by the Linux OS to abort the specified
+ * command.
+ **/
+static int qla4xxx_eh_abort(struct scsi_cmnd *cmd)
+{
+       struct scsi_qla_host *ha = to_qla_host(cmd->device->host);
+       unsigned int id = cmd->device->id;
+       unsigned int lun = cmd->device->lun;
+       unsigned long serial = cmd->serial_number;
+       struct srb *srb = NULL;
+       int ret = SUCCESS;
+       int wait = 0;
+
+       ql4_printk(KERN_INFO, ha,
+           "scsi%ld:%d:%d: Abort command issued cmd=%p, pid=%ld\n",
+           ha->host_no, id, lun, cmd, serial);
+
+       srb = (struct srb *) CMD_SP(cmd);
+
+       if (!srb)
+               return SUCCESS;
+
+       kref_get(&srb->srb_ref);
+
+       if (qla4xxx_abort_task(ha, srb) != QLA_SUCCESS) {
+               DEBUG3(printk("scsi%ld:%d:%d: Abort_task mbx failed.\n",
+                   ha->host_no, id, lun));
+               ret = FAILED;
+       } else {
+               DEBUG3(printk("scsi%ld:%d:%d: Abort_task mbx success.\n",
+                   ha->host_no, id, lun));
+               wait = 1;
+       }
+
+       kref_put(&srb->srb_ref, qla4xxx_srb_compl);
+
+       /* Wait for command to complete */
+       if (wait) {
+               if (!qla4xxx_eh_wait_on_command(ha, cmd)) {
+                       DEBUG2(printk("scsi%ld:%d:%d: Abort handler timed out\n",
+                           ha->host_no, id, lun));
+                       ret = FAILED;
+               }
+       }
+
+       ql4_printk(KERN_INFO, ha,
+           "scsi%ld:%d:%d: Abort command - %s\n",
+           ha->host_no, id, lun, (ret == SUCCESS) ? "succeded" : "failed");
+
+       return ret;
+}
+
+/**
  * qla4xxx_eh_device_reset - callback for target reset.
  * @cmd: Pointer to Linux's SCSI command structure
  *
@@ -1548,7 +2020,12 @@ static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd)
        if (!ddb_entry)
                return ret;
 
-       dev_info(&ha->pdev->dev,
+       ret = iscsi_block_scsi_eh(cmd);
+       if (ret)
+               return ret;
+       ret = FAILED;
+
+       ql4_printk(KERN_INFO, ha,
                   "scsi%ld:%d:%d:%d: DEVICE RESET ISSUED.\n", ha->host_no,
                   cmd->device->channel, cmd->device->id, cmd->device->lun);
 
@@ -1561,13 +2038,13 @@ static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd)
        /* FIXME: wait for hba to go online */
        stat = qla4xxx_reset_lun(ha, ddb_entry, cmd->device->lun);
        if (stat != QLA_SUCCESS) {
-               dev_info(&ha->pdev->dev, "DEVICE RESET FAILED. %d\n", stat);
+               ql4_printk(KERN_INFO, ha, "DEVICE RESET FAILED. %d\n", stat);
                goto eh_dev_reset_done;
        }
 
        if (qla4xxx_eh_wait_for_commands(ha, scsi_target(cmd->device),
                                         cmd->device)) {
-               dev_info(&ha->pdev->dev,
+               ql4_printk(KERN_INFO, ha,
                           "DEVICE RESET FAILED - waiting for "
                           "commands.\n");
                goto eh_dev_reset_done;
@@ -1578,7 +2055,7 @@ static int qla4xxx_eh_device_reset(struct scsi_cmnd *cmd)
                MM_LUN_RESET) != QLA_SUCCESS)
                goto eh_dev_reset_done;
 
-       dev_info(&ha->pdev->dev,
+       ql4_printk(KERN_INFO, ha,
                   "scsi(%ld:%d:%d:%d): DEVICE RESET SUCCEEDED.\n",
                   ha->host_no, cmd->device->channel, cmd->device->id,
                   cmd->device->lun);
@@ -1600,11 +2077,15 @@ static int qla4xxx_eh_target_reset(struct scsi_cmnd *cmd)
 {
        struct scsi_qla_host *ha = to_qla_host(cmd->device->host);
        struct ddb_entry *ddb_entry = cmd->device->hostdata;
-       int stat;
+       int stat, ret;
 
        if (!ddb_entry)
                return FAILED;
 
+       ret = iscsi_block_scsi_eh(cmd);
+       if (ret)
+               return ret;
+
        starget_printk(KERN_INFO, scsi_target(cmd->device),
                       "WARM TARGET RESET ISSUED.\n");
 
@@ -1657,7 +2138,13 @@ static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd)
 
        ha = (struct scsi_qla_host *) cmd->device->host->hostdata;
 
-       dev_info(&ha->pdev->dev,
+       if (ql4xdontresethba) {
+               DEBUG2(printk("scsi%ld: %s: Don't Reset HBA\n",
+                    ha->host_no, __func__));
+               return FAILED;
+       }
+
+       ql4_printk(KERN_INFO, ha,
                   "scsi(%ld:%d:%d:%d): HOST RESET ISSUED.\n", ha->host_no,
                   cmd->device->channel, cmd->device->id, cmd->device->lun);
 
@@ -1669,20 +2156,22 @@ static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd)
                return FAILED;
        }
 
-       /* make sure the dpc thread is stopped while we reset the hba */
-       clear_bit(AF_ONLINE, &ha->flags);
-       flush_workqueue(ha->dpc_thread);
+       if (!test_bit(DPC_RESET_HA, &ha->dpc_flags)) {
+               if (is_qla8022(ha))
+                       set_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags);
+               else
+                       set_bit(DPC_RESET_HA, &ha->dpc_flags);
+       }
 
-       if (qla4xxx_recover_adapter(ha, PRESERVE_DDB_LIST) == QLA_SUCCESS)
+       if (qla4xxx_recover_adapter(ha) == QLA_SUCCESS)
                return_status = SUCCESS;
 
-       dev_info(&ha->pdev->dev, "HOST RESET %s.\n",
+       ql4_printk(KERN_INFO, ha, "HOST RESET %s.\n",
                   return_status == FAILED ? "FAILED" : "SUCCEDED");
 
        return return_status;
 }
 
-
 static struct pci_device_id qla4xxx_pci_tbl[] = {
        {
                .vendor         = PCI_VENDOR_ID_QLOGIC,
@@ -1702,6 +2191,12 @@ static struct pci_device_id qla4xxx_pci_tbl[] = {
                .subvendor      = PCI_ANY_ID,
                .subdevice      = PCI_ANY_ID,
        },
+       {
+               .vendor         = PCI_VENDOR_ID_QLOGIC,
+               .device         = PCI_DEVICE_ID_QLOGIC_ISP8022,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+       },
        {0, 0},
 };
 MODULE_DEVICE_TABLE(pci, qla4xxx_pci_tbl);
@@ -1757,7 +2252,6 @@ no_srp_cache:
 
 static void __exit qla4xxx_module_exit(void)
 {
-       ql4_mod_unload = 1;
        pci_unregister_driver(&qla4xxx_pci_driver);
        iscsi_unregister_transport(&qla4xxx_iscsi_transport);
        kmem_cache_destroy(srb_cachep);