aboutsummaryrefslogtreecommitdiff
path: root/drivers/scsi
diff options
context:
space:
mode:
authorVikas Chaudhary <vikas.chaudhary@qlogic.com>2012-08-22 07:55:08 -0400
committerJames Bottomley <JBottomley@Parallels.com>2012-09-24 12:11:08 +0400
commit6e7b429259fc0b7f2d9b1147466656b34d114815 (patch)
treeb5bc8dc7e7e803f6589c9cdd4e71c8dc7a9932ab /drivers/scsi
parentaec07caedbb769535e78adca30c851c977fd5741 (diff)
[SCSI] qla4xxx: Added support for ISP83XX
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>
Diffstat (limited to 'drivers/scsi')
-rw-r--r--drivers/scsi/qla4xxx/Kconfig4
-rw-r--r--drivers/scsi/qla4xxx/Makefile2
-rw-r--r--drivers/scsi/qla4xxx/ql4_83xx.c1468
-rw-r--r--drivers/scsi/qla4xxx/ql4_83xx.h262
-rw-r--r--drivers/scsi/qla4xxx/ql4_attr.c2
-rw-r--r--drivers/scsi/qla4xxx/ql4_dbg.c28
-rw-r--r--drivers/scsi/qla4xxx/ql4_def.h27
-rw-r--r--drivers/scsi/qla4xxx/ql4_fw.h38
-rw-r--r--drivers/scsi/qla4xxx/ql4_glbl.h41
-rw-r--r--drivers/scsi/qla4xxx/ql4_init.c15
-rw-r--r--drivers/scsi/qla4xxx/ql4_iocb.c12
-rw-r--r--drivers/scsi/qla4xxx/ql4_isr.c202
-rw-r--r--drivers/scsi/qla4xxx/ql4_mbx.c6
-rw-r--r--drivers/scsi/qla4xxx/ql4_nx.c488
-rw-r--r--drivers/scsi/qla4xxx/ql4_nx.h17
-rw-r--r--drivers/scsi/qla4xxx/ql4_os.c262
16 files changed, 2689 insertions, 185 deletions
diff --git a/drivers/scsi/qla4xxx/Kconfig b/drivers/scsi/qla4xxx/Kconfig
index f1ad02ea212..e4dc7c733c2 100644
--- a/drivers/scsi/qla4xxx/Kconfig
+++ b/drivers/scsi/qla4xxx/Kconfig
@@ -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.
diff --git a/drivers/scsi/qla4xxx/Makefile b/drivers/scsi/qla4xxx/Makefile
index 5b44139ff43..4230977748c 100644
--- a/drivers/scsi/qla4xxx/Makefile
+++ b/drivers/scsi/qla4xxx/Makefile
@@ -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
index 00000000000..f963b06a158
--- /dev/null
+++ b/drivers/scsi/qla4xxx/ql4_83xx.c
@@ -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);