[SCSI] qla4xxx: Added support for ISP83XX
authorVikas Chaudhary <vikas.chaudhary@qlogic.com>
Wed, 22 Aug 2012 11:55:08 +0000 (07:55 -0400)
committerJames Bottomley <JBottomley@Parallels.com>
Mon, 24 Sep 2012 08:11:08 +0000 (12:11 +0400)
Signed-off-by: Poornima Vonti <poornima.vonti@qlogic.com>
Signed-off-by: Vikas Chaudhary <vikas.chaudhary@qlogic.com>
Reviewed-by: Mike Christie <michaelc@cs.wisc.edu>
Signed-off-by: James Bottomley <JBottomley@Parallels.com>
16 files changed:
drivers/scsi/qla4xxx/Kconfig
drivers/scsi/qla4xxx/Makefile
drivers/scsi/qla4xxx/ql4_83xx.c [new file with mode: 0644]
drivers/scsi/qla4xxx/ql4_83xx.h [new file with mode: 0644]
drivers/scsi/qla4xxx/ql4_attr.c
drivers/scsi/qla4xxx/ql4_dbg.c
drivers/scsi/qla4xxx/ql4_def.h
drivers/scsi/qla4xxx/ql4_fw.h
drivers/scsi/qla4xxx/ql4_glbl.h
drivers/scsi/qla4xxx/ql4_init.c
drivers/scsi/qla4xxx/ql4_iocb.c
drivers/scsi/qla4xxx/ql4_isr.c
drivers/scsi/qla4xxx/ql4_mbx.c
drivers/scsi/qla4xxx/ql4_nx.c
drivers/scsi/qla4xxx/ql4_nx.h
drivers/scsi/qla4xxx/ql4_os.c

index f1ad02e..e4dc7c7 100644 (file)
@@ -4,5 +4,5 @@ config SCSI_QLA_ISCSI
        select SCSI_ISCSI_ATTRS
        select ISCSI_BOOT_SYSFS
        ---help---
-       This driver supports the QLogic 40xx (ISP4XXX) and 8022 (ISP82XX)
-       iSCSI host adapter family.
+       This driver supports the QLogic 40xx (ISP4XXX), 8022 (ISP82XX)
+       and 8032 (ISP83XX) iSCSI host adapter family.
index 5b44139..4230977 100644 (file)
@@ -1,5 +1,5 @@
 qla4xxx-y := ql4_os.o ql4_init.o ql4_mbx.o ql4_iocb.o ql4_isr.o \
-               ql4_nx.o ql4_nvram.o ql4_dbg.o ql4_attr.o ql4_bsg.o
+               ql4_nx.o ql4_nvram.o ql4_dbg.o ql4_attr.o ql4_bsg.o ql4_83xx.o
 
 obj-$(CONFIG_SCSI_QLA_ISCSI) += qla4xxx.o
 
diff --git a/drivers/scsi/qla4xxx/ql4_83xx.c b/drivers/scsi/qla4xxx/ql4_83xx.c
new file mode 100644 (file)
index 0000000..f963b06
--- /dev/null
@@ -0,0 +1,1468 @@
+/*
+ * QLogic iSCSI HBA Driver
+ * Copyright (c)   2003-2012 QLogic Corporation
+ *
+ * See LICENSE.qla4xxx for copyright and licensing details.
+ */
+
+#include <linux/ratelimit.h>
+
+#include "ql4_def.h"
+#include "ql4_version.h"
+#include "ql4_glbl.h"
+#include "ql4_dbg.h"
+#include "ql4_inline.h"
+
+uint32_t qla4_83xx_rd_reg(struct scsi_qla_host *ha, ulong addr)
+{
+       return readl((void __iomem *)(ha->nx_pcibase + addr));
+}
+
+void qla4_83xx_wr_reg(struct scsi_qla_host *ha, ulong addr, uint32_t val)
+{
+       writel(val, (void __iomem *)(ha->nx_pcibase + addr));
+}
+
+static int qla4_83xx_set_win_base(struct scsi_qla_host *ha, uint32_t addr)
+{
+       uint32_t val;
+       int ret_val = QLA_SUCCESS;
+
+       qla4_83xx_wr_reg(ha, QLA83XX_CRB_WIN_FUNC(ha->func_num), addr);
+       val = qla4_83xx_rd_reg(ha, QLA83XX_CRB_WIN_FUNC(ha->func_num));
+       if (val != addr) {
+               ql4_printk(KERN_ERR, ha, "%s: Failed to set register window : addr written 0x%x, read 0x%x!\n",
+                          __func__, addr, val);
+               ret_val = QLA_ERROR;
+       }
+
+       return ret_val;
+}
+
+int qla4_83xx_rd_reg_indirect(struct scsi_qla_host *ha, uint32_t addr,
+                             uint32_t *data)
+{
+       int ret_val;
+
+       ret_val = qla4_83xx_set_win_base(ha, addr);
+
+       if (ret_val == QLA_SUCCESS)
+               *data = qla4_83xx_rd_reg(ha, QLA83XX_WILDCARD);
+       else
+               ql4_printk(KERN_ERR, ha, "%s: failed read of addr 0x%x!\n",
+                          __func__, addr);
+
+       return ret_val;
+}
+
+int qla4_83xx_wr_reg_indirect(struct scsi_qla_host *ha, uint32_t addr,
+                             uint32_t data)
+{
+       int ret_val;
+
+       ret_val = qla4_83xx_set_win_base(ha, addr);
+
+       if (ret_val == QLA_SUCCESS)
+               qla4_83xx_wr_reg(ha, QLA83XX_WILDCARD, data);
+       else
+               ql4_printk(KERN_ERR, ha, "%s: failed wrt to addr 0x%x, data 0x%x\n",
+                          __func__, addr, data);
+
+       return ret_val;
+}
+
+static int qla4_83xx_flash_lock(struct scsi_qla_host *ha)
+{
+       int lock_owner;
+       int timeout = 0;
+       uint32_t lock_status = 0;
+       int ret_val = QLA_SUCCESS;
+
+       while (lock_status == 0) {
+               lock_status = qla4_83xx_rd_reg(ha, QLA83XX_FLASH_LOCK);
+               if (lock_status)
+                       break;
+
+               if (++timeout >= QLA83XX_FLASH_LOCK_TIMEOUT / 20) {
+                       lock_owner = qla4_83xx_rd_reg(ha,
+                                                     QLA83XX_FLASH_LOCK_ID);
+                       ql4_printk(KERN_ERR, ha, "%s: flash lock by func %d failed, held by func %d\n",
+                                  __func__, ha->func_num, lock_owner);
+                       ret_val = QLA_ERROR;
+                       break;
+               }
+               msleep(20);
+       }
+
+       qla4_83xx_wr_reg(ha, QLA83XX_FLASH_LOCK_ID, ha->func_num);
+       return ret_val;
+}
+
+static void qla4_83xx_flash_unlock(struct scsi_qla_host *ha)
+{
+       /* Reading FLASH_UNLOCK register unlocks the Flash */
+       qla4_83xx_wr_reg(ha, QLA83XX_FLASH_LOCK_ID, 0xFF);
+       qla4_83xx_rd_reg(ha, QLA83XX_FLASH_UNLOCK);
+}
+
+int qla4_83xx_flash_read_u32(struct scsi_qla_host *ha, uint32_t flash_addr,
+                            uint8_t *p_data, int u32_word_count)
+{
+       int i;
+       uint32_t u32_word;
+       uint32_t addr = flash_addr;
+       int ret_val = QLA_SUCCESS;
+
+       ret_val = qla4_83xx_flash_lock(ha);
+       if (ret_val == QLA_ERROR)
+               goto exit_lock_error;
+
+       if (addr & 0x03) {
+               ql4_printk(KERN_ERR, ha, "%s: Illegal addr = 0x%x\n",
+                          __func__, addr);
+               ret_val = QLA_ERROR;
+               goto exit_flash_read;
+       }
+
+       for (i = 0; i < u32_word_count; i++) {
+               ret_val = qla4_83xx_wr_reg_indirect(ha,
+                                                   QLA83XX_FLASH_DIRECT_WINDOW,
+                                                   (addr & 0xFFFF0000));
+               if (ret_val == QLA_ERROR) {
+                       ql4_printk(KERN_ERR, ha, "%s: failed to write addr 0x%x to FLASH_DIRECT_WINDOW\n!",
+                                  __func__, addr);
+                       goto exit_flash_read;
+               }
+
+               ret_val = qla4_83xx_rd_reg_indirect(ha,
+                                               QLA83XX_FLASH_DIRECT_DATA(addr),
+                                               &u32_word);
+               if (ret_val == QLA_ERROR) {
+                       ql4_printk(KERN_ERR, ha, "%s: failed to read addr 0x%x!\n",
+                                  __func__, addr);
+                       goto exit_flash_read;
+               }
+
+               *(__le32 *)p_data = le32_to_cpu(u32_word);
+               p_data = p_data + 4;
+               addr = addr + 4;
+       }
+
+exit_flash_read:
+       qla4_83xx_flash_unlock(ha);
+
+exit_lock_error:
+       return ret_val;
+}
+
+int qla4_83xx_lockless_flash_read_u32(struct scsi_qla_host *ha,
+                                     uint32_t flash_addr, uint8_t *p_data,
+                                     int u32_word_count)
+{
+       uint32_t i;
+       uint32_t u32_word;
+       uint32_t flash_offset;
+       uint32_t addr = flash_addr;
+       int ret_val = QLA_SUCCESS;
+
+       flash_offset = addr & (QLA83XX_FLASH_SECTOR_SIZE - 1);
+
+       if (addr & 0x3) {
+               ql4_printk(KERN_ERR, ha, "%s: Illegal addr = 0x%x\n",
+                          __func__, addr);
+               ret_val = QLA_ERROR;
+               goto exit_lockless_read;
+       }
+
+       ret_val = qla4_83xx_wr_reg_indirect(ha, QLA83XX_FLASH_DIRECT_WINDOW,
+                                           addr);
+       if (ret_val == QLA_ERROR) {
+               ql4_printk(KERN_ERR, ha, "%s: failed to write addr 0x%x to FLASH_DIRECT_WINDOW!\n",
+                          __func__, addr);
+               goto exit_lockless_read;
+       }
+
+       /* Check if data is spread across multiple sectors  */
+       if ((flash_offset + (u32_word_count * sizeof(uint32_t))) >
+           (QLA83XX_FLASH_SECTOR_SIZE - 1)) {
+
+               /* Multi sector read */
+               for (i = 0; i < u32_word_count; i++) {
+                       ret_val = qla4_83xx_rd_reg_indirect(ha,
+                                               QLA83XX_FLASH_DIRECT_DATA(addr),
+                                               &u32_word);
+                       if (ret_val == QLA_ERROR) {
+                               ql4_printk(KERN_ERR, ha, "%s: failed to read addr 0x%x!\n",
+                                          __func__, addr);
+                               goto exit_lockless_read;
+                       }
+
+                       *(__le32 *)p_data  = le32_to_cpu(u32_word);
+                       p_data = p_data + 4;
+                       addr = addr + 4;
+                       flash_offset = flash_offset + 4;
+
+                       if (flash_offset > (QLA83XX_FLASH_SECTOR_SIZE - 1)) {
+                               /* This write is needed once for each sector */
+                               ret_val = qla4_83xx_wr_reg_indirect(ha,
+                                                  QLA83XX_FLASH_DIRECT_WINDOW,
+                                                  addr);
+                               if (ret_val == QLA_ERROR) {
+                                       ql4_printk(KERN_ERR, ha, "%s: failed to write addr 0x%x to FLASH_DIRECT_WINDOW!\n",
+                                                  __func__, addr);
+                                       goto exit_lockless_read;
+                               }
+                               flash_offset = 0;
+                       }
+               }
+       } else {
+               /* Single sector read */
+               for (i = 0; i < u32_word_count; i++) {
+                       ret_val = qla4_83xx_rd_reg_indirect(ha,
+                                               QLA83XX_FLASH_DIRECT_DATA(addr),
+                                               &u32_word);
+                       if (ret_val == QLA_ERROR) {
+                               ql4_printk(KERN_ERR, ha, "%s: failed to read addr 0x%x!\n",
+                                          __func__, addr);
+                               goto exit_lockless_read;
+                       }
+
+                       *(__le32 *)p_data = le32_to_cpu(u32_word);
+                       p_data = p_data + 4;
+                       addr = addr + 4;
+               }
+       }
+
+exit_lockless_read:
+       return ret_val;
+}
+
+void qla4_83xx_rom_lock_recovery(struct scsi_qla_host *ha)
+{
+       if (qla4_83xx_flash_lock(ha))
+               ql4_printk(KERN_INFO, ha, "%s: Resetting rom lock\n", __func__);
+
+       /*
+        * We got the lock, or someone else is holding the lock
+        * since we are restting, forcefully unlock
+        */
+       qla4_83xx_flash_unlock(ha);
+}
+
+/**
+ * qla4_83xx_ms_mem_write_128b - Writes data to MS/off-chip memory
+ * @ha: Pointer to adapter structure
+ * @addr: Flash address to write to
+ * @data: Data to be written
+ * @count: word_count to be written
+ *
+ * Return: On success return QLA_SUCCESS
+ *        On error return QLA_ERROR
+ **/
+static int qla4_83xx_ms_mem_write_128b(struct scsi_qla_host *ha, uint64_t addr,
+                                      uint32_t *data, uint32_t count)
+{
+       int i, j;
+       uint32_t agt_ctrl;
+       unsigned long flags;
+       int ret_val = QLA_SUCCESS;
+
+       /* Only 128-bit aligned access */
+       if (addr & 0xF) {
+               ret_val = QLA_ERROR;
+               goto exit_ms_mem_write;
+       }
+
+       write_lock_irqsave(&ha->hw_lock, flags);
+
+       /* Write address */
+       ret_val = qla4_83xx_wr_reg_indirect(ha, MD_MIU_TEST_AGT_ADDR_HI, 0);
+       if (ret_val == QLA_ERROR) {
+               ql4_printk(KERN_ERR, ha, "%s: write to AGT_ADDR_HI failed\n",
+                          __func__);
+               goto exit_ms_mem_write_unlock;
+       }
+
+       for (i = 0; i < count; i++, addr += 16) {
+               if (!((QLA8XXX_ADDR_IN_RANGE(addr, QLA8XXX_ADDR_QDR_NET,
+                                            QLA8XXX_ADDR_QDR_NET_MAX)) ||
+                     (QLA8XXX_ADDR_IN_RANGE(addr, QLA8XXX_ADDR_DDR_NET,
+                                            QLA8XXX_ADDR_DDR_NET_MAX)))) {
+                       ret_val = QLA_ERROR;
+                       goto exit_ms_mem_write_unlock;
+               }
+
+               ret_val = qla4_83xx_wr_reg_indirect(ha, MD_MIU_TEST_AGT_ADDR_LO,
+                                                   addr);
+               /* Write data */
+               ret_val |= qla4_83xx_wr_reg_indirect(ha,
+                                                    MD_MIU_TEST_AGT_WRDATA_LO,
+                                                    *data++);
+               ret_val |= qla4_83xx_wr_reg_indirect(ha,
+                                                    MD_MIU_TEST_AGT_WRDATA_HI,
+                                                    *data++);
+               ret_val |= qla4_83xx_wr_reg_indirect(ha,
+                                                    MD_MIU_TEST_AGT_WRDATA_ULO,
+                                                    *data++);
+               ret_val |= qla4_83xx_wr_reg_indirect(ha,
+                                                    MD_MIU_TEST_AGT_WRDATA_UHI,
+                                                    *data++);
+               if (ret_val == QLA_ERROR) {
+                       ql4_printk(KERN_ERR, ha, "%s: write to AGT_WRDATA failed\n",
+                                  __func__);
+                       goto exit_ms_mem_write_unlock;
+               }
+
+               /* Check write status */
+               ret_val = qla4_83xx_wr_reg_indirect(ha, MD_MIU_TEST_AGT_CTRL,
+                                                   MIU_TA_CTL_WRITE_ENABLE);
+               ret_val |= qla4_83xx_wr_reg_indirect(ha, MD_MIU_TEST_AGT_CTRL,
+                                                    MIU_TA_CTL_WRITE_START);
+               if (ret_val == QLA_ERROR) {
+                       ql4_printk(KERN_ERR, ha, "%s: write to AGT_CTRL failed\n",
+                                  __func__);
+                       goto exit_ms_mem_write_unlock;
+               }
+
+               for (j = 0; j < MAX_CTL_CHECK; j++) {
+                       ret_val = qla4_83xx_rd_reg_indirect(ha,
+                                                       MD_MIU_TEST_AGT_CTRL,
+                                                       &agt_ctrl);
+                       if (ret_val == QLA_ERROR) {
+                               ql4_printk(KERN_ERR, ha, "%s: failed to read MD_MIU_TEST_AGT_CTRL\n",
+                                          __func__);
+                               goto exit_ms_mem_write_unlock;
+                       }
+                       if ((agt_ctrl & MIU_TA_CTL_BUSY) == 0)
+                               break;
+               }
+
+               /* Status check failed */
+               if (j >= MAX_CTL_CHECK) {
+                       printk_ratelimited(KERN_ERR "%s: MS memory write failed!\n",
+                                          __func__);
+                       ret_val = QLA_ERROR;
+                       goto exit_ms_mem_write_unlock;
+               }
+       }
+
+exit_ms_mem_write_unlock:
+       write_unlock_irqrestore(&ha->hw_lock, flags);
+
+exit_ms_mem_write:
+       return ret_val;
+}
+
+#define INTENT_TO_RECOVER      0x01
+#define PROCEED_TO_RECOVER     0x02
+
+static int qla4_83xx_lock_recovery(struct scsi_qla_host *ha)
+{
+
+       uint32_t lock = 0, lockid;
+       int ret_val = QLA_ERROR;
+
+       lockid = ha->isp_ops->rd_reg_direct(ha, QLA83XX_DRV_LOCKRECOVERY);
+
+       /* Check for other Recovery in progress, go wait */
+       if ((lockid & 0x3) != 0)
+               goto exit_lock_recovery;
+
+       /* Intent to Recover */
+       ha->isp_ops->wr_reg_direct(ha, QLA83XX_DRV_LOCKRECOVERY,
+                                  (ha->func_num << 2) | INTENT_TO_RECOVER);
+
+       msleep(200);
+
+       /* Check Intent to Recover is advertised */
+       lockid = ha->isp_ops->rd_reg_direct(ha, QLA83XX_DRV_LOCKRECOVERY);
+       if ((lockid & 0x3C) != (ha->func_num << 2))
+               goto exit_lock_recovery;
+
+       ql4_printk(KERN_INFO, ha, "%s: IDC Lock recovery initiated for func %d\n",
+                  __func__, ha->func_num);
+
+       /* Proceed to Recover */
+       ha->isp_ops->wr_reg_direct(ha, QLA83XX_DRV_LOCKRECOVERY,
+                                  (ha->func_num << 2) | PROCEED_TO_RECOVER);
+
+       /* Force Unlock */
+       ha->isp_ops->wr_reg_direct(ha, QLA83XX_DRV_LOCK_ID, 0xFF);
+       ha->isp_ops->rd_reg_direct(ha, QLA83XX_DRV_UNLOCK);
+
+       /* Clear bits 0-5 in IDC_RECOVERY register*/
+       ha->isp_ops->wr_reg_direct(ha, QLA83XX_DRV_LOCKRECOVERY, 0);
+
+       /* Get lock */
+       lock = ha->isp_ops->rd_reg_direct(ha, QLA83XX_DRV_LOCK);
+       if (lock) {
+               lockid = ha->isp_ops->rd_reg_direct(ha, QLA83XX_DRV_LOCK_ID);
+               lockid = ((lockid + (1 << 8)) & ~0xFF) | ha->func_num;
+               ha->isp_ops->wr_reg_direct(ha, QLA83XX_DRV_LOCK_ID, lockid);
+               ret_val = QLA_SUCCESS;
+       }
+
+exit_lock_recovery:
+       return ret_val;
+}
+
+#define        QLA83XX_DRV_LOCK_MSLEEP         200
+
+int qla4_83xx_drv_lock(struct scsi_qla_host *ha)
+{
+       int timeout = 0;
+       uint32_t status = 0;
+       int ret_val = QLA_SUCCESS;
+       uint32_t first_owner = 0;
+       uint32_t tmo_owner = 0;
+       uint32_t lock_id;
+       uint32_t func_num;
+       uint32_t lock_cnt;
+
+       while (status == 0) {
+               status = qla4_83xx_rd_reg(ha, QLA83XX_DRV_LOCK);
+               if (status) {
+                       /* Increment Counter (8-31) and update func_num (0-7) on
+                        * getting a successful lock  */
+                       lock_id = qla4_83xx_rd_reg(ha, QLA83XX_DRV_LOCK_ID);
+                       lock_id = ((lock_id + (1 << 8)) & ~0xFF) | ha->func_num;
+                       qla4_83xx_wr_reg(ha, QLA83XX_DRV_LOCK_ID, lock_id);
+                       break;
+               }
+
+               if (timeout == 0)
+                       /* Save counter + ID of function holding the lock for
+                        * first failure */
+                       first_owner = ha->isp_ops->rd_reg_direct(ha,
+                                                         QLA83XX_DRV_LOCK_ID);
+
+               if (++timeout >=
+                   (QLA83XX_DRV_LOCK_TIMEOUT / QLA83XX_DRV_LOCK_MSLEEP)) {
+                       tmo_owner = qla4_83xx_rd_reg(ha, QLA83XX_DRV_LOCK_ID);
+                       func_num = tmo_owner & 0xFF;
+                       lock_cnt = tmo_owner >> 8;
+                       ql4_printk(KERN_INFO, ha, "%s: Lock by func %d failed after 2s, lock held by func %d, lock count %d, first_owner %d\n",
+                                  __func__, ha->func_num, func_num, lock_cnt,
+                                  (first_owner & 0xFF));
+
+                       if (first_owner != tmo_owner) {
+                               /* Some other driver got lock, OR same driver
+                                * got lock again (counter value changed), when
+                                * we were waiting for lock.
+                                * Retry for another 2 sec */
+                               ql4_printk(KERN_INFO, ha, "%s: IDC lock failed for func %d\n",
+                                          __func__, ha->func_num);
+                               timeout = 0;
+                       } else {
+                               /* Same driver holding lock > 2sec.
+                                * Force Recovery */
+                               ret_val = qla4_83xx_lock_recovery(ha);
+                               if (ret_val == QLA_SUCCESS) {
+                                       /* Recovered and got lock */
+                                       ql4_printk(KERN_INFO, ha, "%s: IDC lock Recovery by %d successful\n",
+                                                  __func__, ha->func_num);
+                                       break;
+                               }
+                               /* Recovery Failed, some other function
+                                * has the lock, wait for 2secs and retry */
+                               ql4_printk(KERN_INFO, ha, "%s: IDC lock Recovery by %d failed, Retrying timout\n",
+                                          __func__, ha->func_num);
+                               timeout = 0;
+                       }
+               }
+               msleep(QLA83XX_DRV_LOCK_MSLEEP);
+       }
+
+       return ret_val;
+}
+
+void qla4_83xx_drv_unlock(struct scsi_qla_host *ha)
+{
+       int id;
+
+       id = qla4_83xx_rd_reg(ha, QLA83XX_DRV_LOCK_ID);
+
+       if ((id & 0xFF) != ha->func_num) {
+               ql4_printk(KERN_ERR, ha, "%s: IDC Unlock by %d failed, lock owner is %d\n",
+                          __func__, ha->func_num, (id & 0xFF));
+               return;
+       }
+
+       /* Keep lock counter value, update the ha->func_num to 0xFF */
+       qla4_83xx_wr_reg(ha, QLA83XX_DRV_LOCK_ID, (id | 0xFF));
+       qla4_83xx_rd_reg(ha, QLA83XX_DRV_UNLOCK);
+}
+
+void qla4_83xx_set_idc_dontreset(struct scsi_qla_host *ha)
+{
+       uint32_t idc_ctrl;
+
+       idc_ctrl = qla4_83xx_rd_reg(ha, QLA83XX_IDC_DRV_CTRL);
+       idc_ctrl |= DONTRESET_BIT0;
+       qla4_83xx_wr_reg(ha, QLA83XX_IDC_DRV_CTRL, idc_ctrl);
+       DEBUG2(ql4_printk(KERN_INFO, ha, "%s: idc_ctrl = %d\n", __func__,
+                         idc_ctrl));
+}
+
+void qla4_83xx_clear_idc_dontreset(struct scsi_qla_host *ha)
+{
+       uint32_t idc_ctrl;
+
+       idc_ctrl = qla4_83xx_rd_reg(ha, QLA83XX_IDC_DRV_CTRL);
+       idc_ctrl &= ~DONTRESET_BIT0;
+       qla4_83xx_wr_reg(ha, QLA83XX_IDC_DRV_CTRL, idc_ctrl);
+       DEBUG2(ql4_printk(KERN_INFO, ha, "%s: idc_ctrl = %d\n", __func__,
+                         idc_ctrl));
+}
+
+int qla4_83xx_idc_dontreset(struct scsi_qla_host *ha)
+{
+       uint32_t idc_ctrl;
+
+       idc_ctrl = qla4_83xx_rd_reg(ha, QLA83XX_IDC_DRV_CTRL);
+       return idc_ctrl & DONTRESET_BIT0;
+}
+
+/*-------------------------IDC State Machine ---------------------*/
+
+enum {
+       UNKNOWN_CLASS = 0,
+       NIC_CLASS,
+       FCOE_CLASS,
+       ISCSI_CLASS
+};
+
+struct device_info {
+       int func_num;
+       int device_type;
+       int port_num;
+};
+
+static int qla4_83xx_can_perform_reset(struct scsi_qla_host *ha)
+{
+       uint32_t drv_active;
+       uint32_t dev_part, dev_part1, dev_part2;
+       int i;
+       struct device_info device_map[16];
+       int func_nibble;
+       int nibble;
+       int nic_present = 0;
+       int iscsi_present = 0;
+       int iscsi_func_low = 0;
+
+       /* Use the dev_partition register to determine the PCI function number
+        * and then check drv_active register to see which driver is loaded */
+       dev_part1 = qla4_83xx_rd_reg(ha,
+                                    ha->reg_tbl[QLA8XXX_CRB_DEV_PART_INFO]);
+       dev_part2 = qla4_83xx_rd_reg(ha, QLA83XX_CRB_DEV_PART_INFO2);
+       drv_active = qla4_83xx_rd_reg(ha, ha->reg_tbl[QLA8XXX_CRB_DRV_ACTIVE]);
+
+       /* Each function has 4 bits in dev_partition Info register,
+        * Lower 2 bits - device type, Upper 2 bits - physical port number */
+       dev_part = dev_part1;
+       for (i = nibble = 0; i <= 15; i++, nibble++) {
+               func_nibble = dev_part & (0xF << (nibble * 4));
+               func_nibble >>= (nibble * 4);
+               device_map[i].func_num = i;
+               device_map[i].device_type = func_nibble & 0x3;
+               device_map[i].port_num = func_nibble & 0xC;
+
+               if (device_map[i].device_type == NIC_CLASS) {
+                       if (drv_active & (1 << device_map[i].func_num)) {
+                               nic_present++;
+                               break;
+                       }
+               } else if (device_map[i].device_type == ISCSI_CLASS) {
+                       if (drv_active & (1 << device_map[i].func_num)) {
+                               if (!iscsi_present ||
+                                   (iscsi_present &&
+                                    (iscsi_func_low > device_map[i].func_num)))
+                                       iscsi_func_low = device_map[i].func_num;
+
+                               iscsi_present++;
+                       }
+               }
+
+               /* For function_num[8..15] get info from dev_part2 register */
+               if (nibble == 7) {
+                       nibble = 0;
+                       dev_part = dev_part2;
+               }
+       }
+
+       /* NIC, iSCSI and FCOE are the Reset owners based on order, NIC gets
+        * precedence over iSCSI and FCOE and iSCSI over FCOE, based on drivers
+        * present. */
+       if (!nic_present && (ha->func_num == iscsi_func_low)) {
+               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                 "%s: can reset - NIC not present and lower iSCSI function is %d\n",
+                                 __func__, ha->func_num));
+               return 1;
+       }
+
+       return 0;
+}
+
+/**
+ * qla4_83xx_need_reset_handler - Code to start reset sequence
+ * @ha: pointer to adapter structure
+ *
+ * Note: IDC lock must be held upon entry
+ **/
+void qla4_83xx_need_reset_handler(struct scsi_qla_host *ha)
+{
+       uint32_t dev_state, drv_state, drv_active;
+       unsigned long reset_timeout, dev_init_timeout;
+
+       ql4_printk(KERN_INFO, ha, "%s: Performing ISP error recovery\n",
+                  __func__);
+
+       if (!test_bit(AF_8XXX_RST_OWNER, &ha->flags)) {
+               DEBUG2(ql4_printk(KERN_INFO, ha, "%s: reset acknowledged\n",
+                                 __func__));
+               qla4_8xxx_set_rst_ready(ha);
+
+               /* Non-reset owners ACK Reset and wait for device INIT state
+                * as part of Reset Recovery by Reset Owner */
+               dev_init_timeout = jiffies + (ha->nx_dev_init_timeout * HZ);
+
+               do {
+                       if (time_after_eq(jiffies, dev_init_timeout)) {
+                               ql4_printk(KERN_INFO, ha, "%s: Non Reset owner dev init timeout\n",
+                                          __func__);
+                               break;
+                       }
+
+                       ha->isp_ops->idc_unlock(ha);
+                       msleep(1000);
+                       ha->isp_ops->idc_lock(ha);
+
+                       dev_state = qla4_8xxx_rd_direct(ha,
+                                                       QLA8XXX_CRB_DEV_STATE);
+               } while (dev_state == QLA8XXX_DEV_NEED_RESET);
+       } else {
+               qla4_8xxx_set_rst_ready(ha);
+               reset_timeout = jiffies + (ha->nx_reset_timeout * HZ);
+               drv_state = qla4_8xxx_rd_direct(ha, QLA8XXX_CRB_DRV_STATE);
+               drv_active = qla4_8xxx_rd_direct(ha, QLA8XXX_CRB_DRV_ACTIVE);
+
+               ql4_printk(KERN_INFO, ha, "%s: drv_state = 0x%x, drv_active = 0x%x\n",
+                          __func__, drv_state, drv_active);
+
+               while (drv_state != drv_active) {
+                       if (time_after_eq(jiffies, reset_timeout)) {
+                               ql4_printk(KERN_INFO, ha, "%s: %s: RESET TIMEOUT! drv_state: 0x%08x, drv_active: 0x%08x\n",
+                                          __func__, DRIVER_NAME, drv_state,
+                                          drv_active);
+                               break;
+                       }
+
+                       ha->isp_ops->idc_unlock(ha);
+                       msleep(1000);
+                       ha->isp_ops->idc_lock(ha);
+
+                       drv_state = qla4_8xxx_rd_direct(ha,
+                                                       QLA8XXX_CRB_DRV_STATE);
+                       drv_active = qla4_8xxx_rd_direct(ha,
+                                                       QLA8XXX_CRB_DRV_ACTIVE);
+               }
+
+               if (drv_state != drv_active) {
+                       ql4_printk(KERN_INFO, ha, "%s: Reset_owner turning off drv_active of non-acking function 0x%x\n",
+                                  __func__, (drv_active ^ drv_state));
+                       drv_active = drv_active & drv_state;
+                       qla4_8xxx_wr_direct(ha, QLA8XXX_CRB_DRV_ACTIVE,
+                                           drv_active);
+               }
+
+               clear_bit(AF_8XXX_RST_OWNER, &ha->flags);
+               /* Start Reset Recovery */
+               qla4_8xxx_device_bootstrap(ha);
+       }
+}
+
+void qla4_83xx_get_idc_param(struct scsi_qla_host *ha)
+{
+       uint32_t idc_params, ret_val;
+
+       ret_val = qla4_83xx_flash_read_u32(ha, QLA83XX_IDC_PARAM_ADDR,
+                                          (uint8_t *)&idc_params, 1);
+       if (ret_val == QLA_SUCCESS) {
+               ha->nx_dev_init_timeout = idc_params & 0xFFFF;
+               ha->nx_reset_timeout = (idc_params >> 16) & 0xFFFF;
+       } else {
+               ha->nx_dev_init_timeout = ROM_DEV_INIT_TIMEOUT;
+               ha->nx_reset_timeout = ROM_DRV_RESET_ACK_TIMEOUT;
+       }
+
+       DEBUG2(ql4_printk(KERN_DEBUG, ha,
+                         "%s: ha->nx_dev_init_timeout = %d, ha->nx_reset_timeout = %d\n",
+                         __func__, ha->nx_dev_init_timeout,
+                         ha->nx_reset_timeout));
+}
+
+/*-------------------------Reset Sequence Functions-----------------------*/
+
+static void qla4_83xx_dump_reset_seq_hdr(struct scsi_qla_host *ha)
+{
+       uint8_t *phdr;
+
+       if (!ha->reset_tmplt.buff) {
+               ql4_printk(KERN_ERR, ha, "%s: Error: Invalid reset_seq_template\n",
+                          __func__);
+               return;
+       }
+
+       phdr = ha->reset_tmplt.buff;
+
+       DEBUG2(ql4_printk(KERN_INFO, ha,
+                         "Reset Template: 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n",
+                         *phdr, *(phdr+1), *(phdr+2), *(phdr+3), *(phdr+4),
+                         *(phdr+5), *(phdr+6), *(phdr+7), *(phdr + 8),
+                         *(phdr+9), *(phdr+10), *(phdr+11), *(phdr+12),
+                         *(phdr+13), *(phdr+14), *(phdr+15)));
+}
+
+static int qla4_83xx_copy_bootloader(struct scsi_qla_host *ha)
+{
+       uint8_t *p_cache;
+       uint32_t src, count, size;
+       uint64_t dest;
+       int ret_val = QLA_SUCCESS;
+
+       src = QLA83XX_BOOTLOADER_FLASH_ADDR;
+       dest = qla4_83xx_rd_reg(ha, QLA83XX_BOOTLOADER_ADDR);
+       size = qla4_83xx_rd_reg(ha, QLA83XX_BOOTLOADER_SIZE);
+
+       /* 128 bit alignment check */
+       if (size & 0xF)
+               size = (size + 16) & ~0xF;
+
+       /* 16 byte count */
+       count = size/16;
+
+       p_cache = vmalloc(size);
+       if (p_cache == NULL) {
+               ql4_printk(KERN_ERR, ha, "%s: Failed to allocate memory for boot loader cache\n",
+                          __func__);
+               ret_val = QLA_ERROR;
+               goto exit_copy_bootloader;
+       }
+
+       ret_val = qla4_83xx_lockless_flash_read_u32(ha, src, p_cache,
+                                                   size / sizeof(uint32_t));
+       if (ret_val == QLA_ERROR) {
+               ql4_printk(KERN_ERR, ha, "%s: Error reading firmware from flash\n",
+                          __func__);
+               goto exit_copy_error;
+       }
+       DEBUG2(ql4_printk(KERN_INFO, ha, "%s: Read firmware from flash\n",
+                         __func__));
+
+       /* 128 bit/16 byte write to MS memory */
+       ret_val = qla4_83xx_ms_mem_write_128b(ha, dest, (uint32_t *)p_cache,
+                                             count);
+       if (ret_val == QLA_ERROR) {
+               ql4_printk(KERN_ERR, ha, "%s: Error writing firmware to MS\n",
+                          __func__);
+               goto exit_copy_error;
+       }
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "%s: Wrote firmware size %d to MS\n",
+                         __func__, size));
+
+exit_copy_error:
+       vfree(p_cache);
+
+exit_copy_bootloader:
+       return ret_val;
+}
+
+static int qla4_83xx_check_cmd_peg_status(struct scsi_qla_host *ha)
+{
+       uint32_t val, ret_val = QLA_ERROR;
+       int retries = CRB_CMDPEG_CHECK_RETRY_COUNT;
+
+       do {
+               val = qla4_83xx_rd_reg(ha, QLA83XX_CMDPEG_STATE);
+               if (val == PHAN_INITIALIZE_COMPLETE) {
+                       DEBUG2(ql4_printk(KERN_INFO, ha,
+                                         "%s: Command Peg initialization complete. State=0x%x\n",
+                                         __func__, val));
+                       ret_val = QLA_SUCCESS;
+                       break;
+               }
+               msleep(CRB_CMDPEG_CHECK_DELAY);
+       } while (--retries);
+
+       return ret_val;
+}
+
+/**
+ * qla4_83xx_poll_reg - Poll the given CRB addr for duration msecs till
+ * value read ANDed with test_mask is equal to test_result.
+ *
+ * @ha : Pointer to adapter structure
+ * @addr : CRB register address
+ * @duration : Poll for total of "duration" msecs
+ * @test_mask : Mask value read with "test_mask"
+ * @test_result : Compare (value&test_mask) with test_result.
+ **/
+static int qla4_83xx_poll_reg(struct scsi_qla_host *ha, uint32_t addr,
+                             int duration, uint32_t test_mask,
+                             uint32_t test_result)
+{
+       uint32_t value;
+       uint8_t retries;
+       int ret_val = QLA_SUCCESS;
+
+       ret_val = qla4_83xx_rd_reg_indirect(ha, addr, &value);
+       if (ret_val == QLA_ERROR)
+               goto exit_poll_reg;
+
+       retries = duration / 10;
+       do {
+               if ((value & test_mask) != test_result) {
+                       msleep(duration / 10);
+                       ret_val = qla4_83xx_rd_reg_indirect(ha, addr, &value);
+                       if (ret_val == QLA_ERROR)
+                               goto exit_poll_reg;
+
+                       ret_val = QLA_ERROR;
+               } else {
+                       ret_val = QLA_SUCCESS;
+                       break;
+               }
+       } while (retries--);
+
+exit_poll_reg:
+       if (ret_val == QLA_ERROR) {
+               ha->reset_tmplt.seq_error++;
+               ql4_printk(KERN_ERR, ha, "%s: Poll Failed:  0x%08x 0x%08x 0x%08x\n",
+                          __func__, value, test_mask, test_result);
+       }
+
+       return ret_val;
+}
+
+static int qla4_83xx_reset_seq_checksum_test(struct scsi_qla_host *ha)
+{
+       uint32_t sum =  0;
+       uint16_t *buff = (uint16_t *)ha->reset_tmplt.buff;
+       int u16_count =  ha->reset_tmplt.hdr->size / sizeof(uint16_t);
+       int ret_val;
+
+       while (u16_count-- > 0)
+               sum += *buff++;
+
+       while (sum >> 16)
+               sum = (sum & 0xFFFF) +  (sum >> 16);
+
+       /* checksum of 0 indicates a valid template */
+       if (~sum) {
+               ret_val = QLA_SUCCESS;
+       } else {
+               ql4_printk(KERN_ERR, ha, "%s: Reset seq checksum failed\n",
+                          __func__);
+               ret_val = QLA_ERROR;
+       }
+
+       return ret_val;
+}
+
+/**
+ * qla4_83xx_read_reset_template - Read Reset Template from Flash
+ * @ha: Pointer to adapter structure
+ **/
+void qla4_83xx_read_reset_template(struct scsi_qla_host *ha)
+{
+       uint8_t *p_buff;
+       uint32_t addr, tmplt_hdr_def_size, tmplt_hdr_size;
+       uint32_t ret_val;
+
+       ha->reset_tmplt.seq_error = 0;
+       ha->reset_tmplt.buff = vmalloc(QLA83XX_RESTART_TEMPLATE_SIZE);
+       if (ha->reset_tmplt.buff == NULL) {
+               ql4_printk(KERN_ERR, ha, "%s: Failed to allocate reset template resources\n",
+                          __func__);
+               goto exit_read_reset_template;
+       }
+
+       p_buff = ha->reset_tmplt.buff;
+       addr = QLA83XX_RESET_TEMPLATE_ADDR;
+
+       tmplt_hdr_def_size = sizeof(struct qla4_83xx_reset_template_hdr) /
+                                   sizeof(uint32_t);
+
+       DEBUG2(ql4_printk(KERN_INFO, ha,
+                         "%s: Read template hdr size %d from Flash\n",
+                         __func__, tmplt_hdr_def_size));
+
+       /* Copy template header from flash */
+       ret_val = qla4_83xx_flash_read_u32(ha, addr, p_buff,
+                                          tmplt_hdr_def_size);
+       if (ret_val != QLA_SUCCESS) {
+               ql4_printk(KERN_ERR, ha, "%s: Failed to read reset template\n",
+                          __func__);
+               goto exit_read_template_error;
+       }
+
+       ha->reset_tmplt.hdr =
+               (struct qla4_83xx_reset_template_hdr *)ha->reset_tmplt.buff;
+
+       /* Validate the template header size and signature */
+       tmplt_hdr_size = ha->reset_tmplt.hdr->hdr_size/sizeof(uint32_t);
+       if ((tmplt_hdr_size != tmplt_hdr_def_size) ||
+           (ha->reset_tmplt.hdr->signature != RESET_TMPLT_HDR_SIGNATURE)) {
+               ql4_printk(KERN_ERR, ha, "%s: Template Header size %d is invalid, tmplt_hdr_def_size %d\n",
+                          __func__, tmplt_hdr_size, tmplt_hdr_def_size);
+               goto exit_read_template_error;
+       }
+
+       addr = QLA83XX_RESET_TEMPLATE_ADDR + ha->reset_tmplt.hdr->hdr_size;
+       p_buff = ha->reset_tmplt.buff + ha->reset_tmplt.hdr->hdr_size;
+       tmplt_hdr_def_size = (ha->reset_tmplt.hdr->size -
+                             ha->reset_tmplt.hdr->hdr_size) / sizeof(uint32_t);
+
+       DEBUG2(ql4_printk(KERN_INFO, ha,
+                         "%s: Read rest of the template size %d\n",
+                         __func__, ha->reset_tmplt.hdr->size));
+
+       /* Copy rest of the template */
+       ret_val = qla4_83xx_flash_read_u32(ha, addr, p_buff,
+                                          tmplt_hdr_def_size);
+       if (ret_val != QLA_SUCCESS) {
+               ql4_printk(KERN_ERR, ha, "%s: Failed to read reset tempelate\n",
+                          __func__);
+               goto exit_read_template_error;
+       }
+
+       /* Integrity check */
+       if (qla4_83xx_reset_seq_checksum_test(ha)) {
+               ql4_printk(KERN_ERR, ha, "%s: Reset Seq checksum failed!\n",
+                          __func__);
+               goto exit_read_template_error;
+       }
+       DEBUG2(ql4_printk(KERN_INFO, ha,
+                         "%s: Reset Seq checksum passed, Get stop, start and init seq offsets\n",
+                         __func__));
+
+       /* Get STOP, START, INIT sequence offsets */
+       ha->reset_tmplt.init_offset = ha->reset_tmplt.buff +
+                                     ha->reset_tmplt.hdr->init_seq_offset;
+       ha->reset_tmplt.start_offset = ha->reset_tmplt.buff +
+                                      ha->reset_tmplt.hdr->start_seq_offset;
+       ha->reset_tmplt.stop_offset = ha->reset_tmplt.buff +
+                                     ha->reset_tmplt.hdr->hdr_size;
+       qla4_83xx_dump_reset_seq_hdr(ha);
+
+       goto exit_read_reset_template;
+
+exit_read_template_error:
+       vfree(ha->reset_tmplt.buff);
+
+exit_read_reset_template:
+       return;
+}
+
+/**
+ * qla4_83xx_read_write_crb_reg - Read from raddr and write value to waddr.
+ *
+ * @ha : Pointer to adapter structure
+ * @raddr : CRB address to read from
+ * @waddr : CRB address to write to
+ **/
+static void qla4_83xx_read_write_crb_reg(struct scsi_qla_host *ha,
+                                        uint32_t raddr, uint32_t waddr)
+{
+       uint32_t value;
+
+       qla4_83xx_rd_reg_indirect(ha, raddr, &value);
+       qla4_83xx_wr_reg_indirect(ha, waddr, value);
+}
+
+/**
+ * qla4_83xx_rmw_crb_reg - Read Modify Write crb register
+ *
+ * This function read value from raddr, AND with test_mask,
+ * Shift Left,Right/OR/XOR with values RMW header and write value to waddr.
+ *
+ * @ha : Pointer to adapter structure
+ * @raddr : CRB address to read from
+ * @waddr : CRB address to write to
+ * @p_rmw_hdr : header with shift/or/xor values.
+ **/
+static void qla4_83xx_rmw_crb_reg(struct scsi_qla_host *ha, uint32_t raddr,
+                                 uint32_t waddr,
+                                 struct qla4_83xx_rmw *p_rmw_hdr)
+{
+       uint32_t value;
+
+       if (p_rmw_hdr->index_a)
+               value = ha->reset_tmplt.array[p_rmw_hdr->index_a];
+       else
+               qla4_83xx_rd_reg_indirect(ha, raddr, &value);
+
+       value &= p_rmw_hdr->test_mask;
+       value <<= p_rmw_hdr->shl;
+       value >>= p_rmw_hdr->shr;
+       value |= p_rmw_hdr->or_value;
+       value ^= p_rmw_hdr->xor_value;
+
+       qla4_83xx_wr_reg_indirect(ha, waddr, value);
+
+       return;
+}
+
+static void qla4_83xx_write_list(struct scsi_qla_host *ha,
+                                struct qla4_83xx_reset_entry_hdr *p_hdr)
+{
+       struct qla4_83xx_entry *p_entry;
+       uint32_t i;
+
+       p_entry = (struct qla4_83xx_entry *)
+                 ((char *)p_hdr + sizeof(struct qla4_83xx_reset_entry_hdr));
+
+       for (i = 0; i < p_hdr->count; i++, p_entry++) {
+               qla4_83xx_wr_reg_indirect(ha, p_entry->arg1, p_entry->arg2);
+               if (p_hdr->delay)
+                       udelay((uint32_t)(p_hdr->delay));
+       }
+}
+
+static void qla4_83xx_read_write_list(struct scsi_qla_host *ha,
+                                     struct qla4_83xx_reset_entry_hdr *p_hdr)
+{
+       struct qla4_83xx_entry *p_entry;
+       uint32_t i;
+
+       p_entry = (struct qla4_83xx_entry *)
+                 ((char *)p_hdr + sizeof(struct qla4_83xx_reset_entry_hdr));
+
+       for (i = 0; i < p_hdr->count; i++, p_entry++) {
+               qla4_83xx_read_write_crb_reg(ha, p_entry->arg1, p_entry->arg2);
+               if (p_hdr->delay)
+                       udelay((uint32_t)(p_hdr->delay));
+       }
+}
+
+static void qla4_83xx_poll_list(struct scsi_qla_host *ha,
+                               struct qla4_83xx_reset_entry_hdr *p_hdr)
+{
+       long delay;
+       struct qla4_83xx_entry *p_entry;
+       struct qla4_83xx_poll *p_poll;
+       uint32_t i;
+       uint32_t value;
+
+       p_poll = (struct qla4_83xx_poll *)
+                ((char *)p_hdr + sizeof(struct qla4_83xx_reset_entry_hdr));
+
+       /* Entries start after 8 byte qla4_83xx_poll, poll header contains
+        * the test_mask, test_value. */
+       p_entry = (struct qla4_83xx_entry *)((char *)p_poll +
+                                            sizeof(struct qla4_83xx_poll));
+
+       delay = (long)p_hdr->delay;
+       if (!delay) {
+               for (i = 0; i < p_hdr->count; i++, p_entry++) {
+                       qla4_83xx_poll_reg(ha, p_entry->arg1, delay,
+                                          p_poll->test_mask,
+                                          p_poll->test_value);
+               }
+       } else {
+               for (i = 0; i < p_hdr->count; i++, p_entry++) {
+                       if (qla4_83xx_poll_reg(ha, p_entry->arg1, delay,
+                                              p_poll->test_mask,
+                                              p_poll->test_value)) {
+                               qla4_83xx_rd_reg_indirect(ha, p_entry->arg1,
+                                                         &value);
+                               qla4_83xx_rd_reg_indirect(ha, p_entry->arg2,
+                                                         &value);
+                       }
+               }
+       }
+}
+
+static void qla4_83xx_poll_write_list(struct scsi_qla_host *ha,
+                                     struct qla4_83xx_reset_entry_hdr *p_hdr)
+{
+       long delay;
+       struct qla4_83xx_quad_entry *p_entry;
+       struct qla4_83xx_poll *p_poll;
+       uint32_t i;
+
+       p_poll = (struct qla4_83xx_poll *)
+                ((char *)p_hdr + sizeof(struct qla4_83xx_reset_entry_hdr));
+       p_entry = (struct qla4_83xx_quad_entry *)
+                 ((char *)p_poll + sizeof(struct qla4_83xx_poll));
+       delay = (long)p_hdr->delay;
+
+       for (i = 0; i < p_hdr->count; i++, p_entry++) {
+               qla4_83xx_wr_reg_indirect(ha, p_entry->dr_addr,
+                                         p_entry->dr_value);
+               qla4_83xx_wr_reg_indirect(ha, p_entry->ar_addr,
+                                         p_entry->ar_value);
+               if (delay) {
+                       if (qla4_83xx_poll_reg(ha, p_entry->ar_addr, delay,
+                                              p_poll->test_mask,
+                                              p_poll->test_value)) {
+                               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                                 "%s: Timeout Error: poll list, item_num %d, entry_num %d\n",
+                                                 __func__, i,
+                                                 ha->reset_tmplt.seq_index));
+                       }
+               }
+       }
+}
+
+static void qla4_83xx_read_modify_write(struct scsi_qla_host *ha,
+                                       struct qla4_83xx_reset_entry_hdr *p_hdr)
+{
+       struct qla4_83xx_entry *p_entry;
+       struct qla4_83xx_rmw *p_rmw_hdr;
+       uint32_t i;
+
+       p_rmw_hdr = (struct qla4_83xx_rmw *)
+                   ((char *)p_hdr + sizeof(struct qla4_83xx_reset_entry_hdr));
+       p_entry = (struct qla4_83xx_entry *)
+                 ((char *)p_rmw_hdr + sizeof(struct qla4_83xx_rmw));
+
+       for (i = 0; i < p_hdr->count; i++, p_entry++) {
+               qla4_83xx_rmw_crb_reg(ha, p_entry->arg1, p_entry->arg2,
+                                     p_rmw_hdr);
+               if (p_hdr->delay)
+                       udelay((uint32_t)(p_hdr->delay));
+       }
+}
+
+static void qla4_83xx_pause(struct scsi_qla_host *ha,
+                           struct qla4_83xx_reset_entry_hdr *p_hdr)
+{
+       if (p_hdr->delay)
+               mdelay((uint32_t)((long)p_hdr->delay));
+}
+
+static void qla4_83xx_poll_read_list(struct scsi_qla_host *ha,
+                                    struct qla4_83xx_reset_entry_hdr *p_hdr)
+{
+       long delay;
+       int index;
+       struct qla4_83xx_quad_entry *p_entry;
+       struct qla4_83xx_poll *p_poll;
+       uint32_t i;
+       uint32_t value;
+
+       p_poll = (struct qla4_83xx_poll *)
+                ((char *)p_hdr + sizeof(struct qla4_83xx_reset_entry_hdr));
+       p_entry = (struct qla4_83xx_quad_entry *)
+                 ((char *)p_poll + sizeof(struct qla4_83xx_poll));
+       delay = (long)p_hdr->delay;
+
+       for (i = 0; i < p_hdr->count; i++, p_entry++) {
+               qla4_83xx_wr_reg_indirect(ha, p_entry->ar_addr,
+                                         p_entry->ar_value);
+               if (delay) {
+                       if (qla4_83xx_poll_reg(ha, p_entry->ar_addr, delay,
+                                              p_poll->test_mask,
+                                              p_poll->test_value)) {
+                               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                                 "%s: Timeout Error: poll list, Item_num %d, entry_num %d\n",
+                                                 __func__, i,
+                                                 ha->reset_tmplt.seq_index));
+                       } else {
+                               index = ha->reset_tmplt.array_index;
+                               qla4_83xx_rd_reg_indirect(ha, p_entry->dr_addr,
+                                                         &value);
+                               ha->reset_tmplt.array[index++] = value;
+
+                               if (index == QLA83XX_MAX_RESET_SEQ_ENTRIES)
+                                       ha->reset_tmplt.array_index = 1;
+                       }
+               }
+       }
+}
+
+static void qla4_83xx_seq_end(struct scsi_qla_host *ha,
+                             struct qla4_83xx_reset_entry_hdr *p_hdr)
+{
+       ha->reset_tmplt.seq_end = 1;
+}
+
+static void qla4_83xx_template_end(struct scsi_qla_host *ha,
+                                  struct qla4_83xx_reset_entry_hdr *p_hdr)
+{
+       ha->reset_tmplt.template_end = 1;
+
+       if (ha->reset_tmplt.seq_error == 0) {
+               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                 "%s: Reset sequence completed SUCCESSFULLY.\n",
+                                 __func__));
+       } else {
+               ql4_printk(KERN_ERR, ha, "%s: Reset sequence completed with some timeout errors.\n",
+                          __func__);
+       }
+}
+
+/**
+ * qla4_83xx_process_reset_template - Process reset template.
+ *
+ * Process all entries in reset template till entry with SEQ_END opcode,
+ * which indicates end of the reset template processing. Each entry has a
+ * Reset Entry header, entry opcode/command, with size of the entry, number
+ * of entries in sub-sequence and delay in microsecs or timeout in millisecs.
+ *
+ * @ha : Pointer to adapter structure
+ * @p_buff : Common reset entry header.
+ **/
+static void qla4_83xx_process_reset_template(struct scsi_qla_host *ha,
+                                            char *p_buff)
+{
+       int index, entries;
+       struct qla4_83xx_reset_entry_hdr *p_hdr;
+       char *p_entry = p_buff;
+
+       ha->reset_tmplt.seq_end = 0;
+       ha->reset_tmplt.template_end = 0;
+       entries = ha->reset_tmplt.hdr->entries;
+       index = ha->reset_tmplt.seq_index;
+
+       for (; (!ha->reset_tmplt.seq_end) && (index  < entries); index++) {
+
+               p_hdr = (struct qla4_83xx_reset_entry_hdr *)p_entry;
+               switch (p_hdr->cmd) {
+               case OPCODE_NOP:
+                       break;
+               case OPCODE_WRITE_LIST:
+                       qla4_83xx_write_list(ha, p_hdr);
+                       break;
+               case OPCODE_READ_WRITE_LIST:
+                       qla4_83xx_read_write_list(ha, p_hdr);
+                       break;
+               case OPCODE_POLL_LIST:
+                       qla4_83xx_poll_list(ha, p_hdr);
+                       break;
+               case OPCODE_POLL_WRITE_LIST:
+                       qla4_83xx_poll_write_list(ha, p_hdr);
+                       break;
+               case OPCODE_READ_MODIFY_WRITE:
+                       qla4_83xx_read_modify_write(ha, p_hdr);
+                       break;
+               case OPCODE_SEQ_PAUSE:
+                       qla4_83xx_pause(ha, p_hdr);
+                       break;
+               case OPCODE_SEQ_END:
+                       qla4_83xx_seq_end(ha, p_hdr);
+                       break;
+               case OPCODE_TMPL_END:
+                       qla4_83xx_template_end(ha, p_hdr);
+                       break;
+               case OPCODE_POLL_READ_LIST:
+                       qla4_83xx_poll_read_list(ha, p_hdr);
+                       break;
+               default:
+                       ql4_printk(KERN_ERR, ha, "%s: Unknown command ==> 0x%04x on entry = %d\n",
+                                  __func__, p_hdr->cmd, index);
+                       break;
+               }
+
+               /* Set pointer to next entry in the sequence. */
+               p_entry += p_hdr->size;
+       }
+
+       ha->reset_tmplt.seq_index = index;
+}
+
+static void qla4_83xx_process_stop_seq(struct scsi_qla_host *ha)
+{
+       ha->reset_tmplt.seq_index = 0;
+       qla4_83xx_process_reset_template(ha, ha->reset_tmplt.stop_offset);
+
+       if (ha->reset_tmplt.seq_end != 1)
+               ql4_printk(KERN_ERR, ha, "%s: Abrupt STOP Sub-Sequence end.\n",
+                          __func__);
+}
+
+static void qla4_83xx_process_start_seq(struct scsi_qla_host *ha)
+{
+       qla4_83xx_process_reset_template(ha, ha->reset_tmplt.start_offset);
+
+       if (ha->reset_tmplt.template_end != 1)
+               ql4_printk(KERN_ERR, ha, "%s: Abrupt START Sub-Sequence end.\n",
+                          __func__);
+}
+
+static void qla4_83xx_process_init_seq(struct scsi_qla_host *ha)
+{
+       qla4_83xx_process_reset_template(ha, ha->reset_tmplt.init_offset);
+
+       if (ha->reset_tmplt.seq_end != 1)
+               ql4_printk(KERN_ERR, ha, "%s: Abrupt INIT Sub-Sequence end.\n",
+                          __func__);
+}
+
+static int qla4_83xx_restart(struct scsi_qla_host *ha)
+{
+       int ret_val = QLA_SUCCESS;
+
+       qla4_83xx_process_stop_seq(ha);
+
+       /* Collect minidump*/
+       if (!test_and_clear_bit(AF_83XX_NO_FW_DUMP, &ha->flags))
+               qla4_8xxx_get_minidump(ha);
+
+       qla4_83xx_process_init_seq(ha);
+
+       if (qla4_83xx_copy_bootloader(ha)) {
+               ql4_printk(KERN_ERR, ha, "%s: Copy bootloader, firmware restart failed!\n",
+                          __func__);
+               ret_val = QLA_ERROR;
+               goto exit_restart;
+       }
+
+       qla4_83xx_wr_reg(ha, QLA83XX_FW_IMAGE_VALID, QLA83XX_BOOT_FROM_FLASH);
+       qla4_83xx_process_start_seq(ha);
+
+exit_restart:
+       return ret_val;
+}
+
+int qla4_83xx_start_firmware(struct scsi_qla_host *ha)
+{
+       int ret_val = QLA_SUCCESS;
+
+       ret_val = qla4_83xx_restart(ha);
+       if (ret_val == QLA_ERROR) {
+               ql4_printk(KERN_ERR, ha, "%s: Restart error\n", __func__);
+               goto exit_start_fw;
+       } else {
+               DEBUG2(ql4_printk(KERN_INFO, ha, "%s: Restart done\n",
+                                 __func__));
+       }
+
+       ret_val = qla4_83xx_check_cmd_peg_status(ha);
+       if (ret_val == QLA_ERROR)
+               ql4_printk(KERN_ERR, ha, "%s: Peg not initialized\n",
+                          __func__);
+
+exit_start_fw:
+       return ret_val;
+}
+
+/*----------------------Interrupt Related functions ---------------------*/
+
+void qla4_83xx_disable_intrs(struct scsi_qla_host *ha)
+{
+       uint32_t mb_int, ret;
+
+       if (test_and_clear_bit(AF_INTERRUPTS_ON, &ha->flags))
+               qla4_8xxx_mbx_intr_disable(ha);
+
+       ret = readl(&ha->qla4_83xx_reg->mbox_int);
+       mb_int = ret & ~INT_ENABLE_FW_MB;
+       writel(mb_int, &ha->qla4_83xx_reg->mbox_int);
+       writel(1, &ha->qla4_83xx_reg->leg_int_mask);
+}
+
+void qla4_83xx_enable_intrs(struct scsi_qla_host *ha)
+{
+       uint32_t mb_int;
+
+       qla4_8xxx_mbx_intr_enable(ha);
+       mb_int = INT_ENABLE_FW_MB;
+       writel(mb_int, &ha->qla4_83xx_reg->mbox_int);
+       writel(0, &ha->qla4_83xx_reg->leg_int_mask);
+
+       set_bit(AF_INTERRUPTS_ON, &ha->flags);
+}
+
+void qla4_83xx_queue_mbox_cmd(struct scsi_qla_host *ha, uint32_t *mbx_cmd,
+                             int incount)
+{
+       int i;
+
+       /* Load all mailbox registers, except mailbox 0. */
+       for (i = 1; i < incount; i++)
+               writel(mbx_cmd[i], &ha->qla4_83xx_reg->mailbox_in[i]);
+
+       writel(mbx_cmd[0], &ha->qla4_83xx_reg->mailbox_in[0]);
+
+       /* Set Host Interrupt register to 1, to tell the firmware that
+        * a mailbox command is pending. Firmware after reading the
+        * mailbox command, clears the host interrupt register */
+       writel(HINT_MBX_INT_PENDING, &ha->qla4_83xx_reg->host_intr);
+}
+
+void qla4_83xx_process_mbox_intr(struct scsi_qla_host *ha, int outcount)
+{
+       int intr_status;
+
+       intr_status = readl(&ha->qla4_83xx_reg->risc_intr);
+       if (intr_status) {
+               ha->mbox_status_count = outcount;
+               ha->isp_ops->interrupt_service_routine(ha, intr_status);
+       }
+       writel(0, &ha->qla4_83xx_reg->risc_intr);
+}
+
+/**
+ * qla4_83xx_isp_reset - Resets ISP and aborts all outstanding commands.
+ * @ha: pointer to host adapter structure.
+ **/
+int qla4_83xx_isp_reset(struct scsi_qla_host *ha)
+{
+       int rval;
+       uint32_t dev_state;
+
+       ha->isp_ops->idc_lock(ha);
+       dev_state = qla4_8xxx_rd_direct(ha, QLA8XXX_CRB_DEV_STATE);
+
+       if (ql4xdontresethba)
+               qla4_83xx_set_idc_dontreset(ha);
+
+       if (dev_state == QLA8XXX_DEV_READY) {
+               /* If IDC_CTRL DONTRESETHBA_BIT0 is set dont do reset
+                * recovery */
+               if (qla4_83xx_idc_dontreset(ha) == DONTRESET_BIT0) {
+                       ql4_printk(KERN_ERR, ha, "%s: Reset recovery disabled\n",
+                                  __func__);
+                       rval = QLA_ERROR;
+                       goto exit_isp_reset;
+               }
+
+               DEBUG2(ql4_printk(KERN_INFO, ha, "%s: HW State: NEED RESET\n",
+                                 __func__));
+               qla4_8xxx_wr_direct(ha, QLA8XXX_CRB_DEV_STATE,
+                                   QLA8XXX_DEV_NEED_RESET);
+
+       } else {
+               /* If device_state is NEED_RESET, go ahead with
+                * Reset,irrespective of ql4xdontresethba. This is to allow a
+                * non-reset-owner to force a reset. Non-reset-owner sets
+                * the IDC_CTRL BIT0 to prevent Reset-owner from doing a Reset
+                * and then forces a Reset by setting device_state to
+                * NEED_RESET. */
+               DEBUG2(ql4_printk(KERN_INFO, ha,
+                                 "%s: HW state already set to NEED_RESET\n",
+                                 __func__));
+       }
+
+       /* For ISP8324, Reset owner is NIC, iSCSI or FCOE based on priority
+        * and which drivers are present. Unlike ISP8022, the function setting
+        * NEED_RESET, may not be the Reset owner. */
+       if (qla4_83xx_can_perform_reset(ha))
+               set_bit(AF_8XXX_RST_OWNER, &ha->flags);
+
+       ha->isp_ops->idc_unlock(ha);
+       rval = qla4_8xxx_device_state_handler(ha);
+
+       ha->isp_ops->idc_lock(ha);
+       qla4_8xxx_clear_rst_ready(ha);
+exit_isp_reset:
+       ha->isp_ops->idc_unlock(ha);
+
+       if (rval == QLA_SUCCESS)
+               clear_bit(AF_FW_RECOVERY, &ha->flags);
+
+       return rval;
+}
diff --git a/drivers/scsi/qla4xxx/ql4_83xx.h b/drivers/scsi/qla4xxx/ql4_83xx.h
new file mode 100644 (file)
index 0000000..a679263
--- /dev/null
@@ -0,0 +1,262 @@
+/*
+ * QLogic iSCSI HBA Driver
+ * Copyright (c)  2003-2012 QLogic Corporation
+ *
+ * See LICENSE.qla4xxx for copyright and licensing details.
+ */
+
+#ifndef __QL483XX_H
+#define __QL483XX_H
+
+/* Indirectly Mapped Registers */
+#define QLA83XX_FLASH_SPI_STATUS       0x2808E010
+#define QLA83XX_FLASH_SPI_CONTROL      0x2808E014
+#define QLA83XX_FLASH_STATUS           0x42100004
+#define QLA83XX_FLASH_CONTROL          0x42110004
+#define QLA83XX_FLASH_ADDR             0x42110008
+#define QLA83XX_FLASH_WRDATA           0x4211000C
+#define QLA83XX_FLASH_RDDATA           0x42110018
+#define QLA83XX_FLASH_DIRECT_WINDOW    0x42110030
+#define QLA83XX_FLASH_DIRECT_DATA(DATA) (0x42150000 | (0x0000FFFF&DATA))
+
+/* Directly Mapped Registers in 83xx register table */
+
+/* Flash access regs */
+#define QLA83XX_FLASH_LOCK             0x3850
+#define QLA83XX_FLASH_UNLOCK           0x3854
+#define QLA83XX_FLASH_LOCK_ID          0x3500
+
+/* Driver Lock regs */
+#define QLA83XX_DRV_LOCK               0x3868
+#define QLA83XX_DRV_UNLOCK             0x386C
+#define QLA83XX_DRV_LOCK_ID            0x3504
+#define QLA83XX_DRV_LOCKRECOVERY       0x379C
+
+/* IDC version */
+#define QLA83XX_IDC_VER_MAJ_VALUE       0x1
+#define QLA83XX_IDC_VER_MIN_VALUE       0x0
+
+/* IDC Registers : Driver Coexistence Defines */
+#define QLA83XX_CRB_IDC_VER_MAJOR      0x3780
+#define QLA83XX_CRB_IDC_VER_MINOR      0x3798
+#define QLA83XX_IDC_DRV_CTRL           0x3790
+#define QLA83XX_IDC_DRV_AUDIT          0x3794
+
+/* qla_83xx_reg_tbl registers */
+#define QLA83XX_PEG_HALT_STATUS1       0x34A8
+#define QLA83XX_PEG_HALT_STATUS2       0x34AC
+#define QLA83XX_PEG_ALIVE_COUNTER      0x34B0 /* FW_HEARTBEAT */
+#define QLA83XX_FW_CAPABILITIES                0x3528
+#define QLA83XX_CRB_DRV_ACTIVE         0x3788 /* IDC_DRV_PRESENCE */
+#define QLA83XX_CRB_DEV_STATE          0x3784 /* IDC_DEV_STATE */
+#define QLA83XX_CRB_DRV_STATE          0x378C /* IDC_DRV_ACK */
+#define QLA83XX_CRB_DRV_SCRATCH                0x3548
+#define QLA83XX_CRB_DEV_PART_INFO1     0x37E0
+#define QLA83XX_CRB_DEV_PART_INFO2     0x37E4
+
+#define QLA83XX_FW_VER_MAJOR           0x3550
+#define QLA83XX_FW_VER_MINOR           0x3554
+#define QLA83XX_FW_VER_SUB             0x3558
+#define QLA83XX_NPAR_STATE             0x359C
+#define QLA83XX_FW_IMAGE_VALID         0x35FC
+#define QLA83XX_CMDPEG_STATE           0x3650
+#define QLA83XX_ASIC_TEMP              0x37B4
+#define QLA83XX_FW_API                 0x356C
+#define QLA83XX_DRV_OP_MODE            0x3570
+
+static const uint32_t qla4_83xx_reg_tbl[] = {
+       QLA83XX_PEG_HALT_STATUS1,
+       QLA83XX_PEG_HALT_STATUS2,
+       QLA83XX_PEG_ALIVE_COUNTER,
+       QLA83XX_CRB_DRV_ACTIVE,
+       QLA83XX_CRB_DEV_STATE,
+       QLA83XX_CRB_DRV_STATE,
+       QLA83XX_CRB_DRV_SCRATCH,
+       QLA83XX_CRB_DEV_PART_INFO1,
+       QLA83XX_CRB_IDC_VER_MAJOR,
+       QLA83XX_FW_VER_MAJOR,
+       QLA83XX_FW_VER_MINOR,
+       QLA83XX_FW_VER_SUB,
+       QLA83XX_CMDPEG_STATE,
+       QLA83XX_ASIC_TEMP,
+};
+
+#define QLA83XX_CRB_WIN_BASE           0x3800
+#define QLA83XX_CRB_WIN_FUNC(f)                (QLA83XX_CRB_WIN_BASE+((f)*4))
+#define QLA83XX_SEM_LOCK_BASE          0x3840
+#define QLA83XX_SEM_UNLOCK_BASE                0x3844
+#define QLA83XX_SEM_LOCK_FUNC(f)       (QLA83XX_SEM_LOCK_BASE+((f)*8))
+#define QLA83XX_SEM_UNLOCK_FUNC(f)     (QLA83XX_SEM_UNLOCK_BASE+((f)*8))
+#define QLA83XX_LINK_STATE(f)          (0x3698+((f) > 7 ? 4 : 0))
+#define QLA83XX_LINK_SPEED(f)          (0x36E0+(((f) >> 2) * 4))
+#define QLA83XX_MAX_LINK_SPEED(f)       (0x36F0+(((f) / 4) * 4))
+#define QLA83XX_LINK_SPEED_FACTOR      10
+
+/* FLASH API Defines */
+#define QLA83xx_FLASH_MAX_WAIT_USEC    100
+#define QLA83XX_FLASH_LOCK_TIMEOUT     10000
+#define QLA83XX_FLASH_SECTOR_SIZE      65536
+#define QLA83XX_DRV_LOCK_TIMEOUT       2000
+#define QLA83XX_FLASH_SECTOR_ERASE_CMD 0xdeadbeef
+#define QLA83XX_FLASH_WRITE_CMD                0xdacdacda
+#define QLA83XX_FLASH_BUFFER_WRITE_CMD 0xcadcadca
+#define QLA83XX_FLASH_READ_RETRY_COUNT 2000
+#define QLA83XX_FLASH_STATUS_READY     0x6
+#define QLA83XX_FLASH_BUFFER_WRITE_MIN 2
+#define QLA83XX_FLASH_BUFFER_WRITE_MAX 64
+#define QLA83XX_FLASH_STATUS_REG_POLL_DELAY 1
+#define QLA83XX_ERASE_MODE             1
+#define QLA83XX_WRITE_MODE             2
+#define QLA83XX_DWORD_WRITE_MODE       3
+
+#define QLA83XX_GLOBAL_RESET           0x38CC
+#define QLA83XX_WILDCARD               0x38F0
+#define QLA83XX_INFORMANT              0x38FC
+#define QLA83XX_HOST_MBX_CTRL          0x3038
+#define QLA83XX_FW_MBX_CTRL            0x303C
+#define QLA83XX_BOOTLOADER_ADDR                0x355C
+#define QLA83XX_BOOTLOADER_SIZE                0x3560
+#define QLA83XX_FW_IMAGE_ADDR          0x3564
+#define QLA83XX_MBX_INTR_ENABLE                0x1000
+#define QLA83XX_MBX_INTR_MASK          0x1200
+
+/* IDC Control Register bit defines */
+#define DONTRESET_BIT0         0x1
+#define GRACEFUL_RESET_BIT1    0x2
+
+#define QLA83XX_HALT_STATUS_INFORMATIONAL      (0x1 << 29)
+#define QLA83XX_HALT_STATUS_FW_RESET           (0x2 << 29)
+#define QLA83XX_HALT_STATUS_UNRECOVERABLE      (0x4 << 29)
+
+/* Firmware image definitions */
+#define QLA83XX_BOOTLOADER_FLASH_ADDR  0x10000
+#define QLA83XX_BOOT_FROM_FLASH                0
+
+#define QLA83XX_IDC_PARAM_ADDR         0x3e8020
+/* Reset template definitions */
+#define QLA83XX_MAX_RESET_SEQ_ENTRIES  16
+#define QLA83XX_RESTART_TEMPLATE_SIZE  0x2000
+#define QLA83XX_RESET_TEMPLATE_ADDR    0x4F0000
+#define QLA83XX_RESET_SEQ_VERSION      0x0101
+
+/* Reset template entry opcodes */
+#define OPCODE_NOP                     0x0000
+#define OPCODE_WRITE_LIST              0x0001
+#define OPCODE_READ_WRITE_LIST         0x0002
+#define OPCODE_POLL_LIST               0x0004
+#define OPCODE_POLL_WRITE_LIST         0x0008
+#define OPCODE_READ_MODIFY_WRITE       0x0010
+#define OPCODE_SEQ_PAUSE               0x0020
+#define OPCODE_SEQ_END                 0x0040
+#define OPCODE_TMPL_END                        0x0080
+#define OPCODE_POLL_READ_LIST          0x0100
+
+/* Template Header */
+#define RESET_TMPLT_HDR_SIGNATURE      0xCAFE
+struct qla4_83xx_reset_template_hdr {
+       __le16  version;
+       __le16  signature;
+       __le16  size;
+       __le16  entries;
+       __le16  hdr_size;
+       __le16  checksum;
+       __le16  init_seq_offset;
+       __le16  start_seq_offset;
+} __packed;
+
+/* Common Entry Header. */
+struct qla4_83xx_reset_entry_hdr {
+       __le16 cmd;
+       __le16 size;
+       __le16 count;
+       __le16 delay;
+} __packed;
+
+/* Generic poll entry type. */
+struct qla4_83xx_poll {
+       __le32  test_mask;
+       __le32  test_value;
+} __packed;
+
+/* Read modify write entry type. */
+struct qla4_83xx_rmw {
+       __le32  test_mask;
+       __le32  xor_value;
+       __le32  or_value;
+       uint8_t shl;
+       uint8_t shr;
+       uint8_t index_a;
+       uint8_t rsvd;
+} __packed;
+
+/* Generic Entry Item with 2 DWords. */
+struct qla4_83xx_entry {
+       __le32 arg1;
+       __le32 arg2;
+} __packed;
+
+/* Generic Entry Item with 4 DWords.*/
+struct qla4_83xx_quad_entry {
+       __le32 dr_addr;
+       __le32 dr_value;
+       __le32 ar_addr;
+       __le32 ar_value;
+} __packed;
+
+struct qla4_83xx_reset_template {
+       int seq_index;
+       int seq_error;
+       int array_index;
+       uint32_t array[QLA83XX_MAX_RESET_SEQ_ENTRIES];
+       uint8_t *buff;
+       uint8_t *stop_offset;
+       uint8_t *start_offset;
+       uint8_t *init_offset;
+       struct qla4_83xx_reset_template_hdr *hdr;
+       uint8_t seq_end;
+       uint8_t template_end;
+};
+
+/* POLLRD Entry */
+struct qla83xx_minidump_entry_pollrd {
+       struct qla8xxx_minidump_entry_hdr h;
+       uint32_t select_addr;
+       uint32_t read_addr;
+       uint32_t select_value;
+       uint16_t select_value_stride;
+       uint16_t op_count;
+       uint32_t poll_wait;
+       uint32_t poll_mask;
+       uint32_t data_size;
+       uint32_t rsvd_1;
+};
+
+/* RDMUX2 Entry */
+struct qla83xx_minidump_entry_rdmux2 {
+       struct qla8xxx_minidump_entry_hdr h;
+       uint32_t select_addr_1;
+       uint32_t select_addr_2;
+       uint32_t select_value_1;
+       uint32_t select_value_2;
+       uint32_t op_count;
+       uint32_t select_value_mask;
+       uint32_t read_addr;
+       uint8_t select_value_stride;
+       uint8_t data_size;
+       uint8_t rsvd[2];
+};
+
+/* POLLRDMWR Entry */
+struct qla83xx_minidump_entry_pollrdmwr {
+       struct qla8xxx_minidump_entry_hdr h;
+       uint32_t addr_1;
+       uint32_t addr_2;
+       uint32_t value_1;
+       uint32_t value_2;
+       uint32_t poll_wait;
+       uint32_t poll_mask;
+       uint32_t modify_mask;
+       uint32_t data_size;
+};
+
+#endif
index a24008f..76819b7 100644 (file)
@@ -150,7 +150,7 @@ qla4xxx_fw_version_show(struct device *dev,
 {
        struct scsi_qla_host *ha = to_qla_host(class_to_shost(dev));
 
-       if (is_qla8022(ha))
+       if (is_qla80XX(ha))
                return snprintf(buf, PAGE_SIZE, "%d.%02d.%02d (%x)\n",
                                ha->firmware_version[0],
                                ha->firmware_version[1],
index ea6af8c..f2a841f 100644 (file)
@@ -131,3 +131,31 @@ void qla4xxx_dump_registers(struct scsi_qla_host *ha)
                    &ha->reg->ctrl_status);
        }
 }
+
+void qla4_8xxx_dump_peg_reg(struct scsi_qla_host *ha)
+{
+       uint32_t halt_status1, halt_status2;
+
+       halt_status1 = qla4_8xxx_rd_direct(ha, QLA8XXX_PEG_HALT_STATUS1);
+       halt_status2 = qla4_8xxx_rd_direct(ha, QLA8XXX_PEG_HALT_STATUS2);
+
+       if (is_qla8022(ha)) {
+               ql4_printk(KERN_INFO, ha,
+                          "scsi(%ld): %s, ISP8022 Dumping hw/fw registers:\n"
+                          " PEG_HALT_STATUS1: 0x%x, PEG_HALT_STATUS2: 0x%x,\n"
+                          " PEG_NET_0_PC: 0x%x, PEG_NET_1_PC: 0x%x,\n"
+                          " PEG_NET_2_PC: 0x%x, PEG_NET_3_PC: 0x%x,\n"
+                          " PEG_NET_4_PC: 0x%x\n", ha->host_no,
+                          __func__, halt_status1, halt_status2,
+                          qla4_82xx_rd_32(ha, QLA82XX_CRB_PEG_NET_0 + 0x3c),
+                          qla4_82xx_rd_32(ha, QLA82XX_CRB_PEG_NET_1 + 0x3c),
+                          qla4_82xx_rd_32(ha, QLA82XX_CRB_PEG_NET_2 + 0x3c),
+                          qla4_82xx_rd_32(ha, QLA82XX_CRB_PEG_NET_3 + 0x3c),
+                          qla4_82xx_rd_32(ha, QLA82XX_CRB_PEG_NET_4 + 0x3c));
+       } else if (is_qla8032(ha)) {
+               ql4_printk(KERN_INFO, ha,
+                          "scsi(%ld): %s, ISP8324 Dumping hw/fw registers:\n"
+                          " PEG_HALT_STATUS1: 0x%x, PEG_HALT_STATUS2: 0x%x,\n",
+                          ha->host_no, __func__, halt_status1, halt_status2);
+       }
+}
index 11271a2..58b52cf 100644 (file)
@@ -42,6 +42,7 @@
 #include "ql4_nx.h"
 #include "ql4_fw.h"
 #include "ql4_nvram.h"
+#include "ql4_83xx.h"
 
 #ifndef PCI_DEVICE_ID_QLOGIC_ISP4010
 #define PCI_DEVICE_ID_QLOGIC_ISP4010   0x4010
 #define PCI_DEVICE_ID_QLOGIC_ISP8022   0x8022
 #endif
 
+#ifndef PCI_DEVICE_ID_QLOGIC_ISP8324
+#define PCI_DEVICE_ID_QLOGIC_ISP8324   0x8032
+#endif
+
 #define ISP4XXX_PCI_FN_1       0x1
 #define ISP4XXX_PCI_FN_2       0x3
 
@@ -510,6 +515,7 @@ struct scsi_qla_host {
 #define AF_82XX_FW_DUMPED              24 /* 0x01000000 */
 #define AF_8XXX_RST_OWNER              25 /* 0x02000000 */
 #define AF_82XX_DUMP_READING           26 /* 0x04000000 */
+#define AF_83XX_NO_FW_DUMP             27 /* 0x08000000 */
 
        unsigned long dpc_flags;
 
@@ -746,6 +752,10 @@ struct scsi_qla_host {
        uint32_t mrb_index;
 
        uint32_t *reg_tbl;
+       struct qla4_83xx_reset_template reset_tmplt;
+       struct device_reg_83xx  __iomem *qla4_83xx_reg; /* Base I/O address
+                                                          for ISP8324 */
+       uint32_t pf_bit;
 };
 
 struct ql4_task_data {
@@ -808,13 +818,20 @@ static inline int is_qla8022(struct scsi_qla_host *ha)
        return ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8022;
 }
 
-/* Note: Currently AER/EEH is now supported only for 8022 cards
- * This function needs to be updated when AER/EEH is enabled
- * for other cards.
- */
+static inline int is_qla8032(struct scsi_qla_host *ha)
+{
+       return ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8324;
+}
+
+static inline int is_qla80XX(struct scsi_qla_host *ha)
+{
+       return is_qla8022(ha) || is_qla8032(ha);
+}
+
 static inline int is_aer_supported(struct scsi_qla_host *ha)
 {
-       return ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8022;
+       return ((ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8022) ||
+               (ha->pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8324));
 }
 
 static inline int adapter_up(struct scsi_qla_host *ha)
index 037d380..3f36950 100644 (file)
@@ -65,6 +65,40 @@ struct device_reg_82xx {
 #define ISRX_82XX_RISC_INT     BIT_0 /* RISC interrupt. */
 };
 
+/* ISP 83xx I/O Register Set structure */
+struct device_reg_83xx {
+       __le32 mailbox_in[16];  /* 0x0000 */
+       __le32 reserve1[496];   /* 0x0040 */
+       __le32 mailbox_out[16]; /* 0x0800 */
+       __le32 reserve2[496];
+       __le32 mbox_int;        /* 0x1000 */
+       __le32 reserve3[63];
+       __le32 req_q_out;       /* 0x1100 */
+       __le32 reserve4[63];
+
+       __le32 rsp_q_in;        /* 0x1200 */
+       __le32 reserve5[1919];
+
+       __le32 req_q_in;        /* 0x3000 */
+       __le32 reserve6[3];
+       __le32 iocb_int_mask;   /* 0x3010 */
+       __le32 reserve7[3];
+       __le32 rsp_q_out;       /* 0x3020 */
+       __le32 reserve8[3];
+       __le32 anonymousbuff;   /* 0x3030 */
+       __le32 mb_int_mask;     /* 0x3034 */
+
+       __le32 host_intr;       /* 0x3038 - Host Interrupt Register */
+       __le32 risc_intr;       /* 0x303C - RISC Interrupt Register */
+       __le32 reserve9[544];
+       __le32 leg_int_ptr;     /* 0x38C0 - Legacy Interrupt Pointer Register */
+       __le32 leg_int_trig;    /* 0x38C4 - Legacy Interrupt Trigger Control */
+       __le32 leg_int_mask;    /* 0x38C8 - Legacy Interrupt Mask Register */
+};
+
+#define INT_ENABLE_FW_MB       (1 << 2)
+#define INT_MASK_FW_MB         (1 << 2)
+
 /*  remote register set (access via PCI memory read/write) */
 struct isp_reg {
 #define MBOX_REG_COUNT 8
@@ -1198,6 +1232,9 @@ struct ql_iscsi_stats {
 #define QLA8XXX_DBG_STATE_ARRAY_LEN            16
 #define QLA8XXX_DBG_CAP_SIZE_ARRAY_LEN         8
 #define QLA8XXX_DBG_RSVD_ARRAY_LEN             8
+#define QLA83XX_DBG_OCM_WNDREG_ARRAY_LEN       16
+#define QLA83XX_SS_OCM_WNDREG_INDEX            3
+#define QLA83XX_SS_PCI_INDEX                   0
 
 struct qla4_8xxx_minidump_template_hdr {
        uint32_t entry_type;
@@ -1216,6 +1253,7 @@ struct qla4_8xxx_minidump_template_hdr {
 
        uint32_t saved_state_array[QLA8XXX_DBG_STATE_ARRAY_LEN];
        uint32_t capture_size_array[QLA8XXX_DBG_CAP_SIZE_ARRAY_LEN];
+       uint32_t ocm_window_reg[QLA83XX_DBG_OCM_WNDREG_ARRAY_LEN];
 };
 
 #endif /*  _QLA4X_FW_H */
index 1010d71..0c6acad 100644 (file)
@@ -214,6 +214,47 @@ void qla4_82xx_process_mbox_intr(struct scsi_qla_host *ha, int outcount);
 void qla4xxx_queue_mbox_cmd(struct scsi_qla_host *ha, uint32_t *mbx_cmd,
                            int incount);
 void qla4xxx_process_mbox_intr(struct scsi_qla_host *ha, int outcount);
+void qla4_8xxx_dump_peg_reg(struct scsi_qla_host *ha);
+void qla4_83xx_disable_intrs(struct scsi_qla_host *ha);
+void qla4_83xx_enable_intrs(struct scsi_qla_host *ha);
+int qla4_83xx_start_firmware(struct scsi_qla_host *ha);
+irqreturn_t qla4_83xx_intr_handler(int irq, void *dev_id);
+void qla4_83xx_interrupt_service_routine(struct scsi_qla_host *ha,
+                                        uint32_t intr_status);
+int qla4_83xx_isp_reset(struct scsi_qla_host *ha);
+void qla4_83xx_queue_iocb(struct scsi_qla_host *ha);
+void qla4_83xx_complete_iocb(struct scsi_qla_host *ha);
+uint16_t qla4_83xx_rd_shdw_req_q_out(struct scsi_qla_host *ha);
+uint16_t qla4_83xx_rd_shdw_rsp_q_in(struct scsi_qla_host *ha);
+uint32_t qla4_83xx_rd_reg(struct scsi_qla_host *ha, ulong addr);
+void qla4_83xx_wr_reg(struct scsi_qla_host *ha, ulong addr, uint32_t val);
+int qla4_83xx_rd_reg_indirect(struct scsi_qla_host *ha, uint32_t addr,
+                             uint32_t *data);
+int qla4_83xx_wr_reg_indirect(struct scsi_qla_host *ha, uint32_t addr,
+                             uint32_t data);
+int qla4_83xx_drv_lock(struct scsi_qla_host *ha);
+void qla4_83xx_drv_unlock(struct scsi_qla_host *ha);
+void qla4_83xx_rom_lock_recovery(struct scsi_qla_host *ha);
+void qla4_83xx_queue_mbox_cmd(struct scsi_qla_host *ha, uint32_t *mbx_cmd,
+                             int incount);
+void qla4_83xx_process_mbox_intr(struct scsi_qla_host *ha, int outcount);
+void qla4_83xx_read_reset_template(struct scsi_qla_host *ha);
+void qla4_83xx_set_idc_dontreset(struct scsi_qla_host *ha);
+int qla4_83xx_idc_dontreset(struct scsi_qla_host *ha);
+int qla4_83xx_lockless_flash_read_u32(struct scsi_qla_host *ha,
+                                     uint32_t flash_addr, uint8_t *p_data,
+                                     int u32_word_count);
+void qla4_83xx_clear_idc_dontreset(struct scsi_qla_host *ha);
+void qla4_83xx_need_reset_handler(struct scsi_qla_host *ha);
+int qla4_83xx_flash_read_u32(struct scsi_qla_host *ha, uint32_t flash_addr,
+                            uint8_t *p_data, int u32_word_count);
+void qla4_83xx_get_idc_param(struct scsi_qla_host *ha);
+void qla4_8xxx_set_rst_ready(struct scsi_qla_host *ha);
+void qla4_8xxx_clear_rst_ready(struct scsi_qla_host *ha);
+int qla4_8xxx_device_bootstrap(struct scsi_qla_host *ha);
+void qla4_8xxx_get_minidump(struct scsi_qla_host *ha);
+int qla4_8xxx_mbx_intr_disable(struct scsi_qla_host *ha);
+int qla4_8xxx_mbx_intr_enable(struct scsi_qla_host *ha);
 
 extern int ql4xextended_error_logging;
 extern int ql4xdontresethba;
index 6bc983d..a1881d0 100644 (file)
@@ -107,6 +107,13 @@ int qla4xxx_init_rings(struct scsi_qla_host *ha)
                    (unsigned long  __iomem *)&ha->qla4_82xx_reg->rsp_q_in);
                writel(0,
                    (unsigned long  __iomem *)&ha->qla4_82xx_reg->rsp_q_out);
+       } else if (is_qla8032(ha)) {
+               writel(0,
+                      (unsigned long __iomem *)&ha->qla4_83xx_reg->req_q_in);
+               writel(0,
+                      (unsigned long __iomem *)&ha->qla4_83xx_reg->rsp_q_in);
+               writel(0,
+                      (unsigned long __iomem *)&ha->qla4_83xx_reg->rsp_q_out);
        } else {
                /*
                 * Initialize DMA Shadow registers.  The firmware is really
@@ -524,7 +531,7 @@ static int qla4xxx_init_firmware(struct scsi_qla_host *ha)
        /* For 82xx, stop firmware before initializing because if BIOS
         * has previously initialized firmware, then driver's initialize
         * firmware will fail. */
-       if (is_qla8022(ha))
+       if (is_qla80XX(ha))
                qla4_8xxx_stop_firmware(ha);
 
        ql4_printk(KERN_INFO, ha, "Initializing firmware..\n");
@@ -537,7 +544,7 @@ static int qla4xxx_init_firmware(struct scsi_qla_host *ha)
        if (!qla4xxx_fw_ready(ha))
                return status;
 
-       if (is_qla8022(ha) && !test_bit(AF_INIT_DONE, &ha->flags))
+       if (is_qla80XX(ha) && !test_bit(AF_INIT_DONE, &ha->flags))
                qla4xxx_alloc_fw_dump(ha);
 
        return qla4xxx_get_firmware_status(ha);
@@ -946,9 +953,9 @@ int qla4xxx_initialize_adapter(struct scsi_qla_host *ha, int is_reset)
 
        set_bit(AF_ONLINE, &ha->flags);
 exit_init_hba:
-       if (is_qla8022(ha) && (status == QLA_ERROR)) {
+       if (is_qla80XX(ha) && (status == QLA_ERROR)) {
                /* Since interrupts are registered in start_firmware for
-                * 82xx, release them here if initialize_adapter fails */
+                * 80XX, release them here if initialize_adapter fails */
                qla4xxx_free_irqs(ha);
        }
 
index 1def688..b6a4e36 100644 (file)
@@ -192,6 +192,18 @@ static void qla4xxx_build_scsi_iocbs(struct srb *srb,
        }
 }
 
+void qla4_83xx_queue_iocb(struct scsi_qla_host *ha)
+{
+       writel(ha->request_in, &ha->qla4_83xx_reg->req_q_in);
+       readl(&ha->qla4_83xx_reg->req_q_in);
+}
+
+void qla4_83xx_complete_iocb(struct scsi_qla_host *ha)
+{
+       writel(ha->response_out, &ha->qla4_83xx_reg->rsp_q_out);
+       readl(&ha->qla4_83xx_reg->rsp_q_out);
+}
+
 /**
  * qla4_82xx_queue_iocb - Tell ISP it's got new request(s)
  * @ha: pointer to host adapter structure.
index 55d366b..cb78e9c 100644 (file)
@@ -126,7 +126,7 @@ static void qla4xxx_status_entry(struct scsi_qla_host *ha,
                ql4_printk(KERN_WARNING, ha, "%s invalid status entry: "
                           "handle=0x%0x, srb=%p\n", __func__,
                           sts_entry->handle, srb);
-               if (is_qla8022(ha))
+               if (is_qla80XX(ha))
                        set_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags);
                else
                        set_bit(DPC_RESET_HA, &ha->dpc_flags);
@@ -594,6 +594,14 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha,
 {
        int i;
        uint32_t mbox_sts[MBOX_AEN_REG_COUNT];
+       __le32 __iomem *mailbox_out;
+
+       if (is_qla8032(ha))
+               mailbox_out = &ha->qla4_83xx_reg->mailbox_out[0];
+       else if (is_qla8022(ha))
+               mailbox_out = &ha->qla4_82xx_reg->mailbox_out[0];
+       else
+               mailbox_out = &ha->reg->mailbox[0];
 
        if ((mbox_status == MBOX_STS_BUSY) ||
            (mbox_status == MBOX_STS_INTERMEDIATE_COMPLETION) ||
@@ -606,9 +614,7 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha,
                         * location and set mailbox command done flag
                         */
                        for (i = 0; i < ha->mbox_status_count; i++)
-                               ha->mbox_status[i] = is_qla8022(ha)
-                                   ? readl(&ha->qla4_82xx_reg->mailbox_out[i])
-                                   : readl(&ha->reg->mailbox[i]);
+                               ha->mbox_status[i] = readl(&mailbox_out[i]);
 
                        set_bit(AF_MBOX_COMMAND_DONE, &ha->flags);
 
@@ -617,9 +623,7 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha,
                }
        } else if (mbox_status >> 12 == MBOX_ASYNC_EVENT_STATUS) {
                for (i = 0; i < MBOX_AEN_REG_COUNT; i++)
-                       mbox_sts[i] = is_qla8022(ha)
-                           ? readl(&ha->qla4_82xx_reg->mailbox_out[i])
-                           : readl(&ha->reg->mailbox[i]);
+                       mbox_sts[i] = readl(&mailbox_out[i]);
 
                /* Immediately process the AENs that don't require much work.
                 * Only queue the database_changed AENs */
@@ -635,7 +639,8 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha,
                        ql4_printk(KERN_INFO, ha, "%s: System Err\n", __func__);
                        qla4xxx_dump_registers(ha);
 
-                       if (ql4xdontresethba) {
+                       if ((is_qla8022(ha) && ql4xdontresethba) ||
+                           (is_qla8032(ha) && qla4_83xx_idc_dontreset(ha))) {
                                DEBUG2(printk("scsi%ld: %s:Don't Reset HBA\n",
                                    ha->host_no, __func__));
                        } else {
@@ -651,7 +656,7 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha,
                case MBOX_ASTS_DHCP_LEASE_EXPIRED:
                        DEBUG2(printk("scsi%ld: AEN %04x, ERROR Status, "
                                      "Reset HA\n", ha->host_no, mbox_status));
-                       if (is_qla8022(ha))
+                       if (is_qla80XX(ha))
                                set_bit(DPC_RESET_HA_FW_CONTEXT,
                                        &ha->dpc_flags);
                        else
@@ -716,7 +721,7 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha,
                                set_bit(DPC_GET_DHCP_IP_ADDR, &ha->dpc_flags);
                        else if ((mbox_sts[3] == ACB_STATE_ACQUIRING) &&
                                 (mbox_sts[2] == ACB_STATE_VALID)) {
-                               if (is_qla8022(ha))
+                               if (is_qla80XX(ha))
                                        set_bit(DPC_RESET_HA_FW_CONTEXT,
                                                &ha->dpc_flags);
                                else
@@ -815,6 +820,23 @@ static void qla4xxx_isr_decode_mailbox(struct scsi_qla_host * ha,
        }
 }
 
+void qla4_83xx_interrupt_service_routine(struct scsi_qla_host *ha,
+                                        uint32_t intr_status)
+{
+       /* Process mailbox/asynch event interrupt.*/
+       if (intr_status) {
+               qla4xxx_isr_decode_mailbox(ha,
+                               readl(&ha->qla4_83xx_reg->mailbox_out[0]));
+               /* clear the interrupt */
+               writel(0, &ha->qla4_83xx_reg->risc_intr);
+       } else {
+               qla4xxx_process_response_queue(ha);
+       }
+
+       /* clear the interrupt */
+       writel(0, &ha->qla4_83xx_reg->mb_int_mask);
+}
+
 /**
  * qla4_82xx_interrupt_service_routine - isr
  * @ha: pointer to host adapter structure.
@@ -1045,6 +1067,59 @@ irqreturn_t qla4_82xx_intr_handler(int irq, void *dev_id)
        return IRQ_HANDLED;
 }
 
+#define LEG_INT_PTR_B31                (1 << 31)
+#define LEG_INT_PTR_B30                (1 << 30)
+#define PF_BITS_MASK           (0xF << 16)
+
+/**
+ * qla4_83xx_intr_handler - hardware interrupt handler.
+ * @irq: Unused
+ * @dev_id: Pointer to host adapter structure
+ **/
+irqreturn_t qla4_83xx_intr_handler(int irq, void *dev_id)
+{
+       struct scsi_qla_host *ha = dev_id;
+       uint32_t leg_int_ptr = 0;
+       unsigned long flags = 0;
+
+       ha->isr_count++;
+       leg_int_ptr = readl(&ha->qla4_83xx_reg->leg_int_ptr);
+
+       /* Legacy interrupt is valid if bit31 of leg_int_ptr is set */
+       if (!(leg_int_ptr & LEG_INT_PTR_B31)) {
+               ql4_printk(KERN_ERR, ha,
+                          "%s: Legacy Interrupt Bit 31 not set, spurious interrupt!\n",
+                          __func__);
+               return IRQ_NONE;
+       }
+
+       /* Validate the PCIE function ID set in leg_int_ptr bits [19..16] */
+       if ((leg_int_ptr & PF_BITS_MASK) != ha->pf_bit) {
+               ql4_printk(KERN_ERR, ha,
+                          "%s: Incorrect function ID 0x%x in legacy interrupt register, ha->pf_bit = 0x%x\n",
+                          __func__, (leg_int_ptr & PF_BITS_MASK), ha->pf_bit);
+               return IRQ_NONE;
+       }
+
+       /* To de-assert legacy interrupt, write 0 to Legacy Interrupt Trigger
+        * Control register and poll till Legacy Interrupt Pointer register
+        * bit30 is 0.
+        */
+       writel(0, &ha->qla4_83xx_reg->leg_int_trig);
+       do {
+               leg_int_ptr = readl(&ha->qla4_83xx_reg->leg_int_ptr);
+               if ((leg_int_ptr & PF_BITS_MASK) != ha->pf_bit)
+                       break;
+       } while (leg_int_ptr & LEG_INT_PTR_B30);
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+       leg_int_ptr = readl(&ha->qla4_83xx_reg->risc_intr);
+       ha->isp_ops->interrupt_service_routine(ha, leg_int_ptr);
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+       return IRQ_HANDLED;
+}
+
 irqreturn_t
 qla4_8xxx_msi_handler(int irq, void *dev_id)
 {
@@ -1068,6 +1143,37 @@ qla4_8xxx_msi_handler(int irq, void *dev_id)
        return qla4_8xxx_default_intr_handler(irq, dev_id);
 }
 
+static irqreturn_t qla4_83xx_mailbox_intr_handler(int irq, void *dev_id)
+{
+       struct scsi_qla_host *ha = dev_id;
+       unsigned long flags;
+       uint32_t ival = 0;
+
+       spin_lock_irqsave(&ha->hardware_lock, flags);
+
+       ival = readl(&ha->qla4_83xx_reg->risc_intr);
+       if (ival == 0) {
+               ql4_printk(KERN_INFO, ha,
+                          "%s: It is a spurious mailbox interrupt!\n",
+                          __func__);
+               ival = readl(&ha->qla4_83xx_reg->mb_int_mask);
+               ival &= ~INT_MASK_FW_MB;
+               writel(ival, &ha->qla4_83xx_reg->mb_int_mask);
+               goto exit;
+       }
+
+       qla4xxx_isr_decode_mailbox(ha,
+                                  readl(&ha->qla4_83xx_reg->mailbox_out[0]));
+       writel(0, &ha->qla4_83xx_reg->risc_intr);
+       ival = readl(&ha->qla4_83xx_reg->mb_int_mask);
+       ival &= ~INT_MASK_FW_MB;
+       writel(ival, &ha->qla4_83xx_reg->mb_int_mask);
+       ha->isr_count++;
+exit:
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
+       return IRQ_HANDLED;
+}
+
 /**
  * qla4_8xxx_default_intr_handler - hardware interrupt handler.
  * @irq: Unused
@@ -1084,29 +1190,32 @@ qla4_8xxx_default_intr_handler(int irq, void *dev_id)
        uint32_t intr_status;
        uint8_t reqs_count = 0;
 
-       spin_lock_irqsave(&ha->hardware_lock, flags);
-       while (1) {
-               if (!(readl(&ha->qla4_82xx_reg->host_int) &
-                   ISRX_82XX_RISC_INT)) {
-                       qla4_82xx_spurious_interrupt(ha, reqs_count);
-                       break;
-               }
+       if (is_qla8032(ha)) {
+               qla4_83xx_mailbox_intr_handler(irq, dev_id);
+       } else {
+               spin_lock_irqsave(&ha->hardware_lock, flags);
+               while (1) {
+                       if (!(readl(&ha->qla4_82xx_reg->host_int) &
+                           ISRX_82XX_RISC_INT)) {
+                               qla4_82xx_spurious_interrupt(ha, reqs_count);
+                               break;
+                       }
 
-               intr_status =  readl(&ha->qla4_82xx_reg->host_status);
-               if ((intr_status &
-                   (HSRX_RISC_MB_INT | HSRX_RISC_IOCB_INT)) == 0) {
-                       qla4_82xx_spurious_interrupt(ha, reqs_count);
-                       break;
-               }
+                       intr_status =  readl(&ha->qla4_82xx_reg->host_status);
+                       if ((intr_status &
+                           (HSRX_RISC_MB_INT | HSRX_RISC_IOCB_INT)) == 0) {
+                               qla4_82xx_spurious_interrupt(ha, reqs_count);
+                               break;
+                       }
 
-               ha->isp_ops->interrupt_service_routine(ha, intr_status);
+                       ha->isp_ops->interrupt_service_routine(ha, intr_status);
 
-               if (++reqs_count == MAX_REQS_SERVICED_PER_INTR)
-                       break;
+                       if (++reqs_count == MAX_REQS_SERVICED_PER_INTR)
+                               break;
+               }
+               ha->isr_count++;
+               spin_unlock_irqrestore(&ha->hardware_lock, flags);
        }
-
-       ha->isr_count++;
-       spin_unlock_irqrestore(&ha->hardware_lock, flags);
        return IRQ_HANDLED;
 }
 
@@ -1115,13 +1224,25 @@ qla4_8xxx_msix_rsp_q(int irq, void *dev_id)
 {
        struct scsi_qla_host *ha = dev_id;
        unsigned long flags;
+       uint32_t ival = 0;
 
        spin_lock_irqsave(&ha->hardware_lock, flags);
-       qla4xxx_process_response_queue(ha);
-       writel(0, &ha->qla4_82xx_reg->host_int);
-       spin_unlock_irqrestore(&ha->hardware_lock, flags);
-
+       if (is_qla8032(ha)) {
+               ival = readl(&ha->qla4_83xx_reg->iocb_int_mask);
+               if (ival == 0) {
+                       ql4_printk(KERN_INFO, ha, "%s: It is a spurious iocb interrupt!\n",
+                                  __func__);
+                       goto exit_msix_rsp_q;
+               }
+               qla4xxx_process_response_queue(ha);
+               writel(0, &ha->qla4_83xx_reg->iocb_int_mask);
+       } else {
+               qla4xxx_process_response_queue(ha);
+               writel(0, &ha->qla4_82xx_reg->host_int);
+       }
        ha->isr_count++;
+exit_msix_rsp_q:
+       spin_unlock_irqrestore(&ha->hardware_lock, flags);
        return IRQ_HANDLED;
 }
 
@@ -1196,8 +1317,15 @@ int qla4xxx_request_irqs(struct scsi_qla_host *ha)
        if (is_qla40XX(ha))
                goto try_intx;
 
-       if (ql4xenablemsix == 2)
+       if (ql4xenablemsix == 2) {
+               /* Note: MSI Interrupts not supported for ISP8324 */
+               if (is_qla8032(ha)) {
+                       ql4_printk(KERN_INFO, ha, "%s: MSI Interrupts not supported for ISP8324, Falling back-to INTx mode\n",
+                                  __func__);
+                       goto try_intx;
+               }
                goto try_msi;
+       }
 
        if (ql4xenablemsix == 0 || ql4xenablemsix != 1)
                goto try_intx;
@@ -1208,6 +1336,12 @@ int qla4xxx_request_irqs(struct scsi_qla_host *ha)
                DEBUG2(ql4_printk(KERN_INFO, ha,
                    "MSI-X: Enabled (0x%X).\n", ha->revision_id));
                goto irq_attached;
+       } else {
+               if (is_qla8032(ha)) {
+                       ql4_printk(KERN_INFO, ha, "%s: ISP8324: MSI-X: Falling back-to INTx mode. ret = %d\n",
+                                  __func__, ret);
+                       goto try_intx;
+               }
        }
 
        ql4_printk(KERN_WARNING, ha,
index 73324fb..80fa20d 100644 (file)
@@ -107,7 +107,7 @@ int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount,
                msleep(10);
        }
 
-       if (is_qla8022(ha)) {
+       if (is_qla80XX(ha)) {
                if (test_bit(AF_FW_RECOVERY, &ha->flags)) {
                        DEBUG2(ql4_printk(KERN_WARNING, ha,
                                          "scsi%ld: %s: prematurely completing mbx cmd as firmware recovery detected\n",
@@ -183,7 +183,7 @@ int qla4xxx_mailbox_command(struct scsi_qla_host *ha, uint8_t inCount,
 
        /* Check for mailbox timeout. */
        if (!test_bit(AF_MBOX_COMMAND_DONE, &ha->flags)) {
-               if (is_qla8022(ha) &&
+               if (is_qla80XX(ha) &&
                    test_bit(AF_FW_RECOVERY, &ha->flags)) {
                        DEBUG2(ql4_printk(KERN_INFO, ha,
                            "scsi%ld: %s: prematurely completing mbx cmd as "
@@ -544,7 +544,7 @@ int qla4xxx_initialize_fw_cb(struct scsi_qla_host * ha)
                __constant_cpu_to_le16(FWOPT_SESSION_MODE |
                                       FWOPT_INITIATOR_MODE);
 
-       if (is_qla8022(ha))
+       if (is_qla80XX(ha))
                init_fw_cb->fw_options |=
                    __constant_cpu_to_le16(FWOPT_ENABLE_CRBDB);
 
index 6daa25c..3e55608 100644 (file)
@@ -10,6 +10,7 @@
 #include <linux/ratelimit.h>
 #include "ql4_def.h"
 #include "ql4_glbl.h"
+#include "ql4_inline.h"
 
 #include <asm-generic/io-64-nonatomic-lo-hi.h>
 
@@ -1511,7 +1512,17 @@ qla4_8xxx_set_drv_active(struct scsi_qla_host *ha)
        uint32_t drv_active;
 
        drv_active = qla4_8xxx_rd_direct(ha, QLA8XXX_CRB_DRV_ACTIVE);
-       drv_active |= (1 << (ha->func_num * 4));
+
+       /*
+        * For ISP8324, drv_active register has 1 bit per function,
+        * shift 1 by func_num to set a bit for the function.
+        * For ISP8022, drv_active has 4 bits per function
+        */
+       if (is_qla8032(ha))
+               drv_active |= (1 << ha->func_num);
+       else
+               drv_active |= (1 << (ha->func_num * 4));
+
        ql4_printk(KERN_INFO, ha, "%s(%ld): drv_active: 0x%08x\n",
                   __func__, ha->host_no, drv_active);
        qla4_8xxx_wr_direct(ha, QLA8XXX_CRB_DRV_ACTIVE, drv_active);
@@ -1523,7 +1534,17 @@ qla4_8xxx_clear_drv_active(struct scsi_qla_host *ha)
        uint32_t drv_active;
 
        drv_active = qla4_8xxx_rd_direct(ha, QLA8XXX_CRB_DRV_ACTIVE);
-       drv_active &= ~(1 << (ha->func_num * 4));
+
+       /*
+        * For ISP8324, drv_active register has 1 bit per function,
+        * shift 1 by func_num to set a bit for the function.
+        * For ISP8022, drv_active has 4 bits per function
+        */
+       if (is_qla8032(ha))
+               drv_active &= ~(1 << (ha->func_num));
+       else
+               drv_active &= ~(1 << (ha->func_num * 4));
+
        ql4_printk(KERN_INFO, ha, "%s(%ld): drv_active: 0x%08x\n",
                   __func__, ha->host_no, drv_active);
        qla4_8xxx_wr_direct(ha, QLA8XXX_CRB_DRV_ACTIVE, drv_active);
@@ -1536,32 +1557,60 @@ inline int qla4_8xxx_need_reset(struct scsi_qla_host *ha)
 
        drv_active = qla4_8xxx_rd_direct(ha, QLA8XXX_CRB_DRV_ACTIVE);
        drv_state = qla4_8xxx_rd_direct(ha, QLA8XXX_CRB_DRV_STATE);
-       rval = drv_state & (1 << (ha->func_num * 4));
+
+       /*
+        * For ISP8324, drv_active register has 1 bit per function,
+        * shift 1 by func_num to set a bit for the function.
+        * For ISP8022, drv_active has 4 bits per function
+        */
+       if (is_qla8032(ha))
+               rval = drv_state & (1 << ha->func_num);
+       else
+               rval = drv_state & (1 << (ha->func_num * 4));
+
        if ((test_bit(AF_EEH_BUSY, &ha->flags)) && drv_active)
                rval = 1;
 
        return rval;
 }
 
-static inline void
-qla4_8xxx_set_rst_ready(struct scsi_qla_host *ha)
+void qla4_8xxx_set_rst_ready(struct scsi_qla_host *ha)
 {
        uint32_t drv_state;
 
        drv_state = qla4_8xxx_rd_direct(ha, QLA8XXX_CRB_DRV_STATE);
-       drv_state |= (1 << (ha->func_num * 4));
+
+       /*
+        * For ISP8324, drv_active register has 1 bit per function,
+        * shift 1 by func_num to set a bit for the function.
+        * For ISP8022, drv_active has 4 bits per function
+        */
+       if (is_qla8032(ha))
+               drv_state |= (1 << ha->func_num);
+       else
+               drv_state |= (1 << (ha->func_num * 4));
+
        ql4_printk(KERN_INFO, ha, "%s(%ld): drv_state: 0x%08x\n",
                   __func__, ha->host_no, drv_state);
        qla4_8xxx_wr_direct(ha, QLA8XXX_CRB_DRV_STATE, drv_state);
 }
 
-static inline void
-qla4_8xxx_clear_rst_ready(struct scsi_qla_host *ha)
+void qla4_8xxx_clear_rst_ready(struct scsi_qla_host *ha)
 {
        uint32_t drv_state;
 
        drv_state = qla4_8xxx_rd_direct(ha, QLA8XXX_CRB_DRV_STATE);
-       drv_state &= ~(1 << (ha->func_num * 4));
+
+       /*
+        * For ISP8324, drv_active register has 1 bit per function,
+        * shift 1 by func_num to set a bit for the function.
+        * For ISP8022, drv_active has 4 bits per function
+        */
+       if (is_qla8032(ha))
+               drv_state &= ~(1 << ha->func_num);
+       else
+               drv_state &= ~(1 << (ha->func_num * 4));
+
        ql4_printk(KERN_INFO, ha, "%s(%ld): drv_state: 0x%08x\n",
                   __func__, ha->host_no, drv_state);
        qla4_8xxx_wr_direct(ha, QLA8XXX_CRB_DRV_STATE, drv_state);
@@ -1573,7 +1622,17 @@ qla4_8xxx_set_qsnt_ready(struct scsi_qla_host *ha)
        uint32_t qsnt_state;
 
        qsnt_state = qla4_8xxx_rd_direct(ha, QLA8XXX_CRB_DRV_STATE);
-       qsnt_state |= (2 << (ha->func_num * 4));
+
+       /*
+        * For ISP8324, drv_active register has 1 bit per function,
+        * shift 1 by func_num to set a bit for the function.
+        * For ISP8022, drv_active has 4 bits per function.
+        */
+       if (is_qla8032(ha))
+               qsnt_state |= (1 << ha->func_num);
+       else
+               qsnt_state |= (2 << (ha->func_num * 4));
+
        qla4_8xxx_wr_direct(ha, QLA8XXX_CRB_DRV_STATE, qsnt_state);
 }
 
@@ -2104,6 +2163,196 @@ static void qla4_8xxx_mark_entry_skipped(struct scsi_qla_host *ha,
                          entry_hdr->d_ctrl.entry_capture_mask));
 }
 
+/* ISP83xx functions to process new minidump entries... */
+static uint32_t qla83xx_minidump_process_pollrd(struct scsi_qla_host *ha,
+                               struct qla8xxx_minidump_entry_hdr *entry_hdr,
+                               uint32_t **d_ptr)
+{
+       uint32_t r_addr, s_addr, s_value, r_value, poll_wait, poll_mask;
+       uint16_t s_stride, i;
+       uint32_t *data_ptr = *d_ptr;
+       uint32_t rval = QLA_SUCCESS;
+       struct qla83xx_minidump_entry_pollrd *pollrd_hdr;
+
+       pollrd_hdr = (struct qla83xx_minidump_entry_pollrd *)entry_hdr;
+       s_addr = le32_to_cpu(pollrd_hdr->select_addr);
+       r_addr = le32_to_cpu(pollrd_hdr->read_addr);
+       s_value = le32_to_cpu(pollrd_hdr->select_value);
+       s_stride = le32_to_cpu(pollrd_hdr->select_value_stride);
+
+       poll_wait = le32_to_cpu(pollrd_hdr->poll_wait);
+       poll_mask = le32_to_cpu(pollrd_hdr->poll_mask);
+
+       for (i = 0; i < le32_to_cpu(pollrd_hdr->op_count); i++) {
+               ha->isp_ops->wr_reg_indirect(ha, s_addr, s_value);
+               poll_wait = le32_to_cpu(pollrd_hdr->poll_wait);
+               while (1) {
+                       ha->isp_ops->rd_reg_indirect(ha, s_addr, &r_value);
+
+                       if ((r_value & poll_mask) != 0) {
+                               break;
+                       } else {
+                               msleep(1);
+                               if (--poll_wait == 0) {
+                                       ql4_printk(KERN_ERR, ha, "%s: TIMEOUT\n",
+                                                  __func__);
+                                       rval = QLA_ERROR;
+                                       goto exit_process_pollrd;
+                               }
+                       }
+               }
+               ha->isp_ops->rd_reg_indirect(ha, r_addr, &r_value);
+               *data_ptr++ = cpu_to_le32(s_value);
+               *data_ptr++ = cpu_to_le32(r_value);
+               s_value += s_stride;
+       }
+
+       *d_ptr = data_ptr;
+
+exit_process_pollrd:
+       return rval;
+}
+
+static void qla83xx_minidump_process_rdmux2(struct scsi_qla_host *ha,
+                               struct qla8xxx_minidump_entry_hdr *entry_hdr,
+                               uint32_t **d_ptr)
+{
+       uint32_t sel_val1, sel_val2, t_sel_val, data, i;
+       uint32_t sel_addr1, sel_addr2, sel_val_mask, read_addr;
+       struct qla83xx_minidump_entry_rdmux2 *rdmux2_hdr;
+       uint32_t *data_ptr = *d_ptr;
+
+       rdmux2_hdr = (struct qla83xx_minidump_entry_rdmux2 *)entry_hdr;
+       sel_val1 = le32_to_cpu(rdmux2_hdr->select_value_1);
+       sel_val2 = le32_to_cpu(rdmux2_hdr->select_value_2);
+       sel_addr1 = le32_to_cpu(rdmux2_hdr->select_addr_1);
+       sel_addr2 = le32_to_cpu(rdmux2_hdr->select_addr_2);
+       sel_val_mask = le32_to_cpu(rdmux2_hdr->select_value_mask);
+       read_addr = le32_to_cpu(rdmux2_hdr->read_addr);
+
+       for (i = 0; i < rdmux2_hdr->op_count; i++) {
+               ha->isp_ops->wr_reg_indirect(ha, sel_addr1, sel_val1);
+               t_sel_val = sel_val1 & sel_val_mask;
+               *data_ptr++ = cpu_to_le32(t_sel_val);
+
+               ha->isp_ops->wr_reg_indirect(ha, sel_addr2, t_sel_val);
+               ha->isp_ops->rd_reg_indirect(ha, read_addr, &data);
+
+               *data_ptr++ = cpu_to_le32(data);
+
+               ha->isp_ops->wr_reg_indirect(ha, sel_addr1, sel_val2);
+               t_sel_val = sel_val2 & sel_val_mask;
+               *data_ptr++ = cpu_to_le32(t_sel_val);
+
+               ha->isp_ops->wr_reg_indirect(ha, sel_addr2, t_sel_val);
+               ha->isp_ops->rd_reg_indirect(ha, read_addr, &data);
+
+               *data_ptr++ = cpu_to_le32(data);
+
+               sel_val1 += rdmux2_hdr->select_value_stride;
+               sel_val2 += rdmux2_hdr->select_value_stride;
+       }
+
+       *d_ptr = data_ptr;
+}
+
+static uint32_t qla83xx_minidump_process_pollrdmwr(struct scsi_qla_host *ha,
+                               struct qla8xxx_minidump_entry_hdr *entry_hdr,
+                               uint32_t **d_ptr)
+{
+       uint32_t poll_wait, poll_mask, r_value, data;
+       uint32_t addr_1, addr_2, value_1, value_2;
+       uint32_t *data_ptr = *d_ptr;
+       uint32_t rval = QLA_SUCCESS;
+       struct qla83xx_minidump_entry_pollrdmwr *poll_hdr;
+
+       poll_hdr = (struct qla83xx_minidump_entry_pollrdmwr *)entry_hdr;
+       addr_1 = le32_to_cpu(poll_hdr->addr_1);
+       addr_2 = le32_to_cpu(poll_hdr->addr_2);
+       value_1 = le32_to_cpu(poll_hdr->value_1);
+       value_2 = le32_to_cpu(poll_hdr->value_2);
+       poll_mask = le32_to_cpu(poll_hdr->poll_mask);
+
+       ha->isp_ops->wr_reg_indirect(ha, addr_1, value_1);
+
+       poll_wait = le32_to_cpu(poll_hdr->poll_wait);
+       while (1) {
+               ha->isp_ops->rd_reg_indirect(ha, addr_1, &r_value);
+
+               if ((r_value & poll_mask) != 0) {
+                       break;
+               } else {
+                       msleep(1);
+                       if (--poll_wait == 0) {
+                               ql4_printk(KERN_ERR, ha, "%s: TIMEOUT_1\n",
+                                          __func__);
+                               rval = QLA_ERROR;
+                               goto exit_process_pollrdmwr;
+                       }
+               }
+       }
+
+       ha->isp_ops->rd_reg_indirect(ha, addr_2, &data);
+       data &= le32_to_cpu(poll_hdr->modify_mask);
+       ha->isp_ops->wr_reg_indirect(ha, addr_2, data);
+       ha->isp_ops->wr_reg_indirect(ha, addr_1, value_2);
+
+       poll_wait = le32_to_cpu(poll_hdr->poll_wait);
+       while (1) {
+               ha->isp_ops->rd_reg_indirect(ha, addr_1, &r_value);
+
+               if ((r_value & poll_mask) != 0) {
+                       break;
+               } else {
+                       msleep(1);
+                       if (--poll_wait == 0) {
+                               ql4_printk(KERN_ERR, ha, "%s: TIMEOUT_2\n",
+                                          __func__);
+                               rval = QLA_ERROR;
+                               goto exit_process_pollrdmwr;
+                       }
+               }
+       }
+
+       *data_ptr++ = cpu_to_le32(addr_2);
+       *data_ptr++ = cpu_to_le32(data);
+       *d_ptr = data_ptr;
+
+exit_process_pollrdmwr:
+       return rval;
+}
+
+static uint32_t qla4_83xx_minidump_process_rdrom(struct scsi_qla_host *ha,
+                               struct qla8xxx_minidump_entry_hdr *entry_hdr,
+                               uint32_t **d_ptr)
+{
+       uint32_t fl_addr, u32_count, rval;
+       struct qla8xxx_minidump_entry_rdrom *rom_hdr;
+       uint32_t *data_ptr = *d_ptr;
+
+       rom_hdr = (struct qla8xxx_minidump_entry_rdrom *)entry_hdr;
+       fl_addr = le32_to_cpu(rom_hdr->read_addr);
+       u32_count = le32_to_cpu(rom_hdr->read_data_size)/sizeof(uint32_t);
+
+       DEBUG2(ql4_printk(KERN_INFO, ha, "[%s]: fl_addr: 0x%x, count: 0x%x\n",
+                         __func__, fl_addr, u32_count));
+
+       rval = qla4_83xx_lockless_flash_read_u32(ha, fl_addr,
+                                                (u8 *)(data_ptr), u32_count);
+
+       if (rval == QLA_ERROR) {
+               ql4_printk(KERN_ERR, ha, "%s: Flash Read Error,Count=%d\n",
+                          __func__, u32_count);
+               goto exit_process_rdrom;
+       }
+
+       data_ptr += u32_count;
+       *d_ptr = data_ptr;
+
+exit_process_rdrom:
+       return rval;
+}
+
 /**
  * qla4_8xxx_collect_md_data - Retrieve firmware minidump data.
  * @ha: pointer to adapter structure
@@ -2151,6 +2400,10 @@ static int qla4_8xxx_collect_md_data(struct scsi_qla_host *ha)
                                        (((uint8_t *)ha->fw_dump_tmplt_hdr) +
                                         tmplt_hdr->first_entry_offset);
 
+       if (is_qla8032(ha))
+               tmplt_hdr->saved_state_array[QLA83XX_SS_OCM_WNDREG_INDEX] =
+                                       tmplt_hdr->ocm_window_reg[ha->func_num];
+
        /* Walk through the entry headers - validate/perform required action */
        for (i = 0; i < num_entry_hdr; i++) {
                if (data_collected >= ha->fw_dump_size) {
@@ -2201,8 +2454,18 @@ static int qla4_8xxx_collect_md_data(struct scsi_qla_host *ha)
                        break;
                case QLA8XXX_BOARD:
                case QLA8XXX_RDROM:
-                       qla4_82xx_minidump_process_rdrom(ha, entry_hdr,
-                                                        &data_ptr);
+                       if (is_qla8022(ha)) {
+                               qla4_82xx_minidump_process_rdrom(ha, entry_hdr,
+                                                                &data_ptr);
+                       } else if (is_qla8032(ha)) {
+                               rval = qla4_83xx_minidump_process_rdrom(ha,
+                                                                   entry_hdr,
+                                                                   &data_ptr);
+                               if (rval != QLA_SUCCESS)
+                                       qla4_8xxx_mark_entry_skipped(ha,
+                                                                    entry_hdr,
+                                                                    i);
+                       }
                        break;
                case QLA8XXX_L2DTG:
                case QLA8XXX_L2ITG:
@@ -2215,6 +2478,8 @@ static int qla4_8xxx_collect_md_data(struct scsi_qla_host *ha)
                                goto md_failed;
                        }
                        break;
+               case QLA8XXX_L1DTG:
+               case QLA8XXX_L1ITG:
                case QLA8XXX_L1DAT:
                case QLA8XXX_L1INS:
                        qla4_8xxx_minidump_process_l1cache(ha, entry_hdr,
@@ -2232,6 +2497,34 @@ static int qla4_8xxx_collect_md_data(struct scsi_qla_host *ha)
                        qla4_8xxx_minidump_process_queue(ha, entry_hdr,
                                                         &data_ptr);
                        break;
+               case QLA83XX_POLLRD:
+                       if (!is_qla8032(ha)) {
+                               qla4_8xxx_mark_entry_skipped(ha, entry_hdr, i);
+                               break;
+                       }
+                       rval = qla83xx_minidump_process_pollrd(ha, entry_hdr,
+                                                              &data_ptr);
+                       if (rval != QLA_SUCCESS)
+                               qla4_8xxx_mark_entry_skipped(ha, entry_hdr, i);
+                       break;
+               case QLA83XX_RDMUX2:
+                       if (!is_qla8032(ha)) {
+                               qla4_8xxx_mark_entry_skipped(ha, entry_hdr, i);
+                               break;
+                       }
+                       qla83xx_minidump_process_rdmux2(ha, entry_hdr,
+                                                       &data_ptr);
+                       break;
+               case QLA83XX_POLLRDMWR:
+                       if (!is_qla8032(ha)) {
+                               qla4_8xxx_mark_entry_skipped(ha, entry_hdr, i);
+                               break;
+                       }
+                       rval = qla83xx_minidump_process_pollrdmwr(ha, entry_hdr,
+                                                                 &data_ptr);
+                       if (rval != QLA_SUCCESS)
+                               qla4_8xxx_mark_entry_skipped(ha, entry_hdr, i);
+                       break;
                case QLA8XXX_RDNOP:
                default:
                        qla4_8xxx_mark_entry_skipped(ha, entry_hdr, i);
@@ -2283,7 +2576,7 @@ static void qla4_8xxx_uevent_emit(struct scsi_qla_host *ha, u32 code)
        kobject_uevent_env(&(&ha->pdev->dev)->kobj, KOBJ_CHANGE, envp);
 }
 
-static void qla4_8xxx_get_minidump(struct scsi_qla_host *ha)
+void qla4_8xxx_get_minidump(struct scsi_qla_host *ha)
 {
        if (ql4xenablemd && test_bit(AF_FW_RECOVERY, &ha->flags) &&
            !test_bit(AF_82XX_FW_DUMPED, &ha->flags)) {
@@ -2303,12 +2596,11 @@ static void qla4_8xxx_get_minidump(struct scsi_qla_host *ha)
  *
  * Note: IDC lock must be held upon entry
  **/
-static int
-qla4_8xxx_device_bootstrap(struct scsi_qla_host *ha)
+int qla4_8xxx_device_bootstrap(struct scsi_qla_host *ha)
 {
        int rval = QLA_ERROR;
        int i, timeout;
-       uint32_t old_count, count;
+       uint32_t old_count, count, idc_ctrl;
        int need_reset = 0, peg_stuck = 1;
 
        need_reset = ha->isp_ops->need_reset(ha);
@@ -2351,8 +2643,24 @@ dev_initialize:
        qla4_8xxx_wr_direct(ha, QLA8XXX_CRB_DEV_STATE,
                            QLA8XXX_DEV_INITIALIZING);
 
+       /*
+        * For ISP8324, if IDC_CTRL GRACEFUL_RESET_BIT1 is set, reset it after
+        * device goes to INIT state.
+        */
+       if (is_qla8032(ha)) {
+               idc_ctrl = qla4_83xx_rd_reg(ha, QLA83XX_IDC_DRV_CTRL);
+               if (idc_ctrl & GRACEFUL_RESET_BIT1) {
+                       qla4_83xx_wr_reg(ha, QLA83XX_IDC_DRV_CTRL,
+                                        (idc_ctrl & ~GRACEFUL_RESET_BIT1));
+                       set_bit(AF_83XX_NO_FW_DUMP, &ha->flags);
+               }
+       }
+
        ha->isp_ops->idc_unlock(ha);
-       qla4_8xxx_get_minidump(ha);
+
+       if (is_qla8022(ha))
+               qla4_8xxx_get_minidump(ha);
+
        rval = ha->isp_ops->restart_firmware(ha);
        ha->isp_ops->idc_lock(ha);
 
@@ -2487,14 +2795,77 @@ static void qla4_82xx_set_idc_ver(struct scsi_qla_host *ha)
        }
 }
 
-static void qla4_8xxx_update_idc_reg(struct scsi_qla_host *ha)
+static int qla4_83xx_set_idc_ver(struct scsi_qla_host *ha)
 {
-       if (!test_bit(AF_INIT_DONE, &ha->flags)) {
-               ha->isp_ops->idc_lock(ha);
-               qla4_8xxx_set_drv_active(ha);
+       int idc_ver;
+       uint32_t drv_active;
+       int rval = QLA_SUCCESS;
+
+       drv_active = qla4_8xxx_rd_direct(ha, QLA8XXX_CRB_DRV_ACTIVE);
+       if (drv_active == (1 << ha->func_num)) {
+               idc_ver = qla4_8xxx_rd_direct(ha, QLA8XXX_CRB_DRV_IDC_VERSION);
+               idc_ver &= (~0xFF);
+               idc_ver |= QLA83XX_IDC_VER_MAJ_VALUE;
+               qla4_8xxx_wr_direct(ha, QLA8XXX_CRB_DRV_IDC_VERSION, idc_ver);
+               ql4_printk(KERN_INFO, ha,
+                          "%s: IDC version updated to %d\n", __func__,
+                          QLA83XX_IDC_VER_MAJ_VALUE);
+       } else {
+               idc_ver = qla4_8xxx_rd_direct(ha, QLA8XXX_CRB_DRV_IDC_VERSION);
+               idc_ver &= 0xFF;
+               if (QLA83XX_IDC_VER_MAJ_VALUE != idc_ver) {
+                       ql4_printk(KERN_INFO, ha,
+                                  "%s: qla4xxx driver IDC version %d is not compatible with IDC version %d of other drivers!\n",
+                                  __func__, QLA83XX_IDC_VER_MAJ_VALUE,
+                                  idc_ver);
+                       rval = QLA_ERROR;
+                       goto exit_set_idc_ver;
+               }
+       }
+
+       /* Update IDC_MINOR_VERSION */
+       idc_ver = qla4_83xx_rd_reg(ha, QLA83XX_CRB_IDC_VER_MINOR);
+       idc_ver &= ~(0x03 << (ha->func_num * 2));
+       idc_ver |= (QLA83XX_IDC_VER_MIN_VALUE << (ha->func_num * 2));
+       qla4_83xx_wr_reg(ha, QLA83XX_CRB_IDC_VER_MINOR, idc_ver);
+
+exit_set_idc_ver:
+       return rval;
+}
+
+static int qla4_8xxx_update_idc_reg(struct scsi_qla_host *ha)
+{
+       uint32_t drv_active;
+       int rval = QLA_SUCCESS;
+
+       if (test_bit(AF_INIT_DONE, &ha->flags))
+               goto exit_update_idc_reg;
+
+       ha->isp_ops->idc_lock(ha);
+       qla4_8xxx_set_drv_active(ha);
+
+       /*
+        * If we are the first driver to load and
+        * ql4xdontresethba is not set, clear IDC_CTRL BIT0.
+        */
+       if (is_qla8032(ha)) {
+               drv_active = qla4_8xxx_rd_direct(ha, QLA8XXX_CRB_DRV_ACTIVE);
+               if ((drv_active == (1 << ha->func_num)) && !ql4xdontresethba)
+                       qla4_83xx_clear_idc_dontreset(ha);
+       }
+
+       if (is_qla8022(ha)) {
                qla4_82xx_set_idc_ver(ha);
-               ha->isp_ops->idc_unlock(ha);
+       } else if (is_qla8032(ha)) {
+               rval = qla4_83xx_set_idc_ver(ha);
+               if (rval == QLA_ERROR)
+                       qla4_8xxx_clear_drv_active(ha);
        }
+
+       ha->isp_ops->idc_unlock(ha);
+
+exit_update_idc_reg:
+       return rval;
 }
 
 /**
@@ -2509,7 +2880,9 @@ int qla4_8xxx_device_state_handler(struct scsi_qla_host *ha)
        int rval = QLA_SUCCESS;
        unsigned long dev_init_timeout;
 
-       qla4_8xxx_update_idc_reg(ha);
+       rval = qla4_8xxx_update_idc_reg(ha);
+       if (rval == QLA_ERROR)
+               goto exit_state_handler;
 
        dev_state = qla4_8xxx_rd_direct(ha, QLA8XXX_CRB_DEV_STATE);
        DEBUG2(ql4_printk(KERN_INFO, ha, "Device state is 0x%x = %s\n",
@@ -2550,16 +2923,25 @@ int qla4_8xxx_device_state_handler(struct scsi_qla_host *ha)
                        ha->isp_ops->idc_lock(ha);
                        break;
                case QLA8XXX_DEV_NEED_RESET:
-                       if (!ql4xdontresethba) {
-                               qla4_82xx_need_reset_handler(ha);
-                               /* Update timeout value after need
-                                * reset handler */
-                               dev_init_timeout = jiffies +
-                                       (ha->nx_dev_init_timeout * HZ);
-                       } else {
-                               ha->isp_ops->idc_unlock(ha);
-                               msleep(1000);
-                               ha->isp_ops->idc_lock(ha);
+                       /*
+                        * For ISP8324, if NEED_RESET is set by any driver,
+                        * it should be honored, irrespective of IDC_CTRL
+                        * DONTRESET_BIT0
+                        */
+                       if (is_qla8032(ha)) {
+                               qla4_83xx_need_reset_handler(ha);
+                       } else if (is_qla8022(ha)) {
+                               if (!ql4xdontresethba) {
+                                       qla4_82xx_need_reset_handler(ha);
+                                       /* Update timeout value after need
+                                        * reset handler */
+                                       dev_init_timeout = jiffies +
+                                               (ha->nx_dev_init_timeout * HZ);
+                               } else {
+                                       ha->isp_ops->idc_unlock(ha);
+                                       msleep(1000);
+                                       ha->isp_ops->idc_lock(ha);
+                               }
                        }
                        break;
                case QLA8XXX_DEV_NEED_QUIESCENT:
@@ -2587,6 +2969,7 @@ int qla4_8xxx_device_state_handler(struct scsi_qla_host *ha)
        }
 exit:
        ha->isp_ops->idc_unlock(ha);
+exit_state_handler:
        return rval;
 }
 
@@ -2595,8 +2978,13 @@ int qla4_8xxx_load_risc(struct scsi_qla_host *ha)
        int retval;
 
        /* clear the interrupt */
-       writel(0, &ha->qla4_82xx_reg->host_int);
-       readl(&ha->qla4_82xx_reg->host_int);
+       if (is_qla8032(ha)) {
+               writel(0, &ha->qla4_83xx_reg->risc_intr);
+               readl(&ha->qla4_83xx_reg->risc_intr);
+       } else if (is_qla8022(ha)) {
+               writel(0, &ha->qla4_82xx_reg->host_int);
+               readl(&ha->qla4_82xx_reg->host_int);
+       }
 
        retval = qla4_8xxx_device_state_handler(ha);
 
@@ -2695,7 +3083,7 @@ qla4_8xxx_get_flt_info(struct scsi_qla_host *ha, uint32_t flt_addr)
        const char *loc, *locations[] = { "DEF", "FLT" };
        uint16_t *wptr;
        uint16_t cnt, chksum;
-       uint32_t start;
+       uint32_t start, status;
        struct qla_flt_header *flt;
        struct qla_flt_region *region;
        struct ql82xx_hw_data *hw = &ha->hw;
@@ -2704,8 +3092,18 @@ qla4_8xxx_get_flt_info(struct scsi_qla_host *ha, uint32_t flt_addr)
        wptr = (uint16_t *)ha->request_ring;
        flt = (struct qla_flt_header *)ha->request_ring;
        region = (struct qla_flt_region *)&flt[1];
-       qla4_82xx_read_optrom_data(ha, (uint8_t *)ha->request_ring,
-                       flt_addr << 2, OPTROM_BURST_SIZE);
+
+       if (is_qla8022(ha)) {
+               qla4_82xx_read_optrom_data(ha, (uint8_t *)ha->request_ring,
+                                          flt_addr << 2, OPTROM_BURST_SIZE);
+       } else if (is_qla8032(ha)) {
+               status = qla4_83xx_flash_read_u32(ha, flt_addr << 2,
+                                                 (uint8_t *)ha->request_ring,
+                                                 0x400);
+               if (status != QLA_SUCCESS)
+                       goto no_flash_data;
+       }
+
        if (*wptr == __constant_cpu_to_le16(0xffff))
                goto no_flash_data;
        if (flt->version != __constant_cpu_to_le16(1)) {
@@ -2918,8 +3316,12 @@ qla4_8xxx_get_flash_info(struct scsi_qla_host *ha)
                return ret;
 
        qla4_8xxx_get_flt_info(ha, flt_addr);
-       qla4_82xx_get_fdt_info(ha);
-       qla4_82xx_get_idc_param(ha);
+       if (is_qla8022(ha)) {
+               qla4_82xx_get_fdt_info(ha);
+               qla4_82xx_get_idc_param(ha);
+       } else if (is_qla8032(ha)) {
+               qla4_83xx_get_idc_param(ha);
+       }
 
        return QLA_SUCCESS;
 }
@@ -3063,8 +3465,7 @@ exit_validate_mac82:
 
 /* Interrupt handling helpers. */
 
-static int
-qla4_8xxx_mbx_intr_enable(struct scsi_qla_host *ha)
+int qla4_8xxx_mbx_intr_enable(struct scsi_qla_host *ha)
 {
        uint32_t mbox_cmd[MBOX_REG_COUNT];
        uint32_t mbox_sts[MBOX_REG_COUNT];
@@ -3085,8 +3486,7 @@ qla4_8xxx_mbx_intr_enable(struct scsi_qla_host *ha)
        return QLA_SUCCESS;
 }
 
-static int
-qla4_8xxx_mbx_intr_disable(struct scsi_qla_host *ha)
+int qla4_8xxx_mbx_intr_disable(struct scsi_qla_host *ha)
 {
        uint32_t mbox_cmd[MBOX_REG_COUNT];
        uint32_t mbox_sts[MBOX_REG_COUNT];
index 1894de0..ec5cbd0 100644 (file)
@@ -25,6 +25,8 @@
 #define CRB_RCVPEG_STATE               QLA82XX_REG(0x13c)
 #define CRB_DMA_SHIFT                  QLA82XX_REG(0xcc)
 #define CRB_TEMP_STATE                 QLA82XX_REG(0x1b4)
+#define CRB_CMDPEG_CHECK_RETRY_COUNT   60
+#define CRB_CMDPEG_CHECK_DELAY         500
 
 #define qla82xx_get_temp_val(x)                ((x) >> 16)
 #define qla82xx_get_temp_state(x)      ((x) & 0xffff)
@@ -508,6 +510,7 @@ enum {
 
 #define QLA82XX_P2_ADDR_QDR_NET_MAX    (0x00000003001fffffULL)
 #define QLA82XX_P3_ADDR_QDR_NET_MAX    (0x0000000303ffffffULL)
+#define QLA8XXX_ADDR_QDR_NET_MAX       (0x0000000307ffffffULL)
 
 #define QLA82XX_PCI_CRBSPACE           (unsigned long)0x06000000
 #define QLA82XX_PCI_DIRECT_CRB         (unsigned long)0x04400000
@@ -852,9 +855,13 @@ struct crb_addr_pair {
 #define QLA8XXX_L2ITG  22
 #define QLA8XXX_L2DAT  23
 #define QLA8XXX_L2INS  24
+#define QLA83XX_POLLRD 35
+#define QLA83XX_RDMUX2 36
+#define QLA83XX_POLLRDMWR  37
 #define QLA8XXX_RDROM  71
 #define QLA8XXX_RDMEM  72
 #define QLA8XXX_CNTRL  98
+#define QLA83XX_TLHDR  99
 #define QLA8XXX_RDEND  255
 
 /* Opcodes for Control Entries.
@@ -1007,6 +1014,16 @@ struct qla8xxx_minidump_entry_queue {
 #define MD_MIU_TEST_AGT_ADDR_LO                        0x41000094
 #define MD_MIU_TEST_AGT_ADDR_HI                        0x41000098
 
+#define MD_MIU_TEST_AGT_WRDATA_LO              0x410000A0
+#define MD_MIU_TEST_AGT_WRDATA_HI              0x410000A4
+#define MD_MIU_TEST_AGT_WRDATA_ULO             0x410000B0
+#define MD_MIU_TEST_AGT_WRDATA_UHI             0x410000B4
+
+#define MD_MIU_TEST_AGT_RDDATA_LO              0x410000A8
+#define MD_MIU_TEST_AGT_RDDATA_HI              0x410000AC
+#define MD_MIU_TEST_AGT_RDDATA_ULO             0x410000B8
+#define MD_MIU_TEST_AGT_RDDATA_UHI             0x410000BC
+
 static const int MD_MIU_TEST_AGT_RDDATA[] = { 0x410000A8,
                                0x410000AC, 0x410000B8, 0x410000BC };
 #endif
index 519f666..3e0e5de 100644 (file)
@@ -18,6 +18,7 @@
 #include "ql4_glbl.h"
 #include "ql4_dbg.h"
 #include "ql4_inline.h"
+#include "ql4_83xx.h"
 
 /*
  * Driver version
@@ -2315,8 +2316,17 @@ static void qla4xxx_mem_free(struct scsi_qla_host *ha)
                if (ha->nx_pcibase)
                        iounmap(
                            (struct device_reg_82xx __iomem *)ha->nx_pcibase);
-       } else if (ha->reg)
+       } else if (is_qla8032(ha)) {
+               if (ha->nx_pcibase)
+                       iounmap(
+                           (struct device_reg_83xx __iomem *)ha->nx_pcibase);
+       } else if (ha->reg) {
                iounmap(ha->reg);
+       }
+
+       if (ha->reset_tmplt.buff)
+               vfree(ha->reset_tmplt.buff);
+
        pci_release_regions(ha->pdev);
 }
 
@@ -2454,7 +2464,6 @@ static int qla4_8xxx_check_temp(struct scsi_qla_host *ha)
 static int qla4_8xxx_check_fw_alive(struct scsi_qla_host *ha)
 {
        uint32_t fw_heartbeat_counter;
-       uint32_t halt_status1, halt_status2;
        int status = QLA_SUCCESS;
 
        fw_heartbeat_counter = qla4_8xxx_rd_direct(ha,
@@ -2472,28 +2481,7 @@ static int qla4_8xxx_check_fw_alive(struct scsi_qla_host *ha)
                /* FW not alive after 2 seconds */
                if (ha->seconds_since_last_heartbeat == 2) {
                        ha->seconds_since_last_heartbeat = 0;
-                       halt_status1 = qla4_8xxx_rd_direct(ha,
-                                               QLA8XXX_PEG_HALT_STATUS1);
-                       halt_status2 = qla4_8xxx_rd_direct(ha,
-                                               QLA8XXX_PEG_HALT_STATUS2);
-
-                       ql4_printk(KERN_INFO, ha,
-                                  "scsi(%ld): %s, Dumping hw/fw registers:\n "
-                                  " PEG_HALT_STATUS1: 0x%x, PEG_HALT_STATUS2:"
-                                  " 0x%x,\n PEG_NET_0_PC: 0x%x, PEG_NET_1_PC:"
-                                  " 0x%x,\n PEG_NET_2_PC: 0x%x, PEG_NET_3_PC:"
-                                  " 0x%x,\n PEG_NET_4_PC: 0x%x\n", ha->host_no,
-                                  __func__, halt_status1, halt_status2,
-                                  qla4_82xx_rd_32(ha, QLA82XX_CRB_PEG_NET_0 +
-                                                  0x3c),
-                                  qla4_82xx_rd_32(ha, QLA82XX_CRB_PEG_NET_1 +
-                                                  0x3c),
-                                  qla4_82xx_rd_32(ha, QLA82XX_CRB_PEG_NET_2 +
-                                                  0x3c),
-                                  qla4_82xx_rd_32(ha, QLA82XX_CRB_PEG_NET_3 +
-                                                  0x3c),
-                                  qla4_82xx_rd_32(ha, QLA82XX_CRB_PEG_NET_4 +
-                                                  0x3c));
+                       qla4_8xxx_dump_peg_reg(ha);
                        status = QLA_ERROR;
                }
        } else
@@ -2503,6 +2491,48 @@ static int qla4_8xxx_check_fw_alive(struct scsi_qla_host *ha)
        return status;
 }
 
+static void qla4_8xxx_process_fw_error(struct scsi_qla_host *ha)
+{
+       uint32_t halt_status;
+       int halt_status_unrecoverable = 0;
+
+       halt_status = qla4_8xxx_rd_direct(ha, QLA8XXX_PEG_HALT_STATUS1);
+
+       if (is_qla8022(ha)) {
+               ql4_printk(KERN_INFO, ha, "%s: disabling pause transmit on port 0 & 1.\n",
+                          __func__);
+               qla4_82xx_wr_32(ha, QLA82XX_CRB_NIU + 0x98,
+                               CRB_NIU_XG_PAUSE_CTL_P0 |
+                               CRB_NIU_XG_PAUSE_CTL_P1);
+
+               if (QLA82XX_FWERROR_CODE(halt_status) == 0x67)
+                       ql4_printk(KERN_ERR, ha, "%s: Firmware aborted with error code 0x00006700. Device is being reset\n",
+                                  __func__);
+               if (halt_status & HALT_STATUS_UNRECOVERABLE)
+                       halt_status_unrecoverable = 1;
+       } else if (is_qla8032(ha)) {
+               if (halt_status & QLA83XX_HALT_STATUS_FW_RESET)
+                       ql4_printk(KERN_ERR, ha, "%s: Firmware error detected device is being reset\n",
+                                  __func__);
+               else if (halt_status & QLA83XX_HALT_STATUS_UNRECOVERABLE)
+                       halt_status_unrecoverable = 1;
+       }
+
+       /*
+        * Since we cannot change dev_state in interrupt context,
+        * set appropriate DPC flag then wakeup DPC
+        */
+       if (halt_status_unrecoverable) {
+               set_bit(DPC_HA_UNRECOVERABLE, &ha->dpc_flags);
+       } else {
+               ql4_printk(KERN_INFO, ha, "%s: detect abort needed!\n",
+                          __func__);
+               set_bit(DPC_RESET_HA, &ha->dpc_flags);
+       }
+       qla4xxx_mailbox_premature_completion(ha);
+       qla4xxx_wake_dpc(ha);
+}
+
 /**
  * qla4_8xxx_watchdog - Poll dev state
  * @ha: Pointer to host adapter structure.
@@ -2511,7 +2541,7 @@ static int qla4_8xxx_check_fw_alive(struct scsi_qla_host *ha)
  **/
 void qla4_8xxx_watchdog(struct scsi_qla_host *ha)
 {
-       uint32_t dev_state, halt_status;
+       uint32_t dev_state;
 
        /* don't poll if reset is going on */
        if (!(test_bit(DPC_RESET_ACTIVE, &ha->dpc_flags) ||
@@ -2520,16 +2550,18 @@ void qla4_8xxx_watchdog(struct scsi_qla_host *ha)
                dev_state = qla4_8xxx_rd_direct(ha, QLA8XXX_CRB_DEV_STATE);
 
                if (qla4_8xxx_check_temp(ha)) {
-                       ql4_printk(KERN_INFO, ha, "disabling pause"
-                                  " transmit on port 0 & 1.\n");
-                       qla4_82xx_wr_32(ha, QLA82XX_CRB_NIU + 0x98,
-                                       CRB_NIU_XG_PAUSE_CTL_P0 |
-                                       CRB_NIU_XG_PAUSE_CTL_P1);
+                       if (is_qla8022(ha)) {
+                               ql4_printk(KERN_INFO, ha, "disabling pause transmit on port 0 & 1.\n");
+                               qla4_82xx_wr_32(ha, QLA82XX_CRB_NIU + 0x98,
+                                               CRB_NIU_XG_PAUSE_CTL_P0 |
+                                               CRB_NIU_XG_PAUSE_CTL_P1);
+                       }
                        set_bit(DPC_HA_UNRECOVERABLE, &ha->dpc_flags);
                        qla4xxx_wake_dpc(ha);
                } else if (dev_state == QLA8XXX_DEV_NEED_RESET &&
-                   !test_bit(DPC_RESET_HA, &ha->dpc_flags)) {
-                       if (!ql4xdontresethba) {
+                          !test_bit(DPC_RESET_HA, &ha->dpc_flags)) {
+                       if (is_qla8032(ha) ||
+                           (is_qla8022(ha) && !ql4xdontresethba)) {
                                ql4_printk(KERN_INFO, ha, "%s: HW State: "
                                    "NEED RESET!\n", __func__);
                                set_bit(DPC_RESET_HA, &ha->dpc_flags);
@@ -2543,36 +2575,8 @@ void qla4_8xxx_watchdog(struct scsi_qla_host *ha)
                        qla4xxx_wake_dpc(ha);
                } else  {
                        /* Check firmware health */
-                       if (qla4_8xxx_check_fw_alive(ha)) {
-                               ql4_printk(KERN_INFO, ha, "disabling pause"
-                                          " transmit on port 0 & 1.\n");
-                               qla4_82xx_wr_32(ha, QLA82XX_CRB_NIU + 0x98,
-                                               CRB_NIU_XG_PAUSE_CTL_P0 |
-                                               CRB_NIU_XG_PAUSE_CTL_P1);
-                               halt_status = qla4_8xxx_rd_direct(ha,
-                                                  QLA8XXX_PEG_HALT_STATUS1);
-
-                               if (QLA82XX_FWERROR_CODE(halt_status) == 0x67)
-                                       ql4_printk(KERN_ERR, ha, "%s:"
-                                                  " Firmware aborted with"
-                                                  " error code 0x00006700."
-                                                  " Device is being reset\n",
-                                                  __func__);
-
-                               /* 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 {
-                                       ql4_printk(KERN_INFO, ha, "%s: detect "
-                                                  "abort needed!\n", __func__);
-                                       set_bit(DPC_RESET_HA, &ha->dpc_flags);
-                               }
-                               qla4xxx_mailbox_premature_completion(ha);
-                               qla4xxx_wake_dpc(ha);
-                       }
+                       if (qla4_8xxx_check_fw_alive(ha))
+                               qla4_8xxx_process_fw_error(ha);
                }
        }
 }
@@ -2654,9 +2658,8 @@ static void qla4xxx_timer(struct scsi_qla_host *ha)
        if (!pci_channel_offline(ha->pdev))
                pci_read_config_word(ha->pdev, PCI_VENDOR_ID, &w);
 
-       if (is_qla8022(ha)) {
+       if (is_qla80XX(ha))
                qla4_8xxx_watchdog(ha);
-       }
 
        if (is_qla40XX(ha)) {
                /* Check for heartbeat interval. */
@@ -2955,9 +2958,9 @@ static int qla4xxx_recover_adapter(struct scsi_qla_host *ha)
                goto recover_ha_init_adapter;
        }
 
-       /* For the ISP-82xx adapter, issue a stop_firmware if invoked
+       /* For the ISP-8xxx adapter, issue a stop_firmware if invoked
         * from eh_host_reset or ioctl module */
-       if (is_qla8022(ha) && !reset_chip &&
+       if (is_qla80XX(ha) && !reset_chip &&
            test_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags)) {
 
                DEBUG2(ql4_printk(KERN_INFO, ha,
@@ -2980,13 +2983,13 @@ static int qla4xxx_recover_adapter(struct scsi_qla_host *ha)
        }
 
        /* Issue full chip reset if recovering from a catastrophic error,
-        * or if stop_firmware fails for ISP-82xx.
+        * or if stop_firmware fails for ISP-8xxx.
         * This is the default case for ISP-4xxx */
        if (is_qla40XX(ha) || reset_chip) {
                if (is_qla40XX(ha))
                        goto chip_reset;
 
-               /* Check if 82XX firmware is alive or not
+               /* Check if 8XXX firmware is alive or not
                 * We may have arrived here from NEED_RESET
                 * detection only */
                if (test_bit(AF_FW_RECOVERY, &ha->flags))
@@ -3041,7 +3044,7 @@ recover_ha_init_adapter:
                 * Since we don't want to block the DPC for too long
                 * with multiple resets in the same thread,
                 * utilize DPC to retry */
-               if (is_qla8022(ha)) {
+               if (is_qla80XX(ha)) {
                        ha->isp_ops->idc_lock(ha);
                        dev_state = qla4_8xxx_rd_direct(ha,
                                                        QLA8XXX_CRB_DEV_STATE);
@@ -3386,7 +3389,7 @@ static void qla4xxx_do_dpc(struct work_struct *work)
        /* post events to application */
        qla4xxx_do_work(ha);
 
-       if (is_qla8022(ha)) {
+       if (is_qla80XX(ha)) {
                if (test_bit(DPC_HA_UNRECOVERABLE, &ha->dpc_flags)) {
                        ha->isp_ops->idc_lock(ha);
                        qla4_8xxx_wr_direct(ha, QLA8XXX_CRB_DEV_STATE,
@@ -3404,7 +3407,8 @@ static void qla4xxx_do_dpc(struct work_struct *work)
            (test_bit(DPC_RESET_HA, &ha->dpc_flags) ||
            test_bit(DPC_RESET_HA_INTR, &ha->dpc_flags) ||
            test_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags))) {
-               if (ql4xdontresethba) {
+               if ((is_qla8022(ha) && ql4xdontresethba) ||
+                   (is_qla8032(ha) && qla4_83xx_idc_dontreset(ha))) {
                        DEBUG2(printk("scsi%ld: %s: Don't Reset HBA\n",
                            ha->host_no, __func__));
                        clear_bit(DPC_RESET_HA, &ha->dpc_flags);
@@ -3514,7 +3518,7 @@ static void qla4xxx_free_adapter(struct scsi_qla_host *ha)
        /* Put firmware in known state */
        ha->isp_ops->reset_firmware(ha);
 
-       if (is_qla8022(ha)) {
+       if (is_qla80XX(ha)) {
                ha->isp_ops->idc_lock(ha);
                qla4_8xxx_clear_drv_active(ha);
                ha->isp_ops->idc_unlock(ha);
@@ -3564,16 +3568,20 @@ int qla4_8xxx_iospace_config(struct scsi_qla_host *ha)
        /* Mapping of IO base pointer, door bell read and write pointer */
 
        /* mapping of IO base pointer */
-       ha->qla4_82xx_reg =
-           (struct device_reg_82xx  __iomem *)((uint8_t *)ha->nx_pcibase +
-           0xbc000 + (ha->pdev->devfn << 11));
+       if (is_qla8022(ha)) {
+               ha->qla4_82xx_reg = (struct device_reg_82xx  __iomem *)
+                                   ((uint8_t *)ha->nx_pcibase + 0xbc000 +
+                                    (ha->pdev->devfn << 11));
+               ha->nx_db_wr_ptr = (ha->pdev->devfn == 4 ? QLA82XX_CAM_RAM_DB1 :
+                                   QLA82XX_CAM_RAM_DB2);
+       } else if (is_qla8032(ha)) {
+               ha->qla4_83xx_reg = (struct device_reg_83xx __iomem *)
+                                   ((uint8_t *)ha->nx_pcibase);
+       }
 
        db_base = pci_resource_start(pdev, 4);  /* doorbell is on bar 4 */
        db_len = pci_resource_len(pdev, 4);
 
-       ha->nx_db_wr_ptr = (ha->pdev->devfn == 4 ? QLA82XX_CAM_RAM_DB1 :
-           QLA82XX_CAM_RAM_DB2);
-
        return 0;
 iospace_error_exit:
        return -ENOMEM;
@@ -3693,6 +3701,34 @@ static struct isp_operations qla4_82xx_isp_ops = {
        .process_mailbox_interrupt = qla4_82xx_process_mbox_intr,
 };
 
+static struct isp_operations qla4_83xx_isp_ops = {
+       .iospace_config         = qla4_8xxx_iospace_config,
+       .pci_config             = qla4_8xxx_pci_config,
+       .disable_intrs          = qla4_83xx_disable_intrs,
+       .enable_intrs           = qla4_83xx_enable_intrs,
+       .start_firmware         = qla4_8xxx_load_risc,
+       .restart_firmware       = qla4_83xx_start_firmware,
+       .intr_handler           = qla4_83xx_intr_handler,
+       .interrupt_service_routine = qla4_83xx_interrupt_service_routine,
+       .need_reset             = qla4_8xxx_need_reset,
+       .reset_chip             = qla4_83xx_isp_reset,
+       .reset_firmware         = qla4_8xxx_stop_firmware,
+       .queue_iocb             = qla4_83xx_queue_iocb,
+       .complete_iocb          = qla4_83xx_complete_iocb,
+       .rd_shdw_req_q_out      = qla4_83xx_rd_shdw_req_q_out,
+       .rd_shdw_rsp_q_in       = qla4_83xx_rd_shdw_rsp_q_in,
+       .get_sys_info           = qla4_8xxx_get_sys_info,
+       .rd_reg_direct          = qla4_83xx_rd_reg,
+       .wr_reg_direct          = qla4_83xx_wr_reg,
+       .rd_reg_indirect        = qla4_83xx_rd_reg_indirect,
+       .wr_reg_indirect        = qla4_83xx_wr_reg_indirect,
+       .idc_lock               = qla4_83xx_drv_lock,
+       .idc_unlock             = qla4_83xx_drv_unlock,
+       .rom_lock_recovery      = qla4_83xx_rom_lock_recovery,
+       .queue_mailbox_command  = qla4_83xx_queue_mbox_cmd,
+       .process_mailbox_interrupt = qla4_83xx_process_mbox_intr,
+};
+
 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);
@@ -3703,6 +3739,11 @@ uint16_t qla4_82xx_rd_shdw_req_q_out(struct scsi_qla_host *ha)
        return (uint16_t)le32_to_cpu(readl(&ha->qla4_82xx_reg->req_q_out));
 }
 
+uint16_t qla4_83xx_rd_shdw_req_q_out(struct scsi_qla_host *ha)
+{
+       return (uint16_t)le32_to_cpu(readl(&ha->qla4_83xx_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);
@@ -3713,6 +3754,11 @@ uint16_t qla4_82xx_rd_shdw_rsp_q_in(struct scsi_qla_host *ha)
        return (uint16_t)le32_to_cpu(readl(&ha->qla4_82xx_reg->rsp_q_in));
 }
 
+uint16_t qla4_83xx_rd_shdw_rsp_q_in(struct scsi_qla_host *ha)
+{
+       return (uint16_t)le32_to_cpu(readl(&ha->qla4_83xx_reg->rsp_q_in));
+}
+
 static ssize_t qla4xxx_show_boot_eth_info(void *data, int type, char *buf)
 {
        struct scsi_qla_host *ha = data;
@@ -5085,6 +5131,7 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
        ha->pdev = pdev;
        ha->host = host;
        ha->host_no = host->host_no;
+       ha->func_num = PCI_FUNC(ha->pdev->devfn);
 
        pci_enable_pcie_error_reporting(pdev);
 
@@ -5092,24 +5139,28 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
        if (is_qla8022(ha)) {
                ha->isp_ops = &qla4_82xx_isp_ops;
                ha->reg_tbl = (uint32_t *) qla4_82xx_reg_tbl;
-               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 if (is_qla8032(ha)) {
+               ha->isp_ops = &qla4_83xx_isp_ops;
+               ha->reg_tbl = (uint32_t *)qla4_83xx_reg_tbl;
        } else {
                ha->isp_ops = &qla4xxx_isp_ops;
        }
 
-       /* Set EEH reset type to fundamental if required by hba */
-       if (is_qla8022(ha))
+       if (is_qla80XX(ha)) {
+               rwlock_init(&ha->hw_lock);
+               ha->pf_bit = ha->func_num << 16;
+               /* Set EEH reset type to fundamental if required by hba */
                pdev->needs_freset = 1;
+       }
 
        /* Configure PCI I/O space. */
        ret = ha->isp_ops->iospace_config(ha);
@@ -5165,8 +5216,20 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
        if (ret)
                goto probe_failed;
 
-       if (is_qla8022(ha))
-               (void) qla4_8xxx_get_flash_info(ha);
+       if (is_qla80XX(ha))
+               qla4_8xxx_get_flash_info(ha);
+
+       if (is_qla8032(ha)) {
+               qla4_83xx_read_reset_template(ha);
+               /*
+                * NOTE: If ql4dontresethba==1, set IDC_CTRL DONTRESET_BIT0.
+                * If DONRESET_BIT0 is set, drivers should not set dev_state
+                * to NEED_RESET. But if NEED_RESET is set, drivers should
+                * should honor the reset.
+                */
+               if (ql4xdontresethba == 1)
+                       qla4_83xx_set_idc_dontreset(ha);
+       }
 
        /*
         * Initialize the Host adapter request/response queues and
@@ -5177,7 +5240,7 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
        while ((!test_bit(AF_ONLINE, &ha->flags)) &&
            init_retry_count++ < MAX_INIT_RETRIES) {
 
-               if (is_qla8022(ha)) {
+               if (is_qla80XX(ha)) {
                        ha->isp_ops->idc_lock(ha);
                        dev_state = qla4_8xxx_rd_direct(ha,
                                                        QLA82XX_CRB_DEV_STATE);
@@ -5201,7 +5264,8 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
        if (!test_bit(AF_ONLINE, &ha->flags)) {
                ql4_printk(KERN_WARNING, ha, "Failed to initialize adapter\n");
 
-               if (is_qla8022(ha) && ql4xdontresethba) {
+               if ((is_qla8022(ha) && ql4xdontresethba) ||
+                   (is_qla8032(ha) && qla4_83xx_idc_dontreset(ha))) {
                        /* Put the device in failed state. */
                        DEBUG2(printk(KERN_ERR "HW STATE: FAILED\n"));
                        ha->isp_ops->idc_lock(ha);
@@ -5233,7 +5297,8 @@ static int __devinit qla4xxx_probe_adapter(struct pci_dev *pdev,
                goto remove_host;
        }
 
-       /* For ISP-82XX, request_irqs is called in qla4_8xxx_load_risc
+       /*
+        * For ISP-8XXX, 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.
@@ -5793,7 +5858,16 @@ static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd)
 
        ha = to_qla_host(cmd->device->host);
 
-       if (ql4xdontresethba) {
+       if (is_qla8032(ha) && ql4xdontresethba)
+               qla4_83xx_set_idc_dontreset(ha);
+
+       /*
+        * For ISP8324, if IDC_CTRL DONTRESET_BIT0 is set by other
+        * protocol drivers, we should not set device_state to
+        * NEED_RESET
+        */
+       if (ql4xdontresethba ||
+           (is_qla8032(ha) && qla4_83xx_idc_dontreset(ha))) {
                DEBUG2(printk("scsi%ld: %s: Don't Reset HBA\n",
                     ha->host_no, __func__));
 
@@ -5817,7 +5891,7 @@ static int qla4xxx_eh_host_reset(struct scsi_cmnd *cmd)
        }
 
        if (!test_bit(DPC_RESET_HA, &ha->dpc_flags)) {
-               if (is_qla8022(ha))
+               if (is_qla80XX(ha))
                        set_bit(DPC_RESET_HA_FW_CONTEXT, &ha->dpc_flags);
                else
                        set_bit(DPC_RESET_HA, &ha->dpc_flags);
@@ -5912,7 +5986,7 @@ static int qla4xxx_host_reset(struct Scsi_Host *shost, int reset_type)
                break;
        case SCSI_FIRMWARE_RESET:
                if (!test_bit(DPC_RESET_HA, &ha->dpc_flags)) {
-                       if (is_qla8022(ha))
+                       if (is_qla80XX(ha))
                                /* set firmware context reset */
                                set_bit(DPC_RESET_HA_FW_CONTEXT,
                                        &ha->dpc_flags);
@@ -6150,7 +6224,7 @@ qla4xxx_pci_slot_reset(struct pci_dev *pdev)
 
        ha->isp_ops->disable_intrs(ha);
 
-       if (is_qla8022(ha)) {
+       if (is_qla80XX(ha)) {
                if (qla4_8xxx_error_recovery(ha) == QLA_SUCCESS) {
                        ret = PCI_ERS_RESULT_RECOVERED;
                        goto exit_slot_reset;
@@ -6216,6 +6290,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_ISP8324,
+               .subvendor      = PCI_ANY_ID,
+               .subdevice      = PCI_ANY_ID,
+       },
        {0, 0},
 };
 MODULE_DEVICE_TABLE(pci, qla4xxx_pci_tbl);