]> err.no Git - linux-2.6/blobdiff - drivers/scsi/qla2xxx/qla_os.c
Merge branches 'pxa-ezx', 'pxa-magician' and 'pxa-palm' into pxa
[linux-2.6] / drivers / scsi / qla2xxx / qla_os.c
index 2d28937d30c71d0f12f173365008ca059873222c..48eaa3bb5433892b2167f18215e6fe35e1cbe113 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/vmalloc.h>
 #include <linux/delay.h>
 #include <linux/kthread.h>
+#include <linux/mutex.h>
 
 #include <scsi/scsi_tcq.h>
 #include <scsi/scsicam.h>
@@ -26,9 +27,6 @@ char qla2x00_version_str[40];
  */
 static struct kmem_cache *srb_cachep;
 
-/*
- * Ioctl related information.
- */
 int num_hosts;
 int ql2xlogintimeout = 20;
 module_param(ql2xlogintimeout, int, S_IRUGO|S_IRUSR);
@@ -70,7 +68,7 @@ static void qla2x00_free_device(scsi_qla_host_t *);
 
 static void qla2x00_config_dma_addressing(scsi_qla_host_t *ha);
 
-int ql2xfdmienable;
+int ql2xfdmienable=1;
 module_param(ql2xfdmienable, int, S_IRUGO|S_IRUSR);
 MODULE_PARM_DESC(ql2xfdmienable,
                "Enables FDMI registratons "
@@ -103,9 +101,9 @@ static int qla24xx_queuecommand(struct scsi_cmnd *cmd,
                void (*fn)(struct scsi_cmnd *));
 static int qla2xxx_eh_abort(struct scsi_cmnd *);
 static int qla2xxx_eh_device_reset(struct scsi_cmnd *);
+static int qla2xxx_eh_target_reset(struct scsi_cmnd *);
 static int qla2xxx_eh_bus_reset(struct scsi_cmnd *);
 static int qla2xxx_eh_host_reset(struct scsi_cmnd *);
-static int qla2x00_device_reset(scsi_qla_host_t *, fc_port_t *);
 
 static int qla2x00_change_queue_depth(struct scsi_device *, int);
 static int qla2x00_change_queue_type(struct scsi_device *, int);
@@ -117,6 +115,7 @@ static struct scsi_host_template qla2x00_driver_template = {
 
        .eh_abort_handler       = qla2xxx_eh_abort,
        .eh_device_reset_handler = qla2xxx_eh_device_reset,
+       .eh_target_reset_handler = qla2xxx_eh_target_reset,
        .eh_bus_reset_handler   = qla2xxx_eh_bus_reset,
        .eh_host_reset_handler  = qla2xxx_eh_host_reset,
 
@@ -148,6 +147,7 @@ struct scsi_host_template qla24xx_driver_template = {
 
        .eh_abort_handler       = qla2xxx_eh_abort,
        .eh_device_reset_handler = qla2xxx_eh_device_reset,
+       .eh_target_reset_handler = qla2xxx_eh_target_reset,
        .eh_bus_reset_handler   = qla2xxx_eh_bus_reset,
        .eh_host_reset_handler  = qla2xxx_eh_host_reset,
 
@@ -253,9 +253,9 @@ qla24xx_pci_info_str(struct scsi_qla_host *ha, char *str)
 
                strcpy(str, "PCIe (");
                if (lspeed == 1)
-                       strcat(str, "2.5Gb/s ");
+                       strcat(str, "2.5GT/s ");
                else if (lspeed == 2)
-                       strcat(str, "5.0Gb/s ");
+                       strcat(str, "5.0GT/s ");
                else
                        strcat(str, "<unknown> ");
                snprintf(lwstr, sizeof(lwstr), "x%d)", lwidth);
@@ -340,6 +340,8 @@ qla24xx_fw_version_str(struct scsi_qla_host *ha, char *str)
                strcat(str, "[T10 CRC] ");
        if (ha->fw_attributes & BIT_5)
                strcat(str, "[VI] ");
+       if (ha->fw_attributes & BIT_10)
+               strcat(str, "[84XX] ");
        if (ha->fw_attributes & BIT_13)
                strcat(str, "[Experimental]");
        return str;
@@ -570,8 +572,6 @@ qla2x00_wait_for_hba_online(scsi_qla_host_t *ha)
        else
                return_status = QLA_FUNCTION_FAILED;
 
-       DEBUG2(printk("%s return_status=%d\n",__func__,return_status));
-
        return (return_status);
 }
 
@@ -685,7 +685,6 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
 
                DEBUG2(printk("%s(%ld): aborting sp %p from RISC. pid=%ld.\n",
                    __func__, ha->host_no, sp, serial));
-               DEBUG3(qla2x00_print_scsi_cmd(cmd));
 
                spin_unlock_irqrestore(&pha->hardware_lock, flags);
                if (ha->isp_ops->abort_command(ha, sp)) {
@@ -719,190 +718,122 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd)
        return ret;
 }
 
-/**************************************************************************
-* qla2x00_eh_wait_for_pending_target_commands
-*
-* Description:
-*    Waits for all the commands to come back from the specified target.
-*
-* Input:
-*    ha - pointer to scsi_qla_host structure.
-*    t  - target
-* Returns:
-*    Either SUCCESS or FAILED.
-*
-* Note:
-**************************************************************************/
+enum nexus_wait_type {
+       WAIT_HOST = 0,
+       WAIT_TARGET,
+       WAIT_LUN,
+};
+
 static int
-qla2x00_eh_wait_for_pending_target_commands(scsi_qla_host_t *ha, unsigned int t)
+qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *ha, unsigned int t,
+    unsigned int l, enum nexus_wait_type type)
 {
-       int     cnt;
-       int     status;
-       srb_t           *sp;
-       struct scsi_cmnd *cmd;
+       int cnt, match, status;
+       srb_t *sp;
        unsigned long flags;
        scsi_qla_host_t *pha = to_qla_parent(ha);
 
-       status = 0;
-
-       /*
-        * Waiting for all commands for the designated target in the active
-        * array
-        */
-       for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
-               spin_lock_irqsave(&pha->hardware_lock, flags);
+       status = QLA_SUCCESS;
+       spin_lock_irqsave(&pha->hardware_lock, flags);
+       for (cnt = 1; status == QLA_SUCCESS && cnt < MAX_OUTSTANDING_COMMANDS;
+           cnt++) {
                sp = pha->outstanding_cmds[cnt];
-               if (sp) {
-                       cmd = sp->cmd;
-                       spin_unlock_irqrestore(&pha->hardware_lock, flags);
-                       if (cmd->device->id == t &&
-                           ha->vp_idx == sp->ha->vp_idx) {
-                               if (!qla2x00_eh_wait_on_command(ha, cmd)) {
-                                       status = 1;
-                                       break;
-                               }
-                       }
-               } else {
-                       spin_unlock_irqrestore(&pha->hardware_lock, flags);
+               if (!sp)
+                       continue;
+               if (ha->vp_idx != sp->ha->vp_idx)
+                       continue;
+               match = 0;
+               switch (type) {
+               case WAIT_HOST:
+                       match = 1;
+                       break;
+               case WAIT_TARGET:
+                       match = sp->cmd->device->id == t;
+                       break;
+               case WAIT_LUN:
+                       match = (sp->cmd->device->id == t &&
+                           sp->cmd->device->lun == l);
+                       break;
                }
+               if (!match)
+                       continue;
+
+               spin_unlock_irqrestore(&pha->hardware_lock, flags);
+               status = qla2x00_eh_wait_on_command(ha, sp->cmd);
+               spin_lock_irqsave(&pha->hardware_lock, flags);
        }
-       return (status);
+       spin_unlock_irqrestore(&pha->hardware_lock, flags);
+
+       return status;
 }
 
+static char *reset_errors[] = {
+       "HBA not online",
+       "HBA not ready",
+       "Task management failed",
+       "Waiting for command completions",
+};
 
-/**************************************************************************
-* qla2xxx_eh_device_reset
-*
-* Description:
-*    The device reset function will reset the target and abort any
-*    executing commands.
-*
-*    NOTE: The use of SP is undefined within this context.  Do *NOT*
-*          attempt to use this value, even if you determine it is
-*          non-null.
-*
-* Input:
-*    cmd = Linux SCSI command packet of the command that cause the
-*          bus device reset.
-*
-* Returns:
-*    SUCCESS/FAILURE (defined as macro in scsi.h).
-*
-**************************************************************************/
 static int
-qla2xxx_eh_device_reset(struct scsi_cmnd *cmd)
+__qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
+    struct scsi_cmnd *cmd, int (*do_reset)(struct fc_port *, unsigned int))
 {
        scsi_qla_host_t *ha = shost_priv(cmd->device->host);
        fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
-       int ret = FAILED;
-       unsigned int id, lun;
-       unsigned long serial;
+       int err;
 
        qla2x00_block_error_handler(cmd);
 
-       id = cmd->device->id;
-       lun = cmd->device->lun;
-       serial = cmd->serial_number;
-
        if (!fcport)
-               return ret;
+               return FAILED;
 
-       qla_printk(KERN_INFO, ha,
-           "scsi(%ld:%d:%d): DEVICE RESET ISSUED.\n", ha->host_no, id, lun);
+       qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d): %s RESET ISSUED.\n",
+           ha->host_no, cmd->device->id, cmd->device->lun, name);
 
+       err = 0;
        if (qla2x00_wait_for_hba_online(ha) != QLA_SUCCESS)
-               goto eh_dev_reset_done;
-
-       if (qla2x00_wait_for_loop_ready(ha) == QLA_SUCCESS) {
-               if (qla2x00_device_reset(ha, fcport) == 0)
-                       ret = SUCCESS;
-
-#if defined(LOGOUT_AFTER_DEVICE_RESET)
-               if (ret == SUCCESS) {
-                       if (fcport->flags & FC_FABRIC_DEVICE) {
-                               ha->isp_ops->fabric_logout(ha, fcport->loop_id);
-                               qla2x00_mark_device_lost(ha, fcport, 0, 0);
-                       }
-               }
-#endif
-       } else {
-               DEBUG2(printk(KERN_INFO
-                   "%s failed: loop not ready\n",__func__));
-       }
-
-       if (ret == FAILED) {
-               DEBUG3(printk("%s(%ld): device reset failed\n",
-                   __func__, ha->host_no));
-               qla_printk(KERN_INFO, ha, "%s: device reset failed\n",
-                   __func__);
+               goto eh_reset_failed;
+       err = 1;
+       if (qla2x00_wait_for_loop_ready(ha) != QLA_SUCCESS)
+               goto eh_reset_failed;
+       err = 2;
+       if (do_reset(fcport, cmd->device->lun) != QLA_SUCCESS)
+               goto eh_reset_failed;
+       err = 3;
+       if (qla2x00_eh_wait_for_pending_commands(ha, cmd->device->id,
+           cmd->device->lun, type) != QLA_SUCCESS)
+               goto eh_reset_failed;
+
+       qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d): %s RESET SUCCEEDED.\n",
+           ha->host_no, cmd->device->id, cmd->device->lun, name);
+
+       return SUCCESS;
+
+ eh_reset_failed:
+       qla_printk(KERN_INFO, ha, "scsi(%ld:%d:%d): %s RESET FAILED: %s.\n",
+           ha->host_no, cmd->device->id, cmd->device->lun, name,
+           reset_errors[err]);
+       return FAILED;
+}
 
-               goto eh_dev_reset_done;
-       }
+static int
+qla2xxx_eh_device_reset(struct scsi_cmnd *cmd)
+{
+       scsi_qla_host_t *ha = shost_priv(cmd->device->host);
 
-       /* Flush outstanding commands. */
-       if (qla2x00_eh_wait_for_pending_target_commands(ha, id))
-               ret = FAILED;
-       if (ret == FAILED) {
-               DEBUG3(printk("%s(%ld): failed while waiting for commands\n",
-                   __func__, ha->host_no));
-               qla_printk(KERN_INFO, ha,
-                   "%s: failed while waiting for commands\n", __func__);
-       } else
-               qla_printk(KERN_INFO, ha,
-                   "scsi(%ld:%d:%d): DEVICE RESET SUCCEEDED.\n", ha->host_no,
-                   id, lun);
- eh_dev_reset_done:
-       return ret;
+       return __qla2xxx_eh_generic_reset("DEVICE", WAIT_LUN, cmd,
+           ha->isp_ops->lun_reset);
 }
 
-/**************************************************************************
-* qla2x00_eh_wait_for_pending_commands
-*
-* Description:
-*    Waits for all the commands to come back from the specified host.
-*
-* Input:
-*    ha - pointer to scsi_qla_host structure.
-*
-* Returns:
-*    1 : SUCCESS
-*    0 : FAILED
-*
-* Note:
-**************************************************************************/
 static int
-qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *ha)
+qla2xxx_eh_target_reset(struct scsi_cmnd *cmd)
 {
-       int     cnt;
-       int     status;
-       srb_t           *sp;
-       struct scsi_cmnd *cmd;
-       unsigned long flags;
-
-       status = 1;
+       scsi_qla_host_t *ha = shost_priv(cmd->device->host);
 
-       /*
-        * Waiting for all commands for the designated target in the active
-        * array
-        */
-       for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
-               spin_lock_irqsave(&ha->hardware_lock, flags);
-               sp = ha->outstanding_cmds[cnt];
-               if (sp) {
-                       cmd = sp->cmd;
-                       spin_unlock_irqrestore(&ha->hardware_lock, flags);
-                       status = qla2x00_eh_wait_on_command(ha, cmd);
-                       if (status == 0)
-                               break;
-               }
-               else {
-                       spin_unlock_irqrestore(&ha->hardware_lock, flags);
-               }
-       }
-       return (status);
+       return __qla2xxx_eh_generic_reset("TARGET", WAIT_TARGET, cmd,
+           ha->isp_ops->target_reset);
 }
 
-
 /**************************************************************************
 * qla2xxx_eh_bus_reset
 *
@@ -953,7 +884,8 @@ qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd)
                goto eh_bus_reset_done;
 
        /* Flush outstanding commands. */
-       if (!qla2x00_eh_wait_for_pending_commands(pha))
+       if (qla2x00_eh_wait_for_pending_commands(pha, 0, 0, WAIT_HOST) !=
+           QLA_SUCCESS)
                ret = FAILED;
 
 eh_bus_reset_done:
@@ -1024,7 +956,8 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd)
        clear_bit(ABORT_ISP_ACTIVE, &pha->dpc_flags);
 
        /* Waiting for our command in done_queue to be returned to OS.*/
-       if (qla2x00_eh_wait_for_pending_commands(pha))
+       if (qla2x00_eh_wait_for_pending_commands(pha, 0, 0, WAIT_HOST) ==
+           QLA_SUCCESS)
                ret = SUCCESS;
 
        if (ha->parent)
@@ -1080,7 +1013,7 @@ qla2x00_loop_reset(scsi_qla_host_t *ha)
                        if (fcport->port_type != FCT_TARGET)
                                continue;
 
-                       ret = qla2x00_device_reset(ha, fcport);
+                       ret = ha->isp_ops->target_reset(fcport, 0);
                        if (ret != QLA_SUCCESS) {
                                DEBUG2_3(printk("%s(%ld): bus_reset failed: "
                                    "target_reset=%d d_id=%x.\n", __func__,
@@ -1095,26 +1028,6 @@ qla2x00_loop_reset(scsi_qla_host_t *ha)
        return QLA_SUCCESS;
 }
 
-/*
- * qla2x00_device_reset
- *     Issue bus device reset message to the target.
- *
- * Input:
- *     ha = adapter block pointer.
- *     t = SCSI ID.
- *     TARGET_QUEUE_LOCK must be released.
- *     ADAPTER_STATE_LOCK must be released.
- *
- * Context:
- *     Kernel context.
- */
-static int
-qla2x00_device_reset(scsi_qla_host_t *ha, fc_port_t *reset_fcport)
-{
-       /* Abort Target command will clear Reservation */
-       return ha->isp_ops->abort_target(reset_fcport);
-}
-
 void
 qla2x00_abort_all_cmds(scsi_qla_host_t *ha, int res)
 {
@@ -1292,7 +1205,8 @@ static struct isp_operations qla2100_isp_ops = {
        .enable_intrs           = qla2x00_enable_intrs,
        .disable_intrs          = qla2x00_disable_intrs,
        .abort_command          = qla2x00_abort_command,
-       .abort_target           = qla2x00_abort_target,
+       .target_reset           = qla2x00_abort_target,
+       .lun_reset              = qla2x00_lun_reset,
        .fabric_login           = qla2x00_login_fabric,
        .fabric_logout          = qla2x00_fabric_logout,
        .calc_req_entries       = qla2x00_calc_iocbs_32,
@@ -1325,7 +1239,8 @@ static struct isp_operations qla2300_isp_ops = {
        .enable_intrs           = qla2x00_enable_intrs,
        .disable_intrs          = qla2x00_disable_intrs,
        .abort_command          = qla2x00_abort_command,
-       .abort_target           = qla2x00_abort_target,
+       .target_reset           = qla2x00_abort_target,
+       .lun_reset              = qla2x00_lun_reset,
        .fabric_login           = qla2x00_login_fabric,
        .fabric_logout          = qla2x00_fabric_logout,
        .calc_req_entries       = qla2x00_calc_iocbs_32,
@@ -1358,7 +1273,8 @@ static struct isp_operations qla24xx_isp_ops = {
        .enable_intrs           = qla24xx_enable_intrs,
        .disable_intrs          = qla24xx_disable_intrs,
        .abort_command          = qla24xx_abort_command,
-       .abort_target           = qla24xx_abort_target,
+       .target_reset           = qla24xx_abort_target,
+       .lun_reset              = qla24xx_lun_reset,
        .fabric_login           = qla24xx_login_fabric,
        .fabric_logout          = qla24xx_fabric_logout,
        .calc_req_entries       = NULL,
@@ -1391,7 +1307,8 @@ static struct isp_operations qla25xx_isp_ops = {
        .enable_intrs           = qla24xx_enable_intrs,
        .disable_intrs          = qla24xx_disable_intrs,
        .abort_command          = qla24xx_abort_command,
-       .abort_target           = qla24xx_abort_target,
+       .target_reset           = qla24xx_abort_target,
+       .lun_reset              = qla24xx_lun_reset,
        .fabric_login           = qla24xx_login_fabric,
        .fabric_logout          = qla24xx_fabric_logout,
        .calc_req_entries       = NULL,
@@ -1464,6 +1381,13 @@ qla2x00_set_isp_flags(scsi_qla_host_t *ha)
                ha->device_type |= DT_IIDMA;
                ha->fw_srisc_address = RISC_START_ADDRESS_2400;
                break;
+       case PCI_DEVICE_ID_QLOGIC_ISP8432:
+               ha->device_type |= DT_ISP8432;
+               ha->device_type |= DT_ZIO_SUPPORTED;
+               ha->device_type |= DT_FWI2;
+               ha->device_type |= DT_IIDMA;
+               ha->fw_srisc_address = RISC_START_ADDRESS_2400;
+               break;
        case PCI_DEVICE_ID_QLOGIC_ISP5422:
                ha->device_type |= DT_ISP5422;
                ha->device_type |= DT_FWI2;
@@ -1587,6 +1511,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        sht = &qla2x00_driver_template;
        if (pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2422 ||
            pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2432 ||
+           pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8432 ||
            pdev->device == PCI_DEVICE_ID_QLOGIC_ISP5422 ||
            pdev->device == PCI_DEVICE_ID_QLOGIC_ISP5432 ||
            pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2532) {
@@ -1677,7 +1602,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
                if (IS_QLA2322(ha) || IS_QLA6322(ha))
                        ha->optrom_size = OPTROM_SIZE_2322;
                ha->isp_ops = &qla2300_isp_ops;
-       } else if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) {
+       } else if (IS_QLA24XX_TYPE(ha)) {
                host->max_id = MAX_TARGETS_2200;
                ha->mbx_count = MAILBOX_REGISTER_COUNT;
                ha->request_q_length = REQUEST_ENTRY_CNT_24XX;
@@ -1699,13 +1624,15 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
                ha->gid_list_info_size = 8;
                ha->optrom_size = OPTROM_SIZE_25XX;
                ha->isp_ops = &qla25xx_isp_ops;
+               ha->hw_event_start = PCI_FUNC(pdev->devfn) ?
+                   FA_HW_EVENT1_ADDR: FA_HW_EVENT0_ADDR;
        }
        host->can_queue = ha->request_q_length + 128;
 
        /* load the F/W, read paramaters, and init the H/W */
        ha->instance = num_hosts;
 
-       init_MUTEX(&ha->vport_sem);
+       mutex_init(&ha->vport_lock);
        init_completion(&ha->mbx_cmd_comp);
        complete(&ha->mbx_cmd_comp);
        init_completion(&ha->mbx_intr_comp);
@@ -1713,6 +1640,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
        INIT_LIST_HEAD(&ha->list);
        INIT_LIST_HEAD(&ha->fcports);
        INIT_LIST_HEAD(&ha->vp_list);
+       INIT_LIST_HEAD(&ha->work_list);
 
        set_bit(0, (unsigned long *) ha->vp_idx_map);
 
@@ -1819,6 +1747,8 @@ qla2x00_remove_one(struct pci_dev *pdev)
 
        qla2x00_dfs_remove(ha);
 
+       qla84xx_put_chip(ha);
+
        qla2x00_free_sysfs_attr(ha);
 
        fc_remove_host(ha->host);
@@ -2206,6 +2136,99 @@ qla2x00_mem_free(scsi_qla_host_t *ha)
        kfree(ha->nvram);
 }
 
+static struct qla_work_evt *
+qla2x00_alloc_work(struct scsi_qla_host *ha, enum qla_work_type type,
+    int locked)
+{
+       struct qla_work_evt *e;
+
+       e = kzalloc(sizeof(struct qla_work_evt), locked ? GFP_ATOMIC:
+           GFP_KERNEL);
+       if (!e)
+               return NULL;
+
+       INIT_LIST_HEAD(&e->list);
+       e->type = type;
+       e->flags = QLA_EVT_FLAG_FREE;
+       return e;
+}
+
+static int
+qla2x00_post_work(struct scsi_qla_host *ha, struct qla_work_evt *e, int locked)
+{
+       unsigned long flags;
+       scsi_qla_host_t *pha = to_qla_parent(ha);
+
+       if (!locked)
+               spin_lock_irqsave(&pha->hardware_lock, flags);
+       list_add_tail(&e->list, &ha->work_list);
+       qla2xxx_wake_dpc(ha);
+       if (!locked)
+               spin_unlock_irqrestore(&pha->hardware_lock, flags);
+       return QLA_SUCCESS;
+}
+
+int
+qla2x00_post_aen_work(struct scsi_qla_host *ha, enum fc_host_event_code code,
+    u32 data)
+{
+       struct qla_work_evt *e;
+
+       e = qla2x00_alloc_work(ha, QLA_EVT_AEN, 1);
+       if (!e)
+               return QLA_FUNCTION_FAILED;
+
+       e->u.aen.code = code;
+       e->u.aen.data = data;
+       return qla2x00_post_work(ha, e, 1);
+}
+
+int
+qla2x00_post_hwe_work(struct scsi_qla_host *ha, uint16_t code, uint16_t d1,
+    uint16_t d2, uint16_t d3)
+{
+       struct qla_work_evt *e;
+
+       e = qla2x00_alloc_work(ha, QLA_EVT_HWE_LOG, 1);
+       if (!e)
+               return QLA_FUNCTION_FAILED;
+
+       e->u.hwe.code = code;
+       e->u.hwe.d1 = d1;
+       e->u.hwe.d2 = d2;
+       e->u.hwe.d3 = d3;
+       return qla2x00_post_work(ha, e, 1);
+}
+
+static void
+qla2x00_do_work(struct scsi_qla_host *ha)
+{
+       struct qla_work_evt *e;
+       scsi_qla_host_t *pha = to_qla_parent(ha);
+
+       spin_lock_irq(&pha->hardware_lock);
+       while (!list_empty(&ha->work_list)) {
+               e = list_entry(ha->work_list.next, struct qla_work_evt, list);
+               list_del_init(&e->list);
+               spin_unlock_irq(&pha->hardware_lock);
+
+               switch (e->type) {
+               case QLA_EVT_AEN:
+                       fc_host_post_event(ha->host, fc_get_event_number(),
+                           e->u.aen.code, e->u.aen.data);
+                       break;
+               case QLA_EVT_HWE_LOG:
+                       qla2xxx_hw_event_log(ha, e->u.hwe.code, e->u.hwe.d1,
+                           e->u.hwe.d2, e->u.hwe.d3);
+                       break;
+               }
+               if (e->flags & QLA_EVT_FLAG_FREE)
+                       kfree(e);
+               spin_lock_irq(&pha->hardware_lock);
+       }
+       spin_unlock_irq(&pha->hardware_lock);
+}
+
 /**************************************************************************
 * qla2x00_do_dpc
 *   This kernel thread is a task that is schedule by the interrupt handler
@@ -2257,6 +2280,8 @@ qla2x00_do_dpc(void *data)
                        continue;
                }
 
+               qla2x00_do_work(ha);
+
                if (test_and_clear_bit(ISP_ABORT_NEEDED, &ha->dpc_flags)) {
 
                        DEBUG(printk("scsi(%ld): dpc: sched "
@@ -2291,12 +2316,6 @@ qla2x00_do_dpc(void *data)
                if (test_and_clear_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags))
                        qla2x00_update_fcports(ha);
 
-               if (test_and_clear_bit(LOOP_RESET_NEEDED, &ha->dpc_flags)) {
-                       DEBUG(printk("scsi(%ld): dpc: sched loop_reset()\n",
-                           ha->host_no));
-                       qla2x00_loop_reset(ha);
-               }
-
                if (test_and_clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags) &&
                    (!(test_and_set_bit(RESET_ACTIVE, &ha->dpc_flags)))) {
 
@@ -2357,7 +2376,7 @@ qla2x00_do_dpc(void *data)
                                        } else {
                                                fcport->login_retry = 0;
                                        }
-                                       if (fcport->login_retry == 0)
+                                       if (fcport->login_retry == 0 && status != QLA_SUCCESS)
                                                fcport->loop_id = FC_NO_LOOP_ID;
                                }
                                if (test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags))
@@ -2367,19 +2386,6 @@ qla2x00_do_dpc(void *data)
                            ha->host_no));
                }
 
-               if ((test_bit(LOGIN_RETRY_NEEDED, &ha->dpc_flags)) &&
-                   atomic_read(&ha->loop_state) != LOOP_DOWN) {
-
-                       clear_bit(LOGIN_RETRY_NEEDED, &ha->dpc_flags);
-                       DEBUG(printk("scsi(%ld): qla2x00_login_retry()\n",
-                           ha->host_no));
-
-                       set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
-
-                       DEBUG(printk("scsi(%ld): qla2x00_login_retry - end\n",
-                           ha->host_no));
-               }
-
                if (test_and_clear_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) {
 
                        DEBUG(printk("scsi(%ld): qla2x00_loop_resync()\n",
@@ -2397,18 +2403,6 @@ qla2x00_do_dpc(void *data)
                            ha->host_no));
                }
 
-               if (test_and_clear_bit(FCPORT_RESCAN_NEEDED, &ha->dpc_flags)) {
-
-                       DEBUG(printk("scsi(%ld): Rescan flagged fcports...\n",
-                           ha->host_no));
-
-                       qla2x00_rescan_fcports(ha);
-
-                       DEBUG(printk("scsi(%ld): Rescan flagged fcports..."
-                           "end.\n",
-                           ha->host_no));
-               }
-
                if (!ha->interrupts_on)
                        ha->isp_ops->enable_intrs(ha);
 
@@ -2586,7 +2580,8 @@ qla2x00_timer(scsi_qla_host_t *ha)
                        set_bit(RESTART_QUEUES_NEEDED, &ha->dpc_flags);
                        start_dpc++;
 
-                       if (!(ha->device_flags & DFLG_NO_CABLE)) {
+                       if (!(ha->device_flags & DFLG_NO_CABLE) &&
+                           !ha->parent) {
                                DEBUG(printk("scsi(%ld): Loop down - "
                                    "aborting ISP.\n",
                                    ha->host_no));
@@ -2607,13 +2602,15 @@ qla2x00_timer(scsi_qla_host_t *ha)
                start_dpc++;
        }
 
+       /* Process any deferred work. */
+       if (!list_empty(&ha->work_list))
+               start_dpc++;
+
        /* Schedule the DPC routine if needed */
        if ((test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) ||
            test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags) ||
-           test_bit(LOOP_RESET_NEEDED, &ha->dpc_flags) ||
            test_bit(FCPORT_UPDATE_NEEDED, &ha->dpc_flags) ||
            start_dpc ||
-           test_bit(LOGIN_RETRY_NEEDED, &ha->dpc_flags) ||
            test_bit(RESET_MARKER_NEEDED, &ha->dpc_flags) ||
            test_bit(BEACON_BLINK_NEEDED, &ha->dpc_flags) ||
            test_bit(VP_DPC_NEEDED, &ha->dpc_flags) ||
@@ -2640,7 +2637,7 @@ qla2x00_timer(scsi_qla_host_t *ha)
 #define FW_FILE_ISP24XX        "ql2400_fw.bin"
 #define FW_FILE_ISP25XX        "ql2500_fw.bin"
 
-static DECLARE_MUTEX(qla_fw_lock);
+static DEFINE_MUTEX(qla_fw_lock);
 
 static struct fw_blob qla_fw_blobs[FW_BLOBS] = {
        { .name = FW_FILE_ISP21XX, .segs = { 0x1000, 0 }, },
@@ -2665,13 +2662,13 @@ qla2x00_request_firmware(scsi_qla_host_t *ha)
                blob = &qla_fw_blobs[FW_ISP2300];
        } else if (IS_QLA2322(ha) || IS_QLA6322(ha)) {
                blob = &qla_fw_blobs[FW_ISP2322];
-       } else if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) {
+       } else if (IS_QLA24XX_TYPE(ha)) {
                blob = &qla_fw_blobs[FW_ISP24XX];
        } else if (IS_QLA25XX(ha)) {
                blob = &qla_fw_blobs[FW_ISP25XX];
        }
 
-       down(&qla_fw_lock);
+       mutex_lock(&qla_fw_lock);
        if (blob->fw)
                goto out;
 
@@ -2684,7 +2681,7 @@ qla2x00_request_firmware(scsi_qla_host_t *ha)
        }
 
 out:
-       up(&qla_fw_lock);
+       mutex_unlock(&qla_fw_lock);
        return blob;
 }
 
@@ -2693,11 +2690,11 @@ qla2x00_release_firmware(void)
 {
        int idx;
 
-       down(&qla_fw_lock);
+       mutex_lock(&qla_fw_lock);
        for (idx = 0; idx < FW_BLOBS; idx++)
                if (qla_fw_blobs[idx].fw)
                        release_firmware(qla_fw_blobs[idx].fw);
-       up(&qla_fw_lock);
+       mutex_unlock(&qla_fw_lock);
 }
 
 static pci_ers_result_t
@@ -2815,6 +2812,7 @@ static struct pci_device_id qla2xxx_pci_tbl[] = {
        { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP6322) },
        { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2422) },
        { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2432) },
+       { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8432) },
        { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP5422) },
        { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP5432) },
        { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2532) },
@@ -2869,7 +2867,8 @@ qla2x00_module_init(void)
                return -ENODEV;
        }
 
-       printk(KERN_INFO "QLogic Fibre Channel HBA Driver\n");
+       printk(KERN_INFO "QLogic Fibre Channel HBA Driver: %s\n",
+           qla2x00_version_str);
        ret = pci_register_driver(&qla2xxx_pci_driver);
        if (ret) {
                kmem_cache_destroy(srb_cachep);