summaryrefslogtreecommitdiff
path: root/drivers/scsi/qla2xxx
diff options
context:
space:
mode:
authorSrikant Patnaik2015-01-11 12:28:04 +0530
committerSrikant Patnaik2015-01-11 12:28:04 +0530
commit871480933a1c28f8a9fed4c4d34d06c439a7a422 (patch)
tree8718f573808810c2a1e8cb8fb6ac469093ca2784 /drivers/scsi/qla2xxx
parent9d40ac5867b9aefe0722bc1f110b965ff294d30d (diff)
downloadFOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.tar.gz
FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.tar.bz2
FOSSEE-netbook-kernel-source-871480933a1c28f8a9fed4c4d34d06c439a7a422.zip
Moved, renamed, and deleted files
The original directory structure was scattered and unorganized. Changes are basically to make it look like kernel structure.
Diffstat (limited to 'drivers/scsi/qla2xxx')
-rw-r--r--drivers/scsi/qla2xxx/Kconfig27
-rw-r--r--drivers/scsi/qla2xxx/Makefile5
-rw-r--r--drivers/scsi/qla2xxx/qla_attr.c1971
-rw-r--r--drivers/scsi/qla2xxx/qla_bsg.c1716
-rw-r--r--drivers/scsi/qla2xxx/qla_bsg.h186
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.c2495
-rw-r--r--drivers/scsi/qla2xxx/qla_dbg.h341
-rw-r--r--drivers/scsi/qla2xxx/qla_def.h3068
-rw-r--r--drivers/scsi/qla2xxx/qla_devtbl.h99
-rw-r--r--drivers/scsi/qla2xxx/qla_dfs.c181
-rw-r--r--drivers/scsi/qla2xxx/qla_fw.h1891
-rw-r--r--drivers/scsi/qla2xxx/qla_gbl.h611
-rw-r--r--drivers/scsi/qla2xxx/qla_gs.c2025
-rw-r--r--drivers/scsi/qla2xxx/qla_init.c5558
-rw-r--r--drivers/scsi/qla2xxx/qla_inline.h189
-rw-r--r--drivers/scsi/qla2xxx/qla_iocb.c2647
-rw-r--r--drivers/scsi/qla2xxx/qla_isr.c2597
-rw-r--r--drivers/scsi/qla2xxx/qla_mbx.c4571
-rw-r--r--drivers/scsi/qla2xxx/qla_mid.c836
-rw-r--r--drivers/scsi/qla2xxx/qla_nx.c4428
-rw-r--r--drivers/scsi/qla2xxx/qla_nx.h1181
-rw-r--r--drivers/scsi/qla2xxx/qla_os.c4533
-rw-r--r--drivers/scsi/qla2xxx/qla_settings.h12
-rw-r--r--drivers/scsi/qla2xxx/qla_sup.c3044
-rw-r--r--drivers/scsi/qla2xxx/qla_version.h15
25 files changed, 44227 insertions, 0 deletions
diff --git a/drivers/scsi/qla2xxx/Kconfig b/drivers/scsi/qla2xxx/Kconfig
new file mode 100644
index 00000000..6208d562
--- /dev/null
+++ b/drivers/scsi/qla2xxx/Kconfig
@@ -0,0 +1,27 @@
+config SCSI_QLA_FC
+ tristate "QLogic QLA2XXX Fibre Channel Support"
+ depends on PCI && SCSI
+ select SCSI_FC_ATTRS
+ select FW_LOADER
+ ---help---
+ This qla2xxx driver supports all QLogic Fibre Channel
+ PCI and PCIe host adapters.
+
+ By default, firmware for the ISP parts will be loaded
+ via the Firmware Loader interface.
+
+ ISP Firmware Filename
+ ---------- -----------------
+ 21xx ql2100_fw.bin
+ 22xx ql2200_fw.bin
+ 2300, 2312, 6312 ql2300_fw.bin
+ 2322, 6322 ql2322_fw.bin
+ 24xx, 54xx ql2400_fw.bin
+ 25xx ql2500_fw.bin
+
+ Upon request, the driver caches the firmware image until
+ the driver is unloaded.
+
+ Firmware images can be retrieved from:
+
+ ftp://ftp.qlogic.com/outgoing/linux/firmware/
diff --git a/drivers/scsi/qla2xxx/Makefile b/drivers/scsi/qla2xxx/Makefile
new file mode 100644
index 00000000..5df782f4
--- /dev/null
+++ b/drivers/scsi/qla2xxx/Makefile
@@ -0,0 +1,5 @@
+qla2xxx-y := qla_os.o qla_init.o qla_mbx.o qla_iocb.o qla_isr.o qla_gs.o \
+ qla_dbg.o qla_sup.o qla_attr.o qla_mid.o qla_dfs.o qla_bsg.o \
+ qla_nx.o
+
+obj-$(CONFIG_SCSI_QLA_FC) += qla2xxx.o
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c
new file mode 100644
index 00000000..5926f5a8
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_attr.c
@@ -0,0 +1,1971 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2003-2011 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#include "qla_def.h"
+
+#include <linux/kthread.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+
+static int qla24xx_vport_disable(struct fc_vport *, bool);
+
+/* SYSFS attributes --------------------------------------------------------- */
+
+static ssize_t
+qla2x00_sysfs_read_fw_dump(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
+ int rval = 0;
+
+ if (ha->fw_dump_reading == 0)
+ return 0;
+
+ if (IS_QLA82XX(ha)) {
+ if (off < ha->md_template_size) {
+ rval = memory_read_from_buffer(buf, count,
+ &off, ha->md_tmplt_hdr, ha->md_template_size);
+ return rval;
+ }
+ off -= ha->md_template_size;
+ rval = memory_read_from_buffer(buf, count,
+ &off, ha->md_dump, ha->md_dump_size);
+ return rval;
+ } else
+ return memory_read_from_buffer(buf, count, &off, ha->fw_dump,
+ ha->fw_dump_len);
+}
+
+static ssize_t
+qla2x00_sysfs_write_fw_dump(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
+ int reading;
+
+ if (off != 0)
+ return (0);
+
+ reading = simple_strtol(buf, NULL, 10);
+ switch (reading) {
+ case 0:
+ if (!ha->fw_dump_reading)
+ break;
+
+ ql_log(ql_log_info, vha, 0x705d,
+ "Firmware dump cleared on (%ld).\n", vha->host_no);
+
+ if (IS_QLA82XX(vha->hw)) {
+ qla82xx_md_free(vha);
+ qla82xx_md_prep(vha);
+ }
+ ha->fw_dump_reading = 0;
+ ha->fw_dumped = 0;
+ break;
+ case 1:
+ if (ha->fw_dumped && !ha->fw_dump_reading) {
+ ha->fw_dump_reading = 1;
+
+ ql_log(ql_log_info, vha, 0x705e,
+ "Raw firmware dump ready for read on (%ld).\n",
+ vha->host_no);
+ }
+ break;
+ case 2:
+ qla2x00_alloc_fw_dump(vha);
+ break;
+ case 3:
+ if (IS_QLA82XX(ha)) {
+ qla82xx_idc_lock(ha);
+ qla82xx_set_reset_owner(vha);
+ qla82xx_idc_unlock(ha);
+ } else
+ qla2x00_system_error(vha);
+ break;
+ case 4:
+ if (IS_QLA82XX(ha)) {
+ if (ha->md_tmplt_hdr)
+ ql_dbg(ql_dbg_user, vha, 0x705b,
+ "MiniDump supported with this firmware.\n");
+ else
+ ql_dbg(ql_dbg_user, vha, 0x709d,
+ "MiniDump not supported with this firmware.\n");
+ }
+ break;
+ case 5:
+ if (IS_QLA82XX(ha))
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ break;
+ }
+ return count;
+}
+
+static struct bin_attribute sysfs_fw_dump_attr = {
+ .attr = {
+ .name = "fw_dump",
+ .mode = S_IRUSR | S_IWUSR,
+ },
+ .size = 0,
+ .read = qla2x00_sysfs_read_fw_dump,
+ .write = qla2x00_sysfs_write_fw_dump,
+};
+
+static ssize_t
+qla2x00_sysfs_read_nvram(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return 0;
+
+ if (IS_NOCACHE_VPD_TYPE(ha))
+ ha->isp_ops->read_optrom(vha, ha->nvram, ha->flt_region_nvram << 2,
+ ha->nvram_size);
+ return memory_read_from_buffer(buf, count, &off, ha->nvram,
+ ha->nvram_size);
+}
+
+static ssize_t
+qla2x00_sysfs_write_nvram(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
+ uint16_t cnt;
+
+ if (!capable(CAP_SYS_ADMIN) || off != 0 || count != ha->nvram_size ||
+ !ha->isp_ops->write_nvram)
+ return -EINVAL;
+
+ /* Checksum NVRAM. */
+ if (IS_FWI2_CAPABLE(ha)) {
+ uint32_t *iter;
+ uint32_t chksum;
+
+ iter = (uint32_t *)buf;
+ chksum = 0;
+ for (cnt = 0; cnt < ((count >> 2) - 1); cnt++)
+ chksum += le32_to_cpu(*iter++);
+ chksum = ~chksum + 1;
+ *iter = cpu_to_le32(chksum);
+ } else {
+ uint8_t *iter;
+ uint8_t chksum;
+
+ iter = (uint8_t *)buf;
+ chksum = 0;
+ for (cnt = 0; cnt < count - 1; cnt++)
+ chksum += *iter++;
+ chksum = ~chksum + 1;
+ *iter = chksum;
+ }
+
+ if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x705f,
+ "HBA not online, failing NVRAM update.\n");
+ return -EAGAIN;
+ }
+
+ /* Write NVRAM. */
+ ha->isp_ops->write_nvram(vha, (uint8_t *)buf, ha->nvram_base, count);
+ ha->isp_ops->read_nvram(vha, (uint8_t *)ha->nvram, ha->nvram_base,
+ count);
+
+ ql_dbg(ql_dbg_user, vha, 0x7060,
+ "Setting ISP_ABORT_NEEDED\n");
+ /* NVRAM settings take effect immediately. */
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
+ qla2x00_wait_for_chip_reset(vha);
+
+ return count;
+}
+
+static struct bin_attribute sysfs_nvram_attr = {
+ .attr = {
+ .name = "nvram",
+ .mode = S_IRUSR | S_IWUSR,
+ },
+ .size = 512,
+ .read = qla2x00_sysfs_read_nvram,
+ .write = qla2x00_sysfs_write_nvram,
+};
+
+static ssize_t
+qla2x00_sysfs_read_optrom(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
+
+ if (ha->optrom_state != QLA_SREADING)
+ return 0;
+
+ return memory_read_from_buffer(buf, count, &off, ha->optrom_buffer,
+ ha->optrom_region_size);
+}
+
+static ssize_t
+qla2x00_sysfs_write_optrom(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
+
+ if (ha->optrom_state != QLA_SWRITING)
+ return -EINVAL;
+ if (off > ha->optrom_region_size)
+ return -ERANGE;
+ if (off + count > ha->optrom_region_size)
+ count = ha->optrom_region_size - off;
+
+ memcpy(&ha->optrom_buffer[off], buf, count);
+
+ return count;
+}
+
+static struct bin_attribute sysfs_optrom_attr = {
+ .attr = {
+ .name = "optrom",
+ .mode = S_IRUSR | S_IWUSR,
+ },
+ .size = 0,
+ .read = qla2x00_sysfs_read_optrom,
+ .write = qla2x00_sysfs_write_optrom,
+};
+
+static ssize_t
+qla2x00_sysfs_write_optrom_ctl(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
+
+ uint32_t start = 0;
+ uint32_t size = ha->optrom_size;
+ int val, valid;
+
+ if (off)
+ return -EINVAL;
+
+ if (unlikely(pci_channel_offline(ha->pdev)))
+ return -EAGAIN;
+
+ if (sscanf(buf, "%d:%x:%x", &val, &start, &size) < 1)
+ return -EINVAL;
+ if (start > ha->optrom_size)
+ return -EINVAL;
+
+ switch (val) {
+ case 0:
+ if (ha->optrom_state != QLA_SREADING &&
+ ha->optrom_state != QLA_SWRITING)
+ return -EINVAL;
+
+ ha->optrom_state = QLA_SWAITING;
+
+ ql_dbg(ql_dbg_user, vha, 0x7061,
+ "Freeing flash region allocation -- 0x%x bytes.\n",
+ ha->optrom_region_size);
+
+ vfree(ha->optrom_buffer);
+ ha->optrom_buffer = NULL;
+ break;
+ case 1:
+ if (ha->optrom_state != QLA_SWAITING)
+ return -EINVAL;
+
+ ha->optrom_region_start = start;
+ ha->optrom_region_size = start + size > ha->optrom_size ?
+ ha->optrom_size - start : size;
+
+ ha->optrom_state = QLA_SREADING;
+ ha->optrom_buffer = vmalloc(ha->optrom_region_size);
+ if (ha->optrom_buffer == NULL) {
+ ql_log(ql_log_warn, vha, 0x7062,
+ "Unable to allocate memory for optrom retrieval "
+ "(%x).\n", ha->optrom_region_size);
+
+ ha->optrom_state = QLA_SWAITING;
+ return -ENOMEM;
+ }
+
+ if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x7063,
+ "HBA not online, failing NVRAM update.\n");
+ return -EAGAIN;
+ }
+
+ ql_dbg(ql_dbg_user, vha, 0x7064,
+ "Reading flash region -- 0x%x/0x%x.\n",
+ ha->optrom_region_start, ha->optrom_region_size);
+
+ memset(ha->optrom_buffer, 0, ha->optrom_region_size);
+ ha->isp_ops->read_optrom(vha, ha->optrom_buffer,
+ ha->optrom_region_start, ha->optrom_region_size);
+ break;
+ case 2:
+ if (ha->optrom_state != QLA_SWAITING)
+ return -EINVAL;
+
+ /*
+ * We need to be more restrictive on which FLASH regions are
+ * allowed to be updated via user-space. Regions accessible
+ * via this method include:
+ *
+ * ISP21xx/ISP22xx/ISP23xx type boards:
+ *
+ * 0x000000 -> 0x020000 -- Boot code.
+ *
+ * ISP2322/ISP24xx type boards:
+ *
+ * 0x000000 -> 0x07ffff -- Boot code.
+ * 0x080000 -> 0x0fffff -- Firmware.
+ *
+ * ISP25xx type boards:
+ *
+ * 0x000000 -> 0x07ffff -- Boot code.
+ * 0x080000 -> 0x0fffff -- Firmware.
+ * 0x120000 -> 0x12ffff -- VPD and HBA parameters.
+ */
+ valid = 0;
+ if (ha->optrom_size == OPTROM_SIZE_2300 && start == 0)
+ valid = 1;
+ else if (start == (ha->flt_region_boot * 4) ||
+ start == (ha->flt_region_fw * 4))
+ valid = 1;
+ else if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha)
+ || IS_CNA_CAPABLE(ha) || IS_QLA2031(ha))
+ valid = 1;
+ if (!valid) {
+ ql_log(ql_log_warn, vha, 0x7065,
+ "Invalid start region 0x%x/0x%x.\n", start, size);
+ return -EINVAL;
+ }
+
+ ha->optrom_region_start = start;
+ ha->optrom_region_size = start + size > ha->optrom_size ?
+ ha->optrom_size - start : size;
+
+ ha->optrom_state = QLA_SWRITING;
+ ha->optrom_buffer = vmalloc(ha->optrom_region_size);
+ if (ha->optrom_buffer == NULL) {
+ ql_log(ql_log_warn, vha, 0x7066,
+ "Unable to allocate memory for optrom update "
+ "(%x)\n", ha->optrom_region_size);
+
+ ha->optrom_state = QLA_SWAITING;
+ return -ENOMEM;
+ }
+
+ ql_dbg(ql_dbg_user, vha, 0x7067,
+ "Staging flash region write -- 0x%x/0x%x.\n",
+ ha->optrom_region_start, ha->optrom_region_size);
+
+ memset(ha->optrom_buffer, 0, ha->optrom_region_size);
+ break;
+ case 3:
+ if (ha->optrom_state != QLA_SWRITING)
+ return -EINVAL;
+
+ if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x7068,
+ "HBA not online, failing flash update.\n");
+ return -EAGAIN;
+ }
+
+ ql_dbg(ql_dbg_user, vha, 0x7069,
+ "Writing flash region -- 0x%x/0x%x.\n",
+ ha->optrom_region_start, ha->optrom_region_size);
+
+ ha->isp_ops->write_optrom(vha, ha->optrom_buffer,
+ ha->optrom_region_start, ha->optrom_region_size);
+ break;
+ default:
+ return -EINVAL;
+ }
+ return count;
+}
+
+static struct bin_attribute sysfs_optrom_ctl_attr = {
+ .attr = {
+ .name = "optrom_ctl",
+ .mode = S_IWUSR,
+ },
+ .size = 0,
+ .write = qla2x00_sysfs_write_optrom_ctl,
+};
+
+static ssize_t
+qla2x00_sysfs_read_vpd(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
+
+ if (unlikely(pci_channel_offline(ha->pdev)))
+ return -EAGAIN;
+
+ if (!capable(CAP_SYS_ADMIN))
+ return -EINVAL;
+
+ if (IS_NOCACHE_VPD_TYPE(ha))
+ ha->isp_ops->read_optrom(vha, ha->vpd, ha->flt_region_vpd << 2,
+ ha->vpd_size);
+ return memory_read_from_buffer(buf, count, &off, ha->vpd, ha->vpd_size);
+}
+
+static ssize_t
+qla2x00_sysfs_write_vpd(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
+ uint8_t *tmp_data;
+
+ if (unlikely(pci_channel_offline(ha->pdev)))
+ return 0;
+
+ if (!capable(CAP_SYS_ADMIN) || off != 0 || count != ha->vpd_size ||
+ !ha->isp_ops->write_nvram)
+ return 0;
+
+ if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x706a,
+ "HBA not online, failing VPD update.\n");
+ return -EAGAIN;
+ }
+
+ /* Write NVRAM. */
+ ha->isp_ops->write_nvram(vha, (uint8_t *)buf, ha->vpd_base, count);
+ ha->isp_ops->read_nvram(vha, (uint8_t *)ha->vpd, ha->vpd_base, count);
+
+ /* Update flash version information for 4Gb & above. */
+ if (!IS_FWI2_CAPABLE(ha))
+ return -EINVAL;
+
+ tmp_data = vmalloc(256);
+ if (!tmp_data) {
+ ql_log(ql_log_warn, vha, 0x706b,
+ "Unable to allocate memory for VPD information update.\n");
+ return -ENOMEM;
+ }
+ ha->isp_ops->get_flash_version(vha, tmp_data);
+ vfree(tmp_data);
+
+ return count;
+}
+
+static struct bin_attribute sysfs_vpd_attr = {
+ .attr = {
+ .name = "vpd",
+ .mode = S_IRUSR | S_IWUSR,
+ },
+ .size = 0,
+ .read = qla2x00_sysfs_read_vpd,
+ .write = qla2x00_sysfs_write_vpd,
+};
+
+static ssize_t
+qla2x00_sysfs_read_sfp(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
+ uint16_t iter, addr, offset;
+ int rval;
+
+ if (!capable(CAP_SYS_ADMIN) || off != 0 || count != SFP_DEV_SIZE * 2)
+ return 0;
+
+ if (ha->sfp_data)
+ goto do_read;
+
+ ha->sfp_data = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
+ &ha->sfp_data_dma);
+ if (!ha->sfp_data) {
+ ql_log(ql_log_warn, vha, 0x706c,
+ "Unable to allocate memory for SFP read-data.\n");
+ return 0;
+ }
+
+do_read:
+ memset(ha->sfp_data, 0, SFP_BLOCK_SIZE);
+ addr = 0xa0;
+ for (iter = 0, offset = 0; iter < (SFP_DEV_SIZE * 2) / SFP_BLOCK_SIZE;
+ iter++, offset += SFP_BLOCK_SIZE) {
+ if (iter == 4) {
+ /* Skip to next device address. */
+ addr = 0xa2;
+ offset = 0;
+ }
+
+ rval = qla2x00_read_sfp(vha, ha->sfp_data_dma, ha->sfp_data,
+ addr, offset, SFP_BLOCK_SIZE, 0);
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x706d,
+ "Unable to read SFP data (%x/%x/%x).\n", rval,
+ addr, offset);
+
+ return -EIO;
+ }
+ memcpy(buf, ha->sfp_data, SFP_BLOCK_SIZE);
+ buf += SFP_BLOCK_SIZE;
+ }
+
+ return count;
+}
+
+static struct bin_attribute sysfs_sfp_attr = {
+ .attr = {
+ .name = "sfp",
+ .mode = S_IRUSR | S_IWUSR,
+ },
+ .size = SFP_DEV_SIZE * 2,
+ .read = qla2x00_sysfs_read_sfp,
+};
+
+static ssize_t
+qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+ int type;
+
+ if (off != 0)
+ return -EINVAL;
+
+ type = simple_strtol(buf, NULL, 10);
+ switch (type) {
+ case 0x2025c:
+ ql_log(ql_log_info, vha, 0x706e,
+ "Issuing ISP reset.\n");
+
+ scsi_block_requests(vha->host);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ if (IS_QLA82XX(ha)) {
+ qla82xx_idc_lock(ha);
+ qla82xx_set_reset_owner(vha);
+ qla82xx_idc_unlock(ha);
+ }
+ qla2xxx_wake_dpc(vha);
+ qla2x00_wait_for_chip_reset(vha);
+ scsi_unblock_requests(vha->host);
+ break;
+ case 0x2025d:
+ if (!IS_QLA81XX(ha))
+ return -EPERM;
+
+ ql_log(ql_log_info, vha, 0x706f,
+ "Issuing MPI reset.\n");
+
+ /* Make sure FC side is not in reset */
+ qla2x00_wait_for_hba_online(vha);
+
+ /* Issue MPI reset */
+ scsi_block_requests(vha->host);
+ if (qla81xx_restart_mpi_firmware(vha) != QLA_SUCCESS)
+ ql_log(ql_log_warn, vha, 0x7070,
+ "MPI reset failed.\n");
+ scsi_unblock_requests(vha->host);
+ break;
+ case 0x2025e:
+ if (!IS_QLA82XX(ha) || vha != base_vha) {
+ ql_log(ql_log_info, vha, 0x7071,
+ "FCoE ctx reset no supported.\n");
+ return -EPERM;
+ }
+
+ ql_log(ql_log_info, vha, 0x7072,
+ "Issuing FCoE ctx reset.\n");
+ set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
+ qla2x00_wait_for_fcoe_ctx_reset(vha);
+ break;
+ }
+ return count;
+}
+
+static struct bin_attribute sysfs_reset_attr = {
+ .attr = {
+ .name = "reset",
+ .mode = S_IWUSR,
+ },
+ .size = 0,
+ .write = qla2x00_sysfs_write_reset,
+};
+
+static ssize_t
+qla2x00_sysfs_read_xgmac_stats(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
+ int rval;
+ uint16_t actual_size;
+
+ if (!capable(CAP_SYS_ADMIN) || off != 0 || count > XGMAC_DATA_SIZE)
+ return 0;
+
+ if (ha->xgmac_data)
+ goto do_read;
+
+ ha->xgmac_data = dma_alloc_coherent(&ha->pdev->dev, XGMAC_DATA_SIZE,
+ &ha->xgmac_data_dma, GFP_KERNEL);
+ if (!ha->xgmac_data) {
+ ql_log(ql_log_warn, vha, 0x7076,
+ "Unable to allocate memory for XGMAC read-data.\n");
+ return 0;
+ }
+
+do_read:
+ actual_size = 0;
+ memset(ha->xgmac_data, 0, XGMAC_DATA_SIZE);
+
+ rval = qla2x00_get_xgmac_stats(vha, ha->xgmac_data_dma,
+ XGMAC_DATA_SIZE, &actual_size);
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x7077,
+ "Unable to read XGMAC data (%x).\n", rval);
+ count = 0;
+ }
+
+ count = actual_size > count ? count: actual_size;
+ memcpy(buf, ha->xgmac_data, count);
+
+ return count;
+}
+
+static struct bin_attribute sysfs_xgmac_stats_attr = {
+ .attr = {
+ .name = "xgmac_stats",
+ .mode = S_IRUSR,
+ },
+ .size = 0,
+ .read = qla2x00_sysfs_read_xgmac_stats,
+};
+
+static ssize_t
+qla2x00_sysfs_read_dcbx_tlv(struct file *filp, struct kobject *kobj,
+ struct bin_attribute *bin_attr,
+ char *buf, loff_t off, size_t count)
+{
+ struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj,
+ struct device, kobj)));
+ struct qla_hw_data *ha = vha->hw;
+ int rval;
+ uint16_t actual_size;
+
+ if (!capable(CAP_SYS_ADMIN) || off != 0 || count > DCBX_TLV_DATA_SIZE)
+ return 0;
+
+ if (ha->dcbx_tlv)
+ goto do_read;
+
+ ha->dcbx_tlv = dma_alloc_coherent(&ha->pdev->dev, DCBX_TLV_DATA_SIZE,
+ &ha->dcbx_tlv_dma, GFP_KERNEL);
+ if (!ha->dcbx_tlv) {
+ ql_log(ql_log_warn, vha, 0x7078,
+ "Unable to allocate memory for DCBX TLV read-data.\n");
+ return -ENOMEM;
+ }
+
+do_read:
+ actual_size = 0;
+ memset(ha->dcbx_tlv, 0, DCBX_TLV_DATA_SIZE);
+
+ rval = qla2x00_get_dcbx_params(vha, ha->dcbx_tlv_dma,
+ DCBX_TLV_DATA_SIZE);
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x7079,
+ "Unable to read DCBX TLV (%x).\n", rval);
+ return -EIO;
+ }
+
+ memcpy(buf, ha->dcbx_tlv, count);
+
+ return count;
+}
+
+static struct bin_attribute sysfs_dcbx_tlv_attr = {
+ .attr = {
+ .name = "dcbx_tlv",
+ .mode = S_IRUSR,
+ },
+ .size = 0,
+ .read = qla2x00_sysfs_read_dcbx_tlv,
+};
+
+static struct sysfs_entry {
+ char *name;
+ struct bin_attribute *attr;
+ int is4GBp_only;
+} bin_file_entries[] = {
+ { "fw_dump", &sysfs_fw_dump_attr, },
+ { "nvram", &sysfs_nvram_attr, },
+ { "optrom", &sysfs_optrom_attr, },
+ { "optrom_ctl", &sysfs_optrom_ctl_attr, },
+ { "vpd", &sysfs_vpd_attr, 1 },
+ { "sfp", &sysfs_sfp_attr, 1 },
+ { "reset", &sysfs_reset_attr, },
+ { "xgmac_stats", &sysfs_xgmac_stats_attr, 3 },
+ { "dcbx_tlv", &sysfs_dcbx_tlv_attr, 3 },
+ { NULL },
+};
+
+void
+qla2x00_alloc_sysfs_attr(scsi_qla_host_t *vha)
+{
+ struct Scsi_Host *host = vha->host;
+ struct sysfs_entry *iter;
+ int ret;
+
+ for (iter = bin_file_entries; iter->name; iter++) {
+ if (iter->is4GBp_only && !IS_FWI2_CAPABLE(vha->hw))
+ continue;
+ if (iter->is4GBp_only == 2 && !IS_QLA25XX(vha->hw))
+ continue;
+ if (iter->is4GBp_only == 3 && !(IS_CNA_CAPABLE(vha->hw)))
+ continue;
+
+ ret = sysfs_create_bin_file(&host->shost_gendev.kobj,
+ iter->attr);
+ if (ret)
+ ql_log(ql_log_warn, vha, 0x00f3,
+ "Unable to create sysfs %s binary attribute (%d).\n",
+ iter->name, ret);
+ else
+ ql_dbg(ql_dbg_init, vha, 0x00f4,
+ "Successfully created sysfs %s binary attribure.\n",
+ iter->name);
+ }
+}
+
+void
+qla2x00_free_sysfs_attr(scsi_qla_host_t *vha)
+{
+ struct Scsi_Host *host = vha->host;
+ struct sysfs_entry *iter;
+ struct qla_hw_data *ha = vha->hw;
+
+ for (iter = bin_file_entries; iter->name; iter++) {
+ if (iter->is4GBp_only && !IS_FWI2_CAPABLE(ha))
+ continue;
+ if (iter->is4GBp_only == 2 && !IS_QLA25XX(ha))
+ continue;
+ if (iter->is4GBp_only == 3 && !(IS_CNA_CAPABLE(vha->hw)))
+ continue;
+
+ sysfs_remove_bin_file(&host->shost_gendev.kobj,
+ iter->attr);
+ }
+
+ if (ha->beacon_blink_led == 1)
+ ha->isp_ops->beacon_off(vha);
+}
+
+/* Scsi_Host attributes. */
+
+static ssize_t
+qla2x00_drvr_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return snprintf(buf, PAGE_SIZE, "%s\n", qla2x00_version_str);
+}
+
+static ssize_t
+qla2x00_fw_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+ char fw_str[128];
+
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ ha->isp_ops->fw_version_str(vha, fw_str));
+}
+
+static ssize_t
+qla2x00_serial_num_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+ uint32_t sn;
+
+ if (IS_FWI2_CAPABLE(ha)) {
+ qla2xxx_get_vpd_field(vha, "SN", buf, PAGE_SIZE);
+ return snprintf(buf, PAGE_SIZE, "%s\n", buf);
+ }
+
+ sn = ((ha->serial0 & 0x1f) << 16) | (ha->serial2 << 8) | ha->serial1;
+ return snprintf(buf, PAGE_SIZE, "%c%05d\n", 'A' + sn / 100000,
+ sn % 100000);
+}
+
+static ssize_t
+qla2x00_isp_name_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ return snprintf(buf, PAGE_SIZE, "ISP%04X\n", vha->hw->pdev->device);
+}
+
+static ssize_t
+qla2x00_isp_id_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+ return snprintf(buf, PAGE_SIZE, "%04x %04x %04x %04x\n",
+ ha->product_id[0], ha->product_id[1], ha->product_id[2],
+ ha->product_id[3]);
+}
+
+static ssize_t
+qla2x00_model_name_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ return snprintf(buf, PAGE_SIZE, "%s\n", vha->hw->model_number);
+}
+
+static ssize_t
+qla2x00_model_desc_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ vha->hw->model_desc ? vha->hw->model_desc : "");
+}
+
+static ssize_t
+qla2x00_pci_info_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ char pci_info[30];
+
+ return snprintf(buf, PAGE_SIZE, "%s\n",
+ vha->hw->isp_ops->pci_info_str(vha, pci_info));
+}
+
+static ssize_t
+qla2x00_link_state_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+ int len = 0;
+
+ if (atomic_read(&vha->loop_state) == LOOP_DOWN ||
+ atomic_read(&vha->loop_state) == LOOP_DEAD ||
+ vha->device_flags & DFLG_NO_CABLE)
+ len = snprintf(buf, PAGE_SIZE, "Link Down\n");
+ else if (atomic_read(&vha->loop_state) != LOOP_READY ||
+ qla2x00_reset_active(vha))
+ len = snprintf(buf, PAGE_SIZE, "Unknown Link State\n");
+ else {
+ len = snprintf(buf, PAGE_SIZE, "Link Up - ");
+
+ switch (ha->current_topology) {
+ case ISP_CFG_NL:
+ len += snprintf(buf + len, PAGE_SIZE-len, "Loop\n");
+ break;
+ case ISP_CFG_FL:
+ len += snprintf(buf + len, PAGE_SIZE-len, "FL_Port\n");
+ break;
+ case ISP_CFG_N:
+ len += snprintf(buf + len, PAGE_SIZE-len,
+ "N_Port to N_Port\n");
+ break;
+ case ISP_CFG_F:
+ len += snprintf(buf + len, PAGE_SIZE-len, "F_Port\n");
+ break;
+ default:
+ len += snprintf(buf + len, PAGE_SIZE-len, "Loop\n");
+ break;
+ }
+ }
+ return len;
+}
+
+static ssize_t
+qla2x00_zio_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ int len = 0;
+
+ switch (vha->hw->zio_mode) {
+ case QLA_ZIO_MODE_6:
+ len += snprintf(buf + len, PAGE_SIZE-len, "Mode 6\n");
+ break;
+ case QLA_ZIO_DISABLED:
+ len += snprintf(buf + len, PAGE_SIZE-len, "Disabled\n");
+ break;
+ }
+ return len;
+}
+
+static ssize_t
+qla2x00_zio_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+ int val = 0;
+ uint16_t zio_mode;
+
+ if (!IS_ZIO_SUPPORTED(ha))
+ return -ENOTSUPP;
+
+ if (sscanf(buf, "%d", &val) != 1)
+ return -EINVAL;
+
+ if (val)
+ zio_mode = QLA_ZIO_MODE_6;
+ else
+ zio_mode = QLA_ZIO_DISABLED;
+
+ /* Update per-hba values and queue a reset. */
+ if (zio_mode != QLA_ZIO_DISABLED || ha->zio_mode != QLA_ZIO_DISABLED) {
+ ha->zio_mode = zio_mode;
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ }
+ return strlen(buf);
+}
+
+static ssize_t
+qla2x00_zio_timer_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+
+ return snprintf(buf, PAGE_SIZE, "%d us\n", vha->hw->zio_timer * 100);
+}
+
+static ssize_t
+qla2x00_zio_timer_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ int val = 0;
+ uint16_t zio_timer;
+
+ if (sscanf(buf, "%d", &val) != 1)
+ return -EINVAL;
+ if (val > 25500 || val < 100)
+ return -ERANGE;
+
+ zio_timer = (uint16_t)(val / 100);
+ vha->hw->zio_timer = zio_timer;
+
+ return strlen(buf);
+}
+
+static ssize_t
+qla2x00_beacon_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ int len = 0;
+
+ if (vha->hw->beacon_blink_led)
+ len += snprintf(buf + len, PAGE_SIZE-len, "Enabled\n");
+ else
+ len += snprintf(buf + len, PAGE_SIZE-len, "Disabled\n");
+ return len;
+}
+
+static ssize_t
+qla2x00_beacon_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+ int val = 0;
+ int rval;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return -EPERM;
+
+ if (test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) {
+ ql_log(ql_log_warn, vha, 0x707a,
+ "Abort ISP active -- ignoring beacon request.\n");
+ return -EBUSY;
+ }
+
+ if (sscanf(buf, "%d", &val) != 1)
+ return -EINVAL;
+
+ if (val)
+ rval = ha->isp_ops->beacon_on(vha);
+ else
+ rval = ha->isp_ops->beacon_off(vha);
+
+ if (rval != QLA_SUCCESS)
+ count = 0;
+
+ return count;
+}
+
+static ssize_t
+qla2x00_optrom_bios_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+ return snprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->bios_revision[1],
+ ha->bios_revision[0]);
+}
+
+static ssize_t
+qla2x00_optrom_efi_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+ return snprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->efi_revision[1],
+ ha->efi_revision[0]);
+}
+
+static ssize_t
+qla2x00_optrom_fcode_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+ return snprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->fcode_revision[1],
+ ha->fcode_revision[0]);
+}
+
+static ssize_t
+qla2x00_optrom_fw_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+ return snprintf(buf, PAGE_SIZE, "%d.%02d.%02d %d\n",
+ ha->fw_revision[0], ha->fw_revision[1], ha->fw_revision[2],
+ ha->fw_revision[3]);
+}
+
+static ssize_t
+qla2x00_optrom_gold_fw_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha))
+ return snprintf(buf, PAGE_SIZE, "\n");
+
+ return snprintf(buf, PAGE_SIZE, "%d.%02d.%02d (%d)\n",
+ ha->gold_fw_version[0], ha->gold_fw_version[1],
+ ha->gold_fw_version[2], ha->gold_fw_version[3]);
+}
+
+static ssize_t
+qla2x00_total_isp_aborts_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+ return snprintf(buf, PAGE_SIZE, "%d\n",
+ ha->qla_stats.total_isp_aborts);
+}
+
+static ssize_t
+qla24xx_84xx_fw_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ int rval = QLA_SUCCESS;
+ uint16_t status[2] = {0, 0};
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!IS_QLA84XX(ha))
+ return snprintf(buf, PAGE_SIZE, "\n");
+
+ if (ha->cs84xx->op_fw_version == 0)
+ rval = qla84xx_verify_chip(vha, status);
+
+ if ((rval == QLA_SUCCESS) && (status[0] == 0))
+ return snprintf(buf, PAGE_SIZE, "%u\n",
+ (uint32_t)ha->cs84xx->op_fw_version);
+
+ return snprintf(buf, PAGE_SIZE, "\n");
+}
+
+static ssize_t
+qla2x00_mpi_version_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!IS_QLA81XX(ha) && !IS_QLA8031(ha))
+ return snprintf(buf, PAGE_SIZE, "\n");
+
+ return snprintf(buf, PAGE_SIZE, "%d.%02d.%02d (%x)\n",
+ ha->mpi_version[0], ha->mpi_version[1], ha->mpi_version[2],
+ ha->mpi_capabilities);
+}
+
+static ssize_t
+qla2x00_phy_version_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha))
+ return snprintf(buf, PAGE_SIZE, "\n");
+
+ return snprintf(buf, PAGE_SIZE, "%d.%02d.%02d\n",
+ ha->phy_version[0], ha->phy_version[1], ha->phy_version[2]);
+}
+
+static ssize_t
+qla2x00_flash_block_size_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ struct qla_hw_data *ha = vha->hw;
+
+ return snprintf(buf, PAGE_SIZE, "0x%x\n", ha->fdt_block_size);
+}
+
+static ssize_t
+qla2x00_vlan_id_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+
+ if (!IS_CNA_CAPABLE(vha->hw))
+ return snprintf(buf, PAGE_SIZE, "\n");
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", vha->fcoe_vlan_id);
+}
+
+static ssize_t
+qla2x00_vn_port_mac_address_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+
+ if (!IS_CNA_CAPABLE(vha->hw))
+ return snprintf(buf, PAGE_SIZE, "\n");
+
+ return snprintf(buf, PAGE_SIZE, "%02x:%02x:%02x:%02x:%02x:%02x\n",
+ vha->fcoe_vn_port_mac[5], vha->fcoe_vn_port_mac[4],
+ vha->fcoe_vn_port_mac[3], vha->fcoe_vn_port_mac[2],
+ vha->fcoe_vn_port_mac[1], vha->fcoe_vn_port_mac[0]);
+}
+
+static ssize_t
+qla2x00_fabric_param_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+
+ return snprintf(buf, PAGE_SIZE, "%d\n", vha->hw->switch_cap);
+}
+
+static ssize_t
+qla2x00_thermal_temp_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ int rval = QLA_FUNCTION_FAILED;
+ uint16_t temp, frac;
+
+ if (!vha->hw->flags.thermal_supported)
+ return snprintf(buf, PAGE_SIZE, "\n");
+
+ temp = frac = 0;
+ if (qla2x00_reset_active(vha))
+ ql_log(ql_log_warn, vha, 0x707b,
+ "ISP reset active.\n");
+ else if (!vha->hw->flags.eeh_busy)
+ rval = qla2x00_get_thermal_temp(vha, &temp, &frac);
+ if (rval != QLA_SUCCESS)
+ return snprintf(buf, PAGE_SIZE, "\n");
+
+ return snprintf(buf, PAGE_SIZE, "%d.%02d\n", temp, frac);
+}
+
+static ssize_t
+qla2x00_fw_state_show(struct device *dev, struct device_attribute *attr,
+ char *buf)
+{
+ scsi_qla_host_t *vha = shost_priv(class_to_shost(dev));
+ int rval = QLA_FUNCTION_FAILED;
+ uint16_t state[5];
+
+ if (qla2x00_reset_active(vha))
+ ql_log(ql_log_warn, vha, 0x707c,
+ "ISP reset active.\n");
+ else if (!vha->hw->flags.eeh_busy)
+ rval = qla2x00_get_firmware_state(vha, state);
+ if (rval != QLA_SUCCESS)
+ memset(state, -1, sizeof(state));
+
+ return snprintf(buf, PAGE_SIZE, "0x%x 0x%x 0x%x 0x%x 0x%x\n", state[0],
+ state[1], state[2], state[3], state[4]);
+}
+
+static DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show, NULL);
+static DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL);
+static DEVICE_ATTR(serial_num, S_IRUGO, qla2x00_serial_num_show, NULL);
+static DEVICE_ATTR(isp_name, S_IRUGO, qla2x00_isp_name_show, NULL);
+static DEVICE_ATTR(isp_id, S_IRUGO, qla2x00_isp_id_show, NULL);
+static DEVICE_ATTR(model_name, S_IRUGO, qla2x00_model_name_show, NULL);
+static DEVICE_ATTR(model_desc, S_IRUGO, qla2x00_model_desc_show, NULL);
+static DEVICE_ATTR(pci_info, S_IRUGO, qla2x00_pci_info_show, NULL);
+static DEVICE_ATTR(link_state, S_IRUGO, qla2x00_link_state_show, NULL);
+static DEVICE_ATTR(zio, S_IRUGO | S_IWUSR, qla2x00_zio_show, qla2x00_zio_store);
+static DEVICE_ATTR(zio_timer, S_IRUGO | S_IWUSR, qla2x00_zio_timer_show,
+ qla2x00_zio_timer_store);
+static DEVICE_ATTR(beacon, S_IRUGO | S_IWUSR, qla2x00_beacon_show,
+ qla2x00_beacon_store);
+static DEVICE_ATTR(optrom_bios_version, S_IRUGO,
+ qla2x00_optrom_bios_version_show, NULL);
+static DEVICE_ATTR(optrom_efi_version, S_IRUGO,
+ qla2x00_optrom_efi_version_show, NULL);
+static DEVICE_ATTR(optrom_fcode_version, S_IRUGO,
+ qla2x00_optrom_fcode_version_show, NULL);
+static DEVICE_ATTR(optrom_fw_version, S_IRUGO, qla2x00_optrom_fw_version_show,
+ NULL);
+static DEVICE_ATTR(optrom_gold_fw_version, S_IRUGO,
+ qla2x00_optrom_gold_fw_version_show, NULL);
+static DEVICE_ATTR(84xx_fw_version, S_IRUGO, qla24xx_84xx_fw_version_show,
+ NULL);
+static DEVICE_ATTR(total_isp_aborts, S_IRUGO, qla2x00_total_isp_aborts_show,
+ NULL);
+static DEVICE_ATTR(mpi_version, S_IRUGO, qla2x00_mpi_version_show, NULL);
+static DEVICE_ATTR(phy_version, S_IRUGO, qla2x00_phy_version_show, NULL);
+static DEVICE_ATTR(flash_block_size, S_IRUGO, qla2x00_flash_block_size_show,
+ NULL);
+static DEVICE_ATTR(vlan_id, S_IRUGO, qla2x00_vlan_id_show, NULL);
+static DEVICE_ATTR(vn_port_mac_address, S_IRUGO,
+ qla2x00_vn_port_mac_address_show, NULL);
+static DEVICE_ATTR(fabric_param, S_IRUGO, qla2x00_fabric_param_show, NULL);
+static DEVICE_ATTR(fw_state, S_IRUGO, qla2x00_fw_state_show, NULL);
+static DEVICE_ATTR(thermal_temp, S_IRUGO, qla2x00_thermal_temp_show, NULL);
+
+struct device_attribute *qla2x00_host_attrs[] = {
+ &dev_attr_driver_version,
+ &dev_attr_fw_version,
+ &dev_attr_serial_num,
+ &dev_attr_isp_name,
+ &dev_attr_isp_id,
+ &dev_attr_model_name,
+ &dev_attr_model_desc,
+ &dev_attr_pci_info,
+ &dev_attr_link_state,
+ &dev_attr_zio,
+ &dev_attr_zio_timer,
+ &dev_attr_beacon,
+ &dev_attr_optrom_bios_version,
+ &dev_attr_optrom_efi_version,
+ &dev_attr_optrom_fcode_version,
+ &dev_attr_optrom_fw_version,
+ &dev_attr_84xx_fw_version,
+ &dev_attr_total_isp_aborts,
+ &dev_attr_mpi_version,
+ &dev_attr_phy_version,
+ &dev_attr_flash_block_size,
+ &dev_attr_vlan_id,
+ &dev_attr_vn_port_mac_address,
+ &dev_attr_fabric_param,
+ &dev_attr_fw_state,
+ &dev_attr_optrom_gold_fw_version,
+ &dev_attr_thermal_temp,
+ NULL,
+};
+
+/* Host attributes. */
+
+static void
+qla2x00_get_host_port_id(struct Scsi_Host *shost)
+{
+ scsi_qla_host_t *vha = shost_priv(shost);
+
+ fc_host_port_id(shost) = vha->d_id.b.domain << 16 |
+ vha->d_id.b.area << 8 | vha->d_id.b.al_pa;
+}
+
+static void
+qla2x00_get_host_speed(struct Scsi_Host *shost)
+{
+ struct qla_hw_data *ha = ((struct scsi_qla_host *)
+ (shost_priv(shost)))->hw;
+ u32 speed = FC_PORTSPEED_UNKNOWN;
+
+ switch (ha->link_data_rate) {
+ case PORT_SPEED_1GB:
+ speed = FC_PORTSPEED_1GBIT;
+ break;
+ case PORT_SPEED_2GB:
+ speed = FC_PORTSPEED_2GBIT;
+ break;
+ case PORT_SPEED_4GB:
+ speed = FC_PORTSPEED_4GBIT;
+ break;
+ case PORT_SPEED_8GB:
+ speed = FC_PORTSPEED_8GBIT;
+ break;
+ case PORT_SPEED_10GB:
+ speed = FC_PORTSPEED_10GBIT;
+ break;
+ case PORT_SPEED_16GB:
+ speed = FC_PORTSPEED_16GBIT;
+ break;
+ }
+ fc_host_speed(shost) = speed;
+}
+
+static void
+qla2x00_get_host_port_type(struct Scsi_Host *shost)
+{
+ scsi_qla_host_t *vha = shost_priv(shost);
+ uint32_t port_type = FC_PORTTYPE_UNKNOWN;
+
+ if (vha->vp_idx) {
+ fc_host_port_type(shost) = FC_PORTTYPE_NPIV;
+ return;
+ }
+ switch (vha->hw->current_topology) {
+ case ISP_CFG_NL:
+ port_type = FC_PORTTYPE_LPORT;
+ break;
+ case ISP_CFG_FL:
+ port_type = FC_PORTTYPE_NLPORT;
+ break;
+ case ISP_CFG_N:
+ port_type = FC_PORTTYPE_PTP;
+ break;
+ case ISP_CFG_F:
+ port_type = FC_PORTTYPE_NPORT;
+ break;
+ }
+ fc_host_port_type(shost) = port_type;
+}
+
+static void
+qla2x00_get_starget_node_name(struct scsi_target *starget)
+{
+ struct Scsi_Host *host = dev_to_shost(starget->dev.parent);
+ scsi_qla_host_t *vha = shost_priv(host);
+ fc_port_t *fcport;
+ u64 node_name = 0;
+
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ if (fcport->rport &&
+ starget->id == fcport->rport->scsi_target_id) {
+ node_name = wwn_to_u64(fcport->node_name);
+ break;
+ }
+ }
+
+ fc_starget_node_name(starget) = node_name;
+}
+
+static void
+qla2x00_get_starget_port_name(struct scsi_target *starget)
+{
+ struct Scsi_Host *host = dev_to_shost(starget->dev.parent);
+ scsi_qla_host_t *vha = shost_priv(host);
+ fc_port_t *fcport;
+ u64 port_name = 0;
+
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ if (fcport->rport &&
+ starget->id == fcport->rport->scsi_target_id) {
+ port_name = wwn_to_u64(fcport->port_name);
+ break;
+ }
+ }
+
+ fc_starget_port_name(starget) = port_name;
+}
+
+static void
+qla2x00_get_starget_port_id(struct scsi_target *starget)
+{
+ struct Scsi_Host *host = dev_to_shost(starget->dev.parent);
+ scsi_qla_host_t *vha = shost_priv(host);
+ fc_port_t *fcport;
+ uint32_t port_id = ~0U;
+
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ if (fcport->rport &&
+ starget->id == fcport->rport->scsi_target_id) {
+ port_id = fcport->d_id.b.domain << 16 |
+ fcport->d_id.b.area << 8 | fcport->d_id.b.al_pa;
+ break;
+ }
+ }
+
+ fc_starget_port_id(starget) = port_id;
+}
+
+static void
+qla2x00_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout)
+{
+ if (timeout)
+ rport->dev_loss_tmo = timeout;
+ else
+ rport->dev_loss_tmo = 1;
+}
+
+static void
+qla2x00_dev_loss_tmo_callbk(struct fc_rport *rport)
+{
+ struct Scsi_Host *host = rport_to_shost(rport);
+ fc_port_t *fcport = *(fc_port_t **)rport->dd_data;
+ unsigned long flags;
+
+ if (!fcport)
+ return;
+
+ /* Now that the rport has been deleted, set the fcport state to
+ FCS_DEVICE_DEAD */
+ qla2x00_set_fcport_state(fcport, FCS_DEVICE_DEAD);
+
+ /*
+ * Transport has effectively 'deleted' the rport, clear
+ * all local references.
+ */
+ spin_lock_irqsave(host->host_lock, flags);
+ fcport->rport = fcport->drport = NULL;
+ *((fc_port_t **)rport->dd_data) = NULL;
+ spin_unlock_irqrestore(host->host_lock, flags);
+
+ if (test_bit(ABORT_ISP_ACTIVE, &fcport->vha->dpc_flags))
+ return;
+
+ if (unlikely(pci_channel_offline(fcport->vha->hw->pdev))) {
+ qla2x00_abort_all_cmds(fcport->vha, DID_NO_CONNECT << 16);
+ return;
+ }
+}
+
+static void
+qla2x00_terminate_rport_io(struct fc_rport *rport)
+{
+ fc_port_t *fcport = *(fc_port_t **)rport->dd_data;
+
+ if (!fcport)
+ return;
+
+ if (test_bit(ABORT_ISP_ACTIVE, &fcport->vha->dpc_flags))
+ return;
+
+ if (unlikely(pci_channel_offline(fcport->vha->hw->pdev))) {
+ qla2x00_abort_all_cmds(fcport->vha, DID_NO_CONNECT << 16);
+ return;
+ }
+ /*
+ * At this point all fcport's software-states are cleared. Perform any
+ * final cleanup of firmware resources (PCBs and XCBs).
+ */
+ if (fcport->loop_id != FC_NO_LOOP_ID &&
+ !test_bit(UNLOADING, &fcport->vha->dpc_flags)) {
+ if (IS_FWI2_CAPABLE(fcport->vha->hw))
+ fcport->vha->hw->isp_ops->fabric_logout(fcport->vha,
+ fcport->loop_id, fcport->d_id.b.domain,
+ fcport->d_id.b.area, fcport->d_id.b.al_pa);
+ else
+ qla2x00_port_logout(fcport->vha, fcport);
+ }
+}
+
+static int
+qla2x00_issue_lip(struct Scsi_Host *shost)
+{
+ scsi_qla_host_t *vha = shost_priv(shost);
+
+ qla2x00_loop_reset(vha);
+ return 0;
+}
+
+static struct fc_host_statistics *
+qla2x00_get_fc_host_stats(struct Scsi_Host *shost)
+{
+ scsi_qla_host_t *vha = shost_priv(shost);
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+ int rval;
+ struct link_statistics *stats;
+ dma_addr_t stats_dma;
+ struct fc_host_statistics *pfc_host_stat;
+
+ pfc_host_stat = &ha->fc_host_stat;
+ memset(pfc_host_stat, -1, sizeof(struct fc_host_statistics));
+
+ if (test_bit(UNLOADING, &vha->dpc_flags))
+ goto done;
+
+ if (unlikely(pci_channel_offline(ha->pdev)))
+ goto done;
+
+ stats = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &stats_dma);
+ if (stats == NULL) {
+ ql_log(ql_log_warn, vha, 0x707d,
+ "Failed to allocate memory for stats.\n");
+ goto done;
+ }
+ memset(stats, 0, DMA_POOL_SIZE);
+
+ rval = QLA_FUNCTION_FAILED;
+ if (IS_FWI2_CAPABLE(ha)) {
+ rval = qla24xx_get_isp_stats(base_vha, stats, stats_dma);
+ } else if (atomic_read(&base_vha->loop_state) == LOOP_READY &&
+ !qla2x00_reset_active(vha) && !ha->dpc_active) {
+ /* Must be in a 'READY' state for statistics retrieval. */
+ rval = qla2x00_get_link_status(base_vha, base_vha->loop_id,
+ stats, stats_dma);
+ }
+
+ if (rval != QLA_SUCCESS)
+ goto done_free;
+
+ pfc_host_stat->link_failure_count = stats->link_fail_cnt;
+ pfc_host_stat->loss_of_sync_count = stats->loss_sync_cnt;
+ pfc_host_stat->loss_of_signal_count = stats->loss_sig_cnt;
+ pfc_host_stat->prim_seq_protocol_err_count = stats->prim_seq_err_cnt;
+ pfc_host_stat->invalid_tx_word_count = stats->inval_xmit_word_cnt;
+ pfc_host_stat->invalid_crc_count = stats->inval_crc_cnt;
+ if (IS_FWI2_CAPABLE(ha)) {
+ pfc_host_stat->lip_count = stats->lip_cnt;
+ pfc_host_stat->tx_frames = stats->tx_frames;
+ pfc_host_stat->rx_frames = stats->rx_frames;
+ pfc_host_stat->dumped_frames = stats->dumped_frames;
+ pfc_host_stat->nos_count = stats->nos_rcvd;
+ }
+ pfc_host_stat->fcp_input_megabytes = ha->qla_stats.input_bytes >> 20;
+ pfc_host_stat->fcp_output_megabytes = ha->qla_stats.output_bytes >> 20;
+
+done_free:
+ dma_pool_free(ha->s_dma_pool, stats, stats_dma);
+done:
+ return pfc_host_stat;
+}
+
+static void
+qla2x00_get_host_symbolic_name(struct Scsi_Host *shost)
+{
+ scsi_qla_host_t *vha = shost_priv(shost);
+
+ qla2x00_get_sym_node_name(vha, fc_host_symbolic_name(shost));
+}
+
+static void
+qla2x00_set_host_system_hostname(struct Scsi_Host *shost)
+{
+ scsi_qla_host_t *vha = shost_priv(shost);
+
+ set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
+}
+
+static void
+qla2x00_get_host_fabric_name(struct Scsi_Host *shost)
+{
+ scsi_qla_host_t *vha = shost_priv(shost);
+ uint8_t node_name[WWN_SIZE] = { 0xFF, 0xFF, 0xFF, 0xFF, \
+ 0xFF, 0xFF, 0xFF, 0xFF};
+ u64 fabric_name = wwn_to_u64(node_name);
+
+ if (vha->device_flags & SWITCH_FOUND)
+ fabric_name = wwn_to_u64(vha->fabric_node_name);
+
+ fc_host_fabric_name(shost) = fabric_name;
+}
+
+static void
+qla2x00_get_host_port_state(struct Scsi_Host *shost)
+{
+ scsi_qla_host_t *vha = shost_priv(shost);
+ struct scsi_qla_host *base_vha = pci_get_drvdata(vha->hw->pdev);
+
+ if (!base_vha->flags.online) {
+ fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE;
+ return;
+ }
+
+ switch (atomic_read(&base_vha->loop_state)) {
+ case LOOP_UPDATE:
+ fc_host_port_state(shost) = FC_PORTSTATE_DIAGNOSTICS;
+ break;
+ case LOOP_DOWN:
+ if (test_bit(LOOP_RESYNC_NEEDED, &base_vha->dpc_flags))
+ fc_host_port_state(shost) = FC_PORTSTATE_DIAGNOSTICS;
+ else
+ fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN;
+ break;
+ case LOOP_DEAD:
+ fc_host_port_state(shost) = FC_PORTSTATE_LINKDOWN;
+ break;
+ case LOOP_READY:
+ fc_host_port_state(shost) = FC_PORTSTATE_ONLINE;
+ break;
+ default:
+ fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN;
+ break;
+ }
+}
+
+static int
+qla24xx_vport_create(struct fc_vport *fc_vport, bool disable)
+{
+ int ret = 0;
+ uint8_t qos = 0;
+ scsi_qla_host_t *base_vha = shost_priv(fc_vport->shost);
+ scsi_qla_host_t *vha = NULL;
+ struct qla_hw_data *ha = base_vha->hw;
+ uint16_t options = 0;
+ int cnt;
+ struct req_que *req = ha->req_q_map[0];
+
+ ret = qla24xx_vport_create_req_sanity_check(fc_vport);
+ if (ret) {
+ ql_log(ql_log_warn, vha, 0x707e,
+ "Vport sanity check failed, status %x\n", ret);
+ return (ret);
+ }
+
+ vha = qla24xx_create_vhost(fc_vport);
+ if (vha == NULL) {
+ ql_log(ql_log_warn, vha, 0x707f, "Vport create host failed.\n");
+ return FC_VPORT_FAILED;
+ }
+ if (disable) {
+ atomic_set(&vha->vp_state, VP_OFFLINE);
+ fc_vport_set_state(fc_vport, FC_VPORT_DISABLED);
+ } else
+ atomic_set(&vha->vp_state, VP_FAILED);
+
+ /* ready to create vport */
+ ql_log(ql_log_info, vha, 0x7080,
+ "VP entry id %d assigned.\n", vha->vp_idx);
+
+ /* initialized vport states */
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ vha->vp_err_state= VP_ERR_PORTDWN;
+ vha->vp_prev_err_state= VP_ERR_UNKWN;
+ /* Check if physical ha port is Up */
+ if (atomic_read(&base_vha->loop_state) == LOOP_DOWN ||
+ atomic_read(&base_vha->loop_state) == LOOP_DEAD) {
+ /* Don't retry or attempt login of this virtual port */
+ ql_dbg(ql_dbg_user, vha, 0x7081,
+ "Vport loop state is not UP.\n");
+ atomic_set(&vha->loop_state, LOOP_DEAD);
+ if (!disable)
+ fc_vport_set_state(fc_vport, FC_VPORT_LINKDOWN);
+ }
+
+ if (IS_T10_PI_CAPABLE(ha) && ql2xenabledif) {
+ if (ha->fw_attributes & BIT_4) {
+ int prot = 0;
+ vha->flags.difdix_supported = 1;
+ ql_dbg(ql_dbg_user, vha, 0x7082,
+ "Registered for DIF/DIX type 1 and 3 protection.\n");
+ if (ql2xenabledif == 1)
+ prot = SHOST_DIX_TYPE0_PROTECTION;
+ scsi_host_set_prot(vha->host,
+ prot | SHOST_DIF_TYPE1_PROTECTION
+ | SHOST_DIF_TYPE2_PROTECTION
+ | SHOST_DIF_TYPE3_PROTECTION
+ | SHOST_DIX_TYPE1_PROTECTION
+ | SHOST_DIX_TYPE2_PROTECTION
+ | SHOST_DIX_TYPE3_PROTECTION);
+ scsi_host_set_guard(vha->host, SHOST_DIX_GUARD_CRC);
+ } else
+ vha->flags.difdix_supported = 0;
+ }
+
+ if (scsi_add_host_with_dma(vha->host, &fc_vport->dev,
+ &ha->pdev->dev)) {
+ ql_dbg(ql_dbg_user, vha, 0x7083,
+ "scsi_add_host failure for VP[%d].\n", vha->vp_idx);
+ goto vport_create_failed_2;
+ }
+
+ /* initialize attributes */
+ fc_host_dev_loss_tmo(vha->host) = ha->port_down_retry_count;
+ fc_host_node_name(vha->host) = wwn_to_u64(vha->node_name);
+ fc_host_port_name(vha->host) = wwn_to_u64(vha->port_name);
+ fc_host_supported_classes(vha->host) =
+ fc_host_supported_classes(base_vha->host);
+ fc_host_supported_speeds(vha->host) =
+ fc_host_supported_speeds(base_vha->host);
+
+ qla24xx_vport_disable(fc_vport, disable);
+
+ if (ha->flags.cpu_affinity_enabled) {
+ req = ha->req_q_map[1];
+ ql_dbg(ql_dbg_multiq, vha, 0xc000,
+ "Request queue %p attached with "
+ "VP[%d], cpu affinity =%d\n",
+ req, vha->vp_idx, ha->flags.cpu_affinity_enabled);
+ goto vport_queue;
+ } else if (ql2xmaxqueues == 1 || !ha->npiv_info)
+ goto vport_queue;
+ /* Create a request queue in QoS mode for the vport */
+ for (cnt = 0; cnt < ha->nvram_npiv_size; cnt++) {
+ if (memcmp(ha->npiv_info[cnt].port_name, vha->port_name, 8) == 0
+ && memcmp(ha->npiv_info[cnt].node_name, vha->node_name,
+ 8) == 0) {
+ qos = ha->npiv_info[cnt].q_qos;
+ break;
+ }
+ }
+
+ if (qos) {
+ ret = qla25xx_create_req_que(ha, options, vha->vp_idx, 0, 0,
+ qos);
+ if (!ret)
+ ql_log(ql_log_warn, vha, 0x7084,
+ "Can't create request queue for VP[%d]\n",
+ vha->vp_idx);
+ else {
+ ql_dbg(ql_dbg_multiq, vha, 0xc001,
+ "Request Que:%d Q0s: %d) created for VP[%d]\n",
+ ret, qos, vha->vp_idx);
+ ql_dbg(ql_dbg_user, vha, 0x7085,
+ "Request Que:%d Q0s: %d) created for VP[%d]\n",
+ ret, qos, vha->vp_idx);
+ req = ha->req_q_map[ret];
+ }
+ }
+
+vport_queue:
+ vha->req = req;
+ return 0;
+
+vport_create_failed_2:
+ qla24xx_disable_vp(vha);
+ qla24xx_deallocate_vp_id(vha);
+ scsi_host_put(vha->host);
+ return FC_VPORT_FAILED;
+}
+
+static int
+qla24xx_vport_delete(struct fc_vport *fc_vport)
+{
+ scsi_qla_host_t *vha = fc_vport->dd_data;
+ struct qla_hw_data *ha = vha->hw;
+ uint16_t id = vha->vp_idx;
+
+ while (test_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags) ||
+ test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags))
+ msleep(1000);
+
+ qla24xx_disable_vp(vha);
+
+ vha->flags.delete_progress = 1;
+
+ fc_remove_host(vha->host);
+
+ scsi_remove_host(vha->host);
+
+ /* Allow timer to run to drain queued items, when removing vp */
+ qla24xx_deallocate_vp_id(vha);
+
+ if (vha->timer_active) {
+ qla2x00_vp_stop_timer(vha);
+ ql_dbg(ql_dbg_user, vha, 0x7086,
+ "Timer for the VP[%d] has stopped\n", vha->vp_idx);
+ }
+
+ /* No pending activities shall be there on the vha now */
+ if (ql2xextended_error_logging & ql_dbg_user)
+ msleep(random32()%10); /* Just to see if something falls on
+ * the net we have placed below */
+
+ BUG_ON(atomic_read(&vha->vref_count));
+
+ qla2x00_free_fcports(vha);
+
+ mutex_lock(&ha->vport_lock);
+ ha->cur_vport_count--;
+ clear_bit(vha->vp_idx, ha->vp_idx_map);
+ mutex_unlock(&ha->vport_lock);
+
+ if (vha->req->id && !ha->flags.cpu_affinity_enabled) {
+ if (qla25xx_delete_req_que(vha, vha->req) != QLA_SUCCESS)
+ ql_log(ql_log_warn, vha, 0x7087,
+ "Queue delete failed.\n");
+ }
+
+ ql_log(ql_log_info, vha, 0x7088, "VP[%d] deleted.\n", id);
+ scsi_host_put(vha->host);
+ return 0;
+}
+
+static int
+qla24xx_vport_disable(struct fc_vport *fc_vport, bool disable)
+{
+ scsi_qla_host_t *vha = fc_vport->dd_data;
+
+ if (disable)
+ qla24xx_disable_vp(vha);
+ else
+ qla24xx_enable_vp(vha);
+
+ return 0;
+}
+
+struct fc_function_template qla2xxx_transport_functions = {
+
+ .show_host_node_name = 1,
+ .show_host_port_name = 1,
+ .show_host_supported_classes = 1,
+ .show_host_supported_speeds = 1,
+
+ .get_host_port_id = qla2x00_get_host_port_id,
+ .show_host_port_id = 1,
+ .get_host_speed = qla2x00_get_host_speed,
+ .show_host_speed = 1,
+ .get_host_port_type = qla2x00_get_host_port_type,
+ .show_host_port_type = 1,
+ .get_host_symbolic_name = qla2x00_get_host_symbolic_name,
+ .show_host_symbolic_name = 1,
+ .set_host_system_hostname = qla2x00_set_host_system_hostname,
+ .show_host_system_hostname = 1,
+ .get_host_fabric_name = qla2x00_get_host_fabric_name,
+ .show_host_fabric_name = 1,
+ .get_host_port_state = qla2x00_get_host_port_state,
+ .show_host_port_state = 1,
+
+ .dd_fcrport_size = sizeof(struct fc_port *),
+ .show_rport_supported_classes = 1,
+
+ .get_starget_node_name = qla2x00_get_starget_node_name,
+ .show_starget_node_name = 1,
+ .get_starget_port_name = qla2x00_get_starget_port_name,
+ .show_starget_port_name = 1,
+ .get_starget_port_id = qla2x00_get_starget_port_id,
+ .show_starget_port_id = 1,
+
+ .set_rport_dev_loss_tmo = qla2x00_set_rport_loss_tmo,
+ .show_rport_dev_loss_tmo = 1,
+
+ .issue_fc_host_lip = qla2x00_issue_lip,
+ .dev_loss_tmo_callbk = qla2x00_dev_loss_tmo_callbk,
+ .terminate_rport_io = qla2x00_terminate_rport_io,
+ .get_fc_host_stats = qla2x00_get_fc_host_stats,
+
+ .vport_create = qla24xx_vport_create,
+ .vport_disable = qla24xx_vport_disable,
+ .vport_delete = qla24xx_vport_delete,
+ .bsg_request = qla24xx_bsg_request,
+ .bsg_timeout = qla24xx_bsg_timeout,
+};
+
+struct fc_function_template qla2xxx_transport_vport_functions = {
+
+ .show_host_node_name = 1,
+ .show_host_port_name = 1,
+ .show_host_supported_classes = 1,
+
+ .get_host_port_id = qla2x00_get_host_port_id,
+ .show_host_port_id = 1,
+ .get_host_speed = qla2x00_get_host_speed,
+ .show_host_speed = 1,
+ .get_host_port_type = qla2x00_get_host_port_type,
+ .show_host_port_type = 1,
+ .get_host_symbolic_name = qla2x00_get_host_symbolic_name,
+ .show_host_symbolic_name = 1,
+ .set_host_system_hostname = qla2x00_set_host_system_hostname,
+ .show_host_system_hostname = 1,
+ .get_host_fabric_name = qla2x00_get_host_fabric_name,
+ .show_host_fabric_name = 1,
+ .get_host_port_state = qla2x00_get_host_port_state,
+ .show_host_port_state = 1,
+
+ .dd_fcrport_size = sizeof(struct fc_port *),
+ .show_rport_supported_classes = 1,
+
+ .get_starget_node_name = qla2x00_get_starget_node_name,
+ .show_starget_node_name = 1,
+ .get_starget_port_name = qla2x00_get_starget_port_name,
+ .show_starget_port_name = 1,
+ .get_starget_port_id = qla2x00_get_starget_port_id,
+ .show_starget_port_id = 1,
+
+ .set_rport_dev_loss_tmo = qla2x00_set_rport_loss_tmo,
+ .show_rport_dev_loss_tmo = 1,
+
+ .issue_fc_host_lip = qla2x00_issue_lip,
+ .dev_loss_tmo_callbk = qla2x00_dev_loss_tmo_callbk,
+ .terminate_rport_io = qla2x00_terminate_rport_io,
+ .get_fc_host_stats = qla2x00_get_fc_host_stats,
+ .bsg_request = qla24xx_bsg_request,
+ .bsg_timeout = qla24xx_bsg_timeout,
+};
+
+void
+qla2x00_init_host_attr(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ u32 speed = FC_PORTSPEED_UNKNOWN;
+
+ fc_host_dev_loss_tmo(vha->host) = ha->port_down_retry_count;
+ fc_host_node_name(vha->host) = wwn_to_u64(vha->node_name);
+ fc_host_port_name(vha->host) = wwn_to_u64(vha->port_name);
+ fc_host_supported_classes(vha->host) = FC_COS_CLASS3;
+ fc_host_max_npiv_vports(vha->host) = ha->max_npiv_vports;
+ fc_host_npiv_vports_inuse(vha->host) = ha->cur_vport_count;
+
+ if (IS_CNA_CAPABLE(ha))
+ speed = FC_PORTSPEED_10GBIT;
+ else if (IS_QLA25XX(ha))
+ speed = FC_PORTSPEED_8GBIT | FC_PORTSPEED_4GBIT |
+ FC_PORTSPEED_2GBIT | FC_PORTSPEED_1GBIT;
+ else if (IS_QLA24XX_TYPE(ha))
+ speed = FC_PORTSPEED_4GBIT | FC_PORTSPEED_2GBIT |
+ FC_PORTSPEED_1GBIT;
+ else if (IS_QLA23XX(ha))
+ speed = FC_PORTSPEED_2GBIT | FC_PORTSPEED_1GBIT;
+ else
+ speed = FC_PORTSPEED_1GBIT;
+ fc_host_supported_speeds(vha->host) = speed;
+}
diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c
new file mode 100644
index 00000000..bc3cc6d9
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_bsg.c
@@ -0,0 +1,1716 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2003-2011 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#include "qla_def.h"
+
+#include <linux/kthread.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+
+/* BSG support for ELS/CT pass through */
+void
+qla2x00_bsg_job_done(void *data, void *ptr, int res)
+{
+ srb_t *sp = (srb_t *)ptr;
+ struct scsi_qla_host *vha = (scsi_qla_host_t *)data;
+ struct fc_bsg_job *bsg_job = sp->u.bsg_job;
+
+ bsg_job->reply->result = res;
+ bsg_job->job_done(bsg_job);
+ sp->free(vha, sp);
+}
+
+void
+qla2x00_bsg_sp_free(void *data, void *ptr)
+{
+ srb_t *sp = (srb_t *)ptr;
+ struct scsi_qla_host *vha = (scsi_qla_host_t *)data;
+ struct fc_bsg_job *bsg_job = sp->u.bsg_job;
+ struct qla_hw_data *ha = vha->hw;
+
+ dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
+
+ dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+
+ if (sp->type == SRB_CT_CMD ||
+ sp->type == SRB_ELS_CMD_HST)
+ kfree(sp->fcport);
+ mempool_free(sp, vha->hw->srb_mempool);
+}
+
+int
+qla24xx_fcp_prio_cfg_valid(scsi_qla_host_t *vha,
+ struct qla_fcp_prio_cfg *pri_cfg, uint8_t flag)
+{
+ int i, ret, num_valid;
+ uint8_t *bcode;
+ struct qla_fcp_prio_entry *pri_entry;
+ uint32_t *bcode_val_ptr, bcode_val;
+
+ ret = 1;
+ num_valid = 0;
+ bcode = (uint8_t *)pri_cfg;
+ bcode_val_ptr = (uint32_t *)pri_cfg;
+ bcode_val = (uint32_t)(*bcode_val_ptr);
+
+ if (bcode_val == 0xFFFFFFFF) {
+ /* No FCP Priority config data in flash */
+ ql_dbg(ql_dbg_user, vha, 0x7051,
+ "No FCP Priority config data.\n");
+ return 0;
+ }
+
+ if (bcode[0] != 'H' || bcode[1] != 'Q' || bcode[2] != 'O' ||
+ bcode[3] != 'S') {
+ /* Invalid FCP priority data header*/
+ ql_dbg(ql_dbg_user, vha, 0x7052,
+ "Invalid FCP Priority data header. bcode=0x%x.\n",
+ bcode_val);
+ return 0;
+ }
+ if (flag != 1)
+ return ret;
+
+ pri_entry = &pri_cfg->entry[0];
+ for (i = 0; i < pri_cfg->num_entries; i++) {
+ if (pri_entry->flags & FCP_PRIO_ENTRY_TAG_VALID)
+ num_valid++;
+ pri_entry++;
+ }
+
+ if (num_valid == 0) {
+ /* No valid FCP priority data entries */
+ ql_dbg(ql_dbg_user, vha, 0x7053,
+ "No valid FCP Priority data entries.\n");
+ ret = 0;
+ } else {
+ /* FCP priority data is valid */
+ ql_dbg(ql_dbg_user, vha, 0x7054,
+ "Valid FCP priority data. num entries = %d.\n",
+ num_valid);
+ }
+
+ return ret;
+}
+
+static int
+qla24xx_proc_fcp_prio_cfg_cmd(struct fc_bsg_job *bsg_job)
+{
+ struct Scsi_Host *host = bsg_job->shost;
+ scsi_qla_host_t *vha = shost_priv(host);
+ struct qla_hw_data *ha = vha->hw;
+ int ret = 0;
+ uint32_t len;
+ uint32_t oper;
+
+ if (!(IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) || IS_QLA82XX(ha))) {
+ ret = -EINVAL;
+ goto exit_fcp_prio_cfg;
+ }
+
+ /* Get the sub command */
+ oper = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1];
+
+ /* Only set config is allowed if config memory is not allocated */
+ if (!ha->fcp_prio_cfg && (oper != QLFC_FCP_PRIO_SET_CONFIG)) {
+ ret = -EINVAL;
+ goto exit_fcp_prio_cfg;
+ }
+ switch (oper) {
+ case QLFC_FCP_PRIO_DISABLE:
+ if (ha->flags.fcp_prio_enabled) {
+ ha->flags.fcp_prio_enabled = 0;
+ ha->fcp_prio_cfg->attributes &=
+ ~FCP_PRIO_ATTR_ENABLE;
+ qla24xx_update_all_fcp_prio(vha);
+ bsg_job->reply->result = DID_OK;
+ } else {
+ ret = -EINVAL;
+ bsg_job->reply->result = (DID_ERROR << 16);
+ goto exit_fcp_prio_cfg;
+ }
+ break;
+
+ case QLFC_FCP_PRIO_ENABLE:
+ if (!ha->flags.fcp_prio_enabled) {
+ if (ha->fcp_prio_cfg) {
+ ha->flags.fcp_prio_enabled = 1;
+ ha->fcp_prio_cfg->attributes |=
+ FCP_PRIO_ATTR_ENABLE;
+ qla24xx_update_all_fcp_prio(vha);
+ bsg_job->reply->result = DID_OK;
+ } else {
+ ret = -EINVAL;
+ bsg_job->reply->result = (DID_ERROR << 16);
+ goto exit_fcp_prio_cfg;
+ }
+ }
+ break;
+
+ case QLFC_FCP_PRIO_GET_CONFIG:
+ len = bsg_job->reply_payload.payload_len;
+ if (!len || len > FCP_PRIO_CFG_SIZE) {
+ ret = -EINVAL;
+ bsg_job->reply->result = (DID_ERROR << 16);
+ goto exit_fcp_prio_cfg;
+ }
+
+ bsg_job->reply->result = DID_OK;
+ bsg_job->reply->reply_payload_rcv_len =
+ sg_copy_from_buffer(
+ bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, ha->fcp_prio_cfg,
+ len);
+
+ break;
+
+ case QLFC_FCP_PRIO_SET_CONFIG:
+ len = bsg_job->request_payload.payload_len;
+ if (!len || len > FCP_PRIO_CFG_SIZE) {
+ bsg_job->reply->result = (DID_ERROR << 16);
+ ret = -EINVAL;
+ goto exit_fcp_prio_cfg;
+ }
+
+ if (!ha->fcp_prio_cfg) {
+ ha->fcp_prio_cfg = vmalloc(FCP_PRIO_CFG_SIZE);
+ if (!ha->fcp_prio_cfg) {
+ ql_log(ql_log_warn, vha, 0x7050,
+ "Unable to allocate memory for fcp prio "
+ "config data (%x).\n", FCP_PRIO_CFG_SIZE);
+ bsg_job->reply->result = (DID_ERROR << 16);
+ ret = -ENOMEM;
+ goto exit_fcp_prio_cfg;
+ }
+ }
+
+ memset(ha->fcp_prio_cfg, 0, FCP_PRIO_CFG_SIZE);
+ sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, ha->fcp_prio_cfg,
+ FCP_PRIO_CFG_SIZE);
+
+ /* validate fcp priority data */
+
+ if (!qla24xx_fcp_prio_cfg_valid(vha,
+ (struct qla_fcp_prio_cfg *) ha->fcp_prio_cfg, 1)) {
+ bsg_job->reply->result = (DID_ERROR << 16);
+ ret = -EINVAL;
+ /* If buffer was invalidatic int
+ * fcp_prio_cfg is of no use
+ */
+ vfree(ha->fcp_prio_cfg);
+ ha->fcp_prio_cfg = NULL;
+ goto exit_fcp_prio_cfg;
+ }
+
+ ha->flags.fcp_prio_enabled = 0;
+ if (ha->fcp_prio_cfg->attributes & FCP_PRIO_ATTR_ENABLE)
+ ha->flags.fcp_prio_enabled = 1;
+ qla24xx_update_all_fcp_prio(vha);
+ bsg_job->reply->result = DID_OK;
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+exit_fcp_prio_cfg:
+ bsg_job->job_done(bsg_job);
+ return ret;
+}
+
+static int
+qla2x00_process_els(struct fc_bsg_job *bsg_job)
+{
+ struct fc_rport *rport;
+ fc_port_t *fcport = NULL;
+ struct Scsi_Host *host;
+ scsi_qla_host_t *vha;
+ struct qla_hw_data *ha;
+ srb_t *sp;
+ const char *type;
+ int req_sg_cnt, rsp_sg_cnt;
+ int rval = (DRIVER_ERROR << 16);
+ uint16_t nextlid = 0;
+
+ if (bsg_job->request->msgcode == FC_BSG_RPT_ELS) {
+ rport = bsg_job->rport;
+ fcport = *(fc_port_t **) rport->dd_data;
+ host = rport_to_shost(rport);
+ vha = shost_priv(host);
+ ha = vha->hw;
+ type = "FC_BSG_RPT_ELS";
+ } else {
+ host = bsg_job->shost;
+ vha = shost_priv(host);
+ ha = vha->hw;
+ type = "FC_BSG_HST_ELS_NOLOGIN";
+ }
+
+ /* pass through is supported only for ISP 4Gb or higher */
+ if (!IS_FWI2_CAPABLE(ha)) {
+ ql_dbg(ql_dbg_user, vha, 0x7001,
+ "ELS passthru not supported for ISP23xx based adapters.\n");
+ rval = -EPERM;
+ goto done;
+ }
+
+ /* Multiple SG's are not supported for ELS requests */
+ if (bsg_job->request_payload.sg_cnt > 1 ||
+ bsg_job->reply_payload.sg_cnt > 1) {
+ ql_dbg(ql_dbg_user, vha, 0x7002,
+ "Multiple SG's are not suppored for ELS requests, "
+ "request_sg_cnt=%x reply_sg_cnt=%x.\n",
+ bsg_job->request_payload.sg_cnt,
+ bsg_job->reply_payload.sg_cnt);
+ rval = -EPERM;
+ goto done;
+ }
+
+ /* ELS request for rport */
+ if (bsg_job->request->msgcode == FC_BSG_RPT_ELS) {
+ /* make sure the rport is logged in,
+ * if not perform fabric login
+ */
+ if (qla2x00_fabric_login(vha, fcport, &nextlid)) {
+ ql_dbg(ql_dbg_user, vha, 0x7003,
+ "Failed to login port %06X for ELS passthru.\n",
+ fcport->d_id.b24);
+ rval = -EIO;
+ goto done;
+ }
+ } else {
+ /* Allocate a dummy fcport structure, since functions
+ * preparing the IOCB and mailbox command retrieves port
+ * specific information from fcport structure. For Host based
+ * ELS commands there will be no fcport structure allocated
+ */
+ fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
+ if (!fcport) {
+ rval = -ENOMEM;
+ goto done;
+ }
+
+ /* Initialize all required fields of fcport */
+ fcport->vha = vha;
+ fcport->vp_idx = vha->vp_idx;
+ fcport->d_id.b.al_pa =
+ bsg_job->request->rqst_data.h_els.port_id[0];
+ fcport->d_id.b.area =
+ bsg_job->request->rqst_data.h_els.port_id[1];
+ fcport->d_id.b.domain =
+ bsg_job->request->rqst_data.h_els.port_id[2];
+ fcport->loop_id =
+ (fcport->d_id.b.al_pa == 0xFD) ?
+ NPH_FABRIC_CONTROLLER : NPH_F_PORT;
+ }
+
+ if (!vha->flags.online) {
+ ql_log(ql_log_warn, vha, 0x7005, "Host not online.\n");
+ rval = -EIO;
+ goto done;
+ }
+
+ req_sg_cnt =
+ dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ if (!req_sg_cnt) {
+ rval = -ENOMEM;
+ goto done_free_fcport;
+ }
+
+ rsp_sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ if (!rsp_sg_cnt) {
+ rval = -ENOMEM;
+ goto done_free_fcport;
+ }
+
+ if ((req_sg_cnt != bsg_job->request_payload.sg_cnt) ||
+ (rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) {
+ ql_log(ql_log_warn, vha, 0x7008,
+ "dma mapping resulted in different sg counts, "
+ "request_sg_cnt: %x dma_request_sg_cnt:%x reply_sg_cnt:%x "
+ "dma_reply_sg_cnt:%x.\n", bsg_job->request_payload.sg_cnt,
+ req_sg_cnt, bsg_job->reply_payload.sg_cnt, rsp_sg_cnt);
+ rval = -EAGAIN;
+ goto done_unmap_sg;
+ }
+
+ /* Alloc SRB structure */
+ sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
+ if (!sp) {
+ rval = -ENOMEM;
+ goto done_unmap_sg;
+ }
+
+ sp->type =
+ (bsg_job->request->msgcode == FC_BSG_RPT_ELS ?
+ SRB_ELS_CMD_RPT : SRB_ELS_CMD_HST);
+ sp->name =
+ (bsg_job->request->msgcode == FC_BSG_RPT_ELS ?
+ "bsg_els_rpt" : "bsg_els_hst");
+ sp->u.bsg_job = bsg_job;
+ sp->free = qla2x00_bsg_sp_free;
+ sp->done = qla2x00_bsg_job_done;
+
+ ql_dbg(ql_dbg_user, vha, 0x700a,
+ "bsg rqst type: %s els type: %x - loop-id=%x "
+ "portid=%-2x%02x%02x.\n", type,
+ bsg_job->request->rqst_data.h_els.command_code, fcport->loop_id,
+ fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa);
+
+ rval = qla2x00_start_sp(sp);
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x700e,
+ "qla2x00_start_sp failed = %d\n", rval);
+ mempool_free(sp, ha->srb_mempool);
+ rval = -EIO;
+ goto done_unmap_sg;
+ }
+ return rval;
+
+done_unmap_sg:
+ dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ goto done_free_fcport;
+
+done_free_fcport:
+ if (bsg_job->request->msgcode == FC_BSG_HST_ELS_NOLOGIN)
+ kfree(fcport);
+done:
+ return rval;
+}
+
+inline uint16_t
+qla24xx_calc_ct_iocbs(uint16_t dsds)
+{
+ uint16_t iocbs;
+
+ iocbs = 1;
+ if (dsds > 2) {
+ iocbs += (dsds - 2) / 5;
+ if ((dsds - 2) % 5)
+ iocbs++;
+ }
+ return iocbs;
+}
+
+static int
+qla2x00_process_ct(struct fc_bsg_job *bsg_job)
+{
+ srb_t *sp;
+ struct Scsi_Host *host = bsg_job->shost;
+ scsi_qla_host_t *vha = shost_priv(host);
+ struct qla_hw_data *ha = vha->hw;
+ int rval = (DRIVER_ERROR << 16);
+ int req_sg_cnt, rsp_sg_cnt;
+ uint16_t loop_id;
+ struct fc_port *fcport;
+ char *type = "FC_BSG_HST_CT";
+
+ req_sg_cnt =
+ dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ if (!req_sg_cnt) {
+ ql_log(ql_log_warn, vha, 0x700f,
+ "dma_map_sg return %d for request\n", req_sg_cnt);
+ rval = -ENOMEM;
+ goto done;
+ }
+
+ rsp_sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ if (!rsp_sg_cnt) {
+ ql_log(ql_log_warn, vha, 0x7010,
+ "dma_map_sg return %d for reply\n", rsp_sg_cnt);
+ rval = -ENOMEM;
+ goto done;
+ }
+
+ if ((req_sg_cnt != bsg_job->request_payload.sg_cnt) ||
+ (rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) {
+ ql_log(ql_log_warn, vha, 0x7011,
+ "request_sg_cnt: %x dma_request_sg_cnt: %x reply_sg_cnt:%x "
+ "dma_reply_sg_cnt: %x\n", bsg_job->request_payload.sg_cnt,
+ req_sg_cnt, bsg_job->reply_payload.sg_cnt, rsp_sg_cnt);
+ rval = -EAGAIN;
+ goto done_unmap_sg;
+ }
+
+ if (!vha->flags.online) {
+ ql_log(ql_log_warn, vha, 0x7012,
+ "Host is not online.\n");
+ rval = -EIO;
+ goto done_unmap_sg;
+ }
+
+ loop_id =
+ (bsg_job->request->rqst_data.h_ct.preamble_word1 & 0xFF000000)
+ >> 24;
+ switch (loop_id) {
+ case 0xFC:
+ loop_id = cpu_to_le16(NPH_SNS);
+ break;
+ case 0xFA:
+ loop_id = vha->mgmt_svr_loop_id;
+ break;
+ default:
+ ql_dbg(ql_dbg_user, vha, 0x7013,
+ "Unknown loop id: %x.\n", loop_id);
+ rval = -EINVAL;
+ goto done_unmap_sg;
+ }
+
+ /* Allocate a dummy fcport structure, since functions preparing the
+ * IOCB and mailbox command retrieves port specific information
+ * from fcport structure. For Host based ELS commands there will be
+ * no fcport structure allocated
+ */
+ fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
+ if (!fcport) {
+ ql_log(ql_log_warn, vha, 0x7014,
+ "Failed to allocate fcport.\n");
+ rval = -ENOMEM;
+ goto done_unmap_sg;
+ }
+
+ /* Initialize all required fields of fcport */
+ fcport->vha = vha;
+ fcport->vp_idx = vha->vp_idx;
+ fcport->d_id.b.al_pa = bsg_job->request->rqst_data.h_ct.port_id[0];
+ fcport->d_id.b.area = bsg_job->request->rqst_data.h_ct.port_id[1];
+ fcport->d_id.b.domain = bsg_job->request->rqst_data.h_ct.port_id[2];
+ fcport->loop_id = loop_id;
+
+ /* Alloc SRB structure */
+ sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
+ if (!sp) {
+ ql_log(ql_log_warn, vha, 0x7015,
+ "qla2x00_get_sp failed.\n");
+ rval = -ENOMEM;
+ goto done_free_fcport;
+ }
+
+ sp->type = SRB_CT_CMD;
+ sp->name = "bsg_ct";
+ sp->iocbs = qla24xx_calc_ct_iocbs(req_sg_cnt + rsp_sg_cnt);
+ sp->u.bsg_job = bsg_job;
+ sp->free = qla2x00_bsg_sp_free;
+ sp->done = qla2x00_bsg_job_done;
+
+ ql_dbg(ql_dbg_user, vha, 0x7016,
+ "bsg rqst type: %s else type: %x - "
+ "loop-id=%x portid=%02x%02x%02x.\n", type,
+ (bsg_job->request->rqst_data.h_ct.preamble_word2 >> 16),
+ fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa);
+
+ rval = qla2x00_start_sp(sp);
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x7017,
+ "qla2x00_start_sp failed=%d.\n", rval);
+ mempool_free(sp, ha->srb_mempool);
+ rval = -EIO;
+ goto done_free_fcport;
+ }
+ return rval;
+
+done_free_fcport:
+ kfree(fcport);
+done_unmap_sg:
+ dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+done:
+ return rval;
+}
+
+/* Set the port configuration to enable the
+ * internal loopback on ISP81XX
+ */
+static inline int
+qla81xx_set_internal_loopback(scsi_qla_host_t *vha, uint16_t *config,
+ uint16_t *new_config)
+{
+ int ret = 0;
+ int rval = 0;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha))
+ goto done_set_internal;
+
+ new_config[0] = config[0] | (ENABLE_INTERNAL_LOOPBACK << 1);
+ memcpy(&new_config[1], &config[1], sizeof(uint16_t) * 3) ;
+
+ ha->notify_dcbx_comp = 1;
+ ret = qla81xx_set_port_config(vha, new_config);
+ if (ret != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x7021,
+ "set port config failed.\n");
+ ha->notify_dcbx_comp = 0;
+ rval = -EINVAL;
+ goto done_set_internal;
+ }
+
+ /* Wait for DCBX complete event */
+ if (!wait_for_completion_timeout(&ha->dcbx_comp, (20 * HZ))) {
+ ql_dbg(ql_dbg_user, vha, 0x7022,
+ "State change notification not received.\n");
+ } else
+ ql_dbg(ql_dbg_user, vha, 0x7023,
+ "State change received.\n");
+
+ ha->notify_dcbx_comp = 0;
+
+done_set_internal:
+ return rval;
+}
+
+/* Set the port configuration to disable the
+ * internal loopback on ISP81XX
+ */
+static inline int
+qla81xx_reset_internal_loopback(scsi_qla_host_t *vha, uint16_t *config,
+ int wait)
+{
+ int ret = 0;
+ int rval = 0;
+ uint16_t new_config[4];
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha))
+ goto done_reset_internal;
+
+ memset(new_config, 0 , sizeof(new_config));
+ if ((config[0] & INTERNAL_LOOPBACK_MASK) >> 1 ==
+ ENABLE_INTERNAL_LOOPBACK) {
+ new_config[0] = config[0] & ~INTERNAL_LOOPBACK_MASK;
+ memcpy(&new_config[1], &config[1], sizeof(uint16_t) * 3) ;
+
+ ha->notify_dcbx_comp = wait;
+ ret = qla81xx_set_port_config(vha, new_config);
+ if (ret != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x7025,
+ "Set port config failed.\n");
+ ha->notify_dcbx_comp = 0;
+ rval = -EINVAL;
+ goto done_reset_internal;
+ }
+
+ /* Wait for DCBX complete event */
+ if (wait && !wait_for_completion_timeout(&ha->dcbx_comp,
+ (20 * HZ))) {
+ ql_dbg(ql_dbg_user, vha, 0x7026,
+ "State change notification not received.\n");
+ ha->notify_dcbx_comp = 0;
+ rval = -EINVAL;
+ goto done_reset_internal;
+ } else
+ ql_dbg(ql_dbg_user, vha, 0x7027,
+ "State change received.\n");
+
+ ha->notify_dcbx_comp = 0;
+ }
+done_reset_internal:
+ return rval;
+}
+
+static int
+qla2x00_process_loopback(struct fc_bsg_job *bsg_job)
+{
+ struct Scsi_Host *host = bsg_job->shost;
+ scsi_qla_host_t *vha = shost_priv(host);
+ struct qla_hw_data *ha = vha->hw;
+ int rval;
+ uint8_t command_sent;
+ char *type;
+ struct msg_echo_lb elreq;
+ uint16_t response[MAILBOX_REGISTER_COUNT];
+ uint16_t config[4], new_config[4];
+ uint8_t *fw_sts_ptr;
+ uint8_t *req_data = NULL;
+ dma_addr_t req_data_dma;
+ uint32_t req_data_len;
+ uint8_t *rsp_data = NULL;
+ dma_addr_t rsp_data_dma;
+ uint32_t rsp_data_len;
+
+ if (!vha->flags.online) {
+ ql_log(ql_log_warn, vha, 0x7019, "Host is not online.\n");
+ return -EIO;
+ }
+
+ elreq.req_sg_cnt = dma_map_sg(&ha->pdev->dev,
+ bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt,
+ DMA_TO_DEVICE);
+
+ if (!elreq.req_sg_cnt) {
+ ql_log(ql_log_warn, vha, 0x701a,
+ "dma_map_sg returned %d for request.\n", elreq.req_sg_cnt);
+ return -ENOMEM;
+ }
+
+ elreq.rsp_sg_cnt = dma_map_sg(&ha->pdev->dev,
+ bsg_job->reply_payload.sg_list, bsg_job->reply_payload.sg_cnt,
+ DMA_FROM_DEVICE);
+
+ if (!elreq.rsp_sg_cnt) {
+ ql_log(ql_log_warn, vha, 0x701b,
+ "dma_map_sg returned %d for reply.\n", elreq.rsp_sg_cnt);
+ rval = -ENOMEM;
+ goto done_unmap_req_sg;
+ }
+
+ if ((elreq.req_sg_cnt != bsg_job->request_payload.sg_cnt) ||
+ (elreq.rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) {
+ ql_log(ql_log_warn, vha, 0x701c,
+ "dma mapping resulted in different sg counts, "
+ "request_sg_cnt: %x dma_request_sg_cnt: %x "
+ "reply_sg_cnt: %x dma_reply_sg_cnt: %x.\n",
+ bsg_job->request_payload.sg_cnt, elreq.req_sg_cnt,
+ bsg_job->reply_payload.sg_cnt, elreq.rsp_sg_cnt);
+ rval = -EAGAIN;
+ goto done_unmap_sg;
+ }
+ req_data_len = rsp_data_len = bsg_job->request_payload.payload_len;
+ req_data = dma_alloc_coherent(&ha->pdev->dev, req_data_len,
+ &req_data_dma, GFP_KERNEL);
+ if (!req_data) {
+ ql_log(ql_log_warn, vha, 0x701d,
+ "dma alloc failed for req_data.\n");
+ rval = -ENOMEM;
+ goto done_unmap_sg;
+ }
+
+ rsp_data = dma_alloc_coherent(&ha->pdev->dev, rsp_data_len,
+ &rsp_data_dma, GFP_KERNEL);
+ if (!rsp_data) {
+ ql_log(ql_log_warn, vha, 0x7004,
+ "dma alloc failed for rsp_data.\n");
+ rval = -ENOMEM;
+ goto done_free_dma_req;
+ }
+
+ /* Copy the request buffer in req_data now */
+ sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, req_data, req_data_len);
+
+ elreq.send_dma = req_data_dma;
+ elreq.rcv_dma = rsp_data_dma;
+ elreq.transfer_size = req_data_len;
+
+ elreq.options = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1];
+
+ if ((ha->current_topology == ISP_CFG_F ||
+ (atomic_read(&vha->loop_state) == LOOP_DOWN) ||
+ ((IS_QLA81XX(ha) || IS_QLA83XX(ha)) &&
+ le32_to_cpu(*(uint32_t *)req_data) == ELS_OPCODE_BYTE
+ && req_data_len == MAX_ELS_FRAME_PAYLOAD)) &&
+ elreq.options == EXTERNAL_LOOPBACK) {
+ type = "FC_BSG_HST_VENDOR_ECHO_DIAG";
+ ql_dbg(ql_dbg_user, vha, 0x701e,
+ "BSG request type: %s.\n", type);
+ command_sent = INT_DEF_LB_ECHO_CMD;
+ rval = qla2x00_echo_test(vha, &elreq, response);
+ } else {
+ if (IS_QLA81XX(ha) || IS_QLA8031(ha)) {
+ memset(config, 0, sizeof(config));
+ memset(new_config, 0, sizeof(new_config));
+ if (qla81xx_get_port_config(vha, config)) {
+ ql_log(ql_log_warn, vha, 0x701f,
+ "Get port config failed.\n");
+ bsg_job->reply->result = (DID_ERROR << 16);
+ rval = -EPERM;
+ goto done_free_dma_req;
+ }
+
+ if (elreq.options != EXTERNAL_LOOPBACK) {
+ ql_dbg(ql_dbg_user, vha, 0x7020,
+ "Internal: current port config = %x\n",
+ config[0]);
+ if (qla81xx_set_internal_loopback(vha, config,
+ new_config)) {
+ ql_log(ql_log_warn, vha, 0x7024,
+ "Internal loopback failed.\n");
+ bsg_job->reply->result =
+ (DID_ERROR << 16);
+ rval = -EPERM;
+ goto done_free_dma_req;
+ }
+ } else {
+ /* For external loopback to work
+ * ensure internal loopback is disabled
+ */
+ if (qla81xx_reset_internal_loopback(vha,
+ config, 1)) {
+ bsg_job->reply->result =
+ (DID_ERROR << 16);
+ rval = -EPERM;
+ goto done_free_dma_req;
+ }
+ }
+
+ type = "FC_BSG_HST_VENDOR_LOOPBACK";
+ ql_dbg(ql_dbg_user, vha, 0x7028,
+ "BSG request type: %s.\n", type);
+
+ command_sent = INT_DEF_LB_LOOPBACK_CMD;
+ rval = qla2x00_loopback_test(vha, &elreq, response);
+
+ if (new_config[0]) {
+ /* Revert back to original port config
+ * Also clear internal loopback
+ */
+ qla81xx_reset_internal_loopback(vha,
+ new_config, 0);
+ }
+
+ if (response[0] == MBS_COMMAND_ERROR &&
+ response[1] == MBS_LB_RESET) {
+ ql_log(ql_log_warn, vha, 0x7029,
+ "MBX command error, Aborting ISP.\n");
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
+ qla2x00_wait_for_chip_reset(vha);
+ /* Also reset the MPI */
+ if (qla81xx_restart_mpi_firmware(vha) !=
+ QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x702a,
+ "MPI reset failed.\n");
+ }
+
+ bsg_job->reply->result = (DID_ERROR << 16);
+ rval = -EIO;
+ goto done_free_dma_req;
+ }
+ } else {
+ type = "FC_BSG_HST_VENDOR_LOOPBACK";
+ ql_dbg(ql_dbg_user, vha, 0x702b,
+ "BSG request type: %s.\n", type);
+ command_sent = INT_DEF_LB_LOOPBACK_CMD;
+ rval = qla2x00_loopback_test(vha, &elreq, response);
+ }
+ }
+
+ if (rval) {
+ ql_log(ql_log_warn, vha, 0x702c,
+ "Vendor request %s failed.\n", type);
+
+ fw_sts_ptr = ((uint8_t *)bsg_job->req->sense) +
+ sizeof(struct fc_bsg_reply);
+
+ memcpy(fw_sts_ptr, response, sizeof(response));
+ fw_sts_ptr += sizeof(response);
+ *fw_sts_ptr = command_sent;
+ rval = 0;
+ bsg_job->reply->result = (DID_ERROR << 16);
+ } else {
+ ql_dbg(ql_dbg_user, vha, 0x702d,
+ "Vendor request %s completed.\n", type);
+
+ bsg_job->reply_len = sizeof(struct fc_bsg_reply) +
+ sizeof(response) + sizeof(uint8_t);
+ bsg_job->reply->reply_payload_rcv_len =
+ bsg_job->reply_payload.payload_len;
+ fw_sts_ptr = ((uint8_t *)bsg_job->req->sense) +
+ sizeof(struct fc_bsg_reply);
+ memcpy(fw_sts_ptr, response, sizeof(response));
+ fw_sts_ptr += sizeof(response);
+ *fw_sts_ptr = command_sent;
+ bsg_job->reply->result = DID_OK;
+ sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, rsp_data,
+ rsp_data_len);
+ }
+ bsg_job->job_done(bsg_job);
+
+ dma_free_coherent(&ha->pdev->dev, rsp_data_len,
+ rsp_data, rsp_data_dma);
+done_free_dma_req:
+ dma_free_coherent(&ha->pdev->dev, req_data_len,
+ req_data, req_data_dma);
+done_unmap_sg:
+ dma_unmap_sg(&ha->pdev->dev,
+ bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+done_unmap_req_sg:
+ dma_unmap_sg(&ha->pdev->dev,
+ bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ return rval;
+}
+
+static int
+qla84xx_reset(struct fc_bsg_job *bsg_job)
+{
+ struct Scsi_Host *host = bsg_job->shost;
+ scsi_qla_host_t *vha = shost_priv(host);
+ struct qla_hw_data *ha = vha->hw;
+ int rval = 0;
+ uint32_t flag;
+
+ if (!IS_QLA84XX(ha)) {
+ ql_dbg(ql_dbg_user, vha, 0x702f, "Not 84xx, exiting.\n");
+ return -EINVAL;
+ }
+
+ flag = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1];
+
+ rval = qla84xx_reset_chip(vha, flag == A84_ISSUE_RESET_DIAG_FW);
+
+ if (rval) {
+ ql_log(ql_log_warn, vha, 0x7030,
+ "Vendor request 84xx reset failed.\n");
+ rval = 0;
+ bsg_job->reply->result = (DID_ERROR << 16);
+
+ } else {
+ ql_dbg(ql_dbg_user, vha, 0x7031,
+ "Vendor request 84xx reset completed.\n");
+ bsg_job->reply->result = DID_OK;
+ }
+
+ bsg_job->job_done(bsg_job);
+ return rval;
+}
+
+static int
+qla84xx_updatefw(struct fc_bsg_job *bsg_job)
+{
+ struct Scsi_Host *host = bsg_job->shost;
+ scsi_qla_host_t *vha = shost_priv(host);
+ struct qla_hw_data *ha = vha->hw;
+ struct verify_chip_entry_84xx *mn = NULL;
+ dma_addr_t mn_dma, fw_dma;
+ void *fw_buf = NULL;
+ int rval = 0;
+ uint32_t sg_cnt;
+ uint32_t data_len;
+ uint16_t options;
+ uint32_t flag;
+ uint32_t fw_ver;
+
+ if (!IS_QLA84XX(ha)) {
+ ql_dbg(ql_dbg_user, vha, 0x7032,
+ "Not 84xx, exiting.\n");
+ return -EINVAL;
+ }
+
+ sg_cnt = dma_map_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ if (!sg_cnt) {
+ ql_log(ql_log_warn, vha, 0x7033,
+ "dma_map_sg returned %d for request.\n", sg_cnt);
+ return -ENOMEM;
+ }
+
+ if (sg_cnt != bsg_job->request_payload.sg_cnt) {
+ ql_log(ql_log_warn, vha, 0x7034,
+ "DMA mapping resulted in different sg counts, "
+ "request_sg_cnt: %x dma_request_sg_cnt: %x.\n",
+ bsg_job->request_payload.sg_cnt, sg_cnt);
+ rval = -EAGAIN;
+ goto done_unmap_sg;
+ }
+
+ data_len = bsg_job->request_payload.payload_len;
+ fw_buf = dma_alloc_coherent(&ha->pdev->dev, data_len,
+ &fw_dma, GFP_KERNEL);
+ if (!fw_buf) {
+ ql_log(ql_log_warn, vha, 0x7035,
+ "DMA alloc failed for fw_buf.\n");
+ rval = -ENOMEM;
+ goto done_unmap_sg;
+ }
+
+ sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, fw_buf, data_len);
+
+ mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma);
+ if (!mn) {
+ ql_log(ql_log_warn, vha, 0x7036,
+ "DMA alloc failed for fw buffer.\n");
+ rval = -ENOMEM;
+ goto done_free_fw_buf;
+ }
+
+ flag = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1];
+ fw_ver = le32_to_cpu(*((uint32_t *)((uint32_t *)fw_buf + 2)));
+
+ memset(mn, 0, sizeof(struct access_chip_84xx));
+ mn->entry_type = VERIFY_CHIP_IOCB_TYPE;
+ mn->entry_count = 1;
+
+ options = VCO_FORCE_UPDATE | VCO_END_OF_DATA;
+ if (flag == A84_ISSUE_UPDATE_DIAGFW_CMD)
+ options |= VCO_DIAG_FW;
+
+ mn->options = cpu_to_le16(options);
+ mn->fw_ver = cpu_to_le32(fw_ver);
+ mn->fw_size = cpu_to_le32(data_len);
+ mn->fw_seq_size = cpu_to_le32(data_len);
+ mn->dseg_address[0] = cpu_to_le32(LSD(fw_dma));
+ mn->dseg_address[1] = cpu_to_le32(MSD(fw_dma));
+ mn->dseg_length = cpu_to_le32(data_len);
+ mn->data_seg_cnt = cpu_to_le16(1);
+
+ rval = qla2x00_issue_iocb_timeout(vha, mn, mn_dma, 0, 120);
+
+ if (rval) {
+ ql_log(ql_log_warn, vha, 0x7037,
+ "Vendor request 84xx updatefw failed.\n");
+
+ rval = 0;
+ bsg_job->reply->result = (DID_ERROR << 16);
+ } else {
+ ql_dbg(ql_dbg_user, vha, 0x7038,
+ "Vendor request 84xx updatefw completed.\n");
+
+ bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+ bsg_job->reply->result = DID_OK;
+ }
+
+ bsg_job->job_done(bsg_job);
+ dma_pool_free(ha->s_dma_pool, mn, mn_dma);
+
+done_free_fw_buf:
+ dma_free_coherent(&ha->pdev->dev, data_len, fw_buf, fw_dma);
+
+done_unmap_sg:
+ dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
+
+ return rval;
+}
+
+static int
+qla84xx_mgmt_cmd(struct fc_bsg_job *bsg_job)
+{
+ struct Scsi_Host *host = bsg_job->shost;
+ scsi_qla_host_t *vha = shost_priv(host);
+ struct qla_hw_data *ha = vha->hw;
+ struct access_chip_84xx *mn = NULL;
+ dma_addr_t mn_dma, mgmt_dma;
+ void *mgmt_b = NULL;
+ int rval = 0;
+ struct qla_bsg_a84_mgmt *ql84_mgmt;
+ uint32_t sg_cnt;
+ uint32_t data_len = 0;
+ uint32_t dma_direction = DMA_NONE;
+
+ if (!IS_QLA84XX(ha)) {
+ ql_log(ql_log_warn, vha, 0x703a,
+ "Not 84xx, exiting.\n");
+ return -EINVAL;
+ }
+
+ ql84_mgmt = (struct qla_bsg_a84_mgmt *)((char *)bsg_job->request +
+ sizeof(struct fc_bsg_request));
+ if (!ql84_mgmt) {
+ ql_log(ql_log_warn, vha, 0x703b,
+ "MGMT header not provided, exiting.\n");
+ return -EINVAL;
+ }
+
+ mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma);
+ if (!mn) {
+ ql_log(ql_log_warn, vha, 0x703c,
+ "DMA alloc failed for fw buffer.\n");
+ return -ENOMEM;
+ }
+
+ memset(mn, 0, sizeof(struct access_chip_84xx));
+ mn->entry_type = ACCESS_CHIP_IOCB_TYPE;
+ mn->entry_count = 1;
+
+ switch (ql84_mgmt->mgmt.cmd) {
+ case QLA84_MGMT_READ_MEM:
+ case QLA84_MGMT_GET_INFO:
+ sg_cnt = dma_map_sg(&ha->pdev->dev,
+ bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+ if (!sg_cnt) {
+ ql_log(ql_log_warn, vha, 0x703d,
+ "dma_map_sg returned %d for reply.\n", sg_cnt);
+ rval = -ENOMEM;
+ goto exit_mgmt;
+ }
+
+ dma_direction = DMA_FROM_DEVICE;
+
+ if (sg_cnt != bsg_job->reply_payload.sg_cnt) {
+ ql_log(ql_log_warn, vha, 0x703e,
+ "DMA mapping resulted in different sg counts, "
+ "reply_sg_cnt: %x dma_reply_sg_cnt: %x.\n",
+ bsg_job->reply_payload.sg_cnt, sg_cnt);
+ rval = -EAGAIN;
+ goto done_unmap_sg;
+ }
+
+ data_len = bsg_job->reply_payload.payload_len;
+
+ mgmt_b = dma_alloc_coherent(&ha->pdev->dev, data_len,
+ &mgmt_dma, GFP_KERNEL);
+ if (!mgmt_b) {
+ ql_log(ql_log_warn, vha, 0x703f,
+ "DMA alloc failed for mgmt_b.\n");
+ rval = -ENOMEM;
+ goto done_unmap_sg;
+ }
+
+ if (ql84_mgmt->mgmt.cmd == QLA84_MGMT_READ_MEM) {
+ mn->options = cpu_to_le16(ACO_DUMP_MEMORY);
+ mn->parameter1 =
+ cpu_to_le32(
+ ql84_mgmt->mgmt.mgmtp.u.mem.start_addr);
+
+ } else if (ql84_mgmt->mgmt.cmd == QLA84_MGMT_GET_INFO) {
+ mn->options = cpu_to_le16(ACO_REQUEST_INFO);
+ mn->parameter1 =
+ cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.info.type);
+
+ mn->parameter2 =
+ cpu_to_le32(
+ ql84_mgmt->mgmt.mgmtp.u.info.context);
+ }
+ break;
+
+ case QLA84_MGMT_WRITE_MEM:
+ sg_cnt = dma_map_sg(&ha->pdev->dev,
+ bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
+
+ if (!sg_cnt) {
+ ql_log(ql_log_warn, vha, 0x7040,
+ "dma_map_sg returned %d.\n", sg_cnt);
+ rval = -ENOMEM;
+ goto exit_mgmt;
+ }
+
+ dma_direction = DMA_TO_DEVICE;
+
+ if (sg_cnt != bsg_job->request_payload.sg_cnt) {
+ ql_log(ql_log_warn, vha, 0x7041,
+ "DMA mapping resulted in different sg counts, "
+ "request_sg_cnt: %x dma_request_sg_cnt: %x.\n",
+ bsg_job->request_payload.sg_cnt, sg_cnt);
+ rval = -EAGAIN;
+ goto done_unmap_sg;
+ }
+
+ data_len = bsg_job->request_payload.payload_len;
+ mgmt_b = dma_alloc_coherent(&ha->pdev->dev, data_len,
+ &mgmt_dma, GFP_KERNEL);
+ if (!mgmt_b) {
+ ql_log(ql_log_warn, vha, 0x7042,
+ "DMA alloc failed for mgmt_b.\n");
+ rval = -ENOMEM;
+ goto done_unmap_sg;
+ }
+
+ sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, mgmt_b, data_len);
+
+ mn->options = cpu_to_le16(ACO_LOAD_MEMORY);
+ mn->parameter1 =
+ cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.mem.start_addr);
+ break;
+
+ case QLA84_MGMT_CHNG_CONFIG:
+ mn->options = cpu_to_le16(ACO_CHANGE_CONFIG_PARAM);
+ mn->parameter1 =
+ cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.config.id);
+
+ mn->parameter2 =
+ cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.config.param0);
+
+ mn->parameter3 =
+ cpu_to_le32(ql84_mgmt->mgmt.mgmtp.u.config.param1);
+ break;
+
+ default:
+ rval = -EIO;
+ goto exit_mgmt;
+ }
+
+ if (ql84_mgmt->mgmt.cmd != QLA84_MGMT_CHNG_CONFIG) {
+ mn->total_byte_cnt = cpu_to_le32(ql84_mgmt->mgmt.len);
+ mn->dseg_count = cpu_to_le16(1);
+ mn->dseg_address[0] = cpu_to_le32(LSD(mgmt_dma));
+ mn->dseg_address[1] = cpu_to_le32(MSD(mgmt_dma));
+ mn->dseg_length = cpu_to_le32(ql84_mgmt->mgmt.len);
+ }
+
+ rval = qla2x00_issue_iocb(vha, mn, mn_dma, 0);
+
+ if (rval) {
+ ql_log(ql_log_warn, vha, 0x7043,
+ "Vendor request 84xx mgmt failed.\n");
+
+ rval = 0;
+ bsg_job->reply->result = (DID_ERROR << 16);
+
+ } else {
+ ql_dbg(ql_dbg_user, vha, 0x7044,
+ "Vendor request 84xx mgmt completed.\n");
+
+ bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+ bsg_job->reply->result = DID_OK;
+
+ if ((ql84_mgmt->mgmt.cmd == QLA84_MGMT_READ_MEM) ||
+ (ql84_mgmt->mgmt.cmd == QLA84_MGMT_GET_INFO)) {
+ bsg_job->reply->reply_payload_rcv_len =
+ bsg_job->reply_payload.payload_len;
+
+ sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, mgmt_b,
+ data_len);
+ }
+ }
+
+ bsg_job->job_done(bsg_job);
+
+done_unmap_sg:
+ if (mgmt_b)
+ dma_free_coherent(&ha->pdev->dev, data_len, mgmt_b, mgmt_dma);
+
+ if (dma_direction == DMA_TO_DEVICE)
+ dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE);
+ else if (dma_direction == DMA_FROM_DEVICE)
+ dma_unmap_sg(&ha->pdev->dev, bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE);
+
+exit_mgmt:
+ dma_pool_free(ha->s_dma_pool, mn, mn_dma);
+
+ return rval;
+}
+
+static int
+qla24xx_iidma(struct fc_bsg_job *bsg_job)
+{
+ struct Scsi_Host *host = bsg_job->shost;
+ scsi_qla_host_t *vha = shost_priv(host);
+ int rval = 0;
+ struct qla_port_param *port_param = NULL;
+ fc_port_t *fcport = NULL;
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+ uint8_t *rsp_ptr = NULL;
+
+ if (!IS_IIDMA_CAPABLE(vha->hw)) {
+ ql_log(ql_log_info, vha, 0x7046, "iiDMA not supported.\n");
+ return -EINVAL;
+ }
+
+ port_param = (struct qla_port_param *)((char *)bsg_job->request +
+ sizeof(struct fc_bsg_request));
+ if (!port_param) {
+ ql_log(ql_log_warn, vha, 0x7047,
+ "port_param header not provided.\n");
+ return -EINVAL;
+ }
+
+ if (port_param->fc_scsi_addr.dest_type != EXT_DEF_TYPE_WWPN) {
+ ql_log(ql_log_warn, vha, 0x7048,
+ "Invalid destination type.\n");
+ return -EINVAL;
+ }
+
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ if (fcport->port_type != FCT_TARGET)
+ continue;
+
+ if (memcmp(port_param->fc_scsi_addr.dest_addr.wwpn,
+ fcport->port_name, sizeof(fcport->port_name)))
+ continue;
+ break;
+ }
+
+ if (!fcport) {
+ ql_log(ql_log_warn, vha, 0x7049,
+ "Failed to find port.\n");
+ return -EINVAL;
+ }
+
+ if (atomic_read(&fcport->state) != FCS_ONLINE) {
+ ql_log(ql_log_warn, vha, 0x704a,
+ "Port is not online.\n");
+ return -EINVAL;
+ }
+
+ if (fcport->flags & FCF_LOGIN_NEEDED) {
+ ql_log(ql_log_warn, vha, 0x704b,
+ "Remote port not logged in flags = 0x%x.\n", fcport->flags);
+ return -EINVAL;
+ }
+
+ if (port_param->mode)
+ rval = qla2x00_set_idma_speed(vha, fcport->loop_id,
+ port_param->speed, mb);
+ else
+ rval = qla2x00_get_idma_speed(vha, fcport->loop_id,
+ &port_param->speed, mb);
+
+ if (rval) {
+ ql_log(ql_log_warn, vha, 0x704c,
+ "iIDMA cmd failed for %02x%02x%02x%02x%02x%02x%02x%02x -- "
+ "%04x %x %04x %04x.\n", fcport->port_name[0],
+ fcport->port_name[1], fcport->port_name[2],
+ fcport->port_name[3], fcport->port_name[4],
+ fcport->port_name[5], fcport->port_name[6],
+ fcport->port_name[7], rval, fcport->fp_speed, mb[0], mb[1]);
+ rval = 0;
+ bsg_job->reply->result = (DID_ERROR << 16);
+
+ } else {
+ if (!port_param->mode) {
+ bsg_job->reply_len = sizeof(struct fc_bsg_reply) +
+ sizeof(struct qla_port_param);
+
+ rsp_ptr = ((uint8_t *)bsg_job->reply) +
+ sizeof(struct fc_bsg_reply);
+
+ memcpy(rsp_ptr, port_param,
+ sizeof(struct qla_port_param));
+ }
+
+ bsg_job->reply->result = DID_OK;
+ }
+
+ bsg_job->job_done(bsg_job);
+ return rval;
+}
+
+static int
+qla2x00_optrom_setup(struct fc_bsg_job *bsg_job, scsi_qla_host_t *vha,
+ uint8_t is_update)
+{
+ uint32_t start = 0;
+ int valid = 0;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (unlikely(pci_channel_offline(ha->pdev)))
+ return -EINVAL;
+
+ start = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1];
+ if (start > ha->optrom_size) {
+ ql_log(ql_log_warn, vha, 0x7055,
+ "start %d > optrom_size %d.\n", start, ha->optrom_size);
+ return -EINVAL;
+ }
+
+ if (ha->optrom_state != QLA_SWAITING) {
+ ql_log(ql_log_info, vha, 0x7056,
+ "optrom_state %d.\n", ha->optrom_state);
+ return -EBUSY;
+ }
+
+ ha->optrom_region_start = start;
+ ql_dbg(ql_dbg_user, vha, 0x7057, "is_update=%d.\n", is_update);
+ if (is_update) {
+ if (ha->optrom_size == OPTROM_SIZE_2300 && start == 0)
+ valid = 1;
+ else if (start == (ha->flt_region_boot * 4) ||
+ start == (ha->flt_region_fw * 4))
+ valid = 1;
+ else if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) ||
+ IS_CNA_CAPABLE(ha) || IS_QLA2031(ha))
+ valid = 1;
+ if (!valid) {
+ ql_log(ql_log_warn, vha, 0x7058,
+ "Invalid start region 0x%x/0x%x.\n", start,
+ bsg_job->request_payload.payload_len);
+ return -EINVAL;
+ }
+
+ ha->optrom_region_size = start +
+ bsg_job->request_payload.payload_len > ha->optrom_size ?
+ ha->optrom_size - start :
+ bsg_job->request_payload.payload_len;
+ ha->optrom_state = QLA_SWRITING;
+ } else {
+ ha->optrom_region_size = start +
+ bsg_job->reply_payload.payload_len > ha->optrom_size ?
+ ha->optrom_size - start :
+ bsg_job->reply_payload.payload_len;
+ ha->optrom_state = QLA_SREADING;
+ }
+
+ ha->optrom_buffer = vmalloc(ha->optrom_region_size);
+ if (!ha->optrom_buffer) {
+ ql_log(ql_log_warn, vha, 0x7059,
+ "Read: Unable to allocate memory for optrom retrieval "
+ "(%x)\n", ha->optrom_region_size);
+
+ ha->optrom_state = QLA_SWAITING;
+ return -ENOMEM;
+ }
+
+ memset(ha->optrom_buffer, 0, ha->optrom_region_size);
+ return 0;
+}
+
+static int
+qla2x00_read_optrom(struct fc_bsg_job *bsg_job)
+{
+ struct Scsi_Host *host = bsg_job->shost;
+ scsi_qla_host_t *vha = shost_priv(host);
+ struct qla_hw_data *ha = vha->hw;
+ int rval = 0;
+
+ if (ha->flags.isp82xx_reset_hdlr_active)
+ return -EBUSY;
+
+ rval = qla2x00_optrom_setup(bsg_job, vha, 0);
+ if (rval)
+ return rval;
+
+ ha->isp_ops->read_optrom(vha, ha->optrom_buffer,
+ ha->optrom_region_start, ha->optrom_region_size);
+
+ sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, ha->optrom_buffer,
+ ha->optrom_region_size);
+
+ bsg_job->reply->reply_payload_rcv_len = ha->optrom_region_size;
+ bsg_job->reply->result = DID_OK;
+ vfree(ha->optrom_buffer);
+ ha->optrom_buffer = NULL;
+ ha->optrom_state = QLA_SWAITING;
+ bsg_job->job_done(bsg_job);
+ return rval;
+}
+
+static int
+qla2x00_update_optrom(struct fc_bsg_job *bsg_job)
+{
+ struct Scsi_Host *host = bsg_job->shost;
+ scsi_qla_host_t *vha = shost_priv(host);
+ struct qla_hw_data *ha = vha->hw;
+ int rval = 0;
+
+ rval = qla2x00_optrom_setup(bsg_job, vha, 1);
+ if (rval)
+ return rval;
+
+ sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, ha->optrom_buffer,
+ ha->optrom_region_size);
+
+ ha->isp_ops->write_optrom(vha, ha->optrom_buffer,
+ ha->optrom_region_start, ha->optrom_region_size);
+
+ bsg_job->reply->result = DID_OK;
+ vfree(ha->optrom_buffer);
+ ha->optrom_buffer = NULL;
+ ha->optrom_state = QLA_SWAITING;
+ bsg_job->job_done(bsg_job);
+ return rval;
+}
+
+static int
+qla2x00_update_fru_versions(struct fc_bsg_job *bsg_job)
+{
+ struct Scsi_Host *host = bsg_job->shost;
+ scsi_qla_host_t *vha = shost_priv(host);
+ struct qla_hw_data *ha = vha->hw;
+ int rval = 0;
+ uint8_t bsg[DMA_POOL_SIZE];
+ struct qla_image_version_list *list = (void *)bsg;
+ struct qla_image_version *image;
+ uint32_t count;
+ dma_addr_t sfp_dma;
+ void *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+ if (!sfp) {
+ bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] =
+ EXT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, list, sizeof(bsg));
+
+ image = list->version;
+ count = list->count;
+ while (count--) {
+ memcpy(sfp, &image->field_info, sizeof(image->field_info));
+ rval = qla2x00_write_sfp(vha, sfp_dma, sfp,
+ image->field_address.device, image->field_address.offset,
+ sizeof(image->field_info), image->field_address.option);
+ if (rval) {
+ bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] =
+ EXT_STATUS_MAILBOX;
+ goto dealloc;
+ }
+ image++;
+ }
+
+ bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = 0;
+
+dealloc:
+ dma_pool_free(ha->s_dma_pool, sfp, sfp_dma);
+
+done:
+ bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+ bsg_job->reply->result = DID_OK << 16;
+ bsg_job->job_done(bsg_job);
+
+ return 0;
+}
+
+static int
+qla2x00_read_fru_status(struct fc_bsg_job *bsg_job)
+{
+ struct Scsi_Host *host = bsg_job->shost;
+ scsi_qla_host_t *vha = shost_priv(host);
+ struct qla_hw_data *ha = vha->hw;
+ int rval = 0;
+ uint8_t bsg[DMA_POOL_SIZE];
+ struct qla_status_reg *sr = (void *)bsg;
+ dma_addr_t sfp_dma;
+ uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+ if (!sfp) {
+ bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] =
+ EXT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, sr, sizeof(*sr));
+
+ rval = qla2x00_read_sfp(vha, sfp_dma, sfp,
+ sr->field_address.device, sr->field_address.offset,
+ sizeof(sr->status_reg), sr->field_address.option);
+ sr->status_reg = *sfp;
+
+ if (rval) {
+ bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] =
+ EXT_STATUS_MAILBOX;
+ goto dealloc;
+ }
+
+ sg_copy_from_buffer(bsg_job->reply_payload.sg_list,
+ bsg_job->reply_payload.sg_cnt, sr, sizeof(*sr));
+
+ bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = 0;
+
+dealloc:
+ dma_pool_free(ha->s_dma_pool, sfp, sfp_dma);
+
+done:
+ bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+ bsg_job->reply->reply_payload_rcv_len = sizeof(*sr);
+ bsg_job->reply->result = DID_OK << 16;
+ bsg_job->job_done(bsg_job);
+
+ return 0;
+}
+
+static int
+qla2x00_write_fru_status(struct fc_bsg_job *bsg_job)
+{
+ struct Scsi_Host *host = bsg_job->shost;
+ scsi_qla_host_t *vha = shost_priv(host);
+ struct qla_hw_data *ha = vha->hw;
+ int rval = 0;
+ uint8_t bsg[DMA_POOL_SIZE];
+ struct qla_status_reg *sr = (void *)bsg;
+ dma_addr_t sfp_dma;
+ uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma);
+ if (!sfp) {
+ bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] =
+ EXT_STATUS_NO_MEMORY;
+ goto done;
+ }
+
+ sg_copy_to_buffer(bsg_job->request_payload.sg_list,
+ bsg_job->request_payload.sg_cnt, sr, sizeof(*sr));
+
+ *sfp = sr->status_reg;
+ rval = qla2x00_write_sfp(vha, sfp_dma, sfp,
+ sr->field_address.device, sr->field_address.offset,
+ sizeof(sr->status_reg), sr->field_address.option);
+
+ if (rval) {
+ bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] =
+ EXT_STATUS_MAILBOX;
+ goto dealloc;
+ }
+
+ bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = 0;
+
+dealloc:
+ dma_pool_free(ha->s_dma_pool, sfp, sfp_dma);
+
+done:
+ bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+ bsg_job->reply->result = DID_OK << 16;
+ bsg_job->job_done(bsg_job);
+
+ return 0;
+}
+
+static int
+qla2x00_process_vendor_specific(struct fc_bsg_job *bsg_job)
+{
+ switch (bsg_job->request->rqst_data.h_vendor.vendor_cmd[0]) {
+ case QL_VND_LOOPBACK:
+ return qla2x00_process_loopback(bsg_job);
+
+ case QL_VND_A84_RESET:
+ return qla84xx_reset(bsg_job);
+
+ case QL_VND_A84_UPDATE_FW:
+ return qla84xx_updatefw(bsg_job);
+
+ case QL_VND_A84_MGMT_CMD:
+ return qla84xx_mgmt_cmd(bsg_job);
+
+ case QL_VND_IIDMA:
+ return qla24xx_iidma(bsg_job);
+
+ case QL_VND_FCP_PRIO_CFG_CMD:
+ return qla24xx_proc_fcp_prio_cfg_cmd(bsg_job);
+
+ case QL_VND_READ_FLASH:
+ return qla2x00_read_optrom(bsg_job);
+
+ case QL_VND_UPDATE_FLASH:
+ return qla2x00_update_optrom(bsg_job);
+
+ case QL_VND_SET_FRU_VERSION:
+ return qla2x00_update_fru_versions(bsg_job);
+
+ case QL_VND_READ_FRU_STATUS:
+ return qla2x00_read_fru_status(bsg_job);
+
+ case QL_VND_WRITE_FRU_STATUS:
+ return qla2x00_write_fru_status(bsg_job);
+
+ default:
+ bsg_job->reply->result = (DID_ERROR << 16);
+ bsg_job->job_done(bsg_job);
+ return -ENOSYS;
+ }
+}
+
+int
+qla24xx_bsg_request(struct fc_bsg_job *bsg_job)
+{
+ int ret = -EINVAL;
+ struct fc_rport *rport;
+ fc_port_t *fcport = NULL;
+ struct Scsi_Host *host;
+ scsi_qla_host_t *vha;
+
+ /* In case no data transferred. */
+ bsg_job->reply->reply_payload_rcv_len = 0;
+
+ if (bsg_job->request->msgcode == FC_BSG_RPT_ELS) {
+ rport = bsg_job->rport;
+ fcport = *(fc_port_t **) rport->dd_data;
+ host = rport_to_shost(rport);
+ vha = shost_priv(host);
+ } else {
+ host = bsg_job->shost;
+ vha = shost_priv(host);
+ }
+
+ if (qla2x00_reset_active(vha)) {
+ ql_dbg(ql_dbg_user, vha, 0x709f,
+ "BSG: ISP abort active/needed -- cmd=%d.\n",
+ bsg_job->request->msgcode);
+ bsg_job->reply->result = (DID_ERROR << 16);
+ bsg_job->job_done(bsg_job);
+ return -EBUSY;
+ }
+
+ ql_dbg(ql_dbg_user, vha, 0x7000,
+ "Entered %s msgcode=0x%x.\n", __func__, bsg_job->request->msgcode);
+
+ switch (bsg_job->request->msgcode) {
+ case FC_BSG_RPT_ELS:
+ case FC_BSG_HST_ELS_NOLOGIN:
+ ret = qla2x00_process_els(bsg_job);
+ break;
+ case FC_BSG_HST_CT:
+ ret = qla2x00_process_ct(bsg_job);
+ break;
+ case FC_BSG_HST_VENDOR:
+ ret = qla2x00_process_vendor_specific(bsg_job);
+ break;
+ case FC_BSG_HST_ADD_RPORT:
+ case FC_BSG_HST_DEL_RPORT:
+ case FC_BSG_RPT_CT:
+ default:
+ ql_log(ql_log_warn, vha, 0x705a, "Unsupported BSG request.\n");
+ bsg_job->reply->result = ret;
+ break;
+ }
+ return ret;
+}
+
+int
+qla24xx_bsg_timeout(struct fc_bsg_job *bsg_job)
+{
+ scsi_qla_host_t *vha = shost_priv(bsg_job->shost);
+ struct qla_hw_data *ha = vha->hw;
+ srb_t *sp;
+ int cnt, que;
+ unsigned long flags;
+ struct req_que *req;
+
+ /* find the bsg job from the active list of commands */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ for (que = 0; que < ha->max_req_queues; que++) {
+ req = ha->req_q_map[que];
+ if (!req)
+ continue;
+
+ for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+ sp = req->outstanding_cmds[cnt];
+ if (sp) {
+ if (((sp->type == SRB_CT_CMD) ||
+ (sp->type == SRB_ELS_CMD_HST))
+ && (sp->u.bsg_job == bsg_job)) {
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ if (ha->isp_ops->abort_command(sp)) {
+ ql_log(ql_log_warn, vha, 0x7089,
+ "mbx abort_command "
+ "failed.\n");
+ bsg_job->req->errors =
+ bsg_job->reply->result = -EIO;
+ } else {
+ ql_dbg(ql_dbg_user, vha, 0x708a,
+ "mbx abort_command "
+ "success.\n");
+ bsg_job->req->errors =
+ bsg_job->reply->result = 0;
+ }
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ goto done;
+ }
+ }
+ }
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ ql_log(ql_log_info, vha, 0x708b, "SRB not found to abort.\n");
+ bsg_job->req->errors = bsg_job->reply->result = -ENXIO;
+ return 0;
+
+done:
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ if (bsg_job->request->msgcode == FC_BSG_HST_CT)
+ kfree(sp->fcport);
+ mempool_free(sp, ha->srb_mempool);
+ return 0;
+}
diff --git a/drivers/scsi/qla2xxx/qla_bsg.h b/drivers/scsi/qla2xxx/qla_bsg.h
new file mode 100644
index 00000000..70caa63a
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_bsg.h
@@ -0,0 +1,186 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2003-2011 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#ifndef __QLA_BSG_H
+#define __QLA_BSG_H
+
+/* BSG Vendor specific commands */
+#define QL_VND_LOOPBACK 0x01
+#define QL_VND_A84_RESET 0x02
+#define QL_VND_A84_UPDATE_FW 0x03
+#define QL_VND_A84_MGMT_CMD 0x04
+#define QL_VND_IIDMA 0x05
+#define QL_VND_FCP_PRIO_CFG_CMD 0x06
+#define QL_VND_READ_FLASH 0x07
+#define QL_VND_UPDATE_FLASH 0x08
+#define QL_VND_SET_FRU_VERSION 0x0B
+#define QL_VND_READ_FRU_STATUS 0x0C
+#define QL_VND_WRITE_FRU_STATUS 0x0D
+
+/* BSG Vendor specific subcode returns */
+#define EXT_STATUS_OK 0
+#define EXT_STATUS_ERR 1
+#define EXT_STATUS_INVALID_PARAM 6
+#define EXT_STATUS_MAILBOX 11
+#define EXT_STATUS_NO_MEMORY 17
+
+/* BSG definations for interpreting CommandSent field */
+#define INT_DEF_LB_LOOPBACK_CMD 0
+#define INT_DEF_LB_ECHO_CMD 1
+
+/* Loopback related definations */
+#define EXTERNAL_LOOPBACK 0xF2
+#define ENABLE_INTERNAL_LOOPBACK 0x02
+#define INTERNAL_LOOPBACK_MASK 0x000E
+#define MAX_ELS_FRAME_PAYLOAD 252
+#define ELS_OPCODE_BYTE 0x10
+
+/* BSG Vendor specific definations */
+#define A84_ISSUE_WRITE_TYPE_CMD 0
+#define A84_ISSUE_READ_TYPE_CMD 1
+#define A84_CLEANUP_CMD 2
+#define A84_ISSUE_RESET_OP_FW 3
+#define A84_ISSUE_RESET_DIAG_FW 4
+#define A84_ISSUE_UPDATE_OPFW_CMD 5
+#define A84_ISSUE_UPDATE_DIAGFW_CMD 6
+
+struct qla84_mgmt_param {
+ union {
+ struct {
+ uint32_t start_addr;
+ } mem; /* for QLA84_MGMT_READ/WRITE_MEM */
+ struct {
+ uint32_t id;
+#define QLA84_MGMT_CONFIG_ID_UIF 1
+#define QLA84_MGMT_CONFIG_ID_FCOE_COS 2
+#define QLA84_MGMT_CONFIG_ID_PAUSE 3
+#define QLA84_MGMT_CONFIG_ID_TIMEOUTS 4
+
+ uint32_t param0;
+ uint32_t param1;
+ } config; /* for QLA84_MGMT_CHNG_CONFIG */
+
+ struct {
+ uint32_t type;
+#define QLA84_MGMT_INFO_CONFIG_LOG_DATA 1 /* Get Config Log Data */
+#define QLA84_MGMT_INFO_LOG_DATA 2 /* Get Log Data */
+#define QLA84_MGMT_INFO_PORT_STAT 3 /* Get Port Statistics */
+#define QLA84_MGMT_INFO_LIF_STAT 4 /* Get LIF Statistics */
+#define QLA84_MGMT_INFO_ASIC_STAT 5 /* Get ASIC Statistics */
+#define QLA84_MGMT_INFO_CONFIG_PARAMS 6 /* Get Config Parameters */
+#define QLA84_MGMT_INFO_PANIC_LOG 7 /* Get Panic Log */
+
+ uint32_t context;
+/*
+* context definitions for QLA84_MGMT_INFO_CONFIG_LOG_DATA
+*/
+#define IC_LOG_DATA_LOG_ID_DEBUG_LOG 0
+#define IC_LOG_DATA_LOG_ID_LEARN_LOG 1
+#define IC_LOG_DATA_LOG_ID_FC_ACL_INGRESS_LOG 2
+#define IC_LOG_DATA_LOG_ID_FC_ACL_EGRESS_LOG 3
+#define IC_LOG_DATA_LOG_ID_ETHERNET_ACL_INGRESS_LOG 4
+#define IC_LOG_DATA_LOG_ID_ETHERNET_ACL_EGRESS_LOG 5
+#define IC_LOG_DATA_LOG_ID_MESSAGE_TRANSMIT_LOG 6
+#define IC_LOG_DATA_LOG_ID_MESSAGE_RECEIVE_LOG 7
+#define IC_LOG_DATA_LOG_ID_LINK_EVENT_LOG 8
+#define IC_LOG_DATA_LOG_ID_DCX_LOG 9
+
+/*
+* context definitions for QLA84_MGMT_INFO_PORT_STAT
+*/
+#define IC_PORT_STATISTICS_PORT_NUMBER_ETHERNET_PORT0 0
+#define IC_PORT_STATISTICS_PORT_NUMBER_ETHERNET_PORT1 1
+#define IC_PORT_STATISTICS_PORT_NUMBER_NSL_PORT0 2
+#define IC_PORT_STATISTICS_PORT_NUMBER_NSL_PORT1 3
+#define IC_PORT_STATISTICS_PORT_NUMBER_FC_PORT0 4
+#define IC_PORT_STATISTICS_PORT_NUMBER_FC_PORT1 5
+
+
+/*
+* context definitions for QLA84_MGMT_INFO_LIF_STAT
+*/
+#define IC_LIF_STATISTICS_LIF_NUMBER_ETHERNET_PORT0 0
+#define IC_LIF_STATISTICS_LIF_NUMBER_ETHERNET_PORT1 1
+#define IC_LIF_STATISTICS_LIF_NUMBER_FC_PORT0 2
+#define IC_LIF_STATISTICS_LIF_NUMBER_FC_PORT1 3
+#define IC_LIF_STATISTICS_LIF_NUMBER_CPU 6
+
+ } info; /* for QLA84_MGMT_GET_INFO */
+ } u;
+};
+
+struct qla84_msg_mgmt {
+ uint16_t cmd;
+#define QLA84_MGMT_READ_MEM 0x00
+#define QLA84_MGMT_WRITE_MEM 0x01
+#define QLA84_MGMT_CHNG_CONFIG 0x02
+#define QLA84_MGMT_GET_INFO 0x03
+ uint16_t rsrvd;
+ struct qla84_mgmt_param mgmtp;/* parameters for cmd */
+ uint32_t len; /* bytes in payload following this struct */
+ uint8_t payload[0]; /* payload for cmd */
+};
+
+struct qla_bsg_a84_mgmt {
+ struct qla84_msg_mgmt mgmt;
+} __attribute__ ((packed));
+
+struct qla_scsi_addr {
+ uint16_t bus;
+ uint16_t target;
+} __attribute__ ((packed));
+
+struct qla_ext_dest_addr {
+ union {
+ uint8_t wwnn[8];
+ uint8_t wwpn[8];
+ uint8_t id[4];
+ struct qla_scsi_addr scsi_addr;
+ } dest_addr;
+ uint16_t dest_type;
+#define EXT_DEF_TYPE_WWPN 2
+ uint16_t lun;
+ uint16_t padding[2];
+} __attribute__ ((packed));
+
+struct qla_port_param {
+ struct qla_ext_dest_addr fc_scsi_addr;
+ uint16_t mode;
+ uint16_t speed;
+} __attribute__ ((packed));
+
+
+/* FRU VPD */
+
+#define MAX_FRU_SIZE 36
+
+struct qla_field_address {
+ uint16_t offset;
+ uint16_t device;
+ uint16_t option;
+} __packed;
+
+struct qla_field_info {
+ uint8_t version[MAX_FRU_SIZE];
+} __packed;
+
+struct qla_image_version {
+ struct qla_field_address field_address;
+ struct qla_field_info field_info;
+} __packed;
+
+struct qla_image_version_list {
+ uint32_t count;
+ struct qla_image_version version[0];
+} __packed;
+
+struct qla_status_reg {
+ struct qla_field_address field_address;
+ uint8_t status_reg;
+ uint8_t reserved[7];
+} __packed;
+
+#endif
diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c
new file mode 100644
index 00000000..62324a1d
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_dbg.c
@@ -0,0 +1,2495 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2003-2011 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+
+/*
+ * Table for showing the current message id in use for particular level
+ * Change this table for addition of log/debug messages.
+ * ----------------------------------------------------------------------
+ * | Level | Last Value Used | Holes |
+ * ----------------------------------------------------------------------
+ * | Module Init and Probe | 0x0120 | 0x4b,0xba,0xfa |
+ * | Mailbox commands | 0x113e | 0x112c-0x112e |
+ * | | | 0x113a |
+ * | Device Discovery | 0x2086 | 0x2020-0x2022 |
+ * | Queue Command and IO tracing | 0x3030 | 0x3006,0x3008 |
+ * | | | 0x302d-0x302e |
+ * | DPC Thread | 0x401c | |
+ * | Async Events | 0x505d | 0x502b-0x502f |
+ * | | | 0x5047,0x5052 |
+ * | Timer Routines | 0x6011 | 0x600e-0x600f |
+ * | User Space Interactions | 0x709f | 0x7018,0x702e, |
+ * | | | 0x7039,0x7045, |
+ * | | | 0x7073-0x7075, |
+ * | | | 0x708c |
+ * | Task Management | 0x803c | 0x8025-0x8026 |
+ * | | | 0x800b,0x8039 |
+ * | AER/EEH | 0x900f | |
+ * | Virtual Port | 0xa007 | |
+ * | ISP82XX Specific | 0xb054 | 0xb053 |
+ * | MultiQ | 0xc00c | |
+ * | Misc | 0xd010 | |
+ * ----------------------------------------------------------------------
+ */
+
+#include "qla_def.h"
+
+#include <linux/delay.h>
+
+static uint32_t ql_dbg_offset = 0x800;
+
+static inline void
+qla2xxx_prep_dump(struct qla_hw_data *ha, struct qla2xxx_fw_dump *fw_dump)
+{
+ fw_dump->fw_major_version = htonl(ha->fw_major_version);
+ fw_dump->fw_minor_version = htonl(ha->fw_minor_version);
+ fw_dump->fw_subminor_version = htonl(ha->fw_subminor_version);
+ fw_dump->fw_attributes = htonl(ha->fw_attributes);
+
+ fw_dump->vendor = htonl(ha->pdev->vendor);
+ fw_dump->device = htonl(ha->pdev->device);
+ fw_dump->subsystem_vendor = htonl(ha->pdev->subsystem_vendor);
+ fw_dump->subsystem_device = htonl(ha->pdev->subsystem_device);
+}
+
+static inline void *
+qla2xxx_copy_queues(struct qla_hw_data *ha, void *ptr)
+{
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
+ /* Request queue. */
+ memcpy(ptr, req->ring, req->length *
+ sizeof(request_t));
+
+ /* Response queue. */
+ ptr += req->length * sizeof(request_t);
+ memcpy(ptr, rsp->ring, rsp->length *
+ sizeof(response_t));
+
+ return ptr + (rsp->length * sizeof(response_t));
+}
+
+static int
+qla24xx_dump_ram(struct qla_hw_data *ha, uint32_t addr, uint32_t *ram,
+ uint32_t ram_dwords, void **nxt)
+{
+ int rval;
+ uint32_t cnt, stat, timer, dwords, idx;
+ uint16_t mb0;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+ dma_addr_t dump_dma = ha->gid_list_dma;
+ uint32_t *dump = (uint32_t *)ha->gid_list;
+
+ rval = QLA_SUCCESS;
+ mb0 = 0;
+
+ WRT_REG_WORD(&reg->mailbox0, MBC_DUMP_RISC_RAM_EXTENDED);
+ clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
+
+ dwords = qla2x00_gid_list_size(ha) / 4;
+ for (cnt = 0; cnt < ram_dwords && rval == QLA_SUCCESS;
+ cnt += dwords, addr += dwords) {
+ if (cnt + dwords > ram_dwords)
+ dwords = ram_dwords - cnt;
+
+ WRT_REG_WORD(&reg->mailbox1, LSW(addr));
+ WRT_REG_WORD(&reg->mailbox8, MSW(addr));
+
+ WRT_REG_WORD(&reg->mailbox2, MSW(dump_dma));
+ WRT_REG_WORD(&reg->mailbox3, LSW(dump_dma));
+ WRT_REG_WORD(&reg->mailbox6, MSW(MSD(dump_dma)));
+ WRT_REG_WORD(&reg->mailbox7, LSW(MSD(dump_dma)));
+
+ WRT_REG_WORD(&reg->mailbox4, MSW(dwords));
+ WRT_REG_WORD(&reg->mailbox5, LSW(dwords));
+ WRT_REG_DWORD(&reg->hccr, HCCRX_SET_HOST_INT);
+
+ for (timer = 6000000; timer; timer--) {
+ /* Check for pending interrupts. */
+ stat = RD_REG_DWORD(&reg->host_status);
+ if (stat & HSRX_RISC_INT) {
+ stat &= 0xff;
+
+ if (stat == 0x1 || stat == 0x2 ||
+ stat == 0x10 || stat == 0x11) {
+ set_bit(MBX_INTERRUPT,
+ &ha->mbx_cmd_flags);
+
+ mb0 = RD_REG_WORD(&reg->mailbox0);
+
+ WRT_REG_DWORD(&reg->hccr,
+ HCCRX_CLR_RISC_INT);
+ RD_REG_DWORD(&reg->hccr);
+ break;
+ }
+
+ /* Clear this intr; it wasn't a mailbox intr */
+ WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_INT);
+ RD_REG_DWORD(&reg->hccr);
+ }
+ udelay(5);
+ }
+
+ if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) {
+ rval = mb0 & MBS_MASK;
+ for (idx = 0; idx < dwords; idx++)
+ ram[cnt + idx] = swab32(dump[idx]);
+ } else {
+ rval = QLA_FUNCTION_FAILED;
+ }
+ }
+
+ *nxt = rval == QLA_SUCCESS ? &ram[cnt]: NULL;
+ return rval;
+}
+
+static int
+qla24xx_dump_memory(struct qla_hw_data *ha, uint32_t *code_ram,
+ uint32_t cram_size, void **nxt)
+{
+ int rval;
+
+ /* Code RAM. */
+ rval = qla24xx_dump_ram(ha, 0x20000, code_ram, cram_size / 4, nxt);
+ if (rval != QLA_SUCCESS)
+ return rval;
+
+ /* External Memory. */
+ return qla24xx_dump_ram(ha, 0x100000, *nxt,
+ ha->fw_memory_size - 0x100000 + 1, nxt);
+}
+
+static uint32_t *
+qla24xx_read_window(struct device_reg_24xx __iomem *reg, uint32_t iobase,
+ uint32_t count, uint32_t *buf)
+{
+ uint32_t __iomem *dmp_reg;
+
+ WRT_REG_DWORD(&reg->iobase_addr, iobase);
+ dmp_reg = &reg->iobase_window;
+ while (count--)
+ *buf++ = htonl(RD_REG_DWORD(dmp_reg++));
+
+ return buf;
+}
+
+static inline int
+qla24xx_pause_risc(struct device_reg_24xx __iomem *reg)
+{
+ int rval = QLA_SUCCESS;
+ uint32_t cnt;
+
+ WRT_REG_DWORD(&reg->hccr, HCCRX_SET_RISC_PAUSE);
+ for (cnt = 30000;
+ ((RD_REG_DWORD(&reg->host_status) & HSRX_RISC_PAUSED) == 0) &&
+ rval == QLA_SUCCESS; cnt--) {
+ if (cnt)
+ udelay(100);
+ else
+ rval = QLA_FUNCTION_TIMEOUT;
+ }
+
+ return rval;
+}
+
+static int
+qla24xx_soft_reset(struct qla_hw_data *ha)
+{
+ int rval = QLA_SUCCESS;
+ uint32_t cnt;
+ uint16_t mb0, wd;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+ /* Reset RISC. */
+ WRT_REG_DWORD(&reg->ctrl_status, CSRX_DMA_SHUTDOWN|MWB_4096_BYTES);
+ for (cnt = 0; cnt < 30000; cnt++) {
+ if ((RD_REG_DWORD(&reg->ctrl_status) & CSRX_DMA_ACTIVE) == 0)
+ break;
+
+ udelay(10);
+ }
+
+ WRT_REG_DWORD(&reg->ctrl_status,
+ CSRX_ISP_SOFT_RESET|CSRX_DMA_SHUTDOWN|MWB_4096_BYTES);
+ pci_read_config_word(ha->pdev, PCI_COMMAND, &wd);
+
+ udelay(100);
+ /* Wait for firmware to complete NVRAM accesses. */
+ mb0 = (uint32_t) RD_REG_WORD(&reg->mailbox0);
+ for (cnt = 10000 ; cnt && mb0; cnt--) {
+ udelay(5);
+ mb0 = (uint32_t) RD_REG_WORD(&reg->mailbox0);
+ barrier();
+ }
+
+ /* Wait for soft-reset to complete. */
+ for (cnt = 0; cnt < 30000; cnt++) {
+ if ((RD_REG_DWORD(&reg->ctrl_status) &
+ CSRX_ISP_SOFT_RESET) == 0)
+ break;
+
+ udelay(10);
+ }
+ WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_RESET);
+ RD_REG_DWORD(&reg->hccr); /* PCI Posting. */
+
+ for (cnt = 30000; RD_REG_WORD(&reg->mailbox0) != 0 &&
+ rval == QLA_SUCCESS; cnt--) {
+ if (cnt)
+ udelay(100);
+ else
+ rval = QLA_FUNCTION_TIMEOUT;
+ }
+
+ return rval;
+}
+
+static int
+qla2xxx_dump_ram(struct qla_hw_data *ha, uint32_t addr, uint16_t *ram,
+ uint32_t ram_words, void **nxt)
+{
+ int rval;
+ uint32_t cnt, stat, timer, words, idx;
+ uint16_t mb0;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ dma_addr_t dump_dma = ha->gid_list_dma;
+ uint16_t *dump = (uint16_t *)ha->gid_list;
+
+ rval = QLA_SUCCESS;
+ mb0 = 0;
+
+ WRT_MAILBOX_REG(ha, reg, 0, MBC_DUMP_RISC_RAM_EXTENDED);
+ clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
+
+ words = qla2x00_gid_list_size(ha) / 2;
+ for (cnt = 0; cnt < ram_words && rval == QLA_SUCCESS;
+ cnt += words, addr += words) {
+ if (cnt + words > ram_words)
+ words = ram_words - cnt;
+
+ WRT_MAILBOX_REG(ha, reg, 1, LSW(addr));
+ WRT_MAILBOX_REG(ha, reg, 8, MSW(addr));
+
+ WRT_MAILBOX_REG(ha, reg, 2, MSW(dump_dma));
+ WRT_MAILBOX_REG(ha, reg, 3, LSW(dump_dma));
+ WRT_MAILBOX_REG(ha, reg, 6, MSW(MSD(dump_dma)));
+ WRT_MAILBOX_REG(ha, reg, 7, LSW(MSD(dump_dma)));
+
+ WRT_MAILBOX_REG(ha, reg, 4, words);
+ WRT_REG_WORD(&reg->hccr, HCCR_SET_HOST_INT);
+
+ for (timer = 6000000; timer; timer--) {
+ /* Check for pending interrupts. */
+ stat = RD_REG_DWORD(&reg->u.isp2300.host_status);
+ if (stat & HSR_RISC_INT) {
+ stat &= 0xff;
+
+ if (stat == 0x1 || stat == 0x2) {
+ set_bit(MBX_INTERRUPT,
+ &ha->mbx_cmd_flags);
+
+ mb0 = RD_MAILBOX_REG(ha, reg, 0);
+
+ /* Release mailbox registers. */
+ WRT_REG_WORD(&reg->semaphore, 0);
+ WRT_REG_WORD(&reg->hccr,
+ HCCR_CLR_RISC_INT);
+ RD_REG_WORD(&reg->hccr);
+ break;
+ } else if (stat == 0x10 || stat == 0x11) {
+ set_bit(MBX_INTERRUPT,
+ &ha->mbx_cmd_flags);
+
+ mb0 = RD_MAILBOX_REG(ha, reg, 0);
+
+ WRT_REG_WORD(&reg->hccr,
+ HCCR_CLR_RISC_INT);
+ RD_REG_WORD(&reg->hccr);
+ break;
+ }
+
+ /* clear this intr; it wasn't a mailbox intr */
+ WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
+ RD_REG_WORD(&reg->hccr);
+ }
+ udelay(5);
+ }
+
+ if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) {
+ rval = mb0 & MBS_MASK;
+ for (idx = 0; idx < words; idx++)
+ ram[cnt + idx] = swab16(dump[idx]);
+ } else {
+ rval = QLA_FUNCTION_FAILED;
+ }
+ }
+
+ *nxt = rval == QLA_SUCCESS ? &ram[cnt]: NULL;
+ return rval;
+}
+
+static inline void
+qla2xxx_read_window(struct device_reg_2xxx __iomem *reg, uint32_t count,
+ uint16_t *buf)
+{
+ uint16_t __iomem *dmp_reg = &reg->u.isp2300.fb_cmd;
+
+ while (count--)
+ *buf++ = htons(RD_REG_WORD(dmp_reg++));
+}
+
+static inline void *
+qla24xx_copy_eft(struct qla_hw_data *ha, void *ptr)
+{
+ if (!ha->eft)
+ return ptr;
+
+ memcpy(ptr, ha->eft, ntohl(ha->fw_dump->eft_size));
+ return ptr + ntohl(ha->fw_dump->eft_size);
+}
+
+static inline void *
+qla25xx_copy_fce(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain)
+{
+ uint32_t cnt;
+ uint32_t *iter_reg;
+ struct qla2xxx_fce_chain *fcec = ptr;
+
+ if (!ha->fce)
+ return ptr;
+
+ *last_chain = &fcec->type;
+ fcec->type = __constant_htonl(DUMP_CHAIN_FCE);
+ fcec->chain_size = htonl(sizeof(struct qla2xxx_fce_chain) +
+ fce_calc_size(ha->fce_bufs));
+ fcec->size = htonl(fce_calc_size(ha->fce_bufs));
+ fcec->addr_l = htonl(LSD(ha->fce_dma));
+ fcec->addr_h = htonl(MSD(ha->fce_dma));
+
+ iter_reg = fcec->eregs;
+ for (cnt = 0; cnt < 8; cnt++)
+ *iter_reg++ = htonl(ha->fce_mb[cnt]);
+
+ memcpy(iter_reg, ha->fce, ntohl(fcec->size));
+
+ return (char *)iter_reg + ntohl(fcec->size);
+}
+
+static inline void *
+qla25xx_copy_mqueues(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain)
+{
+ struct qla2xxx_mqueue_chain *q;
+ struct qla2xxx_mqueue_header *qh;
+ struct req_que *req;
+ struct rsp_que *rsp;
+ int que;
+
+ if (!ha->mqenable)
+ return ptr;
+
+ /* Request queues */
+ for (que = 1; que < ha->max_req_queues; que++) {
+ req = ha->req_q_map[que];
+ if (!req)
+ break;
+
+ /* Add chain. */
+ q = ptr;
+ *last_chain = &q->type;
+ q->type = __constant_htonl(DUMP_CHAIN_QUEUE);
+ q->chain_size = htonl(
+ sizeof(struct qla2xxx_mqueue_chain) +
+ sizeof(struct qla2xxx_mqueue_header) +
+ (req->length * sizeof(request_t)));
+ ptr += sizeof(struct qla2xxx_mqueue_chain);
+
+ /* Add header. */
+ qh = ptr;
+ qh->queue = __constant_htonl(TYPE_REQUEST_QUEUE);
+ qh->number = htonl(que);
+ qh->size = htonl(req->length * sizeof(request_t));
+ ptr += sizeof(struct qla2xxx_mqueue_header);
+
+ /* Add data. */
+ memcpy(ptr, req->ring, req->length * sizeof(request_t));
+ ptr += req->length * sizeof(request_t);
+ }
+
+ /* Response queues */
+ for (que = 1; que < ha->max_rsp_queues; que++) {
+ rsp = ha->rsp_q_map[que];
+ if (!rsp)
+ break;
+
+ /* Add chain. */
+ q = ptr;
+ *last_chain = &q->type;
+ q->type = __constant_htonl(DUMP_CHAIN_QUEUE);
+ q->chain_size = htonl(
+ sizeof(struct qla2xxx_mqueue_chain) +
+ sizeof(struct qla2xxx_mqueue_header) +
+ (rsp->length * sizeof(response_t)));
+ ptr += sizeof(struct qla2xxx_mqueue_chain);
+
+ /* Add header. */
+ qh = ptr;
+ qh->queue = __constant_htonl(TYPE_RESPONSE_QUEUE);
+ qh->number = htonl(que);
+ qh->size = htonl(rsp->length * sizeof(response_t));
+ ptr += sizeof(struct qla2xxx_mqueue_header);
+
+ /* Add data. */
+ memcpy(ptr, rsp->ring, rsp->length * sizeof(response_t));
+ ptr += rsp->length * sizeof(response_t);
+ }
+
+ return ptr;
+}
+
+static inline void *
+qla25xx_copy_mq(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain)
+{
+ uint32_t cnt, que_idx;
+ uint8_t que_cnt;
+ struct qla2xxx_mq_chain *mq = ptr;
+ struct device_reg_25xxmq __iomem *reg;
+
+ if (!ha->mqenable || IS_QLA83XX(ha))
+ return ptr;
+
+ mq = ptr;
+ *last_chain = &mq->type;
+ mq->type = __constant_htonl(DUMP_CHAIN_MQ);
+ mq->chain_size = __constant_htonl(sizeof(struct qla2xxx_mq_chain));
+
+ que_cnt = ha->max_req_queues > ha->max_rsp_queues ?
+ ha->max_req_queues : ha->max_rsp_queues;
+ mq->count = htonl(que_cnt);
+ for (cnt = 0; cnt < que_cnt; cnt++) {
+ reg = (struct device_reg_25xxmq *) ((void *)
+ ha->mqiobase + cnt * QLA_QUE_PAGE);
+ que_idx = cnt * 4;
+ mq->qregs[que_idx] = htonl(RD_REG_DWORD(&reg->req_q_in));
+ mq->qregs[que_idx+1] = htonl(RD_REG_DWORD(&reg->req_q_out));
+ mq->qregs[que_idx+2] = htonl(RD_REG_DWORD(&reg->rsp_q_in));
+ mq->qregs[que_idx+3] = htonl(RD_REG_DWORD(&reg->rsp_q_out));
+ }
+
+ return ptr + sizeof(struct qla2xxx_mq_chain);
+}
+
+void
+qla2xxx_dump_post_process(scsi_qla_host_t *vha, int rval)
+{
+ struct qla_hw_data *ha = vha->hw;
+
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0xd000,
+ "Failed to dump firmware (%x).\n", rval);
+ ha->fw_dumped = 0;
+ } else {
+ ql_log(ql_log_info, vha, 0xd001,
+ "Firmware dump saved to temp buffer (%ld/%p).\n",
+ vha->host_no, ha->fw_dump);
+ ha->fw_dumped = 1;
+ qla2x00_post_uevent_work(vha, QLA_UEVENT_CODE_FW_DUMP);
+ }
+}
+
+/**
+ * qla2300_fw_dump() - Dumps binary data from the 2300 firmware.
+ * @ha: HA context
+ * @hardware_locked: Called with the hardware_lock
+ */
+void
+qla2300_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
+{
+ int rval;
+ uint32_t cnt;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ uint16_t __iomem *dmp_reg;
+ unsigned long flags;
+ struct qla2300_fw_dump *fw;
+ void *nxt;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+
+ flags = 0;
+
+ if (!hardware_locked)
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ if (!ha->fw_dump) {
+ ql_log(ql_log_warn, vha, 0xd002,
+ "No buffer available for dump.\n");
+ goto qla2300_fw_dump_failed;
+ }
+
+ if (ha->fw_dumped) {
+ ql_log(ql_log_warn, vha, 0xd003,
+ "Firmware has been previously dumped (%p) "
+ "-- ignoring request.\n",
+ ha->fw_dump);
+ goto qla2300_fw_dump_failed;
+ }
+ fw = &ha->fw_dump->isp.isp23;
+ qla2xxx_prep_dump(ha, ha->fw_dump);
+
+ rval = QLA_SUCCESS;
+ fw->hccr = htons(RD_REG_WORD(&reg->hccr));
+
+ /* Pause RISC. */
+ WRT_REG_WORD(&reg->hccr, HCCR_PAUSE_RISC);
+ if (IS_QLA2300(ha)) {
+ for (cnt = 30000;
+ (RD_REG_WORD(&reg->hccr) & HCCR_RISC_PAUSE) == 0 &&
+ rval == QLA_SUCCESS; cnt--) {
+ if (cnt)
+ udelay(100);
+ else
+ rval = QLA_FUNCTION_TIMEOUT;
+ }
+ } else {
+ RD_REG_WORD(&reg->hccr); /* PCI Posting. */
+ udelay(10);
+ }
+
+ if (rval == QLA_SUCCESS) {
+ dmp_reg = &reg->flash_address;
+ for (cnt = 0; cnt < sizeof(fw->pbiu_reg) / 2; cnt++)
+ fw->pbiu_reg[cnt] = htons(RD_REG_WORD(dmp_reg++));
+
+ dmp_reg = &reg->u.isp2300.req_q_in;
+ for (cnt = 0; cnt < sizeof(fw->risc_host_reg) / 2; cnt++)
+ fw->risc_host_reg[cnt] = htons(RD_REG_WORD(dmp_reg++));
+
+ dmp_reg = &reg->u.isp2300.mailbox0;
+ for (cnt = 0; cnt < sizeof(fw->mailbox_reg) / 2; cnt++)
+ fw->mailbox_reg[cnt] = htons(RD_REG_WORD(dmp_reg++));
+
+ WRT_REG_WORD(&reg->ctrl_status, 0x40);
+ qla2xxx_read_window(reg, 32, fw->resp_dma_reg);
+
+ WRT_REG_WORD(&reg->ctrl_status, 0x50);
+ qla2xxx_read_window(reg, 48, fw->dma_reg);
+
+ WRT_REG_WORD(&reg->ctrl_status, 0x00);
+ dmp_reg = &reg->risc_hw;
+ for (cnt = 0; cnt < sizeof(fw->risc_hdw_reg) / 2; cnt++)
+ fw->risc_hdw_reg[cnt] = htons(RD_REG_WORD(dmp_reg++));
+
+ WRT_REG_WORD(&reg->pcr, 0x2000);
+ qla2xxx_read_window(reg, 16, fw->risc_gp0_reg);
+
+ WRT_REG_WORD(&reg->pcr, 0x2200);
+ qla2xxx_read_window(reg, 16, fw->risc_gp1_reg);
+
+ WRT_REG_WORD(&reg->pcr, 0x2400);
+ qla2xxx_read_window(reg, 16, fw->risc_gp2_reg);
+
+ WRT_REG_WORD(&reg->pcr, 0x2600);
+ qla2xxx_read_window(reg, 16, fw->risc_gp3_reg);
+
+ WRT_REG_WORD(&reg->pcr, 0x2800);
+ qla2xxx_read_window(reg, 16, fw->risc_gp4_reg);
+
+ WRT_REG_WORD(&reg->pcr, 0x2A00);
+ qla2xxx_read_window(reg, 16, fw->risc_gp5_reg);
+
+ WRT_REG_WORD(&reg->pcr, 0x2C00);
+ qla2xxx_read_window(reg, 16, fw->risc_gp6_reg);
+
+ WRT_REG_WORD(&reg->pcr, 0x2E00);
+ qla2xxx_read_window(reg, 16, fw->risc_gp7_reg);
+
+ WRT_REG_WORD(&reg->ctrl_status, 0x10);
+ qla2xxx_read_window(reg, 64, fw->frame_buf_hdw_reg);
+
+ WRT_REG_WORD(&reg->ctrl_status, 0x20);
+ qla2xxx_read_window(reg, 64, fw->fpm_b0_reg);
+
+ WRT_REG_WORD(&reg->ctrl_status, 0x30);
+ qla2xxx_read_window(reg, 64, fw->fpm_b1_reg);
+
+ /* Reset RISC. */
+ WRT_REG_WORD(&reg->ctrl_status, CSR_ISP_SOFT_RESET);
+ for (cnt = 0; cnt < 30000; cnt++) {
+ if ((RD_REG_WORD(&reg->ctrl_status) &
+ CSR_ISP_SOFT_RESET) == 0)
+ break;
+
+ udelay(10);
+ }
+ }
+
+ if (!IS_QLA2300(ha)) {
+ for (cnt = 30000; RD_MAILBOX_REG(ha, reg, 0) != 0 &&
+ rval == QLA_SUCCESS; cnt--) {
+ if (cnt)
+ udelay(100);
+ else
+ rval = QLA_FUNCTION_TIMEOUT;
+ }
+ }
+
+ /* Get RISC SRAM. */
+ if (rval == QLA_SUCCESS)
+ rval = qla2xxx_dump_ram(ha, 0x800, fw->risc_ram,
+ sizeof(fw->risc_ram) / 2, &nxt);
+
+ /* Get stack SRAM. */
+ if (rval == QLA_SUCCESS)
+ rval = qla2xxx_dump_ram(ha, 0x10000, fw->stack_ram,
+ sizeof(fw->stack_ram) / 2, &nxt);
+
+ /* Get data SRAM. */
+ if (rval == QLA_SUCCESS)
+ rval = qla2xxx_dump_ram(ha, 0x11000, fw->data_ram,
+ ha->fw_memory_size - 0x11000 + 1, &nxt);
+
+ if (rval == QLA_SUCCESS)
+ qla2xxx_copy_queues(ha, nxt);
+
+ qla2xxx_dump_post_process(base_vha, rval);
+
+qla2300_fw_dump_failed:
+ if (!hardware_locked)
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+/**
+ * qla2100_fw_dump() - Dumps binary data from the 2100/2200 firmware.
+ * @ha: HA context
+ * @hardware_locked: Called with the hardware_lock
+ */
+void
+qla2100_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
+{
+ int rval;
+ uint32_t cnt, timer;
+ uint16_t risc_address;
+ uint16_t mb0, mb2;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ uint16_t __iomem *dmp_reg;
+ unsigned long flags;
+ struct qla2100_fw_dump *fw;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+
+ risc_address = 0;
+ mb0 = mb2 = 0;
+ flags = 0;
+
+ if (!hardware_locked)
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ if (!ha->fw_dump) {
+ ql_log(ql_log_warn, vha, 0xd004,
+ "No buffer available for dump.\n");
+ goto qla2100_fw_dump_failed;
+ }
+
+ if (ha->fw_dumped) {
+ ql_log(ql_log_warn, vha, 0xd005,
+ "Firmware has been previously dumped (%p) "
+ "-- ignoring request.\n",
+ ha->fw_dump);
+ goto qla2100_fw_dump_failed;
+ }
+ fw = &ha->fw_dump->isp.isp21;
+ qla2xxx_prep_dump(ha, ha->fw_dump);
+
+ rval = QLA_SUCCESS;
+ fw->hccr = htons(RD_REG_WORD(&reg->hccr));
+
+ /* Pause RISC. */
+ WRT_REG_WORD(&reg->hccr, HCCR_PAUSE_RISC);
+ for (cnt = 30000; (RD_REG_WORD(&reg->hccr) & HCCR_RISC_PAUSE) == 0 &&
+ rval == QLA_SUCCESS; cnt--) {
+ if (cnt)
+ udelay(100);
+ else
+ rval = QLA_FUNCTION_TIMEOUT;
+ }
+ if (rval == QLA_SUCCESS) {
+ dmp_reg = &reg->flash_address;
+ for (cnt = 0; cnt < sizeof(fw->pbiu_reg) / 2; cnt++)
+ fw->pbiu_reg[cnt] = htons(RD_REG_WORD(dmp_reg++));
+
+ dmp_reg = &reg->u.isp2100.mailbox0;
+ for (cnt = 0; cnt < ha->mbx_count; cnt++) {
+ if (cnt == 8)
+ dmp_reg = &reg->u_end.isp2200.mailbox8;
+
+ fw->mailbox_reg[cnt] = htons(RD_REG_WORD(dmp_reg++));
+ }
+
+ dmp_reg = &reg->u.isp2100.unused_2[0];
+ for (cnt = 0; cnt < sizeof(fw->dma_reg) / 2; cnt++)
+ fw->dma_reg[cnt] = htons(RD_REG_WORD(dmp_reg++));
+
+ WRT_REG_WORD(&reg->ctrl_status, 0x00);
+ dmp_reg = &reg->risc_hw;
+ for (cnt = 0; cnt < sizeof(fw->risc_hdw_reg) / 2; cnt++)
+ fw->risc_hdw_reg[cnt] = htons(RD_REG_WORD(dmp_reg++));
+
+ WRT_REG_WORD(&reg->pcr, 0x2000);
+ qla2xxx_read_window(reg, 16, fw->risc_gp0_reg);
+
+ WRT_REG_WORD(&reg->pcr, 0x2100);
+ qla2xxx_read_window(reg, 16, fw->risc_gp1_reg);
+
+ WRT_REG_WORD(&reg->pcr, 0x2200);
+ qla2xxx_read_window(reg, 16, fw->risc_gp2_reg);
+
+ WRT_REG_WORD(&reg->pcr, 0x2300);
+ qla2xxx_read_window(reg, 16, fw->risc_gp3_reg);
+
+ WRT_REG_WORD(&reg->pcr, 0x2400);
+ qla2xxx_read_window(reg, 16, fw->risc_gp4_reg);
+
+ WRT_REG_WORD(&reg->pcr, 0x2500);
+ qla2xxx_read_window(reg, 16, fw->risc_gp5_reg);
+
+ WRT_REG_WORD(&reg->pcr, 0x2600);
+ qla2xxx_read_window(reg, 16, fw->risc_gp6_reg);
+
+ WRT_REG_WORD(&reg->pcr, 0x2700);
+ qla2xxx_read_window(reg, 16, fw->risc_gp7_reg);
+
+ WRT_REG_WORD(&reg->ctrl_status, 0x10);
+ qla2xxx_read_window(reg, 16, fw->frame_buf_hdw_reg);
+
+ WRT_REG_WORD(&reg->ctrl_status, 0x20);
+ qla2xxx_read_window(reg, 64, fw->fpm_b0_reg);
+
+ WRT_REG_WORD(&reg->ctrl_status, 0x30);
+ qla2xxx_read_window(reg, 64, fw->fpm_b1_reg);
+
+ /* Reset the ISP. */
+ WRT_REG_WORD(&reg->ctrl_status, CSR_ISP_SOFT_RESET);
+ }
+
+ for (cnt = 30000; RD_MAILBOX_REG(ha, reg, 0) != 0 &&
+ rval == QLA_SUCCESS; cnt--) {
+ if (cnt)
+ udelay(100);
+ else
+ rval = QLA_FUNCTION_TIMEOUT;
+ }
+
+ /* Pause RISC. */
+ if (rval == QLA_SUCCESS && (IS_QLA2200(ha) || (IS_QLA2100(ha) &&
+ (RD_REG_WORD(&reg->mctr) & (BIT_1 | BIT_0)) != 0))) {
+
+ WRT_REG_WORD(&reg->hccr, HCCR_PAUSE_RISC);
+ for (cnt = 30000;
+ (RD_REG_WORD(&reg->hccr) & HCCR_RISC_PAUSE) == 0 &&
+ rval == QLA_SUCCESS; cnt--) {
+ if (cnt)
+ udelay(100);
+ else
+ rval = QLA_FUNCTION_TIMEOUT;
+ }
+ if (rval == QLA_SUCCESS) {
+ /* Set memory configuration and timing. */
+ if (IS_QLA2100(ha))
+ WRT_REG_WORD(&reg->mctr, 0xf1);
+ else
+ WRT_REG_WORD(&reg->mctr, 0xf2);
+ RD_REG_WORD(&reg->mctr); /* PCI Posting. */
+
+ /* Release RISC. */
+ WRT_REG_WORD(&reg->hccr, HCCR_RELEASE_RISC);
+ }
+ }
+
+ if (rval == QLA_SUCCESS) {
+ /* Get RISC SRAM. */
+ risc_address = 0x1000;
+ WRT_MAILBOX_REG(ha, reg, 0, MBC_READ_RAM_WORD);
+ clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
+ }
+ for (cnt = 0; cnt < sizeof(fw->risc_ram) / 2 && rval == QLA_SUCCESS;
+ cnt++, risc_address++) {
+ WRT_MAILBOX_REG(ha, reg, 1, risc_address);
+ WRT_REG_WORD(&reg->hccr, HCCR_SET_HOST_INT);
+
+ for (timer = 6000000; timer != 0; timer--) {
+ /* Check for pending interrupts. */
+ if (RD_REG_WORD(&reg->istatus) & ISR_RISC_INT) {
+ if (RD_REG_WORD(&reg->semaphore) & BIT_0) {
+ set_bit(MBX_INTERRUPT,
+ &ha->mbx_cmd_flags);
+
+ mb0 = RD_MAILBOX_REG(ha, reg, 0);
+ mb2 = RD_MAILBOX_REG(ha, reg, 2);
+
+ WRT_REG_WORD(&reg->semaphore, 0);
+ WRT_REG_WORD(&reg->hccr,
+ HCCR_CLR_RISC_INT);
+ RD_REG_WORD(&reg->hccr);
+ break;
+ }
+ WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
+ RD_REG_WORD(&reg->hccr);
+ }
+ udelay(5);
+ }
+
+ if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) {
+ rval = mb0 & MBS_MASK;
+ fw->risc_ram[cnt] = htons(mb2);
+ } else {
+ rval = QLA_FUNCTION_FAILED;
+ }
+ }
+
+ if (rval == QLA_SUCCESS)
+ qla2xxx_copy_queues(ha, &fw->risc_ram[cnt]);
+
+ qla2xxx_dump_post_process(base_vha, rval);
+
+qla2100_fw_dump_failed:
+ if (!hardware_locked)
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+void
+qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
+{
+ int rval;
+ uint32_t cnt;
+ uint32_t risc_address;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+ uint32_t __iomem *dmp_reg;
+ uint32_t *iter_reg;
+ uint16_t __iomem *mbx_reg;
+ unsigned long flags;
+ struct qla24xx_fw_dump *fw;
+ uint32_t ext_mem_cnt;
+ void *nxt;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+
+ if (IS_QLA82XX(ha))
+ return;
+
+ risc_address = ext_mem_cnt = 0;
+ flags = 0;
+
+ if (!hardware_locked)
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ if (!ha->fw_dump) {
+ ql_log(ql_log_warn, vha, 0xd006,
+ "No buffer available for dump.\n");
+ goto qla24xx_fw_dump_failed;
+ }
+
+ if (ha->fw_dumped) {
+ ql_log(ql_log_warn, vha, 0xd007,
+ "Firmware has been previously dumped (%p) "
+ "-- ignoring request.\n",
+ ha->fw_dump);
+ goto qla24xx_fw_dump_failed;
+ }
+ fw = &ha->fw_dump->isp.isp24;
+ qla2xxx_prep_dump(ha, ha->fw_dump);
+
+ fw->host_status = htonl(RD_REG_DWORD(&reg->host_status));
+
+ /* Pause RISC. */
+ rval = qla24xx_pause_risc(reg);
+ if (rval != QLA_SUCCESS)
+ goto qla24xx_fw_dump_failed_0;
+
+ /* Host interface registers. */
+ dmp_reg = &reg->flash_addr;
+ for (cnt = 0; cnt < sizeof(fw->host_reg) / 4; cnt++)
+ fw->host_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++));
+
+ /* Disable interrupts. */
+ WRT_REG_DWORD(&reg->ictrl, 0);
+ RD_REG_DWORD(&reg->ictrl);
+
+ /* Shadow registers. */
+ WRT_REG_DWORD(&reg->iobase_addr, 0x0F70);
+ RD_REG_DWORD(&reg->iobase_addr);
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0000000);
+ fw->shadow_reg[0] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0100000);
+ fw->shadow_reg[1] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0200000);
+ fw->shadow_reg[2] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0300000);
+ fw->shadow_reg[3] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0400000);
+ fw->shadow_reg[4] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0500000);
+ fw->shadow_reg[5] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0600000);
+ fw->shadow_reg[6] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ /* Mailbox registers. */
+ mbx_reg = &reg->mailbox0;
+ for (cnt = 0; cnt < sizeof(fw->mailbox_reg) / 2; cnt++)
+ fw->mailbox_reg[cnt] = htons(RD_REG_WORD(mbx_reg++));
+
+ /* Transfer sequence registers. */
+ iter_reg = fw->xseq_gp_reg;
+ iter_reg = qla24xx_read_window(reg, 0xBF00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF60, 16, iter_reg);
+ qla24xx_read_window(reg, 0xBF70, 16, iter_reg);
+
+ qla24xx_read_window(reg, 0xBFE0, 16, fw->xseq_0_reg);
+ qla24xx_read_window(reg, 0xBFF0, 16, fw->xseq_1_reg);
+
+ /* Receive sequence registers. */
+ iter_reg = fw->rseq_gp_reg;
+ iter_reg = qla24xx_read_window(reg, 0xFF00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF60, 16, iter_reg);
+ qla24xx_read_window(reg, 0xFF70, 16, iter_reg);
+
+ qla24xx_read_window(reg, 0xFFD0, 16, fw->rseq_0_reg);
+ qla24xx_read_window(reg, 0xFFE0, 16, fw->rseq_1_reg);
+ qla24xx_read_window(reg, 0xFFF0, 16, fw->rseq_2_reg);
+
+ /* Command DMA registers. */
+ qla24xx_read_window(reg, 0x7100, 16, fw->cmd_dma_reg);
+
+ /* Queues. */
+ iter_reg = fw->req0_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7200, 8, iter_reg);
+ dmp_reg = &reg->iobase_q;
+ for (cnt = 0; cnt < 7; cnt++)
+ *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++));
+
+ iter_reg = fw->resp0_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7300, 8, iter_reg);
+ dmp_reg = &reg->iobase_q;
+ for (cnt = 0; cnt < 7; cnt++)
+ *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++));
+
+ iter_reg = fw->req1_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7400, 8, iter_reg);
+ dmp_reg = &reg->iobase_q;
+ for (cnt = 0; cnt < 7; cnt++)
+ *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++));
+
+ /* Transmit DMA registers. */
+ iter_reg = fw->xmt0_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7600, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7610, 16, iter_reg);
+
+ iter_reg = fw->xmt1_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7620, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7630, 16, iter_reg);
+
+ iter_reg = fw->xmt2_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7640, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7650, 16, iter_reg);
+
+ iter_reg = fw->xmt3_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7660, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7670, 16, iter_reg);
+
+ iter_reg = fw->xmt4_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7680, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7690, 16, iter_reg);
+
+ qla24xx_read_window(reg, 0x76A0, 16, fw->xmt_data_dma_reg);
+
+ /* Receive DMA registers. */
+ iter_reg = fw->rcvt0_data_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7700, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7710, 16, iter_reg);
+
+ iter_reg = fw->rcvt1_data_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7720, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7730, 16, iter_reg);
+
+ /* RISC registers. */
+ iter_reg = fw->risc_gp_reg;
+ iter_reg = qla24xx_read_window(reg, 0x0F00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F60, 16, iter_reg);
+ qla24xx_read_window(reg, 0x0F70, 16, iter_reg);
+
+ /* Local memory controller registers. */
+ iter_reg = fw->lmc_reg;
+ iter_reg = qla24xx_read_window(reg, 0x3000, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3010, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3020, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3030, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3040, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3050, 16, iter_reg);
+ qla24xx_read_window(reg, 0x3060, 16, iter_reg);
+
+ /* Fibre Protocol Module registers. */
+ iter_reg = fw->fpm_hdw_reg;
+ iter_reg = qla24xx_read_window(reg, 0x4000, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4010, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4020, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4030, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4040, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4050, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4060, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4070, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4080, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4090, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x40A0, 16, iter_reg);
+ qla24xx_read_window(reg, 0x40B0, 16, iter_reg);
+
+ /* Frame Buffer registers. */
+ iter_reg = fw->fb_hdw_reg;
+ iter_reg = qla24xx_read_window(reg, 0x6000, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6010, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6020, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6030, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6040, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6100, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6130, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6150, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6170, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6190, 16, iter_reg);
+ qla24xx_read_window(reg, 0x61B0, 16, iter_reg);
+
+ rval = qla24xx_soft_reset(ha);
+ if (rval != QLA_SUCCESS)
+ goto qla24xx_fw_dump_failed_0;
+
+ rval = qla24xx_dump_memory(ha, fw->code_ram, sizeof(fw->code_ram),
+ &nxt);
+ if (rval != QLA_SUCCESS)
+ goto qla24xx_fw_dump_failed_0;
+
+ nxt = qla2xxx_copy_queues(ha, nxt);
+
+ qla24xx_copy_eft(ha, nxt);
+
+qla24xx_fw_dump_failed_0:
+ qla2xxx_dump_post_process(base_vha, rval);
+
+qla24xx_fw_dump_failed:
+ if (!hardware_locked)
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+void
+qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
+{
+ int rval;
+ uint32_t cnt;
+ uint32_t risc_address;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+ uint32_t __iomem *dmp_reg;
+ uint32_t *iter_reg;
+ uint16_t __iomem *mbx_reg;
+ unsigned long flags;
+ struct qla25xx_fw_dump *fw;
+ uint32_t ext_mem_cnt;
+ void *nxt, *nxt_chain;
+ uint32_t *last_chain = NULL;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+
+ risc_address = ext_mem_cnt = 0;
+ flags = 0;
+
+ if (!hardware_locked)
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ if (!ha->fw_dump) {
+ ql_log(ql_log_warn, vha, 0xd008,
+ "No buffer available for dump.\n");
+ goto qla25xx_fw_dump_failed;
+ }
+
+ if (ha->fw_dumped) {
+ ql_log(ql_log_warn, vha, 0xd009,
+ "Firmware has been previously dumped (%p) "
+ "-- ignoring request.\n",
+ ha->fw_dump);
+ goto qla25xx_fw_dump_failed;
+ }
+ fw = &ha->fw_dump->isp.isp25;
+ qla2xxx_prep_dump(ha, ha->fw_dump);
+ ha->fw_dump->version = __constant_htonl(2);
+
+ fw->host_status = htonl(RD_REG_DWORD(&reg->host_status));
+
+ /* Pause RISC. */
+ rval = qla24xx_pause_risc(reg);
+ if (rval != QLA_SUCCESS)
+ goto qla25xx_fw_dump_failed_0;
+
+ /* Host/Risc registers. */
+ iter_reg = fw->host_risc_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7000, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7010, 16, iter_reg);
+
+ /* PCIe registers. */
+ WRT_REG_DWORD(&reg->iobase_addr, 0x7C00);
+ RD_REG_DWORD(&reg->iobase_addr);
+ WRT_REG_DWORD(&reg->iobase_window, 0x01);
+ dmp_reg = &reg->iobase_c4;
+ fw->pcie_regs[0] = htonl(RD_REG_DWORD(dmp_reg++));
+ fw->pcie_regs[1] = htonl(RD_REG_DWORD(dmp_reg++));
+ fw->pcie_regs[2] = htonl(RD_REG_DWORD(dmp_reg));
+ fw->pcie_regs[3] = htonl(RD_REG_DWORD(&reg->iobase_window));
+
+ WRT_REG_DWORD(&reg->iobase_window, 0x00);
+ RD_REG_DWORD(&reg->iobase_window);
+
+ /* Host interface registers. */
+ dmp_reg = &reg->flash_addr;
+ for (cnt = 0; cnt < sizeof(fw->host_reg) / 4; cnt++)
+ fw->host_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++));
+
+ /* Disable interrupts. */
+ WRT_REG_DWORD(&reg->ictrl, 0);
+ RD_REG_DWORD(&reg->ictrl);
+
+ /* Shadow registers. */
+ WRT_REG_DWORD(&reg->iobase_addr, 0x0F70);
+ RD_REG_DWORD(&reg->iobase_addr);
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0000000);
+ fw->shadow_reg[0] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0100000);
+ fw->shadow_reg[1] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0200000);
+ fw->shadow_reg[2] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0300000);
+ fw->shadow_reg[3] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0400000);
+ fw->shadow_reg[4] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0500000);
+ fw->shadow_reg[5] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0600000);
+ fw->shadow_reg[6] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0700000);
+ fw->shadow_reg[7] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0800000);
+ fw->shadow_reg[8] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0900000);
+ fw->shadow_reg[9] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0A00000);
+ fw->shadow_reg[10] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ /* RISC I/O register. */
+ WRT_REG_DWORD(&reg->iobase_addr, 0x0010);
+ fw->risc_io_reg = htonl(RD_REG_DWORD(&reg->iobase_window));
+
+ /* Mailbox registers. */
+ mbx_reg = &reg->mailbox0;
+ for (cnt = 0; cnt < sizeof(fw->mailbox_reg) / 2; cnt++)
+ fw->mailbox_reg[cnt] = htons(RD_REG_WORD(mbx_reg++));
+
+ /* Transfer sequence registers. */
+ iter_reg = fw->xseq_gp_reg;
+ iter_reg = qla24xx_read_window(reg, 0xBF00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF60, 16, iter_reg);
+ qla24xx_read_window(reg, 0xBF70, 16, iter_reg);
+
+ iter_reg = fw->xseq_0_reg;
+ iter_reg = qla24xx_read_window(reg, 0xBFC0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBFD0, 16, iter_reg);
+ qla24xx_read_window(reg, 0xBFE0, 16, iter_reg);
+
+ qla24xx_read_window(reg, 0xBFF0, 16, fw->xseq_1_reg);
+
+ /* Receive sequence registers. */
+ iter_reg = fw->rseq_gp_reg;
+ iter_reg = qla24xx_read_window(reg, 0xFF00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF60, 16, iter_reg);
+ qla24xx_read_window(reg, 0xFF70, 16, iter_reg);
+
+ iter_reg = fw->rseq_0_reg;
+ iter_reg = qla24xx_read_window(reg, 0xFFC0, 16, iter_reg);
+ qla24xx_read_window(reg, 0xFFD0, 16, iter_reg);
+
+ qla24xx_read_window(reg, 0xFFE0, 16, fw->rseq_1_reg);
+ qla24xx_read_window(reg, 0xFFF0, 16, fw->rseq_2_reg);
+
+ /* Auxiliary sequence registers. */
+ iter_reg = fw->aseq_gp_reg;
+ iter_reg = qla24xx_read_window(reg, 0xB000, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB010, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB020, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB030, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB040, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB050, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB060, 16, iter_reg);
+ qla24xx_read_window(reg, 0xB070, 16, iter_reg);
+
+ iter_reg = fw->aseq_0_reg;
+ iter_reg = qla24xx_read_window(reg, 0xB0C0, 16, iter_reg);
+ qla24xx_read_window(reg, 0xB0D0, 16, iter_reg);
+
+ qla24xx_read_window(reg, 0xB0E0, 16, fw->aseq_1_reg);
+ qla24xx_read_window(reg, 0xB0F0, 16, fw->aseq_2_reg);
+
+ /* Command DMA registers. */
+ qla24xx_read_window(reg, 0x7100, 16, fw->cmd_dma_reg);
+
+ /* Queues. */
+ iter_reg = fw->req0_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7200, 8, iter_reg);
+ dmp_reg = &reg->iobase_q;
+ for (cnt = 0; cnt < 7; cnt++)
+ *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++));
+
+ iter_reg = fw->resp0_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7300, 8, iter_reg);
+ dmp_reg = &reg->iobase_q;
+ for (cnt = 0; cnt < 7; cnt++)
+ *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++));
+
+ iter_reg = fw->req1_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7400, 8, iter_reg);
+ dmp_reg = &reg->iobase_q;
+ for (cnt = 0; cnt < 7; cnt++)
+ *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++));
+
+ /* Transmit DMA registers. */
+ iter_reg = fw->xmt0_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7600, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7610, 16, iter_reg);
+
+ iter_reg = fw->xmt1_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7620, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7630, 16, iter_reg);
+
+ iter_reg = fw->xmt2_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7640, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7650, 16, iter_reg);
+
+ iter_reg = fw->xmt3_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7660, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7670, 16, iter_reg);
+
+ iter_reg = fw->xmt4_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7680, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7690, 16, iter_reg);
+
+ qla24xx_read_window(reg, 0x76A0, 16, fw->xmt_data_dma_reg);
+
+ /* Receive DMA registers. */
+ iter_reg = fw->rcvt0_data_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7700, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7710, 16, iter_reg);
+
+ iter_reg = fw->rcvt1_data_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7720, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7730, 16, iter_reg);
+
+ /* RISC registers. */
+ iter_reg = fw->risc_gp_reg;
+ iter_reg = qla24xx_read_window(reg, 0x0F00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F60, 16, iter_reg);
+ qla24xx_read_window(reg, 0x0F70, 16, iter_reg);
+
+ /* Local memory controller registers. */
+ iter_reg = fw->lmc_reg;
+ iter_reg = qla24xx_read_window(reg, 0x3000, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3010, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3020, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3030, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3040, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3050, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3060, 16, iter_reg);
+ qla24xx_read_window(reg, 0x3070, 16, iter_reg);
+
+ /* Fibre Protocol Module registers. */
+ iter_reg = fw->fpm_hdw_reg;
+ iter_reg = qla24xx_read_window(reg, 0x4000, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4010, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4020, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4030, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4040, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4050, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4060, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4070, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4080, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4090, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x40A0, 16, iter_reg);
+ qla24xx_read_window(reg, 0x40B0, 16, iter_reg);
+
+ /* Frame Buffer registers. */
+ iter_reg = fw->fb_hdw_reg;
+ iter_reg = qla24xx_read_window(reg, 0x6000, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6010, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6020, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6030, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6040, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6100, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6130, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6150, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6170, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6190, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x61B0, 16, iter_reg);
+ qla24xx_read_window(reg, 0x6F00, 16, iter_reg);
+
+ /* Multi queue registers */
+ nxt_chain = qla25xx_copy_mq(ha, (void *)ha->fw_dump + ha->chain_offset,
+ &last_chain);
+
+ rval = qla24xx_soft_reset(ha);
+ if (rval != QLA_SUCCESS)
+ goto qla25xx_fw_dump_failed_0;
+
+ rval = qla24xx_dump_memory(ha, fw->code_ram, sizeof(fw->code_ram),
+ &nxt);
+ if (rval != QLA_SUCCESS)
+ goto qla25xx_fw_dump_failed_0;
+
+ nxt = qla2xxx_copy_queues(ha, nxt);
+
+ nxt = qla24xx_copy_eft(ha, nxt);
+
+ /* Chain entries -- started with MQ. */
+ nxt_chain = qla25xx_copy_fce(ha, nxt_chain, &last_chain);
+ nxt_chain = qla25xx_copy_mqueues(ha, nxt_chain, &last_chain);
+ if (last_chain) {
+ ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT);
+ *last_chain |= __constant_htonl(DUMP_CHAIN_LAST);
+ }
+
+ /* Adjust valid length. */
+ ha->fw_dump_len = (nxt_chain - (void *)ha->fw_dump);
+
+qla25xx_fw_dump_failed_0:
+ qla2xxx_dump_post_process(base_vha, rval);
+
+qla25xx_fw_dump_failed:
+ if (!hardware_locked)
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+void
+qla81xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
+{
+ int rval;
+ uint32_t cnt;
+ uint32_t risc_address;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+ uint32_t __iomem *dmp_reg;
+ uint32_t *iter_reg;
+ uint16_t __iomem *mbx_reg;
+ unsigned long flags;
+ struct qla81xx_fw_dump *fw;
+ uint32_t ext_mem_cnt;
+ void *nxt, *nxt_chain;
+ uint32_t *last_chain = NULL;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+
+ risc_address = ext_mem_cnt = 0;
+ flags = 0;
+
+ if (!hardware_locked)
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ if (!ha->fw_dump) {
+ ql_log(ql_log_warn, vha, 0xd00a,
+ "No buffer available for dump.\n");
+ goto qla81xx_fw_dump_failed;
+ }
+
+ if (ha->fw_dumped) {
+ ql_log(ql_log_warn, vha, 0xd00b,
+ "Firmware has been previously dumped (%p) "
+ "-- ignoring request.\n",
+ ha->fw_dump);
+ goto qla81xx_fw_dump_failed;
+ }
+ fw = &ha->fw_dump->isp.isp81;
+ qla2xxx_prep_dump(ha, ha->fw_dump);
+
+ fw->host_status = htonl(RD_REG_DWORD(&reg->host_status));
+
+ /* Pause RISC. */
+ rval = qla24xx_pause_risc(reg);
+ if (rval != QLA_SUCCESS)
+ goto qla81xx_fw_dump_failed_0;
+
+ /* Host/Risc registers. */
+ iter_reg = fw->host_risc_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7000, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7010, 16, iter_reg);
+
+ /* PCIe registers. */
+ WRT_REG_DWORD(&reg->iobase_addr, 0x7C00);
+ RD_REG_DWORD(&reg->iobase_addr);
+ WRT_REG_DWORD(&reg->iobase_window, 0x01);
+ dmp_reg = &reg->iobase_c4;
+ fw->pcie_regs[0] = htonl(RD_REG_DWORD(dmp_reg++));
+ fw->pcie_regs[1] = htonl(RD_REG_DWORD(dmp_reg++));
+ fw->pcie_regs[2] = htonl(RD_REG_DWORD(dmp_reg));
+ fw->pcie_regs[3] = htonl(RD_REG_DWORD(&reg->iobase_window));
+
+ WRT_REG_DWORD(&reg->iobase_window, 0x00);
+ RD_REG_DWORD(&reg->iobase_window);
+
+ /* Host interface registers. */
+ dmp_reg = &reg->flash_addr;
+ for (cnt = 0; cnt < sizeof(fw->host_reg) / 4; cnt++)
+ fw->host_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++));
+
+ /* Disable interrupts. */
+ WRT_REG_DWORD(&reg->ictrl, 0);
+ RD_REG_DWORD(&reg->ictrl);
+
+ /* Shadow registers. */
+ WRT_REG_DWORD(&reg->iobase_addr, 0x0F70);
+ RD_REG_DWORD(&reg->iobase_addr);
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0000000);
+ fw->shadow_reg[0] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0100000);
+ fw->shadow_reg[1] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0200000);
+ fw->shadow_reg[2] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0300000);
+ fw->shadow_reg[3] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0400000);
+ fw->shadow_reg[4] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0500000);
+ fw->shadow_reg[5] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0600000);
+ fw->shadow_reg[6] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0700000);
+ fw->shadow_reg[7] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0800000);
+ fw->shadow_reg[8] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0900000);
+ fw->shadow_reg[9] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0A00000);
+ fw->shadow_reg[10] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ /* RISC I/O register. */
+ WRT_REG_DWORD(&reg->iobase_addr, 0x0010);
+ fw->risc_io_reg = htonl(RD_REG_DWORD(&reg->iobase_window));
+
+ /* Mailbox registers. */
+ mbx_reg = &reg->mailbox0;
+ for (cnt = 0; cnt < sizeof(fw->mailbox_reg) / 2; cnt++)
+ fw->mailbox_reg[cnt] = htons(RD_REG_WORD(mbx_reg++));
+
+ /* Transfer sequence registers. */
+ iter_reg = fw->xseq_gp_reg;
+ iter_reg = qla24xx_read_window(reg, 0xBF00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF60, 16, iter_reg);
+ qla24xx_read_window(reg, 0xBF70, 16, iter_reg);
+
+ iter_reg = fw->xseq_0_reg;
+ iter_reg = qla24xx_read_window(reg, 0xBFC0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBFD0, 16, iter_reg);
+ qla24xx_read_window(reg, 0xBFE0, 16, iter_reg);
+
+ qla24xx_read_window(reg, 0xBFF0, 16, fw->xseq_1_reg);
+
+ /* Receive sequence registers. */
+ iter_reg = fw->rseq_gp_reg;
+ iter_reg = qla24xx_read_window(reg, 0xFF00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF60, 16, iter_reg);
+ qla24xx_read_window(reg, 0xFF70, 16, iter_reg);
+
+ iter_reg = fw->rseq_0_reg;
+ iter_reg = qla24xx_read_window(reg, 0xFFC0, 16, iter_reg);
+ qla24xx_read_window(reg, 0xFFD0, 16, iter_reg);
+
+ qla24xx_read_window(reg, 0xFFE0, 16, fw->rseq_1_reg);
+ qla24xx_read_window(reg, 0xFFF0, 16, fw->rseq_2_reg);
+
+ /* Auxiliary sequence registers. */
+ iter_reg = fw->aseq_gp_reg;
+ iter_reg = qla24xx_read_window(reg, 0xB000, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB010, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB020, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB030, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB040, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB050, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB060, 16, iter_reg);
+ qla24xx_read_window(reg, 0xB070, 16, iter_reg);
+
+ iter_reg = fw->aseq_0_reg;
+ iter_reg = qla24xx_read_window(reg, 0xB0C0, 16, iter_reg);
+ qla24xx_read_window(reg, 0xB0D0, 16, iter_reg);
+
+ qla24xx_read_window(reg, 0xB0E0, 16, fw->aseq_1_reg);
+ qla24xx_read_window(reg, 0xB0F0, 16, fw->aseq_2_reg);
+
+ /* Command DMA registers. */
+ qla24xx_read_window(reg, 0x7100, 16, fw->cmd_dma_reg);
+
+ /* Queues. */
+ iter_reg = fw->req0_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7200, 8, iter_reg);
+ dmp_reg = &reg->iobase_q;
+ for (cnt = 0; cnt < 7; cnt++)
+ *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++));
+
+ iter_reg = fw->resp0_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7300, 8, iter_reg);
+ dmp_reg = &reg->iobase_q;
+ for (cnt = 0; cnt < 7; cnt++)
+ *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++));
+
+ iter_reg = fw->req1_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7400, 8, iter_reg);
+ dmp_reg = &reg->iobase_q;
+ for (cnt = 0; cnt < 7; cnt++)
+ *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++));
+
+ /* Transmit DMA registers. */
+ iter_reg = fw->xmt0_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7600, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7610, 16, iter_reg);
+
+ iter_reg = fw->xmt1_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7620, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7630, 16, iter_reg);
+
+ iter_reg = fw->xmt2_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7640, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7650, 16, iter_reg);
+
+ iter_reg = fw->xmt3_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7660, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7670, 16, iter_reg);
+
+ iter_reg = fw->xmt4_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7680, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7690, 16, iter_reg);
+
+ qla24xx_read_window(reg, 0x76A0, 16, fw->xmt_data_dma_reg);
+
+ /* Receive DMA registers. */
+ iter_reg = fw->rcvt0_data_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7700, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7710, 16, iter_reg);
+
+ iter_reg = fw->rcvt1_data_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7720, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7730, 16, iter_reg);
+
+ /* RISC registers. */
+ iter_reg = fw->risc_gp_reg;
+ iter_reg = qla24xx_read_window(reg, 0x0F00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F60, 16, iter_reg);
+ qla24xx_read_window(reg, 0x0F70, 16, iter_reg);
+
+ /* Local memory controller registers. */
+ iter_reg = fw->lmc_reg;
+ iter_reg = qla24xx_read_window(reg, 0x3000, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3010, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3020, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3030, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3040, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3050, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3060, 16, iter_reg);
+ qla24xx_read_window(reg, 0x3070, 16, iter_reg);
+
+ /* Fibre Protocol Module registers. */
+ iter_reg = fw->fpm_hdw_reg;
+ iter_reg = qla24xx_read_window(reg, 0x4000, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4010, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4020, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4030, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4040, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4050, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4060, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4070, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4080, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4090, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x40A0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x40B0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x40C0, 16, iter_reg);
+ qla24xx_read_window(reg, 0x40D0, 16, iter_reg);
+
+ /* Frame Buffer registers. */
+ iter_reg = fw->fb_hdw_reg;
+ iter_reg = qla24xx_read_window(reg, 0x6000, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6010, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6020, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6030, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6040, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6100, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6130, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6150, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6170, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6190, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x61B0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x61C0, 16, iter_reg);
+ qla24xx_read_window(reg, 0x6F00, 16, iter_reg);
+
+ /* Multi queue registers */
+ nxt_chain = qla25xx_copy_mq(ha, (void *)ha->fw_dump + ha->chain_offset,
+ &last_chain);
+
+ rval = qla24xx_soft_reset(ha);
+ if (rval != QLA_SUCCESS)
+ goto qla81xx_fw_dump_failed_0;
+
+ rval = qla24xx_dump_memory(ha, fw->code_ram, sizeof(fw->code_ram),
+ &nxt);
+ if (rval != QLA_SUCCESS)
+ goto qla81xx_fw_dump_failed_0;
+
+ nxt = qla2xxx_copy_queues(ha, nxt);
+
+ nxt = qla24xx_copy_eft(ha, nxt);
+
+ /* Chain entries -- started with MQ. */
+ nxt_chain = qla25xx_copy_fce(ha, nxt_chain, &last_chain);
+ nxt_chain = qla25xx_copy_mqueues(ha, nxt_chain, &last_chain);
+ if (last_chain) {
+ ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT);
+ *last_chain |= __constant_htonl(DUMP_CHAIN_LAST);
+ }
+
+ /* Adjust valid length. */
+ ha->fw_dump_len = (nxt_chain - (void *)ha->fw_dump);
+
+qla81xx_fw_dump_failed_0:
+ qla2xxx_dump_post_process(base_vha, rval);
+
+qla81xx_fw_dump_failed:
+ if (!hardware_locked)
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+void
+qla83xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked)
+{
+ int rval;
+ uint32_t cnt, reg_data;
+ uint32_t risc_address;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+ uint32_t __iomem *dmp_reg;
+ uint32_t *iter_reg;
+ uint16_t __iomem *mbx_reg;
+ unsigned long flags;
+ struct qla83xx_fw_dump *fw;
+ uint32_t ext_mem_cnt;
+ void *nxt, *nxt_chain;
+ uint32_t *last_chain = NULL;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+
+ risc_address = ext_mem_cnt = 0;
+ flags = 0;
+
+ if (!hardware_locked)
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ if (!ha->fw_dump) {
+ ql_log(ql_log_warn, vha, 0xd00c,
+ "No buffer available for dump!!!\n");
+ goto qla83xx_fw_dump_failed;
+ }
+
+ if (ha->fw_dumped) {
+ ql_log(ql_log_warn, vha, 0xd00d,
+ "Firmware has been previously dumped (%p) -- ignoring "
+ "request...\n", ha->fw_dump);
+ goto qla83xx_fw_dump_failed;
+ }
+ fw = &ha->fw_dump->isp.isp83;
+ qla2xxx_prep_dump(ha, ha->fw_dump);
+
+ fw->host_status = htonl(RD_REG_DWORD(&reg->host_status));
+
+ /* Pause RISC. */
+ rval = qla24xx_pause_risc(reg);
+ if (rval != QLA_SUCCESS)
+ goto qla83xx_fw_dump_failed_0;
+
+ WRT_REG_DWORD(&reg->iobase_addr, 0x6000);
+ dmp_reg = &reg->iobase_window;
+ reg_data = RD_REG_DWORD(dmp_reg);
+ WRT_REG_DWORD(dmp_reg, 0);
+
+ dmp_reg = &reg->unused_4_1[0];
+ reg_data = RD_REG_DWORD(dmp_reg);
+ WRT_REG_DWORD(dmp_reg, 0);
+
+ WRT_REG_DWORD(&reg->iobase_addr, 0x6010);
+ dmp_reg = &reg->unused_4_1[2];
+ reg_data = RD_REG_DWORD(dmp_reg);
+ WRT_REG_DWORD(dmp_reg, 0);
+
+ /* select PCR and disable ecc checking and correction */
+ WRT_REG_DWORD(&reg->iobase_addr, 0x0F70);
+ RD_REG_DWORD(&reg->iobase_addr);
+ WRT_REG_DWORD(&reg->iobase_select, 0x60000000); /* write to F0h = PCR */
+
+ /* Host/Risc registers. */
+ iter_reg = fw->host_risc_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7000, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x7010, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7040, 16, iter_reg);
+
+ /* PCIe registers. */
+ WRT_REG_DWORD(&reg->iobase_addr, 0x7C00);
+ RD_REG_DWORD(&reg->iobase_addr);
+ WRT_REG_DWORD(&reg->iobase_window, 0x01);
+ dmp_reg = &reg->iobase_c4;
+ fw->pcie_regs[0] = htonl(RD_REG_DWORD(dmp_reg++));
+ fw->pcie_regs[1] = htonl(RD_REG_DWORD(dmp_reg++));
+ fw->pcie_regs[2] = htonl(RD_REG_DWORD(dmp_reg));
+ fw->pcie_regs[3] = htonl(RD_REG_DWORD(&reg->iobase_window));
+
+ WRT_REG_DWORD(&reg->iobase_window, 0x00);
+ RD_REG_DWORD(&reg->iobase_window);
+
+ /* Host interface registers. */
+ dmp_reg = &reg->flash_addr;
+ for (cnt = 0; cnt < sizeof(fw->host_reg) / 4; cnt++)
+ fw->host_reg[cnt] = htonl(RD_REG_DWORD(dmp_reg++));
+
+ /* Disable interrupts. */
+ WRT_REG_DWORD(&reg->ictrl, 0);
+ RD_REG_DWORD(&reg->ictrl);
+
+ /* Shadow registers. */
+ WRT_REG_DWORD(&reg->iobase_addr, 0x0F70);
+ RD_REG_DWORD(&reg->iobase_addr);
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0000000);
+ fw->shadow_reg[0] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0100000);
+ fw->shadow_reg[1] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0200000);
+ fw->shadow_reg[2] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0300000);
+ fw->shadow_reg[3] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0400000);
+ fw->shadow_reg[4] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0500000);
+ fw->shadow_reg[5] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0600000);
+ fw->shadow_reg[6] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0700000);
+ fw->shadow_reg[7] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0800000);
+ fw->shadow_reg[8] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0900000);
+ fw->shadow_reg[9] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ WRT_REG_DWORD(&reg->iobase_select, 0xB0A00000);
+ fw->shadow_reg[10] = htonl(RD_REG_DWORD(&reg->iobase_sdata));
+
+ /* RISC I/O register. */
+ WRT_REG_DWORD(&reg->iobase_addr, 0x0010);
+ fw->risc_io_reg = htonl(RD_REG_DWORD(&reg->iobase_window));
+
+ /* Mailbox registers. */
+ mbx_reg = &reg->mailbox0;
+ for (cnt = 0; cnt < sizeof(fw->mailbox_reg) / 2; cnt++)
+ fw->mailbox_reg[cnt] = htons(RD_REG_WORD(mbx_reg++));
+
+ /* Transfer sequence registers. */
+ iter_reg = fw->xseq_gp_reg;
+ iter_reg = qla24xx_read_window(reg, 0xBE00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBE10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBE20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBE30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBE40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBE50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBE60, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBE70, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBF60, 16, iter_reg);
+ qla24xx_read_window(reg, 0xBF70, 16, iter_reg);
+
+ iter_reg = fw->xseq_0_reg;
+ iter_reg = qla24xx_read_window(reg, 0xBFC0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xBFD0, 16, iter_reg);
+ qla24xx_read_window(reg, 0xBFE0, 16, iter_reg);
+
+ qla24xx_read_window(reg, 0xBFF0, 16, fw->xseq_1_reg);
+
+ qla24xx_read_window(reg, 0xBEF0, 16, fw->xseq_2_reg);
+
+ /* Receive sequence registers. */
+ iter_reg = fw->rseq_gp_reg;
+ iter_reg = qla24xx_read_window(reg, 0xFE00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFE10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFE20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFE30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFE40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFE50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFE60, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFE70, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xFF60, 16, iter_reg);
+ qla24xx_read_window(reg, 0xFF70, 16, iter_reg);
+
+ iter_reg = fw->rseq_0_reg;
+ iter_reg = qla24xx_read_window(reg, 0xFFC0, 16, iter_reg);
+ qla24xx_read_window(reg, 0xFFD0, 16, iter_reg);
+
+ qla24xx_read_window(reg, 0xFFE0, 16, fw->rseq_1_reg);
+ qla24xx_read_window(reg, 0xFFF0, 16, fw->rseq_2_reg);
+ qla24xx_read_window(reg, 0xFEF0, 16, fw->rseq_3_reg);
+
+ /* Auxiliary sequence registers. */
+ iter_reg = fw->aseq_gp_reg;
+ iter_reg = qla24xx_read_window(reg, 0xB000, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB010, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB020, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB030, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB040, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB050, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB060, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB070, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB100, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB110, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB120, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB130, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB140, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB150, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0xB160, 16, iter_reg);
+ qla24xx_read_window(reg, 0xB170, 16, iter_reg);
+
+ iter_reg = fw->aseq_0_reg;
+ iter_reg = qla24xx_read_window(reg, 0xB0C0, 16, iter_reg);
+ qla24xx_read_window(reg, 0xB0D0, 16, iter_reg);
+
+ qla24xx_read_window(reg, 0xB0E0, 16, fw->aseq_1_reg);
+ qla24xx_read_window(reg, 0xB0F0, 16, fw->aseq_2_reg);
+ qla24xx_read_window(reg, 0xB1F0, 16, fw->aseq_3_reg);
+
+ /* Command DMA registers. */
+ iter_reg = fw->cmd_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7100, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x7120, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x7130, 16, iter_reg);
+ qla24xx_read_window(reg, 0x71F0, 16, iter_reg);
+
+ /* Queues. */
+ iter_reg = fw->req0_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7200, 8, iter_reg);
+ dmp_reg = &reg->iobase_q;
+ for (cnt = 0; cnt < 7; cnt++)
+ *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++));
+
+ iter_reg = fw->resp0_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7300, 8, iter_reg);
+ dmp_reg = &reg->iobase_q;
+ for (cnt = 0; cnt < 7; cnt++)
+ *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++));
+
+ iter_reg = fw->req1_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7400, 8, iter_reg);
+ dmp_reg = &reg->iobase_q;
+ for (cnt = 0; cnt < 7; cnt++)
+ *iter_reg++ = htonl(RD_REG_DWORD(dmp_reg++));
+
+ /* Transmit DMA registers. */
+ iter_reg = fw->xmt0_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7600, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7610, 16, iter_reg);
+
+ iter_reg = fw->xmt1_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7620, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7630, 16, iter_reg);
+
+ iter_reg = fw->xmt2_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7640, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7650, 16, iter_reg);
+
+ iter_reg = fw->xmt3_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7660, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7670, 16, iter_reg);
+
+ iter_reg = fw->xmt4_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7680, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7690, 16, iter_reg);
+
+ qla24xx_read_window(reg, 0x76A0, 16, fw->xmt_data_dma_reg);
+
+ /* Receive DMA registers. */
+ iter_reg = fw->rcvt0_data_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7700, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7710, 16, iter_reg);
+
+ iter_reg = fw->rcvt1_data_dma_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7720, 16, iter_reg);
+ qla24xx_read_window(reg, 0x7730, 16, iter_reg);
+
+ /* RISC registers. */
+ iter_reg = fw->risc_gp_reg;
+ iter_reg = qla24xx_read_window(reg, 0x0F00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x0F60, 16, iter_reg);
+ qla24xx_read_window(reg, 0x0F70, 16, iter_reg);
+
+ /* Local memory controller registers. */
+ iter_reg = fw->lmc_reg;
+ iter_reg = qla24xx_read_window(reg, 0x3000, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3010, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3020, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3030, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3040, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3050, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x3060, 16, iter_reg);
+ qla24xx_read_window(reg, 0x3070, 16, iter_reg);
+
+ /* Fibre Protocol Module registers. */
+ iter_reg = fw->fpm_hdw_reg;
+ iter_reg = qla24xx_read_window(reg, 0x4000, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4010, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4020, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4030, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4040, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4050, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4060, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4070, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4080, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x4090, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x40A0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x40B0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x40C0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x40D0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x40E0, 16, iter_reg);
+ qla24xx_read_window(reg, 0x40F0, 16, iter_reg);
+
+ /* RQ0 Array registers. */
+ iter_reg = fw->rq0_array_reg;
+ iter_reg = qla24xx_read_window(reg, 0x5C00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5C10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5C20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5C30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5C40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5C50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5C60, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5C70, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5C80, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5C90, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5CA0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5CB0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5CC0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5CD0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5CE0, 16, iter_reg);
+ qla24xx_read_window(reg, 0x5CF0, 16, iter_reg);
+
+ /* RQ1 Array registers. */
+ iter_reg = fw->rq1_array_reg;
+ iter_reg = qla24xx_read_window(reg, 0x5D00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5D10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5D20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5D30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5D40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5D50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5D60, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5D70, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5D80, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5D90, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5DA0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5DB0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5DC0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5DD0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5DE0, 16, iter_reg);
+ qla24xx_read_window(reg, 0x5DF0, 16, iter_reg);
+
+ /* RP0 Array registers. */
+ iter_reg = fw->rp0_array_reg;
+ iter_reg = qla24xx_read_window(reg, 0x5E00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5E10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5E20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5E30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5E40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5E50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5E60, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5E70, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5E80, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5E90, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5EA0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5EB0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5EC0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5ED0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5EE0, 16, iter_reg);
+ qla24xx_read_window(reg, 0x5EF0, 16, iter_reg);
+
+ /* RP1 Array registers. */
+ iter_reg = fw->rp1_array_reg;
+ iter_reg = qla24xx_read_window(reg, 0x5F00, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5F10, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5F20, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5F30, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5F40, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5F50, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5F60, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5F70, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5F80, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5F90, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5FA0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5FB0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5FC0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5FD0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x5FE0, 16, iter_reg);
+ qla24xx_read_window(reg, 0x5FF0, 16, iter_reg);
+
+ iter_reg = fw->at0_array_reg;
+ iter_reg = qla24xx_read_window(reg, 0x7080, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x7090, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x70A0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x70B0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x70C0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x70D0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x70E0, 16, iter_reg);
+ qla24xx_read_window(reg, 0x70F0, 16, iter_reg);
+
+ /* I/O Queue Control registers. */
+ qla24xx_read_window(reg, 0x7800, 16, fw->queue_control_reg);
+
+ /* Frame Buffer registers. */
+ iter_reg = fw->fb_hdw_reg;
+ iter_reg = qla24xx_read_window(reg, 0x6000, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6010, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6020, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6030, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6040, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6060, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6070, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6100, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6130, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6150, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6170, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6190, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x61B0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x61C0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6530, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6540, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6550, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6560, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6570, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6580, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x6590, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x65A0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x65B0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x65C0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x65D0, 16, iter_reg);
+ iter_reg = qla24xx_read_window(reg, 0x65E0, 16, iter_reg);
+ qla24xx_read_window(reg, 0x6F00, 16, iter_reg);
+
+ /* Multi queue registers */
+ nxt_chain = qla25xx_copy_mq(ha, (void *)ha->fw_dump + ha->chain_offset,
+ &last_chain);
+
+ rval = qla24xx_soft_reset(ha);
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0xd00e,
+ "SOFT RESET FAILED, forcing continuation of dump!!!\n");
+ rval = QLA_SUCCESS;
+
+ ql_log(ql_log_warn, vha, 0xd00f, "try a bigger hammer!!!\n");
+
+ WRT_REG_DWORD(&reg->hccr, HCCRX_SET_RISC_RESET);
+ RD_REG_DWORD(&reg->hccr);
+
+ WRT_REG_DWORD(&reg->hccr, HCCRX_REL_RISC_PAUSE);
+ RD_REG_DWORD(&reg->hccr);
+
+ WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_RESET);
+ RD_REG_DWORD(&reg->hccr);
+
+ for (cnt = 30000; cnt && (RD_REG_WORD(&reg->mailbox0)); cnt--)
+ udelay(5);
+
+ if (!cnt) {
+ nxt = fw->code_ram;
+ nxt += sizeof(fw->code_ram),
+ nxt += (ha->fw_memory_size - 0x100000 + 1);
+ goto copy_queue;
+ } else
+ ql_log(ql_log_warn, vha, 0xd010,
+ "bigger hammer success?\n");
+ }
+
+ rval = qla24xx_dump_memory(ha, fw->code_ram, sizeof(fw->code_ram),
+ &nxt);
+ if (rval != QLA_SUCCESS)
+ goto qla83xx_fw_dump_failed_0;
+
+copy_queue:
+ nxt = qla2xxx_copy_queues(ha, nxt);
+
+ nxt = qla24xx_copy_eft(ha, nxt);
+
+ /* Chain entries -- started with MQ. */
+ nxt_chain = qla25xx_copy_fce(ha, nxt_chain, &last_chain);
+ nxt_chain = qla25xx_copy_mqueues(ha, nxt_chain, &last_chain);
+ if (last_chain) {
+ ha->fw_dump->version |= __constant_htonl(DUMP_CHAIN_VARIANT);
+ *last_chain |= __constant_htonl(DUMP_CHAIN_LAST);
+ }
+
+ /* Adjust valid length. */
+ ha->fw_dump_len = (nxt_chain - (void *)ha->fw_dump);
+
+qla83xx_fw_dump_failed_0:
+ qla2xxx_dump_post_process(base_vha, rval);
+
+qla83xx_fw_dump_failed:
+ if (!hardware_locked)
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+/****************************************************************************/
+/* Driver Debug Functions. */
+/****************************************************************************/
+
+static inline int
+ql_mask_match(uint32_t level)
+{
+ if (ql2xextended_error_logging == 1)
+ ql2xextended_error_logging = QL_DBG_DEFAULT1_MASK;
+ return (level & ql2xextended_error_logging) == level;
+}
+
+/*
+ * This function is for formatting and logging debug information.
+ * It is to be used when vha is available. It formats the message
+ * and logs it to the messages file.
+ * parameters:
+ * level: The level of the debug messages to be printed.
+ * If ql2xextended_error_logging value is correctly set,
+ * this message will appear in the messages file.
+ * vha: Pointer to the scsi_qla_host_t.
+ * id: This is a unique identifier for the level. It identifies the
+ * part of the code from where the message originated.
+ * msg: The message to be displayed.
+ */
+void
+ql_dbg(uint32_t level, scsi_qla_host_t *vha, int32_t id, const char *fmt, ...)
+{
+ va_list va;
+ struct va_format vaf;
+
+ if (!ql_mask_match(level))
+ return;
+
+ va_start(va, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &va;
+
+ if (vha != NULL) {
+ const struct pci_dev *pdev = vha->hw->pdev;
+ /* <module-name> <pci-name> <msg-id>:<host> Message */
+ pr_warn("%s [%s]-%04x:%ld: %pV",
+ QL_MSGHDR, dev_name(&(pdev->dev)), id + ql_dbg_offset,
+ vha->host_no, &vaf);
+ } else {
+ pr_warn("%s [%s]-%04x: : %pV",
+ QL_MSGHDR, "0000:00:00.0", id + ql_dbg_offset, &vaf);
+ }
+
+ va_end(va);
+
+}
+
+/*
+ * This function is for formatting and logging debug information.
+ * It is to be used when vha is not available and pci is availble,
+ * i.e., before host allocation. It formats the message and logs it
+ * to the messages file.
+ * parameters:
+ * level: The level of the debug messages to be printed.
+ * If ql2xextended_error_logging value is correctly set,
+ * this message will appear in the messages file.
+ * pdev: Pointer to the struct pci_dev.
+ * id: This is a unique id for the level. It identifies the part
+ * of the code from where the message originated.
+ * msg: The message to be displayed.
+ */
+void
+ql_dbg_pci(uint32_t level, struct pci_dev *pdev, int32_t id,
+ const char *fmt, ...)
+{
+ va_list va;
+ struct va_format vaf;
+
+ if (pdev == NULL)
+ return;
+ if (!ql_mask_match(level))
+ return;
+
+ va_start(va, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &va;
+
+ /* <module-name> <dev-name>:<msg-id> Message */
+ pr_warn("%s [%s]-%04x: : %pV",
+ QL_MSGHDR, dev_name(&(pdev->dev)), id + ql_dbg_offset, &vaf);
+
+ va_end(va);
+}
+
+/*
+ * This function is for formatting and logging log messages.
+ * It is to be used when vha is available. It formats the message
+ * and logs it to the messages file. All the messages will be logged
+ * irrespective of value of ql2xextended_error_logging.
+ * parameters:
+ * level: The level of the log messages to be printed in the
+ * messages file.
+ * vha: Pointer to the scsi_qla_host_t
+ * id: This is a unique id for the level. It identifies the
+ * part of the code from where the message originated.
+ * msg: The message to be displayed.
+ */
+void
+ql_log(uint32_t level, scsi_qla_host_t *vha, int32_t id, const char *fmt, ...)
+{
+ va_list va;
+ struct va_format vaf;
+ char pbuf[128];
+
+ if (level > ql_errlev)
+ return;
+
+ if (vha != NULL) {
+ const struct pci_dev *pdev = vha->hw->pdev;
+ /* <module-name> <msg-id>:<host> Message */
+ snprintf(pbuf, sizeof(pbuf), "%s [%s]-%04x:%ld: ",
+ QL_MSGHDR, dev_name(&(pdev->dev)), id, vha->host_no);
+ } else {
+ snprintf(pbuf, sizeof(pbuf), "%s [%s]-%04x: : ",
+ QL_MSGHDR, "0000:00:00.0", id);
+ }
+ pbuf[sizeof(pbuf) - 1] = 0;
+
+ va_start(va, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &va;
+
+ switch (level) {
+ case ql_log_fatal: /* FATAL LOG */
+ pr_crit("%s%pV", pbuf, &vaf);
+ break;
+ case ql_log_warn:
+ pr_err("%s%pV", pbuf, &vaf);
+ break;
+ case ql_log_info:
+ pr_warn("%s%pV", pbuf, &vaf);
+ break;
+ default:
+ pr_info("%s%pV", pbuf, &vaf);
+ break;
+ }
+
+ va_end(va);
+}
+
+/*
+ * This function is for formatting and logging log messages.
+ * It is to be used when vha is not available and pci is availble,
+ * i.e., before host allocation. It formats the message and logs
+ * it to the messages file. All the messages are logged irrespective
+ * of the value of ql2xextended_error_logging.
+ * parameters:
+ * level: The level of the log messages to be printed in the
+ * messages file.
+ * pdev: Pointer to the struct pci_dev.
+ * id: This is a unique id for the level. It identifies the
+ * part of the code from where the message originated.
+ * msg: The message to be displayed.
+ */
+void
+ql_log_pci(uint32_t level, struct pci_dev *pdev, int32_t id,
+ const char *fmt, ...)
+{
+ va_list va;
+ struct va_format vaf;
+ char pbuf[128];
+
+ if (pdev == NULL)
+ return;
+ if (level > ql_errlev)
+ return;
+
+ /* <module-name> <dev-name>:<msg-id> Message */
+ snprintf(pbuf, sizeof(pbuf), "%s [%s]-%04x: : ",
+ QL_MSGHDR, dev_name(&(pdev->dev)), id);
+ pbuf[sizeof(pbuf) - 1] = 0;
+
+ va_start(va, fmt);
+
+ vaf.fmt = fmt;
+ vaf.va = &va;
+
+ switch (level) {
+ case ql_log_fatal: /* FATAL LOG */
+ pr_crit("%s%pV", pbuf, &vaf);
+ break;
+ case ql_log_warn:
+ pr_err("%s%pV", pbuf, &vaf);
+ break;
+ case ql_log_info:
+ pr_warn("%s%pV", pbuf, &vaf);
+ break;
+ default:
+ pr_info("%s%pV", pbuf, &vaf);
+ break;
+ }
+
+ va_end(va);
+}
+
+void
+ql_dump_regs(uint32_t level, scsi_qla_host_t *vha, int32_t id)
+{
+ int i;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24;
+ struct device_reg_82xx __iomem *reg82 = &ha->iobase->isp82;
+ uint16_t __iomem *mbx_reg;
+
+ if (!ql_mask_match(level))
+ return;
+
+ if (IS_QLA82XX(ha))
+ mbx_reg = &reg82->mailbox_in[0];
+ else if (IS_FWI2_CAPABLE(ha))
+ mbx_reg = &reg24->mailbox0;
+ else
+ mbx_reg = MAILBOX_REG(ha, reg, 0);
+
+ ql_dbg(level, vha, id, "Mailbox registers:\n");
+ for (i = 0; i < 6; i++)
+ ql_dbg(level, vha, id,
+ "mbox[%d] 0x%04x\n", i, RD_REG_WORD(mbx_reg++));
+}
+
+
+void
+ql_dump_buffer(uint32_t level, scsi_qla_host_t *vha, int32_t id,
+ uint8_t *b, uint32_t size)
+{
+ uint32_t cnt;
+ uint8_t c;
+
+ if (!ql_mask_match(level))
+ return;
+
+ ql_dbg(level, vha, id, " 0 1 2 3 4 5 6 7 8 "
+ "9 Ah Bh Ch Dh Eh Fh\n");
+ ql_dbg(level, vha, id, "----------------------------------"
+ "----------------------------\n");
+
+ ql_dbg(level, vha, id, " ");
+ for (cnt = 0; cnt < size;) {
+ c = *b++;
+ printk("%02x", (uint32_t) c);
+ cnt++;
+ if (!(cnt % 16))
+ printk("\n");
+ else
+ printk(" ");
+ }
+ if (cnt % 16)
+ ql_dbg(level, vha, id, "\n");
+}
diff --git a/drivers/scsi/qla2xxx/qla_dbg.h b/drivers/scsi/qla2xxx/qla_dbg.h
new file mode 100644
index 00000000..2157bdf1
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_dbg.h
@@ -0,0 +1,341 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2003-2011 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+
+#include "qla_def.h"
+
+/*
+ * Firmware Dump structure definition
+ */
+
+struct qla2300_fw_dump {
+ uint16_t hccr;
+ uint16_t pbiu_reg[8];
+ uint16_t risc_host_reg[8];
+ uint16_t mailbox_reg[32];
+ uint16_t resp_dma_reg[32];
+ uint16_t dma_reg[48];
+ uint16_t risc_hdw_reg[16];
+ uint16_t risc_gp0_reg[16];
+ uint16_t risc_gp1_reg[16];
+ uint16_t risc_gp2_reg[16];
+ uint16_t risc_gp3_reg[16];
+ uint16_t risc_gp4_reg[16];
+ uint16_t risc_gp5_reg[16];
+ uint16_t risc_gp6_reg[16];
+ uint16_t risc_gp7_reg[16];
+ uint16_t frame_buf_hdw_reg[64];
+ uint16_t fpm_b0_reg[64];
+ uint16_t fpm_b1_reg[64];
+ uint16_t risc_ram[0xf800];
+ uint16_t stack_ram[0x1000];
+ uint16_t data_ram[1];
+};
+
+struct qla2100_fw_dump {
+ uint16_t hccr;
+ uint16_t pbiu_reg[8];
+ uint16_t mailbox_reg[32];
+ uint16_t dma_reg[48];
+ uint16_t risc_hdw_reg[16];
+ uint16_t risc_gp0_reg[16];
+ uint16_t risc_gp1_reg[16];
+ uint16_t risc_gp2_reg[16];
+ uint16_t risc_gp3_reg[16];
+ uint16_t risc_gp4_reg[16];
+ uint16_t risc_gp5_reg[16];
+ uint16_t risc_gp6_reg[16];
+ uint16_t risc_gp7_reg[16];
+ uint16_t frame_buf_hdw_reg[16];
+ uint16_t fpm_b0_reg[64];
+ uint16_t fpm_b1_reg[64];
+ uint16_t risc_ram[0xf000];
+};
+
+struct qla24xx_fw_dump {
+ uint32_t host_status;
+ uint32_t host_reg[32];
+ uint32_t shadow_reg[7];
+ uint16_t mailbox_reg[32];
+ uint32_t xseq_gp_reg[128];
+ uint32_t xseq_0_reg[16];
+ uint32_t xseq_1_reg[16];
+ uint32_t rseq_gp_reg[128];
+ uint32_t rseq_0_reg[16];
+ uint32_t rseq_1_reg[16];
+ uint32_t rseq_2_reg[16];
+ uint32_t cmd_dma_reg[16];
+ uint32_t req0_dma_reg[15];
+ uint32_t resp0_dma_reg[15];
+ uint32_t req1_dma_reg[15];
+ uint32_t xmt0_dma_reg[32];
+ uint32_t xmt1_dma_reg[32];
+ uint32_t xmt2_dma_reg[32];
+ uint32_t xmt3_dma_reg[32];
+ uint32_t xmt4_dma_reg[32];
+ uint32_t xmt_data_dma_reg[16];
+ uint32_t rcvt0_data_dma_reg[32];
+ uint32_t rcvt1_data_dma_reg[32];
+ uint32_t risc_gp_reg[128];
+ uint32_t lmc_reg[112];
+ uint32_t fpm_hdw_reg[192];
+ uint32_t fb_hdw_reg[176];
+ uint32_t code_ram[0x2000];
+ uint32_t ext_mem[1];
+};
+
+struct qla25xx_fw_dump {
+ uint32_t host_status;
+ uint32_t host_risc_reg[32];
+ uint32_t pcie_regs[4];
+ uint32_t host_reg[32];
+ uint32_t shadow_reg[11];
+ uint32_t risc_io_reg;
+ uint16_t mailbox_reg[32];
+ uint32_t xseq_gp_reg[128];
+ uint32_t xseq_0_reg[48];
+ uint32_t xseq_1_reg[16];
+ uint32_t rseq_gp_reg[128];
+ uint32_t rseq_0_reg[32];
+ uint32_t rseq_1_reg[16];
+ uint32_t rseq_2_reg[16];
+ uint32_t aseq_gp_reg[128];
+ uint32_t aseq_0_reg[32];
+ uint32_t aseq_1_reg[16];
+ uint32_t aseq_2_reg[16];
+ uint32_t cmd_dma_reg[16];
+ uint32_t req0_dma_reg[15];
+ uint32_t resp0_dma_reg[15];
+ uint32_t req1_dma_reg[15];
+ uint32_t xmt0_dma_reg[32];
+ uint32_t xmt1_dma_reg[32];
+ uint32_t xmt2_dma_reg[32];
+ uint32_t xmt3_dma_reg[32];
+ uint32_t xmt4_dma_reg[32];
+ uint32_t xmt_data_dma_reg[16];
+ uint32_t rcvt0_data_dma_reg[32];
+ uint32_t rcvt1_data_dma_reg[32];
+ uint32_t risc_gp_reg[128];
+ uint32_t lmc_reg[128];
+ uint32_t fpm_hdw_reg[192];
+ uint32_t fb_hdw_reg[192];
+ uint32_t code_ram[0x2000];
+ uint32_t ext_mem[1];
+};
+
+struct qla81xx_fw_dump {
+ uint32_t host_status;
+ uint32_t host_risc_reg[32];
+ uint32_t pcie_regs[4];
+ uint32_t host_reg[32];
+ uint32_t shadow_reg[11];
+ uint32_t risc_io_reg;
+ uint16_t mailbox_reg[32];
+ uint32_t xseq_gp_reg[128];
+ uint32_t xseq_0_reg[48];
+ uint32_t xseq_1_reg[16];
+ uint32_t rseq_gp_reg[128];
+ uint32_t rseq_0_reg[32];
+ uint32_t rseq_1_reg[16];
+ uint32_t rseq_2_reg[16];
+ uint32_t aseq_gp_reg[128];
+ uint32_t aseq_0_reg[32];
+ uint32_t aseq_1_reg[16];
+ uint32_t aseq_2_reg[16];
+ uint32_t cmd_dma_reg[16];
+ uint32_t req0_dma_reg[15];
+ uint32_t resp0_dma_reg[15];
+ uint32_t req1_dma_reg[15];
+ uint32_t xmt0_dma_reg[32];
+ uint32_t xmt1_dma_reg[32];
+ uint32_t xmt2_dma_reg[32];
+ uint32_t xmt3_dma_reg[32];
+ uint32_t xmt4_dma_reg[32];
+ uint32_t xmt_data_dma_reg[16];
+ uint32_t rcvt0_data_dma_reg[32];
+ uint32_t rcvt1_data_dma_reg[32];
+ uint32_t risc_gp_reg[128];
+ uint32_t lmc_reg[128];
+ uint32_t fpm_hdw_reg[224];
+ uint32_t fb_hdw_reg[208];
+ uint32_t code_ram[0x2000];
+ uint32_t ext_mem[1];
+};
+
+struct qla83xx_fw_dump {
+ uint32_t host_status;
+ uint32_t host_risc_reg[48];
+ uint32_t pcie_regs[4];
+ uint32_t host_reg[32];
+ uint32_t shadow_reg[11];
+ uint32_t risc_io_reg;
+ uint16_t mailbox_reg[32];
+ uint32_t xseq_gp_reg[256];
+ uint32_t xseq_0_reg[48];
+ uint32_t xseq_1_reg[16];
+ uint32_t xseq_2_reg[16];
+ uint32_t rseq_gp_reg[256];
+ uint32_t rseq_0_reg[32];
+ uint32_t rseq_1_reg[16];
+ uint32_t rseq_2_reg[16];
+ uint32_t rseq_3_reg[16];
+ uint32_t aseq_gp_reg[256];
+ uint32_t aseq_0_reg[32];
+ uint32_t aseq_1_reg[16];
+ uint32_t aseq_2_reg[16];
+ uint32_t aseq_3_reg[16];
+ uint32_t cmd_dma_reg[64];
+ uint32_t req0_dma_reg[15];
+ uint32_t resp0_dma_reg[15];
+ uint32_t req1_dma_reg[15];
+ uint32_t xmt0_dma_reg[32];
+ uint32_t xmt1_dma_reg[32];
+ uint32_t xmt2_dma_reg[32];
+ uint32_t xmt3_dma_reg[32];
+ uint32_t xmt4_dma_reg[32];
+ uint32_t xmt_data_dma_reg[16];
+ uint32_t rcvt0_data_dma_reg[32];
+ uint32_t rcvt1_data_dma_reg[32];
+ uint32_t risc_gp_reg[128];
+ uint32_t lmc_reg[128];
+ uint32_t fpm_hdw_reg[256];
+ uint32_t rq0_array_reg[256];
+ uint32_t rq1_array_reg[256];
+ uint32_t rp0_array_reg[256];
+ uint32_t rp1_array_reg[256];
+ uint32_t queue_control_reg[16];
+ uint32_t fb_hdw_reg[432];
+ uint32_t at0_array_reg[128];
+ uint32_t code_ram[0x2400];
+ uint32_t ext_mem[1];
+};
+
+#define EFT_NUM_BUFFERS 4
+#define EFT_BYTES_PER_BUFFER 0x4000
+#define EFT_SIZE ((EFT_BYTES_PER_BUFFER) * (EFT_NUM_BUFFERS))
+
+#define FCE_NUM_BUFFERS 64
+#define FCE_BYTES_PER_BUFFER 0x400
+#define FCE_SIZE ((FCE_BYTES_PER_BUFFER) * (FCE_NUM_BUFFERS))
+#define fce_calc_size(b) ((FCE_BYTES_PER_BUFFER) * (b))
+
+struct qla2xxx_fce_chain {
+ uint32_t type;
+ uint32_t chain_size;
+
+ uint32_t size;
+ uint32_t addr_l;
+ uint32_t addr_h;
+ uint32_t eregs[8];
+};
+
+struct qla2xxx_mq_chain {
+ uint32_t type;
+ uint32_t chain_size;
+
+ uint32_t count;
+ uint32_t qregs[4 * QLA_MQ_SIZE];
+};
+
+struct qla2xxx_mqueue_header {
+ uint32_t queue;
+#define TYPE_REQUEST_QUEUE 0x1
+#define TYPE_RESPONSE_QUEUE 0x2
+ uint32_t number;
+ uint32_t size;
+};
+
+struct qla2xxx_mqueue_chain {
+ uint32_t type;
+ uint32_t chain_size;
+};
+
+#define DUMP_CHAIN_VARIANT 0x80000000
+#define DUMP_CHAIN_FCE 0x7FFFFAF0
+#define DUMP_CHAIN_MQ 0x7FFFFAF1
+#define DUMP_CHAIN_QUEUE 0x7FFFFAF2
+#define DUMP_CHAIN_LAST 0x80000000
+
+struct qla2xxx_fw_dump {
+ uint8_t signature[4];
+ uint32_t version;
+
+ uint32_t fw_major_version;
+ uint32_t fw_minor_version;
+ uint32_t fw_subminor_version;
+ uint32_t fw_attributes;
+
+ uint32_t vendor;
+ uint32_t device;
+ uint32_t subsystem_vendor;
+ uint32_t subsystem_device;
+
+ uint32_t fixed_size;
+ uint32_t mem_size;
+ uint32_t req_q_size;
+ uint32_t rsp_q_size;
+
+ uint32_t eft_size;
+ uint32_t eft_addr_l;
+ uint32_t eft_addr_h;
+
+ uint32_t header_size;
+
+ union {
+ struct qla2100_fw_dump isp21;
+ struct qla2300_fw_dump isp23;
+ struct qla24xx_fw_dump isp24;
+ struct qla25xx_fw_dump isp25;
+ struct qla81xx_fw_dump isp81;
+ struct qla83xx_fw_dump isp83;
+ } isp;
+};
+
+#define QL_MSGHDR "qla2xxx"
+#define QL_DBG_DEFAULT1_MASK 0x1e400000
+
+#define ql_log_fatal 0 /* display fatal errors */
+#define ql_log_warn 1 /* display critical errors */
+#define ql_log_info 2 /* display all recovered errors */
+#define ql_log_all 3 /* This value is only used by ql_errlev.
+ * No messages will use this value.
+ * This should be always highest value
+ * as compared to other log levels.
+ */
+
+extern int ql_errlev;
+
+void __attribute__((format (printf, 4, 5)))
+ql_dbg(uint32_t, scsi_qla_host_t *vha, int32_t, const char *fmt, ...);
+void __attribute__((format (printf, 4, 5)))
+ql_dbg_pci(uint32_t, struct pci_dev *pdev, int32_t, const char *fmt, ...);
+
+void __attribute__((format (printf, 4, 5)))
+ql_log(uint32_t, scsi_qla_host_t *vha, int32_t, const char *fmt, ...);
+void __attribute__((format (printf, 4, 5)))
+ql_log_pci(uint32_t, struct pci_dev *pdev, int32_t, const char *fmt, ...);
+
+/* Debug Levels */
+/* The 0x40000000 is the max value any debug level can have
+ * as ql2xextended_error_logging is of type signed int
+ */
+#define ql_dbg_init 0x40000000 /* Init Debug */
+#define ql_dbg_mbx 0x20000000 /* MBX Debug */
+#define ql_dbg_disc 0x10000000 /* Device Discovery Debug */
+#define ql_dbg_io 0x08000000 /* IO Tracing Debug */
+#define ql_dbg_dpc 0x04000000 /* DPC Thead Debug */
+#define ql_dbg_async 0x02000000 /* Async events Debug */
+#define ql_dbg_timer 0x01000000 /* Timer Debug */
+#define ql_dbg_user 0x00800000 /* User Space Interations Debug */
+#define ql_dbg_taskm 0x00400000 /* Task Management Debug */
+#define ql_dbg_aer 0x00200000 /* AER/EEH Debug */
+#define ql_dbg_multiq 0x00100000 /* MultiQ Debug */
+#define ql_dbg_p3p 0x00080000 /* P3P specific Debug */
+#define ql_dbg_vport 0x00040000 /* Virtual Port Debug */
+#define ql_dbg_buffer 0x00020000 /* For dumping the buffer/regs */
+#define ql_dbg_misc 0x00010000 /* For dumping everything that is not
+ * not covered by upper categories
+ */
diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h
new file mode 100644
index 00000000..a2443031
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_def.h
@@ -0,0 +1,3068 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2003-2011 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#ifndef __QLA_DEF_H
+#define __QLA_DEF_H
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/pci.h>
+#include <linux/dma-mapping.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/dmapool.h>
+#include <linux/mempool.h>
+#include <linux/spinlock.h>
+#include <linux/completion.h>
+#include <linux/interrupt.h>
+#include <linux/workqueue.h>
+#include <linux/firmware.h>
+#include <linux/aer.h>
+#include <linux/mutex.h>
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_transport_fc.h>
+#include <scsi/scsi_bsg_fc.h>
+
+#include "qla_bsg.h"
+#include "qla_nx.h"
+#define QLA2XXX_DRIVER_NAME "qla2xxx"
+#define QLA2XXX_APIDEV "ql2xapidev"
+
+/*
+ * We have MAILBOX_REGISTER_COUNT sized arrays in a few places,
+ * but that's fine as we don't look at the last 24 ones for
+ * ISP2100 HBAs.
+ */
+#define MAILBOX_REGISTER_COUNT_2100 8
+#define MAILBOX_REGISTER_COUNT_2200 24
+#define MAILBOX_REGISTER_COUNT 32
+
+#define QLA2200A_RISC_ROM_VER 4
+#define FPM_2300 6
+#define FPM_2310 7
+
+#include "qla_settings.h"
+
+/*
+ * Data bit definitions
+ */
+#define BIT_0 0x1
+#define BIT_1 0x2
+#define BIT_2 0x4
+#define BIT_3 0x8
+#define BIT_4 0x10
+#define BIT_5 0x20
+#define BIT_6 0x40
+#define BIT_7 0x80
+#define BIT_8 0x100
+#define BIT_9 0x200
+#define BIT_10 0x400
+#define BIT_11 0x800
+#define BIT_12 0x1000
+#define BIT_13 0x2000
+#define BIT_14 0x4000
+#define BIT_15 0x8000
+#define BIT_16 0x10000
+#define BIT_17 0x20000
+#define BIT_18 0x40000
+#define BIT_19 0x80000
+#define BIT_20 0x100000
+#define BIT_21 0x200000
+#define BIT_22 0x400000
+#define BIT_23 0x800000
+#define BIT_24 0x1000000
+#define BIT_25 0x2000000
+#define BIT_26 0x4000000
+#define BIT_27 0x8000000
+#define BIT_28 0x10000000
+#define BIT_29 0x20000000
+#define BIT_30 0x40000000
+#define BIT_31 0x80000000
+
+#define LSB(x) ((uint8_t)(x))
+#define MSB(x) ((uint8_t)((uint16_t)(x) >> 8))
+
+#define LSW(x) ((uint16_t)(x))
+#define MSW(x) ((uint16_t)((uint32_t)(x) >> 16))
+
+#define LSD(x) ((uint32_t)((uint64_t)(x)))
+#define MSD(x) ((uint32_t)((((uint64_t)(x)) >> 16) >> 16))
+
+#define MAKE_HANDLE(x, y) ((uint32_t)((((uint32_t)(x)) << 16) | (uint32_t)(y)))
+
+/*
+ * I/O register
+*/
+
+#define RD_REG_BYTE(addr) readb(addr)
+#define RD_REG_WORD(addr) readw(addr)
+#define RD_REG_DWORD(addr) readl(addr)
+#define RD_REG_BYTE_RELAXED(addr) readb_relaxed(addr)
+#define RD_REG_WORD_RELAXED(addr) readw_relaxed(addr)
+#define RD_REG_DWORD_RELAXED(addr) readl_relaxed(addr)
+#define WRT_REG_BYTE(addr, data) writeb(data,addr)
+#define WRT_REG_WORD(addr, data) writew(data,addr)
+#define WRT_REG_DWORD(addr, data) writel(data,addr)
+
+/*
+ * The ISP2312 v2 chip cannot access the FLASH/GPIO registers via MMIO in an
+ * 133Mhz slot.
+ */
+#define RD_REG_WORD_PIO(addr) (inw((unsigned long)addr))
+#define WRT_REG_WORD_PIO(addr, data) (outw(data,(unsigned long)addr))
+
+/*
+ * Fibre Channel device definitions.
+ */
+#define WWN_SIZE 8 /* Size of WWPN, WWN & WWNN */
+#define MAX_FIBRE_DEVICES_2100 512
+#define MAX_FIBRE_DEVICES_2400 2048
+#define MAX_FIBRE_DEVICES_LOOP 128
+#define MAX_FIBRE_DEVICES_MAX MAX_FIBRE_DEVICES_2400
+#define MAX_FIBRE_LUNS 0xFFFF
+#define MAX_HOST_COUNT 16
+
+/*
+ * Host adapter default definitions.
+ */
+#define MAX_BUSES 1 /* We only have one bus today */
+#define MIN_LUNS 8
+#define MAX_LUNS MAX_FIBRE_LUNS
+#define MAX_CMDS_PER_LUN 255
+
+/*
+ * Fibre Channel device definitions.
+ */
+#define SNS_LAST_LOOP_ID_2100 0xfe
+#define SNS_LAST_LOOP_ID_2300 0x7ff
+
+#define LAST_LOCAL_LOOP_ID 0x7d
+#define SNS_FL_PORT 0x7e
+#define FABRIC_CONTROLLER 0x7f
+#define SIMPLE_NAME_SERVER 0x80
+#define SNS_FIRST_LOOP_ID 0x81
+#define MANAGEMENT_SERVER 0xfe
+#define BROADCAST 0xff
+
+/*
+ * There is no correspondence between an N-PORT id and an AL_PA. Therefore the
+ * valid range of an N-PORT id is 0 through 0x7ef.
+ */
+#define NPH_LAST_HANDLE 0x7ef
+#define NPH_MGMT_SERVER 0x7fa /* FFFFFA */
+#define NPH_SNS 0x7fc /* FFFFFC */
+#define NPH_FABRIC_CONTROLLER 0x7fd /* FFFFFD */
+#define NPH_F_PORT 0x7fe /* FFFFFE */
+#define NPH_IP_BROADCAST 0x7ff /* FFFFFF */
+
+#define MAX_CMDSZ 16 /* SCSI maximum CDB size. */
+#include "qla_fw.h"
+
+/*
+ * Timeout timer counts in seconds
+ */
+#define PORT_RETRY_TIME 1
+#define LOOP_DOWN_TIMEOUT 60
+#define LOOP_DOWN_TIME 255 /* 240 */
+#define LOOP_DOWN_RESET (LOOP_DOWN_TIME - 30)
+
+/* Maximum outstanding commands in ISP queues (1-65535) */
+#define MAX_OUTSTANDING_COMMANDS 1024
+
+/* ISP request and response entry counts (37-65535) */
+#define REQUEST_ENTRY_CNT_2100 128 /* Number of request entries. */
+#define REQUEST_ENTRY_CNT_2200 2048 /* Number of request entries. */
+#define REQUEST_ENTRY_CNT_24XX 2048 /* Number of request entries. */
+#define RESPONSE_ENTRY_CNT_2100 64 /* Number of response entries.*/
+#define RESPONSE_ENTRY_CNT_2300 512 /* Number of response entries.*/
+#define RESPONSE_ENTRY_CNT_MQ 128 /* Number of response entries.*/
+
+struct req_que;
+
+/*
+ * (sd.h is not exported, hence local inclusion)
+ * Data Integrity Field tuple.
+ */
+struct sd_dif_tuple {
+ __be16 guard_tag; /* Checksum */
+ __be16 app_tag; /* Opaque storage */
+ __be32 ref_tag; /* Target LBA or indirect LBA */
+};
+
+/*
+ * SCSI Request Block
+ */
+struct srb_cmd {
+ struct scsi_cmnd *cmd; /* Linux SCSI command pkt */
+ uint32_t request_sense_length;
+ uint8_t *request_sense_ptr;
+ void *ctx;
+};
+
+/*
+ * SRB flag definitions
+ */
+#define SRB_DMA_VALID BIT_0 /* Command sent to ISP */
+#define SRB_FCP_CMND_DMA_VALID BIT_12 /* DIF: DSD List valid */
+#define SRB_CRC_CTX_DMA_VALID BIT_2 /* DIF: context DMA valid */
+#define SRB_CRC_PROT_DMA_VALID BIT_4 /* DIF: prot DMA valid */
+#define SRB_CRC_CTX_DSD_VALID BIT_5 /* DIF: dsd_list valid */
+
+/* To identify if a srb is of T10-CRC type. @sp => srb_t pointer */
+#define IS_PROT_IO(sp) (sp->flags & SRB_CRC_CTX_DSD_VALID)
+
+/*
+ * SRB extensions.
+ */
+struct srb_iocb {
+ union {
+ struct {
+ uint16_t flags;
+#define SRB_LOGIN_RETRIED BIT_0
+#define SRB_LOGIN_COND_PLOGI BIT_1
+#define SRB_LOGIN_SKIP_PRLI BIT_2
+ uint16_t data[2];
+ } logio;
+ struct {
+ /*
+ * Values for flags field below are as
+ * defined in tsk_mgmt_entry struct
+ * for control_flags field in qla_fw.h.
+ */
+ uint32_t flags;
+ uint32_t lun;
+ uint32_t data;
+ } tmf;
+ } u;
+
+ struct timer_list timer;
+ void (*timeout)(void *);
+};
+
+/* Values for srb_ctx type */
+#define SRB_LOGIN_CMD 1
+#define SRB_LOGOUT_CMD 2
+#define SRB_ELS_CMD_RPT 3
+#define SRB_ELS_CMD_HST 4
+#define SRB_CT_CMD 5
+#define SRB_ADISC_CMD 6
+#define SRB_TM_CMD 7
+#define SRB_SCSI_CMD 8
+
+typedef struct srb {
+ atomic_t ref_count;
+ struct fc_port *fcport;
+ uint32_t handle;
+ uint16_t flags;
+ uint16_t type;
+ char *name;
+ int iocbs;
+ union {
+ struct srb_iocb iocb_cmd;
+ struct fc_bsg_job *bsg_job;
+ struct srb_cmd scmd;
+ } u;
+ void (*done)(void *, void *, int);
+ void (*free)(void *, void *);
+} srb_t;
+
+#define GET_CMD_SP(sp) (sp->u.scmd.cmd)
+#define SET_CMD_SP(sp, cmd) (sp->u.scmd.cmd = cmd)
+#define GET_CMD_CTX_SP(sp) (sp->u.scmd.ctx)
+
+#define GET_CMD_SENSE_LEN(sp) \
+ (sp->u.scmd.request_sense_length)
+#define SET_CMD_SENSE_LEN(sp, len) \
+ (sp->u.scmd.request_sense_length = len)
+#define GET_CMD_SENSE_PTR(sp) \
+ (sp->u.scmd.request_sense_ptr)
+#define SET_CMD_SENSE_PTR(sp, ptr) \
+ (sp->u.scmd.request_sense_ptr = ptr)
+
+struct msg_echo_lb {
+ dma_addr_t send_dma;
+ dma_addr_t rcv_dma;
+ uint16_t req_sg_cnt;
+ uint16_t rsp_sg_cnt;
+ uint16_t options;
+ uint32_t transfer_size;
+};
+
+/*
+ * ISP I/O Register Set structure definitions.
+ */
+struct device_reg_2xxx {
+ uint16_t flash_address; /* Flash BIOS address */
+ uint16_t flash_data; /* Flash BIOS data */
+ uint16_t unused_1[1]; /* Gap */
+ uint16_t ctrl_status; /* Control/Status */
+#define CSR_FLASH_64K_BANK BIT_3 /* Flash upper 64K bank select */
+#define CSR_FLASH_ENABLE BIT_1 /* Flash BIOS Read/Write enable */
+#define CSR_ISP_SOFT_RESET BIT_0 /* ISP soft reset */
+
+ uint16_t ictrl; /* Interrupt control */
+#define ICR_EN_INT BIT_15 /* ISP enable interrupts. */
+#define ICR_EN_RISC BIT_3 /* ISP enable RISC interrupts. */
+
+ uint16_t istatus; /* Interrupt status */
+#define ISR_RISC_INT BIT_3 /* RISC interrupt */
+
+ uint16_t semaphore; /* Semaphore */
+ uint16_t nvram; /* NVRAM register. */
+#define NVR_DESELECT 0
+#define NVR_BUSY BIT_15
+#define NVR_WRT_ENABLE BIT_14 /* Write enable */
+#define NVR_PR_ENABLE BIT_13 /* Protection register enable */
+#define NVR_DATA_IN BIT_3
+#define NVR_DATA_OUT BIT_2
+#define NVR_SELECT BIT_1
+#define NVR_CLOCK BIT_0
+
+#define NVR_WAIT_CNT 20000
+
+ union {
+ struct {
+ uint16_t mailbox0;
+ uint16_t mailbox1;
+ uint16_t mailbox2;
+ uint16_t mailbox3;
+ uint16_t mailbox4;
+ uint16_t mailbox5;
+ uint16_t mailbox6;
+ uint16_t mailbox7;
+ uint16_t unused_2[59]; /* Gap */
+ } __attribute__((packed)) isp2100;
+ struct {
+ /* Request Queue */
+ uint16_t req_q_in; /* In-Pointer */
+ uint16_t req_q_out; /* Out-Pointer */
+ /* Response Queue */
+ uint16_t rsp_q_in; /* In-Pointer */
+ uint16_t rsp_q_out; /* Out-Pointer */
+
+ /* RISC to Host Status */
+ uint32_t host_status;
+#define HSR_RISC_INT BIT_15 /* RISC interrupt */
+#define HSR_RISC_PAUSED BIT_8 /* RISC Paused */
+
+ /* Host to Host Semaphore */
+ uint16_t host_semaphore;
+ uint16_t unused_3[17]; /* Gap */
+ uint16_t mailbox0;
+ uint16_t mailbox1;
+ uint16_t mailbox2;
+ uint16_t mailbox3;
+ uint16_t mailbox4;
+ uint16_t mailbox5;
+ uint16_t mailbox6;
+ uint16_t mailbox7;
+ uint16_t mailbox8;
+ uint16_t mailbox9;
+ uint16_t mailbox10;
+ uint16_t mailbox11;
+ uint16_t mailbox12;
+ uint16_t mailbox13;
+ uint16_t mailbox14;
+ uint16_t mailbox15;
+ uint16_t mailbox16;
+ uint16_t mailbox17;
+ uint16_t mailbox18;
+ uint16_t mailbox19;
+ uint16_t mailbox20;
+ uint16_t mailbox21;
+ uint16_t mailbox22;
+ uint16_t mailbox23;
+ uint16_t mailbox24;
+ uint16_t mailbox25;
+ uint16_t mailbox26;
+ uint16_t mailbox27;
+ uint16_t mailbox28;
+ uint16_t mailbox29;
+ uint16_t mailbox30;
+ uint16_t mailbox31;
+ uint16_t fb_cmd;
+ uint16_t unused_4[10]; /* Gap */
+ } __attribute__((packed)) isp2300;
+ } u;
+
+ uint16_t fpm_diag_config;
+ uint16_t unused_5[0x4]; /* Gap */
+ uint16_t risc_hw;
+ uint16_t unused_5_1; /* Gap */
+ uint16_t pcr; /* Processor Control Register. */
+ uint16_t unused_6[0x5]; /* Gap */
+ uint16_t mctr; /* Memory Configuration and Timing. */
+ uint16_t unused_7[0x3]; /* Gap */
+ uint16_t fb_cmd_2100; /* Unused on 23XX */
+ uint16_t unused_8[0x3]; /* Gap */
+ uint16_t hccr; /* Host command & control register. */
+#define HCCR_HOST_INT BIT_7 /* Host interrupt bit */
+#define HCCR_RISC_PAUSE BIT_5 /* Pause mode bit */
+ /* HCCR commands */
+#define HCCR_RESET_RISC 0x1000 /* Reset RISC */
+#define HCCR_PAUSE_RISC 0x2000 /* Pause RISC */
+#define HCCR_RELEASE_RISC 0x3000 /* Release RISC from reset. */
+#define HCCR_SET_HOST_INT 0x5000 /* Set host interrupt */
+#define HCCR_CLR_HOST_INT 0x6000 /* Clear HOST interrupt */
+#define HCCR_CLR_RISC_INT 0x7000 /* Clear RISC interrupt */
+#define HCCR_DISABLE_PARITY_PAUSE 0x4001 /* Disable parity error RISC pause. */
+#define HCCR_ENABLE_PARITY 0xA000 /* Enable PARITY interrupt */
+
+ uint16_t unused_9[5]; /* Gap */
+ uint16_t gpiod; /* GPIO Data register. */
+ uint16_t gpioe; /* GPIO Enable register. */
+#define GPIO_LED_MASK 0x00C0
+#define GPIO_LED_GREEN_OFF_AMBER_OFF 0x0000
+#define GPIO_LED_GREEN_ON_AMBER_OFF 0x0040
+#define GPIO_LED_GREEN_OFF_AMBER_ON 0x0080
+#define GPIO_LED_GREEN_ON_AMBER_ON 0x00C0
+#define GPIO_LED_ALL_OFF 0x0000
+#define GPIO_LED_RED_ON_OTHER_OFF 0x0001 /* isp2322 */
+#define GPIO_LED_RGA_ON 0x00C1 /* isp2322: red green amber */
+
+ union {
+ struct {
+ uint16_t unused_10[8]; /* Gap */
+ uint16_t mailbox8;
+ uint16_t mailbox9;
+ uint16_t mailbox10;
+ uint16_t mailbox11;
+ uint16_t mailbox12;
+ uint16_t mailbox13;
+ uint16_t mailbox14;
+ uint16_t mailbox15;
+ uint16_t mailbox16;
+ uint16_t mailbox17;
+ uint16_t mailbox18;
+ uint16_t mailbox19;
+ uint16_t mailbox20;
+ uint16_t mailbox21;
+ uint16_t mailbox22;
+ uint16_t mailbox23; /* Also probe reg. */
+ } __attribute__((packed)) isp2200;
+ } u_end;
+};
+
+struct device_reg_25xxmq {
+ uint32_t req_q_in;
+ uint32_t req_q_out;
+ uint32_t rsp_q_in;
+ uint32_t rsp_q_out;
+};
+
+typedef union {
+ struct device_reg_2xxx isp;
+ struct device_reg_24xx isp24;
+ struct device_reg_25xxmq isp25mq;
+ struct device_reg_82xx isp82;
+} device_reg_t;
+
+#define ISP_REQ_Q_IN(ha, reg) \
+ (IS_QLA2100(ha) || IS_QLA2200(ha) ? \
+ &(reg)->u.isp2100.mailbox4 : \
+ &(reg)->u.isp2300.req_q_in)
+#define ISP_REQ_Q_OUT(ha, reg) \
+ (IS_QLA2100(ha) || IS_QLA2200(ha) ? \
+ &(reg)->u.isp2100.mailbox4 : \
+ &(reg)->u.isp2300.req_q_out)
+#define ISP_RSP_Q_IN(ha, reg) \
+ (IS_QLA2100(ha) || IS_QLA2200(ha) ? \
+ &(reg)->u.isp2100.mailbox5 : \
+ &(reg)->u.isp2300.rsp_q_in)
+#define ISP_RSP_Q_OUT(ha, reg) \
+ (IS_QLA2100(ha) || IS_QLA2200(ha) ? \
+ &(reg)->u.isp2100.mailbox5 : \
+ &(reg)->u.isp2300.rsp_q_out)
+
+#define MAILBOX_REG(ha, reg, num) \
+ (IS_QLA2100(ha) || IS_QLA2200(ha) ? \
+ (num < 8 ? \
+ &(reg)->u.isp2100.mailbox0 + (num) : \
+ &(reg)->u_end.isp2200.mailbox8 + (num) - 8) : \
+ &(reg)->u.isp2300.mailbox0 + (num))
+#define RD_MAILBOX_REG(ha, reg, num) \
+ RD_REG_WORD(MAILBOX_REG(ha, reg, num))
+#define WRT_MAILBOX_REG(ha, reg, num, data) \
+ WRT_REG_WORD(MAILBOX_REG(ha, reg, num), data)
+
+#define FB_CMD_REG(ha, reg) \
+ (IS_QLA2100(ha) || IS_QLA2200(ha) ? \
+ &(reg)->fb_cmd_2100 : \
+ &(reg)->u.isp2300.fb_cmd)
+#define RD_FB_CMD_REG(ha, reg) \
+ RD_REG_WORD(FB_CMD_REG(ha, reg))
+#define WRT_FB_CMD_REG(ha, reg, data) \
+ WRT_REG_WORD(FB_CMD_REG(ha, reg), data)
+
+typedef struct {
+ uint32_t out_mb; /* outbound from driver */
+ uint32_t in_mb; /* Incoming from RISC */
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+ long buf_size;
+ void *bufp;
+ uint32_t tov;
+ uint8_t flags;
+#define MBX_DMA_IN BIT_0
+#define MBX_DMA_OUT BIT_1
+#define IOCTL_CMD BIT_2
+} mbx_cmd_t;
+
+#define MBX_TOV_SECONDS 30
+
+/*
+ * ISP product identification definitions in mailboxes after reset.
+ */
+#define PROD_ID_1 0x4953
+#define PROD_ID_2 0x0000
+#define PROD_ID_2a 0x5020
+#define PROD_ID_3 0x2020
+
+/*
+ * ISP mailbox Self-Test status codes
+ */
+#define MBS_FRM_ALIVE 0 /* Firmware Alive. */
+#define MBS_CHKSUM_ERR 1 /* Checksum Error. */
+#define MBS_BUSY 4 /* Busy. */
+
+/*
+ * ISP mailbox command complete status codes
+ */
+#define MBS_COMMAND_COMPLETE 0x4000
+#define MBS_INVALID_COMMAND 0x4001
+#define MBS_HOST_INTERFACE_ERROR 0x4002
+#define MBS_TEST_FAILED 0x4003
+#define MBS_COMMAND_ERROR 0x4005
+#define MBS_COMMAND_PARAMETER_ERROR 0x4006
+#define MBS_PORT_ID_USED 0x4007
+#define MBS_LOOP_ID_USED 0x4008
+#define MBS_ALL_IDS_IN_USE 0x4009
+#define MBS_NOT_LOGGED_IN 0x400A
+#define MBS_LINK_DOWN_ERROR 0x400B
+#define MBS_DIAG_ECHO_TEST_ERROR 0x400C
+
+/*
+ * ISP mailbox asynchronous event status codes
+ */
+#define MBA_ASYNC_EVENT 0x8000 /* Asynchronous event. */
+#define MBA_RESET 0x8001 /* Reset Detected. */
+#define MBA_SYSTEM_ERR 0x8002 /* System Error. */
+#define MBA_REQ_TRANSFER_ERR 0x8003 /* Request Transfer Error. */
+#define MBA_RSP_TRANSFER_ERR 0x8004 /* Response Transfer Error. */
+#define MBA_WAKEUP_THRES 0x8005 /* Request Queue Wake-up. */
+#define MBA_LIP_OCCURRED 0x8010 /* Loop Initialization Procedure */
+ /* occurred. */
+#define MBA_LOOP_UP 0x8011 /* FC Loop UP. */
+#define MBA_LOOP_DOWN 0x8012 /* FC Loop Down. */
+#define MBA_LIP_RESET 0x8013 /* LIP reset occurred. */
+#define MBA_PORT_UPDATE 0x8014 /* Port Database update. */
+#define MBA_RSCN_UPDATE 0x8015 /* Register State Chg Notification. */
+#define MBA_LIP_F8 0x8016 /* Received a LIP F8. */
+#define MBA_LOOP_INIT_ERR 0x8017 /* Loop Initialization Error. */
+#define MBA_FABRIC_AUTH_REQ 0x801b /* Fabric Authentication Required. */
+#define MBA_SCSI_COMPLETION 0x8020 /* SCSI Command Complete. */
+#define MBA_CTIO_COMPLETION 0x8021 /* CTIO Complete. */
+#define MBA_IP_COMPLETION 0x8022 /* IP Transmit Command Complete. */
+#define MBA_IP_RECEIVE 0x8023 /* IP Received. */
+#define MBA_IP_BROADCAST 0x8024 /* IP Broadcast Received. */
+#define MBA_IP_LOW_WATER_MARK 0x8025 /* IP Low Water Mark reached. */
+#define MBA_IP_RCV_BUFFER_EMPTY 0x8026 /* IP receive buffer queue empty. */
+#define MBA_IP_HDR_DATA_SPLIT 0x8027 /* IP header/data splitting feature */
+ /* used. */
+#define MBA_TRACE_NOTIFICATION 0x8028 /* Trace/Diagnostic notification. */
+#define MBA_POINT_TO_POINT 0x8030 /* Point to point mode. */
+#define MBA_CMPLT_1_16BIT 0x8031 /* Completion 1 16bit IOSB. */
+#define MBA_CMPLT_2_16BIT 0x8032 /* Completion 2 16bit IOSB. */
+#define MBA_CMPLT_3_16BIT 0x8033 /* Completion 3 16bit IOSB. */
+#define MBA_CMPLT_4_16BIT 0x8034 /* Completion 4 16bit IOSB. */
+#define MBA_CMPLT_5_16BIT 0x8035 /* Completion 5 16bit IOSB. */
+#define MBA_CHG_IN_CONNECTION 0x8036 /* Change in connection mode. */
+#define MBA_RIO_RESPONSE 0x8040 /* RIO response queue update. */
+#define MBA_ZIO_RESPONSE 0x8040 /* ZIO response queue update. */
+#define MBA_CMPLT_2_32BIT 0x8042 /* Completion 2 32bit IOSB. */
+#define MBA_BYPASS_NOTIFICATION 0x8043 /* Auto bypass notification. */
+#define MBA_DISCARD_RND_FRAME 0x8048 /* discard RND frame due to error. */
+#define MBA_REJECTED_FCP_CMD 0x8049 /* rejected FCP_CMD. */
+
+/* ISP mailbox loopback echo diagnostic error code */
+#define MBS_LB_RESET 0x17
+/*
+ * Firmware options 1, 2, 3.
+ */
+#define FO1_AE_ON_LIPF8 BIT_0
+#define FO1_AE_ALL_LIP_RESET BIT_1
+#define FO1_CTIO_RETRY BIT_3
+#define FO1_DISABLE_LIP_F7_SW BIT_4
+#define FO1_DISABLE_100MS_LOS_WAIT BIT_5
+#define FO1_DISABLE_GPIO6_7 BIT_6 /* LED bits */
+#define FO1_AE_ON_LOOP_INIT_ERR BIT_7
+#define FO1_SET_EMPHASIS_SWING BIT_8
+#define FO1_AE_AUTO_BYPASS BIT_9
+#define FO1_ENABLE_PURE_IOCB BIT_10
+#define FO1_AE_PLOGI_RJT BIT_11
+#define FO1_ENABLE_ABORT_SEQUENCE BIT_12
+#define FO1_AE_QUEUE_FULL BIT_13
+
+#define FO2_ENABLE_ATIO_TYPE_3 BIT_0
+#define FO2_REV_LOOPBACK BIT_1
+
+#define FO3_ENABLE_EMERG_IOCB BIT_0
+#define FO3_AE_RND_ERROR BIT_1
+
+/* 24XX additional firmware options */
+#define ADD_FO_COUNT 3
+#define ADD_FO1_DISABLE_GPIO_LED_CTRL BIT_6 /* LED bits */
+#define ADD_FO1_ENABLE_PUREX_IOCB BIT_10
+
+#define ADD_FO2_ENABLE_SEL_CLS2 BIT_5
+
+#define ADD_FO3_NO_ABT_ON_LINK_DOWN BIT_14
+
+/*
+ * ISP mailbox commands
+ */
+#define MBC_LOAD_RAM 1 /* Load RAM. */
+#define MBC_EXECUTE_FIRMWARE 2 /* Execute firmware. */
+#define MBC_WRITE_RAM_WORD 4 /* Write RAM word. */
+#define MBC_READ_RAM_WORD 5 /* Read RAM word. */
+#define MBC_MAILBOX_REGISTER_TEST 6 /* Wrap incoming mailboxes */
+#define MBC_VERIFY_CHECKSUM 7 /* Verify checksum. */
+#define MBC_GET_FIRMWARE_VERSION 8 /* Get firmware revision. */
+#define MBC_LOAD_RISC_RAM 9 /* Load RAM command. */
+#define MBC_DUMP_RISC_RAM 0xa /* Dump RAM command. */
+#define MBC_LOAD_RISC_RAM_EXTENDED 0xb /* Load RAM extended. */
+#define MBC_DUMP_RISC_RAM_EXTENDED 0xc /* Dump RAM extended. */
+#define MBC_WRITE_RAM_WORD_EXTENDED 0xd /* Write RAM word extended */
+#define MBC_READ_RAM_EXTENDED 0xf /* Read RAM extended. */
+#define MBC_IOCB_COMMAND 0x12 /* Execute IOCB command. */
+#define MBC_STOP_FIRMWARE 0x14 /* Stop firmware. */
+#define MBC_ABORT_COMMAND 0x15 /* Abort IOCB command. */
+#define MBC_ABORT_DEVICE 0x16 /* Abort device (ID/LUN). */
+#define MBC_ABORT_TARGET 0x17 /* Abort target (ID). */
+#define MBC_RESET 0x18 /* Reset. */
+#define MBC_GET_ADAPTER_LOOP_ID 0x20 /* Get loop id of ISP2200. */
+#define MBC_GET_RETRY_COUNT 0x22 /* Get f/w retry cnt/delay. */
+#define MBC_DISABLE_VI 0x24 /* Disable VI operation. */
+#define MBC_ENABLE_VI 0x25 /* Enable VI operation. */
+#define MBC_GET_FIRMWARE_OPTION 0x28 /* Get Firmware Options. */
+#define MBC_SET_FIRMWARE_OPTION 0x38 /* Set Firmware Options. */
+#define MBC_LOOP_PORT_BYPASS 0x40 /* Loop Port Bypass. */
+#define MBC_LOOP_PORT_ENABLE 0x41 /* Loop Port Enable. */
+#define MBC_GET_RESOURCE_COUNTS 0x42 /* Get Resource Counts. */
+#define MBC_NON_PARTICIPATE 0x43 /* Non-Participating Mode. */
+#define MBC_DIAGNOSTIC_ECHO 0x44 /* Diagnostic echo. */
+#define MBC_DIAGNOSTIC_LOOP_BACK 0x45 /* Diagnostic loop back. */
+#define MBC_ONLINE_SELF_TEST 0x46 /* Online self-test. */
+#define MBC_ENHANCED_GET_PORT_DATABASE 0x47 /* Get port database + login */
+#define MBC_CONFIGURE_VF 0x4b /* Configure VFs */
+#define MBC_RESET_LINK_STATUS 0x52 /* Reset Link Error Status */
+#define MBC_IOCB_COMMAND_A64 0x54 /* Execute IOCB command (64) */
+#define MBC_PORT_LOGOUT 0x56 /* Port Logout request */
+#define MBC_SEND_RNID_ELS 0x57 /* Send RNID ELS request */
+#define MBC_SET_RNID_PARAMS 0x59 /* Set RNID parameters */
+#define MBC_GET_RNID_PARAMS 0x5a /* Data Rate */
+#define MBC_DATA_RATE 0x5d /* Get RNID parameters */
+#define MBC_INITIALIZE_FIRMWARE 0x60 /* Initialize firmware */
+#define MBC_INITIATE_LIP 0x62 /* Initiate Loop */
+ /* Initialization Procedure */
+#define MBC_GET_FC_AL_POSITION_MAP 0x63 /* Get FC_AL Position Map. */
+#define MBC_GET_PORT_DATABASE 0x64 /* Get Port Database. */
+#define MBC_CLEAR_ACA 0x65 /* Clear ACA. */
+#define MBC_TARGET_RESET 0x66 /* Target Reset. */
+#define MBC_CLEAR_TASK_SET 0x67 /* Clear Task Set. */
+#define MBC_ABORT_TASK_SET 0x68 /* Abort Task Set. */
+#define MBC_GET_FIRMWARE_STATE 0x69 /* Get firmware state. */
+#define MBC_GET_PORT_NAME 0x6a /* Get port name. */
+#define MBC_GET_LINK_STATUS 0x6b /* Get port link status. */
+#define MBC_LIP_RESET 0x6c /* LIP reset. */
+#define MBC_SEND_SNS_COMMAND 0x6e /* Send Simple Name Server */
+ /* commandd. */
+#define MBC_LOGIN_FABRIC_PORT 0x6f /* Login fabric port. */
+#define MBC_SEND_CHANGE_REQUEST 0x70 /* Send Change Request. */
+#define MBC_LOGOUT_FABRIC_PORT 0x71 /* Logout fabric port. */
+#define MBC_LIP_FULL_LOGIN 0x72 /* Full login LIP. */
+#define MBC_LOGIN_LOOP_PORT 0x74 /* Login Loop Port. */
+#define MBC_PORT_NODE_NAME_LIST 0x75 /* Get port/node name list. */
+#define MBC_INITIALIZE_RECEIVE_QUEUE 0x77 /* Initialize receive queue */
+#define MBC_UNLOAD_IP 0x79 /* Shutdown IP */
+#define MBC_GET_ID_LIST 0x7C /* Get Port ID list. */
+#define MBC_SEND_LFA_COMMAND 0x7D /* Send Loop Fabric Address */
+#define MBC_LUN_RESET 0x7E /* Send LUN reset */
+
+/*
+ * ISP24xx mailbox commands
+ */
+#define MBC_SERDES_PARAMS 0x10 /* Serdes Tx Parameters. */
+#define MBC_GET_IOCB_STATUS 0x12 /* Get IOCB status command. */
+#define MBC_PORT_PARAMS 0x1A /* Port iDMA Parameters. */
+#define MBC_GET_TIMEOUT_PARAMS 0x22 /* Get FW timeouts. */
+#define MBC_TRACE_CONTROL 0x27 /* Trace control command. */
+#define MBC_GEN_SYSTEM_ERROR 0x2a /* Generate System Error. */
+#define MBC_WRITE_SFP 0x30 /* Write SFP Data. */
+#define MBC_READ_SFP 0x31 /* Read SFP Data. */
+#define MBC_SET_TIMEOUT_PARAMS 0x32 /* Set FW timeouts. */
+#define MBC_MID_INITIALIZE_FIRMWARE 0x48 /* MID Initialize firmware. */
+#define MBC_MID_GET_VP_DATABASE 0x49 /* MID Get VP Database. */
+#define MBC_MID_GET_VP_ENTRY 0x4a /* MID Get VP Entry. */
+#define MBC_HOST_MEMORY_COPY 0x53 /* Host Memory Copy. */
+#define MBC_SEND_RNFT_ELS 0x5e /* Send RNFT ELS request */
+#define MBC_GET_LINK_PRIV_STATS 0x6d /* Get link & private data. */
+#define MBC_SET_VENDOR_ID 0x76 /* Set Vendor ID. */
+#define MBC_SET_PORT_CONFIG 0x122 /* Set port configuration */
+#define MBC_GET_PORT_CONFIG 0x123 /* Get port configuration */
+
+/*
+ * ISP81xx mailbox commands
+ */
+#define MBC_WRITE_MPI_REGISTER 0x01 /* Write MPI Register. */
+
+/* Firmware return data sizes */
+#define FCAL_MAP_SIZE 128
+
+/* Mailbox bit definitions for out_mb and in_mb */
+#define MBX_31 BIT_31
+#define MBX_30 BIT_30
+#define MBX_29 BIT_29
+#define MBX_28 BIT_28
+#define MBX_27 BIT_27
+#define MBX_26 BIT_26
+#define MBX_25 BIT_25
+#define MBX_24 BIT_24
+#define MBX_23 BIT_23
+#define MBX_22 BIT_22
+#define MBX_21 BIT_21
+#define MBX_20 BIT_20
+#define MBX_19 BIT_19
+#define MBX_18 BIT_18
+#define MBX_17 BIT_17
+#define MBX_16 BIT_16
+#define MBX_15 BIT_15
+#define MBX_14 BIT_14
+#define MBX_13 BIT_13
+#define MBX_12 BIT_12
+#define MBX_11 BIT_11
+#define MBX_10 BIT_10
+#define MBX_9 BIT_9
+#define MBX_8 BIT_8
+#define MBX_7 BIT_7
+#define MBX_6 BIT_6
+#define MBX_5 BIT_5
+#define MBX_4 BIT_4
+#define MBX_3 BIT_3
+#define MBX_2 BIT_2
+#define MBX_1 BIT_1
+#define MBX_0 BIT_0
+
+/*
+ * Firmware state codes from get firmware state mailbox command
+ */
+#define FSTATE_CONFIG_WAIT 0
+#define FSTATE_WAIT_AL_PA 1
+#define FSTATE_WAIT_LOGIN 2
+#define FSTATE_READY 3
+#define FSTATE_LOSS_OF_SYNC 4
+#define FSTATE_ERROR 5
+#define FSTATE_REINIT 6
+#define FSTATE_NON_PART 7
+
+#define FSTATE_CONFIG_CORRECT 0
+#define FSTATE_P2P_RCV_LIP 1
+#define FSTATE_P2P_CHOOSE_LOOP 2
+#define FSTATE_P2P_RCV_UNIDEN_LIP 3
+#define FSTATE_FATAL_ERROR 4
+#define FSTATE_LOOP_BACK_CONN 5
+
+/*
+ * Port Database structure definition
+ * Little endian except where noted.
+ */
+#define PORT_DATABASE_SIZE 128 /* bytes */
+typedef struct {
+ uint8_t options;
+ uint8_t control;
+ uint8_t master_state;
+ uint8_t slave_state;
+ uint8_t reserved[2];
+ uint8_t hard_address;
+ uint8_t reserved_1;
+ uint8_t port_id[4];
+ uint8_t node_name[WWN_SIZE];
+ uint8_t port_name[WWN_SIZE];
+ uint16_t execution_throttle;
+ uint16_t execution_count;
+ uint8_t reset_count;
+ uint8_t reserved_2;
+ uint16_t resource_allocation;
+ uint16_t current_allocation;
+ uint16_t queue_head;
+ uint16_t queue_tail;
+ uint16_t transmit_execution_list_next;
+ uint16_t transmit_execution_list_previous;
+ uint16_t common_features;
+ uint16_t total_concurrent_sequences;
+ uint16_t RO_by_information_category;
+ uint8_t recipient;
+ uint8_t initiator;
+ uint16_t receive_data_size;
+ uint16_t concurrent_sequences;
+ uint16_t open_sequences_per_exchange;
+ uint16_t lun_abort_flags;
+ uint16_t lun_stop_flags;
+ uint16_t stop_queue_head;
+ uint16_t stop_queue_tail;
+ uint16_t port_retry_timer;
+ uint16_t next_sequence_id;
+ uint16_t frame_count;
+ uint16_t PRLI_payload_length;
+ uint8_t prli_svc_param_word_0[2]; /* Big endian */
+ /* Bits 15-0 of word 0 */
+ uint8_t prli_svc_param_word_3[2]; /* Big endian */
+ /* Bits 15-0 of word 3 */
+ uint16_t loop_id;
+ uint16_t extended_lun_info_list_pointer;
+ uint16_t extended_lun_stop_list_pointer;
+} port_database_t;
+
+/*
+ * Port database slave/master states
+ */
+#define PD_STATE_DISCOVERY 0
+#define PD_STATE_WAIT_DISCOVERY_ACK 1
+#define PD_STATE_PORT_LOGIN 2
+#define PD_STATE_WAIT_PORT_LOGIN_ACK 3
+#define PD_STATE_PROCESS_LOGIN 4
+#define PD_STATE_WAIT_PROCESS_LOGIN_ACK 5
+#define PD_STATE_PORT_LOGGED_IN 6
+#define PD_STATE_PORT_UNAVAILABLE 7
+#define PD_STATE_PROCESS_LOGOUT 8
+#define PD_STATE_WAIT_PROCESS_LOGOUT_ACK 9
+#define PD_STATE_PORT_LOGOUT 10
+#define PD_STATE_WAIT_PORT_LOGOUT_ACK 11
+
+
+#define QLA_ZIO_MODE_6 (BIT_2 | BIT_1)
+#define QLA_ZIO_DISABLED 0
+#define QLA_ZIO_DEFAULT_TIMER 2
+
+/*
+ * ISP Initialization Control Block.
+ * Little endian except where noted.
+ */
+#define ICB_VERSION 1
+typedef struct {
+ uint8_t version;
+ uint8_t reserved_1;
+
+ /*
+ * LSB BIT 0 = Enable Hard Loop Id
+ * LSB BIT 1 = Enable Fairness
+ * LSB BIT 2 = Enable Full-Duplex
+ * LSB BIT 3 = Enable Fast Posting
+ * LSB BIT 4 = Enable Target Mode
+ * LSB BIT 5 = Disable Initiator Mode
+ * LSB BIT 6 = Enable ADISC
+ * LSB BIT 7 = Enable Target Inquiry Data
+ *
+ * MSB BIT 0 = Enable PDBC Notify
+ * MSB BIT 1 = Non Participating LIP
+ * MSB BIT 2 = Descending Loop ID Search
+ * MSB BIT 3 = Acquire Loop ID in LIPA
+ * MSB BIT 4 = Stop PortQ on Full Status
+ * MSB BIT 5 = Full Login after LIP
+ * MSB BIT 6 = Node Name Option
+ * MSB BIT 7 = Ext IFWCB enable bit
+ */
+ uint8_t firmware_options[2];
+
+ uint16_t frame_payload_size;
+ uint16_t max_iocb_allocation;
+ uint16_t execution_throttle;
+ uint8_t retry_count;
+ uint8_t retry_delay; /* unused */
+ uint8_t port_name[WWN_SIZE]; /* Big endian. */
+ uint16_t hard_address;
+ uint8_t inquiry_data;
+ uint8_t login_timeout;
+ uint8_t node_name[WWN_SIZE]; /* Big endian. */
+
+ uint16_t request_q_outpointer;
+ uint16_t response_q_inpointer;
+ uint16_t request_q_length;
+ uint16_t response_q_length;
+ uint32_t request_q_address[2];
+ uint32_t response_q_address[2];
+
+ uint16_t lun_enables;
+ uint8_t command_resource_count;
+ uint8_t immediate_notify_resource_count;
+ uint16_t timeout;
+ uint8_t reserved_2[2];
+
+ /*
+ * LSB BIT 0 = Timer Operation mode bit 0
+ * LSB BIT 1 = Timer Operation mode bit 1
+ * LSB BIT 2 = Timer Operation mode bit 2
+ * LSB BIT 3 = Timer Operation mode bit 3
+ * LSB BIT 4 = Init Config Mode bit 0
+ * LSB BIT 5 = Init Config Mode bit 1
+ * LSB BIT 6 = Init Config Mode bit 2
+ * LSB BIT 7 = Enable Non part on LIHA failure
+ *
+ * MSB BIT 0 = Enable class 2
+ * MSB BIT 1 = Enable ACK0
+ * MSB BIT 2 =
+ * MSB BIT 3 =
+ * MSB BIT 4 = FC Tape Enable
+ * MSB BIT 5 = Enable FC Confirm
+ * MSB BIT 6 = Enable command queuing in target mode
+ * MSB BIT 7 = No Logo On Link Down
+ */
+ uint8_t add_firmware_options[2];
+
+ uint8_t response_accumulation_timer;
+ uint8_t interrupt_delay_timer;
+
+ /*
+ * LSB BIT 0 = Enable Read xfr_rdy
+ * LSB BIT 1 = Soft ID only
+ * LSB BIT 2 =
+ * LSB BIT 3 =
+ * LSB BIT 4 = FCP RSP Payload [0]
+ * LSB BIT 5 = FCP RSP Payload [1] / Sbus enable - 2200
+ * LSB BIT 6 = Enable Out-of-Order frame handling
+ * LSB BIT 7 = Disable Automatic PLOGI on Local Loop
+ *
+ * MSB BIT 0 = Sbus enable - 2300
+ * MSB BIT 1 =
+ * MSB BIT 2 =
+ * MSB BIT 3 =
+ * MSB BIT 4 = LED mode
+ * MSB BIT 5 = enable 50 ohm termination
+ * MSB BIT 6 = Data Rate (2300 only)
+ * MSB BIT 7 = Data Rate (2300 only)
+ */
+ uint8_t special_options[2];
+
+ uint8_t reserved_3[26];
+} init_cb_t;
+
+/*
+ * Get Link Status mailbox command return buffer.
+ */
+#define GLSO_SEND_RPS BIT_0
+#define GLSO_USE_DID BIT_3
+
+struct link_statistics {
+ uint32_t link_fail_cnt;
+ uint32_t loss_sync_cnt;
+ uint32_t loss_sig_cnt;
+ uint32_t prim_seq_err_cnt;
+ uint32_t inval_xmit_word_cnt;
+ uint32_t inval_crc_cnt;
+ uint32_t lip_cnt;
+ uint32_t unused1[0x1a];
+ uint32_t tx_frames;
+ uint32_t rx_frames;
+ uint32_t dumped_frames;
+ uint32_t unused2[2];
+ uint32_t nos_rcvd;
+};
+
+/*
+ * NVRAM Command values.
+ */
+#define NV_START_BIT BIT_2
+#define NV_WRITE_OP (BIT_26+BIT_24)
+#define NV_READ_OP (BIT_26+BIT_25)
+#define NV_ERASE_OP (BIT_26+BIT_25+BIT_24)
+#define NV_MASK_OP (BIT_26+BIT_25+BIT_24)
+#define NV_DELAY_COUNT 10
+
+/*
+ * QLogic ISP2100, ISP2200 and ISP2300 NVRAM structure definition.
+ */
+typedef struct {
+ /*
+ * NVRAM header
+ */
+ uint8_t id[4];
+ uint8_t nvram_version;
+ uint8_t reserved_0;
+
+ /*
+ * NVRAM RISC parameter block
+ */
+ uint8_t parameter_block_version;
+ uint8_t reserved_1;
+
+ /*
+ * LSB BIT 0 = Enable Hard Loop Id
+ * LSB BIT 1 = Enable Fairness
+ * LSB BIT 2 = Enable Full-Duplex
+ * LSB BIT 3 = Enable Fast Posting
+ * LSB BIT 4 = Enable Target Mode
+ * LSB BIT 5 = Disable Initiator Mode
+ * LSB BIT 6 = Enable ADISC
+ * LSB BIT 7 = Enable Target Inquiry Data
+ *
+ * MSB BIT 0 = Enable PDBC Notify
+ * MSB BIT 1 = Non Participating LIP
+ * MSB BIT 2 = Descending Loop ID Search
+ * MSB BIT 3 = Acquire Loop ID in LIPA
+ * MSB BIT 4 = Stop PortQ on Full Status
+ * MSB BIT 5 = Full Login after LIP
+ * MSB BIT 6 = Node Name Option
+ * MSB BIT 7 = Ext IFWCB enable bit
+ */
+ uint8_t firmware_options[2];
+
+ uint16_t frame_payload_size;
+ uint16_t max_iocb_allocation;
+ uint16_t execution_throttle;
+ uint8_t retry_count;
+ uint8_t retry_delay; /* unused */
+ uint8_t port_name[WWN_SIZE]; /* Big endian. */
+ uint16_t hard_address;
+ uint8_t inquiry_data;
+ uint8_t login_timeout;
+ uint8_t node_name[WWN_SIZE]; /* Big endian. */
+
+ /*
+ * LSB BIT 0 = Timer Operation mode bit 0
+ * LSB BIT 1 = Timer Operation mode bit 1
+ * LSB BIT 2 = Timer Operation mode bit 2
+ * LSB BIT 3 = Timer Operation mode bit 3
+ * LSB BIT 4 = Init Config Mode bit 0
+ * LSB BIT 5 = Init Config Mode bit 1
+ * LSB BIT 6 = Init Config Mode bit 2
+ * LSB BIT 7 = Enable Non part on LIHA failure
+ *
+ * MSB BIT 0 = Enable class 2
+ * MSB BIT 1 = Enable ACK0
+ * MSB BIT 2 =
+ * MSB BIT 3 =
+ * MSB BIT 4 = FC Tape Enable
+ * MSB BIT 5 = Enable FC Confirm
+ * MSB BIT 6 = Enable command queuing in target mode
+ * MSB BIT 7 = No Logo On Link Down
+ */
+ uint8_t add_firmware_options[2];
+
+ uint8_t response_accumulation_timer;
+ uint8_t interrupt_delay_timer;
+
+ /*
+ * LSB BIT 0 = Enable Read xfr_rdy
+ * LSB BIT 1 = Soft ID only
+ * LSB BIT 2 =
+ * LSB BIT 3 =
+ * LSB BIT 4 = FCP RSP Payload [0]
+ * LSB BIT 5 = FCP RSP Payload [1] / Sbus enable - 2200
+ * LSB BIT 6 = Enable Out-of-Order frame handling
+ * LSB BIT 7 = Disable Automatic PLOGI on Local Loop
+ *
+ * MSB BIT 0 = Sbus enable - 2300
+ * MSB BIT 1 =
+ * MSB BIT 2 =
+ * MSB BIT 3 =
+ * MSB BIT 4 = LED mode
+ * MSB BIT 5 = enable 50 ohm termination
+ * MSB BIT 6 = Data Rate (2300 only)
+ * MSB BIT 7 = Data Rate (2300 only)
+ */
+ uint8_t special_options[2];
+
+ /* Reserved for expanded RISC parameter block */
+ uint8_t reserved_2[22];
+
+ /*
+ * LSB BIT 0 = Tx Sensitivity 1G bit 0
+ * LSB BIT 1 = Tx Sensitivity 1G bit 1
+ * LSB BIT 2 = Tx Sensitivity 1G bit 2
+ * LSB BIT 3 = Tx Sensitivity 1G bit 3
+ * LSB BIT 4 = Rx Sensitivity 1G bit 0
+ * LSB BIT 5 = Rx Sensitivity 1G bit 1
+ * LSB BIT 6 = Rx Sensitivity 1G bit 2
+ * LSB BIT 7 = Rx Sensitivity 1G bit 3
+ *
+ * MSB BIT 0 = Tx Sensitivity 2G bit 0
+ * MSB BIT 1 = Tx Sensitivity 2G bit 1
+ * MSB BIT 2 = Tx Sensitivity 2G bit 2
+ * MSB BIT 3 = Tx Sensitivity 2G bit 3
+ * MSB BIT 4 = Rx Sensitivity 2G bit 0
+ * MSB BIT 5 = Rx Sensitivity 2G bit 1
+ * MSB BIT 6 = Rx Sensitivity 2G bit 2
+ * MSB BIT 7 = Rx Sensitivity 2G bit 3
+ *
+ * LSB BIT 0 = Output Swing 1G bit 0
+ * LSB BIT 1 = Output Swing 1G bit 1
+ * LSB BIT 2 = Output Swing 1G bit 2
+ * LSB BIT 3 = Output Emphasis 1G bit 0
+ * LSB BIT 4 = Output Emphasis 1G bit 1
+ * LSB BIT 5 = Output Swing 2G bit 0
+ * LSB BIT 6 = Output Swing 2G bit 1
+ * LSB BIT 7 = Output Swing 2G bit 2
+ *
+ * MSB BIT 0 = Output Emphasis 2G bit 0
+ * MSB BIT 1 = Output Emphasis 2G bit 1
+ * MSB BIT 2 = Output Enable
+ * MSB BIT 3 =
+ * MSB BIT 4 =
+ * MSB BIT 5 =
+ * MSB BIT 6 =
+ * MSB BIT 7 =
+ */
+ uint8_t seriallink_options[4];
+
+ /*
+ * NVRAM host parameter block
+ *
+ * LSB BIT 0 = Enable spinup delay
+ * LSB BIT 1 = Disable BIOS
+ * LSB BIT 2 = Enable Memory Map BIOS
+ * LSB BIT 3 = Enable Selectable Boot
+ * LSB BIT 4 = Disable RISC code load
+ * LSB BIT 5 = Set cache line size 1
+ * LSB BIT 6 = PCI Parity Disable
+ * LSB BIT 7 = Enable extended logging
+ *
+ * MSB BIT 0 = Enable 64bit addressing
+ * MSB BIT 1 = Enable lip reset
+ * MSB BIT 2 = Enable lip full login
+ * MSB BIT 3 = Enable target reset
+ * MSB BIT 4 = Enable database storage
+ * MSB BIT 5 = Enable cache flush read
+ * MSB BIT 6 = Enable database load
+ * MSB BIT 7 = Enable alternate WWN
+ */
+ uint8_t host_p[2];
+
+ uint8_t boot_node_name[WWN_SIZE];
+ uint8_t boot_lun_number;
+ uint8_t reset_delay;
+ uint8_t port_down_retry_count;
+ uint8_t boot_id_number;
+ uint16_t max_luns_per_target;
+ uint8_t fcode_boot_port_name[WWN_SIZE];
+ uint8_t alternate_port_name[WWN_SIZE];
+ uint8_t alternate_node_name[WWN_SIZE];
+
+ /*
+ * BIT 0 = Selective Login
+ * BIT 1 = Alt-Boot Enable
+ * BIT 2 =
+ * BIT 3 = Boot Order List
+ * BIT 4 =
+ * BIT 5 = Selective LUN
+ * BIT 6 =
+ * BIT 7 = unused
+ */
+ uint8_t efi_parameters;
+
+ uint8_t link_down_timeout;
+
+ uint8_t adapter_id[16];
+
+ uint8_t alt1_boot_node_name[WWN_SIZE];
+ uint16_t alt1_boot_lun_number;
+ uint8_t alt2_boot_node_name[WWN_SIZE];
+ uint16_t alt2_boot_lun_number;
+ uint8_t alt3_boot_node_name[WWN_SIZE];
+ uint16_t alt3_boot_lun_number;
+ uint8_t alt4_boot_node_name[WWN_SIZE];
+ uint16_t alt4_boot_lun_number;
+ uint8_t alt5_boot_node_name[WWN_SIZE];
+ uint16_t alt5_boot_lun_number;
+ uint8_t alt6_boot_node_name[WWN_SIZE];
+ uint16_t alt6_boot_lun_number;
+ uint8_t alt7_boot_node_name[WWN_SIZE];
+ uint16_t alt7_boot_lun_number;
+
+ uint8_t reserved_3[2];
+
+ /* Offset 200-215 : Model Number */
+ uint8_t model_number[16];
+
+ /* OEM related items */
+ uint8_t oem_specific[16];
+
+ /*
+ * NVRAM Adapter Features offset 232-239
+ *
+ * LSB BIT 0 = External GBIC
+ * LSB BIT 1 = Risc RAM parity
+ * LSB BIT 2 = Buffer Plus Module
+ * LSB BIT 3 = Multi Chip Adapter
+ * LSB BIT 4 = Internal connector
+ * LSB BIT 5 =
+ * LSB BIT 6 =
+ * LSB BIT 7 =
+ *
+ * MSB BIT 0 =
+ * MSB BIT 1 =
+ * MSB BIT 2 =
+ * MSB BIT 3 =
+ * MSB BIT 4 =
+ * MSB BIT 5 =
+ * MSB BIT 6 =
+ * MSB BIT 7 =
+ */
+ uint8_t adapter_features[2];
+
+ uint8_t reserved_4[16];
+
+ /* Subsystem vendor ID for ISP2200 */
+ uint16_t subsystem_vendor_id_2200;
+
+ /* Subsystem device ID for ISP2200 */
+ uint16_t subsystem_device_id_2200;
+
+ uint8_t reserved_5;
+ uint8_t checksum;
+} nvram_t;
+
+/*
+ * ISP queue - response queue entry definition.
+ */
+typedef struct {
+ uint8_t data[60];
+ uint32_t signature;
+#define RESPONSE_PROCESSED 0xDEADDEAD /* Signature */
+} response_t;
+
+typedef union {
+ uint16_t extended;
+ struct {
+ uint8_t reserved;
+ uint8_t standard;
+ } id;
+} target_id_t;
+
+#define SET_TARGET_ID(ha, to, from) \
+do { \
+ if (HAS_EXTENDED_IDS(ha)) \
+ to.extended = cpu_to_le16(from); \
+ else \
+ to.id.standard = (uint8_t)from; \
+} while (0)
+
+/*
+ * ISP queue - command entry structure definition.
+ */
+#define COMMAND_TYPE 0x11 /* Command entry */
+typedef struct {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t handle; /* System handle. */
+ target_id_t target; /* SCSI ID */
+ uint16_t lun; /* SCSI LUN */
+ uint16_t control_flags; /* Control flags. */
+#define CF_WRITE BIT_6
+#define CF_READ BIT_5
+#define CF_SIMPLE_TAG BIT_3
+#define CF_ORDERED_TAG BIT_2
+#define CF_HEAD_TAG BIT_1
+ uint16_t reserved_1;
+ uint16_t timeout; /* Command timeout. */
+ uint16_t dseg_count; /* Data segment count. */
+ uint8_t scsi_cdb[MAX_CMDSZ]; /* SCSI command words. */
+ uint32_t byte_count; /* Total byte count. */
+ uint32_t dseg_0_address; /* Data segment 0 address. */
+ uint32_t dseg_0_length; /* Data segment 0 length. */
+ uint32_t dseg_1_address; /* Data segment 1 address. */
+ uint32_t dseg_1_length; /* Data segment 1 length. */
+ uint32_t dseg_2_address; /* Data segment 2 address. */
+ uint32_t dseg_2_length; /* Data segment 2 length. */
+} cmd_entry_t;
+
+/*
+ * ISP queue - 64-Bit addressing, command entry structure definition.
+ */
+#define COMMAND_A64_TYPE 0x19 /* Command A64 entry */
+typedef struct {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t handle; /* System handle. */
+ target_id_t target; /* SCSI ID */
+ uint16_t lun; /* SCSI LUN */
+ uint16_t control_flags; /* Control flags. */
+ uint16_t reserved_1;
+ uint16_t timeout; /* Command timeout. */
+ uint16_t dseg_count; /* Data segment count. */
+ uint8_t scsi_cdb[MAX_CMDSZ]; /* SCSI command words. */
+ uint32_t byte_count; /* Total byte count. */
+ uint32_t dseg_0_address[2]; /* Data segment 0 address. */
+ uint32_t dseg_0_length; /* Data segment 0 length. */
+ uint32_t dseg_1_address[2]; /* Data segment 1 address. */
+ uint32_t dseg_1_length; /* Data segment 1 length. */
+} cmd_a64_entry_t, request_t;
+
+/*
+ * ISP queue - continuation entry structure definition.
+ */
+#define CONTINUE_TYPE 0x02 /* Continuation entry. */
+typedef struct {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t reserved;
+ uint32_t dseg_0_address; /* Data segment 0 address. */
+ uint32_t dseg_0_length; /* Data segment 0 length. */
+ uint32_t dseg_1_address; /* Data segment 1 address. */
+ uint32_t dseg_1_length; /* Data segment 1 length. */
+ uint32_t dseg_2_address; /* Data segment 2 address. */
+ uint32_t dseg_2_length; /* Data segment 2 length. */
+ uint32_t dseg_3_address; /* Data segment 3 address. */
+ uint32_t dseg_3_length; /* Data segment 3 length. */
+ uint32_t dseg_4_address; /* Data segment 4 address. */
+ uint32_t dseg_4_length; /* Data segment 4 length. */
+ uint32_t dseg_5_address; /* Data segment 5 address. */
+ uint32_t dseg_5_length; /* Data segment 5 length. */
+ uint32_t dseg_6_address; /* Data segment 6 address. */
+ uint32_t dseg_6_length; /* Data segment 6 length. */
+} cont_entry_t;
+
+/*
+ * ISP queue - 64-Bit addressing, continuation entry structure definition.
+ */
+#define CONTINUE_A64_TYPE 0x0A /* Continuation A64 entry. */
+typedef struct {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t dseg_0_address[2]; /* Data segment 0 address. */
+ uint32_t dseg_0_length; /* Data segment 0 length. */
+ uint32_t dseg_1_address[2]; /* Data segment 1 address. */
+ uint32_t dseg_1_length; /* Data segment 1 length. */
+ uint32_t dseg_2_address [2]; /* Data segment 2 address. */
+ uint32_t dseg_2_length; /* Data segment 2 length. */
+ uint32_t dseg_3_address[2]; /* Data segment 3 address. */
+ uint32_t dseg_3_length; /* Data segment 3 length. */
+ uint32_t dseg_4_address[2]; /* Data segment 4 address. */
+ uint32_t dseg_4_length; /* Data segment 4 length. */
+} cont_a64_entry_t;
+
+#define PO_MODE_DIF_INSERT 0
+#define PO_MODE_DIF_REMOVE BIT_0
+#define PO_MODE_DIF_PASS BIT_1
+#define PO_MODE_DIF_REPLACE (BIT_0 + BIT_1)
+#define PO_ENABLE_DIF_BUNDLING BIT_8
+#define PO_ENABLE_INCR_GUARD_SEED BIT_3
+#define PO_DISABLE_INCR_REF_TAG BIT_5
+#define PO_DISABLE_GUARD_CHECK BIT_4
+/*
+ * ISP queue - 64-Bit addressing, continuation crc entry structure definition.
+ */
+struct crc_context {
+ uint32_t handle; /* System handle. */
+ uint32_t ref_tag;
+ uint16_t app_tag;
+ uint8_t ref_tag_mask[4]; /* Validation/Replacement Mask*/
+ uint8_t app_tag_mask[2]; /* Validation/Replacement Mask*/
+ uint16_t guard_seed; /* Initial Guard Seed */
+ uint16_t prot_opts; /* Requested Data Protection Mode */
+ uint16_t blk_size; /* Data size in bytes */
+ uint16_t runt_blk_guard; /* Guard value for runt block (tape
+ * only) */
+ uint32_t byte_count; /* Total byte count/ total data
+ * transfer count */
+ union {
+ struct {
+ uint32_t reserved_1;
+ uint16_t reserved_2;
+ uint16_t reserved_3;
+ uint32_t reserved_4;
+ uint32_t data_address[2];
+ uint32_t data_length;
+ uint32_t reserved_5[2];
+ uint32_t reserved_6;
+ } nobundling;
+ struct {
+ uint32_t dif_byte_count; /* Total DIF byte
+ * count */
+ uint16_t reserved_1;
+ uint16_t dseg_count; /* Data segment count */
+ uint32_t reserved_2;
+ uint32_t data_address[2];
+ uint32_t data_length;
+ uint32_t dif_address[2];
+ uint32_t dif_length; /* Data segment 0
+ * length */
+ } bundling;
+ } u;
+
+ struct fcp_cmnd fcp_cmnd;
+ dma_addr_t crc_ctx_dma;
+ /* List of DMA context transfers */
+ struct list_head dsd_list;
+
+ /* This structure should not exceed 512 bytes */
+};
+
+#define CRC_CONTEXT_LEN_FW (offsetof(struct crc_context, fcp_cmnd.lun))
+#define CRC_CONTEXT_FCPCMND_OFF (offsetof(struct crc_context, fcp_cmnd.lun))
+
+/*
+ * ISP queue - status entry structure definition.
+ */
+#define STATUS_TYPE 0x03 /* Status entry. */
+typedef struct {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t handle; /* System handle. */
+ uint16_t scsi_status; /* SCSI status. */
+ uint16_t comp_status; /* Completion status. */
+ uint16_t state_flags; /* State flags. */
+ uint16_t status_flags; /* Status flags. */
+ uint16_t rsp_info_len; /* Response Info Length. */
+ uint16_t req_sense_length; /* Request sense data length. */
+ uint32_t residual_length; /* Residual transfer length. */
+ uint8_t rsp_info[8]; /* FCP response information. */
+ uint8_t req_sense_data[32]; /* Request sense data. */
+} sts_entry_t;
+
+/*
+ * Status entry entry status
+ */
+#define RF_RQ_DMA_ERROR BIT_6 /* Request Queue DMA error. */
+#define RF_INV_E_ORDER BIT_5 /* Invalid entry order. */
+#define RF_INV_E_COUNT BIT_4 /* Invalid entry count. */
+#define RF_INV_E_PARAM BIT_3 /* Invalid entry parameter. */
+#define RF_INV_E_TYPE BIT_2 /* Invalid entry type. */
+#define RF_BUSY BIT_1 /* Busy */
+#define RF_MASK (RF_RQ_DMA_ERROR | RF_INV_E_ORDER | RF_INV_E_COUNT | \
+ RF_INV_E_PARAM | RF_INV_E_TYPE | RF_BUSY)
+#define RF_MASK_24XX (RF_INV_E_ORDER | RF_INV_E_COUNT | RF_INV_E_PARAM | \
+ RF_INV_E_TYPE)
+
+/*
+ * Status entry SCSI status bit definitions.
+ */
+#define SS_MASK 0xfff /* Reserved bits BIT_12-BIT_15*/
+#define SS_RESIDUAL_UNDER BIT_11
+#define SS_RESIDUAL_OVER BIT_10
+#define SS_SENSE_LEN_VALID BIT_9
+#define SS_RESPONSE_INFO_LEN_VALID BIT_8
+
+#define SS_RESERVE_CONFLICT (BIT_4 | BIT_3)
+#define SS_BUSY_CONDITION BIT_3
+#define SS_CONDITION_MET BIT_2
+#define SS_CHECK_CONDITION BIT_1
+
+/*
+ * Status entry completion status
+ */
+#define CS_COMPLETE 0x0 /* No errors */
+#define CS_INCOMPLETE 0x1 /* Incomplete transfer of cmd. */
+#define CS_DMA 0x2 /* A DMA direction error. */
+#define CS_TRANSPORT 0x3 /* Transport error. */
+#define CS_RESET 0x4 /* SCSI bus reset occurred */
+#define CS_ABORTED 0x5 /* System aborted command. */
+#define CS_TIMEOUT 0x6 /* Timeout error. */
+#define CS_DATA_OVERRUN 0x7 /* Data overrun. */
+#define CS_DIF_ERROR 0xC /* DIF error detected */
+
+#define CS_DATA_UNDERRUN 0x15 /* Data Underrun. */
+#define CS_QUEUE_FULL 0x1C /* Queue Full. */
+#define CS_PORT_UNAVAILABLE 0x28 /* Port unavailable */
+ /* (selection timeout) */
+#define CS_PORT_LOGGED_OUT 0x29 /* Port Logged Out */
+#define CS_PORT_CONFIG_CHG 0x2A /* Port Configuration Changed */
+#define CS_PORT_BUSY 0x2B /* Port Busy */
+#define CS_COMPLETE_CHKCOND 0x30 /* Error? */
+#define CS_BAD_PAYLOAD 0x80 /* Driver defined */
+#define CS_UNKNOWN 0x81 /* Driver defined */
+#define CS_RETRY 0x82 /* Driver defined */
+#define CS_LOOP_DOWN_ABORT 0x83 /* Driver defined */
+
+/*
+ * Status entry status flags
+ */
+#define SF_ABTS_TERMINATED BIT_10
+#define SF_LOGOUT_SENT BIT_13
+
+/*
+ * ISP queue - status continuation entry structure definition.
+ */
+#define STATUS_CONT_TYPE 0x10 /* Status continuation entry. */
+typedef struct {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+ uint8_t data[60]; /* data */
+} sts_cont_entry_t;
+
+/*
+ * ISP queue - RIO Type 1 status entry (32 bit I/O entry handles)
+ * structure definition.
+ */
+#define STATUS_TYPE_21 0x21 /* Status entry. */
+typedef struct {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t handle_count; /* Handle count. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t handle[15]; /* System handles. */
+} sts21_entry_t;
+
+/*
+ * ISP queue - RIO Type 2 status entry (16 bit I/O entry handles)
+ * structure definition.
+ */
+#define STATUS_TYPE_22 0x22 /* Status entry. */
+typedef struct {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t handle_count; /* Handle count. */
+ uint8_t entry_status; /* Entry Status. */
+ uint16_t handle[30]; /* System handles. */
+} sts22_entry_t;
+
+/*
+ * ISP queue - marker entry structure definition.
+ */
+#define MARKER_TYPE 0x04 /* Marker entry. */
+typedef struct {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t handle_count; /* Handle count. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t sys_define_2; /* System defined. */
+ target_id_t target; /* SCSI ID */
+ uint8_t modifier; /* Modifier (7-0). */
+#define MK_SYNC_ID_LUN 0 /* Synchronize ID/LUN */
+#define MK_SYNC_ID 1 /* Synchronize ID */
+#define MK_SYNC_ALL 2 /* Synchronize all ID/LUN */
+#define MK_SYNC_LIP 3 /* Synchronize all ID/LUN, */
+ /* clear port changed, */
+ /* use sequence number. */
+ uint8_t reserved_1;
+ uint16_t sequence_number; /* Sequence number of event */
+ uint16_t lun; /* SCSI LUN */
+ uint8_t reserved_2[48];
+} mrk_entry_t;
+
+/*
+ * ISP queue - Management Server entry structure definition.
+ */
+#define MS_IOCB_TYPE 0x29 /* Management Server IOCB entry */
+typedef struct {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t handle_count; /* Handle count. */
+ uint8_t entry_status; /* Entry Status. */
+ uint32_t handle1; /* System handle. */
+ target_id_t loop_id;
+ uint16_t status;
+ uint16_t control_flags; /* Control flags. */
+ uint16_t reserved2;
+ uint16_t timeout;
+ uint16_t cmd_dsd_count;
+ uint16_t total_dsd_count;
+ uint8_t type;
+ uint8_t r_ctl;
+ uint16_t rx_id;
+ uint16_t reserved3;
+ uint32_t handle2;
+ uint32_t rsp_bytecount;
+ uint32_t req_bytecount;
+ uint32_t dseg_req_address[2]; /* Data segment 0 address. */
+ uint32_t dseg_req_length; /* Data segment 0 length. */
+ uint32_t dseg_rsp_address[2]; /* Data segment 1 address. */
+ uint32_t dseg_rsp_length; /* Data segment 1 length. */
+} ms_iocb_entry_t;
+
+
+/*
+ * ISP queue - Mailbox Command entry structure definition.
+ */
+#define MBX_IOCB_TYPE 0x39
+struct mbx_entry {
+ uint8_t entry_type;
+ uint8_t entry_count;
+ uint8_t sys_define1;
+ /* Use sys_define1 for source type */
+#define SOURCE_SCSI 0x00
+#define SOURCE_IP 0x01
+#define SOURCE_VI 0x02
+#define SOURCE_SCTP 0x03
+#define SOURCE_MP 0x04
+#define SOURCE_MPIOCTL 0x05
+#define SOURCE_ASYNC_IOCB 0x07
+
+ uint8_t entry_status;
+
+ uint32_t handle;
+ target_id_t loop_id;
+
+ uint16_t status;
+ uint16_t state_flags;
+ uint16_t status_flags;
+
+ uint32_t sys_define2[2];
+
+ uint16_t mb0;
+ uint16_t mb1;
+ uint16_t mb2;
+ uint16_t mb3;
+ uint16_t mb6;
+ uint16_t mb7;
+ uint16_t mb9;
+ uint16_t mb10;
+ uint32_t reserved_2[2];
+ uint8_t node_name[WWN_SIZE];
+ uint8_t port_name[WWN_SIZE];
+};
+
+/*
+ * ISP request and response queue entry sizes
+ */
+#define RESPONSE_ENTRY_SIZE (sizeof(response_t))
+#define REQUEST_ENTRY_SIZE (sizeof(request_t))
+
+
+/*
+ * 24 bit port ID type definition.
+ */
+typedef union {
+ uint32_t b24 : 24;
+
+ struct {
+#ifdef __BIG_ENDIAN
+ uint8_t domain;
+ uint8_t area;
+ uint8_t al_pa;
+#elif defined(__LITTLE_ENDIAN)
+ uint8_t al_pa;
+ uint8_t area;
+ uint8_t domain;
+#else
+#error "__BIG_ENDIAN or __LITTLE_ENDIAN must be defined!"
+#endif
+ uint8_t rsvd_1;
+ } b;
+} port_id_t;
+#define INVALID_PORT_ID 0xFFFFFF
+
+/*
+ * Switch info gathering structure.
+ */
+typedef struct {
+ port_id_t d_id;
+ uint8_t node_name[WWN_SIZE];
+ uint8_t port_name[WWN_SIZE];
+ uint8_t fabric_port_name[WWN_SIZE];
+ uint16_t fp_speed;
+ uint8_t fc4_type;
+} sw_info_t;
+
+/* FCP-4 types */
+#define FC4_TYPE_FCP_SCSI 0x08
+#define FC4_TYPE_OTHER 0x0
+#define FC4_TYPE_UNKNOWN 0xff
+
+/*
+ * Fibre channel port type.
+ */
+ typedef enum {
+ FCT_UNKNOWN,
+ FCT_RSCN,
+ FCT_SWITCH,
+ FCT_BROADCAST,
+ FCT_INITIATOR,
+ FCT_TARGET
+} fc_port_type_t;
+
+/*
+ * Fibre channel port structure.
+ */
+typedef struct fc_port {
+ struct list_head list;
+ struct scsi_qla_host *vha;
+
+ uint8_t node_name[WWN_SIZE];
+ uint8_t port_name[WWN_SIZE];
+ port_id_t d_id;
+ uint16_t loop_id;
+ uint16_t old_loop_id;
+
+ uint8_t fcp_prio;
+
+ uint8_t fabric_port_name[WWN_SIZE];
+ uint16_t fp_speed;
+
+ fc_port_type_t port_type;
+
+ atomic_t state;
+ uint32_t flags;
+
+ int login_retry;
+
+ struct fc_rport *rport, *drport;
+ u32 supported_classes;
+
+ uint16_t vp_idx;
+ uint8_t fc4_type;
+ uint8_t scan_state;
+} fc_port_t;
+
+/*
+ * Fibre channel port/lun states.
+ */
+#define FCS_UNCONFIGURED 1
+#define FCS_DEVICE_DEAD 2
+#define FCS_DEVICE_LOST 3
+#define FCS_ONLINE 4
+
+static const char * const port_state_str[] = {
+ "Unknown",
+ "UNCONFIGURED",
+ "DEAD",
+ "LOST",
+ "ONLINE"
+};
+
+/*
+ * FC port flags.
+ */
+#define FCF_FABRIC_DEVICE BIT_0
+#define FCF_LOGIN_NEEDED BIT_1
+#define FCF_FCP2_DEVICE BIT_2
+#define FCF_ASYNC_SENT BIT_3
+
+/* No loop ID flag. */
+#define FC_NO_LOOP_ID 0x1000
+
+/*
+ * FC-CT interface
+ *
+ * NOTE: All structures are big-endian in form.
+ */
+
+#define CT_REJECT_RESPONSE 0x8001
+#define CT_ACCEPT_RESPONSE 0x8002
+#define CT_REASON_INVALID_COMMAND_CODE 0x01
+#define CT_REASON_CANNOT_PERFORM 0x09
+#define CT_REASON_COMMAND_UNSUPPORTED 0x0b
+#define CT_EXPL_ALREADY_REGISTERED 0x10
+
+#define NS_N_PORT_TYPE 0x01
+#define NS_NL_PORT_TYPE 0x02
+#define NS_NX_PORT_TYPE 0x7F
+
+#define GA_NXT_CMD 0x100
+#define GA_NXT_REQ_SIZE (16 + 4)
+#define GA_NXT_RSP_SIZE (16 + 620)
+
+#define GID_PT_CMD 0x1A1
+#define GID_PT_REQ_SIZE (16 + 4)
+
+#define GPN_ID_CMD 0x112
+#define GPN_ID_REQ_SIZE (16 + 4)
+#define GPN_ID_RSP_SIZE (16 + 8)
+
+#define GNN_ID_CMD 0x113
+#define GNN_ID_REQ_SIZE (16 + 4)
+#define GNN_ID_RSP_SIZE (16 + 8)
+
+#define GFT_ID_CMD 0x117
+#define GFT_ID_REQ_SIZE (16 + 4)
+#define GFT_ID_RSP_SIZE (16 + 32)
+
+#define RFT_ID_CMD 0x217
+#define RFT_ID_REQ_SIZE (16 + 4 + 32)
+#define RFT_ID_RSP_SIZE 16
+
+#define RFF_ID_CMD 0x21F
+#define RFF_ID_REQ_SIZE (16 + 4 + 2 + 1 + 1)
+#define RFF_ID_RSP_SIZE 16
+
+#define RNN_ID_CMD 0x213
+#define RNN_ID_REQ_SIZE (16 + 4 + 8)
+#define RNN_ID_RSP_SIZE 16
+
+#define RSNN_NN_CMD 0x239
+#define RSNN_NN_REQ_SIZE (16 + 8 + 1 + 255)
+#define RSNN_NN_RSP_SIZE 16
+
+#define GFPN_ID_CMD 0x11C
+#define GFPN_ID_REQ_SIZE (16 + 4)
+#define GFPN_ID_RSP_SIZE (16 + 8)
+
+#define GPSC_CMD 0x127
+#define GPSC_REQ_SIZE (16 + 8)
+#define GPSC_RSP_SIZE (16 + 2 + 2)
+
+#define GFF_ID_CMD 0x011F
+#define GFF_ID_REQ_SIZE (16 + 4)
+#define GFF_ID_RSP_SIZE (16 + 128)
+
+/*
+ * HBA attribute types.
+ */
+#define FDMI_HBA_ATTR_COUNT 9
+#define FDMI_HBA_NODE_NAME 1
+#define FDMI_HBA_MANUFACTURER 2
+#define FDMI_HBA_SERIAL_NUMBER 3
+#define FDMI_HBA_MODEL 4
+#define FDMI_HBA_MODEL_DESCRIPTION 5
+#define FDMI_HBA_HARDWARE_VERSION 6
+#define FDMI_HBA_DRIVER_VERSION 7
+#define FDMI_HBA_OPTION_ROM_VERSION 8
+#define FDMI_HBA_FIRMWARE_VERSION 9
+#define FDMI_HBA_OS_NAME_AND_VERSION 0xa
+#define FDMI_HBA_MAXIMUM_CT_PAYLOAD_LENGTH 0xb
+
+struct ct_fdmi_hba_attr {
+ uint16_t type;
+ uint16_t len;
+ union {
+ uint8_t node_name[WWN_SIZE];
+ uint8_t manufacturer[32];
+ uint8_t serial_num[8];
+ uint8_t model[16];
+ uint8_t model_desc[80];
+ uint8_t hw_version[16];
+ uint8_t driver_version[32];
+ uint8_t orom_version[16];
+ uint8_t fw_version[16];
+ uint8_t os_version[128];
+ uint8_t max_ct_len[4];
+ } a;
+};
+
+struct ct_fdmi_hba_attributes {
+ uint32_t count;
+ struct ct_fdmi_hba_attr entry[FDMI_HBA_ATTR_COUNT];
+};
+
+/*
+ * Port attribute types.
+ */
+#define FDMI_PORT_ATTR_COUNT 6
+#define FDMI_PORT_FC4_TYPES 1
+#define FDMI_PORT_SUPPORT_SPEED 2
+#define FDMI_PORT_CURRENT_SPEED 3
+#define FDMI_PORT_MAX_FRAME_SIZE 4
+#define FDMI_PORT_OS_DEVICE_NAME 5
+#define FDMI_PORT_HOST_NAME 6
+
+#define FDMI_PORT_SPEED_1GB 0x1
+#define FDMI_PORT_SPEED_2GB 0x2
+#define FDMI_PORT_SPEED_10GB 0x4
+#define FDMI_PORT_SPEED_4GB 0x8
+#define FDMI_PORT_SPEED_8GB 0x10
+#define FDMI_PORT_SPEED_16GB 0x20
+#define FDMI_PORT_SPEED_UNKNOWN 0x8000
+
+struct ct_fdmi_port_attr {
+ uint16_t type;
+ uint16_t len;
+ union {
+ uint8_t fc4_types[32];
+ uint32_t sup_speed;
+ uint32_t cur_speed;
+ uint32_t max_frame_size;
+ uint8_t os_dev_name[32];
+ uint8_t host_name[32];
+ } a;
+};
+
+/*
+ * Port Attribute Block.
+ */
+struct ct_fdmi_port_attributes {
+ uint32_t count;
+ struct ct_fdmi_port_attr entry[FDMI_PORT_ATTR_COUNT];
+};
+
+/* FDMI definitions. */
+#define GRHL_CMD 0x100
+#define GHAT_CMD 0x101
+#define GRPL_CMD 0x102
+#define GPAT_CMD 0x110
+
+#define RHBA_CMD 0x200
+#define RHBA_RSP_SIZE 16
+
+#define RHAT_CMD 0x201
+#define RPRT_CMD 0x210
+
+#define RPA_CMD 0x211
+#define RPA_RSP_SIZE 16
+
+#define DHBA_CMD 0x300
+#define DHBA_REQ_SIZE (16 + 8)
+#define DHBA_RSP_SIZE 16
+
+#define DHAT_CMD 0x301
+#define DPRT_CMD 0x310
+#define DPA_CMD 0x311
+
+/* CT command header -- request/response common fields */
+struct ct_cmd_hdr {
+ uint8_t revision;
+ uint8_t in_id[3];
+ uint8_t gs_type;
+ uint8_t gs_subtype;
+ uint8_t options;
+ uint8_t reserved;
+};
+
+/* CT command request */
+struct ct_sns_req {
+ struct ct_cmd_hdr header;
+ uint16_t command;
+ uint16_t max_rsp_size;
+ uint8_t fragment_id;
+ uint8_t reserved[3];
+
+ union {
+ /* GA_NXT, GPN_ID, GNN_ID, GFT_ID, GFPN_ID */
+ struct {
+ uint8_t reserved;
+ uint8_t port_id[3];
+ } port_id;
+
+ struct {
+ uint8_t port_type;
+ uint8_t domain;
+ uint8_t area;
+ uint8_t reserved;
+ } gid_pt;
+
+ struct {
+ uint8_t reserved;
+ uint8_t port_id[3];
+ uint8_t fc4_types[32];
+ } rft_id;
+
+ struct {
+ uint8_t reserved;
+ uint8_t port_id[3];
+ uint16_t reserved2;
+ uint8_t fc4_feature;
+ uint8_t fc4_type;
+ } rff_id;
+
+ struct {
+ uint8_t reserved;
+ uint8_t port_id[3];
+ uint8_t node_name[8];
+ } rnn_id;
+
+ struct {
+ uint8_t node_name[8];
+ uint8_t name_len;
+ uint8_t sym_node_name[255];
+ } rsnn_nn;
+
+ struct {
+ uint8_t hba_indentifier[8];
+ } ghat;
+
+ struct {
+ uint8_t hba_identifier[8];
+ uint32_t entry_count;
+ uint8_t port_name[8];
+ struct ct_fdmi_hba_attributes attrs;
+ } rhba;
+
+ struct {
+ uint8_t hba_identifier[8];
+ struct ct_fdmi_hba_attributes attrs;
+ } rhat;
+
+ struct {
+ uint8_t port_name[8];
+ struct ct_fdmi_port_attributes attrs;
+ } rpa;
+
+ struct {
+ uint8_t port_name[8];
+ } dhba;
+
+ struct {
+ uint8_t port_name[8];
+ } dhat;
+
+ struct {
+ uint8_t port_name[8];
+ } dprt;
+
+ struct {
+ uint8_t port_name[8];
+ } dpa;
+
+ struct {
+ uint8_t port_name[8];
+ } gpsc;
+
+ struct {
+ uint8_t reserved;
+ uint8_t port_name[3];
+ } gff_id;
+ } req;
+};
+
+/* CT command response header */
+struct ct_rsp_hdr {
+ struct ct_cmd_hdr header;
+ uint16_t response;
+ uint16_t residual;
+ uint8_t fragment_id;
+ uint8_t reason_code;
+ uint8_t explanation_code;
+ uint8_t vendor_unique;
+};
+
+struct ct_sns_gid_pt_data {
+ uint8_t control_byte;
+ uint8_t port_id[3];
+};
+
+struct ct_sns_rsp {
+ struct ct_rsp_hdr header;
+
+ union {
+ struct {
+ uint8_t port_type;
+ uint8_t port_id[3];
+ uint8_t port_name[8];
+ uint8_t sym_port_name_len;
+ uint8_t sym_port_name[255];
+ uint8_t node_name[8];
+ uint8_t sym_node_name_len;
+ uint8_t sym_node_name[255];
+ uint8_t init_proc_assoc[8];
+ uint8_t node_ip_addr[16];
+ uint8_t class_of_service[4];
+ uint8_t fc4_types[32];
+ uint8_t ip_address[16];
+ uint8_t fabric_port_name[8];
+ uint8_t reserved;
+ uint8_t hard_address[3];
+ } ga_nxt;
+
+ struct {
+ /* Assume the largest number of targets for the union */
+ struct ct_sns_gid_pt_data
+ entries[MAX_FIBRE_DEVICES_MAX];
+ } gid_pt;
+
+ struct {
+ uint8_t port_name[8];
+ } gpn_id;
+
+ struct {
+ uint8_t node_name[8];
+ } gnn_id;
+
+ struct {
+ uint8_t fc4_types[32];
+ } gft_id;
+
+ struct {
+ uint32_t entry_count;
+ uint8_t port_name[8];
+ struct ct_fdmi_hba_attributes attrs;
+ } ghat;
+
+ struct {
+ uint8_t port_name[8];
+ } gfpn_id;
+
+ struct {
+ uint16_t speeds;
+ uint16_t speed;
+ } gpsc;
+
+#define GFF_FCP_SCSI_OFFSET 7
+ struct {
+ uint8_t fc4_features[128];
+ } gff_id;
+ } rsp;
+};
+
+struct ct_sns_pkt {
+ union {
+ struct ct_sns_req req;
+ struct ct_sns_rsp rsp;
+ } p;
+};
+
+/*
+ * SNS command structures -- for 2200 compatibility.
+ */
+#define RFT_ID_SNS_SCMD_LEN 22
+#define RFT_ID_SNS_CMD_SIZE 60
+#define RFT_ID_SNS_DATA_SIZE 16
+
+#define RNN_ID_SNS_SCMD_LEN 10
+#define RNN_ID_SNS_CMD_SIZE 36
+#define RNN_ID_SNS_DATA_SIZE 16
+
+#define GA_NXT_SNS_SCMD_LEN 6
+#define GA_NXT_SNS_CMD_SIZE 28
+#define GA_NXT_SNS_DATA_SIZE (620 + 16)
+
+#define GID_PT_SNS_SCMD_LEN 6
+#define GID_PT_SNS_CMD_SIZE 28
+/*
+ * Assume MAX_FIBRE_DEVICES_2100 as these defines are only used with older
+ * adapters.
+ */
+#define GID_PT_SNS_DATA_SIZE (MAX_FIBRE_DEVICES_2100 * 4 + 16)
+
+#define GPN_ID_SNS_SCMD_LEN 6
+#define GPN_ID_SNS_CMD_SIZE 28
+#define GPN_ID_SNS_DATA_SIZE (8 + 16)
+
+#define GNN_ID_SNS_SCMD_LEN 6
+#define GNN_ID_SNS_CMD_SIZE 28
+#define GNN_ID_SNS_DATA_SIZE (8 + 16)
+
+struct sns_cmd_pkt {
+ union {
+ struct {
+ uint16_t buffer_length;
+ uint16_t reserved_1;
+ uint32_t buffer_address[2];
+ uint16_t subcommand_length;
+ uint16_t reserved_2;
+ uint16_t subcommand;
+ uint16_t size;
+ uint32_t reserved_3;
+ uint8_t param[36];
+ } cmd;
+
+ uint8_t rft_data[RFT_ID_SNS_DATA_SIZE];
+ uint8_t rnn_data[RNN_ID_SNS_DATA_SIZE];
+ uint8_t gan_data[GA_NXT_SNS_DATA_SIZE];
+ uint8_t gid_data[GID_PT_SNS_DATA_SIZE];
+ uint8_t gpn_data[GPN_ID_SNS_DATA_SIZE];
+ uint8_t gnn_data[GNN_ID_SNS_DATA_SIZE];
+ } p;
+};
+
+struct fw_blob {
+ char *name;
+ uint32_t segs[4];
+ const struct firmware *fw;
+};
+
+/* Return data from MBC_GET_ID_LIST call. */
+struct gid_list_info {
+ uint8_t al_pa;
+ uint8_t area;
+ uint8_t domain;
+ uint8_t loop_id_2100; /* ISP2100/ISP2200 -- 4 bytes. */
+ uint16_t loop_id; /* ISP23XX -- 6 bytes. */
+ uint16_t reserved_1; /* ISP24XX -- 8 bytes. */
+};
+
+/* NPIV */
+typedef struct vport_info {
+ uint8_t port_name[WWN_SIZE];
+ uint8_t node_name[WWN_SIZE];
+ int vp_id;
+ uint16_t loop_id;
+ unsigned long host_no;
+ uint8_t port_id[3];
+ int loop_state;
+} vport_info_t;
+
+typedef struct vport_params {
+ uint8_t port_name[WWN_SIZE];
+ uint8_t node_name[WWN_SIZE];
+ uint32_t options;
+#define VP_OPTS_RETRY_ENABLE BIT_0
+#define VP_OPTS_VP_DISABLE BIT_1
+} vport_params_t;
+
+/* NPIV - return codes of VP create and modify */
+#define VP_RET_CODE_OK 0
+#define VP_RET_CODE_FATAL 1
+#define VP_RET_CODE_WRONG_ID 2
+#define VP_RET_CODE_WWPN 3
+#define VP_RET_CODE_RESOURCES 4
+#define VP_RET_CODE_NO_MEM 5
+#define VP_RET_CODE_NOT_FOUND 6
+
+struct qla_hw_data;
+struct rsp_que;
+/*
+ * ISP operations
+ */
+struct isp_operations {
+
+ int (*pci_config) (struct scsi_qla_host *);
+ void (*reset_chip) (struct scsi_qla_host *);
+ int (*chip_diag) (struct scsi_qla_host *);
+ void (*config_rings) (struct scsi_qla_host *);
+ void (*reset_adapter) (struct scsi_qla_host *);
+ int (*nvram_config) (struct scsi_qla_host *);
+ void (*update_fw_options) (struct scsi_qla_host *);
+ int (*load_risc) (struct scsi_qla_host *, uint32_t *);
+
+ char * (*pci_info_str) (struct scsi_qla_host *, char *);
+ char * (*fw_version_str) (struct scsi_qla_host *, char *);
+
+ irq_handler_t intr_handler;
+ void (*enable_intrs) (struct qla_hw_data *);
+ void (*disable_intrs) (struct qla_hw_data *);
+
+ int (*abort_command) (srb_t *);
+ int (*target_reset) (struct fc_port *, unsigned int, int);
+ int (*lun_reset) (struct fc_port *, unsigned int, int);
+ int (*fabric_login) (struct scsi_qla_host *, uint16_t, uint8_t,
+ uint8_t, uint8_t, uint16_t *, uint8_t);
+ int (*fabric_logout) (struct scsi_qla_host *, uint16_t, uint8_t,
+ uint8_t, uint8_t);
+
+ uint16_t (*calc_req_entries) (uint16_t);
+ void (*build_iocbs) (srb_t *, cmd_entry_t *, uint16_t);
+ void * (*prep_ms_iocb) (struct scsi_qla_host *, uint32_t, uint32_t);
+ void * (*prep_ms_fdmi_iocb) (struct scsi_qla_host *, uint32_t,
+ uint32_t);
+
+ uint8_t * (*read_nvram) (struct scsi_qla_host *, uint8_t *,
+ uint32_t, uint32_t);
+ int (*write_nvram) (struct scsi_qla_host *, uint8_t *, uint32_t,
+ uint32_t);
+
+ void (*fw_dump) (struct scsi_qla_host *, int);
+
+ int (*beacon_on) (struct scsi_qla_host *);
+ int (*beacon_off) (struct scsi_qla_host *);
+ void (*beacon_blink) (struct scsi_qla_host *);
+
+ uint8_t * (*read_optrom) (struct scsi_qla_host *, uint8_t *,
+ uint32_t, uint32_t);
+ int (*write_optrom) (struct scsi_qla_host *, uint8_t *, uint32_t,
+ uint32_t);
+
+ int (*get_flash_version) (struct scsi_qla_host *, void *);
+ int (*start_scsi) (srb_t *);
+ int (*abort_isp) (struct scsi_qla_host *);
+ int (*iospace_config)(struct qla_hw_data*);
+};
+
+/* MSI-X Support *************************************************************/
+
+#define QLA_MSIX_CHIP_REV_24XX 3
+#define QLA_MSIX_FW_MODE(m) (((m) & (BIT_7|BIT_8|BIT_9)) >> 7)
+#define QLA_MSIX_FW_MODE_1(m) (QLA_MSIX_FW_MODE(m) == 1)
+
+#define QLA_MSIX_DEFAULT 0x00
+#define QLA_MSIX_RSP_Q 0x01
+
+#define QLA_MIDX_DEFAULT 0
+#define QLA_MIDX_RSP_Q 1
+#define QLA_PCI_MSIX_CONTROL 0xa2
+#define QLA_83XX_PCI_MSIX_CONTROL 0x92
+
+struct scsi_qla_host;
+
+struct qla_msix_entry {
+ int have_irq;
+ uint32_t vector;
+ uint16_t entry;
+ struct rsp_que *rsp;
+};
+
+#define WATCH_INTERVAL 1 /* number of seconds */
+
+/* Work events. */
+enum qla_work_type {
+ QLA_EVT_AEN,
+ QLA_EVT_IDC_ACK,
+ QLA_EVT_ASYNC_LOGIN,
+ QLA_EVT_ASYNC_LOGIN_DONE,
+ QLA_EVT_ASYNC_LOGOUT,
+ QLA_EVT_ASYNC_LOGOUT_DONE,
+ QLA_EVT_ASYNC_ADISC,
+ QLA_EVT_ASYNC_ADISC_DONE,
+ QLA_EVT_UEVENT,
+};
+
+
+struct qla_work_evt {
+ struct list_head list;
+ enum qla_work_type type;
+ u32 flags;
+#define QLA_EVT_FLAG_FREE 0x1
+
+ union {
+ struct {
+ enum fc_host_event_code code;
+ u32 data;
+ } aen;
+ struct {
+#define QLA_IDC_ACK_REGS 7
+ uint16_t mb[QLA_IDC_ACK_REGS];
+ } idc_ack;
+ struct {
+ struct fc_port *fcport;
+#define QLA_LOGIO_LOGIN_RETRIED BIT_0
+ u16 data[2];
+ } logio;
+ struct {
+ u32 code;
+#define QLA_UEVENT_CODE_FW_DUMP 0
+ } uevent;
+ } u;
+};
+
+struct qla_chip_state_84xx {
+ struct list_head list;
+ struct kref kref;
+
+ void *bus;
+ spinlock_t access_lock;
+ struct mutex fw_update_mutex;
+ uint32_t fw_update;
+ uint32_t op_fw_version;
+ uint32_t op_fw_size;
+ uint32_t op_fw_seq_size;
+ uint32_t diag_fw_version;
+ uint32_t gold_fw_version;
+};
+
+struct qla_statistics {
+ uint32_t total_isp_aborts;
+ uint64_t input_bytes;
+ uint64_t output_bytes;
+};
+
+/* Multi queue support */
+#define MBC_INITIALIZE_MULTIQ 0x1f
+#define QLA_QUE_PAGE 0X1000
+#define QLA_MQ_SIZE 32
+#define QLA_MAX_QUEUES 256
+#define ISP_QUE_REG(ha, id) \
+ ((ha->mqenable || IS_QLA83XX(ha)) ? \
+ ((void *)(ha->mqiobase) +\
+ (QLA_QUE_PAGE * id)) :\
+ ((void *)(ha->iobase)))
+#define QLA_REQ_QUE_ID(tag) \
+ ((tag < QLA_MAX_QUEUES && tag > 0) ? tag : 0)
+#define QLA_DEFAULT_QUE_QOS 5
+#define QLA_PRECONFIG_VPORTS 32
+#define QLA_MAX_VPORTS_QLA24XX 128
+#define QLA_MAX_VPORTS_QLA25XX 256
+/* Response queue data structure */
+struct rsp_que {
+ dma_addr_t dma;
+ response_t *ring;
+ response_t *ring_ptr;
+ uint32_t __iomem *rsp_q_in; /* FWI2-capable only. */
+ uint32_t __iomem *rsp_q_out;
+ uint16_t ring_index;
+ uint16_t out_ptr;
+ uint16_t length;
+ uint16_t options;
+ uint16_t rid;
+ uint16_t id;
+ uint16_t vp_idx;
+ struct qla_hw_data *hw;
+ struct qla_msix_entry *msix;
+ struct req_que *req;
+ srb_t *status_srb; /* status continuation entry */
+ struct work_struct q_work;
+};
+
+/* Request queue data structure */
+struct req_que {
+ dma_addr_t dma;
+ request_t *ring;
+ request_t *ring_ptr;
+ uint32_t __iomem *req_q_in; /* FWI2-capable only. */
+ uint32_t __iomem *req_q_out;
+ uint16_t ring_index;
+ uint16_t in_ptr;
+ uint16_t cnt;
+ uint16_t length;
+ uint16_t options;
+ uint16_t rid;
+ uint16_t id;
+ uint16_t qos;
+ uint16_t vp_idx;
+ struct rsp_que *rsp;
+ srb_t *outstanding_cmds[MAX_OUTSTANDING_COMMANDS];
+ uint32_t current_outstanding_cmd;
+ int max_q_depth;
+};
+
+/* Place holder for FW buffer parameters */
+struct qlfc_fw {
+ void *fw_buf;
+ dma_addr_t fw_dma;
+ uint32_t len;
+};
+
+/*
+ * Qlogic host adapter specific data structure.
+*/
+struct qla_hw_data {
+ struct pci_dev *pdev;
+ /* SRB cache. */
+#define SRB_MIN_REQ 128
+ mempool_t *srb_mempool;
+
+ volatile struct {
+ uint32_t mbox_int :1;
+ uint32_t mbox_busy :1;
+ uint32_t disable_risc_code_load :1;
+ uint32_t enable_64bit_addressing :1;
+ uint32_t enable_lip_reset :1;
+ uint32_t enable_target_reset :1;
+ uint32_t enable_lip_full_login :1;
+ uint32_t enable_led_scheme :1;
+
+ uint32_t msi_enabled :1;
+ uint32_t msix_enabled :1;
+ uint32_t disable_serdes :1;
+ uint32_t gpsc_supported :1;
+ uint32_t npiv_supported :1;
+ uint32_t pci_channel_io_perm_failure :1;
+ uint32_t fce_enabled :1;
+ uint32_t fac_supported :1;
+
+ uint32_t chip_reset_done :1;
+ uint32_t port0 :1;
+ uint32_t running_gold_fw :1;
+ uint32_t eeh_busy :1;
+ uint32_t cpu_affinity_enabled :1;
+ uint32_t disable_msix_handshake :1;
+ uint32_t fcp_prio_enabled :1;
+ uint32_t isp82xx_fw_hung:1;
+
+ uint32_t quiesce_owner:1;
+ uint32_t thermal_supported:1;
+ uint32_t isp82xx_reset_hdlr_active:1;
+ uint32_t isp82xx_reset_owner:1;
+ /* 28 bits */
+ } flags;
+
+ /* This spinlock is used to protect "io transactions", you must
+ * acquire it before doing any IO to the card, eg with RD_REG*() and
+ * WRT_REG*() for the duration of your entire commandtransaction.
+ *
+ * This spinlock is of lower priority than the io request lock.
+ */
+
+ spinlock_t hardware_lock ____cacheline_aligned;
+ int bars;
+ int mem_only;
+ device_reg_t __iomem *iobase; /* Base I/O address */
+ resource_size_t pio_address;
+
+#define MIN_IOBASE_LEN 0x100
+/* Multi queue data structs */
+ device_reg_t __iomem *mqiobase;
+ device_reg_t __iomem *msixbase;
+ uint16_t msix_count;
+ uint8_t mqenable;
+ struct req_que **req_q_map;
+ struct rsp_que **rsp_q_map;
+ unsigned long req_qid_map[(QLA_MAX_QUEUES / 8) / sizeof(unsigned long)];
+ unsigned long rsp_qid_map[(QLA_MAX_QUEUES / 8) / sizeof(unsigned long)];
+ uint8_t max_req_queues;
+ uint8_t max_rsp_queues;
+ struct qla_npiv_entry *npiv_info;
+ uint16_t nvram_npiv_size;
+
+ uint16_t switch_cap;
+#define FLOGI_SEQ_DEL BIT_8
+#define FLOGI_MID_SUPPORT BIT_10
+#define FLOGI_VSAN_SUPPORT BIT_12
+#define FLOGI_SP_SUPPORT BIT_13
+
+ uint8_t port_no; /* Physical port of adapter */
+
+ /* Timeout timers. */
+ uint8_t loop_down_abort_time; /* port down timer */
+ atomic_t loop_down_timer; /* loop down timer */
+ uint8_t link_down_timeout; /* link down timeout */
+ uint16_t max_loop_id;
+ uint16_t max_fibre_devices; /* Maximum number of targets */
+
+ uint16_t fb_rev;
+ uint16_t min_external_loopid; /* First external loop Id */
+
+#define PORT_SPEED_UNKNOWN 0xFFFF
+#define PORT_SPEED_1GB 0x00
+#define PORT_SPEED_2GB 0x01
+#define PORT_SPEED_4GB 0x03
+#define PORT_SPEED_8GB 0x04
+#define PORT_SPEED_16GB 0x05
+#define PORT_SPEED_10GB 0x13
+ uint16_t link_data_rate; /* F/W operating speed */
+
+ uint8_t current_topology;
+ uint8_t prev_topology;
+#define ISP_CFG_NL 1
+#define ISP_CFG_N 2
+#define ISP_CFG_FL 4
+#define ISP_CFG_F 8
+
+ uint8_t operating_mode; /* F/W operating mode */
+#define LOOP 0
+#define P2P 1
+#define LOOP_P2P 2
+#define P2P_LOOP 3
+ uint8_t interrupts_on;
+ uint32_t isp_abort_cnt;
+
+#define PCI_DEVICE_ID_QLOGIC_ISP2532 0x2532
+#define PCI_DEVICE_ID_QLOGIC_ISP8432 0x8432
+#define PCI_DEVICE_ID_QLOGIC_ISP8001 0x8001
+#define PCI_DEVICE_ID_QLOGIC_ISP8031 0x8031
+#define PCI_DEVICE_ID_QLOGIC_ISP2031 0x2031
+ uint32_t device_type;
+#define DT_ISP2100 BIT_0
+#define DT_ISP2200 BIT_1
+#define DT_ISP2300 BIT_2
+#define DT_ISP2312 BIT_3
+#define DT_ISP2322 BIT_4
+#define DT_ISP6312 BIT_5
+#define DT_ISP6322 BIT_6
+#define DT_ISP2422 BIT_7
+#define DT_ISP2432 BIT_8
+#define DT_ISP5422 BIT_9
+#define DT_ISP5432 BIT_10
+#define DT_ISP2532 BIT_11
+#define DT_ISP8432 BIT_12
+#define DT_ISP8001 BIT_13
+#define DT_ISP8021 BIT_14
+#define DT_ISP2031 BIT_15
+#define DT_ISP8031 BIT_16
+#define DT_ISP_LAST (DT_ISP8031 << 1)
+
+#define DT_T10_PI BIT_25
+#define DT_IIDMA BIT_26
+#define DT_FWI2 BIT_27
+#define DT_ZIO_SUPPORTED BIT_28
+#define DT_OEM_001 BIT_29
+#define DT_ISP2200A BIT_30
+#define DT_EXTENDED_IDS BIT_31
+#define DT_MASK(ha) ((ha)->device_type & (DT_ISP_LAST - 1))
+#define IS_QLA2100(ha) (DT_MASK(ha) & DT_ISP2100)
+#define IS_QLA2200(ha) (DT_MASK(ha) & DT_ISP2200)
+#define IS_QLA2300(ha) (DT_MASK(ha) & DT_ISP2300)
+#define IS_QLA2312(ha) (DT_MASK(ha) & DT_ISP2312)
+#define IS_QLA2322(ha) (DT_MASK(ha) & DT_ISP2322)
+#define IS_QLA6312(ha) (DT_MASK(ha) & DT_ISP6312)
+#define IS_QLA6322(ha) (DT_MASK(ha) & DT_ISP6322)
+#define IS_QLA2422(ha) (DT_MASK(ha) & DT_ISP2422)
+#define IS_QLA2432(ha) (DT_MASK(ha) & DT_ISP2432)
+#define IS_QLA5422(ha) (DT_MASK(ha) & DT_ISP5422)
+#define IS_QLA5432(ha) (DT_MASK(ha) & DT_ISP5432)
+#define IS_QLA2532(ha) (DT_MASK(ha) & DT_ISP2532)
+#define IS_QLA8432(ha) (DT_MASK(ha) & DT_ISP8432)
+#define IS_QLA8001(ha) (DT_MASK(ha) & DT_ISP8001)
+#define IS_QLA81XX(ha) (IS_QLA8001(ha))
+#define IS_QLA82XX(ha) (DT_MASK(ha) & DT_ISP8021)
+#define IS_QLA2031(ha) (DT_MASK(ha) & DT_ISP2031)
+#define IS_QLA8031(ha) (DT_MASK(ha) & DT_ISP8031)
+
+#define IS_QLA23XX(ha) (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA2322(ha) || \
+ IS_QLA6312(ha) || IS_QLA6322(ha))
+#define IS_QLA24XX(ha) (IS_QLA2422(ha) || IS_QLA2432(ha))
+#define IS_QLA54XX(ha) (IS_QLA5422(ha) || IS_QLA5432(ha))
+#define IS_QLA25XX(ha) (IS_QLA2532(ha))
+#define IS_QLA83XX(ha) (IS_QLA2031(ha) || IS_QLA8031(ha))
+#define IS_QLA84XX(ha) (IS_QLA8432(ha))
+#define IS_QLA24XX_TYPE(ha) (IS_QLA24XX(ha) || IS_QLA54XX(ha) || \
+ IS_QLA84XX(ha))
+#define IS_CNA_CAPABLE(ha) (IS_QLA81XX(ha) || IS_QLA82XX(ha) || \
+ IS_QLA8031(ha))
+#define IS_QLA2XXX_MIDTYPE(ha) (IS_QLA24XX(ha) || IS_QLA84XX(ha) || \
+ IS_QLA25XX(ha) || IS_QLA81XX(ha) || \
+ IS_QLA82XX(ha) || IS_QLA83XX(ha))
+#define IS_MSIX_NACK_CAPABLE(ha) (IS_QLA81XX(ha) || IS_QLA83XX(ha))
+#define IS_NOPOLLING_TYPE(ha) ((IS_QLA25XX(ha) || IS_QLA81XX(ha) || \
+ IS_QLA83XX(ha)) && (ha)->flags.msix_enabled)
+#define IS_FAC_REQUIRED(ha) (IS_QLA81XX(ha) || IS_QLA83XX(ha))
+#define IS_NOCACHE_VPD_TYPE(ha) (IS_QLA81XX(ha) || IS_QLA83XX(ha))
+#define IS_ALOGIO_CAPABLE(ha) (IS_QLA23XX(ha) || IS_FWI2_CAPABLE(ha))
+
+#define IS_T10_PI_CAPABLE(ha) ((ha)->device_type & DT_T10_PI)
+#define IS_IIDMA_CAPABLE(ha) ((ha)->device_type & DT_IIDMA)
+#define IS_FWI2_CAPABLE(ha) ((ha)->device_type & DT_FWI2)
+#define IS_ZIO_SUPPORTED(ha) ((ha)->device_type & DT_ZIO_SUPPORTED)
+#define IS_OEM_001(ha) ((ha)->device_type & DT_OEM_001)
+#define HAS_EXTENDED_IDS(ha) ((ha)->device_type & DT_EXTENDED_IDS)
+#define IS_CT6_SUPPORTED(ha) ((ha)->device_type & DT_CT6_SUPPORTED)
+#define IS_MQUE_CAPABLE(ha) ((ha)->mqenable || IS_QLA83XX(ha))
+
+ /* HBA serial number */
+ uint8_t serial0;
+ uint8_t serial1;
+ uint8_t serial2;
+
+ /* NVRAM configuration data */
+#define MAX_NVRAM_SIZE 4096
+#define VPD_OFFSET MAX_NVRAM_SIZE / 2
+ uint16_t nvram_size;
+ uint16_t nvram_base;
+ void *nvram;
+ uint16_t vpd_size;
+ uint16_t vpd_base;
+ void *vpd;
+
+ uint16_t loop_reset_delay;
+ uint8_t retry_count;
+ uint8_t login_timeout;
+ uint16_t r_a_tov;
+ int port_down_retry_count;
+ uint8_t mbx_count;
+
+ uint32_t login_retry_count;
+ /* SNS command interfaces. */
+ ms_iocb_entry_t *ms_iocb;
+ dma_addr_t ms_iocb_dma;
+ struct ct_sns_pkt *ct_sns;
+ dma_addr_t ct_sns_dma;
+ /* SNS command interfaces for 2200. */
+ struct sns_cmd_pkt *sns_cmd;
+ dma_addr_t sns_cmd_dma;
+
+#define SFP_DEV_SIZE 256
+#define SFP_BLOCK_SIZE 64
+ void *sfp_data;
+ dma_addr_t sfp_data_dma;
+
+#define XGMAC_DATA_SIZE 4096
+ void *xgmac_data;
+ dma_addr_t xgmac_data_dma;
+
+#define DCBX_TLV_DATA_SIZE 4096
+ void *dcbx_tlv;
+ dma_addr_t dcbx_tlv_dma;
+
+ struct task_struct *dpc_thread;
+ uint8_t dpc_active; /* DPC routine is active */
+
+ dma_addr_t gid_list_dma;
+ struct gid_list_info *gid_list;
+ int gid_list_info_size;
+
+ /* Small DMA pool allocations -- maximum 256 bytes in length. */
+#define DMA_POOL_SIZE 256
+ struct dma_pool *s_dma_pool;
+
+ dma_addr_t init_cb_dma;
+ init_cb_t *init_cb;
+ int init_cb_size;
+ dma_addr_t ex_init_cb_dma;
+ struct ex_init_cb_81xx *ex_init_cb;
+
+ void *async_pd;
+ dma_addr_t async_pd_dma;
+
+ void *swl;
+
+ /* These are used by mailbox operations. */
+ volatile uint16_t mailbox_out[MAILBOX_REGISTER_COUNT];
+
+ mbx_cmd_t *mcp;
+ unsigned long mbx_cmd_flags;
+#define MBX_INTERRUPT 1
+#define MBX_INTR_WAIT 2
+#define MBX_UPDATE_FLASH_ACTIVE 3
+
+ struct mutex vport_lock; /* Virtual port synchronization */
+ spinlock_t vport_slock; /* order is hardware_lock, then vport_slock */
+ struct completion mbx_cmd_comp; /* Serialize mbx access */
+ struct completion mbx_intr_comp; /* Used for completion notification */
+ struct completion dcbx_comp; /* For set port config notification */
+ int notify_dcbx_comp;
+
+ /* Basic firmware related information. */
+ uint16_t fw_major_version;
+ uint16_t fw_minor_version;
+ uint16_t fw_subminor_version;
+ uint16_t fw_attributes;
+ uint16_t fw_attributes_h;
+ uint16_t fw_attributes_ext[2];
+ uint32_t fw_memory_size;
+ uint32_t fw_transfer_size;
+ uint32_t fw_srisc_address;
+#define RISC_START_ADDRESS_2100 0x1000
+#define RISC_START_ADDRESS_2300 0x800
+#define RISC_START_ADDRESS_2400 0x100000
+ uint16_t fw_xcb_count;
+
+ uint16_t fw_options[16]; /* slots: 1,2,3,10,11 */
+ uint8_t fw_seriallink_options[4];
+ uint16_t fw_seriallink_options24[4];
+
+ uint8_t mpi_version[3];
+ uint32_t mpi_capabilities;
+ uint8_t phy_version[3];
+
+ /* Firmware dump information. */
+ struct qla2xxx_fw_dump *fw_dump;
+ uint32_t fw_dump_len;
+ int fw_dumped;
+ int fw_dump_reading;
+ dma_addr_t eft_dma;
+ void *eft;
+
+ uint32_t chain_offset;
+ struct dentry *dfs_dir;
+ struct dentry *dfs_fce;
+ dma_addr_t fce_dma;
+ void *fce;
+ uint32_t fce_bufs;
+ uint16_t fce_mb[8];
+ uint64_t fce_wr, fce_rd;
+ struct mutex fce_mutex;
+
+ uint32_t pci_attr;
+ uint16_t chip_revision;
+
+ uint16_t product_id[4];
+
+ uint8_t model_number[16+1];
+#define BINZERO "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
+ char model_desc[80];
+ uint8_t adapter_id[16+1];
+
+ /* Option ROM information. */
+ char *optrom_buffer;
+ uint32_t optrom_size;
+ int optrom_state;
+#define QLA_SWAITING 0
+#define QLA_SREADING 1
+#define QLA_SWRITING 2
+ uint32_t optrom_region_start;
+ uint32_t optrom_region_size;
+
+/* PCI expansion ROM image information. */
+#define ROM_CODE_TYPE_BIOS 0
+#define ROM_CODE_TYPE_FCODE 1
+#define ROM_CODE_TYPE_EFI 3
+ uint8_t bios_revision[2];
+ uint8_t efi_revision[2];
+ uint8_t fcode_revision[16];
+ uint32_t fw_revision[4];
+
+ uint32_t gold_fw_version[4];
+
+ /* Offsets for flash/nvram access (set to ~0 if not used). */
+ uint32_t flash_conf_off;
+ uint32_t flash_data_off;
+ uint32_t nvram_conf_off;
+ uint32_t nvram_data_off;
+
+ uint32_t fdt_wrt_disable;
+ uint32_t fdt_erase_cmd;
+ uint32_t fdt_block_size;
+ uint32_t fdt_unprotect_sec_cmd;
+ uint32_t fdt_protect_sec_cmd;
+
+ uint32_t flt_region_flt;
+ uint32_t flt_region_fdt;
+ uint32_t flt_region_boot;
+ uint32_t flt_region_fw;
+ uint32_t flt_region_vpd_nvram;
+ uint32_t flt_region_vpd;
+ uint32_t flt_region_nvram;
+ uint32_t flt_region_npiv_conf;
+ uint32_t flt_region_gold_fw;
+ uint32_t flt_region_fcp_prio;
+ uint32_t flt_region_bootload;
+
+ /* Needed for BEACON */
+ uint16_t beacon_blink_led;
+ uint8_t beacon_color_state;
+#define QLA_LED_GRN_ON 0x01
+#define QLA_LED_YLW_ON 0x02
+#define QLA_LED_ABR_ON 0x04
+#define QLA_LED_ALL_ON 0x07 /* yellow, green, amber. */
+ /* ISP2322: red, green, amber. */
+ uint16_t zio_mode;
+ uint16_t zio_timer;
+ struct fc_host_statistics fc_host_stat;
+
+ struct qla_msix_entry *msix_entries;
+
+ struct list_head vp_list; /* list of VP */
+ unsigned long vp_idx_map[(MAX_MULTI_ID_FABRIC / 8) /
+ sizeof(unsigned long)];
+ uint16_t num_vhosts; /* number of vports created */
+ uint16_t num_vsans; /* number of vsan created */
+ uint16_t max_npiv_vports; /* 63 or 125 per topoloty */
+ int cur_vport_count;
+
+ struct qla_chip_state_84xx *cs84xx;
+ struct qla_statistics qla_stats;
+ struct isp_operations *isp_ops;
+ struct workqueue_struct *wq;
+ struct qlfc_fw fw_buf;
+
+ /* FCP_CMND priority support */
+ struct qla_fcp_prio_cfg *fcp_prio_cfg;
+
+ struct dma_pool *dl_dma_pool;
+#define DSD_LIST_DMA_POOL_SIZE 512
+
+ struct dma_pool *fcp_cmnd_dma_pool;
+ mempool_t *ctx_mempool;
+#define FCP_CMND_DMA_POOL_SIZE 512
+
+ unsigned long nx_pcibase; /* Base I/O address */
+ uint8_t *nxdb_rd_ptr; /* Doorbell read pointer */
+ unsigned long nxdb_wr_ptr; /* Door bell write pointer */
+
+ uint32_t crb_win;
+ uint32_t curr_window;
+ uint32_t ddr_mn_window;
+ unsigned long mn_win_crb;
+ unsigned long ms_win_crb;
+ int qdr_sn_window;
+ uint32_t nx_dev_init_timeout;
+ uint32_t nx_reset_timeout;
+ rwlock_t hw_lock;
+ uint16_t portnum; /* port number */
+ int link_width;
+ struct fw_blob *hablob;
+ struct qla82xx_legacy_intr_set nx_legacy_intr;
+
+ uint16_t gbl_dsd_inuse;
+ uint16_t gbl_dsd_avail;
+ struct list_head gbl_dsd_list;
+#define NUM_DSD_CHAIN 4096
+
+ uint8_t fw_type;
+ __le32 file_prd_off; /* File firmware product offset */
+
+ uint32_t md_template_size;
+ void *md_tmplt_hdr;
+ dma_addr_t md_tmplt_hdr_dma;
+ void *md_dump;
+ uint32_t md_dump_size;
+};
+
+/*
+ * Qlogic scsi host structure
+ */
+typedef struct scsi_qla_host {
+ struct list_head list;
+ struct list_head vp_fcports; /* list of fcports */
+ struct list_head work_list;
+ spinlock_t work_lock;
+
+ /* Commonly used flags and state information. */
+ struct Scsi_Host *host;
+ unsigned long host_no;
+ uint8_t host_str[16];
+
+ volatile struct {
+ uint32_t init_done :1;
+ uint32_t online :1;
+ uint32_t reset_active :1;
+
+ uint32_t management_server_logged_in :1;
+ uint32_t process_response_queue :1;
+ uint32_t difdix_supported:1;
+ uint32_t delete_progress:1;
+ } flags;
+
+ atomic_t loop_state;
+#define LOOP_TIMEOUT 1
+#define LOOP_DOWN 2
+#define LOOP_UP 3
+#define LOOP_UPDATE 4
+#define LOOP_READY 5
+#define LOOP_DEAD 6
+
+ unsigned long dpc_flags;
+#define RESET_MARKER_NEEDED 0 /* Send marker to ISP. */
+#define RESET_ACTIVE 1
+#define ISP_ABORT_NEEDED 2 /* Initiate ISP abort. */
+#define ABORT_ISP_ACTIVE 3 /* ISP abort in progress. */
+#define LOOP_RESYNC_NEEDED 4 /* Device Resync needed. */
+#define LOOP_RESYNC_ACTIVE 5
+#define LOCAL_LOOP_UPDATE 6 /* Perform a local loop update. */
+#define RSCN_UPDATE 7 /* Perform an RSCN update. */
+#define RELOGIN_NEEDED 8
+#define REGISTER_FC4_NEEDED 9 /* SNS FC4 registration required. */
+#define ISP_ABORT_RETRY 10 /* ISP aborted. */
+#define BEACON_BLINK_NEEDED 11
+#define REGISTER_FDMI_NEEDED 12
+#define FCPORT_UPDATE_NEEDED 13
+#define VP_DPC_NEEDED 14 /* wake up for VP dpc handling */
+#define UNLOADING 15
+#define NPIV_CONFIG_NEEDED 16
+#define ISP_UNRECOVERABLE 17
+#define FCOE_CTX_RESET_NEEDED 18 /* Initiate FCoE context reset */
+#define MPI_RESET_NEEDED 19 /* Initiate MPI FW reset */
+#define ISP_QUIESCE_NEEDED 20 /* Driver need some quiescence */
+
+ uint32_t device_flags;
+#define SWITCH_FOUND BIT_0
+#define DFLG_NO_CABLE BIT_1
+#define DFLG_DEV_FAILED BIT_5
+
+ /* ISP configuration data. */
+ uint16_t loop_id; /* Host adapter loop id */
+
+ port_id_t d_id; /* Host adapter port id */
+ uint8_t marker_needed;
+ uint16_t mgmt_svr_loop_id;
+
+
+
+ /* Timeout timers. */
+ uint8_t loop_down_abort_time; /* port down timer */
+ atomic_t loop_down_timer; /* loop down timer */
+ uint8_t link_down_timeout; /* link down timeout */
+
+ uint32_t timer_active;
+ struct timer_list timer;
+
+ uint8_t node_name[WWN_SIZE];
+ uint8_t port_name[WWN_SIZE];
+ uint8_t fabric_node_name[WWN_SIZE];
+
+ uint16_t fcoe_vlan_id;
+ uint16_t fcoe_fcf_idx;
+ uint8_t fcoe_vn_port_mac[6];
+
+ uint32_t vp_abort_cnt;
+
+ struct fc_vport *fc_vport; /* holds fc_vport * for each vport */
+ uint16_t vp_idx; /* vport ID */
+
+ unsigned long vp_flags;
+#define VP_IDX_ACQUIRED 0 /* bit no 0 */
+#define VP_CREATE_NEEDED 1
+#define VP_BIND_NEEDED 2
+#define VP_DELETE_NEEDED 3
+#define VP_SCR_NEEDED 4 /* State Change Request registration */
+ atomic_t vp_state;
+#define VP_OFFLINE 0
+#define VP_ACTIVE 1
+#define VP_FAILED 2
+// #define VP_DISABLE 3
+ uint16_t vp_err_state;
+ uint16_t vp_prev_err_state;
+#define VP_ERR_UNKWN 0
+#define VP_ERR_PORTDWN 1
+#define VP_ERR_FAB_UNSUPPORTED 2
+#define VP_ERR_FAB_NORESOURCES 3
+#define VP_ERR_FAB_LOGOUT 4
+#define VP_ERR_ADAP_NORESOURCES 5
+ struct qla_hw_data *hw;
+ struct req_que *req;
+ int fw_heartbeat_counter;
+ int seconds_since_last_heartbeat;
+
+ atomic_t vref_count;
+} scsi_qla_host_t;
+
+/*
+ * Macros to help code, maintain, etc.
+ */
+#define LOOP_TRANSITION(ha) \
+ (test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) || \
+ test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags) || \
+ atomic_read(&ha->loop_state) == LOOP_DOWN)
+
+#define QLA_VHA_MARK_BUSY(__vha, __bail) do { \
+ atomic_inc(&__vha->vref_count); \
+ mb(); \
+ if (__vha->flags.delete_progress) { \
+ atomic_dec(&__vha->vref_count); \
+ __bail = 1; \
+ } else { \
+ __bail = 0; \
+ } \
+} while (0)
+
+#define QLA_VHA_MARK_NOT_BUSY(__vha) do { \
+ atomic_dec(&__vha->vref_count); \
+} while (0)
+
+/*
+ * qla2x00 local function return status codes
+ */
+#define MBS_MASK 0x3fff
+
+#define QLA_SUCCESS (MBS_COMMAND_COMPLETE & MBS_MASK)
+#define QLA_INVALID_COMMAND (MBS_INVALID_COMMAND & MBS_MASK)
+#define QLA_INTERFACE_ERROR (MBS_HOST_INTERFACE_ERROR & MBS_MASK)
+#define QLA_TEST_FAILED (MBS_TEST_FAILED & MBS_MASK)
+#define QLA_COMMAND_ERROR (MBS_COMMAND_ERROR & MBS_MASK)
+#define QLA_PARAMETER_ERROR (MBS_COMMAND_PARAMETER_ERROR & MBS_MASK)
+#define QLA_PORT_ID_USED (MBS_PORT_ID_USED & MBS_MASK)
+#define QLA_LOOP_ID_USED (MBS_LOOP_ID_USED & MBS_MASK)
+#define QLA_ALL_IDS_IN_USE (MBS_ALL_IDS_IN_USE & MBS_MASK)
+#define QLA_NOT_LOGGED_IN (MBS_NOT_LOGGED_IN & MBS_MASK)
+
+#define QLA_FUNCTION_TIMEOUT 0x100
+#define QLA_FUNCTION_PARAMETER_ERROR 0x101
+#define QLA_FUNCTION_FAILED 0x102
+#define QLA_MEMORY_ALLOC_FAILED 0x103
+#define QLA_LOCK_TIMEOUT 0x104
+#define QLA_ABORTED 0x105
+#define QLA_SUSPENDED 0x106
+#define QLA_BUSY 0x107
+#define QLA_ALREADY_REGISTERED 0x109
+
+#define NVRAM_DELAY() udelay(10)
+
+#define INVALID_HANDLE (MAX_OUTSTANDING_COMMANDS+1)
+
+/*
+ * Flash support definitions
+ */
+#define OPTROM_SIZE_2300 0x20000
+#define OPTROM_SIZE_2322 0x100000
+#define OPTROM_SIZE_24XX 0x100000
+#define OPTROM_SIZE_25XX 0x200000
+#define OPTROM_SIZE_81XX 0x400000
+#define OPTROM_SIZE_82XX 0x800000
+#define OPTROM_SIZE_83XX 0x1000000
+
+#define OPTROM_BURST_SIZE 0x1000
+#define OPTROM_BURST_DWORDS (OPTROM_BURST_SIZE / 4)
+
+#define QLA_DSDS_PER_IOCB 37
+
+#define CMD_SP(Cmnd) ((Cmnd)->SCp.ptr)
+
+#define QLA_SG_ALL 1024
+
+enum nexus_wait_type {
+ WAIT_HOST = 0,
+ WAIT_TARGET,
+ WAIT_LUN,
+};
+
+#include "qla_gbl.h"
+#include "qla_dbg.h"
+#include "qla_inline.h"
+#endif
diff --git a/drivers/scsi/qla2xxx/qla_devtbl.h b/drivers/scsi/qla2xxx/qla_devtbl.h
new file mode 100644
index 00000000..d6ea69df
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_devtbl.h
@@ -0,0 +1,99 @@
+#define QLA_MODEL_NAMES 0x5C
+
+/*
+ * Adapter model names and descriptions.
+ */
+static char *qla2x00_model_name[QLA_MODEL_NAMES*2] = {
+ "QLA2340", "133MHz PCI-X to 2Gb FC, Single Channel", /* 0x100 */
+ "QLA2342", "133MHz PCI-X to 2Gb FC, Dual Channel", /* 0x101 */
+ "QLA2344", "133MHz PCI-X to 2Gb FC, Quad Channel", /* 0x102 */
+ "QCP2342", "cPCI to 2Gb FC, Dual Channel", /* 0x103 */
+ "QSB2340", "SBUS to 2Gb FC, Single Channel", /* 0x104 */
+ "QSB2342", "SBUS to 2Gb FC, Dual Channel", /* 0x105 */
+ "QLA2310", "Sun 66MHz PCI-X to 2Gb FC, Single Channel", /* 0x106 */
+ "QLA2332", "Sun 66MHz PCI-X to 2Gb FC, Single Channel", /* 0x107 */
+ "QCP2332", "Sun cPCI to 2Gb FC, Dual Channel", /* 0x108 */
+ "QCP2340", "cPCI to 2Gb FC, Single Channel", /* 0x109 */
+ "QLA2342", "Sun 133MHz PCI-X to 2Gb FC, Dual Channel", /* 0x10a */
+ "QCP2342", "Sun - cPCI to 2Gb FC, Dual Channel", /* 0x10b */
+ "QLA2350", "133MHz PCI-X to 2Gb FC, Single Channel", /* 0x10c */
+ "QLA2352", "133MHz PCI-X to 2Gb FC, Dual Channel", /* 0x10d */
+ "QLA2352", "Sun 133MHz PCI-X to 2Gb FC, Dual Channel", /* 0x10e */
+ " ", " ", /* 0x10f */
+ " ", " ", /* 0x110 */
+ " ", " ", /* 0x111 */
+ " ", " ", /* 0x112 */
+ " ", " ", /* 0x113 */
+ " ", " ", /* 0x114 */
+ "QLA2360", "133MHz PCI-X to 2Gb FC, Single Channel", /* 0x115 */
+ "QLA2362", "133MHz PCI-X to 2Gb FC, Dual Channel", /* 0x116 */
+ "QLE2360", "PCI-Express to 2Gb FC, Single Channel", /* 0x117 */
+ "QLE2362", "PCI-Express to 2Gb FC, Dual Channel", /* 0x118 */
+ "QLA200", "133MHz PCI-X to 2Gb FC Optical", /* 0x119 */
+ " ", " ", /* 0x11a */
+ " ", " ", /* 0x11b */
+ "QLA200P", "133MHz PCI-X to 2Gb FC SFP", /* 0x11c */
+ " ", " ", /* 0x11d */
+ " ", " ", /* 0x11e */
+ " ", " ", /* 0x11f */
+ " ", " ", /* 0x120 */
+ " ", " ", /* 0x121 */
+ " ", " ", /* 0x122 */
+ " ", " ", /* 0x123 */
+ " ", " ", /* 0x124 */
+ " ", " ", /* 0x125 */
+ " ", " ", /* 0x126 */
+ " ", " ", /* 0x127 */
+ " ", " ", /* 0x128 */
+ " ", " ", /* 0x129 */
+ " ", " ", /* 0x12a */
+ " ", " ", /* 0x12b */
+ " ", " ", /* 0x12c */
+ " ", " ", /* 0x12d */
+ " ", " ", /* 0x12e */
+ "QLA210", "133MHz PCI-X to 2Gb FC, Single Channel", /* 0x12f */
+ "EMC 250", "133MHz PCI-X to 2Gb FC, Single Channel", /* 0x130 */
+ "HP A7538A", "HP 1p2g PCI-X to 2Gb FC, Single Channel", /* 0x131 */
+ "QLA210", "Sun 133MHz PCI-X to 2Gb FC, Single Channel", /* 0x132 */
+ "QLA2460", "PCI-X 2.0 to 4Gb FC, Single Channel", /* 0x133 */
+ "QLA2462", "PCI-X 2.0 to 4Gb FC, Dual Channel", /* 0x134 */
+ "QMC2462", "IBM eServer BC 4Gb FC Expansion Card", /* 0x135 */
+ "QMC2462S", "IBM eServer BC 4Gb FC Expansion Card SFF", /* 0x136 */
+ "QLE2460", "PCI-Express to 4Gb FC, Single Channel", /* 0x137 */
+ "QLE2462", "PCI-Express to 4Gb FC, Dual Channel", /* 0x138 */
+ "QME2462", "Dell BS PCI-Express to 4Gb FC, Dual Channel", /* 0x139 */
+ " ", " ", /* 0x13a */
+ " ", " ", /* 0x13b */
+ " ", " ", /* 0x13c */
+ "QEM2462", "Sun Server I/O Module 4Gb FC, Dual Channel", /* 0x13d */
+ "QLE210", "PCI-Express to 2Gb FC, Single Channel", /* 0x13e */
+ "QLE220", "PCI-Express to 4Gb FC, Single Channel", /* 0x13f */
+ "QLA2460", "Sun PCI-X 2.0 to 4Gb FC, Single Channel", /* 0x140 */
+ "QLA2462", "Sun PCI-X 2.0 to 4Gb FC, Dual Channel", /* 0x141 */
+ "QLE2460", "Sun PCI-Express to 2Gb FC, Single Channel", /* 0x142 */
+ "QLE2462", "Sun PCI-Express to 4Gb FC, Single Channel", /* 0x143 */
+ "QEM2462", "Server I/O Module 4Gb FC, Dual Channel", /* 0x144 */
+ "QLE2440", "PCI-Express to 4Gb FC, Single Channel", /* 0x145 */
+ "QLE2464", "PCI-Express to 4Gb FC, Quad Channel", /* 0x146 */
+ "QLA2440", "PCI-X 2.0 to 4Gb FC, Single Channel", /* 0x147 */
+ "HP AE369A", "PCI-X 2.0 to 4Gb FC, Dual Channel", /* 0x148 */
+ "QLA2340", "Sun 133MHz PCI-X to 2Gb FC, Single Channel", /* 0x149 */
+ " ", " ", /* 0x14a */
+ " ", " ", /* 0x14b */
+ "QMC2432M", "IBM eServer BC 4Gb FC Expansion Card CFFE", /* 0x14c */
+ "QMC2422M", "IBM eServer BC 4Gb FC Expansion Card CFFX", /* 0x14d */
+ "QLE220", "Sun PCI-Express to 4Gb FC, Single Channel", /* 0x14e */
+ " ", " ", /* 0x14f */
+ " ", " ", /* 0x150 */
+ " ", " ", /* 0x151 */
+ "QME2462", "PCI-Express to 4Gb FC, Dual Channel Mezz HBA", /* 0x152 */
+ "QMH2462", "PCI-Express to 4Gb FC, Dual Channel Mezz HBA", /* 0x153 */
+ " ", " ", /* 0x154 */
+ "QLE220", "PCI-Express to 4Gb FC, Single Channel", /* 0x155 */
+ "QLE220", "PCI-Express to 4Gb FC, Single Channel", /* 0x156 */
+ " ", " ", /* 0x157 */
+ " ", " ", /* 0x158 */
+ " ", " ", /* 0x159 */
+ " ", " ", /* 0x15a */
+ "QME2472", "Dell BS PCI-Express to 4Gb FC, Dual Channel", /* 0x15b */
+};
diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c
new file mode 100644
index 00000000..499c74e3
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_dfs.c
@@ -0,0 +1,181 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2003-2011 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#include "qla_def.h"
+
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+
+static struct dentry *qla2x00_dfs_root;
+static atomic_t qla2x00_dfs_root_count;
+
+static int
+qla2x00_dfs_fce_show(struct seq_file *s, void *unused)
+{
+ scsi_qla_host_t *vha = s->private;
+ uint32_t cnt;
+ uint32_t *fce;
+ uint64_t fce_start;
+ struct qla_hw_data *ha = vha->hw;
+
+ mutex_lock(&ha->fce_mutex);
+
+ seq_printf(s, "FCE Trace Buffer\n");
+ seq_printf(s, "In Pointer = %llx\n\n", (unsigned long long)ha->fce_wr);
+ seq_printf(s, "Base = %llx\n\n", (unsigned long long) ha->fce_dma);
+ seq_printf(s, "FCE Enable Registers\n");
+ seq_printf(s, "%08x %08x %08x %08x %08x %08x\n",
+ ha->fce_mb[0], ha->fce_mb[2], ha->fce_mb[3], ha->fce_mb[4],
+ ha->fce_mb[5], ha->fce_mb[6]);
+
+ fce = (uint32_t *) ha->fce;
+ fce_start = (unsigned long long) ha->fce_dma;
+ for (cnt = 0; cnt < fce_calc_size(ha->fce_bufs) / 4; cnt++) {
+ if (cnt % 8 == 0)
+ seq_printf(s, "\n%llx: ",
+ (unsigned long long)((cnt * 4) + fce_start));
+ else
+ seq_printf(s, " ");
+ seq_printf(s, "%08x", *fce++);
+ }
+
+ seq_printf(s, "\nEnd\n");
+
+ mutex_unlock(&ha->fce_mutex);
+
+ return 0;
+}
+
+static int
+qla2x00_dfs_fce_open(struct inode *inode, struct file *file)
+{
+ scsi_qla_host_t *vha = inode->i_private;
+ struct qla_hw_data *ha = vha->hw;
+ int rval;
+
+ if (!ha->flags.fce_enabled)
+ goto out;
+
+ mutex_lock(&ha->fce_mutex);
+
+ /* Pause tracing to flush FCE buffers. */
+ rval = qla2x00_disable_fce_trace(vha, &ha->fce_wr, &ha->fce_rd);
+ if (rval)
+ ql_dbg(ql_dbg_user, vha, 0x705c,
+ "DebugFS: Unable to disable FCE (%d).\n", rval);
+
+ ha->flags.fce_enabled = 0;
+
+ mutex_unlock(&ha->fce_mutex);
+out:
+ return single_open(file, qla2x00_dfs_fce_show, vha);
+}
+
+static int
+qla2x00_dfs_fce_release(struct inode *inode, struct file *file)
+{
+ scsi_qla_host_t *vha = inode->i_private;
+ struct qla_hw_data *ha = vha->hw;
+ int rval;
+
+ if (ha->flags.fce_enabled)
+ goto out;
+
+ mutex_lock(&ha->fce_mutex);
+
+ /* Re-enable FCE tracing. */
+ ha->flags.fce_enabled = 1;
+ memset(ha->fce, 0, fce_calc_size(ha->fce_bufs));
+ rval = qla2x00_enable_fce_trace(vha, ha->fce_dma, ha->fce_bufs,
+ ha->fce_mb, &ha->fce_bufs);
+ if (rval) {
+ ql_dbg(ql_dbg_user, vha, 0x700d,
+ "DebugFS: Unable to reinitialize FCE (%d).\n", rval);
+ ha->flags.fce_enabled = 0;
+ }
+
+ mutex_unlock(&ha->fce_mutex);
+out:
+ return single_release(inode, file);
+}
+
+static const struct file_operations dfs_fce_ops = {
+ .open = qla2x00_dfs_fce_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = qla2x00_dfs_fce_release,
+};
+
+int
+qla2x00_dfs_setup(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha))
+ goto out;
+ if (!ha->fce)
+ goto out;
+
+ if (qla2x00_dfs_root)
+ goto create_dir;
+
+ atomic_set(&qla2x00_dfs_root_count, 0);
+ qla2x00_dfs_root = debugfs_create_dir(QLA2XXX_DRIVER_NAME, NULL);
+ if (!qla2x00_dfs_root) {
+ ql_log(ql_log_warn, vha, 0x00f7,
+ "Unable to create debugfs root directory.\n");
+ goto out;
+ }
+
+create_dir:
+ if (ha->dfs_dir)
+ goto create_nodes;
+
+ mutex_init(&ha->fce_mutex);
+ ha->dfs_dir = debugfs_create_dir(vha->host_str, qla2x00_dfs_root);
+ if (!ha->dfs_dir) {
+ ql_log(ql_log_warn, vha, 0x00f8,
+ "Unable to create debugfs ha directory.\n");
+ goto out;
+ }
+
+ atomic_inc(&qla2x00_dfs_root_count);
+
+create_nodes:
+ ha->dfs_fce = debugfs_create_file("fce", S_IRUSR, ha->dfs_dir, vha,
+ &dfs_fce_ops);
+ if (!ha->dfs_fce) {
+ ql_log(ql_log_warn, vha, 0x00f9,
+ "Unable to create debugfs fce node.\n");
+ goto out;
+ }
+out:
+ return 0;
+}
+
+int
+qla2x00_dfs_remove(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ if (ha->dfs_fce) {
+ debugfs_remove(ha->dfs_fce);
+ ha->dfs_fce = NULL;
+ }
+
+ if (ha->dfs_dir) {
+ debugfs_remove(ha->dfs_dir);
+ ha->dfs_dir = NULL;
+ atomic_dec(&qla2x00_dfs_root_count);
+ }
+
+ if (atomic_read(&qla2x00_dfs_root_count) == 0 &&
+ qla2x00_dfs_root) {
+ debugfs_remove(qla2x00_dfs_root);
+ qla2x00_dfs_root = NULL;
+ }
+
+ return 0;
+}
diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h
new file mode 100644
index 00000000..6d7d7758
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_fw.h
@@ -0,0 +1,1891 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2003-2011 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#ifndef __QLA_FW_H
+#define __QLA_FW_H
+
+#define MBS_CHECKSUM_ERROR 0x4010
+#define MBS_INVALID_PRODUCT_KEY 0x4020
+
+/*
+ * Firmware Options.
+ */
+#define FO1_ENABLE_PUREX BIT_10
+#define FO1_DISABLE_LED_CTRL BIT_6
+#define FO1_ENABLE_8016 BIT_0
+#define FO2_ENABLE_SEL_CLASS2 BIT_5
+#define FO3_NO_ABTS_ON_LINKDOWN BIT_14
+#define FO3_HOLD_STS_IOCB BIT_12
+
+/*
+ * Port Database structure definition for ISP 24xx.
+ */
+#define PDO_FORCE_ADISC BIT_1
+#define PDO_FORCE_PLOGI BIT_0
+
+
+#define PORT_DATABASE_24XX_SIZE 64
+struct port_database_24xx {
+ uint16_t flags;
+#define PDF_TASK_RETRY_ID BIT_14
+#define PDF_FC_TAPE BIT_7
+#define PDF_ACK0_CAPABLE BIT_6
+#define PDF_FCP2_CONF BIT_5
+#define PDF_CLASS_2 BIT_4
+#define PDF_HARD_ADDR BIT_1
+
+ uint8_t current_login_state;
+ uint8_t last_login_state;
+#define PDS_PLOGI_PENDING 0x03
+#define PDS_PLOGI_COMPLETE 0x04
+#define PDS_PRLI_PENDING 0x05
+#define PDS_PRLI_COMPLETE 0x06
+#define PDS_PORT_UNAVAILABLE 0x07
+#define PDS_PRLO_PENDING 0x09
+#define PDS_LOGO_PENDING 0x11
+#define PDS_PRLI2_PENDING 0x12
+
+ uint8_t hard_address[3];
+ uint8_t reserved_1;
+
+ uint8_t port_id[3];
+ uint8_t sequence_id;
+
+ uint16_t port_timer;
+
+ uint16_t nport_handle; /* N_PORT handle. */
+
+ uint16_t receive_data_size;
+ uint16_t reserved_2;
+
+ uint8_t prli_svc_param_word_0[2]; /* Big endian */
+ /* Bits 15-0 of word 0 */
+ uint8_t prli_svc_param_word_3[2]; /* Big endian */
+ /* Bits 15-0 of word 3 */
+
+ uint8_t port_name[WWN_SIZE];
+ uint8_t node_name[WWN_SIZE];
+
+ uint8_t reserved_3[24];
+};
+
+struct vp_database_24xx {
+ uint16_t vp_status;
+ uint8_t options;
+ uint8_t id;
+ uint8_t port_name[WWN_SIZE];
+ uint8_t node_name[WWN_SIZE];
+ uint16_t port_id_low;
+ uint16_t port_id_high;
+};
+
+struct nvram_24xx {
+ /* NVRAM header. */
+ uint8_t id[4];
+ uint16_t nvram_version;
+ uint16_t reserved_0;
+
+ /* Firmware Initialization Control Block. */
+ uint16_t version;
+ uint16_t reserved_1;
+ uint16_t frame_payload_size;
+ uint16_t execution_throttle;
+ uint16_t exchange_count;
+ uint16_t hard_address;
+
+ uint8_t port_name[WWN_SIZE];
+ uint8_t node_name[WWN_SIZE];
+
+ uint16_t login_retry_count;
+ uint16_t link_down_on_nos;
+ uint16_t interrupt_delay_timer;
+ uint16_t login_timeout;
+
+ uint32_t firmware_options_1;
+ uint32_t firmware_options_2;
+ uint32_t firmware_options_3;
+
+ /* Offset 56. */
+
+ /*
+ * BIT 0 = Control Enable
+ * BIT 1-15 =
+ *
+ * BIT 0-7 = Reserved
+ * BIT 8-10 = Output Swing 1G
+ * BIT 11-13 = Output Emphasis 1G
+ * BIT 14-15 = Reserved
+ *
+ * BIT 0-7 = Reserved
+ * BIT 8-10 = Output Swing 2G
+ * BIT 11-13 = Output Emphasis 2G
+ * BIT 14-15 = Reserved
+ *
+ * BIT 0-7 = Reserved
+ * BIT 8-10 = Output Swing 4G
+ * BIT 11-13 = Output Emphasis 4G
+ * BIT 14-15 = Reserved
+ */
+ uint16_t seriallink_options[4];
+
+ uint16_t reserved_2[16];
+
+ /* Offset 96. */
+ uint16_t reserved_3[16];
+
+ /* PCIe table entries. */
+ uint16_t reserved_4[16];
+
+ /* Offset 160. */
+ uint16_t reserved_5[16];
+
+ /* Offset 192. */
+ uint16_t reserved_6[16];
+
+ /* Offset 224. */
+ uint16_t reserved_7[16];
+
+ /*
+ * BIT 0 = Enable spinup delay
+ * BIT 1 = Disable BIOS
+ * BIT 2 = Enable Memory Map BIOS
+ * BIT 3 = Enable Selectable Boot
+ * BIT 4 = Disable RISC code load
+ * BIT 5 = Disable Serdes
+ * BIT 6 =
+ * BIT 7 =
+ *
+ * BIT 8 =
+ * BIT 9 =
+ * BIT 10 = Enable lip full login
+ * BIT 11 = Enable target reset
+ * BIT 12 =
+ * BIT 13 =
+ * BIT 14 =
+ * BIT 15 = Enable alternate WWN
+ *
+ * BIT 16-31 =
+ */
+ uint32_t host_p;
+
+ uint8_t alternate_port_name[WWN_SIZE];
+ uint8_t alternate_node_name[WWN_SIZE];
+
+ uint8_t boot_port_name[WWN_SIZE];
+ uint16_t boot_lun_number;
+ uint16_t reserved_8;
+
+ uint8_t alt1_boot_port_name[WWN_SIZE];
+ uint16_t alt1_boot_lun_number;
+ uint16_t reserved_9;
+
+ uint8_t alt2_boot_port_name[WWN_SIZE];
+ uint16_t alt2_boot_lun_number;
+ uint16_t reserved_10;
+
+ uint8_t alt3_boot_port_name[WWN_SIZE];
+ uint16_t alt3_boot_lun_number;
+ uint16_t reserved_11;
+
+ /*
+ * BIT 0 = Selective Login
+ * BIT 1 = Alt-Boot Enable
+ * BIT 2 = Reserved
+ * BIT 3 = Boot Order List
+ * BIT 4 = Reserved
+ * BIT 5 = Selective LUN
+ * BIT 6 = Reserved
+ * BIT 7-31 =
+ */
+ uint32_t efi_parameters;
+
+ uint8_t reset_delay;
+ uint8_t reserved_12;
+ uint16_t reserved_13;
+
+ uint16_t boot_id_number;
+ uint16_t reserved_14;
+
+ uint16_t max_luns_per_target;
+ uint16_t reserved_15;
+
+ uint16_t port_down_retry_count;
+ uint16_t link_down_timeout;
+
+ /* FCode parameters. */
+ uint16_t fcode_parameter;
+
+ uint16_t reserved_16[3];
+
+ /* Offset 352. */
+ uint8_t prev_drv_ver_major;
+ uint8_t prev_drv_ver_submajob;
+ uint8_t prev_drv_ver_minor;
+ uint8_t prev_drv_ver_subminor;
+
+ uint16_t prev_bios_ver_major;
+ uint16_t prev_bios_ver_minor;
+
+ uint16_t prev_efi_ver_major;
+ uint16_t prev_efi_ver_minor;
+
+ uint16_t prev_fw_ver_major;
+ uint8_t prev_fw_ver_minor;
+ uint8_t prev_fw_ver_subminor;
+
+ uint16_t reserved_17[8];
+
+ /* Offset 384. */
+ uint16_t reserved_18[16];
+
+ /* Offset 416. */
+ uint16_t reserved_19[16];
+
+ /* Offset 448. */
+ uint16_t reserved_20[16];
+
+ /* Offset 480. */
+ uint8_t model_name[16];
+
+ uint16_t reserved_21[2];
+
+ /* Offset 500. */
+ /* HW Parameter Block. */
+ uint16_t pcie_table_sig;
+ uint16_t pcie_table_offset;
+
+ uint16_t subsystem_vendor_id;
+ uint16_t subsystem_device_id;
+
+ uint32_t checksum;
+};
+
+/*
+ * ISP Initialization Control Block.
+ * Little endian except where noted.
+ */
+#define ICB_VERSION 1
+struct init_cb_24xx {
+ uint16_t version;
+ uint16_t reserved_1;
+
+ uint16_t frame_payload_size;
+ uint16_t execution_throttle;
+ uint16_t exchange_count;
+
+ uint16_t hard_address;
+
+ uint8_t port_name[WWN_SIZE]; /* Big endian. */
+ uint8_t node_name[WWN_SIZE]; /* Big endian. */
+
+ uint16_t response_q_inpointer;
+ uint16_t request_q_outpointer;
+
+ uint16_t login_retry_count;
+
+ uint16_t prio_request_q_outpointer;
+
+ uint16_t response_q_length;
+ uint16_t request_q_length;
+
+ uint16_t link_down_on_nos; /* Milliseconds. */
+
+ uint16_t prio_request_q_length;
+
+ uint32_t request_q_address[2];
+ uint32_t response_q_address[2];
+ uint32_t prio_request_q_address[2];
+
+ uint16_t msix;
+ uint8_t reserved_2[6];
+
+ uint16_t atio_q_inpointer;
+ uint16_t atio_q_length;
+ uint32_t atio_q_address[2];
+
+ uint16_t interrupt_delay_timer; /* 100us increments. */
+ uint16_t login_timeout;
+
+ /*
+ * BIT 0 = Enable Hard Loop Id
+ * BIT 1 = Enable Fairness
+ * BIT 2 = Enable Full-Duplex
+ * BIT 3 = Reserved
+ * BIT 4 = Enable Target Mode
+ * BIT 5 = Disable Initiator Mode
+ * BIT 6 = Reserved
+ * BIT 7 = Reserved
+ *
+ * BIT 8 = Reserved
+ * BIT 9 = Non Participating LIP
+ * BIT 10 = Descending Loop ID Search
+ * BIT 11 = Acquire Loop ID in LIPA
+ * BIT 12 = Reserved
+ * BIT 13 = Full Login after LIP
+ * BIT 14 = Node Name Option
+ * BIT 15-31 = Reserved
+ */
+ uint32_t firmware_options_1;
+
+ /*
+ * BIT 0 = Operation Mode bit 0
+ * BIT 1 = Operation Mode bit 1
+ * BIT 2 = Operation Mode bit 2
+ * BIT 3 = Operation Mode bit 3
+ * BIT 4 = Connection Options bit 0
+ * BIT 5 = Connection Options bit 1
+ * BIT 6 = Connection Options bit 2
+ * BIT 7 = Enable Non part on LIHA failure
+ *
+ * BIT 8 = Enable Class 2
+ * BIT 9 = Enable ACK0
+ * BIT 10 = Reserved
+ * BIT 11 = Enable FC-SP Security
+ * BIT 12 = FC Tape Enable
+ * BIT 13 = Reserved
+ * BIT 14 = Enable Target PRLI Control
+ * BIT 15-31 = Reserved
+ */
+ uint32_t firmware_options_2;
+
+ /*
+ * BIT 0 = Reserved
+ * BIT 1 = Soft ID only
+ * BIT 2 = Reserved
+ * BIT 3 = Reserved
+ * BIT 4 = FCP RSP Payload bit 0
+ * BIT 5 = FCP RSP Payload bit 1
+ * BIT 6 = Enable Receive Out-of-Order data frame handling
+ * BIT 7 = Disable Automatic PLOGI on Local Loop
+ *
+ * BIT 8 = Reserved
+ * BIT 9 = Enable Out-of-Order FCP_XFER_RDY relative offset handling
+ * BIT 10 = Reserved
+ * BIT 11 = Reserved
+ * BIT 12 = Reserved
+ * BIT 13 = Data Rate bit 0
+ * BIT 14 = Data Rate bit 1
+ * BIT 15 = Data Rate bit 2
+ * BIT 16 = Enable 75 ohm Termination Select
+ * BIT 17-31 = Reserved
+ */
+ uint32_t firmware_options_3;
+ uint16_t qos;
+ uint16_t rid;
+ uint8_t reserved_3[20];
+};
+
+/*
+ * ISP queue - command entry structure definition.
+ */
+#define COMMAND_TYPE_6 0x48 /* Command Type 6 entry */
+struct cmd_type_6 {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ uint16_t nport_handle; /* N_PORT handle. */
+ uint16_t timeout; /* Command timeout. */
+
+ uint16_t dseg_count; /* Data segment count. */
+
+ uint16_t fcp_rsp_dsd_len; /* FCP_RSP DSD length. */
+
+ struct scsi_lun lun; /* FCP LUN (BE). */
+
+ uint16_t control_flags; /* Control flags. */
+#define CF_DIF_SEG_DESCR_ENABLE BIT_3
+#define CF_DATA_SEG_DESCR_ENABLE BIT_2
+#define CF_READ_DATA BIT_1
+#define CF_WRITE_DATA BIT_0
+
+ uint16_t fcp_cmnd_dseg_len; /* Data segment length. */
+ uint32_t fcp_cmnd_dseg_address[2]; /* Data segment address. */
+
+ uint32_t fcp_rsp_dseg_address[2]; /* Data segment address. */
+
+ uint32_t byte_count; /* Total byte count. */
+
+ uint8_t port_id[3]; /* PortID of destination port. */
+ uint8_t vp_index;
+
+ uint32_t fcp_data_dseg_address[2]; /* Data segment address. */
+ uint32_t fcp_data_dseg_len; /* Data segment length. */
+};
+
+#define COMMAND_TYPE_7 0x18 /* Command Type 7 entry */
+struct cmd_type_7 {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ uint16_t nport_handle; /* N_PORT handle. */
+ uint16_t timeout; /* Command timeout. */
+#define FW_MAX_TIMEOUT 0x1999
+
+ uint16_t dseg_count; /* Data segment count. */
+ uint16_t reserved_1;
+
+ struct scsi_lun lun; /* FCP LUN (BE). */
+
+ uint16_t task_mgmt_flags; /* Task management flags. */
+#define TMF_CLEAR_ACA BIT_14
+#define TMF_TARGET_RESET BIT_13
+#define TMF_LUN_RESET BIT_12
+#define TMF_CLEAR_TASK_SET BIT_10
+#define TMF_ABORT_TASK_SET BIT_9
+#define TMF_DSD_LIST_ENABLE BIT_2
+#define TMF_READ_DATA BIT_1
+#define TMF_WRITE_DATA BIT_0
+
+ uint8_t task;
+#define TSK_SIMPLE 0
+#define TSK_HEAD_OF_QUEUE 1
+#define TSK_ORDERED 2
+#define TSK_ACA 4
+#define TSK_UNTAGGED 5
+
+ uint8_t crn;
+
+ uint8_t fcp_cdb[MAX_CMDSZ]; /* SCSI command words. */
+ uint32_t byte_count; /* Total byte count. */
+
+ uint8_t port_id[3]; /* PortID of destination port. */
+ uint8_t vp_index;
+
+ uint32_t dseg_0_address[2]; /* Data segment 0 address. */
+ uint32_t dseg_0_len; /* Data segment 0 length. */
+};
+
+#define COMMAND_TYPE_CRC_2 0x6A /* Command Type CRC_2 (Type 6)
+ * (T10-DIF) */
+struct cmd_type_crc_2 {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ uint16_t nport_handle; /* N_PORT handle. */
+ uint16_t timeout; /* Command timeout. */
+
+ uint16_t dseg_count; /* Data segment count. */
+
+ uint16_t fcp_rsp_dseg_len; /* FCP_RSP DSD length. */
+
+ struct scsi_lun lun; /* FCP LUN (BE). */
+
+ uint16_t control_flags; /* Control flags. */
+
+ uint16_t fcp_cmnd_dseg_len; /* Data segment length. */
+ uint32_t fcp_cmnd_dseg_address[2]; /* Data segment address. */
+
+ uint32_t fcp_rsp_dseg_address[2]; /* Data segment address. */
+
+ uint32_t byte_count; /* Total byte count. */
+
+ uint8_t port_id[3]; /* PortID of destination port. */
+ uint8_t vp_index;
+
+ uint32_t crc_context_address[2]; /* Data segment address. */
+ uint16_t crc_context_len; /* Data segment length. */
+ uint16_t reserved_1; /* MUST be set to 0. */
+};
+
+
+/*
+ * ISP queue - status entry structure definition.
+ */
+#define STATUS_TYPE 0x03 /* Status entry. */
+struct sts_entry_24xx {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ uint16_t comp_status; /* Completion status. */
+ uint16_t ox_id; /* OX_ID used by the firmware. */
+
+ uint32_t residual_len; /* FW calc residual transfer length. */
+
+ uint16_t reserved_1;
+ uint16_t state_flags; /* State flags. */
+#define SF_TRANSFERRED_DATA BIT_11
+#define SF_FCP_RSP_DMA BIT_0
+
+ uint16_t reserved_2;
+ uint16_t scsi_status; /* SCSI status. */
+#define SS_CONFIRMATION_REQ BIT_12
+
+ uint32_t rsp_residual_count; /* FCP RSP residual count. */
+
+ uint32_t sense_len; /* FCP SENSE length. */
+ uint32_t rsp_data_len; /* FCP response data length. */
+ uint8_t data[28]; /* FCP response/sense information. */
+ /*
+ * If DIF Error is set in comp_status, these additional fields are
+ * defined:
+ *
+ * !!! NOTE: Firmware sends expected/actual DIF data in big endian
+ * format; but all of the "data" field gets swab32-d in the beginning
+ * of qla2x00_status_entry().
+ *
+ * &data[10] : uint8_t report_runt_bg[2]; - computed guard
+ * &data[12] : uint8_t actual_dif[8]; - DIF Data received
+ * &data[20] : uint8_t expected_dif[8]; - DIF Data computed
+ */
+};
+
+
+/*
+ * Status entry completion status
+ */
+#define CS_DATA_REASSEMBLY_ERROR 0x11 /* Data Reassembly Error.. */
+#define CS_ABTS_BY_TARGET 0x13 /* Target send ABTS to abort IOCB. */
+#define CS_FW_RESOURCE 0x2C /* Firmware Resource Unavailable. */
+#define CS_TASK_MGMT_OVERRUN 0x30 /* Task management overrun (8+). */
+#define CS_ABORT_BY_TARGET 0x47 /* Abort By Target. */
+
+/*
+ * ISP queue - marker entry structure definition.
+ */
+#define MARKER_TYPE 0x04 /* Marker entry. */
+struct mrk_entry_24xx {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t handle_count; /* Handle count. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ uint16_t nport_handle; /* N_PORT handle. */
+
+ uint8_t modifier; /* Modifier (7-0). */
+#define MK_SYNC_ID_LUN 0 /* Synchronize ID/LUN */
+#define MK_SYNC_ID 1 /* Synchronize ID */
+#define MK_SYNC_ALL 2 /* Synchronize all ID/LUN */
+ uint8_t reserved_1;
+
+ uint8_t reserved_2;
+ uint8_t vp_index;
+
+ uint16_t reserved_3;
+
+ uint8_t lun[8]; /* FCP LUN (BE). */
+ uint8_t reserved_4[40];
+};
+
+/*
+ * ISP queue - CT Pass-Through entry structure definition.
+ */
+#define CT_IOCB_TYPE 0x29 /* CT Pass-Through IOCB entry */
+struct ct_entry_24xx {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System Defined. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ uint16_t comp_status; /* Completion status. */
+
+ uint16_t nport_handle; /* N_PORT handle. */
+
+ uint16_t cmd_dsd_count;
+
+ uint8_t vp_index;
+ uint8_t reserved_1;
+
+ uint16_t timeout; /* Command timeout. */
+ uint16_t reserved_2;
+
+ uint16_t rsp_dsd_count;
+
+ uint8_t reserved_3[10];
+
+ uint32_t rsp_byte_count;
+ uint32_t cmd_byte_count;
+
+ uint32_t dseg_0_address[2]; /* Data segment 0 address. */
+ uint32_t dseg_0_len; /* Data segment 0 length. */
+ uint32_t dseg_1_address[2]; /* Data segment 1 address. */
+ uint32_t dseg_1_len; /* Data segment 1 length. */
+};
+
+/*
+ * ISP queue - ELS Pass-Through entry structure definition.
+ */
+#define ELS_IOCB_TYPE 0x53 /* ELS Pass-Through IOCB entry */
+struct els_entry_24xx {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System Defined. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ uint16_t reserved_1;
+
+ uint16_t nport_handle; /* N_PORT handle. */
+
+ uint16_t tx_dsd_count;
+
+ uint8_t vp_index;
+ uint8_t sof_type;
+#define EST_SOFI3 (1 << 4)
+#define EST_SOFI2 (3 << 4)
+
+ uint32_t rx_xchg_address; /* Receive exchange address. */
+ uint16_t rx_dsd_count;
+
+ uint8_t opcode;
+ uint8_t reserved_2;
+
+ uint8_t port_id[3];
+ uint8_t reserved_3;
+
+ uint16_t reserved_4;
+
+ uint16_t control_flags; /* Control flags. */
+#define ECF_PAYLOAD_DESCR_MASK (BIT_15|BIT_14|BIT_13)
+#define EPD_ELS_COMMAND (0 << 13)
+#define EPD_ELS_ACC (1 << 13)
+#define EPD_ELS_RJT (2 << 13)
+#define EPD_RX_XCHG (3 << 13)
+#define ECF_CLR_PASSTHRU_PEND BIT_12
+#define ECF_INCL_FRAME_HDR BIT_11
+
+ uint32_t rx_byte_count;
+ uint32_t tx_byte_count;
+
+ uint32_t tx_address[2]; /* Data segment 0 address. */
+ uint32_t tx_len; /* Data segment 0 length. */
+ uint32_t rx_address[2]; /* Data segment 1 address. */
+ uint32_t rx_len; /* Data segment 1 length. */
+};
+
+struct els_sts_entry_24xx {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System Defined. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ uint16_t comp_status;
+
+ uint16_t nport_handle; /* N_PORT handle. */
+
+ uint16_t reserved_1;
+
+ uint8_t vp_index;
+ uint8_t sof_type;
+
+ uint32_t rx_xchg_address; /* Receive exchange address. */
+ uint16_t reserved_2;
+
+ uint8_t opcode;
+ uint8_t reserved_3;
+
+ uint8_t port_id[3];
+ uint8_t reserved_4;
+
+ uint16_t reserved_5;
+
+ uint16_t control_flags; /* Control flags. */
+ uint32_t total_byte_count;
+ uint32_t error_subcode_1;
+ uint32_t error_subcode_2;
+};
+/*
+ * ISP queue - Mailbox Command entry structure definition.
+ */
+#define MBX_IOCB_TYPE 0x39
+struct mbx_entry_24xx {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t handle_count; /* Handle count. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ uint16_t mbx[28];
+};
+
+
+#define LOGINOUT_PORT_IOCB_TYPE 0x52 /* Login/Logout Port entry. */
+struct logio_entry_24xx {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ uint16_t comp_status; /* Completion status. */
+#define CS_LOGIO_ERROR 0x31 /* Login/Logout IOCB error. */
+
+ uint16_t nport_handle; /* N_PORT handle. */
+
+ uint16_t control_flags; /* Control flags. */
+ /* Modifiers. */
+#define LCF_INCLUDE_SNS BIT_10 /* Include SNS (FFFFFC) during LOGO. */
+#define LCF_FCP2_OVERRIDE BIT_9 /* Set/Reset word 3 of PRLI. */
+#define LCF_CLASS_2 BIT_8 /* Enable class 2 during PLOGI. */
+#define LCF_FREE_NPORT BIT_7 /* Release NPORT handle after LOGO. */
+#define LCF_EXPL_LOGO BIT_6 /* Perform an explicit LOGO. */
+#define LCF_SKIP_PRLI BIT_5 /* Skip PRLI after PLOGI. */
+#define LCF_IMPL_LOGO_ALL BIT_5 /* Implicit LOGO to all ports. */
+#define LCF_COND_PLOGI BIT_4 /* PLOGI only if not logged-in. */
+#define LCF_IMPL_LOGO BIT_4 /* Perform an implicit LOGO. */
+#define LCF_IMPL_PRLO BIT_4 /* Perform an implicit PRLO. */
+ /* Commands. */
+#define LCF_COMMAND_PLOGI 0x00 /* PLOGI. */
+#define LCF_COMMAND_PRLI 0x01 /* PRLI. */
+#define LCF_COMMAND_PDISC 0x02 /* PDISC. */
+#define LCF_COMMAND_ADISC 0x03 /* ADISC. */
+#define LCF_COMMAND_LOGO 0x08 /* LOGO. */
+#define LCF_COMMAND_PRLO 0x09 /* PRLO. */
+#define LCF_COMMAND_TPRLO 0x0A /* TPRLO. */
+
+ uint8_t vp_index;
+ uint8_t reserved_1;
+
+ uint8_t port_id[3]; /* PortID of destination port. */
+
+ uint8_t rsp_size; /* Response size in 32bit words. */
+
+ uint32_t io_parameter[11]; /* General I/O parameters. */
+#define LSC_SCODE_NOLINK 0x01
+#define LSC_SCODE_NOIOCB 0x02
+#define LSC_SCODE_NOXCB 0x03
+#define LSC_SCODE_CMD_FAILED 0x04
+#define LSC_SCODE_NOFABRIC 0x05
+#define LSC_SCODE_FW_NOT_READY 0x07
+#define LSC_SCODE_NOT_LOGGED_IN 0x09
+#define LSC_SCODE_NOPCB 0x0A
+
+#define LSC_SCODE_ELS_REJECT 0x18
+#define LSC_SCODE_CMD_PARAM_ERR 0x19
+#define LSC_SCODE_PORTID_USED 0x1A
+#define LSC_SCODE_NPORT_USED 0x1B
+#define LSC_SCODE_NONPORT 0x1C
+#define LSC_SCODE_LOGGED_IN 0x1D
+#define LSC_SCODE_NOFLOGI_ACC 0x1F
+};
+
+#define TSK_MGMT_IOCB_TYPE 0x14
+struct tsk_mgmt_entry {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t handle_count; /* Handle count. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ uint16_t nport_handle; /* N_PORT handle. */
+
+ uint16_t reserved_1;
+
+ uint16_t delay; /* Activity delay in seconds. */
+
+ uint16_t timeout; /* Command timeout. */
+
+ struct scsi_lun lun; /* FCP LUN (BE). */
+
+ uint32_t control_flags; /* Control Flags. */
+#define TCF_NOTMCMD_TO_TARGET BIT_31
+#define TCF_LUN_RESET BIT_4
+#define TCF_ABORT_TASK_SET BIT_3
+#define TCF_CLEAR_TASK_SET BIT_2
+#define TCF_TARGET_RESET BIT_1
+#define TCF_CLEAR_ACA BIT_0
+
+ uint8_t reserved_2[20];
+
+ uint8_t port_id[3]; /* PortID of destination port. */
+ uint8_t vp_index;
+
+ uint8_t reserved_3[12];
+};
+
+#define ABORT_IOCB_TYPE 0x33
+struct abort_entry_24xx {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t handle_count; /* Handle count. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ uint16_t nport_handle; /* N_PORT handle. */
+ /* or Completion status. */
+
+ uint16_t options; /* Options. */
+#define AOF_NO_ABTS BIT_0 /* Do not send any ABTS. */
+
+ uint32_t handle_to_abort; /* System handle to abort. */
+
+ uint16_t req_que_no;
+ uint8_t reserved_1[30];
+
+ uint8_t port_id[3]; /* PortID of destination port. */
+ uint8_t vp_index;
+
+ uint8_t reserved_2[12];
+};
+
+/*
+ * ISP I/O Register Set structure definitions.
+ */
+struct device_reg_24xx {
+ uint32_t flash_addr; /* Flash/NVRAM BIOS address. */
+#define FARX_DATA_FLAG BIT_31
+#define FARX_ACCESS_FLASH_CONF 0x7FFD0000
+#define FARX_ACCESS_FLASH_DATA 0x7FF00000
+#define FARX_ACCESS_NVRAM_CONF 0x7FFF0000
+#define FARX_ACCESS_NVRAM_DATA 0x7FFE0000
+
+#define FA_NVRAM_FUNC0_ADDR 0x80
+#define FA_NVRAM_FUNC1_ADDR 0x180
+
+#define FA_NVRAM_VPD_SIZE 0x200
+#define FA_NVRAM_VPD0_ADDR 0x00
+#define FA_NVRAM_VPD1_ADDR 0x100
+
+#define FA_BOOT_CODE_ADDR 0x00000
+ /*
+ * RISC code begins at offset 512KB
+ * within flash. Consisting of two
+ * contiguous RISC code segments.
+ */
+#define FA_RISC_CODE_ADDR 0x20000
+#define FA_RISC_CODE_SEGMENTS 2
+
+#define FA_FLASH_DESCR_ADDR_24 0x11000
+#define FA_FLASH_LAYOUT_ADDR_24 0x11400
+#define FA_NPIV_CONF0_ADDR_24 0x16000
+#define FA_NPIV_CONF1_ADDR_24 0x17000
+
+#define FA_FW_AREA_ADDR 0x40000
+#define FA_VPD_NVRAM_ADDR 0x48000
+#define FA_FEATURE_ADDR 0x4C000
+#define FA_FLASH_DESCR_ADDR 0x50000
+#define FA_FLASH_LAYOUT_ADDR 0x50400
+#define FA_HW_EVENT0_ADDR 0x54000
+#define FA_HW_EVENT1_ADDR 0x54400
+#define FA_HW_EVENT_SIZE 0x200
+#define FA_HW_EVENT_ENTRY_SIZE 4
+#define FA_NPIV_CONF0_ADDR 0x5C000
+#define FA_NPIV_CONF1_ADDR 0x5D000
+#define FA_FCP_PRIO0_ADDR 0x10000
+#define FA_FCP_PRIO1_ADDR 0x12000
+
+/*
+ * Flash Error Log Event Codes.
+ */
+#define HW_EVENT_RESET_ERR 0xF00B
+#define HW_EVENT_ISP_ERR 0xF020
+#define HW_EVENT_PARITY_ERR 0xF022
+#define HW_EVENT_NVRAM_CHKSUM_ERR 0xF023
+#define HW_EVENT_FLASH_FW_ERR 0xF024
+
+ uint32_t flash_data; /* Flash/NVRAM BIOS data. */
+
+ uint32_t ctrl_status; /* Control/Status. */
+#define CSRX_FLASH_ACCESS_ERROR BIT_18 /* Flash/NVRAM Access Error. */
+#define CSRX_DMA_ACTIVE BIT_17 /* DMA Active status. */
+#define CSRX_DMA_SHUTDOWN BIT_16 /* DMA Shutdown control status. */
+#define CSRX_FUNCTION BIT_15 /* Function number. */
+ /* PCI-X Bus Mode. */
+#define CSRX_PCIX_BUS_MODE_MASK (BIT_11|BIT_10|BIT_9|BIT_8)
+#define PBM_PCI_33MHZ (0 << 8)
+#define PBM_PCIX_M1_66MHZ (1 << 8)
+#define PBM_PCIX_M1_100MHZ (2 << 8)
+#define PBM_PCIX_M1_133MHZ (3 << 8)
+#define PBM_PCIX_M2_66MHZ (5 << 8)
+#define PBM_PCIX_M2_100MHZ (6 << 8)
+#define PBM_PCIX_M2_133MHZ (7 << 8)
+#define PBM_PCI_66MHZ (8 << 8)
+ /* Max Write Burst byte count. */
+#define CSRX_MAX_WRT_BURST_MASK (BIT_5|BIT_4)
+#define MWB_512_BYTES (0 << 4)
+#define MWB_1024_BYTES (1 << 4)
+#define MWB_2048_BYTES (2 << 4)
+#define MWB_4096_BYTES (3 << 4)
+
+#define CSRX_64BIT_SLOT BIT_2 /* PCI 64-Bit Bus Slot. */
+#define CSRX_FLASH_ENABLE BIT_1 /* Flash BIOS Read/Write enable. */
+#define CSRX_ISP_SOFT_RESET BIT_0 /* ISP soft reset. */
+
+ uint32_t ictrl; /* Interrupt control. */
+#define ICRX_EN_RISC_INT BIT_3 /* Enable RISC interrupts on PCI. */
+
+ uint32_t istatus; /* Interrupt status. */
+#define ISRX_RISC_INT BIT_3 /* RISC interrupt. */
+
+ uint32_t unused_1[2]; /* Gap. */
+
+ /* Request Queue. */
+ uint32_t req_q_in; /* In-Pointer. */
+ uint32_t req_q_out; /* Out-Pointer. */
+ /* Response Queue. */
+ uint32_t rsp_q_in; /* In-Pointer. */
+ uint32_t rsp_q_out; /* Out-Pointer. */
+ /* Priority Request Queue. */
+ uint32_t preq_q_in; /* In-Pointer. */
+ uint32_t preq_q_out; /* Out-Pointer. */
+
+ uint32_t unused_2[2]; /* Gap. */
+
+ /* ATIO Queue. */
+ uint32_t atio_q_in; /* In-Pointer. */
+ uint32_t atio_q_out; /* Out-Pointer. */
+
+ uint32_t host_status;
+#define HSRX_RISC_INT BIT_15 /* RISC to Host interrupt. */
+#define HSRX_RISC_PAUSED BIT_8 /* RISC Paused. */
+
+ uint32_t hccr; /* Host command & control register. */
+ /* HCCR statuses. */
+#define HCCRX_HOST_INT BIT_6 /* Host to RISC interrupt bit. */
+#define HCCRX_RISC_RESET BIT_5 /* RISC Reset mode bit. */
+ /* HCCR commands. */
+ /* NOOP. */
+#define HCCRX_NOOP 0x00000000
+ /* Set RISC Reset. */
+#define HCCRX_SET_RISC_RESET 0x10000000
+ /* Clear RISC Reset. */
+#define HCCRX_CLR_RISC_RESET 0x20000000
+ /* Set RISC Pause. */
+#define HCCRX_SET_RISC_PAUSE 0x30000000
+ /* Releases RISC Pause. */
+#define HCCRX_REL_RISC_PAUSE 0x40000000
+ /* Set HOST to RISC interrupt. */
+#define HCCRX_SET_HOST_INT 0x50000000
+ /* Clear HOST to RISC interrupt. */
+#define HCCRX_CLR_HOST_INT 0x60000000
+ /* Clear RISC to PCI interrupt. */
+#define HCCRX_CLR_RISC_INT 0xA0000000
+
+ uint32_t gpiod; /* GPIO Data register. */
+
+ /* LED update mask. */
+#define GPDX_LED_UPDATE_MASK (BIT_20|BIT_19|BIT_18)
+ /* Data update mask. */
+#define GPDX_DATA_UPDATE_MASK (BIT_17|BIT_16)
+ /* Data update mask. */
+#define GPDX_DATA_UPDATE_2_MASK (BIT_28|BIT_27|BIT_26|BIT_17|BIT_16)
+ /* LED control mask. */
+#define GPDX_LED_COLOR_MASK (BIT_4|BIT_3|BIT_2)
+ /* LED bit values. Color names as
+ * referenced in fw spec.
+ */
+#define GPDX_LED_YELLOW_ON BIT_2
+#define GPDX_LED_GREEN_ON BIT_3
+#define GPDX_LED_AMBER_ON BIT_4
+ /* Data in/out. */
+#define GPDX_DATA_INOUT (BIT_1|BIT_0)
+
+ uint32_t gpioe; /* GPIO Enable register. */
+ /* Enable update mask. */
+#define GPEX_ENABLE_UPDATE_MASK (BIT_17|BIT_16)
+ /* Enable update mask. */
+#define GPEX_ENABLE_UPDATE_2_MASK (BIT_28|BIT_27|BIT_26|BIT_17|BIT_16)
+ /* Enable. */
+#define GPEX_ENABLE (BIT_1|BIT_0)
+
+ uint32_t iobase_addr; /* I/O Bus Base Address register. */
+
+ uint32_t unused_3[10]; /* Gap. */
+
+ uint16_t mailbox0;
+ uint16_t mailbox1;
+ uint16_t mailbox2;
+ uint16_t mailbox3;
+ uint16_t mailbox4;
+ uint16_t mailbox5;
+ uint16_t mailbox6;
+ uint16_t mailbox7;
+ uint16_t mailbox8;
+ uint16_t mailbox9;
+ uint16_t mailbox10;
+ uint16_t mailbox11;
+ uint16_t mailbox12;
+ uint16_t mailbox13;
+ uint16_t mailbox14;
+ uint16_t mailbox15;
+ uint16_t mailbox16;
+ uint16_t mailbox17;
+ uint16_t mailbox18;
+ uint16_t mailbox19;
+ uint16_t mailbox20;
+ uint16_t mailbox21;
+ uint16_t mailbox22;
+ uint16_t mailbox23;
+ uint16_t mailbox24;
+ uint16_t mailbox25;
+ uint16_t mailbox26;
+ uint16_t mailbox27;
+ uint16_t mailbox28;
+ uint16_t mailbox29;
+ uint16_t mailbox30;
+ uint16_t mailbox31;
+
+ uint32_t iobase_window;
+ uint32_t iobase_c4;
+ uint32_t iobase_c8;
+ uint32_t unused_4_1[6]; /* Gap. */
+ uint32_t iobase_q;
+ uint32_t unused_5[2]; /* Gap. */
+ uint32_t iobase_select;
+ uint32_t unused_6[2]; /* Gap. */
+ uint32_t iobase_sdata;
+};
+
+/* Trace Control *************************************************************/
+
+#define TC_AEN_DISABLE 0
+
+#define TC_EFT_ENABLE 4
+#define TC_EFT_DISABLE 5
+
+#define TC_FCE_ENABLE 8
+#define TC_FCE_OPTIONS 0
+#define TC_FCE_DEFAULT_RX_SIZE 2112
+#define TC_FCE_DEFAULT_TX_SIZE 2112
+#define TC_FCE_DISABLE 9
+#define TC_FCE_DISABLE_TRACE BIT_0
+
+/* MID Support ***************************************************************/
+
+#define MIN_MULTI_ID_FABRIC 64 /* Must be power-of-2. */
+#define MAX_MULTI_ID_FABRIC 256 /* ... */
+
+#define for_each_mapped_vp_idx(_ha, _idx) \
+ for (_idx = find_next_bit((_ha)->vp_idx_map, \
+ (_ha)->max_npiv_vports + 1, 1); \
+ _idx <= (_ha)->max_npiv_vports; \
+ _idx = find_next_bit((_ha)->vp_idx_map, \
+ (_ha)->max_npiv_vports + 1, _idx + 1)) \
+
+struct mid_conf_entry_24xx {
+ uint16_t reserved_1;
+
+ /*
+ * BIT 0 = Enable Hard Loop Id
+ * BIT 1 = Acquire Loop ID in LIPA
+ * BIT 2 = ID not Acquired
+ * BIT 3 = Enable VP
+ * BIT 4 = Enable Initiator Mode
+ * BIT 5 = Disable Target Mode
+ * BIT 6-7 = Reserved
+ */
+ uint8_t options;
+
+ uint8_t hard_address;
+
+ uint8_t port_name[WWN_SIZE];
+ uint8_t node_name[WWN_SIZE];
+};
+
+struct mid_init_cb_24xx {
+ struct init_cb_24xx init_cb;
+
+ uint16_t count;
+ uint16_t options;
+
+ struct mid_conf_entry_24xx entries[MAX_MULTI_ID_FABRIC];
+};
+
+
+struct mid_db_entry_24xx {
+ uint16_t status;
+#define MDBS_NON_PARTIC BIT_3
+#define MDBS_ID_ACQUIRED BIT_1
+#define MDBS_ENABLED BIT_0
+
+ uint8_t options;
+ uint8_t hard_address;
+
+ uint8_t port_name[WWN_SIZE];
+ uint8_t node_name[WWN_SIZE];
+
+ uint8_t port_id[3];
+ uint8_t reserved_1;
+};
+
+/*
+ * Virtual Port Control IOCB
+ */
+#define VP_CTRL_IOCB_TYPE 0x30 /* Vitual Port Control entry. */
+struct vp_ctrl_entry_24xx {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ uint16_t vp_idx_failed;
+
+ uint16_t comp_status; /* Completion status. */
+#define CS_VCE_IOCB_ERROR 0x01 /* Error processing IOCB */
+#define CS_VCE_ACQ_ID_ERROR 0x02 /* Error while acquireing ID. */
+#define CS_VCE_BUSY 0x05 /* Firmware not ready to accept cmd. */
+
+ uint16_t command;
+#define VCE_COMMAND_ENABLE_VPS 0x00 /* Enable VPs. */
+#define VCE_COMMAND_DISABLE_VPS 0x08 /* Disable VPs. */
+#define VCE_COMMAND_DISABLE_VPS_REINIT 0x09 /* Disable VPs and reinit link. */
+#define VCE_COMMAND_DISABLE_VPS_LOGO 0x0a /* Disable VPs and LOGO ports. */
+#define VCE_COMMAND_DISABLE_VPS_LOGO_ALL 0x0b /* Disable VPs and LOGO ports. */
+
+ uint16_t vp_count;
+
+ uint8_t vp_idx_map[16];
+ uint16_t flags;
+ uint16_t id;
+ uint16_t reserved_4;
+ uint16_t hopct;
+ uint8_t reserved_5[24];
+};
+
+/*
+ * Modify Virtual Port Configuration IOCB
+ */
+#define VP_CONFIG_IOCB_TYPE 0x31 /* Vitual Port Config entry. */
+struct vp_config_entry_24xx {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t handle_count;
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ uint16_t flags;
+#define CS_VF_BIND_VPORTS_TO_VF BIT_0
+#define CS_VF_SET_QOS_OF_VPORTS BIT_1
+#define CS_VF_SET_HOPS_OF_VPORTS BIT_2
+
+ uint16_t comp_status; /* Completion status. */
+#define CS_VCT_STS_ERROR 0x01 /* Specified VPs were not disabled. */
+#define CS_VCT_CNT_ERROR 0x02 /* Invalid VP count. */
+#define CS_VCT_ERROR 0x03 /* Unknown error. */
+#define CS_VCT_IDX_ERROR 0x02 /* Invalid VP index. */
+#define CS_VCT_BUSY 0x05 /* Firmware not ready to accept cmd. */
+
+ uint8_t command;
+#define VCT_COMMAND_MOD_VPS 0x00 /* Modify VP configurations. */
+#define VCT_COMMAND_MOD_ENABLE_VPS 0x01 /* Modify configuration & enable VPs. */
+
+ uint8_t vp_count;
+
+ uint8_t vp_index1;
+ uint8_t vp_index2;
+
+ uint8_t options_idx1;
+ uint8_t hard_address_idx1;
+ uint16_t reserved_vp1;
+ uint8_t port_name_idx1[WWN_SIZE];
+ uint8_t node_name_idx1[WWN_SIZE];
+
+ uint8_t options_idx2;
+ uint8_t hard_address_idx2;
+ uint16_t reserved_vp2;
+ uint8_t port_name_idx2[WWN_SIZE];
+ uint8_t node_name_idx2[WWN_SIZE];
+ uint16_t id;
+ uint16_t reserved_4;
+ uint16_t hopct;
+ uint8_t reserved_5[2];
+};
+
+#define VP_RPT_ID_IOCB_TYPE 0x32 /* Report ID Acquisition entry. */
+struct vp_rpt_id_entry_24xx {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+
+ uint16_t vp_count; /* Format 0 -- | VP setup | VP acq |. */
+ /* Format 1 -- | VP count |. */
+ uint16_t vp_idx; /* Format 0 -- Reserved. */
+ /* Format 1 -- VP status and index. */
+
+ uint8_t port_id[3];
+ uint8_t format;
+
+ uint8_t vp_idx_map[16];
+
+ uint8_t reserved_4[32];
+};
+
+#define VF_EVFP_IOCB_TYPE 0x26 /* Exchange Virtual Fabric Parameters entry. */
+struct vf_evfp_entry_24xx {
+ uint8_t entry_type; /* Entry type. */
+ uint8_t entry_count; /* Entry count. */
+ uint8_t sys_define; /* System defined. */
+ uint8_t entry_status; /* Entry Status. */
+
+ uint32_t handle; /* System handle. */
+ uint16_t comp_status; /* Completion status. */
+ uint16_t timeout; /* timeout */
+ uint16_t adim_tagging_mode;
+
+ uint16_t vfport_id;
+ uint32_t exch_addr;
+
+ uint16_t nport_handle; /* N_PORT handle. */
+ uint16_t control_flags;
+ uint32_t io_parameter_0;
+ uint32_t io_parameter_1;
+ uint32_t tx_address[2]; /* Data segment 0 address. */
+ uint32_t tx_len; /* Data segment 0 length. */
+ uint32_t rx_address[2]; /* Data segment 1 address. */
+ uint32_t rx_len; /* Data segment 1 length. */
+};
+
+/* END MID Support ***********************************************************/
+
+/* Flash Description Table ***************************************************/
+
+struct qla_fdt_layout {
+ uint8_t sig[4];
+ uint16_t version;
+ uint16_t len;
+ uint16_t checksum;
+ uint8_t unused1[2];
+ uint8_t model[16];
+ uint16_t man_id;
+ uint16_t id;
+ uint8_t flags;
+ uint8_t erase_cmd;
+ uint8_t alt_erase_cmd;
+ uint8_t wrt_enable_cmd;
+ uint8_t wrt_enable_bits;
+ uint8_t wrt_sts_reg_cmd;
+ uint8_t unprotect_sec_cmd;
+ uint8_t read_man_id_cmd;
+ uint32_t block_size;
+ uint32_t alt_block_size;
+ uint32_t flash_size;
+ uint32_t wrt_enable_data;
+ uint8_t read_id_addr_len;
+ uint8_t wrt_disable_bits;
+ uint8_t read_dev_id_len;
+ uint8_t chip_erase_cmd;
+ uint16_t read_timeout;
+ uint8_t protect_sec_cmd;
+ uint8_t unused2[65];
+};
+
+/* Flash Layout Table ********************************************************/
+
+struct qla_flt_location {
+ uint8_t sig[4];
+ uint16_t start_lo;
+ uint16_t start_hi;
+ uint8_t version;
+ uint8_t unused[5];
+ uint16_t checksum;
+};
+
+struct qla_flt_header {
+ uint16_t version;
+ uint16_t length;
+ uint16_t checksum;
+ uint16_t unused;
+};
+
+#define FLT_REG_FW 0x01
+#define FLT_REG_BOOT_CODE 0x07
+#define FLT_REG_VPD_0 0x14
+#define FLT_REG_NVRAM_0 0x15
+#define FLT_REG_VPD_1 0x16
+#define FLT_REG_NVRAM_1 0x17
+#define FLT_REG_FDT 0x1a
+#define FLT_REG_FLT 0x1c
+#define FLT_REG_HW_EVENT_0 0x1d
+#define FLT_REG_HW_EVENT_1 0x1f
+#define FLT_REG_NPIV_CONF_0 0x29
+#define FLT_REG_NPIV_CONF_1 0x2a
+#define FLT_REG_GOLD_FW 0x2f
+#define FLT_REG_FCP_PRIO_0 0x87
+#define FLT_REG_FCP_PRIO_1 0x88
+#define FLT_REG_FCOE_FW 0xA4
+#define FLT_REG_FCOE_VPD_0 0xA9
+#define FLT_REG_FCOE_NVRAM_0 0xAA
+#define FLT_REG_FCOE_VPD_1 0xAB
+#define FLT_REG_FCOE_NVRAM_1 0xAC
+
+struct qla_flt_region {
+ uint32_t code;
+ uint32_t size;
+ uint32_t start;
+ uint32_t end;
+};
+
+/* Flash NPIV Configuration Table ********************************************/
+
+struct qla_npiv_header {
+ uint8_t sig[2];
+ uint16_t version;
+ uint16_t entries;
+ uint16_t unused[4];
+ uint16_t checksum;
+};
+
+struct qla_npiv_entry {
+ uint16_t flags;
+ uint16_t vf_id;
+ uint8_t q_qos;
+ uint8_t f_qos;
+ uint16_t unused1;
+ uint8_t port_name[WWN_SIZE];
+ uint8_t node_name[WWN_SIZE];
+};
+
+/* 84XX Support **************************************************************/
+
+#define MBA_ISP84XX_ALERT 0x800f /* Alert Notification. */
+#define A84_PANIC_RECOVERY 0x1
+#define A84_OP_LOGIN_COMPLETE 0x2
+#define A84_DIAG_LOGIN_COMPLETE 0x3
+#define A84_GOLD_LOGIN_COMPLETE 0x4
+
+#define MBC_ISP84XX_RESET 0x3a /* Reset. */
+
+#define FSTATE_REMOTE_FC_DOWN BIT_0
+#define FSTATE_NSL_LINK_DOWN BIT_1
+#define FSTATE_IS_DIAG_FW BIT_2
+#define FSTATE_LOGGED_IN BIT_3
+#define FSTATE_WAITING_FOR_VERIFY BIT_4
+
+#define VERIFY_CHIP_IOCB_TYPE 0x1B
+struct verify_chip_entry_84xx {
+ uint8_t entry_type;
+ uint8_t entry_count;
+ uint8_t sys_defined;
+ uint8_t entry_status;
+
+ uint32_t handle;
+
+ uint16_t options;
+#define VCO_DONT_UPDATE_FW BIT_0
+#define VCO_FORCE_UPDATE BIT_1
+#define VCO_DONT_RESET_UPDATE BIT_2
+#define VCO_DIAG_FW BIT_3
+#define VCO_END_OF_DATA BIT_14
+#define VCO_ENABLE_DSD BIT_15
+
+ uint16_t reserved_1;
+
+ uint16_t data_seg_cnt;
+ uint16_t reserved_2[3];
+
+ uint32_t fw_ver;
+ uint32_t exchange_address;
+
+ uint32_t reserved_3[3];
+ uint32_t fw_size;
+ uint32_t fw_seq_size;
+ uint32_t relative_offset;
+
+ uint32_t dseg_address[2];
+ uint32_t dseg_length;
+};
+
+struct verify_chip_rsp_84xx {
+ uint8_t entry_type;
+ uint8_t entry_count;
+ uint8_t sys_defined;
+ uint8_t entry_status;
+
+ uint32_t handle;
+
+ uint16_t comp_status;
+#define CS_VCS_CHIP_FAILURE 0x3
+#define CS_VCS_BAD_EXCHANGE 0x8
+#define CS_VCS_SEQ_COMPLETEi 0x40
+
+ uint16_t failure_code;
+#define VFC_CHECKSUM_ERROR 0x1
+#define VFC_INVALID_LEN 0x2
+#define VFC_ALREADY_IN_PROGRESS 0x8
+
+ uint16_t reserved_1[4];
+
+ uint32_t fw_ver;
+ uint32_t exchange_address;
+
+ uint32_t reserved_2[6];
+};
+
+#define ACCESS_CHIP_IOCB_TYPE 0x2B
+struct access_chip_84xx {
+ uint8_t entry_type;
+ uint8_t entry_count;
+ uint8_t sys_defined;
+ uint8_t entry_status;
+
+ uint32_t handle;
+
+ uint16_t options;
+#define ACO_DUMP_MEMORY 0x0
+#define ACO_LOAD_MEMORY 0x1
+#define ACO_CHANGE_CONFIG_PARAM 0x2
+#define ACO_REQUEST_INFO 0x3
+
+ uint16_t reserved1;
+
+ uint16_t dseg_count;
+ uint16_t reserved2[3];
+
+ uint32_t parameter1;
+ uint32_t parameter2;
+ uint32_t parameter3;
+
+ uint32_t reserved3[3];
+ uint32_t total_byte_cnt;
+ uint32_t reserved4;
+
+ uint32_t dseg_address[2];
+ uint32_t dseg_length;
+};
+
+struct access_chip_rsp_84xx {
+ uint8_t entry_type;
+ uint8_t entry_count;
+ uint8_t sys_defined;
+ uint8_t entry_status;
+
+ uint32_t handle;
+
+ uint16_t comp_status;
+ uint16_t failure_code;
+ uint32_t residual_count;
+
+ uint32_t reserved[12];
+};
+
+/* 81XX Support **************************************************************/
+
+#define MBA_DCBX_START 0x8016
+#define MBA_DCBX_COMPLETE 0x8030
+#define MBA_FCF_CONF_ERR 0x8031
+#define MBA_DCBX_PARAM_UPDATE 0x8032
+#define MBA_IDC_COMPLETE 0x8100
+#define MBA_IDC_NOTIFY 0x8101
+#define MBA_IDC_TIME_EXT 0x8102
+
+#define MBC_IDC_ACK 0x101
+#define MBC_RESTART_MPI_FW 0x3d
+#define MBC_FLASH_ACCESS_CTRL 0x3e /* Control flash access. */
+#define MBC_GET_XGMAC_STATS 0x7a
+#define MBC_GET_DCBX_PARAMS 0x51
+
+/*
+ * ISP83xx mailbox commands
+ */
+#define MBC_WRITE_REMOTE_REG 0x0001 /* Write remote register */
+
+/* Flash access control option field bit definitions */
+#define FAC_OPT_FORCE_SEMAPHORE BIT_15
+#define FAC_OPT_REQUESTOR_ID BIT_14
+#define FAC_OPT_CMD_SUBCODE 0xff
+
+/* Flash access control command subcodes */
+#define FAC_OPT_CMD_WRITE_PROTECT 0x00
+#define FAC_OPT_CMD_WRITE_ENABLE 0x01
+#define FAC_OPT_CMD_ERASE_SECTOR 0x02
+#define FAC_OPT_CMD_LOCK_SEMAPHORE 0x03
+#define FAC_OPT_CMD_UNLOCK_SEMAPHORE 0x04
+#define FAC_OPT_CMD_GET_SECTOR_SIZE 0x05
+
+struct nvram_81xx {
+ /* NVRAM header. */
+ uint8_t id[4];
+ uint16_t nvram_version;
+ uint16_t reserved_0;
+
+ /* Firmware Initialization Control Block. */
+ uint16_t version;
+ uint16_t reserved_1;
+ uint16_t frame_payload_size;
+ uint16_t execution_throttle;
+ uint16_t exchange_count;
+ uint16_t reserved_2;
+
+ uint8_t port_name[WWN_SIZE];
+ uint8_t node_name[WWN_SIZE];
+
+ uint16_t login_retry_count;
+ uint16_t reserved_3;
+ uint16_t interrupt_delay_timer;
+ uint16_t login_timeout;
+
+ uint32_t firmware_options_1;
+ uint32_t firmware_options_2;
+ uint32_t firmware_options_3;
+
+ uint16_t reserved_4[4];
+
+ /* Offset 64. */
+ uint8_t enode_mac[6];
+ uint16_t reserved_5[5];
+
+ /* Offset 80. */
+ uint16_t reserved_6[24];
+
+ /* Offset 128. */
+ uint16_t ex_version;
+ uint8_t prio_fcf_matching_flags;
+ uint8_t reserved_6_1[3];
+ uint16_t pri_fcf_vlan_id;
+ uint8_t pri_fcf_fabric_name[8];
+ uint16_t reserved_6_2[7];
+ uint8_t spma_mac_addr[6];
+ uint16_t reserved_6_3[14];
+
+ /* Offset 192. */
+ uint16_t reserved_7[32];
+
+ /*
+ * BIT 0 = Enable spinup delay
+ * BIT 1 = Disable BIOS
+ * BIT 2 = Enable Memory Map BIOS
+ * BIT 3 = Enable Selectable Boot
+ * BIT 4 = Disable RISC code load
+ * BIT 5 = Disable Serdes
+ * BIT 6 = Opt boot mode
+ * BIT 7 = Interrupt enable
+ *
+ * BIT 8 = EV Control enable
+ * BIT 9 = Enable lip reset
+ * BIT 10 = Enable lip full login
+ * BIT 11 = Enable target reset
+ * BIT 12 = Stop firmware
+ * BIT 13 = Enable nodename option
+ * BIT 14 = Default WWPN valid
+ * BIT 15 = Enable alternate WWN
+ *
+ * BIT 16 = CLP LUN string
+ * BIT 17 = CLP Target string
+ * BIT 18 = CLP BIOS enable string
+ * BIT 19 = CLP Serdes string
+ * BIT 20 = CLP WWPN string
+ * BIT 21 = CLP WWNN string
+ * BIT 22 =
+ * BIT 23 =
+ * BIT 24 = Keep WWPN
+ * BIT 25 = Temp WWPN
+ * BIT 26-31 =
+ */
+ uint32_t host_p;
+
+ uint8_t alternate_port_name[WWN_SIZE];
+ uint8_t alternate_node_name[WWN_SIZE];
+
+ uint8_t boot_port_name[WWN_SIZE];
+ uint16_t boot_lun_number;
+ uint16_t reserved_8;
+
+ uint8_t alt1_boot_port_name[WWN_SIZE];
+ uint16_t alt1_boot_lun_number;
+ uint16_t reserved_9;
+
+ uint8_t alt2_boot_port_name[WWN_SIZE];
+ uint16_t alt2_boot_lun_number;
+ uint16_t reserved_10;
+
+ uint8_t alt3_boot_port_name[WWN_SIZE];
+ uint16_t alt3_boot_lun_number;
+ uint16_t reserved_11;
+
+ /*
+ * BIT 0 = Selective Login
+ * BIT 1 = Alt-Boot Enable
+ * BIT 2 = Reserved
+ * BIT 3 = Boot Order List
+ * BIT 4 = Reserved
+ * BIT 5 = Selective LUN
+ * BIT 6 = Reserved
+ * BIT 7-31 =
+ */
+ uint32_t efi_parameters;
+
+ uint8_t reset_delay;
+ uint8_t reserved_12;
+ uint16_t reserved_13;
+
+ uint16_t boot_id_number;
+ uint16_t reserved_14;
+
+ uint16_t max_luns_per_target;
+ uint16_t reserved_15;
+
+ uint16_t port_down_retry_count;
+ uint16_t link_down_timeout;
+
+ /* FCode parameters. */
+ uint16_t fcode_parameter;
+
+ uint16_t reserved_16[3];
+
+ /* Offset 352. */
+ uint8_t reserved_17[4];
+ uint16_t reserved_18[5];
+ uint8_t reserved_19[2];
+ uint16_t reserved_20[8];
+
+ /* Offset 384. */
+ uint8_t reserved_21[16];
+ uint16_t reserved_22[3];
+
+ /*
+ * BIT 0 = Extended BB credits for LR
+ * BIT 1 = Virtual Fabric Enable
+ * BIT 2 = Enhanced Features Unused
+ * BIT 3-7 = Enhanced Features Reserved
+ */
+ /* Enhanced Features */
+ uint8_t enhanced_features;
+
+ uint8_t reserved_23;
+ uint16_t reserved_24[4];
+
+ /* Offset 416. */
+ uint16_t reserved_25[32];
+
+ /* Offset 480. */
+ uint8_t model_name[16];
+
+ /* Offset 496. */
+ uint16_t feature_mask_l;
+ uint16_t feature_mask_h;
+ uint16_t reserved_26[2];
+
+ uint16_t subsystem_vendor_id;
+ uint16_t subsystem_device_id;
+
+ uint32_t checksum;
+};
+
+/*
+ * ISP Initialization Control Block.
+ * Little endian except where noted.
+ */
+#define ICB_VERSION 1
+struct init_cb_81xx {
+ uint16_t version;
+ uint16_t reserved_1;
+
+ uint16_t frame_payload_size;
+ uint16_t execution_throttle;
+ uint16_t exchange_count;
+
+ uint16_t reserved_2;
+
+ uint8_t port_name[WWN_SIZE]; /* Big endian. */
+ uint8_t node_name[WWN_SIZE]; /* Big endian. */
+
+ uint16_t response_q_inpointer;
+ uint16_t request_q_outpointer;
+
+ uint16_t login_retry_count;
+
+ uint16_t prio_request_q_outpointer;
+
+ uint16_t response_q_length;
+ uint16_t request_q_length;
+
+ uint16_t reserved_3;
+
+ uint16_t prio_request_q_length;
+
+ uint32_t request_q_address[2];
+ uint32_t response_q_address[2];
+ uint32_t prio_request_q_address[2];
+
+ uint8_t reserved_4[8];
+
+ uint16_t atio_q_inpointer;
+ uint16_t atio_q_length;
+ uint32_t atio_q_address[2];
+
+ uint16_t interrupt_delay_timer; /* 100us increments. */
+ uint16_t login_timeout;
+
+ /*
+ * BIT 0-3 = Reserved
+ * BIT 4 = Enable Target Mode
+ * BIT 5 = Disable Initiator Mode
+ * BIT 6 = Reserved
+ * BIT 7 = Reserved
+ *
+ * BIT 8-13 = Reserved
+ * BIT 14 = Node Name Option
+ * BIT 15-31 = Reserved
+ */
+ uint32_t firmware_options_1;
+
+ /*
+ * BIT 0 = Operation Mode bit 0
+ * BIT 1 = Operation Mode bit 1
+ * BIT 2 = Operation Mode bit 2
+ * BIT 3 = Operation Mode bit 3
+ * BIT 4-7 = Reserved
+ *
+ * BIT 8 = Enable Class 2
+ * BIT 9 = Enable ACK0
+ * BIT 10 = Reserved
+ * BIT 11 = Enable FC-SP Security
+ * BIT 12 = FC Tape Enable
+ * BIT 13 = Reserved
+ * BIT 14 = Enable Target PRLI Control
+ * BIT 15-31 = Reserved
+ */
+ uint32_t firmware_options_2;
+
+ /*
+ * BIT 0-3 = Reserved
+ * BIT 4 = FCP RSP Payload bit 0
+ * BIT 5 = FCP RSP Payload bit 1
+ * BIT 6 = Enable Receive Out-of-Order data frame handling
+ * BIT 7 = Reserved
+ *
+ * BIT 8 = Reserved
+ * BIT 9 = Enable Out-of-Order FCP_XFER_RDY relative offset handling
+ * BIT 10-16 = Reserved
+ * BIT 17 = Enable multiple FCFs
+ * BIT 18-20 = MAC addressing mode
+ * BIT 21-25 = Ethernet data rate
+ * BIT 26 = Enable ethernet header rx IOCB for ATIO q
+ * BIT 27 = Enable ethernet header rx IOCB for response q
+ * BIT 28 = SPMA selection bit 0
+ * BIT 28 = SPMA selection bit 1
+ * BIT 30-31 = Reserved
+ */
+ uint32_t firmware_options_3;
+
+ uint8_t reserved_5[8];
+
+ uint8_t enode_mac[6];
+
+ uint8_t reserved_6[10];
+};
+
+struct mid_init_cb_81xx {
+ struct init_cb_81xx init_cb;
+
+ uint16_t count;
+ uint16_t options;
+
+ struct mid_conf_entry_24xx entries[MAX_MULTI_ID_FABRIC];
+};
+
+struct ex_init_cb_81xx {
+ uint16_t ex_version;
+ uint8_t prio_fcf_matching_flags;
+ uint8_t reserved_1[3];
+ uint16_t pri_fcf_vlan_id;
+ uint8_t pri_fcf_fabric_name[8];
+ uint16_t reserved_2[7];
+ uint8_t spma_mac_addr[6];
+ uint16_t reserved_3[14];
+};
+
+#define FARX_ACCESS_FLASH_CONF_81XX 0x7FFD0000
+#define FARX_ACCESS_FLASH_DATA_81XX 0x7F800000
+
+/* FCP priority config defines *************************************/
+/* operations */
+#define QLFC_FCP_PRIO_DISABLE 0x0
+#define QLFC_FCP_PRIO_ENABLE 0x1
+#define QLFC_FCP_PRIO_GET_CONFIG 0x2
+#define QLFC_FCP_PRIO_SET_CONFIG 0x3
+
+struct qla_fcp_prio_entry {
+ uint16_t flags; /* Describes parameter(s) in FCP */
+ /* priority entry that are valid */
+#define FCP_PRIO_ENTRY_VALID 0x1
+#define FCP_PRIO_ENTRY_TAG_VALID 0x2
+#define FCP_PRIO_ENTRY_SPID_VALID 0x4
+#define FCP_PRIO_ENTRY_DPID_VALID 0x8
+#define FCP_PRIO_ENTRY_LUNB_VALID 0x10
+#define FCP_PRIO_ENTRY_LUNE_VALID 0x20
+#define FCP_PRIO_ENTRY_SWWN_VALID 0x40
+#define FCP_PRIO_ENTRY_DWWN_VALID 0x80
+ uint8_t tag; /* Priority value */
+ uint8_t reserved; /* Reserved for future use */
+ uint32_t src_pid; /* Src port id. high order byte */
+ /* unused; -1 (wild card) */
+ uint32_t dst_pid; /* Src port id. high order byte */
+ /* unused; -1 (wild card) */
+ uint16_t lun_beg; /* 1st lun num of lun range. */
+ /* -1 (wild card) */
+ uint16_t lun_end; /* 2nd lun num of lun range. */
+ /* -1 (wild card) */
+ uint8_t src_wwpn[8]; /* Source WWPN: -1 (wild card) */
+ uint8_t dst_wwpn[8]; /* Destination WWPN: -1 (wild card) */
+};
+
+struct qla_fcp_prio_cfg {
+ uint8_t signature[4]; /* "HQOS" signature of config data */
+ uint16_t version; /* 1: Initial version */
+ uint16_t length; /* config data size in num bytes */
+ uint16_t checksum; /* config data bytes checksum */
+ uint16_t num_entries; /* Number of entries */
+ uint16_t size_of_entry; /* Size of each entry in num bytes */
+ uint8_t attributes; /* enable/disable, persistence */
+#define FCP_PRIO_ATTR_DISABLE 0x0
+#define FCP_PRIO_ATTR_ENABLE 0x1
+#define FCP_PRIO_ATTR_PERSIST 0x2
+ uint8_t reserved; /* Reserved for future use */
+#define FCP_PRIO_CFG_HDR_SIZE 0x10
+ struct qla_fcp_prio_entry entry[1]; /* fcp priority entries */
+#define FCP_PRIO_CFG_ENTRY_SIZE 0x20
+};
+
+#define FCP_PRIO_CFG_SIZE (32*1024) /* fcp prio data per port*/
+
+/* 25XX Support ****************************************************/
+#define FA_FCP_PRIO0_ADDR_25 0x3C000
+#define FA_FCP_PRIO1_ADDR_25 0x3E000
+
+/* 81XX Flash locations -- occupies second 2MB region. */
+#define FA_BOOT_CODE_ADDR_81 0x80000
+#define FA_RISC_CODE_ADDR_81 0xA0000
+#define FA_FW_AREA_ADDR_81 0xC0000
+#define FA_VPD_NVRAM_ADDR_81 0xD0000
+#define FA_VPD0_ADDR_81 0xD0000
+#define FA_VPD1_ADDR_81 0xD0400
+#define FA_NVRAM0_ADDR_81 0xD0080
+#define FA_NVRAM1_ADDR_81 0xD0180
+#define FA_FEATURE_ADDR_81 0xD4000
+#define FA_FLASH_DESCR_ADDR_81 0xD8000
+#define FA_FLASH_LAYOUT_ADDR_81 0xD8400
+#define FA_HW_EVENT0_ADDR_81 0xDC000
+#define FA_HW_EVENT1_ADDR_81 0xDC400
+#define FA_NPIV_CONF0_ADDR_81 0xD1000
+#define FA_NPIV_CONF1_ADDR_81 0xD2000
+
+/* 83XX Flash locations -- occupies second 8MB region. */
+#define FA_FLASH_LAYOUT_ADDR_83 0xFC400
+
+#endif
diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h
new file mode 100644
index 00000000..9f065804
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_gbl.h
@@ -0,0 +1,611 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2003-2011 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#ifndef __QLA_GBL_H
+#define __QLA_GBL_H
+
+#include <linux/interrupt.h>
+
+/*
+ * Global Function Prototypes in qla_init.c source file.
+ */
+extern int qla2x00_initialize_adapter(scsi_qla_host_t *);
+
+extern int qla2100_pci_config(struct scsi_qla_host *);
+extern int qla2300_pci_config(struct scsi_qla_host *);
+extern int qla24xx_pci_config(scsi_qla_host_t *);
+extern int qla25xx_pci_config(scsi_qla_host_t *);
+extern void qla2x00_reset_chip(struct scsi_qla_host *);
+extern void qla24xx_reset_chip(struct scsi_qla_host *);
+extern int qla2x00_chip_diag(struct scsi_qla_host *);
+extern int qla24xx_chip_diag(struct scsi_qla_host *);
+extern void qla2x00_config_rings(struct scsi_qla_host *);
+extern void qla24xx_config_rings(struct scsi_qla_host *);
+extern void qla2x00_reset_adapter(struct scsi_qla_host *);
+extern void qla24xx_reset_adapter(struct scsi_qla_host *);
+extern int qla2x00_nvram_config(struct scsi_qla_host *);
+extern int qla24xx_nvram_config(struct scsi_qla_host *);
+extern int qla81xx_nvram_config(struct scsi_qla_host *);
+extern void qla2x00_update_fw_options(struct scsi_qla_host *);
+extern void qla24xx_update_fw_options(scsi_qla_host_t *);
+extern void qla81xx_update_fw_options(scsi_qla_host_t *);
+extern int qla2x00_load_risc(struct scsi_qla_host *, uint32_t *);
+extern int qla24xx_load_risc(scsi_qla_host_t *, uint32_t *);
+extern int qla81xx_load_risc(scsi_qla_host_t *, uint32_t *);
+
+extern int qla2x00_perform_loop_resync(scsi_qla_host_t *);
+extern int qla2x00_loop_resync(scsi_qla_host_t *);
+
+extern int qla2x00_find_new_loop_id(scsi_qla_host_t *, fc_port_t *);
+
+extern int qla2x00_fabric_login(scsi_qla_host_t *, fc_port_t *, uint16_t *);
+extern int qla2x00_local_device_login(scsi_qla_host_t *, fc_port_t *);
+
+extern void qla2x00_update_fcports(scsi_qla_host_t *);
+
+extern int qla2x00_abort_isp(scsi_qla_host_t *);
+extern void qla2x00_abort_isp_cleanup(scsi_qla_host_t *);
+extern void qla82xx_quiescent_state_cleanup(scsi_qla_host_t *);
+
+extern void qla2x00_update_fcport(scsi_qla_host_t *, fc_port_t *);
+
+extern void qla2x00_alloc_fw_dump(scsi_qla_host_t *);
+extern void qla2x00_try_to_stop_firmware(scsi_qla_host_t *);
+
+extern int qla2x00_get_thermal_temp(scsi_qla_host_t *, uint16_t *, uint16_t *);
+
+extern void qla84xx_put_chip(struct scsi_qla_host *);
+
+extern int qla2x00_async_login(struct scsi_qla_host *, fc_port_t *,
+ uint16_t *);
+extern int qla2x00_async_logout(struct scsi_qla_host *, fc_port_t *);
+extern int qla2x00_async_adisc(struct scsi_qla_host *, fc_port_t *,
+ uint16_t *);
+extern int qla2x00_async_tm_cmd(fc_port_t *, uint32_t, uint32_t, uint32_t);
+extern void qla2x00_async_login_done(struct scsi_qla_host *, fc_port_t *,
+ uint16_t *);
+extern void qla2x00_async_logout_done(struct scsi_qla_host *, fc_port_t *,
+ uint16_t *);
+extern void qla2x00_async_adisc_done(struct scsi_qla_host *, fc_port_t *,
+ uint16_t *);
+extern void *qla2x00_alloc_iocbs(struct scsi_qla_host *, srb_t *);
+extern int qla24xx_update_fcport_fcp_prio(scsi_qla_host_t *, fc_port_t *);
+
+extern fc_port_t *
+qla2x00_alloc_fcport(scsi_qla_host_t *, gfp_t );
+/*
+ * Global Data in qla_os.c source file.
+ */
+extern char qla2x00_version_str[];
+
+extern int ql2xlogintimeout;
+extern int qlport_down_retry;
+extern int ql2xplogiabsentdevice;
+extern int ql2xloginretrycount;
+extern int ql2xfdmienable;
+extern int ql2xallocfwdump;
+extern int ql2xextended_error_logging;
+extern int ql2xiidmaenable;
+extern int ql2xmaxqueues;
+extern int ql2xmultique_tag;
+extern int ql2xfwloadbin;
+extern int ql2xetsenable;
+extern int ql2xshiftctondsd;
+extern int ql2xdbwr;
+extern int ql2xasynctmfenable;
+extern int ql2xgffidenable;
+extern int ql2xenabledif;
+extern int ql2xenablehba_err_chk;
+extern int ql2xtargetreset;
+extern int ql2xdontresethba;
+extern unsigned int ql2xmaxlun;
+extern int ql2xmdcapmask;
+extern int ql2xmdenable;
+
+extern int qla2x00_loop_reset(scsi_qla_host_t *);
+extern void qla2x00_abort_all_cmds(scsi_qla_host_t *, int);
+extern int qla2x00_post_aen_work(struct scsi_qla_host *, enum
+ fc_host_event_code, u32);
+extern int qla2x00_post_idc_ack_work(struct scsi_qla_host *, uint16_t *);
+extern int qla2x00_post_async_login_work(struct scsi_qla_host *, fc_port_t *,
+ uint16_t *);
+extern int qla2x00_post_async_login_done_work(struct scsi_qla_host *,
+ fc_port_t *, uint16_t *);
+extern int qla2x00_post_async_logout_work(struct scsi_qla_host *, fc_port_t *,
+ uint16_t *);
+extern int qla2x00_post_async_logout_done_work(struct scsi_qla_host *,
+ fc_port_t *, uint16_t *);
+extern int qla2x00_post_async_adisc_work(struct scsi_qla_host *, fc_port_t *,
+ uint16_t *);
+extern int qla2x00_post_async_adisc_done_work(struct scsi_qla_host *,
+ fc_port_t *, uint16_t *);
+extern int qla2x00_post_uevent_work(struct scsi_qla_host *, u32);
+
+extern int qla81xx_restart_mpi_firmware(scsi_qla_host_t *);
+
+extern struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *,
+ struct qla_hw_data *);
+extern void qla2x00_free_host(struct scsi_qla_host *);
+extern void qla2x00_relogin(struct scsi_qla_host *);
+extern void qla2x00_do_work(struct scsi_qla_host *);
+extern void qla2x00_free_fcports(struct scsi_qla_host *);
+
+/*
+ * Global Functions in qla_mid.c source file.
+ */
+extern struct scsi_host_template qla2xxx_driver_template;
+extern struct scsi_transport_template *qla2xxx_transport_vport_template;
+extern void qla2x00_timer(scsi_qla_host_t *);
+extern void qla2x00_start_timer(scsi_qla_host_t *, void *, unsigned long);
+extern void qla24xx_deallocate_vp_id(scsi_qla_host_t *);
+extern int qla24xx_disable_vp (scsi_qla_host_t *);
+extern int qla24xx_enable_vp (scsi_qla_host_t *);
+extern int qla24xx_control_vp(scsi_qla_host_t *, int );
+extern int qla24xx_modify_vp_config(scsi_qla_host_t *);
+extern int qla2x00_send_change_request(scsi_qla_host_t *, uint16_t, uint16_t);
+extern void qla2x00_vp_stop_timer(scsi_qla_host_t *);
+extern int qla24xx_configure_vhba (scsi_qla_host_t *);
+extern void qla24xx_report_id_acquisition(scsi_qla_host_t *,
+ struct vp_rpt_id_entry_24xx *);
+extern void qla2x00_do_dpc_all_vps(scsi_qla_host_t *);
+extern int qla24xx_vport_create_req_sanity_check(struct fc_vport *);
+extern scsi_qla_host_t * qla24xx_create_vhost(struct fc_vport *);
+
+extern void qla2x00_sp_free_dma(void *, void *);
+extern char *qla2x00_get_fw_version_str(struct scsi_qla_host *, char *);
+
+extern void qla2x00_mark_device_lost(scsi_qla_host_t *, fc_port_t *, int, int);
+extern void qla2x00_mark_all_devices_lost(scsi_qla_host_t *, int);
+
+extern struct fw_blob *qla2x00_request_firmware(scsi_qla_host_t *);
+
+extern int qla2x00_wait_for_hba_online(scsi_qla_host_t *);
+extern int qla2x00_wait_for_chip_reset(scsi_qla_host_t *);
+extern int qla2x00_wait_for_fcoe_ctx_reset(scsi_qla_host_t *);
+
+extern void qla2xxx_wake_dpc(struct scsi_qla_host *);
+extern void qla2x00_alert_all_vps(struct rsp_que *, uint16_t *);
+extern void qla2x00_async_event(scsi_qla_host_t *, struct rsp_que *,
+ uint16_t *);
+extern int qla2x00_vp_abort_isp(scsi_qla_host_t *);
+
+/*
+ * Global Function Prototypes in qla_iocb.c source file.
+ */
+extern uint16_t qla2x00_calc_iocbs_32(uint16_t);
+extern uint16_t qla2x00_calc_iocbs_64(uint16_t);
+extern void qla2x00_build_scsi_iocbs_32(srb_t *, cmd_entry_t *, uint16_t);
+extern void qla2x00_build_scsi_iocbs_64(srb_t *, cmd_entry_t *, uint16_t);
+extern int qla2x00_start_scsi(srb_t *sp);
+extern int qla24xx_start_scsi(srb_t *sp);
+int qla2x00_marker(struct scsi_qla_host *, struct req_que *, struct rsp_que *,
+ uint16_t, uint16_t, uint8_t);
+extern int qla2x00_start_sp(srb_t *);
+extern uint16_t qla24xx_calc_iocbs(scsi_qla_host_t *, uint16_t);
+extern void qla24xx_build_scsi_iocbs(srb_t *, struct cmd_type_7 *, uint16_t);
+extern int qla24xx_dif_start_scsi(srb_t *);
+
+
+/*
+ * Global Function Prototypes in qla_mbx.c source file.
+ */
+extern int
+qla2x00_load_ram(scsi_qla_host_t *, dma_addr_t, uint32_t, uint32_t);
+
+extern int
+qla2x00_dump_ram(scsi_qla_host_t *, dma_addr_t, uint32_t, uint32_t);
+
+extern int
+qla2x00_execute_fw(scsi_qla_host_t *, uint32_t);
+
+extern int
+qla2x00_get_fw_version(scsi_qla_host_t *);
+
+extern int
+qla2x00_get_fw_options(scsi_qla_host_t *, uint16_t *);
+
+extern int
+qla2x00_set_fw_options(scsi_qla_host_t *, uint16_t *);
+
+extern int
+qla2x00_mbx_reg_test(scsi_qla_host_t *);
+
+extern int
+qla2x00_verify_checksum(scsi_qla_host_t *, uint32_t);
+
+extern int
+qla2x00_issue_iocb(scsi_qla_host_t *, void *, dma_addr_t, size_t);
+
+extern int
+qla2x00_abort_command(srb_t *);
+
+extern int
+qla2x00_abort_target(struct fc_port *, unsigned int, int);
+
+extern int
+qla2x00_lun_reset(struct fc_port *, unsigned int, int);
+
+extern int
+qla2x00_get_adapter_id(scsi_qla_host_t *, uint16_t *, uint8_t *, uint8_t *,
+ uint8_t *, uint16_t *, uint16_t *);
+
+extern int
+qla2x00_get_retry_cnt(scsi_qla_host_t *, uint8_t *, uint8_t *, uint16_t *);
+
+extern int
+qla2x00_init_firmware(scsi_qla_host_t *, uint16_t);
+
+extern int
+qla2x00_get_port_database(scsi_qla_host_t *, fc_port_t *, uint8_t);
+
+extern int
+qla2x00_get_firmware_state(scsi_qla_host_t *, uint16_t *);
+
+extern int
+qla2x00_get_port_name(scsi_qla_host_t *, uint16_t, uint8_t *, uint8_t);
+
+extern int
+qla2x00_lip_reset(scsi_qla_host_t *);
+
+extern int
+qla2x00_send_sns(scsi_qla_host_t *, dma_addr_t, uint16_t, size_t);
+
+extern int
+qla2x00_login_fabric(scsi_qla_host_t *, uint16_t, uint8_t, uint8_t, uint8_t,
+ uint16_t *, uint8_t);
+extern int
+qla24xx_login_fabric(scsi_qla_host_t *, uint16_t, uint8_t, uint8_t, uint8_t,
+ uint16_t *, uint8_t);
+
+extern int
+qla2x00_login_local_device(scsi_qla_host_t *, fc_port_t *, uint16_t *,
+ uint8_t);
+
+extern int
+qla2x00_fabric_logout(scsi_qla_host_t *, uint16_t, uint8_t, uint8_t, uint8_t);
+
+extern int
+qla24xx_fabric_logout(scsi_qla_host_t *, uint16_t, uint8_t, uint8_t, uint8_t);
+
+extern int
+qla2x00_full_login_lip(scsi_qla_host_t *ha);
+
+extern int
+qla2x00_get_id_list(scsi_qla_host_t *, void *, dma_addr_t, uint16_t *);
+
+extern int
+qla2x00_get_resource_cnts(scsi_qla_host_t *, uint16_t *, uint16_t *,
+ uint16_t *, uint16_t *, uint16_t *, uint16_t *);
+
+extern int
+qla2x00_get_fcal_position_map(scsi_qla_host_t *ha, char *pos_map);
+
+extern int
+qla2x00_get_link_status(scsi_qla_host_t *, uint16_t, struct link_statistics *,
+ dma_addr_t);
+
+extern int
+qla24xx_get_isp_stats(scsi_qla_host_t *, struct link_statistics *,
+ dma_addr_t);
+
+extern int qla24xx_abort_command(srb_t *);
+extern int
+qla24xx_abort_target(struct fc_port *, unsigned int, int);
+extern int
+qla24xx_lun_reset(struct fc_port *, unsigned int, int);
+extern int
+qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *, unsigned int,
+ unsigned int, enum nexus_wait_type);
+extern int
+qla2x00_system_error(scsi_qla_host_t *);
+
+extern int
+qla2x00_set_serdes_params(scsi_qla_host_t *, uint16_t, uint16_t, uint16_t);
+
+extern int
+qla2x00_stop_firmware(scsi_qla_host_t *);
+
+extern int
+qla2x00_enable_eft_trace(scsi_qla_host_t *, dma_addr_t, uint16_t);
+extern int
+qla2x00_disable_eft_trace(scsi_qla_host_t *);
+
+extern int
+qla2x00_enable_fce_trace(scsi_qla_host_t *, dma_addr_t, uint16_t , uint16_t *,
+ uint32_t *);
+
+extern int
+qla2x00_disable_fce_trace(scsi_qla_host_t *, uint64_t *, uint64_t *);
+
+extern int
+qla2x00_read_sfp(scsi_qla_host_t *, dma_addr_t, uint8_t *,
+ uint16_t, uint16_t, uint16_t, uint16_t);
+
+extern int
+qla2x00_write_sfp(scsi_qla_host_t *, dma_addr_t, uint8_t *,
+ uint16_t, uint16_t, uint16_t, uint16_t);
+
+extern int
+qla2x00_set_idma_speed(scsi_qla_host_t *, uint16_t, uint16_t, uint16_t *);
+
+extern int qla84xx_verify_chip(struct scsi_qla_host *, uint16_t *);
+
+extern int qla81xx_idc_ack(scsi_qla_host_t *, uint16_t *);
+
+extern int
+qla81xx_fac_get_sector_size(scsi_qla_host_t *, uint32_t *);
+
+extern int
+qla81xx_fac_do_write_enable(scsi_qla_host_t *, int);
+
+extern int
+qla81xx_fac_erase_sector(scsi_qla_host_t *, uint32_t, uint32_t);
+
+extern int
+qla2x00_get_xgmac_stats(scsi_qla_host_t *, dma_addr_t, uint16_t, uint16_t *);
+
+extern int
+qla2x00_get_dcbx_params(scsi_qla_host_t *, dma_addr_t, uint16_t);
+
+extern int
+qla2x00_read_ram_word(scsi_qla_host_t *, uint32_t, uint32_t *);
+
+extern int
+qla2x00_write_ram_word(scsi_qla_host_t *, uint32_t, uint32_t);
+
+extern int
+qla81xx_write_mpi_register(scsi_qla_host_t *, uint16_t *);
+extern int qla2x00_get_data_rate(scsi_qla_host_t *);
+extern int qla24xx_set_fcp_prio(scsi_qla_host_t *, uint16_t, uint16_t,
+ uint16_t *);
+extern int
+qla81xx_get_port_config(scsi_qla_host_t *, uint16_t *);
+
+extern int
+qla81xx_set_port_config(scsi_qla_host_t *, uint16_t *);
+
+extern int
+qla2x00_port_logout(scsi_qla_host_t *, struct fc_port *);
+
+/*
+ * Global Function Prototypes in qla_isr.c source file.
+ */
+extern irqreturn_t qla2100_intr_handler(int, void *);
+extern irqreturn_t qla2300_intr_handler(int, void *);
+extern irqreturn_t qla24xx_intr_handler(int, void *);
+extern void qla2x00_process_response_queue(struct rsp_que *);
+extern void
+qla24xx_process_response_queue(struct scsi_qla_host *, struct rsp_que *);
+extern int qla2x00_request_irqs(struct qla_hw_data *, struct rsp_que *);
+extern void qla2x00_free_irqs(scsi_qla_host_t *);
+
+extern int qla2x00_get_data_rate(scsi_qla_host_t *);
+/*
+ * Global Function Prototypes in qla_sup.c source file.
+ */
+extern void qla2x00_release_nvram_protection(scsi_qla_host_t *);
+extern uint32_t *qla24xx_read_flash_data(scsi_qla_host_t *, uint32_t *,
+ uint32_t, uint32_t);
+extern uint8_t *qla2x00_read_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t,
+ uint32_t);
+extern uint8_t *qla24xx_read_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t,
+ uint32_t);
+extern int qla2x00_write_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t,
+ uint32_t);
+extern int qla24xx_write_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t,
+ uint32_t);
+extern uint8_t *qla25xx_read_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t,
+ uint32_t);
+extern int qla25xx_write_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t,
+ uint32_t);
+
+extern int qla2x00_beacon_on(struct scsi_qla_host *);
+extern int qla2x00_beacon_off(struct scsi_qla_host *);
+extern void qla2x00_beacon_blink(struct scsi_qla_host *);
+extern int qla24xx_beacon_on(struct scsi_qla_host *);
+extern int qla24xx_beacon_off(struct scsi_qla_host *);
+extern void qla24xx_beacon_blink(struct scsi_qla_host *);
+extern void qla83xx_beacon_blink(struct scsi_qla_host *);
+extern int qla82xx_beacon_on(struct scsi_qla_host *);
+extern int qla82xx_beacon_off(struct scsi_qla_host *);
+extern int qla83xx_write_remote_reg(struct scsi_qla_host *, uint32_t, uint32_t);
+
+extern uint8_t *qla2x00_read_optrom_data(struct scsi_qla_host *, uint8_t *,
+ uint32_t, uint32_t);
+extern int qla2x00_write_optrom_data(struct scsi_qla_host *, uint8_t *,
+ uint32_t, uint32_t);
+extern uint8_t *qla24xx_read_optrom_data(struct scsi_qla_host *, uint8_t *,
+ uint32_t, uint32_t);
+extern int qla24xx_write_optrom_data(struct scsi_qla_host *, uint8_t *,
+ uint32_t, uint32_t);
+extern uint8_t *qla25xx_read_optrom_data(struct scsi_qla_host *, uint8_t *,
+ uint32_t, uint32_t);
+
+extern int qla2x00_get_flash_version(scsi_qla_host_t *, void *);
+extern int qla24xx_get_flash_version(scsi_qla_host_t *, void *);
+
+extern int qla2xxx_get_flash_info(scsi_qla_host_t *);
+extern int qla2xxx_get_vpd_field(scsi_qla_host_t *, char *, char *, size_t);
+
+extern void qla2xxx_flash_npiv_conf(scsi_qla_host_t *);
+extern int qla24xx_read_fcp_prio_cfg(scsi_qla_host_t *);
+
+/*
+ * Global Function Prototypes in qla_dbg.c source file.
+ */
+extern void qla2100_fw_dump(scsi_qla_host_t *, int);
+extern void qla2300_fw_dump(scsi_qla_host_t *, int);
+extern void qla24xx_fw_dump(scsi_qla_host_t *, int);
+extern void qla25xx_fw_dump(scsi_qla_host_t *, int);
+extern void qla81xx_fw_dump(scsi_qla_host_t *, int);
+extern void qla2x00_dump_regs(scsi_qla_host_t *);
+extern void qla2x00_dump_buffer(uint8_t *, uint32_t);
+extern void qla2x00_dump_buffer_zipped(uint8_t *, uint32_t);
+extern void ql_dump_regs(uint32_t, scsi_qla_host_t *, int32_t);
+extern void ql_dump_buffer(uint32_t, scsi_qla_host_t *, int32_t,
+ uint8_t *, uint32_t);
+extern void qla2xxx_dump_post_process(scsi_qla_host_t *, int);
+
+/*
+ * Global Function Prototypes in qla_gs.c source file.
+ */
+extern void *qla2x00_prep_ms_iocb(scsi_qla_host_t *, uint32_t, uint32_t);
+extern void *qla24xx_prep_ms_iocb(scsi_qla_host_t *, uint32_t, uint32_t);
+extern int qla2x00_ga_nxt(scsi_qla_host_t *, fc_port_t *);
+extern int qla2x00_gid_pt(scsi_qla_host_t *, sw_info_t *);
+extern int qla2x00_gpn_id(scsi_qla_host_t *, sw_info_t *);
+extern int qla2x00_gnn_id(scsi_qla_host_t *, sw_info_t *);
+extern void qla2x00_gff_id(scsi_qla_host_t *, sw_info_t *);
+extern int qla2x00_rft_id(scsi_qla_host_t *);
+extern int qla2x00_rff_id(scsi_qla_host_t *);
+extern int qla2x00_rnn_id(scsi_qla_host_t *);
+extern int qla2x00_rsnn_nn(scsi_qla_host_t *);
+extern void *qla2x00_prep_ms_fdmi_iocb(scsi_qla_host_t *, uint32_t, uint32_t);
+extern void *qla24xx_prep_ms_fdmi_iocb(scsi_qla_host_t *, uint32_t, uint32_t);
+extern int qla2x00_fdmi_register(scsi_qla_host_t *);
+extern int qla2x00_gfpn_id(scsi_qla_host_t *, sw_info_t *);
+extern int qla2x00_gpsc(scsi_qla_host_t *, sw_info_t *);
+extern void qla2x00_get_sym_node_name(scsi_qla_host_t *, uint8_t *);
+
+/*
+ * Global Function Prototypes in qla_attr.c source file.
+ */
+struct device_attribute;
+extern struct device_attribute *qla2x00_host_attrs[];
+struct fc_function_template;
+extern struct fc_function_template qla2xxx_transport_functions;
+extern struct fc_function_template qla2xxx_transport_vport_functions;
+extern void qla2x00_alloc_sysfs_attr(scsi_qla_host_t *);
+extern void qla2x00_free_sysfs_attr(scsi_qla_host_t *);
+extern void qla2x00_init_host_attr(scsi_qla_host_t *);
+extern void qla2x00_alloc_sysfs_attr(scsi_qla_host_t *);
+extern void qla2x00_free_sysfs_attr(scsi_qla_host_t *);
+extern int qla2x00_loopback_test(scsi_qla_host_t *, struct msg_echo_lb *, uint16_t *);
+extern int qla2x00_echo_test(scsi_qla_host_t *,
+ struct msg_echo_lb *, uint16_t *);
+extern int qla24xx_update_all_fcp_prio(scsi_qla_host_t *);
+extern int qla24xx_fcp_prio_cfg_valid(scsi_qla_host_t *,
+ struct qla_fcp_prio_cfg *, uint8_t);
+
+/*
+ * Global Function Prototypes in qla_dfs.c source file.
+ */
+extern int qla2x00_dfs_setup(scsi_qla_host_t *);
+extern int qla2x00_dfs_remove(scsi_qla_host_t *);
+
+/* Globa function prototypes for multi-q */
+extern int qla25xx_request_irq(struct rsp_que *);
+extern int qla25xx_init_req_que(struct scsi_qla_host *, struct req_que *);
+extern int qla25xx_init_rsp_que(struct scsi_qla_host *, struct rsp_que *);
+extern int qla25xx_create_req_que(struct qla_hw_data *, uint16_t, uint8_t,
+ uint16_t, int, uint8_t);
+extern int qla25xx_create_rsp_que(struct qla_hw_data *, uint16_t, uint8_t,
+ uint16_t, int);
+extern void qla2x00_init_response_q_entries(struct rsp_que *);
+extern int qla25xx_delete_req_que(struct scsi_qla_host *, struct req_que *);
+extern int qla25xx_delete_queues(struct scsi_qla_host *);
+extern uint16_t qla24xx_rd_req_reg(struct qla_hw_data *, uint16_t);
+extern uint16_t qla25xx_rd_req_reg(struct qla_hw_data *, uint16_t);
+extern void qla24xx_wrt_req_reg(struct qla_hw_data *, uint16_t, uint16_t);
+extern void qla25xx_wrt_req_reg(struct qla_hw_data *, uint16_t, uint16_t);
+extern void qla25xx_wrt_rsp_reg(struct qla_hw_data *, uint16_t, uint16_t);
+extern void qla24xx_wrt_rsp_reg(struct qla_hw_data *, uint16_t, uint16_t);
+
+/* qla82xx related functions */
+
+/* PCI related functions */
+extern int qla82xx_pci_config(struct scsi_qla_host *);
+extern int qla82xx_pci_mem_read_2M(struct qla_hw_data *, u64, void *, int);
+extern char *qla82xx_pci_info_str(struct scsi_qla_host *, char *);
+extern int qla82xx_pci_region_offset(struct pci_dev *, int);
+extern int qla82xx_iospace_config(struct qla_hw_data *);
+
+/* Initialization related functions */
+extern void qla82xx_reset_chip(struct scsi_qla_host *);
+extern void qla82xx_config_rings(struct scsi_qla_host *);
+extern void qla82xx_watchdog(scsi_qla_host_t *);
+extern int qla82xx_start_firmware(scsi_qla_host_t *);
+
+/* Firmware and flash related functions */
+extern int qla82xx_load_risc(scsi_qla_host_t *, uint32_t *);
+extern uint8_t *qla82xx_read_optrom_data(struct scsi_qla_host *, uint8_t *,
+ uint32_t, uint32_t);
+extern int qla82xx_write_optrom_data(struct scsi_qla_host *, uint8_t *,
+ uint32_t, uint32_t);
+
+/* Mailbox related functions */
+extern int qla82xx_abort_isp(scsi_qla_host_t *);
+extern int qla82xx_restart_isp(scsi_qla_host_t *);
+
+/* IOCB related functions */
+extern int qla82xx_start_scsi(srb_t *);
+extern void qla2x00_sp_free(void *, void *);
+extern void qla2x00_sp_timeout(unsigned long);
+extern void qla2x00_bsg_job_done(void *, void *, int);
+extern void qla2x00_bsg_sp_free(void *, void *);
+
+/* Interrupt related */
+extern irqreturn_t qla82xx_intr_handler(int, void *);
+extern irqreturn_t qla82xx_msi_handler(int, void *);
+extern irqreturn_t qla82xx_msix_default(int, void *);
+extern irqreturn_t qla82xx_msix_rsp_q(int, void *);
+extern void qla82xx_enable_intrs(struct qla_hw_data *);
+extern void qla82xx_disable_intrs(struct qla_hw_data *);
+extern void qla82xx_poll(int, void *);
+extern void qla82xx_init_flags(struct qla_hw_data *);
+
+/* ISP 8021 hardware related */
+extern void qla82xx_set_drv_active(scsi_qla_host_t *);
+extern void qla82xx_crb_win_unlock(struct qla_hw_data *);
+extern int qla82xx_wr_32(struct qla_hw_data *, ulong, u32);
+extern int qla82xx_rd_32(struct qla_hw_data *, ulong);
+extern int qla82xx_rdmem(struct qla_hw_data *, u64, void *, int);
+extern int qla82xx_wrmem(struct qla_hw_data *, u64, void *, int);
+
+/* ISP 8021 IDC */
+extern void qla82xx_clear_drv_active(struct qla_hw_data *);
+extern uint32_t qla82xx_wait_for_state_change(scsi_qla_host_t *, uint32_t);
+extern int qla82xx_idc_lock(struct qla_hw_data *);
+extern void qla82xx_idc_unlock(struct qla_hw_data *);
+extern int qla82xx_device_state_handler(scsi_qla_host_t *);
+extern void qla82xx_clear_qsnt_ready(scsi_qla_host_t *);
+
+extern void qla2x00_set_model_info(scsi_qla_host_t *, uint8_t *,
+ size_t, char *);
+extern int qla82xx_mbx_intr_enable(scsi_qla_host_t *);
+extern int qla82xx_mbx_intr_disable(scsi_qla_host_t *);
+extern void qla82xx_start_iocbs(scsi_qla_host_t *);
+extern int qla82xx_fcoe_ctx_reset(scsi_qla_host_t *);
+extern int qla82xx_check_md_needed(scsi_qla_host_t *);
+extern void qla82xx_chip_reset_cleanup(scsi_qla_host_t *);
+extern int qla81xx_set_led_config(scsi_qla_host_t *, uint16_t *);
+extern int qla81xx_get_led_config(scsi_qla_host_t *, uint16_t *);
+extern int qla82xx_mbx_beacon_ctl(scsi_qla_host_t *, int);
+extern char *qdev_state(uint32_t);
+extern void qla82xx_clear_pending_mbx(scsi_qla_host_t *);
+
+/* BSG related functions */
+extern int qla24xx_bsg_request(struct fc_bsg_job *);
+extern int qla24xx_bsg_timeout(struct fc_bsg_job *);
+extern int qla84xx_reset_chip(scsi_qla_host_t *, uint16_t);
+extern int qla2x00_issue_iocb_timeout(scsi_qla_host_t *, void *,
+ dma_addr_t, size_t, uint32_t);
+extern int qla2x00_get_idma_speed(scsi_qla_host_t *, uint16_t,
+ uint16_t *, uint16_t *);
+
+/* 83xx related functions */
+extern void qla83xx_fw_dump(scsi_qla_host_t *, int);
+
+/* Minidump related functions */
+extern int qla82xx_md_get_template_size(scsi_qla_host_t *);
+extern int qla82xx_md_get_template(scsi_qla_host_t *);
+extern int qla82xx_md_alloc(scsi_qla_host_t *);
+extern void qla82xx_md_free(scsi_qla_host_t *);
+extern int qla82xx_md_collect(scsi_qla_host_t *);
+extern void qla82xx_md_prep(scsi_qla_host_t *);
+extern void qla82xx_set_reset_owner(scsi_qla_host_t *);
+
+#endif /* _QLA_GBL_H */
diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c
new file mode 100644
index 00000000..3128f804
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_gs.c
@@ -0,0 +1,2025 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2003-2011 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#include "qla_def.h"
+
+static int qla2x00_sns_ga_nxt(scsi_qla_host_t *, fc_port_t *);
+static int qla2x00_sns_gid_pt(scsi_qla_host_t *, sw_info_t *);
+static int qla2x00_sns_gpn_id(scsi_qla_host_t *, sw_info_t *);
+static int qla2x00_sns_gnn_id(scsi_qla_host_t *, sw_info_t *);
+static int qla2x00_sns_rft_id(scsi_qla_host_t *);
+static int qla2x00_sns_rnn_id(scsi_qla_host_t *);
+
+/**
+ * qla2x00_prep_ms_iocb() - Prepare common MS/CT IOCB fields for SNS CT query.
+ * @ha: HA context
+ * @req_size: request size in bytes
+ * @rsp_size: response size in bytes
+ *
+ * Returns a pointer to the @ha's ms_iocb.
+ */
+void *
+qla2x00_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size)
+{
+ struct qla_hw_data *ha = vha->hw;
+ ms_iocb_entry_t *ms_pkt;
+
+ ms_pkt = ha->ms_iocb;
+ memset(ms_pkt, 0, sizeof(ms_iocb_entry_t));
+
+ ms_pkt->entry_type = MS_IOCB_TYPE;
+ ms_pkt->entry_count = 1;
+ SET_TARGET_ID(ha, ms_pkt->loop_id, SIMPLE_NAME_SERVER);
+ ms_pkt->control_flags = __constant_cpu_to_le16(CF_READ | CF_HEAD_TAG);
+ ms_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
+ ms_pkt->cmd_dsd_count = __constant_cpu_to_le16(1);
+ ms_pkt->total_dsd_count = __constant_cpu_to_le16(2);
+ ms_pkt->rsp_bytecount = cpu_to_le32(rsp_size);
+ ms_pkt->req_bytecount = cpu_to_le32(req_size);
+
+ ms_pkt->dseg_req_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
+ ms_pkt->dseg_req_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
+ ms_pkt->dseg_req_length = ms_pkt->req_bytecount;
+
+ ms_pkt->dseg_rsp_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
+ ms_pkt->dseg_rsp_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
+ ms_pkt->dseg_rsp_length = ms_pkt->rsp_bytecount;
+
+ return (ms_pkt);
+}
+
+/**
+ * qla24xx_prep_ms_iocb() - Prepare common CT IOCB fields for SNS CT query.
+ * @ha: HA context
+ * @req_size: request size in bytes
+ * @rsp_size: response size in bytes
+ *
+ * Returns a pointer to the @ha's ms_iocb.
+ */
+void *
+qla24xx_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size)
+{
+ struct qla_hw_data *ha = vha->hw;
+ struct ct_entry_24xx *ct_pkt;
+
+ ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
+ memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
+
+ ct_pkt->entry_type = CT_IOCB_TYPE;
+ ct_pkt->entry_count = 1;
+ ct_pkt->nport_handle = __constant_cpu_to_le16(NPH_SNS);
+ ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
+ ct_pkt->cmd_dsd_count = __constant_cpu_to_le16(1);
+ ct_pkt->rsp_dsd_count = __constant_cpu_to_le16(1);
+ ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size);
+ ct_pkt->cmd_byte_count = cpu_to_le32(req_size);
+
+ ct_pkt->dseg_0_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
+ ct_pkt->dseg_0_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
+ ct_pkt->dseg_0_len = ct_pkt->cmd_byte_count;
+
+ ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
+ ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
+ ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count;
+ ct_pkt->vp_index = vha->vp_idx;
+
+ return (ct_pkt);
+}
+
+/**
+ * qla2x00_prep_ct_req() - Prepare common CT request fields for SNS query.
+ * @ct_req: CT request buffer
+ * @cmd: GS command
+ * @rsp_size: response size in bytes
+ *
+ * Returns a pointer to the intitialized @ct_req.
+ */
+static inline struct ct_sns_req *
+qla2x00_prep_ct_req(struct ct_sns_req *ct_req, uint16_t cmd, uint16_t rsp_size)
+{
+ memset(ct_req, 0, sizeof(struct ct_sns_pkt));
+
+ ct_req->header.revision = 0x01;
+ ct_req->header.gs_type = 0xFC;
+ ct_req->header.gs_subtype = 0x02;
+ ct_req->command = cpu_to_be16(cmd);
+ ct_req->max_rsp_size = cpu_to_be16((rsp_size - 16) / 4);
+
+ return (ct_req);
+}
+
+static int
+qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt,
+ struct ct_sns_rsp *ct_rsp, const char *routine)
+{
+ int rval;
+ uint16_t comp_status;
+ struct qla_hw_data *ha = vha->hw;
+
+ rval = QLA_FUNCTION_FAILED;
+ if (ms_pkt->entry_status != 0) {
+ ql_dbg(ql_dbg_disc, vha, 0x2031,
+ "%s failed, error status (%x) on port_id: %02x%02x%02x.\n",
+ routine, ms_pkt->entry_status, vha->d_id.b.domain,
+ vha->d_id.b.area, vha->d_id.b.al_pa);
+ } else {
+ if (IS_FWI2_CAPABLE(ha))
+ comp_status = le16_to_cpu(
+ ((struct ct_entry_24xx *)ms_pkt)->comp_status);
+ else
+ comp_status = le16_to_cpu(ms_pkt->status);
+ switch (comp_status) {
+ case CS_COMPLETE:
+ case CS_DATA_UNDERRUN:
+ case CS_DATA_OVERRUN: /* Overrun? */
+ if (ct_rsp->header.response !=
+ __constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) {
+ ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x2077,
+ "%s failed rejected request on port_id: "
+ "%02x%02x%02x.\n", routine,
+ vha->d_id.b.domain, vha->d_id.b.area,
+ vha->d_id.b.al_pa);
+ ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha,
+ 0x2078, (uint8_t *)&ct_rsp->header,
+ sizeof(struct ct_rsp_hdr));
+ rval = QLA_INVALID_COMMAND;
+ } else
+ rval = QLA_SUCCESS;
+ break;
+ default:
+ ql_dbg(ql_dbg_disc, vha, 0x2033,
+ "%s failed, completion status (%x) on port_id: "
+ "%02x%02x%02x.\n", routine, comp_status,
+ vha->d_id.b.domain, vha->d_id.b.area,
+ vha->d_id.b.al_pa);
+ break;
+ }
+ }
+ return rval;
+}
+
+/**
+ * qla2x00_ga_nxt() - SNS scan for fabric devices via GA_NXT command.
+ * @ha: HA context
+ * @fcport: fcport entry to updated
+ *
+ * Returns 0 on success.
+ */
+int
+qla2x00_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport)
+{
+ int rval;
+
+ ms_iocb_entry_t *ms_pkt;
+ struct ct_sns_req *ct_req;
+ struct ct_sns_rsp *ct_rsp;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return qla2x00_sns_ga_nxt(vha, fcport);
+
+ /* Issue GA_NXT */
+ /* Prepare common MS IOCB */
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GA_NXT_REQ_SIZE,
+ GA_NXT_RSP_SIZE);
+
+ /* Prepare CT request */
+ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GA_NXT_CMD,
+ GA_NXT_RSP_SIZE);
+ ct_rsp = &ha->ct_sns->p.rsp;
+
+ /* Prepare CT arguments -- port_id */
+ ct_req->req.port_id.port_id[0] = fcport->d_id.b.domain;
+ ct_req->req.port_id.port_id[1] = fcport->d_id.b.area;
+ ct_req->req.port_id.port_id[2] = fcport->d_id.b.al_pa;
+
+ /* Execute MS IOCB */
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
+ sizeof(ms_iocb_entry_t));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_disc, vha, 0x2062,
+ "GA_NXT issue IOCB failed (%d).\n", rval);
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "GA_NXT") !=
+ QLA_SUCCESS) {
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ /* Populate fc_port_t entry. */
+ fcport->d_id.b.domain = ct_rsp->rsp.ga_nxt.port_id[0];
+ fcport->d_id.b.area = ct_rsp->rsp.ga_nxt.port_id[1];
+ fcport->d_id.b.al_pa = ct_rsp->rsp.ga_nxt.port_id[2];
+
+ memcpy(fcport->node_name, ct_rsp->rsp.ga_nxt.node_name,
+ WWN_SIZE);
+ memcpy(fcport->port_name, ct_rsp->rsp.ga_nxt.port_name,
+ WWN_SIZE);
+
+ if (ct_rsp->rsp.ga_nxt.port_type != NS_N_PORT_TYPE &&
+ ct_rsp->rsp.ga_nxt.port_type != NS_NL_PORT_TYPE)
+ fcport->d_id.b.domain = 0xf0;
+
+ ql_dbg(ql_dbg_disc, vha, 0x2063,
+ "GA_NXT entry - nn %02x%02x%02x%02x%02x%02x%02x%02x "
+ "pn %02x%02x%02x%02x%02x%02x%02x%02x "
+ "port_id=%02x%02x%02x.\n",
+ fcport->node_name[0], fcport->node_name[1],
+ fcport->node_name[2], fcport->node_name[3],
+ fcport->node_name[4], fcport->node_name[5],
+ fcport->node_name[6], fcport->node_name[7],
+ fcport->port_name[0], fcport->port_name[1],
+ fcport->port_name[2], fcport->port_name[3],
+ fcport->port_name[4], fcport->port_name[5],
+ fcport->port_name[6], fcport->port_name[7],
+ fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa);
+ }
+
+ return (rval);
+}
+
+static inline int
+qla2x00_gid_pt_rsp_size(scsi_qla_host_t *vha)
+{
+ return vha->hw->max_fibre_devices * 4 + 16;
+}
+
+/**
+ * qla2x00_gid_pt() - SNS scan for fabric devices via GID_PT command.
+ * @ha: HA context
+ * @list: switch info entries to populate
+ *
+ * NOTE: Non-Nx_Ports are not requested.
+ *
+ * Returns 0 on success.
+ */
+int
+qla2x00_gid_pt(scsi_qla_host_t *vha, sw_info_t *list)
+{
+ int rval;
+ uint16_t i;
+
+ ms_iocb_entry_t *ms_pkt;
+ struct ct_sns_req *ct_req;
+ struct ct_sns_rsp *ct_rsp;
+
+ struct ct_sns_gid_pt_data *gid_data;
+ struct qla_hw_data *ha = vha->hw;
+ uint16_t gid_pt_rsp_size;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return qla2x00_sns_gid_pt(vha, list);
+
+ gid_data = NULL;
+ gid_pt_rsp_size = qla2x00_gid_pt_rsp_size(vha);
+ /* Issue GID_PT */
+ /* Prepare common MS IOCB */
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GID_PT_REQ_SIZE,
+ gid_pt_rsp_size);
+
+ /* Prepare CT request */
+ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GID_PT_CMD,
+ gid_pt_rsp_size);
+ ct_rsp = &ha->ct_sns->p.rsp;
+
+ /* Prepare CT arguments -- port_type */
+ ct_req->req.gid_pt.port_type = NS_NX_PORT_TYPE;
+
+ /* Execute MS IOCB */
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
+ sizeof(ms_iocb_entry_t));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_disc, vha, 0x2055,
+ "GID_PT issue IOCB failed (%d).\n", rval);
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "GID_PT") !=
+ QLA_SUCCESS) {
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ /* Set port IDs in switch info list. */
+ for (i = 0; i < ha->max_fibre_devices; i++) {
+ gid_data = &ct_rsp->rsp.gid_pt.entries[i];
+ list[i].d_id.b.domain = gid_data->port_id[0];
+ list[i].d_id.b.area = gid_data->port_id[1];
+ list[i].d_id.b.al_pa = gid_data->port_id[2];
+ memset(list[i].fabric_port_name, 0, WWN_SIZE);
+ list[i].fp_speed = PORT_SPEED_UNKNOWN;
+
+ /* Last one exit. */
+ if (gid_data->control_byte & BIT_7) {
+ list[i].d_id.b.rsvd_1 = gid_data->control_byte;
+ break;
+ }
+ }
+
+ /*
+ * If we've used all available slots, then the switch is
+ * reporting back more devices than we can handle with this
+ * single call. Return a failed status, and let GA_NXT handle
+ * the overload.
+ */
+ if (i == ha->max_fibre_devices)
+ rval = QLA_FUNCTION_FAILED;
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_gpn_id() - SNS Get Port Name (GPN_ID) query.
+ * @ha: HA context
+ * @list: switch info entries to populate
+ *
+ * Returns 0 on success.
+ */
+int
+qla2x00_gpn_id(scsi_qla_host_t *vha, sw_info_t *list)
+{
+ int rval = QLA_SUCCESS;
+ uint16_t i;
+
+ ms_iocb_entry_t *ms_pkt;
+ struct ct_sns_req *ct_req;
+ struct ct_sns_rsp *ct_rsp;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return qla2x00_sns_gpn_id(vha, list);
+
+ for (i = 0; i < ha->max_fibre_devices; i++) {
+ /* Issue GPN_ID */
+ /* Prepare common MS IOCB */
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GPN_ID_REQ_SIZE,
+ GPN_ID_RSP_SIZE);
+
+ /* Prepare CT request */
+ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GPN_ID_CMD,
+ GPN_ID_RSP_SIZE);
+ ct_rsp = &ha->ct_sns->p.rsp;
+
+ /* Prepare CT arguments -- port_id */
+ ct_req->req.port_id.port_id[0] = list[i].d_id.b.domain;
+ ct_req->req.port_id.port_id[1] = list[i].d_id.b.area;
+ ct_req->req.port_id.port_id[2] = list[i].d_id.b.al_pa;
+
+ /* Execute MS IOCB */
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
+ sizeof(ms_iocb_entry_t));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_disc, vha, 0x2056,
+ "GPN_ID issue IOCB failed (%d).\n", rval);
+ break;
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp,
+ "GPN_ID") != QLA_SUCCESS) {
+ rval = QLA_FUNCTION_FAILED;
+ break;
+ } else {
+ /* Save portname */
+ memcpy(list[i].port_name,
+ ct_rsp->rsp.gpn_id.port_name, WWN_SIZE);
+ }
+
+ /* Last device exit. */
+ if (list[i].d_id.b.rsvd_1 != 0)
+ break;
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_gnn_id() - SNS Get Node Name (GNN_ID) query.
+ * @ha: HA context
+ * @list: switch info entries to populate
+ *
+ * Returns 0 on success.
+ */
+int
+qla2x00_gnn_id(scsi_qla_host_t *vha, sw_info_t *list)
+{
+ int rval = QLA_SUCCESS;
+ uint16_t i;
+ struct qla_hw_data *ha = vha->hw;
+ ms_iocb_entry_t *ms_pkt;
+ struct ct_sns_req *ct_req;
+ struct ct_sns_rsp *ct_rsp;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return qla2x00_sns_gnn_id(vha, list);
+
+ for (i = 0; i < ha->max_fibre_devices; i++) {
+ /* Issue GNN_ID */
+ /* Prepare common MS IOCB */
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GNN_ID_REQ_SIZE,
+ GNN_ID_RSP_SIZE);
+
+ /* Prepare CT request */
+ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GNN_ID_CMD,
+ GNN_ID_RSP_SIZE);
+ ct_rsp = &ha->ct_sns->p.rsp;
+
+ /* Prepare CT arguments -- port_id */
+ ct_req->req.port_id.port_id[0] = list[i].d_id.b.domain;
+ ct_req->req.port_id.port_id[1] = list[i].d_id.b.area;
+ ct_req->req.port_id.port_id[2] = list[i].d_id.b.al_pa;
+
+ /* Execute MS IOCB */
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
+ sizeof(ms_iocb_entry_t));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_disc, vha, 0x2057,
+ "GNN_ID issue IOCB failed (%d).\n", rval);
+ break;
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp,
+ "GNN_ID") != QLA_SUCCESS) {
+ rval = QLA_FUNCTION_FAILED;
+ break;
+ } else {
+ /* Save nodename */
+ memcpy(list[i].node_name,
+ ct_rsp->rsp.gnn_id.node_name, WWN_SIZE);
+
+ ql_dbg(ql_dbg_disc, vha, 0x2058,
+ "GID_PT entry - nn %02x%02x%02x%02x%02x%02x%02X%02x "
+ "pn %02x%02x%02x%02x%02x%02x%02X%02x "
+ "portid=%02x%02x%02x.\n",
+ list[i].node_name[0], list[i].node_name[1],
+ list[i].node_name[2], list[i].node_name[3],
+ list[i].node_name[4], list[i].node_name[5],
+ list[i].node_name[6], list[i].node_name[7],
+ list[i].port_name[0], list[i].port_name[1],
+ list[i].port_name[2], list[i].port_name[3],
+ list[i].port_name[4], list[i].port_name[5],
+ list[i].port_name[6], list[i].port_name[7],
+ list[i].d_id.b.domain, list[i].d_id.b.area,
+ list[i].d_id.b.al_pa);
+ }
+
+ /* Last device exit. */
+ if (list[i].d_id.b.rsvd_1 != 0)
+ break;
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_rft_id() - SNS Register FC-4 TYPEs (RFT_ID) supported by the HBA.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+int
+qla2x00_rft_id(scsi_qla_host_t *vha)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ ms_iocb_entry_t *ms_pkt;
+ struct ct_sns_req *ct_req;
+ struct ct_sns_rsp *ct_rsp;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return qla2x00_sns_rft_id(vha);
+
+ /* Issue RFT_ID */
+ /* Prepare common MS IOCB */
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, RFT_ID_REQ_SIZE,
+ RFT_ID_RSP_SIZE);
+
+ /* Prepare CT request */
+ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RFT_ID_CMD,
+ RFT_ID_RSP_SIZE);
+ ct_rsp = &ha->ct_sns->p.rsp;
+
+ /* Prepare CT arguments -- port_id, FC-4 types */
+ ct_req->req.rft_id.port_id[0] = vha->d_id.b.domain;
+ ct_req->req.rft_id.port_id[1] = vha->d_id.b.area;
+ ct_req->req.rft_id.port_id[2] = vha->d_id.b.al_pa;
+
+ ct_req->req.rft_id.fc4_types[2] = 0x01; /* FCP-3 */
+
+ /* Execute MS IOCB */
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
+ sizeof(ms_iocb_entry_t));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_disc, vha, 0x2043,
+ "RFT_ID issue IOCB failed (%d).\n", rval);
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RFT_ID") !=
+ QLA_SUCCESS) {
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ ql_dbg(ql_dbg_disc, vha, 0x2044,
+ "RFT_ID exiting normally.\n");
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_rff_id() - SNS Register FC-4 Features (RFF_ID) supported by the HBA.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+int
+qla2x00_rff_id(scsi_qla_host_t *vha)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ ms_iocb_entry_t *ms_pkt;
+ struct ct_sns_req *ct_req;
+ struct ct_sns_rsp *ct_rsp;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+ ql_dbg(ql_dbg_disc, vha, 0x2046,
+ "RFF_ID call not supported on ISP2100/ISP2200.\n");
+ return (QLA_SUCCESS);
+ }
+
+ /* Issue RFF_ID */
+ /* Prepare common MS IOCB */
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, RFF_ID_REQ_SIZE,
+ RFF_ID_RSP_SIZE);
+
+ /* Prepare CT request */
+ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RFF_ID_CMD,
+ RFF_ID_RSP_SIZE);
+ ct_rsp = &ha->ct_sns->p.rsp;
+
+ /* Prepare CT arguments -- port_id, FC-4 feature, FC-4 type */
+ ct_req->req.rff_id.port_id[0] = vha->d_id.b.domain;
+ ct_req->req.rff_id.port_id[1] = vha->d_id.b.area;
+ ct_req->req.rff_id.port_id[2] = vha->d_id.b.al_pa;
+
+ ct_req->req.rff_id.fc4_feature = BIT_1;
+ ct_req->req.rff_id.fc4_type = 0x08; /* SCSI - FCP */
+
+ /* Execute MS IOCB */
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
+ sizeof(ms_iocb_entry_t));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_disc, vha, 0x2047,
+ "RFF_ID issue IOCB failed (%d).\n", rval);
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RFF_ID") !=
+ QLA_SUCCESS) {
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ ql_dbg(ql_dbg_disc, vha, 0x2048,
+ "RFF_ID exiting normally.\n");
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_rnn_id() - SNS Register Node Name (RNN_ID) of the HBA.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+int
+qla2x00_rnn_id(scsi_qla_host_t *vha)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ ms_iocb_entry_t *ms_pkt;
+ struct ct_sns_req *ct_req;
+ struct ct_sns_rsp *ct_rsp;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return qla2x00_sns_rnn_id(vha);
+
+ /* Issue RNN_ID */
+ /* Prepare common MS IOCB */
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, RNN_ID_REQ_SIZE,
+ RNN_ID_RSP_SIZE);
+
+ /* Prepare CT request */
+ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RNN_ID_CMD,
+ RNN_ID_RSP_SIZE);
+ ct_rsp = &ha->ct_sns->p.rsp;
+
+ /* Prepare CT arguments -- port_id, node_name */
+ ct_req->req.rnn_id.port_id[0] = vha->d_id.b.domain;
+ ct_req->req.rnn_id.port_id[1] = vha->d_id.b.area;
+ ct_req->req.rnn_id.port_id[2] = vha->d_id.b.al_pa;
+
+ memcpy(ct_req->req.rnn_id.node_name, vha->node_name, WWN_SIZE);
+
+ /* Execute MS IOCB */
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
+ sizeof(ms_iocb_entry_t));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_disc, vha, 0x204d,
+ "RNN_ID issue IOCB failed (%d).\n", rval);
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RNN_ID") !=
+ QLA_SUCCESS) {
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ ql_dbg(ql_dbg_disc, vha, 0x204e,
+ "RNN_ID exiting normally.\n");
+ }
+
+ return (rval);
+}
+
+void
+qla2x00_get_sym_node_name(scsi_qla_host_t *vha, uint8_t *snn)
+{
+ struct qla_hw_data *ha = vha->hw;
+ sprintf(snn, "%s FW:v%d.%02d.%02d DVR:v%s",ha->model_number,
+ ha->fw_major_version, ha->fw_minor_version,
+ ha->fw_subminor_version, qla2x00_version_str);
+}
+
+/**
+ * qla2x00_rsnn_nn() - SNS Register Symbolic Node Name (RSNN_NN) of the HBA.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+int
+qla2x00_rsnn_nn(scsi_qla_host_t *vha)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ ms_iocb_entry_t *ms_pkt;
+ struct ct_sns_req *ct_req;
+ struct ct_sns_rsp *ct_rsp;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+ ql_dbg(ql_dbg_disc, vha, 0x2050,
+ "RSNN_ID call unsupported on ISP2100/ISP2200.\n");
+ return (QLA_SUCCESS);
+ }
+
+ /* Issue RSNN_NN */
+ /* Prepare common MS IOCB */
+ /* Request size adjusted after CT preparation */
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, 0, RSNN_NN_RSP_SIZE);
+
+ /* Prepare CT request */
+ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RSNN_NN_CMD,
+ RSNN_NN_RSP_SIZE);
+ ct_rsp = &ha->ct_sns->p.rsp;
+
+ /* Prepare CT arguments -- node_name, symbolic node_name, size */
+ memcpy(ct_req->req.rsnn_nn.node_name, vha->node_name, WWN_SIZE);
+
+ /* Prepare the Symbolic Node Name */
+ qla2x00_get_sym_node_name(vha, ct_req->req.rsnn_nn.sym_node_name);
+
+ /* Calculate SNN length */
+ ct_req->req.rsnn_nn.name_len =
+ (uint8_t)strlen(ct_req->req.rsnn_nn.sym_node_name);
+
+ /* Update MS IOCB request */
+ ms_pkt->req_bytecount =
+ cpu_to_le32(24 + 1 + ct_req->req.rsnn_nn.name_len);
+ ms_pkt->dseg_req_length = ms_pkt->req_bytecount;
+
+ /* Execute MS IOCB */
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
+ sizeof(ms_iocb_entry_t));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_disc, vha, 0x2051,
+ "RSNN_NN issue IOCB failed (%d).\n", rval);
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RSNN_NN") !=
+ QLA_SUCCESS) {
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ ql_dbg(ql_dbg_disc, vha, 0x2052,
+ "RSNN_NN exiting normally.\n");
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_prep_sns_cmd() - Prepare common SNS command request fields for query.
+ * @ha: HA context
+ * @cmd: GS command
+ * @scmd_len: Subcommand length
+ * @data_size: response size in bytes
+ *
+ * Returns a pointer to the @ha's sns_cmd.
+ */
+static inline struct sns_cmd_pkt *
+qla2x00_prep_sns_cmd(scsi_qla_host_t *vha, uint16_t cmd, uint16_t scmd_len,
+ uint16_t data_size)
+{
+ uint16_t wc;
+ struct sns_cmd_pkt *sns_cmd;
+ struct qla_hw_data *ha = vha->hw;
+
+ sns_cmd = ha->sns_cmd;
+ memset(sns_cmd, 0, sizeof(struct sns_cmd_pkt));
+ wc = data_size / 2; /* Size in 16bit words. */
+ sns_cmd->p.cmd.buffer_length = cpu_to_le16(wc);
+ sns_cmd->p.cmd.buffer_address[0] = cpu_to_le32(LSD(ha->sns_cmd_dma));
+ sns_cmd->p.cmd.buffer_address[1] = cpu_to_le32(MSD(ha->sns_cmd_dma));
+ sns_cmd->p.cmd.subcommand_length = cpu_to_le16(scmd_len);
+ sns_cmd->p.cmd.subcommand = cpu_to_le16(cmd);
+ wc = (data_size - 16) / 4; /* Size in 32bit words. */
+ sns_cmd->p.cmd.size = cpu_to_le16(wc);
+
+ return (sns_cmd);
+}
+
+/**
+ * qla2x00_sns_ga_nxt() - SNS scan for fabric devices via GA_NXT command.
+ * @ha: HA context
+ * @fcport: fcport entry to updated
+ *
+ * This command uses the old Exectute SNS Command mailbox routine.
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_sns_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport)
+{
+ int rval = QLA_SUCCESS;
+ struct qla_hw_data *ha = vha->hw;
+ struct sns_cmd_pkt *sns_cmd;
+
+ /* Issue GA_NXT. */
+ /* Prepare SNS command request. */
+ sns_cmd = qla2x00_prep_sns_cmd(vha, GA_NXT_CMD, GA_NXT_SNS_SCMD_LEN,
+ GA_NXT_SNS_DATA_SIZE);
+
+ /* Prepare SNS command arguments -- port_id. */
+ sns_cmd->p.cmd.param[0] = fcport->d_id.b.al_pa;
+ sns_cmd->p.cmd.param[1] = fcport->d_id.b.area;
+ sns_cmd->p.cmd.param[2] = fcport->d_id.b.domain;
+
+ /* Execute SNS command. */
+ rval = qla2x00_send_sns(vha, ha->sns_cmd_dma, GA_NXT_SNS_CMD_SIZE / 2,
+ sizeof(struct sns_cmd_pkt));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_disc, vha, 0x205f,
+ "GA_NXT Send SNS failed (%d).\n", rval);
+ } else if (sns_cmd->p.gan_data[8] != 0x80 ||
+ sns_cmd->p.gan_data[9] != 0x02) {
+ ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x2084,
+ "GA_NXT failed, rejected request ga_nxt_rsp:\n");
+ ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2074,
+ sns_cmd->p.gan_data, 16);
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ /* Populate fc_port_t entry. */
+ fcport->d_id.b.domain = sns_cmd->p.gan_data[17];
+ fcport->d_id.b.area = sns_cmd->p.gan_data[18];
+ fcport->d_id.b.al_pa = sns_cmd->p.gan_data[19];
+
+ memcpy(fcport->node_name, &sns_cmd->p.gan_data[284], WWN_SIZE);
+ memcpy(fcport->port_name, &sns_cmd->p.gan_data[20], WWN_SIZE);
+
+ if (sns_cmd->p.gan_data[16] != NS_N_PORT_TYPE &&
+ sns_cmd->p.gan_data[16] != NS_NL_PORT_TYPE)
+ fcport->d_id.b.domain = 0xf0;
+
+ ql_dbg(ql_dbg_disc, vha, 0x2061,
+ "GA_NXT entry - nn %02x%02x%02x%02x%02x%02x%02x%02x "
+ "pn %02x%02x%02x%02x%02x%02x%02x%02x "
+ "port_id=%02x%02x%02x.\n",
+ fcport->node_name[0], fcport->node_name[1],
+ fcport->node_name[2], fcport->node_name[3],
+ fcport->node_name[4], fcport->node_name[5],
+ fcport->node_name[6], fcport->node_name[7],
+ fcport->port_name[0], fcport->port_name[1],
+ fcport->port_name[2], fcport->port_name[3],
+ fcport->port_name[4], fcport->port_name[5],
+ fcport->port_name[6], fcport->port_name[7],
+ fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa);
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_sns_gid_pt() - SNS scan for fabric devices via GID_PT command.
+ * @ha: HA context
+ * @list: switch info entries to populate
+ *
+ * This command uses the old Exectute SNS Command mailbox routine.
+ *
+ * NOTE: Non-Nx_Ports are not requested.
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_sns_gid_pt(scsi_qla_host_t *vha, sw_info_t *list)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ uint16_t i;
+ uint8_t *entry;
+ struct sns_cmd_pkt *sns_cmd;
+ uint16_t gid_pt_sns_data_size;
+
+ gid_pt_sns_data_size = qla2x00_gid_pt_rsp_size(vha);
+
+ /* Issue GID_PT. */
+ /* Prepare SNS command request. */
+ sns_cmd = qla2x00_prep_sns_cmd(vha, GID_PT_CMD, GID_PT_SNS_SCMD_LEN,
+ gid_pt_sns_data_size);
+
+ /* Prepare SNS command arguments -- port_type. */
+ sns_cmd->p.cmd.param[0] = NS_NX_PORT_TYPE;
+
+ /* Execute SNS command. */
+ rval = qla2x00_send_sns(vha, ha->sns_cmd_dma, GID_PT_SNS_CMD_SIZE / 2,
+ sizeof(struct sns_cmd_pkt));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_disc, vha, 0x206d,
+ "GID_PT Send SNS failed (%d).\n", rval);
+ } else if (sns_cmd->p.gid_data[8] != 0x80 ||
+ sns_cmd->p.gid_data[9] != 0x02) {
+ ql_dbg(ql_dbg_disc, vha, 0x202f,
+ "GID_PT failed, rejected request, gid_rsp:\n");
+ ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2081,
+ sns_cmd->p.gid_data, 16);
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ /* Set port IDs in switch info list. */
+ for (i = 0; i < ha->max_fibre_devices; i++) {
+ entry = &sns_cmd->p.gid_data[(i * 4) + 16];
+ list[i].d_id.b.domain = entry[1];
+ list[i].d_id.b.area = entry[2];
+ list[i].d_id.b.al_pa = entry[3];
+
+ /* Last one exit. */
+ if (entry[0] & BIT_7) {
+ list[i].d_id.b.rsvd_1 = entry[0];
+ break;
+ }
+ }
+
+ /*
+ * If we've used all available slots, then the switch is
+ * reporting back more devices that we can handle with this
+ * single call. Return a failed status, and let GA_NXT handle
+ * the overload.
+ */
+ if (i == ha->max_fibre_devices)
+ rval = QLA_FUNCTION_FAILED;
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_sns_gpn_id() - SNS Get Port Name (GPN_ID) query.
+ * @ha: HA context
+ * @list: switch info entries to populate
+ *
+ * This command uses the old Exectute SNS Command mailbox routine.
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_sns_gpn_id(scsi_qla_host_t *vha, sw_info_t *list)
+{
+ int rval = QLA_SUCCESS;
+ struct qla_hw_data *ha = vha->hw;
+ uint16_t i;
+ struct sns_cmd_pkt *sns_cmd;
+
+ for (i = 0; i < ha->max_fibre_devices; i++) {
+ /* Issue GPN_ID */
+ /* Prepare SNS command request. */
+ sns_cmd = qla2x00_prep_sns_cmd(vha, GPN_ID_CMD,
+ GPN_ID_SNS_SCMD_LEN, GPN_ID_SNS_DATA_SIZE);
+
+ /* Prepare SNS command arguments -- port_id. */
+ sns_cmd->p.cmd.param[0] = list[i].d_id.b.al_pa;
+ sns_cmd->p.cmd.param[1] = list[i].d_id.b.area;
+ sns_cmd->p.cmd.param[2] = list[i].d_id.b.domain;
+
+ /* Execute SNS command. */
+ rval = qla2x00_send_sns(vha, ha->sns_cmd_dma,
+ GPN_ID_SNS_CMD_SIZE / 2, sizeof(struct sns_cmd_pkt));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_disc, vha, 0x2032,
+ "GPN_ID Send SNS failed (%d).\n", rval);
+ } else if (sns_cmd->p.gpn_data[8] != 0x80 ||
+ sns_cmd->p.gpn_data[9] != 0x02) {
+ ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x207e,
+ "GPN_ID failed, rejected request, gpn_rsp:\n");
+ ql_dump_buffer(ql_dbg_disc, vha, 0x207f,
+ sns_cmd->p.gpn_data, 16);
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ /* Save portname */
+ memcpy(list[i].port_name, &sns_cmd->p.gpn_data[16],
+ WWN_SIZE);
+ }
+
+ /* Last device exit. */
+ if (list[i].d_id.b.rsvd_1 != 0)
+ break;
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_sns_gnn_id() - SNS Get Node Name (GNN_ID) query.
+ * @ha: HA context
+ * @list: switch info entries to populate
+ *
+ * This command uses the old Exectute SNS Command mailbox routine.
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_sns_gnn_id(scsi_qla_host_t *vha, sw_info_t *list)
+{
+ int rval = QLA_SUCCESS;
+ struct qla_hw_data *ha = vha->hw;
+ uint16_t i;
+ struct sns_cmd_pkt *sns_cmd;
+
+ for (i = 0; i < ha->max_fibre_devices; i++) {
+ /* Issue GNN_ID */
+ /* Prepare SNS command request. */
+ sns_cmd = qla2x00_prep_sns_cmd(vha, GNN_ID_CMD,
+ GNN_ID_SNS_SCMD_LEN, GNN_ID_SNS_DATA_SIZE);
+
+ /* Prepare SNS command arguments -- port_id. */
+ sns_cmd->p.cmd.param[0] = list[i].d_id.b.al_pa;
+ sns_cmd->p.cmd.param[1] = list[i].d_id.b.area;
+ sns_cmd->p.cmd.param[2] = list[i].d_id.b.domain;
+
+ /* Execute SNS command. */
+ rval = qla2x00_send_sns(vha, ha->sns_cmd_dma,
+ GNN_ID_SNS_CMD_SIZE / 2, sizeof(struct sns_cmd_pkt));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_disc, vha, 0x203f,
+ "GNN_ID Send SNS failed (%d).\n", rval);
+ } else if (sns_cmd->p.gnn_data[8] != 0x80 ||
+ sns_cmd->p.gnn_data[9] != 0x02) {
+ ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x2082,
+ "GNN_ID failed, rejected request, gnn_rsp:\n");
+ ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x207a,
+ sns_cmd->p.gnn_data, 16);
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ /* Save nodename */
+ memcpy(list[i].node_name, &sns_cmd->p.gnn_data[16],
+ WWN_SIZE);
+
+ ql_dbg(ql_dbg_disc, vha, 0x206e,
+ "GID_PT entry - nn %02x%02x%02x%02x%02x%02x%02x%02x "
+ "pn %02x%02x%02x%02x%02x%02x%02x%02x "
+ "port_id=%02x%02x%02x.\n",
+ list[i].node_name[0], list[i].node_name[1],
+ list[i].node_name[2], list[i].node_name[3],
+ list[i].node_name[4], list[i].node_name[5],
+ list[i].node_name[6], list[i].node_name[7],
+ list[i].port_name[0], list[i].port_name[1],
+ list[i].port_name[2], list[i].port_name[3],
+ list[i].port_name[4], list[i].port_name[5],
+ list[i].port_name[6], list[i].port_name[7],
+ list[i].d_id.b.domain, list[i].d_id.b.area,
+ list[i].d_id.b.al_pa);
+ }
+
+ /* Last device exit. */
+ if (list[i].d_id.b.rsvd_1 != 0)
+ break;
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_snd_rft_id() - SNS Register FC-4 TYPEs (RFT_ID) supported by the HBA.
+ * @ha: HA context
+ *
+ * This command uses the old Exectute SNS Command mailbox routine.
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_sns_rft_id(scsi_qla_host_t *vha)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ struct sns_cmd_pkt *sns_cmd;
+
+ /* Issue RFT_ID. */
+ /* Prepare SNS command request. */
+ sns_cmd = qla2x00_prep_sns_cmd(vha, RFT_ID_CMD, RFT_ID_SNS_SCMD_LEN,
+ RFT_ID_SNS_DATA_SIZE);
+
+ /* Prepare SNS command arguments -- port_id, FC-4 types */
+ sns_cmd->p.cmd.param[0] = vha->d_id.b.al_pa;
+ sns_cmd->p.cmd.param[1] = vha->d_id.b.area;
+ sns_cmd->p.cmd.param[2] = vha->d_id.b.domain;
+
+ sns_cmd->p.cmd.param[5] = 0x01; /* FCP-3 */
+
+ /* Execute SNS command. */
+ rval = qla2x00_send_sns(vha, ha->sns_cmd_dma, RFT_ID_SNS_CMD_SIZE / 2,
+ sizeof(struct sns_cmd_pkt));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_disc, vha, 0x2060,
+ "RFT_ID Send SNS failed (%d).\n", rval);
+ } else if (sns_cmd->p.rft_data[8] != 0x80 ||
+ sns_cmd->p.rft_data[9] != 0x02) {
+ ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x2083,
+ "RFT_ID failed, rejected request rft_rsp:\n");
+ ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2080,
+ sns_cmd->p.rft_data, 16);
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ ql_dbg(ql_dbg_disc, vha, 0x2073,
+ "RFT_ID exiting normally.\n");
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_sns_rnn_id() - SNS Register Node Name (RNN_ID) of the HBA.
+ * HBA.
+ * @ha: HA context
+ *
+ * This command uses the old Exectute SNS Command mailbox routine.
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_sns_rnn_id(scsi_qla_host_t *vha)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ struct sns_cmd_pkt *sns_cmd;
+
+ /* Issue RNN_ID. */
+ /* Prepare SNS command request. */
+ sns_cmd = qla2x00_prep_sns_cmd(vha, RNN_ID_CMD, RNN_ID_SNS_SCMD_LEN,
+ RNN_ID_SNS_DATA_SIZE);
+
+ /* Prepare SNS command arguments -- port_id, nodename. */
+ sns_cmd->p.cmd.param[0] = vha->d_id.b.al_pa;
+ sns_cmd->p.cmd.param[1] = vha->d_id.b.area;
+ sns_cmd->p.cmd.param[2] = vha->d_id.b.domain;
+
+ sns_cmd->p.cmd.param[4] = vha->node_name[7];
+ sns_cmd->p.cmd.param[5] = vha->node_name[6];
+ sns_cmd->p.cmd.param[6] = vha->node_name[5];
+ sns_cmd->p.cmd.param[7] = vha->node_name[4];
+ sns_cmd->p.cmd.param[8] = vha->node_name[3];
+ sns_cmd->p.cmd.param[9] = vha->node_name[2];
+ sns_cmd->p.cmd.param[10] = vha->node_name[1];
+ sns_cmd->p.cmd.param[11] = vha->node_name[0];
+
+ /* Execute SNS command. */
+ rval = qla2x00_send_sns(vha, ha->sns_cmd_dma, RNN_ID_SNS_CMD_SIZE / 2,
+ sizeof(struct sns_cmd_pkt));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_disc, vha, 0x204a,
+ "RNN_ID Send SNS failed (%d).\n", rval);
+ } else if (sns_cmd->p.rnn_data[8] != 0x80 ||
+ sns_cmd->p.rnn_data[9] != 0x02) {
+ ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x207b,
+ "RNN_ID failed, rejected request, rnn_rsp:\n");
+ ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x207c,
+ sns_cmd->p.rnn_data, 16);
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ ql_dbg(ql_dbg_disc, vha, 0x204c,
+ "RNN_ID exiting normally.\n");
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_mgmt_svr_login() - Login to fabric Management Service.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_mgmt_svr_login(scsi_qla_host_t *vha)
+{
+ int ret, rval;
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+ struct qla_hw_data *ha = vha->hw;
+ ret = QLA_SUCCESS;
+ if (vha->flags.management_server_logged_in)
+ return ret;
+
+ rval = ha->isp_ops->fabric_login(vha, vha->mgmt_svr_loop_id, 0xff, 0xff,
+ 0xfa, mb, BIT_1|BIT_0);
+ if (rval != QLA_SUCCESS || mb[0] != MBS_COMMAND_COMPLETE) {
+ if (rval == QLA_MEMORY_ALLOC_FAILED)
+ ql_dbg(ql_dbg_disc, vha, 0x2085,
+ "Failed management_server login: loopid=%x "
+ "rval=%d\n", vha->mgmt_svr_loop_id, rval);
+ else
+ ql_dbg(ql_dbg_disc, vha, 0x2024,
+ "Failed management_server login: loopid=%x "
+ "mb[0]=%x mb[1]=%x mb[2]=%x mb[6]=%x mb[7]=%x.\n",
+ vha->mgmt_svr_loop_id, mb[0], mb[1], mb[2], mb[6],
+ mb[7]);
+ ret = QLA_FUNCTION_FAILED;
+ } else
+ vha->flags.management_server_logged_in = 1;
+
+ return ret;
+}
+
+/**
+ * qla2x00_prep_ms_fdmi_iocb() - Prepare common MS IOCB fields for FDMI query.
+ * @ha: HA context
+ * @req_size: request size in bytes
+ * @rsp_size: response size in bytes
+ *
+ * Returns a pointer to the @ha's ms_iocb.
+ */
+void *
+qla2x00_prep_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size,
+ uint32_t rsp_size)
+{
+ ms_iocb_entry_t *ms_pkt;
+ struct qla_hw_data *ha = vha->hw;
+ ms_pkt = ha->ms_iocb;
+ memset(ms_pkt, 0, sizeof(ms_iocb_entry_t));
+
+ ms_pkt->entry_type = MS_IOCB_TYPE;
+ ms_pkt->entry_count = 1;
+ SET_TARGET_ID(ha, ms_pkt->loop_id, vha->mgmt_svr_loop_id);
+ ms_pkt->control_flags = __constant_cpu_to_le16(CF_READ | CF_HEAD_TAG);
+ ms_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
+ ms_pkt->cmd_dsd_count = __constant_cpu_to_le16(1);
+ ms_pkt->total_dsd_count = __constant_cpu_to_le16(2);
+ ms_pkt->rsp_bytecount = cpu_to_le32(rsp_size);
+ ms_pkt->req_bytecount = cpu_to_le32(req_size);
+
+ ms_pkt->dseg_req_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
+ ms_pkt->dseg_req_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
+ ms_pkt->dseg_req_length = ms_pkt->req_bytecount;
+
+ ms_pkt->dseg_rsp_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
+ ms_pkt->dseg_rsp_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
+ ms_pkt->dseg_rsp_length = ms_pkt->rsp_bytecount;
+
+ return ms_pkt;
+}
+
+/**
+ * qla24xx_prep_ms_fdmi_iocb() - Prepare common MS IOCB fields for FDMI query.
+ * @ha: HA context
+ * @req_size: request size in bytes
+ * @rsp_size: response size in bytes
+ *
+ * Returns a pointer to the @ha's ms_iocb.
+ */
+void *
+qla24xx_prep_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size,
+ uint32_t rsp_size)
+{
+ struct ct_entry_24xx *ct_pkt;
+ struct qla_hw_data *ha = vha->hw;
+
+ ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
+ memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
+
+ ct_pkt->entry_type = CT_IOCB_TYPE;
+ ct_pkt->entry_count = 1;
+ ct_pkt->nport_handle = cpu_to_le16(vha->mgmt_svr_loop_id);
+ ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
+ ct_pkt->cmd_dsd_count = __constant_cpu_to_le16(1);
+ ct_pkt->rsp_dsd_count = __constant_cpu_to_le16(1);
+ ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size);
+ ct_pkt->cmd_byte_count = cpu_to_le32(req_size);
+
+ ct_pkt->dseg_0_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
+ ct_pkt->dseg_0_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
+ ct_pkt->dseg_0_len = ct_pkt->cmd_byte_count;
+
+ ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
+ ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
+ ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count;
+ ct_pkt->vp_index = vha->vp_idx;
+
+ return ct_pkt;
+}
+
+static inline ms_iocb_entry_t *
+qla2x00_update_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size)
+{
+ struct qla_hw_data *ha = vha->hw;
+ ms_iocb_entry_t *ms_pkt = ha->ms_iocb;
+ struct ct_entry_24xx *ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
+
+ if (IS_FWI2_CAPABLE(ha)) {
+ ct_pkt->cmd_byte_count = cpu_to_le32(req_size);
+ ct_pkt->dseg_0_len = ct_pkt->cmd_byte_count;
+ } else {
+ ms_pkt->req_bytecount = cpu_to_le32(req_size);
+ ms_pkt->dseg_req_length = ms_pkt->req_bytecount;
+ }
+
+ return ms_pkt;
+}
+
+/**
+ * qla2x00_prep_ct_req() - Prepare common CT request fields for SNS query.
+ * @ct_req: CT request buffer
+ * @cmd: GS command
+ * @rsp_size: response size in bytes
+ *
+ * Returns a pointer to the intitialized @ct_req.
+ */
+static inline struct ct_sns_req *
+qla2x00_prep_ct_fdmi_req(struct ct_sns_req *ct_req, uint16_t cmd,
+ uint16_t rsp_size)
+{
+ memset(ct_req, 0, sizeof(struct ct_sns_pkt));
+
+ ct_req->header.revision = 0x01;
+ ct_req->header.gs_type = 0xFA;
+ ct_req->header.gs_subtype = 0x10;
+ ct_req->command = cpu_to_be16(cmd);
+ ct_req->max_rsp_size = cpu_to_be16((rsp_size - 16) / 4);
+
+ return ct_req;
+}
+
+/**
+ * qla2x00_fdmi_rhba() -
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_fdmi_rhba(scsi_qla_host_t *vha)
+{
+ int rval, alen;
+ uint32_t size, sn;
+
+ ms_iocb_entry_t *ms_pkt;
+ struct ct_sns_req *ct_req;
+ struct ct_sns_rsp *ct_rsp;
+ uint8_t *entries;
+ struct ct_fdmi_hba_attr *eiter;
+ struct qla_hw_data *ha = vha->hw;
+
+ /* Issue RHBA */
+ /* Prepare common MS IOCB */
+ /* Request size adjusted after CT preparation */
+ ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(vha, 0, RHBA_RSP_SIZE);
+
+ /* Prepare CT request */
+ ct_req = qla2x00_prep_ct_fdmi_req(&ha->ct_sns->p.req, RHBA_CMD,
+ RHBA_RSP_SIZE);
+ ct_rsp = &ha->ct_sns->p.rsp;
+
+ /* Prepare FDMI command arguments -- attribute block, attributes. */
+ memcpy(ct_req->req.rhba.hba_identifier, vha->port_name, WWN_SIZE);
+ ct_req->req.rhba.entry_count = __constant_cpu_to_be32(1);
+ memcpy(ct_req->req.rhba.port_name, vha->port_name, WWN_SIZE);
+ size = 2 * WWN_SIZE + 4 + 4;
+
+ /* Attributes */
+ ct_req->req.rhba.attrs.count =
+ __constant_cpu_to_be32(FDMI_HBA_ATTR_COUNT);
+ entries = ct_req->req.rhba.hba_identifier;
+
+ /* Nodename. */
+ eiter = (struct ct_fdmi_hba_attr *) (entries + size);
+ eiter->type = __constant_cpu_to_be16(FDMI_HBA_NODE_NAME);
+ eiter->len = __constant_cpu_to_be16(4 + WWN_SIZE);
+ memcpy(eiter->a.node_name, vha->node_name, WWN_SIZE);
+ size += 4 + WWN_SIZE;
+
+ ql_dbg(ql_dbg_disc, vha, 0x2025,
+ "NodeName = %02x%02x%02x%02x%02x%02x%02x%02x.\n",
+ eiter->a.node_name[0], eiter->a.node_name[1],
+ eiter->a.node_name[2], eiter->a.node_name[3],
+ eiter->a.node_name[4], eiter->a.node_name[5],
+ eiter->a.node_name[6], eiter->a.node_name[7]);
+
+ /* Manufacturer. */
+ eiter = (struct ct_fdmi_hba_attr *) (entries + size);
+ eiter->type = __constant_cpu_to_be16(FDMI_HBA_MANUFACTURER);
+ strcpy(eiter->a.manufacturer, "QLogic Corporation");
+ alen = strlen(eiter->a.manufacturer);
+ alen += (alen & 3) ? (4 - (alen & 3)) : 4;
+ eiter->len = cpu_to_be16(4 + alen);
+ size += 4 + alen;
+
+ ql_dbg(ql_dbg_disc, vha, 0x2026,
+ "Manufacturer = %s.\n", eiter->a.manufacturer);
+
+ /* Serial number. */
+ eiter = (struct ct_fdmi_hba_attr *) (entries + size);
+ eiter->type = __constant_cpu_to_be16(FDMI_HBA_SERIAL_NUMBER);
+ sn = ((ha->serial0 & 0x1f) << 16) | (ha->serial2 << 8) | ha->serial1;
+ sprintf(eiter->a.serial_num, "%c%05d", 'A' + sn / 100000, sn % 100000);
+ alen = strlen(eiter->a.serial_num);
+ alen += (alen & 3) ? (4 - (alen & 3)) : 4;
+ eiter->len = cpu_to_be16(4 + alen);
+ size += 4 + alen;
+
+ ql_dbg(ql_dbg_disc, vha, 0x2027,
+ "Serial no. = %s.\n", eiter->a.serial_num);
+
+ /* Model name. */
+ eiter = (struct ct_fdmi_hba_attr *) (entries + size);
+ eiter->type = __constant_cpu_to_be16(FDMI_HBA_MODEL);
+ strcpy(eiter->a.model, ha->model_number);
+ alen = strlen(eiter->a.model);
+ alen += (alen & 3) ? (4 - (alen & 3)) : 4;
+ eiter->len = cpu_to_be16(4 + alen);
+ size += 4 + alen;
+
+ ql_dbg(ql_dbg_disc, vha, 0x2028,
+ "Model Name = %s.\n", eiter->a.model);
+
+ /* Model description. */
+ eiter = (struct ct_fdmi_hba_attr *) (entries + size);
+ eiter->type = __constant_cpu_to_be16(FDMI_HBA_MODEL_DESCRIPTION);
+ if (ha->model_desc)
+ strncpy(eiter->a.model_desc, ha->model_desc, 80);
+ alen = strlen(eiter->a.model_desc);
+ alen += (alen & 3) ? (4 - (alen & 3)) : 4;
+ eiter->len = cpu_to_be16(4 + alen);
+ size += 4 + alen;
+
+ ql_dbg(ql_dbg_disc, vha, 0x2029,
+ "Model Desc = %s.\n", eiter->a.model_desc);
+
+ /* Hardware version. */
+ eiter = (struct ct_fdmi_hba_attr *) (entries + size);
+ eiter->type = __constant_cpu_to_be16(FDMI_HBA_HARDWARE_VERSION);
+ strcpy(eiter->a.hw_version, ha->adapter_id);
+ alen = strlen(eiter->a.hw_version);
+ alen += (alen & 3) ? (4 - (alen & 3)) : 4;
+ eiter->len = cpu_to_be16(4 + alen);
+ size += 4 + alen;
+
+ ql_dbg(ql_dbg_disc, vha, 0x202a,
+ "Hardware ver = %s.\n", eiter->a.hw_version);
+
+ /* Driver version. */
+ eiter = (struct ct_fdmi_hba_attr *) (entries + size);
+ eiter->type = __constant_cpu_to_be16(FDMI_HBA_DRIVER_VERSION);
+ strcpy(eiter->a.driver_version, qla2x00_version_str);
+ alen = strlen(eiter->a.driver_version);
+ alen += (alen & 3) ? (4 - (alen & 3)) : 4;
+ eiter->len = cpu_to_be16(4 + alen);
+ size += 4 + alen;
+
+ ql_dbg(ql_dbg_disc, vha, 0x202b,
+ "Driver ver = %s.\n", eiter->a.driver_version);
+
+ /* Option ROM version. */
+ eiter = (struct ct_fdmi_hba_attr *) (entries + size);
+ eiter->type = __constant_cpu_to_be16(FDMI_HBA_OPTION_ROM_VERSION);
+ strcpy(eiter->a.orom_version, "0.00");
+ alen = strlen(eiter->a.orom_version);
+ alen += (alen & 3) ? (4 - (alen & 3)) : 4;
+ eiter->len = cpu_to_be16(4 + alen);
+ size += 4 + alen;
+
+ ql_dbg(ql_dbg_disc, vha , 0x202c,
+ "Optrom vers = %s.\n", eiter->a.orom_version);
+
+ /* Firmware version */
+ eiter = (struct ct_fdmi_hba_attr *) (entries + size);
+ eiter->type = __constant_cpu_to_be16(FDMI_HBA_FIRMWARE_VERSION);
+ ha->isp_ops->fw_version_str(vha, eiter->a.fw_version);
+ alen = strlen(eiter->a.fw_version);
+ alen += (alen & 3) ? (4 - (alen & 3)) : 4;
+ eiter->len = cpu_to_be16(4 + alen);
+ size += 4 + alen;
+
+ ql_dbg(ql_dbg_disc, vha, 0x202d,
+ "Firmware vers = %s.\n", eiter->a.fw_version);
+
+ /* Update MS request size. */
+ qla2x00_update_ms_fdmi_iocb(vha, size + 16);
+
+ ql_dbg(ql_dbg_disc, vha, 0x202e,
+ "RHBA identifier = "
+ "%02x%02x%02x%02x%02x%02x%02x%02x size=%d.\n",
+ ct_req->req.rhba.hba_identifier[0],
+ ct_req->req.rhba.hba_identifier[1],
+ ct_req->req.rhba.hba_identifier[2],
+ ct_req->req.rhba.hba_identifier[3],
+ ct_req->req.rhba.hba_identifier[4],
+ ct_req->req.rhba.hba_identifier[5],
+ ct_req->req.rhba.hba_identifier[6],
+ ct_req->req.rhba.hba_identifier[7], size);
+ ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2076,
+ entries, size);
+
+ /* Execute MS IOCB */
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
+ sizeof(ms_iocb_entry_t));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_disc, vha, 0x2030,
+ "RHBA issue IOCB failed (%d).\n", rval);
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RHBA") !=
+ QLA_SUCCESS) {
+ rval = QLA_FUNCTION_FAILED;
+ if (ct_rsp->header.reason_code == CT_REASON_CANNOT_PERFORM &&
+ ct_rsp->header.explanation_code ==
+ CT_EXPL_ALREADY_REGISTERED) {
+ ql_dbg(ql_dbg_disc, vha, 0x2034,
+ "HBA already registered.\n");
+ rval = QLA_ALREADY_REGISTERED;
+ }
+ } else {
+ ql_dbg(ql_dbg_disc, vha, 0x2035,
+ "RHBA exiting normally.\n");
+ }
+
+ return rval;
+}
+
+/**
+ * qla2x00_fdmi_dhba() -
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_fdmi_dhba(scsi_qla_host_t *vha)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ ms_iocb_entry_t *ms_pkt;
+ struct ct_sns_req *ct_req;
+ struct ct_sns_rsp *ct_rsp;
+
+ /* Issue RPA */
+ /* Prepare common MS IOCB */
+ ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(vha, DHBA_REQ_SIZE,
+ DHBA_RSP_SIZE);
+
+ /* Prepare CT request */
+ ct_req = qla2x00_prep_ct_fdmi_req(&ha->ct_sns->p.req, DHBA_CMD,
+ DHBA_RSP_SIZE);
+ ct_rsp = &ha->ct_sns->p.rsp;
+
+ /* Prepare FDMI command arguments -- portname. */
+ memcpy(ct_req->req.dhba.port_name, vha->port_name, WWN_SIZE);
+
+ ql_dbg(ql_dbg_disc, vha, 0x2036,
+ "DHBA portname = %02x%02x%02x%02x%02x%02x%02x%02x.\n",
+ ct_req->req.dhba.port_name[0], ct_req->req.dhba.port_name[1],
+ ct_req->req.dhba.port_name[2], ct_req->req.dhba.port_name[3],
+ ct_req->req.dhba.port_name[4], ct_req->req.dhba.port_name[5],
+ ct_req->req.dhba.port_name[6], ct_req->req.dhba.port_name[7]);
+
+ /* Execute MS IOCB */
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
+ sizeof(ms_iocb_entry_t));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_disc, vha, 0x2037,
+ "DHBA issue IOCB failed (%d).\n", rval);
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "DHBA") !=
+ QLA_SUCCESS) {
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ ql_dbg(ql_dbg_disc, vha, 0x2038,
+ "DHBA exiting normally.\n");
+ }
+
+ return rval;
+}
+
+/**
+ * qla2x00_fdmi_rpa() -
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_fdmi_rpa(scsi_qla_host_t *vha)
+{
+ int rval, alen;
+ uint32_t size, max_frame_size;
+ struct qla_hw_data *ha = vha->hw;
+ ms_iocb_entry_t *ms_pkt;
+ struct ct_sns_req *ct_req;
+ struct ct_sns_rsp *ct_rsp;
+ uint8_t *entries;
+ struct ct_fdmi_port_attr *eiter;
+ struct init_cb_24xx *icb24 = (struct init_cb_24xx *)ha->init_cb;
+
+ /* Issue RPA */
+ /* Prepare common MS IOCB */
+ /* Request size adjusted after CT preparation */
+ ms_pkt = ha->isp_ops->prep_ms_fdmi_iocb(vha, 0, RPA_RSP_SIZE);
+
+ /* Prepare CT request */
+ ct_req = qla2x00_prep_ct_fdmi_req(&ha->ct_sns->p.req, RPA_CMD,
+ RPA_RSP_SIZE);
+ ct_rsp = &ha->ct_sns->p.rsp;
+
+ /* Prepare FDMI command arguments -- attribute block, attributes. */
+ memcpy(ct_req->req.rpa.port_name, vha->port_name, WWN_SIZE);
+ size = WWN_SIZE + 4;
+
+ /* Attributes */
+ ct_req->req.rpa.attrs.count =
+ __constant_cpu_to_be32(FDMI_PORT_ATTR_COUNT - 1);
+ entries = ct_req->req.rpa.port_name;
+
+ /* FC4 types. */
+ eiter = (struct ct_fdmi_port_attr *) (entries + size);
+ eiter->type = __constant_cpu_to_be16(FDMI_PORT_FC4_TYPES);
+ eiter->len = __constant_cpu_to_be16(4 + 32);
+ eiter->a.fc4_types[2] = 0x01;
+ size += 4 + 32;
+
+ ql_dbg(ql_dbg_disc, vha, 0x2039,
+ "FC4_TYPES=%02x %02x.\n",
+ eiter->a.fc4_types[2],
+ eiter->a.fc4_types[1]);
+
+ /* Supported speed. */
+ eiter = (struct ct_fdmi_port_attr *) (entries + size);
+ eiter->type = __constant_cpu_to_be16(FDMI_PORT_SUPPORT_SPEED);
+ eiter->len = __constant_cpu_to_be16(4 + 4);
+ if (IS_CNA_CAPABLE(ha))
+ eiter->a.sup_speed = __constant_cpu_to_be32(
+ FDMI_PORT_SPEED_10GB);
+ else if (IS_QLA25XX(ha))
+ eiter->a.sup_speed = __constant_cpu_to_be32(
+ FDMI_PORT_SPEED_1GB|FDMI_PORT_SPEED_2GB|
+ FDMI_PORT_SPEED_4GB|FDMI_PORT_SPEED_8GB);
+ else if (IS_QLA24XX_TYPE(ha))
+ eiter->a.sup_speed = __constant_cpu_to_be32(
+ FDMI_PORT_SPEED_1GB|FDMI_PORT_SPEED_2GB|
+ FDMI_PORT_SPEED_4GB);
+ else if (IS_QLA23XX(ha))
+ eiter->a.sup_speed =__constant_cpu_to_be32(
+ FDMI_PORT_SPEED_1GB|FDMI_PORT_SPEED_2GB);
+ else
+ eiter->a.sup_speed = __constant_cpu_to_be32(
+ FDMI_PORT_SPEED_1GB);
+ size += 4 + 4;
+
+ ql_dbg(ql_dbg_disc, vha, 0x203a,
+ "Supported_Speed=%x.\n", eiter->a.sup_speed);
+
+ /* Current speed. */
+ eiter = (struct ct_fdmi_port_attr *) (entries + size);
+ eiter->type = __constant_cpu_to_be16(FDMI_PORT_CURRENT_SPEED);
+ eiter->len = __constant_cpu_to_be16(4 + 4);
+ switch (ha->link_data_rate) {
+ case PORT_SPEED_1GB:
+ eiter->a.cur_speed =
+ __constant_cpu_to_be32(FDMI_PORT_SPEED_1GB);
+ break;
+ case PORT_SPEED_2GB:
+ eiter->a.cur_speed =
+ __constant_cpu_to_be32(FDMI_PORT_SPEED_2GB);
+ break;
+ case PORT_SPEED_4GB:
+ eiter->a.cur_speed =
+ __constant_cpu_to_be32(FDMI_PORT_SPEED_4GB);
+ break;
+ case PORT_SPEED_8GB:
+ eiter->a.cur_speed =
+ __constant_cpu_to_be32(FDMI_PORT_SPEED_8GB);
+ break;
+ case PORT_SPEED_10GB:
+ eiter->a.cur_speed =
+ __constant_cpu_to_be32(FDMI_PORT_SPEED_10GB);
+ break;
+ case PORT_SPEED_16GB:
+ eiter->a.cur_speed =
+ __constant_cpu_to_be32(FDMI_PORT_SPEED_16GB);
+ break;
+ default:
+ eiter->a.cur_speed =
+ __constant_cpu_to_be32(FDMI_PORT_SPEED_UNKNOWN);
+ break;
+ }
+ size += 4 + 4;
+
+ ql_dbg(ql_dbg_disc, vha, 0x203b,
+ "Current_Speed=%x.\n", eiter->a.cur_speed);
+
+ /* Max frame size. */
+ eiter = (struct ct_fdmi_port_attr *) (entries + size);
+ eiter->type = __constant_cpu_to_be16(FDMI_PORT_MAX_FRAME_SIZE);
+ eiter->len = __constant_cpu_to_be16(4 + 4);
+ max_frame_size = IS_FWI2_CAPABLE(ha) ?
+ le16_to_cpu(icb24->frame_payload_size):
+ le16_to_cpu(ha->init_cb->frame_payload_size);
+ eiter->a.max_frame_size = cpu_to_be32(max_frame_size);
+ size += 4 + 4;
+
+ ql_dbg(ql_dbg_disc, vha, 0x203c,
+ "Max_Frame_Size=%x.\n", eiter->a.max_frame_size);
+
+ /* OS device name. */
+ eiter = (struct ct_fdmi_port_attr *) (entries + size);
+ eiter->type = __constant_cpu_to_be16(FDMI_PORT_OS_DEVICE_NAME);
+ strcpy(eiter->a.os_dev_name, QLA2XXX_DRIVER_NAME);
+ alen = strlen(eiter->a.os_dev_name);
+ alen += (alen & 3) ? (4 - (alen & 3)) : 4;
+ eiter->len = cpu_to_be16(4 + alen);
+ size += 4 + alen;
+
+ ql_dbg(ql_dbg_disc, vha, 0x204b,
+ "OS_Device_Name=%s.\n", eiter->a.os_dev_name);
+
+ /* Hostname. */
+ if (strlen(fc_host_system_hostname(vha->host))) {
+ ct_req->req.rpa.attrs.count =
+ __constant_cpu_to_be32(FDMI_PORT_ATTR_COUNT);
+ eiter = (struct ct_fdmi_port_attr *) (entries + size);
+ eiter->type = __constant_cpu_to_be16(FDMI_PORT_HOST_NAME);
+ snprintf(eiter->a.host_name, sizeof(eiter->a.host_name),
+ "%s", fc_host_system_hostname(vha->host));
+ alen = strlen(eiter->a.host_name);
+ alen += (alen & 3) ? (4 - (alen & 3)) : 4;
+ eiter->len = cpu_to_be16(4 + alen);
+ size += 4 + alen;
+
+ ql_dbg(ql_dbg_disc, vha, 0x203d,
+ "HostName=%s.\n", eiter->a.host_name);
+ }
+
+ /* Update MS request size. */
+ qla2x00_update_ms_fdmi_iocb(vha, size + 16);
+
+ ql_dbg(ql_dbg_disc, vha, 0x203e,
+ "RPA portname= %02x%02x%02x%02x%02X%02x%02x%02x size=%d.\n",
+ ct_req->req.rpa.port_name[0], ct_req->req.rpa.port_name[1],
+ ct_req->req.rpa.port_name[2], ct_req->req.rpa.port_name[3],
+ ct_req->req.rpa.port_name[4], ct_req->req.rpa.port_name[5],
+ ct_req->req.rpa.port_name[6], ct_req->req.rpa.port_name[7],
+ size);
+ ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2079,
+ entries, size);
+
+ /* Execute MS IOCB */
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
+ sizeof(ms_iocb_entry_t));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_disc, vha, 0x2040,
+ "RPA issue IOCB failed (%d).\n", rval);
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp, "RPA") !=
+ QLA_SUCCESS) {
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ ql_dbg(ql_dbg_disc, vha, 0x2041,
+ "RPA exiting nornally.\n");
+ }
+
+ return rval;
+}
+
+/**
+ * qla2x00_fdmi_register() -
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+int
+qla2x00_fdmi_register(scsi_qla_host_t *vha)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return QLA_FUNCTION_FAILED;
+
+ rval = qla2x00_mgmt_svr_login(vha);
+ if (rval)
+ return rval;
+
+ rval = qla2x00_fdmi_rhba(vha);
+ if (rval) {
+ if (rval != QLA_ALREADY_REGISTERED)
+ return rval;
+
+ rval = qla2x00_fdmi_dhba(vha);
+ if (rval)
+ return rval;
+
+ rval = qla2x00_fdmi_rhba(vha);
+ if (rval)
+ return rval;
+ }
+ rval = qla2x00_fdmi_rpa(vha);
+
+ return rval;
+}
+
+/**
+ * qla2x00_gfpn_id() - SNS Get Fabric Port Name (GFPN_ID) query.
+ * @ha: HA context
+ * @list: switch info entries to populate
+ *
+ * Returns 0 on success.
+ */
+int
+qla2x00_gfpn_id(scsi_qla_host_t *vha, sw_info_t *list)
+{
+ int rval = QLA_SUCCESS;
+ uint16_t i;
+ struct qla_hw_data *ha = vha->hw;
+ ms_iocb_entry_t *ms_pkt;
+ struct ct_sns_req *ct_req;
+ struct ct_sns_rsp *ct_rsp;
+
+ if (!IS_IIDMA_CAPABLE(ha))
+ return QLA_FUNCTION_FAILED;
+
+ for (i = 0; i < ha->max_fibre_devices; i++) {
+ /* Issue GFPN_ID */
+ /* Prepare common MS IOCB */
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GFPN_ID_REQ_SIZE,
+ GFPN_ID_RSP_SIZE);
+
+ /* Prepare CT request */
+ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GFPN_ID_CMD,
+ GFPN_ID_RSP_SIZE);
+ ct_rsp = &ha->ct_sns->p.rsp;
+
+ /* Prepare CT arguments -- port_id */
+ ct_req->req.port_id.port_id[0] = list[i].d_id.b.domain;
+ ct_req->req.port_id.port_id[1] = list[i].d_id.b.area;
+ ct_req->req.port_id.port_id[2] = list[i].d_id.b.al_pa;
+
+ /* Execute MS IOCB */
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
+ sizeof(ms_iocb_entry_t));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_disc, vha, 0x2023,
+ "GFPN_ID issue IOCB failed (%d).\n", rval);
+ break;
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp,
+ "GFPN_ID") != QLA_SUCCESS) {
+ rval = QLA_FUNCTION_FAILED;
+ break;
+ } else {
+ /* Save fabric portname */
+ memcpy(list[i].fabric_port_name,
+ ct_rsp->rsp.gfpn_id.port_name, WWN_SIZE);
+ }
+
+ /* Last device exit. */
+ if (list[i].d_id.b.rsvd_1 != 0)
+ break;
+ }
+
+ return (rval);
+}
+
+static inline void *
+qla24xx_prep_ms_fm_iocb(scsi_qla_host_t *vha, uint32_t req_size,
+ uint32_t rsp_size)
+{
+ struct ct_entry_24xx *ct_pkt;
+ struct qla_hw_data *ha = vha->hw;
+ ct_pkt = (struct ct_entry_24xx *)ha->ms_iocb;
+ memset(ct_pkt, 0, sizeof(struct ct_entry_24xx));
+
+ ct_pkt->entry_type = CT_IOCB_TYPE;
+ ct_pkt->entry_count = 1;
+ ct_pkt->nport_handle = cpu_to_le16(vha->mgmt_svr_loop_id);
+ ct_pkt->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
+ ct_pkt->cmd_dsd_count = __constant_cpu_to_le16(1);
+ ct_pkt->rsp_dsd_count = __constant_cpu_to_le16(1);
+ ct_pkt->rsp_byte_count = cpu_to_le32(rsp_size);
+ ct_pkt->cmd_byte_count = cpu_to_le32(req_size);
+
+ ct_pkt->dseg_0_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
+ ct_pkt->dseg_0_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
+ ct_pkt->dseg_0_len = ct_pkt->cmd_byte_count;
+
+ ct_pkt->dseg_1_address[0] = cpu_to_le32(LSD(ha->ct_sns_dma));
+ ct_pkt->dseg_1_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma));
+ ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count;
+ ct_pkt->vp_index = vha->vp_idx;
+
+ return ct_pkt;
+}
+
+
+static inline struct ct_sns_req *
+qla24xx_prep_ct_fm_req(struct ct_sns_req *ct_req, uint16_t cmd,
+ uint16_t rsp_size)
+{
+ memset(ct_req, 0, sizeof(struct ct_sns_pkt));
+
+ ct_req->header.revision = 0x01;
+ ct_req->header.gs_type = 0xFA;
+ ct_req->header.gs_subtype = 0x01;
+ ct_req->command = cpu_to_be16(cmd);
+ ct_req->max_rsp_size = cpu_to_be16((rsp_size - 16) / 4);
+
+ return ct_req;
+}
+
+/**
+ * qla2x00_gpsc() - FCS Get Port Speed Capabilities (GPSC) query.
+ * @ha: HA context
+ * @list: switch info entries to populate
+ *
+ * Returns 0 on success.
+ */
+int
+qla2x00_gpsc(scsi_qla_host_t *vha, sw_info_t *list)
+{
+ int rval;
+ uint16_t i;
+ struct qla_hw_data *ha = vha->hw;
+ ms_iocb_entry_t *ms_pkt;
+ struct ct_sns_req *ct_req;
+ struct ct_sns_rsp *ct_rsp;
+
+ if (!IS_IIDMA_CAPABLE(ha))
+ return QLA_FUNCTION_FAILED;
+ if (!ha->flags.gpsc_supported)
+ return QLA_FUNCTION_FAILED;
+
+ rval = qla2x00_mgmt_svr_login(vha);
+ if (rval)
+ return rval;
+
+ for (i = 0; i < ha->max_fibre_devices; i++) {
+ /* Issue GFPN_ID */
+ /* Prepare common MS IOCB */
+ ms_pkt = qla24xx_prep_ms_fm_iocb(vha, GPSC_REQ_SIZE,
+ GPSC_RSP_SIZE);
+
+ /* Prepare CT request */
+ ct_req = qla24xx_prep_ct_fm_req(&ha->ct_sns->p.req,
+ GPSC_CMD, GPSC_RSP_SIZE);
+ ct_rsp = &ha->ct_sns->p.rsp;
+
+ /* Prepare CT arguments -- port_name */
+ memcpy(ct_req->req.gpsc.port_name, list[i].fabric_port_name,
+ WWN_SIZE);
+
+ /* Execute MS IOCB */
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
+ sizeof(ms_iocb_entry_t));
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_disc, vha, 0x2059,
+ "GPSC issue IOCB failed (%d).\n", rval);
+ } else if ((rval = qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp,
+ "GPSC")) != QLA_SUCCESS) {
+ /* FM command unsupported? */
+ if (rval == QLA_INVALID_COMMAND &&
+ (ct_rsp->header.reason_code ==
+ CT_REASON_INVALID_COMMAND_CODE ||
+ ct_rsp->header.reason_code ==
+ CT_REASON_COMMAND_UNSUPPORTED)) {
+ ql_dbg(ql_dbg_disc, vha, 0x205a,
+ "GPSC command unsupported, disabling "
+ "query.\n");
+ ha->flags.gpsc_supported = 0;
+ rval = QLA_FUNCTION_FAILED;
+ break;
+ }
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ /* Save port-speed */
+ switch (be16_to_cpu(ct_rsp->rsp.gpsc.speed)) {
+ case BIT_15:
+ list[i].fp_speed = PORT_SPEED_1GB;
+ break;
+ case BIT_14:
+ list[i].fp_speed = PORT_SPEED_2GB;
+ break;
+ case BIT_13:
+ list[i].fp_speed = PORT_SPEED_4GB;
+ break;
+ case BIT_12:
+ list[i].fp_speed = PORT_SPEED_10GB;
+ break;
+ case BIT_11:
+ list[i].fp_speed = PORT_SPEED_8GB;
+ break;
+ }
+
+ ql_dbg(ql_dbg_disc, vha, 0x205b,
+ "GPSC ext entry - fpn "
+ "%02x%02x%02x%02x%02x%02x%02x%02x speeds=%04x "
+ "speed=%04x.\n",
+ list[i].fabric_port_name[0],
+ list[i].fabric_port_name[1],
+ list[i].fabric_port_name[2],
+ list[i].fabric_port_name[3],
+ list[i].fabric_port_name[4],
+ list[i].fabric_port_name[5],
+ list[i].fabric_port_name[6],
+ list[i].fabric_port_name[7],
+ be16_to_cpu(ct_rsp->rsp.gpsc.speeds),
+ be16_to_cpu(ct_rsp->rsp.gpsc.speed));
+ }
+
+ /* Last device exit. */
+ if (list[i].d_id.b.rsvd_1 != 0)
+ break;
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_gff_id() - SNS Get FC-4 Features (GFF_ID) query.
+ *
+ * @ha: HA context
+ * @list: switch info entries to populate
+ *
+ */
+void
+qla2x00_gff_id(scsi_qla_host_t *vha, sw_info_t *list)
+{
+ int rval;
+ uint16_t i;
+
+ ms_iocb_entry_t *ms_pkt;
+ struct ct_sns_req *ct_req;
+ struct ct_sns_rsp *ct_rsp;
+ struct qla_hw_data *ha = vha->hw;
+ uint8_t fcp_scsi_features = 0;
+
+ for (i = 0; i < ha->max_fibre_devices; i++) {
+ /* Set default FC4 Type as UNKNOWN so the default is to
+ * Process this port */
+ list[i].fc4_type = FC4_TYPE_UNKNOWN;
+
+ /* Do not attempt GFF_ID if we are not FWI_2 capable */
+ if (!IS_FWI2_CAPABLE(ha))
+ continue;
+
+ /* Prepare common MS IOCB */
+ ms_pkt = ha->isp_ops->prep_ms_iocb(vha, GFF_ID_REQ_SIZE,
+ GFF_ID_RSP_SIZE);
+
+ /* Prepare CT request */
+ ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GFF_ID_CMD,
+ GFF_ID_RSP_SIZE);
+ ct_rsp = &ha->ct_sns->p.rsp;
+
+ /* Prepare CT arguments -- port_id */
+ ct_req->req.port_id.port_id[0] = list[i].d_id.b.domain;
+ ct_req->req.port_id.port_id[1] = list[i].d_id.b.area;
+ ct_req->req.port_id.port_id[2] = list[i].d_id.b.al_pa;
+
+ /* Execute MS IOCB */
+ rval = qla2x00_issue_iocb(vha, ha->ms_iocb, ha->ms_iocb_dma,
+ sizeof(ms_iocb_entry_t));
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_disc, vha, 0x205c,
+ "GFF_ID issue IOCB failed (%d).\n", rval);
+ } else if (qla2x00_chk_ms_status(vha, ms_pkt, ct_rsp,
+ "GFF_ID") != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_disc, vha, 0x205d,
+ "GFF_ID IOCB status had a failure status code.\n");
+ } else {
+ fcp_scsi_features =
+ ct_rsp->rsp.gff_id.fc4_features[GFF_FCP_SCSI_OFFSET];
+ fcp_scsi_features &= 0x0f;
+
+ if (fcp_scsi_features)
+ list[i].fc4_type = FC4_TYPE_FCP_SCSI;
+ else
+ list[i].fc4_type = FC4_TYPE_OTHER;
+ }
+
+ /* Last device exit. */
+ if (list[i].d_id.b.rsvd_1 != 0)
+ break;
+ }
+}
diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c
new file mode 100644
index 00000000..b9465643
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_init.c
@@ -0,0 +1,5558 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2003-2011 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#include "qla_def.h"
+#include "qla_gbl.h"
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+
+#include "qla_devtbl.h"
+
+#ifdef CONFIG_SPARC
+#include <asm/prom.h>
+#endif
+
+/*
+* QLogic ISP2x00 Hardware Support Function Prototypes.
+*/
+static int qla2x00_isp_firmware(scsi_qla_host_t *);
+static int qla2x00_setup_chip(scsi_qla_host_t *);
+static int qla2x00_init_rings(scsi_qla_host_t *);
+static int qla2x00_fw_ready(scsi_qla_host_t *);
+static int qla2x00_configure_hba(scsi_qla_host_t *);
+static int qla2x00_configure_loop(scsi_qla_host_t *);
+static int qla2x00_configure_local_loop(scsi_qla_host_t *);
+static int qla2x00_configure_fabric(scsi_qla_host_t *);
+static int qla2x00_find_all_fabric_devs(scsi_qla_host_t *, struct list_head *);
+static int qla2x00_fabric_dev_login(scsi_qla_host_t *, fc_port_t *,
+ uint16_t *);
+
+static int qla2x00_restart_isp(scsi_qla_host_t *);
+
+static struct qla_chip_state_84xx *qla84xx_get_chip(struct scsi_qla_host *);
+static int qla84xx_init_chip(scsi_qla_host_t *);
+static int qla25xx_init_queues(struct qla_hw_data *);
+
+/* SRB Extensions ---------------------------------------------------------- */
+
+void
+qla2x00_sp_timeout(unsigned long __data)
+{
+ srb_t *sp = (srb_t *)__data;
+ struct srb_iocb *iocb;
+ fc_port_t *fcport = sp->fcport;
+ struct qla_hw_data *ha = fcport->vha->hw;
+ struct req_que *req;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ req = ha->req_q_map[0];
+ req->outstanding_cmds[sp->handle] = NULL;
+ iocb = &sp->u.iocb_cmd;
+ iocb->timeout(sp);
+ sp->free(fcport->vha, sp);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+void
+qla2x00_sp_free(void *data, void *ptr)
+{
+ srb_t *sp = (srb_t *)ptr;
+ struct srb_iocb *iocb = &sp->u.iocb_cmd;
+ struct scsi_qla_host *vha = (scsi_qla_host_t *)data;
+
+ del_timer(&iocb->timer);
+ mempool_free(sp, vha->hw->srb_mempool);
+
+ QLA_VHA_MARK_NOT_BUSY(vha);
+}
+
+/* Asynchronous Login/Logout Routines -------------------------------------- */
+
+static inline unsigned long
+qla2x00_get_async_timeout(struct scsi_qla_host *vha)
+{
+ unsigned long tmo;
+ struct qla_hw_data *ha = vha->hw;
+
+ /* Firmware should use switch negotiated r_a_tov for timeout. */
+ tmo = ha->r_a_tov / 10 * 2;
+ if (!IS_FWI2_CAPABLE(ha)) {
+ /*
+ * Except for earlier ISPs where the timeout is seeded from the
+ * initialization control block.
+ */
+ tmo = ha->login_timeout;
+ }
+ return tmo;
+}
+
+static void
+qla2x00_async_iocb_timeout(void *data)
+{
+ srb_t *sp = (srb_t *)data;
+ fc_port_t *fcport = sp->fcport;
+
+ ql_dbg(ql_dbg_disc, fcport->vha, 0x2071,
+ "Async-%s timeout - hdl=%x portid=%02x%02x%02x.\n",
+ sp->name, sp->handle, fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa);
+
+ fcport->flags &= ~FCF_ASYNC_SENT;
+ if (sp->type == SRB_LOGIN_CMD) {
+ struct srb_iocb *lio = &sp->u.iocb_cmd;
+ qla2x00_post_async_logout_work(fcport->vha, fcport, NULL);
+ /* Retry as needed. */
+ lio->u.logio.data[0] = MBS_COMMAND_ERROR;
+ lio->u.logio.data[1] = lio->u.logio.flags & SRB_LOGIN_RETRIED ?
+ QLA_LOGIO_LOGIN_RETRIED : 0;
+ qla2x00_post_async_login_done_work(fcport->vha, fcport,
+ lio->u.logio.data);
+ }
+}
+
+static void
+qla2x00_async_login_sp_done(void *data, void *ptr, int res)
+{
+ srb_t *sp = (srb_t *)ptr;
+ struct srb_iocb *lio = &sp->u.iocb_cmd;
+ struct scsi_qla_host *vha = (scsi_qla_host_t *)data;
+
+ if (!test_bit(UNLOADING, &vha->dpc_flags))
+ qla2x00_post_async_login_done_work(sp->fcport->vha, sp->fcport,
+ lio->u.logio.data);
+ sp->free(sp->fcport->vha, sp);
+}
+
+int
+qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport,
+ uint16_t *data)
+{
+ srb_t *sp;
+ struct srb_iocb *lio;
+ int rval;
+
+ rval = QLA_FUNCTION_FAILED;
+ sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
+ if (!sp)
+ goto done;
+
+ sp->type = SRB_LOGIN_CMD;
+ sp->name = "login";
+ qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+
+ lio = &sp->u.iocb_cmd;
+ lio->timeout = qla2x00_async_iocb_timeout;
+ sp->done = qla2x00_async_login_sp_done;
+ lio->u.logio.flags |= SRB_LOGIN_COND_PLOGI;
+ if (data[1] & QLA_LOGIO_LOGIN_RETRIED)
+ lio->u.logio.flags |= SRB_LOGIN_RETRIED;
+ rval = qla2x00_start_sp(sp);
+ if (rval != QLA_SUCCESS)
+ goto done_free_sp;
+
+ ql_dbg(ql_dbg_disc, vha, 0x2072,
+ "Async-login - hdl=%x, loopid=%x portid=%02x%02x%02x "
+ "retries=%d.\n", sp->handle, fcport->loop_id,
+ fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa,
+ fcport->login_retry);
+ return rval;
+
+done_free_sp:
+ sp->free(fcport->vha, sp);
+done:
+ return rval;
+}
+
+static void
+qla2x00_async_logout_sp_done(void *data, void *ptr, int res)
+{
+ srb_t *sp = (srb_t *)ptr;
+ struct srb_iocb *lio = &sp->u.iocb_cmd;
+ struct scsi_qla_host *vha = (scsi_qla_host_t *)data;
+
+ if (!test_bit(UNLOADING, &vha->dpc_flags))
+ qla2x00_post_async_logout_done_work(sp->fcport->vha, sp->fcport,
+ lio->u.logio.data);
+ sp->free(sp->fcport->vha, sp);
+}
+
+int
+qla2x00_async_logout(struct scsi_qla_host *vha, fc_port_t *fcport)
+{
+ srb_t *sp;
+ struct srb_iocb *lio;
+ int rval;
+
+ rval = QLA_FUNCTION_FAILED;
+ sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
+ if (!sp)
+ goto done;
+
+ sp->type = SRB_LOGOUT_CMD;
+ sp->name = "logout";
+ qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+
+ lio = &sp->u.iocb_cmd;
+ lio->timeout = qla2x00_async_iocb_timeout;
+ sp->done = qla2x00_async_logout_sp_done;
+ rval = qla2x00_start_sp(sp);
+ if (rval != QLA_SUCCESS)
+ goto done_free_sp;
+
+ ql_dbg(ql_dbg_disc, vha, 0x2070,
+ "Async-logout - hdl=%x loop-id=%x portid=%02x%02x%02x.\n",
+ sp->handle, fcport->loop_id, fcport->d_id.b.domain,
+ fcport->d_id.b.area, fcport->d_id.b.al_pa);
+ return rval;
+
+done_free_sp:
+ sp->free(fcport->vha, sp);
+done:
+ return rval;
+}
+
+static void
+qla2x00_async_adisc_sp_done(void *data, void *ptr, int res)
+{
+ srb_t *sp = (srb_t *)ptr;
+ struct srb_iocb *lio = &sp->u.iocb_cmd;
+ struct scsi_qla_host *vha = (scsi_qla_host_t *)data;
+
+ if (!test_bit(UNLOADING, &vha->dpc_flags))
+ qla2x00_post_async_adisc_done_work(sp->fcport->vha, sp->fcport,
+ lio->u.logio.data);
+ sp->free(sp->fcport->vha, sp);
+}
+
+int
+qla2x00_async_adisc(struct scsi_qla_host *vha, fc_port_t *fcport,
+ uint16_t *data)
+{
+ srb_t *sp;
+ struct srb_iocb *lio;
+ int rval;
+
+ rval = QLA_FUNCTION_FAILED;
+ sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
+ if (!sp)
+ goto done;
+
+ sp->type = SRB_ADISC_CMD;
+ sp->name = "adisc";
+ qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+
+ lio = &sp->u.iocb_cmd;
+ lio->timeout = qla2x00_async_iocb_timeout;
+ sp->done = qla2x00_async_adisc_sp_done;
+ if (data[1] & QLA_LOGIO_LOGIN_RETRIED)
+ lio->u.logio.flags |= SRB_LOGIN_RETRIED;
+ rval = qla2x00_start_sp(sp);
+ if (rval != QLA_SUCCESS)
+ goto done_free_sp;
+
+ ql_dbg(ql_dbg_disc, vha, 0x206f,
+ "Async-adisc - hdl=%x loopid=%x portid=%02x%02x%02x.\n",
+ sp->handle, fcport->loop_id, fcport->d_id.b.domain,
+ fcport->d_id.b.area, fcport->d_id.b.al_pa);
+ return rval;
+
+done_free_sp:
+ sp->free(fcport->vha, sp);
+done:
+ return rval;
+}
+
+static void
+qla2x00_async_tm_cmd_done(void *data, void *ptr, int res)
+{
+ srb_t *sp = (srb_t *)ptr;
+ struct srb_iocb *iocb = &sp->u.iocb_cmd;
+ struct scsi_qla_host *vha = (scsi_qla_host_t *)data;
+ uint32_t flags;
+ uint16_t lun;
+ int rval;
+
+ if (!test_bit(UNLOADING, &vha->dpc_flags)) {
+ flags = iocb->u.tmf.flags;
+ lun = (uint16_t)iocb->u.tmf.lun;
+
+ /* Issue Marker IOCB */
+ rval = qla2x00_marker(vha, vha->hw->req_q_map[0],
+ vha->hw->rsp_q_map[0], sp->fcport->loop_id, lun,
+ flags == TCF_LUN_RESET ? MK_SYNC_ID_LUN : MK_SYNC_ID);
+
+ if ((rval != QLA_SUCCESS) || iocb->u.tmf.data) {
+ ql_dbg(ql_dbg_taskm, vha, 0x8030,
+ "TM IOCB failed (%x).\n", rval);
+ }
+ }
+ sp->free(sp->fcport->vha, sp);
+}
+
+int
+qla2x00_async_tm_cmd(fc_port_t *fcport, uint32_t tm_flags, uint32_t lun,
+ uint32_t tag)
+{
+ struct scsi_qla_host *vha = fcport->vha;
+ srb_t *sp;
+ struct srb_iocb *tcf;
+ int rval;
+
+ rval = QLA_FUNCTION_FAILED;
+ sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL);
+ if (!sp)
+ goto done;
+
+ sp->type = SRB_TM_CMD;
+ sp->name = "tmf";
+ qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2);
+
+ tcf = &sp->u.iocb_cmd;
+ tcf->u.tmf.flags = tm_flags;
+ tcf->u.tmf.lun = lun;
+ tcf->u.tmf.data = tag;
+ tcf->timeout = qla2x00_async_iocb_timeout;
+ sp->done = qla2x00_async_tm_cmd_done;
+
+ rval = qla2x00_start_sp(sp);
+ if (rval != QLA_SUCCESS)
+ goto done_free_sp;
+
+ ql_dbg(ql_dbg_taskm, vha, 0x802f,
+ "Async-tmf hdl=%x loop-id=%x portid=%02x%02x%02x.\n",
+ sp->handle, fcport->loop_id, fcport->d_id.b.domain,
+ fcport->d_id.b.area, fcport->d_id.b.al_pa);
+ return rval;
+
+done_free_sp:
+ sp->free(fcport->vha, sp);
+done:
+ return rval;
+}
+
+void
+qla2x00_async_login_done(struct scsi_qla_host *vha, fc_port_t *fcport,
+ uint16_t *data)
+{
+ int rval;
+
+ switch (data[0]) {
+ case MBS_COMMAND_COMPLETE:
+ /*
+ * Driver must validate login state - If PRLI not complete,
+ * force a relogin attempt via implicit LOGO, PLOGI, and PRLI
+ * requests.
+ */
+ rval = qla2x00_get_port_database(vha, fcport, 0);
+ if (rval == QLA_NOT_LOGGED_IN) {
+ fcport->flags &= ~FCF_ASYNC_SENT;
+ fcport->flags |= FCF_LOGIN_NEEDED;
+ set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
+ break;
+ }
+
+ if (rval != QLA_SUCCESS) {
+ qla2x00_post_async_logout_work(vha, fcport, NULL);
+ qla2x00_post_async_login_work(vha, fcport, NULL);
+ break;
+ }
+ if (fcport->flags & FCF_FCP2_DEVICE) {
+ qla2x00_post_async_adisc_work(vha, fcport, data);
+ break;
+ }
+ qla2x00_update_fcport(vha, fcport);
+ break;
+ case MBS_COMMAND_ERROR:
+ fcport->flags &= ~FCF_ASYNC_SENT;
+ if (data[1] & QLA_LOGIO_LOGIN_RETRIED)
+ set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
+ else
+ qla2x00_mark_device_lost(vha, fcport, 1, 0);
+ break;
+ case MBS_PORT_ID_USED:
+ fcport->loop_id = data[1];
+ qla2x00_post_async_logout_work(vha, fcport, NULL);
+ qla2x00_post_async_login_work(vha, fcport, NULL);
+ break;
+ case MBS_LOOP_ID_USED:
+ fcport->loop_id++;
+ rval = qla2x00_find_new_loop_id(vha, fcport);
+ if (rval != QLA_SUCCESS) {
+ fcport->flags &= ~FCF_ASYNC_SENT;
+ qla2x00_mark_device_lost(vha, fcport, 1, 0);
+ break;
+ }
+ qla2x00_post_async_login_work(vha, fcport, NULL);
+ break;
+ }
+ return;
+}
+
+void
+qla2x00_async_logout_done(struct scsi_qla_host *vha, fc_port_t *fcport,
+ uint16_t *data)
+{
+ qla2x00_mark_device_lost(vha, fcport, 1, 0);
+ return;
+}
+
+void
+qla2x00_async_adisc_done(struct scsi_qla_host *vha, fc_port_t *fcport,
+ uint16_t *data)
+{
+ if (data[0] == MBS_COMMAND_COMPLETE) {
+ qla2x00_update_fcport(vha, fcport);
+
+ return;
+ }
+
+ /* Retry login. */
+ fcport->flags &= ~FCF_ASYNC_SENT;
+ if (data[1] & QLA_LOGIO_LOGIN_RETRIED)
+ set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
+ else
+ qla2x00_mark_device_lost(vha, fcport, 1, 0);
+
+ return;
+}
+
+/****************************************************************************/
+/* QLogic ISP2x00 Hardware Support Functions. */
+/****************************************************************************/
+
+/*
+* qla2x00_initialize_adapter
+* Initialize board.
+*
+* Input:
+* ha = adapter block pointer.
+*
+* Returns:
+* 0 = success
+*/
+int
+qla2x00_initialize_adapter(scsi_qla_host_t *vha)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+
+ /* Clear adapter flags. */
+ vha->flags.online = 0;
+ ha->flags.chip_reset_done = 0;
+ vha->flags.reset_active = 0;
+ ha->flags.pci_channel_io_perm_failure = 0;
+ ha->flags.eeh_busy = 0;
+ ha->flags.thermal_supported = 1;
+ atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ vha->device_flags = DFLG_NO_CABLE;
+ vha->dpc_flags = 0;
+ vha->flags.management_server_logged_in = 0;
+ vha->marker_needed = 0;
+ ha->isp_abort_cnt = 0;
+ ha->beacon_blink_led = 0;
+
+ set_bit(0, ha->req_qid_map);
+ set_bit(0, ha->rsp_qid_map);
+
+ ql_dbg(ql_dbg_init, vha, 0x0040,
+ "Configuring PCI space...\n");
+ rval = ha->isp_ops->pci_config(vha);
+ if (rval) {
+ ql_log(ql_log_warn, vha, 0x0044,
+ "Unable to configure PCI space.\n");
+ return (rval);
+ }
+
+ ha->isp_ops->reset_chip(vha);
+
+ rval = qla2xxx_get_flash_info(vha);
+ if (rval) {
+ ql_log(ql_log_fatal, vha, 0x004f,
+ "Unable to validate FLASH data.\n");
+ return (rval);
+ }
+
+ ha->isp_ops->get_flash_version(vha, req->ring);
+ ql_dbg(ql_dbg_init, vha, 0x0061,
+ "Configure NVRAM parameters...\n");
+
+ ha->isp_ops->nvram_config(vha);
+
+ if (ha->flags.disable_serdes) {
+ /* Mask HBA via NVRAM settings? */
+ ql_log(ql_log_info, vha, 0x0077,
+ "Masking HBA WWPN "
+ "%02x%02x%02x%02x%02x%02x%02x%02x (via NVRAM).\n",
+ vha->port_name[0], vha->port_name[1],
+ vha->port_name[2], vha->port_name[3],
+ vha->port_name[4], vha->port_name[5],
+ vha->port_name[6], vha->port_name[7]);
+ return QLA_FUNCTION_FAILED;
+ }
+
+ ql_dbg(ql_dbg_init, vha, 0x0078,
+ "Verifying loaded RISC code...\n");
+
+ if (qla2x00_isp_firmware(vha) != QLA_SUCCESS) {
+ rval = ha->isp_ops->chip_diag(vha);
+ if (rval)
+ return (rval);
+ rval = qla2x00_setup_chip(vha);
+ if (rval)
+ return (rval);
+ }
+
+ if (IS_QLA84XX(ha)) {
+ ha->cs84xx = qla84xx_get_chip(vha);
+ if (!ha->cs84xx) {
+ ql_log(ql_log_warn, vha, 0x00d0,
+ "Unable to configure ISP84XX.\n");
+ return QLA_FUNCTION_FAILED;
+ }
+ }
+ rval = qla2x00_init_rings(vha);
+ ha->flags.chip_reset_done = 1;
+
+ if (rval == QLA_SUCCESS && IS_QLA84XX(ha)) {
+ /* Issue verify 84xx FW IOCB to complete 84xx initialization */
+ rval = qla84xx_init_chip(vha);
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x00d4,
+ "Unable to initialize ISP84XX.\n");
+ qla84xx_put_chip(vha);
+ }
+ }
+
+ if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha))
+ qla24xx_read_fcp_prio_cfg(vha);
+
+ return (rval);
+}
+
+/**
+ * qla2100_pci_config() - Setup ISP21xx PCI configuration registers.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+int
+qla2100_pci_config(scsi_qla_host_t *vha)
+{
+ uint16_t w;
+ unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ pci_set_master(ha->pdev);
+ pci_try_set_mwi(ha->pdev);
+
+ pci_read_config_word(ha->pdev, PCI_COMMAND, &w);
+ w |= (PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
+ pci_write_config_word(ha->pdev, PCI_COMMAND, w);
+
+ pci_disable_rom(ha->pdev);
+
+ /* Get PCI bus information. */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ ha->pci_attr = RD_REG_WORD(&reg->ctrl_status);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return QLA_SUCCESS;
+}
+
+/**
+ * qla2300_pci_config() - Setup ISP23xx PCI configuration registers.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+int
+qla2300_pci_config(scsi_qla_host_t *vha)
+{
+ uint16_t w;
+ unsigned long flags = 0;
+ uint32_t cnt;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ pci_set_master(ha->pdev);
+ pci_try_set_mwi(ha->pdev);
+
+ pci_read_config_word(ha->pdev, PCI_COMMAND, &w);
+ w |= (PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
+
+ if (IS_QLA2322(ha) || IS_QLA6322(ha))
+ w &= ~PCI_COMMAND_INTX_DISABLE;
+ pci_write_config_word(ha->pdev, PCI_COMMAND, w);
+
+ /*
+ * If this is a 2300 card and not 2312, reset the
+ * COMMAND_INVALIDATE due to a bug in the 2300. Unfortunately,
+ * the 2310 also reports itself as a 2300 so we need to get the
+ * fb revision level -- a 6 indicates it really is a 2300 and
+ * not a 2310.
+ */
+ if (IS_QLA2300(ha)) {
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Pause RISC. */
+ WRT_REG_WORD(&reg->hccr, HCCR_PAUSE_RISC);
+ for (cnt = 0; cnt < 30000; cnt++) {
+ if ((RD_REG_WORD(&reg->hccr) & HCCR_RISC_PAUSE) != 0)
+ break;
+
+ udelay(10);
+ }
+
+ /* Select FPM registers. */
+ WRT_REG_WORD(&reg->ctrl_status, 0x20);
+ RD_REG_WORD(&reg->ctrl_status);
+
+ /* Get the fb rev level */
+ ha->fb_rev = RD_FB_CMD_REG(ha, reg);
+
+ if (ha->fb_rev == FPM_2300)
+ pci_clear_mwi(ha->pdev);
+
+ /* Deselect FPM registers. */
+ WRT_REG_WORD(&reg->ctrl_status, 0x0);
+ RD_REG_WORD(&reg->ctrl_status);
+
+ /* Release RISC module. */
+ WRT_REG_WORD(&reg->hccr, HCCR_RELEASE_RISC);
+ for (cnt = 0; cnt < 30000; cnt++) {
+ if ((RD_REG_WORD(&reg->hccr) & HCCR_RISC_PAUSE) == 0)
+ break;
+
+ udelay(10);
+ }
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ }
+
+ pci_write_config_byte(ha->pdev, PCI_LATENCY_TIMER, 0x80);
+
+ pci_disable_rom(ha->pdev);
+
+ /* Get PCI bus information. */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ ha->pci_attr = RD_REG_WORD(&reg->ctrl_status);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return QLA_SUCCESS;
+}
+
+/**
+ * qla24xx_pci_config() - Setup ISP24xx PCI configuration registers.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+int
+qla24xx_pci_config(scsi_qla_host_t *vha)
+{
+ uint16_t w;
+ unsigned long flags = 0;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+ pci_set_master(ha->pdev);
+ pci_try_set_mwi(ha->pdev);
+
+ pci_read_config_word(ha->pdev, PCI_COMMAND, &w);
+ w |= (PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
+ w &= ~PCI_COMMAND_INTX_DISABLE;
+ pci_write_config_word(ha->pdev, PCI_COMMAND, w);
+
+ pci_write_config_byte(ha->pdev, PCI_LATENCY_TIMER, 0x80);
+
+ /* PCI-X -- adjust Maximum Memory Read Byte Count (2048). */
+ if (pci_find_capability(ha->pdev, PCI_CAP_ID_PCIX))
+ pcix_set_mmrbc(ha->pdev, 2048);
+
+ /* PCIe -- adjust Maximum Read Request Size (2048). */
+ if (pci_find_capability(ha->pdev, PCI_CAP_ID_EXP))
+ pcie_set_readrq(ha->pdev, 2048);
+
+ pci_disable_rom(ha->pdev);
+
+ ha->chip_revision = ha->pdev->revision;
+
+ /* Get PCI bus information. */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ ha->pci_attr = RD_REG_DWORD(&reg->ctrl_status);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return QLA_SUCCESS;
+}
+
+/**
+ * qla25xx_pci_config() - Setup ISP25xx PCI configuration registers.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+int
+qla25xx_pci_config(scsi_qla_host_t *vha)
+{
+ uint16_t w;
+ struct qla_hw_data *ha = vha->hw;
+
+ pci_set_master(ha->pdev);
+ pci_try_set_mwi(ha->pdev);
+
+ pci_read_config_word(ha->pdev, PCI_COMMAND, &w);
+ w |= (PCI_COMMAND_PARITY | PCI_COMMAND_SERR);
+ w &= ~PCI_COMMAND_INTX_DISABLE;
+ pci_write_config_word(ha->pdev, PCI_COMMAND, w);
+
+ /* PCIe -- adjust Maximum Read Request Size (2048). */
+ if (pci_find_capability(ha->pdev, PCI_CAP_ID_EXP))
+ pcie_set_readrq(ha->pdev, 2048);
+
+ pci_disable_rom(ha->pdev);
+
+ ha->chip_revision = ha->pdev->revision;
+
+ return QLA_SUCCESS;
+}
+
+/**
+ * qla2x00_isp_firmware() - Choose firmware image.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_isp_firmware(scsi_qla_host_t *vha)
+{
+ int rval;
+ uint16_t loop_id, topo, sw_cap;
+ uint8_t domain, area, al_pa;
+ struct qla_hw_data *ha = vha->hw;
+
+ /* Assume loading risc code */
+ rval = QLA_FUNCTION_FAILED;
+
+ if (ha->flags.disable_risc_code_load) {
+ ql_log(ql_log_info, vha, 0x0079, "RISC CODE NOT loaded.\n");
+
+ /* Verify checksum of loaded RISC code. */
+ rval = qla2x00_verify_checksum(vha, ha->fw_srisc_address);
+ if (rval == QLA_SUCCESS) {
+ /* And, verify we are not in ROM code. */
+ rval = qla2x00_get_adapter_id(vha, &loop_id, &al_pa,
+ &area, &domain, &topo, &sw_cap);
+ }
+ }
+
+ if (rval)
+ ql_dbg(ql_dbg_init, vha, 0x007a,
+ "**** Load RISC code ****.\n");
+
+ return (rval);
+}
+
+/**
+ * qla2x00_reset_chip() - Reset ISP chip.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+void
+qla2x00_reset_chip(scsi_qla_host_t *vha)
+{
+ unsigned long flags = 0;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ uint32_t cnt;
+ uint16_t cmd;
+
+ if (unlikely(pci_channel_offline(ha->pdev)))
+ return;
+
+ ha->isp_ops->disable_intrs(ha);
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Turn off master enable */
+ cmd = 0;
+ pci_read_config_word(ha->pdev, PCI_COMMAND, &cmd);
+ cmd &= ~PCI_COMMAND_MASTER;
+ pci_write_config_word(ha->pdev, PCI_COMMAND, cmd);
+
+ if (!IS_QLA2100(ha)) {
+ /* Pause RISC. */
+ WRT_REG_WORD(&reg->hccr, HCCR_PAUSE_RISC);
+ if (IS_QLA2200(ha) || IS_QLA2300(ha)) {
+ for (cnt = 0; cnt < 30000; cnt++) {
+ if ((RD_REG_WORD(&reg->hccr) &
+ HCCR_RISC_PAUSE) != 0)
+ break;
+ udelay(100);
+ }
+ } else {
+ RD_REG_WORD(&reg->hccr); /* PCI Posting. */
+ udelay(10);
+ }
+
+ /* Select FPM registers. */
+ WRT_REG_WORD(&reg->ctrl_status, 0x20);
+ RD_REG_WORD(&reg->ctrl_status); /* PCI Posting. */
+
+ /* FPM Soft Reset. */
+ WRT_REG_WORD(&reg->fpm_diag_config, 0x100);
+ RD_REG_WORD(&reg->fpm_diag_config); /* PCI Posting. */
+
+ /* Toggle Fpm Reset. */
+ if (!IS_QLA2200(ha)) {
+ WRT_REG_WORD(&reg->fpm_diag_config, 0x0);
+ RD_REG_WORD(&reg->fpm_diag_config); /* PCI Posting. */
+ }
+
+ /* Select frame buffer registers. */
+ WRT_REG_WORD(&reg->ctrl_status, 0x10);
+ RD_REG_WORD(&reg->ctrl_status); /* PCI Posting. */
+
+ /* Reset frame buffer FIFOs. */
+ if (IS_QLA2200(ha)) {
+ WRT_FB_CMD_REG(ha, reg, 0xa000);
+ RD_FB_CMD_REG(ha, reg); /* PCI Posting. */
+ } else {
+ WRT_FB_CMD_REG(ha, reg, 0x00fc);
+
+ /* Read back fb_cmd until zero or 3 seconds max */
+ for (cnt = 0; cnt < 3000; cnt++) {
+ if ((RD_FB_CMD_REG(ha, reg) & 0xff) == 0)
+ break;
+ udelay(100);
+ }
+ }
+
+ /* Select RISC module registers. */
+ WRT_REG_WORD(&reg->ctrl_status, 0);
+ RD_REG_WORD(&reg->ctrl_status); /* PCI Posting. */
+
+ /* Reset RISC processor. */
+ WRT_REG_WORD(&reg->hccr, HCCR_RESET_RISC);
+ RD_REG_WORD(&reg->hccr); /* PCI Posting. */
+
+ /* Release RISC processor. */
+ WRT_REG_WORD(&reg->hccr, HCCR_RELEASE_RISC);
+ RD_REG_WORD(&reg->hccr); /* PCI Posting. */
+ }
+
+ WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
+ WRT_REG_WORD(&reg->hccr, HCCR_CLR_HOST_INT);
+
+ /* Reset ISP chip. */
+ WRT_REG_WORD(&reg->ctrl_status, CSR_ISP_SOFT_RESET);
+
+ /* Wait for RISC to recover from reset. */
+ if (IS_QLA2100(ha) || IS_QLA2200(ha) || IS_QLA2300(ha)) {
+ /*
+ * It is necessary to for a delay here since the card doesn't
+ * respond to PCI reads during a reset. On some architectures
+ * this will result in an MCA.
+ */
+ udelay(20);
+ for (cnt = 30000; cnt; cnt--) {
+ if ((RD_REG_WORD(&reg->ctrl_status) &
+ CSR_ISP_SOFT_RESET) == 0)
+ break;
+ udelay(100);
+ }
+ } else
+ udelay(10);
+
+ /* Reset RISC processor. */
+ WRT_REG_WORD(&reg->hccr, HCCR_RESET_RISC);
+
+ WRT_REG_WORD(&reg->semaphore, 0);
+
+ /* Release RISC processor. */
+ WRT_REG_WORD(&reg->hccr, HCCR_RELEASE_RISC);
+ RD_REG_WORD(&reg->hccr); /* PCI Posting. */
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha) || IS_QLA2300(ha)) {
+ for (cnt = 0; cnt < 30000; cnt++) {
+ if (RD_MAILBOX_REG(ha, reg, 0) != MBS_BUSY)
+ break;
+
+ udelay(100);
+ }
+ } else
+ udelay(100);
+
+ /* Turn on master enable */
+ cmd |= PCI_COMMAND_MASTER;
+ pci_write_config_word(ha->pdev, PCI_COMMAND, cmd);
+
+ /* Disable RISC pause on FPM parity error. */
+ if (!IS_QLA2100(ha)) {
+ WRT_REG_WORD(&reg->hccr, HCCR_DISABLE_PARITY_PAUSE);
+ RD_REG_WORD(&reg->hccr); /* PCI Posting. */
+ }
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+/**
+ * qla81xx_reset_mpi() - Reset's MPI FW via Write MPI Register MBC.
+ *
+ * Returns 0 on success.
+ */
+int
+qla81xx_reset_mpi(scsi_qla_host_t *vha)
+{
+ uint16_t mb[4] = {0x1010, 0, 1, 0};
+
+ if (!IS_QLA81XX(vha->hw))
+ return QLA_SUCCESS;
+
+ return qla81xx_write_mpi_register(vha, mb);
+}
+
+/**
+ * qla24xx_reset_risc() - Perform full reset of ISP24xx RISC.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+static inline void
+qla24xx_reset_risc(scsi_qla_host_t *vha)
+{
+ unsigned long flags = 0;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+ uint32_t cnt, d2;
+ uint16_t wd;
+ static int abts_cnt; /* ISP abort retry counts */
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Reset RISC. */
+ WRT_REG_DWORD(&reg->ctrl_status, CSRX_DMA_SHUTDOWN|MWB_4096_BYTES);
+ for (cnt = 0; cnt < 30000; cnt++) {
+ if ((RD_REG_DWORD(&reg->ctrl_status) & CSRX_DMA_ACTIVE) == 0)
+ break;
+
+ udelay(10);
+ }
+
+ WRT_REG_DWORD(&reg->ctrl_status,
+ CSRX_ISP_SOFT_RESET|CSRX_DMA_SHUTDOWN|MWB_4096_BYTES);
+ pci_read_config_word(ha->pdev, PCI_COMMAND, &wd);
+
+ udelay(100);
+ /* Wait for firmware to complete NVRAM accesses. */
+ d2 = (uint32_t) RD_REG_WORD(&reg->mailbox0);
+ for (cnt = 10000 ; cnt && d2; cnt--) {
+ udelay(5);
+ d2 = (uint32_t) RD_REG_WORD(&reg->mailbox0);
+ barrier();
+ }
+
+ /* Wait for soft-reset to complete. */
+ d2 = RD_REG_DWORD(&reg->ctrl_status);
+ for (cnt = 6000000 ; cnt && (d2 & CSRX_ISP_SOFT_RESET); cnt--) {
+ udelay(5);
+ d2 = RD_REG_DWORD(&reg->ctrl_status);
+ barrier();
+ }
+
+ /* If required, do an MPI FW reset now */
+ if (test_and_clear_bit(MPI_RESET_NEEDED, &vha->dpc_flags)) {
+ if (qla81xx_reset_mpi(vha) != QLA_SUCCESS) {
+ if (++abts_cnt < 5) {
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ set_bit(MPI_RESET_NEEDED, &vha->dpc_flags);
+ } else {
+ /*
+ * We exhausted the ISP abort retries. We have to
+ * set the board offline.
+ */
+ abts_cnt = 0;
+ vha->flags.online = 0;
+ }
+ }
+ }
+
+ WRT_REG_DWORD(&reg->hccr, HCCRX_SET_RISC_RESET);
+ RD_REG_DWORD(&reg->hccr);
+
+ WRT_REG_DWORD(&reg->hccr, HCCRX_REL_RISC_PAUSE);
+ RD_REG_DWORD(&reg->hccr);
+
+ WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_RESET);
+ RD_REG_DWORD(&reg->hccr);
+
+ d2 = (uint32_t) RD_REG_WORD(&reg->mailbox0);
+ for (cnt = 6000000 ; cnt && d2; cnt--) {
+ udelay(5);
+ d2 = (uint32_t) RD_REG_WORD(&reg->mailbox0);
+ barrier();
+ }
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ if (IS_NOPOLLING_TYPE(ha))
+ ha->isp_ops->enable_intrs(ha);
+}
+
+/**
+ * qla24xx_reset_chip() - Reset ISP24xx chip.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+void
+qla24xx_reset_chip(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+
+ if (pci_channel_offline(ha->pdev) &&
+ ha->flags.pci_channel_io_perm_failure) {
+ return;
+ }
+
+ ha->isp_ops->disable_intrs(ha);
+
+ /* Perform RISC reset. */
+ qla24xx_reset_risc(vha);
+}
+
+/**
+ * qla2x00_chip_diag() - Test chip for proper operation.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+int
+qla2x00_chip_diag(scsi_qla_host_t *vha)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ unsigned long flags = 0;
+ uint16_t data;
+ uint32_t cnt;
+ uint16_t mb[5];
+ struct req_que *req = ha->req_q_map[0];
+
+ /* Assume a failed state */
+ rval = QLA_FUNCTION_FAILED;
+
+ ql_dbg(ql_dbg_init, vha, 0x007b,
+ "Testing device at %lx.\n", (u_long)&reg->flash_address);
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Reset ISP chip. */
+ WRT_REG_WORD(&reg->ctrl_status, CSR_ISP_SOFT_RESET);
+
+ /*
+ * We need to have a delay here since the card will not respond while
+ * in reset causing an MCA on some architectures.
+ */
+ udelay(20);
+ data = qla2x00_debounce_register(&reg->ctrl_status);
+ for (cnt = 6000000 ; cnt && (data & CSR_ISP_SOFT_RESET); cnt--) {
+ udelay(5);
+ data = RD_REG_WORD(&reg->ctrl_status);
+ barrier();
+ }
+
+ if (!cnt)
+ goto chip_diag_failed;
+
+ ql_dbg(ql_dbg_init, vha, 0x007c,
+ "Reset register cleared by chip reset.\n");
+
+ /* Reset RISC processor. */
+ WRT_REG_WORD(&reg->hccr, HCCR_RESET_RISC);
+ WRT_REG_WORD(&reg->hccr, HCCR_RELEASE_RISC);
+
+ /* Workaround for QLA2312 PCI parity error */
+ if (IS_QLA2100(ha) || IS_QLA2200(ha) || IS_QLA2300(ha)) {
+ data = qla2x00_debounce_register(MAILBOX_REG(ha, reg, 0));
+ for (cnt = 6000000; cnt && (data == MBS_BUSY); cnt--) {
+ udelay(5);
+ data = RD_MAILBOX_REG(ha, reg, 0);
+ barrier();
+ }
+ } else
+ udelay(10);
+
+ if (!cnt)
+ goto chip_diag_failed;
+
+ /* Check product ID of chip */
+ ql_dbg(ql_dbg_init, vha, 0x007d, "Checking product Id of chip.\n");
+
+ mb[1] = RD_MAILBOX_REG(ha, reg, 1);
+ mb[2] = RD_MAILBOX_REG(ha, reg, 2);
+ mb[3] = RD_MAILBOX_REG(ha, reg, 3);
+ mb[4] = qla2x00_debounce_register(MAILBOX_REG(ha, reg, 4));
+ if (mb[1] != PROD_ID_1 || (mb[2] != PROD_ID_2 && mb[2] != PROD_ID_2a) ||
+ mb[3] != PROD_ID_3) {
+ ql_log(ql_log_warn, vha, 0x0062,
+ "Wrong product ID = 0x%x,0x%x,0x%x.\n",
+ mb[1], mb[2], mb[3]);
+
+ goto chip_diag_failed;
+ }
+ ha->product_id[0] = mb[1];
+ ha->product_id[1] = mb[2];
+ ha->product_id[2] = mb[3];
+ ha->product_id[3] = mb[4];
+
+ /* Adjust fw RISC transfer size */
+ if (req->length > 1024)
+ ha->fw_transfer_size = REQUEST_ENTRY_SIZE * 1024;
+ else
+ ha->fw_transfer_size = REQUEST_ENTRY_SIZE *
+ req->length;
+
+ if (IS_QLA2200(ha) &&
+ RD_MAILBOX_REG(ha, reg, 7) == QLA2200A_RISC_ROM_VER) {
+ /* Limit firmware transfer size with a 2200A */
+ ql_dbg(ql_dbg_init, vha, 0x007e, "Found QLA2200A Chip.\n");
+
+ ha->device_type |= DT_ISP2200A;
+ ha->fw_transfer_size = 128;
+ }
+
+ /* Wrap Incoming Mailboxes Test. */
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ ql_dbg(ql_dbg_init, vha, 0x007f, "Checking mailboxes.\n");
+ rval = qla2x00_mbx_reg_test(vha);
+ if (rval)
+ ql_log(ql_log_warn, vha, 0x0080,
+ "Failed mailbox send register test.\n");
+ else
+ /* Flag a successful rval */
+ rval = QLA_SUCCESS;
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+chip_diag_failed:
+ if (rval)
+ ql_log(ql_log_info, vha, 0x0081,
+ "Chip diagnostics **** FAILED ****.\n");
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return (rval);
+}
+
+/**
+ * qla24xx_chip_diag() - Test ISP24xx for proper operation.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+int
+qla24xx_chip_diag(scsi_qla_host_t *vha)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+
+ if (IS_QLA82XX(ha))
+ return QLA_SUCCESS;
+
+ ha->fw_transfer_size = REQUEST_ENTRY_SIZE * req->length;
+
+ rval = qla2x00_mbx_reg_test(vha);
+ if (rval) {
+ ql_log(ql_log_warn, vha, 0x0082,
+ "Failed mailbox send register test.\n");
+ } else {
+ /* Flag a successful rval */
+ rval = QLA_SUCCESS;
+ }
+
+ return rval;
+}
+
+void
+qla2x00_alloc_fw_dump(scsi_qla_host_t *vha)
+{
+ int rval;
+ uint32_t dump_size, fixed_size, mem_size, req_q_size, rsp_q_size,
+ eft_size, fce_size, mq_size;
+ dma_addr_t tc_dma;
+ void *tc;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
+
+ if (ha->fw_dump) {
+ ql_dbg(ql_dbg_init, vha, 0x00bd,
+ "Firmware dump already allocated.\n");
+ return;
+ }
+
+ ha->fw_dumped = 0;
+ fixed_size = mem_size = eft_size = fce_size = mq_size = 0;
+ if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+ fixed_size = sizeof(struct qla2100_fw_dump);
+ } else if (IS_QLA23XX(ha)) {
+ fixed_size = offsetof(struct qla2300_fw_dump, data_ram);
+ mem_size = (ha->fw_memory_size - 0x11000 + 1) *
+ sizeof(uint16_t);
+ } else if (IS_FWI2_CAPABLE(ha)) {
+ if (IS_QLA83XX(ha))
+ fixed_size = offsetof(struct qla83xx_fw_dump, ext_mem);
+ else if (IS_QLA81XX(ha))
+ fixed_size = offsetof(struct qla81xx_fw_dump, ext_mem);
+ else if (IS_QLA25XX(ha))
+ fixed_size = offsetof(struct qla25xx_fw_dump, ext_mem);
+ else
+ fixed_size = offsetof(struct qla24xx_fw_dump, ext_mem);
+ mem_size = (ha->fw_memory_size - 0x100000 + 1) *
+ sizeof(uint32_t);
+ if (ha->mqenable) {
+ if (!IS_QLA83XX(ha))
+ mq_size = sizeof(struct qla2xxx_mq_chain);
+ /*
+ * Allocate maximum buffer size for all queues.
+ * Resizing must be done at end-of-dump processing.
+ */
+ mq_size += ha->max_req_queues *
+ (req->length * sizeof(request_t));
+ mq_size += ha->max_rsp_queues *
+ (rsp->length * sizeof(response_t));
+ }
+ /* Allocate memory for Fibre Channel Event Buffer. */
+ if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha))
+ goto try_eft;
+
+ tc = dma_alloc_coherent(&ha->pdev->dev, FCE_SIZE, &tc_dma,
+ GFP_KERNEL);
+ if (!tc) {
+ ql_log(ql_log_warn, vha, 0x00be,
+ "Unable to allocate (%d KB) for FCE.\n",
+ FCE_SIZE / 1024);
+ goto try_eft;
+ }
+
+ memset(tc, 0, FCE_SIZE);
+ rval = qla2x00_enable_fce_trace(vha, tc_dma, FCE_NUM_BUFFERS,
+ ha->fce_mb, &ha->fce_bufs);
+ if (rval) {
+ ql_log(ql_log_warn, vha, 0x00bf,
+ "Unable to initialize FCE (%d).\n", rval);
+ dma_free_coherent(&ha->pdev->dev, FCE_SIZE, tc,
+ tc_dma);
+ ha->flags.fce_enabled = 0;
+ goto try_eft;
+ }
+ ql_dbg(ql_dbg_init, vha, 0x00c0,
+ "Allocate (%d KB) for FCE...\n", FCE_SIZE / 1024);
+
+ fce_size = sizeof(struct qla2xxx_fce_chain) + FCE_SIZE;
+ ha->flags.fce_enabled = 1;
+ ha->fce_dma = tc_dma;
+ ha->fce = tc;
+try_eft:
+ /* Allocate memory for Extended Trace Buffer. */
+ tc = dma_alloc_coherent(&ha->pdev->dev, EFT_SIZE, &tc_dma,
+ GFP_KERNEL);
+ if (!tc) {
+ ql_log(ql_log_warn, vha, 0x00c1,
+ "Unable to allocate (%d KB) for EFT.\n",
+ EFT_SIZE / 1024);
+ goto cont_alloc;
+ }
+
+ memset(tc, 0, EFT_SIZE);
+ rval = qla2x00_enable_eft_trace(vha, tc_dma, EFT_NUM_BUFFERS);
+ if (rval) {
+ ql_log(ql_log_warn, vha, 0x00c2,
+ "Unable to initialize EFT (%d).\n", rval);
+ dma_free_coherent(&ha->pdev->dev, EFT_SIZE, tc,
+ tc_dma);
+ goto cont_alloc;
+ }
+ ql_dbg(ql_dbg_init, vha, 0x00c3,
+ "Allocated (%d KB) EFT ...\n", EFT_SIZE / 1024);
+
+ eft_size = EFT_SIZE;
+ ha->eft_dma = tc_dma;
+ ha->eft = tc;
+ }
+cont_alloc:
+ req_q_size = req->length * sizeof(request_t);
+ rsp_q_size = rsp->length * sizeof(response_t);
+
+ dump_size = offsetof(struct qla2xxx_fw_dump, isp);
+ dump_size += fixed_size + mem_size + req_q_size + rsp_q_size + eft_size;
+ ha->chain_offset = dump_size;
+ dump_size += mq_size + fce_size;
+
+ ha->fw_dump = vmalloc(dump_size);
+ if (!ha->fw_dump) {
+ ql_log(ql_log_warn, vha, 0x00c4,
+ "Unable to allocate (%d KB) for firmware dump.\n",
+ dump_size / 1024);
+
+ if (ha->fce) {
+ dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce,
+ ha->fce_dma);
+ ha->fce = NULL;
+ ha->fce_dma = 0;
+ }
+
+ if (ha->eft) {
+ dma_free_coherent(&ha->pdev->dev, eft_size, ha->eft,
+ ha->eft_dma);
+ ha->eft = NULL;
+ ha->eft_dma = 0;
+ }
+ return;
+ }
+ ql_dbg(ql_dbg_init, vha, 0x00c5,
+ "Allocated (%d KB) for firmware dump.\n", dump_size / 1024);
+
+ ha->fw_dump_len = dump_size;
+ ha->fw_dump->signature[0] = 'Q';
+ ha->fw_dump->signature[1] = 'L';
+ ha->fw_dump->signature[2] = 'G';
+ ha->fw_dump->signature[3] = 'C';
+ ha->fw_dump->version = __constant_htonl(1);
+
+ ha->fw_dump->fixed_size = htonl(fixed_size);
+ ha->fw_dump->mem_size = htonl(mem_size);
+ ha->fw_dump->req_q_size = htonl(req_q_size);
+ ha->fw_dump->rsp_q_size = htonl(rsp_q_size);
+
+ ha->fw_dump->eft_size = htonl(eft_size);
+ ha->fw_dump->eft_addr_l = htonl(LSD(ha->eft_dma));
+ ha->fw_dump->eft_addr_h = htonl(MSD(ha->eft_dma));
+
+ ha->fw_dump->header_size =
+ htonl(offsetof(struct qla2xxx_fw_dump, isp));
+}
+
+static int
+qla81xx_mpi_sync(scsi_qla_host_t *vha)
+{
+#define MPS_MASK 0xe0
+ int rval;
+ uint16_t dc;
+ uint32_t dw;
+
+ if (!IS_QLA81XX(vha->hw))
+ return QLA_SUCCESS;
+
+ rval = qla2x00_write_ram_word(vha, 0x7c00, 1);
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x0105,
+ "Unable to acquire semaphore.\n");
+ goto done;
+ }
+
+ pci_read_config_word(vha->hw->pdev, 0x54, &dc);
+ rval = qla2x00_read_ram_word(vha, 0x7a15, &dw);
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x0067, "Unable to read sync.\n");
+ goto done_release;
+ }
+
+ dc &= MPS_MASK;
+ if (dc == (dw & MPS_MASK))
+ goto done_release;
+
+ dw &= ~MPS_MASK;
+ dw |= dc;
+ rval = qla2x00_write_ram_word(vha, 0x7a15, dw);
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x0114, "Unable to gain sync.\n");
+ }
+
+done_release:
+ rval = qla2x00_write_ram_word(vha, 0x7c00, 0);
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x006d,
+ "Unable to release semaphore.\n");
+ }
+
+done:
+ return rval;
+}
+
+/**
+ * qla2x00_setup_chip() - Load and start RISC firmware.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_setup_chip(scsi_qla_host_t *vha)
+{
+ int rval;
+ uint32_t srisc_address = 0;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ unsigned long flags;
+ uint16_t fw_major_version;
+
+ if (IS_QLA82XX(ha)) {
+ rval = ha->isp_ops->load_risc(vha, &srisc_address);
+ if (rval == QLA_SUCCESS) {
+ qla2x00_stop_firmware(vha);
+ goto enable_82xx_npiv;
+ } else
+ goto failed;
+ }
+
+ if (!IS_FWI2_CAPABLE(ha) && !IS_QLA2100(ha) && !IS_QLA2200(ha)) {
+ /* Disable SRAM, Instruction RAM and GP RAM parity. */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ WRT_REG_WORD(&reg->hccr, (HCCR_ENABLE_PARITY + 0x0));
+ RD_REG_WORD(&reg->hccr);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ }
+
+ qla81xx_mpi_sync(vha);
+
+ /* Load firmware sequences */
+ rval = ha->isp_ops->load_risc(vha, &srisc_address);
+ if (rval == QLA_SUCCESS) {
+ ql_dbg(ql_dbg_init, vha, 0x00c9,
+ "Verifying Checksum of loaded RISC code.\n");
+
+ rval = qla2x00_verify_checksum(vha, srisc_address);
+ if (rval == QLA_SUCCESS) {
+ /* Start firmware execution. */
+ ql_dbg(ql_dbg_init, vha, 0x00ca,
+ "Starting firmware.\n");
+
+ rval = qla2x00_execute_fw(vha, srisc_address);
+ /* Retrieve firmware information. */
+ if (rval == QLA_SUCCESS) {
+enable_82xx_npiv:
+ fw_major_version = ha->fw_major_version;
+ if (IS_QLA82XX(ha))
+ qla82xx_check_md_needed(vha);
+ else
+ rval = qla2x00_get_fw_version(vha);
+ if (rval != QLA_SUCCESS)
+ goto failed;
+ ha->flags.npiv_supported = 0;
+ if (IS_QLA2XXX_MIDTYPE(ha) &&
+ (ha->fw_attributes & BIT_2)) {
+ ha->flags.npiv_supported = 1;
+ if ((!ha->max_npiv_vports) ||
+ ((ha->max_npiv_vports + 1) %
+ MIN_MULTI_ID_FABRIC))
+ ha->max_npiv_vports =
+ MIN_MULTI_ID_FABRIC - 1;
+ }
+ qla2x00_get_resource_cnts(vha, NULL,
+ &ha->fw_xcb_count, NULL, NULL,
+ &ha->max_npiv_vports, NULL);
+
+ if (!fw_major_version && ql2xallocfwdump
+ && !IS_QLA82XX(ha))
+ qla2x00_alloc_fw_dump(vha);
+ }
+ } else {
+ ql_log(ql_log_fatal, vha, 0x00cd,
+ "ISP Firmware failed checksum.\n");
+ goto failed;
+ }
+ }
+
+ if (!IS_FWI2_CAPABLE(ha) && !IS_QLA2100(ha) && !IS_QLA2200(ha)) {
+ /* Enable proper parity. */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ if (IS_QLA2300(ha))
+ /* SRAM parity */
+ WRT_REG_WORD(&reg->hccr, HCCR_ENABLE_PARITY + 0x1);
+ else
+ /* SRAM, Instruction RAM and GP RAM parity */
+ WRT_REG_WORD(&reg->hccr, HCCR_ENABLE_PARITY + 0x7);
+ RD_REG_WORD(&reg->hccr);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ }
+
+ if (IS_QLA83XX(ha))
+ goto skip_fac_check;
+
+ if (rval == QLA_SUCCESS && IS_FAC_REQUIRED(ha)) {
+ uint32_t size;
+
+ rval = qla81xx_fac_get_sector_size(vha, &size);
+ if (rval == QLA_SUCCESS) {
+ ha->flags.fac_supported = 1;
+ ha->fdt_block_size = size << 2;
+ } else {
+ ql_log(ql_log_warn, vha, 0x00ce,
+ "Unsupported FAC firmware (%d.%02d.%02d).\n",
+ ha->fw_major_version, ha->fw_minor_version,
+ ha->fw_subminor_version);
+skip_fac_check:
+ if (IS_QLA83XX(ha)) {
+ ha->flags.fac_supported = 0;
+ rval = QLA_SUCCESS;
+ }
+ }
+ }
+failed:
+ if (rval) {
+ ql_log(ql_log_fatal, vha, 0x00cf,
+ "Setup chip ****FAILED****.\n");
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_init_response_q_entries() - Initializes response queue entries.
+ * @ha: HA context
+ *
+ * Beginning of request ring has initialization control block already built
+ * by nvram config routine.
+ *
+ * Returns 0 on success.
+ */
+void
+qla2x00_init_response_q_entries(struct rsp_que *rsp)
+{
+ uint16_t cnt;
+ response_t *pkt;
+
+ rsp->ring_ptr = rsp->ring;
+ rsp->ring_index = 0;
+ rsp->status_srb = NULL;
+ pkt = rsp->ring_ptr;
+ for (cnt = 0; cnt < rsp->length; cnt++) {
+ pkt->signature = RESPONSE_PROCESSED;
+ pkt++;
+ }
+}
+
+/**
+ * qla2x00_update_fw_options() - Read and process firmware options.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+void
+qla2x00_update_fw_options(scsi_qla_host_t *vha)
+{
+ uint16_t swing, emphasis, tx_sens, rx_sens;
+ struct qla_hw_data *ha = vha->hw;
+
+ memset(ha->fw_options, 0, sizeof(ha->fw_options));
+ qla2x00_get_fw_options(vha, ha->fw_options);
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ return;
+
+ /* Serial Link options. */
+ ql_dbg(ql_dbg_init + ql_dbg_buffer, vha, 0x0115,
+ "Serial link options.\n");
+ ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0109,
+ (uint8_t *)&ha->fw_seriallink_options,
+ sizeof(ha->fw_seriallink_options));
+
+ ha->fw_options[1] &= ~FO1_SET_EMPHASIS_SWING;
+ if (ha->fw_seriallink_options[3] & BIT_2) {
+ ha->fw_options[1] |= FO1_SET_EMPHASIS_SWING;
+
+ /* 1G settings */
+ swing = ha->fw_seriallink_options[2] & (BIT_2 | BIT_1 | BIT_0);
+ emphasis = (ha->fw_seriallink_options[2] &
+ (BIT_4 | BIT_3)) >> 3;
+ tx_sens = ha->fw_seriallink_options[0] &
+ (BIT_3 | BIT_2 | BIT_1 | BIT_0);
+ rx_sens = (ha->fw_seriallink_options[0] &
+ (BIT_7 | BIT_6 | BIT_5 | BIT_4)) >> 4;
+ ha->fw_options[10] = (emphasis << 14) | (swing << 8);
+ if (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA6312(ha)) {
+ if (rx_sens == 0x0)
+ rx_sens = 0x3;
+ ha->fw_options[10] |= (tx_sens << 4) | rx_sens;
+ } else if (IS_QLA2322(ha) || IS_QLA6322(ha))
+ ha->fw_options[10] |= BIT_5 |
+ ((rx_sens & (BIT_1 | BIT_0)) << 2) |
+ (tx_sens & (BIT_1 | BIT_0));
+
+ /* 2G settings */
+ swing = (ha->fw_seriallink_options[2] &
+ (BIT_7 | BIT_6 | BIT_5)) >> 5;
+ emphasis = ha->fw_seriallink_options[3] & (BIT_1 | BIT_0);
+ tx_sens = ha->fw_seriallink_options[1] &
+ (BIT_3 | BIT_2 | BIT_1 | BIT_0);
+ rx_sens = (ha->fw_seriallink_options[1] &
+ (BIT_7 | BIT_6 | BIT_5 | BIT_4)) >> 4;
+ ha->fw_options[11] = (emphasis << 14) | (swing << 8);
+ if (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA6312(ha)) {
+ if (rx_sens == 0x0)
+ rx_sens = 0x3;
+ ha->fw_options[11] |= (tx_sens << 4) | rx_sens;
+ } else if (IS_QLA2322(ha) || IS_QLA6322(ha))
+ ha->fw_options[11] |= BIT_5 |
+ ((rx_sens & (BIT_1 | BIT_0)) << 2) |
+ (tx_sens & (BIT_1 | BIT_0));
+ }
+
+ /* FCP2 options. */
+ /* Return command IOCBs without waiting for an ABTS to complete. */
+ ha->fw_options[3] |= BIT_13;
+
+ /* LED scheme. */
+ if (ha->flags.enable_led_scheme)
+ ha->fw_options[2] |= BIT_12;
+
+ /* Detect ISP6312. */
+ if (IS_QLA6312(ha))
+ ha->fw_options[2] |= BIT_13;
+
+ /* Update firmware options. */
+ qla2x00_set_fw_options(vha, ha->fw_options);
+}
+
+void
+qla24xx_update_fw_options(scsi_qla_host_t *vha)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (IS_QLA82XX(ha))
+ return;
+
+ /* Update Serial Link options. */
+ if ((le16_to_cpu(ha->fw_seriallink_options24[0]) & BIT_0) == 0)
+ return;
+
+ rval = qla2x00_set_serdes_params(vha,
+ le16_to_cpu(ha->fw_seriallink_options24[1]),
+ le16_to_cpu(ha->fw_seriallink_options24[2]),
+ le16_to_cpu(ha->fw_seriallink_options24[3]));
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x0104,
+ "Unable to update Serial Link options (%x).\n", rval);
+ }
+}
+
+void
+qla2x00_config_rings(struct scsi_qla_host *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
+
+ /* Setup ring parameters in initialization control block. */
+ ha->init_cb->request_q_outpointer = __constant_cpu_to_le16(0);
+ ha->init_cb->response_q_inpointer = __constant_cpu_to_le16(0);
+ ha->init_cb->request_q_length = cpu_to_le16(req->length);
+ ha->init_cb->response_q_length = cpu_to_le16(rsp->length);
+ ha->init_cb->request_q_address[0] = cpu_to_le32(LSD(req->dma));
+ ha->init_cb->request_q_address[1] = cpu_to_le32(MSD(req->dma));
+ ha->init_cb->response_q_address[0] = cpu_to_le32(LSD(rsp->dma));
+ ha->init_cb->response_q_address[1] = cpu_to_le32(MSD(rsp->dma));
+
+ WRT_REG_WORD(ISP_REQ_Q_IN(ha, reg), 0);
+ WRT_REG_WORD(ISP_REQ_Q_OUT(ha, reg), 0);
+ WRT_REG_WORD(ISP_RSP_Q_IN(ha, reg), 0);
+ WRT_REG_WORD(ISP_RSP_Q_OUT(ha, reg), 0);
+ RD_REG_WORD(ISP_RSP_Q_OUT(ha, reg)); /* PCI Posting. */
+}
+
+void
+qla24xx_config_rings(struct scsi_qla_host *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ device_reg_t __iomem *reg = ISP_QUE_REG(ha, 0);
+ struct device_reg_2xxx __iomem *ioreg = &ha->iobase->isp;
+ struct qla_msix_entry *msix;
+ struct init_cb_24xx *icb;
+ uint16_t rid = 0;
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
+
+ /* Setup ring parameters in initialization control block. */
+ icb = (struct init_cb_24xx *)ha->init_cb;
+ icb->request_q_outpointer = __constant_cpu_to_le16(0);
+ icb->response_q_inpointer = __constant_cpu_to_le16(0);
+ icb->request_q_length = cpu_to_le16(req->length);
+ icb->response_q_length = cpu_to_le16(rsp->length);
+ icb->request_q_address[0] = cpu_to_le32(LSD(req->dma));
+ icb->request_q_address[1] = cpu_to_le32(MSD(req->dma));
+ icb->response_q_address[0] = cpu_to_le32(LSD(rsp->dma));
+ icb->response_q_address[1] = cpu_to_le32(MSD(rsp->dma));
+
+ if (ha->mqenable || IS_QLA83XX(ha)) {
+ icb->qos = __constant_cpu_to_le16(QLA_DEFAULT_QUE_QOS);
+ icb->rid = __constant_cpu_to_le16(rid);
+ if (ha->flags.msix_enabled) {
+ msix = &ha->msix_entries[1];
+ ql_dbg(ql_dbg_init, vha, 0x00fd,
+ "Registering vector 0x%x for base que.\n",
+ msix->entry);
+ icb->msix = cpu_to_le16(msix->entry);
+ }
+ /* Use alternate PCI bus number */
+ if (MSB(rid))
+ icb->firmware_options_2 |=
+ __constant_cpu_to_le32(BIT_19);
+ /* Use alternate PCI devfn */
+ if (LSB(rid))
+ icb->firmware_options_2 |=
+ __constant_cpu_to_le32(BIT_18);
+
+ /* Use Disable MSIX Handshake mode for capable adapters */
+ if ((ha->fw_attributes & BIT_6) && (IS_MSIX_NACK_CAPABLE(ha)) &&
+ (ha->flags.msix_enabled)) {
+ icb->firmware_options_2 &=
+ __constant_cpu_to_le32(~BIT_22);
+ ha->flags.disable_msix_handshake = 1;
+ ql_dbg(ql_dbg_init, vha, 0x00fe,
+ "MSIX Handshake Disable Mode turned on.\n");
+ } else {
+ icb->firmware_options_2 |=
+ __constant_cpu_to_le32(BIT_22);
+ }
+ icb->firmware_options_2 |= __constant_cpu_to_le32(BIT_23);
+
+ WRT_REG_DWORD(&reg->isp25mq.req_q_in, 0);
+ WRT_REG_DWORD(&reg->isp25mq.req_q_out, 0);
+ WRT_REG_DWORD(&reg->isp25mq.rsp_q_in, 0);
+ WRT_REG_DWORD(&reg->isp25mq.rsp_q_out, 0);
+ } else {
+ WRT_REG_DWORD(&reg->isp24.req_q_in, 0);
+ WRT_REG_DWORD(&reg->isp24.req_q_out, 0);
+ WRT_REG_DWORD(&reg->isp24.rsp_q_in, 0);
+ WRT_REG_DWORD(&reg->isp24.rsp_q_out, 0);
+ }
+ /* PCI posting */
+ RD_REG_DWORD(&ioreg->hccr);
+}
+
+/**
+ * qla2x00_init_rings() - Initializes firmware.
+ * @ha: HA context
+ *
+ * Beginning of request ring has initialization control block already built
+ * by nvram config routine.
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_init_rings(scsi_qla_host_t *vha)
+{
+ int rval;
+ unsigned long flags = 0;
+ int cnt, que;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req;
+ struct rsp_que *rsp;
+ struct mid_init_cb_24xx *mid_init_cb =
+ (struct mid_init_cb_24xx *) ha->init_cb;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Clear outstanding commands array. */
+ for (que = 0; que < ha->max_req_queues; que++) {
+ req = ha->req_q_map[que];
+ if (!req)
+ continue;
+ for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++)
+ req->outstanding_cmds[cnt] = NULL;
+
+ req->current_outstanding_cmd = 1;
+
+ /* Initialize firmware. */
+ req->ring_ptr = req->ring;
+ req->ring_index = 0;
+ req->cnt = req->length;
+ }
+
+ for (que = 0; que < ha->max_rsp_queues; que++) {
+ rsp = ha->rsp_q_map[que];
+ if (!rsp)
+ continue;
+ /* Initialize response queue entries */
+ qla2x00_init_response_q_entries(rsp);
+ }
+
+ spin_lock(&ha->vport_slock);
+
+ spin_unlock(&ha->vport_slock);
+
+ ha->isp_ops->config_rings(vha);
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ /* Update any ISP specific firmware options before initialization. */
+ ha->isp_ops->update_fw_options(vha);
+
+ ql_dbg(ql_dbg_init, vha, 0x00d1, "Issue init firmware.\n");
+
+ if (ha->flags.npiv_supported) {
+ if (ha->operating_mode == LOOP)
+ ha->max_npiv_vports = MIN_MULTI_ID_FABRIC - 1;
+ mid_init_cb->count = cpu_to_le16(ha->max_npiv_vports);
+ }
+
+ if (IS_FWI2_CAPABLE(ha)) {
+ mid_init_cb->options = __constant_cpu_to_le16(BIT_1);
+ mid_init_cb->init_cb.execution_throttle =
+ cpu_to_le16(ha->fw_xcb_count);
+ }
+
+ rval = qla2x00_init_firmware(vha, ha->init_cb_size);
+ if (rval) {
+ ql_log(ql_log_fatal, vha, 0x00d2,
+ "Init Firmware **** FAILED ****.\n");
+ } else {
+ ql_dbg(ql_dbg_init, vha, 0x00d3,
+ "Init Firmware -- success.\n");
+ }
+
+ return (rval);
+}
+
+/**
+ * qla2x00_fw_ready() - Waits for firmware ready.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+static int
+qla2x00_fw_ready(scsi_qla_host_t *vha)
+{
+ int rval;
+ unsigned long wtime, mtime, cs84xx_time;
+ uint16_t min_wait; /* Minimum wait time if loop is down */
+ uint16_t wait_time; /* Wait time if loop is coming ready */
+ uint16_t state[5];
+ struct qla_hw_data *ha = vha->hw;
+
+ rval = QLA_SUCCESS;
+
+ /* 20 seconds for loop down. */
+ min_wait = 20;
+
+ /*
+ * Firmware should take at most one RATOV to login, plus 5 seconds for
+ * our own processing.
+ */
+ if ((wait_time = (ha->retry_count*ha->login_timeout) + 5) < min_wait) {
+ wait_time = min_wait;
+ }
+
+ /* Min wait time if loop down */
+ mtime = jiffies + (min_wait * HZ);
+
+ /* wait time before firmware ready */
+ wtime = jiffies + (wait_time * HZ);
+
+ /* Wait for ISP to finish LIP */
+ if (!vha->flags.init_done)
+ ql_log(ql_log_info, vha, 0x801e,
+ "Waiting for LIP to complete.\n");
+
+ do {
+ rval = qla2x00_get_firmware_state(vha, state);
+ if (rval == QLA_SUCCESS) {
+ if (state[0] < FSTATE_LOSS_OF_SYNC) {
+ vha->device_flags &= ~DFLG_NO_CABLE;
+ }
+ if (IS_QLA84XX(ha) && state[0] != FSTATE_READY) {
+ ql_dbg(ql_dbg_taskm, vha, 0x801f,
+ "fw_state=%x 84xx=%x.\n", state[0],
+ state[2]);
+ if ((state[2] & FSTATE_LOGGED_IN) &&
+ (state[2] & FSTATE_WAITING_FOR_VERIFY)) {
+ ql_dbg(ql_dbg_taskm, vha, 0x8028,
+ "Sending verify iocb.\n");
+
+ cs84xx_time = jiffies;
+ rval = qla84xx_init_chip(vha);
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn,
+ vha, 0x8007,
+ "Init chip failed.\n");
+ break;
+ }
+
+ /* Add time taken to initialize. */
+ cs84xx_time = jiffies - cs84xx_time;
+ wtime += cs84xx_time;
+ mtime += cs84xx_time;
+ ql_dbg(ql_dbg_taskm, vha, 0x8008,
+ "Increasing wait time by %ld. "
+ "New time %ld.\n", cs84xx_time,
+ wtime);
+ }
+ } else if (state[0] == FSTATE_READY) {
+ ql_dbg(ql_dbg_taskm, vha, 0x8037,
+ "F/W Ready - OK.\n");
+
+ qla2x00_get_retry_cnt(vha, &ha->retry_count,
+ &ha->login_timeout, &ha->r_a_tov);
+
+ rval = QLA_SUCCESS;
+ break;
+ }
+
+ rval = QLA_FUNCTION_FAILED;
+
+ if (atomic_read(&vha->loop_down_timer) &&
+ state[0] != FSTATE_READY) {
+ /* Loop down. Timeout on min_wait for states
+ * other than Wait for Login.
+ */
+ if (time_after_eq(jiffies, mtime)) {
+ ql_log(ql_log_info, vha, 0x8038,
+ "Cable is unplugged...\n");
+
+ vha->device_flags |= DFLG_NO_CABLE;
+ break;
+ }
+ }
+ } else {
+ /* Mailbox cmd failed. Timeout on min_wait. */
+ if (time_after_eq(jiffies, mtime) ||
+ ha->flags.isp82xx_fw_hung)
+ break;
+ }
+
+ if (time_after_eq(jiffies, wtime))
+ break;
+
+ /* Delay for a while */
+ msleep(500);
+ } while (1);
+
+ ql_dbg(ql_dbg_taskm, vha, 0x803a,
+ "fw_state=%x (%x, %x, %x, %x) " "curr time=%lx.\n", state[0],
+ state[1], state[2], state[3], state[4], jiffies);
+
+ if (rval && !(vha->device_flags & DFLG_NO_CABLE)) {
+ ql_log(ql_log_warn, vha, 0x803b,
+ "Firmware ready **** FAILED ****.\n");
+ }
+
+ return (rval);
+}
+
+/*
+* qla2x00_configure_hba
+* Setup adapter context.
+*
+* Input:
+* ha = adapter state pointer.
+*
+* Returns:
+* 0 = success
+*
+* Context:
+* Kernel context.
+*/
+static int
+qla2x00_configure_hba(scsi_qla_host_t *vha)
+{
+ int rval;
+ uint16_t loop_id;
+ uint16_t topo;
+ uint16_t sw_cap;
+ uint8_t al_pa;
+ uint8_t area;
+ uint8_t domain;
+ char connect_type[22];
+ struct qla_hw_data *ha = vha->hw;
+
+ /* Get host addresses. */
+ rval = qla2x00_get_adapter_id(vha,
+ &loop_id, &al_pa, &area, &domain, &topo, &sw_cap);
+ if (rval != QLA_SUCCESS) {
+ if (LOOP_TRANSITION(vha) || atomic_read(&ha->loop_down_timer) ||
+ IS_CNA_CAPABLE(ha) ||
+ (rval == QLA_COMMAND_ERROR && loop_id == 0x7)) {
+ ql_dbg(ql_dbg_disc, vha, 0x2008,
+ "Loop is in a transition state.\n");
+ } else {
+ ql_log(ql_log_warn, vha, 0x2009,
+ "Unable to get host loop ID.\n");
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ }
+ return (rval);
+ }
+
+ if (topo == 4) {
+ ql_log(ql_log_info, vha, 0x200a,
+ "Cannot get topology - retrying.\n");
+ return (QLA_FUNCTION_FAILED);
+ }
+
+ vha->loop_id = loop_id;
+
+ /* initialize */
+ ha->min_external_loopid = SNS_FIRST_LOOP_ID;
+ ha->operating_mode = LOOP;
+ ha->switch_cap = 0;
+
+ switch (topo) {
+ case 0:
+ ql_dbg(ql_dbg_disc, vha, 0x200b, "HBA in NL topology.\n");
+ ha->current_topology = ISP_CFG_NL;
+ strcpy(connect_type, "(Loop)");
+ break;
+
+ case 1:
+ ql_dbg(ql_dbg_disc, vha, 0x200c, "HBA in FL topology.\n");
+ ha->switch_cap = sw_cap;
+ ha->current_topology = ISP_CFG_FL;
+ strcpy(connect_type, "(FL_Port)");
+ break;
+
+ case 2:
+ ql_dbg(ql_dbg_disc, vha, 0x200d, "HBA in N P2P topology.\n");
+ ha->operating_mode = P2P;
+ ha->current_topology = ISP_CFG_N;
+ strcpy(connect_type, "(N_Port-to-N_Port)");
+ break;
+
+ case 3:
+ ql_dbg(ql_dbg_disc, vha, 0x200e, "HBA in F P2P topology.\n");
+ ha->switch_cap = sw_cap;
+ ha->operating_mode = P2P;
+ ha->current_topology = ISP_CFG_F;
+ strcpy(connect_type, "(F_Port)");
+ break;
+
+ default:
+ ql_dbg(ql_dbg_disc, vha, 0x200f,
+ "HBA in unknown topology %x, using NL.\n", topo);
+ ha->current_topology = ISP_CFG_NL;
+ strcpy(connect_type, "(Loop)");
+ break;
+ }
+
+ /* Save Host port and loop ID. */
+ /* byte order - Big Endian */
+ vha->d_id.b.domain = domain;
+ vha->d_id.b.area = area;
+ vha->d_id.b.al_pa = al_pa;
+
+ if (!vha->flags.init_done)
+ ql_log(ql_log_info, vha, 0x2010,
+ "Topology - %s, Host Loop address 0x%x.\n",
+ connect_type, vha->loop_id);
+
+ if (rval) {
+ ql_log(ql_log_warn, vha, 0x2011,
+ "%s FAILED\n", __func__);
+ } else {
+ ql_dbg(ql_dbg_disc, vha, 0x2012,
+ "%s success\n", __func__);
+ }
+
+ return(rval);
+}
+
+inline void
+qla2x00_set_model_info(scsi_qla_host_t *vha, uint8_t *model, size_t len,
+ char *def)
+{
+ char *st, *en;
+ uint16_t index;
+ struct qla_hw_data *ha = vha->hw;
+ int use_tbl = !IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) &&
+ !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha);
+
+ if (memcmp(model, BINZERO, len) != 0) {
+ strncpy(ha->model_number, model, len);
+ st = en = ha->model_number;
+ en += len - 1;
+ while (en > st) {
+ if (*en != 0x20 && *en != 0x00)
+ break;
+ *en-- = '\0';
+ }
+
+ index = (ha->pdev->subsystem_device & 0xff);
+ if (use_tbl &&
+ ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC &&
+ index < QLA_MODEL_NAMES)
+ strncpy(ha->model_desc,
+ qla2x00_model_name[index * 2 + 1],
+ sizeof(ha->model_desc) - 1);
+ } else {
+ index = (ha->pdev->subsystem_device & 0xff);
+ if (use_tbl &&
+ ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC &&
+ index < QLA_MODEL_NAMES) {
+ strcpy(ha->model_number,
+ qla2x00_model_name[index * 2]);
+ strncpy(ha->model_desc,
+ qla2x00_model_name[index * 2 + 1],
+ sizeof(ha->model_desc) - 1);
+ } else {
+ strcpy(ha->model_number, def);
+ }
+ }
+ if (IS_FWI2_CAPABLE(ha))
+ qla2xxx_get_vpd_field(vha, "\x82", ha->model_desc,
+ sizeof(ha->model_desc));
+}
+
+/* On sparc systems, obtain port and node WWN from firmware
+ * properties.
+ */
+static void qla2xxx_nvram_wwn_from_ofw(scsi_qla_host_t *vha, nvram_t *nv)
+{
+#ifdef CONFIG_SPARC
+ struct qla_hw_data *ha = vha->hw;
+ struct pci_dev *pdev = ha->pdev;
+ struct device_node *dp = pci_device_to_OF_node(pdev);
+ const u8 *val;
+ int len;
+
+ val = of_get_property(dp, "port-wwn", &len);
+ if (val && len >= WWN_SIZE)
+ memcpy(nv->port_name, val, WWN_SIZE);
+
+ val = of_get_property(dp, "node-wwn", &len);
+ if (val && len >= WWN_SIZE)
+ memcpy(nv->node_name, val, WWN_SIZE);
+#endif
+}
+
+/*
+* NVRAM configuration for ISP 2xxx
+*
+* Input:
+* ha = adapter block pointer.
+*
+* Output:
+* initialization control block in response_ring
+* host adapters parameters in host adapter block
+*
+* Returns:
+* 0 = success.
+*/
+int
+qla2x00_nvram_config(scsi_qla_host_t *vha)
+{
+ int rval;
+ uint8_t chksum = 0;
+ uint16_t cnt;
+ uint8_t *dptr1, *dptr2;
+ struct qla_hw_data *ha = vha->hw;
+ init_cb_t *icb = ha->init_cb;
+ nvram_t *nv = ha->nvram;
+ uint8_t *ptr = ha->nvram;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ rval = QLA_SUCCESS;
+
+ /* Determine NVRAM starting address. */
+ ha->nvram_size = sizeof(nvram_t);
+ ha->nvram_base = 0;
+ if (!IS_QLA2100(ha) && !IS_QLA2200(ha) && !IS_QLA2300(ha))
+ if ((RD_REG_WORD(&reg->ctrl_status) >> 14) == 1)
+ ha->nvram_base = 0x80;
+
+ /* Get NVRAM data and calculate checksum. */
+ ha->isp_ops->read_nvram(vha, ptr, ha->nvram_base, ha->nvram_size);
+ for (cnt = 0, chksum = 0; cnt < ha->nvram_size; cnt++)
+ chksum += *ptr++;
+
+ ql_dbg(ql_dbg_init + ql_dbg_buffer, vha, 0x010f,
+ "Contents of NVRAM.\n");
+ ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0110,
+ (uint8_t *)nv, ha->nvram_size);
+
+ /* Bad NVRAM data, set defaults parameters. */
+ if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' ||
+ nv->id[2] != 'P' || nv->id[3] != ' ' || nv->nvram_version < 1) {
+ /* Reset NVRAM data. */
+ ql_log(ql_log_warn, vha, 0x0064,
+ "Inconisistent NVRAM "
+ "detected: checksum=0x%x id=%c version=0x%x.\n",
+ chksum, nv->id[0], nv->nvram_version);
+ ql_log(ql_log_warn, vha, 0x0065,
+ "Falling back to "
+ "functioning (yet invalid -- WWPN) defaults.\n");
+
+ /*
+ * Set default initialization control block.
+ */
+ memset(nv, 0, ha->nvram_size);
+ nv->parameter_block_version = ICB_VERSION;
+
+ if (IS_QLA23XX(ha)) {
+ nv->firmware_options[0] = BIT_2 | BIT_1;
+ nv->firmware_options[1] = BIT_7 | BIT_5;
+ nv->add_firmware_options[0] = BIT_5;
+ nv->add_firmware_options[1] = BIT_5 | BIT_4;
+ nv->frame_payload_size = __constant_cpu_to_le16(2048);
+ nv->special_options[1] = BIT_7;
+ } else if (IS_QLA2200(ha)) {
+ nv->firmware_options[0] = BIT_2 | BIT_1;
+ nv->firmware_options[1] = BIT_7 | BIT_5;
+ nv->add_firmware_options[0] = BIT_5;
+ nv->add_firmware_options[1] = BIT_5 | BIT_4;
+ nv->frame_payload_size = __constant_cpu_to_le16(1024);
+ } else if (IS_QLA2100(ha)) {
+ nv->firmware_options[0] = BIT_3 | BIT_1;
+ nv->firmware_options[1] = BIT_5;
+ nv->frame_payload_size = __constant_cpu_to_le16(1024);
+ }
+
+ nv->max_iocb_allocation = __constant_cpu_to_le16(256);
+ nv->execution_throttle = __constant_cpu_to_le16(16);
+ nv->retry_count = 8;
+ nv->retry_delay = 1;
+
+ nv->port_name[0] = 33;
+ nv->port_name[3] = 224;
+ nv->port_name[4] = 139;
+
+ qla2xxx_nvram_wwn_from_ofw(vha, nv);
+
+ nv->login_timeout = 4;
+
+ /*
+ * Set default host adapter parameters
+ */
+ nv->host_p[1] = BIT_2;
+ nv->reset_delay = 5;
+ nv->port_down_retry_count = 8;
+ nv->max_luns_per_target = __constant_cpu_to_le16(8);
+ nv->link_down_timeout = 60;
+
+ rval = 1;
+ }
+
+#if defined(CONFIG_IA64_GENERIC) || defined(CONFIG_IA64_SGI_SN2)
+ /*
+ * The SN2 does not provide BIOS emulation which means you can't change
+ * potentially bogus BIOS settings. Force the use of default settings
+ * for link rate and frame size. Hope that the rest of the settings
+ * are valid.
+ */
+ if (ia64_platform_is("sn2")) {
+ nv->frame_payload_size = __constant_cpu_to_le16(2048);
+ if (IS_QLA23XX(ha))
+ nv->special_options[1] = BIT_7;
+ }
+#endif
+
+ /* Reset Initialization control block */
+ memset(icb, 0, ha->init_cb_size);
+
+ /*
+ * Setup driver NVRAM options.
+ */
+ nv->firmware_options[0] |= (BIT_6 | BIT_1);
+ nv->firmware_options[0] &= ~(BIT_5 | BIT_4);
+ nv->firmware_options[1] |= (BIT_5 | BIT_0);
+ nv->firmware_options[1] &= ~BIT_4;
+
+ if (IS_QLA23XX(ha)) {
+ nv->firmware_options[0] |= BIT_2;
+ nv->firmware_options[0] &= ~BIT_3;
+ nv->firmware_options[0] &= ~BIT_6;
+ nv->add_firmware_options[1] |= BIT_5 | BIT_4;
+
+ if (IS_QLA2300(ha)) {
+ if (ha->fb_rev == FPM_2310) {
+ strcpy(ha->model_number, "QLA2310");
+ } else {
+ strcpy(ha->model_number, "QLA2300");
+ }
+ } else {
+ qla2x00_set_model_info(vha, nv->model_number,
+ sizeof(nv->model_number), "QLA23xx");
+ }
+ } else if (IS_QLA2200(ha)) {
+ nv->firmware_options[0] |= BIT_2;
+ /*
+ * 'Point-to-point preferred, else loop' is not a safe
+ * connection mode setting.
+ */
+ if ((nv->add_firmware_options[0] & (BIT_6 | BIT_5 | BIT_4)) ==
+ (BIT_5 | BIT_4)) {
+ /* Force 'loop preferred, else point-to-point'. */
+ nv->add_firmware_options[0] &= ~(BIT_6 | BIT_5 | BIT_4);
+ nv->add_firmware_options[0] |= BIT_5;
+ }
+ strcpy(ha->model_number, "QLA22xx");
+ } else /*if (IS_QLA2100(ha))*/ {
+ strcpy(ha->model_number, "QLA2100");
+ }
+
+ /*
+ * Copy over NVRAM RISC parameter block to initialization control block.
+ */
+ dptr1 = (uint8_t *)icb;
+ dptr2 = (uint8_t *)&nv->parameter_block_version;
+ cnt = (uint8_t *)&icb->request_q_outpointer - (uint8_t *)&icb->version;
+ while (cnt--)
+ *dptr1++ = *dptr2++;
+
+ /* Copy 2nd half. */
+ dptr1 = (uint8_t *)icb->add_firmware_options;
+ cnt = (uint8_t *)icb->reserved_3 - (uint8_t *)icb->add_firmware_options;
+ while (cnt--)
+ *dptr1++ = *dptr2++;
+
+ /* Use alternate WWN? */
+ if (nv->host_p[1] & BIT_7) {
+ memcpy(icb->node_name, nv->alternate_node_name, WWN_SIZE);
+ memcpy(icb->port_name, nv->alternate_port_name, WWN_SIZE);
+ }
+
+ /* Prepare nodename */
+ if ((icb->firmware_options[1] & BIT_6) == 0) {
+ /*
+ * Firmware will apply the following mask if the nodename was
+ * not provided.
+ */
+ memcpy(icb->node_name, icb->port_name, WWN_SIZE);
+ icb->node_name[0] &= 0xF0;
+ }
+
+ /*
+ * Set host adapter parameters.
+ */
+
+ /*
+ * BIT_7 in the host-parameters section allows for modification to
+ * internal driver logging.
+ */
+ if (nv->host_p[0] & BIT_7)
+ ql2xextended_error_logging = QL_DBG_DEFAULT1_MASK;
+ ha->flags.disable_risc_code_load = ((nv->host_p[0] & BIT_4) ? 1 : 0);
+ /* Always load RISC code on non ISP2[12]00 chips. */
+ if (!IS_QLA2100(ha) && !IS_QLA2200(ha))
+ ha->flags.disable_risc_code_load = 0;
+ ha->flags.enable_lip_reset = ((nv->host_p[1] & BIT_1) ? 1 : 0);
+ ha->flags.enable_lip_full_login = ((nv->host_p[1] & BIT_2) ? 1 : 0);
+ ha->flags.enable_target_reset = ((nv->host_p[1] & BIT_3) ? 1 : 0);
+ ha->flags.enable_led_scheme = (nv->special_options[1] & BIT_4) ? 1 : 0;
+ ha->flags.disable_serdes = 0;
+
+ ha->operating_mode =
+ (icb->add_firmware_options[0] & (BIT_6 | BIT_5 | BIT_4)) >> 4;
+
+ memcpy(ha->fw_seriallink_options, nv->seriallink_options,
+ sizeof(ha->fw_seriallink_options));
+
+ /* save HBA serial number */
+ ha->serial0 = icb->port_name[5];
+ ha->serial1 = icb->port_name[6];
+ ha->serial2 = icb->port_name[7];
+ memcpy(vha->node_name, icb->node_name, WWN_SIZE);
+ memcpy(vha->port_name, icb->port_name, WWN_SIZE);
+
+ icb->execution_throttle = __constant_cpu_to_le16(0xFFFF);
+
+ ha->retry_count = nv->retry_count;
+
+ /* Set minimum login_timeout to 4 seconds. */
+ if (nv->login_timeout != ql2xlogintimeout)
+ nv->login_timeout = ql2xlogintimeout;
+ if (nv->login_timeout < 4)
+ nv->login_timeout = 4;
+ ha->login_timeout = nv->login_timeout;
+ icb->login_timeout = nv->login_timeout;
+
+ /* Set minimum RATOV to 100 tenths of a second. */
+ ha->r_a_tov = 100;
+
+ ha->loop_reset_delay = nv->reset_delay;
+
+ /* Link Down Timeout = 0:
+ *
+ * When Port Down timer expires we will start returning
+ * I/O's to OS with "DID_NO_CONNECT".
+ *
+ * Link Down Timeout != 0:
+ *
+ * The driver waits for the link to come up after link down
+ * before returning I/Os to OS with "DID_NO_CONNECT".
+ */
+ if (nv->link_down_timeout == 0) {
+ ha->loop_down_abort_time =
+ (LOOP_DOWN_TIME - LOOP_DOWN_TIMEOUT);
+ } else {
+ ha->link_down_timeout = nv->link_down_timeout;
+ ha->loop_down_abort_time =
+ (LOOP_DOWN_TIME - ha->link_down_timeout);
+ }
+
+ /*
+ * Need enough time to try and get the port back.
+ */
+ ha->port_down_retry_count = nv->port_down_retry_count;
+ if (qlport_down_retry)
+ ha->port_down_retry_count = qlport_down_retry;
+ /* Set login_retry_count */
+ ha->login_retry_count = nv->retry_count;
+ if (ha->port_down_retry_count == nv->port_down_retry_count &&
+ ha->port_down_retry_count > 3)
+ ha->login_retry_count = ha->port_down_retry_count;
+ else if (ha->port_down_retry_count > (int)ha->login_retry_count)
+ ha->login_retry_count = ha->port_down_retry_count;
+ if (ql2xloginretrycount)
+ ha->login_retry_count = ql2xloginretrycount;
+
+ icb->lun_enables = __constant_cpu_to_le16(0);
+ icb->command_resource_count = 0;
+ icb->immediate_notify_resource_count = 0;
+ icb->timeout = __constant_cpu_to_le16(0);
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+ /* Enable RIO */
+ icb->firmware_options[0] &= ~BIT_3;
+ icb->add_firmware_options[0] &=
+ ~(BIT_3 | BIT_2 | BIT_1 | BIT_0);
+ icb->add_firmware_options[0] |= BIT_2;
+ icb->response_accumulation_timer = 3;
+ icb->interrupt_delay_timer = 5;
+
+ vha->flags.process_response_queue = 1;
+ } else {
+ /* Enable ZIO. */
+ if (!vha->flags.init_done) {
+ ha->zio_mode = icb->add_firmware_options[0] &
+ (BIT_3 | BIT_2 | BIT_1 | BIT_0);
+ ha->zio_timer = icb->interrupt_delay_timer ?
+ icb->interrupt_delay_timer: 2;
+ }
+ icb->add_firmware_options[0] &=
+ ~(BIT_3 | BIT_2 | BIT_1 | BIT_0);
+ vha->flags.process_response_queue = 0;
+ if (ha->zio_mode != QLA_ZIO_DISABLED) {
+ ha->zio_mode = QLA_ZIO_MODE_6;
+
+ ql_log(ql_log_info, vha, 0x0068,
+ "ZIO mode %d enabled; timer delay (%d us).\n",
+ ha->zio_mode, ha->zio_timer * 100);
+
+ icb->add_firmware_options[0] |= (uint8_t)ha->zio_mode;
+ icb->interrupt_delay_timer = (uint8_t)ha->zio_timer;
+ vha->flags.process_response_queue = 1;
+ }
+ }
+
+ if (rval) {
+ ql_log(ql_log_warn, vha, 0x0069,
+ "NVRAM configuration failed.\n");
+ }
+ return (rval);
+}
+
+static void
+qla2x00_rport_del(void *data)
+{
+ fc_port_t *fcport = data;
+ struct fc_rport *rport;
+ unsigned long flags;
+
+ spin_lock_irqsave(fcport->vha->host->host_lock, flags);
+ rport = fcport->drport ? fcport->drport: fcport->rport;
+ fcport->drport = NULL;
+ spin_unlock_irqrestore(fcport->vha->host->host_lock, flags);
+ if (rport)
+ fc_remote_port_delete(rport);
+}
+
+/**
+ * qla2x00_alloc_fcport() - Allocate a generic fcport.
+ * @ha: HA context
+ * @flags: allocation flags
+ *
+ * Returns a pointer to the allocated fcport, or NULL, if none available.
+ */
+fc_port_t *
+qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags)
+{
+ fc_port_t *fcport;
+
+ fcport = kzalloc(sizeof(fc_port_t), flags);
+ if (!fcport)
+ return NULL;
+
+ /* Setup fcport template structure. */
+ fcport->vha = vha;
+ fcport->vp_idx = vha->vp_idx;
+ fcport->port_type = FCT_UNKNOWN;
+ fcport->loop_id = FC_NO_LOOP_ID;
+ qla2x00_set_fcport_state(fcport, FCS_UNCONFIGURED);
+ fcport->supported_classes = FC_COS_UNSPECIFIED;
+
+ return fcport;
+}
+
+/*
+ * qla2x00_configure_loop
+ * Updates Fibre Channel Device Database with what is actually on loop.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Returns:
+ * 0 = success.
+ * 1 = error.
+ * 2 = database was full and device was not configured.
+ */
+static int
+qla2x00_configure_loop(scsi_qla_host_t *vha)
+{
+ int rval;
+ unsigned long flags, save_flags;
+ struct qla_hw_data *ha = vha->hw;
+ rval = QLA_SUCCESS;
+
+ /* Get Initiator ID */
+ if (test_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags)) {
+ rval = qla2x00_configure_hba(vha);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_disc, vha, 0x2013,
+ "Unable to configure HBA.\n");
+ return (rval);
+ }
+ }
+
+ save_flags = flags = vha->dpc_flags;
+ ql_dbg(ql_dbg_disc, vha, 0x2014,
+ "Configure loop -- dpc flags = 0x%lx.\n", flags);
+
+ /*
+ * If we have both an RSCN and PORT UPDATE pending then handle them
+ * both at the same time.
+ */
+ clear_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
+ clear_bit(RSCN_UPDATE, &vha->dpc_flags);
+
+ qla2x00_get_data_rate(vha);
+
+ /* Determine what we need to do */
+ if (ha->current_topology == ISP_CFG_FL &&
+ (test_bit(LOCAL_LOOP_UPDATE, &flags))) {
+
+ set_bit(RSCN_UPDATE, &flags);
+
+ } else if (ha->current_topology == ISP_CFG_F &&
+ (test_bit(LOCAL_LOOP_UPDATE, &flags))) {
+
+ set_bit(RSCN_UPDATE, &flags);
+ clear_bit(LOCAL_LOOP_UPDATE, &flags);
+
+ } else if (ha->current_topology == ISP_CFG_N) {
+ clear_bit(RSCN_UPDATE, &flags);
+
+ } else if (!vha->flags.online ||
+ (test_bit(ABORT_ISP_ACTIVE, &flags))) {
+
+ set_bit(RSCN_UPDATE, &flags);
+ set_bit(LOCAL_LOOP_UPDATE, &flags);
+ }
+
+ if (test_bit(LOCAL_LOOP_UPDATE, &flags)) {
+ if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) {
+ ql_dbg(ql_dbg_disc, vha, 0x2015,
+ "Loop resync needed, failing.\n");
+ rval = QLA_FUNCTION_FAILED;
+ } else
+ rval = qla2x00_configure_local_loop(vha);
+ }
+
+ if (rval == QLA_SUCCESS && test_bit(RSCN_UPDATE, &flags)) {
+ if (LOOP_TRANSITION(vha)) {
+ ql_dbg(ql_dbg_disc, vha, 0x201e,
+ "Needs RSCN update and loop transition.\n");
+ rval = QLA_FUNCTION_FAILED;
+ }
+ else
+ rval = qla2x00_configure_fabric(vha);
+ }
+
+ if (rval == QLA_SUCCESS) {
+ if (atomic_read(&vha->loop_down_timer) ||
+ test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) {
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ atomic_set(&vha->loop_state, LOOP_READY);
+ ql_dbg(ql_dbg_disc, vha, 0x2069,
+ "LOOP READY.\n");
+ }
+ }
+
+ if (rval) {
+ ql_dbg(ql_dbg_disc, vha, 0x206a,
+ "%s *** FAILED ***.\n", __func__);
+ } else {
+ ql_dbg(ql_dbg_disc, vha, 0x206b,
+ "%s: exiting normally.\n", __func__);
+ }
+
+ /* Restore state if a resync event occurred during processing */
+ if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) {
+ if (test_bit(LOCAL_LOOP_UPDATE, &save_flags))
+ set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
+ if (test_bit(RSCN_UPDATE, &save_flags)) {
+ set_bit(RSCN_UPDATE, &vha->dpc_flags);
+ }
+ }
+
+ return (rval);
+}
+
+
+
+/*
+ * qla2x00_configure_local_loop
+ * Updates Fibre Channel Device Database with local loop devices.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Returns:
+ * 0 = success.
+ */
+static int
+qla2x00_configure_local_loop(scsi_qla_host_t *vha)
+{
+ int rval, rval2;
+ int found_devs;
+ int found;
+ fc_port_t *fcport, *new_fcport;
+
+ uint16_t index;
+ uint16_t entries;
+ char *id_iter;
+ uint16_t loop_id;
+ uint8_t domain, area, al_pa;
+ struct qla_hw_data *ha = vha->hw;
+
+ found_devs = 0;
+ new_fcport = NULL;
+ entries = MAX_FIBRE_DEVICES_LOOP;
+
+ ql_dbg(ql_dbg_disc, vha, 0x2016,
+ "Getting FCAL position map.\n");
+ if (ql2xextended_error_logging & ql_dbg_disc)
+ qla2x00_get_fcal_position_map(vha, NULL);
+
+ /* Get list of logged in devices. */
+ memset(ha->gid_list, 0, qla2x00_gid_list_size(ha));
+ rval = qla2x00_get_id_list(vha, ha->gid_list, ha->gid_list_dma,
+ &entries);
+ if (rval != QLA_SUCCESS)
+ goto cleanup_allocation;
+
+ ql_dbg(ql_dbg_disc, vha, 0x2017,
+ "Entries in ID list (%d).\n", entries);
+ ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x2075,
+ (uint8_t *)ha->gid_list,
+ entries * sizeof(struct gid_list_info));
+
+ /* Allocate temporary fcport for any new fcports discovered. */
+ new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
+ if (new_fcport == NULL) {
+ ql_log(ql_log_warn, vha, 0x2018,
+ "Memory allocation failed for fcport.\n");
+ rval = QLA_MEMORY_ALLOC_FAILED;
+ goto cleanup_allocation;
+ }
+ new_fcport->flags &= ~FCF_FABRIC_DEVICE;
+
+ /*
+ * Mark local devices that were present with FCF_DEVICE_LOST for now.
+ */
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ if (atomic_read(&fcport->state) == FCS_ONLINE &&
+ fcport->port_type != FCT_BROADCAST &&
+ (fcport->flags & FCF_FABRIC_DEVICE) == 0) {
+
+ ql_dbg(ql_dbg_disc, vha, 0x2019,
+ "Marking port lost loop_id=0x%04x.\n",
+ fcport->loop_id);
+
+ qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST);
+ }
+ }
+
+ /* Add devices to port list. */
+ id_iter = (char *)ha->gid_list;
+ for (index = 0; index < entries; index++) {
+ domain = ((struct gid_list_info *)id_iter)->domain;
+ area = ((struct gid_list_info *)id_iter)->area;
+ al_pa = ((struct gid_list_info *)id_iter)->al_pa;
+ if (IS_QLA2100(ha) || IS_QLA2200(ha))
+ loop_id = (uint16_t)
+ ((struct gid_list_info *)id_iter)->loop_id_2100;
+ else
+ loop_id = le16_to_cpu(
+ ((struct gid_list_info *)id_iter)->loop_id);
+ id_iter += ha->gid_list_info_size;
+
+ /* Bypass reserved domain fields. */
+ if ((domain & 0xf0) == 0xf0)
+ continue;
+
+ /* Bypass if not same domain and area of adapter. */
+ if (area && domain &&
+ (area != vha->d_id.b.area || domain != vha->d_id.b.domain))
+ continue;
+
+ /* Bypass invalid local loop ID. */
+ if (loop_id > LAST_LOCAL_LOOP_ID)
+ continue;
+
+ /* Fill in member data. */
+ new_fcport->d_id.b.domain = domain;
+ new_fcport->d_id.b.area = area;
+ new_fcport->d_id.b.al_pa = al_pa;
+ new_fcport->loop_id = loop_id;
+ new_fcport->vp_idx = vha->vp_idx;
+ rval2 = qla2x00_get_port_database(vha, new_fcport, 0);
+ if (rval2 != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_disc, vha, 0x201a,
+ "Failed to retrieve fcport information "
+ "-- get_port_database=%x, loop_id=0x%04x.\n",
+ rval2, new_fcport->loop_id);
+ ql_dbg(ql_dbg_disc, vha, 0x201b,
+ "Scheduling resync.\n");
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ continue;
+ }
+
+ /* Check for matching device in port list. */
+ found = 0;
+ fcport = NULL;
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ if (memcmp(new_fcport->port_name, fcport->port_name,
+ WWN_SIZE))
+ continue;
+
+ fcport->flags &= ~FCF_FABRIC_DEVICE;
+ fcport->loop_id = new_fcport->loop_id;
+ fcport->port_type = new_fcport->port_type;
+ fcport->d_id.b24 = new_fcport->d_id.b24;
+ memcpy(fcport->node_name, new_fcport->node_name,
+ WWN_SIZE);
+
+ found++;
+ break;
+ }
+
+ if (!found) {
+ /* New device, add to fcports list. */
+ if (vha->vp_idx) {
+ new_fcport->vha = vha;
+ new_fcport->vp_idx = vha->vp_idx;
+ }
+ list_add_tail(&new_fcport->list, &vha->vp_fcports);
+
+ /* Allocate a new replacement fcport. */
+ fcport = new_fcport;
+ new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
+ if (new_fcport == NULL) {
+ ql_log(ql_log_warn, vha, 0x201c,
+ "Failed to allocate memory for fcport.\n");
+ rval = QLA_MEMORY_ALLOC_FAILED;
+ goto cleanup_allocation;
+ }
+ new_fcport->flags &= ~FCF_FABRIC_DEVICE;
+ }
+
+ /* Base iIDMA settings on HBA port speed. */
+ fcport->fp_speed = ha->link_data_rate;
+
+ qla2x00_update_fcport(vha, fcport);
+
+ found_devs++;
+ }
+
+cleanup_allocation:
+ kfree(new_fcport);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_disc, vha, 0x201d,
+ "Configure local loop error exit: rval=%x.\n", rval);
+ }
+
+ return (rval);
+}
+
+static void
+qla2x00_iidma_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
+{
+#define LS_UNKNOWN 2
+ static char *link_speeds[] = { "1", "2", "?", "4", "8", "10" };
+ char *link_speed;
+ int rval;
+ uint16_t mb[4];
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!IS_IIDMA_CAPABLE(ha))
+ return;
+
+ if (atomic_read(&fcport->state) != FCS_ONLINE)
+ return;
+
+ if (fcport->fp_speed == PORT_SPEED_UNKNOWN ||
+ fcport->fp_speed > ha->link_data_rate)
+ return;
+
+ rval = qla2x00_set_idma_speed(vha, fcport->loop_id, fcport->fp_speed,
+ mb);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_disc, vha, 0x2004,
+ "Unable to adjust iIDMA "
+ "%02x%02x%02x%02x%02x%02x%02x%02x -- %04x %x %04x "
+ "%04x.\n", fcport->port_name[0], fcport->port_name[1],
+ fcport->port_name[2], fcport->port_name[3],
+ fcport->port_name[4], fcport->port_name[5],
+ fcport->port_name[6], fcport->port_name[7], rval,
+ fcport->fp_speed, mb[0], mb[1]);
+ } else {
+ link_speed = link_speeds[LS_UNKNOWN];
+ if (fcport->fp_speed < 5)
+ link_speed = link_speeds[fcport->fp_speed];
+ else if (fcport->fp_speed == 0x13)
+ link_speed = link_speeds[5];
+ ql_dbg(ql_dbg_disc, vha, 0x2005,
+ "iIDMA adjusted to %s GB/s "
+ "on %02x%02x%02x%02x%02x%02x%02x%02x.\n", link_speed,
+ fcport->port_name[0], fcport->port_name[1],
+ fcport->port_name[2], fcport->port_name[3],
+ fcport->port_name[4], fcport->port_name[5],
+ fcport->port_name[6], fcport->port_name[7]);
+ }
+}
+
+static void
+qla2x00_reg_remote_port(scsi_qla_host_t *vha, fc_port_t *fcport)
+{
+ struct fc_rport_identifiers rport_ids;
+ struct fc_rport *rport;
+ unsigned long flags;
+
+ qla2x00_rport_del(fcport);
+
+ rport_ids.node_name = wwn_to_u64(fcport->node_name);
+ rport_ids.port_name = wwn_to_u64(fcport->port_name);
+ rport_ids.port_id = fcport->d_id.b.domain << 16 |
+ fcport->d_id.b.area << 8 | fcport->d_id.b.al_pa;
+ rport_ids.roles = FC_RPORT_ROLE_UNKNOWN;
+ fcport->rport = rport = fc_remote_port_add(vha->host, 0, &rport_ids);
+ if (!rport) {
+ ql_log(ql_log_warn, vha, 0x2006,
+ "Unable to allocate fc remote port.\n");
+ return;
+ }
+ spin_lock_irqsave(fcport->vha->host->host_lock, flags);
+ *((fc_port_t **)rport->dd_data) = fcport;
+ spin_unlock_irqrestore(fcport->vha->host->host_lock, flags);
+
+ rport->supported_classes = fcport->supported_classes;
+
+ rport_ids.roles = FC_RPORT_ROLE_UNKNOWN;
+ if (fcport->port_type == FCT_INITIATOR)
+ rport_ids.roles |= FC_RPORT_ROLE_FCP_INITIATOR;
+ if (fcport->port_type == FCT_TARGET)
+ rport_ids.roles |= FC_RPORT_ROLE_FCP_TARGET;
+ fc_remote_port_rolechg(rport, rport_ids.roles);
+}
+
+/*
+ * qla2x00_update_fcport
+ * Updates device on list.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * fcport = port structure pointer.
+ *
+ * Return:
+ * 0 - Success
+ * BIT_0 - error
+ *
+ * Context:
+ * Kernel context.
+ */
+void
+qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport)
+{
+ fcport->vha = vha;
+ fcport->login_retry = 0;
+ fcport->flags &= ~(FCF_LOGIN_NEEDED | FCF_ASYNC_SENT);
+
+ qla2x00_iidma_fcport(vha, fcport);
+ qla24xx_update_fcport_fcp_prio(vha, fcport);
+ qla2x00_reg_remote_port(vha, fcport);
+ qla2x00_set_fcport_state(fcport, FCS_ONLINE);
+}
+
+/*
+ * qla2x00_configure_fabric
+ * Setup SNS devices with loop ID's.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Returns:
+ * 0 = success.
+ * BIT_0 = error
+ */
+static int
+qla2x00_configure_fabric(scsi_qla_host_t *vha)
+{
+ int rval;
+ fc_port_t *fcport, *fcptemp;
+ uint16_t next_loopid;
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+ uint16_t loop_id;
+ LIST_HEAD(new_fcports);
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+
+ /* If FL port exists, then SNS is present */
+ if (IS_FWI2_CAPABLE(ha))
+ loop_id = NPH_F_PORT;
+ else
+ loop_id = SNS_FL_PORT;
+ rval = qla2x00_get_port_name(vha, loop_id, vha->fabric_node_name, 1);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_disc, vha, 0x201f,
+ "MBX_GET_PORT_NAME failed, No FL Port.\n");
+
+ vha->device_flags &= ~SWITCH_FOUND;
+ return (QLA_SUCCESS);
+ }
+ vha->device_flags |= SWITCH_FOUND;
+
+ do {
+ /* FDMI support. */
+ if (ql2xfdmienable &&
+ test_and_clear_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags))
+ qla2x00_fdmi_register(vha);
+
+ /* Ensure we are logged into the SNS. */
+ if (IS_FWI2_CAPABLE(ha))
+ loop_id = NPH_SNS;
+ else
+ loop_id = SIMPLE_NAME_SERVER;
+ rval = ha->isp_ops->fabric_login(vha, loop_id, 0xff, 0xff,
+ 0xfc, mb, BIT_1|BIT_0);
+ if (rval != QLA_SUCCESS) {
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ return rval;
+ }
+ if (mb[0] != MBS_COMMAND_COMPLETE) {
+ ql_dbg(ql_dbg_disc, vha, 0x2042,
+ "Failed SNS login: loop_id=%x mb[0]=%x mb[1]=%x mb[2]=%x "
+ "mb[6]=%x mb[7]=%x.\n", loop_id, mb[0], mb[1],
+ mb[2], mb[6], mb[7]);
+ return (QLA_SUCCESS);
+ }
+
+ if (test_and_clear_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags)) {
+ if (qla2x00_rft_id(vha)) {
+ /* EMPTY */
+ ql_dbg(ql_dbg_disc, vha, 0x2045,
+ "Register FC-4 TYPE failed.\n");
+ }
+ if (qla2x00_rff_id(vha)) {
+ /* EMPTY */
+ ql_dbg(ql_dbg_disc, vha, 0x2049,
+ "Register FC-4 Features failed.\n");
+ }
+ if (qla2x00_rnn_id(vha)) {
+ /* EMPTY */
+ ql_dbg(ql_dbg_disc, vha, 0x204f,
+ "Register Node Name failed.\n");
+ } else if (qla2x00_rsnn_nn(vha)) {
+ /* EMPTY */
+ ql_dbg(ql_dbg_disc, vha, 0x2053,
+ "Register Symobilic Node Name failed.\n");
+ }
+ }
+
+#define QLA_FCPORT_SCAN 1
+#define QLA_FCPORT_FOUND 2
+
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ fcport->scan_state = QLA_FCPORT_SCAN;
+ }
+
+ rval = qla2x00_find_all_fabric_devs(vha, &new_fcports);
+ if (rval != QLA_SUCCESS)
+ break;
+
+ /*
+ * Logout all previous fabric devices marked lost, except
+ * FCP2 devices.
+ */
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
+ break;
+
+ if ((fcport->flags & FCF_FABRIC_DEVICE) == 0)
+ continue;
+
+ if (fcport->scan_state == QLA_FCPORT_SCAN &&
+ atomic_read(&fcport->state) == FCS_ONLINE) {
+ qla2x00_mark_device_lost(vha, fcport,
+ ql2xplogiabsentdevice, 0);
+ if (fcport->loop_id != FC_NO_LOOP_ID &&
+ (fcport->flags & FCF_FCP2_DEVICE) == 0 &&
+ fcport->port_type != FCT_INITIATOR &&
+ fcport->port_type != FCT_BROADCAST) {
+ ha->isp_ops->fabric_logout(vha,
+ fcport->loop_id,
+ fcport->d_id.b.domain,
+ fcport->d_id.b.area,
+ fcport->d_id.b.al_pa);
+ fcport->loop_id = FC_NO_LOOP_ID;
+ }
+ }
+ }
+
+ /* Starting free loop ID. */
+ next_loopid = ha->min_external_loopid;
+
+ /*
+ * Scan through our port list and login entries that need to be
+ * logged in.
+ */
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ if (atomic_read(&vha->loop_down_timer) ||
+ test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
+ break;
+
+ if ((fcport->flags & FCF_FABRIC_DEVICE) == 0 ||
+ (fcport->flags & FCF_LOGIN_NEEDED) == 0)
+ continue;
+
+ if (fcport->loop_id == FC_NO_LOOP_ID) {
+ fcport->loop_id = next_loopid;
+ rval = qla2x00_find_new_loop_id(
+ base_vha, fcport);
+ if (rval != QLA_SUCCESS) {
+ /* Ran out of IDs to use */
+ break;
+ }
+ }
+ /* Login and update database */
+ qla2x00_fabric_dev_login(vha, fcport, &next_loopid);
+ }
+
+ /* Exit if out of loop IDs. */
+ if (rval != QLA_SUCCESS) {
+ break;
+ }
+
+ /*
+ * Login and add the new devices to our port list.
+ */
+ list_for_each_entry_safe(fcport, fcptemp, &new_fcports, list) {
+ if (atomic_read(&vha->loop_down_timer) ||
+ test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
+ break;
+
+ /* Find a new loop ID to use. */
+ fcport->loop_id = next_loopid;
+ rval = qla2x00_find_new_loop_id(base_vha, fcport);
+ if (rval != QLA_SUCCESS) {
+ /* Ran out of IDs to use */
+ break;
+ }
+
+ /* Login and update database */
+ qla2x00_fabric_dev_login(vha, fcport, &next_loopid);
+
+ if (vha->vp_idx) {
+ fcport->vha = vha;
+ fcport->vp_idx = vha->vp_idx;
+ }
+ list_move_tail(&fcport->list, &vha->vp_fcports);
+ }
+ } while (0);
+
+ /* Free all new device structures not processed. */
+ list_for_each_entry_safe(fcport, fcptemp, &new_fcports, list) {
+ list_del(&fcport->list);
+ kfree(fcport);
+ }
+
+ if (rval) {
+ ql_dbg(ql_dbg_disc, vha, 0x2068,
+ "Configure fabric error exit rval=%d.\n", rval);
+ }
+
+ return (rval);
+}
+
+/*
+ * qla2x00_find_all_fabric_devs
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * dev = database device entry pointer.
+ *
+ * Returns:
+ * 0 = success.
+ *
+ * Context:
+ * Kernel context.
+ */
+static int
+qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha,
+ struct list_head *new_fcports)
+{
+ int rval;
+ uint16_t loop_id;
+ fc_port_t *fcport, *new_fcport, *fcptemp;
+ int found;
+
+ sw_info_t *swl;
+ int swl_idx;
+ int first_dev, last_dev;
+ port_id_t wrap = {}, nxt_d_id;
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *vp, *base_vha = pci_get_drvdata(ha->pdev);
+ struct scsi_qla_host *tvp;
+
+ rval = QLA_SUCCESS;
+
+ /* Try GID_PT to get device list, else GAN. */
+ if (!ha->swl)
+ ha->swl = kcalloc(ha->max_fibre_devices, sizeof(sw_info_t),
+ GFP_KERNEL);
+ swl = ha->swl;
+ if (!swl) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_disc, vha, 0x2054,
+ "GID_PT allocations failed, fallback on GA_NXT.\n");
+ } else {
+ memset(swl, 0, ha->max_fibre_devices * sizeof(sw_info_t));
+ if (qla2x00_gid_pt(vha, swl) != QLA_SUCCESS) {
+ swl = NULL;
+ } else if (qla2x00_gpn_id(vha, swl) != QLA_SUCCESS) {
+ swl = NULL;
+ } else if (qla2x00_gnn_id(vha, swl) != QLA_SUCCESS) {
+ swl = NULL;
+ } else if (ql2xiidmaenable &&
+ qla2x00_gfpn_id(vha, swl) == QLA_SUCCESS) {
+ qla2x00_gpsc(vha, swl);
+ }
+
+ /* If other queries succeeded probe for FC-4 type */
+ if (swl)
+ qla2x00_gff_id(vha, swl);
+ }
+ swl_idx = 0;
+
+ /* Allocate temporary fcport for any new fcports discovered. */
+ new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
+ if (new_fcport == NULL) {
+ ql_log(ql_log_warn, vha, 0x205e,
+ "Failed to allocate memory for fcport.\n");
+ return (QLA_MEMORY_ALLOC_FAILED);
+ }
+ new_fcport->flags |= (FCF_FABRIC_DEVICE | FCF_LOGIN_NEEDED);
+ /* Set start port ID scan at adapter ID. */
+ first_dev = 1;
+ last_dev = 0;
+
+ /* Starting free loop ID. */
+ loop_id = ha->min_external_loopid;
+ for (; loop_id <= ha->max_loop_id; loop_id++) {
+ if (qla2x00_is_reserved_id(vha, loop_id))
+ continue;
+
+ if (ha->current_topology == ISP_CFG_FL &&
+ (atomic_read(&vha->loop_down_timer) ||
+ LOOP_TRANSITION(vha))) {
+ atomic_set(&vha->loop_down_timer, 0);
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
+ break;
+ }
+
+ if (swl != NULL) {
+ if (last_dev) {
+ wrap.b24 = new_fcport->d_id.b24;
+ } else {
+ new_fcport->d_id.b24 = swl[swl_idx].d_id.b24;
+ memcpy(new_fcport->node_name,
+ swl[swl_idx].node_name, WWN_SIZE);
+ memcpy(new_fcport->port_name,
+ swl[swl_idx].port_name, WWN_SIZE);
+ memcpy(new_fcport->fabric_port_name,
+ swl[swl_idx].fabric_port_name, WWN_SIZE);
+ new_fcport->fp_speed = swl[swl_idx].fp_speed;
+ new_fcport->fc4_type = swl[swl_idx].fc4_type;
+
+ if (swl[swl_idx].d_id.b.rsvd_1 != 0) {
+ last_dev = 1;
+ }
+ swl_idx++;
+ }
+ } else {
+ /* Send GA_NXT to the switch */
+ rval = qla2x00_ga_nxt(vha, new_fcport);
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x2064,
+ "SNS scan failed -- assuming "
+ "zero-entry result.\n");
+ list_for_each_entry_safe(fcport, fcptemp,
+ new_fcports, list) {
+ list_del(&fcport->list);
+ kfree(fcport);
+ }
+ rval = QLA_SUCCESS;
+ break;
+ }
+ }
+
+ /* If wrap on switch device list, exit. */
+ if (first_dev) {
+ wrap.b24 = new_fcport->d_id.b24;
+ first_dev = 0;
+ } else if (new_fcport->d_id.b24 == wrap.b24) {
+ ql_dbg(ql_dbg_disc, vha, 0x2065,
+ "Device wrap (%02x%02x%02x).\n",
+ new_fcport->d_id.b.domain,
+ new_fcport->d_id.b.area,
+ new_fcport->d_id.b.al_pa);
+ break;
+ }
+
+ /* Bypass if same physical adapter. */
+ if (new_fcport->d_id.b24 == base_vha->d_id.b24)
+ continue;
+
+ /* Bypass virtual ports of the same host. */
+ found = 0;
+ if (ha->num_vhosts) {
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) {
+ if (new_fcport->d_id.b24 == vp->d_id.b24) {
+ found = 1;
+ break;
+ }
+ }
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
+ if (found)
+ continue;
+ }
+
+ /* Bypass if same domain and area of adapter. */
+ if (((new_fcport->d_id.b24 & 0xffff00) ==
+ (vha->d_id.b24 & 0xffff00)) && ha->current_topology ==
+ ISP_CFG_FL)
+ continue;
+
+ /* Bypass reserved domain fields. */
+ if ((new_fcport->d_id.b.domain & 0xf0) == 0xf0)
+ continue;
+
+ /* Bypass ports whose FCP-4 type is not FCP_SCSI */
+ if (ql2xgffidenable &&
+ (new_fcport->fc4_type != FC4_TYPE_FCP_SCSI &&
+ new_fcport->fc4_type != FC4_TYPE_UNKNOWN))
+ continue;
+
+ /* Locate matching device in database. */
+ found = 0;
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ if (memcmp(new_fcport->port_name, fcport->port_name,
+ WWN_SIZE))
+ continue;
+
+ fcport->scan_state = QLA_FCPORT_FOUND;
+
+ found++;
+
+ /* Update port state. */
+ memcpy(fcport->fabric_port_name,
+ new_fcport->fabric_port_name, WWN_SIZE);
+ fcport->fp_speed = new_fcport->fp_speed;
+
+ /*
+ * If address the same and state FCS_ONLINE, nothing
+ * changed.
+ */
+ if (fcport->d_id.b24 == new_fcport->d_id.b24 &&
+ atomic_read(&fcport->state) == FCS_ONLINE) {
+ break;
+ }
+
+ /*
+ * If device was not a fabric device before.
+ */
+ if ((fcport->flags & FCF_FABRIC_DEVICE) == 0) {
+ fcport->d_id.b24 = new_fcport->d_id.b24;
+ fcport->loop_id = FC_NO_LOOP_ID;
+ fcport->flags |= (FCF_FABRIC_DEVICE |
+ FCF_LOGIN_NEEDED);
+ break;
+ }
+
+ /*
+ * Port ID changed or device was marked to be updated;
+ * Log it out if still logged in and mark it for
+ * relogin later.
+ */
+ fcport->d_id.b24 = new_fcport->d_id.b24;
+ fcport->flags |= FCF_LOGIN_NEEDED;
+ if (fcport->loop_id != FC_NO_LOOP_ID &&
+ (fcport->flags & FCF_FCP2_DEVICE) == 0 &&
+ (fcport->flags & FCF_ASYNC_SENT) == 0 &&
+ fcport->port_type != FCT_INITIATOR &&
+ fcport->port_type != FCT_BROADCAST) {
+ ha->isp_ops->fabric_logout(vha, fcport->loop_id,
+ fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa);
+ fcport->loop_id = FC_NO_LOOP_ID;
+ }
+
+ break;
+ }
+
+ if (found)
+ continue;
+ /* If device was not in our fcports list, then add it. */
+ list_add_tail(&new_fcport->list, new_fcports);
+
+ /* Allocate a new replacement fcport. */
+ nxt_d_id.b24 = new_fcport->d_id.b24;
+ new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL);
+ if (new_fcport == NULL) {
+ ql_log(ql_log_warn, vha, 0x2066,
+ "Memory allocation failed for fcport.\n");
+ return (QLA_MEMORY_ALLOC_FAILED);
+ }
+ new_fcport->flags |= (FCF_FABRIC_DEVICE | FCF_LOGIN_NEEDED);
+ new_fcport->d_id.b24 = nxt_d_id.b24;
+ }
+
+ kfree(new_fcport);
+
+ return (rval);
+}
+
+/*
+ * qla2x00_find_new_loop_id
+ * Scan through our port list and find a new usable loop ID.
+ *
+ * Input:
+ * ha: adapter state pointer.
+ * dev: port structure pointer.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_find_new_loop_id(scsi_qla_host_t *vha, fc_port_t *dev)
+{
+ int rval;
+ int found;
+ fc_port_t *fcport;
+ uint16_t first_loop_id;
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *vp;
+ struct scsi_qla_host *tvp;
+ unsigned long flags = 0;
+
+ rval = QLA_SUCCESS;
+
+ /* Save starting loop ID. */
+ first_loop_id = dev->loop_id;
+
+ for (;;) {
+ /* Skip loop ID if already used by adapter. */
+ if (dev->loop_id == vha->loop_id)
+ dev->loop_id++;
+
+ /* Skip reserved loop IDs. */
+ while (qla2x00_is_reserved_id(vha, dev->loop_id))
+ dev->loop_id++;
+
+ /* Reset loop ID if passed the end. */
+ if (dev->loop_id > ha->max_loop_id) {
+ /* first loop ID. */
+ dev->loop_id = ha->min_external_loopid;
+ }
+
+ /* Check for loop ID being already in use. */
+ found = 0;
+ fcport = NULL;
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) {
+ list_for_each_entry(fcport, &vp->vp_fcports, list) {
+ if (fcport->loop_id == dev->loop_id &&
+ fcport != dev) {
+ /* ID possibly in use */
+ found++;
+ break;
+ }
+ }
+ if (found)
+ break;
+ }
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
+ /* If not in use then it is free to use. */
+ if (!found) {
+ ql_dbg(ql_dbg_disc, dev->vha, 0x2086,
+ "Assigning new loopid=%x, portid=%x.\n",
+ dev->loop_id, dev->d_id.b24);
+ break;
+ }
+
+ /* ID in use. Try next value. */
+ dev->loop_id++;
+
+ /* If wrap around. No free ID to use. */
+ if (dev->loop_id == first_loop_id) {
+ dev->loop_id = FC_NO_LOOP_ID;
+ rval = QLA_FUNCTION_FAILED;
+ break;
+ }
+ }
+
+ return (rval);
+}
+
+/*
+ * qla2x00_fabric_dev_login
+ * Login fabric target device and update FC port database.
+ *
+ * Input:
+ * ha: adapter state pointer.
+ * fcport: port structure list pointer.
+ * next_loopid: contains value of a new loop ID that can be used
+ * by the next login attempt.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+static int
+qla2x00_fabric_dev_login(scsi_qla_host_t *vha, fc_port_t *fcport,
+ uint16_t *next_loopid)
+{
+ int rval;
+ int retry;
+ uint8_t opts;
+ struct qla_hw_data *ha = vha->hw;
+
+ rval = QLA_SUCCESS;
+ retry = 0;
+
+ if (IS_ALOGIO_CAPABLE(ha)) {
+ if (fcport->flags & FCF_ASYNC_SENT)
+ return rval;
+ fcport->flags |= FCF_ASYNC_SENT;
+ rval = qla2x00_post_async_login_work(vha, fcport, NULL);
+ if (!rval)
+ return rval;
+ }
+
+ fcport->flags &= ~FCF_ASYNC_SENT;
+ rval = qla2x00_fabric_login(vha, fcport, next_loopid);
+ if (rval == QLA_SUCCESS) {
+ /* Send an ADISC to FCP2 devices.*/
+ opts = 0;
+ if (fcport->flags & FCF_FCP2_DEVICE)
+ opts |= BIT_1;
+ rval = qla2x00_get_port_database(vha, fcport, opts);
+ if (rval != QLA_SUCCESS) {
+ ha->isp_ops->fabric_logout(vha, fcport->loop_id,
+ fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa);
+ qla2x00_mark_device_lost(vha, fcport, 1, 0);
+ } else {
+ qla2x00_update_fcport(vha, fcport);
+ }
+ } else {
+ /* Retry Login. */
+ qla2x00_mark_device_lost(vha, fcport, 1, 0);
+ }
+
+ return (rval);
+}
+
+/*
+ * qla2x00_fabric_login
+ * Issue fabric login command.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * device = pointer to FC device type structure.
+ *
+ * Returns:
+ * 0 - Login successfully
+ * 1 - Login failed
+ * 2 - Initiator device
+ * 3 - Fatal error
+ */
+int
+qla2x00_fabric_login(scsi_qla_host_t *vha, fc_port_t *fcport,
+ uint16_t *next_loopid)
+{
+ int rval;
+ int retry;
+ uint16_t tmp_loopid;
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+ struct qla_hw_data *ha = vha->hw;
+
+ retry = 0;
+ tmp_loopid = 0;
+
+ for (;;) {
+ ql_dbg(ql_dbg_disc, vha, 0x2000,
+ "Trying Fabric Login w/loop id 0x%04x for port "
+ "%02x%02x%02x.\n",
+ fcport->loop_id, fcport->d_id.b.domain,
+ fcport->d_id.b.area, fcport->d_id.b.al_pa);
+
+ /* Login fcport on switch. */
+ rval = ha->isp_ops->fabric_login(vha, fcport->loop_id,
+ fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa, mb, BIT_0);
+ if (rval != QLA_SUCCESS) {
+ return rval;
+ }
+ if (mb[0] == MBS_PORT_ID_USED) {
+ /*
+ * Device has another loop ID. The firmware team
+ * recommends the driver perform an implicit login with
+ * the specified ID again. The ID we just used is save
+ * here so we return with an ID that can be tried by
+ * the next login.
+ */
+ retry++;
+ tmp_loopid = fcport->loop_id;
+ fcport->loop_id = mb[1];
+
+ ql_dbg(ql_dbg_disc, vha, 0x2001,
+ "Fabric Login: port in use - next loop "
+ "id=0x%04x, port id= %02x%02x%02x.\n",
+ fcport->loop_id, fcport->d_id.b.domain,
+ fcport->d_id.b.area, fcport->d_id.b.al_pa);
+
+ } else if (mb[0] == MBS_COMMAND_COMPLETE) {
+ /*
+ * Login succeeded.
+ */
+ if (retry) {
+ /* A retry occurred before. */
+ *next_loopid = tmp_loopid;
+ } else {
+ /*
+ * No retry occurred before. Just increment the
+ * ID value for next login.
+ */
+ *next_loopid = (fcport->loop_id + 1);
+ }
+
+ if (mb[1] & BIT_0) {
+ fcport->port_type = FCT_INITIATOR;
+ } else {
+ fcport->port_type = FCT_TARGET;
+ if (mb[1] & BIT_1) {
+ fcport->flags |= FCF_FCP2_DEVICE;
+ }
+ }
+
+ if (mb[10] & BIT_0)
+ fcport->supported_classes |= FC_COS_CLASS2;
+ if (mb[10] & BIT_1)
+ fcport->supported_classes |= FC_COS_CLASS3;
+
+ rval = QLA_SUCCESS;
+ break;
+ } else if (mb[0] == MBS_LOOP_ID_USED) {
+ /*
+ * Loop ID already used, try next loop ID.
+ */
+ fcport->loop_id++;
+ rval = qla2x00_find_new_loop_id(vha, fcport);
+ if (rval != QLA_SUCCESS) {
+ /* Ran out of loop IDs to use */
+ break;
+ }
+ } else if (mb[0] == MBS_COMMAND_ERROR) {
+ /*
+ * Firmware possibly timed out during login. If NO
+ * retries are left to do then the device is declared
+ * dead.
+ */
+ *next_loopid = fcport->loop_id;
+ ha->isp_ops->fabric_logout(vha, fcport->loop_id,
+ fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa);
+ qla2x00_mark_device_lost(vha, fcport, 1, 0);
+
+ rval = 1;
+ break;
+ } else {
+ /*
+ * unrecoverable / not handled error
+ */
+ ql_dbg(ql_dbg_disc, vha, 0x2002,
+ "Failed=%x port_id=%02x%02x%02x loop_id=%x "
+ "jiffies=%lx.\n", mb[0], fcport->d_id.b.domain,
+ fcport->d_id.b.area, fcport->d_id.b.al_pa,
+ fcport->loop_id, jiffies);
+
+ *next_loopid = fcport->loop_id;
+ ha->isp_ops->fabric_logout(vha, fcport->loop_id,
+ fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa);
+ fcport->loop_id = FC_NO_LOOP_ID;
+ fcport->login_retry = 0;
+
+ rval = 3;
+ break;
+ }
+ }
+
+ return (rval);
+}
+
+/*
+ * qla2x00_local_device_login
+ * Issue local device login command.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * loop_id = loop id of device to login to.
+ *
+ * Returns (Where's the #define!!!!):
+ * 0 - Login successfully
+ * 1 - Login failed
+ * 3 - Fatal error
+ */
+int
+qla2x00_local_device_login(scsi_qla_host_t *vha, fc_port_t *fcport)
+{
+ int rval;
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+
+ memset(mb, 0, sizeof(mb));
+ rval = qla2x00_login_local_device(vha, fcport, mb, BIT_0);
+ if (rval == QLA_SUCCESS) {
+ /* Interrogate mailbox registers for any errors */
+ if (mb[0] == MBS_COMMAND_ERROR)
+ rval = 1;
+ else if (mb[0] == MBS_COMMAND_PARAMETER_ERROR)
+ /* device not in PCB table */
+ rval = 3;
+ }
+
+ return (rval);
+}
+
+/*
+ * qla2x00_loop_resync
+ * Resync with fibre channel devices.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Returns:
+ * 0 = success
+ */
+int
+qla2x00_loop_resync(scsi_qla_host_t *vha)
+{
+ int rval = QLA_SUCCESS;
+ uint32_t wait_time;
+ struct req_que *req;
+ struct rsp_que *rsp;
+
+ if (vha->hw->flags.cpu_affinity_enabled)
+ req = vha->hw->req_q_map[0];
+ else
+ req = vha->req;
+ rsp = req->rsp;
+
+ clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
+ if (vha->flags.online) {
+ if (!(rval = qla2x00_fw_ready(vha))) {
+ /* Wait at most MAX_TARGET RSCNs for a stable link. */
+ wait_time = 256;
+ do {
+ /* Issue a marker after FW becomes ready. */
+ qla2x00_marker(vha, req, rsp, 0, 0,
+ MK_SYNC_ALL);
+ vha->marker_needed = 0;
+
+ /* Remap devices on Loop. */
+ clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+
+ qla2x00_configure_loop(vha);
+ wait_time--;
+ } while (!atomic_read(&vha->loop_down_timer) &&
+ !(test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags))
+ && wait_time && (test_bit(LOOP_RESYNC_NEEDED,
+ &vha->dpc_flags)));
+ }
+ }
+
+ if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags))
+ return (QLA_FUNCTION_FAILED);
+
+ if (rval)
+ ql_dbg(ql_dbg_disc, vha, 0x206c,
+ "%s *** FAILED ***.\n", __func__);
+
+ return (rval);
+}
+
+/*
+* qla2x00_perform_loop_resync
+* Description: This function will set the appropriate flags and call
+* qla2x00_loop_resync. If successful loop will be resynced
+* Arguments : scsi_qla_host_t pointer
+* returm : Success or Failure
+*/
+
+int qla2x00_perform_loop_resync(scsi_qla_host_t *ha)
+{
+ int32_t rval = 0;
+
+ if (!test_and_set_bit(LOOP_RESYNC_ACTIVE, &ha->dpc_flags)) {
+ /*Configure the flags so that resync happens properly*/
+ atomic_set(&ha->loop_down_timer, 0);
+ if (!(ha->device_flags & DFLG_NO_CABLE)) {
+ atomic_set(&ha->loop_state, LOOP_UP);
+ set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags);
+ set_bit(REGISTER_FC4_NEEDED, &ha->dpc_flags);
+ set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags);
+
+ rval = qla2x00_loop_resync(ha);
+ } else
+ atomic_set(&ha->loop_state, LOOP_DEAD);
+
+ clear_bit(LOOP_RESYNC_ACTIVE, &ha->dpc_flags);
+ }
+
+ return rval;
+}
+
+void
+qla2x00_update_fcports(scsi_qla_host_t *base_vha)
+{
+ fc_port_t *fcport;
+ struct scsi_qla_host *vha;
+ struct qla_hw_data *ha = base_vha->hw;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ /* Go with deferred removal of rport references. */
+ list_for_each_entry(vha, &base_vha->hw->vp_list, list) {
+ atomic_inc(&vha->vref_count);
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ if (fcport->drport &&
+ atomic_read(&fcport->state) != FCS_UNCONFIGURED) {
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
+ qla2x00_rport_del(fcport);
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ }
+ }
+ atomic_dec(&vha->vref_count);
+ }
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+}
+
+/*
+* qla82xx_quiescent_state_cleanup
+* Description: This function will block the new I/Os
+* Its not aborting any I/Os as context
+* is not destroyed during quiescence
+* Arguments: scsi_qla_host_t
+* return : void
+*/
+void
+qla82xx_quiescent_state_cleanup(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *vp;
+
+ ql_dbg(ql_dbg_p3p, vha, 0xb002,
+ "Performing ISP error recovery - ha=%p.\n", ha);
+
+ atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME);
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ qla2x00_mark_all_devices_lost(vha, 0);
+ list_for_each_entry(vp, &ha->vp_list, list)
+ qla2x00_mark_all_devices_lost(vha, 0);
+ } else {
+ if (!atomic_read(&vha->loop_down_timer))
+ atomic_set(&vha->loop_down_timer,
+ LOOP_DOWN_TIME);
+ }
+ /* Wait for pending cmds to complete */
+ qla2x00_eh_wait_for_pending_commands(vha, 0, 0, WAIT_HOST);
+}
+
+void
+qla2x00_abort_isp_cleanup(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *vp;
+ unsigned long flags;
+ fc_port_t *fcport;
+
+ /* For ISP82XX, driver waits for completion of the commands.
+ * online flag should be set.
+ */
+ if (!IS_QLA82XX(ha))
+ vha->flags.online = 0;
+ ha->flags.chip_reset_done = 0;
+ clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ ha->qla_stats.total_isp_aborts++;
+
+ ql_log(ql_log_info, vha, 0x00af,
+ "Performing ISP error recovery - ha=%p.\n", ha);
+
+ /* For ISP82XX, reset_chip is just disabling interrupts.
+ * Driver waits for the completion of the commands.
+ * the interrupts need to be enabled.
+ */
+ if (!IS_QLA82XX(ha))
+ ha->isp_ops->reset_chip(vha);
+
+ atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ qla2x00_mark_all_devices_lost(vha, 0);
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ list_for_each_entry(vp, &ha->vp_list, list) {
+ atomic_inc(&vp->vref_count);
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
+ qla2x00_mark_all_devices_lost(vp, 0);
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ atomic_dec(&vp->vref_count);
+ }
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+ } else {
+ if (!atomic_read(&vha->loop_down_timer))
+ atomic_set(&vha->loop_down_timer,
+ LOOP_DOWN_TIME);
+ }
+
+ /* Clear all async request states across all VPs. */
+ list_for_each_entry(fcport, &vha->vp_fcports, list)
+ fcport->flags &= ~(FCF_LOGIN_NEEDED | FCF_ASYNC_SENT);
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ list_for_each_entry(vp, &ha->vp_list, list) {
+ atomic_inc(&vp->vref_count);
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
+ list_for_each_entry(fcport, &vp->vp_fcports, list)
+ fcport->flags &= ~(FCF_LOGIN_NEEDED | FCF_ASYNC_SENT);
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ atomic_dec(&vp->vref_count);
+ }
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
+ if (!ha->flags.eeh_busy) {
+ /* Make sure for ISP 82XX IO DMA is complete */
+ if (IS_QLA82XX(ha)) {
+ qla82xx_chip_reset_cleanup(vha);
+ ql_log(ql_log_info, vha, 0x00b4,
+ "Done chip reset cleanup.\n");
+
+ /* Done waiting for pending commands.
+ * Reset the online flag.
+ */
+ vha->flags.online = 0;
+ }
+
+ /* Requeue all commands in outstanding command list. */
+ qla2x00_abort_all_cmds(vha, DID_RESET << 16);
+ }
+}
+
+/*
+* qla2x00_abort_isp
+* Resets ISP and aborts all outstanding commands.
+*
+* Input:
+* ha = adapter block pointer.
+*
+* Returns:
+* 0 = success
+*/
+int
+qla2x00_abort_isp(scsi_qla_host_t *vha)
+{
+ int rval;
+ uint8_t status = 0;
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *vp;
+ struct req_que *req = ha->req_q_map[0];
+ unsigned long flags;
+
+ if (vha->flags.online) {
+ qla2x00_abort_isp_cleanup(vha);
+
+ if (unlikely(pci_channel_offline(ha->pdev) &&
+ ha->flags.pci_channel_io_perm_failure)) {
+ clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
+ status = 0;
+ return status;
+ }
+
+ ha->isp_ops->get_flash_version(vha, req->ring);
+
+ ha->isp_ops->nvram_config(vha);
+
+ if (!qla2x00_restart_isp(vha)) {
+ clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
+
+ if (!atomic_read(&vha->loop_down_timer)) {
+ /*
+ * Issue marker command only when we are going
+ * to start the I/O .
+ */
+ vha->marker_needed = 1;
+ }
+
+ vha->flags.online = 1;
+
+ ha->isp_ops->enable_intrs(ha);
+
+ ha->isp_abort_cnt = 0;
+ clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
+
+ if (IS_QLA81XX(ha) || IS_QLA8031(ha))
+ qla2x00_get_fw_version(vha);
+ if (ha->fce) {
+ ha->flags.fce_enabled = 1;
+ memset(ha->fce, 0,
+ fce_calc_size(ha->fce_bufs));
+ rval = qla2x00_enable_fce_trace(vha,
+ ha->fce_dma, ha->fce_bufs, ha->fce_mb,
+ &ha->fce_bufs);
+ if (rval) {
+ ql_log(ql_log_warn, vha, 0x8033,
+ "Unable to reinitialize FCE "
+ "(%d).\n", rval);
+ ha->flags.fce_enabled = 0;
+ }
+ }
+
+ if (ha->eft) {
+ memset(ha->eft, 0, EFT_SIZE);
+ rval = qla2x00_enable_eft_trace(vha,
+ ha->eft_dma, EFT_NUM_BUFFERS);
+ if (rval) {
+ ql_log(ql_log_warn, vha, 0x8034,
+ "Unable to reinitialize EFT "
+ "(%d).\n", rval);
+ }
+ }
+ } else { /* failed the ISP abort */
+ vha->flags.online = 1;
+ if (test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) {
+ if (ha->isp_abort_cnt == 0) {
+ ql_log(ql_log_fatal, vha, 0x8035,
+ "ISP error recover failed - "
+ "board disabled.\n");
+ /*
+ * The next call disables the board
+ * completely.
+ */
+ ha->isp_ops->reset_adapter(vha);
+ vha->flags.online = 0;
+ clear_bit(ISP_ABORT_RETRY,
+ &vha->dpc_flags);
+ status = 0;
+ } else { /* schedule another ISP abort */
+ ha->isp_abort_cnt--;
+ ql_dbg(ql_dbg_taskm, vha, 0x8020,
+ "ISP abort - retry remaining %d.\n",
+ ha->isp_abort_cnt);
+ status = 1;
+ }
+ } else {
+ ha->isp_abort_cnt = MAX_RETRIES_OF_ISP_ABORT;
+ ql_dbg(ql_dbg_taskm, vha, 0x8021,
+ "ISP error recovery - retrying (%d) "
+ "more times.\n", ha->isp_abort_cnt);
+ set_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
+ status = 1;
+ }
+ }
+
+ }
+
+ if (!status) {
+ ql_dbg(ql_dbg_taskm, vha, 0x8022, "%s succeeded.\n", __func__);
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ list_for_each_entry(vp, &ha->vp_list, list) {
+ if (vp->vp_idx) {
+ atomic_inc(&vp->vref_count);
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
+ qla2x00_vp_abort_isp(vp);
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ atomic_dec(&vp->vref_count);
+ }
+ }
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
+ } else {
+ ql_log(ql_log_warn, vha, 0x8023, "%s **** FAILED ****.\n",
+ __func__);
+ }
+
+ return(status);
+}
+
+/*
+* qla2x00_restart_isp
+* restarts the ISP after a reset
+*
+* Input:
+* ha = adapter block pointer.
+*
+* Returns:
+* 0 = success
+*/
+static int
+qla2x00_restart_isp(scsi_qla_host_t *vha)
+{
+ int status = 0;
+ uint32_t wait_time;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
+
+ /* If firmware needs to be loaded */
+ if (qla2x00_isp_firmware(vha)) {
+ vha->flags.online = 0;
+ status = ha->isp_ops->chip_diag(vha);
+ if (!status)
+ status = qla2x00_setup_chip(vha);
+ }
+
+ if (!status && !(status = qla2x00_init_rings(vha))) {
+ clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
+ ha->flags.chip_reset_done = 1;
+ /* Initialize the queues in use */
+ qla25xx_init_queues(ha);
+
+ status = qla2x00_fw_ready(vha);
+ if (!status) {
+ ql_dbg(ql_dbg_taskm, vha, 0x8031,
+ "Start configure loop status = %d.\n", status);
+
+ /* Issue a marker after FW becomes ready. */
+ qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL);
+
+ vha->flags.online = 1;
+ /* Wait at most MAX_TARGET RSCNs for a stable link. */
+ wait_time = 256;
+ do {
+ clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ qla2x00_configure_loop(vha);
+ wait_time--;
+ } while (!atomic_read(&vha->loop_down_timer) &&
+ !(test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags))
+ && wait_time && (test_bit(LOOP_RESYNC_NEEDED,
+ &vha->dpc_flags)));
+ }
+
+ /* if no cable then assume it's good */
+ if ((vha->device_flags & DFLG_NO_CABLE))
+ status = 0;
+
+ ql_dbg(ql_dbg_taskm, vha, 0x8032,
+ "Configure loop done, status = 0x%x.\n", status);
+ }
+ return (status);
+}
+
+static int
+qla25xx_init_queues(struct qla_hw_data *ha)
+{
+ struct rsp_que *rsp = NULL;
+ struct req_que *req = NULL;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+ int ret = -1;
+ int i;
+
+ for (i = 1; i < ha->max_rsp_queues; i++) {
+ rsp = ha->rsp_q_map[i];
+ if (rsp) {
+ rsp->options &= ~BIT_0;
+ ret = qla25xx_init_rsp_que(base_vha, rsp);
+ if (ret != QLA_SUCCESS)
+ ql_dbg(ql_dbg_init, base_vha, 0x00ff,
+ "%s Rsp que: %d init failed.\n",
+ __func__, rsp->id);
+ else
+ ql_dbg(ql_dbg_init, base_vha, 0x0100,
+ "%s Rsp que: %d inited.\n",
+ __func__, rsp->id);
+ }
+ }
+ for (i = 1; i < ha->max_req_queues; i++) {
+ req = ha->req_q_map[i];
+ if (req) {
+ /* Clear outstanding commands array. */
+ req->options &= ~BIT_0;
+ ret = qla25xx_init_req_que(base_vha, req);
+ if (ret != QLA_SUCCESS)
+ ql_dbg(ql_dbg_init, base_vha, 0x0101,
+ "%s Req que: %d init failed.\n",
+ __func__, req->id);
+ else
+ ql_dbg(ql_dbg_init, base_vha, 0x0102,
+ "%s Req que: %d inited.\n",
+ __func__, req->id);
+ }
+ }
+ return ret;
+}
+
+/*
+* qla2x00_reset_adapter
+* Reset adapter.
+*
+* Input:
+* ha = adapter block pointer.
+*/
+void
+qla2x00_reset_adapter(scsi_qla_host_t *vha)
+{
+ unsigned long flags = 0;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ vha->flags.online = 0;
+ ha->isp_ops->disable_intrs(ha);
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ WRT_REG_WORD(&reg->hccr, HCCR_RESET_RISC);
+ RD_REG_WORD(&reg->hccr); /* PCI Posting. */
+ WRT_REG_WORD(&reg->hccr, HCCR_RELEASE_RISC);
+ RD_REG_WORD(&reg->hccr); /* PCI Posting. */
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+void
+qla24xx_reset_adapter(scsi_qla_host_t *vha)
+{
+ unsigned long flags = 0;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+ if (IS_QLA82XX(ha))
+ return;
+
+ vha->flags.online = 0;
+ ha->isp_ops->disable_intrs(ha);
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ WRT_REG_DWORD(&reg->hccr, HCCRX_SET_RISC_RESET);
+ RD_REG_DWORD(&reg->hccr);
+ WRT_REG_DWORD(&reg->hccr, HCCRX_REL_RISC_PAUSE);
+ RD_REG_DWORD(&reg->hccr);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ if (IS_NOPOLLING_TYPE(ha))
+ ha->isp_ops->enable_intrs(ha);
+}
+
+/* On sparc systems, obtain port and node WWN from firmware
+ * properties.
+ */
+static void qla24xx_nvram_wwn_from_ofw(scsi_qla_host_t *vha,
+ struct nvram_24xx *nv)
+{
+#ifdef CONFIG_SPARC
+ struct qla_hw_data *ha = vha->hw;
+ struct pci_dev *pdev = ha->pdev;
+ struct device_node *dp = pci_device_to_OF_node(pdev);
+ const u8 *val;
+ int len;
+
+ val = of_get_property(dp, "port-wwn", &len);
+ if (val && len >= WWN_SIZE)
+ memcpy(nv->port_name, val, WWN_SIZE);
+
+ val = of_get_property(dp, "node-wwn", &len);
+ if (val && len >= WWN_SIZE)
+ memcpy(nv->node_name, val, WWN_SIZE);
+#endif
+}
+
+int
+qla24xx_nvram_config(scsi_qla_host_t *vha)
+{
+ int rval;
+ struct init_cb_24xx *icb;
+ struct nvram_24xx *nv;
+ uint32_t *dptr;
+ uint8_t *dptr1, *dptr2;
+ uint32_t chksum;
+ uint16_t cnt;
+ struct qla_hw_data *ha = vha->hw;
+
+ rval = QLA_SUCCESS;
+ icb = (struct init_cb_24xx *)ha->init_cb;
+ nv = ha->nvram;
+
+ /* Determine NVRAM starting address. */
+ if (ha->flags.port0) {
+ ha->nvram_base = FA_NVRAM_FUNC0_ADDR;
+ ha->vpd_base = FA_NVRAM_VPD0_ADDR;
+ } else {
+ ha->nvram_base = FA_NVRAM_FUNC1_ADDR;
+ ha->vpd_base = FA_NVRAM_VPD1_ADDR;
+ }
+ ha->nvram_size = sizeof(struct nvram_24xx);
+ ha->vpd_size = FA_NVRAM_VPD_SIZE;
+ if (IS_QLA82XX(ha))
+ ha->vpd_size = FA_VPD_SIZE_82XX;
+
+ /* Get VPD data into cache */
+ ha->vpd = ha->nvram + VPD_OFFSET;
+ ha->isp_ops->read_nvram(vha, (uint8_t *)ha->vpd,
+ ha->nvram_base - FA_NVRAM_FUNC0_ADDR, FA_NVRAM_VPD_SIZE * 4);
+
+ /* Get NVRAM data into cache and calculate checksum. */
+ dptr = (uint32_t *)nv;
+ ha->isp_ops->read_nvram(vha, (uint8_t *)dptr, ha->nvram_base,
+ ha->nvram_size);
+ for (cnt = 0, chksum = 0; cnt < ha->nvram_size >> 2; cnt++)
+ chksum += le32_to_cpu(*dptr++);
+
+ ql_dbg(ql_dbg_init + ql_dbg_buffer, vha, 0x006a,
+ "Contents of NVRAM\n");
+ ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x010d,
+ (uint8_t *)nv, ha->nvram_size);
+
+ /* Bad NVRAM data, set defaults parameters. */
+ if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' || nv->id[2] != 'P'
+ || nv->id[3] != ' ' ||
+ nv->nvram_version < __constant_cpu_to_le16(ICB_VERSION)) {
+ /* Reset NVRAM data. */
+ ql_log(ql_log_warn, vha, 0x006b,
+ "Inconisistent NVRAM detected: checksum=0x%x id=%c "
+ "version=0x%x.\n", chksum, nv->id[0], nv->nvram_version);
+ ql_log(ql_log_warn, vha, 0x006c,
+ "Falling back to functioning (yet invalid -- WWPN) "
+ "defaults.\n");
+
+ /*
+ * Set default initialization control block.
+ */
+ memset(nv, 0, ha->nvram_size);
+ nv->nvram_version = __constant_cpu_to_le16(ICB_VERSION);
+ nv->version = __constant_cpu_to_le16(ICB_VERSION);
+ nv->frame_payload_size = __constant_cpu_to_le16(2048);
+ nv->execution_throttle = __constant_cpu_to_le16(0xFFFF);
+ nv->exchange_count = __constant_cpu_to_le16(0);
+ nv->hard_address = __constant_cpu_to_le16(124);
+ nv->port_name[0] = 0x21;
+ nv->port_name[1] = 0x00 + ha->port_no;
+ nv->port_name[2] = 0x00;
+ nv->port_name[3] = 0xe0;
+ nv->port_name[4] = 0x8b;
+ nv->port_name[5] = 0x1c;
+ nv->port_name[6] = 0x55;
+ nv->port_name[7] = 0x86;
+ nv->node_name[0] = 0x20;
+ nv->node_name[1] = 0x00;
+ nv->node_name[2] = 0x00;
+ nv->node_name[3] = 0xe0;
+ nv->node_name[4] = 0x8b;
+ nv->node_name[5] = 0x1c;
+ nv->node_name[6] = 0x55;
+ nv->node_name[7] = 0x86;
+ qla24xx_nvram_wwn_from_ofw(vha, nv);
+ nv->login_retry_count = __constant_cpu_to_le16(8);
+ nv->interrupt_delay_timer = __constant_cpu_to_le16(0);
+ nv->login_timeout = __constant_cpu_to_le16(0);
+ nv->firmware_options_1 =
+ __constant_cpu_to_le32(BIT_14|BIT_13|BIT_2|BIT_1);
+ nv->firmware_options_2 = __constant_cpu_to_le32(2 << 4);
+ nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_12);
+ nv->firmware_options_3 = __constant_cpu_to_le32(2 << 13);
+ nv->host_p = __constant_cpu_to_le32(BIT_11|BIT_10);
+ nv->efi_parameters = __constant_cpu_to_le32(0);
+ nv->reset_delay = 5;
+ nv->max_luns_per_target = __constant_cpu_to_le16(128);
+ nv->port_down_retry_count = __constant_cpu_to_le16(30);
+ nv->link_down_timeout = __constant_cpu_to_le16(30);
+
+ rval = 1;
+ }
+
+ /* Reset Initialization control block */
+ memset(icb, 0, ha->init_cb_size);
+
+ /* Copy 1st segment. */
+ dptr1 = (uint8_t *)icb;
+ dptr2 = (uint8_t *)&nv->version;
+ cnt = (uint8_t *)&icb->response_q_inpointer - (uint8_t *)&icb->version;
+ while (cnt--)
+ *dptr1++ = *dptr2++;
+
+ icb->login_retry_count = nv->login_retry_count;
+ icb->link_down_on_nos = nv->link_down_on_nos;
+
+ /* Copy 2nd segment. */
+ dptr1 = (uint8_t *)&icb->interrupt_delay_timer;
+ dptr2 = (uint8_t *)&nv->interrupt_delay_timer;
+ cnt = (uint8_t *)&icb->reserved_3 -
+ (uint8_t *)&icb->interrupt_delay_timer;
+ while (cnt--)
+ *dptr1++ = *dptr2++;
+
+ /*
+ * Setup driver NVRAM options.
+ */
+ qla2x00_set_model_info(vha, nv->model_name, sizeof(nv->model_name),
+ "QLA2462");
+
+ /* Use alternate WWN? */
+ if (nv->host_p & __constant_cpu_to_le32(BIT_15)) {
+ memcpy(icb->node_name, nv->alternate_node_name, WWN_SIZE);
+ memcpy(icb->port_name, nv->alternate_port_name, WWN_SIZE);
+ }
+
+ /* Prepare nodename */
+ if ((icb->firmware_options_1 & __constant_cpu_to_le32(BIT_14)) == 0) {
+ /*
+ * Firmware will apply the following mask if the nodename was
+ * not provided.
+ */
+ memcpy(icb->node_name, icb->port_name, WWN_SIZE);
+ icb->node_name[0] &= 0xF0;
+ }
+
+ /* Set host adapter parameters. */
+ ha->flags.disable_risc_code_load = 0;
+ ha->flags.enable_lip_reset = 0;
+ ha->flags.enable_lip_full_login =
+ le32_to_cpu(nv->host_p) & BIT_10 ? 1: 0;
+ ha->flags.enable_target_reset =
+ le32_to_cpu(nv->host_p) & BIT_11 ? 1: 0;
+ ha->flags.enable_led_scheme = 0;
+ ha->flags.disable_serdes = le32_to_cpu(nv->host_p) & BIT_5 ? 1: 0;
+
+ ha->operating_mode = (le32_to_cpu(icb->firmware_options_2) &
+ (BIT_6 | BIT_5 | BIT_4)) >> 4;
+
+ memcpy(ha->fw_seriallink_options24, nv->seriallink_options,
+ sizeof(ha->fw_seriallink_options24));
+
+ /* save HBA serial number */
+ ha->serial0 = icb->port_name[5];
+ ha->serial1 = icb->port_name[6];
+ ha->serial2 = icb->port_name[7];
+ memcpy(vha->node_name, icb->node_name, WWN_SIZE);
+ memcpy(vha->port_name, icb->port_name, WWN_SIZE);
+
+ icb->execution_throttle = __constant_cpu_to_le16(0xFFFF);
+
+ ha->retry_count = le16_to_cpu(nv->login_retry_count);
+
+ /* Set minimum login_timeout to 4 seconds. */
+ if (le16_to_cpu(nv->login_timeout) < ql2xlogintimeout)
+ nv->login_timeout = cpu_to_le16(ql2xlogintimeout);
+ if (le16_to_cpu(nv->login_timeout) < 4)
+ nv->login_timeout = __constant_cpu_to_le16(4);
+ ha->login_timeout = le16_to_cpu(nv->login_timeout);
+ icb->login_timeout = nv->login_timeout;
+
+ /* Set minimum RATOV to 100 tenths of a second. */
+ ha->r_a_tov = 100;
+
+ ha->loop_reset_delay = nv->reset_delay;
+
+ /* Link Down Timeout = 0:
+ *
+ * When Port Down timer expires we will start returning
+ * I/O's to OS with "DID_NO_CONNECT".
+ *
+ * Link Down Timeout != 0:
+ *
+ * The driver waits for the link to come up after link down
+ * before returning I/Os to OS with "DID_NO_CONNECT".
+ */
+ if (le16_to_cpu(nv->link_down_timeout) == 0) {
+ ha->loop_down_abort_time =
+ (LOOP_DOWN_TIME - LOOP_DOWN_TIMEOUT);
+ } else {
+ ha->link_down_timeout = le16_to_cpu(nv->link_down_timeout);
+ ha->loop_down_abort_time =
+ (LOOP_DOWN_TIME - ha->link_down_timeout);
+ }
+
+ /* Need enough time to try and get the port back. */
+ ha->port_down_retry_count = le16_to_cpu(nv->port_down_retry_count);
+ if (qlport_down_retry)
+ ha->port_down_retry_count = qlport_down_retry;
+
+ /* Set login_retry_count */
+ ha->login_retry_count = le16_to_cpu(nv->login_retry_count);
+ if (ha->port_down_retry_count ==
+ le16_to_cpu(nv->port_down_retry_count) &&
+ ha->port_down_retry_count > 3)
+ ha->login_retry_count = ha->port_down_retry_count;
+ else if (ha->port_down_retry_count > (int)ha->login_retry_count)
+ ha->login_retry_count = ha->port_down_retry_count;
+ if (ql2xloginretrycount)
+ ha->login_retry_count = ql2xloginretrycount;
+
+ /* Enable ZIO. */
+ if (!vha->flags.init_done) {
+ ha->zio_mode = le32_to_cpu(icb->firmware_options_2) &
+ (BIT_3 | BIT_2 | BIT_1 | BIT_0);
+ ha->zio_timer = le16_to_cpu(icb->interrupt_delay_timer) ?
+ le16_to_cpu(icb->interrupt_delay_timer): 2;
+ }
+ icb->firmware_options_2 &= __constant_cpu_to_le32(
+ ~(BIT_3 | BIT_2 | BIT_1 | BIT_0));
+ vha->flags.process_response_queue = 0;
+ if (ha->zio_mode != QLA_ZIO_DISABLED) {
+ ha->zio_mode = QLA_ZIO_MODE_6;
+
+ ql_log(ql_log_info, vha, 0x006f,
+ "ZIO mode %d enabled; timer delay (%d us).\n",
+ ha->zio_mode, ha->zio_timer * 100);
+
+ icb->firmware_options_2 |= cpu_to_le32(
+ (uint32_t)ha->zio_mode);
+ icb->interrupt_delay_timer = cpu_to_le16(ha->zio_timer);
+ vha->flags.process_response_queue = 1;
+ }
+
+ if (rval) {
+ ql_log(ql_log_warn, vha, 0x0070,
+ "NVRAM configuration failed.\n");
+ }
+ return (rval);
+}
+
+static int
+qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr,
+ uint32_t faddr)
+{
+ int rval = QLA_SUCCESS;
+ int segments, fragment;
+ uint32_t *dcode, dlen;
+ uint32_t risc_addr;
+ uint32_t risc_size;
+ uint32_t i;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+
+ ql_dbg(ql_dbg_init, vha, 0x008b,
+ "FW: Loading firmware from flash (%x).\n", faddr);
+
+ rval = QLA_SUCCESS;
+
+ segments = FA_RISC_CODE_SEGMENTS;
+ dcode = (uint32_t *)req->ring;
+ *srisc_addr = 0;
+
+ /* Validate firmware image by checking version. */
+ qla24xx_read_flash_data(vha, dcode, faddr + 4, 4);
+ for (i = 0; i < 4; i++)
+ dcode[i] = be32_to_cpu(dcode[i]);
+ if ((dcode[0] == 0xffffffff && dcode[1] == 0xffffffff &&
+ dcode[2] == 0xffffffff && dcode[3] == 0xffffffff) ||
+ (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 &&
+ dcode[3] == 0)) {
+ ql_log(ql_log_fatal, vha, 0x008c,
+ "Unable to verify the integrity of flash firmware "
+ "image.\n");
+ ql_log(ql_log_fatal, vha, 0x008d,
+ "Firmware data: %08x %08x %08x %08x.\n",
+ dcode[0], dcode[1], dcode[2], dcode[3]);
+
+ return QLA_FUNCTION_FAILED;
+ }
+
+ while (segments && rval == QLA_SUCCESS) {
+ /* Read segment's load information. */
+ qla24xx_read_flash_data(vha, dcode, faddr, 4);
+
+ risc_addr = be32_to_cpu(dcode[2]);
+ *srisc_addr = *srisc_addr == 0 ? risc_addr : *srisc_addr;
+ risc_size = be32_to_cpu(dcode[3]);
+
+ fragment = 0;
+ while (risc_size > 0 && rval == QLA_SUCCESS) {
+ dlen = (uint32_t)(ha->fw_transfer_size >> 2);
+ if (dlen > risc_size)
+ dlen = risc_size;
+
+ ql_dbg(ql_dbg_init, vha, 0x008e,
+ "Loading risc segment@ risc addr %x "
+ "number of dwords 0x%x offset 0x%x.\n",
+ risc_addr, dlen, faddr);
+
+ qla24xx_read_flash_data(vha, dcode, faddr, dlen);
+ for (i = 0; i < dlen; i++)
+ dcode[i] = swab32(dcode[i]);
+
+ rval = qla2x00_load_ram(vha, req->dma, risc_addr,
+ dlen);
+ if (rval) {
+ ql_log(ql_log_fatal, vha, 0x008f,
+ "Failed to load segment %d of firmware.\n",
+ fragment);
+ break;
+ }
+
+ faddr += dlen;
+ risc_addr += dlen;
+ risc_size -= dlen;
+ fragment++;
+ }
+
+ /* Next segment. */
+ segments--;
+ }
+
+ return rval;
+}
+
+#define QLA_FW_URL "ftp://ftp.qlogic.com/outgoing/linux/firmware/"
+
+int
+qla2x00_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
+{
+ int rval;
+ int i, fragment;
+ uint16_t *wcode, *fwcode;
+ uint32_t risc_addr, risc_size, fwclen, wlen, *seg;
+ struct fw_blob *blob;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+
+ /* Load firmware blob. */
+ blob = qla2x00_request_firmware(vha);
+ if (!blob) {
+ ql_log(ql_log_info, vha, 0x0083,
+ "Fimware image unavailable.\n");
+ ql_log(ql_log_info, vha, 0x0084,
+ "Firmware images can be retrieved from: "QLA_FW_URL ".\n");
+ return QLA_FUNCTION_FAILED;
+ }
+
+ rval = QLA_SUCCESS;
+
+ wcode = (uint16_t *)req->ring;
+ *srisc_addr = 0;
+ fwcode = (uint16_t *)blob->fw->data;
+ fwclen = 0;
+
+ /* Validate firmware image by checking version. */
+ if (blob->fw->size < 8 * sizeof(uint16_t)) {
+ ql_log(ql_log_fatal, vha, 0x0085,
+ "Unable to verify integrity of firmware image (%Zd).\n",
+ blob->fw->size);
+ goto fail_fw_integrity;
+ }
+ for (i = 0; i < 4; i++)
+ wcode[i] = be16_to_cpu(fwcode[i + 4]);
+ if ((wcode[0] == 0xffff && wcode[1] == 0xffff && wcode[2] == 0xffff &&
+ wcode[3] == 0xffff) || (wcode[0] == 0 && wcode[1] == 0 &&
+ wcode[2] == 0 && wcode[3] == 0)) {
+ ql_log(ql_log_fatal, vha, 0x0086,
+ "Unable to verify integrity of firmware image.\n");
+ ql_log(ql_log_fatal, vha, 0x0087,
+ "Firmware data: %04x %04x %04x %04x.\n",
+ wcode[0], wcode[1], wcode[2], wcode[3]);
+ goto fail_fw_integrity;
+ }
+
+ seg = blob->segs;
+ while (*seg && rval == QLA_SUCCESS) {
+ risc_addr = *seg;
+ *srisc_addr = *srisc_addr == 0 ? *seg : *srisc_addr;
+ risc_size = be16_to_cpu(fwcode[3]);
+
+ /* Validate firmware image size. */
+ fwclen += risc_size * sizeof(uint16_t);
+ if (blob->fw->size < fwclen) {
+ ql_log(ql_log_fatal, vha, 0x0088,
+ "Unable to verify integrity of firmware image "
+ "(%Zd).\n", blob->fw->size);
+ goto fail_fw_integrity;
+ }
+
+ fragment = 0;
+ while (risc_size > 0 && rval == QLA_SUCCESS) {
+ wlen = (uint16_t)(ha->fw_transfer_size >> 1);
+ if (wlen > risc_size)
+ wlen = risc_size;
+ ql_dbg(ql_dbg_init, vha, 0x0089,
+ "Loading risc segment@ risc addr %x number of "
+ "words 0x%x.\n", risc_addr, wlen);
+
+ for (i = 0; i < wlen; i++)
+ wcode[i] = swab16(fwcode[i]);
+
+ rval = qla2x00_load_ram(vha, req->dma, risc_addr,
+ wlen);
+ if (rval) {
+ ql_log(ql_log_fatal, vha, 0x008a,
+ "Failed to load segment %d of firmware.\n",
+ fragment);
+ break;
+ }
+
+ fwcode += wlen;
+ risc_addr += wlen;
+ risc_size -= wlen;
+ fragment++;
+ }
+
+ /* Next segment. */
+ seg++;
+ }
+ return rval;
+
+fail_fw_integrity:
+ return QLA_FUNCTION_FAILED;
+}
+
+static int
+qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr)
+{
+ int rval;
+ int segments, fragment;
+ uint32_t *dcode, dlen;
+ uint32_t risc_addr;
+ uint32_t risc_size;
+ uint32_t i;
+ struct fw_blob *blob;
+ uint32_t *fwcode, fwclen;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+
+ /* Load firmware blob. */
+ blob = qla2x00_request_firmware(vha);
+ if (!blob) {
+ ql_log(ql_log_warn, vha, 0x0090,
+ "Fimware image unavailable.\n");
+ ql_log(ql_log_warn, vha, 0x0091,
+ "Firmware images can be retrieved from: "
+ QLA_FW_URL ".\n");
+
+ return QLA_FUNCTION_FAILED;
+ }
+
+ ql_dbg(ql_dbg_init, vha, 0x0092,
+ "FW: Loading via request-firmware.\n");
+
+ rval = QLA_SUCCESS;
+
+ segments = FA_RISC_CODE_SEGMENTS;
+ dcode = (uint32_t *)req->ring;
+ *srisc_addr = 0;
+ fwcode = (uint32_t *)blob->fw->data;
+ fwclen = 0;
+
+ /* Validate firmware image by checking version. */
+ if (blob->fw->size < 8 * sizeof(uint32_t)) {
+ ql_log(ql_log_fatal, vha, 0x0093,
+ "Unable to verify integrity of firmware image (%Zd).\n",
+ blob->fw->size);
+ goto fail_fw_integrity;
+ }
+ for (i = 0; i < 4; i++)
+ dcode[i] = be32_to_cpu(fwcode[i + 4]);
+ if ((dcode[0] == 0xffffffff && dcode[1] == 0xffffffff &&
+ dcode[2] == 0xffffffff && dcode[3] == 0xffffffff) ||
+ (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 &&
+ dcode[3] == 0)) {
+ ql_log(ql_log_fatal, vha, 0x0094,
+ "Unable to verify integrity of firmware image (%Zd).\n",
+ blob->fw->size);
+ ql_log(ql_log_fatal, vha, 0x0095,
+ "Firmware data: %08x %08x %08x %08x.\n",
+ dcode[0], dcode[1], dcode[2], dcode[3]);
+ goto fail_fw_integrity;
+ }
+
+ while (segments && rval == QLA_SUCCESS) {
+ risc_addr = be32_to_cpu(fwcode[2]);
+ *srisc_addr = *srisc_addr == 0 ? risc_addr : *srisc_addr;
+ risc_size = be32_to_cpu(fwcode[3]);
+
+ /* Validate firmware image size. */
+ fwclen += risc_size * sizeof(uint32_t);
+ if (blob->fw->size < fwclen) {
+ ql_log(ql_log_fatal, vha, 0x0096,
+ "Unable to verify integrity of firmware image "
+ "(%Zd).\n", blob->fw->size);
+
+ goto fail_fw_integrity;
+ }
+
+ fragment = 0;
+ while (risc_size > 0 && rval == QLA_SUCCESS) {
+ dlen = (uint32_t)(ha->fw_transfer_size >> 2);
+ if (dlen > risc_size)
+ dlen = risc_size;
+
+ ql_dbg(ql_dbg_init, vha, 0x0097,
+ "Loading risc segment@ risc addr %x "
+ "number of dwords 0x%x.\n", risc_addr, dlen);
+
+ for (i = 0; i < dlen; i++)
+ dcode[i] = swab32(fwcode[i]);
+
+ rval = qla2x00_load_ram(vha, req->dma, risc_addr,
+ dlen);
+ if (rval) {
+ ql_log(ql_log_fatal, vha, 0x0098,
+ "Failed to load segment %d of firmware.\n",
+ fragment);
+ break;
+ }
+
+ fwcode += dlen;
+ risc_addr += dlen;
+ risc_size -= dlen;
+ fragment++;
+ }
+
+ /* Next segment. */
+ segments--;
+ }
+ return rval;
+
+fail_fw_integrity:
+ return QLA_FUNCTION_FAILED;
+}
+
+int
+qla24xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
+{
+ int rval;
+
+ if (ql2xfwloadbin == 1)
+ return qla81xx_load_risc(vha, srisc_addr);
+
+ /*
+ * FW Load priority:
+ * 1) Firmware via request-firmware interface (.bin file).
+ * 2) Firmware residing in flash.
+ */
+ rval = qla24xx_load_risc_blob(vha, srisc_addr);
+ if (rval == QLA_SUCCESS)
+ return rval;
+
+ return qla24xx_load_risc_flash(vha, srisc_addr,
+ vha->hw->flt_region_fw);
+}
+
+int
+qla81xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (ql2xfwloadbin == 2)
+ goto try_blob_fw;
+
+ /*
+ * FW Load priority:
+ * 1) Firmware residing in flash.
+ * 2) Firmware via request-firmware interface (.bin file).
+ * 3) Golden-Firmware residing in flash -- limited operation.
+ */
+ rval = qla24xx_load_risc_flash(vha, srisc_addr, ha->flt_region_fw);
+ if (rval == QLA_SUCCESS)
+ return rval;
+
+try_blob_fw:
+ rval = qla24xx_load_risc_blob(vha, srisc_addr);
+ if (rval == QLA_SUCCESS || !ha->flt_region_gold_fw)
+ return rval;
+
+ ql_log(ql_log_info, vha, 0x0099,
+ "Attempting to fallback to golden firmware.\n");
+ rval = qla24xx_load_risc_flash(vha, srisc_addr, ha->flt_region_gold_fw);
+ if (rval != QLA_SUCCESS)
+ return rval;
+
+ ql_log(ql_log_info, vha, 0x009a, "Update operational firmware.\n");
+ ha->flags.running_gold_fw = 1;
+ return rval;
+}
+
+void
+qla2x00_try_to_stop_firmware(scsi_qla_host_t *vha)
+{
+ int ret, retries;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (ha->flags.pci_channel_io_perm_failure)
+ return;
+ if (!IS_FWI2_CAPABLE(ha))
+ return;
+ if (!ha->fw_major_version)
+ return;
+
+ ret = qla2x00_stop_firmware(vha);
+ for (retries = 5; ret != QLA_SUCCESS && ret != QLA_FUNCTION_TIMEOUT &&
+ ret != QLA_INVALID_COMMAND && retries ; retries--) {
+ ha->isp_ops->reset_chip(vha);
+ if (ha->isp_ops->chip_diag(vha) != QLA_SUCCESS)
+ continue;
+ if (qla2x00_setup_chip(vha) != QLA_SUCCESS)
+ continue;
+ ql_log(ql_log_info, vha, 0x8015,
+ "Attempting retry of stop-firmware command.\n");
+ ret = qla2x00_stop_firmware(vha);
+ }
+}
+
+int
+qla24xx_configure_vhba(scsi_qla_host_t *vha)
+{
+ int rval = QLA_SUCCESS;
+ int rval2;
+ uint16_t mb[MAILBOX_REGISTER_COUNT];
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+ struct req_que *req;
+ struct rsp_que *rsp;
+
+ if (!vha->vp_idx)
+ return -EINVAL;
+
+ rval = qla2x00_fw_ready(base_vha);
+ if (ha->flags.cpu_affinity_enabled)
+ req = ha->req_q_map[0];
+ else
+ req = vha->req;
+ rsp = req->rsp;
+
+ if (rval == QLA_SUCCESS) {
+ clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
+ qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL);
+ }
+
+ vha->flags.management_server_logged_in = 0;
+
+ /* Login to SNS first */
+ rval2 = ha->isp_ops->fabric_login(vha, NPH_SNS, 0xff, 0xff, 0xfc, mb,
+ BIT_1);
+ if (rval2 != QLA_SUCCESS || mb[0] != MBS_COMMAND_COMPLETE) {
+ if (rval2 == QLA_MEMORY_ALLOC_FAILED)
+ ql_dbg(ql_dbg_init, vha, 0x0120,
+ "Failed SNS login: loop_id=%x, rval2=%d\n",
+ NPH_SNS, rval2);
+ else
+ ql_dbg(ql_dbg_init, vha, 0x0103,
+ "Failed SNS login: loop_id=%x mb[0]=%x mb[1]=%x "
+ "mb[2]=%x mb[6]=%x mb[7]=%x.\n",
+ NPH_SNS, mb[0], mb[1], mb[2], mb[6], mb[7]);
+ return (QLA_FUNCTION_FAILED);
+ }
+
+ atomic_set(&vha->loop_down_timer, 0);
+ atomic_set(&vha->loop_state, LOOP_UP);
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
+ rval = qla2x00_loop_resync(base_vha);
+
+ return rval;
+}
+
+/* 84XX Support **************************************************************/
+
+static LIST_HEAD(qla_cs84xx_list);
+static DEFINE_MUTEX(qla_cs84xx_mutex);
+
+static struct qla_chip_state_84xx *
+qla84xx_get_chip(struct scsi_qla_host *vha)
+{
+ struct qla_chip_state_84xx *cs84xx;
+ struct qla_hw_data *ha = vha->hw;
+
+ mutex_lock(&qla_cs84xx_mutex);
+
+ /* Find any shared 84xx chip. */
+ list_for_each_entry(cs84xx, &qla_cs84xx_list, list) {
+ if (cs84xx->bus == ha->pdev->bus) {
+ kref_get(&cs84xx->kref);
+ goto done;
+ }
+ }
+
+ cs84xx = kzalloc(sizeof(*cs84xx), GFP_KERNEL);
+ if (!cs84xx)
+ goto done;
+
+ kref_init(&cs84xx->kref);
+ spin_lock_init(&cs84xx->access_lock);
+ mutex_init(&cs84xx->fw_update_mutex);
+ cs84xx->bus = ha->pdev->bus;
+
+ list_add_tail(&cs84xx->list, &qla_cs84xx_list);
+done:
+ mutex_unlock(&qla_cs84xx_mutex);
+ return cs84xx;
+}
+
+static void
+__qla84xx_chip_release(struct kref *kref)
+{
+ struct qla_chip_state_84xx *cs84xx =
+ container_of(kref, struct qla_chip_state_84xx, kref);
+
+ mutex_lock(&qla_cs84xx_mutex);
+ list_del(&cs84xx->list);
+ mutex_unlock(&qla_cs84xx_mutex);
+ kfree(cs84xx);
+}
+
+void
+qla84xx_put_chip(struct scsi_qla_host *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ if (ha->cs84xx)
+ kref_put(&ha->cs84xx->kref, __qla84xx_chip_release);
+}
+
+static int
+qla84xx_init_chip(scsi_qla_host_t *vha)
+{
+ int rval;
+ uint16_t status[2];
+ struct qla_hw_data *ha = vha->hw;
+
+ mutex_lock(&ha->cs84xx->fw_update_mutex);
+
+ rval = qla84xx_verify_chip(vha, status);
+
+ mutex_unlock(&ha->cs84xx->fw_update_mutex);
+
+ return rval != QLA_SUCCESS || status[0] ? QLA_FUNCTION_FAILED:
+ QLA_SUCCESS;
+}
+
+/* 81XX Support **************************************************************/
+
+int
+qla81xx_nvram_config(scsi_qla_host_t *vha)
+{
+ int rval;
+ struct init_cb_81xx *icb;
+ struct nvram_81xx *nv;
+ uint32_t *dptr;
+ uint8_t *dptr1, *dptr2;
+ uint32_t chksum;
+ uint16_t cnt;
+ struct qla_hw_data *ha = vha->hw;
+
+ rval = QLA_SUCCESS;
+ icb = (struct init_cb_81xx *)ha->init_cb;
+ nv = ha->nvram;
+
+ /* Determine NVRAM starting address. */
+ ha->nvram_size = sizeof(struct nvram_81xx);
+ ha->vpd_size = FA_NVRAM_VPD_SIZE;
+
+ /* Get VPD data into cache */
+ ha->vpd = ha->nvram + VPD_OFFSET;
+ ha->isp_ops->read_optrom(vha, ha->vpd, ha->flt_region_vpd << 2,
+ ha->vpd_size);
+
+ /* Get NVRAM data into cache and calculate checksum. */
+ ha->isp_ops->read_optrom(vha, ha->nvram, ha->flt_region_nvram << 2,
+ ha->nvram_size);
+ dptr = (uint32_t *)nv;
+ for (cnt = 0, chksum = 0; cnt < ha->nvram_size >> 2; cnt++)
+ chksum += le32_to_cpu(*dptr++);
+
+ ql_dbg(ql_dbg_init + ql_dbg_buffer, vha, 0x0111,
+ "Contents of NVRAM:\n");
+ ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0112,
+ (uint8_t *)nv, ha->nvram_size);
+
+ /* Bad NVRAM data, set defaults parameters. */
+ if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' || nv->id[2] != 'P'
+ || nv->id[3] != ' ' ||
+ nv->nvram_version < __constant_cpu_to_le16(ICB_VERSION)) {
+ /* Reset NVRAM data. */
+ ql_log(ql_log_info, vha, 0x0073,
+ "Inconisistent NVRAM detected: checksum=0x%x id=%c "
+ "version=0x%x.\n", chksum, nv->id[0],
+ le16_to_cpu(nv->nvram_version));
+ ql_log(ql_log_info, vha, 0x0074,
+ "Falling back to functioning (yet invalid -- WWPN) "
+ "defaults.\n");
+
+ /*
+ * Set default initialization control block.
+ */
+ memset(nv, 0, ha->nvram_size);
+ nv->nvram_version = __constant_cpu_to_le16(ICB_VERSION);
+ nv->version = __constant_cpu_to_le16(ICB_VERSION);
+ nv->frame_payload_size = __constant_cpu_to_le16(2048);
+ nv->execution_throttle = __constant_cpu_to_le16(0xFFFF);
+ nv->exchange_count = __constant_cpu_to_le16(0);
+ nv->port_name[0] = 0x21;
+ nv->port_name[1] = 0x00 + ha->port_no;
+ nv->port_name[2] = 0x00;
+ nv->port_name[3] = 0xe0;
+ nv->port_name[4] = 0x8b;
+ nv->port_name[5] = 0x1c;
+ nv->port_name[6] = 0x55;
+ nv->port_name[7] = 0x86;
+ nv->node_name[0] = 0x20;
+ nv->node_name[1] = 0x00;
+ nv->node_name[2] = 0x00;
+ nv->node_name[3] = 0xe0;
+ nv->node_name[4] = 0x8b;
+ nv->node_name[5] = 0x1c;
+ nv->node_name[6] = 0x55;
+ nv->node_name[7] = 0x86;
+ nv->login_retry_count = __constant_cpu_to_le16(8);
+ nv->interrupt_delay_timer = __constant_cpu_to_le16(0);
+ nv->login_timeout = __constant_cpu_to_le16(0);
+ nv->firmware_options_1 =
+ __constant_cpu_to_le32(BIT_14|BIT_13|BIT_2|BIT_1);
+ nv->firmware_options_2 = __constant_cpu_to_le32(2 << 4);
+ nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_12);
+ nv->firmware_options_3 = __constant_cpu_to_le32(2 << 13);
+ nv->host_p = __constant_cpu_to_le32(BIT_11|BIT_10);
+ nv->efi_parameters = __constant_cpu_to_le32(0);
+ nv->reset_delay = 5;
+ nv->max_luns_per_target = __constant_cpu_to_le16(128);
+ nv->port_down_retry_count = __constant_cpu_to_le16(30);
+ nv->link_down_timeout = __constant_cpu_to_le16(180);
+ nv->enode_mac[0] = 0x00;
+ nv->enode_mac[1] = 0xC0;
+ nv->enode_mac[2] = 0xDD;
+ nv->enode_mac[3] = 0x04;
+ nv->enode_mac[4] = 0x05;
+ nv->enode_mac[5] = 0x06 + ha->port_no;
+
+ rval = 1;
+ }
+
+ /* Reset Initialization control block */
+ memset(icb, 0, ha->init_cb_size);
+
+ /* Copy 1st segment. */
+ dptr1 = (uint8_t *)icb;
+ dptr2 = (uint8_t *)&nv->version;
+ cnt = (uint8_t *)&icb->response_q_inpointer - (uint8_t *)&icb->version;
+ while (cnt--)
+ *dptr1++ = *dptr2++;
+
+ icb->login_retry_count = nv->login_retry_count;
+
+ /* Copy 2nd segment. */
+ dptr1 = (uint8_t *)&icb->interrupt_delay_timer;
+ dptr2 = (uint8_t *)&nv->interrupt_delay_timer;
+ cnt = (uint8_t *)&icb->reserved_5 -
+ (uint8_t *)&icb->interrupt_delay_timer;
+ while (cnt--)
+ *dptr1++ = *dptr2++;
+
+ memcpy(icb->enode_mac, nv->enode_mac, sizeof(icb->enode_mac));
+ /* Some boards (with valid NVRAMs) still have NULL enode_mac!! */
+ if (!memcmp(icb->enode_mac, "\0\0\0\0\0\0", sizeof(icb->enode_mac))) {
+ icb->enode_mac[0] = 0x00;
+ icb->enode_mac[1] = 0xC0;
+ icb->enode_mac[2] = 0xDD;
+ icb->enode_mac[3] = 0x04;
+ icb->enode_mac[4] = 0x05;
+ icb->enode_mac[5] = 0x06 + ha->port_no;
+ }
+
+ /* Use extended-initialization control block. */
+ memcpy(ha->ex_init_cb, &nv->ex_version, sizeof(*ha->ex_init_cb));
+
+ /*
+ * Setup driver NVRAM options.
+ */
+ qla2x00_set_model_info(vha, nv->model_name, sizeof(nv->model_name),
+ "QLE8XXX");
+
+ /* Use alternate WWN? */
+ if (nv->host_p & __constant_cpu_to_le32(BIT_15)) {
+ memcpy(icb->node_name, nv->alternate_node_name, WWN_SIZE);
+ memcpy(icb->port_name, nv->alternate_port_name, WWN_SIZE);
+ }
+
+ /* Prepare nodename */
+ if ((icb->firmware_options_1 & __constant_cpu_to_le32(BIT_14)) == 0) {
+ /*
+ * Firmware will apply the following mask if the nodename was
+ * not provided.
+ */
+ memcpy(icb->node_name, icb->port_name, WWN_SIZE);
+ icb->node_name[0] &= 0xF0;
+ }
+
+ /* Set host adapter parameters. */
+ ha->flags.disable_risc_code_load = 0;
+ ha->flags.enable_lip_reset = 0;
+ ha->flags.enable_lip_full_login =
+ le32_to_cpu(nv->host_p) & BIT_10 ? 1: 0;
+ ha->flags.enable_target_reset =
+ le32_to_cpu(nv->host_p) & BIT_11 ? 1: 0;
+ ha->flags.enable_led_scheme = 0;
+ ha->flags.disable_serdes = le32_to_cpu(nv->host_p) & BIT_5 ? 1: 0;
+
+ ha->operating_mode = (le32_to_cpu(icb->firmware_options_2) &
+ (BIT_6 | BIT_5 | BIT_4)) >> 4;
+
+ /* save HBA serial number */
+ ha->serial0 = icb->port_name[5];
+ ha->serial1 = icb->port_name[6];
+ ha->serial2 = icb->port_name[7];
+ memcpy(vha->node_name, icb->node_name, WWN_SIZE);
+ memcpy(vha->port_name, icb->port_name, WWN_SIZE);
+
+ icb->execution_throttle = __constant_cpu_to_le16(0xFFFF);
+
+ ha->retry_count = le16_to_cpu(nv->login_retry_count);
+
+ /* Set minimum login_timeout to 4 seconds. */
+ if (le16_to_cpu(nv->login_timeout) < ql2xlogintimeout)
+ nv->login_timeout = cpu_to_le16(ql2xlogintimeout);
+ if (le16_to_cpu(nv->login_timeout) < 4)
+ nv->login_timeout = __constant_cpu_to_le16(4);
+ ha->login_timeout = le16_to_cpu(nv->login_timeout);
+ icb->login_timeout = nv->login_timeout;
+
+ /* Set minimum RATOV to 100 tenths of a second. */
+ ha->r_a_tov = 100;
+
+ ha->loop_reset_delay = nv->reset_delay;
+
+ /* Link Down Timeout = 0:
+ *
+ * When Port Down timer expires we will start returning
+ * I/O's to OS with "DID_NO_CONNECT".
+ *
+ * Link Down Timeout != 0:
+ *
+ * The driver waits for the link to come up after link down
+ * before returning I/Os to OS with "DID_NO_CONNECT".
+ */
+ if (le16_to_cpu(nv->link_down_timeout) == 0) {
+ ha->loop_down_abort_time =
+ (LOOP_DOWN_TIME - LOOP_DOWN_TIMEOUT);
+ } else {
+ ha->link_down_timeout = le16_to_cpu(nv->link_down_timeout);
+ ha->loop_down_abort_time =
+ (LOOP_DOWN_TIME - ha->link_down_timeout);
+ }
+
+ /* Need enough time to try and get the port back. */
+ ha->port_down_retry_count = le16_to_cpu(nv->port_down_retry_count);
+ if (qlport_down_retry)
+ ha->port_down_retry_count = qlport_down_retry;
+
+ /* Set login_retry_count */
+ ha->login_retry_count = le16_to_cpu(nv->login_retry_count);
+ if (ha->port_down_retry_count ==
+ le16_to_cpu(nv->port_down_retry_count) &&
+ ha->port_down_retry_count > 3)
+ ha->login_retry_count = ha->port_down_retry_count;
+ else if (ha->port_down_retry_count > (int)ha->login_retry_count)
+ ha->login_retry_count = ha->port_down_retry_count;
+ if (ql2xloginretrycount)
+ ha->login_retry_count = ql2xloginretrycount;
+
+ /* if not running MSI-X we need handshaking on interrupts */
+ if (!vha->hw->flags.msix_enabled && IS_QLA83XX(ha))
+ icb->firmware_options_2 |= __constant_cpu_to_le32(BIT_22);
+
+ /* Enable ZIO. */
+ if (!vha->flags.init_done) {
+ ha->zio_mode = le32_to_cpu(icb->firmware_options_2) &
+ (BIT_3 | BIT_2 | BIT_1 | BIT_0);
+ ha->zio_timer = le16_to_cpu(icb->interrupt_delay_timer) ?
+ le16_to_cpu(icb->interrupt_delay_timer): 2;
+ }
+ icb->firmware_options_2 &= __constant_cpu_to_le32(
+ ~(BIT_3 | BIT_2 | BIT_1 | BIT_0));
+ vha->flags.process_response_queue = 0;
+ if (ha->zio_mode != QLA_ZIO_DISABLED) {
+ ha->zio_mode = QLA_ZIO_MODE_6;
+
+ ql_log(ql_log_info, vha, 0x0075,
+ "ZIO mode %d enabled; timer delay (%d us).\n",
+ ha->zio_mode,
+ ha->zio_timer * 100);
+
+ icb->firmware_options_2 |= cpu_to_le32(
+ (uint32_t)ha->zio_mode);
+ icb->interrupt_delay_timer = cpu_to_le16(ha->zio_timer);
+ vha->flags.process_response_queue = 1;
+ }
+
+ if (rval) {
+ ql_log(ql_log_warn, vha, 0x0076,
+ "NVRAM configuration failed.\n");
+ }
+ return (rval);
+}
+
+int
+qla82xx_restart_isp(scsi_qla_host_t *vha)
+{
+ int status, rval;
+ uint32_t wait_time;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
+ struct scsi_qla_host *vp;
+ unsigned long flags;
+
+ status = qla2x00_init_rings(vha);
+ if (!status) {
+ clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
+ ha->flags.chip_reset_done = 1;
+
+ status = qla2x00_fw_ready(vha);
+ if (!status) {
+ ql_log(ql_log_info, vha, 0x803c,
+ "Start configure loop, status =%d.\n", status);
+
+ /* Issue a marker after FW becomes ready. */
+ qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL);
+
+ vha->flags.online = 1;
+ /* Wait at most MAX_TARGET RSCNs for a stable link. */
+ wait_time = 256;
+ do {
+ clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ qla2x00_configure_loop(vha);
+ wait_time--;
+ } while (!atomic_read(&vha->loop_down_timer) &&
+ !(test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) &&
+ wait_time &&
+ (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)));
+ }
+
+ /* if no cable then assume it's good */
+ if ((vha->device_flags & DFLG_NO_CABLE))
+ status = 0;
+
+ ql_log(ql_log_info, vha, 0x8000,
+ "Configure loop done, status = 0x%x.\n", status);
+ }
+
+ if (!status) {
+ clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
+
+ if (!atomic_read(&vha->loop_down_timer)) {
+ /*
+ * Issue marker command only when we are going
+ * to start the I/O .
+ */
+ vha->marker_needed = 1;
+ }
+
+ vha->flags.online = 1;
+
+ ha->isp_ops->enable_intrs(ha);
+
+ ha->isp_abort_cnt = 0;
+ clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
+
+ /* Update the firmware version */
+ status = qla82xx_check_md_needed(vha);
+
+ if (ha->fce) {
+ ha->flags.fce_enabled = 1;
+ memset(ha->fce, 0,
+ fce_calc_size(ha->fce_bufs));
+ rval = qla2x00_enable_fce_trace(vha,
+ ha->fce_dma, ha->fce_bufs, ha->fce_mb,
+ &ha->fce_bufs);
+ if (rval) {
+ ql_log(ql_log_warn, vha, 0x8001,
+ "Unable to reinitialize FCE (%d).\n",
+ rval);
+ ha->flags.fce_enabled = 0;
+ }
+ }
+
+ if (ha->eft) {
+ memset(ha->eft, 0, EFT_SIZE);
+ rval = qla2x00_enable_eft_trace(vha,
+ ha->eft_dma, EFT_NUM_BUFFERS);
+ if (rval) {
+ ql_log(ql_log_warn, vha, 0x8010,
+ "Unable to reinitialize EFT (%d).\n",
+ rval);
+ }
+ }
+ }
+
+ if (!status) {
+ ql_dbg(ql_dbg_taskm, vha, 0x8011,
+ "qla82xx_restart_isp succeeded.\n");
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ list_for_each_entry(vp, &ha->vp_list, list) {
+ if (vp->vp_idx) {
+ atomic_inc(&vp->vref_count);
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
+ qla2x00_vp_abort_isp(vp);
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ atomic_dec(&vp->vref_count);
+ }
+ }
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
+ } else {
+ ql_log(ql_log_warn, vha, 0x8016,
+ "qla82xx_restart_isp **** FAILED ****.\n");
+ }
+
+ return status;
+}
+
+void
+qla81xx_update_fw_options(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!ql2xetsenable)
+ return;
+
+ /* Enable ETS Burst. */
+ memset(ha->fw_options, 0, sizeof(ha->fw_options));
+ ha->fw_options[2] |= BIT_9;
+ qla2x00_set_fw_options(vha, ha->fw_options);
+}
+
+/*
+ * qla24xx_get_fcp_prio
+ * Gets the fcp cmd priority value for the logged in port.
+ * Looks for a match of the port descriptors within
+ * each of the fcp prio config entries. If a match is found,
+ * the tag (priority) value is returned.
+ *
+ * Input:
+ * vha = scsi host structure pointer.
+ * fcport = port structure pointer.
+ *
+ * Return:
+ * non-zero (if found)
+ * -1 (if not found)
+ *
+ * Context:
+ * Kernel context
+ */
+static int
+qla24xx_get_fcp_prio(scsi_qla_host_t *vha, fc_port_t *fcport)
+{
+ int i, entries;
+ uint8_t pid_match, wwn_match;
+ int priority;
+ uint32_t pid1, pid2;
+ uint64_t wwn1, wwn2;
+ struct qla_fcp_prio_entry *pri_entry;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!ha->fcp_prio_cfg || !ha->flags.fcp_prio_enabled)
+ return -1;
+
+ priority = -1;
+ entries = ha->fcp_prio_cfg->num_entries;
+ pri_entry = &ha->fcp_prio_cfg->entry[0];
+
+ for (i = 0; i < entries; i++) {
+ pid_match = wwn_match = 0;
+
+ if (!(pri_entry->flags & FCP_PRIO_ENTRY_VALID)) {
+ pri_entry++;
+ continue;
+ }
+
+ /* check source pid for a match */
+ if (pri_entry->flags & FCP_PRIO_ENTRY_SPID_VALID) {
+ pid1 = pri_entry->src_pid & INVALID_PORT_ID;
+ pid2 = vha->d_id.b24 & INVALID_PORT_ID;
+ if (pid1 == INVALID_PORT_ID)
+ pid_match++;
+ else if (pid1 == pid2)
+ pid_match++;
+ }
+
+ /* check destination pid for a match */
+ if (pri_entry->flags & FCP_PRIO_ENTRY_DPID_VALID) {
+ pid1 = pri_entry->dst_pid & INVALID_PORT_ID;
+ pid2 = fcport->d_id.b24 & INVALID_PORT_ID;
+ if (pid1 == INVALID_PORT_ID)
+ pid_match++;
+ else if (pid1 == pid2)
+ pid_match++;
+ }
+
+ /* check source WWN for a match */
+ if (pri_entry->flags & FCP_PRIO_ENTRY_SWWN_VALID) {
+ wwn1 = wwn_to_u64(vha->port_name);
+ wwn2 = wwn_to_u64(pri_entry->src_wwpn);
+ if (wwn2 == (uint64_t)-1)
+ wwn_match++;
+ else if (wwn1 == wwn2)
+ wwn_match++;
+ }
+
+ /* check destination WWN for a match */
+ if (pri_entry->flags & FCP_PRIO_ENTRY_DWWN_VALID) {
+ wwn1 = wwn_to_u64(fcport->port_name);
+ wwn2 = wwn_to_u64(pri_entry->dst_wwpn);
+ if (wwn2 == (uint64_t)-1)
+ wwn_match++;
+ else if (wwn1 == wwn2)
+ wwn_match++;
+ }
+
+ if (pid_match == 2 || wwn_match == 2) {
+ /* Found a matching entry */
+ if (pri_entry->flags & FCP_PRIO_ENTRY_TAG_VALID)
+ priority = pri_entry->tag;
+ break;
+ }
+
+ pri_entry++;
+ }
+
+ return priority;
+}
+
+/*
+ * qla24xx_update_fcport_fcp_prio
+ * Activates fcp priority for the logged in fc port
+ *
+ * Input:
+ * vha = scsi host structure pointer.
+ * fcp = port structure pointer.
+ *
+ * Return:
+ * QLA_SUCCESS or QLA_FUNCTION_FAILED
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla24xx_update_fcport_fcp_prio(scsi_qla_host_t *vha, fc_port_t *fcport)
+{
+ int ret;
+ int priority;
+ uint16_t mb[5];
+
+ if (fcport->port_type != FCT_TARGET ||
+ fcport->loop_id == FC_NO_LOOP_ID)
+ return QLA_FUNCTION_FAILED;
+
+ priority = qla24xx_get_fcp_prio(vha, fcport);
+ if (priority < 0)
+ return QLA_FUNCTION_FAILED;
+
+ if (IS_QLA82XX(vha->hw)) {
+ fcport->fcp_prio = priority & 0xf;
+ return QLA_SUCCESS;
+ }
+
+ ret = qla24xx_set_fcp_prio(vha, fcport->loop_id, priority, mb);
+ if (ret == QLA_SUCCESS) {
+ if (fcport->fcp_prio != priority)
+ ql_dbg(ql_dbg_user, vha, 0x709e,
+ "Updated FCP_CMND priority - value=%d loop_id=%d "
+ "port_id=%02x%02x%02x.\n", priority,
+ fcport->loop_id, fcport->d_id.b.domain,
+ fcport->d_id.b.area, fcport->d_id.b.al_pa);
+ fcport->fcp_prio = priority & 0xf;
+ } else
+ ql_dbg(ql_dbg_user, vha, 0x704f,
+ "Unable to update FCP_CMND priority - ret=0x%x for "
+ "loop_id=%d port_id=%02x%02x%02x.\n", ret, fcport->loop_id,
+ fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa);
+ return ret;
+}
+
+/*
+ * qla24xx_update_all_fcp_prio
+ * Activates fcp priority for all the logged in ports
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Return:
+ * QLA_SUCCESS or QLA_FUNCTION_FAILED
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla24xx_update_all_fcp_prio(scsi_qla_host_t *vha)
+{
+ int ret;
+ fc_port_t *fcport;
+
+ ret = QLA_FUNCTION_FAILED;
+ /* We need to set priority for all logged in ports */
+ list_for_each_entry(fcport, &vha->vp_fcports, list)
+ ret = qla24xx_update_fcport_fcp_prio(vha, fcport);
+
+ return ret;
+}
diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h
new file mode 100644
index 00000000..6e457643
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_inline.h
@@ -0,0 +1,189 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2003-2011 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+
+/*
+ * qla2x00_debounce_register
+ * Debounce register.
+ *
+ * Input:
+ * port = register address.
+ *
+ * Returns:
+ * register value.
+ */
+static __inline__ uint16_t
+qla2x00_debounce_register(volatile uint16_t __iomem *addr)
+{
+ volatile uint16_t first;
+ volatile uint16_t second;
+
+ do {
+ first = RD_REG_WORD(addr);
+ barrier();
+ cpu_relax();
+ second = RD_REG_WORD(addr);
+ } while (first != second);
+
+ return (first);
+}
+
+static inline void
+qla2x00_poll(struct rsp_que *rsp)
+{
+ unsigned long flags;
+ struct qla_hw_data *ha = rsp->hw;
+ local_irq_save(flags);
+ if (IS_QLA82XX(ha))
+ qla82xx_poll(0, rsp);
+ else
+ ha->isp_ops->intr_handler(0, rsp);
+ local_irq_restore(flags);
+}
+
+static inline uint8_t *
+host_to_fcp_swap(uint8_t *fcp, uint32_t bsize)
+{
+ uint32_t *ifcp = (uint32_t *) fcp;
+ uint32_t *ofcp = (uint32_t *) fcp;
+ uint32_t iter = bsize >> 2;
+
+ for (; iter ; iter--)
+ *ofcp++ = swab32(*ifcp++);
+
+ return fcp;
+}
+
+static inline int
+qla2x00_is_reserved_id(scsi_qla_host_t *vha, uint16_t loop_id)
+{
+ struct qla_hw_data *ha = vha->hw;
+ if (IS_FWI2_CAPABLE(ha))
+ return (loop_id > NPH_LAST_HANDLE);
+
+ return ((loop_id > ha->max_loop_id && loop_id < SNS_FIRST_LOOP_ID) ||
+ loop_id == MANAGEMENT_SERVER || loop_id == BROADCAST);
+}
+
+static inline void
+qla2x00_clean_dsd_pool(struct qla_hw_data *ha, srb_t *sp)
+{
+ struct dsd_dma *dsd_ptr, *tdsd_ptr;
+ struct crc_context *ctx;
+
+ ctx = (struct crc_context *)GET_CMD_CTX_SP(sp);
+
+ /* clean up allocated prev pool */
+ list_for_each_entry_safe(dsd_ptr, tdsd_ptr,
+ &ctx->dsd_list, list) {
+ dma_pool_free(ha->dl_dma_pool, dsd_ptr->dsd_addr,
+ dsd_ptr->dsd_list_dma);
+ list_del(&dsd_ptr->list);
+ kfree(dsd_ptr);
+ }
+ INIT_LIST_HEAD(&ctx->dsd_list);
+}
+
+static inline void
+qla2x00_set_fcport_state(fc_port_t *fcport, int state)
+{
+ int old_state;
+
+ old_state = atomic_read(&fcport->state);
+ atomic_set(&fcport->state, state);
+
+ /* Don't print state transitions during initial allocation of fcport */
+ if (old_state && old_state != state) {
+ ql_dbg(ql_dbg_disc, fcport->vha, 0x207d,
+ "FCPort state transitioned from %s to %s - "
+ "portid=%02x%02x%02x.\n",
+ port_state_str[old_state], port_state_str[state],
+ fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa);
+ }
+}
+
+static inline int
+qla2x00_hba_err_chk_enabled(srb_t *sp)
+{
+ /*
+ * Uncomment when corresponding SCSI changes are done.
+ *
+ if (!sp->cmd->prot_chk)
+ return 0;
+ *
+ */
+ switch (scsi_get_prot_op(GET_CMD_SP(sp))) {
+ case SCSI_PROT_READ_STRIP:
+ case SCSI_PROT_WRITE_INSERT:
+ if (ql2xenablehba_err_chk >= 1)
+ return 1;
+ break;
+ case SCSI_PROT_READ_PASS:
+ case SCSI_PROT_WRITE_PASS:
+ if (ql2xenablehba_err_chk >= 2)
+ return 1;
+ break;
+ case SCSI_PROT_READ_INSERT:
+ case SCSI_PROT_WRITE_STRIP:
+ return 1;
+ }
+ return 0;
+}
+
+static inline int
+qla2x00_reset_active(scsi_qla_host_t *vha)
+{
+ scsi_qla_host_t *base_vha = pci_get_drvdata(vha->hw->pdev);
+
+ /* Test appropriate base-vha and vha flags. */
+ return test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags) ||
+ test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags) ||
+ test_bit(ISP_ABORT_RETRY, &base_vha->dpc_flags) ||
+ test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) ||
+ test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags);
+}
+
+static inline srb_t *
+qla2x00_get_sp(scsi_qla_host_t *vha, fc_port_t *fcport, gfp_t flag)
+{
+ srb_t *sp = NULL;
+ struct qla_hw_data *ha = vha->hw;
+ uint8_t bail;
+
+ QLA_VHA_MARK_BUSY(vha, bail);
+ if (unlikely(bail))
+ return NULL;
+
+ sp = mempool_alloc(ha->srb_mempool, flag);
+ if (!sp)
+ goto done;
+
+ memset(sp, 0, sizeof(*sp));
+ sp->fcport = fcport;
+ sp->iocbs = 1;
+done:
+ if (!sp)
+ QLA_VHA_MARK_NOT_BUSY(vha);
+ return sp;
+}
+
+static inline void
+qla2x00_init_timer(srb_t *sp, unsigned long tmo)
+{
+ init_timer(&sp->u.iocb_cmd.timer);
+ sp->u.iocb_cmd.timer.expires = jiffies + tmo * HZ;
+ sp->u.iocb_cmd.timer.data = (unsigned long)sp;
+ sp->u.iocb_cmd.timer.function = qla2x00_sp_timeout;
+ add_timer(&sp->u.iocb_cmd.timer);
+ sp->free = qla2x00_sp_free;
+}
+
+static inline int
+qla2x00_gid_list_size(struct qla_hw_data *ha)
+{
+ return sizeof(struct gid_list_info) * ha->max_fibre_devices;
+}
diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c
new file mode 100644
index 00000000..eac95092
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_iocb.c
@@ -0,0 +1,2647 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2003-2011 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#include "qla_def.h"
+
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+
+#include <scsi/scsi_tcq.h>
+
+static void qla25xx_set_que(srb_t *, struct rsp_que **);
+/**
+ * qla2x00_get_cmd_direction() - Determine control_flag data direction.
+ * @cmd: SCSI command
+ *
+ * Returns the proper CF_* direction based on CDB.
+ */
+static inline uint16_t
+qla2x00_get_cmd_direction(srb_t *sp)
+{
+ uint16_t cflags;
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
+
+ cflags = 0;
+
+ /* Set transfer direction */
+ if (cmd->sc_data_direction == DMA_TO_DEVICE) {
+ cflags = CF_WRITE;
+ sp->fcport->vha->hw->qla_stats.output_bytes +=
+ scsi_bufflen(cmd);
+ } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
+ cflags = CF_READ;
+ sp->fcport->vha->hw->qla_stats.input_bytes +=
+ scsi_bufflen(cmd);
+ }
+ return (cflags);
+}
+
+/**
+ * qla2x00_calc_iocbs_32() - Determine number of Command Type 2 and
+ * Continuation Type 0 IOCBs to allocate.
+ *
+ * @dsds: number of data segment decriptors needed
+ *
+ * Returns the number of IOCB entries needed to store @dsds.
+ */
+uint16_t
+qla2x00_calc_iocbs_32(uint16_t dsds)
+{
+ uint16_t iocbs;
+
+ iocbs = 1;
+ if (dsds > 3) {
+ iocbs += (dsds - 3) / 7;
+ if ((dsds - 3) % 7)
+ iocbs++;
+ }
+ return (iocbs);
+}
+
+/**
+ * qla2x00_calc_iocbs_64() - Determine number of Command Type 3 and
+ * Continuation Type 1 IOCBs to allocate.
+ *
+ * @dsds: number of data segment decriptors needed
+ *
+ * Returns the number of IOCB entries needed to store @dsds.
+ */
+uint16_t
+qla2x00_calc_iocbs_64(uint16_t dsds)
+{
+ uint16_t iocbs;
+
+ iocbs = 1;
+ if (dsds > 2) {
+ iocbs += (dsds - 2) / 5;
+ if ((dsds - 2) % 5)
+ iocbs++;
+ }
+ return (iocbs);
+}
+
+/**
+ * qla2x00_prep_cont_type0_iocb() - Initialize a Continuation Type 0 IOCB.
+ * @ha: HA context
+ *
+ * Returns a pointer to the Continuation Type 0 IOCB packet.
+ */
+static inline cont_entry_t *
+qla2x00_prep_cont_type0_iocb(struct scsi_qla_host *vha)
+{
+ cont_entry_t *cont_pkt;
+ struct req_que *req = vha->req;
+ /* Adjust ring index. */
+ req->ring_index++;
+ if (req->ring_index == req->length) {
+ req->ring_index = 0;
+ req->ring_ptr = req->ring;
+ } else {
+ req->ring_ptr++;
+ }
+
+ cont_pkt = (cont_entry_t *)req->ring_ptr;
+
+ /* Load packet defaults. */
+ *((uint32_t *)(&cont_pkt->entry_type)) =
+ __constant_cpu_to_le32(CONTINUE_TYPE);
+
+ return (cont_pkt);
+}
+
+/**
+ * qla2x00_prep_cont_type1_iocb() - Initialize a Continuation Type 1 IOCB.
+ * @ha: HA context
+ *
+ * Returns a pointer to the continuation type 1 IOCB packet.
+ */
+static inline cont_a64_entry_t *
+qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha, struct req_que *req)
+{
+ cont_a64_entry_t *cont_pkt;
+
+ /* Adjust ring index. */
+ req->ring_index++;
+ if (req->ring_index == req->length) {
+ req->ring_index = 0;
+ req->ring_ptr = req->ring;
+ } else {
+ req->ring_ptr++;
+ }
+
+ cont_pkt = (cont_a64_entry_t *)req->ring_ptr;
+
+ /* Load packet defaults. */
+ *((uint32_t *)(&cont_pkt->entry_type)) =
+ __constant_cpu_to_le32(CONTINUE_A64_TYPE);
+
+ return (cont_pkt);
+}
+
+static inline int
+qla24xx_configure_prot_mode(srb_t *sp, uint16_t *fw_prot_opts)
+{
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
+ uint8_t guard = scsi_host_get_guard(cmd->device->host);
+
+ /* We only support T10 DIF right now */
+ if (guard != SHOST_DIX_GUARD_CRC) {
+ ql_dbg(ql_dbg_io, sp->fcport->vha, 0x3007,
+ "Unsupported guard: %d for cmd=%p.\n", guard, cmd);
+ return 0;
+ }
+
+ /* We always use DIFF Bundling for best performance */
+ *fw_prot_opts = 0;
+
+ /* Translate SCSI opcode to a protection opcode */
+ switch (scsi_get_prot_op(cmd)) {
+ case SCSI_PROT_READ_STRIP:
+ *fw_prot_opts |= PO_MODE_DIF_REMOVE;
+ break;
+ case SCSI_PROT_WRITE_INSERT:
+ *fw_prot_opts |= PO_MODE_DIF_INSERT;
+ break;
+ case SCSI_PROT_READ_INSERT:
+ *fw_prot_opts |= PO_MODE_DIF_INSERT;
+ break;
+ case SCSI_PROT_WRITE_STRIP:
+ *fw_prot_opts |= PO_MODE_DIF_REMOVE;
+ break;
+ case SCSI_PROT_READ_PASS:
+ *fw_prot_opts |= PO_MODE_DIF_PASS;
+ break;
+ case SCSI_PROT_WRITE_PASS:
+ *fw_prot_opts |= PO_MODE_DIF_PASS;
+ break;
+ default: /* Normal Request */
+ *fw_prot_opts |= PO_MODE_DIF_PASS;
+ break;
+ }
+
+ return scsi_prot_sg_count(cmd);
+}
+
+/*
+ * qla2x00_build_scsi_iocbs_32() - Build IOCB command utilizing 32bit
+ * capable IOCB types.
+ *
+ * @sp: SRB command to process
+ * @cmd_pkt: Command type 2 IOCB
+ * @tot_dsds: Total number of segments to transfer
+ */
+void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt,
+ uint16_t tot_dsds)
+{
+ uint16_t avail_dsds;
+ uint32_t *cur_dsd;
+ scsi_qla_host_t *vha;
+ struct scsi_cmnd *cmd;
+ struct scatterlist *sg;
+ int i;
+
+ cmd = GET_CMD_SP(sp);
+
+ /* Update entry type to indicate Command Type 2 IOCB */
+ *((uint32_t *)(&cmd_pkt->entry_type)) =
+ __constant_cpu_to_le32(COMMAND_TYPE);
+
+ /* No data transfer */
+ if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) {
+ cmd_pkt->byte_count = __constant_cpu_to_le32(0);
+ return;
+ }
+
+ vha = sp->fcport->vha;
+ cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(sp));
+
+ /* Three DSDs are available in the Command Type 2 IOCB */
+ avail_dsds = 3;
+ cur_dsd = (uint32_t *)&cmd_pkt->dseg_0_address;
+
+ /* Load data segments */
+ scsi_for_each_sg(cmd, sg, tot_dsds, i) {
+ cont_entry_t *cont_pkt;
+
+ /* Allocate additional continuation packets? */
+ if (avail_dsds == 0) {
+ /*
+ * Seven DSDs are available in the Continuation
+ * Type 0 IOCB.
+ */
+ cont_pkt = qla2x00_prep_cont_type0_iocb(vha);
+ cur_dsd = (uint32_t *)&cont_pkt->dseg_0_address;
+ avail_dsds = 7;
+ }
+
+ *cur_dsd++ = cpu_to_le32(sg_dma_address(sg));
+ *cur_dsd++ = cpu_to_le32(sg_dma_len(sg));
+ avail_dsds--;
+ }
+}
+
+/**
+ * qla2x00_build_scsi_iocbs_64() - Build IOCB command utilizing 64bit
+ * capable IOCB types.
+ *
+ * @sp: SRB command to process
+ * @cmd_pkt: Command type 3 IOCB
+ * @tot_dsds: Total number of segments to transfer
+ */
+void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt,
+ uint16_t tot_dsds)
+{
+ uint16_t avail_dsds;
+ uint32_t *cur_dsd;
+ scsi_qla_host_t *vha;
+ struct scsi_cmnd *cmd;
+ struct scatterlist *sg;
+ int i;
+
+ cmd = GET_CMD_SP(sp);
+
+ /* Update entry type to indicate Command Type 3 IOCB */
+ *((uint32_t *)(&cmd_pkt->entry_type)) =
+ __constant_cpu_to_le32(COMMAND_A64_TYPE);
+
+ /* No data transfer */
+ if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) {
+ cmd_pkt->byte_count = __constant_cpu_to_le32(0);
+ return;
+ }
+
+ vha = sp->fcport->vha;
+ cmd_pkt->control_flags |= cpu_to_le16(qla2x00_get_cmd_direction(sp));
+
+ /* Two DSDs are available in the Command Type 3 IOCB */
+ avail_dsds = 2;
+ cur_dsd = (uint32_t *)&cmd_pkt->dseg_0_address;
+
+ /* Load data segments */
+ scsi_for_each_sg(cmd, sg, tot_dsds, i) {
+ dma_addr_t sle_dma;
+ cont_a64_entry_t *cont_pkt;
+
+ /* Allocate additional continuation packets? */
+ if (avail_dsds == 0) {
+ /*
+ * Five DSDs are available in the Continuation
+ * Type 1 IOCB.
+ */
+ cont_pkt = qla2x00_prep_cont_type1_iocb(vha, vha->req);
+ cur_dsd = (uint32_t *)cont_pkt->dseg_0_address;
+ avail_dsds = 5;
+ }
+
+ sle_dma = sg_dma_address(sg);
+ *cur_dsd++ = cpu_to_le32(LSD(sle_dma));
+ *cur_dsd++ = cpu_to_le32(MSD(sle_dma));
+ *cur_dsd++ = cpu_to_le32(sg_dma_len(sg));
+ avail_dsds--;
+ }
+}
+
+/**
+ * qla2x00_start_scsi() - Send a SCSI command to the ISP
+ * @sp: command to send to the ISP
+ *
+ * Returns non-zero if a failure occurred, else zero.
+ */
+int
+qla2x00_start_scsi(srb_t *sp)
+{
+ int ret, nseg;
+ unsigned long flags;
+ scsi_qla_host_t *vha;
+ struct scsi_cmnd *cmd;
+ uint32_t *clr_ptr;
+ uint32_t index;
+ uint32_t handle;
+ cmd_entry_t *cmd_pkt;
+ uint16_t cnt;
+ uint16_t req_cnt;
+ uint16_t tot_dsds;
+ struct device_reg_2xxx __iomem *reg;
+ struct qla_hw_data *ha;
+ struct req_que *req;
+ struct rsp_que *rsp;
+ char tag[2];
+
+ /* Setup device pointers. */
+ ret = 0;
+ vha = sp->fcport->vha;
+ ha = vha->hw;
+ reg = &ha->iobase->isp;
+ cmd = GET_CMD_SP(sp);
+ req = ha->req_q_map[0];
+ rsp = ha->rsp_q_map[0];
+ /* So we know we haven't pci_map'ed anything yet */
+ tot_dsds = 0;
+
+ /* Send marker if required */
+ if (vha->marker_needed != 0) {
+ if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL) !=
+ QLA_SUCCESS) {
+ return (QLA_FUNCTION_FAILED);
+ }
+ vha->marker_needed = 0;
+ }
+
+ /* Acquire ring specific lock */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Check for room in outstanding command list. */
+ handle = req->current_outstanding_cmd;
+ for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) {
+ handle++;
+ if (handle == MAX_OUTSTANDING_COMMANDS)
+ handle = 1;
+ if (!req->outstanding_cmds[handle])
+ break;
+ }
+ if (index == MAX_OUTSTANDING_COMMANDS)
+ goto queuing_error;
+
+ /* Map the sg table so we have an accurate count of sg entries needed */
+ if (scsi_sg_count(cmd)) {
+ nseg = dma_map_sg(&ha->pdev->dev, scsi_sglist(cmd),
+ scsi_sg_count(cmd), cmd->sc_data_direction);
+ if (unlikely(!nseg))
+ goto queuing_error;
+ } else
+ nseg = 0;
+
+ tot_dsds = nseg;
+
+ /* Calculate the number of request entries needed. */
+ req_cnt = ha->isp_ops->calc_req_entries(tot_dsds);
+ if (req->cnt < (req_cnt + 2)) {
+ cnt = RD_REG_WORD_RELAXED(ISP_REQ_Q_OUT(ha, reg));
+ if (req->ring_index < cnt)
+ req->cnt = cnt - req->ring_index;
+ else
+ req->cnt = req->length -
+ (req->ring_index - cnt);
+ }
+ if (req->cnt < (req_cnt + 2))
+ goto queuing_error;
+
+ /* Build command packet */
+ req->current_outstanding_cmd = handle;
+ req->outstanding_cmds[handle] = sp;
+ sp->handle = handle;
+ cmd->host_scribble = (unsigned char *)(unsigned long)handle;
+ req->cnt -= req_cnt;
+
+ cmd_pkt = (cmd_entry_t *)req->ring_ptr;
+ cmd_pkt->handle = handle;
+ /* Zero out remaining portion of packet. */
+ clr_ptr = (uint32_t *)cmd_pkt + 2;
+ memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
+ cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
+
+ /* Set target ID and LUN number*/
+ SET_TARGET_ID(ha, cmd_pkt->target, sp->fcport->loop_id);
+ cmd_pkt->lun = cpu_to_le16(cmd->device->lun);
+
+ /* Update tagged queuing modifier */
+ if (scsi_populate_tag_msg(cmd, tag)) {
+ switch (tag[0]) {
+ case HEAD_OF_QUEUE_TAG:
+ cmd_pkt->control_flags =
+ __constant_cpu_to_le16(CF_HEAD_TAG);
+ break;
+ case ORDERED_QUEUE_TAG:
+ cmd_pkt->control_flags =
+ __constant_cpu_to_le16(CF_ORDERED_TAG);
+ break;
+ default:
+ cmd_pkt->control_flags =
+ __constant_cpu_to_le16(CF_SIMPLE_TAG);
+ break;
+ }
+ }
+
+ /* Load SCSI command packet. */
+ memcpy(cmd_pkt->scsi_cdb, cmd->cmnd, cmd->cmd_len);
+ cmd_pkt->byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd));
+
+ /* Build IOCB segments */
+ ha->isp_ops->build_iocbs(sp, cmd_pkt, tot_dsds);
+
+ /* Set total data segment count. */
+ cmd_pkt->entry_count = (uint8_t)req_cnt;
+ wmb();
+
+ /* Adjust ring index. */
+ req->ring_index++;
+ if (req->ring_index == req->length) {
+ req->ring_index = 0;
+ req->ring_ptr = req->ring;
+ } else
+ req->ring_ptr++;
+
+ sp->flags |= SRB_DMA_VALID;
+
+ /* Set chip new ring index. */
+ WRT_REG_WORD(ISP_REQ_Q_IN(ha, reg), req->ring_index);
+ RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, reg)); /* PCI Posting. */
+
+ /* Manage unprocessed RIO/ZIO commands in response queue. */
+ if (vha->flags.process_response_queue &&
+ rsp->ring_ptr->signature != RESPONSE_PROCESSED)
+ qla2x00_process_response_queue(rsp);
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ return (QLA_SUCCESS);
+
+queuing_error:
+ if (tot_dsds)
+ scsi_dma_unmap(cmd);
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return (QLA_FUNCTION_FAILED);
+}
+
+/**
+ * qla2x00_start_iocbs() - Execute the IOCB command
+ */
+static void
+qla2x00_start_iocbs(struct scsi_qla_host *vha, struct req_que *req)
+{
+ struct qla_hw_data *ha = vha->hw;
+ device_reg_t __iomem *reg = ISP_QUE_REG(ha, req->id);
+
+ if (IS_QLA82XX(ha)) {
+ qla82xx_start_iocbs(vha);
+ } else {
+ /* Adjust ring index. */
+ req->ring_index++;
+ if (req->ring_index == req->length) {
+ req->ring_index = 0;
+ req->ring_ptr = req->ring;
+ } else
+ req->ring_ptr++;
+
+ /* Set chip new ring index. */
+ if (ha->mqenable || IS_QLA83XX(ha)) {
+ WRT_REG_DWORD(req->req_q_in, req->ring_index);
+ RD_REG_DWORD_RELAXED(&ha->iobase->isp24.hccr);
+ } else if (IS_FWI2_CAPABLE(ha)) {
+ WRT_REG_DWORD(&reg->isp24.req_q_in, req->ring_index);
+ RD_REG_DWORD_RELAXED(&reg->isp24.req_q_in);
+ } else {
+ WRT_REG_WORD(ISP_REQ_Q_IN(ha, &reg->isp),
+ req->ring_index);
+ RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, &reg->isp));
+ }
+ }
+}
+
+/**
+ * qla2x00_marker() - Send a marker IOCB to the firmware.
+ * @ha: HA context
+ * @loop_id: loop ID
+ * @lun: LUN
+ * @type: marker modifier
+ *
+ * Can be called from both normal and interrupt context.
+ *
+ * Returns non-zero if a failure occurred, else zero.
+ */
+static int
+__qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req,
+ struct rsp_que *rsp, uint16_t loop_id,
+ uint16_t lun, uint8_t type)
+{
+ mrk_entry_t *mrk;
+ struct mrk_entry_24xx *mrk24;
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
+
+ mrk24 = NULL;
+ req = ha->req_q_map[0];
+ mrk = (mrk_entry_t *)qla2x00_alloc_iocbs(vha, 0);
+ if (mrk == NULL) {
+ ql_log(ql_log_warn, base_vha, 0x3026,
+ "Failed to allocate Marker IOCB.\n");
+
+ return (QLA_FUNCTION_FAILED);
+ }
+
+ mrk->entry_type = MARKER_TYPE;
+ mrk->modifier = type;
+ if (type != MK_SYNC_ALL) {
+ if (IS_FWI2_CAPABLE(ha)) {
+ mrk24 = (struct mrk_entry_24xx *) mrk;
+ mrk24->nport_handle = cpu_to_le16(loop_id);
+ mrk24->lun[1] = LSB(lun);
+ mrk24->lun[2] = MSB(lun);
+ host_to_fcp_swap(mrk24->lun, sizeof(mrk24->lun));
+ mrk24->vp_index = vha->vp_idx;
+ mrk24->handle = MAKE_HANDLE(req->id, mrk24->handle);
+ } else {
+ SET_TARGET_ID(ha, mrk->target, loop_id);
+ mrk->lun = cpu_to_le16(lun);
+ }
+ }
+ wmb();
+
+ qla2x00_start_iocbs(vha, req);
+
+ return (QLA_SUCCESS);
+}
+
+int
+qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req,
+ struct rsp_que *rsp, uint16_t loop_id, uint16_t lun,
+ uint8_t type)
+{
+ int ret;
+ unsigned long flags = 0;
+
+ spin_lock_irqsave(&vha->hw->hardware_lock, flags);
+ ret = __qla2x00_marker(vha, req, rsp, loop_id, lun, type);
+ spin_unlock_irqrestore(&vha->hw->hardware_lock, flags);
+
+ return (ret);
+}
+
+/**
+ * qla24xx_calc_iocbs() - Determine number of Command Type 3 and
+ * Continuation Type 1 IOCBs to allocate.
+ *
+ * @dsds: number of data segment decriptors needed
+ *
+ * Returns the number of IOCB entries needed to store @dsds.
+ */
+inline uint16_t
+qla24xx_calc_iocbs(scsi_qla_host_t *vha, uint16_t dsds)
+{
+ uint16_t iocbs;
+
+ iocbs = 1;
+ if (dsds > 1) {
+ iocbs += (dsds - 1) / 5;
+ if ((dsds - 1) % 5)
+ iocbs++;
+ }
+ return iocbs;
+}
+
+static inline int
+qla24xx_build_scsi_type_6_iocbs(srb_t *sp, struct cmd_type_6 *cmd_pkt,
+ uint16_t tot_dsds)
+{
+ uint32_t *cur_dsd = NULL;
+ scsi_qla_host_t *vha;
+ struct qla_hw_data *ha;
+ struct scsi_cmnd *cmd;
+ struct scatterlist *cur_seg;
+ uint32_t *dsd_seg;
+ void *next_dsd;
+ uint8_t avail_dsds;
+ uint8_t first_iocb = 1;
+ uint32_t dsd_list_len;
+ struct dsd_dma *dsd_ptr;
+ struct ct6_dsd *ctx;
+
+ cmd = GET_CMD_SP(sp);
+
+ /* Update entry type to indicate Command Type 3 IOCB */
+ *((uint32_t *)(&cmd_pkt->entry_type)) =
+ __constant_cpu_to_le32(COMMAND_TYPE_6);
+
+ /* No data transfer */
+ if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) {
+ cmd_pkt->byte_count = __constant_cpu_to_le32(0);
+ return 0;
+ }
+
+ vha = sp->fcport->vha;
+ ha = vha->hw;
+
+ /* Set transfer direction */
+ if (cmd->sc_data_direction == DMA_TO_DEVICE) {
+ cmd_pkt->control_flags =
+ __constant_cpu_to_le16(CF_WRITE_DATA);
+ ha->qla_stats.output_bytes += scsi_bufflen(cmd);
+ } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
+ cmd_pkt->control_flags =
+ __constant_cpu_to_le16(CF_READ_DATA);
+ ha->qla_stats.input_bytes += scsi_bufflen(cmd);
+ }
+
+ cur_seg = scsi_sglist(cmd);
+ ctx = GET_CMD_CTX_SP(sp);
+
+ while (tot_dsds) {
+ avail_dsds = (tot_dsds > QLA_DSDS_PER_IOCB) ?
+ QLA_DSDS_PER_IOCB : tot_dsds;
+ tot_dsds -= avail_dsds;
+ dsd_list_len = (avail_dsds + 1) * QLA_DSD_SIZE;
+
+ dsd_ptr = list_first_entry(&ha->gbl_dsd_list,
+ struct dsd_dma, list);
+ next_dsd = dsd_ptr->dsd_addr;
+ list_del(&dsd_ptr->list);
+ ha->gbl_dsd_avail--;
+ list_add_tail(&dsd_ptr->list, &ctx->dsd_list);
+ ctx->dsd_use_cnt++;
+ ha->gbl_dsd_inuse++;
+
+ if (first_iocb) {
+ first_iocb = 0;
+ dsd_seg = (uint32_t *)&cmd_pkt->fcp_data_dseg_address;
+ *dsd_seg++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma));
+ *dsd_seg++ = cpu_to_le32(MSD(dsd_ptr->dsd_list_dma));
+ cmd_pkt->fcp_data_dseg_len = cpu_to_le32(dsd_list_len);
+ } else {
+ *cur_dsd++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma));
+ *cur_dsd++ = cpu_to_le32(MSD(dsd_ptr->dsd_list_dma));
+ *cur_dsd++ = cpu_to_le32(dsd_list_len);
+ }
+ cur_dsd = (uint32_t *)next_dsd;
+ while (avail_dsds) {
+ dma_addr_t sle_dma;
+
+ sle_dma = sg_dma_address(cur_seg);
+ *cur_dsd++ = cpu_to_le32(LSD(sle_dma));
+ *cur_dsd++ = cpu_to_le32(MSD(sle_dma));
+ *cur_dsd++ = cpu_to_le32(sg_dma_len(cur_seg));
+ cur_seg = sg_next(cur_seg);
+ avail_dsds--;
+ }
+ }
+
+ /* Null termination */
+ *cur_dsd++ = 0;
+ *cur_dsd++ = 0;
+ *cur_dsd++ = 0;
+ cmd_pkt->control_flags |= CF_DATA_SEG_DESCR_ENABLE;
+ return 0;
+}
+
+/*
+ * qla24xx_calc_dsd_lists() - Determine number of DSD list required
+ * for Command Type 6.
+ *
+ * @dsds: number of data segment decriptors needed
+ *
+ * Returns the number of dsd list needed to store @dsds.
+ */
+inline uint16_t
+qla24xx_calc_dsd_lists(uint16_t dsds)
+{
+ uint16_t dsd_lists = 0;
+
+ dsd_lists = (dsds/QLA_DSDS_PER_IOCB);
+ if (dsds % QLA_DSDS_PER_IOCB)
+ dsd_lists++;
+ return dsd_lists;
+}
+
+
+/**
+ * qla24xx_build_scsi_iocbs() - Build IOCB command utilizing Command Type 7
+ * IOCB types.
+ *
+ * @sp: SRB command to process
+ * @cmd_pkt: Command type 3 IOCB
+ * @tot_dsds: Total number of segments to transfer
+ */
+inline void
+qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt,
+ uint16_t tot_dsds)
+{
+ uint16_t avail_dsds;
+ uint32_t *cur_dsd;
+ scsi_qla_host_t *vha;
+ struct scsi_cmnd *cmd;
+ struct scatterlist *sg;
+ int i;
+ struct req_que *req;
+
+ cmd = GET_CMD_SP(sp);
+
+ /* Update entry type to indicate Command Type 3 IOCB */
+ *((uint32_t *)(&cmd_pkt->entry_type)) =
+ __constant_cpu_to_le32(COMMAND_TYPE_7);
+
+ /* No data transfer */
+ if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) {
+ cmd_pkt->byte_count = __constant_cpu_to_le32(0);
+ return;
+ }
+
+ vha = sp->fcport->vha;
+ req = vha->req;
+
+ /* Set transfer direction */
+ if (cmd->sc_data_direction == DMA_TO_DEVICE) {
+ cmd_pkt->task_mgmt_flags =
+ __constant_cpu_to_le16(TMF_WRITE_DATA);
+ sp->fcport->vha->hw->qla_stats.output_bytes +=
+ scsi_bufflen(cmd);
+ } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
+ cmd_pkt->task_mgmt_flags =
+ __constant_cpu_to_le16(TMF_READ_DATA);
+ sp->fcport->vha->hw->qla_stats.input_bytes +=
+ scsi_bufflen(cmd);
+ }
+
+ /* One DSD is available in the Command Type 3 IOCB */
+ avail_dsds = 1;
+ cur_dsd = (uint32_t *)&cmd_pkt->dseg_0_address;
+
+ /* Load data segments */
+
+ scsi_for_each_sg(cmd, sg, tot_dsds, i) {
+ dma_addr_t sle_dma;
+ cont_a64_entry_t *cont_pkt;
+
+ /* Allocate additional continuation packets? */
+ if (avail_dsds == 0) {
+ /*
+ * Five DSDs are available in the Continuation
+ * Type 1 IOCB.
+ */
+ cont_pkt = qla2x00_prep_cont_type1_iocb(vha, vha->req);
+ cur_dsd = (uint32_t *)cont_pkt->dseg_0_address;
+ avail_dsds = 5;
+ }
+
+ sle_dma = sg_dma_address(sg);
+ *cur_dsd++ = cpu_to_le32(LSD(sle_dma));
+ *cur_dsd++ = cpu_to_le32(MSD(sle_dma));
+ *cur_dsd++ = cpu_to_le32(sg_dma_len(sg));
+ avail_dsds--;
+ }
+}
+
+struct fw_dif_context {
+ uint32_t ref_tag;
+ uint16_t app_tag;
+ uint8_t ref_tag_mask[4]; /* Validation/Replacement Mask*/
+ uint8_t app_tag_mask[2]; /* Validation/Replacement Mask*/
+};
+
+/*
+ * qla24xx_set_t10dif_tags_from_cmd - Extract Ref and App tags from SCSI command
+ *
+ */
+static inline void
+qla24xx_set_t10dif_tags(srb_t *sp, struct fw_dif_context *pkt,
+ unsigned int protcnt)
+{
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
+
+ switch (scsi_get_prot_type(cmd)) {
+ case SCSI_PROT_DIF_TYPE0:
+ /*
+ * No check for ql2xenablehba_err_chk, as it would be an
+ * I/O error if hba tag generation is not done.
+ */
+ pkt->ref_tag = cpu_to_le32((uint32_t)
+ (0xffffffff & scsi_get_lba(cmd)));
+
+ if (!qla2x00_hba_err_chk_enabled(sp))
+ break;
+
+ pkt->ref_tag_mask[0] = 0xff;
+ pkt->ref_tag_mask[1] = 0xff;
+ pkt->ref_tag_mask[2] = 0xff;
+ pkt->ref_tag_mask[3] = 0xff;
+ break;
+
+ /*
+ * For TYPE 2 protection: 16 bit GUARD + 32 bit REF tag has to
+ * match LBA in CDB + N
+ */
+ case SCSI_PROT_DIF_TYPE2:
+ pkt->app_tag = __constant_cpu_to_le16(0);
+ pkt->app_tag_mask[0] = 0x0;
+ pkt->app_tag_mask[1] = 0x0;
+
+ pkt->ref_tag = cpu_to_le32((uint32_t)
+ (0xffffffff & scsi_get_lba(cmd)));
+
+ if (!qla2x00_hba_err_chk_enabled(sp))
+ break;
+
+ /* enable ALL bytes of the ref tag */
+ pkt->ref_tag_mask[0] = 0xff;
+ pkt->ref_tag_mask[1] = 0xff;
+ pkt->ref_tag_mask[2] = 0xff;
+ pkt->ref_tag_mask[3] = 0xff;
+ break;
+
+ /* For Type 3 protection: 16 bit GUARD only */
+ case SCSI_PROT_DIF_TYPE3:
+ pkt->ref_tag_mask[0] = pkt->ref_tag_mask[1] =
+ pkt->ref_tag_mask[2] = pkt->ref_tag_mask[3] =
+ 0x00;
+ break;
+
+ /*
+ * For TYpe 1 protection: 16 bit GUARD tag, 32 bit REF tag, and
+ * 16 bit app tag.
+ */
+ case SCSI_PROT_DIF_TYPE1:
+ pkt->ref_tag = cpu_to_le32((uint32_t)
+ (0xffffffff & scsi_get_lba(cmd)));
+ pkt->app_tag = __constant_cpu_to_le16(0);
+ pkt->app_tag_mask[0] = 0x0;
+ pkt->app_tag_mask[1] = 0x0;
+
+ if (!qla2x00_hba_err_chk_enabled(sp))
+ break;
+
+ /* enable ALL bytes of the ref tag */
+ pkt->ref_tag_mask[0] = 0xff;
+ pkt->ref_tag_mask[1] = 0xff;
+ pkt->ref_tag_mask[2] = 0xff;
+ pkt->ref_tag_mask[3] = 0xff;
+ break;
+ }
+
+ ql_dbg(ql_dbg_io, vha, 0x3009,
+ "Setting protection Tags: (BIG) ref tag = 0x%x, app tag = 0x%x, "
+ "prot SG count %d, cmd lba 0x%x, prot_type=%u cmd=%p.\n",
+ pkt->ref_tag, pkt->app_tag, protcnt, (int)scsi_get_lba(cmd),
+ scsi_get_prot_type(cmd), cmd);
+}
+
+struct qla2_sgx {
+ dma_addr_t dma_addr; /* OUT */
+ uint32_t dma_len; /* OUT */
+
+ uint32_t tot_bytes; /* IN */
+ struct scatterlist *cur_sg; /* IN */
+
+ /* for book keeping, bzero on initial invocation */
+ uint32_t bytes_consumed;
+ uint32_t num_bytes;
+ uint32_t tot_partial;
+
+ /* for debugging */
+ uint32_t num_sg;
+ srb_t *sp;
+};
+
+static int
+qla24xx_get_one_block_sg(uint32_t blk_sz, struct qla2_sgx *sgx,
+ uint32_t *partial)
+{
+ struct scatterlist *sg;
+ uint32_t cumulative_partial, sg_len;
+ dma_addr_t sg_dma_addr;
+
+ if (sgx->num_bytes == sgx->tot_bytes)
+ return 0;
+
+ sg = sgx->cur_sg;
+ cumulative_partial = sgx->tot_partial;
+
+ sg_dma_addr = sg_dma_address(sg);
+ sg_len = sg_dma_len(sg);
+
+ sgx->dma_addr = sg_dma_addr + sgx->bytes_consumed;
+
+ if ((cumulative_partial + (sg_len - sgx->bytes_consumed)) >= blk_sz) {
+ sgx->dma_len = (blk_sz - cumulative_partial);
+ sgx->tot_partial = 0;
+ sgx->num_bytes += blk_sz;
+ *partial = 0;
+ } else {
+ sgx->dma_len = sg_len - sgx->bytes_consumed;
+ sgx->tot_partial += sgx->dma_len;
+ *partial = 1;
+ }
+
+ sgx->bytes_consumed += sgx->dma_len;
+
+ if (sg_len == sgx->bytes_consumed) {
+ sg = sg_next(sg);
+ sgx->num_sg++;
+ sgx->cur_sg = sg;
+ sgx->bytes_consumed = 0;
+ }
+
+ return 1;
+}
+
+static int
+qla24xx_walk_and_build_sglist_no_difb(struct qla_hw_data *ha, srb_t *sp,
+ uint32_t *dsd, uint16_t tot_dsds)
+{
+ void *next_dsd;
+ uint8_t avail_dsds = 0;
+ uint32_t dsd_list_len;
+ struct dsd_dma *dsd_ptr;
+ struct scatterlist *sg_prot;
+ uint32_t *cur_dsd = dsd;
+ uint16_t used_dsds = tot_dsds;
+
+ uint32_t prot_int;
+ uint32_t partial;
+ struct qla2_sgx sgx;
+ dma_addr_t sle_dma;
+ uint32_t sle_dma_len, tot_prot_dma_len = 0;
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
+
+ prot_int = cmd->device->sector_size;
+
+ memset(&sgx, 0, sizeof(struct qla2_sgx));
+ sgx.tot_bytes = scsi_bufflen(cmd);
+ sgx.cur_sg = scsi_sglist(cmd);
+ sgx.sp = sp;
+
+ sg_prot = scsi_prot_sglist(cmd);
+
+ while (qla24xx_get_one_block_sg(prot_int, &sgx, &partial)) {
+
+ sle_dma = sgx.dma_addr;
+ sle_dma_len = sgx.dma_len;
+alloc_and_fill:
+ /* Allocate additional continuation packets? */
+ if (avail_dsds == 0) {
+ avail_dsds = (used_dsds > QLA_DSDS_PER_IOCB) ?
+ QLA_DSDS_PER_IOCB : used_dsds;
+ dsd_list_len = (avail_dsds + 1) * 12;
+ used_dsds -= avail_dsds;
+
+ /* allocate tracking DS */
+ dsd_ptr = kzalloc(sizeof(struct dsd_dma), GFP_ATOMIC);
+ if (!dsd_ptr)
+ return 1;
+
+ /* allocate new list */
+ dsd_ptr->dsd_addr = next_dsd =
+ dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC,
+ &dsd_ptr->dsd_list_dma);
+
+ if (!next_dsd) {
+ /*
+ * Need to cleanup only this dsd_ptr, rest
+ * will be done by sp_free_dma()
+ */
+ kfree(dsd_ptr);
+ return 1;
+ }
+
+ list_add_tail(&dsd_ptr->list,
+ &((struct crc_context *)sp->u.scmd.ctx)->dsd_list);
+
+ sp->flags |= SRB_CRC_CTX_DSD_VALID;
+
+ /* add new list to cmd iocb or last list */
+ *cur_dsd++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma));
+ *cur_dsd++ = cpu_to_le32(MSD(dsd_ptr->dsd_list_dma));
+ *cur_dsd++ = dsd_list_len;
+ cur_dsd = (uint32_t *)next_dsd;
+ }
+ *cur_dsd++ = cpu_to_le32(LSD(sle_dma));
+ *cur_dsd++ = cpu_to_le32(MSD(sle_dma));
+ *cur_dsd++ = cpu_to_le32(sle_dma_len);
+ avail_dsds--;
+
+ if (partial == 0) {
+ /* Got a full protection interval */
+ sle_dma = sg_dma_address(sg_prot) + tot_prot_dma_len;
+ sle_dma_len = 8;
+
+ tot_prot_dma_len += sle_dma_len;
+ if (tot_prot_dma_len == sg_dma_len(sg_prot)) {
+ tot_prot_dma_len = 0;
+ sg_prot = sg_next(sg_prot);
+ }
+
+ partial = 1; /* So as to not re-enter this block */
+ goto alloc_and_fill;
+ }
+ }
+ /* Null termination */
+ *cur_dsd++ = 0;
+ *cur_dsd++ = 0;
+ *cur_dsd++ = 0;
+ return 0;
+}
+
+static int
+qla24xx_walk_and_build_sglist(struct qla_hw_data *ha, srb_t *sp, uint32_t *dsd,
+ uint16_t tot_dsds)
+{
+ void *next_dsd;
+ uint8_t avail_dsds = 0;
+ uint32_t dsd_list_len;
+ struct dsd_dma *dsd_ptr;
+ struct scatterlist *sg;
+ uint32_t *cur_dsd = dsd;
+ int i;
+ uint16_t used_dsds = tot_dsds;
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
+
+ uint8_t *cp;
+
+ scsi_for_each_sg(cmd, sg, tot_dsds, i) {
+ dma_addr_t sle_dma;
+
+ /* Allocate additional continuation packets? */
+ if (avail_dsds == 0) {
+ avail_dsds = (used_dsds > QLA_DSDS_PER_IOCB) ?
+ QLA_DSDS_PER_IOCB : used_dsds;
+ dsd_list_len = (avail_dsds + 1) * 12;
+ used_dsds -= avail_dsds;
+
+ /* allocate tracking DS */
+ dsd_ptr = kzalloc(sizeof(struct dsd_dma), GFP_ATOMIC);
+ if (!dsd_ptr)
+ return 1;
+
+ /* allocate new list */
+ dsd_ptr->dsd_addr = next_dsd =
+ dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC,
+ &dsd_ptr->dsd_list_dma);
+
+ if (!next_dsd) {
+ /*
+ * Need to cleanup only this dsd_ptr, rest
+ * will be done by sp_free_dma()
+ */
+ kfree(dsd_ptr);
+ return 1;
+ }
+
+ list_add_tail(&dsd_ptr->list,
+ &((struct crc_context *)sp->u.scmd.ctx)->dsd_list);
+
+ sp->flags |= SRB_CRC_CTX_DSD_VALID;
+
+ /* add new list to cmd iocb or last list */
+ *cur_dsd++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma));
+ *cur_dsd++ = cpu_to_le32(MSD(dsd_ptr->dsd_list_dma));
+ *cur_dsd++ = dsd_list_len;
+ cur_dsd = (uint32_t *)next_dsd;
+ }
+ sle_dma = sg_dma_address(sg);
+ ql_dbg(ql_dbg_io, vha, 0x300a,
+ "sg entry %d - addr=0x%x 0x%x, " "len=%d for cmd=%p.\n",
+ i, LSD(sle_dma), MSD(sle_dma), sg_dma_len(sg), cmd);
+ *cur_dsd++ = cpu_to_le32(LSD(sle_dma));
+ *cur_dsd++ = cpu_to_le32(MSD(sle_dma));
+ *cur_dsd++ = cpu_to_le32(sg_dma_len(sg));
+ avail_dsds--;
+
+ if (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_PASS) {
+ cp = page_address(sg_page(sg)) + sg->offset;
+ ql_dbg(ql_dbg_io, vha, 0x300b,
+ "User data buffer=%p for cmd=%p.\n", cp, cmd);
+ }
+ }
+ /* Null termination */
+ *cur_dsd++ = 0;
+ *cur_dsd++ = 0;
+ *cur_dsd++ = 0;
+ return 0;
+}
+
+static int
+qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *ha, srb_t *sp,
+ uint32_t *dsd,
+ uint16_t tot_dsds)
+{
+ void *next_dsd;
+ uint8_t avail_dsds = 0;
+ uint32_t dsd_list_len;
+ struct dsd_dma *dsd_ptr;
+ struct scatterlist *sg;
+ int i;
+ struct scsi_cmnd *cmd;
+ uint32_t *cur_dsd = dsd;
+ uint16_t used_dsds = tot_dsds;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+ uint8_t *cp;
+
+ cmd = GET_CMD_SP(sp);
+ scsi_for_each_prot_sg(cmd, sg, tot_dsds, i) {
+ dma_addr_t sle_dma;
+
+ /* Allocate additional continuation packets? */
+ if (avail_dsds == 0) {
+ avail_dsds = (used_dsds > QLA_DSDS_PER_IOCB) ?
+ QLA_DSDS_PER_IOCB : used_dsds;
+ dsd_list_len = (avail_dsds + 1) * 12;
+ used_dsds -= avail_dsds;
+
+ /* allocate tracking DS */
+ dsd_ptr = kzalloc(sizeof(struct dsd_dma), GFP_ATOMIC);
+ if (!dsd_ptr)
+ return 1;
+
+ /* allocate new list */
+ dsd_ptr->dsd_addr = next_dsd =
+ dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC,
+ &dsd_ptr->dsd_list_dma);
+
+ if (!next_dsd) {
+ /*
+ * Need to cleanup only this dsd_ptr, rest
+ * will be done by sp_free_dma()
+ */
+ kfree(dsd_ptr);
+ return 1;
+ }
+
+ list_add_tail(&dsd_ptr->list,
+ &((struct crc_context *)sp->u.scmd.ctx)->dsd_list);
+
+ sp->flags |= SRB_CRC_CTX_DSD_VALID;
+
+ /* add new list to cmd iocb or last list */
+ *cur_dsd++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma));
+ *cur_dsd++ = cpu_to_le32(MSD(dsd_ptr->dsd_list_dma));
+ *cur_dsd++ = dsd_list_len;
+ cur_dsd = (uint32_t *)next_dsd;
+ }
+ sle_dma = sg_dma_address(sg);
+ if (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_PASS) {
+ ql_dbg(ql_dbg_io, vha, 0x3027,
+ "%s(): %p, sg_entry %d - "
+ "addr=0x%x0x%x, len=%d.\n",
+ __func__, cur_dsd, i,
+ LSD(sle_dma), MSD(sle_dma), sg_dma_len(sg));
+ }
+ *cur_dsd++ = cpu_to_le32(LSD(sle_dma));
+ *cur_dsd++ = cpu_to_le32(MSD(sle_dma));
+ *cur_dsd++ = cpu_to_le32(sg_dma_len(sg));
+
+ if (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_PASS) {
+ cp = page_address(sg_page(sg)) + sg->offset;
+ ql_dbg(ql_dbg_io, vha, 0x3028,
+ "%s(): Protection Data buffer = %p.\n", __func__,
+ cp);
+ }
+ avail_dsds--;
+ }
+ /* Null termination */
+ *cur_dsd++ = 0;
+ *cur_dsd++ = 0;
+ *cur_dsd++ = 0;
+ return 0;
+}
+
+/**
+ * qla24xx_build_scsi_crc_2_iocbs() - Build IOCB command utilizing Command
+ * Type 6 IOCB types.
+ *
+ * @sp: SRB command to process
+ * @cmd_pkt: Command type 3 IOCB
+ * @tot_dsds: Total number of segments to transfer
+ */
+static inline int
+qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt,
+ uint16_t tot_dsds, uint16_t tot_prot_dsds, uint16_t fw_prot_opts)
+{
+ uint32_t *cur_dsd, *fcp_dl;
+ scsi_qla_host_t *vha;
+ struct scsi_cmnd *cmd;
+ struct scatterlist *cur_seg;
+ int sgc;
+ uint32_t total_bytes = 0;
+ uint32_t data_bytes;
+ uint32_t dif_bytes;
+ uint8_t bundling = 1;
+ uint16_t blk_size;
+ uint8_t *clr_ptr;
+ struct crc_context *crc_ctx_pkt = NULL;
+ struct qla_hw_data *ha;
+ uint8_t additional_fcpcdb_len;
+ uint16_t fcp_cmnd_len;
+ struct fcp_cmnd *fcp_cmnd;
+ dma_addr_t crc_ctx_dma;
+ char tag[2];
+
+ cmd = GET_CMD_SP(sp);
+
+ sgc = 0;
+ /* Update entry type to indicate Command Type CRC_2 IOCB */
+ *((uint32_t *)(&cmd_pkt->entry_type)) =
+ __constant_cpu_to_le32(COMMAND_TYPE_CRC_2);
+
+ vha = sp->fcport->vha;
+ ha = vha->hw;
+
+ /* No data transfer */
+ data_bytes = scsi_bufflen(cmd);
+ if (!data_bytes || cmd->sc_data_direction == DMA_NONE) {
+ cmd_pkt->byte_count = __constant_cpu_to_le32(0);
+ return QLA_SUCCESS;
+ }
+
+ cmd_pkt->vp_index = sp->fcport->vp_idx;
+
+ /* Set transfer direction */
+ if (cmd->sc_data_direction == DMA_TO_DEVICE) {
+ cmd_pkt->control_flags =
+ __constant_cpu_to_le16(CF_WRITE_DATA);
+ } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) {
+ cmd_pkt->control_flags =
+ __constant_cpu_to_le16(CF_READ_DATA);
+ }
+
+ if ((scsi_get_prot_op(cmd) == SCSI_PROT_READ_INSERT) ||
+ (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_STRIP) ||
+ (scsi_get_prot_op(cmd) == SCSI_PROT_READ_STRIP) ||
+ (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_INSERT))
+ bundling = 0;
+
+ /* Allocate CRC context from global pool */
+ crc_ctx_pkt = sp->u.scmd.ctx =
+ dma_pool_alloc(ha->dl_dma_pool, GFP_ATOMIC, &crc_ctx_dma);
+
+ if (!crc_ctx_pkt)
+ goto crc_queuing_error;
+
+ /* Zero out CTX area. */
+ clr_ptr = (uint8_t *)crc_ctx_pkt;
+ memset(clr_ptr, 0, sizeof(*crc_ctx_pkt));
+
+ crc_ctx_pkt->crc_ctx_dma = crc_ctx_dma;
+
+ sp->flags |= SRB_CRC_CTX_DMA_VALID;
+
+ /* Set handle */
+ crc_ctx_pkt->handle = cmd_pkt->handle;
+
+ INIT_LIST_HEAD(&crc_ctx_pkt->dsd_list);
+
+ qla24xx_set_t10dif_tags(sp, (struct fw_dif_context *)
+ &crc_ctx_pkt->ref_tag, tot_prot_dsds);
+
+ cmd_pkt->crc_context_address[0] = cpu_to_le32(LSD(crc_ctx_dma));
+ cmd_pkt->crc_context_address[1] = cpu_to_le32(MSD(crc_ctx_dma));
+ cmd_pkt->crc_context_len = CRC_CONTEXT_LEN_FW;
+
+ /* Determine SCSI command length -- align to 4 byte boundary */
+ if (cmd->cmd_len > 16) {
+ additional_fcpcdb_len = cmd->cmd_len - 16;
+ if ((cmd->cmd_len % 4) != 0) {
+ /* SCSI cmd > 16 bytes must be multiple of 4 */
+ goto crc_queuing_error;
+ }
+ fcp_cmnd_len = 12 + cmd->cmd_len + 4;
+ } else {
+ additional_fcpcdb_len = 0;
+ fcp_cmnd_len = 12 + 16 + 4;
+ }
+
+ fcp_cmnd = &crc_ctx_pkt->fcp_cmnd;
+
+ fcp_cmnd->additional_cdb_len = additional_fcpcdb_len;
+ if (cmd->sc_data_direction == DMA_TO_DEVICE)
+ fcp_cmnd->additional_cdb_len |= 1;
+ else if (cmd->sc_data_direction == DMA_FROM_DEVICE)
+ fcp_cmnd->additional_cdb_len |= 2;
+
+ int_to_scsilun(cmd->device->lun, &fcp_cmnd->lun);
+ memcpy(fcp_cmnd->cdb, cmd->cmnd, cmd->cmd_len);
+ cmd_pkt->fcp_cmnd_dseg_len = cpu_to_le16(fcp_cmnd_len);
+ cmd_pkt->fcp_cmnd_dseg_address[0] = cpu_to_le32(
+ LSD(crc_ctx_dma + CRC_CONTEXT_FCPCMND_OFF));
+ cmd_pkt->fcp_cmnd_dseg_address[1] = cpu_to_le32(
+ MSD(crc_ctx_dma + CRC_CONTEXT_FCPCMND_OFF));
+ fcp_cmnd->task_management = 0;
+
+ /*
+ * Update tagged queuing modifier if using command tag queuing
+ */
+ if (scsi_populate_tag_msg(cmd, tag)) {
+ switch (tag[0]) {
+ case HEAD_OF_QUEUE_TAG:
+ fcp_cmnd->task_attribute = TSK_HEAD_OF_QUEUE;
+ break;
+ case ORDERED_QUEUE_TAG:
+ fcp_cmnd->task_attribute = TSK_ORDERED;
+ break;
+ default:
+ fcp_cmnd->task_attribute = 0;
+ break;
+ }
+ } else {
+ fcp_cmnd->task_attribute = 0;
+ }
+
+ cmd_pkt->fcp_rsp_dseg_len = 0; /* Let response come in status iocb */
+
+ /* Compute dif len and adjust data len to incude protection */
+ dif_bytes = 0;
+ blk_size = cmd->device->sector_size;
+ dif_bytes = (data_bytes / blk_size) * 8;
+
+ switch (scsi_get_prot_op(GET_CMD_SP(sp))) {
+ case SCSI_PROT_READ_INSERT:
+ case SCSI_PROT_WRITE_STRIP:
+ total_bytes = data_bytes;
+ data_bytes += dif_bytes;
+ break;
+
+ case SCSI_PROT_READ_STRIP:
+ case SCSI_PROT_WRITE_INSERT:
+ case SCSI_PROT_READ_PASS:
+ case SCSI_PROT_WRITE_PASS:
+ total_bytes = data_bytes + dif_bytes;
+ break;
+ default:
+ BUG();
+ }
+
+ if (!qla2x00_hba_err_chk_enabled(sp))
+ fw_prot_opts |= 0x10; /* Disable Guard tag checking */
+
+ if (!bundling) {
+ cur_dsd = (uint32_t *) &crc_ctx_pkt->u.nobundling.data_address;
+ } else {
+ /*
+ * Configure Bundling if we need to fetch interlaving
+ * protection PCI accesses
+ */
+ fw_prot_opts |= PO_ENABLE_DIF_BUNDLING;
+ crc_ctx_pkt->u.bundling.dif_byte_count = cpu_to_le32(dif_bytes);
+ crc_ctx_pkt->u.bundling.dseg_count = cpu_to_le16(tot_dsds -
+ tot_prot_dsds);
+ cur_dsd = (uint32_t *) &crc_ctx_pkt->u.bundling.data_address;
+ }
+
+ /* Finish the common fields of CRC pkt */
+ crc_ctx_pkt->blk_size = cpu_to_le16(blk_size);
+ crc_ctx_pkt->prot_opts = cpu_to_le16(fw_prot_opts);
+ crc_ctx_pkt->byte_count = cpu_to_le32(data_bytes);
+ crc_ctx_pkt->guard_seed = __constant_cpu_to_le16(0);
+ /* Fibre channel byte count */
+ cmd_pkt->byte_count = cpu_to_le32(total_bytes);
+ fcp_dl = (uint32_t *)(crc_ctx_pkt->fcp_cmnd.cdb + 16 +
+ additional_fcpcdb_len);
+ *fcp_dl = htonl(total_bytes);
+
+ if (!data_bytes || cmd->sc_data_direction == DMA_NONE) {
+ cmd_pkt->byte_count = __constant_cpu_to_le32(0);
+ return QLA_SUCCESS;
+ }
+ /* Walks data segments */
+
+ cmd_pkt->control_flags |=
+ __constant_cpu_to_le16(CF_DATA_SEG_DESCR_ENABLE);
+
+ if (!bundling && tot_prot_dsds) {
+ if (qla24xx_walk_and_build_sglist_no_difb(ha, sp,
+ cur_dsd, tot_dsds))
+ goto crc_queuing_error;
+ } else if (qla24xx_walk_and_build_sglist(ha, sp, cur_dsd,
+ (tot_dsds - tot_prot_dsds)))
+ goto crc_queuing_error;
+
+ if (bundling && tot_prot_dsds) {
+ /* Walks dif segments */
+ cur_seg = scsi_prot_sglist(cmd);
+ cmd_pkt->control_flags |=
+ __constant_cpu_to_le16(CF_DIF_SEG_DESCR_ENABLE);
+ cur_dsd = (uint32_t *) &crc_ctx_pkt->u.bundling.dif_address;
+ if (qla24xx_walk_and_build_prot_sglist(ha, sp, cur_dsd,
+ tot_prot_dsds))
+ goto crc_queuing_error;
+ }
+ return QLA_SUCCESS;
+
+crc_queuing_error:
+ /* Cleanup will be performed by the caller */
+
+ return QLA_FUNCTION_FAILED;
+}
+
+/**
+ * qla24xx_start_scsi() - Send a SCSI command to the ISP
+ * @sp: command to send to the ISP
+ *
+ * Returns non-zero if a failure occurred, else zero.
+ */
+int
+qla24xx_start_scsi(srb_t *sp)
+{
+ int ret, nseg;
+ unsigned long flags;
+ uint32_t *clr_ptr;
+ uint32_t index;
+ uint32_t handle;
+ struct cmd_type_7 *cmd_pkt;
+ uint16_t cnt;
+ uint16_t req_cnt;
+ uint16_t tot_dsds;
+ struct req_que *req = NULL;
+ struct rsp_que *rsp = NULL;
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
+ struct scsi_qla_host *vha = sp->fcport->vha;
+ struct qla_hw_data *ha = vha->hw;
+ char tag[2];
+
+ /* Setup device pointers. */
+ ret = 0;
+
+ qla25xx_set_que(sp, &rsp);
+ req = vha->req;
+
+ /* So we know we haven't pci_map'ed anything yet */
+ tot_dsds = 0;
+
+ /* Send marker if required */
+ if (vha->marker_needed != 0) {
+ if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL) !=
+ QLA_SUCCESS)
+ return QLA_FUNCTION_FAILED;
+ vha->marker_needed = 0;
+ }
+
+ /* Acquire ring specific lock */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Check for room in outstanding command list. */
+ handle = req->current_outstanding_cmd;
+ for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) {
+ handle++;
+ if (handle == MAX_OUTSTANDING_COMMANDS)
+ handle = 1;
+ if (!req->outstanding_cmds[handle])
+ break;
+ }
+ if (index == MAX_OUTSTANDING_COMMANDS) {
+ goto queuing_error;
+ }
+
+ /* Map the sg table so we have an accurate count of sg entries needed */
+ if (scsi_sg_count(cmd)) {
+ nseg = dma_map_sg(&ha->pdev->dev, scsi_sglist(cmd),
+ scsi_sg_count(cmd), cmd->sc_data_direction);
+ if (unlikely(!nseg))
+ goto queuing_error;
+ } else
+ nseg = 0;
+
+ tot_dsds = nseg;
+ req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
+ if (req->cnt < (req_cnt + 2)) {
+ cnt = RD_REG_DWORD_RELAXED(req->req_q_out);
+
+ if (req->ring_index < cnt)
+ req->cnt = cnt - req->ring_index;
+ else
+ req->cnt = req->length -
+ (req->ring_index - cnt);
+ }
+ if (req->cnt < (req_cnt + 2))
+ goto queuing_error;
+
+ /* Build command packet. */
+ req->current_outstanding_cmd = handle;
+ req->outstanding_cmds[handle] = sp;
+ sp->handle = handle;
+ cmd->host_scribble = (unsigned char *)(unsigned long)handle;
+ req->cnt -= req_cnt;
+
+ cmd_pkt = (struct cmd_type_7 *)req->ring_ptr;
+ cmd_pkt->handle = MAKE_HANDLE(req->id, handle);
+
+ /* Zero out remaining portion of packet. */
+ /* tagged queuing modifier -- default is TSK_SIMPLE (0). */
+ clr_ptr = (uint32_t *)cmd_pkt + 2;
+ memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
+ cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
+
+ /* Set NPORT-ID and LUN number*/
+ cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+ cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
+ cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
+ cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
+ cmd_pkt->vp_index = sp->fcport->vp_idx;
+
+ int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
+ host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun));
+
+ /* Update tagged queuing modifier -- default is TSK_SIMPLE (0). */
+ if (scsi_populate_tag_msg(cmd, tag)) {
+ switch (tag[0]) {
+ case HEAD_OF_QUEUE_TAG:
+ cmd_pkt->task = TSK_HEAD_OF_QUEUE;
+ break;
+ case ORDERED_QUEUE_TAG:
+ cmd_pkt->task = TSK_ORDERED;
+ break;
+ }
+ }
+
+ /* Load SCSI command packet. */
+ memcpy(cmd_pkt->fcp_cdb, cmd->cmnd, cmd->cmd_len);
+ host_to_fcp_swap(cmd_pkt->fcp_cdb, sizeof(cmd_pkt->fcp_cdb));
+
+ cmd_pkt->byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd));
+
+ /* Build IOCB segments */
+ qla24xx_build_scsi_iocbs(sp, cmd_pkt, tot_dsds);
+
+ /* Set total data segment count. */
+ cmd_pkt->entry_count = (uint8_t)req_cnt;
+ /* Specify response queue number where completion should happen */
+ cmd_pkt->entry_status = (uint8_t) rsp->id;
+ wmb();
+ /* Adjust ring index. */
+ req->ring_index++;
+ if (req->ring_index == req->length) {
+ req->ring_index = 0;
+ req->ring_ptr = req->ring;
+ } else
+ req->ring_ptr++;
+
+ sp->flags |= SRB_DMA_VALID;
+
+ /* Set chip new ring index. */
+ WRT_REG_DWORD(req->req_q_in, req->ring_index);
+ RD_REG_DWORD_RELAXED(&ha->iobase->isp24.hccr);
+
+ /* Manage unprocessed RIO/ZIO commands in response queue. */
+ if (vha->flags.process_response_queue &&
+ rsp->ring_ptr->signature != RESPONSE_PROCESSED)
+ qla24xx_process_response_queue(vha, rsp);
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ return QLA_SUCCESS;
+
+queuing_error:
+ if (tot_dsds)
+ scsi_dma_unmap(cmd);
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return QLA_FUNCTION_FAILED;
+}
+
+
+/**
+ * qla24xx_dif_start_scsi() - Send a SCSI command to the ISP
+ * @sp: command to send to the ISP
+ *
+ * Returns non-zero if a failure occurred, else zero.
+ */
+int
+qla24xx_dif_start_scsi(srb_t *sp)
+{
+ int nseg;
+ unsigned long flags;
+ uint32_t *clr_ptr;
+ uint32_t index;
+ uint32_t handle;
+ uint16_t cnt;
+ uint16_t req_cnt = 0;
+ uint16_t tot_dsds;
+ uint16_t tot_prot_dsds;
+ uint16_t fw_prot_opts = 0;
+ struct req_que *req = NULL;
+ struct rsp_que *rsp = NULL;
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
+ struct scsi_qla_host *vha = sp->fcport->vha;
+ struct qla_hw_data *ha = vha->hw;
+ struct cmd_type_crc_2 *cmd_pkt;
+ uint32_t status = 0;
+
+#define QDSS_GOT_Q_SPACE BIT_0
+
+ /* Only process protection or >16 cdb in this routine */
+ if (scsi_get_prot_op(cmd) == SCSI_PROT_NORMAL) {
+ if (cmd->cmd_len <= 16)
+ return qla24xx_start_scsi(sp);
+ }
+
+ /* Setup device pointers. */
+
+ qla25xx_set_que(sp, &rsp);
+ req = vha->req;
+
+ /* So we know we haven't pci_map'ed anything yet */
+ tot_dsds = 0;
+
+ /* Send marker if required */
+ if (vha->marker_needed != 0) {
+ if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL) !=
+ QLA_SUCCESS)
+ return QLA_FUNCTION_FAILED;
+ vha->marker_needed = 0;
+ }
+
+ /* Acquire ring specific lock */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Check for room in outstanding command list. */
+ handle = req->current_outstanding_cmd;
+ for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) {
+ handle++;
+ if (handle == MAX_OUTSTANDING_COMMANDS)
+ handle = 1;
+ if (!req->outstanding_cmds[handle])
+ break;
+ }
+
+ if (index == MAX_OUTSTANDING_COMMANDS)
+ goto queuing_error;
+
+ /* Compute number of required data segments */
+ /* Map the sg table so we have an accurate count of sg entries needed */
+ if (scsi_sg_count(cmd)) {
+ nseg = dma_map_sg(&ha->pdev->dev, scsi_sglist(cmd),
+ scsi_sg_count(cmd), cmd->sc_data_direction);
+ if (unlikely(!nseg))
+ goto queuing_error;
+ else
+ sp->flags |= SRB_DMA_VALID;
+
+ if ((scsi_get_prot_op(cmd) == SCSI_PROT_READ_INSERT) ||
+ (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_STRIP)) {
+ struct qla2_sgx sgx;
+ uint32_t partial;
+
+ memset(&sgx, 0, sizeof(struct qla2_sgx));
+ sgx.tot_bytes = scsi_bufflen(cmd);
+ sgx.cur_sg = scsi_sglist(cmd);
+ sgx.sp = sp;
+
+ nseg = 0;
+ while (qla24xx_get_one_block_sg(
+ cmd->device->sector_size, &sgx, &partial))
+ nseg++;
+ }
+ } else
+ nseg = 0;
+
+ /* number of required data segments */
+ tot_dsds = nseg;
+
+ /* Compute number of required protection segments */
+ if (qla24xx_configure_prot_mode(sp, &fw_prot_opts)) {
+ nseg = dma_map_sg(&ha->pdev->dev, scsi_prot_sglist(cmd),
+ scsi_prot_sg_count(cmd), cmd->sc_data_direction);
+ if (unlikely(!nseg))
+ goto queuing_error;
+ else
+ sp->flags |= SRB_CRC_PROT_DMA_VALID;
+
+ if ((scsi_get_prot_op(cmd) == SCSI_PROT_READ_INSERT) ||
+ (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_STRIP)) {
+ nseg = scsi_bufflen(cmd) / cmd->device->sector_size;
+ }
+ } else {
+ nseg = 0;
+ }
+
+ req_cnt = 1;
+ /* Total Data and protection sg segment(s) */
+ tot_prot_dsds = nseg;
+ tot_dsds += nseg;
+ if (req->cnt < (req_cnt + 2)) {
+ cnt = RD_REG_DWORD_RELAXED(req->req_q_out);
+
+ if (req->ring_index < cnt)
+ req->cnt = cnt - req->ring_index;
+ else
+ req->cnt = req->length -
+ (req->ring_index - cnt);
+ }
+
+ if (req->cnt < (req_cnt + 2))
+ goto queuing_error;
+
+ status |= QDSS_GOT_Q_SPACE;
+
+ /* Build header part of command packet (excluding the OPCODE). */
+ req->current_outstanding_cmd = handle;
+ req->outstanding_cmds[handle] = sp;
+ sp->handle = handle;
+ cmd->host_scribble = (unsigned char *)(unsigned long)handle;
+ req->cnt -= req_cnt;
+
+ /* Fill-in common area */
+ cmd_pkt = (struct cmd_type_crc_2 *)req->ring_ptr;
+ cmd_pkt->handle = MAKE_HANDLE(req->id, handle);
+
+ clr_ptr = (uint32_t *)cmd_pkt + 2;
+ memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
+
+ /* Set NPORT-ID and LUN number*/
+ cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+ cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
+ cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
+ cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
+
+ int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
+ host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun));
+
+ /* Total Data and protection segment(s) */
+ cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
+
+ /* Build IOCB segments and adjust for data protection segments */
+ if (qla24xx_build_scsi_crc_2_iocbs(sp, (struct cmd_type_crc_2 *)
+ req->ring_ptr, tot_dsds, tot_prot_dsds, fw_prot_opts) !=
+ QLA_SUCCESS)
+ goto queuing_error;
+
+ cmd_pkt->entry_count = (uint8_t)req_cnt;
+ /* Specify response queue number where completion should happen */
+ cmd_pkt->entry_status = (uint8_t) rsp->id;
+ cmd_pkt->timeout = __constant_cpu_to_le16(0);
+ wmb();
+
+ /* Adjust ring index. */
+ req->ring_index++;
+ if (req->ring_index == req->length) {
+ req->ring_index = 0;
+ req->ring_ptr = req->ring;
+ } else
+ req->ring_ptr++;
+
+ /* Set chip new ring index. */
+ WRT_REG_DWORD(req->req_q_in, req->ring_index);
+ RD_REG_DWORD_RELAXED(&ha->iobase->isp24.hccr);
+
+ /* Manage unprocessed RIO/ZIO commands in response queue. */
+ if (vha->flags.process_response_queue &&
+ rsp->ring_ptr->signature != RESPONSE_PROCESSED)
+ qla24xx_process_response_queue(vha, rsp);
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return QLA_SUCCESS;
+
+queuing_error:
+ if (status & QDSS_GOT_Q_SPACE) {
+ req->outstanding_cmds[handle] = NULL;
+ req->cnt += req_cnt;
+ }
+ /* Cleanup will be performed by the caller (queuecommand) */
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ return QLA_FUNCTION_FAILED;
+}
+
+
+static void qla25xx_set_que(srb_t *sp, struct rsp_que **rsp)
+{
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
+ struct qla_hw_data *ha = sp->fcport->vha->hw;
+ int affinity = cmd->request->cpu;
+
+ if (ha->flags.cpu_affinity_enabled && affinity >= 0 &&
+ affinity < ha->max_rsp_queues - 1)
+ *rsp = ha->rsp_q_map[affinity + 1];
+ else
+ *rsp = ha->rsp_q_map[0];
+}
+
+/* Generic Control-SRB manipulation functions. */
+void *
+qla2x00_alloc_iocbs(scsi_qla_host_t *vha, srb_t *sp)
+{
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+ device_reg_t __iomem *reg = ISP_QUE_REG(ha, req->id);
+ uint32_t index, handle;
+ request_t *pkt;
+ uint16_t cnt, req_cnt;
+
+ pkt = NULL;
+ req_cnt = 1;
+ handle = 0;
+
+ if (!sp)
+ goto skip_cmd_array;
+
+ /* Check for room in outstanding command list. */
+ handle = req->current_outstanding_cmd;
+ for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) {
+ handle++;
+ if (handle == MAX_OUTSTANDING_COMMANDS)
+ handle = 1;
+ if (!req->outstanding_cmds[handle])
+ break;
+ }
+ if (index == MAX_OUTSTANDING_COMMANDS) {
+ ql_log(ql_log_warn, vha, 0x700b,
+ "No room on oustanding cmd array.\n");
+ goto queuing_error;
+ }
+
+ /* Prep command array. */
+ req->current_outstanding_cmd = handle;
+ req->outstanding_cmds[handle] = sp;
+ sp->handle = handle;
+
+ /* Adjust entry-counts as needed. */
+ if (sp->type != SRB_SCSI_CMD)
+ req_cnt = sp->iocbs;
+
+skip_cmd_array:
+ /* Check for room on request queue. */
+ if (req->cnt < req_cnt) {
+ if (ha->mqenable || IS_QLA83XX(ha))
+ cnt = RD_REG_DWORD(&reg->isp25mq.req_q_out);
+ else if (IS_QLA82XX(ha))
+ cnt = RD_REG_DWORD(&reg->isp82.req_q_out);
+ else if (IS_FWI2_CAPABLE(ha))
+ cnt = RD_REG_DWORD(&reg->isp24.req_q_out);
+ else
+ cnt = qla2x00_debounce_register(
+ ISP_REQ_Q_OUT(ha, &reg->isp));
+
+ if (req->ring_index < cnt)
+ req->cnt = cnt - req->ring_index;
+ else
+ req->cnt = req->length -
+ (req->ring_index - cnt);
+ }
+ if (req->cnt < req_cnt)
+ goto queuing_error;
+
+ /* Prep packet */
+ req->cnt -= req_cnt;
+ pkt = req->ring_ptr;
+ memset(pkt, 0, REQUEST_ENTRY_SIZE);
+ pkt->entry_count = req_cnt;
+ pkt->handle = handle;
+
+queuing_error:
+ return pkt;
+}
+
+static void
+qla24xx_login_iocb(srb_t *sp, struct logio_entry_24xx *logio)
+{
+ struct srb_iocb *lio = &sp->u.iocb_cmd;
+
+ logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
+ logio->control_flags = cpu_to_le16(LCF_COMMAND_PLOGI);
+ if (lio->u.logio.flags & SRB_LOGIN_COND_PLOGI)
+ logio->control_flags |= cpu_to_le16(LCF_COND_PLOGI);
+ if (lio->u.logio.flags & SRB_LOGIN_SKIP_PRLI)
+ logio->control_flags |= cpu_to_le16(LCF_SKIP_PRLI);
+ logio->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+ logio->port_id[0] = sp->fcport->d_id.b.al_pa;
+ logio->port_id[1] = sp->fcport->d_id.b.area;
+ logio->port_id[2] = sp->fcport->d_id.b.domain;
+ logio->vp_index = sp->fcport->vp_idx;
+}
+
+static void
+qla2x00_login_iocb(srb_t *sp, struct mbx_entry *mbx)
+{
+ struct qla_hw_data *ha = sp->fcport->vha->hw;
+ struct srb_iocb *lio = &sp->u.iocb_cmd;
+ uint16_t opts;
+
+ mbx->entry_type = MBX_IOCB_TYPE;
+ SET_TARGET_ID(ha, mbx->loop_id, sp->fcport->loop_id);
+ mbx->mb0 = cpu_to_le16(MBC_LOGIN_FABRIC_PORT);
+ opts = lio->u.logio.flags & SRB_LOGIN_COND_PLOGI ? BIT_0 : 0;
+ opts |= lio->u.logio.flags & SRB_LOGIN_SKIP_PRLI ? BIT_1 : 0;
+ if (HAS_EXTENDED_IDS(ha)) {
+ mbx->mb1 = cpu_to_le16(sp->fcport->loop_id);
+ mbx->mb10 = cpu_to_le16(opts);
+ } else {
+ mbx->mb1 = cpu_to_le16((sp->fcport->loop_id << 8) | opts);
+ }
+ mbx->mb2 = cpu_to_le16(sp->fcport->d_id.b.domain);
+ mbx->mb3 = cpu_to_le16(sp->fcport->d_id.b.area << 8 |
+ sp->fcport->d_id.b.al_pa);
+ mbx->mb9 = cpu_to_le16(sp->fcport->vp_idx);
+}
+
+static void
+qla24xx_logout_iocb(srb_t *sp, struct logio_entry_24xx *logio)
+{
+ logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
+ logio->control_flags =
+ cpu_to_le16(LCF_COMMAND_LOGO|LCF_IMPL_LOGO);
+ logio->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+ logio->port_id[0] = sp->fcport->d_id.b.al_pa;
+ logio->port_id[1] = sp->fcport->d_id.b.area;
+ logio->port_id[2] = sp->fcport->d_id.b.domain;
+ logio->vp_index = sp->fcport->vp_idx;
+}
+
+static void
+qla2x00_logout_iocb(srb_t *sp, struct mbx_entry *mbx)
+{
+ struct qla_hw_data *ha = sp->fcport->vha->hw;
+
+ mbx->entry_type = MBX_IOCB_TYPE;
+ SET_TARGET_ID(ha, mbx->loop_id, sp->fcport->loop_id);
+ mbx->mb0 = cpu_to_le16(MBC_LOGOUT_FABRIC_PORT);
+ mbx->mb1 = HAS_EXTENDED_IDS(ha) ?
+ cpu_to_le16(sp->fcport->loop_id):
+ cpu_to_le16(sp->fcport->loop_id << 8);
+ mbx->mb2 = cpu_to_le16(sp->fcport->d_id.b.domain);
+ mbx->mb3 = cpu_to_le16(sp->fcport->d_id.b.area << 8 |
+ sp->fcport->d_id.b.al_pa);
+ mbx->mb9 = cpu_to_le16(sp->fcport->vp_idx);
+ /* Implicit: mbx->mbx10 = 0. */
+}
+
+static void
+qla24xx_adisc_iocb(srb_t *sp, struct logio_entry_24xx *logio)
+{
+ logio->entry_type = LOGINOUT_PORT_IOCB_TYPE;
+ logio->control_flags = cpu_to_le16(LCF_COMMAND_ADISC);
+ logio->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+ logio->vp_index = sp->fcport->vp_idx;
+}
+
+static void
+qla2x00_adisc_iocb(srb_t *sp, struct mbx_entry *mbx)
+{
+ struct qla_hw_data *ha = sp->fcport->vha->hw;
+
+ mbx->entry_type = MBX_IOCB_TYPE;
+ SET_TARGET_ID(ha, mbx->loop_id, sp->fcport->loop_id);
+ mbx->mb0 = cpu_to_le16(MBC_GET_PORT_DATABASE);
+ if (HAS_EXTENDED_IDS(ha)) {
+ mbx->mb1 = cpu_to_le16(sp->fcport->loop_id);
+ mbx->mb10 = cpu_to_le16(BIT_0);
+ } else {
+ mbx->mb1 = cpu_to_le16((sp->fcport->loop_id << 8) | BIT_0);
+ }
+ mbx->mb2 = cpu_to_le16(MSW(ha->async_pd_dma));
+ mbx->mb3 = cpu_to_le16(LSW(ha->async_pd_dma));
+ mbx->mb6 = cpu_to_le16(MSW(MSD(ha->async_pd_dma)));
+ mbx->mb7 = cpu_to_le16(LSW(MSD(ha->async_pd_dma)));
+ mbx->mb9 = cpu_to_le16(sp->fcport->vp_idx);
+}
+
+static void
+qla24xx_tm_iocb(srb_t *sp, struct tsk_mgmt_entry *tsk)
+{
+ uint32_t flags;
+ unsigned int lun;
+ struct fc_port *fcport = sp->fcport;
+ scsi_qla_host_t *vha = fcport->vha;
+ struct qla_hw_data *ha = vha->hw;
+ struct srb_iocb *iocb = &sp->u.iocb_cmd;
+ struct req_que *req = vha->req;
+
+ flags = iocb->u.tmf.flags;
+ lun = iocb->u.tmf.lun;
+
+ tsk->entry_type = TSK_MGMT_IOCB_TYPE;
+ tsk->entry_count = 1;
+ tsk->handle = MAKE_HANDLE(req->id, tsk->handle);
+ tsk->nport_handle = cpu_to_le16(fcport->loop_id);
+ tsk->timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
+ tsk->control_flags = cpu_to_le32(flags);
+ tsk->port_id[0] = fcport->d_id.b.al_pa;
+ tsk->port_id[1] = fcport->d_id.b.area;
+ tsk->port_id[2] = fcport->d_id.b.domain;
+ tsk->vp_index = fcport->vp_idx;
+
+ if (flags == TCF_LUN_RESET) {
+ int_to_scsilun(lun, &tsk->lun);
+ host_to_fcp_swap((uint8_t *)&tsk->lun,
+ sizeof(tsk->lun));
+ }
+}
+
+static void
+qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb)
+{
+ struct fc_bsg_job *bsg_job = sp->u.bsg_job;
+
+ els_iocb->entry_type = ELS_IOCB_TYPE;
+ els_iocb->entry_count = 1;
+ els_iocb->sys_define = 0;
+ els_iocb->entry_status = 0;
+ els_iocb->handle = sp->handle;
+ els_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+ els_iocb->tx_dsd_count = __constant_cpu_to_le16(bsg_job->request_payload.sg_cnt);
+ els_iocb->vp_index = sp->fcport->vp_idx;
+ els_iocb->sof_type = EST_SOFI3;
+ els_iocb->rx_dsd_count = __constant_cpu_to_le16(bsg_job->reply_payload.sg_cnt);
+
+ els_iocb->opcode =
+ sp->type == SRB_ELS_CMD_RPT ?
+ bsg_job->request->rqst_data.r_els.els_code :
+ bsg_job->request->rqst_data.h_els.command_code;
+ els_iocb->port_id[0] = sp->fcport->d_id.b.al_pa;
+ els_iocb->port_id[1] = sp->fcport->d_id.b.area;
+ els_iocb->port_id[2] = sp->fcport->d_id.b.domain;
+ els_iocb->control_flags = 0;
+ els_iocb->rx_byte_count =
+ cpu_to_le32(bsg_job->reply_payload.payload_len);
+ els_iocb->tx_byte_count =
+ cpu_to_le32(bsg_job->request_payload.payload_len);
+
+ els_iocb->tx_address[0] = cpu_to_le32(LSD(sg_dma_address
+ (bsg_job->request_payload.sg_list)));
+ els_iocb->tx_address[1] = cpu_to_le32(MSD(sg_dma_address
+ (bsg_job->request_payload.sg_list)));
+ els_iocb->tx_len = cpu_to_le32(sg_dma_len
+ (bsg_job->request_payload.sg_list));
+
+ els_iocb->rx_address[0] = cpu_to_le32(LSD(sg_dma_address
+ (bsg_job->reply_payload.sg_list)));
+ els_iocb->rx_address[1] = cpu_to_le32(MSD(sg_dma_address
+ (bsg_job->reply_payload.sg_list)));
+ els_iocb->rx_len = cpu_to_le32(sg_dma_len
+ (bsg_job->reply_payload.sg_list));
+}
+
+static void
+qla2x00_ct_iocb(srb_t *sp, ms_iocb_entry_t *ct_iocb)
+{
+ uint16_t avail_dsds;
+ uint32_t *cur_dsd;
+ struct scatterlist *sg;
+ int index;
+ uint16_t tot_dsds;
+ scsi_qla_host_t *vha = sp->fcport->vha;
+ struct qla_hw_data *ha = vha->hw;
+ struct fc_bsg_job *bsg_job = sp->u.bsg_job;
+ int loop_iterartion = 0;
+ int cont_iocb_prsnt = 0;
+ int entry_count = 1;
+
+ memset(ct_iocb, 0, sizeof(ms_iocb_entry_t));
+ ct_iocb->entry_type = CT_IOCB_TYPE;
+ ct_iocb->entry_status = 0;
+ ct_iocb->handle1 = sp->handle;
+ SET_TARGET_ID(ha, ct_iocb->loop_id, sp->fcport->loop_id);
+ ct_iocb->status = __constant_cpu_to_le16(0);
+ ct_iocb->control_flags = __constant_cpu_to_le16(0);
+ ct_iocb->timeout = 0;
+ ct_iocb->cmd_dsd_count =
+ __constant_cpu_to_le16(bsg_job->request_payload.sg_cnt);
+ ct_iocb->total_dsd_count =
+ __constant_cpu_to_le16(bsg_job->request_payload.sg_cnt + 1);
+ ct_iocb->req_bytecount =
+ cpu_to_le32(bsg_job->request_payload.payload_len);
+ ct_iocb->rsp_bytecount =
+ cpu_to_le32(bsg_job->reply_payload.payload_len);
+
+ ct_iocb->dseg_req_address[0] = cpu_to_le32(LSD(sg_dma_address
+ (bsg_job->request_payload.sg_list)));
+ ct_iocb->dseg_req_address[1] = cpu_to_le32(MSD(sg_dma_address
+ (bsg_job->request_payload.sg_list)));
+ ct_iocb->dseg_req_length = ct_iocb->req_bytecount;
+
+ ct_iocb->dseg_rsp_address[0] = cpu_to_le32(LSD(sg_dma_address
+ (bsg_job->reply_payload.sg_list)));
+ ct_iocb->dseg_rsp_address[1] = cpu_to_le32(MSD(sg_dma_address
+ (bsg_job->reply_payload.sg_list)));
+ ct_iocb->dseg_rsp_length = ct_iocb->rsp_bytecount;
+
+ avail_dsds = 1;
+ cur_dsd = (uint32_t *)ct_iocb->dseg_rsp_address;
+ index = 0;
+ tot_dsds = bsg_job->reply_payload.sg_cnt;
+
+ for_each_sg(bsg_job->reply_payload.sg_list, sg, tot_dsds, index) {
+ dma_addr_t sle_dma;
+ cont_a64_entry_t *cont_pkt;
+
+ /* Allocate additional continuation packets? */
+ if (avail_dsds == 0) {
+ /*
+ * Five DSDs are available in the Cont.
+ * Type 1 IOCB.
+ */
+ cont_pkt = qla2x00_prep_cont_type1_iocb(vha,
+ vha->hw->req_q_map[0]);
+ cur_dsd = (uint32_t *) cont_pkt->dseg_0_address;
+ avail_dsds = 5;
+ cont_iocb_prsnt = 1;
+ entry_count++;
+ }
+
+ sle_dma = sg_dma_address(sg);
+ *cur_dsd++ = cpu_to_le32(LSD(sle_dma));
+ *cur_dsd++ = cpu_to_le32(MSD(sle_dma));
+ *cur_dsd++ = cpu_to_le32(sg_dma_len(sg));
+ loop_iterartion++;
+ avail_dsds--;
+ }
+ ct_iocb->entry_count = entry_count;
+}
+
+static void
+qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb)
+{
+ uint16_t avail_dsds;
+ uint32_t *cur_dsd;
+ struct scatterlist *sg;
+ int index;
+ uint16_t tot_dsds;
+ scsi_qla_host_t *vha = sp->fcport->vha;
+ struct qla_hw_data *ha = vha->hw;
+ struct fc_bsg_job *bsg_job = sp->u.bsg_job;
+ int loop_iterartion = 0;
+ int cont_iocb_prsnt = 0;
+ int entry_count = 1;
+
+ ct_iocb->entry_type = CT_IOCB_TYPE;
+ ct_iocb->entry_status = 0;
+ ct_iocb->sys_define = 0;
+ ct_iocb->handle = sp->handle;
+
+ ct_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+ ct_iocb->vp_index = sp->fcport->vp_idx;
+ ct_iocb->comp_status = __constant_cpu_to_le16(0);
+
+ ct_iocb->cmd_dsd_count =
+ __constant_cpu_to_le16(bsg_job->request_payload.sg_cnt);
+ ct_iocb->timeout = 0;
+ ct_iocb->rsp_dsd_count =
+ __constant_cpu_to_le16(bsg_job->reply_payload.sg_cnt);
+ ct_iocb->rsp_byte_count =
+ cpu_to_le32(bsg_job->reply_payload.payload_len);
+ ct_iocb->cmd_byte_count =
+ cpu_to_le32(bsg_job->request_payload.payload_len);
+ ct_iocb->dseg_0_address[0] = cpu_to_le32(LSD(sg_dma_address
+ (bsg_job->request_payload.sg_list)));
+ ct_iocb->dseg_0_address[1] = cpu_to_le32(MSD(sg_dma_address
+ (bsg_job->request_payload.sg_list)));
+ ct_iocb->dseg_0_len = cpu_to_le32(sg_dma_len
+ (bsg_job->request_payload.sg_list));
+
+ avail_dsds = 1;
+ cur_dsd = (uint32_t *)ct_iocb->dseg_1_address;
+ index = 0;
+ tot_dsds = bsg_job->reply_payload.sg_cnt;
+
+ for_each_sg(bsg_job->reply_payload.sg_list, sg, tot_dsds, index) {
+ dma_addr_t sle_dma;
+ cont_a64_entry_t *cont_pkt;
+
+ /* Allocate additional continuation packets? */
+ if (avail_dsds == 0) {
+ /*
+ * Five DSDs are available in the Cont.
+ * Type 1 IOCB.
+ */
+ cont_pkt = qla2x00_prep_cont_type1_iocb(vha,
+ ha->req_q_map[0]);
+ cur_dsd = (uint32_t *) cont_pkt->dseg_0_address;
+ avail_dsds = 5;
+ cont_iocb_prsnt = 1;
+ entry_count++;
+ }
+
+ sle_dma = sg_dma_address(sg);
+ *cur_dsd++ = cpu_to_le32(LSD(sle_dma));
+ *cur_dsd++ = cpu_to_le32(MSD(sle_dma));
+ *cur_dsd++ = cpu_to_le32(sg_dma_len(sg));
+ loop_iterartion++;
+ avail_dsds--;
+ }
+ ct_iocb->entry_count = entry_count;
+}
+
+/*
+ * qla82xx_start_scsi() - Send a SCSI command to the ISP
+ * @sp: command to send to the ISP
+ *
+ * Returns non-zero if a failure occurred, else zero.
+ */
+int
+qla82xx_start_scsi(srb_t *sp)
+{
+ int ret, nseg;
+ unsigned long flags;
+ struct scsi_cmnd *cmd;
+ uint32_t *clr_ptr;
+ uint32_t index;
+ uint32_t handle;
+ uint16_t cnt;
+ uint16_t req_cnt;
+ uint16_t tot_dsds;
+ struct device_reg_82xx __iomem *reg;
+ uint32_t dbval;
+ uint32_t *fcp_dl;
+ uint8_t additional_cdb_len;
+ struct ct6_dsd *ctx;
+ struct scsi_qla_host *vha = sp->fcport->vha;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = NULL;
+ struct rsp_que *rsp = NULL;
+ char tag[2];
+
+ /* Setup device pointers. */
+ ret = 0;
+ reg = &ha->iobase->isp82;
+ cmd = GET_CMD_SP(sp);
+ req = vha->req;
+ rsp = ha->rsp_q_map[0];
+
+ /* So we know we haven't pci_map'ed anything yet */
+ tot_dsds = 0;
+
+ dbval = 0x04 | (ha->portnum << 5);
+
+ /* Send marker if required */
+ if (vha->marker_needed != 0) {
+ if (qla2x00_marker(vha, req,
+ rsp, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x300c,
+ "qla2x00_marker failed for cmd=%p.\n", cmd);
+ return QLA_FUNCTION_FAILED;
+ }
+ vha->marker_needed = 0;
+ }
+
+ /* Acquire ring specific lock */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Check for room in outstanding command list. */
+ handle = req->current_outstanding_cmd;
+ for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) {
+ handle++;
+ if (handle == MAX_OUTSTANDING_COMMANDS)
+ handle = 1;
+ if (!req->outstanding_cmds[handle])
+ break;
+ }
+ if (index == MAX_OUTSTANDING_COMMANDS)
+ goto queuing_error;
+
+ /* Map the sg table so we have an accurate count of sg entries needed */
+ if (scsi_sg_count(cmd)) {
+ nseg = dma_map_sg(&ha->pdev->dev, scsi_sglist(cmd),
+ scsi_sg_count(cmd), cmd->sc_data_direction);
+ if (unlikely(!nseg))
+ goto queuing_error;
+ } else
+ nseg = 0;
+
+ tot_dsds = nseg;
+
+ if (tot_dsds > ql2xshiftctondsd) {
+ struct cmd_type_6 *cmd_pkt;
+ uint16_t more_dsd_lists = 0;
+ struct dsd_dma *dsd_ptr;
+ uint16_t i;
+
+ more_dsd_lists = qla24xx_calc_dsd_lists(tot_dsds);
+ if ((more_dsd_lists + ha->gbl_dsd_inuse) >= NUM_DSD_CHAIN) {
+ ql_dbg(ql_dbg_io, vha, 0x300d,
+ "Num of DSD list %d is than %d for cmd=%p.\n",
+ more_dsd_lists + ha->gbl_dsd_inuse, NUM_DSD_CHAIN,
+ cmd);
+ goto queuing_error;
+ }
+
+ if (more_dsd_lists <= ha->gbl_dsd_avail)
+ goto sufficient_dsds;
+ else
+ more_dsd_lists -= ha->gbl_dsd_avail;
+
+ for (i = 0; i < more_dsd_lists; i++) {
+ dsd_ptr = kzalloc(sizeof(struct dsd_dma), GFP_ATOMIC);
+ if (!dsd_ptr) {
+ ql_log(ql_log_fatal, vha, 0x300e,
+ "Failed to allocate memory for dsd_dma "
+ "for cmd=%p.\n", cmd);
+ goto queuing_error;
+ }
+
+ dsd_ptr->dsd_addr = dma_pool_alloc(ha->dl_dma_pool,
+ GFP_ATOMIC, &dsd_ptr->dsd_list_dma);
+ if (!dsd_ptr->dsd_addr) {
+ kfree(dsd_ptr);
+ ql_log(ql_log_fatal, vha, 0x300f,
+ "Failed to allocate memory for dsd_addr "
+ "for cmd=%p.\n", cmd);
+ goto queuing_error;
+ }
+ list_add_tail(&dsd_ptr->list, &ha->gbl_dsd_list);
+ ha->gbl_dsd_avail++;
+ }
+
+sufficient_dsds:
+ req_cnt = 1;
+
+ if (req->cnt < (req_cnt + 2)) {
+ cnt = (uint16_t)RD_REG_DWORD_RELAXED(
+ &reg->req_q_out[0]);
+ if (req->ring_index < cnt)
+ req->cnt = cnt - req->ring_index;
+ else
+ req->cnt = req->length -
+ (req->ring_index - cnt);
+ }
+
+ if (req->cnt < (req_cnt + 2))
+ goto queuing_error;
+
+ ctx = sp->u.scmd.ctx =
+ mempool_alloc(ha->ctx_mempool, GFP_ATOMIC);
+ if (!ctx) {
+ ql_log(ql_log_fatal, vha, 0x3010,
+ "Failed to allocate ctx for cmd=%p.\n", cmd);
+ goto queuing_error;
+ }
+
+ memset(ctx, 0, sizeof(struct ct6_dsd));
+ ctx->fcp_cmnd = dma_pool_alloc(ha->fcp_cmnd_dma_pool,
+ GFP_ATOMIC, &ctx->fcp_cmnd_dma);
+ if (!ctx->fcp_cmnd) {
+ ql_log(ql_log_fatal, vha, 0x3011,
+ "Failed to allocate fcp_cmnd for cmd=%p.\n", cmd);
+ goto queuing_error_fcp_cmnd;
+ }
+
+ /* Initialize the DSD list and dma handle */
+ INIT_LIST_HEAD(&ctx->dsd_list);
+ ctx->dsd_use_cnt = 0;
+
+ if (cmd->cmd_len > 16) {
+ additional_cdb_len = cmd->cmd_len - 16;
+ if ((cmd->cmd_len % 4) != 0) {
+ /* SCSI command bigger than 16 bytes must be
+ * multiple of 4
+ */
+ ql_log(ql_log_warn, vha, 0x3012,
+ "scsi cmd len %d not multiple of 4 "
+ "for cmd=%p.\n", cmd->cmd_len, cmd);
+ goto queuing_error_fcp_cmnd;
+ }
+ ctx->fcp_cmnd_len = 12 + cmd->cmd_len + 4;
+ } else {
+ additional_cdb_len = 0;
+ ctx->fcp_cmnd_len = 12 + 16 + 4;
+ }
+
+ cmd_pkt = (struct cmd_type_6 *)req->ring_ptr;
+ cmd_pkt->handle = MAKE_HANDLE(req->id, handle);
+
+ /* Zero out remaining portion of packet. */
+ /* tagged queuing modifier -- default is TSK_SIMPLE (0). */
+ clr_ptr = (uint32_t *)cmd_pkt + 2;
+ memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
+ cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
+
+ /* Set NPORT-ID and LUN number*/
+ cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+ cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
+ cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
+ cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
+ cmd_pkt->vp_index = sp->fcport->vp_idx;
+
+ /* Build IOCB segments */
+ if (qla24xx_build_scsi_type_6_iocbs(sp, cmd_pkt, tot_dsds))
+ goto queuing_error_fcp_cmnd;
+
+ int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
+ host_to_fcp_swap((uint8_t *)&cmd_pkt->lun, sizeof(cmd_pkt->lun));
+
+ /* build FCP_CMND IU */
+ memset(ctx->fcp_cmnd, 0, sizeof(struct fcp_cmnd));
+ int_to_scsilun(cmd->device->lun, &ctx->fcp_cmnd->lun);
+ ctx->fcp_cmnd->additional_cdb_len = additional_cdb_len;
+
+ if (cmd->sc_data_direction == DMA_TO_DEVICE)
+ ctx->fcp_cmnd->additional_cdb_len |= 1;
+ else if (cmd->sc_data_direction == DMA_FROM_DEVICE)
+ ctx->fcp_cmnd->additional_cdb_len |= 2;
+
+ /*
+ * Update tagged queuing modifier -- default is TSK_SIMPLE (0).
+ */
+ if (scsi_populate_tag_msg(cmd, tag)) {
+ switch (tag[0]) {
+ case HEAD_OF_QUEUE_TAG:
+ ctx->fcp_cmnd->task_attribute =
+ TSK_HEAD_OF_QUEUE;
+ break;
+ case ORDERED_QUEUE_TAG:
+ ctx->fcp_cmnd->task_attribute =
+ TSK_ORDERED;
+ break;
+ }
+ }
+
+ /* Populate the FCP_PRIO. */
+ if (ha->flags.fcp_prio_enabled)
+ ctx->fcp_cmnd->task_attribute |=
+ sp->fcport->fcp_prio << 3;
+
+ memcpy(ctx->fcp_cmnd->cdb, cmd->cmnd, cmd->cmd_len);
+
+ fcp_dl = (uint32_t *)(ctx->fcp_cmnd->cdb + 16 +
+ additional_cdb_len);
+ *fcp_dl = htonl((uint32_t)scsi_bufflen(cmd));
+
+ cmd_pkt->fcp_cmnd_dseg_len = cpu_to_le16(ctx->fcp_cmnd_len);
+ cmd_pkt->fcp_cmnd_dseg_address[0] =
+ cpu_to_le32(LSD(ctx->fcp_cmnd_dma));
+ cmd_pkt->fcp_cmnd_dseg_address[1] =
+ cpu_to_le32(MSD(ctx->fcp_cmnd_dma));
+
+ sp->flags |= SRB_FCP_CMND_DMA_VALID;
+ cmd_pkt->byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd));
+ /* Set total data segment count. */
+ cmd_pkt->entry_count = (uint8_t)req_cnt;
+ /* Specify response queue number where
+ * completion should happen
+ */
+ cmd_pkt->entry_status = (uint8_t) rsp->id;
+ } else {
+ struct cmd_type_7 *cmd_pkt;
+ req_cnt = qla24xx_calc_iocbs(vha, tot_dsds);
+ if (req->cnt < (req_cnt + 2)) {
+ cnt = (uint16_t)RD_REG_DWORD_RELAXED(
+ &reg->req_q_out[0]);
+ if (req->ring_index < cnt)
+ req->cnt = cnt - req->ring_index;
+ else
+ req->cnt = req->length -
+ (req->ring_index - cnt);
+ }
+ if (req->cnt < (req_cnt + 2))
+ goto queuing_error;
+
+ cmd_pkt = (struct cmd_type_7 *)req->ring_ptr;
+ cmd_pkt->handle = MAKE_HANDLE(req->id, handle);
+
+ /* Zero out remaining portion of packet. */
+ /* tagged queuing modifier -- default is TSK_SIMPLE (0).*/
+ clr_ptr = (uint32_t *)cmd_pkt + 2;
+ memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8);
+ cmd_pkt->dseg_count = cpu_to_le16(tot_dsds);
+
+ /* Set NPORT-ID and LUN number*/
+ cmd_pkt->nport_handle = cpu_to_le16(sp->fcport->loop_id);
+ cmd_pkt->port_id[0] = sp->fcport->d_id.b.al_pa;
+ cmd_pkt->port_id[1] = sp->fcport->d_id.b.area;
+ cmd_pkt->port_id[2] = sp->fcport->d_id.b.domain;
+ cmd_pkt->vp_index = sp->fcport->vp_idx;
+
+ int_to_scsilun(cmd->device->lun, &cmd_pkt->lun);
+ host_to_fcp_swap((uint8_t *)&cmd_pkt->lun,
+ sizeof(cmd_pkt->lun));
+
+ /*
+ * Update tagged queuing modifier -- default is TSK_SIMPLE (0).
+ */
+ if (scsi_populate_tag_msg(cmd, tag)) {
+ switch (tag[0]) {
+ case HEAD_OF_QUEUE_TAG:
+ cmd_pkt->task = TSK_HEAD_OF_QUEUE;
+ break;
+ case ORDERED_QUEUE_TAG:
+ cmd_pkt->task = TSK_ORDERED;
+ break;
+ }
+ }
+
+ /* Populate the FCP_PRIO. */
+ if (ha->flags.fcp_prio_enabled)
+ cmd_pkt->task |= sp->fcport->fcp_prio << 3;
+
+ /* Load SCSI command packet. */
+ memcpy(cmd_pkt->fcp_cdb, cmd->cmnd, cmd->cmd_len);
+ host_to_fcp_swap(cmd_pkt->fcp_cdb, sizeof(cmd_pkt->fcp_cdb));
+
+ cmd_pkt->byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd));
+
+ /* Build IOCB segments */
+ qla24xx_build_scsi_iocbs(sp, cmd_pkt, tot_dsds);
+
+ /* Set total data segment count. */
+ cmd_pkt->entry_count = (uint8_t)req_cnt;
+ /* Specify response queue number where
+ * completion should happen.
+ */
+ cmd_pkt->entry_status = (uint8_t) rsp->id;
+
+ }
+ /* Build command packet. */
+ req->current_outstanding_cmd = handle;
+ req->outstanding_cmds[handle] = sp;
+ sp->handle = handle;
+ cmd->host_scribble = (unsigned char *)(unsigned long)handle;
+ req->cnt -= req_cnt;
+ wmb();
+
+ /* Adjust ring index. */
+ req->ring_index++;
+ if (req->ring_index == req->length) {
+ req->ring_index = 0;
+ req->ring_ptr = req->ring;
+ } else
+ req->ring_ptr++;
+
+ sp->flags |= SRB_DMA_VALID;
+
+ /* Set chip new ring index. */
+ /* write, read and verify logic */
+ dbval = dbval | (req->id << 8) | (req->ring_index << 16);
+ if (ql2xdbwr)
+ qla82xx_wr_32(ha, ha->nxdb_wr_ptr, dbval);
+ else {
+ WRT_REG_DWORD(
+ (unsigned long __iomem *)ha->nxdb_wr_ptr,
+ dbval);
+ wmb();
+ while (RD_REG_DWORD(ha->nxdb_rd_ptr) != dbval) {
+ WRT_REG_DWORD(
+ (unsigned long __iomem *)ha->nxdb_wr_ptr,
+ dbval);
+ wmb();
+ }
+ }
+
+ /* Manage unprocessed RIO/ZIO commands in response queue. */
+ if (vha->flags.process_response_queue &&
+ rsp->ring_ptr->signature != RESPONSE_PROCESSED)
+ qla24xx_process_response_queue(vha, rsp);
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ return QLA_SUCCESS;
+
+queuing_error_fcp_cmnd:
+ dma_pool_free(ha->fcp_cmnd_dma_pool, ctx->fcp_cmnd, ctx->fcp_cmnd_dma);
+queuing_error:
+ if (tot_dsds)
+ scsi_dma_unmap(cmd);
+
+ if (sp->u.scmd.ctx) {
+ mempool_free(sp->u.scmd.ctx, ha->ctx_mempool);
+ sp->u.scmd.ctx = NULL;
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return QLA_FUNCTION_FAILED;
+}
+
+int
+qla2x00_start_sp(srb_t *sp)
+{
+ int rval;
+ struct qla_hw_data *ha = sp->fcport->vha->hw;
+ void *pkt;
+ unsigned long flags;
+
+ rval = QLA_FUNCTION_FAILED;
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ pkt = qla2x00_alloc_iocbs(sp->fcport->vha, sp);
+ if (!pkt) {
+ ql_log(ql_log_warn, sp->fcport->vha, 0x700c,
+ "qla2x00_alloc_iocbs failed.\n");
+ goto done;
+ }
+
+ rval = QLA_SUCCESS;
+ switch (sp->type) {
+ case SRB_LOGIN_CMD:
+ IS_FWI2_CAPABLE(ha) ?
+ qla24xx_login_iocb(sp, pkt) :
+ qla2x00_login_iocb(sp, pkt);
+ break;
+ case SRB_LOGOUT_CMD:
+ IS_FWI2_CAPABLE(ha) ?
+ qla24xx_logout_iocb(sp, pkt) :
+ qla2x00_logout_iocb(sp, pkt);
+ break;
+ case SRB_ELS_CMD_RPT:
+ case SRB_ELS_CMD_HST:
+ qla24xx_els_iocb(sp, pkt);
+ break;
+ case SRB_CT_CMD:
+ IS_FWI2_CAPABLE(ha) ?
+ qla24xx_ct_iocb(sp, pkt) :
+ qla2x00_ct_iocb(sp, pkt);
+ break;
+ case SRB_ADISC_CMD:
+ IS_FWI2_CAPABLE(ha) ?
+ qla24xx_adisc_iocb(sp, pkt) :
+ qla2x00_adisc_iocb(sp, pkt);
+ break;
+ case SRB_TM_CMD:
+ qla24xx_tm_iocb(sp, pkt);
+ break;
+ default:
+ break;
+ }
+
+ wmb();
+ qla2x00_start_iocbs(sp->fcport->vha, ha->req_q_map[0]);
+done:
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ return rval;
+}
diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c
new file mode 100644
index 00000000..ce422880
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_isr.c
@@ -0,0 +1,2597 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2003-2011 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#include "qla_def.h"
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsi_bsg_fc.h>
+#include <scsi/scsi_eh.h>
+
+static void qla2x00_mbx_completion(scsi_qla_host_t *, uint16_t);
+static void qla2x00_process_completed_request(struct scsi_qla_host *,
+ struct req_que *, uint32_t);
+static void qla2x00_status_entry(scsi_qla_host_t *, struct rsp_que *, void *);
+static void qla2x00_status_cont_entry(struct rsp_que *, sts_cont_entry_t *);
+static void qla2x00_error_entry(scsi_qla_host_t *, struct rsp_que *,
+ sts_entry_t *);
+
+/**
+ * qla2100_intr_handler() - Process interrupts for the ISP2100 and ISP2200.
+ * @irq:
+ * @dev_id: SCSI driver HA context
+ *
+ * Called by system whenever the host adapter generates an interrupt.
+ *
+ * Returns handled flag.
+ */
+irqreturn_t
+qla2100_intr_handler(int irq, void *dev_id)
+{
+ scsi_qla_host_t *vha;
+ struct qla_hw_data *ha;
+ struct device_reg_2xxx __iomem *reg;
+ int status;
+ unsigned long iter;
+ uint16_t hccr;
+ uint16_t mb[4];
+ struct rsp_que *rsp;
+ unsigned long flags;
+
+ rsp = (struct rsp_que *) dev_id;
+ if (!rsp) {
+ ql_log(ql_log_info, NULL, 0x505d,
+ "%s: NULL response queue pointer.\n", __func__);
+ return (IRQ_NONE);
+ }
+
+ ha = rsp->hw;
+ reg = &ha->iobase->isp;
+ status = 0;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ vha = pci_get_drvdata(ha->pdev);
+ for (iter = 50; iter--; ) {
+ hccr = RD_REG_WORD(&reg->hccr);
+ if (hccr & HCCR_RISC_PAUSE) {
+ if (pci_channel_offline(ha->pdev))
+ break;
+
+ /*
+ * Issue a "HARD" reset in order for the RISC interrupt
+ * bit to be cleared. Schedule a big hammer to get
+ * out of the RISC PAUSED state.
+ */
+ WRT_REG_WORD(&reg->hccr, HCCR_RESET_RISC);
+ RD_REG_WORD(&reg->hccr);
+
+ ha->isp_ops->fw_dump(vha, 1);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ break;
+ } else if ((RD_REG_WORD(&reg->istatus) & ISR_RISC_INT) == 0)
+ break;
+
+ if (RD_REG_WORD(&reg->semaphore) & BIT_0) {
+ WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
+ RD_REG_WORD(&reg->hccr);
+
+ /* Get mailbox data. */
+ mb[0] = RD_MAILBOX_REG(ha, reg, 0);
+ if (mb[0] > 0x3fff && mb[0] < 0x8000) {
+ qla2x00_mbx_completion(vha, mb[0]);
+ status |= MBX_INTERRUPT;
+ } else if (mb[0] > 0x7fff && mb[0] < 0xc000) {
+ mb[1] = RD_MAILBOX_REG(ha, reg, 1);
+ mb[2] = RD_MAILBOX_REG(ha, reg, 2);
+ mb[3] = RD_MAILBOX_REG(ha, reg, 3);
+ qla2x00_async_event(vha, rsp, mb);
+ } else {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_async, vha, 0x5025,
+ "Unrecognized interrupt type (%d).\n",
+ mb[0]);
+ }
+ /* Release mailbox registers. */
+ WRT_REG_WORD(&reg->semaphore, 0);
+ RD_REG_WORD(&reg->semaphore);
+ } else {
+ qla2x00_process_response_queue(rsp);
+
+ WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
+ RD_REG_WORD(&reg->hccr);
+ }
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) &&
+ (status & MBX_INTERRUPT) && ha->flags.mbox_int) {
+ set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
+ complete(&ha->mbx_intr_comp);
+ }
+
+ return (IRQ_HANDLED);
+}
+
+/**
+ * qla2300_intr_handler() - Process interrupts for the ISP23xx and ISP63xx.
+ * @irq:
+ * @dev_id: SCSI driver HA context
+ *
+ * Called by system whenever the host adapter generates an interrupt.
+ *
+ * Returns handled flag.
+ */
+irqreturn_t
+qla2300_intr_handler(int irq, void *dev_id)
+{
+ scsi_qla_host_t *vha;
+ struct device_reg_2xxx __iomem *reg;
+ int status;
+ unsigned long iter;
+ uint32_t stat;
+ uint16_t hccr;
+ uint16_t mb[4];
+ struct rsp_que *rsp;
+ struct qla_hw_data *ha;
+ unsigned long flags;
+
+ rsp = (struct rsp_que *) dev_id;
+ if (!rsp) {
+ ql_log(ql_log_info, NULL, 0x5058,
+ "%s: NULL response queue pointer.\n", __func__);
+ return (IRQ_NONE);
+ }
+
+ ha = rsp->hw;
+ reg = &ha->iobase->isp;
+ status = 0;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ vha = pci_get_drvdata(ha->pdev);
+ for (iter = 50; iter--; ) {
+ stat = RD_REG_DWORD(&reg->u.isp2300.host_status);
+ if (stat & HSR_RISC_PAUSED) {
+ if (unlikely(pci_channel_offline(ha->pdev)))
+ break;
+
+ hccr = RD_REG_WORD(&reg->hccr);
+ if (hccr & (BIT_15 | BIT_13 | BIT_11 | BIT_8))
+ ql_log(ql_log_warn, vha, 0x5026,
+ "Parity error -- HCCR=%x, Dumping "
+ "firmware.\n", hccr);
+ else
+ ql_log(ql_log_warn, vha, 0x5027,
+ "RISC paused -- HCCR=%x, Dumping "
+ "firmware.\n", hccr);
+
+ /*
+ * Issue a "HARD" reset in order for the RISC
+ * interrupt bit to be cleared. Schedule a big
+ * hammer to get out of the RISC PAUSED state.
+ */
+ WRT_REG_WORD(&reg->hccr, HCCR_RESET_RISC);
+ RD_REG_WORD(&reg->hccr);
+
+ ha->isp_ops->fw_dump(vha, 1);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ break;
+ } else if ((stat & HSR_RISC_INT) == 0)
+ break;
+
+ switch (stat & 0xff) {
+ case 0x1:
+ case 0x2:
+ case 0x10:
+ case 0x11:
+ qla2x00_mbx_completion(vha, MSW(stat));
+ status |= MBX_INTERRUPT;
+
+ /* Release mailbox registers. */
+ WRT_REG_WORD(&reg->semaphore, 0);
+ break;
+ case 0x12:
+ mb[0] = MSW(stat);
+ mb[1] = RD_MAILBOX_REG(ha, reg, 1);
+ mb[2] = RD_MAILBOX_REG(ha, reg, 2);
+ mb[3] = RD_MAILBOX_REG(ha, reg, 3);
+ qla2x00_async_event(vha, rsp, mb);
+ break;
+ case 0x13:
+ qla2x00_process_response_queue(rsp);
+ break;
+ case 0x15:
+ mb[0] = MBA_CMPLT_1_16BIT;
+ mb[1] = MSW(stat);
+ qla2x00_async_event(vha, rsp, mb);
+ break;
+ case 0x16:
+ mb[0] = MBA_SCSI_COMPLETION;
+ mb[1] = MSW(stat);
+ mb[2] = RD_MAILBOX_REG(ha, reg, 2);
+ qla2x00_async_event(vha, rsp, mb);
+ break;
+ default:
+ ql_dbg(ql_dbg_async, vha, 0x5028,
+ "Unrecognized interrupt type (%d).\n", stat & 0xff);
+ break;
+ }
+ WRT_REG_WORD(&reg->hccr, HCCR_CLR_RISC_INT);
+ RD_REG_WORD_RELAXED(&reg->hccr);
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) &&
+ (status & MBX_INTERRUPT) && ha->flags.mbox_int) {
+ set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
+ complete(&ha->mbx_intr_comp);
+ }
+
+ return (IRQ_HANDLED);
+}
+
+/**
+ * qla2x00_mbx_completion() - Process mailbox command completions.
+ * @ha: SCSI driver HA context
+ * @mb0: Mailbox0 register
+ */
+static void
+qla2x00_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0)
+{
+ uint16_t cnt;
+ uint32_t mboxes;
+ uint16_t __iomem *wptr;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ /* Read all mbox registers? */
+ mboxes = (1 << ha->mbx_count) - 1;
+ if (!ha->mcp)
+ ql_dbg(ql_dbg_async, vha, 0x5001, "MBX pointer ERRROR.\n");
+ else
+ mboxes = ha->mcp->in_mb;
+
+ /* Load return mailbox registers. */
+ ha->flags.mbox_int = 1;
+ ha->mailbox_out[0] = mb0;
+ mboxes >>= 1;
+ wptr = (uint16_t __iomem *)MAILBOX_REG(ha, reg, 1);
+
+ for (cnt = 1; cnt < ha->mbx_count; cnt++) {
+ if (IS_QLA2200(ha) && cnt == 8)
+ wptr = (uint16_t __iomem *)MAILBOX_REG(ha, reg, 8);
+ if ((cnt == 4 || cnt == 5) && (mboxes & BIT_0))
+ ha->mailbox_out[cnt] = qla2x00_debounce_register(wptr);
+ else if (mboxes & BIT_0)
+ ha->mailbox_out[cnt] = RD_REG_WORD(wptr);
+
+ wptr++;
+ mboxes >>= 1;
+ }
+}
+
+static void
+qla81xx_idc_event(scsi_qla_host_t *vha, uint16_t aen, uint16_t descr)
+{
+ static char *event[] =
+ { "Complete", "Request Notification", "Time Extension" };
+ int rval;
+ struct device_reg_24xx __iomem *reg24 = &vha->hw->iobase->isp24;
+ uint16_t __iomem *wptr;
+ uint16_t cnt, timeout, mb[QLA_IDC_ACK_REGS];
+
+ /* Seed data -- mailbox1 -> mailbox7. */
+ wptr = (uint16_t __iomem *)&reg24->mailbox1;
+ for (cnt = 0; cnt < QLA_IDC_ACK_REGS; cnt++, wptr++)
+ mb[cnt] = RD_REG_WORD(wptr);
+
+ ql_dbg(ql_dbg_async, vha, 0x5021,
+ "Inter-Driver Communication %s -- "
+ "%04x %04x %04x %04x %04x %04x %04x.\n",
+ event[aen & 0xff], mb[0], mb[1], mb[2], mb[3],
+ mb[4], mb[5], mb[6]);
+
+ /* Acknowledgement needed? [Notify && non-zero timeout]. */
+ timeout = (descr >> 8) & 0xf;
+ if (aen != MBA_IDC_NOTIFY || !timeout)
+ return;
+
+ ql_dbg(ql_dbg_async, vha, 0x5022,
+ "%lu Inter-Driver Communication %s -- ACK timeout=%d.\n",
+ vha->host_no, event[aen & 0xff], timeout);
+
+ rval = qla2x00_post_idc_ack_work(vha, mb);
+ if (rval != QLA_SUCCESS)
+ ql_log(ql_log_warn, vha, 0x5023,
+ "IDC failed to post ACK.\n");
+}
+
+/**
+ * qla2x00_async_event() - Process aynchronous events.
+ * @ha: SCSI driver HA context
+ * @mb: Mailbox registers (0 - 3)
+ */
+void
+qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb)
+{
+#define LS_UNKNOWN 2
+ static char *link_speeds[] = { "1", "2", "?", "4", "8", "16", "10" };
+ char *link_speed;
+ uint16_t handle_cnt;
+ uint16_t cnt, mbx;
+ uint32_t handles[5];
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24;
+ struct device_reg_82xx __iomem *reg82 = &ha->iobase->isp82;
+ uint32_t rscn_entry, host_pid;
+ unsigned long flags;
+
+ /* Setup to process RIO completion. */
+ handle_cnt = 0;
+ if (IS_CNA_CAPABLE(ha))
+ goto skip_rio;
+ switch (mb[0]) {
+ case MBA_SCSI_COMPLETION:
+ handles[0] = le32_to_cpu((uint32_t)((mb[2] << 16) | mb[1]));
+ handle_cnt = 1;
+ break;
+ case MBA_CMPLT_1_16BIT:
+ handles[0] = mb[1];
+ handle_cnt = 1;
+ mb[0] = MBA_SCSI_COMPLETION;
+ break;
+ case MBA_CMPLT_2_16BIT:
+ handles[0] = mb[1];
+ handles[1] = mb[2];
+ handle_cnt = 2;
+ mb[0] = MBA_SCSI_COMPLETION;
+ break;
+ case MBA_CMPLT_3_16BIT:
+ handles[0] = mb[1];
+ handles[1] = mb[2];
+ handles[2] = mb[3];
+ handle_cnt = 3;
+ mb[0] = MBA_SCSI_COMPLETION;
+ break;
+ case MBA_CMPLT_4_16BIT:
+ handles[0] = mb[1];
+ handles[1] = mb[2];
+ handles[2] = mb[3];
+ handles[3] = (uint32_t)RD_MAILBOX_REG(ha, reg, 6);
+ handle_cnt = 4;
+ mb[0] = MBA_SCSI_COMPLETION;
+ break;
+ case MBA_CMPLT_5_16BIT:
+ handles[0] = mb[1];
+ handles[1] = mb[2];
+ handles[2] = mb[3];
+ handles[3] = (uint32_t)RD_MAILBOX_REG(ha, reg, 6);
+ handles[4] = (uint32_t)RD_MAILBOX_REG(ha, reg, 7);
+ handle_cnt = 5;
+ mb[0] = MBA_SCSI_COMPLETION;
+ break;
+ case MBA_CMPLT_2_32BIT:
+ handles[0] = le32_to_cpu((uint32_t)((mb[2] << 16) | mb[1]));
+ handles[1] = le32_to_cpu(
+ ((uint32_t)(RD_MAILBOX_REG(ha, reg, 7) << 16)) |
+ RD_MAILBOX_REG(ha, reg, 6));
+ handle_cnt = 2;
+ mb[0] = MBA_SCSI_COMPLETION;
+ break;
+ default:
+ break;
+ }
+skip_rio:
+ switch (mb[0]) {
+ case MBA_SCSI_COMPLETION: /* Fast Post */
+ if (!vha->flags.online)
+ break;
+
+ for (cnt = 0; cnt < handle_cnt; cnt++)
+ qla2x00_process_completed_request(vha, rsp->req,
+ handles[cnt]);
+ break;
+
+ case MBA_RESET: /* Reset */
+ ql_dbg(ql_dbg_async, vha, 0x5002,
+ "Asynchronous RESET.\n");
+
+ set_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
+ break;
+
+ case MBA_SYSTEM_ERR: /* System Error */
+ mbx = (IS_QLA81XX(ha) || IS_QLA83XX(ha)) ?
+ RD_REG_WORD(&reg24->mailbox7) : 0;
+ ql_log(ql_log_warn, vha, 0x5003,
+ "ISP System Error - mbx1=%xh mbx2=%xh mbx3=%xh "
+ "mbx7=%xh.\n", mb[1], mb[2], mb[3], mbx);
+
+ ha->isp_ops->fw_dump(vha, 1);
+
+ if (IS_FWI2_CAPABLE(ha)) {
+ if (mb[1] == 0 && mb[2] == 0) {
+ ql_log(ql_log_fatal, vha, 0x5004,
+ "Unrecoverable Hardware Error: adapter "
+ "marked OFFLINE!\n");
+ vha->flags.online = 0;
+ vha->device_flags |= DFLG_DEV_FAILED;
+ } else {
+ /* Check to see if MPI timeout occurred */
+ if ((mbx & MBX_3) && (ha->flags.port0))
+ set_bit(MPI_RESET_NEEDED,
+ &vha->dpc_flags);
+
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ }
+ } else if (mb[1] == 0) {
+ ql_log(ql_log_fatal, vha, 0x5005,
+ "Unrecoverable Hardware Error: adapter marked "
+ "OFFLINE!\n");
+ vha->flags.online = 0;
+ vha->device_flags |= DFLG_DEV_FAILED;
+ } else
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ break;
+
+ case MBA_REQ_TRANSFER_ERR: /* Request Transfer Error */
+ ql_log(ql_log_warn, vha, 0x5006,
+ "ISP Request Transfer Error (%x).\n", mb[1]);
+
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ break;
+
+ case MBA_RSP_TRANSFER_ERR: /* Response Transfer Error */
+ ql_log(ql_log_warn, vha, 0x5007,
+ "ISP Response Transfer Error.\n");
+
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ break;
+
+ case MBA_WAKEUP_THRES: /* Request Queue Wake-up */
+ ql_dbg(ql_dbg_async, vha, 0x5008,
+ "Asynchronous WAKEUP_THRES.\n");
+ break;
+
+ case MBA_LIP_OCCURRED: /* Loop Initialization Procedure */
+ ql_dbg(ql_dbg_async, vha, 0x5009,
+ "LIP occurred (%x).\n", mb[1]);
+
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+ qla2x00_mark_all_devices_lost(vha, 1);
+ }
+
+ if (vha->vp_idx) {
+ atomic_set(&vha->vp_state, VP_FAILED);
+ fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
+ }
+
+ set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags);
+ set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
+
+ vha->flags.management_server_logged_in = 0;
+ qla2x00_post_aen_work(vha, FCH_EVT_LIP, mb[1]);
+ break;
+
+ case MBA_LOOP_UP: /* Loop Up Event */
+ if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+ link_speed = link_speeds[0];
+ ha->link_data_rate = PORT_SPEED_1GB;
+ } else {
+ link_speed = link_speeds[LS_UNKNOWN];
+ if (mb[1] < 6)
+ link_speed = link_speeds[mb[1]];
+ else if (mb[1] == 0x13)
+ link_speed = link_speeds[6];
+ ha->link_data_rate = mb[1];
+ }
+
+ ql_dbg(ql_dbg_async, vha, 0x500a,
+ "LOOP UP detected (%s Gbps).\n", link_speed);
+
+ vha->flags.management_server_logged_in = 0;
+ qla2x00_post_aen_work(vha, FCH_EVT_LINKUP, ha->link_data_rate);
+ break;
+
+ case MBA_LOOP_DOWN: /* Loop Down Event */
+ mbx = (IS_QLA81XX(ha) || IS_QLA8031(ha))
+ ? RD_REG_WORD(&reg24->mailbox4) : 0;
+ mbx = IS_QLA82XX(ha) ? RD_REG_WORD(&reg82->mailbox_out[4]) : mbx;
+ ql_dbg(ql_dbg_async, vha, 0x500b,
+ "LOOP DOWN detected (%x %x %x %x).\n",
+ mb[1], mb[2], mb[3], mbx);
+
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+ vha->device_flags |= DFLG_NO_CABLE;
+ qla2x00_mark_all_devices_lost(vha, 1);
+ }
+
+ if (vha->vp_idx) {
+ atomic_set(&vha->vp_state, VP_FAILED);
+ fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
+ }
+
+ vha->flags.management_server_logged_in = 0;
+ ha->link_data_rate = PORT_SPEED_UNKNOWN;
+ qla2x00_post_aen_work(vha, FCH_EVT_LINKDOWN, 0);
+ break;
+
+ case MBA_LIP_RESET: /* LIP reset occurred */
+ ql_dbg(ql_dbg_async, vha, 0x500c,
+ "LIP reset occurred (%x).\n", mb[1]);
+
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+ qla2x00_mark_all_devices_lost(vha, 1);
+ }
+
+ if (vha->vp_idx) {
+ atomic_set(&vha->vp_state, VP_FAILED);
+ fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
+ }
+
+ set_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
+
+ ha->operating_mode = LOOP;
+ vha->flags.management_server_logged_in = 0;
+ qla2x00_post_aen_work(vha, FCH_EVT_LIPRESET, mb[1]);
+ break;
+
+ /* case MBA_DCBX_COMPLETE: */
+ case MBA_POINT_TO_POINT: /* Point-to-Point */
+ if (IS_QLA2100(ha))
+ break;
+
+ if (IS_QLA81XX(ha) || IS_QLA82XX(ha) || IS_QLA8031(ha)) {
+ ql_dbg(ql_dbg_async, vha, 0x500d,
+ "DCBX Completed -- %04x %04x %04x.\n",
+ mb[1], mb[2], mb[3]);
+ if (ha->notify_dcbx_comp)
+ complete(&ha->dcbx_comp);
+
+ } else
+ ql_dbg(ql_dbg_async, vha, 0x500e,
+ "Asynchronous P2P MODE received.\n");
+
+ /*
+ * Until there's a transition from loop down to loop up, treat
+ * this as loop down only.
+ */
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ if (!atomic_read(&vha->loop_down_timer))
+ atomic_set(&vha->loop_down_timer,
+ LOOP_DOWN_TIME);
+ qla2x00_mark_all_devices_lost(vha, 1);
+ }
+
+ if (vha->vp_idx) {
+ atomic_set(&vha->vp_state, VP_FAILED);
+ fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
+ }
+
+ if (!(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)))
+ set_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
+
+ set_bit(REGISTER_FC4_NEEDED, &vha->dpc_flags);
+ set_bit(REGISTER_FDMI_NEEDED, &vha->dpc_flags);
+
+ ha->flags.gpsc_supported = 1;
+ vha->flags.management_server_logged_in = 0;
+ break;
+
+ case MBA_CHG_IN_CONNECTION: /* Change in connection mode */
+ if (IS_QLA2100(ha))
+ break;
+
+ ql_dbg(ql_dbg_async, vha, 0x500f,
+ "Configuration change detected: value=%x.\n", mb[1]);
+
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ if (!atomic_read(&vha->loop_down_timer))
+ atomic_set(&vha->loop_down_timer,
+ LOOP_DOWN_TIME);
+ qla2x00_mark_all_devices_lost(vha, 1);
+ }
+
+ if (vha->vp_idx) {
+ atomic_set(&vha->vp_state, VP_FAILED);
+ fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
+ }
+
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
+ break;
+
+ case MBA_PORT_UPDATE: /* Port database update */
+ /*
+ * Handle only global and vn-port update events
+ *
+ * Relevant inputs:
+ * mb[1] = N_Port handle of changed port
+ * OR 0xffff for global event
+ * mb[2] = New login state
+ * 7 = Port logged out
+ * mb[3] = LSB is vp_idx, 0xff = all vps
+ *
+ * Skip processing if:
+ * Event is global, vp_idx is NOT all vps,
+ * vp_idx does not match
+ * Event is not global, vp_idx does not match
+ */
+ if (IS_QLA2XXX_MIDTYPE(ha) &&
+ ((mb[1] == 0xffff && (mb[3] & 0xff) != 0xff) ||
+ (mb[1] != 0xffff)) && vha->vp_idx != (mb[3] & 0xff))
+ break;
+
+ /* Global event -- port logout or port unavailable. */
+ if (mb[1] == 0xffff && mb[2] == 0x7) {
+ ql_dbg(ql_dbg_async, vha, 0x5010,
+ "Port unavailable %04x %04x %04x.\n",
+ mb[1], mb[2], mb[3]);
+
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ atomic_set(&vha->loop_down_timer,
+ LOOP_DOWN_TIME);
+ vha->device_flags |= DFLG_NO_CABLE;
+ qla2x00_mark_all_devices_lost(vha, 1);
+ }
+
+ if (vha->vp_idx) {
+ atomic_set(&vha->vp_state, VP_FAILED);
+ fc_vport_set_state(vha->fc_vport,
+ FC_VPORT_FAILED);
+ qla2x00_mark_all_devices_lost(vha, 1);
+ }
+
+ vha->flags.management_server_logged_in = 0;
+ ha->link_data_rate = PORT_SPEED_UNKNOWN;
+ break;
+ }
+
+ /*
+ * If PORT UPDATE is global (received LIP_OCCURRED/LIP_RESET
+ * event etc. earlier indicating loop is down) then process
+ * it. Otherwise ignore it and Wait for RSCN to come in.
+ */
+ atomic_set(&vha->loop_down_timer, 0);
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN &&
+ atomic_read(&vha->loop_state) != LOOP_DEAD) {
+ ql_dbg(ql_dbg_async, vha, 0x5011,
+ "Asynchronous PORT UPDATE ignored %04x/%04x/%04x.\n",
+ mb[1], mb[2], mb[3]);
+ break;
+ }
+
+ ql_dbg(ql_dbg_async, vha, 0x5012,
+ "Port database changed %04x %04x %04x.\n",
+ mb[1], mb[2], mb[3]);
+
+ /*
+ * Mark all devices as missing so we will login again.
+ */
+ atomic_set(&vha->loop_state, LOOP_UP);
+
+ qla2x00_mark_all_devices_lost(vha, 1);
+
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
+ break;
+
+ case MBA_RSCN_UPDATE: /* State Change Registration */
+ /* Check if the Vport has issued a SCR */
+ if (vha->vp_idx && test_bit(VP_SCR_NEEDED, &vha->vp_flags))
+ break;
+ /* Only handle SCNs for our Vport index. */
+ if (ha->flags.npiv_supported && vha->vp_idx != (mb[3] & 0xff))
+ break;
+
+ ql_dbg(ql_dbg_async, vha, 0x5013,
+ "RSCN database changed -- %04x %04x %04x.\n",
+ mb[1], mb[2], mb[3]);
+
+ rscn_entry = ((mb[1] & 0xff) << 16) | mb[2];
+ host_pid = (vha->d_id.b.domain << 16) | (vha->d_id.b.area << 8)
+ | vha->d_id.b.al_pa;
+ if (rscn_entry == host_pid) {
+ ql_dbg(ql_dbg_async, vha, 0x5014,
+ "Ignoring RSCN update to local host "
+ "port ID (%06x).\n", host_pid);
+ break;
+ }
+
+ /* Ignore reserved bits from RSCN-payload. */
+ rscn_entry = ((mb[1] & 0x3ff) << 16) | mb[2];
+
+ atomic_set(&vha->loop_down_timer, 0);
+ vha->flags.management_server_logged_in = 0;
+
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ set_bit(RSCN_UPDATE, &vha->dpc_flags);
+ qla2x00_post_aen_work(vha, FCH_EVT_RSCN, rscn_entry);
+ break;
+
+ /* case MBA_RIO_RESPONSE: */
+ case MBA_ZIO_RESPONSE:
+ ql_dbg(ql_dbg_async, vha, 0x5015,
+ "[R|Z]IO update completion.\n");
+
+ if (IS_FWI2_CAPABLE(ha))
+ qla24xx_process_response_queue(vha, rsp);
+ else
+ qla2x00_process_response_queue(rsp);
+ break;
+
+ case MBA_DISCARD_RND_FRAME:
+ ql_dbg(ql_dbg_async, vha, 0x5016,
+ "Discard RND Frame -- %04x %04x %04x.\n",
+ mb[1], mb[2], mb[3]);
+ break;
+
+ case MBA_TRACE_NOTIFICATION:
+ ql_dbg(ql_dbg_async, vha, 0x5017,
+ "Trace Notification -- %04x %04x.\n", mb[1], mb[2]);
+ break;
+
+ case MBA_ISP84XX_ALERT:
+ ql_dbg(ql_dbg_async, vha, 0x5018,
+ "ISP84XX Alert Notification -- %04x %04x %04x.\n",
+ mb[1], mb[2], mb[3]);
+
+ spin_lock_irqsave(&ha->cs84xx->access_lock, flags);
+ switch (mb[1]) {
+ case A84_PANIC_RECOVERY:
+ ql_log(ql_log_info, vha, 0x5019,
+ "Alert 84XX: panic recovery %04x %04x.\n",
+ mb[2], mb[3]);
+ break;
+ case A84_OP_LOGIN_COMPLETE:
+ ha->cs84xx->op_fw_version = mb[3] << 16 | mb[2];
+ ql_log(ql_log_info, vha, 0x501a,
+ "Alert 84XX: firmware version %x.\n",
+ ha->cs84xx->op_fw_version);
+ break;
+ case A84_DIAG_LOGIN_COMPLETE:
+ ha->cs84xx->diag_fw_version = mb[3] << 16 | mb[2];
+ ql_log(ql_log_info, vha, 0x501b,
+ "Alert 84XX: diagnostic firmware version %x.\n",
+ ha->cs84xx->diag_fw_version);
+ break;
+ case A84_GOLD_LOGIN_COMPLETE:
+ ha->cs84xx->diag_fw_version = mb[3] << 16 | mb[2];
+ ha->cs84xx->fw_update = 1;
+ ql_log(ql_log_info, vha, 0x501c,
+ "Alert 84XX: gold firmware version %x.\n",
+ ha->cs84xx->gold_fw_version);
+ break;
+ default:
+ ql_log(ql_log_warn, vha, 0x501d,
+ "Alert 84xx: Invalid Alert %04x %04x %04x.\n",
+ mb[1], mb[2], mb[3]);
+ }
+ spin_unlock_irqrestore(&ha->cs84xx->access_lock, flags);
+ break;
+ case MBA_DCBX_START:
+ ql_dbg(ql_dbg_async, vha, 0x501e,
+ "DCBX Started -- %04x %04x %04x.\n",
+ mb[1], mb[2], mb[3]);
+ break;
+ case MBA_DCBX_PARAM_UPDATE:
+ ql_dbg(ql_dbg_async, vha, 0x501f,
+ "DCBX Parameters Updated -- %04x %04x %04x.\n",
+ mb[1], mb[2], mb[3]);
+ break;
+ case MBA_FCF_CONF_ERR:
+ ql_dbg(ql_dbg_async, vha, 0x5020,
+ "FCF Configuration Error -- %04x %04x %04x.\n",
+ mb[1], mb[2], mb[3]);
+ break;
+ case MBA_IDC_COMPLETE:
+ case MBA_IDC_NOTIFY:
+ case MBA_IDC_TIME_EXT:
+ qla81xx_idc_event(vha, mb[0], mb[1]);
+ break;
+ default:
+ ql_dbg(ql_dbg_async, vha, 0x5057,
+ "Unknown AEN:%04x %04x %04x %04x\n",
+ mb[0], mb[1], mb[2], mb[3]);
+ }
+
+ if (!vha->vp_idx && ha->num_vhosts)
+ qla2x00_alert_all_vps(rsp, mb);
+}
+
+/**
+ * qla2x00_process_completed_request() - Process a Fast Post response.
+ * @ha: SCSI driver HA context
+ * @index: SRB index
+ */
+static void
+qla2x00_process_completed_request(struct scsi_qla_host *vha,
+ struct req_que *req, uint32_t index)
+{
+ srb_t *sp;
+ struct qla_hw_data *ha = vha->hw;
+
+ /* Validate handle. */
+ if (index >= MAX_OUTSTANDING_COMMANDS) {
+ ql_log(ql_log_warn, vha, 0x3014,
+ "Invalid SCSI command index (%x).\n", index);
+
+ if (IS_QLA82XX(ha))
+ set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
+ else
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ return;
+ }
+
+ sp = req->outstanding_cmds[index];
+ if (sp) {
+ /* Free outstanding command slot. */
+ req->outstanding_cmds[index] = NULL;
+
+ /* Save ISP completion status */
+ sp->done(ha, sp, DID_OK << 16);
+ } else {
+ ql_log(ql_log_warn, vha, 0x3016, "Invalid SCSI SRB.\n");
+
+ if (IS_QLA82XX(ha))
+ set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
+ else
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ }
+}
+
+static srb_t *
+qla2x00_get_sp_from_handle(scsi_qla_host_t *vha, const char *func,
+ struct req_que *req, void *iocb)
+{
+ struct qla_hw_data *ha = vha->hw;
+ sts_entry_t *pkt = iocb;
+ srb_t *sp = NULL;
+ uint16_t index;
+
+ index = LSW(pkt->handle);
+ if (index >= MAX_OUTSTANDING_COMMANDS) {
+ ql_log(ql_log_warn, vha, 0x5031,
+ "Invalid command index (%x).\n", index);
+ if (IS_QLA82XX(ha))
+ set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
+ else
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ goto done;
+ }
+ sp = req->outstanding_cmds[index];
+ if (!sp) {
+ ql_log(ql_log_warn, vha, 0x5032,
+ "Invalid completion handle (%x) -- timed-out.\n", index);
+ return sp;
+ }
+ if (sp->handle != index) {
+ ql_log(ql_log_warn, vha, 0x5033,
+ "SRB handle (%x) mismatch %x.\n", sp->handle, index);
+ return NULL;
+ }
+
+ req->outstanding_cmds[index] = NULL;
+
+done:
+ return sp;
+}
+
+static void
+qla2x00_mbx_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
+ struct mbx_entry *mbx)
+{
+ const char func[] = "MBX-IOCB";
+ const char *type;
+ fc_port_t *fcport;
+ srb_t *sp;
+ struct srb_iocb *lio;
+ uint16_t *data;
+ uint16_t status;
+
+ sp = qla2x00_get_sp_from_handle(vha, func, req, mbx);
+ if (!sp)
+ return;
+
+ lio = &sp->u.iocb_cmd;
+ type = sp->name;
+ fcport = sp->fcport;
+ data = lio->u.logio.data;
+
+ data[0] = MBS_COMMAND_ERROR;
+ data[1] = lio->u.logio.flags & SRB_LOGIN_RETRIED ?
+ QLA_LOGIO_LOGIN_RETRIED : 0;
+ if (mbx->entry_status) {
+ ql_dbg(ql_dbg_async, vha, 0x5043,
+ "Async-%s error entry - hdl=%x portid=%02x%02x%02x "
+ "entry-status=%x status=%x state-flag=%x "
+ "status-flags=%x.\n", type, sp->handle,
+ fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa, mbx->entry_status,
+ le16_to_cpu(mbx->status), le16_to_cpu(mbx->state_flags),
+ le16_to_cpu(mbx->status_flags));
+
+ ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5029,
+ (uint8_t *)mbx, sizeof(*mbx));
+
+ goto logio_done;
+ }
+
+ status = le16_to_cpu(mbx->status);
+ if (status == 0x30 && sp->type == SRB_LOGIN_CMD &&
+ le16_to_cpu(mbx->mb0) == MBS_COMMAND_COMPLETE)
+ status = 0;
+ if (!status && le16_to_cpu(mbx->mb0) == MBS_COMMAND_COMPLETE) {
+ ql_dbg(ql_dbg_async, vha, 0x5045,
+ "Async-%s complete - hdl=%x portid=%02x%02x%02x mbx1=%x.\n",
+ type, sp->handle, fcport->d_id.b.domain,
+ fcport->d_id.b.area, fcport->d_id.b.al_pa,
+ le16_to_cpu(mbx->mb1));
+
+ data[0] = MBS_COMMAND_COMPLETE;
+ if (sp->type == SRB_LOGIN_CMD) {
+ fcport->port_type = FCT_TARGET;
+ if (le16_to_cpu(mbx->mb1) & BIT_0)
+ fcport->port_type = FCT_INITIATOR;
+ else if (le16_to_cpu(mbx->mb1) & BIT_1)
+ fcport->flags |= FCF_FCP2_DEVICE;
+ }
+ goto logio_done;
+ }
+
+ data[0] = le16_to_cpu(mbx->mb0);
+ switch (data[0]) {
+ case MBS_PORT_ID_USED:
+ data[1] = le16_to_cpu(mbx->mb1);
+ break;
+ case MBS_LOOP_ID_USED:
+ break;
+ default:
+ data[0] = MBS_COMMAND_ERROR;
+ break;
+ }
+
+ ql_log(ql_log_warn, vha, 0x5046,
+ "Async-%s failed - hdl=%x portid=%02x%02x%02x status=%x "
+ "mb0=%x mb1=%x mb2=%x mb6=%x mb7=%x.\n", type, sp->handle,
+ fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa,
+ status, le16_to_cpu(mbx->mb0), le16_to_cpu(mbx->mb1),
+ le16_to_cpu(mbx->mb2), le16_to_cpu(mbx->mb6),
+ le16_to_cpu(mbx->mb7));
+
+logio_done:
+ sp->done(vha, sp, 0);
+}
+
+static void
+qla2x00_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
+ sts_entry_t *pkt, int iocb_type)
+{
+ const char func[] = "CT_IOCB";
+ const char *type;
+ srb_t *sp;
+ struct fc_bsg_job *bsg_job;
+ uint16_t comp_status;
+ int res;
+
+ sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
+ if (!sp)
+ return;
+
+ bsg_job = sp->u.bsg_job;
+
+ type = "ct pass-through";
+
+ comp_status = le16_to_cpu(pkt->comp_status);
+
+ /* return FC_CTELS_STATUS_OK and leave the decoding of the ELS/CT
+ * fc payload to the caller
+ */
+ bsg_job->reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_OK;
+ bsg_job->reply_len = sizeof(struct fc_bsg_reply);
+
+ if (comp_status != CS_COMPLETE) {
+ if (comp_status == CS_DATA_UNDERRUN) {
+ res = DID_OK << 16;
+ bsg_job->reply->reply_payload_rcv_len =
+ le16_to_cpu(((sts_entry_t *)pkt)->rsp_info_len);
+
+ ql_log(ql_log_warn, vha, 0x5048,
+ "CT pass-through-%s error "
+ "comp_status-status=0x%x total_byte = 0x%x.\n",
+ type, comp_status,
+ bsg_job->reply->reply_payload_rcv_len);
+ } else {
+ ql_log(ql_log_warn, vha, 0x5049,
+ "CT pass-through-%s error "
+ "comp_status-status=0x%x.\n", type, comp_status);
+ res = DID_ERROR << 16;
+ bsg_job->reply->reply_payload_rcv_len = 0;
+ }
+ ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5035,
+ (uint8_t *)pkt, sizeof(*pkt));
+ } else {
+ res = DID_OK << 16;
+ bsg_job->reply->reply_payload_rcv_len =
+ bsg_job->reply_payload.payload_len;
+ bsg_job->reply_len = 0;
+ }
+
+ sp->done(vha, sp, res);
+}
+
+static void
+qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req,
+ struct sts_entry_24xx *pkt, int iocb_type)
+{
+ const char func[] = "ELS_CT_IOCB";
+ const char *type;
+ srb_t *sp;
+ struct fc_bsg_job *bsg_job;
+ uint16_t comp_status;
+ uint32_t fw_status[3];
+ uint8_t* fw_sts_ptr;
+ int res;
+
+ sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
+ if (!sp)
+ return;
+ bsg_job = sp->u.bsg_job;
+
+ type = NULL;
+ switch (sp->type) {
+ case SRB_ELS_CMD_RPT:
+ case SRB_ELS_CMD_HST:
+ type = "els";
+ break;
+ case SRB_CT_CMD:
+ type = "ct pass-through";
+ break;
+ default:
+ ql_dbg(ql_dbg_user, vha, 0x503e,
+ "Unrecognized SRB: (%p) type=%d.\n", sp, sp->type);
+ return;
+ }
+
+ comp_status = fw_status[0] = le16_to_cpu(pkt->comp_status);
+ fw_status[1] = le16_to_cpu(((struct els_sts_entry_24xx*)pkt)->error_subcode_1);
+ fw_status[2] = le16_to_cpu(((struct els_sts_entry_24xx*)pkt)->error_subcode_2);
+
+ /* return FC_CTELS_STATUS_OK and leave the decoding of the ELS/CT
+ * fc payload to the caller
+ */
+ bsg_job->reply->reply_data.ctels_reply.status = FC_CTELS_STATUS_OK;
+ bsg_job->reply_len = sizeof(struct fc_bsg_reply) + sizeof(fw_status);
+
+ if (comp_status != CS_COMPLETE) {
+ if (comp_status == CS_DATA_UNDERRUN) {
+ res = DID_OK << 16;
+ bsg_job->reply->reply_payload_rcv_len =
+ le16_to_cpu(((struct els_sts_entry_24xx *)pkt)->total_byte_count);
+
+ ql_dbg(ql_dbg_user, vha, 0x503f,
+ "ELS-CT pass-through-%s error hdl=%x comp_status-status=0x%x "
+ "error subcode 1=0x%x error subcode 2=0x%x total_byte = 0x%x.\n",
+ type, sp->handle, comp_status, fw_status[1], fw_status[2],
+ le16_to_cpu(((struct els_sts_entry_24xx *)
+ pkt)->total_byte_count));
+ fw_sts_ptr = ((uint8_t*)bsg_job->req->sense) + sizeof(struct fc_bsg_reply);
+ memcpy( fw_sts_ptr, fw_status, sizeof(fw_status));
+ }
+ else {
+ ql_dbg(ql_dbg_user, vha, 0x5040,
+ "ELS-CT pass-through-%s error hdl=%x comp_status-status=0x%x "
+ "error subcode 1=0x%x error subcode 2=0x%x.\n",
+ type, sp->handle, comp_status,
+ le16_to_cpu(((struct els_sts_entry_24xx *)
+ pkt)->error_subcode_1),
+ le16_to_cpu(((struct els_sts_entry_24xx *)
+ pkt)->error_subcode_2));
+ res = DID_ERROR << 16;
+ bsg_job->reply->reply_payload_rcv_len = 0;
+ fw_sts_ptr = ((uint8_t*)bsg_job->req->sense) + sizeof(struct fc_bsg_reply);
+ memcpy( fw_sts_ptr, fw_status, sizeof(fw_status));
+ }
+ ql_dump_buffer(ql_dbg_user + ql_dbg_buffer, vha, 0x5056,
+ (uint8_t *)pkt, sizeof(*pkt));
+ }
+ else {
+ res = DID_OK << 16;
+ bsg_job->reply->reply_payload_rcv_len = bsg_job->reply_payload.payload_len;
+ bsg_job->reply_len = 0;
+ }
+
+ sp->done(vha, sp, res);
+}
+
+static void
+qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req,
+ struct logio_entry_24xx *logio)
+{
+ const char func[] = "LOGIO-IOCB";
+ const char *type;
+ fc_port_t *fcport;
+ srb_t *sp;
+ struct srb_iocb *lio;
+ uint16_t *data;
+ uint32_t iop[2];
+
+ sp = qla2x00_get_sp_from_handle(vha, func, req, logio);
+ if (!sp)
+ return;
+
+ lio = &sp->u.iocb_cmd;
+ type = sp->name;
+ fcport = sp->fcport;
+ data = lio->u.logio.data;
+
+ data[0] = MBS_COMMAND_ERROR;
+ data[1] = lio->u.logio.flags & SRB_LOGIN_RETRIED ?
+ QLA_LOGIO_LOGIN_RETRIED : 0;
+ if (logio->entry_status) {
+ ql_log(ql_log_warn, fcport->vha, 0x5034,
+ "Async-%s error entry - hdl=%x"
+ "portid=%02x%02x%02x entry-status=%x.\n",
+ type, sp->handle, fcport->d_id.b.domain,
+ fcport->d_id.b.area, fcport->d_id.b.al_pa,
+ logio->entry_status);
+ ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x504d,
+ (uint8_t *)logio, sizeof(*logio));
+
+ goto logio_done;
+ }
+
+ if (le16_to_cpu(logio->comp_status) == CS_COMPLETE) {
+ ql_dbg(ql_dbg_async, fcport->vha, 0x5036,
+ "Async-%s complete - hdl=%x portid=%02x%02x%02x "
+ "iop0=%x.\n", type, sp->handle, fcport->d_id.b.domain,
+ fcport->d_id.b.area, fcport->d_id.b.al_pa,
+ le32_to_cpu(logio->io_parameter[0]));
+
+ data[0] = MBS_COMMAND_COMPLETE;
+ if (sp->type != SRB_LOGIN_CMD)
+ goto logio_done;
+
+ iop[0] = le32_to_cpu(logio->io_parameter[0]);
+ if (iop[0] & BIT_4) {
+ fcport->port_type = FCT_TARGET;
+ if (iop[0] & BIT_8)
+ fcport->flags |= FCF_FCP2_DEVICE;
+ } else if (iop[0] & BIT_5)
+ fcport->port_type = FCT_INITIATOR;
+
+ if (logio->io_parameter[7] || logio->io_parameter[8])
+ fcport->supported_classes |= FC_COS_CLASS2;
+ if (logio->io_parameter[9] || logio->io_parameter[10])
+ fcport->supported_classes |= FC_COS_CLASS3;
+
+ goto logio_done;
+ }
+
+ iop[0] = le32_to_cpu(logio->io_parameter[0]);
+ iop[1] = le32_to_cpu(logio->io_parameter[1]);
+ switch (iop[0]) {
+ case LSC_SCODE_PORTID_USED:
+ data[0] = MBS_PORT_ID_USED;
+ data[1] = LSW(iop[1]);
+ break;
+ case LSC_SCODE_NPORT_USED:
+ data[0] = MBS_LOOP_ID_USED;
+ break;
+ default:
+ data[0] = MBS_COMMAND_ERROR;
+ break;
+ }
+
+ ql_dbg(ql_dbg_async, fcport->vha, 0x5037,
+ "Async-%s failed - hdl=%x portid=%02x%02x%02x comp=%x "
+ "iop0=%x iop1=%x.\n", type, sp->handle, fcport->d_id.b.domain,
+ fcport->d_id.b.area, fcport->d_id.b.al_pa,
+ le16_to_cpu(logio->comp_status),
+ le32_to_cpu(logio->io_parameter[0]),
+ le32_to_cpu(logio->io_parameter[1]));
+
+logio_done:
+ sp->done(vha, sp, 0);
+}
+
+static void
+qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req,
+ struct tsk_mgmt_entry *tsk)
+{
+ const char func[] = "TMF-IOCB";
+ const char *type;
+ fc_port_t *fcport;
+ srb_t *sp;
+ struct srb_iocb *iocb;
+ struct sts_entry_24xx *sts = (struct sts_entry_24xx *)tsk;
+ int error = 1;
+
+ sp = qla2x00_get_sp_from_handle(vha, func, req, tsk);
+ if (!sp)
+ return;
+
+ iocb = &sp->u.iocb_cmd;
+ type = sp->name;
+ fcport = sp->fcport;
+
+ if (sts->entry_status) {
+ ql_log(ql_log_warn, fcport->vha, 0x5038,
+ "Async-%s error - hdl=%x entry-status(%x).\n",
+ type, sp->handle, sts->entry_status);
+ } else if (sts->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) {
+ ql_log(ql_log_warn, fcport->vha, 0x5039,
+ "Async-%s error - hdl=%x completion status(%x).\n",
+ type, sp->handle, sts->comp_status);
+ } else if (!(le16_to_cpu(sts->scsi_status) &
+ SS_RESPONSE_INFO_LEN_VALID)) {
+ ql_log(ql_log_warn, fcport->vha, 0x503a,
+ "Async-%s error - hdl=%x no response info(%x).\n",
+ type, sp->handle, sts->scsi_status);
+ } else if (le32_to_cpu(sts->rsp_data_len) < 4) {
+ ql_log(ql_log_warn, fcport->vha, 0x503b,
+ "Async-%s error - hdl=%x not enough response(%d).\n",
+ type, sp->handle, sts->rsp_data_len);
+ } else if (sts->data[3]) {
+ ql_log(ql_log_warn, fcport->vha, 0x503c,
+ "Async-%s error - hdl=%x response(%x).\n",
+ type, sp->handle, sts->data[3]);
+ } else {
+ error = 0;
+ }
+
+ if (error) {
+ iocb->u.tmf.data = error;
+ ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5055,
+ (uint8_t *)sts, sizeof(*sts));
+ }
+
+ sp->done(vha, sp, 0);
+}
+
+/**
+ * qla2x00_process_response_queue() - Process response queue entries.
+ * @ha: SCSI driver HA context
+ */
+void
+qla2x00_process_response_queue(struct rsp_que *rsp)
+{
+ struct scsi_qla_host *vha;
+ struct qla_hw_data *ha = rsp->hw;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ sts_entry_t *pkt;
+ uint16_t handle_cnt;
+ uint16_t cnt;
+
+ vha = pci_get_drvdata(ha->pdev);
+
+ if (!vha->flags.online)
+ return;
+
+ while (rsp->ring_ptr->signature != RESPONSE_PROCESSED) {
+ pkt = (sts_entry_t *)rsp->ring_ptr;
+
+ rsp->ring_index++;
+ if (rsp->ring_index == rsp->length) {
+ rsp->ring_index = 0;
+ rsp->ring_ptr = rsp->ring;
+ } else {
+ rsp->ring_ptr++;
+ }
+
+ if (pkt->entry_status != 0) {
+ qla2x00_error_entry(vha, rsp, pkt);
+ ((response_t *)pkt)->signature = RESPONSE_PROCESSED;
+ wmb();
+ continue;
+ }
+
+ switch (pkt->entry_type) {
+ case STATUS_TYPE:
+ qla2x00_status_entry(vha, rsp, pkt);
+ break;
+ case STATUS_TYPE_21:
+ handle_cnt = ((sts21_entry_t *)pkt)->handle_count;
+ for (cnt = 0; cnt < handle_cnt; cnt++) {
+ qla2x00_process_completed_request(vha, rsp->req,
+ ((sts21_entry_t *)pkt)->handle[cnt]);
+ }
+ break;
+ case STATUS_TYPE_22:
+ handle_cnt = ((sts22_entry_t *)pkt)->handle_count;
+ for (cnt = 0; cnt < handle_cnt; cnt++) {
+ qla2x00_process_completed_request(vha, rsp->req,
+ ((sts22_entry_t *)pkt)->handle[cnt]);
+ }
+ break;
+ case STATUS_CONT_TYPE:
+ qla2x00_status_cont_entry(rsp, (sts_cont_entry_t *)pkt);
+ break;
+ case MBX_IOCB_TYPE:
+ qla2x00_mbx_iocb_entry(vha, rsp->req,
+ (struct mbx_entry *)pkt);
+ break;
+ case CT_IOCB_TYPE:
+ qla2x00_ct_entry(vha, rsp->req, pkt, CT_IOCB_TYPE);
+ break;
+ default:
+ /* Type Not Supported. */
+ ql_log(ql_log_warn, vha, 0x504a,
+ "Received unknown response pkt type %x "
+ "entry status=%x.\n",
+ pkt->entry_type, pkt->entry_status);
+ break;
+ }
+ ((response_t *)pkt)->signature = RESPONSE_PROCESSED;
+ wmb();
+ }
+
+ /* Adjust ring index */
+ WRT_REG_WORD(ISP_RSP_Q_OUT(ha, reg), rsp->ring_index);
+}
+
+static inline void
+qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t par_sense_len,
+ uint32_t sense_len, struct rsp_que *rsp, int res)
+{
+ struct scsi_qla_host *vha = sp->fcport->vha;
+ struct scsi_cmnd *cp = GET_CMD_SP(sp);
+ uint32_t track_sense_len;
+
+ if (sense_len >= SCSI_SENSE_BUFFERSIZE)
+ sense_len = SCSI_SENSE_BUFFERSIZE;
+
+ SET_CMD_SENSE_LEN(sp, sense_len);
+ SET_CMD_SENSE_PTR(sp, cp->sense_buffer);
+ track_sense_len = sense_len;
+
+ if (sense_len > par_sense_len)
+ sense_len = par_sense_len;
+
+ memcpy(cp->sense_buffer, sense_data, sense_len);
+
+ SET_CMD_SENSE_PTR(sp, cp->sense_buffer + sense_len);
+ track_sense_len -= sense_len;
+ SET_CMD_SENSE_LEN(sp, track_sense_len);
+
+ if (track_sense_len != 0) {
+ rsp->status_srb = sp;
+ cp->result = res;
+ }
+
+ if (sense_len) {
+ ql_dbg(ql_dbg_io + ql_dbg_buffer, vha, 0x301c,
+ "Check condition Sense data, nexus%ld:%d:%d cmd=%p.\n",
+ sp->fcport->vha->host_no, cp->device->id, cp->device->lun,
+ cp);
+ ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x302b,
+ cp->sense_buffer, sense_len);
+ }
+}
+
+struct scsi_dif_tuple {
+ __be16 guard; /* Checksum */
+ __be16 app_tag; /* APPL identifer */
+ __be32 ref_tag; /* Target LBA or indirect LBA */
+};
+
+/*
+ * Checks the guard or meta-data for the type of error
+ * detected by the HBA. In case of errors, we set the
+ * ASC/ASCQ fields in the sense buffer with ILLEGAL_REQUEST
+ * to indicate to the kernel that the HBA detected error.
+ */
+static inline int
+qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24)
+{
+ struct scsi_qla_host *vha = sp->fcport->vha;
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
+ uint8_t *ap = &sts24->data[12];
+ uint8_t *ep = &sts24->data[20];
+ uint32_t e_ref_tag, a_ref_tag;
+ uint16_t e_app_tag, a_app_tag;
+ uint16_t e_guard, a_guard;
+
+ /*
+ * swab32 of the "data" field in the beginning of qla2x00_status_entry()
+ * would make guard field appear at offset 2
+ */
+ a_guard = le16_to_cpu(*(uint16_t *)(ap + 2));
+ a_app_tag = le16_to_cpu(*(uint16_t *)(ap + 0));
+ a_ref_tag = le32_to_cpu(*(uint32_t *)(ap + 4));
+ e_guard = le16_to_cpu(*(uint16_t *)(ep + 2));
+ e_app_tag = le16_to_cpu(*(uint16_t *)(ep + 0));
+ e_ref_tag = le32_to_cpu(*(uint32_t *)(ep + 4));
+
+ ql_dbg(ql_dbg_io, vha, 0x3023,
+ "iocb(s) %p Returned STATUS.\n", sts24);
+
+ ql_dbg(ql_dbg_io, vha, 0x3024,
+ "DIF ERROR in cmd 0x%x lba 0x%llx act ref"
+ " tag=0x%x, exp ref_tag=0x%x, act app tag=0x%x, exp app"
+ " tag=0x%x, act guard=0x%x, exp guard=0x%x.\n",
+ cmd->cmnd[0], (u64)scsi_get_lba(cmd), a_ref_tag, e_ref_tag,
+ a_app_tag, e_app_tag, a_guard, e_guard);
+
+ /*
+ * Ignore sector if:
+ * For type 3: ref & app tag is all 'f's
+ * For type 0,1,2: app tag is all 'f's
+ */
+ if ((a_app_tag == 0xffff) &&
+ ((scsi_get_prot_type(cmd) != SCSI_PROT_DIF_TYPE3) ||
+ (a_ref_tag == 0xffffffff))) {
+ uint32_t blocks_done, resid;
+ sector_t lba_s = scsi_get_lba(cmd);
+
+ /* 2TB boundary case covered automatically with this */
+ blocks_done = e_ref_tag - (uint32_t)lba_s + 1;
+
+ resid = scsi_bufflen(cmd) - (blocks_done *
+ cmd->device->sector_size);
+
+ scsi_set_resid(cmd, resid);
+ cmd->result = DID_OK << 16;
+
+ /* Update protection tag */
+ if (scsi_prot_sg_count(cmd)) {
+ uint32_t i, j = 0, k = 0, num_ent;
+ struct scatterlist *sg;
+ struct sd_dif_tuple *spt;
+
+ /* Patch the corresponding protection tags */
+ scsi_for_each_prot_sg(cmd, sg,
+ scsi_prot_sg_count(cmd), i) {
+ num_ent = sg_dma_len(sg) / 8;
+ if (k + num_ent < blocks_done) {
+ k += num_ent;
+ continue;
+ }
+ j = blocks_done - k - 1;
+ k = blocks_done;
+ break;
+ }
+
+ if (k != blocks_done) {
+ ql_log(ql_log_warn, vha, 0x302f,
+ "unexpected tag values tag:lba=%x:%llx)\n",
+ e_ref_tag, (unsigned long long)lba_s);
+ return 1;
+ }
+
+ spt = page_address(sg_page(sg)) + sg->offset;
+ spt += j;
+
+ spt->app_tag = 0xffff;
+ if (scsi_get_prot_type(cmd) == SCSI_PROT_DIF_TYPE3)
+ spt->ref_tag = 0xffffffff;
+ }
+
+ return 0;
+ }
+
+ /* check guard */
+ if (e_guard != a_guard) {
+ scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x10, 0x1);
+ set_driver_byte(cmd, DRIVER_SENSE);
+ set_host_byte(cmd, DID_ABORT);
+ cmd->result |= SAM_STAT_CHECK_CONDITION << 1;
+ return 1;
+ }
+
+ /* check ref tag */
+ if (e_ref_tag != a_ref_tag) {
+ scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x10, 0x3);
+ set_driver_byte(cmd, DRIVER_SENSE);
+ set_host_byte(cmd, DID_ABORT);
+ cmd->result |= SAM_STAT_CHECK_CONDITION << 1;
+ return 1;
+ }
+
+ /* check appl tag */
+ if (e_app_tag != a_app_tag) {
+ scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST,
+ 0x10, 0x2);
+ set_driver_byte(cmd, DRIVER_SENSE);
+ set_host_byte(cmd, DID_ABORT);
+ cmd->result |= SAM_STAT_CHECK_CONDITION << 1;
+ return 1;
+ }
+
+ return 1;
+}
+
+/**
+ * qla2x00_status_entry() - Process a Status IOCB entry.
+ * @ha: SCSI driver HA context
+ * @pkt: Entry pointer
+ */
+static void
+qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt)
+{
+ srb_t *sp;
+ fc_port_t *fcport;
+ struct scsi_cmnd *cp;
+ sts_entry_t *sts;
+ struct sts_entry_24xx *sts24;
+ uint16_t comp_status;
+ uint16_t scsi_status;
+ uint16_t ox_id;
+ uint8_t lscsi_status;
+ int32_t resid;
+ uint32_t sense_len, par_sense_len, rsp_info_len, resid_len,
+ fw_resid_len;
+ uint8_t *rsp_info, *sense_data;
+ struct qla_hw_data *ha = vha->hw;
+ uint32_t handle;
+ uint16_t que;
+ struct req_que *req;
+ int logit = 1;
+ int res = 0;
+
+ sts = (sts_entry_t *) pkt;
+ sts24 = (struct sts_entry_24xx *) pkt;
+ if (IS_FWI2_CAPABLE(ha)) {
+ comp_status = le16_to_cpu(sts24->comp_status);
+ scsi_status = le16_to_cpu(sts24->scsi_status) & SS_MASK;
+ } else {
+ comp_status = le16_to_cpu(sts->comp_status);
+ scsi_status = le16_to_cpu(sts->scsi_status) & SS_MASK;
+ }
+ handle = (uint32_t) LSW(sts->handle);
+ que = MSW(sts->handle);
+ req = ha->req_q_map[que];
+
+ /* Fast path completion. */
+ if (comp_status == CS_COMPLETE && scsi_status == 0) {
+ qla2x00_process_completed_request(vha, req, handle);
+
+ return;
+ }
+
+ /* Validate handle. */
+ if (handle < MAX_OUTSTANDING_COMMANDS) {
+ sp = req->outstanding_cmds[handle];
+ req->outstanding_cmds[handle] = NULL;
+ } else
+ sp = NULL;
+
+ if (sp == NULL) {
+ ql_dbg(ql_dbg_io, vha, 0x3017,
+ "Invalid status handle (0x%x).\n", sts->handle);
+
+ if (IS_QLA82XX(ha))
+ set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
+ else
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
+ return;
+ }
+ cp = GET_CMD_SP(sp);
+ if (cp == NULL) {
+ ql_dbg(ql_dbg_io, vha, 0x3018,
+ "Command already returned (0x%x/%p).\n",
+ sts->handle, sp);
+
+ return;
+ }
+
+ lscsi_status = scsi_status & STATUS_MASK;
+
+ fcport = sp->fcport;
+
+ ox_id = 0;
+ sense_len = par_sense_len = rsp_info_len = resid_len =
+ fw_resid_len = 0;
+ if (IS_FWI2_CAPABLE(ha)) {
+ if (scsi_status & SS_SENSE_LEN_VALID)
+ sense_len = le32_to_cpu(sts24->sense_len);
+ if (scsi_status & SS_RESPONSE_INFO_LEN_VALID)
+ rsp_info_len = le32_to_cpu(sts24->rsp_data_len);
+ if (scsi_status & (SS_RESIDUAL_UNDER | SS_RESIDUAL_OVER))
+ resid_len = le32_to_cpu(sts24->rsp_residual_count);
+ if (comp_status == CS_DATA_UNDERRUN)
+ fw_resid_len = le32_to_cpu(sts24->residual_len);
+ rsp_info = sts24->data;
+ sense_data = sts24->data;
+ host_to_fcp_swap(sts24->data, sizeof(sts24->data));
+ ox_id = le16_to_cpu(sts24->ox_id);
+ par_sense_len = sizeof(sts24->data);
+ } else {
+ if (scsi_status & SS_SENSE_LEN_VALID)
+ sense_len = le16_to_cpu(sts->req_sense_length);
+ if (scsi_status & SS_RESPONSE_INFO_LEN_VALID)
+ rsp_info_len = le16_to_cpu(sts->rsp_info_len);
+ resid_len = le32_to_cpu(sts->residual_length);
+ rsp_info = sts->rsp_info;
+ sense_data = sts->req_sense_data;
+ par_sense_len = sizeof(sts->req_sense_data);
+ }
+
+ /* Check for any FCP transport errors. */
+ if (scsi_status & SS_RESPONSE_INFO_LEN_VALID) {
+ /* Sense data lies beyond any FCP RESPONSE data. */
+ if (IS_FWI2_CAPABLE(ha)) {
+ sense_data += rsp_info_len;
+ par_sense_len -= rsp_info_len;
+ }
+ if (rsp_info_len > 3 && rsp_info[3]) {
+ ql_dbg(ql_dbg_io, fcport->vha, 0x3019,
+ "FCP I/O protocol failure (0x%x/0x%x).\n",
+ rsp_info_len, rsp_info[3]);
+
+ res = DID_BUS_BUSY << 16;
+ goto out;
+ }
+ }
+
+ /* Check for overrun. */
+ if (IS_FWI2_CAPABLE(ha) && comp_status == CS_COMPLETE &&
+ scsi_status & SS_RESIDUAL_OVER)
+ comp_status = CS_DATA_OVERRUN;
+
+ /*
+ * Based on Host and scsi status generate status code for Linux
+ */
+ switch (comp_status) {
+ case CS_COMPLETE:
+ case CS_QUEUE_FULL:
+ if (scsi_status == 0) {
+ res = DID_OK << 16;
+ break;
+ }
+ if (scsi_status & (SS_RESIDUAL_UNDER | SS_RESIDUAL_OVER)) {
+ resid = resid_len;
+ scsi_set_resid(cp, resid);
+
+ if (!lscsi_status &&
+ ((unsigned)(scsi_bufflen(cp) - resid) <
+ cp->underflow)) {
+ ql_dbg(ql_dbg_io, fcport->vha, 0x301a,
+ "Mid-layer underflow "
+ "detected (0x%x of 0x%x bytes).\n",
+ resid, scsi_bufflen(cp));
+
+ res = DID_ERROR << 16;
+ break;
+ }
+ }
+ res = DID_OK << 16 | lscsi_status;
+
+ if (lscsi_status == SAM_STAT_TASK_SET_FULL) {
+ ql_dbg(ql_dbg_io, fcport->vha, 0x301b,
+ "QUEUE FULL detected.\n");
+ break;
+ }
+ logit = 0;
+ if (lscsi_status != SS_CHECK_CONDITION)
+ break;
+
+ memset(cp->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+ if (!(scsi_status & SS_SENSE_LEN_VALID))
+ break;
+
+ qla2x00_handle_sense(sp, sense_data, par_sense_len, sense_len,
+ rsp, res);
+ break;
+
+ case CS_DATA_UNDERRUN:
+ /* Use F/W calculated residual length. */
+ resid = IS_FWI2_CAPABLE(ha) ? fw_resid_len : resid_len;
+ scsi_set_resid(cp, resid);
+ if (scsi_status & SS_RESIDUAL_UNDER) {
+ if (IS_FWI2_CAPABLE(ha) && fw_resid_len != resid_len) {
+ ql_dbg(ql_dbg_io, fcport->vha, 0x301d,
+ "Dropped frame(s) detected "
+ "(0x%x of 0x%x bytes).\n",
+ resid, scsi_bufflen(cp));
+
+ res = DID_ERROR << 16 | lscsi_status;
+ goto check_scsi_status;
+ }
+
+ if (!lscsi_status &&
+ ((unsigned)(scsi_bufflen(cp) - resid) <
+ cp->underflow)) {
+ ql_dbg(ql_dbg_io, fcport->vha, 0x301e,
+ "Mid-layer underflow "
+ "detected (0x%x of 0x%x bytes).\n",
+ resid, scsi_bufflen(cp));
+
+ res = DID_ERROR << 16;
+ break;
+ }
+ } else if (lscsi_status != SAM_STAT_TASK_SET_FULL &&
+ lscsi_status != SAM_STAT_BUSY) {
+ /*
+ * scsi status of task set and busy are considered to be
+ * task not completed.
+ */
+
+ ql_dbg(ql_dbg_io, fcport->vha, 0x301f,
+ "Dropped frame(s) detected (0x%x "
+ "of 0x%x bytes).\n", resid,
+ scsi_bufflen(cp));
+
+ res = DID_ERROR << 16 | lscsi_status;
+ goto check_scsi_status;
+ } else {
+ ql_dbg(ql_dbg_io, fcport->vha, 0x3030,
+ "scsi_status: 0x%x, lscsi_status: 0x%x\n",
+ scsi_status, lscsi_status);
+ }
+
+ res = DID_OK << 16 | lscsi_status;
+ logit = 0;
+
+check_scsi_status:
+ /*
+ * Check to see if SCSI Status is non zero. If so report SCSI
+ * Status.
+ */
+ if (lscsi_status != 0) {
+ if (lscsi_status == SAM_STAT_TASK_SET_FULL) {
+ ql_dbg(ql_dbg_io, fcport->vha, 0x3020,
+ "QUEUE FULL detected.\n");
+ logit = 1;
+ break;
+ }
+ if (lscsi_status != SS_CHECK_CONDITION)
+ break;
+
+ memset(cp->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
+ if (!(scsi_status & SS_SENSE_LEN_VALID))
+ break;
+
+ qla2x00_handle_sense(sp, sense_data, par_sense_len,
+ sense_len, rsp, res);
+ }
+ break;
+
+ case CS_PORT_LOGGED_OUT:
+ case CS_PORT_CONFIG_CHG:
+ case CS_PORT_BUSY:
+ case CS_INCOMPLETE:
+ case CS_PORT_UNAVAILABLE:
+ case CS_TIMEOUT:
+ case CS_RESET:
+
+ /*
+ * We are going to have the fc class block the rport
+ * while we try to recover so instruct the mid layer
+ * to requeue until the class decides how to handle this.
+ */
+ res = DID_TRANSPORT_DISRUPTED << 16;
+
+ if (comp_status == CS_TIMEOUT) {
+ if (IS_FWI2_CAPABLE(ha))
+ break;
+ else if ((le16_to_cpu(sts->status_flags) &
+ SF_LOGOUT_SENT) == 0)
+ break;
+ }
+
+ ql_dbg(ql_dbg_io, fcport->vha, 0x3021,
+ "Port down status: port-state=0x%x.\n",
+ atomic_read(&fcport->state));
+
+ if (atomic_read(&fcport->state) == FCS_ONLINE)
+ qla2x00_mark_device_lost(fcport->vha, fcport, 1, 1);
+ break;
+
+ case CS_ABORTED:
+ res = DID_RESET << 16;
+ break;
+
+ case CS_DIF_ERROR:
+ logit = qla2x00_handle_dif_error(sp, sts24);
+ break;
+ default:
+ res = DID_ERROR << 16;
+ break;
+ }
+
+out:
+ if (logit)
+ ql_dbg(ql_dbg_io, fcport->vha, 0x3022,
+ "FCP command status: 0x%x-0x%x (0x%x) "
+ "nexus=%ld:%d:%d portid=%02x%02x%02x oxid=0x%x "
+ "cdb=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x len=0x%x "
+ "rsp_info=0x%x resid=0x%x fw_resid=0x%x.\n",
+ comp_status, scsi_status, res, vha->host_no,
+ cp->device->id, cp->device->lun, fcport->d_id.b.domain,
+ fcport->d_id.b.area, fcport->d_id.b.al_pa, ox_id,
+ cp->cmnd[0], cp->cmnd[1], cp->cmnd[2], cp->cmnd[3],
+ cp->cmnd[4], cp->cmnd[5], cp->cmnd[6], cp->cmnd[7],
+ cp->cmnd[8], cp->cmnd[9], scsi_bufflen(cp), rsp_info_len,
+ resid_len, fw_resid_len);
+
+ if (rsp->status_srb == NULL)
+ sp->done(ha, sp, res);
+}
+
+/**
+ * qla2x00_status_cont_entry() - Process a Status Continuations entry.
+ * @ha: SCSI driver HA context
+ * @pkt: Entry pointer
+ *
+ * Extended sense data.
+ */
+static void
+qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt)
+{
+ uint8_t sense_sz = 0;
+ struct qla_hw_data *ha = rsp->hw;
+ struct scsi_qla_host *vha = pci_get_drvdata(ha->pdev);
+ srb_t *sp = rsp->status_srb;
+ struct scsi_cmnd *cp;
+ uint32_t sense_len;
+ uint8_t *sense_ptr;
+
+ if (!sp || !GET_CMD_SENSE_LEN(sp))
+ return;
+
+ sense_len = GET_CMD_SENSE_LEN(sp);
+ sense_ptr = GET_CMD_SENSE_PTR(sp);
+
+ cp = GET_CMD_SP(sp);
+ if (cp == NULL) {
+ ql_log(ql_log_warn, vha, 0x3025,
+ "cmd is NULL: already returned to OS (sp=%p).\n", sp);
+
+ rsp->status_srb = NULL;
+ return;
+ }
+
+ if (sense_len > sizeof(pkt->data))
+ sense_sz = sizeof(pkt->data);
+ else
+ sense_sz = sense_len;
+
+ /* Move sense data. */
+ if (IS_FWI2_CAPABLE(ha))
+ host_to_fcp_swap(pkt->data, sizeof(pkt->data));
+ memcpy(sense_ptr, pkt->data, sense_sz);
+ ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x302c,
+ sense_ptr, sense_sz);
+
+ sense_len -= sense_sz;
+ sense_ptr += sense_sz;
+
+ SET_CMD_SENSE_PTR(sp, sense_ptr);
+ SET_CMD_SENSE_LEN(sp, sense_len);
+
+ /* Place command on done queue. */
+ if (sense_len == 0) {
+ rsp->status_srb = NULL;
+ sp->done(ha, sp, cp->result);
+ }
+}
+
+/**
+ * qla2x00_error_entry() - Process an error entry.
+ * @ha: SCSI driver HA context
+ * @pkt: Entry pointer
+ */
+static void
+qla2x00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, sts_entry_t *pkt)
+{
+ srb_t *sp;
+ struct qla_hw_data *ha = vha->hw;
+ const char func[] = "ERROR-IOCB";
+ uint16_t que = MSW(pkt->handle);
+ struct req_que *req = NULL;
+ int res = DID_ERROR << 16;
+
+ ql_dbg(ql_dbg_async, vha, 0x502a,
+ "type of error status in response: 0x%x\n", pkt->entry_status);
+
+ if (que >= ha->max_req_queues || !ha->req_q_map[que])
+ goto fatal;
+
+ req = ha->req_q_map[que];
+
+ if (pkt->entry_status & RF_BUSY)
+ res = DID_BUS_BUSY << 16;
+
+ sp = qla2x00_get_sp_from_handle(vha, func, req, pkt);
+ if (sp) {
+ sp->done(ha, sp, res);
+ return;
+ }
+fatal:
+ ql_log(ql_log_warn, vha, 0x5030,
+ "Error entry - invalid handle/queue.\n");
+
+ if (IS_QLA82XX(ha))
+ set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags);
+ else
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
+}
+
+/**
+ * qla24xx_mbx_completion() - Process mailbox command completions.
+ * @ha: SCSI driver HA context
+ * @mb0: Mailbox0 register
+ */
+static void
+qla24xx_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0)
+{
+ uint16_t cnt;
+ uint32_t mboxes;
+ uint16_t __iomem *wptr;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+ /* Read all mbox registers? */
+ mboxes = (1 << ha->mbx_count) - 1;
+ if (!ha->mcp)
+ ql_dbg(ql_dbg_async, vha, 0x504e, "MBX pointer ERRROR.\n");
+ else
+ mboxes = ha->mcp->in_mb;
+
+ /* Load return mailbox registers. */
+ ha->flags.mbox_int = 1;
+ ha->mailbox_out[0] = mb0;
+ mboxes >>= 1;
+ wptr = (uint16_t __iomem *)&reg->mailbox1;
+
+ for (cnt = 1; cnt < ha->mbx_count; cnt++) {
+ if (mboxes & BIT_0)
+ ha->mailbox_out[cnt] = RD_REG_WORD(wptr);
+
+ mboxes >>= 1;
+ wptr++;
+ }
+}
+
+/**
+ * qla24xx_process_response_queue() - Process response queue entries.
+ * @ha: SCSI driver HA context
+ */
+void qla24xx_process_response_queue(struct scsi_qla_host *vha,
+ struct rsp_que *rsp)
+{
+ struct sts_entry_24xx *pkt;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!vha->flags.online)
+ return;
+
+ while (rsp->ring_ptr->signature != RESPONSE_PROCESSED) {
+ pkt = (struct sts_entry_24xx *)rsp->ring_ptr;
+
+ rsp->ring_index++;
+ if (rsp->ring_index == rsp->length) {
+ rsp->ring_index = 0;
+ rsp->ring_ptr = rsp->ring;
+ } else {
+ rsp->ring_ptr++;
+ }
+
+ if (pkt->entry_status != 0) {
+ qla2x00_error_entry(vha, rsp, (sts_entry_t *) pkt);
+ ((response_t *)pkt)->signature = RESPONSE_PROCESSED;
+ wmb();
+ continue;
+ }
+
+ switch (pkt->entry_type) {
+ case STATUS_TYPE:
+ qla2x00_status_entry(vha, rsp, pkt);
+ break;
+ case STATUS_CONT_TYPE:
+ qla2x00_status_cont_entry(rsp, (sts_cont_entry_t *)pkt);
+ break;
+ case VP_RPT_ID_IOCB_TYPE:
+ qla24xx_report_id_acquisition(vha,
+ (struct vp_rpt_id_entry_24xx *)pkt);
+ break;
+ case LOGINOUT_PORT_IOCB_TYPE:
+ qla24xx_logio_entry(vha, rsp->req,
+ (struct logio_entry_24xx *)pkt);
+ break;
+ case TSK_MGMT_IOCB_TYPE:
+ qla24xx_tm_iocb_entry(vha, rsp->req,
+ (struct tsk_mgmt_entry *)pkt);
+ break;
+ case CT_IOCB_TYPE:
+ qla24xx_els_ct_entry(vha, rsp->req, pkt, CT_IOCB_TYPE);
+ break;
+ case ELS_IOCB_TYPE:
+ qla24xx_els_ct_entry(vha, rsp->req, pkt, ELS_IOCB_TYPE);
+ break;
+ case MARKER_TYPE:
+ /* Do nothing in this case, this check is to prevent it
+ * from falling into default case
+ */
+ break;
+ default:
+ /* Type Not Supported. */
+ ql_dbg(ql_dbg_async, vha, 0x5042,
+ "Received unknown response pkt type %x "
+ "entry status=%x.\n",
+ pkt->entry_type, pkt->entry_status);
+ break;
+ }
+ ((response_t *)pkt)->signature = RESPONSE_PROCESSED;
+ wmb();
+ }
+
+ /* Adjust ring index */
+ if (IS_QLA82XX(ha)) {
+ struct device_reg_82xx __iomem *reg = &ha->iobase->isp82;
+ WRT_REG_DWORD(&reg->rsp_q_out[0], rsp->ring_index);
+ } else
+ WRT_REG_DWORD(rsp->rsp_q_out, rsp->ring_index);
+}
+
+static void
+qla2xxx_check_risc_status(scsi_qla_host_t *vha)
+{
+ int rval;
+ uint32_t cnt;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+ if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha))
+ return;
+
+ rval = QLA_SUCCESS;
+ WRT_REG_DWORD(&reg->iobase_addr, 0x7C00);
+ RD_REG_DWORD(&reg->iobase_addr);
+ WRT_REG_DWORD(&reg->iobase_window, 0x0001);
+ for (cnt = 10000; (RD_REG_DWORD(&reg->iobase_window) & BIT_0) == 0 &&
+ rval == QLA_SUCCESS; cnt--) {
+ if (cnt) {
+ WRT_REG_DWORD(&reg->iobase_window, 0x0001);
+ udelay(10);
+ } else
+ rval = QLA_FUNCTION_TIMEOUT;
+ }
+ if (rval == QLA_SUCCESS)
+ goto next_test;
+
+ WRT_REG_DWORD(&reg->iobase_window, 0x0003);
+ for (cnt = 100; (RD_REG_DWORD(&reg->iobase_window) & BIT_0) == 0 &&
+ rval == QLA_SUCCESS; cnt--) {
+ if (cnt) {
+ WRT_REG_DWORD(&reg->iobase_window, 0x0003);
+ udelay(10);
+ } else
+ rval = QLA_FUNCTION_TIMEOUT;
+ }
+ if (rval != QLA_SUCCESS)
+ goto done;
+
+next_test:
+ if (RD_REG_DWORD(&reg->iobase_c8) & BIT_3)
+ ql_log(ql_log_info, vha, 0x504c,
+ "Additional code -- 0x55AA.\n");
+
+done:
+ WRT_REG_DWORD(&reg->iobase_window, 0x0000);
+ RD_REG_DWORD(&reg->iobase_window);
+}
+
+/**
+ * qla24xx_intr_handler() - Process interrupts for the ISP23xx and ISP24xx.
+ * @irq:
+ * @dev_id: SCSI driver HA context
+ *
+ * Called by system whenever the host adapter generates an interrupt.
+ *
+ * Returns handled flag.
+ */
+irqreturn_t
+qla24xx_intr_handler(int irq, void *dev_id)
+{
+ scsi_qla_host_t *vha;
+ struct qla_hw_data *ha;
+ struct device_reg_24xx __iomem *reg;
+ int status;
+ unsigned long iter;
+ uint32_t stat;
+ uint32_t hccr;
+ uint16_t mb[4];
+ struct rsp_que *rsp;
+ unsigned long flags;
+
+ rsp = (struct rsp_que *) dev_id;
+ if (!rsp) {
+ ql_log(ql_log_info, NULL, 0x5059,
+ "%s: NULL response queue pointer.\n", __func__);
+ return IRQ_NONE;
+ }
+
+ ha = rsp->hw;
+ reg = &ha->iobase->isp24;
+ status = 0;
+
+ if (unlikely(pci_channel_offline(ha->pdev)))
+ return IRQ_HANDLED;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ vha = pci_get_drvdata(ha->pdev);
+ for (iter = 50; iter--; ) {
+ stat = RD_REG_DWORD(&reg->host_status);
+ if (stat & HSRX_RISC_PAUSED) {
+ if (unlikely(pci_channel_offline(ha->pdev)))
+ break;
+
+ hccr = RD_REG_DWORD(&reg->hccr);
+
+ ql_log(ql_log_warn, vha, 0x504b,
+ "RISC paused -- HCCR=%x, Dumping firmware.\n",
+ hccr);
+
+ qla2xxx_check_risc_status(vha);
+
+ ha->isp_ops->fw_dump(vha, 1);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ break;
+ } else if ((stat & HSRX_RISC_INT) == 0)
+ break;
+
+ switch (stat & 0xff) {
+ case 0x1:
+ case 0x2:
+ case 0x10:
+ case 0x11:
+ qla24xx_mbx_completion(vha, MSW(stat));
+ status |= MBX_INTERRUPT;
+
+ break;
+ case 0x12:
+ mb[0] = MSW(stat);
+ mb[1] = RD_REG_WORD(&reg->mailbox1);
+ mb[2] = RD_REG_WORD(&reg->mailbox2);
+ mb[3] = RD_REG_WORD(&reg->mailbox3);
+ qla2x00_async_event(vha, rsp, mb);
+ break;
+ case 0x13:
+ case 0x14:
+ qla24xx_process_response_queue(vha, rsp);
+ break;
+ default:
+ ql_dbg(ql_dbg_async, vha, 0x504f,
+ "Unrecognized interrupt type (%d).\n", stat * 0xff);
+ break;
+ }
+ WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_INT);
+ RD_REG_DWORD_RELAXED(&reg->hccr);
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) &&
+ (status & MBX_INTERRUPT) && ha->flags.mbox_int) {
+ set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
+ complete(&ha->mbx_intr_comp);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+qla24xx_msix_rsp_q(int irq, void *dev_id)
+{
+ struct qla_hw_data *ha;
+ struct rsp_que *rsp;
+ struct device_reg_24xx __iomem *reg;
+ struct scsi_qla_host *vha;
+ unsigned long flags;
+
+ rsp = (struct rsp_que *) dev_id;
+ if (!rsp) {
+ ql_log(ql_log_info, NULL, 0x505a,
+ "%s: NULL response queue pointer.\n", __func__);
+ return IRQ_NONE;
+ }
+ ha = rsp->hw;
+ reg = &ha->iobase->isp24;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ vha = pci_get_drvdata(ha->pdev);
+ qla24xx_process_response_queue(vha, rsp);
+ if (!ha->flags.disable_msix_handshake) {
+ WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_INT);
+ RD_REG_DWORD_RELAXED(&reg->hccr);
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+qla25xx_msix_rsp_q(int irq, void *dev_id)
+{
+ struct qla_hw_data *ha;
+ struct rsp_que *rsp;
+ struct device_reg_24xx __iomem *reg;
+ unsigned long flags;
+
+ rsp = (struct rsp_que *) dev_id;
+ if (!rsp) {
+ ql_log(ql_log_info, NULL, 0x505b,
+ "%s: NULL response queue pointer.\n", __func__);
+ return IRQ_NONE;
+ }
+ ha = rsp->hw;
+
+ /* Clear the interrupt, if enabled, for this response queue */
+ if (!ha->flags.disable_msix_handshake) {
+ reg = &ha->iobase->isp24;
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_INT);
+ RD_REG_DWORD_RELAXED(&reg->hccr);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ }
+ queue_work_on((int) (rsp->id - 1), ha->wq, &rsp->q_work);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t
+qla24xx_msix_default(int irq, void *dev_id)
+{
+ scsi_qla_host_t *vha;
+ struct qla_hw_data *ha;
+ struct rsp_que *rsp;
+ struct device_reg_24xx __iomem *reg;
+ int status;
+ uint32_t stat;
+ uint32_t hccr;
+ uint16_t mb[4];
+ unsigned long flags;
+
+ rsp = (struct rsp_que *) dev_id;
+ if (!rsp) {
+ ql_log(ql_log_info, NULL, 0x505c,
+ "%s: NULL response queue pointer.\n", __func__);
+ return IRQ_NONE;
+ }
+ ha = rsp->hw;
+ reg = &ha->iobase->isp24;
+ status = 0;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ vha = pci_get_drvdata(ha->pdev);
+ do {
+ stat = RD_REG_DWORD(&reg->host_status);
+ if (stat & HSRX_RISC_PAUSED) {
+ if (unlikely(pci_channel_offline(ha->pdev)))
+ break;
+
+ hccr = RD_REG_DWORD(&reg->hccr);
+
+ ql_log(ql_log_info, vha, 0x5050,
+ "RISC paused -- HCCR=%x, Dumping firmware.\n",
+ hccr);
+
+ qla2xxx_check_risc_status(vha);
+
+ ha->isp_ops->fw_dump(vha, 1);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ break;
+ } else if ((stat & HSRX_RISC_INT) == 0)
+ break;
+
+ switch (stat & 0xff) {
+ case 0x1:
+ case 0x2:
+ case 0x10:
+ case 0x11:
+ qla24xx_mbx_completion(vha, MSW(stat));
+ status |= MBX_INTERRUPT;
+
+ break;
+ case 0x12:
+ mb[0] = MSW(stat);
+ mb[1] = RD_REG_WORD(&reg->mailbox1);
+ mb[2] = RD_REG_WORD(&reg->mailbox2);
+ mb[3] = RD_REG_WORD(&reg->mailbox3);
+ qla2x00_async_event(vha, rsp, mb);
+ break;
+ case 0x13:
+ case 0x14:
+ qla24xx_process_response_queue(vha, rsp);
+ break;
+ default:
+ ql_dbg(ql_dbg_async, vha, 0x5051,
+ "Unrecognized interrupt type (%d).\n", stat & 0xff);
+ break;
+ }
+ WRT_REG_DWORD(&reg->hccr, HCCRX_CLR_RISC_INT);
+ } while (0);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) &&
+ (status & MBX_INTERRUPT) && ha->flags.mbox_int) {
+ set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
+ complete(&ha->mbx_intr_comp);
+ }
+ return IRQ_HANDLED;
+}
+
+/* Interrupt handling helpers. */
+
+struct qla_init_msix_entry {
+ const char *name;
+ irq_handler_t handler;
+};
+
+static struct qla_init_msix_entry msix_entries[3] = {
+ { "qla2xxx (default)", qla24xx_msix_default },
+ { "qla2xxx (rsp_q)", qla24xx_msix_rsp_q },
+ { "qla2xxx (multiq)", qla25xx_msix_rsp_q },
+};
+
+static struct qla_init_msix_entry qla82xx_msix_entries[2] = {
+ { "qla2xxx (default)", qla82xx_msix_default },
+ { "qla2xxx (rsp_q)", qla82xx_msix_rsp_q },
+};
+
+static void
+qla24xx_disable_msix(struct qla_hw_data *ha)
+{
+ int i;
+ struct qla_msix_entry *qentry;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ for (i = 0; i < ha->msix_count; i++) {
+ qentry = &ha->msix_entries[i];
+ if (qentry->have_irq)
+ free_irq(qentry->vector, qentry->rsp);
+ }
+ pci_disable_msix(ha->pdev);
+ kfree(ha->msix_entries);
+ ha->msix_entries = NULL;
+ ha->flags.msix_enabled = 0;
+ ql_dbg(ql_dbg_init, vha, 0x0042,
+ "Disabled the MSI.\n");
+}
+
+static int
+qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp)
+{
+#define MIN_MSIX_COUNT 2
+ int i, ret;
+ struct msix_entry *entries;
+ struct qla_msix_entry *qentry;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ entries = kzalloc(sizeof(struct msix_entry) * ha->msix_count,
+ GFP_KERNEL);
+ if (!entries) {
+ ql_log(ql_log_warn, vha, 0x00bc,
+ "Failed to allocate memory for msix_entry.\n");
+ return -ENOMEM;
+ }
+
+ for (i = 0; i < ha->msix_count; i++)
+ entries[i].entry = i;
+
+ ret = pci_enable_msix(ha->pdev, entries, ha->msix_count);
+ if (ret) {
+ if (ret < MIN_MSIX_COUNT)
+ goto msix_failed;
+
+ ql_log(ql_log_warn, vha, 0x00c6,
+ "MSI-X: Failed to enable support "
+ "-- %d/%d\n Retry with %d vectors.\n",
+ ha->msix_count, ret, ret);
+ ha->msix_count = ret;
+ ret = pci_enable_msix(ha->pdev, entries, ha->msix_count);
+ if (ret) {
+msix_failed:
+ ql_log(ql_log_fatal, vha, 0x00c7,
+ "MSI-X: Failed to enable support, "
+ "giving up -- %d/%d.\n",
+ ha->msix_count, ret);
+ goto msix_out;
+ }
+ ha->max_rsp_queues = ha->msix_count - 1;
+ }
+ ha->msix_entries = kzalloc(sizeof(struct qla_msix_entry) *
+ ha->msix_count, GFP_KERNEL);
+ if (!ha->msix_entries) {
+ ql_log(ql_log_fatal, vha, 0x00c8,
+ "Failed to allocate memory for ha->msix_entries.\n");
+ ret = -ENOMEM;
+ goto msix_out;
+ }
+ ha->flags.msix_enabled = 1;
+
+ for (i = 0; i < ha->msix_count; i++) {
+ qentry = &ha->msix_entries[i];
+ qentry->vector = entries[i].vector;
+ qentry->entry = entries[i].entry;
+ qentry->have_irq = 0;
+ qentry->rsp = NULL;
+ }
+
+ /* Enable MSI-X vectors for the base queue */
+ for (i = 0; i < 2; i++) {
+ qentry = &ha->msix_entries[i];
+ if (IS_QLA82XX(ha)) {
+ ret = request_irq(qentry->vector,
+ qla82xx_msix_entries[i].handler,
+ 0, qla82xx_msix_entries[i].name, rsp);
+ } else {
+ ret = request_irq(qentry->vector,
+ msix_entries[i].handler,
+ 0, msix_entries[i].name, rsp);
+ }
+ if (ret) {
+ ql_log(ql_log_fatal, vha, 0x00cb,
+ "MSI-X: unable to register handler -- %x/%d.\n",
+ qentry->vector, ret);
+ qla24xx_disable_msix(ha);
+ ha->mqenable = 0;
+ goto msix_out;
+ }
+ qentry->have_irq = 1;
+ qentry->rsp = rsp;
+ rsp->msix = qentry;
+ }
+
+ /* Enable MSI-X vector for response queue update for queue 0 */
+ if (IS_QLA83XX(ha)) {
+ if (ha->msixbase && ha->mqiobase &&
+ (ha->max_rsp_queues > 1 || ha->max_req_queues > 1))
+ ha->mqenable = 1;
+ } else
+ if (ha->mqiobase
+ && (ha->max_rsp_queues > 1 || ha->max_req_queues > 1))
+ ha->mqenable = 1;
+ ql_dbg(ql_dbg_multiq, vha, 0xc005,
+ "mqiobase=%p, max_rsp_queues=%d, max_req_queues=%d.\n",
+ ha->mqiobase, ha->max_rsp_queues, ha->max_req_queues);
+ ql_dbg(ql_dbg_init, vha, 0x0055,
+ "mqiobase=%p, max_rsp_queues=%d, max_req_queues=%d.\n",
+ ha->mqiobase, ha->max_rsp_queues, ha->max_req_queues);
+
+msix_out:
+ kfree(entries);
+ return ret;
+}
+
+int
+qla2x00_request_irqs(struct qla_hw_data *ha, struct rsp_que *rsp)
+{
+ int ret;
+ device_reg_t __iomem *reg = ha->iobase;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ /* If possible, enable MSI-X. */
+ if (!IS_QLA2432(ha) && !IS_QLA2532(ha) && !IS_QLA8432(ha) &&
+ !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha))
+ goto skip_msi;
+
+ if (ha->pdev->subsystem_vendor == PCI_VENDOR_ID_HP &&
+ (ha->pdev->subsystem_device == 0x7040 ||
+ ha->pdev->subsystem_device == 0x7041 ||
+ ha->pdev->subsystem_device == 0x1705)) {
+ ql_log(ql_log_warn, vha, 0x0034,
+ "MSI-X: Unsupported ISP 2432 SSVID/SSDID (0x%X,0x%X).\n",
+ ha->pdev->subsystem_vendor,
+ ha->pdev->subsystem_device);
+ goto skip_msi;
+ }
+
+ if (IS_QLA2432(ha) && (ha->pdev->revision < QLA_MSIX_CHIP_REV_24XX)) {
+ ql_log(ql_log_warn, vha, 0x0035,
+ "MSI-X; Unsupported ISP2432 (0x%X, 0x%X).\n",
+ ha->pdev->revision, QLA_MSIX_CHIP_REV_24XX);
+ goto skip_msix;
+ }
+
+ ret = qla24xx_enable_msix(ha, rsp);
+ if (!ret) {
+ ql_dbg(ql_dbg_init, vha, 0x0036,
+ "MSI-X: Enabled (0x%X, 0x%X).\n",
+ ha->chip_revision, ha->fw_attributes);
+ goto clear_risc_ints;
+ }
+ ql_log(ql_log_info, vha, 0x0037,
+ "MSI-X Falling back-to MSI mode -%d.\n", ret);
+skip_msix:
+
+ if (!IS_QLA24XX(ha) && !IS_QLA2532(ha) && !IS_QLA8432(ha) &&
+ !IS_QLA8001(ha))
+ goto skip_msi;
+
+ ret = pci_enable_msi(ha->pdev);
+ if (!ret) {
+ ql_dbg(ql_dbg_init, vha, 0x0038,
+ "MSI: Enabled.\n");
+ ha->flags.msi_enabled = 1;
+ } else
+ ql_log(ql_log_warn, vha, 0x0039,
+ "MSI-X; Falling back-to INTa mode -- %d.\n", ret);
+skip_msi:
+
+ ret = request_irq(ha->pdev->irq, ha->isp_ops->intr_handler,
+ ha->flags.msi_enabled ? 0 : IRQF_SHARED,
+ QLA2XXX_DRIVER_NAME, rsp);
+ if (ret) {
+ ql_log(ql_log_warn, vha, 0x003a,
+ "Failed to reserve interrupt %d already in use.\n",
+ ha->pdev->irq);
+ goto fail;
+ }
+
+clear_risc_ints:
+
+ /*
+ * FIXME: Noted that 8014s were being dropped during NK testing.
+ * Timing deltas during MSI-X/INTa transitions?
+ */
+ if (IS_QLA81XX(ha) || IS_QLA82XX(ha) || IS_QLA83XX(ha))
+ goto fail;
+ spin_lock_irq(&ha->hardware_lock);
+ if (IS_FWI2_CAPABLE(ha)) {
+ WRT_REG_DWORD(&reg->isp24.hccr, HCCRX_CLR_HOST_INT);
+ WRT_REG_DWORD(&reg->isp24.hccr, HCCRX_CLR_RISC_INT);
+ } else {
+ WRT_REG_WORD(&reg->isp.semaphore, 0);
+ WRT_REG_WORD(&reg->isp.hccr, HCCR_CLR_RISC_INT);
+ WRT_REG_WORD(&reg->isp.hccr, HCCR_CLR_HOST_INT);
+ }
+ spin_unlock_irq(&ha->hardware_lock);
+
+fail:
+ return ret;
+}
+
+void
+qla2x00_free_irqs(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ struct rsp_que *rsp = ha->rsp_q_map[0];
+
+ if (ha->flags.msix_enabled)
+ qla24xx_disable_msix(ha);
+ else if (ha->flags.msi_enabled) {
+ free_irq(ha->pdev->irq, rsp);
+ pci_disable_msi(ha->pdev);
+ } else
+ free_irq(ha->pdev->irq, rsp);
+}
+
+
+int qla25xx_request_irq(struct rsp_que *rsp)
+{
+ struct qla_hw_data *ha = rsp->hw;
+ struct qla_init_msix_entry *intr = &msix_entries[2];
+ struct qla_msix_entry *msix = rsp->msix;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+ int ret;
+
+ ret = request_irq(msix->vector, intr->handler, 0, intr->name, rsp);
+ if (ret) {
+ ql_log(ql_log_fatal, vha, 0x00e6,
+ "MSI-X: Unable to register handler -- %x/%d.\n",
+ msix->vector, ret);
+ return ret;
+ }
+ msix->have_irq = 1;
+ msix->rsp = rsp;
+ return ret;
+}
diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c
new file mode 100644
index 00000000..b4a23394
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_mbx.c
@@ -0,0 +1,4571 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2003-2011 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#include "qla_def.h"
+
+#include <linux/delay.h>
+#include <linux/gfp.h>
+
+
+/*
+ * qla2x00_mailbox_command
+ * Issue mailbox command and waits for completion.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * mcp = driver internal mbx struct pointer.
+ *
+ * Output:
+ * mb[MAX_MAILBOX_REGISTER_COUNT] = returned mailbox data.
+ *
+ * Returns:
+ * 0 : QLA_SUCCESS = cmd performed success
+ * 1 : QLA_FUNCTION_FAILED (error encountered)
+ * 6 : QLA_FUNCTION_TIMEOUT (timeout condition encountered)
+ *
+ * Context:
+ * Kernel context.
+ */
+static int
+qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp)
+{
+ int rval;
+ unsigned long flags = 0;
+ device_reg_t __iomem *reg;
+ uint8_t abort_active;
+ uint8_t io_lock_on;
+ uint16_t command = 0;
+ uint16_t *iptr;
+ uint16_t __iomem *optr;
+ uint32_t cnt;
+ uint32_t mboxes;
+ unsigned long wait_time;
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1000, "Entered %s.\n", __func__);
+
+ if (ha->pdev->error_state > pci_channel_io_frozen) {
+ ql_log(ql_log_warn, vha, 0x1001,
+ "error_state is greater than pci_channel_io_frozen, "
+ "exiting.\n");
+ return QLA_FUNCTION_TIMEOUT;
+ }
+
+ if (vha->device_flags & DFLG_DEV_FAILED) {
+ ql_log(ql_log_warn, vha, 0x1002,
+ "Device in failed state, exiting.\n");
+ return QLA_FUNCTION_TIMEOUT;
+ }
+
+ reg = ha->iobase;
+ io_lock_on = base_vha->flags.init_done;
+
+ rval = QLA_SUCCESS;
+ abort_active = test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+
+
+ if (ha->flags.pci_channel_io_perm_failure) {
+ ql_log(ql_log_warn, vha, 0x1003,
+ "Perm failure on EEH timeout MBX, exiting.\n");
+ return QLA_FUNCTION_TIMEOUT;
+ }
+
+ if (ha->flags.isp82xx_fw_hung) {
+ /* Setting Link-Down error */
+ mcp->mb[0] = MBS_LINK_DOWN_ERROR;
+ ql_log(ql_log_warn, vha, 0x1004,
+ "FW hung = %d.\n", ha->flags.isp82xx_fw_hung);
+ return QLA_FUNCTION_TIMEOUT;
+ }
+
+ /*
+ * Wait for active mailbox commands to finish by waiting at most tov
+ * seconds. This is to serialize actual issuing of mailbox cmds during
+ * non ISP abort time.
+ */
+ if (!wait_for_completion_timeout(&ha->mbx_cmd_comp, mcp->tov * HZ)) {
+ /* Timeout occurred. Return error. */
+ ql_log(ql_log_warn, vha, 0x1005,
+ "Cmd access timeout, cmd=0x%x, Exiting.\n",
+ mcp->mb[0]);
+ return QLA_FUNCTION_TIMEOUT;
+ }
+
+ ha->flags.mbox_busy = 1;
+ /* Save mailbox command for debug */
+ ha->mcp = mcp;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1006,
+ "Prepare to issue mbox cmd=0x%x.\n", mcp->mb[0]);
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Load mailbox registers. */
+ if (IS_QLA82XX(ha))
+ optr = (uint16_t __iomem *)&reg->isp82.mailbox_in[0];
+ else if (IS_FWI2_CAPABLE(ha) && !IS_QLA82XX(ha))
+ optr = (uint16_t __iomem *)&reg->isp24.mailbox0;
+ else
+ optr = (uint16_t __iomem *)MAILBOX_REG(ha, &reg->isp, 0);
+
+ iptr = mcp->mb;
+ command = mcp->mb[0];
+ mboxes = mcp->out_mb;
+
+ for (cnt = 0; cnt < ha->mbx_count; cnt++) {
+ if (IS_QLA2200(ha) && cnt == 8)
+ optr =
+ (uint16_t __iomem *)MAILBOX_REG(ha, &reg->isp, 8);
+ if (mboxes & BIT_0)
+ WRT_REG_WORD(optr, *iptr);
+
+ mboxes >>= 1;
+ optr++;
+ iptr++;
+ }
+
+ ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1111,
+ "Loaded MBX registers (displayed in bytes) =.\n");
+ ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1112,
+ (uint8_t *)mcp->mb, 16);
+ ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1113,
+ ".\n");
+ ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1114,
+ ((uint8_t *)mcp->mb + 0x10), 16);
+ ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1115,
+ ".\n");
+ ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1116,
+ ((uint8_t *)mcp->mb + 0x20), 8);
+ ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1117,
+ "I/O Address = %p.\n", optr);
+ ql_dump_regs(ql_dbg_mbx + ql_dbg_buffer, vha, 0x100e);
+
+ /* Issue set host interrupt command to send cmd out. */
+ ha->flags.mbox_int = 0;
+ clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
+
+ /* Unlock mbx registers and wait for interrupt */
+ ql_dbg(ql_dbg_mbx, vha, 0x100f,
+ "Going to unlock irq & waiting for interrupts. "
+ "jiffies=%lx.\n", jiffies);
+
+ /* Wait for mbx cmd completion until timeout */
+
+ if ((!abort_active && io_lock_on) || IS_NOPOLLING_TYPE(ha)) {
+ set_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags);
+
+ if (IS_QLA82XX(ha)) {
+ if (RD_REG_DWORD(&reg->isp82.hint) &
+ HINT_MBX_INT_PENDING) {
+ spin_unlock_irqrestore(&ha->hardware_lock,
+ flags);
+ ha->flags.mbox_busy = 0;
+ ql_dbg(ql_dbg_mbx, vha, 0x1010,
+ "Pending mailbox timeout, exiting.\n");
+ rval = QLA_FUNCTION_TIMEOUT;
+ goto premature_exit;
+ }
+ WRT_REG_DWORD(&reg->isp82.hint, HINT_MBX_INT_PENDING);
+ } else if (IS_FWI2_CAPABLE(ha))
+ WRT_REG_DWORD(&reg->isp24.hccr, HCCRX_SET_HOST_INT);
+ else
+ WRT_REG_WORD(&reg->isp.hccr, HCCR_SET_HOST_INT);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ wait_for_completion_timeout(&ha->mbx_intr_comp, mcp->tov * HZ);
+
+ clear_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags);
+
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x1011,
+ "Cmd=%x Polling Mode.\n", command);
+
+ if (IS_QLA82XX(ha)) {
+ if (RD_REG_DWORD(&reg->isp82.hint) &
+ HINT_MBX_INT_PENDING) {
+ spin_unlock_irqrestore(&ha->hardware_lock,
+ flags);
+ ha->flags.mbox_busy = 0;
+ ql_dbg(ql_dbg_mbx, vha, 0x1012,
+ "Pending mailbox timeout, exiting.\n");
+ rval = QLA_FUNCTION_TIMEOUT;
+ goto premature_exit;
+ }
+ WRT_REG_DWORD(&reg->isp82.hint, HINT_MBX_INT_PENDING);
+ } else if (IS_FWI2_CAPABLE(ha))
+ WRT_REG_DWORD(&reg->isp24.hccr, HCCRX_SET_HOST_INT);
+ else
+ WRT_REG_WORD(&reg->isp.hccr, HCCR_SET_HOST_INT);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ wait_time = jiffies + mcp->tov * HZ; /* wait at most tov secs */
+ while (!ha->flags.mbox_int) {
+ if (time_after(jiffies, wait_time))
+ break;
+
+ /* Check for pending interrupts. */
+ qla2x00_poll(ha->rsp_q_map[0]);
+
+ if (!ha->flags.mbox_int &&
+ !(IS_QLA2200(ha) &&
+ command == MBC_LOAD_RISC_RAM_EXTENDED))
+ msleep(10);
+ } /* while */
+ ql_dbg(ql_dbg_mbx, vha, 0x1013,
+ "Waited %d sec.\n",
+ (uint)((jiffies - (wait_time - (mcp->tov * HZ)))/HZ));
+ }
+
+ /* Check whether we timed out */
+ if (ha->flags.mbox_int) {
+ uint16_t *iptr2;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1014,
+ "Cmd=%x completed.\n", command);
+
+ /* Got interrupt. Clear the flag. */
+ ha->flags.mbox_int = 0;
+ clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
+
+ if (ha->flags.isp82xx_fw_hung) {
+ ha->flags.mbox_busy = 0;
+ /* Setting Link-Down error */
+ mcp->mb[0] = MBS_LINK_DOWN_ERROR;
+ ha->mcp = NULL;
+ rval = QLA_FUNCTION_FAILED;
+ ql_log(ql_log_warn, vha, 0x1015,
+ "FW hung = %d.\n", ha->flags.isp82xx_fw_hung);
+ goto premature_exit;
+ }
+
+ if (ha->mailbox_out[0] != MBS_COMMAND_COMPLETE)
+ rval = QLA_FUNCTION_FAILED;
+
+ /* Load return mailbox registers. */
+ iptr2 = mcp->mb;
+ iptr = (uint16_t *)&ha->mailbox_out[0];
+ mboxes = mcp->in_mb;
+ for (cnt = 0; cnt < ha->mbx_count; cnt++) {
+ if (mboxes & BIT_0)
+ *iptr2 = *iptr;
+
+ mboxes >>= 1;
+ iptr2++;
+ iptr++;
+ }
+ } else {
+
+ uint16_t mb0;
+ uint32_t ictrl;
+
+ if (IS_FWI2_CAPABLE(ha)) {
+ mb0 = RD_REG_WORD(&reg->isp24.mailbox0);
+ ictrl = RD_REG_DWORD(&reg->isp24.ictrl);
+ } else {
+ mb0 = RD_MAILBOX_REG(ha, &reg->isp, 0);
+ ictrl = RD_REG_WORD(&reg->isp.ictrl);
+ }
+ ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1119,
+ "MBX Command timeout for cmd %x.\n", command);
+ ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x111a,
+ "iocontrol=%x jiffies=%lx.\n", ictrl, jiffies);
+ ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x111b,
+ "mb[0] = 0x%x.\n", mb0);
+ ql_dump_regs(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1019);
+
+ /*
+ * Attempt to capture a firmware dump for further analysis
+ * of the current firmware state
+ */
+ ha->isp_ops->fw_dump(vha, 0);
+
+ rval = QLA_FUNCTION_TIMEOUT;
+ }
+
+ ha->flags.mbox_busy = 0;
+
+ /* Clean up */
+ ha->mcp = NULL;
+
+ if ((abort_active || !io_lock_on) && !IS_NOPOLLING_TYPE(ha)) {
+ ql_dbg(ql_dbg_mbx, vha, 0x101a,
+ "Checking for additional resp interrupt.\n");
+
+ /* polling mode for non isp_abort commands. */
+ qla2x00_poll(ha->rsp_q_map[0]);
+ }
+
+ if (rval == QLA_FUNCTION_TIMEOUT &&
+ mcp->mb[0] != MBC_GEN_SYSTEM_ERROR) {
+ if (!io_lock_on || (mcp->flags & IOCTL_CMD) ||
+ ha->flags.eeh_busy) {
+ /* not in dpc. schedule it for dpc to take over. */
+ ql_dbg(ql_dbg_mbx, vha, 0x101b,
+ "Timeout, schedule isp_abort_needed.\n");
+
+ if (!test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) &&
+ !test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) &&
+ !test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) {
+ if (IS_QLA82XX(ha)) {
+ ql_dbg(ql_dbg_mbx, vha, 0x112a,
+ "disabling pause transmit on port "
+ "0 & 1.\n");
+ qla82xx_wr_32(ha,
+ QLA82XX_CRB_NIU + 0x98,
+ CRB_NIU_XG_PAUSE_CTL_P0|
+ CRB_NIU_XG_PAUSE_CTL_P1);
+ }
+ ql_log(ql_log_info, base_vha, 0x101c,
+ "Mailbox cmd timeout occured, cmd=0x%x, "
+ "mb[0]=0x%x, eeh_busy=0x%x. Scheduling ISP "
+ "abort.\n", command, mcp->mb[0],
+ ha->flags.eeh_busy);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
+ }
+ } else if (!abort_active) {
+ /* call abort directly since we are in the DPC thread */
+ ql_dbg(ql_dbg_mbx, vha, 0x101d,
+ "Timeout, calling abort_isp.\n");
+
+ if (!test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) &&
+ !test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) &&
+ !test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) {
+ if (IS_QLA82XX(ha)) {
+ ql_dbg(ql_dbg_mbx, vha, 0x112b,
+ "disabling pause transmit on port "
+ "0 & 1.\n");
+ qla82xx_wr_32(ha,
+ QLA82XX_CRB_NIU + 0x98,
+ CRB_NIU_XG_PAUSE_CTL_P0|
+ CRB_NIU_XG_PAUSE_CTL_P1);
+ }
+ ql_log(ql_log_info, base_vha, 0x101e,
+ "Mailbox cmd timeout occured, cmd=0x%x, "
+ "mb[0]=0x%x. Scheduling ISP abort ",
+ command, mcp->mb[0]);
+ set_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags);
+ clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ /* Allow next mbx cmd to come in. */
+ complete(&ha->mbx_cmd_comp);
+ if (ha->isp_ops->abort_isp(vha)) {
+ /* Failed. retry later. */
+ set_bit(ISP_ABORT_NEEDED,
+ &vha->dpc_flags);
+ }
+ clear_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags);
+ ql_dbg(ql_dbg_mbx, vha, 0x101f,
+ "Finished abort_isp.\n");
+ goto mbx_done;
+ }
+ }
+ }
+
+premature_exit:
+ /* Allow next mbx cmd to come in. */
+ complete(&ha->mbx_cmd_comp);
+
+mbx_done:
+ if (rval) {
+ ql_dbg(ql_dbg_mbx, base_vha, 0x1020,
+ "**** Failed mbx[0]=%x, mb[1]=%x, mb[2]=%x, mb[3]=%x, cmd=%x ****.\n",
+ mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3], command);
+ } else {
+ ql_dbg(ql_dbg_mbx, base_vha, 0x1021, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla2x00_load_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t risc_addr,
+ uint32_t risc_code_size)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1022, "Entered %s.\n", __func__);
+
+ if (MSW(risc_addr) || IS_FWI2_CAPABLE(ha)) {
+ mcp->mb[0] = MBC_LOAD_RISC_RAM_EXTENDED;
+ mcp->mb[8] = MSW(risc_addr);
+ mcp->out_mb = MBX_8|MBX_0;
+ } else {
+ mcp->mb[0] = MBC_LOAD_RISC_RAM;
+ mcp->out_mb = MBX_0;
+ }
+ mcp->mb[1] = LSW(risc_addr);
+ mcp->mb[2] = MSW(req_dma);
+ mcp->mb[3] = LSW(req_dma);
+ mcp->mb[6] = MSW(MSD(req_dma));
+ mcp->mb[7] = LSW(MSD(req_dma));
+ mcp->out_mb |= MBX_7|MBX_6|MBX_3|MBX_2|MBX_1;
+ if (IS_FWI2_CAPABLE(ha)) {
+ mcp->mb[4] = MSW(risc_code_size);
+ mcp->mb[5] = LSW(risc_code_size);
+ mcp->out_mb |= MBX_5|MBX_4;
+ } else {
+ mcp->mb[4] = LSW(risc_code_size);
+ mcp->out_mb |= MBX_4;
+ }
+
+ mcp->in_mb = MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1023,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x1024, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+#define EXTENDED_BB_CREDITS BIT_0
+/*
+ * qla2x00_execute_fw
+ * Start adapter firmware.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1025, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_EXECUTE_FIRMWARE;
+ mcp->out_mb = MBX_0;
+ mcp->in_mb = MBX_0;
+ if (IS_FWI2_CAPABLE(ha)) {
+ mcp->mb[1] = MSW(risc_addr);
+ mcp->mb[2] = LSW(risc_addr);
+ mcp->mb[3] = 0;
+ if (IS_QLA81XX(ha) || IS_QLA83XX(ha)) {
+ struct nvram_81xx *nv = ha->nvram;
+ mcp->mb[4] = (nv->enhanced_features &
+ EXTENDED_BB_CREDITS);
+ } else
+ mcp->mb[4] = 0;
+ mcp->out_mb |= MBX_4|MBX_3|MBX_2|MBX_1;
+ mcp->in_mb |= MBX_1;
+ } else {
+ mcp->mb[1] = LSW(risc_addr);
+ mcp->out_mb |= MBX_1;
+ if (IS_QLA2322(ha) || IS_QLA6322(ha)) {
+ mcp->mb[2] = 0;
+ mcp->out_mb |= MBX_2;
+ }
+ }
+
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1026,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ if (IS_FWI2_CAPABLE(ha)) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1027,
+ "Done exchanges=%x.\n", mcp->mb[1]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x1028, "Done %s.\n", __func__);
+ }
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_get_fw_version
+ * Get firmware version.
+ *
+ * Input:
+ * ha: adapter state pointer.
+ * major: pointer for major number.
+ * minor: pointer for minor number.
+ * subminor: pointer for subminor number.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_get_fw_version(scsi_qla_host_t *vha)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1029, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_GET_FIRMWARE_VERSION;
+ mcp->out_mb = MBX_0;
+ mcp->in_mb = MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ if (IS_QLA81XX(vha->hw) || IS_QLA8031(ha))
+ mcp->in_mb |= MBX_13|MBX_12|MBX_11|MBX_10|MBX_9|MBX_8;
+ if (IS_QLA83XX(vha->hw))
+ mcp->in_mb |= MBX_17|MBX_16|MBX_15;
+ mcp->flags = 0;
+ mcp->tov = MBX_TOV_SECONDS;
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS)
+ goto failed;
+
+ /* Return mailbox data. */
+ ha->fw_major_version = mcp->mb[1];
+ ha->fw_minor_version = mcp->mb[2];
+ ha->fw_subminor_version = mcp->mb[3];
+ ha->fw_attributes = mcp->mb[6];
+ if (IS_QLA2100(vha->hw) || IS_QLA2200(vha->hw))
+ ha->fw_memory_size = 0x1FFFF; /* Defaults to 128KB. */
+ else
+ ha->fw_memory_size = (mcp->mb[5] << 16) | mcp->mb[4];
+ if (IS_QLA81XX(vha->hw) || IS_QLA8031(vha->hw)) {
+ ha->mpi_version[0] = mcp->mb[10] & 0xff;
+ ha->mpi_version[1] = mcp->mb[11] >> 8;
+ ha->mpi_version[2] = mcp->mb[11] & 0xff;
+ ha->mpi_capabilities = (mcp->mb[12] << 16) | mcp->mb[13];
+ ha->phy_version[0] = mcp->mb[8] & 0xff;
+ ha->phy_version[1] = mcp->mb[9] >> 8;
+ ha->phy_version[2] = mcp->mb[9] & 0xff;
+ }
+ if (IS_QLA83XX(ha)) {
+ if (mcp->mb[6] & BIT_15) {
+ ha->fw_attributes_h = mcp->mb[15];
+ ha->fw_attributes_ext[0] = mcp->mb[16];
+ ha->fw_attributes_ext[1] = mcp->mb[17];
+ ql_dbg(ql_dbg_mbx, vha, 0x1139,
+ "%s: FW_attributes Upper: 0x%x, Lower: 0x%x.\n",
+ __func__, mcp->mb[15], mcp->mb[6]);
+ } else
+ ql_dbg(ql_dbg_mbx, vha, 0x112f,
+ "%s: FwAttributes [Upper] invalid, MB6:%04x\n",
+ __func__, mcp->mb[6]);
+ }
+
+failed:
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x102a, "Failed=%x.\n", rval);
+ } else {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x102b, "Done %s.\n", __func__);
+ }
+ return rval;
+}
+
+/*
+ * qla2x00_get_fw_options
+ * Set firmware options.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * fwopt = pointer for firmware options.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_get_fw_options(scsi_qla_host_t *vha, uint16_t *fwopts)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x102c, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_GET_FIRMWARE_OPTION;
+ mcp->out_mb = MBX_0;
+ mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x102d, "Failed=%x.\n", rval);
+ } else {
+ fwopts[0] = mcp->mb[0];
+ fwopts[1] = mcp->mb[1];
+ fwopts[2] = mcp->mb[2];
+ fwopts[3] = mcp->mb[3];
+
+ ql_dbg(ql_dbg_mbx, vha, 0x102e, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+
+/*
+ * qla2x00_set_fw_options
+ * Set firmware options.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * fwopt = pointer for firmware options.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_set_fw_options(scsi_qla_host_t *vha, uint16_t *fwopts)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x102f, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_SET_FIRMWARE_OPTION;
+ mcp->mb[1] = fwopts[1];
+ mcp->mb[2] = fwopts[2];
+ mcp->mb[3] = fwopts[3];
+ mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ if (IS_FWI2_CAPABLE(vha->hw)) {
+ mcp->in_mb |= MBX_1;
+ } else {
+ mcp->mb[10] = fwopts[10];
+ mcp->mb[11] = fwopts[11];
+ mcp->mb[12] = 0; /* Undocumented, but used */
+ mcp->out_mb |= MBX_12|MBX_11|MBX_10;
+ }
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ fwopts[0] = mcp->mb[0];
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x1030,
+ "Failed=%x (%x/%x).\n", rval, mcp->mb[0], mcp->mb[1]);
+ } else {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x1031, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_mbx_reg_test
+ * Mailbox register wrap test.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_mbx_reg_test(scsi_qla_host_t *vha)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1032, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_MAILBOX_REGISTER_TEST;
+ mcp->mb[1] = 0xAAAA;
+ mcp->mb[2] = 0x5555;
+ mcp->mb[3] = 0xAA55;
+ mcp->mb[4] = 0x55AA;
+ mcp->mb[5] = 0xA5A5;
+ mcp->mb[6] = 0x5A5A;
+ mcp->mb[7] = 0x2525;
+ mcp->out_mb = MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval == QLA_SUCCESS) {
+ if (mcp->mb[1] != 0xAAAA || mcp->mb[2] != 0x5555 ||
+ mcp->mb[3] != 0xAA55 || mcp->mb[4] != 0x55AA)
+ rval = QLA_FUNCTION_FAILED;
+ if (mcp->mb[5] != 0xA5A5 || mcp->mb[6] != 0x5A5A ||
+ mcp->mb[7] != 0x2525)
+ rval = QLA_FUNCTION_FAILED;
+ }
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x1033, "Failed=%x.\n", rval);
+ } else {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x1034, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_verify_checksum
+ * Verify firmware checksum.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_verify_checksum(scsi_qla_host_t *vha, uint32_t risc_addr)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1035, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_VERIFY_CHECKSUM;
+ mcp->out_mb = MBX_0;
+ mcp->in_mb = MBX_0;
+ if (IS_FWI2_CAPABLE(vha->hw)) {
+ mcp->mb[1] = MSW(risc_addr);
+ mcp->mb[2] = LSW(risc_addr);
+ mcp->out_mb |= MBX_2|MBX_1;
+ mcp->in_mb |= MBX_2|MBX_1;
+ } else {
+ mcp->mb[1] = LSW(risc_addr);
+ mcp->out_mb |= MBX_1;
+ mcp->in_mb |= MBX_1;
+ }
+
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1036,
+ "Failed=%x chm sum=%x.\n", rval, IS_FWI2_CAPABLE(vha->hw) ?
+ (mcp->mb[2] << 16) | mcp->mb[1] : mcp->mb[1]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x1037, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_issue_iocb
+ * Issue IOCB using mailbox command
+ *
+ * Input:
+ * ha = adapter state pointer.
+ * buffer = buffer pointer.
+ * phys_addr = physical address of buffer.
+ * size = size of buffer.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_issue_iocb_timeout(scsi_qla_host_t *vha, void *buffer,
+ dma_addr_t phys_addr, size_t size, uint32_t tov)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1038, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_IOCB_COMMAND_A64;
+ mcp->mb[1] = 0;
+ mcp->mb[2] = MSW(phys_addr);
+ mcp->mb[3] = LSW(phys_addr);
+ mcp->mb[6] = MSW(MSD(phys_addr));
+ mcp->mb[7] = LSW(MSD(phys_addr));
+ mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_2|MBX_0;
+ mcp->tov = tov;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x1039, "Failed=%x.\n", rval);
+ } else {
+ sts_entry_t *sts_entry = (sts_entry_t *) buffer;
+
+ /* Mask reserved bits. */
+ sts_entry->entry_status &=
+ IS_FWI2_CAPABLE(vha->hw) ? RF_MASK_24XX : RF_MASK;
+ ql_dbg(ql_dbg_mbx, vha, 0x103a, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla2x00_issue_iocb(scsi_qla_host_t *vha, void *buffer, dma_addr_t phys_addr,
+ size_t size)
+{
+ return qla2x00_issue_iocb_timeout(vha, buffer, phys_addr, size,
+ MBX_TOV_SECONDS);
+}
+
+/*
+ * qla2x00_abort_command
+ * Abort command aborts a specified IOCB.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * sp = SB structure pointer.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_abort_command(srb_t *sp)
+{
+ unsigned long flags = 0;
+ int rval;
+ uint32_t handle = 0;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ fc_port_t *fcport = sp->fcport;
+ scsi_qla_host_t *vha = fcport->vha;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = vha->req;
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
+
+ ql_dbg(ql_dbg_mbx, vha, 0x103b, "Entered %s.\n", __func__);
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) {
+ if (req->outstanding_cmds[handle] == sp)
+ break;
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ if (handle == MAX_OUTSTANDING_COMMANDS) {
+ /* command not found */
+ return QLA_FUNCTION_FAILED;
+ }
+
+ mcp->mb[0] = MBC_ABORT_COMMAND;
+ if (HAS_EXTENDED_IDS(ha))
+ mcp->mb[1] = fcport->loop_id;
+ else
+ mcp->mb[1] = fcport->loop_id << 8;
+ mcp->mb[2] = (uint16_t)handle;
+ mcp->mb[3] = (uint16_t)(handle >> 16);
+ mcp->mb[6] = (uint16_t)cmd->device->lun;
+ mcp->out_mb = MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x103c, "Failed=%x.\n", rval);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x103d, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla2x00_abort_target(struct fc_port *fcport, unsigned int l, int tag)
+{
+ int rval, rval2;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ scsi_qla_host_t *vha;
+ struct req_que *req;
+ struct rsp_que *rsp;
+
+ l = l;
+ vha = fcport->vha;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x103e, "Entered %s.\n", __func__);
+
+ req = vha->hw->req_q_map[0];
+ rsp = req->rsp;
+ mcp->mb[0] = MBC_ABORT_TARGET;
+ mcp->out_mb = MBX_9|MBX_2|MBX_1|MBX_0;
+ if (HAS_EXTENDED_IDS(vha->hw)) {
+ mcp->mb[1] = fcport->loop_id;
+ mcp->mb[10] = 0;
+ mcp->out_mb |= MBX_10;
+ } else {
+ mcp->mb[1] = fcport->loop_id << 8;
+ }
+ mcp->mb[2] = vha->hw->loop_reset_delay;
+ mcp->mb[9] = vha->vp_idx;
+
+ mcp->in_mb = MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x103f, "Failed=%x.\n", rval);
+ }
+
+ /* Issue marker IOCB. */
+ rval2 = qla2x00_marker(vha, req, rsp, fcport->loop_id, 0,
+ MK_SYNC_ID);
+ if (rval2 != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1040,
+ "Failed to issue marker IOCB (%x).\n", rval2);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x1041, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla2x00_lun_reset(struct fc_port *fcport, unsigned int l, int tag)
+{
+ int rval, rval2;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ scsi_qla_host_t *vha;
+ struct req_que *req;
+ struct rsp_que *rsp;
+
+ vha = fcport->vha;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1042, "Entered %s.\n", __func__);
+
+ req = vha->hw->req_q_map[0];
+ rsp = req->rsp;
+ mcp->mb[0] = MBC_LUN_RESET;
+ mcp->out_mb = MBX_9|MBX_3|MBX_2|MBX_1|MBX_0;
+ if (HAS_EXTENDED_IDS(vha->hw))
+ mcp->mb[1] = fcport->loop_id;
+ else
+ mcp->mb[1] = fcport->loop_id << 8;
+ mcp->mb[2] = l;
+ mcp->mb[3] = 0;
+ mcp->mb[9] = vha->vp_idx;
+
+ mcp->in_mb = MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1043, "Failed=%x.\n", rval);
+ }
+
+ /* Issue marker IOCB. */
+ rval2 = qla2x00_marker(vha, req, rsp, fcport->loop_id, l,
+ MK_SYNC_ID_LUN);
+ if (rval2 != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1044,
+ "Failed to issue marker IOCB (%x).\n", rval2);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x1045, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_get_adapter_id
+ * Get adapter ID and topology.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * id = pointer for loop ID.
+ * al_pa = pointer for AL_PA.
+ * area = pointer for area.
+ * domain = pointer for domain.
+ * top = pointer for topology.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa,
+ uint8_t *area, uint8_t *domain, uint16_t *top, uint16_t *sw_cap)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1046, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_GET_ADAPTER_LOOP_ID;
+ mcp->mb[9] = vha->vp_idx;
+ mcp->out_mb = MBX_9|MBX_0;
+ mcp->in_mb = MBX_9|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ if (IS_CNA_CAPABLE(vha->hw))
+ mcp->in_mb |= MBX_13|MBX_12|MBX_11|MBX_10;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (mcp->mb[0] == MBS_COMMAND_ERROR)
+ rval = QLA_COMMAND_ERROR;
+ else if (mcp->mb[0] == MBS_INVALID_COMMAND)
+ rval = QLA_INVALID_COMMAND;
+
+ /* Return data. */
+ *id = mcp->mb[1];
+ *al_pa = LSB(mcp->mb[2]);
+ *area = MSB(mcp->mb[2]);
+ *domain = LSB(mcp->mb[3]);
+ *top = mcp->mb[6];
+ *sw_cap = mcp->mb[7];
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x1047, "Failed=%x.\n", rval);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x1048, "Done %s.\n", __func__);
+
+ if (IS_CNA_CAPABLE(vha->hw)) {
+ vha->fcoe_vlan_id = mcp->mb[9] & 0xfff;
+ vha->fcoe_fcf_idx = mcp->mb[10];
+ vha->fcoe_vn_port_mac[5] = mcp->mb[11] >> 8;
+ vha->fcoe_vn_port_mac[4] = mcp->mb[11] & 0xff;
+ vha->fcoe_vn_port_mac[3] = mcp->mb[12] >> 8;
+ vha->fcoe_vn_port_mac[2] = mcp->mb[12] & 0xff;
+ vha->fcoe_vn_port_mac[1] = mcp->mb[13] >> 8;
+ vha->fcoe_vn_port_mac[0] = mcp->mb[13] & 0xff;
+ }
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_get_retry_cnt
+ * Get current firmware login retry count and delay.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * retry_cnt = pointer to login retry count.
+ * tov = pointer to login timeout value.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_get_retry_cnt(scsi_qla_host_t *vha, uint8_t *retry_cnt, uint8_t *tov,
+ uint16_t *r_a_tov)
+{
+ int rval;
+ uint16_t ratov;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1049, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_GET_RETRY_COUNT;
+ mcp->out_mb = MBX_0;
+ mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x104a,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ /* Convert returned data and check our values. */
+ *r_a_tov = mcp->mb[3] / 2;
+ ratov = (mcp->mb[3]/2) / 10; /* mb[3] value is in 100ms */
+ if (mcp->mb[1] * ratov > (*retry_cnt) * (*tov)) {
+ /* Update to the larger values */
+ *retry_cnt = (uint8_t)mcp->mb[1];
+ *tov = ratov;
+ }
+
+ ql_dbg(ql_dbg_mbx, vha, 0x104b,
+ "Done %s mb3=%d ratov=%d.\n", __func__, mcp->mb[3], ratov);
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_init_firmware
+ * Initialize adapter firmware.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * dptr = Initialization control block pointer.
+ * size = size of initialization control block.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x104c, "Entered %s.\n", __func__);
+
+ if (IS_QLA82XX(ha) && ql2xdbwr)
+ qla82xx_wr_32(ha, ha->nxdb_wr_ptr,
+ (0x04 | (ha->portnum << 5) | (0 << 8) | (0 << 16)));
+
+ if (ha->flags.npiv_supported)
+ mcp->mb[0] = MBC_MID_INITIALIZE_FIRMWARE;
+ else
+ mcp->mb[0] = MBC_INITIALIZE_FIRMWARE;
+
+ mcp->mb[1] = 0;
+ mcp->mb[2] = MSW(ha->init_cb_dma);
+ mcp->mb[3] = LSW(ha->init_cb_dma);
+ mcp->mb[6] = MSW(MSD(ha->init_cb_dma));
+ mcp->mb[7] = LSW(MSD(ha->init_cb_dma));
+ mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ if ((IS_QLA81XX(ha) || IS_QLA83XX(ha)) && ha->ex_init_cb->ex_version) {
+ mcp->mb[1] = BIT_0;
+ mcp->mb[10] = MSW(ha->ex_init_cb_dma);
+ mcp->mb[11] = LSW(ha->ex_init_cb_dma);
+ mcp->mb[12] = MSW(MSD(ha->ex_init_cb_dma));
+ mcp->mb[13] = LSW(MSD(ha->ex_init_cb_dma));
+ mcp->mb[14] = sizeof(*ha->ex_init_cb);
+ mcp->out_mb |= MBX_14|MBX_13|MBX_12|MBX_11|MBX_10;
+ }
+ /* 1 and 2 should normally be captured. */
+ mcp->in_mb = MBX_2|MBX_1|MBX_0;
+ if (IS_QLA83XX(ha))
+ /* mb3 is additional info about the installed SFP. */
+ mcp->in_mb |= MBX_3;
+ mcp->buf_size = size;
+ mcp->flags = MBX_DMA_OUT;
+ mcp->tov = MBX_TOV_SECONDS;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x104d,
+ "Failed=%x mb[0]=%x, mb[1]=%x, mb[2]=%x, mb[3]=%x,.\n",
+ rval, mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3]);
+ } else {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x104e, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_get_port_database
+ * Issue normal/enhanced get port database mailbox command
+ * and copy device name as necessary.
+ *
+ * Input:
+ * ha = adapter state pointer.
+ * dev = structure pointer.
+ * opt = enhanced cmd option byte.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_get_port_database(scsi_qla_host_t *vha, fc_port_t *fcport, uint8_t opt)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ port_database_t *pd;
+ struct port_database_24xx *pd24;
+ dma_addr_t pd_dma;
+ struct qla_hw_data *ha = vha->hw;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x104f, "Entered %s.\n", __func__);
+
+ pd24 = NULL;
+ pd = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &pd_dma);
+ if (pd == NULL) {
+ ql_log(ql_log_warn, vha, 0x1050,
+ "Failed to allocate port database structure.\n");
+ return QLA_MEMORY_ALLOC_FAILED;
+ }
+ memset(pd, 0, max(PORT_DATABASE_SIZE, PORT_DATABASE_24XX_SIZE));
+
+ mcp->mb[0] = MBC_GET_PORT_DATABASE;
+ if (opt != 0 && !IS_FWI2_CAPABLE(ha))
+ mcp->mb[0] = MBC_ENHANCED_GET_PORT_DATABASE;
+ mcp->mb[2] = MSW(pd_dma);
+ mcp->mb[3] = LSW(pd_dma);
+ mcp->mb[6] = MSW(MSD(pd_dma));
+ mcp->mb[7] = LSW(MSD(pd_dma));
+ mcp->mb[9] = vha->vp_idx;
+ mcp->out_mb = MBX_9|MBX_7|MBX_6|MBX_3|MBX_2|MBX_0;
+ mcp->in_mb = MBX_0;
+ if (IS_FWI2_CAPABLE(ha)) {
+ mcp->mb[1] = fcport->loop_id;
+ mcp->mb[10] = opt;
+ mcp->out_mb |= MBX_10|MBX_1;
+ mcp->in_mb |= MBX_1;
+ } else if (HAS_EXTENDED_IDS(ha)) {
+ mcp->mb[1] = fcport->loop_id;
+ mcp->mb[10] = opt;
+ mcp->out_mb |= MBX_10|MBX_1;
+ } else {
+ mcp->mb[1] = fcport->loop_id << 8 | opt;
+ mcp->out_mb |= MBX_1;
+ }
+ mcp->buf_size = IS_FWI2_CAPABLE(ha) ?
+ PORT_DATABASE_24XX_SIZE : PORT_DATABASE_SIZE;
+ mcp->flags = MBX_DMA_IN;
+ mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2);
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS)
+ goto gpd_error_out;
+
+ if (IS_FWI2_CAPABLE(ha)) {
+ uint64_t zero = 0;
+ pd24 = (struct port_database_24xx *) pd;
+
+ /* Check for logged in state. */
+ if (pd24->current_login_state != PDS_PRLI_COMPLETE &&
+ pd24->last_login_state != PDS_PRLI_COMPLETE) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1051,
+ "Unable to verify login-state (%x/%x) for "
+ "loop_id %x.\n", pd24->current_login_state,
+ pd24->last_login_state, fcport->loop_id);
+ rval = QLA_FUNCTION_FAILED;
+ goto gpd_error_out;
+ }
+
+ if (fcport->loop_id == FC_NO_LOOP_ID ||
+ (memcmp(fcport->port_name, (uint8_t *)&zero, 8) &&
+ memcmp(fcport->port_name, pd24->port_name, 8))) {
+ /* We lost the device mid way. */
+ rval = QLA_NOT_LOGGED_IN;
+ goto gpd_error_out;
+ }
+
+ /* Names are little-endian. */
+ memcpy(fcport->node_name, pd24->node_name, WWN_SIZE);
+ memcpy(fcport->port_name, pd24->port_name, WWN_SIZE);
+
+ /* Get port_id of device. */
+ fcport->d_id.b.domain = pd24->port_id[0];
+ fcport->d_id.b.area = pd24->port_id[1];
+ fcport->d_id.b.al_pa = pd24->port_id[2];
+ fcport->d_id.b.rsvd_1 = 0;
+
+ /* If not target must be initiator or unknown type. */
+ if ((pd24->prli_svc_param_word_3[0] & BIT_4) == 0)
+ fcport->port_type = FCT_INITIATOR;
+ else
+ fcport->port_type = FCT_TARGET;
+ } else {
+ uint64_t zero = 0;
+
+ /* Check for logged in state. */
+ if (pd->master_state != PD_STATE_PORT_LOGGED_IN &&
+ pd->slave_state != PD_STATE_PORT_LOGGED_IN) {
+ ql_dbg(ql_dbg_mbx, vha, 0x100a,
+ "Unable to verify login-state (%x/%x) - "
+ "portid=%02x%02x%02x.\n", pd->master_state,
+ pd->slave_state, fcport->d_id.b.domain,
+ fcport->d_id.b.area, fcport->d_id.b.al_pa);
+ rval = QLA_FUNCTION_FAILED;
+ goto gpd_error_out;
+ }
+
+ if (fcport->loop_id == FC_NO_LOOP_ID ||
+ (memcmp(fcport->port_name, (uint8_t *)&zero, 8) &&
+ memcmp(fcport->port_name, pd->port_name, 8))) {
+ /* We lost the device mid way. */
+ rval = QLA_NOT_LOGGED_IN;
+ goto gpd_error_out;
+ }
+
+ /* Names are little-endian. */
+ memcpy(fcport->node_name, pd->node_name, WWN_SIZE);
+ memcpy(fcport->port_name, pd->port_name, WWN_SIZE);
+
+ /* Get port_id of device. */
+ fcport->d_id.b.domain = pd->port_id[0];
+ fcport->d_id.b.area = pd->port_id[3];
+ fcport->d_id.b.al_pa = pd->port_id[2];
+ fcport->d_id.b.rsvd_1 = 0;
+
+ /* If not target must be initiator or unknown type. */
+ if ((pd->prli_svc_param_word_3[0] & BIT_4) == 0)
+ fcport->port_type = FCT_INITIATOR;
+ else
+ fcport->port_type = FCT_TARGET;
+
+ /* Passback COS information. */
+ fcport->supported_classes = (pd->options & BIT_4) ?
+ FC_COS_CLASS2: FC_COS_CLASS3;
+ }
+
+gpd_error_out:
+ dma_pool_free(ha->s_dma_pool, pd, pd_dma);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1052,
+ "Failed=%x mb[0]=%x mb[1]=%x.\n", rval,
+ mcp->mb[0], mcp->mb[1]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x1053, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_get_firmware_state
+ * Get adapter firmware state.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * dptr = pointer for firmware state.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1054, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_GET_FIRMWARE_STATE;
+ mcp->out_mb = MBX_0;
+ if (IS_FWI2_CAPABLE(vha->hw))
+ mcp->in_mb = MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ else
+ mcp->in_mb = MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ /* Return firmware states. */
+ states[0] = mcp->mb[1];
+ if (IS_FWI2_CAPABLE(vha->hw)) {
+ states[1] = mcp->mb[2];
+ states[2] = mcp->mb[3];
+ states[3] = mcp->mb[4];
+ states[4] = mcp->mb[5];
+ }
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x1055, "Failed=%x.\n", rval);
+ } else {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x1056, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_get_port_name
+ * Issue get port name mailbox command.
+ * Returned name is in big endian format.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * loop_id = loop ID of device.
+ * name = pointer for name.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_get_port_name(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t *name,
+ uint8_t opt)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1057, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_GET_PORT_NAME;
+ mcp->mb[9] = vha->vp_idx;
+ mcp->out_mb = MBX_9|MBX_1|MBX_0;
+ if (HAS_EXTENDED_IDS(vha->hw)) {
+ mcp->mb[1] = loop_id;
+ mcp->mb[10] = opt;
+ mcp->out_mb |= MBX_10;
+ } else {
+ mcp->mb[1] = loop_id << 8 | opt;
+ }
+
+ mcp->in_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x1058, "Failed=%x.\n", rval);
+ } else {
+ if (name != NULL) {
+ /* This function returns name in big endian. */
+ name[0] = MSB(mcp->mb[2]);
+ name[1] = LSB(mcp->mb[2]);
+ name[2] = MSB(mcp->mb[3]);
+ name[3] = LSB(mcp->mb[3]);
+ name[4] = MSB(mcp->mb[6]);
+ name[5] = LSB(mcp->mb[6]);
+ name[6] = MSB(mcp->mb[7]);
+ name[7] = LSB(mcp->mb[7]);
+ }
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1059, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_lip_reset
+ * Issue LIP reset mailbox command.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_lip_reset(scsi_qla_host_t *vha)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x105a, "Entered %s.\n", __func__);
+
+ if (IS_CNA_CAPABLE(vha->hw)) {
+ /* Logout across all FCFs. */
+ mcp->mb[0] = MBC_LIP_FULL_LOGIN;
+ mcp->mb[1] = BIT_1;
+ mcp->mb[2] = 0;
+ mcp->out_mb = MBX_2|MBX_1|MBX_0;
+ } else if (IS_FWI2_CAPABLE(vha->hw)) {
+ mcp->mb[0] = MBC_LIP_FULL_LOGIN;
+ mcp->mb[1] = BIT_6;
+ mcp->mb[2] = 0;
+ mcp->mb[3] = vha->hw->loop_reset_delay;
+ mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+ } else {
+ mcp->mb[0] = MBC_LIP_RESET;
+ mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+ if (HAS_EXTENDED_IDS(vha->hw)) {
+ mcp->mb[1] = 0x00ff;
+ mcp->mb[10] = 0;
+ mcp->out_mb |= MBX_10;
+ } else {
+ mcp->mb[1] = 0xff00;
+ }
+ mcp->mb[2] = vha->hw->loop_reset_delay;
+ mcp->mb[3] = 0;
+ }
+ mcp->in_mb = MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x105b, "Failed=%x.\n", rval);
+ } else {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x105c, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_send_sns
+ * Send SNS command.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * sns = pointer for command.
+ * cmd_size = command size.
+ * buf_size = response/command size.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_send_sns(scsi_qla_host_t *vha, dma_addr_t sns_phys_address,
+ uint16_t cmd_size, size_t buf_size)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x105d, "Entered %s.\n", __func__);
+
+ ql_dbg(ql_dbg_mbx, vha, 0x105e,
+ "Retry cnt=%d ratov=%d total tov=%d.\n",
+ vha->hw->retry_count, vha->hw->login_timeout, mcp->tov);
+
+ mcp->mb[0] = MBC_SEND_SNS_COMMAND;
+ mcp->mb[1] = cmd_size;
+ mcp->mb[2] = MSW(sns_phys_address);
+ mcp->mb[3] = LSW(sns_phys_address);
+ mcp->mb[6] = MSW(MSD(sns_phys_address));
+ mcp->mb[7] = LSW(MSD(sns_phys_address));
+ mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0|MBX_1;
+ mcp->buf_size = buf_size;
+ mcp->flags = MBX_DMA_OUT|MBX_DMA_IN;
+ mcp->tov = (vha->hw->login_timeout * 2) + (vha->hw->login_timeout / 2);
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x105f,
+ "Failed=%x mb[0]=%x mb[1]=%x.\n",
+ rval, mcp->mb[0], mcp->mb[1]);
+ } else {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x1060, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
+ uint8_t area, uint8_t al_pa, uint16_t *mb, uint8_t opt)
+{
+ int rval;
+
+ struct logio_entry_24xx *lg;
+ dma_addr_t lg_dma;
+ uint32_t iop[2];
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req;
+ struct rsp_que *rsp;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1061, "Entered %s.\n", __func__);
+
+ if (ha->flags.cpu_affinity_enabled)
+ req = ha->req_q_map[0];
+ else
+ req = vha->req;
+ rsp = req->rsp;
+
+ lg = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
+ if (lg == NULL) {
+ ql_log(ql_log_warn, vha, 0x1062,
+ "Failed to allocate login IOCB.\n");
+ return QLA_MEMORY_ALLOC_FAILED;
+ }
+ memset(lg, 0, sizeof(struct logio_entry_24xx));
+
+ lg->entry_type = LOGINOUT_PORT_IOCB_TYPE;
+ lg->entry_count = 1;
+ lg->handle = MAKE_HANDLE(req->id, lg->handle);
+ lg->nport_handle = cpu_to_le16(loop_id);
+ lg->control_flags = __constant_cpu_to_le16(LCF_COMMAND_PLOGI);
+ if (opt & BIT_0)
+ lg->control_flags |= __constant_cpu_to_le16(LCF_COND_PLOGI);
+ if (opt & BIT_1)
+ lg->control_flags |= __constant_cpu_to_le16(LCF_SKIP_PRLI);
+ lg->port_id[0] = al_pa;
+ lg->port_id[1] = area;
+ lg->port_id[2] = domain;
+ lg->vp_index = vha->vp_idx;
+ rval = qla2x00_issue_iocb_timeout(vha, lg, lg_dma, 0,
+ (ha->r_a_tov / 10 * 2) + 2);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1063,
+ "Failed to issue login IOCB (%x).\n", rval);
+ } else if (lg->entry_status != 0) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1064,
+ "Failed to complete IOCB -- error status (%x).\n",
+ lg->entry_status);
+ rval = QLA_FUNCTION_FAILED;
+ } else if (lg->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) {
+ iop[0] = le32_to_cpu(lg->io_parameter[0]);
+ iop[1] = le32_to_cpu(lg->io_parameter[1]);
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1065,
+ "Failed to complete IOCB -- completion status (%x) "
+ "ioparam=%x/%x.\n", le16_to_cpu(lg->comp_status),
+ iop[0], iop[1]);
+
+ switch (iop[0]) {
+ case LSC_SCODE_PORTID_USED:
+ mb[0] = MBS_PORT_ID_USED;
+ mb[1] = LSW(iop[1]);
+ break;
+ case LSC_SCODE_NPORT_USED:
+ mb[0] = MBS_LOOP_ID_USED;
+ break;
+ case LSC_SCODE_NOLINK:
+ case LSC_SCODE_NOIOCB:
+ case LSC_SCODE_NOXCB:
+ case LSC_SCODE_CMD_FAILED:
+ case LSC_SCODE_NOFABRIC:
+ case LSC_SCODE_FW_NOT_READY:
+ case LSC_SCODE_NOT_LOGGED_IN:
+ case LSC_SCODE_NOPCB:
+ case LSC_SCODE_ELS_REJECT:
+ case LSC_SCODE_CMD_PARAM_ERR:
+ case LSC_SCODE_NONPORT:
+ case LSC_SCODE_LOGGED_IN:
+ case LSC_SCODE_NOFLOGI_ACC:
+ default:
+ mb[0] = MBS_COMMAND_ERROR;
+ break;
+ }
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x1066, "Done %s.\n", __func__);
+
+ iop[0] = le32_to_cpu(lg->io_parameter[0]);
+
+ mb[0] = MBS_COMMAND_COMPLETE;
+ mb[1] = 0;
+ if (iop[0] & BIT_4) {
+ if (iop[0] & BIT_8)
+ mb[1] |= BIT_1;
+ } else
+ mb[1] = BIT_0;
+
+ /* Passback COS information. */
+ mb[10] = 0;
+ if (lg->io_parameter[7] || lg->io_parameter[8])
+ mb[10] |= BIT_0; /* Class 2. */
+ if (lg->io_parameter[9] || lg->io_parameter[10])
+ mb[10] |= BIT_1; /* Class 3. */
+ }
+
+ dma_pool_free(ha->s_dma_pool, lg, lg_dma);
+
+ return rval;
+}
+
+/*
+ * qla2x00_login_fabric
+ * Issue login fabric port mailbox command.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * loop_id = device loop ID.
+ * domain = device domain.
+ * area = device area.
+ * al_pa = device AL_PA.
+ * status = pointer for return status.
+ * opt = command options.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
+ uint8_t area, uint8_t al_pa, uint16_t *mb, uint8_t opt)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1067, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_LOGIN_FABRIC_PORT;
+ mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+ if (HAS_EXTENDED_IDS(ha)) {
+ mcp->mb[1] = loop_id;
+ mcp->mb[10] = opt;
+ mcp->out_mb |= MBX_10;
+ } else {
+ mcp->mb[1] = (loop_id << 8) | opt;
+ }
+ mcp->mb[2] = domain;
+ mcp->mb[3] = area << 8 | al_pa;
+
+ mcp->in_mb = MBX_7|MBX_6|MBX_2|MBX_1|MBX_0;
+ mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2);
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ /* Return mailbox statuses. */
+ if (mb != NULL) {
+ mb[0] = mcp->mb[0];
+ mb[1] = mcp->mb[1];
+ mb[2] = mcp->mb[2];
+ mb[6] = mcp->mb[6];
+ mb[7] = mcp->mb[7];
+ /* COS retrieved from Get-Port-Database mailbox command. */
+ mb[10] = 0;
+ }
+
+ if (rval != QLA_SUCCESS) {
+ /* RLU tmp code: need to change main mailbox_command function to
+ * return ok even when the mailbox completion value is not
+ * SUCCESS. The caller needs to be responsible to interpret
+ * the return values of this mailbox command if we're not
+ * to change too much of the existing code.
+ */
+ if (mcp->mb[0] == 0x4001 || mcp->mb[0] == 0x4002 ||
+ mcp->mb[0] == 0x4003 || mcp->mb[0] == 0x4005 ||
+ mcp->mb[0] == 0x4006)
+ rval = QLA_SUCCESS;
+
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x1068,
+ "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x.\n",
+ rval, mcp->mb[0], mcp->mb[1], mcp->mb[2]);
+ } else {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x1069, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_login_local_device
+ * Issue login loop port mailbox command.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * loop_id = device loop ID.
+ * opt = command options.
+ *
+ * Returns:
+ * Return status code.
+ *
+ * Context:
+ * Kernel context.
+ *
+ */
+int
+qla2x00_login_local_device(scsi_qla_host_t *vha, fc_port_t *fcport,
+ uint16_t *mb_ret, uint8_t opt)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x106a, "Entered %s.\n", __func__);
+
+ if (IS_FWI2_CAPABLE(ha))
+ return qla24xx_login_fabric(vha, fcport->loop_id,
+ fcport->d_id.b.domain, fcport->d_id.b.area,
+ fcport->d_id.b.al_pa, mb_ret, opt);
+
+ mcp->mb[0] = MBC_LOGIN_LOOP_PORT;
+ if (HAS_EXTENDED_IDS(ha))
+ mcp->mb[1] = fcport->loop_id;
+ else
+ mcp->mb[1] = fcport->loop_id << 8;
+ mcp->mb[2] = opt;
+ mcp->out_mb = MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_7|MBX_6|MBX_1|MBX_0;
+ mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2);
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ /* Return mailbox statuses. */
+ if (mb_ret != NULL) {
+ mb_ret[0] = mcp->mb[0];
+ mb_ret[1] = mcp->mb[1];
+ mb_ret[6] = mcp->mb[6];
+ mb_ret[7] = mcp->mb[7];
+ }
+
+ if (rval != QLA_SUCCESS) {
+ /* AV tmp code: need to change main mailbox_command function to
+ * return ok even when the mailbox completion value is not
+ * SUCCESS. The caller needs to be responsible to interpret
+ * the return values of this mailbox command if we're not
+ * to change too much of the existing code.
+ */
+ if (mcp->mb[0] == 0x4005 || mcp->mb[0] == 0x4006)
+ rval = QLA_SUCCESS;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x106b,
+ "Failed=%x mb[0]=%x mb[1]=%x mb[6]=%x mb[7]=%x.\n",
+ rval, mcp->mb[0], mcp->mb[1], mcp->mb[6], mcp->mb[7]);
+ } else {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x106c, "Done %s.\n", __func__);
+ }
+
+ return (rval);
+}
+
+int
+qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
+ uint8_t area, uint8_t al_pa)
+{
+ int rval;
+ struct logio_entry_24xx *lg;
+ dma_addr_t lg_dma;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req;
+ struct rsp_que *rsp;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x106d, "Entered %s.\n", __func__);
+
+ lg = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma);
+ if (lg == NULL) {
+ ql_log(ql_log_warn, vha, 0x106e,
+ "Failed to allocate logout IOCB.\n");
+ return QLA_MEMORY_ALLOC_FAILED;
+ }
+ memset(lg, 0, sizeof(struct logio_entry_24xx));
+
+ if (ql2xmaxqueues > 1)
+ req = ha->req_q_map[0];
+ else
+ req = vha->req;
+ rsp = req->rsp;
+ lg->entry_type = LOGINOUT_PORT_IOCB_TYPE;
+ lg->entry_count = 1;
+ lg->handle = MAKE_HANDLE(req->id, lg->handle);
+ lg->nport_handle = cpu_to_le16(loop_id);
+ lg->control_flags =
+ __constant_cpu_to_le16(LCF_COMMAND_LOGO|LCF_IMPL_LOGO|
+ LCF_FREE_NPORT);
+ lg->port_id[0] = al_pa;
+ lg->port_id[1] = area;
+ lg->port_id[2] = domain;
+ lg->vp_index = vha->vp_idx;
+ rval = qla2x00_issue_iocb_timeout(vha, lg, lg_dma, 0,
+ (ha->r_a_tov / 10 * 2) + 2);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x106f,
+ "Failed to issue logout IOCB (%x).\n", rval);
+ } else if (lg->entry_status != 0) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1070,
+ "Failed to complete IOCB -- error status (%x).\n",
+ lg->entry_status);
+ rval = QLA_FUNCTION_FAILED;
+ } else if (lg->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1071,
+ "Failed to complete IOCB -- completion status (%x) "
+ "ioparam=%x/%x.\n", le16_to_cpu(lg->comp_status),
+ le32_to_cpu(lg->io_parameter[0]),
+ le32_to_cpu(lg->io_parameter[1]));
+ } else {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x1072, "Done %s.\n", __func__);
+ }
+
+ dma_pool_free(ha->s_dma_pool, lg, lg_dma);
+
+ return rval;
+}
+
+/*
+ * qla2x00_fabric_logout
+ * Issue logout fabric port mailbox command.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * loop_id = device loop ID.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain,
+ uint8_t area, uint8_t al_pa)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1073, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_LOGOUT_FABRIC_PORT;
+ mcp->out_mb = MBX_1|MBX_0;
+ if (HAS_EXTENDED_IDS(vha->hw)) {
+ mcp->mb[1] = loop_id;
+ mcp->mb[10] = 0;
+ mcp->out_mb |= MBX_10;
+ } else {
+ mcp->mb[1] = loop_id << 8;
+ }
+
+ mcp->in_mb = MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x1074,
+ "Failed=%x mb[1]=%x.\n", rval, mcp->mb[1]);
+ } else {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x1075, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_full_login_lip
+ * Issue full login LIP mailbox command.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * TARGET_QUEUE_LOCK must be released.
+ * ADAPTER_STATE_LOCK must be released.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_full_login_lip(scsi_qla_host_t *vha)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1076, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_LIP_FULL_LOGIN;
+ mcp->mb[1] = IS_FWI2_CAPABLE(vha->hw) ? BIT_3 : 0;
+ mcp->mb[2] = 0;
+ mcp->mb[3] = 0;
+ mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x1077, "Failed=%x.\n", rval);
+ } else {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x1078, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_get_id_list
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_get_id_list(scsi_qla_host_t *vha, void *id_list, dma_addr_t id_list_dma,
+ uint16_t *entries)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1079, "Entered %s.\n", __func__);
+
+ if (id_list == NULL)
+ return QLA_FUNCTION_FAILED;
+
+ mcp->mb[0] = MBC_GET_ID_LIST;
+ mcp->out_mb = MBX_0;
+ if (IS_FWI2_CAPABLE(vha->hw)) {
+ mcp->mb[2] = MSW(id_list_dma);
+ mcp->mb[3] = LSW(id_list_dma);
+ mcp->mb[6] = MSW(MSD(id_list_dma));
+ mcp->mb[7] = LSW(MSD(id_list_dma));
+ mcp->mb[8] = 0;
+ mcp->mb[9] = vha->vp_idx;
+ mcp->out_mb |= MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2;
+ } else {
+ mcp->mb[1] = MSW(id_list_dma);
+ mcp->mb[2] = LSW(id_list_dma);
+ mcp->mb[3] = MSW(MSD(id_list_dma));
+ mcp->mb[6] = LSW(MSD(id_list_dma));
+ mcp->out_mb |= MBX_6|MBX_3|MBX_2|MBX_1;
+ }
+ mcp->in_mb = MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x107a, "Failed=%x.\n", rval);
+ } else {
+ *entries = mcp->mb[1];
+ ql_dbg(ql_dbg_mbx, vha, 0x107b, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_get_resource_cnts
+ * Get current firmware resource counts.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt,
+ uint16_t *orig_xchg_cnt, uint16_t *cur_iocb_cnt,
+ uint16_t *orig_iocb_cnt, uint16_t *max_npiv_vports, uint16_t *max_fcfs)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x107c, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_GET_RESOURCE_COUNTS;
+ mcp->out_mb = MBX_0;
+ mcp->in_mb = MBX_11|MBX_10|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ if (IS_QLA81XX(vha->hw) || IS_QLA83XX(vha->hw))
+ mcp->in_mb |= MBX_12;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x107d,
+ "Failed mb[0]=%x.\n", mcp->mb[0]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x107e,
+ "Done %s mb1=%x mb2=%x mb3=%x mb6=%x mb7=%x mb10=%x "
+ "mb11=%x mb12=%x.\n", __func__, mcp->mb[1], mcp->mb[2],
+ mcp->mb[3], mcp->mb[6], mcp->mb[7], mcp->mb[10],
+ mcp->mb[11], mcp->mb[12]);
+
+ if (cur_xchg_cnt)
+ *cur_xchg_cnt = mcp->mb[3];
+ if (orig_xchg_cnt)
+ *orig_xchg_cnt = mcp->mb[6];
+ if (cur_iocb_cnt)
+ *cur_iocb_cnt = mcp->mb[7];
+ if (orig_iocb_cnt)
+ *orig_iocb_cnt = mcp->mb[10];
+ if (vha->hw->flags.npiv_supported && max_npiv_vports)
+ *max_npiv_vports = mcp->mb[11];
+ if ((IS_QLA81XX(vha->hw) || IS_QLA83XX(vha->hw)) && max_fcfs)
+ *max_fcfs = mcp->mb[12];
+ }
+
+ return (rval);
+}
+
+/*
+ * qla2x00_get_fcal_position_map
+ * Get FCAL (LILP) position map using mailbox command
+ *
+ * Input:
+ * ha = adapter state pointer.
+ * pos_map = buffer pointer (can be NULL).
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla2x00_get_fcal_position_map(scsi_qla_host_t *vha, char *pos_map)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ char *pmap;
+ dma_addr_t pmap_dma;
+ struct qla_hw_data *ha = vha->hw;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x107f, "Entered %s.\n", __func__);
+
+ pmap = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &pmap_dma);
+ if (pmap == NULL) {
+ ql_log(ql_log_warn, vha, 0x1080,
+ "Memory alloc failed.\n");
+ return QLA_MEMORY_ALLOC_FAILED;
+ }
+ memset(pmap, 0, FCAL_MAP_SIZE);
+
+ mcp->mb[0] = MBC_GET_FC_AL_POSITION_MAP;
+ mcp->mb[2] = MSW(pmap_dma);
+ mcp->mb[3] = LSW(pmap_dma);
+ mcp->mb[6] = MSW(MSD(pmap_dma));
+ mcp->mb[7] = LSW(MSD(pmap_dma));
+ mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_0;
+ mcp->in_mb = MBX_1|MBX_0;
+ mcp->buf_size = FCAL_MAP_SIZE;
+ mcp->flags = MBX_DMA_IN;
+ mcp->tov = (ha->login_timeout * 2) + (ha->login_timeout / 2);
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval == QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1081,
+ "mb0/mb1=%x/%X FC/AL position map size (%x).\n",
+ mcp->mb[0], mcp->mb[1], (unsigned)pmap[0]);
+ ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x111d,
+ pmap, pmap[0] + 1);
+
+ if (pos_map)
+ memcpy(pos_map, pmap, FCAL_MAP_SIZE);
+ }
+ dma_pool_free(ha->s_dma_pool, pmap, pmap_dma);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1082, "Failed=%x.\n", rval);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x1083, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+/*
+ * qla2x00_get_link_status
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * loop_id = device loop ID.
+ * ret_buf = pointer to link status return buffer.
+ *
+ * Returns:
+ * 0 = success.
+ * BIT_0 = mem alloc error.
+ * BIT_1 = mailbox error.
+ */
+int
+qla2x00_get_link_status(scsi_qla_host_t *vha, uint16_t loop_id,
+ struct link_statistics *stats, dma_addr_t stats_dma)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ uint32_t *siter, *diter, dwords;
+ struct qla_hw_data *ha = vha->hw;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1084, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_GET_LINK_STATUS;
+ mcp->mb[2] = MSW(stats_dma);
+ mcp->mb[3] = LSW(stats_dma);
+ mcp->mb[6] = MSW(MSD(stats_dma));
+ mcp->mb[7] = LSW(MSD(stats_dma));
+ mcp->out_mb = MBX_7|MBX_6|MBX_3|MBX_2|MBX_0;
+ mcp->in_mb = MBX_0;
+ if (IS_FWI2_CAPABLE(ha)) {
+ mcp->mb[1] = loop_id;
+ mcp->mb[4] = 0;
+ mcp->mb[10] = 0;
+ mcp->out_mb |= MBX_10|MBX_4|MBX_1;
+ mcp->in_mb |= MBX_1;
+ } else if (HAS_EXTENDED_IDS(ha)) {
+ mcp->mb[1] = loop_id;
+ mcp->mb[10] = 0;
+ mcp->out_mb |= MBX_10|MBX_1;
+ } else {
+ mcp->mb[1] = loop_id << 8;
+ mcp->out_mb |= MBX_1;
+ }
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = IOCTL_CMD;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval == QLA_SUCCESS) {
+ if (mcp->mb[0] != MBS_COMMAND_COMPLETE) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1085,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ /* Copy over data -- firmware data is LE. */
+ ql_dbg(ql_dbg_mbx, vha, 0x1086, "Done %s.\n", __func__);
+ dwords = offsetof(struct link_statistics, unused1) / 4;
+ siter = diter = &stats->link_fail_cnt;
+ while (dwords--)
+ *diter++ = le32_to_cpu(*siter++);
+ }
+ } else {
+ /* Failed. */
+ ql_dbg(ql_dbg_mbx, vha, 0x1087, "Failed=%x.\n", rval);
+ }
+
+ return rval;
+}
+
+int
+qla24xx_get_isp_stats(scsi_qla_host_t *vha, struct link_statistics *stats,
+ dma_addr_t stats_dma)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ uint32_t *siter, *diter, dwords;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1088, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_GET_LINK_PRIV_STATS;
+ mcp->mb[2] = MSW(stats_dma);
+ mcp->mb[3] = LSW(stats_dma);
+ mcp->mb[6] = MSW(MSD(stats_dma));
+ mcp->mb[7] = LSW(MSD(stats_dma));
+ mcp->mb[8] = sizeof(struct link_statistics) / 4;
+ mcp->mb[9] = vha->vp_idx;
+ mcp->mb[10] = 0;
+ mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_0;
+ mcp->in_mb = MBX_2|MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = IOCTL_CMD;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval == QLA_SUCCESS) {
+ if (mcp->mb[0] != MBS_COMMAND_COMPLETE) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1089,
+ "Failed mb[0]=%x.\n", mcp->mb[0]);
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x108a, "Done %s.\n", __func__);
+ /* Copy over data -- firmware data is LE. */
+ dwords = sizeof(struct link_statistics) / 4;
+ siter = diter = &stats->link_fail_cnt;
+ while (dwords--)
+ *diter++ = le32_to_cpu(*siter++);
+ }
+ } else {
+ /* Failed. */
+ ql_dbg(ql_dbg_mbx, vha, 0x108b, "Failed=%x.\n", rval);
+ }
+
+ return rval;
+}
+
+int
+qla24xx_abort_command(srb_t *sp)
+{
+ int rval;
+ unsigned long flags = 0;
+
+ struct abort_entry_24xx *abt;
+ dma_addr_t abt_dma;
+ uint32_t handle;
+ fc_port_t *fcport = sp->fcport;
+ struct scsi_qla_host *vha = fcport->vha;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = vha->req;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x108c, "Entered %s.\n", __func__);
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) {
+ if (req->outstanding_cmds[handle] == sp)
+ break;
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ if (handle == MAX_OUTSTANDING_COMMANDS) {
+ /* Command not found. */
+ return QLA_FUNCTION_FAILED;
+ }
+
+ abt = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &abt_dma);
+ if (abt == NULL) {
+ ql_log(ql_log_warn, vha, 0x108d,
+ "Failed to allocate abort IOCB.\n");
+ return QLA_MEMORY_ALLOC_FAILED;
+ }
+ memset(abt, 0, sizeof(struct abort_entry_24xx));
+
+ abt->entry_type = ABORT_IOCB_TYPE;
+ abt->entry_count = 1;
+ abt->handle = MAKE_HANDLE(req->id, abt->handle);
+ abt->nport_handle = cpu_to_le16(fcport->loop_id);
+ abt->handle_to_abort = MAKE_HANDLE(req->id, handle);
+ abt->port_id[0] = fcport->d_id.b.al_pa;
+ abt->port_id[1] = fcport->d_id.b.area;
+ abt->port_id[2] = fcport->d_id.b.domain;
+ abt->vp_index = fcport->vp_idx;
+
+ abt->req_que_no = cpu_to_le16(req->id);
+
+ rval = qla2x00_issue_iocb(vha, abt, abt_dma, 0);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x108e,
+ "Failed to issue IOCB (%x).\n", rval);
+ } else if (abt->entry_status != 0) {
+ ql_dbg(ql_dbg_mbx, vha, 0x108f,
+ "Failed to complete IOCB -- error status (%x).\n",
+ abt->entry_status);
+ rval = QLA_FUNCTION_FAILED;
+ } else if (abt->nport_handle != __constant_cpu_to_le16(0)) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1090,
+ "Failed to complete IOCB -- completion status (%x).\n",
+ le16_to_cpu(abt->nport_handle));
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x1091, "Done %s.\n", __func__);
+ }
+
+ dma_pool_free(ha->s_dma_pool, abt, abt_dma);
+
+ return rval;
+}
+
+struct tsk_mgmt_cmd {
+ union {
+ struct tsk_mgmt_entry tsk;
+ struct sts_entry_24xx sts;
+ } p;
+};
+
+static int
+__qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport,
+ unsigned int l, int tag)
+{
+ int rval, rval2;
+ struct tsk_mgmt_cmd *tsk;
+ struct sts_entry_24xx *sts;
+ dma_addr_t tsk_dma;
+ scsi_qla_host_t *vha;
+ struct qla_hw_data *ha;
+ struct req_que *req;
+ struct rsp_que *rsp;
+
+ vha = fcport->vha;
+ ha = vha->hw;
+ req = vha->req;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1092, "Entered %s.\n", __func__);
+
+ if (ha->flags.cpu_affinity_enabled)
+ rsp = ha->rsp_q_map[tag + 1];
+ else
+ rsp = req->rsp;
+ tsk = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &tsk_dma);
+ if (tsk == NULL) {
+ ql_log(ql_log_warn, vha, 0x1093,
+ "Failed to allocate task management IOCB.\n");
+ return QLA_MEMORY_ALLOC_FAILED;
+ }
+ memset(tsk, 0, sizeof(struct tsk_mgmt_cmd));
+
+ tsk->p.tsk.entry_type = TSK_MGMT_IOCB_TYPE;
+ tsk->p.tsk.entry_count = 1;
+ tsk->p.tsk.handle = MAKE_HANDLE(req->id, tsk->p.tsk.handle);
+ tsk->p.tsk.nport_handle = cpu_to_le16(fcport->loop_id);
+ tsk->p.tsk.timeout = cpu_to_le16(ha->r_a_tov / 10 * 2);
+ tsk->p.tsk.control_flags = cpu_to_le32(type);
+ tsk->p.tsk.port_id[0] = fcport->d_id.b.al_pa;
+ tsk->p.tsk.port_id[1] = fcport->d_id.b.area;
+ tsk->p.tsk.port_id[2] = fcport->d_id.b.domain;
+ tsk->p.tsk.vp_index = fcport->vp_idx;
+ if (type == TCF_LUN_RESET) {
+ int_to_scsilun(l, &tsk->p.tsk.lun);
+ host_to_fcp_swap((uint8_t *)&tsk->p.tsk.lun,
+ sizeof(tsk->p.tsk.lun));
+ }
+
+ sts = &tsk->p.sts;
+ rval = qla2x00_issue_iocb(vha, tsk, tsk_dma, 0);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1094,
+ "Failed to issue %s reset IOCB (%x).\n", name, rval);
+ } else if (sts->entry_status != 0) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1095,
+ "Failed to complete IOCB -- error status (%x).\n",
+ sts->entry_status);
+ rval = QLA_FUNCTION_FAILED;
+ } else if (sts->comp_status !=
+ __constant_cpu_to_le16(CS_COMPLETE)) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1096,
+ "Failed to complete IOCB -- completion status (%x).\n",
+ le16_to_cpu(sts->comp_status));
+ rval = QLA_FUNCTION_FAILED;
+ } else if (le16_to_cpu(sts->scsi_status) &
+ SS_RESPONSE_INFO_LEN_VALID) {
+ if (le32_to_cpu(sts->rsp_data_len) < 4) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1097,
+ "Ignoring inconsistent data length -- not enough "
+ "response info (%d).\n",
+ le32_to_cpu(sts->rsp_data_len));
+ } else if (sts->data[3]) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1098,
+ "Failed to complete IOCB -- response (%x).\n",
+ sts->data[3]);
+ rval = QLA_FUNCTION_FAILED;
+ }
+ }
+
+ /* Issue marker IOCB. */
+ rval2 = qla2x00_marker(vha, req, rsp, fcport->loop_id, l,
+ type == TCF_LUN_RESET ? MK_SYNC_ID_LUN: MK_SYNC_ID);
+ if (rval2 != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1099,
+ "Failed to issue marker IOCB (%x).\n", rval2);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x109a, "Done %s.\n", __func__);
+ }
+
+ dma_pool_free(ha->s_dma_pool, tsk, tsk_dma);
+
+ return rval;
+}
+
+int
+qla24xx_abort_target(struct fc_port *fcport, unsigned int l, int tag)
+{
+ struct qla_hw_data *ha = fcport->vha->hw;
+
+ if ((ql2xasynctmfenable) && IS_FWI2_CAPABLE(ha))
+ return qla2x00_async_tm_cmd(fcport, TCF_TARGET_RESET, l, tag);
+
+ return __qla24xx_issue_tmf("Target", TCF_TARGET_RESET, fcport, l, tag);
+}
+
+int
+qla24xx_lun_reset(struct fc_port *fcport, unsigned int l, int tag)
+{
+ struct qla_hw_data *ha = fcport->vha->hw;
+
+ if ((ql2xasynctmfenable) && IS_FWI2_CAPABLE(ha))
+ return qla2x00_async_tm_cmd(fcport, TCF_LUN_RESET, l, tag);
+
+ return __qla24xx_issue_tmf("Lun", TCF_LUN_RESET, fcport, l, tag);
+}
+
+int
+qla2x00_system_error(scsi_qla_host_t *vha)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!IS_QLA23XX(ha) && !IS_FWI2_CAPABLE(ha))
+ return QLA_FUNCTION_FAILED;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x109b, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_GEN_SYSTEM_ERROR;
+ mcp->out_mb = MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->tov = 5;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x109c, "Failed=%x.\n", rval);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x109d, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+/**
+ * qla2x00_set_serdes_params() -
+ * @ha: HA context
+ *
+ * Returns
+ */
+int
+qla2x00_set_serdes_params(scsi_qla_host_t *vha, uint16_t sw_em_1g,
+ uint16_t sw_em_2g, uint16_t sw_em_4g)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x109e, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_SERDES_PARAMS;
+ mcp->mb[1] = BIT_0;
+ mcp->mb[2] = sw_em_1g | BIT_15;
+ mcp->mb[3] = sw_em_2g | BIT_15;
+ mcp->mb[4] = sw_em_4g | BIT_15;
+ mcp->out_mb = MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x109f,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ /*EMPTY*/
+ ql_dbg(ql_dbg_mbx, vha, 0x10a0, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla2x00_stop_firmware(scsi_qla_host_t *vha)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ if (!IS_FWI2_CAPABLE(vha->hw))
+ return QLA_FUNCTION_FAILED;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10a1, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_STOP_FIRMWARE;
+ mcp->mb[1] = 0;
+ mcp->out_mb = MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->tov = 5;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10a2, "Failed=%x.\n", rval);
+ if (mcp->mb[0] == MBS_INVALID_COMMAND)
+ rval = QLA_INVALID_COMMAND;
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10a3, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla2x00_enable_eft_trace(scsi_qla_host_t *vha, dma_addr_t eft_dma,
+ uint16_t buffers)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10a4, "Entered %s.\n", __func__);
+
+ if (!IS_FWI2_CAPABLE(vha->hw))
+ return QLA_FUNCTION_FAILED;
+
+ if (unlikely(pci_channel_offline(vha->hw->pdev)))
+ return QLA_FUNCTION_FAILED;
+
+ mcp->mb[0] = MBC_TRACE_CONTROL;
+ mcp->mb[1] = TC_EFT_ENABLE;
+ mcp->mb[2] = LSW(eft_dma);
+ mcp->mb[3] = MSW(eft_dma);
+ mcp->mb[4] = LSW(MSD(eft_dma));
+ mcp->mb[5] = MSW(MSD(eft_dma));
+ mcp->mb[6] = buffers;
+ mcp->mb[7] = TC_AEN_DISABLE;
+ mcp->out_mb = MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10a5,
+ "Failed=%x mb[0]=%x mb[1]=%x.\n",
+ rval, mcp->mb[0], mcp->mb[1]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10a6, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla2x00_disable_eft_trace(scsi_qla_host_t *vha)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10a7, "Entered %s.\n", __func__);
+
+ if (!IS_FWI2_CAPABLE(vha->hw))
+ return QLA_FUNCTION_FAILED;
+
+ if (unlikely(pci_channel_offline(vha->hw->pdev)))
+ return QLA_FUNCTION_FAILED;
+
+ mcp->mb[0] = MBC_TRACE_CONTROL;
+ mcp->mb[1] = TC_EFT_DISABLE;
+ mcp->out_mb = MBX_1|MBX_0;
+ mcp->in_mb = MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10a8,
+ "Failed=%x mb[0]=%x mb[1]=%x.\n",
+ rval, mcp->mb[0], mcp->mb[1]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10a9, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla2x00_enable_fce_trace(scsi_qla_host_t *vha, dma_addr_t fce_dma,
+ uint16_t buffers, uint16_t *mb, uint32_t *dwords)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10aa, "Entered %s.\n", __func__);
+
+ if (!IS_QLA25XX(vha->hw) && !IS_QLA81XX(vha->hw) &&
+ !IS_QLA83XX(vha->hw))
+ return QLA_FUNCTION_FAILED;
+
+ if (unlikely(pci_channel_offline(vha->hw->pdev)))
+ return QLA_FUNCTION_FAILED;
+
+ mcp->mb[0] = MBC_TRACE_CONTROL;
+ mcp->mb[1] = TC_FCE_ENABLE;
+ mcp->mb[2] = LSW(fce_dma);
+ mcp->mb[3] = MSW(fce_dma);
+ mcp->mb[4] = LSW(MSD(fce_dma));
+ mcp->mb[5] = MSW(MSD(fce_dma));
+ mcp->mb[6] = buffers;
+ mcp->mb[7] = TC_AEN_DISABLE;
+ mcp->mb[8] = 0;
+ mcp->mb[9] = TC_FCE_DEFAULT_RX_SIZE;
+ mcp->mb[10] = TC_FCE_DEFAULT_TX_SIZE;
+ mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|
+ MBX_1|MBX_0;
+ mcp->in_mb = MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10ab,
+ "Failed=%x mb[0]=%x mb[1]=%x.\n",
+ rval, mcp->mb[0], mcp->mb[1]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10ac, "Done %s.\n", __func__);
+
+ if (mb)
+ memcpy(mb, mcp->mb, 8 * sizeof(*mb));
+ if (dwords)
+ *dwords = buffers;
+ }
+
+ return rval;
+}
+
+int
+qla2x00_disable_fce_trace(scsi_qla_host_t *vha, uint64_t *wr, uint64_t *rd)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10ad, "Entered %s.\n", __func__);
+
+ if (!IS_FWI2_CAPABLE(vha->hw))
+ return QLA_FUNCTION_FAILED;
+
+ if (unlikely(pci_channel_offline(vha->hw->pdev)))
+ return QLA_FUNCTION_FAILED;
+
+ mcp->mb[0] = MBC_TRACE_CONTROL;
+ mcp->mb[1] = TC_FCE_DISABLE;
+ mcp->mb[2] = TC_FCE_DISABLE_TRACE;
+ mcp->out_mb = MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_9|MBX_8|MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|
+ MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10ae,
+ "Failed=%x mb[0]=%x mb[1]=%x.\n",
+ rval, mcp->mb[0], mcp->mb[1]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10af, "Done %s.\n", __func__);
+
+ if (wr)
+ *wr = (uint64_t) mcp->mb[5] << 48 |
+ (uint64_t) mcp->mb[4] << 32 |
+ (uint64_t) mcp->mb[3] << 16 |
+ (uint64_t) mcp->mb[2];
+ if (rd)
+ *rd = (uint64_t) mcp->mb[9] << 48 |
+ (uint64_t) mcp->mb[8] << 32 |
+ (uint64_t) mcp->mb[7] << 16 |
+ (uint64_t) mcp->mb[6];
+ }
+
+ return rval;
+}
+
+int
+qla2x00_get_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id,
+ uint16_t *port_speed, uint16_t *mb)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10b0, "Entered %s.\n", __func__);
+
+ if (!IS_IIDMA_CAPABLE(vha->hw))
+ return QLA_FUNCTION_FAILED;
+
+ mcp->mb[0] = MBC_PORT_PARAMS;
+ mcp->mb[1] = loop_id;
+ mcp->mb[2] = mcp->mb[3] = 0;
+ mcp->mb[9] = vha->vp_idx;
+ mcp->out_mb = MBX_9|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_3|MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ /* Return mailbox statuses. */
+ if (mb != NULL) {
+ mb[0] = mcp->mb[0];
+ mb[1] = mcp->mb[1];
+ mb[3] = mcp->mb[3];
+ }
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10b1, "Failed=%x.\n", rval);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10b2, "Done %s.\n", __func__);
+ if (port_speed)
+ *port_speed = mcp->mb[3];
+ }
+
+ return rval;
+}
+
+int
+qla2x00_set_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id,
+ uint16_t port_speed, uint16_t *mb)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10b3, "Entered %s.\n", __func__);
+
+ if (!IS_IIDMA_CAPABLE(vha->hw))
+ return QLA_FUNCTION_FAILED;
+
+ mcp->mb[0] = MBC_PORT_PARAMS;
+ mcp->mb[1] = loop_id;
+ mcp->mb[2] = BIT_0;
+ if (IS_CNA_CAPABLE(vha->hw))
+ mcp->mb[3] = port_speed & (BIT_5|BIT_4|BIT_3|BIT_2|BIT_1|BIT_0);
+ else
+ mcp->mb[3] = port_speed & (BIT_2|BIT_1|BIT_0);
+ mcp->mb[9] = vha->vp_idx;
+ mcp->out_mb = MBX_9|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_3|MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ /* Return mailbox statuses. */
+ if (mb != NULL) {
+ mb[0] = mcp->mb[0];
+ mb[1] = mcp->mb[1];
+ mb[3] = mcp->mb[3];
+ }
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10b4, "Failed=%x.\n", rval);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10b5, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+void
+qla24xx_report_id_acquisition(scsi_qla_host_t *vha,
+ struct vp_rpt_id_entry_24xx *rptid_entry)
+{
+ uint8_t vp_idx;
+ uint16_t stat = le16_to_cpu(rptid_entry->vp_idx);
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *vp;
+ unsigned long flags;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10b6, "Entered %s.\n", __func__);
+
+ if (rptid_entry->entry_status != 0)
+ return;
+
+ if (rptid_entry->format == 0) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10b7,
+ "Format 0 : Number of VPs setup %d, number of "
+ "VPs acquired %d.\n",
+ MSB(le16_to_cpu(rptid_entry->vp_count)),
+ LSB(le16_to_cpu(rptid_entry->vp_count)));
+ ql_dbg(ql_dbg_mbx, vha, 0x10b8,
+ "Primary port id %02x%02x%02x.\n",
+ rptid_entry->port_id[2], rptid_entry->port_id[1],
+ rptid_entry->port_id[0]);
+ } else if (rptid_entry->format == 1) {
+ vp_idx = LSB(stat);
+ ql_dbg(ql_dbg_mbx, vha, 0x10b9,
+ "Format 1: VP[%d] enabled - status %d - with "
+ "port id %02x%02x%02x.\n", vp_idx, MSB(stat),
+ rptid_entry->port_id[2], rptid_entry->port_id[1],
+ rptid_entry->port_id[0]);
+
+ vp = vha;
+ if (vp_idx == 0 && (MSB(stat) != 1))
+ goto reg_needed;
+
+ if (MSB(stat) != 0) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10ba,
+ "Could not acquire ID for VP[%d].\n", vp_idx);
+ return;
+ }
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ list_for_each_entry(vp, &ha->vp_list, list)
+ if (vp_idx == vp->vp_idx)
+ break;
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
+ if (!vp)
+ return;
+
+ vp->d_id.b.domain = rptid_entry->port_id[2];
+ vp->d_id.b.area = rptid_entry->port_id[1];
+ vp->d_id.b.al_pa = rptid_entry->port_id[0];
+
+ /*
+ * Cannot configure here as we are still sitting on the
+ * response queue. Handle it in dpc context.
+ */
+ set_bit(VP_IDX_ACQUIRED, &vp->vp_flags);
+
+reg_needed:
+ set_bit(REGISTER_FC4_NEEDED, &vp->dpc_flags);
+ set_bit(REGISTER_FDMI_NEEDED, &vp->dpc_flags);
+ set_bit(VP_DPC_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
+ }
+}
+
+/*
+ * qla24xx_modify_vp_config
+ * Change VP configuration for vha
+ *
+ * Input:
+ * vha = adapter block pointer.
+ *
+ * Returns:
+ * qla2xxx local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla24xx_modify_vp_config(scsi_qla_host_t *vha)
+{
+ int rval;
+ struct vp_config_entry_24xx *vpmod;
+ dma_addr_t vpmod_dma;
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+
+ /* This can be called by the parent */
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10bb, "Entered %s.\n", __func__);
+
+ vpmod = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &vpmod_dma);
+ if (!vpmod) {
+ ql_log(ql_log_warn, vha, 0x10bc,
+ "Failed to allocate modify VP IOCB.\n");
+ return QLA_MEMORY_ALLOC_FAILED;
+ }
+
+ memset(vpmod, 0, sizeof(struct vp_config_entry_24xx));
+ vpmod->entry_type = VP_CONFIG_IOCB_TYPE;
+ vpmod->entry_count = 1;
+ vpmod->command = VCT_COMMAND_MOD_ENABLE_VPS;
+ vpmod->vp_count = 1;
+ vpmod->vp_index1 = vha->vp_idx;
+ vpmod->options_idx1 = BIT_3|BIT_4|BIT_5;
+ memcpy(vpmod->node_name_idx1, vha->node_name, WWN_SIZE);
+ memcpy(vpmod->port_name_idx1, vha->port_name, WWN_SIZE);
+ vpmod->entry_count = 1;
+
+ rval = qla2x00_issue_iocb(base_vha, vpmod, vpmod_dma, 0);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10bd,
+ "Failed to issue VP config IOCB (%x).\n", rval);
+ } else if (vpmod->comp_status != 0) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10be,
+ "Failed to complete IOCB -- error status (%x).\n",
+ vpmod->comp_status);
+ rval = QLA_FUNCTION_FAILED;
+ } else if (vpmod->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10bf,
+ "Failed to complete IOCB -- completion status (%x).\n",
+ le16_to_cpu(vpmod->comp_status));
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ /* EMPTY */
+ ql_dbg(ql_dbg_mbx, vha, 0x10c0, "Done %s.\n", __func__);
+ fc_vport_set_state(vha->fc_vport, FC_VPORT_INITIALIZING);
+ }
+ dma_pool_free(ha->s_dma_pool, vpmod, vpmod_dma);
+
+ return rval;
+}
+
+/*
+ * qla24xx_control_vp
+ * Enable a virtual port for given host
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * vhba = virtual adapter (unused)
+ * index = index number for enabled VP
+ *
+ * Returns:
+ * qla2xxx local function return status code.
+ *
+ * Context:
+ * Kernel context.
+ */
+int
+qla24xx_control_vp(scsi_qla_host_t *vha, int cmd)
+{
+ int rval;
+ int map, pos;
+ struct vp_ctrl_entry_24xx *vce;
+ dma_addr_t vce_dma;
+ struct qla_hw_data *ha = vha->hw;
+ int vp_index = vha->vp_idx;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10c1,
+ "Entered %s enabling index %d.\n", __func__, vp_index);
+
+ if (vp_index == 0 || vp_index >= ha->max_npiv_vports)
+ return QLA_PARAMETER_ERROR;
+
+ vce = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &vce_dma);
+ if (!vce) {
+ ql_log(ql_log_warn, vha, 0x10c2,
+ "Failed to allocate VP control IOCB.\n");
+ return QLA_MEMORY_ALLOC_FAILED;
+ }
+ memset(vce, 0, sizeof(struct vp_ctrl_entry_24xx));
+
+ vce->entry_type = VP_CTRL_IOCB_TYPE;
+ vce->entry_count = 1;
+ vce->command = cpu_to_le16(cmd);
+ vce->vp_count = __constant_cpu_to_le16(1);
+
+ /* index map in firmware starts with 1; decrement index
+ * this is ok as we never use index 0
+ */
+ map = (vp_index - 1) / 8;
+ pos = (vp_index - 1) & 7;
+ mutex_lock(&ha->vport_lock);
+ vce->vp_idx_map[map] |= 1 << pos;
+ mutex_unlock(&ha->vport_lock);
+
+ rval = qla2x00_issue_iocb(base_vha, vce, vce_dma, 0);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10c3,
+ "Failed to issue VP control IOCB (%x).\n", rval);
+ } else if (vce->entry_status != 0) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10c4,
+ "Failed to complete IOCB -- error status (%x).\n",
+ vce->entry_status);
+ rval = QLA_FUNCTION_FAILED;
+ } else if (vce->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10c5,
+ "Failed to complet IOCB -- completion status (%x).\n",
+ le16_to_cpu(vce->comp_status));
+ rval = QLA_FUNCTION_FAILED;
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10c6, "Done %s.\n", __func__);
+ }
+
+ dma_pool_free(ha->s_dma_pool, vce, vce_dma);
+
+ return rval;
+}
+
+/*
+ * qla2x00_send_change_request
+ * Receive or disable RSCN request from fabric controller
+ *
+ * Input:
+ * ha = adapter block pointer
+ * format = registration format:
+ * 0 - Reserved
+ * 1 - Fabric detected registration
+ * 2 - N_port detected registration
+ * 3 - Full registration
+ * FF - clear registration
+ * vp_idx = Virtual port index
+ *
+ * Returns:
+ * qla2x00 local function return status code.
+ *
+ * Context:
+ * Kernel Context
+ */
+
+int
+qla2x00_send_change_request(scsi_qla_host_t *vha, uint16_t format,
+ uint16_t vp_idx)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10c7, "Entered %s.\n", __func__);
+
+ /*
+ * This command is implicitly executed by firmware during login for the
+ * physical hosts
+ */
+ if (vp_idx == 0)
+ return QLA_FUNCTION_FAILED;
+
+ mcp->mb[0] = MBC_SEND_CHANGE_REQUEST;
+ mcp->mb[1] = format;
+ mcp->mb[9] = vp_idx;
+ mcp->out_mb = MBX_9|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0|MBX_1;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval == QLA_SUCCESS) {
+ if (mcp->mb[0] != MBS_COMMAND_COMPLETE) {
+ rval = BIT_1;
+ }
+ } else
+ rval = BIT_1;
+
+ return rval;
+}
+
+int
+qla2x00_dump_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t addr,
+ uint32_t size)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1009, "Entered %s.\n", __func__);
+
+ if (MSW(addr) || IS_FWI2_CAPABLE(vha->hw)) {
+ mcp->mb[0] = MBC_DUMP_RISC_RAM_EXTENDED;
+ mcp->mb[8] = MSW(addr);
+ mcp->out_mb = MBX_8|MBX_0;
+ } else {
+ mcp->mb[0] = MBC_DUMP_RISC_RAM;
+ mcp->out_mb = MBX_0;
+ }
+ mcp->mb[1] = LSW(addr);
+ mcp->mb[2] = MSW(req_dma);
+ mcp->mb[3] = LSW(req_dma);
+ mcp->mb[6] = MSW(MSD(req_dma));
+ mcp->mb[7] = LSW(MSD(req_dma));
+ mcp->out_mb |= MBX_7|MBX_6|MBX_3|MBX_2|MBX_1;
+ if (IS_FWI2_CAPABLE(vha->hw)) {
+ mcp->mb[4] = MSW(size);
+ mcp->mb[5] = LSW(size);
+ mcp->out_mb |= MBX_5|MBX_4;
+ } else {
+ mcp->mb[4] = LSW(size);
+ mcp->out_mb |= MBX_4;
+ }
+
+ mcp->in_mb = MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1008,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x1007, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+/* 84XX Support **************************************************************/
+
+struct cs84xx_mgmt_cmd {
+ union {
+ struct verify_chip_entry_84xx req;
+ struct verify_chip_rsp_84xx rsp;
+ } p;
+};
+
+int
+qla84xx_verify_chip(struct scsi_qla_host *vha, uint16_t *status)
+{
+ int rval, retry;
+ struct cs84xx_mgmt_cmd *mn;
+ dma_addr_t mn_dma;
+ uint16_t options;
+ unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10c8, "Entered %s.\n", __func__);
+
+ mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma);
+ if (mn == NULL) {
+ return QLA_MEMORY_ALLOC_FAILED;
+ }
+
+ /* Force Update? */
+ options = ha->cs84xx->fw_update ? VCO_FORCE_UPDATE : 0;
+ /* Diagnostic firmware? */
+ /* options |= MENLO_DIAG_FW; */
+ /* We update the firmware with only one data sequence. */
+ options |= VCO_END_OF_DATA;
+
+ do {
+ retry = 0;
+ memset(mn, 0, sizeof(*mn));
+ mn->p.req.entry_type = VERIFY_CHIP_IOCB_TYPE;
+ mn->p.req.entry_count = 1;
+ mn->p.req.options = cpu_to_le16(options);
+
+ ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x111c,
+ "Dump of Verify Request.\n");
+ ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x111e,
+ (uint8_t *)mn, sizeof(*mn));
+
+ rval = qla2x00_issue_iocb_timeout(vha, mn, mn_dma, 0, 120);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10cb,
+ "Failed to issue verify IOCB (%x).\n", rval);
+ goto verify_done;
+ }
+
+ ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1110,
+ "Dump of Verify Response.\n");
+ ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1118,
+ (uint8_t *)mn, sizeof(*mn));
+
+ status[0] = le16_to_cpu(mn->p.rsp.comp_status);
+ status[1] = status[0] == CS_VCS_CHIP_FAILURE ?
+ le16_to_cpu(mn->p.rsp.failure_code) : 0;
+ ql_dbg(ql_dbg_mbx, vha, 0x10ce,
+ "cs=%x fc=%x.\n", status[0], status[1]);
+
+ if (status[0] != CS_COMPLETE) {
+ rval = QLA_FUNCTION_FAILED;
+ if (!(options & VCO_DONT_UPDATE_FW)) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10cf,
+ "Firmware update failed. Retrying "
+ "without update firmware.\n");
+ options |= VCO_DONT_UPDATE_FW;
+ options &= ~VCO_FORCE_UPDATE;
+ retry = 1;
+ }
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10d0,
+ "Firmware updated to %x.\n",
+ le32_to_cpu(mn->p.rsp.fw_ver));
+
+ /* NOTE: we only update OP firmware. */
+ spin_lock_irqsave(&ha->cs84xx->access_lock, flags);
+ ha->cs84xx->op_fw_version =
+ le32_to_cpu(mn->p.rsp.fw_ver);
+ spin_unlock_irqrestore(&ha->cs84xx->access_lock,
+ flags);
+ }
+ } while (retry);
+
+verify_done:
+ dma_pool_free(ha->s_dma_pool, mn, mn_dma);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10d1, "Failed=%x.\n", rval);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10d2, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req)
+{
+ int rval;
+ unsigned long flags;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ struct device_reg_25xxmq __iomem *reg;
+ struct qla_hw_data *ha = vha->hw;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10d3, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_INITIALIZE_MULTIQ;
+ mcp->mb[1] = req->options;
+ mcp->mb[2] = MSW(LSD(req->dma));
+ mcp->mb[3] = LSW(LSD(req->dma));
+ mcp->mb[6] = MSW(MSD(req->dma));
+ mcp->mb[7] = LSW(MSD(req->dma));
+ mcp->mb[5] = req->length;
+ if (req->rsp)
+ mcp->mb[10] = req->rsp->id;
+ mcp->mb[12] = req->qos;
+ mcp->mb[11] = req->vp_idx;
+ mcp->mb[13] = req->rid;
+ if (IS_QLA83XX(ha))
+ mcp->mb[15] = 0;
+
+ reg = (struct device_reg_25xxmq *)((void *)(ha->mqiobase) +
+ QLA_QUE_PAGE * req->id);
+
+ mcp->mb[4] = req->id;
+ /* que in ptr index */
+ mcp->mb[8] = 0;
+ /* que out ptr index */
+ mcp->mb[9] = 0;
+ mcp->out_mb = MBX_14|MBX_13|MBX_12|MBX_11|MBX_10|MBX_9|MBX_8|MBX_7|
+ MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->flags = MBX_DMA_OUT;
+ mcp->tov = MBX_TOV_SECONDS * 2;
+
+ if (IS_QLA81XX(ha) || IS_QLA83XX(ha))
+ mcp->in_mb |= MBX_1;
+ if (IS_QLA83XX(ha)) {
+ mcp->out_mb |= MBX_15;
+ /* debug q create issue in SR-IOV */
+ mcp->in_mb |= MBX_9 | MBX_8 | MBX_7;
+ }
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ if (!(req->options & BIT_0)) {
+ WRT_REG_DWORD(&reg->req_q_in, 0);
+ if (!IS_QLA83XX(ha))
+ WRT_REG_DWORD(&reg->req_q_out, 0);
+ }
+ req->req_q_in = &reg->req_q_in;
+ req->req_q_out = &reg->req_q_out;
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10d4,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10d5, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
+{
+ int rval;
+ unsigned long flags;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ struct device_reg_25xxmq __iomem *reg;
+ struct qla_hw_data *ha = vha->hw;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10d6, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_INITIALIZE_MULTIQ;
+ mcp->mb[1] = rsp->options;
+ mcp->mb[2] = MSW(LSD(rsp->dma));
+ mcp->mb[3] = LSW(LSD(rsp->dma));
+ mcp->mb[6] = MSW(MSD(rsp->dma));
+ mcp->mb[7] = LSW(MSD(rsp->dma));
+ mcp->mb[5] = rsp->length;
+ mcp->mb[14] = rsp->msix->entry;
+ mcp->mb[13] = rsp->rid;
+ if (IS_QLA83XX(ha))
+ mcp->mb[15] = 0;
+
+ reg = (struct device_reg_25xxmq *)((void *)(ha->mqiobase) +
+ QLA_QUE_PAGE * rsp->id);
+
+ mcp->mb[4] = rsp->id;
+ /* que in ptr index */
+ mcp->mb[8] = 0;
+ /* que out ptr index */
+ mcp->mb[9] = 0;
+ mcp->out_mb = MBX_14|MBX_13|MBX_9|MBX_8|MBX_7
+ |MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->flags = MBX_DMA_OUT;
+ mcp->tov = MBX_TOV_SECONDS * 2;
+
+ if (IS_QLA81XX(ha)) {
+ mcp->out_mb |= MBX_12|MBX_11|MBX_10;
+ mcp->in_mb |= MBX_1;
+ } else if (IS_QLA83XX(ha)) {
+ mcp->out_mb |= MBX_15|MBX_12|MBX_11|MBX_10;
+ mcp->in_mb |= MBX_1;
+ /* debug q create issue in SR-IOV */
+ mcp->in_mb |= MBX_9 | MBX_8 | MBX_7;
+ }
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ if (!(rsp->options & BIT_0)) {
+ WRT_REG_DWORD(&reg->rsp_q_out, 0);
+ if (!IS_QLA83XX(ha))
+ WRT_REG_DWORD(&reg->rsp_q_in, 0);
+ }
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10d7,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10d8, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla81xx_idc_ack(scsi_qla_host_t *vha, uint16_t *mb)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10d9, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_IDC_ACK;
+ memcpy(&mcp->mb[1], mb, QLA_IDC_ACK_REGS * sizeof(uint16_t));
+ mcp->out_mb = MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10da,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10db, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla81xx_fac_get_sector_size(scsi_qla_host_t *vha, uint32_t *sector_size)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10dc, "Entered %s.\n", __func__);
+
+ if (!IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw))
+ return QLA_FUNCTION_FAILED;
+
+ mcp->mb[0] = MBC_FLASH_ACCESS_CTRL;
+ mcp->mb[1] = FAC_OPT_CMD_GET_SECTOR_SIZE;
+ mcp->out_mb = MBX_1|MBX_0;
+ mcp->in_mb = MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10dd,
+ "Failed=%x mb[0]=%x mb[1]=%x.\n",
+ rval, mcp->mb[0], mcp->mb[1]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10de, "Done %s.\n", __func__);
+ *sector_size = mcp->mb[1];
+ }
+
+ return rval;
+}
+
+int
+qla81xx_fac_do_write_enable(scsi_qla_host_t *vha, int enable)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ if (!IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw))
+ return QLA_FUNCTION_FAILED;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10df, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_FLASH_ACCESS_CTRL;
+ mcp->mb[1] = enable ? FAC_OPT_CMD_WRITE_ENABLE :
+ FAC_OPT_CMD_WRITE_PROTECT;
+ mcp->out_mb = MBX_1|MBX_0;
+ mcp->in_mb = MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10e0,
+ "Failed=%x mb[0]=%x mb[1]=%x.\n",
+ rval, mcp->mb[0], mcp->mb[1]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10e1, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla81xx_fac_erase_sector(scsi_qla_host_t *vha, uint32_t start, uint32_t finish)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ if (!IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw))
+ return QLA_FUNCTION_FAILED;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10e2, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_FLASH_ACCESS_CTRL;
+ mcp->mb[1] = FAC_OPT_CMD_ERASE_SECTOR;
+ mcp->mb[2] = LSW(start);
+ mcp->mb[3] = MSW(start);
+ mcp->mb[4] = LSW(finish);
+ mcp->mb[5] = MSW(finish);
+ mcp->out_mb = MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_2|MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10e3,
+ "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x.\n",
+ rval, mcp->mb[0], mcp->mb[1], mcp->mb[2]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10e4, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla81xx_restart_mpi_firmware(scsi_qla_host_t *vha)
+{
+ int rval = 0;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10e5, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_RESTART_MPI_FW;
+ mcp->out_mb = MBX_0;
+ mcp->in_mb = MBX_0|MBX_1;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10e6,
+ "Failed=%x mb[0]=%x mb[1]=%x.\n",
+ rval, mcp->mb[0], mcp->mb[1]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10e7, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla2x00_read_sfp(scsi_qla_host_t *vha, dma_addr_t sfp_dma, uint8_t *sfp,
+ uint16_t dev, uint16_t off, uint16_t len, uint16_t opt)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10e8, "Entered %s.\n", __func__);
+
+ if (!IS_FWI2_CAPABLE(ha))
+ return QLA_FUNCTION_FAILED;
+
+ if (len == 1)
+ opt |= BIT_0;
+
+ mcp->mb[0] = MBC_READ_SFP;
+ mcp->mb[1] = dev;
+ mcp->mb[2] = MSW(sfp_dma);
+ mcp->mb[3] = LSW(sfp_dma);
+ mcp->mb[6] = MSW(MSD(sfp_dma));
+ mcp->mb[7] = LSW(MSD(sfp_dma));
+ mcp->mb[8] = len;
+ mcp->mb[9] = off;
+ mcp->mb[10] = opt;
+ mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (opt & BIT_0)
+ *sfp = mcp->mb[1];
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10e9,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10ea, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla2x00_write_sfp(scsi_qla_host_t *vha, dma_addr_t sfp_dma, uint8_t *sfp,
+ uint16_t dev, uint16_t off, uint16_t len, uint16_t opt)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10eb, "Entered %s.\n", __func__);
+
+ if (!IS_FWI2_CAPABLE(ha))
+ return QLA_FUNCTION_FAILED;
+
+ if (len == 1)
+ opt |= BIT_0;
+
+ if (opt & BIT_0)
+ len = *sfp;
+
+ mcp->mb[0] = MBC_WRITE_SFP;
+ mcp->mb[1] = dev;
+ mcp->mb[2] = MSW(sfp_dma);
+ mcp->mb[3] = LSW(sfp_dma);
+ mcp->mb[6] = MSW(MSD(sfp_dma));
+ mcp->mb[7] = LSW(MSD(sfp_dma));
+ mcp->mb[8] = len;
+ mcp->mb[9] = off;
+ mcp->mb[10] = opt;
+ mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10ec,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10ed, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla2x00_get_xgmac_stats(scsi_qla_host_t *vha, dma_addr_t stats_dma,
+ uint16_t size_in_bytes, uint16_t *actual_size)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10ee, "Entered %s.\n", __func__);
+
+ if (!IS_CNA_CAPABLE(vha->hw))
+ return QLA_FUNCTION_FAILED;
+
+ mcp->mb[0] = MBC_GET_XGMAC_STATS;
+ mcp->mb[2] = MSW(stats_dma);
+ mcp->mb[3] = LSW(stats_dma);
+ mcp->mb[6] = MSW(MSD(stats_dma));
+ mcp->mb[7] = LSW(MSD(stats_dma));
+ mcp->mb[8] = size_in_bytes >> 2;
+ mcp->out_mb = MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_0;
+ mcp->in_mb = MBX_2|MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10ef,
+ "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x.\n",
+ rval, mcp->mb[0], mcp->mb[1], mcp->mb[2]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10f0, "Done %s.\n", __func__);
+
+
+ *actual_size = mcp->mb[2] << 2;
+ }
+
+ return rval;
+}
+
+int
+qla2x00_get_dcbx_params(scsi_qla_host_t *vha, dma_addr_t tlv_dma,
+ uint16_t size)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10f1, "Entered %s.\n", __func__);
+
+ if (!IS_CNA_CAPABLE(vha->hw))
+ return QLA_FUNCTION_FAILED;
+
+ mcp->mb[0] = MBC_GET_DCBX_PARAMS;
+ mcp->mb[1] = 0;
+ mcp->mb[2] = MSW(tlv_dma);
+ mcp->mb[3] = LSW(tlv_dma);
+ mcp->mb[6] = MSW(MSD(tlv_dma));
+ mcp->mb[7] = LSW(MSD(tlv_dma));
+ mcp->mb[8] = size;
+ mcp->out_mb = MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_2|MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10f2,
+ "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x.\n",
+ rval, mcp->mb[0], mcp->mb[1], mcp->mb[2]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10f3, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla2x00_read_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t *data)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10f4, "Entered %s.\n", __func__);
+
+ if (!IS_FWI2_CAPABLE(vha->hw))
+ return QLA_FUNCTION_FAILED;
+
+ mcp->mb[0] = MBC_READ_RAM_EXTENDED;
+ mcp->mb[1] = LSW(risc_addr);
+ mcp->mb[8] = MSW(risc_addr);
+ mcp->out_mb = MBX_8|MBX_1|MBX_0;
+ mcp->in_mb = MBX_3|MBX_2|MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10f5,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10f6, "Done %s.\n", __func__);
+ *data = mcp->mb[3] << 16 | mcp->mb[2];
+ }
+
+ return rval;
+}
+
+int
+qla2x00_loopback_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq,
+ uint16_t *mresp)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ uint32_t iter_cnt = 0x1;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10f7, "Entered %s.\n", __func__);
+
+ memset(mcp->mb, 0 , sizeof(mcp->mb));
+ mcp->mb[0] = MBC_DIAGNOSTIC_LOOP_BACK;
+ mcp->mb[1] = mreq->options | BIT_6; // BIT_6 specifies 64 bit addressing
+
+ /* transfer count */
+ mcp->mb[10] = LSW(mreq->transfer_size);
+ mcp->mb[11] = MSW(mreq->transfer_size);
+
+ /* send data address */
+ mcp->mb[14] = LSW(mreq->send_dma);
+ mcp->mb[15] = MSW(mreq->send_dma);
+ mcp->mb[20] = LSW(MSD(mreq->send_dma));
+ mcp->mb[21] = MSW(MSD(mreq->send_dma));
+
+ /* receive data address */
+ mcp->mb[16] = LSW(mreq->rcv_dma);
+ mcp->mb[17] = MSW(mreq->rcv_dma);
+ mcp->mb[6] = LSW(MSD(mreq->rcv_dma));
+ mcp->mb[7] = MSW(MSD(mreq->rcv_dma));
+
+ /* Iteration count */
+ mcp->mb[18] = LSW(iter_cnt);
+ mcp->mb[19] = MSW(iter_cnt);
+
+ mcp->out_mb = MBX_21|MBX_20|MBX_19|MBX_18|MBX_17|MBX_16|MBX_15|
+ MBX_14|MBX_13|MBX_12|MBX_11|MBX_10|MBX_7|MBX_6|MBX_1|MBX_0;
+ if (IS_CNA_CAPABLE(vha->hw))
+ mcp->out_mb |= MBX_2;
+ mcp->in_mb = MBX_19|MBX_18|MBX_3|MBX_2|MBX_1|MBX_0;
+
+ mcp->buf_size = mreq->transfer_size;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = MBX_DMA_OUT|MBX_DMA_IN|IOCTL_CMD;
+
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10f8,
+ "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x mb[3]=%x mb[18]=%x "
+ "mb[19]=%x.\n", rval, mcp->mb[0], mcp->mb[1], mcp->mb[2],
+ mcp->mb[3], mcp->mb[18], mcp->mb[19]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10f9, "Done %s.\n", __func__);
+ }
+
+ /* Copy mailbox information */
+ memcpy( mresp, mcp->mb, 64);
+ return rval;
+}
+
+int
+qla2x00_echo_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq,
+ uint16_t *mresp)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10fa, "Entered %s.\n", __func__);
+
+ memset(mcp->mb, 0 , sizeof(mcp->mb));
+ mcp->mb[0] = MBC_DIAGNOSTIC_ECHO;
+ mcp->mb[1] = mreq->options | BIT_6; /* BIT_6 specifies 64bit address */
+ if (IS_CNA_CAPABLE(ha)) {
+ mcp->mb[1] |= BIT_15;
+ mcp->mb[2] = vha->fcoe_fcf_idx;
+ }
+ mcp->mb[16] = LSW(mreq->rcv_dma);
+ mcp->mb[17] = MSW(mreq->rcv_dma);
+ mcp->mb[6] = LSW(MSD(mreq->rcv_dma));
+ mcp->mb[7] = MSW(MSD(mreq->rcv_dma));
+
+ mcp->mb[10] = LSW(mreq->transfer_size);
+
+ mcp->mb[14] = LSW(mreq->send_dma);
+ mcp->mb[15] = MSW(mreq->send_dma);
+ mcp->mb[20] = LSW(MSD(mreq->send_dma));
+ mcp->mb[21] = MSW(MSD(mreq->send_dma));
+
+ mcp->out_mb = MBX_21|MBX_20|MBX_17|MBX_16|MBX_15|
+ MBX_14|MBX_10|MBX_7|MBX_6|MBX_1|MBX_0;
+ if (IS_CNA_CAPABLE(ha))
+ mcp->out_mb |= MBX_2;
+
+ mcp->in_mb = MBX_0;
+ if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) ||
+ IS_CNA_CAPABLE(ha) || IS_QLA2031(ha))
+ mcp->in_mb |= MBX_1;
+ if (IS_CNA_CAPABLE(ha) || IS_QLA2031(ha))
+ mcp->in_mb |= MBX_3;
+
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = MBX_DMA_OUT|MBX_DMA_IN|IOCTL_CMD;
+ mcp->buf_size = mreq->transfer_size;
+
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10fb,
+ "Failed=%x mb[0]=%x mb[1]=%x.\n",
+ rval, mcp->mb[0], mcp->mb[1]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10fc, "Done %s.\n", __func__);
+ }
+
+ /* Copy mailbox information */
+ memcpy(mresp, mcp->mb, 64);
+ return rval;
+}
+
+int
+qla84xx_reset_chip(scsi_qla_host_t *vha, uint16_t enable_diagnostic)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10fd,
+ "Entered %s enable_diag=%d.\n", __func__, enable_diagnostic);
+
+ mcp->mb[0] = MBC_ISP84XX_RESET;
+ mcp->mb[1] = enable_diagnostic;
+ mcp->out_mb = MBX_1|MBX_0;
+ mcp->in_mb = MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = MBX_DMA_OUT|MBX_DMA_IN|IOCTL_CMD;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS)
+ ql_dbg(ql_dbg_mbx, vha, 0x10fe, "Failed=%x.\n", rval);
+ else
+ ql_dbg(ql_dbg_mbx, vha, 0x10ff, "Done %s.\n", __func__);
+
+ return rval;
+}
+
+int
+qla2x00_write_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t data)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1100, "Entered %s.\n", __func__);
+
+ if (!IS_FWI2_CAPABLE(vha->hw))
+ return QLA_FUNCTION_FAILED;
+
+ mcp->mb[0] = MBC_WRITE_RAM_WORD_EXTENDED;
+ mcp->mb[1] = LSW(risc_addr);
+ mcp->mb[2] = LSW(data);
+ mcp->mb[3] = MSW(data);
+ mcp->mb[8] = MSW(risc_addr);
+ mcp->out_mb = MBX_8|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1101,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x1102, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla81xx_write_mpi_register(scsi_qla_host_t *vha, uint16_t *mb)
+{
+ int rval;
+ uint32_t stat, timer;
+ uint16_t mb0 = 0;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+ rval = QLA_SUCCESS;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1103, "Entered %s.\n", __func__);
+
+ clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
+
+ /* Write the MBC data to the registers */
+ WRT_REG_WORD(&reg->mailbox0, MBC_WRITE_MPI_REGISTER);
+ WRT_REG_WORD(&reg->mailbox1, mb[0]);
+ WRT_REG_WORD(&reg->mailbox2, mb[1]);
+ WRT_REG_WORD(&reg->mailbox3, mb[2]);
+ WRT_REG_WORD(&reg->mailbox4, mb[3]);
+
+ WRT_REG_DWORD(&reg->hccr, HCCRX_SET_HOST_INT);
+
+ /* Poll for MBC interrupt */
+ for (timer = 6000000; timer; timer--) {
+ /* Check for pending interrupts. */
+ stat = RD_REG_DWORD(&reg->host_status);
+ if (stat & HSRX_RISC_INT) {
+ stat &= 0xff;
+
+ if (stat == 0x1 || stat == 0x2 ||
+ stat == 0x10 || stat == 0x11) {
+ set_bit(MBX_INTERRUPT,
+ &ha->mbx_cmd_flags);
+ mb0 = RD_REG_WORD(&reg->mailbox0);
+ WRT_REG_DWORD(&reg->hccr,
+ HCCRX_CLR_RISC_INT);
+ RD_REG_DWORD(&reg->hccr);
+ break;
+ }
+ }
+ udelay(5);
+ }
+
+ if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags))
+ rval = mb0 & MBS_MASK;
+ else
+ rval = QLA_FUNCTION_FAILED;
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1104,
+ "Failed=%x mb[0]=%x.\n", rval, mb[0]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x1105, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla2x00_get_data_rate(scsi_qla_host_t *vha)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1106, "Entered %s.\n", __func__);
+
+ if (!IS_FWI2_CAPABLE(ha))
+ return QLA_FUNCTION_FAILED;
+
+ mcp->mb[0] = MBC_DATA_RATE;
+ mcp->mb[1] = 0;
+ mcp->out_mb = MBX_1|MBX_0;
+ mcp->in_mb = MBX_2|MBX_1|MBX_0;
+ if (IS_QLA83XX(ha))
+ mcp->in_mb |= MBX_3;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1107,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x1108, "Done %s.\n", __func__);
+ if (mcp->mb[1] != 0x7)
+ ha->link_data_rate = mcp->mb[1];
+ }
+
+ return rval;
+}
+
+int
+qla81xx_get_port_config(scsi_qla_host_t *vha, uint16_t *mb)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1109, "Entered %s.\n", __func__);
+
+ if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha))
+ return QLA_FUNCTION_FAILED;
+ mcp->mb[0] = MBC_GET_PORT_CONFIG;
+ mcp->out_mb = MBX_0;
+ mcp->in_mb = MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x110a,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ /* Copy all bits to preserve original value */
+ memcpy(mb, &mcp->mb[1], sizeof(uint16_t) * 4);
+
+ ql_dbg(ql_dbg_mbx, vha, 0x110b, "Done %s.\n", __func__);
+ }
+ return rval;
+}
+
+int
+qla81xx_set_port_config(scsi_qla_host_t *vha, uint16_t *mb)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x110c, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_SET_PORT_CONFIG;
+ /* Copy all bits to preserve original setting */
+ memcpy(&mcp->mb[1], mb, sizeof(uint16_t) * 4);
+ mcp->out_mb = MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x110d,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else
+ ql_dbg(ql_dbg_mbx, vha, 0x110e, "Done %s.\n", __func__);
+
+ return rval;
+}
+
+
+int
+qla24xx_set_fcp_prio(scsi_qla_host_t *vha, uint16_t loop_id, uint16_t priority,
+ uint16_t *mb)
+{
+ int rval;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ struct qla_hw_data *ha = vha->hw;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x110f, "Entered %s.\n", __func__);
+
+ if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha))
+ return QLA_FUNCTION_FAILED;
+
+ mcp->mb[0] = MBC_PORT_PARAMS;
+ mcp->mb[1] = loop_id;
+ if (ha->flags.fcp_prio_enabled)
+ mcp->mb[2] = BIT_1;
+ else
+ mcp->mb[2] = BIT_2;
+ mcp->mb[4] = priority & 0xf;
+ mcp->mb[9] = vha->vp_idx;
+ mcp->out_mb = MBX_9|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_4|MBX_3|MBX_1|MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (mb != NULL) {
+ mb[0] = mcp->mb[0];
+ mb[1] = mcp->mb[1];
+ mb[3] = mcp->mb[3];
+ mb[4] = mcp->mb[4];
+ }
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10cd, "Failed=%x.\n", rval);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x10cc, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla2x00_get_thermal_temp(scsi_qla_host_t *vha, uint16_t *temp, uint16_t *frac)
+{
+ int rval;
+ uint8_t byte;
+ struct qla_hw_data *ha = vha->hw;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x10ca, "Entered %s.\n", __func__);
+
+ /* Integer part */
+ rval = qla2x00_read_sfp(vha, 0, &byte, 0x98, 0x01, 1, BIT_13|BIT_0);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x10c9, "Failed=%x.\n", rval);
+ ha->flags.thermal_supported = 0;
+ goto fail;
+ }
+ *temp = byte;
+
+ /* Fraction part */
+ rval = qla2x00_read_sfp(vha, 0, &byte, 0x98, 0x10, 1, BIT_13|BIT_0);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1019, "Failed=%x.\n", rval);
+ ha->flags.thermal_supported = 0;
+ goto fail;
+ }
+ *frac = (byte >> 6) * 25;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1018, "Done %s.\n", __func__);
+fail:
+ return rval;
+}
+
+int
+qla82xx_mbx_intr_enable(scsi_qla_host_t *vha)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1017, "Entered %s.\n", __func__);
+
+ if (!IS_FWI2_CAPABLE(ha))
+ return QLA_FUNCTION_FAILED;
+
+ memset(mcp, 0, sizeof(mbx_cmd_t));
+ mcp->mb[0] = MBC_TOGGLE_INTERRUPT;
+ mcp->mb[1] = 1;
+
+ mcp->out_mb = MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1016,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x100e, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla82xx_mbx_intr_disable(scsi_qla_host_t *vha)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x100d, "Entered %s.\n", __func__);
+
+ if (!IS_QLA82XX(ha))
+ return QLA_FUNCTION_FAILED;
+
+ memset(mcp, 0, sizeof(mbx_cmd_t));
+ mcp->mb[0] = MBC_TOGGLE_INTERRUPT;
+ mcp->mb[1] = 0;
+
+ mcp->out_mb = MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x100c,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x100b, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla82xx_md_get_template_size(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ int rval = QLA_FUNCTION_FAILED;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x111f, "Entered %s.\n", __func__);
+
+ memset(mcp->mb, 0 , sizeof(mcp->mb));
+ mcp->mb[0] = LSW(MBC_DIAGNOSTIC_MINIDUMP_TEMPLATE);
+ mcp->mb[1] = MSW(MBC_DIAGNOSTIC_MINIDUMP_TEMPLATE);
+ mcp->mb[2] = LSW(RQST_TMPLT_SIZE);
+ mcp->mb[3] = MSW(RQST_TMPLT_SIZE);
+
+ mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_14|MBX_13|MBX_12|MBX_11|MBX_10|MBX_9|MBX_8|
+ MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+
+ mcp->flags = MBX_DMA_OUT|MBX_DMA_IN|IOCTL_CMD;
+ mcp->tov = MBX_TOV_SECONDS;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ /* Always copy back return mailbox values. */
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1120,
+ "mailbox command FAILED=0x%x, subcode=%x.\n",
+ (mcp->mb[1] << 16) | mcp->mb[0],
+ (mcp->mb[3] << 16) | mcp->mb[2]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x1121, "Done %s.\n", __func__);
+ ha->md_template_size = ((mcp->mb[3] << 16) | mcp->mb[2]);
+ if (!ha->md_template_size) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1122,
+ "Null template size obtained.\n");
+ rval = QLA_FUNCTION_FAILED;
+ }
+ }
+ return rval;
+}
+
+int
+qla82xx_md_get_template(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+ int rval = QLA_FUNCTION_FAILED;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1123, "Entered %s.\n", __func__);
+
+ ha->md_tmplt_hdr = dma_alloc_coherent(&ha->pdev->dev,
+ ha->md_template_size, &ha->md_tmplt_hdr_dma, GFP_KERNEL);
+ if (!ha->md_tmplt_hdr) {
+ ql_log(ql_log_warn, vha, 0x1124,
+ "Unable to allocate memory for Minidump template.\n");
+ return rval;
+ }
+
+ memset(mcp->mb, 0 , sizeof(mcp->mb));
+ mcp->mb[0] = LSW(MBC_DIAGNOSTIC_MINIDUMP_TEMPLATE);
+ mcp->mb[1] = MSW(MBC_DIAGNOSTIC_MINIDUMP_TEMPLATE);
+ mcp->mb[2] = LSW(RQST_TMPLT);
+ mcp->mb[3] = MSW(RQST_TMPLT);
+ mcp->mb[4] = LSW(LSD(ha->md_tmplt_hdr_dma));
+ mcp->mb[5] = MSW(LSD(ha->md_tmplt_hdr_dma));
+ mcp->mb[6] = LSW(MSD(ha->md_tmplt_hdr_dma));
+ mcp->mb[7] = MSW(MSD(ha->md_tmplt_hdr_dma));
+ mcp->mb[8] = LSW(ha->md_template_size);
+ mcp->mb[9] = MSW(ha->md_template_size);
+
+ mcp->flags = MBX_DMA_OUT|MBX_DMA_IN|IOCTL_CMD;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->out_mb = MBX_11|MBX_10|MBX_9|MBX_8|
+ MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+ mcp->in_mb = MBX_3|MBX_2|MBX_1|MBX_0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1125,
+ "mailbox command FAILED=0x%x, subcode=%x.\n",
+ ((mcp->mb[1] << 16) | mcp->mb[0]),
+ ((mcp->mb[3] << 16) | mcp->mb[2]));
+ } else
+ ql_dbg(ql_dbg_mbx, vha, 0x1126, "Done %s.\n", __func__);
+ return rval;
+}
+
+int
+qla81xx_set_led_config(scsi_qla_host_t *vha, uint16_t *led_cfg)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ if (!IS_QLA81XX(ha) && !IS_QLA8031(ha))
+ return QLA_FUNCTION_FAILED;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1133, "Entered %s.\n", __func__);
+
+ memset(mcp, 0, sizeof(mbx_cmd_t));
+ mcp->mb[0] = MBC_SET_LED_CONFIG;
+ mcp->mb[1] = led_cfg[0];
+ mcp->mb[2] = led_cfg[1];
+ if (IS_QLA8031(ha)) {
+ mcp->mb[3] = led_cfg[2];
+ mcp->mb[4] = led_cfg[3];
+ mcp->mb[5] = led_cfg[4];
+ mcp->mb[6] = led_cfg[5];
+ }
+
+ mcp->out_mb = MBX_2|MBX_1|MBX_0;
+ if (IS_QLA8031(ha))
+ mcp->out_mb |= MBX_6|MBX_5|MBX_4|MBX_3;
+ mcp->in_mb = MBX_0;
+ mcp->tov = 30;
+ mcp->flags = 0;
+
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1134,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x1135, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla81xx_get_led_config(scsi_qla_host_t *vha, uint16_t *led_cfg)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ if (!IS_QLA81XX(ha) && !IS_QLA8031(ha))
+ return QLA_FUNCTION_FAILED;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1136, "Entered %s.\n", __func__);
+
+ memset(mcp, 0, sizeof(mbx_cmd_t));
+ mcp->mb[0] = MBC_GET_LED_CONFIG;
+
+ mcp->out_mb = MBX_0;
+ mcp->in_mb = MBX_2|MBX_1|MBX_0;
+ if (IS_QLA8031(ha))
+ mcp->in_mb |= MBX_6|MBX_5|MBX_4|MBX_3;
+ mcp->tov = 30;
+ mcp->flags = 0;
+
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1137,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ led_cfg[0] = mcp->mb[1];
+ led_cfg[1] = mcp->mb[2];
+ if (IS_QLA8031(ha)) {
+ led_cfg[2] = mcp->mb[3];
+ led_cfg[3] = mcp->mb[4];
+ led_cfg[4] = mcp->mb[5];
+ led_cfg[5] = mcp->mb[6];
+ }
+ ql_dbg(ql_dbg_mbx, vha, 0x1138, "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla82xx_mbx_beacon_ctl(scsi_qla_host_t *vha, int enable)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ if (!IS_QLA82XX(ha))
+ return QLA_FUNCTION_FAILED;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1127,
+ "Entered %s.\n", __func__);
+
+ memset(mcp, 0, sizeof(mbx_cmd_t));
+ mcp->mb[0] = MBC_SET_LED_CONFIG;
+ if (enable)
+ mcp->mb[7] = 0xE;
+ else
+ mcp->mb[7] = 0xD;
+
+ mcp->out_mb = MBX_7|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1128,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x1129,
+ "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla83xx_write_remote_reg(scsi_qla_host_t *vha, uint32_t reg, uint32_t data)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ if (!IS_QLA83XX(ha))
+ return QLA_FUNCTION_FAILED;
+
+ ql_dbg(ql_dbg_mbx, vha, 0x1130, "Entered %s.\n", __func__);
+
+ mcp->mb[0] = MBC_WRITE_REMOTE_REG;
+ mcp->mb[1] = LSW(reg);
+ mcp->mb[2] = MSW(reg);
+ mcp->mb[3] = LSW(data);
+ mcp->mb[4] = MSW(data);
+ mcp->out_mb = MBX_4|MBX_3|MBX_2|MBX_1|MBX_0;
+
+ mcp->in_mb = MBX_1|MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_mbx, vha, 0x1131,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ } else {
+ ql_dbg(ql_dbg_mbx, vha, 0x1132,
+ "Done %s.\n", __func__);
+ }
+
+ return rval;
+}
+
+int
+qla2x00_port_logout(scsi_qla_host_t *vha, struct fc_port *fcport)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ mbx_cmd_t mc;
+ mbx_cmd_t *mcp = &mc;
+
+ if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+ ql_dbg(ql_dbg_mbx, vha, 0x113b,
+ "Implicit LOGO Unsupported.\n");
+ return QLA_FUNCTION_FAILED;
+ }
+
+
+ ql_dbg(ql_dbg_mbx, vha, 0x113c, "Done %s.\n", __func__);
+
+ /* Perform Implicit LOGO. */
+ mcp->mb[0] = MBC_PORT_LOGOUT;
+ mcp->mb[1] = fcport->loop_id;
+ mcp->mb[10] = BIT_15;
+ mcp->out_mb = MBX_10|MBX_1|MBX_0;
+ mcp->in_mb = MBX_0;
+ mcp->tov = MBX_TOV_SECONDS;
+ mcp->flags = 0;
+ rval = qla2x00_mailbox_command(vha, mcp);
+ if (rval != QLA_SUCCESS)
+ ql_dbg(ql_dbg_mbx, vha, 0x113d,
+ "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]);
+ else
+ ql_dbg(ql_dbg_mbx, vha, 0x113e, "Done %s.\n", __func__);
+
+ return rval;
+}
+
diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c
new file mode 100644
index 00000000..aa062a1b
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_mid.c
@@ -0,0 +1,836 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2003-2011 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#include "qla_def.h"
+#include "qla_gbl.h"
+
+#include <linux/moduleparam.h>
+#include <linux/vmalloc.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsicam.h>
+#include <linux/delay.h>
+
+void
+qla2x00_vp_stop_timer(scsi_qla_host_t *vha)
+{
+ if (vha->vp_idx && vha->timer_active) {
+ del_timer_sync(&vha->timer);
+ vha->timer_active = 0;
+ }
+}
+
+static uint32_t
+qla24xx_allocate_vp_id(scsi_qla_host_t *vha)
+{
+ uint32_t vp_id;
+ struct qla_hw_data *ha = vha->hw;
+ unsigned long flags;
+
+ /* Find an empty slot and assign an vp_id */
+ mutex_lock(&ha->vport_lock);
+ vp_id = find_first_zero_bit(ha->vp_idx_map, ha->max_npiv_vports + 1);
+ if (vp_id > ha->max_npiv_vports) {
+ ql_dbg(ql_dbg_vport, vha, 0xa000,
+ "vp_id %d is bigger than max-supported %d.\n",
+ vp_id, ha->max_npiv_vports);
+ mutex_unlock(&ha->vport_lock);
+ return vp_id;
+ }
+
+ set_bit(vp_id, ha->vp_idx_map);
+ ha->num_vhosts++;
+ vha->vp_idx = vp_id;
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ list_add_tail(&vha->list, &ha->vp_list);
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
+ mutex_unlock(&ha->vport_lock);
+ return vp_id;
+}
+
+void
+qla24xx_deallocate_vp_id(scsi_qla_host_t *vha)
+{
+ uint16_t vp_id;
+ struct qla_hw_data *ha = vha->hw;
+ unsigned long flags = 0;
+
+ mutex_lock(&ha->vport_lock);
+ /*
+ * Wait for all pending activities to finish before removing vport from
+ * the list.
+ * Lock needs to be held for safe removal from the list (it
+ * ensures no active vp_list traversal while the vport is removed
+ * from the queue)
+ */
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ while (atomic_read(&vha->vref_count)) {
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
+ msleep(500);
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ }
+ list_del(&vha->list);
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
+ vp_id = vha->vp_idx;
+ ha->num_vhosts--;
+ clear_bit(vp_id, ha->vp_idx_map);
+
+ mutex_unlock(&ha->vport_lock);
+}
+
+static scsi_qla_host_t *
+qla24xx_find_vhost_by_name(struct qla_hw_data *ha, uint8_t *port_name)
+{
+ scsi_qla_host_t *vha;
+ struct scsi_qla_host *tvha;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ /* Locate matching device in database. */
+ list_for_each_entry_safe(vha, tvha, &ha->vp_list, list) {
+ if (!memcmp(port_name, vha->port_name, WWN_SIZE)) {
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+ return vha;
+ }
+ }
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+ return NULL;
+}
+
+/*
+ * qla2x00_mark_vp_devices_dead
+ * Updates fcport state when device goes offline.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * fcport = port structure pointer.
+ *
+ * Return:
+ * None.
+ *
+ * Context:
+ */
+static void
+qla2x00_mark_vp_devices_dead(scsi_qla_host_t *vha)
+{
+ /*
+ * !!! NOTE !!!
+ * This function, if called in contexts other than vp create, disable
+ * or delete, please make sure this is synchronized with the
+ * delete thread.
+ */
+ fc_port_t *fcport;
+
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ ql_dbg(ql_dbg_vport, vha, 0xa001,
+ "Marking port dead, loop_id=0x%04x : %x.\n",
+ fcport->loop_id, fcport->vp_idx);
+
+ qla2x00_mark_device_lost(vha, fcport, 0, 0);
+ qla2x00_set_fcport_state(fcport, FCS_UNCONFIGURED);
+ }
+}
+
+int
+qla24xx_disable_vp(scsi_qla_host_t *vha)
+{
+ int ret;
+
+ ret = qla24xx_control_vp(vha, VCE_COMMAND_DISABLE_VPS_LOGO_ALL);
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+
+ qla2x00_mark_vp_devices_dead(vha);
+ atomic_set(&vha->vp_state, VP_FAILED);
+ vha->flags.management_server_logged_in = 0;
+ if (ret == QLA_SUCCESS) {
+ fc_vport_set_state(vha->fc_vport, FC_VPORT_DISABLED);
+ } else {
+ fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
+ return -1;
+ }
+ return 0;
+}
+
+int
+qla24xx_enable_vp(scsi_qla_host_t *vha)
+{
+ int ret;
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
+
+ /* Check if physical ha port is Up */
+ if (atomic_read(&base_vha->loop_state) == LOOP_DOWN ||
+ atomic_read(&base_vha->loop_state) == LOOP_DEAD ||
+ !(ha->current_topology & ISP_CFG_F)) {
+ vha->vp_err_state = VP_ERR_PORTDWN;
+ fc_vport_set_state(vha->fc_vport, FC_VPORT_LINKDOWN);
+ goto enable_failed;
+ }
+
+ /* Initialize the new vport unless it is a persistent port */
+ mutex_lock(&ha->vport_lock);
+ ret = qla24xx_modify_vp_config(vha);
+ mutex_unlock(&ha->vport_lock);
+
+ if (ret != QLA_SUCCESS) {
+ fc_vport_set_state(vha->fc_vport, FC_VPORT_FAILED);
+ goto enable_failed;
+ }
+
+ ql_dbg(ql_dbg_taskm, vha, 0x801a,
+ "Virtual port with id: %d - Enabled.\n", vha->vp_idx);
+ return 0;
+
+enable_failed:
+ ql_dbg(ql_dbg_taskm, vha, 0x801b,
+ "Virtual port with id: %d - Disabled.\n", vha->vp_idx);
+ return 1;
+}
+
+static void
+qla24xx_configure_vp(scsi_qla_host_t *vha)
+{
+ struct fc_vport *fc_vport;
+ int ret;
+
+ fc_vport = vha->fc_vport;
+
+ ql_dbg(ql_dbg_vport, vha, 0xa002,
+ "%s: change request #3.\n", __func__);
+ ret = qla2x00_send_change_request(vha, 0x3, vha->vp_idx);
+ if (ret != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_vport, vha, 0xa003, "Failed to enable "
+ "receiving of RSCN requests: 0x%x.\n", ret);
+ return;
+ } else {
+ /* Corresponds to SCR enabled */
+ clear_bit(VP_SCR_NEEDED, &vha->vp_flags);
+ }
+
+ vha->flags.online = 1;
+ if (qla24xx_configure_vhba(vha))
+ return;
+
+ atomic_set(&vha->vp_state, VP_ACTIVE);
+ fc_vport_set_state(fc_vport, FC_VPORT_ACTIVE);
+}
+
+void
+qla2x00_alert_all_vps(struct rsp_que *rsp, uint16_t *mb)
+{
+ scsi_qla_host_t *vha;
+ struct qla_hw_data *ha = rsp->hw;
+ int i = 0;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ list_for_each_entry(vha, &ha->vp_list, list) {
+ if (vha->vp_idx) {
+ atomic_inc(&vha->vref_count);
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
+ switch (mb[0]) {
+ case MBA_LIP_OCCURRED:
+ case MBA_LOOP_UP:
+ case MBA_LOOP_DOWN:
+ case MBA_LIP_RESET:
+ case MBA_POINT_TO_POINT:
+ case MBA_CHG_IN_CONNECTION:
+ case MBA_PORT_UPDATE:
+ case MBA_RSCN_UPDATE:
+ ql_dbg(ql_dbg_async, vha, 0x5024,
+ "Async_event for VP[%d], mb=0x%x vha=%p.\n",
+ i, *mb, vha);
+ qla2x00_async_event(vha, rsp, mb);
+ break;
+ }
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ atomic_dec(&vha->vref_count);
+ }
+ i++;
+ }
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+}
+
+int
+qla2x00_vp_abort_isp(scsi_qla_host_t *vha)
+{
+ /*
+ * Physical port will do most of the abort and recovery work. We can
+ * just treat it as a loop down
+ */
+ if (atomic_read(&vha->loop_state) != LOOP_DOWN) {
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ qla2x00_mark_all_devices_lost(vha, 0);
+ } else {
+ if (!atomic_read(&vha->loop_down_timer))
+ atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+ }
+
+ /*
+ * To exclusively reset vport, we need to log it out first. Note: this
+ * control_vp can fail if ISP reset is already issued, this is
+ * expected, as the vp would be already logged out due to ISP reset.
+ */
+ if (!test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags))
+ qla24xx_control_vp(vha, VCE_COMMAND_DISABLE_VPS_LOGO_ALL);
+
+ ql_dbg(ql_dbg_taskm, vha, 0x801d,
+ "Scheduling enable of Vport %d.\n", vha->vp_idx);
+ return qla24xx_enable_vp(vha);
+}
+
+static int
+qla2x00_do_dpc_vp(scsi_qla_host_t *vha)
+{
+ ql_dbg(ql_dbg_dpc, vha, 0x4012,
+ "Entering %s.\n", __func__);
+ ql_dbg(ql_dbg_dpc, vha, 0x4013,
+ "vp_flags: 0x%lx.\n", vha->vp_flags);
+
+ qla2x00_do_work(vha);
+
+ if (test_and_clear_bit(VP_IDX_ACQUIRED, &vha->vp_flags)) {
+ /* VP acquired. complete port configuration */
+ ql_dbg(ql_dbg_dpc, vha, 0x4014,
+ "Configure VP scheduled.\n");
+ qla24xx_configure_vp(vha);
+ ql_dbg(ql_dbg_dpc, vha, 0x4015,
+ "Configure VP end.\n");
+ return 0;
+ }
+
+ if (test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags)) {
+ ql_dbg(ql_dbg_dpc, vha, 0x4016,
+ "FCPort update scheduled.\n");
+ qla2x00_update_fcports(vha);
+ clear_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags);
+ ql_dbg(ql_dbg_dpc, vha, 0x4017,
+ "FCPort update end.\n");
+ }
+
+ if ((test_and_clear_bit(RELOGIN_NEEDED, &vha->dpc_flags)) &&
+ !test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags) &&
+ atomic_read(&vha->loop_state) != LOOP_DOWN) {
+
+ ql_dbg(ql_dbg_dpc, vha, 0x4018,
+ "Relogin needed scheduled.\n");
+ qla2x00_relogin(vha);
+ ql_dbg(ql_dbg_dpc, vha, 0x4019,
+ "Relogin needed end.\n");
+ }
+
+ if (test_and_clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags) &&
+ (!(test_and_set_bit(RESET_ACTIVE, &vha->dpc_flags)))) {
+ clear_bit(RESET_ACTIVE, &vha->dpc_flags);
+ }
+
+ if (test_and_clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) {
+ if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags))) {
+ ql_dbg(ql_dbg_dpc, vha, 0x401a,
+ "Loop resync scheduled.\n");
+ qla2x00_loop_resync(vha);
+ clear_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags);
+ ql_dbg(ql_dbg_dpc, vha, 0x401b,
+ "Loop resync end.\n");
+ }
+ }
+
+ ql_dbg(ql_dbg_dpc, vha, 0x401c,
+ "Exiting %s.\n", __func__);
+ return 0;
+}
+
+void
+qla2x00_do_dpc_all_vps(scsi_qla_host_t *vha)
+{
+ int ret;
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *vp;
+ unsigned long flags = 0;
+
+ if (vha->vp_idx)
+ return;
+ if (list_empty(&ha->vp_list))
+ return;
+
+ clear_bit(VP_DPC_NEEDED, &vha->dpc_flags);
+
+ if (!(ha->current_topology & ISP_CFG_F))
+ return;
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ list_for_each_entry(vp, &ha->vp_list, list) {
+ if (vp->vp_idx) {
+ atomic_inc(&vp->vref_count);
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+
+ ret = qla2x00_do_dpc_vp(vp);
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+ atomic_dec(&vp->vref_count);
+ }
+ }
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+}
+
+int
+qla24xx_vport_create_req_sanity_check(struct fc_vport *fc_vport)
+{
+ scsi_qla_host_t *base_vha = shost_priv(fc_vport->shost);
+ struct qla_hw_data *ha = base_vha->hw;
+ scsi_qla_host_t *vha;
+ uint8_t port_name[WWN_SIZE];
+
+ if (fc_vport->roles != FC_PORT_ROLE_FCP_INITIATOR)
+ return VPCERR_UNSUPPORTED;
+
+ /* Check up the F/W and H/W support NPIV */
+ if (!ha->flags.npiv_supported)
+ return VPCERR_UNSUPPORTED;
+
+ /* Check up whether npiv supported switch presented */
+ if (!(ha->switch_cap & FLOGI_MID_SUPPORT))
+ return VPCERR_NO_FABRIC_SUPP;
+
+ /* Check up unique WWPN */
+ u64_to_wwn(fc_vport->port_name, port_name);
+ if (!memcmp(port_name, base_vha->port_name, WWN_SIZE))
+ return VPCERR_BAD_WWN;
+ vha = qla24xx_find_vhost_by_name(ha, port_name);
+ if (vha)
+ return VPCERR_BAD_WWN;
+
+ /* Check up max-npiv-supports */
+ if (ha->num_vhosts > ha->max_npiv_vports) {
+ ql_dbg(ql_dbg_vport, vha, 0xa004,
+ "num_vhosts %ud is bigger "
+ "than max_npiv_vports %ud.\n",
+ ha->num_vhosts, ha->max_npiv_vports);
+ return VPCERR_UNSUPPORTED;
+ }
+ return 0;
+}
+
+scsi_qla_host_t *
+qla24xx_create_vhost(struct fc_vport *fc_vport)
+{
+ scsi_qla_host_t *base_vha = shost_priv(fc_vport->shost);
+ struct qla_hw_data *ha = base_vha->hw;
+ scsi_qla_host_t *vha;
+ struct scsi_host_template *sht = &qla2xxx_driver_template;
+ struct Scsi_Host *host;
+
+ vha = qla2x00_create_host(sht, ha);
+ if (!vha) {
+ ql_log(ql_log_warn, vha, 0xa005,
+ "scsi_host_alloc() failed for vport.\n");
+ return(NULL);
+ }
+
+ host = vha->host;
+ fc_vport->dd_data = vha;
+ /* New host info */
+ u64_to_wwn(fc_vport->node_name, vha->node_name);
+ u64_to_wwn(fc_vport->port_name, vha->port_name);
+
+ vha->fc_vport = fc_vport;
+ vha->device_flags = 0;
+ vha->vp_idx = qla24xx_allocate_vp_id(vha);
+ if (vha->vp_idx > ha->max_npiv_vports) {
+ ql_dbg(ql_dbg_vport, vha, 0xa006,
+ "Couldn't allocate vp_id.\n");
+ goto create_vhost_failed;
+ }
+ vha->mgmt_svr_loop_id = 10 + vha->vp_idx;
+
+ vha->dpc_flags = 0L;
+
+ /*
+ * To fix the issue of processing a parent's RSCN for the vport before
+ * its SCR is complete.
+ */
+ set_bit(VP_SCR_NEEDED, &vha->vp_flags);
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+
+ qla2x00_start_timer(vha, qla2x00_timer, WATCH_INTERVAL);
+
+ vha->req = base_vha->req;
+ host->can_queue = base_vha->req->length + 128;
+ host->this_id = 255;
+ host->cmd_per_lun = 3;
+ if (IS_T10_PI_CAPABLE(ha) && ql2xenabledif)
+ host->max_cmd_len = 32;
+ else
+ host->max_cmd_len = MAX_CMDSZ;
+ host->max_channel = MAX_BUSES - 1;
+ host->max_lun = ql2xmaxlun;
+ host->unique_id = host->host_no;
+ host->max_id = ha->max_fibre_devices;
+ host->transportt = qla2xxx_transport_vport_template;
+
+ ql_dbg(ql_dbg_vport, vha, 0xa007,
+ "Detect vport hba %ld at address = %p.\n",
+ vha->host_no, vha);
+
+ vha->flags.init_done = 1;
+
+ mutex_lock(&ha->vport_lock);
+ set_bit(vha->vp_idx, ha->vp_idx_map);
+ ha->cur_vport_count++;
+ mutex_unlock(&ha->vport_lock);
+
+ return vha;
+
+create_vhost_failed:
+ return NULL;
+}
+
+static void
+qla25xx_free_req_que(struct scsi_qla_host *vha, struct req_que *req)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint16_t que_id = req->id;
+
+ dma_free_coherent(&ha->pdev->dev, (req->length + 1) *
+ sizeof(request_t), req->ring, req->dma);
+ req->ring = NULL;
+ req->dma = 0;
+ if (que_id) {
+ ha->req_q_map[que_id] = NULL;
+ mutex_lock(&ha->vport_lock);
+ clear_bit(que_id, ha->req_qid_map);
+ mutex_unlock(&ha->vport_lock);
+ }
+ kfree(req);
+ req = NULL;
+}
+
+static void
+qla25xx_free_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint16_t que_id = rsp->id;
+
+ if (rsp->msix && rsp->msix->have_irq) {
+ free_irq(rsp->msix->vector, rsp);
+ rsp->msix->have_irq = 0;
+ rsp->msix->rsp = NULL;
+ }
+ dma_free_coherent(&ha->pdev->dev, (rsp->length + 1) *
+ sizeof(response_t), rsp->ring, rsp->dma);
+ rsp->ring = NULL;
+ rsp->dma = 0;
+ if (que_id) {
+ ha->rsp_q_map[que_id] = NULL;
+ mutex_lock(&ha->vport_lock);
+ clear_bit(que_id, ha->rsp_qid_map);
+ mutex_unlock(&ha->vport_lock);
+ }
+ kfree(rsp);
+ rsp = NULL;
+}
+
+int
+qla25xx_delete_req_que(struct scsi_qla_host *vha, struct req_que *req)
+{
+ int ret = -1;
+
+ if (req) {
+ req->options |= BIT_0;
+ ret = qla25xx_init_req_que(vha, req);
+ }
+ if (ret == QLA_SUCCESS)
+ qla25xx_free_req_que(vha, req);
+
+ return ret;
+}
+
+static int
+qla25xx_delete_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp)
+{
+ int ret = -1;
+
+ if (rsp) {
+ rsp->options |= BIT_0;
+ ret = qla25xx_init_rsp_que(vha, rsp);
+ }
+ if (ret == QLA_SUCCESS)
+ qla25xx_free_rsp_que(vha, rsp);
+
+ return ret;
+}
+
+/* Delete all queues for a given vhost */
+int
+qla25xx_delete_queues(struct scsi_qla_host *vha)
+{
+ int cnt, ret = 0;
+ struct req_que *req = NULL;
+ struct rsp_que *rsp = NULL;
+ struct qla_hw_data *ha = vha->hw;
+
+ /* Delete request queues */
+ for (cnt = 1; cnt < ha->max_req_queues; cnt++) {
+ req = ha->req_q_map[cnt];
+ if (req) {
+ ret = qla25xx_delete_req_que(vha, req);
+ if (ret != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x00ea,
+ "Couldn't delete req que %d.\n",
+ req->id);
+ return ret;
+ }
+ }
+ }
+
+ /* Delete response queues */
+ for (cnt = 1; cnt < ha->max_rsp_queues; cnt++) {
+ rsp = ha->rsp_q_map[cnt];
+ if (rsp) {
+ ret = qla25xx_delete_rsp_que(vha, rsp);
+ if (ret != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x00eb,
+ "Couldn't delete rsp que %d.\n",
+ rsp->id);
+ return ret;
+ }
+ }
+ }
+ return ret;
+}
+
+int
+qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options,
+ uint8_t vp_idx, uint16_t rid, int rsp_que, uint8_t qos)
+{
+ int ret = 0;
+ struct req_que *req = NULL;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+ uint16_t que_id = 0;
+ device_reg_t __iomem *reg;
+ uint32_t cnt;
+
+ req = kzalloc(sizeof(struct req_que), GFP_KERNEL);
+ if (req == NULL) {
+ ql_log(ql_log_fatal, base_vha, 0x00d9,
+ "Failed to allocate memory for request queue.\n");
+ goto failed;
+ }
+
+ req->length = REQUEST_ENTRY_CNT_24XX;
+ req->ring = dma_alloc_coherent(&ha->pdev->dev,
+ (req->length + 1) * sizeof(request_t),
+ &req->dma, GFP_KERNEL);
+ if (req->ring == NULL) {
+ ql_log(ql_log_fatal, base_vha, 0x00da,
+ "Failed to allocte memory for request_ring.\n");
+ goto que_failed;
+ }
+
+ mutex_lock(&ha->vport_lock);
+ que_id = find_first_zero_bit(ha->req_qid_map, ha->max_req_queues);
+ if (que_id >= ha->max_req_queues) {
+ mutex_unlock(&ha->vport_lock);
+ ql_log(ql_log_warn, base_vha, 0x00db,
+ "No resources to create additional request queue.\n");
+ goto que_failed;
+ }
+ set_bit(que_id, ha->req_qid_map);
+ ha->req_q_map[que_id] = req;
+ req->rid = rid;
+ req->vp_idx = vp_idx;
+ req->qos = qos;
+
+ ql_dbg(ql_dbg_multiq, base_vha, 0xc002,
+ "queue_id=%d rid=%d vp_idx=%d qos=%d.\n",
+ que_id, req->rid, req->vp_idx, req->qos);
+ ql_dbg(ql_dbg_init, base_vha, 0x00dc,
+ "queue_id=%d rid=%d vp_idx=%d qos=%d.\n",
+ que_id, req->rid, req->vp_idx, req->qos);
+ if (rsp_que < 0)
+ req->rsp = NULL;
+ else
+ req->rsp = ha->rsp_q_map[rsp_que];
+ /* Use alternate PCI bus number */
+ if (MSB(req->rid))
+ options |= BIT_4;
+ /* Use alternate PCI devfn */
+ if (LSB(req->rid))
+ options |= BIT_5;
+ req->options = options;
+
+ ql_dbg(ql_dbg_multiq, base_vha, 0xc003,
+ "options=0x%x.\n", req->options);
+ ql_dbg(ql_dbg_init, base_vha, 0x00dd,
+ "options=0x%x.\n", req->options);
+ for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++)
+ req->outstanding_cmds[cnt] = NULL;
+ req->current_outstanding_cmd = 1;
+
+ req->ring_ptr = req->ring;
+ req->ring_index = 0;
+ req->cnt = req->length;
+ req->id = que_id;
+ reg = ISP_QUE_REG(ha, que_id);
+ req->max_q_depth = ha->req_q_map[0]->max_q_depth;
+ mutex_unlock(&ha->vport_lock);
+ ql_dbg(ql_dbg_multiq, base_vha, 0xc004,
+ "ring_ptr=%p ring_index=%d, "
+ "cnt=%d id=%d max_q_depth=%d.\n",
+ req->ring_ptr, req->ring_index,
+ req->cnt, req->id, req->max_q_depth);
+ ql_dbg(ql_dbg_init, base_vha, 0x00de,
+ "ring_ptr=%p ring_index=%d, "
+ "cnt=%d id=%d max_q_depth=%d.\n",
+ req->ring_ptr, req->ring_index, req->cnt,
+ req->id, req->max_q_depth);
+
+ ret = qla25xx_init_req_que(base_vha, req);
+ if (ret != QLA_SUCCESS) {
+ ql_log(ql_log_fatal, base_vha, 0x00df,
+ "%s failed.\n", __func__);
+ mutex_lock(&ha->vport_lock);
+ clear_bit(que_id, ha->req_qid_map);
+ mutex_unlock(&ha->vport_lock);
+ goto que_failed;
+ }
+
+ return req->id;
+
+que_failed:
+ qla25xx_free_req_que(base_vha, req);
+failed:
+ return 0;
+}
+
+static void qla_do_work(struct work_struct *work)
+{
+ unsigned long flags;
+ struct rsp_que *rsp = container_of(work, struct rsp_que, q_work);
+ struct scsi_qla_host *vha;
+ struct qla_hw_data *ha = rsp->hw;
+
+ spin_lock_irqsave(&rsp->hw->hardware_lock, flags);
+ vha = pci_get_drvdata(ha->pdev);
+ qla24xx_process_response_queue(vha, rsp);
+ spin_unlock_irqrestore(&rsp->hw->hardware_lock, flags);
+}
+
+/* create response queue */
+int
+qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options,
+ uint8_t vp_idx, uint16_t rid, int req)
+{
+ int ret = 0;
+ struct rsp_que *rsp = NULL;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+ uint16_t que_id = 0;
+ device_reg_t __iomem *reg;
+
+ rsp = kzalloc(sizeof(struct rsp_que), GFP_KERNEL);
+ if (rsp == NULL) {
+ ql_log(ql_log_warn, base_vha, 0x0066,
+ "Failed to allocate memory for response queue.\n");
+ goto failed;
+ }
+
+ rsp->length = RESPONSE_ENTRY_CNT_MQ;
+ rsp->ring = dma_alloc_coherent(&ha->pdev->dev,
+ (rsp->length + 1) * sizeof(response_t),
+ &rsp->dma, GFP_KERNEL);
+ if (rsp->ring == NULL) {
+ ql_log(ql_log_warn, base_vha, 0x00e1,
+ "Failed to allocate memory for response ring.\n");
+ goto que_failed;
+ }
+
+ mutex_lock(&ha->vport_lock);
+ que_id = find_first_zero_bit(ha->rsp_qid_map, ha->max_rsp_queues);
+ if (que_id >= ha->max_rsp_queues) {
+ mutex_unlock(&ha->vport_lock);
+ ql_log(ql_log_warn, base_vha, 0x00e2,
+ "No resources to create additional request queue.\n");
+ goto que_failed;
+ }
+ set_bit(que_id, ha->rsp_qid_map);
+
+ if (ha->flags.msix_enabled)
+ rsp->msix = &ha->msix_entries[que_id + 1];
+ else
+ ql_log(ql_log_warn, base_vha, 0x00e3,
+ "MSIX not enalbled.\n");
+
+ ha->rsp_q_map[que_id] = rsp;
+ rsp->rid = rid;
+ rsp->vp_idx = vp_idx;
+ rsp->hw = ha;
+ ql_dbg(ql_dbg_init, base_vha, 0x00e4,
+ "queue_id=%d rid=%d vp_idx=%d hw=%p.\n",
+ que_id, rsp->rid, rsp->vp_idx, rsp->hw);
+ /* Use alternate PCI bus number */
+ if (MSB(rsp->rid))
+ options |= BIT_4;
+ /* Use alternate PCI devfn */
+ if (LSB(rsp->rid))
+ options |= BIT_5;
+ /* Enable MSIX handshake mode on for uncapable adapters */
+ if (!IS_MSIX_NACK_CAPABLE(ha))
+ options |= BIT_6;
+
+ rsp->options = options;
+ rsp->id = que_id;
+ reg = ISP_QUE_REG(ha, que_id);
+ rsp->rsp_q_in = &reg->isp25mq.rsp_q_in;
+ rsp->rsp_q_out = &reg->isp25mq.rsp_q_out;
+ mutex_unlock(&ha->vport_lock);
+ ql_dbg(ql_dbg_multiq, base_vha, 0xc00b,
+ "options=%x id=%d rsp_q_in=%p rsp_q_out=%p",
+ rsp->options, rsp->id, rsp->rsp_q_in,
+ rsp->rsp_q_out);
+ ql_dbg(ql_dbg_init, base_vha, 0x00e5,
+ "options=%x id=%d rsp_q_in=%p rsp_q_out=%p",
+ rsp->options, rsp->id, rsp->rsp_q_in,
+ rsp->rsp_q_out);
+
+ ret = qla25xx_request_irq(rsp);
+ if (ret)
+ goto que_failed;
+
+ ret = qla25xx_init_rsp_que(base_vha, rsp);
+ if (ret != QLA_SUCCESS) {
+ ql_log(ql_log_fatal, base_vha, 0x00e7,
+ "%s failed.\n", __func__);
+ mutex_lock(&ha->vport_lock);
+ clear_bit(que_id, ha->rsp_qid_map);
+ mutex_unlock(&ha->vport_lock);
+ goto que_failed;
+ }
+ if (req >= 0)
+ rsp->req = ha->req_q_map[req];
+ else
+ rsp->req = NULL;
+
+ qla2x00_init_response_q_entries(rsp);
+ if (rsp->hw->wq)
+ INIT_WORK(&rsp->q_work, qla_do_work);
+ return rsp->id;
+
+que_failed:
+ qla25xx_free_rsp_que(base_vha, rsp);
+failed:
+ return 0;
+}
diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c
new file mode 100644
index 00000000..de722a93
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_nx.c
@@ -0,0 +1,4428 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2003-2011 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#include "qla_def.h"
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/ratelimit.h>
+#include <linux/vmalloc.h>
+#include <scsi/scsi_tcq.h>
+
+#define MASK(n) ((1ULL<<(n))-1)
+#define MN_WIN(addr) (((addr & 0x1fc0000) >> 1) | \
+ ((addr >> 25) & 0x3ff))
+#define OCM_WIN(addr) (((addr & 0x1ff0000) >> 1) | \
+ ((addr >> 25) & 0x3ff))
+#define MS_WIN(addr) (addr & 0x0ffc0000)
+#define QLA82XX_PCI_MN_2M (0)
+#define QLA82XX_PCI_MS_2M (0x80000)
+#define QLA82XX_PCI_OCM0_2M (0xc0000)
+#define VALID_OCM_ADDR(addr) (((addr) & 0x3f800) != 0x3f800)
+#define GET_MEM_OFFS_2M(addr) (addr & MASK(18))
+#define BLOCK_PROTECT_BITS 0x0F
+
+/* CRB window related */
+#define CRB_BLK(off) ((off >> 20) & 0x3f)
+#define CRB_SUBBLK(off) ((off >> 16) & 0xf)
+#define CRB_WINDOW_2M (0x130060)
+#define QLA82XX_PCI_CAMQM_2M_END (0x04800800UL)
+#define CRB_HI(off) ((qla82xx_crb_hub_agt[CRB_BLK(off)] << 20) | \
+ ((off) & 0xf0000))
+#define QLA82XX_PCI_CAMQM_2M_BASE (0x000ff800UL)
+#define CRB_INDIRECT_2M (0x1e0000UL)
+
+#define MAX_CRB_XFORM 60
+static unsigned long crb_addr_xform[MAX_CRB_XFORM];
+int qla82xx_crb_table_initialized;
+
+#define qla82xx_crb_addr_transform(name) \
+ (crb_addr_xform[QLA82XX_HW_PX_MAP_CRB_##name] = \
+ QLA82XX_HW_CRB_HUB_AGT_ADR_##name << 20)
+
+static void qla82xx_crb_addr_transform_setup(void)
+{
+ qla82xx_crb_addr_transform(XDMA);
+ qla82xx_crb_addr_transform(TIMR);
+ qla82xx_crb_addr_transform(SRE);
+ qla82xx_crb_addr_transform(SQN3);
+ qla82xx_crb_addr_transform(SQN2);
+ qla82xx_crb_addr_transform(SQN1);
+ qla82xx_crb_addr_transform(SQN0);
+ qla82xx_crb_addr_transform(SQS3);
+ qla82xx_crb_addr_transform(SQS2);
+ qla82xx_crb_addr_transform(SQS1);
+ qla82xx_crb_addr_transform(SQS0);
+ qla82xx_crb_addr_transform(RPMX7);
+ qla82xx_crb_addr_transform(RPMX6);
+ qla82xx_crb_addr_transform(RPMX5);
+ qla82xx_crb_addr_transform(RPMX4);
+ qla82xx_crb_addr_transform(RPMX3);
+ qla82xx_crb_addr_transform(RPMX2);
+ qla82xx_crb_addr_transform(RPMX1);
+ qla82xx_crb_addr_transform(RPMX0);
+ qla82xx_crb_addr_transform(ROMUSB);
+ qla82xx_crb_addr_transform(SN);
+ qla82xx_crb_addr_transform(QMN);
+ qla82xx_crb_addr_transform(QMS);
+ qla82xx_crb_addr_transform(PGNI);
+ qla82xx_crb_addr_transform(PGND);
+ qla82xx_crb_addr_transform(PGN3);
+ qla82xx_crb_addr_transform(PGN2);
+ qla82xx_crb_addr_transform(PGN1);
+ qla82xx_crb_addr_transform(PGN0);
+ qla82xx_crb_addr_transform(PGSI);
+ qla82xx_crb_addr_transform(PGSD);
+ qla82xx_crb_addr_transform(PGS3);
+ qla82xx_crb_addr_transform(PGS2);
+ qla82xx_crb_addr_transform(PGS1);
+ qla82xx_crb_addr_transform(PGS0);
+ qla82xx_crb_addr_transform(PS);
+ qla82xx_crb_addr_transform(PH);
+ qla82xx_crb_addr_transform(NIU);
+ qla82xx_crb_addr_transform(I2Q);
+ qla82xx_crb_addr_transform(EG);
+ qla82xx_crb_addr_transform(MN);
+ qla82xx_crb_addr_transform(MS);
+ qla82xx_crb_addr_transform(CAS2);
+ qla82xx_crb_addr_transform(CAS1);
+ qla82xx_crb_addr_transform(CAS0);
+ qla82xx_crb_addr_transform(CAM);
+ qla82xx_crb_addr_transform(C2C1);
+ qla82xx_crb_addr_transform(C2C0);
+ qla82xx_crb_addr_transform(SMB);
+ qla82xx_crb_addr_transform(OCM0);
+ /*
+ * Used only in P3 just define it for P2 also.
+ */
+ qla82xx_crb_addr_transform(I2C0);
+
+ qla82xx_crb_table_initialized = 1;
+}
+
+struct crb_128M_2M_block_map crb_128M_2M_map[64] = {
+ {{{0, 0, 0, 0} } },
+ {{{1, 0x0100000, 0x0102000, 0x120000},
+ {1, 0x0110000, 0x0120000, 0x130000},
+ {1, 0x0120000, 0x0122000, 0x124000},
+ {1, 0x0130000, 0x0132000, 0x126000},
+ {1, 0x0140000, 0x0142000, 0x128000},
+ {1, 0x0150000, 0x0152000, 0x12a000},
+ {1, 0x0160000, 0x0170000, 0x110000},
+ {1, 0x0170000, 0x0172000, 0x12e000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {1, 0x01e0000, 0x01e0800, 0x122000},
+ {0, 0x0000000, 0x0000000, 0x000000} } } ,
+ {{{1, 0x0200000, 0x0210000, 0x180000} } },
+ {{{0, 0, 0, 0} } },
+ {{{1, 0x0400000, 0x0401000, 0x169000} } },
+ {{{1, 0x0500000, 0x0510000, 0x140000} } },
+ {{{1, 0x0600000, 0x0610000, 0x1c0000} } },
+ {{{1, 0x0700000, 0x0704000, 0x1b8000} } },
+ {{{1, 0x0800000, 0x0802000, 0x170000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {1, 0x08f0000, 0x08f2000, 0x172000} } },
+ {{{1, 0x0900000, 0x0902000, 0x174000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {1, 0x09f0000, 0x09f2000, 0x176000} } },
+ {{{0, 0x0a00000, 0x0a02000, 0x178000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {1, 0x0af0000, 0x0af2000, 0x17a000} } },
+ {{{0, 0x0b00000, 0x0b02000, 0x17c000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {1, 0x0bf0000, 0x0bf2000, 0x17e000} } },
+ {{{1, 0x0c00000, 0x0c04000, 0x1d4000} } },
+ {{{1, 0x0d00000, 0x0d04000, 0x1a4000} } },
+ {{{1, 0x0e00000, 0x0e04000, 0x1a0000} } },
+ {{{1, 0x0f00000, 0x0f01000, 0x164000} } },
+ {{{0, 0x1000000, 0x1004000, 0x1a8000} } },
+ {{{1, 0x1100000, 0x1101000, 0x160000} } },
+ {{{1, 0x1200000, 0x1201000, 0x161000} } },
+ {{{1, 0x1300000, 0x1301000, 0x162000} } },
+ {{{1, 0x1400000, 0x1401000, 0x163000} } },
+ {{{1, 0x1500000, 0x1501000, 0x165000} } },
+ {{{1, 0x1600000, 0x1601000, 0x166000} } },
+ {{{0, 0, 0, 0} } },
+ {{{0, 0, 0, 0} } },
+ {{{0, 0, 0, 0} } },
+ {{{0, 0, 0, 0} } },
+ {{{0, 0, 0, 0} } },
+ {{{0, 0, 0, 0} } },
+ {{{1, 0x1d00000, 0x1d10000, 0x190000} } },
+ {{{1, 0x1e00000, 0x1e01000, 0x16a000} } },
+ {{{1, 0x1f00000, 0x1f10000, 0x150000} } },
+ {{{0} } },
+ {{{1, 0x2100000, 0x2102000, 0x120000},
+ {1, 0x2110000, 0x2120000, 0x130000},
+ {1, 0x2120000, 0x2122000, 0x124000},
+ {1, 0x2130000, 0x2132000, 0x126000},
+ {1, 0x2140000, 0x2142000, 0x128000},
+ {1, 0x2150000, 0x2152000, 0x12a000},
+ {1, 0x2160000, 0x2170000, 0x110000},
+ {1, 0x2170000, 0x2172000, 0x12e000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000},
+ {0, 0x0000000, 0x0000000, 0x000000} } },
+ {{{1, 0x2200000, 0x2204000, 0x1b0000} } },
+ {{{0} } },
+ {{{0} } },
+ {{{0} } },
+ {{{0} } },
+ {{{0} } },
+ {{{1, 0x2800000, 0x2804000, 0x1a4000} } },
+ {{{1, 0x2900000, 0x2901000, 0x16b000} } },
+ {{{1, 0x2a00000, 0x2a00400, 0x1ac400} } },
+ {{{1, 0x2b00000, 0x2b00400, 0x1ac800} } },
+ {{{1, 0x2c00000, 0x2c00400, 0x1acc00} } },
+ {{{1, 0x2d00000, 0x2d00400, 0x1ad000} } },
+ {{{1, 0x2e00000, 0x2e00400, 0x1ad400} } },
+ {{{1, 0x2f00000, 0x2f00400, 0x1ad800} } },
+ {{{1, 0x3000000, 0x3000400, 0x1adc00} } },
+ {{{0, 0x3100000, 0x3104000, 0x1a8000} } },
+ {{{1, 0x3200000, 0x3204000, 0x1d4000} } },
+ {{{1, 0x3300000, 0x3304000, 0x1a0000} } },
+ {{{0} } },
+ {{{1, 0x3500000, 0x3500400, 0x1ac000} } },
+ {{{1, 0x3600000, 0x3600400, 0x1ae000} } },
+ {{{1, 0x3700000, 0x3700400, 0x1ae400} } },
+ {{{1, 0x3800000, 0x3804000, 0x1d0000} } },
+ {{{1, 0x3900000, 0x3904000, 0x1b4000} } },
+ {{{1, 0x3a00000, 0x3a04000, 0x1d8000} } },
+ {{{0} } },
+ {{{0} } },
+ {{{1, 0x3d00000, 0x3d04000, 0x1dc000} } },
+ {{{1, 0x3e00000, 0x3e01000, 0x167000} } },
+ {{{1, 0x3f00000, 0x3f01000, 0x168000} } }
+};
+
+/*
+ * top 12 bits of crb internal address (hub, agent)
+ */
+unsigned qla82xx_crb_hub_agt[64] = {
+ 0,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_PS,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_MN,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_MS,
+ 0,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_SRE,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_NIU,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_QMN,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_SQN0,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_SQN1,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_SQN2,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_SQN3,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_I2Q,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_TIMR,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_ROMUSB,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_PGN4,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_XDMA,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_PGN0,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_PGN1,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_PGN2,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_PGN3,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_PGND,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_PGNI,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_PGS0,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_PGS1,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_PGS2,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_PGS3,
+ 0,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_PGSI,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_SN,
+ 0,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_EG,
+ 0,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_PS,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_CAM,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_TIMR,
+ 0,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX1,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX2,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX3,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX4,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX5,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX6,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX7,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_XDMA,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_I2Q,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_ROMUSB,
+ 0,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX0,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX8,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX9,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_OCM0,
+ 0,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_SMB,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_I2C0,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_I2C1,
+ 0,
+ QLA82XX_HW_CRB_HUB_AGT_ADR_PGNC,
+ 0,
+};
+
+/* Device states */
+char *q_dev_state[] = {
+ "Unknown",
+ "Cold",
+ "Initializing",
+ "Ready",
+ "Need Reset",
+ "Need Quiescent",
+ "Failed",
+ "Quiescent",
+};
+
+char *qdev_state(uint32_t dev_state)
+{
+ return q_dev_state[dev_state];
+}
+
+/*
+ * In: 'off' is offset from CRB space in 128M pci map
+ * Out: 'off' is 2M pci map addr
+ * side effect: lock crb window
+ */
+static void
+qla82xx_pci_set_crbwindow_2M(struct qla_hw_data *ha, ulong *off)
+{
+ u32 win_read;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ ha->crb_win = CRB_HI(*off);
+ writel(ha->crb_win,
+ (void *)(CRB_WINDOW_2M + ha->nx_pcibase));
+
+ /* Read back value to make sure write has gone through before trying
+ * to use it.
+ */
+ win_read = RD_REG_DWORD((void *)(CRB_WINDOW_2M + ha->nx_pcibase));
+ if (win_read != ha->crb_win) {
+ ql_dbg(ql_dbg_p3p, vha, 0xb000,
+ "%s: Written crbwin (0x%x) "
+ "!= Read crbwin (0x%x), off=0x%lx.\n",
+ __func__, ha->crb_win, win_read, *off);
+ }
+ *off = (*off & MASK(16)) + CRB_INDIRECT_2M + ha->nx_pcibase;
+}
+
+static inline unsigned long
+qla82xx_pci_set_crbwindow(struct qla_hw_data *ha, u64 off)
+{
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+ /* See if we are currently pointing to the region we want to use next */
+ if ((off >= QLA82XX_CRB_PCIX_HOST) && (off < QLA82XX_CRB_DDR_NET)) {
+ /* No need to change window. PCIX and PCIEregs are in both
+ * regs are in both windows.
+ */
+ return off;
+ }
+
+ if ((off >= QLA82XX_CRB_PCIX_HOST) && (off < QLA82XX_CRB_PCIX_HOST2)) {
+ /* We are in first CRB window */
+ if (ha->curr_window != 0)
+ WARN_ON(1);
+ return off;
+ }
+
+ if ((off > QLA82XX_CRB_PCIX_HOST2) && (off < QLA82XX_CRB_MAX)) {
+ /* We are in second CRB window */
+ off = off - QLA82XX_CRB_PCIX_HOST2 + QLA82XX_CRB_PCIX_HOST;
+
+ if (ha->curr_window != 1)
+ return off;
+
+ /* We are in the QM or direct access
+ * register region - do nothing
+ */
+ if ((off >= QLA82XX_PCI_DIRECT_CRB) &&
+ (off < QLA82XX_PCI_CAMQM_MAX))
+ return off;
+ }
+ /* strange address given */
+ ql_dbg(ql_dbg_p3p, vha, 0xb001,
+ "%s: Warning: unm_nic_pci_set_crbwindow "
+ "called with an unknown address(%llx).\n",
+ QLA2XXX_DRIVER_NAME, off);
+ return off;
+}
+
+static int
+qla82xx_pci_get_crb_addr_2M(struct qla_hw_data *ha, ulong *off)
+{
+ struct crb_128M_2M_sub_block_map *m;
+
+ if (*off >= QLA82XX_CRB_MAX)
+ return -1;
+
+ if (*off >= QLA82XX_PCI_CAMQM && (*off < QLA82XX_PCI_CAMQM_2M_END)) {
+ *off = (*off - QLA82XX_PCI_CAMQM) +
+ QLA82XX_PCI_CAMQM_2M_BASE + ha->nx_pcibase;
+ return 0;
+ }
+
+ if (*off < QLA82XX_PCI_CRBSPACE)
+ return -1;
+
+ *off -= QLA82XX_PCI_CRBSPACE;
+
+ /* Try direct map */
+ m = &crb_128M_2M_map[CRB_BLK(*off)].sub_block[CRB_SUBBLK(*off)];
+
+ if (m->valid && (m->start_128M <= *off) && (m->end_128M > *off)) {
+ *off = *off + m->start_2M - m->start_128M + ha->nx_pcibase;
+ return 0;
+ }
+ /* Not in direct map, use crb window */
+ return 1;
+}
+
+#define CRB_WIN_LOCK_TIMEOUT 100000000
+static int qla82xx_crb_win_lock(struct qla_hw_data *ha)
+{
+ int done = 0, timeout = 0;
+
+ while (!done) {
+ /* acquire semaphore3 from PCI HW block */
+ done = qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM7_LOCK));
+ if (done == 1)
+ break;
+ if (timeout >= CRB_WIN_LOCK_TIMEOUT)
+ return -1;
+ timeout++;
+ }
+ qla82xx_wr_32(ha, QLA82XX_CRB_WIN_LOCK_ID, ha->portnum);
+ return 0;
+}
+
+int
+qla82xx_wr_32(struct qla_hw_data *ha, ulong off, u32 data)
+{
+ unsigned long flags = 0;
+ int rv;
+
+ rv = qla82xx_pci_get_crb_addr_2M(ha, &off);
+
+ BUG_ON(rv == -1);
+
+ if (rv == 1) {
+ write_lock_irqsave(&ha->hw_lock, flags);
+ qla82xx_crb_win_lock(ha);
+ qla82xx_pci_set_crbwindow_2M(ha, &off);
+ }
+
+ writel(data, (void __iomem *)off);
+
+ if (rv == 1) {
+ qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM7_UNLOCK));
+ write_unlock_irqrestore(&ha->hw_lock, flags);
+ }
+ return 0;
+}
+
+int
+qla82xx_rd_32(struct qla_hw_data *ha, ulong off)
+{
+ unsigned long flags = 0;
+ int rv;
+ u32 data;
+
+ rv = qla82xx_pci_get_crb_addr_2M(ha, &off);
+
+ BUG_ON(rv == -1);
+
+ if (rv == 1) {
+ write_lock_irqsave(&ha->hw_lock, flags);
+ qla82xx_crb_win_lock(ha);
+ qla82xx_pci_set_crbwindow_2M(ha, &off);
+ }
+ data = RD_REG_DWORD((void __iomem *)off);
+
+ if (rv == 1) {
+ qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM7_UNLOCK));
+ write_unlock_irqrestore(&ha->hw_lock, flags);
+ }
+ return data;
+}
+
+#define IDC_LOCK_TIMEOUT 100000000
+int qla82xx_idc_lock(struct qla_hw_data *ha)
+{
+ int i;
+ int done = 0, timeout = 0;
+
+ while (!done) {
+ /* acquire semaphore5 from PCI HW block */
+ done = qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM5_LOCK));
+ if (done == 1)
+ break;
+ if (timeout >= IDC_LOCK_TIMEOUT)
+ return -1;
+
+ timeout++;
+
+ /* Yield CPU */
+ if (!in_interrupt())
+ schedule();
+ else {
+ for (i = 0; i < 20; i++)
+ cpu_relax();
+ }
+ }
+
+ return 0;
+}
+
+void qla82xx_idc_unlock(struct qla_hw_data *ha)
+{
+ qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM5_UNLOCK));
+}
+
+/* PCI Windowing for DDR regions. */
+#define QLA82XX_ADDR_IN_RANGE(addr, low, high) \
+ (((addr) <= (high)) && ((addr) >= (low)))
+/*
+ * check memory access boundary.
+ * used by test agent. support ddr access only for now
+ */
+static unsigned long
+qla82xx_pci_mem_bound_check(struct qla_hw_data *ha,
+ unsigned long long addr, int size)
+{
+ if (!QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_DDR_NET,
+ QLA82XX_ADDR_DDR_NET_MAX) ||
+ !QLA82XX_ADDR_IN_RANGE(addr + size - 1, QLA82XX_ADDR_DDR_NET,
+ QLA82XX_ADDR_DDR_NET_MAX) ||
+ ((size != 1) && (size != 2) && (size != 4) && (size != 8)))
+ return 0;
+ else
+ return 1;
+}
+
+int qla82xx_pci_set_window_warning_count;
+
+static unsigned long
+qla82xx_pci_set_window(struct qla_hw_data *ha, unsigned long long addr)
+{
+ int window;
+ u32 win_read;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_DDR_NET,
+ QLA82XX_ADDR_DDR_NET_MAX)) {
+ /* DDR network side */
+ window = MN_WIN(addr);
+ ha->ddr_mn_window = window;
+ qla82xx_wr_32(ha,
+ ha->mn_win_crb | QLA82XX_PCI_CRBSPACE, window);
+ win_read = qla82xx_rd_32(ha,
+ ha->mn_win_crb | QLA82XX_PCI_CRBSPACE);
+ if ((win_read << 17) != window) {
+ ql_dbg(ql_dbg_p3p, vha, 0xb003,
+ "%s: Written MNwin (0x%x) != Read MNwin (0x%x).\n",
+ __func__, window, win_read);
+ }
+ addr = GET_MEM_OFFS_2M(addr) + QLA82XX_PCI_DDR_NET;
+ } else if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_OCM0,
+ QLA82XX_ADDR_OCM0_MAX)) {
+ unsigned int temp1;
+ if ((addr & 0x00ff800) == 0xff800) {
+ ql_log(ql_log_warn, vha, 0xb004,
+ "%s: QM access not handled.\n", __func__);
+ addr = -1UL;
+ }
+ window = OCM_WIN(addr);
+ ha->ddr_mn_window = window;
+ qla82xx_wr_32(ha,
+ ha->mn_win_crb | QLA82XX_PCI_CRBSPACE, window);
+ win_read = qla82xx_rd_32(ha,
+ ha->mn_win_crb | QLA82XX_PCI_CRBSPACE);
+ temp1 = ((window & 0x1FF) << 7) |
+ ((window & 0x0FFFE0000) >> 17);
+ if (win_read != temp1) {
+ ql_log(ql_log_warn, vha, 0xb005,
+ "%s: Written OCMwin (0x%x) != Read OCMwin (0x%x).\n",
+ __func__, temp1, win_read);
+ }
+ addr = GET_MEM_OFFS_2M(addr) + QLA82XX_PCI_OCM0_2M;
+
+ } else if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_QDR_NET,
+ QLA82XX_P3_ADDR_QDR_NET_MAX)) {
+ /* QDR network side */
+ window = MS_WIN(addr);
+ ha->qdr_sn_window = window;
+ qla82xx_wr_32(ha,
+ ha->ms_win_crb | QLA82XX_PCI_CRBSPACE, window);
+ win_read = qla82xx_rd_32(ha,
+ ha->ms_win_crb | QLA82XX_PCI_CRBSPACE);
+ if (win_read != window) {
+ ql_log(ql_log_warn, vha, 0xb006,
+ "%s: Written MSwin (0x%x) != Read MSwin (0x%x).\n",
+ __func__, window, win_read);
+ }
+ addr = GET_MEM_OFFS_2M(addr) + QLA82XX_PCI_QDR_NET;
+ } else {
+ /*
+ * peg gdb frequently accesses memory that doesn't exist,
+ * this limits the chit chat so debugging isn't slowed down.
+ */
+ if ((qla82xx_pci_set_window_warning_count++ < 8) ||
+ (qla82xx_pci_set_window_warning_count%64 == 0)) {
+ ql_log(ql_log_warn, vha, 0xb007,
+ "%s: Warning:%s Unknown address range!.\n",
+ __func__, QLA2XXX_DRIVER_NAME);
+ }
+ addr = -1UL;
+ }
+ return addr;
+}
+
+/* check if address is in the same windows as the previous access */
+static int qla82xx_pci_is_same_window(struct qla_hw_data *ha,
+ unsigned long long addr)
+{
+ int window;
+ unsigned long long qdr_max;
+
+ qdr_max = QLA82XX_P3_ADDR_QDR_NET_MAX;
+
+ /* DDR network side */
+ if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_DDR_NET,
+ QLA82XX_ADDR_DDR_NET_MAX))
+ BUG();
+ else if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_OCM0,
+ QLA82XX_ADDR_OCM0_MAX))
+ return 1;
+ else if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_OCM1,
+ QLA82XX_ADDR_OCM1_MAX))
+ return 1;
+ else if (QLA82XX_ADDR_IN_RANGE(addr, QLA82XX_ADDR_QDR_NET, qdr_max)) {
+ /* QDR network side */
+ window = ((addr - QLA82XX_ADDR_QDR_NET) >> 22) & 0x3f;
+ if (ha->qdr_sn_window == window)
+ return 1;
+ }
+ return 0;
+}
+
+static int qla82xx_pci_mem_read_direct(struct qla_hw_data *ha,
+ u64 off, void *data, int size)
+{
+ unsigned long flags;
+ void *addr = NULL;
+ int ret = 0;
+ u64 start;
+ uint8_t *mem_ptr = NULL;
+ unsigned long mem_base;
+ unsigned long mem_page;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ write_lock_irqsave(&ha->hw_lock, flags);
+
+ /*
+ * If attempting to access unknown address or straddle hw windows,
+ * do not access.
+ */
+ start = qla82xx_pci_set_window(ha, off);
+ if ((start == -1UL) ||
+ (qla82xx_pci_is_same_window(ha, off + size - 1) == 0)) {
+ write_unlock_irqrestore(&ha->hw_lock, flags);
+ ql_log(ql_log_fatal, vha, 0xb008,
+ "%s out of bound pci memory "
+ "access, offset is 0x%llx.\n",
+ QLA2XXX_DRIVER_NAME, off);
+ return -1;
+ }
+
+ write_unlock_irqrestore(&ha->hw_lock, flags);
+ mem_base = pci_resource_start(ha->pdev, 0);
+ mem_page = start & PAGE_MASK;
+ /* Map two pages whenever user tries to access addresses in two
+ * consecutive pages.
+ */
+ if (mem_page != ((start + size - 1) & PAGE_MASK))
+ mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE * 2);
+ else
+ mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE);
+ if (mem_ptr == 0UL) {
+ *(u8 *)data = 0;
+ return -1;
+ }
+ addr = mem_ptr;
+ addr += start & (PAGE_SIZE - 1);
+ write_lock_irqsave(&ha->hw_lock, flags);
+
+ switch (size) {
+ case 1:
+ *(u8 *)data = readb(addr);
+ break;
+ case 2:
+ *(u16 *)data = readw(addr);
+ break;
+ case 4:
+ *(u32 *)data = readl(addr);
+ break;
+ case 8:
+ *(u64 *)data = readq(addr);
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+ write_unlock_irqrestore(&ha->hw_lock, flags);
+
+ if (mem_ptr)
+ iounmap(mem_ptr);
+ return ret;
+}
+
+static int
+qla82xx_pci_mem_write_direct(struct qla_hw_data *ha,
+ u64 off, void *data, int size)
+{
+ unsigned long flags;
+ void *addr = NULL;
+ int ret = 0;
+ u64 start;
+ uint8_t *mem_ptr = NULL;
+ unsigned long mem_base;
+ unsigned long mem_page;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ write_lock_irqsave(&ha->hw_lock, flags);
+
+ /*
+ * If attempting to access unknown address or straddle hw windows,
+ * do not access.
+ */
+ start = qla82xx_pci_set_window(ha, off);
+ if ((start == -1UL) ||
+ (qla82xx_pci_is_same_window(ha, off + size - 1) == 0)) {
+ write_unlock_irqrestore(&ha->hw_lock, flags);
+ ql_log(ql_log_fatal, vha, 0xb009,
+ "%s out of bount memory "
+ "access, offset is 0x%llx.\n",
+ QLA2XXX_DRIVER_NAME, off);
+ return -1;
+ }
+
+ write_unlock_irqrestore(&ha->hw_lock, flags);
+ mem_base = pci_resource_start(ha->pdev, 0);
+ mem_page = start & PAGE_MASK;
+ /* Map two pages whenever user tries to access addresses in two
+ * consecutive pages.
+ */
+ if (mem_page != ((start + size - 1) & PAGE_MASK))
+ mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE*2);
+ else
+ mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE);
+ if (mem_ptr == 0UL)
+ return -1;
+
+ addr = mem_ptr;
+ addr += start & (PAGE_SIZE - 1);
+ write_lock_irqsave(&ha->hw_lock, flags);
+
+ switch (size) {
+ case 1:
+ writeb(*(u8 *)data, addr);
+ break;
+ case 2:
+ writew(*(u16 *)data, addr);
+ break;
+ case 4:
+ writel(*(u32 *)data, addr);
+ break;
+ case 8:
+ writeq(*(u64 *)data, addr);
+ break;
+ default:
+ ret = -1;
+ break;
+ }
+ write_unlock_irqrestore(&ha->hw_lock, flags);
+ if (mem_ptr)
+ iounmap(mem_ptr);
+ return ret;
+}
+
+#define MTU_FUDGE_FACTOR 100
+static unsigned long
+qla82xx_decode_crb_addr(unsigned long addr)
+{
+ int i;
+ unsigned long base_addr, offset, pci_base;
+
+ if (!qla82xx_crb_table_initialized)
+ qla82xx_crb_addr_transform_setup();
+
+ pci_base = ADDR_ERROR;
+ base_addr = addr & 0xfff00000;
+ offset = addr & 0x000fffff;
+
+ for (i = 0; i < MAX_CRB_XFORM; i++) {
+ if (crb_addr_xform[i] == base_addr) {
+ pci_base = i << 20;
+ break;
+ }
+ }
+ if (pci_base == ADDR_ERROR)
+ return pci_base;
+ return pci_base + offset;
+}
+
+static long rom_max_timeout = 100;
+static long qla82xx_rom_lock_timeout = 100;
+
+static int
+qla82xx_rom_lock(struct qla_hw_data *ha)
+{
+ int done = 0, timeout = 0;
+
+ while (!done) {
+ /* acquire semaphore2 from PCI HW block */
+ done = qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM2_LOCK));
+ if (done == 1)
+ break;
+ if (timeout >= qla82xx_rom_lock_timeout)
+ return -1;
+ timeout++;
+ }
+ qla82xx_wr_32(ha, QLA82XX_ROM_LOCK_ID, ROM_LOCK_DRIVER);
+ return 0;
+}
+
+static void
+qla82xx_rom_unlock(struct qla_hw_data *ha)
+{
+ qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM2_UNLOCK));
+}
+
+static int
+qla82xx_wait_rom_busy(struct qla_hw_data *ha)
+{
+ long timeout = 0;
+ long done = 0 ;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ while (done == 0) {
+ done = qla82xx_rd_32(ha, QLA82XX_ROMUSB_GLB_STATUS);
+ done &= 4;
+ timeout++;
+ if (timeout >= rom_max_timeout) {
+ ql_dbg(ql_dbg_p3p, vha, 0xb00a,
+ "%s: Timeout reached waiting for rom busy.\n",
+ QLA2XXX_DRIVER_NAME);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int
+qla82xx_wait_rom_done(struct qla_hw_data *ha)
+{
+ long timeout = 0;
+ long done = 0 ;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ while (done == 0) {
+ done = qla82xx_rd_32(ha, QLA82XX_ROMUSB_GLB_STATUS);
+ done &= 2;
+ timeout++;
+ if (timeout >= rom_max_timeout) {
+ ql_dbg(ql_dbg_p3p, vha, 0xb00b,
+ "%s: Timeout reached waiting for rom done.\n",
+ QLA2XXX_DRIVER_NAME);
+ return -1;
+ }
+ }
+ return 0;
+}
+
+int
+qla82xx_md_rw_32(struct qla_hw_data *ha, uint32_t off, u32 data, uint8_t flag)
+{
+ uint32_t off_value, rval = 0;
+
+ WRT_REG_DWORD((void *)(CRB_WINDOW_2M + ha->nx_pcibase),
+ (off & 0xFFFF0000));
+
+ /* Read back value to make sure write has gone through */
+ RD_REG_DWORD((void *)(CRB_WINDOW_2M + ha->nx_pcibase));
+ off_value = (off & 0x0000FFFF);
+
+ if (flag)
+ WRT_REG_DWORD((void *)
+ (off_value + CRB_INDIRECT_2M + ha->nx_pcibase),
+ data);
+ else
+ rval = RD_REG_DWORD((void *)
+ (off_value + CRB_INDIRECT_2M + ha->nx_pcibase));
+
+ return rval;
+}
+
+static int
+qla82xx_do_rom_fast_read(struct qla_hw_data *ha, int addr, int *valp)
+{
+ /* Dword reads to flash. */
+ qla82xx_md_rw_32(ha, MD_DIRECT_ROM_WINDOW, (addr & 0xFFFF0000), 1);
+ *valp = qla82xx_md_rw_32(ha, MD_DIRECT_ROM_READ_BASE +
+ (addr & 0x0000FFFF), 0, 0);
+
+ return 0;
+}
+
+static int
+qla82xx_rom_fast_read(struct qla_hw_data *ha, int addr, int *valp)
+{
+ int ret, loops = 0;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ while ((qla82xx_rom_lock(ha) != 0) && (loops < 50000)) {
+ udelay(100);
+ schedule();
+ loops++;
+ }
+ if (loops >= 50000) {
+ ql_log(ql_log_fatal, vha, 0x00b9,
+ "Failed to aquire SEM2 lock.\n");
+ return -1;
+ }
+ ret = qla82xx_do_rom_fast_read(ha, addr, valp);
+ qla82xx_rom_unlock(ha);
+ return ret;
+}
+
+static int
+qla82xx_read_status_reg(struct qla_hw_data *ha, uint32_t *val)
+{
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+ qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_RDSR);
+ qla82xx_wait_rom_busy(ha);
+ if (qla82xx_wait_rom_done(ha)) {
+ ql_log(ql_log_warn, vha, 0xb00c,
+ "Error waiting for rom done.\n");
+ return -1;
+ }
+ *val = qla82xx_rd_32(ha, QLA82XX_ROMUSB_ROM_RDATA);
+ return 0;
+}
+
+static int
+qla82xx_flash_wait_write_finish(struct qla_hw_data *ha)
+{
+ long timeout = 0;
+ uint32_t done = 1 ;
+ uint32_t val;
+ int ret = 0;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ABYTE_CNT, 0);
+ while ((done != 0) && (ret == 0)) {
+ ret = qla82xx_read_status_reg(ha, &val);
+ done = val & 1;
+ timeout++;
+ udelay(10);
+ cond_resched();
+ if (timeout >= 50000) {
+ ql_log(ql_log_warn, vha, 0xb00d,
+ "Timeout reached waiting for write finish.\n");
+ return -1;
+ }
+ }
+ return ret;
+}
+
+static int
+qla82xx_flash_set_write_enable(struct qla_hw_data *ha)
+{
+ uint32_t val;
+ qla82xx_wait_rom_busy(ha);
+ qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ABYTE_CNT, 0);
+ qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_WREN);
+ qla82xx_wait_rom_busy(ha);
+ if (qla82xx_wait_rom_done(ha))
+ return -1;
+ if (qla82xx_read_status_reg(ha, &val) != 0)
+ return -1;
+ if ((val & 2) != 2)
+ return -1;
+ return 0;
+}
+
+static int
+qla82xx_write_status_reg(struct qla_hw_data *ha, uint32_t val)
+{
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+ if (qla82xx_flash_set_write_enable(ha))
+ return -1;
+ qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_WDATA, val);
+ qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, 0x1);
+ if (qla82xx_wait_rom_done(ha)) {
+ ql_log(ql_log_warn, vha, 0xb00e,
+ "Error waiting for rom done.\n");
+ return -1;
+ }
+ return qla82xx_flash_wait_write_finish(ha);
+}
+
+static int
+qla82xx_write_disable_flash(struct qla_hw_data *ha)
+{
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+ qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_WRDI);
+ if (qla82xx_wait_rom_done(ha)) {
+ ql_log(ql_log_warn, vha, 0xb00f,
+ "Error waiting for rom done.\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+ql82xx_rom_lock_d(struct qla_hw_data *ha)
+{
+ int loops = 0;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ while ((qla82xx_rom_lock(ha) != 0) && (loops < 50000)) {
+ udelay(100);
+ cond_resched();
+ loops++;
+ }
+ if (loops >= 50000) {
+ ql_log(ql_log_warn, vha, 0xb010,
+ "ROM lock failed.\n");
+ return -1;
+ }
+ return 0;
+}
+
+static int
+qla82xx_write_flash_dword(struct qla_hw_data *ha, uint32_t flashaddr,
+ uint32_t data)
+{
+ int ret = 0;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ ret = ql82xx_rom_lock_d(ha);
+ if (ret < 0) {
+ ql_log(ql_log_warn, vha, 0xb011,
+ "ROM lock failed.\n");
+ return ret;
+ }
+
+ if (qla82xx_flash_set_write_enable(ha))
+ goto done_write;
+
+ qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_WDATA, data);
+ qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ADDRESS, flashaddr);
+ qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ABYTE_CNT, 3);
+ qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_PP);
+ qla82xx_wait_rom_busy(ha);
+ if (qla82xx_wait_rom_done(ha)) {
+ ql_log(ql_log_warn, vha, 0xb012,
+ "Error waiting for rom done.\n");
+ ret = -1;
+ goto done_write;
+ }
+
+ ret = qla82xx_flash_wait_write_finish(ha);
+
+done_write:
+ qla82xx_rom_unlock(ha);
+ return ret;
+}
+
+/* This routine does CRB initialize sequence
+ * to put the ISP into operational state
+ */
+static int
+qla82xx_pinit_from_rom(scsi_qla_host_t *vha)
+{
+ int addr, val;
+ int i ;
+ struct crb_addr_pair *buf;
+ unsigned long off;
+ unsigned offset, n;
+ struct qla_hw_data *ha = vha->hw;
+
+ struct crb_addr_pair {
+ long addr;
+ long data;
+ };
+
+ /* Halt all the indiviual PEGs and other blocks of the ISP */
+ qla82xx_rom_lock(ha);
+
+ /* disable all I2Q */
+ qla82xx_wr_32(ha, QLA82XX_CRB_I2Q + 0x10, 0x0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_I2Q + 0x14, 0x0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_I2Q + 0x18, 0x0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_I2Q + 0x1c, 0x0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_I2Q + 0x20, 0x0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_I2Q + 0x24, 0x0);
+
+ /* disable all niu interrupts */
+ qla82xx_wr_32(ha, QLA82XX_CRB_NIU + 0x40, 0xff);
+ /* disable xge rx/tx */
+ qla82xx_wr_32(ha, QLA82XX_CRB_NIU + 0x70000, 0x00);
+ /* disable xg1 rx/tx */
+ qla82xx_wr_32(ha, QLA82XX_CRB_NIU + 0x80000, 0x00);
+ /* disable sideband mac */
+ qla82xx_wr_32(ha, QLA82XX_CRB_NIU + 0x90000, 0x00);
+ /* disable ap0 mac */
+ qla82xx_wr_32(ha, QLA82XX_CRB_NIU + 0xa0000, 0x00);
+ /* disable ap1 mac */
+ qla82xx_wr_32(ha, QLA82XX_CRB_NIU + 0xb0000, 0x00);
+
+ /* halt sre */
+ val = qla82xx_rd_32(ha, QLA82XX_CRB_SRE + 0x1000);
+ qla82xx_wr_32(ha, QLA82XX_CRB_SRE + 0x1000, val & (~(0x1)));
+
+ /* halt epg */
+ qla82xx_wr_32(ha, QLA82XX_CRB_EPG + 0x1300, 0x1);
+
+ /* halt timers */
+ qla82xx_wr_32(ha, QLA82XX_CRB_TIMER + 0x0, 0x0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_TIMER + 0x8, 0x0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_TIMER + 0x10, 0x0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_TIMER + 0x18, 0x0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_TIMER + 0x100, 0x0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_TIMER + 0x200, 0x0);
+
+ /* halt pegs */
+ qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_0 + 0x3c, 1);
+ qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_1 + 0x3c, 1);
+ qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_2 + 0x3c, 1);
+ qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_3 + 0x3c, 1);
+ qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_4 + 0x3c, 1);
+ msleep(20);
+
+ /* big hammer */
+ if (test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags))
+ /* don't reset CAM block on reset */
+ qla82xx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, 0xfeffffff);
+ else
+ qla82xx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, 0xffffffff);
+ qla82xx_rom_unlock(ha);
+
+ /* Read the signature value from the flash.
+ * Offset 0: Contain signature (0xcafecafe)
+ * Offset 4: Offset and number of addr/value pairs
+ * that present in CRB initialize sequence
+ */
+ if (qla82xx_rom_fast_read(ha, 0, &n) != 0 || n != 0xcafecafeUL ||
+ qla82xx_rom_fast_read(ha, 4, &n) != 0) {
+ ql_log(ql_log_fatal, vha, 0x006e,
+ "Error Reading crb_init area: n: %08x.\n", n);
+ return -1;
+ }
+
+ /* Offset in flash = lower 16 bits
+ * Number of enteries = upper 16 bits
+ */
+ offset = n & 0xffffU;
+ n = (n >> 16) & 0xffffU;
+
+ /* number of addr/value pair should not exceed 1024 enteries */
+ if (n >= 1024) {
+ ql_log(ql_log_fatal, vha, 0x0071,
+ "Card flash not initialized:n=0x%x.\n", n);
+ return -1;
+ }
+
+ ql_log(ql_log_info, vha, 0x0072,
+ "%d CRB init values found in ROM.\n", n);
+
+ buf = kmalloc(n * sizeof(struct crb_addr_pair), GFP_KERNEL);
+ if (buf == NULL) {
+ ql_log(ql_log_fatal, vha, 0x010c,
+ "Unable to allocate memory.\n");
+ return -1;
+ }
+
+ for (i = 0; i < n; i++) {
+ if (qla82xx_rom_fast_read(ha, 8*i + 4*offset, &val) != 0 ||
+ qla82xx_rom_fast_read(ha, 8*i + 4*offset + 4, &addr) != 0) {
+ kfree(buf);
+ return -1;
+ }
+
+ buf[i].addr = addr;
+ buf[i].data = val;
+ }
+
+ for (i = 0; i < n; i++) {
+ /* Translate internal CRB initialization
+ * address to PCI bus address
+ */
+ off = qla82xx_decode_crb_addr((unsigned long)buf[i].addr) +
+ QLA82XX_PCI_CRBSPACE;
+ /* Not all CRB addr/value pair to be written,
+ * some of them are skipped
+ */
+
+ /* skipping cold reboot MAGIC */
+ if (off == QLA82XX_CAM_RAM(0x1fc))
+ continue;
+
+ /* do not reset PCI */
+ if (off == (ROMUSB_GLB + 0xbc))
+ continue;
+
+ /* skip core clock, so that firmware can increase the clock */
+ if (off == (ROMUSB_GLB + 0xc8))
+ continue;
+
+ /* skip the function enable register */
+ if (off == QLA82XX_PCIE_REG(PCIE_SETUP_FUNCTION))
+ continue;
+
+ if (off == QLA82XX_PCIE_REG(PCIE_SETUP_FUNCTION2))
+ continue;
+
+ if ((off & 0x0ff00000) == QLA82XX_CRB_SMB)
+ continue;
+
+ if ((off & 0x0ff00000) == QLA82XX_CRB_DDR_NET)
+ continue;
+
+ if (off == ADDR_ERROR) {
+ ql_log(ql_log_fatal, vha, 0x0116,
+ "Unknow addr: 0x%08lx.\n", buf[i].addr);
+ continue;
+ }
+
+ qla82xx_wr_32(ha, off, buf[i].data);
+
+ /* ISP requires much bigger delay to settle down,
+ * else crb_window returns 0xffffffff
+ */
+ if (off == QLA82XX_ROMUSB_GLB_SW_RESET)
+ msleep(1000);
+
+ /* ISP requires millisec delay between
+ * successive CRB register updation
+ */
+ msleep(1);
+ }
+
+ kfree(buf);
+
+ /* Resetting the data and instruction cache */
+ qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_D+0xec, 0x1e);
+ qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_D+0x4c, 8);
+ qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_I+0x4c, 8);
+
+ /* Clear all protocol processing engines */
+ qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_0+0x8, 0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_0+0xc, 0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_1+0x8, 0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_1+0xc, 0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_2+0x8, 0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_2+0xc, 0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_3+0x8, 0);
+ qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_3+0xc, 0);
+ return 0;
+}
+
+static int
+qla82xx_pci_mem_write_2M(struct qla_hw_data *ha,
+ u64 off, void *data, int size)
+{
+ int i, j, ret = 0, loop, sz[2], off0;
+ int scale, shift_amount, startword;
+ uint32_t temp;
+ uint64_t off8, mem_crb, tmpw, word[2] = {0, 0};
+
+ /*
+ * If not MN, go check for MS or invalid.
+ */
+ if (off >= QLA82XX_ADDR_QDR_NET && off <= QLA82XX_P3_ADDR_QDR_NET_MAX)
+ mem_crb = QLA82XX_CRB_QDR_NET;
+ else {
+ mem_crb = QLA82XX_CRB_DDR_NET;
+ if (qla82xx_pci_mem_bound_check(ha, off, size) == 0)
+ return qla82xx_pci_mem_write_direct(ha,
+ off, data, size);
+ }
+
+ off0 = off & 0x7;
+ sz[0] = (size < (8 - off0)) ? size : (8 - off0);
+ sz[1] = size - sz[0];
+
+ off8 = off & 0xfffffff0;
+ loop = (((off & 0xf) + size - 1) >> 4) + 1;
+ shift_amount = 4;
+ scale = 2;
+ startword = (off & 0xf)/8;
+
+ for (i = 0; i < loop; i++) {
+ if (qla82xx_pci_mem_read_2M(ha, off8 +
+ (i << shift_amount), &word[i * scale], 8))
+ return -1;
+ }
+
+ switch (size) {
+ case 1:
+ tmpw = *((uint8_t *)data);
+ break;
+ case 2:
+ tmpw = *((uint16_t *)data);
+ break;
+ case 4:
+ tmpw = *((uint32_t *)data);
+ break;
+ case 8:
+ default:
+ tmpw = *((uint64_t *)data);
+ break;
+ }
+
+ if (sz[0] == 8) {
+ word[startword] = tmpw;
+ } else {
+ word[startword] &=
+ ~((~(~0ULL << (sz[0] * 8))) << (off0 * 8));
+ word[startword] |= tmpw << (off0 * 8);
+ }
+ if (sz[1] != 0) {
+ word[startword+1] &= ~(~0ULL << (sz[1] * 8));
+ word[startword+1] |= tmpw >> (sz[0] * 8);
+ }
+
+ for (i = 0; i < loop; i++) {
+ temp = off8 + (i << shift_amount);
+ qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_ADDR_LO, temp);
+ temp = 0;
+ qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_ADDR_HI, temp);
+ temp = word[i * scale] & 0xffffffff;
+ qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_WRDATA_LO, temp);
+ temp = (word[i * scale] >> 32) & 0xffffffff;
+ qla82xx_wr_32(ha, mem_crb+MIU_TEST_AGT_WRDATA_HI, temp);
+ temp = word[i*scale + 1] & 0xffffffff;
+ qla82xx_wr_32(ha, mem_crb +
+ MIU_TEST_AGT_WRDATA_UPPER_LO, temp);
+ temp = (word[i*scale + 1] >> 32) & 0xffffffff;
+ qla82xx_wr_32(ha, mem_crb +
+ MIU_TEST_AGT_WRDATA_UPPER_HI, temp);
+
+ temp = MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE;
+ qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_CTRL, temp);
+ temp = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE | MIU_TA_CTL_WRITE;
+ qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_CTRL, temp);
+
+ for (j = 0; j < MAX_CTL_CHECK; j++) {
+ temp = qla82xx_rd_32(ha, mem_crb + MIU_TEST_AGT_CTRL);
+ if ((temp & MIU_TA_CTL_BUSY) == 0)
+ break;
+ }
+
+ if (j >= MAX_CTL_CHECK) {
+ if (printk_ratelimit())
+ dev_err(&ha->pdev->dev,
+ "failed to write through agent.\n");
+ ret = -1;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int
+qla82xx_fw_load_from_flash(struct qla_hw_data *ha)
+{
+ int i;
+ long size = 0;
+ long flashaddr = ha->flt_region_bootload << 2;
+ long memaddr = BOOTLD_START;
+ u64 data;
+ u32 high, low;
+ size = (IMAGE_START - BOOTLD_START) / 8;
+
+ for (i = 0; i < size; i++) {
+ if ((qla82xx_rom_fast_read(ha, flashaddr, (int *)&low)) ||
+ (qla82xx_rom_fast_read(ha, flashaddr + 4, (int *)&high))) {
+ return -1;
+ }
+ data = ((u64)high << 32) | low ;
+ qla82xx_pci_mem_write_2M(ha, memaddr, &data, 8);
+ flashaddr += 8;
+ memaddr += 8;
+
+ if (i % 0x1000 == 0)
+ msleep(1);
+ }
+ udelay(100);
+ read_lock(&ha->hw_lock);
+ qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_0 + 0x18, 0x1020);
+ qla82xx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, 0x80001e);
+ read_unlock(&ha->hw_lock);
+ return 0;
+}
+
+int
+qla82xx_pci_mem_read_2M(struct qla_hw_data *ha,
+ u64 off, void *data, int size)
+{
+ int i, j = 0, k, start, end, loop, sz[2], off0[2];
+ int shift_amount;
+ uint32_t temp;
+ uint64_t off8, val, mem_crb, word[2] = {0, 0};
+
+ /*
+ * If not MN, go check for MS or invalid.
+ */
+
+ if (off >= QLA82XX_ADDR_QDR_NET && off <= QLA82XX_P3_ADDR_QDR_NET_MAX)
+ mem_crb = QLA82XX_CRB_QDR_NET;
+ else {
+ mem_crb = QLA82XX_CRB_DDR_NET;
+ if (qla82xx_pci_mem_bound_check(ha, off, size) == 0)
+ return qla82xx_pci_mem_read_direct(ha,
+ off, data, size);
+ }
+
+ off8 = off & 0xfffffff0;
+ off0[0] = off & 0xf;
+ sz[0] = (size < (16 - off0[0])) ? size : (16 - off0[0]);
+ shift_amount = 4;
+ loop = ((off0[0] + size - 1) >> shift_amount) + 1;
+ off0[1] = 0;
+ sz[1] = size - sz[0];
+
+ for (i = 0; i < loop; i++) {
+ temp = off8 + (i << shift_amount);
+ qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_ADDR_LO, temp);
+ temp = 0;
+ qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_ADDR_HI, temp);
+ temp = MIU_TA_CTL_ENABLE;
+ qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_CTRL, temp);
+ temp = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE;
+ qla82xx_wr_32(ha, mem_crb + MIU_TEST_AGT_CTRL, temp);
+
+ for (j = 0; j < MAX_CTL_CHECK; j++) {
+ temp = qla82xx_rd_32(ha, mem_crb + MIU_TEST_AGT_CTRL);
+ if ((temp & MIU_TA_CTL_BUSY) == 0)
+ break;
+ }
+
+ if (j >= MAX_CTL_CHECK) {
+ if (printk_ratelimit())
+ dev_err(&ha->pdev->dev,
+ "failed to read through agent.\n");
+ break;
+ }
+
+ start = off0[i] >> 2;
+ end = (off0[i] + sz[i] - 1) >> 2;
+ for (k = start; k <= end; k++) {
+ temp = qla82xx_rd_32(ha,
+ mem_crb + MIU_TEST_AGT_RDDATA(k));
+ word[i] |= ((uint64_t)temp << (32 * (k & 1)));
+ }
+ }
+
+ if (j >= MAX_CTL_CHECK)
+ return -1;
+
+ if ((off0[0] & 7) == 0) {
+ val = word[0];
+ } else {
+ val = ((word[0] >> (off0[0] * 8)) & (~(~0ULL << (sz[0] * 8)))) |
+ ((word[1] & (~(~0ULL << (sz[1] * 8)))) << (sz[0] * 8));
+ }
+
+ switch (size) {
+ case 1:
+ *(uint8_t *)data = val;
+ break;
+ case 2:
+ *(uint16_t *)data = val;
+ break;
+ case 4:
+ *(uint32_t *)data = val;
+ break;
+ case 8:
+ *(uint64_t *)data = val;
+ break;
+ }
+ return 0;
+}
+
+
+static struct qla82xx_uri_table_desc *
+qla82xx_get_table_desc(const u8 *unirom, int section)
+{
+ uint32_t i;
+ struct qla82xx_uri_table_desc *directory =
+ (struct qla82xx_uri_table_desc *)&unirom[0];
+ __le32 offset;
+ __le32 tab_type;
+ __le32 entries = cpu_to_le32(directory->num_entries);
+
+ for (i = 0; i < entries; i++) {
+ offset = cpu_to_le32(directory->findex) +
+ (i * cpu_to_le32(directory->entry_size));
+ tab_type = cpu_to_le32(*((u32 *)&unirom[offset] + 8));
+
+ if (tab_type == section)
+ return (struct qla82xx_uri_table_desc *)&unirom[offset];
+ }
+
+ return NULL;
+}
+
+static struct qla82xx_uri_data_desc *
+qla82xx_get_data_desc(struct qla_hw_data *ha,
+ u32 section, u32 idx_offset)
+{
+ const u8 *unirom = ha->hablob->fw->data;
+ int idx = cpu_to_le32(*((int *)&unirom[ha->file_prd_off] + idx_offset));
+ struct qla82xx_uri_table_desc *tab_desc = NULL;
+ __le32 offset;
+
+ tab_desc = qla82xx_get_table_desc(unirom, section);
+ if (!tab_desc)
+ return NULL;
+
+ offset = cpu_to_le32(tab_desc->findex) +
+ (cpu_to_le32(tab_desc->entry_size) * idx);
+
+ return (struct qla82xx_uri_data_desc *)&unirom[offset];
+}
+
+static u8 *
+qla82xx_get_bootld_offset(struct qla_hw_data *ha)
+{
+ u32 offset = BOOTLD_START;
+ struct qla82xx_uri_data_desc *uri_desc = NULL;
+
+ if (ha->fw_type == QLA82XX_UNIFIED_ROMIMAGE) {
+ uri_desc = qla82xx_get_data_desc(ha,
+ QLA82XX_URI_DIR_SECT_BOOTLD, QLA82XX_URI_BOOTLD_IDX_OFF);
+ if (uri_desc)
+ offset = cpu_to_le32(uri_desc->findex);
+ }
+
+ return (u8 *)&ha->hablob->fw->data[offset];
+}
+
+static __le32
+qla82xx_get_fw_size(struct qla_hw_data *ha)
+{
+ struct qla82xx_uri_data_desc *uri_desc = NULL;
+
+ if (ha->fw_type == QLA82XX_UNIFIED_ROMIMAGE) {
+ uri_desc = qla82xx_get_data_desc(ha, QLA82XX_URI_DIR_SECT_FW,
+ QLA82XX_URI_FIRMWARE_IDX_OFF);
+ if (uri_desc)
+ return cpu_to_le32(uri_desc->size);
+ }
+
+ return cpu_to_le32(*(u32 *)&ha->hablob->fw->data[FW_SIZE_OFFSET]);
+}
+
+static u8 *
+qla82xx_get_fw_offs(struct qla_hw_data *ha)
+{
+ u32 offset = IMAGE_START;
+ struct qla82xx_uri_data_desc *uri_desc = NULL;
+
+ if (ha->fw_type == QLA82XX_UNIFIED_ROMIMAGE) {
+ uri_desc = qla82xx_get_data_desc(ha, QLA82XX_URI_DIR_SECT_FW,
+ QLA82XX_URI_FIRMWARE_IDX_OFF);
+ if (uri_desc)
+ offset = cpu_to_le32(uri_desc->findex);
+ }
+
+ return (u8 *)&ha->hablob->fw->data[offset];
+}
+
+/* PCI related functions */
+char *
+qla82xx_pci_info_str(struct scsi_qla_host *vha, char *str)
+{
+ int pcie_reg;
+ struct qla_hw_data *ha = vha->hw;
+ char lwstr[6];
+ uint16_t lnk;
+
+ pcie_reg = pci_find_capability(ha->pdev, PCI_CAP_ID_EXP);
+ pci_read_config_word(ha->pdev, pcie_reg + PCI_EXP_LNKSTA, &lnk);
+ ha->link_width = (lnk >> 4) & 0x3f;
+
+ strcpy(str, "PCIe (");
+ strcat(str, "2.5Gb/s ");
+ snprintf(lwstr, sizeof(lwstr), "x%d)", ha->link_width);
+ strcat(str, lwstr);
+ return str;
+}
+
+int qla82xx_pci_region_offset(struct pci_dev *pdev, int region)
+{
+ unsigned long val = 0;
+ u32 control;
+
+ switch (region) {
+ case 0:
+ val = 0;
+ break;
+ case 1:
+ pci_read_config_dword(pdev, QLA82XX_PCI_REG_MSIX_TBL, &control);
+ val = control + QLA82XX_MSIX_TBL_SPACE;
+ break;
+ }
+ return val;
+}
+
+
+int
+qla82xx_iospace_config(struct qla_hw_data *ha)
+{
+ uint32_t len = 0;
+
+ if (pci_request_regions(ha->pdev, QLA2XXX_DRIVER_NAME)) {
+ ql_log_pci(ql_log_fatal, ha->pdev, 0x000c,
+ "Failed to reserver selected regions.\n");
+ goto iospace_error_exit;
+ }
+
+ /* Use MMIO operations for all accesses. */
+ if (!(pci_resource_flags(ha->pdev, 0) & IORESOURCE_MEM)) {
+ ql_log_pci(ql_log_fatal, ha->pdev, 0x000d,
+ "Region #0 not an MMIO resource, aborting.\n");
+ goto iospace_error_exit;
+ }
+
+ len = pci_resource_len(ha->pdev, 0);
+ ha->nx_pcibase =
+ (unsigned long)ioremap(pci_resource_start(ha->pdev, 0), len);
+ if (!ha->nx_pcibase) {
+ ql_log_pci(ql_log_fatal, ha->pdev, 0x000e,
+ "Cannot remap pcibase MMIO, aborting.\n");
+ pci_release_regions(ha->pdev);
+ goto iospace_error_exit;
+ }
+
+ /* Mapping of IO base pointer */
+ ha->iobase = (device_reg_t __iomem *)((uint8_t *)ha->nx_pcibase +
+ 0xbc000 + (ha->pdev->devfn << 11));
+
+ if (!ql2xdbwr) {
+ ha->nxdb_wr_ptr =
+ (unsigned long)ioremap((pci_resource_start(ha->pdev, 4) +
+ (ha->pdev->devfn << 12)), 4);
+ if (!ha->nxdb_wr_ptr) {
+ ql_log_pci(ql_log_fatal, ha->pdev, 0x000f,
+ "Cannot remap MMIO, aborting.\n");
+ pci_release_regions(ha->pdev);
+ goto iospace_error_exit;
+ }
+
+ /* Mapping of IO base pointer,
+ * door bell read and write pointer
+ */
+ ha->nxdb_rd_ptr = (uint8_t *) ha->nx_pcibase + (512 * 1024) +
+ (ha->pdev->devfn * 8);
+ } else {
+ ha->nxdb_wr_ptr = (ha->pdev->devfn == 6 ?
+ QLA82XX_CAMRAM_DB1 :
+ QLA82XX_CAMRAM_DB2);
+ }
+
+ ha->max_req_queues = ha->max_rsp_queues = 1;
+ ha->msix_count = ha->max_rsp_queues + 1;
+ ql_dbg_pci(ql_dbg_multiq, ha->pdev, 0xc006,
+ "nx_pci_base=%p iobase=%p "
+ "max_req_queues=%d msix_count=%d.\n",
+ (void *)ha->nx_pcibase, ha->iobase,
+ ha->max_req_queues, ha->msix_count);
+ ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0010,
+ "nx_pci_base=%p iobase=%p "
+ "max_req_queues=%d msix_count=%d.\n",
+ (void *)ha->nx_pcibase, ha->iobase,
+ ha->max_req_queues, ha->msix_count);
+ return 0;
+
+iospace_error_exit:
+ return -ENOMEM;
+}
+
+/* GS related functions */
+
+/* Initialization related functions */
+
+/**
+ * qla82xx_pci_config() - Setup ISP82xx PCI configuration registers.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+*/
+int
+qla82xx_pci_config(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ int ret;
+
+ pci_set_master(ha->pdev);
+ ret = pci_set_mwi(ha->pdev);
+ ha->chip_revision = ha->pdev->revision;
+ ql_dbg(ql_dbg_init, vha, 0x0043,
+ "Chip revision:%d.\n",
+ ha->chip_revision);
+ return 0;
+}
+
+/**
+ * qla82xx_reset_chip() - Setup ISP82xx PCI configuration registers.
+ * @ha: HA context
+ *
+ * Returns 0 on success.
+ */
+void
+qla82xx_reset_chip(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ ha->isp_ops->disable_intrs(ha);
+}
+
+void qla82xx_config_rings(struct scsi_qla_host *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_82xx __iomem *reg = &ha->iobase->isp82;
+ struct init_cb_81xx *icb;
+ struct req_que *req = ha->req_q_map[0];
+ struct rsp_que *rsp = ha->rsp_q_map[0];
+
+ /* Setup ring parameters in initialization control block. */
+ icb = (struct init_cb_81xx *)ha->init_cb;
+ icb->request_q_outpointer = __constant_cpu_to_le16(0);
+ icb->response_q_inpointer = __constant_cpu_to_le16(0);
+ icb->request_q_length = cpu_to_le16(req->length);
+ icb->response_q_length = cpu_to_le16(rsp->length);
+ icb->request_q_address[0] = cpu_to_le32(LSD(req->dma));
+ icb->request_q_address[1] = cpu_to_le32(MSD(req->dma));
+ icb->response_q_address[0] = cpu_to_le32(LSD(rsp->dma));
+ icb->response_q_address[1] = cpu_to_le32(MSD(rsp->dma));
+
+ WRT_REG_DWORD((unsigned long __iomem *)&reg->req_q_out[0], 0);
+ WRT_REG_DWORD((unsigned long __iomem *)&reg->rsp_q_in[0], 0);
+ WRT_REG_DWORD((unsigned long __iomem *)&reg->rsp_q_out[0], 0);
+}
+
+void qla82xx_reset_adapter(struct scsi_qla_host *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ vha->flags.online = 0;
+ qla2x00_try_to_stop_firmware(vha);
+ ha->isp_ops->disable_intrs(ha);
+}
+
+static int
+qla82xx_fw_load_from_blob(struct qla_hw_data *ha)
+{
+ u64 *ptr64;
+ u32 i, flashaddr, size;
+ __le64 data;
+
+ size = (IMAGE_START - BOOTLD_START) / 8;
+
+ ptr64 = (u64 *)qla82xx_get_bootld_offset(ha);
+ flashaddr = BOOTLD_START;
+
+ for (i = 0; i < size; i++) {
+ data = cpu_to_le64(ptr64[i]);
+ if (qla82xx_pci_mem_write_2M(ha, flashaddr, &data, 8))
+ return -EIO;
+ flashaddr += 8;
+ }
+
+ flashaddr = FLASH_ADDR_START;
+ size = (__force u32)qla82xx_get_fw_size(ha) / 8;
+ ptr64 = (u64 *)qla82xx_get_fw_offs(ha);
+
+ for (i = 0; i < size; i++) {
+ data = cpu_to_le64(ptr64[i]);
+
+ if (qla82xx_pci_mem_write_2M(ha, flashaddr, &data, 8))
+ return -EIO;
+ flashaddr += 8;
+ }
+ udelay(100);
+
+ /* Write a magic value to CAMRAM register
+ * at a specified offset to indicate
+ * that all data is written and
+ * ready for firmware to initialize.
+ */
+ qla82xx_wr_32(ha, QLA82XX_CAM_RAM(0x1fc), QLA82XX_BDINFO_MAGIC);
+
+ read_lock(&ha->hw_lock);
+ qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_0 + 0x18, 0x1020);
+ qla82xx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, 0x80001e);
+ read_unlock(&ha->hw_lock);
+ return 0;
+}
+
+static int
+qla82xx_set_product_offset(struct qla_hw_data *ha)
+{
+ struct qla82xx_uri_table_desc *ptab_desc = NULL;
+ const uint8_t *unirom = ha->hablob->fw->data;
+ uint32_t i;
+ __le32 entries;
+ __le32 flags, file_chiprev, offset;
+ uint8_t chiprev = ha->chip_revision;
+ /* Hardcoding mn_present flag for P3P */
+ int mn_present = 0;
+ uint32_t flagbit;
+
+ ptab_desc = qla82xx_get_table_desc(unirom,
+ QLA82XX_URI_DIR_SECT_PRODUCT_TBL);
+ if (!ptab_desc)
+ return -1;
+
+ entries = cpu_to_le32(ptab_desc->num_entries);
+
+ for (i = 0; i < entries; i++) {
+ offset = cpu_to_le32(ptab_desc->findex) +
+ (i * cpu_to_le32(ptab_desc->entry_size));
+ flags = cpu_to_le32(*((int *)&unirom[offset] +
+ QLA82XX_URI_FLAGS_OFF));
+ file_chiprev = cpu_to_le32(*((int *)&unirom[offset] +
+ QLA82XX_URI_CHIP_REV_OFF));
+
+ flagbit = mn_present ? 1 : 2;
+
+ if ((chiprev == file_chiprev) && ((1ULL << flagbit) & flags)) {
+ ha->file_prd_off = offset;
+ return 0;
+ }
+ }
+ return -1;
+}
+
+int
+qla82xx_validate_firmware_blob(scsi_qla_host_t *vha, uint8_t fw_type)
+{
+ __le32 val;
+ uint32_t min_size;
+ struct qla_hw_data *ha = vha->hw;
+ const struct firmware *fw = ha->hablob->fw;
+
+ ha->fw_type = fw_type;
+
+ if (fw_type == QLA82XX_UNIFIED_ROMIMAGE) {
+ if (qla82xx_set_product_offset(ha))
+ return -EINVAL;
+
+ min_size = QLA82XX_URI_FW_MIN_SIZE;
+ } else {
+ val = cpu_to_le32(*(u32 *)&fw->data[QLA82XX_FW_MAGIC_OFFSET]);
+ if ((__force u32)val != QLA82XX_BDINFO_MAGIC)
+ return -EINVAL;
+
+ min_size = QLA82XX_FW_MIN_SIZE;
+ }
+
+ if (fw->size < min_size)
+ return -EINVAL;
+ return 0;
+}
+
+static int
+qla82xx_check_cmdpeg_state(struct qla_hw_data *ha)
+{
+ u32 val = 0;
+ int retries = 60;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ do {
+ read_lock(&ha->hw_lock);
+ val = qla82xx_rd_32(ha, CRB_CMDPEG_STATE);
+ read_unlock(&ha->hw_lock);
+
+ switch (val) {
+ case PHAN_INITIALIZE_COMPLETE:
+ case PHAN_INITIALIZE_ACK:
+ return QLA_SUCCESS;
+ case PHAN_INITIALIZE_FAILED:
+ break;
+ default:
+ break;
+ }
+ ql_log(ql_log_info, vha, 0x00a8,
+ "CRB_CMDPEG_STATE: 0x%x and retries:0x%x.\n",
+ val, retries);
+
+ msleep(500);
+
+ } while (--retries);
+
+ ql_log(ql_log_fatal, vha, 0x00a9,
+ "Cmd Peg initialization failed: 0x%x.\n", val);
+
+ val = qla82xx_rd_32(ha, QLA82XX_ROMUSB_GLB_PEGTUNE_DONE);
+ read_lock(&ha->hw_lock);
+ qla82xx_wr_32(ha, CRB_CMDPEG_STATE, PHAN_INITIALIZE_FAILED);
+ read_unlock(&ha->hw_lock);
+ return QLA_FUNCTION_FAILED;
+}
+
+static int
+qla82xx_check_rcvpeg_state(struct qla_hw_data *ha)
+{
+ u32 val = 0;
+ int retries = 60;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ do {
+ read_lock(&ha->hw_lock);
+ val = qla82xx_rd_32(ha, CRB_RCVPEG_STATE);
+ read_unlock(&ha->hw_lock);
+
+ switch (val) {
+ case PHAN_INITIALIZE_COMPLETE:
+ case PHAN_INITIALIZE_ACK:
+ return QLA_SUCCESS;
+ case PHAN_INITIALIZE_FAILED:
+ break;
+ default:
+ break;
+ }
+ ql_log(ql_log_info, vha, 0x00ab,
+ "CRB_RCVPEG_STATE: 0x%x and retries: 0x%x.\n",
+ val, retries);
+
+ msleep(500);
+
+ } while (--retries);
+
+ ql_log(ql_log_fatal, vha, 0x00ac,
+ "Rcv Peg initializatin failed: 0x%x.\n", val);
+ read_lock(&ha->hw_lock);
+ qla82xx_wr_32(ha, CRB_RCVPEG_STATE, PHAN_INITIALIZE_FAILED);
+ read_unlock(&ha->hw_lock);
+ return QLA_FUNCTION_FAILED;
+}
+
+/* ISR related functions */
+uint32_t qla82xx_isr_int_target_mask_enable[8] = {
+ ISR_INT_TARGET_MASK, ISR_INT_TARGET_MASK_F1,
+ ISR_INT_TARGET_MASK_F2, ISR_INT_TARGET_MASK_F3,
+ ISR_INT_TARGET_MASK_F4, ISR_INT_TARGET_MASK_F5,
+ ISR_INT_TARGET_MASK_F7, ISR_INT_TARGET_MASK_F7
+};
+
+uint32_t qla82xx_isr_int_target_status[8] = {
+ ISR_INT_TARGET_STATUS, ISR_INT_TARGET_STATUS_F1,
+ ISR_INT_TARGET_STATUS_F2, ISR_INT_TARGET_STATUS_F3,
+ ISR_INT_TARGET_STATUS_F4, ISR_INT_TARGET_STATUS_F5,
+ ISR_INT_TARGET_STATUS_F7, ISR_INT_TARGET_STATUS_F7
+};
+
+static struct qla82xx_legacy_intr_set legacy_intr[] = \
+ QLA82XX_LEGACY_INTR_CONFIG;
+
+/*
+ * qla82xx_mbx_completion() - Process mailbox command completions.
+ * @ha: SCSI driver HA context
+ * @mb0: Mailbox0 register
+ */
+static void
+qla82xx_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0)
+{
+ uint16_t cnt;
+ uint16_t __iomem *wptr;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_82xx __iomem *reg = &ha->iobase->isp82;
+ wptr = (uint16_t __iomem *)&reg->mailbox_out[1];
+
+ /* Load return mailbox registers. */
+ ha->flags.mbox_int = 1;
+ ha->mailbox_out[0] = mb0;
+
+ for (cnt = 1; cnt < ha->mbx_count; cnt++) {
+ ha->mailbox_out[cnt] = RD_REG_WORD(wptr);
+ wptr++;
+ }
+
+ if (!ha->mcp)
+ ql_dbg(ql_dbg_async, vha, 0x5053,
+ "MBX pointer ERROR.\n");
+}
+
+/*
+ * qla82xx_intr_handler() - Process interrupts for the ISP23xx and ISP63xx.
+ * @irq:
+ * @dev_id: SCSI driver HA context
+ * @regs:
+ *
+ * Called by system whenever the host adapter generates an interrupt.
+ *
+ * Returns handled flag.
+ */
+irqreturn_t
+qla82xx_intr_handler(int irq, void *dev_id)
+{
+ scsi_qla_host_t *vha;
+ struct qla_hw_data *ha;
+ struct rsp_que *rsp;
+ struct device_reg_82xx __iomem *reg;
+ int status = 0, status1 = 0;
+ unsigned long flags;
+ unsigned long iter;
+ uint32_t stat = 0;
+ uint16_t mb[4];
+
+ rsp = (struct rsp_que *) dev_id;
+ if (!rsp) {
+ ql_log(ql_log_info, NULL, 0xb054,
+ "%s: NULL response queue pointer.\n", __func__);
+ return IRQ_NONE;
+ }
+ ha = rsp->hw;
+
+ if (!ha->flags.msi_enabled) {
+ status = qla82xx_rd_32(ha, ISR_INT_VECTOR);
+ if (!(status & ha->nx_legacy_intr.int_vec_bit))
+ return IRQ_NONE;
+
+ status1 = qla82xx_rd_32(ha, ISR_INT_STATE_REG);
+ if (!ISR_IS_LEGACY_INTR_TRIGGERED(status1))
+ return IRQ_NONE;
+ }
+
+ /* clear the interrupt */
+ qla82xx_wr_32(ha, ha->nx_legacy_intr.tgt_status_reg, 0xffffffff);
+
+ /* read twice to ensure write is flushed */
+ qla82xx_rd_32(ha, ISR_INT_VECTOR);
+ qla82xx_rd_32(ha, ISR_INT_VECTOR);
+
+ reg = &ha->iobase->isp82;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ vha = pci_get_drvdata(ha->pdev);
+ for (iter = 1; iter--; ) {
+
+ if (RD_REG_DWORD(&reg->host_int)) {
+ stat = RD_REG_DWORD(&reg->host_status);
+
+ switch (stat & 0xff) {
+ case 0x1:
+ case 0x2:
+ case 0x10:
+ case 0x11:
+ qla82xx_mbx_completion(vha, MSW(stat));
+ status |= MBX_INTERRUPT;
+ break;
+ case 0x12:
+ mb[0] = MSW(stat);
+ mb[1] = RD_REG_WORD(&reg->mailbox_out[1]);
+ mb[2] = RD_REG_WORD(&reg->mailbox_out[2]);
+ mb[3] = RD_REG_WORD(&reg->mailbox_out[3]);
+ qla2x00_async_event(vha, rsp, mb);
+ break;
+ case 0x13:
+ qla24xx_process_response_queue(vha, rsp);
+ break;
+ default:
+ ql_dbg(ql_dbg_async, vha, 0x5054,
+ "Unrecognized interrupt type (%d).\n",
+ stat & 0xff);
+ break;
+ }
+ }
+ WRT_REG_DWORD(&reg->host_int, 0);
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ if (!ha->flags.msi_enabled)
+ qla82xx_wr_32(ha, ha->nx_legacy_intr.tgt_mask_reg, 0xfbff);
+
+#ifdef QL_DEBUG_LEVEL_17
+ if (!irq && ha->flags.eeh_busy)
+ ql_log(ql_log_warn, vha, 0x503d,
+ "isr:status %x, cmd_flags %lx, mbox_int %x, stat %x.\n",
+ status, ha->mbx_cmd_flags, ha->flags.mbox_int, stat);
+#endif
+
+ if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) &&
+ (status & MBX_INTERRUPT) && ha->flags.mbox_int) {
+ set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
+ complete(&ha->mbx_intr_comp);
+ }
+ return IRQ_HANDLED;
+}
+
+irqreturn_t
+qla82xx_msix_default(int irq, void *dev_id)
+{
+ scsi_qla_host_t *vha;
+ struct qla_hw_data *ha;
+ struct rsp_que *rsp;
+ struct device_reg_82xx __iomem *reg;
+ int status = 0;
+ unsigned long flags;
+ uint32_t stat = 0;
+ uint16_t mb[4];
+
+ rsp = (struct rsp_que *) dev_id;
+ if (!rsp) {
+ printk(KERN_INFO
+ "%s(): NULL response queue pointer.\n", __func__);
+ return IRQ_NONE;
+ }
+ ha = rsp->hw;
+
+ reg = &ha->iobase->isp82;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ vha = pci_get_drvdata(ha->pdev);
+ do {
+ if (RD_REG_DWORD(&reg->host_int)) {
+ stat = RD_REG_DWORD(&reg->host_status);
+
+ switch (stat & 0xff) {
+ case 0x1:
+ case 0x2:
+ case 0x10:
+ case 0x11:
+ qla82xx_mbx_completion(vha, MSW(stat));
+ status |= MBX_INTERRUPT;
+ break;
+ case 0x12:
+ mb[0] = MSW(stat);
+ mb[1] = RD_REG_WORD(&reg->mailbox_out[1]);
+ mb[2] = RD_REG_WORD(&reg->mailbox_out[2]);
+ mb[3] = RD_REG_WORD(&reg->mailbox_out[3]);
+ qla2x00_async_event(vha, rsp, mb);
+ break;
+ case 0x13:
+ qla24xx_process_response_queue(vha, rsp);
+ break;
+ default:
+ ql_dbg(ql_dbg_async, vha, 0x5041,
+ "Unrecognized interrupt type (%d).\n",
+ stat & 0xff);
+ break;
+ }
+ }
+ WRT_REG_DWORD(&reg->host_int, 0);
+ } while (0);
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+#ifdef QL_DEBUG_LEVEL_17
+ if (!irq && ha->flags.eeh_busy)
+ ql_log(ql_log_warn, vha, 0x5044,
+ "isr:status %x, cmd_flags %lx, mbox_int %x, stat %x.\n",
+ status, ha->mbx_cmd_flags, ha->flags.mbox_int, stat);
+#endif
+
+ if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) &&
+ (status & MBX_INTERRUPT) && ha->flags.mbox_int) {
+ set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags);
+ complete(&ha->mbx_intr_comp);
+ }
+ return IRQ_HANDLED;
+}
+
+irqreturn_t
+qla82xx_msix_rsp_q(int irq, void *dev_id)
+{
+ scsi_qla_host_t *vha;
+ struct qla_hw_data *ha;
+ struct rsp_que *rsp;
+ struct device_reg_82xx __iomem *reg;
+ unsigned long flags;
+
+ rsp = (struct rsp_que *) dev_id;
+ if (!rsp) {
+ printk(KERN_INFO
+ "%s(): NULL response queue pointer.\n", __func__);
+ return IRQ_NONE;
+ }
+
+ ha = rsp->hw;
+ reg = &ha->iobase->isp82;
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ vha = pci_get_drvdata(ha->pdev);
+ qla24xx_process_response_queue(vha, rsp);
+ WRT_REG_DWORD(&reg->host_int, 0);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ return IRQ_HANDLED;
+}
+
+void
+qla82xx_poll(int irq, void *dev_id)
+{
+ scsi_qla_host_t *vha;
+ struct qla_hw_data *ha;
+ struct rsp_que *rsp;
+ struct device_reg_82xx __iomem *reg;
+ int status = 0;
+ uint32_t stat;
+ uint16_t mb[4];
+ unsigned long flags;
+
+ rsp = (struct rsp_que *) dev_id;
+ if (!rsp) {
+ printk(KERN_INFO
+ "%s(): NULL response queue pointer.\n", __func__);
+ return;
+ }
+ ha = rsp->hw;
+
+ reg = &ha->iobase->isp82;
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ vha = pci_get_drvdata(ha->pdev);
+
+ if (RD_REG_DWORD(&reg->host_int)) {
+ stat = RD_REG_DWORD(&reg->host_status);
+ switch (stat & 0xff) {
+ case 0x1:
+ case 0x2:
+ case 0x10:
+ case 0x11:
+ qla82xx_mbx_completion(vha, MSW(stat));
+ status |= MBX_INTERRUPT;
+ break;
+ case 0x12:
+ mb[0] = MSW(stat);
+ mb[1] = RD_REG_WORD(&reg->mailbox_out[1]);
+ mb[2] = RD_REG_WORD(&reg->mailbox_out[2]);
+ mb[3] = RD_REG_WORD(&reg->mailbox_out[3]);
+ qla2x00_async_event(vha, rsp, mb);
+ break;
+ case 0x13:
+ qla24xx_process_response_queue(vha, rsp);
+ break;
+ default:
+ ql_dbg(ql_dbg_p3p, vha, 0xb013,
+ "Unrecognized interrupt type (%d).\n",
+ stat * 0xff);
+ break;
+ }
+ }
+ WRT_REG_DWORD(&reg->host_int, 0);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+void
+qla82xx_enable_intrs(struct qla_hw_data *ha)
+{
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+ qla82xx_mbx_intr_enable(vha);
+ spin_lock_irq(&ha->hardware_lock);
+ qla82xx_wr_32(ha, ha->nx_legacy_intr.tgt_mask_reg, 0xfbff);
+ spin_unlock_irq(&ha->hardware_lock);
+ ha->interrupts_on = 1;
+}
+
+void
+qla82xx_disable_intrs(struct qla_hw_data *ha)
+{
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+ qla82xx_mbx_intr_disable(vha);
+ spin_lock_irq(&ha->hardware_lock);
+ qla82xx_wr_32(ha, ha->nx_legacy_intr.tgt_mask_reg, 0x0400);
+ spin_unlock_irq(&ha->hardware_lock);
+ ha->interrupts_on = 0;
+}
+
+void qla82xx_init_flags(struct qla_hw_data *ha)
+{
+ struct qla82xx_legacy_intr_set *nx_legacy_intr;
+
+ /* ISP 8021 initializations */
+ rwlock_init(&ha->hw_lock);
+ ha->qdr_sn_window = -1;
+ ha->ddr_mn_window = -1;
+ ha->curr_window = 255;
+ ha->portnum = PCI_FUNC(ha->pdev->devfn);
+ nx_legacy_intr = &legacy_intr[ha->portnum];
+ 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;
+}
+
+inline void
+qla82xx_set_drv_active(scsi_qla_host_t *vha)
+{
+ uint32_t drv_active;
+ struct qla_hw_data *ha = vha->hw;
+
+ drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
+
+ /* If reset value is all FF's, initialize DRV_ACTIVE */
+ if (drv_active == 0xffffffff) {
+ qla82xx_wr_32(ha, QLA82XX_CRB_DRV_ACTIVE,
+ QLA82XX_DRV_NOT_ACTIVE);
+ drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
+ }
+ drv_active |= (QLA82XX_DRV_ACTIVE << (ha->portnum * 4));
+ qla82xx_wr_32(ha, QLA82XX_CRB_DRV_ACTIVE, drv_active);
+}
+
+inline void
+qla82xx_clear_drv_active(struct qla_hw_data *ha)
+{
+ uint32_t drv_active;
+
+ drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
+ drv_active &= ~(QLA82XX_DRV_ACTIVE << (ha->portnum * 4));
+ qla82xx_wr_32(ha, QLA82XX_CRB_DRV_ACTIVE, drv_active);
+}
+
+static inline int
+qla82xx_need_reset(struct qla_hw_data *ha)
+{
+ uint32_t drv_state;
+ int rval;
+
+ if (ha->flags.isp82xx_reset_owner)
+ return 1;
+ else {
+ drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
+ rval = drv_state & (QLA82XX_DRVST_RST_RDY << (ha->portnum * 4));
+ return rval;
+ }
+}
+
+static inline void
+qla82xx_set_rst_ready(struct qla_hw_data *ha)
+{
+ uint32_t drv_state;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
+
+ /* If reset value is all FF's, initialize DRV_STATE */
+ if (drv_state == 0xffffffff) {
+ qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, QLA82XX_DRVST_NOT_RDY);
+ drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
+ }
+ drv_state |= (QLA82XX_DRVST_RST_RDY << (ha->portnum * 4));
+ ql_dbg(ql_dbg_init, vha, 0x00bb,
+ "drv_state = 0x%08x.\n", drv_state);
+ qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, drv_state);
+}
+
+static inline void
+qla82xx_clear_rst_ready(struct qla_hw_data *ha)
+{
+ uint32_t drv_state;
+
+ drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
+ drv_state &= ~(QLA82XX_DRVST_RST_RDY << (ha->portnum * 4));
+ qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, drv_state);
+}
+
+static inline void
+qla82xx_set_qsnt_ready(struct qla_hw_data *ha)
+{
+ uint32_t qsnt_state;
+
+ qsnt_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
+ qsnt_state |= (QLA82XX_DRVST_QSNT_RDY << (ha->portnum * 4));
+ qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, qsnt_state);
+}
+
+void
+qla82xx_clear_qsnt_ready(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint32_t qsnt_state;
+
+ qsnt_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
+ qsnt_state &= ~(QLA82XX_DRVST_QSNT_RDY << (ha->portnum * 4));
+ qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, qsnt_state);
+}
+
+static int
+qla82xx_load_fw(scsi_qla_host_t *vha)
+{
+ int rst;
+ struct fw_blob *blob;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (qla82xx_pinit_from_rom(vha) != QLA_SUCCESS) {
+ ql_log(ql_log_fatal, vha, 0x009f,
+ "Error during CRB initialization.\n");
+ return QLA_FUNCTION_FAILED;
+ }
+ udelay(500);
+
+ /* Bring QM and CAMRAM out of reset */
+ rst = qla82xx_rd_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET);
+ rst &= ~((1 << 28) | (1 << 24));
+ qla82xx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, rst);
+
+ /*
+ * FW Load priority:
+ * 1) Operational firmware residing in flash.
+ * 2) Firmware via request-firmware interface (.bin file).
+ */
+ if (ql2xfwloadbin == 2)
+ goto try_blob_fw;
+
+ ql_log(ql_log_info, vha, 0x00a0,
+ "Attempting to load firmware from flash.\n");
+
+ if (qla82xx_fw_load_from_flash(ha) == QLA_SUCCESS) {
+ ql_log(ql_log_info, vha, 0x00a1,
+ "Firmware loaded successully from flash.\n");
+ return QLA_SUCCESS;
+ } else {
+ ql_log(ql_log_warn, vha, 0x0108,
+ "Firmware load from flash failed.\n");
+ }
+
+try_blob_fw:
+ ql_log(ql_log_info, vha, 0x00a2,
+ "Attempting to load firmware from blob.\n");
+
+ /* Load firmware blob. */
+ blob = ha->hablob = qla2x00_request_firmware(vha);
+ if (!blob) {
+ ql_log(ql_log_fatal, vha, 0x00a3,
+ "Firmware image not preset.\n");
+ goto fw_load_failed;
+ }
+
+ /* Validating firmware blob */
+ if (qla82xx_validate_firmware_blob(vha,
+ QLA82XX_FLASH_ROMIMAGE)) {
+ /* Fallback to URI format */
+ if (qla82xx_validate_firmware_blob(vha,
+ QLA82XX_UNIFIED_ROMIMAGE)) {
+ ql_log(ql_log_fatal, vha, 0x00a4,
+ "No valid firmware image found.\n");
+ return QLA_FUNCTION_FAILED;
+ }
+ }
+
+ if (qla82xx_fw_load_from_blob(ha) == QLA_SUCCESS) {
+ ql_log(ql_log_info, vha, 0x00a5,
+ "Firmware loaded successfully from binary blob.\n");
+ return QLA_SUCCESS;
+ } else {
+ ql_log(ql_log_fatal, vha, 0x00a6,
+ "Firmware load failed for binary blob.\n");
+ blob->fw = NULL;
+ blob = NULL;
+ goto fw_load_failed;
+ }
+ return QLA_SUCCESS;
+
+fw_load_failed:
+ return QLA_FUNCTION_FAILED;
+}
+
+int
+qla82xx_start_firmware(scsi_qla_host_t *vha)
+{
+ int pcie_cap;
+ uint16_t lnk;
+ struct qla_hw_data *ha = vha->hw;
+
+ /* scrub dma mask expansion register */
+ qla82xx_wr_32(ha, CRB_DMA_SHIFT, QLA82XX_DMA_SHIFT_VALUE);
+
+ /* Put both the PEG CMD and RCV PEG to default state
+ * of 0 before resetting the hardware
+ */
+ qla82xx_wr_32(ha, CRB_CMDPEG_STATE, 0);
+ qla82xx_wr_32(ha, CRB_RCVPEG_STATE, 0);
+
+ /* Overwrite stale initialization register values */
+ qla82xx_wr_32(ha, QLA82XX_PEG_HALT_STATUS1, 0);
+ qla82xx_wr_32(ha, QLA82XX_PEG_HALT_STATUS2, 0);
+
+ if (qla82xx_load_fw(vha) != QLA_SUCCESS) {
+ ql_log(ql_log_fatal, vha, 0x00a7,
+ "Error trying to start fw.\n");
+ return QLA_FUNCTION_FAILED;
+ }
+
+ /* Handshake with the card before we register the devices. */
+ if (qla82xx_check_cmdpeg_state(ha) != QLA_SUCCESS) {
+ ql_log(ql_log_fatal, vha, 0x00aa,
+ "Error during card handshake.\n");
+ return QLA_FUNCTION_FAILED;
+ }
+
+ /* Negotiated Link width */
+ pcie_cap = pci_find_capability(ha->pdev, PCI_CAP_ID_EXP);
+ pci_read_config_word(ha->pdev, pcie_cap + PCI_EXP_LNKSTA, &lnk);
+ ha->link_width = (lnk >> 4) & 0x3f;
+
+ /* Synchronize with Receive peg */
+ return qla82xx_check_rcvpeg_state(ha);
+}
+
+static uint32_t *
+qla82xx_read_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr,
+ uint32_t length)
+{
+ uint32_t i;
+ uint32_t val;
+ struct qla_hw_data *ha = vha->hw;
+
+ /* Dword reads to flash. */
+ for (i = 0; i < length/4; i++, faddr += 4) {
+ if (qla82xx_rom_fast_read(ha, faddr, &val)) {
+ ql_log(ql_log_warn, vha, 0x0106,
+ "Do ROM fast read failed.\n");
+ goto done_read;
+ }
+ dwptr[i] = __constant_cpu_to_le32(val);
+ }
+done_read:
+ return dwptr;
+}
+
+static int
+qla82xx_unprotect_flash(struct qla_hw_data *ha)
+{
+ int ret;
+ uint32_t val;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ ret = ql82xx_rom_lock_d(ha);
+ if (ret < 0) {
+ ql_log(ql_log_warn, vha, 0xb014,
+ "ROM Lock failed.\n");
+ return ret;
+ }
+
+ ret = qla82xx_read_status_reg(ha, &val);
+ if (ret < 0)
+ goto done_unprotect;
+
+ val &= ~(BLOCK_PROTECT_BITS << 2);
+ ret = qla82xx_write_status_reg(ha, val);
+ if (ret < 0) {
+ val |= (BLOCK_PROTECT_BITS << 2);
+ qla82xx_write_status_reg(ha, val);
+ }
+
+ if (qla82xx_write_disable_flash(ha) != 0)
+ ql_log(ql_log_warn, vha, 0xb015,
+ "Write disable failed.\n");
+
+done_unprotect:
+ qla82xx_rom_unlock(ha);
+ return ret;
+}
+
+static int
+qla82xx_protect_flash(struct qla_hw_data *ha)
+{
+ int ret;
+ uint32_t val;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ ret = ql82xx_rom_lock_d(ha);
+ if (ret < 0) {
+ ql_log(ql_log_warn, vha, 0xb016,
+ "ROM Lock failed.\n");
+ return ret;
+ }
+
+ ret = qla82xx_read_status_reg(ha, &val);
+ if (ret < 0)
+ goto done_protect;
+
+ val |= (BLOCK_PROTECT_BITS << 2);
+ /* LOCK all sectors */
+ ret = qla82xx_write_status_reg(ha, val);
+ if (ret < 0)
+ ql_log(ql_log_warn, vha, 0xb017,
+ "Write status register failed.\n");
+
+ if (qla82xx_write_disable_flash(ha) != 0)
+ ql_log(ql_log_warn, vha, 0xb018,
+ "Write disable failed.\n");
+done_protect:
+ qla82xx_rom_unlock(ha);
+ return ret;
+}
+
+static int
+qla82xx_erase_sector(struct qla_hw_data *ha, int addr)
+{
+ int ret = 0;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ ret = ql82xx_rom_lock_d(ha);
+ if (ret < 0) {
+ ql_log(ql_log_warn, vha, 0xb019,
+ "ROM Lock failed.\n");
+ return ret;
+ }
+
+ qla82xx_flash_set_write_enable(ha);
+ qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ADDRESS, addr);
+ qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ABYTE_CNT, 3);
+ qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_SE);
+
+ if (qla82xx_wait_rom_done(ha)) {
+ ql_log(ql_log_warn, vha, 0xb01a,
+ "Error waiting for rom done.\n");
+ ret = -1;
+ goto done;
+ }
+ ret = qla82xx_flash_wait_write_finish(ha);
+done:
+ qla82xx_rom_unlock(ha);
+ return ret;
+}
+
+/*
+ * Address and length are byte address
+ */
+uint8_t *
+qla82xx_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf,
+ uint32_t offset, uint32_t length)
+{
+ scsi_block_requests(vha->host);
+ qla82xx_read_flash_data(vha, (uint32_t *)buf, offset, length);
+ scsi_unblock_requests(vha->host);
+ return buf;
+}
+
+static int
+qla82xx_write_flash_data(struct scsi_qla_host *vha, uint32_t *dwptr,
+ uint32_t faddr, uint32_t dwords)
+{
+ int ret;
+ uint32_t liter;
+ uint32_t sec_mask, rest_addr;
+ dma_addr_t optrom_dma;
+ void *optrom = NULL;
+ int page_mode = 0;
+ struct qla_hw_data *ha = vha->hw;
+
+ ret = -1;
+
+ /* Prepare burst-capable write on supported ISPs. */
+ if (page_mode && !(faddr & 0xfff) &&
+ dwords > OPTROM_BURST_DWORDS) {
+ optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
+ &optrom_dma, GFP_KERNEL);
+ if (!optrom) {
+ ql_log(ql_log_warn, vha, 0xb01b,
+ "Unable to allocate memory "
+ "for optron burst write (%x KB).\n",
+ OPTROM_BURST_SIZE / 1024);
+ }
+ }
+
+ rest_addr = ha->fdt_block_size - 1;
+ sec_mask = ~rest_addr;
+
+ ret = qla82xx_unprotect_flash(ha);
+ if (ret) {
+ ql_log(ql_log_warn, vha, 0xb01c,
+ "Unable to unprotect flash for update.\n");
+ goto write_done;
+ }
+
+ for (liter = 0; liter < dwords; liter++, faddr += 4, dwptr++) {
+ /* Are we at the beginning of a sector? */
+ if ((faddr & rest_addr) == 0) {
+
+ ret = qla82xx_erase_sector(ha, faddr);
+ if (ret) {
+ ql_log(ql_log_warn, vha, 0xb01d,
+ "Unable to erase sector: address=%x.\n",
+ faddr);
+ break;
+ }
+ }
+
+ /* Go with burst-write. */
+ if (optrom && (liter + OPTROM_BURST_DWORDS) <= dwords) {
+ /* Copy data to DMA'ble buffer. */
+ memcpy(optrom, dwptr, OPTROM_BURST_SIZE);
+
+ ret = qla2x00_load_ram(vha, optrom_dma,
+ (ha->flash_data_off | faddr),
+ OPTROM_BURST_DWORDS);
+ if (ret != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0xb01e,
+ "Unable to burst-write optrom segment "
+ "(%x/%x/%llx).\n", ret,
+ (ha->flash_data_off | faddr),
+ (unsigned long long)optrom_dma);
+ ql_log(ql_log_warn, vha, 0xb01f,
+ "Reverting to slow-write.\n");
+
+ dma_free_coherent(&ha->pdev->dev,
+ OPTROM_BURST_SIZE, optrom, optrom_dma);
+ optrom = NULL;
+ } else {
+ liter += OPTROM_BURST_DWORDS - 1;
+ faddr += OPTROM_BURST_DWORDS - 1;
+ dwptr += OPTROM_BURST_DWORDS - 1;
+ continue;
+ }
+ }
+
+ ret = qla82xx_write_flash_dword(ha, faddr,
+ cpu_to_le32(*dwptr));
+ if (ret) {
+ ql_dbg(ql_dbg_p3p, vha, 0xb020,
+ "Unable to program flash address=%x data=%x.\n",
+ faddr, *dwptr);
+ break;
+ }
+ }
+
+ ret = qla82xx_protect_flash(ha);
+ if (ret)
+ ql_log(ql_log_warn, vha, 0xb021,
+ "Unable to protect flash after update.\n");
+write_done:
+ if (optrom)
+ dma_free_coherent(&ha->pdev->dev,
+ OPTROM_BURST_SIZE, optrom, optrom_dma);
+ return ret;
+}
+
+int
+qla82xx_write_optrom_data(struct scsi_qla_host *vha, uint8_t *buf,
+ uint32_t offset, uint32_t length)
+{
+ int rval;
+
+ /* Suspend HBA. */
+ scsi_block_requests(vha->host);
+ rval = qla82xx_write_flash_data(vha, (uint32_t *)buf, offset,
+ length >> 2);
+ scsi_unblock_requests(vha->host);
+
+ /* Convert return ISP82xx to generic */
+ if (rval)
+ rval = QLA_FUNCTION_FAILED;
+ else
+ rval = QLA_SUCCESS;
+ return rval;
+}
+
+void
+qla82xx_start_iocbs(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+ struct device_reg_82xx __iomem *reg;
+ uint32_t dbval;
+
+ /* Adjust ring index. */
+ req->ring_index++;
+ if (req->ring_index == req->length) {
+ req->ring_index = 0;
+ req->ring_ptr = req->ring;
+ } else
+ req->ring_ptr++;
+
+ reg = &ha->iobase->isp82;
+ dbval = 0x04 | (ha->portnum << 5);
+
+ dbval = dbval | (req->id << 8) | (req->ring_index << 16);
+ if (ql2xdbwr)
+ qla82xx_wr_32(ha, ha->nxdb_wr_ptr, dbval);
+ else {
+ WRT_REG_DWORD((unsigned long __iomem *)ha->nxdb_wr_ptr, dbval);
+ wmb();
+ while (RD_REG_DWORD(ha->nxdb_rd_ptr) != dbval) {
+ WRT_REG_DWORD((unsigned long __iomem *)ha->nxdb_wr_ptr,
+ dbval);
+ wmb();
+ }
+ }
+}
+
+void qla82xx_rom_lock_recovery(struct qla_hw_data *ha)
+{
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ if (qla82xx_rom_lock(ha))
+ /* Someone else is holding the lock. */
+ ql_log(ql_log_info, vha, 0xb022,
+ "Resetting rom_lock.\n");
+
+ /*
+ * Either we got the lock, or someone
+ * else died while holding it.
+ * In either case, unlock.
+ */
+ qla82xx_rom_unlock(ha);
+}
+
+/*
+ * qla82xx_device_bootstrap
+ * Initialize device, set DEV_READY, start fw
+ *
+ * Note:
+ * IDC lock must be held upon entry
+ *
+ * Return:
+ * Success : 0
+ * Failed : 1
+ */
+static int
+qla82xx_device_bootstrap(scsi_qla_host_t *vha)
+{
+ int rval = QLA_SUCCESS;
+ int i, timeout;
+ uint32_t old_count, count;
+ struct qla_hw_data *ha = vha->hw;
+ int need_reset = 0, peg_stuck = 1;
+
+ need_reset = qla82xx_need_reset(ha);
+
+ old_count = qla82xx_rd_32(ha, QLA82XX_PEG_ALIVE_COUNTER);
+
+ for (i = 0; i < 10; i++) {
+ timeout = msleep_interruptible(200);
+ if (timeout) {
+ qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
+ QLA82XX_DEV_FAILED);
+ return QLA_FUNCTION_FAILED;
+ }
+
+ count = qla82xx_rd_32(ha, QLA82XX_PEG_ALIVE_COUNTER);
+ if (count != old_count)
+ peg_stuck = 0;
+ }
+
+ if (need_reset) {
+ /* We are trying to perform a recovery here. */
+ if (peg_stuck)
+ qla82xx_rom_lock_recovery(ha);
+ goto dev_initialize;
+ } else {
+ /* Start of day for this ha context. */
+ if (peg_stuck) {
+ /* Either we are the first or recovery in progress. */
+ qla82xx_rom_lock_recovery(ha);
+ goto dev_initialize;
+ } else
+ /* Firmware already running. */
+ goto dev_ready;
+ }
+
+ return rval;
+
+dev_initialize:
+ /* set to DEV_INITIALIZING */
+ ql_log(ql_log_info, vha, 0x009e,
+ "HW State: INITIALIZING.\n");
+ qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_INITIALIZING);
+
+ /* Driver that sets device state to initializating sets IDC version */
+ qla82xx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION, QLA82XX_IDC_VERSION);
+
+ qla82xx_idc_unlock(ha);
+ rval = qla82xx_start_firmware(vha);
+ qla82xx_idc_lock(ha);
+
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_fatal, vha, 0x00ad,
+ "HW State: FAILED.\n");
+ qla82xx_clear_drv_active(ha);
+ qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_FAILED);
+ return rval;
+ }
+
+dev_ready:
+ ql_log(ql_log_info, vha, 0x00ae,
+ "HW State: READY.\n");
+ qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_READY);
+
+ return QLA_SUCCESS;
+}
+
+/*
+* qla82xx_need_qsnt_handler
+* Code to start quiescence sequence
+*
+* Note:
+* IDC lock must be held upon entry
+*
+* Return: void
+*/
+
+static void
+qla82xx_need_qsnt_handler(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint32_t dev_state, drv_state, drv_active;
+ unsigned long reset_timeout;
+
+ if (vha->flags.online) {
+ /*Block any further I/O and wait for pending cmnds to complete*/
+ qla82xx_quiescent_state_cleanup(vha);
+ }
+
+ /* Set the quiescence ready bit */
+ qla82xx_set_qsnt_ready(ha);
+
+ /*wait for 30 secs for other functions to ack */
+ reset_timeout = jiffies + (30 * HZ);
+
+ drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
+ drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
+ /* Its 2 that is written when qsnt is acked, moving one bit */
+ drv_active = drv_active << 0x01;
+
+ while (drv_state != drv_active) {
+
+ if (time_after_eq(jiffies, reset_timeout)) {
+ /* quiescence timeout, other functions didn't ack
+ * changing the state to DEV_READY
+ */
+ ql_log(ql_log_info, vha, 0xb023,
+ "%s : QUIESCENT TIMEOUT.\n", QLA2XXX_DRIVER_NAME);
+ ql_log(ql_log_info, vha, 0xb024,
+ "DRV_ACTIVE:%d DRV_STATE:%d.\n",
+ drv_active, drv_state);
+ qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
+ QLA82XX_DEV_READY);
+ ql_log(ql_log_info, vha, 0xb025,
+ "HW State: DEV_READY.\n");
+ qla82xx_idc_unlock(ha);
+ qla2x00_perform_loop_resync(vha);
+ qla82xx_idc_lock(ha);
+
+ qla82xx_clear_qsnt_ready(vha);
+ return;
+ }
+
+ qla82xx_idc_unlock(ha);
+ msleep(1000);
+ qla82xx_idc_lock(ha);
+
+ drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
+ drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
+ drv_active = drv_active << 0x01;
+ }
+ dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
+ /* everyone acked so set the state to DEV_QUIESCENCE */
+ if (dev_state == QLA82XX_DEV_NEED_QUIESCENT) {
+ ql_log(ql_log_info, vha, 0xb026,
+ "HW State: DEV_QUIESCENT.\n");
+ qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_QUIESCENT);
+ }
+}
+
+/*
+* qla82xx_wait_for_state_change
+* Wait for device state to change from given current state
+*
+* Note:
+* IDC lock must not be held upon entry
+*
+* Return:
+* Changed device state.
+*/
+uint32_t
+qla82xx_wait_for_state_change(scsi_qla_host_t *vha, uint32_t curr_state)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint32_t dev_state;
+
+ do {
+ msleep(1000);
+ qla82xx_idc_lock(ha);
+ dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
+ qla82xx_idc_unlock(ha);
+ } while (dev_state == curr_state);
+
+ return dev_state;
+}
+
+static void
+qla82xx_dev_failed_handler(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+
+ /* Disable the board */
+ ql_log(ql_log_fatal, vha, 0x00b8,
+ "Disabling the board.\n");
+
+ qla82xx_idc_lock(ha);
+ qla82xx_clear_drv_active(ha);
+ qla82xx_idc_unlock(ha);
+
+ /* Set DEV_FAILED flag to disable timer */
+ vha->device_flags |= DFLG_DEV_FAILED;
+ qla2x00_abort_all_cmds(vha, DID_NO_CONNECT << 16);
+ qla2x00_mark_all_devices_lost(vha, 0);
+ vha->flags.online = 0;
+ vha->flags.init_done = 0;
+}
+
+/*
+ * qla82xx_need_reset_handler
+ * Code to start reset sequence
+ *
+ * Note:
+ * IDC lock must be held upon entry
+ *
+ * Return:
+ * Success : 0
+ * Failed : 1
+ */
+static void
+qla82xx_need_reset_handler(scsi_qla_host_t *vha)
+{
+ uint32_t dev_state, drv_state, drv_active;
+ uint32_t active_mask = 0;
+ unsigned long reset_timeout;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+
+ if (vha->flags.online) {
+ qla82xx_idc_unlock(ha);
+ qla2x00_abort_isp_cleanup(vha);
+ ha->isp_ops->get_flash_version(vha, req->ring);
+ ha->isp_ops->nvram_config(vha);
+ qla82xx_idc_lock(ha);
+ }
+
+ drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
+ if (!ha->flags.isp82xx_reset_owner) {
+ ql_dbg(ql_dbg_p3p, vha, 0xb028,
+ "reset_acknowledged by 0x%x\n", ha->portnum);
+ qla82xx_set_rst_ready(ha);
+ } else {
+ active_mask = ~(QLA82XX_DRV_ACTIVE << (ha->portnum * 4));
+ drv_active &= active_mask;
+ ql_dbg(ql_dbg_p3p, vha, 0xb029,
+ "active_mask: 0x%08x\n", active_mask);
+ }
+
+ /* wait for 10 seconds for reset ack from all functions */
+ reset_timeout = jiffies + (ha->nx_reset_timeout * HZ);
+
+ drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
+ drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
+ dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
+
+ ql_dbg(ql_dbg_p3p, vha, 0xb02a,
+ "drv_state: 0x%08x, drv_active: 0x%08x, "
+ "dev_state: 0x%08x, active_mask: 0x%08x\n",
+ drv_state, drv_active, dev_state, active_mask);
+
+ while (drv_state != drv_active &&
+ dev_state != QLA82XX_DEV_INITIALIZING) {
+ if (time_after_eq(jiffies, reset_timeout)) {
+ ql_log(ql_log_warn, vha, 0x00b5,
+ "Reset timeout.\n");
+ break;
+ }
+ qla82xx_idc_unlock(ha);
+ msleep(1000);
+ qla82xx_idc_lock(ha);
+ drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE);
+ drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
+ if (ha->flags.isp82xx_reset_owner)
+ drv_active &= active_mask;
+ dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
+ }
+
+ ql_dbg(ql_dbg_p3p, vha, 0xb02b,
+ "drv_state: 0x%08x, drv_active: 0x%08x, "
+ "dev_state: 0x%08x, active_mask: 0x%08x\n",
+ drv_state, drv_active, dev_state, active_mask);
+
+ ql_log(ql_log_info, vha, 0x00b6,
+ "Device state is 0x%x = %s.\n",
+ dev_state,
+ dev_state < MAX_STATES ? qdev_state(dev_state) : "Unknown");
+
+ /* Force to DEV_COLD unless someone else is starting a reset */
+ if (dev_state != QLA82XX_DEV_INITIALIZING &&
+ dev_state != QLA82XX_DEV_COLD) {
+ ql_log(ql_log_info, vha, 0x00b7,
+ "HW State: COLD/RE-INIT.\n");
+ qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_COLD);
+ qla82xx_set_rst_ready(ha);
+ if (ql2xmdenable) {
+ if (qla82xx_md_collect(vha))
+ ql_log(ql_log_warn, vha, 0xb02c,
+ "Not able to collect minidump.\n");
+ } else
+ ql_log(ql_log_warn, vha, 0xb04f,
+ "Minidump disabled.\n");
+ }
+}
+
+int
+qla82xx_check_md_needed(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint16_t fw_major_version, fw_minor_version, fw_subminor_version;
+ int rval = QLA_SUCCESS;
+
+ fw_major_version = ha->fw_major_version;
+ fw_minor_version = ha->fw_minor_version;
+ fw_subminor_version = ha->fw_subminor_version;
+
+ rval = qla2x00_get_fw_version(vha);
+ if (rval != QLA_SUCCESS)
+ return rval;
+
+ if (ql2xmdenable) {
+ if (!ha->fw_dumped) {
+ if (fw_major_version != ha->fw_major_version ||
+ fw_minor_version != ha->fw_minor_version ||
+ fw_subminor_version != ha->fw_subminor_version) {
+ ql_log(ql_log_info, vha, 0xb02d,
+ "Firmware version differs "
+ "Previous version: %d:%d:%d - "
+ "New version: %d:%d:%d\n",
+ ha->fw_major_version,
+ ha->fw_minor_version,
+ ha->fw_subminor_version,
+ fw_major_version, fw_minor_version,
+ fw_subminor_version);
+ /* Release MiniDump resources */
+ qla82xx_md_free(vha);
+ /* ALlocate MiniDump resources */
+ qla82xx_md_prep(vha);
+ }
+ } else
+ ql_log(ql_log_info, vha, 0xb02e,
+ "Firmware dump available to retrieve\n");
+ }
+ return rval;
+}
+
+
+int
+qla82xx_check_fw_alive(scsi_qla_host_t *vha)
+{
+ uint32_t fw_heartbeat_counter;
+ int status = 0;
+
+ fw_heartbeat_counter = qla82xx_rd_32(vha->hw,
+ QLA82XX_PEG_ALIVE_COUNTER);
+ /* all 0xff, assume AER/EEH in progress, ignore */
+ if (fw_heartbeat_counter == 0xffffffff) {
+ ql_dbg(ql_dbg_timer, vha, 0x6003,
+ "FW heartbeat counter is 0xffffffff, "
+ "returning status=%d.\n", status);
+ return status;
+ }
+ if (vha->fw_heartbeat_counter == fw_heartbeat_counter) {
+ vha->seconds_since_last_heartbeat++;
+ /* FW not alive after 2 seconds */
+ if (vha->seconds_since_last_heartbeat == 2) {
+ vha->seconds_since_last_heartbeat = 0;
+ status = 1;
+ }
+ } else
+ vha->seconds_since_last_heartbeat = 0;
+ vha->fw_heartbeat_counter = fw_heartbeat_counter;
+ if (status)
+ ql_dbg(ql_dbg_timer, vha, 0x6004,
+ "Returning status=%d.\n", status);
+ return status;
+}
+
+/*
+ * qla82xx_device_state_handler
+ * Main state handler
+ *
+ * Note:
+ * IDC lock must be held upon entry
+ *
+ * Return:
+ * Success : 0
+ * Failed : 1
+ */
+int
+qla82xx_device_state_handler(scsi_qla_host_t *vha)
+{
+ uint32_t dev_state;
+ uint32_t old_dev_state;
+ int rval = QLA_SUCCESS;
+ unsigned long dev_init_timeout;
+ struct qla_hw_data *ha = vha->hw;
+ int loopcount = 0;
+
+ qla82xx_idc_lock(ha);
+ if (!vha->flags.init_done)
+ qla82xx_set_drv_active(vha);
+
+ dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
+ old_dev_state = dev_state;
+ ql_log(ql_log_info, vha, 0x009b,
+ "Device state is 0x%x = %s.\n",
+ dev_state,
+ dev_state < MAX_STATES ? qdev_state(dev_state) : "Unknown");
+
+ /* wait for 30 seconds for device to go ready */
+ dev_init_timeout = jiffies + (ha->nx_dev_init_timeout * HZ);
+
+ while (1) {
+
+ if (time_after_eq(jiffies, dev_init_timeout)) {
+ ql_log(ql_log_fatal, vha, 0x009c,
+ "Device init failed.\n");
+ rval = QLA_FUNCTION_FAILED;
+ break;
+ }
+ dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
+ if (old_dev_state != dev_state) {
+ loopcount = 0;
+ old_dev_state = dev_state;
+ }
+ if (loopcount < 5) {
+ ql_log(ql_log_info, vha, 0x009d,
+ "Device state is 0x%x = %s.\n",
+ dev_state,
+ dev_state < MAX_STATES ? qdev_state(dev_state) :
+ "Unknown");
+ }
+
+ switch (dev_state) {
+ case QLA82XX_DEV_READY:
+ ha->flags.isp82xx_reset_owner = 0;
+ goto exit;
+ case QLA82XX_DEV_COLD:
+ rval = qla82xx_device_bootstrap(vha);
+ break;
+ case QLA82XX_DEV_INITIALIZING:
+ qla82xx_idc_unlock(ha);
+ msleep(1000);
+ qla82xx_idc_lock(ha);
+ break;
+ case QLA82XX_DEV_NEED_RESET:
+ if (!ql2xdontresethba)
+ qla82xx_need_reset_handler(vha);
+ else {
+ qla82xx_idc_unlock(ha);
+ msleep(1000);
+ qla82xx_idc_lock(ha);
+ }
+ dev_init_timeout = jiffies +
+ (ha->nx_dev_init_timeout * HZ);
+ break;
+ case QLA82XX_DEV_NEED_QUIESCENT:
+ qla82xx_need_qsnt_handler(vha);
+ /* Reset timeout value after quiescence handler */
+ dev_init_timeout = jiffies + (ha->nx_dev_init_timeout\
+ * HZ);
+ break;
+ case QLA82XX_DEV_QUIESCENT:
+ /* Owner will exit and other will wait for the state
+ * to get changed
+ */
+ if (ha->flags.quiesce_owner)
+ goto exit;
+
+ qla82xx_idc_unlock(ha);
+ msleep(1000);
+ qla82xx_idc_lock(ha);
+
+ /* Reset timeout value after quiescence handler */
+ dev_init_timeout = jiffies + (ha->nx_dev_init_timeout\
+ * HZ);
+ break;
+ case QLA82XX_DEV_FAILED:
+ qla82xx_dev_failed_handler(vha);
+ rval = QLA_FUNCTION_FAILED;
+ goto exit;
+ default:
+ qla82xx_idc_unlock(ha);
+ msleep(1000);
+ qla82xx_idc_lock(ha);
+ }
+ loopcount++;
+ }
+exit:
+ qla82xx_idc_unlock(ha);
+ return rval;
+}
+
+void qla82xx_clear_pending_mbx(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+
+ if (ha->flags.mbox_busy) {
+ ha->flags.mbox_int = 1;
+ ha->flags.mbox_busy = 0;
+ ql_log(ql_log_warn, vha, 0x6010,
+ "Doing premature completion of mbx command.\n");
+ if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags))
+ complete(&ha->mbx_intr_comp);
+ }
+}
+
+void qla82xx_watchdog(scsi_qla_host_t *vha)
+{
+ uint32_t dev_state, halt_status;
+ struct qla_hw_data *ha = vha->hw;
+
+ /* don't poll if reset is going on */
+ if (!ha->flags.isp82xx_reset_hdlr_active) {
+ dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
+ if (dev_state == QLA82XX_DEV_NEED_RESET &&
+ !test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) {
+ ql_log(ql_log_warn, vha, 0x6001,
+ "Adapter reset needed.\n");
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
+ } else if (dev_state == QLA82XX_DEV_NEED_QUIESCENT &&
+ !test_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags)) {
+ ql_log(ql_log_warn, vha, 0x6002,
+ "Quiescent needed.\n");
+ set_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
+ } else {
+ if (qla82xx_check_fw_alive(vha)) {
+ ql_dbg(ql_dbg_timer, vha, 0x6011,
+ "disabling pause transmit on port 0 & 1.\n");
+ qla82xx_wr_32(ha, QLA82XX_CRB_NIU + 0x98,
+ CRB_NIU_XG_PAUSE_CTL_P0|CRB_NIU_XG_PAUSE_CTL_P1);
+ halt_status = qla82xx_rd_32(ha,
+ QLA82XX_PEG_HALT_STATUS1);
+ ql_log(ql_log_info, vha, 0x6005,
+ "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", halt_status,
+ qla82xx_rd_32(ha, QLA82XX_PEG_HALT_STATUS2),
+ qla82xx_rd_32(ha,
+ QLA82XX_CRB_PEG_NET_0 + 0x3c),
+ qla82xx_rd_32(ha,
+ QLA82XX_CRB_PEG_NET_1 + 0x3c),
+ qla82xx_rd_32(ha,
+ QLA82XX_CRB_PEG_NET_2 + 0x3c),
+ qla82xx_rd_32(ha,
+ QLA82XX_CRB_PEG_NET_3 + 0x3c),
+ qla82xx_rd_32(ha,
+ QLA82XX_CRB_PEG_NET_4 + 0x3c));
+ if (((halt_status & 0x1fffff00) >> 8) == 0x67)
+ ql_log(ql_log_warn, vha, 0xb052,
+ "Firmware aborted with "
+ "error code 0x00006700. Device is "
+ "being reset.\n");
+ if (halt_status & HALT_STATUS_UNRECOVERABLE) {
+ set_bit(ISP_UNRECOVERABLE,
+ &vha->dpc_flags);
+ } else {
+ ql_log(ql_log_info, vha, 0x6006,
+ "Detect abort needed.\n");
+ set_bit(ISP_ABORT_NEEDED,
+ &vha->dpc_flags);
+ }
+ qla2xxx_wake_dpc(vha);
+ ha->flags.isp82xx_fw_hung = 1;
+ ql_log(ql_log_warn, vha, 0x6007, "Firmware hung.\n");
+ qla82xx_clear_pending_mbx(vha);
+ }
+ }
+ }
+}
+
+int qla82xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr)
+{
+ int rval;
+ rval = qla82xx_device_state_handler(vha);
+ return rval;
+}
+
+void
+qla82xx_set_reset_owner(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint32_t dev_state;
+
+ dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE);
+ if (dev_state == QLA82XX_DEV_READY) {
+ ql_log(ql_log_info, vha, 0xb02f,
+ "HW State: NEED RESET\n");
+ qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
+ QLA82XX_DEV_NEED_RESET);
+ ha->flags.isp82xx_reset_owner = 1;
+ ql_dbg(ql_dbg_p3p, vha, 0xb030,
+ "reset_owner is 0x%x\n", ha->portnum);
+ } else
+ ql_log(ql_log_info, vha, 0xb031,
+ "Device state is 0x%x = %s.\n",
+ dev_state,
+ dev_state < MAX_STATES ? qdev_state(dev_state) : "Unknown");
+}
+
+/*
+ * qla82xx_abort_isp
+ * Resets ISP and aborts all outstanding commands.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ *
+ * Returns:
+ * 0 = success
+ */
+int
+qla82xx_abort_isp(scsi_qla_host_t *vha)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (vha->device_flags & DFLG_DEV_FAILED) {
+ ql_log(ql_log_warn, vha, 0x8024,
+ "Device in failed state, exiting.\n");
+ return QLA_SUCCESS;
+ }
+ ha->flags.isp82xx_reset_hdlr_active = 1;
+
+ qla82xx_idc_lock(ha);
+ qla82xx_set_reset_owner(vha);
+ qla82xx_idc_unlock(ha);
+
+ rval = qla82xx_device_state_handler(vha);
+
+ qla82xx_idc_lock(ha);
+ qla82xx_clear_rst_ready(ha);
+ qla82xx_idc_unlock(ha);
+
+ if (rval == QLA_SUCCESS) {
+ ha->flags.isp82xx_fw_hung = 0;
+ ha->flags.isp82xx_reset_hdlr_active = 0;
+ qla82xx_restart_isp(vha);
+ }
+
+ if (rval) {
+ vha->flags.online = 1;
+ if (test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) {
+ if (ha->isp_abort_cnt == 0) {
+ ql_log(ql_log_warn, vha, 0x8027,
+ "ISP error recover failed - board "
+ "disabled.\n");
+ /*
+ * The next call disables the board
+ * completely.
+ */
+ ha->isp_ops->reset_adapter(vha);
+ vha->flags.online = 0;
+ clear_bit(ISP_ABORT_RETRY,
+ &vha->dpc_flags);
+ rval = QLA_SUCCESS;
+ } else { /* schedule another ISP abort */
+ ha->isp_abort_cnt--;
+ ql_log(ql_log_warn, vha, 0x8036,
+ "ISP abort - retry remaining %d.\n",
+ ha->isp_abort_cnt);
+ rval = QLA_FUNCTION_FAILED;
+ }
+ } else {
+ ha->isp_abort_cnt = MAX_RETRIES_OF_ISP_ABORT;
+ ql_dbg(ql_dbg_taskm, vha, 0x8029,
+ "ISP error recovery - retrying (%d) more times.\n",
+ ha->isp_abort_cnt);
+ set_bit(ISP_ABORT_RETRY, &vha->dpc_flags);
+ rval = QLA_FUNCTION_FAILED;
+ }
+ }
+ return rval;
+}
+
+/*
+ * qla82xx_fcoe_ctx_reset
+ * Perform a quick reset and aborts all outstanding commands.
+ * This will only perform an FCoE context reset and avoids a full blown
+ * chip reset.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * is_reset_path = flag for identifying the reset path.
+ *
+ * Returns:
+ * 0 = success
+ */
+int qla82xx_fcoe_ctx_reset(scsi_qla_host_t *vha)
+{
+ int rval = QLA_FUNCTION_FAILED;
+
+ if (vha->flags.online) {
+ /* Abort all outstanding commands, so as to be requeued later */
+ qla2x00_abort_isp_cleanup(vha);
+ }
+
+ /* Stop currently executing firmware.
+ * This will destroy existing FCoE context at the F/W end.
+ */
+ qla2x00_try_to_stop_firmware(vha);
+
+ /* Restart. Creates a new FCoE context on INIT_FIRMWARE. */
+ rval = qla82xx_restart_isp(vha);
+
+ return rval;
+}
+
+/*
+ * qla2x00_wait_for_fcoe_ctx_reset
+ * Wait till the FCoE context is reset.
+ *
+ * Note:
+ * Does context switching here.
+ * Release SPIN_LOCK (if any) before calling this routine.
+ *
+ * Return:
+ * Success (fcoe_ctx reset is done) : 0
+ * Failed (fcoe_ctx reset not completed within max loop timout ) : 1
+ */
+int qla2x00_wait_for_fcoe_ctx_reset(scsi_qla_host_t *vha)
+{
+ int status = QLA_FUNCTION_FAILED;
+ unsigned long wait_reset;
+
+ wait_reset = jiffies + (MAX_LOOP_TIMEOUT * HZ);
+ while ((test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags) ||
+ test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags))
+ && time_before(jiffies, wait_reset)) {
+
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(HZ);
+
+ if (!test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags) &&
+ !test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) {
+ status = QLA_SUCCESS;
+ break;
+ }
+ }
+ ql_dbg(ql_dbg_p3p, vha, 0xb027,
+ "%s: status=%d.\n", __func__, status);
+
+ return status;
+}
+
+void
+qla82xx_chip_reset_cleanup(scsi_qla_host_t *vha)
+{
+ int i;
+ unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
+
+ /* Check if 82XX firmware is alive or not
+ * We may have arrived here from NEED_RESET
+ * detection only
+ */
+ if (!ha->flags.isp82xx_fw_hung) {
+ for (i = 0; i < 2; i++) {
+ msleep(1000);
+ if (qla82xx_check_fw_alive(vha)) {
+ ha->flags.isp82xx_fw_hung = 1;
+ qla82xx_clear_pending_mbx(vha);
+ break;
+ }
+ }
+ }
+ ql_dbg(ql_dbg_init, vha, 0x00b0,
+ "Entered %s fw_hung=%d.\n",
+ __func__, ha->flags.isp82xx_fw_hung);
+
+ /* Abort all commands gracefully if fw NOT hung */
+ if (!ha->flags.isp82xx_fw_hung) {
+ int cnt, que;
+ srb_t *sp;
+ struct req_que *req;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ for (que = 0; que < ha->max_req_queues; que++) {
+ req = ha->req_q_map[que];
+ if (!req)
+ continue;
+ for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+ sp = req->outstanding_cmds[cnt];
+ if (sp) {
+ if (!sp->u.scmd.ctx ||
+ (sp->flags & SRB_FCP_CMND_DMA_VALID)) {
+ spin_unlock_irqrestore(
+ &ha->hardware_lock, flags);
+ if (ha->isp_ops->abort_command(sp)) {
+ ql_log(ql_log_info, vha,
+ 0x00b1,
+ "mbx abort failed.\n");
+ } else {
+ ql_log(ql_log_info, vha,
+ 0x00b2,
+ "mbx abort success.\n");
+ }
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ }
+ }
+ }
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ /* Wait for pending cmds (physical and virtual) to complete */
+ if (!qla2x00_eh_wait_for_pending_commands(vha, 0, 0,
+ WAIT_HOST) == QLA_SUCCESS) {
+ ql_dbg(ql_dbg_init, vha, 0x00b3,
+ "Done wait for "
+ "pending commands.\n");
+ }
+ }
+}
+
+/* Minidump related functions */
+static int
+qla82xx_minidump_process_control(scsi_qla_host_t *vha,
+ qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr)
+{
+ struct qla_hw_data *ha = vha->hw;
+ struct qla82xx_md_entry_crb *crb_entry;
+ uint32_t read_value, opcode, poll_time;
+ uint32_t addr, index, crb_addr;
+ unsigned long wtime;
+ struct qla82xx_md_template_hdr *tmplt_hdr;
+ uint32_t rval = QLA_SUCCESS;
+ int i;
+
+ tmplt_hdr = (struct qla82xx_md_template_hdr *)ha->md_tmplt_hdr;
+ crb_entry = (struct qla82xx_md_entry_crb *)entry_hdr;
+ crb_addr = crb_entry->addr;
+
+ for (i = 0; i < crb_entry->op_count; i++) {
+ opcode = crb_entry->crb_ctrl.opcode;
+ if (opcode & QLA82XX_DBG_OPCODE_WR) {
+ qla82xx_md_rw_32(ha, crb_addr,
+ crb_entry->value_1, 1);
+ opcode &= ~QLA82XX_DBG_OPCODE_WR;
+ }
+
+ if (opcode & QLA82XX_DBG_OPCODE_RW) {
+ read_value = qla82xx_md_rw_32(ha, crb_addr, 0, 0);
+ qla82xx_md_rw_32(ha, crb_addr, read_value, 1);
+ opcode &= ~QLA82XX_DBG_OPCODE_RW;
+ }
+
+ if (opcode & QLA82XX_DBG_OPCODE_AND) {
+ read_value = qla82xx_md_rw_32(ha, crb_addr, 0, 0);
+ read_value &= crb_entry->value_2;
+ opcode &= ~QLA82XX_DBG_OPCODE_AND;
+ if (opcode & QLA82XX_DBG_OPCODE_OR) {
+ read_value |= crb_entry->value_3;
+ opcode &= ~QLA82XX_DBG_OPCODE_OR;
+ }
+ qla82xx_md_rw_32(ha, crb_addr, read_value, 1);
+ }
+
+ if (opcode & QLA82XX_DBG_OPCODE_OR) {
+ read_value = qla82xx_md_rw_32(ha, crb_addr, 0, 0);
+ read_value |= crb_entry->value_3;
+ qla82xx_md_rw_32(ha, crb_addr, read_value, 1);
+ opcode &= ~QLA82XX_DBG_OPCODE_OR;
+ }
+
+ if (opcode & QLA82XX_DBG_OPCODE_POLL) {
+ poll_time = crb_entry->crb_strd.poll_timeout;
+ wtime = jiffies + poll_time;
+ read_value = qla82xx_md_rw_32(ha, crb_addr, 0, 0);
+
+ do {
+ if ((read_value & crb_entry->value_2)
+ == crb_entry->value_1)
+ break;
+ else if (time_after_eq(jiffies, wtime)) {
+ /* capturing dump failed */
+ rval = QLA_FUNCTION_FAILED;
+ break;
+ } else
+ read_value = qla82xx_md_rw_32(ha,
+ crb_addr, 0, 0);
+ } while (1);
+ opcode &= ~QLA82XX_DBG_OPCODE_POLL;
+ }
+
+ if (opcode & QLA82XX_DBG_OPCODE_RDSTATE) {
+ if (crb_entry->crb_strd.state_index_a) {
+ index = crb_entry->crb_strd.state_index_a;
+ addr = tmplt_hdr->saved_state_array[index];
+ } else
+ addr = crb_addr;
+
+ read_value = qla82xx_md_rw_32(ha, addr, 0, 0);
+ index = crb_entry->crb_ctrl.state_index_v;
+ tmplt_hdr->saved_state_array[index] = read_value;
+ opcode &= ~QLA82XX_DBG_OPCODE_RDSTATE;
+ }
+
+ if (opcode & QLA82XX_DBG_OPCODE_WRSTATE) {
+ if (crb_entry->crb_strd.state_index_a) {
+ index = crb_entry->crb_strd.state_index_a;
+ addr = tmplt_hdr->saved_state_array[index];
+ } else
+ addr = crb_addr;
+
+ if (crb_entry->crb_ctrl.state_index_v) {
+ index = crb_entry->crb_ctrl.state_index_v;
+ read_value =
+ tmplt_hdr->saved_state_array[index];
+ } else
+ read_value = crb_entry->value_1;
+
+ qla82xx_md_rw_32(ha, addr, read_value, 1);
+ opcode &= ~QLA82XX_DBG_OPCODE_WRSTATE;
+ }
+
+ if (opcode & QLA82XX_DBG_OPCODE_MDSTATE) {
+ index = crb_entry->crb_ctrl.state_index_v;
+ read_value = tmplt_hdr->saved_state_array[index];
+ read_value <<= crb_entry->crb_ctrl.shl;
+ read_value >>= crb_entry->crb_ctrl.shr;
+ if (crb_entry->value_2)
+ read_value &= crb_entry->value_2;
+ read_value |= crb_entry->value_3;
+ read_value += crb_entry->value_1;
+ tmplt_hdr->saved_state_array[index] = read_value;
+ opcode &= ~QLA82XX_DBG_OPCODE_MDSTATE;
+ }
+ crb_addr += crb_entry->crb_strd.addr_stride;
+ }
+ return rval;
+}
+
+static void
+qla82xx_minidump_process_rdocm(scsi_qla_host_t *vha,
+ qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint32_t r_addr, r_stride, loop_cnt, i, r_value;
+ struct qla82xx_md_entry_rdocm *ocm_hdr;
+ uint32_t *data_ptr = *d_ptr;
+
+ ocm_hdr = (struct qla82xx_md_entry_rdocm *)entry_hdr;
+ r_addr = ocm_hdr->read_addr;
+ r_stride = ocm_hdr->read_addr_stride;
+ loop_cnt = ocm_hdr->op_count;
+
+ for (i = 0; i < loop_cnt; i++) {
+ r_value = RD_REG_DWORD((void *)(r_addr + ha->nx_pcibase));
+ *data_ptr++ = cpu_to_le32(r_value);
+ r_addr += r_stride;
+ }
+ *d_ptr = data_ptr;
+}
+
+static void
+qla82xx_minidump_process_rdmux(scsi_qla_host_t *vha,
+ qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint32_t r_addr, s_stride, s_addr, s_value, loop_cnt, i, r_value;
+ struct qla82xx_md_entry_mux *mux_hdr;
+ uint32_t *data_ptr = *d_ptr;
+
+ mux_hdr = (struct qla82xx_md_entry_mux *)entry_hdr;
+ r_addr = mux_hdr->read_addr;
+ s_addr = mux_hdr->select_addr;
+ s_stride = mux_hdr->select_value_stride;
+ s_value = mux_hdr->select_value;
+ loop_cnt = mux_hdr->op_count;
+
+ for (i = 0; i < loop_cnt; i++) {
+ qla82xx_md_rw_32(ha, s_addr, s_value, 1);
+ r_value = qla82xx_md_rw_32(ha, r_addr, 0, 0);
+ *data_ptr++ = cpu_to_le32(s_value);
+ *data_ptr++ = cpu_to_le32(r_value);
+ s_value += s_stride;
+ }
+ *d_ptr = data_ptr;
+}
+
+static void
+qla82xx_minidump_process_rdcrb(scsi_qla_host_t *vha,
+ qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint32_t r_addr, r_stride, loop_cnt, i, r_value;
+ struct qla82xx_md_entry_crb *crb_hdr;
+ uint32_t *data_ptr = *d_ptr;
+
+ crb_hdr = (struct qla82xx_md_entry_crb *)entry_hdr;
+ r_addr = crb_hdr->addr;
+ r_stride = crb_hdr->crb_strd.addr_stride;
+ loop_cnt = crb_hdr->op_count;
+
+ for (i = 0; i < loop_cnt; i++) {
+ r_value = qla82xx_md_rw_32(ha, r_addr, 0, 0);
+ *data_ptr++ = cpu_to_le32(r_addr);
+ *data_ptr++ = cpu_to_le32(r_value);
+ r_addr += r_stride;
+ }
+ *d_ptr = data_ptr;
+}
+
+static int
+qla82xx_minidump_process_l2tag(scsi_qla_host_t *vha,
+ qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint32_t addr, r_addr, c_addr, t_r_addr;
+ uint32_t i, k, loop_count, t_value, r_cnt, r_value;
+ unsigned long p_wait, w_time, p_mask;
+ uint32_t c_value_w, c_value_r;
+ struct qla82xx_md_entry_cache *cache_hdr;
+ int rval = QLA_FUNCTION_FAILED;
+ uint32_t *data_ptr = *d_ptr;
+
+ cache_hdr = (struct qla82xx_md_entry_cache *)entry_hdr;
+ loop_count = cache_hdr->op_count;
+ r_addr = cache_hdr->read_addr;
+ c_addr = cache_hdr->control_addr;
+ c_value_w = cache_hdr->cache_ctrl.write_value;
+
+ t_r_addr = cache_hdr->tag_reg_addr;
+ t_value = cache_hdr->addr_ctrl.init_tag_value;
+ r_cnt = cache_hdr->read_ctrl.read_addr_cnt;
+ p_wait = cache_hdr->cache_ctrl.poll_wait;
+ p_mask = cache_hdr->cache_ctrl.poll_mask;
+
+ for (i = 0; i < loop_count; i++) {
+ qla82xx_md_rw_32(ha, t_r_addr, t_value, 1);
+ if (c_value_w)
+ qla82xx_md_rw_32(ha, c_addr, c_value_w, 1);
+
+ if (p_mask) {
+ w_time = jiffies + p_wait;
+ do {
+ c_value_r = qla82xx_md_rw_32(ha, c_addr, 0, 0);
+ if ((c_value_r & p_mask) == 0)
+ break;
+ else if (time_after_eq(jiffies, w_time)) {
+ /* capturing dump failed */
+ ql_dbg(ql_dbg_p3p, vha, 0xb032,
+ "c_value_r: 0x%x, poll_mask: 0x%lx, "
+ "w_time: 0x%lx\n",
+ c_value_r, p_mask, w_time);
+ return rval;
+ }
+ } while (1);
+ }
+
+ addr = r_addr;
+ for (k = 0; k < r_cnt; k++) {
+ r_value = qla82xx_md_rw_32(ha, addr, 0, 0);
+ *data_ptr++ = cpu_to_le32(r_value);
+ addr += cache_hdr->read_ctrl.read_addr_stride;
+ }
+ t_value += cache_hdr->addr_ctrl.tag_value_stride;
+ }
+ *d_ptr = data_ptr;
+ return QLA_SUCCESS;
+}
+
+static void
+qla82xx_minidump_process_l1cache(scsi_qla_host_t *vha,
+ qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint32_t addr, r_addr, c_addr, t_r_addr;
+ uint32_t i, k, loop_count, t_value, r_cnt, r_value;
+ uint32_t c_value_w;
+ struct qla82xx_md_entry_cache *cache_hdr;
+ uint32_t *data_ptr = *d_ptr;
+
+ cache_hdr = (struct qla82xx_md_entry_cache *)entry_hdr;
+ loop_count = cache_hdr->op_count;
+ r_addr = cache_hdr->read_addr;
+ c_addr = cache_hdr->control_addr;
+ c_value_w = cache_hdr->cache_ctrl.write_value;
+
+ t_r_addr = cache_hdr->tag_reg_addr;
+ t_value = cache_hdr->addr_ctrl.init_tag_value;
+ r_cnt = cache_hdr->read_ctrl.read_addr_cnt;
+
+ for (i = 0; i < loop_count; i++) {
+ qla82xx_md_rw_32(ha, t_r_addr, t_value, 1);
+ qla82xx_md_rw_32(ha, c_addr, c_value_w, 1);
+ addr = r_addr;
+ for (k = 0; k < r_cnt; k++) {
+ r_value = qla82xx_md_rw_32(ha, addr, 0, 0);
+ *data_ptr++ = cpu_to_le32(r_value);
+ addr += cache_hdr->read_ctrl.read_addr_stride;
+ }
+ t_value += cache_hdr->addr_ctrl.tag_value_stride;
+ }
+ *d_ptr = data_ptr;
+}
+
+static void
+qla82xx_minidump_process_queue(scsi_qla_host_t *vha,
+ qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint32_t s_addr, r_addr;
+ uint32_t r_stride, r_value, r_cnt, qid = 0;
+ uint32_t i, k, loop_cnt;
+ struct qla82xx_md_entry_queue *q_hdr;
+ uint32_t *data_ptr = *d_ptr;
+
+ q_hdr = (struct qla82xx_md_entry_queue *)entry_hdr;
+ s_addr = q_hdr->select_addr;
+ r_cnt = q_hdr->rd_strd.read_addr_cnt;
+ r_stride = q_hdr->rd_strd.read_addr_stride;
+ loop_cnt = q_hdr->op_count;
+
+ for (i = 0; i < loop_cnt; i++) {
+ qla82xx_md_rw_32(ha, s_addr, qid, 1);
+ r_addr = q_hdr->read_addr;
+ for (k = 0; k < r_cnt; k++) {
+ r_value = qla82xx_md_rw_32(ha, r_addr, 0, 0);
+ *data_ptr++ = cpu_to_le32(r_value);
+ r_addr += r_stride;
+ }
+ qid += q_hdr->q_strd.queue_id_stride;
+ }
+ *d_ptr = data_ptr;
+}
+
+static void
+qla82xx_minidump_process_rdrom(scsi_qla_host_t *vha,
+ qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint32_t r_addr, r_value;
+ uint32_t i, loop_cnt;
+ struct qla82xx_md_entry_rdrom *rom_hdr;
+ uint32_t *data_ptr = *d_ptr;
+
+ rom_hdr = (struct qla82xx_md_entry_rdrom *)entry_hdr;
+ r_addr = rom_hdr->read_addr;
+ loop_cnt = rom_hdr->read_data_size/sizeof(uint32_t);
+
+ for (i = 0; i < loop_cnt; i++) {
+ qla82xx_md_rw_32(ha, MD_DIRECT_ROM_WINDOW,
+ (r_addr & 0xFFFF0000), 1);
+ r_value = qla82xx_md_rw_32(ha,
+ MD_DIRECT_ROM_READ_BASE +
+ (r_addr & 0x0000FFFF), 0, 0);
+ *data_ptr++ = cpu_to_le32(r_value);
+ r_addr += sizeof(uint32_t);
+ }
+ *d_ptr = data_ptr;
+}
+
+static int
+qla82xx_minidump_process_rdmem(scsi_qla_host_t *vha,
+ qla82xx_md_entry_hdr_t *entry_hdr, uint32_t **d_ptr)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint32_t r_addr, r_value, r_data;
+ uint32_t i, j, loop_cnt;
+ struct qla82xx_md_entry_rdmem *m_hdr;
+ unsigned long flags;
+ int rval = QLA_FUNCTION_FAILED;
+ uint32_t *data_ptr = *d_ptr;
+
+ m_hdr = (struct qla82xx_md_entry_rdmem *)entry_hdr;
+ r_addr = m_hdr->read_addr;
+ loop_cnt = m_hdr->read_data_size/16;
+
+ if (r_addr & 0xf) {
+ ql_log(ql_log_warn, vha, 0xb033,
+ "Read addr 0x%x not 16 bytes alligned\n", r_addr);
+ return rval;
+ }
+
+ if (m_hdr->read_data_size % 16) {
+ ql_log(ql_log_warn, vha, 0xb034,
+ "Read data[0x%x] not multiple of 16 bytes\n",
+ m_hdr->read_data_size);
+ return rval;
+ }
+
+ ql_dbg(ql_dbg_p3p, vha, 0xb035,
+ "[%s]: rdmem_addr: 0x%x, read_data_size: 0x%x, loop_cnt: 0x%x\n",
+ __func__, r_addr, m_hdr->read_data_size, loop_cnt);
+
+ write_lock_irqsave(&ha->hw_lock, flags);
+ for (i = 0; i < loop_cnt; i++) {
+ qla82xx_md_rw_32(ha, MD_MIU_TEST_AGT_ADDR_LO, r_addr, 1);
+ r_value = 0;
+ qla82xx_md_rw_32(ha, MD_MIU_TEST_AGT_ADDR_HI, r_value, 1);
+ r_value = MIU_TA_CTL_ENABLE;
+ qla82xx_md_rw_32(ha, MD_MIU_TEST_AGT_CTRL, r_value, 1);
+ r_value = MIU_TA_CTL_START | MIU_TA_CTL_ENABLE;
+ qla82xx_md_rw_32(ha, MD_MIU_TEST_AGT_CTRL, r_value, 1);
+
+ for (j = 0; j < MAX_CTL_CHECK; j++) {
+ r_value = qla82xx_md_rw_32(ha,
+ MD_MIU_TEST_AGT_CTRL, 0, 0);
+ if ((r_value & MIU_TA_CTL_BUSY) == 0)
+ break;
+ }
+
+ if (j >= MAX_CTL_CHECK) {
+ printk_ratelimited(KERN_ERR
+ "failed to read through agent\n");
+ write_unlock_irqrestore(&ha->hw_lock, flags);
+ return rval;
+ }
+
+ for (j = 0; j < 4; j++) {
+ r_data = qla82xx_md_rw_32(ha,
+ MD_MIU_TEST_AGT_RDDATA[j], 0, 0);
+ *data_ptr++ = cpu_to_le32(r_data);
+ }
+ r_addr += 16;
+ }
+ write_unlock_irqrestore(&ha->hw_lock, flags);
+ *d_ptr = data_ptr;
+ return QLA_SUCCESS;
+}
+
+static int
+qla82xx_validate_template_chksum(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint64_t chksum = 0;
+ uint32_t *d_ptr = (uint32_t *)ha->md_tmplt_hdr;
+ int count = ha->md_template_size/sizeof(uint32_t);
+
+ while (count-- > 0)
+ chksum += *d_ptr++;
+ while (chksum >> 32)
+ chksum = (chksum & 0xFFFFFFFF) + (chksum >> 32);
+ return ~chksum;
+}
+
+static void
+qla82xx_mark_entry_skipped(scsi_qla_host_t *vha,
+ qla82xx_md_entry_hdr_t *entry_hdr, int index)
+{
+ entry_hdr->d_ctrl.driver_flags |= QLA82XX_DBG_SKIPPED_FLAG;
+ ql_dbg(ql_dbg_p3p, vha, 0xb036,
+ "Skipping entry[%d]: "
+ "ETYPE[0x%x]-ELEVEL[0x%x]\n",
+ index, entry_hdr->entry_type,
+ entry_hdr->d_ctrl.entry_capture_mask);
+}
+
+int
+qla82xx_md_collect(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ int no_entry_hdr = 0;
+ qla82xx_md_entry_hdr_t *entry_hdr;
+ struct qla82xx_md_template_hdr *tmplt_hdr;
+ uint32_t *data_ptr;
+ uint32_t total_data_size = 0, f_capture_mask, data_collected = 0;
+ int i = 0, rval = QLA_FUNCTION_FAILED;
+
+ tmplt_hdr = (struct qla82xx_md_template_hdr *)ha->md_tmplt_hdr;
+ data_ptr = (uint32_t *)ha->md_dump;
+
+ if (ha->fw_dumped) {
+ ql_log(ql_log_warn, vha, 0xb037,
+ "Firmware has been previously dumped (%p) "
+ "-- ignoring request.\n", ha->fw_dump);
+ goto md_failed;
+ }
+
+ ha->fw_dumped = 0;
+
+ if (!ha->md_tmplt_hdr || !ha->md_dump) {
+ ql_log(ql_log_warn, vha, 0xb038,
+ "Memory not allocated for minidump capture\n");
+ goto md_failed;
+ }
+
+ if (qla82xx_validate_template_chksum(vha)) {
+ ql_log(ql_log_info, vha, 0xb039,
+ "Template checksum validation error\n");
+ goto md_failed;
+ }
+
+ no_entry_hdr = tmplt_hdr->num_of_entries;
+ ql_dbg(ql_dbg_p3p, vha, 0xb03a,
+ "No of entry headers in Template: 0x%x\n", no_entry_hdr);
+
+ ql_dbg(ql_dbg_p3p, vha, 0xb03b,
+ "Capture Mask obtained: 0x%x\n", tmplt_hdr->capture_debug_level);
+
+ f_capture_mask = tmplt_hdr->capture_debug_level & 0xFF;
+
+ /* Validate whether required debug level is set */
+ if ((f_capture_mask & 0x3) != 0x3) {
+ ql_log(ql_log_warn, vha, 0xb03c,
+ "Minimum required capture mask[0x%x] level not set\n",
+ f_capture_mask);
+ goto md_failed;
+ }
+ tmplt_hdr->driver_capture_mask = ql2xmdcapmask;
+
+ tmplt_hdr->driver_info[0] = vha->host_no;
+ tmplt_hdr->driver_info[1] = (QLA_DRIVER_MAJOR_VER << 24) |
+ (QLA_DRIVER_MINOR_VER << 16) | (QLA_DRIVER_PATCH_VER << 8) |
+ QLA_DRIVER_BETA_VER;
+
+ total_data_size = ha->md_dump_size;
+
+ ql_dbg(ql_dbg_p3p, vha, 0xb03d,
+ "Total minidump data_size 0x%x to be captured\n", total_data_size);
+
+ /* Check whether template obtained is valid */
+ if (tmplt_hdr->entry_type != QLA82XX_TLHDR) {
+ ql_log(ql_log_warn, vha, 0xb04e,
+ "Bad template header entry type: 0x%x obtained\n",
+ tmplt_hdr->entry_type);
+ goto md_failed;
+ }
+
+ entry_hdr = (qla82xx_md_entry_hdr_t *) \
+ (((uint8_t *)ha->md_tmplt_hdr) + tmplt_hdr->first_entry_offset);
+
+ /* Walk through the entry headers */
+ for (i = 0; i < no_entry_hdr; i++) {
+
+ if (data_collected > total_data_size) {
+ ql_log(ql_log_warn, vha, 0xb03e,
+ "More MiniDump data collected: [0x%x]\n",
+ data_collected);
+ goto md_failed;
+ }
+
+ if (!(entry_hdr->d_ctrl.entry_capture_mask &
+ ql2xmdcapmask)) {
+ entry_hdr->d_ctrl.driver_flags |=
+ QLA82XX_DBG_SKIPPED_FLAG;
+ ql_dbg(ql_dbg_p3p, vha, 0xb03f,
+ "Skipping entry[%d]: "
+ "ETYPE[0x%x]-ELEVEL[0x%x]\n",
+ i, entry_hdr->entry_type,
+ entry_hdr->d_ctrl.entry_capture_mask);
+ goto skip_nxt_entry;
+ }
+
+ ql_dbg(ql_dbg_p3p, vha, 0xb040,
+ "[%s]: data ptr[%d]: %p, entry_hdr: %p\n"
+ "entry_type: 0x%x, captrue_mask: 0x%x\n",
+ __func__, i, data_ptr, entry_hdr,
+ entry_hdr->entry_type,
+ entry_hdr->d_ctrl.entry_capture_mask);
+
+ ql_dbg(ql_dbg_p3p, vha, 0xb041,
+ "Data collected: [0x%x], Dump size left:[0x%x]\n",
+ data_collected, (ha->md_dump_size - data_collected));
+
+ /* Decode the entry type and take
+ * required action to capture debug data */
+ switch (entry_hdr->entry_type) {
+ case QLA82XX_RDEND:
+ qla82xx_mark_entry_skipped(vha, entry_hdr, i);
+ break;
+ case QLA82XX_CNTRL:
+ rval = qla82xx_minidump_process_control(vha,
+ entry_hdr, &data_ptr);
+ if (rval != QLA_SUCCESS) {
+ qla82xx_mark_entry_skipped(vha, entry_hdr, i);
+ goto md_failed;
+ }
+ break;
+ case QLA82XX_RDCRB:
+ qla82xx_minidump_process_rdcrb(vha,
+ entry_hdr, &data_ptr);
+ break;
+ case QLA82XX_RDMEM:
+ rval = qla82xx_minidump_process_rdmem(vha,
+ entry_hdr, &data_ptr);
+ if (rval != QLA_SUCCESS) {
+ qla82xx_mark_entry_skipped(vha, entry_hdr, i);
+ goto md_failed;
+ }
+ break;
+ case QLA82XX_BOARD:
+ case QLA82XX_RDROM:
+ qla82xx_minidump_process_rdrom(vha,
+ entry_hdr, &data_ptr);
+ break;
+ case QLA82XX_L2DTG:
+ case QLA82XX_L2ITG:
+ case QLA82XX_L2DAT:
+ case QLA82XX_L2INS:
+ rval = qla82xx_minidump_process_l2tag(vha,
+ entry_hdr, &data_ptr);
+ if (rval != QLA_SUCCESS) {
+ qla82xx_mark_entry_skipped(vha, entry_hdr, i);
+ goto md_failed;
+ }
+ break;
+ case QLA82XX_L1DAT:
+ case QLA82XX_L1INS:
+ qla82xx_minidump_process_l1cache(vha,
+ entry_hdr, &data_ptr);
+ break;
+ case QLA82XX_RDOCM:
+ qla82xx_minidump_process_rdocm(vha,
+ entry_hdr, &data_ptr);
+ break;
+ case QLA82XX_RDMUX:
+ qla82xx_minidump_process_rdmux(vha,
+ entry_hdr, &data_ptr);
+ break;
+ case QLA82XX_QUEUE:
+ qla82xx_minidump_process_queue(vha,
+ entry_hdr, &data_ptr);
+ break;
+ case QLA82XX_RDNOP:
+ default:
+ qla82xx_mark_entry_skipped(vha, entry_hdr, i);
+ break;
+ }
+
+ ql_dbg(ql_dbg_p3p, vha, 0xb042,
+ "[%s]: data ptr[%d]: %p\n", __func__, i, data_ptr);
+
+ data_collected = (uint8_t *)data_ptr -
+ (uint8_t *)ha->md_dump;
+skip_nxt_entry:
+ entry_hdr = (qla82xx_md_entry_hdr_t *) \
+ (((uint8_t *)entry_hdr) + entry_hdr->entry_size);
+ }
+
+ if (data_collected != total_data_size) {
+ ql_dbg(ql_dbg_p3p, vha, 0xb043,
+ "MiniDump data mismatch: Data collected: [0x%x],"
+ "total_data_size:[0x%x]\n",
+ data_collected, total_data_size);
+ goto md_failed;
+ }
+
+ ql_log(ql_log_info, vha, 0xb044,
+ "Firmware dump saved to temp buffer (%ld/%p %ld/%p).\n",
+ vha->host_no, ha->md_tmplt_hdr, vha->host_no, ha->md_dump);
+ ha->fw_dumped = 1;
+ qla2x00_post_uevent_work(vha, QLA_UEVENT_CODE_FW_DUMP);
+
+md_failed:
+ return rval;
+}
+
+int
+qla82xx_md_alloc(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ int i, k;
+ struct qla82xx_md_template_hdr *tmplt_hdr;
+
+ tmplt_hdr = (struct qla82xx_md_template_hdr *)ha->md_tmplt_hdr;
+
+ if (ql2xmdcapmask < 0x3 || ql2xmdcapmask > 0x7F) {
+ ql2xmdcapmask = tmplt_hdr->capture_debug_level & 0xFF;
+ ql_log(ql_log_info, vha, 0xb045,
+ "Forcing driver capture mask to firmware default capture mask: 0x%x.\n",
+ ql2xmdcapmask);
+ }
+
+ for (i = 0x2, k = 1; (i & QLA82XX_DEFAULT_CAP_MASK); i <<= 1, k++) {
+ if (i & ql2xmdcapmask)
+ ha->md_dump_size += tmplt_hdr->capture_size_array[k];
+ }
+
+ if (ha->md_dump) {
+ ql_log(ql_log_warn, vha, 0xb046,
+ "Firmware dump previously allocated.\n");
+ return 1;
+ }
+
+ ha->md_dump = vmalloc(ha->md_dump_size);
+ if (ha->md_dump == NULL) {
+ ql_log(ql_log_warn, vha, 0xb047,
+ "Unable to allocate memory for Minidump size "
+ "(0x%x).\n", ha->md_dump_size);
+ return 1;
+ }
+ return 0;
+}
+
+void
+qla82xx_md_free(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+
+ /* Release the template header allocated */
+ if (ha->md_tmplt_hdr) {
+ ql_log(ql_log_info, vha, 0xb048,
+ "Free MiniDump template: %p, size (%d KB)\n",
+ ha->md_tmplt_hdr, ha->md_template_size / 1024);
+ dma_free_coherent(&ha->pdev->dev, ha->md_template_size,
+ ha->md_tmplt_hdr, ha->md_tmplt_hdr_dma);
+ ha->md_tmplt_hdr = 0;
+ }
+
+ /* Release the template data buffer allocated */
+ if (ha->md_dump) {
+ ql_log(ql_log_info, vha, 0xb049,
+ "Free MiniDump memory: %p, size (%d KB)\n",
+ ha->md_dump, ha->md_dump_size / 1024);
+ vfree(ha->md_dump);
+ ha->md_dump_size = 0;
+ ha->md_dump = 0;
+ }
+}
+
+void
+qla82xx_md_prep(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ int rval;
+
+ /* Get Minidump template size */
+ rval = qla82xx_md_get_template_size(vha);
+ if (rval == QLA_SUCCESS) {
+ ql_log(ql_log_info, vha, 0xb04a,
+ "MiniDump Template size obtained (%d KB)\n",
+ ha->md_template_size / 1024);
+
+ /* Get Minidump template */
+ rval = qla82xx_md_get_template(vha);
+ if (rval == QLA_SUCCESS) {
+ ql_dbg(ql_dbg_p3p, vha, 0xb04b,
+ "MiniDump Template obtained\n");
+
+ /* Allocate memory for minidump */
+ rval = qla82xx_md_alloc(vha);
+ if (rval == QLA_SUCCESS)
+ ql_log(ql_log_info, vha, 0xb04c,
+ "MiniDump memory allocated (%d KB)\n",
+ ha->md_dump_size / 1024);
+ else {
+ ql_log(ql_log_info, vha, 0xb04d,
+ "Free MiniDump template: %p, size: (%d KB)\n",
+ ha->md_tmplt_hdr,
+ ha->md_template_size / 1024);
+ dma_free_coherent(&ha->pdev->dev,
+ ha->md_template_size,
+ ha->md_tmplt_hdr, ha->md_tmplt_hdr_dma);
+ ha->md_tmplt_hdr = 0;
+ }
+
+ }
+ }
+}
+
+int
+qla82xx_beacon_on(struct scsi_qla_host *vha)
+{
+
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ qla82xx_idc_lock(ha);
+ rval = qla82xx_mbx_beacon_ctl(vha, 1);
+
+ if (rval) {
+ ql_log(ql_log_warn, vha, 0xb050,
+ "mbx set led config failed in %s\n", __func__);
+ goto exit;
+ }
+ ha->beacon_blink_led = 1;
+exit:
+ qla82xx_idc_unlock(ha);
+ return rval;
+}
+
+int
+qla82xx_beacon_off(struct scsi_qla_host *vha)
+{
+
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+ qla82xx_idc_lock(ha);
+ rval = qla82xx_mbx_beacon_ctl(vha, 0);
+
+ if (rval) {
+ ql_log(ql_log_warn, vha, 0xb051,
+ "mbx set led config failed in %s\n", __func__);
+ goto exit;
+ }
+ ha->beacon_blink_led = 0;
+exit:
+ qla82xx_idc_unlock(ha);
+ return rval;
+}
diff --git a/drivers/scsi/qla2xxx/qla_nx.h b/drivers/scsi/qla2xxx/qla_nx.h
new file mode 100644
index 00000000..4ac50e27
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_nx.h
@@ -0,0 +1,1181 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2003-2011 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#ifndef __QLA_NX_H
+#define __QLA_NX_H
+
+/*
+ * Following are the states of the Phantom. Phantom will set them and
+ * Host will read to check if the fields are correct.
+*/
+#define PHAN_INITIALIZE_FAILED 0xffff
+#define PHAN_INITIALIZE_COMPLETE 0xff01
+
+/* Host writes the following to notify that it has done the init-handshake */
+#define PHAN_INITIALIZE_ACK 0xf00f
+#define PHAN_PEG_RCV_INITIALIZED 0xff01
+
+/*CRB_RELATED*/
+#define QLA82XX_CRB_BASE QLA82XX_CAM_RAM(0x200)
+#define QLA82XX_REG(X) (QLA82XX_CRB_BASE+(X))
+
+#define CRB_CMDPEG_STATE QLA82XX_REG(0x50)
+#define CRB_RCVPEG_STATE QLA82XX_REG(0x13c)
+#define BOOT_LOADER_DIMM_STATUS QLA82XX_REG(0x54)
+#define CRB_DMA_SHIFT QLA82XX_REG(0xcc)
+#define QLA82XX_DMA_SHIFT_VALUE 0x55555555
+
+#define QLA82XX_HW_H0_CH_HUB_ADR 0x05
+#define QLA82XX_HW_H1_CH_HUB_ADR 0x0E
+#define QLA82XX_HW_H2_CH_HUB_ADR 0x03
+#define QLA82XX_HW_H3_CH_HUB_ADR 0x01
+#define QLA82XX_HW_H4_CH_HUB_ADR 0x06
+#define QLA82XX_HW_H5_CH_HUB_ADR 0x07
+#define QLA82XX_HW_H6_CH_HUB_ADR 0x08
+
+/* Hub 0 */
+#define QLA82XX_HW_MN_CRB_AGT_ADR 0x15
+#define QLA82XX_HW_MS_CRB_AGT_ADR 0x25
+
+/* Hub 1 */
+#define QLA82XX_HW_PS_CRB_AGT_ADR 0x73
+#define QLA82XX_HW_QMS_CRB_AGT_ADR 0x00
+#define QLA82XX_HW_RPMX3_CRB_AGT_ADR 0x0b
+#define QLA82XX_HW_SQGS0_CRB_AGT_ADR 0x01
+#define QLA82XX_HW_SQGS1_CRB_AGT_ADR 0x02
+#define QLA82XX_HW_SQGS2_CRB_AGT_ADR 0x03
+#define QLA82XX_HW_SQGS3_CRB_AGT_ADR 0x04
+#define QLA82XX_HW_C2C0_CRB_AGT_ADR 0x58
+#define QLA82XX_HW_C2C1_CRB_AGT_ADR 0x59
+#define QLA82XX_HW_C2C2_CRB_AGT_ADR 0x5a
+#define QLA82XX_HW_RPMX2_CRB_AGT_ADR 0x0a
+#define QLA82XX_HW_RPMX4_CRB_AGT_ADR 0x0c
+#define QLA82XX_HW_RPMX7_CRB_AGT_ADR 0x0f
+#define QLA82XX_HW_RPMX9_CRB_AGT_ADR 0x12
+#define QLA82XX_HW_SMB_CRB_AGT_ADR 0x18
+
+/* Hub 2 */
+#define QLA82XX_HW_NIU_CRB_AGT_ADR 0x31
+#define QLA82XX_HW_I2C0_CRB_AGT_ADR 0x19
+#define QLA82XX_HW_I2C1_CRB_AGT_ADR 0x29
+
+#define QLA82XX_HW_SN_CRB_AGT_ADR 0x10
+#define QLA82XX_HW_I2Q_CRB_AGT_ADR 0x20
+#define QLA82XX_HW_LPC_CRB_AGT_ADR 0x22
+#define QLA82XX_HW_ROMUSB_CRB_AGT_ADR 0x21
+#define QLA82XX_HW_QM_CRB_AGT_ADR 0x66
+#define QLA82XX_HW_SQG0_CRB_AGT_ADR 0x60
+#define QLA82XX_HW_SQG1_CRB_AGT_ADR 0x61
+#define QLA82XX_HW_SQG2_CRB_AGT_ADR 0x62
+#define QLA82XX_HW_SQG3_CRB_AGT_ADR 0x63
+#define QLA82XX_HW_RPMX1_CRB_AGT_ADR 0x09
+#define QLA82XX_HW_RPMX5_CRB_AGT_ADR 0x0d
+#define QLA82XX_HW_RPMX6_CRB_AGT_ADR 0x0e
+#define QLA82XX_HW_RPMX8_CRB_AGT_ADR 0x11
+
+/* Hub 3 */
+#define QLA82XX_HW_PH_CRB_AGT_ADR 0x1A
+#define QLA82XX_HW_SRE_CRB_AGT_ADR 0x50
+#define QLA82XX_HW_EG_CRB_AGT_ADR 0x51
+#define QLA82XX_HW_RPMX0_CRB_AGT_ADR 0x08
+
+/* Hub 4 */
+#define QLA82XX_HW_PEGN0_CRB_AGT_ADR 0x40
+#define QLA82XX_HW_PEGN1_CRB_AGT_ADR 0x41
+#define QLA82XX_HW_PEGN2_CRB_AGT_ADR 0x42
+#define QLA82XX_HW_PEGN3_CRB_AGT_ADR 0x43
+#define QLA82XX_HW_PEGNI_CRB_AGT_ADR 0x44
+#define QLA82XX_HW_PEGND_CRB_AGT_ADR 0x45
+#define QLA82XX_HW_PEGNC_CRB_AGT_ADR 0x46
+#define QLA82XX_HW_PEGR0_CRB_AGT_ADR 0x47
+#define QLA82XX_HW_PEGR1_CRB_AGT_ADR 0x48
+#define QLA82XX_HW_PEGR2_CRB_AGT_ADR 0x49
+#define QLA82XX_HW_PEGR3_CRB_AGT_ADR 0x4a
+#define QLA82XX_HW_PEGN4_CRB_AGT_ADR 0x4b
+
+/* Hub 5 */
+#define QLA82XX_HW_PEGS0_CRB_AGT_ADR 0x40
+#define QLA82XX_HW_PEGS1_CRB_AGT_ADR 0x41
+#define QLA82XX_HW_PEGS2_CRB_AGT_ADR 0x42
+#define QLA82XX_HW_PEGS3_CRB_AGT_ADR 0x43
+#define QLA82XX_HW_PEGSI_CRB_AGT_ADR 0x44
+#define QLA82XX_HW_PEGSD_CRB_AGT_ADR 0x45
+#define QLA82XX_HW_PEGSC_CRB_AGT_ADR 0x46
+
+/* Hub 6 */
+#define QLA82XX_HW_CAS0_CRB_AGT_ADR 0x46
+#define QLA82XX_HW_CAS1_CRB_AGT_ADR 0x47
+#define QLA82XX_HW_CAS2_CRB_AGT_ADR 0x48
+#define QLA82XX_HW_CAS3_CRB_AGT_ADR 0x49
+#define QLA82XX_HW_NCM_CRB_AGT_ADR 0x16
+#define QLA82XX_HW_TMR_CRB_AGT_ADR 0x17
+#define QLA82XX_HW_XDMA_CRB_AGT_ADR 0x05
+#define QLA82XX_HW_OCM0_CRB_AGT_ADR 0x06
+#define QLA82XX_HW_OCM1_CRB_AGT_ADR 0x07
+
+/* This field defines PCI/X adr [25:20] of agents on the CRB */
+/* */
+#define QLA82XX_HW_PX_MAP_CRB_PH 0
+#define QLA82XX_HW_PX_MAP_CRB_PS 1
+#define QLA82XX_HW_PX_MAP_CRB_MN 2
+#define QLA82XX_HW_PX_MAP_CRB_MS 3
+#define QLA82XX_HW_PX_MAP_CRB_SRE 5
+#define QLA82XX_HW_PX_MAP_CRB_NIU 6
+#define QLA82XX_HW_PX_MAP_CRB_QMN 7
+#define QLA82XX_HW_PX_MAP_CRB_SQN0 8
+#define QLA82XX_HW_PX_MAP_CRB_SQN1 9
+#define QLA82XX_HW_PX_MAP_CRB_SQN2 10
+#define QLA82XX_HW_PX_MAP_CRB_SQN3 11
+#define QLA82XX_HW_PX_MAP_CRB_QMS 12
+#define QLA82XX_HW_PX_MAP_CRB_SQS0 13
+#define QLA82XX_HW_PX_MAP_CRB_SQS1 14
+#define QLA82XX_HW_PX_MAP_CRB_SQS2 15
+#define QLA82XX_HW_PX_MAP_CRB_SQS3 16
+#define QLA82XX_HW_PX_MAP_CRB_PGN0 17
+#define QLA82XX_HW_PX_MAP_CRB_PGN1 18
+#define QLA82XX_HW_PX_MAP_CRB_PGN2 19
+#define QLA82XX_HW_PX_MAP_CRB_PGN3 20
+#define QLA82XX_HW_PX_MAP_CRB_PGN4 QLA82XX_HW_PX_MAP_CRB_SQS2
+#define QLA82XX_HW_PX_MAP_CRB_PGND 21
+#define QLA82XX_HW_PX_MAP_CRB_PGNI 22
+#define QLA82XX_HW_PX_MAP_CRB_PGS0 23
+#define QLA82XX_HW_PX_MAP_CRB_PGS1 24
+#define QLA82XX_HW_PX_MAP_CRB_PGS2 25
+#define QLA82XX_HW_PX_MAP_CRB_PGS3 26
+#define QLA82XX_HW_PX_MAP_CRB_PGSD 27
+#define QLA82XX_HW_PX_MAP_CRB_PGSI 28
+#define QLA82XX_HW_PX_MAP_CRB_SN 29
+#define QLA82XX_HW_PX_MAP_CRB_EG 31
+#define QLA82XX_HW_PX_MAP_CRB_PH2 32
+#define QLA82XX_HW_PX_MAP_CRB_PS2 33
+#define QLA82XX_HW_PX_MAP_CRB_CAM 34
+#define QLA82XX_HW_PX_MAP_CRB_CAS0 35
+#define QLA82XX_HW_PX_MAP_CRB_CAS1 36
+#define QLA82XX_HW_PX_MAP_CRB_CAS2 37
+#define QLA82XX_HW_PX_MAP_CRB_C2C0 38
+#define QLA82XX_HW_PX_MAP_CRB_C2C1 39
+#define QLA82XX_HW_PX_MAP_CRB_TIMR 40
+#define QLA82XX_HW_PX_MAP_CRB_RPMX1 42
+#define QLA82XX_HW_PX_MAP_CRB_RPMX2 43
+#define QLA82XX_HW_PX_MAP_CRB_RPMX3 44
+#define QLA82XX_HW_PX_MAP_CRB_RPMX4 45
+#define QLA82XX_HW_PX_MAP_CRB_RPMX5 46
+#define QLA82XX_HW_PX_MAP_CRB_RPMX6 47
+#define QLA82XX_HW_PX_MAP_CRB_RPMX7 48
+#define QLA82XX_HW_PX_MAP_CRB_XDMA 49
+#define QLA82XX_HW_PX_MAP_CRB_I2Q 50
+#define QLA82XX_HW_PX_MAP_CRB_ROMUSB 51
+#define QLA82XX_HW_PX_MAP_CRB_CAS3 52
+#define QLA82XX_HW_PX_MAP_CRB_RPMX0 53
+#define QLA82XX_HW_PX_MAP_CRB_RPMX8 54
+#define QLA82XX_HW_PX_MAP_CRB_RPMX9 55
+#define QLA82XX_HW_PX_MAP_CRB_OCM0 56
+#define QLA82XX_HW_PX_MAP_CRB_OCM1 57
+#define QLA82XX_HW_PX_MAP_CRB_SMB 58
+#define QLA82XX_HW_PX_MAP_CRB_I2C0 59
+#define QLA82XX_HW_PX_MAP_CRB_I2C1 60
+#define QLA82XX_HW_PX_MAP_CRB_LPC 61
+#define QLA82XX_HW_PX_MAP_CRB_PGNC 62
+#define QLA82XX_HW_PX_MAP_CRB_PGR0 63
+#define QLA82XX_HW_PX_MAP_CRB_PGR1 4
+#define QLA82XX_HW_PX_MAP_CRB_PGR2 30
+#define QLA82XX_HW_PX_MAP_CRB_PGR3 41
+
+/* This field defines CRB adr [31:20] of the agents */
+/* */
+
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_MN ((QLA82XX_HW_H0_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_MN_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_PH ((QLA82XX_HW_H0_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_PH_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_MS ((QLA82XX_HW_H0_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_MS_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_PS ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_PS_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_SS ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_SS_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX3 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_RPMX3_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_QMS ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_QMS_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQS0 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_SQGS0_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQS1 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_SQGS1_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQS2 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_SQGS2_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQS3 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_SQGS3_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_C2C0 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_C2C0_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_C2C1 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_C2C1_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX2 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_RPMX2_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX4 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_RPMX4_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX7 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_RPMX7_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX9 ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_RPMX9_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_SMB ((QLA82XX_HW_H1_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_SMB_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_NIU ((QLA82XX_HW_H2_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_NIU_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_I2C0 ((QLA82XX_HW_H2_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_I2C0_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_I2C1 ((QLA82XX_HW_H2_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_I2C1_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_SRE ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_SRE_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_EG ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_EG_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX0 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_RPMX0_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_QMN ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_QM_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQN0 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_SQG0_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQN1 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_SQG1_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQN2 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_SQG2_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_SQN3 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_SQG3_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX1 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_RPMX1_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX5 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_RPMX5_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX6 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_RPMX6_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_RPMX8 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_RPMX8_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_CAS0 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_CAS0_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_CAS1 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_CAS1_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_CAS2 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_CAS2_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_CAS3 ((QLA82XX_HW_H3_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_CAS3_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGNI ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_PEGNI_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGND ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_PEGND_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGN0 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_PEGN0_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGN1 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_PEGN1_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGN2 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_PEGN2_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGN3 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_PEGN3_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGN4 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_PEGN4_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGNC ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_PEGNC_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGR0 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_PEGR0_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGR1 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_PEGR1_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGR2 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_PEGR2_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGR3 ((QLA82XX_HW_H4_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_PEGR3_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGSI ((QLA82XX_HW_H5_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_PEGSI_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGSD ((QLA82XX_HW_H5_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_PEGSD_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGS0 ((QLA82XX_HW_H5_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_PEGS0_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGS1 ((QLA82XX_HW_H5_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_PEGS1_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGS2 ((QLA82XX_HW_H5_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_PEGS2_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGS3 ((QLA82XX_HW_H5_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_PEGS3_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_PGSC ((QLA82XX_HW_H5_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_PEGSC_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_CAM ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_NCM_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_TIMR ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_TMR_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_XDMA ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_XDMA_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_SN ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_SN_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_I2Q ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_I2Q_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_ROMUSB ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_ROMUSB_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_OCM0 ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_OCM0_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_OCM1 ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_OCM1_CRB_AGT_ADR)
+#define QLA82XX_HW_CRB_HUB_AGT_ADR_LPC ((QLA82XX_HW_H6_CH_HUB_ADR << 7) | \
+ QLA82XX_HW_LPC_CRB_AGT_ADR)
+
+#define ROMUSB_GLB (QLA82XX_CRB_ROMUSB + 0x00000)
+#define QLA82XX_ROMUSB_GLB_PEGTUNE_DONE (ROMUSB_GLB + 0x005c)
+#define QLA82XX_ROMUSB_GLB_STATUS (ROMUSB_GLB + 0x0004)
+#define QLA82XX_ROMUSB_GLB_SW_RESET (ROMUSB_GLB + 0x0008)
+#define QLA82XX_ROMUSB_ROM_ADDRESS (ROMUSB_ROM + 0x0008)
+#define QLA82XX_ROMUSB_ROM_WDATA (ROMUSB_ROM + 0x000c)
+#define QLA82XX_ROMUSB_ROM_ABYTE_CNT (ROMUSB_ROM + 0x0010)
+#define QLA82XX_ROMUSB_ROM_DUMMY_BYTE_CNT (ROMUSB_ROM + 0x0014)
+#define QLA82XX_ROMUSB_ROM_RDATA (ROMUSB_ROM + 0x0018)
+
+#define ROMUSB_ROM (QLA82XX_CRB_ROMUSB + 0x10000)
+#define QLA82XX_ROMUSB_ROM_INSTR_OPCODE (ROMUSB_ROM + 0x0004)
+#define QLA82XX_ROMUSB_GLB_CAS_RST (ROMUSB_GLB + 0x0038)
+
+/* Lock IDs for ROM lock */
+#define ROM_LOCK_DRIVER 0x0d417340
+
+#define QLA82XX_PCI_CRB_WINDOWSIZE 0x00100000 /* all are 1MB windows */
+#define QLA82XX_PCI_CRB_WINDOW(A) \
+ (QLA82XX_PCI_CRBSPACE + (A)*QLA82XX_PCI_CRB_WINDOWSIZE)
+#define QLA82XX_CRB_C2C_0 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_C2C0)
+#define QLA82XX_CRB_C2C_1 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_C2C1)
+#define QLA82XX_CRB_C2C_2 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_C2C2)
+#define QLA82XX_CRB_CAM \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_CAM)
+#define QLA82XX_CRB_CASPER \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_CAS)
+#define QLA82XX_CRB_CASPER_0 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_CAS0)
+#define QLA82XX_CRB_CASPER_1 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_CAS1)
+#define QLA82XX_CRB_CASPER_2 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_CAS2)
+#define QLA82XX_CRB_DDR_MD \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_MS)
+#define QLA82XX_CRB_DDR_NET \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_MN)
+#define QLA82XX_CRB_EPG \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_EG)
+#define QLA82XX_CRB_I2Q \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_I2Q)
+#define QLA82XX_CRB_NIU \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_NIU)
+
+#define QLA82XX_CRB_PCIX_HOST \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PH)
+#define QLA82XX_CRB_PCIX_HOST2 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PH2)
+#define QLA82XX_CRB_PCIX_MD \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PS)
+#define QLA82XX_CRB_PCIE \
+ QLA82XX_CRB_PCIX_MD
+
+/* window 1 pcie slot */
+#define QLA82XX_CRB_PCIE2 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PS2)
+#define QLA82XX_CRB_PEG_MD_0 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGS0)
+#define QLA82XX_CRB_PEG_MD_1 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGS1)
+#define QLA82XX_CRB_PEG_MD_2 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGS2)
+#define QLA82XX_CRB_PEG_MD_3 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGS3)
+#define QLA82XX_CRB_PEG_MD_3 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGS3)
+#define QLA82XX_CRB_PEG_MD_D \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGSD)
+#define QLA82XX_CRB_PEG_MD_I \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGSI)
+#define QLA82XX_CRB_PEG_NET_0 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGN0)
+#define QLA82XX_CRB_PEG_NET_1 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGN1)
+#define QLA82XX_CRB_PEG_NET_2 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGN2)
+#define QLA82XX_CRB_PEG_NET_3 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGN3)
+#define QLA82XX_CRB_PEG_NET_4 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGN4)
+#define QLA82XX_CRB_PEG_NET_D \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGND)
+#define QLA82XX_CRB_PEG_NET_I \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_PGNI)
+#define QLA82XX_CRB_PQM_MD \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_QMS)
+#define QLA82XX_CRB_PQM_NET \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_QMN)
+#define QLA82XX_CRB_QDR_MD \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SS)
+#define QLA82XX_CRB_QDR_NET \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SN)
+#define QLA82XX_CRB_ROMUSB \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_ROMUSB)
+#define QLA82XX_CRB_RPMX_0 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX0)
+#define QLA82XX_CRB_RPMX_1 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX1)
+#define QLA82XX_CRB_RPMX_2 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX2)
+#define QLA82XX_CRB_RPMX_3 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX3)
+#define QLA82XX_CRB_RPMX_4 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX4)
+#define QLA82XX_CRB_RPMX_5 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX5)
+#define QLA82XX_CRB_RPMX_6 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX6)
+#define QLA82XX_CRB_RPMX_7 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_RPMX7)
+#define QLA82XX_CRB_SQM_MD_0 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQS0)
+#define QLA82XX_CRB_SQM_MD_1 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQS1)
+#define QLA82XX_CRB_SQM_MD_2 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQS2)
+#define QLA82XX_CRB_SQM_MD_3 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQS3)
+#define QLA82XX_CRB_SQM_NET_0 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQN0)
+#define QLA82XX_CRB_SQM_NET_1 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQN1)
+#define QLA82XX_CRB_SQM_NET_2 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQN2)
+#define QLA82XX_CRB_SQM_NET_3 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SQN3)
+#define QLA82XX_CRB_SRE \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SRE)
+#define QLA82XX_CRB_TIMER \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_TIMR)
+#define QLA82XX_CRB_XDMA \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_XDMA)
+#define QLA82XX_CRB_I2C0 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_I2C0)
+#define QLA82XX_CRB_I2C1 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_I2C1)
+#define QLA82XX_CRB_OCM0 \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_OCM0)
+#define QLA82XX_CRB_SMB \
+ QLA82XX_PCI_CRB_WINDOW(QLA82XX_HW_PX_MAP_CRB_SMB)
+#define QLA82XX_CRB_MAX \
+ QLA82XX_PCI_CRB_WINDOW(64)
+
+/*
+ * ====================== BASE ADDRESSES ON-CHIP ======================
+ * Base addresses of major components on-chip.
+ * ====================== BASE ADDRESSES ON-CHIP ======================
+ */
+#define QLA82XX_ADDR_DDR_NET (0x0000000000000000ULL)
+#define QLA82XX_ADDR_DDR_NET_MAX (0x000000000fffffffULL)
+
+/* Imbus address bit used to indicate a host address. This bit is
+ * eliminated by the pcie bar and bar select before presentation
+ * over pcie. */
+/* host memory via IMBUS */
+#define QLA82XX_P2_ADDR_PCIE (0x0000000800000000ULL)
+#define QLA82XX_P3_ADDR_PCIE (0x0000008000000000ULL)
+#define QLA82XX_ADDR_PCIE_MAX (0x0000000FFFFFFFFFULL)
+#define QLA82XX_ADDR_OCM0 (0x0000000200000000ULL)
+#define QLA82XX_ADDR_OCM0_MAX (0x00000002000fffffULL)
+#define QLA82XX_ADDR_OCM1 (0x0000000200400000ULL)
+#define QLA82XX_ADDR_OCM1_MAX (0x00000002004fffffULL)
+#define QLA82XX_ADDR_QDR_NET (0x0000000300000000ULL)
+#define QLA82XX_P3_ADDR_QDR_NET_MAX (0x0000000303ffffffULL)
+
+#define QLA82XX_PCI_CRBSPACE (unsigned long)0x06000000
+#define QLA82XX_PCI_DIRECT_CRB (unsigned long)0x04400000
+#define QLA82XX_PCI_CAMQM (unsigned long)0x04800000
+#define QLA82XX_PCI_CAMQM_MAX (unsigned long)0x04ffffff
+#define QLA82XX_PCI_DDR_NET (unsigned long)0x00000000
+#define QLA82XX_PCI_QDR_NET (unsigned long)0x04000000
+#define QLA82XX_PCI_QDR_NET_MAX (unsigned long)0x043fffff
+
+/*
+ * Register offsets for MN
+ */
+#define MIU_CONTROL (0x000)
+#define MIU_TAG (0x004)
+#define MIU_TEST_AGT_CTRL (0x090)
+#define MIU_TEST_AGT_ADDR_LO (0x094)
+#define MIU_TEST_AGT_ADDR_HI (0x098)
+#define MIU_TEST_AGT_WRDATA_LO (0x0a0)
+#define MIU_TEST_AGT_WRDATA_HI (0x0a4)
+#define MIU_TEST_AGT_WRDATA(i) (0x0a0+(4*(i)))
+#define MIU_TEST_AGT_RDDATA_LO (0x0a8)
+#define MIU_TEST_AGT_RDDATA_HI (0x0ac)
+#define MIU_TEST_AGT_RDDATA(i) (0x0a8+(4*(i)))
+#define MIU_TEST_AGT_ADDR_MASK 0xfffffff8
+#define MIU_TEST_AGT_UPPER_ADDR(off) (0)
+
+/* MIU_TEST_AGT_CTRL flags. work for SIU as well */
+#define MIU_TA_CTL_START 1
+#define MIU_TA_CTL_ENABLE 2
+#define MIU_TA_CTL_WRITE 4
+#define MIU_TA_CTL_BUSY 8
+
+/*CAM RAM */
+# define QLA82XX_CAM_RAM_BASE (QLA82XX_CRB_CAM + 0x02000)
+# define QLA82XX_CAM_RAM(reg) (QLA82XX_CAM_RAM_BASE + (reg))
+
+#define QLA82XX_PORT_MODE_ADDR (QLA82XX_CAM_RAM(0x24))
+#define QLA82XX_PEG_HALT_STATUS1 (QLA82XX_CAM_RAM(0xa8))
+#define QLA82XX_PEG_HALT_STATUS2 (QLA82XX_CAM_RAM(0xac))
+#define QLA82XX_PEG_ALIVE_COUNTER (QLA82XX_CAM_RAM(0xb0))
+
+#define QLA82XX_CAMRAM_DB1 (QLA82XX_CAM_RAM(0x1b8))
+#define QLA82XX_CAMRAM_DB2 (QLA82XX_CAM_RAM(0x1bc))
+
+#define HALT_STATUS_UNRECOVERABLE 0x80000000
+#define HALT_STATUS_RECOVERABLE 0x40000000
+
+/* Driver Coexistence Defines */
+#define QLA82XX_CRB_DRV_ACTIVE (QLA82XX_CAM_RAM(0x138))
+#define QLA82XX_CRB_DEV_STATE (QLA82XX_CAM_RAM(0x140))
+#define QLA82XX_CRB_DRV_STATE (QLA82XX_CAM_RAM(0x144))
+#define QLA82XX_CRB_DRV_SCRATCH (QLA82XX_CAM_RAM(0x148))
+#define QLA82XX_CRB_DEV_PART_INFO (QLA82XX_CAM_RAM(0x14c))
+#define QLA82XX_CRB_DRV_IDC_VERSION (QLA82XX_CAM_RAM(0x174))
+
+/* Every driver should use these Device State */
+#define QLA82XX_DEV_COLD 1
+#define QLA82XX_DEV_INITIALIZING 2
+#define QLA82XX_DEV_READY 3
+#define QLA82XX_DEV_NEED_RESET 4
+#define QLA82XX_DEV_NEED_QUIESCENT 5
+#define QLA82XX_DEV_FAILED 6
+#define QLA82XX_DEV_QUIESCENT 7
+#define MAX_STATES 8 /* Increment if new state added */
+
+#define QLA82XX_IDC_VERSION 1
+#define QLA82XX_ROM_DEV_INIT_TIMEOUT 30
+#define QLA82XX_ROM_DRV_RESET_ACK_TIMEOUT 10
+
+#define QLA82XX_ROM_LOCK_ID (QLA82XX_CAM_RAM(0x100))
+#define QLA82XX_CRB_WIN_LOCK_ID (QLA82XX_CAM_RAM(0x124))
+#define QLA82XX_FW_VERSION_MAJOR (QLA82XX_CAM_RAM(0x150))
+#define QLA82XX_FW_VERSION_MINOR (QLA82XX_CAM_RAM(0x154))
+#define QLA82XX_FW_VERSION_SUB (QLA82XX_CAM_RAM(0x158))
+#define QLA82XX_PCIE_REG(reg) (QLA82XX_CRB_PCIE + (reg))
+
+#define PCIE_CHICKEN3 (0x120c8)
+#define PCIE_SETUP_FUNCTION (0x12040)
+#define PCIE_SETUP_FUNCTION2 (0x12048)
+
+#define QLA82XX_PCIX_PS_REG(reg) (QLA82XX_CRB_PCIX_MD + (reg))
+#define QLA82XX_PCIX_PS2_REG(reg) (QLA82XX_CRB_PCIE2 + (reg))
+
+#define PCIE_SEM2_LOCK (0x1c010) /* Flash lock */
+#define PCIE_SEM2_UNLOCK (0x1c014) /* Flash unlock */
+#define PCIE_SEM5_LOCK (0x1c028) /* Coexistence lock */
+#define PCIE_SEM5_UNLOCK (0x1c02c) /* Coexistence unlock */
+#define PCIE_SEM7_LOCK (0x1c038) /* crb win lock */
+#define PCIE_SEM7_UNLOCK (0x1c03c) /* crbwin unlock*/
+
+/* Different drive state */
+#define QLA82XX_DRVST_NOT_RDY 0
+#define QLA82XX_DRVST_RST_RDY 1
+#define QLA82XX_DRVST_QSNT_RDY 2
+
+/* Different drive active state */
+#define QLA82XX_DRV_NOT_ACTIVE 0
+#define QLA82XX_DRV_ACTIVE 1
+
+/*
+ * The PCI VendorID and DeviceID for our board.
+ */
+#define PCI_DEVICE_ID_QLOGIC_ISP8021 0x8021
+
+#define QLA82XX_MSIX_TBL_SPACE 8192
+#define QLA82XX_PCI_REG_MSIX_TBL 0x44
+#define QLA82XX_PCI_MSIX_CONTROL 0x40
+
+struct crb_128M_2M_sub_block_map {
+ unsigned valid;
+ unsigned start_128M;
+ unsigned end_128M;
+ unsigned start_2M;
+};
+
+struct crb_128M_2M_block_map {
+ struct crb_128M_2M_sub_block_map sub_block[16];
+};
+
+struct crb_addr_pair {
+ long addr;
+ long data;
+};
+
+#define ADDR_ERROR ((unsigned long) 0xffffffff)
+#define MAX_CTL_CHECK 1000
+
+/***************************************************************************
+ * PCI related defines.
+ **************************************************************************/
+
+/*
+ * Interrupt related defines.
+ */
+#define PCIX_TARGET_STATUS (0x10118)
+#define PCIX_TARGET_STATUS_F1 (0x10160)
+#define PCIX_TARGET_STATUS_F2 (0x10164)
+#define PCIX_TARGET_STATUS_F3 (0x10168)
+#define PCIX_TARGET_STATUS_F4 (0x10360)
+#define PCIX_TARGET_STATUS_F5 (0x10364)
+#define PCIX_TARGET_STATUS_F6 (0x10368)
+#define PCIX_TARGET_STATUS_F7 (0x1036c)
+
+#define PCIX_TARGET_MASK (0x10128)
+#define PCIX_TARGET_MASK_F1 (0x10170)
+#define PCIX_TARGET_MASK_F2 (0x10174)
+#define PCIX_TARGET_MASK_F3 (0x10178)
+#define PCIX_TARGET_MASK_F4 (0x10370)
+#define PCIX_TARGET_MASK_F5 (0x10374)
+#define PCIX_TARGET_MASK_F6 (0x10378)
+#define PCIX_TARGET_MASK_F7 (0x1037c)
+
+/*
+ * Message Signaled Interrupts
+ */
+#define PCIX_MSI_F0 (0x13000)
+#define PCIX_MSI_F1 (0x13004)
+#define PCIX_MSI_F2 (0x13008)
+#define PCIX_MSI_F3 (0x1300c)
+#define PCIX_MSI_F4 (0x13010)
+#define PCIX_MSI_F5 (0x13014)
+#define PCIX_MSI_F6 (0x13018)
+#define PCIX_MSI_F7 (0x1301c)
+#define PCIX_MSI_F(FUNC) (0x13000 + ((FUNC) * 4))
+#define PCIX_INT_VECTOR (0x10100)
+#define PCIX_INT_MASK (0x10104)
+
+/*
+ * Interrupt state machine and other bits.
+ */
+#define PCIE_MISCCFG_RC (0x1206c)
+
+#define ISR_INT_TARGET_STATUS \
+ (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS))
+#define ISR_INT_TARGET_STATUS_F1 \
+ (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS_F1))
+#define ISR_INT_TARGET_STATUS_F2 \
+ (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS_F2))
+#define ISR_INT_TARGET_STATUS_F3 \
+ (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS_F3))
+#define ISR_INT_TARGET_STATUS_F4 \
+ (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS_F4))
+#define ISR_INT_TARGET_STATUS_F5 \
+ (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS_F5))
+#define ISR_INT_TARGET_STATUS_F6 \
+ (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS_F6))
+#define ISR_INT_TARGET_STATUS_F7 \
+ (QLA82XX_PCIX_PS_REG(PCIX_TARGET_STATUS_F7))
+
+#define ISR_INT_TARGET_MASK \
+ (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK))
+#define ISR_INT_TARGET_MASK_F1 \
+ (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK_F1))
+#define ISR_INT_TARGET_MASK_F2 \
+ (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK_F2))
+#define ISR_INT_TARGET_MASK_F3 \
+ (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK_F3))
+#define ISR_INT_TARGET_MASK_F4 \
+ (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK_F4))
+#define ISR_INT_TARGET_MASK_F5 \
+ (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK_F5))
+#define ISR_INT_TARGET_MASK_F6 \
+ (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK_F6))
+#define ISR_INT_TARGET_MASK_F7 \
+ (QLA82XX_PCIX_PS_REG(PCIX_TARGET_MASK_F7))
+
+#define ISR_INT_VECTOR \
+ (QLA82XX_PCIX_PS_REG(PCIX_INT_VECTOR))
+#define ISR_INT_MASK \
+ (QLA82XX_PCIX_PS_REG(PCIX_INT_MASK))
+#define ISR_INT_STATE_REG \
+ (QLA82XX_PCIX_PS_REG(PCIE_MISCCFG_RC))
+
+#define ISR_MSI_INT_TRIGGER(FUNC) \
+ (QLA82XX_PCIX_PS_REG(PCIX_MSI_F(FUNC)))
+
+#define ISR_IS_LEGACY_INTR_IDLE(VAL) (((VAL) & 0x300) == 0)
+#define ISR_IS_LEGACY_INTR_TRIGGERED(VAL) (((VAL) & 0x300) == 0x200)
+
+/*
+ * PCI Interrupt Vector Values.
+ */
+#define PCIX_INT_VECTOR_BIT_F0 0x0080
+#define PCIX_INT_VECTOR_BIT_F1 0x0100
+#define PCIX_INT_VECTOR_BIT_F2 0x0200
+#define PCIX_INT_VECTOR_BIT_F3 0x0400
+#define PCIX_INT_VECTOR_BIT_F4 0x0800
+#define PCIX_INT_VECTOR_BIT_F5 0x1000
+#define PCIX_INT_VECTOR_BIT_F6 0x2000
+#define PCIX_INT_VECTOR_BIT_F7 0x4000
+
+struct qla82xx_legacy_intr_set {
+ uint32_t int_vec_bit;
+ uint32_t tgt_status_reg;
+ uint32_t tgt_mask_reg;
+ uint32_t pci_int_reg;
+};
+
+#define QLA82XX_LEGACY_INTR_CONFIG \
+{ \
+ { \
+ .int_vec_bit = PCIX_INT_VECTOR_BIT_F0, \
+ .tgt_status_reg = ISR_INT_TARGET_STATUS, \
+ .tgt_mask_reg = ISR_INT_TARGET_MASK, \
+ .pci_int_reg = ISR_MSI_INT_TRIGGER(0) }, \
+ \
+ { \
+ .int_vec_bit = PCIX_INT_VECTOR_BIT_F1, \
+ .tgt_status_reg = ISR_INT_TARGET_STATUS_F1, \
+ .tgt_mask_reg = ISR_INT_TARGET_MASK_F1, \
+ .pci_int_reg = ISR_MSI_INT_TRIGGER(1) }, \
+ \
+ { \
+ .int_vec_bit = PCIX_INT_VECTOR_BIT_F2, \
+ .tgt_status_reg = ISR_INT_TARGET_STATUS_F2, \
+ .tgt_mask_reg = ISR_INT_TARGET_MASK_F2, \
+ .pci_int_reg = ISR_MSI_INT_TRIGGER(2) }, \
+ \
+ { \
+ .int_vec_bit = PCIX_INT_VECTOR_BIT_F3, \
+ .tgt_status_reg = ISR_INT_TARGET_STATUS_F3, \
+ .tgt_mask_reg = ISR_INT_TARGET_MASK_F3, \
+ .pci_int_reg = ISR_MSI_INT_TRIGGER(3) }, \
+ \
+ { \
+ .int_vec_bit = PCIX_INT_VECTOR_BIT_F4, \
+ .tgt_status_reg = ISR_INT_TARGET_STATUS_F4, \
+ .tgt_mask_reg = ISR_INT_TARGET_MASK_F4, \
+ .pci_int_reg = ISR_MSI_INT_TRIGGER(4) }, \
+ \
+ { \
+ .int_vec_bit = PCIX_INT_VECTOR_BIT_F5, \
+ .tgt_status_reg = ISR_INT_TARGET_STATUS_F5, \
+ .tgt_mask_reg = ISR_INT_TARGET_MASK_F5, \
+ .pci_int_reg = ISR_MSI_INT_TRIGGER(5) }, \
+ \
+ { \
+ .int_vec_bit = PCIX_INT_VECTOR_BIT_F6, \
+ .tgt_status_reg = ISR_INT_TARGET_STATUS_F6, \
+ .tgt_mask_reg = ISR_INT_TARGET_MASK_F6, \
+ .pci_int_reg = ISR_MSI_INT_TRIGGER(6) }, \
+ \
+ { \
+ .int_vec_bit = PCIX_INT_VECTOR_BIT_F7, \
+ .tgt_status_reg = ISR_INT_TARGET_STATUS_F7, \
+ .tgt_mask_reg = ISR_INT_TARGET_MASK_F7, \
+ .pci_int_reg = ISR_MSI_INT_TRIGGER(7) }, \
+}
+
+#define BRDCFG_START 0x4000
+#define BOOTLD_START 0x10000
+#define IMAGE_START 0x100000
+#define FLASH_ADDR_START 0x43000
+
+/* Magic number to let user know flash is programmed */
+#define QLA82XX_BDINFO_MAGIC 0x12345678
+#define QLA82XX_FW_MAGIC_OFFSET (BRDCFG_START + 0x128)
+#define FW_SIZE_OFFSET (0x3e840c)
+#define QLA82XX_FW_MIN_SIZE 0x3fffff
+
+/* UNIFIED ROMIMAGE START */
+#define QLA82XX_URI_FW_MIN_SIZE 0xc8000
+#define QLA82XX_URI_DIR_SECT_PRODUCT_TBL 0x0
+#define QLA82XX_URI_DIR_SECT_BOOTLD 0x6
+#define QLA82XX_URI_DIR_SECT_FW 0x7
+
+/* Offsets */
+#define QLA82XX_URI_CHIP_REV_OFF 10
+#define QLA82XX_URI_FLAGS_OFF 11
+#define QLA82XX_URI_BIOS_VERSION_OFF 12
+#define QLA82XX_URI_BOOTLD_IDX_OFF 27
+#define QLA82XX_URI_FIRMWARE_IDX_OFF 29
+
+struct qla82xx_uri_table_desc{
+ uint32_t findex;
+ uint32_t num_entries;
+ uint32_t entry_size;
+ uint32_t reserved[5];
+};
+
+struct qla82xx_uri_data_desc{
+ uint32_t findex;
+ uint32_t size;
+ uint32_t reserved[5];
+};
+
+/* UNIFIED ROMIMAGE END */
+
+#define QLA82XX_UNIFIED_ROMIMAGE 3
+#define QLA82XX_FLASH_ROMIMAGE 4
+#define QLA82XX_UNKNOWN_ROMIMAGE 0xff
+
+#define MIU_TEST_AGT_WRDATA_UPPER_LO (0x0b0)
+#define MIU_TEST_AGT_WRDATA_UPPER_HI (0x0b4)
+
+#ifndef readq
+static inline u64 readq(void __iomem *addr)
+{
+ return readl(addr) | (((u64) readl(addr + 4)) << 32LL);
+}
+#endif
+
+#ifndef writeq
+static inline void writeq(u64 val, void __iomem *addr)
+{
+ writel(((u32) (val)), (addr));
+ writel(((u32) (val >> 32)), (addr + 4));
+}
+#endif
+
+/* Request and response queue size */
+#define REQUEST_ENTRY_CNT_82XX 128 /* Number of request entries. */
+#define RESPONSE_ENTRY_CNT_82XX 128 /* Number of response entries.*/
+
+/*
+ * ISP 8021 I/O Register Set structure definitions.
+ */
+struct device_reg_82xx {
+ uint32_t req_q_out[64]; /* Request Queue out-Pointer (64 * 4) */
+ uint32_t rsp_q_in[64]; /* Response Queue In-Pointer. */
+ uint32_t rsp_q_out[64]; /* Response Queue Out-Pointer. */
+
+ uint16_t mailbox_in[32]; /* Mail box In registers */
+ uint16_t unused_1[32];
+ uint32_t hint; /* Host interrupt register */
+#define HINT_MBX_INT_PENDING BIT_0
+ uint16_t unused_2[62];
+ uint16_t mailbox_out[32]; /* Mail box Out registers */
+ uint32_t unused_3[48];
+
+ uint32_t host_status; /* host status */
+#define HSRX_RISC_INT BIT_15 /* RISC to Host interrupt. */
+#define HSRX_RISC_PAUSED BIT_8 /* RISC Paused. */
+ uint32_t host_int; /* Interrupt status. */
+#define ISRX_NX_RISC_INT BIT_0 /* RISC interrupt. */
+};
+
+struct fcp_cmnd {
+ struct scsi_lun lun;
+ uint8_t crn;
+ uint8_t task_attribute;
+ uint8_t task_management;
+ uint8_t additional_cdb_len;
+ uint8_t cdb[260]; /* 256 for CDB len and 4 for FCP_DL */
+};
+
+struct dsd_dma {
+ struct list_head list;
+ dma_addr_t dsd_list_dma;
+ void *dsd_addr;
+};
+
+#define QLA_DSDS_PER_IOCB 37
+#define QLA_DSD_SIZE 12
+struct ct6_dsd {
+ uint16_t fcp_cmnd_len;
+ dma_addr_t fcp_cmnd_dma;
+ struct fcp_cmnd *fcp_cmnd;
+ int dsd_use_cnt;
+ struct list_head dsd_list;
+};
+
+#define MBC_TOGGLE_INTERRUPT 0x10
+#define MBC_SET_LED_CONFIG 0x125 /* FCoE specific LED control */
+#define MBC_GET_LED_CONFIG 0x126 /* FCoE specific LED control */
+
+/* Flash offset */
+#define FLT_REG_BOOTLOAD_82XX 0x72
+#define FLT_REG_BOOT_CODE_82XX 0x78
+#define FLT_REG_FW_82XX 0x74
+#define FLT_REG_GOLD_FW_82XX 0x75
+#define FLT_REG_VPD_82XX 0x81
+
+#define FA_VPD_SIZE_82XX 0x400
+
+#define FA_FLASH_LAYOUT_ADDR_82 0xFC400
+
+/******************************************************************************
+*
+* Definitions specific to M25P flash
+*
+*******************************************************************************
+* Instructions
+*/
+#define M25P_INSTR_WREN 0x06
+#define M25P_INSTR_WRDI 0x04
+#define M25P_INSTR_RDID 0x9f
+#define M25P_INSTR_RDSR 0x05
+#define M25P_INSTR_WRSR 0x01
+#define M25P_INSTR_READ 0x03
+#define M25P_INSTR_FAST_READ 0x0b
+#define M25P_INSTR_PP 0x02
+#define M25P_INSTR_SE 0xd8
+#define M25P_INSTR_BE 0xc7
+#define M25P_INSTR_DP 0xb9
+#define M25P_INSTR_RES 0xab
+
+/* Minidump related */
+
+/*
+ * Version of the template
+ * 4 Bytes
+ * X.Major.Minor.RELEASE
+ */
+#define QLA82XX_MINIDUMP_VERSION 0x10101
+
+/*
+ * Entry Type Defines
+ */
+#define QLA82XX_RDNOP 0
+#define QLA82XX_RDCRB 1
+#define QLA82XX_RDMUX 2
+#define QLA82XX_QUEUE 3
+#define QLA82XX_BOARD 4
+#define QLA82XX_RDSRE 5
+#define QLA82XX_RDOCM 6
+#define QLA82XX_CACHE 10
+#define QLA82XX_L1DAT 11
+#define QLA82XX_L1INS 12
+#define QLA82XX_L2DTG 21
+#define QLA82XX_L2ITG 22
+#define QLA82XX_L2DAT 23
+#define QLA82XX_L2INS 24
+#define QLA82XX_RDROM 71
+#define QLA82XX_RDMEM 72
+#define QLA82XX_CNTRL 98
+#define QLA82XX_TLHDR 99
+#define QLA82XX_RDEND 255
+
+/*
+ * Opcodes for Control Entries.
+ * These Flags are bit fields.
+ */
+#define QLA82XX_DBG_OPCODE_WR 0x01
+#define QLA82XX_DBG_OPCODE_RW 0x02
+#define QLA82XX_DBG_OPCODE_AND 0x04
+#define QLA82XX_DBG_OPCODE_OR 0x08
+#define QLA82XX_DBG_OPCODE_POLL 0x10
+#define QLA82XX_DBG_OPCODE_RDSTATE 0x20
+#define QLA82XX_DBG_OPCODE_WRSTATE 0x40
+#define QLA82XX_DBG_OPCODE_MDSTATE 0x80
+
+/*
+ * Template Header and Entry Header definitions start here.
+ */
+
+/*
+ * Template Header
+ * Parts of the template header can be modified by the driver.
+ * These include the saved_state_array, capture_debug_level, driver_timestamp
+ */
+
+#define QLA82XX_DBG_STATE_ARRAY_LEN 16
+#define QLA82XX_DBG_CAP_SIZE_ARRAY_LEN 8
+#define QLA82XX_DBG_RSVD_ARRAY_LEN 8
+
+/*
+ * Driver Flags
+ */
+#define QLA82XX_DBG_SKIPPED_FLAG 0x80 /* driver skipped this entry */
+#define QLA82XX_DEFAULT_CAP_MASK 0xFF /* default capture mask */
+
+struct qla82xx_md_template_hdr {
+ uint32_t entry_type;
+ uint32_t first_entry_offset;
+ uint32_t size_of_template;
+ uint32_t capture_debug_level;
+
+ uint32_t num_of_entries;
+ uint32_t version;
+ uint32_t driver_timestamp;
+ uint32_t template_checksum;
+
+ uint32_t driver_capture_mask;
+ uint32_t driver_info[3];
+
+ uint32_t saved_state_array[QLA82XX_DBG_STATE_ARRAY_LEN];
+ uint32_t capture_size_array[QLA82XX_DBG_CAP_SIZE_ARRAY_LEN];
+
+ /* markers_array used to capture some special locations on board */
+ uint32_t markers_array[QLA82XX_DBG_RSVD_ARRAY_LEN];
+ uint32_t num_of_free_entries; /* For internal use */
+ uint32_t free_entry_offset; /* For internal use */
+ uint32_t total_table_size; /* For internal use */
+ uint32_t bkup_table_offset; /* For internal use */
+} __packed;
+
+/*
+ * Entry Header: Common to All Entry Types
+ */
+
+/*
+ * Driver Code is for driver to write some info about the entry.
+ * Currently not used.
+ */
+typedef struct qla82xx_md_entry_hdr {
+ uint32_t entry_type;
+ uint32_t entry_size;
+ uint32_t entry_capture_size;
+ struct {
+ uint8_t entry_capture_mask;
+ uint8_t entry_code;
+ uint8_t driver_code;
+ uint8_t driver_flags;
+ } d_ctrl;
+} __packed qla82xx_md_entry_hdr_t;
+
+/*
+ * Read CRB entry header
+ */
+struct qla82xx_md_entry_crb {
+ qla82xx_md_entry_hdr_t h;
+ uint32_t addr;
+ struct {
+ uint8_t addr_stride;
+ uint8_t state_index_a;
+ uint16_t poll_timeout;
+ } crb_strd;
+
+ uint32_t data_size;
+ uint32_t op_count;
+
+ struct {
+ uint8_t opcode;
+ uint8_t state_index_v;
+ uint8_t shl;
+ uint8_t shr;
+ } crb_ctrl;
+
+ uint32_t value_1;
+ uint32_t value_2;
+ uint32_t value_3;
+} __packed;
+
+/*
+ * Cache entry header
+ */
+struct qla82xx_md_entry_cache {
+ qla82xx_md_entry_hdr_t h;
+
+ uint32_t tag_reg_addr;
+ struct {
+ uint16_t tag_value_stride;
+ uint16_t init_tag_value;
+ } addr_ctrl;
+
+ uint32_t data_size;
+ uint32_t op_count;
+
+ uint32_t control_addr;
+ struct {
+ uint16_t write_value;
+ uint8_t poll_mask;
+ uint8_t poll_wait;
+ } cache_ctrl;
+
+ uint32_t read_addr;
+ struct {
+ uint8_t read_addr_stride;
+ uint8_t read_addr_cnt;
+ uint16_t rsvd_1;
+ } read_ctrl;
+} __packed;
+
+/*
+ * Read OCM
+ */
+struct qla82xx_md_entry_rdocm {
+ qla82xx_md_entry_hdr_t h;
+
+ uint32_t rsvd_0;
+ uint32_t rsvd_1;
+ uint32_t data_size;
+ uint32_t op_count;
+
+ uint32_t rsvd_2;
+ uint32_t rsvd_3;
+ uint32_t read_addr;
+ uint32_t read_addr_stride;
+ uint32_t read_addr_cntrl;
+} __packed;
+
+/*
+ * Read Memory
+ */
+struct qla82xx_md_entry_rdmem {
+ qla82xx_md_entry_hdr_t h;
+ uint32_t rsvd[6];
+ uint32_t read_addr;
+ uint32_t read_data_size;
+} __packed;
+
+/*
+ * Read ROM
+ */
+struct qla82xx_md_entry_rdrom {
+ qla82xx_md_entry_hdr_t h;
+ uint32_t rsvd[6];
+ uint32_t read_addr;
+ uint32_t read_data_size;
+} __packed;
+
+struct qla82xx_md_entry_mux {
+ qla82xx_md_entry_hdr_t h;
+
+ uint32_t select_addr;
+ uint32_t rsvd_0;
+ uint32_t data_size;
+ uint32_t op_count;
+
+ uint32_t select_value;
+ uint32_t select_value_stride;
+ uint32_t read_addr;
+ uint32_t rsvd_1;
+} __packed;
+
+struct qla82xx_md_entry_queue {
+ qla82xx_md_entry_hdr_t h;
+
+ uint32_t select_addr;
+ struct {
+ uint16_t queue_id_stride;
+ uint16_t rsvd_0;
+ } q_strd;
+
+ uint32_t data_size;
+ uint32_t op_count;
+ uint32_t rsvd_1;
+ uint32_t rsvd_2;
+
+ uint32_t read_addr;
+ struct {
+ uint8_t read_addr_stride;
+ uint8_t read_addr_cnt;
+ uint16_t rsvd_3;
+ } rd_strd;
+} __packed;
+
+#define MBC_DIAGNOSTIC_MINIDUMP_TEMPLATE 0x129
+#define RQST_TMPLT_SIZE 0x0
+#define RQST_TMPLT 0x1
+#define MD_DIRECT_ROM_WINDOW 0x42110030
+#define MD_DIRECT_ROM_READ_BASE 0x42150000
+#define MD_MIU_TEST_AGT_CTRL 0x41000090
+#define MD_MIU_TEST_AGT_ADDR_LO 0x41000094
+#define MD_MIU_TEST_AGT_ADDR_HI 0x41000098
+
+static const int MD_MIU_TEST_AGT_RDDATA[] = { 0x410000A8, 0x410000AC,
+ 0x410000B8, 0x410000BC };
+
+#define CRB_NIU_XG_PAUSE_CTL_P0 0x1
+#define CRB_NIU_XG_PAUSE_CTL_P1 0x8
+
+#endif
diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c
new file mode 100644
index 00000000..7db80337
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_os.c
@@ -0,0 +1,4533 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2003-2011 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#include "qla_def.h"
+
+#include <linux/moduleparam.h>
+#include <linux/vmalloc.h>
+#include <linux/delay.h>
+#include <linux/kthread.h>
+#include <linux/mutex.h>
+#include <linux/kobject.h>
+#include <linux/slab.h>
+
+#include <scsi/scsi_tcq.h>
+#include <scsi/scsicam.h>
+#include <scsi/scsi_transport.h>
+#include <scsi/scsi_transport_fc.h>
+
+/*
+ * Driver version
+ */
+char qla2x00_version_str[40];
+
+static int apidev_major;
+
+/*
+ * SRB allocation cache
+ */
+static struct kmem_cache *srb_cachep;
+
+/*
+ * CT6 CTX allocation cache
+ */
+static struct kmem_cache *ctx_cachep;
+/*
+ * error level for logging
+ */
+int ql_errlev = ql_log_all;
+
+int ql2xlogintimeout = 20;
+module_param(ql2xlogintimeout, int, S_IRUGO);
+MODULE_PARM_DESC(ql2xlogintimeout,
+ "Login timeout value in seconds.");
+
+int qlport_down_retry;
+module_param(qlport_down_retry, int, S_IRUGO);
+MODULE_PARM_DESC(qlport_down_retry,
+ "Maximum number of command retries to a port that returns "
+ "a PORT-DOWN status.");
+
+int ql2xplogiabsentdevice;
+module_param(ql2xplogiabsentdevice, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(ql2xplogiabsentdevice,
+ "Option to enable PLOGI to devices that are not present after "
+ "a Fabric scan. This is needed for several broken switches. "
+ "Default is 0 - no PLOGI. 1 - perfom PLOGI.");
+
+int ql2xloginretrycount = 0;
+module_param(ql2xloginretrycount, int, S_IRUGO);
+MODULE_PARM_DESC(ql2xloginretrycount,
+ "Specify an alternate value for the NVRAM login retry count.");
+
+int ql2xallocfwdump = 1;
+module_param(ql2xallocfwdump, int, S_IRUGO);
+MODULE_PARM_DESC(ql2xallocfwdump,
+ "Option to enable allocation of memory for a firmware dump "
+ "during HBA initialization. Memory allocation requirements "
+ "vary by ISP type. Default is 1 - allocate memory.");
+
+int ql2xextended_error_logging;
+module_param(ql2xextended_error_logging, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(ql2xextended_error_logging,
+ "Option to enable extended error logging,\n"
+ "\t\tDefault is 0 - no logging. 0x40000000 - Module Init & Probe.\n"
+ "\t\t0x20000000 - Mailbox Cmnds. 0x10000000 - Device Discovery.\n"
+ "\t\t0x08000000 - IO tracing. 0x04000000 - DPC Thread.\n"
+ "\t\t0x02000000 - Async events. 0x01000000 - Timer routines.\n"
+ "\t\t0x00800000 - User space. 0x00400000 - Task Management.\n"
+ "\t\t0x00200000 - AER/EEH. 0x00100000 - Multi Q.\n"
+ "\t\t0x00080000 - P3P Specific. 0x00040000 - Virtual Port.\n"
+ "\t\t0x00020000 - Buffer Dump. 0x00010000 - Misc.\n"
+ "\t\t0x7fffffff - For enabling all logs, can be too many logs.\n"
+ "\t\t0x1e400000 - Preferred value for capturing essential "
+ "debug information (equivalent to old "
+ "ql2xextended_error_logging=1).\n"
+ "\t\tDo LOGICAL OR of the value to enable more than one level");
+
+int ql2xshiftctondsd = 6;
+module_param(ql2xshiftctondsd, int, S_IRUGO);
+MODULE_PARM_DESC(ql2xshiftctondsd,
+ "Set to control shifting of command type processing "
+ "based on total number of SG elements.");
+
+static void qla2x00_free_device(scsi_qla_host_t *);
+
+int ql2xfdmienable=1;
+module_param(ql2xfdmienable, int, S_IRUGO);
+MODULE_PARM_DESC(ql2xfdmienable,
+ "Enables FDMI registrations. "
+ "0 - no FDMI. Default is 1 - perform FDMI.");
+
+#define MAX_Q_DEPTH 32
+static int ql2xmaxqdepth = MAX_Q_DEPTH;
+module_param(ql2xmaxqdepth, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(ql2xmaxqdepth,
+ "Maximum queue depth to report for target devices.");
+
+/* Do not change the value of this after module load */
+int ql2xenabledif = 0;
+module_param(ql2xenabledif, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(ql2xenabledif,
+ " Enable T10-CRC-DIF "
+ " Default is 0 - No DIF Support. 1 - Enable it"
+ ", 2 - Enable DIF for all types, except Type 0.");
+
+int ql2xenablehba_err_chk = 2;
+module_param(ql2xenablehba_err_chk, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(ql2xenablehba_err_chk,
+ " Enable T10-CRC-DIF Error isolation by HBA:\n"
+ " Default is 1.\n"
+ " 0 -- Error isolation disabled\n"
+ " 1 -- Error isolation enabled only for DIX Type 0\n"
+ " 2 -- Error isolation enabled for all Types\n");
+
+int ql2xiidmaenable=1;
+module_param(ql2xiidmaenable, int, S_IRUGO);
+MODULE_PARM_DESC(ql2xiidmaenable,
+ "Enables iIDMA settings "
+ "Default is 1 - perform iIDMA. 0 - no iIDMA.");
+
+int ql2xmaxqueues = 1;
+module_param(ql2xmaxqueues, int, S_IRUGO);
+MODULE_PARM_DESC(ql2xmaxqueues,
+ "Enables MQ settings "
+ "Default is 1 for single queue. Set it to number "
+ "of queues in MQ mode.");
+
+int ql2xmultique_tag;
+module_param(ql2xmultique_tag, int, S_IRUGO);
+MODULE_PARM_DESC(ql2xmultique_tag,
+ "Enables CPU affinity settings for the driver "
+ "Default is 0 for no affinity of request and response IO. "
+ "Set it to 1 to turn on the cpu affinity.");
+
+int ql2xfwloadbin;
+module_param(ql2xfwloadbin, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(ql2xfwloadbin,
+ "Option to specify location from which to load ISP firmware:.\n"
+ " 2 -- load firmware via the request_firmware() (hotplug).\n"
+ " interface.\n"
+ " 1 -- load firmware from flash.\n"
+ " 0 -- use default semantics.\n");
+
+int ql2xetsenable;
+module_param(ql2xetsenable, int, S_IRUGO);
+MODULE_PARM_DESC(ql2xetsenable,
+ "Enables firmware ETS burst."
+ "Default is 0 - skip ETS enablement.");
+
+int ql2xdbwr = 1;
+module_param(ql2xdbwr, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(ql2xdbwr,
+ "Option to specify scheme for request queue posting.\n"
+ " 0 -- Regular doorbell.\n"
+ " 1 -- CAMRAM doorbell (faster).\n");
+
+int ql2xtargetreset = 1;
+module_param(ql2xtargetreset, int, S_IRUGO);
+MODULE_PARM_DESC(ql2xtargetreset,
+ "Enable target reset."
+ "Default is 1 - use hw defaults.");
+
+int ql2xgffidenable;
+module_param(ql2xgffidenable, int, S_IRUGO);
+MODULE_PARM_DESC(ql2xgffidenable,
+ "Enables GFF_ID checks of port type. "
+ "Default is 0 - Do not use GFF_ID information.");
+
+int ql2xasynctmfenable;
+module_param(ql2xasynctmfenable, int, S_IRUGO);
+MODULE_PARM_DESC(ql2xasynctmfenable,
+ "Enables issue of TM IOCBs asynchronously via IOCB mechanism"
+ "Default is 0 - Issue TM IOCBs via mailbox mechanism.");
+
+int ql2xdontresethba;
+module_param(ql2xdontresethba, int, S_IRUGO|S_IWUSR);
+MODULE_PARM_DESC(ql2xdontresethba,
+ "Option to specify reset behaviour.\n"
+ " 0 (Default) -- Reset on failure.\n"
+ " 1 -- Do not reset on failure.\n");
+
+uint ql2xmaxlun = MAX_LUNS;
+module_param(ql2xmaxlun, uint, S_IRUGO);
+MODULE_PARM_DESC(ql2xmaxlun,
+ "Defines the maximum LU number to register with the SCSI "
+ "midlayer. Default is 65535.");
+
+int ql2xmdcapmask = 0x1F;
+module_param(ql2xmdcapmask, int, S_IRUGO);
+MODULE_PARM_DESC(ql2xmdcapmask,
+ "Set the Minidump driver capture mask level. "
+ "Default is 0x1F - Can be set to 0x3, 0x7, 0xF, 0x1F, 0x7F.");
+
+int ql2xmdenable = 1;
+module_param(ql2xmdenable, int, S_IRUGO);
+MODULE_PARM_DESC(ql2xmdenable,
+ "Enable/disable MiniDump. "
+ "0 - MiniDump disabled. "
+ "1 (Default) - MiniDump enabled.");
+
+/*
+ * SCSI host template entry points
+ */
+static int qla2xxx_slave_configure(struct scsi_device * device);
+static int qla2xxx_slave_alloc(struct scsi_device *);
+static int qla2xxx_scan_finished(struct Scsi_Host *, unsigned long time);
+static void qla2xxx_scan_start(struct Scsi_Host *);
+static void qla2xxx_slave_destroy(struct scsi_device *);
+static int qla2xxx_queuecommand(struct Scsi_Host *h, struct scsi_cmnd *cmd);
+static int qla2xxx_eh_abort(struct scsi_cmnd *);
+static int qla2xxx_eh_device_reset(struct scsi_cmnd *);
+static int qla2xxx_eh_target_reset(struct scsi_cmnd *);
+static int qla2xxx_eh_bus_reset(struct scsi_cmnd *);
+static int qla2xxx_eh_host_reset(struct scsi_cmnd *);
+
+static int qla2x00_change_queue_depth(struct scsi_device *, int, int);
+static int qla2x00_change_queue_type(struct scsi_device *, int);
+
+struct scsi_host_template qla2xxx_driver_template = {
+ .module = THIS_MODULE,
+ .name = QLA2XXX_DRIVER_NAME,
+ .queuecommand = qla2xxx_queuecommand,
+
+ .eh_abort_handler = qla2xxx_eh_abort,
+ .eh_device_reset_handler = qla2xxx_eh_device_reset,
+ .eh_target_reset_handler = qla2xxx_eh_target_reset,
+ .eh_bus_reset_handler = qla2xxx_eh_bus_reset,
+ .eh_host_reset_handler = qla2xxx_eh_host_reset,
+
+ .slave_configure = qla2xxx_slave_configure,
+
+ .slave_alloc = qla2xxx_slave_alloc,
+ .slave_destroy = qla2xxx_slave_destroy,
+ .scan_finished = qla2xxx_scan_finished,
+ .scan_start = qla2xxx_scan_start,
+ .change_queue_depth = qla2x00_change_queue_depth,
+ .change_queue_type = qla2x00_change_queue_type,
+ .this_id = -1,
+ .cmd_per_lun = 3,
+ .use_clustering = ENABLE_CLUSTERING,
+ .sg_tablesize = SG_ALL,
+
+ .max_sectors = 0xFFFF,
+ .shost_attrs = qla2x00_host_attrs,
+};
+
+static struct scsi_transport_template *qla2xxx_transport_template = NULL;
+struct scsi_transport_template *qla2xxx_transport_vport_template = NULL;
+
+/* TODO Convert to inlines
+ *
+ * Timer routines
+ */
+
+__inline__ void
+qla2x00_start_timer(scsi_qla_host_t *vha, void *func, unsigned long interval)
+{
+ init_timer(&vha->timer);
+ vha->timer.expires = jiffies + interval * HZ;
+ vha->timer.data = (unsigned long)vha;
+ vha->timer.function = (void (*)(unsigned long))func;
+ add_timer(&vha->timer);
+ vha->timer_active = 1;
+}
+
+static inline void
+qla2x00_restart_timer(scsi_qla_host_t *vha, unsigned long interval)
+{
+ /* Currently used for 82XX only. */
+ if (vha->device_flags & DFLG_DEV_FAILED) {
+ ql_dbg(ql_dbg_timer, vha, 0x600d,
+ "Device in a failed state, returning.\n");
+ return;
+ }
+
+ mod_timer(&vha->timer, jiffies + interval * HZ);
+}
+
+static __inline__ void
+qla2x00_stop_timer(scsi_qla_host_t *vha)
+{
+ del_timer_sync(&vha->timer);
+ vha->timer_active = 0;
+}
+
+static int qla2x00_do_dpc(void *data);
+
+static void qla2x00_rst_aen(scsi_qla_host_t *);
+
+static int qla2x00_mem_alloc(struct qla_hw_data *, uint16_t, uint16_t,
+ struct req_que **, struct rsp_que **);
+static void qla2x00_free_fw_dump(struct qla_hw_data *);
+static void qla2x00_mem_free(struct qla_hw_data *);
+
+/* -------------------------------------------------------------------------- */
+static int qla2x00_alloc_queues(struct qla_hw_data *ha)
+{
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+ ha->req_q_map = kzalloc(sizeof(struct req_que *) * ha->max_req_queues,
+ GFP_KERNEL);
+ if (!ha->req_q_map) {
+ ql_log(ql_log_fatal, vha, 0x003b,
+ "Unable to allocate memory for request queue ptrs.\n");
+ goto fail_req_map;
+ }
+
+ ha->rsp_q_map = kzalloc(sizeof(struct rsp_que *) * ha->max_rsp_queues,
+ GFP_KERNEL);
+ if (!ha->rsp_q_map) {
+ ql_log(ql_log_fatal, vha, 0x003c,
+ "Unable to allocate memory for response queue ptrs.\n");
+ goto fail_rsp_map;
+ }
+ set_bit(0, ha->rsp_qid_map);
+ set_bit(0, ha->req_qid_map);
+ return 1;
+
+fail_rsp_map:
+ kfree(ha->req_q_map);
+ ha->req_q_map = NULL;
+fail_req_map:
+ return -ENOMEM;
+}
+
+static void qla2x00_free_req_que(struct qla_hw_data *ha, struct req_que *req)
+{
+ if (req && req->ring)
+ dma_free_coherent(&ha->pdev->dev,
+ (req->length + 1) * sizeof(request_t),
+ req->ring, req->dma);
+
+ kfree(req);
+ req = NULL;
+}
+
+static void qla2x00_free_rsp_que(struct qla_hw_data *ha, struct rsp_que *rsp)
+{
+ if (rsp && rsp->ring)
+ dma_free_coherent(&ha->pdev->dev,
+ (rsp->length + 1) * sizeof(response_t),
+ rsp->ring, rsp->dma);
+
+ kfree(rsp);
+ rsp = NULL;
+}
+
+static void qla2x00_free_queues(struct qla_hw_data *ha)
+{
+ struct req_que *req;
+ struct rsp_que *rsp;
+ int cnt;
+
+ for (cnt = 0; cnt < ha->max_req_queues; cnt++) {
+ req = ha->req_q_map[cnt];
+ qla2x00_free_req_que(ha, req);
+ }
+ kfree(ha->req_q_map);
+ ha->req_q_map = NULL;
+
+ for (cnt = 0; cnt < ha->max_rsp_queues; cnt++) {
+ rsp = ha->rsp_q_map[cnt];
+ qla2x00_free_rsp_que(ha, rsp);
+ }
+ kfree(ha->rsp_q_map);
+ ha->rsp_q_map = NULL;
+}
+
+static int qla25xx_setup_mode(struct scsi_qla_host *vha)
+{
+ uint16_t options = 0;
+ int ques, req, ret;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!(ha->fw_attributes & BIT_6)) {
+ ql_log(ql_log_warn, vha, 0x00d8,
+ "Firmware is not multi-queue capable.\n");
+ goto fail;
+ }
+ if (ql2xmultique_tag) {
+ /* create a request queue for IO */
+ options |= BIT_7;
+ req = qla25xx_create_req_que(ha, options, 0, 0, -1,
+ QLA_DEFAULT_QUE_QOS);
+ if (!req) {
+ ql_log(ql_log_warn, vha, 0x00e0,
+ "Failed to create request queue.\n");
+ goto fail;
+ }
+ ha->wq = alloc_workqueue("qla2xxx_wq", WQ_MEM_RECLAIM, 1);
+ vha->req = ha->req_q_map[req];
+ options |= BIT_1;
+ for (ques = 1; ques < ha->max_rsp_queues; ques++) {
+ ret = qla25xx_create_rsp_que(ha, options, 0, 0, req);
+ if (!ret) {
+ ql_log(ql_log_warn, vha, 0x00e8,
+ "Failed to create response queue.\n");
+ goto fail2;
+ }
+ }
+ ha->flags.cpu_affinity_enabled = 1;
+ ql_dbg(ql_dbg_multiq, vha, 0xc007,
+ "CPU affinity mode enalbed, "
+ "no. of response queues:%d no. of request queues:%d.\n",
+ ha->max_rsp_queues, ha->max_req_queues);
+ ql_dbg(ql_dbg_init, vha, 0x00e9,
+ "CPU affinity mode enalbed, "
+ "no. of response queues:%d no. of request queues:%d.\n",
+ ha->max_rsp_queues, ha->max_req_queues);
+ }
+ return 0;
+fail2:
+ qla25xx_delete_queues(vha);
+ destroy_workqueue(ha->wq);
+ ha->wq = NULL;
+ vha->req = ha->req_q_map[0];
+fail:
+ ha->mqenable = 0;
+ kfree(ha->req_q_map);
+ kfree(ha->rsp_q_map);
+ ha->max_req_queues = ha->max_rsp_queues = 1;
+ return 1;
+}
+
+static char *
+qla2x00_pci_info_str(struct scsi_qla_host *vha, char *str)
+{
+ struct qla_hw_data *ha = vha->hw;
+ static char *pci_bus_modes[] = {
+ "33", "66", "100", "133",
+ };
+ uint16_t pci_bus;
+
+ strcpy(str, "PCI");
+ pci_bus = (ha->pci_attr & (BIT_9 | BIT_10)) >> 9;
+ if (pci_bus) {
+ strcat(str, "-X (");
+ strcat(str, pci_bus_modes[pci_bus]);
+ } else {
+ pci_bus = (ha->pci_attr & BIT_8) >> 8;
+ strcat(str, " (");
+ strcat(str, pci_bus_modes[pci_bus]);
+ }
+ strcat(str, " MHz)");
+
+ return (str);
+}
+
+static char *
+qla24xx_pci_info_str(struct scsi_qla_host *vha, char *str)
+{
+ static char *pci_bus_modes[] = { "33", "66", "100", "133", };
+ struct qla_hw_data *ha = vha->hw;
+ uint32_t pci_bus;
+ int pcie_reg;
+
+ pcie_reg = pci_find_capability(ha->pdev, PCI_CAP_ID_EXP);
+ if (pcie_reg) {
+ char lwstr[6];
+ uint16_t pcie_lstat, lspeed, lwidth;
+
+ pcie_reg += 0x12;
+ pci_read_config_word(ha->pdev, pcie_reg, &pcie_lstat);
+ lspeed = pcie_lstat & (BIT_0 | BIT_1 | BIT_2 | BIT_3);
+ lwidth = (pcie_lstat &
+ (BIT_4 | BIT_5 | BIT_6 | BIT_7 | BIT_8 | BIT_9)) >> 4;
+
+ strcpy(str, "PCIe (");
+ if (lspeed == 1)
+ strcat(str, "2.5GT/s ");
+ else if (lspeed == 2)
+ strcat(str, "5.0GT/s ");
+ else
+ strcat(str, "<unknown> ");
+ snprintf(lwstr, sizeof(lwstr), "x%d)", lwidth);
+ strcat(str, lwstr);
+
+ return str;
+ }
+
+ strcpy(str, "PCI");
+ pci_bus = (ha->pci_attr & CSRX_PCIX_BUS_MODE_MASK) >> 8;
+ if (pci_bus == 0 || pci_bus == 8) {
+ strcat(str, " (");
+ strcat(str, pci_bus_modes[pci_bus >> 3]);
+ } else {
+ strcat(str, "-X ");
+ if (pci_bus & BIT_2)
+ strcat(str, "Mode 2");
+ else
+ strcat(str, "Mode 1");
+ strcat(str, " (");
+ strcat(str, pci_bus_modes[pci_bus & ~BIT_2]);
+ }
+ strcat(str, " MHz)");
+
+ return str;
+}
+
+static char *
+qla2x00_fw_version_str(struct scsi_qla_host *vha, char *str)
+{
+ char un_str[10];
+ struct qla_hw_data *ha = vha->hw;
+
+ sprintf(str, "%d.%02d.%02d ", ha->fw_major_version,
+ ha->fw_minor_version,
+ ha->fw_subminor_version);
+
+ if (ha->fw_attributes & BIT_9) {
+ strcat(str, "FLX");
+ return (str);
+ }
+
+ switch (ha->fw_attributes & 0xFF) {
+ case 0x7:
+ strcat(str, "EF");
+ break;
+ case 0x17:
+ strcat(str, "TP");
+ break;
+ case 0x37:
+ strcat(str, "IP");
+ break;
+ case 0x77:
+ strcat(str, "VI");
+ break;
+ default:
+ sprintf(un_str, "(%x)", ha->fw_attributes);
+ strcat(str, un_str);
+ break;
+ }
+ if (ha->fw_attributes & 0x100)
+ strcat(str, "X");
+
+ return (str);
+}
+
+static char *
+qla24xx_fw_version_str(struct scsi_qla_host *vha, char *str)
+{
+ struct qla_hw_data *ha = vha->hw;
+
+ sprintf(str, "%d.%02d.%02d (%x)", ha->fw_major_version,
+ ha->fw_minor_version, ha->fw_subminor_version, ha->fw_attributes);
+ return str;
+}
+
+void
+qla2x00_sp_free_dma(void *vha, void *ptr)
+{
+ srb_t *sp = (srb_t *)ptr;
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
+ struct qla_hw_data *ha = sp->fcport->vha->hw;
+ void *ctx = GET_CMD_CTX_SP(sp);
+
+ if (sp->flags & SRB_DMA_VALID) {
+ scsi_dma_unmap(cmd);
+ sp->flags &= ~SRB_DMA_VALID;
+ }
+
+ if (sp->flags & SRB_CRC_PROT_DMA_VALID) {
+ dma_unmap_sg(&ha->pdev->dev, scsi_prot_sglist(cmd),
+ scsi_prot_sg_count(cmd), cmd->sc_data_direction);
+ sp->flags &= ~SRB_CRC_PROT_DMA_VALID;
+ }
+
+ if (sp->flags & SRB_CRC_CTX_DSD_VALID) {
+ /* List assured to be having elements */
+ qla2x00_clean_dsd_pool(ha, sp);
+ sp->flags &= ~SRB_CRC_CTX_DSD_VALID;
+ }
+
+ if (sp->flags & SRB_CRC_CTX_DMA_VALID) {
+ dma_pool_free(ha->dl_dma_pool, ctx,
+ ((struct crc_context *)ctx)->crc_ctx_dma);
+ sp->flags &= ~SRB_CRC_CTX_DMA_VALID;
+ }
+
+ if (sp->flags & SRB_FCP_CMND_DMA_VALID) {
+ struct ct6_dsd *ctx1 = (struct ct6_dsd *)ctx;
+
+ dma_pool_free(ha->fcp_cmnd_dma_pool, ctx1->fcp_cmnd,
+ ctx1->fcp_cmnd_dma);
+ list_splice(&ctx1->dsd_list, &ha->gbl_dsd_list);
+ ha->gbl_dsd_inuse -= ctx1->dsd_use_cnt;
+ ha->gbl_dsd_avail += ctx1->dsd_use_cnt;
+ mempool_free(ctx1, ha->ctx_mempool);
+ ctx1 = NULL;
+ }
+
+ CMD_SP(cmd) = NULL;
+ mempool_free(sp, ha->srb_mempool);
+}
+
+static void
+qla2x00_sp_compl(void *data, void *ptr, int res)
+{
+ struct qla_hw_data *ha = (struct qla_hw_data *)data;
+ srb_t *sp = (srb_t *)ptr;
+ struct scsi_cmnd *cmd = GET_CMD_SP(sp);
+
+ cmd->result = res;
+
+ if (atomic_read(&sp->ref_count) == 0) {
+ ql_dbg(ql_dbg_io, sp->fcport->vha, 0x3015,
+ "SP reference-count to ZERO -- sp=%p cmd=%p.\n",
+ sp, GET_CMD_SP(sp));
+ if (ql2xextended_error_logging & ql_dbg_io)
+ BUG();
+ return;
+ }
+ if (!atomic_dec_and_test(&sp->ref_count))
+ return;
+
+ qla2x00_sp_free_dma(ha, sp);
+ cmd->scsi_done(cmd);
+}
+
+static int
+qla2xxx_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd)
+{
+ scsi_qla_host_t *vha = shost_priv(host);
+ fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
+ struct fc_rport *rport = starget_to_rport(scsi_target(cmd->device));
+ struct qla_hw_data *ha = vha->hw;
+ struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev);
+ srb_t *sp;
+ int rval;
+
+ if (ha->flags.eeh_busy) {
+ if (ha->flags.pci_channel_io_perm_failure) {
+ ql_dbg(ql_dbg_io, vha, 0x3001,
+ "PCI Channel IO permanent failure, exiting "
+ "cmd=%p.\n", cmd);
+ cmd->result = DID_NO_CONNECT << 16;
+ } else {
+ ql_dbg(ql_dbg_io, vha, 0x3002,
+ "EEH_Busy, Requeuing the cmd=%p.\n", cmd);
+ cmd->result = DID_REQUEUE << 16;
+ }
+ goto qc24_fail_command;
+ }
+
+ rval = fc_remote_port_chkready(rport);
+ if (rval) {
+ cmd->result = rval;
+ ql_dbg(ql_dbg_io, vha, 0x3003,
+ "fc_remote_port_chkready failed for cmd=%p, rval=0x%x.\n",
+ cmd, rval);
+ goto qc24_fail_command;
+ }
+
+ if (!vha->flags.difdix_supported &&
+ scsi_get_prot_op(cmd) != SCSI_PROT_NORMAL) {
+ ql_dbg(ql_dbg_io, vha, 0x3004,
+ "DIF Cap not reg, fail DIF capable cmd's:%p.\n",
+ cmd);
+ cmd->result = DID_NO_CONNECT << 16;
+ goto qc24_fail_command;
+ }
+
+ if (!fcport) {
+ cmd->result = DID_NO_CONNECT << 16;
+ goto qc24_fail_command;
+ }
+
+ if (atomic_read(&fcport->state) != FCS_ONLINE) {
+ if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD ||
+ atomic_read(&base_vha->loop_state) == LOOP_DEAD) {
+ ql_dbg(ql_dbg_io, vha, 0x3005,
+ "Returning DNC, fcport_state=%d loop_state=%d.\n",
+ atomic_read(&fcport->state),
+ atomic_read(&base_vha->loop_state));
+ cmd->result = DID_NO_CONNECT << 16;
+ goto qc24_fail_command;
+ }
+ goto qc24_target_busy;
+ }
+
+ sp = qla2x00_get_sp(base_vha, fcport, GFP_ATOMIC);
+ if (!sp)
+ goto qc24_host_busy;
+
+ sp->u.scmd.cmd = cmd;
+ sp->type = SRB_SCSI_CMD;
+ atomic_set(&sp->ref_count, 1);
+ CMD_SP(cmd) = (void *)sp;
+ sp->free = qla2x00_sp_free_dma;
+ sp->done = qla2x00_sp_compl;
+
+ rval = ha->isp_ops->start_scsi(sp);
+ if (rval != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_io, vha, 0x3013,
+ "Start scsi failed rval=%d for cmd=%p.\n", rval, cmd);
+ goto qc24_host_busy_free_sp;
+ }
+
+ return 0;
+
+qc24_host_busy_free_sp:
+ qla2x00_sp_free_dma(ha, sp);
+
+qc24_host_busy:
+ return SCSI_MLQUEUE_HOST_BUSY;
+
+qc24_target_busy:
+ return SCSI_MLQUEUE_TARGET_BUSY;
+
+qc24_fail_command:
+ cmd->scsi_done(cmd);
+
+ return 0;
+}
+
+/*
+ * qla2x00_eh_wait_on_command
+ * Waits for the command to be returned by the Firmware for some
+ * max time.
+ *
+ * Input:
+ * cmd = Scsi Command to wait on.
+ *
+ * Return:
+ * Not Found : 0
+ * Found : 1
+ */
+static int
+qla2x00_eh_wait_on_command(struct scsi_cmnd *cmd)
+{
+#define ABORT_POLLING_PERIOD 1000
+#define ABORT_WAIT_ITER ((10 * 1000) / (ABORT_POLLING_PERIOD))
+ unsigned long wait_iter = ABORT_WAIT_ITER;
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
+ struct qla_hw_data *ha = vha->hw;
+ int ret = QLA_SUCCESS;
+
+ if (unlikely(pci_channel_offline(ha->pdev)) || ha->flags.eeh_busy) {
+ ql_dbg(ql_dbg_taskm, vha, 0x8005,
+ "Return:eh_wait.\n");
+ return ret;
+ }
+
+ while (CMD_SP(cmd) && wait_iter--) {
+ msleep(ABORT_POLLING_PERIOD);
+ }
+ if (CMD_SP(cmd))
+ ret = QLA_FUNCTION_FAILED;
+
+ return ret;
+}
+
+/*
+ * qla2x00_wait_for_hba_online
+ * Wait till the HBA is online after going through
+ * <= MAX_RETRIES_OF_ISP_ABORT or
+ * finally HBA is disabled ie marked offline
+ *
+ * Input:
+ * ha - pointer to host adapter structure
+ *
+ * Note:
+ * Does context switching-Release SPIN_LOCK
+ * (if any) before calling this routine.
+ *
+ * Return:
+ * Success (Adapter is online) : 0
+ * Failed (Adapter is offline/disabled) : 1
+ */
+int
+qla2x00_wait_for_hba_online(scsi_qla_host_t *vha)
+{
+ int return_status;
+ unsigned long wait_online;
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
+
+ wait_online = jiffies + (MAX_LOOP_TIMEOUT * HZ);
+ while (((test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags)) ||
+ test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags) ||
+ test_bit(ISP_ABORT_RETRY, &base_vha->dpc_flags) ||
+ ha->dpc_active) && time_before(jiffies, wait_online)) {
+
+ msleep(1000);
+ }
+ if (base_vha->flags.online)
+ return_status = QLA_SUCCESS;
+ else
+ return_status = QLA_FUNCTION_FAILED;
+
+ return (return_status);
+}
+
+/*
+ * qla2x00_wait_for_reset_ready
+ * Wait till the HBA is online after going through
+ * <= MAX_RETRIES_OF_ISP_ABORT or
+ * finally HBA is disabled ie marked offline or flash
+ * operations are in progress.
+ *
+ * Input:
+ * ha - pointer to host adapter structure
+ *
+ * Note:
+ * Does context switching-Release SPIN_LOCK
+ * (if any) before calling this routine.
+ *
+ * Return:
+ * Success (Adapter is online/no flash ops) : 0
+ * Failed (Adapter is offline/disabled/flash ops in progress) : 1
+ */
+static int
+qla2x00_wait_for_reset_ready(scsi_qla_host_t *vha)
+{
+ int return_status;
+ unsigned long wait_online;
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
+
+ wait_online = jiffies + (MAX_LOOP_TIMEOUT * HZ);
+ while (((test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags)) ||
+ test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags) ||
+ test_bit(ISP_ABORT_RETRY, &base_vha->dpc_flags) ||
+ ha->optrom_state != QLA_SWAITING ||
+ ha->dpc_active) && time_before(jiffies, wait_online))
+ msleep(1000);
+
+ if (base_vha->flags.online && ha->optrom_state == QLA_SWAITING)
+ return_status = QLA_SUCCESS;
+ else
+ return_status = QLA_FUNCTION_FAILED;
+
+ ql_dbg(ql_dbg_taskm, vha, 0x8019,
+ "%s return status=%d.\n", __func__, return_status);
+
+ return return_status;
+}
+
+int
+qla2x00_wait_for_chip_reset(scsi_qla_host_t *vha)
+{
+ int return_status;
+ unsigned long wait_reset;
+ struct qla_hw_data *ha = vha->hw;
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
+
+ wait_reset = jiffies + (MAX_LOOP_TIMEOUT * HZ);
+ while (((test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags)) ||
+ test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags) ||
+ test_bit(ISP_ABORT_RETRY, &base_vha->dpc_flags) ||
+ ha->dpc_active) && time_before(jiffies, wait_reset)) {
+
+ msleep(1000);
+
+ if (!test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags) &&
+ ha->flags.chip_reset_done)
+ break;
+ }
+ if (ha->flags.chip_reset_done)
+ return_status = QLA_SUCCESS;
+ else
+ return_status = QLA_FUNCTION_FAILED;
+
+ return return_status;
+}
+
+static void
+sp_get(struct srb *sp)
+{
+ atomic_inc(&sp->ref_count);
+}
+
+/**************************************************************************
+* qla2xxx_eh_abort
+*
+* Description:
+* The abort function will abort the specified command.
+*
+* Input:
+* cmd = Linux SCSI command packet to be aborted.
+*
+* Returns:
+* Either SUCCESS or FAILED.
+*
+* Note:
+* Only return FAILED if command not returned by firmware.
+**************************************************************************/
+static int
+qla2xxx_eh_abort(struct scsi_cmnd *cmd)
+{
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
+ srb_t *sp;
+ int ret;
+ unsigned int id, lun;
+ unsigned long flags;
+ int wait = 0;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!CMD_SP(cmd))
+ return SUCCESS;
+
+ ret = fc_block_scsi_eh(cmd);
+ if (ret != 0)
+ return ret;
+ ret = SUCCESS;
+
+ id = cmd->device->id;
+ lun = cmd->device->lun;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ sp = (srb_t *) CMD_SP(cmd);
+ if (!sp) {
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ return SUCCESS;
+ }
+
+ ql_dbg(ql_dbg_taskm, vha, 0x8002,
+ "Aborting from RISC nexus=%ld:%d:%d sp=%p cmd=%p\n",
+ vha->host_no, id, lun, sp, cmd);
+
+ /* Get a reference to the sp and drop the lock.*/
+ sp_get(sp);
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ if (ha->isp_ops->abort_command(sp)) {
+ ret = FAILED;
+ ql_dbg(ql_dbg_taskm, vha, 0x8003,
+ "Abort command mbx failed cmd=%p.\n", cmd);
+ } else {
+ ql_dbg(ql_dbg_taskm, vha, 0x8004,
+ "Abort command mbx success cmd=%p.\n", cmd);
+ wait = 1;
+ }
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ sp->done(ha, sp, 0);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ /* Did the command return during mailbox execution? */
+ if (ret == FAILED && !CMD_SP(cmd))
+ ret = SUCCESS;
+
+ /* Wait for the command to be returned. */
+ if (wait) {
+ if (qla2x00_eh_wait_on_command(cmd) != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x8006,
+ "Abort handler timed out cmd=%p.\n", cmd);
+ ret = FAILED;
+ }
+ }
+
+ ql_log(ql_log_info, vha, 0x801c,
+ "Abort command issued nexus=%ld:%d:%d -- %d %x.\n",
+ vha->host_no, id, lun, wait, ret);
+
+ return ret;
+}
+
+int
+qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *vha, unsigned int t,
+ unsigned int l, enum nexus_wait_type type)
+{
+ int cnt, match, status;
+ unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req;
+ srb_t *sp;
+ struct scsi_cmnd *cmd;
+
+ status = QLA_SUCCESS;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ req = vha->req;
+ for (cnt = 1; status == QLA_SUCCESS &&
+ cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+ sp = req->outstanding_cmds[cnt];
+ if (!sp)
+ continue;
+ if (sp->type != SRB_SCSI_CMD)
+ continue;
+ if (vha->vp_idx != sp->fcport->vha->vp_idx)
+ continue;
+ match = 0;
+ cmd = GET_CMD_SP(sp);
+ switch (type) {
+ case WAIT_HOST:
+ match = 1;
+ break;
+ case WAIT_TARGET:
+ match = cmd->device->id == t;
+ break;
+ case WAIT_LUN:
+ match = (cmd->device->id == t &&
+ cmd->device->lun == l);
+ break;
+ }
+ if (!match)
+ continue;
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ status = qla2x00_eh_wait_on_command(cmd);
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return status;
+}
+
+static char *reset_errors[] = {
+ "HBA not online",
+ "HBA not ready",
+ "Task management failed",
+ "Waiting for command completions",
+};
+
+static int
+__qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type,
+ struct scsi_cmnd *cmd, int (*do_reset)(struct fc_port *, unsigned int, int))
+{
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
+ fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
+ int err;
+
+ if (!fcport) {
+ return FAILED;
+ }
+
+ err = fc_block_scsi_eh(cmd);
+ if (err != 0)
+ return err;
+
+ ql_log(ql_log_info, vha, 0x8009,
+ "%s RESET ISSUED nexus=%ld:%d:%d cmd=%p.\n", name, vha->host_no,
+ cmd->device->id, cmd->device->lun, cmd);
+
+ err = 0;
+ if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x800a,
+ "Wait for hba online failed for cmd=%p.\n", cmd);
+ goto eh_reset_failed;
+ }
+ err = 2;
+ if (do_reset(fcport, cmd->device->lun, cmd->request->cpu + 1)
+ != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x800c,
+ "do_reset failed for cmd=%p.\n", cmd);
+ goto eh_reset_failed;
+ }
+ err = 3;
+ if (qla2x00_eh_wait_for_pending_commands(vha, cmd->device->id,
+ cmd->device->lun, type) != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x800d,
+ "wait for peding cmds failed for cmd=%p.\n", cmd);
+ goto eh_reset_failed;
+ }
+
+ ql_log(ql_log_info, vha, 0x800e,
+ "%s RESET SUCCEEDED nexus:%ld:%d:%d cmd=%p.\n", name,
+ vha->host_no, cmd->device->id, cmd->device->lun, cmd);
+
+ return SUCCESS;
+
+eh_reset_failed:
+ ql_log(ql_log_info, vha, 0x800f,
+ "%s RESET FAILED: %s nexus=%ld:%d:%d cmd=%p.\n", name,
+ reset_errors[err], vha->host_no, cmd->device->id, cmd->device->lun,
+ cmd);
+ return FAILED;
+}
+
+static int
+qla2xxx_eh_device_reset(struct scsi_cmnd *cmd)
+{
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
+ struct qla_hw_data *ha = vha->hw;
+
+ return __qla2xxx_eh_generic_reset("DEVICE", WAIT_LUN, cmd,
+ ha->isp_ops->lun_reset);
+}
+
+static int
+qla2xxx_eh_target_reset(struct scsi_cmnd *cmd)
+{
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
+ struct qla_hw_data *ha = vha->hw;
+
+ return __qla2xxx_eh_generic_reset("TARGET", WAIT_TARGET, cmd,
+ ha->isp_ops->target_reset);
+}
+
+/**************************************************************************
+* qla2xxx_eh_bus_reset
+*
+* Description:
+* The bus reset function will reset the bus and abort any executing
+* commands.
+*
+* Input:
+* cmd = Linux SCSI command packet of the command that cause the
+* bus reset.
+*
+* Returns:
+* SUCCESS/FAILURE (defined as macro in scsi.h).
+*
+**************************************************************************/
+static int
+qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd)
+{
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
+ fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata;
+ int ret = FAILED;
+ unsigned int id, lun;
+
+ id = cmd->device->id;
+ lun = cmd->device->lun;
+
+ if (!fcport) {
+ return ret;
+ }
+
+ ret = fc_block_scsi_eh(cmd);
+ if (ret != 0)
+ return ret;
+ ret = FAILED;
+
+ ql_log(ql_log_info, vha, 0x8012,
+ "BUS RESET ISSUED nexus=%ld:%d%d.\n", vha->host_no, id, lun);
+
+ if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
+ ql_log(ql_log_fatal, vha, 0x8013,
+ "Wait for hba online failed board disabled.\n");
+ goto eh_bus_reset_done;
+ }
+
+ if (qla2x00_loop_reset(vha) == QLA_SUCCESS)
+ ret = SUCCESS;
+
+ if (ret == FAILED)
+ goto eh_bus_reset_done;
+
+ /* Flush outstanding commands. */
+ if (qla2x00_eh_wait_for_pending_commands(vha, 0, 0, WAIT_HOST) !=
+ QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x8014,
+ "Wait for pending commands failed.\n");
+ ret = FAILED;
+ }
+
+eh_bus_reset_done:
+ ql_log(ql_log_warn, vha, 0x802b,
+ "BUS RESET %s nexus=%ld:%d:%d.\n",
+ (ret == FAILED) ? "FAILED" : "SUCCEDED", vha->host_no, id, lun);
+
+ return ret;
+}
+
+/**************************************************************************
+* qla2xxx_eh_host_reset
+*
+* Description:
+* The reset function will reset the Adapter.
+*
+* Input:
+* cmd = Linux SCSI command packet of the command that cause the
+* adapter reset.
+*
+* Returns:
+* Either SUCCESS or FAILED.
+*
+* Note:
+**************************************************************************/
+static int
+qla2xxx_eh_host_reset(struct scsi_cmnd *cmd)
+{
+ scsi_qla_host_t *vha = shost_priv(cmd->device->host);
+ struct qla_hw_data *ha = vha->hw;
+ int ret = FAILED;
+ unsigned int id, lun;
+ scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev);
+
+ id = cmd->device->id;
+ lun = cmd->device->lun;
+
+ ql_log(ql_log_info, vha, 0x8018,
+ "ADAPTER RESET ISSUED nexus=%ld:%d:%d.\n", vha->host_no, id, lun);
+
+ if (qla2x00_wait_for_reset_ready(vha) != QLA_SUCCESS)
+ goto eh_host_reset_lock;
+
+ if (vha != base_vha) {
+ if (qla2x00_vp_abort_isp(vha))
+ goto eh_host_reset_lock;
+ } else {
+ if (IS_QLA82XX(vha->hw)) {
+ if (!qla82xx_fcoe_ctx_reset(vha)) {
+ /* Ctx reset success */
+ ret = SUCCESS;
+ goto eh_host_reset_lock;
+ }
+ /* fall thru if ctx reset failed */
+ }
+ if (ha->wq)
+ flush_workqueue(ha->wq);
+
+ set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+ if (ha->isp_ops->abort_isp(base_vha)) {
+ clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+ /* failed. schedule dpc to try */
+ set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags);
+
+ if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x802a,
+ "wait for hba online failed.\n");
+ goto eh_host_reset_lock;
+ }
+ }
+ clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+ }
+
+ /* Waiting for command to be returned to OS.*/
+ if (qla2x00_eh_wait_for_pending_commands(vha, 0, 0, WAIT_HOST) ==
+ QLA_SUCCESS)
+ ret = SUCCESS;
+
+eh_host_reset_lock:
+ ql_log(ql_log_info, vha, 0x8017,
+ "ADAPTER RESET %s nexus=%ld:%d:%d.\n",
+ (ret == FAILED) ? "FAILED" : "SUCCEEDED", vha->host_no, id, lun);
+
+ return ret;
+}
+
+/*
+* qla2x00_loop_reset
+* Issue loop reset.
+*
+* Input:
+* ha = adapter block pointer.
+*
+* Returns:
+* 0 = success
+*/
+int
+qla2x00_loop_reset(scsi_qla_host_t *vha)
+{
+ int ret;
+ struct fc_port *fcport;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (ql2xtargetreset == 1 && ha->flags.enable_target_reset) {
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ if (fcport->port_type != FCT_TARGET)
+ continue;
+
+ ret = ha->isp_ops->target_reset(fcport, 0, 0);
+ if (ret != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_taskm, vha, 0x802c,
+ "Bus Reset failed: Target Reset=%d "
+ "d_id=%x.\n", ret, fcport->d_id.b24);
+ }
+ }
+ }
+
+ if (ha->flags.enable_lip_full_login && !IS_CNA_CAPABLE(ha)) {
+ ret = qla2x00_full_login_lip(vha);
+ if (ret != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_taskm, vha, 0x802d,
+ "full_login_lip=%d.\n", ret);
+ }
+ atomic_set(&vha->loop_state, LOOP_DOWN);
+ atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME);
+ qla2x00_mark_all_devices_lost(vha, 0);
+ }
+
+ if (ha->flags.enable_lip_reset) {
+ ret = qla2x00_lip_reset(vha);
+ if (ret != QLA_SUCCESS)
+ ql_dbg(ql_dbg_taskm, vha, 0x802e,
+ "lip_reset failed (%d).\n", ret);
+ }
+
+ /* Issue marker command only when we are going to start the I/O */
+ vha->marker_needed = 1;
+
+ return QLA_SUCCESS;
+}
+
+void
+qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res)
+{
+ int que, cnt;
+ unsigned long flags;
+ srb_t *sp;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ for (que = 0; que < ha->max_req_queues; que++) {
+ req = ha->req_q_map[que];
+ if (!req)
+ continue;
+ for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) {
+ sp = req->outstanding_cmds[cnt];
+ if (sp) {
+ req->outstanding_cmds[cnt] = NULL;
+ sp->done(vha, sp, res);
+ }
+ }
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+static int
+qla2xxx_slave_alloc(struct scsi_device *sdev)
+{
+ struct fc_rport *rport = starget_to_rport(scsi_target(sdev));
+
+ if (!rport || fc_remote_port_chkready(rport))
+ return -ENXIO;
+
+ sdev->hostdata = *(fc_port_t **)rport->dd_data;
+
+ return 0;
+}
+
+static int
+qla2xxx_slave_configure(struct scsi_device *sdev)
+{
+ scsi_qla_host_t *vha = shost_priv(sdev->host);
+ struct req_que *req = vha->req;
+
+ if (sdev->tagged_supported)
+ scsi_activate_tcq(sdev, req->max_q_depth);
+ else
+ scsi_deactivate_tcq(sdev, req->max_q_depth);
+ return 0;
+}
+
+static void
+qla2xxx_slave_destroy(struct scsi_device *sdev)
+{
+ sdev->hostdata = NULL;
+}
+
+static void qla2x00_handle_queue_full(struct scsi_device *sdev, int qdepth)
+{
+ fc_port_t *fcport = (struct fc_port *) sdev->hostdata;
+
+ if (!scsi_track_queue_full(sdev, qdepth))
+ return;
+
+ ql_dbg(ql_dbg_io, fcport->vha, 0x3029,
+ "Queue depth adjusted-down to %d for nexus=%ld:%d:%d.\n",
+ sdev->queue_depth, fcport->vha->host_no, sdev->id, sdev->lun);
+}
+
+static void qla2x00_adjust_sdev_qdepth_up(struct scsi_device *sdev, int qdepth)
+{
+ fc_port_t *fcport = sdev->hostdata;
+ struct scsi_qla_host *vha = fcport->vha;
+ struct req_que *req = NULL;
+
+ req = vha->req;
+ if (!req)
+ return;
+
+ if (req->max_q_depth <= sdev->queue_depth || req->max_q_depth < qdepth)
+ return;
+
+ if (sdev->ordered_tags)
+ scsi_adjust_queue_depth(sdev, MSG_ORDERED_TAG, qdepth);
+ else
+ scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, qdepth);
+
+ ql_dbg(ql_dbg_io, vha, 0x302a,
+ "Queue depth adjusted-up to %d for nexus=%ld:%d:%d.\n",
+ sdev->queue_depth, fcport->vha->host_no, sdev->id, sdev->lun);
+}
+
+static int
+qla2x00_change_queue_depth(struct scsi_device *sdev, int qdepth, int reason)
+{
+ switch (reason) {
+ case SCSI_QDEPTH_DEFAULT:
+ scsi_adjust_queue_depth(sdev, scsi_get_tag_type(sdev), qdepth);
+ break;
+ case SCSI_QDEPTH_QFULL:
+ qla2x00_handle_queue_full(sdev, qdepth);
+ break;
+ case SCSI_QDEPTH_RAMP_UP:
+ qla2x00_adjust_sdev_qdepth_up(sdev, qdepth);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return sdev->queue_depth;
+}
+
+static int
+qla2x00_change_queue_type(struct scsi_device *sdev, int tag_type)
+{
+ if (sdev->tagged_supported) {
+ scsi_set_tag_type(sdev, tag_type);
+ if (tag_type)
+ scsi_activate_tcq(sdev, sdev->queue_depth);
+ else
+ scsi_deactivate_tcq(sdev, sdev->queue_depth);
+ } else
+ tag_type = 0;
+
+ return tag_type;
+}
+
+/**
+ * qla2x00_config_dma_addressing() - Configure OS DMA addressing method.
+ * @ha: HA context
+ *
+ * At exit, the @ha's flags.enable_64bit_addressing set to indicated
+ * supported addressing method.
+ */
+static void
+qla2x00_config_dma_addressing(struct qla_hw_data *ha)
+{
+ /* Assume a 32bit DMA mask. */
+ ha->flags.enable_64bit_addressing = 0;
+
+ if (!dma_set_mask(&ha->pdev->dev, DMA_BIT_MASK(64))) {
+ /* Any upper-dword bits set? */
+ if (MSD(dma_get_required_mask(&ha->pdev->dev)) &&
+ !pci_set_consistent_dma_mask(ha->pdev, DMA_BIT_MASK(64))) {
+ /* Ok, a 64bit DMA mask is applicable. */
+ ha->flags.enable_64bit_addressing = 1;
+ ha->isp_ops->calc_req_entries = qla2x00_calc_iocbs_64;
+ ha->isp_ops->build_iocbs = qla2x00_build_scsi_iocbs_64;
+ return;
+ }
+ }
+
+ dma_set_mask(&ha->pdev->dev, DMA_BIT_MASK(32));
+ pci_set_consistent_dma_mask(ha->pdev, DMA_BIT_MASK(32));
+}
+
+static void
+qla2x00_enable_intrs(struct qla_hw_data *ha)
+{
+ unsigned long flags = 0;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ ha->interrupts_on = 1;
+ /* enable risc and host interrupts */
+ WRT_REG_WORD(&reg->ictrl, ICR_EN_INT | ICR_EN_RISC);
+ RD_REG_WORD(&reg->ictrl);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+}
+
+static void
+qla2x00_disable_intrs(struct qla_hw_data *ha)
+{
+ unsigned long flags = 0;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ ha->interrupts_on = 0;
+ /* disable risc and host interrupts */
+ WRT_REG_WORD(&reg->ictrl, 0);
+ RD_REG_WORD(&reg->ictrl);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+static void
+qla24xx_enable_intrs(struct qla_hw_data *ha)
+{
+ unsigned long flags = 0;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ ha->interrupts_on = 1;
+ WRT_REG_DWORD(&reg->ictrl, ICRX_EN_RISC_INT);
+ RD_REG_DWORD(&reg->ictrl);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+static void
+qla24xx_disable_intrs(struct qla_hw_data *ha)
+{
+ unsigned long flags = 0;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+ if (IS_NOPOLLING_TYPE(ha))
+ return;
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ ha->interrupts_on = 0;
+ WRT_REG_DWORD(&reg->ictrl, 0);
+ RD_REG_DWORD(&reg->ictrl);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+static int
+qla2x00_iospace_config(struct qla_hw_data *ha)
+{
+ resource_size_t pio;
+ uint16_t msix;
+ int cpus;
+
+ if (pci_request_selected_regions(ha->pdev, ha->bars,
+ QLA2XXX_DRIVER_NAME)) {
+ ql_log_pci(ql_log_fatal, ha->pdev, 0x0011,
+ "Failed to reserve PIO/MMIO regions (%s), aborting.\n",
+ pci_name(ha->pdev));
+ goto iospace_error_exit;
+ }
+ if (!(ha->bars & 1))
+ goto skip_pio;
+
+ /* We only need PIO for Flash operations on ISP2312 v2 chips. */
+ pio = pci_resource_start(ha->pdev, 0);
+ if (pci_resource_flags(ha->pdev, 0) & IORESOURCE_IO) {
+ if (pci_resource_len(ha->pdev, 0) < MIN_IOBASE_LEN) {
+ ql_log_pci(ql_log_warn, ha->pdev, 0x0012,
+ "Invalid pci I/O region size (%s).\n",
+ pci_name(ha->pdev));
+ pio = 0;
+ }
+ } else {
+ ql_log_pci(ql_log_warn, ha->pdev, 0x0013,
+ "Region #0 no a PIO resource (%s).\n",
+ pci_name(ha->pdev));
+ pio = 0;
+ }
+ ha->pio_address = pio;
+ ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0014,
+ "PIO address=%llu.\n",
+ (unsigned long long)ha->pio_address);
+
+skip_pio:
+ /* Use MMIO operations for all accesses. */
+ if (!(pci_resource_flags(ha->pdev, 1) & IORESOURCE_MEM)) {
+ ql_log_pci(ql_log_fatal, ha->pdev, 0x0015,
+ "Region #1 not an MMIO resource (%s), aborting.\n",
+ pci_name(ha->pdev));
+ goto iospace_error_exit;
+ }
+ if (pci_resource_len(ha->pdev, 1) < MIN_IOBASE_LEN) {
+ ql_log_pci(ql_log_fatal, ha->pdev, 0x0016,
+ "Invalid PCI mem region size (%s), aborting.\n",
+ pci_name(ha->pdev));
+ goto iospace_error_exit;
+ }
+
+ ha->iobase = ioremap(pci_resource_start(ha->pdev, 1), MIN_IOBASE_LEN);
+ if (!ha->iobase) {
+ ql_log_pci(ql_log_fatal, ha->pdev, 0x0017,
+ "Cannot remap MMIO (%s), aborting.\n",
+ pci_name(ha->pdev));
+ goto iospace_error_exit;
+ }
+
+ /* Determine queue resources */
+ ha->max_req_queues = ha->max_rsp_queues = 1;
+ if ((ql2xmaxqueues <= 1 && !ql2xmultique_tag) ||
+ (ql2xmaxqueues > 1 && ql2xmultique_tag) ||
+ (!IS_QLA25XX(ha) && !IS_QLA81XX(ha)))
+ goto mqiobase_exit;
+
+ ha->mqiobase = ioremap(pci_resource_start(ha->pdev, 3),
+ pci_resource_len(ha->pdev, 3));
+ if (ha->mqiobase) {
+ ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0018,
+ "MQIO Base=%p.\n", ha->mqiobase);
+ /* Read MSIX vector size of the board */
+ pci_read_config_word(ha->pdev, QLA_PCI_MSIX_CONTROL, &msix);
+ ha->msix_count = msix;
+ /* Max queues are bounded by available msix vectors */
+ /* queue 0 uses two msix vectors */
+ if (ql2xmultique_tag) {
+ cpus = num_online_cpus();
+ ha->max_rsp_queues = (ha->msix_count - 1 > cpus) ?
+ (cpus + 1) : (ha->msix_count - 1);
+ ha->max_req_queues = 2;
+ } else if (ql2xmaxqueues > 1) {
+ ha->max_req_queues = ql2xmaxqueues > QLA_MQ_SIZE ?
+ QLA_MQ_SIZE : ql2xmaxqueues;
+ ql_dbg_pci(ql_dbg_multiq, ha->pdev, 0xc008,
+ "QoS mode set, max no of request queues:%d.\n",
+ ha->max_req_queues);
+ ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0019,
+ "QoS mode set, max no of request queues:%d.\n",
+ ha->max_req_queues);
+ }
+ ql_log_pci(ql_log_info, ha->pdev, 0x001a,
+ "MSI-X vector count: %d.\n", msix);
+ } else
+ ql_log_pci(ql_log_info, ha->pdev, 0x001b,
+ "BAR 3 not enabled.\n");
+
+mqiobase_exit:
+ ha->msix_count = ha->max_rsp_queues + 1;
+ ql_dbg_pci(ql_dbg_init, ha->pdev, 0x001c,
+ "MSIX Count:%d.\n", ha->msix_count);
+ return (0);
+
+iospace_error_exit:
+ return (-ENOMEM);
+}
+
+
+static int
+qla83xx_iospace_config(struct qla_hw_data *ha)
+{
+ uint16_t msix;
+ int cpus;
+
+ if (pci_request_selected_regions(ha->pdev, ha->bars,
+ QLA2XXX_DRIVER_NAME)) {
+ ql_log_pci(ql_log_fatal, ha->pdev, 0x0117,
+ "Failed to reserve PIO/MMIO regions (%s), aborting.\n",
+ pci_name(ha->pdev));
+
+ goto iospace_error_exit;
+ }
+
+ /* Use MMIO operations for all accesses. */
+ if (!(pci_resource_flags(ha->pdev, 0) & IORESOURCE_MEM)) {
+ ql_log_pci(ql_log_warn, ha->pdev, 0x0118,
+ "Invalid pci I/O region size (%s).\n",
+ pci_name(ha->pdev));
+ goto iospace_error_exit;
+ }
+ if (pci_resource_len(ha->pdev, 0) < MIN_IOBASE_LEN) {
+ ql_log_pci(ql_log_warn, ha->pdev, 0x0119,
+ "Invalid PCI mem region size (%s), aborting\n",
+ pci_name(ha->pdev));
+ goto iospace_error_exit;
+ }
+
+ ha->iobase = ioremap(pci_resource_start(ha->pdev, 0), MIN_IOBASE_LEN);
+ if (!ha->iobase) {
+ ql_log_pci(ql_log_fatal, ha->pdev, 0x011a,
+ "Cannot remap MMIO (%s), aborting.\n",
+ pci_name(ha->pdev));
+ goto iospace_error_exit;
+ }
+
+ /* 64bit PCI BAR - BAR2 will correspoond to region 4 */
+ /* 83XX 26XX always use MQ type access for queues
+ * - mbar 2, a.k.a region 4 */
+ ha->max_req_queues = ha->max_rsp_queues = 1;
+ ha->mqiobase = ioremap(pci_resource_start(ha->pdev, 4),
+ pci_resource_len(ha->pdev, 4));
+
+ if (!ha->mqiobase) {
+ ql_log_pci(ql_log_fatal, ha->pdev, 0x011d,
+ "BAR2/region4 not enabled\n");
+ goto mqiobase_exit;
+ }
+
+ ha->msixbase = ioremap(pci_resource_start(ha->pdev, 2),
+ pci_resource_len(ha->pdev, 2));
+ if (ha->msixbase) {
+ /* Read MSIX vector size of the board */
+ pci_read_config_word(ha->pdev,
+ QLA_83XX_PCI_MSIX_CONTROL, &msix);
+ ha->msix_count = msix;
+ /* Max queues are bounded by available msix vectors */
+ /* queue 0 uses two msix vectors */
+ if (ql2xmultique_tag) {
+ cpus = num_online_cpus();
+ ha->max_rsp_queues = (ha->msix_count - 1 > cpus) ?
+ (cpus + 1) : (ha->msix_count - 1);
+ ha->max_req_queues = 2;
+ } else if (ql2xmaxqueues > 1) {
+ ha->max_req_queues = ql2xmaxqueues > QLA_MQ_SIZE ?
+ QLA_MQ_SIZE : ql2xmaxqueues;
+ ql_dbg_pci(ql_dbg_multiq, ha->pdev, 0xc00c,
+ "QoS mode set, max no of request queues:%d.\n",
+ ha->max_req_queues);
+ ql_dbg_pci(ql_dbg_init, ha->pdev, 0x011b,
+ "QoS mode set, max no of request queues:%d.\n",
+ ha->max_req_queues);
+ }
+ ql_log_pci(ql_log_info, ha->pdev, 0x011c,
+ "MSI-X vector count: %d.\n", msix);
+ } else
+ ql_log_pci(ql_log_info, ha->pdev, 0x011e,
+ "BAR 1 not enabled.\n");
+
+mqiobase_exit:
+ ha->msix_count = ha->max_rsp_queues + 1;
+ ql_dbg_pci(ql_dbg_init, ha->pdev, 0x011f,
+ "MSIX Count:%d.\n", ha->msix_count);
+ return 0;
+
+iospace_error_exit:
+ return -ENOMEM;
+}
+
+static struct isp_operations qla2100_isp_ops = {
+ .pci_config = qla2100_pci_config,
+ .reset_chip = qla2x00_reset_chip,
+ .chip_diag = qla2x00_chip_diag,
+ .config_rings = qla2x00_config_rings,
+ .reset_adapter = qla2x00_reset_adapter,
+ .nvram_config = qla2x00_nvram_config,
+ .update_fw_options = qla2x00_update_fw_options,
+ .load_risc = qla2x00_load_risc,
+ .pci_info_str = qla2x00_pci_info_str,
+ .fw_version_str = qla2x00_fw_version_str,
+ .intr_handler = qla2100_intr_handler,
+ .enable_intrs = qla2x00_enable_intrs,
+ .disable_intrs = qla2x00_disable_intrs,
+ .abort_command = qla2x00_abort_command,
+ .target_reset = qla2x00_abort_target,
+ .lun_reset = qla2x00_lun_reset,
+ .fabric_login = qla2x00_login_fabric,
+ .fabric_logout = qla2x00_fabric_logout,
+ .calc_req_entries = qla2x00_calc_iocbs_32,
+ .build_iocbs = qla2x00_build_scsi_iocbs_32,
+ .prep_ms_iocb = qla2x00_prep_ms_iocb,
+ .prep_ms_fdmi_iocb = qla2x00_prep_ms_fdmi_iocb,
+ .read_nvram = qla2x00_read_nvram_data,
+ .write_nvram = qla2x00_write_nvram_data,
+ .fw_dump = qla2100_fw_dump,
+ .beacon_on = NULL,
+ .beacon_off = NULL,
+ .beacon_blink = NULL,
+ .read_optrom = qla2x00_read_optrom_data,
+ .write_optrom = qla2x00_write_optrom_data,
+ .get_flash_version = qla2x00_get_flash_version,
+ .start_scsi = qla2x00_start_scsi,
+ .abort_isp = qla2x00_abort_isp,
+ .iospace_config = qla2x00_iospace_config,
+};
+
+static struct isp_operations qla2300_isp_ops = {
+ .pci_config = qla2300_pci_config,
+ .reset_chip = qla2x00_reset_chip,
+ .chip_diag = qla2x00_chip_diag,
+ .config_rings = qla2x00_config_rings,
+ .reset_adapter = qla2x00_reset_adapter,
+ .nvram_config = qla2x00_nvram_config,
+ .update_fw_options = qla2x00_update_fw_options,
+ .load_risc = qla2x00_load_risc,
+ .pci_info_str = qla2x00_pci_info_str,
+ .fw_version_str = qla2x00_fw_version_str,
+ .intr_handler = qla2300_intr_handler,
+ .enable_intrs = qla2x00_enable_intrs,
+ .disable_intrs = qla2x00_disable_intrs,
+ .abort_command = qla2x00_abort_command,
+ .target_reset = qla2x00_abort_target,
+ .lun_reset = qla2x00_lun_reset,
+ .fabric_login = qla2x00_login_fabric,
+ .fabric_logout = qla2x00_fabric_logout,
+ .calc_req_entries = qla2x00_calc_iocbs_32,
+ .build_iocbs = qla2x00_build_scsi_iocbs_32,
+ .prep_ms_iocb = qla2x00_prep_ms_iocb,
+ .prep_ms_fdmi_iocb = qla2x00_prep_ms_fdmi_iocb,
+ .read_nvram = qla2x00_read_nvram_data,
+ .write_nvram = qla2x00_write_nvram_data,
+ .fw_dump = qla2300_fw_dump,
+ .beacon_on = qla2x00_beacon_on,
+ .beacon_off = qla2x00_beacon_off,
+ .beacon_blink = qla2x00_beacon_blink,
+ .read_optrom = qla2x00_read_optrom_data,
+ .write_optrom = qla2x00_write_optrom_data,
+ .get_flash_version = qla2x00_get_flash_version,
+ .start_scsi = qla2x00_start_scsi,
+ .abort_isp = qla2x00_abort_isp,
+ .iospace_config = qla2x00_iospace_config,
+};
+
+static struct isp_operations qla24xx_isp_ops = {
+ .pci_config = qla24xx_pci_config,
+ .reset_chip = qla24xx_reset_chip,
+ .chip_diag = qla24xx_chip_diag,
+ .config_rings = qla24xx_config_rings,
+ .reset_adapter = qla24xx_reset_adapter,
+ .nvram_config = qla24xx_nvram_config,
+ .update_fw_options = qla24xx_update_fw_options,
+ .load_risc = qla24xx_load_risc,
+ .pci_info_str = qla24xx_pci_info_str,
+ .fw_version_str = qla24xx_fw_version_str,
+ .intr_handler = qla24xx_intr_handler,
+ .enable_intrs = qla24xx_enable_intrs,
+ .disable_intrs = qla24xx_disable_intrs,
+ .abort_command = qla24xx_abort_command,
+ .target_reset = qla24xx_abort_target,
+ .lun_reset = qla24xx_lun_reset,
+ .fabric_login = qla24xx_login_fabric,
+ .fabric_logout = qla24xx_fabric_logout,
+ .calc_req_entries = NULL,
+ .build_iocbs = NULL,
+ .prep_ms_iocb = qla24xx_prep_ms_iocb,
+ .prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb,
+ .read_nvram = qla24xx_read_nvram_data,
+ .write_nvram = qla24xx_write_nvram_data,
+ .fw_dump = qla24xx_fw_dump,
+ .beacon_on = qla24xx_beacon_on,
+ .beacon_off = qla24xx_beacon_off,
+ .beacon_blink = qla24xx_beacon_blink,
+ .read_optrom = qla24xx_read_optrom_data,
+ .write_optrom = qla24xx_write_optrom_data,
+ .get_flash_version = qla24xx_get_flash_version,
+ .start_scsi = qla24xx_start_scsi,
+ .abort_isp = qla2x00_abort_isp,
+ .iospace_config = qla2x00_iospace_config,
+};
+
+static struct isp_operations qla25xx_isp_ops = {
+ .pci_config = qla25xx_pci_config,
+ .reset_chip = qla24xx_reset_chip,
+ .chip_diag = qla24xx_chip_diag,
+ .config_rings = qla24xx_config_rings,
+ .reset_adapter = qla24xx_reset_adapter,
+ .nvram_config = qla24xx_nvram_config,
+ .update_fw_options = qla24xx_update_fw_options,
+ .load_risc = qla24xx_load_risc,
+ .pci_info_str = qla24xx_pci_info_str,
+ .fw_version_str = qla24xx_fw_version_str,
+ .intr_handler = qla24xx_intr_handler,
+ .enable_intrs = qla24xx_enable_intrs,
+ .disable_intrs = qla24xx_disable_intrs,
+ .abort_command = qla24xx_abort_command,
+ .target_reset = qla24xx_abort_target,
+ .lun_reset = qla24xx_lun_reset,
+ .fabric_login = qla24xx_login_fabric,
+ .fabric_logout = qla24xx_fabric_logout,
+ .calc_req_entries = NULL,
+ .build_iocbs = NULL,
+ .prep_ms_iocb = qla24xx_prep_ms_iocb,
+ .prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb,
+ .read_nvram = qla25xx_read_nvram_data,
+ .write_nvram = qla25xx_write_nvram_data,
+ .fw_dump = qla25xx_fw_dump,
+ .beacon_on = qla24xx_beacon_on,
+ .beacon_off = qla24xx_beacon_off,
+ .beacon_blink = qla24xx_beacon_blink,
+ .read_optrom = qla25xx_read_optrom_data,
+ .write_optrom = qla24xx_write_optrom_data,
+ .get_flash_version = qla24xx_get_flash_version,
+ .start_scsi = qla24xx_dif_start_scsi,
+ .abort_isp = qla2x00_abort_isp,
+ .iospace_config = qla2x00_iospace_config,
+};
+
+static struct isp_operations qla81xx_isp_ops = {
+ .pci_config = qla25xx_pci_config,
+ .reset_chip = qla24xx_reset_chip,
+ .chip_diag = qla24xx_chip_diag,
+ .config_rings = qla24xx_config_rings,
+ .reset_adapter = qla24xx_reset_adapter,
+ .nvram_config = qla81xx_nvram_config,
+ .update_fw_options = qla81xx_update_fw_options,
+ .load_risc = qla81xx_load_risc,
+ .pci_info_str = qla24xx_pci_info_str,
+ .fw_version_str = qla24xx_fw_version_str,
+ .intr_handler = qla24xx_intr_handler,
+ .enable_intrs = qla24xx_enable_intrs,
+ .disable_intrs = qla24xx_disable_intrs,
+ .abort_command = qla24xx_abort_command,
+ .target_reset = qla24xx_abort_target,
+ .lun_reset = qla24xx_lun_reset,
+ .fabric_login = qla24xx_login_fabric,
+ .fabric_logout = qla24xx_fabric_logout,
+ .calc_req_entries = NULL,
+ .build_iocbs = NULL,
+ .prep_ms_iocb = qla24xx_prep_ms_iocb,
+ .prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb,
+ .read_nvram = NULL,
+ .write_nvram = NULL,
+ .fw_dump = qla81xx_fw_dump,
+ .beacon_on = qla24xx_beacon_on,
+ .beacon_off = qla24xx_beacon_off,
+ .beacon_blink = qla83xx_beacon_blink,
+ .read_optrom = qla25xx_read_optrom_data,
+ .write_optrom = qla24xx_write_optrom_data,
+ .get_flash_version = qla24xx_get_flash_version,
+ .start_scsi = qla24xx_dif_start_scsi,
+ .abort_isp = qla2x00_abort_isp,
+ .iospace_config = qla2x00_iospace_config,
+};
+
+static struct isp_operations qla82xx_isp_ops = {
+ .pci_config = qla82xx_pci_config,
+ .reset_chip = qla82xx_reset_chip,
+ .chip_diag = qla24xx_chip_diag,
+ .config_rings = qla82xx_config_rings,
+ .reset_adapter = qla24xx_reset_adapter,
+ .nvram_config = qla81xx_nvram_config,
+ .update_fw_options = qla24xx_update_fw_options,
+ .load_risc = qla82xx_load_risc,
+ .pci_info_str = qla82xx_pci_info_str,
+ .fw_version_str = qla24xx_fw_version_str,
+ .intr_handler = qla82xx_intr_handler,
+ .enable_intrs = qla82xx_enable_intrs,
+ .disable_intrs = qla82xx_disable_intrs,
+ .abort_command = qla24xx_abort_command,
+ .target_reset = qla24xx_abort_target,
+ .lun_reset = qla24xx_lun_reset,
+ .fabric_login = qla24xx_login_fabric,
+ .fabric_logout = qla24xx_fabric_logout,
+ .calc_req_entries = NULL,
+ .build_iocbs = NULL,
+ .prep_ms_iocb = qla24xx_prep_ms_iocb,
+ .prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb,
+ .read_nvram = qla24xx_read_nvram_data,
+ .write_nvram = qla24xx_write_nvram_data,
+ .fw_dump = qla24xx_fw_dump,
+ .beacon_on = qla82xx_beacon_on,
+ .beacon_off = qla82xx_beacon_off,
+ .beacon_blink = NULL,
+ .read_optrom = qla82xx_read_optrom_data,
+ .write_optrom = qla82xx_write_optrom_data,
+ .get_flash_version = qla24xx_get_flash_version,
+ .start_scsi = qla82xx_start_scsi,
+ .abort_isp = qla82xx_abort_isp,
+ .iospace_config = qla82xx_iospace_config,
+};
+
+static struct isp_operations qla83xx_isp_ops = {
+ .pci_config = qla25xx_pci_config,
+ .reset_chip = qla24xx_reset_chip,
+ .chip_diag = qla24xx_chip_diag,
+ .config_rings = qla24xx_config_rings,
+ .reset_adapter = qla24xx_reset_adapter,
+ .nvram_config = qla81xx_nvram_config,
+ .update_fw_options = qla81xx_update_fw_options,
+ .load_risc = qla81xx_load_risc,
+ .pci_info_str = qla24xx_pci_info_str,
+ .fw_version_str = qla24xx_fw_version_str,
+ .intr_handler = qla24xx_intr_handler,
+ .enable_intrs = qla24xx_enable_intrs,
+ .disable_intrs = qla24xx_disable_intrs,
+ .abort_command = qla24xx_abort_command,
+ .target_reset = qla24xx_abort_target,
+ .lun_reset = qla24xx_lun_reset,
+ .fabric_login = qla24xx_login_fabric,
+ .fabric_logout = qla24xx_fabric_logout,
+ .calc_req_entries = NULL,
+ .build_iocbs = NULL,
+ .prep_ms_iocb = qla24xx_prep_ms_iocb,
+ .prep_ms_fdmi_iocb = qla24xx_prep_ms_fdmi_iocb,
+ .read_nvram = NULL,
+ .write_nvram = NULL,
+ .fw_dump = qla83xx_fw_dump,
+ .beacon_on = qla24xx_beacon_on,
+ .beacon_off = qla24xx_beacon_off,
+ .beacon_blink = qla83xx_beacon_blink,
+ .read_optrom = qla25xx_read_optrom_data,
+ .write_optrom = qla24xx_write_optrom_data,
+ .get_flash_version = qla24xx_get_flash_version,
+ .start_scsi = qla24xx_dif_start_scsi,
+ .abort_isp = qla2x00_abort_isp,
+ .iospace_config = qla83xx_iospace_config,
+};
+
+static inline void
+qla2x00_set_isp_flags(struct qla_hw_data *ha)
+{
+ ha->device_type = DT_EXTENDED_IDS;
+ switch (ha->pdev->device) {
+ case PCI_DEVICE_ID_QLOGIC_ISP2100:
+ ha->device_type |= DT_ISP2100;
+ ha->device_type &= ~DT_EXTENDED_IDS;
+ ha->fw_srisc_address = RISC_START_ADDRESS_2100;
+ break;
+ case PCI_DEVICE_ID_QLOGIC_ISP2200:
+ ha->device_type |= DT_ISP2200;
+ ha->device_type &= ~DT_EXTENDED_IDS;
+ ha->fw_srisc_address = RISC_START_ADDRESS_2100;
+ break;
+ case PCI_DEVICE_ID_QLOGIC_ISP2300:
+ ha->device_type |= DT_ISP2300;
+ ha->device_type |= DT_ZIO_SUPPORTED;
+ ha->fw_srisc_address = RISC_START_ADDRESS_2300;
+ break;
+ case PCI_DEVICE_ID_QLOGIC_ISP2312:
+ ha->device_type |= DT_ISP2312;
+ ha->device_type |= DT_ZIO_SUPPORTED;
+ ha->fw_srisc_address = RISC_START_ADDRESS_2300;
+ break;
+ case PCI_DEVICE_ID_QLOGIC_ISP2322:
+ ha->device_type |= DT_ISP2322;
+ ha->device_type |= DT_ZIO_SUPPORTED;
+ if (ha->pdev->subsystem_vendor == 0x1028 &&
+ ha->pdev->subsystem_device == 0x0170)
+ ha->device_type |= DT_OEM_001;
+ ha->fw_srisc_address = RISC_START_ADDRESS_2300;
+ break;
+ case PCI_DEVICE_ID_QLOGIC_ISP6312:
+ ha->device_type |= DT_ISP6312;
+ ha->fw_srisc_address = RISC_START_ADDRESS_2300;
+ break;
+ case PCI_DEVICE_ID_QLOGIC_ISP6322:
+ ha->device_type |= DT_ISP6322;
+ ha->fw_srisc_address = RISC_START_ADDRESS_2300;
+ break;
+ case PCI_DEVICE_ID_QLOGIC_ISP2422:
+ ha->device_type |= DT_ISP2422;
+ ha->device_type |= DT_ZIO_SUPPORTED;
+ ha->device_type |= DT_FWI2;
+ ha->device_type |= DT_IIDMA;
+ ha->fw_srisc_address = RISC_START_ADDRESS_2400;
+ break;
+ case PCI_DEVICE_ID_QLOGIC_ISP2432:
+ ha->device_type |= DT_ISP2432;
+ ha->device_type |= DT_ZIO_SUPPORTED;
+ ha->device_type |= DT_FWI2;
+ ha->device_type |= DT_IIDMA;
+ ha->fw_srisc_address = RISC_START_ADDRESS_2400;
+ break;
+ case PCI_DEVICE_ID_QLOGIC_ISP8432:
+ ha->device_type |= DT_ISP8432;
+ ha->device_type |= DT_ZIO_SUPPORTED;
+ ha->device_type |= DT_FWI2;
+ ha->device_type |= DT_IIDMA;
+ ha->fw_srisc_address = RISC_START_ADDRESS_2400;
+ break;
+ case PCI_DEVICE_ID_QLOGIC_ISP5422:
+ ha->device_type |= DT_ISP5422;
+ ha->device_type |= DT_FWI2;
+ ha->fw_srisc_address = RISC_START_ADDRESS_2400;
+ break;
+ case PCI_DEVICE_ID_QLOGIC_ISP5432:
+ ha->device_type |= DT_ISP5432;
+ ha->device_type |= DT_FWI2;
+ ha->fw_srisc_address = RISC_START_ADDRESS_2400;
+ break;
+ case PCI_DEVICE_ID_QLOGIC_ISP2532:
+ ha->device_type |= DT_ISP2532;
+ ha->device_type |= DT_ZIO_SUPPORTED;
+ ha->device_type |= DT_FWI2;
+ ha->device_type |= DT_IIDMA;
+ ha->fw_srisc_address = RISC_START_ADDRESS_2400;
+ break;
+ case PCI_DEVICE_ID_QLOGIC_ISP8001:
+ ha->device_type |= DT_ISP8001;
+ ha->device_type |= DT_ZIO_SUPPORTED;
+ ha->device_type |= DT_FWI2;
+ ha->device_type |= DT_IIDMA;
+ ha->fw_srisc_address = RISC_START_ADDRESS_2400;
+ break;
+ case PCI_DEVICE_ID_QLOGIC_ISP8021:
+ ha->device_type |= DT_ISP8021;
+ ha->device_type |= DT_ZIO_SUPPORTED;
+ ha->device_type |= DT_FWI2;
+ ha->fw_srisc_address = RISC_START_ADDRESS_2400;
+ /* Initialize 82XX ISP flags */
+ qla82xx_init_flags(ha);
+ break;
+ case PCI_DEVICE_ID_QLOGIC_ISP2031:
+ ha->device_type |= DT_ISP2031;
+ ha->device_type |= DT_ZIO_SUPPORTED;
+ ha->device_type |= DT_FWI2;
+ ha->device_type |= DT_IIDMA;
+ ha->device_type |= DT_T10_PI;
+ ha->fw_srisc_address = RISC_START_ADDRESS_2400;
+ break;
+ case PCI_DEVICE_ID_QLOGIC_ISP8031:
+ ha->device_type |= DT_ISP8031;
+ ha->device_type |= DT_ZIO_SUPPORTED;
+ ha->device_type |= DT_FWI2;
+ ha->device_type |= DT_IIDMA;
+ ha->device_type |= DT_T10_PI;
+ ha->fw_srisc_address = RISC_START_ADDRESS_2400;
+ break;
+ }
+
+ if (IS_QLA82XX(ha))
+ ha->port_no = !(ha->portnum & 1);
+ else
+ /* Get adapter physical port no from interrupt pin register. */
+ pci_read_config_byte(ha->pdev, PCI_INTERRUPT_PIN, &ha->port_no);
+
+ if (ha->port_no & 1)
+ ha->flags.port0 = 1;
+ else
+ ha->flags.port0 = 0;
+ ql_dbg_pci(ql_dbg_init, ha->pdev, 0x000b,
+ "device_type=0x%x port=%d fw_srisc_address=0x%x.\n",
+ ha->device_type, ha->flags.port0, ha->fw_srisc_address);
+}
+
+static void
+qla2xxx_scan_start(struct Scsi_Host *shost)
+{
+ scsi_qla_host_t *vha = shost_priv(shost);
+
+ if (vha->hw->flags.running_gold_fw)
+ return;
+
+ set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags);
+ set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags);
+ set_bit(RSCN_UPDATE, &vha->dpc_flags);
+ set_bit(NPIV_CONFIG_NEEDED, &vha->dpc_flags);
+}
+
+static int
+qla2xxx_scan_finished(struct Scsi_Host *shost, unsigned long time)
+{
+ scsi_qla_host_t *vha = shost_priv(shost);
+
+ if (!vha->host)
+ return 1;
+ if (time > vha->hw->loop_reset_delay * HZ)
+ return 1;
+
+ return atomic_read(&vha->loop_state) == LOOP_READY;
+}
+
+/*
+ * PCI driver interface
+ */
+static int __devinit
+qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+ int ret = -ENODEV;
+ struct Scsi_Host *host;
+ scsi_qla_host_t *base_vha = NULL;
+ struct qla_hw_data *ha;
+ char pci_info[30];
+ char fw_str[30];
+ struct scsi_host_template *sht;
+ int bars, mem_only = 0;
+ uint16_t req_length = 0, rsp_length = 0;
+ struct req_que *req = NULL;
+ struct rsp_que *rsp = NULL;
+
+ bars = pci_select_bars(pdev, IORESOURCE_MEM | IORESOURCE_IO);
+ sht = &qla2xxx_driver_template;
+ if (pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2422 ||
+ pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2432 ||
+ pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8432 ||
+ pdev->device == PCI_DEVICE_ID_QLOGIC_ISP5422 ||
+ pdev->device == PCI_DEVICE_ID_QLOGIC_ISP5432 ||
+ pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2532 ||
+ pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8001 ||
+ pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8021 ||
+ pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2031 ||
+ pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8031) {
+ bars = pci_select_bars(pdev, IORESOURCE_MEM);
+ mem_only = 1;
+ ql_dbg_pci(ql_dbg_init, pdev, 0x0007,
+ "Mem only adapter.\n");
+ }
+ ql_dbg_pci(ql_dbg_init, pdev, 0x0008,
+ "Bars=%d.\n", bars);
+
+ if (mem_only) {
+ if (pci_enable_device_mem(pdev))
+ goto probe_out;
+ } else {
+ if (pci_enable_device(pdev))
+ goto probe_out;
+ }
+
+ /* This may fail but that's ok */
+ pci_enable_pcie_error_reporting(pdev);
+
+ ha = kzalloc(sizeof(struct qla_hw_data), GFP_KERNEL);
+ if (!ha) {
+ ql_log_pci(ql_log_fatal, pdev, 0x0009,
+ "Unable to allocate memory for ha.\n");
+ goto probe_out;
+ }
+ ql_dbg_pci(ql_dbg_init, pdev, 0x000a,
+ "Memory allocated for ha=%p.\n", ha);
+ ha->pdev = pdev;
+
+ /* Clear our data area */
+ ha->bars = bars;
+ ha->mem_only = mem_only;
+ spin_lock_init(&ha->hardware_lock);
+ spin_lock_init(&ha->vport_slock);
+
+ /* Set ISP-type information. */
+ qla2x00_set_isp_flags(ha);
+
+ /* Set EEH reset type to fundamental if required by hba */
+ if (IS_QLA24XX(ha) || IS_QLA25XX(ha) || IS_QLA81XX(ha))
+ pdev->needs_freset = 1;
+
+ ha->prev_topology = 0;
+ ha->init_cb_size = sizeof(init_cb_t);
+ ha->link_data_rate = PORT_SPEED_UNKNOWN;
+ ha->optrom_size = OPTROM_SIZE_2300;
+
+ /* Assign ISP specific operations. */
+ if (IS_QLA2100(ha)) {
+ ha->max_fibre_devices = MAX_FIBRE_DEVICES_2100;
+ ha->mbx_count = MAILBOX_REGISTER_COUNT_2100;
+ req_length = REQUEST_ENTRY_CNT_2100;
+ rsp_length = RESPONSE_ENTRY_CNT_2100;
+ ha->max_loop_id = SNS_LAST_LOOP_ID_2100;
+ ha->gid_list_info_size = 4;
+ ha->flash_conf_off = ~0;
+ ha->flash_data_off = ~0;
+ ha->nvram_conf_off = ~0;
+ ha->nvram_data_off = ~0;
+ ha->isp_ops = &qla2100_isp_ops;
+ } else if (IS_QLA2200(ha)) {
+ ha->max_fibre_devices = MAX_FIBRE_DEVICES_2100;
+ ha->mbx_count = MAILBOX_REGISTER_COUNT_2200;
+ req_length = REQUEST_ENTRY_CNT_2200;
+ rsp_length = RESPONSE_ENTRY_CNT_2100;
+ ha->max_loop_id = SNS_LAST_LOOP_ID_2100;
+ ha->gid_list_info_size = 4;
+ ha->flash_conf_off = ~0;
+ ha->flash_data_off = ~0;
+ ha->nvram_conf_off = ~0;
+ ha->nvram_data_off = ~0;
+ ha->isp_ops = &qla2100_isp_ops;
+ } else if (IS_QLA23XX(ha)) {
+ ha->max_fibre_devices = MAX_FIBRE_DEVICES_2100;
+ ha->mbx_count = MAILBOX_REGISTER_COUNT;
+ req_length = REQUEST_ENTRY_CNT_2200;
+ rsp_length = RESPONSE_ENTRY_CNT_2300;
+ ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
+ ha->gid_list_info_size = 6;
+ if (IS_QLA2322(ha) || IS_QLA6322(ha))
+ ha->optrom_size = OPTROM_SIZE_2322;
+ ha->flash_conf_off = ~0;
+ ha->flash_data_off = ~0;
+ ha->nvram_conf_off = ~0;
+ ha->nvram_data_off = ~0;
+ ha->isp_ops = &qla2300_isp_ops;
+ } else if (IS_QLA24XX_TYPE(ha)) {
+ ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400;
+ ha->mbx_count = MAILBOX_REGISTER_COUNT;
+ req_length = REQUEST_ENTRY_CNT_24XX;
+ rsp_length = RESPONSE_ENTRY_CNT_2300;
+ ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
+ ha->init_cb_size = sizeof(struct mid_init_cb_24xx);
+ ha->gid_list_info_size = 8;
+ ha->optrom_size = OPTROM_SIZE_24XX;
+ ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA24XX;
+ ha->isp_ops = &qla24xx_isp_ops;
+ ha->flash_conf_off = FARX_ACCESS_FLASH_CONF;
+ ha->flash_data_off = FARX_ACCESS_FLASH_DATA;
+ ha->nvram_conf_off = FARX_ACCESS_NVRAM_CONF;
+ ha->nvram_data_off = FARX_ACCESS_NVRAM_DATA;
+ } else if (IS_QLA25XX(ha)) {
+ ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400;
+ ha->mbx_count = MAILBOX_REGISTER_COUNT;
+ req_length = REQUEST_ENTRY_CNT_24XX;
+ rsp_length = RESPONSE_ENTRY_CNT_2300;
+ ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
+ ha->init_cb_size = sizeof(struct mid_init_cb_24xx);
+ ha->gid_list_info_size = 8;
+ ha->optrom_size = OPTROM_SIZE_25XX;
+ ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX;
+ ha->isp_ops = &qla25xx_isp_ops;
+ ha->flash_conf_off = FARX_ACCESS_FLASH_CONF;
+ ha->flash_data_off = FARX_ACCESS_FLASH_DATA;
+ ha->nvram_conf_off = FARX_ACCESS_NVRAM_CONF;
+ ha->nvram_data_off = FARX_ACCESS_NVRAM_DATA;
+ } else if (IS_QLA81XX(ha)) {
+ ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400;
+ ha->mbx_count = MAILBOX_REGISTER_COUNT;
+ req_length = REQUEST_ENTRY_CNT_24XX;
+ rsp_length = RESPONSE_ENTRY_CNT_2300;
+ ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
+ ha->init_cb_size = sizeof(struct mid_init_cb_81xx);
+ ha->gid_list_info_size = 8;
+ ha->optrom_size = OPTROM_SIZE_81XX;
+ ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX;
+ ha->isp_ops = &qla81xx_isp_ops;
+ ha->flash_conf_off = FARX_ACCESS_FLASH_CONF_81XX;
+ ha->flash_data_off = FARX_ACCESS_FLASH_DATA_81XX;
+ ha->nvram_conf_off = ~0;
+ ha->nvram_data_off = ~0;
+ } else if (IS_QLA82XX(ha)) {
+ ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400;
+ ha->mbx_count = MAILBOX_REGISTER_COUNT;
+ req_length = REQUEST_ENTRY_CNT_82XX;
+ rsp_length = RESPONSE_ENTRY_CNT_82XX;
+ ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
+ ha->init_cb_size = sizeof(struct mid_init_cb_81xx);
+ ha->gid_list_info_size = 8;
+ ha->optrom_size = OPTROM_SIZE_82XX;
+ ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX;
+ ha->isp_ops = &qla82xx_isp_ops;
+ ha->flash_conf_off = FARX_ACCESS_FLASH_CONF;
+ ha->flash_data_off = FARX_ACCESS_FLASH_DATA;
+ ha->nvram_conf_off = FARX_ACCESS_NVRAM_CONF;
+ ha->nvram_data_off = FARX_ACCESS_NVRAM_DATA;
+ } else if (IS_QLA83XX(ha)) {
+ ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400;
+ ha->mbx_count = MAILBOX_REGISTER_COUNT;
+ req_length = REQUEST_ENTRY_CNT_24XX;
+ rsp_length = RESPONSE_ENTRY_CNT_2300;
+ ha->max_loop_id = SNS_LAST_LOOP_ID_2300;
+ ha->init_cb_size = sizeof(struct mid_init_cb_81xx);
+ ha->gid_list_info_size = 8;
+ ha->optrom_size = OPTROM_SIZE_83XX;
+ ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX;
+ ha->isp_ops = &qla83xx_isp_ops;
+ ha->flash_conf_off = FARX_ACCESS_FLASH_CONF_81XX;
+ ha->flash_data_off = FARX_ACCESS_FLASH_DATA_81XX;
+ ha->nvram_conf_off = ~0;
+ ha->nvram_data_off = ~0;
+ }
+
+ ql_dbg_pci(ql_dbg_init, pdev, 0x001e,
+ "mbx_count=%d, req_length=%d, "
+ "rsp_length=%d, max_loop_id=%d, init_cb_size=%d, "
+ "gid_list_info_size=%d, optrom_size=%d, nvram_npiv_size=%d, "
+ "max_fibre_devices=%d.\n",
+ ha->mbx_count, req_length, rsp_length, ha->max_loop_id,
+ ha->init_cb_size, ha->gid_list_info_size, ha->optrom_size,
+ ha->nvram_npiv_size, ha->max_fibre_devices);
+ ql_dbg_pci(ql_dbg_init, pdev, 0x001f,
+ "isp_ops=%p, flash_conf_off=%d, "
+ "flash_data_off=%d, nvram_conf_off=%d, nvram_data_off=%d.\n",
+ ha->isp_ops, ha->flash_conf_off, ha->flash_data_off,
+ ha->nvram_conf_off, ha->nvram_data_off);
+
+ /* Configure PCI I/O space */
+ ret = ha->isp_ops->iospace_config(ha);
+ if (ret)
+ goto probe_hw_failed;
+
+ ql_log_pci(ql_log_info, pdev, 0x001d,
+ "Found an ISP%04X irq %d iobase 0x%p.\n",
+ pdev->device, pdev->irq, ha->iobase);
+ mutex_init(&ha->vport_lock);
+ init_completion(&ha->mbx_cmd_comp);
+ complete(&ha->mbx_cmd_comp);
+ init_completion(&ha->mbx_intr_comp);
+ init_completion(&ha->dcbx_comp);
+
+ set_bit(0, (unsigned long *) ha->vp_idx_map);
+
+ qla2x00_config_dma_addressing(ha);
+ ql_dbg_pci(ql_dbg_init, pdev, 0x0020,
+ "64 Bit addressing is %s.\n",
+ ha->flags.enable_64bit_addressing ? "enable" :
+ "disable");
+ ret = qla2x00_mem_alloc(ha, req_length, rsp_length, &req, &rsp);
+ if (!ret) {
+ ql_log_pci(ql_log_fatal, pdev, 0x0031,
+ "Failed to allocate memory for adapter, aborting.\n");
+
+ goto probe_hw_failed;
+ }
+
+ req->max_q_depth = MAX_Q_DEPTH;
+ if (ql2xmaxqdepth != 0 && ql2xmaxqdepth <= 0xffffU)
+ req->max_q_depth = ql2xmaxqdepth;
+
+
+ base_vha = qla2x00_create_host(sht, ha);
+ if (!base_vha) {
+ ret = -ENOMEM;
+ qla2x00_mem_free(ha);
+ qla2x00_free_req_que(ha, req);
+ qla2x00_free_rsp_que(ha, rsp);
+ goto probe_hw_failed;
+ }
+
+ pci_set_drvdata(pdev, base_vha);
+
+ host = base_vha->host;
+ base_vha->req = req;
+ host->can_queue = req->length + 128;
+ if (IS_QLA2XXX_MIDTYPE(ha))
+ base_vha->mgmt_svr_loop_id = 10 + base_vha->vp_idx;
+ else
+ base_vha->mgmt_svr_loop_id = MANAGEMENT_SERVER +
+ base_vha->vp_idx;
+
+ /* Set the SG table size based on ISP type */
+ if (!IS_FWI2_CAPABLE(ha)) {
+ if (IS_QLA2100(ha))
+ host->sg_tablesize = 32;
+ } else {
+ if (!IS_QLA82XX(ha))
+ host->sg_tablesize = QLA_SG_ALL;
+ }
+ ql_dbg(ql_dbg_init, base_vha, 0x0032,
+ "can_queue=%d, req=%p, "
+ "mgmt_svr_loop_id=%d, sg_tablesize=%d.\n",
+ host->can_queue, base_vha->req,
+ base_vha->mgmt_svr_loop_id, host->sg_tablesize);
+ host->max_id = ha->max_fibre_devices;
+ host->this_id = 255;
+ host->cmd_per_lun = 3;
+ host->unique_id = host->host_no;
+ if (IS_T10_PI_CAPABLE(ha) && ql2xenabledif)
+ host->max_cmd_len = 32;
+ else
+ host->max_cmd_len = MAX_CMDSZ;
+ host->max_channel = MAX_BUSES - 1;
+ host->max_lun = ql2xmaxlun;
+ host->transportt = qla2xxx_transport_template;
+ sht->vendor_id = (SCSI_NL_VID_TYPE_PCI | PCI_VENDOR_ID_QLOGIC);
+
+ ql_dbg(ql_dbg_init, base_vha, 0x0033,
+ "max_id=%d this_id=%d "
+ "cmd_per_len=%d unique_id=%d max_cmd_len=%d max_channel=%d "
+ "max_lun=%d transportt=%p, vendor_id=%llu.\n", host->max_id,
+ host->this_id, host->cmd_per_lun, host->unique_id,
+ host->max_cmd_len, host->max_channel, host->max_lun,
+ host->transportt, sht->vendor_id);
+
+ /* Set up the irqs */
+ ret = qla2x00_request_irqs(ha, rsp);
+ if (ret)
+ goto probe_init_failed;
+
+ pci_save_state(pdev);
+
+ /* Alloc arrays of request and response ring ptrs */
+que_init:
+ if (!qla2x00_alloc_queues(ha)) {
+ ql_log(ql_log_fatal, base_vha, 0x003d,
+ "Failed to allocate memory for queue pointers.. aborting.\n");
+ goto probe_init_failed;
+ }
+
+ ha->rsp_q_map[0] = rsp;
+ ha->req_q_map[0] = req;
+ rsp->req = req;
+ req->rsp = rsp;
+ set_bit(0, ha->req_qid_map);
+ set_bit(0, ha->rsp_qid_map);
+ /* FWI2-capable only. */
+ req->req_q_in = &ha->iobase->isp24.req_q_in;
+ req->req_q_out = &ha->iobase->isp24.req_q_out;
+ rsp->rsp_q_in = &ha->iobase->isp24.rsp_q_in;
+ rsp->rsp_q_out = &ha->iobase->isp24.rsp_q_out;
+ if (ha->mqenable || IS_QLA83XX(ha)) {
+ req->req_q_in = &ha->mqiobase->isp25mq.req_q_in;
+ req->req_q_out = &ha->mqiobase->isp25mq.req_q_out;
+ rsp->rsp_q_in = &ha->mqiobase->isp25mq.rsp_q_in;
+ rsp->rsp_q_out = &ha->mqiobase->isp25mq.rsp_q_out;
+ }
+
+ if (IS_QLA82XX(ha)) {
+ req->req_q_out = &ha->iobase->isp82.req_q_out[0];
+ rsp->rsp_q_in = &ha->iobase->isp82.rsp_q_in[0];
+ rsp->rsp_q_out = &ha->iobase->isp82.rsp_q_out[0];
+ }
+
+ ql_dbg(ql_dbg_multiq, base_vha, 0xc009,
+ "rsp_q_map=%p req_q_map=%p rsp->req=%p req->rsp=%p.\n",
+ ha->rsp_q_map, ha->req_q_map, rsp->req, req->rsp);
+ ql_dbg(ql_dbg_multiq, base_vha, 0xc00a,
+ "req->req_q_in=%p req->req_q_out=%p "
+ "rsp->rsp_q_in=%p rsp->rsp_q_out=%p.\n",
+ req->req_q_in, req->req_q_out,
+ rsp->rsp_q_in, rsp->rsp_q_out);
+ ql_dbg(ql_dbg_init, base_vha, 0x003e,
+ "rsp_q_map=%p req_q_map=%p rsp->req=%p req->rsp=%p.\n",
+ ha->rsp_q_map, ha->req_q_map, rsp->req, req->rsp);
+ ql_dbg(ql_dbg_init, base_vha, 0x003f,
+ "req->req_q_in=%p req->req_q_out=%p rsp->rsp_q_in=%p rsp->rsp_q_out=%p.\n",
+ req->req_q_in, req->req_q_out, rsp->rsp_q_in, rsp->rsp_q_out);
+
+ if (qla2x00_initialize_adapter(base_vha)) {
+ ql_log(ql_log_fatal, base_vha, 0x00d6,
+ "Failed to initialize adapter - Adapter flags %x.\n",
+ base_vha->device_flags);
+
+ if (IS_QLA82XX(ha)) {
+ qla82xx_idc_lock(ha);
+ qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
+ QLA82XX_DEV_FAILED);
+ qla82xx_idc_unlock(ha);
+ ql_log(ql_log_fatal, base_vha, 0x00d7,
+ "HW State: FAILED.\n");
+ }
+
+ ret = -ENODEV;
+ goto probe_failed;
+ }
+
+ if (ha->mqenable) {
+ if (qla25xx_setup_mode(base_vha)) {
+ ql_log(ql_log_warn, base_vha, 0x00ec,
+ "Failed to create queues, falling back to single queue mode.\n");
+ goto que_init;
+ }
+ }
+
+ if (ha->flags.running_gold_fw)
+ goto skip_dpc;
+
+ /*
+ * Startup the kernel thread for this host adapter
+ */
+ ha->dpc_thread = kthread_create(qla2x00_do_dpc, ha,
+ "%s_dpc", base_vha->host_str);
+ if (IS_ERR(ha->dpc_thread)) {
+ ql_log(ql_log_fatal, base_vha, 0x00ed,
+ "Failed to start DPC thread.\n");
+ ret = PTR_ERR(ha->dpc_thread);
+ goto probe_failed;
+ }
+ ql_dbg(ql_dbg_init, base_vha, 0x00ee,
+ "DPC thread started successfully.\n");
+
+skip_dpc:
+ list_add_tail(&base_vha->list, &ha->vp_list);
+ base_vha->host->irq = ha->pdev->irq;
+
+ /* Initialized the timer */
+ qla2x00_start_timer(base_vha, qla2x00_timer, WATCH_INTERVAL);
+ ql_dbg(ql_dbg_init, base_vha, 0x00ef,
+ "Started qla2x00_timer with "
+ "interval=%d.\n", WATCH_INTERVAL);
+ ql_dbg(ql_dbg_init, base_vha, 0x00f0,
+ "Detected hba at address=%p.\n",
+ ha);
+
+ if (IS_T10_PI_CAPABLE(ha) && ql2xenabledif) {
+ if (ha->fw_attributes & BIT_4) {
+ int prot = 0;
+ base_vha->flags.difdix_supported = 1;
+ ql_dbg(ql_dbg_init, base_vha, 0x00f1,
+ "Registering for DIF/DIX type 1 and 3 protection.\n");
+ if (ql2xenabledif == 1)
+ prot = SHOST_DIX_TYPE0_PROTECTION;
+ scsi_host_set_prot(host,
+ prot | SHOST_DIF_TYPE1_PROTECTION
+ | SHOST_DIF_TYPE2_PROTECTION
+ | SHOST_DIF_TYPE3_PROTECTION
+ | SHOST_DIX_TYPE1_PROTECTION
+ | SHOST_DIX_TYPE2_PROTECTION
+ | SHOST_DIX_TYPE3_PROTECTION);
+ scsi_host_set_guard(host, SHOST_DIX_GUARD_CRC);
+ } else
+ base_vha->flags.difdix_supported = 0;
+ }
+
+ ha->isp_ops->enable_intrs(ha);
+
+ ret = scsi_add_host(host, &pdev->dev);
+ if (ret)
+ goto probe_failed;
+
+ base_vha->flags.init_done = 1;
+ base_vha->flags.online = 1;
+
+ ql_dbg(ql_dbg_init, base_vha, 0x00f2,
+ "Init done and hba is online.\n");
+
+ scsi_scan_host(host);
+
+ qla2x00_alloc_sysfs_attr(base_vha);
+
+ qla2x00_init_host_attr(base_vha);
+
+ qla2x00_dfs_setup(base_vha);
+
+ ql_log(ql_log_info, base_vha, 0x00fb,
+ "QLogic %s - %s.\n",
+ ha->model_number, ha->model_desc ? ha->model_desc : "");
+ ql_log(ql_log_info, base_vha, 0x00fc,
+ "ISP%04X: %s @ %s hdma%c host#=%ld fw=%s.\n",
+ pdev->device, ha->isp_ops->pci_info_str(base_vha, pci_info),
+ pci_name(pdev), ha->flags.enable_64bit_addressing ? '+' : '-',
+ base_vha->host_no,
+ ha->isp_ops->fw_version_str(base_vha, fw_str));
+
+ return 0;
+
+probe_init_failed:
+ qla2x00_free_req_que(ha, req);
+ qla2x00_free_rsp_que(ha, rsp);
+ ha->max_req_queues = ha->max_rsp_queues = 0;
+
+probe_failed:
+ if (base_vha->timer_active)
+ qla2x00_stop_timer(base_vha);
+ base_vha->flags.online = 0;
+ if (ha->dpc_thread) {
+ struct task_struct *t = ha->dpc_thread;
+
+ ha->dpc_thread = NULL;
+ kthread_stop(t);
+ }
+
+ qla2x00_free_device(base_vha);
+
+ scsi_host_put(base_vha->host);
+
+probe_hw_failed:
+ if (IS_QLA82XX(ha)) {
+ qla82xx_idc_lock(ha);
+ qla82xx_clear_drv_active(ha);
+ qla82xx_idc_unlock(ha);
+ iounmap((device_reg_t __iomem *)ha->nx_pcibase);
+ if (!ql2xdbwr)
+ iounmap((device_reg_t __iomem *)ha->nxdb_wr_ptr);
+ } else {
+ if (ha->iobase)
+ iounmap(ha->iobase);
+ }
+ pci_release_selected_regions(ha->pdev, ha->bars);
+ kfree(ha);
+ ha = NULL;
+
+probe_out:
+ pci_disable_device(pdev);
+ return ret;
+}
+
+static void
+qla2x00_shutdown(struct pci_dev *pdev)
+{
+ scsi_qla_host_t *vha;
+ struct qla_hw_data *ha;
+
+ vha = pci_get_drvdata(pdev);
+ ha = vha->hw;
+
+ /* Turn-off FCE trace */
+ if (ha->flags.fce_enabled) {
+ qla2x00_disable_fce_trace(vha, NULL, NULL);
+ ha->flags.fce_enabled = 0;
+ }
+
+ /* Turn-off EFT trace */
+ if (ha->eft)
+ qla2x00_disable_eft_trace(vha);
+
+ /* Stop currently executing firmware. */
+ qla2x00_try_to_stop_firmware(vha);
+
+ /* Turn adapter off line */
+ vha->flags.online = 0;
+
+ /* turn-off interrupts on the card */
+ if (ha->interrupts_on) {
+ vha->flags.init_done = 0;
+ ha->isp_ops->disable_intrs(ha);
+ }
+
+ qla2x00_free_irqs(vha);
+
+ qla2x00_free_fw_dump(ha);
+}
+
+static void
+qla2x00_remove_one(struct pci_dev *pdev)
+{
+ scsi_qla_host_t *base_vha, *vha;
+ struct qla_hw_data *ha;
+ unsigned long flags;
+
+ base_vha = pci_get_drvdata(pdev);
+ ha = base_vha->hw;
+
+ mutex_lock(&ha->vport_lock);
+ while (ha->cur_vport_count) {
+ struct Scsi_Host *scsi_host;
+
+ spin_lock_irqsave(&ha->vport_slock, flags);
+
+ BUG_ON(base_vha->list.next == &ha->vp_list);
+ /* This assumes first entry in ha->vp_list is always base vha */
+ vha = list_first_entry(&base_vha->list, scsi_qla_host_t, list);
+ scsi_host = scsi_host_get(vha->host);
+
+ spin_unlock_irqrestore(&ha->vport_slock, flags);
+ mutex_unlock(&ha->vport_lock);
+
+ fc_vport_terminate(vha->fc_vport);
+ scsi_host_put(vha->host);
+
+ mutex_lock(&ha->vport_lock);
+ }
+ mutex_unlock(&ha->vport_lock);
+
+ set_bit(UNLOADING, &base_vha->dpc_flags);
+
+ qla2x00_abort_all_cmds(base_vha, DID_NO_CONNECT << 16);
+
+ qla2x00_dfs_remove(base_vha);
+
+ qla84xx_put_chip(base_vha);
+
+ /* Disable timer */
+ if (base_vha->timer_active)
+ qla2x00_stop_timer(base_vha);
+
+ base_vha->flags.online = 0;
+
+ /* Flush the work queue and remove it */
+ if (ha->wq) {
+ flush_workqueue(ha->wq);
+ destroy_workqueue(ha->wq);
+ ha->wq = NULL;
+ }
+
+ /* Kill the kernel thread for this host */
+ if (ha->dpc_thread) {
+ struct task_struct *t = ha->dpc_thread;
+
+ /*
+ * qla2xxx_wake_dpc checks for ->dpc_thread
+ * so we need to zero it out.
+ */
+ ha->dpc_thread = NULL;
+ kthread_stop(t);
+ }
+
+ qla2x00_free_sysfs_attr(base_vha);
+
+ fc_remove_host(base_vha->host);
+
+ scsi_remove_host(base_vha->host);
+
+ qla2x00_free_device(base_vha);
+
+ scsi_host_put(base_vha->host);
+
+ if (IS_QLA82XX(ha)) {
+ qla82xx_idc_lock(ha);
+ qla82xx_clear_drv_active(ha);
+ qla82xx_idc_unlock(ha);
+
+ iounmap((device_reg_t __iomem *)ha->nx_pcibase);
+ if (!ql2xdbwr)
+ iounmap((device_reg_t __iomem *)ha->nxdb_wr_ptr);
+ } else {
+ if (ha->iobase)
+ iounmap(ha->iobase);
+
+ if (ha->mqiobase)
+ iounmap(ha->mqiobase);
+
+ if (IS_QLA83XX(ha) && ha->msixbase)
+ iounmap(ha->msixbase);
+ }
+
+ pci_release_selected_regions(ha->pdev, ha->bars);
+ kfree(ha);
+ ha = NULL;
+
+ pci_disable_pcie_error_reporting(pdev);
+
+ pci_disable_device(pdev);
+ pci_set_drvdata(pdev, NULL);
+}
+
+static void
+qla2x00_free_device(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+
+ qla2x00_abort_all_cmds(vha, DID_NO_CONNECT << 16);
+
+ /* Disable timer */
+ if (vha->timer_active)
+ qla2x00_stop_timer(vha);
+
+ /* Kill the kernel thread for this host */
+ if (ha->dpc_thread) {
+ struct task_struct *t = ha->dpc_thread;
+
+ /*
+ * qla2xxx_wake_dpc checks for ->dpc_thread
+ * so we need to zero it out.
+ */
+ ha->dpc_thread = NULL;
+ kthread_stop(t);
+ }
+
+ qla25xx_delete_queues(vha);
+
+ if (ha->flags.fce_enabled)
+ qla2x00_disable_fce_trace(vha, NULL, NULL);
+
+ if (ha->eft)
+ qla2x00_disable_eft_trace(vha);
+
+ /* Stop currently executing firmware. */
+ qla2x00_try_to_stop_firmware(vha);
+
+ vha->flags.online = 0;
+
+ /* turn-off interrupts on the card */
+ if (ha->interrupts_on) {
+ vha->flags.init_done = 0;
+ ha->isp_ops->disable_intrs(ha);
+ }
+
+ qla2x00_free_irqs(vha);
+
+ qla2x00_free_fcports(vha);
+
+ qla2x00_mem_free(ha);
+
+ qla82xx_md_free(vha);
+
+ qla2x00_free_queues(ha);
+}
+
+void qla2x00_free_fcports(struct scsi_qla_host *vha)
+{
+ fc_port_t *fcport, *tfcport;
+
+ list_for_each_entry_safe(fcport, tfcport, &vha->vp_fcports, list) {
+ list_del(&fcport->list);
+ kfree(fcport);
+ fcport = NULL;
+ }
+}
+
+static inline void
+qla2x00_schedule_rport_del(struct scsi_qla_host *vha, fc_port_t *fcport,
+ int defer)
+{
+ struct fc_rport *rport;
+ scsi_qla_host_t *base_vha;
+ unsigned long flags;
+
+ if (!fcport->rport)
+ return;
+
+ rport = fcport->rport;
+ if (defer) {
+ base_vha = pci_get_drvdata(vha->hw->pdev);
+ spin_lock_irqsave(vha->host->host_lock, flags);
+ fcport->drport = rport;
+ spin_unlock_irqrestore(vha->host->host_lock, flags);
+ set_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags);
+ qla2xxx_wake_dpc(base_vha);
+ } else
+ fc_remote_port_delete(rport);
+}
+
+/*
+ * qla2x00_mark_device_lost Updates fcport state when device goes offline.
+ *
+ * Input: ha = adapter block pointer. fcport = port structure pointer.
+ *
+ * Return: None.
+ *
+ * Context:
+ */
+void qla2x00_mark_device_lost(scsi_qla_host_t *vha, fc_port_t *fcport,
+ int do_login, int defer)
+{
+ if (atomic_read(&fcport->state) == FCS_ONLINE &&
+ vha->vp_idx == fcport->vp_idx) {
+ qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST);
+ qla2x00_schedule_rport_del(vha, fcport, defer);
+ }
+ /*
+ * We may need to retry the login, so don't change the state of the
+ * port but do the retries.
+ */
+ if (atomic_read(&fcport->state) != FCS_DEVICE_DEAD)
+ qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST);
+
+ if (!do_login)
+ return;
+
+ if (fcport->login_retry == 0) {
+ fcport->login_retry = vha->hw->login_retry_count;
+ set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
+
+ ql_dbg(ql_dbg_disc, vha, 0x2067,
+ "Port login retry "
+ "%02x%02x%02x%02x%02x%02x%02x%02x, "
+ "id = 0x%04x retry cnt=%d.\n",
+ fcport->port_name[0], fcport->port_name[1],
+ fcport->port_name[2], fcport->port_name[3],
+ fcport->port_name[4], fcport->port_name[5],
+ fcport->port_name[6], fcport->port_name[7],
+ fcport->loop_id, fcport->login_retry);
+ }
+}
+
+/*
+ * qla2x00_mark_all_devices_lost
+ * Updates fcport state when device goes offline.
+ *
+ * Input:
+ * ha = adapter block pointer.
+ * fcport = port structure pointer.
+ *
+ * Return:
+ * None.
+ *
+ * Context:
+ */
+void
+qla2x00_mark_all_devices_lost(scsi_qla_host_t *vha, int defer)
+{
+ fc_port_t *fcport;
+
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ if (vha->vp_idx != 0 && vha->vp_idx != fcport->vp_idx)
+ continue;
+
+ /*
+ * No point in marking the device as lost, if the device is
+ * already DEAD.
+ */
+ if (atomic_read(&fcport->state) == FCS_DEVICE_DEAD)
+ continue;
+ if (atomic_read(&fcport->state) == FCS_ONLINE) {
+ qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST);
+ if (defer)
+ qla2x00_schedule_rport_del(vha, fcport, defer);
+ else if (vha->vp_idx == fcport->vp_idx)
+ qla2x00_schedule_rport_del(vha, fcport, defer);
+ }
+ }
+}
+
+/*
+* qla2x00_mem_alloc
+* Allocates adapter memory.
+*
+* Returns:
+* 0 = success.
+* !0 = failure.
+*/
+static int
+qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len,
+ struct req_que **req, struct rsp_que **rsp)
+{
+ char name[16];
+
+ ha->init_cb = dma_alloc_coherent(&ha->pdev->dev, ha->init_cb_size,
+ &ha->init_cb_dma, GFP_KERNEL);
+ if (!ha->init_cb)
+ goto fail;
+
+ ha->gid_list = dma_alloc_coherent(&ha->pdev->dev,
+ qla2x00_gid_list_size(ha), &ha->gid_list_dma, GFP_KERNEL);
+ if (!ha->gid_list)
+ goto fail_free_init_cb;
+
+ ha->srb_mempool = mempool_create_slab_pool(SRB_MIN_REQ, srb_cachep);
+ if (!ha->srb_mempool)
+ goto fail_free_gid_list;
+
+ if (IS_QLA82XX(ha)) {
+ /* Allocate cache for CT6 Ctx. */
+ if (!ctx_cachep) {
+ ctx_cachep = kmem_cache_create("qla2xxx_ctx",
+ sizeof(struct ct6_dsd), 0,
+ SLAB_HWCACHE_ALIGN, NULL);
+ if (!ctx_cachep)
+ goto fail_free_gid_list;
+ }
+ ha->ctx_mempool = mempool_create_slab_pool(SRB_MIN_REQ,
+ ctx_cachep);
+ if (!ha->ctx_mempool)
+ goto fail_free_srb_mempool;
+ ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0021,
+ "ctx_cachep=%p ctx_mempool=%p.\n",
+ ctx_cachep, ha->ctx_mempool);
+ }
+
+ /* Get memory for cached NVRAM */
+ ha->nvram = kzalloc(MAX_NVRAM_SIZE, GFP_KERNEL);
+ if (!ha->nvram)
+ goto fail_free_ctx_mempool;
+
+ snprintf(name, sizeof(name), "%s_%d", QLA2XXX_DRIVER_NAME,
+ ha->pdev->device);
+ ha->s_dma_pool = dma_pool_create(name, &ha->pdev->dev,
+ DMA_POOL_SIZE, 8, 0);
+ if (!ha->s_dma_pool)
+ goto fail_free_nvram;
+
+ ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0022,
+ "init_cb=%p gid_list=%p, srb_mempool=%p s_dma_pool=%p.\n",
+ ha->init_cb, ha->gid_list, ha->srb_mempool, ha->s_dma_pool);
+
+ if (IS_QLA82XX(ha) || ql2xenabledif) {
+ ha->dl_dma_pool = dma_pool_create(name, &ha->pdev->dev,
+ DSD_LIST_DMA_POOL_SIZE, 8, 0);
+ if (!ha->dl_dma_pool) {
+ ql_log_pci(ql_log_fatal, ha->pdev, 0x0023,
+ "Failed to allocate memory for dl_dma_pool.\n");
+ goto fail_s_dma_pool;
+ }
+
+ ha->fcp_cmnd_dma_pool = dma_pool_create(name, &ha->pdev->dev,
+ FCP_CMND_DMA_POOL_SIZE, 8, 0);
+ if (!ha->fcp_cmnd_dma_pool) {
+ ql_log_pci(ql_log_fatal, ha->pdev, 0x0024,
+ "Failed to allocate memory for fcp_cmnd_dma_pool.\n");
+ goto fail_dl_dma_pool;
+ }
+ ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0025,
+ "dl_dma_pool=%p fcp_cmnd_dma_pool=%p.\n",
+ ha->dl_dma_pool, ha->fcp_cmnd_dma_pool);
+ }
+
+ /* Allocate memory for SNS commands */
+ if (IS_QLA2100(ha) || IS_QLA2200(ha)) {
+ /* Get consistent memory allocated for SNS commands */
+ ha->sns_cmd = dma_alloc_coherent(&ha->pdev->dev,
+ sizeof(struct sns_cmd_pkt), &ha->sns_cmd_dma, GFP_KERNEL);
+ if (!ha->sns_cmd)
+ goto fail_dma_pool;
+ ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0026,
+ "sns_cmd: %p.\n", ha->sns_cmd);
+ } else {
+ /* Get consistent memory allocated for MS IOCB */
+ ha->ms_iocb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
+ &ha->ms_iocb_dma);
+ if (!ha->ms_iocb)
+ goto fail_dma_pool;
+ /* Get consistent memory allocated for CT SNS commands */
+ ha->ct_sns = dma_alloc_coherent(&ha->pdev->dev,
+ sizeof(struct ct_sns_pkt), &ha->ct_sns_dma, GFP_KERNEL);
+ if (!ha->ct_sns)
+ goto fail_free_ms_iocb;
+ ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0027,
+ "ms_iocb=%p ct_sns=%p.\n",
+ ha->ms_iocb, ha->ct_sns);
+ }
+
+ /* Allocate memory for request ring */
+ *req = kzalloc(sizeof(struct req_que), GFP_KERNEL);
+ if (!*req) {
+ ql_log_pci(ql_log_fatal, ha->pdev, 0x0028,
+ "Failed to allocate memory for req.\n");
+ goto fail_req;
+ }
+ (*req)->length = req_len;
+ (*req)->ring = dma_alloc_coherent(&ha->pdev->dev,
+ ((*req)->length + 1) * sizeof(request_t),
+ &(*req)->dma, GFP_KERNEL);
+ if (!(*req)->ring) {
+ ql_log_pci(ql_log_fatal, ha->pdev, 0x0029,
+ "Failed to allocate memory for req_ring.\n");
+ goto fail_req_ring;
+ }
+ /* Allocate memory for response ring */
+ *rsp = kzalloc(sizeof(struct rsp_que), GFP_KERNEL);
+ if (!*rsp) {
+ ql_log_pci(ql_log_fatal, ha->pdev, 0x002a,
+ "Failed to allocate memory for rsp.\n");
+ goto fail_rsp;
+ }
+ (*rsp)->hw = ha;
+ (*rsp)->length = rsp_len;
+ (*rsp)->ring = dma_alloc_coherent(&ha->pdev->dev,
+ ((*rsp)->length + 1) * sizeof(response_t),
+ &(*rsp)->dma, GFP_KERNEL);
+ if (!(*rsp)->ring) {
+ ql_log_pci(ql_log_fatal, ha->pdev, 0x002b,
+ "Failed to allocate memory for rsp_ring.\n");
+ goto fail_rsp_ring;
+ }
+ (*req)->rsp = *rsp;
+ (*rsp)->req = *req;
+ ql_dbg_pci(ql_dbg_init, ha->pdev, 0x002c,
+ "req=%p req->length=%d req->ring=%p rsp=%p "
+ "rsp->length=%d rsp->ring=%p.\n",
+ *req, (*req)->length, (*req)->ring, *rsp, (*rsp)->length,
+ (*rsp)->ring);
+ /* Allocate memory for NVRAM data for vports */
+ if (ha->nvram_npiv_size) {
+ ha->npiv_info = kzalloc(sizeof(struct qla_npiv_entry) *
+ ha->nvram_npiv_size, GFP_KERNEL);
+ if (!ha->npiv_info) {
+ ql_log_pci(ql_log_fatal, ha->pdev, 0x002d,
+ "Failed to allocate memory for npiv_info.\n");
+ goto fail_npiv_info;
+ }
+ } else
+ ha->npiv_info = NULL;
+
+ /* Get consistent memory allocated for EX-INIT-CB. */
+ if (IS_CNA_CAPABLE(ha) || IS_QLA2031(ha)) {
+ ha->ex_init_cb = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
+ &ha->ex_init_cb_dma);
+ if (!ha->ex_init_cb)
+ goto fail_ex_init_cb;
+ ql_dbg_pci(ql_dbg_init, ha->pdev, 0x002e,
+ "ex_init_cb=%p.\n", ha->ex_init_cb);
+ }
+
+ INIT_LIST_HEAD(&ha->gbl_dsd_list);
+
+ /* Get consistent memory allocated for Async Port-Database. */
+ if (!IS_FWI2_CAPABLE(ha)) {
+ ha->async_pd = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL,
+ &ha->async_pd_dma);
+ if (!ha->async_pd)
+ goto fail_async_pd;
+ ql_dbg_pci(ql_dbg_init, ha->pdev, 0x002f,
+ "async_pd=%p.\n", ha->async_pd);
+ }
+
+ INIT_LIST_HEAD(&ha->vp_list);
+ return 1;
+
+fail_async_pd:
+ dma_pool_free(ha->s_dma_pool, ha->ex_init_cb, ha->ex_init_cb_dma);
+fail_ex_init_cb:
+ kfree(ha->npiv_info);
+fail_npiv_info:
+ dma_free_coherent(&ha->pdev->dev, ((*rsp)->length + 1) *
+ sizeof(response_t), (*rsp)->ring, (*rsp)->dma);
+ (*rsp)->ring = NULL;
+ (*rsp)->dma = 0;
+fail_rsp_ring:
+ kfree(*rsp);
+fail_rsp:
+ dma_free_coherent(&ha->pdev->dev, ((*req)->length + 1) *
+ sizeof(request_t), (*req)->ring, (*req)->dma);
+ (*req)->ring = NULL;
+ (*req)->dma = 0;
+fail_req_ring:
+ kfree(*req);
+fail_req:
+ dma_free_coherent(&ha->pdev->dev, sizeof(struct ct_sns_pkt),
+ ha->ct_sns, ha->ct_sns_dma);
+ ha->ct_sns = NULL;
+ ha->ct_sns_dma = 0;
+fail_free_ms_iocb:
+ dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma);
+ ha->ms_iocb = NULL;
+ ha->ms_iocb_dma = 0;
+fail_dma_pool:
+ if (IS_QLA82XX(ha) || ql2xenabledif) {
+ dma_pool_destroy(ha->fcp_cmnd_dma_pool);
+ ha->fcp_cmnd_dma_pool = NULL;
+ }
+fail_dl_dma_pool:
+ if (IS_QLA82XX(ha) || ql2xenabledif) {
+ dma_pool_destroy(ha->dl_dma_pool);
+ ha->dl_dma_pool = NULL;
+ }
+fail_s_dma_pool:
+ dma_pool_destroy(ha->s_dma_pool);
+ ha->s_dma_pool = NULL;
+fail_free_nvram:
+ kfree(ha->nvram);
+ ha->nvram = NULL;
+fail_free_ctx_mempool:
+ mempool_destroy(ha->ctx_mempool);
+ ha->ctx_mempool = NULL;
+fail_free_srb_mempool:
+ mempool_destroy(ha->srb_mempool);
+ ha->srb_mempool = NULL;
+fail_free_gid_list:
+ dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha),
+ ha->gid_list,
+ ha->gid_list_dma);
+ ha->gid_list = NULL;
+ ha->gid_list_dma = 0;
+fail_free_init_cb:
+ dma_free_coherent(&ha->pdev->dev, ha->init_cb_size, ha->init_cb,
+ ha->init_cb_dma);
+ ha->init_cb = NULL;
+ ha->init_cb_dma = 0;
+fail:
+ ql_log(ql_log_fatal, NULL, 0x0030,
+ "Memory allocation failure.\n");
+ return -ENOMEM;
+}
+
+/*
+* qla2x00_free_fw_dump
+* Frees fw dump stuff.
+*
+* Input:
+* ha = adapter block pointer.
+*/
+static void
+qla2x00_free_fw_dump(struct qla_hw_data *ha)
+{
+ if (ha->fce)
+ dma_free_coherent(&ha->pdev->dev, FCE_SIZE, ha->fce,
+ ha->fce_dma);
+
+ if (ha->fw_dump) {
+ if (ha->eft)
+ dma_free_coherent(&ha->pdev->dev,
+ ntohl(ha->fw_dump->eft_size), ha->eft, ha->eft_dma);
+ vfree(ha->fw_dump);
+ }
+ ha->fce = NULL;
+ ha->fce_dma = 0;
+ ha->eft = NULL;
+ ha->eft_dma = 0;
+ ha->fw_dump = NULL;
+ ha->fw_dumped = 0;
+ ha->fw_dump_reading = 0;
+}
+
+/*
+* qla2x00_mem_free
+* Frees all adapter allocated memory.
+*
+* Input:
+* ha = adapter block pointer.
+*/
+static void
+qla2x00_mem_free(struct qla_hw_data *ha)
+{
+ qla2x00_free_fw_dump(ha);
+
+ if (ha->srb_mempool)
+ mempool_destroy(ha->srb_mempool);
+
+ if (ha->dcbx_tlv)
+ dma_free_coherent(&ha->pdev->dev, DCBX_TLV_DATA_SIZE,
+ ha->dcbx_tlv, ha->dcbx_tlv_dma);
+
+ if (ha->xgmac_data)
+ dma_free_coherent(&ha->pdev->dev, XGMAC_DATA_SIZE,
+ ha->xgmac_data, ha->xgmac_data_dma);
+
+ if (ha->sns_cmd)
+ dma_free_coherent(&ha->pdev->dev, sizeof(struct sns_cmd_pkt),
+ ha->sns_cmd, ha->sns_cmd_dma);
+
+ if (ha->ct_sns)
+ dma_free_coherent(&ha->pdev->dev, sizeof(struct ct_sns_pkt),
+ ha->ct_sns, ha->ct_sns_dma);
+
+ if (ha->sfp_data)
+ dma_pool_free(ha->s_dma_pool, ha->sfp_data, ha->sfp_data_dma);
+
+ if (ha->ms_iocb)
+ dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma);
+
+ if (ha->ex_init_cb)
+ dma_pool_free(ha->s_dma_pool,
+ ha->ex_init_cb, ha->ex_init_cb_dma);
+
+ if (ha->async_pd)
+ dma_pool_free(ha->s_dma_pool, ha->async_pd, ha->async_pd_dma);
+
+ if (ha->s_dma_pool)
+ dma_pool_destroy(ha->s_dma_pool);
+
+ if (ha->gid_list)
+ dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha),
+ ha->gid_list, ha->gid_list_dma);
+
+ if (IS_QLA82XX(ha)) {
+ if (!list_empty(&ha->gbl_dsd_list)) {
+ struct dsd_dma *dsd_ptr, *tdsd_ptr;
+
+ /* clean up allocated prev pool */
+ list_for_each_entry_safe(dsd_ptr,
+ tdsd_ptr, &ha->gbl_dsd_list, list) {
+ dma_pool_free(ha->dl_dma_pool,
+ dsd_ptr->dsd_addr, dsd_ptr->dsd_list_dma);
+ list_del(&dsd_ptr->list);
+ kfree(dsd_ptr);
+ }
+ }
+ }
+
+ if (ha->dl_dma_pool)
+ dma_pool_destroy(ha->dl_dma_pool);
+
+ if (ha->fcp_cmnd_dma_pool)
+ dma_pool_destroy(ha->fcp_cmnd_dma_pool);
+
+ if (ha->ctx_mempool)
+ mempool_destroy(ha->ctx_mempool);
+
+ if (ha->init_cb)
+ dma_free_coherent(&ha->pdev->dev, ha->init_cb_size,
+ ha->init_cb, ha->init_cb_dma);
+ vfree(ha->optrom_buffer);
+ kfree(ha->nvram);
+ kfree(ha->npiv_info);
+ kfree(ha->swl);
+
+ ha->srb_mempool = NULL;
+ ha->ctx_mempool = NULL;
+ ha->sns_cmd = NULL;
+ ha->sns_cmd_dma = 0;
+ ha->ct_sns = NULL;
+ ha->ct_sns_dma = 0;
+ ha->ms_iocb = NULL;
+ ha->ms_iocb_dma = 0;
+ ha->init_cb = NULL;
+ ha->init_cb_dma = 0;
+ ha->ex_init_cb = NULL;
+ ha->ex_init_cb_dma = 0;
+ ha->async_pd = NULL;
+ ha->async_pd_dma = 0;
+
+ ha->s_dma_pool = NULL;
+ ha->dl_dma_pool = NULL;
+ ha->fcp_cmnd_dma_pool = NULL;
+
+ ha->gid_list = NULL;
+ ha->gid_list_dma = 0;
+}
+
+struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht,
+ struct qla_hw_data *ha)
+{
+ struct Scsi_Host *host;
+ struct scsi_qla_host *vha = NULL;
+
+ host = scsi_host_alloc(sht, sizeof(scsi_qla_host_t));
+ if (host == NULL) {
+ ql_log_pci(ql_log_fatal, ha->pdev, 0x0107,
+ "Failed to allocate host from the scsi layer, aborting.\n");
+ goto fail;
+ }
+
+ /* Clear our data area */
+ vha = shost_priv(host);
+ memset(vha, 0, sizeof(scsi_qla_host_t));
+
+ vha->host = host;
+ vha->host_no = host->host_no;
+ vha->hw = ha;
+
+ INIT_LIST_HEAD(&vha->vp_fcports);
+ INIT_LIST_HEAD(&vha->work_list);
+ INIT_LIST_HEAD(&vha->list);
+
+ spin_lock_init(&vha->work_lock);
+
+ sprintf(vha->host_str, "%s_%ld", QLA2XXX_DRIVER_NAME, vha->host_no);
+ ql_dbg(ql_dbg_init, vha, 0x0041,
+ "Allocated the host=%p hw=%p vha=%p dev_name=%s",
+ vha->host, vha->hw, vha,
+ dev_name(&(ha->pdev->dev)));
+
+ return vha;
+
+fail:
+ return vha;
+}
+
+static struct qla_work_evt *
+qla2x00_alloc_work(struct scsi_qla_host *vha, enum qla_work_type type)
+{
+ struct qla_work_evt *e;
+ uint8_t bail;
+
+ QLA_VHA_MARK_BUSY(vha, bail);
+ if (bail)
+ return NULL;
+
+ e = kzalloc(sizeof(struct qla_work_evt), GFP_ATOMIC);
+ if (!e) {
+ QLA_VHA_MARK_NOT_BUSY(vha);
+ return NULL;
+ }
+
+ INIT_LIST_HEAD(&e->list);
+ e->type = type;
+ e->flags = QLA_EVT_FLAG_FREE;
+ return e;
+}
+
+static int
+qla2x00_post_work(struct scsi_qla_host *vha, struct qla_work_evt *e)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&vha->work_lock, flags);
+ list_add_tail(&e->list, &vha->work_list);
+ spin_unlock_irqrestore(&vha->work_lock, flags);
+ qla2xxx_wake_dpc(vha);
+
+ return QLA_SUCCESS;
+}
+
+int
+qla2x00_post_aen_work(struct scsi_qla_host *vha, enum fc_host_event_code code,
+ u32 data)
+{
+ struct qla_work_evt *e;
+
+ e = qla2x00_alloc_work(vha, QLA_EVT_AEN);
+ if (!e)
+ return QLA_FUNCTION_FAILED;
+
+ e->u.aen.code = code;
+ e->u.aen.data = data;
+ return qla2x00_post_work(vha, e);
+}
+
+int
+qla2x00_post_idc_ack_work(struct scsi_qla_host *vha, uint16_t *mb)
+{
+ struct qla_work_evt *e;
+
+ e = qla2x00_alloc_work(vha, QLA_EVT_IDC_ACK);
+ if (!e)
+ return QLA_FUNCTION_FAILED;
+
+ memcpy(e->u.idc_ack.mb, mb, QLA_IDC_ACK_REGS * sizeof(uint16_t));
+ return qla2x00_post_work(vha, e);
+}
+
+#define qla2x00_post_async_work(name, type) \
+int qla2x00_post_async_##name##_work( \
+ struct scsi_qla_host *vha, \
+ fc_port_t *fcport, uint16_t *data) \
+{ \
+ struct qla_work_evt *e; \
+ \
+ e = qla2x00_alloc_work(vha, type); \
+ if (!e) \
+ return QLA_FUNCTION_FAILED; \
+ \
+ e->u.logio.fcport = fcport; \
+ if (data) { \
+ e->u.logio.data[0] = data[0]; \
+ e->u.logio.data[1] = data[1]; \
+ } \
+ return qla2x00_post_work(vha, e); \
+}
+
+qla2x00_post_async_work(login, QLA_EVT_ASYNC_LOGIN);
+qla2x00_post_async_work(login_done, QLA_EVT_ASYNC_LOGIN_DONE);
+qla2x00_post_async_work(logout, QLA_EVT_ASYNC_LOGOUT);
+qla2x00_post_async_work(logout_done, QLA_EVT_ASYNC_LOGOUT_DONE);
+qla2x00_post_async_work(adisc, QLA_EVT_ASYNC_ADISC);
+qla2x00_post_async_work(adisc_done, QLA_EVT_ASYNC_ADISC_DONE);
+
+int
+qla2x00_post_uevent_work(struct scsi_qla_host *vha, u32 code)
+{
+ struct qla_work_evt *e;
+
+ e = qla2x00_alloc_work(vha, QLA_EVT_UEVENT);
+ if (!e)
+ return QLA_FUNCTION_FAILED;
+
+ e->u.uevent.code = code;
+ return qla2x00_post_work(vha, e);
+}
+
+static void
+qla2x00_uevent_emit(struct scsi_qla_host *vha, u32 code)
+{
+ char event_string[40];
+ char *envp[] = { event_string, NULL };
+
+ switch (code) {
+ case QLA_UEVENT_CODE_FW_DUMP:
+ snprintf(event_string, sizeof(event_string), "FW_DUMP=%ld",
+ vha->host_no);
+ break;
+ default:
+ /* do nothing */
+ break;
+ }
+ kobject_uevent_env(&vha->hw->pdev->dev.kobj, KOBJ_CHANGE, envp);
+}
+
+void
+qla2x00_do_work(struct scsi_qla_host *vha)
+{
+ struct qla_work_evt *e, *tmp;
+ unsigned long flags;
+ LIST_HEAD(work);
+
+ spin_lock_irqsave(&vha->work_lock, flags);
+ list_splice_init(&vha->work_list, &work);
+ spin_unlock_irqrestore(&vha->work_lock, flags);
+
+ list_for_each_entry_safe(e, tmp, &work, list) {
+ list_del_init(&e->list);
+
+ switch (e->type) {
+ case QLA_EVT_AEN:
+ fc_host_post_event(vha->host, fc_get_event_number(),
+ e->u.aen.code, e->u.aen.data);
+ break;
+ case QLA_EVT_IDC_ACK:
+ qla81xx_idc_ack(vha, e->u.idc_ack.mb);
+ break;
+ case QLA_EVT_ASYNC_LOGIN:
+ qla2x00_async_login(vha, e->u.logio.fcport,
+ e->u.logio.data);
+ break;
+ case QLA_EVT_ASYNC_LOGIN_DONE:
+ qla2x00_async_login_done(vha, e->u.logio.fcport,
+ e->u.logio.data);
+ break;
+ case QLA_EVT_ASYNC_LOGOUT:
+ qla2x00_async_logout(vha, e->u.logio.fcport);
+ break;
+ case QLA_EVT_ASYNC_LOGOUT_DONE:
+ qla2x00_async_logout_done(vha, e->u.logio.fcport,
+ e->u.logio.data);
+ break;
+ case QLA_EVT_ASYNC_ADISC:
+ qla2x00_async_adisc(vha, e->u.logio.fcport,
+ e->u.logio.data);
+ break;
+ case QLA_EVT_ASYNC_ADISC_DONE:
+ qla2x00_async_adisc_done(vha, e->u.logio.fcport,
+ e->u.logio.data);
+ break;
+ case QLA_EVT_UEVENT:
+ qla2x00_uevent_emit(vha, e->u.uevent.code);
+ break;
+ }
+ if (e->flags & QLA_EVT_FLAG_FREE)
+ kfree(e);
+
+ /* For each work completed decrement vha ref count */
+ QLA_VHA_MARK_NOT_BUSY(vha);
+ }
+}
+
+/* Relogins all the fcports of a vport
+ * Context: dpc thread
+ */
+void qla2x00_relogin(struct scsi_qla_host *vha)
+{
+ fc_port_t *fcport;
+ int status;
+ uint16_t next_loopid = 0;
+ struct qla_hw_data *ha = vha->hw;
+ uint16_t data[2];
+
+ list_for_each_entry(fcport, &vha->vp_fcports, list) {
+ /*
+ * If the port is not ONLINE then try to login
+ * to it if we haven't run out of retries.
+ */
+ if (atomic_read(&fcport->state) != FCS_ONLINE &&
+ fcport->login_retry && !(fcport->flags & FCF_ASYNC_SENT)) {
+ fcport->login_retry--;
+ if (fcport->flags & FCF_FABRIC_DEVICE) {
+ if (fcport->flags & FCF_FCP2_DEVICE)
+ ha->isp_ops->fabric_logout(vha,
+ fcport->loop_id,
+ fcport->d_id.b.domain,
+ fcport->d_id.b.area,
+ fcport->d_id.b.al_pa);
+
+ if (fcport->loop_id == FC_NO_LOOP_ID) {
+ fcport->loop_id = next_loopid =
+ ha->min_external_loopid;
+ status = qla2x00_find_new_loop_id(
+ vha, fcport);
+ if (status != QLA_SUCCESS) {
+ /* Ran out of IDs to use */
+ break;
+ }
+ }
+
+ if (IS_ALOGIO_CAPABLE(ha)) {
+ fcport->flags |= FCF_ASYNC_SENT;
+ data[0] = 0;
+ data[1] = QLA_LOGIO_LOGIN_RETRIED;
+ status = qla2x00_post_async_login_work(
+ vha, fcport, data);
+ if (status == QLA_SUCCESS)
+ continue;
+ /* Attempt a retry. */
+ status = 1;
+ } else {
+ status = qla2x00_fabric_login(vha,
+ fcport, &next_loopid);
+ if (status == QLA_SUCCESS) {
+ int status2;
+ uint8_t opts;
+
+ opts = 0;
+ if (fcport->flags &
+ FCF_FCP2_DEVICE)
+ opts |= BIT_1;
+ status2 =
+ qla2x00_get_port_database(
+ vha, fcport,
+ opts);
+ if (status2 != QLA_SUCCESS)
+ status = 1;
+ }
+ }
+ } else
+ status = qla2x00_local_device_login(vha,
+ fcport);
+
+ if (status == QLA_SUCCESS) {
+ fcport->old_loop_id = fcport->loop_id;
+
+ ql_dbg(ql_dbg_disc, vha, 0x2003,
+ "Port login OK: logged in ID 0x%x.\n",
+ fcport->loop_id);
+
+ qla2x00_update_fcport(vha, fcport);
+
+ } else if (status == 1) {
+ set_bit(RELOGIN_NEEDED, &vha->dpc_flags);
+ /* retry the login again */
+ ql_dbg(ql_dbg_disc, vha, 0x2007,
+ "Retrying %d login again loop_id 0x%x.\n",
+ fcport->login_retry, fcport->loop_id);
+ } else {
+ fcport->login_retry = 0;
+ }
+
+ if (fcport->login_retry == 0 && status != QLA_SUCCESS)
+ fcport->loop_id = FC_NO_LOOP_ID;
+ }
+ if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))
+ break;
+ }
+}
+
+/**************************************************************************
+* qla2x00_do_dpc
+* This kernel thread is a task that is schedule by the interrupt handler
+* to perform the background processing for interrupts.
+*
+* Notes:
+* This task always run in the context of a kernel thread. It
+* is kick-off by the driver's detect code and starts up
+* up one per adapter. It immediately goes to sleep and waits for
+* some fibre event. When either the interrupt handler or
+* the timer routine detects a event it will one of the task
+* bits then wake us up.
+**************************************************************************/
+static int
+qla2x00_do_dpc(void *data)
+{
+ int rval;
+ scsi_qla_host_t *base_vha;
+ struct qla_hw_data *ha;
+
+ ha = (struct qla_hw_data *)data;
+ base_vha = pci_get_drvdata(ha->pdev);
+
+ set_user_nice(current, -20);
+
+ set_current_state(TASK_INTERRUPTIBLE);
+ while (!kthread_should_stop()) {
+ ql_dbg(ql_dbg_dpc, base_vha, 0x4000,
+ "DPC handler sleeping.\n");
+
+ schedule();
+ __set_current_state(TASK_RUNNING);
+
+ if (!base_vha->flags.init_done || ha->flags.mbox_busy)
+ goto end_loop;
+
+ if (ha->flags.eeh_busy) {
+ ql_dbg(ql_dbg_dpc, base_vha, 0x4003,
+ "eeh_busy=%d.\n", ha->flags.eeh_busy);
+ goto end_loop;
+ }
+
+ ha->dpc_active = 1;
+
+ ql_dbg(ql_dbg_dpc, base_vha, 0x4001,
+ "DPC handler waking up.\n");
+ ql_dbg(ql_dbg_dpc, base_vha, 0x4002,
+ "dpc_flags=0x%lx.\n", base_vha->dpc_flags);
+
+ qla2x00_do_work(base_vha);
+
+ if (IS_QLA82XX(ha)) {
+ if (test_and_clear_bit(ISP_UNRECOVERABLE,
+ &base_vha->dpc_flags)) {
+ qla82xx_idc_lock(ha);
+ qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
+ QLA82XX_DEV_FAILED);
+ qla82xx_idc_unlock(ha);
+ ql_log(ql_log_info, base_vha, 0x4004,
+ "HW State: FAILED.\n");
+ qla82xx_device_state_handler(base_vha);
+ continue;
+ }
+
+ if (test_and_clear_bit(FCOE_CTX_RESET_NEEDED,
+ &base_vha->dpc_flags)) {
+
+ ql_dbg(ql_dbg_dpc, base_vha, 0x4005,
+ "FCoE context reset scheduled.\n");
+ if (!(test_and_set_bit(ABORT_ISP_ACTIVE,
+ &base_vha->dpc_flags))) {
+ if (qla82xx_fcoe_ctx_reset(base_vha)) {
+ /* FCoE-ctx reset failed.
+ * Escalate to chip-reset
+ */
+ set_bit(ISP_ABORT_NEEDED,
+ &base_vha->dpc_flags);
+ }
+ clear_bit(ABORT_ISP_ACTIVE,
+ &base_vha->dpc_flags);
+ }
+
+ ql_dbg(ql_dbg_dpc, base_vha, 0x4006,
+ "FCoE context reset end.\n");
+ }
+ }
+
+ if (test_and_clear_bit(ISP_ABORT_NEEDED,
+ &base_vha->dpc_flags)) {
+
+ ql_dbg(ql_dbg_dpc, base_vha, 0x4007,
+ "ISP abort scheduled.\n");
+ if (!(test_and_set_bit(ABORT_ISP_ACTIVE,
+ &base_vha->dpc_flags))) {
+
+ if (ha->isp_ops->abort_isp(base_vha)) {
+ /* failed. retry later */
+ set_bit(ISP_ABORT_NEEDED,
+ &base_vha->dpc_flags);
+ }
+ clear_bit(ABORT_ISP_ACTIVE,
+ &base_vha->dpc_flags);
+ }
+
+ ql_dbg(ql_dbg_dpc, base_vha, 0x4008,
+ "ISP abort end.\n");
+ }
+
+ if (test_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags)) {
+ qla2x00_update_fcports(base_vha);
+ clear_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags);
+ }
+
+ if (test_bit(ISP_QUIESCE_NEEDED, &base_vha->dpc_flags)) {
+ ql_dbg(ql_dbg_dpc, base_vha, 0x4009,
+ "Quiescence mode scheduled.\n");
+ qla82xx_device_state_handler(base_vha);
+ clear_bit(ISP_QUIESCE_NEEDED, &base_vha->dpc_flags);
+ if (!ha->flags.quiesce_owner) {
+ qla2x00_perform_loop_resync(base_vha);
+
+ qla82xx_idc_lock(ha);
+ qla82xx_clear_qsnt_ready(base_vha);
+ qla82xx_idc_unlock(ha);
+ }
+ ql_dbg(ql_dbg_dpc, base_vha, 0x400a,
+ "Quiescence mode end.\n");
+ }
+
+ if (test_and_clear_bit(RESET_MARKER_NEEDED,
+ &base_vha->dpc_flags) &&
+ (!(test_and_set_bit(RESET_ACTIVE, &base_vha->dpc_flags)))) {
+
+ ql_dbg(ql_dbg_dpc, base_vha, 0x400b,
+ "Reset marker scheduled.\n");
+ qla2x00_rst_aen(base_vha);
+ clear_bit(RESET_ACTIVE, &base_vha->dpc_flags);
+ ql_dbg(ql_dbg_dpc, base_vha, 0x400c,
+ "Reset marker end.\n");
+ }
+
+ /* Retry each device up to login retry count */
+ if ((test_and_clear_bit(RELOGIN_NEEDED,
+ &base_vha->dpc_flags)) &&
+ !test_bit(LOOP_RESYNC_NEEDED, &base_vha->dpc_flags) &&
+ atomic_read(&base_vha->loop_state) != LOOP_DOWN) {
+
+ ql_dbg(ql_dbg_dpc, base_vha, 0x400d,
+ "Relogin scheduled.\n");
+ qla2x00_relogin(base_vha);
+ ql_dbg(ql_dbg_dpc, base_vha, 0x400e,
+ "Relogin end.\n");
+ }
+
+ if (test_and_clear_bit(LOOP_RESYNC_NEEDED,
+ &base_vha->dpc_flags)) {
+
+ ql_dbg(ql_dbg_dpc, base_vha, 0x400f,
+ "Loop resync scheduled.\n");
+
+ if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE,
+ &base_vha->dpc_flags))) {
+
+ rval = qla2x00_loop_resync(base_vha);
+
+ clear_bit(LOOP_RESYNC_ACTIVE,
+ &base_vha->dpc_flags);
+ }
+
+ ql_dbg(ql_dbg_dpc, base_vha, 0x4010,
+ "Loop resync end.\n");
+ }
+
+ if (test_bit(NPIV_CONFIG_NEEDED, &base_vha->dpc_flags) &&
+ atomic_read(&base_vha->loop_state) == LOOP_READY) {
+ clear_bit(NPIV_CONFIG_NEEDED, &base_vha->dpc_flags);
+ qla2xxx_flash_npiv_conf(base_vha);
+ }
+
+ if (!ha->interrupts_on)
+ ha->isp_ops->enable_intrs(ha);
+
+ if (test_and_clear_bit(BEACON_BLINK_NEEDED,
+ &base_vha->dpc_flags))
+ ha->isp_ops->beacon_blink(base_vha);
+
+ qla2x00_do_dpc_all_vps(base_vha);
+
+ ha->dpc_active = 0;
+end_loop:
+ set_current_state(TASK_INTERRUPTIBLE);
+ } /* End of while(1) */
+ __set_current_state(TASK_RUNNING);
+
+ ql_dbg(ql_dbg_dpc, base_vha, 0x4011,
+ "DPC handler exiting.\n");
+
+ /*
+ * Make sure that nobody tries to wake us up again.
+ */
+ ha->dpc_active = 0;
+
+ /* Cleanup any residual CTX SRBs. */
+ qla2x00_abort_all_cmds(base_vha, DID_NO_CONNECT << 16);
+
+ return 0;
+}
+
+void
+qla2xxx_wake_dpc(struct scsi_qla_host *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ struct task_struct *t = ha->dpc_thread;
+
+ if (!test_bit(UNLOADING, &vha->dpc_flags) && t)
+ wake_up_process(t);
+}
+
+/*
+* qla2x00_rst_aen
+* Processes asynchronous reset.
+*
+* Input:
+* ha = adapter block pointer.
+*/
+static void
+qla2x00_rst_aen(scsi_qla_host_t *vha)
+{
+ if (vha->flags.online && !vha->flags.reset_active &&
+ !atomic_read(&vha->loop_down_timer) &&
+ !(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags))) {
+ do {
+ clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags);
+
+ /*
+ * Issue marker command only when we are going to start
+ * the I/O.
+ */
+ vha->marker_needed = 1;
+ } while (!atomic_read(&vha->loop_down_timer) &&
+ (test_bit(RESET_MARKER_NEEDED, &vha->dpc_flags)));
+ }
+}
+
+/**************************************************************************
+* qla2x00_timer
+*
+* Description:
+* One second timer
+*
+* Context: Interrupt
+***************************************************************************/
+void
+qla2x00_timer(scsi_qla_host_t *vha)
+{
+ unsigned long cpu_flags = 0;
+ int start_dpc = 0;
+ int index;
+ srb_t *sp;
+ uint16_t w;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req;
+
+ if (ha->flags.eeh_busy) {
+ ql_dbg(ql_dbg_timer, vha, 0x6000,
+ "EEH = %d, restarting timer.\n",
+ ha->flags.eeh_busy);
+ qla2x00_restart_timer(vha, WATCH_INTERVAL);
+ return;
+ }
+
+ /* Hardware read to raise pending EEH errors during mailbox waits. */
+ if (!pci_channel_offline(ha->pdev))
+ pci_read_config_word(ha->pdev, PCI_VENDOR_ID, &w);
+
+ /* Make sure qla82xx_watchdog is run only for physical port */
+ if (!vha->vp_idx && IS_QLA82XX(ha)) {
+ if (test_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags))
+ start_dpc++;
+ qla82xx_watchdog(vha);
+ }
+
+ /* Loop down handler. */
+ if (atomic_read(&vha->loop_down_timer) > 0 &&
+ !(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) &&
+ !(test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags))
+ && vha->flags.online) {
+
+ if (atomic_read(&vha->loop_down_timer) ==
+ vha->loop_down_abort_time) {
+
+ ql_log(ql_log_info, vha, 0x6008,
+ "Loop down - aborting the queues before time expires.\n");
+
+ if (!IS_QLA2100(ha) && vha->link_down_timeout)
+ atomic_set(&vha->loop_state, LOOP_DEAD);
+
+ /*
+ * Schedule an ISP abort to return any FCP2-device
+ * commands.
+ */
+ /* NPIV - scan physical port only */
+ if (!vha->vp_idx) {
+ spin_lock_irqsave(&ha->hardware_lock,
+ cpu_flags);
+ req = ha->req_q_map[0];
+ for (index = 1;
+ index < MAX_OUTSTANDING_COMMANDS;
+ index++) {
+ fc_port_t *sfcp;
+
+ sp = req->outstanding_cmds[index];
+ if (!sp)
+ continue;
+ if (sp->type != SRB_SCSI_CMD)
+ continue;
+ sfcp = sp->fcport;
+ if (!(sfcp->flags & FCF_FCP2_DEVICE))
+ continue;
+
+ if (IS_QLA82XX(ha))
+ set_bit(FCOE_CTX_RESET_NEEDED,
+ &vha->dpc_flags);
+ else
+ set_bit(ISP_ABORT_NEEDED,
+ &vha->dpc_flags);
+ break;
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock,
+ cpu_flags);
+ }
+ start_dpc++;
+ }
+
+ /* if the loop has been down for 4 minutes, reinit adapter */
+ if (atomic_dec_and_test(&vha->loop_down_timer) != 0) {
+ if (!(vha->device_flags & DFLG_NO_CABLE)) {
+ ql_log(ql_log_warn, vha, 0x6009,
+ "Loop down - aborting ISP.\n");
+
+ if (IS_QLA82XX(ha))
+ set_bit(FCOE_CTX_RESET_NEEDED,
+ &vha->dpc_flags);
+ else
+ set_bit(ISP_ABORT_NEEDED,
+ &vha->dpc_flags);
+ }
+ }
+ ql_dbg(ql_dbg_timer, vha, 0x600a,
+ "Loop down - seconds remaining %d.\n",
+ atomic_read(&vha->loop_down_timer));
+ }
+
+ /* Check if beacon LED needs to be blinked for physical host only */
+ if (!vha->vp_idx && (ha->beacon_blink_led == 1)) {
+ /* There is no beacon_blink function for ISP82xx */
+ if (!IS_QLA82XX(ha)) {
+ set_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags);
+ start_dpc++;
+ }
+ }
+
+ /* Process any deferred work. */
+ if (!list_empty(&vha->work_list))
+ start_dpc++;
+
+ /* Schedule the DPC routine if needed */
+ if ((test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) ||
+ test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags) ||
+ test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags) ||
+ start_dpc ||
+ test_bit(RESET_MARKER_NEEDED, &vha->dpc_flags) ||
+ test_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags) ||
+ test_bit(ISP_UNRECOVERABLE, &vha->dpc_flags) ||
+ test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags) ||
+ test_bit(VP_DPC_NEEDED, &vha->dpc_flags) ||
+ test_bit(RELOGIN_NEEDED, &vha->dpc_flags))) {
+ ql_dbg(ql_dbg_timer, vha, 0x600b,
+ "isp_abort_needed=%d loop_resync_needed=%d "
+ "fcport_update_needed=%d start_dpc=%d "
+ "reset_marker_needed=%d",
+ test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags),
+ test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags),
+ test_bit(FCPORT_UPDATE_NEEDED, &vha->dpc_flags),
+ start_dpc,
+ test_bit(RESET_MARKER_NEEDED, &vha->dpc_flags));
+ ql_dbg(ql_dbg_timer, vha, 0x600c,
+ "beacon_blink_needed=%d isp_unrecoverable=%d "
+ "fcoe_ctx_reset_needed=%d vp_dpc_needed=%d "
+ "relogin_needed=%d.\n",
+ test_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags),
+ test_bit(ISP_UNRECOVERABLE, &vha->dpc_flags),
+ test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags),
+ test_bit(VP_DPC_NEEDED, &vha->dpc_flags),
+ test_bit(RELOGIN_NEEDED, &vha->dpc_flags));
+ qla2xxx_wake_dpc(vha);
+ }
+
+ qla2x00_restart_timer(vha, WATCH_INTERVAL);
+}
+
+/* Firmware interface routines. */
+
+#define FW_BLOBS 10
+#define FW_ISP21XX 0
+#define FW_ISP22XX 1
+#define FW_ISP2300 2
+#define FW_ISP2322 3
+#define FW_ISP24XX 4
+#define FW_ISP25XX 5
+#define FW_ISP81XX 6
+#define FW_ISP82XX 7
+#define FW_ISP2031 8
+#define FW_ISP8031 9
+
+#define FW_FILE_ISP21XX "ql2100_fw.bin"
+#define FW_FILE_ISP22XX "ql2200_fw.bin"
+#define FW_FILE_ISP2300 "ql2300_fw.bin"
+#define FW_FILE_ISP2322 "ql2322_fw.bin"
+#define FW_FILE_ISP24XX "ql2400_fw.bin"
+#define FW_FILE_ISP25XX "ql2500_fw.bin"
+#define FW_FILE_ISP81XX "ql8100_fw.bin"
+#define FW_FILE_ISP82XX "ql8200_fw.bin"
+#define FW_FILE_ISP2031 "ql2600_fw.bin"
+#define FW_FILE_ISP8031 "ql8300_fw.bin"
+
+static DEFINE_MUTEX(qla_fw_lock);
+
+static struct fw_blob qla_fw_blobs[FW_BLOBS] = {
+ { .name = FW_FILE_ISP21XX, .segs = { 0x1000, 0 }, },
+ { .name = FW_FILE_ISP22XX, .segs = { 0x1000, 0 }, },
+ { .name = FW_FILE_ISP2300, .segs = { 0x800, 0 }, },
+ { .name = FW_FILE_ISP2322, .segs = { 0x800, 0x1c000, 0x1e000, 0 }, },
+ { .name = FW_FILE_ISP24XX, },
+ { .name = FW_FILE_ISP25XX, },
+ { .name = FW_FILE_ISP81XX, },
+ { .name = FW_FILE_ISP82XX, },
+ { .name = FW_FILE_ISP2031, },
+ { .name = FW_FILE_ISP8031, },
+};
+
+struct fw_blob *
+qla2x00_request_firmware(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ struct fw_blob *blob;
+
+ if (IS_QLA2100(ha)) {
+ blob = &qla_fw_blobs[FW_ISP21XX];
+ } else if (IS_QLA2200(ha)) {
+ blob = &qla_fw_blobs[FW_ISP22XX];
+ } else if (IS_QLA2300(ha) || IS_QLA2312(ha) || IS_QLA6312(ha)) {
+ blob = &qla_fw_blobs[FW_ISP2300];
+ } else if (IS_QLA2322(ha) || IS_QLA6322(ha)) {
+ blob = &qla_fw_blobs[FW_ISP2322];
+ } else if (IS_QLA24XX_TYPE(ha)) {
+ blob = &qla_fw_blobs[FW_ISP24XX];
+ } else if (IS_QLA25XX(ha)) {
+ blob = &qla_fw_blobs[FW_ISP25XX];
+ } else if (IS_QLA81XX(ha)) {
+ blob = &qla_fw_blobs[FW_ISP81XX];
+ } else if (IS_QLA82XX(ha)) {
+ blob = &qla_fw_blobs[FW_ISP82XX];
+ } else if (IS_QLA2031(ha)) {
+ blob = &qla_fw_blobs[FW_ISP2031];
+ } else if (IS_QLA8031(ha)) {
+ blob = &qla_fw_blobs[FW_ISP8031];
+ } else {
+ return NULL;
+ }
+
+ mutex_lock(&qla_fw_lock);
+ if (blob->fw)
+ goto out;
+
+ if (request_firmware(&blob->fw, blob->name, &ha->pdev->dev)) {
+ ql_log(ql_log_warn, vha, 0x0063,
+ "Failed to load firmware image (%s).\n", blob->name);
+ blob->fw = NULL;
+ blob = NULL;
+ goto out;
+ }
+
+out:
+ mutex_unlock(&qla_fw_lock);
+ return blob;
+}
+
+static void
+qla2x00_release_firmware(void)
+{
+ int idx;
+
+ mutex_lock(&qla_fw_lock);
+ for (idx = 0; idx < FW_BLOBS; idx++)
+ if (qla_fw_blobs[idx].fw)
+ release_firmware(qla_fw_blobs[idx].fw);
+ mutex_unlock(&qla_fw_lock);
+}
+
+static pci_ers_result_t
+qla2xxx_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state)
+{
+ scsi_qla_host_t *vha = pci_get_drvdata(pdev);
+ struct qla_hw_data *ha = vha->hw;
+
+ ql_dbg(ql_dbg_aer, vha, 0x9000,
+ "PCI error detected, state %x.\n", state);
+
+ switch (state) {
+ case pci_channel_io_normal:
+ ha->flags.eeh_busy = 0;
+ return PCI_ERS_RESULT_CAN_RECOVER;
+ case pci_channel_io_frozen:
+ ha->flags.eeh_busy = 1;
+ /* For ISP82XX complete any pending mailbox cmd */
+ if (IS_QLA82XX(ha)) {
+ ha->flags.isp82xx_fw_hung = 1;
+ ql_dbg(ql_dbg_aer, vha, 0x9001, "Pci channel io frozen\n");
+ qla82xx_clear_pending_mbx(vha);
+ }
+ qla2x00_free_irqs(vha);
+ pci_disable_device(pdev);
+ /* Return back all IOs */
+ qla2x00_abort_all_cmds(vha, DID_RESET << 16);
+ return PCI_ERS_RESULT_NEED_RESET;
+ case pci_channel_io_perm_failure:
+ ha->flags.pci_channel_io_perm_failure = 1;
+ qla2x00_abort_all_cmds(vha, DID_NO_CONNECT << 16);
+ return PCI_ERS_RESULT_DISCONNECT;
+ }
+ return PCI_ERS_RESULT_NEED_RESET;
+}
+
+static pci_ers_result_t
+qla2xxx_pci_mmio_enabled(struct pci_dev *pdev)
+{
+ int risc_paused = 0;
+ uint32_t stat;
+ unsigned long flags;
+ scsi_qla_host_t *base_vha = pci_get_drvdata(pdev);
+ struct qla_hw_data *ha = base_vha->hw;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24;
+
+ if (IS_QLA82XX(ha))
+ return PCI_ERS_RESULT_RECOVERED;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ if (IS_QLA2100(ha) || IS_QLA2200(ha)){
+ stat = RD_REG_DWORD(&reg->hccr);
+ if (stat & HCCR_RISC_PAUSE)
+ risc_paused = 1;
+ } else if (IS_QLA23XX(ha)) {
+ stat = RD_REG_DWORD(&reg->u.isp2300.host_status);
+ if (stat & HSR_RISC_PAUSED)
+ risc_paused = 1;
+ } else if (IS_FWI2_CAPABLE(ha)) {
+ stat = RD_REG_DWORD(&reg24->host_status);
+ if (stat & HSRX_RISC_PAUSED)
+ risc_paused = 1;
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ if (risc_paused) {
+ ql_log(ql_log_info, base_vha, 0x9003,
+ "RISC paused -- mmio_enabled, Dumping firmware.\n");
+ ha->isp_ops->fw_dump(base_vha, 0);
+
+ return PCI_ERS_RESULT_NEED_RESET;
+ } else
+ return PCI_ERS_RESULT_RECOVERED;
+}
+
+uint32_t qla82xx_error_recovery(scsi_qla_host_t *base_vha)
+{
+ uint32_t rval = QLA_FUNCTION_FAILED;
+ uint32_t drv_active = 0;
+ struct qla_hw_data *ha = base_vha->hw;
+ int fn;
+ struct pci_dev *other_pdev = NULL;
+
+ ql_dbg(ql_dbg_aer, base_vha, 0x9006,
+ "Entered %s.\n", __func__);
+
+ set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+
+ if (base_vha->flags.online) {
+ /* Abort all outstanding commands,
+ * so as to be requeued later */
+ qla2x00_abort_isp_cleanup(base_vha);
+ }
+
+
+ fn = PCI_FUNC(ha->pdev->devfn);
+ while (fn > 0) {
+ fn--;
+ ql_dbg(ql_dbg_aer, base_vha, 0x9007,
+ "Finding pci device at function = 0x%x.\n", fn);
+ other_pdev =
+ pci_get_domain_bus_and_slot(pci_domain_nr(ha->pdev->bus),
+ ha->pdev->bus->number, PCI_DEVFN(PCI_SLOT(ha->pdev->devfn),
+ fn));
+
+ if (!other_pdev)
+ continue;
+ if (atomic_read(&other_pdev->enable_cnt)) {
+ ql_dbg(ql_dbg_aer, base_vha, 0x9008,
+ "Found PCI func available and enable at 0x%x.\n",
+ fn);
+ pci_dev_put(other_pdev);
+ break;
+ }
+ pci_dev_put(other_pdev);
+ }
+
+ if (!fn) {
+ /* Reset owner */
+ ql_dbg(ql_dbg_aer, base_vha, 0x9009,
+ "This devfn is reset owner = 0x%x.\n",
+ ha->pdev->devfn);
+ qla82xx_idc_lock(ha);
+
+ qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
+ QLA82XX_DEV_INITIALIZING);
+
+ qla82xx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION,
+ QLA82XX_IDC_VERSION);
+
+ drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE);
+ ql_dbg(ql_dbg_aer, base_vha, 0x900a,
+ "drv_active = 0x%x.\n", drv_active);
+
+ qla82xx_idc_unlock(ha);
+ /* Reset if device is not already reset
+ * drv_active would be 0 if a reset has already been done
+ */
+ if (drv_active)
+ rval = qla82xx_start_firmware(base_vha);
+ else
+ rval = QLA_SUCCESS;
+ qla82xx_idc_lock(ha);
+
+ if (rval != QLA_SUCCESS) {
+ ql_log(ql_log_info, base_vha, 0x900b,
+ "HW State: FAILED.\n");
+ qla82xx_clear_drv_active(ha);
+ qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
+ QLA82XX_DEV_FAILED);
+ } else {
+ ql_log(ql_log_info, base_vha, 0x900c,
+ "HW State: READY.\n");
+ qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE,
+ QLA82XX_DEV_READY);
+ qla82xx_idc_unlock(ha);
+ ha->flags.isp82xx_fw_hung = 0;
+ rval = qla82xx_restart_isp(base_vha);
+ qla82xx_idc_lock(ha);
+ /* Clear driver state register */
+ qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, 0);
+ qla82xx_set_drv_active(base_vha);
+ }
+ qla82xx_idc_unlock(ha);
+ } else {
+ ql_dbg(ql_dbg_aer, base_vha, 0x900d,
+ "This devfn is not reset owner = 0x%x.\n",
+ ha->pdev->devfn);
+ if ((qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE) ==
+ QLA82XX_DEV_READY)) {
+ ha->flags.isp82xx_fw_hung = 0;
+ rval = qla82xx_restart_isp(base_vha);
+ qla82xx_idc_lock(ha);
+ qla82xx_set_drv_active(base_vha);
+ qla82xx_idc_unlock(ha);
+ }
+ }
+ clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+
+ return rval;
+}
+
+static pci_ers_result_t
+qla2xxx_pci_slot_reset(struct pci_dev *pdev)
+{
+ pci_ers_result_t ret = PCI_ERS_RESULT_DISCONNECT;
+ scsi_qla_host_t *base_vha = pci_get_drvdata(pdev);
+ struct qla_hw_data *ha = base_vha->hw;
+ struct rsp_que *rsp;
+ int rc, retries = 10;
+
+ ql_dbg(ql_dbg_aer, base_vha, 0x9004,
+ "Slot Reset.\n");
+
+ /* Workaround: qla2xxx driver which access hardware earlier
+ * needs error state to be pci_channel_io_online.
+ * Otherwise mailbox command timesout.
+ */
+ pdev->error_state = pci_channel_io_normal;
+
+ pci_restore_state(pdev);
+
+ /* pci_restore_state() clears the saved_state flag of the device
+ * save restored state which resets saved_state flag
+ */
+ pci_save_state(pdev);
+
+ if (ha->mem_only)
+ rc = pci_enable_device_mem(pdev);
+ else
+ rc = pci_enable_device(pdev);
+
+ if (rc) {
+ ql_log(ql_log_warn, base_vha, 0x9005,
+ "Can't re-enable PCI device after reset.\n");
+ goto exit_slot_reset;
+ }
+
+ rsp = ha->rsp_q_map[0];
+ if (qla2x00_request_irqs(ha, rsp))
+ goto exit_slot_reset;
+
+ if (ha->isp_ops->pci_config(base_vha))
+ goto exit_slot_reset;
+
+ if (IS_QLA82XX(ha)) {
+ if (qla82xx_error_recovery(base_vha) == QLA_SUCCESS) {
+ ret = PCI_ERS_RESULT_RECOVERED;
+ goto exit_slot_reset;
+ } else
+ goto exit_slot_reset;
+ }
+
+ while (ha->flags.mbox_busy && retries--)
+ msleep(1000);
+
+ set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+ if (ha->isp_ops->abort_isp(base_vha) == QLA_SUCCESS)
+ ret = PCI_ERS_RESULT_RECOVERED;
+ clear_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags);
+
+
+exit_slot_reset:
+ ql_dbg(ql_dbg_aer, base_vha, 0x900e,
+ "slot_reset return %x.\n", ret);
+
+ return ret;
+}
+
+static void
+qla2xxx_pci_resume(struct pci_dev *pdev)
+{
+ scsi_qla_host_t *base_vha = pci_get_drvdata(pdev);
+ struct qla_hw_data *ha = base_vha->hw;
+ int ret;
+
+ ql_dbg(ql_dbg_aer, base_vha, 0x900f,
+ "pci_resume.\n");
+
+ ret = qla2x00_wait_for_hba_online(base_vha);
+ if (ret != QLA_SUCCESS) {
+ ql_log(ql_log_fatal, base_vha, 0x9002,
+ "The device failed to resume I/O from slot/link_reset.\n");
+ }
+
+ pci_cleanup_aer_uncorrect_error_status(pdev);
+
+ ha->flags.eeh_busy = 0;
+}
+
+static struct pci_error_handlers qla2xxx_err_handler = {
+ .error_detected = qla2xxx_pci_error_detected,
+ .mmio_enabled = qla2xxx_pci_mmio_enabled,
+ .slot_reset = qla2xxx_pci_slot_reset,
+ .resume = qla2xxx_pci_resume,
+};
+
+static struct pci_device_id qla2xxx_pci_tbl[] = {
+ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2100) },
+ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2200) },
+ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2300) },
+ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2312) },
+ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2322) },
+ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP6312) },
+ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP6322) },
+ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2422) },
+ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2432) },
+ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8432) },
+ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP5422) },
+ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP5432) },
+ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2532) },
+ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2031) },
+ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8001) },
+ { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8021) },
+ { 0 },
+};
+MODULE_DEVICE_TABLE(pci, qla2xxx_pci_tbl);
+
+static struct pci_driver qla2xxx_pci_driver = {
+ .name = QLA2XXX_DRIVER_NAME,
+ .driver = {
+ .owner = THIS_MODULE,
+ },
+ .id_table = qla2xxx_pci_tbl,
+ .probe = qla2x00_probe_one,
+ .remove = qla2x00_remove_one,
+ .shutdown = qla2x00_shutdown,
+ .err_handler = &qla2xxx_err_handler,
+};
+
+static struct file_operations apidev_fops = {
+ .owner = THIS_MODULE,
+ .llseek = noop_llseek,
+};
+
+/**
+ * qla2x00_module_init - Module initialization.
+ **/
+static int __init
+qla2x00_module_init(void)
+{
+ int ret = 0;
+
+ /* Allocate cache for SRBs. */
+ srb_cachep = kmem_cache_create("qla2xxx_srbs", sizeof(srb_t), 0,
+ SLAB_HWCACHE_ALIGN, NULL);
+ if (srb_cachep == NULL) {
+ ql_log(ql_log_fatal, NULL, 0x0001,
+ "Unable to allocate SRB cache...Failing load!.\n");
+ return -ENOMEM;
+ }
+
+ /* Derive version string. */
+ strcpy(qla2x00_version_str, QLA2XXX_VERSION);
+ if (ql2xextended_error_logging)
+ strcat(qla2x00_version_str, "-debug");
+
+ qla2xxx_transport_template =
+ fc_attach_transport(&qla2xxx_transport_functions);
+ if (!qla2xxx_transport_template) {
+ kmem_cache_destroy(srb_cachep);
+ ql_log(ql_log_fatal, NULL, 0x0002,
+ "fc_attach_transport failed...Failing load!.\n");
+ return -ENODEV;
+ }
+
+ apidev_major = register_chrdev(0, QLA2XXX_APIDEV, &apidev_fops);
+ if (apidev_major < 0) {
+ ql_log(ql_log_fatal, NULL, 0x0003,
+ "Unable to register char device %s.\n", QLA2XXX_APIDEV);
+ }
+
+ qla2xxx_transport_vport_template =
+ fc_attach_transport(&qla2xxx_transport_vport_functions);
+ if (!qla2xxx_transport_vport_template) {
+ kmem_cache_destroy(srb_cachep);
+ fc_release_transport(qla2xxx_transport_template);
+ ql_log(ql_log_fatal, NULL, 0x0004,
+ "fc_attach_transport vport failed...Failing load!.\n");
+ return -ENODEV;
+ }
+ ql_log(ql_log_info, NULL, 0x0005,
+ "QLogic Fibre Channel HBA Driver: %s.\n",
+ qla2x00_version_str);
+ ret = pci_register_driver(&qla2xxx_pci_driver);
+ if (ret) {
+ kmem_cache_destroy(srb_cachep);
+ fc_release_transport(qla2xxx_transport_template);
+ fc_release_transport(qla2xxx_transport_vport_template);
+ ql_log(ql_log_fatal, NULL, 0x0006,
+ "pci_register_driver failed...ret=%d Failing load!.\n",
+ ret);
+ }
+ return ret;
+}
+
+/**
+ * qla2x00_module_exit - Module cleanup.
+ **/
+static void __exit
+qla2x00_module_exit(void)
+{
+ unregister_chrdev(apidev_major, QLA2XXX_APIDEV);
+ pci_unregister_driver(&qla2xxx_pci_driver);
+ qla2x00_release_firmware();
+ kmem_cache_destroy(srb_cachep);
+ if (ctx_cachep)
+ kmem_cache_destroy(ctx_cachep);
+ fc_release_transport(qla2xxx_transport_template);
+ fc_release_transport(qla2xxx_transport_vport_template);
+}
+
+module_init(qla2x00_module_init);
+module_exit(qla2x00_module_exit);
+
+MODULE_AUTHOR("QLogic Corporation");
+MODULE_DESCRIPTION("QLogic Fibre Channel HBA Driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION(QLA2XXX_VERSION);
+MODULE_FIRMWARE(FW_FILE_ISP21XX);
+MODULE_FIRMWARE(FW_FILE_ISP22XX);
+MODULE_FIRMWARE(FW_FILE_ISP2300);
+MODULE_FIRMWARE(FW_FILE_ISP2322);
+MODULE_FIRMWARE(FW_FILE_ISP24XX);
+MODULE_FIRMWARE(FW_FILE_ISP25XX);
diff --git a/drivers/scsi/qla2xxx/qla_settings.h b/drivers/scsi/qla2xxx/qla_settings.h
new file mode 100644
index 00000000..d70f0300
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_settings.h
@@ -0,0 +1,12 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2003-2011 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#define MAX_RETRIES_OF_ISP_ABORT 5
+
+/* Max time to wait for the loop to be in LOOP_READY state */
+#define MAX_LOOP_TIMEOUT (60 * 5)
+
+#include "qla_version.h"
diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c
new file mode 100644
index 00000000..a683e766
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_sup.c
@@ -0,0 +1,3044 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2003-2011 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+#include "qla_def.h"
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <asm/uaccess.h>
+
+/*
+ * NVRAM support routines
+ */
+
+/**
+ * qla2x00_lock_nvram_access() -
+ * @ha: HA context
+ */
+static void
+qla2x00_lock_nvram_access(struct qla_hw_data *ha)
+{
+ uint16_t data;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ if (!IS_QLA2100(ha) && !IS_QLA2200(ha) && !IS_QLA2300(ha)) {
+ data = RD_REG_WORD(&reg->nvram);
+ while (data & NVR_BUSY) {
+ udelay(100);
+ data = RD_REG_WORD(&reg->nvram);
+ }
+
+ /* Lock resource */
+ WRT_REG_WORD(&reg->u.isp2300.host_semaphore, 0x1);
+ RD_REG_WORD(&reg->u.isp2300.host_semaphore);
+ udelay(5);
+ data = RD_REG_WORD(&reg->u.isp2300.host_semaphore);
+ while ((data & BIT_0) == 0) {
+ /* Lock failed */
+ udelay(100);
+ WRT_REG_WORD(&reg->u.isp2300.host_semaphore, 0x1);
+ RD_REG_WORD(&reg->u.isp2300.host_semaphore);
+ udelay(5);
+ data = RD_REG_WORD(&reg->u.isp2300.host_semaphore);
+ }
+ }
+}
+
+/**
+ * qla2x00_unlock_nvram_access() -
+ * @ha: HA context
+ */
+static void
+qla2x00_unlock_nvram_access(struct qla_hw_data *ha)
+{
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ if (!IS_QLA2100(ha) && !IS_QLA2200(ha) && !IS_QLA2300(ha)) {
+ WRT_REG_WORD(&reg->u.isp2300.host_semaphore, 0);
+ RD_REG_WORD(&reg->u.isp2300.host_semaphore);
+ }
+}
+
+/**
+ * qla2x00_nv_write() - Prepare for NVRAM read/write operation.
+ * @ha: HA context
+ * @data: Serial interface selector
+ */
+static void
+qla2x00_nv_write(struct qla_hw_data *ha, uint16_t data)
+{
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ WRT_REG_WORD(&reg->nvram, data | NVR_SELECT | NVR_WRT_ENABLE);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+ WRT_REG_WORD(&reg->nvram, data | NVR_SELECT | NVR_CLOCK |
+ NVR_WRT_ENABLE);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+ WRT_REG_WORD(&reg->nvram, data | NVR_SELECT | NVR_WRT_ENABLE);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+}
+
+/**
+ * qla2x00_nvram_request() - Sends read command to NVRAM and gets data from
+ * NVRAM.
+ * @ha: HA context
+ * @nv_cmd: NVRAM command
+ *
+ * Bit definitions for NVRAM command:
+ *
+ * Bit 26 = start bit
+ * Bit 25, 24 = opcode
+ * Bit 23-16 = address
+ * Bit 15-0 = write data
+ *
+ * Returns the word read from nvram @addr.
+ */
+static uint16_t
+qla2x00_nvram_request(struct qla_hw_data *ha, uint32_t nv_cmd)
+{
+ uint8_t cnt;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ uint16_t data = 0;
+ uint16_t reg_data;
+
+ /* Send command to NVRAM. */
+ nv_cmd <<= 5;
+ for (cnt = 0; cnt < 11; cnt++) {
+ if (nv_cmd & BIT_31)
+ qla2x00_nv_write(ha, NVR_DATA_OUT);
+ else
+ qla2x00_nv_write(ha, 0);
+ nv_cmd <<= 1;
+ }
+
+ /* Read data from NVRAM. */
+ for (cnt = 0; cnt < 16; cnt++) {
+ WRT_REG_WORD(&reg->nvram, NVR_SELECT | NVR_CLOCK);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+ data <<= 1;
+ reg_data = RD_REG_WORD(&reg->nvram);
+ if (reg_data & NVR_DATA_IN)
+ data |= BIT_0;
+ WRT_REG_WORD(&reg->nvram, NVR_SELECT);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+ }
+
+ /* Deselect chip. */
+ WRT_REG_WORD(&reg->nvram, NVR_DESELECT);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+
+ return data;
+}
+
+
+/**
+ * qla2x00_get_nvram_word() - Calculates word position in NVRAM and calls the
+ * request routine to get the word from NVRAM.
+ * @ha: HA context
+ * @addr: Address in NVRAM to read
+ *
+ * Returns the word read from nvram @addr.
+ */
+static uint16_t
+qla2x00_get_nvram_word(struct qla_hw_data *ha, uint32_t addr)
+{
+ uint16_t data;
+ uint32_t nv_cmd;
+
+ nv_cmd = addr << 16;
+ nv_cmd |= NV_READ_OP;
+ data = qla2x00_nvram_request(ha, nv_cmd);
+
+ return (data);
+}
+
+/**
+ * qla2x00_nv_deselect() - Deselect NVRAM operations.
+ * @ha: HA context
+ */
+static void
+qla2x00_nv_deselect(struct qla_hw_data *ha)
+{
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ WRT_REG_WORD(&reg->nvram, NVR_DESELECT);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ NVRAM_DELAY();
+}
+
+/**
+ * qla2x00_write_nvram_word() - Write NVRAM data.
+ * @ha: HA context
+ * @addr: Address in NVRAM to write
+ * @data: word to program
+ */
+static void
+qla2x00_write_nvram_word(struct qla_hw_data *ha, uint32_t addr, uint16_t data)
+{
+ int count;
+ uint16_t word;
+ uint32_t nv_cmd, wait_cnt;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ qla2x00_nv_write(ha, NVR_DATA_OUT);
+ qla2x00_nv_write(ha, 0);
+ qla2x00_nv_write(ha, 0);
+
+ for (word = 0; word < 8; word++)
+ qla2x00_nv_write(ha, NVR_DATA_OUT);
+
+ qla2x00_nv_deselect(ha);
+
+ /* Write data */
+ nv_cmd = (addr << 16) | NV_WRITE_OP;
+ nv_cmd |= data;
+ nv_cmd <<= 5;
+ for (count = 0; count < 27; count++) {
+ if (nv_cmd & BIT_31)
+ qla2x00_nv_write(ha, NVR_DATA_OUT);
+ else
+ qla2x00_nv_write(ha, 0);
+
+ nv_cmd <<= 1;
+ }
+
+ qla2x00_nv_deselect(ha);
+
+ /* Wait for NVRAM to become ready */
+ WRT_REG_WORD(&reg->nvram, NVR_SELECT);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ wait_cnt = NVR_WAIT_CNT;
+ do {
+ if (!--wait_cnt) {
+ ql_dbg(ql_dbg_user, vha, 0x708d,
+ "NVRAM didn't go ready...\n");
+ break;
+ }
+ NVRAM_DELAY();
+ word = RD_REG_WORD(&reg->nvram);
+ } while ((word & NVR_DATA_IN) == 0);
+
+ qla2x00_nv_deselect(ha);
+
+ /* Disable writes */
+ qla2x00_nv_write(ha, NVR_DATA_OUT);
+ for (count = 0; count < 10; count++)
+ qla2x00_nv_write(ha, 0);
+
+ qla2x00_nv_deselect(ha);
+}
+
+static int
+qla2x00_write_nvram_word_tmo(struct qla_hw_data *ha, uint32_t addr,
+ uint16_t data, uint32_t tmo)
+{
+ int ret, count;
+ uint16_t word;
+ uint32_t nv_cmd;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ ret = QLA_SUCCESS;
+
+ qla2x00_nv_write(ha, NVR_DATA_OUT);
+ qla2x00_nv_write(ha, 0);
+ qla2x00_nv_write(ha, 0);
+
+ for (word = 0; word < 8; word++)
+ qla2x00_nv_write(ha, NVR_DATA_OUT);
+
+ qla2x00_nv_deselect(ha);
+
+ /* Write data */
+ nv_cmd = (addr << 16) | NV_WRITE_OP;
+ nv_cmd |= data;
+ nv_cmd <<= 5;
+ for (count = 0; count < 27; count++) {
+ if (nv_cmd & BIT_31)
+ qla2x00_nv_write(ha, NVR_DATA_OUT);
+ else
+ qla2x00_nv_write(ha, 0);
+
+ nv_cmd <<= 1;
+ }
+
+ qla2x00_nv_deselect(ha);
+
+ /* Wait for NVRAM to become ready */
+ WRT_REG_WORD(&reg->nvram, NVR_SELECT);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ do {
+ NVRAM_DELAY();
+ word = RD_REG_WORD(&reg->nvram);
+ if (!--tmo) {
+ ret = QLA_FUNCTION_FAILED;
+ break;
+ }
+ } while ((word & NVR_DATA_IN) == 0);
+
+ qla2x00_nv_deselect(ha);
+
+ /* Disable writes */
+ qla2x00_nv_write(ha, NVR_DATA_OUT);
+ for (count = 0; count < 10; count++)
+ qla2x00_nv_write(ha, 0);
+
+ qla2x00_nv_deselect(ha);
+
+ return ret;
+}
+
+/**
+ * qla2x00_clear_nvram_protection() -
+ * @ha: HA context
+ */
+static int
+qla2x00_clear_nvram_protection(struct qla_hw_data *ha)
+{
+ int ret, stat;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ uint32_t word, wait_cnt;
+ uint16_t wprot, wprot_old;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ /* Clear NVRAM write protection. */
+ ret = QLA_FUNCTION_FAILED;
+
+ wprot_old = cpu_to_le16(qla2x00_get_nvram_word(ha, ha->nvram_base));
+ stat = qla2x00_write_nvram_word_tmo(ha, ha->nvram_base,
+ __constant_cpu_to_le16(0x1234), 100000);
+ wprot = cpu_to_le16(qla2x00_get_nvram_word(ha, ha->nvram_base));
+ if (stat != QLA_SUCCESS || wprot != 0x1234) {
+ /* Write enable. */
+ qla2x00_nv_write(ha, NVR_DATA_OUT);
+ qla2x00_nv_write(ha, 0);
+ qla2x00_nv_write(ha, 0);
+ for (word = 0; word < 8; word++)
+ qla2x00_nv_write(ha, NVR_DATA_OUT);
+
+ qla2x00_nv_deselect(ha);
+
+ /* Enable protection register. */
+ qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
+ qla2x00_nv_write(ha, NVR_PR_ENABLE);
+ qla2x00_nv_write(ha, NVR_PR_ENABLE);
+ for (word = 0; word < 8; word++)
+ qla2x00_nv_write(ha, NVR_DATA_OUT | NVR_PR_ENABLE);
+
+ qla2x00_nv_deselect(ha);
+
+ /* Clear protection register (ffff is cleared). */
+ qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
+ qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
+ qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
+ for (word = 0; word < 8; word++)
+ qla2x00_nv_write(ha, NVR_DATA_OUT | NVR_PR_ENABLE);
+
+ qla2x00_nv_deselect(ha);
+
+ /* Wait for NVRAM to become ready. */
+ WRT_REG_WORD(&reg->nvram, NVR_SELECT);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ wait_cnt = NVR_WAIT_CNT;
+ do {
+ if (!--wait_cnt) {
+ ql_dbg(ql_dbg_user, vha, 0x708e,
+ "NVRAM didn't go ready...\n");
+ break;
+ }
+ NVRAM_DELAY();
+ word = RD_REG_WORD(&reg->nvram);
+ } while ((word & NVR_DATA_IN) == 0);
+
+ if (wait_cnt)
+ ret = QLA_SUCCESS;
+ } else
+ qla2x00_write_nvram_word(ha, ha->nvram_base, wprot_old);
+
+ return ret;
+}
+
+static void
+qla2x00_set_nvram_protection(struct qla_hw_data *ha, int stat)
+{
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ uint32_t word, wait_cnt;
+ scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev);
+
+ if (stat != QLA_SUCCESS)
+ return;
+
+ /* Set NVRAM write protection. */
+ /* Write enable. */
+ qla2x00_nv_write(ha, NVR_DATA_OUT);
+ qla2x00_nv_write(ha, 0);
+ qla2x00_nv_write(ha, 0);
+ for (word = 0; word < 8; word++)
+ qla2x00_nv_write(ha, NVR_DATA_OUT);
+
+ qla2x00_nv_deselect(ha);
+
+ /* Enable protection register. */
+ qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
+ qla2x00_nv_write(ha, NVR_PR_ENABLE);
+ qla2x00_nv_write(ha, NVR_PR_ENABLE);
+ for (word = 0; word < 8; word++)
+ qla2x00_nv_write(ha, NVR_DATA_OUT | NVR_PR_ENABLE);
+
+ qla2x00_nv_deselect(ha);
+
+ /* Enable protection register. */
+ qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
+ qla2x00_nv_write(ha, NVR_PR_ENABLE);
+ qla2x00_nv_write(ha, NVR_PR_ENABLE | NVR_DATA_OUT);
+ for (word = 0; word < 8; word++)
+ qla2x00_nv_write(ha, NVR_PR_ENABLE);
+
+ qla2x00_nv_deselect(ha);
+
+ /* Wait for NVRAM to become ready. */
+ WRT_REG_WORD(&reg->nvram, NVR_SELECT);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ wait_cnt = NVR_WAIT_CNT;
+ do {
+ if (!--wait_cnt) {
+ ql_dbg(ql_dbg_user, vha, 0x708f,
+ "NVRAM didn't go ready...\n");
+ break;
+ }
+ NVRAM_DELAY();
+ word = RD_REG_WORD(&reg->nvram);
+ } while ((word & NVR_DATA_IN) == 0);
+}
+
+
+/*****************************************************************************/
+/* Flash Manipulation Routines */
+/*****************************************************************************/
+
+static inline uint32_t
+flash_conf_addr(struct qla_hw_data *ha, uint32_t faddr)
+{
+ return ha->flash_conf_off | faddr;
+}
+
+static inline uint32_t
+flash_data_addr(struct qla_hw_data *ha, uint32_t faddr)
+{
+ return ha->flash_data_off | faddr;
+}
+
+static inline uint32_t
+nvram_conf_addr(struct qla_hw_data *ha, uint32_t naddr)
+{
+ return ha->nvram_conf_off | naddr;
+}
+
+static inline uint32_t
+nvram_data_addr(struct qla_hw_data *ha, uint32_t naddr)
+{
+ return ha->nvram_data_off | naddr;
+}
+
+static uint32_t
+qla24xx_read_flash_dword(struct qla_hw_data *ha, uint32_t addr)
+{
+ int rval;
+ uint32_t cnt, data;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+ WRT_REG_DWORD(&reg->flash_addr, addr & ~FARX_DATA_FLAG);
+ /* Wait for READ cycle to complete. */
+ rval = QLA_SUCCESS;
+ for (cnt = 3000;
+ (RD_REG_DWORD(&reg->flash_addr) & FARX_DATA_FLAG) == 0 &&
+ rval == QLA_SUCCESS; cnt--) {
+ if (cnt)
+ udelay(10);
+ else
+ rval = QLA_FUNCTION_TIMEOUT;
+ cond_resched();
+ }
+
+ /* TODO: What happens if we time out? */
+ data = 0xDEADDEAD;
+ if (rval == QLA_SUCCESS)
+ data = RD_REG_DWORD(&reg->flash_data);
+
+ return data;
+}
+
+uint32_t *
+qla24xx_read_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr,
+ uint32_t dwords)
+{
+ uint32_t i;
+ struct qla_hw_data *ha = vha->hw;
+
+ /* Dword reads to flash. */
+ for (i = 0; i < dwords; i++, faddr++)
+ dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(ha,
+ flash_data_addr(ha, faddr)));
+
+ return dwptr;
+}
+
+static int
+qla24xx_write_flash_dword(struct qla_hw_data *ha, uint32_t addr, uint32_t data)
+{
+ int rval;
+ uint32_t cnt;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+ WRT_REG_DWORD(&reg->flash_data, data);
+ RD_REG_DWORD(&reg->flash_data); /* PCI Posting. */
+ WRT_REG_DWORD(&reg->flash_addr, addr | FARX_DATA_FLAG);
+ /* Wait for Write cycle to complete. */
+ rval = QLA_SUCCESS;
+ for (cnt = 500000; (RD_REG_DWORD(&reg->flash_addr) & FARX_DATA_FLAG) &&
+ rval == QLA_SUCCESS; cnt--) {
+ if (cnt)
+ udelay(10);
+ else
+ rval = QLA_FUNCTION_TIMEOUT;
+ cond_resched();
+ }
+ return rval;
+}
+
+static void
+qla24xx_get_flash_manufacturer(struct qla_hw_data *ha, uint8_t *man_id,
+ uint8_t *flash_id)
+{
+ uint32_t ids;
+
+ ids = qla24xx_read_flash_dword(ha, flash_conf_addr(ha, 0x03ab));
+ *man_id = LSB(ids);
+ *flash_id = MSB(ids);
+
+ /* Check if man_id and flash_id are valid. */
+ if (ids != 0xDEADDEAD && (*man_id == 0 || *flash_id == 0)) {
+ /* Read information using 0x9f opcode
+ * Device ID, Mfg ID would be read in the format:
+ * <Ext Dev Info><Device ID Part2><Device ID Part 1><Mfg ID>
+ * Example: ATMEL 0x00 01 45 1F
+ * Extract MFG and Dev ID from last two bytes.
+ */
+ ids = qla24xx_read_flash_dword(ha, flash_conf_addr(ha, 0x009f));
+ *man_id = LSB(ids);
+ *flash_id = MSB(ids);
+ }
+}
+
+static int
+qla2xxx_find_flt_start(scsi_qla_host_t *vha, uint32_t *start)
+{
+ const char *loc, *locations[] = { "DEF", "PCI" };
+ uint32_t pcihdr, pcids;
+ uint32_t *dcode;
+ uint8_t *buf, *bcode, last_image;
+ uint16_t cnt, chksum, *wptr;
+ struct qla_flt_location *fltl;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+
+ /*
+ * FLT-location structure resides after the last PCI region.
+ */
+
+ /* Begin with sane defaults. */
+ loc = locations[0];
+ *start = 0;
+ if (IS_QLA24XX_TYPE(ha))
+ *start = FA_FLASH_LAYOUT_ADDR_24;
+ else if (IS_QLA25XX(ha))
+ *start = FA_FLASH_LAYOUT_ADDR;
+ else if (IS_QLA81XX(ha))
+ *start = FA_FLASH_LAYOUT_ADDR_81;
+ else if (IS_QLA82XX(ha)) {
+ *start = FA_FLASH_LAYOUT_ADDR_82;
+ goto end;
+ } else if (IS_QLA83XX(ha)) {
+ *start = FA_FLASH_LAYOUT_ADDR_83;
+ goto end;
+ }
+ /* Begin with first PCI expansion ROM header. */
+ buf = (uint8_t *)req->ring;
+ dcode = (uint32_t *)req->ring;
+ pcihdr = 0;
+ last_image = 1;
+ do {
+ /* Verify PCI expansion ROM header. */
+ qla24xx_read_flash_data(vha, dcode, pcihdr >> 2, 0x20);
+ bcode = buf + (pcihdr % 4);
+ if (bcode[0x0] != 0x55 || bcode[0x1] != 0xaa)
+ goto end;
+
+ /* Locate PCI data structure. */
+ pcids = pcihdr + ((bcode[0x19] << 8) | bcode[0x18]);
+ qla24xx_read_flash_data(vha, dcode, pcids >> 2, 0x20);
+ bcode = buf + (pcihdr % 4);
+
+ /* Validate signature of PCI data structure. */
+ if (bcode[0x0] != 'P' || bcode[0x1] != 'C' ||
+ bcode[0x2] != 'I' || bcode[0x3] != 'R')
+ goto end;
+
+ last_image = bcode[0x15] & BIT_7;
+
+ /* Locate next PCI expansion ROM. */
+ pcihdr += ((bcode[0x11] << 8) | bcode[0x10]) * 512;
+ } while (!last_image);
+
+ /* Now verify FLT-location structure. */
+ fltl = (struct qla_flt_location *)req->ring;
+ qla24xx_read_flash_data(vha, dcode, pcihdr >> 2,
+ sizeof(struct qla_flt_location) >> 2);
+ if (fltl->sig[0] != 'Q' || fltl->sig[1] != 'F' ||
+ fltl->sig[2] != 'L' || fltl->sig[3] != 'T')
+ goto end;
+
+ wptr = (uint16_t *)req->ring;
+ cnt = sizeof(struct qla_flt_location) >> 1;
+ for (chksum = 0; cnt; cnt--)
+ chksum += le16_to_cpu(*wptr++);
+ if (chksum) {
+ ql_log(ql_log_fatal, vha, 0x0045,
+ "Inconsistent FLTL detected: checksum=0x%x.\n", chksum);
+ ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x010e,
+ buf, sizeof(struct qla_flt_location));
+ return QLA_FUNCTION_FAILED;
+ }
+
+ /* Good data. Use specified location. */
+ loc = locations[1];
+ *start = (le16_to_cpu(fltl->start_hi) << 16 |
+ le16_to_cpu(fltl->start_lo)) >> 2;
+end:
+ ql_dbg(ql_dbg_init, vha, 0x0046,
+ "FLTL[%s] = 0x%x.\n",
+ loc, *start);
+ return QLA_SUCCESS;
+}
+
+static void
+qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr)
+{
+ const char *loc, *locations[] = { "DEF", "FLT" };
+ const uint32_t def_fw[] =
+ { FA_RISC_CODE_ADDR, FA_RISC_CODE_ADDR, FA_RISC_CODE_ADDR_81 };
+ const uint32_t def_boot[] =
+ { FA_BOOT_CODE_ADDR, FA_BOOT_CODE_ADDR, FA_BOOT_CODE_ADDR_81 };
+ const uint32_t def_vpd_nvram[] =
+ { FA_VPD_NVRAM_ADDR, FA_VPD_NVRAM_ADDR, FA_VPD_NVRAM_ADDR_81 };
+ const uint32_t def_vpd0[] =
+ { 0, 0, FA_VPD0_ADDR_81 };
+ const uint32_t def_vpd1[] =
+ { 0, 0, FA_VPD1_ADDR_81 };
+ const uint32_t def_nvram0[] =
+ { 0, 0, FA_NVRAM0_ADDR_81 };
+ const uint32_t def_nvram1[] =
+ { 0, 0, FA_NVRAM1_ADDR_81 };
+ const uint32_t def_fdt[] =
+ { FA_FLASH_DESCR_ADDR_24, FA_FLASH_DESCR_ADDR,
+ FA_FLASH_DESCR_ADDR_81 };
+ const uint32_t def_npiv_conf0[] =
+ { FA_NPIV_CONF0_ADDR_24, FA_NPIV_CONF0_ADDR,
+ FA_NPIV_CONF0_ADDR_81 };
+ const uint32_t def_npiv_conf1[] =
+ { FA_NPIV_CONF1_ADDR_24, FA_NPIV_CONF1_ADDR,
+ FA_NPIV_CONF1_ADDR_81 };
+ const uint32_t fcp_prio_cfg0[] =
+ { FA_FCP_PRIO0_ADDR, FA_FCP_PRIO0_ADDR_25,
+ 0 };
+ const uint32_t fcp_prio_cfg1[] =
+ { FA_FCP_PRIO1_ADDR, FA_FCP_PRIO1_ADDR_25,
+ 0 };
+ uint32_t def;
+ uint16_t *wptr;
+ uint16_t cnt, chksum;
+ uint32_t start;
+ struct qla_flt_header *flt;
+ struct qla_flt_region *region;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+
+ def = 0;
+ if (IS_QLA25XX(ha))
+ def = 1;
+ else if (IS_QLA81XX(ha))
+ def = 2;
+
+ /* Assign FCP prio region since older adapters may not have FLT, or
+ FCP prio region in it's FLT.
+ */
+ ha->flt_region_fcp_prio = ha->flags.port0 ?
+ fcp_prio_cfg0[def] : fcp_prio_cfg1[def];
+
+ ha->flt_region_flt = flt_addr;
+ wptr = (uint16_t *)req->ring;
+ flt = (struct qla_flt_header *)req->ring;
+ region = (struct qla_flt_region *)&flt[1];
+ ha->isp_ops->read_optrom(vha, (uint8_t *)req->ring,
+ flt_addr << 2, OPTROM_BURST_SIZE);
+ if (*wptr == __constant_cpu_to_le16(0xffff))
+ goto no_flash_data;
+ if (flt->version != __constant_cpu_to_le16(1)) {
+ ql_log(ql_log_warn, vha, 0x0047,
+ "Unsupported FLT detected: version=0x%x length=0x%x checksum=0x%x.\n",
+ le16_to_cpu(flt->version), le16_to_cpu(flt->length),
+ le16_to_cpu(flt->checksum));
+ goto no_flash_data;
+ }
+
+ cnt = (sizeof(struct qla_flt_header) + le16_to_cpu(flt->length)) >> 1;
+ for (chksum = 0; cnt; cnt--)
+ chksum += le16_to_cpu(*wptr++);
+ if (chksum) {
+ ql_log(ql_log_fatal, vha, 0x0048,
+ "Inconsistent FLT detected: version=0x%x length=0x%x checksum=0x%x.\n",
+ le16_to_cpu(flt->version), le16_to_cpu(flt->length),
+ le16_to_cpu(flt->checksum));
+ goto no_flash_data;
+ }
+
+ loc = locations[1];
+ cnt = le16_to_cpu(flt->length) / sizeof(struct qla_flt_region);
+ for ( ; cnt; cnt--, region++) {
+ /* Store addresses as DWORD offsets. */
+ start = le32_to_cpu(region->start) >> 2;
+ ql_dbg(ql_dbg_init, vha, 0x0049,
+ "FLT[%02x]: start=0x%x "
+ "end=0x%x size=0x%x.\n", le32_to_cpu(region->code),
+ start, le32_to_cpu(region->end) >> 2,
+ le32_to_cpu(region->size));
+
+ switch (le32_to_cpu(region->code) & 0xff) {
+ case FLT_REG_FCOE_FW:
+ if (!IS_QLA8031(ha))
+ break;
+ ha->flt_region_fw = start;
+ break;
+ case FLT_REG_FW:
+ if (IS_QLA8031(ha))
+ break;
+ ha->flt_region_fw = start;
+ break;
+ case FLT_REG_BOOT_CODE:
+ ha->flt_region_boot = start;
+ break;
+ case FLT_REG_VPD_0:
+ if (IS_QLA8031(ha))
+ break;
+ ha->flt_region_vpd_nvram = start;
+ if (IS_QLA82XX(ha))
+ break;
+ if (ha->flags.port0)
+ ha->flt_region_vpd = start;
+ break;
+ case FLT_REG_VPD_1:
+ if (IS_QLA82XX(ha) || IS_QLA8031(ha))
+ break;
+ if (!ha->flags.port0)
+ ha->flt_region_vpd = start;
+ break;
+ case FLT_REG_NVRAM_0:
+ if (IS_QLA8031(ha))
+ break;
+ if (ha->flags.port0)
+ ha->flt_region_nvram = start;
+ break;
+ case FLT_REG_NVRAM_1:
+ if (IS_QLA8031(ha))
+ break;
+ if (!ha->flags.port0)
+ ha->flt_region_nvram = start;
+ break;
+ case FLT_REG_FDT:
+ ha->flt_region_fdt = start;
+ break;
+ case FLT_REG_NPIV_CONF_0:
+ if (ha->flags.port0)
+ ha->flt_region_npiv_conf = start;
+ break;
+ case FLT_REG_NPIV_CONF_1:
+ if (!ha->flags.port0)
+ ha->flt_region_npiv_conf = start;
+ break;
+ case FLT_REG_GOLD_FW:
+ ha->flt_region_gold_fw = start;
+ break;
+ case FLT_REG_FCP_PRIO_0:
+ if (ha->flags.port0)
+ ha->flt_region_fcp_prio = start;
+ break;
+ case FLT_REG_FCP_PRIO_1:
+ if (!ha->flags.port0)
+ ha->flt_region_fcp_prio = start;
+ break;
+ case FLT_REG_BOOT_CODE_82XX:
+ ha->flt_region_boot = start;
+ break;
+ case FLT_REG_FW_82XX:
+ ha->flt_region_fw = start;
+ break;
+ case FLT_REG_GOLD_FW_82XX:
+ ha->flt_region_gold_fw = start;
+ break;
+ case FLT_REG_BOOTLOAD_82XX:
+ ha->flt_region_bootload = start;
+ break;
+ case FLT_REG_VPD_82XX:
+ ha->flt_region_vpd = start;
+ break;
+ case FLT_REG_FCOE_VPD_0:
+ if (!IS_QLA8031(ha))
+ break;
+ ha->flt_region_vpd_nvram = start;
+ if (ha->flags.port0)
+ ha->flt_region_vpd = start;
+ break;
+ case FLT_REG_FCOE_VPD_1:
+ if (!IS_QLA8031(ha))
+ break;
+ if (!ha->flags.port0)
+ ha->flt_region_vpd = start;
+ break;
+ case FLT_REG_FCOE_NVRAM_0:
+ if (!IS_QLA8031(ha))
+ break;
+ if (ha->flags.port0)
+ ha->flt_region_nvram = start;
+ break;
+ case FLT_REG_FCOE_NVRAM_1:
+ if (!IS_QLA8031(ha))
+ break;
+ if (!ha->flags.port0)
+ ha->flt_region_nvram = start;
+ break;
+ }
+ }
+ goto done;
+
+no_flash_data:
+ /* Use hardcoded defaults. */
+ loc = locations[0];
+ ha->flt_region_fw = def_fw[def];
+ ha->flt_region_boot = def_boot[def];
+ ha->flt_region_vpd_nvram = def_vpd_nvram[def];
+ ha->flt_region_vpd = ha->flags.port0 ?
+ def_vpd0[def] : def_vpd1[def];
+ ha->flt_region_nvram = ha->flags.port0 ?
+ def_nvram0[def] : def_nvram1[def];
+ ha->flt_region_fdt = def_fdt[def];
+ ha->flt_region_npiv_conf = ha->flags.port0 ?
+ def_npiv_conf0[def] : def_npiv_conf1[def];
+done:
+ ql_dbg(ql_dbg_init, vha, 0x004a,
+ "FLT[%s]: boot=0x%x fw=0x%x vpd_nvram=0x%x vpd=0x%x nvram=0x%x "
+ "fdt=0x%x flt=0x%x npiv=0x%x fcp_prif_cfg=0x%x.\n",
+ loc, ha->flt_region_boot, ha->flt_region_fw,
+ ha->flt_region_vpd_nvram, ha->flt_region_vpd, ha->flt_region_nvram,
+ ha->flt_region_fdt, ha->flt_region_flt, ha->flt_region_npiv_conf,
+ ha->flt_region_fcp_prio);
+}
+
+static void
+qla2xxx_get_fdt_info(scsi_qla_host_t *vha)
+{
+#define FLASH_BLK_SIZE_4K 0x1000
+#define FLASH_BLK_SIZE_32K 0x8000
+#define FLASH_BLK_SIZE_64K 0x10000
+ const char *loc, *locations[] = { "MID", "FDT" };
+ uint16_t cnt, chksum;
+ uint16_t *wptr;
+ struct qla_fdt_layout *fdt;
+ uint8_t man_id, flash_id;
+ uint16_t mid = 0, fid = 0;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+
+ wptr = (uint16_t *)req->ring;
+ fdt = (struct qla_fdt_layout *)req->ring;
+ ha->isp_ops->read_optrom(vha, (uint8_t *)req->ring,
+ ha->flt_region_fdt << 2, OPTROM_BURST_SIZE);
+ if (*wptr == __constant_cpu_to_le16(0xffff))
+ goto no_flash_data;
+ if (fdt->sig[0] != 'Q' || fdt->sig[1] != 'L' || fdt->sig[2] != 'I' ||
+ fdt->sig[3] != 'D')
+ goto no_flash_data;
+
+ for (cnt = 0, chksum = 0; cnt < sizeof(struct qla_fdt_layout) >> 1;
+ cnt++)
+ chksum += le16_to_cpu(*wptr++);
+ if (chksum) {
+ ql_dbg(ql_dbg_init, vha, 0x004c,
+ "Inconsistent FDT detected:"
+ " checksum=0x%x id=%c version0x%x.\n", chksum,
+ fdt->sig[0], le16_to_cpu(fdt->version));
+ ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0113,
+ (uint8_t *)fdt, sizeof(*fdt));
+ goto no_flash_data;
+ }
+
+ loc = locations[1];
+ mid = le16_to_cpu(fdt->man_id);
+ fid = le16_to_cpu(fdt->id);
+ ha->fdt_wrt_disable = fdt->wrt_disable_bits;
+ ha->fdt_erase_cmd = flash_conf_addr(ha, 0x0300 | fdt->erase_cmd);
+ ha->fdt_block_size = le32_to_cpu(fdt->block_size);
+ if (fdt->unprotect_sec_cmd) {
+ ha->fdt_unprotect_sec_cmd = flash_conf_addr(ha, 0x0300 |
+ fdt->unprotect_sec_cmd);
+ ha->fdt_protect_sec_cmd = fdt->protect_sec_cmd ?
+ flash_conf_addr(ha, 0x0300 | fdt->protect_sec_cmd):
+ flash_conf_addr(ha, 0x0336);
+ }
+ goto done;
+no_flash_data:
+ loc = locations[0];
+ if (IS_QLA82XX(ha)) {
+ ha->fdt_block_size = FLASH_BLK_SIZE_64K;
+ goto done;
+ }
+ qla24xx_get_flash_manufacturer(ha, &man_id, &flash_id);
+ mid = man_id;
+ fid = flash_id;
+ ha->fdt_wrt_disable = 0x9c;
+ ha->fdt_erase_cmd = flash_conf_addr(ha, 0x03d8);
+ switch (man_id) {
+ case 0xbf: /* STT flash. */
+ if (flash_id == 0x8e)
+ ha->fdt_block_size = FLASH_BLK_SIZE_64K;
+ else
+ ha->fdt_block_size = FLASH_BLK_SIZE_32K;
+
+ if (flash_id == 0x80)
+ ha->fdt_erase_cmd = flash_conf_addr(ha, 0x0352);
+ break;
+ case 0x13: /* ST M25P80. */
+ ha->fdt_block_size = FLASH_BLK_SIZE_64K;
+ break;
+ case 0x1f: /* Atmel 26DF081A. */
+ ha->fdt_block_size = FLASH_BLK_SIZE_4K;
+ ha->fdt_erase_cmd = flash_conf_addr(ha, 0x0320);
+ ha->fdt_unprotect_sec_cmd = flash_conf_addr(ha, 0x0339);
+ ha->fdt_protect_sec_cmd = flash_conf_addr(ha, 0x0336);
+ break;
+ default:
+ /* Default to 64 kb sector size. */
+ ha->fdt_block_size = FLASH_BLK_SIZE_64K;
+ break;
+ }
+done:
+ ql_dbg(ql_dbg_init, vha, 0x004d,
+ "FDT[%s]: (0x%x/0x%x) erase=0x%x "
+ "pr=%x wrtd=0x%x blk=0x%x.\n",
+ loc, mid, fid,
+ ha->fdt_erase_cmd, ha->fdt_protect_sec_cmd,
+ ha->fdt_wrt_disable, ha->fdt_block_size);
+
+}
+
+static void
+qla2xxx_get_idc_param(scsi_qla_host_t *vha)
+{
+#define QLA82XX_IDC_PARAM_ADDR 0x003e885c
+ uint32_t *wptr;
+ struct qla_hw_data *ha = vha->hw;
+ struct req_que *req = ha->req_q_map[0];
+
+ if (!IS_QLA82XX(ha))
+ return;
+
+ wptr = (uint32_t *)req->ring;
+ ha->isp_ops->read_optrom(vha, (uint8_t *)req->ring,
+ QLA82XX_IDC_PARAM_ADDR , 8);
+
+ if (*wptr == __constant_cpu_to_le32(0xffffffff)) {
+ ha->nx_dev_init_timeout = QLA82XX_ROM_DEV_INIT_TIMEOUT;
+ ha->nx_reset_timeout = QLA82XX_ROM_DRV_RESET_ACK_TIMEOUT;
+ } else {
+ ha->nx_dev_init_timeout = le32_to_cpu(*wptr++);
+ ha->nx_reset_timeout = le32_to_cpu(*wptr);
+ }
+ ql_dbg(ql_dbg_init, vha, 0x004e,
+ "nx_dev_init_timeout=%d "
+ "nx_reset_timeout=%d.\n", ha->nx_dev_init_timeout,
+ ha->nx_reset_timeout);
+ return;
+}
+
+int
+qla2xxx_get_flash_info(scsi_qla_host_t *vha)
+{
+ int ret;
+ uint32_t flt_addr;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) &&
+ !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha))
+ return QLA_SUCCESS;
+
+ ret = qla2xxx_find_flt_start(vha, &flt_addr);
+ if (ret != QLA_SUCCESS)
+ return ret;
+
+ qla2xxx_get_flt_info(vha, flt_addr);
+ qla2xxx_get_fdt_info(vha);
+ qla2xxx_get_idc_param(vha);
+
+ return QLA_SUCCESS;
+}
+
+void
+qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha)
+{
+#define NPIV_CONFIG_SIZE (16*1024)
+ void *data;
+ uint16_t *wptr;
+ uint16_t cnt, chksum;
+ int i;
+ struct qla_npiv_header hdr;
+ struct qla_npiv_entry *entry;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) &&
+ !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha))
+ return;
+
+ if (ha->flags.isp82xx_reset_hdlr_active)
+ return;
+
+ ha->isp_ops->read_optrom(vha, (uint8_t *)&hdr,
+ ha->flt_region_npiv_conf << 2, sizeof(struct qla_npiv_header));
+ if (hdr.version == __constant_cpu_to_le16(0xffff))
+ return;
+ if (hdr.version != __constant_cpu_to_le16(1)) {
+ ql_dbg(ql_dbg_user, vha, 0x7090,
+ "Unsupported NPIV-Config "
+ "detected: version=0x%x entries=0x%x checksum=0x%x.\n",
+ le16_to_cpu(hdr.version), le16_to_cpu(hdr.entries),
+ le16_to_cpu(hdr.checksum));
+ return;
+ }
+
+ data = kmalloc(NPIV_CONFIG_SIZE, GFP_KERNEL);
+ if (!data) {
+ ql_log(ql_log_warn, vha, 0x7091,
+ "Unable to allocate memory for data.\n");
+ return;
+ }
+
+ ha->isp_ops->read_optrom(vha, (uint8_t *)data,
+ ha->flt_region_npiv_conf << 2, NPIV_CONFIG_SIZE);
+
+ cnt = (sizeof(struct qla_npiv_header) + le16_to_cpu(hdr.entries) *
+ sizeof(struct qla_npiv_entry)) >> 1;
+ for (wptr = data, chksum = 0; cnt; cnt--)
+ chksum += le16_to_cpu(*wptr++);
+ if (chksum) {
+ ql_dbg(ql_dbg_user, vha, 0x7092,
+ "Inconsistent NPIV-Config "
+ "detected: version=0x%x entries=0x%x checksum=0x%x.\n",
+ le16_to_cpu(hdr.version), le16_to_cpu(hdr.entries),
+ le16_to_cpu(hdr.checksum));
+ goto done;
+ }
+
+ entry = data + sizeof(struct qla_npiv_header);
+ cnt = le16_to_cpu(hdr.entries);
+ for (i = 0; cnt; cnt--, entry++, i++) {
+ uint16_t flags;
+ struct fc_vport_identifiers vid;
+ struct fc_vport *vport;
+
+ memcpy(&ha->npiv_info[i], entry, sizeof(struct qla_npiv_entry));
+
+ flags = le16_to_cpu(entry->flags);
+ if (flags == 0xffff)
+ continue;
+ if ((flags & BIT_0) == 0)
+ continue;
+
+ memset(&vid, 0, sizeof(vid));
+ vid.roles = FC_PORT_ROLE_FCP_INITIATOR;
+ vid.vport_type = FC_PORTTYPE_NPIV;
+ vid.disable = false;
+ vid.port_name = wwn_to_u64(entry->port_name);
+ vid.node_name = wwn_to_u64(entry->node_name);
+
+ ql_dbg(ql_dbg_user, vha, 0x7093,
+ "NPIV[%02x]: wwpn=%llx "
+ "wwnn=%llx vf_id=0x%x Q_qos=0x%x F_qos=0x%x.\n", cnt,
+ (unsigned long long)vid.port_name,
+ (unsigned long long)vid.node_name,
+ le16_to_cpu(entry->vf_id),
+ entry->q_qos, entry->f_qos);
+
+ if (i < QLA_PRECONFIG_VPORTS) {
+ vport = fc_vport_create(vha->host, 0, &vid);
+ if (!vport)
+ ql_log(ql_log_warn, vha, 0x7094,
+ "NPIV-Config Failed to create vport [%02x]: "
+ "wwpn=%llx wwnn=%llx.\n", cnt,
+ (unsigned long long)vid.port_name,
+ (unsigned long long)vid.node_name);
+ }
+ }
+done:
+ kfree(data);
+}
+
+static int
+qla24xx_unprotect_flash(scsi_qla_host_t *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+ if (ha->flags.fac_supported)
+ return qla81xx_fac_do_write_enable(vha, 1);
+
+ /* Enable flash write. */
+ WRT_REG_DWORD(&reg->ctrl_status,
+ RD_REG_DWORD(&reg->ctrl_status) | CSRX_FLASH_ENABLE);
+ RD_REG_DWORD(&reg->ctrl_status); /* PCI Posting. */
+
+ if (!ha->fdt_wrt_disable)
+ goto done;
+
+ /* Disable flash write-protection, first clear SR protection bit */
+ qla24xx_write_flash_dword(ha, flash_conf_addr(ha, 0x101), 0);
+ /* Then write zero again to clear remaining SR bits.*/
+ qla24xx_write_flash_dword(ha, flash_conf_addr(ha, 0x101), 0);
+done:
+ return QLA_SUCCESS;
+}
+
+static int
+qla24xx_protect_flash(scsi_qla_host_t *vha)
+{
+ uint32_t cnt;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+ if (ha->flags.fac_supported)
+ return qla81xx_fac_do_write_enable(vha, 0);
+
+ if (!ha->fdt_wrt_disable)
+ goto skip_wrt_protect;
+
+ /* Enable flash write-protection and wait for completion. */
+ qla24xx_write_flash_dword(ha, flash_conf_addr(ha, 0x101),
+ ha->fdt_wrt_disable);
+ for (cnt = 300; cnt &&
+ qla24xx_read_flash_dword(ha, flash_conf_addr(ha, 0x005)) & BIT_0;
+ cnt--) {
+ udelay(10);
+ }
+
+skip_wrt_protect:
+ /* Disable flash write. */
+ WRT_REG_DWORD(&reg->ctrl_status,
+ RD_REG_DWORD(&reg->ctrl_status) & ~CSRX_FLASH_ENABLE);
+ RD_REG_DWORD(&reg->ctrl_status); /* PCI Posting. */
+
+ return QLA_SUCCESS;
+}
+
+static int
+qla24xx_erase_sector(scsi_qla_host_t *vha, uint32_t fdata)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint32_t start, finish;
+
+ if (ha->flags.fac_supported) {
+ start = fdata >> 2;
+ finish = start + (ha->fdt_block_size >> 2) - 1;
+ return qla81xx_fac_erase_sector(vha, flash_data_addr(ha,
+ start), flash_data_addr(ha, finish));
+ }
+
+ return qla24xx_write_flash_dword(ha, ha->fdt_erase_cmd,
+ (fdata & 0xff00) | ((fdata << 16) & 0xff0000) |
+ ((fdata >> 16) & 0xff));
+}
+
+static int
+qla24xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr,
+ uint32_t dwords)
+{
+ int ret;
+ uint32_t liter;
+ uint32_t sec_mask, rest_addr;
+ uint32_t fdata;
+ dma_addr_t optrom_dma;
+ void *optrom = NULL;
+ struct qla_hw_data *ha = vha->hw;
+
+ /* Prepare burst-capable write on supported ISPs. */
+ if ((IS_QLA25XX(ha) || IS_QLA81XX(ha) || IS_QLA83XX(ha)) &&
+ !(faddr & 0xfff) && dwords > OPTROM_BURST_DWORDS) {
+ optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
+ &optrom_dma, GFP_KERNEL);
+ if (!optrom) {
+ ql_log(ql_log_warn, vha, 0x7095,
+ "Unable to allocate "
+ "memory for optrom burst write (%x KB).\n",
+ OPTROM_BURST_SIZE / 1024);
+ }
+ }
+
+ rest_addr = (ha->fdt_block_size >> 2) - 1;
+ sec_mask = ~rest_addr;
+
+ ret = qla24xx_unprotect_flash(vha);
+ if (ret != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x7096,
+ "Unable to unprotect flash for update.\n");
+ goto done;
+ }
+
+ for (liter = 0; liter < dwords; liter++, faddr++, dwptr++) {
+ fdata = (faddr & sec_mask) << 2;
+
+ /* Are we at the beginning of a sector? */
+ if ((faddr & rest_addr) == 0) {
+ /* Do sector unprotect. */
+ if (ha->fdt_unprotect_sec_cmd)
+ qla24xx_write_flash_dword(ha,
+ ha->fdt_unprotect_sec_cmd,
+ (fdata & 0xff00) | ((fdata << 16) &
+ 0xff0000) | ((fdata >> 16) & 0xff));
+ ret = qla24xx_erase_sector(vha, fdata);
+ if (ret != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_user, vha, 0x7007,
+ "Unable to erase erase sector: address=%x.\n",
+ faddr);
+ break;
+ }
+ }
+
+ /* Go with burst-write. */
+ if (optrom && (liter + OPTROM_BURST_DWORDS) <= dwords) {
+ /* Copy data to DMA'ble buffer. */
+ memcpy(optrom, dwptr, OPTROM_BURST_SIZE);
+
+ ret = qla2x00_load_ram(vha, optrom_dma,
+ flash_data_addr(ha, faddr),
+ OPTROM_BURST_DWORDS);
+ if (ret != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x7097,
+ "Unable to burst-write optrom segment "
+ "(%x/%x/%llx).\n", ret,
+ flash_data_addr(ha, faddr),
+ (unsigned long long)optrom_dma);
+ ql_log(ql_log_warn, vha, 0x7098,
+ "Reverting to slow-write.\n");
+
+ dma_free_coherent(&ha->pdev->dev,
+ OPTROM_BURST_SIZE, optrom, optrom_dma);
+ optrom = NULL;
+ } else {
+ liter += OPTROM_BURST_DWORDS - 1;
+ faddr += OPTROM_BURST_DWORDS - 1;
+ dwptr += OPTROM_BURST_DWORDS - 1;
+ continue;
+ }
+ }
+
+ ret = qla24xx_write_flash_dword(ha,
+ flash_data_addr(ha, faddr), cpu_to_le32(*dwptr));
+ if (ret != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_user, vha, 0x7006,
+ "Unable to program flash address=%x data=%x.\n",
+ faddr, *dwptr);
+ break;
+ }
+
+ /* Do sector protect. */
+ if (ha->fdt_unprotect_sec_cmd &&
+ ((faddr & rest_addr) == rest_addr))
+ qla24xx_write_flash_dword(ha,
+ ha->fdt_protect_sec_cmd,
+ (fdata & 0xff00) | ((fdata << 16) &
+ 0xff0000) | ((fdata >> 16) & 0xff));
+ }
+
+ ret = qla24xx_protect_flash(vha);
+ if (ret != QLA_SUCCESS)
+ ql_log(ql_log_warn, vha, 0x7099,
+ "Unable to protect flash after update.\n");
+done:
+ if (optrom)
+ dma_free_coherent(&ha->pdev->dev,
+ OPTROM_BURST_SIZE, optrom, optrom_dma);
+
+ return ret;
+}
+
+uint8_t *
+qla2x00_read_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr,
+ uint32_t bytes)
+{
+ uint32_t i;
+ uint16_t *wptr;
+ struct qla_hw_data *ha = vha->hw;
+
+ /* Word reads to NVRAM via registers. */
+ wptr = (uint16_t *)buf;
+ qla2x00_lock_nvram_access(ha);
+ for (i = 0; i < bytes >> 1; i++, naddr++)
+ wptr[i] = cpu_to_le16(qla2x00_get_nvram_word(ha,
+ naddr));
+ qla2x00_unlock_nvram_access(ha);
+
+ return buf;
+}
+
+uint8_t *
+qla24xx_read_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr,
+ uint32_t bytes)
+{
+ uint32_t i;
+ uint32_t *dwptr;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (IS_QLA82XX(ha))
+ return buf;
+
+ /* Dword reads to flash. */
+ dwptr = (uint32_t *)buf;
+ for (i = 0; i < bytes >> 2; i++, naddr++)
+ dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(ha,
+ nvram_data_addr(ha, naddr)));
+
+ return buf;
+}
+
+int
+qla2x00_write_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr,
+ uint32_t bytes)
+{
+ int ret, stat;
+ uint32_t i;
+ uint16_t *wptr;
+ unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
+
+ ret = QLA_SUCCESS;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ qla2x00_lock_nvram_access(ha);
+
+ /* Disable NVRAM write-protection. */
+ stat = qla2x00_clear_nvram_protection(ha);
+
+ wptr = (uint16_t *)buf;
+ for (i = 0; i < bytes >> 1; i++, naddr++) {
+ qla2x00_write_nvram_word(ha, naddr,
+ cpu_to_le16(*wptr));
+ wptr++;
+ }
+
+ /* Enable NVRAM write-protection. */
+ qla2x00_set_nvram_protection(ha, stat);
+
+ qla2x00_unlock_nvram_access(ha);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ return ret;
+}
+
+int
+qla24xx_write_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr,
+ uint32_t bytes)
+{
+ int ret;
+ uint32_t i;
+ uint32_t *dwptr;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+ ret = QLA_SUCCESS;
+
+ if (IS_QLA82XX(ha))
+ return ret;
+
+ /* Enable flash write. */
+ WRT_REG_DWORD(&reg->ctrl_status,
+ RD_REG_DWORD(&reg->ctrl_status) | CSRX_FLASH_ENABLE);
+ RD_REG_DWORD(&reg->ctrl_status); /* PCI Posting. */
+
+ /* Disable NVRAM write-protection. */
+ qla24xx_write_flash_dword(ha, nvram_conf_addr(ha, 0x101), 0);
+ qla24xx_write_flash_dword(ha, nvram_conf_addr(ha, 0x101), 0);
+
+ /* Dword writes to flash. */
+ dwptr = (uint32_t *)buf;
+ for (i = 0; i < bytes >> 2; i++, naddr++, dwptr++) {
+ ret = qla24xx_write_flash_dword(ha,
+ nvram_data_addr(ha, naddr), cpu_to_le32(*dwptr));
+ if (ret != QLA_SUCCESS) {
+ ql_dbg(ql_dbg_user, vha, 0x709a,
+ "Unable to program nvram address=%x data=%x.\n",
+ naddr, *dwptr);
+ break;
+ }
+ }
+
+ /* Enable NVRAM write-protection. */
+ qla24xx_write_flash_dword(ha, nvram_conf_addr(ha, 0x101), 0x8c);
+
+ /* Disable flash write. */
+ WRT_REG_DWORD(&reg->ctrl_status,
+ RD_REG_DWORD(&reg->ctrl_status) & ~CSRX_FLASH_ENABLE);
+ RD_REG_DWORD(&reg->ctrl_status); /* PCI Posting. */
+
+ return ret;
+}
+
+uint8_t *
+qla25xx_read_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr,
+ uint32_t bytes)
+{
+ uint32_t i;
+ uint32_t *dwptr;
+ struct qla_hw_data *ha = vha->hw;
+
+ /* Dword reads to flash. */
+ dwptr = (uint32_t *)buf;
+ for (i = 0; i < bytes >> 2; i++, naddr++)
+ dwptr[i] = cpu_to_le32(qla24xx_read_flash_dword(ha,
+ flash_data_addr(ha, ha->flt_region_vpd_nvram | naddr)));
+
+ return buf;
+}
+
+int
+qla25xx_write_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr,
+ uint32_t bytes)
+{
+ struct qla_hw_data *ha = vha->hw;
+#define RMW_BUFFER_SIZE (64 * 1024)
+ uint8_t *dbuf;
+
+ dbuf = vmalloc(RMW_BUFFER_SIZE);
+ if (!dbuf)
+ return QLA_MEMORY_ALLOC_FAILED;
+ ha->isp_ops->read_optrom(vha, dbuf, ha->flt_region_vpd_nvram << 2,
+ RMW_BUFFER_SIZE);
+ memcpy(dbuf + (naddr << 2), buf, bytes);
+ ha->isp_ops->write_optrom(vha, dbuf, ha->flt_region_vpd_nvram << 2,
+ RMW_BUFFER_SIZE);
+ vfree(dbuf);
+
+ return QLA_SUCCESS;
+}
+
+static inline void
+qla2x00_flip_colors(struct qla_hw_data *ha, uint16_t *pflags)
+{
+ if (IS_QLA2322(ha)) {
+ /* Flip all colors. */
+ if (ha->beacon_color_state == QLA_LED_ALL_ON) {
+ /* Turn off. */
+ ha->beacon_color_state = 0;
+ *pflags = GPIO_LED_ALL_OFF;
+ } else {
+ /* Turn on. */
+ ha->beacon_color_state = QLA_LED_ALL_ON;
+ *pflags = GPIO_LED_RGA_ON;
+ }
+ } else {
+ /* Flip green led only. */
+ if (ha->beacon_color_state == QLA_LED_GRN_ON) {
+ /* Turn off. */
+ ha->beacon_color_state = 0;
+ *pflags = GPIO_LED_GREEN_OFF_AMBER_OFF;
+ } else {
+ /* Turn on. */
+ ha->beacon_color_state = QLA_LED_GRN_ON;
+ *pflags = GPIO_LED_GREEN_ON_AMBER_OFF;
+ }
+ }
+}
+
+#define PIO_REG(h, r) ((h)->pio_address + offsetof(struct device_reg_2xxx, r))
+
+void
+qla2x00_beacon_blink(struct scsi_qla_host *vha)
+{
+ uint16_t gpio_enable;
+ uint16_t gpio_data;
+ uint16_t led_color = 0;
+ unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ if (IS_QLA82XX(ha))
+ return;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+
+ /* Save the Original GPIOE. */
+ if (ha->pio_address) {
+ gpio_enable = RD_REG_WORD_PIO(PIO_REG(ha, gpioe));
+ gpio_data = RD_REG_WORD_PIO(PIO_REG(ha, gpiod));
+ } else {
+ gpio_enable = RD_REG_WORD(&reg->gpioe);
+ gpio_data = RD_REG_WORD(&reg->gpiod);
+ }
+
+ /* Set the modified gpio_enable values */
+ gpio_enable |= GPIO_LED_MASK;
+
+ if (ha->pio_address) {
+ WRT_REG_WORD_PIO(PIO_REG(ha, gpioe), gpio_enable);
+ } else {
+ WRT_REG_WORD(&reg->gpioe, gpio_enable);
+ RD_REG_WORD(&reg->gpioe);
+ }
+
+ qla2x00_flip_colors(ha, &led_color);
+
+ /* Clear out any previously set LED color. */
+ gpio_data &= ~GPIO_LED_MASK;
+
+ /* Set the new input LED color to GPIOD. */
+ gpio_data |= led_color;
+
+ /* Set the modified gpio_data values */
+ if (ha->pio_address) {
+ WRT_REG_WORD_PIO(PIO_REG(ha, gpiod), gpio_data);
+ } else {
+ WRT_REG_WORD(&reg->gpiod, gpio_data);
+ RD_REG_WORD(&reg->gpiod);
+ }
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+int
+qla2x00_beacon_on(struct scsi_qla_host *vha)
+{
+ uint16_t gpio_enable;
+ uint16_t gpio_data;
+ unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ ha->fw_options[1] &= ~FO1_SET_EMPHASIS_SWING;
+ ha->fw_options[1] |= FO1_DISABLE_GPIO6_7;
+
+ if (qla2x00_set_fw_options(vha, ha->fw_options) != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x709b,
+ "Unable to update fw options (beacon on).\n");
+ return QLA_FUNCTION_FAILED;
+ }
+
+ /* Turn off LEDs. */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ if (ha->pio_address) {
+ gpio_enable = RD_REG_WORD_PIO(PIO_REG(ha, gpioe));
+ gpio_data = RD_REG_WORD_PIO(PIO_REG(ha, gpiod));
+ } else {
+ gpio_enable = RD_REG_WORD(&reg->gpioe);
+ gpio_data = RD_REG_WORD(&reg->gpiod);
+ }
+ gpio_enable |= GPIO_LED_MASK;
+
+ /* Set the modified gpio_enable values. */
+ if (ha->pio_address) {
+ WRT_REG_WORD_PIO(PIO_REG(ha, gpioe), gpio_enable);
+ } else {
+ WRT_REG_WORD(&reg->gpioe, gpio_enable);
+ RD_REG_WORD(&reg->gpioe);
+ }
+
+ /* Clear out previously set LED colour. */
+ gpio_data &= ~GPIO_LED_MASK;
+ if (ha->pio_address) {
+ WRT_REG_WORD_PIO(PIO_REG(ha, gpiod), gpio_data);
+ } else {
+ WRT_REG_WORD(&reg->gpiod, gpio_data);
+ RD_REG_WORD(&reg->gpiod);
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+ /*
+ * Let the per HBA timer kick off the blinking process based on
+ * the following flags. No need to do anything else now.
+ */
+ ha->beacon_blink_led = 1;
+ ha->beacon_color_state = 0;
+
+ return QLA_SUCCESS;
+}
+
+int
+qla2x00_beacon_off(struct scsi_qla_host *vha)
+{
+ int rval = QLA_SUCCESS;
+ struct qla_hw_data *ha = vha->hw;
+
+ ha->beacon_blink_led = 0;
+
+ /* Set the on flag so when it gets flipped it will be off. */
+ if (IS_QLA2322(ha))
+ ha->beacon_color_state = QLA_LED_ALL_ON;
+ else
+ ha->beacon_color_state = QLA_LED_GRN_ON;
+
+ ha->isp_ops->beacon_blink(vha); /* This turns green LED off */
+
+ ha->fw_options[1] &= ~FO1_SET_EMPHASIS_SWING;
+ ha->fw_options[1] &= ~FO1_DISABLE_GPIO6_7;
+
+ rval = qla2x00_set_fw_options(vha, ha->fw_options);
+ if (rval != QLA_SUCCESS)
+ ql_log(ql_log_warn, vha, 0x709c,
+ "Unable to update fw options (beacon off).\n");
+ return rval;
+}
+
+
+static inline void
+qla24xx_flip_colors(struct qla_hw_data *ha, uint16_t *pflags)
+{
+ /* Flip all colors. */
+ if (ha->beacon_color_state == QLA_LED_ALL_ON) {
+ /* Turn off. */
+ ha->beacon_color_state = 0;
+ *pflags = 0;
+ } else {
+ /* Turn on. */
+ ha->beacon_color_state = QLA_LED_ALL_ON;
+ *pflags = GPDX_LED_YELLOW_ON | GPDX_LED_AMBER_ON;
+ }
+}
+
+void
+qla24xx_beacon_blink(struct scsi_qla_host *vha)
+{
+ uint16_t led_color = 0;
+ uint32_t gpio_data;
+ unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+ /* Save the Original GPIOD. */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ gpio_data = RD_REG_DWORD(&reg->gpiod);
+
+ /* Enable the gpio_data reg for update. */
+ gpio_data |= GPDX_LED_UPDATE_MASK;
+
+ WRT_REG_DWORD(&reg->gpiod, gpio_data);
+ gpio_data = RD_REG_DWORD(&reg->gpiod);
+
+ /* Set the color bits. */
+ qla24xx_flip_colors(ha, &led_color);
+
+ /* Clear out any previously set LED color. */
+ gpio_data &= ~GPDX_LED_COLOR_MASK;
+
+ /* Set the new input LED color to GPIOD. */
+ gpio_data |= led_color;
+
+ /* Set the modified gpio_data values. */
+ WRT_REG_DWORD(&reg->gpiod, gpio_data);
+ gpio_data = RD_REG_DWORD(&reg->gpiod);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+void
+qla83xx_beacon_blink(struct scsi_qla_host *vha)
+{
+ uint32_t led_select_value;
+ struct qla_hw_data *ha = vha->hw;
+ uint16_t led_cfg[6];
+ uint16_t orig_led_cfg[6];
+
+ if (!IS_QLA83XX(ha) && !IS_QLA81XX(ha))
+ return;
+
+ if (IS_QLA2031(ha) && ha->beacon_blink_led) {
+ if (ha->flags.port0)
+ led_select_value = 0x00201320;
+ else
+ led_select_value = 0x00201328;
+
+ qla83xx_write_remote_reg(vha, led_select_value, 0x40002000);
+ qla83xx_write_remote_reg(vha, led_select_value + 4, 0x40002000);
+ msleep(1000);
+ qla83xx_write_remote_reg(vha, led_select_value, 0x40004000);
+ qla83xx_write_remote_reg(vha, led_select_value + 4, 0x40004000);
+ } else if ((IS_QLA8031(ha) || IS_QLA81XX(ha)) && ha->beacon_blink_led) {
+ int rval;
+
+ /* Save Current */
+ rval = qla81xx_get_led_config(vha, orig_led_cfg);
+ /* Do the blink */
+ if (rval == QLA_SUCCESS) {
+ if (IS_QLA81XX(ha)) {
+ led_cfg[0] = 0x4000;
+ led_cfg[1] = 0x2000;
+ led_cfg[2] = 0;
+ led_cfg[3] = 0;
+ led_cfg[4] = 0;
+ led_cfg[5] = 0;
+ } else {
+ led_cfg[0] = 0x4000;
+ led_cfg[1] = 0x4000;
+ led_cfg[2] = 0x4000;
+ led_cfg[3] = 0x2000;
+ led_cfg[4] = 0;
+ led_cfg[5] = 0x2000;
+ }
+ rval = qla81xx_set_led_config(vha, led_cfg);
+ msleep(1000);
+ if (IS_QLA81XX(ha)) {
+ led_cfg[0] = 0x4000;
+ led_cfg[1] = 0x2000;
+ led_cfg[2] = 0;
+ } else {
+ led_cfg[0] = 0x4000;
+ led_cfg[1] = 0x2000;
+ led_cfg[2] = 0x4000;
+ led_cfg[3] = 0x4000;
+ led_cfg[4] = 0;
+ led_cfg[5] = 0x2000;
+ }
+ rval = qla81xx_set_led_config(vha, led_cfg);
+ }
+ /* On exit, restore original (presumes no status change) */
+ qla81xx_set_led_config(vha, orig_led_cfg);
+ }
+}
+
+int
+qla24xx_beacon_on(struct scsi_qla_host *vha)
+{
+ uint32_t gpio_data;
+ unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+ if (IS_QLA82XX(ha))
+ return QLA_SUCCESS;
+
+ if (IS_QLA8031(ha) || IS_QLA81XX(ha))
+ goto skip_gpio; /* let blink handle it */
+
+ if (ha->beacon_blink_led == 0) {
+ /* Enable firmware for update */
+ ha->fw_options[1] |= ADD_FO1_DISABLE_GPIO_LED_CTRL;
+
+ if (qla2x00_set_fw_options(vha, ha->fw_options) != QLA_SUCCESS)
+ return QLA_FUNCTION_FAILED;
+
+ if (qla2x00_get_fw_options(vha, ha->fw_options) !=
+ QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x7009,
+ "Unable to update fw options (beacon on).\n");
+ return QLA_FUNCTION_FAILED;
+ }
+
+ if (IS_QLA2031(ha))
+ goto skip_gpio;
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ gpio_data = RD_REG_DWORD(&reg->gpiod);
+
+ /* Enable the gpio_data reg for update. */
+ gpio_data |= GPDX_LED_UPDATE_MASK;
+ WRT_REG_DWORD(&reg->gpiod, gpio_data);
+ RD_REG_DWORD(&reg->gpiod);
+
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+ }
+
+ /* So all colors blink together. */
+ ha->beacon_color_state = 0;
+
+skip_gpio:
+ /* Let the per HBA timer kick off the blinking process. */
+ ha->beacon_blink_led = 1;
+
+ return QLA_SUCCESS;
+}
+
+int
+qla24xx_beacon_off(struct scsi_qla_host *vha)
+{
+ uint32_t gpio_data;
+ unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+ if (IS_QLA82XX(ha))
+ return QLA_SUCCESS;
+
+ ha->beacon_blink_led = 0;
+
+ if (IS_QLA2031(ha))
+ goto set_fw_options;
+
+ if (IS_QLA8031(ha) || IS_QLA81XX(ha))
+ return QLA_SUCCESS;
+
+ ha->beacon_color_state = QLA_LED_ALL_ON;
+
+ ha->isp_ops->beacon_blink(vha); /* Will flip to all off. */
+
+ /* Give control back to firmware. */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ gpio_data = RD_REG_DWORD(&reg->gpiod);
+
+ /* Disable the gpio_data reg for update. */
+ gpio_data &= ~GPDX_LED_UPDATE_MASK;
+ WRT_REG_DWORD(&reg->gpiod, gpio_data);
+ RD_REG_DWORD(&reg->gpiod);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+
+set_fw_options:
+ ha->fw_options[1] &= ~ADD_FO1_DISABLE_GPIO_LED_CTRL;
+
+ if (qla2x00_set_fw_options(vha, ha->fw_options) != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x704d,
+ "Unable to update fw options (beacon on).\n");
+ return QLA_FUNCTION_FAILED;
+ }
+
+ if (qla2x00_get_fw_options(vha, ha->fw_options) != QLA_SUCCESS) {
+ ql_log(ql_log_warn, vha, 0x704e,
+ "Unable to update fw options (beacon on).\n");
+ return QLA_FUNCTION_FAILED;
+ }
+
+ return QLA_SUCCESS;
+}
+
+
+/*
+ * Flash support routines
+ */
+
+/**
+ * qla2x00_flash_enable() - Setup flash for reading and writing.
+ * @ha: HA context
+ */
+static void
+qla2x00_flash_enable(struct qla_hw_data *ha)
+{
+ uint16_t data;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ data = RD_REG_WORD(&reg->ctrl_status);
+ data |= CSR_FLASH_ENABLE;
+ WRT_REG_WORD(&reg->ctrl_status, data);
+ RD_REG_WORD(&reg->ctrl_status); /* PCI Posting. */
+}
+
+/**
+ * qla2x00_flash_disable() - Disable flash and allow RISC to run.
+ * @ha: HA context
+ */
+static void
+qla2x00_flash_disable(struct qla_hw_data *ha)
+{
+ uint16_t data;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ data = RD_REG_WORD(&reg->ctrl_status);
+ data &= ~(CSR_FLASH_ENABLE);
+ WRT_REG_WORD(&reg->ctrl_status, data);
+ RD_REG_WORD(&reg->ctrl_status); /* PCI Posting. */
+}
+
+/**
+ * qla2x00_read_flash_byte() - Reads a byte from flash
+ * @ha: HA context
+ * @addr: Address in flash to read
+ *
+ * A word is read from the chip, but, only the lower byte is valid.
+ *
+ * Returns the byte read from flash @addr.
+ */
+static uint8_t
+qla2x00_read_flash_byte(struct qla_hw_data *ha, uint32_t addr)
+{
+ uint16_t data;
+ uint16_t bank_select;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ bank_select = RD_REG_WORD(&reg->ctrl_status);
+
+ if (IS_QLA2322(ha) || IS_QLA6322(ha)) {
+ /* Specify 64K address range: */
+ /* clear out Module Select and Flash Address bits [19:16]. */
+ bank_select &= ~0xf8;
+ bank_select |= addr >> 12 & 0xf0;
+ bank_select |= CSR_FLASH_64K_BANK;
+ WRT_REG_WORD(&reg->ctrl_status, bank_select);
+ RD_REG_WORD(&reg->ctrl_status); /* PCI Posting. */
+
+ WRT_REG_WORD(&reg->flash_address, (uint16_t)addr);
+ data = RD_REG_WORD(&reg->flash_data);
+
+ return (uint8_t)data;
+ }
+
+ /* Setup bit 16 of flash address. */
+ if ((addr & BIT_16) && ((bank_select & CSR_FLASH_64K_BANK) == 0)) {
+ bank_select |= CSR_FLASH_64K_BANK;
+ WRT_REG_WORD(&reg->ctrl_status, bank_select);
+ RD_REG_WORD(&reg->ctrl_status); /* PCI Posting. */
+ } else if (((addr & BIT_16) == 0) &&
+ (bank_select & CSR_FLASH_64K_BANK)) {
+ bank_select &= ~(CSR_FLASH_64K_BANK);
+ WRT_REG_WORD(&reg->ctrl_status, bank_select);
+ RD_REG_WORD(&reg->ctrl_status); /* PCI Posting. */
+ }
+
+ /* Always perform IO mapped accesses to the FLASH registers. */
+ if (ha->pio_address) {
+ uint16_t data2;
+
+ WRT_REG_WORD_PIO(PIO_REG(ha, flash_address), (uint16_t)addr);
+ do {
+ data = RD_REG_WORD_PIO(PIO_REG(ha, flash_data));
+ barrier();
+ cpu_relax();
+ data2 = RD_REG_WORD_PIO(PIO_REG(ha, flash_data));
+ } while (data != data2);
+ } else {
+ WRT_REG_WORD(&reg->flash_address, (uint16_t)addr);
+ data = qla2x00_debounce_register(&reg->flash_data);
+ }
+
+ return (uint8_t)data;
+}
+
+/**
+ * qla2x00_write_flash_byte() - Write a byte to flash
+ * @ha: HA context
+ * @addr: Address in flash to write
+ * @data: Data to write
+ */
+static void
+qla2x00_write_flash_byte(struct qla_hw_data *ha, uint32_t addr, uint8_t data)
+{
+ uint16_t bank_select;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ bank_select = RD_REG_WORD(&reg->ctrl_status);
+ if (IS_QLA2322(ha) || IS_QLA6322(ha)) {
+ /* Specify 64K address range: */
+ /* clear out Module Select and Flash Address bits [19:16]. */
+ bank_select &= ~0xf8;
+ bank_select |= addr >> 12 & 0xf0;
+ bank_select |= CSR_FLASH_64K_BANK;
+ WRT_REG_WORD(&reg->ctrl_status, bank_select);
+ RD_REG_WORD(&reg->ctrl_status); /* PCI Posting. */
+
+ WRT_REG_WORD(&reg->flash_address, (uint16_t)addr);
+ RD_REG_WORD(&reg->ctrl_status); /* PCI Posting. */
+ WRT_REG_WORD(&reg->flash_data, (uint16_t)data);
+ RD_REG_WORD(&reg->ctrl_status); /* PCI Posting. */
+
+ return;
+ }
+
+ /* Setup bit 16 of flash address. */
+ if ((addr & BIT_16) && ((bank_select & CSR_FLASH_64K_BANK) == 0)) {
+ bank_select |= CSR_FLASH_64K_BANK;
+ WRT_REG_WORD(&reg->ctrl_status, bank_select);
+ RD_REG_WORD(&reg->ctrl_status); /* PCI Posting. */
+ } else if (((addr & BIT_16) == 0) &&
+ (bank_select & CSR_FLASH_64K_BANK)) {
+ bank_select &= ~(CSR_FLASH_64K_BANK);
+ WRT_REG_WORD(&reg->ctrl_status, bank_select);
+ RD_REG_WORD(&reg->ctrl_status); /* PCI Posting. */
+ }
+
+ /* Always perform IO mapped accesses to the FLASH registers. */
+ if (ha->pio_address) {
+ WRT_REG_WORD_PIO(PIO_REG(ha, flash_address), (uint16_t)addr);
+ WRT_REG_WORD_PIO(PIO_REG(ha, flash_data), (uint16_t)data);
+ } else {
+ WRT_REG_WORD(&reg->flash_address, (uint16_t)addr);
+ RD_REG_WORD(&reg->ctrl_status); /* PCI Posting. */
+ WRT_REG_WORD(&reg->flash_data, (uint16_t)data);
+ RD_REG_WORD(&reg->ctrl_status); /* PCI Posting. */
+ }
+}
+
+/**
+ * qla2x00_poll_flash() - Polls flash for completion.
+ * @ha: HA context
+ * @addr: Address in flash to poll
+ * @poll_data: Data to be polled
+ * @man_id: Flash manufacturer ID
+ * @flash_id: Flash ID
+ *
+ * This function polls the device until bit 7 of what is read matches data
+ * bit 7 or until data bit 5 becomes a 1. If that hapens, the flash ROM timed
+ * out (a fatal error). The flash book recommeds reading bit 7 again after
+ * reading bit 5 as a 1.
+ *
+ * Returns 0 on success, else non-zero.
+ */
+static int
+qla2x00_poll_flash(struct qla_hw_data *ha, uint32_t addr, uint8_t poll_data,
+ uint8_t man_id, uint8_t flash_id)
+{
+ int status;
+ uint8_t flash_data;
+ uint32_t cnt;
+
+ status = 1;
+
+ /* Wait for 30 seconds for command to finish. */
+ poll_data &= BIT_7;
+ for (cnt = 3000000; cnt; cnt--) {
+ flash_data = qla2x00_read_flash_byte(ha, addr);
+ if ((flash_data & BIT_7) == poll_data) {
+ status = 0;
+ break;
+ }
+
+ if (man_id != 0x40 && man_id != 0xda) {
+ if ((flash_data & BIT_5) && cnt > 2)
+ cnt = 2;
+ }
+ udelay(10);
+ barrier();
+ cond_resched();
+ }
+ return status;
+}
+
+/**
+ * qla2x00_program_flash_address() - Programs a flash address
+ * @ha: HA context
+ * @addr: Address in flash to program
+ * @data: Data to be written in flash
+ * @man_id: Flash manufacturer ID
+ * @flash_id: Flash ID
+ *
+ * Returns 0 on success, else non-zero.
+ */
+static int
+qla2x00_program_flash_address(struct qla_hw_data *ha, uint32_t addr,
+ uint8_t data, uint8_t man_id, uint8_t flash_id)
+{
+ /* Write Program Command Sequence. */
+ if (IS_OEM_001(ha)) {
+ qla2x00_write_flash_byte(ha, 0xaaa, 0xaa);
+ qla2x00_write_flash_byte(ha, 0x555, 0x55);
+ qla2x00_write_flash_byte(ha, 0xaaa, 0xa0);
+ qla2x00_write_flash_byte(ha, addr, data);
+ } else {
+ if (man_id == 0xda && flash_id == 0xc1) {
+ qla2x00_write_flash_byte(ha, addr, data);
+ if (addr & 0x7e)
+ return 0;
+ } else {
+ qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
+ qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
+ qla2x00_write_flash_byte(ha, 0x5555, 0xa0);
+ qla2x00_write_flash_byte(ha, addr, data);
+ }
+ }
+
+ udelay(150);
+
+ /* Wait for write to complete. */
+ return qla2x00_poll_flash(ha, addr, data, man_id, flash_id);
+}
+
+/**
+ * qla2x00_erase_flash() - Erase the flash.
+ * @ha: HA context
+ * @man_id: Flash manufacturer ID
+ * @flash_id: Flash ID
+ *
+ * Returns 0 on success, else non-zero.
+ */
+static int
+qla2x00_erase_flash(struct qla_hw_data *ha, uint8_t man_id, uint8_t flash_id)
+{
+ /* Individual Sector Erase Command Sequence */
+ if (IS_OEM_001(ha)) {
+ qla2x00_write_flash_byte(ha, 0xaaa, 0xaa);
+ qla2x00_write_flash_byte(ha, 0x555, 0x55);
+ qla2x00_write_flash_byte(ha, 0xaaa, 0x80);
+ qla2x00_write_flash_byte(ha, 0xaaa, 0xaa);
+ qla2x00_write_flash_byte(ha, 0x555, 0x55);
+ qla2x00_write_flash_byte(ha, 0xaaa, 0x10);
+ } else {
+ qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
+ qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
+ qla2x00_write_flash_byte(ha, 0x5555, 0x80);
+ qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
+ qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
+ qla2x00_write_flash_byte(ha, 0x5555, 0x10);
+ }
+
+ udelay(150);
+
+ /* Wait for erase to complete. */
+ return qla2x00_poll_flash(ha, 0x00, 0x80, man_id, flash_id);
+}
+
+/**
+ * qla2x00_erase_flash_sector() - Erase a flash sector.
+ * @ha: HA context
+ * @addr: Flash sector to erase
+ * @sec_mask: Sector address mask
+ * @man_id: Flash manufacturer ID
+ * @flash_id: Flash ID
+ *
+ * Returns 0 on success, else non-zero.
+ */
+static int
+qla2x00_erase_flash_sector(struct qla_hw_data *ha, uint32_t addr,
+ uint32_t sec_mask, uint8_t man_id, uint8_t flash_id)
+{
+ /* Individual Sector Erase Command Sequence */
+ qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
+ qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
+ qla2x00_write_flash_byte(ha, 0x5555, 0x80);
+ qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
+ qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
+ if (man_id == 0x1f && flash_id == 0x13)
+ qla2x00_write_flash_byte(ha, addr & sec_mask, 0x10);
+ else
+ qla2x00_write_flash_byte(ha, addr & sec_mask, 0x30);
+
+ udelay(150);
+
+ /* Wait for erase to complete. */
+ return qla2x00_poll_flash(ha, addr, 0x80, man_id, flash_id);
+}
+
+/**
+ * qla2x00_get_flash_manufacturer() - Read manufacturer ID from flash chip.
+ * @man_id: Flash manufacturer ID
+ * @flash_id: Flash ID
+ */
+static void
+qla2x00_get_flash_manufacturer(struct qla_hw_data *ha, uint8_t *man_id,
+ uint8_t *flash_id)
+{
+ qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
+ qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
+ qla2x00_write_flash_byte(ha, 0x5555, 0x90);
+ *man_id = qla2x00_read_flash_byte(ha, 0x0000);
+ *flash_id = qla2x00_read_flash_byte(ha, 0x0001);
+ qla2x00_write_flash_byte(ha, 0x5555, 0xaa);
+ qla2x00_write_flash_byte(ha, 0x2aaa, 0x55);
+ qla2x00_write_flash_byte(ha, 0x5555, 0xf0);
+}
+
+static void
+qla2x00_read_flash_data(struct qla_hw_data *ha, uint8_t *tmp_buf,
+ uint32_t saddr, uint32_t length)
+{
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+ uint32_t midpoint, ilength;
+ uint8_t data;
+
+ midpoint = length / 2;
+
+ WRT_REG_WORD(&reg->nvram, 0);
+ RD_REG_WORD(&reg->nvram);
+ for (ilength = 0; ilength < length; saddr++, ilength++, tmp_buf++) {
+ if (ilength == midpoint) {
+ WRT_REG_WORD(&reg->nvram, NVR_SELECT);
+ RD_REG_WORD(&reg->nvram);
+ }
+ data = qla2x00_read_flash_byte(ha, saddr);
+ if (saddr % 100)
+ udelay(10);
+ *tmp_buf = data;
+ cond_resched();
+ }
+}
+
+static inline void
+qla2x00_suspend_hba(struct scsi_qla_host *vha)
+{
+ int cnt;
+ unsigned long flags;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ /* Suspend HBA. */
+ scsi_block_requests(vha->host);
+ ha->isp_ops->disable_intrs(ha);
+ set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
+
+ /* Pause RISC. */
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ WRT_REG_WORD(&reg->hccr, HCCR_PAUSE_RISC);
+ RD_REG_WORD(&reg->hccr);
+ if (IS_QLA2100(ha) || IS_QLA2200(ha) || IS_QLA2300(ha)) {
+ for (cnt = 0; cnt < 30000; cnt++) {
+ if ((RD_REG_WORD(&reg->hccr) & HCCR_RISC_PAUSE) != 0)
+ break;
+ udelay(100);
+ }
+ } else {
+ udelay(10);
+ }
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+static inline void
+qla2x00_resume_hba(struct scsi_qla_host *vha)
+{
+ struct qla_hw_data *ha = vha->hw;
+
+ /* Resume HBA. */
+ clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
+ set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags);
+ qla2xxx_wake_dpc(vha);
+ qla2x00_wait_for_chip_reset(vha);
+ scsi_unblock_requests(vha->host);
+}
+
+uint8_t *
+qla2x00_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf,
+ uint32_t offset, uint32_t length)
+{
+ uint32_t addr, midpoint;
+ uint8_t *data;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ /* Suspend HBA. */
+ qla2x00_suspend_hba(vha);
+
+ /* Go with read. */
+ midpoint = ha->optrom_size / 2;
+
+ qla2x00_flash_enable(ha);
+ WRT_REG_WORD(&reg->nvram, 0);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ for (addr = offset, data = buf; addr < length; addr++, data++) {
+ if (addr == midpoint) {
+ WRT_REG_WORD(&reg->nvram, NVR_SELECT);
+ RD_REG_WORD(&reg->nvram); /* PCI Posting. */
+ }
+
+ *data = qla2x00_read_flash_byte(ha, addr);
+ }
+ qla2x00_flash_disable(ha);
+
+ /* Resume HBA. */
+ qla2x00_resume_hba(vha);
+
+ return buf;
+}
+
+int
+qla2x00_write_optrom_data(struct scsi_qla_host *vha, uint8_t *buf,
+ uint32_t offset, uint32_t length)
+{
+
+ int rval;
+ uint8_t man_id, flash_id, sec_number, data;
+ uint16_t wd;
+ uint32_t addr, liter, sec_mask, rest_addr;
+ struct qla_hw_data *ha = vha->hw;
+ struct device_reg_2xxx __iomem *reg = &ha->iobase->isp;
+
+ /* Suspend HBA. */
+ qla2x00_suspend_hba(vha);
+
+ rval = QLA_SUCCESS;
+ sec_number = 0;
+
+ /* Reset ISP chip. */
+ WRT_REG_WORD(&reg->ctrl_status, CSR_ISP_SOFT_RESET);
+ pci_read_config_word(ha->pdev, PCI_COMMAND, &wd);
+
+ /* Go with write. */
+ qla2x00_flash_enable(ha);
+ do { /* Loop once to provide quick error exit */
+ /* Structure of flash memory based on manufacturer */
+ if (IS_OEM_001(ha)) {
+ /* OEM variant with special flash part. */
+ man_id = flash_id = 0;
+ rest_addr = 0xffff;
+ sec_mask = 0x10000;
+ goto update_flash;
+ }
+ qla2x00_get_flash_manufacturer(ha, &man_id, &flash_id);
+ switch (man_id) {
+ case 0x20: /* ST flash. */
+ if (flash_id == 0xd2 || flash_id == 0xe3) {
+ /*
+ * ST m29w008at part - 64kb sector size with
+ * 32kb,8kb,8kb,16kb sectors at memory address
+ * 0xf0000.
+ */
+ rest_addr = 0xffff;
+ sec_mask = 0x10000;
+ break;
+ }
+ /*
+ * ST m29w010b part - 16kb sector size
+ * Default to 16kb sectors
+ */
+ rest_addr = 0x3fff;
+ sec_mask = 0x1c000;
+ break;
+ case 0x40: /* Mostel flash. */
+ /* Mostel v29c51001 part - 512 byte sector size. */
+ rest_addr = 0x1ff;
+ sec_mask = 0x1fe00;
+ break;
+ case 0xbf: /* SST flash. */
+ /* SST39sf10 part - 4kb sector size. */
+ rest_addr = 0xfff;
+ sec_mask = 0x1f000;
+ break;
+ case 0xda: /* Winbond flash. */
+ /* Winbond W29EE011 part - 256 byte sector size. */
+ rest_addr = 0x7f;
+ sec_mask = 0x1ff80;
+ break;
+ case 0xc2: /* Macronix flash. */
+ /* 64k sector size. */
+ if (flash_id == 0x38 || flash_id == 0x4f) {
+ rest_addr = 0xffff;
+ sec_mask = 0x10000;
+ break;
+ }
+ /* Fall through... */
+
+ case 0x1f: /* Atmel flash. */
+ /* 512k sector size. */
+ if (flash_id == 0x13) {
+ rest_addr = 0x7fffffff;
+ sec_mask = 0x80000000;
+ break;
+ }
+ /* Fall through... */
+
+ case 0x01: /* AMD flash. */
+ if (flash_id == 0x38 || flash_id == 0x40 ||
+ flash_id == 0x4f) {
+ /* Am29LV081 part - 64kb sector size. */
+ /* Am29LV002BT part - 64kb sector size. */
+ rest_addr = 0xffff;
+ sec_mask = 0x10000;
+ break;
+ } else if (flash_id == 0x3e) {
+ /*
+ * Am29LV008b part - 64kb sector size with
+ * 32kb,8kb,8kb,16kb sector at memory address
+ * h0xf0000.
+ */
+ rest_addr = 0xffff;
+ sec_mask = 0x10000;
+ break;
+ } else if (flash_id == 0x20 || flash_id == 0x6e) {
+ /*
+ * Am29LV010 part or AM29f010 - 16kb sector
+ * size.
+ */
+ rest_addr = 0x3fff;
+ sec_mask = 0x1c000;
+ break;
+ } else if (flash_id == 0x6d) {
+ /* Am29LV001 part - 8kb sector size. */
+ rest_addr = 0x1fff;
+ sec_mask = 0x1e000;
+ break;
+ }
+ default:
+ /* Default to 16 kb sector size. */
+ rest_addr = 0x3fff;
+ sec_mask = 0x1c000;
+ break;
+ }
+
+update_flash:
+ if (IS_QLA2322(ha) || IS_QLA6322(ha)) {
+ if (qla2x00_erase_flash(ha, man_id, flash_id)) {
+ rval = QLA_FUNCTION_FAILED;
+ break;
+ }
+ }
+
+ for (addr = offset, liter = 0; liter < length; liter++,
+ addr++) {
+ data = buf[liter];
+ /* Are we at the beginning of a sector? */
+ if ((addr & rest_addr) == 0) {
+ if (IS_QLA2322(ha) || IS_QLA6322(ha)) {
+ if (addr >= 0x10000UL) {
+ if (((addr >> 12) & 0xf0) &&
+ ((man_id == 0x01 &&
+ flash_id == 0x3e) ||
+ (man_id == 0x20 &&
+ flash_id == 0xd2))) {
+ sec_number++;
+ if (sec_number == 1) {
+ rest_addr =
+ 0x7fff;
+ sec_mask =
+ 0x18000;
+ } else if (
+ sec_number == 2 ||
+ sec_number == 3) {
+ rest_addr =
+ 0x1fff;
+ sec_mask =
+ 0x1e000;
+ } else if (
+ sec_number == 4) {
+ rest_addr =
+ 0x3fff;
+ sec_mask =
+ 0x1c000;
+ }
+ }
+ }
+ } else if (addr == ha->optrom_size / 2) {
+ WRT_REG_WORD(&reg->nvram, NVR_SELECT);
+ RD_REG_WORD(&reg->nvram);
+ }
+
+ if (flash_id == 0xda && man_id == 0xc1) {
+ qla2x00_write_flash_byte(ha, 0x5555,
+ 0xaa);
+ qla2x00_write_flash_byte(ha, 0x2aaa,
+ 0x55);
+ qla2x00_write_flash_byte(ha, 0x5555,
+ 0xa0);
+ } else if (!IS_QLA2322(ha) && !IS_QLA6322(ha)) {
+ /* Then erase it */
+ if (qla2x00_erase_flash_sector(ha,
+ addr, sec_mask, man_id,
+ flash_id)) {
+ rval = QLA_FUNCTION_FAILED;
+ break;
+ }
+ if (man_id == 0x01 && flash_id == 0x6d)
+ sec_number++;
+ }
+ }
+
+ if (man_id == 0x01 && flash_id == 0x6d) {
+ if (sec_number == 1 &&
+ addr == (rest_addr - 1)) {
+ rest_addr = 0x0fff;
+ sec_mask = 0x1f000;
+ } else if (sec_number == 3 && (addr & 0x7ffe)) {
+ rest_addr = 0x3fff;
+ sec_mask = 0x1c000;
+ }
+ }
+
+ if (qla2x00_program_flash_address(ha, addr, data,
+ man_id, flash_id)) {
+ rval = QLA_FUNCTION_FAILED;
+ break;
+ }
+ cond_resched();
+ }
+ } while (0);
+ qla2x00_flash_disable(ha);
+
+ /* Resume HBA. */
+ qla2x00_resume_hba(vha);
+
+ return rval;
+}
+
+uint8_t *
+qla24xx_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf,
+ uint32_t offset, uint32_t length)
+{
+ struct qla_hw_data *ha = vha->hw;
+
+ /* Suspend HBA. */
+ scsi_block_requests(vha->host);
+ set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
+
+ /* Go with read. */
+ qla24xx_read_flash_data(vha, (uint32_t *)buf, offset >> 2, length >> 2);
+
+ /* Resume HBA. */
+ clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
+ scsi_unblock_requests(vha->host);
+
+ return buf;
+}
+
+int
+qla24xx_write_optrom_data(struct scsi_qla_host *vha, uint8_t *buf,
+ uint32_t offset, uint32_t length)
+{
+ int rval;
+ struct qla_hw_data *ha = vha->hw;
+
+ /* Suspend HBA. */
+ scsi_block_requests(vha->host);
+ set_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
+
+ /* Go with write. */
+ rval = qla24xx_write_flash_data(vha, (uint32_t *)buf, offset >> 2,
+ length >> 2);
+
+ clear_bit(MBX_UPDATE_FLASH_ACTIVE, &ha->mbx_cmd_flags);
+ scsi_unblock_requests(vha->host);
+
+ return rval;
+}
+
+uint8_t *
+qla25xx_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf,
+ uint32_t offset, uint32_t length)
+{
+ int rval;
+ dma_addr_t optrom_dma;
+ void *optrom;
+ uint8_t *pbuf;
+ uint32_t faddr, left, burst;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (IS_QLA25XX(ha) || IS_QLA81XX(ha))
+ goto try_fast;
+ if (offset & 0xfff)
+ goto slow_read;
+ if (length < OPTROM_BURST_SIZE)
+ goto slow_read;
+
+try_fast:
+ optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
+ &optrom_dma, GFP_KERNEL);
+ if (!optrom) {
+ ql_log(ql_log_warn, vha, 0x00cc,
+ "Unable to allocate memory for optrom burst read (%x KB).\n",
+ OPTROM_BURST_SIZE / 1024);
+ goto slow_read;
+ }
+
+ pbuf = buf;
+ faddr = offset >> 2;
+ left = length >> 2;
+ burst = OPTROM_BURST_DWORDS;
+ while (left != 0) {
+ if (burst > left)
+ burst = left;
+
+ rval = qla2x00_dump_ram(vha, optrom_dma,
+ flash_data_addr(ha, faddr), burst);
+ if (rval) {
+ ql_log(ql_log_warn, vha, 0x00f5,
+ "Unable to burst-read optrom segment (%x/%x/%llx).\n",
+ rval, flash_data_addr(ha, faddr),
+ (unsigned long long)optrom_dma);
+ ql_log(ql_log_warn, vha, 0x00f6,
+ "Reverting to slow-read.\n");
+
+ dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE,
+ optrom, optrom_dma);
+ goto slow_read;
+ }
+
+ memcpy(pbuf, optrom, burst * 4);
+
+ left -= burst;
+ faddr += burst;
+ pbuf += burst * 4;
+ }
+
+ dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, optrom,
+ optrom_dma);
+
+ return buf;
+
+slow_read:
+ return qla24xx_read_optrom_data(vha, buf, offset, length);
+}
+
+/**
+ * qla2x00_get_fcode_version() - Determine an FCODE image's version.
+ * @ha: HA context
+ * @pcids: Pointer to the FCODE PCI data structure
+ *
+ * The process of retrieving the FCODE version information is at best
+ * described as interesting.
+ *
+ * Within the first 100h bytes of the image an ASCII string is present
+ * which contains several pieces of information including the FCODE
+ * version. Unfortunately it seems the only reliable way to retrieve
+ * the version is by scanning for another sentinel within the string,
+ * the FCODE build date:
+ *
+ * ... 2.00.02 10/17/02 ...
+ *
+ * Returns QLA_SUCCESS on successful retrieval of version.
+ */
+static void
+qla2x00_get_fcode_version(struct qla_hw_data *ha, uint32_t pcids)
+{
+ int ret = QLA_FUNCTION_FAILED;
+ uint32_t istart, iend, iter, vend;
+ uint8_t do_next, rbyte, *vbyte;
+
+ memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision));
+
+ /* Skip the PCI data structure. */
+ istart = pcids +
+ ((qla2x00_read_flash_byte(ha, pcids + 0x0B) << 8) |
+ qla2x00_read_flash_byte(ha, pcids + 0x0A));
+ iend = istart + 0x100;
+ do {
+ /* Scan for the sentinel date string...eeewww. */
+ do_next = 0;
+ iter = istart;
+ while ((iter < iend) && !do_next) {
+ iter++;
+ if (qla2x00_read_flash_byte(ha, iter) == '/') {
+ if (qla2x00_read_flash_byte(ha, iter + 2) ==
+ '/')
+ do_next++;
+ else if (qla2x00_read_flash_byte(ha,
+ iter + 3) == '/')
+ do_next++;
+ }
+ }
+ if (!do_next)
+ break;
+
+ /* Backtrack to previous ' ' (space). */
+ do_next = 0;
+ while ((iter > istart) && !do_next) {
+ iter--;
+ if (qla2x00_read_flash_byte(ha, iter) == ' ')
+ do_next++;
+ }
+ if (!do_next)
+ break;
+
+ /*
+ * Mark end of version tag, and find previous ' ' (space) or
+ * string length (recent FCODE images -- major hack ahead!!!).
+ */
+ vend = iter - 1;
+ do_next = 0;
+ while ((iter > istart) && !do_next) {
+ iter--;
+ rbyte = qla2x00_read_flash_byte(ha, iter);
+ if (rbyte == ' ' || rbyte == 0xd || rbyte == 0x10)
+ do_next++;
+ }
+ if (!do_next)
+ break;
+
+ /* Mark beginning of version tag, and copy data. */
+ iter++;
+ if ((vend - iter) &&
+ ((vend - iter) < sizeof(ha->fcode_revision))) {
+ vbyte = ha->fcode_revision;
+ while (iter <= vend) {
+ *vbyte++ = qla2x00_read_flash_byte(ha, iter);
+ iter++;
+ }
+ ret = QLA_SUCCESS;
+ }
+ } while (0);
+
+ if (ret != QLA_SUCCESS)
+ memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision));
+}
+
+int
+qla2x00_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
+{
+ int ret = QLA_SUCCESS;
+ uint8_t code_type, last_image;
+ uint32_t pcihdr, pcids;
+ uint8_t *dbyte;
+ uint16_t *dcode;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!ha->pio_address || !mbuf)
+ return QLA_FUNCTION_FAILED;
+
+ memset(ha->bios_revision, 0, sizeof(ha->bios_revision));
+ memset(ha->efi_revision, 0, sizeof(ha->efi_revision));
+ memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision));
+ memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
+
+ qla2x00_flash_enable(ha);
+
+ /* Begin with first PCI expansion ROM header. */
+ pcihdr = 0;
+ last_image = 1;
+ do {
+ /* Verify PCI expansion ROM header. */
+ if (qla2x00_read_flash_byte(ha, pcihdr) != 0x55 ||
+ qla2x00_read_flash_byte(ha, pcihdr + 0x01) != 0xaa) {
+ /* No signature */
+ ql_log(ql_log_fatal, vha, 0x0050,
+ "No matching ROM signature.\n");
+ ret = QLA_FUNCTION_FAILED;
+ break;
+ }
+
+ /* Locate PCI data structure. */
+ pcids = pcihdr +
+ ((qla2x00_read_flash_byte(ha, pcihdr + 0x19) << 8) |
+ qla2x00_read_flash_byte(ha, pcihdr + 0x18));
+
+ /* Validate signature of PCI data structure. */
+ if (qla2x00_read_flash_byte(ha, pcids) != 'P' ||
+ qla2x00_read_flash_byte(ha, pcids + 0x1) != 'C' ||
+ qla2x00_read_flash_byte(ha, pcids + 0x2) != 'I' ||
+ qla2x00_read_flash_byte(ha, pcids + 0x3) != 'R') {
+ /* Incorrect header. */
+ ql_log(ql_log_fatal, vha, 0x0051,
+ "PCI data struct not found pcir_adr=%x.\n", pcids);
+ ret = QLA_FUNCTION_FAILED;
+ break;
+ }
+
+ /* Read version */
+ code_type = qla2x00_read_flash_byte(ha, pcids + 0x14);
+ switch (code_type) {
+ case ROM_CODE_TYPE_BIOS:
+ /* Intel x86, PC-AT compatible. */
+ ha->bios_revision[0] =
+ qla2x00_read_flash_byte(ha, pcids + 0x12);
+ ha->bios_revision[1] =
+ qla2x00_read_flash_byte(ha, pcids + 0x13);
+ ql_dbg(ql_dbg_init, vha, 0x0052,
+ "Read BIOS %d.%d.\n",
+ ha->bios_revision[1], ha->bios_revision[0]);
+ break;
+ case ROM_CODE_TYPE_FCODE:
+ /* Open Firmware standard for PCI (FCode). */
+ /* Eeeewww... */
+ qla2x00_get_fcode_version(ha, pcids);
+ break;
+ case ROM_CODE_TYPE_EFI:
+ /* Extensible Firmware Interface (EFI). */
+ ha->efi_revision[0] =
+ qla2x00_read_flash_byte(ha, pcids + 0x12);
+ ha->efi_revision[1] =
+ qla2x00_read_flash_byte(ha, pcids + 0x13);
+ ql_dbg(ql_dbg_init, vha, 0x0053,
+ "Read EFI %d.%d.\n",
+ ha->efi_revision[1], ha->efi_revision[0]);
+ break;
+ default:
+ ql_log(ql_log_warn, vha, 0x0054,
+ "Unrecognized code type %x at pcids %x.\n",
+ code_type, pcids);
+ break;
+ }
+
+ last_image = qla2x00_read_flash_byte(ha, pcids + 0x15) & BIT_7;
+
+ /* Locate next PCI expansion ROM. */
+ pcihdr += ((qla2x00_read_flash_byte(ha, pcids + 0x11) << 8) |
+ qla2x00_read_flash_byte(ha, pcids + 0x10)) * 512;
+ } while (!last_image);
+
+ if (IS_QLA2322(ha)) {
+ /* Read firmware image information. */
+ memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
+ dbyte = mbuf;
+ memset(dbyte, 0, 8);
+ dcode = (uint16_t *)dbyte;
+
+ qla2x00_read_flash_data(ha, dbyte, ha->flt_region_fw * 4 + 10,
+ 8);
+ ql_dbg(ql_dbg_init + ql_dbg_buffer, vha, 0x010a,
+ "Dumping fw "
+ "ver from flash:.\n");
+ ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x010b,
+ (uint8_t *)dbyte, 8);
+
+ if ((dcode[0] == 0xffff && dcode[1] == 0xffff &&
+ dcode[2] == 0xffff && dcode[3] == 0xffff) ||
+ (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 &&
+ dcode[3] == 0)) {
+ ql_log(ql_log_warn, vha, 0x0057,
+ "Unrecognized fw revision at %x.\n",
+ ha->flt_region_fw * 4);
+ } else {
+ /* values are in big endian */
+ ha->fw_revision[0] = dbyte[0] << 16 | dbyte[1];
+ ha->fw_revision[1] = dbyte[2] << 16 | dbyte[3];
+ ha->fw_revision[2] = dbyte[4] << 16 | dbyte[5];
+ ql_dbg(ql_dbg_init, vha, 0x0058,
+ "FW Version: "
+ "%d.%d.%d.\n", ha->fw_revision[0],
+ ha->fw_revision[1], ha->fw_revision[2]);
+ }
+ }
+
+ qla2x00_flash_disable(ha);
+
+ return ret;
+}
+
+int
+qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf)
+{
+ int ret = QLA_SUCCESS;
+ uint32_t pcihdr, pcids;
+ uint32_t *dcode;
+ uint8_t *bcode;
+ uint8_t code_type, last_image;
+ int i;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (IS_QLA82XX(ha))
+ return ret;
+
+ if (!mbuf)
+ return QLA_FUNCTION_FAILED;
+
+ memset(ha->bios_revision, 0, sizeof(ha->bios_revision));
+ memset(ha->efi_revision, 0, sizeof(ha->efi_revision));
+ memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision));
+ memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
+
+ dcode = mbuf;
+
+ /* Begin with first PCI expansion ROM header. */
+ pcihdr = ha->flt_region_boot << 2;
+ last_image = 1;
+ do {
+ /* Verify PCI expansion ROM header. */
+ qla24xx_read_flash_data(vha, dcode, pcihdr >> 2, 0x20);
+ bcode = mbuf + (pcihdr % 4);
+ if (bcode[0x0] != 0x55 || bcode[0x1] != 0xaa) {
+ /* No signature */
+ ql_log(ql_log_fatal, vha, 0x0059,
+ "No matching ROM signature.\n");
+ ret = QLA_FUNCTION_FAILED;
+ break;
+ }
+
+ /* Locate PCI data structure. */
+ pcids = pcihdr + ((bcode[0x19] << 8) | bcode[0x18]);
+
+ qla24xx_read_flash_data(vha, dcode, pcids >> 2, 0x20);
+ bcode = mbuf + (pcihdr % 4);
+
+ /* Validate signature of PCI data structure. */
+ if (bcode[0x0] != 'P' || bcode[0x1] != 'C' ||
+ bcode[0x2] != 'I' || bcode[0x3] != 'R') {
+ /* Incorrect header. */
+ ql_log(ql_log_fatal, vha, 0x005a,
+ "PCI data struct not found pcir_adr=%x.\n", pcids);
+ ret = QLA_FUNCTION_FAILED;
+ break;
+ }
+
+ /* Read version */
+ code_type = bcode[0x14];
+ switch (code_type) {
+ case ROM_CODE_TYPE_BIOS:
+ /* Intel x86, PC-AT compatible. */
+ ha->bios_revision[0] = bcode[0x12];
+ ha->bios_revision[1] = bcode[0x13];
+ ql_dbg(ql_dbg_init, vha, 0x005b,
+ "Read BIOS %d.%d.\n",
+ ha->bios_revision[1], ha->bios_revision[0]);
+ break;
+ case ROM_CODE_TYPE_FCODE:
+ /* Open Firmware standard for PCI (FCode). */
+ ha->fcode_revision[0] = bcode[0x12];
+ ha->fcode_revision[1] = bcode[0x13];
+ ql_dbg(ql_dbg_init, vha, 0x005c,
+ "Read FCODE %d.%d.\n",
+ ha->fcode_revision[1], ha->fcode_revision[0]);
+ break;
+ case ROM_CODE_TYPE_EFI:
+ /* Extensible Firmware Interface (EFI). */
+ ha->efi_revision[0] = bcode[0x12];
+ ha->efi_revision[1] = bcode[0x13];
+ ql_dbg(ql_dbg_init, vha, 0x005d,
+ "Read EFI %d.%d.\n",
+ ha->efi_revision[1], ha->efi_revision[0]);
+ break;
+ default:
+ ql_log(ql_log_warn, vha, 0x005e,
+ "Unrecognized code type %x at pcids %x.\n",
+ code_type, pcids);
+ break;
+ }
+
+ last_image = bcode[0x15] & BIT_7;
+
+ /* Locate next PCI expansion ROM. */
+ pcihdr += ((bcode[0x11] << 8) | bcode[0x10]) * 512;
+ } while (!last_image);
+
+ /* Read firmware image information. */
+ memset(ha->fw_revision, 0, sizeof(ha->fw_revision));
+ dcode = mbuf;
+
+ qla24xx_read_flash_data(vha, dcode, ha->flt_region_fw + 4, 4);
+ for (i = 0; i < 4; i++)
+ dcode[i] = be32_to_cpu(dcode[i]);
+
+ if ((dcode[0] == 0xffffffff && dcode[1] == 0xffffffff &&
+ dcode[2] == 0xffffffff && dcode[3] == 0xffffffff) ||
+ (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 &&
+ dcode[3] == 0)) {
+ ql_log(ql_log_warn, vha, 0x005f,
+ "Unrecognized fw revision at %x.\n",
+ ha->flt_region_fw * 4);
+ } else {
+ ha->fw_revision[0] = dcode[0];
+ ha->fw_revision[1] = dcode[1];
+ ha->fw_revision[2] = dcode[2];
+ ha->fw_revision[3] = dcode[3];
+ ql_dbg(ql_dbg_init, vha, 0x0060,
+ "Firmware revision %d.%d.%d.%d.\n",
+ ha->fw_revision[0], ha->fw_revision[1],
+ ha->fw_revision[2], ha->fw_revision[3]);
+ }
+
+ /* Check for golden firmware and get version if available */
+ if (!IS_QLA81XX(ha)) {
+ /* Golden firmware is not present in non 81XX adapters */
+ return ret;
+ }
+
+ memset(ha->gold_fw_version, 0, sizeof(ha->gold_fw_version));
+ dcode = mbuf;
+ ha->isp_ops->read_optrom(vha, (uint8_t *)dcode,
+ ha->flt_region_gold_fw << 2, 32);
+
+ if (dcode[4] == 0xFFFFFFFF && dcode[5] == 0xFFFFFFFF &&
+ dcode[6] == 0xFFFFFFFF && dcode[7] == 0xFFFFFFFF) {
+ ql_log(ql_log_warn, vha, 0x0056,
+ "Unrecognized golden fw at 0x%x.\n",
+ ha->flt_region_gold_fw * 4);
+ return ret;
+ }
+
+ for (i = 4; i < 8; i++)
+ ha->gold_fw_version[i-4] = be32_to_cpu(dcode[i]);
+
+ return ret;
+}
+
+static int
+qla2xxx_is_vpd_valid(uint8_t *pos, uint8_t *end)
+{
+ if (pos >= end || *pos != 0x82)
+ return 0;
+
+ pos += 3 + pos[1];
+ if (pos >= end || *pos != 0x90)
+ return 0;
+
+ pos += 3 + pos[1];
+ if (pos >= end || *pos != 0x78)
+ return 0;
+
+ return 1;
+}
+
+int
+qla2xxx_get_vpd_field(scsi_qla_host_t *vha, char *key, char *str, size_t size)
+{
+ struct qla_hw_data *ha = vha->hw;
+ uint8_t *pos = ha->vpd;
+ uint8_t *end = pos + ha->vpd_size;
+ int len = 0;
+
+ if (!IS_FWI2_CAPABLE(ha) || !qla2xxx_is_vpd_valid(pos, end))
+ return 0;
+
+ while (pos < end && *pos != 0x78) {
+ len = (*pos == 0x82) ? pos[1] : pos[2];
+
+ if (!strncmp(pos, key, strlen(key)))
+ break;
+
+ if (*pos != 0x90 && *pos != 0x91)
+ pos += len;
+
+ pos += 3;
+ }
+
+ if (pos < end - len && *pos != 0x78)
+ return snprintf(str, size, "%.*s", len, pos + 3);
+
+ return 0;
+}
+
+int
+qla24xx_read_fcp_prio_cfg(scsi_qla_host_t *vha)
+{
+ int len, max_len;
+ uint32_t fcp_prio_addr;
+ struct qla_hw_data *ha = vha->hw;
+
+ if (!ha->fcp_prio_cfg) {
+ ha->fcp_prio_cfg = vmalloc(FCP_PRIO_CFG_SIZE);
+ if (!ha->fcp_prio_cfg) {
+ ql_log(ql_log_warn, vha, 0x00d5,
+ "Unable to allocate memory for fcp priorty data (%x).\n",
+ FCP_PRIO_CFG_SIZE);
+ return QLA_FUNCTION_FAILED;
+ }
+ }
+ memset(ha->fcp_prio_cfg, 0, FCP_PRIO_CFG_SIZE);
+
+ fcp_prio_addr = ha->flt_region_fcp_prio;
+
+ /* first read the fcp priority data header from flash */
+ ha->isp_ops->read_optrom(vha, (uint8_t *)ha->fcp_prio_cfg,
+ fcp_prio_addr << 2, FCP_PRIO_CFG_HDR_SIZE);
+
+ if (!qla24xx_fcp_prio_cfg_valid(vha, ha->fcp_prio_cfg, 0))
+ goto fail;
+
+ /* read remaining FCP CMD config data from flash */
+ fcp_prio_addr += (FCP_PRIO_CFG_HDR_SIZE >> 2);
+ len = ha->fcp_prio_cfg->num_entries * FCP_PRIO_CFG_ENTRY_SIZE;
+ max_len = FCP_PRIO_CFG_SIZE - FCP_PRIO_CFG_HDR_SIZE;
+
+ ha->isp_ops->read_optrom(vha, (uint8_t *)&ha->fcp_prio_cfg->entry[0],
+ fcp_prio_addr << 2, (len < max_len ? len : max_len));
+
+ /* revalidate the entire FCP priority config data, including entries */
+ if (!qla24xx_fcp_prio_cfg_valid(vha, ha->fcp_prio_cfg, 1))
+ goto fail;
+
+ ha->flags.fcp_prio_enabled = 1;
+ return QLA_SUCCESS;
+fail:
+ vfree(ha->fcp_prio_cfg);
+ ha->fcp_prio_cfg = NULL;
+ return QLA_FUNCTION_FAILED;
+}
diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h
new file mode 100644
index 00000000..f5fdb16b
--- /dev/null
+++ b/drivers/scsi/qla2xxx/qla_version.h
@@ -0,0 +1,15 @@
+/*
+ * QLogic Fibre Channel HBA Driver
+ * Copyright (c) 2003-2011 QLogic Corporation
+ *
+ * See LICENSE.qla2xxx for copyright and licensing details.
+ */
+/*
+ * Driver version
+ */
+#define QLA2XXX_VERSION "8.04.00.03-k"
+
+#define QLA_DRIVER_MAJOR_VER 8
+#define QLA_DRIVER_MINOR_VER 4
+#define QLA_DRIVER_PATCH_VER 0
+#define QLA_DRIVER_BETA_VER 3