diff options
Diffstat (limited to 'drivers/scsi/qla2xxx')
34 files changed, 34370 insertions, 6249 deletions
diff --git a/drivers/scsi/qla2xxx/Kconfig b/drivers/scsi/qla2xxx/Kconfig index 6208d562890..23d607218ae 100644 --- a/drivers/scsi/qla2xxx/Kconfig +++ b/drivers/scsi/qla2xxx/Kconfig @@ -24,4 +24,15 @@ config SCSI_QLA_FC Firmware images can be retrieved from: - ftp://ftp.qlogic.com/outgoing/linux/firmware/ + http://ldriver.qlogic.com/firmware/ + + They are also included in the linux-firmware tree as well. + +config TCM_QLA2XXX + tristate "TCM_QLA2XXX fabric module for Qlogic 2xxx series target mode HBAs" + depends on SCSI_QLA_FC && TARGET_CORE + select LIBFC + select BTREE + default n + ---help--- + Say Y here to enable the TCM_QLA2XXX fabric module for Qlogic 2xxx series target mode HBAs diff --git a/drivers/scsi/qla2xxx/Makefile b/drivers/scsi/qla2xxx/Makefile index 5df782f4a09..44def6bb4bb 100644 --- a/drivers/scsi/qla2xxx/Makefile +++ b/drivers/scsi/qla2xxx/Makefile @@ -1,5 +1,6 @@ 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 + qla_nx.o qla_mr.o qla_nx2.o qla_target.o qla_tmpl.o obj-$(CONFIG_SCSI_QLA_FC) += qla2xxx.o +obj-$(CONFIG_TCM_QLA2XXX) += tcm_qla2xxx.o diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 532313e0725..16fe5196e6d 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -1,10 +1,11 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ #include "qla_def.h" +#include "qla_target.h" #include <linux/kthread.h> #include <linux/vmalloc.h> @@ -23,12 +24,29 @@ qla2x00_sysfs_read_fw_dump(struct file *filp, struct kobject *kobj, 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) + if (!(ha->fw_dump_reading || ha->mctp_dump_reading)) return 0; - return memory_read_from_buffer(buf, count, &off, ha->fw_dump, + if (IS_P3P_TYPE(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 if (ha->mctp_dumped && ha->mctp_dump_reading) + return memory_read_from_buffer(buf, count, &off, ha->mctp_dump, + MCTP_DUMP_SIZE); + else if (ha->fw_dump_reading) + return memory_read_from_buffer(buf, count, &off, ha->fw_dump, ha->fw_dump_len); + else + return 0; } static ssize_t @@ -41,12 +59,6 @@ qla2x00_sysfs_write_fw_dump(struct file *filp, struct kobject *kobj, struct qla_hw_data *ha = vha->hw; int reading; - if (IS_QLA82XX(ha)) { - DEBUG2(qla_printk(KERN_INFO, ha, - "Firmware dump not supported for ISP82xx\n")); - return count; - } - if (off != 0) return (0); @@ -56,9 +68,13 @@ qla2x00_sysfs_write_fw_dump(struct file *filp, struct kobject *kobj, if (!ha->fw_dump_reading) break; - qla_printk(KERN_INFO, ha, + ql_log(ql_log_info, vha, 0x705d, "Firmware dump cleared on (%ld).\n", vha->host_no); + if (IS_P3P_TYPE(ha)) { + qla82xx_md_free(vha); + qla82xx_md_prep(vha); + } ha->fw_dump_reading = 0; ha->fw_dumped = 0; break; @@ -66,7 +82,7 @@ qla2x00_sysfs_write_fw_dump(struct file *filp, struct kobject *kobj, if (ha->fw_dumped && !ha->fw_dump_reading) { ha->fw_dump_reading = 1; - qla_printk(KERN_INFO, ha, + ql_log(ql_log_info, vha, 0x705e, "Raw firmware dump ready for read on (%ld).\n", vha->host_no); } @@ -75,10 +91,49 @@ qla2x00_sysfs_write_fw_dump(struct file *filp, struct kobject *kobj, qla2x00_alloc_fw_dump(vha); break; case 3: - qla2x00_system_error(vha); + if (IS_QLA82XX(ha)) { + qla82xx_idc_lock(ha); + qla82xx_set_reset_owner(vha); + qla82xx_idc_unlock(ha); + } else if (IS_QLA8044(ha)) { + qla8044_idc_lock(ha); + qla82xx_set_reset_owner(vha); + qla8044_idc_unlock(ha); + } else + qla2x00_system_error(vha); + break; + case 4: + if (IS_P3P_TYPE(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_P3P_TYPE(ha)) + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + break; + case 6: + if (!ha->mctp_dump_reading) + break; + ql_log(ql_log_info, vha, 0x70c1, + "MCTP dump cleared on (%ld).\n", vha->host_no); + ha->mctp_dump_reading = 0; + ha->mctp_dumped = 0; + break; + case 7: + if (ha->mctp_dumped && !ha->mctp_dump_reading) { + ha->mctp_dump_reading = 1; + ql_log(ql_log_info, vha, 0x70c2, + "Raw mctp dump ready for read on (%ld).\n", + vha->host_no); + } break; } - return (count); + return count; } static struct bin_attribute sysfs_fw_dump_attr = { @@ -92,6 +147,92 @@ static struct bin_attribute sysfs_fw_dump_attr = { }; static ssize_t +qla2x00_sysfs_read_fw_dump_template(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->fw_dump_template || !ha->fw_dump_template_len) + return 0; + + ql_dbg(ql_dbg_user, vha, 0x70e2, + "chunk <- off=%llx count=%zx\n", off, count); + return memory_read_from_buffer(buf, count, &off, + ha->fw_dump_template, ha->fw_dump_template_len); +} + +static ssize_t +qla2x00_sysfs_write_fw_dump_template(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 size; + + if (off == 0) { + if (ha->fw_dump) + vfree(ha->fw_dump); + if (ha->fw_dump_template) + vfree(ha->fw_dump_template); + + ha->fw_dump = NULL; + ha->fw_dump_len = 0; + ha->fw_dump_template = NULL; + ha->fw_dump_template_len = 0; + + size = qla27xx_fwdt_template_size(buf); + ql_dbg(ql_dbg_user, vha, 0x70d1, + "-> allocating fwdt (%x bytes)...\n", size); + ha->fw_dump_template = vmalloc(size); + if (!ha->fw_dump_template) { + ql_log(ql_log_warn, vha, 0x70d2, + "Failed allocate fwdt (%x bytes).\n", size); + return -ENOMEM; + } + ha->fw_dump_template_len = size; + } + + if (off + count > ha->fw_dump_template_len) { + count = ha->fw_dump_template_len - off; + ql_dbg(ql_dbg_user, vha, 0x70d3, + "chunk -> truncating to %zx bytes.\n", count); + } + + ql_dbg(ql_dbg_user, vha, 0x70d4, + "chunk -> off=%llx count=%zx\n", off, count); + memcpy(ha->fw_dump_template + off, buf, count); + + if (off + count == ha->fw_dump_template_len) { + size = qla27xx_fwdt_calculate_dump_size(vha); + ql_dbg(ql_dbg_user, vha, 0x70d5, + "-> allocating fwdump (%x bytes)...\n", size); + ha->fw_dump = vmalloc(size); + if (!ha->fw_dump) { + ql_log(ql_log_warn, vha, 0x70d6, + "Failed allocate fwdump (%x bytes).\n", size); + return -ENOMEM; + } + ha->fw_dump_len = size; + } + + return count; +} +static struct bin_attribute sysfs_fw_dump_template_attr = { + .attr = { + .name = "fw_dump_template", + .mode = S_IRUSR | S_IWUSR, + }, + .size = 0, + .read = qla2x00_sysfs_read_fw_dump_template, + .write = qla2x00_sysfs_write_fw_dump_template, +}; + +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) @@ -122,7 +263,7 @@ qla2x00_sysfs_write_nvram(struct file *filp, struct kobject *kobj, if (!capable(CAP_SYS_ADMIN) || off != 0 || count != ha->nvram_size || !ha->isp_ops->write_nvram) - return 0; + return -EINVAL; /* Checksum NVRAM. */ if (IS_FWI2_CAPABLE(ha)) { @@ -148,7 +289,7 @@ qla2x00_sysfs_write_nvram(struct file *filp, struct kobject *kobj, } if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x705f, "HBA not online, failing NVRAM update.\n"); return -EAGAIN; } @@ -158,12 +299,14 @@ qla2x00_sysfs_write_nvram(struct file *filp, struct kobject *kobj, 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); + return count; } static struct bin_attribute sysfs_nvram_attr = { @@ -184,12 +327,17 @@ qla2x00_sysfs_read_optrom(struct file *filp, struct kobject *kobj, struct scsi_qla_host *vha = shost_priv(dev_to_shost(container_of(kobj, struct device, kobj))); struct qla_hw_data *ha = vha->hw; + ssize_t rval = 0; if (ha->optrom_state != QLA_SREADING) return 0; - return memory_read_from_buffer(buf, count, &off, ha->optrom_buffer, - ha->optrom_region_size); + mutex_lock(&ha->optrom_mutex); + rval = memory_read_from_buffer(buf, count, &off, ha->optrom_buffer, + ha->optrom_region_size); + mutex_unlock(&ha->optrom_mutex); + + return rval; } static ssize_t @@ -208,7 +356,9 @@ qla2x00_sysfs_write_optrom(struct file *filp, struct kobject *kobj, if (off + count > ha->optrom_region_size) count = ha->optrom_region_size - off; + mutex_lock(&ha->optrom_mutex); memcpy(&ha->optrom_buffer[off], buf, count); + mutex_unlock(&ha->optrom_mutex); return count; } @@ -231,40 +381,44 @@ qla2x00_sysfs_write_optrom_ctl(struct file *filp, struct kobject *kobj, 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; + ssize_t rval = count; if (off) - return 0; + return -EINVAL; if (unlikely(pci_channel_offline(ha->pdev))) - return 0; + return -EAGAIN; if (sscanf(buf, "%d:%x:%x", &val, &start, &size) < 1) return -EINVAL; if (start > ha->optrom_size) return -EINVAL; + mutex_lock(&ha->optrom_mutex); switch (val) { case 0: if (ha->optrom_state != QLA_SREADING && - ha->optrom_state != QLA_SWRITING) - break; - + ha->optrom_state != QLA_SWRITING) { + rval = -EINVAL; + goto out; + } ha->optrom_state = QLA_SWAITING; - DEBUG2(qla_printk(KERN_INFO, ha, + ql_dbg(ql_dbg_user, vha, 0x7061, "Freeing flash region allocation -- 0x%x bytes.\n", - ha->optrom_region_size)); + ha->optrom_region_size); vfree(ha->optrom_buffer); ha->optrom_buffer = NULL; break; case 1: - if (ha->optrom_state != QLA_SWAITING) - break; + if (ha->optrom_state != QLA_SWAITING) { + rval = -EINVAL; + goto out; + } ha->optrom_region_start = start; ha->optrom_region_size = start + size > ha->optrom_size ? @@ -273,31 +427,35 @@ qla2x00_sysfs_write_optrom_ctl(struct file *filp, struct kobject *kobj, ha->optrom_state = QLA_SREADING; ha->optrom_buffer = vmalloc(ha->optrom_region_size); if (ha->optrom_buffer == NULL) { - qla_printk(KERN_WARNING, ha, + 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 count; + rval = -ENOMEM; + goto out; } if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, - "HBA not online, failing NVRAM update.\n"); - return -EAGAIN; + ql_log(ql_log_warn, vha, 0x7063, + "HBA not online, failing NVRAM update.\n"); + rval = -EAGAIN; + goto out; } - DEBUG2(qla_printk(KERN_INFO, ha, + ql_dbg(ql_dbg_user, vha, 0x7064, "Reading flash region -- 0x%x/0x%x.\n", - ha->optrom_region_start, ha->optrom_region_size)); + 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) - break; + if (ha->optrom_state != QLA_SWAITING) { + rval = -EINVAL; + goto out; + } /* * We need to be more restrictive on which FLASH regions are @@ -325,12 +483,14 @@ qla2x00_sysfs_write_optrom_ctl(struct file *filp, struct kobject *kobj, else if (start == (ha->flt_region_boot * 4) || start == (ha->flt_region_fw * 4)) valid = 1; - else if (IS_QLA25XX(ha) || IS_QLA8XXX_TYPE(ha)) + else if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) + || IS_CNA_CAPABLE(ha) || IS_QLA2031(ha)) valid = 1; if (!valid) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x7065, "Invalid start region 0x%x/0x%x.\n", start, size); - return -EINVAL; + rval = -EINVAL; + goto out; } ha->optrom_region_start = start; @@ -340,41 +500,48 @@ qla2x00_sysfs_write_optrom_ctl(struct file *filp, struct kobject *kobj, ha->optrom_state = QLA_SWRITING; ha->optrom_buffer = vmalloc(ha->optrom_region_size); if (ha->optrom_buffer == NULL) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x7066, "Unable to allocate memory for optrom update " - "(%x).\n", ha->optrom_region_size); + "(%x)\n", ha->optrom_region_size); ha->optrom_state = QLA_SWAITING; - return count; + rval = -ENOMEM; + goto out; } - DEBUG2(qla_printk(KERN_INFO, ha, + ql_dbg(ql_dbg_user, vha, 0x7067, "Staging flash region write -- 0x%x/0x%x.\n", - ha->optrom_region_start, ha->optrom_region_size)); + 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) - break; + if (ha->optrom_state != QLA_SWRITING) { + rval = -EINVAL; + goto out; + } if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x7068, "HBA not online, failing flash update.\n"); - return -EAGAIN; + rval = -EAGAIN; + goto out; } - DEBUG2(qla_printk(KERN_INFO, ha, + ql_dbg(ql_dbg_user, vha, 0x7069, "Writing flash region -- 0x%x/0x%x.\n", - ha->optrom_region_start, ha->optrom_region_size)); + 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: - count = -EINVAL; + rval = -EINVAL; } - return count; + +out: + mutex_unlock(&ha->optrom_mutex); + return rval; } static struct bin_attribute sysfs_optrom_ctl_attr = { @@ -396,10 +563,10 @@ qla2x00_sysfs_read_vpd(struct file *filp, struct kobject *kobj, struct qla_hw_data *ha = vha->hw; if (unlikely(pci_channel_offline(ha->pdev))) - return 0; + return -EAGAIN; if (!capable(CAP_SYS_ADMIN)) - return 0; + return -EINVAL; if (IS_NOCACHE_VPD_TYPE(ha)) ha->isp_ops->read_optrom(vha, ha->vpd, ha->flt_region_vpd << 2, @@ -425,7 +592,7 @@ qla2x00_sysfs_write_vpd(struct file *filp, struct kobject *kobj, return 0; if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x706a, "HBA not online, failing VPD update.\n"); return -EAGAIN; } @@ -436,17 +603,17 @@ qla2x00_sysfs_write_vpd(struct file *filp, struct kobject *kobj, /* Update flash version information for 4Gb & above. */ if (!IS_FWI2_CAPABLE(ha)) - goto done; + return -EINVAL; tmp_data = vmalloc(256); if (!tmp_data) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x706b, "Unable to allocate memory for VPD information update.\n"); - goto done; + return -ENOMEM; } ha->isp_ops->get_flash_version(vha, tmp_data); vfree(tmp_data); -done: + return count; } @@ -480,7 +647,7 @@ qla2x00_sysfs_read_sfp(struct file *filp, struct kobject *kobj, ha->sfp_data = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &ha->sfp_data_dma); if (!ha->sfp_data) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x706c, "Unable to allocate memory for SFP read-data.\n"); return 0; } @@ -497,13 +664,13 @@ do_read: } rval = qla2x00_read_sfp(vha, ha->sfp_data_dma, ha->sfp_data, - addr, offset, SFP_BLOCK_SIZE, 0); + addr, offset, SFP_BLOCK_SIZE, BIT_1); if (rval != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x706d, "Unable to read SFP data (%x/%x/%x).\n", rval, addr, offset); - count = 0; - break; + + return -EIO; } memcpy(buf, ha->sfp_data, SFP_BLOCK_SIZE); buf += SFP_BLOCK_SIZE; @@ -531,53 +698,117 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj, struct qla_hw_data *ha = vha->hw; struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); int type; - + uint32_t idc_control; + uint8_t *tmp_data = NULL; if (off != 0) - return 0; + return -EINVAL; type = simple_strtol(buf, NULL, 10); switch (type) { case 0x2025c: - qla_printk(KERN_INFO, ha, - "Issuing ISP reset on (%ld).\n", vha->host_no); + ql_log(ql_log_info, vha, 0x706e, + "Issuing ISP reset.\n"); scsi_block_requests(vha->host); - set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); - qla2xxx_wake_dpc(vha); + if (IS_QLA82XX(ha)) { + ha->flags.isp82xx_no_md_cap = 1; + qla82xx_idc_lock(ha); + qla82xx_set_reset_owner(vha); + qla82xx_idc_unlock(ha); + } else if (IS_QLA8044(ha)) { + qla8044_idc_lock(ha); + idc_control = qla8044_rd_reg(ha, + QLA8044_IDC_DRV_CTRL); + qla8044_wr_reg(ha, QLA8044_IDC_DRV_CTRL, + (idc_control | GRACEFUL_RESET_BIT1)); + qla82xx_set_reset_owner(vha); + qla8044_idc_unlock(ha); + } else { + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + } qla2x00_wait_for_chip_reset(vha); scsi_unblock_requests(vha->host); break; case 0x2025d: - if (!IS_QLA81XX(ha)) + if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha)) + return -EPERM; + + ql_log(ql_log_info, vha, 0x706f, + "Issuing MPI reset.\n"); + + if (IS_QLA83XX(ha)) { + uint32_t idc_control; + + qla83xx_idc_lock(vha, 0); + __qla83xx_get_idc_control(vha, &idc_control); + idc_control |= QLA83XX_IDC_GRACEFUL_RESET; + __qla83xx_set_idc_control(vha, idc_control); + qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, + QLA8XXX_DEV_NEED_RESET); + qla83xx_idc_audit(vha, IDC_AUDIT_TIMESTAMP); + qla83xx_idc_unlock(vha, 0); break; - - qla_printk(KERN_INFO, ha, - "Issuing MPI reset on (%ld).\n", vha->host_no); - - /* 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) - qla_printk(KERN_WARNING, ha, - "MPI reset failed on (%ld).\n", vha->host_no); - scsi_unblock_requests(vha->host); - break; + } else { + /* 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) { - qla_printk(KERN_INFO, ha, - "FCoE ctx reset not supported for host%ld.\n", - vha->host_no); - return count; + if (!IS_P3P_TYPE(ha) || vha != base_vha) { + ql_log(ql_log_info, vha, 0x7071, + "FCoE ctx reset no supported.\n"); + return -EPERM; } - qla_printk(KERN_INFO, ha, - "Issuing FCoE CTX reset on host%ld.\n", vha->host_no); + 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; + case 0x2025f: + if (!IS_QLA8031(ha)) + return -EPERM; + ql_log(ql_log_info, vha, 0x70bc, + "Disabling Reset by IDC control\n"); + qla83xx_idc_lock(vha, 0); + __qla83xx_get_idc_control(vha, &idc_control); + idc_control |= QLA83XX_IDC_RESET_DISABLED; + __qla83xx_set_idc_control(vha, idc_control); + qla83xx_idc_unlock(vha, 0); + break; + case 0x20260: + if (!IS_QLA8031(ha)) + return -EPERM; + ql_log(ql_log_info, vha, 0x70bd, + "Enabling Reset by IDC control\n"); + qla83xx_idc_lock(vha, 0); + __qla83xx_get_idc_control(vha, &idc_control); + idc_control &= ~QLA83XX_IDC_RESET_DISABLED; + __qla83xx_set_idc_control(vha, idc_control); + qla83xx_idc_unlock(vha, 0); + break; + case 0x20261: + ql_dbg(ql_dbg_user, vha, 0x70e0, + "Updating cache versions without reset "); + + tmp_data = vmalloc(256); + if (!tmp_data) { + ql_log(ql_log_warn, vha, 0x70e1, + "Unable to allocate memory for VPD information update.\n"); + return -ENOMEM; + } + ha->isp_ops->get_flash_version(vha, tmp_data); + vfree(tmp_data); + break; } return count; } @@ -592,144 +823,6 @@ static struct bin_attribute sysfs_reset_attr = { }; static ssize_t -qla2x00_sysfs_write_edc(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 dev, adr, opt, len; - int rval; - - ha->edc_data_len = 0; - - if (!capable(CAP_SYS_ADMIN) || off != 0 || count < 8) - return 0; - - if (!ha->edc_data) { - ha->edc_data = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, - &ha->edc_data_dma); - if (!ha->edc_data) { - DEBUG2(qla_printk(KERN_INFO, ha, - "Unable to allocate memory for EDC write.\n")); - return 0; - } - } - - dev = le16_to_cpup((void *)&buf[0]); - adr = le16_to_cpup((void *)&buf[2]); - opt = le16_to_cpup((void *)&buf[4]); - len = le16_to_cpup((void *)&buf[6]); - - if (!(opt & BIT_0)) - if (len == 0 || len > DMA_POOL_SIZE || len > count - 8) - return -EINVAL; - - memcpy(ha->edc_data, &buf[8], len); - - rval = qla2x00_write_sfp(vha, ha->edc_data_dma, ha->edc_data, - dev, adr, len, opt); - if (rval != QLA_SUCCESS) { - DEBUG2(qla_printk(KERN_INFO, ha, - "Unable to write EDC (%x) %02x:%02x:%04x:%02x:%02x.\n", - rval, dev, adr, opt, len, buf[8])); - return 0; - } - - return count; -} - -static struct bin_attribute sysfs_edc_attr = { - .attr = { - .name = "edc", - .mode = S_IWUSR, - }, - .size = 0, - .write = qla2x00_sysfs_write_edc, -}; - -static ssize_t -qla2x00_sysfs_write_edc_status(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 dev, adr, opt, len; - int rval; - - ha->edc_data_len = 0; - - if (!capable(CAP_SYS_ADMIN) || off != 0 || count < 8) - return 0; - - if (!ha->edc_data) { - ha->edc_data = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, - &ha->edc_data_dma); - if (!ha->edc_data) { - DEBUG2(qla_printk(KERN_INFO, ha, - "Unable to allocate memory for EDC status.\n")); - return 0; - } - } - - dev = le16_to_cpup((void *)&buf[0]); - adr = le16_to_cpup((void *)&buf[2]); - opt = le16_to_cpup((void *)&buf[4]); - len = le16_to_cpup((void *)&buf[6]); - - if (!(opt & BIT_0)) - if (len == 0 || len > DMA_POOL_SIZE) - return -EINVAL; - - memset(ha->edc_data, 0, len); - rval = qla2x00_read_sfp(vha, ha->edc_data_dma, ha->edc_data, - dev, adr, len, opt); - if (rval != QLA_SUCCESS) { - DEBUG2(qla_printk(KERN_INFO, ha, - "Unable to write EDC status (%x) %02x:%02x:%04x:%02x.\n", - rval, dev, adr, opt, len)); - return 0; - } - - ha->edc_data_len = len; - - return count; -} - -static ssize_t -qla2x00_sysfs_read_edc_status(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) || off != 0 || count == 0) - return 0; - - if (!ha->edc_data || ha->edc_data_len == 0 || ha->edc_data_len > count) - return -EINVAL; - - memcpy(buf, ha->edc_data, ha->edc_data_len); - - return ha->edc_data_len; -} - -static struct bin_attribute sysfs_edc_status_attr = { - .attr = { - .name = "edc_status", - .mode = S_IRUSR | S_IWUSR, - }, - .size = 0, - .write = qla2x00_sysfs_write_edc_status, - .read = qla2x00_sysfs_read_edc_status, -}; - -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) @@ -749,7 +842,7 @@ qla2x00_sysfs_read_xgmac_stats(struct file *filp, struct kobject *kobj, ha->xgmac_data = dma_alloc_coherent(&ha->pdev->dev, XGMAC_DATA_SIZE, &ha->xgmac_data_dma, GFP_KERNEL); if (!ha->xgmac_data) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x7076, "Unable to allocate memory for XGMAC read-data.\n"); return 0; } @@ -761,7 +854,7 @@ do_read: rval = qla2x00_get_xgmac_stats(vha, ha->xgmac_data_dma, XGMAC_DATA_SIZE, &actual_size); if (rval != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x7077, "Unable to read XGMAC data (%x).\n", rval); count = 0; } @@ -801,9 +894,9 @@ qla2x00_sysfs_read_dcbx_tlv(struct file *filp, struct kobject *kobj, ha->dcbx_tlv = dma_alloc_coherent(&ha->pdev->dev, DCBX_TLV_DATA_SIZE, &ha->dcbx_tlv_dma, GFP_KERNEL); if (!ha->dcbx_tlv) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x7078, "Unable to allocate memory for DCBX TLV read-data.\n"); - return 0; + return -ENOMEM; } do_read: @@ -813,9 +906,9 @@ do_read: rval = qla2x00_get_dcbx_params(vha, ha->dcbx_tlv_dma, DCBX_TLV_DATA_SIZE); if (rval != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, - "Unable to read DCBX TLV data (%x).\n", rval); - count = 0; + ql_log(ql_log_warn, vha, 0x7079, + "Unable to read DCBX TLV (%x).\n", rval); + return -EIO; } memcpy(buf, ha->dcbx_tlv, count); @@ -838,14 +931,13 @@ static struct sysfs_entry { int is4GBp_only; } bin_file_entries[] = { { "fw_dump", &sysfs_fw_dump_attr, }, + { "fw_dump_template", &sysfs_fw_dump_template_attr, 0x27 }, { "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, }, - { "edc", &sysfs_edc_attr, 2 }, - { "edc_status", &sysfs_edc_status_attr, 2 }, { "xgmac_stats", &sysfs_xgmac_stats_attr, 3 }, { "dcbx_tlv", &sysfs_dcbx_tlv_attr, 3 }, { NULL }, @@ -863,20 +955,26 @@ qla2x00_alloc_sysfs_attr(scsi_qla_host_t *vha) continue; if (iter->is4GBp_only == 2 && !IS_QLA25XX(vha->hw)) continue; - if (iter->is4GBp_only == 3 && !(IS_QLA8XXX_TYPE(vha->hw))) + if (iter->is4GBp_only == 3 && !(IS_CNA_CAPABLE(vha->hw))) + continue; + if (iter->is4GBp_only == 0x27 && !IS_QLA27XX(vha->hw)) continue; ret = sysfs_create_bin_file(&host->shost_gendev.kobj, iter->attr); if (ret) - qla_printk(KERN_INFO, vha->hw, - "Unable to create sysfs %s binary attribute " - "(%d).\n", iter->name, 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) +qla2x00_free_sysfs_attr(scsi_qla_host_t *vha, bool stop_beacon) { struct Scsi_Host *host = vha->host; struct sysfs_entry *iter; @@ -887,14 +985,14 @@ qla2x00_free_sysfs_attr(scsi_qla_host_t *vha) continue; if (iter->is4GBp_only == 2 && !IS_QLA25XX(ha)) continue; - if (iter->is4GBp_only == 3 && !!(IS_QLA8XXX_TYPE(vha->hw))) + 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) + if (stop_beacon && ha->beacon_blink_led == 1) ha->isp_ops->beacon_off(vha); } @@ -904,7 +1002,7 @@ 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); + return scnprintf(buf, PAGE_SIZE, "%s\n", qla2x00_version_str); } static ssize_t @@ -915,7 +1013,7 @@ qla2x00_fw_version_show(struct device *dev, struct qla_hw_data *ha = vha->hw; char fw_str[128]; - return snprintf(buf, PAGE_SIZE, "%s\n", + return scnprintf(buf, PAGE_SIZE, "%s\n", ha->isp_ops->fw_version_str(vha, fw_str)); } @@ -927,13 +1025,16 @@ qla2x00_serial_num_show(struct device *dev, struct device_attribute *attr, 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); + if (IS_QLAFX00(vha->hw)) { + return scnprintf(buf, PAGE_SIZE, "%s\n", + vha->hw->mr.serial_num); + } else if (IS_FWI2_CAPABLE(ha)) { + qla2xxx_get_vpd_field(vha, "SN", buf, PAGE_SIZE - 1); + return strlen(strcat(buf, "\n")); } sn = ((ha->serial0 & 0x1f) << 16) | (ha->serial2 << 8) | ha->serial1; - return snprintf(buf, PAGE_SIZE, "%c%05d\n", 'A' + sn / 100000, + return scnprintf(buf, PAGE_SIZE, "%c%05d\n", 'A' + sn / 100000, sn % 100000); } @@ -942,7 +1043,7 @@ 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); + return scnprintf(buf, PAGE_SIZE, "ISP%04X\n", vha->hw->pdev->device); } static ssize_t @@ -951,7 +1052,12 @@ qla2x00_isp_id_show(struct device *dev, struct device_attribute *attr, { 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", + + if (IS_QLAFX00(vha->hw)) + return scnprintf(buf, PAGE_SIZE, "%s\n", + vha->hw->mr.hw_version); + + return scnprintf(buf, PAGE_SIZE, "%04x %04x %04x %04x\n", ha->product_id[0], ha->product_id[1], ha->product_id[2], ha->product_id[3]); } @@ -961,7 +1067,8 @@ 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); + + return scnprintf(buf, PAGE_SIZE, "%s\n", vha->hw->model_number); } static ssize_t @@ -969,7 +1076,7 @@ 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", + return scnprintf(buf, PAGE_SIZE, "%s\n", vha->hw->model_desc ? vha->hw->model_desc : ""); } @@ -980,7 +1087,7 @@ qla2x00_pci_info_show(struct device *dev, struct device_attribute *attr, scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); char pci_info[30]; - return snprintf(buf, PAGE_SIZE, "%s\n", + return scnprintf(buf, PAGE_SIZE, "%s\n", vha->hw->isp_ops->pci_info_str(vha, pci_info)); } @@ -995,30 +1102,29 @@ qla2x00_link_state_show(struct device *dev, struct device_attribute *attr, 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"); + len = scnprintf(buf, PAGE_SIZE, "Link Down\n"); else if (atomic_read(&vha->loop_state) != LOOP_READY || - test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || - test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) - len = snprintf(buf, PAGE_SIZE, "Unknown Link State\n"); + qla2x00_reset_active(vha)) + len = scnprintf(buf, PAGE_SIZE, "Unknown Link State\n"); else { - len = snprintf(buf, PAGE_SIZE, "Link Up - "); + len = scnprintf(buf, PAGE_SIZE, "Link Up - "); switch (ha->current_topology) { case ISP_CFG_NL: - len += snprintf(buf + len, PAGE_SIZE-len, "Loop\n"); + len += scnprintf(buf + len, PAGE_SIZE-len, "Loop\n"); break; case ISP_CFG_FL: - len += snprintf(buf + len, PAGE_SIZE-len, "FL_Port\n"); + len += scnprintf(buf + len, PAGE_SIZE-len, "FL_Port\n"); break; case ISP_CFG_N: - len += snprintf(buf + len, PAGE_SIZE-len, + len += scnprintf(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"); + len += scnprintf(buf + len, PAGE_SIZE-len, "F_Port\n"); break; default: - len += snprintf(buf + len, PAGE_SIZE-len, "Loop\n"); + len += scnprintf(buf + len, PAGE_SIZE-len, "Loop\n"); break; } } @@ -1034,10 +1140,10 @@ qla2x00_zio_show(struct device *dev, struct device_attribute *attr, switch (vha->hw->zio_mode) { case QLA_ZIO_MODE_6: - len += snprintf(buf + len, PAGE_SIZE-len, "Mode 6\n"); + len += scnprintf(buf + len, PAGE_SIZE-len, "Mode 6\n"); break; case QLA_ZIO_DISABLED: - len += snprintf(buf + len, PAGE_SIZE-len, "Disabled\n"); + len += scnprintf(buf + len, PAGE_SIZE-len, "Disabled\n"); break; } return len; @@ -1077,7 +1183,7 @@ qla2x00_zio_timer_show(struct device *dev, struct device_attribute *attr, { scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); - return snprintf(buf, PAGE_SIZE, "%d us\n", vha->hw->zio_timer * 100); + return scnprintf(buf, PAGE_SIZE, "%d us\n", vha->hw->zio_timer * 100); } static ssize_t @@ -1107,9 +1213,9 @@ qla2x00_beacon_show(struct device *dev, struct device_attribute *attr, int len = 0; if (vha->hw->beacon_blink_led) - len += snprintf(buf + len, PAGE_SIZE-len, "Enabled\n"); + len += scnprintf(buf + len, PAGE_SIZE-len, "Enabled\n"); else - len += snprintf(buf + len, PAGE_SIZE-len, "Disabled\n"); + len += scnprintf(buf + len, PAGE_SIZE-len, "Disabled\n"); return len; } @@ -1126,7 +1232,7 @@ qla2x00_beacon_store(struct device *dev, struct device_attribute *attr, return -EPERM; if (test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x707a, "Abort ISP active -- ignoring beacon request.\n"); return -EBUSY; } @@ -1151,7 +1257,7 @@ qla2x00_optrom_bios_version_show(struct device *dev, { 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], + return scnprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->bios_revision[1], ha->bios_revision[0]); } @@ -1161,7 +1267,7 @@ qla2x00_optrom_efi_version_show(struct device *dev, { 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], + return scnprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->efi_revision[1], ha->efi_revision[0]); } @@ -1171,7 +1277,7 @@ qla2x00_optrom_fcode_version_show(struct device *dev, { 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], + return scnprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->fcode_revision[1], ha->fcode_revision[0]); } @@ -1181,7 +1287,7 @@ qla2x00_optrom_fw_version_show(struct device *dev, { 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", + return scnprintf(buf, PAGE_SIZE, "%d.%02d.%02d %d\n", ha->fw_revision[0], ha->fw_revision[1], ha->fw_revision[2], ha->fw_revision[3]); } @@ -1193,10 +1299,10 @@ qla2x00_optrom_gold_fw_version_show(struct device *dev, scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); struct qla_hw_data *ha = vha->hw; - if (!IS_QLA81XX(ha)) - return snprintf(buf, PAGE_SIZE, "\n"); + if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha) && !IS_QLA27XX(ha)) + return scnprintf(buf, PAGE_SIZE, "\n"); - return snprintf(buf, PAGE_SIZE, "%d.%02d.%02d (%d)\n", + return scnprintf(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]); } @@ -1206,9 +1312,8 @@ 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); + return scnprintf(buf, PAGE_SIZE, "%d\n", + vha->qla_stats.total_isp_aborts); } static ssize_t @@ -1221,16 +1326,16 @@ qla24xx_84xx_fw_version_show(struct device *dev, struct qla_hw_data *ha = vha->hw; if (!IS_QLA84XX(ha)) - return snprintf(buf, PAGE_SIZE, "\n"); + return scnprintf(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", + return scnprintf(buf, PAGE_SIZE, "%u\n", (uint32_t)ha->cs84xx->op_fw_version); - return snprintf(buf, PAGE_SIZE, "\n"); + return scnprintf(buf, PAGE_SIZE, "\n"); } static ssize_t @@ -1240,10 +1345,10 @@ qla2x00_mpi_version_show(struct device *dev, struct device_attribute *attr, scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); struct qla_hw_data *ha = vha->hw; - if (!IS_QLA81XX(ha)) - return snprintf(buf, PAGE_SIZE, "\n"); + if (!IS_QLA81XX(ha) && !IS_QLA8031(ha) && !IS_QLA8044(ha)) + return scnprintf(buf, PAGE_SIZE, "\n"); - return snprintf(buf, PAGE_SIZE, "%d.%02d.%02d (%x)\n", + return scnprintf(buf, PAGE_SIZE, "%d.%02d.%02d (%x)\n", ha->mpi_version[0], ha->mpi_version[1], ha->mpi_version[2], ha->mpi_capabilities); } @@ -1255,10 +1360,10 @@ qla2x00_phy_version_show(struct device *dev, struct device_attribute *attr, scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); struct qla_hw_data *ha = vha->hw; - if (!IS_QLA81XX(ha)) - return snprintf(buf, PAGE_SIZE, "\n"); + if (!IS_QLA81XX(ha) && !IS_QLA8031(ha)) + return scnprintf(buf, PAGE_SIZE, "\n"); - return snprintf(buf, PAGE_SIZE, "%d.%02d.%02d\n", + return scnprintf(buf, PAGE_SIZE, "%d.%02d.%02d\n", ha->phy_version[0], ha->phy_version[1], ha->phy_version[2]); } @@ -1269,7 +1374,7 @@ qla2x00_flash_block_size_show(struct device *dev, 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); + return scnprintf(buf, PAGE_SIZE, "0x%x\n", ha->fdt_block_size); } static ssize_t @@ -1278,10 +1383,10 @@ qla2x00_vlan_id_show(struct device *dev, struct device_attribute *attr, { scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); - if (!IS_QLA8XXX_TYPE(vha->hw)) - return snprintf(buf, PAGE_SIZE, "\n"); + if (!IS_CNA_CAPABLE(vha->hw)) + return scnprintf(buf, PAGE_SIZE, "\n"); - return snprintf(buf, PAGE_SIZE, "%d\n", vha->fcoe_vlan_id); + return scnprintf(buf, PAGE_SIZE, "%d\n", vha->fcoe_vlan_id); } static ssize_t @@ -1290,13 +1395,10 @@ qla2x00_vn_port_mac_address_show(struct device *dev, { scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); - if (!IS_QLA8XXX_TYPE(vha->hw)) - return snprintf(buf, PAGE_SIZE, "\n"); + if (!IS_CNA_CAPABLE(vha->hw)) + return scnprintf(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]); + return scnprintf(buf, PAGE_SIZE, "%pMR\n", vha->fcoe_vn_port_mac); } static ssize_t @@ -1305,7 +1407,7 @@ qla2x00_fabric_param_show(struct device *dev, struct device_attribute *attr, { scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); - return snprintf(buf, PAGE_SIZE, "%d\n", vha->hw->switch_cap); + return scnprintf(buf, PAGE_SIZE, "%d\n", vha->hw->switch_cap); } static ssize_t @@ -1313,24 +1415,23 @@ 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; + uint16_t temp = 0; - if (!vha->hw->flags.thermal_supported) - return snprintf(buf, PAGE_SIZE, "\n"); + if (qla2x00_reset_active(vha)) { + ql_log(ql_log_warn, vha, 0x70dc, "ISP reset active.\n"); + goto done; + } - temp = frac = 0; - if (test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || - test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) - DEBUG2_3_11(printk(KERN_WARNING - "%s(%ld): isp reset in progress.\n", - __func__, vha->host_no)); - else if (!vha->hw->flags.eeh_busy) - rval = qla2x00_get_thermal_temp(vha, &temp, &frac); - if (rval != QLA_SUCCESS) - temp = frac = 0; + if (vha->hw->flags.eeh_busy) { + ql_log(ql_log_warn, vha, 0x70dd, "PCI EEH busy.\n"); + goto done; + } - return snprintf(buf, PAGE_SIZE, "%d.%02d\n", temp, frac); + if (qla2x00_get_thermal_temp(vha, &temp) == QLA_SUCCESS) + return scnprintf(buf, PAGE_SIZE, "%d\n", temp); + +done: + return scnprintf(buf, PAGE_SIZE, "\n"); } static ssize_t @@ -1340,20 +1441,99 @@ qla2x00_fw_state_show(struct device *dev, struct device_attribute *attr, scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); int rval = QLA_FUNCTION_FAILED; uint16_t state[5]; + uint32_t pstate; - if (test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || - test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) - DEBUG2_3_11(printk("%s(%ld): isp reset in progress.\n", - __func__, vha->host_no)); + if (IS_QLAFX00(vha->hw)) { + pstate = qlafx00_fw_state_show(dev, attr, buf); + return scnprintf(buf, PAGE_SIZE, "0x%x\n", pstate); + } + + 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], + return scnprintf(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 ssize_t +qla2x00_diag_requests_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + + if (!IS_BIDI_CAPABLE(vha->hw)) + return scnprintf(buf, PAGE_SIZE, "\n"); + + return scnprintf(buf, PAGE_SIZE, "%llu\n", vha->bidi_stats.io_count); +} + +static ssize_t +qla2x00_diag_megabytes_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + + if (!IS_BIDI_CAPABLE(vha->hw)) + return scnprintf(buf, PAGE_SIZE, "\n"); + + return scnprintf(buf, PAGE_SIZE, "%llu\n", + vha->bidi_stats.transfer_bytes >> 20); +} + +static ssize_t +qla2x00_fw_dump_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; + uint32_t size; + + if (!ha->fw_dumped) + size = 0; + else if (IS_P3P_TYPE(ha)) + size = ha->md_template_size + ha->md_dump_size; + else + size = ha->fw_dump_len; + + return scnprintf(buf, PAGE_SIZE, "%d\n", size); +} + +static ssize_t +qla2x00_allow_cna_fw_dump_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + + if (!IS_P3P_TYPE(vha->hw)) + return scnprintf(buf, PAGE_SIZE, "\n"); + else + return scnprintf(buf, PAGE_SIZE, "%s\n", + vha->hw->allow_cna_fw_dump ? "true" : "false"); +} + +static ssize_t +qla2x00_allow_cna_fw_dump_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; + + if (!IS_P3P_TYPE(vha->hw)) + return -EINVAL; + + if (sscanf(buf, "%d", &val) != 1) + return -EINVAL; + + vha->hw->allow_cna_fw_dump = val != 0; + + return strlen(buf); +} + 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); @@ -1392,6 +1572,12 @@ static DEVICE_ATTR(vn_port_mac_address, S_IRUGO, 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); +static DEVICE_ATTR(diag_requests, S_IRUGO, qla2x00_diag_requests_show, NULL); +static DEVICE_ATTR(diag_megabytes, S_IRUGO, qla2x00_diag_megabytes_show, NULL); +static DEVICE_ATTR(fw_dump_size, S_IRUGO, qla2x00_fw_dump_size_show, NULL); +static DEVICE_ATTR(allow_cna_fw_dump, S_IRUGO | S_IWUSR, + qla2x00_allow_cna_fw_dump_show, + qla2x00_allow_cna_fw_dump_store); struct device_attribute *qla2x00_host_attrs[] = { &dev_attr_driver_version, @@ -1421,6 +1607,10 @@ struct device_attribute *qla2x00_host_attrs[] = { &dev_attr_fw_state, &dev_attr_optrom_gold_fw_version, &dev_attr_thermal_temp, + &dev_attr_diag_requests, + &dev_attr_diag_megabytes, + &dev_attr_fw_dump_size, + &dev_attr_allow_cna_fw_dump, NULL, }; @@ -1442,6 +1632,11 @@ qla2x00_get_host_speed(struct Scsi_Host *shost) (shost_priv(shost)))->hw; u32 speed = FC_PORTSPEED_UNKNOWN; + if (IS_QLAFX00(ha)) { + qlafx00_get_host_speed(shost); + return; + } + switch (ha->link_data_rate) { case PORT_SPEED_1GB: speed = FC_PORTSPEED_1GBIT; @@ -1458,6 +1653,12 @@ qla2x00_get_host_speed(struct Scsi_Host *shost) case PORT_SPEED_10GB: speed = FC_PORTSPEED_10GBIT; break; + case PORT_SPEED_16GB: + speed = FC_PORTSPEED_16GBIT; + break; + case PORT_SPEED_32GB: + speed = FC_PORTSPEED_32GBIT; + break; } fc_host_speed(shost) = speed; } @@ -1607,11 +1808,14 @@ qla2x00_terminate_rport_io(struct fc_rport *rport) * 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)) - 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); + if (fcport->loop_id != FC_NO_LOOP_ID) { + 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 @@ -1619,6 +1823,9 @@ qla2x00_issue_lip(struct Scsi_Host *shost) { scsi_qla_host_t *vha = shost_priv(shost); + if (IS_QLAFX00(vha->hw)) + return 0; + qla2x00_loop_reset(vha); return 0; } @@ -1634,19 +1841,25 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost) dma_addr_t stats_dma; struct fc_host_statistics *pfc_host_stat; - pfc_host_stat = &ha->fc_host_stat; + pfc_host_stat = &vha->fc_host_stat; memset(pfc_host_stat, -1, sizeof(struct fc_host_statistics)); + if (IS_QLAFX00(vha->hw)) + goto done; + if (test_bit(UNLOADING, &vha->dpc_flags)) goto done; if (unlikely(pci_channel_offline(ha->pdev))) goto done; + if (qla2x00_reset_active(vha)) + goto done; + stats = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &stats_dma); if (stats == NULL) { - DEBUG2_3_11(printk("%s(%ld): Failed to allocate memory.\n", - __func__, base_vha->host_no)); + ql_log(ql_log_warn, vha, 0x707d, + "Failed to allocate memory for stats.\n"); goto done; } memset(stats, 0, DMA_POOL_SIZE); @@ -1655,9 +1868,7 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost) 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 && - !test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags) && - !test_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags) && - !ha->dpc_active) { + !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); @@ -1676,11 +1887,21 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost) 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->dumped_frames = stats->discarded_frames; pfc_host_stat->nos_count = stats->nos_rcvd; + pfc_host_stat->error_frames = + stats->dropped_frames + stats->discarded_frames; + pfc_host_stat->rx_words = vha->qla_stats.input_bytes; + pfc_host_stat->tx_words = vha->qla_stats.output_bytes; } - pfc_host_stat->fcp_input_megabytes = ha->qla_stats.input_bytes >> 20; - pfc_host_stat->fcp_output_megabytes = ha->qla_stats.output_bytes >> 20; + pfc_host_stat->fcp_control_requests = vha->qla_stats.control_requests; + pfc_host_stat->fcp_input_requests = vha->qla_stats.input_requests; + pfc_host_stat->fcp_output_requests = vha->qla_stats.output_requests; + pfc_host_stat->fcp_input_megabytes = vha->qla_stats.input_bytes >> 20; + pfc_host_stat->fcp_output_megabytes = vha->qla_stats.output_bytes >> 20; + pfc_host_stat->seconds_since_last_reset = + get_jiffies_64() - vha->qla_stats.jiffies_at_last_reset; + do_div(pfc_host_stat->seconds_since_last_reset, HZ); done_free: dma_pool_free(ha->s_dma_pool, stats, stats_dma); @@ -1689,6 +1910,16 @@ done: } static void +qla2x00_reset_host_stats(struct Scsi_Host *shost) +{ + scsi_qla_host_t *vha = shost_priv(shost); + + memset(&vha->fc_host_stat, 0, sizeof(vha->fc_host_stat)); + + vha->qla_stats.jiffies_at_last_reset = get_jiffies_64(); +} + +static void qla2x00_get_host_symbolic_name(struct Scsi_Host *shost) { scsi_qla_host_t *vha = shost_priv(shost); @@ -1724,12 +1955,31 @@ 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) + if (!base_vha->flags.online) { fc_host_port_state(shost) = FC_PORTSTATE_OFFLINE; - else if (atomic_read(&base_vha->loop_state) == LOOP_TIMEOUT) - fc_host_port_state(shost) = FC_PORTSTATE_UNKNOWN; - else + 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 @@ -1746,15 +1996,14 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable) ret = qla24xx_vport_create_req_sanity_check(fc_vport); if (ret) { - DEBUG15(printk("qla24xx_vport_create_req_sanity_check failed, " - "status %x\n", 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) { - DEBUG15(printk ("qla24xx_create_vhost failed, vha = %p\n", - vha)); + ql_log(ql_log_warn, vha, 0x707f, "Vport create host failed.\n"); return FC_VPORT_FAILED; } if (disable) { @@ -1764,8 +2013,8 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable) atomic_set(&vha->vp_state, VP_FAILED); /* ready to create vport */ - qla_printk(KERN_INFO, vha->hw, "VP entry id %d assigned.\n", - vha->vp_idx); + 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); @@ -1775,35 +2024,44 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable) 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 */ - DEBUG15(printk ("scsi(%ld): pport loop_state is not UP.\n", - base_vha->host_no)); + 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_QLA25XX(ha) || IS_QLA81XX(ha)) && ql2xenabledif) { + if (IS_T10_PI_CAPABLE(ha) && ql2xenabledif) { if (ha->fw_attributes & BIT_4) { + int prot = 0, guard; vha->flags.difdix_supported = 1; - DEBUG18(qla_printk(KERN_INFO, ha, - "Registering for DIF/DIX type 1 and 3" - " protection.\n")); + 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, - SHOST_DIF_TYPE1_PROTECTION + 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); + + guard = SHOST_DIX_GUARD_CRC; + + if (IS_PI_IPGUARD_CAPABLE(ha) && + (ql2xenabledif > 1 || IS_PI_DIFB_DIX0_CAPABLE(ha))) + guard |= SHOST_DIX_GUARD_IP; + + scsi_host_set_guard(vha->host, guard); } else vha->flags.difdix_supported = 0; } if (scsi_add_host_with_dma(vha->host, &fc_vport->dev, &ha->pdev->dev)) { - DEBUG15(printk("scsi(%ld): scsi_add_host failure for VP[%d].\n", - vha->host_no, vha->vp_idx)); + ql_dbg(ql_dbg_user, vha, 0x7083, + "scsi_add_host failure for VP[%d].\n", vha->vp_idx); goto vport_create_failed_2; } @@ -1816,10 +2074,15 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable) fc_host_supported_speeds(vha->host) = fc_host_supported_speeds(base_vha->host); + qlt_vport_create(vha, ha); 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; @@ -1832,17 +2095,21 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable) break; } } + if (qos) { ret = qla25xx_create_req_que(ha, options, vha->vp_idx, 0, 0, qos); if (!ret) - qla_printk(KERN_WARNING, ha, - "Can't create request queue for vp_idx:%d\n", - vha->vp_idx); + ql_log(ql_log_warn, vha, 0x7084, + "Can't create request queue for VP[%d]\n", + vha->vp_idx); else { - DEBUG2(qla_printk(KERN_INFO, ha, - "Request Que:%d (QoS: %d) created for vp_idx:%d\n", - ret, qos, vha->vp_idx)); + 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]; } } @@ -1873,6 +2140,8 @@ qla24xx_vport_delete(struct fc_vport *fc_vport) vha->flags.delete_progress = 1; + qlt_remove_target(ha, vha); + fc_remove_host(vha->host); scsi_remove_host(vha->host); @@ -1882,14 +2151,10 @@ qla24xx_vport_delete(struct fc_vport *fc_vport) if (vha->timer_active) { qla2x00_vp_stop_timer(vha); - DEBUG15(printk(KERN_INFO "scsi(%ld): timer for the vport[%d]" - " = %p has stopped\n", vha->host_no, vha->vp_idx, 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 */ - DEBUG(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); @@ -1901,12 +2166,12 @@ qla24xx_vport_delete(struct fc_vport *fc_vport) if (vha->req->id && !ha->flags.cpu_affinity_enabled) { if (qla25xx_delete_req_que(vha, vha->req) != QLA_SUCCESS) - qla_printk(KERN_WARNING, ha, - "Queue delete failed.\n"); + 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); - qla_printk(KERN_INFO, ha, "vport %d deleted\n", id); return 0; } @@ -1962,6 +2227,7 @@ struct fc_function_template qla2xxx_transport_functions = { .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, + .reset_fc_host_stats = qla2x00_reset_host_stats, .vport_create = qla24xx_vport_create, .vport_disable = qla24xx_vport_disable, @@ -2008,6 +2274,8 @@ struct fc_function_template qla2xxx_transport_vport_functions = { .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, + .reset_fc_host_stats = qla2x00_reset_host_stats, + .bsg_request = qla24xx_bsg_request, .bsg_timeout = qla24xx_bsg_timeout, }; @@ -2021,12 +2289,16 @@ qla2x00_init_host_attr(scsi_qla_host_t *vha) 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_supported_classes(vha->host) = ha->tgt.enable_class_2 ? + (FC_COS_CLASS2|FC_COS_CLASS3) : 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_QLA8XXX_TYPE(ha)) + if (IS_CNA_CAPABLE(ha)) speed = FC_PORTSPEED_10GBIT; + else if (IS_QLA2031(ha)) + speed = FC_PORTSPEED_16GBIT | FC_PORTSPEED_8GBIT | + FC_PORTSPEED_4GBIT; else if (IS_QLA25XX(ha)) speed = FC_PORTSPEED_8GBIT | FC_PORTSPEED_4GBIT | FC_PORTSPEED_2GBIT | FC_PORTSPEED_1GBIT; @@ -2035,6 +2307,12 @@ qla2x00_init_host_attr(scsi_qla_host_t *vha) FC_PORTSPEED_1GBIT; else if (IS_QLA23XX(ha)) speed = FC_PORTSPEED_2GBIT | FC_PORTSPEED_1GBIT; + else if (IS_QLAFX00(ha)) + speed = FC_PORTSPEED_8GBIT | FC_PORTSPEED_4GBIT | + FC_PORTSPEED_2GBIT | FC_PORTSPEED_1GBIT; + else if (IS_QLA27XX(ha)) + speed = FC_PORTSPEED_32GBIT | FC_PORTSPEED_16GBIT | + FC_PORTSPEED_8GBIT; 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 index 8c10e2c4928..524f9eb7fcd 100644 --- a/drivers/scsi/qla2xxx/qla_bsg.c +++ b/drivers/scsi/qla2xxx/qla_bsg.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -11,32 +11,58 @@ #include <linux/delay.h> /* BSG support for ELS/CT pass through */ -inline srb_t * -qla2x00_get_ctx_bsg_sp(scsi_qla_host_t *vha, fc_port_t *fcport, size_t size) +void +qla2x00_bsg_job_done(void *data, void *ptr, int res) { - srb_t *sp; + 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 = sp->fcport->vha; + struct fc_bsg_job *bsg_job = sp->u.bsg_job; struct qla_hw_data *ha = vha->hw; - struct srb_ctx *ctx; + struct qla_mt_iocb_rqst_fx00 *piocb_rqst; - sp = mempool_alloc(ha->srb_mempool, GFP_KERNEL); - if (!sp) - goto done; - ctx = kzalloc(size, GFP_KERNEL); - if (!ctx) { - mempool_free(sp, ha->srb_mempool); - sp = NULL; - goto done; + if (sp->type == SRB_FXIOCB_BCMD) { + piocb_rqst = (struct qla_mt_iocb_rqst_fx00 *) + &bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; + + if (piocb_rqst->flags & SRB_FXDISC_REQ_DMA_VALID) + dma_unmap_sg(&ha->pdev->dev, + bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + + if (piocb_rqst->flags & SRB_FXDISC_RESP_DMA_VALID) + dma_unmap_sg(&ha->pdev->dev, + bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); + } else { + 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); } - memset(sp, 0, sizeof(*sp)); - sp->fcport = fcport; - sp->ctx = ctx; -done: - return sp; + if (sp->type == SRB_CT_CMD || + sp->type == SRB_FXIOCB_BCMD || + sp->type == SRB_ELS_CMD_HST) + kfree(sp->fcport); + qla2x00_rel_sp(vha, sp); } int -qla24xx_fcp_prio_cfg_valid(struct qla_fcp_prio_cfg *pri_cfg, uint8_t flag) +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; @@ -51,18 +77,17 @@ qla24xx_fcp_prio_cfg_valid(struct qla_fcp_prio_cfg *pri_cfg, uint8_t flag) if (bcode_val == 0xFFFFFFFF) { /* No FCP Priority config data in flash */ - DEBUG2(printk(KERN_INFO - "%s: No FCP priority config data.\n", - __func__)); + 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*/ - DEBUG2(printk(KERN_ERR - "%s: Invalid FCP Priority data header. bcode=0x%x\n", - __func__, bcode_val)); + ql_dbg(ql_dbg_user, vha, 0x7052, + "Invalid FCP Priority data header. bcode=0x%x.\n", + bcode_val); return 0; } if (flag != 1) @@ -77,15 +102,14 @@ qla24xx_fcp_prio_cfg_valid(struct qla_fcp_prio_cfg *pri_cfg, uint8_t flag) if (num_valid == 0) { /* No valid FCP priority data entries */ - DEBUG2(printk(KERN_ERR - "%s: No valid FCP Priority data entries.\n", - __func__)); + ql_dbg(ql_dbg_user, vha, 0x7053, + "No valid FCP Priority data entries.\n"); ret = 0; } else { /* FCP priority data is valid */ - DEBUG2(printk(KERN_INFO - "%s: Valid FCP priority data. num entries = %d\n", - __func__, num_valid)); + ql_dbg(ql_dbg_user, vha, 0x7054, + "Valid FCP priority data. num entries = %d.\n", + num_valid); } return ret; @@ -101,20 +125,11 @@ qla24xx_proc_fcp_prio_cfg_cmd(struct fc_bsg_job *bsg_job) uint32_t len; uint32_t oper; - bsg_job->reply->reply_payload_rcv_len = 0; - - if (!(IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha))) { + if (!(IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) || IS_P3P_TYPE(ha))) { ret = -EINVAL; goto exit_fcp_prio_cfg; } - 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)) { - ret = -EBUSY; - goto exit_fcp_prio_cfg; - } - /* Get the sub command */ oper = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; @@ -182,10 +197,9 @@ qla24xx_proc_fcp_prio_cfg_cmd(struct fc_bsg_job *bsg_job) if (!ha->fcp_prio_cfg) { ha->fcp_prio_cfg = vmalloc(FCP_PRIO_CFG_SIZE); if (!ha->fcp_prio_cfg) { - qla_printk(KERN_WARNING, ha, - "Unable to allocate memory " - "for fcp prio config data (%x).\n", - FCP_PRIO_CFG_SIZE); + 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; @@ -198,9 +212,9 @@ qla24xx_proc_fcp_prio_cfg_cmd(struct fc_bsg_job *bsg_job) FCP_PRIO_CFG_SIZE); /* validate fcp priority data */ - if (!qla24xx_fcp_prio_cfg_valid( - (struct qla_fcp_prio_cfg *) - ha->fcp_prio_cfg, 1)) { + + 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 @@ -222,9 +236,11 @@ qla24xx_proc_fcp_prio_cfg_cmd(struct fc_bsg_job *bsg_job) break; } exit_fcp_prio_cfg: - bsg_job->job_done(bsg_job); + if (!ret) + bsg_job->job_done(bsg_job); return ret; } + static int qla2x00_process_els(struct fc_bsg_job *bsg_job) { @@ -238,7 +254,6 @@ qla2x00_process_els(struct fc_bsg_job *bsg_job) int req_sg_cnt, rsp_sg_cnt; int rval = (DRIVER_ERROR << 16); uint16_t nextlid = 0; - struct srb_ctx *els; if (bsg_job->request->msgcode == FC_BSG_RPT_ELS) { rport = bsg_job->rport; @@ -254,11 +269,16 @@ qla2x00_process_els(struct fc_bsg_job *bsg_job) type = "FC_BSG_HST_ELS_NOLOGIN"; } + if (!vha->flags.online) { + ql_log(ql_log_warn, vha, 0x7005, "Host not online.\n"); + rval = -EIO; + goto done; + } + /* pass through is supported only for ISP 4Gb or higher */ if (!IS_FWI2_CAPABLE(ha)) { - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld):ELS passthru not supported for ISP23xx based " - "adapters\n", vha->host_no)); + ql_dbg(ql_dbg_user, vha, 0x7001, + "ELS passthru not supported for ISP23xx based adapters.\n"); rval = -EPERM; goto done; } @@ -266,11 +286,11 @@ qla2x00_process_els(struct fc_bsg_job *bsg_job) /* Multiple SG's are not supported for ELS requests */ if (bsg_job->request_payload.sg_cnt > 1 || bsg_job->reply_payload.sg_cnt > 1) { - DEBUG2(printk(KERN_INFO - "multiple SG's are not supported for ELS requests" - " [request_sg_cnt: %x reply_sg_cnt: %x]\n", - bsg_job->request_payload.sg_cnt, - bsg_job->reply_payload.sg_cnt)); + 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; } @@ -281,9 +301,9 @@ qla2x00_process_els(struct fc_bsg_job *bsg_job) * if not perform fabric login */ if (qla2x00_fabric_login(vha, fcport, &nextlid)) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "failed to login port %06X for ELS passthru\n", - fcport->d_id.b24)); + ql_dbg(ql_dbg_user, vha, 0x7003, + "Failed to login port %06X for ELS passthru.\n", + fcport->d_id.b24); rval = -EIO; goto done; } @@ -301,7 +321,6 @@ qla2x00_process_els(struct fc_bsg_job *bsg_job) /* 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 = @@ -313,13 +332,6 @@ qla2x00_process_els(struct fc_bsg_job *bsg_job) NPH_FABRIC_CONTROLLER : NPH_F_PORT; } - if (!vha->flags.online) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "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); @@ -337,43 +349,43 @@ qla2x00_process_els(struct fc_bsg_job *bsg_job) if ((req_sg_cnt != bsg_job->request_payload.sg_cnt) || (rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) { - DEBUG2(printk(KERN_INFO - "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)); + 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_ctx_bsg_sp(vha, fcport, sizeof(struct srb_ctx)); + sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); if (!sp) { rval = -ENOMEM; goto done_unmap_sg; } - els = sp->ctx; - els->type = + sp->type = (bsg_job->request->msgcode == FC_BSG_RPT_ELS ? SRB_ELS_CMD_RPT : SRB_ELS_CMD_HST); - els->name = + sp->name = (bsg_job->request->msgcode == FC_BSG_RPT_ELS ? "bsg_els_rpt" : "bsg_els_hst"); - els->u.bsg_job = bsg_job; + sp->u.bsg_job = bsg_job; + sp->free = qla2x00_bsg_sp_free; + sp->done = qla2x00_bsg_job_done; - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld:%x): bsg rqst type: %s els type: %x - loop-id=%x " - "portid=%02x%02x%02x.\n", vha->host_no, sp->handle, 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)); + 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) { - kfree(sp->ctx); - mempool_free(sp, ha->srb_mempool); + ql_log(ql_log_warn, vha, 0x700e, + "qla2x00_start_sp failed = %d\n", rval); + qla2x00_rel_sp(vha, sp); rval = -EIO; goto done_unmap_sg; } @@ -387,12 +399,26 @@ done_unmap_sg: goto done_free_fcport; done_free_fcport: - if (bsg_job->request->msgcode == FC_BSG_HST_ELS_NOLOGIN) + if (bsg_job->request->msgcode == FC_BSG_RPT_ELS) 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) { @@ -405,12 +431,13 @@ qla2x00_process_ct(struct fc_bsg_job *bsg_job) uint16_t loop_id; struct fc_port *fcport; char *type = "FC_BSG_HST_CT"; - struct srb_ctx *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; } @@ -418,24 +445,25 @@ qla2x00_process_ct(struct fc_bsg_job *bsg_job) 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)) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "[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)); + 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) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "host not online\n")); + ql_log(ql_log_warn, vha, 0x7012, + "Host is not online.\n"); rval = -EIO; goto done_unmap_sg; } @@ -451,8 +479,8 @@ qla2x00_process_ct(struct fc_bsg_job *bsg_job) loop_id = vha->mgmt_svr_loop_id; break; default: - DEBUG2(qla_printk(KERN_INFO, ha, - "Unknown loop id: %x\n", loop_id)); + ql_dbg(ql_dbg_user, vha, 0x7013, + "Unknown loop id: %x.\n", loop_id); rval = -EINVAL; goto done_unmap_sg; } @@ -464,41 +492,47 @@ qla2x00_process_ct(struct fc_bsg_job *bsg_job) */ 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_ctx_bsg_sp(vha, fcport, sizeof(struct srb_ctx)); + 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; } - ct = sp->ctx; - ct->type = SRB_CT_CMD; - ct->name = "bsg_ct"; - ct->u.bsg_job = bsg_job; + 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; - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld:%x): bsg rqst type: %s els type: %x - loop-id=%x " - "portid=%02x%02x%02x.\n", vha->host_no, sp->handle, 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)); + 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) { - kfree(sp->ctx); - mempool_free(sp, ha->srb_mempool); + ql_log(ql_log_warn, vha, 0x7017, + "qla2x00_start_sp failed=%d.\n", rval); + qla2x00_rel_sp(vha, sp); rval = -EIO; goto done_free_fcport; } @@ -515,98 +549,153 @@ done: return rval; } -/* Set the port configuration to enable the - * internal loopback on ISP81XX - */ +/* Disable loopback mode */ 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)) - 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) { - DEBUG2(printk(KERN_ERR - "%s(%lu): Set port config failed\n", - __func__, vha->host_no)); - 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))) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "State change notificaition not received.\n")); - } else - DEBUG2(qla_printk(KERN_INFO, ha, - "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) +qla81xx_reset_loopback_mode(scsi_qla_host_t *vha, uint16_t *config, + int wait, int wait2) { int ret = 0; int rval = 0; uint16_t new_config[4]; struct qla_hw_data *ha = vha->hw; - if (!IS_QLA81XX(ha)) + if (!IS_QLA81XX(ha) && !IS_QLA8031(ha) && !IS_QLA8044(ha)) goto done_reset_internal; memset(new_config, 0 , sizeof(new_config)); if ((config[0] & INTERNAL_LOOPBACK_MASK) >> 1 == - ENABLE_INTERNAL_LOOPBACK) { + ENABLE_INTERNAL_LOOPBACK || + (config[0] & INTERNAL_LOOPBACK_MASK) >> 1 == + ENABLE_EXTERNAL_LOOPBACK) { new_config[0] = config[0] & ~INTERNAL_LOOPBACK_MASK; + ql_dbg(ql_dbg_user, vha, 0x70bf, "new_config[0]=%02x\n", + (new_config[0] & INTERNAL_LOOPBACK_MASK)); memcpy(&new_config[1], &config[1], sizeof(uint16_t) * 3) ; ha->notify_dcbx_comp = wait; + ha->notify_lb_portup_comp = wait2; + ret = qla81xx_set_port_config(vha, new_config); if (ret != QLA_SUCCESS) { - DEBUG2(printk(KERN_ERR - "%s(%lu): Set port config failed\n", - __func__, vha->host_no)); + ql_log(ql_log_warn, vha, 0x7025, + "Set port config failed.\n"); ha->notify_dcbx_comp = 0; + ha->notify_lb_portup_comp = 0; rval = -EINVAL; goto done_reset_internal; } /* Wait for DCBX complete event */ if (wait && !wait_for_completion_timeout(&ha->dcbx_comp, - (20 * HZ))) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "State change notificaition not received.\n")); + (DCBX_COMP_TIMEOUT * HZ))) { + ql_dbg(ql_dbg_user, vha, 0x7026, + "DCBX completion not received.\n"); ha->notify_dcbx_comp = 0; + ha->notify_lb_portup_comp = 0; rval = -EINVAL; goto done_reset_internal; } else - DEBUG2(qla_printk(KERN_INFO, ha, - "State change RECEIVED\n")); + ql_dbg(ql_dbg_user, vha, 0x7027, + "DCBX completion received.\n"); + + if (wait2 && + !wait_for_completion_timeout(&ha->lb_portup_comp, + (LB_PORTUP_COMP_TIMEOUT * HZ))) { + ql_dbg(ql_dbg_user, vha, 0x70c5, + "Port up completion not received.\n"); + ha->notify_lb_portup_comp = 0; + rval = -EINVAL; + goto done_reset_internal; + } else + ql_dbg(ql_dbg_user, vha, 0x70c6, + "Port up completion received.\n"); ha->notify_dcbx_comp = 0; + ha->notify_lb_portup_comp = 0; } done_reset_internal: return rval; } +/* + * Set the port configuration to enable the internal or external loopback + * depending on the loopback mode. + */ +static inline int +qla81xx_set_loopback_mode(scsi_qla_host_t *vha, uint16_t *config, + uint16_t *new_config, uint16_t mode) +{ + int ret = 0; + int rval = 0; + unsigned long rem_tmo = 0, current_tmo = 0; + struct qla_hw_data *ha = vha->hw; + + if (!IS_QLA81XX(ha) && !IS_QLA8031(ha) && !IS_QLA8044(ha)) + goto done_set_internal; + + if (mode == INTERNAL_LOOPBACK) + new_config[0] = config[0] | (ENABLE_INTERNAL_LOOPBACK << 1); + else if (mode == EXTERNAL_LOOPBACK) + new_config[0] = config[0] | (ENABLE_EXTERNAL_LOOPBACK << 1); + ql_dbg(ql_dbg_user, vha, 0x70be, + "new_config[0]=%02x\n", (new_config[0] & INTERNAL_LOOPBACK_MASK)); + + 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 */ + current_tmo = DCBX_COMP_TIMEOUT * HZ; + while (1) { + rem_tmo = wait_for_completion_timeout(&ha->dcbx_comp, + current_tmo); + if (!ha->idc_extend_tmo || rem_tmo) { + ha->idc_extend_tmo = 0; + break; + } + current_tmo = ha->idc_extend_tmo * HZ; + ha->idc_extend_tmo = 0; + } + + if (!rem_tmo) { + ql_dbg(ql_dbg_user, vha, 0x7022, + "DCBX completion not received.\n"); + ret = qla81xx_reset_loopback_mode(vha, new_config, 0, 0); + /* + * If the reset of the loopback mode doesn't work take a FCoE + * dump and reset the chip. + */ + if (ret) { + ha->isp_ops->fw_dump(vha, 0); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + } + rval = -EINVAL; + } else { + if (ha->flags.idc_compl_status) { + ql_dbg(ql_dbg_user, vha, 0x70c3, + "Bad status in IDC Completion AEN\n"); + rval = -EINVAL; + ha->flags.idc_compl_status = 0; + } else + ql_dbg(ql_dbg_user, vha, 0x7023, + "DCBX completion received.\n"); + } + + ha->notify_dcbx_comp = 0; + ha->idc_extend_tmo = 0; + +done_set_internal: + return rval; +} + static int qla2x00_process_loopback(struct fc_bsg_job *bsg_job) { @@ -627,13 +716,8 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job) dma_addr_t rsp_data_dma; uint32_t rsp_data_len; - 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)) - return -EBUSY; - if (!vha->flags.online) { - DEBUG2(qla_printk(KERN_WARNING, ha, "host not online\n")); + ql_log(ql_log_warn, vha, 0x7019, "Host is not online.\n"); return -EIO; } @@ -641,26 +725,31 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job) bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); - if (!elreq.req_sg_cnt) + 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)) { - DEBUG2(printk(KERN_INFO - "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)); + 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; } @@ -668,8 +757,8 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job) req_data = dma_alloc_coherent(&ha->pdev->dev, req_data_len, &req_data_dma, GFP_KERNEL); if (!req_data) { - DEBUG2(printk(KERN_ERR "%s: dma alloc for req_data " - "failed for host=%lu\n", __func__, vha->host_no)); + ql_log(ql_log_warn, vha, 0x701d, + "dma alloc failed for req_data.\n"); rval = -ENOMEM; goto done_unmap_sg; } @@ -677,8 +766,8 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job) rsp_data = dma_alloc_coherent(&ha->pdev->dev, rsp_data_len, &rsp_data_dma, GFP_KERNEL); if (!rsp_data) { - DEBUG2(printk(KERN_ERR "%s: dma alloc for rsp_data " - "failed for host=%lu\n", __func__, vha->host_no)); + ql_log(ql_log_warn, vha, 0x7004, + "dma alloc failed for rsp_data.\n"); rval = -ENOMEM; goto done_free_dma_req; } @@ -692,138 +781,141 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job) elreq.transfer_size = req_data_len; elreq.options = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; + elreq.iteration_count = + bsg_job->request->rqst_data.h_vendor.vendor_cmd[2]; - if ((ha->current_topology == ISP_CFG_F || - (IS_QLA81XX(ha) && + if (atomic_read(&vha->loop_state) == LOOP_READY && + (ha->current_topology == ISP_CFG_F || + ((IS_QLA81XX(ha) || IS_QLA8031(ha) || IS_QLA8044(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"; - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld) bsg rqst type: %s\n", vha->host_no, type)); + 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)) { + if (IS_QLA81XX(ha) || IS_QLA8031(ha) || IS_QLA8044(ha)) { memset(config, 0, sizeof(config)); memset(new_config, 0, sizeof(new_config)); + if (qla81xx_get_port_config(vha, config)) { - DEBUG2(printk(KERN_ERR - "%s(%lu): Get port config failed\n", - __func__, vha->host_no)); - bsg_job->reply->reply_payload_rcv_len = 0; - bsg_job->reply->result = (DID_ERROR << 16); + ql_log(ql_log_warn, vha, 0x701f, + "Get port config failed.\n"); rval = -EPERM; - goto done_free_dma_req; + goto done_free_dma_rsp; } - if (elreq.options != EXTERNAL_LOOPBACK) { - DEBUG2(qla_printk(KERN_INFO, ha, - "Internal: current port config = %x\n", - config[0])); - if (qla81xx_set_internal_loopback(vha, config, - new_config)) { - bsg_job->reply->reply_payload_rcv_len = - 0; - 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->reply_payload_rcv_len = - 0; - bsg_job->reply->result = - (DID_ERROR << 16); - rval = -EPERM; - goto done_free_dma_req; - } + if ((config[0] & INTERNAL_LOOPBACK_MASK) != 0) { + ql_dbg(ql_dbg_user, vha, 0x70c4, + "Loopback operation already in " + "progress.\n"); + rval = -EAGAIN; + goto done_free_dma_rsp; + } + + ql_dbg(ql_dbg_user, vha, 0x70c0, + "elreq.options=%04x\n", elreq.options); + + if (elreq.options == EXTERNAL_LOOPBACK) + if (IS_QLA8031(ha) || IS_QLA8044(ha)) + rval = qla81xx_set_loopback_mode(vha, + config, new_config, elreq.options); + else + rval = qla81xx_reset_loopback_mode(vha, + config, 1, 0); + else + rval = qla81xx_set_loopback_mode(vha, config, + new_config, elreq.options); + + if (rval) { + rval = -EPERM; + goto done_free_dma_rsp; } type = "FC_BSG_HST_VENDOR_LOOPBACK"; - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld) bsg rqst type: %s\n", - vha->host_no, type)); + 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) { - DEBUG2(printk(KERN_ERR "%s(%ld): ABORTing " - "ISP\n", __func__, vha->host_no)); + 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) { - qla_printk(KERN_INFO, ha, - "MPI reset failed for host%ld.\n", - vha->host_no); + if (IS_QLA81XX(ha)) { + if (qla81xx_restart_mpi_firmware(vha) != + QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x702a, + "MPI reset failed.\n"); + } } - bsg_job->reply->reply_payload_rcv_len = 0; - bsg_job->reply->result = (DID_ERROR << 16); rval = -EIO; - goto done_free_dma_req; + goto done_free_dma_rsp; } + + if (new_config[0]) { + int ret; + + /* Revert back to original port config + * Also clear internal loopback + */ + ret = qla81xx_reset_loopback_mode(vha, + new_config, 0, 1); + if (ret) { + /* + * If the reset of the loopback mode + * doesn't work take FCoE dump and then + * reset the chip. + */ + ha->isp_ops->fw_dump(vha, 0); + set_bit(ISP_ABORT_NEEDED, + &vha->dpc_flags); + } + + } + } else { type = "FC_BSG_HST_VENDOR_LOOPBACK"; - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld) bsg rqst type: %s\n", - vha->host_no, type)); + 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) { - DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " - "request %s failed\n", vha->host_no, type)); + 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->reply_payload_rcv_len = 0; bsg_job->reply->result = (DID_ERROR << 16); + bsg_job->reply->reply_payload_rcv_len = 0; } else { - DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " - "request %s completed\n", vha->host_no, 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; + ql_dbg(ql_dbg_user, vha, 0x702d, + "Vendor request %s completed.\n", type); + bsg_job->reply->result = (DID_OK << 16); 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); + bsg_job->reply_len = sizeof(struct fc_bsg_reply) + + sizeof(response) + sizeof(uint8_t); + 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; + +done_free_dma_rsp: dma_free_coherent(&ha->pdev->dev, rsp_data_len, rsp_data, rsp_data_dma); done_free_dma_req: @@ -837,6 +929,8 @@ 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); + if (!rval) + bsg_job->job_done(bsg_job); return rval; } @@ -849,14 +943,8 @@ qla84xx_reset(struct fc_bsg_job *bsg_job) int rval = 0; uint32_t flag; - 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)) - return -EBUSY; - if (!IS_QLA84XX(ha)) { - DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld): Not 84xx, " - "exiting.\n", vha->host_no)); + ql_dbg(ql_dbg_user, vha, 0x702f, "Not 84xx, exiting.\n"); return -EINVAL; } @@ -865,18 +953,17 @@ qla84xx_reset(struct fc_bsg_job *bsg_job) rval = qla84xx_reset_chip(vha, flag == A84_ISSUE_RESET_DIAG_FW); if (rval) { - DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " - "request 84xx reset failed\n", vha->host_no)); - rval = bsg_job->reply->reply_payload_rcv_len = 0; - bsg_job->reply->result = (DID_ERROR << 16); + ql_log(ql_log_warn, vha, 0x7030, + "Vendor request 84xx reset failed.\n"); + rval = (DID_ERROR << 16); } else { - DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " - "request 84xx reset completed\n", vha->host_no)); + 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); } - bsg_job->job_done(bsg_job); return rval; } @@ -896,27 +983,25 @@ qla84xx_updatefw(struct fc_bsg_job *bsg_job) uint32_t flag; uint32_t fw_ver; - 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)) - return -EBUSY; - if (!IS_QLA84XX(ha)) { - DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld): Not 84xx, " - "exiting.\n", vha->host_no)); + 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) + 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) { - DEBUG2(printk(KERN_INFO - "dma mapping resulted in different sg counts " - "request_sg_cnt: %x dma_request_sg_cnt: %x ", - bsg_job->request_payload.sg_cnt, 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; } @@ -925,8 +1010,8 @@ qla84xx_updatefw(struct fc_bsg_job *bsg_job) fw_buf = dma_alloc_coherent(&ha->pdev->dev, data_len, &fw_dma, GFP_KERNEL); if (!fw_buf) { - DEBUG2(printk(KERN_ERR "%s: dma alloc for fw_buf " - "failed for host=%lu\n", __func__, vha->host_no)); + ql_log(ql_log_warn, vha, 0x7035, + "DMA alloc failed for fw_buf.\n"); rval = -ENOMEM; goto done_unmap_sg; } @@ -936,8 +1021,8 @@ qla84xx_updatefw(struct fc_bsg_job *bsg_job) mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma); if (!mn) { - DEBUG2(printk(KERN_ERR "%s: dma alloc for fw buffer " - "failed for host=%lu\n", __func__, vha->host_no)); + ql_log(ql_log_warn, vha, 0x7036, + "DMA alloc failed for fw buffer.\n"); rval = -ENOMEM; goto done_free_fw_buf; } @@ -965,21 +1050,18 @@ qla84xx_updatefw(struct fc_bsg_job *bsg_job) rval = qla2x00_issue_iocb_timeout(vha, mn, mn_dma, 0, 120); if (rval) { - DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " - "request 84xx updatefw failed\n", vha->host_no)); - - rval = bsg_job->reply->reply_payload_rcv_len = 0; - bsg_job->reply->result = (DID_ERROR << 16); + ql_log(ql_log_warn, vha, 0x7037, + "Vendor request 84xx updatefw failed.\n"); + rval = (DID_ERROR << 16); } else { - DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " - "request 84xx updatefw completed\n", vha->host_no)); + 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: @@ -989,6 +1071,8 @@ done_unmap_sg: dma_unmap_sg(&ha->pdev->dev, bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + if (!rval) + bsg_job->job_done(bsg_job); return rval; } @@ -1007,36 +1091,23 @@ qla84xx_mgmt_cmd(struct fc_bsg_job *bsg_job) uint32_t data_len = 0; uint32_t dma_direction = DMA_NONE; - 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)) - return -EBUSY; - if (!IS_QLA84XX(ha)) { - DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld): Not 84xx, " - "exiting.\n", vha->host_no)); - return -EINVAL; - } - - ql84_mgmt = (struct qla_bsg_a84_mgmt *)((char *)bsg_job->request + - sizeof(struct fc_bsg_request)); - if (!ql84_mgmt) { - DEBUG2(printk("%s(%ld): mgmt header not provided, exiting.\n", - __func__, vha->host_no)); + ql_log(ql_log_warn, vha, 0x703a, + "Not 84xx, exiting.\n"); return -EINVAL; } mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma); if (!mn) { - DEBUG2(printk(KERN_ERR "%s: dma alloc for fw buffer " - "failed for host=%lu\n", __func__, vha->host_no)); + 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; - + ql84_mgmt = (void *)bsg_job->request + sizeof(struct fc_bsg_request); switch (ql84_mgmt->mgmt.cmd) { case QLA84_MGMT_READ_MEM: case QLA84_MGMT_GET_INFO: @@ -1044,6 +1115,8 @@ qla84xx_mgmt_cmd(struct fc_bsg_job *bsg_job) 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; } @@ -1051,10 +1124,10 @@ qla84xx_mgmt_cmd(struct fc_bsg_job *bsg_job) dma_direction = DMA_FROM_DEVICE; if (sg_cnt != bsg_job->reply_payload.sg_cnt) { - DEBUG2(printk(KERN_INFO - "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)); + 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; } @@ -1064,9 +1137,8 @@ qla84xx_mgmt_cmd(struct fc_bsg_job *bsg_job) mgmt_b = dma_alloc_coherent(&ha->pdev->dev, data_len, &mgmt_dma, GFP_KERNEL); if (!mgmt_b) { - DEBUG2(printk(KERN_ERR "%s: dma alloc for mgmt_b " - "failed for host=%lu\n", - __func__, vha->host_no)); + ql_log(ql_log_warn, vha, 0x703f, + "DMA alloc failed for mgmt_b.\n"); rval = -ENOMEM; goto done_unmap_sg; } @@ -1094,6 +1166,8 @@ qla84xx_mgmt_cmd(struct fc_bsg_job *bsg_job) 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; } @@ -1101,10 +1175,10 @@ qla84xx_mgmt_cmd(struct fc_bsg_job *bsg_job) dma_direction = DMA_TO_DEVICE; if (sg_cnt != bsg_job->request_payload.sg_cnt) { - DEBUG2(printk(KERN_INFO - "dma mapping resulted in different sg counts " - "request_sg_cnt: %x dma_request_sg_cnt: %x ", - bsg_job->request_payload.sg_cnt, 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; } @@ -1113,9 +1187,8 @@ qla84xx_mgmt_cmd(struct fc_bsg_job *bsg_job) mgmt_b = dma_alloc_coherent(&ha->pdev->dev, data_len, &mgmt_dma, GFP_KERNEL); if (!mgmt_b) { - DEBUG2(printk(KERN_ERR "%s: dma alloc for mgmt_b " - "failed for host=%lu\n", - __func__, vha->host_no)); + ql_log(ql_log_warn, vha, 0x7042, + "DMA alloc failed for mgmt_b.\n"); rval = -ENOMEM; goto done_unmap_sg; } @@ -1156,15 +1229,14 @@ qla84xx_mgmt_cmd(struct fc_bsg_job *bsg_job) rval = qla2x00_issue_iocb(vha, mn, mn_dma, 0); if (rval) { - DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " - "request 84xx mgmt failed\n", vha->host_no)); + ql_log(ql_log_warn, vha, 0x7043, + "Vendor request 84xx mgmt failed.\n"); - rval = bsg_job->reply->reply_payload_rcv_len = 0; - bsg_job->reply->result = (DID_ERROR << 16); + rval = (DID_ERROR << 16); } else { - DEBUG2(qla_printk(KERN_WARNING, ha, "scsi(%ld) Vendor " - "request 84xx mgmt completed\n", vha->host_no)); + 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; @@ -1180,8 +1252,6 @@ qla84xx_mgmt_cmd(struct fc_bsg_job *bsg_job) } } - bsg_job->job_done(bsg_job); - done_unmap_sg: if (mgmt_b) dma_free_coherent(&ha->pdev->dev, data_len, mgmt_b, mgmt_dma); @@ -1196,6 +1266,8 @@ done_unmap_sg: exit_mgmt: dma_pool_free(ha->s_dma_pool, mn, mn_dma); + if (!rval) + bsg_job->job_done(bsg_job); return rval; } @@ -1204,37 +1276,22 @@ qla24xx_iidma(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; struct qla_port_param *port_param = NULL; fc_port_t *fcport = NULL; + int found = 0; uint16_t mb[MAILBOX_REGISTER_COUNT]; uint8_t *rsp_ptr = NULL; - bsg_job->reply->reply_payload_rcv_len = 0; - - 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)) - return -EBUSY; - if (!IS_IIDMA_CAPABLE(vha->hw)) { - DEBUG2(qla_printk(KERN_WARNING, ha, "%s(%lu): iiDMA not " - "supported\n", __func__, vha->host_no)); - return -EINVAL; - } - - port_param = (struct qla_port_param *)((char *)bsg_job->request + - sizeof(struct fc_bsg_request)); - if (!port_param) { - DEBUG2(printk("%s(%ld): port_param header not provided, " - "exiting.\n", __func__, vha->host_no)); + ql_log(ql_log_info, vha, 0x7046, "iiDMA not supported.\n"); return -EINVAL; } + port_param = (void *)bsg_job->request + sizeof(struct fc_bsg_request); if (port_param->fc_scsi_addr.dest_type != EXT_DEF_TYPE_WWPN) { - DEBUG2(printk(KERN_ERR "%s(%ld): Invalid destination type\n", - __func__, vha->host_no)); + ql_log(ql_log_warn, vha, 0x7048, + "Invalid destination type.\n"); return -EINVAL; } @@ -1245,25 +1302,26 @@ qla24xx_iidma(struct fc_bsg_job *bsg_job) if (memcmp(port_param->fc_scsi_addr.dest_addr.wwpn, fcport->port_name, sizeof(fcport->port_name))) continue; + + found = 1; break; } - if (!fcport) { - DEBUG2(printk(KERN_ERR "%s(%ld): Failed to find port\n", - __func__, vha->host_no)); + if (!found) { + ql_log(ql_log_warn, vha, 0x7049, + "Failed to find port.\n"); return -EINVAL; } if (atomic_read(&fcport->state) != FCS_ONLINE) { - DEBUG2(printk(KERN_ERR "%s(%ld): Port not online\n", - __func__, vha->host_no)); + ql_log(ql_log_warn, vha, 0x704a, + "Port is not online.\n"); return -EINVAL; } if (fcport->flags & FCF_LOGIN_NEEDED) { - DEBUG2(printk(KERN_ERR "%s(%ld): Remote port not logged in, " - "flags = 0x%x\n", - __func__, vha->host_no, fcport->flags)); + ql_log(ql_log_warn, vha, 0x704b, + "Remote port not logged in flags = 0x%x.\n", fcport->flags); return -EINVAL; } @@ -1275,18 +1333,11 @@ qla24xx_iidma(struct fc_bsg_job *bsg_job) &port_param->speed, mb); if (rval) { - DEBUG16(printk(KERN_ERR "scsi(%ld): iIDMA cmd failed for " - "%02x%02x%02x%02x%02x%02x%02x%02x -- " - "%04x %x %04x %04x.\n", - vha->host_no, 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); - + ql_log(ql_log_warn, vha, 0x704c, + "iIDMA cmd failed for %8phN -- " + "%04x %x %04x %04x.\n", fcport->port_name, + rval, fcport->fp_speed, mb[0], mb[1]); + rval = (DID_ERROR << 16); } else { if (!port_param->mode) { bsg_job->reply_len = sizeof(struct fc_bsg_reply) + @@ -1300,33 +1351,38 @@ qla24xx_iidma(struct fc_bsg_job *bsg_job) } bsg_job->reply->result = DID_OK; + bsg_job->job_done(bsg_job); } - bsg_job->job_done(bsg_job); return rval; } static int -qla2x00_optrom_setup(struct fc_bsg_job *bsg_job, struct qla_hw_data *ha, +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; - - bsg_job->reply->reply_payload_rcv_len = 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) + 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) + 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; @@ -1334,12 +1390,12 @@ qla2x00_optrom_setup(struct fc_bsg_job *bsg_job, struct qla_hw_data *ha, start == (ha->flt_region_fw * 4)) valid = 1; else if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) || - IS_QLA8XXX_TYPE(ha)) + IS_CNA_CAPABLE(ha) || IS_QLA2031(ha)) valid = 1; if (!valid) { - qla_printk(KERN_WARNING, ha, - "Invalid start region 0x%x/0x%x.\n", - start, bsg_job->request_payload.payload_len); + ql_log(ql_log_warn, vha, 0x7058, + "Invalid start region 0x%x/0x%x.\n", start, + bsg_job->request_payload.payload_len); return -EINVAL; } @@ -1358,9 +1414,9 @@ qla2x00_optrom_setup(struct fc_bsg_job *bsg_job, struct qla_hw_data *ha, ha->optrom_buffer = vmalloc(ha->optrom_region_size); if (!ha->optrom_buffer) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x7059, "Read: Unable to allocate memory for optrom retrieval " - "(%x).\n", ha->optrom_region_size); + "(%x)\n", ha->optrom_region_size); ha->optrom_state = QLA_SWAITING; return -ENOMEM; @@ -1378,9 +1434,15 @@ qla2x00_read_optrom(struct fc_bsg_job *bsg_job) struct qla_hw_data *ha = vha->hw; int rval = 0; - rval = qla2x00_optrom_setup(bsg_job, ha, 0); - if (rval) + if (ha->flags.nic_core_reset_hdlr_active) + return -EBUSY; + + mutex_lock(&ha->optrom_mutex); + rval = qla2x00_optrom_setup(bsg_job, vha, 0); + if (rval) { + mutex_unlock(&ha->optrom_mutex); return rval; + } ha->isp_ops->read_optrom(vha, ha->optrom_buffer, ha->optrom_region_start, ha->optrom_region_size); @@ -1394,6 +1456,7 @@ qla2x00_read_optrom(struct fc_bsg_job *bsg_job) vfree(ha->optrom_buffer); ha->optrom_buffer = NULL; ha->optrom_state = QLA_SWAITING; + mutex_unlock(&ha->optrom_mutex); bsg_job->job_done(bsg_job); return rval; } @@ -1406,9 +1469,15 @@ qla2x00_update_optrom(struct fc_bsg_job *bsg_job) struct qla_hw_data *ha = vha->hw; int rval = 0; - rval = qla2x00_optrom_setup(bsg_job, ha, 1); - if (rval) + mutex_lock(&ha->optrom_mutex); + rval = qla2x00_optrom_setup(bsg_job, vha, 1); + if (rval) { + mutex_unlock(&ha->optrom_mutex); return rval; + } + + /* Set the isp82xx_no_md_cap not to capture minidump */ + ha->flags.isp82xx_no_md_cap = 1; sg_copy_to_buffer(bsg_job->request_payload.sg_list, bsg_job->request_payload.sg_cnt, ha->optrom_buffer, @@ -1421,11 +1490,626 @@ qla2x00_update_optrom(struct fc_bsg_job *bsg_job) vfree(ha->optrom_buffer); ha->optrom_buffer = NULL; ha->optrom_state = QLA_SWAITING; + mutex_unlock(&ha->optrom_mutex); 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_write_i2c(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_i2c_access *i2c = (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, i2c, sizeof(*i2c)); + + memcpy(sfp, i2c->buffer, i2c->length); + rval = qla2x00_write_sfp(vha, sfp_dma, sfp, + i2c->device, i2c->offset, i2c->length, i2c->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_read_i2c(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_i2c_access *i2c = (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, i2c, sizeof(*i2c)); + + rval = qla2x00_read_sfp(vha, sfp_dma, sfp, + i2c->device, i2c->offset, i2c->length, i2c->option); + + if (rval) { + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_MAILBOX; + goto dealloc; + } + + memcpy(i2c->buffer, sfp, i2c->length); + sg_copy_from_buffer(bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, i2c, sizeof(*i2c)); + + 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(*i2c); + bsg_job->reply->result = DID_OK << 16; + bsg_job->job_done(bsg_job); + + return 0; +} + +static int +qla24xx_process_bidir_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; + uint16_t thread_id; + uint32_t rval = EXT_STATUS_OK; + uint16_t req_sg_cnt = 0; + uint16_t rsp_sg_cnt = 0; + uint16_t nextlid = 0; + uint32_t tot_dsds; + srb_t *sp = NULL; + uint32_t req_data_len = 0; + uint32_t rsp_data_len = 0; + + /* Check the type of the adapter */ + if (!IS_BIDI_CAPABLE(ha)) { + ql_log(ql_log_warn, vha, 0x70a0, + "This adapter is not supported\n"); + rval = EXT_STATUS_NOT_SUPPORTED; + goto done; + } + + 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)) { + rval = EXT_STATUS_BUSY; + goto done; + } + + /* Check if host is online */ + if (!vha->flags.online) { + ql_log(ql_log_warn, vha, 0x70a1, + "Host is not online\n"); + rval = EXT_STATUS_DEVICE_OFFLINE; + goto done; + } + + /* Check if cable is plugged in or not */ + if (vha->device_flags & DFLG_NO_CABLE) { + ql_log(ql_log_warn, vha, 0x70a2, + "Cable is unplugged...\n"); + rval = EXT_STATUS_INVALID_CFG; + goto done; + } + + /* Check if the switch is connected or not */ + if (ha->current_topology != ISP_CFG_F) { + ql_log(ql_log_warn, vha, 0x70a3, + "Host is not connected to the switch\n"); + rval = EXT_STATUS_INVALID_CFG; + goto done; + } + + /* Check if operating mode is P2P */ + if (ha->operating_mode != P2P) { + ql_log(ql_log_warn, vha, 0x70a4, + "Host is operating mode is not P2p\n"); + rval = EXT_STATUS_INVALID_CFG; + goto done; + } + + thread_id = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; + + mutex_lock(&ha->selflogin_lock); + if (vha->self_login_loop_id == 0) { + /* Initialize all required fields of fcport */ + vha->bidir_fcport.vha = vha; + vha->bidir_fcport.d_id.b.al_pa = vha->d_id.b.al_pa; + vha->bidir_fcport.d_id.b.area = vha->d_id.b.area; + vha->bidir_fcport.d_id.b.domain = vha->d_id.b.domain; + vha->bidir_fcport.loop_id = vha->loop_id; + + if (qla2x00_fabric_login(vha, &(vha->bidir_fcport), &nextlid)) { + ql_log(ql_log_warn, vha, 0x70a7, + "Failed to login port %06X for bidirectional IOCB\n", + vha->bidir_fcport.d_id.b24); + mutex_unlock(&ha->selflogin_lock); + rval = EXT_STATUS_MAILBOX; + goto done; + } + vha->self_login_loop_id = nextlid - 1; + + } + /* Assign the self login loop id to fcport */ + mutex_unlock(&ha->selflogin_lock); + + vha->bidir_fcport.loop_id = vha->self_login_loop_id; + + 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 = EXT_STATUS_NO_MEMORY; + 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) { + rval = EXT_STATUS_NO_MEMORY; + goto done_unmap_req_sg; + } + + if ((req_sg_cnt != bsg_job->request_payload.sg_cnt) || + (rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) { + ql_dbg(ql_dbg_user, vha, 0x70a9, + "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 = EXT_STATUS_NO_MEMORY; + goto done_unmap_sg; + } + + if (req_data_len != rsp_data_len) { + rval = EXT_STATUS_BUSY; + ql_log(ql_log_warn, vha, 0x70aa, + "req_data_len != rsp_data_len\n"); + goto done_unmap_sg; + } + + req_data_len = bsg_job->request_payload.payload_len; + rsp_data_len = bsg_job->reply_payload.payload_len; + + + /* Alloc SRB structure */ + sp = qla2x00_get_sp(vha, &(vha->bidir_fcport), GFP_KERNEL); + if (!sp) { + ql_dbg(ql_dbg_user, vha, 0x70ac, + "Alloc SRB structure failed\n"); + rval = EXT_STATUS_NO_MEMORY; + goto done_unmap_sg; + } + + /*Populate srb->ctx with bidir ctx*/ + sp->u.bsg_job = bsg_job; + sp->free = qla2x00_bsg_sp_free; + sp->type = SRB_BIDI_CMD; + sp->done = qla2x00_bsg_job_done; + + /* Add the read and write sg count */ + tot_dsds = rsp_sg_cnt + req_sg_cnt; + + rval = qla2x00_start_bidir(sp, vha, tot_dsds); + if (rval != EXT_STATUS_OK) + goto done_free_srb; + /* the bsg request will be completed in the interrupt handler */ + return rval; + +done_free_srb: + mempool_free(sp, ha->srb_mempool); +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); +done: + + /* Return an error vendor specific response + * and complete the bsg request + */ + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = rval; + bsg_job->reply_len = sizeof(struct fc_bsg_reply); + bsg_job->reply->reply_payload_rcv_len = 0; + bsg_job->reply->result = (DID_OK) << 16; + bsg_job->job_done(bsg_job); + /* Always return success, vendor rsp carries correct status */ + return 0; +} + +static int +qlafx00_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; + int rval = (DRIVER_ERROR << 16); + struct qla_mt_iocb_rqst_fx00 *piocb_rqst; + srb_t *sp; + int req_sg_cnt = 0, rsp_sg_cnt = 0; + struct fc_port *fcport; + char *type = "FC_BSG_HST_FX_MGMT"; + + /* Copy the IOCB specific information */ + piocb_rqst = (struct qla_mt_iocb_rqst_fx00 *) + &bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; + + /* Dump the vendor information */ + ql_dump_buffer(ql_dbg_user + ql_dbg_verbose , vha, 0x70cf, + (uint8_t *)piocb_rqst, sizeof(struct qla_mt_iocb_rqst_fx00)); + + if (!vha->flags.online) { + ql_log(ql_log_warn, vha, 0x70d0, + "Host is not online.\n"); + rval = -EIO; + goto done; + } + + if (piocb_rqst->flags & SRB_FXDISC_REQ_DMA_VALID) { + 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, 0x70c7, + "dma_map_sg return %d for request\n", req_sg_cnt); + rval = -ENOMEM; + goto done; + } + } + + if (piocb_rqst->flags & SRB_FXDISC_RESP_DMA_VALID) { + 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, 0x70c8, + "dma_map_sg return %d for reply\n", rsp_sg_cnt); + rval = -ENOMEM; + goto done_unmap_req_sg; + } + } + + ql_dbg(ql_dbg_user, vha, 0x70c9, + "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); + + /* 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, 0x70ca, + "Failed to allocate fcport.\n"); + rval = -ENOMEM; + goto done_unmap_rsp_sg; + } + + /* Alloc SRB structure */ + sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); + if (!sp) { + ql_log(ql_log_warn, vha, 0x70cb, + "qla2x00_get_sp failed.\n"); + rval = -ENOMEM; + goto done_free_fcport; + } + + /* Initialize all required fields of fcport */ + fcport->vha = vha; + fcport->loop_id = piocb_rqst->dataword; + + sp->type = SRB_FXIOCB_BCMD; + sp->name = "bsg_fx_mgmt"; + 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, 0x70cc, + "bsg rqst type: %s fx_mgmt_type: %x id=%x\n", + type, piocb_rqst->func_type, fcport->loop_id); + + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x70cd, + "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_rsp_sg: + if (piocb_rqst->flags & SRB_FXDISC_RESP_DMA_VALID) + 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: + if (piocb_rqst->flags & SRB_FXDISC_REQ_DMA_VALID) + dma_unmap_sg(&ha->pdev->dev, + bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); + +done: + return rval; +} + +static int +qla26xx_serdes_op(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_serdes_reg sr; + + memset(&sr, 0, sizeof(sr)); + + sg_copy_to_buffer(bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, &sr, sizeof(sr)); + + switch (sr.cmd) { + case INT_SC_SERDES_WRITE_REG: + rval = qla2x00_write_serdes_word(vha, sr.addr, sr.val); + bsg_job->reply->reply_payload_rcv_len = 0; + break; + case INT_SC_SERDES_READ_REG: + rval = qla2x00_read_serdes_word(vha, sr.addr, &sr.val); + sg_copy_from_buffer(bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, &sr, sizeof(sr)); + bsg_job->reply->reply_payload_rcv_len = sizeof(sr); + break; + default: + ql_dbg(ql_dbg_user, vha, 0x708c, + "Unknown serdes cmd %x.\n", sr.cmd); + rval = -EINVAL; + break; + } + + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = + rval ? EXT_STATUS_MAILBOX : 0; + + 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 +qla8044_serdes_op(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_serdes_reg_ex sr; + + memset(&sr, 0, sizeof(sr)); + + sg_copy_to_buffer(bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, &sr, sizeof(sr)); + + switch (sr.cmd) { + case INT_SC_SERDES_WRITE_REG: + rval = qla8044_write_serdes_word(vha, sr.addr, sr.val); + bsg_job->reply->reply_payload_rcv_len = 0; + break; + case INT_SC_SERDES_READ_REG: + rval = qla8044_read_serdes_word(vha, sr.addr, &sr.val); + sg_copy_from_buffer(bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, &sr, sizeof(sr)); + bsg_job->reply->reply_payload_rcv_len = sizeof(sr); + break; + default: + ql_dbg(ql_dbg_user, vha, 0x70cf, + "Unknown serdes cmd %x.\n", sr.cmd); + rval = -EINVAL; + break; + } + + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = + rval ? EXT_STATUS_MAILBOX : 0; + + 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]) { @@ -1453,9 +2137,34 @@ qla2x00_process_vendor_specific(struct fc_bsg_job *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); + + case QL_VND_WRITE_I2C: + return qla2x00_write_i2c(bsg_job); + + case QL_VND_READ_I2C: + return qla2x00_read_i2c(bsg_job); + + case QL_VND_DIAG_IO_CMD: + return qla24xx_process_bidir_cmd(bsg_job); + + case QL_VND_FX00_MGMT_CMD: + return qlafx00_mgmt_cmd(bsg_job); + + case QL_VND_SERDES_OP: + return qla26xx_serdes_op(bsg_job); + + case QL_VND_SERDES_OP_EX: + return qla8044_serdes_op(bsg_job); + default: - bsg_job->reply->result = (DID_ERROR << 16); - bsg_job->job_done(bsg_job); return -ENOSYS; } } @@ -1464,6 +2173,33 @@ 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); + 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: @@ -1480,7 +2216,7 @@ qla24xx_bsg_request(struct fc_bsg_job *bsg_job) case FC_BSG_HST_DEL_RPORT: case FC_BSG_RPT_CT: default: - DEBUG2(printk("qla2xxx: unsupported BSG request\n")); + ql_log(ql_log_warn, vha, 0x705a, "Unsupported BSG request.\n"); break; } return ret; @@ -1495,7 +2231,6 @@ qla24xx_bsg_timeout(struct fc_bsg_job *bsg_job) int cnt, que; unsigned long flags; struct req_que *req; - struct srb_ctx *sp_bsg; /* find the bsg job from the active list of commands */ spin_lock_irqsave(&ha->hardware_lock, flags); @@ -1504,27 +2239,25 @@ qla24xx_bsg_timeout(struct fc_bsg_job *bsg_job) if (!req) continue; - for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { + for (cnt = 1; cnt < req->num_outstanding_cmds; cnt++) { sp = req->outstanding_cmds[cnt]; if (sp) { - sp_bsg = sp->ctx; - - if (((sp_bsg->type == SRB_CT_CMD) || - (sp_bsg->type == SRB_ELS_CMD_HST)) - && (sp_bsg->u.bsg_job == bsg_job)) { + if (((sp->type == SRB_CT_CMD) || + (sp->type == SRB_ELS_CMD_HST) || + (sp->type == SRB_FXIOCB_BCMD)) + && (sp->u.bsg_job == bsg_job)) { + req->outstanding_cmds[cnt] = NULL; spin_unlock_irqrestore(&ha->hardware_lock, flags); if (ha->isp_ops->abort_command(sp)) { - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld): mbx " - "abort_command failed\n", - vha->host_no)); + ql_log(ql_log_warn, vha, 0x7089, + "mbx abort_command " + "failed.\n"); bsg_job->req->errors = bsg_job->reply->result = -EIO; } else { - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld): mbx " - "abort_command success\n", - vha->host_no)); + ql_dbg(ql_dbg_user, vha, 0x708a, + "mbx abort_command " + "success.\n"); bsg_job->req->errors = bsg_job->reply->result = 0; } @@ -1535,16 +2268,12 @@ qla24xx_bsg_timeout(struct fc_bsg_job *bsg_job) } } spin_unlock_irqrestore(&ha->hardware_lock, flags); - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld) SRB not found to abort\n", vha->host_no)); + 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); - kfree(sp->ctx); - mempool_free(sp, ha->srb_mempool); + sp->free(vha, sp); return 0; } diff --git a/drivers/scsi/qla2xxx/qla_bsg.h b/drivers/scsi/qla2xxx/qla_bsg.h index 0f0f54e35f0..d38f9efa56f 100644 --- a/drivers/scsi/qla2xxx/qla_bsg.h +++ b/drivers/scsi/qla2xxx/qla_bsg.h @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -16,14 +16,47 @@ #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 +#define QL_VND_DIAG_IO_CMD 0x0A +#define QL_VND_WRITE_I2C 0x10 +#define QL_VND_READ_I2C 0x11 +#define QL_VND_FX00_MGMT_CMD 0x12 +#define QL_VND_SERDES_OP 0x13 +#define QL_VND_SERDES_OP_EX 0x14 + +/* BSG Vendor specific subcode returns */ +#define EXT_STATUS_OK 0 +#define EXT_STATUS_ERR 1 +#define EXT_STATUS_BUSY 2 +#define EXT_STATUS_INVALID_PARAM 6 +#define EXT_STATUS_DATA_OVERRUN 7 +#define EXT_STATUS_DATA_UNDERRUN 8 +#define EXT_STATUS_MAILBOX 11 +#define EXT_STATUS_NO_MEMORY 17 +#define EXT_STATUS_DEVICE_OFFLINE 22 + +/* + * To support bidirectional iocb + * BSG Vendor specific returns + */ +#define EXT_STATUS_NOT_SUPPORTED 27 +#define EXT_STATUS_INVALID_CFG 28 +#define EXT_STATUS_DMA_ERR 29 +#define EXT_STATUS_TIMEOUT 30 +#define EXT_STATUS_THREAD_FAILED 31 +#define EXT_STATUS_DATA_CMP_FAILED 32 /* BSG definations for interpreting CommandSent field */ #define INT_DEF_LB_LOOPBACK_CMD 0 #define INT_DEF_LB_ECHO_CMD 1 /* Loopback related definations */ +#define INTERNAL_LOOPBACK 0xF1 #define EXTERNAL_LOOPBACK 0xF2 #define ENABLE_INTERNAL_LOOPBACK 0x02 +#define ENABLE_EXTERNAL_LOOPBACK 0x04 #define INTERNAL_LOOPBACK_MASK 0x000E #define MAX_ELS_FRAME_PAYLOAD 252 #define ELS_OPCODE_BYTE 0x10 @@ -141,4 +174,62 @@ struct qla_port_param { 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; + +struct qla_i2c_access { + uint16_t device; + uint16_t offset; + uint16_t option; + uint16_t length; + uint8_t buffer[0x40]; +} __packed; + +/* 26xx serdes register interface */ + +/* serdes reg commands */ +#define INT_SC_SERDES_READ_REG 1 +#define INT_SC_SERDES_WRITE_REG 2 + +struct qla_serdes_reg { + uint16_t cmd; + uint16_t addr; + uint16_t val; +} __packed; + +struct qla_serdes_reg_ex { + uint16_t cmd; + uint32_t addr; + uint32_t val; +} __packed; + #endif diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index c53719a9a74..c72ee97bf3f 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -1,13 +1,87 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2014 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 | 0x017d | 0x004b,0x0141 | + * | | | 0x0144,0x0146 | + * | | | 0x015b-0x0160 | + * | | | 0x016e-0x0170 | + * | Mailbox commands | 0x118d | 0x1018-0x1019 | + * | | | 0x10ca | + * | | | 0x1115-0x1116 | + * | | | 0x111a-0x111b | + * | | | 0x1155-0x1158 | + * | Device Discovery | 0x2095 | 0x2020-0x2022, | + * | | | 0x2011-0x2012, | + * | | | 0x2016 | + * | Queue Command and IO tracing | 0x3059 | 0x3006-0x300b | + * | | | 0x3027-0x3028 | + * | | | 0x303d-0x3041 | + * | | | 0x302d,0x3033 | + * | | | 0x3036,0x3038 | + * | | | 0x303a | + * | DPC Thread | 0x4023 | 0x4002,0x4013 | + * | Async Events | 0x5087 | 0x502b-0x502f | + * | | | 0x5047,0x5052 | + * | | | 0x5084,0x5075 | + * | | | 0x503d,0x5044 | + * | | | 0x507b | + * | Timer Routines | 0x6012 | | + * | User Space Interactions | 0x70e2 | 0x7018,0x702e | + * | | | 0x7020,0x7024 | + * | | | 0x7039,0x7045 | + * | | | 0x7073-0x7075 | + * | | | 0x70a5-0x70a6 | + * | | | 0x70a8,0x70ab | + * | | | 0x70ad-0x70ae | + * | | | 0x70d7-0x70db | + * | | | 0x70de-0x70df | + * | Task Management | 0x803d | 0x8000,0x800b | + * | | | 0x8019 | + * | | | 0x8025,0x8026 | + * | | | 0x8031,0x8032 | + * | | | 0x8039,0x803c | + * | AER/EEH | 0x9011 | | + * | Virtual Port | 0xa007 | | + * | ISP82XX Specific | 0xb157 | 0xb002,0xb024 | + * | | | 0xb09e,0xb0ae | + * | | | 0xb0c3,0xb0c6 | + * | | | 0xb0e0-0xb0ef | + * | | | 0xb085,0xb0dc | + * | | | 0xb107,0xb108 | + * | | | 0xb111,0xb11e | + * | | | 0xb12c,0xb12d | + * | | | 0xb13a,0xb142 | + * | | | 0xb13c-0xb140 | + * | | | 0xb149 | + * | MultiQ | 0xc00c | | + * | Misc | 0xd212 | 0xd017-0xd019 | + * | | | 0xd020 | + * | | | 0xd030-0xd0ff | + * | | | 0xd101-0xd1fe | + * | | | 0xd213-0xd2fe | + * | Target Mode | 0xe078 | | + * | Target Mode Management | 0xf072 | 0xf002-0xf003 | + * | | | 0xf046-0xf049 | + * | Target Mode Task Management | 0x1000b | | + * ---------------------------------------------------------------------- + */ + #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) { @@ -39,7 +113,87 @@ qla2xxx_copy_queues(struct qla_hw_data *ha, void *ptr) return ptr + (rsp->length * sizeof(response_t)); } -static int +int +qla27xx_dump_mpi_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, mb1; + 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(®->mailbox0, MBC_LOAD_DUMP_MPI_RAM); + 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(®->mailbox1, LSW(addr)); + WRT_REG_WORD(®->mailbox8, MSW(addr)); + + WRT_REG_WORD(®->mailbox2, MSW(dump_dma)); + WRT_REG_WORD(®->mailbox3, LSW(dump_dma)); + WRT_REG_WORD(®->mailbox6, MSW(MSD(dump_dma))); + WRT_REG_WORD(®->mailbox7, LSW(MSD(dump_dma))); + + WRT_REG_WORD(®->mailbox4, MSW(dwords)); + WRT_REG_WORD(®->mailbox5, LSW(dwords)); + + WRT_REG_WORD(®->mailbox9, 0); + WRT_REG_DWORD(®->hccr, HCCRX_SET_HOST_INT); + + ha->flags.mbox_int = 0; + for (timer = 6000000; timer; timer--) { + /* Check for pending interrupts. */ + stat = RD_REG_DWORD(®->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(®->mailbox0); + mb1 = RD_REG_WORD(®->mailbox1); + + WRT_REG_DWORD(®->hccr, + HCCRX_CLR_RISC_INT); + RD_REG_DWORD(®->hccr); + break; + } + + /* Clear this intr; it wasn't a mailbox intr */ + WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); + RD_REG_DWORD(®->hccr); + } + udelay(5); + } + ha->flags.mbox_int = 1; + + if (test_and_clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags)) { + rval = mb0 & MBS_MASK; + for (idx = 0; idx < dwords; idx++) + ram[cnt + idx] = IS_QLA27XX(ha) ? + le32_to_cpu(dump[idx]) : swab32(dump[idx]); + } else { + rval = QLA_FUNCTION_FAILED; + } + } + + *nxt = rval == QLA_SUCCESS ? &ram[cnt] : NULL; + return rval; +} + +int qla24xx_dump_ram(struct qla_hw_data *ha, uint32_t addr, uint32_t *ram, uint32_t ram_dwords, void **nxt) { @@ -56,7 +210,7 @@ qla24xx_dump_ram(struct qla_hw_data *ha, uint32_t addr, uint32_t *ram, WRT_REG_WORD(®->mailbox0, MBC_DUMP_RISC_RAM_EXTENDED); clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); - dwords = GID_LIST_SIZE / 4; + 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) @@ -74,6 +228,7 @@ qla24xx_dump_ram(struct qla_hw_data *ha, uint32_t addr, uint32_t *ram, WRT_REG_WORD(®->mailbox5, LSW(dwords)); WRT_REG_DWORD(®->hccr, HCCRX_SET_HOST_INT); + ha->flags.mbox_int = 0; for (timer = 6000000; timer; timer--) { /* Check for pending interrupts. */ stat = RD_REG_DWORD(®->host_status); @@ -99,11 +254,13 @@ qla24xx_dump_ram(struct qla_hw_data *ha, uint32_t addr, uint32_t *ram, } udelay(5); } + ha->flags.mbox_int = 1; 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]); + ram[cnt + idx] = IS_QLA27XX(ha) ? + le32_to_cpu(dump[idx]) : swab32(dump[idx]); } else { rval = QLA_FUNCTION_FAILED; } @@ -124,9 +281,15 @@ qla24xx_dump_memory(struct qla_hw_data *ha, uint32_t *code_ram, if (rval != QLA_SUCCESS) return rval; + set_bit(RISC_SRAM_DUMP_CMPL, &ha->fw_dump_cap_flags); + /* External Memory. */ - return qla24xx_dump_ram(ha, 0x100000, *nxt, + rval = qla24xx_dump_ram(ha, 0x100000, *nxt, ha->fw_memory_size - 0x100000 + 1, nxt); + if (rval == QLA_SUCCESS) + set_bit(RISC_EXT_MEM_DUMP_CMPL, &ha->fw_dump_cap_flags); + + return rval; } static uint32_t * @@ -143,34 +306,30 @@ qla24xx_read_window(struct device_reg_24xx __iomem *reg, uint32_t iobase, return buf; } -static inline int -qla24xx_pause_risc(struct device_reg_24xx __iomem *reg) +void +qla24xx_pause_risc(struct device_reg_24xx __iomem *reg, struct qla_hw_data *ha) { - int rval = QLA_SUCCESS; - uint32_t cnt; - WRT_REG_DWORD(®->hccr, HCCRX_SET_RISC_PAUSE); - for (cnt = 30000; - ((RD_REG_DWORD(®->host_status) & HSRX_RISC_PAUSED) == 0) && - rval == QLA_SUCCESS; cnt--) { - if (cnt) - udelay(100); - else - rval = QLA_FUNCTION_TIMEOUT; - } - return rval; + /* 100 usec delay is sufficient enough for hardware to pause RISC */ + udelay(100); + if (RD_REG_DWORD(®->host_status) & HSRX_RISC_PAUSED) + set_bit(RISC_PAUSE_CMPL, &ha->fw_dump_cap_flags); } -static int +int qla24xx_soft_reset(struct qla_hw_data *ha) { int rval = QLA_SUCCESS; uint32_t cnt; - uint16_t mb0, wd; + uint16_t wd; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; - /* Reset RISC. */ + /* + * Reset RISC. The delay is dependent on system architecture. + * Driver can proceed with the reset sequence after waiting + * for a timeout period. + */ WRT_REG_DWORD(®->ctrl_status, CSRX_DMA_SHUTDOWN|MWB_4096_BYTES); for (cnt = 0; cnt < 30000; cnt++) { if ((RD_REG_DWORD(®->ctrl_status) & CSRX_DMA_ACTIVE) == 0) @@ -178,19 +337,14 @@ qla24xx_soft_reset(struct qla_hw_data *ha) udelay(10); } + if (!(RD_REG_DWORD(®->ctrl_status) & CSRX_DMA_ACTIVE)) + set_bit(DMA_SHUTDOWN_CMPL, &ha->fw_dump_cap_flags); WRT_REG_DWORD(®->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(®->mailbox0); - for (cnt = 10000 ; cnt && mb0; cnt--) { - udelay(5); - mb0 = (uint32_t) RD_REG_WORD(®->mailbox0); - barrier(); - } /* Wait for soft-reset to complete. */ for (cnt = 0; cnt < 30000; cnt++) { @@ -200,16 +354,21 @@ qla24xx_soft_reset(struct qla_hw_data *ha) udelay(10); } + if (!(RD_REG_DWORD(®->ctrl_status) & CSRX_ISP_SOFT_RESET)) + set_bit(ISP_RESET_CMPL, &ha->fw_dump_cap_flags); + WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_RESET); RD_REG_DWORD(®->hccr); /* PCI Posting. */ - for (cnt = 30000; RD_REG_WORD(®->mailbox0) != 0 && + for (cnt = 10000; RD_REG_WORD(®->mailbox0) != 0 && rval == QLA_SUCCESS; cnt--) { if (cnt) - udelay(100); + udelay(10); else rval = QLA_FUNCTION_TIMEOUT; } + if (rval == QLA_SUCCESS) + set_bit(RISC_RDY_AFT_RESET, &ha->fw_dump_cap_flags); return rval; } @@ -231,7 +390,7 @@ qla2xxx_dump_ram(struct qla_hw_data *ha, uint32_t addr, uint16_t *ram, WRT_MAILBOX_REG(ha, reg, 0, MBC_DUMP_RISC_RAM_EXTENDED); clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); - words = GID_LIST_SIZE / 2; + 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) @@ -342,7 +501,126 @@ qla25xx_copy_fce(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain) memcpy(iter_reg, ha->fce, ntohl(fcec->size)); - return iter_reg; + return (char *)iter_reg + ntohl(fcec->size); +} + +static inline void * +qla2xxx_copy_atioqueues(struct qla_hw_data *ha, void *ptr, + uint32_t **last_chain) +{ + struct qla2xxx_mqueue_chain *q; + struct qla2xxx_mqueue_header *qh; + uint32_t num_queues; + int que; + struct { + int length; + void *ring; + } aq, *aqp; + + if (!ha->tgt.atio_ring) + return ptr; + + num_queues = 1; + aqp = &aq; + aqp->length = ha->tgt.atio_q_length; + aqp->ring = ha->tgt.atio_ring; + + for (que = 0; que < num_queues; que++) { + /* aqp = ha->atio_q_map[que]; */ + 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) + + (aqp->length * sizeof(request_t))); + ptr += sizeof(struct qla2xxx_mqueue_chain); + + /* Add header. */ + qh = ptr; + qh->queue = __constant_htonl(TYPE_ATIO_QUEUE); + qh->number = htonl(que); + qh->size = htonl(aqp->length * sizeof(request_t)); + ptr += sizeof(struct qla2xxx_mqueue_header); + + /* Add data. */ + memcpy(ptr, aqp->ring, aqp->length * sizeof(request_t)); + + ptr += aqp->length * sizeof(request_t); + } + + return ptr; +} + +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 * @@ -351,9 +629,9 @@ 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; + device_reg_t __iomem *reg; - if (!ha->mqenable) + if (!ha->mqenable || IS_QLA83XX(ha) || IS_QLA27XX(ha)) return ptr; mq = ptr; @@ -365,31 +643,35 @@ qla25xx_copy_mq(struct qla_hw_data *ha, void *ptr, uint32_t **last_chain) 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); + reg = ISP_QUE_REG(ha, cnt); que_idx = cnt * 4; - mq->qregs[que_idx] = htonl(RD_REG_DWORD(®->req_q_in)); - mq->qregs[que_idx+1] = htonl(RD_REG_DWORD(®->req_q_out)); - mq->qregs[que_idx+2] = htonl(RD_REG_DWORD(®->rsp_q_in)); - mq->qregs[que_idx+3] = htonl(RD_REG_DWORD(®->rsp_q_out)); + mq->qregs[que_idx] = + htonl(RD_REG_DWORD(®->isp25mq.req_q_in)); + mq->qregs[que_idx+1] = + htonl(RD_REG_DWORD(®->isp25mq.req_q_out)); + mq->qregs[que_idx+2] = + htonl(RD_REG_DWORD(®->isp25mq.rsp_q_in)); + mq->qregs[que_idx+3] = + htonl(RD_REG_DWORD(®->isp25mq.rsp_q_out)); } return ptr + sizeof(struct qla2xxx_mq_chain); } -static void +void qla2xxx_dump_post_process(scsi_qla_host_t *vha, int rval) { struct qla_hw_data *ha = vha->hw; if (rval != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, - "Failed to dump firmware (%x)!!!\n", rval); + ql_log(ql_log_warn, vha, 0xd000, + "Failed to dump firmware (%x), dump status flags (0x%lx).\n", + rval, ha->fw_dump_cap_flags); ha->fw_dumped = 0; } else { - qla_printk(KERN_INFO, ha, - "Firmware dump saved to temp buffer (%ld/%p).\n", - vha->host_no, ha->fw_dump); + ql_log(ql_log_info, vha, 0xd001, + "Firmware dump saved to temp buffer (%ld/%p), dump status flags (0x%lx).\n", + vha->host_no, ha->fw_dump, ha->fw_dump_cap_flags); ha->fw_dumped = 1; qla2x00_post_uevent_work(vha, QLA_UEVENT_CODE_FW_DUMP); } @@ -419,15 +701,16 @@ qla2300_fw_dump(scsi_qla_host_t *vha, int hardware_locked) spin_lock_irqsave(&ha->hardware_lock, flags); if (!ha->fw_dump) { - qla_printk(KERN_WARNING, ha, - "No buffer available for dump!!!\n"); + ql_log(ql_log_warn, vha, 0xd002, + "No buffer available for dump.\n"); goto qla2300_fw_dump_failed; } if (ha->fw_dumped) { - qla_printk(KERN_WARNING, ha, - "Firmware has been previously dumped (%p) -- ignoring " - "request...\n", ha->fw_dump); + 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; @@ -582,15 +865,16 @@ qla2100_fw_dump(scsi_qla_host_t *vha, int hardware_locked) spin_lock_irqsave(&ha->hardware_lock, flags); if (!ha->fw_dump) { - qla_printk(KERN_WARNING, ha, - "No buffer available for dump!!!\n"); + ql_log(ql_log_warn, vha, 0xd004, + "No buffer available for dump.\n"); goto qla2100_fw_dump_failed; } if (ha->fw_dumped) { - qla_printk(KERN_WARNING, ha, - "Firmware has been previously dumped (%p) -- ignoring " - "request...\n", ha->fw_dump); + 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; @@ -767,27 +1051,31 @@ qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) struct qla24xx_fw_dump *fw; uint32_t ext_mem_cnt; void *nxt; + void *nxt_chain; + uint32_t *last_chain = NULL; struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); - if (IS_QLA82XX(ha)) + if (IS_P3P_TYPE(ha)) return; risc_address = ext_mem_cnt = 0; flags = 0; + ha->fw_dump_cap_flags = 0; if (!hardware_locked) spin_lock_irqsave(&ha->hardware_lock, flags); if (!ha->fw_dump) { - qla_printk(KERN_WARNING, ha, - "No buffer available for dump!!!\n"); + ql_log(ql_log_warn, vha, 0xd006, + "No buffer available for dump.\n"); goto qla24xx_fw_dump_failed; } if (ha->fw_dumped) { - qla_printk(KERN_WARNING, ha, - "Firmware has been previously dumped (%p) -- ignoring " - "request...\n", ha->fw_dump); + 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; @@ -795,10 +1083,11 @@ qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) fw->host_status = htonl(RD_REG_DWORD(®->host_status)); - /* Pause RISC. */ - rval = qla24xx_pause_risc(reg); - if (rval != QLA_SUCCESS) - goto qla24xx_fw_dump_failed_0; + /* + * Pause RISC. No need to track timeout, as resetting the chip + * is the right approach incase of pause timeout + */ + qla24xx_pause_risc(reg, ha); /* Host interface registers. */ dmp_reg = ®->flash_addr; @@ -984,6 +1273,16 @@ qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) qla24xx_copy_eft(ha, nxt); + nxt_chain = (void *)ha->fw_dump + ha->chain_offset; + nxt_chain = qla2xxx_copy_atioqueues(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); + qla24xx_fw_dump_failed_0: qla2xxx_dump_post_process(base_vha, rval); @@ -1012,20 +1311,22 @@ qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) risc_address = ext_mem_cnt = 0; flags = 0; + ha->fw_dump_cap_flags = 0; if (!hardware_locked) spin_lock_irqsave(&ha->hardware_lock, flags); if (!ha->fw_dump) { - qla_printk(KERN_WARNING, ha, - "No buffer available for dump!!!\n"); + ql_log(ql_log_warn, vha, 0xd008, + "No buffer available for dump.\n"); goto qla25xx_fw_dump_failed; } if (ha->fw_dumped) { - qla_printk(KERN_WARNING, ha, - "Firmware has been previously dumped (%p) -- ignoring " - "request...\n", ha->fw_dump); + 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; @@ -1034,10 +1335,11 @@ qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) fw->host_status = htonl(RD_REG_DWORD(®->host_status)); - /* Pause RISC. */ - rval = qla24xx_pause_risc(reg); - if (rval != QLA_SUCCESS) - goto qla25xx_fw_dump_failed_0; + /* + * Pause RISC. No need to track timeout, as resetting the chip + * is the right approach incase of pause timeout + */ + qla24xx_pause_risc(reg, ha); /* Host/Risc registers. */ iter_reg = fw->host_risc_reg; @@ -1286,15 +1588,20 @@ qla25xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) nxt = qla2xxx_copy_queues(ha, nxt); - nxt = qla24xx_copy_eft(ha, nxt); + qla24xx_copy_eft(ha, nxt); /* Chain entries -- started with MQ. */ - qla25xx_copy_fce(ha, nxt_chain, &last_chain); + nxt_chain = qla25xx_copy_fce(ha, nxt_chain, &last_chain); + nxt_chain = qla25xx_copy_mqueues(ha, nxt_chain, &last_chain); + nxt_chain = qla2xxx_copy_atioqueues(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); @@ -1323,20 +1630,22 @@ qla81xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) risc_address = ext_mem_cnt = 0; flags = 0; + ha->fw_dump_cap_flags = 0; if (!hardware_locked) spin_lock_irqsave(&ha->hardware_lock, flags); if (!ha->fw_dump) { - qla_printk(KERN_WARNING, ha, - "No buffer available for dump!!!\n"); + ql_log(ql_log_warn, vha, 0xd00a, + "No buffer available for dump.\n"); goto qla81xx_fw_dump_failed; } if (ha->fw_dumped) { - qla_printk(KERN_WARNING, ha, - "Firmware has been previously dumped (%p) -- ignoring " - "request...\n", ha->fw_dump); + 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; @@ -1344,10 +1653,11 @@ qla81xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) fw->host_status = htonl(RD_REG_DWORD(®->host_status)); - /* Pause RISC. */ - rval = qla24xx_pause_risc(reg); - if (rval != QLA_SUCCESS) - goto qla81xx_fw_dump_failed_0; + /* + * Pause RISC. No need to track timeout, as resetting the chip + * is the right approach incase of pause timeout + */ + qla24xx_pause_risc(reg, ha); /* Host/Risc registers. */ iter_reg = fw->host_risc_reg; @@ -1599,15 +1909,20 @@ qla81xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) nxt = qla2xxx_copy_queues(ha, nxt); - nxt = qla24xx_copy_eft(ha, nxt); + qla24xx_copy_eft(ha, nxt); /* Chain entries -- started with MQ. */ - qla25xx_copy_fce(ha, nxt_chain, &last_chain); + nxt_chain = qla25xx_copy_fce(ha, nxt_chain, &last_chain); + nxt_chain = qla25xx_copy_mqueues(ha, nxt_chain, &last_chain); + nxt_chain = qla2xxx_copy_atioqueues(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); @@ -1616,43 +1931,762 @@ qla81xx_fw_dump_failed: 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; + ha->fw_dump_cap_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(®->host_status)); + + /* + * Pause RISC. No need to track timeout, as resetting the chip + * is the right approach incase of pause timeout + */ + qla24xx_pause_risc(reg, ha); + + WRT_REG_DWORD(®->iobase_addr, 0x6000); + dmp_reg = ®->iobase_window; + reg_data = RD_REG_DWORD(dmp_reg); + WRT_REG_DWORD(dmp_reg, 0); + + dmp_reg = ®->unused_4_1[0]; + reg_data = RD_REG_DWORD(dmp_reg); + WRT_REG_DWORD(dmp_reg, 0); + + WRT_REG_DWORD(®->iobase_addr, 0x6010); + dmp_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(®->iobase_addr, 0x0F70); + RD_REG_DWORD(®->iobase_addr); + WRT_REG_DWORD(®->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(®->iobase_addr, 0x7C00); + RD_REG_DWORD(®->iobase_addr); + WRT_REG_DWORD(®->iobase_window, 0x01); + dmp_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(®->iobase_window)); + + WRT_REG_DWORD(®->iobase_window, 0x00); + RD_REG_DWORD(®->iobase_window); + + /* Host interface registers. */ + dmp_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(®->ictrl, 0); + RD_REG_DWORD(®->ictrl); + + /* Shadow registers. */ + WRT_REG_DWORD(®->iobase_addr, 0x0F70); + RD_REG_DWORD(®->iobase_addr); + WRT_REG_DWORD(®->iobase_select, 0xB0000000); + fw->shadow_reg[0] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0100000); + fw->shadow_reg[1] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0200000); + fw->shadow_reg[2] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0300000); + fw->shadow_reg[3] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0400000); + fw->shadow_reg[4] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0500000); + fw->shadow_reg[5] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0600000); + fw->shadow_reg[6] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0700000); + fw->shadow_reg[7] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0800000); + fw->shadow_reg[8] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0900000); + fw->shadow_reg[9] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + WRT_REG_DWORD(®->iobase_select, 0xB0A00000); + fw->shadow_reg[10] = htonl(RD_REG_DWORD(®->iobase_sdata)); + + /* RISC I/O register. */ + WRT_REG_DWORD(®->iobase_addr, 0x0010); + fw->risc_io_reg = htonl(RD_REG_DWORD(®->iobase_window)); + + /* Mailbox registers. */ + mbx_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 = ®->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 = ®->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 = ®->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(®->hccr, HCCRX_SET_RISC_RESET); + RD_REG_DWORD(®->hccr); + + WRT_REG_DWORD(®->hccr, HCCRX_REL_RISC_PAUSE); + RD_REG_DWORD(®->hccr); + + WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_RESET); + RD_REG_DWORD(®->hccr); + + for (cnt = 30000; cnt && (RD_REG_WORD(®->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 { + set_bit(RISC_RDY_AFT_RESET, &ha->fw_dump_cap_flags); + 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); + + 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); + nxt_chain = qla2xxx_copy_atioqueues(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 -qla2x00_dump_regs(scsi_qla_host_t *vha) +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 available, + * 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 available, + * 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; - mbx_reg = IS_FWI2_CAPABLE(ha) ? ®24->mailbox0: - MAILBOX_REG(ha, reg, 0); + if (!ql_mask_match(level)) + return; + + if (IS_P3P_TYPE(ha)) + mbx_reg = ®82->mailbox_in[0]; + else if (IS_FWI2_CAPABLE(ha)) + mbx_reg = ®24->mailbox0; + else + mbx_reg = MAILBOX_REG(ha, reg, 0); - printk("Mailbox registers:\n"); + ql_dbg(level, vha, id, "Mailbox registers:\n"); for (i = 0; i < 6; i++) - printk("scsi(%ld): mbox %d 0x%04x \n", vha->host_no, i, - RD_REG_WORD(mbx_reg++)); + ql_dbg(level, vha, id, + "mbox[%d] 0x%04x\n", i, RD_REG_WORD(mbx_reg++)); } void -qla2x00_dump_buffer(uint8_t * b, uint32_t size) +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; - printk(" 0 1 2 3 4 5 6 7 8 9 " - "Ah Bh Ch Dh Eh Fh\n"); - printk("----------------------------------------" - "----------------------\n"); + 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); + printk("%02x", (uint32_t) c); cnt++; if (!(cnt % 16)) printk("\n"); @@ -1660,65 +2694,5 @@ qla2x00_dump_buffer(uint8_t * b, uint32_t size) printk(" "); } if (cnt % 16) - printk("\n"); -} - -void -qla2x00_dump_buffer_zipped(uint8_t *b, uint32_t size) -{ - uint32_t cnt; - uint8_t c; - uint8_t last16[16], cur16[16]; - uint32_t lc = 0, num_same16 = 0, j; - - printk(KERN_DEBUG " 0 1 2 3 4 5 6 7 8 9 " - "Ah Bh Ch Dh Eh Fh\n"); - printk(KERN_DEBUG "----------------------------------------" - "----------------------\n"); - - for (cnt = 0; cnt < size;) { - c = *b++; - - cur16[lc++] = c; - - cnt++; - if (cnt % 16) - continue; - - /* We have 16 now */ - lc = 0; - if (num_same16 == 0) { - memcpy(last16, cur16, 16); - num_same16++; - continue; - } - if (memcmp(cur16, last16, 16) == 0) { - num_same16++; - continue; - } - for (j = 0; j < 16; j++) - printk(KERN_DEBUG "%02x ", (uint32_t)last16[j]); - printk(KERN_DEBUG "\n"); - - if (num_same16 > 1) - printk(KERN_DEBUG "> prev pattern repeats (%u)" - "more times\n", num_same16-1); - memcpy(last16, cur16, 16); - num_same16 = 1; - } - - if (num_same16) { - for (j = 0; j < 16; j++) - printk(KERN_DEBUG "%02x ", (uint32_t)last16[j]); - printk(KERN_DEBUG "\n"); - - if (num_same16 > 1) - printk(KERN_DEBUG "> prev pattern repeats (%u)" - "more times\n", num_same16-1); - } - if (lc) { - for (j = 0; j < lc; j++) - printk(KERN_DEBUG "%02x ", (uint32_t)cur16[j]); - printk(KERN_DEBUG "\n"); - } + ql_dbg(level, vha, id, "\n"); } diff --git a/drivers/scsi/qla2xxx/qla_dbg.h b/drivers/scsi/qla2xxx/qla_dbg.h index 930414541ec..e1fc4e66966 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.h +++ b/drivers/scsi/qla2xxx/qla_dbg.h @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -8,146 +8,6 @@ #include "qla_def.h" /* - * Driver debug definitions. - */ -/* #define QL_DEBUG_LEVEL_1 */ /* Output register accesses to COM1 */ -/* #define QL_DEBUG_LEVEL_2 */ /* Output error msgs to COM1 */ -/* #define QL_DEBUG_LEVEL_3 */ /* Output function trace msgs to COM1 */ -/* #define QL_DEBUG_LEVEL_4 */ /* Output NVRAM trace msgs to COM1 */ -/* #define QL_DEBUG_LEVEL_5 */ /* Output ring trace msgs to COM1 */ -/* #define QL_DEBUG_LEVEL_6 */ /* Output WATCHDOG timer trace to COM1 */ -/* #define QL_DEBUG_LEVEL_7 */ /* Output RISC load trace msgs to COM1 */ -/* #define QL_DEBUG_LEVEL_8 */ /* Output ring saturation msgs to COM1 */ -/* #define QL_DEBUG_LEVEL_9 */ /* Output IOCTL trace msgs */ -/* #define QL_DEBUG_LEVEL_10 */ /* Output IOCTL error msgs */ -/* #define QL_DEBUG_LEVEL_11 */ /* Output Mbx Cmd trace msgs */ -/* #define QL_DEBUG_LEVEL_12 */ /* Output IP trace msgs */ -/* #define QL_DEBUG_LEVEL_13 */ /* Output fdmi function trace msgs */ -/* #define QL_DEBUG_LEVEL_14 */ /* Output RSCN trace msgs */ -/* #define QL_DEBUG_LEVEL_15 */ /* Output NPIV trace msgs */ -/* #define QL_DEBUG_LEVEL_16 */ /* Output ISP84XX trace msgs */ -/* #define QL_DEBUG_LEVEL_17 */ /* Output EEH trace messages */ -/* #define QL_DEBUG_LEVEL_18 */ /* Output T10 CRC trace messages */ - -/* -* Macros use for debugging the driver. -*/ - -#define DEBUG(x) do { if (ql2xextended_error_logging) { x; } } while (0) - -#if defined(QL_DEBUG_LEVEL_1) -#define DEBUG1(x) do {x;} while (0) -#else -#define DEBUG1(x) do {} while (0) -#endif - -#define DEBUG2(x) do { if (ql2xextended_error_logging) { x; } } while (0) -#define DEBUG2_3(x) do { if (ql2xextended_error_logging) { x; } } while (0) -#define DEBUG2_3_11(x) do { if (ql2xextended_error_logging) { x; } } while (0) -#define DEBUG2_9_10(x) do { if (ql2xextended_error_logging) { x; } } while (0) -#define DEBUG2_11(x) do { if (ql2xextended_error_logging) { x; } } while (0) -#define DEBUG2_13(x) do { if (ql2xextended_error_logging) { x; } } while (0) -#define DEBUG2_16(x) do { if (ql2xextended_error_logging) { x; } } while (0) -#define DEBUG2_17(x) do { if (ql2xextended_error_logging) { x; } } while (0) - -#if defined(QL_DEBUG_LEVEL_3) -#define DEBUG3(x) do {x;} while (0) -#define DEBUG3_11(x) do {x;} while (0) -#else -#define DEBUG3(x) do {} while (0) -#endif - -#if defined(QL_DEBUG_LEVEL_4) -#define DEBUG4(x) do {x;} while (0) -#else -#define DEBUG4(x) do {} while (0) -#endif - -#if defined(QL_DEBUG_LEVEL_5) -#define DEBUG5(x) do {x;} while (0) -#else -#define DEBUG5(x) do {} while (0) -#endif - -#if defined(QL_DEBUG_LEVEL_7) -#define DEBUG7(x) do {x;} while (0) -#else -#define DEBUG7(x) do {} while (0) -#endif - -#if defined(QL_DEBUG_LEVEL_9) -#define DEBUG9(x) do {x;} while (0) -#define DEBUG9_10(x) do {x;} while (0) -#else -#define DEBUG9(x) do {} while (0) -#endif - -#if defined(QL_DEBUG_LEVEL_10) -#define DEBUG10(x) do {x;} while (0) -#define DEBUG9_10(x) do {x;} while (0) -#else -#define DEBUG10(x) do {} while (0) - #if !defined(DEBUG9_10) - #define DEBUG9_10(x) do {} while (0) - #endif -#endif - -#if defined(QL_DEBUG_LEVEL_11) -#define DEBUG11(x) do{x;} while(0) -#if !defined(DEBUG3_11) -#define DEBUG3_11(x) do{x;} while(0) -#endif -#else -#define DEBUG11(x) do{} while(0) - #if !defined(QL_DEBUG_LEVEL_3) - #define DEBUG3_11(x) do{} while(0) - #endif -#endif - -#if defined(QL_DEBUG_LEVEL_12) -#define DEBUG12(x) do {x;} while (0) -#else -#define DEBUG12(x) do {} while (0) -#endif - -#if defined(QL_DEBUG_LEVEL_13) -#define DEBUG13(x) do {x;} while (0) -#else -#define DEBUG13(x) do {} while (0) -#endif - -#if defined(QL_DEBUG_LEVEL_14) -#define DEBUG14(x) do {x;} while (0) -#else -#define DEBUG14(x) do {} while (0) -#endif - -#if defined(QL_DEBUG_LEVEL_15) -#define DEBUG15(x) do {x;} while (0) -#else -#define DEBUG15(x) do {} while (0) -#endif - -#if defined(QL_DEBUG_LEVEL_16) -#define DEBUG16(x) do {x;} while (0) -#else -#define DEBUG16(x) do {} while (0) -#endif - -#if defined(QL_DEBUG_LEVEL_17) -#define DEBUG17(x) do {x;} while (0) -#else -#define DEBUG17(x) do {} while (0) -#endif - -#if defined(QL_DEBUG_LEVEL_18) -#define DEBUG18(x) do {if (ql2xextended_error_logging) x; } while (0) -#else -#define DEBUG18(x) do {} while (0) -#endif - - -/* * Firmware Dump structure definition */ @@ -305,6 +165,54 @@ struct qla81xx_fw_dump { 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)) @@ -332,9 +240,24 @@ struct qla2xxx_mq_chain { uint32_t qregs[4 * QLA_MQ_SIZE]; }; +struct qla2xxx_mqueue_header { + uint32_t queue; +#define TYPE_REQUEST_QUEUE 0x1 +#define TYPE_RESPONSE_QUEUE 0x2 +#define TYPE_ATIO_QUEUE 0x3 + 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 { @@ -368,5 +291,68 @@ struct qla2xxx_fw_dump { 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 + */ +#define ql_dbg_verbose 0x00008000 /* More verbosity for each level + * This is to be used with other levels where + * more verbosity is required. It might not + * be applicable to all the levels. + */ +#define ql_dbg_tgt 0x00004000 /* Target mode */ +#define ql_dbg_tgt_mgt 0x00002000 /* Target mode management */ +#define ql_dbg_tgt_tmr 0x00001000 /* Target mode task management */ + +extern int qla27xx_dump_mpi_ram(struct qla_hw_data *, uint32_t, uint32_t *, + uint32_t, void **); +extern int qla24xx_dump_ram(struct qla_hw_data *, uint32_t, uint32_t *, + uint32_t, void **); +extern void qla24xx_pause_risc(struct device_reg_24xx __iomem *, + struct qla_hw_data *); +extern int qla24xx_soft_reset(struct qla_hw_data *); diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index cc5a79259d3..de5d0ae19d8 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -35,8 +35,10 @@ #include "qla_bsg.h" #include "qla_nx.h" +#include "qla_nx2.h" #define QLA2XXX_DRIVER_NAME "qla2xxx" #define QLA2XXX_APIDEV "ql2xapidev" +#define QLA2XXX_MANUFACTURER "QLogic Corporation" /* * We have MAILBOX_REGISTER_COUNT sized arrays in a few places, @@ -44,6 +46,7 @@ * 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 @@ -114,6 +117,82 @@ #define WRT_REG_DWORD(addr, data) writel(data,addr) /* + * ISP83XX specific remote register addresses + */ +#define QLA83XX_LED_PORT0 0x00201320 +#define QLA83XX_LED_PORT1 0x00201328 +#define QLA83XX_IDC_DEV_STATE 0x22102384 +#define QLA83XX_IDC_MAJOR_VERSION 0x22102380 +#define QLA83XX_IDC_MINOR_VERSION 0x22102398 +#define QLA83XX_IDC_DRV_PRESENCE 0x22102388 +#define QLA83XX_IDC_DRIVER_ACK 0x2210238c +#define QLA83XX_IDC_CONTROL 0x22102390 +#define QLA83XX_IDC_AUDIT 0x22102394 +#define QLA83XX_IDC_LOCK_RECOVERY 0x2210239c +#define QLA83XX_DRIVER_LOCKID 0x22102104 +#define QLA83XX_DRIVER_LOCK 0x8111c028 +#define QLA83XX_DRIVER_UNLOCK 0x8111c02c +#define QLA83XX_FLASH_LOCKID 0x22102100 +#define QLA83XX_FLASH_LOCK 0x8111c010 +#define QLA83XX_FLASH_UNLOCK 0x8111c014 +#define QLA83XX_DEV_PARTINFO1 0x221023e0 +#define QLA83XX_DEV_PARTINFO2 0x221023e4 +#define QLA83XX_FW_HEARTBEAT 0x221020b0 +#define QLA83XX_PEG_HALT_STATUS1 0x221020a8 +#define QLA83XX_PEG_HALT_STATUS2 0x221020ac + +/* 83XX: Macros defining 8200 AEN Reason codes */ +#define IDC_DEVICE_STATE_CHANGE BIT_0 +#define IDC_PEG_HALT_STATUS_CHANGE BIT_1 +#define IDC_NIC_FW_REPORTED_FAILURE BIT_2 +#define IDC_HEARTBEAT_FAILURE BIT_3 + +/* 83XX: Macros defining 8200 AEN Error-levels */ +#define ERR_LEVEL_NON_FATAL 0x1 +#define ERR_LEVEL_RECOVERABLE_FATAL 0x2 +#define ERR_LEVEL_UNRECOVERABLE_FATAL 0x4 + +/* 83XX: Macros for IDC Version */ +#define QLA83XX_SUPP_IDC_MAJOR_VERSION 0x01 +#define QLA83XX_SUPP_IDC_MINOR_VERSION 0x0 + +/* 83XX: Macros for scheduling dpc tasks */ +#define QLA83XX_NIC_CORE_RESET 0x1 +#define QLA83XX_IDC_STATE_HANDLER 0x2 +#define QLA83XX_NIC_CORE_UNRECOVERABLE 0x3 + +/* 83XX: Macros for defining IDC-Control bits */ +#define QLA83XX_IDC_RESET_DISABLED BIT_0 +#define QLA83XX_IDC_GRACEFUL_RESET BIT_1 + +/* 83XX: Macros for different timeouts */ +#define QLA83XX_IDC_INITIALIZATION_TIMEOUT 30 +#define QLA83XX_IDC_RESET_ACK_TIMEOUT 10 +#define QLA83XX_MAX_LOCK_RECOVERY_WAIT (2 * HZ) + +/* 83XX: Macros for defining class in DEV-Partition Info register */ +#define QLA83XX_CLASS_TYPE_NONE 0x0 +#define QLA83XX_CLASS_TYPE_NIC 0x1 +#define QLA83XX_CLASS_TYPE_FCOE 0x2 +#define QLA83XX_CLASS_TYPE_ISCSI 0x3 + +/* 83XX: Macros for IDC Lock-Recovery stages */ +#define IDC_LOCK_RECOVERY_STAGE1 0x1 /* Stage1: Intent for + * lock-recovery + */ +#define IDC_LOCK_RECOVERY_STAGE2 0x2 /* Stage2: Perform lock-recovery */ + +/* 83XX: Macros for IDC Audit type */ +#define IDC_AUDIT_TIMESTAMP 0x0 /* IDC-AUDIT: Record timestamp of + * dev-state change to NEED-RESET + * or NEED-QUIESCENT + */ +#define IDC_AUDIT_COMPLETION 0x1 /* IDC-AUDIT: Record duration of + * reset-recovery completion is + * second + */ + +/* * The ISP2312 v2 chip cannot access the FLASH/GPIO registers via MMIO in an * 133Mhz slot. */ @@ -124,17 +203,18 @@ * Fibre Channel device definitions. */ #define WWN_SIZE 8 /* Size of WWPN, WWN & WWNN */ -#define MAX_FIBRE_DEVICES 512 +#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 LOOPID_MAP_SIZE (ha->max_fibre_devices) #define MAX_FIBRE_LUNS 0xFFFF -#define MAX_RSCN_COUNT 32 #define MAX_HOST_COUNT 16 /* * Host adapter default definitions. */ #define MAX_BUSES 1 /* We only have one bus today */ -#define MAX_TARGETS_2100 MAX_FIBRE_DEVICES -#define MAX_TARGETS_2200 MAX_FIBRE_DEVICES #define MIN_LUNS 8 #define MAX_LUNS MAX_FIBRE_LUNS #define MAX_CMDS_PER_LUN 255 @@ -166,7 +246,6 @@ #define MAX_CMDSZ 16 /* SCSI maximum CDB size. */ #include "qla_fw.h" - /* * Timeout timer counts in seconds */ @@ -175,8 +254,8 @@ #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 +#define DEFAULT_OUTSTANDING_COMMANDS 1024 +#define MIN_OUTSTANDING_COMMANDS 128 /* ISP request and response entry counts (37-65535) */ #define REQUEST_ENTRY_CNT_2100 128 /* Number of request entries. */ @@ -185,6 +264,8 @@ #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.*/ +#define ATIO_ENTRY_CNT_24XX 4096 /* Number of ATIO entries. */ +#define RESPONSE_ENTRY_CNT_FX00 256 /* Number of response entries.*/ struct req_que; @@ -201,20 +282,13 @@ struct sd_dif_tuple { /* * SCSI Request Block */ -typedef struct srb { - atomic_t ref_count; - struct fc_port *fcport; - uint32_t handle; - +struct srb_cmd { struct scsi_cmnd *cmd; /* Linux SCSI command pkt */ - - uint16_t flags; - uint32_t request_sense_length; + uint32_t fw_sense_length; uint8_t *request_sense_ptr; - void *ctx; -} srb_t; +}; /* * SRB flag definitions @@ -249,14 +323,43 @@ struct srb_iocb { uint32_t flags; uint32_t lun; uint32_t data; + struct completion comp; + __le16 comp_status; } tmf; + struct { +#define SRB_FXDISC_REQ_DMA_VALID BIT_0 +#define SRB_FXDISC_RESP_DMA_VALID BIT_1 +#define SRB_FXDISC_REQ_DWRD_VALID BIT_2 +#define SRB_FXDISC_RSP_DWRD_VALID BIT_3 +#define FXDISC_TIMEOUT 20 + uint8_t flags; + uint32_t req_len; + uint32_t rsp_len; + void *req_addr; + void *rsp_addr; + dma_addr_t req_dma_handle; + dma_addr_t rsp_dma_handle; + __le32 adapter_id; + __le32 adapter_id_hi; + __le16 req_func_type; + __le32 req_data; + __le32 req_data_extra; + __le32 result; + __le32 seq_number; + __le16 fw_flags; + struct completion fxiocb_comp; + __le32 reserved_0; + uint8_t reserved_1; + } fxiocb; + struct { + uint32_t cmd_hndl; + __le16 comp_status; + struct completion comp; + } abt; } u; struct timer_list timer; - - void (*done)(srb_t *); - void (*free)(srb_t *); - void (*timeout)(srb_t *); + void (*timeout)(void *); }; /* Values for srb_ctx type */ @@ -267,15 +370,46 @@ struct srb_iocb { #define SRB_CT_CMD 5 #define SRB_ADISC_CMD 6 #define SRB_TM_CMD 7 +#define SRB_SCSI_CMD 8 +#define SRB_BIDI_CMD 9 +#define SRB_FXIOCB_DCMD 10 +#define SRB_FXIOCB_BCMD 11 +#define SRB_ABT_CMD 12 -struct srb_ctx { + +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 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) +#define GET_FW_SENSE_LEN(sp) \ + (sp->u.scmd.fw_sense_length) +#define SET_FW_SENSE_LEN(sp, len) \ + (sp->u.scmd.fw_sense_length = len) struct msg_echo_lb { dma_addr_t send_dma; @@ -284,6 +418,7 @@ struct msg_echo_lb { uint16_t rsp_sg_cnt; uint16_t options; uint32_t transfer_size; + uint32_t iteration_count; }; /* @@ -446,14 +581,80 @@ struct device_reg_25xxmq { uint32_t req_q_out; uint32_t rsp_q_in; uint32_t rsp_q_out; + uint32_t atio_q_in; + uint32_t atio_q_out; }; + +struct device_reg_fx00 { + uint32_t mailbox0; /* 00 */ + uint32_t mailbox1; /* 04 */ + uint32_t mailbox2; /* 08 */ + uint32_t mailbox3; /* 0C */ + uint32_t mailbox4; /* 10 */ + uint32_t mailbox5; /* 14 */ + uint32_t mailbox6; /* 18 */ + uint32_t mailbox7; /* 1C */ + uint32_t mailbox8; /* 20 */ + uint32_t mailbox9; /* 24 */ + uint32_t mailbox10; /* 28 */ + uint32_t mailbox11; + uint32_t mailbox12; + uint32_t mailbox13; + uint32_t mailbox14; + uint32_t mailbox15; + uint32_t mailbox16; + uint32_t mailbox17; + uint32_t mailbox18; + uint32_t mailbox19; + uint32_t mailbox20; + uint32_t mailbox21; + uint32_t mailbox22; + uint32_t mailbox23; + uint32_t mailbox24; + uint32_t mailbox25; + uint32_t mailbox26; + uint32_t mailbox27; + uint32_t mailbox28; + uint32_t mailbox29; + uint32_t mailbox30; + uint32_t mailbox31; + uint32_t aenmailbox0; + uint32_t aenmailbox1; + uint32_t aenmailbox2; + uint32_t aenmailbox3; + uint32_t aenmailbox4; + uint32_t aenmailbox5; + uint32_t aenmailbox6; + uint32_t aenmailbox7; + /* Request Queue. */ + uint32_t req_q_in; /* A0 - Request Queue In-Pointer */ + uint32_t req_q_out; /* A4 - Request Queue Out-Pointer */ + /* Response Queue. */ + uint32_t rsp_q_in; /* A8 - Response Queue In-Pointer */ + uint32_t rsp_q_out; /* AC - Response Queue Out-Pointer */ + /* Init values shadowed on FW Up Event */ + uint32_t initval0; /* B0 */ + uint32_t initval1; /* B4 */ + uint32_t initval2; /* B8 */ + uint32_t initval3; /* BC */ + uint32_t initval4; /* C0 */ + uint32_t initval5; /* C4 */ + uint32_t initval6; /* C8 */ + uint32_t initval7; /* CC */ + uint32_t fwheartbeat; /* D0 */ + uint32_t pseudoaen; /* D4 */ +}; + + + 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; + struct device_reg_fx00 ispfx00; +} __iomem device_reg_t; #define ISP_REQ_Q_IN(ha, reg) \ (IS_QLA2100(ha) || IS_QLA2200(ha) ? \ @@ -472,6 +673,9 @@ typedef union { &(reg)->u.isp2100.mailbox5 : \ &(reg)->u.isp2300.rsp_q_out) +#define ISP_ATIO_Q_IN(vha) (vha->hw->tgt.atio_q_in) +#define ISP_ATIO_Q_OUT(vha) (vha->hw->tgt.atio_q_out) + #define MAILBOX_REG(ha, reg, num) \ (IS_QLA2100(ha) || IS_QLA2200(ha) ? \ (num < 8 ? \ @@ -505,6 +709,20 @@ typedef struct { #define IOCTL_CMD BIT_2 } mbx_cmd_t; +struct mbx_cmd_32 { + uint32_t out_mb; /* outbound from driver */ + uint32_t in_mb; /* Incoming from RISC */ + uint32_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 +}; + + #define MBX_TOV_SECONDS 30 /* @@ -580,6 +798,30 @@ typedef struct { #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. */ +#define MBA_FW_NOT_STARTED 0x8050 /* Firmware not started */ +#define MBA_FW_STARTING 0x8051 /* Firmware starting */ +#define MBA_FW_RESTART_CMPLT 0x8060 /* Firmware restart complete */ +#define MBA_INIT_REQUIRED 0x8061 /* Initialization required */ +#define MBA_SHUTDOWN_REQUESTED 0x8062 /* Shutdown Requested */ +#define MBA_FW_INIT_FAILURE 0x8401 /* Firmware initialization failure */ +#define MBA_MIRROR_LUN_CHANGE 0x8402 /* Mirror LUN State Change + Notification */ +#define MBA_FW_POLL_STATE 0x8600 /* Firmware in poll diagnostic state */ +#define MBA_FW_RESET_FCT 0x8502 /* Firmware reset factory defaults */ +#define MBA_FW_INIT_INPROGRESS 0x8500 /* Firmware boot in progress */ +/* 83XX FCoE specific */ +#define MBA_IDC_AEN 0x8200 /* FCoE: NIC Core state change AEN */ + +/* Interrupt type codes */ +#define INTR_ROM_MB_SUCCESS 0x1 +#define INTR_ROM_MB_FAILED 0x2 +#define INTR_MB_SUCCESS 0x10 +#define INTR_MB_FAILED 0x11 +#define INTR_ASYNC_EVENT 0x12 +#define INTR_RSP_QUE_UPDATE 0x13 +#define INTR_RSP_QUE_UPDATE_83XX 0x14 +#define INTR_ATIO_QUE_UPDATE 0x1C +#define INTR_ATIO_RSP_QUE_UPDATE 0x1D /* ISP mailbox loopback echo diagnostic error code */ #define MBS_LB_RESET 0x17 @@ -620,7 +862,6 @@ typedef struct { */ #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. */ @@ -651,12 +892,14 @@ typedef struct { #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_GET_RNID_PARAMS 0x5a /* Get RNID parameters */ +#define MBC_DATA_RATE 0x5d /* Data Rate */ #define MBC_INITIALIZE_FIRMWARE 0x60 /* Initialize firmware */ #define MBC_INITIATE_LIP 0x62 /* Initiate Loop */ /* Initialization Procedure */ @@ -685,8 +928,17 @@ typedef struct { #define MBC_LUN_RESET 0x7E /* Send LUN reset */ /* + * all the Mt. Rainier mailbox command codes that clash with FC/FCoE ones + * should be defined with MBC_MR_* + */ +#define MBC_MR_DRV_SHUTDOWN 0x6A + +/* * ISP24xx mailbox commands */ +#define MBC_WRITE_SERDES 0x3 /* Write serdes word. */ +#define MBC_READ_SERDES 0x4 /* Read serdes word. */ +#define MBC_LOAD_DUMP_MPI_RAM 0x5 /* Load/Dump MPI RAM. */ #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. */ @@ -702,7 +954,9 @@ typedef struct { #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_LINK_INITIALIZATION 0x72 /* Do link initialization. */ #define MBC_SET_VENDOR_ID 0x76 /* Set Vendor ID. */ +#define MBC_PORT_RESET 0x120 /* Port Reset */ #define MBC_SET_PORT_CONFIG 0x122 /* Set port configuration */ #define MBC_GET_PORT_CONFIG 0x123 /* Get port configuration */ @@ -711,6 +965,13 @@ typedef struct { */ #define MBC_WRITE_MPI_REGISTER 0x01 /* Write MPI Register. */ +/* + * ISP8044 mailbox commands + */ +#define MBC_SET_GET_ETH_SERDES_REG 0x150 +#define HCS_WRITE_SERDES 0x3 +#define HCS_READ_SERDES 0x4 + /* Firmware return data sizes */ #define FCAL_MAP_SIZE 128 @@ -748,6 +1009,9 @@ typedef struct { #define MBX_1 BIT_1 #define MBX_0 BIT_0 +#define RNID_TYPE_SET_VERSION 0x9 +#define RNID_TYPE_ASIC_TEMP 0xC + /* * Firmware state codes from get firmware state mailbox command */ @@ -958,8 +1222,9 @@ struct link_statistics { uint32_t unused1[0x1a]; uint32_t tx_frames; uint32_t rx_frames; - uint32_t dumped_frames; - uint32_t unused2[2]; + uint32_t discarded_frames; + uint32_t dropped_frames; + uint32_t unused2[1]; uint32_t nos_rcvd; }; @@ -1220,11 +1485,27 @@ typedef struct { * ISP queue - response queue entry definition. */ typedef struct { - uint8_t data[60]; + 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 defined handle */ + uint8_t data[52]; uint32_t signature; #define RESPONSE_PROCESSED 0xDEADDEAD /* Signature */ } response_t; +/* + * ISP queue - ATIO queue entry definition. + */ +struct atio { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t data[58]; + uint32_t signature; +#define ATIO_PROCESSED 0xDEADDEAD /* Signature */ +}; + typedef union { uint16_t extended; struct { @@ -1344,28 +1625,39 @@ typedef struct { } 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_MODE_DIF_REMOVE 1 +#define PO_MODE_DIF_PASS 2 +#define PO_MODE_DIF_REPLACE 3 +#define PO_MODE_DIF_TCP_CKSUM 6 #define PO_ENABLE_INCR_GUARD_SEED BIT_3 -#define PO_DISABLE_INCR_REF_TAG BIT_5 #define PO_DISABLE_GUARD_CHECK BIT_4 +#define PO_DISABLE_INCR_REF_TAG BIT_5 +#define PO_DIS_HEADER_MODE BIT_7 +#define PO_ENABLE_DIF_BUNDLING BIT_8 +#define PO_DIS_FRAME_MODE BIT_9 +#define PO_DIS_VALD_APP_ESC BIT_10 /* Dis validation for escape tag/ffffh */ +#define PO_DIS_VALD_APP_REF_ESC BIT_11 + +#define PO_DIS_APP_TAG_REPL BIT_12 /* disable REG Tag replacement */ +#define PO_DIS_REF_TAG_REPL BIT_13 +#define PO_DIS_APP_TAG_VALD BIT_14 /* disable REF Tag validation */ +#define PO_DIS_REF_TAG_VALD BIT_15 + /* * 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; + __le32 ref_tag; + __le16 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 */ + __le16 guard_seed; /* Initial Guard Seed */ + __le16 prot_opts; /* Requested Data Protection Mode */ + __le16 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 + __le32 byte_count; /* Total byte count/ total data * transfer count */ union { struct { @@ -1379,10 +1671,10 @@ struct crc_context { uint32_t reserved_6; } nobundling; struct { - uint32_t dif_byte_count; /* Total DIF byte + __le32 dif_byte_count; /* Total DIF byte * count */ uint16_t reserved_1; - uint16_t dseg_count; /* Data segment count */ + __le16 dseg_count; /* Data segment count */ uint32_t reserved_2; uint32_t data_address[2]; uint32_t data_length; @@ -1473,11 +1765,20 @@ typedef struct { #define CS_PORT_CONFIG_CHG 0x2A /* Port Configuration Changed */ #define CS_PORT_BUSY 0x2B /* Port Busy */ #define CS_COMPLETE_CHKCOND 0x30 /* Error? */ +#define CS_IOCB_ERROR 0x31 /* Generic error for IOCB request + failure */ #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 */ +#define CS_BIDIR_RD_OVERRUN 0x700 +#define CS_BIDIR_RD_WR_OVERRUN 0x707 +#define CS_BIDIR_RD_OVERRUN_WR_UNDERRUN 0x715 +#define CS_BIDIR_RD_UNDERRUN 0x1500 +#define CS_BIDIR_RD_UNDERRUN_WR_OVERRUN 0x1507 +#define CS_BIDIR_RD_WR_UNDERRUN 0x1515 +#define CS_BIDIR_DMA 0x200 /* * Status entry status flags */ @@ -1690,6 +1991,9 @@ typedef struct fc_port { uint16_t loop_id; uint16_t old_loop_id; + uint16_t tgt_id; + uint16_t old_tgt_id; + uint8_t fcp_prio; uint8_t fabric_port_name[WWN_SIZE]; @@ -1705,10 +2009,17 @@ typedef struct fc_port { struct fc_rport *rport, *drport; u32 supported_classes; - uint16_t vp_idx; uint8_t fc4_type; + uint8_t scan_state; + + unsigned long last_queue_full; + unsigned long last_ramp_up; + + uint16_t port_id; } fc_port_t; +#include "qla_mr.h" + /* * Fibre channel port/lun states. */ @@ -1732,6 +2043,7 @@ static const char * const port_state_str[] = { #define FCF_LOGIN_NEEDED BIT_1 #define FCF_FCP2_DEVICE BIT_2 #define FCF_ASYNC_SENT BIT_3 +#define FCF_CONF_COMP_SUPPORTED BIT_4 /* No loop ID flag. */ #define FC_NO_LOOP_ID 0x1000 @@ -1759,7 +2071,6 @@ static const char * const port_state_str[] = { #define GID_PT_CMD 0x1A1 #define GID_PT_REQ_SIZE (16 + 4) -#define GID_PT_RSP_SIZE (16 + (MAX_FIBRE_DEVICES * 4)) #define GPN_ID_CMD 0x112 #define GPN_ID_REQ_SIZE (16 + 4) @@ -1857,6 +2168,7 @@ struct ct_fdmi_hba_attributes { #define FDMI_PORT_SPEED_4GB 0x8 #define FDMI_PORT_SPEED_8GB 0x10 #define FDMI_PORT_SPEED_16GB 0x20 +#define FDMI_PORT_SPEED_32GB 0x40 #define FDMI_PORT_SPEED_UNKNOWN 0x8000 struct ct_fdmi_port_attr { @@ -2049,7 +2361,9 @@ struct ct_sns_rsp { } ga_nxt; struct { - struct ct_sns_gid_pt_data entries[MAX_FIBRE_DEVICES]; + /* Assume the largest number of targets for the union */ + struct ct_sns_gid_pt_data + entries[MAX_FIBRE_DEVICES_MAX]; } gid_pt; struct { @@ -2110,7 +2424,11 @@ struct ct_sns_pkt { #define GID_PT_SNS_SCMD_LEN 6 #define GID_PT_SNS_CMD_SIZE 28 -#define GID_PT_SNS_DATA_SIZE (MAX_FIBRE_DEVICES * 4 + 16) +/* + * 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 @@ -2158,7 +2476,6 @@ struct gid_list_info { uint16_t loop_id; /* ISP23XX -- 6 bytes. */ uint16_t reserved_1; /* ISP24XX -- 8 bytes. */ }; -#define GID_LIST_SIZE (sizeof(struct gid_list_info) * MAX_FIBRE_DEVICES) /* NPIV */ typedef struct vport_info { @@ -2244,6 +2561,8 @@ struct isp_operations { 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*); + int (*initialize_adapter)(struct scsi_qla_host *); }; /* MSI-X Support *************************************************************/ @@ -2258,6 +2577,7 @@ struct isp_operations { #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; @@ -2281,6 +2601,7 @@ enum qla_work_type { QLA_EVT_ASYNC_ADISC, QLA_EVT_ASYNC_ADISC_DONE, QLA_EVT_UEVENT, + QLA_EVT_AENFX, }; @@ -2308,7 +2629,15 @@ struct qla_work_evt { u32 code; #define QLA_UEVENT_CODE_FW_DUMP 0 } uevent; - } u; + struct { + uint32_t evtcode; + uint32_t mbx[8]; + uint32_t count; + } aenfx; + struct { + srb_t *sp; + } iosb; + } u; }; struct qla_chip_state_84xx { @@ -2330,6 +2659,16 @@ struct qla_statistics { uint32_t total_isp_aborts; uint64_t input_bytes; uint64_t output_bytes; + uint64_t input_requests; + uint64_t output_requests; + uint32_t control_requests; + + uint64_t jiffies_at_last_reset; +}; + +struct bidi_statistics { + unsigned long long io_count; + unsigned long long transfer_bytes; }; /* Multi queue support */ @@ -2338,10 +2677,9 @@ struct qla_statistics { #define QLA_MQ_SIZE 32 #define QLA_MAX_QUEUES 256 #define ISP_QUE_REG(ha, id) \ - ((ha->mqenable) ? \ - ((void *)(ha->mqiobase) +\ - (QLA_QUE_PAGE * id)) :\ - ((void *)(ha->iobase))) + ((ha->mqenable || IS_QLA83XX(ha) || IS_QLA27XX(ha)) ? \ + ((void __iomem *)ha->mqiobase + (QLA_QUE_PAGE * id)) :\ + ((void __iomem *)ha->iobase)) #define QLA_REQ_QUE_ID(tag) \ ((tag < QLA_MAX_QUEUES && tag > 0) ? tag : 0) #define QLA_DEFAULT_QUE_QOS 5 @@ -2357,6 +2695,7 @@ struct rsp_que { uint32_t __iomem *rsp_q_out; uint16_t ring_index; uint16_t out_ptr; + uint16_t *in_ptr; /* queue shadow in index */ uint16_t length; uint16_t options; uint16_t rid; @@ -2367,6 +2706,11 @@ struct rsp_que { struct req_que *req; srb_t *status_srb; /* status continuation entry */ struct work_struct q_work; + + dma_addr_t dma_fx00; + response_t *ring_fx00; + uint16_t length_fx00; + uint8_t rsp_pkt[REQUEST_ENTRY_SIZE]; }; /* Request queue data structure */ @@ -2378,6 +2722,7 @@ struct req_que { uint32_t __iomem *req_q_out; uint16_t ring_index; uint16_t in_ptr; + uint16_t *out_ptr; /* queue shadow out index */ uint16_t cnt; uint16_t length; uint16_t options; @@ -2386,9 +2731,15 @@ struct req_que { uint16_t qos; uint16_t vp_idx; struct rsp_que *rsp; - srb_t *outstanding_cmds[MAX_OUTSTANDING_COMMANDS]; + srb_t **outstanding_cmds; uint32_t current_outstanding_cmd; + uint16_t num_outstanding_cmds; int max_q_depth; + + dma_addr_t dma_fx00; + request_t *ring_fx00; + uint16_t length_fx00; + uint8_t req_pkt[REQUEST_ENTRY_SIZE]; }; /* Place holder for FW buffer parameters */ @@ -2398,6 +2749,45 @@ struct qlfc_fw { uint32_t len; }; +struct scsi_qlt_host { + void *target_lport_ptr; + struct mutex tgt_mutex; + struct mutex tgt_host_action_mutex; + struct qla_tgt *qla_tgt; +}; + +struct qlt_hw_data { + /* Protected by hw lock */ + uint32_t enable_class_2:1; + uint32_t enable_explicit_conf:1; + uint32_t ini_mode_force_reverse:1; + uint32_t node_name_set:1; + + dma_addr_t atio_dma; /* Physical address. */ + struct atio *atio_ring; /* Base virtual address */ + struct atio *atio_ring_ptr; /* Current address. */ + uint16_t atio_ring_index; /* Current index. */ + uint16_t atio_q_length; + uint32_t __iomem *atio_q_in; + uint32_t __iomem *atio_q_out; + + struct qla_tgt_func_tmpl *tgt_ops; + struct qla_tgt_cmd *cmds[DEFAULT_OUTSTANDING_COMMANDS]; + uint16_t current_handle; + + struct qla_tgt_vp_map *tgt_vp_map; + + int saved_set; + uint16_t saved_exchange_count; + uint32_t saved_firmware_options_1; + uint32_t saved_firmware_options_2; + uint32_t saved_firmware_options_3; + uint8_t saved_firmware_options[2]; + uint8_t saved_add_firmware_options[2]; + + uint8_t tgt_node_name[WWN_SIZE]; +}; + /* * Qlogic host adapter specific data structure. */ @@ -2427,18 +2817,24 @@ struct qla_hw_data { 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 nic_core_hung:1; uint32_t quiesce_owner:1; - uint32_t thermal_supported:1; - uint32_t isp82xx_reset_hdlr_active:1; - /* 26 bits */ + uint32_t nic_core_reset_hdlr_active:1; + uint32_t nic_core_reset_owner:1; + uint32_t isp82xx_no_md_cap:1; + uint32_t host_shutting_down:1; + uint32_t idc_compl_status:1; + + uint32_t mr_reset_hdlr_active:1; + uint32_t mr_intr_valid:1; + /* 34 bits */ } flags; /* This spinlock is used to protect "io transactions", you must @@ -2451,12 +2847,27 @@ struct qla_hw_data { spinlock_t hardware_lock ____cacheline_aligned; int bars; int mem_only; - device_reg_t __iomem *iobase; /* Base I/O address */ + device_reg_t *iobase; /* Base I/O address */ resource_size_t pio_address; #define MIN_IOBASE_LEN 0x100 -/* Multi queue data structs */ - device_reg_t __iomem *mqiobase; + dma_addr_t bar0_hdl; + + void __iomem *cregbase; + dma_addr_t bar2_hdl; +#define BAR0_LEN_FX00 (1024 * 1024) +#define BAR2_LEN_FX00 (128 * 1024) + + uint32_t rqstq_intr_code; + uint32_t mbx_intr_code; + uint32_t req_que_len; + uint32_t rsp_que_len; + uint32_t req_que_off; + uint32_t rsp_que_off; + + /* Multi queue data structs */ + device_reg_t *mqiobase; + device_reg_t *msixbase; uint16_t msix_count; uint8_t mqenable; struct req_que **req_q_map; @@ -2481,6 +2892,7 @@ struct qla_hw_data { 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 */ @@ -2490,6 +2902,8 @@ struct qla_hw_data { #define PORT_SPEED_2GB 0x01 #define PORT_SPEED_4GB 0x03 #define PORT_SPEED_8GB 0x04 +#define PORT_SPEED_16GB 0x05 +#define PORT_SPEED_32GB 0x06 #define PORT_SPEED_10GB 0x13 uint16_t link_data_rate; /* F/W operating speed */ @@ -2511,6 +2925,11 @@ struct qla_hw_data { #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 +#define PCI_DEVICE_ID_QLOGIC_ISP2071 0x2071 +#define PCI_DEVICE_ID_QLOGIC_ISP2271 0x2271 + uint32_t device_type; #define DT_ISP2100 BIT_0 #define DT_ISP2200 BIT_1 @@ -2527,8 +2946,15 @@ struct qla_hw_data { #define DT_ISP8432 BIT_12 #define DT_ISP8001 BIT_13 #define DT_ISP8021 BIT_14 -#define DT_ISP_LAST (DT_ISP8021 << 1) - +#define DT_ISP2031 BIT_15 +#define DT_ISP8031 BIT_16 +#define DT_ISPFX00 BIT_17 +#define DT_ISP8044 BIT_18 +#define DT_ISP2071 BIT_19 +#define DT_ISP2271 BIT_20 +#define DT_ISP_LAST (DT_ISP2271 << 1) + +#define DT_T10_PI BIT_25 #define DT_IIDMA BIT_26 #define DT_FWI2 BIT_27 #define DT_ZIO_SUPPORTED BIT_28 @@ -2550,33 +2976,62 @@ struct qla_hw_data { #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_QLA8044(ha) (DT_MASK(ha) & DT_ISP8044) +#define IS_QLA2031(ha) (DT_MASK(ha) & DT_ISP2031) +#define IS_QLA8031(ha) (DT_MASK(ha) & DT_ISP8031) +#define IS_QLAFX00(ha) (DT_MASK(ha) & DT_ISPFX00) +#define IS_QLA2071(ha) (DT_MASK(ha) & DT_ISP2071) +#define IS_QLA2271(ha) (DT_MASK(ha) & DT_ISP2271) #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_QLA27XX(ha) (IS_QLA2071(ha) || IS_QLA2271(ha)) #define IS_QLA24XX_TYPE(ha) (IS_QLA24XX(ha) || IS_QLA54XX(ha) || \ IS_QLA84XX(ha)) -#define IS_QLA81XX(ha) (IS_QLA8001(ha)) -#define IS_QLA8XXX_TYPE(ha) (IS_QLA81XX(ha) || IS_QLA82XX(ha)) +#define IS_CNA_CAPABLE(ha) (IS_QLA81XX(ha) || IS_QLA82XX(ha) || \ + IS_QLA8031(ha) || IS_QLA8044(ha)) +#define IS_P3P_TYPE(ha) (IS_QLA82XX(ha) || IS_QLA8044(ha)) #define IS_QLA2XXX_MIDTYPE(ha) (IS_QLA24XX(ha) || IS_QLA84XX(ha) || \ IS_QLA25XX(ha) || IS_QLA81XX(ha) || \ - IS_QLA82XX(ha)) -#define IS_MSIX_NACK_CAPABLE(ha) (IS_QLA81XX(ha)) -#define IS_NOPOLLING_TYPE(ha) ((IS_QLA25XX(ha) || IS_QLA81XX(ha)) && \ - (ha)->flags.msix_enabled) -#define IS_FAC_REQUIRED(ha) (IS_QLA81XX(ha)) -#define IS_NOCACHE_VPD_TYPE(ha) (IS_QLA81XX(ha)) + IS_QLA82XX(ha) || IS_QLA83XX(ha) || \ + IS_QLA8044(ha) || IS_QLA27XX(ha)) +#define IS_MSIX_NACK_CAPABLE(ha) (IS_QLA81XX(ha) || IS_QLA83XX(ha)) +#define IS_NOPOLLING_TYPE(ha) (IS_QLA81XX(ha) && (ha)->flags.msix_enabled) +#define IS_FAC_REQUIRED(ha) (IS_QLA81XX(ha) || IS_QLA83XX(ha) || \ + IS_QLA27XX(ha)) +#define IS_NOCACHE_VPD_TYPE(ha) (IS_QLA81XX(ha) || IS_QLA83XX(ha) || \ + IS_QLA27XX(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) || \ + IS_QLA27XX(ha)) +#define IS_BIDI_CAPABLE(ha) ((IS_QLA25XX(ha) || IS_QLA2031(ha))) +/* Bit 21 of fw_attributes decides the MCTP capabilities */ +#define IS_MCTP_CAPABLE(ha) (IS_QLA2031(ha) && \ + ((ha)->fw_attributes_ext[0] & BIT_0)) +#define IS_PI_UNINIT_CAPABLE(ha) (IS_QLA83XX(ha)) +#define IS_PI_IPGUARD_CAPABLE(ha) (IS_QLA83XX(ha)) +#define IS_PI_DIFB_DIX0_CAPABLE(ha) (0) +#define IS_PI_SPLIT_DET_CAPABLE_HBA(ha) (IS_QLA83XX(ha)) +#define IS_PI_SPLIT_DET_CAPABLE(ha) (IS_PI_SPLIT_DET_CAPABLE_HBA(ha) && \ + (((ha)->fw_attributes_h << 16 | (ha)->fw_attributes) & BIT_22)) +#define IS_ATIO_MSIX_CAPABLE(ha) (IS_QLA83XX(ha)) +#define IS_TGT_MODE_CAPABLE(ha) (ha->tgt.atio_q_length) +#define IS_SHADOW_REG_CAPABLE(ha) (IS_QLA27XX(ha)) /* HBA serial number */ uint8_t serial0; @@ -2599,6 +3054,7 @@ struct qla_hw_data { uint16_t r_a_tov; int port_down_retry_count; uint8_t mbx_count; + uint8_t aen_mbx_count; uint32_t login_retry_count; /* SNS command interfaces. */ @@ -2615,10 +3071,6 @@ struct qla_hw_data { void *sfp_data; dma_addr_t sfp_data_dma; - uint8_t *edc_data; - dma_addr_t edc_data_dma; - uint16_t edc_data_len; - #define XGMAC_DATA_SIZE 4096 void *xgmac_data; dma_addr_t xgmac_data_dma; @@ -2647,10 +3099,16 @@ struct qla_hw_data { 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]; + uint16_t mailbox_out[MAILBOX_REGISTER_COUNT]; + uint32_t mailbox_out32[MAILBOX_REGISTER_COUNT]; + uint32_t aenmb[AEN_MAILBOX_REGISTER_COUNT_FX00]; mbx_cmd_t *mcp; + struct mbx_cmd_32 *mcp32; + unsigned long mbx_cmd_flags; #define MBX_INTERRUPT 1 #define MBX_INTR_WAIT 2 @@ -2661,13 +3119,22 @@ struct qla_hw_data { 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 */ + struct completion lb_portup_comp; /* Used to wait for link up during + * loopback */ +#define DCBX_COMP_TIMEOUT 20 +#define LB_PORTUP_COMP_TIMEOUT 10 + int notify_dcbx_comp; + int notify_lb_portup_comp; + struct mutex selflogin_lock; /* 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; @@ -2675,6 +3142,10 @@ struct qla_hw_data { #define RISC_START_ADDRESS_2300 0x800 #define RISC_START_ADDRESS_2400 0x100000 uint16_t fw_xcb_count; + uint16_t fw_iocb_count; + + uint32_t fw_shared_ram_start; + uint32_t fw_shared_ram_end; uint16_t fw_options[16]; /* slots: 1,2,3,10,11 */ uint8_t fw_seriallink_options[4]; @@ -2684,14 +3155,30 @@ struct qla_hw_data { uint32_t mpi_capabilities; uint8_t phy_version[3]; + /* Firmware dump template */ + void *fw_dump_template; + uint32_t fw_dump_template_len; /* Firmware dump information. */ struct qla2xxx_fw_dump *fw_dump; uint32_t fw_dump_len; int fw_dumped; + unsigned long fw_dump_cap_flags; +#define RISC_PAUSE_CMPL 0 +#define DMA_SHUTDOWN_CMPL 1 +#define ISP_RESET_CMPL 2 +#define RISC_RDY_AFT_RESET 3 +#define RISC_SRAM_DUMP_CMPL 4 +#define RISC_EXT_MEM_DUMP_CMPL 5 int fw_dump_reading; + int prev_minidump_failed; dma_addr_t eft_dma; void *eft; - +/* Current size of mctp dump is 0x086064 bytes */ +#define MCTP_DUMP_SIZE 0x086064 + dma_addr_t mctp_dump_dma; + void *mctp_dump; + int mctp_dumped; + int mctp_dump_reading; uint32_t chain_offset; struct dentry *dfs_dir; struct dentry *dfs_fce; @@ -2721,6 +3208,7 @@ struct qla_hw_data { #define QLA_SWRITING 2 uint32_t optrom_region_start; uint32_t optrom_region_size; + struct mutex optrom_mutex; /* PCI expansion ROM image information. */ #define ROM_CODE_TYPE_BIOS 0 @@ -2740,10 +3228,12 @@ struct qla_hw_data { uint32_t nvram_data_off; uint32_t fdt_wrt_disable; + uint32_t fdt_wrt_enable; uint32_t fdt_erase_cmd; uint32_t fdt_block_size; uint32_t fdt_unprotect_sec_cmd; uint32_t fdt_protect_sec_cmd; + uint32_t fdt_wrt_sts_reg_cmd; uint32_t flt_region_flt; uint32_t flt_region_fdt; @@ -2767,7 +3257,6 @@ struct qla_hw_data { /* 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; @@ -2805,8 +3294,8 @@ struct qla_hw_data { 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; + uint32_t fcoe_dev_init_timeout; + uint32_t fcoe_reset_timeout; rwlock_t hw_lock; uint16_t portnum; /* port number */ int link_width; @@ -2820,6 +3309,33 @@ struct qla_hw_data { 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; + + void *loop_id_map; + + /* QLA83XX IDC specific fields */ + uint32_t idc_audit_ts; + uint32_t idc_extend_tmo; + + /* DPC low-priority workqueue */ + struct workqueue_struct *dpc_lp_wq; + struct work_struct idc_aen; + /* DPC high-priority workqueue */ + struct workqueue_struct *dpc_hp_wq; + struct work_struct nic_core_reset; + struct work_struct idc_state_handler; + struct work_struct nic_core_unrecoverable; + struct work_struct board_disable; + + struct mr_data_fx00 mr; + + struct qlt_hw_data tgt; + int allow_cna_fw_dump; }; /* @@ -2839,13 +3355,14 @@ typedef struct scsi_qla_host { volatile struct { uint32_t init_done :1; uint32_t online :1; - uint32_t rscn_queue_overflow :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; + + uint32_t fw_tgt_reported:1; } flags; atomic_t loop_state; @@ -2878,6 +3395,12 @@ typedef struct scsi_qla_host { #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 */ +#define SCR_PENDING 21 /* SCR in target mode */ +#define PORT_UPDATE_NEEDED 22 +#define FX00_RESET_RECOVERY 23 +#define FX00_TARGET_SCAN 24 +#define FX00_CRITEMP_RECOVERY 25 +#define FX00_HOST_INFO_RESEND 26 uint32_t device_flags; #define SWITCH_FOUND BIT_0 @@ -2886,6 +3409,13 @@ typedef struct scsi_qla_host { /* ISP configuration data. */ uint16_t loop_id; /* Host adapter loop id */ + uint16_t self_login_loop_id; /* host adapter loop id + * get it on self login + */ + fc_port_t bidir_fcport; /* fcport used for bidir cmnds + * no need of allocating it for + * each command + */ port_id_t d_id; /* Host adapter port id */ uint8_t marker_needed; @@ -2893,11 +3423,6 @@ typedef struct scsi_qla_host { - /* RSCN queue. */ - uint32_t rscn_queue[MAX_RSCN_COUNT]; - uint8_t rscn_in_ptr; - uint8_t rscn_out_ptr; - /* Timeout timers. */ uint8_t loop_down_abort_time; /* port down timer */ atomic_t loop_down_timer; /* loop down timer */ @@ -2914,7 +3439,7 @@ typedef struct scsi_qla_host { uint16_t fcoe_fcf_idx; uint8_t fcoe_vn_port_mac[6]; - uint32_t vp_abort_cnt; + uint32_t vp_abort_cnt; struct fc_vport *fc_vport; /* holds fc_vport * for each vport */ uint16_t vp_idx; /* vport ID */ @@ -2939,13 +3464,27 @@ typedef struct scsi_qla_host { #define VP_ERR_FAB_LOGOUT 4 #define VP_ERR_ADAP_NORESOURCES 5 struct qla_hw_data *hw; + struct scsi_qlt_host vha_tgt; struct req_que *req; int fw_heartbeat_counter; int seconds_since_last_heartbeat; + struct fc_host_statistics fc_host_stat; + struct qla_statistics qla_stats; + struct bidi_statistics bidi_stats; atomic_t vref_count; + struct qla8044_reset_template reset_tmplt; } scsi_qla_host_t; +#define SET_VP_IDX 1 +#define SET_AL_PA 2 +#define RESET_VP_IDX 3 +#define RESET_AL_PA 4 +struct qla_tgt_vp_map { + uint8_t idx; + scsi_qla_host_t *vha; +}; + /* * Macros to help code, maintain, etc. */ @@ -2954,6 +3493,10 @@ typedef struct scsi_qla_host { test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags) || \ atomic_read(&ha->loop_state) == LOOP_DOWN) +#define STATE_TRANSITION(ha) \ + (test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) || \ + test_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags)) + #define QLA_VHA_MARK_BUSY(__vha, __bail) do { \ atomic_inc(&__vha->vref_count); \ mb(); \ @@ -2969,10 +3512,6 @@ typedef struct scsi_qla_host { atomic_dec(&__vha->vref_count); \ } while (0) - -#define qla_printk(level, ha, format, arg...) \ - dev_printk(level , &((ha)->pdev->dev) , format , ## arg) - /* * qla2x00 local function return status codes */ @@ -2997,13 +3536,10 @@ typedef struct scsi_qla_host { #define QLA_ABORTED 0x105 #define QLA_SUSPENDED 0x106 #define QLA_BUSY 0x107 -#define QLA_RSCNS_HANDLED 0x108 #define QLA_ALREADY_REGISTERED 0x109 #define NVRAM_DELAY() udelay(10) -#define INVALID_HANDLE (MAX_OUTSTANDING_COMMANDS+1) - /* * Flash support definitions */ @@ -3013,6 +3549,7 @@ typedef struct scsi_qla_host { #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) diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c index a5a4e1275bf..2ca39b8e716 100644 --- a/drivers/scsi/qla2xxx/qla_dfs.c +++ b/drivers/scsi/qla2xxx/qla_dfs.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -64,7 +64,7 @@ qla2x00_dfs_fce_open(struct inode *inode, struct file *file) /* Pause tracing to flush FCE buffers. */ rval = qla2x00_disable_fce_trace(vha, &ha->fce_wr, &ha->fce_rd); if (rval) - qla_printk(KERN_WARNING, ha, + ql_dbg(ql_dbg_user, vha, 0x705c, "DebugFS: Unable to disable FCE (%d).\n", rval); ha->flags.fce_enabled = 0; @@ -92,7 +92,7 @@ qla2x00_dfs_fce_release(struct inode *inode, struct file *file) rval = qla2x00_enable_fce_trace(vha, ha->fce_dma, ha->fce_bufs, ha->fce_mb, &ha->fce_bufs); if (rval) { - qla_printk(KERN_WARNING, ha, + ql_dbg(ql_dbg_user, vha, 0x700d, "DebugFS: Unable to reinitialize FCE (%d).\n", rval); ha->flags.fce_enabled = 0; } @@ -114,7 +114,8 @@ qla2x00_dfs_setup(scsi_qla_host_t *vha) { struct qla_hw_data *ha = vha->hw; - if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha)) + if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha) && + !IS_QLA27XX(ha)) goto out; if (!ha->fce) goto out; @@ -125,8 +126,8 @@ qla2x00_dfs_setup(scsi_qla_host_t *vha) atomic_set(&qla2x00_dfs_root_count, 0); qla2x00_dfs_root = debugfs_create_dir(QLA2XXX_DRIVER_NAME, NULL); if (!qla2x00_dfs_root) { - qla_printk(KERN_NOTICE, ha, - "DebugFS: Unable to create root directory.\n"); + ql_log(ql_log_warn, vha, 0x00f7, + "Unable to create debugfs root directory.\n"); goto out; } @@ -137,8 +138,8 @@ create_dir: mutex_init(&ha->fce_mutex); ha->dfs_dir = debugfs_create_dir(vha->host_str, qla2x00_dfs_root); if (!ha->dfs_dir) { - qla_printk(KERN_NOTICE, ha, - "DebugFS: Unable to create ha directory.\n"); + ql_log(ql_log_warn, vha, 0x00f8, + "Unable to create debugfs ha directory.\n"); goto out; } @@ -148,8 +149,8 @@ create_nodes: ha->dfs_fce = debugfs_create_file("fce", S_IRUSR, ha->dfs_dir, vha, &dfs_fce_ops); if (!ha->dfs_fce) { - qla_printk(KERN_NOTICE, ha, - "DebugFS: Unable to fce node.\n"); + ql_log(ql_log_warn, vha, 0x00f9, + "Unable to create debugfs fce node.\n"); goto out; } out: diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h index 691783abfb6..eb8f57249f1 100644 --- a/drivers/scsi/qla2xxx/qla_fw.h +++ b/drivers/scsi/qla2xxx/qla_fw.h @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -300,7 +300,8 @@ struct init_cb_24xx { uint32_t prio_request_q_address[2]; uint16_t msix; - uint8_t reserved_2[6]; + uint16_t msix_atio; + uint8_t reserved_2[4]; uint16_t atio_q_inpointer; uint16_t atio_q_length; @@ -370,7 +371,10 @@ struct init_cb_24xx { * BIT 14 = Data Rate bit 1 * BIT 15 = Data Rate bit 2 * BIT 16 = Enable 75 ohm Termination Select - * BIT 17-31 = Reserved + * BIT 17-28 = Reserved + * BIT 29 = Enable response queue 0 in index shadowing + * BIT 30 = Enable request queue 0 out index shadowing + * BIT 31 = Reserved */ uint32_t firmware_options_3; uint16_t qos; @@ -381,6 +385,44 @@ struct init_cb_24xx { /* * ISP queue - command entry structure definition. */ +#define COMMAND_BIDIRECTIONAL 0x75 +struct cmd_bidir { + 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 hanlde. */ + + uint16_t timeout; /* Commnad timeout. */ + + uint16_t wr_dseg_count; /* Write Data segment count. */ + uint16_t rd_dseg_count; /* Read Data segment count. */ + + struct scsi_lun lun; /* FCP LUN (BE). */ + + uint16_t control_flags; /* Control flags. */ +#define BD_WRAP_BACK BIT_3 +#define BD_READ_DATA BIT_1 +#define BD_WRITE_DATA BIT_0 + + uint16_t fcp_cmnd_dseg_len; /* Data segment length. */ + uint32_t fcp_cmnd_dseg_address[2]; /* Data segment address. */ + + uint16_t reserved[2]; /* Reserved */ + + uint32_t rd_byte_count; /* Total Byte count Read. */ + uint32_t wr_byte_count; /* Total Byte count write. */ + + uint8_t port_id[3]; /* PortID of destination port.*/ + uint8_t vp_index; + + uint32_t fcp_data_dseg_address[2]; /* Data segment address. */ + uint16_t fcp_data_dseg_len; /* Data segment length. */ +}; + #define COMMAND_TYPE_6 0x48 /* Command Type 6 entry */ struct cmd_type_6 { uint8_t entry_type; /* Entry type. */ @@ -537,6 +579,11 @@ struct sts_entry_24xx { /* * 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 @@ -1049,6 +1096,27 @@ struct device_reg_24xx { uint32_t unused_6[2]; /* Gap. */ uint32_t iobase_sdata; }; +/* RISC-RISC semaphore register PCI offet */ +#define RISC_REGISTER_BASE_OFFSET 0x7010 +#define RISC_REGISTER_WINDOW_OFFET 0x6 + +/* RISC-RISC semaphore/flag register (risc address 0x7016) */ + +#define RISC_SEMAPHORE 0x1UL +#define RISC_SEMAPHORE_WE (RISC_SEMAPHORE << 16) +#define RISC_SEMAPHORE_CLR (RISC_SEMAPHORE_WE | 0x0UL) +#define RISC_SEMAPHORE_SET (RISC_SEMAPHORE_WE | RISC_SEMAPHORE) + +#define RISC_SEMAPHORE_FORCE 0x8000UL +#define RISC_SEMAPHORE_FORCE_WE (RISC_SEMAPHORE_FORCE << 16) +#define RISC_SEMAPHORE_FORCE_CLR (RISC_SEMAPHORE_FORCE_WE | 0x0UL) +#define RISC_SEMAPHORE_FORCE_SET \ + (RISC_SEMAPHORE_FORCE_WE | RISC_SEMAPHORE_FORCE) + +/* RISC semaphore timeouts (ms) */ +#define TIMEOUT_SEMAPHORE 2500 +#define TIMEOUT_SEMAPHORE_FORCE 2000 +#define TIMEOUT_TOTAL_ELAPSED 4500 /* Trace Control *************************************************************/ @@ -1069,13 +1137,6 @@ struct device_reg_24xx { #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; @@ -1125,7 +1186,7 @@ struct mid_db_entry_24xx { /* * Virtual Port Control IOCB */ -#define VP_CTRL_IOCB_TYPE 0x30 /* Vitual Port Control entry. */ +#define VP_CTRL_IOCB_TYPE 0x30 /* Virtual Port Control entry. */ struct vp_ctrl_entry_24xx { uint8_t entry_type; /* Entry type. */ uint8_t entry_count; /* Entry count. */ @@ -1161,7 +1222,7 @@ struct vp_ctrl_entry_24xx { /* * Modify Virtual Port Configuration IOCB */ -#define VP_CONFIG_IOCB_TYPE 0x31 /* Vitual Port Config entry. */ +#define VP_CONFIG_IOCB_TYPE 0x31 /* Virtual Port Config entry. */ struct vp_config_entry_24xx { uint8_t entry_type; /* Entry type. */ uint8_t entry_count; /* Entry count. */ @@ -1313,6 +1374,10 @@ struct qla_flt_header { #define FLT_REG_NVRAM_0 0x15 #define FLT_REG_VPD_1 0x16 #define FLT_REG_NVRAM_1 0x17 +#define FLT_REG_VPD_2 0xD4 +#define FLT_REG_NVRAM_2 0xD5 +#define FLT_REG_VPD_3 0xD6 +#define FLT_REG_NVRAM_3 0xD7 #define FLT_REG_FDT 0x1a #define FLT_REG_FLT 0x1c #define FLT_REG_HW_EVENT_0 0x1d @@ -1322,6 +1387,11 @@ struct qla_flt_header { #define FLT_REG_GOLD_FW 0x2f #define FLT_REG_FCP_PRIO_0 0x87 #define FLT_REG_FCP_PRIO_1 0x88 +#define FLT_REG_CNA_FW 0x97 +#define FLT_REG_BOOT_CODE_8044 0xA2 +#define FLT_REG_FCOE_FW 0xA4 +#define FLT_REG_FCOE_NVRAM_0 0xAA +#define FLT_REG_FCOE_NVRAM_1 0xAC struct qla_flt_region { uint32_t code; @@ -1489,6 +1559,14 @@ struct access_chip_rsp_84xx { #define MBC_GET_XGMAC_STATS 0x7a #define MBC_GET_DCBX_PARAMS 0x51 +/* + * ISP83xx mailbox commands + */ +#define MBC_WRITE_REMOTE_REG 0x0001 /* Write remote register */ +#define MBC_READ_REMOTE_REG 0x0009 /* Read remote register */ +#define MBC_RESTART_NIC_FIRMWARE 0x003d /* Restart NIC firmware */ +#define MBC_SET_ACCESS_CONTROL 0x003e /* Access control command */ + /* Flash access control option field bit definitions */ #define FAC_OPT_FORCE_SEMAPHORE BIT_15 #define FAC_OPT_REQUESTOR_ID BIT_14 @@ -1870,4 +1948,7 @@ struct qla_fcp_prio_cfg { #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 index 0b381224ae4..d48dea8fab1 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -48,14 +48,14 @@ 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_quiesce_io(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 int qla2x00_get_thermal_temp(scsi_qla_host_t *, uint16_t *); extern void qla84xx_put_chip(struct scsi_qla_host *); @@ -71,13 +71,23 @@ 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_async_tm_cmd_done(struct scsi_qla_host *, fc_port_t *, - struct srb_iocb *); 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 ); + +extern int __qla83xx_set_idc_control(scsi_qla_host_t *, uint32_t); +extern int __qla83xx_get_idc_control(scsi_qla_host_t *, uint32_t *); +extern void qla83xx_idc_audit(scsi_qla_host_t *, int); +extern int qla83xx_nic_core_reset(scsi_qla_host_t *); +extern void qla83xx_reset_ownership(scsi_qla_host_t *); +extern int qla2xxx_mctp_dump(scsi_qla_host_t *); + +extern int +qla2x00_alloc_outstanding_cmds(struct qla_hw_data *, struct req_que *); +extern int qla2x00_init_rings(scsi_qla_host_t *); + /* * Global Data in qla_os.c source file. */ @@ -104,6 +114,8 @@ 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); @@ -122,7 +134,6 @@ 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 *); @@ -133,6 +144,24 @@ 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 *); +extern void qla83xx_schedule_work(scsi_qla_host_t *, int); +extern void qla83xx_service_idc_aen(struct work_struct *); +extern void qla83xx_nic_core_unrecoverable_work(struct work_struct *); +extern void qla83xx_idc_state_handler_work(struct work_struct *); +extern void qla83xx_nic_core_reset_work(struct work_struct *); + +extern void qla83xx_idc_lock(scsi_qla_host_t *, uint16_t); +extern void qla83xx_idc_unlock(scsi_qla_host_t *, uint16_t); +extern int qla83xx_idc_state_handler(scsi_qla_host_t *); +extern int qla83xx_set_drv_presence(scsi_qla_host_t *vha); +extern int __qla83xx_set_drv_presence(scsi_qla_host_t *vha); +extern int qla83xx_clear_drv_presence(scsi_qla_host_t *vha); +extern int __qla83xx_clear_drv_presence(scsi_qla_host_t *vha); +extern int qla2x00_post_uevent_work(struct scsi_qla_host *, u32); + +extern int qla2x00_post_uevent_work(struct scsi_qla_host *, u32); +extern void qla2x00_disable_board_on_pci_error(struct work_struct *); + /* * Global Functions in qla_mid.c source file. */ @@ -154,8 +183,7 @@ 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_compl(struct qla_hw_data *, srb_t *); - +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); @@ -176,6 +204,7 @@ 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); @@ -185,9 +214,18 @@ 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(uint16_t); -extern void qla24xx_build_scsi_iocbs(srb_t *, struct cmd_type_7 *, uint16_t); extern int qla24xx_dif_start_scsi(srb_t *); +extern int qla2x00_start_bidir(srb_t *, struct scsi_qla_host *, uint32_t); +extern unsigned long qla2x00_get_async_timeout(struct scsi_qla_host *); + +extern void *qla2x00_alloc_iocbs(scsi_qla_host_t *, srb_t *); +extern int qla2x00_issue_marker(scsi_qla_host_t *, int); +extern int qla24xx_walk_and_build_sglist_no_difb(struct qla_hw_data *, srb_t *, + uint32_t *, uint16_t, struct qla_tgt_cmd *); +extern int qla24xx_walk_and_build_sglist(struct qla_hw_data *, srb_t *, + uint32_t *, uint16_t, struct qla_tgt_cmd *); +extern int qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *, srb_t *, + uint32_t *, uint16_t, struct qla_tgt_cmd *); /* @@ -203,8 +241,7 @@ extern int qla2x00_execute_fw(scsi_qla_host_t *, uint32_t); extern int -qla2x00_get_fw_version(scsi_qla_host_t *, uint16_t *, uint16_t *, uint16_t *, - uint16_t *, uint32_t *, uint8_t *, uint32_t *, uint8_t *); +qla2x00_get_fw_version(scsi_qla_host_t *); extern int qla2x00_get_fw_options(scsi_qla_host_t *, uint16_t *); @@ -241,6 +278,9 @@ extern int qla2x00_init_firmware(scsi_qla_host_t *, uint16_t); extern int +qla2x00_get_node_name_list(scsi_qla_host_t *, void **, int *); + +extern int qla2x00_get_port_database(scsi_qla_host_t *, fc_port_t *, uint8_t); extern int @@ -250,6 +290,9 @@ extern int qla2x00_get_port_name(scsi_qla_host_t *, uint16_t, uint8_t *, uint8_t); extern int +qla24xx_link_initialize(scsi_qla_host_t *); + +extern int qla2x00_lip_reset(scsi_qla_host_t *); extern int @@ -294,6 +337,7 @@ qla24xx_get_isp_stats(scsi_qla_host_t *, struct link_statistics *, dma_addr_t); extern int qla24xx_abort_command(srb_t *); +extern int qla24xx_async_abort_command(srb_t *); extern int qla24xx_abort_target(struct fc_port *, unsigned int, int); extern int @@ -305,6 +349,16 @@ extern int qla2x00_system_error(scsi_qla_host_t *); extern int +qla2x00_write_serdes_word(scsi_qla_host_t *, uint16_t, uint16_t); +extern int +qla2x00_read_serdes_word(scsi_qla_host_t *, uint16_t, uint16_t *); + +extern int +qla8044_write_serdes_word(scsi_qla_host_t *, uint32_t, uint32_t); +extern int +qla8044_read_serdes_word(scsi_qla_host_t *, uint32_t, uint32_t *); + +extern int qla2x00_set_serdes_params(scsi_qla_host_t *, uint16_t, uint16_t, uint16_t); extern int @@ -323,6 +377,12 @@ extern int qla2x00_disable_fce_trace(scsi_qla_host_t *, uint64_t *, uint64_t *); extern int +qla82xx_set_driver_version(scsi_qla_host_t *, char *); + +extern int +qla25xx_set_driver_version(scsi_qla_host_t *, char *); + +extern int qla2x00_read_sfp(scsi_qla_host_t *, dma_addr_t, uint8_t *, uint16_t, uint16_t, uint16_t, uint16_t); @@ -369,6 +429,12 @@ 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 *); + +extern int +qla2x00_dump_mctp_data(scsi_qla_host_t *, dma_addr_t, uint32_t, uint32_t); + /* * Global Function Prototypes in qla_isr.c source file. */ @@ -382,24 +448,34 @@ 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 *); +extern const char *qla2x00_get_link_speed_str(struct qla_hw_data *, uint16_t); +extern srb_t * +qla2x00_get_sp_from_handle(scsi_qla_host_t *, const char *, struct req_que *, + void *); +extern void +qla2x00_process_completed_request(struct scsi_qla_host *, struct req_que *, + uint32_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); + uint32_t, uint32_t); extern uint8_t *qla2x00_read_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t, - uint32_t); + uint32_t); extern uint8_t *qla24xx_read_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t, - uint32_t); + uint32_t); extern int qla2x00_write_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t, - uint32_t); + uint32_t); extern int qla24xx_write_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t, - uint32_t); + uint32_t); extern uint8_t *qla25xx_read_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t, - uint32_t); + uint32_t); extern int qla25xx_write_nvram_data(scsi_qla_host_t *, uint8_t *, uint32_t, - uint32_t); + uint32_t); +extern int qla2x00_is_a_vp_did(scsi_qla_host_t *, uint32_t); +bool qla2x00_check_reg_for_disconnect(scsi_qla_host_t *, uint32_t); extern int qla2x00_beacon_on(struct scsi_qla_host *); extern int qla2x00_beacon_off(struct scsi_qla_host *); @@ -407,20 +483,32 @@ 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_wr_reg(scsi_qla_host_t *, uint32_t, uint32_t); +extern int qla83xx_rd_reg(scsi_qla_host_t *, uint32_t, uint32_t *); +extern int qla83xx_restart_nic_firmware(scsi_qla_host_t *); +extern int qla83xx_access_control(scsi_qla_host_t *, uint16_t, uint32_t, + uint32_t, uint16_t *); extern uint8_t *qla2x00_read_optrom_data(struct scsi_qla_host *, uint8_t *, - uint32_t, uint32_t); + uint32_t, uint32_t); extern int qla2x00_write_optrom_data(struct scsi_qla_host *, uint8_t *, - uint32_t, uint32_t); + uint32_t, uint32_t); extern uint8_t *qla24xx_read_optrom_data(struct scsi_qla_host *, uint8_t *, - uint32_t, uint32_t); + uint32_t, uint32_t); extern int qla24xx_write_optrom_data(struct scsi_qla_host *, uint8_t *, - uint32_t, uint32_t); + uint32_t, uint32_t); extern uint8_t *qla25xx_read_optrom_data(struct scsi_qla_host *, uint8_t *, - uint32_t, uint32_t); + uint32_t, uint32_t); +extern uint8_t *qla8044_read_optrom_data(struct scsi_qla_host *, + uint8_t *, uint32_t, uint32_t); +extern void qla8044_watchdog(struct scsi_qla_host *vha); extern int qla2x00_get_flash_version(scsi_qla_host_t *, void *); extern int qla24xx_get_flash_version(scsi_qla_host_t *, void *); +extern int qla82xx_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); @@ -436,9 +524,23 @@ 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 qla82xx_fw_dump(scsi_qla_host_t *, int); +extern void qla8044_fw_dump(scsi_qla_host_t *, int); + +extern void qla27xx_fwdump(scsi_qla_host_t *, int); +extern ulong qla27xx_fwdt_calculate_dump_size(struct scsi_qla_host *); +extern int qla27xx_fwdt_template_valid(void *); +extern ulong qla27xx_fwdt_template_size(void *); +extern const void *qla27xx_fwdt_template_default(void); +extern ulong qla27xx_fwdt_template_default_size(void); + 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. @@ -470,15 +572,15 @@ 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_free_sysfs_attr(scsi_qla_host_t *, bool); 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(struct qla_fcp_prio_cfg *, uint8_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. @@ -504,12 +606,48 @@ 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); +/* qlafx00 related functions */ +extern int qlafx00_pci_config(struct scsi_qla_host *); +extern int qlafx00_initialize_adapter(struct scsi_qla_host *); +extern void qlafx00_soft_reset(scsi_qla_host_t *); +extern int qlafx00_chip_diag(scsi_qla_host_t *); +extern void qlafx00_config_rings(struct scsi_qla_host *); +extern char *qlafx00_pci_info_str(struct scsi_qla_host *, char *); +extern char *qlafx00_fw_version_str(struct scsi_qla_host *, char *); +extern irqreturn_t qlafx00_intr_handler(int, void *); +extern void qlafx00_enable_intrs(struct qla_hw_data *); +extern void qlafx00_disable_intrs(struct qla_hw_data *); +extern int qlafx00_abort_target(fc_port_t *, unsigned int, int); +extern int qlafx00_lun_reset(fc_port_t *, unsigned int, int); +extern int qlafx00_start_scsi(srb_t *); +extern int qlafx00_abort_isp(scsi_qla_host_t *); +extern int qlafx00_iospace_config(struct qla_hw_data *); +extern int qlafx00_init_firmware(scsi_qla_host_t *, uint16_t); +extern int qlafx00_driver_shutdown(scsi_qla_host_t *, int); +extern int qlafx00_fw_ready(scsi_qla_host_t *); +extern int qlafx00_configure_devices(scsi_qla_host_t *); +extern int qlafx00_reset_initialize(scsi_qla_host_t *); +extern int qlafx00_fx_disc(scsi_qla_host_t *, fc_port_t *, uint16_t); +extern int qlafx00_process_aen(struct scsi_qla_host *, struct qla_work_evt *); +extern int qlafx00_post_aenfx_work(struct scsi_qla_host *, uint32_t, + uint32_t *, int); +extern uint32_t qlafx00_fw_state_show(struct device *, + struct device_attribute *, char *); +extern void qlafx00_get_host_speed(struct Scsi_Host *); +extern void qlafx00_init_response_q_entries(struct rsp_que *); + +extern void qlafx00_tm_iocb(srb_t *, struct tsk_mgmt_entry_fx00 *); +extern void qlafx00_abort_iocb(srb_t *, struct abort_iocb_entry_fx00 *); +extern void qlafx00_fxdisc_iocb(srb_t *, struct fxdisc_entry_fx00 *); +extern void qlafx00_timer_routine(scsi_qla_host_t *); +extern int qlafx00_rescan_isp(scsi_qla_host_t *); +extern int qlafx00_loop_reset(scsi_qla_host_t *vha); + /* 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 *); @@ -522,9 +660,9 @@ 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); + uint32_t, uint32_t); extern int qla82xx_write_optrom_data(struct scsi_qla_host *, uint8_t *, - uint32_t, uint32_t); + uint32_t, uint32_t); /* Mailbox related functions */ extern int qla82xx_abort_isp(scsi_qla_host_t *); @@ -532,6 +670,11 @@ 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 *); +extern void qla2x00_start_iocbs(struct scsi_qla_host *, struct req_que *); /* Interrupt related */ extern irqreturn_t qla82xx_intr_handler(int, void *); @@ -545,7 +688,6 @@ 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); @@ -557,15 +699,24 @@ 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 qla8xxx_dev_failed_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 *); + 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(srb_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 *); +extern int qla82xx_read_temperature(scsi_qla_host_t *); +extern int qla8044_read_temperature(scsi_qla_host_t *); /* BSG related functions */ extern int qla24xx_bsg_request(struct fc_bsg_job *); @@ -575,4 +726,43 @@ 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 *); +extern int qla82xx_validate_template_chksum(scsi_qla_host_t *vha); + +/* Function declarations for ISP8044 */ +extern int qla8044_idc_lock(struct qla_hw_data *ha); +extern void qla8044_idc_unlock(struct qla_hw_data *ha); +extern uint32_t qla8044_rd_reg(struct qla_hw_data *ha, ulong addr); +extern void qla8044_wr_reg(struct qla_hw_data *ha, ulong addr, uint32_t val); +extern void qla8044_read_reset_template(struct scsi_qla_host *ha); +extern void qla8044_set_idc_dontreset(struct scsi_qla_host *ha); +extern int qla8044_rd_direct(struct scsi_qla_host *vha, const uint32_t crb_reg); +extern void qla8044_wr_direct(struct scsi_qla_host *vha, + const uint32_t crb_reg, const uint32_t value); +extern inline void qla8044_set_qsnt_ready(struct scsi_qla_host *vha); +extern inline void qla8044_need_reset_handler(struct scsi_qla_host *vha); +extern int qla8044_device_state_handler(struct scsi_qla_host *vha); +extern void qla8044_clear_qsnt_ready(struct scsi_qla_host *vha); +extern void qla8044_clear_drv_active(struct qla_hw_data *); +void qla8044_get_minidump(struct scsi_qla_host *vha); +int qla8044_collect_md_data(struct scsi_qla_host *vha); +extern int qla8044_md_get_template(scsi_qla_host_t *); +extern int qla8044_write_optrom_data(struct scsi_qla_host *, uint8_t *, + uint32_t, uint32_t); +extern irqreturn_t qla8044_intr_handler(int, void *); +extern void qla82xx_mbx_completion(scsi_qla_host_t *, uint16_t); +extern int qla8044_abort_isp(scsi_qla_host_t *); +extern int qla8044_check_fw_alive(struct scsi_qla_host *); + #endif /* _QLA_GBL_H */ diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c index 8cd9066ad90..a0df3b1b382 100644 --- a/drivers/scsi/qla2xxx/qla_gs.c +++ b/drivers/scsi/qla2xxx/qla_gs.c @@ -1,10 +1,11 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ #include "qla_def.h" +#include "qla_target.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 *); @@ -48,6 +49,8 @@ qla2x00_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size) ms_pkt->dseg_rsp_address[1] = cpu_to_le32(MSD(ha->ct_sns_dma)); ms_pkt->dseg_rsp_length = ms_pkt->rsp_bytecount; + vha->qla_stats.control_requests++; + return (ms_pkt); } @@ -86,6 +89,8 @@ qla24xx_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size) ct_pkt->dseg_1_len = ct_pkt->rsp_byte_count; ct_pkt->vp_index = vha->vp_idx; + vha->qla_stats.control_requests++; + return (ct_pkt); } @@ -98,17 +103,17 @@ qla24xx_prep_ms_iocb(scsi_qla_host_t *vha, uint32_t req_size, uint32_t rsp_size) * 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) +qla2x00_prep_ct_req(struct ct_sns_pkt *p, uint16_t cmd, uint16_t rsp_size) { - memset(ct_req, 0, sizeof(struct ct_sns_pkt)); + memset(p, 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); + p->p.req.header.revision = 0x01; + p->p.req.header.gs_type = 0xFC; + p->p.req.header.gs_subtype = 0x02; + p->p.req.command = cpu_to_be16(cmd); + p->p.req.max_rsp_size = cpu_to_be16((rsp_size - 16) / 4); - return (ct_req); + return &p->p.req; } static int @@ -121,11 +126,10 @@ qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt, rval = QLA_FUNCTION_FAILED; if (ms_pkt->entry_status != 0) { - DEBUG2_3(printk(KERN_WARNING "scsi(%ld): %s failed, error status " - "(%x) on port_id: %02x%02x%02x.\n", - vha->host_no, routine, ms_pkt->entry_status, - vha->d_id.b.domain, vha->d_id.b.area, - vha->d_id.b.al_pa)); + 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( @@ -138,24 +142,24 @@ qla2x00_chk_ms_status(scsi_qla_host_t *vha, ms_iocb_entry_t *ms_pkt, case CS_DATA_OVERRUN: /* Overrun? */ if (ct_rsp->header.response != __constant_cpu_to_be16(CT_ACCEPT_RESPONSE)) { - DEBUG2_3(printk("scsi(%ld): %s failed, " - "rejected request on port_id: %02x%02x%02x\n", - vha->host_no, routine, + 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)); - DEBUG2_3(qla2x00_dump_buffer( - (uint8_t *)&ct_rsp->header, - sizeof(struct ct_rsp_hdr))); + 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: - DEBUG2_3(printk("scsi(%ld): %s failed, completion " - "status (%x) on port_id: %02x%02x%02x.\n", - vha->host_no, routine, comp_status, + 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)); + vha->d_id.b.al_pa); break; } } @@ -188,7 +192,7 @@ qla2x00_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport) GA_NXT_RSP_SIZE); /* Prepare CT request */ - ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GA_NXT_CMD, + ct_req = qla2x00_prep_ct_req(ha->ct_sns, GA_NXT_CMD, GA_NXT_RSP_SIZE); ct_rsp = &ha->ct_sns->p.rsp; @@ -202,8 +206,8 @@ qla2x00_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport) sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): GA_NXT issue IOCB failed (%d).\n", - vha->host_no, rval)); + 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; @@ -218,30 +222,30 @@ qla2x00_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport) memcpy(fcport->port_name, ct_rsp->rsp.ga_nxt.port_name, WWN_SIZE); + fcport->fc4_type = (ct_rsp->rsp.ga_nxt.fc4_types[2] & BIT_0) ? + FC4_TYPE_FCP_SCSI : FC4_TYPE_OTHER; + 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; - DEBUG2_3(printk("scsi(%ld): GA_NXT entry - " - "nn %02x%02x%02x%02x%02x%02x%02x%02x " - "pn %02x%02x%02x%02x%02x%02x%02x%02x " - "portid=%02x%02x%02x.\n", - vha->host_no, - 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], + ql_dbg(ql_dbg_disc, vha, 0x2063, + "GA_NXT entry - nn %8phN pn %8phN " + "port_id=%02x%02x%02x.\n", + fcport->node_name, fcport->port_name, fcport->d_id.b.domain, fcport->d_id.b.area, - fcport->d_id.b.al_pa)); + 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 @@ -263,20 +267,20 @@ qla2x00_gid_pt(scsi_qla_host_t *vha, sw_info_t *list) 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); + 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_req = qla2x00_prep_ct_req(ha->ct_sns, GID_PT_CMD, gid_pt_rsp_size); ct_rsp = &ha->ct_sns->p.rsp; /* Prepare CT arguments -- port_type */ @@ -287,14 +291,14 @@ qla2x00_gid_pt(scsi_qla_host_t *vha, sw_info_t *list) sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): GID_PT issue IOCB failed (%d).\n", - vha->host_no, rval)); + 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 < MAX_FIBRE_DEVICES; i++) { + 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]; @@ -315,7 +319,7 @@ qla2x00_gid_pt(scsi_qla_host_t *vha, sw_info_t *list) * single call. Return a failed status, and let GA_NXT handle * the overload. */ - if (i == MAX_FIBRE_DEVICES) + if (i == ha->max_fibre_devices) rval = QLA_FUNCTION_FAILED; } @@ -332,7 +336,7 @@ qla2x00_gid_pt(scsi_qla_host_t *vha, sw_info_t *list) int qla2x00_gpn_id(scsi_qla_host_t *vha, sw_info_t *list) { - int rval; + int rval = QLA_SUCCESS; uint16_t i; ms_iocb_entry_t *ms_pkt; @@ -343,14 +347,14 @@ qla2x00_gpn_id(scsi_qla_host_t *vha, sw_info_t *list) if (IS_QLA2100(ha) || IS_QLA2200(ha)) return qla2x00_sns_gpn_id(vha, list); - for (i = 0; i < MAX_FIBRE_DEVICES; i++) { + 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, + ct_req = qla2x00_prep_ct_req(ha->ct_sns, GPN_ID_CMD, GPN_ID_RSP_SIZE); ct_rsp = &ha->ct_sns->p.rsp; @@ -364,11 +368,13 @@ qla2x00_gpn_id(scsi_qla_host_t *vha, sw_info_t *list) sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): GPN_ID issue IOCB failed " - "(%d).\n", vha->host_no, rval)); + 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, @@ -393,7 +399,7 @@ qla2x00_gpn_id(scsi_qla_host_t *vha, sw_info_t *list) int qla2x00_gnn_id(scsi_qla_host_t *vha, sw_info_t *list) { - int rval; + int rval = QLA_SUCCESS; uint16_t i; struct qla_hw_data *ha = vha->hw; ms_iocb_entry_t *ms_pkt; @@ -403,14 +409,14 @@ qla2x00_gnn_id(scsi_qla_host_t *vha, sw_info_t *list) if (IS_QLA2100(ha) || IS_QLA2200(ha)) return qla2x00_sns_gnn_id(vha, list); - for (i = 0; i < MAX_FIBRE_DEVICES; i++) { + 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, + ct_req = qla2x00_prep_ct_req(ha->ct_sns, GNN_ID_CMD, GNN_ID_RSP_SIZE); ct_rsp = &ha->ct_sns->p.rsp; @@ -424,31 +430,24 @@ qla2x00_gnn_id(scsi_qla_host_t *vha, sw_info_t *list) sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): GNN_ID issue IOCB failed " - "(%d).\n", vha->host_no, rval)); + 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); - DEBUG2_3(printk("scsi(%ld): GID_PT entry - " - "nn %02x%02x%02x%02x%02x%02x%02x%02x " - "pn %02x%02x%02x%02x%02x%02x%02x%02x " + ql_dbg(ql_dbg_disc, vha, 0x2058, + "GID_PT entry - nn %8phN pn %8phN " "portid=%02x%02x%02x.\n", - vha->host_no, - 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].node_name, list[i].port_name, list[i].d_id.b.domain, list[i].d_id.b.area, - list[i].d_id.b.al_pa)); + list[i].d_id.b.al_pa); } /* Last device exit. */ @@ -483,7 +482,7 @@ qla2x00_rft_id(scsi_qla_host_t *vha) RFT_ID_RSP_SIZE); /* Prepare CT request */ - ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RFT_ID_CMD, + ct_req = qla2x00_prep_ct_req(ha->ct_sns, RFT_ID_CMD, RFT_ID_RSP_SIZE); ct_rsp = &ha->ct_sns->p.rsp; @@ -499,14 +498,14 @@ qla2x00_rft_id(scsi_qla_host_t *vha) sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): RFT_ID issue IOCB failed (%d).\n", - vha->host_no, rval)); + 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 { - DEBUG2(printk("scsi(%ld): RFT_ID exiting normally.\n", - vha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2044, + "RFT_ID exiting normally.\n"); } return (rval); @@ -528,8 +527,8 @@ qla2x00_rff_id(scsi_qla_host_t *vha) struct ct_sns_rsp *ct_rsp; if (IS_QLA2100(ha) || IS_QLA2200(ha)) { - DEBUG2(printk("scsi(%ld): RFF_ID call unsupported on " - "ISP2100/ISP2200.\n", vha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2046, + "RFF_ID call not supported on ISP2100/ISP2200.\n"); return (QLA_SUCCESS); } @@ -539,7 +538,7 @@ qla2x00_rff_id(scsi_qla_host_t *vha) RFF_ID_RSP_SIZE); /* Prepare CT request */ - ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, RFF_ID_CMD, + ct_req = qla2x00_prep_ct_req(ha->ct_sns, RFF_ID_CMD, RFF_ID_RSP_SIZE); ct_rsp = &ha->ct_sns->p.rsp; @@ -548,7 +547,8 @@ qla2x00_rff_id(scsi_qla_host_t *vha) 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; + qlt_rff_id(vha, ct_req); + ct_req->req.rff_id.fc4_type = 0x08; /* SCSI - FCP */ /* Execute MS IOCB */ @@ -556,14 +556,14 @@ qla2x00_rff_id(scsi_qla_host_t *vha) sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): RFF_ID issue IOCB failed (%d).\n", - vha->host_no, rval)); + 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 { - DEBUG2(printk("scsi(%ld): RFF_ID exiting normally.\n", - vha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2048, + "RFF_ID exiting normally.\n"); } return (rval); @@ -593,8 +593,7 @@ qla2x00_rnn_id(scsi_qla_host_t *vha) 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_req = qla2x00_prep_ct_req(ha->ct_sns, RNN_ID_CMD, RNN_ID_RSP_SIZE); ct_rsp = &ha->ct_sns->p.rsp; /* Prepare CT arguments -- port_id, node_name */ @@ -609,14 +608,14 @@ qla2x00_rnn_id(scsi_qla_host_t *vha) sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): RNN_ID issue IOCB failed (%d).\n", - vha->host_no, rval)); + 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 { - DEBUG2(printk("scsi(%ld): RNN_ID exiting normally.\n", - vha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x204e, + "RNN_ID exiting normally.\n"); } return (rval); @@ -626,9 +625,14 @@ 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); + + if (IS_QLAFX00(ha)) + sprintf(snn, "%s FW:v%s DVR:v%s", ha->model_number, + ha->mr.fw_version, qla2x00_version_str); + else + 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); } /** @@ -647,8 +651,8 @@ qla2x00_rsnn_nn(scsi_qla_host_t *vha) struct ct_sns_rsp *ct_rsp; if (IS_QLA2100(ha) || IS_QLA2200(ha)) { - DEBUG2(printk("scsi(%ld): RSNN_ID call unsupported on " - "ISP2100/ISP2200.\n", vha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2050, + "RSNN_ID call unsupported on ISP2100/ISP2200.\n"); return (QLA_SUCCESS); } @@ -658,7 +662,7 @@ qla2x00_rsnn_nn(scsi_qla_host_t *vha) 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, + ct_req = qla2x00_prep_ct_req(ha->ct_sns, RSNN_NN_CMD, RSNN_NN_RSP_SIZE); ct_rsp = &ha->ct_sns->p.rsp; @@ -682,14 +686,14 @@ qla2x00_rsnn_nn(scsi_qla_host_t *vha) sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): RSNN_NN issue IOCB failed (%d).\n", - vha->host_no, rval)); + 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 { - DEBUG2(printk("scsi(%ld): RSNN_NN exiting normally.\n", - vha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2052, + "RSNN_NN exiting normally.\n"); } return (rval); @@ -723,6 +727,8 @@ qla2x00_prep_sns_cmd(scsi_qla_host_t *vha, uint16_t cmd, uint16_t scmd_len, wc = (data_size - 16) / 4; /* Size in 32bit words. */ sns_cmd->p.cmd.size = cpu_to_le16(wc); + vha->qla_stats.control_requests++; + return (sns_cmd); } @@ -738,7 +744,7 @@ qla2x00_prep_sns_cmd(scsi_qla_host_t *vha, uint16_t cmd, uint16_t scmd_len, static int qla2x00_sns_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport) { - int rval; + int rval = QLA_SUCCESS; struct qla_hw_data *ha = vha->hw; struct sns_cmd_pkt *sns_cmd; @@ -757,13 +763,14 @@ qla2x00_sns_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport) sizeof(struct sns_cmd_pkt)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): GA_NXT Send SNS failed (%d).\n", - vha->host_no, rval)); + 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) { - DEBUG2_3(printk("scsi(%ld): GA_NXT failed, rejected request, " - "ga_nxt_rsp:\n", vha->host_no)); - DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gan_data, 16)); + 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. */ @@ -778,21 +785,12 @@ qla2x00_sns_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport) sns_cmd->p.gan_data[16] != NS_NL_PORT_TYPE) fcport->d_id.b.domain = 0xf0; - DEBUG2_3(printk("scsi(%ld): GA_NXT entry - " - "nn %02x%02x%02x%02x%02x%02x%02x%02x " - "pn %02x%02x%02x%02x%02x%02x%02x%02x " - "portid=%02x%02x%02x.\n", - vha->host_no, - 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], + ql_dbg(ql_dbg_disc, vha, 0x2061, + "GA_NXT entry - nn %8phN pn %8phN " + "port_id=%02x%02x%02x.\n", + fcport->node_name, fcport->port_name, fcport->d_id.b.domain, fcport->d_id.b.area, - fcport->d_id.b.al_pa)); + fcport->d_id.b.al_pa); } return (rval); @@ -817,11 +815,14 @@ qla2x00_sns_gid_pt(scsi_qla_host_t *vha, sw_info_t *list) 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); + gid_pt_sns_data_size); /* Prepare SNS command arguments -- port_type. */ sns_cmd->p.cmd.param[0] = NS_NX_PORT_TYPE; @@ -831,17 +832,18 @@ qla2x00_sns_gid_pt(scsi_qla_host_t *vha, sw_info_t *list) sizeof(struct sns_cmd_pkt)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): GID_PT Send SNS failed (%d).\n", - vha->host_no, rval)); + 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) { - DEBUG2_3(printk("scsi(%ld): GID_PT failed, rejected request, " - "gid_rsp:\n", vha->host_no)); - DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gid_data, 16)); + 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 < MAX_FIBRE_DEVICES; i++) { + 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]; @@ -860,7 +862,7 @@ qla2x00_sns_gid_pt(scsi_qla_host_t *vha, sw_info_t *list) * single call. Return a failed status, and let GA_NXT handle * the overload. */ - if (i == MAX_FIBRE_DEVICES) + if (i == ha->max_fibre_devices) rval = QLA_FUNCTION_FAILED; } @@ -879,12 +881,12 @@ qla2x00_sns_gid_pt(scsi_qla_host_t *vha, sw_info_t *list) static int qla2x00_sns_gpn_id(scsi_qla_host_t *vha, sw_info_t *list) { - int rval; + int rval = QLA_SUCCESS; struct qla_hw_data *ha = vha->hw; uint16_t i; struct sns_cmd_pkt *sns_cmd; - for (i = 0; i < MAX_FIBRE_DEVICES; i++) { + 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, @@ -900,13 +902,14 @@ qla2x00_sns_gpn_id(scsi_qla_host_t *vha, sw_info_t *list) GPN_ID_SNS_CMD_SIZE / 2, sizeof(struct sns_cmd_pkt)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): GPN_ID Send SNS failed " - "(%d).\n", vha->host_no, rval)); + 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) { - DEBUG2_3(printk("scsi(%ld): GPN_ID failed, rejected " - "request, gpn_rsp:\n", vha->host_no)); - DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gpn_data, 16)); + ql_dbg(ql_dbg_disc + ql_dbg_buffer, vha, 0x207e, + "GPN_ID failed, rejected request, gpn_rsp:\n"); + ql_dump_buffer(ql_dbg_disc + ql_dbg_buffer, vha, 0x207f, + sns_cmd->p.gpn_data, 16); rval = QLA_FUNCTION_FAILED; } else { /* Save portname */ @@ -934,12 +937,12 @@ qla2x00_sns_gpn_id(scsi_qla_host_t *vha, sw_info_t *list) static int qla2x00_sns_gnn_id(scsi_qla_host_t *vha, sw_info_t *list) { - int rval; + int rval = QLA_SUCCESS; struct qla_hw_data *ha = vha->hw; uint16_t i; struct sns_cmd_pkt *sns_cmd; - for (i = 0; i < MAX_FIBRE_DEVICES; i++) { + 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, @@ -955,34 +958,26 @@ qla2x00_sns_gnn_id(scsi_qla_host_t *vha, sw_info_t *list) GNN_ID_SNS_CMD_SIZE / 2, sizeof(struct sns_cmd_pkt)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): GNN_ID Send SNS failed " - "(%d).\n", vha->host_no, rval)); + 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) { - DEBUG2_3(printk("scsi(%ld): GNN_ID failed, rejected " - "request, gnn_rsp:\n", vha->host_no)); - DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.gnn_data, 16)); + 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); - DEBUG2_3(printk("scsi(%ld): 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", - vha->host_no, - 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], + ql_dbg(ql_dbg_disc, vha, 0x206e, + "GID_PT entry - nn %8phN pn %8phN " + "port_id=%02x%02x%02x.\n", + list[i].node_name, list[i].port_name, list[i].d_id.b.domain, list[i].d_id.b.area, - list[i].d_id.b.al_pa)); + list[i].d_id.b.al_pa); } /* Last device exit. */ @@ -1025,17 +1020,18 @@ qla2x00_sns_rft_id(scsi_qla_host_t *vha) sizeof(struct sns_cmd_pkt)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): RFT_ID Send SNS failed (%d).\n", - vha->host_no, rval)); + 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) { - DEBUG2_3(printk("scsi(%ld): RFT_ID failed, rejected request, " - "rft_rsp:\n", vha->host_no)); - DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.rft_data, 16)); + 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 { - DEBUG2(printk("scsi(%ld): RFT_ID exiting normally.\n", - vha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2073, + "RFT_ID exiting normally.\n"); } return (rval); @@ -1081,17 +1077,18 @@ qla2x00_sns_rnn_id(scsi_qla_host_t *vha) sizeof(struct sns_cmd_pkt)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): RNN_ID Send SNS failed (%d).\n", - vha->host_no, rval)); + 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) { - DEBUG2_3(printk("scsi(%ld): RNN_ID failed, rejected request, " - "rnn_rsp:\n", vha->host_no)); - DEBUG2_3(qla2x00_dump_buffer(sns_cmd->p.rnn_data, 16)); + 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 { - DEBUG2(printk("scsi(%ld): RNN_ID exiting normally.\n", - vha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x204c, + "RNN_ID exiting normally.\n"); } return (rval); @@ -1106,20 +1103,26 @@ qla2x00_sns_rnn_id(scsi_qla_host_t *vha) static int qla2x00_mgmt_svr_login(scsi_qla_host_t *vha) { - int ret; + 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; - ha->isp_ops->fabric_login(vha, vha->mgmt_svr_loop_id, 0xff, 0xff, 0xfa, - mb, BIT_1|BIT_0); - if (mb[0] != MBS_COMMAND_COMPLETE) { - DEBUG2_13(printk("%s(%ld): Failed MANAGEMENT_SERVER login: " - "loop_id=%x mb[0]=%x mb[1]=%x mb[2]=%x mb[6]=%x mb[7]=%x\n", - __func__, vha->host_no, vha->mgmt_svr_loop_id, mb[0], mb[1], - mb[2], mb[6], mb[7])); + rval = ha->isp_ops->fabric_login(vha, vha->mgmt_svr_loop_id, 0xff, 0xff, + 0xfa, mb, BIT_1); + 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; @@ -1231,18 +1234,18 @@ qla2x00_update_ms_fdmi_iocb(scsi_qla_host_t *vha, uint32_t req_size) * 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, +qla2x00_prep_ct_fdmi_req(struct ct_sns_pkt *p, uint16_t cmd, uint16_t rsp_size) { - memset(ct_req, 0, sizeof(struct ct_sns_pkt)); + memset(p, 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); + p->p.req.header.revision = 0x01; + p->p.req.header.gs_type = 0xFA; + p->p.req.header.gs_subtype = 0x10; + p->p.req.command = cpu_to_be16(cmd); + p->p.req.max_rsp_size = cpu_to_be16((rsp_size - 16) / 4); - return ct_req; + return &p->p.req; } /** @@ -1270,8 +1273,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *vha) 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_req = qla2x00_prep_ct_fdmi_req(ha->ct_sns, RHBA_CMD, RHBA_RSP_SIZE); ct_rsp = &ha->ct_sns->p.rsp; /* Prepare FDMI command arguments -- attribute block, attributes. */ @@ -1292,23 +1294,20 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *vha) memcpy(eiter->a.node_name, vha->node_name, WWN_SIZE); size += 4 + WWN_SIZE; - DEBUG13(printk("%s(%ld): NODENAME=%02x%02x%02x%02x%02x%02x%02x%02x.\n", - __func__, vha->host_no, - 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])); + ql_dbg(ql_dbg_disc, vha, 0x2025, + "NodeName = %8phN.\n", eiter->a.node_name); /* 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 = strlen(QLA2XXX_MANUFACTURER); + strncpy(eiter->a.manufacturer, QLA2XXX_MANUFACTURER, alen + 1); alen += (alen & 3) ? (4 - (alen & 3)) : 4; eiter->len = cpu_to_be16(4 + alen); size += 4 + alen; - DEBUG13(printk("%s(%ld): MANUFACTURER=%s.\n", __func__, vha->host_no, - eiter->a.manufacturer)); + ql_dbg(ql_dbg_disc, vha, 0x2026, + "Manufacturer = %s.\n", eiter->a.manufacturer); /* Serial number. */ eiter = (struct ct_fdmi_hba_attr *) (entries + size); @@ -1320,8 +1319,8 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *vha) eiter->len = cpu_to_be16(4 + alen); size += 4 + alen; - DEBUG13(printk("%s(%ld): SERIALNO=%s.\n", __func__, vha->host_no, - eiter->a.serial_num)); + 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); @@ -1332,21 +1331,20 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *vha) eiter->len = cpu_to_be16(4 + alen); size += 4 + alen; - DEBUG13(printk("%s(%ld): MODEL_NAME=%s.\n", __func__, vha->host_no, - eiter->a.model)); + 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); + 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; - DEBUG13(printk("%s(%ld): MODEL_DESC=%s.\n", __func__, vha->host_no, - eiter->a.model_desc)); + 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); @@ -1357,8 +1355,8 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *vha) eiter->len = cpu_to_be16(4 + alen); size += 4 + alen; - DEBUG13(printk("%s(%ld): HARDWAREVER=%s.\n", __func__, vha->host_no, - eiter->a.hw_version)); + 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); @@ -1369,8 +1367,8 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *vha) eiter->len = cpu_to_be16(4 + alen); size += 4 + alen; - DEBUG13(printk("%s(%ld): DRIVERVER=%s.\n", __func__, vha->host_no, - eiter->a.driver_version)); + 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); @@ -1381,8 +1379,8 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *vha) eiter->len = cpu_to_be16(4 + alen); size += 4 + alen; - DEBUG13(printk("%s(%ld): OPTROMVER=%s.\n", __func__, vha->host_no, - eiter->a.orom_version)); + 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); @@ -1393,44 +1391,38 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *vha) eiter->len = cpu_to_be16(4 + alen); size += 4 + alen; - DEBUG13(printk("%s(%ld): FIRMWAREVER=%s.\n", __func__, vha->host_no, - eiter->a.fw_version)); + 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); - DEBUG13(printk("%s(%ld): RHBA identifier=" - "%02x%02x%02x%02x%02x%02x%02x%02x size=%d.\n", __func__, - vha->host_no, 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)); - DEBUG13(qla2x00_dump_buffer(entries, size)); + ql_dbg(ql_dbg_disc, vha, 0x202e, + "RHBA identifier = %8phN size=%d.\n", + ct_req->req.rhba.hba_identifier, 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*/ - DEBUG2_3(printk("scsi(%ld): RHBA issue IOCB failed (%d).\n", - vha->host_no, rval)); + 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) { - DEBUG2_13(printk("%s(%ld): HBA already registered.\n", - __func__, vha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2034, + "HBA already registered.\n"); rval = QLA_ALREADY_REGISTERED; } } else { - DEBUG2(printk("scsi(%ld): RHBA exiting normally.\n", - vha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2035, + "RHBA exiting normally.\n"); } return rval; @@ -1457,33 +1449,28 @@ qla2x00_fdmi_dhba(scsi_qla_host_t *vha) DHBA_RSP_SIZE); /* Prepare CT request */ - ct_req = qla2x00_prep_ct_fdmi_req(&ha->ct_sns->p.req, DHBA_CMD, - DHBA_RSP_SIZE); + ct_req = qla2x00_prep_ct_fdmi_req(ha->ct_sns, 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); - DEBUG13(printk("%s(%ld): DHBA portname=" - "%02x%02x%02x%02x%02x%02x%02x%02x.\n", __func__, vha->host_no, - 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])); + ql_dbg(ql_dbg_disc, vha, 0x2036, + "DHBA portname = %8phN.\n", ct_req->req.dhba.port_name); /* 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*/ - DEBUG2_3(printk("scsi(%ld): DHBA issue IOCB failed (%d).\n", - vha->host_no, rval)); + 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 { - DEBUG2(printk("scsi(%ld): DHBA exiting normally.\n", - vha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2038, + "DHBA exiting normally.\n"); } return rval; @@ -1514,8 +1501,7 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *vha) 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_req = qla2x00_prep_ct_fdmi_req(ha->ct_sns, RPA_CMD, RPA_RSP_SIZE); ct_rsp = &ha->ct_sns->p.rsp; /* Prepare FDMI command arguments -- attribute block, attributes. */ @@ -1534,17 +1520,22 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *vha) eiter->a.fc4_types[2] = 0x01; size += 4 + 32; - DEBUG13(printk("%s(%ld): FC4_TYPES=%02x %02x.\n", __func__, - vha->host_no, eiter->a.fc4_types[2], - eiter->a.fc4_types[1])); + 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_QLA8XXX_TYPE(ha)) + if (IS_CNA_CAPABLE(ha)) eiter->a.sup_speed = __constant_cpu_to_be32( FDMI_PORT_SPEED_10GB); + else if (IS_QLA27XX(ha)) + eiter->a.sup_speed = __constant_cpu_to_be32( + FDMI_PORT_SPEED_32GB|FDMI_PORT_SPEED_16GB| + FDMI_PORT_SPEED_8GB); else if (IS_QLA25XX(ha)) eiter->a.sup_speed = __constant_cpu_to_be32( FDMI_PORT_SPEED_1GB|FDMI_PORT_SPEED_2GB| @@ -1561,8 +1552,8 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *vha) FDMI_PORT_SPEED_1GB); size += 4 + 4; - DEBUG13(printk("%s(%ld): SUPPORTED_SPEED=%x.\n", __func__, vha->host_no, - eiter->a.sup_speed)); + 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); @@ -1589,6 +1580,14 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *vha) 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; + case PORT_SPEED_32GB: + eiter->a.cur_speed = + __constant_cpu_to_be32(FDMI_PORT_SPEED_32GB); + break; default: eiter->a.cur_speed = __constant_cpu_to_be32(FDMI_PORT_SPEED_UNKNOWN); @@ -1596,8 +1595,8 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *vha) } size += 4 + 4; - DEBUG13(printk("%s(%ld): CURRENT_SPEED=%x.\n", __func__, vha->host_no, - eiter->a.cur_speed)); + 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); @@ -1609,20 +1608,20 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *vha) eiter->a.max_frame_size = cpu_to_be32(max_frame_size); size += 4 + 4; - DEBUG13(printk("%s(%ld): MAX_FRAME_SIZE=%x.\n", __func__, vha->host_no, - eiter->a.max_frame_size)); + 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 = strlen(QLA2XXX_DRIVER_NAME); + strncpy(eiter->a.os_dev_name, QLA2XXX_DRIVER_NAME, alen + 1); alen += (alen & 3) ? (4 - (alen & 3)) : 4; eiter->len = cpu_to_be16(4 + alen); size += 4 + alen; - DEBUG13(printk("%s(%ld): OS_DEVICE_NAME=%s.\n", __func__, vha->host_no, - eiter->a.os_dev_name)); + 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))) { @@ -1637,35 +1636,31 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *vha) eiter->len = cpu_to_be16(4 + alen); size += 4 + alen; - DEBUG13(printk("%s(%ld): HOSTNAME=%s.\n", __func__, - vha->host_no, eiter->a.host_name)); + 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); - DEBUG13(printk("%s(%ld): RPA portname=" - "%02x%02x%02x%02x%02x%02x%02x%02x size=%d.\n", __func__, - vha->host_no, 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)); - DEBUG13(qla2x00_dump_buffer(entries, size)); + ql_dbg(ql_dbg_disc, vha, 0x203e, + "RPA portname= %8phN size=%d.\n", ct_req->req.rpa.port_name, 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*/ - DEBUG2_3(printk("scsi(%ld): RPA issue IOCB failed (%d).\n", - vha->host_no, rval)); + 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 { - DEBUG2(printk("scsi(%ld): RPA exiting normally.\n", - vha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2041, + "RPA exiting nornally.\n"); } return rval; @@ -1683,7 +1678,8 @@ qla2x00_fdmi_register(scsi_qla_host_t *vha) int rval; struct qla_hw_data *ha = vha->hw; - if (IS_QLA2100(ha) || IS_QLA2200(ha)) + if (IS_QLA2100(ha) || IS_QLA2200(ha) || + IS_QLAFX00(ha)) return QLA_FUNCTION_FAILED; rval = qla2x00_mgmt_svr_login(vha); @@ -1718,7 +1714,7 @@ qla2x00_fdmi_register(scsi_qla_host_t *vha) int qla2x00_gfpn_id(scsi_qla_host_t *vha, sw_info_t *list) { - int rval; + int rval = QLA_SUCCESS; uint16_t i; struct qla_hw_data *ha = vha->hw; ms_iocb_entry_t *ms_pkt; @@ -1728,14 +1724,14 @@ qla2x00_gfpn_id(scsi_qla_host_t *vha, sw_info_t *list) if (!IS_IIDMA_CAPABLE(ha)) return QLA_FUNCTION_FAILED; - for (i = 0; i < MAX_FIBRE_DEVICES; i++) { + 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, + ct_req = qla2x00_prep_ct_req(ha->ct_sns, GFPN_ID_CMD, GFPN_ID_RSP_SIZE); ct_rsp = &ha->ct_sns->p.rsp; @@ -1749,11 +1745,13 @@ qla2x00_gfpn_id(scsi_qla_host_t *vha, sw_info_t *list) sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): GFPN_ID issue IOCB " - "failed (%d).\n", vha->host_no, rval)); + 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, @@ -1800,18 +1798,18 @@ qla24xx_prep_ms_fm_iocb(scsi_qla_host_t *vha, uint32_t req_size, static inline struct ct_sns_req * -qla24xx_prep_ct_fm_req(struct ct_sns_req *ct_req, uint16_t cmd, +qla24xx_prep_ct_fm_req(struct ct_sns_pkt *p, uint16_t cmd, uint16_t rsp_size) { - memset(ct_req, 0, sizeof(struct ct_sns_pkt)); + memset(p, 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); + p->p.req.header.revision = 0x01; + p->p.req.header.gs_type = 0xFA; + p->p.req.header.gs_subtype = 0x01; + p->p.req.command = cpu_to_be16(cmd); + p->p.req.max_rsp_size = cpu_to_be16((rsp_size - 16) / 4); - return ct_req; + return &p->p.req; } /** @@ -1840,15 +1838,15 @@ qla2x00_gpsc(scsi_qla_host_t *vha, sw_info_t *list) if (rval) return rval; - for (i = 0; i < MAX_FIBRE_DEVICES; i++) { + 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_req = qla24xx_prep_ct_fm_req(ha->ct_sns, GPSC_CMD, + GPSC_RSP_SIZE); ct_rsp = &ha->ct_sns->p.rsp; /* Prepare CT arguments -- port_name */ @@ -1860,8 +1858,8 @@ qla2x00_gpsc(scsi_qla_host_t *vha, sw_info_t *list) sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3(printk("scsi(%ld): GPSC issue IOCB " - "failed (%d).\n", vha->host_no, rval)); + 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? */ @@ -1870,9 +1868,9 @@ qla2x00_gpsc(scsi_qla_host_t *vha, sw_info_t *list) CT_REASON_INVALID_COMMAND_CODE || ct_rsp->header.reason_code == CT_REASON_COMMAND_UNSUPPORTED)) { - DEBUG2(printk("scsi(%ld): GPSC command " - "unsupported, disabling query...\n", - vha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x205a, + "GPSC command unsupported, disabling " + "query.\n"); ha->flags.gpsc_supported = 0; rval = QLA_FUNCTION_FAILED; break; @@ -1896,21 +1894,20 @@ qla2x00_gpsc(scsi_qla_host_t *vha, sw_info_t *list) case BIT_11: list[i].fp_speed = PORT_SPEED_8GB; break; + case BIT_10: + list[i].fp_speed = PORT_SPEED_16GB; + break; + case BIT_8: + list[i].fp_speed = PORT_SPEED_32GB; + break; } - DEBUG2_3(printk("scsi(%ld): GPSC ext entry - " - "fpn %02x%02x%02x%02x%02x%02x%02x%02x speeds=%04x " - "speed=%04x.\n", vha->host_no, - 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], + ql_dbg(ql_dbg_disc, vha, 0x205b, + "GPSC ext entry - fpn " + "%8phN speeds=%04x speed=%04x.\n", + list[i].fabric_port_name, be16_to_cpu(ct_rsp->rsp.gpsc.speeds), - be16_to_cpu(ct_rsp->rsp.gpsc.speed))); + be16_to_cpu(ct_rsp->rsp.gpsc.speed)); } /* Last device exit. */ @@ -1940,7 +1937,7 @@ qla2x00_gff_id(scsi_qla_host_t *vha, sw_info_t *list) struct qla_hw_data *ha = vha->hw; uint8_t fcp_scsi_features = 0; - for (i = 0; i < MAX_FIBRE_DEVICES; i++) { + 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; @@ -1954,7 +1951,7 @@ qla2x00_gff_id(scsi_qla_host_t *vha, sw_info_t *list) GFF_ID_RSP_SIZE); /* Prepare CT request */ - ct_req = qla2x00_prep_ct_req(&ha->ct_sns->p.req, GFF_ID_CMD, + ct_req = qla2x00_prep_ct_req(ha->ct_sns, GFF_ID_CMD, GFF_ID_RSP_SIZE); ct_rsp = &ha->ct_sns->p.rsp; @@ -1968,14 +1965,12 @@ qla2x00_gff_id(scsi_qla_host_t *vha, sw_info_t *list) sizeof(ms_iocb_entry_t)); if (rval != QLA_SUCCESS) { - DEBUG2_3(printk(KERN_INFO - "scsi(%ld): GFF_ID issue IOCB failed " - "(%d).\n", vha->host_no, rval)); + 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) { - DEBUG2_3(printk(KERN_INFO - "scsi(%ld): GFF_ID IOCB status had a " - "failure status code\n", vha->host_no)); + 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]; diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 920b76bfbb9..e2184412617 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -17,19 +17,20 @@ #include <asm/prom.h> #endif +#include <target/target_core_base.h> +#include "qla_target.h" + /* * 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_device_resync(scsi_qla_host_t *); static int qla2x00_fabric_dev_login(scsi_qla_host_t *, fc_port_t *, uint16_t *); @@ -41,11 +42,10 @@ static int qla25xx_init_queues(struct qla_hw_data *); /* SRB Extensions ---------------------------------------------------------- */ -static void -qla2x00_ctx_sp_timeout(unsigned long __data) +void +qla2x00_sp_timeout(unsigned long __data) { srb_t *sp = (srb_t *)__data; - struct srb_ctx *ctx; struct srb_iocb *iocb; fc_port_t *fcport = sp->fcport; struct qla_hw_data *ha = fcport->vha->hw; @@ -55,81 +55,26 @@ qla2x00_ctx_sp_timeout(unsigned long __data) spin_lock_irqsave(&ha->hardware_lock, flags); req = ha->req_q_map[0]; req->outstanding_cmds[sp->handle] = NULL; - ctx = sp->ctx; - iocb = ctx->u.iocb_cmd; + iocb = &sp->u.iocb_cmd; iocb->timeout(sp); - iocb->free(sp); + sp->free(fcport->vha, sp); spin_unlock_irqrestore(&ha->hardware_lock, flags); } -static void -qla2x00_ctx_sp_free(srb_t *sp) +void +qla2x00_sp_free(void *data, void *ptr) { - struct srb_ctx *ctx = sp->ctx; - struct srb_iocb *iocb = ctx->u.iocb_cmd; - struct scsi_qla_host *vha = sp->fcport->vha; + 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); - kfree(iocb); - kfree(ctx); - mempool_free(sp, sp->fcport->vha->hw->srb_mempool); - - QLA_VHA_MARK_NOT_BUSY(vha); -} - -inline srb_t * -qla2x00_get_ctx_sp(scsi_qla_host_t *vha, fc_port_t *fcport, size_t size, - unsigned long tmo) -{ - srb_t *sp = NULL; - struct qla_hw_data *ha = vha->hw; - struct srb_ctx *ctx; - struct srb_iocb *iocb; - uint8_t bail; - - QLA_VHA_MARK_BUSY(vha, bail); - if (bail) - return NULL; - - sp = mempool_alloc(ha->srb_mempool, GFP_KERNEL); - if (!sp) - goto done; - ctx = kzalloc(size, GFP_KERNEL); - if (!ctx) { - mempool_free(sp, ha->srb_mempool); - sp = NULL; - goto done; - } - iocb = kzalloc(sizeof(struct srb_iocb), GFP_KERNEL); - if (!iocb) { - mempool_free(sp, ha->srb_mempool); - sp = NULL; - kfree(ctx); - goto done; - } - - memset(sp, 0, sizeof(*sp)); - sp->fcport = fcport; - sp->ctx = ctx; - ctx->u.iocb_cmd = iocb; - iocb->free = qla2x00_ctx_sp_free; - - init_timer(&iocb->timer); - if (!tmo) - goto done; - iocb->timer.expires = jiffies + tmo * HZ; - iocb->timer.data = (unsigned long)sp; - iocb->timer.function = qla2x00_ctx_sp_timeout; - add_timer(&iocb->timer); -done: - if (!sp) - QLA_VHA_MARK_NOT_BUSY(vha); - return sp; + qla2x00_rel_sp(vha, sp); } /* Asynchronous Login/Logout Routines -------------------------------------- */ -static inline unsigned long +unsigned long qla2x00_get_async_timeout(struct scsi_qla_host *vha) { unsigned long tmo; @@ -137,7 +82,9 @@ qla2x00_get_async_timeout(struct scsi_qla_host *vha) /* Firmware should use switch negotiated r_a_tov for timeout. */ tmo = ha->r_a_tov / 10 * 2; - if (!IS_FWI2_CAPABLE(ha)) { + if (IS_QLAFX00(ha)) { + tmo = FX00_DEF_RATOV * 2; + } else if (!IS_FWI2_CAPABLE(ha)) { /* * Except for earlier ISPs where the timeout is seeded from the * initialization control block. @@ -148,20 +95,19 @@ qla2x00_get_async_timeout(struct scsi_qla_host *vha) } static void -qla2x00_async_iocb_timeout(srb_t *sp) +qla2x00_async_iocb_timeout(void *data) { + srb_t *sp = (srb_t *)data; fc_port_t *fcport = sp->fcport; - struct srb_ctx *ctx = sp->ctx; - DEBUG2(printk(KERN_WARNING - "scsi(%ld:%x): Async-%s timeout - portid=%02x%02x%02x.\n", - fcport->vha->host_no, sp->handle, - ctx->name, fcport->d_id.b.domain, - fcport->d_id.b.area, fcport->d_id.b.al_pa)); + 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 (ctx->type == SRB_LOGIN_CMD) { - struct srb_iocb *lio = ctx->u.iocb_cmd; + 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; @@ -173,14 +119,16 @@ qla2x00_async_iocb_timeout(srb_t *sp) } static void -qla2x00_async_login_ctx_done(srb_t *sp) +qla2x00_async_login_sp_done(void *data, void *ptr, int res) { - struct srb_ctx *ctx = sp->ctx; - struct srb_iocb *lio = ctx->u.iocb_cmd; - - qla2x00_post_async_login_done_work(sp->fcport->vha, sp->fcport, - lio->u.logio.data); - lio->free(sp); + 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 @@ -188,22 +136,21 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport, uint16_t *data) { srb_t *sp; - struct srb_ctx *ctx; struct srb_iocb *lio; int rval; rval = QLA_FUNCTION_FAILED; - sp = qla2x00_get_ctx_sp(vha, fcport, sizeof(struct srb_ctx), - qla2x00_get_async_timeout(vha) + 2); + sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); if (!sp) goto done; - ctx = sp->ctx; - ctx->type = SRB_LOGIN_CMD; - ctx->name = "login"; - lio = ctx->u.iocb_cmd; + 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; - lio->done = qla2x00_async_login_ctx_done; + 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; @@ -211,75 +158,78 @@ qla2x00_async_login(struct scsi_qla_host *vha, fc_port_t *fcport, if (rval != QLA_SUCCESS) goto done_free_sp; - DEBUG2(printk(KERN_DEBUG - "scsi(%ld:%x): Async-login - loop-id=%x portid=%02x%02x%02x " - "retries=%d.\n", fcport->vha->host_no, sp->handle, fcport->loop_id, + 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)); + fcport->login_retry); return rval; done_free_sp: - lio->free(sp); + sp->free(fcport->vha, sp); done: return rval; } static void -qla2x00_async_logout_ctx_done(srb_t *sp) +qla2x00_async_logout_sp_done(void *data, void *ptr, int res) { - struct srb_ctx *ctx = sp->ctx; - struct srb_iocb *lio = ctx->u.iocb_cmd; - - qla2x00_post_async_logout_done_work(sp->fcport->vha, sp->fcport, - lio->u.logio.data); - lio->free(sp); + 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_ctx *ctx; struct srb_iocb *lio; int rval; rval = QLA_FUNCTION_FAILED; - sp = qla2x00_get_ctx_sp(vha, fcport, sizeof(struct srb_ctx), - qla2x00_get_async_timeout(vha) + 2); + sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); if (!sp) goto done; - ctx = sp->ctx; - ctx->type = SRB_LOGOUT_CMD; - ctx->name = "logout"; - lio = ctx->u.iocb_cmd; + 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; - lio->done = qla2x00_async_logout_ctx_done; + sp->done = qla2x00_async_logout_sp_done; rval = qla2x00_start_sp(sp); if (rval != QLA_SUCCESS) goto done_free_sp; - DEBUG2(printk(KERN_DEBUG - "scsi(%ld:%x): Async-logout - loop-id=%x portid=%02x%02x%02x.\n", - fcport->vha->host_no, sp->handle, fcport->loop_id, - fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa)); + 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: - lio->free(sp); + sp->free(fcport->vha, sp); done: return rval; } static void -qla2x00_async_adisc_ctx_done(srb_t *sp) +qla2x00_async_adisc_sp_done(void *data, void *ptr, int res) { - struct srb_ctx *ctx = sp->ctx; - struct srb_iocb *lio = ctx->u.iocb_cmd; - - qla2x00_post_async_adisc_done_work(sp->fcport->vha, sp->fcport, - lio->u.logio.data); - lio->free(sp); + 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 @@ -287,49 +237,55 @@ qla2x00_async_adisc(struct scsi_qla_host *vha, fc_port_t *fcport, uint16_t *data) { srb_t *sp; - struct srb_ctx *ctx; struct srb_iocb *lio; int rval; rval = QLA_FUNCTION_FAILED; - sp = qla2x00_get_ctx_sp(vha, fcport, sizeof(struct srb_ctx), - qla2x00_get_async_timeout(vha) + 2); + sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); if (!sp) goto done; - ctx = sp->ctx; - ctx->type = SRB_ADISC_CMD; - ctx->name = "adisc"; - lio = ctx->u.iocb_cmd; + 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; - lio->done = qla2x00_async_adisc_ctx_done; + 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; - DEBUG2(printk(KERN_DEBUG - "scsi(%ld:%x): Async-adisc - loop-id=%x portid=%02x%02x%02x.\n", - fcport->vha->host_no, sp->handle, fcport->loop_id, - fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa)); - + 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: - lio->free(sp); + sp->free(fcport->vha, sp); done: return rval; } static void -qla2x00_async_tm_cmd_ctx_done(srb_t *sp) +qla2x00_tmf_iocb_timeout(void *data) { - struct srb_ctx *ctx = sp->ctx; - struct srb_iocb *iocb = (struct srb_iocb *)ctx->u.iocb_cmd; + srb_t *sp = (srb_t *)data; + struct srb_iocb *tmf = &sp->u.iocb_cmd; - qla2x00_async_tm_cmd_done(sp->fcport->vha, sp->fcport, iocb); - iocb->free(sp); + tmf->u.tmf.comp_status = CS_TIMEOUT; + complete(&tmf->u.tmf.comp); +} + +static void +qla2x00_tmf_sp_done(void *data, void *ptr, int res) +{ + srb_t *sp = (srb_t *)ptr; + struct srb_iocb *tmf = &sp->u.iocb_cmd; + complete(&tmf->u.tmf.comp); } int @@ -337,44 +293,148 @@ qla2x00_async_tm_cmd(fc_port_t *fcport, uint32_t flags, uint32_t lun, uint32_t tag) { struct scsi_qla_host *vha = fcport->vha; + struct srb_iocb *tm_iocb; srb_t *sp; - struct srb_ctx *ctx; - struct srb_iocb *tcf; - int rval; + int rval = QLA_FUNCTION_FAILED; - rval = QLA_FUNCTION_FAILED; - sp = qla2x00_get_ctx_sp(vha, fcport, sizeof(struct srb_ctx), - qla2x00_get_async_timeout(vha) + 2); + sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); if (!sp) goto done; - ctx = sp->ctx; - ctx->type = SRB_TM_CMD; - ctx->name = "tmf"; - tcf = ctx->u.iocb_cmd; - tcf->u.tmf.flags = flags; - tcf->u.tmf.lun = lun; - tcf->u.tmf.data = tag; - tcf->timeout = qla2x00_async_iocb_timeout; - tcf->done = qla2x00_async_tm_cmd_ctx_done; + tm_iocb = &sp->u.iocb_cmd; + sp->type = SRB_TM_CMD; + sp->name = "tmf"; + qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha)); + tm_iocb->u.tmf.flags = flags; + tm_iocb->u.tmf.lun = lun; + tm_iocb->u.tmf.data = tag; + sp->done = qla2x00_tmf_sp_done; + tm_iocb->timeout = qla2x00_tmf_iocb_timeout; + init_completion(&tm_iocb->u.tmf.comp); rval = qla2x00_start_sp(sp); if (rval != QLA_SUCCESS) goto done_free_sp; - DEBUG2(printk(KERN_DEBUG - "scsi(%ld:%x): Async-tmf - loop-id=%x portid=%02x%02x%02x.\n", - fcport->vha->host_no, sp->handle, fcport->loop_id, - fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa)); + 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); + + wait_for_completion(&tm_iocb->u.tmf.comp); + rval = tm_iocb->u.tmf.comp_status == CS_COMPLETE ? + QLA_SUCCESS : QLA_FUNCTION_FAILED; + + if ((rval != QLA_SUCCESS) || tm_iocb->u.tmf.data) { + ql_dbg(ql_dbg_taskm, vha, 0x8030, + "TM IOCB failed (%x).\n", rval); + } + + if (!test_bit(UNLOADING, &vha->dpc_flags) && !IS_QLAFX00(vha->hw)) { + flags = tm_iocb->u.tmf.flags; + lun = (uint16_t)tm_iocb->u.tmf.lun; + + /* Issue Marker IOCB */ + 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); + } + +done_free_sp: + sp->free(vha, sp); +done: return rval; +} + +static void +qla24xx_abort_iocb_timeout(void *data) +{ + srb_t *sp = (srb_t *)data; + struct srb_iocb *abt = &sp->u.iocb_cmd; + + abt->u.abt.comp_status = CS_TIMEOUT; + complete(&abt->u.abt.comp); +} + +static void +qla24xx_abort_sp_done(void *data, void *ptr, int res) +{ + srb_t *sp = (srb_t *)ptr; + struct srb_iocb *abt = &sp->u.iocb_cmd; + + complete(&abt->u.abt.comp); +} + +static int +qla24xx_async_abort_cmd(srb_t *cmd_sp) +{ + scsi_qla_host_t *vha = cmd_sp->fcport->vha; + fc_port_t *fcport = cmd_sp->fcport; + struct srb_iocb *abt_iocb; + srb_t *sp; + int rval = QLA_FUNCTION_FAILED; + + sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); + if (!sp) + goto done; + + abt_iocb = &sp->u.iocb_cmd; + sp->type = SRB_ABT_CMD; + sp->name = "abort"; + qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha)); + abt_iocb->u.abt.cmd_hndl = cmd_sp->handle; + sp->done = qla24xx_abort_sp_done; + abt_iocb->timeout = qla24xx_abort_iocb_timeout; + init_completion(&abt_iocb->u.abt.comp); + + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) + goto done_free_sp; + + ql_dbg(ql_dbg_async, vha, 0x507c, + "Abort command issued - hdl=%x, target_id=%x\n", + cmd_sp->handle, fcport->tgt_id); + + wait_for_completion(&abt_iocb->u.abt.comp); + + rval = abt_iocb->u.abt.comp_status == CS_COMPLETE ? + QLA_SUCCESS : QLA_FUNCTION_FAILED; done_free_sp: - tcf->free(sp); + sp->free(vha, sp); done: return rval; } +int +qla24xx_async_abort_command(srb_t *sp) +{ + unsigned long flags = 0; + + 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; + + spin_lock_irqsave(&ha->hardware_lock, flags); + for (handle = 1; handle < req->num_outstanding_cmds; handle++) { + if (req->outstanding_cmds[handle] == sp) + break; + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); + if (handle == req->num_outstanding_cmds) { + /* Command not found. */ + return QLA_FUNCTION_FAILED; + } + if (sp->type == SRB_FXIOCB_DCMD) + return qlafx00_fx_disc(vha, &vha->hw->mr.fcport, + FXDISC_ABORT_IOCTL); + + return qla24xx_async_abort_cmd(sp); +} + void qla2x00_async_login_done(struct scsi_qla_host *vha, fc_port_t *fcport, uint16_t *data) @@ -389,6 +449,13 @@ qla2x00_async_login_done(struct scsi_qla_host *vha, fc_port_t *fcport, * 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); @@ -454,34 +521,82 @@ qla2x00_async_adisc_done(struct scsi_qla_host *vha, fc_port_t *fcport, return; } -void -qla2x00_async_tm_cmd_done(struct scsi_qla_host *vha, fc_port_t *fcport, - struct srb_iocb *iocb) +/****************************************************************************/ +/* QLogic ISP2x00 Hardware Support Functions. */ +/****************************************************************************/ + +static int +qla83xx_nic_core_fw_load(scsi_qla_host_t *vha) { - int rval; - uint32_t flags; - uint16_t lun; + int rval = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + uint32_t idc_major_ver, idc_minor_ver; + uint16_t config[4]; - flags = iocb->u.tmf.flags; - lun = (uint16_t)iocb->u.tmf.lun; + qla83xx_idc_lock(vha, 0); - /* Issue Marker IOCB */ - rval = qla2x00_marker(vha, vha->hw->req_q_map[0], - vha->hw->rsp_q_map[0], fcport->loop_id, lun, - flags == TCF_LUN_RESET ? MK_SYNC_ID_LUN : MK_SYNC_ID); + /* SV: TODO: Assign initialization timeout from + * flash-info / other param + */ + ha->fcoe_dev_init_timeout = QLA83XX_IDC_INITIALIZATION_TIMEOUT; + ha->fcoe_reset_timeout = QLA83XX_IDC_RESET_ACK_TIMEOUT; - if ((rval != QLA_SUCCESS) || iocb->u.tmf.data) { - DEBUG2_3_11(printk(KERN_WARNING - "%s(%ld): TM IOCB failed (%x).\n", - __func__, vha->host_no, rval)); + /* Set our fcoe function presence */ + if (__qla83xx_set_drv_presence(vha) != QLA_SUCCESS) { + ql_dbg(ql_dbg_p3p, vha, 0xb077, + "Error while setting DRV-Presence.\n"); + rval = QLA_FUNCTION_FAILED; + goto exit; } - return; -} + /* Decide the reset ownership */ + qla83xx_reset_ownership(vha); -/****************************************************************************/ -/* QLogic ISP2x00 Hardware Support Functions. */ -/****************************************************************************/ + /* + * On first protocol driver load: + * Init-Owner: Set IDC-Major-Version and Clear IDC-Lock-Recovery + * register. + * Others: Check compatibility with current IDC Major version. + */ + qla83xx_rd_reg(vha, QLA83XX_IDC_MAJOR_VERSION, &idc_major_ver); + if (ha->flags.nic_core_reset_owner) { + /* Set IDC Major version */ + idc_major_ver = QLA83XX_SUPP_IDC_MAJOR_VERSION; + qla83xx_wr_reg(vha, QLA83XX_IDC_MAJOR_VERSION, idc_major_ver); + + /* Clearing IDC-Lock-Recovery register */ + qla83xx_wr_reg(vha, QLA83XX_IDC_LOCK_RECOVERY, 0); + } else if (idc_major_ver != QLA83XX_SUPP_IDC_MAJOR_VERSION) { + /* + * Clear further IDC participation if we are not compatible with + * the current IDC Major Version. + */ + ql_log(ql_log_warn, vha, 0xb07d, + "Failing load, idc_major_ver=%d, expected_major_ver=%d.\n", + idc_major_ver, QLA83XX_SUPP_IDC_MAJOR_VERSION); + __qla83xx_clear_drv_presence(vha); + rval = QLA_FUNCTION_FAILED; + goto exit; + } + /* Each function sets its supported Minor version. */ + qla83xx_rd_reg(vha, QLA83XX_IDC_MINOR_VERSION, &idc_minor_ver); + idc_minor_ver |= (QLA83XX_SUPP_IDC_MINOR_VERSION << (ha->portnum * 2)); + qla83xx_wr_reg(vha, QLA83XX_IDC_MINOR_VERSION, idc_minor_ver); + + if (ha->flags.nic_core_reset_owner) { + memset(config, 0, sizeof(config)); + if (!qla81xx_get_port_config(vha, config)) + qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, + QLA8XXX_DEV_READY); + } + + rval = qla83xx_idc_state_handler(vha); + +exit: + qla83xx_idc_unlock(vha, 0); + + return rval; +} /* * qla2x00_initialize_adapter @@ -506,7 +621,7 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha) vha->flags.reset_active = 0; ha->flags.pci_channel_io_perm_failure = 0; ha->flags.eeh_busy = 0; - ha->flags.thermal_supported = 1; + vha->qla_stats.jiffies_at_last_reset = get_jiffies_64(); atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); atomic_set(&vha->loop_state, LOOP_DOWN); vha->device_flags = DFLG_NO_CABLE; @@ -519,11 +634,12 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha) set_bit(0, ha->req_qid_map); set_bit(0, ha->rsp_qid_map); - qla_printk(KERN_INFO, ha, "Configuring PCI space...\n"); + ql_dbg(ql_dbg_init, vha, 0x0040, + "Configuring PCI space...\n"); rval = ha->isp_ops->pci_config(vha); if (rval) { - DEBUG2(printk("scsi(%ld): Unable to configure PCI space.\n", - vha->host_no)); + ql_log(ql_log_warn, vha, 0x0044, + "Unable to configure PCI space.\n"); return (rval); } @@ -531,29 +647,37 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha) rval = qla2xxx_get_flash_info(vha); if (rval) { - DEBUG2(printk("scsi(%ld): Unable to validate FLASH data.\n", - vha->host_no)); - return (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); + if (IS_QLA8044(ha)) { + qla8044_read_reset_template(vha); - qla_printk(KERN_INFO, ha, "Configure NVRAM parameters...\n"); + /* NOTE: If ql2xdontresethba==1, set IDC_CTRL DONTRESET_BIT0. + * If DONRESET_BIT0 is set, drivers should not set dev_state + * to NEED_RESET. But if NEED_RESET is set, drivers should + * should honor the reset. */ + if (ql2xdontresethba == 1) + qla8044_set_idc_dontreset(vha); + } + + 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? */ - qla_printk(KERN_INFO, ha, "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]); + ql_log(ql_log_info, vha, 0x0077, + "Masking HBA WWPN %8phN (via NVRAM).\n", vha->port_name); return QLA_FUNCTION_FAILED; } - qla_printk(KERN_INFO, ha, "Verifying loaded RISC code...\n"); + 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); @@ -567,27 +691,43 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha) if (IS_QLA84XX(ha)) { ha->cs84xx = qla84xx_get_chip(vha); if (!ha->cs84xx) { - qla_printk(KERN_ERR, ha, + ql_log(ql_log_warn, vha, 0x00d0, "Unable to configure ISP84XX.\n"); return QLA_FUNCTION_FAILED; } } - rval = qla2x00_init_rings(vha); + + if (qla_ini_mode_enabled(vha)) + 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) { - qla_printk(KERN_ERR, ha, - "Unable to initialize ISP84XX.\n"); + ql_log(ql_log_warn, vha, 0x00d4, + "Unable to initialize ISP84XX.\n"); qla84xx_put_chip(vha); } } + /* Load the NIC Core f/w if we are the first protocol driver. */ + if (IS_QLA8031(ha)) { + rval = qla83xx_nic_core_fw_load(vha); + if (rval) + ql_log(ql_log_warn, vha, 0x0124, + "Error in initializing NIC Core f/w.\n"); + } + if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha)) qla24xx_read_fcp_prio_cfg(vha); + if (IS_P3P_TYPE(ha)) + qla82xx_set_driver_version(vha, QLA2XXX_VERSION); + else + qla25xx_set_driver_version(vha, QLA2XXX_VERSION); + return (rval); } @@ -733,8 +873,8 @@ qla24xx_pci_config(scsi_qla_host_t *vha) 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); + if (pci_is_pcie(ha->pdev)) + pcie_set_readrq(ha->pdev, 4096); pci_disable_rom(ha->pdev); @@ -769,8 +909,8 @@ qla25xx_pci_config(scsi_qla_host_t *vha) 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); + if (pci_is_pcie(ha->pdev)) + pcie_set_readrq(ha->pdev, 4096); pci_disable_rom(ha->pdev); @@ -797,9 +937,7 @@ qla2x00_isp_firmware(scsi_qla_host_t *vha) rval = QLA_FUNCTION_FAILED; if (ha->flags.disable_risc_code_load) { - DEBUG2(printk("scsi(%ld): RISC CODE NOT loaded\n", - vha->host_no)); - qla_printk(KERN_INFO, ha, "RISC CODE NOT loaded\n"); + 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); @@ -810,10 +948,9 @@ qla2x00_isp_firmware(scsi_qla_host_t *vha) } } - if (rval) { - DEBUG2_3(printk("scsi(%ld): **** Load RISC code ****\n", - vha->host_no)); - } + if (rval) + ql_dbg(ql_dbg_init, vha, 0x007a, + "**** Load RISC code ****.\n"); return (rval); } @@ -967,11 +1104,14 @@ qla2x00_reset_chip(scsi_qla_host_t *vha) * * Returns 0 on success. */ -int +static 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); } @@ -1062,6 +1202,83 @@ qla24xx_reset_risc(scsi_qla_host_t *vha) ha->isp_ops->enable_intrs(ha); } +static void +qla25xx_read_risc_sema_reg(scsi_qla_host_t *vha, uint32_t *data) +{ + struct device_reg_24xx __iomem *reg = &vha->hw->iobase->isp24; + + WRT_REG_DWORD(®->iobase_addr, RISC_REGISTER_BASE_OFFSET); + *data = RD_REG_DWORD(®->iobase_window + RISC_REGISTER_WINDOW_OFFET); + +} + +static void +qla25xx_write_risc_sema_reg(scsi_qla_host_t *vha, uint32_t data) +{ + struct device_reg_24xx __iomem *reg = &vha->hw->iobase->isp24; + + WRT_REG_DWORD(®->iobase_addr, RISC_REGISTER_BASE_OFFSET); + WRT_REG_DWORD(®->iobase_window + RISC_REGISTER_WINDOW_OFFET, data); +} + +static void +qla25xx_manipulate_risc_semaphore(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t wd32 = 0; + uint delta_msec = 100; + uint elapsed_msec = 0; + uint timeout_msec; + ulong n; + + if (!IS_QLA25XX(ha) && !IS_QLA2031(ha)) + return; + +attempt: + timeout_msec = TIMEOUT_SEMAPHORE; + n = timeout_msec / delta_msec; + while (n--) { + qla25xx_write_risc_sema_reg(vha, RISC_SEMAPHORE_SET); + qla25xx_read_risc_sema_reg(vha, &wd32); + if (wd32 & RISC_SEMAPHORE) + break; + msleep(delta_msec); + elapsed_msec += delta_msec; + if (elapsed_msec > TIMEOUT_TOTAL_ELAPSED) + goto force; + } + + if (!(wd32 & RISC_SEMAPHORE)) + goto force; + + if (!(wd32 & RISC_SEMAPHORE_FORCE)) + goto acquired; + + qla25xx_write_risc_sema_reg(vha, RISC_SEMAPHORE_CLR); + timeout_msec = TIMEOUT_SEMAPHORE_FORCE; + n = timeout_msec / delta_msec; + while (n--) { + qla25xx_read_risc_sema_reg(vha, &wd32); + if (!(wd32 & RISC_SEMAPHORE_FORCE)) + break; + msleep(delta_msec); + elapsed_msec += delta_msec; + if (elapsed_msec > TIMEOUT_TOTAL_ELAPSED) + goto force; + } + + if (wd32 & RISC_SEMAPHORE_FORCE) + qla25xx_write_risc_sema_reg(vha, RISC_SEMAPHORE_FORCE_CLR); + + goto attempt; + +force: + qla25xx_write_risc_sema_reg(vha, RISC_SEMAPHORE_FORCE_SET); + +acquired: + return; +} + /** * qla24xx_reset_chip() - Reset ISP24xx chip. * @ha: HA context @@ -1080,6 +1297,8 @@ qla24xx_reset_chip(scsi_qla_host_t *vha) ha->isp_ops->disable_intrs(ha); + qla25xx_manipulate_risc_semaphore(vha); + /* Perform RISC reset. */ qla24xx_reset_risc(vha); } @@ -1105,8 +1324,8 @@ qla2x00_chip_diag(scsi_qla_host_t *vha) /* Assume a failed state */ rval = QLA_FUNCTION_FAILED; - DEBUG3(printk("scsi(%ld): Testing device at %lx.\n", - vha->host_no, (u_long)®->flash_address)); + ql_dbg(ql_dbg_init, vha, 0x007b, + "Testing device at %lx.\n", (u_long)®->flash_address); spin_lock_irqsave(&ha->hardware_lock, flags); @@ -1128,8 +1347,8 @@ qla2x00_chip_diag(scsi_qla_host_t *vha) if (!cnt) goto chip_diag_failed; - DEBUG3(printk("scsi(%ld): Reset register cleared by chip reset\n", - vha->host_no)); + ql_dbg(ql_dbg_init, vha, 0x007c, + "Reset register cleared by chip reset.\n"); /* Reset RISC processor. */ WRT_REG_WORD(®->hccr, HCCR_RESET_RISC); @@ -1150,7 +1369,7 @@ qla2x00_chip_diag(scsi_qla_host_t *vha) goto chip_diag_failed; /* Check product ID of chip */ - DEBUG3(printk("scsi(%ld): Checking product ID of chip\n", vha->host_no)); + 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); @@ -1158,8 +1377,9 @@ qla2x00_chip_diag(scsi_qla_host_t *vha) 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) { - qla_printk(KERN_WARNING, ha, - "Wrong product ID = 0x%x,0x%x,0x%x\n", mb[1], mb[2], mb[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; } @@ -1178,8 +1398,7 @@ qla2x00_chip_diag(scsi_qla_host_t *vha) if (IS_QLA2200(ha) && RD_MAILBOX_REG(ha, reg, 7) == QLA2200A_RISC_ROM_VER) { /* Limit firmware transfer size with a 2200A */ - DEBUG3(printk("scsi(%ld): Found QLA2200A chip.\n", - vha->host_no)); + ql_dbg(ql_dbg_init, vha, 0x007e, "Found QLA2200A Chip.\n"); ha->device_type |= DT_ISP2200A; ha->fw_transfer_size = 128; @@ -1188,24 +1407,20 @@ qla2x00_chip_diag(scsi_qla_host_t *vha) /* Wrap Incoming Mailboxes Test. */ spin_unlock_irqrestore(&ha->hardware_lock, flags); - DEBUG3(printk("scsi(%ld): Checking mailboxes.\n", vha->host_no)); + ql_dbg(ql_dbg_init, vha, 0x007f, "Checking mailboxes.\n"); rval = qla2x00_mbx_reg_test(vha); - if (rval) { - DEBUG(printk("scsi(%ld): Failed mailbox send register test\n", - vha->host_no)); - qla_printk(KERN_WARNING, ha, - "Failed mailbox send register test\n"); - } - else { + 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) - DEBUG2_3(printk("scsi(%ld): Chip diagnostics **** FAILED " - "****\n", vha->host_no)); + ql_log(ql_log_info, vha, 0x0081, + "Chip diagnostics **** FAILED ****.\n"); spin_unlock_irqrestore(&ha->hardware_lock, flags); @@ -1225,17 +1440,15 @@ qla24xx_chip_diag(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; struct req_que *req = ha->req_q_map[0]; - if (IS_QLA82XX(ha)) + if (IS_P3P_TYPE(ha)) return QLA_SUCCESS; ha->fw_transfer_size = REQUEST_ENTRY_SIZE * req->length; rval = qla2x00_mbx_reg_test(vha); if (rval) { - DEBUG(printk("scsi(%ld): Failed mailbox send register test\n", - vha->host_no)); - qla_printk(KERN_WARNING, ha, - "Failed mailbox send register test\n"); + ql_log(ql_log_warn, vha, 0x0082, + "Failed mailbox send register test.\n"); } else { /* Flag a successful rval */ rval = QLA_SUCCESS; @@ -1257,13 +1470,19 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) struct rsp_que *rsp = ha->rsp_q_map[0]; if (ha->fw_dump) { - qla_printk(KERN_WARNING, ha, - "Firmware dump previously allocated.\n"); + 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; + ha->fw_dump_cap_flags = 0; + dump_size = fixed_size = mem_size = eft_size = fce_size = mq_size = 0; + req_q_size = rsp_q_size = 0; + + if (IS_QLA27XX(ha)) + goto try_fce; + if (IS_QLA2100(ha) || IS_QLA2200(ha)) { fixed_size = sizeof(struct qla2100_fw_dump); } else if (IS_QLA23XX(ha)) { @@ -1271,25 +1490,48 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) mem_size = (ha->fw_memory_size - 0x11000 + 1) * sizeof(uint16_t); } else if (IS_FWI2_CAPABLE(ha)) { - if (IS_QLA81XX(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) - mq_size = sizeof(struct qla2xxx_mq_chain); + 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)); + } + if (ha->tgt.atio_ring) + mq_size += ha->tgt.atio_q_length * sizeof(request_t); /* Allocate memory for Fibre Channel Event Buffer. */ - if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha)) + if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha) && + !IS_QLA27XX(ha)) goto try_eft; +try_fce: + if (ha->fce) + dma_free_coherent(&ha->pdev->dev, + FCE_SIZE, ha->fce, ha->fce_dma); + + /* Allocate memory for Fibre Channel Event Buffer. */ tc = dma_alloc_coherent(&ha->pdev->dev, FCE_SIZE, &tc_dma, GFP_KERNEL); if (!tc) { - qla_printk(KERN_WARNING, ha, "Unable to allocate " - "(%d KB) for FCE.\n", FCE_SIZE / 1024); + ql_log(ql_log_warn, vha, 0x00be, + "Unable to allocate (%d KB) for FCE.\n", + FCE_SIZE / 1024); goto try_eft; } @@ -1297,61 +1539,79 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) rval = qla2x00_enable_fce_trace(vha, tc_dma, FCE_NUM_BUFFERS, ha->fce_mb, &ha->fce_bufs); if (rval) { - qla_printk(KERN_WARNING, ha, "Unable to initialize " - "FCE (%d).\n", 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; } - - qla_printk(KERN_INFO, ha, "Allocated (%d KB) for FCE...\n", - FCE_SIZE / 1024); + 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: + if (ha->eft) + dma_free_coherent(&ha->pdev->dev, + EFT_SIZE, ha->eft, ha->eft_dma); + /* Allocate memory for Extended Trace Buffer. */ tc = dma_alloc_coherent(&ha->pdev->dev, EFT_SIZE, &tc_dma, GFP_KERNEL); if (!tc) { - qla_printk(KERN_WARNING, ha, "Unable to allocate " - "(%d KB) for EFT.\n", EFT_SIZE / 1024); + 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) { - qla_printk(KERN_WARNING, ha, "Unable to initialize " - "EFT (%d).\n", 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; } - - qla_printk(KERN_INFO, ha, "Allocated (%d KB) for EFT...\n", - EFT_SIZE / 1024); + 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: + if (IS_QLA27XX(ha)) { + if (!ha->fw_dump_template) { + ql_log(ql_log_warn, vha, 0x00ba, + "Failed missing fwdump template\n"); + return; + } + dump_size = qla27xx_fwdt_calculate_dump_size(vha); + ql_dbg(ql_dbg_init, vha, 0x00fa, + "-> allocating fwdump (%x bytes)...\n", dump_size); + goto allocate; + } + 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; +allocate: ha->fw_dump = vmalloc(dump_size); if (!ha->fw_dump) { - qla_printk(KERN_WARNING, ha, "Unable to allocate (%d KB) for " - "firmware dump!!!\n", dump_size / 1024); + 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, @@ -1368,10 +1628,13 @@ cont_alloc: } return; } - qla_printk(KERN_INFO, ha, "Allocated (%d KB) for firmware dump...\n", - dump_size / 1024); - ha->fw_dump_len = dump_size; + ql_dbg(ql_dbg_init, vha, 0x00c5, + "Allocated (%d KB) for firmware dump.\n", dump_size / 1024); + + if (IS_QLA27XX(ha)) + return; + ha->fw_dump->signature[0] = 'Q'; ha->fw_dump->signature[1] = 'L'; ha->fw_dump->signature[2] = 'G'; @@ -1398,23 +1661,21 @@ qla81xx_mpi_sync(scsi_qla_host_t *vha) int rval; uint16_t dc; uint32_t dw; - struct qla_hw_data *ha = vha->hw; if (!IS_QLA81XX(vha->hw)) return QLA_SUCCESS; rval = qla2x00_write_ram_word(vha, 0x7c00, 1); if (rval != QLA_SUCCESS) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "Sync-MPI: Unable to acquire semaphore.\n")); + 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) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "Sync-MPI: Unable to read sync.\n")); + ql_log(ql_log_warn, vha, 0x0067, "Unable to read sync.\n"); goto done_release; } @@ -1426,21 +1687,61 @@ qla81xx_mpi_sync(scsi_qla_host_t *vha) dw |= dc; rval = qla2x00_write_ram_word(vha, 0x7a15, dw); if (rval != QLA_SUCCESS) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "Sync-MPI: Unable to gain sync.\n")); + 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) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "Sync-MPI: Unable to release semaphore.\n")); + ql_log(ql_log_warn, vha, 0x006d, + "Unable to release semaphore.\n"); } done: return rval; } +int +qla2x00_alloc_outstanding_cmds(struct qla_hw_data *ha, struct req_que *req) +{ + /* Don't try to reallocate the array */ + if (req->outstanding_cmds) + return QLA_SUCCESS; + + if (!IS_FWI2_CAPABLE(ha) || (ha->mqiobase && + (ql2xmultique_tag || ql2xmaxqueues > 1))) + req->num_outstanding_cmds = DEFAULT_OUTSTANDING_COMMANDS; + else { + if (ha->fw_xcb_count <= ha->fw_iocb_count) + req->num_outstanding_cmds = ha->fw_xcb_count; + else + req->num_outstanding_cmds = ha->fw_iocb_count; + } + + req->outstanding_cmds = kzalloc(sizeof(srb_t *) * + req->num_outstanding_cmds, GFP_KERNEL); + + if (!req->outstanding_cmds) { + /* + * Try to allocate a minimal size just so we can get through + * initialization. + */ + req->num_outstanding_cmds = MIN_OUTSTANDING_COMMANDS; + req->outstanding_cmds = kzalloc(sizeof(srb_t *) * + req->num_outstanding_cmds, GFP_KERNEL); + + if (!req->outstanding_cmds) { + ql_log(ql_log_fatal, NULL, 0x0126, + "Failed to allocate memory for " + "outstanding_cmds for req_que %p.\n", req); + req->num_outstanding_cmds = 0; + return QLA_FUNCTION_FAILED; + } + } + + return QLA_SUCCESS; +} + /** * qla2x00_setup_chip() - Load and start RISC firmware. * @ha: HA context @@ -1457,7 +1758,7 @@ qla2x00_setup_chip(scsi_qla_host_t *vha) unsigned long flags; uint16_t fw_major_version; - if (IS_QLA82XX(ha)) { + if (IS_P3P_TYPE(ha)) { rval = ha->isp_ops->load_risc(vha, &srisc_address); if (rval == QLA_SUCCESS) { qla2x00_stop_firmware(vha); @@ -1479,27 +1780,24 @@ qla2x00_setup_chip(scsi_qla_host_t *vha) /* Load firmware sequences */ rval = ha->isp_ops->load_risc(vha, &srisc_address); if (rval == QLA_SUCCESS) { - DEBUG(printk("scsi(%ld): Verifying Checksum of loaded RISC " - "code.\n", vha->host_no)); + 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. */ - DEBUG(printk("scsi(%ld): Checksum OK, start " - "firmware.\n", vha->host_no)); + 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; - rval = qla2x00_get_fw_version(vha, - &ha->fw_major_version, - &ha->fw_minor_version, - &ha->fw_subminor_version, - &ha->fw_attributes, &ha->fw_memory_size, - ha->mpi_version, &ha->mpi_capabilities, - ha->phy_version); + if (IS_P3P_TYPE(ha)) + qla82xx_check_md_needed(vha); + else + rval = qla2x00_get_fw_version(vha); if (rval != QLA_SUCCESS) goto failed; ha->flags.npiv_supported = 0; @@ -1513,20 +1811,31 @@ enable_82xx_npiv: MIN_MULTI_ID_FABRIC - 1; } qla2x00_get_resource_cnts(vha, NULL, - &ha->fw_xcb_count, NULL, NULL, + &ha->fw_xcb_count, NULL, &ha->fw_iocb_count, &ha->max_npiv_vports, NULL); - if (!fw_major_version && ql2xallocfwdump) { - if (!IS_QLA82XX(ha)) - qla2x00_alloc_fw_dump(vha); - } + /* + * Allocate the array of outstanding commands + * now that we know the firmware resources. + */ + rval = qla2x00_alloc_outstanding_cmds(ha, + vha->req); + if (rval != QLA_SUCCESS) + goto failed; + + if (!fw_major_version && ql2xallocfwdump + && !(IS_P3P_TYPE(ha))) + qla2x00_alloc_fw_dump(vha); + } else { + goto failed; } } else { - DEBUG2(printk(KERN_INFO - "scsi(%ld): ISP Firmware failed checksum.\n", - vha->host_no)); + ql_log(ql_log_fatal, vha, 0x00cd, + "ISP Firmware failed checksum.\n"); + goto failed; } - } + } else + goto failed; if (!IS_FWI2_CAPABLE(ha) && !IS_QLA2100(ha) && !IS_QLA2200(ha)) { /* Enable proper parity. */ @@ -1549,16 +1858,21 @@ enable_82xx_npiv: ha->flags.fac_supported = 1; ha->fdt_block_size = size << 2; } else { - qla_printk(KERN_ERR, ha, + 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); + + if (IS_QLA83XX(ha) || IS_QLA27XX(ha)) { + ha->flags.fac_supported = 0; + rval = QLA_SUCCESS; + } } } failed: if (rval) { - DEBUG2_3(printk("scsi(%ld): Setup chip **** FAILED ****.\n", - vha->host_no)); + ql_log(ql_log_fatal, vha, 0x00cf, + "Setup chip ****FAILED****.\n"); } return (rval); @@ -1608,10 +1922,11 @@ qla2x00_update_fw_options(scsi_qla_host_t *vha) return; /* Serial Link options. */ - DEBUG3(printk("scsi(%ld): Serial link options:\n", - vha->host_no)); - DEBUG3(qla2x00_dump_buffer((uint8_t *)&ha->fw_seriallink_options, - sizeof(ha->fw_seriallink_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) { @@ -1676,7 +1991,7 @@ qla24xx_update_fw_options(scsi_qla_host_t *vha) int rval; struct qla_hw_data *ha = vha->hw; - if (IS_QLA82XX(ha)) + if (IS_P3P_TYPE(ha)) return; /* Update Serial Link options. */ @@ -1688,7 +2003,7 @@ qla24xx_update_fw_options(scsi_qla_host_t *vha) le16_to_cpu(ha->fw_seriallink_options24[2]), le16_to_cpu(ha->fw_seriallink_options24[3])); if (rval != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x0104, "Unable to update Serial Link options (%x).\n", rval); } } @@ -1730,7 +2045,7 @@ qla24xx_config_rings(struct scsi_qla_host *vha) 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. */ + /* 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); @@ -1741,13 +2056,24 @@ qla24xx_config_rings(struct scsi_qla_host *vha) 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) { + /* Setup ATIO queue dma pointers for target mode */ + icb->atio_q_inpointer = __constant_cpu_to_le16(0); + icb->atio_q_length = cpu_to_le16(ha->tgt.atio_q_length); + icb->atio_q_address[0] = cpu_to_le32(LSD(ha->tgt.atio_dma)); + icb->atio_q_address[1] = cpu_to_le32(MSD(ha->tgt.atio_dma)); + + if (IS_SHADOW_REG_CAPABLE(ha)) + icb->firmware_options_2 |= + __constant_cpu_to_le32(BIT_30|BIT_29); + + if (ha->mqenable || IS_QLA83XX(ha) || IS_QLA27XX(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]; - DEBUG2_17(printk(KERN_INFO - "Registering vector 0x%x for base que\n", msix->entry)); + 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 */ @@ -1760,12 +2086,13 @@ qla24xx_config_rings(struct scsi_qla_host *vha) __constant_cpu_to_le32(BIT_18); /* Use Disable MSIX Handshake mode for capable adapters */ - if (IS_MSIX_NACK_CAPABLE(ha)) { + 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; - qla_printk(KERN_INFO, ha, - "MSIX Handshake Disable Mode turned on\n"); + 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); @@ -1782,6 +2109,8 @@ qla24xx_config_rings(struct scsi_qla_host *vha) WRT_REG_DWORD(®->isp24.rsp_q_in, 0); WRT_REG_DWORD(®->isp24.rsp_q_out, 0); } + qlt_24xx_config_rings(vha); + /* PCI posting */ RD_REG_DWORD(&ioreg->hccr); } @@ -1795,7 +2124,7 @@ qla24xx_config_rings(struct scsi_qla_host *vha) * * Returns 0 on success. */ -static int +int qla2x00_init_rings(scsi_qla_host_t *vha) { int rval; @@ -1804,7 +2133,6 @@ qla2x00_init_rings(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; struct req_que *req; struct rsp_que *rsp; - struct scsi_qla_host *vp; struct mid_init_cb_24xx *mid_init_cb = (struct mid_init_cb_24xx *) ha->init_cb; @@ -1815,7 +2143,9 @@ qla2x00_init_rings(scsi_qla_host_t *vha) req = ha->req_q_map[que]; if (!req) continue; - for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) + req->out_ptr = (void *)(req->ring + req->length); + *req->out_ptr = 0; + for (cnt = 1; cnt < req->num_outstanding_cmds; cnt++) req->outstanding_cmds[cnt] = NULL; req->current_outstanding_cmd = 1; @@ -1830,30 +2160,36 @@ qla2x00_init_rings(scsi_qla_host_t *vha) rsp = ha->rsp_q_map[que]; if (!rsp) continue; + rsp->in_ptr = (void *)(rsp->ring + rsp->length); + *rsp->in_ptr = 0; /* Initialize response queue entries */ - qla2x00_init_response_q_entries(rsp); - } - - spin_lock(&ha->vport_slock); - /* Clear RSCN queue. */ - list_for_each_entry(vp, &ha->vp_list, list) { - vp->rscn_in_ptr = 0; - vp->rscn_out_ptr = 0; + if (IS_QLAFX00(ha)) + qlafx00_init_response_q_entries(rsp); + else + qla2x00_init_response_q_entries(rsp); } - spin_unlock(&ha->vport_slock); + ha->tgt.atio_ring_ptr = ha->tgt.atio_ring; + ha->tgt.atio_ring_index = 0; + /* Initialize ATIO queue entries */ + qlt_init_atio_q_entries(vha); ha->isp_ops->config_rings(vha); spin_unlock_irqrestore(&ha->hardware_lock, flags); + ql_dbg(ql_dbg_init, vha, 0x00d1, "Issue init firmware.\n"); + + if (IS_QLAFX00(ha)) { + rval = qlafx00_init_firmware(vha, ha->init_cb_size); + goto next_check; + } + /* Update any ISP specific firmware options before initialization. */ ha->isp_ops->update_fw_options(vha); - DEBUG(printk("scsi(%ld): Issue init firmware.\n", vha->host_no)); - if (ha->flags.npiv_supported) { - if (ha->operating_mode == LOOP) + if (ha->operating_mode == LOOP && !IS_CNA_CAPABLE(ha)) ha->max_npiv_vports = MIN_MULTI_ID_FABRIC - 1; mid_init_cb->count = cpu_to_le16(ha->max_npiv_vports); } @@ -1865,12 +2201,13 @@ qla2x00_init_rings(scsi_qla_host_t *vha) } rval = qla2x00_init_firmware(vha, ha->init_cb_size); +next_check: if (rval) { - DEBUG2_3(printk("scsi(%ld): Init firmware **** FAILED ****.\n", - vha->host_no)); + ql_log(ql_log_fatal, vha, 0x00d2, + "Init Firmware **** FAILED ****.\n"); } else { - DEBUG3(printk("scsi(%ld): Init firmware -- success.\n", - vha->host_no)); + ql_dbg(ql_dbg_init, vha, 0x00d3, + "Init Firmware -- success.\n"); } return (rval); @@ -1892,6 +2229,9 @@ qla2x00_fw_ready(scsi_qla_host_t *vha) uint16_t state[5]; struct qla_hw_data *ha = vha->hw; + if (IS_QLAFX00(vha->hw)) + return qlafx00_fw_ready(vha); + rval = QLA_SUCCESS; /* 20 seconds for loop down. */ @@ -1913,42 +2253,46 @@ qla2x00_fw_ready(scsi_qla_host_t *vha) /* Wait for ISP to finish LIP */ if (!vha->flags.init_done) - qla_printk(KERN_INFO, ha, "Waiting for LIP to complete...\n"); - - DEBUG3(printk("scsi(%ld): Waiting for LIP to complete...\n", - vha->host_no)); + ql_log(ql_log_info, vha, 0x801e, + "Waiting for LIP to complete.\n"); do { + memset(state, -1, sizeof(state)); 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) { - DEBUG16(printk("scsi(%ld): fw_state=%x " - "84xx=%x.\n", vha->host_no, state[0], - state[2])); + 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)) { - DEBUG16(printk("scsi(%ld): Sending " - "verify iocb.\n", vha->host_no)); + ql_dbg(ql_dbg_taskm, vha, 0x8028, + "Sending verify iocb.\n"); cs84xx_time = jiffies; rval = qla84xx_init_chip(vha); - if (rval != QLA_SUCCESS) + 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; - DEBUG16(printk("scsi(%ld): Increasing " - "wait time by %ld. New time %ld\n", - vha->host_no, cs84xx_time, wtime)); + 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) { - DEBUG(printk("scsi(%ld): F/W Ready - OK \n", - vha->host_no)); + 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); @@ -1965,7 +2309,7 @@ qla2x00_fw_ready(scsi_qla_host_t *vha) * other than Wait for Login. */ if (time_after_eq(jiffies, mtime)) { - qla_printk(KERN_INFO, ha, + ql_log(ql_log_info, vha, 0x8038, "Cable is unplugged...\n"); vha->device_flags |= DFLG_NO_CABLE; @@ -1984,18 +2328,15 @@ qla2x00_fw_ready(scsi_qla_host_t *vha) /* Delay for a while */ msleep(500); - - DEBUG3(printk("scsi(%ld): fw_state=%x curr time=%lx.\n", - vha->host_no, state[0], jiffies)); } while (1); - DEBUG(printk("scsi(%ld): fw_state=%x (%x, %x, %x, %x) curr time=%lx.\n", - vha->host_no, state[0], state[1], state[2], state[3], state[4], - jiffies)); + 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) { - DEBUG2_3(printk("scsi(%ld): Firmware ready **** FAILED ****.\n", - vha->host_no)); + if (rval && !(vha->device_flags & DFLG_NO_CABLE)) { + ql_log(ql_log_warn, vha, 0x803b, + "Firmware ready **** FAILED ****.\n"); } return (rval); @@ -2026,27 +2367,36 @@ qla2x00_configure_hba(scsi_qla_host_t *vha) uint8_t domain; char connect_type[22]; struct qla_hw_data *ha = vha->hw; + unsigned long flags; + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); /* 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_QLA8XXX_TYPE(ha) || + IS_CNA_CAPABLE(ha) || (rval == QLA_COMMAND_ERROR && loop_id == 0x7)) { - DEBUG2(printk("%s(%ld) Loop is in a transition state\n", - __func__, vha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2008, + "Loop is in a transition state.\n"); } else { - qla_printk(KERN_WARNING, ha, - "ERROR -- Unable to get host loop ID.\n"); + ql_log(ql_log_warn, vha, 0x2009, + "Unable to get host loop ID.\n"); + if (IS_FWI2_CAPABLE(ha) && (vha == base_vha) && + (rval == QLA_COMMAND_ERROR && loop_id == 0x1b)) { + ql_log(ql_log_warn, vha, 0x1151, + "Doing link init.\n"); + if (qla24xx_link_initialize(vha) == QLA_SUCCESS) + return rval; + } set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); } return (rval); } if (topo == 4) { - qla_printk(KERN_INFO, ha, - "Cannot get topology - retrying.\n"); + ql_log(ql_log_info, vha, 0x200a, + "Cannot get topology - retrying.\n"); return (QLA_FUNCTION_FAILED); } @@ -2059,31 +2409,27 @@ qla2x00_configure_hba(scsi_qla_host_t *vha) switch (topo) { case 0: - DEBUG3(printk("scsi(%ld): HBA in NL topology.\n", - vha->host_no)); + 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: - DEBUG3(printk("scsi(%ld): HBA in FL topology.\n", - vha->host_no)); + 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: - DEBUG3(printk("scsi(%ld): HBA in N P2P topology.\n", - vha->host_no)); + 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: - DEBUG3(printk("scsi(%ld): HBA in F P2P topology.\n", - vha->host_no)); + 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; @@ -2091,9 +2437,8 @@ qla2x00_configure_hba(scsi_qla_host_t *vha) break; default: - DEBUG3(printk("scsi(%ld): HBA in unknown topology %x. " - "Using NL.\n", - vha->host_no, topo)); + 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; @@ -2105,17 +2450,15 @@ qla2x00_configure_hba(scsi_qla_host_t *vha) vha->d_id.b.area = area; vha->d_id.b.al_pa = al_pa; + spin_lock_irqsave(&ha->vport_slock, flags); + qlt_update_vp_map(vha, SET_AL_PA); + spin_unlock_irqrestore(&ha->vport_slock, flags); + if (!vha->flags.init_done) - qla_printk(KERN_INFO, ha, - "Topology - %s, Host Loop address 0x%x\n", + ql_log(ql_log_info, vha, 0x2010, + "Topology - %s, Host Loop address 0x%x.\n", connect_type, vha->loop_id); - if (rval) { - DEBUG2_3(printk("scsi(%ld): FAILED.\n", vha->host_no)); - } else { - DEBUG3(printk("scsi(%ld): exiting normally.\n", vha->host_no)); - } - return(rval); } @@ -2127,7 +2470,7 @@ qla2x00_set_model_info(scsi_qla_host_t *vha, uint8_t *model, size_t len, uint16_t index; struct qla_hw_data *ha = vha->hw; int use_tbl = !IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && - !IS_QLA8XXX_TYPE(ha); + !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha); if (memcmp(model, BINZERO, len) != 0) { strncpy(ha->model_number, model, len); @@ -2227,18 +2570,22 @@ qla2x00_nvram_config(scsi_qla_host_t *vha) for (cnt = 0, chksum = 0; cnt < ha->nvram_size; cnt++) chksum += *ptr++; - DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", vha->host_no)); - DEBUG5(qla2x00_dump_buffer((uint8_t *)nv, ha->nvram_size)); + 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. */ - qla_printk(KERN_WARNING, ha, "Inconsistent NVRAM detected: " - "checksum=0x%x id=%c version=0x%x.\n", chksum, nv->id[0], - nv->nvram_version); - qla_printk(KERN_WARNING, ha, "Falling back to functioning (yet " - "invalid -- WWPN) defaults.\n"); + ql_log(ql_log_warn, vha, 0x0064, + "Inconsistent 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. @@ -2318,7 +2665,7 @@ qla2x00_nvram_config(scsi_qla_host_t *vha) if (IS_QLA23XX(ha)) { nv->firmware_options[0] |= BIT_2; nv->firmware_options[0] &= ~BIT_3; - nv->firmware_options[0] &= ~BIT_6; + nv->special_options[0] &= ~BIT_6; nv->add_firmware_options[1] |= BIT_5 | BIT_4; if (IS_QLA2300(ha)) { @@ -2382,8 +2729,13 @@ qla2x00_nvram_config(scsi_qla_host_t *vha) /* * 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 = 1; + 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)) @@ -2488,10 +2840,7 @@ qla2x00_nvram_config(scsi_qla_host_t *vha) if (ha->zio_mode != QLA_ZIO_DISABLED) { ha->zio_mode = QLA_ZIO_MODE_6; - DEBUG2(printk("scsi(%ld): ZIO mode %d enabled; timer " - "delay (%d us).\n", vha->host_no, ha->zio_mode, - ha->zio_timer * 100)); - qla_printk(KERN_INFO, ha, + ql_log(ql_log_info, vha, 0x0068, "ZIO mode %d enabled; timer delay (%d us).\n", ha->zio_mode, ha->zio_timer * 100); @@ -2502,8 +2851,8 @@ qla2x00_nvram_config(scsi_qla_host_t *vha) } if (rval) { - DEBUG2_3(printk(KERN_WARNING - "scsi(%ld): NVRAM configuration failed!\n", vha->host_no)); + ql_log(ql_log_warn, vha, 0x0069, + "NVRAM configuration failed.\n"); } return (rval); } @@ -2513,14 +2862,21 @@ qla2x00_rport_del(void *data) { fc_port_t *fcport = data; struct fc_rport *rport; + scsi_qla_host_t *vha = fcport->vha; 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) + if (rport) { fc_remote_port_delete(rport); + /* + * Release the target mode FC NEXUS in qla_target.c code + * if target mod is enabled. + */ + qlt_fc_port_deleted(vha, fcport); + } } /** @@ -2541,7 +2897,6 @@ qla2x00_alloc_fcport(scsi_qla_host_t *vha, gfp_t flags) /* 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); @@ -2574,15 +2929,15 @@ qla2x00_configure_loop(scsi_qla_host_t *vha) if (test_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags)) { rval = qla2x00_configure_hba(vha); if (rval != QLA_SUCCESS) { - DEBUG(printk("scsi(%ld): Unable to configure HBA.\n", - vha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2013, + "Unable to configure HBA.\n"); return (rval); } } save_flags = flags = vha->dpc_flags; - DEBUG(printk("scsi(%ld): Configure loop -- dpc flags =0x%lx\n", - vha->host_no, 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 @@ -2597,13 +2952,11 @@ qla2x00_configure_loop(scsi_qla_host_t *vha) if (ha->current_topology == ISP_CFG_FL && (test_bit(LOCAL_LOOP_UPDATE, &flags))) { - vha->flags.rscn_queue_overflow = 1; set_bit(RSCN_UPDATE, &flags); } else if (ha->current_topology == ISP_CFG_F && (test_bit(LOCAL_LOOP_UPDATE, &flags))) { - vha->flags.rscn_queue_overflow = 1; set_bit(RSCN_UPDATE, &flags); clear_bit(LOCAL_LOOP_UPDATE, &flags); @@ -2613,21 +2966,25 @@ qla2x00_configure_loop(scsi_qla_host_t *vha) } else if (!vha->flags.online || (test_bit(ABORT_ISP_ACTIVE, &flags))) { - vha->flags.rscn_queue_overflow = 1; 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)) + 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 + } else rval = qla2x00_configure_local_loop(vha); } if (rval == QLA_SUCCESS && test_bit(RSCN_UPDATE, &flags)) { - if (LOOP_TRANSITION(vha)) + 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); } @@ -2638,16 +2995,17 @@ qla2x00_configure_loop(scsi_qla_host_t *vha) rval = QLA_FUNCTION_FAILED; } else { atomic_set(&vha->loop_state, LOOP_READY); - - DEBUG(printk("scsi(%ld): LOOP READY\n", vha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2069, + "LOOP READY.\n"); } } if (rval) { - DEBUG2_3(printk("%s(%ld): *** FAILED ***\n", - __func__, vha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x206a, + "%s *** FAILED ***.\n", __func__); } else { - DEBUG3(printk("%s: exiting normally\n", __func__)); + ql_dbg(ql_dbg_disc, vha, 0x206b, + "%s: exiting normally.\n", __func__); } /* Restore state if a resync event occurred during processing */ @@ -2656,8 +3014,6 @@ qla2x00_configure_loop(scsi_qla_host_t *vha) set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags); if (test_bit(RSCN_UPDATE, &save_flags)) { set_bit(RSCN_UPDATE, &vha->dpc_flags); - if (!IS_ALOGIO_CAPABLE(ha)) - vha->flags.rscn_queue_overflow = 1; } } @@ -2693,26 +3049,26 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha) found_devs = 0; new_fcport = NULL; - entries = MAX_FIBRE_DEVICES; - - DEBUG3(printk("scsi(%ld): Getting FCAL position map\n", vha->host_no)); - DEBUG3(qla2x00_get_fcal_position_map(vha, NULL)); + entries = MAX_FIBRE_DEVICES_LOOP; /* Get list of logged in devices. */ - memset(ha->gid_list, 0, GID_LIST_SIZE); + 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; - DEBUG3(printk("scsi(%ld): Entries in ID list (%d)\n", - vha->host_no, entries)); - DEBUG3(qla2x00_dump_buffer((uint8_t *)ha->gid_list, - entries * sizeof(struct gid_list_info))); + 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; } @@ -2726,9 +3082,9 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha) fcport->port_type != FCT_BROADCAST && (fcport->flags & FCF_FABRIC_DEVICE) == 0) { - DEBUG(printk("scsi(%ld): Marking port lost, " - "loop_id=0x%04x\n", - vha->host_no, fcport->loop_id)); + 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); } @@ -2761,20 +3117,21 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha) if (loop_id > LAST_LOCAL_LOOP_ID) continue; + memset(new_fcport, 0, sizeof(fc_port_t)); + /* 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) { - DEBUG2(printk("scsi(%ld): Failed to retrieve fcport " - "information -- get_port_database=%x, " - "loop_id=0x%04x\n", - vha->host_no, rval2, new_fcport->loop_id)); - DEBUG2(printk("scsi(%ld): Scheduling resync...\n", - vha->host_no)); + 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; } @@ -2800,16 +3157,14 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha) 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; } @@ -2828,8 +3183,8 @@ cleanup_allocation: kfree(new_fcport); if (rval != QLA_SUCCESS) { - DEBUG2(printk("scsi(%ld): Configure local loop error exit: " - "rval=%x\n", vha->host_no, rval)); + ql_dbg(ql_dbg_disc, vha, 0x201d, + "Configure local loop error exit: rval=%x.\n", rval); } return (rval); @@ -2838,9 +3193,6 @@ cleanup_allocation: 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; @@ -2858,27 +3210,14 @@ qla2x00_iidma_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) rval = qla2x00_set_idma_speed(vha, fcport->loop_id, fcport->fp_speed, mb); if (rval != QLA_SUCCESS) { - DEBUG2(printk("scsi(%ld): Unable to adjust iIDMA " - "%02x%02x%02x%02x%02x%02x%02x%02x -- %04x %x %04x %04x.\n", - vha->host_no, 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])); + ql_dbg(ql_dbg_disc, vha, 0x2004, + "Unable to adjust iIDMA %8phN -- %04x %x %04x %04x.\n", + fcport->port_name, 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]; - DEBUG2(qla_printk(KERN_INFO, ha, - "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])); + ql_dbg(ql_dbg_disc, vha, 0x2005, + "iIDMA adjusted to %s GB/s on %8phN.\n", + qla2x00_get_link_speed_str(ha, fcport->fp_speed), + fcport->port_name); } } @@ -2887,7 +3226,6 @@ qla2x00_reg_remote_port(scsi_qla_host_t *vha, fc_port_t *fcport) { struct fc_rport_identifiers rport_ids; struct fc_rport *rport; - struct qla_hw_data *ha = vha->hw; unsigned long flags; qla2x00_rport_del(fcport); @@ -2899,10 +3237,16 @@ qla2x00_reg_remote_port(scsi_qla_host_t *vha, fc_port_t *fcport) rport_ids.roles = FC_RPORT_ROLE_UNKNOWN; fcport->rport = rport = fc_remote_port_add(vha->host, 0, &rport_ids); if (!rport) { - qla_printk(KERN_WARNING, ha, - "Unable to allocate fc remote port!\n"); + ql_log(ql_log_warn, vha, 0x2006, + "Unable to allocate fc remote port.\n"); return; } + /* + * Create target mode FC NEXUS in qla_target.c if target mode is + * enabled.. + */ + qlt_fc_port_added(vha, fcport); + 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); @@ -2936,13 +3280,19 @@ void qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) { fcport->vha = vha; + + if (IS_QLAFX00(vha->hw)) { + qla2x00_set_fcport_state(fcport, FCS_ONLINE); + qla2x00_reg_remote_port(vha, fcport); + return; + } fcport->login_retry = 0; fcport->flags &= ~(FCF_LOGIN_NEEDED | FCF_ASYNC_SENT); + qla2x00_set_fcport_state(fcport, FCS_ONLINE); 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); } /* @@ -2959,7 +3309,7 @@ qla2x00_update_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) static int qla2x00_configure_fabric(scsi_qla_host_t *vha) { - int rval, rval2; + int rval; fc_port_t *fcport, *fcptemp; uint16_t next_loopid; uint16_t mb[MAILBOX_REGISTER_COUNT]; @@ -2975,20 +3325,14 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) loop_id = SNS_FL_PORT; rval = qla2x00_get_port_name(vha, loop_id, vha->fabric_node_name, 1); if (rval != QLA_SUCCESS) { - DEBUG2(printk("scsi(%ld): MBC_GET_PORT_NAME Failed, No FL " - "Port\n", vha->host_no)); + 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; - /* Mark devices that need re-synchronization. */ - rval2 = qla2x00_device_resync(vha); - if (rval2 == QLA_RSCNS_HANDLED) { - /* No point doing the scan, just continue. */ - return (QLA_SUCCESS); - } do { /* FDMI support. */ if (ql2xfdmienable && @@ -3000,38 +3344,49 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) loop_id = NPH_SNS; else loop_id = SIMPLE_NAME_SERVER; - ha->isp_ops->fabric_login(vha, loop_id, 0xff, 0xff, - 0xfc, mb, BIT_1 | BIT_0); + 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) { - DEBUG2(qla_printk(KERN_INFO, ha, - "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])); + 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 */ - DEBUG2(printk("scsi(%ld): Register FC-4 " - "TYPE failed.\n", vha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2045, + "Register FC-4 TYPE failed.\n"); } if (qla2x00_rff_id(vha)) { /* EMPTY */ - DEBUG2(printk("scsi(%ld): Register FC-4 " - "Features failed.\n", vha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x2049, + "Register FC-4 Features failed.\n"); } if (qla2x00_rnn_id(vha)) { /* EMPTY */ - DEBUG2(printk("scsi(%ld): Register Node Name " - "failed.\n", vha->host_no)); + ql_dbg(ql_dbg_disc, vha, 0x204f, + "Register Node Name failed.\n"); } else if (qla2x00_rsnn_nn(vha)) { /* EMPTY */ - DEBUG2(printk("scsi(%ld): Register Symbolic " - "Node Name failed.\n", vha->host_no)); + 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; @@ -3047,7 +3402,8 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) if ((fcport->flags & FCF_FABRIC_DEVICE) == 0) continue; - if (atomic_read(&fcport->state) == FCS_DEVICE_LOST) { + 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 && @@ -3059,7 +3415,7 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa); - fcport->loop_id = FC_NO_LOOP_ID; + qla2x00_clear_loop_id(fcport); } } } @@ -3117,10 +3473,6 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) /* 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); @@ -3132,8 +3484,8 @@ qla2x00_configure_fabric(scsi_qla_host_t *vha) } if (rval) { - DEBUG2(printk("scsi(%ld): Configure fabric error exit: " - "rval=%d\n", vha->host_no, rval)); + ql_dbg(ql_dbg_disc, vha, 0x2068, + "Configure fabric error exit rval=%d.\n", rval); } return (rval); @@ -3166,26 +3518,26 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, 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; + struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); rval = QLA_SUCCESS; /* Try GID_PT to get device list, else GAN. */ - swl = kcalloc(MAX_FIBRE_DEVICES, sizeof(sw_info_t), GFP_KERNEL); + if (!ha->swl) + ha->swl = kcalloc(ha->max_fibre_devices, sizeof(sw_info_t), + GFP_KERNEL); + swl = ha->swl; if (!swl) { /*EMPTY*/ - DEBUG2(printk("scsi(%ld): GID_PT allocations failed, fallback " - "on GA_NXT\n", vha->host_no)); + 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) { - kfree(swl); swl = NULL; } else if (qla2x00_gpn_id(vha, swl) != QLA_SUCCESS) { - kfree(swl); swl = NULL; } else if (qla2x00_gnn_id(vha, swl) != QLA_SUCCESS) { - kfree(swl); swl = NULL; } else if (ql2xiidmaenable && qla2x00_gfpn_id(vha, swl) == QLA_SUCCESS) { @@ -3201,7 +3553,8 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, /* Allocate temporary fcport for any new fcports discovered. */ new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); if (new_fcport == NULL) { - kfree(swl); + 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); @@ -3247,9 +3600,9 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, /* Send GA_NXT to the switch */ rval = qla2x00_ga_nxt(vha, new_fcport); if (rval != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, - "SNS scan failed -- assuming zero-entry " - "result...\n"); + 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); @@ -3265,9 +3618,11 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, wrap.b24 = new_fcport->d_id.b24; first_dev = 0; } else if (new_fcport->d_id.b24 == wrap.b24) { - DEBUG2(printk("scsi(%ld): device wrap (%02x%02x%02x)\n", - vha->host_no, new_fcport->d_id.b.domain, - new_fcport->d_id.b.area, new_fcport->d_id.b.al_pa)); + 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; } @@ -3276,22 +3631,8 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, 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; - } + if (qla2x00_is_a_vp_did(vha, new_fcport->d_id.b24)) + continue; /* Bypass if same domain and area of adapter. */ if (((new_fcport->d_id.b24 & 0xffff00) == @@ -3316,6 +3657,8 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, WWN_SIZE)) continue; + fcport->scan_state = QLA_FCPORT_FOUND; + found++; /* Update port state. */ @@ -3337,7 +3680,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, */ if ((fcport->flags & FCF_FABRIC_DEVICE) == 0) { fcport->d_id.b24 = new_fcport->d_id.b24; - fcport->loop_id = FC_NO_LOOP_ID; + qla2x00_clear_loop_id(fcport); fcport->flags |= (FCF_FABRIC_DEVICE | FCF_LOGIN_NEEDED); break; @@ -3352,12 +3695,13 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, 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; + qla2x00_clear_loop_id(fcport); } break; @@ -3372,14 +3716,14 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, nxt_d_id.b24 = new_fcport->d_id.b24; new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); if (new_fcport == NULL) { - kfree(swl); + 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(swl); kfree(new_fcport); return (rval); @@ -3403,173 +3747,33 @@ 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) { - 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_device_resync - * Marks devices in the database that needs resynchronization. - * - * Input: - * ha = adapter block pointer. - * - * Context: - * Kernel context. - */ -static int -qla2x00_device_resync(scsi_qla_host_t *vha) -{ - int rval; - uint32_t mask; - fc_port_t *fcport; - uint32_t rscn_entry; - uint8_t rscn_out_iter; - uint8_t format; - port_id_t d_id = {}; - - rval = QLA_RSCNS_HANDLED; - - while (vha->rscn_out_ptr != vha->rscn_in_ptr || - vha->flags.rscn_queue_overflow) { - - rscn_entry = vha->rscn_queue[vha->rscn_out_ptr]; - format = MSB(MSW(rscn_entry)); - d_id.b.domain = LSB(MSW(rscn_entry)); - d_id.b.area = MSB(LSW(rscn_entry)); - d_id.b.al_pa = LSB(LSW(rscn_entry)); - - DEBUG(printk("scsi(%ld): RSCN queue entry[%d] = " - "[%02x/%02x%02x%02x].\n", - vha->host_no, vha->rscn_out_ptr, format, d_id.b.domain, - d_id.b.area, d_id.b.al_pa)); - - vha->rscn_out_ptr++; - if (vha->rscn_out_ptr == MAX_RSCN_COUNT) - vha->rscn_out_ptr = 0; - - /* Skip duplicate entries. */ - for (rscn_out_iter = vha->rscn_out_ptr; - !vha->flags.rscn_queue_overflow && - rscn_out_iter != vha->rscn_in_ptr; - rscn_out_iter = (rscn_out_iter == - (MAX_RSCN_COUNT - 1)) ? 0: rscn_out_iter + 1) { - - if (rscn_entry != vha->rscn_queue[rscn_out_iter]) - break; - - DEBUG(printk("scsi(%ld): Skipping duplicate RSCN queue " - "entry found at [%d].\n", vha->host_no, - rscn_out_iter)); - - vha->rscn_out_ptr = rscn_out_iter; - } - - /* Queue overflow, set switch default case. */ - if (vha->flags.rscn_queue_overflow) { - DEBUG(printk("scsi(%ld): device_resync: rscn " - "overflow.\n", vha->host_no)); - - format = 3; - vha->flags.rscn_queue_overflow = 0; - } + spin_lock_irqsave(&ha->vport_slock, flags); - switch (format) { - case 0: - mask = 0xffffff; - break; - case 1: - mask = 0xffff00; - break; - case 2: - mask = 0xff0000; - break; - default: - mask = 0x0; - d_id.b24 = 0; - vha->rscn_out_ptr = vha->rscn_in_ptr; - break; - } + dev->loop_id = find_first_zero_bit(ha->loop_id_map, + LOOPID_MAP_SIZE); + if (dev->loop_id >= LOOPID_MAP_SIZE || + qla2x00_is_reserved_id(vha, dev->loop_id)) { + dev->loop_id = FC_NO_LOOP_ID; + rval = QLA_FUNCTION_FAILED; + } else + set_bit(dev->loop_id, ha->loop_id_map); - rval = QLA_SUCCESS; + spin_unlock_irqrestore(&ha->vport_slock, flags); - list_for_each_entry(fcport, &vha->vp_fcports, list) { - if ((fcport->flags & FCF_FABRIC_DEVICE) == 0 || - (fcport->d_id.b24 & mask) != d_id.b24 || - fcport->port_type == FCT_BROADCAST) - continue; + if (rval == QLA_SUCCESS) + ql_dbg(ql_dbg_disc, dev->vha, 0x2086, + "Assigning new loopid=%x, portid=%x.\n", + dev->loop_id, dev->d_id.b24); + else + ql_log(ql_log_warn, dev->vha, 0x2087, + "No loop_id's available, portid=%x.\n", + dev->d_id.b24); - if (atomic_read(&fcport->state) == FCS_ONLINE) { - if (format != 3 || - fcport->port_type != FCT_INITIATOR) { - qla2x00_mark_device_lost(vha, fcport, - 0, 0); - } - } - } - } return (rval); } @@ -3626,6 +3830,9 @@ qla2x00_fabric_dev_login(scsi_qla_host_t *vha, fc_port_t *fcport, } else { qla2x00_update_fcport(vha, fcport); } + } else { + /* Retry Login. */ + qla2x00_mark_device_lost(vha, fcport, 1, 0); } return (rval); @@ -3659,15 +3866,19 @@ qla2x00_fabric_login(scsi_qla_host_t *vha, fc_port_t *fcport, tmp_loopid = 0; for (;;) { - DEBUG(printk("scsi(%ld): Trying Fabric Login w/loop id 0x%04x " - "for port %02x%02x%02x.\n", - vha->host_no, fcport->loop_id, fcport->d_id.b.domain, - fcport->d_id.b.area, fcport->d_id.b.al_pa)); + 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. */ - ha->isp_ops->fabric_login(vha, fcport->loop_id, + 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 @@ -3680,10 +3891,11 @@ qla2x00_fabric_login(scsi_qla_host_t *vha, fc_port_t *fcport, tmp_loopid = fcport->loop_id; fcport->loop_id = mb[1]; - DEBUG(printk("Fabric Login: port in use - next " - "loop id=0x%04x, port Id=%02x%02x%02x.\n", + 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)); + fcport->d_id.b.area, fcport->d_id.b.al_pa); } else if (mb[0] == MBS_COMMAND_COMPLETE) { /* @@ -3714,6 +3926,12 @@ qla2x00_fabric_login(scsi_qla_host_t *vha, fc_port_t *fcport, if (mb[10] & BIT_1) fcport->supported_classes |= FC_COS_CLASS3; + if (IS_FWI2_CAPABLE(ha)) { + if (mb[10] & BIT_7) + fcport->flags |= + FCF_CONF_COMP_SUPPORTED; + } + rval = QLA_SUCCESS; break; } else if (mb[0] == MBS_LOOP_ID_USED) { @@ -3744,17 +3962,17 @@ qla2x00_fabric_login(scsi_qla_host_t *vha, fc_port_t *fcport, /* * unrecoverable / not handled error */ - DEBUG2(printk("%s(%ld): failed=%x port_id=%02x%02x%02x " - "loop_id=%x jiffies=%lx.\n", - __func__, vha->host_no, mb[0], - fcport->d_id.b.domain, fcport->d_id.b.area, - fcport->d_id.b.al_pa, fcport->loop_id, jiffies)); + 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; + qla2x00_clear_loop_id(fcport); fcport->login_retry = 0; rval = 3; @@ -3822,24 +4040,30 @@ qla2x00_loop_resync(scsi_qla_host_t *vha) req = vha->req; rsp = req->rsp; - atomic_set(&vha->loop_state, LOOP_UPDATE); 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 { - atomic_set(&vha->loop_state, LOOP_UPDATE); - - /* Issue a marker after FW becomes ready. */ - qla2x00_marker(vha, req, rsp, 0, 0, - MK_SYNC_ALL); - vha->marker_needed = 0; + if (!IS_QLAFX00(vha->hw)) { + /* + * 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); + if (IS_QLAFX00(vha->hw)) + qlafx00_configure_devices(vha); + else + qla2x00_configure_loop(vha); + wait_time--; } while (!atomic_read(&vha->loop_down_timer) && !(test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) @@ -3852,7 +4076,8 @@ qla2x00_loop_resync(scsi_qla_host_t *vha) return (QLA_FUNCTION_FAILED); if (rval) - DEBUG2_3(printk("%s(): **** FAILED ****\n", __func__)); + ql_dbg(ql_dbg_disc, vha, 0x206c, + "%s *** FAILED ***.\n", __func__); return (rval); } @@ -3904,9 +4129,7 @@ qla2x00_update_fcports(scsi_qla_host_t *base_vha) 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); } } @@ -3915,8 +4138,323 @@ qla2x00_update_fcports(scsi_qla_host_t *base_vha) spin_unlock_irqrestore(&ha->vport_slock, flags); } +/* Assumes idc_lock always held on entry */ +void +qla83xx_reset_ownership(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t drv_presence, drv_presence_mask; + uint32_t dev_part_info1, dev_part_info2, class_type; + uint32_t class_type_mask = 0x3; + uint16_t fcoe_other_function = 0xffff, i; + + if (IS_QLA8044(ha)) { + drv_presence = qla8044_rd_direct(vha, + QLA8044_CRB_DRV_ACTIVE_INDEX); + dev_part_info1 = qla8044_rd_direct(vha, + QLA8044_CRB_DEV_PART_INFO_INDEX); + dev_part_info2 = qla8044_rd_direct(vha, + QLA8044_CRB_DEV_PART_INFO2); + } else { + qla83xx_rd_reg(vha, QLA83XX_IDC_DRV_PRESENCE, &drv_presence); + qla83xx_rd_reg(vha, QLA83XX_DEV_PARTINFO1, &dev_part_info1); + qla83xx_rd_reg(vha, QLA83XX_DEV_PARTINFO2, &dev_part_info2); + } + for (i = 0; i < 8; i++) { + class_type = ((dev_part_info1 >> (i * 4)) & class_type_mask); + if ((class_type == QLA83XX_CLASS_TYPE_FCOE) && + (i != ha->portnum)) { + fcoe_other_function = i; + break; + } + } + if (fcoe_other_function == 0xffff) { + for (i = 0; i < 8; i++) { + class_type = ((dev_part_info2 >> (i * 4)) & + class_type_mask); + if ((class_type == QLA83XX_CLASS_TYPE_FCOE) && + ((i + 8) != ha->portnum)) { + fcoe_other_function = i + 8; + break; + } + } + } + /* + * Prepare drv-presence mask based on fcoe functions present. + * However consider only valid physical fcoe function numbers (0-15). + */ + drv_presence_mask = ~((1 << (ha->portnum)) | + ((fcoe_other_function == 0xffff) ? + 0 : (1 << (fcoe_other_function)))); + + /* We are the reset owner iff: + * - No other protocol drivers present. + * - This is the lowest among fcoe functions. */ + if (!(drv_presence & drv_presence_mask) && + (ha->portnum < fcoe_other_function)) { + ql_dbg(ql_dbg_p3p, vha, 0xb07f, + "This host is Reset owner.\n"); + ha->flags.nic_core_reset_owner = 1; + } +} + +static int +__qla83xx_set_drv_ack(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + uint32_t drv_ack; + + rval = qla83xx_rd_reg(vha, QLA83XX_IDC_DRIVER_ACK, &drv_ack); + if (rval == QLA_SUCCESS) { + drv_ack |= (1 << ha->portnum); + rval = qla83xx_wr_reg(vha, QLA83XX_IDC_DRIVER_ACK, drv_ack); + } + + return rval; +} + +static int +__qla83xx_clear_drv_ack(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + uint32_t drv_ack; + + rval = qla83xx_rd_reg(vha, QLA83XX_IDC_DRIVER_ACK, &drv_ack); + if (rval == QLA_SUCCESS) { + drv_ack &= ~(1 << ha->portnum); + rval = qla83xx_wr_reg(vha, QLA83XX_IDC_DRIVER_ACK, drv_ack); + } + + return rval; +} + +static const char * +qla83xx_dev_state_to_string(uint32_t dev_state) +{ + switch (dev_state) { + case QLA8XXX_DEV_COLD: + return "COLD/RE-INIT"; + case QLA8XXX_DEV_INITIALIZING: + return "INITIALIZING"; + case QLA8XXX_DEV_READY: + return "READY"; + case QLA8XXX_DEV_NEED_RESET: + return "NEED RESET"; + case QLA8XXX_DEV_NEED_QUIESCENT: + return "NEED QUIESCENT"; + case QLA8XXX_DEV_FAILED: + return "FAILED"; + case QLA8XXX_DEV_QUIESCENT: + return "QUIESCENT"; + default: + return "Unknown"; + } +} + +/* Assumes idc-lock always held on entry */ +void +qla83xx_idc_audit(scsi_qla_host_t *vha, int audit_type) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t idc_audit_reg = 0, duration_secs = 0; + + switch (audit_type) { + case IDC_AUDIT_TIMESTAMP: + ha->idc_audit_ts = (jiffies_to_msecs(jiffies) / 1000); + idc_audit_reg = (ha->portnum) | + (IDC_AUDIT_TIMESTAMP << 7) | (ha->idc_audit_ts << 8); + qla83xx_wr_reg(vha, QLA83XX_IDC_AUDIT, idc_audit_reg); + break; + + case IDC_AUDIT_COMPLETION: + duration_secs = ((jiffies_to_msecs(jiffies) - + jiffies_to_msecs(ha->idc_audit_ts)) / 1000); + idc_audit_reg = (ha->portnum) | + (IDC_AUDIT_COMPLETION << 7) | (duration_secs << 8); + qla83xx_wr_reg(vha, QLA83XX_IDC_AUDIT, idc_audit_reg); + break; + + default: + ql_log(ql_log_warn, vha, 0xb078, + "Invalid audit type specified.\n"); + break; + } +} + +/* Assumes idc_lock always held on entry */ +static int +qla83xx_initiating_reset(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t idc_control, dev_state; + + __qla83xx_get_idc_control(vha, &idc_control); + if ((idc_control & QLA83XX_IDC_RESET_DISABLED)) { + ql_log(ql_log_info, vha, 0xb080, + "NIC Core reset has been disabled. idc-control=0x%x\n", + idc_control); + return QLA_FUNCTION_FAILED; + } + + /* Set NEED-RESET iff in READY state and we are the reset-owner */ + qla83xx_rd_reg(vha, QLA83XX_IDC_DEV_STATE, &dev_state); + if (ha->flags.nic_core_reset_owner && dev_state == QLA8XXX_DEV_READY) { + qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, + QLA8XXX_DEV_NEED_RESET); + ql_log(ql_log_info, vha, 0xb056, "HW State: NEED RESET.\n"); + qla83xx_idc_audit(vha, IDC_AUDIT_TIMESTAMP); + } else { + const char *state = qla83xx_dev_state_to_string(dev_state); + ql_log(ql_log_info, vha, 0xb057, "HW State: %s.\n", state); + + /* SV: XXX: Is timeout required here? */ + /* Wait for IDC state change READY -> NEED_RESET */ + while (dev_state == QLA8XXX_DEV_READY) { + qla83xx_idc_unlock(vha, 0); + msleep(200); + qla83xx_idc_lock(vha, 0); + qla83xx_rd_reg(vha, QLA83XX_IDC_DEV_STATE, &dev_state); + } + } + + /* Send IDC ack by writing to drv-ack register */ + __qla83xx_set_drv_ack(vha); + + return QLA_SUCCESS; +} + +int +__qla83xx_set_idc_control(scsi_qla_host_t *vha, uint32_t idc_control) +{ + return qla83xx_wr_reg(vha, QLA83XX_IDC_CONTROL, idc_control); +} + +int +__qla83xx_get_idc_control(scsi_qla_host_t *vha, uint32_t *idc_control) +{ + return qla83xx_rd_reg(vha, QLA83XX_IDC_CONTROL, idc_control); +} + +static int +qla83xx_check_driver_presence(scsi_qla_host_t *vha) +{ + uint32_t drv_presence = 0; + struct qla_hw_data *ha = vha->hw; + + qla83xx_rd_reg(vha, QLA83XX_IDC_DRV_PRESENCE, &drv_presence); + if (drv_presence & (1 << ha->portnum)) + return QLA_SUCCESS; + else + return QLA_TEST_FAILED; +} + +int +qla83xx_nic_core_reset(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + + ql_dbg(ql_dbg_p3p, vha, 0xb058, + "Entered %s().\n", __func__); + + if (vha->device_flags & DFLG_DEV_FAILED) { + ql_log(ql_log_warn, vha, 0xb059, + "Device in unrecoverable FAILED state.\n"); + return QLA_FUNCTION_FAILED; + } + + qla83xx_idc_lock(vha, 0); + + if (qla83xx_check_driver_presence(vha) != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0xb05a, + "Function=0x%x has been removed from IDC participation.\n", + ha->portnum); + rval = QLA_FUNCTION_FAILED; + goto exit; + } + + qla83xx_reset_ownership(vha); + + rval = qla83xx_initiating_reset(vha); + + /* + * Perform reset if we are the reset-owner, + * else wait till IDC state changes to READY/FAILED. + */ + if (rval == QLA_SUCCESS) { + rval = qla83xx_idc_state_handler(vha); + + if (rval == QLA_SUCCESS) + ha->flags.nic_core_hung = 0; + __qla83xx_clear_drv_ack(vha); + } + +exit: + qla83xx_idc_unlock(vha, 0); + + ql_dbg(ql_dbg_p3p, vha, 0xb05b, "Exiting %s.\n", __func__); + + return rval; +} + +int +qla2xxx_mctp_dump(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + int rval = QLA_FUNCTION_FAILED; + + if (!IS_MCTP_CAPABLE(ha)) { + /* This message can be removed from the final version */ + ql_log(ql_log_info, vha, 0x506d, + "This board is not MCTP capable\n"); + return rval; + } + + if (!ha->mctp_dump) { + ha->mctp_dump = dma_alloc_coherent(&ha->pdev->dev, + MCTP_DUMP_SIZE, &ha->mctp_dump_dma, GFP_KERNEL); + + if (!ha->mctp_dump) { + ql_log(ql_log_warn, vha, 0x506e, + "Failed to allocate memory for mctp dump\n"); + return rval; + } + } + +#define MCTP_DUMP_STR_ADDR 0x00000000 + rval = qla2x00_dump_mctp_data(vha, ha->mctp_dump_dma, + MCTP_DUMP_STR_ADDR, MCTP_DUMP_SIZE/4); + if (rval != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x506f, + "Failed to capture mctp dump\n"); + } else { + ql_log(ql_log_info, vha, 0x5070, + "Mctp dump capture for host (%ld/%p).\n", + vha->host_no, ha->mctp_dump); + ha->mctp_dumped = 1; + } + + if (!ha->flags.nic_core_reset_hdlr_active && !ha->portnum) { + ha->flags.nic_core_reset_hdlr_active = 1; + rval = qla83xx_restart_nic_firmware(vha); + if (rval) + /* NIC Core reset failed. */ + ql_log(ql_log_warn, vha, 0x5071, + "Failed to restart nic firmware\n"); + else + ql_dbg(ql_dbg_p3p, vha, 0xb084, + "Restarted NIC firmware successfully.\n"); + ha->flags.nic_core_reset_hdlr_active = 0; + } + + return rval; + +} + /* -* qla82xx_quiescent_state_cleanup +* qla2x00_quiesce_io * Description: This function will block the new I/Os * Its not aborting any I/Os as context * is not destroyed during quiescence @@ -3924,20 +4462,20 @@ qla2x00_update_fcports(scsi_qla_host_t *base_vha) * return : void */ void -qla82xx_quiescent_state_cleanup(scsi_qla_host_t *vha) +qla2x00_quiesce_io(scsi_qla_host_t *vha) { struct qla_hw_data *ha = vha->hw; struct scsi_qla_host *vp; - qla_printk(KERN_INFO, ha, - "Performing ISP error recovery - ha= %p.\n", ha); + ql_dbg(ql_dbg_dpc, vha, 0x401d, + "Quiescing I/O - 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); + qla2x00_mark_all_devices_lost(vp, 0); } else { if (!atomic_read(&vha->loop_down_timer)) atomic_set(&vha->loop_down_timer, @@ -3958,20 +4496,20 @@ qla2x00_abort_isp_cleanup(scsi_qla_host_t *vha) /* For ISP82XX, driver waits for completion of the commands. * online flag should be set. */ - if (!IS_QLA82XX(ha)) + if (!(IS_P3P_TYPE(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++; + vha->qla_stats.total_isp_aborts++; - qla_printk(KERN_INFO, ha, - "Performing ISP error recovery - ha= %p.\n", ha); + 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)) + if (!(IS_P3P_TYPE(ha))) ha->isp_ops->reset_chip(vha); atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); @@ -4014,8 +4552,10 @@ qla2x00_abort_isp_cleanup(scsi_qla_host_t *vha) if (!ha->flags.eeh_busy) { /* Make sure for ISP 82XX IO DMA is complete */ - if (IS_QLA82XX(ha)) { + if (IS_P3P_TYPE(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. @@ -4051,6 +4591,14 @@ qla2x00_abort_isp(scsi_qla_host_t *vha) if (vha->flags.online) { qla2x00_abort_isp_cleanup(vha); + if (IS_QLA8031(ha)) { + ql_dbg(ql_dbg_p3p, vha, 0xb05c, + "Clearing fcoe driver presence.\n"); + if (qla83xx_clear_drv_presence(vha) != QLA_SUCCESS) + ql_dbg(ql_dbg_p3p, vha, 0xb073, + "Error while clearing DRV-Presence.\n"); + } + if (unlikely(pci_channel_offline(ha->pdev) && ha->flags.pci_channel_io_perm_failure)) { clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags); @@ -4080,15 +4628,8 @@ qla2x00_abort_isp(scsi_qla_host_t *vha) ha->isp_abort_cnt = 0; clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags); - if (IS_QLA81XX(ha)) - qla2x00_get_fw_version(vha, - &ha->fw_major_version, - &ha->fw_minor_version, - &ha->fw_subminor_version, - &ha->fw_attributes, &ha->fw_memory_size, - ha->mpi_version, &ha->mpi_capabilities, - ha->phy_version); - + if (IS_QLA81XX(ha) || IS_QLA8031(ha)) + qla2x00_get_fw_version(vha); if (ha->fce) { ha->flags.fce_enabled = 1; memset(ha->fce, 0, @@ -4097,7 +4638,7 @@ qla2x00_abort_isp(scsi_qla_host_t *vha) ha->fce_dma, ha->fce_bufs, ha->fce_mb, &ha->fce_bufs); if (rval) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x8033, "Unable to reinitialize FCE " "(%d).\n", rval); ha->flags.fce_enabled = 0; @@ -4109,7 +4650,7 @@ qla2x00_abort_isp(scsi_qla_host_t *vha) rval = qla2x00_enable_eft_trace(vha, ha->eft_dma, EFT_NUM_BUFFERS); if (rval) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x8034, "Unable to reinitialize EFT " "(%d).\n", rval); } @@ -4118,9 +4659,9 @@ qla2x00_abort_isp(scsi_qla_host_t *vha) vha->flags.online = 1; if (test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) { if (ha->isp_abort_cnt == 0) { - qla_printk(KERN_WARNING, ha, - "ISP error recovery failed - " - "board disabled\n"); + ql_log(ql_log_fatal, vha, 0x8035, + "ISP error recover failed - " + "board disabled.\n"); /* * The next call disables the board * completely. @@ -4132,16 +4673,16 @@ qla2x00_abort_isp(scsi_qla_host_t *vha) status = 0; } else { /* schedule another ISP abort */ ha->isp_abort_cnt--; - DEBUG(printk("qla%ld: ISP abort - " - "retry remaining %d\n", - vha->host_no, 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; - DEBUG(printk("qla2x00(%ld): ISP error recovery " - "- retrying (%d) more times\n", - vha->host_no, ha->isp_abort_cnt)); + 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; } @@ -4150,9 +4691,7 @@ qla2x00_abort_isp(scsi_qla_host_t *vha) } if (!status) { - DEBUG(printk(KERN_INFO - "qla2x00_abort_isp(%ld): succeeded.\n", - vha->host_no)); + 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) { @@ -4168,9 +4707,16 @@ qla2x00_abort_isp(scsi_qla_host_t *vha) } spin_unlock_irqrestore(&ha->vport_slock, flags); + if (IS_QLA8031(ha)) { + ql_dbg(ql_dbg_p3p, vha, 0xb05d, + "Setting back fcoe driver presence.\n"); + if (qla83xx_set_drv_presence(vha) != QLA_SUCCESS) + ql_dbg(ql_dbg_p3p, vha, 0xb074, + "Error while setting DRV-Presence.\n"); + } } else { - qla_printk(KERN_INFO, ha, - "qla2x00_abort_isp: **** FAILED ****\n"); + ql_log(ql_log_warn, vha, 0x8023, "%s **** FAILED ****.\n", + __func__); } return(status); @@ -4190,10 +4736,10 @@ 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]; + unsigned long flags; /* If firmware needs to be loaded */ if (qla2x00_isp_firmware(vha)) { @@ -4206,37 +4752,32 @@ qla2x00_restart_isp(scsi_qla_host_t *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) { - DEBUG(printk("%s(): Start configure loop, " - "status = %d\n", __func__, 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))); + + /* + * Process any ATIO queue entries that came in + * while we weren't online. + */ + spin_lock_irqsave(&ha->hardware_lock, flags); + if (qla_tgt_mode_enabled(vha)) + qlt_24xx_process_atio_queue(vha); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); } /* if no cable then assume it's good */ if ((vha->device_flags & DFLG_NO_CABLE)) status = 0; - - DEBUG(printk("%s(): Configure loop done, status = 0x%x\n", - __func__, - status)); } return (status); } @@ -4256,13 +4797,13 @@ qla25xx_init_queues(struct qla_hw_data *ha) rsp->options &= ~BIT_0; ret = qla25xx_init_rsp_que(base_vha, rsp); if (ret != QLA_SUCCESS) - DEBUG2_17(printk(KERN_WARNING - "%s Rsp que:%d init failed\n", __func__, - rsp->id)); + ql_dbg(ql_dbg_init, base_vha, 0x00ff, + "%s Rsp que: %d init failed.\n", + __func__, rsp->id); else - DEBUG2_17(printk(KERN_INFO - "%s Rsp que:%d inited\n", __func__, - rsp->id)); + 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++) { @@ -4272,13 +4813,13 @@ qla25xx_init_queues(struct qla_hw_data *ha) req->options &= ~BIT_0; ret = qla25xx_init_req_que(base_vha, req); if (ret != QLA_SUCCESS) - DEBUG2_17(printk(KERN_WARNING - "%s Req que:%d init failed\n", __func__, - req->id)); + ql_dbg(ql_dbg_init, base_vha, 0x0101, + "%s Req que: %d init failed.\n", + __func__, req->id); else - DEBUG2_17(printk(KERN_WARNING - "%s Req que:%d inited\n", __func__, - req->id)); + ql_dbg(ql_dbg_init, base_vha, 0x0102, + "%s Req que: %d inited.\n", + __func__, req->id); } } return ret; @@ -4316,7 +4857,7 @@ qla24xx_reset_adapter(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; - if (IS_QLA82XX(ha)) + if (IS_P3P_TYPE(ha)) return; vha->flags.online = 0; @@ -4373,17 +4914,16 @@ qla24xx_nvram_config(scsi_qla_host_t *vha) nv = ha->nvram; /* Determine NVRAM starting address. */ - if (ha->flags.port0) { + if (ha->port_no == 0) { 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; @@ -4397,19 +4937,22 @@ qla24xx_nvram_config(scsi_qla_host_t *vha) for (cnt = 0, chksum = 0; cnt < ha->nvram_size >> 2; cnt++) chksum += le32_to_cpu(*dptr++); - DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", vha->host_no)); - DEBUG5(qla2x00_dump_buffer((uint8_t *)nv, ha->nvram_size)); + 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. */ - qla_printk(KERN_WARNING, ha, "Inconsistent NVRAM detected: " - "checksum=0x%x id=%c version=0x%x.\n", chksum, nv->id[0], - le16_to_cpu(nv->nvram_version)); - qla_printk(KERN_WARNING, ha, "Falling back to functioning (yet " - "invalid -- WWPN) defaults.\n"); + ql_log(ql_log_warn, vha, 0x006b, + "Inconsistent 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. @@ -4422,7 +4965,7 @@ qla24xx_nvram_config(scsi_qla_host_t *vha) 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[1] = 0x00 + ha->port_no + 1; nv->port_name[2] = 0x00; nv->port_name[3] = 0xe0; nv->port_name[4] = 0x8b; @@ -4456,6 +4999,15 @@ qla24xx_nvram_config(scsi_qla_host_t *vha) rval = 1; } + if (!qla_ini_mode_enabled(vha)) { + /* Don't enable full login after initial LIP */ + nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_13); + /* Don't enable LIP full login for initiator */ + nv->host_p &= __constant_cpu_to_le32(~BIT_10); + } + + qlt_24xx_config_nvram_stage1(vha, nv); + /* Reset Initialization control block */ memset(icb, 0, ha->init_cb_size); @@ -4483,8 +5035,10 @@ qla24xx_nvram_config(scsi_qla_host_t *vha) qla2x00_set_model_info(vha, nv->model_name, sizeof(nv->model_name), "QLA2462"); - /* Use alternate WWN? */ + qlt_24xx_config_nvram_stage2(vha, icb); + if (nv->host_p & __constant_cpu_to_le32(BIT_15)) { + /* Use alternate WWN? */ memcpy(icb->node_name, nv->alternate_node_name, WWN_SIZE); memcpy(icb->port_name, nv->alternate_port_name, WWN_SIZE); } @@ -4587,10 +5141,7 @@ qla24xx_nvram_config(scsi_qla_host_t *vha) if (ha->zio_mode != QLA_ZIO_DISABLED) { ha->zio_mode = QLA_ZIO_MODE_6; - DEBUG2(printk("scsi(%ld): ZIO mode %d enabled; timer delay " - "(%d us).\n", vha->host_no, ha->zio_mode, - ha->zio_timer * 100)); - qla_printk(KERN_INFO, ha, + ql_log(ql_log_info, vha, 0x006f, "ZIO mode %d enabled; timer delay (%d us).\n", ha->zio_mode, ha->zio_timer * 100); @@ -4601,8 +5152,8 @@ qla24xx_nvram_config(scsi_qla_host_t *vha) } if (rval) { - DEBUG2_3(printk(KERN_WARNING - "scsi(%ld): NVRAM configuration failed!\n", vha->host_no)); + ql_log(ql_log_warn, vha, 0x0070, + "NVRAM configuration failed.\n"); } return (rval); } @@ -4620,8 +5171,8 @@ qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr, struct qla_hw_data *ha = vha->hw; struct req_que *req = ha->req_q_map[0]; - qla_printk(KERN_INFO, ha, - "FW: Loading from flash (%x)...\n", faddr); + ql_dbg(ql_dbg_init, vha, 0x008b, + "FW: Loading firmware from flash (%x).\n", faddr); rval = QLA_SUCCESS; @@ -4637,11 +5188,12 @@ qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr, dcode[2] == 0xffffffff && dcode[3] == 0xffffffff) || (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 && dcode[3] == 0)) { - qla_printk(KERN_WARNING, ha, - "Unable to verify integrity of flash firmware image!\n"); - qla_printk(KERN_WARNING, ha, - "Firmware data: %08x %08x %08x %08x!\n", dcode[0], - dcode[1], dcode[2], dcode[3]); + 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; } @@ -4660,9 +5212,10 @@ qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr, if (dlen > risc_size) dlen = risc_size; - DEBUG7(printk("scsi(%ld): Loading risc segment@ risc " - "addr %x, number of dwords 0x%x, offset 0x%x.\n", - vha->host_no, risc_addr, dlen, faddr)); + 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++) @@ -4671,12 +5224,9 @@ qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr, rval = qla2x00_load_ram(vha, req->dma, risc_addr, dlen); if (rval) { - DEBUG(printk("scsi(%ld):[ERROR] Failed to load " - "segment %d of firmware\n", vha->host_no, - fragment)); - qla_printk(KERN_WARNING, ha, - "[ERROR] Failed to load segment %d of " - "firmware\n", fragment); + ql_log(ql_log_fatal, vha, 0x008f, + "Failed to load segment %d of firmware.\n", + fragment); break; } @@ -4690,10 +5240,103 @@ qla24xx_load_risc_flash(scsi_qla_host_t *vha, uint32_t *srisc_addr, segments--; } + if (!IS_QLA27XX(ha)) + return rval; + + if (ha->fw_dump_template) + vfree(ha->fw_dump_template); + ha->fw_dump_template = NULL; + ha->fw_dump_template_len = 0; + + ql_dbg(ql_dbg_init, vha, 0x0161, + "Loading fwdump template from %x\n", faddr); + qla24xx_read_flash_data(vha, dcode, faddr, 7); + risc_size = be32_to_cpu(dcode[2]); + ql_dbg(ql_dbg_init, vha, 0x0162, + "-> array size %x dwords\n", risc_size); + if (risc_size == 0 || risc_size == ~0) + goto default_template; + + dlen = (risc_size - 8) * sizeof(*dcode); + ql_dbg(ql_dbg_init, vha, 0x0163, + "-> template allocating %x bytes...\n", dlen); + ha->fw_dump_template = vmalloc(dlen); + if (!ha->fw_dump_template) { + ql_log(ql_log_warn, vha, 0x0164, + "Failed fwdump template allocate %x bytes.\n", risc_size); + goto default_template; + } + + faddr += 7; + risc_size -= 8; + dcode = ha->fw_dump_template; + qla24xx_read_flash_data(vha, dcode, faddr, risc_size); + for (i = 0; i < risc_size; i++) + dcode[i] = le32_to_cpu(dcode[i]); + + if (!qla27xx_fwdt_template_valid(dcode)) { + ql_log(ql_log_warn, vha, 0x0165, + "Failed fwdump template validate\n"); + goto default_template; + } + + dlen = qla27xx_fwdt_template_size(dcode); + ql_dbg(ql_dbg_init, vha, 0x0166, + "-> template size %x bytes\n", dlen); + if (dlen > risc_size * sizeof(*dcode)) { + ql_log(ql_log_warn, vha, 0x0167, + "Failed fwdump template exceeds array by %x bytes\n", + (uint32_t)(dlen - risc_size * sizeof(*dcode))); + goto default_template; + } + ha->fw_dump_template_len = dlen; + return rval; + +default_template: + ql_log(ql_log_warn, vha, 0x0168, "Using default fwdump template\n"); + if (ha->fw_dump_template) + vfree(ha->fw_dump_template); + ha->fw_dump_template = NULL; + ha->fw_dump_template_len = 0; + + dlen = qla27xx_fwdt_template_default_size(); + ql_dbg(ql_dbg_init, vha, 0x0169, + "-> template allocating %x bytes...\n", dlen); + ha->fw_dump_template = vmalloc(dlen); + if (!ha->fw_dump_template) { + ql_log(ql_log_warn, vha, 0x016a, + "Failed fwdump template allocate %x bytes.\n", risc_size); + goto failed_template; + } + + dcode = ha->fw_dump_template; + risc_size = dlen / sizeof(*dcode); + memcpy(dcode, qla27xx_fwdt_template_default(), dlen); + for (i = 0; i < risc_size; i++) + dcode[i] = be32_to_cpu(dcode[i]); + + if (!qla27xx_fwdt_template_valid(ha->fw_dump_template)) { + ql_log(ql_log_warn, vha, 0x016b, + "Failed fwdump template validate\n"); + goto failed_template; + } + + dlen = qla27xx_fwdt_template_size(ha->fw_dump_template); + ql_dbg(ql_dbg_init, vha, 0x016c, + "-> template size %x bytes\n", dlen); + ha->fw_dump_template_len = dlen; + return rval; + +failed_template: + ql_log(ql_log_warn, vha, 0x016d, "Failed default fwdump template\n"); + if (ha->fw_dump_template) + vfree(ha->fw_dump_template); + ha->fw_dump_template = NULL; + ha->fw_dump_template_len = 0; return rval; } -#define QLA_FW_URL "ftp://ftp.qlogic.com/outgoing/linux/firmware/" +#define QLA_FW_URL "http://ldriver.qlogic.com/firmware/" int qla2x00_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) @@ -4709,9 +5352,10 @@ qla2x00_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) /* Load firmware blob. */ blob = qla2x00_request_firmware(vha); if (!blob) { - qla_printk(KERN_ERR, ha, "Firmware image unavailable.\n"); - qla_printk(KERN_ERR, ha, "Firmware images can be retrieved " - "from: " QLA_FW_URL ".\n"); + 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; } @@ -4724,8 +5368,8 @@ qla2x00_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) /* Validate firmware image by checking version. */ if (blob->fw->size < 8 * sizeof(uint16_t)) { - qla_printk(KERN_WARNING, ha, - "Unable to verify integrity of firmware image (%Zd)!\n", + ql_log(ql_log_fatal, vha, 0x0085, + "Unable to verify integrity of firmware image (%Zd).\n", blob->fw->size); goto fail_fw_integrity; } @@ -4734,11 +5378,11 @@ qla2x00_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) 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)) { - qla_printk(KERN_WARNING, ha, - "Unable to verify integrity of firmware image!\n"); - qla_printk(KERN_WARNING, ha, - "Firmware data: %04x %04x %04x %04x!\n", wcode[0], - wcode[1], wcode[2], wcode[3]); + 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; } @@ -4751,9 +5395,9 @@ qla2x00_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) /* Validate firmware image size. */ fwclen += risc_size * sizeof(uint16_t); if (blob->fw->size < fwclen) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_fatal, vha, 0x0088, "Unable to verify integrity of firmware image " - "(%Zd)!\n", blob->fw->size); + "(%Zd).\n", blob->fw->size); goto fail_fw_integrity; } @@ -4762,10 +5406,9 @@ qla2x00_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) wlen = (uint16_t)(ha->fw_transfer_size >> 1); if (wlen > risc_size) wlen = risc_size; - - DEBUG7(printk("scsi(%ld): Loading risc segment@ risc " - "addr %x, number of words 0x%x.\n", vha->host_no, - risc_addr, wlen)); + 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]); @@ -4773,12 +5416,9 @@ qla2x00_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) rval = qla2x00_load_ram(vha, req->dma, risc_addr, wlen); if (rval) { - DEBUG(printk("scsi(%ld):[ERROR] Failed to load " - "segment %d of firmware\n", vha->host_no, - fragment)); - qla_printk(KERN_WARNING, ha, - "[ERROR] Failed to load segment %d of " - "firmware\n", fragment); + ql_log(ql_log_fatal, vha, 0x008a, + "Failed to load segment %d of firmware.\n", + fragment); break; } @@ -4807,22 +5447,25 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr) uint32_t risc_size; uint32_t i; struct fw_blob *blob; - uint32_t *fwcode, fwclen; + const uint32_t *fwcode; + uint32_t 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) { - qla_printk(KERN_ERR, ha, "Firmware image unavailable.\n"); - qla_printk(KERN_ERR, ha, "Firmware images can be retrieved " - "from: " QLA_FW_URL ".\n"); + 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; } - qla_printk(KERN_INFO, ha, - "FW: Loading via request-firmware...\n"); + ql_dbg(ql_dbg_init, vha, 0x0092, + "FW: Loading via request-firmware.\n"); rval = QLA_SUCCESS; @@ -4834,10 +5477,10 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr) /* Validate firmware image by checking version. */ if (blob->fw->size < 8 * sizeof(uint32_t)) { - qla_printk(KERN_WARNING, ha, - "Unable to verify integrity of firmware image (%Zd)!\n", + ql_log(ql_log_fatal, vha, 0x0093, + "Unable to verify integrity of firmware image (%Zd).\n", blob->fw->size); - goto fail_fw_integrity; + return QLA_FUNCTION_FAILED; } for (i = 0; i < 4; i++) dcode[i] = be32_to_cpu(fwcode[i + 4]); @@ -4845,12 +5488,13 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr) dcode[2] == 0xffffffff && dcode[3] == 0xffffffff) || (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 && dcode[3] == 0)) { - qla_printk(KERN_WARNING, ha, - "Unable to verify integrity of firmware image!\n"); - qla_printk(KERN_WARNING, ha, - "Firmware data: %08x %08x %08x %08x!\n", dcode[0], - dcode[1], dcode[2], dcode[3]); - goto fail_fw_integrity; + 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]); + return QLA_FUNCTION_FAILED; } while (segments && rval == QLA_SUCCESS) { @@ -4861,11 +5505,10 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr) /* Validate firmware image size. */ fwclen += risc_size * sizeof(uint32_t); if (blob->fw->size < fwclen) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_fatal, vha, 0x0096, "Unable to verify integrity of firmware image " - "(%Zd)!\n", blob->fw->size); - - goto fail_fw_integrity; + "(%Zd).\n", blob->fw->size); + return QLA_FUNCTION_FAILED; } fragment = 0; @@ -4874,9 +5517,9 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr) if (dlen > risc_size) dlen = risc_size; - DEBUG7(printk("scsi(%ld): Loading risc segment@ risc " - "addr %x, number of dwords 0x%x.\n", vha->host_no, - risc_addr, dlen)); + 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]); @@ -4884,12 +5527,9 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr) rval = qla2x00_load_ram(vha, req->dma, risc_addr, dlen); if (rval) { - DEBUG(printk("scsi(%ld):[ERROR] Failed to load " - "segment %d of firmware\n", vha->host_no, - fragment)); - qla_printk(KERN_WARNING, ha, - "[ERROR] Failed to load segment %d of " - "firmware\n", fragment); + ql_log(ql_log_fatal, vha, 0x0098, + "Failed to load segment %d of firmware.\n", + fragment); break; } @@ -4902,10 +5542,100 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr) /* Next segment. */ segments--; } + + if (!IS_QLA27XX(ha)) + return rval; + + if (ha->fw_dump_template) + vfree(ha->fw_dump_template); + ha->fw_dump_template = NULL; + ha->fw_dump_template_len = 0; + + ql_dbg(ql_dbg_init, vha, 0x171, + "Loading fwdump template from %x\n", + (uint32_t)((void *)fwcode - (void *)blob->fw->data)); + risc_size = be32_to_cpu(fwcode[2]); + ql_dbg(ql_dbg_init, vha, 0x172, + "-> array size %x dwords\n", risc_size); + if (risc_size == 0 || risc_size == ~0) + goto default_template; + + dlen = (risc_size - 8) * sizeof(*fwcode); + ql_dbg(ql_dbg_init, vha, 0x0173, + "-> template allocating %x bytes...\n", dlen); + ha->fw_dump_template = vmalloc(dlen); + if (!ha->fw_dump_template) { + ql_log(ql_log_warn, vha, 0x0174, + "Failed fwdump template allocate %x bytes.\n", risc_size); + goto default_template; + } + + fwcode += 7; + risc_size -= 8; + dcode = ha->fw_dump_template; + for (i = 0; i < risc_size; i++) + dcode[i] = le32_to_cpu(fwcode[i]); + + if (!qla27xx_fwdt_template_valid(dcode)) { + ql_log(ql_log_warn, vha, 0x0175, + "Failed fwdump template validate\n"); + goto default_template; + } + + dlen = qla27xx_fwdt_template_size(dcode); + ql_dbg(ql_dbg_init, vha, 0x0176, + "-> template size %x bytes\n", dlen); + if (dlen > risc_size * sizeof(*fwcode)) { + ql_log(ql_log_warn, vha, 0x0177, + "Failed fwdump template exceeds array by %x bytes\n", + (uint32_t)(dlen - risc_size * sizeof(*fwcode))); + goto default_template; + } + ha->fw_dump_template_len = dlen; return rval; -fail_fw_integrity: - return QLA_FUNCTION_FAILED; +default_template: + ql_log(ql_log_warn, vha, 0x0178, "Using default fwdump template\n"); + if (ha->fw_dump_template) + vfree(ha->fw_dump_template); + ha->fw_dump_template = NULL; + ha->fw_dump_template_len = 0; + + dlen = qla27xx_fwdt_template_default_size(); + ql_dbg(ql_dbg_init, vha, 0x0179, + "-> template allocating %x bytes...\n", dlen); + ha->fw_dump_template = vmalloc(dlen); + if (!ha->fw_dump_template) { + ql_log(ql_log_warn, vha, 0x017a, + "Failed fwdump template allocate %x bytes.\n", risc_size); + goto failed_template; + } + + dcode = ha->fw_dump_template; + risc_size = dlen / sizeof(*fwcode); + fwcode = qla27xx_fwdt_template_default(); + for (i = 0; i < risc_size; i++) + dcode[i] = be32_to_cpu(fwcode[i]); + + if (!qla27xx_fwdt_template_valid(ha->fw_dump_template)) { + ql_log(ql_log_warn, vha, 0x017b, + "Failed fwdump template validate\n"); + goto failed_template; + } + + dlen = qla27xx_fwdt_template_size(ha->fw_dump_template); + ql_dbg(ql_dbg_init, vha, 0x017c, + "-> template size %x bytes\n", dlen); + ha->fw_dump_template_len = dlen; + return rval; + +failed_template: + ql_log(ql_log_warn, vha, 0x017d, "Failed default fwdump template\n"); + if (ha->fw_dump_template) + vfree(ha->fw_dump_template); + ha->fw_dump_template = NULL; + ha->fw_dump_template_len = 0; + return rval; } int @@ -4953,16 +5683,14 @@ try_blob_fw: if (rval == QLA_SUCCESS || !ha->flt_region_gold_fw) return rval; - qla_printk(KERN_ERR, ha, - "FW: Attempting to fallback to golden firmware...\n"); + 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; - qla_printk(KERN_ERR, ha, - "FW: Please update operational firmware...\n"); + ql_log(ql_log_info, vha, 0x009a, "Update operational firmware.\n"); ha->flags.running_gold_fw = 1; - return rval; } @@ -4987,8 +5715,8 @@ qla2x00_try_to_stop_firmware(scsi_qla_host_t *vha) continue; if (qla2x00_setup_chip(vha) != QLA_SUCCESS) continue; - qla_printk(KERN_INFO, ha, - "Attempting retry of stop-firmware command...\n"); + ql_log(ql_log_info, vha, 0x8015, + "Attempting retry of stop-firmware command.\n"); ret = qla2x00_stop_firmware(vha); } } @@ -4997,6 +5725,7 @@ 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); @@ -5021,12 +5750,18 @@ qla24xx_configure_vhba(scsi_qla_host_t *vha) vha->flags.management_server_logged_in = 0; /* Login to SNS first */ - ha->isp_ops->fabric_login(vha, NPH_SNS, 0xff, 0xff, 0xfc, mb, BIT_1); - if (mb[0] != MBS_COMMAND_COMPLETE) { - DEBUG15(qla_printk(KERN_INFO, ha, - "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])); + 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); } @@ -5133,6 +5868,8 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) /* Determine NVRAM starting address. */ ha->nvram_size = sizeof(struct nvram_81xx); ha->vpd_size = FA_NVRAM_VPD_SIZE; + if (IS_P3P_TYPE(ha) || IS_QLA8031(ha)) + ha->vpd_size = FA_VPD_SIZE_82XX; /* Get VPD data into cache */ ha->vpd = ha->nvram + VPD_OFFSET; @@ -5146,19 +5883,23 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) for (cnt = 0, chksum = 0; cnt < ha->nvram_size >> 2; cnt++) chksum += le32_to_cpu(*dptr++); - DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", vha->host_no)); - DEBUG5(qla2x00_dump_buffer((uint8_t *)nv, ha->nvram_size)); + 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. */ - qla_printk(KERN_WARNING, ha, "Inconsistent NVRAM detected: " - "checksum=0x%x id=%c version=0x%x.\n", chksum, nv->id[0], + ql_log(ql_log_info, vha, 0x0073, + "Inconsistent NVRAM detected: checksum=0x%x id=%c " + "version=0x%x.\n", chksum, nv->id[0], le16_to_cpu(nv->nvram_version)); - qla_printk(KERN_WARNING, ha, "Falling back to functioning (yet " - "invalid -- WWPN) defaults.\n"); + ql_log(ql_log_info, vha, 0x0074, + "Falling back to functioning (yet invalid -- WWPN) " + "defaults.\n"); /* * Set default initialization control block. @@ -5170,7 +5911,7 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) 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[1] = 0x00 + ha->port_no + 1; nv->port_name[2] = 0x00; nv->port_name[3] = 0xe0; nv->port_name[4] = 0x8b; @@ -5198,17 +5939,22 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) 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); + nv->link_down_timeout = __constant_cpu_to_le16(180); nv->enode_mac[0] = 0x00; - nv->enode_mac[1] = 0x02; - nv->enode_mac[2] = 0x03; + 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; + nv->enode_mac[5] = 0x06 + ha->port_no + 1; rval = 1; } + if (IS_T10_PI_CAPABLE(ha)) + nv->frame_payload_size &= ~7; + + qlt_81xx_config_nvram_stage1(vha, nv); + /* Reset Initialization control block */ memset(icb, 0, ha->init_cb_size); @@ -5232,12 +5978,12 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) 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] = 0x01; - icb->enode_mac[1] = 0x02; - icb->enode_mac[2] = 0x03; + 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; + icb->enode_mac[5] = 0x06 + ha->port_no + 1; } /* Use extended-initialization control block. */ @@ -5249,6 +5995,8 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) qla2x00_set_model_info(vha, nv->model_name, sizeof(nv->model_name), "QLE8XXX"); + qlt_81xx_config_nvram_stage2(vha, icb); + /* Use alternate WWN? */ if (nv->host_p & __constant_cpu_to_le32(BIT_15)) { memcpy(icb->node_name, nv->alternate_node_name, WWN_SIZE); @@ -5304,7 +6052,7 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) /* Link Down Timeout = 0: * - * When Port Down timer expires we will start returning + * When Port Down timer expires we will start returning * I/O's to OS with "DID_NO_CONNECT". * * Link Down Timeout != 0: @@ -5337,6 +6085,10 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) 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) || IS_QLA27XX(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) & @@ -5350,12 +6102,10 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) if (ha->zio_mode != QLA_ZIO_DISABLED) { ha->zio_mode = QLA_ZIO_MODE_6; - DEBUG2(printk("scsi(%ld): ZIO mode %d enabled; timer delay " - "(%d us).\n", vha->host_no, ha->zio_mode, - ha->zio_timer * 100)); - qla_printk(KERN_INFO, ha, + ql_log(ql_log_info, vha, 0x0075, "ZIO mode %d enabled; timer delay (%d us).\n", - ha->zio_mode, ha->zio_timer * 100); + ha->zio_mode, + ha->zio_timer * 100); icb->firmware_options_2 |= cpu_to_le32( (uint32_t)ha->zio_mode); @@ -5364,8 +6114,8 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) } if (rval) { - DEBUG2_3(printk(KERN_WARNING - "scsi(%ld): NVRAM configuration failed!\n", vha->host_no)); + ql_log(ql_log_warn, vha, 0x0076, + "NVRAM configuration failed.\n"); } return (rval); } @@ -5374,7 +6124,6 @@ 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]; @@ -5388,33 +6137,15 @@ qla82xx_restart_isp(scsi_qla_host_t *vha) status = qla2x00_fw_ready(vha); if (!status) { - qla_printk(KERN_INFO, ha, - "%s(): Start configure loop, " - "status = %d\n", __func__, 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))); + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); } /* if no cable then assume it's good */ if ((vha->device_flags & DFLG_NO_CABLE)) status = 0; - - qla_printk(KERN_INFO, ha, - "%s(): Configure loop done, status = 0x%x\n", - __func__, status); } if (!status) { @@ -5428,19 +6159,13 @@ qla82xx_restart_isp(scsi_qla_host_t *vha) 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 */ - qla2x00_get_fw_version(vha, &ha->fw_major_version, - &ha->fw_minor_version, &ha->fw_subminor_version, - &ha->fw_attributes, &ha->fw_memory_size, - ha->mpi_version, &ha->mpi_capabilities, - ha->phy_version); + status = qla82xx_check_md_needed(vha); if (ha->fce) { ha->flags.fce_enabled = 1; @@ -5450,9 +6175,9 @@ qla82xx_restart_isp(scsi_qla_host_t *vha) ha->fce_dma, ha->fce_bufs, ha->fce_mb, &ha->fce_bufs); if (rval) { - qla_printk(KERN_WARNING, ha, - "Unable to reinitialize FCE " - "(%d).\n", rval); + ql_log(ql_log_warn, vha, 0x8001, + "Unable to reinitialize FCE (%d).\n", + rval); ha->flags.fce_enabled = 0; } } @@ -5462,17 +6187,16 @@ qla82xx_restart_isp(scsi_qla_host_t *vha) rval = qla2x00_enable_eft_trace(vha, ha->eft_dma, EFT_NUM_BUFFERS); if (rval) { - qla_printk(KERN_WARNING, ha, - "Unable to reinitialize EFT " - "(%d).\n", rval); + ql_log(ql_log_warn, vha, 0x8010, + "Unable to reinitialize EFT (%d).\n", + rval); } } } if (!status) { - DEBUG(printk(KERN_INFO - "qla82xx_restart_isp(%ld): succeeded.\n", - vha->host_no)); + 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) { @@ -5489,8 +6213,8 @@ qla82xx_restart_isp(scsi_qla_host_t *vha) spin_unlock_irqrestore(&ha->vport_slock, flags); } else { - qla_printk(KERN_INFO, ha, - "qla82xx_restart_isp: **** FAILED ****\n"); + ql_log(ql_log_warn, vha, 0x8016, + "qla82xx_restart_isp **** FAILED ****.\n"); } return status; @@ -5636,14 +6360,26 @@ qla24xx_update_fcport_fcp_prio(scsi_qla_host_t *vha, fc_port_t *fcport) if (priority < 0) return QLA_FUNCTION_FAILED; - ret = qla24xx_set_fcp_prio(vha, fcport->loop_id, priority, mb); - if (ret == QLA_SUCCESS) - fcport->fcp_prio = priority; - else - DEBUG2(printk(KERN_WARNING - "scsi(%ld): Unable to activate fcp priority, " - " ret=0x%x\n", vha->host_no, ret)); + if (IS_P3P_TYPE(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; } diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h index 4c8167e11f6..b3b1d6fc2d6 100644 --- a/drivers/scsi/qla2xxx/qla_inline.h +++ b/drivers/scsi/qla2xxx/qla_inline.h @@ -1,10 +1,33 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ +#include "qla_target.h" +/** + * 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. + */ +static 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; +} + /* * qla2x00_debounce_register * Debounce register. @@ -37,7 +60,7 @@ qla2x00_poll(struct rsp_que *rsp) unsigned long flags; struct qla_hw_data *ha = rsp->hw; local_irq_save(flags); - if (IS_QLA82XX(ha)) + if (IS_P3P_TYPE(ha)) qla82xx_poll(0, rsp); else ha->isp_ops->intr_handler(0, rsp); @@ -57,6 +80,31 @@ host_to_fcp_swap(uint8_t *fcp, uint32_t bsize) return fcp; } +static inline void +host_to_adap(uint8_t *src, uint8_t *dst, uint32_t bsize) +{ + uint32_t *isrc = (uint32_t *) src; + __le32 *odest = (__le32 *) dst; + uint32_t iter = bsize >> 2; + + for (; iter ; iter--) + *odest++ = cpu_to_le32(*isrc++); +} + +static inline void +qla2x00_set_reserved_loop_ids(struct qla_hw_data *ha) +{ + int i; + + if (IS_FWI2_CAPABLE(ha)) + return; + + for (i = 0; i < SNS_FIRST_LOOP_ID; i++) + set_bit(i, ha->loop_id_map); + set_bit(MANAGEMENT_SERVER, ha->loop_id_map); + set_bit(BROADCAST, ha->loop_id_map); +} + static inline int qla2x00_is_reserved_id(scsi_qla_host_t *vha, uint16_t loop_id) { @@ -69,19 +117,42 @@ qla2x00_is_reserved_id(scsi_qla_host_t *vha, uint16_t loop_id) } static inline void -qla2x00_clean_dsd_pool(struct qla_hw_data *ha, srb_t *sp) +qla2x00_clear_loop_id(fc_port_t *fcport) { + struct qla_hw_data *ha = fcport->vha->hw; + + if (fcport->loop_id == FC_NO_LOOP_ID || + qla2x00_is_reserved_id(fcport->vha, fcport->loop_id)) + return; + + clear_bit(fcport->loop_id, ha->loop_id_map); + fcport->loop_id = FC_NO_LOOP_ID; +} + +static inline void +qla2x00_clean_dsd_pool(struct qla_hw_data *ha, srb_t *sp, + struct qla_tgt_cmd *tc) { struct dsd_dma *dsd_ptr, *tdsd_ptr; + struct crc_context *ctx; + + if (sp) + ctx = (struct crc_context *)GET_CMD_CTX_SP(sp); + else if (tc) + ctx = (struct crc_context *)tc->ctx; + else { + BUG(); + return; + } /* clean up allocated prev pool */ list_for_each_entry_safe(dsd_ptr, tdsd_ptr, - &((struct crc_context *)sp->ctx)->dsd_list, list) { + &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(&((struct crc_context *)sp->ctx)->dsd_list); + INIT_LIST_HEAD(&ctx->dsd_list); } static inline void @@ -94,11 +165,117 @@ qla2x00_set_fcport_state(fc_port_t *fcport, int state) /* Don't print state transitions during initial allocation of fcport */ if (old_state && old_state != state) { - DEBUG(qla_printk(KERN_WARNING, fcport->vha->hw, - "scsi(%ld): FCPort state transitioned from %s to %s - " - "portid=%02x%02x%02x.\n", fcport->vha->host_no, + 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)); + 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_rel_sp(scsi_qla_host_t *vha, srb_t *sp) +{ + mempool_free(sp, vha->hw->srb_mempool); + QLA_VHA_MARK_NOT_BUSY(vha); +} + +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; + if ((IS_QLAFX00(sp->fcport->vha->hw)) && + (sp->type == SRB_FXIOCB_DCMD)) + init_completion(&sp->u.iocb_cmd.u.fxiocb.fxiocb_comp); +} + +static inline int +qla2x00_gid_list_size(struct qla_hw_data *ha) +{ + if (IS_QLAFX00(ha)) + return sizeof(uint32_t) * 32; + else + return sizeof(struct gid_list_info) * ha->max_fibre_devices; +} + +static inline void +qla2x00_handle_mbx_completion(struct qla_hw_data *ha, int status) +{ + 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); + clear_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags); + complete(&ha->mbx_intr_comp); } } diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index 7bac3cd109d..76093152959 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -1,18 +1,17 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ #include "qla_def.h" +#include "qla_target.h" #include <linux/blkdev.h> #include <linux/delay.h> #include <scsi/scsi_tcq.h> -static void qla2x00_isp_cmd(struct scsi_qla_host *, struct req_que *); - static void qla25xx_set_que(srb_t *, struct rsp_que **); /** * qla2x00_get_cmd_direction() - Determine control_flag data direction. @@ -24,18 +23,20 @@ static inline uint16_t qla2x00_get_cmd_direction(srb_t *sp) { uint16_t cflags; + struct scsi_cmnd *cmd = GET_CMD_SP(sp); + struct scsi_qla_host *vha = sp->fcport->vha; cflags = 0; /* Set transfer direction */ - if (sp->cmd->sc_data_direction == DMA_TO_DEVICE) { + if (cmd->sc_data_direction == DMA_TO_DEVICE) { cflags = CF_WRITE; - sp->fcport->vha->hw->qla_stats.output_bytes += - scsi_bufflen(sp->cmd); - } else if (sp->cmd->sc_data_direction == DMA_FROM_DEVICE) { + vha->qla_stats.output_bytes += scsi_bufflen(cmd); + vha->qla_stats.output_requests++; + } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) { cflags = CF_READ; - sp->fcport->vha->hw->qla_stats.input_bytes += - scsi_bufflen(sp->cmd); + vha->qla_stats.input_bytes += scsi_bufflen(cmd); + vha->qla_stats.input_requests++; } return (cflags); } @@ -120,11 +121,10 @@ qla2x00_prep_cont_type0_iocb(struct scsi_qla_host *vha) * 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) +qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha, struct req_que *req) { cont_a64_entry_t *cont_pkt; - struct req_que *req = vha->req; /* Adjust ring index. */ req->ring_index++; if (req->ring_index == req->length) { @@ -137,7 +137,8 @@ qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha) cont_pkt = (cont_a64_entry_t *)req->ring_ptr; /* Load packet defaults. */ - *((uint32_t *)(&cont_pkt->entry_type)) = + *((uint32_t *)(&cont_pkt->entry_type)) = IS_QLAFX00(vha->hw) ? + __constant_cpu_to_le32(CONTINUE_A64_TYPE_FX00) : __constant_cpu_to_le32(CONTINUE_A64_TYPE); return (cont_pkt); @@ -146,19 +147,14 @@ qla2x00_prep_cont_type1_iocb(scsi_qla_host_t *vha) static inline int qla24xx_configure_prot_mode(srb_t *sp, uint16_t *fw_prot_opts) { - uint8_t guard = scsi_host_get_guard(sp->cmd->device->host); - - /* We only support T10 DIF right now */ - if (guard != SHOST_DIX_GUARD_CRC) { - DEBUG2(printk(KERN_ERR "Unsupported guard: %d\n", guard)); - return 0; - } + struct scsi_cmnd *cmd = GET_CMD_SP(sp); + uint8_t guard = scsi_host_get_guard(cmd->device->host); /* We always use DIFF Bundling for best performance */ *fw_prot_opts = 0; /* Translate SCSI opcode to a protection opcode */ - switch (scsi_get_prot_op(sp->cmd)) { + switch (scsi_get_prot_op(cmd)) { case SCSI_PROT_READ_STRIP: *fw_prot_opts |= PO_MODE_DIF_REMOVE; break; @@ -172,17 +168,18 @@ qla24xx_configure_prot_mode(srb_t *sp, uint16_t *fw_prot_opts) *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; + if (guard & SHOST_DIX_GUARD_IP) + *fw_prot_opts |= PO_MODE_DIF_TCP_CKSUM; + else + *fw_prot_opts |= PO_MODE_DIF_PASS; break; default: /* Normal Request */ *fw_prot_opts |= PO_MODE_DIF_PASS; break; } - return scsi_prot_sg_count(sp->cmd); + return scsi_prot_sg_count(cmd); } /* @@ -203,7 +200,7 @@ void qla2x00_build_scsi_iocbs_32(srb_t *sp, cmd_entry_t *cmd_pkt, struct scatterlist *sg; int i; - cmd = sp->cmd; + cmd = GET_CMD_SP(sp); /* Update entry type to indicate Command Type 2 IOCB */ *((uint32_t *)(&cmd_pkt->entry_type)) = @@ -261,7 +258,7 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt, struct scatterlist *sg; int i; - cmd = sp->cmd; + cmd = GET_CMD_SP(sp); /* Update entry type to indicate Command Type 3 IOCB */ *((uint32_t *)(&cmd_pkt->entry_type)) = @@ -291,7 +288,7 @@ void qla2x00_build_scsi_iocbs_64(srb_t *sp, cmd_entry_t *cmd_pkt, * Five DSDs are available in the Continuation * Type 1 IOCB. */ - cont_pkt = qla2x00_prep_cont_type1_iocb(vha); + cont_pkt = qla2x00_prep_cont_type1_iocb(vha, vha->req); cur_dsd = (uint32_t *)cont_pkt->dseg_0_address; avail_dsds = 5; } @@ -335,7 +332,7 @@ qla2x00_start_scsi(srb_t *sp) vha = sp->fcport->vha; ha = vha->hw; reg = &ha->iobase->isp; - cmd = sp->cmd; + 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 */ @@ -343,9 +340,10 @@ qla2x00_start_scsi(srb_t *sp) /* Send marker if required */ if (vha->marker_needed != 0) { - if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL) - != QLA_SUCCESS) + if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL) != + QLA_SUCCESS) { return (QLA_FUNCTION_FAILED); + } vha->marker_needed = 0; } @@ -354,14 +352,14 @@ qla2x00_start_scsi(srb_t *sp) /* Check for room in outstanding command list. */ handle = req->current_outstanding_cmd; - for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) { + for (index = 1; index < req->num_outstanding_cmds; index++) { handle++; - if (handle == MAX_OUTSTANDING_COMMANDS) + if (handle == req->num_outstanding_cmds) handle = 1; if (!req->outstanding_cmds[handle]) break; } - if (index == MAX_OUTSTANDING_COMMANDS) + if (index == req->num_outstanding_cmds) goto queuing_error; /* Map the sg table so we have an accurate count of sg entries needed */ @@ -384,15 +382,16 @@ qla2x00_start_scsi(srb_t *sp) else req->cnt = req->length - (req->ring_index - cnt); + /* If still no head room then bail out */ + if (req->cnt < (req_cnt + 2)) + goto queuing_error; } - 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; - sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle; + cmd->host_scribble = (unsigned char *)(unsigned long)handle; req->cnt -= req_cnt; cmd_pkt = (cmd_entry_t *)req->ring_ptr; @@ -404,7 +403,7 @@ qla2x00_start_scsi(srb_t *sp) /* Set target ID and LUN number*/ SET_TARGET_ID(ha, cmd_pkt->target, sp->fcport->loop_id); - cmd_pkt->lun = cpu_to_le16(sp->cmd->device->lun); + cmd_pkt->lun = cpu_to_le16(cmd->device->lun); /* Update tagged queuing modifier */ if (scsi_populate_tag_msg(cmd, tag)) { @@ -422,6 +421,8 @@ qla2x00_start_scsi(srb_t *sp) __constant_cpu_to_le16(CF_SIMPLE_TAG); break; } + } else { + cmd_pkt->control_flags = __constant_cpu_to_le16(CF_SIMPLE_TAG); } /* Load SCSI command packet. */ @@ -467,6 +468,45 @@ queuing_error: } /** + * qla2x00_start_iocbs() - Execute the IOCB command + */ +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_P3P_TYPE(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) || IS_QLA27XX(ha)) { + WRT_REG_DWORD(req->req_q_in, req->ring_index); + RD_REG_DWORD_RELAXED(&ha->iobase->isp24.hccr); + } else if (IS_QLAFX00(ha)) { + WRT_REG_DWORD(®->ispfx00.req_q_in, req->ring_index); + RD_REG_DWORD_RELAXED(®->ispfx00.req_q_in); + QLAFX00_SET_HST_INTR(ha, ha->rqstq_intr_code); + } else if (IS_FWI2_CAPABLE(ha)) { + WRT_REG_DWORD(®->isp24.req_q_in, req->ring_index); + RD_REG_DWORD_RELAXED(®->isp24.req_q_in); + } else { + WRT_REG_WORD(ISP_REQ_Q_IN(ha, ®->isp), + req->ring_index); + RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, ®->isp)); + } + } +} + +/** * qla2x00_marker() - Send a marker IOCB to the firmware. * @ha: HA context * @loop_id: loop ID @@ -483,15 +523,16 @@ __qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req, uint16_t lun, uint8_t type) { mrk_entry_t *mrk; - struct mrk_entry_24xx *mrk24; + struct mrk_entry_24xx *mrk24 = NULL; + struct qla_hw_data *ha = vha->hw; scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); - mrk24 = NULL; - mrk = (mrk_entry_t *)qla2x00_alloc_iocbs(vha, 0); + req = ha->req_q_map[0]; + mrk = (mrk_entry_t *)qla2x00_alloc_iocbs(vha, NULL); if (mrk == NULL) { - DEBUG2_3(printk("%s(%ld): failed to allocate Marker IOCB.\n", - __func__, base_vha->host_no)); + ql_log(ql_log_warn, base_vha, 0x3026, + "Failed to allocate Marker IOCB.\n"); return (QLA_FUNCTION_FAILED); } @@ -514,7 +555,7 @@ __qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req, } wmb(); - qla2x00_isp_cmd(vha, req); + qla2x00_start_iocbs(vha, req); return (QLA_SUCCESS); } @@ -534,91 +575,144 @@ qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req, return (ret); } -/** - * qla2x00_isp_cmd() - Modify the request ring pointer. - * @ha: HA context +/* + * qla2x00_issue_marker * - * Note: The caller must hold the hardware lock before calling this routine. + * Issue marker + * Caller CAN have hardware lock held as specified by ha_locked parameter. + * Might release it, then reaquire. */ -static void -qla2x00_isp_cmd(struct scsi_qla_host *vha, struct req_que *req) +int qla2x00_issue_marker(scsi_qla_host_t *vha, int ha_locked) { - struct qla_hw_data *ha = vha->hw; - device_reg_t __iomem *reg = ISP_QUE_REG(ha, req->id); - struct device_reg_2xxx __iomem *ioreg = &ha->iobase->isp; + if (ha_locked) { + if (__qla2x00_marker(vha, vha->req, vha->req->rsp, 0, 0, + MK_SYNC_ALL) != QLA_SUCCESS) + return QLA_FUNCTION_FAILED; + } else { + if (qla2x00_marker(vha, vha->req, vha->req->rsp, 0, 0, + MK_SYNC_ALL) != QLA_SUCCESS) + return QLA_FUNCTION_FAILED; + } + vha->marker_needed = 0; - DEBUG5(printk("%s(): IOCB data:\n", __func__)); - DEBUG5(qla2x00_dump_buffer( - (uint8_t *)req->ring_ptr, REQUEST_ENTRY_SIZE)); + return QLA_SUCCESS; +} - /* 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++; +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; - /* Set chip new ring index. */ - if (IS_QLA82XX(ha)) { - uint32_t dbval = 0x04 | (ha->portnum << 5); - - /* 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(); - } - } - } else if (ha->mqenable) { - /* Set chip new ring index. */ - WRT_REG_DWORD(®->isp25mq.req_q_in, req->ring_index); - RD_REG_DWORD(&ioreg->hccr); - } else { - if (IS_FWI2_CAPABLE(ha)) { - WRT_REG_DWORD(®->isp24.req_q_in, req->ring_index); - RD_REG_DWORD_RELAXED(®->isp24.req_q_in); + 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); + vha->qla_stats.output_bytes += scsi_bufflen(cmd); + vha->qla_stats.output_requests++; + } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) { + cmd_pkt->control_flags = + __constant_cpu_to_le16(CF_READ_DATA); + vha->qla_stats.input_bytes += scsi_bufflen(cmd); + vha->qla_stats.input_requests++; + } + + 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 { - WRT_REG_WORD(ISP_REQ_Q_IN(ha, ®->isp), - req->ring_index); - RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, ®->isp)); + *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_iocbs() - Determine number of Command Type 3 and - * Continuation Type 1 IOCBs to allocate. +/* + * 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 IOCB entries needed to store @dsds. + * Returns the number of dsd list needed to store @dsds. */ inline uint16_t -qla24xx_calc_iocbs(uint16_t dsds) +qla24xx_calc_dsd_lists(uint16_t dsds) { - uint16_t iocbs; + uint16_t dsd_lists = 0; - iocbs = 1; - if (dsds > 1) { - iocbs += (dsds - 1) / 5; - if ((dsds - 1) % 5) - iocbs++; - } - DEBUG3(printk(KERN_DEBUG "%s(): Required PKT(s) = %d\n", - __func__, iocbs)); - return iocbs; + 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. @@ -639,7 +733,7 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt, int i; struct req_que *req; - cmd = sp->cmd; + cmd = GET_CMD_SP(sp); /* Update entry type to indicate Command Type 3 IOCB */ *((uint32_t *)(&cmd_pkt->entry_type)) = @@ -658,13 +752,13 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt, 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(sp->cmd); + vha->qla_stats.output_bytes += scsi_bufflen(cmd); + vha->qla_stats.output_requests++; } 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(sp->cmd); + vha->qla_stats.input_bytes += scsi_bufflen(cmd); + vha->qla_stats.input_requests++; } /* One DSD is available in the Command Type 3 IOCB */ @@ -683,7 +777,7 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt, * Five DSDs are available in the Continuation * Type 1 IOCB. */ - cont_pkt = qla2x00_prep_cont_type1_iocb(vha); + cont_pkt = qla2x00_prep_cont_type1_iocb(vha, vha->req); cur_dsd = (uint32_t *)cont_pkt->dseg_0_address; avail_dsds = 5; } @@ -708,19 +802,27 @@ struct fw_dif_context { * */ static inline void -qla24xx_set_t10dif_tags(struct scsi_cmnd *cmd, struct fw_dif_context *pkt, +qla24xx_set_t10dif_tags(srb_t *sp, struct fw_dif_context *pkt, unsigned int protcnt) { - struct sd_dif_tuple *spt; - unsigned char op = scsi_get_prot_op(cmd); + struct scsi_cmnd *cmd = GET_CMD_SP(sp); switch (scsi_get_prot_type(cmd)) { - /* For TYPE 0 protection: no checking */ case SCSI_PROT_DIF_TYPE0: - pkt->ref_tag_mask[0] = 0x00; - pkt->ref_tag_mask[1] = 0x00; - pkt->ref_tag_mask[2] = 0x00; - pkt->ref_tag_mask[3] = 0x00; + /* + * 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; /* @@ -728,20 +830,16 @@ qla24xx_set_t10dif_tags(struct scsi_cmnd *cmd, struct fw_dif_context *pkt, * match LBA in CDB + N */ case SCSI_PROT_DIF_TYPE2: - if (!ql2xenablehba_err_chk) - break; - - if (scsi_prot_sg_count(cmd)) { - spt = page_address(sg_page(scsi_prot_sglist(cmd))) + - scsi_prot_sglist(cmd)[0].offset; - pkt->app_tag = swab32(spt->app_tag); - pkt->app_tag_mask[0] = 0xff; - pkt->app_tag_mask[1] = 0xff; - } + 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; @@ -761,26 +859,15 @@ qla24xx_set_t10dif_tags(struct scsi_cmnd *cmd, struct fw_dif_context *pkt, * 16 bit app tag. */ case SCSI_PROT_DIF_TYPE1: - if (!ql2xenablehba_err_chk) + 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; - if (protcnt && (op == SCSI_PROT_WRITE_STRIP || - op == SCSI_PROT_WRITE_PASS)) { - spt = page_address(sg_page(scsi_prot_sglist(cmd))) + - scsi_prot_sglist(cmd)[0].offset; - DEBUG18(printk(KERN_DEBUG - "%s(): LBA from user %p, lba = 0x%x\n", - __func__, spt, (int)spt->ref_tag)); - pkt->ref_tag = swab32(spt->ref_tag); - pkt->app_tag_mask[0] = 0x0; - pkt->app_tag_mask[1] = 0x0; - } else { - 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; - } /* enable ALL bytes of the ref tag */ pkt->ref_tag_mask[0] = 0xff; pkt->ref_tag_mask[1] = 0xff; @@ -788,31 +875,215 @@ qla24xx_set_t10dif_tags(struct scsi_cmnd *cmd, struct fw_dif_context *pkt, pkt->ref_tag_mask[3] = 0xff; break; } - - DEBUG18(printk(KERN_DEBUG - "%s(): Setting protection Tags: (BIG) ref tag = 0x%x," - " app tag = 0x%x, prot SG count %d , cmd lba 0x%x," - " prot_type=%u\n", __func__, pkt->ref_tag, pkt->app_tag, protcnt, - (int)scsi_get_lba(cmd), scsi_get_prot_type(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; +} + +int +qla24xx_walk_and_build_sglist_no_difb(struct qla_hw_data *ha, srb_t *sp, + uint32_t *dsd, uint16_t tot_dsds, struct qla_tgt_cmd *tc) +{ + 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; /* protection interval */ + 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; + struct scsi_qla_host *vha; + + memset(&sgx, 0, sizeof(struct qla2_sgx)); + if (sp) { + vha = sp->fcport->vha; + cmd = GET_CMD_SP(sp); + prot_int = cmd->device->sector_size; + + sgx.tot_bytes = scsi_bufflen(cmd); + sgx.cur_sg = scsi_sglist(cmd); + sgx.sp = sp; + + sg_prot = scsi_prot_sglist(cmd); + } else if (tc) { + vha = tc->vha; + prot_int = tc->blk_sz; + sgx.tot_bytes = tc->bufflen; + sgx.cur_sg = tc->sg; + sg_prot = tc->prot_sg; + } else { + BUG(); + return 1; + } + + 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; + } + + if (sp) { + list_add_tail(&dsd_ptr->list, + &((struct crc_context *) + sp->u.scmd.ctx)->dsd_list); + + sp->flags |= SRB_CRC_CTX_DSD_VALID; + } else { + list_add_tail(&dsd_ptr->list, + &(tc->ctx->dsd_list)); + tc->ctx_dsd_alloced = 1; + } + + + /* 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; +} + +int qla24xx_walk_and_build_sglist(struct qla_hw_data *ha, srb_t *sp, uint32_t *dsd, - uint16_t tot_dsds) + uint16_t tot_dsds, struct qla_tgt_cmd *tc) { void *next_dsd; uint8_t avail_dsds = 0; uint32_t dsd_list_len; struct dsd_dma *dsd_ptr; - struct scatterlist *sg; + struct scatterlist *sg, *sgl; uint32_t *cur_dsd = dsd; int i; uint16_t used_dsds = tot_dsds; + struct scsi_cmnd *cmd; + struct scsi_qla_host *vha; + + if (sp) { + cmd = GET_CMD_SP(sp); + sgl = scsi_sglist(cmd); + vha = sp->fcport->vha; + } else if (tc) { + sgl = tc->sg; + vha = tc->vha; + } else { + BUG(); + return 1; + } - uint8_t *cp; - scsi_for_each_sg(sp->cmd, sg, tot_dsds, i) { + for_each_sg(sgl, sg, tot_dsds, i) { dma_addr_t sle_dma; /* Allocate additional continuation packets? */ @@ -841,10 +1112,17 @@ qla24xx_walk_and_build_sglist(struct qla_hw_data *ha, srb_t *sp, uint32_t *dsd, return 1; } - list_add_tail(&dsd_ptr->list, - &((struct crc_context *)sp->ctx)->dsd_list); + if (sp) { + list_add_tail(&dsd_ptr->list, + &((struct crc_context *) + sp->u.scmd.ctx)->dsd_list); - sp->flags |= SRB_CRC_CTX_DSD_VALID; + sp->flags |= SRB_CRC_CTX_DSD_VALID; + } else { + list_add_tail(&dsd_ptr->list, + &(tc->ctx->dsd_list)); + tc->ctx_dsd_alloced = 1; + } /* add new list to cmd iocb or last list */ *cur_dsd++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma)); @@ -853,19 +1131,12 @@ qla24xx_walk_and_build_sglist(struct qla_hw_data *ha, srb_t *sp, uint32_t *dsd, cur_dsd = (uint32_t *)next_dsd; } sle_dma = sg_dma_address(sg); - DEBUG18(printk("%s(): %p, sg entry %d - addr =0x%x 0x%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)); avail_dsds--; - if (scsi_get_prot_op(sp->cmd) == SCSI_PROT_WRITE_PASS) { - cp = page_address(sg_page(sg)) + sg->offset; - DEBUG18(printk("%s(): User Data buffer= %p:\n", - __func__ , cp)); - } } /* Null termination */ *cur_dsd++ = 0; @@ -874,26 +1145,37 @@ qla24xx_walk_and_build_sglist(struct qla_hw_data *ha, srb_t *sp, uint32_t *dsd, return 0; } -static int +int qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *ha, srb_t *sp, - uint32_t *dsd, - uint16_t tot_dsds) + uint32_t *dsd, uint16_t tot_dsds, struct qla_tgt_cmd *tc) { void *next_dsd; uint8_t avail_dsds = 0; uint32_t dsd_list_len; struct dsd_dma *dsd_ptr; - struct scatterlist *sg; + struct scatterlist *sg, *sgl; int i; struct scsi_cmnd *cmd; uint32_t *cur_dsd = dsd; - uint16_t used_dsds = tot_dsds; - - uint8_t *cp; + uint16_t used_dsds = tot_dsds; + struct scsi_qla_host *vha; + + if (sp) { + cmd = GET_CMD_SP(sp); + sgl = scsi_prot_sglist(cmd); + vha = sp->fcport->vha; + } else if (tc) { + vha = tc->vha; + sgl = tc->prot_sg; + } else { + BUG(); + return 1; + } + ql_dbg(ql_dbg_tgt, vha, 0xe021, + "%s: enter\n", __func__); - cmd = sp->cmd; - scsi_for_each_prot_sg(cmd, sg, tot_dsds, i) { + for_each_sg(sgl, sg, tot_dsds, i) { dma_addr_t sle_dma; /* Allocate additional continuation packets? */ @@ -922,10 +1204,17 @@ qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *ha, srb_t *sp, return 1; } - list_add_tail(&dsd_ptr->list, - &((struct crc_context *)sp->ctx)->dsd_list); + if (sp) { + list_add_tail(&dsd_ptr->list, + &((struct crc_context *) + sp->u.scmd.ctx)->dsd_list); - sp->flags |= SRB_CRC_CTX_DSD_VALID; + sp->flags |= SRB_CRC_CTX_DSD_VALID; + } else { + list_add_tail(&dsd_ptr->list, + &(tc->ctx->dsd_list)); + tc->ctx_dsd_alloced = 1; + } /* add new list to cmd iocb or last list */ *cur_dsd++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma)); @@ -934,21 +1223,11 @@ qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *ha, srb_t *sp, cur_dsd = (uint32_t *)next_dsd; } sle_dma = sg_dma_address(sg); - if (scsi_get_prot_op(sp->cmd) == SCSI_PROT_WRITE_PASS) { - DEBUG18(printk(KERN_DEBUG - "%s(): %p, sg entry %d - addr =0x%x" - "0x%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(sp->cmd) == SCSI_PROT_WRITE_PASS) { - cp = page_address(sg_page(sg)) + sg->offset; - DEBUG18(printk("%s(): Protection Data buffer = %p:\n", - __func__ , cp)); - } avail_dsds--; } /* Null termination */ @@ -973,9 +1252,8 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, 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; + uint32_t total_bytes = 0; uint32_t data_bytes; uint32_t dif_bytes; uint8_t bundling = 1; @@ -989,30 +1267,24 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, dma_addr_t crc_ctx_dma; char tag[2]; - cmd = sp->cmd; + 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) { - DEBUG18(printk(KERN_INFO "%s: Zero data bytes or DMA-NONE %d\n", - __func__, data_bytes)); cmd_pkt->byte_count = __constant_cpu_to_le32(0); return QLA_SUCCESS; } - vha = sp->fcport->vha; - ha = vha->hw; - - DEBUG18(printk(KERN_DEBUG - "%s(%ld): Executing cmd sp %p, prot_op=%u.\n", __func__, - vha->host_no, sp, scsi_get_prot_op(sp->cmd))); - - cmd_pkt->vp_index = sp->fcport->vp_idx; + cmd_pkt->vp_index = sp->fcport->vha->vp_idx; /* Set transfer direction */ if (cmd->sc_data_direction == DMA_TO_DEVICE) { @@ -1023,13 +1295,15 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, __constant_cpu_to_le16(CF_READ_DATA); } - tot_prot_dsds = scsi_prot_sg_count(cmd); - if (!tot_prot_dsds) + 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->ctx = dma_pool_alloc(ha->dl_dma_pool, - GFP_ATOMIC, &crc_ctx_dma); + 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; @@ -1047,7 +1321,7 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, INIT_LIST_HEAD(&crc_ctx_pkt->dsd_list); - qla24xx_set_t10dif_tags(cmd, (struct fw_dif_context *) + 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)); @@ -1056,8 +1330,6 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, /* Determine SCSI command length -- align to 4 byte boundary */ if (cmd->cmd_len > 16) { - DEBUG18(printk(KERN_INFO "%s(): **** SCSI CMD > 16\n", - __func__)); additional_fcpcdb_len = cmd->cmd_len - 16; if ((cmd->cmd_len % 4) != 0) { /* SCSI cmd > 16 bytes must be multiple of 4 */ @@ -1077,8 +1349,7 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, else if (cmd->sc_data_direction == DMA_FROM_DEVICE) fcp_cmnd->additional_cdb_len |= 2; - int_to_scsilun(sp->cmd->device->lun, &fcp_cmnd->lun); - host_to_fcp_swap((uint8_t *)&fcp_cmnd->lun, sizeof(fcp_cmnd->lun)); + 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( @@ -1099,31 +1370,49 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, fcp_cmnd->task_attribute = TSK_ORDERED; break; default: - fcp_cmnd->task_attribute = 0; + fcp_cmnd->task_attribute = TSK_SIMPLE; break; } } else { - fcp_cmnd->task_attribute = 0; + fcp_cmnd->task_attribute = TSK_SIMPLE; } cmd_pkt->fcp_rsp_dseg_len = 0; /* Let response come in status iocb */ - DEBUG18(printk(KERN_INFO "%s(%ld): Total SG(s) Entries %d, Data" - "entries %d, data bytes %d, Protection entries %d\n", - __func__, vha->host_no, tot_dsds, (tot_dsds-tot_prot_dsds), - data_bytes, tot_prot_dsds)); - /* Compute dif len and adjust data len to incude protection */ - total_bytes = data_bytes; dif_bytes = 0; blk_size = cmd->device->sector_size; - if (scsi_get_prot_op(cmd) != SCSI_PROT_NORMAL) { - dif_bytes = (data_bytes / blk_size) * 8; - total_bytes += dif_bytes; + 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 (!ql2xenablehba_err_chk) + if (!qla2x00_hba_err_chk_enabled(sp)) fw_prot_opts |= 0x10; /* Disable Guard tag checking */ + /* HBA error checking enabled */ + else if (IS_PI_UNINIT_CAPABLE(ha)) { + if ((scsi_get_prot_type(GET_CMD_SP(sp)) == SCSI_PROT_DIF_TYPE1) + || (scsi_get_prot_type(GET_CMD_SP(sp)) == + SCSI_PROT_DIF_TYPE2)) + fw_prot_opts |= BIT_10; + else if (scsi_get_prot_type(GET_CMD_SP(sp)) == + SCSI_PROT_DIF_TYPE3) + fw_prot_opts |= BIT_11; + } if (!bundling) { cur_dsd = (uint32_t *) &crc_ctx_pkt->u.nobundling.data_address; @@ -1150,14 +1439,7 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, additional_fcpcdb_len); *fcp_dl = htonl(total_bytes); - DEBUG18(printk(KERN_INFO "%s(%ld): dif bytes = 0x%x (%d), total bytes" - " = 0x%x (%d), dat block size =0x%x (%d)\n", __func__, - vha->host_no, dif_bytes, dif_bytes, total_bytes, total_bytes, - crc_ctx_pkt->blk_size, crc_ctx_pkt->blk_size)); - if (!data_bytes || cmd->sc_data_direction == DMA_NONE) { - DEBUG18(printk(KERN_INFO "%s: Zero data bytes or DMA-NONE %d\n", - __func__, data_bytes)); cmd_pkt->byte_count = __constant_cpu_to_le32(0); return QLA_SUCCESS; } @@ -1165,25 +1447,27 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, cmd_pkt->control_flags |= __constant_cpu_to_le16(CF_DATA_SEG_DESCR_ENABLE); - if (qla24xx_walk_and_build_sglist(ha, sp, cur_dsd, - (tot_dsds - tot_prot_dsds))) + + if (!bundling && tot_prot_dsds) { + if (qla24xx_walk_and_build_sglist_no_difb(ha, sp, + cur_dsd, tot_dsds, NULL)) + goto crc_queuing_error; + } else if (qla24xx_walk_and_build_sglist(ha, sp, cur_dsd, + (tot_dsds - tot_prot_dsds), NULL)) 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)) + tot_prot_dsds, NULL)) goto crc_queuing_error; } return QLA_SUCCESS; crc_queuing_error: - DEBUG18(qla_printk(KERN_INFO, ha, - "CMD sent FAILED crc_q error:sp = %p\n", sp)); /* Cleanup will be performed by the caller */ return QLA_FUNCTION_FAILED; @@ -1209,7 +1493,7 @@ qla24xx_start_scsi(srb_t *sp) uint16_t tot_dsds; struct req_que *req = NULL; struct rsp_que *rsp = NULL; - struct scsi_cmnd *cmd = sp->cmd; + 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]; @@ -1225,8 +1509,8 @@ qla24xx_start_scsi(srb_t *sp) /* Send marker if required */ if (vha->marker_needed != 0) { - if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL) - != QLA_SUCCESS) + if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL) != + QLA_SUCCESS) return QLA_FUNCTION_FAILED; vha->marker_needed = 0; } @@ -1236,14 +1520,14 @@ qla24xx_start_scsi(srb_t *sp) /* Check for room in outstanding command list. */ handle = req->current_outstanding_cmd; - for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) { + for (index = 1; index < req->num_outstanding_cmds; index++) { handle++; - if (handle == MAX_OUTSTANDING_COMMANDS) + if (handle == req->num_outstanding_cmds) handle = 1; if (!req->outstanding_cmds[handle]) break; } - if (index == MAX_OUTSTANDING_COMMANDS) + if (index == req->num_outstanding_cmds) goto queuing_error; /* Map the sg table so we have an accurate count of sg entries needed */ @@ -1256,25 +1540,24 @@ qla24xx_start_scsi(srb_t *sp) nseg = 0; tot_dsds = nseg; - - req_cnt = qla24xx_calc_iocbs(tot_dsds); + req_cnt = qla24xx_calc_iocbs(vha, tot_dsds); if (req->cnt < (req_cnt + 2)) { - cnt = RD_REG_DWORD_RELAXED(req->req_q_out); - + cnt = IS_SHADOW_REG_CAPABLE(ha) ? *req->out_ptr : + 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; } - 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; - sp->cmd->host_scribble = (unsigned char *)(unsigned long)handle; + cmd->host_scribble = (unsigned char *)(unsigned long)handle; req->cnt -= req_cnt; cmd_pkt = (struct cmd_type_7 *)req->ring_ptr; @@ -1291,9 +1574,9 @@ qla24xx_start_scsi(srb_t *sp) 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; + cmd_pkt->vp_index = sp->fcport->vha->vp_idx; - int_to_scsilun(sp->cmd->device->lun, &cmd_pkt->lun); + 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). */ @@ -1305,7 +1588,12 @@ qla24xx_start_scsi(srb_t *sp) case ORDERED_QUEUE_TAG: cmd_pkt->task = TSK_ORDERED; break; + default: + cmd_pkt->task = TSK_SIMPLE; + break; } + } else { + cmd_pkt->task = TSK_SIMPLE; } /* Load SCSI command packet. */ @@ -1322,7 +1610,6 @@ qla24xx_start_scsi(srb_t *sp) /* 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) { @@ -1354,7 +1641,6 @@ queuing_error: return QLA_FUNCTION_FAILED; } - /** * qla24xx_dif_start_scsi() - Send a SCSI command to the ISP * @sp: command to send to the ISP @@ -1376,7 +1662,7 @@ qla24xx_dif_start_scsi(srb_t *sp) uint16_t fw_prot_opts = 0; struct req_que *req = NULL; struct rsp_que *rsp = NULL; - struct scsi_cmnd *cmd = sp->cmd; + 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; @@ -1411,15 +1697,15 @@ qla24xx_dif_start_scsi(srb_t *sp) /* Check for room in outstanding command list. */ handle = req->current_outstanding_cmd; - for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) { + for (index = 1; index < req->num_outstanding_cmds; index++) { handle++; - if (handle == MAX_OUTSTANDING_COMMANDS) + if (handle == req->num_outstanding_cmds) handle = 1; if (!req->outstanding_cmds[handle]) break; } - if (index == MAX_OUTSTANDING_COMMANDS) + if (index == req->num_outstanding_cmds) goto queuing_error; /* Compute number of required data segments */ @@ -1431,6 +1717,22 @@ qla24xx_dif_start_scsi(srb_t *sp) 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; @@ -1445,6 +1747,11 @@ qla24xx_dif_start_scsi(srb_t *sp) 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; } @@ -1454,24 +1761,24 @@ qla24xx_dif_start_scsi(srb_t *sp) tot_prot_dsds = nseg; tot_dsds += nseg; if (req->cnt < (req_cnt + 2)) { - cnt = RD_REG_DWORD_RELAXED(req->req_q_out); - + cnt = IS_SHADOW_REG_CAPABLE(ha) ? *req->out_ptr : + 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; } - 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->cmd->host_scribble = (unsigned char *)(unsigned long)handle; + sp->handle = handle; + cmd->host_scribble = (unsigned char *)(unsigned long)handle; req->cnt -= req_cnt; /* Fill-in common area */ @@ -1487,7 +1794,7 @@ qla24xx_dif_start_scsi(srb_t *sp) 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(sp->cmd->device->lun, &cmd_pkt->lun); + 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) */ @@ -1534,16 +1841,13 @@ queuing_error: /* Cleanup will be performed by the caller (queuecommand) */ spin_unlock_irqrestore(&ha->hardware_lock, flags); - - DEBUG18(qla_printk(KERN_INFO, ha, - "CMD sent FAILED SCSI prot_op:%02x\n", scsi_get_prot_op(cmd))); return QLA_FUNCTION_FAILED; } static void qla25xx_set_que(srb_t *sp, struct rsp_que **rsp) { - struct scsi_cmnd *cmd = sp->cmd; + struct scsi_cmnd *cmd = GET_CMD_SP(sp); struct qla_hw_data *ha = sp->fcport->vha->hw; int affinity = cmd->request->cpu; @@ -1574,30 +1878,39 @@ qla2x00_alloc_iocbs(scsi_qla_host_t *vha, srb_t *sp) /* Check for room in outstanding command list. */ handle = req->current_outstanding_cmd; - for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) { + for (index = 1; index < req->num_outstanding_cmds; index++) { handle++; - if (handle == MAX_OUTSTANDING_COMMANDS) + if (handle == req->num_outstanding_cmds) handle = 1; if (!req->outstanding_cmds[handle]) break; } - if (index == MAX_OUTSTANDING_COMMANDS) + if (index == req->num_outstanding_cmds) { + ql_log(ql_log_warn, vha, 0x700b, + "No room on outstanding 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) + if (ha->mqenable || IS_QLA83XX(ha) || IS_QLA27XX(ha)) cnt = RD_REG_DWORD(®->isp25mq.req_q_out); - else if (IS_QLA82XX(ha)) + else if (IS_P3P_TYPE(ha)) cnt = RD_REG_DWORD(®->isp82.req_q_out); else if (IS_FWI2_CAPABLE(ha)) cnt = RD_REG_DWORD(®->isp24.req_q_out); + else if (IS_QLAFX00(ha)) + cnt = RD_REG_DWORD(®->ispfx00.req_q_out); else cnt = qla2x00_debounce_register( ISP_REQ_Q_OUT(ha, ®->isp)); @@ -1615,54 +1928,22 @@ skip_cmd_array: req->cnt -= req_cnt; pkt = req->ring_ptr; memset(pkt, 0, REQUEST_ENTRY_SIZE); - pkt->entry_count = req_cnt; - pkt->handle = handle; + if (IS_QLAFX00(ha)) { + WRT_REG_BYTE((void __iomem *)&pkt->entry_count, req_cnt); + WRT_REG_WORD((void __iomem *)&pkt->handle, handle); + } else { + pkt->entry_count = req_cnt; + pkt->handle = handle; + } queuing_error: return pkt; } static void -qla2x00_start_iocbs(srb_t *sp) -{ - struct qla_hw_data *ha = sp->fcport->vha->hw; - struct req_que *req = ha->req_q_map[0]; - device_reg_t __iomem *reg = ISP_QUE_REG(ha, req->id); - struct device_reg_2xxx __iomem *ioreg = &ha->iobase->isp; - - if (IS_QLA82XX(ha)) { - qla82xx_start_iocbs(sp); - } 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) { - WRT_REG_DWORD(®->isp25mq.req_q_in, req->ring_index); - RD_REG_DWORD(&ioreg->hccr); - } else if (IS_QLA82XX(ha)) { - qla82xx_start_iocbs(sp); - } else if (IS_FWI2_CAPABLE(ha)) { - WRT_REG_DWORD(®->isp24.req_q_in, req->ring_index); - RD_REG_DWORD_RELAXED(®->isp24.req_q_in); - } else { - WRT_REG_WORD(ISP_REQ_Q_IN(ha, ®->isp), - req->ring_index); - RD_REG_WORD_RELAXED(ISP_REQ_Q_IN(ha, ®->isp)); - } - } -} - -static void qla24xx_login_iocb(srb_t *sp, struct logio_entry_24xx *logio) { - struct srb_ctx *ctx = sp->ctx; - struct srb_iocb *lio = ctx->u.iocb_cmd; + struct srb_iocb *lio = &sp->u.iocb_cmd; logio->entry_type = LOGINOUT_PORT_IOCB_TYPE; logio->control_flags = cpu_to_le16(LCF_COMMAND_PLOGI); @@ -1674,15 +1955,14 @@ qla24xx_login_iocb(srb_t *sp, struct logio_entry_24xx *logio) 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; + logio->vp_index = sp->fcport->vha->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_ctx *ctx = sp->ctx; - struct srb_iocb *lio = ctx->u.iocb_cmd; + struct srb_iocb *lio = &sp->u.iocb_cmd; uint16_t opts; mbx->entry_type = MBX_IOCB_TYPE; @@ -1699,7 +1979,7 @@ qla2x00_login_iocb(srb_t *sp, struct mbx_entry *mbx) 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); + mbx->mb9 = cpu_to_le16(sp->fcport->vha->vp_idx); } static void @@ -1712,7 +1992,7 @@ qla24xx_logout_iocb(srb_t *sp, struct logio_entry_24xx *logio) 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; + logio->vp_index = sp->fcport->vha->vp_idx; } static void @@ -1729,7 +2009,7 @@ qla2x00_logout_iocb(srb_t *sp, struct mbx_entry *mbx) 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); + mbx->mb9 = cpu_to_le16(sp->fcport->vha->vp_idx); /* Implicit: mbx->mbx10 = 0. */ } @@ -1739,7 +2019,7 @@ 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; + logio->vp_index = sp->fcport->vha->vp_idx; } static void @@ -1760,7 +2040,7 @@ qla2x00_adisc_iocb(srb_t *sp, struct mbx_entry *mbx) 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); + mbx->mb9 = cpu_to_le16(sp->fcport->vha->vp_idx); } static void @@ -1771,8 +2051,7 @@ qla24xx_tm_iocb(srb_t *sp, struct tsk_mgmt_entry *tsk) struct fc_port *fcport = sp->fcport; scsi_qla_host_t *vha = fcport->vha; struct qla_hw_data *ha = vha->hw; - struct srb_ctx *ctx = sp->ctx; - struct srb_iocb *iocb = ctx->u.iocb_cmd; + struct srb_iocb *iocb = &sp->u.iocb_cmd; struct req_que *req = vha->req; flags = iocb->u.tmf.flags; @@ -1787,7 +2066,7 @@ qla24xx_tm_iocb(srb_t *sp, struct tsk_mgmt_entry *tsk) 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; + tsk->vp_index = fcport->vha->vp_idx; if (flags == TCF_LUN_RESET) { int_to_scsilun(lun, &tsk->lun); @@ -1799,7 +2078,7 @@ qla24xx_tm_iocb(srb_t *sp, struct tsk_mgmt_entry *tsk) static void qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb) { - struct fc_bsg_job *bsg_job = ((struct srb_ctx *)sp->ctx)->u.bsg_job; + struct fc_bsg_job *bsg_job = sp->u.bsg_job; els_iocb->entry_type = ELS_IOCB_TYPE; els_iocb->entry_count = 1; @@ -1808,12 +2087,12 @@ qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb) 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->vp_index = sp->fcport->vha->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 = - (((struct srb_ctx *)sp->ctx)->type == SRB_ELS_CMD_RPT) ? + 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; @@ -1838,6 +2117,8 @@ qla24xx_els_iocb(srb_t *sp, struct els_entry_24xx *els_iocb) (bsg_job->reply_payload.sg_list))); els_iocb->rx_len = cpu_to_le32(sg_dma_len (bsg_job->reply_payload.sg_list)); + + sp->fcport->vha->qla_stats.control_requests++; } static void @@ -1850,7 +2131,7 @@ qla2x00_ct_iocb(srb_t *sp, ms_iocb_entry_t *ct_iocb) 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 = ((struct srb_ctx *)sp->ctx)->u.bsg_job; + struct fc_bsg_job *bsg_job = sp->u.bsg_job; int loop_iterartion = 0; int cont_iocb_prsnt = 0; int entry_count = 1; @@ -1899,7 +2180,8 @@ qla2x00_ct_iocb(srb_t *sp, ms_iocb_entry_t *ct_iocb) * Five DSDs are available in the Cont. * Type 1 IOCB. */ - cont_pkt = qla2x00_prep_cont_type1_iocb(vha); + 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; @@ -1914,6 +2196,8 @@ qla2x00_ct_iocb(srb_t *sp, ms_iocb_entry_t *ct_iocb) avail_dsds--; } ct_iocb->entry_count = entry_count; + + sp->fcport->vha->qla_stats.control_requests++; } static void @@ -1925,7 +2209,8 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb) int index; uint16_t tot_dsds; scsi_qla_host_t *vha = sp->fcport->vha; - struct fc_bsg_job *bsg_job = ((struct srb_ctx *)sp->ctx)->u.bsg_job; + 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; @@ -1936,7 +2221,7 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb) 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->vp_index = sp->fcport->vha->vp_idx; ct_iocb->comp_status = __constant_cpu_to_le16(0); ct_iocb->cmd_dsd_count = @@ -1970,7 +2255,8 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb) * Five DSDs are available in the Cont. * Type 1 IOCB. */ - cont_pkt = qla2x00_prep_cont_type1_iocb(vha); + 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; @@ -1987,23 +2273,424 @@ qla24xx_ct_iocb(srb_t *sp, struct ct_entry_24xx *ct_iocb) 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 < req->num_outstanding_cmds; index++) { + handle++; + if (handle == req->num_outstanding_cmds) + handle = 1; + if (!req->outstanding_cmds[handle]) + break; + } + if (index == req->num_outstanding_cmds) + 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( + ®->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; + } + + /* 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->vha->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( + ®->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->vha->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((void __iomem *)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; +} + +void +qla24xx_abort_iocb(srb_t *sp, struct abort_entry_24xx *abt_iocb) +{ + struct srb_iocb *aio = &sp->u.iocb_cmd; + scsi_qla_host_t *vha = sp->fcport->vha; + struct req_que *req = vha->req; + + memset(abt_iocb, 0, sizeof(struct abort_entry_24xx)); + abt_iocb->entry_type = ABORT_IOCB_TYPE; + abt_iocb->entry_count = 1; + abt_iocb->handle = cpu_to_le32(MAKE_HANDLE(req->id, sp->handle)); + abt_iocb->nport_handle = cpu_to_le16(sp->fcport->loop_id); + abt_iocb->handle_to_abort = + cpu_to_le32(MAKE_HANDLE(req->id, aio->u.abt.cmd_hndl)); + abt_iocb->port_id[0] = sp->fcport->d_id.b.al_pa; + abt_iocb->port_id[1] = sp->fcport->d_id.b.area; + abt_iocb->port_id[2] = sp->fcport->d_id.b.domain; + abt_iocb->vp_index = vha->vp_idx; + abt_iocb->req_que_no = cpu_to_le16(req->id); + /* Send the command to the firmware */ + wmb(); +} + int qla2x00_start_sp(srb_t *sp) { int rval; struct qla_hw_data *ha = sp->fcport->vha->hw; void *pkt; - struct srb_ctx *ctx = sp->ctx; unsigned long flags; rval = QLA_FUNCTION_FAILED; spin_lock_irqsave(&ha->hardware_lock, flags); pkt = qla2x00_alloc_iocbs(sp->fcport->vha, sp); - if (!pkt) + if (!pkt) { + ql_log(ql_log_warn, sp->fcport->vha, 0x700c, + "qla2x00_alloc_iocbs failed.\n"); goto done; + } rval = QLA_SUCCESS; - switch (ctx->type) { + switch (sp->type) { case SRB_LOGIN_CMD: IS_FWI2_CAPABLE(ha) ? qla24xx_login_iocb(sp, pkt) : @@ -2020,8 +2707,8 @@ qla2x00_start_sp(srb_t *sp) break; case SRB_CT_CMD: IS_FWI2_CAPABLE(ha) ? - qla24xx_ct_iocb(sp, pkt) : - qla2x00_ct_iocb(sp, pkt); + qla24xx_ct_iocb(sp, pkt) : + qla2x00_ct_iocb(sp, pkt); break; case SRB_ADISC_CMD: IS_FWI2_CAPABLE(ha) ? @@ -2029,15 +2716,218 @@ qla2x00_start_sp(srb_t *sp) qla2x00_adisc_iocb(sp, pkt); break; case SRB_TM_CMD: - qla24xx_tm_iocb(sp, pkt); + IS_QLAFX00(ha) ? + qlafx00_tm_iocb(sp, pkt) : + qla24xx_tm_iocb(sp, pkt); + break; + case SRB_FXIOCB_DCMD: + case SRB_FXIOCB_BCMD: + qlafx00_fxdisc_iocb(sp, pkt); + break; + case SRB_ABT_CMD: + IS_QLAFX00(ha) ? + qlafx00_abort_iocb(sp, pkt) : + qla24xx_abort_iocb(sp, pkt); break; default: break; } wmb(); - qla2x00_start_iocbs(sp); + qla2x00_start_iocbs(sp->fcport->vha, ha->req_q_map[0]); done: spin_unlock_irqrestore(&ha->hardware_lock, flags); return rval; } + +static void +qla25xx_build_bidir_iocb(srb_t *sp, struct scsi_qla_host *vha, + struct cmd_bidir *cmd_pkt, uint32_t tot_dsds) +{ + uint16_t avail_dsds; + uint32_t *cur_dsd; + uint32_t req_data_len = 0; + uint32_t rsp_data_len = 0; + struct scatterlist *sg; + int index; + int entry_count = 1; + struct fc_bsg_job *bsg_job = sp->u.bsg_job; + + /*Update entry type to indicate bidir command */ + *((uint32_t *)(&cmd_pkt->entry_type)) = + __constant_cpu_to_le32(COMMAND_BIDIRECTIONAL); + + /* Set the transfer direction, in this set both flags + * Also set the BD_WRAP_BACK flag, firmware will take care + * assigning DID=SID for outgoing pkts. + */ + cmd_pkt->wr_dseg_count = cpu_to_le16(bsg_job->request_payload.sg_cnt); + cmd_pkt->rd_dseg_count = cpu_to_le16(bsg_job->reply_payload.sg_cnt); + cmd_pkt->control_flags = + __constant_cpu_to_le16(BD_WRITE_DATA | BD_READ_DATA | + BD_WRAP_BACK); + + req_data_len = rsp_data_len = bsg_job->request_payload.payload_len; + cmd_pkt->wr_byte_count = cpu_to_le32(req_data_len); + cmd_pkt->rd_byte_count = cpu_to_le32(rsp_data_len); + cmd_pkt->timeout = cpu_to_le16(qla2x00_get_async_timeout(vha) + 2); + + vha->bidi_stats.transfer_bytes += req_data_len; + vha->bidi_stats.io_count++; + + vha->qla_stats.output_bytes += req_data_len; + vha->qla_stats.output_requests++; + + /* Only one dsd is available for bidirectional IOCB, remaining dsds + * are bundled in continuation iocb + */ + avail_dsds = 1; + cur_dsd = (uint32_t *)&cmd_pkt->fcp_data_dseg_address; + + index = 0; + + for_each_sg(bsg_job->request_payload.sg_list, sg, + bsg_job->request_payload.sg_cnt, index) { + dma_addr_t sle_dma; + cont_a64_entry_t *cont_pkt; + + /* Allocate additional continuation packets */ + if (avail_dsds == 0) { + /* Continuation type 1 IOCB can accomodate + * 5 DSDS + */ + cont_pkt = qla2x00_prep_cont_type1_iocb(vha, vha->req); + cur_dsd = (uint32_t *) cont_pkt->dseg_0_address; + avail_dsds = 5; + 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)); + avail_dsds--; + } + /* For read request DSD will always goes to continuation IOCB + * and follow the write DSD. If there is room on the current IOCB + * then it is added to that IOCB else new continuation IOCB is + * allocated. + */ + for_each_sg(bsg_job->reply_payload.sg_list, sg, + bsg_job->reply_payload.sg_cnt, index) { + dma_addr_t sle_dma; + cont_a64_entry_t *cont_pkt; + + /* Allocate additional continuation packets */ + if (avail_dsds == 0) { + /* Continuation type 1 IOCB can accomodate + * 5 DSDS + */ + cont_pkt = qla2x00_prep_cont_type1_iocb(vha, vha->req); + cur_dsd = (uint32_t *) cont_pkt->dseg_0_address; + avail_dsds = 5; + 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)); + avail_dsds--; + } + /* This value should be same as number of IOCB required for this cmd */ + cmd_pkt->entry_count = entry_count; +} + +int +qla2x00_start_bidir(srb_t *sp, struct scsi_qla_host *vha, uint32_t tot_dsds) +{ + + struct qla_hw_data *ha = vha->hw; + unsigned long flags; + uint32_t handle; + uint32_t index; + uint16_t req_cnt; + uint16_t cnt; + uint32_t *clr_ptr; + struct cmd_bidir *cmd_pkt = NULL; + struct rsp_que *rsp; + struct req_que *req; + int rval = EXT_STATUS_OK; + + rval = QLA_SUCCESS; + + rsp = ha->rsp_q_map[0]; + req = vha->req; + + /* Send marker if required */ + if (vha->marker_needed != 0) { + if (qla2x00_marker(vha, req, + rsp, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS) + return EXT_STATUS_MAILBOX; + 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 < req->num_outstanding_cmds; index++) { + handle++; + if (handle == req->num_outstanding_cmds) + handle = 1; + if (!req->outstanding_cmds[handle]) + break; + } + + if (index == req->num_outstanding_cmds) { + rval = EXT_STATUS_BUSY; + goto queuing_error; + } + + /* Calculate number of IOCB required */ + req_cnt = qla24xx_calc_iocbs(vha, tot_dsds); + + /* Check for room on request queue. */ + if (req->cnt < req_cnt + 2) { + cnt = IS_SHADOW_REG_CAPABLE(ha) ? *req->out_ptr : + 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) { + rval = EXT_STATUS_BUSY; + goto queuing_error; + } + + cmd_pkt = (struct cmd_bidir *)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); + + /* Set NPORT-ID (of vha)*/ + cmd_pkt->nport_handle = cpu_to_le16(vha->self_login_loop_id); + cmd_pkt->port_id[0] = vha->d_id.b.al_pa; + cmd_pkt->port_id[1] = vha->d_id.b.area; + cmd_pkt->port_id[2] = vha->d_id.b.domain; + + qla25xx_build_bidir_iocb(sp, vha, cmd_pkt, tot_dsds); + cmd_pkt->entry_status = (uint8_t) rsp->id; + /* Build command packet. */ + req->current_outstanding_cmd = handle; + req->outstanding_cmds[handle] = sp; + sp->handle = handle; + req->cnt -= req_cnt; + + /* Send the command to the firmware */ + wmb(); + qla2x00_start_iocbs(vha, req); +queuing_error: + 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 index 1b60a95adb5..a56825c73c3 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -1,10 +1,11 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ #include "qla_def.h" +#include "qla_target.h" #include <linux/delay.h> #include <linux/slab.h> @@ -13,8 +14,6 @@ #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 *, @@ -44,8 +43,8 @@ qla2100_intr_handler(int irq, void *dev_id) rsp = (struct rsp_que *) dev_id; if (!rsp) { - printk(KERN_INFO - "%s(): NULL response queue pointer\n", __func__); + ql_log(ql_log_info, NULL, 0x505d, + "%s: NULL response queue pointer.\n", __func__); return (IRQ_NONE); } @@ -57,13 +56,23 @@ qla2100_intr_handler(int irq, void *dev_id) vha = pci_get_drvdata(ha->pdev); for (iter = 50; iter--; ) { hccr = RD_REG_WORD(®->hccr); + /* Check for PCI disconnection */ + if (hccr == 0xffff) { + /* + * Schedule this on the default system workqueue so that + * all the adapter workqueues and the DPC thread can be + * shutdown cleanly. + */ + schedule_work(&ha->board_disable); + break; + } 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 hammmer to get + * bit to be cleared. Schedule a big hammer to get * out of the RISC PAUSED state. */ WRT_REG_WORD(®->hccr, HCCR_RESET_RISC); @@ -91,9 +100,9 @@ qla2100_intr_handler(int irq, void *dev_id) qla2x00_async_event(vha, rsp, mb); } else { /*EMPTY*/ - DEBUG2(printk("scsi(%ld): Unrecognized " - "interrupt type (%d).\n", - vha->host_no, mb[0])); + ql_dbg(ql_dbg_async, vha, 0x5025, + "Unrecognized interrupt type (%d).\n", + mb[0]); } /* Release mailbox registers. */ WRT_REG_WORD(®->semaphore, 0); @@ -105,17 +114,28 @@ qla2100_intr_handler(int irq, void *dev_id) RD_REG_WORD(®->hccr); } } + qla2x00_handle_mbx_completion(ha, status); 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); } +bool +qla2x00_check_reg_for_disconnect(scsi_qla_host_t *vha, uint32_t reg) +{ + /* Check for PCI disconnection */ + if (reg == 0xffffffff) { + /* + * Schedule this on the default system workqueue so that all the + * adapter workqueues and the DPC thread can be shutdown + * cleanly. + */ + schedule_work(&vha->hw->board_disable); + return true; + } else + return false; +} + /** * qla2300_intr_handler() - Process interrupts for the ISP23xx and ISP63xx. * @irq: @@ -141,8 +161,8 @@ qla2300_intr_handler(int irq, void *dev_id) rsp = (struct rsp_que *) dev_id; if (!rsp) { - printk(KERN_INFO - "%s(): NULL response queue pointer\n", __func__); + ql_log(ql_log_info, NULL, 0x5058, + "%s: NULL response queue pointer.\n", __func__); return (IRQ_NONE); } @@ -154,22 +174,27 @@ qla2300_intr_handler(int irq, void *dev_id) vha = pci_get_drvdata(ha->pdev); for (iter = 50; iter--; ) { stat = RD_REG_DWORD(®->u.isp2300.host_status); + if (qla2x00_check_reg_for_disconnect(vha, stat)) + break; if (stat & HSR_RISC_PAUSED) { if (unlikely(pci_channel_offline(ha->pdev))) break; hccr = RD_REG_WORD(®->hccr); + if (hccr & (BIT_15 | BIT_13 | BIT_11 | BIT_8)) - qla_printk(KERN_INFO, ha, "Parity error -- " - "HCCR=%x, Dumping firmware!\n", hccr); + ql_log(ql_log_warn, vha, 0x5026, + "Parity error -- HCCR=%x, Dumping " + "firmware.\n", hccr); else - qla_printk(KERN_INFO, ha, "RISC paused -- " - "HCCR=%x, Dumping firmware!\n", hccr); + 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 - * hammmer to get out of the RISC PAUSED state. + * hammer to get out of the RISC PAUSED state. */ WRT_REG_WORD(®->hccr, HCCR_RESET_RISC); RD_REG_WORD(®->hccr); @@ -213,22 +238,16 @@ qla2300_intr_handler(int irq, void *dev_id) qla2x00_async_event(vha, rsp, mb); break; default: - DEBUG2(printk("scsi(%ld): Unrecognized interrupt type " - "(%d).\n", - vha->host_no, stat & 0xff)); + ql_dbg(ql_dbg_async, vha, 0x5028, + "Unrecognized interrupt type (%d).\n", stat & 0xff); break; } WRT_REG_WORD(®->hccr, HCCR_CLR_RISC_INT); RD_REG_WORD_RELAXED(®->hccr); } + qla2x00_handle_mbx_completion(ha, status); 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); } @@ -241,32 +260,34 @@ 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 ERROR.\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) + if ((cnt == 4 || cnt == 5) && (mboxes & BIT_0)) ha->mailbox_out[cnt] = qla2x00_debounce_register(wptr); - else + else if (mboxes & BIT_0) ha->mailbox_out[cnt] = RD_REG_WORD(wptr); wptr++; - } - - if (ha->mcp) { - DEBUG3(printk("%s(%ld): Got mailbox completion. cmd=%x.\n", - __func__, vha->host_no, ha->mcp->mb[0])); - } else { - DEBUG2_3(printk("%s(%ld): MBX pointer ERROR!\n", - __func__, vha->host_no)); + mboxes >>= 1; } } @@ -277,31 +298,264 @@ qla81xx_idc_event(scsi_qla_host_t *vha, uint16_t aen, uint16_t descr) { "Complete", "Request Notification", "Time Extension" }; int rval; struct device_reg_24xx __iomem *reg24 = &vha->hw->iobase->isp24; + struct device_reg_82xx __iomem *reg82 = &vha->hw->iobase->isp82; uint16_t __iomem *wptr; uint16_t cnt, timeout, mb[QLA_IDC_ACK_REGS]; /* Seed data -- mailbox1 -> mailbox7. */ - wptr = (uint16_t __iomem *)®24->mailbox1; + if (IS_QLA81XX(vha->hw) || IS_QLA83XX(vha->hw)) + wptr = (uint16_t __iomem *)®24->mailbox1; + else if (IS_QLA8044(vha->hw)) + wptr = (uint16_t __iomem *)®82->mailbox_out[1]; + else + return; + for (cnt = 0; cnt < QLA_IDC_ACK_REGS; cnt++, wptr++) mb[cnt] = RD_REG_WORD(wptr); - DEBUG2(printk("scsi(%ld): Inter-Driver Commucation %s -- " - "%04x %04x %04x %04x %04x %04x %04x.\n", vha->host_no, - event[aen & 0xff], - mb[0], mb[1], mb[2], mb[3], mb[4], mb[5], mb[6])); + 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]); + switch (aen) { + /* Handle IDC Error completion case. */ + case MBA_IDC_COMPLETE: + if (mb[1] >> 15) { + vha->hw->flags.idc_compl_status = 1; + if (vha->hw->notify_dcbx_comp && !vha->vp_idx) + complete(&vha->hw->dcbx_comp); + } + break; - /* Acknowledgement needed? [Notify && non-zero timeout]. */ - timeout = (descr >> 8) & 0xf; - if (aen != MBA_IDC_NOTIFY || !timeout) - return; + case MBA_IDC_NOTIFY: + /* Acknowledgement needed? [Notify && non-zero timeout]. */ + timeout = (descr >> 8) & 0xf; + ql_dbg(ql_dbg_async, vha, 0x5022, + "%lu Inter-Driver Communication %s -- ACK timeout=%d.\n", + vha->host_no, event[aen & 0xff], timeout); - DEBUG2(printk("scsi(%ld): Inter-Driver Commucation %s -- " - "ACK timeout=%d.\n", vha->host_no, event[aen & 0xff], timeout)); + if (!timeout) + return; + 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"); + break; + case MBA_IDC_TIME_EXT: + vha->hw->idc_extend_tmo = descr; + ql_dbg(ql_dbg_async, vha, 0x5087, + "%lu Inter-Driver Communication %s -- " + "Extend timeout by=%d.\n", + vha->host_no, event[aen & 0xff], vha->hw->idc_extend_tmo); + break; + } +} - rval = qla2x00_post_idc_ack_work(vha, mb); - if (rval != QLA_SUCCESS) - qla_printk(KERN_WARNING, vha->hw, - "IDC failed to post ACK.\n"); +#define LS_UNKNOWN 2 +const char * +qla2x00_get_link_speed_str(struct qla_hw_data *ha, uint16_t speed) +{ + static const char *const link_speeds[] = { + "1", "2", "?", "4", "8", "16", "32", "10" + }; +#define QLA_LAST_SPEED 7 + + if (IS_QLA2100(ha) || IS_QLA2200(ha)) + return link_speeds[0]; + else if (speed == 0x13) + return link_speeds[QLA_LAST_SPEED]; + else if (speed < QLA_LAST_SPEED) + return link_speeds[speed]; + else + return link_speeds[LS_UNKNOWN]; +} + +static void +qla83xx_handle_8200_aen(scsi_qla_host_t *vha, uint16_t *mb) +{ + struct qla_hw_data *ha = vha->hw; + + /* + * 8200 AEN Interpretation: + * mb[0] = AEN code + * mb[1] = AEN Reason code + * mb[2] = LSW of Peg-Halt Status-1 Register + * mb[6] = MSW of Peg-Halt Status-1 Register + * mb[3] = LSW of Peg-Halt Status-2 register + * mb[7] = MSW of Peg-Halt Status-2 register + * mb[4] = IDC Device-State Register value + * mb[5] = IDC Driver-Presence Register value + */ + ql_dbg(ql_dbg_async, vha, 0x506b, "AEN Code: mb[0] = 0x%x AEN reason: " + "mb[1] = 0x%x PH-status1: mb[2] = 0x%x PH-status1: mb[6] = 0x%x.\n", + mb[0], mb[1], mb[2], mb[6]); + ql_dbg(ql_dbg_async, vha, 0x506c, "PH-status2: mb[3] = 0x%x " + "PH-status2: mb[7] = 0x%x Device-State: mb[4] = 0x%x " + "Drv-Presence: mb[5] = 0x%x.\n", mb[3], mb[7], mb[4], mb[5]); + + if (mb[1] & (IDC_PEG_HALT_STATUS_CHANGE | IDC_NIC_FW_REPORTED_FAILURE | + IDC_HEARTBEAT_FAILURE)) { + ha->flags.nic_core_hung = 1; + ql_log(ql_log_warn, vha, 0x5060, + "83XX: F/W Error Reported: Check if reset required.\n"); + + if (mb[1] & IDC_PEG_HALT_STATUS_CHANGE) { + uint32_t protocol_engine_id, fw_err_code, err_level; + + /* + * IDC_PEG_HALT_STATUS_CHANGE interpretation: + * - PEG-Halt Status-1 Register: + * (LSW = mb[2], MSW = mb[6]) + * Bits 0-7 = protocol-engine ID + * Bits 8-28 = f/w error code + * Bits 29-31 = Error-level + * Error-level 0x1 = Non-Fatal error + * Error-level 0x2 = Recoverable Fatal error + * Error-level 0x4 = UnRecoverable Fatal error + * - PEG-Halt Status-2 Register: + * (LSW = mb[3], MSW = mb[7]) + */ + protocol_engine_id = (mb[2] & 0xff); + fw_err_code = (((mb[2] & 0xff00) >> 8) | + ((mb[6] & 0x1fff) << 8)); + err_level = ((mb[6] & 0xe000) >> 13); + ql_log(ql_log_warn, vha, 0x5061, "PegHalt Status-1 " + "Register: protocol_engine_id=0x%x " + "fw_err_code=0x%x err_level=0x%x.\n", + protocol_engine_id, fw_err_code, err_level); + ql_log(ql_log_warn, vha, 0x5062, "PegHalt Status-2 " + "Register: 0x%x%x.\n", mb[7], mb[3]); + if (err_level == ERR_LEVEL_NON_FATAL) { + ql_log(ql_log_warn, vha, 0x5063, + "Not a fatal error, f/w has recovered " + "iteself.\n"); + } else if (err_level == ERR_LEVEL_RECOVERABLE_FATAL) { + ql_log(ql_log_fatal, vha, 0x5064, + "Recoverable Fatal error: Chip reset " + "required.\n"); + qla83xx_schedule_work(vha, + QLA83XX_NIC_CORE_RESET); + } else if (err_level == ERR_LEVEL_UNRECOVERABLE_FATAL) { + ql_log(ql_log_fatal, vha, 0x5065, + "Unrecoverable Fatal error: Set FAILED " + "state, reboot required.\n"); + qla83xx_schedule_work(vha, + QLA83XX_NIC_CORE_UNRECOVERABLE); + } + } + + if (mb[1] & IDC_NIC_FW_REPORTED_FAILURE) { + uint16_t peg_fw_state, nw_interface_link_up; + uint16_t nw_interface_signal_detect, sfp_status; + uint16_t htbt_counter, htbt_monitor_enable; + uint16_t sfp_additonal_info, sfp_multirate; + uint16_t sfp_tx_fault, link_speed, dcbx_status; + + /* + * IDC_NIC_FW_REPORTED_FAILURE interpretation: + * - PEG-to-FC Status Register: + * (LSW = mb[2], MSW = mb[6]) + * Bits 0-7 = Peg-Firmware state + * Bit 8 = N/W Interface Link-up + * Bit 9 = N/W Interface signal detected + * Bits 10-11 = SFP Status + * SFP Status 0x0 = SFP+ transceiver not expected + * SFP Status 0x1 = SFP+ transceiver not present + * SFP Status 0x2 = SFP+ transceiver invalid + * SFP Status 0x3 = SFP+ transceiver present and + * valid + * Bits 12-14 = Heartbeat Counter + * Bit 15 = Heartbeat Monitor Enable + * Bits 16-17 = SFP Additional Info + * SFP info 0x0 = Unregocnized transceiver for + * Ethernet + * SFP info 0x1 = SFP+ brand validation failed + * SFP info 0x2 = SFP+ speed validation failed + * SFP info 0x3 = SFP+ access error + * Bit 18 = SFP Multirate + * Bit 19 = SFP Tx Fault + * Bits 20-22 = Link Speed + * Bits 23-27 = Reserved + * Bits 28-30 = DCBX Status + * DCBX Status 0x0 = DCBX Disabled + * DCBX Status 0x1 = DCBX Enabled + * DCBX Status 0x2 = DCBX Exchange error + * Bit 31 = Reserved + */ + peg_fw_state = (mb[2] & 0x00ff); + nw_interface_link_up = ((mb[2] & 0x0100) >> 8); + nw_interface_signal_detect = ((mb[2] & 0x0200) >> 9); + sfp_status = ((mb[2] & 0x0c00) >> 10); + htbt_counter = ((mb[2] & 0x7000) >> 12); + htbt_monitor_enable = ((mb[2] & 0x8000) >> 15); + sfp_additonal_info = (mb[6] & 0x0003); + sfp_multirate = ((mb[6] & 0x0004) >> 2); + sfp_tx_fault = ((mb[6] & 0x0008) >> 3); + link_speed = ((mb[6] & 0x0070) >> 4); + dcbx_status = ((mb[6] & 0x7000) >> 12); + + ql_log(ql_log_warn, vha, 0x5066, + "Peg-to-Fc Status Register:\n" + "peg_fw_state=0x%x, nw_interface_link_up=0x%x, " + "nw_interface_signal_detect=0x%x" + "\nsfp_statis=0x%x.\n ", peg_fw_state, + nw_interface_link_up, nw_interface_signal_detect, + sfp_status); + ql_log(ql_log_warn, vha, 0x5067, + "htbt_counter=0x%x, htbt_monitor_enable=0x%x, " + "sfp_additonal_info=0x%x, sfp_multirate=0x%x.\n ", + htbt_counter, htbt_monitor_enable, + sfp_additonal_info, sfp_multirate); + ql_log(ql_log_warn, vha, 0x5068, + "sfp_tx_fault=0x%x, link_state=0x%x, " + "dcbx_status=0x%x.\n", sfp_tx_fault, link_speed, + dcbx_status); + + qla83xx_schedule_work(vha, QLA83XX_NIC_CORE_RESET); + } + + if (mb[1] & IDC_HEARTBEAT_FAILURE) { + ql_log(ql_log_warn, vha, 0x5069, + "Heartbeat Failure encountered, chip reset " + "required.\n"); + + qla83xx_schedule_work(vha, QLA83XX_NIC_CORE_RESET); + } + } + + if (mb[1] & IDC_DEVICE_STATE_CHANGE) { + ql_log(ql_log_info, vha, 0x506a, + "IDC Device-State changed = 0x%x.\n", mb[4]); + if (ha->flags.nic_core_reset_owner) + return; + qla83xx_schedule_work(vha, MBA_IDC_AEN); + } +} + +int +qla2x00_is_a_vp_did(scsi_qla_host_t *vha, uint32_t rscn_entry) +{ + struct qla_hw_data *ha = vha->hw; + scsi_qla_host_t *vp; + uint32_t vp_did; + unsigned long flags; + int ret = 0; + + if (!ha->num_vhosts) + return ret; + + spin_lock_irqsave(&ha->vport_slock, flags); + list_for_each_entry(vp, &ha->vp_list, list) { + vp_did = vp->d_id.b24; + if (vp_did == rscn_entry) { + ret = 1; + break; + } + } + spin_unlock_irqrestore(&ha->vport_slock, flags); + + return ret; } /** @@ -312,9 +566,6 @@ qla81xx_idc_event(scsi_qla_host_t *vha, uint16_t aen, uint16_t descr) 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", "10" }; - char *link_speed; uint16_t handle_cnt; uint16_t cnt, mbx; uint32_t handles[5]; @@ -323,12 +574,11 @@ qla2x00_async_event(scsi_qla_host_t *vha, struct rsp_que *rsp, uint16_t *mb) struct device_reg_24xx __iomem *reg24 = &ha->iobase->isp24; struct device_reg_82xx __iomem *reg82 = &ha->iobase->isp82; uint32_t rscn_entry, host_pid; - uint8_t rscn_queue_index; unsigned long flags; /* Setup to process RIO completion. */ handle_cnt = 0; - if (IS_QLA8XXX_TYPE(ha)) + if (IS_CNA_CAPABLE(ha)) goto skip_rio; switch (mb[0]) { case MBA_SCSI_COMPLETION: @@ -393,15 +643,16 @@ skip_rio: break; case MBA_RESET: /* Reset */ - DEBUG2(printk("scsi(%ld): Asynchronous RESET.\n", - vha->host_no)); + 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) ? RD_REG_WORD(®24->mailbox7) : 0; - qla_printk(KERN_INFO, ha, + mbx = (IS_QLA81XX(ha) || IS_QLA83XX(ha) || IS_QLA27XX(ha)) ? + RD_REG_WORD(®24->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); @@ -409,53 +660,51 @@ skip_rio: if (IS_FWI2_CAPABLE(ha)) { if (mb[1] == 0 && mb[2] == 0) { - qla_printk(KERN_ERR, ha, + 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)) + if ((mbx & MBX_3) && (ha->port_no == 0)) set_bit(MPI_RESET_NEEDED, &vha->dpc_flags); set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); } } else if (mb[1] == 0) { - qla_printk(KERN_INFO, ha, + 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 */ - DEBUG2(printk("scsi(%ld): ISP Request Transfer Error (%x).\n", - vha->host_no, mb[1])); - qla_printk(KERN_WARNING, ha, - "ISP Request Transfer Error (%x).\n", mb[1]); + 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 */ - DEBUG2(printk("scsi(%ld): ISP Response Transfer Error.\n", - vha->host_no)); - qla_printk(KERN_WARNING, ha, "ISP Response Transfer Error.\n"); + 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 */ - DEBUG2(printk("scsi(%ld): Asynchronous WAKEUP_THRES.\n", - vha->host_no)); - break; + ql_dbg(ql_dbg_async, vha, 0x5008, + "Asynchronous WAKEUP_THRES.\n"); + break; case MBA_LIP_OCCURRED: /* Loop Initialization Procedure */ - DEBUG2(printk("scsi(%ld): LIP occurred (%x).\n", vha->host_no, - mb[1])); - qla_printk(KERN_INFO, ha, "LIP occurred (%x).\n", mb[1]); + 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); @@ -476,36 +725,27 @@ skip_rio: break; case MBA_LOOP_UP: /* Loop Up Event */ - if (IS_QLA2100(ha) || IS_QLA2200(ha)) { - link_speed = link_speeds[0]; + if (IS_QLA2100(ha) || IS_QLA2200(ha)) ha->link_data_rate = PORT_SPEED_1GB; - } else { - link_speed = link_speeds[LS_UNKNOWN]; - if (mb[1] < 5) - link_speed = link_speeds[mb[1]]; - else if (mb[1] == 0x13) - link_speed = link_speeds[5]; + else ha->link_data_rate = mb[1]; - } - DEBUG2(printk("scsi(%ld): Asynchronous LOOP UP (%s Gbps).\n", - vha->host_no, link_speed)); - qla_printk(KERN_INFO, ha, "LOOP UP detected (%s Gbps).\n", - link_speed); + ql_dbg(ql_dbg_async, vha, 0x500a, + "LOOP UP detected (%s Gbps).\n", + qla2x00_get_link_speed_str(ha, ha->link_data_rate)); 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) ? RD_REG_WORD(®24->mailbox4) : 0; - mbx = IS_QLA82XX(ha) ? RD_REG_WORD(®82->mailbox_out[4]) : mbx; - DEBUG2(printk("scsi(%ld): Asynchronous LOOP DOWN " - "(%x %x %x %x).\n", vha->host_no, mb[1], mb[2], mb[3], - mbx)); - qla_printk(KERN_INFO, ha, - "LOOP DOWN detected (%x %x %x %x).\n", mb[1], mb[2], mb[3], - mbx); + mbx = (IS_QLA81XX(ha) || IS_QLA8031(ha)) + ? RD_REG_WORD(®24->mailbox4) : 0; + mbx = (IS_P3P_TYPE(ha)) ? RD_REG_WORD(®82->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); @@ -525,9 +765,7 @@ skip_rio: break; case MBA_LIP_RESET: /* LIP reset occurred */ - DEBUG2(printk("scsi(%ld): Asynchronous LIP RESET (%x).\n", - vha->host_no, mb[1])); - qla_printk(KERN_INFO, ha, + ql_dbg(ql_dbg_async, vha, 0x500c, "LIP reset occurred (%x).\n", mb[1]); if (atomic_read(&vha->loop_state) != LOOP_DOWN) { @@ -553,15 +791,16 @@ skip_rio: if (IS_QLA2100(ha)) break; - if (IS_QLA8XXX_TYPE(ha)) { - DEBUG2(printk("scsi(%ld): DCBX Completed -- %04x %04x " - "%04x\n", vha->host_no, mb[1], mb[2], mb[3])); - if (ha->notify_dcbx_comp) + if (IS_CNA_CAPABLE(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 && !vha->vp_idx) complete(&ha->dcbx_comp); } else - DEBUG2(printk("scsi(%ld): Asynchronous P2P MODE " - "received.\n", vha->host_no)); + ql_dbg(ql_dbg_async, vha, 0x500e, + "Asynchronous P2P MODE received.\n"); /* * Until there's a transition from loop down to loop up, treat @@ -594,10 +833,7 @@ skip_rio: if (IS_QLA2100(ha)) break; - DEBUG2(printk("scsi(%ld): Asynchronous Change In Connection " - "received.\n", - vha->host_no)); - qla_printk(KERN_INFO, ha, + ql_dbg(ql_dbg_async, vha, 0x500f, "Configuration change detected: value=%x.\n", mb[1]); if (atomic_read(&vha->loop_state) != LOOP_DOWN) { @@ -640,11 +876,11 @@ skip_rio: /* Global event -- port logout or port unavailable. */ if (mb[1] == 0xffff && mb[2] == 0x7) { - DEBUG2(printk("scsi(%ld): Asynchronous PORT UPDATE.\n", - vha->host_no)); - DEBUG(printk(KERN_INFO - "scsi(%ld): Port unavailable %04x %04x %04x.\n", - vha->host_no, mb[1], mb[2], mb[3])); + ql_dbg(ql_dbg_async, vha, 0x5010, + "Port unavailable %04x %04x %04x.\n", + mb[1], mb[2], mb[3]); + ql_log(ql_log_warn, vha, 0x505e, + "Link is offline.\n"); if (atomic_read(&vha->loop_state) != LOOP_DOWN) { atomic_set(&vha->loop_state, LOOP_DOWN); @@ -672,19 +908,21 @@ skip_rio: * 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) { - DEBUG2(printk("scsi(%ld): Asynchronous PORT UPDATE " - "ignored %04x/%04x/%04x.\n", vha->host_no, mb[1], - mb[2], mb[3])); + if (mb[1] != 0xffff || (mb[2] != 0x6 && mb[2] != 0x4)) { + ql_dbg(ql_dbg_async, vha, 0x5011, + "Asynchronous PORT UPDATE ignored %04x/%04x/%04x.\n", + mb[1], mb[2], mb[3]); + + qlt_async_event(mb[0], vha, mb); break; } - DEBUG2(printk("scsi(%ld): Asynchronous PORT UPDATE.\n", - vha->host_no)); - DEBUG(printk(KERN_INFO - "scsi(%ld): Port database changed %04x %04x %04x.\n", - vha->host_no, mb[1], mb[2], mb[3])); + ql_dbg(ql_dbg_async, vha, 0x5012, + "Port database changed %04x %04x %04x.\n", + mb[1], mb[2], mb[3]); + ql_log(ql_log_warn, vha, 0x505f, + "Link is operational (%s Gbps).\n", + qla2x00_get_link_speed_str(ha, ha->link_data_rate)); /* * Mark all devices as missing so we will login again. @@ -693,10 +931,13 @@ skip_rio: qla2x00_mark_all_devices_lost(vha, 1); - vha->flags.rscn_queue_overflow = 1; + if (vha->vp_idx == 0 && !qla_ini_mode_enabled(vha)) + set_bit(SCR_PENDING, &vha->dpc_flags); set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); set_bit(LOCAL_LOOP_UPDATE, &vha->dpc_flags); + + qlt_async_event(mb[0], vha, mb); break; case MBA_RSCN_UPDATE: /* State Change Registration */ @@ -707,36 +948,27 @@ skip_rio: if (ha->flags.npiv_supported && vha->vp_idx != (mb[3] & 0xff)) break; - DEBUG2(printk("scsi(%ld): Asynchronous RSCR UPDATE.\n", - vha->host_no)); - DEBUG(printk(KERN_INFO - "scsi(%ld): RSCN database changed -- %04x %04x %04x.\n", - vha->host_no, mb[1], mb[2], mb[3])); + 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) { - DEBUG(printk(KERN_INFO - "scsi(%ld): Ignoring RSCN update to local host " - "port ID (%06x)\n", - vha->host_no, 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]; - rscn_queue_index = vha->rscn_in_ptr + 1; - if (rscn_queue_index == MAX_RSCN_COUNT) - rscn_queue_index = 0; - if (rscn_queue_index != vha->rscn_out_ptr) { - vha->rscn_queue[vha->rscn_in_ptr] = rscn_entry; - vha->rscn_in_ptr = rscn_queue_index; - } else { - vha->flags.rscn_queue_overflow = 1; - } - atomic_set(&vha->loop_state, LOOP_UPDATE); + /* Skip RSCNs for virtual ports on the same physical port */ + if (qla2x00_is_a_vp_did(vha, rscn_entry)) + break; + atomic_set(&vha->loop_down_timer, 0); vha->flags.management_server_logged_in = 0; @@ -747,8 +979,8 @@ skip_rio: /* case MBA_RIO_RESPONSE: */ case MBA_ZIO_RESPONSE: - DEBUG3(printk("scsi(%ld): [R|Z]IO update completion.\n", - vha->host_no)); + ql_dbg(ql_dbg_async, vha, 0x5015, + "[R|Z]IO update completion.\n"); if (IS_FWI2_CAPABLE(ha)) qla24xx_process_response_queue(vha, rsp); @@ -757,69 +989,111 @@ skip_rio: break; case MBA_DISCARD_RND_FRAME: - DEBUG2(printk("scsi(%ld): Discard RND Frame -- %04x %04x " - "%04x.\n", vha->host_no, mb[1], mb[2], mb[3])); + 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: - DEBUG2(printk("scsi(%ld): Trace Notification -- %04x %04x.\n", - vha->host_no, mb[1], mb[2])); + ql_dbg(ql_dbg_async, vha, 0x5017, + "Trace Notification -- %04x %04x.\n", mb[1], mb[2]); break; case MBA_ISP84XX_ALERT: - DEBUG2(printk("scsi(%ld): ISP84XX Alert Notification -- " - "%04x %04x %04x\n", vha->host_no, mb[1], mb[2], mb[3])); + 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: - qla_printk(KERN_INFO, ha, "Alert 84XX: panic recovery " - "%04x %04x\n", mb[2], mb[3]); + 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]; - DEBUG2(qla_printk(KERN_INFO, ha, "Alert 84XX:" - "firmware version %x\n", ha->cs84xx->op_fw_version)); + 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]; - DEBUG2(qla_printk(KERN_INFO, ha, "Alert 84XX:" - "diagnostic firmware version %x\n", - ha->cs84xx->diag_fw_version)); + 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; - DEBUG2(qla_printk(KERN_INFO, ha, "Alert 84XX: gold " - "firmware version %x\n", - ha->cs84xx->gold_fw_version)); + ql_log(ql_log_info, vha, 0x501c, + "Alert 84XX: gold firmware version %x.\n", + ha->cs84xx->gold_fw_version); break; default: - qla_printk(KERN_ERR, ha, - "Alert 84xx: Invalid Alert %04x %04x %04x\n", + 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: - DEBUG2(printk("scsi(%ld): DCBX Started -- %04x %04x %04x\n", - vha->host_no, mb[1], mb[2], mb[3])); + 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: - DEBUG2(printk("scsi(%ld): DCBX Parameters Updated -- " - "%04x %04x %04x\n", vha->host_no, mb[1], mb[2], mb[3])); + 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: - DEBUG2(printk("scsi(%ld): FCF Configuration Error -- " - "%04x %04x %04x\n", vha->host_no, mb[1], mb[2], mb[3])); + 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: + if (IS_QLA8031(vha->hw) || IS_QLA8044(ha)) { + mb[4] = RD_REG_WORD(®24->mailbox4); + if (((mb[2] & 0x7fff) == MBC_PORT_RESET || + (mb[2] & 0x7fff) == MBC_SET_PORT_CONFIG) && + (mb[4] & INTERNAL_LOOPBACK_MASK) != 0) { + set_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags); + /* + * Extend loop down timer since port is active. + */ + if (atomic_read(&vha->loop_state) == LOOP_DOWN) + atomic_set(&vha->loop_down_timer, + LOOP_DOWN_TIME); + qla2xxx_wake_dpc(vha); + } + } + case MBA_IDC_COMPLETE: + if (ha->notify_lb_portup_comp && !vha->vp_idx) + complete(&ha->lb_portup_comp); + /* Fallthru */ case MBA_IDC_TIME_EXT: - qla81xx_idc_event(vha, mb[0], mb[1]); + if (IS_QLA81XX(vha->hw) || IS_QLA8031(vha->hw) || + IS_QLA8044(ha)) + qla81xx_idc_event(vha, mb[0], mb[1]); + break; + + case MBA_IDC_AEN: + mb[4] = RD_REG_WORD(®24->mailbox4); + mb[5] = RD_REG_WORD(®24->mailbox5); + mb[6] = RD_REG_WORD(®24->mailbox6); + mb[7] = RD_REG_WORD(®24->mailbox7); + qla83xx_handle_8200_aen(vha, mb); break; + + default: + ql_dbg(ql_dbg_async, vha, 0x5057, + "Unknown AEN:%04x %04x %04x %04x\n", + mb[0], mb[1], mb[2], mb[3]); } + qlt_async_event(mb[0], vha, mb); + if (!vha->vp_idx && ha->num_vhosts) qla2x00_alert_all_vps(rsp, mb); } @@ -829,21 +1103,19 @@ skip_rio: * @ha: SCSI driver HA context * @index: SRB index */ -static void +void qla2x00_process_completed_request(struct scsi_qla_host *vha, - struct req_que *req, uint32_t index) + struct req_que *req, uint32_t index) { srb_t *sp; struct qla_hw_data *ha = vha->hw; /* Validate handle. */ - if (index >= MAX_OUTSTANDING_COMMANDS) { - DEBUG2(printk("scsi(%ld): Invalid SCSI completion handle %d.\n", - vha->host_no, index)); - qla_printk(KERN_WARNING, ha, - "Invalid SCSI completion handle %d.\n", index); + if (index >= req->num_outstanding_cmds) { + ql_log(ql_log_warn, vha, 0x3014, + "Invalid SCSI command index (%x).\n", index); - if (IS_QLA82XX(ha)) + if (IS_P3P_TYPE(ha)) set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags); else set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); @@ -856,22 +1128,18 @@ qla2x00_process_completed_request(struct scsi_qla_host *vha, req->outstanding_cmds[index] = NULL; /* Save ISP completion status */ - sp->cmd->result = DID_OK << 16; - qla2x00_sp_compl(ha, sp); + sp->done(ha, sp, DID_OK << 16); } else { - DEBUG2(printk("scsi(%ld) Req:%d: Invalid ISP SCSI completion" - " handle(0x%x)\n", vha->host_no, req->id, index)); - qla_printk(KERN_WARNING, ha, - "Invalid ISP SCSI completion handle\n"); + ql_log(ql_log_warn, vha, 0x3016, "Invalid SCSI SRB.\n"); - if (IS_QLA82XX(ha)) + if (IS_P3P_TYPE(ha)) set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags); else set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); } } -static srb_t * +srb_t * qla2x00_get_sp_from_handle(scsi_qla_host_t *vha, const char *func, struct req_que *req, void *iocb) { @@ -881,10 +1149,10 @@ qla2x00_get_sp_from_handle(scsi_qla_host_t *vha, const char *func, uint16_t index; index = LSW(pkt->handle); - if (index >= MAX_OUTSTANDING_COMMANDS) { - qla_printk(KERN_WARNING, ha, - "%s: Invalid completion handle (%x).\n", func, index); - if (IS_QLA82XX(ha)) + if (index >= req->num_outstanding_cmds) { + ql_log(ql_log_warn, vha, 0x5031, + "Invalid command index (%x).\n", index); + if (IS_P3P_TYPE(ha)) set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags); else set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); @@ -892,15 +1160,13 @@ qla2x00_get_sp_from_handle(scsi_qla_host_t *vha, const char *func, } sp = req->outstanding_cmds[index]; if (!sp) { - qla_printk(KERN_WARNING, ha, - "%s: Invalid completion handle (%x) -- timed-out.\n", func, - index); + ql_log(ql_log_warn, vha, 0x5032, + "Invalid completion handle (%x) -- timed-out.\n", index); return sp; } if (sp->handle != index) { - qla_printk(KERN_WARNING, ha, - "%s: SRB handle (%x) mismatch %x.\n", func, sp->handle, - index); + ql_log(ql_log_warn, vha, 0x5033, + "SRB handle (%x) mismatch %x.\n", sp->handle, index); return NULL; } @@ -919,7 +1185,6 @@ qla2x00_mbx_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, fc_port_t *fcport; srb_t *sp; struct srb_iocb *lio; - struct srb_ctx *ctx; uint16_t *data; uint16_t status; @@ -927,9 +1192,8 @@ qla2x00_mbx_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, if (!sp) return; - ctx = sp->ctx; - lio = ctx->u.iocb_cmd; - type = ctx->name; + lio = &sp->u.iocb_cmd; + type = sp->name; fcport = sp->fcport; data = lio->u.logio.data; @@ -937,35 +1201,34 @@ qla2x00_mbx_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, data[1] = lio->u.logio.flags & SRB_LOGIN_RETRIED ? QLA_LOGIO_LOGIN_RETRIED : 0; if (mbx->entry_status) { - DEBUG2(printk(KERN_WARNING - "scsi(%ld:%x): Async-%s error entry - portid=%02x%02x%02x " + 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", - fcport->vha->host_no, sp->handle, type, + "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))); + le16_to_cpu(mbx->status_flags)); - DEBUG2(qla2x00_dump_buffer((uint8_t *)mbx, sizeof(*mbx))); + 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 && ctx->type == SRB_LOGIN_CMD && + 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) { - DEBUG2(printk(KERN_DEBUG - "scsi(%ld:%x): Async-%s complete - portid=%02x%02x%02x " - "mbx1=%x.\n", - fcport->vha->host_no, sp->handle, type, - fcport->d_id.b.domain, fcport->d_id.b.area, - fcport->d_id.b.al_pa, le16_to_cpu(mbx->mb1))); + 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 (ctx->type == SRB_LOGIN_CMD) { + if (sp->type == SRB_LOGIN_CMD) { fcport->port_type = FCT_TARGET; if (le16_to_cpu(mbx->mb1) & BIT_0) fcport->port_type = FCT_INITIATOR; @@ -987,17 +1250,16 @@ qla2x00_mbx_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, break; } - DEBUG2(printk(KERN_WARNING - "scsi(%ld:%x): Async-%s failed - portid=%02x%02x%02x status=%x " - "mb0=%x mb1=%x mb2=%x mb6=%x mb7=%x.\n", - fcport->vha->host_no, sp->handle, type, 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), + 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))); + le16_to_cpu(mbx->mb7)); logio_done: - lio->done(sp); + sp->done(vha, sp, 0); } static void @@ -1006,30 +1268,18 @@ qla2x00_ct_entry(scsi_qla_host_t *vha, struct req_que *req, { const char func[] = "CT_IOCB"; const char *type; - struct qla_hw_data *ha = vha->hw; srb_t *sp; - struct srb_ctx *sp_bsg; 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; - sp_bsg = sp->ctx; - bsg_job = sp_bsg->u.bsg_job; + bsg_job = sp->u.bsg_job; - type = NULL; - switch (sp_bsg->type) { - case SRB_CT_CMD: - type = "ct pass-through"; - break; - default: - qla_printk(KERN_WARNING, ha, - "%s: Unrecognized SRB: (%p) type=%d.\n", func, sp, - sp_bsg->type); - return; - } + type = "ct pass-through"; comp_status = le16_to_cpu(pkt->comp_status); @@ -1041,43 +1291,32 @@ qla2x00_ct_entry(scsi_qla_host_t *vha, struct req_que *req, if (comp_status != CS_COMPLETE) { if (comp_status == CS_DATA_UNDERRUN) { - bsg_job->reply->result = DID_OK << 16; + res = DID_OK << 16; bsg_job->reply->reply_payload_rcv_len = le16_to_cpu(((sts_entry_t *)pkt)->rsp_info_len); - DEBUG2(qla_printk(KERN_WARNING, ha, - "scsi(%ld): CT pass-through-%s error " + ql_log(ql_log_warn, vha, 0x5048, + "CT pass-through-%s error " "comp_status-status=0x%x total_byte = 0x%x.\n", - vha->host_no, type, comp_status, - bsg_job->reply->reply_payload_rcv_len)); + type, comp_status, + bsg_job->reply->reply_payload_rcv_len); } else { - DEBUG2(qla_printk(KERN_WARNING, ha, - "scsi(%ld): CT pass-through-%s error " - "comp_status-status=0x%x.\n", - vha->host_no, type, comp_status)); - bsg_job->reply->result = DID_ERROR << 16; + 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; } - DEBUG2(qla2x00_dump_buffer((uint8_t *)pkt, sizeof(*pkt))); + ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5035, + (uint8_t *)pkt, sizeof(*pkt)); } else { - bsg_job->reply->result = DID_OK << 16; + res = DID_OK << 16; bsg_job->reply->reply_payload_rcv_len = bsg_job->reply_payload.payload_len; bsg_job->reply_len = 0; } - 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_bsg->type == SRB_ELS_CMD_HST || sp_bsg->type == SRB_CT_CMD) - kfree(sp->fcport); - - kfree(sp->ctx); - mempool_free(sp, ha->srb_mempool); - bsg_job->job_done(bsg_job); + sp->done(vha, sp, res); } static void @@ -1086,22 +1325,20 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req, { const char func[] = "ELS_CT_IOCB"; const char *type; - struct qla_hw_data *ha = vha->hw; srb_t *sp; - struct srb_ctx *sp_bsg; 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; - sp_bsg = sp->ctx; - bsg_job = sp_bsg->u.bsg_job; + bsg_job = sp->u.bsg_job; type = NULL; - switch (sp_bsg->type) { + switch (sp->type) { case SRB_ELS_CMD_RPT: case SRB_ELS_CMD_HST: type = "els"; @@ -1110,9 +1347,8 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req, type = "ct pass-through"; break; default: - qla_printk(KERN_WARNING, ha, - "%s: Unrecognized SRB: (%p) type=%d.\n", func, sp, - sp_bsg->type); + ql_dbg(ql_dbg_user, vha, 0x503e, + "Unrecognized SRB: (%p) type=%d.\n", sp, sp->type); return; } @@ -1128,50 +1364,43 @@ qla24xx_els_ct_entry(scsi_qla_host_t *vha, struct req_que *req, if (comp_status != CS_COMPLETE) { if (comp_status == CS_DATA_UNDERRUN) { - bsg_job->reply->result = DID_OK << 16; + res = DID_OK << 16; bsg_job->reply->reply_payload_rcv_len = - le16_to_cpu(((struct els_sts_entry_24xx*)pkt)->total_byte_count); + le16_to_cpu(((struct els_sts_entry_24xx *)pkt)->total_byte_count); - DEBUG2(qla_printk(KERN_WARNING, ha, - "scsi(%ld:0x%x): ELS-CT pass-through-%s error comp_status-status=0x%x " + 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", - vha->host_no, sp->handle, type, comp_status, fw_status[1], fw_status[2], - le16_to_cpu(((struct els_sts_entry_24xx*)pkt)->total_byte_count))); + 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 { - DEBUG2(qla_printk(KERN_WARNING, ha, - "scsi(%ld:0x%x): ELS-CT pass-through-%s error comp_status-status=0x%x " + 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", - vha->host_no, sp->handle, type, 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))); - bsg_job->reply->result = DID_ERROR << 16; + 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)); } - DEBUG2(qla2x00_dump_buffer((uint8_t *)pkt, sizeof(*pkt))); + ql_dump_buffer(ql_dbg_user + ql_dbg_buffer, vha, 0x5056, + (uint8_t *)pkt, sizeof(*pkt)); } else { - bsg_job->reply->result = DID_OK << 16; + res = DID_OK << 16; bsg_job->reply->reply_payload_rcv_len = bsg_job->reply_payload.payload_len; bsg_job->reply_len = 0; } - 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_bsg->type == SRB_ELS_CMD_HST) || - (sp_bsg->type == SRB_CT_CMD)) - kfree(sp->fcport); - kfree(sp->ctx); - mempool_free(sp, ha->srb_mempool); - bsg_job->job_done(bsg_job); + sp->done(vha, sp, res); } static void @@ -1183,7 +1412,6 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, fc_port_t *fcport; srb_t *sp; struct srb_iocb *lio; - struct srb_ctx *ctx; uint16_t *data; uint32_t iop[2]; @@ -1191,9 +1419,8 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, if (!sp) return; - ctx = sp->ctx; - lio = ctx->u.iocb_cmd; - type = ctx->name; + lio = &sp->u.iocb_cmd; + type = sp->name; fcport = sp->fcport; data = lio->u.logio.data; @@ -1201,28 +1428,27 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, data[1] = lio->u.logio.flags & SRB_LOGIN_RETRIED ? QLA_LOGIO_LOGIN_RETRIED : 0; if (logio->entry_status) { - DEBUG2(printk(KERN_WARNING - "scsi(%ld:%x): Async-%s error entry - " + ql_log(ql_log_warn, fcport->vha, 0x5034, + "Async-%s error entry - hdl=%x" "portid=%02x%02x%02x entry-status=%x.\n", - fcport->vha->host_no, sp->handle, type, - fcport->d_id.b.domain, fcport->d_id.b.area, - fcport->d_id.b.al_pa, logio->entry_status)); - DEBUG2(qla2x00_dump_buffer((uint8_t *)logio, sizeof(*logio))); + 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) { - DEBUG2(printk(KERN_DEBUG - "scsi(%ld:%x): Async-%s complete - portid=%02x%02x%02x " - "iop0=%x.\n", - fcport->vha->host_no, sp->handle, type, - fcport->d_id.b.domain, fcport->d_id.b.area, - fcport->d_id.b.al_pa, - le32_to_cpu(logio->io_parameter[0]))); + 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 (ctx->type != SRB_LOGIN_CMD) + if (sp->type != SRB_LOGIN_CMD) goto logio_done; iop[0] = le32_to_cpu(logio->io_parameter[0]); @@ -1233,6 +1459,9 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, } else if (iop[0] & BIT_5) fcport->port_type = FCT_INITIATOR; + if (iop[0] & BIT_7) + fcport->flags |= FCF_CONF_COMP_SUPPORTED; + if (logio->io_parameter[7] || logio->io_parameter[8]) fcport->supported_classes |= FC_COS_CLASS2; if (logio->io_parameter[9] || logio->io_parameter[10]) @@ -1256,77 +1485,66 @@ qla24xx_logio_entry(scsi_qla_host_t *vha, struct req_que *req, break; } - DEBUG2(printk(KERN_WARNING - "scsi(%ld:%x): Async-%s failed - portid=%02x%02x%02x comp=%x " - "iop0=%x iop1=%x.\n", - fcport->vha->host_no, sp->handle, type, fcport->d_id.b.domain, + 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]))); + le32_to_cpu(logio->io_parameter[1])); logio_done: - lio->done(sp); + 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) +qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, void *tsk) { const char func[] = "TMF-IOCB"; const char *type; fc_port_t *fcport; srb_t *sp; struct srb_iocb *iocb; - struct srb_ctx *ctx; 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; - ctx = sp->ctx; - iocb = ctx->u.iocb_cmd; - type = ctx->name; + iocb = &sp->u.iocb_cmd; + type = sp->name; fcport = sp->fcport; + iocb->u.tmf.data = QLA_SUCCESS; if (sts->entry_status) { - DEBUG2(printk(KERN_WARNING - "scsi(%ld:%x): Async-%s error - entry-status(%x).\n", - fcport->vha->host_no, sp->handle, type, - 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); + iocb->u.tmf.data = QLA_FUNCTION_FAILED; } else if (sts->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) { - DEBUG2(printk(KERN_WARNING - "scsi(%ld:%x): Async-%s error - completion status(%x).\n", - fcport->vha->host_no, sp->handle, type, - sts->comp_status)); - } else if (!(le16_to_cpu(sts->scsi_status) & + ql_log(ql_log_warn, fcport->vha, 0x5039, + "Async-%s error - hdl=%x completion status(%x).\n", + type, sp->handle, sts->comp_status); + iocb->u.tmf.data = QLA_FUNCTION_FAILED; + } else if ((le16_to_cpu(sts->scsi_status) & SS_RESPONSE_INFO_LEN_VALID)) { - DEBUG2(printk(KERN_WARNING - "scsi(%ld:%x): Async-%s error - no response info(%x).\n", - fcport->vha->host_no, sp->handle, type, - sts->scsi_status)); - } else if (le32_to_cpu(sts->rsp_data_len) < 4) { - DEBUG2(printk(KERN_WARNING - "scsi(%ld:%x): Async-%s error - not enough response(%d).\n", - fcport->vha->host_no, sp->handle, type, - sts->rsp_data_len)); - } else if (sts->data[3]) { - DEBUG2(printk(KERN_WARNING - "scsi(%ld:%x): Async-%s error - response(%x).\n", - fcport->vha->host_no, sp->handle, type, - sts->data[3])); - } else { - error = 0; + 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]); + iocb->u.tmf.data = QLA_FUNCTION_FAILED; + } } - if (error) { - iocb->u.tmf.data = error; - DEBUG2(qla2x00_dump_buffer((uint8_t *)sts, sizeof(*sts))); - } + if (iocb->u.tmf.data != QLA_SUCCESS) + ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5055, + (uint8_t *)sts, sizeof(*sts)); - iocb->done(sp); + sp->done(vha, sp, 0); } /** @@ -1360,9 +1578,6 @@ qla2x00_process_response_queue(struct rsp_que *rsp) } if (pkt->entry_status != 0) { - DEBUG3(printk(KERN_INFO - "scsi(%ld): Process error entry.\n", vha->host_no)); - qla2x00_error_entry(vha, rsp, pkt); ((response_t *)pkt)->signature = RESPONSE_PROCESSED; wmb(); @@ -1399,10 +1614,10 @@ qla2x00_process_response_queue(struct rsp_que *rsp) break; default: /* Type Not Supported. */ - DEBUG4(printk(KERN_WARNING - "scsi(%ld): Received unknown response pkt type %x " + ql_log(ql_log_warn, vha, 0x504a, + "Received unknown response pkt type %x " "entry status=%x.\n", - vha->host_no, pkt->entry_type, pkt->entry_status)); + pkt->entry_type, pkt->entry_status); break; } ((response_t *)pkt)->signature = RESPONSE_PROCESSED; @@ -1414,37 +1629,47 @@ qla2x00_process_response_queue(struct rsp_que *rsp) } 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) + uint32_t sense_len, struct rsp_que *rsp, int res) { - struct scsi_cmnd *cp = sp->cmd; + 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; - sp->request_sense_length = sense_len; - sp->request_sense_ptr = cp->sense_buffer; - if (sp->request_sense_length > par_sense_len) + 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); - sp->request_sense_ptr += sense_len; - sp->request_sense_length -= sense_len; - if (sp->request_sense_length != 0) + 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; + } - DEBUG5(printk("%s(): Check condition Sense data, scsi(%ld:%d:%d:%d) " - "cmd=%p\n", __func__, sp->fcport->vha->host_no, - cp->device->channel, cp->device->id, cp->device->lun, cp)); - if (sense_len) - DEBUG5(qla2x00_dump_buffer(cp->sense_buffer, sense_len)); + 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 */ + __be16 app_tag; /* APPL identifier */ __be32 ref_tag; /* Target LBA or indirect LBA */ }; @@ -1454,34 +1679,94 @@ struct scsi_dif_tuple { * ASC/ASCQ fields in the sense buffer with ILLEGAL_REQUEST * to indicate to the kernel that the HBA detected error. */ -static inline void +static inline int qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24) { - struct scsi_cmnd *cmd = sp->cmd; - struct scsi_dif_tuple *ep = - (struct scsi_dif_tuple *)&sts24->data[20]; - struct scsi_dif_tuple *ap = - (struct scsi_dif_tuple *)&sts24->data[12]; + 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; - e_ref_tag = be32_to_cpu(ep->ref_tag); - a_ref_tag = be32_to_cpu(ap->ref_tag); - e_app_tag = be16_to_cpu(ep->app_tag); - a_app_tag = be16_to_cpu(ap->app_tag); - e_guard = be16_to_cpu(ep->guard); - a_guard = be16_to_cpu(ap->guard); - - DEBUG18(printk(KERN_DEBUG - "%s(): iocb(s) %p Returned STATUS\n", __func__, sts24)); - - DEBUG18(printk(KERN_ERR "DIF ERROR in cmd 0x%x lba 0x%llx act ref" + /* + * 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", + " 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)); + 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) { @@ -1490,28 +1775,176 @@ qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24) set_driver_byte(cmd, DRIVER_SENSE); set_host_byte(cmd, DID_ABORT); cmd->result |= SAM_STAT_CHECK_CONDITION << 1; - return; + return 1; } - /* check appl tag */ - if (e_app_tag != a_app_tag) { + /* check ref tag */ + if (e_ref_tag != a_ref_tag) { scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST, - 0x10, 0x2); + 0x10, 0x3); set_driver_byte(cmd, DRIVER_SENSE); set_host_byte(cmd, DID_ABORT); cmd->result |= SAM_STAT_CHECK_CONDITION << 1; - return; + return 1; } - /* check ref tag */ - if (e_ref_tag != a_ref_tag) { + /* check appl tag */ + if (e_app_tag != a_app_tag) { scsi_build_sense_buffer(1, cmd->sense_buffer, ILLEGAL_REQUEST, - 0x10, 0x3); + 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; +} + +static void +qla25xx_process_bidir_status_iocb(scsi_qla_host_t *vha, void *pkt, + struct req_que *req, uint32_t index) +{ + struct qla_hw_data *ha = vha->hw; + srb_t *sp; + uint16_t comp_status; + uint16_t scsi_status; + uint16_t thread_id; + uint32_t rval = EXT_STATUS_OK; + struct fc_bsg_job *bsg_job = NULL; + sts_entry_t *sts; + struct sts_entry_24xx *sts24; + sts = (sts_entry_t *) pkt; + sts24 = (struct sts_entry_24xx *) pkt; + + /* Validate handle. */ + if (index >= req->num_outstanding_cmds) { + ql_log(ql_log_warn, vha, 0x70af, + "Invalid SCSI completion handle 0x%x.\n", index); + 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; + bsg_job = sp->u.bsg_job; + } else { + ql_log(ql_log_warn, vha, 0x70b0, + "Req:%d: Invalid ISP SCSI completion handle(0x%x)\n", + req->id, index); + + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); return; } + + 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; + } + + thread_id = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; + switch (comp_status) { + case CS_COMPLETE: + if (scsi_status == 0) { + bsg_job->reply->reply_payload_rcv_len = + bsg_job->reply_payload.payload_len; + vha->qla_stats.input_bytes += + bsg_job->reply->reply_payload_rcv_len; + vha->qla_stats.input_requests++; + rval = EXT_STATUS_OK; + } + goto done; + + case CS_DATA_OVERRUN: + ql_dbg(ql_dbg_user, vha, 0x70b1, + "Command completed with date overrun thread_id=%d\n", + thread_id); + rval = EXT_STATUS_DATA_OVERRUN; + break; + + case CS_DATA_UNDERRUN: + ql_dbg(ql_dbg_user, vha, 0x70b2, + "Command completed with date underrun thread_id=%d\n", + thread_id); + rval = EXT_STATUS_DATA_UNDERRUN; + break; + case CS_BIDIR_RD_OVERRUN: + ql_dbg(ql_dbg_user, vha, 0x70b3, + "Command completed with read data overrun thread_id=%d\n", + thread_id); + rval = EXT_STATUS_DATA_OVERRUN; + break; + + case CS_BIDIR_RD_WR_OVERRUN: + ql_dbg(ql_dbg_user, vha, 0x70b4, + "Command completed with read and write data overrun " + "thread_id=%d\n", thread_id); + rval = EXT_STATUS_DATA_OVERRUN; + break; + + case CS_BIDIR_RD_OVERRUN_WR_UNDERRUN: + ql_dbg(ql_dbg_user, vha, 0x70b5, + "Command completed with read data over and write data " + "underrun thread_id=%d\n", thread_id); + rval = EXT_STATUS_DATA_OVERRUN; + break; + + case CS_BIDIR_RD_UNDERRUN: + ql_dbg(ql_dbg_user, vha, 0x70b6, + "Command completed with read data data underrun " + "thread_id=%d\n", thread_id); + rval = EXT_STATUS_DATA_UNDERRUN; + break; + + case CS_BIDIR_RD_UNDERRUN_WR_OVERRUN: + ql_dbg(ql_dbg_user, vha, 0x70b7, + "Command completed with read data under and write data " + "overrun thread_id=%d\n", thread_id); + rval = EXT_STATUS_DATA_UNDERRUN; + break; + + case CS_BIDIR_RD_WR_UNDERRUN: + ql_dbg(ql_dbg_user, vha, 0x70b8, + "Command completed with read and write data underrun " + "thread_id=%d\n", thread_id); + rval = EXT_STATUS_DATA_UNDERRUN; + break; + + case CS_BIDIR_DMA: + ql_dbg(ql_dbg_user, vha, 0x70b9, + "Command completed with data DMA error thread_id=%d\n", + thread_id); + rval = EXT_STATUS_DMA_ERR; + break; + + case CS_TIMEOUT: + ql_dbg(ql_dbg_user, vha, 0x70ba, + "Command completed with timeout thread_id=%d\n", + thread_id); + rval = EXT_STATUS_TIMEOUT; + break; + default: + ql_dbg(ql_dbg_user, vha, 0x70bb, + "Command completed with completion status=0x%x " + "thread_id=%d\n", comp_status, thread_id); + rval = EXT_STATUS_ERR; + break; + } + bsg_job->reply->reply_payload_rcv_len = 0; + +done: + /* Return the vendor specific reply to API */ + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = rval; + bsg_job->reply_len = sizeof(struct fc_bsg_reply); + /* Always return DID_OK, bsg will send the vendor specific response + * in this case only */ + sp->done(vha, sp, (DID_OK << 6)); + } /** @@ -1540,12 +1973,15 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) uint16_t que; struct req_que *req; int logit = 1; + int res = 0; + uint16_t state_flags = 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; + state_flags = le16_to_cpu(sts24->state_flags); } else { comp_status = le16_to_cpu(sts->comp_status); scsi_status = le16_to_cpu(sts->scsi_status) & SS_MASK; @@ -1554,42 +1990,64 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) 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); - + /* Check for invalid queue pointer */ + if (req == NULL || + que >= find_first_zero_bit(ha->req_qid_map, ha->max_req_queues)) { + ql_dbg(ql_dbg_io, vha, 0x3059, + "Invalid status handle (0x%x): Bad req pointer. req=%p, " + "que=%u.\n", sts->handle, req, que); return; } /* Validate handle. */ - if (handle < MAX_OUTSTANDING_COMMANDS) { + if (handle < req->num_outstanding_cmds) sp = req->outstanding_cmds[handle]; - req->outstanding_cmds[handle] = NULL; - } else + else sp = NULL; if (sp == NULL) { - qla_printk(KERN_WARNING, ha, - "scsi(%ld): Invalid status handle (0x%x).\n", vha->host_no, - sts->handle); + ql_dbg(ql_dbg_io, vha, 0x3017, + "Invalid status handle (0x%x).\n", sts->handle); + + if (!test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) { + if (IS_P3P_TYPE(ha)) + set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags); + else + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + } + return; + } + + if (unlikely((state_flags & BIT_1) && (sp->type == SRB_BIDI_CMD))) { + qla25xx_process_bidir_status_iocb(vha, pkt, req, handle); + return; + } + + /* Task Management completion. */ + if (sp->type == SRB_TM_CMD) { + qla24xx_tm_iocb_entry(vha, req, pkt); + return; + } + + /* Fast path completion. */ + if (comp_status == CS_COMPLETE && scsi_status == 0) { + qla2x00_process_completed_request(vha, req, 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 = sp->cmd; + + req->outstanding_cmds[handle] = NULL; + cp = GET_CMD_SP(sp); if (cp == NULL) { - qla_printk(KERN_WARNING, ha, - "scsi(%ld): Command already returned (0x%x/%p).\n", - vha->host_no, sts->handle, sp); + ql_dbg(ql_dbg_io, vha, 0x3018, + "Command already returned (0x%x/%p).\n", + sts->handle, sp); return; } - lscsi_status = scsi_status & STATUS_MASK; + lscsi_status = scsi_status & STATUS_MASK; fcport = sp->fcport; @@ -1629,12 +2087,11 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) par_sense_len -= rsp_info_len; } if (rsp_info_len > 3 && rsp_info[3]) { - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d): FCP I/O protocol failure " - "(0x%x/0x%x).\n", vha->host_no, cp->device->id, - cp->device->lun, rsp_info_len, 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]); - cp->result = DID_BUS_BUSY << 16; + res = DID_BUS_BUSY << 16; goto out; } } @@ -1651,7 +2108,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) case CS_COMPLETE: case CS_QUEUE_FULL: if (scsi_status == 0) { - cp->result = DID_OK << 16; + res = DID_OK << 16; break; } if (scsi_status & (SS_RESIDUAL_UNDER | SS_RESIDUAL_OVER)) { @@ -1661,22 +2118,20 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) if (!lscsi_status && ((unsigned)(scsi_bufflen(cp) - resid) < cp->underflow)) { - qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d): Mid-layer underflow " + ql_dbg(ql_dbg_io, fcport->vha, 0x301a, + "Mid-layer underflow " "detected (0x%x of 0x%x bytes).\n", - vha->host_no, cp->device->id, - cp->device->lun, resid, scsi_bufflen(cp)); + resid, scsi_bufflen(cp)); - cp->result = DID_ERROR << 16; + res = DID_ERROR << 16; break; } } - cp->result = DID_OK << 16 | lscsi_status; + res = DID_OK << 16 | lscsi_status; if (lscsi_status == SAM_STAT_TASK_SET_FULL) { - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d) QUEUE FULL detected.\n", - vha->host_no, cp->device->id, cp->device->lun)); + ql_dbg(ql_dbg_io, fcport->vha, 0x301b, + "QUEUE FULL detected.\n"); break; } logit = 0; @@ -1688,7 +2143,7 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) break; qla2x00_handle_sense(sp, sense_data, par_sense_len, sense_len, - rsp); + rsp, res); break; case CS_DATA_UNDERRUN: @@ -1697,39 +2152,47 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) scsi_set_resid(cp, resid); if (scsi_status & SS_RESIDUAL_UNDER) { if (IS_FWI2_CAPABLE(ha) && fw_resid_len != resid_len) { - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d) Dropped frame(s) detected " - "(0x%x of 0x%x bytes).\n", vha->host_no, - cp->device->id, cp->device->lun, resid, - scsi_bufflen(cp))); + ql_dbg(ql_dbg_io, fcport->vha, 0x301d, + "Dropped frame(s) detected " + "(0x%x of 0x%x bytes).\n", + resid, scsi_bufflen(cp)); - cp->result = DID_ERROR << 16 | lscsi_status; - break; + res = DID_ERROR << 16 | lscsi_status; + goto check_scsi_status; } if (!lscsi_status && ((unsigned)(scsi_bufflen(cp) - resid) < cp->underflow)) { - qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d): Mid-layer underflow " + ql_dbg(ql_dbg_io, fcport->vha, 0x301e, + "Mid-layer underflow " "detected (0x%x of 0x%x bytes).\n", - vha->host_no, cp->device->id, - cp->device->lun, resid, scsi_bufflen(cp)); + resid, scsi_bufflen(cp)); - cp->result = DID_ERROR << 16; + res = DID_ERROR << 16; break; } - } else { - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d) Dropped frame(s) detected (0x%x " - "of 0x%x bytes).\n", vha->host_no, cp->device->id, - cp->device->lun, resid, scsi_bufflen(cp))); + } 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)); - cp->result = DID_ERROR << 16 | lscsi_status; + 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); } - cp->result = DID_OK << 16 | lscsi_status; + res = DID_OK << 16 | lscsi_status; logit = 0; check_scsi_status: @@ -1739,10 +2202,8 @@ check_scsi_status: */ if (lscsi_status != 0) { if (lscsi_status == SAM_STAT_TASK_SET_FULL) { - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d) QUEUE FULL detected.\n", - vha->host_no, cp->device->id, - cp->device->lun)); + ql_dbg(ql_dbg_io, fcport->vha, 0x3020, + "QUEUE FULL detected.\n"); logit = 1; break; } @@ -1754,7 +2215,7 @@ check_scsi_status: break; qla2x00_handle_sense(sp, sense_data, par_sense_len, - sense_len, rsp); + sense_len, rsp, res); } break; @@ -1771,7 +2232,7 @@ check_scsi_status: * while we try to recover so instruct the mid layer * to requeue until the class decides how to handle this. */ - cp->result = DID_TRANSPORT_DISRUPTED << 16; + res = DID_TRANSPORT_DISRUPTED << 16; if (comp_status == CS_TIMEOUT) { if (IS_FWI2_CAPABLE(ha)) @@ -1781,41 +2242,56 @@ check_scsi_status: break; } - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d) Port down status: port-state=0x%x\n", - vha->host_no, cp->device->id, cp->device->lun, - atomic_read(&fcport->state))); + ql_dbg(ql_dbg_io, fcport->vha, 0x3021, + "Port to be marked lost on fcport=%02x%02x%02x, current " + "port state= %s.\n", fcport->d_id.b.domain, + fcport->d_id.b.area, fcport->d_id.b.al_pa, + port_state_str[atomic_read(&fcport->state)]); if (atomic_read(&fcport->state) == FCS_ONLINE) qla2x00_mark_device_lost(fcport->vha, fcport, 1, 1); break; case CS_ABORTED: - cp->result = DID_RESET << 16; + res = DID_RESET << 16; break; case CS_DIF_ERROR: - qla2x00_handle_dif_error(sp, sts24); + logit = qla2x00_handle_dif_error(sp, sts24); + res = cp->result; break; + + case CS_TRANSPORT: + res = DID_ERROR << 16; + + if (!IS_PI_SPLIT_DET_CAPABLE(ha)) + break; + + if (state_flags & BIT_4) + scmd_printk(KERN_WARNING, cp, + "Unsupported device '%s' found.\n", + cp->device->vendor); + break; + default: - cp->result = DID_ERROR << 16; + res = DID_ERROR << 16; break; } out: if (logit) - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d) FCP command status: 0x%x-0x%x (0x%x) " - "portid=%02x%02x%02x oxid=0x%x cdb=%02x%02x%02x len=0x%x " - "rsp_info=0x%x resid=0x%x fw_resid=0x%x\n", vha->host_no, - cp->device->id, cp->device->lun, comp_status, scsi_status, - cp->result, 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], scsi_bufflen(cp), rsp_info_len, resid_len, - fw_resid_len)); + 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=%10phN 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, scsi_bufflen(cp), rsp_info_len, + resid_len, fw_resid_len); if (rsp->status_srb == NULL) - qla2x00_sp_compl(ha, sp); + sp->done(ha, sp, res); } /** @@ -1828,44 +2304,51 @@ out: static void qla2x00_status_cont_entry(struct rsp_que *rsp, sts_cont_entry_t *pkt) { - uint8_t sense_sz = 0; + uint8_t sense_sz = 0; struct qla_hw_data *ha = rsp->hw; - srb_t *sp = rsp->status_srb; + 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 != NULL && sp->request_sense_length != 0) { - cp = sp->cmd; - if (cp == NULL) { - DEBUG2(printk("%s(): Cmd already returned back to OS " - "sp=%p.\n", __func__, sp)); - qla_printk(KERN_INFO, ha, - "cmd is NULL: already returned to OS (sp=%p)\n", - sp); + if (!sp || !GET_CMD_SENSE_LEN(sp)) + return; - rsp->status_srb = NULL; - return; - } + sense_len = GET_CMD_SENSE_LEN(sp); + sense_ptr = GET_CMD_SENSE_PTR(sp); - if (sp->request_sense_length > sizeof(pkt->data)) { - sense_sz = sizeof(pkt->data); - } else { - sense_sz = sp->request_sense_length; - } + 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); - /* Move sense data. */ - if (IS_FWI2_CAPABLE(ha)) - host_to_fcp_swap(pkt->data, sizeof(pkt->data)); - memcpy(sp->request_sense_ptr, pkt->data, sense_sz); - DEBUG5(qla2x00_dump_buffer(sp->request_sense_ptr, sense_sz)); + rsp->status_srb = NULL; + return; + } - sp->request_sense_ptr += sense_sz; - sp->request_sense_length -= sense_sz; + if (sense_len > sizeof(pkt->data)) + sense_sz = sizeof(pkt->data); + else + sense_sz = sense_len; - /* Place command on done queue. */ - if (sp->request_sense_length == 0) { - rsp->status_srb = NULL; - qla2x00_sp_compl(ha, sp); - } + /* 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); } } @@ -1879,61 +2362,36 @@ 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; - uint32_t handle = LSW(pkt->handle); + const char func[] = "ERROR-IOCB"; uint16_t que = MSW(pkt->handle); - struct req_que *req = ha->req_q_map[que]; -#if defined(QL_DEBUG_LEVEL_2) - if (pkt->entry_status & RF_INV_E_ORDER) - qla_printk(KERN_ERR, ha, "%s: Invalid Entry Order\n", __func__); - else if (pkt->entry_status & RF_INV_E_COUNT) - qla_printk(KERN_ERR, ha, "%s: Invalid Entry Count\n", __func__); - else if (pkt->entry_status & RF_INV_E_PARAM) - qla_printk(KERN_ERR, ha, - "%s: Invalid Entry Parameter\n", __func__); - else if (pkt->entry_status & RF_INV_E_TYPE) - qla_printk(KERN_ERR, ha, "%s: Invalid Entry Type\n", __func__); - else if (pkt->entry_status & RF_BUSY) - qla_printk(KERN_ERR, ha, "%s: Busy\n", __func__); - else - qla_printk(KERN_ERR, ha, "%s: UNKNOWN flag error\n", __func__); -#endif + struct req_que *req = NULL; + int res = DID_ERROR << 16; - /* Validate handle. */ - if (handle < MAX_OUTSTANDING_COMMANDS) - sp = req->outstanding_cmds[handle]; - else - sp = NULL; + ql_dbg(ql_dbg_async, vha, 0x502a, + "type of error status in response: 0x%x\n", pkt->entry_status); - if (sp) { - /* Free outstanding command slot. */ - req->outstanding_cmds[handle] = NULL; - - /* Bad payload or header */ - if (pkt->entry_status & - (RF_INV_E_ORDER | RF_INV_E_COUNT | - RF_INV_E_PARAM | RF_INV_E_TYPE)) { - sp->cmd->result = DID_ERROR << 16; - } else if (pkt->entry_status & RF_BUSY) { - sp->cmd->result = DID_BUS_BUSY << 16; - } else { - sp->cmd->result = DID_ERROR << 16; - } - qla2x00_sp_compl(ha, sp); + if (que >= ha->max_req_queues || !ha->req_q_map[que]) + goto fatal; - } else if (pkt->entry_type == COMMAND_A64_TYPE || pkt->entry_type == - COMMAND_TYPE || pkt->entry_type == COMMAND_TYPE_7 - || pkt->entry_type == COMMAND_TYPE_6) { - DEBUG2(printk("scsi(%ld): Error entry - invalid handle\n", - vha->host_no)); - qla_printk(KERN_WARNING, ha, - "Error entry - invalid handle\n"); + req = ha->req_q_map[que]; - 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); + 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_P3P_TYPE(ha)) + set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags); + else + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); } /** @@ -1945,27 +2403,48 @@ 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 ERROR.\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 *)®->mailbox1; for (cnt = 1; cnt < ha->mbx_count; cnt++) { - ha->mailbox_out[cnt] = RD_REG_WORD(wptr); + if (mboxes & BIT_0) + ha->mailbox_out[cnt] = RD_REG_WORD(wptr); + + mboxes >>= 1; wptr++; } +} - if (ha->mcp) { - DEBUG3(printk("%s(%ld): Got mailbox completion. cmd=%x.\n", - __func__, vha->host_no, ha->mcp->mb[0])); - } else { - DEBUG2_3(printk("%s(%ld): MBX pointer ERROR!\n", - __func__, vha->host_no)); - } +static void +qla24xx_abort_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, + struct abort_entry_24xx *pkt) +{ + const char func[] = "ABT_IOCB"; + srb_t *sp; + struct srb_iocb *abt; + + sp = qla2x00_get_sp_from_handle(vha, func, req, pkt); + if (!sp) + return; + + abt = &sp->u.iocb_cmd; + abt->u.abt.comp_status = le32_to_cpu(pkt->nport_handle); + sp->done(vha, sp, 0); } /** @@ -1993,14 +2472,16 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, } if (pkt->entry_status != 0) { - DEBUG3(printk(KERN_INFO - "scsi(%ld): Process error entry.\n", vha->host_no)); - qla2x00_error_entry(vha, rsp, (sts_entry_t *) pkt); + + if (qlt_24xx_process_response_error(vha, pkt)) + goto process_err; + ((response_t *)pkt)->signature = RESPONSE_PROCESSED; wmb(); continue; } +process_err: switch (pkt->entry_type) { case STATUS_TYPE: @@ -2017,23 +2498,36 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, 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: + case CT_IOCB_TYPE: qla24xx_els_ct_entry(vha, rsp->req, pkt, CT_IOCB_TYPE); - clear_bit(MBX_INTERRUPT, &vha->hw->mbx_cmd_flags); break; - case ELS_IOCB_TYPE: + case ELS_IOCB_TYPE: qla24xx_els_ct_entry(vha, rsp->req, pkt, ELS_IOCB_TYPE); break; + case ABTS_RECV_24XX: + /* ensure that the ATIO queue is empty */ + qlt_24xx_process_atio_queue(vha); + case ABTS_RESP_24XX: + case CTIO_TYPE7: + case NOTIFY_ACK_TYPE: + case CTIO_CRC2: + qlt_response_pkt_all_vps(vha, (response_t *)pkt); + break; + case MARKER_TYPE: + /* Do nothing in this case, this check is to prevent it + * from falling into default case + */ + break; + case ABORT_IOCB_TYPE: + qla24xx_abort_iocb_entry(vha, rsp->req, + (struct abort_entry_24xx *)pkt); + break; default: /* Type Not Supported. */ - DEBUG4(printk(KERN_WARNING - "scsi(%ld): Received unknown response pkt type %x " + ql_dbg(ql_dbg_async, vha, 0x5042, + "Received unknown response pkt type %x " "entry status=%x.\n", - vha->host_no, pkt->entry_type, pkt->entry_status)); + pkt->entry_type, pkt->entry_status); break; } ((response_t *)pkt)->signature = RESPONSE_PROCESSED; @@ -2041,7 +2535,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, } /* Adjust ring index */ - if (IS_QLA82XX(ha)) { + if (IS_P3P_TYPE(ha)) { struct device_reg_82xx __iomem *reg = &ha->iobase->isp82; WRT_REG_DWORD(®->rsp_q_out[0], rsp->ring_index); } else @@ -2056,7 +2550,8 @@ qla2xxx_check_risc_status(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; - if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha)) + if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha) && + !IS_QLA27XX(ha)) return; rval = QLA_SUCCESS; @@ -2074,6 +2569,7 @@ qla2xxx_check_risc_status(scsi_qla_host_t *vha) if (rval == QLA_SUCCESS) goto next_test; + rval = QLA_SUCCESS; WRT_REG_DWORD(®->iobase_window, 0x0003); for (cnt = 100; (RD_REG_DWORD(®->iobase_window) & BIT_0) == 0 && rval == QLA_SUCCESS; cnt--) { @@ -2088,7 +2584,8 @@ qla2xxx_check_risc_status(scsi_qla_host_t *vha) next_test: if (RD_REG_DWORD(®->iobase_c8) & BIT_3) - qla_printk(KERN_INFO, ha, "Additional code -- 0x55AA.\n"); + ql_log(ql_log_info, vha, 0x504c, + "Additional code -- 0x55AA.\n"); done: WRT_REG_DWORD(®->iobase_window, 0x0000); @@ -2096,7 +2593,7 @@ done: } /** - * qla24xx_intr_handler() - Process interrupts for the ISP23xx and ISP63xx. + * qla24xx_intr_handler() - Process interrupts for the ISP23xx and ISP24xx. * @irq: * @dev_id: SCSI driver HA context * @@ -2114,14 +2611,14 @@ qla24xx_intr_handler(int irq, void *dev_id) unsigned long iter; uint32_t stat; uint32_t hccr; - uint16_t mb[4]; + uint16_t mb[8]; struct rsp_que *rsp; unsigned long flags; rsp = (struct rsp_que *) dev_id; if (!rsp) { - printk(KERN_INFO - "%s(): NULL response queue pointer\n", __func__); + ql_log(ql_log_info, NULL, 0x5059, + "%s: NULL response queue pointer.\n", __func__); return IRQ_NONE; } @@ -2136,14 +2633,17 @@ qla24xx_intr_handler(int irq, void *dev_id) vha = pci_get_drvdata(ha->pdev); for (iter = 50; iter--; ) { stat = RD_REG_DWORD(®->host_status); + if (qla2x00_check_reg_for_disconnect(vha, stat)) + break; if (stat & HSRX_RISC_PAUSED) { if (unlikely(pci_channel_offline(ha->pdev))) break; hccr = RD_REG_DWORD(®->hccr); - qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, " - "Dumping firmware!\n", hccr); + ql_log(ql_log_warn, vha, 0x504b, + "RISC paused -- HCCR=%x, Dumping firmware.\n", + hccr); qla2xxx_check_risc_status(vha); @@ -2154,42 +2654,45 @@ qla24xx_intr_handler(int irq, void *dev_id) break; switch (stat & 0xff) { - case 0x1: - case 0x2: - case 0x10: - case 0x11: + case INTR_ROM_MB_SUCCESS: + case INTR_ROM_MB_FAILED: + case INTR_MB_SUCCESS: + case INTR_MB_FAILED: qla24xx_mbx_completion(vha, MSW(stat)); status |= MBX_INTERRUPT; break; - case 0x12: + case INTR_ASYNC_EVENT: mb[0] = MSW(stat); mb[1] = RD_REG_WORD(®->mailbox1); mb[2] = RD_REG_WORD(®->mailbox2); mb[3] = RD_REG_WORD(®->mailbox3); qla2x00_async_event(vha, rsp, mb); break; - case 0x13: - case 0x14: + case INTR_RSP_QUE_UPDATE: + case INTR_RSP_QUE_UPDATE_83XX: + qla24xx_process_response_queue(vha, rsp); + break; + case INTR_ATIO_QUE_UPDATE: + qlt_24xx_process_atio_queue(vha); + break; + case INTR_ATIO_RSP_QUE_UPDATE: + qlt_24xx_process_atio_queue(vha); qla24xx_process_response_queue(vha, rsp); break; default: - DEBUG2(printk("scsi(%ld): Unrecognized interrupt type " - "(%d).\n", - vha->host_no, stat & 0xff)); + ql_dbg(ql_dbg_async, vha, 0x504f, + "Unrecognized interrupt type (%d).\n", stat * 0xff); break; } WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); RD_REG_DWORD_RELAXED(®->hccr); + if (unlikely(IS_QLA83XX(ha) && (ha->pdev->revision == 1))) + ndelay(3500); } + qla2x00_handle_mbx_completion(ha, status); 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; } @@ -2201,11 +2704,12 @@ qla24xx_msix_rsp_q(int irq, void *dev_id) struct device_reg_24xx __iomem *reg; struct scsi_qla_host *vha; unsigned long flags; + uint32_t stat = 0; rsp = (struct rsp_que *) dev_id; if (!rsp) { - printk(KERN_INFO - "%s(): NULL response queue pointer\n", __func__); + ql_log(ql_log_info, NULL, 0x505a, + "%s: NULL response queue pointer.\n", __func__); return IRQ_NONE; } ha = rsp->hw; @@ -2214,11 +2718,19 @@ qla24xx_msix_rsp_q(int irq, void *dev_id) spin_lock_irqsave(&ha->hardware_lock, flags); vha = pci_get_drvdata(ha->pdev); + /* + * Use host_status register to check to PCI disconnection before we + * we process the response queue. + */ + stat = RD_REG_DWORD(®->host_status); + if (qla2x00_check_reg_for_disconnect(vha, stat)) + goto out; qla24xx_process_response_queue(vha, rsp); if (!ha->flags.disable_msix_handshake) { WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); RD_REG_DWORD_RELAXED(®->hccr); } +out: spin_unlock_irqrestore(&ha->hardware_lock, flags); return IRQ_HANDLED; @@ -2228,28 +2740,34 @@ static irqreturn_t qla25xx_msix_rsp_q(int irq, void *dev_id) { struct qla_hw_data *ha; + scsi_qla_host_t *vha; struct rsp_que *rsp; struct device_reg_24xx __iomem *reg; unsigned long flags; + uint32_t hccr = 0; rsp = (struct rsp_que *) dev_id; if (!rsp) { - printk(KERN_INFO - "%s(): NULL response queue pointer\n", __func__); + ql_log(ql_log_info, NULL, 0x505b, + "%s: NULL response queue pointer.\n", __func__); return IRQ_NONE; } ha = rsp->hw; + vha = pci_get_drvdata(ha->pdev); /* Clear the interrupt, if enabled, for this response queue */ - if (rsp->options & ~BIT_6) { + if (!ha->flags.disable_msix_handshake) { reg = &ha->iobase->isp24; spin_lock_irqsave(&ha->hardware_lock, flags); WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); - RD_REG_DWORD_RELAXED(®->hccr); + hccr = RD_REG_DWORD_RELAXED(®->hccr); spin_unlock_irqrestore(&ha->hardware_lock, flags); } + if (qla2x00_check_reg_for_disconnect(vha, hccr)) + goto out; queue_work_on((int) (rsp->id - 1), ha->wq, &rsp->q_work); +out: return IRQ_HANDLED; } @@ -2263,13 +2781,13 @@ qla24xx_msix_default(int irq, void *dev_id) int status; uint32_t stat; uint32_t hccr; - uint16_t mb[4]; + uint16_t mb[8]; unsigned long flags; rsp = (struct rsp_que *) dev_id; if (!rsp) { - DEBUG(printk( - "%s(): NULL response queue pointer\n", __func__)); + ql_log(ql_log_info, NULL, 0x505c, + "%s: NULL response queue pointer.\n", __func__); return IRQ_NONE; } ha = rsp->hw; @@ -2280,14 +2798,17 @@ qla24xx_msix_default(int irq, void *dev_id) vha = pci_get_drvdata(ha->pdev); do { stat = RD_REG_DWORD(®->host_status); + if (qla2x00_check_reg_for_disconnect(vha, stat)) + break; if (stat & HSRX_RISC_PAUSED) { if (unlikely(pci_channel_offline(ha->pdev))) break; hccr = RD_REG_DWORD(®->hccr); - qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, " - "Dumping firmware!\n", hccr); + ql_log(ql_log_info, vha, 0x5050, + "RISC paused -- HCCR=%x, Dumping firmware.\n", + hccr); qla2xxx_check_risc_status(vha); @@ -2298,40 +2819,42 @@ qla24xx_msix_default(int irq, void *dev_id) break; switch (stat & 0xff) { - case 0x1: - case 0x2: - case 0x10: - case 0x11: + case INTR_ROM_MB_SUCCESS: + case INTR_ROM_MB_FAILED: + case INTR_MB_SUCCESS: + case INTR_MB_FAILED: qla24xx_mbx_completion(vha, MSW(stat)); status |= MBX_INTERRUPT; break; - case 0x12: + case INTR_ASYNC_EVENT: mb[0] = MSW(stat); mb[1] = RD_REG_WORD(®->mailbox1); mb[2] = RD_REG_WORD(®->mailbox2); mb[3] = RD_REG_WORD(®->mailbox3); qla2x00_async_event(vha, rsp, mb); break; - case 0x13: - case 0x14: + case INTR_RSP_QUE_UPDATE: + case INTR_RSP_QUE_UPDATE_83XX: + qla24xx_process_response_queue(vha, rsp); + break; + case INTR_ATIO_QUE_UPDATE: + qlt_24xx_process_atio_queue(vha); + break; + case INTR_ATIO_RSP_QUE_UPDATE: + qlt_24xx_process_atio_queue(vha); qla24xx_process_response_queue(vha, rsp); break; default: - DEBUG2(printk("scsi(%ld): Unrecognized interrupt type " - "(%d).\n", - vha->host_no, stat & 0xff)); + ql_dbg(ql_dbg_async, vha, 0x5051, + "Unrecognized interrupt type (%d).\n", stat & 0xff); break; } WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); } while (0); + qla2x00_handle_mbx_completion(ha, status); 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; } @@ -2353,11 +2876,18 @@ static struct qla_init_msix_entry qla82xx_msix_entries[2] = { { "qla2xxx (rsp_q)", qla82xx_msix_rsp_q }, }; +static struct qla_init_msix_entry qla83xx_msix_entries[3] = { + { "qla2xxx (default)", qla24xx_msix_default }, + { "qla2xxx (rsp_q)", qla24xx_msix_rsp_q }, + { "qla2xxx (atio_q)", qla83xx_msix_atio_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]; @@ -2368,20 +2898,27 @@ qla24xx_disable_msix(struct qla_hw_data *ha) 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 +#define ATIO_VECTOR 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) + 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; @@ -2391,16 +2928,18 @@ qla24xx_enable_msix(struct qla_hw_data *ha, struct rsp_que *rsp) if (ret < MIN_MSIX_COUNT) goto msix_failed; - qla_printk(KERN_WARNING, ha, - "MSI-X: Failed to enable support -- %d/%d\n" - " Retry with %d vectors\n", ha->msix_count, ret, ret); + 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: - qla_printk(KERN_WARNING, ha, "MSI-X: Failed to enable" - " support, giving up -- %d/%d\n", - ha->msix_count, ret); + 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; @@ -2408,6 +2947,8 @@ msix_failed: 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; } @@ -2424,31 +2965,60 @@ msix_failed: /* Enable MSI-X vectors for the base queue */ for (i = 0; i < 2; i++) { qentry = &ha->msix_entries[i]; - if (IS_QLA82XX(ha)) { + if (IS_P3P_TYPE(ha)) ret = request_irq(qentry->vector, qla82xx_msix_entries[i].handler, 0, qla82xx_msix_entries[i].name, rsp); - } else { + else ret = request_irq(qentry->vector, msix_entries[i].handler, 0, msix_entries[i].name, rsp); - } - if (ret) { - qla_printk(KERN_WARNING, ha, - "MSI-X: Unable to register handler -- %x/%d.\n", - qentry->vector, ret); - qla24xx_disable_msix(ha); - ha->mqenable = 0; - goto msix_out; - } + if (ret) + goto msix_register_fail; qentry->have_irq = 1; qentry->rsp = rsp; rsp->msix = qentry; } + /* + * If target mode is enable, also request the vector for the ATIO + * queue. + */ + if (QLA_TGT_MODE_ENABLED() && IS_ATIO_MSIX_CAPABLE(ha)) { + qentry = &ha->msix_entries[ATIO_VECTOR]; + ret = request_irq(qentry->vector, + qla83xx_msix_entries[ATIO_VECTOR].handler, + 0, qla83xx_msix_entries[ATIO_VECTOR].name, rsp); + qentry->have_irq = 1; + qentry->rsp = rsp; + rsp->msix = qentry; + } + +msix_register_fail: + 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; + } + /* Enable MSI-X vector for response queue update for queue 0 */ - if (ha->mqiobase && (ha->max_rsp_queues > 1 || ha->max_req_queues > 1)) - ha->mqenable = 1; + if (IS_QLA83XX(ha) || IS_QLA27XX(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); @@ -2458,84 +3028,85 @@ msix_out: int qla2x00_request_irqs(struct qla_hw_data *ha, struct rsp_que *rsp) { - int ret; - device_reg_t __iomem *reg = ha->iobase; + int ret = QLA_FUNCTION_FAILED; + device_reg_t *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_QLA8XXX_TYPE(ha)) + if (!IS_QLA2432(ha) && !IS_QLA2532(ha) && !IS_QLA8432(ha) && + !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha) && !IS_QLAFX00(ha) && + !IS_QLA27XX(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)) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "MSI-X: Unsupported ISP2432 SSVID/SSDID (0x%X,0x%X).\n", + 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)); + ha->pdev->subsystem_device); goto skip_msi; } - if (IS_QLA2432(ha) && (ha->pdev->revision < QLA_MSIX_CHIP_REV_24XX || - !QLA_MSIX_FW_MODE_1(ha->fw_attributes))) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "MSI-X: Unsupported ISP2432 (0x%X, 0x%X).\n", - ha->pdev->revision, ha->fw_attributes)); + 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) { - DEBUG2(qla_printk(KERN_INFO, ha, - "MSI-X: Enabled (0x%X, 0x%X).\n", ha->chip_revision, - ha->fw_attributes)); + 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; } - qla_printk(KERN_WARNING, ha, - "MSI-X: Falling back-to MSI mode -- %d.\n", ret); + skip_msix: + ql_log(ql_log_info, vha, 0x0037, + "Falling back-to MSI mode -%d.\n", ret); + if (!IS_QLA24XX(ha) && !IS_QLA2532(ha) && !IS_QLA8432(ha) && - !IS_QLA8001(ha)) + !IS_QLA8001(ha) && !IS_P3P_TYPE(ha) && !IS_QLAFX00(ha) && + !IS_QLA27XX(ha)) goto skip_msi; ret = pci_enable_msi(ha->pdev); if (!ret) { - DEBUG2(qla_printk(KERN_INFO, ha, "MSI: Enabled.\n")); + ql_dbg(ql_dbg_init, vha, 0x0038, + "MSI: Enabled.\n"); ha->flags.msi_enabled = 1; } else - qla_printk(KERN_WARNING, ha, - "MSI-X: Falling back-to INTa mode -- %d.\n", ret); + ql_log(ql_log_warn, vha, 0x0039, + "Falling back-to INTa mode -- %d.\n", ret); skip_msi: + /* Skip INTx on ISP82xx. */ + if (!ha->flags.msi_enabled && IS_QLA82XX(ha)) + return QLA_FUNCTION_FAILED; + ret = request_irq(ha->pdev->irq, ha->isp_ops->intr_handler, ha->flags.msi_enabled ? 0 : IRQF_SHARED, QLA2XXX_DRIVER_NAME, rsp); if (ret) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x003a, "Failed to reserve interrupt %d already in use.\n", ha->pdev->irq); goto fail; + } else if (!ha->flags.msi_enabled) { + ql_dbg(ql_dbg_init, vha, 0x0125, + "INTa mode: Enabled.\n"); + ha->flags.mr_intr_valid = 1; } 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)) - goto fail; spin_lock_irq(&ha->hardware_lock); - if (IS_FWI2_CAPABLE(ha)) { - WRT_REG_DWORD(®->isp24.hccr, HCCRX_CLR_HOST_INT); - WRT_REG_DWORD(®->isp24.hccr, HCCRX_CLR_RISC_INT); - } else { + if (!IS_FWI2_CAPABLE(ha)) WRT_REG_WORD(®->isp.semaphore, 0); - WRT_REG_WORD(®->isp.hccr, HCCR_CLR_RISC_INT); - WRT_REG_WORD(®->isp.hccr, HCCR_CLR_HOST_INT); - } spin_unlock_irq(&ha->hardware_lock); fail: @@ -2546,7 +3117,15 @@ 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]; + struct rsp_que *rsp; + + /* + * We need to check that ha->rsp_q_map is valid in case we are called + * from a probe failure context. + */ + if (!ha->rsp_q_map || !ha->rsp_q_map[0]) + return; + rsp = ha->rsp_q_map[0]; if (ha->flags.msix_enabled) qla24xx_disable_msix(ha); @@ -2563,13 +3142,14 @@ 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) { - qla_printk(KERN_WARNING, ha, - "MSI-X: Unable to register handler -- %x/%d.\n", - msix->vector, 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; diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index c26f0acdfec..1c33a77db5c 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -1,10 +1,11 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ #include "qla_def.h" +#include "qla_target.h" #include <linux/delay.h> #include <linux/gfp.h> @@ -34,7 +35,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) { int rval; unsigned long flags = 0; - device_reg_t __iomem *reg; + device_reg_t *reg; uint8_t abort_active; uint8_t io_lock_on; uint16_t command = 0; @@ -46,14 +47,18 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) struct qla_hw_data *ha = vha->hw; scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); - if (ha->pdev->error_state > pci_channel_io_frozen) + 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) { - DEBUG2_3_11(qla_printk(KERN_WARNING, ha, - "%s(%ld): Device in failed state, " - "timeout MBX Exiting.\n", - __func__, base_vha->host_no)); + ql_log(ql_log_warn, vha, 0x1002, + "Device in failed state, exiting.\n"); return QLA_FUNCTION_TIMEOUT; } @@ -63,19 +68,19 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) rval = QLA_SUCCESS; abort_active = test_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags); - DEBUG11(printk("%s(%ld): entered.\n", __func__, base_vha->host_no)); if (ha->flags.pci_channel_io_perm_failure) { - DEBUG(printk("%s(%ld): Perm failure on EEH, timeout MBX " - "Exiting.\n", __func__, vha->host_no)); + 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) { + if (IS_P3P_TYPE(ha) && ha->flags.isp82xx_fw_hung) { /* Setting Link-Down error */ mcp->mb[0] = MBS_LINK_DOWN_ERROR; - rval = QLA_FUNCTION_FAILED; - goto premature_exit; + ql_log(ql_log_warn, vha, 0x1004, + "FW hung = %d.\n", ha->flags.isp82xx_fw_hung); + return QLA_FUNCTION_TIMEOUT; } /* @@ -85,8 +90,9 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) */ if (!wait_for_completion_timeout(&ha->mbx_cmd_comp, mcp->tov * HZ)) { /* Timeout occurred. Return error. */ - DEBUG2_3_11(printk("%s(%ld): cmd access timeout. " - "Exiting.\n", __func__, base_vha->host_no)); + ql_log(ql_log_warn, vha, 0x1005, + "Cmd access timeout, cmd=0x%x, Exiting.\n", + mcp->mb[0]); return QLA_FUNCTION_TIMEOUT; } @@ -94,15 +100,15 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) /* Save mailbox command for debug */ ha->mcp = mcp; - DEBUG11(printk("scsi(%ld): prepare to issue mbox cmd=0x%x.\n", - base_vha->host_no, mcp->mb[0])); + 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)) + if (IS_P3P_TYPE(ha)) optr = (uint16_t __iomem *)®->isp82.mailbox_in[0]; - else if (IS_FWI2_CAPABLE(ha) && !IS_QLA82XX(ha)) + else if (IS_FWI2_CAPABLE(ha) && !(IS_P3P_TYPE(ha))) optr = (uint16_t __iomem *)®->isp24.mailbox0; else optr = (uint16_t __iomem *)MAILBOX_REG(ha, ®->isp, 0); @@ -111,53 +117,48 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) command = mcp->mb[0]; mboxes = mcp->out_mb; + ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1111, + "Mailbox registers (OUT):\n"); for (cnt = 0; cnt < ha->mbx_count; cnt++) { if (IS_QLA2200(ha) && cnt == 8) optr = (uint16_t __iomem *)MAILBOX_REG(ha, ®->isp, 8); - if (mboxes & BIT_0) + if (mboxes & BIT_0) { + ql_dbg(ql_dbg_mbx, vha, 0x1112, + "mbox[%d]<-0x%04x\n", cnt, *iptr); WRT_REG_WORD(optr, *iptr); + } mboxes >>= 1; optr++; iptr++; } -#if defined(QL_DEBUG_LEVEL_1) - printk("%s(%ld): Loaded MBX registers (displayed in bytes) = \n", - __func__, base_vha->host_no); - qla2x00_dump_buffer((uint8_t *)mcp->mb, 16); - printk("\n"); - qla2x00_dump_buffer(((uint8_t *)mcp->mb + 0x10), 16); - printk("\n"); - qla2x00_dump_buffer(((uint8_t *)mcp->mb + 0x20), 8); - printk("\n"); - printk("%s(%ld): I/O address = %p.\n", __func__, base_vha->host_no, - optr); - qla2x00_dump_regs(base_vha); -#endif + ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1117, + "I/O Address = %p.\n", optr); /* 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 */ - DEBUG11(printk("%s(%ld): going to unlock irq & waiting for interrupt. " - "jiffies=%lx.\n", __func__, base_vha->host_no, jiffies)); + 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 (IS_P3P_TYPE(ha)) { if (RD_REG_DWORD(®->isp82.hint) & HINT_MBX_INT_PENDING) { spin_unlock_irqrestore(&ha->hardware_lock, flags); - DEBUG2_3_11(printk(KERN_INFO - "%s(%ld): Pending Mailbox timeout. " - "Exiting.\n", __func__, base_vha->host_no)); + ha->flags.mbox_busy = 0; + ql_dbg(ql_dbg_mbx, vha, 0x1010, + "Pending mailbox timeout, exiting.\n"); rval = QLA_FUNCTION_TIMEOUT; goto premature_exit; } @@ -168,22 +169,26 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) WRT_REG_WORD(®->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); - + if (!wait_for_completion_timeout(&ha->mbx_intr_comp, + mcp->tov * HZ)) { + ql_dbg(ql_dbg_mbx, vha, 0x117a, + "cmd=%x Timeout.\n", command); + spin_lock_irqsave(&ha->hardware_lock, flags); + clear_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + } } else { - DEBUG3_11(printk("%s(%ld): cmd=%x POLLING MODE.\n", __func__, - base_vha->host_no, command)); + ql_dbg(ql_dbg_mbx, vha, 0x1011, + "Cmd=%x Polling Mode.\n", command); - if (IS_QLA82XX(ha)) { + if (IS_P3P_TYPE(ha)) { if (RD_REG_DWORD(®->isp82.hint) & HINT_MBX_INT_PENDING) { spin_unlock_irqrestore(&ha->hardware_lock, flags); - DEBUG2_3_11(printk(KERN_INFO - "%s(%ld): Pending Mailbox timeout. " - "Exiting.\n", __func__, base_vha->host_no)); + ha->flags.mbox_busy = 0; + ql_dbg(ql_dbg_mbx, vha, 0x1012, + "Pending mailbox timeout, exiting.\n"); rval = QLA_FUNCTION_TIMEOUT; goto premature_exit; } @@ -207,28 +212,30 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) command == MBC_LOAD_RISC_RAM_EXTENDED)) msleep(10); } /* while */ - DEBUG17(qla_printk(KERN_WARNING, ha, - "Waited %d sec\n", - (uint)((jiffies - (wait_time - (mcp->tov * HZ)))/HZ))); + 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; - DEBUG3_11(printk("%s(%ld): cmd %x completed.\n", __func__, - base_vha->host_no, command)); + 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) { + if (IS_P3P_TYPE(ha) && 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; } @@ -239,9 +246,15 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) iptr2 = mcp->mb; iptr = (uint16_t *)&ha->mailbox_out[0]; mboxes = mcp->in_mb; + + ql_dbg(ql_dbg_mbx, vha, 0x1113, + "Mailbox registers (IN):\n"); for (cnt = 0; cnt < ha->mbx_count; cnt++) { - if (mboxes & BIT_0) + if (mboxes & BIT_0) { *iptr2 = *iptr; + ql_dbg(ql_dbg_mbx, vha, 0x1114, + "mbox[%d]->0x%04x\n", cnt, *iptr2); + } mboxes >>= 1; iptr2++; @@ -249,8 +262,6 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) } } else { -#if defined(QL_DEBUG_LEVEL_2) || defined(QL_DEBUG_LEVEL_3) || \ - defined(QL_DEBUG_LEVEL_11) uint16_t mb0; uint32_t ictrl; @@ -261,14 +272,18 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) mb0 = RD_MAILBOX_REG(ha, ®->isp, 0); ictrl = RD_REG_WORD(®->isp.ictrl); } - printk("%s(%ld): **** MB Command Timeout for cmd %x ****\n", - __func__, base_vha->host_no, command); - printk("%s(%ld): icontrol=%x jiffies=%lx\n", __func__, - base_vha->host_no, ictrl, jiffies); - printk("%s(%ld): *** mailbox[0] = 0x%x ***\n", __func__, - base_vha->host_no, mb0); - qla2x00_dump_regs(base_vha); -#endif + ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1119, + "MBX Command timeout for cmd %x, iocontrol=%x jiffies=%lx " + "mb[0]=0x%x\n", command, ictrl, jiffies, 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. We do not need to do this + * if we are intentionally generating a dump. + */ + if (mcp->mb[0] != MBC_GEN_SYSTEM_ERROR) + ha->isp_ops->fw_dump(vha, 0); rval = QLA_FUNCTION_TIMEOUT; } @@ -279,8 +294,8 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) ha->mcp = NULL; if ((abort_active || !io_lock_on) && !IS_NOPOLLING_TYPE(ha)) { - DEBUG11(printk("%s(%ld): checking for additional resp " - "interrupt.\n", __func__, base_vha->host_no)); + 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]); @@ -291,52 +306,63 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) if (!io_lock_on || (mcp->flags & IOCTL_CMD) || ha->flags.eeh_busy) { /* not in dpc. schedule it for dpc to take over. */ - DEBUG(printk("%s(%ld): timeout schedule " - "isp_abort_needed.\n", __func__, - base_vha->host_no)); - DEBUG2_3_11(printk("%s(%ld): timeout schedule " - "isp_abort_needed.\n", __func__, - base_vha->host_no)); + 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)) { - - qla_printk(KERN_WARNING, ha, - "Mailbox command timeout occurred. " - "Scheduling ISP " "abort. eeh_busy: 0x%x\n", + 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 occurred, 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 */ - DEBUG(printk("%s(%ld): timeout calling abort_isp\n", - __func__, base_vha->host_no)); - DEBUG2_3_11(printk("%s(%ld): timeout calling " - "abort_isp\n", __func__, base_vha->host_no)); + 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)) { - - qla_printk(KERN_WARNING, ha, - "Mailbox command timeout occurred. " - "Issuing ISP abort.\n"); - + 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 occurred, 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); - DEBUG(printk("%s(%ld): finished abort_isp\n", - __func__, vha->host_no)); - DEBUG2_3_11(printk( - "%s(%ld): finished abort_isp\n", - __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx, vha, 0x101f, + "Finished abort_isp.\n"); + goto mbx_done; } } } @@ -345,13 +371,13 @@ premature_exit: /* Allow next mbx cmd to come in. */ complete(&ha->mbx_cmd_comp); +mbx_done: if (rval) { - DEBUG2_3_11(printk("%s(%ld): **** FAILED. mbx0=%x, mbx1=%x, " - "mbx2=%x, cmd=%x ****\n", __func__, base_vha->host_no, - mcp->mb[0], mcp->mb[1], mcp->mb[2], command)); + ql_log(ql_log_warn, 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 { - DEBUG11(printk("%s(%ld): done.\n", __func__, - base_vha->host_no)); + ql_dbg(ql_dbg_mbx, base_vha, 0x1021, "Done %s.\n", __func__); } return rval; @@ -366,7 +392,8 @@ qla2x00_load_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t risc_addr, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1022, + "Entered %s.\n", __func__); if (MSW(risc_addr) || IS_FWI2_CAPABLE(ha)) { mcp->mb[0] = MBC_LOAD_RISC_RAM_EXTENDED; @@ -397,10 +424,11 @@ qla2x00_load_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t risc_addr, rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__, - vha->host_no, rval, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x1023, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1024, + "Done %s.\n", __func__); } return rval; @@ -430,7 +458,8 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1025, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_EXECUTE_FIRMWARE; mcp->out_mb = MBX_0; @@ -439,7 +468,8 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr) mcp->mb[1] = MSW(risc_addr); mcp->mb[2] = LSW(risc_addr); mcp->mb[3] = 0; - if (IS_QLA81XX(ha)) { + if (IS_QLA25XX(ha) || IS_QLA81XX(ha) || IS_QLA83XX(ha) || + IS_QLA27XX(ha)) { struct nvram_81xx *nv = ha->nvram; mcp->mb[4] = (nv->enhanced_features & EXTENDED_BB_CREDITS); @@ -461,15 +491,15 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr) rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__, - vha->host_no, rval, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x1026, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); } else { if (IS_FWI2_CAPABLE(ha)) { - DEBUG11(printk("%s(%ld): done exchanges=%x.\n", - __func__, vha->host_no, mcp->mb[1])); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1027, + "Done exchanges=%x.\n", mcp->mb[1]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, - vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1028, + "Done %s.\n", __func__); } } @@ -493,21 +523,25 @@ qla2x00_execute_fw(scsi_qla_host_t *vha, uint32_t risc_addr) * Kernel context. */ int -qla2x00_get_fw_version(scsi_qla_host_t *vha, uint16_t *major, uint16_t *minor, - uint16_t *subminor, uint16_t *attributes, uint32_t *memory, uint8_t *mpi, - uint32_t *mpi_caps, uint8_t *phy) +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; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, 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)) + if (IS_QLA81XX(vha->hw) || IS_QLA8031(ha) || IS_QLA8044(ha)) mcp->in_mb |= MBX_13|MBX_12|MBX_11|MBX_10|MBX_9|MBX_8; + if (IS_FWI2_CAPABLE(ha)) + mcp->in_mb |= MBX_17|MBX_16|MBX_15; + if (IS_QLA27XX(ha)) + mcp->in_mb |= MBX_21|MBX_20|MBX_19|MBX_18; mcp->flags = 0; mcp->tov = MBX_TOV_SECONDS; rval = qla2x00_mailbox_command(vha, mcp); @@ -515,31 +549,47 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha, uint16_t *major, uint16_t *minor, goto failed; /* Return mailbox data. */ - *major = mcp->mb[1]; - *minor = mcp->mb[2]; - *subminor = mcp->mb[3]; - *attributes = mcp->mb[6]; + 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)) - *memory = 0x1FFFF; /* Defaults to 128KB. */ + ha->fw_memory_size = 0x1FFFF; /* Defaults to 128KB. */ else - *memory = (mcp->mb[5] << 16) | mcp->mb[4]; - if (IS_QLA81XX(vha->hw)) { - mpi[0] = mcp->mb[10] & 0xff; - mpi[1] = mcp->mb[11] >> 8; - mpi[2] = mcp->mb[11] & 0xff; - *mpi_caps = (mcp->mb[12] << 16) | mcp->mb[13]; - phy[0] = mcp->mb[8] & 0xff; - phy[1] = mcp->mb[9] >> 8; - phy[2] = mcp->mb[9] & 0xff; + ha->fw_memory_size = (mcp->mb[5] << 16) | mcp->mb[4]; + if (IS_QLA81XX(vha->hw) || IS_QLA8031(vha->hw) || IS_QLA8044(ha)) { + 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_FWI2_CAPABLE(ha)) { + 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 + ql_dbg_verbose, vha, 0x1139, + "%s: FW_attributes Upper: 0x%x, Lower: 0x%x.\n", + __func__, mcp->mb[15], mcp->mb[6]); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x112f, + "%s: Ext_FwAttributes Upper: 0x%x, Lower: 0x%x.\n", + __func__, mcp->mb[17], mcp->mb[16]); + } + if (IS_QLA27XX(ha)) { + ha->fw_shared_ram_start = (mcp->mb[19] << 16) | mcp->mb[18]; + ha->fw_shared_ram_end = (mcp->mb[21] << 16) | mcp->mb[20]; + } + failed: if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, - vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x102a, "Failed=%x.\n", rval); } else { /*EMPTY*/ - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x102b, + "Done %s.\n", __func__); } return rval; } @@ -565,7 +615,8 @@ qla2x00_get_fw_options(scsi_qla_host_t *vha, uint16_t *fwopts) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x102c, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_GET_FIRMWARE_OPTION; mcp->out_mb = MBX_0; @@ -576,15 +627,15 @@ qla2x00_get_fw_options(scsi_qla_host_t *vha, uint16_t *fwopts) if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, - vha->host_no, rval)); + 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]; - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x102e, + "Done %s.\n", __func__); } return rval; @@ -612,7 +663,8 @@ qla2x00_set_fw_options(scsi_qla_host_t *vha, uint16_t *fwopts) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x102f, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_SET_FIRMWARE_OPTION; mcp->mb[1] = fwopts[1]; @@ -636,11 +688,12 @@ qla2x00_set_fw_options(scsi_qla_host_t *vha, uint16_t *fwopts) if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("%s(%ld): failed=%x (%x/%x).\n", __func__, - vha->host_no, rval, mcp->mb[0], mcp->mb[1])); + ql_dbg(ql_dbg_mbx, vha, 0x1030, + "Failed=%x (%x/%x).\n", rval, mcp->mb[0], mcp->mb[1]); } else { /*EMPTY*/ - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1031, + "Done %s.\n", __func__); } return rval; @@ -668,7 +721,8 @@ qla2x00_mbx_reg_test(scsi_qla_host_t *vha) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("qla2x00_mbx_reg_test(%ld): entered.\n", vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1032, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_MAILBOX_REGISTER_TEST; mcp->mb[1] = 0xAAAA; @@ -695,12 +749,11 @@ qla2x00_mbx_reg_test(scsi_qla_host_t *vha) if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("qla2x00_mbx_reg_test(%ld): failed=%x.\n", - vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x1033, "Failed=%x.\n", rval); } else { /*EMPTY*/ - DEBUG11(printk("qla2x00_mbx_reg_test(%ld): done.\n", - vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1034, + "Done %s.\n", __func__); } return rval; @@ -728,7 +781,8 @@ qla2x00_verify_checksum(scsi_qla_host_t *vha, uint32_t risc_addr) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1035, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_VERIFY_CHECKSUM; mcp->out_mb = MBX_0; @@ -749,11 +803,12 @@ qla2x00_verify_checksum(scsi_qla_host_t *vha, uint32_t risc_addr) rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x chk sum=%x.\n", __func__, - vha->host_no, rval, IS_FWI2_CAPABLE(vha->hw) ? - (mcp->mb[2] << 16) | mcp->mb[1]: mcp->mb[1])); + 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 { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1037, + "Done %s.\n", __func__); } return rval; @@ -785,6 +840,9 @@ qla2x00_issue_iocb_timeout(scsi_qla_host_t *vha, void *buffer, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1038, + "Entered %s.\n", __func__); + mcp->mb[0] = MBC_IOCB_COMMAND_A64; mcp->mb[1] = 0; mcp->mb[2] = MSW(phys_addr); @@ -799,14 +857,15 @@ qla2x00_issue_iocb_timeout(scsi_qla_host_t *vha, void *buffer, if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG(printk("qla2x00_issue_iocb(%ld): failed rval 0x%x\n", - vha->host_no, rval)); + 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 + ql_dbg_verbose, vha, 0x103a, + "Done %s.\n", __func__); } return rval; @@ -846,17 +905,19 @@ qla2x00_abort_command(srb_t *sp) 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); - DEBUG11(printk("qla2x00_abort_command(%ld): entered.\n", vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x103b, + "Entered %s.\n", __func__); spin_lock_irqsave(&ha->hardware_lock, flags); - for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) { + for (handle = 1; handle < req->num_outstanding_cmds; handle++) { if (req->outstanding_cmds[handle] == sp) break; } spin_unlock_irqrestore(&ha->hardware_lock, flags); - if (handle == MAX_OUTSTANDING_COMMANDS) { + if (handle == req->num_outstanding_cmds) { /* command not found */ return QLA_FUNCTION_FAILED; } @@ -868,7 +929,7 @@ qla2x00_abort_command(srb_t *sp) 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)sp->cmd->device->lun; + 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; @@ -876,11 +937,10 @@ qla2x00_abort_command(srb_t *sp) rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("qla2x00_abort_command(%ld): failed=%x.\n", - vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x103c, "Failed=%x.\n", rval); } else { - DEBUG11(printk("qla2x00_abort_command(%ld): done.\n", - vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x103d, + "Done %s.\n", __func__); } return rval; @@ -896,10 +956,12 @@ qla2x00_abort_target(struct fc_port *fcport, unsigned int l, int tag) struct req_que *req; struct rsp_que *rsp; - DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->vha->host_no)); - l = l; vha = fcport->vha; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x103e, + "Entered %s.\n", __func__); + req = vha->hw->req_q_map[0]; rsp = req->rsp; mcp->mb[0] = MBC_ABORT_TARGET; @@ -919,18 +981,19 @@ qla2x00_abort_target(struct fc_port *fcport, unsigned int l, int tag) mcp->flags = 0; rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, - vha->host_no, rval)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, 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) { - DEBUG2_3_11(printk("%s(%ld): failed to issue Marker IOCB " - "(%x).\n", __func__, vha->host_no, rval2)); + ql_dbg(ql_dbg_mbx, vha, 0x1040, + "Failed to issue marker IOCB (%x).\n", rval2); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1041, + "Done %s.\n", __func__); } return rval; @@ -946,9 +1009,11 @@ qla2x00_lun_reset(struct fc_port *fcport, unsigned int l, int tag) struct req_que *req; struct rsp_que *rsp; - DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->vha->host_no)); - vha = fcport->vha; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1042, + "Entered %s.\n", __func__); + req = vha->hw->req_q_map[0]; rsp = req->rsp; mcp->mb[0] = MBC_LUN_RESET; @@ -966,18 +1031,18 @@ qla2x00_lun_reset(struct fc_port *fcport, unsigned int l, int tag) mcp->flags = 0; rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, - vha->host_no, rval)); + 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) { - DEBUG2_3_11(printk("%s(%ld): failed to issue Marker IOCB " - "(%x).\n", __func__, vha->host_no, rval2)); + ql_dbg(ql_dbg_mbx, vha, 0x1044, + "Failed to issue marker IOCB (%x).\n", rval2); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1045, + "Done %s.\n", __func__); } return rval; @@ -1011,14 +1076,14 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("qla2x00_get_adapter_id(%ld): entered.\n", - vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, 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_QLA8XXX_TYPE(vha->hw)) + 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; @@ -1038,13 +1103,12 @@ qla2x00_get_adapter_id(scsi_qla_host_t *vha, uint16_t *id, uint8_t *al_pa, if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("qla2x00_get_adapter_id(%ld): failed=%x.\n", - vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x1047, "Failed=%x.\n", rval); } else { - DEBUG11(printk("qla2x00_get_adapter_id(%ld): done.\n", - vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1048, + "Done %s.\n", __func__); - if (IS_QLA8XXX_TYPE(vha->hw)) { + 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; @@ -1083,8 +1147,8 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *vha, uint8_t *retry_cnt, uint8_t *tov, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("qla2x00_get_retry_cnt(%ld): entered.\n", - vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1049, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_GET_RETRY_COUNT; mcp->out_mb = MBX_0; @@ -1095,8 +1159,8 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *vha, uint8_t *retry_cnt, uint8_t *tov, if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("qla2x00_get_retry_cnt(%ld): failed = %x.\n", - vha->host_no, mcp->mb[0])); + 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; @@ -1107,8 +1171,8 @@ qla2x00_get_retry_cnt(scsi_qla_host_t *vha, uint8_t *retry_cnt, uint8_t *tov, *tov = ratov; } - DEBUG11(printk("qla2x00_get_retry_cnt(%ld): done. mb3=%d " - "ratov=%d.\n", vha->host_no, mcp->mb[3], ratov)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x104b, + "Done %s mb3=%d ratov=%d.\n", __func__, mcp->mb[3], ratov); } return rval; @@ -1139,10 +1203,10 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size) mbx_cmd_t *mcp = &mc; struct qla_hw_data *ha = vha->hw; - DEBUG11(printk("qla2x00_init_firmware(%ld): entered.\n", - vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x104c, + "Entered %s.\n", __func__); - if (IS_QLA82XX(ha) && ql2xdbwr) + if (IS_P3P_TYPE(ha) && ql2xdbwr) qla82xx_wr_32(ha, ha->nxdb_wr_ptr, (0x04 | (ha->portnum << 5) | (0 << 8) | (0 << 16))); @@ -1157,7 +1221,7 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size) 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) && ha->ex_init_cb->ex_version) { + if (ha->ex_init_cb && 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); @@ -1166,7 +1230,11 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size) mcp->mb[14] = sizeof(*ha->ex_init_cb); mcp->out_mb |= MBX_14|MBX_13|MBX_12|MBX_11|MBX_10; } - mcp->in_mb = MBX_0; + /* 1 and 2 should normally be captured. */ + mcp->in_mb = MBX_2|MBX_1|MBX_0; + if (IS_QLA83XX(ha) || IS_QLA27XX(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; @@ -1174,15 +1242,104 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size) if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("qla2x00_init_firmware(%ld): failed=%x " - "mb0=%x.\n", - vha->host_no, rval, mcp->mb[0])); + 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*/ - DEBUG11(printk("qla2x00_init_firmware(%ld): done.\n", - vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x104e, + "Done %s.\n", __func__); + } + + return rval; +} + +/* + * qla2x00_get_node_name_list + * Issue get node name list mailbox command, kmalloc() + * and return the resulting list. Caller must kfree() it! + * + * Input: + * ha = adapter state pointer. + * out_data = resulting list + * out_len = length of the resulting list + * + * Returns: + * qla2x00 local function return status code. + * + * Context: + * Kernel context. + */ +int +qla2x00_get_node_name_list(scsi_qla_host_t *vha, void **out_data, int *out_len) +{ + struct qla_hw_data *ha = vha->hw; + struct qla_port_24xx_data *list = NULL; + void *pmap; + mbx_cmd_t mc; + dma_addr_t pmap_dma; + ulong dma_size; + int rval, left; + + left = 1; + while (left > 0) { + dma_size = left * sizeof(*list); + pmap = dma_alloc_coherent(&ha->pdev->dev, dma_size, + &pmap_dma, GFP_KERNEL); + if (!pmap) { + ql_log(ql_log_warn, vha, 0x113f, + "%s(%ld): DMA Alloc failed of %ld\n", + __func__, vha->host_no, dma_size); + rval = QLA_MEMORY_ALLOC_FAILED; + goto out; + } + + mc.mb[0] = MBC_PORT_NODE_NAME_LIST; + mc.mb[1] = BIT_1 | BIT_3; + mc.mb[2] = MSW(pmap_dma); + mc.mb[3] = LSW(pmap_dma); + mc.mb[6] = MSW(MSD(pmap_dma)); + mc.mb[7] = LSW(MSD(pmap_dma)); + mc.mb[8] = dma_size; + mc.out_mb = MBX_0|MBX_1|MBX_2|MBX_3|MBX_6|MBX_7|MBX_8; + mc.in_mb = MBX_0|MBX_1; + mc.tov = 30; + mc.flags = MBX_DMA_IN; + + rval = qla2x00_mailbox_command(vha, &mc); + if (rval != QLA_SUCCESS) { + if ((mc.mb[0] == MBS_COMMAND_ERROR) && + (mc.mb[1] == 0xA)) { + left += le16_to_cpu(mc.mb[2]) / + sizeof(struct qla_port_24xx_data); + goto restart; + } + goto out_free; + } + + left = 0; + + list = kmemdup(pmap, dma_size, GFP_KERNEL); + if (!list) { + ql_log(ql_log_warn, vha, 0x1140, + "%s(%ld): failed to allocate node names list " + "structure.\n", __func__, vha->host_no); + rval = QLA_MEMORY_ALLOC_FAILED; + goto out_free; + } + +restart: + dma_free_coherent(&ha->pdev->dev, dma_size, pmap, pmap_dma); } + *out_data = list; + *out_len = dma_size; + +out: + return rval; + +out_free: + dma_free_coherent(&ha->pdev->dev, dma_size, pmap, pmap_dma); return rval; } @@ -1213,13 +1370,14 @@ qla2x00_get_port_database(scsi_qla_host_t *vha, fc_port_t *fcport, uint8_t opt) dma_addr_t pd_dma; struct qla_hw_data *ha = vha->hw; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x104f, + "Entered %s.\n", __func__); pd24 = NULL; pd = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &pd_dma); if (pd == NULL) { - DEBUG2_3(printk("%s(%ld): failed to allocate Port Database " - "structure.\n", __func__, vha->host_no)); + 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)); @@ -1256,21 +1414,28 @@ qla2x00_get_port_database(scsi_qla_host_t *vha, fc_port_t *fcport, uint8_t opt) 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) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "scsi(%ld): Unable to verify login-state (%x/%x) " - " - portid=%02x%02x%02x.\n", vha->host_no, - pd24->current_login_state, pd24->last_login_state, - fcport->d_id.b.domain, fcport->d_id.b.area, - fcport->d_id.b.al_pa)); + 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); @@ -1286,20 +1451,36 @@ qla2x00_get_port_database(scsi_qla_host_t *vha, fc_port_t *fcport, uint8_t opt) fcport->port_type = FCT_INITIATOR; else fcport->port_type = FCT_TARGET; + + /* Passback COS information. */ + fcport->supported_classes = (pd24->flags & PDF_CLASS_2) ? + FC_COS_CLASS2 : FC_COS_CLASS3; + + if (pd24->prli_svc_param_word_3[0] & BIT_7) + fcport->flags |= FCF_CONF_COMP_SUPPORTED; } 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) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "scsi(%ld): Unable to verify login-state (%x/%x) " - " - portid=%02x%02x%02x.\n", vha->host_no, - pd->master_state, pd->slave_state, - fcport->d_id.b.domain, fcport->d_id.b.area, - fcport->d_id.b.al_pa)); + 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); @@ -1325,10 +1506,12 @@ gpd_error_out: dma_pool_free(ha->s_dma_pool, pd, pd_dma); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n", - __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1])); + ql_dbg(ql_dbg_mbx, vha, 0x1052, + "Failed=%x mb[0]=%x mb[1]=%x.\n", rval, + mcp->mb[0], mcp->mb[1]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1053, + "Done %s.\n", __func__); } return rval; @@ -1357,8 +1540,8 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("qla2x00_get_firmware_state(%ld): entered.\n", - vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1054, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_GET_FIRMWARE_STATE; mcp->out_mb = MBX_0; @@ -1381,12 +1564,11 @@ qla2x00_get_firmware_state(scsi_qla_host_t *vha, uint16_t *states) if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("qla2x00_get_firmware_state(%ld): " - "failed=%x.\n", vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x1055, "Failed=%x.\n", rval); } else { /*EMPTY*/ - DEBUG11(printk("qla2x00_get_firmware_state(%ld): done.\n", - vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1056, + "Done %s.\n", __func__); } return rval; @@ -1418,8 +1600,8 @@ qla2x00_get_port_name(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t *name, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("qla2x00_get_port_name(%ld): entered.\n", - vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1057, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_GET_PORT_NAME; mcp->mb[9] = vha->vp_idx; @@ -1439,8 +1621,7 @@ qla2x00_get_port_name(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t *name, if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("qla2x00_get_port_name(%ld): failed=%x.\n", - vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x1058, "Failed=%x.\n", rval); } else { if (name != NULL) { /* This function returns name in big endian. */ @@ -1454,8 +1635,60 @@ qla2x00_get_port_name(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t *name, name[7] = LSB(mcp->mb[7]); } - DEBUG11(printk("qla2x00_get_port_name(%ld): done.\n", - vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1059, + "Done %s.\n", __func__); + } + + return rval; +} + +/* + * qla24xx_link_initialization + * Issue link initialization 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 +qla24xx_link_initialize(scsi_qla_host_t *vha) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1152, + "Entered %s.\n", __func__); + + if (!IS_FWI2_CAPABLE(vha->hw) || IS_CNA_CAPABLE(vha->hw)) + return QLA_FUNCTION_FAILED; + + mcp->mb[0] = MBC_LINK_INITIALIZATION; + mcp->mb[1] = BIT_4; + if (vha->hw->operating_mode == LOOP) + mcp->mb[1] |= BIT_6; + else + mcp->mb[1] |= BIT_5; + 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) { + ql_dbg(ql_dbg_mbx, vha, 0x1153, "Failed=%x.\n", rval); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1154, + "Done %s.\n", __func__); } return rval; @@ -1483,9 +1716,10 @@ qla2x00_lip_reset(scsi_qla_host_t *vha) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x105a, + "Entered %s.\n", __func__); - if (IS_QLA8XXX_TYPE(vha->hw)) { + if (IS_CNA_CAPABLE(vha->hw)) { /* Logout across all FCFs. */ mcp->mb[0] = MBC_LIP_FULL_LOGIN; mcp->mb[1] = BIT_1; @@ -1517,11 +1751,11 @@ qla2x00_lip_reset(scsi_qla_host_t *vha) if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", - __func__, vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x105b, "Failed=%x.\n", rval); } else { /*EMPTY*/ - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x105c, + "Done %s.\n", __func__); } return rval; @@ -1553,12 +1787,12 @@ qla2x00_send_sns(scsi_qla_host_t *vha, dma_addr_t sns_phys_address, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("qla2x00_send_sns(%ld): entered.\n", - vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x105d, + "Entered %s.\n", __func__); - DEBUG11(printk("qla2x00_send_sns: retry cnt=%d ratov=%d total " - "tov=%d.\n", vha->hw->retry_count, vha->hw->login_timeout, - mcp->tov)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, 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; @@ -1575,13 +1809,13 @@ qla2x00_send_sns(scsi_qla_host_t *vha, dma_addr_t sns_phys_address, if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG(printk("qla2x00_send_sns(%ld): failed=%x mb[0]=%x " - "mb[1]=%x.\n", vha->host_no, rval, mcp->mb[0], mcp->mb[1])); - DEBUG2_3_11(printk("qla2x00_send_sns(%ld): failed=%x mb[0]=%x " - "mb[1]=%x.\n", vha->host_no, rval, mcp->mb[0], mcp->mb[1])); + 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*/ - DEBUG11(printk("qla2x00_send_sns(%ld): done.\n", vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1060, + "Done %s.\n", __func__); } return rval; @@ -1600,7 +1834,8 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, struct req_que *req; struct rsp_que *rsp; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1061, + "Entered %s.\n", __func__); if (ha->flags.cpu_affinity_enabled) req = ha->req_q_map[0]; @@ -1610,8 +1845,8 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, lg = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma); if (lg == NULL) { - DEBUG2_3(printk("%s(%ld): failed to allocate Login IOCB.\n", - __func__, vha->host_no)); + 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)); @@ -1629,23 +1864,24 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, lg->port_id[1] = area; lg->port_id[2] = domain; lg->vp_index = vha->vp_idx; - rval = qla2x00_issue_iocb(vha, lg, lg_dma, 0); + rval = qla2x00_issue_iocb_timeout(vha, lg, lg_dma, 0, + (ha->r_a_tov / 10 * 2) + 2); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed to issue Login IOCB " - "(%x).\n", __func__, vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x1063, + "Failed to issue login IOCB (%x).\n", rval); } else if (lg->entry_status != 0) { - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- error status (%x).\n", __func__, vha->host_no, - lg->entry_status)); + 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]); - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- completion status (%x) ioparam=%x/%x.\n", __func__, - vha->host_no, le16_to_cpu(lg->comp_status), iop[0], - iop[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: @@ -1673,7 +1909,8 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, break; } } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1066, + "Done %s.\n", __func__); iop[0] = le32_to_cpu(lg->io_parameter[0]); @@ -1691,6 +1928,10 @@ qla24xx_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, mb[10] |= BIT_0; /* Class 2. */ if (lg->io_parameter[9] || lg->io_parameter[10]) mb[10] |= BIT_1; /* Class 3. */ + if (lg->io_parameter[0] & __constant_cpu_to_le32(BIT_7)) + mb[10] |= BIT_7; /* Confirmed Completion + * Allowed + */ } dma_pool_free(ha->s_dma_pool, lg, lg_dma); @@ -1728,7 +1969,8 @@ qla2x00_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, mbx_cmd_t *mcp = &mc; struct qla_hw_data *ha = vha->hw; - DEBUG11(printk("qla2x00_login_fabric(%ld): entered.\n", vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1067, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_LOGIN_FABRIC_PORT; mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0; @@ -1771,13 +2013,13 @@ qla2x00_login_fabric(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, rval = QLA_SUCCESS; /*EMPTY*/ - DEBUG2_3_11(printk("qla2x00_login_fabric(%ld): failed=%x " - "mb[0]=%x mb[1]=%x mb[2]=%x.\n", vha->host_no, rval, - mcp->mb[0], mcp->mb[1], mcp->mb[2])); + 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*/ - DEBUG11(printk("qla2x00_login_fabric(%ld): done.\n", - vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1069, + "Done %s.\n", __func__); } return rval; @@ -1808,13 +2050,14 @@ qla2x00_login_local_device(scsi_qla_host_t *vha, fc_port_t *fcport, mbx_cmd_t *mcp = &mc; struct qla_hw_data *ha = vha->hw; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, 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); - DEBUG3(printk("%s(%ld): entered.\n", __func__, vha->host_no)); - mcp->mb[0] = MBC_LOGIN_LOOP_PORT; if (HAS_EXTENDED_IDS(ha)) mcp->mb[1] = fcport->loop_id; @@ -1845,15 +2088,13 @@ qla2x00_login_local_device(scsi_qla_host_t *vha, fc_port_t *fcport, if (mcp->mb[0] == 0x4005 || mcp->mb[0] == 0x4006) rval = QLA_SUCCESS; - DEBUG(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x " - "mb[6]=%x mb[7]=%x.\n", __func__, vha->host_no, rval, - mcp->mb[0], mcp->mb[1], mcp->mb[6], mcp->mb[7])); - DEBUG2_3(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x " - "mb[6]=%x mb[7]=%x.\n", __func__, vha->host_no, rval, - mcp->mb[0], mcp->mb[1], mcp->mb[6], mcp->mb[7])); + 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*/ - DEBUG3(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x106c, + "Done %s.\n", __func__); } return (rval); @@ -1870,12 +2111,13 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, struct req_que *req; struct rsp_que *rsp; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x106d, + "Entered %s.\n", __func__); lg = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &lg_dma); if (lg == NULL) { - DEBUG2_3(printk("%s(%ld): failed to allocate Logout IOCB.\n", - __func__, vha->host_no)); + 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)); @@ -1896,25 +2138,26 @@ qla24xx_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, lg->port_id[1] = area; lg->port_id[2] = domain; lg->vp_index = vha->vp_idx; - - rval = qla2x00_issue_iocb(vha, lg, lg_dma, 0); + rval = qla2x00_issue_iocb_timeout(vha, lg, lg_dma, 0, + (ha->r_a_tov / 10 * 2) + 2); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed to issue Logout IOCB " - "(%x).\n", __func__, vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x106f, + "Failed to issue logout IOCB (%x).\n", rval); } else if (lg->entry_status != 0) { - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- error status (%x).\n", __func__, vha->host_no, - lg->entry_status)); + 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)) { - DEBUG2_3_11(printk("%s(%ld %d): failed to complete IOCB " - "-- completion status (%x) ioparam=%x/%x.\n", __func__, - vha->host_no, vha->vp_idx, le16_to_cpu(lg->comp_status), + 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]))); + le32_to_cpu(lg->io_parameter[1])); } else { /*EMPTY*/ - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1072, + "Done %s.\n", __func__); } dma_pool_free(ha->s_dma_pool, lg, lg_dma); @@ -1946,8 +2189,8 @@ qla2x00_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("qla2x00_fabric_logout(%ld): entered.\n", - vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1073, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_LOGOUT_FABRIC_PORT; mcp->out_mb = MBX_1|MBX_0; @@ -1966,12 +2209,12 @@ qla2x00_fabric_logout(scsi_qla_host_t *vha, uint16_t loop_id, uint8_t domain, if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("qla2x00_fabric_logout(%ld): failed=%x " - "mbx1=%x.\n", vha->host_no, rval, mcp->mb[1])); + ql_dbg(ql_dbg_mbx, vha, 0x1074, + "Failed=%x mb[1]=%x.\n", rval, mcp->mb[1]); } else { /*EMPTY*/ - DEBUG11(printk("qla2x00_fabric_logout(%ld): done.\n", - vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1075, + "Done %s.\n", __func__); } return rval; @@ -1999,8 +2242,8 @@ qla2x00_full_login_lip(scsi_qla_host_t *vha) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("qla2x00_full_login_lip(%ld): entered.\n", - vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1076, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_LIP_FULL_LOGIN; mcp->mb[1] = IS_FWI2_CAPABLE(vha->hw) ? BIT_3 : 0; @@ -2014,12 +2257,11 @@ qla2x00_full_login_lip(scsi_qla_host_t *vha) if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("qla2x00_full_login_lip(%ld): failed=%x.\n", - vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x1077, "Failed=%x.\n", rval); } else { /*EMPTY*/ - DEBUG11(printk("qla2x00_full_login_lip(%ld): done.\n", - vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1078, + "Done %s.\n", __func__); } return rval; @@ -2045,8 +2287,8 @@ qla2x00_get_id_list(scsi_qla_host_t *vha, void *id_list, dma_addr_t id_list_dma, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("qla2x00_get_id_list(%ld): entered.\n", - vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1079, + "Entered %s.\n", __func__); if (id_list == NULL) return QLA_FUNCTION_FAILED; @@ -2075,12 +2317,11 @@ qla2x00_get_id_list(scsi_qla_host_t *vha, void *id_list, dma_addr_t id_list_dma, if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("qla2x00_get_id_list(%ld): failed=%x.\n", - vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x107a, "Failed=%x.\n", rval); } else { *entries = mcp->mb[1]; - DEBUG11(printk("qla2x00_get_id_list(%ld): done.\n", - vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x107b, + "Done %s.\n", __func__); } return rval; @@ -2108,12 +2349,13 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, 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)) + if (IS_QLA81XX(vha->hw) || IS_QLA83XX(vha->hw) || IS_QLA27XX(vha->hw)) mcp->in_mb |= MBX_12; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; @@ -2121,14 +2363,14 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt, if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("%s(%ld): failed = %x.\n", __func__, - vha->host_no, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x107d, + "Failed mb[0]=%x.\n", mcp->mb[0]); } else { - DEBUG11(printk("%s(%ld): done. mb1=%x mb2=%x mb3=%x mb6=%x " - "mb7=%x mb10=%x mb11=%x mb12=%x.\n", __func__, - vha->host_no, mcp->mb[1], mcp->mb[2], mcp->mb[3], - mcp->mb[6], mcp->mb[7], mcp->mb[10], mcp->mb[11], - mcp->mb[12])); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, 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]; @@ -2140,14 +2382,13 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_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) && max_fcfs) + if ((IS_QLA81XX(vha->hw) || IS_QLA83XX(vha->hw)) && max_fcfs) *max_fcfs = mcp->mb[12]; } return (rval); } -#if defined(QL_DEBUG_LEVEL_3) /* * qla2x00_get_fcal_position_map * Get FCAL (LILP) position map using mailbox command @@ -2172,10 +2413,13 @@ qla2x00_get_fcal_position_map(scsi_qla_host_t *vha, char *pos_map) dma_addr_t pmap_dma; struct qla_hw_data *ha = vha->hw; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x107f, + "Entered %s.\n", __func__); + pmap = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &pmap_dma); if (pmap == NULL) { - DEBUG2_3_11(printk("%s(%ld): **** Mem Alloc Failed ****", - __func__, vha->host_no)); + ql_log(ql_log_warn, vha, 0x1080, + "Memory alloc failed.\n"); return QLA_MEMORY_ALLOC_FAILED; } memset(pmap, 0, FCAL_MAP_SIZE); @@ -2193,10 +2437,11 @@ qla2x00_get_fcal_position_map(scsi_qla_host_t *vha, char *pos_map) rval = qla2x00_mailbox_command(vha, mcp); if (rval == QLA_SUCCESS) { - DEBUG11(printk("%s(%ld): (mb0=%x/mb1=%x) FC/AL Position Map " - "size (%x)\n", __func__, vha->host_no, mcp->mb[0], - mcp->mb[1], (unsigned)pmap[0])); - DEBUG11(qla2x00_dump_buffer(pmap, pmap[0] + 1)); + ql_dbg(ql_dbg_mbx + ql_dbg_buffer, 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); @@ -2204,15 +2449,14 @@ qla2x00_get_fcal_position_map(scsi_qla_host_t *vha, char *pos_map) dma_pool_free(ha->s_dma_pool, pmap, pmap_dma); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, - vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x1082, "Failed=%x.\n", rval); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1083, + "Done %s.\n", __func__); } return rval; } -#endif /* * qla2x00_get_link_status @@ -2237,7 +2481,8 @@ qla2x00_get_link_status(scsi_qla_host_t *vha, uint16_t loop_id, uint32_t *siter, *diter, dwords; struct qla_hw_data *ha = vha->hw; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1084, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_GET_LINK_STATUS; mcp->mb[2] = MSW(stats_dma); @@ -2266,11 +2511,13 @@ qla2x00_get_link_status(scsi_qla_host_t *vha, uint16_t loop_id, if (rval == QLA_SUCCESS) { if (mcp->mb[0] != MBS_COMMAND_COMPLETE) { - DEBUG2_3_11(printk("%s(%ld): cmd failed. mbx0=%x.\n", - __func__, vha->host_no, mcp->mb[0])); + 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 + ql_dbg_verbose, vha, 0x1086, + "Done %s.\n", __func__); dwords = offsetof(struct link_statistics, unused1) / 4; siter = diter = &stats->link_fail_cnt; while (dwords--) @@ -2278,8 +2525,7 @@ qla2x00_get_link_status(scsi_qla_host_t *vha, uint16_t loop_id, } } else { /* Failed. */ - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, - vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x1087, "Failed=%x.\n", rval); } return rval; @@ -2294,7 +2540,8 @@ qla24xx_get_isp_stats(scsi_qla_host_t *vha, struct link_statistics *stats, mbx_cmd_t *mcp = &mc; uint32_t *siter, *diter, dwords; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1088, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_GET_LINK_PRIV_STATS; mcp->mb[2] = MSW(stats_dma); @@ -2312,10 +2559,12 @@ qla24xx_get_isp_stats(scsi_qla_host_t *vha, struct link_statistics *stats, if (rval == QLA_SUCCESS) { if (mcp->mb[0] != MBS_COMMAND_COMPLETE) { - DEBUG2_3_11(printk("%s(%ld): cmd failed. mbx0=%x.\n", - __func__, vha->host_no, mcp->mb[0])); + 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 + ql_dbg_verbose, 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; @@ -2324,8 +2573,7 @@ qla24xx_get_isp_stats(scsi_qla_host_t *vha, struct link_statistics *stats, } } else { /* Failed. */ - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, - vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x108b, "Failed=%x.\n", rval); } return rval; @@ -2345,23 +2593,27 @@ qla24xx_abort_command(srb_t *sp) struct qla_hw_data *ha = vha->hw; struct req_que *req = vha->req; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x108c, + "Entered %s.\n", __func__); + + if (ql2xasynctmfenable) + return qla24xx_async_abort_command(sp); spin_lock_irqsave(&ha->hardware_lock, flags); - for (handle = 1; handle < MAX_OUTSTANDING_COMMANDS; handle++) { + for (handle = 1; handle < req->num_outstanding_cmds; handle++) { if (req->outstanding_cmds[handle] == sp) break; } spin_unlock_irqrestore(&ha->hardware_lock, flags); - if (handle == MAX_OUTSTANDING_COMMANDS) { + if (handle == req->num_outstanding_cmds) { /* Command not found. */ return QLA_FUNCTION_FAILED; } abt = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &abt_dma); if (abt == NULL) { - DEBUG2_3(printk("%s(%ld): failed to allocate Abort IOCB.\n", - __func__, vha->host_no)); + 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)); @@ -2374,26 +2626,30 @@ qla24xx_abort_command(srb_t *sp) 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->vp_index = fcport->vha->vp_idx; abt->req_que_no = cpu_to_le16(req->id); rval = qla2x00_issue_iocb(vha, abt, abt_dma, 0); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed to issue IOCB (%x).\n", - __func__, vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x108e, + "Failed to issue IOCB (%x).\n", rval); } else if (abt->entry_status != 0) { - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- error status (%x).\n", __func__, vha->host_no, - abt->entry_status)); + 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)) { - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- completion status (%x).\n", __func__, vha->host_no, - le16_to_cpu(abt->nport_handle))); - rval = QLA_FUNCTION_FAILED; + ql_dbg(ql_dbg_mbx, vha, 0x1090, + "Failed to complete IOCB -- completion status (%x).\n", + le16_to_cpu(abt->nport_handle)); + if (abt->nport_handle == CS_IOCB_ERROR) + rval = QLA_FUNCTION_PARAMETER_ERROR; + else + rval = QLA_FUNCTION_FAILED; } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1091, + "Done %s.\n", __func__); } dma_pool_free(ha->s_dma_pool, abt, abt_dma); @@ -2421,19 +2677,21 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport, struct req_que *req; struct rsp_que *rsp; - DEBUG11(printk("%s(%ld): entered.\n", __func__, fcport->vha->host_no)); - vha = fcport->vha; ha = vha->hw; req = vha->req; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, 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) { - DEBUG2_3(printk("%s(%ld): failed to allocate Task Management " - "IOCB.\n", __func__, vha->host_no)); + 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)); @@ -2447,7 +2705,7 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport, 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; + tsk->p.tsk.vp_index = fcport->vha->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, @@ -2457,30 +2715,30 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport, sts = &tsk->p.sts; rval = qla2x00_issue_iocb(vha, tsk, tsk_dma, 0); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed to issue %s Reset IOCB " - "(%x).\n", __func__, vha->host_no, name, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x1094, + "Failed to issue %s reset IOCB (%x).\n", name, rval); } else if (sts->entry_status != 0) { - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- error status (%x).\n", __func__, vha->host_no, - sts->entry_status)); + 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)) { - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- completion status (%x).\n", __func__, - vha->host_no, le16_to_cpu(sts->comp_status))); + 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) { - DEBUG2_3_11(printk("%s(%ld): ignoring inconsistent " - "data length -- not enough response info (%d).\n", - __func__, vha->host_no, - le32_to_cpu(sts->rsp_data_len))); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1097, + "Ignoring inconsistent data length -- not enough " + "response info (%d).\n", + le32_to_cpu(sts->rsp_data_len)); } else if (sts->data[3]) { - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- response (%x).\n", __func__, - vha->host_no, sts->data[3])); + ql_dbg(ql_dbg_mbx, vha, 0x1098, + "Failed to complete IOCB -- response (%x).\n", + sts->data[3]); rval = QLA_FUNCTION_FAILED; } } @@ -2489,10 +2747,11 @@ __qla24xx_issue_tmf(char *name, uint32_t type, struct fc_port *fcport, 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) { - DEBUG2_3_11(printk("%s(%ld): failed to issue Marker IOCB " - "(%x).\n", __func__, vha->host_no, rval2)); + ql_dbg(ql_dbg_mbx, vha, 0x1099, + "Failed to issue marker IOCB (%x).\n", rval2); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x109a, + "Done %s.\n", __func__); } dma_pool_free(ha->s_dma_pool, tsk, tsk_dma); @@ -2533,7 +2792,8 @@ qla2x00_system_error(scsi_qla_host_t *vha) if (!IS_QLA23XX(ha) && !IS_FWI2_CAPABLE(ha)) return QLA_FUNCTION_FAILED; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x109b, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_GEN_SYSTEM_ERROR; mcp->out_mb = MBX_0; @@ -2543,10 +2803,151 @@ qla2x00_system_error(scsi_qla_host_t *vha) rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, - vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x109c, "Failed=%x.\n", rval); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x109d, + "Done %s.\n", __func__); + } + + return rval; +} + +int +qla2x00_write_serdes_word(scsi_qla_host_t *vha, uint16_t addr, uint16_t data) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + if (!IS_QLA2031(vha->hw)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1182, + "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_WRITE_SERDES; + mcp->mb[1] = addr; + mcp->mb[2] = data & 0xff; + 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) { + ql_dbg(ql_dbg_mbx, vha, 0x1183, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1184, + "Done %s.\n", __func__); + } + + return rval; +} + +int +qla2x00_read_serdes_word(scsi_qla_host_t *vha, uint16_t addr, uint16_t *data) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + if (!IS_QLA2031(vha->hw)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1185, + "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_READ_SERDES; + mcp->mb[1] = addr; + mcp->mb[3] = 0; + mcp->out_mb = MBX_3|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); + + *data = mcp->mb[1] & 0xff; + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x1186, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1187, + "Done %s.\n", __func__); + } + + return rval; +} + +int +qla8044_write_serdes_word(scsi_qla_host_t *vha, uint32_t addr, uint32_t data) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + if (!IS_QLA8044(vha->hw)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1186, + "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_SET_GET_ETH_SERDES_REG; + mcp->mb[1] = HCS_WRITE_SERDES; + mcp->mb[3] = LSW(addr); + mcp->mb[4] = MSW(addr); + mcp->mb[5] = LSW(data); + mcp->mb[6] = MSW(data); + mcp->out_mb = MBX_6|MBX_5|MBX_4|MBX_3|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, 0x1187, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1188, + "Done %s.\n", __func__); + } + + return rval; +} + +int +qla8044_read_serdes_word(scsi_qla_host_t *vha, uint32_t addr, uint32_t *data) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + if (!IS_QLA8044(vha->hw)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1189, + "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_SET_GET_ETH_SERDES_REG; + mcp->mb[1] = HCS_READ_SERDES; + mcp->mb[3] = LSW(addr); + mcp->mb[4] = MSW(addr); + mcp->out_mb = MBX_4|MBX_3|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); + + *data = mcp->mb[2] << 16 | mcp->mb[1]; + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x118a, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x118b, + "Done %s.\n", __func__); } return rval; @@ -2566,7 +2967,8 @@ qla2x00_set_serdes_params(scsi_qla_host_t *vha, uint16_t sw_em_1g, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x109e, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_SERDES_PARAMS; mcp->mb[1] = BIT_0; @@ -2581,11 +2983,12 @@ qla2x00_set_serdes_params(scsi_qla_host_t *vha, uint16_t sw_em_1g, if (rval != QLA_SUCCESS) { /*EMPTY*/ - DEBUG2_3_11(printk("%s(%ld): failed=%x (%x).\n", __func__, - vha->host_no, rval, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x109f, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); } else { /*EMPTY*/ - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a0, + "Done %s.\n", __func__); } return rval; @@ -2601,22 +3004,24 @@ qla2x00_stop_firmware(scsi_qla_host_t *vha) if (!IS_FWI2_CAPABLE(vha->hw)) return QLA_FUNCTION_FAILED; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a1, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_STOP_FIRMWARE; - mcp->out_mb = MBX_0; + 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) { - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, - vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x10a2, "Failed=%x.\n", rval); if (mcp->mb[0] == MBS_INVALID_COMMAND) rval = QLA_INVALID_COMMAND; } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a3, + "Done %s.\n", __func__); } return rval; @@ -2630,14 +3035,15 @@ qla2x00_enable_eft_trace(scsi_qla_host_t *vha, dma_addr_t eft_dma, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, 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; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); - mcp->mb[0] = MBC_TRACE_CONTROL; mcp->mb[1] = TC_EFT_ENABLE; mcp->mb[2] = LSW(eft_dma); @@ -2652,10 +3058,12 @@ qla2x00_enable_eft_trace(scsi_qla_host_t *vha, dma_addr_t eft_dma, mcp->flags = 0; rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n", - __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1])); + ql_dbg(ql_dbg_mbx, vha, 0x10a5, + "Failed=%x mb[0]=%x mb[1]=%x.\n", + rval, mcp->mb[0], mcp->mb[1]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a6, + "Done %s.\n", __func__); } return rval; @@ -2668,14 +3076,15 @@ qla2x00_disable_eft_trace(scsi_qla_host_t *vha) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, 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; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); - mcp->mb[0] = MBC_TRACE_CONTROL; mcp->mb[1] = TC_EFT_DISABLE; mcp->out_mb = MBX_1|MBX_0; @@ -2684,10 +3093,12 @@ qla2x00_disable_eft_trace(scsi_qla_host_t *vha) mcp->flags = 0; rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n", - __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1])); + ql_dbg(ql_dbg_mbx, vha, 0x10a8, + "Failed=%x mb[0]=%x mb[1]=%x.\n", + rval, mcp->mb[0], mcp->mb[1]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10a9, + "Done %s.\n", __func__); } return rval; @@ -2701,14 +3112,16 @@ qla2x00_enable_fce_trace(scsi_qla_host_t *vha, dma_addr_t fce_dma, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_QLA25XX(vha->hw) && !IS_QLA81XX(vha->hw)) + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10aa, + "Entered %s.\n", __func__); + + if (!IS_QLA25XX(vha->hw) && !IS_QLA81XX(vha->hw) && + !IS_QLA83XX(vha->hw) && !IS_QLA27XX(vha->hw)) return QLA_FUNCTION_FAILED; if (unlikely(pci_channel_offline(vha->hw->pdev))) return QLA_FUNCTION_FAILED; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); - mcp->mb[0] = MBC_TRACE_CONTROL; mcp->mb[1] = TC_FCE_ENABLE; mcp->mb[2] = LSW(fce_dma); @@ -2727,10 +3140,12 @@ qla2x00_enable_fce_trace(scsi_qla_host_t *vha, dma_addr_t fce_dma, mcp->flags = 0; rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n", - __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1])); + ql_dbg(ql_dbg_mbx, vha, 0x10ab, + "Failed=%x mb[0]=%x mb[1]=%x.\n", + rval, mcp->mb[0], mcp->mb[1]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ac, + "Done %s.\n", __func__); if (mb) memcpy(mb, mcp->mb, 8 * sizeof(*mb)); @@ -2748,14 +3163,15 @@ qla2x00_disable_fce_trace(scsi_qla_host_t *vha, uint64_t *wr, uint64_t *rd) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, 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; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); - mcp->mb[0] = MBC_TRACE_CONTROL; mcp->mb[1] = TC_FCE_DISABLE; mcp->mb[2] = TC_FCE_DISABLE_TRACE; @@ -2766,10 +3182,12 @@ qla2x00_disable_fce_trace(scsi_qla_host_t *vha, uint64_t *wr, uint64_t *rd) mcp->flags = 0; rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n", - __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1])); + ql_dbg(ql_dbg_mbx, vha, 0x10ae, + "Failed=%x mb[0]=%x mb[1]=%x.\n", + rval, mcp->mb[0], mcp->mb[1]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10af, + "Done %s.\n", __func__); if (wr) *wr = (uint64_t) mcp->mb[5] << 48 | @@ -2794,11 +3212,12 @@ qla2x00_get_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b0, + "Entered %s.\n", __func__); + if (!IS_IIDMA_CAPABLE(vha->hw)) return QLA_FUNCTION_FAILED; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); - mcp->mb[0] = MBC_PORT_PARAMS; mcp->mb[1] = loop_id; mcp->mb[2] = mcp->mb[3] = 0; @@ -2817,10 +3236,10 @@ qla2x00_get_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id, } if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, - vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x10b1, "Failed=%x.\n", rval); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b2, + "Done %s.\n", __func__); if (port_speed) *port_speed = mcp->mb[3]; } @@ -2836,15 +3255,16 @@ qla2x00_set_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b3, + "Entered %s.\n", __func__); + if (!IS_IIDMA_CAPABLE(vha->hw)) return QLA_FUNCTION_FAILED; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); - mcp->mb[0] = MBC_PORT_PARAMS; mcp->mb[1] = loop_id; mcp->mb[2] = BIT_0; - if (IS_QLA8XXX_TYPE(vha->hw)) + 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); @@ -2863,10 +3283,11 @@ qla2x00_set_idma_speed(scsi_qla_host_t *vha, uint16_t loop_id, } if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x.\n", __func__, - vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x10b4, + "Failed=%x.\n", rval); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b5, + "Done %s.\n", __func__); } return rval; @@ -2881,44 +3302,53 @@ qla24xx_report_id_acquisition(scsi_qla_host_t *vha, struct qla_hw_data *ha = vha->hw; scsi_qla_host_t *vp; unsigned long flags; + int found; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10b6, + "Entered %s.\n", __func__); if (rptid_entry->entry_status != 0) return; if (rptid_entry->format == 0) { - DEBUG15(printk("%s:format 0 : scsi(%ld) number of VPs setup %d," - " number of VPs acquired %d\n", __func__, vha->host_no, - MSB(le16_to_cpu(rptid_entry->vp_count)), - LSB(le16_to_cpu(rptid_entry->vp_count)))); - DEBUG15(printk("%s primary port id %02x%02x%02x\n", __func__, - rptid_entry->port_id[2], rptid_entry->port_id[1], - rptid_entry->port_id[0])); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, 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 + ql_dbg_verbose, 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); - DEBUG15(printk("%s:format 1: scsi(%ld): VP[%d] enabled " - "- status %d - " - "with port id %02x%02x%02x\n", __func__, vha->host_no, - vp_idx, MSB(stat), + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, 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])); + rptid_entry->port_id[0]); vp = vha; if (vp_idx == 0 && (MSB(stat) != 1)) goto reg_needed; - if (MSB(stat) == 1) { - DEBUG2(printk("scsi(%ld): Could not acquire ID for " - "VP[%d].\n", vha->host_no, vp_idx)); + if (MSB(stat) != 0 && MSB(stat) != 2) { + ql_dbg(ql_dbg_mbx, vha, 0x10ba, + "Could not acquire ID for VP[%d].\n", vp_idx); return; } + found = 0; spin_lock_irqsave(&ha->vport_slock, flags); - list_for_each_entry(vp, &ha->vp_list, list) - if (vp_idx == vp->vp_idx) + list_for_each_entry(vp, &ha->vp_list, list) { + if (vp_idx == vp->vp_idx) { + found = 1; break; + } + } spin_unlock_irqrestore(&ha->vport_slock, flags); - if (!vp) + if (!found) return; vp->d_id.b.domain = rptid_entry->port_id[2]; @@ -2963,10 +3393,13 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha) /* This can be called by the parent */ + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10bb, + "Entered %s.\n", __func__); + vpmod = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &vpmod_dma); if (!vpmod) { - DEBUG2_3(printk("%s(%ld): failed to allocate Modify VP " - "IOCB.\n", __func__, vha->host_no)); + ql_log(ql_log_warn, vha, 0x10bc, + "Failed to allocate modify VP IOCB.\n"); return QLA_MEMORY_ALLOC_FAILED; } @@ -2977,28 +3410,31 @@ qla24xx_modify_vp_config(scsi_qla_host_t *vha) vpmod->vp_count = 1; vpmod->vp_index1 = vha->vp_idx; vpmod->options_idx1 = BIT_3|BIT_4|BIT_5; + + qlt_modify_vp_config(vha, vpmod); + 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) { - DEBUG2_3_11(printk("%s(%ld): failed to issue VP config IOCB" - "(%x).\n", __func__, base_vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x10bd, + "Failed to issue VP config IOCB (%x).\n", rval); } else if (vpmod->comp_status != 0) { - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- error status (%x).\n", __func__, base_vha->host_no, - vpmod->comp_status)); + 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)) { - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- completion status (%x).\n", __func__, base_vha->host_no, - le16_to_cpu(vpmod->comp_status))); + 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 */ - DEBUG11(printk("%s(%ld): done.\n", __func__, - base_vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, 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); @@ -3032,17 +3468,16 @@ qla24xx_control_vp(scsi_qla_host_t *vha, int cmd) int vp_index = vha->vp_idx; struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); - DEBUG11(printk("%s(%ld): entered. Enabling index %d\n", __func__, - vha->host_no, vp_index)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, 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) { - DEBUG2_3(printk("%s(%ld): " - "failed to allocate VP Control IOCB.\n", __func__, - base_vha->host_no)); + 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)); @@ -3063,28 +3498,21 @@ qla24xx_control_vp(scsi_qla_host_t *vha, int cmd) rval = qla2x00_issue_iocb(base_vha, vce, vce_dma, 0); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed to issue VP control IOCB" - "(%x).\n", __func__, base_vha->host_no, rval)); - printk("%s(%ld): failed to issue VP control IOCB" - "(%x).\n", __func__, base_vha->host_no, rval); + ql_dbg(ql_dbg_mbx, vha, 0x10c3, + "Failed to issue VP control IOCB (%x).\n", rval); } else if (vce->entry_status != 0) { - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- error status (%x).\n", __func__, base_vha->host_no, - vce->entry_status)); - printk("%s(%ld): failed to complete IOCB " - "-- error status (%x).\n", __func__, base_vha->host_no, + 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)) { - DEBUG2_3_11(printk("%s(%ld): failed to complete IOCB " - "-- completion status (%x).\n", __func__, base_vha->host_no, - le16_to_cpu(vce->comp_status))); - printk("%s(%ld): failed to complete IOCB " - "-- completion status (%x).\n", __func__, base_vha->host_no, + 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 { - DEBUG2(printk("%s(%ld): done.\n", __func__, base_vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c6, + "Done %s.\n", __func__); } dma_pool_free(ha->s_dma_pool, vce, vce_dma); @@ -3121,12 +3549,8 @@ qla2x00_send_change_request(scsi_qla_host_t *vha, uint16_t format, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - /* - * This command is implicitly executed by firmware during login for the - * physical hosts - */ - if (vp_idx == 0) - return QLA_FUNCTION_FAILED; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c7, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_SEND_CHANGE_REQUEST; mcp->mb[1] = format; @@ -3155,7 +3579,8 @@ qla2x00_dump_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t addr, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1009, + "Entered %s.\n", __func__); if (MSW(addr) || IS_FWI2_CAPABLE(vha->hw)) { mcp->mb[0] = MBC_DUMP_RISC_RAM_EXTENDED; @@ -3186,15 +3611,15 @@ qla2x00_dump_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t addr, rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__, - vha->host_no, rval, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x1008, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1007, + "Done %s.\n", __func__); } return rval; } - /* 84XX Support **************************************************************/ struct cs84xx_mgmt_cmd { @@ -3214,12 +3639,11 @@ qla84xx_verify_chip(struct scsi_qla_host *vha, uint16_t *status) unsigned long flags; struct qla_hw_data *ha = vha->hw; - DEBUG16(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10c8, + "Entered %s.\n", __func__); mn = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &mn_dma); if (mn == NULL) { - DEBUG2_3(printk("%s(%ld): failed to allocate Verify ISP84XX " - "IOCB.\n", __func__, vha->host_no)); return QLA_MEMORY_ALLOC_FAILED; } @@ -3237,43 +3661,43 @@ qla84xx_verify_chip(struct scsi_qla_host *vha, uint16_t *status) mn->p.req.entry_count = 1; mn->p.req.options = cpu_to_le16(options); - DEBUG16(printk("%s(%ld): Dump of Verify Request.\n", __func__, - vha->host_no)); - DEBUG16(qla2x00_dump_buffer((uint8_t *)mn, - sizeof(*mn))); + 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) { - DEBUG2_16(printk("%s(%ld): failed to issue Verify " - "IOCB (%x).\n", __func__, vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x10cb, + "Failed to issue verify IOCB (%x).\n", rval); goto verify_done; } - DEBUG16(printk("%s(%ld): Dump of Verify Response.\n", __func__, - vha->host_no)); - DEBUG16(qla2x00_dump_buffer((uint8_t *)mn, - sizeof(*mn))); + 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; - DEBUG2_16(printk("%s(%ld): cs=%x fc=%x\n", __func__, - vha->host_no, status[0], status[1])); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, 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)) { - DEBUG2_16(printk("%s(%ld): Firmware update " - "failed. Retrying without update " - "firmware.\n", __func__, vha->host_no)); + 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 { - DEBUG2_16(printk("%s(%ld): firmware updated to %x.\n", - __func__, vha->host_no, - le32_to_cpu(mn->p.rsp.fw_ver))); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, 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); @@ -3288,10 +3712,11 @@ verify_done: dma_pool_free(ha->s_dma_pool, mn, mn_dma); if (rval != QLA_SUCCESS) { - DEBUG2_16(printk("%s(%ld): failed=%x.\n", __func__, - vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x10d1, + "Failed=%x.\n", rval); } else { - DEBUG16(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d2, + "Done %s.\n", __func__); } return rval; @@ -3304,9 +3729,14 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req) 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 + ql_dbg_verbose, vha, 0x10d3, + "Entered %s.\n", __func__); + + if (IS_SHADOW_REG_CAPABLE(ha)) + req->options |= BIT_13; + mcp->mb[0] = MBC_INITIALIZE_MULTIQ; mcp->mb[1] = req->options; mcp->mb[2] = MSW(LSD(req->dma)); @@ -3319,34 +3749,45 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req) mcp->mb[12] = req->qos; mcp->mb[11] = req->vp_idx; mcp->mb[13] = req->rid; - - reg = (struct device_reg_25xxmq *)((void *)(ha->mqiobase) + - QLA_QUE_PAGE * req->id); + if (IS_QLA83XX(ha) || IS_QLA27XX(ha)) + mcp->mb[15] = 0; mcp->mb[4] = req->id; /* que in ptr index */ mcp->mb[8] = 0; /* que out ptr index */ - mcp->mb[9] = 0; + mcp->mb[9] = *req->out_ptr = 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 = 60; + mcp->tov = MBX_TOV_SECONDS * 2; + + if (IS_QLA81XX(ha) || IS_QLA83XX(ha) || IS_QLA27XX(ha)) + mcp->in_mb |= MBX_1; + if (IS_QLA83XX(ha) || IS_QLA27XX(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(®->req_q_in, 0); - WRT_REG_DWORD(®->req_q_out, 0); + WRT_REG_DWORD(req->req_q_in, 0); + if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha)) + WRT_REG_DWORD(req->req_q_out, 0); } - req->req_q_in = ®->req_q_in; - req->req_q_out = ®->req_q_out; spin_unlock_irqrestore(&ha->hardware_lock, flags); rval = qla2x00_mailbox_command(vha, mcp); - if (rval != QLA_SUCCESS) - DEBUG2_3_11(printk(KERN_WARNING "%s(%ld): failed=%x mb0=%x.\n", - __func__, vha->host_no, rval, mcp->mb[0])); + 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 + ql_dbg_verbose, vha, 0x10d5, + "Done %s.\n", __func__); + } + return rval; } @@ -3357,9 +3798,14 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp) 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 + ql_dbg_verbose, vha, 0x10d6, + "Entered %s.\n", __func__); + + if (IS_SHADOW_REG_CAPABLE(ha)) + rsp->options |= BIT_13; + mcp->mb[0] = MBC_INITIALIZE_MULTIQ; mcp->mb[1] = rsp->options; mcp->mb[2] = MSW(LSD(rsp->dma)); @@ -3369,34 +3815,48 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp) mcp->mb[5] = rsp->length; mcp->mb[14] = rsp->msix->entry; mcp->mb[13] = rsp->rid; - - reg = (struct device_reg_25xxmq *)((void *)(ha->mqiobase) + - QLA_QUE_PAGE * rsp->id); + if (IS_QLA83XX(ha) || IS_QLA27XX(ha)) + mcp->mb[15] = 0; mcp->mb[4] = rsp->id; /* que in ptr index */ - mcp->mb[8] = 0; + mcp->mb[8] = *rsp->in_ptr = 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 = 60; + 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) || IS_QLA27XX(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(®->rsp_q_out, 0); - WRT_REG_DWORD(®->rsp_q_in, 0); + WRT_REG_DWORD(rsp->rsp_q_out, 0); + if (!IS_QLA83XX(ha)) + WRT_REG_DWORD(rsp->rsp_q_in, 0); } spin_unlock_irqrestore(&ha->hardware_lock, flags); rval = qla2x00_mailbox_command(vha, mcp); - if (rval != QLA_SUCCESS) - DEBUG2_3_11(printk(KERN_WARNING "%s(%ld): failed=%x " - "mb0=%x.\n", __func__, - vha->host_no, rval, mcp->mb[0])); + 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 + ql_dbg_verbose, vha, 0x10d8, + "Done %s.\n", __func__); + } + return rval; } @@ -3407,7 +3867,8 @@ qla81xx_idc_ack(scsi_qla_host_t *vha, uint16_t *mb) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10d9, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_IDC_ACK; memcpy(&mcp->mb[1], mb, QLA_IDC_ACK_REGS * sizeof(uint16_t)); @@ -3418,10 +3879,11 @@ qla81xx_idc_ack(scsi_qla_host_t *vha, uint16_t *mb) rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x (%x).\n", __func__, - vha->host_no, rval, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x10da, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10db, + "Done %s.\n", __func__); } return rval; @@ -3434,10 +3896,12 @@ qla81xx_fac_get_sector_size(scsi_qla_host_t *vha, uint32_t *sector_size) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_QLA81XX(vha->hw)) - return QLA_FUNCTION_FAILED; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10dc, + "Entered %s.\n", __func__); - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + if (!IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw) && + !IS_QLA27XX(vha->hw)) + return QLA_FUNCTION_FAILED; mcp->mb[0] = MBC_FLASH_ACCESS_CTRL; mcp->mb[1] = FAC_OPT_CMD_GET_SECTOR_SIZE; @@ -3448,10 +3912,12 @@ qla81xx_fac_get_sector_size(scsi_qla_host_t *vha, uint32_t *sector_size) rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n", - __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1])); + ql_dbg(ql_dbg_mbx, vha, 0x10dd, + "Failed=%x mb[0]=%x mb[1]=%x.\n", + rval, mcp->mb[0], mcp->mb[1]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10de, + "Done %s.\n", __func__); *sector_size = mcp->mb[1]; } @@ -3465,10 +3931,12 @@ qla81xx_fac_do_write_enable(scsi_qla_host_t *vha, int enable) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_QLA81XX(vha->hw)) + if (!IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw) && + !IS_QLA27XX(vha->hw)) return QLA_FUNCTION_FAILED; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10df, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_FLASH_ACCESS_CTRL; mcp->mb[1] = enable ? FAC_OPT_CMD_WRITE_ENABLE : @@ -3480,10 +3948,12 @@ qla81xx_fac_do_write_enable(scsi_qla_host_t *vha, int enable) rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x.\n", - __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1])); + ql_dbg(ql_dbg_mbx, vha, 0x10e0, + "Failed=%x mb[0]=%x mb[1]=%x.\n", + rval, mcp->mb[0], mcp->mb[1]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e1, + "Done %s.\n", __func__); } return rval; @@ -3496,10 +3966,12 @@ qla81xx_fac_erase_sector(scsi_qla_host_t *vha, uint32_t start, uint32_t finish) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_QLA81XX(vha->hw)) + if (!IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw) && + !IS_QLA27XX(vha->hw)) return QLA_FUNCTION_FAILED; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e2, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_FLASH_ACCESS_CTRL; mcp->mb[1] = FAC_OPT_CMD_ERASE_SECTOR; @@ -3514,11 +3986,12 @@ qla81xx_fac_erase_sector(scsi_qla_host_t *vha, uint32_t start, uint32_t finish) rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x mb[1]=%x " - "mb[2]=%x.\n", __func__, vha->host_no, rval, mcp->mb[0], - mcp->mb[1], mcp->mb[2])); + 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 { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e4, + "Done %s.\n", __func__); } return rval; @@ -3531,7 +4004,8 @@ qla81xx_restart_mpi_firmware(scsi_qla_host_t *vha) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e5, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_RESTART_MPI_FW; mcp->out_mb = MBX_0; @@ -3541,10 +4015,151 @@ qla81xx_restart_mpi_firmware(scsi_qla_host_t *vha) rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=0x%x mb[1]=0x%x.\n", - __func__, vha->host_no, rval, mcp->mb[0], mcp->mb[1])); + 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 + ql_dbg_verbose, vha, 0x10e7, + "Done %s.\n", __func__); + } + + return rval; +} + +int +qla82xx_set_driver_version(scsi_qla_host_t *vha, char *version) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + int i; + int len; + uint16_t *str; + struct qla_hw_data *ha = vha->hw; + + if (!IS_P3P_TYPE(ha)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x117b, + "Entered %s.\n", __func__); + + str = (void *)version; + len = strlen(version); + + mcp->mb[0] = MBC_SET_RNID_PARAMS; + mcp->mb[1] = RNID_TYPE_SET_VERSION << 8; + mcp->out_mb = MBX_1|MBX_0; + for (i = 4; i < 16 && len; i++, str++, len -= 2) { + mcp->mb[i] = cpu_to_le16p(str); + mcp->out_mb |= 1<<i; + } + for (; i < 16; i++) { + mcp->mb[i] = 0; + mcp->out_mb |= 1<<i; + } + 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, 0x117c, + "Failed=%x mb[0]=%x,%x.\n", rval, mcp->mb[0], mcp->mb[1]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x117d, + "Done %s.\n", __func__); + } + + return rval; +} + +int +qla25xx_set_driver_version(scsi_qla_host_t *vha, char *version) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + int len; + uint16_t dwlen; + uint8_t *str; + dma_addr_t str_dma; + struct qla_hw_data *ha = vha->hw; + + if (!IS_FWI2_CAPABLE(ha) || IS_QLA24XX_TYPE(ha) || IS_QLA81XX(ha) || + IS_P3P_TYPE(ha)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x117e, + "Entered %s.\n", __func__); + + str = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &str_dma); + if (!str) { + ql_log(ql_log_warn, vha, 0x117f, + "Failed to allocate driver version param.\n"); + return QLA_MEMORY_ALLOC_FAILED; + } + + memcpy(str, "\x7\x3\x11\x0", 4); + dwlen = str[0]; + len = dwlen * 4 - 4; + memset(str + 4, 0, len); + if (len > strlen(version)) + len = strlen(version); + memcpy(str + 4, version, len); + + mcp->mb[0] = MBC_SET_RNID_PARAMS; + mcp->mb[1] = RNID_TYPE_SET_VERSION << 8 | dwlen; + mcp->mb[2] = MSW(LSD(str_dma)); + mcp->mb[3] = LSW(LSD(str_dma)); + mcp->mb[6] = MSW(MSD(str_dma)); + mcp->mb[7] = LSW(MSD(str_dma)); + mcp->out_mb = 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, 0x1180, + "Failed=%x mb[0]=%x,%x.\n", rval, mcp->mb[0], mcp->mb[1]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1181, + "Done %s.\n", __func__); + } + + dma_pool_free(ha->s_dma_pool, str, str_dma); + + return rval; +} + +static int +qla2x00_read_asic_temperature(scsi_qla_host_t *vha, uint16_t *temp) +{ + 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 + ql_dbg_verbose, vha, 0x1159, + "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_GET_RNID_PARAMS; + mcp->mb[1] = RNID_TYPE_ASIC_TEMP << 8; + 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); + *temp = mcp->mb[1]; + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x115a, + "Failed=%x mb[0]=%x,%x.\n", rval, mcp->mb[0], mcp->mb[1]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x115b, + "Done %s.\n", __func__); } return rval; @@ -3559,11 +4174,12 @@ qla2x00_read_sfp(scsi_qla_host_t *vha, dma_addr_t sfp_dma, uint8_t *sfp, mbx_cmd_t *mcp = &mc; struct qla_hw_data *ha = vha->hw; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e8, + "Entered %s.\n", __func__); + if (!IS_FWI2_CAPABLE(ha)) return QLA_FUNCTION_FAILED; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); - if (len == 1) opt |= BIT_0; @@ -3586,10 +4202,11 @@ qla2x00_read_sfp(scsi_qla_host_t *vha, dma_addr_t sfp_dma, uint8_t *sfp, *sfp = mcp->mb[1]; if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x (%x).\n", __func__, - vha->host_no, rval, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x10e9, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ea, + "Done %s.\n", __func__); } return rval; @@ -3604,11 +4221,12 @@ qla2x00_write_sfp(scsi_qla_host_t *vha, dma_addr_t sfp_dma, uint8_t *sfp, mbx_cmd_t *mcp = &mc; struct qla_hw_data *ha = vha->hw; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10eb, + "Entered %s.\n", __func__); + if (!IS_FWI2_CAPABLE(ha)) return QLA_FUNCTION_FAILED; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); - if (len == 1) opt |= BIT_0; @@ -3631,10 +4249,11 @@ qla2x00_write_sfp(scsi_qla_host_t *vha, dma_addr_t sfp_dma, uint8_t *sfp, rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x (%x).\n", __func__, - vha->host_no, rval, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x10ec, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ed, + "Done %s.\n", __func__); } return rval; @@ -3648,10 +4267,11 @@ qla2x00_get_xgmac_stats(scsi_qla_host_t *vha, dma_addr_t stats_dma, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_QLA8XXX_TYPE(vha->hw)) - return QLA_FUNCTION_FAILED; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ee, + "Entered %s.\n", __func__); - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + if (!IS_CNA_CAPABLE(vha->hw)) + return QLA_FUNCTION_FAILED; mcp->mb[0] = MBC_GET_XGMAC_STATS; mcp->mb[2] = MSW(stats_dma); @@ -3666,11 +4286,13 @@ qla2x00_get_xgmac_stats(scsi_qla_host_t *vha, dma_addr_t stats_dma, rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=0x%x " - "mb[1]=0x%x mb[2]=0x%x.\n", __func__, vha->host_no, rval, - mcp->mb[0], mcp->mb[1], mcp->mb[2])); + 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 { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f0, + "Done %s.\n", __func__); + *actual_size = mcp->mb[2] << 2; } @@ -3686,10 +4308,11 @@ qla2x00_get_dcbx_params(scsi_qla_host_t *vha, dma_addr_t tlv_dma, mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_QLA8XXX_TYPE(vha->hw)) - return QLA_FUNCTION_FAILED; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f1, + "Entered %s.\n", __func__); - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + if (!IS_CNA_CAPABLE(vha->hw)) + return QLA_FUNCTION_FAILED; mcp->mb[0] = MBC_GET_DCBX_PARAMS; mcp->mb[1] = 0; @@ -3705,11 +4328,12 @@ qla2x00_get_dcbx_params(scsi_qla_host_t *vha, dma_addr_t tlv_dma, rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=0x%x " - "mb[1]=0x%x mb[2]=0x%x.\n", __func__, vha->host_no, rval, - mcp->mb[0], mcp->mb[1], mcp->mb[2])); + 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 { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f3, + "Done %s.\n", __func__); } return rval; @@ -3722,11 +4346,12 @@ qla2x00_read_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t *data) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f4, + "Entered %s.\n", __func__); + if (!IS_FWI2_CAPABLE(vha->hw)) return QLA_FUNCTION_FAILED; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); - mcp->mb[0] = MBC_READ_RAM_EXTENDED; mcp->mb[1] = LSW(risc_addr); mcp->mb[8] = MSW(risc_addr); @@ -3736,10 +4361,11 @@ qla2x00_read_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t *data) mcp->flags = 0; rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__, - vha->host_no, rval, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x10f5, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f6, + "Done %s.\n", __func__); *data = mcp->mb[3] << 16 | mcp->mb[2]; } @@ -3753,9 +4379,9 @@ qla2x00_loopback_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq, int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - uint32_t iter_cnt = 0x1; - DEBUG11(printk("scsi(%ld): entered.\n", vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f7, + "Entered %s.\n", __func__); memset(mcp->mb, 0 , sizeof(mcp->mb)); mcp->mb[0] = MBC_DIAGNOSTIC_LOOP_BACK; @@ -3778,12 +4404,12 @@ qla2x00_loopback_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq, mcp->mb[7] = MSW(MSD(mreq->rcv_dma)); /* Iteration count */ - mcp->mb[18] = LSW(iter_cnt); - mcp->mb[19] = MSW(iter_cnt); + mcp->mb[18] = LSW(mreq->iteration_count); + mcp->mb[19] = MSW(mreq->iteration_count); 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_QLA8XXX_TYPE(vha->hw)) + 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; @@ -3794,15 +4420,13 @@ qla2x00_loopback_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq, rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2(printk(KERN_WARNING - "(%ld): failed=%x mb[0]=0x%x " - "mb[1]=0x%x mb[2]=0x%x mb[3]=0x%x mb[18]=0x%x " - "mb[19]=0x%x.\n", - vha->host_no, rval, mcp->mb[0], mcp->mb[1], mcp->mb[2], - mcp->mb[3], mcp->mb[18], mcp->mb[19])); + 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 { - DEBUG2(printk(KERN_WARNING - "scsi(%ld): done.\n", vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10f9, + "Done %s.\n", __func__); } /* Copy mailbox information */ @@ -3819,12 +4443,13 @@ qla2x00_echo_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq, mbx_cmd_t *mcp = &mc; struct qla_hw_data *ha = vha->hw; - DEBUG11(printk("scsi(%ld): entered.\n", vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, 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_QLA8XXX_TYPE(ha)) { + if (IS_CNA_CAPABLE(ha)) { mcp->mb[1] |= BIT_15; mcp->mb[2] = vha->fcoe_fcf_idx; } @@ -3842,13 +4467,14 @@ qla2x00_echo_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq, 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_QLA8XXX_TYPE(ha)) + if (IS_CNA_CAPABLE(ha)) mcp->out_mb |= MBX_2; mcp->in_mb = MBX_0; - if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) || IS_QLA8XXX_TYPE(ha)) + if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) || + IS_CNA_CAPABLE(ha) || IS_QLA2031(ha)) mcp->in_mb |= MBX_1; - if (IS_QLA8XXX_TYPE(ha)) + if (IS_CNA_CAPABLE(ha) || IS_QLA2031(ha)) mcp->in_mb |= MBX_3; mcp->tov = MBX_TOV_SECONDS; @@ -3858,12 +4484,12 @@ qla2x00_echo_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq, rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2(printk(KERN_WARNING - "(%ld): failed=%x mb[0]=0x%x mb[1]=0x%x.\n", - vha->host_no, rval, mcp->mb[0], mcp->mb[1])); + ql_dbg(ql_dbg_mbx, vha, 0x10fb, + "Failed=%x mb[0]=%x mb[1]=%x.\n", + rval, mcp->mb[0], mcp->mb[1]); } else { - DEBUG2(printk(KERN_WARNING - "scsi(%ld): done.\n", vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10fc, + "Done %s.\n", __func__); } /* Copy mailbox information */ @@ -3872,14 +4498,14 @@ qla2x00_echo_test(scsi_qla_host_t *vha, struct msg_echo_lb *mreq, } int -qla84xx_reset_chip(scsi_qla_host_t *ha, uint16_t enable_diagnostic) +qla84xx_reset_chip(scsi_qla_host_t *vha, uint16_t enable_diagnostic) { int rval; mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG16(printk("%s(%ld): enable_diag=%d entered.\n", __func__, - ha->host_no, enable_diagnostic)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10fd, + "Entered %s enable_diag=%d.\n", __func__, enable_diagnostic); mcp->mb[0] = MBC_ISP84XX_RESET; mcp->mb[1] = enable_diagnostic; @@ -3887,13 +4513,13 @@ qla84xx_reset_chip(scsi_qla_host_t *ha, uint16_t enable_diagnostic) 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(ha, mcp); + rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) - DEBUG16(printk("%s(%ld): failed=%x.\n", __func__, ha->host_no, - rval)); + ql_dbg(ql_dbg_mbx, vha, 0x10fe, "Failed=%x.\n", rval); else - DEBUG16(printk("%s(%ld): done.\n", __func__, ha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ff, + "Done %s.\n", __func__); return rval; } @@ -3905,11 +4531,12 @@ qla2x00_write_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t data) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1100, + "Entered %s.\n", __func__); + if (!IS_FWI2_CAPABLE(vha->hw)) return QLA_FUNCTION_FAILED; - DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); - mcp->mb[0] = MBC_WRITE_RAM_WORD_EXTENDED; mcp->mb[1] = LSW(risc_addr); mcp->mb[2] = LSW(data); @@ -3921,10 +4548,11 @@ qla2x00_write_ram_word(scsi_qla_host_t *vha, uint32_t risc_addr, uint32_t data) mcp->flags = 0; rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk("%s(%ld): failed=%x mb[0]=%x.\n", __func__, - vha->host_no, rval, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x1101, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); } else { - DEBUG11(printk("%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1102, + "Done %s.\n", __func__); } return rval; @@ -3941,8 +4569,8 @@ qla81xx_write_mpi_register(scsi_qla_host_t *vha, uint16_t *mb) rval = QLA_SUCCESS; - DEBUG11(qla_printk(KERN_INFO, ha, - "%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1103, + "Entered %s.\n", __func__); clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); @@ -3982,15 +4610,16 @@ qla81xx_write_mpi_register(scsi_qla_host_t *vha, uint16_t *mb) rval = QLA_FUNCTION_FAILED; if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk(KERN_INFO "%s(%ld): failed=%x mb[0]=%x.\n", - __func__, vha->host_no, rval, mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x1104, + "Failed=%x mb[0]=%x.\n", rval, mb[0]); } else { - DEBUG11(printk(KERN_INFO - "%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1105, + "Done %s.\n", __func__); } return rval; } + int qla2x00_get_data_rate(scsi_qla_host_t *vha) { @@ -3999,25 +4628,27 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha) mbx_cmd_t *mcp = &mc; struct qla_hw_data *ha = vha->hw; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1106, + "Entered %s.\n", __func__); + if (!IS_FWI2_CAPABLE(ha)) return QLA_FUNCTION_FAILED; - DEBUG11(qla_printk(KERN_INFO, ha, - "%s(%ld): entered.\n", __func__, vha->host_no)); - 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) || IS_QLA27XX(ha)) + mcp->in_mb |= MBX_3; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk(KERN_INFO "%s(%ld): failed=%x mb[0]=%x.\n", - __func__, vha->host_no, rval, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x1107, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); } else { - DEBUG11(printk(KERN_INFO - "%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1108, + "Done %s.\n", __func__); if (mcp->mb[1] != 0x7) ha->link_data_rate = mcp->mb[1]; } @@ -4033,10 +4664,11 @@ qla81xx_get_port_config(scsi_qla_host_t *vha, uint16_t *mb) mbx_cmd_t *mcp = &mc; struct qla_hw_data *ha = vha->hw; - DEBUG11(printk(KERN_INFO - "%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1109, + "Entered %s.\n", __func__); - if (!IS_QLA81XX(ha)) + if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha) && !IS_QLA8044(ha) && + !IS_QLA27XX(ha)) return QLA_FUNCTION_FAILED; mcp->mb[0] = MBC_GET_PORT_CONFIG; mcp->out_mb = MBX_0; @@ -4047,15 +4679,14 @@ qla81xx_get_port_config(scsi_qla_host_t *vha, uint16_t *mb) rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk(KERN_WARNING - "%s(%ld): failed=%x (%x).\n", __func__, - vha->host_no, rval, mcp->mb[0])); + 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); - DEBUG11(printk(KERN_INFO - "%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110b, + "Done %s.\n", __func__); } return rval; } @@ -4067,8 +4698,8 @@ qla81xx_set_port_config(scsi_qla_host_t *vha, uint16_t *mb) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - DEBUG11(printk(KERN_INFO - "%s(%ld): entered.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110c, + "Entered %s.\n", __func__); mcp->mb[0] = MBC_SET_PORT_CONFIG; /* Copy all bits to preserve original setting */ @@ -4080,12 +4711,11 @@ qla81xx_set_port_config(scsi_qla_host_t *vha, uint16_t *mb) rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk(KERN_WARNING - "%s(%ld): failed=%x (%x).\n", __func__, - vha->host_no, rval, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x110d, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); } else - DEBUG11(printk(KERN_INFO - "%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110e, + "Done %s.\n", __func__); return rval; } @@ -4100,12 +4730,12 @@ qla24xx_set_fcp_prio(scsi_qla_host_t *vha, uint16_t loop_id, uint16_t priority, mbx_cmd_t *mcp = &mc; struct qla_hw_data *ha = vha->hw; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x110f, + "Entered %s.\n", __func__); + if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha)) return QLA_FUNCTION_FAILED; - DEBUG11(printk(KERN_INFO - "%s(%ld): entered.\n", __func__, vha->host_no)); - mcp->mb[0] = MBC_PORT_PARAMS; mcp->mb[1] = loop_id; if (ha->flags.fcp_prio_enabled) @@ -4127,48 +4757,59 @@ qla24xx_set_fcp_prio(scsi_qla_host_t *vha, uint16_t loop_id, uint16_t priority, } if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk(KERN_WARNING - "%s(%ld): failed=%x.\n", __func__, - vha->host_no, rval)); + ql_dbg(ql_dbg_mbx, vha, 0x10cd, "Failed=%x.\n", rval); } else { - DEBUG11(printk(KERN_INFO - "%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10cc, + "Done %s.\n", __func__); } return rval; } int -qla2x00_get_thermal_temp(scsi_qla_host_t *vha, uint16_t *temp, uint16_t *frac) +qla2x00_get_thermal_temp(scsi_qla_host_t *vha, uint16_t *temp) { - int rval; - uint8_t byte; + int rval = QLA_FUNCTION_FAILED; struct qla_hw_data *ha = vha->hw; + uint8_t byte; - DEBUG11(printk(KERN_INFO "%s(%ld): entered.\n", __func__, vha->host_no)); + if (!IS_FWI2_CAPABLE(ha) || IS_QLA24XX_TYPE(ha) || IS_QLA81XX(ha)) { + ql_dbg(ql_dbg_mbx, vha, 0x1150, + "Thermal not supported by this card.\n"); + return rval; + } - /* Integer part */ - rval = qla2x00_read_sfp(vha, 0, &byte, 0x98, 0x01, 1, BIT_13|BIT_0); - if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk(KERN_WARNING - "%s(%ld): failed=%x.\n", __func__, vha->host_no, rval)); - ha->flags.thermal_supported = 0; - goto fail; + if (IS_QLA25XX(ha)) { + if (ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC && + ha->pdev->subsystem_device == 0x0175) { + rval = qla2x00_read_sfp(vha, 0, &byte, + 0x98, 0x1, 1, BIT_13|BIT_0); + *temp = byte; + return rval; + } + if (ha->pdev->subsystem_vendor == PCI_VENDOR_ID_HP && + ha->pdev->subsystem_device == 0x338e) { + rval = qla2x00_read_sfp(vha, 0, &byte, + 0x98, 0x1, 1, BIT_15|BIT_14|BIT_0); + *temp = byte; + return rval; + } + ql_dbg(ql_dbg_mbx, vha, 0x10c9, + "Thermal not supported by this card.\n"); + return rval; } - *temp = byte; - /* Fraction part */ - rval = qla2x00_read_sfp(vha, 0, &byte, 0x98, 0x10, 1, BIT_13|BIT_0); - if (rval != QLA_SUCCESS) { - DEBUG2_3_11(printk(KERN_WARNING - "%s(%ld): failed=%x.\n", __func__, vha->host_no, rval)); - ha->flags.thermal_supported = 0; - goto fail; + if (IS_QLA82XX(ha)) { + *temp = qla82xx_read_temperature(vha); + rval = QLA_SUCCESS; + return rval; + } else if (IS_QLA8044(ha)) { + *temp = qla8044_read_temperature(vha); + rval = QLA_SUCCESS; + return rval; } - *frac = (byte >> 6) * 25; - DEBUG11(printk(KERN_INFO "%s(%ld): done.\n", __func__, vha->host_no)); -fail: + rval = qla2x00_read_asic_temperature(vha, temp); return rval; } @@ -4180,12 +4821,12 @@ qla82xx_mbx_intr_enable(scsi_qla_host_t *vha) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1017, + "Entered %s.\n", __func__); + if (!IS_FWI2_CAPABLE(ha)) return QLA_FUNCTION_FAILED; - DEBUG11(qla_printk(KERN_INFO, ha, - "%s(%ld): entered.\n", __func__, vha->host_no)); - memset(mcp, 0, sizeof(mbx_cmd_t)); mcp->mb[0] = MBC_TOGGLE_INTERRUPT; mcp->mb[1] = 1; @@ -4197,12 +4838,11 @@ qla82xx_mbx_intr_enable(scsi_qla_host_t *vha) rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(qla_printk(KERN_WARNING, ha, - "%s(%ld): failed=%x mb[0]=%x.\n", __func__, - vha->host_no, rval, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x1016, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); } else { - DEBUG11(qla_printk(KERN_INFO, ha, - "%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x100e, + "Done %s.\n", __func__); } return rval; @@ -4216,11 +4856,11 @@ qla82xx_mbx_intr_disable(scsi_qla_host_t *vha) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_QLA82XX(ha)) - return QLA_FUNCTION_FAILED; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x100d, + "Entered %s.\n", __func__); - DEBUG11(qla_printk(KERN_INFO, ha, - "%s(%ld): entered.\n", __func__, vha->host_no)); + if (!IS_P3P_TYPE(ha)) + return QLA_FUNCTION_FAILED; memset(mcp, 0, sizeof(mbx_cmd_t)); mcp->mb[0] = MBC_TOGGLE_INTERRUPT; @@ -4233,12 +4873,542 @@ qla82xx_mbx_intr_disable(scsi_qla_host_t *vha) rval = qla2x00_mailbox_command(vha, mcp); if (rval != QLA_SUCCESS) { - DEBUG2_3_11(qla_printk(KERN_WARNING, ha, - "%s(%ld): failed=%x mb[0]=%x.\n", __func__, - vha->host_no, rval, mcp->mb[0])); + ql_dbg(ql_dbg_mbx, vha, 0x100c, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, 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 + ql_dbg_verbose, 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 + ql_dbg_verbose, 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 + ql_dbg_verbose, 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 + ql_dbg_verbose, vha, 0x1126, + "Done %s.\n", __func__); + return rval; +} + +int +qla8044_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; + int offset = 0, size = MINIDUMP_SIZE_36K; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0xb11f, + "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, 0xb11b, + "Unable to allocate memory for Minidump template.\n"); + return rval; + } + + memset(mcp->mb, 0 , sizeof(mcp->mb)); + while (offset < ha->md_template_size) { + 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 + offset)); + mcp->mb[5] = MSW(LSD(ha->md_tmplt_hdr_dma + offset)); + mcp->mb[6] = LSW(MSD(ha->md_tmplt_hdr_dma + offset)); + mcp->mb[7] = MSW(MSD(ha->md_tmplt_hdr_dma + offset)); + mcp->mb[8] = LSW(size); + mcp->mb[9] = MSW(size); + mcp->mb[10] = offset & 0x0000FFFF; + mcp->mb[11] = offset & 0xFFFF0000; + 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, 0xb11c, + "mailbox command FAILED=0x%x, subcode=%x.\n", + ((mcp->mb[1] << 16) | mcp->mb[0]), + ((mcp->mb[3] << 16) | mcp->mb[2])); + return rval; + } else + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0xb11d, + "Done %s.\n", __func__); + offset = offset + size; + } + 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 + ql_dbg_verbose, 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 + ql_dbg_verbose, 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 + ql_dbg_verbose, 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 + ql_dbg_verbose, 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_P3P_TYPE(ha)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, 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 + ql_dbg_verbose, vha, 0x1129, + "Done %s.\n", __func__); + } + + return rval; +} + +int +qla83xx_wr_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) && !IS_QLA27XX(ha)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, 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 + ql_dbg_verbose, 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 + ql_dbg_verbose, vha, 0x113b, + "Implicit LOGO Unsupported.\n"); + return QLA_FUNCTION_FAILED; + } + + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x113c, + "Entering %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 + ql_dbg_verbose, vha, 0x113e, + "Done %s.\n", __func__); + + return rval; +} + +int +qla83xx_rd_reg(scsi_qla_host_t *vha, uint32_t reg, uint32_t *data) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + struct qla_hw_data *ha = vha->hw; + unsigned long retry_max_time = jiffies + (2 * HZ); + + if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx, vha, 0x114b, "Entered %s.\n", __func__); + +retry_rd_reg: + mcp->mb[0] = MBC_READ_REMOTE_REG; + mcp->mb[1] = LSW(reg); + mcp->mb[2] = MSW(reg); + mcp->out_mb = MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_4|MBX_3|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, 0x114c, + "Failed=%x mb[0]=%x mb[1]=%x.\n", + rval, mcp->mb[0], mcp->mb[1]); + } else { + *data = (mcp->mb[3] | (mcp->mb[4] << 16)); + if (*data == QLA8XXX_BAD_VALUE) { + /* + * During soft-reset CAMRAM register reads might + * return 0xbad0bad0. So retry for MAX of 2 sec + * while reading camram registers. + */ + if (time_after(jiffies, retry_max_time)) { + ql_dbg(ql_dbg_mbx, vha, 0x1141, + "Failure to read CAMRAM register. " + "data=0x%x.\n", *data); + return QLA_FUNCTION_FAILED; + } + msleep(100); + goto retry_rd_reg; + } + ql_dbg(ql_dbg_mbx, vha, 0x1142, "Done %s.\n", __func__); + } + + return rval; +} + +int +qla83xx_restart_nic_firmware(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_QLA83XX(ha)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx, vha, 0x1143, "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_RESTART_NIC_FIRMWARE; + mcp->out_mb = 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, 0x1144, + "Failed=%x mb[0]=%x mb[1]=%x.\n", + rval, mcp->mb[0], mcp->mb[1]); + ha->isp_ops->fw_dump(vha, 0); + } else { + ql_dbg(ql_dbg_mbx, vha, 0x1145, "Done %s.\n", __func__); + } + + return rval; +} + +int +qla83xx_access_control(scsi_qla_host_t *vha, uint16_t options, + uint32_t start_addr, uint32_t end_addr, uint16_t *sector_size) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + uint8_t subcode = (uint8_t)options; + struct qla_hw_data *ha = vha->hw; + + if (!IS_QLA8031(ha)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx, vha, 0x1146, "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_SET_ACCESS_CONTROL; + mcp->mb[1] = options; + mcp->out_mb = MBX_1|MBX_0; + if (subcode & BIT_2) { + mcp->mb[2] = LSW(start_addr); + mcp->mb[3] = MSW(start_addr); + mcp->mb[4] = LSW(end_addr); + mcp->mb[5] = MSW(end_addr); + mcp->out_mb |= MBX_5|MBX_4|MBX_3|MBX_2; + } + mcp->in_mb = MBX_2|MBX_1|MBX_0; + if (!(subcode & (BIT_2 | BIT_5))) + mcp->in_mb |= MBX_4|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, 0x1147, + "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x mb[3]=%x mb[4]=%x.\n", + rval, mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3], + mcp->mb[4]); + ha->isp_ops->fw_dump(vha, 0); + } else { + if (subcode & BIT_5) + *sector_size = mcp->mb[1]; + else if (subcode & (BIT_6 | BIT_7)) { + ql_dbg(ql_dbg_mbx, vha, 0x1148, + "Driver-lock id=%x%x", mcp->mb[4], mcp->mb[3]); + } else if (subcode & (BIT_3 | BIT_4)) { + ql_dbg(ql_dbg_mbx, vha, 0x1149, + "Flash-lock id=%x%x", mcp->mb[4], mcp->mb[3]); + } + ql_dbg(ql_dbg_mbx, vha, 0x114a, "Done %s.\n", __func__); + } + + return rval; +} + +int +qla2x00_dump_mctp_data(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; + + if (!IS_MCTP_CAPABLE(vha->hw)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x114f, + "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_DUMP_RISC_RAM_EXTENDED; + mcp->mb[1] = LSW(addr); + mcp->mb[2] = MSW(req_dma); + mcp->mb[3] = LSW(req_dma); + mcp->mb[4] = MSW(size); + mcp->mb[5] = LSW(size); + mcp->mb[6] = MSW(MSD(req_dma)); + mcp->mb[7] = LSW(MSD(req_dma)); + mcp->mb[8] = MSW(addr); + /* Setting RAM ID to valid */ + mcp->mb[10] |= BIT_7; + /* For MCTP RAM ID is 0x40 */ + mcp->mb[10] |= 0x40; + + mcp->out_mb |= MBX_10|MBX_8|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, 0x114e, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); } else { - DEBUG11(qla_printk(KERN_INFO, ha, - "%s(%ld): done.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x114d, + "Done %s.\n", __func__); } return rval; diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c index 5e343919aca..89998244f48 100644 --- a/drivers/scsi/qla2xxx/qla_mid.c +++ b/drivers/scsi/qla2xxx/qla_mid.c @@ -1,11 +1,12 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ #include "qla_def.h" #include "qla_gbl.h" +#include "qla_target.h" #include <linux/moduleparam.h> #include <linux/vmalloc.h> @@ -36,8 +37,9 @@ qla24xx_allocate_vp_id(scsi_qla_host_t *vha) 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) { - DEBUG15(printk ("vp_id %d is bigger than max-supported %d.\n", - 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; } @@ -48,6 +50,9 @@ qla24xx_allocate_vp_id(scsi_qla_host_t *vha) spin_lock_irqsave(&ha->vport_slock, flags); list_add_tail(&vha->list, &ha->vp_list); + + qlt_update_vp_map(vha, SET_VP_IDX); + spin_unlock_irqrestore(&ha->vport_slock, flags); mutex_unlock(&ha->vport_lock); @@ -78,6 +83,7 @@ qla24xx_deallocate_vp_id(scsi_qla_host_t *vha) spin_lock_irqsave(&ha->vport_slock, flags); } list_del(&vha->list); + qlt_update_vp_map(vha, RESET_VP_IDX); spin_unlock_irqrestore(&ha->vport_slock, flags); vp_id = vha->vp_idx; @@ -131,9 +137,9 @@ qla2x00_mark_vp_devices_dead(scsi_qla_host_t *vha) fc_port_t *fcport; list_for_each_entry(fcport, &vha->vp_fcports, list) { - DEBUG15(printk("scsi(%ld): Marking port dead, " - "loop_id=0x%04x :%x\n", - vha->host_no, fcport->loop_id, fcport->vp_idx)); + ql_dbg(ql_dbg_vport, vha, 0xa001, + "Marking port dead, loop_id=0x%04x : %x.\n", + fcport->loop_id, fcport->vha->vp_idx); qla2x00_mark_device_lost(vha, fcport, 0, 0); qla2x00_set_fcport_state(fcport, FCS_UNCONFIGURED); @@ -143,12 +149,18 @@ qla2x00_mark_vp_devices_dead(scsi_qla_host_t *vha) int qla24xx_disable_vp(scsi_qla_host_t *vha) { + unsigned long flags; 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); + /* Remove port id from vp target map */ + spin_lock_irqsave(&vha->hw->vport_slock, flags); + qlt_update_vp_map(vha, RESET_AL_PA); + spin_unlock_irqrestore(&vha->hw->vport_slock, flags); + qla2x00_mark_vp_devices_dead(vha); atomic_set(&vha->vp_state, VP_FAILED); vha->flags.management_server_logged_in = 0; @@ -187,13 +199,13 @@ qla24xx_enable_vp(scsi_qla_host_t *vha) goto enable_failed; } - DEBUG15(qla_printk(KERN_INFO, ha, - "Virtual port with id: %d - Enabled\n", vha->vp_idx)); + ql_dbg(ql_dbg_taskm, vha, 0x801a, + "Virtual port with id: %d - Enabled.\n", vha->vp_idx); return 0; enable_failed: - DEBUG15(qla_printk(KERN_INFO, ha, - "Virtual port with id: %d - Disabled\n", vha->vp_idx)); + ql_dbg(ql_dbg_taskm, vha, 0x801b, + "Virtual port with id: %d - Disabled.\n", vha->vp_idx); return 1; } @@ -205,12 +217,12 @@ qla24xx_configure_vp(scsi_qla_host_t *vha) fc_vport = vha->fc_vport; - DEBUG15(printk("scsi(%ld): %s: change request #3 for this host.\n", - vha->host_no, __func__)); + 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) { - DEBUG15(qla_printk(KERN_ERR, vha->hw, "Failed to enable " - "receiving of RSCN requests: 0x%x\n", ret)); + ql_dbg(ql_dbg_vport, vha, 0xa003, "Failed to enable " + "receiving of RSCN requests: 0x%x.\n", ret); return; } else { /* Corresponds to SCR enabled */ @@ -248,9 +260,9 @@ qla2x00_alert_all_vps(struct rsp_que *rsp, uint16_t *mb) case MBA_CHG_IN_CONNECTION: case MBA_PORT_UPDATE: case MBA_RSCN_UPDATE: - DEBUG15(printk("scsi(%ld)%s: Async_event for" - " VP[%d], mb = 0x%x, vha=%p\n", - vha->host_no, __func__, i, *mb, vha)); + 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; } @@ -286,37 +298,47 @@ qla2x00_vp_abort_isp(scsi_qla_host_t *vha) if (!test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) qla24xx_control_vp(vha, VCE_COMMAND_DISABLE_VPS_LOGO_ALL); - DEBUG15(printk("scsi(%ld): Scheduling enable of Vport %d...\n", - vha->host_no, vha->vp_idx)); + 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 + ql_dbg_verbose, vha, 0x4012, + "Entering %s vp_flags: 0x%lx.\n", __func__, 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) { - DEBUG(printk("scsi(%ld): qla2x00_port_login()\n", - vha->host_no)); + ql_dbg(ql_dbg_dpc, vha, 0x4018, + "Relogin needed scheduled.\n"); qla2x00_relogin(vha); - - DEBUG(printk("scsi(%ld): qla2x00_port_login - end\n", - vha->host_no)); + ql_dbg(ql_dbg_dpc, vha, 0x4019, + "Relogin needed end.\n"); } if (test_and_clear_bit(RESET_MARKER_NEEDED, &vha->dpc_flags) && @@ -326,11 +348,17 @@ qla2x00_do_dpc_vp(scsi_qla_host_t *vha) 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 + ql_dbg_verbose, vha, 0x401c, + "Exiting %s.\n", __func__); return 0; } @@ -396,9 +424,10 @@ qla24xx_vport_create_req_sanity_check(struct fc_vport *fc_vport) /* Check up max-npiv-supports */ if (ha->num_vhosts > ha->max_npiv_vports) { - DEBUG15(printk("scsi(%ld): num_vhosts %ud is bigger than " - "max_npv_vports %ud.\n", base_vha->host_no, - 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; @@ -415,7 +444,8 @@ qla24xx_create_vhost(struct fc_vport *fc_vport) vha = qla2x00_create_host(sht, ha); if (!vha) { - DEBUG(printk("qla2xxx: scsi_host_alloc() failed for vport\n")); + ql_log(ql_log_warn, vha, 0xa005, + "scsi_host_alloc() failed for vport.\n"); return(NULL); } @@ -429,8 +459,8 @@ qla24xx_create_vhost(struct fc_vport *fc_vport) vha->device_flags = 0; vha->vp_idx = qla24xx_allocate_vp_id(vha); if (vha->vp_idx > ha->max_npiv_vports) { - DEBUG15(printk("scsi(%ld): Couldn't allocate vp_id.\n", - vha->host_no)); + 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; @@ -449,20 +479,20 @@ qla24xx_create_vhost(struct fc_vport *fc_vport) vha->req = base_vha->req; host->can_queue = base_vha->req->length + 128; - host->this_id = 255; host->cmd_per_lun = 3; - if ((IS_QLA25XX(ha) || IS_QLA81XX(ha)) && ql2xenabledif) + 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 = MAX_TARGETS_2200; + host->max_id = ha->max_fibre_devices; host->transportt = qla2xxx_transport_vport_template; - DEBUG15(printk("DEBUG: detect vport hba %ld at address = %p\n", - vha->host_no, vha)); + ql_dbg(ql_dbg_vport, vha, 0xa007, + "Detect vport hba %ld at address = %p.\n", + vha->host_no, vha); vha->flags.init_done = 1; @@ -493,6 +523,7 @@ qla25xx_free_req_que(struct scsi_qla_host *vha, struct req_que *req) clear_bit(que_id, ha->req_qid_map); mutex_unlock(&ha->vport_lock); } + kfree(req->outstanding_cmds); kfree(req); req = NULL; } @@ -567,9 +598,9 @@ qla25xx_delete_queues(struct scsi_qla_host *vha) if (req) { ret = qla25xx_delete_req_que(vha, req); if (ret != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, - "Couldn't delete req que %d\n", - req->id); + ql_log(ql_log_warn, vha, 0x00ea, + "Couldn't delete req que %d.\n", + req->id); return ret; } } @@ -581,9 +612,9 @@ qla25xx_delete_queues(struct scsi_qla_host *vha) if (rsp) { ret = qla25xx_delete_rsp_que(vha, rsp); if (ret != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, - "Couldn't delete rsp que %d\n", - rsp->id); + ql_log(ql_log_warn, vha, 0x00eb, + "Couldn't delete rsp que %d.\n", + rsp->id); return ret; } } @@ -599,13 +630,13 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options, 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; + device_reg_t *reg; uint32_t cnt; req = kzalloc(sizeof(struct req_que), GFP_KERNEL); if (req == NULL) { - qla_printk(KERN_WARNING, ha, "could not allocate memory" - "for request que\n"); + ql_log(ql_log_fatal, base_vha, 0x00d9, + "Failed to allocate memory for request queue.\n"); goto failed; } @@ -614,17 +645,21 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options, (req->length + 1) * sizeof(request_t), &req->dma, GFP_KERNEL); if (req->ring == NULL) { - qla_printk(KERN_WARNING, ha, - "Memory Allocation failed - request_ring\n"); + ql_log(ql_log_fatal, base_vha, 0x00da, + "Failed to allocate memory for request_ring.\n"); goto que_failed; } + ret = qla2x00_alloc_outstanding_cmds(ha, req); + if (ret != QLA_SUCCESS) + 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); - qla_printk(KERN_INFO, ha, "No resources to create " - "additional request queue\n"); + 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); @@ -633,6 +668,12 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options, 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 @@ -645,7 +686,11 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options, options |= BIT_5; req->options = options; - for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) + 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 < req->num_outstanding_cmds; cnt++) req->outstanding_cmds[cnt] = NULL; req->current_outstanding_cmd = 1; @@ -654,12 +699,25 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options, req->cnt = req->length; req->id = que_id; reg = ISP_QUE_REG(ha, que_id); + req->req_q_in = ®->isp25mq.req_q_in; + req->req_q_out = ®->isp25mq.req_q_out; 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) { - qla_printk(KERN_WARNING, ha, "%s failed\n", __func__); + 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); @@ -696,12 +754,12 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options, 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; + device_reg_t *reg; rsp = kzalloc(sizeof(struct rsp_que), GFP_KERNEL); if (rsp == NULL) { - qla_printk(KERN_WARNING, ha, "could not allocate memory for" - " response que\n"); + ql_log(ql_log_warn, base_vha, 0x0066, + "Failed to allocate memory for response queue.\n"); goto failed; } @@ -710,8 +768,8 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options, (rsp->length + 1) * sizeof(response_t), &rsp->dma, GFP_KERNEL); if (rsp->ring == NULL) { - qla_printk(KERN_WARNING, ha, - "Memory Allocation failed - response_ring\n"); + ql_log(ql_log_warn, base_vha, 0x00e1, + "Failed to allocate memory for response ring.\n"); goto que_failed; } @@ -719,8 +777,8 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options, 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); - qla_printk(KERN_INFO, ha, "No resources to create " - "additional response queue\n"); + 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); @@ -728,12 +786,16 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options, if (ha->flags.msix_enabled) rsp->msix = &ha->msix_entries[que_id + 1]; else - qla_printk(KERN_WARNING, ha, "msix not enabled\n"); + 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; @@ -750,6 +812,14 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options, rsp->rsp_q_in = ®->isp25mq.rsp_q_in; rsp->rsp_q_out = ®->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) @@ -757,7 +827,8 @@ qla25xx_create_rsp_que(struct qla_hw_data *ha, uint16_t options, ret = qla25xx_init_rsp_que(base_vha, rsp); if (ret != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, "%s failed\n", __func__); + 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); diff --git a/drivers/scsi/qla2xxx/qla_mr.c b/drivers/scsi/qla2xxx/qla_mr.c new file mode 100644 index 00000000000..abeb3901498 --- /dev/null +++ b/drivers/scsi/qla2xxx/qla_mr.c @@ -0,0 +1,3472 @@ +/* + * QLogic Fibre Channel HBA Driver + * Copyright (c) 2003-2014 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> +#include <linux/utsname.h> + + +/* QLAFX00 specific Mailbox implementation functions */ + +/* + * qlafx00_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 +qlafx00_mailbox_command(scsi_qla_host_t *vha, struct mbx_cmd_32 *mcp) + +{ + int rval; + unsigned long flags = 0; + device_reg_t *reg; + uint8_t abort_active; + uint8_t io_lock_on; + uint16_t command = 0; + uint32_t *iptr; + uint32_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); + + if (ha->pdev->error_state > pci_channel_io_frozen) { + ql_log(ql_log_warn, vha, 0x115c, + "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, 0x115f, + "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, 0x1175, + "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, 0x1176, + "FW hung = %d.\n", ha->flags.isp82xx_fw_hung); + rval = QLA_FUNCTION_FAILED; + goto premature_exit; + } + + /* + * 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, 0x1177, + "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->mcp32 = mcp; + + ql_dbg(ql_dbg_mbx, vha, 0x1178, + "Prepare to issue mbox cmd=0x%x.\n", mcp->mb[0]); + + spin_lock_irqsave(&ha->hardware_lock, flags); + + /* Load mailbox registers. */ + optr = (uint32_t __iomem *)®->ispfx00.mailbox0; + + iptr = mcp->mb; + command = mcp->mb[0]; + mboxes = mcp->out_mb; + + for (cnt = 0; cnt < ha->mbx_count; cnt++) { + if (mboxes & BIT_0) + WRT_REG_DWORD(optr, *iptr); + + mboxes >>= 1; + optr++; + iptr++; + } + + /* Issue set host interrupt command to send cmd out. */ + ha->flags.mbox_int = 0; + clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); + + ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1172, + (uint8_t *)mcp->mb, 16); + ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1173, + ((uint8_t *)mcp->mb + 0x10), 16); + ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1174, + ((uint8_t *)mcp->mb + 0x20), 8); + + /* Unlock mbx registers and wait for interrupt */ + ql_dbg(ql_dbg_mbx, vha, 0x1179, + "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); + + QLAFX00_SET_HST_INTR(ha, ha->mbx_intr_code); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + wait_for_completion_timeout(&ha->mbx_intr_comp, mcp->tov * HZ); + } else { + ql_dbg(ql_dbg_mbx, vha, 0x112c, + "Cmd=%x Polling Mode.\n", command); + + QLAFX00_SET_HST_INTR(ha, ha->mbx_intr_code); + 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)) + usleep_range(10000, 11000); + } /* while */ + ql_dbg(ql_dbg_mbx, vha, 0x112d, + "Waited %d sec.\n", + (uint)((jiffies - (wait_time - (mcp->tov * HZ)))/HZ)); + } + + /* Check whether we timed out */ + if (ha->flags.mbox_int) { + uint32_t *iptr2; + + ql_dbg(ql_dbg_mbx, vha, 0x112e, + "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->mailbox_out32[0] != MBS_COMMAND_COMPLETE) + rval = QLA_FUNCTION_FAILED; + + /* Load return mailbox registers. */ + iptr2 = mcp->mb; + iptr = (uint32_t *)&ha->mailbox_out32[0]; + mboxes = mcp->in_mb; + for (cnt = 0; cnt < ha->mbx_count; cnt++) { + if (mboxes & BIT_0) + *iptr2 = *iptr; + + mboxes >>= 1; + iptr2++; + iptr++; + } + } else { + + rval = QLA_FUNCTION_TIMEOUT; + } + + ha->flags.mbox_busy = 0; + + /* Clean up */ + ha->mcp32 = NULL; + + if ((abort_active || !io_lock_on) && !IS_NOPOLLING_TYPE(ha)) { + ql_dbg(ql_dbg_mbx, vha, 0x113a, + "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, 0x115d, + "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)) { + + ql_log(ql_log_info, base_vha, 0x115e, + "Mailbox cmd timeout occurred, 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, 0x1160, + "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)) { + + ql_log(ql_log_info, base_vha, 0x1161, + "Mailbox cmd timeout occurred, 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); + 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, 0x1162, + "Finished abort_isp.\n"); + } + } + } + +premature_exit: + /* Allow next mbx cmd to come in. */ + complete(&ha->mbx_cmd_comp); + + if (rval) { + ql_log(ql_log_warn, base_vha, 0x1163, + "**** 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, 0x1164, "Done %s.\n", __func__); + } + + return rval; +} + +/* + * qlafx00_driver_shutdown + * Indicate a driver shutdown to firmware. + * + * Input: + * ha = adapter block pointer. + * + * Returns: + * local function return status code. + * + * Context: + * Kernel context. + */ +int +qlafx00_driver_shutdown(scsi_qla_host_t *vha, int tmo) +{ + int rval; + struct mbx_cmd_32 mc; + struct mbx_cmd_32 *mcp = &mc; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1166, + "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_MR_DRV_SHUTDOWN; + mcp->out_mb = MBX_0; + mcp->in_mb = MBX_0; + if (tmo) + mcp->tov = tmo; + else + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qlafx00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x1167, + "Failed=%x.\n", rval); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1168, + "Done %s.\n", __func__); + } + + return rval; +} + +/* + * qlafx00_get_firmware_state + * Get adapter firmware state. + * + * Input: + * ha = adapter block pointer. + * TARGET_QUEUE_LOCK must be released. + * ADAPTER_STATE_LOCK must be released. + * + * Returns: + * qla7xxx local function return status code. + * + * Context: + * Kernel context. + */ +static int +qlafx00_get_firmware_state(scsi_qla_host_t *vha, uint32_t *states) +{ + int rval; + struct mbx_cmd_32 mc; + struct mbx_cmd_32 *mcp = &mc; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1169, + "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_GET_FIRMWARE_STATE; + mcp->out_mb = MBX_0; + mcp->in_mb = MBX_1|MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qlafx00_mailbox_command(vha, mcp); + + /* Return firmware states. */ + states[0] = mcp->mb[1]; + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x116a, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x116b, + "Done %s.\n", __func__); + } + return rval; +} + +/* + * qlafx00_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: + * qlafx00 local function return status code. + * + * Context: + * Kernel context. + */ +int +qlafx00_init_firmware(scsi_qla_host_t *vha, uint16_t size) +{ + int rval; + struct mbx_cmd_32 mc; + struct mbx_cmd_32 *mcp = &mc; + struct qla_hw_data *ha = vha->hw; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x116c, + "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_INITIALIZE_FIRMWARE; + + mcp->mb[1] = 0; + mcp->mb[2] = MSD(ha->init_cb_dma); + mcp->mb[3] = LSD(ha->init_cb_dma); + + mcp->out_mb = MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_0; + mcp->buf_size = size; + mcp->flags = MBX_DMA_OUT; + mcp->tov = MBX_TOV_SECONDS; + rval = qlafx00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x116d, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x116e, + "Done %s.\n", __func__); + } + return rval; +} + +/* + * qlafx00_mbx_reg_test + */ +static int +qlafx00_mbx_reg_test(scsi_qla_host_t *vha) +{ + int rval; + struct mbx_cmd_32 mc; + struct mbx_cmd_32 *mcp = &mc; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x116f, + "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->mb[8] = 0xBBBB; + mcp->mb[9] = 0x6666; + mcp->mb[10] = 0xBB66; + mcp->mb[11] = 0x66BB; + mcp->mb[12] = 0xB6B6; + mcp->mb[13] = 0x6B6B; + mcp->mb[14] = 0x3636; + mcp->mb[15] = 0xCCCC; + + + mcp->out_mb = MBX_15|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_15|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->buf_size = 0; + mcp->flags = MBX_DMA_OUT; + mcp->tov = MBX_TOV_SECONDS; + rval = qlafx00_mailbox_command(vha, mcp); + if (rval == QLA_SUCCESS) { + if (mcp->mb[17] != 0xAAAA || mcp->mb[18] != 0x5555 || + mcp->mb[19] != 0xAA55 || mcp->mb[20] != 0x55AA) + rval = QLA_FUNCTION_FAILED; + if (mcp->mb[21] != 0xA5A5 || mcp->mb[22] != 0x5A5A || + mcp->mb[23] != 0x2525 || mcp->mb[24] != 0xBBBB) + rval = QLA_FUNCTION_FAILED; + if (mcp->mb[25] != 0x6666 || mcp->mb[26] != 0xBB66 || + mcp->mb[27] != 0x66BB || mcp->mb[28] != 0xB6B6) + rval = QLA_FUNCTION_FAILED; + if (mcp->mb[29] != 0x6B6B || mcp->mb[30] != 0x3636 || + mcp->mb[31] != 0xCCCC) + rval = QLA_FUNCTION_FAILED; + } + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x1170, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1171, + "Done %s.\n", __func__); + } + return rval; +} + +/** + * qlafx00_pci_config() - Setup ISPFx00 PCI configuration registers. + * @ha: HA context + * + * Returns 0 on success. + */ +int +qlafx00_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_is_pcie(ha->pdev)) + pcie_set_readrq(ha->pdev, 2048); + + ha->chip_revision = ha->pdev->revision; + + return QLA_SUCCESS; +} + +/** + * qlafx00_warm_reset() - Perform warm reset of iSA(CPUs being reset on SOC). + * @ha: HA context + * + */ +static inline void +qlafx00_soc_cpu_reset(scsi_qla_host_t *vha) +{ + unsigned long flags = 0; + struct qla_hw_data *ha = vha->hw; + int i, core; + uint32_t cnt; + uint32_t reg_val; + + spin_lock_irqsave(&ha->hardware_lock, flags); + + QLAFX00_SET_HBA_SOC_REG(ha, 0x80004, 0); + QLAFX00_SET_HBA_SOC_REG(ha, 0x82004, 0); + + /* stop the XOR DMA engines */ + QLAFX00_SET_HBA_SOC_REG(ha, 0x60920, 0x02); + QLAFX00_SET_HBA_SOC_REG(ha, 0x60924, 0x02); + QLAFX00_SET_HBA_SOC_REG(ha, 0xf0920, 0x02); + QLAFX00_SET_HBA_SOC_REG(ha, 0xf0924, 0x02); + + /* stop the IDMA engines */ + reg_val = QLAFX00_GET_HBA_SOC_REG(ha, 0x60840); + reg_val &= ~(1<<12); + QLAFX00_SET_HBA_SOC_REG(ha, 0x60840, reg_val); + + reg_val = QLAFX00_GET_HBA_SOC_REG(ha, 0x60844); + reg_val &= ~(1<<12); + QLAFX00_SET_HBA_SOC_REG(ha, 0x60844, reg_val); + + reg_val = QLAFX00_GET_HBA_SOC_REG(ha, 0x60848); + reg_val &= ~(1<<12); + QLAFX00_SET_HBA_SOC_REG(ha, 0x60848, reg_val); + + reg_val = QLAFX00_GET_HBA_SOC_REG(ha, 0x6084C); + reg_val &= ~(1<<12); + QLAFX00_SET_HBA_SOC_REG(ha, 0x6084C, reg_val); + + for (i = 0; i < 100000; i++) { + if ((QLAFX00_GET_HBA_SOC_REG(ha, 0xd0000) & 0x10000000) == 0 && + (QLAFX00_GET_HBA_SOC_REG(ha, 0x10600) & 0x1) == 0) + break; + udelay(100); + } + + /* Set all 4 cores in reset */ + for (i = 0; i < 4; i++) { + QLAFX00_SET_HBA_SOC_REG(ha, + (SOC_SW_RST_CONTROL_REG_CORE0 + 8*i), (0xF01)); + QLAFX00_SET_HBA_SOC_REG(ha, + (SOC_SW_RST_CONTROL_REG_CORE0 + 4 + 8*i), (0x01010101)); + } + + /* Reset all units in Fabric */ + QLAFX00_SET_HBA_SOC_REG(ha, SOC_FABRIC_RST_CONTROL_REG, (0x011f0101)); + + /* */ + QLAFX00_SET_HBA_SOC_REG(ha, 0x10610, 1); + QLAFX00_SET_HBA_SOC_REG(ha, 0x10600, 0); + + /* Set all 4 core Memory Power Down Registers */ + for (i = 0; i < 5; i++) { + QLAFX00_SET_HBA_SOC_REG(ha, + (SOC_PWR_MANAGEMENT_PWR_DOWN_REG + 4*i), (0x0)); + } + + /* Reset all interrupt control registers */ + for (i = 0; i < 115; i++) { + QLAFX00_SET_HBA_SOC_REG(ha, + (SOC_INTERRUPT_SOURCE_I_CONTROL_REG + 4*i), (0x0)); + } + + /* Reset Timers control registers. per core */ + for (core = 0; core < 4; core++) + for (i = 0; i < 8; i++) + QLAFX00_SET_HBA_SOC_REG(ha, + (SOC_CORE_TIMER_REG + 0x100*core + 4*i), (0x0)); + + /* Reset per core IRQ ack register */ + for (core = 0; core < 4; core++) + QLAFX00_SET_HBA_SOC_REG(ha, + (SOC_IRQ_ACK_REG + 0x100*core), (0x3FF)); + + /* Set Fabric control and config to defaults */ + QLAFX00_SET_HBA_SOC_REG(ha, SOC_FABRIC_CONTROL_REG, (0x2)); + QLAFX00_SET_HBA_SOC_REG(ha, SOC_FABRIC_CONFIG_REG, (0x3)); + + /* Kick in Fabric units */ + QLAFX00_SET_HBA_SOC_REG(ha, SOC_FABRIC_RST_CONTROL_REG, (0x0)); + + /* Kick in Core0 to start boot process */ + QLAFX00_SET_HBA_SOC_REG(ha, SOC_SW_RST_CONTROL_REG_CORE0, (0xF00)); + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + /* Wait 10secs for soft-reset to complete. */ + for (cnt = 10; cnt; cnt--) { + msleep(1000); + barrier(); + } +} + +/** + * qlafx00_soft_reset() - Soft Reset ISPFx00. + * @ha: HA context + * + * Returns 0 on success. + */ +void +qlafx00_soft_reset(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + + if (unlikely(pci_channel_offline(ha->pdev) && + ha->flags.pci_channel_io_perm_failure)) + return; + + ha->isp_ops->disable_intrs(ha); + qlafx00_soc_cpu_reset(vha); +} + +/** + * qlafx00_chip_diag() - Test ISPFx00 for proper operation. + * @ha: HA context + * + * Returns 0 on success. + */ +int +qlafx00_chip_diag(scsi_qla_host_t *vha) +{ + int rval = 0; + struct qla_hw_data *ha = vha->hw; + struct req_que *req = ha->req_q_map[0]; + + ha->fw_transfer_size = REQUEST_ENTRY_SIZE * req->length; + + rval = qlafx00_mbx_reg_test(vha); + if (rval) { + ql_log(ql_log_warn, vha, 0x1165, + "Failed mailbox send register test\n"); + } else { + /* Flag a successful rval */ + rval = QLA_SUCCESS; + } + return rval; +} + +void +qlafx00_config_rings(struct scsi_qla_host *vha) +{ + struct qla_hw_data *ha = vha->hw; + struct device_reg_fx00 __iomem *reg = &ha->iobase->ispfx00; + + WRT_REG_DWORD(®->req_q_in, 0); + WRT_REG_DWORD(®->req_q_out, 0); + + WRT_REG_DWORD(®->rsp_q_in, 0); + WRT_REG_DWORD(®->rsp_q_out, 0); + + /* PCI posting */ + RD_REG_DWORD(®->rsp_q_out); +} + +char * +qlafx00_pci_info_str(struct scsi_qla_host *vha, char *str) +{ + struct qla_hw_data *ha = vha->hw; + + if (pci_is_pcie(ha->pdev)) { + strcpy(str, "PCIe iSA"); + return str; + } + return str; +} + +char * +qlafx00_fw_version_str(struct scsi_qla_host *vha, char *str) +{ + struct qla_hw_data *ha = vha->hw; + + sprintf(str, "%s", ha->mr.fw_version); + return str; +} + +void +qlafx00_enable_intrs(struct qla_hw_data *ha) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&ha->hardware_lock, flags); + ha->interrupts_on = 1; + QLAFX00_ENABLE_ICNTRL_REG(ha); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + +void +qlafx00_disable_intrs(struct qla_hw_data *ha) +{ + unsigned long flags = 0; + + spin_lock_irqsave(&ha->hardware_lock, flags); + ha->interrupts_on = 0; + QLAFX00_DISABLE_ICNTRL_REG(ha); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + +int +qlafx00_abort_target(fc_port_t *fcport, unsigned int l, int tag) +{ + return qla2x00_async_tm_cmd(fcport, TCF_TARGET_RESET, l, tag); +} + +int +qlafx00_lun_reset(fc_port_t *fcport, unsigned int l, int tag) +{ + return qla2x00_async_tm_cmd(fcport, TCF_LUN_RESET, l, tag); +} + +int +qlafx00_loop_reset(scsi_qla_host_t *vha) +{ + int ret; + struct fc_port *fcport; + struct qla_hw_data *ha = vha->hw; + + if (ql2xtargetreset) { + 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, 0x803d, + "Bus Reset failed: Reset=%d " + "d_id=%x.\n", ret, fcport->d_id.b24); + } + } + } + return QLA_SUCCESS; +} + +int +qlafx00_iospace_config(struct qla_hw_data *ha) +{ + if (pci_request_selected_regions(ha->pdev, ha->bars, + QLA2XXX_DRIVER_NAME)) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x014e, + "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, 0x014f, + "Invalid pci I/O region size (%s).\n", + pci_name(ha->pdev)); + goto iospace_error_exit; + } + if (pci_resource_len(ha->pdev, 0) < BAR0_LEN_FX00) { + ql_log_pci(ql_log_warn, ha->pdev, 0x0127, + "Invalid PCI mem BAR0 region size (%s), aborting\n", + pci_name(ha->pdev)); + goto iospace_error_exit; + } + + ha->cregbase = + ioremap_nocache(pci_resource_start(ha->pdev, 0), BAR0_LEN_FX00); + if (!ha->cregbase) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x0128, + "cannot remap MMIO (%s), aborting\n", pci_name(ha->pdev)); + goto iospace_error_exit; + } + + if (!(pci_resource_flags(ha->pdev, 2) & IORESOURCE_MEM)) { + ql_log_pci(ql_log_warn, ha->pdev, 0x0129, + "region #2 not an MMIO resource (%s), aborting\n", + pci_name(ha->pdev)); + goto iospace_error_exit; + } + if (pci_resource_len(ha->pdev, 2) < BAR2_LEN_FX00) { + ql_log_pci(ql_log_warn, ha->pdev, 0x012a, + "Invalid PCI mem BAR2 region size (%s), aborting\n", + pci_name(ha->pdev)); + goto iospace_error_exit; + } + + ha->iobase = + ioremap_nocache(pci_resource_start(ha->pdev, 2), BAR2_LEN_FX00); + if (!ha->iobase) { + ql_log_pci(ql_log_fatal, ha->pdev, 0x012b, + "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; + + ql_log_pci(ql_log_info, ha->pdev, 0x012c, + "Bars 0x%x, iobase0 0x%p, iobase2 0x%p\n", + ha->bars, ha->cregbase, ha->iobase); + + return 0; + +iospace_error_exit: + return -ENOMEM; +} + +static void +qlafx00_save_queue_ptrs(struct scsi_qla_host *vha) +{ + 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]; + + req->length_fx00 = req->length; + req->ring_fx00 = req->ring; + req->dma_fx00 = req->dma; + + rsp->length_fx00 = rsp->length; + rsp->ring_fx00 = rsp->ring; + rsp->dma_fx00 = rsp->dma; + + ql_dbg(ql_dbg_init, vha, 0x012d, + "req: %p, ring_fx00: %p, length_fx00: 0x%x," + "req->dma_fx00: 0x%llx\n", req, req->ring_fx00, + req->length_fx00, (u64)req->dma_fx00); + + ql_dbg(ql_dbg_init, vha, 0x012e, + "rsp: %p, ring_fx00: %p, length_fx00: 0x%x," + "rsp->dma_fx00: 0x%llx\n", rsp, rsp->ring_fx00, + rsp->length_fx00, (u64)rsp->dma_fx00); +} + +static int +qlafx00_config_queues(struct scsi_qla_host *vha) +{ + 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]; + dma_addr_t bar2_hdl = pci_resource_start(ha->pdev, 2); + + req->length = ha->req_que_len; + req->ring = (void *)ha->iobase + ha->req_que_off; + req->dma = bar2_hdl + ha->req_que_off; + if ((!req->ring) || (req->length == 0)) { + ql_log_pci(ql_log_info, ha->pdev, 0x012f, + "Unable to allocate memory for req_ring\n"); + return QLA_FUNCTION_FAILED; + } + + ql_dbg(ql_dbg_init, vha, 0x0130, + "req: %p req_ring pointer %p req len 0x%x " + "req off 0x%x\n, req->dma: 0x%llx", + req, req->ring, req->length, + ha->req_que_off, (u64)req->dma); + + rsp->length = ha->rsp_que_len; + rsp->ring = (void *)ha->iobase + ha->rsp_que_off; + rsp->dma = bar2_hdl + ha->rsp_que_off; + if ((!rsp->ring) || (rsp->length == 0)) { + ql_log_pci(ql_log_info, ha->pdev, 0x0131, + "Unable to allocate memory for rsp_ring\n"); + return QLA_FUNCTION_FAILED; + } + + ql_dbg(ql_dbg_init, vha, 0x0132, + "rsp: %p rsp_ring pointer %p rsp len 0x%x " + "rsp off 0x%x, rsp->dma: 0x%llx\n", + rsp, rsp->ring, rsp->length, + ha->rsp_que_off, (u64)rsp->dma); + + return QLA_SUCCESS; +} + +static int +qlafx00_init_fw_ready(scsi_qla_host_t *vha) +{ + int rval = 0; + unsigned long wtime; + uint16_t wait_time; /* Wait time */ + struct qla_hw_data *ha = vha->hw; + struct device_reg_fx00 __iomem *reg = &ha->iobase->ispfx00; + uint32_t aenmbx, aenmbx7 = 0; + uint32_t pseudo_aen; + uint32_t state[5]; + bool done = false; + + /* 30 seconds wait - Adjust if required */ + wait_time = 30; + + pseudo_aen = RD_REG_DWORD(®->pseudoaen); + if (pseudo_aen == 1) { + aenmbx7 = RD_REG_DWORD(®->initval7); + ha->mbx_intr_code = MSW(aenmbx7); + ha->rqstq_intr_code = LSW(aenmbx7); + rval = qlafx00_driver_shutdown(vha, 10); + if (rval != QLA_SUCCESS) + qlafx00_soft_reset(vha); + } + + /* wait time before firmware ready */ + wtime = jiffies + (wait_time * HZ); + do { + aenmbx = RD_REG_DWORD(®->aenmailbox0); + barrier(); + ql_dbg(ql_dbg_mbx, vha, 0x0133, + "aenmbx: 0x%x\n", aenmbx); + + switch (aenmbx) { + case MBA_FW_NOT_STARTED: + case MBA_FW_STARTING: + break; + + case MBA_SYSTEM_ERR: + case MBA_REQ_TRANSFER_ERR: + case MBA_RSP_TRANSFER_ERR: + case MBA_FW_INIT_FAILURE: + qlafx00_soft_reset(vha); + break; + + case MBA_FW_RESTART_CMPLT: + /* Set the mbx and rqstq intr code */ + aenmbx7 = RD_REG_DWORD(®->aenmailbox7); + ha->mbx_intr_code = MSW(aenmbx7); + ha->rqstq_intr_code = LSW(aenmbx7); + ha->req_que_off = RD_REG_DWORD(®->aenmailbox1); + ha->rsp_que_off = RD_REG_DWORD(®->aenmailbox3); + ha->req_que_len = RD_REG_DWORD(®->aenmailbox5); + ha->rsp_que_len = RD_REG_DWORD(®->aenmailbox6); + WRT_REG_DWORD(®->aenmailbox0, 0); + RD_REG_DWORD_RELAXED(®->aenmailbox0); + ql_dbg(ql_dbg_init, vha, 0x0134, + "f/w returned mbx_intr_code: 0x%x, " + "rqstq_intr_code: 0x%x\n", + ha->mbx_intr_code, ha->rqstq_intr_code); + QLAFX00_CLR_INTR_REG(ha, QLAFX00_HST_INT_STS_BITS); + rval = QLA_SUCCESS; + done = true; + break; + + default: + if ((aenmbx & 0xFF00) == MBA_FW_INIT_INPROGRESS) + break; + + /* If fw is apparently not ready. In order to continue, + * we might need to issue Mbox cmd, but the problem is + * that the DoorBell vector values that come with the + * 8060 AEN are most likely gone by now (and thus no + * bell would be rung on the fw side when mbox cmd is + * issued). We have to therefore grab the 8060 AEN + * shadow regs (filled in by FW when the last 8060 + * AEN was being posted). + * Do the following to determine what is needed in + * order to get the FW ready: + * 1. reload the 8060 AEN values from the shadow regs + * 2. clear int status to get rid of possible pending + * interrupts + * 3. issue Get FW State Mbox cmd to determine fw state + * Set the mbx and rqstq intr code from Shadow Regs + */ + aenmbx7 = RD_REG_DWORD(®->initval7); + ha->mbx_intr_code = MSW(aenmbx7); + ha->rqstq_intr_code = LSW(aenmbx7); + ha->req_que_off = RD_REG_DWORD(®->initval1); + ha->rsp_que_off = RD_REG_DWORD(®->initval3); + ha->req_que_len = RD_REG_DWORD(®->initval5); + ha->rsp_que_len = RD_REG_DWORD(®->initval6); + ql_dbg(ql_dbg_init, vha, 0x0135, + "f/w returned mbx_intr_code: 0x%x, " + "rqstq_intr_code: 0x%x\n", + ha->mbx_intr_code, ha->rqstq_intr_code); + QLAFX00_CLR_INTR_REG(ha, QLAFX00_HST_INT_STS_BITS); + + /* Get the FW state */ + rval = qlafx00_get_firmware_state(vha, state); + if (rval != QLA_SUCCESS) { + /* Retry if timer has not expired */ + break; + } + + if (state[0] == FSTATE_FX00_CONFIG_WAIT) { + /* Firmware is waiting to be + * initialized by driver + */ + rval = QLA_SUCCESS; + done = true; + break; + } + + /* Issue driver shutdown and wait until f/w recovers. + * Driver should continue to poll until 8060 AEN is + * received indicating firmware recovery. + */ + ql_dbg(ql_dbg_init, vha, 0x0136, + "Sending Driver shutdown fw_state 0x%x\n", + state[0]); + + rval = qlafx00_driver_shutdown(vha, 10); + if (rval != QLA_SUCCESS) { + rval = QLA_FUNCTION_FAILED; + break; + } + msleep(500); + + wtime = jiffies + (wait_time * HZ); + break; + } + + if (!done) { + if (time_after_eq(jiffies, wtime)) { + ql_dbg(ql_dbg_init, vha, 0x0137, + "Init f/w failed: aen[7]: 0x%x\n", + RD_REG_DWORD(®->aenmailbox7)); + rval = QLA_FUNCTION_FAILED; + done = true; + break; + } + /* Delay for a while */ + msleep(500); + } + } while (!done); + + if (rval) + ql_dbg(ql_dbg_init, vha, 0x0138, + "%s **** FAILED ****.\n", __func__); + else + ql_dbg(ql_dbg_init, vha, 0x0139, + "%s **** SUCCESS ****.\n", __func__); + + return rval; +} + +/* + * qlafx00_fw_ready() - Waits for firmware ready. + * @ha: HA context + * + * Returns 0 on success. + */ +int +qlafx00_fw_ready(scsi_qla_host_t *vha) +{ + int rval; + unsigned long wtime; + uint16_t wait_time; /* Wait time if loop is coming ready */ + uint32_t state[5]; + + rval = QLA_SUCCESS; + + wait_time = 10; + + /* wait time before firmware ready */ + wtime = jiffies + (wait_time * HZ); + + /* Wait for ISP to finish init */ + if (!vha->flags.init_done) + ql_dbg(ql_dbg_init, vha, 0x013a, + "Waiting for init to complete...\n"); + + do { + rval = qlafx00_get_firmware_state(vha, state); + + if (rval == QLA_SUCCESS) { + if (state[0] == FSTATE_FX00_INITIALIZED) { + ql_dbg(ql_dbg_init, vha, 0x013b, + "fw_state=%x\n", state[0]); + rval = QLA_SUCCESS; + break; + } + } + rval = QLA_FUNCTION_FAILED; + + if (time_after_eq(jiffies, wtime)) + break; + + /* Delay for a while */ + msleep(500); + + ql_dbg(ql_dbg_init, vha, 0x013c, + "fw_state=%x curr time=%lx.\n", state[0], jiffies); + } while (1); + + + if (rval) + ql_dbg(ql_dbg_init, vha, 0x013d, + "Firmware ready **** FAILED ****.\n"); + else + ql_dbg(ql_dbg_init, vha, 0x013e, + "Firmware ready **** SUCCESS ****.\n"); + + return rval; +} + +static int +qlafx00_find_all_targets(scsi_qla_host_t *vha, + struct list_head *new_fcports) +{ + int rval; + uint16_t tgt_id; + fc_port_t *fcport, *new_fcport; + int found; + struct qla_hw_data *ha = vha->hw; + + rval = QLA_SUCCESS; + + if (!test_bit(LOOP_RESYNC_ACTIVE, &vha->dpc_flags)) + return QLA_FUNCTION_FAILED; + + if ((atomic_read(&vha->loop_down_timer) || + STATE_TRANSITION(vha))) { + atomic_set(&vha->loop_down_timer, 0); + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); + return QLA_FUNCTION_FAILED; + } + + ql_dbg(ql_dbg_disc + ql_dbg_init, vha, 0x2088, + "Listing Target bit map...\n"); + ql_dump_buffer(ql_dbg_disc + ql_dbg_init, vha, + 0x2089, (uint8_t *)ha->gid_list, 32); + + /* Allocate temporary rmtport for any new rmtports discovered. */ + new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); + if (new_fcport == NULL) + return QLA_MEMORY_ALLOC_FAILED; + + for_each_set_bit(tgt_id, (void *)ha->gid_list, + QLAFX00_TGT_NODE_LIST_SIZE) { + + /* Send get target node info */ + new_fcport->tgt_id = tgt_id; + rval = qlafx00_fx_disc(vha, new_fcport, + FXDISC_GET_TGT_NODE_INFO); + if (rval != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x208a, + "Target info scan failed -- assuming zero-entry " + "result...\n"); + 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; + + found++; + + /* + * If tgt_id is same and state FCS_ONLINE, nothing + * changed. + */ + if (fcport->tgt_id == new_fcport->tgt_id && + atomic_read(&fcport->state) == FCS_ONLINE) + break; + + /* + * Tgt ID changed or device was marked to be updated. + */ + ql_dbg(ql_dbg_disc + ql_dbg_init, vha, 0x208b, + "TGT-ID Change(%s): Present tgt id: " + "0x%x state: 0x%x " + "wwnn = %llx wwpn = %llx.\n", + __func__, fcport->tgt_id, + atomic_read(&fcport->state), + (unsigned long long)wwn_to_u64(fcport->node_name), + (unsigned long long)wwn_to_u64(fcport->port_name)); + + ql_log(ql_log_info, vha, 0x208c, + "TGT-ID Announce(%s): Discovered tgt " + "id 0x%x wwnn = %llx " + "wwpn = %llx.\n", __func__, new_fcport->tgt_id, + (unsigned long long) + wwn_to_u64(new_fcport->node_name), + (unsigned long long) + wwn_to_u64(new_fcport->port_name)); + + if (atomic_read(&fcport->state) != FCS_ONLINE) { + fcport->old_tgt_id = fcport->tgt_id; + fcport->tgt_id = new_fcport->tgt_id; + ql_log(ql_log_info, vha, 0x208d, + "TGT-ID: New fcport Added: %p\n", fcport); + qla2x00_update_fcport(vha, fcport); + } else { + ql_log(ql_log_info, vha, 0x208e, + " Existing TGT-ID %x did not get " + " offline event from firmware.\n", + fcport->old_tgt_id); + qla2x00_mark_device_lost(vha, fcport, 0, 0); + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); + kfree(new_fcport); + return rval; + } + 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. */ + new_fcport = qla2x00_alloc_fcport(vha, GFP_KERNEL); + if (new_fcport == NULL) + return QLA_MEMORY_ALLOC_FAILED; + } + + kfree(new_fcport); + return rval; +} + +/* + * qlafx00_configure_all_targets + * Setup target devices with node ID's. + * + * Input: + * ha = adapter block pointer. + * + * Returns: + * 0 = success. + * BIT_0 = error + */ +static int +qlafx00_configure_all_targets(scsi_qla_host_t *vha) +{ + int rval; + fc_port_t *fcport, *rmptemp; + LIST_HEAD(new_fcports); + + rval = qlafx00_fx_disc(vha, &vha->hw->mr.fcport, + FXDISC_GET_TGT_NODE_LIST); + if (rval != QLA_SUCCESS) { + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); + return rval; + } + + rval = qlafx00_find_all_targets(vha, &new_fcports); + if (rval != QLA_SUCCESS) { + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); + return rval; + } + + /* + * Delete all previous devices marked lost. + */ + list_for_each_entry(fcport, &vha->vp_fcports, list) { + if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) + break; + + if (atomic_read(&fcport->state) == FCS_DEVICE_LOST) { + if (fcport->port_type != FCT_INITIATOR) + qla2x00_mark_device_lost(vha, fcport, 0, 0); + } + } + + /* + * Add the new devices to our devices list. + */ + list_for_each_entry_safe(fcport, rmptemp, &new_fcports, list) { + if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) + break; + + qla2x00_update_fcport(vha, fcport); + list_move_tail(&fcport->list, &vha->vp_fcports); + ql_log(ql_log_info, vha, 0x208f, + "Attach new target id 0x%x wwnn = %llx " + "wwpn = %llx.\n", + fcport->tgt_id, + (unsigned long long)wwn_to_u64(fcport->node_name), + (unsigned long long)wwn_to_u64(fcport->port_name)); + } + + /* Free all new device structures not processed. */ + list_for_each_entry_safe(fcport, rmptemp, &new_fcports, list) { + list_del(&fcport->list); + kfree(fcport); + } + + return rval; +} + +/* + * qlafx00_configure_devices + * 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. + */ +int +qlafx00_configure_devices(scsi_qla_host_t *vha) +{ + int rval; + unsigned long flags, save_flags; + rval = QLA_SUCCESS; + + save_flags = flags = vha->dpc_flags; + + ql_dbg(ql_dbg_disc, vha, 0x2090, + "Configure devices -- dpc flags =0x%lx\n", flags); + + rval = qlafx00_configure_all_targets(vha); + + if (rval == QLA_SUCCESS) { + if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) { + rval = QLA_FUNCTION_FAILED; + } else { + atomic_set(&vha->loop_state, LOOP_READY); + ql_log(ql_log_info, vha, 0x2091, + "Device Ready\n"); + } + } + + if (rval) { + ql_dbg(ql_dbg_disc, vha, 0x2092, + "%s *** FAILED ***.\n", __func__); + } else { + ql_dbg(ql_dbg_disc, vha, 0x2093, + "%s: exiting normally.\n", __func__); + } + return rval; +} + +static void +qlafx00_abort_isp_cleanup(scsi_qla_host_t *vha, bool critemp) +{ + struct qla_hw_data *ha = vha->hw; + fc_port_t *fcport; + + vha->flags.online = 0; + ha->mr.fw_hbt_en = 0; + + if (!critemp) { + ha->flags.chip_reset_done = 0; + clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + vha->qla_stats.total_isp_aborts++; + ql_log(ql_log_info, vha, 0x013f, + "Performing ISP error recovery - ha = %p.\n", ha); + ha->isp_ops->reset_chip(vha); + } + + if (atomic_read(&vha->loop_state) != LOOP_DOWN) { + atomic_set(&vha->loop_state, LOOP_DOWN); + atomic_set(&vha->loop_down_timer, + QLAFX00_LOOP_DOWN_TIME); + } else { + if (!atomic_read(&vha->loop_down_timer)) + atomic_set(&vha->loop_down_timer, + QLAFX00_LOOP_DOWN_TIME); + } + + /* Clear all async request states across all VPs. */ + list_for_each_entry(fcport, &vha->vp_fcports, list) { + fcport->flags = 0; + if (atomic_read(&fcport->state) == FCS_ONLINE) + qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST); + } + + if (!ha->flags.eeh_busy) { + if (critemp) { + qla2x00_abort_all_cmds(vha, DID_NO_CONNECT << 16); + } else { + /* Requeue all commands in outstanding command list. */ + qla2x00_abort_all_cmds(vha, DID_RESET << 16); + } + } + + qla2x00_free_irqs(vha); + if (critemp) + set_bit(FX00_CRITEMP_RECOVERY, &vha->dpc_flags); + else + set_bit(FX00_RESET_RECOVERY, &vha->dpc_flags); + + /* Clear the Interrupts */ + QLAFX00_CLR_INTR_REG(ha, QLAFX00_HST_INT_STS_BITS); + + ql_log(ql_log_info, vha, 0x0140, + "%s Done done - ha=%p.\n", __func__, ha); +} + +/** + * qlafx00_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 +qlafx00_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; + WRT_REG_DWORD((void __iomem *)&pkt->signature, + RESPONSE_PROCESSED); + pkt++; + } +} + +int +qlafx00_rescan_isp(scsi_qla_host_t *vha) +{ + uint32_t status = QLA_FUNCTION_FAILED; + struct qla_hw_data *ha = vha->hw; + struct device_reg_fx00 __iomem *reg = &ha->iobase->ispfx00; + uint32_t aenmbx7; + + qla2x00_request_irqs(ha, ha->rsp_q_map[0]); + + aenmbx7 = RD_REG_DWORD(®->aenmailbox7); + ha->mbx_intr_code = MSW(aenmbx7); + ha->rqstq_intr_code = LSW(aenmbx7); + ha->req_que_off = RD_REG_DWORD(®->aenmailbox1); + ha->rsp_que_off = RD_REG_DWORD(®->aenmailbox3); + ha->req_que_len = RD_REG_DWORD(®->aenmailbox5); + ha->rsp_que_len = RD_REG_DWORD(®->aenmailbox6); + + ql_dbg(ql_dbg_disc, vha, 0x2094, + "fw returned mbx_intr_code: 0x%x, rqstq_intr_code: 0x%x " + " Req que offset 0x%x Rsp que offset 0x%x\n", + ha->mbx_intr_code, ha->rqstq_intr_code, + ha->req_que_off, ha->rsp_que_len); + + /* Clear the Interrupts */ + QLAFX00_CLR_INTR_REG(ha, QLAFX00_HST_INT_STS_BITS); + + status = qla2x00_init_rings(vha); + if (!status) { + vha->flags.online = 1; + + /* if no cable then assume it's good */ + if ((vha->device_flags & DFLG_NO_CABLE)) + status = 0; + /* Register system information */ + if (qlafx00_fx_disc(vha, + &vha->hw->mr.fcport, FXDISC_REG_HOST_INFO)) + ql_dbg(ql_dbg_disc, vha, 0x2095, + "failed to register host info\n"); + } + scsi_unblock_requests(vha->host); + return status; +} + +void +qlafx00_timer_routine(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t fw_heart_beat; + uint32_t aenmbx0; + struct device_reg_fx00 __iomem *reg = &ha->iobase->ispfx00; + uint32_t tempc; + + /* Check firmware health */ + if (ha->mr.fw_hbt_cnt) + ha->mr.fw_hbt_cnt--; + else { + if ((!ha->flags.mr_reset_hdlr_active) && + (!test_bit(UNLOADING, &vha->dpc_flags)) && + (!test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) && + (ha->mr.fw_hbt_en)) { + fw_heart_beat = RD_REG_DWORD(®->fwheartbeat); + if (fw_heart_beat != ha->mr.old_fw_hbt_cnt) { + ha->mr.old_fw_hbt_cnt = fw_heart_beat; + ha->mr.fw_hbt_miss_cnt = 0; + } else { + ha->mr.fw_hbt_miss_cnt++; + if (ha->mr.fw_hbt_miss_cnt == + QLAFX00_HEARTBEAT_MISS_CNT) { + set_bit(ISP_ABORT_NEEDED, + &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + ha->mr.fw_hbt_miss_cnt = 0; + } + } + } + ha->mr.fw_hbt_cnt = QLAFX00_HEARTBEAT_INTERVAL; + } + + if (test_bit(FX00_RESET_RECOVERY, &vha->dpc_flags)) { + /* Reset recovery to be performed in timer routine */ + aenmbx0 = RD_REG_DWORD(®->aenmailbox0); + if (ha->mr.fw_reset_timer_exp) { + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + ha->mr.fw_reset_timer_exp = 0; + } else if (aenmbx0 == MBA_FW_RESTART_CMPLT) { + /* Wake up DPC to rescan the targets */ + set_bit(FX00_TARGET_SCAN, &vha->dpc_flags); + clear_bit(FX00_RESET_RECOVERY, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + ha->mr.fw_reset_timer_tick = QLAFX00_RESET_INTERVAL; + } else if ((aenmbx0 == MBA_FW_STARTING) && + (!ha->mr.fw_hbt_en)) { + ha->mr.fw_hbt_en = 1; + } else if (!ha->mr.fw_reset_timer_tick) { + if (aenmbx0 == ha->mr.old_aenmbx0_state) + ha->mr.fw_reset_timer_exp = 1; + ha->mr.fw_reset_timer_tick = QLAFX00_RESET_INTERVAL; + } else if (aenmbx0 == 0xFFFFFFFF) { + uint32_t data0, data1; + + data0 = QLAFX00_RD_REG(ha, + QLAFX00_BAR1_BASE_ADDR_REG); + data1 = QLAFX00_RD_REG(ha, + QLAFX00_PEX0_WIN0_BASE_ADDR_REG); + + data0 &= 0xffff0000; + data1 &= 0x0000ffff; + + QLAFX00_WR_REG(ha, + QLAFX00_PEX0_WIN0_BASE_ADDR_REG, + (data0 | data1)); + } else if ((aenmbx0 & 0xFF00) == MBA_FW_POLL_STATE) { + ha->mr.fw_reset_timer_tick = + QLAFX00_MAX_RESET_INTERVAL; + } else if (aenmbx0 == MBA_FW_RESET_FCT) { + ha->mr.fw_reset_timer_tick = + QLAFX00_MAX_RESET_INTERVAL; + } + ha->mr.old_aenmbx0_state = aenmbx0; + ha->mr.fw_reset_timer_tick--; + } + if (test_bit(FX00_CRITEMP_RECOVERY, &vha->dpc_flags)) { + /* + * Critical temperature recovery to be + * performed in timer routine + */ + if (ha->mr.fw_critemp_timer_tick == 0) { + tempc = QLAFX00_GET_TEMPERATURE(ha); + ql_dbg(ql_dbg_timer, vha, 0x6012, + "ISPFx00(%s): Critical temp timer, " + "current SOC temperature: %d\n", + __func__, tempc); + if (tempc < ha->mr.critical_temperature) { + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + clear_bit(FX00_CRITEMP_RECOVERY, + &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + } + ha->mr.fw_critemp_timer_tick = + QLAFX00_CRITEMP_INTERVAL; + } else { + ha->mr.fw_critemp_timer_tick--; + } + } + if (ha->mr.host_info_resend) { + /* + * Incomplete host info might be sent to firmware + * durinng system boot - info should be resend + */ + if (ha->mr.hinfo_resend_timer_tick == 0) { + ha->mr.host_info_resend = false; + set_bit(FX00_HOST_INFO_RESEND, &vha->dpc_flags); + ha->mr.hinfo_resend_timer_tick = + QLAFX00_HINFO_RESEND_INTERVAL; + qla2xxx_wake_dpc(vha); + } else { + ha->mr.hinfo_resend_timer_tick--; + } + } + +} + +/* + * qlfx00a_reset_initialize + * Re-initialize after a iSA device reset. + * + * Input: + * ha = adapter block pointer. + * + * Returns: + * 0 = success + */ +int +qlafx00_reset_initialize(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + + if (vha->device_flags & DFLG_DEV_FAILED) { + ql_dbg(ql_dbg_init, vha, 0x0142, + "Device in failed state\n"); + return QLA_SUCCESS; + } + + ha->flags.mr_reset_hdlr_active = 1; + + if (vha->flags.online) { + scsi_block_requests(vha->host); + qlafx00_abort_isp_cleanup(vha, false); + } + + ql_log(ql_log_info, vha, 0x0143, + "(%s): succeeded.\n", __func__); + ha->flags.mr_reset_hdlr_active = 0; + return QLA_SUCCESS; +} + +/* + * qlafx00_abort_isp + * Resets ISP and aborts all outstanding commands. + * + * Input: + * ha = adapter block pointer. + * + * Returns: + * 0 = success + */ +int +qlafx00_abort_isp(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + + if (vha->flags.online) { + if (unlikely(pci_channel_offline(ha->pdev) && + ha->flags.pci_channel_io_perm_failure)) { + clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags); + return QLA_SUCCESS; + } + + scsi_block_requests(vha->host); + qlafx00_abort_isp_cleanup(vha, false); + } else { + scsi_block_requests(vha->host); + clear_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + vha->qla_stats.total_isp_aborts++; + ha->isp_ops->reset_chip(vha); + set_bit(FX00_RESET_RECOVERY, &vha->dpc_flags); + /* Clear the Interrupts */ + QLAFX00_CLR_INTR_REG(ha, QLAFX00_HST_INT_STS_BITS); + } + + ql_log(ql_log_info, vha, 0x0145, + "(%s): succeeded.\n", __func__); + + return QLA_SUCCESS; +} + +static inline fc_port_t* +qlafx00_get_fcport(struct scsi_qla_host *vha, int tgt_id) +{ + fc_port_t *fcport; + + /* Check for matching device in remote port list. */ + fcport = NULL; + list_for_each_entry(fcport, &vha->vp_fcports, list) { + if (fcport->tgt_id == tgt_id) { + ql_dbg(ql_dbg_async, vha, 0x5072, + "Matching fcport(%p) found with TGT-ID: 0x%x " + "and Remote TGT_ID: 0x%x\n", + fcport, fcport->tgt_id, tgt_id); + break; + } + } + return fcport; +} + +static void +qlafx00_tgt_detach(struct scsi_qla_host *vha, int tgt_id) +{ + fc_port_t *fcport; + + ql_log(ql_log_info, vha, 0x5073, + "Detach TGT-ID: 0x%x\n", tgt_id); + + fcport = qlafx00_get_fcport(vha, tgt_id); + if (!fcport) + return; + + qla2x00_mark_device_lost(vha, fcport, 0, 0); + + return; +} + +int +qlafx00_process_aen(struct scsi_qla_host *vha, struct qla_work_evt *evt) +{ + int rval = 0; + uint32_t aen_code, aen_data; + + aen_code = FCH_EVT_VENDOR_UNIQUE; + aen_data = evt->u.aenfx.evtcode; + + switch (evt->u.aenfx.evtcode) { + case QLAFX00_MBA_PORT_UPDATE: /* Port database update */ + if (evt->u.aenfx.mbx[1] == 0) { + if (evt->u.aenfx.mbx[2] == 1) { + if (!vha->flags.fw_tgt_reported) + vha->flags.fw_tgt_reported = 1; + atomic_set(&vha->loop_down_timer, 0); + atomic_set(&vha->loop_state, LOOP_UP); + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + } else if (evt->u.aenfx.mbx[2] == 2) { + qlafx00_tgt_detach(vha, evt->u.aenfx.mbx[3]); + } + } else if (evt->u.aenfx.mbx[1] == 0xffff) { + if (evt->u.aenfx.mbx[2] == 1) { + if (!vha->flags.fw_tgt_reported) + vha->flags.fw_tgt_reported = 1; + set_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); + } else if (evt->u.aenfx.mbx[2] == 2) { + vha->device_flags |= DFLG_NO_CABLE; + qla2x00_mark_all_devices_lost(vha, 1); + } + } + break; + case QLAFX00_MBA_LINK_UP: + aen_code = FCH_EVT_LINKUP; + aen_data = 0; + break; + case QLAFX00_MBA_LINK_DOWN: + aen_code = FCH_EVT_LINKDOWN; + aen_data = 0; + break; + case QLAFX00_MBA_TEMP_CRIT: /* Critical temperature event */ + ql_log(ql_log_info, vha, 0x5082, + "Process critical temperature event " + "aenmb[0]: %x\n", + evt->u.aenfx.evtcode); + scsi_block_requests(vha->host); + qlafx00_abort_isp_cleanup(vha, true); + scsi_unblock_requests(vha->host); + break; + } + + fc_host_post_event(vha->host, fc_get_event_number(), + aen_code, aen_data); + + return rval; +} + +static void +qlafx00_update_host_attr(scsi_qla_host_t *vha, struct port_info_data *pinfo) +{ + u64 port_name = 0, node_name = 0; + + port_name = (unsigned long long)wwn_to_u64(pinfo->port_name); + node_name = (unsigned long long)wwn_to_u64(pinfo->node_name); + + fc_host_node_name(vha->host) = node_name; + fc_host_port_name(vha->host) = port_name; + if (!pinfo->port_type) + vha->hw->current_topology = ISP_CFG_F; + if (pinfo->link_status == QLAFX00_LINK_STATUS_UP) + atomic_set(&vha->loop_state, LOOP_READY); + else if (pinfo->link_status == QLAFX00_LINK_STATUS_DOWN) + atomic_set(&vha->loop_state, LOOP_DOWN); + vha->hw->link_data_rate = (uint16_t)pinfo->link_config; +} + +static void +qla2x00_fxdisc_iocb_timeout(void *data) +{ + srb_t *sp = (srb_t *)data; + struct srb_iocb *lio = &sp->u.iocb_cmd; + + complete(&lio->u.fxiocb.fxiocb_comp); +} + +static void +qla2x00_fxdisc_sp_done(void *data, void *ptr, int res) +{ + srb_t *sp = (srb_t *)ptr; + struct srb_iocb *lio = &sp->u.iocb_cmd; + + complete(&lio->u.fxiocb.fxiocb_comp); +} + +int +qlafx00_fx_disc(scsi_qla_host_t *vha, fc_port_t *fcport, uint16_t fx_type) +{ + srb_t *sp; + struct srb_iocb *fdisc; + int rval = QLA_FUNCTION_FAILED; + struct qla_hw_data *ha = vha->hw; + struct host_system_info *phost_info; + struct register_host_info *preg_hsi; + struct new_utsname *p_sysid = NULL; + struct timeval tv; + + sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); + if (!sp) + goto done; + + fdisc = &sp->u.iocb_cmd; + switch (fx_type) { + case FXDISC_GET_CONFIG_INFO: + fdisc->u.fxiocb.flags = + SRB_FXDISC_RESP_DMA_VALID; + fdisc->u.fxiocb.rsp_len = sizeof(struct config_info_data); + break; + case FXDISC_GET_PORT_INFO: + fdisc->u.fxiocb.flags = + SRB_FXDISC_RESP_DMA_VALID | SRB_FXDISC_REQ_DWRD_VALID; + fdisc->u.fxiocb.rsp_len = QLAFX00_PORT_DATA_INFO; + fdisc->u.fxiocb.req_data = cpu_to_le32(fcport->port_id); + break; + case FXDISC_GET_TGT_NODE_INFO: + fdisc->u.fxiocb.flags = + SRB_FXDISC_RESP_DMA_VALID | SRB_FXDISC_REQ_DWRD_VALID; + fdisc->u.fxiocb.rsp_len = QLAFX00_TGT_NODE_INFO; + fdisc->u.fxiocb.req_data = cpu_to_le32(fcport->tgt_id); + break; + case FXDISC_GET_TGT_NODE_LIST: + fdisc->u.fxiocb.flags = + SRB_FXDISC_RESP_DMA_VALID | SRB_FXDISC_REQ_DWRD_VALID; + fdisc->u.fxiocb.rsp_len = QLAFX00_TGT_NODE_LIST_SIZE; + break; + case FXDISC_REG_HOST_INFO: + fdisc->u.fxiocb.flags = SRB_FXDISC_REQ_DMA_VALID; + fdisc->u.fxiocb.req_len = sizeof(struct register_host_info); + p_sysid = utsname(); + if (!p_sysid) { + ql_log(ql_log_warn, vha, 0x303c, + "Not able to get the system information\n"); + goto done_free_sp; + } + break; + case FXDISC_ABORT_IOCTL: + default: + break; + } + + if (fdisc->u.fxiocb.flags & SRB_FXDISC_REQ_DMA_VALID) { + fdisc->u.fxiocb.req_addr = dma_alloc_coherent(&ha->pdev->dev, + fdisc->u.fxiocb.req_len, + &fdisc->u.fxiocb.req_dma_handle, GFP_KERNEL); + if (!fdisc->u.fxiocb.req_addr) + goto done_free_sp; + + if (fx_type == FXDISC_REG_HOST_INFO) { + preg_hsi = (struct register_host_info *) + fdisc->u.fxiocb.req_addr; + phost_info = &preg_hsi->hsi; + memset(preg_hsi, 0, sizeof(struct register_host_info)); + phost_info->os_type = OS_TYPE_LINUX; + strncpy(phost_info->sysname, + p_sysid->sysname, SYSNAME_LENGTH); + strncpy(phost_info->nodename, + p_sysid->nodename, NODENAME_LENGTH); + if (!strcmp(phost_info->nodename, "(none)")) + ha->mr.host_info_resend = true; + strncpy(phost_info->release, + p_sysid->release, RELEASE_LENGTH); + strncpy(phost_info->version, + p_sysid->version, VERSION_LENGTH); + strncpy(phost_info->machine, + p_sysid->machine, MACHINE_LENGTH); + strncpy(phost_info->domainname, + p_sysid->domainname, DOMNAME_LENGTH); + strncpy(phost_info->hostdriver, + QLA2XXX_VERSION, VERSION_LENGTH); + do_gettimeofday(&tv); + preg_hsi->utc = (uint64_t)tv.tv_sec; + ql_dbg(ql_dbg_init, vha, 0x0149, + "ISP%04X: Host registration with firmware\n", + ha->pdev->device); + ql_dbg(ql_dbg_init, vha, 0x014a, + "os_type = '%d', sysname = '%s', nodname = '%s'\n", + phost_info->os_type, + phost_info->sysname, + phost_info->nodename); + ql_dbg(ql_dbg_init, vha, 0x014b, + "release = '%s', version = '%s'\n", + phost_info->release, + phost_info->version); + ql_dbg(ql_dbg_init, vha, 0x014c, + "machine = '%s' " + "domainname = '%s', hostdriver = '%s'\n", + phost_info->machine, + phost_info->domainname, + phost_info->hostdriver); + ql_dump_buffer(ql_dbg_init + ql_dbg_disc, vha, 0x014d, + (uint8_t *)phost_info, + sizeof(struct host_system_info)); + } + } + + if (fdisc->u.fxiocb.flags & SRB_FXDISC_RESP_DMA_VALID) { + fdisc->u.fxiocb.rsp_addr = dma_alloc_coherent(&ha->pdev->dev, + fdisc->u.fxiocb.rsp_len, + &fdisc->u.fxiocb.rsp_dma_handle, GFP_KERNEL); + if (!fdisc->u.fxiocb.rsp_addr) + goto done_unmap_req; + } + + sp->type = SRB_FXIOCB_DCMD; + sp->name = "fxdisc"; + qla2x00_init_timer(sp, FXDISC_TIMEOUT); + fdisc->timeout = qla2x00_fxdisc_iocb_timeout; + fdisc->u.fxiocb.req_func_type = cpu_to_le16(fx_type); + sp->done = qla2x00_fxdisc_sp_done; + + rval = qla2x00_start_sp(sp); + if (rval != QLA_SUCCESS) + goto done_unmap_dma; + + wait_for_completion(&fdisc->u.fxiocb.fxiocb_comp); + + if (fx_type == FXDISC_GET_CONFIG_INFO) { + struct config_info_data *pinfo = + (struct config_info_data *) fdisc->u.fxiocb.rsp_addr; + strcpy(vha->hw->model_number, pinfo->model_num); + strcpy(vha->hw->model_desc, pinfo->model_description); + memcpy(&vha->hw->mr.symbolic_name, pinfo->symbolic_name, + sizeof(vha->hw->mr.symbolic_name)); + memcpy(&vha->hw->mr.serial_num, pinfo->serial_num, + sizeof(vha->hw->mr.serial_num)); + memcpy(&vha->hw->mr.hw_version, pinfo->hw_version, + sizeof(vha->hw->mr.hw_version)); + memcpy(&vha->hw->mr.fw_version, pinfo->fw_version, + sizeof(vha->hw->mr.fw_version)); + strim(vha->hw->mr.fw_version); + memcpy(&vha->hw->mr.uboot_version, pinfo->uboot_version, + sizeof(vha->hw->mr.uboot_version)); + memcpy(&vha->hw->mr.fru_serial_num, pinfo->fru_serial_num, + sizeof(vha->hw->mr.fru_serial_num)); + vha->hw->mr.critical_temperature = + (pinfo->nominal_temp_value) ? + pinfo->nominal_temp_value : QLAFX00_CRITEMP_THRSHLD; + ha->mr.extended_io_enabled = (pinfo->enabled_capabilities & + QLAFX00_EXTENDED_IO_EN_MASK) != 0; + } else if (fx_type == FXDISC_GET_PORT_INFO) { + struct port_info_data *pinfo = + (struct port_info_data *) fdisc->u.fxiocb.rsp_addr; + memcpy(vha->node_name, pinfo->node_name, WWN_SIZE); + memcpy(vha->port_name, pinfo->port_name, WWN_SIZE); + vha->d_id.b.domain = pinfo->port_id[0]; + vha->d_id.b.area = pinfo->port_id[1]; + vha->d_id.b.al_pa = pinfo->port_id[2]; + qlafx00_update_host_attr(vha, pinfo); + ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0141, + (uint8_t *)pinfo, 16); + } else if (fx_type == FXDISC_GET_TGT_NODE_INFO) { + struct qlafx00_tgt_node_info *pinfo = + (struct qlafx00_tgt_node_info *) fdisc->u.fxiocb.rsp_addr; + memcpy(fcport->node_name, pinfo->tgt_node_wwnn, WWN_SIZE); + memcpy(fcport->port_name, pinfo->tgt_node_wwpn, WWN_SIZE); + fcport->port_type = FCT_TARGET; + ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0144, + (uint8_t *)pinfo, 16); + } else if (fx_type == FXDISC_GET_TGT_NODE_LIST) { + struct qlafx00_tgt_node_info *pinfo = + (struct qlafx00_tgt_node_info *) fdisc->u.fxiocb.rsp_addr; + ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x0146, + (uint8_t *)pinfo, 16); + memcpy(vha->hw->gid_list, pinfo, QLAFX00_TGT_NODE_LIST_SIZE); + } else if (fx_type == FXDISC_ABORT_IOCTL) + fdisc->u.fxiocb.result = + (fdisc->u.fxiocb.result == + cpu_to_le32(QLAFX00_IOCTL_ICOB_ABORT_SUCCESS)) ? + cpu_to_le32(QLA_SUCCESS) : cpu_to_le32(QLA_FUNCTION_FAILED); + + rval = le32_to_cpu(fdisc->u.fxiocb.result); + +done_unmap_dma: + if (fdisc->u.fxiocb.rsp_addr) + dma_free_coherent(&ha->pdev->dev, fdisc->u.fxiocb.rsp_len, + fdisc->u.fxiocb.rsp_addr, fdisc->u.fxiocb.rsp_dma_handle); + +done_unmap_req: + if (fdisc->u.fxiocb.req_addr) + dma_free_coherent(&ha->pdev->dev, fdisc->u.fxiocb.req_len, + fdisc->u.fxiocb.req_addr, fdisc->u.fxiocb.req_dma_handle); +done_free_sp: + sp->free(vha, sp); +done: + return rval; +} + +/* + * qlafx00_initialize_adapter + * Initialize board. + * + * Input: + * ha = adapter block pointer. + * + * Returns: + * 0 = success + */ +int +qlafx00_initialize_adapter(scsi_qla_host_t *vha) +{ + int rval; + struct qla_hw_data *ha = vha->hw; + uint32_t tempc; + + /* 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; + 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; + 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, 0x0147, + "Configuring PCI space...\n"); + + rval = ha->isp_ops->pci_config(vha); + if (rval) { + ql_log(ql_log_warn, vha, 0x0148, + "Unable to configure PCI space.\n"); + return rval; + } + + rval = qlafx00_init_fw_ready(vha); + if (rval != QLA_SUCCESS) + return rval; + + qlafx00_save_queue_ptrs(vha); + + rval = qlafx00_config_queues(vha); + if (rval != QLA_SUCCESS) + return rval; + + /* + * Allocate the array of outstanding commands + * now that we know the firmware resources. + */ + rval = qla2x00_alloc_outstanding_cmds(ha, vha->req); + if (rval != QLA_SUCCESS) + return rval; + + rval = qla2x00_init_rings(vha); + ha->flags.chip_reset_done = 1; + + tempc = QLAFX00_GET_TEMPERATURE(ha); + ql_dbg(ql_dbg_init, vha, 0x0152, + "ISPFx00(%s): Critical temp timer, current SOC temperature: 0x%x\n", + __func__, tempc); + + return rval; +} + +uint32_t +qlafx00_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; + uint32_t state[1]; + + if (qla2x00_reset_active(vha)) + ql_log(ql_log_warn, vha, 0x70ce, + "ISP reset active.\n"); + else if (!vha->hw->flags.eeh_busy) { + rval = qlafx00_get_firmware_state(vha, state); + } + if (rval != QLA_SUCCESS) + memset(state, -1, sizeof(state)); + + return state[0]; +} + +void +qlafx00_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 QLAFX00_PORT_SPEED_2G: + speed = FC_PORTSPEED_2GBIT; + break; + case QLAFX00_PORT_SPEED_4G: + speed = FC_PORTSPEED_4GBIT; + break; + case QLAFX00_PORT_SPEED_8G: + speed = FC_PORTSPEED_8GBIT; + break; + case QLAFX00_PORT_SPEED_10G: + speed = FC_PORTSPEED_10GBIT; + break; + } + fc_host_speed(shost) = speed; +} + +/** QLAFX00 specific ISR implementation functions */ + +static inline void +qlafx00_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; + + SET_FW_SENSE_LEN(sp, 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_FW_SENSE_LEN(sp, GET_FW_SENSE_LEN(sp) - 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); + + ql_dbg(ql_dbg_io, vha, 0x304d, + "sense_len=0x%x par_sense_len=0x%x track_sense_len=0x%x.\n", + sense_len, par_sense_len, track_sense_len); + if (GET_FW_SENSE_LEN(sp) > 0) { + rsp->status_srb = sp; + cp->result = res; + } + + if (sense_len) { + ql_dbg(ql_dbg_io + ql_dbg_buffer, vha, 0x3039, + "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, 0x3049, + cp->sense_buffer, sense_len); + } +} + +static void +qlafx00_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, + struct tsk_mgmt_entry_fx00 *pkt, srb_t *sp, + __le16 sstatus, __le16 cpstatus) +{ + struct srb_iocb *tmf; + + tmf = &sp->u.iocb_cmd; + if (cpstatus != cpu_to_le16((uint16_t)CS_COMPLETE) || + (sstatus & cpu_to_le16((uint16_t)SS_RESPONSE_INFO_LEN_VALID))) + cpstatus = cpu_to_le16((uint16_t)CS_INCOMPLETE); + tmf->u.tmf.comp_status = cpstatus; + sp->done(vha, sp, 0); +} + +static void +qlafx00_abort_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, + struct abort_iocb_entry_fx00 *pkt) +{ + const char func[] = "ABT_IOCB"; + srb_t *sp; + struct srb_iocb *abt; + + sp = qla2x00_get_sp_from_handle(vha, func, req, pkt); + if (!sp) + return; + + abt = &sp->u.iocb_cmd; + abt->u.abt.comp_status = pkt->tgt_id_sts; + sp->done(vha, sp, 0); +} + +static void +qlafx00_ioctl_iosb_entry(scsi_qla_host_t *vha, struct req_que *req, + struct ioctl_iocb_entry_fx00 *pkt) +{ + const char func[] = "IOSB_IOCB"; + srb_t *sp; + struct fc_bsg_job *bsg_job; + struct srb_iocb *iocb_job; + int res; + struct qla_mt_iocb_rsp_fx00 fstatus; + uint8_t *fw_sts_ptr; + + sp = qla2x00_get_sp_from_handle(vha, func, req, pkt); + if (!sp) + return; + + if (sp->type == SRB_FXIOCB_DCMD) { + iocb_job = &sp->u.iocb_cmd; + iocb_job->u.fxiocb.seq_number = pkt->seq_no; + iocb_job->u.fxiocb.fw_flags = pkt->fw_iotcl_flags; + iocb_job->u.fxiocb.result = pkt->status; + if (iocb_job->u.fxiocb.flags & SRB_FXDISC_RSP_DWRD_VALID) + iocb_job->u.fxiocb.req_data = + pkt->dataword_r; + } else { + bsg_job = sp->u.bsg_job; + + memset(&fstatus, 0, sizeof(struct qla_mt_iocb_rsp_fx00)); + + fstatus.reserved_1 = pkt->reserved_0; + fstatus.func_type = pkt->comp_func_num; + fstatus.ioctl_flags = pkt->fw_iotcl_flags; + fstatus.ioctl_data = pkt->dataword_r; + fstatus.adapid = pkt->adapid; + fstatus.reserved_2 = pkt->dataword_r_extra; + fstatus.res_count = pkt->residuallen; + fstatus.status = pkt->status; + fstatus.seq_number = pkt->seq_no; + memcpy(fstatus.reserved_3, + pkt->reserved_2, 20 * sizeof(uint8_t)); + + fw_sts_ptr = ((uint8_t *)bsg_job->req->sense) + + sizeof(struct fc_bsg_reply); + + memcpy(fw_sts_ptr, (uint8_t *)&fstatus, + sizeof(struct qla_mt_iocb_rsp_fx00)); + bsg_job->reply_len = sizeof(struct fc_bsg_reply) + + sizeof(struct qla_mt_iocb_rsp_fx00) + sizeof(uint8_t); + + ql_dump_buffer(ql_dbg_user + ql_dbg_verbose, + sp->fcport->vha, 0x5080, + (uint8_t *)pkt, sizeof(struct ioctl_iocb_entry_fx00)); + + ql_dump_buffer(ql_dbg_user + ql_dbg_verbose, + sp->fcport->vha, 0x5074, + (uint8_t *)fw_sts_ptr, sizeof(struct qla_mt_iocb_rsp_fx00)); + + res = bsg_job->reply->result = DID_OK << 16; + bsg_job->reply->reply_payload_rcv_len = + bsg_job->reply_payload.payload_len; + } + sp->done(vha, sp, res); +} + +/** + * qlafx00_status_entry() - Process a Status IOCB entry. + * @ha: SCSI driver HA context + * @pkt: Entry pointer + */ +static void +qlafx00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) +{ + srb_t *sp; + fc_port_t *fcport; + struct scsi_cmnd *cp; + struct sts_entry_fx00 *sts; + __le16 comp_status; + __le16 scsi_status; + uint16_t ox_id; + __le16 lscsi_status; + int32_t resid; + uint32_t sense_len, par_sense_len, rsp_info_len, resid_len, + fw_resid_len; + uint8_t *rsp_info = NULL, *sense_data = NULL; + struct qla_hw_data *ha = vha->hw; + uint32_t hindex, handle; + uint16_t que; + struct req_que *req; + int logit = 1; + int res = 0; + + sts = (struct sts_entry_fx00 *) pkt; + + comp_status = sts->comp_status; + scsi_status = sts->scsi_status & cpu_to_le16((uint16_t)SS_MASK); + hindex = sts->handle; + handle = LSW(hindex); + + que = MSW(hindex); + req = ha->req_q_map[que]; + + /* Validate handle. */ + if (handle < req->num_outstanding_cmds) + sp = req->outstanding_cmds[handle]; + else + sp = NULL; + + if (sp == NULL) { + ql_dbg(ql_dbg_io, vha, 0x3034, + "Invalid status handle (0x%x).\n", handle); + + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + return; + } + + if (sp->type == SRB_TM_CMD) { + req->outstanding_cmds[handle] = NULL; + qlafx00_tm_iocb_entry(vha, req, pkt, sp, + scsi_status, comp_status); + return; + } + + /* Fast path completion. */ + if (comp_status == CS_COMPLETE && scsi_status == 0) { + qla2x00_process_completed_request(vha, req, handle); + return; + } + + req->outstanding_cmds[handle] = NULL; + cp = GET_CMD_SP(sp); + if (cp == NULL) { + ql_dbg(ql_dbg_io, vha, 0x3048, + "Command already returned (0x%x/%p).\n", + handle, sp); + + return; + } + + lscsi_status = scsi_status & cpu_to_le16((uint16_t)STATUS_MASK); + + fcport = sp->fcport; + + ox_id = 0; + sense_len = par_sense_len = rsp_info_len = resid_len = + fw_resid_len = 0; + if (scsi_status & cpu_to_le16((uint16_t)SS_SENSE_LEN_VALID)) + sense_len = sts->sense_len; + if (scsi_status & cpu_to_le16(((uint16_t)SS_RESIDUAL_UNDER + | (uint16_t)SS_RESIDUAL_OVER))) + resid_len = le32_to_cpu(sts->residual_len); + if (comp_status == cpu_to_le16((uint16_t)CS_DATA_UNDERRUN)) + fw_resid_len = le32_to_cpu(sts->residual_len); + rsp_info = sense_data = sts->data; + par_sense_len = sizeof(sts->data); + + /* Check for overrun. */ + if (comp_status == CS_COMPLETE && + scsi_status & cpu_to_le16((uint16_t)SS_RESIDUAL_OVER)) + comp_status = cpu_to_le16((uint16_t)CS_DATA_OVERRUN); + + /* + * Based on Host and scsi status generate status code for Linux + */ + switch (le16_to_cpu(comp_status)) { + case CS_COMPLETE: + case CS_QUEUE_FULL: + if (scsi_status == 0) { + res = DID_OK << 16; + break; + } + if (scsi_status & cpu_to_le16(((uint16_t)SS_RESIDUAL_UNDER + | (uint16_t)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, 0x3050, + "Mid-layer underflow " + "detected (0x%x of 0x%x bytes).\n", + resid, scsi_bufflen(cp)); + + res = DID_ERROR << 16; + break; + } + } + res = DID_OK << 16 | le16_to_cpu(lscsi_status); + + if (lscsi_status == + cpu_to_le16((uint16_t)SAM_STAT_TASK_SET_FULL)) { + ql_dbg(ql_dbg_io, fcport->vha, 0x3051, + "QUEUE FULL detected.\n"); + break; + } + logit = 0; + if (lscsi_status != cpu_to_le16((uint16_t)SS_CHECK_CONDITION)) + break; + + memset(cp->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); + if (!(scsi_status & cpu_to_le16((uint16_t)SS_SENSE_LEN_VALID))) + break; + + qlafx00_handle_sense(sp, sense_data, par_sense_len, sense_len, + rsp, res); + break; + + case CS_DATA_UNDERRUN: + /* Use F/W calculated residual length. */ + if (IS_FWI2_CAPABLE(ha) || IS_QLAFX00(ha)) + resid = fw_resid_len; + else + resid = resid_len; + scsi_set_resid(cp, resid); + if (scsi_status & cpu_to_le16((uint16_t)SS_RESIDUAL_UNDER)) { + if ((IS_FWI2_CAPABLE(ha) || IS_QLAFX00(ha)) + && fw_resid_len != resid_len) { + ql_dbg(ql_dbg_io, fcport->vha, 0x3052, + "Dropped frame(s) detected " + "(0x%x of 0x%x bytes).\n", + resid, scsi_bufflen(cp)); + + res = DID_ERROR << 16 | + le16_to_cpu(lscsi_status); + goto check_scsi_status; + } + + if (!lscsi_status && + ((unsigned)(scsi_bufflen(cp) - resid) < + cp->underflow)) { + ql_dbg(ql_dbg_io, fcport->vha, 0x3053, + "Mid-layer underflow " + "detected (0x%x of 0x%x bytes, " + "cp->underflow: 0x%x).\n", + resid, scsi_bufflen(cp), cp->underflow); + + res = DID_ERROR << 16; + break; + } + } else if (lscsi_status != + cpu_to_le16((uint16_t)SAM_STAT_TASK_SET_FULL) && + lscsi_status != cpu_to_le16((uint16_t)SAM_STAT_BUSY)) { + /* + * scsi status of task set and busy are considered + * to be task not completed. + */ + + ql_dbg(ql_dbg_io, fcport->vha, 0x3054, + "Dropped frame(s) detected (0x%x " + "of 0x%x bytes).\n", resid, + scsi_bufflen(cp)); + + res = DID_ERROR << 16 | le16_to_cpu(lscsi_status); + goto check_scsi_status; + } else { + ql_dbg(ql_dbg_io, fcport->vha, 0x3055, + "scsi_status: 0x%x, lscsi_status: 0x%x\n", + scsi_status, lscsi_status); + } + + res = DID_OK << 16 | le16_to_cpu(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 == + cpu_to_le16((uint16_t)SAM_STAT_TASK_SET_FULL)) { + ql_dbg(ql_dbg_io, fcport->vha, 0x3056, + "QUEUE FULL detected.\n"); + logit = 1; + break; + } + if (lscsi_status != + cpu_to_le16((uint16_t)SS_CHECK_CONDITION)) + break; + + memset(cp->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); + if (!(scsi_status & + cpu_to_le16((uint16_t)SS_SENSE_LEN_VALID))) + break; + + qlafx00_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; + + ql_dbg(ql_dbg_io, fcport->vha, 0x3057, + "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; + + default: + res = DID_ERROR << 16; + break; + } + + if (logit) + ql_dbg(ql_dbg_io, fcport->vha, 0x3058, + "FCP command status: 0x%x-0x%x (0x%x) nexus=%ld:%d:%d " + "tgt_id: 0x%x lscsi_status: 0x%x cdb=%10phN len=0x%x " + "rsp_info=0x%x resid=0x%x fw_resid=0x%x sense_len=0x%x, " + "par_sense_len=0x%x, rsp_info_len=0x%x\n", + comp_status, scsi_status, res, vha->host_no, + cp->device->id, cp->device->lun, fcport->tgt_id, + lscsi_status, cp->cmnd, scsi_bufflen(cp), + rsp_info_len, resid_len, fw_resid_len, sense_len, + par_sense_len, rsp_info_len); + + if (rsp->status_srb == NULL) + sp->done(ha, sp, res); +} + +/** + * qlafx00_status_cont_entry() - Process a Status Continuations entry. + * @ha: SCSI driver HA context + * @pkt: Entry pointer + * + * Extended sense data. + */ +static void +qlafx00_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) { + ql_dbg(ql_dbg_io, vha, 0x3037, + "no SP, sp = %p\n", sp); + return; + } + + if (!GET_FW_SENSE_LEN(sp)) { + ql_dbg(ql_dbg_io, vha, 0x304b, + "no fw sense data, sp = %p\n", sp); + return; + } + cp = GET_CMD_SP(sp); + if (cp == NULL) { + ql_log(ql_log_warn, vha, 0x303b, + "cmd is NULL: already returned to OS (sp=%p).\n", sp); + + rsp->status_srb = NULL; + return; + } + + if (!GET_CMD_SENSE_LEN(sp)) { + ql_dbg(ql_dbg_io, vha, 0x304c, + "no sense data, sp = %p\n", sp); + } else { + sense_len = GET_CMD_SENSE_LEN(sp); + sense_ptr = GET_CMD_SENSE_PTR(sp); + ql_dbg(ql_dbg_io, vha, 0x304f, + "sp=%p sense_len=0x%x sense_ptr=%p.\n", + sp, sense_len, sense_ptr); + + if (sense_len > sizeof(pkt->data)) + sense_sz = sizeof(pkt->data); + else + sense_sz = sense_len; + + /* Move sense data. */ + ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x304e, + (uint8_t *)pkt, sizeof(sts_cont_entry_t)); + memcpy(sense_ptr, pkt->data, sense_sz); + ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x304a, + 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); + } + sense_len = GET_FW_SENSE_LEN(sp); + sense_len = (sense_len > sizeof(pkt->data)) ? + (sense_len - sizeof(pkt->data)) : 0; + SET_FW_SENSE_LEN(sp, sense_len); + + /* Place command on done queue. */ + if (sense_len == 0) { + rsp->status_srb = NULL; + sp->done(ha, sp, cp->result); + } +} + +/** + * qlafx00_multistatus_entry() - Process Multi response queue entries. + * @ha: SCSI driver HA context + */ +static void +qlafx00_multistatus_entry(struct scsi_qla_host *vha, + struct rsp_que *rsp, void *pkt) +{ + srb_t *sp; + struct multi_sts_entry_fx00 *stsmfx; + struct qla_hw_data *ha = vha->hw; + uint32_t handle, hindex, handle_count, i; + uint16_t que; + struct req_que *req; + __le32 *handle_ptr; + + stsmfx = (struct multi_sts_entry_fx00 *) pkt; + + handle_count = stsmfx->handle_count; + + if (handle_count > MAX_HANDLE_COUNT) { + ql_dbg(ql_dbg_io, vha, 0x3035, + "Invalid handle count (0x%x).\n", handle_count); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + return; + } + + handle_ptr = &stsmfx->handles[0]; + + for (i = 0; i < handle_count; i++) { + hindex = le32_to_cpu(*handle_ptr); + handle = LSW(hindex); + que = MSW(hindex); + req = ha->req_q_map[que]; + + /* Validate handle. */ + if (handle < req->num_outstanding_cmds) + sp = req->outstanding_cmds[handle]; + else + sp = NULL; + + if (sp == NULL) { + ql_dbg(ql_dbg_io, vha, 0x3044, + "Invalid status handle (0x%x).\n", handle); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + return; + } + qla2x00_process_completed_request(vha, req, handle); + handle_ptr++; + } +} + +/** + * qlafx00_error_entry() - Process an error entry. + * @ha: SCSI driver HA context + * @pkt: Entry pointer + */ +static void +qlafx00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, + struct sts_entry_fx00 *pkt, uint8_t estatus, uint8_t etype) +{ + srb_t *sp; + struct qla_hw_data *ha = vha->hw; + const char func[] = "ERROR-IOCB"; + uint16_t que = 0; + struct req_que *req = NULL; + int res = DID_ERROR << 16; + + ql_dbg(ql_dbg_async, vha, 0x507f, + "type of error status in response: 0x%x\n", estatus); + + req = ha->req_q_map[que]; + + sp = qla2x00_get_sp_from_handle(vha, func, req, pkt); + if (sp) { + sp->done(ha, sp, res); + return; + } + + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); +} + +/** + * qlafx00_process_response_queue() - Process response queue entries. + * @ha: SCSI driver HA context + */ +static void +qlafx00_process_response_queue(struct scsi_qla_host *vha, + struct rsp_que *rsp) +{ + struct sts_entry_fx00 *pkt; + response_t *lptr; + uint16_t lreq_q_in = 0; + uint16_t lreq_q_out = 0; + + lreq_q_in = RD_REG_DWORD(rsp->rsp_q_in); + lreq_q_out = rsp->ring_index; + + while (lreq_q_in != lreq_q_out) { + lptr = rsp->ring_ptr; + memcpy_fromio(rsp->rsp_pkt, (void __iomem *)lptr, + sizeof(rsp->rsp_pkt)); + pkt = (struct sts_entry_fx00 *)rsp->rsp_pkt; + + rsp->ring_index++; + lreq_q_out++; + if (rsp->ring_index == rsp->length) { + lreq_q_out = 0; + rsp->ring_index = 0; + rsp->ring_ptr = rsp->ring; + } else { + rsp->ring_ptr++; + } + + if (pkt->entry_status != 0 && + pkt->entry_type != IOCTL_IOSB_TYPE_FX00) { + qlafx00_error_entry(vha, rsp, + (struct sts_entry_fx00 *)pkt, pkt->entry_status, + pkt->entry_type); + continue; + } + + switch (pkt->entry_type) { + case STATUS_TYPE_FX00: + qlafx00_status_entry(vha, rsp, pkt); + break; + + case STATUS_CONT_TYPE_FX00: + qlafx00_status_cont_entry(rsp, (sts_cont_entry_t *)pkt); + break; + + case MULTI_STATUS_TYPE_FX00: + qlafx00_multistatus_entry(vha, rsp, pkt); + break; + + case ABORT_IOCB_TYPE_FX00: + qlafx00_abort_iocb_entry(vha, rsp->req, + (struct abort_iocb_entry_fx00 *)pkt); + break; + + case IOCTL_IOSB_TYPE_FX00: + qlafx00_ioctl_iosb_entry(vha, rsp->req, + (struct ioctl_iocb_entry_fx00 *)pkt); + break; + default: + /* Type Not Supported. */ + ql_dbg(ql_dbg_async, vha, 0x5081, + "Received unknown response pkt type %x " + "entry status=%x.\n", + pkt->entry_type, pkt->entry_status); + break; + } + } + + /* Adjust ring index */ + WRT_REG_DWORD(rsp->rsp_q_out, rsp->ring_index); +} + +/** + * qlafx00_async_event() - Process aynchronous events. + * @ha: SCSI driver HA context + */ +static void +qlafx00_async_event(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + struct device_reg_fx00 __iomem *reg; + int data_size = 1; + + reg = &ha->iobase->ispfx00; + /* Setup to process RIO completion. */ + switch (ha->aenmb[0]) { + case QLAFX00_MBA_SYSTEM_ERR: /* System Error */ + ql_log(ql_log_warn, vha, 0x5079, + "ISP System Error - mbx1=%x\n", ha->aenmb[0]); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + break; + + case QLAFX00_MBA_SHUTDOWN_RQSTD: /* Shutdown requested */ + ql_dbg(ql_dbg_async, vha, 0x5076, + "Asynchronous FW shutdown requested.\n"); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + break; + + case QLAFX00_MBA_PORT_UPDATE: /* Port database update */ + ha->aenmb[1] = RD_REG_DWORD(®->aenmailbox1); + ha->aenmb[2] = RD_REG_DWORD(®->aenmailbox2); + ha->aenmb[3] = RD_REG_DWORD(®->aenmailbox3); + ql_dbg(ql_dbg_async, vha, 0x5077, + "Asynchronous port Update received " + "aenmb[0]: %x, aenmb[1]: %x, aenmb[2]: %x, aenmb[3]: %x\n", + ha->aenmb[0], ha->aenmb[1], ha->aenmb[2], ha->aenmb[3]); + data_size = 4; + break; + + case QLAFX00_MBA_TEMP_OVER: /* Over temperature event */ + ql_log(ql_log_info, vha, 0x5085, + "Asynchronous over temperature event received " + "aenmb[0]: %x\n", + ha->aenmb[0]); + break; + + case QLAFX00_MBA_TEMP_NORM: /* Normal temperature event */ + ql_log(ql_log_info, vha, 0x5086, + "Asynchronous normal temperature event received " + "aenmb[0]: %x\n", + ha->aenmb[0]); + break; + + case QLAFX00_MBA_TEMP_CRIT: /* Critical temperature event */ + ql_log(ql_log_info, vha, 0x5083, + "Asynchronous critical temperature event received " + "aenmb[0]: %x\n", + ha->aenmb[0]); + break; + + default: + ha->aenmb[1] = RD_REG_WORD(®->aenmailbox1); + ha->aenmb[2] = RD_REG_WORD(®->aenmailbox2); + ha->aenmb[3] = RD_REG_WORD(®->aenmailbox3); + ha->aenmb[4] = RD_REG_WORD(®->aenmailbox4); + ha->aenmb[5] = RD_REG_WORD(®->aenmailbox5); + ha->aenmb[6] = RD_REG_WORD(®->aenmailbox6); + ha->aenmb[7] = RD_REG_WORD(®->aenmailbox7); + ql_dbg(ql_dbg_async, vha, 0x5078, + "AEN:%04x %04x %04x %04x :%04x %04x %04x %04x\n", + ha->aenmb[0], ha->aenmb[1], ha->aenmb[2], ha->aenmb[3], + ha->aenmb[4], ha->aenmb[5], ha->aenmb[6], ha->aenmb[7]); + break; + } + qlafx00_post_aenfx_work(vha, ha->aenmb[0], + (uint32_t *)ha->aenmb, data_size); +} + +/** + * + * qlafx00x_mbx_completion() - Process mailbox command completions. + * @ha: SCSI driver HA context + * @mb16: Mailbox16 register + */ +static void +qlafx00_mbx_completion(scsi_qla_host_t *vha, uint32_t mb0) +{ + uint16_t cnt; + uint32_t __iomem *wptr; + struct qla_hw_data *ha = vha->hw; + struct device_reg_fx00 __iomem *reg = &ha->iobase->ispfx00; + + if (!ha->mcp32) + ql_dbg(ql_dbg_async, vha, 0x507e, "MBX pointer ERROR.\n"); + + /* Load return mailbox registers. */ + ha->flags.mbox_int = 1; + ha->mailbox_out32[0] = mb0; + wptr = (uint32_t __iomem *)®->mailbox17; + + for (cnt = 1; cnt < ha->mbx_count; cnt++) { + ha->mailbox_out32[cnt] = RD_REG_DWORD(wptr); + wptr++; + } +} + +/** + * qlafx00_intr_handler() - Process interrupts for the ISPFX00. + * @irq: + * @dev_id: SCSI driver HA context + * + * Called by system whenever the host adapter generates an interrupt. + * + * Returns handled flag. + */ +irqreturn_t +qlafx00_intr_handler(int irq, void *dev_id) +{ + scsi_qla_host_t *vha; + struct qla_hw_data *ha; + struct device_reg_fx00 __iomem *reg; + int status; + unsigned long iter; + uint32_t stat; + uint32_t mb[8]; + struct rsp_que *rsp; + unsigned long flags; + uint32_t clr_intr = 0; + uint32_t intr_stat = 0; + + rsp = (struct rsp_que *) dev_id; + if (!rsp) { + ql_log(ql_log_info, NULL, 0x507d, + "%s: NULL response queue pointer.\n", __func__); + return IRQ_NONE; + } + + ha = rsp->hw; + reg = &ha->iobase->ispfx00; + 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--; clr_intr = 0) { + stat = QLAFX00_RD_INTR_REG(ha); + if (qla2x00_check_reg_for_disconnect(vha, stat)) + break; + intr_stat = stat & QLAFX00_HST_INT_STS_BITS; + if (!intr_stat) + break; + + if (stat & QLAFX00_INTR_MB_CMPLT) { + mb[0] = RD_REG_WORD(®->mailbox16); + qlafx00_mbx_completion(vha, mb[0]); + status |= MBX_INTERRUPT; + clr_intr |= QLAFX00_INTR_MB_CMPLT; + } + if (intr_stat & QLAFX00_INTR_ASYNC_CMPLT) { + ha->aenmb[0] = RD_REG_WORD(®->aenmailbox0); + qlafx00_async_event(vha); + clr_intr |= QLAFX00_INTR_ASYNC_CMPLT; + } + if (intr_stat & QLAFX00_INTR_RSP_CMPLT) { + qlafx00_process_response_queue(vha, rsp); + clr_intr |= QLAFX00_INTR_RSP_CMPLT; + } + + QLAFX00_CLR_INTR_REG(ha, clr_intr); + QLAFX00_RD_INTR_REG(ha); + } + + qla2x00_handle_mbx_completion(ha, status); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return IRQ_HANDLED; +} + +/** QLAFX00 specific IOCB implementation functions */ + +static inline cont_a64_entry_t * +qlafx00_prep_cont_type1_iocb(struct req_que *req, + cont_a64_entry_t *lcont_pkt) +{ + 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. */ + lcont_pkt->entry_type = CONTINUE_A64_TYPE_FX00; + + return cont_pkt; +} + +static inline void +qlafx00_build_scsi_iocbs(srb_t *sp, struct cmd_type_7_fx00 *cmd_pkt, + uint16_t tot_dsds, struct cmd_type_7_fx00 *lcmd_pkt) +{ + uint16_t avail_dsds; + __le32 *cur_dsd; + scsi_qla_host_t *vha; + struct scsi_cmnd *cmd; + struct scatterlist *sg; + int i, cont; + struct req_que *req; + cont_a64_entry_t lcont_pkt; + cont_a64_entry_t *cont_pkt; + + vha = sp->fcport->vha; + req = vha->req; + + cmd = GET_CMD_SP(sp); + cont = 0; + cont_pkt = NULL; + + /* Update entry type to indicate Command Type 3 IOCB */ + lcmd_pkt->entry_type = FX00_COMMAND_TYPE_7; + + /* No data transfer */ + if (!scsi_bufflen(cmd) || cmd->sc_data_direction == DMA_NONE) { + lcmd_pkt->byte_count = __constant_cpu_to_le32(0); + return; + } + + /* Set transfer direction */ + if (cmd->sc_data_direction == DMA_TO_DEVICE) { + lcmd_pkt->cntrl_flags = TMF_WRITE_DATA; + vha->qla_stats.output_bytes += scsi_bufflen(cmd); + } else if (cmd->sc_data_direction == DMA_FROM_DEVICE) { + lcmd_pkt->cntrl_flags = TMF_READ_DATA; + vha->qla_stats.input_bytes += scsi_bufflen(cmd); + } + + /* One DSD is available in the Command Type 3 IOCB */ + avail_dsds = 1; + cur_dsd = (__le32 *)&lcmd_pkt->dseg_0_address; + + /* Load data segments */ + scsi_for_each_sg(cmd, sg, tot_dsds, i) { + dma_addr_t sle_dma; + + /* Allocate additional continuation packets? */ + if (avail_dsds == 0) { + /* + * Five DSDs are available in the Continuation + * Type 1 IOCB. + */ + memset(&lcont_pkt, 0, REQUEST_ENTRY_SIZE); + cont_pkt = + qlafx00_prep_cont_type1_iocb(req, &lcont_pkt); + cur_dsd = (__le32 *)lcont_pkt.dseg_0_address; + avail_dsds = 5; + cont = 1; + } + + 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--; + if (avail_dsds == 0 && cont == 1) { + cont = 0; + memcpy_toio((void __iomem *)cont_pkt, &lcont_pkt, + REQUEST_ENTRY_SIZE); + } + + } + if (avail_dsds != 0 && cont == 1) { + memcpy_toio((void __iomem *)cont_pkt, &lcont_pkt, + REQUEST_ENTRY_SIZE); + } +} + +/** + * qlafx00_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 +qlafx00_start_scsi(srb_t *sp) +{ + int ret, nseg; + unsigned long flags; + uint32_t index; + uint32_t handle; + 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; + struct cmd_type_7_fx00 *cmd_pkt; + struct cmd_type_7_fx00 lcmd_pkt; + struct scsi_lun llun; + char tag[2]; + + /* Setup device pointers. */ + ret = 0; + + rsp = ha->rsp_q_map[0]; + req = vha->req; + + /* So we know we haven't pci_map'ed anything yet */ + tot_dsds = 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 < req->num_outstanding_cmds; index++) { + handle++; + if (handle == req->num_outstanding_cmds) + handle = 1; + if (!req->outstanding_cmds[handle]) + break; + } + if (index == req->num_outstanding_cmds) + 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_fx00 *)req->ring_ptr; + + memset(&lcmd_pkt, 0, REQUEST_ENTRY_SIZE); + + lcmd_pkt.handle = MAKE_HANDLE(req->id, sp->handle); + lcmd_pkt.reserved_0 = 0; + lcmd_pkt.port_path_ctrl = 0; + lcmd_pkt.reserved_1 = 0; + lcmd_pkt.dseg_count = cpu_to_le16(tot_dsds); + lcmd_pkt.tgt_idx = cpu_to_le16(sp->fcport->tgt_id); + + int_to_scsilun(cmd->device->lun, &llun); + host_to_adap((uint8_t *)&llun, (uint8_t *)&lcmd_pkt.lun, + sizeof(lcmd_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: + lcmd_pkt.task = TSK_HEAD_OF_QUEUE; + break; + case ORDERED_QUEUE_TAG: + lcmd_pkt.task = TSK_ORDERED; + break; + } + } + + /* Load SCSI command packet. */ + host_to_adap(cmd->cmnd, lcmd_pkt.fcp_cdb, sizeof(lcmd_pkt.fcp_cdb)); + lcmd_pkt.byte_count = cpu_to_le32((uint32_t)scsi_bufflen(cmd)); + + /* Build IOCB segments */ + qlafx00_build_scsi_iocbs(sp, cmd_pkt, tot_dsds, &lcmd_pkt); + + /* Set total data segment count. */ + lcmd_pkt.entry_count = (uint8_t)req_cnt; + + /* Specify response queue number where completion should happen */ + lcmd_pkt.entry_status = (uint8_t) rsp->id; + + ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x302e, + (uint8_t *)cmd->cmnd, cmd->cmd_len); + ql_dump_buffer(ql_dbg_io + ql_dbg_buffer, vha, 0x3032, + (uint8_t *)&lcmd_pkt, REQUEST_ENTRY_SIZE); + + memcpy_toio((void __iomem *)cmd_pkt, &lcmd_pkt, REQUEST_ENTRY_SIZE); + 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); + QLAFX00_SET_HST_INTR(ha, ha->rqstq_intr_code); + + 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; +} + +void +qlafx00_tm_iocb(srb_t *sp, struct tsk_mgmt_entry_fx00 *ptm_iocb) +{ + struct srb_iocb *fxio = &sp->u.iocb_cmd; + scsi_qla_host_t *vha = sp->fcport->vha; + struct req_que *req = vha->req; + struct tsk_mgmt_entry_fx00 tm_iocb; + struct scsi_lun llun; + + memset(&tm_iocb, 0, sizeof(struct tsk_mgmt_entry_fx00)); + tm_iocb.entry_type = TSK_MGMT_IOCB_TYPE_FX00; + tm_iocb.entry_count = 1; + tm_iocb.handle = cpu_to_le32(MAKE_HANDLE(req->id, sp->handle)); + tm_iocb.reserved_0 = 0; + tm_iocb.tgt_id = cpu_to_le16(sp->fcport->tgt_id); + tm_iocb.control_flags = cpu_to_le32(fxio->u.tmf.flags); + if (tm_iocb.control_flags == cpu_to_le32((uint32_t)TCF_LUN_RESET)) { + int_to_scsilun(fxio->u.tmf.lun, &llun); + host_to_adap((uint8_t *)&llun, (uint8_t *)&tm_iocb.lun, + sizeof(struct scsi_lun)); + } + + memcpy((void *)ptm_iocb, &tm_iocb, + sizeof(struct tsk_mgmt_entry_fx00)); + wmb(); +} + +void +qlafx00_abort_iocb(srb_t *sp, struct abort_iocb_entry_fx00 *pabt_iocb) +{ + struct srb_iocb *fxio = &sp->u.iocb_cmd; + scsi_qla_host_t *vha = sp->fcport->vha; + struct req_que *req = vha->req; + struct abort_iocb_entry_fx00 abt_iocb; + + memset(&abt_iocb, 0, sizeof(struct abort_iocb_entry_fx00)); + abt_iocb.entry_type = ABORT_IOCB_TYPE_FX00; + abt_iocb.entry_count = 1; + abt_iocb.handle = cpu_to_le32(MAKE_HANDLE(req->id, sp->handle)); + abt_iocb.abort_handle = + cpu_to_le32(MAKE_HANDLE(req->id, fxio->u.abt.cmd_hndl)); + abt_iocb.tgt_id_sts = cpu_to_le16(sp->fcport->tgt_id); + abt_iocb.req_que_no = cpu_to_le16(req->id); + + memcpy((void *)pabt_iocb, &abt_iocb, + sizeof(struct abort_iocb_entry_fx00)); + wmb(); +} + +void +qlafx00_fxdisc_iocb(srb_t *sp, struct fxdisc_entry_fx00 *pfxiocb) +{ + struct srb_iocb *fxio = &sp->u.iocb_cmd; + struct qla_mt_iocb_rqst_fx00 *piocb_rqst; + struct fc_bsg_job *bsg_job; + struct fxdisc_entry_fx00 fx_iocb; + uint8_t entry_cnt = 1; + + memset(&fx_iocb, 0, sizeof(struct fxdisc_entry_fx00)); + fx_iocb.entry_type = FX00_IOCB_TYPE; + fx_iocb.handle = cpu_to_le32(sp->handle); + fx_iocb.entry_count = entry_cnt; + + if (sp->type == SRB_FXIOCB_DCMD) { + fx_iocb.func_num = + sp->u.iocb_cmd.u.fxiocb.req_func_type; + fx_iocb.adapid = fxio->u.fxiocb.adapter_id; + fx_iocb.adapid_hi = fxio->u.fxiocb.adapter_id_hi; + fx_iocb.reserved_0 = fxio->u.fxiocb.reserved_0; + fx_iocb.reserved_1 = fxio->u.fxiocb.reserved_1; + fx_iocb.dataword_extra = fxio->u.fxiocb.req_data_extra; + + if (fxio->u.fxiocb.flags & SRB_FXDISC_REQ_DMA_VALID) { + fx_iocb.req_dsdcnt = cpu_to_le16(1); + fx_iocb.req_xfrcnt = + cpu_to_le16(fxio->u.fxiocb.req_len); + fx_iocb.dseg_rq_address[0] = + cpu_to_le32(LSD(fxio->u.fxiocb.req_dma_handle)); + fx_iocb.dseg_rq_address[1] = + cpu_to_le32(MSD(fxio->u.fxiocb.req_dma_handle)); + fx_iocb.dseg_rq_len = + cpu_to_le32(fxio->u.fxiocb.req_len); + } + + if (fxio->u.fxiocb.flags & SRB_FXDISC_RESP_DMA_VALID) { + fx_iocb.rsp_dsdcnt = cpu_to_le16(1); + fx_iocb.rsp_xfrcnt = + cpu_to_le16(fxio->u.fxiocb.rsp_len); + fx_iocb.dseg_rsp_address[0] = + cpu_to_le32(LSD(fxio->u.fxiocb.rsp_dma_handle)); + fx_iocb.dseg_rsp_address[1] = + cpu_to_le32(MSD(fxio->u.fxiocb.rsp_dma_handle)); + fx_iocb.dseg_rsp_len = + cpu_to_le32(fxio->u.fxiocb.rsp_len); + } + + if (fxio->u.fxiocb.flags & SRB_FXDISC_REQ_DWRD_VALID) { + fx_iocb.dataword = fxio->u.fxiocb.req_data; + } + fx_iocb.flags = fxio->u.fxiocb.flags; + } else { + struct scatterlist *sg; + bsg_job = sp->u.bsg_job; + piocb_rqst = (struct qla_mt_iocb_rqst_fx00 *) + &bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; + + fx_iocb.func_num = piocb_rqst->func_type; + fx_iocb.adapid = piocb_rqst->adapid; + fx_iocb.adapid_hi = piocb_rqst->adapid_hi; + fx_iocb.reserved_0 = piocb_rqst->reserved_0; + fx_iocb.reserved_1 = piocb_rqst->reserved_1; + fx_iocb.dataword_extra = piocb_rqst->dataword_extra; + fx_iocb.dataword = piocb_rqst->dataword; + fx_iocb.req_xfrcnt = piocb_rqst->req_len; + fx_iocb.rsp_xfrcnt = piocb_rqst->rsp_len; + + if (piocb_rqst->flags & SRB_FXDISC_REQ_DMA_VALID) { + int avail_dsds, tot_dsds; + cont_a64_entry_t lcont_pkt; + cont_a64_entry_t *cont_pkt = NULL; + __le32 *cur_dsd; + int index = 0, cont = 0; + + fx_iocb.req_dsdcnt = + cpu_to_le16(bsg_job->request_payload.sg_cnt); + tot_dsds = + bsg_job->request_payload.sg_cnt; + cur_dsd = (__le32 *)&fx_iocb.dseg_rq_address[0]; + avail_dsds = 1; + for_each_sg(bsg_job->request_payload.sg_list, sg, + tot_dsds, index) { + dma_addr_t sle_dma; + + /* Allocate additional continuation packets? */ + if (avail_dsds == 0) { + /* + * Five DSDs are available in the Cont. + * Type 1 IOCB. + */ + memset(&lcont_pkt, 0, + REQUEST_ENTRY_SIZE); + cont_pkt = + qlafx00_prep_cont_type1_iocb( + sp->fcport->vha->req, + &lcont_pkt); + cur_dsd = (__le32 *) + lcont_pkt.dseg_0_address; + avail_dsds = 5; + cont = 1; + entry_cnt++; + } + + 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--; + + if (avail_dsds == 0 && cont == 1) { + cont = 0; + memcpy_toio( + (void __iomem *)cont_pkt, + &lcont_pkt, REQUEST_ENTRY_SIZE); + ql_dump_buffer( + ql_dbg_user + ql_dbg_verbose, + sp->fcport->vha, 0x3042, + (uint8_t *)&lcont_pkt, + REQUEST_ENTRY_SIZE); + } + } + if (avail_dsds != 0 && cont == 1) { + memcpy_toio((void __iomem *)cont_pkt, + &lcont_pkt, REQUEST_ENTRY_SIZE); + ql_dump_buffer(ql_dbg_user + ql_dbg_verbose, + sp->fcport->vha, 0x3043, + (uint8_t *)&lcont_pkt, REQUEST_ENTRY_SIZE); + } + } + + if (piocb_rqst->flags & SRB_FXDISC_RESP_DMA_VALID) { + int avail_dsds, tot_dsds; + cont_a64_entry_t lcont_pkt; + cont_a64_entry_t *cont_pkt = NULL; + __le32 *cur_dsd; + int index = 0, cont = 0; + + fx_iocb.rsp_dsdcnt = + cpu_to_le16(bsg_job->reply_payload.sg_cnt); + tot_dsds = bsg_job->reply_payload.sg_cnt; + cur_dsd = (__le32 *)&fx_iocb.dseg_rsp_address[0]; + avail_dsds = 1; + + for_each_sg(bsg_job->reply_payload.sg_list, sg, + tot_dsds, index) { + dma_addr_t sle_dma; + + /* Allocate additional continuation packets? */ + if (avail_dsds == 0) { + /* + * Five DSDs are available in the Cont. + * Type 1 IOCB. + */ + memset(&lcont_pkt, 0, + REQUEST_ENTRY_SIZE); + cont_pkt = + qlafx00_prep_cont_type1_iocb( + sp->fcport->vha->req, + &lcont_pkt); + cur_dsd = (__le32 *) + lcont_pkt.dseg_0_address; + avail_dsds = 5; + cont = 1; + entry_cnt++; + } + + 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--; + + if (avail_dsds == 0 && cont == 1) { + cont = 0; + memcpy_toio((void __iomem *)cont_pkt, + &lcont_pkt, + REQUEST_ENTRY_SIZE); + ql_dump_buffer( + ql_dbg_user + ql_dbg_verbose, + sp->fcport->vha, 0x3045, + (uint8_t *)&lcont_pkt, + REQUEST_ENTRY_SIZE); + } + } + if (avail_dsds != 0 && cont == 1) { + memcpy_toio((void __iomem *)cont_pkt, + &lcont_pkt, REQUEST_ENTRY_SIZE); + ql_dump_buffer(ql_dbg_user + ql_dbg_verbose, + sp->fcport->vha, 0x3046, + (uint8_t *)&lcont_pkt, REQUEST_ENTRY_SIZE); + } + } + + if (piocb_rqst->flags & SRB_FXDISC_REQ_DWRD_VALID) + fx_iocb.dataword = piocb_rqst->dataword; + fx_iocb.flags = piocb_rqst->flags; + fx_iocb.entry_count = entry_cnt; + } + + ql_dump_buffer(ql_dbg_user + ql_dbg_verbose, + sp->fcport->vha, 0x3047, + (uint8_t *)&fx_iocb, sizeof(struct fxdisc_entry_fx00)); + + memcpy_toio((void __iomem *)pfxiocb, &fx_iocb, + sizeof(struct fxdisc_entry_fx00)); + wmb(); +} diff --git a/drivers/scsi/qla2xxx/qla_mr.h b/drivers/scsi/qla2xxx/qla_mr.h new file mode 100644 index 00000000000..aeaa1b40b1f --- /dev/null +++ b/drivers/scsi/qla2xxx/qla_mr.h @@ -0,0 +1,527 @@ +/* + * QLogic Fibre Channel HBA Driver + * Copyright (c) 2003-2014 QLogic Corporation + * + * See LICENSE.qla2xxx for copyright and licensing details. + */ +#ifndef __QLA_MR_H +#define __QLA_MR_H + +/* + * The PCI VendorID and DeviceID for our board. + */ +#define PCI_DEVICE_ID_QLOGIC_ISPF001 0xF001 + +/* FX00 specific definitions */ + +#define FX00_COMMAND_TYPE_7 0x07 /* Command Type 7 entry for 7XXX */ +struct cmd_type_7_fx00 { + 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. */ + uint8_t reserved_0; + uint8_t port_path_ctrl; + uint16_t reserved_1; + + __le16 tgt_idx; /* Target Idx. */ + uint16_t timeout; /* Command timeout. */ + + __le16 dseg_count; /* Data segment count. */ + uint8_t scsi_rsp_dsd_len; + uint8_t reserved_2; + + struct scsi_lun lun; /* LUN (LE). */ + + uint8_t cntrl_flags; + + uint8_t task_mgmt_flags; /* Task management flags. */ + + uint8_t task; + + uint8_t crn; + + uint8_t fcp_cdb[MAX_CMDSZ]; /* SCSI command words. */ + __le32 byte_count; /* Total byte count. */ + + uint32_t dseg_0_address[2]; /* Data segment 0 address. */ + uint32_t dseg_0_len; /* Data segment 0 length. */ +}; + +#define STATUS_TYPE_FX00 0x01 /* Status entry. */ +struct sts_entry_fx00 { + 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. */ + uint32_t reserved_3; /* System handle. */ + + __le16 comp_status; /* Completion status. */ + uint16_t reserved_0; /* OX_ID used by the firmware. */ + + __le32 residual_len; /* FW calc residual transfer length. */ + + uint16_t reserved_1; + uint16_t state_flags; /* State flags. */ + + uint16_t reserved_2; + __le16 scsi_status; /* SCSI status. */ + + uint32_t sense_len; /* FCP SENSE length. */ + uint8_t data[32]; /* FCP response/sense information. */ +}; + + +#define MAX_HANDLE_COUNT 15 +#define MULTI_STATUS_TYPE_FX00 0x0D + +struct multi_sts_entry_fx00 { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t handle_count; + uint8_t entry_status; + + __le32 handles[MAX_HANDLE_COUNT]; +}; + +#define TSK_MGMT_IOCB_TYPE_FX00 0x05 +struct tsk_mgmt_entry_fx00 { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; + uint8_t entry_status; /* Entry Status. */ + + __le32 handle; /* System handle. */ + + uint32_t reserved_0; + + __le16 tgt_id; /* Target Idx. */ + + uint16_t reserved_1; + uint16_t reserved_3; + uint16_t reserved_4; + + struct scsi_lun lun; /* LUN (LE). */ + + __le32 control_flags; /* Control Flags. */ + + uint8_t reserved_2[32]; +}; + + +#define ABORT_IOCB_TYPE_FX00 0x08 /* Abort IOCB status. */ +struct abort_iocb_entry_fx00 { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + + __le32 handle; /* System handle. */ + __le32 reserved_0; + + __le16 tgt_id_sts; /* Completion status. */ + __le16 options; + + __le32 abort_handle; /* System handle. */ + __le32 reserved_2; + + __le16 req_que_no; + uint8_t reserved_1[38]; +}; + +#define IOCTL_IOSB_TYPE_FX00 0x0C +struct ioctl_iocb_entry_fx00 { + 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. */ + uint32_t reserved_0; /* System handle. */ + + uint16_t comp_func_num; + __le16 fw_iotcl_flags; + + __le32 dataword_r; /* Data word returned */ + uint32_t adapid; /* Adapter ID */ + uint32_t dataword_r_extra; + + __le32 seq_no; + uint8_t reserved_2[20]; + uint32_t residuallen; + __le32 status; +}; + +#define STATUS_CONT_TYPE_FX00 0x04 + +#define FX00_IOCB_TYPE 0x0B +struct fxdisc_entry_fx00 { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System Defined. */ + uint8_t entry_status; /* Entry Status. */ + + __le32 handle; /* System handle. */ + __le32 reserved_0; /* System handle. */ + + __le16 func_num; + __le16 req_xfrcnt; + __le16 req_dsdcnt; + __le16 rsp_xfrcnt; + __le16 rsp_dsdcnt; + uint8_t flags; + uint8_t reserved_1; + + __le32 dseg_rq_address[2]; /* Data segment 0 address. */ + __le32 dseg_rq_len; /* Data segment 0 length. */ + __le32 dseg_rsp_address[2]; /* Data segment 1 address. */ + __le32 dseg_rsp_len; /* Data segment 1 length. */ + + __le32 dataword; + __le32 adapid; + __le32 adapid_hi; + __le32 dataword_extra; +}; + +struct qlafx00_tgt_node_info { + uint8_t tgt_node_wwpn[WWN_SIZE]; + uint8_t tgt_node_wwnn[WWN_SIZE]; + uint32_t tgt_node_state; + uint8_t reserved[128]; + uint32_t reserved_1[8]; + uint64_t reserved_2[4]; +} __packed; + +#define QLAFX00_TGT_NODE_INFO sizeof(struct qlafx00_tgt_node_info) + +#define QLAFX00_LINK_STATUS_DOWN 0x10 +#define QLAFX00_LINK_STATUS_UP 0x11 + +#define QLAFX00_PORT_SPEED_2G 0x2 +#define QLAFX00_PORT_SPEED_4G 0x4 +#define QLAFX00_PORT_SPEED_8G 0x8 +#define QLAFX00_PORT_SPEED_10G 0xa +struct port_info_data { + uint8_t port_state; + uint8_t port_type; + uint16_t port_identifier; + uint32_t up_port_state; + uint8_t fw_ver_num[32]; + uint8_t portal_attrib; + uint16_t host_option; + uint8_t reset_delay; + uint8_t pdwn_retry_cnt; + uint16_t max_luns2tgt; + uint8_t risc_ver; + uint8_t pconn_option; + uint16_t risc_option; + uint16_t max_frame_len; + uint16_t max_iocb_alloc; + uint16_t exec_throttle; + uint8_t retry_cnt; + uint8_t retry_delay; + uint8_t port_name[8]; + uint8_t port_id[3]; + uint8_t link_status; + uint8_t plink_rate; + uint32_t link_config; + uint16_t adap_haddr; + uint8_t tgt_disc; + uint8_t log_tout; + uint8_t node_name[8]; + uint16_t erisc_opt1; + uint8_t resp_acc_tmr; + uint8_t intr_del_tmr; + uint8_t erisc_opt2; + uint8_t alt_port_name[8]; + uint8_t alt_node_name[8]; + uint8_t link_down_tout; + uint8_t conn_type; + uint8_t fc_fw_mode; + uint32_t uiReserved[48]; +} __packed; + +/* OS Type Designations */ +#define OS_TYPE_UNKNOWN 0 +#define OS_TYPE_LINUX 2 + +/* Linux Info */ +#define SYSNAME_LENGTH 128 +#define NODENAME_LENGTH 64 +#define RELEASE_LENGTH 64 +#define VERSION_LENGTH 64 +#define MACHINE_LENGTH 64 +#define DOMNAME_LENGTH 64 + +struct host_system_info { + uint32_t os_type; + char sysname[SYSNAME_LENGTH]; + char nodename[NODENAME_LENGTH]; + char release[RELEASE_LENGTH]; + char version[VERSION_LENGTH]; + char machine[MACHINE_LENGTH]; + char domainname[DOMNAME_LENGTH]; + char hostdriver[VERSION_LENGTH]; + uint32_t reserved[64]; +} __packed; + +struct register_host_info { + struct host_system_info hsi; /* host system info */ + uint64_t utc; /* UTC (system time) */ + uint32_t reserved[64]; /* future additions */ +} __packed; + + +#define QLAFX00_PORT_DATA_INFO (sizeof(struct port_info_data)) +#define QLAFX00_TGT_NODE_LIST_SIZE (sizeof(uint32_t) * 32) + +struct config_info_data { + uint8_t model_num[16]; + uint8_t model_description[80]; + uint8_t reserved0[160]; + uint8_t symbolic_name[64]; + uint8_t serial_num[32]; + uint8_t hw_version[16]; + uint8_t fw_version[16]; + uint8_t uboot_version[16]; + uint8_t fru_serial_num[32]; + + uint8_t fc_port_count; + uint8_t iscsi_port_count; + uint8_t reserved1[2]; + + uint8_t mode; + uint8_t log_level; + uint8_t reserved2[2]; + + uint32_t log_size; + + uint8_t tgt_pres_mode; + uint8_t iqn_flags; + uint8_t lun_mapping; + + uint64_t adapter_id; + + uint32_t cluster_key_len; + uint8_t cluster_key[16]; + + uint64_t cluster_master_id; + uint64_t cluster_slave_id; + uint8_t cluster_flags; + uint32_t enabled_capabilities; + uint32_t nominal_temp_value; +} __packed; + +#define FXDISC_GET_CONFIG_INFO 0x01 +#define FXDISC_GET_PORT_INFO 0x02 +#define FXDISC_GET_TGT_NODE_INFO 0x80 +#define FXDISC_GET_TGT_NODE_LIST 0x81 +#define FXDISC_REG_HOST_INFO 0x99 +#define FXDISC_ABORT_IOCTL 0xff + +#define QLAFX00_HBA_ICNTRL_REG 0x20B08 +#define QLAFX00_ICR_ENB_MASK 0x80000000 +#define QLAFX00_ICR_DIS_MASK 0x7fffffff +#define QLAFX00_HST_RST_REG 0x18264 +#define QLAFX00_SOC_TEMP_REG 0x184C4 +#define QLAFX00_HST_TO_HBA_REG 0x20A04 +#define QLAFX00_HBA_TO_HOST_REG 0x21B70 +#define QLAFX00_HST_INT_STS_BITS 0x7 +#define QLAFX00_BAR1_BASE_ADDR_REG 0x40018 +#define QLAFX00_PEX0_WIN0_BASE_ADDR_REG 0x41824 + +#define QLAFX00_INTR_MB_CMPLT 0x1 +#define QLAFX00_INTR_RSP_CMPLT 0x2 +#define QLAFX00_INTR_ASYNC_CMPLT 0x4 + +#define QLAFX00_MBA_SYSTEM_ERR 0x8002 +#define QLAFX00_MBA_TEMP_OVER 0x8005 +#define QLAFX00_MBA_TEMP_NORM 0x8006 +#define QLAFX00_MBA_TEMP_CRIT 0x8007 +#define QLAFX00_MBA_LINK_UP 0x8011 +#define QLAFX00_MBA_LINK_DOWN 0x8012 +#define QLAFX00_MBA_PORT_UPDATE 0x8014 +#define QLAFX00_MBA_SHUTDOWN_RQSTD 0x8062 + +#define SOC_SW_RST_CONTROL_REG_CORE0 0x0020800 +#define SOC_FABRIC_RST_CONTROL_REG 0x0020840 +#define SOC_FABRIC_CONTROL_REG 0x0020200 +#define SOC_FABRIC_CONFIG_REG 0x0020204 +#define SOC_PWR_MANAGEMENT_PWR_DOWN_REG 0x001820C + +#define SOC_INTERRUPT_SOURCE_I_CONTROL_REG 0x0020B00 +#define SOC_CORE_TIMER_REG 0x0021850 +#define SOC_IRQ_ACK_REG 0x00218b4 + +#define CONTINUE_A64_TYPE_FX00 0x03 /* Continuation entry. */ + +#define QLAFX00_SET_HST_INTR(ha, value) \ + WRT_REG_DWORD((ha)->cregbase + QLAFX00_HST_TO_HBA_REG, \ + value) + +#define QLAFX00_CLR_HST_INTR(ha, value) \ + WRT_REG_DWORD((ha)->cregbase + QLAFX00_HBA_TO_HOST_REG, \ + ~value) + +#define QLAFX00_RD_INTR_REG(ha) \ + RD_REG_DWORD((ha)->cregbase + QLAFX00_HBA_TO_HOST_REG) + +#define QLAFX00_CLR_INTR_REG(ha, value) \ + WRT_REG_DWORD((ha)->cregbase + QLAFX00_HBA_TO_HOST_REG, \ + ~value) + +#define QLAFX00_SET_HBA_SOC_REG(ha, off, val)\ + WRT_REG_DWORD((ha)->cregbase + off, val) + +#define QLAFX00_GET_HBA_SOC_REG(ha, off)\ + RD_REG_DWORD((ha)->cregbase + off) + +#define QLAFX00_HBA_RST_REG(ha, val)\ + WRT_REG_DWORD((ha)->cregbase + QLAFX00_HST_RST_REG, val) + +#define QLAFX00_RD_ICNTRL_REG(ha) \ + RD_REG_DWORD((ha)->cregbase + QLAFX00_HBA_ICNTRL_REG) + +#define QLAFX00_ENABLE_ICNTRL_REG(ha) \ + WRT_REG_DWORD((ha)->cregbase + QLAFX00_HBA_ICNTRL_REG, \ + (QLAFX00_GET_HBA_SOC_REG(ha, QLAFX00_HBA_ICNTRL_REG) | \ + QLAFX00_ICR_ENB_MASK)) + +#define QLAFX00_DISABLE_ICNTRL_REG(ha) \ + WRT_REG_DWORD((ha)->cregbase + QLAFX00_HBA_ICNTRL_REG, \ + (QLAFX00_GET_HBA_SOC_REG(ha, QLAFX00_HBA_ICNTRL_REG) & \ + QLAFX00_ICR_DIS_MASK)) + +#define QLAFX00_RD_REG(ha, off) \ + RD_REG_DWORD((ha)->cregbase + off) + +#define QLAFX00_WR_REG(ha, off, val) \ + WRT_REG_DWORD((ha)->cregbase + off, val) + +struct qla_mt_iocb_rqst_fx00 { + __le32 reserved_0; + + __le16 func_type; + uint8_t flags; + uint8_t reserved_1; + + __le32 dataword; + + __le32 adapid; + __le32 adapid_hi; + + __le32 dataword_extra; + + __le16 req_len; + __le16 reserved_2; + + __le16 rsp_len; + __le16 reserved_3; +}; + +struct qla_mt_iocb_rsp_fx00 { + uint32_t reserved_1; + + uint16_t func_type; + __le16 ioctl_flags; + + __le32 ioctl_data; + + uint32_t adapid; + uint32_t adapid_hi; + + uint32_t reserved_2; + __le32 seq_number; + + uint8_t reserved_3[20]; + + int32_t res_count; + + __le32 status; +}; + + +#define MAILBOX_REGISTER_COUNT_FX00 16 +#define AEN_MAILBOX_REGISTER_COUNT_FX00 8 +#define MAX_FIBRE_DEVICES_FX00 512 +#define MAX_LUNS_FX00 0x1024 +#define MAX_TARGETS_FX00 MAX_ISA_DEVICES +#define REQUEST_ENTRY_CNT_FX00 512 /* Number of request entries. */ +#define RESPONSE_ENTRY_CNT_FX00 256 /* Number of response entries.*/ + +/* + * Firmware state codes for QLAFX00 adapters + */ +#define FSTATE_FX00_CONFIG_WAIT 0x0000 /* Waiting for driver to issue + * Initialize FW Mbox cmd + */ +#define FSTATE_FX00_INITIALIZED 0x1000 /* FW has been initialized by + * the driver + */ + +#define FX00_DEF_RATOV 10 + +struct mr_data_fx00 { + uint8_t symbolic_name[64]; + uint8_t serial_num[32]; + uint8_t hw_version[16]; + uint8_t fw_version[16]; + uint8_t uboot_version[16]; + uint8_t fru_serial_num[32]; + fc_port_t fcport; /* fcport used for requests + * that are not linked + * to a particular target + */ + uint8_t fw_hbt_en; + uint8_t fw_hbt_cnt; + uint8_t fw_hbt_miss_cnt; + uint32_t old_fw_hbt_cnt; + uint16_t fw_reset_timer_tick; + uint8_t fw_reset_timer_exp; + uint16_t fw_critemp_timer_tick; + uint32_t old_aenmbx0_state; + uint32_t critical_temperature; + bool extended_io_enabled; + bool host_info_resend; + uint8_t hinfo_resend_timer_tick; +}; + +#define QLAFX00_EXTENDED_IO_EN_MASK 0x20 + +/* + * SoC Junction Temperature is stored in + * bits 9:1 of SoC Junction Temperature Register + * in a firmware specific format format. + * To get the temperature in Celsius degrees + * the value from this bitfiled should be converted + * using this formula: + * Temperature (degrees C) = ((3,153,000 - (10,000 * X)) / 13,825) + * where X is the bit field value + * this macro reads the register, extracts the bitfield value, + * performs the calcualtions and returns temperature in Celsius + */ +#define QLAFX00_GET_TEMPERATURE(ha) ((3153000 - (10000 * \ + ((QLAFX00_RD_REG(ha, QLAFX00_SOC_TEMP_REG) & 0x3FE) >> 1))) / 13825) + + +#define QLAFX00_LOOP_DOWN_TIME 615 /* 600 */ +#define QLAFX00_HEARTBEAT_INTERVAL 6 /* number of seconds */ +#define QLAFX00_HEARTBEAT_MISS_CNT 3 /* number of miss */ +#define QLAFX00_RESET_INTERVAL 120 /* number of seconds */ +#define QLAFX00_MAX_RESET_INTERVAL 600 /* number of seconds */ +#define QLAFX00_CRITEMP_INTERVAL 60 /* number of seconds */ +#define QLAFX00_HINFO_RESEND_INTERVAL 60 /* number of seconds */ + +#define QLAFX00_CRITEMP_THRSHLD 80 /* Celsius degrees */ + +/* Max conncurrent IOs that can be queued */ +#define QLAFX00_MAX_CANQUEUE 1024 + +/* IOCTL IOCB abort success */ +#define QLAFX00_IOCTL_ICOB_ABORT_SUCCESS 0x68 + +#endif diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c index e1138bcc834..58f3c912d96 100644 --- a/drivers/scsi/qla2xxx/qla_nx.c +++ b/drivers/scsi/qla2xxx/qla_nx.c @@ -1,12 +1,14 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2014 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) @@ -34,7 +36,7 @@ #define MAX_CRB_XFORM 60 static unsigned long crb_addr_xform[MAX_CRB_XFORM]; -int qla82xx_crb_table_initialized; +static int qla82xx_crb_table_initialized; #define qla82xx_crb_addr_transform(name) \ (crb_addr_xform[QLA82XX_HW_PX_MAP_CRB_##name] = \ @@ -100,7 +102,7 @@ static void qla82xx_crb_addr_transform_setup(void) qla82xx_crb_table_initialized = 1; } -struct crb_128M_2M_block_map crb_128M_2M_map[64] = { +static struct crb_128M_2M_block_map crb_128M_2M_map[64] = { {{{0, 0, 0, 0} } }, {{{1, 0x0100000, 0x0102000, 0x120000}, {1, 0x0110000, 0x0120000, 0x130000}, @@ -260,7 +262,7 @@ struct crb_128M_2M_block_map crb_128M_2M_map[64] = { /* * top 12 bits of crb internal address (hub, agent) */ -unsigned qla82xx_crb_hub_agt[64] = { +static unsigned qla82xx_crb_hub_agt[64] = { 0, QLA82XX_HW_CRB_HUB_AGT_ADR_PS, QLA82XX_HW_CRB_HUB_AGT_ADR_MN, @@ -328,7 +330,7 @@ unsigned qla82xx_crb_hub_agt[64] = { }; /* Device states */ -char *qdev_state[] = { +static char *q_dev_state[] = { "Unknown", "Cold", "Initializing", @@ -339,6 +341,11 @@ char *qdev_state[] = { "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 @@ -348,19 +355,22 @@ 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)); + (void __iomem *)(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)); + win_read = RD_REG_DWORD((void __iomem *) + (CRB_WINDOW_2M + ha->nx_pcibase)); if (win_read != ha->crb_win) { - DEBUG2(qla_printk(KERN_INFO, ha, - "%s: Written crbwin (0x%x) != Read crbwin (0x%x), " - "off=0x%lx\n", __func__, ha->crb_win, win_read, *off)); + 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; } @@ -368,6 +378,7 @@ qla82xx_pci_set_crbwindow_2M(struct qla_hw_data *ha, ulong *off) 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 @@ -398,9 +409,10 @@ qla82xx_pci_set_crbwindow(struct qla_hw_data *ha, u64 off) return off; } /* strange address given */ - qla_printk(KERN_WARNING, ha, - "%s: Warning: unm_nic_pci_set_crbwindow called with" - " an unknown address(%llx)\n", QLA2XXX_DRIVER_NAME, off); + 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; } @@ -556,13 +568,14 @@ qla82xx_pci_mem_bound_check(struct qla_hw_data *ha, return 1; } -int qla82xx_pci_set_window_warning_count; +static 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)) { @@ -574,8 +587,8 @@ qla82xx_pci_set_window(struct qla_hw_data *ha, unsigned long long addr) win_read = qla82xx_rd_32(ha, ha->mn_win_crb | QLA82XX_PCI_CRBSPACE); if ((win_read << 17) != window) { - qla_printk(KERN_WARNING, ha, - "%s: Written MNwin (0x%x) != Read MNwin (0x%x)\n", + 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; @@ -583,7 +596,7 @@ qla82xx_pci_set_window(struct qla_hw_data *ha, unsigned long long addr) QLA82XX_ADDR_OCM0_MAX)) { unsigned int temp1; if ((addr & 0x00ff800) == 0xff800) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0xb004, "%s: QM access not handled.\n", __func__); addr = -1UL; } @@ -596,8 +609,8 @@ qla82xx_pci_set_window(struct qla_hw_data *ha, unsigned long long addr) temp1 = ((window & 0x1FF) << 7) | ((window & 0x0FFFE0000) >> 17); if (win_read != temp1) { - qla_printk(KERN_WARNING, ha, - "%s: Written OCMwin (0x%x) != Read OCMwin (0x%x)\n", + 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; @@ -612,8 +625,8 @@ qla82xx_pci_set_window(struct qla_hw_data *ha, unsigned long long addr) win_read = qla82xx_rd_32(ha, ha->ms_win_crb | QLA82XX_PCI_CRBSPACE); if (win_read != window) { - qla_printk(KERN_WARNING, ha, - "%s: Written MSwin (0x%x) != Read MSwin (0x%x)\n", + 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; @@ -624,9 +637,9 @@ qla82xx_pci_set_window(struct qla_hw_data *ha, unsigned long long addr) */ if ((qla82xx_pci_set_window_warning_count++ < 8) || (qla82xx_pci_set_window_warning_count%64 == 0)) { - qla_printk(KERN_WARNING, ha, - "%s: Warning:%s Unknown address range!\n", __func__, - QLA2XXX_DRIVER_NAME); + ql_log(ql_log_warn, vha, 0xb007, + "%s: Warning:%s Unknown address range!.\n", + __func__, QLA2XXX_DRIVER_NAME); } addr = -1UL; } @@ -665,12 +678,13 @@ static int qla82xx_pci_mem_read_direct(struct qla_hw_data *ha, u64 off, void *data, int size) { unsigned long flags; - void *addr = NULL; + void __iomem *addr = NULL; int ret = 0; u64 start; - uint8_t *mem_ptr = NULL; + uint8_t __iomem *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); @@ -682,9 +696,10 @@ static int qla82xx_pci_mem_read_direct(struct qla_hw_data *ha, if ((start == -1UL) || (qla82xx_pci_is_same_window(ha, off + size - 1) == 0)) { write_unlock_irqrestore(&ha->hw_lock, flags); - qla_printk(KERN_ERR, ha, - "%s out of bound pci memory access. " - "offset is 0x%llx\n", QLA2XXX_DRIVER_NAME, off); + 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; } @@ -698,7 +713,7 @@ static int qla82xx_pci_mem_read_direct(struct qla_hw_data *ha, mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE * 2); else mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE); - if (mem_ptr == 0UL) { + if (mem_ptr == NULL) { *(u8 *)data = 0; return -1; } @@ -735,12 +750,13 @@ qla82xx_pci_mem_write_direct(struct qla_hw_data *ha, u64 off, void *data, int size) { unsigned long flags; - void *addr = NULL; + void __iomem *addr = NULL; int ret = 0; u64 start; - uint8_t *mem_ptr = NULL; + uint8_t __iomem *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); @@ -752,9 +768,10 @@ qla82xx_pci_mem_write_direct(struct qla_hw_data *ha, if ((start == -1UL) || (qla82xx_pci_is_same_window(ha, off + size - 1) == 0)) { write_unlock_irqrestore(&ha->hw_lock, flags); - qla_printk(KERN_ERR, ha, - "%s out of bound pci memory access. " - "offset is 0x%llx\n", QLA2XXX_DRIVER_NAME, off); + ql_log(ql_log_fatal, vha, 0xb009, + "%s out of bount memory " + "access, offset is 0x%llx.\n", + QLA2XXX_DRIVER_NAME, off); return -1; } @@ -768,7 +785,7 @@ qla82xx_pci_mem_write_direct(struct qla_hw_data *ha, mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE*2); else mem_ptr = ioremap(mem_base + mem_page, PAGE_SIZE); - if (mem_ptr == 0UL) + if (mem_ptr == NULL) return -1; addr = mem_ptr; @@ -830,23 +847,31 @@ static int qla82xx_rom_lock(struct qla_hw_data *ha) { int done = 0, timeout = 0; + uint32_t lock_owner = 0; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); 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) + if (timeout >= qla82xx_rom_lock_timeout) { + lock_owner = qla82xx_rd_32(ha, QLA82XX_ROM_LOCK_ID); + ql_log(ql_log_warn, vha, 0xb157, + "%s: Simultaneous flash access by following ports, active port = %d: accessing port = %d", + __func__, ha->portnum, lock_owner); return -1; + } timeout++; } - qla82xx_wr_32(ha, QLA82XX_ROM_LOCK_ID, ROM_LOCK_DRIVER); + qla82xx_wr_32(ha, QLA82XX_ROM_LOCK_ID, ha->portnum); return 0; } static void qla82xx_rom_unlock(struct qla_hw_data *ha) { + qla82xx_wr_32(ha, QLA82XX_ROM_LOCK_ID, 0xffffffff); qla82xx_rd_32(ha, QLA82XX_PCIE_REG(PCIE_SEM2_UNLOCK)); } @@ -855,15 +880,16 @@ 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) { - DEBUG(qla_printk(KERN_INFO, ha, - "%s: Timeout reached waiting for rom busy", - QLA2XXX_DRIVER_NAME)); + ql_dbg(ql_dbg_p3p, vha, 0xb00a, + "%s: Timeout reached waiting for rom busy.\n", + QLA2XXX_DRIVER_NAME); return -1; } } @@ -875,15 +901,16 @@ 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) { - DEBUG(qla_printk(KERN_INFO, ha, - "%s: Timeout reached waiting for rom done", - QLA2XXX_DRIVER_NAME)); + ql_dbg(ql_dbg_p3p, vha, 0xb00b, + "%s: Timeout reached waiting for rom done.\n", + QLA2XXX_DRIVER_NAME); return -1; } } @@ -891,25 +918,36 @@ qla82xx_wait_rom_done(struct qla_hw_data *ha) } static 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 __iomem *)(CRB_WINDOW_2M + ha->nx_pcibase), + (off & 0xFFFF0000)); + + /* Read back value to make sure write has gone through */ + RD_REG_DWORD((void __iomem *)(CRB_WINDOW_2M + ha->nx_pcibase)); + off_value = (off & 0x0000FFFF); + + if (flag) + WRT_REG_DWORD((void __iomem *) + (off_value + CRB_INDIRECT_2M + ha->nx_pcibase), + data); + else + rval = RD_REG_DWORD((void __iomem *) + (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) { - qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ADDRESS, addr); - qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_DUMMY_BYTE_CNT, 0); - qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ABYTE_CNT, 3); - qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, 0xb); - qla82xx_wait_rom_busy(ha); - if (qla82xx_wait_rom_done(ha)) { - qla_printk(KERN_WARNING, ha, - "%s: Error waiting for rom done\n", - QLA2XXX_DRIVER_NAME); - return -1; - } - /* Reset abyte_cnt and dummy_byte_cnt */ - qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_DUMMY_BYTE_CNT, 0); - udelay(10); - cond_resched(); - qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_ABYTE_CNT, 0); - *valp = qla82xx_rd_32(ha, QLA82XX_ROMUSB_ROM_RDATA); + /* 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; } @@ -917,6 +955,8 @@ static int qla82xx_rom_fast_read(struct qla_hw_data *ha, int addr, int *valp) { int ret, loops = 0; + uint32_t lock_owner = 0; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); while ((qla82xx_rom_lock(ha) != 0) && (loops < 50000)) { udelay(100); @@ -924,9 +964,10 @@ qla82xx_rom_fast_read(struct qla_hw_data *ha, int addr, int *valp) loops++; } if (loops >= 50000) { - qla_printk(KERN_INFO, ha, - "%s: qla82xx_rom_lock failed\n", - QLA2XXX_DRIVER_NAME); + lock_owner = qla82xx_rd_32(ha, QLA82XX_ROM_LOCK_ID); + ql_log(ql_log_fatal, vha, 0x00b9, + "Failed to acquire SEM2 lock, Lock Owner %u.\n", + lock_owner); return -1; } ret = qla82xx_do_rom_fast_read(ha, addr, valp); @@ -937,11 +978,12 @@ qla82xx_rom_fast_read(struct qla_hw_data *ha, int addr, int *valp) 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)) { - qla_printk(KERN_WARNING, ha, - "Error waiting for rom done\n"); + ql_log(ql_log_warn, vha, 0xb00c, + "Error waiting for rom done.\n"); return -1; } *val = qla82xx_rd_32(ha, QLA82XX_ROMUSB_ROM_RDATA); @@ -955,6 +997,7 @@ qla82xx_flash_wait_write_finish(struct qla_hw_data *ha) 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)) { @@ -964,8 +1007,8 @@ qla82xx_flash_wait_write_finish(struct qla_hw_data *ha) udelay(10); cond_resched(); if (timeout >= 50000) { - qla_printk(KERN_WARNING, ha, - "Timeout reached waiting for write finish"); + ql_log(ql_log_warn, vha, 0xb00d, + "Timeout reached waiting for write finish.\n"); return -1; } } @@ -992,13 +1035,14 @@ qla82xx_flash_set_write_enable(struct qla_hw_data *ha) 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)) { - qla_printk(KERN_WARNING, ha, - "Error waiting for rom done\n"); + ql_log(ql_log_warn, vha, 0xb00e, + "Error waiting for rom done.\n"); return -1; } return qla82xx_flash_wait_write_finish(ha); @@ -1007,10 +1051,11 @@ qla82xx_write_status_reg(struct qla_hw_data *ha, uint32_t val) 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)) { - qla_printk(KERN_WARNING, ha, - "Error waiting for rom done\n"); + ql_log(ql_log_warn, vha, 0xb00f, + "Error waiting for rom done.\n"); return -1; } return 0; @@ -1020,16 +1065,21 @@ static int ql82xx_rom_lock_d(struct qla_hw_data *ha) { int loops = 0; + uint32_t lock_owner = 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) { - qla_printk(KERN_WARNING, ha, "ROM lock failed\n"); + lock_owner = qla82xx_rd_32(ha, QLA82XX_ROM_LOCK_ID); + ql_log(ql_log_warn, vha, 0xb010, + "ROM lock failed, Lock Owner %u.\n", lock_owner); return -1; } - return 0;; + return 0; } static int @@ -1037,10 +1087,12 @@ 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) { - qla_printk(KERN_WARNING, ha, "ROM Lock failed\n"); + ql_log(ql_log_warn, vha, 0xb011, + "ROM lock failed.\n"); return ret; } @@ -1053,8 +1105,8 @@ qla82xx_write_flash_dword(struct qla_hw_data *ha, uint32_t flashaddr, qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_PP); qla82xx_wait_rom_busy(ha); if (qla82xx_wait_rom_done(ha)) { - qla_printk(KERN_WARNING, ha, - "Error waiting for rom done\n"); + ql_log(ql_log_warn, vha, 0xb012, + "Error waiting for rom done.\n"); ret = -1; goto done_write; } @@ -1084,7 +1136,7 @@ qla82xx_pinit_from_rom(scsi_qla_host_t *vha) long data; }; - /* Halt all the indiviual PEGs and other blocks of the ISP */ + /* Halt all the individual PEGs and other blocks of the ISP */ qla82xx_rom_lock(ha); /* disable all I2Q */ @@ -1137,19 +1189,6 @@ qla82xx_pinit_from_rom(scsi_qla_host_t *vha) qla82xx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, 0xfeffffff); else qla82xx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, 0xffffffff); - - /* reset ms */ - val = qla82xx_rd_32(ha, QLA82XX_CRB_QDR_NET + 0xe4); - val |= (1 << 1); - qla82xx_wr_32(ha, QLA82XX_CRB_QDR_NET + 0xe4, val); - msleep(20); - - /* unreset ms */ - val = qla82xx_rd_32(ha, QLA82XX_CRB_QDR_NET + 0xe4); - val &= ~(1 << 1); - qla82xx_wr_32(ha, QLA82XX_CRB_QDR_NET + 0xe4, val); - msleep(20); - qla82xx_rom_unlock(ha); /* Read the signature value from the flash. @@ -1159,33 +1198,31 @@ qla82xx_pinit_from_rom(scsi_qla_host_t *vha) */ if (qla82xx_rom_fast_read(ha, 0, &n) != 0 || n != 0xcafecafeUL || qla82xx_rom_fast_read(ha, 4, &n) != 0) { - qla_printk(KERN_WARNING, ha, - "[ERROR] Reading crb_init area: n: %08x\n", n); + 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 + * Number of entries = upper 16 bits */ offset = n & 0xffffU; n = (n >> 16) & 0xffffU; - /* number of addr/value pair should not exceed 1024 enteries */ + /* number of addr/value pair should not exceed 1024 entries */ if (n >= 1024) { - qla_printk(KERN_WARNING, ha, - "%s: %s:n=0x%x [ERROR] Card flash not initialized.\n", - QLA2XXX_DRIVER_NAME, __func__, n); + ql_log(ql_log_fatal, vha, 0x0071, + "Card flash not initialized:n=0x%x.\n", n); return -1; } - qla_printk(KERN_INFO, ha, - "%s: %d CRB init values found in ROM.\n", QLA2XXX_DRIVER_NAME, n); + 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) { - qla_printk(KERN_WARNING, ha, - "%s: [ERROR] Unable to malloc memory.\n", - QLA2XXX_DRIVER_NAME); + ql_log(ql_log_fatal, vha, 0x010c, + "Unable to allocate memory.\n"); return -1; } @@ -1236,9 +1273,8 @@ qla82xx_pinit_from_rom(scsi_qla_host_t *vha) continue; if (off == ADDR_ERROR) { - qla_printk(KERN_WARNING, ha, - "%s: [ERROR] Unknown addr: 0x%08lx\n", - QLA2XXX_DRIVER_NAME, buf[i].addr); + ql_log(ql_log_fatal, vha, 0x0116, + "Unknow addr: 0x%08lx.\n", buf[i].addr); continue; } @@ -1370,7 +1406,7 @@ qla82xx_pci_mem_write_2M(struct qla_hw_data *ha, if (j >= MAX_CTL_CHECK) { if (printk_ratelimit()) dev_err(&ha->pdev->dev, - "failed to write through agent\n"); + "failed to write through agent.\n"); ret = -1; break; } @@ -1460,7 +1496,7 @@ qla82xx_pci_mem_read_2M(struct qla_hw_data *ha, if (j >= MAX_CTL_CHECK) { if (printk_ratelimit()) dev_err(&ha->pdev->dev, - "failed to read through agent\n"); + "failed to read through agent.\n"); break; } @@ -1590,25 +1626,6 @@ qla82xx_get_fw_offs(struct qla_hw_data *ha) } /* 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; @@ -1633,17 +1650,15 @@ qla82xx_iospace_config(struct qla_hw_data *ha) uint32_t len = 0; if (pci_request_regions(ha->pdev, QLA2XXX_DRIVER_NAME)) { - qla_printk(KERN_WARNING, ha, - "Failed to reserve selected regions (%s)\n", - pci_name(ha->pdev)); + 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)) { - qla_printk(KERN_ERR, ha, - "region #0 not an MMIO resource (%s), aborting\n", - pci_name(ha->pdev)); + ql_log_pci(ql_log_fatal, ha->pdev, 0x000d, + "Region #0 not an MMIO resource, aborting.\n"); goto iospace_error_exit; } @@ -1651,26 +1666,28 @@ qla82xx_iospace_config(struct qla_hw_data *ha) ha->nx_pcibase = (unsigned long)ioremap(pci_resource_start(ha->pdev, 0), len); if (!ha->nx_pcibase) { - qla_printk(KERN_ERR, ha, - "cannot remap pcibase MMIO (%s), aborting\n", - pci_name(ha->pdev)); - pci_release_regions(ha->pdev); + ql_log_pci(ql_log_fatal, ha->pdev, 0x000e, + "Cannot remap pcibase MMIO, aborting.\n"); 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 (IS_QLA8044(ha)) { + ha->iobase = + (device_reg_t *)((uint8_t *)ha->nx_pcibase); + } else if (IS_QLA82XX(ha)) { + ha->iobase = + (device_reg_t *)((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) { - qla_printk(KERN_ERR, ha, - "cannot remap MMIO (%s), aborting\n", - pci_name(ha->pdev)); - pci_release_regions(ha->pdev); + ql_log_pci(ql_log_fatal, ha->pdev, 0x000f, + "Cannot remap MMIO, aborting.\n"); goto iospace_error_exit; } @@ -1687,6 +1704,16 @@ qla82xx_iospace_config(struct qla_hw_data *ha) 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: @@ -1712,6 +1739,9 @@ qla82xx_pci_config(scsi_qla_host_t *vha) 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; } @@ -1752,14 +1782,6 @@ void qla82xx_config_rings(struct scsi_qla_host *vha) WRT_REG_DWORD((unsigned long __iomem *)®->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) { @@ -1844,7 +1866,7 @@ qla82xx_set_product_offset(struct qla_hw_data *ha) return -1; } -int +static int qla82xx_validate_firmware_blob(scsi_qla_host_t *vha, uint8_t fw_type) { __le32 val; @@ -1877,6 +1899,7 @@ 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); @@ -1892,15 +1915,15 @@ qla82xx_check_cmdpeg_state(struct qla_hw_data *ha) default: break; } - qla_printk(KERN_WARNING, ha, - "CRB_CMDPEG_STATE: 0x%x and retries: 0x%x\n", - val, retries); + ql_log(ql_log_info, vha, 0x00a8, + "CRB_CMDPEG_STATE: 0x%x and retries:0x%x.\n", + val, retries); msleep(500); } while (--retries); - qla_printk(KERN_INFO, ha, + 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); @@ -1915,6 +1938,7 @@ 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); @@ -1930,17 +1954,16 @@ qla82xx_check_rcvpeg_state(struct qla_hw_data *ha) default: break; } - - qla_printk(KERN_WARNING, ha, - "CRB_RCVPEG_STATE: 0x%x and retries: 0x%x\n", - val, retries); + ql_log(ql_log_info, vha, 0x00ab, + "CRB_RCVPEG_STATE: 0x%x and retries: 0x%x.\n", + val, retries); msleep(500); } while (--retries); - qla_printk(KERN_INFO, ha, - "Rcv Peg initialization failed: 0x%x.\n", val); + 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); @@ -1948,20 +1971,6 @@ qla82xx_check_rcvpeg_state(struct qla_hw_data *ha) } /* 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; @@ -1970,7 +1979,7 @@ static struct qla82xx_legacy_intr_set legacy_intr[] = \ * @ha: SCSI driver HA context * @mb0: Mailbox0 register */ -static void +void qla82xx_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0) { uint16_t cnt; @@ -1988,15 +1997,9 @@ qla82xx_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0) wptr++; } - if (ha->mcp) { - DEBUG3_11(printk(KERN_INFO "%s(%ld): " - "Got mailbox completion. cmd=%x.\n", - __func__, vha->host_no, ha->mcp->mb[0])); - } else { - qla_printk(KERN_INFO, ha, - "%s(%ld): MBX pointer ERROR!\n", - __func__, vha->host_no); - } + if (!ha->mcp) + ql_dbg(ql_dbg_async, vha, 0x5053, + "MBX pointer ERROR.\n"); } /* @@ -2019,13 +2022,13 @@ qla82xx_intr_handler(int irq, void *dev_id) int status = 0, status1 = 0; unsigned long flags; unsigned long iter; - uint32_t stat; + 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__); + ql_log(ql_log_info, NULL, 0xb053, + "%s: NULL response queue pointer.\n", __func__); return IRQ_NONE; } ha = rsp->hw; @@ -2075,30 +2078,21 @@ qla82xx_intr_handler(int irq, void *dev_id) qla24xx_process_response_queue(vha, rsp); break; default: - DEBUG2(printk("scsi(%ld): " - " Unrecognized interrupt type (%d).\n", - vha->host_no, stat & 0xff)); + ql_dbg(ql_dbg_async, vha, 0x5054, + "Unrecognized interrupt type (%d).\n", + stat & 0xff); break; } } WRT_REG_DWORD(®->host_int, 0); } + + qla2x00_handle_mbx_completion(ha, status); 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) - qla_printk(KERN_WARNING, ha, - "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; } @@ -2111,13 +2105,14 @@ qla82xx_msix_default(int irq, void *dev_id) struct device_reg_82xx __iomem *reg; int status = 0; unsigned long flags; - uint32_t stat; + uint32_t stat = 0; + uint32_t host_int = 0; uint16_t mb[4]; rsp = (struct rsp_que *) dev_id; if (!rsp) { printk(KERN_INFO - "%s(): NULL response queue pointer\n", __func__); + "%s(): NULL response queue pointer.\n", __func__); return IRQ_NONE; } ha = rsp->hw; @@ -2127,7 +2122,10 @@ qla82xx_msix_default(int irq, void *dev_id) spin_lock_irqsave(&ha->hardware_lock, flags); vha = pci_get_drvdata(ha->pdev); do { - if (RD_REG_DWORD(®->host_int)) { + host_int = RD_REG_DWORD(®->host_int); + if (qla2x00_check_reg_for_disconnect(vha, host_int)) + break; + if (host_int) { stat = RD_REG_DWORD(®->host_status); switch (stat & 0xff) { @@ -2149,29 +2147,18 @@ qla82xx_msix_default(int irq, void *dev_id) qla24xx_process_response_queue(vha, rsp); break; default: - DEBUG2(printk("scsi(%ld): " - " Unrecognized interrupt type (%d).\n", - vha->host_no, stat & 0xff)); + ql_dbg(ql_dbg_async, vha, 0x5041, + "Unrecognized interrupt type (%d).\n", + stat & 0xff); break; } } WRT_REG_DWORD(®->host_int, 0); } while (0); + qla2x00_handle_mbx_completion(ha, status); spin_unlock_irqrestore(&ha->hardware_lock, flags); -#ifdef QL_DEBUG_LEVEL_17 - if (!irq && ha->flags.eeh_busy) - qla_printk(KERN_WARNING, ha, - "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; } @@ -2182,21 +2169,27 @@ qla82xx_msix_rsp_q(int irq, void *dev_id) struct qla_hw_data *ha; struct rsp_que *rsp; struct device_reg_82xx __iomem *reg; + unsigned long flags; + uint32_t host_int = 0; rsp = (struct rsp_que *) dev_id; if (!rsp) { printk(KERN_INFO - "%s(): NULL response queue pointer\n", __func__); + "%s(): NULL response queue pointer.\n", __func__); return IRQ_NONE; } ha = rsp->hw; reg = &ha->iobase->isp82; - spin_lock_irq(&ha->hardware_lock); + spin_lock_irqsave(&ha->hardware_lock, flags); vha = pci_get_drvdata(ha->pdev); + host_int = RD_REG_DWORD(®->host_int); + if (qla2x00_check_reg_for_disconnect(vha, host_int)) + goto out; qla24xx_process_response_queue(vha, rsp); WRT_REG_DWORD(®->host_int, 0); - spin_unlock_irq(&ha->hardware_lock); +out: + spin_unlock_irqrestore(&ha->hardware_lock, flags); return IRQ_HANDLED; } @@ -2209,13 +2202,14 @@ qla82xx_poll(int irq, void *dev_id) struct device_reg_82xx __iomem *reg; int status = 0; uint32_t stat; + uint32_t host_int = 0; 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__); + "%s(): NULL response queue pointer.\n", __func__); return; } ha = rsp->hw; @@ -2224,7 +2218,10 @@ qla82xx_poll(int irq, void *dev_id) spin_lock_irqsave(&ha->hardware_lock, flags); vha = pci_get_drvdata(ha->pdev); - if (RD_REG_DWORD(®->host_int)) { + host_int = RD_REG_DWORD(®->host_int); + if (qla2x00_check_reg_for_disconnect(vha, host_int)) + goto out; + if (host_int) { stat = RD_REG_DWORD(®->host_status); switch (stat & 0xff) { case 0x1: @@ -2245,13 +2242,14 @@ qla82xx_poll(int irq, void *dev_id) qla24xx_process_response_queue(vha, rsp); break; default: - DEBUG2(printk("scsi(%ld): Unrecognized interrupt type " - "(%d).\n", - vha->host_no, stat & 0xff)); + ql_dbg(ql_dbg_p3p, vha, 0xb013, + "Unrecognized interrupt type (%d).\n", + stat * 0xff); break; } + WRT_REG_DWORD(®->host_int, 0); } - WRT_REG_DWORD(®->host_int, 0); +out: spin_unlock_irqrestore(&ha->hardware_lock, flags); } @@ -2261,7 +2259,10 @@ 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); + if (IS_QLA8044(ha)) + qla8044_wr_reg(ha, LEG_INTR_MASK_OFFSET, 0); + else + qla82xx_wr_32(ha, ha->nx_legacy_intr.tgt_mask_reg, 0xfbff); spin_unlock_irq(&ha->hardware_lock); ha->interrupts_on = 1; } @@ -2272,7 +2273,10 @@ 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); + if (IS_QLA8044(ha)) + qla8044_wr_reg(ha, LEG_INTR_MASK_OFFSET, 1); + else + qla82xx_wr_32(ha, ha->nx_legacy_intr.tgt_mask_reg, 0x0400); spin_unlock_irq(&ha->hardware_lock); ha->interrupts_on = 0; } @@ -2295,6 +2299,29 @@ void qla82xx_init_flags(struct qla_hw_data *ha) } inline void +qla82xx_set_idc_version(scsi_qla_host_t *vha) +{ + int idc_ver; + uint32_t drv_active; + struct qla_hw_data *ha = vha->hw; + + drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); + if (drv_active == (QLA82XX_DRV_ACTIVE << (ha->portnum * 4))) { + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION, + QLA82XX_IDC_VERSION); + ql_log(ql_log_info, vha, 0xb082, + "IDC version updated to %d\n", QLA82XX_IDC_VERSION); + } else { + idc_ver = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_IDC_VERSION); + if (idc_ver != QLA82XX_IDC_VERSION) + ql_log(ql_log_info, vha, 0xb083, + "qla2xxx driver IDC version %d is not compatible " + "with IDC version %d of the other drivers\n", + QLA82XX_IDC_VERSION, idc_ver); + } +} + +inline void qla82xx_set_drv_active(scsi_qla_host_t *vha) { uint32_t drv_active; @@ -2328,9 +2355,13 @@ qla82xx_need_reset(struct qla_hw_data *ha) uint32_t drv_state; int rval; - drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); - rval = drv_state & (QLA82XX_DRVST_RST_RDY << (ha->portnum * 4)); - return rval; + if (ha->flags.nic_core_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 @@ -2347,9 +2378,8 @@ qla82xx_set_rst_ready(struct qla_hw_data *ha) drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); } drv_state |= (QLA82XX_DRVST_RST_RDY << (ha->portnum * 4)); - qla_printk(KERN_INFO, ha, - "%s(%ld):drv_state = 0x%x\n", - __func__, vha->host_no, drv_state); + ql_dbg(ql_dbg_init, vha, 0x00bb, + "drv_state = 0x%08x.\n", drv_state); qla82xx_wr_32(ha, QLA82XX_CRB_DRV_STATE, drv_state); } @@ -2392,8 +2422,8 @@ qla82xx_load_fw(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; if (qla82xx_pinit_from_rom(vha) != QLA_SUCCESS) { - qla_printk(KERN_ERR, ha, - "%s: Error during CRB Initialization\n", __func__); + ql_log(ql_log_fatal, vha, 0x009f, + "Error during CRB initialization.\n"); return QLA_FUNCTION_FAILED; } udelay(500); @@ -2411,27 +2441,27 @@ qla82xx_load_fw(scsi_qla_host_t *vha) if (ql2xfwloadbin == 2) goto try_blob_fw; - qla_printk(KERN_INFO, ha, - "Attempting to load firmware from flash\n"); + ql_log(ql_log_info, vha, 0x00a0, + "Attempting to load firmware from flash.\n"); if (qla82xx_fw_load_from_flash(ha) == QLA_SUCCESS) { - qla_printk(KERN_ERR, ha, - "Firmware loaded successfully from flash\n"); + ql_log(ql_log_info, vha, 0x00a1, + "Firmware loaded successfully from flash.\n"); return QLA_SUCCESS; } else { - qla_printk(KERN_ERR, ha, - "Firmware load from flash failed\n"); + ql_log(ql_log_warn, vha, 0x0108, + "Firmware load from flash failed.\n"); } try_blob_fw: - qla_printk(KERN_INFO, ha, - "Attempting to load firmware from blob\n"); + 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) { - qla_printk(KERN_ERR, ha, - "Firmware image not present.\n"); + ql_log(ql_log_fatal, vha, 0x00a3, + "Firmware image not present.\n"); goto fw_load_failed; } @@ -2441,20 +2471,19 @@ try_blob_fw: /* Fallback to URI format */ if (qla82xx_validate_firmware_blob(vha, QLA82XX_UNIFIED_ROMIMAGE)) { - qla_printk(KERN_ERR, ha, - "No valid firmware image found!!!"); + 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) { - qla_printk(KERN_ERR, ha, - "%s: Firmware loaded successfully " - " from binary blob\n", __func__); + ql_log(ql_log_info, vha, 0x00a5, + "Firmware loaded successfully from binary blob.\n"); return QLA_SUCCESS; } else { - qla_printk(KERN_ERR, ha, - "Firmware load failed from binary blob\n"); + ql_log(ql_log_fatal, vha, 0x00a6, + "Firmware load failed for binary blob.\n"); blob->fw = NULL; blob = NULL; goto fw_load_failed; @@ -2468,7 +2497,6 @@ fw_load_failed: int qla82xx_start_firmware(scsi_qla_host_t *vha) { - int pcie_cap; uint16_t lnk; struct qla_hw_data *ha = vha->hw; @@ -2486,488 +2514,26 @@ qla82xx_start_firmware(scsi_qla_host_t *vha) qla82xx_wr_32(ha, QLA82XX_PEG_HALT_STATUS2, 0); if (qla82xx_load_fw(vha) != QLA_SUCCESS) { - qla_printk(KERN_INFO, ha, - "%s: Error trying to start fw!\n", __func__); + 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) { - qla_printk(KERN_INFO, ha, - "%s: Error during card handshake!\n", __func__); + 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); + pcie_capability_read_word(ha->pdev, PCI_EXP_LNKSTA, &lnk); ha->link_width = (lnk >> 4) & 0x3f; /* Synchronize with Receive peg */ return qla82xx_check_rcvpeg_state(ha); } -static inline int -qla2xx_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 = sp->cmd; - - /* 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 = sp->ctx; - - 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)); - *dsd_seg++ = 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; -} - -/* - * qla82xx_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 -qla82xx_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; -} - -/* - * 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 = sp->cmd; - 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) - 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 = qla82xx_calc_dsd_lists(tot_dsds); - if ((more_dsd_lists + ha->gbl_dsd_inuse) >= NUM_DSD_CHAIN) - 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) - 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); - 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( - ®->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->ctx = mempool_alloc(ha->ctx_mempool, GFP_ATOMIC); - if (!sp->ctx) { - DEBUG(printk(KERN_INFO - "%s(%ld): failed to allocate" - " ctx.\n", __func__, vha->host_no)); - 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) { - DEBUG2_3(printk("%s(%ld): failed to allocate" - " fcp_cmnd.\n", __func__, vha->host_no)); - 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 - */ - 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 (qla2xx_build_scsi_type_6_iocbs(sp, cmd_pkt, tot_dsds)) - goto queuing_error_fcp_cmnd; - - int_to_scsilun(sp->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: - ctx->fcp_cmnd->task_attribute = - TSK_HEAD_OF_QUEUE; - break; - case ORDERED_QUEUE_TAG: - ctx->fcp_cmnd->task_attribute = - TSK_ORDERED; - break; - } - } - - /* build FCP_CMND IU */ - memset(ctx->fcp_cmnd, 0, sizeof(struct fcp_cmnd)); - int_to_scsilun(sp->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; - - 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(tot_dsds); - if (req->cnt < (req_cnt + 2)) { - cnt = (uint16_t)RD_REG_DWORD_RELAXED( - ®->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(sp->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; - - } - /* Build command packet. */ - req->current_outstanding_cmd = handle; - req->outstanding_cmds[handle] = sp; - sp->handle = handle; - sp->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->ctx) { - mempool_free(sp->ctx, ha->ctx_mempool); - sp->ctx = NULL; - } - spin_unlock_irqrestore(&ha->hardware_lock, flags); - - return QLA_FUNCTION_FAILED; -} - static uint32_t * qla82xx_read_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, uint32_t length) @@ -2979,8 +2545,8 @@ qla82xx_read_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, /* Dword reads to flash. */ for (i = 0; i < length/4; i++, faddr += 4) { if (qla82xx_rom_fast_read(ha, faddr, &val)) { - qla_printk(KERN_WARNING, ha, - "Do ROM fast read failed\n"); + ql_log(ql_log_warn, vha, 0x0106, + "Do ROM fast read failed.\n"); goto done_read; } dwptr[i] = __constant_cpu_to_le32(val); @@ -2994,10 +2560,12 @@ 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) { - qla_printk(KERN_WARNING, ha, "ROM Lock failed\n"); + ql_log(ql_log_warn, vha, 0xb014, + "ROM Lock failed.\n"); return ret; } @@ -3013,7 +2581,8 @@ qla82xx_unprotect_flash(struct qla_hw_data *ha) } if (qla82xx_write_disable_flash(ha) != 0) - qla_printk(KERN_WARNING, ha, "Write disable failed\n"); + ql_log(ql_log_warn, vha, 0xb015, + "Write disable failed.\n"); done_unprotect: qla82xx_rom_unlock(ha); @@ -3025,10 +2594,12 @@ 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) { - qla_printk(KERN_WARNING, ha, "ROM Lock failed\n"); + ql_log(ql_log_warn, vha, 0xb016, + "ROM Lock failed.\n"); return ret; } @@ -3040,10 +2611,12 @@ qla82xx_protect_flash(struct qla_hw_data *ha) /* LOCK all sectors */ ret = qla82xx_write_status_reg(ha, val); if (ret < 0) - qla_printk(KERN_WARNING, ha, "Write status register failed\n"); + ql_log(ql_log_warn, vha, 0xb017, + "Write status register failed.\n"); if (qla82xx_write_disable_flash(ha) != 0) - qla_printk(KERN_WARNING, ha, "Write disable failed\n"); + ql_log(ql_log_warn, vha, 0xb018, + "Write disable failed.\n"); done_protect: qla82xx_rom_unlock(ha); return ret; @@ -3053,10 +2626,12 @@ 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) { - qla_printk(KERN_WARNING, ha, "ROM Lock failed\n"); + ql_log(ql_log_warn, vha, 0xb019, + "ROM Lock failed.\n"); return ret; } @@ -3066,8 +2641,8 @@ qla82xx_erase_sector(struct qla_hw_data *ha, int addr) qla82xx_wr_32(ha, QLA82XX_ROMUSB_ROM_INSTR_OPCODE, M25P_INSTR_SE); if (qla82xx_wait_rom_done(ha)) { - qla_printk(KERN_WARNING, ha, - "Error waiting for rom done\n"); + ql_log(ql_log_warn, vha, 0xb01a, + "Error waiting for rom done.\n"); ret = -1; goto done; } @@ -3110,10 +2685,10 @@ qla82xx_write_flash_data(struct scsi_qla_host *vha, uint32_t *dwptr, optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, &optrom_dma, GFP_KERNEL); if (!optrom) { - qla_printk(KERN_DEBUG, ha, - "Unable to allocate memory for optrom " - "burst write (%x KB).\n", - OPTROM_BURST_SIZE / 1024); + ql_log(ql_log_warn, vha, 0xb01b, + "Unable to allocate memory " + "for optrom burst write (%x KB).\n", + OPTROM_BURST_SIZE / 1024); } } @@ -3122,8 +2697,8 @@ qla82xx_write_flash_data(struct scsi_qla_host *vha, uint32_t *dwptr, ret = qla82xx_unprotect_flash(ha); if (ret) { - qla_printk(KERN_WARNING, ha, - "Unable to unprotect flash for update.\n"); + ql_log(ql_log_warn, vha, 0xb01c, + "Unable to unprotect flash for update.\n"); goto write_done; } @@ -3133,9 +2708,9 @@ qla82xx_write_flash_data(struct scsi_qla_host *vha, uint32_t *dwptr, ret = qla82xx_erase_sector(ha, faddr); if (ret) { - DEBUG9(qla_printk(KERN_ERR, ha, - "Unable to erase sector: " - "address=%x.\n", faddr)); + ql_log(ql_log_warn, vha, 0xb01d, + "Unable to erase sector: address=%x.\n", + faddr); break; } } @@ -3149,12 +2724,12 @@ qla82xx_write_flash_data(struct scsi_qla_host *vha, uint32_t *dwptr, (ha->flash_data_off | faddr), OPTROM_BURST_DWORDS); if (ret != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, + 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); - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0xb01f, "Reverting to slow-write.\n"); dma_free_coherent(&ha->pdev->dev, @@ -3171,16 +2746,16 @@ qla82xx_write_flash_data(struct scsi_qla_host *vha, uint32_t *dwptr, ret = qla82xx_write_flash_dword(ha, faddr, cpu_to_le32(*dwptr)); if (ret) { - DEBUG9(printk(KERN_DEBUG "%s(%ld) Unable to program" - "flash address=%x data=%x.\n", __func__, - ha->host_no, faddr, *dwptr)); + 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) - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0xb021, "Unable to protect flash after update.\n"); write_done: if (optrom) @@ -3210,9 +2785,9 @@ qla82xx_write_optrom_data(struct scsi_qla_host *vha, uint8_t *buf, } void -qla82xx_start_iocbs(srb_t *sp) +qla82xx_start_iocbs(scsi_qla_host_t *vha) { - struct qla_hw_data *ha = sp->fcport->vha->hw; + 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; @@ -3234,7 +2809,7 @@ qla82xx_start_iocbs(srb_t *sp) else { WRT_REG_DWORD((unsigned long __iomem *)ha->nxdb_wr_ptr, dbval); wmb(); - while (RD_REG_DWORD(ha->nxdb_rd_ptr) != dbval) { + while (RD_REG_DWORD((void __iomem *)ha->nxdb_rd_ptr) != dbval) { WRT_REG_DWORD((unsigned long __iomem *)ha->nxdb_wr_ptr, dbval); wmb(); @@ -3242,12 +2817,18 @@ qla82xx_start_iocbs(srb_t *sp) } } -void qla82xx_rom_lock_recovery(struct qla_hw_data *ha) +static void +qla82xx_rom_lock_recovery(struct qla_hw_data *ha) { - if (qla82xx_rom_lock(ha)) - /* Someone else is holding the lock. */ - qla_printk(KERN_INFO, ha, "Resetting rom_lock\n"); + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + uint32_t lock_owner = 0; + if (qla82xx_rom_lock(ha)) { + lock_owner = qla82xx_rd_32(ha, QLA82XX_ROM_LOCK_ID); + /* Someone else is holding the lock. */ + ql_log(ql_log_info, vha, 0xb022, + "Resetting rom_lock, Lock Owner %u.\n", lock_owner); + } /* * Either we got the lock, or someone * else died while holding it. @@ -3271,68 +2852,51 @@ static int qla82xx_device_bootstrap(scsi_qla_host_t *vha) { int rval = QLA_SUCCESS; - int i, timeout; + int i; uint32_t old_count, count; struct qla_hw_data *ha = vha->hw; - int need_reset = 0, peg_stuck = 1; + int need_reset = 0; 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) + if (ha->flags.isp82xx_fw_hung) 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; + old_count = qla82xx_rd_32(ha, QLA82XX_PEG_ALIVE_COUNTER); + for (i = 0; i < 10; i++) { + msleep(200); + count = qla82xx_rd_32(ha, QLA82XX_PEG_ALIVE_COUNTER); + if (count != old_count) { + rval = QLA_SUCCESS; + goto dev_ready; + } + } + qla82xx_rom_lock_recovery(ha); } - return rval; - -dev_initialize: /* set to DEV_INITIALIZING */ - qla_printk(KERN_INFO, ha, "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); + ql_log(ql_log_info, vha, 0x009e, + "HW State: INITIALIZING.\n"); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA8XXX_DEV_INITIALIZING); qla82xx_idc_unlock(ha); rval = qla82xx_start_firmware(vha); qla82xx_idc_lock(ha); if (rval != QLA_SUCCESS) { - qla_printk(KERN_INFO, ha, "HW State: FAILED\n"); + 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); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA8XXX_DEV_FAILED); return rval; } dev_ready: - qla_printk(KERN_INFO, ha, "HW State: READY\n"); - qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_READY); + ql_log(ql_log_info, vha, 0x00ae, + "HW State: READY.\n"); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA8XXX_DEV_READY); return QLA_SUCCESS; } @@ -3356,7 +2920,7 @@ qla82xx_need_qsnt_handler(scsi_qla_host_t *vha) if (vha->flags.online) { /*Block any further I/O and wait for pending cmnds to complete*/ - qla82xx_quiescent_state_cleanup(vha); + qla2x00_quiesce_io(vha); } /* Set the quiescence ready bit */ @@ -3376,15 +2940,14 @@ qla82xx_need_qsnt_handler(scsi_qla_host_t *vha) /* quiescence timeout, other functions didn't ack * changing the state to DEV_READY */ - qla_printk(KERN_INFO, ha, - "%s: QUIESCENT TIMEOUT\n", QLA2XXX_DRIVER_NAME); - qla_printk(KERN_INFO, ha, - "DRV_ACTIVE:%d DRV_STATE:%d\n", drv_active, - drv_state); + ql_log(ql_log_info, vha, 0xb023, + "%s : QUIESCENT TIMEOUT DRV_ACTIVE:%d " + "DRV_STATE:%d.\n", QLA2XXX_DRIVER_NAME, + drv_active, drv_state); qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, - QLA82XX_DEV_READY); - qla_printk(KERN_INFO, ha, - "HW State: DEV_READY\n"); + QLA8XXX_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); @@ -3403,9 +2966,10 @@ qla82xx_need_qsnt_handler(scsi_qla_host_t *vha) } 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) { - qla_printk(KERN_INFO, ha, "HW State: DEV_QUIESCENT\n"); - qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_QUIESCENT); + if (dev_state == QLA8XXX_DEV_NEED_QUIESCENT) { + ql_log(ql_log_info, vha, 0xb026, + "HW State: DEV_QUIESCENT.\n"); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA8XXX_DEV_QUIESCENT); } } @@ -3435,17 +2999,22 @@ qla82xx_wait_for_state_change(scsi_qla_host_t *vha, uint32_t curr_state) return dev_state; } -static void -qla82xx_dev_failed_handler(scsi_qla_host_t *vha) +void +qla8xxx_dev_failed_handler(scsi_qla_host_t *vha) { struct qla_hw_data *ha = vha->hw; /* Disable the board */ - qla_printk(KERN_INFO, ha, "Disabling the board\n"); + ql_log(ql_log_fatal, vha, 0x00b8, + "Disabling the board.\n"); - qla82xx_idc_lock(ha); - qla82xx_clear_drv_active(ha); - qla82xx_idc_unlock(ha); + if (IS_QLA82XX(ha)) { + qla82xx_clear_drv_active(ha); + qla82xx_idc_unlock(ha); + } else if (IS_QLA8044(ha)) { + qla8044_clear_drv_active(ha); + qla8044_idc_unlock(ha); + } /* Set DEV_FAILED flag to disable timer */ vha->device_flags |= DFLG_DEV_FAILED; @@ -3470,6 +3039,7 @@ 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]; @@ -3482,18 +3052,35 @@ qla82xx_need_reset_handler(scsi_qla_host_t *vha) qla82xx_idc_lock(ha); } - qla82xx_set_rst_ready(ha); + drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); + if (!ha->flags.nic_core_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); + reset_timeout = jiffies + (ha->fcoe_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); - while (drv_state != drv_active) { + 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 != QLA8XXX_DEV_INITIALIZING) { if (time_after_eq(jiffies, reset_timeout)) { - qla_printk(KERN_INFO, ha, - "%s: RESET TIMEOUT!\n", QLA2XXX_DRIVER_NAME); + ql_log(ql_log_warn, vha, 0x00b5, + "Reset timeout.\n"); break; } qla82xx_idc_unlock(ha); @@ -3501,20 +3088,81 @@ qla82xx_need_reset_handler(scsi_qla_host_t *vha) 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.nic_core_reset_owner) + drv_active &= active_mask; + dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); } - dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); - qla_printk(KERN_INFO, ha, "3:Device state is 0x%x = %s\n", dev_state, - dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown"); + 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) { - qla_printk(KERN_INFO, ha, "HW State: COLD/RE-INIT\n"); - qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_COLD); + if (dev_state != QLA8XXX_DEV_INITIALIZING && + dev_state != QLA8XXX_DEV_COLD) { + ql_log(ql_log_info, vha, 0x00b7, + "HW State: COLD/RE-INIT.\n"); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA8XXX_DEV_COLD); + qla82xx_set_rst_ready(ha); + if (ql2xmdenable) { + if (qla82xx_md_collect(vha)) + ql_log(ql_log_warn, vha, 0xb02c, + "Minidump not collected.\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) || + (ha->prev_minidump_failed)) { + ql_dbg(ql_dbg_p3p, vha, 0xb02d, + "Firmware version differs Previous version: %d:%d:%d - New version: %d:%d:%d, prev_minidump_failed: %d.\n", + fw_major_version, fw_minor_version, + fw_subminor_version, + ha->fw_major_version, + ha->fw_minor_version, + ha->fw_subminor_version, + ha->prev_minidump_failed); + /* 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; +} + + +static int qla82xx_check_fw_alive(scsi_qla_host_t *vha) { uint32_t fw_heartbeat_counter; @@ -3523,8 +3171,12 @@ qla82xx_check_fw_alive(scsi_qla_host_t *vha) 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) + 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 */ @@ -3535,6 +3187,9 @@ qla82xx_check_fw_alive(scsi_qla_host_t *vha) } 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; } @@ -3560,23 +3215,26 @@ qla82xx_device_state_handler(scsi_qla_host_t *vha) int loopcount = 0; qla82xx_idc_lock(ha); - if (!vha->flags.init_done) + if (!vha->flags.init_done) { qla82xx_set_drv_active(vha); + qla82xx_set_idc_version(vha); + } dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); old_dev_state = dev_state; - qla_printk(KERN_INFO, ha, "1:Device state is 0x%x = %s\n", dev_state, - dev_state < MAX_STATES ? qdev_state[dev_state] : "Unknown"); + 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); + dev_init_timeout = jiffies + (ha->fcoe_dev_init_timeout * HZ); while (1) { if (time_after_eq(jiffies, dev_init_timeout)) { - DEBUG(qla_printk(KERN_INFO, ha, - "%s: device init failed!\n", - QLA2XXX_DRIVER_NAME)); + ql_log(ql_log_fatal, vha, 0x009c, + "Device init failed.\n"); rval = QLA_FUNCTION_FAILED; break; } @@ -3586,52 +3244,59 @@ qla82xx_device_state_handler(scsi_qla_host_t *vha) old_dev_state = dev_state; } if (loopcount < 5) { - qla_printk(KERN_INFO, ha, - "2:Device state is 0x%x = %s\n", dev_state, - dev_state < MAX_STATES ? - qdev_state[dev_state] : "Unknown"); + 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: - goto exit; - case QLA82XX_DEV_COLD: + case QLA8XXX_DEV_READY: + ha->flags.nic_core_reset_owner = 0; + goto rel_lock; + case QLA8XXX_DEV_COLD: rval = qla82xx_device_bootstrap(vha); - goto exit; - case QLA82XX_DEV_INITIALIZING: + break; + case QLA8XXX_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); + case QLA8XXX_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); + (ha->fcoe_dev_init_timeout * HZ); break; - case QLA82XX_DEV_NEED_QUIESCENT: + case QLA8XXX_DEV_NEED_QUIESCENT: qla82xx_need_qsnt_handler(vha); /* Reset timeout value after quiescence handler */ - dev_init_timeout = jiffies + (ha->nx_dev_init_timeout\ + dev_init_timeout = jiffies + (ha->fcoe_dev_init_timeout\ * HZ); break; - case QLA82XX_DEV_QUIESCENT: + case QLA8XXX_DEV_QUIESCENT: /* Owner will exit and other will wait for the state * to get changed */ if (ha->flags.quiesce_owner) - goto exit; + goto rel_lock; 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\ + dev_init_timeout = jiffies + (ha->fcoe_dev_init_timeout\ * HZ); break; - case QLA82XX_DEV_FAILED: - qla82xx_dev_failed_handler(vha); + case QLA8XXX_DEV_FAILED: + qla8xxx_dev_failed_handler(vha); rval = QLA_FUNCTION_FAILED; goto exit; default: @@ -3641,44 +3306,102 @@ qla82xx_device_state_handler(scsi_qla_host_t *vha) } loopcount++; } -exit: +rel_lock: qla82xx_idc_unlock(ha); +exit: return rval; } +static int qla82xx_check_temp(scsi_qla_host_t *vha) +{ + uint32_t temp, temp_state, temp_val; + struct qla_hw_data *ha = vha->hw; + + temp = qla82xx_rd_32(ha, CRB_TEMP_STATE); + temp_state = qla82xx_get_temp_state(temp); + temp_val = qla82xx_get_temp_val(temp); + + if (temp_state == QLA82XX_TEMP_PANIC) { + ql_log(ql_log_warn, vha, 0x600e, + "Device temperature %d degrees C exceeds " + " maximum allowed. Hardware has been shut down.\n", + temp_val); + return 1; + } else if (temp_state == QLA82XX_TEMP_WARN) { + ql_log(ql_log_warn, vha, 0x600f, + "Device temperature %d degrees C exceeds " + "operating range. Immediate action needed.\n", + temp_val); + } + return 0; +} + +int qla82xx_read_temperature(scsi_qla_host_t *vha) +{ + uint32_t temp; + + temp = qla82xx_rd_32(vha->hw, CRB_TEMP_STATE); + return qla82xx_get_temp_val(temp); +} + +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_and_clear_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) { + if (!ha->flags.nic_core_reset_hdlr_active) { dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); - if (dev_state == QLA82XX_DEV_NEED_RESET && + if (qla82xx_check_temp(vha)) { + set_bit(ISP_UNRECOVERABLE, &vha->dpc_flags); + ha->flags.isp82xx_fw_hung = 1; + qla82xx_clear_pending_mbx(vha); + } else if (dev_state == QLA8XXX_DEV_NEED_RESET && !test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) { - qla_printk(KERN_WARNING, ha, - "scsi(%ld) %s: Adapter reset needed!\n", - vha->host_no, __func__); + 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 && + } else if (dev_state == QLA8XXX_DEV_NEED_QUIESCENT && !test_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags)) { - DEBUG(qla_printk(KERN_INFO, ha, - "scsi(%ld) %s - detected quiescence needed\n", - vha->host_no, __func__)); + ql_log(ql_log_warn, vha, 0x6002, + "Quiescent needed.\n"); set_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags); - qla2xxx_wake_dpc(vha); + } else if (dev_state == QLA8XXX_DEV_FAILED && + !test_bit(ISP_UNRECOVERABLE, &vha->dpc_flags) && + vha->flags.online == 1) { + ql_log(ql_log_warn, vha, 0xb055, + "Adapter state is failed. Offlining.\n"); + set_bit(ISP_UNRECOVERABLE, &vha->dpc_flags); + ha->flags.isp82xx_fw_hung = 1; + qla82xx_clear_pending_mbx(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); - qla_printk(KERN_INFO, ha, - "scsi(%ld): %s, Dumping hw/fw registers:\n " - " PEG_HALT_STATUS1: 0x%x, PEG_HALT_STATUS2: 0x%x,\n " - " PEG_NET_0_PC: 0x%x, PEG_NET_1_PC: 0x%x,\n " - " PEG_NET_2_PC: 0x%x, PEG_NET_3_PC: 0x%x,\n " - " PEG_NET_4_PC: 0x%x\n", - vha->host_no, __func__, halt_status, + 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), @@ -3690,28 +3413,23 @@ void qla82xx_watchdog(scsi_qla_host_t *vha) 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 { - qla_printk(KERN_INFO, ha, - "scsi(%ld): %s - detect abort needed\n", - vha->host_no, __func__); + 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; - if (ha->flags.mbox_busy) { - ha->flags.mbox_int = 1; - DEBUG2(qla_printk(KERN_ERR, ha, - "scsi(%ld) Due to fw hung, doing " - "premature completion of mbx " - "command\n", vha->host_no)); - if (test_bit(MBX_INTR_WAIT, - &ha->mbx_cmd_flags)) - complete(&ha->mbx_intr_comp); - } + ql_log(ql_log_warn, vha, 0x6007, "Firmware hung.\n"); + qla82xx_clear_pending_mbx(vha); } } } @@ -3719,11 +3437,51 @@ void qla82xx_watchdog(scsi_qla_host_t *vha) int qla82xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) { - int rval; - rval = qla82xx_device_state_handler(vha); + int rval = -1; + struct qla_hw_data *ha = vha->hw; + + if (IS_QLA82XX(ha)) + rval = qla82xx_device_state_handler(vha); + else if (IS_QLA8044(ha)) { + qla8044_idc_lock(ha); + /* Decide the reset ownership */ + qla83xx_reset_ownership(vha); + qla8044_idc_unlock(ha); + rval = qla8044_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 = 0; + + if (IS_QLA82XX(ha)) + dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); + else if (IS_QLA8044(ha)) + dev_state = qla8044_rd_direct(vha, QLA8044_CRB_DEV_STATE_INDEX); + + if (dev_state == QLA8XXX_DEV_READY) { + ql_log(ql_log_info, vha, 0xb02f, + "HW State: NEED RESET\n"); + if (IS_QLA82XX(ha)) { + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, + QLA8XXX_DEV_NEED_RESET); + ha->flags.nic_core_reset_owner = 1; + ql_dbg(ql_dbg_p3p, vha, 0xb030, + "reset_owner is 0x%x\n", ha->portnum); + } else if (IS_QLA8044(ha)) + qla8044_wr_direct(vha, QLA8044_CRB_DEV_STATE_INDEX, + QLA8XXX_DEV_NEED_RESET); + } 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. @@ -3737,31 +3495,29 @@ int qla82xx_load_risc(scsi_qla_host_t *vha, uint32_t *srisc_addr) int qla82xx_abort_isp(scsi_qla_host_t *vha) { - int rval; + int rval = -1; struct qla_hw_data *ha = vha->hw; - uint32_t dev_state; if (vha->device_flags & DFLG_DEV_FAILED) { - qla_printk(KERN_WARNING, ha, - "%s(%ld): Device in failed state, " - "Exiting.\n", __func__, vha->host_no); + ql_log(ql_log_warn, vha, 0x8024, + "Device in failed state, exiting.\n"); return QLA_SUCCESS; } - ha->flags.isp82xx_reset_hdlr_active = 1; + ha->flags.nic_core_reset_hdlr_active = 1; qla82xx_idc_lock(ha); - dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); - if (dev_state == QLA82XX_DEV_READY) { - qla_printk(KERN_INFO, ha, "HW State: NEED RESET\n"); - qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, - QLA82XX_DEV_NEED_RESET); - } else - qla_printk(KERN_INFO, ha, "HW State: %s\n", - dev_state < MAX_STATES ? - qdev_state[dev_state] : "Unknown"); + qla82xx_set_reset_owner(vha); qla82xx_idc_unlock(ha); - rval = qla82xx_device_state_handler(vha); + if (IS_QLA82XX(ha)) + rval = qla82xx_device_state_handler(vha); + else if (IS_QLA8044(ha)) { + qla8044_idc_lock(ha); + /* Decide the reset ownership */ + qla83xx_reset_ownership(vha); + qla8044_idc_unlock(ha); + rval = qla8044_device_state_handler(vha); + } qla82xx_idc_lock(ha); qla82xx_clear_rst_ready(ha); @@ -3769,7 +3525,7 @@ qla82xx_abort_isp(scsi_qla_host_t *vha) if (rval == QLA_SUCCESS) { ha->flags.isp82xx_fw_hung = 0; - ha->flags.isp82xx_reset_hdlr_active = 0; + ha->flags.nic_core_reset_hdlr_active = 0; qla82xx_restart_isp(vha); } @@ -3777,9 +3533,9 @@ qla82xx_abort_isp(scsi_qla_host_t *vha) vha->flags.online = 1; if (test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) { if (ha->isp_abort_cnt == 0) { - qla_printk(KERN_WARNING, ha, - "ISP error recovery failed - " - "board disabled\n"); + ql_log(ql_log_warn, vha, 0x8027, + "ISP error recover failed - board " + "disabled.\n"); /* * The next call disables the board * completely. @@ -3791,16 +3547,16 @@ qla82xx_abort_isp(scsi_qla_host_t *vha) rval = QLA_SUCCESS; } else { /* schedule another ISP abort */ ha->isp_abort_cnt--; - DEBUG(qla_printk(KERN_INFO, ha, - "qla%ld: ISP abort - retry remaining %d\n", - vha->host_no, 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; - DEBUG(qla_printk(KERN_INFO, ha, - "(%ld): ISP error recovery - retrying (%d) " - "more times\n", vha->host_no, ha->isp_abort_cnt)); + 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; } @@ -3872,8 +3628,8 @@ int qla2x00_wait_for_fcoe_ctx_reset(scsi_qla_host_t *vha) break; } } - DEBUG2(printk(KERN_INFO - "%s status=%d\n", __func__, status)); + ql_dbg(ql_dbg_p3p, vha, 0xb027, + "%s: status=%d.\n", __func__, status); return status; } @@ -3881,7 +3637,7 @@ int qla2x00_wait_for_fcoe_ctx_reset(scsi_qla_host_t *vha) void qla82xx_chip_reset_cleanup(scsi_qla_host_t *vha) { - int i; + int i, fw_state = 0; unsigned long flags; struct qla_hw_data *ha = vha->hw; @@ -3892,16 +3648,20 @@ qla82xx_chip_reset_cleanup(scsi_qla_host_t *vha) if (!ha->flags.isp82xx_fw_hung) { for (i = 0; i < 2; i++) { msleep(1000); - if (qla82xx_check_fw_alive(vha)) { + if (IS_QLA82XX(ha)) + fw_state = qla82xx_check_fw_alive(vha); + else if (IS_QLA8044(ha)) + fw_state = qla8044_check_fw_alive(vha); + if (fw_state) { ha->flags.isp82xx_fw_hung = 1; - if (ha->flags.mbox_busy) { - ha->flags.mbox_int = 1; - complete(&ha->mbx_intr_comp); - } + 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) { @@ -3914,21 +3674,23 @@ qla82xx_chip_reset_cleanup(scsi_qla_host_t *vha) req = ha->req_q_map[que]; if (!req) continue; - for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { + for (cnt = 1; cnt < req->num_outstanding_cmds; cnt++) { sp = req->outstanding_cmds[cnt]; if (sp) { - if (!sp->ctx || - (sp->flags & SRB_FCP_CMND_DMA_VALID)) { + if ((!sp->u.scmd.ctx || + (sp->flags & + SRB_FCP_CMND_DMA_VALID)) && + !ha->flags.isp82xx_fw_hung) { spin_unlock_irqrestore( &ha->hardware_lock, flags); if (ha->isp_ops->abort_command(sp)) { - qla_printk(KERN_INFO, ha, - "scsi(%ld): mbx abort command failed in %s\n", - vha->host_no, __func__); + ql_log(ql_log_info, vha, + 0x00b1, + "mbx abort failed.\n"); } else { - qla_printk(KERN_INFO, ha, - "scsi(%ld): mbx abort command success in %s\n", - vha->host_no, __func__); + ql_log(ql_log_info, vha, + 0x00b2, + "mbx abort success.\n"); } spin_lock_irqsave(&ha->hardware_lock, flags); } @@ -3940,8 +3702,817 @@ qla82xx_chip_reset_cleanup(scsi_qla_host_t *vha) /* Wait for pending cmds (physical and virtual) to complete */ if (!qla2x00_eh_wait_for_pending_commands(vha, 0, 0, WAIT_HOST) == QLA_SUCCESS) { - DEBUG2(qla_printk(KERN_INFO, ha, - "Done wait for pending commands\n")); + 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 __iomem *) + (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 aligned\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; +} + +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 (ha->flags.isp82xx_no_md_cap) { + ql_log(ql_log_warn, vha, 0xb054, + "Forced reset from application, " + "ignore minidump capture\n"); + ha->flags.isp82xx_no_md_cap = 0; + 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 = NULL; + } + + /* 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 = NULL; + } +} + +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 */ + if (IS_QLA8044(ha)) + rval = qla8044_md_get_template(vha); + else + 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 = NULL; + } + + } + } +} + +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; +} + +void +qla82xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) +{ + struct qla_hw_data *ha = vha->hw; + + if (!ha->allow_cna_fw_dump) + return; + + scsi_block_requests(vha->host); + ha->flags.isp82xx_no_md_cap = 1; + qla82xx_idc_lock(ha); + qla82xx_set_reset_owner(vha); + qla82xx_idc_unlock(ha); + qla2x00_wait_for_chip_reset(vha); + scsi_unblock_requests(vha->host); } diff --git a/drivers/scsi/qla2xxx/qla_nx.h b/drivers/scsi/qla2xxx/qla_nx.h index 8a21832c669..59c477883a7 100644 --- a/drivers/scsi/qla2xxx/qla_nx.h +++ b/drivers/scsi/qla2xxx/qla_nx.h @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -26,6 +26,7 @@ #define CRB_RCVPEG_STATE QLA82XX_REG(0x13c) #define BOOT_LOADER_DIMM_STATUS QLA82XX_REG(0x54) #define CRB_DMA_SHIFT QLA82XX_REG(0xcc) +#define CRB_TEMP_STATE QLA82XX_REG(0x1b4) #define QLA82XX_DMA_SHIFT_VALUE 0x55555555 #define QLA82XX_HW_H0_CH_HUB_ADR 0x05 @@ -332,9 +333,6 @@ #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) @@ -484,8 +482,6 @@ #define QLA82XX_ADDR_OCM1 (0x0000000200400000ULL) #define QLA82XX_ADDR_OCM1_MAX (0x00000002004fffffULL) #define QLA82XX_ADDR_QDR_NET (0x0000000300000000ULL) - -#define QLA82XX_P2_ADDR_QDR_NET_MAX (0x00000003001fffffULL) #define QLA82XX_P3_ADDR_QDR_NET_MAX (0x0000000303ffffffULL) #define QLA82XX_PCI_CRBSPACE (unsigned long)0x06000000 @@ -543,14 +539,15 @@ #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 QLA8XXX_DEV_COLD 1 +#define QLA8XXX_DEV_INITIALIZING 2 +#define QLA8XXX_DEV_READY 3 +#define QLA8XXX_DEV_NEED_RESET 4 +#define QLA8XXX_DEV_NEED_QUIESCENT 5 +#define QLA8XXX_DEV_FAILED 6 +#define QLA8XXX_DEV_QUIESCENT 7 #define MAX_STATES 8 /* Increment if new state added */ +#define QLA8XXX_BAD_VALUE 0xbad0bad0 #define QLA82XX_IDC_VERSION 1 #define QLA82XX_ROM_DEV_INIT_TIMEOUT 30 @@ -563,7 +560,6 @@ #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) @@ -590,6 +586,7 @@ * The PCI VendorID and DeviceID for our board. */ #define PCI_DEVICE_ID_QLOGIC_ISP8021 0x8021 +#define PCI_DEVICE_ID_QLOGIC_ISP8044 0x8044 #define QLA82XX_MSIX_TBL_SPACE 8192 #define QLA82XX_PCI_REG_MSIX_TBL 0x44 @@ -890,13 +887,15 @@ struct ct6_dsd { }; #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 FLT_REG_VPD_8XXX 0x81 #define FA_VPD_SIZE_82XX 0x400 @@ -922,4 +921,282 @@ struct ct6_dsd { #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 +#define QLA8044_POLLRD 35 +#define QLA8044_RDMUX2 36 +#define QLA8044_L1DTG 8 +#define QLA8044_L1ITG 9 +#define QLA8044_POLLRDMWR 37 + +/* + * 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 + +#define qla82xx_get_temp_val(x) ((x) >> 16) +#define qla82xx_get_temp_val1(x) ((x) && 0x0000FFFF) +#define qla82xx_get_temp_state(x) ((x) & 0xffff) +#define qla82xx_encode_temp(val, state) (((val) << 16) | (state)) + +/* + * Temperature control. + */ +enum { + QLA82XX_TEMP_NORMAL = 0x1, /* Normal operating range */ + QLA82XX_TEMP_WARN, /* Sound alert, temperature getting high */ + QLA82XX_TEMP_PANIC /* Fatal error, hardware has shut down. */ +}; + +#define LEG_INTR_PTR_OFFSET 0x38C0 +#define LEG_INTR_TRIG_OFFSET 0x38C4 +#define LEG_INTR_MASK_OFFSET 0x38C8 #endif diff --git a/drivers/scsi/qla2xxx/qla_nx2.c b/drivers/scsi/qla2xxx/qla_nx2.c new file mode 100644 index 00000000000..da9e3902f21 --- /dev/null +++ b/drivers/scsi/qla2xxx/qla_nx2.c @@ -0,0 +1,4079 @@ +/* + * QLogic Fibre Channel HBA Driver + * Copyright (c) 2003-2014 QLogic Corporation + * + * See LICENSE.qla2xxx for copyright and licensing details. + */ + +#include <linux/vmalloc.h> +#include <linux/delay.h> + +#include "qla_def.h" +#include "qla_gbl.h" + +#include <linux/delay.h> + +#define TIMEOUT_100_MS 100 + +/* 8044 Flash Read/Write functions */ +uint32_t +qla8044_rd_reg(struct qla_hw_data *ha, ulong addr) +{ + return readl((void __iomem *) (ha->nx_pcibase + addr)); +} + +void +qla8044_wr_reg(struct qla_hw_data *ha, ulong addr, uint32_t val) +{ + writel(val, (void __iomem *)((ha)->nx_pcibase + addr)); +} + +int +qla8044_rd_direct(struct scsi_qla_host *vha, + const uint32_t crb_reg) +{ + struct qla_hw_data *ha = vha->hw; + + if (crb_reg < CRB_REG_INDEX_MAX) + return qla8044_rd_reg(ha, qla8044_reg_tbl[crb_reg]); + else + return QLA_FUNCTION_FAILED; +} + +void +qla8044_wr_direct(struct scsi_qla_host *vha, + const uint32_t crb_reg, + const uint32_t value) +{ + struct qla_hw_data *ha = vha->hw; + + if (crb_reg < CRB_REG_INDEX_MAX) + qla8044_wr_reg(ha, qla8044_reg_tbl[crb_reg], value); +} + +static int +qla8044_set_win_base(scsi_qla_host_t *vha, uint32_t addr) +{ + uint32_t val; + int ret_val = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + + qla8044_wr_reg(ha, QLA8044_CRB_WIN_FUNC(ha->portnum), addr); + val = qla8044_rd_reg(ha, QLA8044_CRB_WIN_FUNC(ha->portnum)); + + if (val != addr) { + ql_log(ql_log_warn, vha, 0xb087, + "%s: Failed to set register window : " + "addr written 0x%x, read 0x%x!\n", + __func__, addr, val); + ret_val = QLA_FUNCTION_FAILED; + } + return ret_val; +} + +static int +qla8044_rd_reg_indirect(scsi_qla_host_t *vha, uint32_t addr, uint32_t *data) +{ + int ret_val = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + + ret_val = qla8044_set_win_base(vha, addr); + if (!ret_val) + *data = qla8044_rd_reg(ha, QLA8044_WILDCARD); + else + ql_log(ql_log_warn, vha, 0xb088, + "%s: failed read of addr 0x%x!\n", __func__, addr); + return ret_val; +} + +static int +qla8044_wr_reg_indirect(scsi_qla_host_t *vha, uint32_t addr, uint32_t data) +{ + int ret_val = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + + ret_val = qla8044_set_win_base(vha, addr); + if (!ret_val) + qla8044_wr_reg(ha, QLA8044_WILDCARD, data); + else + ql_log(ql_log_warn, vha, 0xb089, + "%s: failed wrt to addr 0x%x, data 0x%x\n", + __func__, addr, data); + return ret_val; +} + +/* + * qla8044_read_write_crb_reg - Read from raddr and write value to waddr. + * + * @ha : Pointer to adapter structure + * @raddr : CRB address to read from + * @waddr : CRB address to write to + * + */ +static void +qla8044_read_write_crb_reg(struct scsi_qla_host *vha, + uint32_t raddr, uint32_t waddr) +{ + uint32_t value; + + qla8044_rd_reg_indirect(vha, raddr, &value); + qla8044_wr_reg_indirect(vha, waddr, value); +} + +static int +qla8044_poll_wait_for_ready(struct scsi_qla_host *vha, uint32_t addr1, + uint32_t mask) +{ + unsigned long timeout; + uint32_t temp; + + /* jiffies after 100ms */ + timeout = jiffies + msecs_to_jiffies(TIMEOUT_100_MS); + do { + qla8044_rd_reg_indirect(vha, addr1, &temp); + if ((temp & mask) != 0) + break; + if (time_after_eq(jiffies, timeout)) { + ql_log(ql_log_warn, vha, 0xb151, + "Error in processing rdmdio entry\n"); + return -1; + } + } while (1); + + return 0; +} + +static uint32_t +qla8044_ipmdio_rd_reg(struct scsi_qla_host *vha, + uint32_t addr1, uint32_t addr3, uint32_t mask, uint32_t addr) +{ + uint32_t temp; + int ret = 0; + + ret = qla8044_poll_wait_for_ready(vha, addr1, mask); + if (ret == -1) + return -1; + + temp = (0x40000000 | addr); + qla8044_wr_reg_indirect(vha, addr1, temp); + + ret = qla8044_poll_wait_for_ready(vha, addr1, mask); + if (ret == -1) + return 0; + + qla8044_rd_reg_indirect(vha, addr3, &ret); + + return ret; +} + + +static int +qla8044_poll_wait_ipmdio_bus_idle(struct scsi_qla_host *vha, + uint32_t addr1, uint32_t addr2, uint32_t addr3, uint32_t mask) +{ + unsigned long timeout; + uint32_t temp; + + /* jiffies after 100 msecs */ + timeout = jiffies + msecs_to_jiffies(TIMEOUT_100_MS); + do { + temp = qla8044_ipmdio_rd_reg(vha, addr1, addr3, mask, addr2); + if ((temp & 0x1) != 1) + break; + if (time_after_eq(jiffies, timeout)) { + ql_log(ql_log_warn, vha, 0xb152, + "Error in processing mdiobus idle\n"); + return -1; + } + } while (1); + + return 0; +} + +static int +qla8044_ipmdio_wr_reg(struct scsi_qla_host *vha, uint32_t addr1, + uint32_t addr3, uint32_t mask, uint32_t addr, uint32_t value) +{ + int ret = 0; + + ret = qla8044_poll_wait_for_ready(vha, addr1, mask); + if (ret == -1) + return -1; + + qla8044_wr_reg_indirect(vha, addr3, value); + qla8044_wr_reg_indirect(vha, addr1, addr); + + ret = qla8044_poll_wait_for_ready(vha, addr1, mask); + if (ret == -1) + return -1; + + return 0; +} +/* + * qla8044_rmw_crb_reg - Read value from raddr, AND with test_mask, + * Shift Left,Right/OR/XOR with values RMW header and write value to waddr. + * + * @vha : Pointer to adapter structure + * @raddr : CRB address to read from + * @waddr : CRB address to write to + * @p_rmw_hdr : header with shift/or/xor values. + * + */ +static void +qla8044_rmw_crb_reg(struct scsi_qla_host *vha, + uint32_t raddr, uint32_t waddr, struct qla8044_rmw *p_rmw_hdr) +{ + uint32_t value; + + if (p_rmw_hdr->index_a) + value = vha->reset_tmplt.array[p_rmw_hdr->index_a]; + else + qla8044_rd_reg_indirect(vha, raddr, &value); + value &= p_rmw_hdr->test_mask; + value <<= p_rmw_hdr->shl; + value >>= p_rmw_hdr->shr; + value |= p_rmw_hdr->or_value; + value ^= p_rmw_hdr->xor_value; + qla8044_wr_reg_indirect(vha, waddr, value); + return; +} + +inline void +qla8044_set_qsnt_ready(struct scsi_qla_host *vha) +{ + uint32_t qsnt_state; + struct qla_hw_data *ha = vha->hw; + + qsnt_state = qla8044_rd_direct(vha, QLA8044_CRB_DRV_STATE_INDEX); + qsnt_state |= (1 << ha->portnum); + qla8044_wr_direct(vha, QLA8044_CRB_DRV_STATE_INDEX, qsnt_state); + ql_log(ql_log_info, vha, 0xb08e, "%s(%ld): qsnt_state: 0x%08x\n", + __func__, vha->host_no, qsnt_state); +} + +void +qla8044_clear_qsnt_ready(struct scsi_qla_host *vha) +{ + uint32_t qsnt_state; + struct qla_hw_data *ha = vha->hw; + + qsnt_state = qla8044_rd_direct(vha, QLA8044_CRB_DRV_STATE_INDEX); + qsnt_state &= ~(1 << ha->portnum); + qla8044_wr_direct(vha, QLA8044_CRB_DRV_STATE_INDEX, qsnt_state); + ql_log(ql_log_info, vha, 0xb08f, "%s(%ld): qsnt_state: 0x%08x\n", + __func__, vha->host_no, qsnt_state); +} + +/** + * + * qla8044_lock_recovery - Recovers the idc_lock. + * @ha : Pointer to adapter structure + * + * Lock Recovery Register + * 5-2 Lock recovery owner: Function ID of driver doing lock recovery, + * valid if bits 1..0 are set by driver doing lock recovery. + * 1-0 1 - Driver intends to force unlock the IDC lock. + * 2 - Driver is moving forward to unlock the IDC lock. Driver clears + * this field after force unlocking the IDC lock. + * + * Lock Recovery process + * a. Read the IDC_LOCK_RECOVERY register. If the value in bits 1..0 is + * greater than 0, then wait for the other driver to unlock otherwise + * move to the next step. + * b. Indicate intent to force-unlock by writing 1h to the IDC_LOCK_RECOVERY + * register bits 1..0 and also set the function# in bits 5..2. + * c. Read the IDC_LOCK_RECOVERY register again after a delay of 200ms. + * Wait for the other driver to perform lock recovery if the function + * number in bits 5..2 has changed, otherwise move to the next step. + * d. Write a value of 2h to the IDC_LOCK_RECOVERY register bits 1..0 + * leaving your function# in bits 5..2. + * e. Force unlock using the DRIVER_UNLOCK register and immediately clear + * the IDC_LOCK_RECOVERY bits 5..0 by writing 0. + **/ +static int +qla8044_lock_recovery(struct scsi_qla_host *vha) +{ + uint32_t lock = 0, lockid; + struct qla_hw_data *ha = vha->hw; + + lockid = qla8044_rd_reg(ha, QLA8044_DRV_LOCKRECOVERY); + + /* Check for other Recovery in progress, go wait */ + if ((lockid & IDC_LOCK_RECOVERY_STATE_MASK) != 0) + return QLA_FUNCTION_FAILED; + + /* Intent to Recover */ + qla8044_wr_reg(ha, QLA8044_DRV_LOCKRECOVERY, + (ha->portnum << + IDC_LOCK_RECOVERY_STATE_SHIFT_BITS) | INTENT_TO_RECOVER); + msleep(200); + + /* Check Intent to Recover is advertised */ + lockid = qla8044_rd_reg(ha, QLA8044_DRV_LOCKRECOVERY); + if ((lockid & IDC_LOCK_RECOVERY_OWNER_MASK) != (ha->portnum << + IDC_LOCK_RECOVERY_STATE_SHIFT_BITS)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_p3p, vha, 0xb08B, "%s:%d: IDC Lock recovery initiated\n" + , __func__, ha->portnum); + + /* Proceed to Recover */ + qla8044_wr_reg(ha, QLA8044_DRV_LOCKRECOVERY, + (ha->portnum << IDC_LOCK_RECOVERY_STATE_SHIFT_BITS) | + PROCEED_TO_RECOVER); + + /* Force Unlock() */ + qla8044_wr_reg(ha, QLA8044_DRV_LOCK_ID, 0xFF); + qla8044_rd_reg(ha, QLA8044_DRV_UNLOCK); + + /* Clear bits 0-5 in IDC_RECOVERY register*/ + qla8044_wr_reg(ha, QLA8044_DRV_LOCKRECOVERY, 0); + + /* Get lock() */ + lock = qla8044_rd_reg(ha, QLA8044_DRV_LOCK); + if (lock) { + lockid = qla8044_rd_reg(ha, QLA8044_DRV_LOCK_ID); + lockid = ((lockid + (1 << 8)) & ~0xFF) | ha->portnum; + qla8044_wr_reg(ha, QLA8044_DRV_LOCK_ID, lockid); + return QLA_SUCCESS; + } else + return QLA_FUNCTION_FAILED; +} + +int +qla8044_idc_lock(struct qla_hw_data *ha) +{ + uint32_t ret_val = QLA_SUCCESS, timeout = 0, status = 0; + uint32_t lock_id, lock_cnt, func_num, tmo_owner = 0, first_owner = 0; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + + while (status == 0) { + /* acquire semaphore5 from PCI HW block */ + status = qla8044_rd_reg(ha, QLA8044_DRV_LOCK); + + if (status) { + /* Increment Counter (8-31) and update func_num (0-7) on + * getting a successful lock */ + lock_id = qla8044_rd_reg(ha, QLA8044_DRV_LOCK_ID); + lock_id = ((lock_id + (1 << 8)) & ~0xFF) | ha->portnum; + qla8044_wr_reg(ha, QLA8044_DRV_LOCK_ID, lock_id); + break; + } + + if (timeout == 0) + first_owner = qla8044_rd_reg(ha, QLA8044_DRV_LOCK_ID); + + if (++timeout >= + (QLA8044_DRV_LOCK_TIMEOUT / QLA8044_DRV_LOCK_MSLEEP)) { + tmo_owner = qla8044_rd_reg(ha, QLA8044_DRV_LOCK_ID); + func_num = tmo_owner & 0xFF; + lock_cnt = tmo_owner >> 8; + ql_log(ql_log_warn, vha, 0xb114, + "%s: Lock by func %d failed after 2s, lock held " + "by func %d, lock count %d, first_owner %d\n", + __func__, ha->portnum, func_num, lock_cnt, + (first_owner & 0xFF)); + if (first_owner != tmo_owner) { + /* Some other driver got lock, + * OR same driver got lock again (counter + * value changed), when we were waiting for + * lock. Retry for another 2 sec */ + ql_dbg(ql_dbg_p3p, vha, 0xb115, + "%s: %d: IDC lock failed\n", + __func__, ha->portnum); + timeout = 0; + } else { + /* Same driver holding lock > 2sec. + * Force Recovery */ + if (qla8044_lock_recovery(vha) == QLA_SUCCESS) { + /* Recovered and got lock */ + ret_val = QLA_SUCCESS; + ql_dbg(ql_dbg_p3p, vha, 0xb116, + "%s:IDC lock Recovery by %d" + "successful...\n", __func__, + ha->portnum); + } + /* Recovery Failed, some other function + * has the lock, wait for 2secs + * and retry + */ + ql_dbg(ql_dbg_p3p, vha, 0xb08a, + "%s: IDC lock Recovery by %d " + "failed, Retrying timout\n", __func__, + ha->portnum); + timeout = 0; + } + } + msleep(QLA8044_DRV_LOCK_MSLEEP); + } + return ret_val; +} + +void +qla8044_idc_unlock(struct qla_hw_data *ha) +{ + int id; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + + id = qla8044_rd_reg(ha, QLA8044_DRV_LOCK_ID); + + if ((id & 0xFF) != ha->portnum) { + ql_log(ql_log_warn, vha, 0xb118, + "%s: IDC Unlock by %d failed, lock owner is %d!\n", + __func__, ha->portnum, (id & 0xFF)); + return; + } + + /* Keep lock counter value, update the ha->func_num to 0xFF */ + qla8044_wr_reg(ha, QLA8044_DRV_LOCK_ID, (id | 0xFF)); + qla8044_rd_reg(ha, QLA8044_DRV_UNLOCK); +} + +/* 8044 Flash Lock/Unlock functions */ +static int +qla8044_flash_lock(scsi_qla_host_t *vha) +{ + int lock_owner; + int timeout = 0; + uint32_t lock_status = 0; + int ret_val = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + + while (lock_status == 0) { + lock_status = qla8044_rd_reg(ha, QLA8044_FLASH_LOCK); + if (lock_status) + break; + + if (++timeout >= QLA8044_FLASH_LOCK_TIMEOUT / 20) { + lock_owner = qla8044_rd_reg(ha, + QLA8044_FLASH_LOCK_ID); + ql_log(ql_log_warn, vha, 0xb113, + "%s: Simultaneous flash access by following ports, active port = %d: accessing port = %d", + __func__, ha->portnum, lock_owner); + ret_val = QLA_FUNCTION_FAILED; + break; + } + msleep(20); + } + qla8044_wr_reg(ha, QLA8044_FLASH_LOCK_ID, ha->portnum); + return ret_val; +} + +static void +qla8044_flash_unlock(scsi_qla_host_t *vha) +{ + int ret_val; + struct qla_hw_data *ha = vha->hw; + + /* Reading FLASH_UNLOCK register unlocks the Flash */ + qla8044_wr_reg(ha, QLA8044_FLASH_LOCK_ID, 0xFF); + ret_val = qla8044_rd_reg(ha, QLA8044_FLASH_UNLOCK); +} + + +static +void qla8044_flash_lock_recovery(struct scsi_qla_host *vha) +{ + + if (qla8044_flash_lock(vha)) { + /* Someone else is holding the lock. */ + ql_log(ql_log_warn, vha, 0xb120, "Resetting flash_lock\n"); + } + + /* + * Either we got the lock, or someone + * else died while holding it. + * In either case, unlock. + */ + qla8044_flash_unlock(vha); +} + +/* + * Address and length are byte address + */ +static int +qla8044_read_flash_data(scsi_qla_host_t *vha, uint8_t *p_data, + uint32_t flash_addr, int u32_word_count) +{ + int i, ret_val = QLA_SUCCESS; + uint32_t u32_word; + + if (qla8044_flash_lock(vha) != QLA_SUCCESS) { + ret_val = QLA_FUNCTION_FAILED; + goto exit_lock_error; + } + + if (flash_addr & 0x03) { + ql_log(ql_log_warn, vha, 0xb117, + "%s: Illegal addr = 0x%x\n", __func__, flash_addr); + ret_val = QLA_FUNCTION_FAILED; + goto exit_flash_read; + } + + for (i = 0; i < u32_word_count; i++) { + if (qla8044_wr_reg_indirect(vha, QLA8044_FLASH_DIRECT_WINDOW, + (flash_addr & 0xFFFF0000))) { + ql_log(ql_log_warn, vha, 0xb119, + "%s: failed to write addr 0x%x to " + "FLASH_DIRECT_WINDOW\n! ", + __func__, flash_addr); + ret_val = QLA_FUNCTION_FAILED; + goto exit_flash_read; + } + + ret_val = qla8044_rd_reg_indirect(vha, + QLA8044_FLASH_DIRECT_DATA(flash_addr), + &u32_word); + if (ret_val != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0xb08c, + "%s: failed to read addr 0x%x!\n", + __func__, flash_addr); + goto exit_flash_read; + } + + *(uint32_t *)p_data = u32_word; + p_data = p_data + 4; + flash_addr = flash_addr + 4; + } + +exit_flash_read: + qla8044_flash_unlock(vha); + +exit_lock_error: + return ret_val; +} + +/* + * Address and length are byte address + */ +uint8_t * +qla8044_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf, + uint32_t offset, uint32_t length) +{ + scsi_block_requests(vha->host); + if (qla8044_read_flash_data(vha, (uint8_t *)buf, offset, length / 4) + != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0xb08d, + "%s: Failed to read from flash\n", + __func__); + } + scsi_unblock_requests(vha->host); + return buf; +} + +inline int +qla8044_need_reset(struct scsi_qla_host *vha) +{ + uint32_t drv_state, drv_active; + int rval; + struct qla_hw_data *ha = vha->hw; + + drv_active = qla8044_rd_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX); + drv_state = qla8044_rd_direct(vha, QLA8044_CRB_DRV_STATE_INDEX); + + rval = drv_state & (1 << ha->portnum); + + if (ha->flags.eeh_busy && drv_active) + rval = 1; + return rval; +} + +/* + * qla8044_write_list - Write the value (p_entry->arg2) to address specified + * by p_entry->arg1 for all entries in header with delay of p_hdr->delay between + * entries. + * + * @vha : Pointer to adapter structure + * @p_hdr : reset_entry header for WRITE_LIST opcode. + * + */ +static void +qla8044_write_list(struct scsi_qla_host *vha, + struct qla8044_reset_entry_hdr *p_hdr) +{ + struct qla8044_entry *p_entry; + uint32_t i; + + p_entry = (struct qla8044_entry *)((char *)p_hdr + + sizeof(struct qla8044_reset_entry_hdr)); + + for (i = 0; i < p_hdr->count; i++, p_entry++) { + qla8044_wr_reg_indirect(vha, p_entry->arg1, p_entry->arg2); + if (p_hdr->delay) + udelay((uint32_t)(p_hdr->delay)); + } +} + +/* + * qla8044_read_write_list - Read from address specified by p_entry->arg1, + * write value read to address specified by p_entry->arg2, for all entries in + * header with delay of p_hdr->delay between entries. + * + * @vha : Pointer to adapter structure + * @p_hdr : reset_entry header for READ_WRITE_LIST opcode. + * + */ +static void +qla8044_read_write_list(struct scsi_qla_host *vha, + struct qla8044_reset_entry_hdr *p_hdr) +{ + struct qla8044_entry *p_entry; + uint32_t i; + + p_entry = (struct qla8044_entry *)((char *)p_hdr + + sizeof(struct qla8044_reset_entry_hdr)); + + for (i = 0; i < p_hdr->count; i++, p_entry++) { + qla8044_read_write_crb_reg(vha, p_entry->arg1, + p_entry->arg2); + if (p_hdr->delay) + udelay((uint32_t)(p_hdr->delay)); + } +} + +/* + * qla8044_poll_reg - Poll the given CRB addr for duration msecs till + * value read ANDed with test_mask is equal to test_result. + * + * @ha : Pointer to adapter structure + * @addr : CRB register address + * @duration : Poll for total of "duration" msecs + * @test_mask : Mask value read with "test_mask" + * @test_result : Compare (value&test_mask) with test_result. + * + * Return Value - QLA_SUCCESS/QLA_FUNCTION_FAILED + */ +static int +qla8044_poll_reg(struct scsi_qla_host *vha, uint32_t addr, + int duration, uint32_t test_mask, uint32_t test_result) +{ + uint32_t value; + int timeout_error; + uint8_t retries; + int ret_val = QLA_SUCCESS; + + ret_val = qla8044_rd_reg_indirect(vha, addr, &value); + if (ret_val == QLA_FUNCTION_FAILED) { + timeout_error = 1; + goto exit_poll_reg; + } + + /* poll every 1/10 of the total duration */ + retries = duration/10; + + do { + if ((value & test_mask) != test_result) { + timeout_error = 1; + msleep(duration/10); + ret_val = qla8044_rd_reg_indirect(vha, addr, &value); + if (ret_val == QLA_FUNCTION_FAILED) { + timeout_error = 1; + goto exit_poll_reg; + } + } else { + timeout_error = 0; + break; + } + } while (retries--); + +exit_poll_reg: + if (timeout_error) { + vha->reset_tmplt.seq_error++; + ql_log(ql_log_fatal, vha, 0xb090, + "%s: Poll Failed: 0x%08x 0x%08x 0x%08x\n", + __func__, value, test_mask, test_result); + } + + return timeout_error; +} + +/* + * qla8044_poll_list - For all entries in the POLL_LIST header, poll read CRB + * register specified by p_entry->arg1 and compare (value AND test_mask) with + * test_result to validate it. Wait for p_hdr->delay between processing entries. + * + * @ha : Pointer to adapter structure + * @p_hdr : reset_entry header for POLL_LIST opcode. + * + */ +static void +qla8044_poll_list(struct scsi_qla_host *vha, + struct qla8044_reset_entry_hdr *p_hdr) +{ + long delay; + struct qla8044_entry *p_entry; + struct qla8044_poll *p_poll; + uint32_t i; + uint32_t value; + + p_poll = (struct qla8044_poll *) + ((char *)p_hdr + sizeof(struct qla8044_reset_entry_hdr)); + + /* Entries start after 8 byte qla8044_poll, poll header contains + * the test_mask, test_value. + */ + p_entry = (struct qla8044_entry *)((char *)p_poll + + sizeof(struct qla8044_poll)); + + delay = (long)p_hdr->delay; + + if (!delay) { + for (i = 0; i < p_hdr->count; i++, p_entry++) + qla8044_poll_reg(vha, p_entry->arg1, + delay, p_poll->test_mask, p_poll->test_value); + } else { + for (i = 0; i < p_hdr->count; i++, p_entry++) { + if (delay) { + if (qla8044_poll_reg(vha, + p_entry->arg1, delay, + p_poll->test_mask, + p_poll->test_value)) { + /*If + * (data_read&test_mask != test_value) + * read TIMEOUT_ADDR (arg1) and + * ADDR (arg2) registers + */ + qla8044_rd_reg_indirect(vha, + p_entry->arg1, &value); + qla8044_rd_reg_indirect(vha, + p_entry->arg2, &value); + } + } + } + } +} + +/* + * qla8044_poll_write_list - Write dr_value, ar_value to dr_addr/ar_addr, + * read ar_addr, if (value& test_mask != test_mask) re-read till timeout + * expires. + * + * @vha : Pointer to adapter structure + * @p_hdr : reset entry header for POLL_WRITE_LIST opcode. + * + */ +static void +qla8044_poll_write_list(struct scsi_qla_host *vha, + struct qla8044_reset_entry_hdr *p_hdr) +{ + long delay; + struct qla8044_quad_entry *p_entry; + struct qla8044_poll *p_poll; + uint32_t i; + + p_poll = (struct qla8044_poll *)((char *)p_hdr + + sizeof(struct qla8044_reset_entry_hdr)); + + p_entry = (struct qla8044_quad_entry *)((char *)p_poll + + sizeof(struct qla8044_poll)); + + delay = (long)p_hdr->delay; + + for (i = 0; i < p_hdr->count; i++, p_entry++) { + qla8044_wr_reg_indirect(vha, + p_entry->dr_addr, p_entry->dr_value); + qla8044_wr_reg_indirect(vha, + p_entry->ar_addr, p_entry->ar_value); + if (delay) { + if (qla8044_poll_reg(vha, + p_entry->ar_addr, delay, + p_poll->test_mask, + p_poll->test_value)) { + ql_dbg(ql_dbg_p3p, vha, 0xb091, + "%s: Timeout Error: poll list, ", + __func__); + ql_dbg(ql_dbg_p3p, vha, 0xb092, + "item_num %d, entry_num %d\n", i, + vha->reset_tmplt.seq_index); + } + } + } +} + +/* + * qla8044_read_modify_write - Read value from p_entry->arg1, modify the + * value, write value to p_entry->arg2. Process entries with p_hdr->delay + * between entries. + * + * @vha : Pointer to adapter structure + * @p_hdr : header with shift/or/xor values. + * + */ +static void +qla8044_read_modify_write(struct scsi_qla_host *vha, + struct qla8044_reset_entry_hdr *p_hdr) +{ + struct qla8044_entry *p_entry; + struct qla8044_rmw *p_rmw_hdr; + uint32_t i; + + p_rmw_hdr = (struct qla8044_rmw *)((char *)p_hdr + + sizeof(struct qla8044_reset_entry_hdr)); + + p_entry = (struct qla8044_entry *)((char *)p_rmw_hdr + + sizeof(struct qla8044_rmw)); + + for (i = 0; i < p_hdr->count; i++, p_entry++) { + qla8044_rmw_crb_reg(vha, p_entry->arg1, + p_entry->arg2, p_rmw_hdr); + if (p_hdr->delay) + udelay((uint32_t)(p_hdr->delay)); + } +} + +/* + * qla8044_pause - Wait for p_hdr->delay msecs, called between processing + * two entries of a sequence. + * + * @vha : Pointer to adapter structure + * @p_hdr : Common reset entry header. + * + */ +static +void qla8044_pause(struct scsi_qla_host *vha, + struct qla8044_reset_entry_hdr *p_hdr) +{ + if (p_hdr->delay) + mdelay((uint32_t)((long)p_hdr->delay)); +} + +/* + * qla8044_template_end - Indicates end of reset sequence processing. + * + * @vha : Pointer to adapter structure + * @p_hdr : Common reset entry header. + * + */ +static void +qla8044_template_end(struct scsi_qla_host *vha, + struct qla8044_reset_entry_hdr *p_hdr) +{ + vha->reset_tmplt.template_end = 1; + + if (vha->reset_tmplt.seq_error == 0) { + ql_dbg(ql_dbg_p3p, vha, 0xb093, + "%s: Reset sequence completed SUCCESSFULLY.\n", __func__); + } else { + ql_log(ql_log_fatal, vha, 0xb094, + "%s: Reset sequence completed with some timeout " + "errors.\n", __func__); + } +} + +/* + * qla8044_poll_read_list - Write ar_value to ar_addr register, read ar_addr, + * if (value & test_mask != test_value) re-read till timeout value expires, + * read dr_addr register and assign to reset_tmplt.array. + * + * @vha : Pointer to adapter structure + * @p_hdr : Common reset entry header. + * + */ +static void +qla8044_poll_read_list(struct scsi_qla_host *vha, + struct qla8044_reset_entry_hdr *p_hdr) +{ + long delay; + int index; + struct qla8044_quad_entry *p_entry; + struct qla8044_poll *p_poll; + uint32_t i; + uint32_t value; + + p_poll = (struct qla8044_poll *) + ((char *)p_hdr + sizeof(struct qla8044_reset_entry_hdr)); + + p_entry = (struct qla8044_quad_entry *) + ((char *)p_poll + sizeof(struct qla8044_poll)); + + delay = (long)p_hdr->delay; + + for (i = 0; i < p_hdr->count; i++, p_entry++) { + qla8044_wr_reg_indirect(vha, p_entry->ar_addr, + p_entry->ar_value); + if (delay) { + if (qla8044_poll_reg(vha, p_entry->ar_addr, delay, + p_poll->test_mask, p_poll->test_value)) { + ql_dbg(ql_dbg_p3p, vha, 0xb095, + "%s: Timeout Error: poll " + "list, ", __func__); + ql_dbg(ql_dbg_p3p, vha, 0xb096, + "Item_num %d, " + "entry_num %d\n", i, + vha->reset_tmplt.seq_index); + } else { + index = vha->reset_tmplt.array_index; + qla8044_rd_reg_indirect(vha, + p_entry->dr_addr, &value); + vha->reset_tmplt.array[index++] = value; + if (index == QLA8044_MAX_RESET_SEQ_ENTRIES) + vha->reset_tmplt.array_index = 1; + } + } + } +} + +/* + * qla8031_process_reset_template - Process all entries in reset template + * till entry with SEQ_END opcode, which indicates end of the reset template + * processing. Each entry has a Reset Entry header, entry opcode/command, with + * size of the entry, number of entries in sub-sequence and delay in microsecs + * or timeout in millisecs. + * + * @ha : Pointer to adapter structure + * @p_buff : Common reset entry header. + * + */ +static void +qla8044_process_reset_template(struct scsi_qla_host *vha, + char *p_buff) +{ + int index, entries; + struct qla8044_reset_entry_hdr *p_hdr; + char *p_entry = p_buff; + + vha->reset_tmplt.seq_end = 0; + vha->reset_tmplt.template_end = 0; + entries = vha->reset_tmplt.hdr->entries; + index = vha->reset_tmplt.seq_index; + + for (; (!vha->reset_tmplt.seq_end) && (index < entries); index++) { + p_hdr = (struct qla8044_reset_entry_hdr *)p_entry; + switch (p_hdr->cmd) { + case OPCODE_NOP: + break; + case OPCODE_WRITE_LIST: + qla8044_write_list(vha, p_hdr); + break; + case OPCODE_READ_WRITE_LIST: + qla8044_read_write_list(vha, p_hdr); + break; + case OPCODE_POLL_LIST: + qla8044_poll_list(vha, p_hdr); + break; + case OPCODE_POLL_WRITE_LIST: + qla8044_poll_write_list(vha, p_hdr); + break; + case OPCODE_READ_MODIFY_WRITE: + qla8044_read_modify_write(vha, p_hdr); + break; + case OPCODE_SEQ_PAUSE: + qla8044_pause(vha, p_hdr); + break; + case OPCODE_SEQ_END: + vha->reset_tmplt.seq_end = 1; + break; + case OPCODE_TMPL_END: + qla8044_template_end(vha, p_hdr); + break; + case OPCODE_POLL_READ_LIST: + qla8044_poll_read_list(vha, p_hdr); + break; + default: + ql_log(ql_log_fatal, vha, 0xb097, + "%s: Unknown command ==> 0x%04x on " + "entry = %d\n", __func__, p_hdr->cmd, index); + break; + } + /* + *Set pointer to next entry in the sequence. + */ + p_entry += p_hdr->size; + } + vha->reset_tmplt.seq_index = index; +} + +static void +qla8044_process_init_seq(struct scsi_qla_host *vha) +{ + qla8044_process_reset_template(vha, + vha->reset_tmplt.init_offset); + if (vha->reset_tmplt.seq_end != 1) + ql_log(ql_log_fatal, vha, 0xb098, + "%s: Abrupt INIT Sub-Sequence end.\n", + __func__); +} + +static void +qla8044_process_stop_seq(struct scsi_qla_host *vha) +{ + vha->reset_tmplt.seq_index = 0; + qla8044_process_reset_template(vha, vha->reset_tmplt.stop_offset); + if (vha->reset_tmplt.seq_end != 1) + ql_log(ql_log_fatal, vha, 0xb099, + "%s: Abrupt STOP Sub-Sequence end.\n", __func__); +} + +static void +qla8044_process_start_seq(struct scsi_qla_host *vha) +{ + qla8044_process_reset_template(vha, vha->reset_tmplt.start_offset); + if (vha->reset_tmplt.template_end != 1) + ql_log(ql_log_fatal, vha, 0xb09a, + "%s: Abrupt START Sub-Sequence end.\n", + __func__); +} + +static int +qla8044_lockless_flash_read_u32(struct scsi_qla_host *vha, + uint32_t flash_addr, uint8_t *p_data, int u32_word_count) +{ + uint32_t i; + uint32_t u32_word; + uint32_t flash_offset; + uint32_t addr = flash_addr; + int ret_val = QLA_SUCCESS; + + flash_offset = addr & (QLA8044_FLASH_SECTOR_SIZE - 1); + + if (addr & 0x3) { + ql_log(ql_log_fatal, vha, 0xb09b, "%s: Illegal addr = 0x%x\n", + __func__, addr); + ret_val = QLA_FUNCTION_FAILED; + goto exit_lockless_read; + } + + ret_val = qla8044_wr_reg_indirect(vha, + QLA8044_FLASH_DIRECT_WINDOW, (addr)); + + if (ret_val != QLA_SUCCESS) { + ql_log(ql_log_fatal, vha, 0xb09c, + "%s: failed to write addr 0x%x to FLASH_DIRECT_WINDOW!\n", + __func__, addr); + goto exit_lockless_read; + } + + /* Check if data is spread across multiple sectors */ + if ((flash_offset + (u32_word_count * sizeof(uint32_t))) > + (QLA8044_FLASH_SECTOR_SIZE - 1)) { + /* Multi sector read */ + for (i = 0; i < u32_word_count; i++) { + ret_val = qla8044_rd_reg_indirect(vha, + QLA8044_FLASH_DIRECT_DATA(addr), &u32_word); + if (ret_val != QLA_SUCCESS) { + ql_log(ql_log_fatal, vha, 0xb09d, + "%s: failed to read addr 0x%x!\n", + __func__, addr); + goto exit_lockless_read; + } + *(uint32_t *)p_data = u32_word; + p_data = p_data + 4; + addr = addr + 4; + flash_offset = flash_offset + 4; + if (flash_offset > (QLA8044_FLASH_SECTOR_SIZE - 1)) { + /* This write is needed once for each sector */ + ret_val = qla8044_wr_reg_indirect(vha, + QLA8044_FLASH_DIRECT_WINDOW, (addr)); + if (ret_val != QLA_SUCCESS) { + ql_log(ql_log_fatal, vha, 0xb09f, + "%s: failed to write addr " + "0x%x to FLASH_DIRECT_WINDOW!\n", + __func__, addr); + goto exit_lockless_read; + } + flash_offset = 0; + } + } + } else { + /* Single sector read */ + for (i = 0; i < u32_word_count; i++) { + ret_val = qla8044_rd_reg_indirect(vha, + QLA8044_FLASH_DIRECT_DATA(addr), &u32_word); + if (ret_val != QLA_SUCCESS) { + ql_log(ql_log_fatal, vha, 0xb0a0, + "%s: failed to read addr 0x%x!\n", + __func__, addr); + goto exit_lockless_read; + } + *(uint32_t *)p_data = u32_word; + p_data = p_data + 4; + addr = addr + 4; + } + } + +exit_lockless_read: + return ret_val; +} + +/* + * qla8044_ms_mem_write_128b - Writes data to MS/off-chip memory + * + * @vha : Pointer to adapter structure + * addr : Flash address to write to + * data : Data to be written + * count : word_count to be written + * + * Return Value - QLA_SUCCESS/QLA_FUNCTION_FAILED + */ +static int +qla8044_ms_mem_write_128b(struct scsi_qla_host *vha, + uint64_t addr, uint32_t *data, uint32_t count) +{ + int i, j, ret_val = QLA_SUCCESS; + uint32_t agt_ctrl; + unsigned long flags; + struct qla_hw_data *ha = vha->hw; + + /* Only 128-bit aligned access */ + if (addr & 0xF) { + ret_val = QLA_FUNCTION_FAILED; + goto exit_ms_mem_write; + } + write_lock_irqsave(&ha->hw_lock, flags); + + /* Write address */ + ret_val = qla8044_wr_reg_indirect(vha, MD_MIU_TEST_AGT_ADDR_HI, 0); + if (ret_val == QLA_FUNCTION_FAILED) { + ql_log(ql_log_fatal, vha, 0xb0a1, + "%s: write to AGT_ADDR_HI failed!\n", __func__); + goto exit_ms_mem_write_unlock; + } + + for (i = 0; i < count; i++, addr += 16) { + if (!((QLA8044_ADDR_IN_RANGE(addr, QLA8044_ADDR_QDR_NET, + QLA8044_ADDR_QDR_NET_MAX)) || + (QLA8044_ADDR_IN_RANGE(addr, QLA8044_ADDR_DDR_NET, + QLA8044_ADDR_DDR_NET_MAX)))) { + ret_val = QLA_FUNCTION_FAILED; + goto exit_ms_mem_write_unlock; + } + + ret_val = qla8044_wr_reg_indirect(vha, + MD_MIU_TEST_AGT_ADDR_LO, addr); + + /* Write data */ + ret_val += qla8044_wr_reg_indirect(vha, + MD_MIU_TEST_AGT_WRDATA_LO, *data++); + ret_val += qla8044_wr_reg_indirect(vha, + MD_MIU_TEST_AGT_WRDATA_HI, *data++); + ret_val += qla8044_wr_reg_indirect(vha, + MD_MIU_TEST_AGT_WRDATA_ULO, *data++); + ret_val += qla8044_wr_reg_indirect(vha, + MD_MIU_TEST_AGT_WRDATA_UHI, *data++); + if (ret_val == QLA_FUNCTION_FAILED) { + ql_log(ql_log_fatal, vha, 0xb0a2, + "%s: write to AGT_WRDATA failed!\n", + __func__); + goto exit_ms_mem_write_unlock; + } + + /* Check write status */ + ret_val = qla8044_wr_reg_indirect(vha, MD_MIU_TEST_AGT_CTRL, + MIU_TA_CTL_WRITE_ENABLE); + ret_val += qla8044_wr_reg_indirect(vha, MD_MIU_TEST_AGT_CTRL, + MIU_TA_CTL_WRITE_START); + if (ret_val == QLA_FUNCTION_FAILED) { + ql_log(ql_log_fatal, vha, 0xb0a3, + "%s: write to AGT_CTRL failed!\n", __func__); + goto exit_ms_mem_write_unlock; + } + + for (j = 0; j < MAX_CTL_CHECK; j++) { + ret_val = qla8044_rd_reg_indirect(vha, + MD_MIU_TEST_AGT_CTRL, &agt_ctrl); + if (ret_val == QLA_FUNCTION_FAILED) { + ql_log(ql_log_fatal, vha, 0xb0a4, + "%s: failed to read " + "MD_MIU_TEST_AGT_CTRL!\n", __func__); + goto exit_ms_mem_write_unlock; + } + if ((agt_ctrl & MIU_TA_CTL_BUSY) == 0) + break; + } + + /* Status check failed */ + if (j >= MAX_CTL_CHECK) { + ql_log(ql_log_fatal, vha, 0xb0a5, + "%s: MS memory write failed!\n", + __func__); + ret_val = QLA_FUNCTION_FAILED; + goto exit_ms_mem_write_unlock; + } + } + +exit_ms_mem_write_unlock: + write_unlock_irqrestore(&ha->hw_lock, flags); + +exit_ms_mem_write: + return ret_val; +} + +static int +qla8044_copy_bootloader(struct scsi_qla_host *vha) +{ + uint8_t *p_cache; + uint32_t src, count, size; + uint64_t dest; + int ret_val = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + + src = QLA8044_BOOTLOADER_FLASH_ADDR; + dest = qla8044_rd_reg(ha, QLA8044_BOOTLOADER_ADDR); + size = qla8044_rd_reg(ha, QLA8044_BOOTLOADER_SIZE); + + /* 128 bit alignment check */ + if (size & 0xF) + size = (size + 16) & ~0xF; + + /* 16 byte count */ + count = size/16; + + p_cache = vmalloc(size); + if (p_cache == NULL) { + ql_log(ql_log_fatal, vha, 0xb0a6, + "%s: Failed to allocate memory for " + "boot loader cache\n", __func__); + ret_val = QLA_FUNCTION_FAILED; + goto exit_copy_bootloader; + } + + ret_val = qla8044_lockless_flash_read_u32(vha, src, + p_cache, size/sizeof(uint32_t)); + if (ret_val == QLA_FUNCTION_FAILED) { + ql_log(ql_log_fatal, vha, 0xb0a7, + "%s: Error reading F/W from flash!!!\n", __func__); + goto exit_copy_error; + } + ql_dbg(ql_dbg_p3p, vha, 0xb0a8, "%s: Read F/W from flash!\n", + __func__); + + /* 128 bit/16 byte write to MS memory */ + ret_val = qla8044_ms_mem_write_128b(vha, dest, + (uint32_t *)p_cache, count); + if (ret_val == QLA_FUNCTION_FAILED) { + ql_log(ql_log_fatal, vha, 0xb0a9, + "%s: Error writing F/W to MS !!!\n", __func__); + goto exit_copy_error; + } + ql_dbg(ql_dbg_p3p, vha, 0xb0aa, + "%s: Wrote F/W (size %d) to MS !!!\n", + __func__, size); + +exit_copy_error: + vfree(p_cache); + +exit_copy_bootloader: + return ret_val; +} + +static int +qla8044_restart(struct scsi_qla_host *vha) +{ + int ret_val = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + + qla8044_process_stop_seq(vha); + + /* Collect minidump */ + if (ql2xmdenable) + qla8044_get_minidump(vha); + else + ql_log(ql_log_fatal, vha, 0xb14c, + "Minidump disabled.\n"); + + qla8044_process_init_seq(vha); + + if (qla8044_copy_bootloader(vha)) { + ql_log(ql_log_fatal, vha, 0xb0ab, + "%s: Copy bootloader, firmware restart failed!\n", + __func__); + ret_val = QLA_FUNCTION_FAILED; + goto exit_restart; + } + + /* + * Loads F/W from flash + */ + qla8044_wr_reg(ha, QLA8044_FW_IMAGE_VALID, QLA8044_BOOT_FROM_FLASH); + + qla8044_process_start_seq(vha); + +exit_restart: + return ret_val; +} + +/* + * qla8044_check_cmd_peg_status - Check peg status to see if Peg is + * initialized. + * + * @ha : Pointer to adapter structure + * + * Return Value - QLA_SUCCESS/QLA_FUNCTION_FAILED + */ +static int +qla8044_check_cmd_peg_status(struct scsi_qla_host *vha) +{ + uint32_t val, ret_val = QLA_FUNCTION_FAILED; + int retries = CRB_CMDPEG_CHECK_RETRY_COUNT; + struct qla_hw_data *ha = vha->hw; + + do { + val = qla8044_rd_reg(ha, QLA8044_CMDPEG_STATE); + if (val == PHAN_INITIALIZE_COMPLETE) { + ql_dbg(ql_dbg_p3p, vha, 0xb0ac, + "%s: Command Peg initialization " + "complete! state=0x%x\n", __func__, val); + ret_val = QLA_SUCCESS; + break; + } + msleep(CRB_CMDPEG_CHECK_DELAY); + } while (--retries); + + return ret_val; +} + +static int +qla8044_start_firmware(struct scsi_qla_host *vha) +{ + int ret_val = QLA_SUCCESS; + + if (qla8044_restart(vha)) { + ql_log(ql_log_fatal, vha, 0xb0ad, + "%s: Restart Error!!!, Need Reset!!!\n", + __func__); + ret_val = QLA_FUNCTION_FAILED; + goto exit_start_fw; + } else + ql_dbg(ql_dbg_p3p, vha, 0xb0af, + "%s: Restart done!\n", __func__); + + ret_val = qla8044_check_cmd_peg_status(vha); + if (ret_val) { + ql_log(ql_log_fatal, vha, 0xb0b0, + "%s: Peg not initialized!\n", __func__); + ret_val = QLA_FUNCTION_FAILED; + } + +exit_start_fw: + return ret_val; +} + +void +qla8044_clear_drv_active(struct qla_hw_data *ha) +{ + uint32_t drv_active; + struct scsi_qla_host *vha = pci_get_drvdata(ha->pdev); + + drv_active = qla8044_rd_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX); + drv_active &= ~(1 << (ha->portnum)); + + ql_log(ql_log_info, vha, 0xb0b1, + "%s(%ld): drv_active: 0x%08x\n", + __func__, vha->host_no, drv_active); + + qla8044_wr_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX, drv_active); +} + +/* + * qla8044_device_bootstrap - Initialize device, set DEV_READY, start fw + * @ha: pointer to adapter structure + * + * Note: IDC lock must be held upon entry + **/ +static int +qla8044_device_bootstrap(struct scsi_qla_host *vha) +{ + int rval = QLA_FUNCTION_FAILED; + int i; + uint32_t old_count = 0, count = 0; + int need_reset = 0; + uint32_t idc_ctrl; + struct qla_hw_data *ha = vha->hw; + + need_reset = qla8044_need_reset(vha); + + if (!need_reset) { + old_count = qla8044_rd_direct(vha, + QLA8044_PEG_ALIVE_COUNTER_INDEX); + + for (i = 0; i < 10; i++) { + msleep(200); + + count = qla8044_rd_direct(vha, + QLA8044_PEG_ALIVE_COUNTER_INDEX); + if (count != old_count) { + rval = QLA_SUCCESS; + goto dev_ready; + } + } + qla8044_flash_lock_recovery(vha); + } else { + /* We are trying to perform a recovery here. */ + if (ha->flags.isp82xx_fw_hung) + qla8044_flash_lock_recovery(vha); + } + + /* set to DEV_INITIALIZING */ + ql_log(ql_log_info, vha, 0xb0b2, + "%s: HW State: INITIALIZING\n", __func__); + qla8044_wr_direct(vha, QLA8044_CRB_DEV_STATE_INDEX, + QLA8XXX_DEV_INITIALIZING); + + qla8044_idc_unlock(ha); + rval = qla8044_start_firmware(vha); + qla8044_idc_lock(ha); + + if (rval != QLA_SUCCESS) { + ql_log(ql_log_info, vha, 0xb0b3, + "%s: HW State: FAILED\n", __func__); + qla8044_clear_drv_active(ha); + qla8044_wr_direct(vha, QLA8044_CRB_DEV_STATE_INDEX, + QLA8XXX_DEV_FAILED); + return rval; + } + + /* For ISP8044, If IDC_CTRL GRACEFUL_RESET_BIT1 is set , reset it after + * device goes to INIT state. */ + idc_ctrl = qla8044_rd_reg(ha, QLA8044_IDC_DRV_CTRL); + if (idc_ctrl & GRACEFUL_RESET_BIT1) { + qla8044_wr_reg(ha, QLA8044_IDC_DRV_CTRL, + (idc_ctrl & ~GRACEFUL_RESET_BIT1)); + ha->fw_dumped = 0; + } + +dev_ready: + ql_log(ql_log_info, vha, 0xb0b4, + "%s: HW State: READY\n", __func__); + qla8044_wr_direct(vha, QLA8044_CRB_DEV_STATE_INDEX, QLA8XXX_DEV_READY); + + return rval; +} + +/*-------------------------Reset Sequence Functions-----------------------*/ +static void +qla8044_dump_reset_seq_hdr(struct scsi_qla_host *vha) +{ + u8 *phdr; + + if (!vha->reset_tmplt.buff) { + ql_log(ql_log_fatal, vha, 0xb0b5, + "%s: Error Invalid reset_seq_template\n", __func__); + return; + } + + phdr = vha->reset_tmplt.buff; + ql_dbg(ql_dbg_p3p, vha, 0xb0b6, + "Reset Template :\n\t0x%X 0x%X 0x%X 0x%X" + "0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n" + "\t0x%X 0x%X 0x%X 0x%X 0x%X 0x%X\n\n", + *phdr, *(phdr+1), *(phdr+2), *(phdr+3), *(phdr+4), + *(phdr+5), *(phdr+6), *(phdr+7), *(phdr + 8), + *(phdr+9), *(phdr+10), *(phdr+11), *(phdr+12), + *(phdr+13), *(phdr+14), *(phdr+15)); +} + +/* + * qla8044_reset_seq_checksum_test - Validate Reset Sequence template. + * + * @ha : Pointer to adapter structure + * + * Return Value - QLA_SUCCESS/QLA_FUNCTION_FAILED + */ +static int +qla8044_reset_seq_checksum_test(struct scsi_qla_host *vha) +{ + uint32_t sum = 0; + uint16_t *buff = (uint16_t *)vha->reset_tmplt.buff; + int u16_count = vha->reset_tmplt.hdr->size / sizeof(uint16_t); + + while (u16_count-- > 0) + sum += *buff++; + + while (sum >> 16) + sum = (sum & 0xFFFF) + (sum >> 16); + + /* checksum of 0 indicates a valid template */ + if (~sum) { + return QLA_SUCCESS; + } else { + ql_log(ql_log_fatal, vha, 0xb0b7, + "%s: Reset seq checksum failed\n", __func__); + return QLA_FUNCTION_FAILED; + } +} + +/* + * qla8044_read_reset_template - Read Reset Template from Flash, validate + * the template and store offsets of stop/start/init offsets in ha->reset_tmplt. + * + * @ha : Pointer to adapter structure + */ +void +qla8044_read_reset_template(struct scsi_qla_host *vha) +{ + uint8_t *p_buff; + uint32_t addr, tmplt_hdr_def_size, tmplt_hdr_size; + + vha->reset_tmplt.seq_error = 0; + vha->reset_tmplt.buff = vmalloc(QLA8044_RESTART_TEMPLATE_SIZE); + if (vha->reset_tmplt.buff == NULL) { + ql_log(ql_log_fatal, vha, 0xb0b8, + "%s: Failed to allocate reset template resources\n", + __func__); + goto exit_read_reset_template; + } + + p_buff = vha->reset_tmplt.buff; + addr = QLA8044_RESET_TEMPLATE_ADDR; + + tmplt_hdr_def_size = + sizeof(struct qla8044_reset_template_hdr) / sizeof(uint32_t); + + ql_dbg(ql_dbg_p3p, vha, 0xb0b9, + "%s: Read template hdr size %d from Flash\n", + __func__, tmplt_hdr_def_size); + + /* Copy template header from flash */ + if (qla8044_read_flash_data(vha, p_buff, addr, tmplt_hdr_def_size)) { + ql_log(ql_log_fatal, vha, 0xb0ba, + "%s: Failed to read reset template\n", __func__); + goto exit_read_template_error; + } + + vha->reset_tmplt.hdr = + (struct qla8044_reset_template_hdr *) vha->reset_tmplt.buff; + + /* Validate the template header size and signature */ + tmplt_hdr_size = vha->reset_tmplt.hdr->hdr_size/sizeof(uint32_t); + if ((tmplt_hdr_size != tmplt_hdr_def_size) || + (vha->reset_tmplt.hdr->signature != RESET_TMPLT_HDR_SIGNATURE)) { + ql_log(ql_log_fatal, vha, 0xb0bb, + "%s: Template Header size invalid %d " + "tmplt_hdr_def_size %d!!!\n", __func__, + tmplt_hdr_size, tmplt_hdr_def_size); + goto exit_read_template_error; + } + + addr = QLA8044_RESET_TEMPLATE_ADDR + vha->reset_tmplt.hdr->hdr_size; + p_buff = vha->reset_tmplt.buff + vha->reset_tmplt.hdr->hdr_size; + tmplt_hdr_def_size = (vha->reset_tmplt.hdr->size - + vha->reset_tmplt.hdr->hdr_size)/sizeof(uint32_t); + + ql_dbg(ql_dbg_p3p, vha, 0xb0bc, + "%s: Read rest of the template size %d\n", + __func__, vha->reset_tmplt.hdr->size); + + /* Copy rest of the template */ + if (qla8044_read_flash_data(vha, p_buff, addr, tmplt_hdr_def_size)) { + ql_log(ql_log_fatal, vha, 0xb0bd, + "%s: Failed to read reset tempelate\n", __func__); + goto exit_read_template_error; + } + + /* Integrity check */ + if (qla8044_reset_seq_checksum_test(vha)) { + ql_log(ql_log_fatal, vha, 0xb0be, + "%s: Reset Seq checksum failed!\n", __func__); + goto exit_read_template_error; + } + + ql_dbg(ql_dbg_p3p, vha, 0xb0bf, + "%s: Reset Seq checksum passed! Get stop, " + "start and init seq offsets\n", __func__); + + /* Get STOP, START, INIT sequence offsets */ + vha->reset_tmplt.init_offset = vha->reset_tmplt.buff + + vha->reset_tmplt.hdr->init_seq_offset; + + vha->reset_tmplt.start_offset = vha->reset_tmplt.buff + + vha->reset_tmplt.hdr->start_seq_offset; + + vha->reset_tmplt.stop_offset = vha->reset_tmplt.buff + + vha->reset_tmplt.hdr->hdr_size; + + qla8044_dump_reset_seq_hdr(vha); + + goto exit_read_reset_template; + +exit_read_template_error: + vfree(vha->reset_tmplt.buff); + +exit_read_reset_template: + return; +} + +void +qla8044_set_idc_dontreset(struct scsi_qla_host *vha) +{ + uint32_t idc_ctrl; + struct qla_hw_data *ha = vha->hw; + + idc_ctrl = qla8044_rd_reg(ha, QLA8044_IDC_DRV_CTRL); + idc_ctrl |= DONTRESET_BIT0; + ql_dbg(ql_dbg_p3p, vha, 0xb0c0, + "%s: idc_ctrl = %d\n", __func__, idc_ctrl); + qla8044_wr_reg(ha, QLA8044_IDC_DRV_CTRL, idc_ctrl); +} + +inline void +qla8044_set_rst_ready(struct scsi_qla_host *vha) +{ + uint32_t drv_state; + struct qla_hw_data *ha = vha->hw; + + drv_state = qla8044_rd_direct(vha, QLA8044_CRB_DRV_STATE_INDEX); + + /* For ISP8044, drv_active register has 1 bit per function, + * shift 1 by func_num to set a bit for the function.*/ + drv_state |= (1 << ha->portnum); + + ql_log(ql_log_info, vha, 0xb0c1, + "%s(%ld): drv_state: 0x%08x\n", + __func__, vha->host_no, drv_state); + qla8044_wr_direct(vha, QLA8044_CRB_DRV_STATE_INDEX, drv_state); +} + +/** + * qla8044_need_reset_handler - Code to start reset sequence + * @ha: pointer to adapter structure + * + * Note: IDC lock must be held upon entry + **/ +static void +qla8044_need_reset_handler(struct scsi_qla_host *vha) +{ + uint32_t dev_state = 0, drv_state, drv_active; + unsigned long reset_timeout; + struct qla_hw_data *ha = vha->hw; + + ql_log(ql_log_fatal, vha, 0xb0c2, + "%s: Performing ISP error recovery\n", __func__); + + if (vha->flags.online) { + qla8044_idc_unlock(ha); + qla2x00_abort_isp_cleanup(vha); + ha->isp_ops->get_flash_version(vha, vha->req->ring); + ha->isp_ops->nvram_config(vha); + qla8044_idc_lock(ha); + } + + dev_state = qla8044_rd_direct(vha, + QLA8044_CRB_DEV_STATE_INDEX); + drv_state = qla8044_rd_direct(vha, + QLA8044_CRB_DRV_STATE_INDEX); + drv_active = qla8044_rd_direct(vha, + QLA8044_CRB_DRV_ACTIVE_INDEX); + + ql_log(ql_log_info, vha, 0xb0c5, + "%s(%ld): drv_state = 0x%x, drv_active = 0x%x dev_state = 0x%x\n", + __func__, vha->host_no, drv_state, drv_active, dev_state); + + qla8044_set_rst_ready(vha); + + /* wait for 10 seconds for reset ack from all functions */ + reset_timeout = jiffies + (ha->fcoe_reset_timeout * HZ); + + do { + if (time_after_eq(jiffies, reset_timeout)) { + ql_log(ql_log_info, vha, 0xb0c4, + "%s: Function %d: Reset Ack Timeout!, drv_state: 0x%08x, drv_active: 0x%08x\n", + __func__, ha->portnum, drv_state, drv_active); + break; + } + + qla8044_idc_unlock(ha); + msleep(1000); + qla8044_idc_lock(ha); + + dev_state = qla8044_rd_direct(vha, + QLA8044_CRB_DEV_STATE_INDEX); + drv_state = qla8044_rd_direct(vha, + QLA8044_CRB_DRV_STATE_INDEX); + drv_active = qla8044_rd_direct(vha, + QLA8044_CRB_DRV_ACTIVE_INDEX); + } while (((drv_state & drv_active) != drv_active) && + (dev_state == QLA8XXX_DEV_NEED_RESET)); + + /* Remove IDC participation of functions not acknowledging */ + if (drv_state != drv_active) { + ql_log(ql_log_info, vha, 0xb0c7, + "%s(%ld): Function %d turning off drv_active of non-acking function 0x%x\n", + __func__, vha->host_no, ha->portnum, + (drv_active ^ drv_state)); + drv_active = drv_active & drv_state; + qla8044_wr_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX, + drv_active); + } else { + /* + * Reset owner should execute reset recovery, + * if all functions acknowledged + */ + if ((ha->flags.nic_core_reset_owner) && + (dev_state == QLA8XXX_DEV_NEED_RESET)) { + ha->flags.nic_core_reset_owner = 0; + qla8044_device_bootstrap(vha); + return; + } + } + + /* Exit if non active function */ + if (!(drv_active & (1 << ha->portnum))) { + ha->flags.nic_core_reset_owner = 0; + return; + } + + /* + * Execute Reset Recovery if Reset Owner or Function 7 + * is the only active function + */ + if (ha->flags.nic_core_reset_owner || + ((drv_state & drv_active) == QLA8044_FUN7_ACTIVE_INDEX)) { + ha->flags.nic_core_reset_owner = 0; + qla8044_device_bootstrap(vha); + } +} + +static void +qla8044_set_drv_active(struct scsi_qla_host *vha) +{ + uint32_t drv_active; + struct qla_hw_data *ha = vha->hw; + + drv_active = qla8044_rd_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX); + + /* For ISP8044, drv_active register has 1 bit per function, + * shift 1 by func_num to set a bit for the function.*/ + drv_active |= (1 << ha->portnum); + + ql_log(ql_log_info, vha, 0xb0c8, + "%s(%ld): drv_active: 0x%08x\n", + __func__, vha->host_no, drv_active); + qla8044_wr_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX, drv_active); +} + +static int +qla8044_check_drv_active(struct scsi_qla_host *vha) +{ + uint32_t drv_active; + struct qla_hw_data *ha = vha->hw; + + drv_active = qla8044_rd_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX); + if (drv_active & (1 << ha->portnum)) + return QLA_SUCCESS; + else + return QLA_TEST_FAILED; +} + +static void +qla8044_clear_idc_dontreset(struct scsi_qla_host *vha) +{ + uint32_t idc_ctrl; + struct qla_hw_data *ha = vha->hw; + + idc_ctrl = qla8044_rd_reg(ha, QLA8044_IDC_DRV_CTRL); + idc_ctrl &= ~DONTRESET_BIT0; + ql_log(ql_log_info, vha, 0xb0c9, + "%s: idc_ctrl = %d\n", __func__, + idc_ctrl); + qla8044_wr_reg(ha, QLA8044_IDC_DRV_CTRL, idc_ctrl); +} + +static int +qla8044_set_idc_ver(struct scsi_qla_host *vha) +{ + int idc_ver; + uint32_t drv_active; + int rval = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + + drv_active = qla8044_rd_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX); + if (drv_active == (1 << ha->portnum)) { + idc_ver = qla8044_rd_direct(vha, + QLA8044_CRB_DRV_IDC_VERSION_INDEX); + idc_ver &= (~0xFF); + idc_ver |= QLA8044_IDC_VER_MAJ_VALUE; + qla8044_wr_direct(vha, QLA8044_CRB_DRV_IDC_VERSION_INDEX, + idc_ver); + ql_log(ql_log_info, vha, 0xb0ca, + "%s: IDC version updated to %d\n", + __func__, idc_ver); + } else { + idc_ver = qla8044_rd_direct(vha, + QLA8044_CRB_DRV_IDC_VERSION_INDEX); + idc_ver &= 0xFF; + if (QLA8044_IDC_VER_MAJ_VALUE != idc_ver) { + ql_log(ql_log_info, vha, 0xb0cb, + "%s: qla4xxx driver IDC version %d " + "is not compatible with IDC version %d " + "of other drivers!\n", + __func__, QLA8044_IDC_VER_MAJ_VALUE, + idc_ver); + rval = QLA_FUNCTION_FAILED; + goto exit_set_idc_ver; + } + } + + /* Update IDC_MINOR_VERSION */ + idc_ver = qla8044_rd_reg(ha, QLA8044_CRB_IDC_VER_MINOR); + idc_ver &= ~(0x03 << (ha->portnum * 2)); + idc_ver |= (QLA8044_IDC_VER_MIN_VALUE << (ha->portnum * 2)); + qla8044_wr_reg(ha, QLA8044_CRB_IDC_VER_MINOR, idc_ver); + +exit_set_idc_ver: + return rval; +} + +static int +qla8044_update_idc_reg(struct scsi_qla_host *vha) +{ + uint32_t drv_active; + int rval = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + + if (vha->flags.init_done) + goto exit_update_idc_reg; + + qla8044_idc_lock(ha); + qla8044_set_drv_active(vha); + + drv_active = qla8044_rd_direct(vha, + QLA8044_CRB_DRV_ACTIVE_INDEX); + + /* If we are the first driver to load and + * ql2xdontresethba is not set, clear IDC_CTRL BIT0. */ + if ((drv_active == (1 << ha->portnum)) && !ql2xdontresethba) + qla8044_clear_idc_dontreset(vha); + + rval = qla8044_set_idc_ver(vha); + if (rval == QLA_FUNCTION_FAILED) + qla8044_clear_drv_active(ha); + qla8044_idc_unlock(ha); + +exit_update_idc_reg: + return rval; +} + +/** + * qla8044_need_qsnt_handler - Code to start qsnt + * @ha: pointer to adapter structure + **/ +static void +qla8044_need_qsnt_handler(struct scsi_qla_host *vha) +{ + unsigned long qsnt_timeout; + uint32_t drv_state, drv_active, dev_state; + struct qla_hw_data *ha = vha->hw; + + if (vha->flags.online) + qla2x00_quiesce_io(vha); + else + return; + + qla8044_set_qsnt_ready(vha); + + /* Wait for 30 secs for all functions to ack qsnt mode */ + qsnt_timeout = jiffies + (QSNT_ACK_TOV * HZ); + drv_state = qla8044_rd_direct(vha, QLA8044_CRB_DRV_STATE_INDEX); + drv_active = qla8044_rd_direct(vha, QLA8044_CRB_DRV_ACTIVE_INDEX); + + /* Shift drv_active by 1 to match drv_state. As quiescent ready bit + position is at bit 1 and drv active is at bit 0 */ + drv_active = drv_active << 1; + + while (drv_state != drv_active) { + if (time_after_eq(jiffies, qsnt_timeout)) { + /* Other functions did not ack, changing state to + * DEV_READY + */ + clear_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags); + qla8044_wr_direct(vha, QLA8044_CRB_DEV_STATE_INDEX, + QLA8XXX_DEV_READY); + qla8044_clear_qsnt_ready(vha); + ql_log(ql_log_info, vha, 0xb0cc, + "Timeout waiting for quiescent ack!!!\n"); + return; + } + qla8044_idc_unlock(ha); + msleep(1000); + qla8044_idc_lock(ha); + + drv_state = qla8044_rd_direct(vha, + QLA8044_CRB_DRV_STATE_INDEX); + drv_active = qla8044_rd_direct(vha, + QLA8044_CRB_DRV_ACTIVE_INDEX); + drv_active = drv_active << 1; + } + + /* All functions have Acked. Set quiescent state */ + dev_state = qla8044_rd_direct(vha, QLA8044_CRB_DEV_STATE_INDEX); + + if (dev_state == QLA8XXX_DEV_NEED_QUIESCENT) { + qla8044_wr_direct(vha, QLA8044_CRB_DEV_STATE_INDEX, + QLA8XXX_DEV_QUIESCENT); + ql_log(ql_log_info, vha, 0xb0cd, + "%s: HW State: QUIESCENT\n", __func__); + } +} + +/* + * qla8044_device_state_handler - Adapter state machine + * @ha: pointer to host adapter structure. + * + * Note: IDC lock must be UNLOCKED upon entry + **/ +int +qla8044_device_state_handler(struct scsi_qla_host *vha) +{ + uint32_t dev_state; + int rval = QLA_SUCCESS; + unsigned long dev_init_timeout; + struct qla_hw_data *ha = vha->hw; + + rval = qla8044_update_idc_reg(vha); + if (rval == QLA_FUNCTION_FAILED) + goto exit_error; + + dev_state = qla8044_rd_direct(vha, QLA8044_CRB_DEV_STATE_INDEX); + ql_dbg(ql_dbg_p3p, vha, 0xb0ce, + "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->fcoe_dev_init_timeout * HZ); + + qla8044_idc_lock(ha); + + while (1) { + if (time_after_eq(jiffies, dev_init_timeout)) { + if (qla8044_check_drv_active(vha) == QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0xb0cf, + "%s: Device Init Failed 0x%x = %s\n", + QLA2XXX_DRIVER_NAME, dev_state, + dev_state < MAX_STATES ? + qdev_state(dev_state) : "Unknown"); + qla8044_wr_direct(vha, + QLA8044_CRB_DEV_STATE_INDEX, + QLA8XXX_DEV_FAILED); + } + } + + dev_state = qla8044_rd_direct(vha, QLA8044_CRB_DEV_STATE_INDEX); + ql_log(ql_log_info, vha, 0xb0d0, + "Device state is 0x%x = %s\n", + dev_state, dev_state < MAX_STATES ? + qdev_state(dev_state) : "Unknown"); + + /* NOTE: Make sure idc unlocked upon exit of switch statement */ + switch (dev_state) { + case QLA8XXX_DEV_READY: + ha->flags.nic_core_reset_owner = 0; + goto exit; + case QLA8XXX_DEV_COLD: + rval = qla8044_device_bootstrap(vha); + break; + case QLA8XXX_DEV_INITIALIZING: + qla8044_idc_unlock(ha); + msleep(1000); + qla8044_idc_lock(ha); + break; + case QLA8XXX_DEV_NEED_RESET: + /* For ISP8044, if NEED_RESET is set by any driver, + * it should be honored, irrespective of IDC_CTRL + * DONTRESET_BIT0 */ + qla8044_need_reset_handler(vha); + break; + case QLA8XXX_DEV_NEED_QUIESCENT: + /* idc locked/unlocked in handler */ + qla8044_need_qsnt_handler(vha); + + /* Reset the init timeout after qsnt handler */ + dev_init_timeout = jiffies + + (ha->fcoe_reset_timeout * HZ); + break; + case QLA8XXX_DEV_QUIESCENT: + ql_log(ql_log_info, vha, 0xb0d1, + "HW State: QUIESCENT\n"); + + qla8044_idc_unlock(ha); + msleep(1000); + qla8044_idc_lock(ha); + + /* Reset the init timeout after qsnt handler */ + dev_init_timeout = jiffies + + (ha->fcoe_reset_timeout * HZ); + break; + case QLA8XXX_DEV_FAILED: + ha->flags.nic_core_reset_owner = 0; + qla8044_idc_unlock(ha); + qla8xxx_dev_failed_handler(vha); + rval = QLA_FUNCTION_FAILED; + qla8044_idc_lock(ha); + goto exit; + default: + qla8044_idc_unlock(ha); + qla8xxx_dev_failed_handler(vha); + rval = QLA_FUNCTION_FAILED; + qla8044_idc_lock(ha); + goto exit; + } + } +exit: + qla8044_idc_unlock(ha); + +exit_error: + return rval; +} + +/** + * qla4_8xxx_check_temp - Check the ISP82XX temperature. + * @ha: adapter block pointer. + * + * Note: The caller should not hold the idc lock. + **/ +static int +qla8044_check_temp(struct scsi_qla_host *vha) +{ + uint32_t temp, temp_state, temp_val; + int status = QLA_SUCCESS; + + temp = qla8044_rd_direct(vha, QLA8044_CRB_TEMP_STATE_INDEX); + temp_state = qla82xx_get_temp_state(temp); + temp_val = qla82xx_get_temp_val(temp); + + if (temp_state == QLA82XX_TEMP_PANIC) { + ql_log(ql_log_warn, vha, 0xb0d2, + "Device temperature %d degrees C" + " exceeds maximum allowed. Hardware has been shut" + " down\n", temp_val); + status = QLA_FUNCTION_FAILED; + return status; + } else if (temp_state == QLA82XX_TEMP_WARN) { + ql_log(ql_log_warn, vha, 0xb0d3, + "Device temperature %d" + " degrees C exceeds operating range." + " Immediate action needed.\n", temp_val); + } + return 0; +} + +int qla8044_read_temperature(scsi_qla_host_t *vha) +{ + uint32_t temp; + + temp = qla8044_rd_direct(vha, QLA8044_CRB_TEMP_STATE_INDEX); + return qla82xx_get_temp_val(temp); +} + +/** + * qla8044_check_fw_alive - Check firmware health + * @ha: Pointer to host adapter structure. + * + * Context: Interrupt + **/ +int +qla8044_check_fw_alive(struct scsi_qla_host *vha) +{ + uint32_t fw_heartbeat_counter; + uint32_t halt_status1, halt_status2; + int status = QLA_SUCCESS; + + fw_heartbeat_counter = qla8044_rd_direct(vha, + QLA8044_PEG_ALIVE_COUNTER_INDEX); + + /* If PEG_ALIVE_COUNTER is 0xffffffff, AER/EEH is in progress, ignore */ + if (fw_heartbeat_counter == 0xffffffff) { + ql_dbg(ql_dbg_p3p, vha, 0xb0d4, + "scsi%ld: %s: Device in frozen " + "state, QLA82XX_PEG_ALIVE_COUNTER is 0xffffffff\n", + vha->host_no, __func__); + 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; + halt_status1 = qla8044_rd_direct(vha, + QLA8044_PEG_HALT_STATUS1_INDEX); + halt_status2 = qla8044_rd_direct(vha, + QLA8044_PEG_HALT_STATUS2_INDEX); + + ql_log(ql_log_info, vha, 0xb0d5, + "scsi(%ld): %s, ISP8044 " + "Dumping hw/fw registers:\n" + " PEG_HALT_STATUS1: 0x%x, " + "PEG_HALT_STATUS2: 0x%x,\n", + vha->host_no, __func__, halt_status1, + halt_status2); + status = QLA_FUNCTION_FAILED; + } + } else + vha->seconds_since_last_heartbeat = 0; + + vha->fw_heartbeat_counter = fw_heartbeat_counter; + return status; +} + +void +qla8044_watchdog(struct scsi_qla_host *vha) +{ + uint32_t dev_state, halt_status; + int halt_status_unrecoverable = 0; + struct qla_hw_data *ha = vha->hw; + + /* don't poll if reset is going on or FW hang in quiescent state */ + if (!(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || + test_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags))) { + dev_state = qla8044_rd_direct(vha, QLA8044_CRB_DEV_STATE_INDEX); + + if (qla8044_check_fw_alive(vha)) { + ha->flags.isp82xx_fw_hung = 1; + ql_log(ql_log_warn, vha, 0xb10a, + "Firmware hung.\n"); + qla82xx_clear_pending_mbx(vha); + } + + if (qla8044_check_temp(vha)) { + set_bit(ISP_UNRECOVERABLE, &vha->dpc_flags); + ha->flags.isp82xx_fw_hung = 1; + qla2xxx_wake_dpc(vha); + } else if (dev_state == QLA8XXX_DEV_NEED_RESET && + !test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) { + ql_log(ql_log_info, vha, 0xb0d6, + "%s: HW State: NEED RESET!\n", + __func__); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + } else if (dev_state == QLA8XXX_DEV_NEED_QUIESCENT && + !test_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags)) { + ql_log(ql_log_info, vha, 0xb0d7, + "%s: HW State: NEED QUIES detected!\n", + __func__); + set_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + } else { + /* Check firmware health */ + if (ha->flags.isp82xx_fw_hung) { + halt_status = qla8044_rd_direct(vha, + QLA8044_PEG_HALT_STATUS1_INDEX); + if (halt_status & + QLA8044_HALT_STATUS_FW_RESET) { + ql_log(ql_log_fatal, vha, + 0xb0d8, "%s: Firmware " + "error detected device " + "is being reset\n", + __func__); + } else if (halt_status & + QLA8044_HALT_STATUS_UNRECOVERABLE) { + halt_status_unrecoverable = 1; + } + + /* Since we cannot change dev_state in interrupt + * context, set appropriate DPC flag then wakeup + * DPC */ + if (halt_status_unrecoverable) { + set_bit(ISP_UNRECOVERABLE, + &vha->dpc_flags); + } else { + if (dev_state == + QLA8XXX_DEV_QUIESCENT) { + set_bit(FCOE_CTX_RESET_NEEDED, + &vha->dpc_flags); + ql_log(ql_log_info, vha, 0xb0d9, + "%s: FW CONTEXT Reset " + "needed!\n", __func__); + } else { + ql_log(ql_log_info, vha, + 0xb0da, "%s: " + "detect abort needed\n", + __func__); + set_bit(ISP_ABORT_NEEDED, + &vha->dpc_flags); + } + } + qla2xxx_wake_dpc(vha); + } + } + + } +} + +static int +qla8044_minidump_process_control(struct scsi_qla_host *vha, + struct qla8044_minidump_entry_hdr *entry_hdr) +{ + struct qla8044_minidump_entry_crb *crb_entry; + uint32_t read_value, opcode, poll_time, addr, index; + uint32_t crb_addr, rval = QLA_SUCCESS; + unsigned long wtime; + struct qla8044_minidump_template_hdr *tmplt_hdr; + int i; + struct qla_hw_data *ha = vha->hw; + + ql_dbg(ql_dbg_p3p, vha, 0xb0dd, "Entering fn: %s\n", __func__); + tmplt_hdr = (struct qla8044_minidump_template_hdr *) + ha->md_tmplt_hdr; + crb_entry = (struct qla8044_minidump_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) { + qla8044_wr_reg_indirect(vha, crb_addr, + crb_entry->value_1); + opcode &= ~QLA82XX_DBG_OPCODE_WR; + } + + if (opcode & QLA82XX_DBG_OPCODE_RW) { + qla8044_rd_reg_indirect(vha, crb_addr, &read_value); + qla8044_wr_reg_indirect(vha, crb_addr, read_value); + opcode &= ~QLA82XX_DBG_OPCODE_RW; + } + + if (opcode & QLA82XX_DBG_OPCODE_AND) { + qla8044_rd_reg_indirect(vha, crb_addr, &read_value); + 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; + } + qla8044_wr_reg_indirect(vha, crb_addr, read_value); + } + if (opcode & QLA82XX_DBG_OPCODE_OR) { + qla8044_rd_reg_indirect(vha, crb_addr, &read_value); + read_value |= crb_entry->value_3; + qla8044_wr_reg_indirect(vha, crb_addr, read_value); + opcode &= ~QLA82XX_DBG_OPCODE_OR; + } + if (opcode & QLA82XX_DBG_OPCODE_POLL) { + poll_time = crb_entry->crb_strd.poll_timeout; + wtime = jiffies + poll_time; + qla8044_rd_reg_indirect(vha, crb_addr, &read_value); + + 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 { + qla8044_rd_reg_indirect(vha, + crb_addr, &read_value); + } + } 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; + } + + qla8044_rd_reg_indirect(vha, addr, &read_value); + 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; + } + + qla8044_wr_reg_indirect(vha, addr, read_value); + 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 +qla8044_minidump_process_rdcrb(struct scsi_qla_host *vha, + struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr) +{ + uint32_t r_addr, r_stride, loop_cnt, i, r_value; + struct qla8044_minidump_entry_crb *crb_hdr; + uint32_t *data_ptr = *d_ptr; + + ql_dbg(ql_dbg_p3p, vha, 0xb0de, "Entering fn: %s\n", __func__); + crb_hdr = (struct qla8044_minidump_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++) { + qla8044_rd_reg_indirect(vha, r_addr, &r_value); + *data_ptr++ = r_addr; + *data_ptr++ = r_value; + r_addr += r_stride; + } + *d_ptr = data_ptr; +} + +static int +qla8044_minidump_process_rdmem(struct scsi_qla_host *vha, + struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr) +{ + uint32_t r_addr, r_value, r_data; + uint32_t i, j, loop_cnt; + struct qla8044_minidump_entry_rdmem *m_hdr; + unsigned long flags; + uint32_t *data_ptr = *d_ptr; + struct qla_hw_data *ha = vha->hw; + + ql_dbg(ql_dbg_p3p, vha, 0xb0df, "Entering fn: %s\n", __func__); + m_hdr = (struct qla8044_minidump_entry_rdmem *)entry_hdr; + r_addr = m_hdr->read_addr; + loop_cnt = m_hdr->read_data_size/16; + + ql_dbg(ql_dbg_p3p, vha, 0xb0f0, + "[%s]: Read addr: 0x%x, read_data_size: 0x%x\n", + __func__, r_addr, m_hdr->read_data_size); + + if (r_addr & 0xf) { + ql_dbg(ql_dbg_p3p, vha, 0xb0f1, + "[%s]: Read addr 0x%x not 16 bytes aligned\n", + __func__, r_addr); + return QLA_FUNCTION_FAILED; + } + + if (m_hdr->read_data_size % 16) { + ql_dbg(ql_dbg_p3p, vha, 0xb0f2, + "[%s]: Read data[0x%x] not multiple of 16 bytes\n", + __func__, m_hdr->read_data_size); + return QLA_FUNCTION_FAILED; + } + + ql_dbg(ql_dbg_p3p, vha, 0xb0f3, + "[%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++) { + qla8044_wr_reg_indirect(vha, MD_MIU_TEST_AGT_ADDR_LO, r_addr); + r_value = 0; + qla8044_wr_reg_indirect(vha, MD_MIU_TEST_AGT_ADDR_HI, r_value); + r_value = MIU_TA_CTL_ENABLE; + qla8044_wr_reg_indirect(vha, MD_MIU_TEST_AGT_CTRL, r_value); + r_value = MIU_TA_CTL_START_ENABLE; + qla8044_wr_reg_indirect(vha, MD_MIU_TEST_AGT_CTRL, r_value); + + for (j = 0; j < MAX_CTL_CHECK; j++) { + qla8044_rd_reg_indirect(vha, MD_MIU_TEST_AGT_CTRL, + &r_value); + if ((r_value & MIU_TA_CTL_BUSY) == 0) + break; + } + + if (j >= MAX_CTL_CHECK) { + write_unlock_irqrestore(&ha->hw_lock, flags); + return QLA_SUCCESS; + } + + for (j = 0; j < 4; j++) { + qla8044_rd_reg_indirect(vha, MD_MIU_TEST_AGT_RDDATA[j], + &r_data); + *data_ptr++ = r_data; + } + + r_addr += 16; + } + write_unlock_irqrestore(&ha->hw_lock, flags); + + ql_dbg(ql_dbg_p3p, vha, 0xb0f4, + "Leaving fn: %s datacount: 0x%x\n", + __func__, (loop_cnt * 16)); + + *d_ptr = data_ptr; + return QLA_SUCCESS; +} + +/* ISP83xx flash read for _RDROM _BOARD */ +static uint32_t +qla8044_minidump_process_rdrom(struct scsi_qla_host *vha, + struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr) +{ + uint32_t fl_addr, u32_count, rval; + struct qla8044_minidump_entry_rdrom *rom_hdr; + uint32_t *data_ptr = *d_ptr; + + rom_hdr = (struct qla8044_minidump_entry_rdrom *)entry_hdr; + fl_addr = rom_hdr->read_addr; + u32_count = (rom_hdr->read_data_size)/sizeof(uint32_t); + + ql_dbg(ql_dbg_p3p, vha, 0xb0f5, "[%s]: fl_addr: 0x%x, count: 0x%x\n", + __func__, fl_addr, u32_count); + + rval = qla8044_lockless_flash_read_u32(vha, fl_addr, + (u8 *)(data_ptr), u32_count); + + if (rval != QLA_SUCCESS) { + ql_log(ql_log_fatal, vha, 0xb0f6, + "%s: Flash Read Error,Count=%d\n", __func__, u32_count); + return QLA_FUNCTION_FAILED; + } else { + data_ptr += u32_count; + *d_ptr = data_ptr; + return QLA_SUCCESS; + } +} + +static void +qla8044_mark_entry_skipped(struct scsi_qla_host *vha, + struct qla8044_minidump_entry_hdr *entry_hdr, int index) +{ + entry_hdr->d_ctrl.driver_flags |= QLA82XX_DBG_SKIPPED_FLAG; + + ql_log(ql_log_info, vha, 0xb0f7, + "scsi(%ld): Skipping entry[%d]: ETYPE[0x%x]-ELEVEL[0x%x]\n", + vha->host_no, index, entry_hdr->entry_type, + entry_hdr->d_ctrl.entry_capture_mask); +} + +static int +qla8044_minidump_process_l2tag(struct scsi_qla_host *vha, + struct qla8044_minidump_entry_hdr *entry_hdr, + uint32_t **d_ptr) +{ + 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 qla8044_minidump_entry_cache *cache_hdr; + int rval = QLA_FUNCTION_FAILED; + uint32_t *data_ptr = *d_ptr; + + ql_dbg(ql_dbg_p3p, vha, 0xb0f8, "Entering fn: %s\n", __func__); + cache_hdr = (struct qla8044_minidump_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++) { + qla8044_wr_reg_indirect(vha, t_r_addr, t_value); + if (c_value_w) + qla8044_wr_reg_indirect(vha, c_addr, c_value_w); + + if (p_mask) { + w_time = jiffies + p_wait; + do { + qla8044_rd_reg_indirect(vha, c_addr, + &c_value_r); + if ((c_value_r & p_mask) == 0) { + break; + } else if (time_after_eq(jiffies, w_time)) { + /* capturing dump failed */ + return rval; + } + } while (1); + } + + addr = r_addr; + for (k = 0; k < r_cnt; k++) { + qla8044_rd_reg_indirect(vha, addr, &r_value); + *data_ptr++ = 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 +qla8044_minidump_process_l1cache(struct scsi_qla_host *vha, + struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr) +{ + 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 qla8044_minidump_entry_cache *cache_hdr; + uint32_t *data_ptr = *d_ptr; + + cache_hdr = (struct qla8044_minidump_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++) { + qla8044_wr_reg_indirect(vha, t_r_addr, t_value); + qla8044_wr_reg_indirect(vha, c_addr, c_value_w); + addr = r_addr; + for (k = 0; k < r_cnt; k++) { + qla8044_rd_reg_indirect(vha, addr, &r_value); + *data_ptr++ = 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 +qla8044_minidump_process_rdocm(struct scsi_qla_host *vha, + struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr) +{ + uint32_t r_addr, r_stride, loop_cnt, i, r_value; + struct qla8044_minidump_entry_rdocm *ocm_hdr; + uint32_t *data_ptr = *d_ptr; + struct qla_hw_data *ha = vha->hw; + + ql_dbg(ql_dbg_p3p, vha, 0xb0f9, "Entering fn: %s\n", __func__); + + ocm_hdr = (struct qla8044_minidump_entry_rdocm *)entry_hdr; + r_addr = ocm_hdr->read_addr; + r_stride = ocm_hdr->read_addr_stride; + loop_cnt = ocm_hdr->op_count; + + ql_dbg(ql_dbg_p3p, vha, 0xb0fa, + "[%s]: r_addr: 0x%x, r_stride: 0x%x, loop_cnt: 0x%x\n", + __func__, r_addr, r_stride, loop_cnt); + + for (i = 0; i < loop_cnt; i++) { + r_value = readl((void __iomem *)(r_addr + ha->nx_pcibase)); + *data_ptr++ = r_value; + r_addr += r_stride; + } + ql_dbg(ql_dbg_p3p, vha, 0xb0fb, "Leaving fn: %s datacount: 0x%lx\n", + __func__, (long unsigned int) (loop_cnt * sizeof(uint32_t))); + + *d_ptr = data_ptr; +} + +static void +qla8044_minidump_process_rdmux(struct scsi_qla_host *vha, + struct qla8044_minidump_entry_hdr *entry_hdr, + uint32_t **d_ptr) +{ + uint32_t r_addr, s_stride, s_addr, s_value, loop_cnt, i, r_value; + struct qla8044_minidump_entry_mux *mux_hdr; + uint32_t *data_ptr = *d_ptr; + + ql_dbg(ql_dbg_p3p, vha, 0xb0fc, "Entering fn: %s\n", __func__); + + mux_hdr = (struct qla8044_minidump_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++) { + qla8044_wr_reg_indirect(vha, s_addr, s_value); + qla8044_rd_reg_indirect(vha, r_addr, &r_value); + *data_ptr++ = s_value; + *data_ptr++ = r_value; + s_value += s_stride; + } + *d_ptr = data_ptr; +} + +static void +qla8044_minidump_process_queue(struct scsi_qla_host *vha, + struct qla8044_minidump_entry_hdr *entry_hdr, + uint32_t **d_ptr) +{ + uint32_t s_addr, r_addr; + uint32_t r_stride, r_value, r_cnt, qid = 0; + uint32_t i, k, loop_cnt; + struct qla8044_minidump_entry_queue *q_hdr; + uint32_t *data_ptr = *d_ptr; + + ql_dbg(ql_dbg_p3p, vha, 0xb0fd, "Entering fn: %s\n", __func__); + q_hdr = (struct qla8044_minidump_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++) { + qla8044_wr_reg_indirect(vha, s_addr, qid); + r_addr = q_hdr->read_addr; + for (k = 0; k < r_cnt; k++) { + qla8044_rd_reg_indirect(vha, r_addr, &r_value); + *data_ptr++ = r_value; + r_addr += r_stride; + } + qid += q_hdr->q_strd.queue_id_stride; + } + *d_ptr = data_ptr; +} + +/* ISP83xx functions to process new minidump entries... */ +static uint32_t +qla8044_minidump_process_pollrd(struct scsi_qla_host *vha, + struct qla8044_minidump_entry_hdr *entry_hdr, + uint32_t **d_ptr) +{ + uint32_t r_addr, s_addr, s_value, r_value, poll_wait, poll_mask; + uint16_t s_stride, i; + struct qla8044_minidump_entry_pollrd *pollrd_hdr; + uint32_t *data_ptr = *d_ptr; + + pollrd_hdr = (struct qla8044_minidump_entry_pollrd *) entry_hdr; + s_addr = pollrd_hdr->select_addr; + r_addr = pollrd_hdr->read_addr; + s_value = pollrd_hdr->select_value; + s_stride = pollrd_hdr->select_value_stride; + + poll_wait = pollrd_hdr->poll_wait; + poll_mask = pollrd_hdr->poll_mask; + + for (i = 0; i < pollrd_hdr->op_count; i++) { + qla8044_wr_reg_indirect(vha, s_addr, s_value); + poll_wait = pollrd_hdr->poll_wait; + while (1) { + qla8044_rd_reg_indirect(vha, s_addr, &r_value); + if ((r_value & poll_mask) != 0) { + break; + } else { + usleep_range(1000, 1100); + if (--poll_wait == 0) { + ql_log(ql_log_fatal, vha, 0xb0fe, + "%s: TIMEOUT\n", __func__); + goto error; + } + } + } + qla8044_rd_reg_indirect(vha, r_addr, &r_value); + *data_ptr++ = s_value; + *data_ptr++ = r_value; + + s_value += s_stride; + } + *d_ptr = data_ptr; + return QLA_SUCCESS; + +error: + return QLA_FUNCTION_FAILED; +} + +static void +qla8044_minidump_process_rdmux2(struct scsi_qla_host *vha, + struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr) +{ + uint32_t sel_val1, sel_val2, t_sel_val, data, i; + uint32_t sel_addr1, sel_addr2, sel_val_mask, read_addr; + struct qla8044_minidump_entry_rdmux2 *rdmux2_hdr; + uint32_t *data_ptr = *d_ptr; + + rdmux2_hdr = (struct qla8044_minidump_entry_rdmux2 *) entry_hdr; + sel_val1 = rdmux2_hdr->select_value_1; + sel_val2 = rdmux2_hdr->select_value_2; + sel_addr1 = rdmux2_hdr->select_addr_1; + sel_addr2 = rdmux2_hdr->select_addr_2; + sel_val_mask = rdmux2_hdr->select_value_mask; + read_addr = rdmux2_hdr->read_addr; + + for (i = 0; i < rdmux2_hdr->op_count; i++) { + qla8044_wr_reg_indirect(vha, sel_addr1, sel_val1); + t_sel_val = sel_val1 & sel_val_mask; + *data_ptr++ = t_sel_val; + + qla8044_wr_reg_indirect(vha, sel_addr2, t_sel_val); + qla8044_rd_reg_indirect(vha, read_addr, &data); + + *data_ptr++ = data; + + qla8044_wr_reg_indirect(vha, sel_addr1, sel_val2); + t_sel_val = sel_val2 & sel_val_mask; + *data_ptr++ = t_sel_val; + + qla8044_wr_reg_indirect(vha, sel_addr2, t_sel_val); + qla8044_rd_reg_indirect(vha, read_addr, &data); + + *data_ptr++ = data; + + sel_val1 += rdmux2_hdr->select_value_stride; + sel_val2 += rdmux2_hdr->select_value_stride; + } + + *d_ptr = data_ptr; +} + +static uint32_t +qla8044_minidump_process_pollrdmwr(struct scsi_qla_host *vha, + struct qla8044_minidump_entry_hdr *entry_hdr, + uint32_t **d_ptr) +{ + uint32_t poll_wait, poll_mask, r_value, data; + uint32_t addr_1, addr_2, value_1, value_2; + struct qla8044_minidump_entry_pollrdmwr *poll_hdr; + uint32_t *data_ptr = *d_ptr; + + poll_hdr = (struct qla8044_minidump_entry_pollrdmwr *) entry_hdr; + addr_1 = poll_hdr->addr_1; + addr_2 = poll_hdr->addr_2; + value_1 = poll_hdr->value_1; + value_2 = poll_hdr->value_2; + poll_mask = poll_hdr->poll_mask; + + qla8044_wr_reg_indirect(vha, addr_1, value_1); + + poll_wait = poll_hdr->poll_wait; + while (1) { + qla8044_rd_reg_indirect(vha, addr_1, &r_value); + + if ((r_value & poll_mask) != 0) { + break; + } else { + usleep_range(1000, 1100); + if (--poll_wait == 0) { + ql_log(ql_log_fatal, vha, 0xb0ff, + "%s: TIMEOUT\n", __func__); + goto error; + } + } + } + + qla8044_rd_reg_indirect(vha, addr_2, &data); + data &= poll_hdr->modify_mask; + qla8044_wr_reg_indirect(vha, addr_2, data); + qla8044_wr_reg_indirect(vha, addr_1, value_2); + + poll_wait = poll_hdr->poll_wait; + while (1) { + qla8044_rd_reg_indirect(vha, addr_1, &r_value); + + if ((r_value & poll_mask) != 0) { + break; + } else { + usleep_range(1000, 1100); + if (--poll_wait == 0) { + ql_log(ql_log_fatal, vha, 0xb100, + "%s: TIMEOUT2\n", __func__); + goto error; + } + } + } + + *data_ptr++ = addr_2; + *data_ptr++ = data; + + *d_ptr = data_ptr; + + return QLA_SUCCESS; + +error: + return QLA_FUNCTION_FAILED; +} + +#define ISP8044_PEX_DMA_ENGINE_INDEX 8 +#define ISP8044_PEX_DMA_BASE_ADDRESS 0x77320000 +#define ISP8044_PEX_DMA_NUM_OFFSET 0x10000 +#define ISP8044_PEX_DMA_CMD_ADDR_LOW 0x0 +#define ISP8044_PEX_DMA_CMD_ADDR_HIGH 0x04 +#define ISP8044_PEX_DMA_CMD_STS_AND_CNTRL 0x08 + +#define ISP8044_PEX_DMA_READ_SIZE (16 * 1024) +#define ISP8044_PEX_DMA_MAX_WAIT (100 * 100) /* Max wait of 100 msecs */ + +static int +qla8044_check_dma_engine_state(struct scsi_qla_host *vha) +{ + struct qla_hw_data *ha = vha->hw; + int rval = QLA_SUCCESS; + uint32_t dma_eng_num = 0, cmd_sts_and_cntrl = 0; + uint64_t dma_base_addr = 0; + struct qla8044_minidump_template_hdr *tmplt_hdr = NULL; + + tmplt_hdr = ha->md_tmplt_hdr; + dma_eng_num = + tmplt_hdr->saved_state_array[ISP8044_PEX_DMA_ENGINE_INDEX]; + dma_base_addr = ISP8044_PEX_DMA_BASE_ADDRESS + + (dma_eng_num * ISP8044_PEX_DMA_NUM_OFFSET); + + /* Read the pex-dma's command-status-and-control register. */ + rval = qla8044_rd_reg_indirect(vha, + (dma_base_addr + ISP8044_PEX_DMA_CMD_STS_AND_CNTRL), + &cmd_sts_and_cntrl); + if (rval) + return QLA_FUNCTION_FAILED; + + /* Check if requested pex-dma engine is available. */ + if (cmd_sts_and_cntrl & BIT_31) + return QLA_SUCCESS; + + return QLA_FUNCTION_FAILED; +} + +static int +qla8044_start_pex_dma(struct scsi_qla_host *vha, + struct qla8044_minidump_entry_rdmem_pex_dma *m_hdr) +{ + struct qla_hw_data *ha = vha->hw; + int rval = QLA_SUCCESS, wait = 0; + uint32_t dma_eng_num = 0, cmd_sts_and_cntrl = 0; + uint64_t dma_base_addr = 0; + struct qla8044_minidump_template_hdr *tmplt_hdr = NULL; + + tmplt_hdr = ha->md_tmplt_hdr; + dma_eng_num = + tmplt_hdr->saved_state_array[ISP8044_PEX_DMA_ENGINE_INDEX]; + dma_base_addr = ISP8044_PEX_DMA_BASE_ADDRESS + + (dma_eng_num * ISP8044_PEX_DMA_NUM_OFFSET); + + rval = qla8044_wr_reg_indirect(vha, + dma_base_addr + ISP8044_PEX_DMA_CMD_ADDR_LOW, + m_hdr->desc_card_addr); + if (rval) + goto error_exit; + + rval = qla8044_wr_reg_indirect(vha, + dma_base_addr + ISP8044_PEX_DMA_CMD_ADDR_HIGH, 0); + if (rval) + goto error_exit; + + rval = qla8044_wr_reg_indirect(vha, + dma_base_addr + ISP8044_PEX_DMA_CMD_STS_AND_CNTRL, + m_hdr->start_dma_cmd); + if (rval) + goto error_exit; + + /* Wait for dma operation to complete. */ + for (wait = 0; wait < ISP8044_PEX_DMA_MAX_WAIT; wait++) { + rval = qla8044_rd_reg_indirect(vha, + (dma_base_addr + ISP8044_PEX_DMA_CMD_STS_AND_CNTRL), + &cmd_sts_and_cntrl); + if (rval) + goto error_exit; + + if ((cmd_sts_and_cntrl & BIT_1) == 0) + break; + + udelay(10); + } + + /* Wait a max of 100 ms, otherwise fallback to rdmem entry read */ + if (wait >= ISP8044_PEX_DMA_MAX_WAIT) { + rval = QLA_FUNCTION_FAILED; + goto error_exit; + } + +error_exit: + return rval; +} + +static int +qla8044_minidump_pex_dma_read(struct scsi_qla_host *vha, + struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr) +{ + struct qla_hw_data *ha = vha->hw; + int rval = QLA_SUCCESS; + struct qla8044_minidump_entry_rdmem_pex_dma *m_hdr = NULL; + uint32_t chunk_size, read_size; + uint8_t *data_ptr = (uint8_t *)*d_ptr; + void *rdmem_buffer = NULL; + dma_addr_t rdmem_dma; + struct qla8044_pex_dma_descriptor dma_desc; + + rval = qla8044_check_dma_engine_state(vha); + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_p3p, vha, 0xb147, + "DMA engine not available. Fallback to rdmem-read.\n"); + return QLA_FUNCTION_FAILED; + } + + m_hdr = (void *)entry_hdr; + + rdmem_buffer = dma_alloc_coherent(&ha->pdev->dev, + ISP8044_PEX_DMA_READ_SIZE, &rdmem_dma, GFP_KERNEL); + if (!rdmem_buffer) { + ql_dbg(ql_dbg_p3p, vha, 0xb148, + "Unable to allocate rdmem dma buffer\n"); + return QLA_FUNCTION_FAILED; + } + + /* Prepare pex-dma descriptor to be written to MS memory. */ + /* dma-desc-cmd layout: + * 0-3: dma-desc-cmd 0-3 + * 4-7: pcid function number + * 8-15: dma-desc-cmd 8-15 + * dma_bus_addr: dma buffer address + * cmd.read_data_size: amount of data-chunk to be read. + */ + dma_desc.cmd.dma_desc_cmd = (m_hdr->dma_desc_cmd & 0xff0f); + dma_desc.cmd.dma_desc_cmd |= + ((PCI_FUNC(ha->pdev->devfn) & 0xf) << 0x4); + + dma_desc.dma_bus_addr = rdmem_dma; + dma_desc.cmd.read_data_size = chunk_size = ISP8044_PEX_DMA_READ_SIZE; + read_size = 0; + + /* + * Perform rdmem operation using pex-dma. + * Prepare dma in chunks of ISP8044_PEX_DMA_READ_SIZE. + */ + while (read_size < m_hdr->read_data_size) { + if (m_hdr->read_data_size - read_size < + ISP8044_PEX_DMA_READ_SIZE) { + chunk_size = (m_hdr->read_data_size - read_size); + dma_desc.cmd.read_data_size = chunk_size; + } + + dma_desc.src_addr = m_hdr->read_addr + read_size; + + /* Prepare: Write pex-dma descriptor to MS memory. */ + rval = qla8044_ms_mem_write_128b(vha, + m_hdr->desc_card_addr, (void *)&dma_desc, + (sizeof(struct qla8044_pex_dma_descriptor)/16)); + if (rval) { + ql_log(ql_log_warn, vha, 0xb14a, + "%s: Error writing rdmem-dma-init to MS !!!\n", + __func__); + goto error_exit; + } + ql_dbg(ql_dbg_p3p, vha, 0xb14b, + "%s: Dma-descriptor: Instruct for rdmem dma " + "(chunk_size 0x%x).\n", __func__, chunk_size); + + /* Execute: Start pex-dma operation. */ + rval = qla8044_start_pex_dma(vha, m_hdr); + if (rval) + goto error_exit; + + memcpy(data_ptr, rdmem_buffer, chunk_size); + data_ptr += chunk_size; + read_size += chunk_size; + } + + *d_ptr = (void *)data_ptr; + +error_exit: + if (rdmem_buffer) + dma_free_coherent(&ha->pdev->dev, ISP8044_PEX_DMA_READ_SIZE, + rdmem_buffer, rdmem_dma); + + return rval; +} + +static uint32_t +qla8044_minidump_process_rddfe(struct scsi_qla_host *vha, + struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr) +{ + int loop_cnt; + uint32_t addr1, addr2, value, data, temp, wrVal; + uint8_t stride, stride2; + uint16_t count; + uint32_t poll, mask, data_size, modify_mask; + uint32_t wait_count = 0; + + uint32_t *data_ptr = *d_ptr; + + struct qla8044_minidump_entry_rddfe *rddfe; + rddfe = (struct qla8044_minidump_entry_rddfe *) entry_hdr; + + addr1 = rddfe->addr_1; + value = rddfe->value; + stride = rddfe->stride; + stride2 = rddfe->stride2; + count = rddfe->count; + + poll = rddfe->poll; + mask = rddfe->mask; + modify_mask = rddfe->modify_mask; + data_size = rddfe->data_size; + + addr2 = addr1 + stride; + + for (loop_cnt = 0x0; loop_cnt < count; loop_cnt++) { + qla8044_wr_reg_indirect(vha, addr1, (0x40000000 | value)); + + wait_count = 0; + while (wait_count < poll) { + qla8044_rd_reg_indirect(vha, addr1, &temp); + if ((temp & mask) != 0) + break; + wait_count++; + } + + if (wait_count == poll) { + ql_log(ql_log_warn, vha, 0xb153, + "%s: TIMEOUT\n", __func__); + goto error; + } else { + qla8044_rd_reg_indirect(vha, addr2, &temp); + temp = temp & modify_mask; + temp = (temp | ((loop_cnt << 16) | loop_cnt)); + wrVal = ((temp << 16) | temp); + + qla8044_wr_reg_indirect(vha, addr2, wrVal); + qla8044_wr_reg_indirect(vha, addr1, value); + + wait_count = 0; + while (wait_count < poll) { + qla8044_rd_reg_indirect(vha, addr1, &temp); + if ((temp & mask) != 0) + break; + wait_count++; + } + if (wait_count == poll) { + ql_log(ql_log_warn, vha, 0xb154, + "%s: TIMEOUT\n", __func__); + goto error; + } + + qla8044_wr_reg_indirect(vha, addr1, + ((0x40000000 | value) + stride2)); + wait_count = 0; + while (wait_count < poll) { + qla8044_rd_reg_indirect(vha, addr1, &temp); + if ((temp & mask) != 0) + break; + wait_count++; + } + + if (wait_count == poll) { + ql_log(ql_log_warn, vha, 0xb155, + "%s: TIMEOUT\n", __func__); + goto error; + } + + qla8044_rd_reg_indirect(vha, addr2, &data); + + *data_ptr++ = wrVal; + *data_ptr++ = data; + } + + } + + *d_ptr = data_ptr; + return QLA_SUCCESS; + +error: + return -1; + +} + +static uint32_t +qla8044_minidump_process_rdmdio(struct scsi_qla_host *vha, + struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr) +{ + int ret = 0; + uint32_t addr1, addr2, value1, value2, data, selVal; + uint8_t stride1, stride2; + uint32_t addr3, addr4, addr5, addr6, addr7; + uint16_t count, loop_cnt; + uint32_t poll, mask; + uint32_t *data_ptr = *d_ptr; + + struct qla8044_minidump_entry_rdmdio *rdmdio; + + rdmdio = (struct qla8044_minidump_entry_rdmdio *) entry_hdr; + + addr1 = rdmdio->addr_1; + addr2 = rdmdio->addr_2; + value1 = rdmdio->value_1; + stride1 = rdmdio->stride_1; + stride2 = rdmdio->stride_2; + count = rdmdio->count; + + poll = rdmdio->poll; + mask = rdmdio->mask; + value2 = rdmdio->value_2; + + addr3 = addr1 + stride1; + + for (loop_cnt = 0; loop_cnt < count; loop_cnt++) { + ret = qla8044_poll_wait_ipmdio_bus_idle(vha, addr1, addr2, + addr3, mask); + if (ret == -1) + goto error; + + addr4 = addr2 - stride1; + ret = qla8044_ipmdio_wr_reg(vha, addr1, addr3, mask, addr4, + value2); + if (ret == -1) + goto error; + + addr5 = addr2 - (2 * stride1); + ret = qla8044_ipmdio_wr_reg(vha, addr1, addr3, mask, addr5, + value1); + if (ret == -1) + goto error; + + addr6 = addr2 - (3 * stride1); + ret = qla8044_ipmdio_wr_reg(vha, addr1, addr3, mask, + addr6, 0x2); + if (ret == -1) + goto error; + + ret = qla8044_poll_wait_ipmdio_bus_idle(vha, addr1, addr2, + addr3, mask); + if (ret == -1) + goto error; + + addr7 = addr2 - (4 * stride1); + data = qla8044_ipmdio_rd_reg(vha, addr1, addr3, + mask, addr7); + if (data == -1) + goto error; + + selVal = (value2 << 18) | (value1 << 2) | 2; + + stride2 = rdmdio->stride_2; + *data_ptr++ = selVal; + *data_ptr++ = data; + + value1 = value1 + stride2; + *d_ptr = data_ptr; + } + + return 0; + +error: + return -1; +} + +static uint32_t qla8044_minidump_process_pollwr(struct scsi_qla_host *vha, + struct qla8044_minidump_entry_hdr *entry_hdr, uint32_t **d_ptr) +{ + uint32_t addr1, addr2, value1, value2, poll, mask, r_value; + uint32_t wait_count = 0; + struct qla8044_minidump_entry_pollwr *pollwr_hdr; + + pollwr_hdr = (struct qla8044_minidump_entry_pollwr *)entry_hdr; + addr1 = pollwr_hdr->addr_1; + addr2 = pollwr_hdr->addr_2; + value1 = pollwr_hdr->value_1; + value2 = pollwr_hdr->value_2; + + poll = pollwr_hdr->poll; + mask = pollwr_hdr->mask; + + while (wait_count < poll) { + qla8044_rd_reg_indirect(vha, addr1, &r_value); + + if ((r_value & poll) != 0) + break; + wait_count++; + } + + if (wait_count == poll) { + ql_log(ql_log_warn, vha, 0xb156, "%s: TIMEOUT\n", __func__); + goto error; + } + + qla8044_wr_reg_indirect(vha, addr2, value2); + qla8044_wr_reg_indirect(vha, addr1, value1); + + wait_count = 0; + while (wait_count < poll) { + qla8044_rd_reg_indirect(vha, addr1, &r_value); + + if ((r_value & poll) != 0) + break; + wait_count++; + } + + return QLA_SUCCESS; + +error: + return -1; +} + +/* + * + * qla8044_collect_md_data - Retrieve firmware minidump data. + * @ha: pointer to adapter structure + **/ +int +qla8044_collect_md_data(struct scsi_qla_host *vha) +{ + int num_entry_hdr = 0; + struct qla8044_minidump_entry_hdr *entry_hdr; + struct qla8044_minidump_template_hdr *tmplt_hdr; + uint32_t *data_ptr; + uint32_t data_collected = 0, f_capture_mask; + int i, rval = QLA_FUNCTION_FAILED; + uint64_t now; + uint32_t timestamp, idc_control; + struct qla_hw_data *ha = vha->hw; + + if (!ha->md_dump) { + ql_log(ql_log_info, vha, 0xb101, + "%s(%ld) No buffer to dump\n", + __func__, vha->host_no); + return rval; + } + + if (ha->fw_dumped) { + ql_log(ql_log_warn, vha, 0xb10d, + "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, 0xb10e, + "Memory not allocated for minidump capture\n"); + goto md_failed; + } + + qla8044_idc_lock(ha); + idc_control = qla8044_rd_reg(ha, QLA8044_IDC_DRV_CTRL); + if (idc_control & GRACEFUL_RESET_BIT1) { + ql_log(ql_log_warn, vha, 0xb112, + "Forced reset from application, " + "ignore minidump capture\n"); + qla8044_wr_reg(ha, QLA8044_IDC_DRV_CTRL, + (idc_control & ~GRACEFUL_RESET_BIT1)); + qla8044_idc_unlock(ha); + + goto md_failed; + } + qla8044_idc_unlock(ha); + + if (qla82xx_validate_template_chksum(vha)) { + ql_log(ql_log_info, vha, 0xb109, + "Template checksum validation error\n"); + goto md_failed; + } + + tmplt_hdr = (struct qla8044_minidump_template_hdr *) + ha->md_tmplt_hdr; + data_ptr = (uint32_t *)((uint8_t *)ha->md_dump); + num_entry_hdr = tmplt_hdr->num_of_entries; + + ql_dbg(ql_dbg_p3p, vha, 0xb11a, + "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, 0xb10f, + "Minimum required capture mask[0x%x] level not set\n", + f_capture_mask); + + } + tmplt_hdr->driver_capture_mask = ql2xmdcapmask; + ql_log(ql_log_info, vha, 0xb102, + "[%s]: starting data ptr: %p\n", + __func__, data_ptr); + ql_log(ql_log_info, vha, 0xb10b, + "[%s]: no of entry headers in Template: 0x%x\n", + __func__, num_entry_hdr); + ql_log(ql_log_info, vha, 0xb10c, + "[%s]: Total_data_size 0x%x, %d obtained\n", + __func__, ha->md_dump_size, ha->md_dump_size); + + /* Update current timestamp before taking dump */ + now = get_jiffies_64(); + timestamp = (u32)(jiffies_to_msecs(now) / 1000); + tmplt_hdr->driver_timestamp = timestamp; + + entry_hdr = (struct qla8044_minidump_entry_hdr *) + (((uint8_t *)ha->md_tmplt_hdr) + tmplt_hdr->first_entry_offset); + tmplt_hdr->saved_state_array[QLA8044_SS_OCM_WNDREG_INDEX] = + tmplt_hdr->ocm_window_reg[ha->portnum]; + + /* Walk through the entry headers - validate/perform required action */ + for (i = 0; i < num_entry_hdr; i++) { + if (data_collected > ha->md_dump_size) { + ql_log(ql_log_info, vha, 0xb103, + "Data collected: [0x%x], " + "Total Dump size: [0x%x]\n", + data_collected, ha->md_dump_size); + return rval; + } + + if (!(entry_hdr->d_ctrl.entry_capture_mask & + ql2xmdcapmask)) { + entry_hdr->d_ctrl.driver_flags |= + QLA82XX_DBG_SKIPPED_FLAG; + goto skip_nxt_entry; + } + + ql_dbg(ql_dbg_p3p, vha, 0xb104, + "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: + qla8044_mark_entry_skipped(vha, entry_hdr, i); + break; + case QLA82XX_CNTRL: + rval = qla8044_minidump_process_control(vha, + entry_hdr); + if (rval != QLA_SUCCESS) { + qla8044_mark_entry_skipped(vha, entry_hdr, i); + goto md_failed; + } + break; + case QLA82XX_RDCRB: + qla8044_minidump_process_rdcrb(vha, + entry_hdr, &data_ptr); + break; + case QLA82XX_RDMEM: + rval = qla8044_minidump_pex_dma_read(vha, + entry_hdr, &data_ptr); + if (rval != QLA_SUCCESS) { + rval = qla8044_minidump_process_rdmem(vha, + entry_hdr, &data_ptr); + if (rval != QLA_SUCCESS) { + qla8044_mark_entry_skipped(vha, + entry_hdr, i); + goto md_failed; + } + } + break; + case QLA82XX_BOARD: + case QLA82XX_RDROM: + rval = qla8044_minidump_process_rdrom(vha, + entry_hdr, &data_ptr); + if (rval != QLA_SUCCESS) { + qla8044_mark_entry_skipped(vha, + entry_hdr, i); + } + break; + case QLA82XX_L2DTG: + case QLA82XX_L2ITG: + case QLA82XX_L2DAT: + case QLA82XX_L2INS: + rval = qla8044_minidump_process_l2tag(vha, + entry_hdr, &data_ptr); + if (rval != QLA_SUCCESS) { + qla8044_mark_entry_skipped(vha, entry_hdr, i); + goto md_failed; + } + break; + case QLA8044_L1DTG: + case QLA8044_L1ITG: + case QLA82XX_L1DAT: + case QLA82XX_L1INS: + qla8044_minidump_process_l1cache(vha, + entry_hdr, &data_ptr); + break; + case QLA82XX_RDOCM: + qla8044_minidump_process_rdocm(vha, + entry_hdr, &data_ptr); + break; + case QLA82XX_RDMUX: + qla8044_minidump_process_rdmux(vha, + entry_hdr, &data_ptr); + break; + case QLA82XX_QUEUE: + qla8044_minidump_process_queue(vha, + entry_hdr, &data_ptr); + break; + case QLA8044_POLLRD: + rval = qla8044_minidump_process_pollrd(vha, + entry_hdr, &data_ptr); + if (rval != QLA_SUCCESS) + qla8044_mark_entry_skipped(vha, entry_hdr, i); + break; + case QLA8044_RDMUX2: + qla8044_minidump_process_rdmux2(vha, + entry_hdr, &data_ptr); + break; + case QLA8044_POLLRDMWR: + rval = qla8044_minidump_process_pollrdmwr(vha, + entry_hdr, &data_ptr); + if (rval != QLA_SUCCESS) + qla8044_mark_entry_skipped(vha, entry_hdr, i); + break; + case QLA8044_RDDFE: + rval = qla8044_minidump_process_rddfe(vha, entry_hdr, + &data_ptr); + if (rval != QLA_SUCCESS) + qla8044_mark_entry_skipped(vha, entry_hdr, i); + break; + case QLA8044_RDMDIO: + rval = qla8044_minidump_process_rdmdio(vha, entry_hdr, + &data_ptr); + if (rval != QLA_SUCCESS) + qla8044_mark_entry_skipped(vha, entry_hdr, i); + break; + case QLA8044_POLLWR: + rval = qla8044_minidump_process_pollwr(vha, entry_hdr, + &data_ptr); + if (rval != QLA_SUCCESS) + qla8044_mark_entry_skipped(vha, entry_hdr, i); + break; + case QLA82XX_RDNOP: + default: + qla8044_mark_entry_skipped(vha, entry_hdr, i); + break; + } + + data_collected = (uint8_t *)data_ptr - + (uint8_t *)((uint8_t *)ha->md_dump); +skip_nxt_entry: + /* + * next entry in the template + */ + entry_hdr = (struct qla8044_minidump_entry_hdr *) + (((uint8_t *)entry_hdr) + entry_hdr->entry_size); + } + + if (data_collected != ha->md_dump_size) { + ql_log(ql_log_info, vha, 0xb105, + "Dump data mismatch: Data collected: " + "[0x%x], total_data_size:[0x%x]\n", + data_collected, ha->md_dump_size); + rval = QLA_FUNCTION_FAILED; + goto md_failed; + } + + ql_log(ql_log_info, vha, 0xb110, + "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); + + + ql_log(ql_log_info, vha, 0xb106, + "Leaving fn: %s Last entry: 0x%x\n", + __func__, i); +md_failed: + return rval; +} + +void +qla8044_get_minidump(struct scsi_qla_host *vha) +{ + struct qla_hw_data *ha = vha->hw; + + if (!qla8044_collect_md_data(vha)) { + ha->fw_dumped = 1; + ha->prev_minidump_failed = 0; + } else { + ql_log(ql_log_fatal, vha, 0xb0db, + "%s: Unable to collect minidump\n", + __func__); + ha->prev_minidump_failed = 1; + } +} + +static int +qla8044_poll_flash_status_reg(struct scsi_qla_host *vha) +{ + uint32_t flash_status; + int retries = QLA8044_FLASH_READ_RETRY_COUNT; + int ret_val = QLA_SUCCESS; + + while (retries--) { + ret_val = qla8044_rd_reg_indirect(vha, QLA8044_FLASH_STATUS, + &flash_status); + if (ret_val) { + ql_log(ql_log_warn, vha, 0xb13c, + "%s: Failed to read FLASH_STATUS reg.\n", + __func__); + break; + } + if ((flash_status & QLA8044_FLASH_STATUS_READY) == + QLA8044_FLASH_STATUS_READY) + break; + msleep(QLA8044_FLASH_STATUS_REG_POLL_DELAY); + } + + if (!retries) + ret_val = QLA_FUNCTION_FAILED; + + return ret_val; +} + +static int +qla8044_write_flash_status_reg(struct scsi_qla_host *vha, + uint32_t data) +{ + int ret_val = QLA_SUCCESS; + uint32_t cmd; + + cmd = vha->hw->fdt_wrt_sts_reg_cmd; + + ret_val = qla8044_wr_reg_indirect(vha, QLA8044_FLASH_ADDR, + QLA8044_FLASH_STATUS_WRITE_DEF_SIG | cmd); + if (ret_val) { + ql_log(ql_log_warn, vha, 0xb125, + "%s: Failed to write to FLASH_ADDR.\n", __func__); + goto exit_func; + } + + ret_val = qla8044_wr_reg_indirect(vha, QLA8044_FLASH_WRDATA, data); + if (ret_val) { + ql_log(ql_log_warn, vha, 0xb126, + "%s: Failed to write to FLASH_WRDATA.\n", __func__); + goto exit_func; + } + + ret_val = qla8044_wr_reg_indirect(vha, QLA8044_FLASH_CONTROL, + QLA8044_FLASH_SECOND_ERASE_MS_VAL); + if (ret_val) { + ql_log(ql_log_warn, vha, 0xb127, + "%s: Failed to write to FLASH_CONTROL.\n", __func__); + goto exit_func; + } + + ret_val = qla8044_poll_flash_status_reg(vha); + if (ret_val) + ql_log(ql_log_warn, vha, 0xb128, + "%s: Error polling flash status reg.\n", __func__); + +exit_func: + return ret_val; +} + +/* + * This function assumes that the flash lock is held. + */ +static int +qla8044_unprotect_flash(scsi_qla_host_t *vha) +{ + int ret_val; + struct qla_hw_data *ha = vha->hw; + + ret_val = qla8044_write_flash_status_reg(vha, ha->fdt_wrt_enable); + if (ret_val) + ql_log(ql_log_warn, vha, 0xb139, + "%s: Write flash status failed.\n", __func__); + + return ret_val; +} + +/* + * This function assumes that the flash lock is held. + */ +static int +qla8044_protect_flash(scsi_qla_host_t *vha) +{ + int ret_val; + struct qla_hw_data *ha = vha->hw; + + ret_val = qla8044_write_flash_status_reg(vha, ha->fdt_wrt_disable); + if (ret_val) + ql_log(ql_log_warn, vha, 0xb13b, + "%s: Write flash status failed.\n", __func__); + + return ret_val; +} + + +static int +qla8044_erase_flash_sector(struct scsi_qla_host *vha, + uint32_t sector_start_addr) +{ + uint32_t reversed_addr; + int ret_val = QLA_SUCCESS; + + ret_val = qla8044_poll_flash_status_reg(vha); + if (ret_val) { + ql_log(ql_log_warn, vha, 0xb12e, + "%s: Poll flash status after erase failed..\n", __func__); + } + + reversed_addr = (((sector_start_addr & 0xFF) << 16) | + (sector_start_addr & 0xFF00) | + ((sector_start_addr & 0xFF0000) >> 16)); + + ret_val = qla8044_wr_reg_indirect(vha, + QLA8044_FLASH_WRDATA, reversed_addr); + if (ret_val) { + ql_log(ql_log_warn, vha, 0xb12f, + "%s: Failed to write to FLASH_WRDATA.\n", __func__); + } + ret_val = qla8044_wr_reg_indirect(vha, QLA8044_FLASH_ADDR, + QLA8044_FLASH_ERASE_SIG | vha->hw->fdt_erase_cmd); + if (ret_val) { + ql_log(ql_log_warn, vha, 0xb130, + "%s: Failed to write to FLASH_ADDR.\n", __func__); + } + ret_val = qla8044_wr_reg_indirect(vha, QLA8044_FLASH_CONTROL, + QLA8044_FLASH_LAST_ERASE_MS_VAL); + if (ret_val) { + ql_log(ql_log_warn, vha, 0xb131, + "%s: Failed write to FLASH_CONTROL.\n", __func__); + } + ret_val = qla8044_poll_flash_status_reg(vha); + if (ret_val) { + ql_log(ql_log_warn, vha, 0xb132, + "%s: Poll flash status failed.\n", __func__); + } + + + return ret_val; +} + +/* + * qla8044_flash_write_u32 - Write data to flash + * + * @ha : Pointer to adapter structure + * addr : Flash address to write to + * p_data : Data to be written + * + * Return Value - QLA_SUCCESS/QLA_FUNCTION_FAILED + * + * NOTE: Lock should be held on entry + */ +static int +qla8044_flash_write_u32(struct scsi_qla_host *vha, uint32_t addr, + uint32_t *p_data) +{ + int ret_val = QLA_SUCCESS; + + ret_val = qla8044_wr_reg_indirect(vha, QLA8044_FLASH_ADDR, + 0x00800000 | (addr >> 2)); + if (ret_val) { + ql_log(ql_log_warn, vha, 0xb134, + "%s: Failed write to FLASH_ADDR.\n", __func__); + goto exit_func; + } + ret_val = qla8044_wr_reg_indirect(vha, QLA8044_FLASH_WRDATA, *p_data); + if (ret_val) { + ql_log(ql_log_warn, vha, 0xb135, + "%s: Failed write to FLASH_WRDATA.\n", __func__); + goto exit_func; + } + ret_val = qla8044_wr_reg_indirect(vha, QLA8044_FLASH_CONTROL, 0x3D); + if (ret_val) { + ql_log(ql_log_warn, vha, 0xb136, + "%s: Failed write to FLASH_CONTROL.\n", __func__); + goto exit_func; + } + ret_val = qla8044_poll_flash_status_reg(vha); + if (ret_val) { + ql_log(ql_log_warn, vha, 0xb137, + "%s: Poll flash status failed.\n", __func__); + } + +exit_func: + return ret_val; +} + +static int +qla8044_write_flash_buffer_mode(scsi_qla_host_t *vha, uint32_t *dwptr, + uint32_t faddr, uint32_t dwords) +{ + int ret = QLA_FUNCTION_FAILED; + uint32_t spi_val; + + if (dwords < QLA8044_MIN_OPTROM_BURST_DWORDS || + dwords > QLA8044_MAX_OPTROM_BURST_DWORDS) { + ql_dbg(ql_dbg_user, vha, 0xb123, + "Got unsupported dwords = 0x%x.\n", + dwords); + return QLA_FUNCTION_FAILED; + } + + qla8044_rd_reg_indirect(vha, QLA8044_FLASH_SPI_CONTROL, &spi_val); + qla8044_wr_reg_indirect(vha, QLA8044_FLASH_SPI_CONTROL, + spi_val | QLA8044_FLASH_SPI_CTL); + qla8044_wr_reg_indirect(vha, QLA8044_FLASH_ADDR, + QLA8044_FLASH_FIRST_TEMP_VAL); + + /* First DWORD write to FLASH_WRDATA */ + ret = qla8044_wr_reg_indirect(vha, QLA8044_FLASH_WRDATA, + *dwptr++); + qla8044_wr_reg_indirect(vha, QLA8044_FLASH_CONTROL, + QLA8044_FLASH_FIRST_MS_PATTERN); + + ret = qla8044_poll_flash_status_reg(vha); + if (ret) { + ql_log(ql_log_warn, vha, 0xb124, + "%s: Failed.\n", __func__); + goto exit_func; + } + + dwords--; + + qla8044_wr_reg_indirect(vha, QLA8044_FLASH_ADDR, + QLA8044_FLASH_SECOND_TEMP_VAL); + + + /* Second to N-1 DWORDS writes */ + while (dwords != 1) { + qla8044_wr_reg_indirect(vha, QLA8044_FLASH_WRDATA, *dwptr++); + qla8044_wr_reg_indirect(vha, QLA8044_FLASH_CONTROL, + QLA8044_FLASH_SECOND_MS_PATTERN); + ret = qla8044_poll_flash_status_reg(vha); + if (ret) { + ql_log(ql_log_warn, vha, 0xb129, + "%s: Failed.\n", __func__); + goto exit_func; + } + dwords--; + } + + qla8044_wr_reg_indirect(vha, QLA8044_FLASH_ADDR, + QLA8044_FLASH_FIRST_TEMP_VAL | (faddr >> 2)); + + /* Last DWORD write */ + qla8044_wr_reg_indirect(vha, QLA8044_FLASH_WRDATA, *dwptr++); + qla8044_wr_reg_indirect(vha, QLA8044_FLASH_CONTROL, + QLA8044_FLASH_LAST_MS_PATTERN); + ret = qla8044_poll_flash_status_reg(vha); + if (ret) { + ql_log(ql_log_warn, vha, 0xb12a, + "%s: Failed.\n", __func__); + goto exit_func; + } + qla8044_rd_reg_indirect(vha, QLA8044_FLASH_SPI_STATUS, &spi_val); + + if ((spi_val & QLA8044_FLASH_SPI_CTL) == QLA8044_FLASH_SPI_CTL) { + ql_log(ql_log_warn, vha, 0xb12b, + "%s: Failed.\n", __func__); + spi_val = 0; + /* Operation failed, clear error bit. */ + qla8044_rd_reg_indirect(vha, QLA8044_FLASH_SPI_CONTROL, + &spi_val); + qla8044_wr_reg_indirect(vha, QLA8044_FLASH_SPI_CONTROL, + spi_val | QLA8044_FLASH_SPI_CTL); + } +exit_func: + return ret; +} + +static int +qla8044_write_flash_dword_mode(scsi_qla_host_t *vha, uint32_t *dwptr, + uint32_t faddr, uint32_t dwords) +{ + int ret = QLA_FUNCTION_FAILED; + uint32_t liter; + + for (liter = 0; liter < dwords; liter++, faddr += 4, dwptr++) { + ret = qla8044_flash_write_u32(vha, faddr, dwptr); + if (ret) { + ql_dbg(ql_dbg_p3p, vha, 0xb141, + "%s: flash address=%x data=%x.\n", __func__, + faddr, *dwptr); + break; + } + } + + return ret; +} + +int +qla8044_write_optrom_data(struct scsi_qla_host *vha, uint8_t *buf, + uint32_t offset, uint32_t length) +{ + int rval = QLA_FUNCTION_FAILED, i, burst_iter_count; + int dword_count, erase_sec_count; + uint32_t erase_offset; + uint8_t *p_cache, *p_src; + + erase_offset = offset; + + p_cache = kcalloc(length, sizeof(uint8_t), GFP_KERNEL); + if (!p_cache) + return QLA_FUNCTION_FAILED; + + memcpy(p_cache, buf, length); + p_src = p_cache; + dword_count = length / sizeof(uint32_t); + /* Since the offset and legth are sector aligned, it will be always + * multiple of burst_iter_count (64) + */ + burst_iter_count = dword_count / QLA8044_MAX_OPTROM_BURST_DWORDS; + erase_sec_count = length / QLA8044_SECTOR_SIZE; + + /* Suspend HBA. */ + scsi_block_requests(vha->host); + /* Lock and enable write for whole operation. */ + qla8044_flash_lock(vha); + qla8044_unprotect_flash(vha); + + /* Erasing the sectors */ + for (i = 0; i < erase_sec_count; i++) { + rval = qla8044_erase_flash_sector(vha, erase_offset); + ql_dbg(ql_dbg_user, vha, 0xb138, + "Done erase of sector=0x%x.\n", + erase_offset); + if (rval) { + ql_log(ql_log_warn, vha, 0xb121, + "Failed to erase the sector having address: " + "0x%x.\n", erase_offset); + goto out; + } + erase_offset += QLA8044_SECTOR_SIZE; + } + ql_dbg(ql_dbg_user, vha, 0xb13f, + "Got write for addr = 0x%x length=0x%x.\n", + offset, length); + + for (i = 0; i < burst_iter_count; i++) { + + /* Go with write. */ + rval = qla8044_write_flash_buffer_mode(vha, (uint32_t *)p_src, + offset, QLA8044_MAX_OPTROM_BURST_DWORDS); + if (rval) { + /* Buffer Mode failed skip to dword mode */ + ql_log(ql_log_warn, vha, 0xb122, + "Failed to write flash in buffer mode, " + "Reverting to slow-write.\n"); + rval = qla8044_write_flash_dword_mode(vha, + (uint32_t *)p_src, offset, + QLA8044_MAX_OPTROM_BURST_DWORDS); + } + p_src += sizeof(uint32_t) * QLA8044_MAX_OPTROM_BURST_DWORDS; + offset += sizeof(uint32_t) * QLA8044_MAX_OPTROM_BURST_DWORDS; + } + ql_dbg(ql_dbg_user, vha, 0xb133, + "Done writing.\n"); + +out: + qla8044_protect_flash(vha); + qla8044_flash_unlock(vha); + scsi_unblock_requests(vha->host); + kfree(p_cache); + + return rval; +} + +#define LEG_INT_PTR_B31 (1 << 31) +#define LEG_INT_PTR_B30 (1 << 30) +#define PF_BITS_MASK (0xF << 16) +/** + * qla8044_intr_handler() - Process interrupts for the ISP8044 + * @irq: + * @dev_id: SCSI driver HA context + * + * Called by system whenever the host adapter generates an interrupt. + * + * Returns handled flag. + */ +irqreturn_t +qla8044_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; + unsigned long flags; + unsigned long iter; + uint32_t stat; + uint16_t mb[4]; + uint32_t leg_int_ptr = 0, pf_bit; + + rsp = (struct rsp_que *) dev_id; + if (!rsp) { + ql_log(ql_log_info, NULL, 0xb143, + "%s(): NULL response queue pointer\n", __func__); + return IRQ_NONE; + } + ha = rsp->hw; + vha = pci_get_drvdata(ha->pdev); + + if (unlikely(pci_channel_offline(ha->pdev))) + return IRQ_HANDLED; + + leg_int_ptr = qla8044_rd_reg(ha, LEG_INTR_PTR_OFFSET); + + /* Legacy interrupt is valid if bit31 of leg_int_ptr is set */ + if (!(leg_int_ptr & (LEG_INT_PTR_B31))) { + ql_dbg(ql_dbg_p3p, vha, 0xb144, + "%s: Legacy Interrupt Bit 31 not set, " + "spurious interrupt!\n", __func__); + return IRQ_NONE; + } + + pf_bit = ha->portnum << 16; + /* Validate the PCIE function ID set in leg_int_ptr bits [19..16] */ + if ((leg_int_ptr & (PF_BITS_MASK)) != pf_bit) { + ql_dbg(ql_dbg_p3p, vha, 0xb145, + "%s: Incorrect function ID 0x%x in " + "legacy interrupt register, " + "ha->pf_bit = 0x%x\n", __func__, + (leg_int_ptr & (PF_BITS_MASK)), pf_bit); + return IRQ_NONE; + } + + /* To de-assert legacy interrupt, write 0 to Legacy Interrupt Trigger + * Control register and poll till Legacy Interrupt Pointer register + * bit32 is 0. + */ + qla8044_wr_reg(ha, LEG_INTR_TRIG_OFFSET, 0); + do { + leg_int_ptr = qla8044_rd_reg(ha, LEG_INTR_PTR_OFFSET); + if ((leg_int_ptr & (PF_BITS_MASK)) != pf_bit) + break; + } while (leg_int_ptr & (LEG_INT_PTR_B30)); + + reg = &ha->iobase->isp82; + spin_lock_irqsave(&ha->hardware_lock, flags); + for (iter = 1; iter--; ) { + + if (RD_REG_DWORD(®->host_int)) { + stat = RD_REG_DWORD(®->host_status); + if ((stat & HSRX_RISC_INT) == 0) + break; + + 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(®->mailbox_out[1]); + mb[2] = RD_REG_WORD(®->mailbox_out[2]); + mb[3] = RD_REG_WORD(®->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, 0xb146, + "Unrecognized interrupt type " + "(%d).\n", stat & 0xff); + break; + } + } + WRT_REG_DWORD(®->host_int, 0); + } + + qla2x00_handle_mbx_completion(ha, status); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return IRQ_HANDLED; +} + +static int +qla8044_idc_dontreset(struct qla_hw_data *ha) +{ + uint32_t idc_ctrl; + + idc_ctrl = qla8044_rd_reg(ha, QLA8044_IDC_DRV_CTRL); + return idc_ctrl & DONTRESET_BIT0; +} + +static void +qla8044_clear_rst_ready(scsi_qla_host_t *vha) +{ + uint32_t drv_state; + + drv_state = qla8044_rd_direct(vha, QLA8044_CRB_DRV_STATE_INDEX); + + /* + * For ISP8044, drv_active register has 1 bit per function, + * shift 1 by func_num to set a bit for the function. + * For ISP82xx, drv_active has 4 bits per function + */ + drv_state &= ~(1 << vha->hw->portnum); + + ql_dbg(ql_dbg_p3p, vha, 0xb13d, + "drv_state: 0x%08x\n", drv_state); + qla8044_wr_direct(vha, QLA8044_CRB_DRV_STATE_INDEX, drv_state); +} + +int +qla8044_abort_isp(scsi_qla_host_t *vha) +{ + int rval; + uint32_t dev_state; + struct qla_hw_data *ha = vha->hw; + + qla8044_idc_lock(ha); + dev_state = qla8044_rd_direct(vha, QLA8044_CRB_DEV_STATE_INDEX); + + if (ql2xdontresethba) + qla8044_set_idc_dontreset(vha); + + /* If device_state is NEED_RESET, go ahead with + * Reset,irrespective of ql2xdontresethba. This is to allow a + * non-reset-owner to force a reset. Non-reset-owner sets + * the IDC_CTRL BIT0 to prevent Reset-owner from doing a Reset + * and then forces a Reset by setting device_state to + * NEED_RESET. */ + if (dev_state == QLA8XXX_DEV_READY) { + /* If IDC_CTRL DONTRESETHBA_BIT0 is set don't do reset + * recovery */ + if (qla8044_idc_dontreset(ha) == DONTRESET_BIT0) { + ql_dbg(ql_dbg_p3p, vha, 0xb13e, + "Reset recovery disabled\n"); + rval = QLA_FUNCTION_FAILED; + goto exit_isp_reset; + } + + ql_dbg(ql_dbg_p3p, vha, 0xb140, + "HW State: NEED RESET\n"); + qla8044_wr_direct(vha, QLA8044_CRB_DEV_STATE_INDEX, + QLA8XXX_DEV_NEED_RESET); + } + + /* For ISP8044, Reset owner is NIC, iSCSI or FCOE based on priority + * and which drivers are present. Unlike ISP82XX, the function setting + * NEED_RESET, may not be the Reset owner. */ + qla83xx_reset_ownership(vha); + + qla8044_idc_unlock(ha); + rval = qla8044_device_state_handler(vha); + qla8044_idc_lock(ha); + qla8044_clear_rst_ready(vha); + +exit_isp_reset: + qla8044_idc_unlock(ha); + if (rval == QLA_SUCCESS) { + ha->flags.isp82xx_fw_hung = 0; + ha->flags.nic_core_reset_hdlr_active = 0; + rval = qla82xx_restart_isp(vha); + } + + return rval; +} + +void +qla8044_fw_dump(scsi_qla_host_t *vha, int hardware_locked) +{ + struct qla_hw_data *ha = vha->hw; + + if (!ha->allow_cna_fw_dump) + return; + + scsi_block_requests(vha->host); + ha->flags.isp82xx_no_md_cap = 1; + qla8044_idc_lock(ha); + qla82xx_set_reset_owner(vha); + qla8044_idc_unlock(ha); + qla2x00_wait_for_chip_reset(vha); + scsi_unblock_requests(vha->host); +} diff --git a/drivers/scsi/qla2xxx/qla_nx2.h b/drivers/scsi/qla2xxx/qla_nx2.h new file mode 100644 index 00000000000..ada36057d7c --- /dev/null +++ b/drivers/scsi/qla2xxx/qla_nx2.h @@ -0,0 +1,599 @@ +/* + * QLogic Fibre Channel HBA Driver + * Copyright (c) 2003-2014 QLogic Corporation + * + * See LICENSE.qla2xxx for copyright and licensing details. + */ + +#ifndef __QLA_NX2_H +#define __QLA_NX2_H + +#define QSNT_ACK_TOV 30 +#define INTENT_TO_RECOVER 0x01 +#define PROCEED_TO_RECOVER 0x02 +#define IDC_LOCK_RECOVERY_OWNER_MASK 0x3C +#define IDC_LOCK_RECOVERY_STATE_MASK 0x3 +#define IDC_LOCK_RECOVERY_STATE_SHIFT_BITS 2 + +#define QLA8044_DRV_LOCK_MSLEEP 200 +#define QLA8044_ADDR_DDR_NET (0x0000000000000000ULL) +#define QLA8044_ADDR_DDR_NET_MAX (0x000000000fffffffULL) + +#define MD_MIU_TEST_AGT_WRDATA_LO 0x410000A0 +#define MD_MIU_TEST_AGT_WRDATA_HI 0x410000A4 +#define MD_MIU_TEST_AGT_WRDATA_ULO 0x410000B0 +#define MD_MIU_TEST_AGT_WRDATA_UHI 0x410000B4 +#define MD_MIU_TEST_AGT_RDDATA_LO 0x410000A8 +#define MD_MIU_TEST_AGT_RDDATA_HI 0x410000AC +#define MD_MIU_TEST_AGT_RDDATA_ULO 0x410000B8 +#define MD_MIU_TEST_AGT_RDDATA_UHI 0x410000BC + +/* MIU_TEST_AGT_CTRL flags. work for SIU as well */ +#define MIU_TA_CTL_WRITE_ENABLE (MIU_TA_CTL_WRITE | MIU_TA_CTL_ENABLE) +#define MIU_TA_CTL_WRITE_START (MIU_TA_CTL_WRITE | MIU_TA_CTL_ENABLE | \ + MIU_TA_CTL_START) +#define MIU_TA_CTL_START_ENABLE (MIU_TA_CTL_START | MIU_TA_CTL_ENABLE) + +/* 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 QLA8044_P2_ADDR_PCIE (0x0000000800000000ULL) +#define QLA8044_P3_ADDR_PCIE (0x0000008000000000ULL) +#define QLA8044_ADDR_PCIE_MAX (0x0000000FFFFFFFFFULL) +#define QLA8044_ADDR_OCM0 (0x0000000200000000ULL) +#define QLA8044_ADDR_OCM0_MAX (0x00000002000fffffULL) +#define QLA8044_ADDR_OCM1 (0x0000000200400000ULL) +#define QLA8044_ADDR_OCM1_MAX (0x00000002004fffffULL) +#define QLA8044_ADDR_QDR_NET (0x0000000300000000ULL) +#define QLA8044_P2_ADDR_QDR_NET_MAX (0x00000003001fffffULL) +#define QLA8044_P3_ADDR_QDR_NET_MAX (0x0000000303ffffffULL) +#define QLA8044_ADDR_QDR_NET_MAX (0x0000000307ffffffULL) +#define QLA8044_PCI_CRBSPACE ((unsigned long)0x06000000) +#define QLA8044_PCI_DIRECT_CRB ((unsigned long)0x04400000) +#define QLA8044_PCI_CAMQM ((unsigned long)0x04800000) +#define QLA8044_PCI_CAMQM_MAX ((unsigned long)0x04ffffff) +#define QLA8044_PCI_DDR_NET ((unsigned long)0x00000000) +#define QLA8044_PCI_QDR_NET ((unsigned long)0x04000000) +#define QLA8044_PCI_QDR_NET_MAX ((unsigned long)0x043fffff) + +/* PCI Windowing for DDR regions. */ +#define QLA8044_ADDR_IN_RANGE(addr, low, high) \ + (((addr) <= (high)) && ((addr) >= (low))) + +/* Indirectly Mapped Registers */ +#define QLA8044_FLASH_SPI_STATUS 0x2808E010 +#define QLA8044_FLASH_SPI_CONTROL 0x2808E014 +#define QLA8044_FLASH_STATUS 0x42100004 +#define QLA8044_FLASH_CONTROL 0x42110004 +#define QLA8044_FLASH_ADDR 0x42110008 +#define QLA8044_FLASH_WRDATA 0x4211000C +#define QLA8044_FLASH_RDDATA 0x42110018 +#define QLA8044_FLASH_DIRECT_WINDOW 0x42110030 +#define QLA8044_FLASH_DIRECT_DATA(DATA) (0x42150000 | (0x0000FFFF&DATA)) + +/* Flash access regs */ +#define QLA8044_FLASH_LOCK 0x3850 +#define QLA8044_FLASH_UNLOCK 0x3854 +#define QLA8044_FLASH_LOCK_ID 0x3500 + +/* Driver Lock regs */ +#define QLA8044_DRV_LOCK 0x3868 +#define QLA8044_DRV_UNLOCK 0x386C +#define QLA8044_DRV_LOCK_ID 0x3504 +#define QLA8044_DRV_LOCKRECOVERY 0x379C + +/* IDC version */ +#define QLA8044_IDC_VER_MAJ_VALUE 0x1 +#define QLA8044_IDC_VER_MIN_VALUE 0x0 + +/* IDC Registers : Driver Coexistence Defines */ +#define QLA8044_CRB_IDC_VER_MAJOR 0x3780 +#define QLA8044_CRB_IDC_VER_MINOR 0x3798 +#define QLA8044_IDC_DRV_AUDIT 0x3794 +#define QLA8044_SRE_SHIM_CONTROL 0x0D200284 +#define QLA8044_PORT0_RXB_PAUSE_THRS 0x0B2003A4 +#define QLA8044_PORT1_RXB_PAUSE_THRS 0x0B2013A4 +#define QLA8044_PORT0_RXB_TC_MAX_CELL 0x0B200388 +#define QLA8044_PORT1_RXB_TC_MAX_CELL 0x0B201388 +#define QLA8044_PORT0_RXB_TC_STATS 0x0B20039C +#define QLA8044_PORT1_RXB_TC_STATS 0x0B20139C +#define QLA8044_PORT2_IFB_PAUSE_THRS 0x0B200704 +#define QLA8044_PORT3_IFB_PAUSE_THRS 0x0B201704 + +/* set value to pause threshold value */ +#define QLA8044_SET_PAUSE_VAL 0x0 +#define QLA8044_SET_TC_MAX_CELL_VAL 0x03FF03FF +#define QLA8044_PEG_HALT_STATUS1 0x34A8 +#define QLA8044_PEG_HALT_STATUS2 0x34AC +#define QLA8044_PEG_ALIVE_COUNTER 0x34B0 /* FW_HEARTBEAT */ +#define QLA8044_FW_CAPABILITIES 0x3528 +#define QLA8044_CRB_DRV_ACTIVE 0x3788 /* IDC_DRV_PRESENCE */ +#define QLA8044_CRB_DEV_STATE 0x3784 /* IDC_DEV_STATE */ +#define QLA8044_CRB_DRV_STATE 0x378C /* IDC_DRV_ACK */ +#define QLA8044_CRB_DRV_SCRATCH 0x3548 +#define QLA8044_CRB_DEV_PART_INFO1 0x37E0 +#define QLA8044_CRB_DEV_PART_INFO2 0x37E4 +#define QLA8044_FW_VER_MAJOR 0x3550 +#define QLA8044_FW_VER_MINOR 0x3554 +#define QLA8044_FW_VER_SUB 0x3558 +#define QLA8044_NPAR_STATE 0x359C +#define QLA8044_FW_IMAGE_VALID 0x35FC +#define QLA8044_CMDPEG_STATE 0x3650 +#define QLA8044_ASIC_TEMP 0x37B4 +#define QLA8044_FW_API 0x356C +#define QLA8044_DRV_OP_MODE 0x3570 +#define QLA8044_CRB_WIN_BASE 0x3800 +#define QLA8044_CRB_WIN_FUNC(f) (QLA8044_CRB_WIN_BASE+((f)*4)) +#define QLA8044_SEM_LOCK_BASE 0x3840 +#define QLA8044_SEM_UNLOCK_BASE 0x3844 +#define QLA8044_SEM_LOCK_FUNC(f) (QLA8044_SEM_LOCK_BASE+((f)*8)) +#define QLA8044_SEM_UNLOCK_FUNC(f) (QLA8044_SEM_UNLOCK_BASE+((f)*8)) +#define QLA8044_LINK_STATE(f) (0x3698+((f) > 7 ? 4 : 0)) +#define QLA8044_LINK_SPEED(f) (0x36E0+(((f) >> 2) * 4)) +#define QLA8044_MAX_LINK_SPEED(f) (0x36F0+(((f) / 4) * 4)) +#define QLA8044_LINK_SPEED_FACTOR 10 +#define QLA8044_FUN7_ACTIVE_INDEX 0x80 + +/* FLASH API Defines */ +#define QLA8044_FLASH_MAX_WAIT_USEC 100 +#define QLA8044_FLASH_LOCK_TIMEOUT 10000 +#define QLA8044_FLASH_SECTOR_SIZE 65536 +#define QLA8044_DRV_LOCK_TIMEOUT 2000 +#define QLA8044_FLASH_SECTOR_ERASE_CMD 0xdeadbeef +#define QLA8044_FLASH_WRITE_CMD 0xdacdacda +#define QLA8044_FLASH_BUFFER_WRITE_CMD 0xcadcadca +#define QLA8044_FLASH_READ_RETRY_COUNT 2000 +#define QLA8044_FLASH_STATUS_READY 0x6 +#define QLA8044_FLASH_BUFFER_WRITE_MIN 2 +#define QLA8044_FLASH_BUFFER_WRITE_MAX 64 +#define QLA8044_FLASH_STATUS_REG_POLL_DELAY 1 +#define QLA8044_ERASE_MODE 1 +#define QLA8044_WRITE_MODE 2 +#define QLA8044_DWORD_WRITE_MODE 3 +#define QLA8044_GLOBAL_RESET 0x38CC +#define QLA8044_WILDCARD 0x38F0 +#define QLA8044_INFORMANT 0x38FC +#define QLA8044_HOST_MBX_CTRL 0x3038 +#define QLA8044_FW_MBX_CTRL 0x303C +#define QLA8044_BOOTLOADER_ADDR 0x355C +#define QLA8044_BOOTLOADER_SIZE 0x3560 +#define QLA8044_FW_IMAGE_ADDR 0x3564 +#define QLA8044_MBX_INTR_ENABLE 0x1000 +#define QLA8044_MBX_INTR_MASK 0x1200 + +/* IDC Control Register bit defines */ +#define DONTRESET_BIT0 0x1 +#define GRACEFUL_RESET_BIT1 0x2 + +/* ISP8044 PEG_HALT_STATUS1 bits */ +#define QLA8044_HALT_STATUS_INFORMATIONAL (0x1 << 29) +#define QLA8044_HALT_STATUS_FW_RESET (0x2 << 29) +#define QLA8044_HALT_STATUS_UNRECOVERABLE (0x4 << 29) + +/* Firmware image definitions */ +#define QLA8044_BOOTLOADER_FLASH_ADDR 0x10000 +#define QLA8044_BOOT_FROM_FLASH 0 +#define QLA8044_IDC_PARAM_ADDR 0x3e8020 + +/* FLASH related definitions */ +#define QLA8044_OPTROM_BURST_SIZE 0x100 +#define QLA8044_MAX_OPTROM_BURST_DWORDS (QLA8044_OPTROM_BURST_SIZE / 4) +#define QLA8044_MIN_OPTROM_BURST_DWORDS 2 +#define QLA8044_SECTOR_SIZE (64 * 1024) + +#define QLA8044_FLASH_SPI_CTL 0x4 +#define QLA8044_FLASH_FIRST_TEMP_VAL 0x00800000 +#define QLA8044_FLASH_SECOND_TEMP_VAL 0x00800001 +#define QLA8044_FLASH_FIRST_MS_PATTERN 0x43 +#define QLA8044_FLASH_SECOND_MS_PATTERN 0x7F +#define QLA8044_FLASH_LAST_MS_PATTERN 0x7D +#define QLA8044_FLASH_STATUS_WRITE_DEF_SIG 0xFD0100 +#define QLA8044_FLASH_SECOND_ERASE_MS_VAL 0x5 +#define QLA8044_FLASH_ERASE_SIG 0xFD0300 +#define QLA8044_FLASH_LAST_ERASE_MS_VAL 0x3D + +/* Reset template definitions */ +#define QLA8044_MAX_RESET_SEQ_ENTRIES 16 +#define QLA8044_RESTART_TEMPLATE_SIZE 0x2000 +#define QLA8044_RESET_TEMPLATE_ADDR 0x4F0000 +#define QLA8044_RESET_SEQ_VERSION 0x0101 + +/* Reset template entry opcodes */ +#define OPCODE_NOP 0x0000 +#define OPCODE_WRITE_LIST 0x0001 +#define OPCODE_READ_WRITE_LIST 0x0002 +#define OPCODE_POLL_LIST 0x0004 +#define OPCODE_POLL_WRITE_LIST 0x0008 +#define OPCODE_READ_MODIFY_WRITE 0x0010 +#define OPCODE_SEQ_PAUSE 0x0020 +#define OPCODE_SEQ_END 0x0040 +#define OPCODE_TMPL_END 0x0080 +#define OPCODE_POLL_READ_LIST 0x0100 + +/* Template Header */ +#define RESET_TMPLT_HDR_SIGNATURE 0xCAFE +#define QLA8044_IDC_DRV_CTRL 0x3790 +#define AF_8044_NO_FW_DUMP 27 /* 0x08000000 */ + +#define MINIDUMP_SIZE_36K 36864 + +struct qla8044_reset_template_hdr { + uint16_t version; + uint16_t signature; + uint16_t size; + uint16_t entries; + uint16_t hdr_size; + uint16_t checksum; + uint16_t init_seq_offset; + uint16_t start_seq_offset; +} __packed; + +/* Common Entry Header. */ +struct qla8044_reset_entry_hdr { + uint16_t cmd; + uint16_t size; + uint16_t count; + uint16_t delay; +} __packed; + +/* Generic poll entry type. */ +struct qla8044_poll { + uint32_t test_mask; + uint32_t test_value; +} __packed; + +/* Read modify write entry type. */ +struct qla8044_rmw { + uint32_t test_mask; + uint32_t xor_value; + uint32_t or_value; + uint8_t shl; + uint8_t shr; + uint8_t index_a; + uint8_t rsvd; +} __packed; + +/* Generic Entry Item with 2 DWords. */ +struct qla8044_entry { + uint32_t arg1; + uint32_t arg2; +} __packed; + +/* Generic Entry Item with 4 DWords.*/ +struct qla8044_quad_entry { + uint32_t dr_addr; + uint32_t dr_value; + uint32_t ar_addr; + uint32_t ar_value; +} __packed; + +struct qla8044_reset_template { + int seq_index; + int seq_error; + int array_index; + uint32_t array[QLA8044_MAX_RESET_SEQ_ENTRIES]; + uint8_t *buff; + uint8_t *stop_offset; + uint8_t *start_offset; + uint8_t *init_offset; + struct qla8044_reset_template_hdr *hdr; + uint8_t seq_end; + uint8_t template_end; +}; + +/* Driver_code is for driver to write some info about the entry + * currently not used. + */ +struct qla8044_minidump_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; + +/* Read CRB entry header */ +struct qla8044_minidump_entry_crb { + struct qla8044_minidump_entry_hdr 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; + +struct qla8044_minidump_entry_cache { + struct qla8044_minidump_entry_hdr 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 qla8044_minidump_entry_rdocm { + struct qla8044_minidump_entry_hdr 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; +} __packed; + +/* Read Memory */ +struct qla8044_minidump_entry_rdmem { + struct qla8044_minidump_entry_hdr h; + uint32_t rsvd[6]; + uint32_t read_addr; + uint32_t read_data_size; +}; + +/* Read Memory: For Pex-DMA */ +struct qla8044_minidump_entry_rdmem_pex_dma { + struct qla8044_minidump_entry_hdr h; + uint32_t desc_card_addr; + uint16_t dma_desc_cmd; + uint8_t rsvd[2]; + uint32_t start_dma_cmd; + uint8_t rsvd2[12]; + uint32_t read_addr; + uint32_t read_data_size; +} __packed; + +/* Read ROM */ +struct qla8044_minidump_entry_rdrom { + struct qla8044_minidump_entry_hdr h; + uint32_t rsvd[6]; + uint32_t read_addr; + uint32_t read_data_size; +} __packed; + +/* Mux entry */ +struct qla8044_minidump_entry_mux { + struct qla8044_minidump_entry_hdr 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; + +/* Queue entry */ +struct qla8044_minidump_entry_queue { + struct qla8044_minidump_entry_hdr 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; + +/* POLLRD Entry */ +struct qla8044_minidump_entry_pollrd { + struct qla8044_minidump_entry_hdr h; + uint32_t select_addr; + uint32_t read_addr; + uint32_t select_value; + uint16_t select_value_stride; + uint16_t op_count; + uint32_t poll_wait; + uint32_t poll_mask; + uint32_t data_size; + uint32_t rsvd_1; +} __packed; + +struct qla8044_minidump_entry_rddfe { + struct qla8044_minidump_entry_hdr h; + uint32_t addr_1; + uint32_t value; + uint8_t stride; + uint8_t stride2; + uint16_t count; + uint32_t poll; + uint32_t mask; + uint32_t modify_mask; + uint32_t data_size; + uint32_t rsvd; + +} __packed; + +struct qla8044_minidump_entry_rdmdio { + struct qla8044_minidump_entry_hdr h; + + uint32_t addr_1; + uint32_t addr_2; + uint32_t value_1; + uint8_t stride_1; + uint8_t stride_2; + uint16_t count; + uint32_t poll; + uint32_t mask; + uint32_t value_2; + uint32_t data_size; + +} __packed; + +struct qla8044_minidump_entry_pollwr { + struct qla8044_minidump_entry_hdr h; + uint32_t addr_1; + uint32_t addr_2; + uint32_t value_1; + uint32_t value_2; + uint32_t poll; + uint32_t mask; + uint32_t data_size; + uint32_t rsvd; + +} __packed; + +/* RDMUX2 Entry */ +struct qla8044_minidump_entry_rdmux2 { + struct qla8044_minidump_entry_hdr h; + uint32_t select_addr_1; + uint32_t select_addr_2; + uint32_t select_value_1; + uint32_t select_value_2; + uint32_t op_count; + uint32_t select_value_mask; + uint32_t read_addr; + uint8_t select_value_stride; + uint8_t data_size; + uint8_t rsvd[2]; +} __packed; + +/* POLLRDMWR Entry */ +struct qla8044_minidump_entry_pollrdmwr { + struct qla8044_minidump_entry_hdr h; + uint32_t addr_1; + uint32_t addr_2; + uint32_t value_1; + uint32_t value_2; + uint32_t poll_wait; + uint32_t poll_mask; + uint32_t modify_mask; + uint32_t data_size; +} __packed; + +/* IDC additional information */ +struct qla8044_idc_information { + uint32_t request_desc; /* IDC request descriptor */ + uint32_t info1; /* IDC additional info */ + uint32_t info2; /* IDC additional info */ + uint32_t info3; /* IDC additional info */ +} __packed; + +enum qla_regs { + QLA8044_PEG_HALT_STATUS1_INDEX = 0, + QLA8044_PEG_HALT_STATUS2_INDEX, + QLA8044_PEG_ALIVE_COUNTER_INDEX, + QLA8044_CRB_DRV_ACTIVE_INDEX, + QLA8044_CRB_DEV_STATE_INDEX, + QLA8044_CRB_DRV_STATE_INDEX, + QLA8044_CRB_DRV_SCRATCH_INDEX, + QLA8044_CRB_DEV_PART_INFO_INDEX, + QLA8044_CRB_DRV_IDC_VERSION_INDEX, + QLA8044_FW_VERSION_MAJOR_INDEX, + QLA8044_FW_VERSION_MINOR_INDEX, + QLA8044_FW_VERSION_SUB_INDEX, + QLA8044_CRB_CMDPEG_STATE_INDEX, + QLA8044_CRB_TEMP_STATE_INDEX, +} __packed; + +#define CRB_REG_INDEX_MAX 14 +#define CRB_CMDPEG_CHECK_RETRY_COUNT 60 +#define CRB_CMDPEG_CHECK_DELAY 500 + +static const uint32_t qla8044_reg_tbl[] = { + QLA8044_PEG_HALT_STATUS1, + QLA8044_PEG_HALT_STATUS2, + QLA8044_PEG_ALIVE_COUNTER, + QLA8044_CRB_DRV_ACTIVE, + QLA8044_CRB_DEV_STATE, + QLA8044_CRB_DRV_STATE, + QLA8044_CRB_DRV_SCRATCH, + QLA8044_CRB_DEV_PART_INFO1, + QLA8044_CRB_IDC_VER_MAJOR, + QLA8044_FW_VER_MAJOR, + QLA8044_FW_VER_MINOR, + QLA8044_FW_VER_SUB, + QLA8044_CMDPEG_STATE, + QLA8044_ASIC_TEMP, +}; + +/* MiniDump Structures */ + +/* Driver_code is for driver to write some info about the entry + * currently not used. + */ +#define QLA8044_SS_OCM_WNDREG_INDEX 3 +#define QLA8044_DBG_STATE_ARRAY_LEN 16 +#define QLA8044_DBG_CAP_SIZE_ARRAY_LEN 8 +#define QLA8044_DBG_RSVD_ARRAY_LEN 8 +#define QLA8044_DBG_OCM_WNDREG_ARRAY_LEN 16 +#define QLA8044_SS_PCI_INDEX 0 +#define QLA8044_RDDFE 38 +#define QLA8044_RDMDIO 39 +#define QLA8044_POLLWR 40 + +struct qla8044_minidump_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 checksum; + + uint32_t driver_capture_mask; + uint32_t driver_info_word2; + uint32_t driver_info_word3; + uint32_t driver_info_word4; + + uint32_t saved_state_array[QLA8044_DBG_STATE_ARRAY_LEN]; + uint32_t capture_size_array[QLA8044_DBG_CAP_SIZE_ARRAY_LEN]; + uint32_t ocm_window_reg[QLA8044_DBG_OCM_WNDREG_ARRAY_LEN]; +}; + +struct qla8044_pex_dma_descriptor { + struct { + uint32_t read_data_size; /* 0-23: size, 24-31: rsvd */ + uint8_t rsvd[2]; + uint16_t dma_desc_cmd; + } cmd; + uint64_t src_addr; + uint64_t dma_bus_addr; /*0-3: desc-cmd, 4-7: pci-func, 8-15: desc-cmd*/ + uint8_t rsvd[24]; +} __packed; + +#endif diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index f461925a9df..d96bfb55e57 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -13,12 +13,13 @@ #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> +#include "qla_target.h" + /* * Driver version */ @@ -35,6 +36,17 @@ 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; + +static int ql2xenableclass2; +module_param(ql2xenableclass2, int, S_IRUGO|S_IRUSR); +MODULE_PARM_DESC(ql2xenableclass2, + "Specify if Class 2 operations are supported from the very " + "beginning. Default is 0 - class 2 not supported."); + int ql2xlogintimeout = 20; module_param(ql2xlogintimeout, int, S_IRUGO); @@ -69,8 +81,22 @@ MODULE_PARM_DESC(ql2xallocfwdump, 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, " - "Default is 0 - no logging. 1 - log errors."); + "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\t0x00008000 - Verbose. 0x00004000 - Target.\n" + "\t\t0x00002000 - Target Mgmt. 0x00001000 - Target TMF.\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); @@ -78,32 +104,36 @@ 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 +#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."); + "Maximum queue depth to set for each LUN. " + "Default is 32."); -/* Do not change the value of this after module load */ -int ql2xenabledif = 1; -module_param(ql2xenabledif, int, S_IRUGO|S_IWUSR); +int ql2xenabledif = 2; +module_param(ql2xenabledif, int, S_IRUGO); MODULE_PARM_DESC(ql2xenabledif, - " Enable T10-CRC-DIF " - " Default is 0 - No DIF Support. 1 - Enable it"); + " Enable T10-CRC-DIF:\n" + " Default is 2.\n" + " 0 -- No DIF Support\n" + " 1 -- Enable DIF for all types\n" + " 2 -- Enable DIF for all types, except Type 0.\n"); -int ql2xenablehba_err_chk; +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" - " Default is 0 - Error isolation disabled, 1 - Enable it"); + " Enable T10-CRC-DIF Error isolation by HBA:\n" + " Default is 2.\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); @@ -126,10 +156,10 @@ MODULE_PARM_DESC(ql2xmultique_tag, "Set it to 1 to turn on the cpu affinity."); int ql2xfwloadbin; -module_param(ql2xfwloadbin, int, S_IRUGO); +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" + "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"); @@ -141,11 +171,11 @@ MODULE_PARM_DESC(ql2xetsenable, "Default is 0 - skip ETS enablement."); int ql2xdbwr = 1; -module_param(ql2xdbwr, int, S_IRUGO); +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"); + "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); @@ -166,11 +196,11 @@ MODULE_PARM_DESC(ql2xasynctmfenable, "Default is 0 - Issue TM IOCBs via mailbox mechanism."); int ql2xdontresethba; -module_param(ql2xdontresethba, int, S_IRUGO); +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"); + "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); @@ -178,6 +208,19 @@ 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 */ @@ -195,6 +238,7 @@ 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); +static void qla2x00_free_device(scsi_qla_host_t *); struct scsi_host_template qla2xxx_driver_template = { .module = THIS_MODULE, @@ -222,6 +266,8 @@ struct scsi_host_template qla2xxx_driver_template = { .max_sectors = 0xFFFF, .shost_attrs = qla2x00_host_attrs, + + .supported_mode = MODE_INITIATOR, }; static struct scsi_transport_template *qla2xxx_transport_template = NULL; @@ -247,8 +293,11 @@ 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) + 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); } @@ -268,26 +317,33 @@ 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 void qla2x00_sp_free_dma(srb_t *); /* -------------------------------------------------------------------------- */ -static int qla2x00_alloc_queues(struct qla_hw_data *ha) +static int qla2x00_alloc_queues(struct qla_hw_data *ha, struct req_que *req, + struct rsp_que *rsp) { + 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) { - qla_printk(KERN_WARNING, ha, - "Unable to allocate memory for request queue ptrs\n"); + 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) { - qla_printk(KERN_WARNING, ha, - "Unable to allocate memory for response queue ptrs\n"); + ql_log(ql_log_fatal, vha, 0x003c, + "Unable to allocate memory for response queue ptrs.\n"); goto fail_rsp_map; } + /* + * Make sure we record at least the request and response queue zero in + * case we need to free them if part of the probe fails. + */ + ha->rsp_q_map[0] = rsp; + ha->req_q_map[0] = req; set_bit(0, ha->rsp_qid_map); set_bit(0, ha->req_qid_map); return 1; @@ -301,22 +357,35 @@ fail_req_map: static void qla2x00_free_req_que(struct qla_hw_data *ha, struct req_que *req) { - if (req && req->ring) + if (IS_QLAFX00(ha)) { + if (req && req->ring_fx00) + dma_free_coherent(&ha->pdev->dev, + (req->length_fx00 + 1) * sizeof(request_t), + req->ring_fx00, req->dma_fx00); + } else if (req && req->ring) dma_free_coherent(&ha->pdev->dev, (req->length + 1) * sizeof(request_t), req->ring, req->dma); + if (req) + kfree(req->outstanding_cmds); + kfree(req); req = NULL; } static void qla2x00_free_rsp_que(struct qla_hw_data *ha, struct rsp_que *rsp) { - if (rsp && rsp->ring) + if (IS_QLAFX00(ha)) { + if (rsp && rsp->ring) + dma_free_coherent(&ha->pdev->dev, + (rsp->length_fx00 + 1) * sizeof(request_t), + rsp->ring_fx00, rsp->dma_fx00); + } else if (rsp && rsp->ring) { dma_free_coherent(&ha->pdev->dev, (rsp->length + 1) * sizeof(response_t), rsp->ring, rsp->dma); - + } kfree(rsp); rsp = NULL; } @@ -349,8 +418,8 @@ static int qla25xx_setup_mode(struct scsi_qla_host *vha) struct qla_hw_data *ha = vha->hw; if (!(ha->fw_attributes & BIT_6)) { - qla_printk(KERN_INFO, ha, - "Firmware is not multi-queue capable\n"); + ql_log(ql_log_warn, vha, 0x00d8, + "Firmware is not multi-queue capable.\n"); goto fail; } if (ql2xmultique_tag) { @@ -359,8 +428,8 @@ static int qla25xx_setup_mode(struct scsi_qla_host *vha) req = qla25xx_create_req_que(ha, options, 0, 0, -1, QLA_DEFAULT_QUE_QOS); if (!req) { - qla_printk(KERN_WARNING, ha, - "Can't create request queue\n"); + 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); @@ -369,23 +438,27 @@ static int qla25xx_setup_mode(struct scsi_qla_host *vha) for (ques = 1; ques < ha->max_rsp_queues; ques++) { ret = qla25xx_create_rsp_que(ha, options, 0, 0, req); if (!ret) { - qla_printk(KERN_WARNING, ha, - "Response Queue create failed\n"); + ql_log(ql_log_warn, vha, 0x00e8, + "Failed to create response queue.\n"); goto fail2; } } ha->flags.cpu_affinity_enabled = 1; - - DEBUG2(qla_printk(KERN_INFO, ha, - "CPU affinity mode enabled, no. of response" - " queues:%d, no. of request queues:%d\n", - ha->max_rsp_queues, ha->max_req_queues)); + 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); @@ -424,26 +497,30 @@ 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) { + if (pci_is_pcie(ha->pdev)) { char lwstr[6]; - uint16_t pcie_lstat, lspeed, lwidth; + uint32_t 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; + pcie_capability_read_dword(ha->pdev, PCI_EXP_LNKCAP, &lstat); + lspeed = lstat & PCI_EXP_LNKCAP_SLS; + lwidth = (lstat & PCI_EXP_LNKCAP_MLW) >> 4; strcpy(str, "PCIe ("); - if (lspeed == 1) + switch (lspeed) { + case 1: strcat(str, "2.5GT/s "); - else if (lspeed == 2) + break; + case 2: strcat(str, "5.0GT/s "); - else + break; + case 3: + strcat(str, "8.0GT/s "); + break; + default: strcat(str, "<unknown> "); + break; + } snprintf(lwstr, sizeof(lwstr), "x%d)", lwidth); strcat(str, lwstr); @@ -518,27 +595,80 @@ qla24xx_fw_version_str(struct scsi_qla_host *vha, char *str) return str; } -static inline srb_t * -qla2x00_get_new_sp(scsi_qla_host_t *vha, fc_port_t *fcport, - struct scsi_cmnd *cmd) +void +qla2x00_sp_free_dma(void *vha, void *ptr) { - srb_t *sp; - struct qla_hw_data *ha = vha->hw; + 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); - sp = mempool_alloc(ha->srb_mempool, GFP_ATOMIC); - if (!sp) - return sp; + if (sp->flags & SRB_DMA_VALID) { + scsi_dma_unmap(cmd); + sp->flags &= ~SRB_DMA_VALID; + } - atomic_set(&sp->ref_count, 1); - sp->fcport = fcport; - sp->cmd = cmd; - sp->flags = 0; - CMD_SP(cmd) = (void *)sp; - sp->ctx = NULL; + 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, NULL); + 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; + } - return sp; + 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; + qla2x00_rel_sp(sp->fcport->vha, sp); +} + +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); } +/* If we are SP1 here, we need to still take and release the host_lock as SP1 + * does not have the changes necessary to avoid taking host->host_lock. + */ static int qla2xxx_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) { @@ -551,49 +681,77 @@ qla2xxx_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) int rval; if (ha->flags.eeh_busy) { - if (ha->flags.pci_channel_io_perm_failure) + if (ha->flags.pci_channel_io_perm_failure) { + ql_dbg(ql_dbg_aer, vha, 0x9010, + "PCI Channel IO permanent failure, exiting " + "cmd=%p.\n", cmd); cmd->result = DID_NO_CONNECT << 16; - else + } else { + ql_dbg(ql_dbg_aer, vha, 0x9011, + "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 + ql_dbg_verbose, 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) { - DEBUG2(qla_printk(KERN_ERR, ha, - "DIF Cap Not Reg, fail DIF capable cmd's:%x\n", - cmd->cmnd[0])); + 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_new_sp(base_vha, fcport, cmd); + sp = qla2x00_get_sp(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) + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_io + ql_dbg_verbose, 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(sp); - mempool_free(sp, ha->srb_mempool); + qla2x00_sp_free_dma(ha, sp); qc24_host_busy: return SCSI_MLQUEUE_HOST_BUSY; @@ -623,14 +781,15 @@ 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)) +#define ABORT_WAIT_ITER ((2 * 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) { - DEBUG17(qla_printk(KERN_WARNING, ha, "return:eh_wait\n")); + ql_dbg(ql_dbg_taskm, vha, 0x8005, + "Return:eh_wait.\n"); return ret; } @@ -685,11 +844,8 @@ qla2x00_wait_for_hba_online(scsi_qla_host_t *vha) } /* - * 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. + * qla2x00_wait_for_hba_ready + * Wait till the HBA is ready before doing driver unload * * Input: * ha - pointer to host adapter structure @@ -698,34 +854,15 @@ qla2x00_wait_for_hba_online(scsi_qla_host_t *vha) * 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) +static void +qla2x00_wait_for_hba_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)) + while ((!(vha->flags.online) || ha->dpc_active || + ha->flags.mbox_busy)) msleep(1000); - - if (base_vha->flags.online && ha->optrom_state == QLA_SWAITING) - return_status = QLA_SUCCESS; - else - return_status = QLA_FUNCTION_FAILED; - - DEBUG2(printk("%s return_status=%d\n", __func__, return_status)); - - return return_status; } int @@ -756,49 +893,6 @@ qla2x00_wait_for_chip_reset(scsi_qla_host_t *vha) return return_status; } -/* - * qla2x00_wait_for_loop_ready - * Wait for MAX_LOOP_TIMEOUT(5 min) value for loop - * to be in LOOP_READY state. - * Input: - * ha - pointer to host adapter structure - * - * Note: - * Does context switching-Release SPIN_LOCK - * (if any) before calling this routine. - * - * - * Return: - * Success (LOOP_READY) : 0 - * Failed (LOOP_NOT_READY) : 1 - */ -static inline int -qla2x00_wait_for_loop_ready(scsi_qla_host_t *vha) -{ - int return_status = QLA_SUCCESS; - unsigned long loop_timeout ; - struct qla_hw_data *ha = vha->hw; - scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); - - /* wait for 5 min at the max for loop to be ready */ - loop_timeout = jiffies + (MAX_LOOP_TIMEOUT * HZ); - - while ((!atomic_read(&base_vha->loop_down_timer) && - atomic_read(&base_vha->loop_state) == LOOP_DOWN) || - atomic_read(&base_vha->loop_state) != LOOP_READY) { - if (atomic_read(&base_vha->loop_state) == LOOP_DEAD) { - return_status = QLA_FUNCTION_FAILED; - break; - } - msleep(1000); - if (time_after_eq(jiffies, loop_timeout)) { - return_status = QLA_FUNCTION_FAILED; - break; - } - } - return (return_status); -} - static void sp_get(struct srb *sp) { @@ -828,7 +922,7 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) int ret; unsigned int id, lun; unsigned long flags; - int wait = 0; + int rval, wait = 0; struct qla_hw_data *ha = vha->hw; if (!CMD_SP(cmd)) @@ -849,36 +943,59 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) return SUCCESS; } - DEBUG2(printk("%s(%ld): aborting sp %p from RISC.", - __func__, vha->host_no, sp)); + 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)) { - DEBUG2(printk("%s(%ld): abort_command " - "mbx failed.\n", __func__, vha->host_no)); - ret = FAILED; + rval = ha->isp_ops->abort_command(sp); + if (rval) { + if (rval == QLA_FUNCTION_PARAMETER_ERROR) { + /* + * Decrement the ref_count since we can't find the + * command + */ + atomic_dec(&sp->ref_count); + ret = SUCCESS; + } else + ret = FAILED; + + ql_dbg(ql_dbg_taskm, vha, 0x8003, + "Abort command mbx failed cmd=%p, rval=%x.\n", cmd, rval); } else { - DEBUG3(printk("%s(%ld): abort_command " - "mbx success.\n", __func__, vha->host_no)); + ql_dbg(ql_dbg_taskm, vha, 0x8004, + "Abort command mbx success cmd=%p.\n", cmd); wait = 1; } - qla2x00_sp_compl(ha, sp); + + spin_lock_irqsave(&ha->hardware_lock, flags); + /* + * Clear the slot in the oustanding_cmds array if we can't find the + * command to reclaim the resources. + */ + if (rval == QLA_FUNCTION_PARAMETER_ERROR) + vha->req->outstanding_cmds[sp->handle] = NULL; + 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) { - qla_printk(KERN_ERR, ha, - "scsi(%ld:%d:%d): Abort handler timed out -- %x.\n", - vha->host_no, id, lun, ret); + ql_log(ql_log_warn, vha, 0x8006, + "Abort handler timed out cmd=%p.\n", cmd); ret = FAILED; } } - qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d): Abort command issued -- %d %x.\n", + 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; @@ -893,38 +1010,40 @@ qla2x00_eh_wait_for_pending_commands(scsi_qla_host_t *vha, unsigned int t, 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++) { + cnt < req->num_outstanding_cmds; cnt++) { sp = req->outstanding_cmds[cnt]; if (!sp) continue; - if ((sp->ctx) && !IS_PROT_IO(sp)) + 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 = sp->cmd->device->id == t; + match = cmd->device->id == t; break; case WAIT_LUN: - match = (sp->cmd->device->id == t && - sp->cmd->device->lun == l); + 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(sp->cmd); + status = qla2x00_eh_wait_on_command(cmd); spin_lock_irqsave(&ha->hardware_lock, flags); } spin_unlock_irqrestore(&ha->hardware_lock, flags); @@ -947,40 +1066,50 @@ __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type, fc_port_t *fcport = (struct fc_port *) cmd->device->hostdata; int err; - if (!fcport) + if (!fcport) { return FAILED; + } err = fc_block_scsi_eh(cmd); if (err != 0) return err; - qla_printk(KERN_INFO, vha->hw, "scsi(%ld:%d:%d): %s RESET ISSUED.\n", - vha->host_no, cmd->device->id, cmd->device->lun, name); + 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) - goto eh_reset_failed; - err = 1; - if (qla2x00_wait_for_loop_ready(vha) != QLA_SUCCESS) + 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) + != 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) + cmd->device->lun, type) != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x800d, + "wait for pending cmds failed for cmd=%p.\n", cmd); goto eh_reset_failed; + } - qla_printk(KERN_INFO, vha->hw, "scsi(%ld:%d:%d): %s RESET SUCCEEDED.\n", - vha->host_no, cmd->device->id, cmd->device->lun, name); + 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: - qla_printk(KERN_INFO, vha->hw, "scsi(%ld:%d:%d): %s RESET FAILED: %s.\n" - , vha->host_no, cmd->device->id, cmd->device->lun, name, - reset_errors[err]); + 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; } @@ -1030,37 +1159,42 @@ qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd) id = cmd->device->id; lun = cmd->device->lun; - if (!fcport) + if (!fcport) { return ret; + } ret = fc_block_scsi_eh(cmd); if (ret != 0) return ret; ret = FAILED; - qla_printk(KERN_INFO, vha->hw, - "scsi(%ld:%d:%d): BUS RESET ISSUED.\n", vha->host_no, id, lun); + 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) { - DEBUG2(printk("%s failed:board disabled\n",__func__)); + ql_log(ql_log_fatal, vha, 0x8013, + "Wait for hba online failed board disabled.\n"); goto eh_bus_reset_done; } - if (qla2x00_wait_for_loop_ready(vha) == QLA_SUCCESS) { - if (qla2x00_loop_reset(vha) == QLA_SUCCESS) - ret = SUCCESS; - } + 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) + QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x8014, + "Wait for pending commands failed.\n"); ret = FAILED; + } eh_bus_reset_done: - qla_printk(KERN_INFO, vha->hw, "%s: reset %s\n", __func__, - (ret == FAILED) ? "failed" : "succeeded"); + ql_log(ql_log_warn, vha, 0x802b, + "BUS RESET %s nexus=%ld:%d:%d.\n", + (ret == FAILED) ? "FAILED" : "SUCCEEDED", vha->host_no, id, lun); return ret; } @@ -1084,7 +1218,6 @@ static int qla2xxx_eh_host_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; struct qla_hw_data *ha = vha->hw; int ret = FAILED; unsigned int id, lun; @@ -1093,34 +1226,21 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd) 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; - - qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d): ADAPTER RESET ISSUED.\n", vha->host_no, id, lun); - - if (qla2x00_wait_for_reset_ready(vha) != QLA_SUCCESS) - goto eh_host_reset_lock; + ql_log(ql_log_info, vha, 0x8018, + "ADAPTER RESET ISSUED nexus=%ld:%d:%d.\n", vha->host_no, id, lun); /* - * Fixme-may be dpc thread is active and processing - * loop_resync,so wait a while for it to - * be completed and then issue big hammer.Otherwise - * it may cause I/O failure as big hammer marks the - * devices as lost kicking of the port_down_timer - * while dpc is stuck for the mailbox to complete. + * No point in issuing another reset if one is active. Also do not + * attempt a reset if we are updating flash. */ - qla2x00_wait_for_loop_ready(vha); + if (qla2x00_reset_active(vha) || ha->optrom_state != QLA_SWAITING) + 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 (IS_P3P_TYPE(vha->hw)) { if (!qla82xx_fcoe_ctx_reset(vha)) { /* Ctx reset success */ ret = SUCCESS; @@ -1137,8 +1257,11 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd) /* failed. schedule dpc to try */ set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags); - if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) + 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); } @@ -1149,8 +1272,9 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd) ret = SUCCESS; eh_host_reset_lock: - qla_printk(KERN_INFO, ha, "%s: reset %s\n", __func__, - (ret == FAILED) ? "failed" : "succeeded"); + 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; } @@ -1172,6 +1296,10 @@ qla2x00_loop_reset(scsi_qla_host_t *vha) struct fc_port *fcport; struct qla_hw_data *ha = vha->hw; + if (IS_QLAFX00(ha)) { + return qlafx00_loop_reset(vha); + } + if (ql2xtargetreset == 1 && ha->flags.enable_target_reset) { list_for_each_entry(fcport, &vha->vp_fcports, list) { if (fcport->port_type != FCT_TARGET) @@ -1179,33 +1307,30 @@ qla2x00_loop_reset(scsi_qla_host_t *vha) ret = ha->isp_ops->target_reset(fcport, 0, 0); if (ret != QLA_SUCCESS) { - DEBUG2_3(printk("%s(%ld): bus_reset failed: " - "target_reset=%d d_id=%x.\n", __func__, - vha->host_no, ret, fcport->d_id.b24)); + ql_dbg(ql_dbg_taskm, vha, 0x802c, + "Bus Reset failed: Reset=%d " + "d_id=%x.\n", ret, fcport->d_id.b24); } } } - if (ha->flags.enable_lip_full_login && !IS_QLA8XXX_TYPE(ha)) { - ret = qla2x00_full_login_lip(vha); - if (ret != QLA_SUCCESS) { - DEBUG2_3(printk("%s(%ld): failed: " - "full_login_lip=%d.\n", __func__, vha->host_no, - ret)); - } + + if (ha->flags.enable_lip_full_login && !IS_CNA_CAPABLE(ha)) { atomic_set(&vha->loop_state, LOOP_DOWN); atomic_set(&vha->loop_down_timer, LOOP_DOWN_TIME); qla2x00_mark_all_devices_lost(vha, 0); - qla2x00_wait_for_loop_ready(vha); + ret = qla2x00_full_login_lip(vha); + if (ret != QLA_SUCCESS) { + ql_dbg(ql_dbg_taskm, vha, 0x802d, + "full_login_lip=%d.\n", ret); + } } if (ha->flags.enable_lip_reset) { ret = qla2x00_lip_reset(vha); - if (ret != QLA_SUCCESS) { - DEBUG2_3(printk("%s(%ld): failed: " - "lip_reset=%d.\n", __func__, vha->host_no, ret)); - } else - qla2x00_wait_for_loop_ready(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 */ @@ -1220,7 +1345,6 @@ qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res) int que, cnt; unsigned long flags; srb_t *sp; - struct srb_ctx *ctx; struct qla_hw_data *ha = vha->hw; struct req_que *req; @@ -1229,34 +1353,13 @@ qla2x00_abort_all_cmds(scsi_qla_host_t *vha, int res) req = ha->req_q_map[que]; if (!req) continue; - for (cnt = 1; cnt < MAX_OUTSTANDING_COMMANDS; cnt++) { + if (!req->outstanding_cmds) + continue; + for (cnt = 1; cnt < req->num_outstanding_cmds; cnt++) { sp = req->outstanding_cmds[cnt]; if (sp) { req->outstanding_cmds[cnt] = NULL; - if (!sp->ctx || - (sp->flags & SRB_FCP_CMND_DMA_VALID) || - IS_PROT_IO(sp)) { - sp->cmd->result = res; - qla2x00_sp_compl(ha, sp); - } else { - ctx = sp->ctx; - if (ctx->type == SRB_LOGIN_CMD || - ctx->type == SRB_LOGOUT_CMD) { - ctx->u.iocb_cmd->free(sp); - } else { - struct fc_bsg_job *bsg_job = - ctx->u.bsg_job; - if (bsg_job->request->msgcode - == FC_BSG_HST_CT) - kfree(sp->fcport); - bsg_job->req->errors = 0; - bsg_job->reply->result = res; - bsg_job->job_done(bsg_job); - kfree(sp->ctx); - mempool_free(sp, - ha->srb_mempool); - } - } + sp->done(vha, sp, res); } } } @@ -1282,6 +1385,9 @@ qla2xxx_slave_configure(struct scsi_device *sdev) scsi_qla_host_t *vha = shost_priv(sdev->host); struct req_que *req = vha->req; + if (IS_T10_PI_CAPABLE(vha->hw)) + blk_queue_update_dma_alignment(sdev->request_queue, 0x7); + if (sdev->tagged_supported) scsi_activate_tcq(sdev, req->max_q_depth); else @@ -1302,17 +1408,15 @@ static void qla2x00_handle_queue_full(struct scsi_device *sdev, int qdepth) if (!scsi_track_queue_full(sdev, qdepth)) return; - DEBUG2(qla_printk(KERN_INFO, fcport->vha->hw, - "scsi(%ld:%d:%d:%d): Queue depth adjusted-down to %d.\n", - fcport->vha->host_no, sdev->channel, sdev->id, sdev->lun, - sdev->queue_depth)); + 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 qla_hw_data *ha = vha->hw; struct req_que *req = NULL; req = vha->req; @@ -1327,10 +1431,9 @@ static void qla2x00_adjust_sdev_qdepth_up(struct scsi_device *sdev, int qdepth) else scsi_adjust_queue_depth(sdev, MSG_SIMPLE_TAG, qdepth); - DEBUG2(qla_printk(KERN_INFO, ha, - "scsi(%ld:%d:%d:%d): Queue depth adjusted-up to %d.\n", - fcport->vha->host_no, sdev->channel, sdev->id, sdev->lun, - sdev->queue_depth)); + 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 @@ -1454,6 +1557,208 @@ qla24xx_disable_intrs(struct qla_hw_data *ha) 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; + + qlt_83xx_iospace_config(ha); + + 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, @@ -1488,6 +1793,8 @@ static struct isp_operations qla2100_isp_ops = { .get_flash_version = qla2x00_get_flash_version, .start_scsi = qla2x00_start_scsi, .abort_isp = qla2x00_abort_isp, + .iospace_config = qla2x00_iospace_config, + .initialize_adapter = qla2x00_initialize_adapter, }; static struct isp_operations qla2300_isp_ops = { @@ -1524,6 +1831,8 @@ static struct isp_operations qla2300_isp_ops = { .get_flash_version = qla2x00_get_flash_version, .start_scsi = qla2x00_start_scsi, .abort_isp = qla2x00_abort_isp, + .iospace_config = qla2x00_iospace_config, + .initialize_adapter = qla2x00_initialize_adapter, }; static struct isp_operations qla24xx_isp_ops = { @@ -1560,6 +1869,8 @@ static struct isp_operations qla24xx_isp_ops = { .get_flash_version = qla24xx_get_flash_version, .start_scsi = qla24xx_start_scsi, .abort_isp = qla2x00_abort_isp, + .iospace_config = qla2x00_iospace_config, + .initialize_adapter = qla2x00_initialize_adapter, }; static struct isp_operations qla25xx_isp_ops = { @@ -1596,6 +1907,8 @@ static struct isp_operations qla25xx_isp_ops = { .get_flash_version = qla24xx_get_flash_version, .start_scsi = qla24xx_dif_start_scsi, .abort_isp = qla2x00_abort_isp, + .iospace_config = qla2x00_iospace_config, + .initialize_adapter = qla2x00_initialize_adapter, }; static struct isp_operations qla81xx_isp_ops = { @@ -1626,12 +1939,14 @@ static struct isp_operations qla81xx_isp_ops = { .fw_dump = qla81xx_fw_dump, .beacon_on = qla24xx_beacon_on, .beacon_off = qla24xx_beacon_off, - .beacon_blink = qla24xx_beacon_blink, + .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, + .initialize_adapter = qla2x00_initialize_adapter, }; static struct isp_operations qla82xx_isp_ops = { @@ -1643,7 +1958,7 @@ static struct isp_operations qla82xx_isp_ops = { .nvram_config = qla81xx_nvram_config, .update_fw_options = qla24xx_update_fw_options, .load_risc = qla82xx_load_risc, - .pci_info_str = qla82xx_pci_info_str, + .pci_info_str = qla24xx_pci_info_str, .fw_version_str = qla24xx_fw_version_str, .intr_handler = qla82xx_intr_handler, .enable_intrs = qla82xx_enable_intrs, @@ -1659,15 +1974,169 @@ static struct isp_operations qla82xx_isp_ops = { .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, + .fw_dump = qla82xx_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, + .get_flash_version = qla82xx_get_flash_version, .start_scsi = qla82xx_start_scsi, .abort_isp = qla82xx_abort_isp, + .iospace_config = qla82xx_iospace_config, + .initialize_adapter = qla2x00_initialize_adapter, +}; + +static struct isp_operations qla8044_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 = qla24xx_pci_info_str, + .fw_version_str = qla24xx_fw_version_str, + .intr_handler = qla8044_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 = NULL, + .write_nvram = NULL, + .fw_dump = qla8044_fw_dump, + .beacon_on = qla82xx_beacon_on, + .beacon_off = qla82xx_beacon_off, + .beacon_blink = NULL, + .read_optrom = qla8044_read_optrom_data, + .write_optrom = qla8044_write_optrom_data, + .get_flash_version = qla82xx_get_flash_version, + .start_scsi = qla82xx_start_scsi, + .abort_isp = qla8044_abort_isp, + .iospace_config = qla82xx_iospace_config, + .initialize_adapter = qla2x00_initialize_adapter, +}; + +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, + .initialize_adapter = qla2x00_initialize_adapter, +}; + +static struct isp_operations qlafx00_isp_ops = { + .pci_config = qlafx00_pci_config, + .reset_chip = qlafx00_soft_reset, + .chip_diag = qlafx00_chip_diag, + .config_rings = qlafx00_config_rings, + .reset_adapter = qlafx00_soft_reset, + .nvram_config = NULL, + .update_fw_options = NULL, + .load_risc = NULL, + .pci_info_str = qlafx00_pci_info_str, + .fw_version_str = qlafx00_fw_version_str, + .intr_handler = qlafx00_intr_handler, + .enable_intrs = qlafx00_enable_intrs, + .disable_intrs = qlafx00_disable_intrs, + .abort_command = qla24xx_async_abort_command, + .target_reset = qlafx00_abort_target, + .lun_reset = qlafx00_lun_reset, + .fabric_login = NULL, + .fabric_logout = NULL, + .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 = NULL, + .beacon_on = qla24xx_beacon_on, + .beacon_off = qla24xx_beacon_off, + .beacon_blink = NULL, + .read_optrom = qla24xx_read_optrom_data, + .write_optrom = qla24xx_write_optrom_data, + .get_flash_version = qla24xx_get_flash_version, + .start_scsi = qlafx00_start_scsi, + .abort_isp = qlafx00_abort_isp, + .iospace_config = qlafx00_iospace_config, + .initialize_adapter = qlafx00_initialize_adapter, +}; + +static struct isp_operations qla27xx_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 = qla27xx_fwdump, + .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, + .initialize_adapter = qla2x00_initialize_adapter, }; static inline void @@ -1764,118 +2233,63 @@ qla2x00_set_isp_flags(struct qla_hw_data *ha) /* Initialize 82XX ISP flags */ qla82xx_init_flags(ha); break; + case PCI_DEVICE_ID_QLOGIC_ISP8044: + ha->device_type |= DT_ISP8044; + 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; + case PCI_DEVICE_ID_QLOGIC_ISPF001: + ha->device_type |= DT_ISPFX00; + break; + case PCI_DEVICE_ID_QLOGIC_ISP2071: + ha->device_type |= DT_ISP2071; + 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_ISP2271: + ha->device_type |= DT_ISP2271; + 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; } if (IS_QLA82XX(ha)) - ha->port_no = !(ha->portnum & 1); - else + 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; -} - -static int -qla2x00_iospace_config(struct qla_hw_data *ha) -{ - resource_size_t pio; - uint16_t msix; - int cpus; - - if (IS_QLA82XX(ha)) - return qla82xx_iospace_config(ha); - - if (pci_request_selected_regions(ha->pdev, ha->bars, - QLA2XXX_DRIVER_NAME)) { - qla_printk(KERN_WARNING, ha, - "Failed to reserve PIO/MMIO regions (%s)\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) { - qla_printk(KERN_WARNING, ha, - "Invalid PCI I/O region size (%s)...\n", - pci_name(ha->pdev)); - pio = 0; - } - } else { - qla_printk(KERN_WARNING, ha, - "region #0 not a PIO resource (%s)...\n", - pci_name(ha->pdev)); - pio = 0; - } - ha->pio_address = pio; - -skip_pio: - /* Use MMIO operations for all accesses. */ - if (!(pci_resource_flags(ha->pdev, 1) & IORESOURCE_MEM)) { - qla_printk(KERN_ERR, ha, - "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) { - qla_printk(KERN_ERR, ha, - "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) { - qla_printk(KERN_ERR, ha, - "cannot remap MMIO (%s), aborting\n", pci_name(ha->pdev)); - - goto iospace_error_exit; + if (IS_QLA27XX(ha)) + ha->port_no--; + else + ha->port_no = !(ha->port_no & 1); } - /* 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) { - /* 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; - DEBUG2(qla_printk(KERN_INFO, ha, "QoS mode set, max no" - " of request queues:%d\n", ha->max_req_queues)); - } - qla_printk(KERN_INFO, ha, - "MSI-X vector count: %d\n", msix); - } else - qla_printk(KERN_INFO, ha, "BAR 3 not enabled\n"); - -mqiobase_exit: - ha->msix_count = ha->max_rsp_queues + 1; - return (0); - -iospace_error_exit: - return (-ENOMEM); + 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->port_no, ha->fw_srisc_address); } static void @@ -1908,7 +2322,7 @@ qla2xxx_scan_finished(struct Scsi_Host *shost, unsigned long time) /* * PCI driver interface */ -static int __devinit +static int qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) { int ret = -ENODEV; @@ -1916,13 +2330,12 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) scsi_qla_host_t *base_vha = NULL; struct qla_hw_data *ha; char pci_info[30]; - char fw_str[30]; + char fw_str[30], wq_name[30]; struct scsi_host_template *sht; - int bars, max_id, mem_only = 0; + 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 || @@ -1932,10 +2345,20 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) 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_ISP8021 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2031 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8031 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISPF001 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP8044 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2071 || + pdev->device == PCI_DEVICE_ID_QLOGIC_ISP2271) { 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)) @@ -1950,33 +2373,30 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha = kzalloc(sizeof(struct qla_hw_data), GFP_KERNEL); if (!ha) { - DEBUG(printk("Unable to allocate memory for ha\n")); + 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; + ha->tgt.enable_class_2 = ql2xenableclass2; /* Clear our data area */ ha->bars = bars; ha->mem_only = mem_only; spin_lock_init(&ha->hardware_lock); spin_lock_init(&ha->vport_slock); + mutex_init(&ha->selflogin_lock); + mutex_init(&ha->optrom_mutex); /* 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)) { + if (IS_QLA24XX(ha) || IS_QLA25XX(ha) || IS_QLA81XX(ha) || + IS_QLA83XX(ha) || IS_QLA27XX(ha)) pdev->needs_freset = 1; - } - - /* Configure PCI I/O space */ - ret = qla2x00_iospace_config(ha); - if (ret) - goto probe_hw_failed; - - qla_printk(KERN_INFO, ha, - "Found an ISP%04X, irq %d, iobase 0x%p\n", pdev->device, pdev->irq, - ha->iobase); ha->prev_topology = 0; ha->init_cb_size = sizeof(init_cb_t); @@ -1984,9 +2404,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->optrom_size = OPTROM_SIZE_2300; /* Assign ISP specific operations. */ - max_id = MAX_TARGETS_2200; if (IS_QLA2100(ha)) { - max_id = MAX_TARGETS_2100; + 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; @@ -1998,7 +2417,8 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->nvram_data_off = ~0; ha->isp_ops = &qla2100_isp_ops; } else if (IS_QLA2200(ha)) { - ha->mbx_count = MAILBOX_REGISTER_COUNT; + 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; @@ -2009,6 +2429,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) 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; @@ -2022,9 +2443,11 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) 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->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX; 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; @@ -2036,9 +2459,11 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) 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->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX; 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; @@ -2050,9 +2475,11 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) 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->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX; 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; @@ -2064,6 +2491,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) 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; @@ -2077,21 +2505,110 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) 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_QLA8044(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_83XX; + ha->nvram_npiv_size = QLA_MAX_VPORTS_QLA25XX; + ha->isp_ops = &qla8044_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->portnum = PCI_FUNC(ha->pdev->devfn); + 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->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX; + 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; + } else if (IS_QLAFX00(ha)) { + ha->max_fibre_devices = MAX_FIBRE_DEVICES_FX00; + ha->mbx_count = MAILBOX_REGISTER_COUNT_FX00; + ha->aen_mbx_count = AEN_MAILBOX_REGISTER_COUNT_FX00; + req_length = REQUEST_ENTRY_CNT_FX00; + rsp_length = RESPONSE_ENTRY_CNT_FX00; + ha->isp_ops = &qlafx00_isp_ops; + ha->port_down_retry_count = 30; /* default value */ + ha->mr.fw_hbt_cnt = QLAFX00_HEARTBEAT_INTERVAL; + ha->mr.fw_reset_timer_tick = QLAFX00_RESET_INTERVAL; + ha->mr.fw_critemp_timer_tick = QLAFX00_CRITEMP_INTERVAL; + ha->mr.fw_hbt_en = 1; + ha->mr.host_info_resend = false; + ha->mr.hinfo_resend_timer_tick = QLAFX00_HINFO_RESEND_INTERVAL; + } else if (IS_QLA27XX(ha)) { + ha->portnum = PCI_FUNC(ha->pdev->devfn); + 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 = &qla27xx_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 iospace_config_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); + init_completion(&ha->lb_portup_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) { - qla_printk(KERN_WARNING, ha, - "[ERROR] Failed to allocate memory for adapter\n"); + if (ret) { + ql_log_pci(ql_log_fatal, pdev, 0x0031, + "Failed to allocate memory for adapter, aborting.\n"); goto probe_hw_failed; } @@ -2103,9 +2620,6 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) base_vha = qla2x00_create_host(sht, ha); if (!base_vha) { - qla_printk(KERN_WARNING, ha, - "[ERROR] Failed to allocate memory for scsi_host\n"); - ret = -ENOMEM; qla2x00_mem_free(ha); qla2x00_free_req_que(ha, req); @@ -2117,13 +2631,20 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) 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; + /* Setup fcport template structure. */ + ha->mr.fcport.vha = base_vha; + ha->mr.fcport.port_type = FCT_UNKNOWN; + ha->mr.fcport.loop_id = FC_NO_LOOP_ID; + qla2x00_set_fcport_state(&ha->mr.fcport, FCS_UNCONFIGURED); + ha->mr.fcport.supported_classes = FC_COS_UNSPECIFIED; + ha->mr.fcport.scan_state = 1; + /* Set the SG table size based on ISP type */ if (!IS_FWI2_CAPABLE(ha)) { if (IS_QLA2100(ha)) @@ -2132,12 +2653,10 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) if (!IS_QLA82XX(ha)) host->sg_tablesize = QLA_SG_ALL; } - - host->max_id = max_id; - host->this_id = 255; + host->max_id = ha->max_fibre_devices; host->cmd_per_lun = 3; host->unique_id = host->host_no; - if ((IS_QLA25XX(ha) || IS_QLA81XX(ha)) && ql2xenabledif) + if (IS_T10_PI_CAPABLE(ha) && ql2xenabledif) host->max_cmd_len = 32; else host->max_cmd_len = MAX_CMDSZ; @@ -2146,6 +2665,25 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) 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); + +que_init: + /* Alloc arrays of request and response ring ptrs */ + if (!qla2x00_alloc_queues(ha, req, rsp)) { + ql_log(ql_log_fatal, base_vha, 0x003d, + "Failed to allocate memory for queue pointers..." + "aborting.\n"); + goto probe_init_failed; + } + + qlt_probe_one_stage1(base_vha, ha); + /* Set up the irqs */ ret = qla2x00_request_irqs(ha, rsp); if (ret) @@ -2153,64 +2691,97 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) pci_save_state(pdev); - /* Alloc arrays of request and response ring ptrs */ -que_init: - if (!qla2x00_alloc_queues(ha)) { - qla_printk(KERN_WARNING, ha, - "[ERROR] Failed to allocate memory for queue" - " pointers\n"); - goto probe_init_failed; - } - - ha->rsp_q_map[0] = rsp; - ha->req_q_map[0] = req; + /* Assign back pointers */ rsp->req = req; req->rsp = rsp; - set_bit(0, ha->req_qid_map); - set_bit(0, ha->rsp_qid_map); + + if (IS_QLAFX00(ha)) { + ha->rsp_q_map[0] = rsp; + ha->req_q_map[0] = req; + 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) { + if (ha->mqenable || IS_QLA83XX(ha) || IS_QLA27XX(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)) { + if (IS_QLAFX00(ha)) { + req->req_q_in = &ha->iobase->ispfx00.req_q_in; + req->req_q_out = &ha->iobase->ispfx00.req_q_out; + rsp->rsp_q_in = &ha->iobase->ispfx00.rsp_q_in; + rsp->rsp_q_out = &ha->iobase->ispfx00.rsp_q_out; + } + + if (IS_P3P_TYPE(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]; } - if (qla2x00_initialize_adapter(base_vha)) { - qla_printk(KERN_WARNING, ha, - "Failed to initialize adapter\n"); - - DEBUG2(printk("scsi(%ld): Failed to initialize adapter - " - "Adapter flags %x.\n", - base_vha->host_no, base_vha->device_flags)); + 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 (ha->isp_ops->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); + QLA8XXX_DEV_FAILED); qla82xx_idc_unlock(ha); - qla_printk(KERN_INFO, ha, "HW State: FAILED\n"); + ql_log(ql_log_fatal, base_vha, 0x00d7, + "HW State: FAILED.\n"); + } else if (IS_QLA8044(ha)) { + qla8044_idc_lock(ha); + qla8044_wr_direct(base_vha, + QLA8044_CRB_DEV_STATE_INDEX, + QLA8XXX_DEV_FAILED); + qla8044_idc_unlock(ha); + ql_log(ql_log_fatal, base_vha, 0x0150, + "HW State: FAILED.\n"); } ret = -ENODEV; goto probe_failed; } + if (IS_QLAFX00(ha)) + host->can_queue = QLAFX00_MAX_CANQUEUE; + else + host->can_queue = req->num_outstanding_cmds - 10; + + 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); + if (ha->mqenable) { if (qla25xx_setup_mode(base_vha)) { - qla_printk(KERN_WARNING, ha, - "Can't create queues, falling back to single" - " queue mode\n"); + ql_log(ql_log_warn, base_vha, 0x00ec, + "Failed to create queues, falling back to single queue mode.\n"); goto que_init; } } @@ -2222,13 +2793,39 @@ que_init: * Startup the kernel thread for this host adapter */ ha->dpc_thread = kthread_create(qla2x00_do_dpc, ha, - "%s_dpc", base_vha->host_str); + "%s_dpc", base_vha->host_str); if (IS_ERR(ha->dpc_thread)) { - qla_printk(KERN_WARNING, ha, - "Unable to start DPC thread!\n"); + 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"); + + /* + * If we're not coming up in initiator mode, we might sit for + * a while without waking up the dpc thread, which leads to a + * stuck process warning. So just kick the dpc once here and + * let the kthread start (and go back to sleep in qla2x00_do_dpc). + */ + qla2xxx_wake_dpc(base_vha); + + INIT_WORK(&ha->board_disable, qla2x00_disable_board_on_pci_error); + + if (IS_QLA8031(ha) || IS_MCTP_CAPABLE(ha)) { + sprintf(wq_name, "qla2xxx_%lu_dpc_lp_wq", base_vha->host_no); + ha->dpc_lp_wq = create_singlethread_workqueue(wq_name); + INIT_WORK(&ha->idc_aen, qla83xx_service_idc_aen); + + sprintf(wq_name, "qla2xxx_%lu_dpc_hp_wq", base_vha->host_no); + ha->dpc_hp_wq = create_singlethread_workqueue(wq_name); + INIT_WORK(&ha->nic_core_reset, qla83xx_nic_core_reset_work); + INIT_WORK(&ha->idc_state_handler, + qla83xx_idc_state_handler_work); + INIT_WORK(&ha->nic_core_unrecoverable, + qla83xx_nic_core_unrecoverable_work); + } skip_dpc: list_add_tail(&base_vha->list, &ha->vp_list); @@ -2236,60 +2833,101 @@ skip_dpc: /* Initialized the timer */ qla2x00_start_timer(base_vha, qla2x00_timer, WATCH_INTERVAL); - - DEBUG2(printk("DEBUG: detect hba %ld at address = %p\n", - base_vha->host_no, ha)); - - if ((IS_QLA25XX(ha) || IS_QLA81XX(ha)) && ql2xenabledif) { + 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, guard; base_vha->flags.difdix_supported = 1; - DEBUG18(qla_printk(KERN_INFO, ha, - "Registering for DIF/DIX type 1 and 3" - " protection.\n")); + 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, - SHOST_DIF_TYPE1_PROTECTION + 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); + + guard = SHOST_DIX_GUARD_CRC; + + if (IS_PI_IPGUARD_CAPABLE(ha) && + (ql2xenabledif > 1 || IS_PI_DIFB_DIX0_CAPABLE(ha))) + guard |= SHOST_DIX_GUARD_IP; + + scsi_host_set_guard(host, guard); } else base_vha->flags.difdix_supported = 0; } ha->isp_ops->enable_intrs(ha); + if (IS_QLAFX00(ha)) { + ret = qlafx00_fx_disc(base_vha, + &base_vha->hw->mr.fcport, FXDISC_GET_CONFIG_INFO); + host->sg_tablesize = (ha->mr.extended_io_enabled) ? + QLA_SG_ALL : 128; + } + ret = scsi_add_host(host, &pdev->dev); if (ret) goto probe_failed; base_vha->flags.init_done = 1; base_vha->flags.online = 1; + ha->prev_minidump_failed = 0; - scsi_scan_host(host); + ql_dbg(ql_dbg_init, base_vha, 0x00f2, + "Init done and hba is online.\n"); + + if (qla_ini_mode_enabled(base_vha)) + scsi_scan_host(host); + else + ql_dbg(ql_dbg_init, base_vha, 0x0122, + "skipping scsi_scan_host() for non-initiator port\n"); qla2x00_alloc_sysfs_attr(base_vha); + if (IS_QLAFX00(ha)) { + ret = qlafx00_fx_disc(base_vha, + &base_vha->hw->mr.fcport, FXDISC_GET_PORT_INFO); + + /* Register system information */ + ret = qlafx00_fx_disc(base_vha, + &base_vha->hw->mr.fcport, FXDISC_REG_HOST_INFO); + } + qla2x00_init_host_attr(base_vha); qla2x00_dfs_setup(base_vha); - qla_printk(KERN_INFO, ha, "\n" - " QLogic Fibre Channel HBA Driver: %s\n" - " QLogic %s - %s\n" - " ISP%04X: %s @ %s hdma%c, host#=%ld, fw=%s\n", - qla2x00_version_str, ha->model_number, - ha->model_desc ? ha->model_desc : "", pdev->device, - ha->isp_ops->pci_info_str(base_vha, pci_info), pci_name(pdev), - ha->flags.enable_64bit_addressing ? '+' : '-', base_vha->host_no, + ql_log(ql_log_info, base_vha, 0x00fb, + "QLogic %s - %s.\n", ha->model_number, 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)); + qlt_add_target(ha, base_vha); + return 0; probe_init_failed: qla2x00_free_req_que(ha, req); + ha->req_q_map[0] = NULL; + clear_bit(0, ha->req_qid_map); qla2x00_free_rsp_que(ha, rsp); + ha->rsp_q_map[0] = NULL; + clear_bit(0, ha->rsp_qid_map); ha->max_req_queues = ha->max_rsp_queues = 0; probe_failed: @@ -2312,12 +2950,23 @@ probe_hw_failed: qla82xx_idc_lock(ha); qla82xx_clear_drv_active(ha); qla82xx_idc_unlock(ha); - iounmap((device_reg_t __iomem *)ha->nx_pcibase); + } + if (IS_QLA8044(ha)) { + qla8044_idc_lock(ha); + qla8044_clear_drv_active(ha); + qla8044_idc_unlock(ha); + } +iospace_config_failed: + if (IS_P3P_TYPE(ha)) { + if (!ha->nx_pcibase) + iounmap((device_reg_t *)ha->nx_pcibase); if (!ql2xdbwr) - iounmap((device_reg_t __iomem *)ha->nxdb_wr_ptr); + iounmap((device_reg_t *)ha->nxdb_wr_ptr); } else { if (ha->iobase) iounmap(ha->iobase); + if (ha->cregbase) + iounmap(ha->cregbase); } pci_release_selected_regions(ha->pdev, ha->bars); kfree(ha); @@ -2334,9 +2983,16 @@ qla2x00_shutdown(struct pci_dev *pdev) scsi_qla_host_t *vha; struct qla_hw_data *ha; + if (!atomic_read(&pdev->enable_cnt)) + return; + vha = pci_get_drvdata(pdev); ha = vha->hw; + /* Notify ISPFX00 firmware */ + if (IS_QLAFX00(ha)) + qlafx00_driver_shutdown(vha, 20); + /* Turn-off FCE trace */ if (ha->flags.fce_enabled) { qla2x00_disable_fce_trace(vha, NULL, NULL); @@ -2364,20 +3020,16 @@ qla2x00_shutdown(struct pci_dev *pdev) qla2x00_free_fw_dump(ha); } +/* Deletes all the virtual ports for a given ha */ static void -qla2x00_remove_one(struct pci_dev *pdev) +qla2x00_delete_all_vps(struct qla_hw_data *ha, scsi_qla_host_t *base_vha) { - scsi_qla_host_t *base_vha, *vha; - struct qla_hw_data *ha; + struct Scsi_Host *scsi_host; + scsi_qla_host_t *vha; 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); @@ -2394,21 +3046,12 @@ qla2x00_remove_one(struct pci_dev *pdev) 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; - +/* Stops all deferred work threads */ +static void +qla2x00_destroy_deferred_work(struct qla_hw_data *ha) +{ /* Flush the work queue and remove it */ if (ha->wq) { flush_workqueue(ha->wq); @@ -2416,6 +3059,21 @@ qla2x00_remove_one(struct pci_dev *pdev) ha->wq = NULL; } + /* Cancel all work and destroy DPC workqueues */ + if (ha->dpc_lp_wq) { + cancel_work_sync(&ha->idc_aen); + destroy_workqueue(ha->dpc_lp_wq); + ha->dpc_lp_wq = NULL; + } + + if (ha->dpc_hp_wq) { + cancel_work_sync(&ha->nic_core_reset); + cancel_work_sync(&ha->idc_state_handler); + cancel_work_sync(&ha->nic_core_unrecoverable); + destroy_workqueue(ha->dpc_hp_wq); + ha->dpc_hp_wq = NULL; + } + /* Kill the kernel thread for this host */ if (ha->dpc_thread) { struct task_struct *t = ha->dpc_thread; @@ -2427,33 +3085,110 @@ qla2x00_remove_one(struct pci_dev *pdev) ha->dpc_thread = NULL; kthread_stop(t); } +} - qla2x00_free_sysfs_attr(base_vha); +static void +qla2x00_unmap_iobases(struct qla_hw_data *ha) +{ + if (IS_QLA82XX(ha)) { - fc_remove_host(base_vha->host); + iounmap((device_reg_t *)ha->nx_pcibase); + if (!ql2xdbwr) + iounmap((device_reg_t *)ha->nxdb_wr_ptr); + } else { + if (ha->iobase) + iounmap(ha->iobase); - scsi_remove_host(base_vha->host); + if (ha->cregbase) + iounmap(ha->cregbase); - qla2x00_free_device(base_vha); + if (ha->mqiobase) + iounmap(ha->mqiobase); - scsi_host_put(base_vha->host); + if ((IS_QLA83XX(ha) || IS_QLA27XX(ha)) && ha->msixbase) + iounmap(ha->msixbase); + } +} - if (IS_QLA82XX(ha)) { +static void +qla2x00_clear_drv_active(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + + if (IS_QLA8044(ha)) { + qla8044_idc_lock(ha); + qla8044_clear_drv_active(ha); + qla8044_idc_unlock(ha); + } else 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); +static void +qla2x00_remove_one(struct pci_dev *pdev) +{ + scsi_qla_host_t *base_vha; + struct qla_hw_data *ha; - if (ha->mqiobase) - iounmap(ha->mqiobase); + /* + * If the PCI device is disabled that means that probe failed and any + * resources should be have cleaned up on probe exit. + */ + if (!atomic_read(&pdev->enable_cnt)) + return; + + base_vha = pci_get_drvdata(pdev); + ha = base_vha->hw; + + qla2x00_wait_for_hba_ready(base_vha); + + set_bit(UNLOADING, &base_vha->dpc_flags); + + if (IS_QLAFX00(ha)) + qlafx00_driver_shutdown(base_vha, 20); + + qla2x00_delete_all_vps(ha, base_vha); + + if (IS_QLA8031(ha)) { + ql_dbg(ql_dbg_p3p, base_vha, 0xb07e, + "Clearing fcoe driver presence.\n"); + if (qla83xx_clear_drv_presence(base_vha) != QLA_SUCCESS) + ql_dbg(ql_dbg_p3p, base_vha, 0xb079, + "Error while clearing DRV-Presence.\n"); } + 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; + + qla2x00_destroy_deferred_work(ha); + + qlt_remove_target(ha, base_vha); + + qla2x00_free_sysfs_attr(base_vha, true); + + fc_remove_host(base_vha->host); + + scsi_remove_host(base_vha->host); + + qla2x00_free_device(base_vha); + + scsi_host_put(base_vha->host); + + qla2x00_clear_drv_active(base_vha); + + qla2x00_unmap_iobases(ha); + pci_release_selected_regions(ha->pdev, ha->bars); kfree(ha); ha = NULL; @@ -2461,7 +3196,6 @@ qla2x00_remove_one(struct pci_dev *pdev) pci_disable_pcie_error_reporting(pdev); pci_disable_device(pdev); - pci_set_drvdata(pdev, NULL); } static void @@ -2475,18 +3209,6 @@ qla2x00_free_device(scsi_qla_host_t *vha) 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) @@ -2512,6 +3234,8 @@ qla2x00_free_device(scsi_qla_host_t *vha) qla2x00_mem_free(ha); + qla82xx_md_free(vha); + qla2x00_free_queues(ha); } @@ -2521,6 +3245,7 @@ void qla2x00_free_fcports(struct scsi_qla_host *vha) list_for_each_entry_safe(fcport, tfcport, &vha->vp_fcports, list) { list_del(&fcport->list); + qla2x00_clear_loop_id(fcport); kfree(fcport); fcport = NULL; } @@ -2545,8 +3270,10 @@ qla2x00_schedule_rport_del(struct scsi_qla_host *vha, fc_port_t *fcport, spin_unlock_irqrestore(vha->host->host_lock, flags); set_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags); qla2xxx_wake_dpc(base_vha); - } else + } else { fc_remote_port_delete(rport); + qlt_fc_port_deleted(vha, fcport); + } } /* @@ -2561,8 +3288,14 @@ qla2x00_schedule_rport_del(struct scsi_qla_host *vha, fc_port_t *fcport, void qla2x00_mark_device_lost(scsi_qla_host_t *vha, fc_port_t *fcport, int do_login, int defer) { + if (IS_QLAFX00(vha->hw)) { + qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST); + qla2x00_schedule_rport_del(vha, fcport, defer); + return; + } + if (atomic_read(&fcport->state) == FCS_ONLINE && - vha->vp_idx == fcport->vp_idx) { + vha->vp_idx == fcport->vha->vp_idx) { qla2x00_set_fcport_state(fcport, FCS_DEVICE_LOST); qla2x00_schedule_rport_del(vha, fcport, defer); } @@ -2580,20 +3313,9 @@ void qla2x00_mark_device_lost(scsi_qla_host_t *vha, fc_port_t *fcport, fcport->login_retry = vha->hw->login_retry_count; set_bit(RELOGIN_NEEDED, &vha->dpc_flags); - DEBUG(printk("scsi(%ld): Port login retry: " - "%02x%02x%02x%02x%02x%02x%02x%02x, " - "id = 0x%04x retry cnt=%d\n", - vha->host_no, - 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)); + ql_dbg(ql_dbg_disc, vha, 0x2067, + "Port login retry %8phN, id = 0x%04x retry cnt=%d.\n", + fcport->port_name, fcport->loop_id, fcport->login_retry); } } @@ -2616,7 +3338,7 @@ 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) + if (vha->vp_idx != 0 && vha->vp_idx != fcport->vha->vp_idx) continue; /* @@ -2629,7 +3351,7 @@ qla2x00_mark_all_devices_lost(scsi_qla_host_t *vha, int defer) 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) + else if (vha->vp_idx == fcport->vha->vp_idx) qla2x00_schedule_rport_del(vha, fcport, defer); } } @@ -2654,16 +3376,19 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, if (!ha->init_cb) goto fail; - ha->gid_list = dma_alloc_coherent(&ha->pdev->dev, GID_LIST_SIZE, - &ha->gid_list_dma, GFP_KERNEL); - if (!ha->gid_list) + if (qlt_mem_alloc(ha) < 0) goto fail_free_init_cb; + 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_tgt_mem; + 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)) { + if (IS_P3P_TYPE(ha)) { /* Allocate cache for CT6 Ctx. */ if (!ctx_cachep) { ctx_cachep = kmem_cache_create("qla2xxx_ctx", @@ -2676,6 +3401,9 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, 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 */ @@ -2690,22 +3418,29 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, if (!ha->s_dma_pool) goto fail_free_nvram; - if (IS_QLA82XX(ha) || ql2xenabledif) { + 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_P3P_TYPE(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) { - qla_printk(KERN_WARNING, ha, - "Memory Allocation failed - dl_dma_pool\n"); + 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) { - qla_printk(KERN_WARNING, ha, - "Memory Allocation failed - fcp_cmnd_dma_pool\n"); + 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 */ @@ -2715,6 +3450,8 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, 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, @@ -2726,12 +3463,16 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, 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) { - DEBUG(printk("Unable to allocate memory for req\n")); + ql_log_pci(ql_log_fatal, ha->pdev, 0x0028, + "Failed to allocate memory for req.\n"); goto fail_req; } (*req)->length = req_len; @@ -2739,14 +3480,15 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, ((*req)->length + 1) * sizeof(request_t), &(*req)->dma, GFP_KERNEL); if (!(*req)->ring) { - DEBUG(printk("Unable to allocate memory for req_ring\n")); + 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) { - qla_printk(KERN_WARNING, ha, - "Unable to allocate memory for rsp\n"); + ql_log_pci(ql_log_fatal, ha->pdev, 0x002a, + "Failed to allocate memory for rsp.\n"); goto fail_rsp; } (*rsp)->hw = ha; @@ -2755,30 +3497,37 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, ((*rsp)->length + 1) * sizeof(response_t), &(*rsp)->dma, GFP_KERNEL); if (!(*rsp)->ring) { - qla_printk(KERN_WARNING, ha, - "Unable to allocate memory for rsp_ring\n"); + 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); + ha->nvram_npiv_size, GFP_KERNEL); if (!ha->npiv_info) { - qla_printk(KERN_WARNING, ha, - "Unable to allocate memory for npiv info\n"); + 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_QLA8XXX_TYPE(ha)) { + if (IS_CNA_CAPABLE(ha) || IS_QLA2031(ha) || IS_QLA27XX(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); @@ -2789,10 +3538,24 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, &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; + + /* Allocate memory for our loop_id bitmap */ + ha->loop_id_map = kzalloc(BITS_TO_LONGS(LOOPID_MAP_SIZE) * sizeof(long), + GFP_KERNEL); + if (!ha->loop_id_map) + goto fail_async_pd; + else { + qla2x00_set_reserved_loop_ids(ha); + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0123, + "loop_id_map=%p.\n", ha->loop_id_map); + } + + return 0; fail_async_pd: dma_pool_free(ha->s_dma_pool, ha->ex_init_cb, ha->ex_init_cb_dma); @@ -2844,17 +3607,21 @@ fail_free_srb_mempool: mempool_destroy(ha->srb_mempool); ha->srb_mempool = NULL; fail_free_gid_list: - dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, ha->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_tgt_mem: + qlt_mem_free(ha); 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: - DEBUG(printk("%s: Memory allocation failure\n", __func__)); + ql_log(ql_log_fatal, NULL, 0x0030, + "Memory allocation failure.\n"); return -ENOMEM; } @@ -2863,28 +3630,35 @@ fail: * Frees fw dump stuff. * * Input: -* ha = adapter block pointer. +* 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); + 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); + if (ha->eft) + dma_free_coherent(&ha->pdev->dev, + EFT_SIZE, ha->eft, ha->eft_dma); + + if (ha->fw_dump) vfree(ha->fw_dump); - } + if (ha->fw_dump_template) + vfree(ha->fw_dump_template); + 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_cap_flags = 0; ha->fw_dump_reading = 0; + ha->fw_dump = NULL; + ha->fw_dump_len = 0; + ha->fw_dump_template = NULL; + ha->fw_dump_template_len = 0; } /* @@ -2899,6 +3673,10 @@ qla2x00_mem_free(struct qla_hw_data *ha) { qla2x00_free_fw_dump(ha); + if (ha->mctp_dump) + dma_free_coherent(&ha->pdev->dev, MCTP_DUMP_SIZE, ha->mctp_dump, + ha->mctp_dump_dma); + if (ha->srb_mempool) mempool_destroy(ha->srb_mempool); @@ -2921,9 +3699,6 @@ qla2x00_mem_free(struct qla_hw_data *ha) if (ha->sfp_data) dma_pool_free(ha->s_dma_pool, ha->sfp_data, ha->sfp_data_dma); - if (ha->edc_data) - dma_pool_free(ha->s_dma_pool, ha->edc_data, ha->edc_data_dma); - if (ha->ms_iocb) dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma); @@ -2938,8 +3713,8 @@ qla2x00_mem_free(struct qla_hw_data *ha) dma_pool_destroy(ha->s_dma_pool); if (ha->gid_list) - dma_free_coherent(&ha->pdev->dev, GID_LIST_SIZE, ha->gid_list, - ha->gid_list_dma); + 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)) { @@ -2965,12 +3740,16 @@ qla2x00_mem_free(struct qla_hw_data *ha) if (ha->ctx_mempool) mempool_destroy(ha->ctx_mempool); + qlt_mem_free(ha); + 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); + kfree(ha->loop_id_map); ha->srb_mempool = NULL; ha->ctx_mempool = NULL; @@ -2993,6 +3772,10 @@ qla2x00_mem_free(struct qla_hw_data *ha) ha->gid_list = NULL; ha->gid_list_dma = 0; + + ha->tgt.atio_ring = NULL; + ha->tgt.atio_dma = 0; + ha->tgt.tgt_vp_map = NULL; } struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht, @@ -3003,8 +3786,8 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht, host = scsi_host_alloc(sht, sizeof(scsi_qla_host_t)); if (host == NULL) { - printk(KERN_WARNING - "qla2xxx: Couldn't allocate host from scsi layer!\n"); + ql_log_pci(ql_log_fatal, ha->pdev, 0x0107, + "Failed to allocate host from the scsi layer, aborting.\n"); goto fail; } @@ -3023,6 +3806,11 @@ struct scsi_qla_host *qla2x00_create_host(struct scsi_host_template *sht, 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: @@ -3149,6 +3937,22 @@ qla2x00_uevent_emit(struct scsi_qla_host *vha, u32 code) kobject_uevent_env(&vha->hw->pdev->dev.kobj, KOBJ_CHANGE, envp); } +int +qlafx00_post_aenfx_work(struct scsi_qla_host *vha, uint32_t evtcode, + uint32_t *data, int cnt) +{ + struct qla_work_evt *e; + + e = qla2x00_alloc_work(vha, QLA_EVT_AENFX); + if (!e) + return QLA_FUNCTION_FAILED; + + e->u.aenfx.evtcode = evtcode; + e->u.aenfx.count = cnt; + memcpy(e->u.aenfx.mbx, data, sizeof(*data) * cnt); + return qla2x00_post_work(vha, e); +} + void qla2x00_do_work(struct scsi_qla_host *vha) { @@ -3197,6 +4001,9 @@ qla2x00_do_work(struct scsi_qla_host *vha) case QLA_EVT_UEVENT: qla2x00_uevent_emit(vha, e->u.uevent.code); break; + case QLA_EVT_AENFX: + qlafx00_process_aen(vha, e); + break; } if (e->flags & QLA_EVT_FLAG_FREE) kfree(e); @@ -3254,9 +4061,24 @@ void qla2x00_relogin(struct scsi_qla_host *vha) continue; /* Attempt a retry. */ status = 1; - } else + } 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); @@ -3264,30 +4086,734 @@ void qla2x00_relogin(struct scsi_qla_host *vha) if (status == QLA_SUCCESS) { fcport->old_loop_id = fcport->loop_id; - DEBUG(printk("scsi(%ld): port login OK: logged " - "in ID 0x%x\n", vha->host_no, 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 */ - DEBUG(printk("scsi(%ld): Retrying" - " %d login again loop_id 0x%x\n", - vha->host_no, fcport->login_retry, - fcport->loop_id)); + 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; + qla2x00_clear_loop_id(fcport); } if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) break; } } +/* Schedule work on any of the dpc-workqueues */ +void +qla83xx_schedule_work(scsi_qla_host_t *base_vha, int work_code) +{ + struct qla_hw_data *ha = base_vha->hw; + + switch (work_code) { + case MBA_IDC_AEN: /* 0x8200 */ + if (ha->dpc_lp_wq) + queue_work(ha->dpc_lp_wq, &ha->idc_aen); + break; + + case QLA83XX_NIC_CORE_RESET: /* 0x1 */ + if (!ha->flags.nic_core_reset_hdlr_active) { + if (ha->dpc_hp_wq) + queue_work(ha->dpc_hp_wq, &ha->nic_core_reset); + } else + ql_dbg(ql_dbg_p3p, base_vha, 0xb05e, + "NIC Core reset is already active. Skip " + "scheduling it again.\n"); + break; + case QLA83XX_IDC_STATE_HANDLER: /* 0x2 */ + if (ha->dpc_hp_wq) + queue_work(ha->dpc_hp_wq, &ha->idc_state_handler); + break; + case QLA83XX_NIC_CORE_UNRECOVERABLE: /* 0x3 */ + if (ha->dpc_hp_wq) + queue_work(ha->dpc_hp_wq, &ha->nic_core_unrecoverable); + break; + default: + ql_log(ql_log_warn, base_vha, 0xb05f, + "Unknow work-code=0x%x.\n", work_code); + } + + return; +} + +/* Work: Perform NIC Core Unrecoverable state handling */ +void +qla83xx_nic_core_unrecoverable_work(struct work_struct *work) +{ + struct qla_hw_data *ha = + container_of(work, struct qla_hw_data, nic_core_unrecoverable); + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); + uint32_t dev_state = 0; + + qla83xx_idc_lock(base_vha, 0); + qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, &dev_state); + qla83xx_reset_ownership(base_vha); + if (ha->flags.nic_core_reset_owner) { + ha->flags.nic_core_reset_owner = 0; + qla83xx_wr_reg(base_vha, QLA83XX_IDC_DEV_STATE, + QLA8XXX_DEV_FAILED); + ql_log(ql_log_info, base_vha, 0xb060, "HW State: FAILED.\n"); + qla83xx_schedule_work(base_vha, QLA83XX_IDC_STATE_HANDLER); + } + qla83xx_idc_unlock(base_vha, 0); +} + +/* Work: Execute IDC state handler */ +void +qla83xx_idc_state_handler_work(struct work_struct *work) +{ + struct qla_hw_data *ha = + container_of(work, struct qla_hw_data, idc_state_handler); + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); + uint32_t dev_state = 0; + + qla83xx_idc_lock(base_vha, 0); + qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, &dev_state); + if (dev_state == QLA8XXX_DEV_FAILED || + dev_state == QLA8XXX_DEV_NEED_QUIESCENT) + qla83xx_idc_state_handler(base_vha); + qla83xx_idc_unlock(base_vha, 0); +} + +static int +qla83xx_check_nic_core_fw_alive(scsi_qla_host_t *base_vha) +{ + int rval = QLA_SUCCESS; + unsigned long heart_beat_wait = jiffies + (1 * HZ); + uint32_t heart_beat_counter1, heart_beat_counter2; + + do { + if (time_after(jiffies, heart_beat_wait)) { + ql_dbg(ql_dbg_p3p, base_vha, 0xb07c, + "Nic Core f/w is not alive.\n"); + rval = QLA_FUNCTION_FAILED; + break; + } + + qla83xx_idc_lock(base_vha, 0); + qla83xx_rd_reg(base_vha, QLA83XX_FW_HEARTBEAT, + &heart_beat_counter1); + qla83xx_idc_unlock(base_vha, 0); + msleep(100); + qla83xx_idc_lock(base_vha, 0); + qla83xx_rd_reg(base_vha, QLA83XX_FW_HEARTBEAT, + &heart_beat_counter2); + qla83xx_idc_unlock(base_vha, 0); + } while (heart_beat_counter1 == heart_beat_counter2); + + return rval; +} + +/* Work: Perform NIC Core Reset handling */ +void +qla83xx_nic_core_reset_work(struct work_struct *work) +{ + struct qla_hw_data *ha = + container_of(work, struct qla_hw_data, nic_core_reset); + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); + uint32_t dev_state = 0; + + if (IS_QLA2031(ha)) { + if (qla2xxx_mctp_dump(base_vha) != QLA_SUCCESS) + ql_log(ql_log_warn, base_vha, 0xb081, + "Failed to dump mctp\n"); + return; + } + + if (!ha->flags.nic_core_reset_hdlr_active) { + if (qla83xx_check_nic_core_fw_alive(base_vha) == QLA_SUCCESS) { + qla83xx_idc_lock(base_vha, 0); + qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, + &dev_state); + qla83xx_idc_unlock(base_vha, 0); + if (dev_state != QLA8XXX_DEV_NEED_RESET) { + ql_dbg(ql_dbg_p3p, base_vha, 0xb07a, + "Nic Core f/w is alive.\n"); + return; + } + } + + ha->flags.nic_core_reset_hdlr_active = 1; + if (qla83xx_nic_core_reset(base_vha)) { + /* NIC Core reset failed. */ + ql_dbg(ql_dbg_p3p, base_vha, 0xb061, + "NIC Core reset failed.\n"); + } + ha->flags.nic_core_reset_hdlr_active = 0; + } +} + +/* Work: Handle 8200 IDC aens */ +void +qla83xx_service_idc_aen(struct work_struct *work) +{ + struct qla_hw_data *ha = + container_of(work, struct qla_hw_data, idc_aen); + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); + uint32_t dev_state, idc_control; + + qla83xx_idc_lock(base_vha, 0); + qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, &dev_state); + qla83xx_rd_reg(base_vha, QLA83XX_IDC_CONTROL, &idc_control); + qla83xx_idc_unlock(base_vha, 0); + if (dev_state == QLA8XXX_DEV_NEED_RESET) { + if (idc_control & QLA83XX_IDC_GRACEFUL_RESET) { + ql_dbg(ql_dbg_p3p, base_vha, 0xb062, + "Application requested NIC Core Reset.\n"); + qla83xx_schedule_work(base_vha, QLA83XX_NIC_CORE_RESET); + } else if (qla83xx_check_nic_core_fw_alive(base_vha) == + QLA_SUCCESS) { + ql_dbg(ql_dbg_p3p, base_vha, 0xb07b, + "Other protocol driver requested NIC Core Reset.\n"); + qla83xx_schedule_work(base_vha, QLA83XX_NIC_CORE_RESET); + } + } else if (dev_state == QLA8XXX_DEV_FAILED || + dev_state == QLA8XXX_DEV_NEED_QUIESCENT) { + qla83xx_schedule_work(base_vha, QLA83XX_IDC_STATE_HANDLER); + } +} + +static void +qla83xx_wait_logic(void) +{ + int i; + + /* Yield CPU */ + if (!in_interrupt()) { + /* + * Wait about 200ms before retrying again. + * This controls the number of retries for single + * lock operation. + */ + msleep(100); + schedule(); + } else { + for (i = 0; i < 20; i++) + cpu_relax(); /* This a nop instr on i386 */ + } +} + +static int +qla83xx_force_lock_recovery(scsi_qla_host_t *base_vha) +{ + int rval; + uint32_t data; + uint32_t idc_lck_rcvry_stage_mask = 0x3; + uint32_t idc_lck_rcvry_owner_mask = 0x3c; + struct qla_hw_data *ha = base_vha->hw; + ql_dbg(ql_dbg_p3p, base_vha, 0xb086, + "Trying force recovery of the IDC lock.\n"); + + rval = qla83xx_rd_reg(base_vha, QLA83XX_IDC_LOCK_RECOVERY, &data); + if (rval) + return rval; + + if ((data & idc_lck_rcvry_stage_mask) > 0) { + return QLA_SUCCESS; + } else { + data = (IDC_LOCK_RECOVERY_STAGE1) | (ha->portnum << 2); + rval = qla83xx_wr_reg(base_vha, QLA83XX_IDC_LOCK_RECOVERY, + data); + if (rval) + return rval; + + msleep(200); + + rval = qla83xx_rd_reg(base_vha, QLA83XX_IDC_LOCK_RECOVERY, + &data); + if (rval) + return rval; + + if (((data & idc_lck_rcvry_owner_mask) >> 2) == ha->portnum) { + data &= (IDC_LOCK_RECOVERY_STAGE2 | + ~(idc_lck_rcvry_stage_mask)); + rval = qla83xx_wr_reg(base_vha, + QLA83XX_IDC_LOCK_RECOVERY, data); + if (rval) + return rval; + + /* Forcefully perform IDC UnLock */ + rval = qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_UNLOCK, + &data); + if (rval) + return rval; + /* Clear lock-id by setting 0xff */ + rval = qla83xx_wr_reg(base_vha, QLA83XX_DRIVER_LOCKID, + 0xff); + if (rval) + return rval; + /* Clear lock-recovery by setting 0x0 */ + rval = qla83xx_wr_reg(base_vha, + QLA83XX_IDC_LOCK_RECOVERY, 0x0); + if (rval) + return rval; + } else + return QLA_SUCCESS; + } + + return rval; +} + +static int +qla83xx_idc_lock_recovery(scsi_qla_host_t *base_vha) +{ + int rval = QLA_SUCCESS; + uint32_t o_drv_lockid, n_drv_lockid; + unsigned long lock_recovery_timeout; + + lock_recovery_timeout = jiffies + QLA83XX_MAX_LOCK_RECOVERY_WAIT; +retry_lockid: + rval = qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCKID, &o_drv_lockid); + if (rval) + goto exit; + + /* MAX wait time before forcing IDC Lock recovery = 2 secs */ + if (time_after_eq(jiffies, lock_recovery_timeout)) { + if (qla83xx_force_lock_recovery(base_vha) == QLA_SUCCESS) + return QLA_SUCCESS; + else + return QLA_FUNCTION_FAILED; + } + + rval = qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCKID, &n_drv_lockid); + if (rval) + goto exit; + + if (o_drv_lockid == n_drv_lockid) { + qla83xx_wait_logic(); + goto retry_lockid; + } else + return QLA_SUCCESS; + +exit: + return rval; +} + +void +qla83xx_idc_lock(scsi_qla_host_t *base_vha, uint16_t requester_id) +{ + uint16_t options = (requester_id << 15) | BIT_6; + uint32_t data; + uint32_t lock_owner; + struct qla_hw_data *ha = base_vha->hw; + + /* IDC-lock implementation using driver-lock/lock-id remote registers */ +retry_lock: + if (qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCK, &data) + == QLA_SUCCESS) { + if (data) { + /* Setting lock-id to our function-number */ + qla83xx_wr_reg(base_vha, QLA83XX_DRIVER_LOCKID, + ha->portnum); + } else { + qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCKID, + &lock_owner); + ql_dbg(ql_dbg_p3p, base_vha, 0xb063, + "Failed to acquire IDC lock, acquired by %d, " + "retrying...\n", lock_owner); + + /* Retry/Perform IDC-Lock recovery */ + if (qla83xx_idc_lock_recovery(base_vha) + == QLA_SUCCESS) { + qla83xx_wait_logic(); + goto retry_lock; + } else + ql_log(ql_log_warn, base_vha, 0xb075, + "IDC Lock recovery FAILED.\n"); + } + + } + + return; + + /* XXX: IDC-lock implementation using access-control mbx */ +retry_lock2: + if (qla83xx_access_control(base_vha, options, 0, 0, NULL)) { + ql_dbg(ql_dbg_p3p, base_vha, 0xb072, + "Failed to acquire IDC lock. retrying...\n"); + /* Retry/Perform IDC-Lock recovery */ + if (qla83xx_idc_lock_recovery(base_vha) == QLA_SUCCESS) { + qla83xx_wait_logic(); + goto retry_lock2; + } else + ql_log(ql_log_warn, base_vha, 0xb076, + "IDC Lock recovery FAILED.\n"); + } + + return; +} + +void +qla83xx_idc_unlock(scsi_qla_host_t *base_vha, uint16_t requester_id) +{ + uint16_t options = (requester_id << 15) | BIT_7, retry; + uint32_t data; + struct qla_hw_data *ha = base_vha->hw; + + /* IDC-unlock implementation using driver-unlock/lock-id + * remote registers + */ + retry = 0; +retry_unlock: + if (qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCKID, &data) + == QLA_SUCCESS) { + if (data == ha->portnum) { + qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_UNLOCK, &data); + /* Clearing lock-id by setting 0xff */ + qla83xx_wr_reg(base_vha, QLA83XX_DRIVER_LOCKID, 0xff); + } else if (retry < 10) { + /* SV: XXX: IDC unlock retrying needed here? */ + + /* Retry for IDC-unlock */ + qla83xx_wait_logic(); + retry++; + ql_dbg(ql_dbg_p3p, base_vha, 0xb064, + "Failed to release IDC lock, retyring=%d\n", retry); + goto retry_unlock; + } + } else if (retry < 10) { + /* Retry for IDC-unlock */ + qla83xx_wait_logic(); + retry++; + ql_dbg(ql_dbg_p3p, base_vha, 0xb065, + "Failed to read drv-lockid, retyring=%d\n", retry); + goto retry_unlock; + } + + return; + + /* XXX: IDC-unlock implementation using access-control mbx */ + retry = 0; +retry_unlock2: + if (qla83xx_access_control(base_vha, options, 0, 0, NULL)) { + if (retry < 10) { + /* Retry for IDC-unlock */ + qla83xx_wait_logic(); + retry++; + ql_dbg(ql_dbg_p3p, base_vha, 0xb066, + "Failed to release IDC lock, retyring=%d\n", retry); + goto retry_unlock2; + } + } + + return; +} + +int +__qla83xx_set_drv_presence(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + uint32_t drv_presence; + + rval = qla83xx_rd_reg(vha, QLA83XX_IDC_DRV_PRESENCE, &drv_presence); + if (rval == QLA_SUCCESS) { + drv_presence |= (1 << ha->portnum); + rval = qla83xx_wr_reg(vha, QLA83XX_IDC_DRV_PRESENCE, + drv_presence); + } + + return rval; +} + +int +qla83xx_set_drv_presence(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + + qla83xx_idc_lock(vha, 0); + rval = __qla83xx_set_drv_presence(vha); + qla83xx_idc_unlock(vha, 0); + + return rval; +} + +int +__qla83xx_clear_drv_presence(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + uint32_t drv_presence; + + rval = qla83xx_rd_reg(vha, QLA83XX_IDC_DRV_PRESENCE, &drv_presence); + if (rval == QLA_SUCCESS) { + drv_presence &= ~(1 << ha->portnum); + rval = qla83xx_wr_reg(vha, QLA83XX_IDC_DRV_PRESENCE, + drv_presence); + } + + return rval; +} + +int +qla83xx_clear_drv_presence(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + + qla83xx_idc_lock(vha, 0); + rval = __qla83xx_clear_drv_presence(vha); + qla83xx_idc_unlock(vha, 0); + + return rval; +} + +static void +qla83xx_need_reset_handler(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t drv_ack, drv_presence; + unsigned long ack_timeout; + + /* Wait for IDC ACK from all functions (DRV-ACK == DRV-PRESENCE) */ + ack_timeout = jiffies + (ha->fcoe_reset_timeout * HZ); + while (1) { + qla83xx_rd_reg(vha, QLA83XX_IDC_DRIVER_ACK, &drv_ack); + qla83xx_rd_reg(vha, QLA83XX_IDC_DRV_PRESENCE, &drv_presence); + if ((drv_ack & drv_presence) == drv_presence) + break; + + if (time_after_eq(jiffies, ack_timeout)) { + ql_log(ql_log_warn, vha, 0xb067, + "RESET ACK TIMEOUT! drv_presence=0x%x " + "drv_ack=0x%x\n", drv_presence, drv_ack); + /* + * The function(s) which did not ack in time are forced + * to withdraw any further participation in the IDC + * reset. + */ + if (drv_ack != drv_presence) + qla83xx_wr_reg(vha, QLA83XX_IDC_DRV_PRESENCE, + drv_ack); + break; + } + + qla83xx_idc_unlock(vha, 0); + msleep(1000); + qla83xx_idc_lock(vha, 0); + } + + qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, QLA8XXX_DEV_COLD); + ql_log(ql_log_info, vha, 0xb068, "HW State: COLD/RE-INIT.\n"); +} + +static int +qla83xx_device_bootstrap(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + uint32_t idc_control; + + qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, QLA8XXX_DEV_INITIALIZING); + ql_log(ql_log_info, vha, 0xb069, "HW State: INITIALIZING.\n"); + + /* Clearing IDC-Control Graceful-Reset Bit before resetting f/w */ + __qla83xx_get_idc_control(vha, &idc_control); + idc_control &= ~QLA83XX_IDC_GRACEFUL_RESET; + __qla83xx_set_idc_control(vha, 0); + + qla83xx_idc_unlock(vha, 0); + rval = qla83xx_restart_nic_firmware(vha); + qla83xx_idc_lock(vha, 0); + + if (rval != QLA_SUCCESS) { + ql_log(ql_log_fatal, vha, 0xb06a, + "Failed to restart NIC f/w.\n"); + qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, QLA8XXX_DEV_FAILED); + ql_log(ql_log_info, vha, 0xb06b, "HW State: FAILED.\n"); + } else { + ql_dbg(ql_dbg_p3p, vha, 0xb06c, + "Success in restarting nic f/w.\n"); + qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, QLA8XXX_DEV_READY); + ql_log(ql_log_info, vha, 0xb06d, "HW State: READY.\n"); + } + + return rval; +} + +/* Assumes idc_lock always held on entry */ +int +qla83xx_idc_state_handler(scsi_qla_host_t *base_vha) +{ + struct qla_hw_data *ha = base_vha->hw; + int rval = QLA_SUCCESS; + unsigned long dev_init_timeout; + uint32_t dev_state; + + /* Wait for MAX-INIT-TIMEOUT for the device to go ready */ + dev_init_timeout = jiffies + (ha->fcoe_dev_init_timeout * HZ); + + while (1) { + + if (time_after_eq(jiffies, dev_init_timeout)) { + ql_log(ql_log_warn, base_vha, 0xb06e, + "Initialization TIMEOUT!\n"); + /* Init timeout. Disable further NIC Core + * communication. + */ + qla83xx_wr_reg(base_vha, QLA83XX_IDC_DEV_STATE, + QLA8XXX_DEV_FAILED); + ql_log(ql_log_info, base_vha, 0xb06f, + "HW State: FAILED.\n"); + } + + qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, &dev_state); + switch (dev_state) { + case QLA8XXX_DEV_READY: + if (ha->flags.nic_core_reset_owner) + qla83xx_idc_audit(base_vha, + IDC_AUDIT_COMPLETION); + ha->flags.nic_core_reset_owner = 0; + ql_dbg(ql_dbg_p3p, base_vha, 0xb070, + "Reset_owner reset by 0x%x.\n", + ha->portnum); + goto exit; + case QLA8XXX_DEV_COLD: + if (ha->flags.nic_core_reset_owner) + rval = qla83xx_device_bootstrap(base_vha); + else { + /* Wait for AEN to change device-state */ + qla83xx_idc_unlock(base_vha, 0); + msleep(1000); + qla83xx_idc_lock(base_vha, 0); + } + break; + case QLA8XXX_DEV_INITIALIZING: + /* Wait for AEN to change device-state */ + qla83xx_idc_unlock(base_vha, 0); + msleep(1000); + qla83xx_idc_lock(base_vha, 0); + break; + case QLA8XXX_DEV_NEED_RESET: + if (!ql2xdontresethba && ha->flags.nic_core_reset_owner) + qla83xx_need_reset_handler(base_vha); + else { + /* Wait for AEN to change device-state */ + qla83xx_idc_unlock(base_vha, 0); + msleep(1000); + qla83xx_idc_lock(base_vha, 0); + } + /* reset timeout value after need reset handler */ + dev_init_timeout = jiffies + + (ha->fcoe_dev_init_timeout * HZ); + break; + case QLA8XXX_DEV_NEED_QUIESCENT: + /* XXX: DEBUG for now */ + qla83xx_idc_unlock(base_vha, 0); + msleep(1000); + qla83xx_idc_lock(base_vha, 0); + break; + case QLA8XXX_DEV_QUIESCENT: + /* XXX: DEBUG for now */ + if (ha->flags.quiesce_owner) + goto exit; + + qla83xx_idc_unlock(base_vha, 0); + msleep(1000); + qla83xx_idc_lock(base_vha, 0); + dev_init_timeout = jiffies + + (ha->fcoe_dev_init_timeout * HZ); + break; + case QLA8XXX_DEV_FAILED: + if (ha->flags.nic_core_reset_owner) + qla83xx_idc_audit(base_vha, + IDC_AUDIT_COMPLETION); + ha->flags.nic_core_reset_owner = 0; + __qla83xx_clear_drv_presence(base_vha); + qla83xx_idc_unlock(base_vha, 0); + qla8xxx_dev_failed_handler(base_vha); + rval = QLA_FUNCTION_FAILED; + qla83xx_idc_lock(base_vha, 0); + goto exit; + case QLA8XXX_BAD_VALUE: + qla83xx_idc_unlock(base_vha, 0); + msleep(1000); + qla83xx_idc_lock(base_vha, 0); + break; + default: + ql_log(ql_log_warn, base_vha, 0xb071, + "Unknow Device State: %x.\n", dev_state); + qla83xx_idc_unlock(base_vha, 0); + qla8xxx_dev_failed_handler(base_vha); + rval = QLA_FUNCTION_FAILED; + qla83xx_idc_lock(base_vha, 0); + goto exit; + } + } + +exit: + return rval; +} + +void +qla2x00_disable_board_on_pci_error(struct work_struct *work) +{ + struct qla_hw_data *ha = container_of(work, struct qla_hw_data, + board_disable); + struct pci_dev *pdev = ha->pdev; + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); + + ql_log(ql_log_warn, base_vha, 0x015b, + "Disabling adapter.\n"); + + set_bit(UNLOADING, &base_vha->dpc_flags); + + qla2x00_delete_all_vps(ha, base_vha); + + qla2x00_abort_all_cmds(base_vha, DID_NO_CONNECT << 16); + + qla2x00_dfs_remove(base_vha); + + qla84xx_put_chip(base_vha); + + if (base_vha->timer_active) + qla2x00_stop_timer(base_vha); + + base_vha->flags.online = 0; + + qla2x00_destroy_deferred_work(ha); + + /* + * Do not try to stop beacon blink as it will issue a mailbox + * command. + */ + qla2x00_free_sysfs_attr(base_vha, false); + + fc_remove_host(base_vha->host); + + scsi_remove_host(base_vha->host); + + base_vha->flags.init_done = 0; + qla25xx_delete_queues(base_vha); + qla2x00_free_irqs(base_vha); + qla2x00_free_fcports(base_vha); + qla2x00_mem_free(ha); + qla82xx_md_free(base_vha); + qla2x00_free_queues(ha); + + scsi_host_put(base_vha->host); + + qla2x00_unmap_iobases(ha); + + 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); + +} + /************************************************************************** * qla2x00_do_dpc * This kernel thread is a task that is schedule by the interrupt handler @@ -3311,59 +4837,67 @@ qla2x00_do_dpc(void *data) ha = (struct qla_hw_data *)data; base_vha = pci_get_drvdata(ha->pdev); - set_user_nice(current, -20); + set_user_nice(current, MIN_NICE); set_current_state(TASK_INTERRUPTIBLE); while (!kthread_should_stop()) { - DEBUG3(printk("qla2x00: DPC handler sleeping\n")); + ql_dbg(ql_dbg_dpc, base_vha, 0x4000, + "DPC handler sleeping.\n"); schedule(); __set_current_state(TASK_RUNNING); - DEBUG3(printk("qla2x00: DPC handler waking up\n")); - - /* Initialization not yet finished. Don't do anything yet. */ - if (!base_vha->flags.init_done) - continue; + if (!base_vha->flags.init_done || ha->flags.mbox_busy) + goto end_loop; if (ha->flags.eeh_busy) { - DEBUG17(qla_printk(KERN_WARNING, ha, - "qla2x00_do_dpc: dpc_flags: %lx\n", - base_vha->dpc_flags)); - continue; + ql_dbg(ql_dbg_dpc, base_vha, 0x4003, + "eeh_busy=%d.\n", ha->flags.eeh_busy); + goto end_loop; } - DEBUG3(printk("scsi(%ld): DPC handler\n", base_vha->host_no)); - ha->dpc_active = 1; - if (ha->flags.mbox_busy) { - ha->dpc_active = 0; - continue; - } + ql_dbg(ql_dbg_dpc + ql_dbg_verbose, base_vha, 0x4001, + "DPC handler waking up, 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); - qla_printk(KERN_INFO, ha, - "HW State: FAILED\n"); - qla82xx_device_state_handler(base_vha); - continue; + if (IS_P3P_TYPE(ha)) { + if (IS_QLA8044(ha)) { + if (test_and_clear_bit(ISP_UNRECOVERABLE, + &base_vha->dpc_flags)) { + qla8044_idc_lock(ha); + qla8044_wr_direct(base_vha, + QLA8044_CRB_DEV_STATE_INDEX, + QLA8XXX_DEV_FAILED); + qla8044_idc_unlock(ha); + ql_log(ql_log_info, base_vha, 0x4004, + "HW State: FAILED.\n"); + qla8044_device_state_handler(base_vha); + continue; + } + + } else { + if (test_and_clear_bit(ISP_UNRECOVERABLE, + &base_vha->dpc_flags)) { + qla82xx_idc_lock(ha); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, + QLA8XXX_DEV_FAILED); + qla82xx_idc_unlock(ha); + ql_log(ql_log_info, base_vha, 0x0151, + "HW State: FAILED.\n"); + qla82xx_device_state_handler(base_vha); + continue; + } } if (test_and_clear_bit(FCOE_CTX_RESET_NEEDED, &base_vha->dpc_flags)) { - DEBUG(printk(KERN_INFO - "scsi(%ld): dpc: sched " - "qla82xx_fcoe_ctx_reset ha = %p\n", - base_vha->host_no, ha)); + 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)) { @@ -3377,18 +4911,57 @@ qla2x00_do_dpc(void *data) &base_vha->dpc_flags); } - DEBUG(printk("scsi(%ld): dpc:" - " qla82xx_fcoe_ctx_reset end\n", - base_vha->host_no)); + ql_dbg(ql_dbg_dpc, base_vha, 0x4006, + "FCoE context reset end.\n"); + } + } else if (IS_QLAFX00(ha)) { + if (test_and_clear_bit(ISP_UNRECOVERABLE, + &base_vha->dpc_flags)) { + ql_dbg(ql_dbg_dpc, base_vha, 0x4020, + "Firmware Reset Recovery\n"); + if (qlafx00_reset_initialize(base_vha)) { + /* Failed. Abort isp later. */ + if (!test_bit(UNLOADING, + &base_vha->dpc_flags)) { + set_bit(ISP_UNRECOVERABLE, + &base_vha->dpc_flags); + ql_dbg(ql_dbg_dpc, base_vha, + 0x4021, + "Reset Recovery Failed\n"); + } + } + } + + if (test_and_clear_bit(FX00_TARGET_SCAN, + &base_vha->dpc_flags)) { + ql_dbg(ql_dbg_dpc, base_vha, 0x4022, + "ISPFx00 Target Scan scheduled\n"); + if (qlafx00_rescan_isp(base_vha)) { + if (!test_bit(UNLOADING, + &base_vha->dpc_flags)) + set_bit(ISP_UNRECOVERABLE, + &base_vha->dpc_flags); + ql_dbg(ql_dbg_dpc, base_vha, 0x401e, + "ISPFx00 Target Scan Failed\n"); + } + ql_dbg(ql_dbg_dpc, base_vha, 0x401f, + "ISPFx00 Target Scan End\n"); + } + if (test_and_clear_bit(FX00_HOST_INFO_RESEND, + &base_vha->dpc_flags)) { + ql_dbg(ql_dbg_dpc, base_vha, 0x4023, + "ISPFx00 Host Info resend scheduled\n"); + qlafx00_fx_disc(base_vha, + &base_vha->hw->mr.fcport, + FXDISC_REG_HOST_INFO); } } if (test_and_clear_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags)) { - DEBUG(printk("scsi(%ld): dpc: sched " - "qla2x00_abort_isp ha = %p\n", - base_vha->host_no, ha)); + ql_dbg(ql_dbg_dpc, base_vha, 0x4007, + "ISP abort scheduled.\n"); if (!(test_and_set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags))) { @@ -3401,39 +4974,71 @@ qla2x00_do_dpc(void *data) &base_vha->dpc_flags); } - DEBUG(printk("scsi(%ld): dpc: qla2x00_abort_isp end\n", - base_vha->host_no)); + ql_dbg(ql_dbg_dpc, base_vha, 0x4008, + "ISP abort end.\n"); } - if (test_bit(FCPORT_UPDATE_NEEDED, &base_vha->dpc_flags)) { + if (test_and_clear_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(SCR_PENDING, &base_vha->dpc_flags)) { + int ret; + ret = qla2x00_send_change_request(base_vha, 0x3, 0); + if (ret != QLA_SUCCESS) + ql_log(ql_log_warn, base_vha, 0x121, + "Failed to enable receiving of RSCN " + "requests: 0x%x.\n", ret); + clear_bit(SCR_PENDING, &base_vha->dpc_flags); + } + + if (IS_QLAFX00(ha)) + goto loop_resync_check; + if (test_bit(ISP_QUIESCE_NEEDED, &base_vha->dpc_flags)) { - DEBUG(printk(KERN_INFO "scsi(%ld): dpc: sched " - "qla2x00_quiesce_needed ha = %p\n", - base_vha->host_no, ha)); - 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, 0x4009, + "Quiescence mode scheduled.\n"); + if (IS_P3P_TYPE(ha)) { + if (IS_QLA82XX(ha)) + qla82xx_device_state_handler(base_vha); + if (IS_QLA8044(ha)) + qla8044_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); + if (IS_QLA82XX(ha)) { + qla82xx_idc_lock(ha); + qla82xx_clear_qsnt_ready( + base_vha); + qla82xx_idc_unlock(ha); + } else if (IS_QLA8044(ha)) { + qla8044_idc_lock(ha); + qla8044_clear_qsnt_ready( + base_vha); + qla8044_idc_unlock(ha); + } + } + } else { + clear_bit(ISP_QUIESCE_NEEDED, + &base_vha->dpc_flags); + qla2x00_quiesce_io(base_vha); } + ql_dbg(ql_dbg_dpc, base_vha, 0x400a, + "Quiescence mode end.\n"); } if (test_and_clear_bit(RESET_MARKER_NEEDED, - &base_vha->dpc_flags) && + &base_vha->dpc_flags) && (!(test_and_set_bit(RESET_ACTIVE, &base_vha->dpc_flags)))) { - DEBUG(printk("scsi(%ld): qla2x00_reset_marker()\n", - base_vha->host_no)); - + 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 */ @@ -3442,19 +5047,18 @@ qla2x00_do_dpc(void *data) !test_bit(LOOP_RESYNC_NEEDED, &base_vha->dpc_flags) && atomic_read(&base_vha->loop_state) != LOOP_DOWN) { - DEBUG(printk("scsi(%ld): qla2x00_port_login()\n", - base_vha->host_no)); + ql_dbg(ql_dbg_dpc, base_vha, 0x400d, + "Relogin scheduled.\n"); qla2x00_relogin(base_vha); - - DEBUG(printk("scsi(%ld): qla2x00_port_login - end\n", - base_vha->host_no)); + ql_dbg(ql_dbg_dpc, base_vha, 0x400e, + "Relogin end.\n"); } - +loop_resync_check: if (test_and_clear_bit(LOOP_RESYNC_NEEDED, - &base_vha->dpc_flags)) { + &base_vha->dpc_flags)) { - DEBUG(printk("scsi(%ld): qla2x00_loop_resync()\n", - base_vha->host_no)); + ql_dbg(ql_dbg_dpc, base_vha, 0x400f, + "Loop resync scheduled.\n"); if (!(test_and_set_bit(LOOP_RESYNC_ACTIVE, &base_vha->dpc_flags))) { @@ -3465,31 +5069,40 @@ qla2x00_do_dpc(void *data) &base_vha->dpc_flags); } - DEBUG(printk("scsi(%ld): qla2x00_loop_resync - end\n", - base_vha->host_no)); + ql_dbg(ql_dbg_dpc, base_vha, 0x4010, + "Loop resync end.\n"); } + if (IS_QLAFX00(ha)) + goto intr_on_check; + 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); } +intr_on_check: 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); + &base_vha->dpc_flags)) { + if (ha->beacon_blink_led == 1) + ha->isp_ops->beacon_blink(base_vha); + } - qla2x00_do_dpc_all_vps(base_vha); + if (!IS_QLAFX00(ha)) + 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); - DEBUG(printk("scsi(%ld): DPC handler exiting\n", base_vha->host_no)); + ql_dbg(ql_dbg_dpc, base_vha, 0x4011, + "DPC handler exiting.\n"); /* * Make sure that nobody tries to wake us up again. @@ -3538,74 +5151,6 @@ qla2x00_rst_aen(scsi_qla_host_t *vha) } } -static void -qla2x00_sp_free_dma(srb_t *sp) -{ - struct scsi_cmnd *cmd = sp->cmd; - struct qla_hw_data *ha = sp->fcport->vha->hw; - - 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, sp->ctx, - ((struct crc_context *)sp->ctx)->crc_ctx_dma); - sp->flags &= ~SRB_CRC_CTX_DMA_VALID; - } - - CMD_SP(cmd) = NULL; -} - -static void -qla2x00_sp_final_compl(struct qla_hw_data *ha, srb_t *sp) -{ - struct scsi_cmnd *cmd = sp->cmd; - - qla2x00_sp_free_dma(sp); - - if (sp->flags & SRB_FCP_CMND_DMA_VALID) { - struct ct6_dsd *ctx = sp->ctx; - dma_pool_free(ha->fcp_cmnd_dma_pool, ctx->fcp_cmnd, - ctx->fcp_cmnd_dma); - list_splice(&ctx->dsd_list, &ha->gbl_dsd_list); - ha->gbl_dsd_inuse -= ctx->dsd_use_cnt; - ha->gbl_dsd_avail += ctx->dsd_use_cnt; - mempool_free(sp->ctx, ha->ctx_mempool); - sp->ctx = NULL; - } - - mempool_free(sp, ha->srb_mempool); - cmd->scsi_done(cmd); -} - -void -qla2x00_sp_compl(struct qla_hw_data *ha, srb_t *sp) -{ - if (atomic_read(&sp->ref_count) == 0) { - DEBUG2(qla_printk(KERN_WARNING, ha, - "SP reference-count to ZERO -- sp=%p\n", sp)); - DEBUG2(BUG()); - return; - } - if (!atomic_dec_and_test(&sp->ref_count)) - return; - qla2x00_sp_final_compl(ha, sp); -} - /************************************************************************** * qla2x00_timer * @@ -3626,21 +5171,41 @@ qla2x00_timer(scsi_qla_host_t *vha) 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)) + /* + * Hardware read to raise pending EEH errors during mailbox waits. If + * the read returns -1 then disable the board. + */ + if (!pci_channel_offline(ha->pdev)) { pci_read_config_word(ha->pdev, PCI_VENDOR_ID, &w); + if (w == 0xffff) + /* + * Schedule this on the default system workqueue so that + * all the adapter workqueues and the DPC thread can be + * shutdown cleanly. + */ + schedule_work(&ha->board_disable); + } /* Make sure qla82xx_watchdog is run only for physical port */ - if (!vha->vp_idx && IS_QLA82XX(ha)) { + if (!vha->vp_idx && IS_P3P_TYPE(ha)) { if (test_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags)) start_dpc++; - qla82xx_watchdog(vha); + if (IS_QLA82XX(ha)) + qla82xx_watchdog(vha); + else if (IS_QLA8044(ha)) + qla8044_watchdog(vha); } + if (!vha->vp_idx && IS_QLAFX00(ha)) + qlafx00_timer_routine(vha); + /* Loop down handler. */ if (atomic_read(&vha->loop_down_timer) > 0 && !(test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags)) && @@ -3650,9 +5215,8 @@ qla2x00_timer(scsi_qla_host_t *vha) if (atomic_read(&vha->loop_down_timer) == vha->loop_down_abort_time) { - DEBUG(printk("scsi(%ld): Loop Down - aborting the " - "queues before time expire\n", - vha->host_no)); + 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); @@ -3667,14 +5231,14 @@ qla2x00_timer(scsi_qla_host_t *vha) cpu_flags); req = ha->req_q_map[0]; for (index = 1; - index < MAX_OUTSTANDING_COMMANDS; + index < req->num_outstanding_cmds; index++) { fc_port_t *sfcp; sp = req->outstanding_cmds[index]; if (!sp) continue; - if (sp->ctx && !IS_PROT_IO(sp)) + if (sp->type != SRB_SCSI_CMD) continue; sfcp = sp->fcport; if (!(sfcp->flags & FCF_FCP2_DEVICE)) @@ -3697,10 +5261,7 @@ qla2x00_timer(scsi_qla_host_t *vha) /* 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)) { - DEBUG(printk("scsi(%ld): Loop down - " - "aborting ISP.\n", - vha->host_no)); - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x6009, "Loop down - aborting ISP.\n"); if (IS_QLA82XX(ha)) @@ -3711,15 +5272,17 @@ qla2x00_timer(scsi_qla_host_t *vha) &vha->dpc_flags); } } - DEBUG3(printk("scsi(%ld): Loop Down - seconds remaining %d\n", - vha->host_no, - atomic_read(&vha->loop_down_timer))); + 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)) { - set_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags); - start_dpc++; + /* There is no beacon_blink function for ISP82xx */ + if (!IS_P3P_TYPE(ha)) { + set_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags); + start_dpc++; + } } /* Process any deferred work. */ @@ -3736,15 +5299,34 @@ qla2x00_timer(scsi_qla_host_t *vha) 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))) + 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 8 +#define FW_BLOBS 11 #define FW_ISP21XX 0 #define FW_ISP22XX 1 #define FW_ISP2300 2 @@ -3753,6 +5335,9 @@ qla2x00_timer(scsi_qla_host_t *vha) #define FW_ISP25XX 5 #define FW_ISP81XX 6 #define FW_ISP82XX 7 +#define FW_ISP2031 8 +#define FW_ISP8031 9 +#define FW_ISP27XX 10 #define FW_FILE_ISP21XX "ql2100_fw.bin" #define FW_FILE_ISP22XX "ql2200_fw.bin" @@ -3762,6 +5347,10 @@ qla2x00_timer(scsi_qla_host_t *vha) #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" +#define FW_FILE_ISP27XX "ql2700_fw.bin" + static DEFINE_MUTEX(qla_fw_lock); @@ -3774,6 +5363,9 @@ static struct fw_blob qla_fw_blobs[FW_BLOBS] = { { .name = FW_FILE_ISP25XX, }, { .name = FW_FILE_ISP81XX, }, { .name = FW_FILE_ISP82XX, }, + { .name = FW_FILE_ISP2031, }, + { .name = FW_FILE_ISP8031, }, + { .name = FW_FILE_ISP27XX, }, }; struct fw_blob * @@ -3782,7 +5374,6 @@ qla2x00_request_firmware(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; struct fw_blob *blob; - blob = NULL; if (IS_QLA2100(ha)) { blob = &qla_fw_blobs[FW_ISP21XX]; } else if (IS_QLA2200(ha)) { @@ -3799,6 +5390,14 @@ qla2x00_request_firmware(scsi_qla_host_t *vha) 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 if (IS_QLA27XX(ha)) { + blob = &qla_fw_blobs[FW_ISP27XX]; + } else { + return NULL; } mutex_lock(&qla_fw_lock); @@ -3806,8 +5405,8 @@ qla2x00_request_firmware(scsi_qla_host_t *vha) goto out; if (request_firmware(&blob->fw, blob->name, &ha->pdev->dev)) { - DEBUG2(printk("scsi(%ld): Failed to load firmware image " - "(%s).\n", vha->host_no, blob->name)); + ql_log(ql_log_warn, vha, 0x0063, + "Failed to load firmware image (%s).\n", blob->name); blob->fw = NULL; blob = NULL; goto out; @@ -3825,8 +5424,7 @@ qla2x00_release_firmware(void) 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); + release_firmware(qla_fw_blobs[idx].fw); mutex_unlock(&qla_fw_lock); } @@ -3836,8 +5434,8 @@ 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; - DEBUG2(qla_printk(KERN_WARNING, ha, "error_detected:state %x\n", - state)); + ql_dbg(ql_dbg_aer, vha, 0x9000, + "PCI error detected, state %x.\n", state); switch (state) { case pci_channel_io_normal: @@ -3848,13 +5446,8 @@ qla2xxx_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) /* For ISP82XX complete any pending mailbox cmd */ if (IS_QLA82XX(ha)) { ha->flags.isp82xx_fw_hung = 1; - if (ha->flags.mbox_busy) { - ha->flags.mbox_int = 1; - DEBUG2(qla_printk(KERN_ERR, ha, - "Due to pci channel io frozen, doing premature " - "completion of mbx command\n")); - complete(&ha->mbx_intr_comp); - } + 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); @@ -3900,8 +5493,8 @@ qla2xxx_pci_mmio_enabled(struct pci_dev *pdev) spin_unlock_irqrestore(&ha->hardware_lock, flags); if (risc_paused) { - qla_printk(KERN_INFO, ha, "RISC paused -- mmio_enabled, " - "Dumping firmware!\n"); + 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; @@ -3909,7 +5502,8 @@ qla2xxx_pci_mmio_enabled(struct pci_dev *pdev) return PCI_ERS_RESULT_RECOVERED; } -uint32_t qla82xx_error_recovery(scsi_qla_host_t *base_vha) +static uint32_t +qla82xx_error_recovery(scsi_qla_host_t *base_vha) { uint32_t rval = QLA_FUNCTION_FAILED; uint32_t drv_active = 0; @@ -3917,8 +5511,8 @@ uint32_t qla82xx_error_recovery(scsi_qla_host_t *base_vha) int fn; struct pci_dev *other_pdev = NULL; - DEBUG17(qla_printk(KERN_INFO, ha, - "scsi(%ld): In qla82xx_error_recovery\n", base_vha->host_no)); + ql_dbg(ql_dbg_aer, base_vha, 0x9006, + "Entered %s.\n", __func__); set_bit(ABORT_ISP_ACTIVE, &base_vha->dpc_flags); @@ -3932,8 +5526,8 @@ uint32_t qla82xx_error_recovery(scsi_qla_host_t *base_vha) fn = PCI_FUNC(ha->pdev->devfn); while (fn > 0) { fn--; - DEBUG17(qla_printk(KERN_INFO, ha, - "Finding pci device at function = 0x%x\n", 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), @@ -3942,9 +5536,9 @@ uint32_t qla82xx_error_recovery(scsi_qla_host_t *base_vha) if (!other_pdev) continue; if (atomic_read(&other_pdev->enable_cnt)) { - DEBUG17(qla_printk(KERN_INFO, ha, - "Found PCI func available and enabled at 0x%x\n", - fn)); + 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; } @@ -3953,19 +5547,20 @@ uint32_t qla82xx_error_recovery(scsi_qla_host_t *base_vha) if (!fn) { /* Reset owner */ - DEBUG17(qla_printk(KERN_INFO, ha, - "This devfn is reset owner = 0x%x\n", ha->pdev->devfn)); + 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); + QLA8XXX_DEV_INITIALIZING); qla82xx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION, QLA82XX_IDC_VERSION); drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); - DEBUG17(qla_printk(KERN_INFO, ha, - "drv_active = 0x%x\n", 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 @@ -3978,14 +5573,16 @@ uint32_t qla82xx_error_recovery(scsi_qla_host_t *base_vha) qla82xx_idc_lock(ha); if (rval != QLA_SUCCESS) { - qla_printk(KERN_INFO, ha, "HW State: FAILED\n"); + 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); + QLA8XXX_DEV_FAILED); } else { - qla_printk(KERN_INFO, ha, "HW State: READY\n"); + ql_log(ql_log_info, base_vha, 0x900c, + "HW State: READY.\n"); qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, - QLA82XX_DEV_READY); + QLA8XXX_DEV_READY); qla82xx_idc_unlock(ha); ha->flags.isp82xx_fw_hung = 0; rval = qla82xx_restart_isp(base_vha); @@ -3996,10 +5593,11 @@ uint32_t qla82xx_error_recovery(scsi_qla_host_t *base_vha) } qla82xx_idc_unlock(ha); } else { - DEBUG17(qla_printk(KERN_INFO, ha, - "This devfn is not reset owner = 0x%x\n", ha->pdev->devfn)); + 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)) { + QLA8XXX_DEV_READY)) { ha->flags.isp82xx_fw_hung = 0; rval = qla82xx_restart_isp(base_vha); qla82xx_idc_lock(ha); @@ -4021,7 +5619,8 @@ qla2xxx_pci_slot_reset(struct pci_dev *pdev) struct rsp_que *rsp; int rc, retries = 10; - DEBUG17(qla_printk(KERN_WARNING, ha, "slot_reset\n")); + 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. @@ -4042,7 +5641,7 @@ qla2xxx_pci_slot_reset(struct pci_dev *pdev) rc = pci_enable_device(pdev); if (rc) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, base_vha, 0x9005, "Can't re-enable PCI device after reset.\n"); goto exit_slot_reset; } @@ -4072,8 +5671,8 @@ qla2xxx_pci_slot_reset(struct pci_dev *pdev) exit_slot_reset: - DEBUG17(qla_printk(KERN_WARNING, ha, - "slot_reset-return:ret=%x\n", ret)); + ql_dbg(ql_dbg_aer, base_vha, 0x900e, + "slot_reset return %x.\n", ret); return ret; } @@ -4085,13 +5684,13 @@ qla2xxx_pci_resume(struct pci_dev *pdev) struct qla_hw_data *ha = base_vha->hw; int ret; - DEBUG17(qla_printk(KERN_WARNING, ha, "pci_resume\n")); + ql_dbg(ql_dbg_aer, base_vha, 0x900f, + "pci_resume.\n"); ret = qla2x00_wait_for_hba_online(base_vha); if (ret != QLA_SUCCESS) { - qla_printk(KERN_ERR, ha, - "the device failed to resume I/O " - "from slot/link_reset"); + 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); @@ -4099,7 +5698,7 @@ qla2xxx_pci_resume(struct pci_dev *pdev) ha->flags.eeh_busy = 0; } -static struct pci_error_handlers qla2xxx_err_handler = { +static const 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, @@ -4120,8 +5719,14 @@ static struct pci_device_id qla2xxx_pci_tbl[] = { { 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) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8031) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISPF001) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8044) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2071) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2271) }, { 0 }, }; MODULE_DEVICE_TABLE(pci, qla2xxx_pci_tbl); @@ -4138,7 +5743,7 @@ static struct pci_driver qla2xxx_pci_driver = { .err_handler = &qla2xxx_err_handler, }; -static struct file_operations apidev_fops = { +static const struct file_operations apidev_fops = { .owner = THIS_MODULE, .llseek = noop_llseek, }; @@ -4155,11 +5760,26 @@ qla2x00_module_init(void) srb_cachep = kmem_cache_create("qla2xxx_srbs", sizeof(srb_t), 0, SLAB_HWCACHE_ALIGN, NULL); if (srb_cachep == NULL) { - printk(KERN_ERR - "qla2xxx: Unable to allocate SRB cache...Failing load!\n"); + ql_log(ql_log_fatal, NULL, 0x0001, + "Unable to allocate SRB cache...Failing load!.\n"); return -ENOMEM; } + /* Initialize target kmem_cache and mem_pools */ + ret = qlt_init(); + if (ret < 0) { + kmem_cache_destroy(srb_cachep); + return ret; + } else if (ret > 0) { + /* + * If initiator mode is explictly disabled by qlt_init(), + * prevent scsi_transport_fc.c:fc_scsi_scan_rport() from + * performing scsi_scan_target() during LOOP UP event. + */ + qla2xxx_transport_functions.disable_target_scan = 1; + qla2xxx_transport_vport_functions.disable_target_scan = 1; + } + /* Derive version string. */ strcpy(qla2x00_version_str, QLA2XXX_VERSION); if (ql2xextended_error_logging) @@ -4169,30 +5789,40 @@ qla2x00_module_init(void) 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"); + qlt_exit(); return -ENODEV; } apidev_major = register_chrdev(0, QLA2XXX_APIDEV, &apidev_fops); if (apidev_major < 0) { - printk(KERN_WARNING "qla2xxx: Unable to register char device " - "%s\n", QLA2XXX_APIDEV); + 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); + qlt_exit(); fc_release_transport(qla2xxx_transport_template); + ql_log(ql_log_fatal, NULL, 0x0004, + "fc_attach_transport vport failed...Failing load!.\n"); return -ENODEV; } - - printk(KERN_INFO "QLogic Fibre Channel HBA Driver: %s\n", + 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); + qlt_exit(); 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; } @@ -4207,6 +5837,7 @@ qla2x00_module_exit(void) pci_unregister_driver(&qla2xxx_pci_driver); qla2x00_release_firmware(); kmem_cache_destroy(srb_cachep); + qlt_exit(); if (ctx_cachep) kmem_cache_destroy(ctx_cachep); fc_release_transport(qla2xxx_transport_template); diff --git a/drivers/scsi/qla2xxx/qla_settings.h b/drivers/scsi/qla2xxx/qla_settings.h index d70f0300898..2fb7ebfbbc3 100644 --- a/drivers/scsi/qla2xxx/qla_settings.h +++ b/drivers/scsi/qla2xxx/qla_settings.h @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index 693647661ed..bca173e56f1 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -189,6 +189,7 @@ qla2x00_write_nvram_word(struct qla_hw_data *ha, uint32_t addr, uint16_t data) 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); @@ -220,8 +221,8 @@ qla2x00_write_nvram_word(struct qla_hw_data *ha, uint32_t addr, uint16_t data) wait_cnt = NVR_WAIT_CNT; do { if (!--wait_cnt) { - DEBUG9_10(qla_printk(KERN_WARNING, ha, - "NVRAM didn't go ready...\n")); + ql_dbg(ql_dbg_user, vha, 0x708d, + "NVRAM didn't go ready...\n"); break; } NVRAM_DELAY(); @@ -308,6 +309,7 @@ qla2x00_clear_nvram_protection(struct qla_hw_data *ha) 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; @@ -350,8 +352,8 @@ qla2x00_clear_nvram_protection(struct qla_hw_data *ha) wait_cnt = NVR_WAIT_CNT; do { if (!--wait_cnt) { - DEBUG9_10(qla_printk(KERN_WARNING, ha, - "NVRAM didn't go ready...\n")); + ql_dbg(ql_dbg_user, vha, 0x708e, + "NVRAM didn't go ready...\n"); break; } NVRAM_DELAY(); @@ -371,6 +373,7 @@ 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; @@ -409,8 +412,8 @@ qla2x00_set_nvram_protection(struct qla_hw_data *ha, int stat) wait_cnt = NVR_WAIT_CNT; do { if (!--wait_cnt) { - DEBUG9_10(qla_printk(KERN_WARNING, ha, - "NVRAM didn't go ready...\n")); + ql_dbg(ql_dbg_user, vha, 0x708f, + "NVRAM didn't go ready...\n"); break; } NVRAM_DELAY(); @@ -562,9 +565,12 @@ qla2xxx_find_flt_start(scsi_qla_host_t *vha, uint32_t *start) *start = FA_FLASH_LAYOUT_ADDR; else if (IS_QLA81XX(ha)) *start = FA_FLASH_LAYOUT_ADDR_81; - else if (IS_QLA82XX(ha)) { + else if (IS_P3P_TYPE(ha)) { *start = FA_FLASH_LAYOUT_ADDR_82; goto end; + } else if (IS_QLA83XX(ha) || IS_QLA27XX(ha)) { + *start = FA_FLASH_LAYOUT_ADDR_83; + goto end; } /* Begin with first PCI expansion ROM header. */ buf = (uint8_t *)req->ring; @@ -607,9 +613,10 @@ qla2xxx_find_flt_start(scsi_qla_host_t *vha, uint32_t *start) for (chksum = 0; cnt; cnt--) chksum += le16_to_cpu(*wptr++); if (chksum) { - qla_printk(KERN_ERR, ha, + ql_log(ql_log_fatal, vha, 0x0045, "Inconsistent FLTL detected: checksum=0x%x.\n", chksum); - qla2x00_dump_buffer(buf, sizeof(struct qla_flt_location)); + ql_dump_buffer(ql_dbg_init + ql_dbg_buffer, vha, 0x010e, + buf, sizeof(struct qla_flt_location)); return QLA_FUNCTION_FAILED; } @@ -618,7 +625,9 @@ qla2xxx_find_flt_start(scsi_qla_host_t *vha, uint32_t *start) *start = (le16_to_cpu(fltl->start_hi) << 16 | le16_to_cpu(fltl->start_lo)) >> 2; end: - DEBUG2(qla_printk(KERN_DEBUG, ha, "FLTL[%s] = 0x%x.\n", loc, *start)); + ql_dbg(ql_dbg_init, vha, 0x0046, + "FLTL[%s] = 0x%x.\n", + loc, *start); return QLA_SUCCESS; } @@ -673,7 +682,7 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr) /* 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 ? + ha->flt_region_fcp_prio = (ha->port_no == 0) ? fcp_prio_cfg0[def] : fcp_prio_cfg1[def]; ha->flt_region_flt = flt_addr; @@ -685,10 +694,10 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr) if (*wptr == __constant_cpu_to_le16(0xffff)) goto no_flash_data; if (flt->version != __constant_cpu_to_le16(1)) { - DEBUG2(qla_printk(KERN_INFO, ha, "Unsupported FLT detected: " - "version=0x%x length=0x%x checksum=0x%x.\n", + 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))); + le16_to_cpu(flt->checksum)); goto no_flash_data; } @@ -696,10 +705,10 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr) for (chksum = 0; cnt; cnt--) chksum += le16_to_cpu(*wptr++); if (chksum) { - DEBUG2(qla_printk(KERN_INFO, ha, "Inconsistent FLT detected: " - "version=0x%x length=0x%x checksum=0x%x.\n", + 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), - chksum)); + le16_to_cpu(flt->checksum)); goto no_flash_data; } @@ -708,75 +717,134 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr) for ( ; cnt; cnt--, region++) { /* Store addresses as DWORD offsets. */ start = le32_to_cpu(region->start) >> 2; - - DEBUG3(qla_printk(KERN_DEBUG, ha, "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))); + ql_dbg(ql_dbg_init, vha, 0x0049, + "FLT[%02x]: start=0x%x " + "end=0x%x size=0x%x.\n", le32_to_cpu(region->code) & 0xff, + 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)) + if (IS_P3P_TYPE(ha)) break; - if (ha->flags.port0) + if (ha->port_no == 0) ha->flt_region_vpd = start; break; case FLT_REG_VPD_1: - if (IS_QLA82XX(ha)) + if (IS_P3P_TYPE(ha) || IS_QLA8031(ha)) + break; + if (ha->port_no == 1) + ha->flt_region_vpd = start; + break; + case FLT_REG_VPD_2: + if (!IS_QLA27XX(ha)) break; - if (!ha->flags.port0) + if (ha->port_no == 2) + ha->flt_region_vpd = start; + break; + case FLT_REG_VPD_3: + if (!IS_QLA27XX(ha)) + break; + if (ha->port_no == 3) ha->flt_region_vpd = start; break; case FLT_REG_NVRAM_0: - if (ha->flags.port0) + if (IS_QLA8031(ha)) + break; + if (ha->port_no == 0) ha->flt_region_nvram = start; break; case FLT_REG_NVRAM_1: - if (!ha->flags.port0) + if (IS_QLA8031(ha)) + break; + if (ha->port_no == 1) + ha->flt_region_nvram = start; + break; + case FLT_REG_NVRAM_2: + if (!IS_QLA27XX(ha)) + break; + if (ha->port_no == 2) + ha->flt_region_nvram = start; + break; + case FLT_REG_NVRAM_3: + if (!IS_QLA27XX(ha)) + break; + if (ha->port_no == 3) 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) + if (ha->port_no == 0) ha->flt_region_npiv_conf = start; break; case FLT_REG_NPIV_CONF_1: - if (!ha->flags.port0) + if (ha->port_no == 1) 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) + if (ha->port_no == 0) ha->flt_region_fcp_prio = start; break; case FLT_REG_FCP_PRIO_1: - if (!ha->flags.port0) + if (ha->port_no == 1) ha->flt_region_fcp_prio = start; break; case FLT_REG_BOOT_CODE_82XX: ha->flt_region_boot = start; break; + case FLT_REG_BOOT_CODE_8044: + if (IS_QLA8044(ha)) + ha->flt_region_boot = start; + break; case FLT_REG_FW_82XX: ha->flt_region_fw = start; break; + case FLT_REG_CNA_FW: + if (IS_CNA_CAPABLE(ha)) + 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; + case FLT_REG_VPD_8XXX: + if (IS_CNA_CAPABLE(ha)) + ha->flt_region_vpd = start; + break; + case FLT_REG_FCOE_NVRAM_0: + if (!(IS_QLA8031(ha) || IS_QLA8044(ha))) + break; + if (ha->port_no == 0) + ha->flt_region_nvram = start; + break; + case FLT_REG_FCOE_NVRAM_1: + if (!(IS_QLA8031(ha) || IS_QLA8044(ha))) + break; + if (ha->port_no == 1) + ha->flt_region_nvram = start; break; } } @@ -788,20 +856,21 @@ no_flash_data: 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 ? + ha->flt_region_vpd = (ha->port_no == 0) ? def_vpd0[def] : def_vpd1[def]; - ha->flt_region_nvram = ha->flags.port0 ? + ha->flt_region_nvram = (ha->port_no == 0) ? def_nvram0[def] : def_nvram1[def]; ha->flt_region_fdt = def_fdt[def]; - ha->flt_region_npiv_conf = ha->flags.port0 ? + ha->flt_region_npiv_conf = (ha->port_no == 0) ? def_npiv_conf0[def] : def_npiv_conf1[def]; done: - DEBUG2(qla_printk(KERN_DEBUG, ha, "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_prio_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)); + 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 @@ -833,10 +902,12 @@ qla2xxx_get_fdt_info(scsi_qla_host_t *vha) cnt++) chksum += le16_to_cpu(*wptr++); if (chksum) { - DEBUG2(qla_printk(KERN_INFO, ha, "Inconsistent FDT detected: " - "checksum=0x%x id=%c version=0x%x.\n", chksum, fdt->sig[0], - le16_to_cpu(fdt->version))); - DEBUG9(qla2x00_dump_buffer((uint8_t *)fdt, sizeof(*fdt))); + 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; } @@ -844,7 +915,13 @@ qla2xxx_get_fdt_info(scsi_qla_host_t *vha) 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_wrt_enable = fdt->wrt_enable_bits; + ha->fdt_wrt_sts_reg_cmd = fdt->wrt_sts_reg_cmd; + if (IS_QLA8044(ha)) + ha->fdt_erase_cmd = fdt->erase_cmd; + else + 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 | @@ -856,7 +933,7 @@ qla2xxx_get_fdt_info(scsi_qla_host_t *vha) goto done; no_flash_data: loc = locations[0]; - if (IS_QLA82XX(ha)) { + if (IS_P3P_TYPE(ha)) { ha->fdt_block_size = FLASH_BLK_SIZE_64K; goto done; } @@ -890,11 +967,13 @@ no_flash_data: break; } done: - DEBUG2(qla_printk(KERN_DEBUG, ha, "FDT[%s]: (0x%x/0x%x) erase=0x%x " - "pro=%x upro=%x wrtd=0x%x blk=0x%x.\n", loc, mid, fid, + 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_unprotect_sec_cmd, ha->fdt_wrt_disable, - ha->fdt_block_size)); + ha->fdt_wrt_disable, ha->fdt_block_size); + } static void @@ -905,7 +984,7 @@ qla2xxx_get_idc_param(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; struct req_que *req = ha->req_q_map[0]; - if (!IS_QLA82XX(ha)) + if (!(IS_P3P_TYPE(ha))) return; wptr = (uint32_t *)req->ring; @@ -913,12 +992,16 @@ qla2xxx_get_idc_param(scsi_qla_host_t *vha) 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; + ha->fcoe_dev_init_timeout = QLA82XX_ROM_DEV_INIT_TIMEOUT; + ha->fcoe_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); + ha->fcoe_dev_init_timeout = le32_to_cpu(*wptr++); + ha->fcoe_reset_timeout = le32_to_cpu(*wptr); } + ql_dbg(ql_dbg_init, vha, 0x004e, + "fcoe_dev_init_timeout=%d " + "fcoe_reset_timeout=%d.\n", ha->fcoe_dev_init_timeout, + ha->fcoe_reset_timeout); return; } @@ -929,7 +1012,8 @@ qla2xxx_get_flash_info(scsi_qla_host_t *vha) uint32_t flt_addr; struct qla_hw_data *ha = vha->hw; - if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && !IS_QLA8XXX_TYPE(ha)) + if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && + !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha) && !IS_QLA27XX(ha)) return QLA_SUCCESS; ret = qla2xxx_find_flt_start(vha, &flt_addr); @@ -955,7 +1039,14 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha) struct qla_npiv_entry *entry; struct qla_hw_data *ha = vha->hw; - if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && !IS_QLA8XXX_TYPE(ha)) + if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && + !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha)) + return; + + if (ha->flags.nic_core_reset_hdlr_active) + return; + + if (IS_QLA8044(ha)) return; ha->isp_ops->read_optrom(vha, (uint8_t *)&hdr, @@ -963,17 +1054,18 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha) if (hdr.version == __constant_cpu_to_le16(0xffff)) return; if (hdr.version != __constant_cpu_to_le16(1)) { - DEBUG2(qla_printk(KERN_INFO, ha, "Unsupported NPIV-Config " + 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))); + le16_to_cpu(hdr.checksum)); return; } data = kmalloc(NPIV_CONFIG_SIZE, GFP_KERNEL); if (!data) { - DEBUG2(qla_printk(KERN_INFO, ha, "NPIV-Config: Unable to " - "allocate memory.\n")); + ql_log(ql_log_warn, vha, 0x7091, + "Unable to allocate memory for data.\n"); return; } @@ -985,10 +1077,11 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha) for (wptr = data, chksum = 0; cnt; cnt--) chksum += le16_to_cpu(*wptr++); if (chksum) { - DEBUG2(qla_printk(KERN_INFO, ha, "Inconsistent NPIV-Config " + 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), - chksum)); + le16_to_cpu(hdr.checksum)); goto done; } @@ -1014,21 +1107,22 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha) vid.port_name = wwn_to_u64(entry->port_name); vid.node_name = wwn_to_u64(entry->node_name); - DEBUG2(qla_printk(KERN_INFO, ha, "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)); + 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) - qla_printk(KERN_INFO, ha, - "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); + 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: @@ -1122,14 +1216,16 @@ qla24xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, struct qla_hw_data *ha = vha->hw; /* Prepare burst-capable write on supported ISPs. */ - if ((IS_QLA25XX(ha) || IS_QLA81XX(ha)) && !(faddr & 0xfff) && - dwords > OPTROM_BURST_DWORDS) { + if ((IS_QLA25XX(ha) || IS_QLA81XX(ha) || IS_QLA83XX(ha) || + IS_QLA27XX(ha)) && + !(faddr & 0xfff) && dwords > OPTROM_BURST_DWORDS) { optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, &optrom_dma, GFP_KERNEL); if (!optrom) { - qla_printk(KERN_DEBUG, ha, - "Unable to allocate memory for optrom burst write " - "(%x KB).\n", OPTROM_BURST_SIZE / 1024); + ql_log(ql_log_warn, vha, 0x7095, + "Unable to allocate " + "memory for optrom burst write (%x KB).\n", + OPTROM_BURST_SIZE / 1024); } } @@ -1138,7 +1234,7 @@ qla24xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, ret = qla24xx_unprotect_flash(vha); if (ret != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x7096, "Unable to unprotect flash for update.\n"); goto done; } @@ -1156,9 +1252,9 @@ qla24xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, 0xff0000) | ((fdata >> 16) & 0xff)); ret = qla24xx_erase_sector(vha, fdata); if (ret != QLA_SUCCESS) { - DEBUG9(qla_printk(KERN_WARNING, ha, - "Unable to erase sector: address=%x.\n", - faddr)); + ql_dbg(ql_dbg_user, vha, 0x7007, + "Unable to erase erase sector: address=%x.\n", + faddr); break; } } @@ -1172,12 +1268,12 @@ qla24xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, flash_data_addr(ha, faddr), OPTROM_BURST_DWORDS); if (ret != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, + 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); - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x7098, "Reverting to slow-write.\n"); dma_free_coherent(&ha->pdev->dev, @@ -1194,9 +1290,9 @@ qla24xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, ret = qla24xx_write_flash_dword(ha, flash_data_addr(ha, faddr), cpu_to_le32(*dwptr)); if (ret != QLA_SUCCESS) { - DEBUG9(printk("%s(%ld) Unable to program flash " - "address=%x data=%x.\n", __func__, - vha->host_no, faddr, *dwptr)); + ql_dbg(ql_dbg_user, vha, 0x7006, + "Unable to program flash address=%x data=%x.\n", + faddr, *dwptr); break; } @@ -1211,7 +1307,7 @@ qla24xx_write_flash_data(scsi_qla_host_t *vha, uint32_t *dwptr, uint32_t faddr, ret = qla24xx_protect_flash(vha); if (ret != QLA_SUCCESS) - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x7099, "Unable to protect flash after update.\n"); done: if (optrom) @@ -1248,7 +1344,7 @@ qla24xx_read_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr, uint32_t *dwptr; struct qla_hw_data *ha = vha->hw; - if (IS_QLA82XX(ha)) + if (IS_P3P_TYPE(ha)) return buf; /* Dword reads to flash. */ @@ -1306,7 +1402,7 @@ qla24xx_write_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr, ret = QLA_SUCCESS; - if (IS_QLA82XX(ha)) + if (IS_P3P_TYPE(ha)) return ret; /* Enable flash write. */ @@ -1324,9 +1420,9 @@ qla24xx_write_nvram_data(scsi_qla_host_t *vha, uint8_t *buf, uint32_t naddr, ret = qla24xx_write_flash_dword(ha, nvram_data_addr(ha, naddr), cpu_to_le32(*dwptr)); if (ret != QLA_SUCCESS) { - DEBUG9(qla_printk(KERN_WARNING, ha, + ql_dbg(ql_dbg_user, vha, 0x709a, "Unable to program nvram address=%x data=%x.\n", - naddr, *dwptr)); + naddr, *dwptr); break; } } @@ -1420,7 +1516,7 @@ qla2x00_beacon_blink(struct scsi_qla_host *vha) struct qla_hw_data *ha = vha->hw; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; - if (IS_QLA82XX(ha)) + if (IS_P3P_TYPE(ha)) return; spin_lock_irqsave(&ha->hardware_lock, flags); @@ -1476,7 +1572,7 @@ qla2x00_beacon_on(struct scsi_qla_host *vha) ha->fw_options[1] |= FO1_DISABLE_GPIO6_7; if (qla2x00_set_fw_options(vha, ha->fw_options) != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x709b, "Unable to update fw options (beacon on).\n"); return QLA_FUNCTION_FAILED; } @@ -1541,7 +1637,7 @@ qla2x00_beacon_off(struct scsi_qla_host *vha) rval = qla2x00_set_fw_options(vha, ha->fw_options); if (rval != QLA_SUCCESS) - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x709c, "Unable to update fw options (beacon off).\n"); return rval; } @@ -1596,6 +1692,97 @@ qla24xx_beacon_blink(struct scsi_qla_host *vha) spin_unlock_irqrestore(&ha->hardware_lock, flags); } +static uint32_t +qla83xx_select_led_port(struct qla_hw_data *ha) +{ + uint32_t led_select_value = 0; + + if (!IS_QLA83XX(ha)) + goto out; + + if (ha->port_no == 0) + led_select_value = QLA83XX_LED_PORT0; + else + led_select_value = QLA83XX_LED_PORT1; + +out: + return led_select_value; +} + +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]; + uint32_t led_10_value, led_43_value; + + if (!IS_QLA83XX(ha) && !IS_QLA81XX(ha)) + return; + + if (!ha->beacon_blink_led) + return; + + if (IS_QLA2031(ha)) { + led_select_value = qla83xx_select_led_port(ha); + + qla83xx_wr_reg(vha, led_select_value, 0x40000230); + qla83xx_wr_reg(vha, led_select_value + 4, 0x40000230); + } else if (IS_QLA8031(ha)) { + led_select_value = qla83xx_select_led_port(ha); + + qla83xx_rd_reg(vha, led_select_value, &led_10_value); + qla83xx_rd_reg(vha, led_select_value + 0x10, &led_43_value); + qla83xx_wr_reg(vha, led_select_value, 0x01f44000); + msleep(500); + qla83xx_wr_reg(vha, led_select_value, 0x400001f4); + msleep(1000); + qla83xx_wr_reg(vha, led_select_value, led_10_value); + qla83xx_wr_reg(vha, led_select_value + 0x10, led_43_value); + } else if (IS_QLA81XX(ha)) { + 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) { @@ -1604,9 +1791,12 @@ qla24xx_beacon_on(struct scsi_qla_host *vha) struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; - if (IS_QLA82XX(ha)) + if (IS_P3P_TYPE(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; @@ -1616,11 +1806,14 @@ qla24xx_beacon_on(struct scsi_qla_host *vha) if (qla2x00_get_fw_options(vha, ha->fw_options) != QLA_SUCCESS) { - qla_printk(KERN_WARNING, ha, + 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(®->gpiod); @@ -1635,6 +1828,7 @@ qla24xx_beacon_on(struct scsi_qla_host *vha) /* 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; @@ -1649,10 +1843,17 @@ qla24xx_beacon_off(struct scsi_qla_host *vha) struct qla_hw_data *ha = vha->hw; struct device_reg_24xx __iomem *reg = &ha->iobase->isp24; - if (IS_QLA82XX(ha)) + if (IS_P3P_TYPE(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. */ @@ -1667,17 +1868,18 @@ qla24xx_beacon_off(struct scsi_qla_host *vha) RD_REG_DWORD(®->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) { - qla_printk(KERN_WARNING, ha, - "Unable to update fw options (beacon off).\n"); + 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) { - qla_printk(KERN_WARNING, ha, - "Unable to get fw options (beacon off).\n"); + ql_log(ql_log_warn, vha, 0x704e, + "Unable to update fw options (beacon on).\n"); return QLA_FUNCTION_FAILED; } @@ -2152,7 +2354,7 @@ qla2x00_write_optrom_data(struct scsi_qla_host *vha, uint8_t *buf, */ rest_addr = 0xffff; sec_mask = 0x10000; - break; + break; } /* * ST m29w010b part - 16kb sector size @@ -2378,7 +2580,7 @@ qla25xx_read_optrom_data(struct scsi_qla_host *vha, uint8_t *buf, uint32_t faddr, left, burst; struct qla_hw_data *ha = vha->hw; - if (IS_QLA25XX(ha) || IS_QLA81XX(ha)) + if (IS_QLA25XX(ha) || IS_QLA81XX(ha) || IS_QLA27XX(ha)) goto try_fast; if (offset & 0xfff) goto slow_read; @@ -2389,10 +2591,9 @@ try_fast: optrom = dma_alloc_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, &optrom_dma, GFP_KERNEL); if (!optrom) { - qla_printk(KERN_DEBUG, ha, - "Unable to allocate memory for optrom burst read " - "(%x KB).\n", OPTROM_BURST_SIZE / 1024); - + 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; } @@ -2407,12 +2608,11 @@ try_fast: rval = qla2x00_dump_ram(vha, optrom_dma, flash_data_addr(ha, faddr), burst); if (rval) { - qla_printk(KERN_WARNING, ha, - "Unable to burst-read optrom segment " - "(%x/%x/%llx).\n", rval, - flash_data_addr(ha, faddr), + 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); - qla_printk(KERN_WARNING, ha, + ql_log(ql_log_warn, vha, 0x00f6, "Reverting to slow-read.\n"); dma_free_coherent(&ha->pdev->dev, OPTROM_BURST_SIZE, @@ -2556,8 +2756,8 @@ qla2x00_get_flash_version(scsi_qla_host_t *vha, void *mbuf) if (qla2x00_read_flash_byte(ha, pcihdr) != 0x55 || qla2x00_read_flash_byte(ha, pcihdr + 0x01) != 0xaa) { /* No signature */ - DEBUG2(qla_printk(KERN_DEBUG, ha, "No matching ROM " - "signature.\n")); + ql_log(ql_log_fatal, vha, 0x0050, + "No matching ROM signature.\n"); ret = QLA_FUNCTION_FAILED; break; } @@ -2573,8 +2773,8 @@ qla2x00_get_flash_version(scsi_qla_host_t *vha, void *mbuf) qla2x00_read_flash_byte(ha, pcids + 0x2) != 'I' || qla2x00_read_flash_byte(ha, pcids + 0x3) != 'R') { /* Incorrect header. */ - DEBUG2(qla_printk(KERN_INFO, ha, "PCI data struct not " - "found pcir_adr=%x.\n", pcids)); + ql_log(ql_log_fatal, vha, 0x0051, + "PCI data struct not found pcir_adr=%x.\n", pcids); ret = QLA_FUNCTION_FAILED; break; } @@ -2588,8 +2788,9 @@ qla2x00_get_flash_version(scsi_qla_host_t *vha, void *mbuf) qla2x00_read_flash_byte(ha, pcids + 0x12); ha->bios_revision[1] = qla2x00_read_flash_byte(ha, pcids + 0x13); - DEBUG3(qla_printk(KERN_DEBUG, ha, "read BIOS %d.%d.\n", - ha->bios_revision[1], ha->bios_revision[0])); + 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). */ @@ -2602,12 +2803,14 @@ qla2x00_get_flash_version(scsi_qla_host_t *vha, void *mbuf) qla2x00_read_flash_byte(ha, pcids + 0x12); ha->efi_revision[1] = qla2x00_read_flash_byte(ha, pcids + 0x13); - DEBUG3(qla_printk(KERN_DEBUG, ha, "read EFI %d.%d.\n", - ha->efi_revision[1], ha->efi_revision[0])); + ql_dbg(ql_dbg_init, vha, 0x0053, + "Read EFI %d.%d.\n", + ha->efi_revision[1], ha->efi_revision[0]); break; default: - DEBUG2(qla_printk(KERN_INFO, ha, "Unrecognized code " - "type %x at pcids %x.\n", code_type, pcids)); + ql_log(ql_log_warn, vha, 0x0054, + "Unrecognized code type %x at pcids %x.\n", + code_type, pcids); break; } @@ -2627,21 +2830,28 @@ qla2x00_get_flash_version(scsi_qla_host_t *vha, void *mbuf) qla2x00_read_flash_data(ha, dbyte, ha->flt_region_fw * 4 + 10, 8); - DEBUG3(qla_printk(KERN_DEBUG, ha, "dumping fw ver from " - "flash:\n")); - DEBUG3(qla2x00_dump_buffer((uint8_t *)dbyte, 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)) { - DEBUG2(qla_printk(KERN_INFO, ha, "Unrecognized fw " - "revision at %x.\n", ha->flt_region_fw * 4)); + 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]); } } @@ -2651,6 +2861,121 @@ qla2x00_get_flash_version(scsi_qla_host_t *vha, void *mbuf) } int +qla82xx_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; + struct qla_hw_data *ha = vha->hw; + + 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. */ + ha->isp_ops->read_optrom(vha, (uint8_t *)dcode, pcihdr, + 0x20 * 4); + bcode = mbuf + (pcihdr % 4); + if (bcode[0x0] != 0x55 || bcode[0x1] != 0xaa) { + /* No signature */ + ql_log(ql_log_fatal, vha, 0x0154, + "No matching ROM signature.\n"); + ret = QLA_FUNCTION_FAILED; + break; + } + + /* Locate PCI data structure. */ + pcids = pcihdr + ((bcode[0x19] << 8) | bcode[0x18]); + + ha->isp_ops->read_optrom(vha, (uint8_t *)dcode, pcids, + 0x20 * 4); + 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, 0x0155, + "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, 0x0156, + "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, 0x0157, + "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, 0x0158, + "Read EFI %d.%d.\n", + ha->efi_revision[1], ha->efi_revision[0]); + break; + default: + ql_log(ql_log_warn, vha, 0x0159, + "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; + ha->isp_ops->read_optrom(vha, (uint8_t *)dcode, ha->flt_region_fw << 2, + 0x20); + bcode = mbuf + (pcihdr % 4); + + /* Validate signature of PCI data structure. */ + if (bcode[0x0] == 0x3 && bcode[0x1] == 0x0 && + bcode[0x2] == 0x40 && bcode[0x3] == 0x40) { + ha->fw_revision[0] = bcode[0x4]; + ha->fw_revision[1] = bcode[0x5]; + ha->fw_revision[2] = bcode[0x6]; + ql_dbg(ql_dbg_init, vha, 0x0153, + "Firmware revision %d.%d.%d\n", + ha->fw_revision[0], ha->fw_revision[1], + ha->fw_revision[2]); + } + + return ret; +} + +int qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf) { int ret = QLA_SUCCESS; @@ -2661,7 +2986,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf) int i; struct qla_hw_data *ha = vha->hw; - if (IS_QLA82XX(ha)) + if (IS_P3P_TYPE(ha)) return ret; if (!mbuf) @@ -2683,8 +3008,8 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf) bcode = mbuf + (pcihdr % 4); if (bcode[0x0] != 0x55 || bcode[0x1] != 0xaa) { /* No signature */ - DEBUG2(qla_printk(KERN_DEBUG, ha, "No matching ROM " - "signature.\n")); + ql_log(ql_log_fatal, vha, 0x0059, + "No matching ROM signature.\n"); ret = QLA_FUNCTION_FAILED; break; } @@ -2699,8 +3024,8 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf) if (bcode[0x0] != 'P' || bcode[0x1] != 'C' || bcode[0x2] != 'I' || bcode[0x3] != 'R') { /* Incorrect header. */ - DEBUG2(qla_printk(KERN_INFO, ha, "PCI data struct not " - "found pcir_adr=%x.\n", pcids)); + ql_log(ql_log_fatal, vha, 0x005a, + "PCI data struct not found pcir_adr=%x.\n", pcids); ret = QLA_FUNCTION_FAILED; break; } @@ -2712,26 +3037,30 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf) /* Intel x86, PC-AT compatible. */ ha->bios_revision[0] = bcode[0x12]; ha->bios_revision[1] = bcode[0x13]; - DEBUG3(qla_printk(KERN_DEBUG, ha, "read BIOS %d.%d.\n", - ha->bios_revision[1], ha->bios_revision[0])); + 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]; - DEBUG3(qla_printk(KERN_DEBUG, ha, "read FCODE %d.%d.\n", - ha->fcode_revision[1], ha->fcode_revision[0])); + 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]; - DEBUG3(qla_printk(KERN_DEBUG, ha, "read EFI %d.%d.\n", - ha->efi_revision[1], ha->efi_revision[0])); + ql_dbg(ql_dbg_init, vha, 0x005d, + "Read EFI %d.%d.\n", + ha->efi_revision[1], ha->efi_revision[0]); break; default: - DEBUG2(qla_printk(KERN_INFO, ha, "Unrecognized code " - "type %x at pcids %x.\n", code_type, pcids)); + ql_log(ql_log_warn, vha, 0x005e, + "Unrecognized code type %x at pcids %x.\n", + code_type, pcids); break; } @@ -2753,13 +3082,18 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf) dcode[2] == 0xffffffff && dcode[3] == 0xffffffff) || (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 && dcode[3] == 0)) { - DEBUG2(qla_printk(KERN_INFO, ha, "Unrecognized fw " - "revision at %x.\n", ha->flt_region_fw * 4)); + 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 */ @@ -2775,9 +3109,9 @@ qla24xx_get_flash_version(scsi_qla_host_t *vha, void *mbuf) if (dcode[4] == 0xFFFFFFFF && dcode[5] == 0xFFFFFFFF && dcode[6] == 0xFFFFFFFF && dcode[7] == 0xFFFFFFFF) { - DEBUG2(qla_printk(KERN_INFO, ha, - "%s(%ld): Unrecognized golden fw at 0x%x.\n", - __func__, vha->host_no, ha->flt_region_gold_fw * 4)); + ql_log(ql_log_warn, vha, 0x0056, + "Unrecognized golden fw at 0x%x.\n", + ha->flt_region_gold_fw * 4); return ret; } @@ -2843,9 +3177,9 @@ qla24xx_read_fcp_prio_cfg(scsi_qla_host_t *vha) if (!ha->fcp_prio_cfg) { ha->fcp_prio_cfg = vmalloc(FCP_PRIO_CFG_SIZE); if (!ha->fcp_prio_cfg) { - qla_printk(KERN_WARNING, ha, - "Unable to allocate memory for fcp priority data " - "(%x).\n", FCP_PRIO_CFG_SIZE); + 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; } } @@ -2857,7 +3191,7 @@ qla24xx_read_fcp_prio_cfg(scsi_qla_host_t *vha) 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(ha->fcp_prio_cfg, 0)) + if (!qla24xx_fcp_prio_cfg_valid(vha, ha->fcp_prio_cfg, 0)) goto fail; /* read remaining FCP CMD config data from flash */ @@ -2869,7 +3203,7 @@ qla24xx_read_fcp_prio_cfg(scsi_qla_host_t *vha) 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(ha->fcp_prio_cfg, 1)) + if (!qla24xx_fcp_prio_cfg_valid(vha, ha->fcp_prio_cfg, 1)) goto fail; ha->flags.fcp_prio_enabled = 1; diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c new file mode 100644 index 00000000000..e632e14180c --- /dev/null +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -0,0 +1,5639 @@ +/* + * qla_target.c SCSI LLD infrastructure for QLogic 22xx/23xx/24xx/25xx + * + * based on qla2x00t.c code: + * + * Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@vlnb.net> + * Copyright (C) 2004 - 2005 Leonid Stoljar + * Copyright (C) 2006 Nathaniel Clark <nate@misrule.us> + * Copyright (C) 2006 - 2010 ID7 Ltd. + * + * Forward port and refactoring to modern qla2xxx and target/configfs + * + * Copyright (C) 2010-2013 Nicholas A. Bellinger <nab@kernel.org> + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation, version 2 + * of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/blkdev.h> +#include <linux/interrupt.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/list.h> +#include <linux/workqueue.h> +#include <asm/unaligned.h> +#include <scsi/scsi.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_tcq.h> +#include <target/target_core_base.h> +#include <target/target_core_fabric.h> + +#include "qla_def.h" +#include "qla_target.h" + +static char *qlini_mode = QLA2XXX_INI_MODE_STR_ENABLED; +module_param(qlini_mode, charp, S_IRUGO); +MODULE_PARM_DESC(qlini_mode, + "Determines when initiator mode will be enabled. Possible values: " + "\"exclusive\" - initiator mode will be enabled on load, " + "disabled on enabling target mode and then on disabling target mode " + "enabled back; " + "\"disabled\" - initiator mode will never be enabled; " + "\"enabled\" (default) - initiator mode will always stay enabled."); + +int ql2x_ini_mode = QLA2XXX_INI_MODE_EXCLUSIVE; + +/* + * From scsi/fc/fc_fcp.h + */ +enum fcp_resp_rsp_codes { + FCP_TMF_CMPL = 0, + FCP_DATA_LEN_INVALID = 1, + FCP_CMND_FIELDS_INVALID = 2, + FCP_DATA_PARAM_MISMATCH = 3, + FCP_TMF_REJECTED = 4, + FCP_TMF_FAILED = 5, + FCP_TMF_INVALID_LUN = 9, +}; + +/* + * fc_pri_ta from scsi/fc/fc_fcp.h + */ +#define FCP_PTA_SIMPLE 0 /* simple task attribute */ +#define FCP_PTA_HEADQ 1 /* head of queue task attribute */ +#define FCP_PTA_ORDERED 2 /* ordered task attribute */ +#define FCP_PTA_ACA 4 /* auto. contingent allegiance */ +#define FCP_PTA_MASK 7 /* mask for task attribute field */ +#define FCP_PRI_SHIFT 3 /* priority field starts in bit 3 */ +#define FCP_PRI_RESVD_MASK 0x80 /* reserved bits in priority field */ + +/* + * This driver calls qla2x00_alloc_iocbs() and qla2x00_issue_marker(), which + * must be called under HW lock and could unlock/lock it inside. + * It isn't an issue, since in the current implementation on the time when + * those functions are called: + * + * - Either context is IRQ and only IRQ handler can modify HW data, + * including rings related fields, + * + * - Or access to target mode variables from struct qla_tgt doesn't + * cross those functions boundaries, except tgt_stop, which + * additionally protected by irq_cmd_count. + */ +/* Predefs for callbacks handed to qla2xxx LLD */ +static void qlt_24xx_atio_pkt(struct scsi_qla_host *ha, + struct atio_from_isp *pkt); +static void qlt_response_pkt(struct scsi_qla_host *ha, response_t *pkt); +static int qlt_issue_task_mgmt(struct qla_tgt_sess *sess, uint32_t lun, + int fn, void *iocb, int flags); +static void qlt_send_term_exchange(struct scsi_qla_host *ha, struct qla_tgt_cmd + *cmd, struct atio_from_isp *atio, int ha_locked); +static void qlt_reject_free_srr_imm(struct scsi_qla_host *ha, + struct qla_tgt_srr_imm *imm, int ha_lock); +/* + * Global Variables + */ +static struct kmem_cache *qla_tgt_mgmt_cmd_cachep; +static mempool_t *qla_tgt_mgmt_cmd_mempool; +static struct workqueue_struct *qla_tgt_wq; +static DEFINE_MUTEX(qla_tgt_mutex); +static LIST_HEAD(qla_tgt_glist); + +/* ha->hardware_lock supposed to be held on entry (to protect tgt->sess_list) */ +static struct qla_tgt_sess *qlt_find_sess_by_port_name( + struct qla_tgt *tgt, + const uint8_t *port_name) +{ + struct qla_tgt_sess *sess; + + list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) { + if (!memcmp(sess->port_name, port_name, WWN_SIZE)) + return sess; + } + + return NULL; +} + +/* Might release hw lock, then reaquire!! */ +static inline int qlt_issue_marker(struct scsi_qla_host *vha, int vha_locked) +{ + /* Send marker if required */ + if (unlikely(vha->marker_needed != 0)) { + int rc = qla2x00_issue_marker(vha, vha_locked); + if (rc != QLA_SUCCESS) { + ql_dbg(ql_dbg_tgt, vha, 0xe03d, + "qla_target(%d): issue_marker() failed\n", + vha->vp_idx); + } + return rc; + } + return QLA_SUCCESS; +} + +static inline +struct scsi_qla_host *qlt_find_host_by_d_id(struct scsi_qla_host *vha, + uint8_t *d_id) +{ + struct qla_hw_data *ha = vha->hw; + uint8_t vp_idx; + + if ((vha->d_id.b.area != d_id[1]) || (vha->d_id.b.domain != d_id[0])) + return NULL; + + if (vha->d_id.b.al_pa == d_id[2]) + return vha; + + BUG_ON(ha->tgt.tgt_vp_map == NULL); + vp_idx = ha->tgt.tgt_vp_map[d_id[2]].idx; + if (likely(test_bit(vp_idx, ha->vp_idx_map))) + return ha->tgt.tgt_vp_map[vp_idx].vha; + + return NULL; +} + +static inline +struct scsi_qla_host *qlt_find_host_by_vp_idx(struct scsi_qla_host *vha, + uint16_t vp_idx) +{ + struct qla_hw_data *ha = vha->hw; + + if (vha->vp_idx == vp_idx) + return vha; + + BUG_ON(ha->tgt.tgt_vp_map == NULL); + if (likely(test_bit(vp_idx, ha->vp_idx_map))) + return ha->tgt.tgt_vp_map[vp_idx].vha; + + return NULL; +} + +void qlt_24xx_atio_pkt_all_vps(struct scsi_qla_host *vha, + struct atio_from_isp *atio) +{ + ql_dbg(ql_dbg_tgt, vha, 0xe072, + "%s: qla_target(%d): type %x ox_id %04x\n", + __func__, vha->vp_idx, atio->u.raw.entry_type, + be16_to_cpu(atio->u.isp24.fcp_hdr.ox_id)); + + switch (atio->u.raw.entry_type) { + case ATIO_TYPE7: + { + struct scsi_qla_host *host = qlt_find_host_by_d_id(vha, + atio->u.isp24.fcp_hdr.d_id); + if (unlikely(NULL == host)) { + ql_dbg(ql_dbg_tgt, vha, 0xe03e, + "qla_target(%d): Received ATIO_TYPE7 " + "with unknown d_id %x:%x:%x\n", vha->vp_idx, + atio->u.isp24.fcp_hdr.d_id[0], + atio->u.isp24.fcp_hdr.d_id[1], + atio->u.isp24.fcp_hdr.d_id[2]); + break; + } + qlt_24xx_atio_pkt(host, atio); + break; + } + + case IMMED_NOTIFY_TYPE: + { + struct scsi_qla_host *host = vha; + struct imm_ntfy_from_isp *entry = + (struct imm_ntfy_from_isp *)atio; + + if ((entry->u.isp24.vp_index != 0xFF) && + (entry->u.isp24.nport_handle != 0xFFFF)) { + host = qlt_find_host_by_vp_idx(vha, + entry->u.isp24.vp_index); + if (unlikely(!host)) { + ql_dbg(ql_dbg_tgt, vha, 0xe03f, + "qla_target(%d): Received " + "ATIO (IMMED_NOTIFY_TYPE) " + "with unknown vp_index %d\n", + vha->vp_idx, entry->u.isp24.vp_index); + break; + } + } + qlt_24xx_atio_pkt(host, atio); + break; + } + + default: + ql_dbg(ql_dbg_tgt, vha, 0xe040, + "qla_target(%d): Received unknown ATIO atio " + "type %x\n", vha->vp_idx, atio->u.raw.entry_type); + break; + } + + return; +} + +void qlt_response_pkt_all_vps(struct scsi_qla_host *vha, response_t *pkt) +{ + switch (pkt->entry_type) { + case CTIO_CRC2: + ql_dbg(ql_dbg_tgt, vha, 0xe073, + "qla_target(%d):%s: CRC2 Response pkt\n", + vha->vp_idx, __func__); + case CTIO_TYPE7: + { + struct ctio7_from_24xx *entry = (struct ctio7_from_24xx *)pkt; + struct scsi_qla_host *host = qlt_find_host_by_vp_idx(vha, + entry->vp_index); + if (unlikely(!host)) { + ql_dbg(ql_dbg_tgt, vha, 0xe041, + "qla_target(%d): Response pkt (CTIO_TYPE7) " + "received, with unknown vp_index %d\n", + vha->vp_idx, entry->vp_index); + break; + } + qlt_response_pkt(host, pkt); + break; + } + + case IMMED_NOTIFY_TYPE: + { + struct scsi_qla_host *host = vha; + struct imm_ntfy_from_isp *entry = + (struct imm_ntfy_from_isp *)pkt; + + host = qlt_find_host_by_vp_idx(vha, entry->u.isp24.vp_index); + if (unlikely(!host)) { + ql_dbg(ql_dbg_tgt, vha, 0xe042, + "qla_target(%d): Response pkt (IMMED_NOTIFY_TYPE) " + "received, with unknown vp_index %d\n", + vha->vp_idx, entry->u.isp24.vp_index); + break; + } + qlt_response_pkt(host, pkt); + break; + } + + case NOTIFY_ACK_TYPE: + { + struct scsi_qla_host *host = vha; + struct nack_to_isp *entry = (struct nack_to_isp *)pkt; + + if (0xFF != entry->u.isp24.vp_index) { + host = qlt_find_host_by_vp_idx(vha, + entry->u.isp24.vp_index); + if (unlikely(!host)) { + ql_dbg(ql_dbg_tgt, vha, 0xe043, + "qla_target(%d): Response " + "pkt (NOTIFY_ACK_TYPE) " + "received, with unknown " + "vp_index %d\n", vha->vp_idx, + entry->u.isp24.vp_index); + break; + } + } + qlt_response_pkt(host, pkt); + break; + } + + case ABTS_RECV_24XX: + { + struct abts_recv_from_24xx *entry = + (struct abts_recv_from_24xx *)pkt; + struct scsi_qla_host *host = qlt_find_host_by_vp_idx(vha, + entry->vp_index); + if (unlikely(!host)) { + ql_dbg(ql_dbg_tgt, vha, 0xe044, + "qla_target(%d): Response pkt " + "(ABTS_RECV_24XX) received, with unknown " + "vp_index %d\n", vha->vp_idx, entry->vp_index); + break; + } + qlt_response_pkt(host, pkt); + break; + } + + case ABTS_RESP_24XX: + { + struct abts_resp_to_24xx *entry = + (struct abts_resp_to_24xx *)pkt; + struct scsi_qla_host *host = qlt_find_host_by_vp_idx(vha, + entry->vp_index); + if (unlikely(!host)) { + ql_dbg(ql_dbg_tgt, vha, 0xe045, + "qla_target(%d): Response pkt " + "(ABTS_RECV_24XX) received, with unknown " + "vp_index %d\n", vha->vp_idx, entry->vp_index); + break; + } + qlt_response_pkt(host, pkt); + break; + } + + default: + qlt_response_pkt(vha, pkt); + break; + } + +} + +static void qlt_free_session_done(struct work_struct *work) +{ + struct qla_tgt_sess *sess = container_of(work, struct qla_tgt_sess, + free_work); + struct qla_tgt *tgt = sess->tgt; + struct scsi_qla_host *vha = sess->vha; + struct qla_hw_data *ha = vha->hw; + + BUG_ON(!tgt); + /* + * Release the target session for FC Nexus from fabric module code. + */ + if (sess->se_sess != NULL) + ha->tgt.tgt_ops->free_session(sess); + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf001, + "Unregistration of sess %p finished\n", sess); + + kfree(sess); + /* + * We need to protect against race, when tgt is freed before or + * inside wake_up() + */ + tgt->sess_count--; + if (tgt->sess_count == 0) + wake_up_all(&tgt->waitQ); +} + +/* ha->hardware_lock supposed to be held on entry */ +void qlt_unreg_sess(struct qla_tgt_sess *sess) +{ + struct scsi_qla_host *vha = sess->vha; + + vha->hw->tgt.tgt_ops->clear_nacl_from_fcport_map(sess); + + list_del(&sess->sess_list_entry); + if (sess->deleted) + list_del(&sess->del_list_entry); + + INIT_WORK(&sess->free_work, qlt_free_session_done); + schedule_work(&sess->free_work); +} +EXPORT_SYMBOL(qlt_unreg_sess); + +/* ha->hardware_lock supposed to be held on entry */ +static int qlt_reset(struct scsi_qla_host *vha, void *iocb, int mcmd) +{ + struct qla_hw_data *ha = vha->hw; + struct qla_tgt_sess *sess = NULL; + uint32_t unpacked_lun, lun = 0; + uint16_t loop_id; + int res = 0; + struct imm_ntfy_from_isp *n = (struct imm_ntfy_from_isp *)iocb; + struct atio_from_isp *a = (struct atio_from_isp *)iocb; + + loop_id = le16_to_cpu(n->u.isp24.nport_handle); + if (loop_id == 0xFFFF) { +#if 0 /* FIXME: Re-enable Global event handling.. */ + /* Global event */ + atomic_inc(&ha->tgt.qla_tgt->tgt_global_resets_count); + qlt_clear_tgt_db(ha->tgt.qla_tgt, 1); + if (!list_empty(&ha->tgt.qla_tgt->sess_list)) { + sess = list_entry(ha->tgt.qla_tgt->sess_list.next, + typeof(*sess), sess_list_entry); + switch (mcmd) { + case QLA_TGT_NEXUS_LOSS_SESS: + mcmd = QLA_TGT_NEXUS_LOSS; + break; + case QLA_TGT_ABORT_ALL_SESS: + mcmd = QLA_TGT_ABORT_ALL; + break; + case QLA_TGT_NEXUS_LOSS: + case QLA_TGT_ABORT_ALL: + break; + default: + ql_dbg(ql_dbg_tgt, vha, 0xe046, + "qla_target(%d): Not allowed " + "command %x in %s", vha->vp_idx, + mcmd, __func__); + sess = NULL; + break; + } + } else + sess = NULL; +#endif + } else { + sess = ha->tgt.tgt_ops->find_sess_by_loop_id(vha, loop_id); + } + + ql_dbg(ql_dbg_tgt, vha, 0xe000, + "Using sess for qla_tgt_reset: %p\n", sess); + if (!sess) { + res = -ESRCH; + return res; + } + + ql_dbg(ql_dbg_tgt, vha, 0xe047, + "scsi(%ld): resetting (session %p from port %8phC mcmd %x, " + "loop_id %d)\n", vha->host_no, sess, sess->port_name, + mcmd, loop_id); + + lun = a->u.isp24.fcp_cmnd.lun; + unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun); + + return qlt_issue_task_mgmt(sess, unpacked_lun, mcmd, + iocb, QLA24XX_MGMT_SEND_NACK); +} + +/* ha->hardware_lock supposed to be held on entry */ +static void qlt_schedule_sess_for_deletion(struct qla_tgt_sess *sess, + bool immediate) +{ + struct qla_tgt *tgt = sess->tgt; + uint32_t dev_loss_tmo = tgt->ha->port_down_retry_count + 5; + + if (sess->deleted) + return; + + ql_dbg(ql_dbg_tgt, sess->vha, 0xe001, + "Scheduling sess %p for deletion\n", sess); + list_add_tail(&sess->del_list_entry, &tgt->del_sess_list); + sess->deleted = 1; + + if (immediate) + dev_loss_tmo = 0; + + sess->expires = jiffies + dev_loss_tmo * HZ; + + ql_dbg(ql_dbg_tgt, sess->vha, 0xe048, + "qla_target(%d): session for port %8phC (loop ID %d) scheduled for " + "deletion in %u secs (expires: %lu) immed: %d\n", + sess->vha->vp_idx, sess->port_name, sess->loop_id, dev_loss_tmo, + sess->expires, immediate); + + if (immediate) + schedule_delayed_work(&tgt->sess_del_work, 0); + else + schedule_delayed_work(&tgt->sess_del_work, + sess->expires - jiffies); +} + +/* ha->hardware_lock supposed to be held on entry */ +static void qlt_clear_tgt_db(struct qla_tgt *tgt, bool local_only) +{ + struct qla_tgt_sess *sess; + + list_for_each_entry(sess, &tgt->sess_list, sess_list_entry) + qlt_schedule_sess_for_deletion(sess, true); + + /* At this point tgt could be already dead */ +} + +static int qla24xx_get_loop_id(struct scsi_qla_host *vha, const uint8_t *s_id, + uint16_t *loop_id) +{ + struct qla_hw_data *ha = vha->hw; + dma_addr_t gid_list_dma; + struct gid_list_info *gid_list; + char *id_iter; + int res, rc, i; + uint16_t entries; + + gid_list = dma_alloc_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha), + &gid_list_dma, GFP_KERNEL); + if (!gid_list) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf044, + "qla_target(%d): DMA Alloc failed of %u\n", + vha->vp_idx, qla2x00_gid_list_size(ha)); + return -ENOMEM; + } + + /* Get list of logged in devices */ + rc = qla2x00_get_id_list(vha, gid_list, gid_list_dma, &entries); + if (rc != QLA_SUCCESS) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf045, + "qla_target(%d): get_id_list() failed: %x\n", + vha->vp_idx, rc); + res = -1; + goto out_free_id_list; + } + + id_iter = (char *)gid_list; + res = -1; + for (i = 0; i < entries; i++) { + struct gid_list_info *gid = (struct gid_list_info *)id_iter; + if ((gid->al_pa == s_id[2]) && + (gid->area == s_id[1]) && + (gid->domain == s_id[0])) { + *loop_id = le16_to_cpu(gid->loop_id); + res = 0; + break; + } + id_iter += ha->gid_list_info_size; + } + +out_free_id_list: + dma_free_coherent(&ha->pdev->dev, qla2x00_gid_list_size(ha), + gid_list, gid_list_dma); + return res; +} + +/* ha->hardware_lock supposed to be held on entry */ +static void qlt_undelete_sess(struct qla_tgt_sess *sess) +{ + BUG_ON(!sess->deleted); + + list_del(&sess->del_list_entry); + sess->deleted = 0; +} + +static void qlt_del_sess_work_fn(struct delayed_work *work) +{ + struct qla_tgt *tgt = container_of(work, struct qla_tgt, + sess_del_work); + struct scsi_qla_host *vha = tgt->vha; + struct qla_hw_data *ha = vha->hw; + struct qla_tgt_sess *sess; + unsigned long flags, elapsed; + + spin_lock_irqsave(&ha->hardware_lock, flags); + while (!list_empty(&tgt->del_sess_list)) { + sess = list_entry(tgt->del_sess_list.next, typeof(*sess), + del_list_entry); + elapsed = jiffies; + if (time_after_eq(elapsed, sess->expires)) { + qlt_undelete_sess(sess); + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf004, + "Timeout: sess %p about to be deleted\n", + sess); + ha->tgt.tgt_ops->shutdown_sess(sess); + ha->tgt.tgt_ops->put_sess(sess); + } else { + schedule_delayed_work(&tgt->sess_del_work, + sess->expires - elapsed); + break; + } + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + +/* + * Adds an extra ref to allow to drop hw lock after adding sess to the list. + * Caller must put it. + */ +static struct qla_tgt_sess *qlt_create_sess( + struct scsi_qla_host *vha, + fc_port_t *fcport, + bool local) +{ + struct qla_hw_data *ha = vha->hw; + struct qla_tgt_sess *sess; + unsigned long flags; + unsigned char be_sid[3]; + + /* Check to avoid double sessions */ + spin_lock_irqsave(&ha->hardware_lock, flags); + list_for_each_entry(sess, &vha->vha_tgt.qla_tgt->sess_list, + sess_list_entry) { + if (!memcmp(sess->port_name, fcport->port_name, WWN_SIZE)) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf005, + "Double sess %p found (s_id %x:%x:%x, " + "loop_id %d), updating to d_id %x:%x:%x, " + "loop_id %d", sess, sess->s_id.b.domain, + sess->s_id.b.al_pa, sess->s_id.b.area, + sess->loop_id, fcport->d_id.b.domain, + fcport->d_id.b.al_pa, fcport->d_id.b.area, + fcport->loop_id); + + if (sess->deleted) + qlt_undelete_sess(sess); + + kref_get(&sess->se_sess->sess_kref); + ha->tgt.tgt_ops->update_sess(sess, fcport->d_id, fcport->loop_id, + (fcport->flags & FCF_CONF_COMP_SUPPORTED)); + + if (sess->local && !local) + sess->local = 0; + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return sess; + } + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + sess = kzalloc(sizeof(*sess), GFP_KERNEL); + if (!sess) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04a, + "qla_target(%u): session allocation failed, all commands " + "from port %8phC will be refused", vha->vp_idx, + fcport->port_name); + + return NULL; + } + sess->tgt = vha->vha_tgt.qla_tgt; + sess->vha = vha; + sess->s_id = fcport->d_id; + sess->loop_id = fcport->loop_id; + sess->local = local; + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf006, + "Adding sess %p to tgt %p via ->check_initiator_node_acl()\n", + sess, vha->vha_tgt.qla_tgt); + + be_sid[0] = sess->s_id.b.domain; + be_sid[1] = sess->s_id.b.area; + be_sid[2] = sess->s_id.b.al_pa; + /* + * Determine if this fc_port->port_name is allowed to access + * target mode using explict NodeACLs+MappedLUNs, or using + * TPG demo mode. If this is successful a target mode FC nexus + * is created. + */ + if (ha->tgt.tgt_ops->check_initiator_node_acl(vha, + &fcport->port_name[0], sess, &be_sid[0], fcport->loop_id) < 0) { + kfree(sess); + return NULL; + } + /* + * Take an extra reference to ->sess_kref here to handle qla_tgt_sess + * access across ->hardware_lock reaquire. + */ + kref_get(&sess->se_sess->sess_kref); + + sess->conf_compl_supported = (fcport->flags & FCF_CONF_COMP_SUPPORTED); + BUILD_BUG_ON(sizeof(sess->port_name) != sizeof(fcport->port_name)); + memcpy(sess->port_name, fcport->port_name, sizeof(sess->port_name)); + + spin_lock_irqsave(&ha->hardware_lock, flags); + list_add_tail(&sess->sess_list_entry, &vha->vha_tgt.qla_tgt->sess_list); + vha->vha_tgt.qla_tgt->sess_count++; + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04b, + "qla_target(%d): %ssession for wwn %8phC (loop_id %d, " + "s_id %x:%x:%x, confirmed completion %ssupported) added\n", + vha->vp_idx, local ? "local " : "", fcport->port_name, + fcport->loop_id, sess->s_id.b.domain, sess->s_id.b.area, + sess->s_id.b.al_pa, sess->conf_compl_supported ? "" : "not "); + + return sess; +} + +/* + * Called from drivers/scsi/qla2xxx/qla_init.c:qla2x00_reg_remote_port() + */ +void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport) +{ + struct qla_hw_data *ha = vha->hw; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; + struct qla_tgt_sess *sess; + unsigned long flags; + + if (!vha->hw->tgt.tgt_ops) + return; + + if (!tgt || (fcport->port_type != FCT_INITIATOR)) + return; + + if (qla_ini_mode_enabled(vha)) + return; + + spin_lock_irqsave(&ha->hardware_lock, flags); + if (tgt->tgt_stop) { + spin_unlock_irqrestore(&ha->hardware_lock, flags); + return; + } + sess = qlt_find_sess_by_port_name(tgt, fcport->port_name); + if (!sess) { + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + mutex_lock(&vha->vha_tgt.tgt_mutex); + sess = qlt_create_sess(vha, fcport, false); + mutex_unlock(&vha->vha_tgt.tgt_mutex); + + spin_lock_irqsave(&ha->hardware_lock, flags); + } else { + kref_get(&sess->se_sess->sess_kref); + + if (sess->deleted) { + qlt_undelete_sess(sess); + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04c, + "qla_target(%u): %ssession for port %8phC " + "(loop ID %d) reappeared\n", vha->vp_idx, + sess->local ? "local " : "", sess->port_name, + sess->loop_id); + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf007, + "Reappeared sess %p\n", sess); + } + ha->tgt.tgt_ops->update_sess(sess, fcport->d_id, fcport->loop_id, + (fcport->flags & FCF_CONF_COMP_SUPPORTED)); + } + + if (sess && sess->local) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04d, + "qla_target(%u): local session for " + "port %8phC (loop ID %d) became global\n", vha->vp_idx, + fcport->port_name, sess->loop_id); + sess->local = 0; + } + ha->tgt.tgt_ops->put_sess(sess); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + +void qlt_fc_port_deleted(struct scsi_qla_host *vha, fc_port_t *fcport) +{ + struct qla_hw_data *ha = vha->hw; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; + struct qla_tgt_sess *sess; + unsigned long flags; + + if (!vha->hw->tgt.tgt_ops) + return; + + if (!tgt || (fcport->port_type != FCT_INITIATOR)) + return; + + spin_lock_irqsave(&ha->hardware_lock, flags); + if (tgt->tgt_stop) { + spin_unlock_irqrestore(&ha->hardware_lock, flags); + return; + } + sess = qlt_find_sess_by_port_name(tgt, fcport->port_name); + if (!sess) { + spin_unlock_irqrestore(&ha->hardware_lock, flags); + return; + } + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf008, "qla_tgt_fc_port_deleted %p", sess); + + sess->local = 1; + qlt_schedule_sess_for_deletion(sess, false); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + +static inline int test_tgt_sess_count(struct qla_tgt *tgt) +{ + struct qla_hw_data *ha = tgt->ha; + unsigned long flags; + int res; + /* + * We need to protect against race, when tgt is freed before or + * inside wake_up() + */ + spin_lock_irqsave(&ha->hardware_lock, flags); + ql_dbg(ql_dbg_tgt, tgt->vha, 0xe002, + "tgt %p, empty(sess_list)=%d sess_count=%d\n", + tgt, list_empty(&tgt->sess_list), tgt->sess_count); + res = (tgt->sess_count == 0); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return res; +} + +/* Called by tcm_qla2xxx configfs code */ +int qlt_stop_phase1(struct qla_tgt *tgt) +{ + struct scsi_qla_host *vha = tgt->vha; + struct qla_hw_data *ha = tgt->ha; + unsigned long flags; + + mutex_lock(&qla_tgt_mutex); + if (!vha->fc_vport) { + struct Scsi_Host *sh = vha->host; + struct fc_host_attrs *fc_host = shost_to_fc_host(sh); + bool npiv_vports; + + spin_lock_irqsave(sh->host_lock, flags); + npiv_vports = (fc_host->npiv_vports_inuse); + spin_unlock_irqrestore(sh->host_lock, flags); + + if (npiv_vports) { + mutex_unlock(&qla_tgt_mutex); + return -EPERM; + } + } + if (tgt->tgt_stop || tgt->tgt_stopped) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04e, + "Already in tgt->tgt_stop or tgt_stopped state\n"); + mutex_unlock(&qla_tgt_mutex); + return -EPERM; + } + + ql_dbg(ql_dbg_tgt, vha, 0xe003, "Stopping target for host %ld(%p)\n", + vha->host_no, vha); + /* + * Mutex needed to sync with qla_tgt_fc_port_[added,deleted]. + * Lock is needed, because we still can get an incoming packet. + */ + mutex_lock(&vha->vha_tgt.tgt_mutex); + spin_lock_irqsave(&ha->hardware_lock, flags); + tgt->tgt_stop = 1; + qlt_clear_tgt_db(tgt, true); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + mutex_unlock(&vha->vha_tgt.tgt_mutex); + mutex_unlock(&qla_tgt_mutex); + + flush_delayed_work(&tgt->sess_del_work); + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf009, + "Waiting for sess works (tgt %p)", tgt); + spin_lock_irqsave(&tgt->sess_work_lock, flags); + while (!list_empty(&tgt->sess_works_list)) { + spin_unlock_irqrestore(&tgt->sess_work_lock, flags); + flush_scheduled_work(); + spin_lock_irqsave(&tgt->sess_work_lock, flags); + } + spin_unlock_irqrestore(&tgt->sess_work_lock, flags); + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00a, + "Waiting for tgt %p: list_empty(sess_list)=%d " + "sess_count=%d\n", tgt, list_empty(&tgt->sess_list), + tgt->sess_count); + + wait_event(tgt->waitQ, test_tgt_sess_count(tgt)); + + /* Big hammer */ + if (!ha->flags.host_shutting_down && qla_tgt_mode_enabled(vha)) + qlt_disable_vha(vha); + + /* Wait for sessions to clear out (just in case) */ + wait_event(tgt->waitQ, test_tgt_sess_count(tgt)); + return 0; +} +EXPORT_SYMBOL(qlt_stop_phase1); + +/* Called by tcm_qla2xxx configfs code */ +void qlt_stop_phase2(struct qla_tgt *tgt) +{ + struct qla_hw_data *ha = tgt->ha; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + unsigned long flags; + + if (tgt->tgt_stopped) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04f, + "Already in tgt->tgt_stopped state\n"); + dump_stack(); + return; + } + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00b, + "Waiting for %d IRQ commands to complete (tgt %p)", + tgt->irq_cmd_count, tgt); + + mutex_lock(&vha->vha_tgt.tgt_mutex); + spin_lock_irqsave(&ha->hardware_lock, flags); + while (tgt->irq_cmd_count != 0) { + spin_unlock_irqrestore(&ha->hardware_lock, flags); + udelay(2); + spin_lock_irqsave(&ha->hardware_lock, flags); + } + tgt->tgt_stop = 0; + tgt->tgt_stopped = 1; + spin_unlock_irqrestore(&ha->hardware_lock, flags); + mutex_unlock(&vha->vha_tgt.tgt_mutex); + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00c, "Stop of tgt %p finished", + tgt); +} +EXPORT_SYMBOL(qlt_stop_phase2); + +/* Called from qlt_remove_target() -> qla2x00_remove_one() */ +static void qlt_release(struct qla_tgt *tgt) +{ + scsi_qla_host_t *vha = tgt->vha; + + if ((vha->vha_tgt.qla_tgt != NULL) && !tgt->tgt_stopped) + qlt_stop_phase2(tgt); + + vha->vha_tgt.qla_tgt = NULL; + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00d, + "Release of tgt %p finished\n", tgt); + + kfree(tgt); +} + +/* ha->hardware_lock supposed to be held on entry */ +static int qlt_sched_sess_work(struct qla_tgt *tgt, int type, + const void *param, unsigned int param_size) +{ + struct qla_tgt_sess_work_param *prm; + unsigned long flags; + + prm = kzalloc(sizeof(*prm), GFP_ATOMIC); + if (!prm) { + ql_dbg(ql_dbg_tgt_mgt, tgt->vha, 0xf050, + "qla_target(%d): Unable to create session " + "work, command will be refused", 0); + return -ENOMEM; + } + + ql_dbg(ql_dbg_tgt_mgt, tgt->vha, 0xf00e, + "Scheduling work (type %d, prm %p)" + " to find session for param %p (size %d, tgt %p)\n", + type, prm, param, param_size, tgt); + + prm->type = type; + memcpy(&prm->tm_iocb, param, param_size); + + spin_lock_irqsave(&tgt->sess_work_lock, flags); + list_add_tail(&prm->sess_works_list_entry, &tgt->sess_works_list); + spin_unlock_irqrestore(&tgt->sess_work_lock, flags); + + schedule_work(&tgt->sess_work); + + return 0; +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static void qlt_send_notify_ack(struct scsi_qla_host *vha, + struct imm_ntfy_from_isp *ntfy, + uint32_t add_flags, uint16_t resp_code, int resp_code_valid, + uint16_t srr_flags, uint16_t srr_reject_code, uint8_t srr_explan) +{ + struct qla_hw_data *ha = vha->hw; + request_t *pkt; + struct nack_to_isp *nack; + + ql_dbg(ql_dbg_tgt, vha, 0xe004, "Sending NOTIFY_ACK (ha=%p)\n", ha); + + /* Send marker if required */ + if (qlt_issue_marker(vha, 1) != QLA_SUCCESS) + return; + + pkt = (request_t *)qla2x00_alloc_iocbs(vha, NULL); + if (!pkt) { + ql_dbg(ql_dbg_tgt, vha, 0xe049, + "qla_target(%d): %s failed: unable to allocate " + "request packet\n", vha->vp_idx, __func__); + return; + } + + if (vha->vha_tgt.qla_tgt != NULL) + vha->vha_tgt.qla_tgt->notify_ack_expected++; + + pkt->entry_type = NOTIFY_ACK_TYPE; + pkt->entry_count = 1; + + nack = (struct nack_to_isp *)pkt; + nack->ox_id = ntfy->ox_id; + + nack->u.isp24.nport_handle = ntfy->u.isp24.nport_handle; + if (le16_to_cpu(ntfy->u.isp24.status) == IMM_NTFY_ELS) { + nack->u.isp24.flags = ntfy->u.isp24.flags & + __constant_cpu_to_le32(NOTIFY24XX_FLAGS_PUREX_IOCB); + } + nack->u.isp24.srr_rx_id = ntfy->u.isp24.srr_rx_id; + nack->u.isp24.status = ntfy->u.isp24.status; + nack->u.isp24.status_subcode = ntfy->u.isp24.status_subcode; + nack->u.isp24.fw_handle = ntfy->u.isp24.fw_handle; + nack->u.isp24.exchange_address = ntfy->u.isp24.exchange_address; + nack->u.isp24.srr_rel_offs = ntfy->u.isp24.srr_rel_offs; + nack->u.isp24.srr_ui = ntfy->u.isp24.srr_ui; + nack->u.isp24.srr_flags = cpu_to_le16(srr_flags); + nack->u.isp24.srr_reject_code = srr_reject_code; + nack->u.isp24.srr_reject_code_expl = srr_explan; + nack->u.isp24.vp_index = ntfy->u.isp24.vp_index; + + ql_dbg(ql_dbg_tgt, vha, 0xe005, + "qla_target(%d): Sending 24xx Notify Ack %d\n", + vha->vp_idx, nack->u.isp24.status); + + qla2x00_start_iocbs(vha, vha->req); +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static void qlt_24xx_send_abts_resp(struct scsi_qla_host *vha, + struct abts_recv_from_24xx *abts, uint32_t status, + bool ids_reversed) +{ + struct qla_hw_data *ha = vha->hw; + struct abts_resp_to_24xx *resp; + uint32_t f_ctl; + uint8_t *p; + + ql_dbg(ql_dbg_tgt, vha, 0xe006, + "Sending task mgmt ABTS response (ha=%p, atio=%p, status=%x\n", + ha, abts, status); + + /* Send marker if required */ + if (qlt_issue_marker(vha, 1) != QLA_SUCCESS) + return; + + resp = (struct abts_resp_to_24xx *)qla2x00_alloc_iocbs(vha, NULL); + if (!resp) { + ql_dbg(ql_dbg_tgt, vha, 0xe04a, + "qla_target(%d): %s failed: unable to allocate " + "request packet", vha->vp_idx, __func__); + return; + } + + resp->entry_type = ABTS_RESP_24XX; + resp->entry_count = 1; + resp->nport_handle = abts->nport_handle; + resp->vp_index = vha->vp_idx; + resp->sof_type = abts->sof_type; + resp->exchange_address = abts->exchange_address; + resp->fcp_hdr_le = abts->fcp_hdr_le; + f_ctl = __constant_cpu_to_le32(F_CTL_EXCH_CONTEXT_RESP | + F_CTL_LAST_SEQ | F_CTL_END_SEQ | + F_CTL_SEQ_INITIATIVE); + p = (uint8_t *)&f_ctl; + resp->fcp_hdr_le.f_ctl[0] = *p++; + resp->fcp_hdr_le.f_ctl[1] = *p++; + resp->fcp_hdr_le.f_ctl[2] = *p; + if (ids_reversed) { + resp->fcp_hdr_le.d_id[0] = abts->fcp_hdr_le.d_id[0]; + resp->fcp_hdr_le.d_id[1] = abts->fcp_hdr_le.d_id[1]; + resp->fcp_hdr_le.d_id[2] = abts->fcp_hdr_le.d_id[2]; + resp->fcp_hdr_le.s_id[0] = abts->fcp_hdr_le.s_id[0]; + resp->fcp_hdr_le.s_id[1] = abts->fcp_hdr_le.s_id[1]; + resp->fcp_hdr_le.s_id[2] = abts->fcp_hdr_le.s_id[2]; + } else { + resp->fcp_hdr_le.d_id[0] = abts->fcp_hdr_le.s_id[0]; + resp->fcp_hdr_le.d_id[1] = abts->fcp_hdr_le.s_id[1]; + resp->fcp_hdr_le.d_id[2] = abts->fcp_hdr_le.s_id[2]; + resp->fcp_hdr_le.s_id[0] = abts->fcp_hdr_le.d_id[0]; + resp->fcp_hdr_le.s_id[1] = abts->fcp_hdr_le.d_id[1]; + resp->fcp_hdr_le.s_id[2] = abts->fcp_hdr_le.d_id[2]; + } + resp->exchange_addr_to_abort = abts->exchange_addr_to_abort; + if (status == FCP_TMF_CMPL) { + resp->fcp_hdr_le.r_ctl = R_CTL_BASIC_LINK_SERV | R_CTL_B_ACC; + resp->payload.ba_acct.seq_id_valid = SEQ_ID_INVALID; + resp->payload.ba_acct.low_seq_cnt = 0x0000; + resp->payload.ba_acct.high_seq_cnt = 0xFFFF; + resp->payload.ba_acct.ox_id = abts->fcp_hdr_le.ox_id; + resp->payload.ba_acct.rx_id = abts->fcp_hdr_le.rx_id; + } else { + resp->fcp_hdr_le.r_ctl = R_CTL_BASIC_LINK_SERV | R_CTL_B_RJT; + resp->payload.ba_rjt.reason_code = + BA_RJT_REASON_CODE_UNABLE_TO_PERFORM; + /* Other bytes are zero */ + } + + vha->vha_tgt.qla_tgt->abts_resp_expected++; + + qla2x00_start_iocbs(vha, vha->req); +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static void qlt_24xx_retry_term_exchange(struct scsi_qla_host *vha, + struct abts_resp_from_24xx_fw *entry) +{ + struct ctio7_to_24xx *ctio; + + ql_dbg(ql_dbg_tgt, vha, 0xe007, + "Sending retry TERM EXCH CTIO7 (ha=%p)\n", vha->hw); + /* Send marker if required */ + if (qlt_issue_marker(vha, 1) != QLA_SUCCESS) + return; + + ctio = (struct ctio7_to_24xx *)qla2x00_alloc_iocbs(vha, NULL); + if (ctio == NULL) { + ql_dbg(ql_dbg_tgt, vha, 0xe04b, + "qla_target(%d): %s failed: unable to allocate " + "request packet\n", vha->vp_idx, __func__); + return; + } + + /* + * We've got on entrance firmware's response on by us generated + * ABTS response. So, in it ID fields are reversed. + */ + + ctio->entry_type = CTIO_TYPE7; + ctio->entry_count = 1; + ctio->nport_handle = entry->nport_handle; + ctio->handle = QLA_TGT_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; + ctio->timeout = __constant_cpu_to_le16(QLA_TGT_TIMEOUT); + ctio->vp_index = vha->vp_idx; + ctio->initiator_id[0] = entry->fcp_hdr_le.d_id[0]; + ctio->initiator_id[1] = entry->fcp_hdr_le.d_id[1]; + ctio->initiator_id[2] = entry->fcp_hdr_le.d_id[2]; + ctio->exchange_addr = entry->exchange_addr_to_abort; + ctio->u.status1.flags = + __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1 | + CTIO7_FLAGS_TERMINATE); + ctio->u.status1.ox_id = cpu_to_le16(entry->fcp_hdr_le.ox_id); + + qla2x00_start_iocbs(vha, vha->req); + + qlt_24xx_send_abts_resp(vha, (struct abts_recv_from_24xx *)entry, + FCP_TMF_CMPL, true); +} + +/* ha->hardware_lock supposed to be held on entry */ +static int __qlt_24xx_handle_abts(struct scsi_qla_host *vha, + struct abts_recv_from_24xx *abts, struct qla_tgt_sess *sess) +{ + struct qla_hw_data *ha = vha->hw; + struct se_session *se_sess = sess->se_sess; + struct qla_tgt_mgmt_cmd *mcmd; + struct se_cmd *se_cmd; + u32 lun = 0; + int rc; + bool found_lun = false; + + spin_lock(&se_sess->sess_cmd_lock); + list_for_each_entry(se_cmd, &se_sess->sess_cmd_list, se_cmd_list) { + struct qla_tgt_cmd *cmd = + container_of(se_cmd, struct qla_tgt_cmd, se_cmd); + if (cmd->tag == abts->exchange_addr_to_abort) { + lun = cmd->unpacked_lun; + found_lun = true; + break; + } + } + spin_unlock(&se_sess->sess_cmd_lock); + + if (!found_lun) + return -ENOENT; + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00f, + "qla_target(%d): task abort (tag=%d)\n", + vha->vp_idx, abts->exchange_addr_to_abort); + + mcmd = mempool_alloc(qla_tgt_mgmt_cmd_mempool, GFP_ATOMIC); + if (mcmd == NULL) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf051, + "qla_target(%d): %s: Allocation of ABORT cmd failed", + vha->vp_idx, __func__); + return -ENOMEM; + } + memset(mcmd, 0, sizeof(*mcmd)); + + mcmd->sess = sess; + memcpy(&mcmd->orig_iocb.abts, abts, sizeof(mcmd->orig_iocb.abts)); + + rc = ha->tgt.tgt_ops->handle_tmr(mcmd, lun, TMR_ABORT_TASK, + abts->exchange_addr_to_abort); + if (rc != 0) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf052, + "qla_target(%d): tgt_ops->handle_tmr()" + " failed: %d", vha->vp_idx, rc); + mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool); + return -EFAULT; + } + + return 0; +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static void qlt_24xx_handle_abts(struct scsi_qla_host *vha, + struct abts_recv_from_24xx *abts) +{ + struct qla_hw_data *ha = vha->hw; + struct qla_tgt_sess *sess; + uint32_t tag = abts->exchange_addr_to_abort; + uint8_t s_id[3]; + int rc; + + if (le32_to_cpu(abts->fcp_hdr_le.parameter) & ABTS_PARAM_ABORT_SEQ) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf053, + "qla_target(%d): ABTS: Abort Sequence not " + "supported\n", vha->vp_idx); + qlt_24xx_send_abts_resp(vha, abts, FCP_TMF_REJECTED, false); + return; + } + + if (tag == ATIO_EXCHANGE_ADDRESS_UNKNOWN) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf010, + "qla_target(%d): ABTS: Unknown Exchange " + "Address received\n", vha->vp_idx); + qlt_24xx_send_abts_resp(vha, abts, FCP_TMF_REJECTED, false); + return; + } + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf011, + "qla_target(%d): task abort (s_id=%x:%x:%x, " + "tag=%d, param=%x)\n", vha->vp_idx, abts->fcp_hdr_le.s_id[2], + abts->fcp_hdr_le.s_id[1], abts->fcp_hdr_le.s_id[0], tag, + le32_to_cpu(abts->fcp_hdr_le.parameter)); + + s_id[0] = abts->fcp_hdr_le.s_id[2]; + s_id[1] = abts->fcp_hdr_le.s_id[1]; + s_id[2] = abts->fcp_hdr_le.s_id[0]; + + sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, s_id); + if (!sess) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf012, + "qla_target(%d): task abort for non-existant session\n", + vha->vp_idx); + rc = qlt_sched_sess_work(vha->vha_tgt.qla_tgt, + QLA_TGT_SESS_WORK_ABORT, abts, sizeof(*abts)); + if (rc != 0) { + qlt_24xx_send_abts_resp(vha, abts, FCP_TMF_REJECTED, + false); + } + return; + } + + rc = __qlt_24xx_handle_abts(vha, abts, sess); + if (rc != 0) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf054, + "qla_target(%d): __qlt_24xx_handle_abts() failed: %d\n", + vha->vp_idx, rc); + qlt_24xx_send_abts_resp(vha, abts, FCP_TMF_REJECTED, false); + return; + } +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static void qlt_24xx_send_task_mgmt_ctio(struct scsi_qla_host *ha, + struct qla_tgt_mgmt_cmd *mcmd, uint32_t resp_code) +{ + struct atio_from_isp *atio = &mcmd->orig_iocb.atio; + struct ctio7_to_24xx *ctio; + uint16_t temp; + + ql_dbg(ql_dbg_tgt, ha, 0xe008, + "Sending task mgmt CTIO7 (ha=%p, atio=%p, resp_code=%x\n", + ha, atio, resp_code); + + /* Send marker if required */ + if (qlt_issue_marker(ha, 1) != QLA_SUCCESS) + return; + + ctio = (struct ctio7_to_24xx *)qla2x00_alloc_iocbs(ha, NULL); + if (ctio == NULL) { + ql_dbg(ql_dbg_tgt, ha, 0xe04c, + "qla_target(%d): %s failed: unable to allocate " + "request packet\n", ha->vp_idx, __func__); + return; + } + + ctio->entry_type = CTIO_TYPE7; + ctio->entry_count = 1; + ctio->handle = QLA_TGT_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; + ctio->nport_handle = mcmd->sess->loop_id; + ctio->timeout = __constant_cpu_to_le16(QLA_TGT_TIMEOUT); + ctio->vp_index = ha->vp_idx; + ctio->initiator_id[0] = atio->u.isp24.fcp_hdr.s_id[2]; + ctio->initiator_id[1] = atio->u.isp24.fcp_hdr.s_id[1]; + ctio->initiator_id[2] = atio->u.isp24.fcp_hdr.s_id[0]; + ctio->exchange_addr = atio->u.isp24.exchange_addr; + ctio->u.status1.flags = (atio->u.isp24.attr << 9) | + __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1 | + CTIO7_FLAGS_SEND_STATUS); + temp = be16_to_cpu(atio->u.isp24.fcp_hdr.ox_id); + ctio->u.status1.ox_id = cpu_to_le16(temp); + ctio->u.status1.scsi_status = + __constant_cpu_to_le16(SS_RESPONSE_INFO_LEN_VALID); + ctio->u.status1.response_len = __constant_cpu_to_le16(8); + ctio->u.status1.sense_data[0] = resp_code; + + qla2x00_start_iocbs(ha, ha->req); +} + +void qlt_free_mcmd(struct qla_tgt_mgmt_cmd *mcmd) +{ + mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool); +} +EXPORT_SYMBOL(qlt_free_mcmd); + +/* callback from target fabric module code */ +void qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *mcmd) +{ + struct scsi_qla_host *vha = mcmd->sess->vha; + struct qla_hw_data *ha = vha->hw; + unsigned long flags; + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf013, + "TM response mcmd (%p) status %#x state %#x", + mcmd, mcmd->fc_tm_rsp, mcmd->flags); + + spin_lock_irqsave(&ha->hardware_lock, flags); + if (mcmd->flags == QLA24XX_MGMT_SEND_NACK) + qlt_send_notify_ack(vha, &mcmd->orig_iocb.imm_ntfy, + 0, 0, 0, 0, 0, 0); + else { + if (mcmd->se_cmd.se_tmr_req->function == TMR_ABORT_TASK) + qlt_24xx_send_abts_resp(vha, &mcmd->orig_iocb.abts, + mcmd->fc_tm_rsp, false); + else + qlt_24xx_send_task_mgmt_ctio(vha, mcmd, + mcmd->fc_tm_rsp); + } + /* + * Make the callback for ->free_mcmd() to queue_work() and invoke + * target_put_sess_cmd() to drop cmd_kref to 1. The final + * target_put_sess_cmd() call will be made from TFO->check_stop_free() + * -> tcm_qla2xxx_check_stop_free() to release the TMR associated se_cmd + * descriptor after TFO->queue_tm_rsp() -> tcm_qla2xxx_queue_tm_rsp() -> + * qlt_xmit_tm_rsp() returns here.. + */ + ha->tgt.tgt_ops->free_mcmd(mcmd); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} +EXPORT_SYMBOL(qlt_xmit_tm_rsp); + +/* No locks */ +static int qlt_pci_map_calc_cnt(struct qla_tgt_prm *prm) +{ + struct qla_tgt_cmd *cmd = prm->cmd; + + BUG_ON(cmd->sg_cnt == 0); + + prm->sg = (struct scatterlist *)cmd->sg; + prm->seg_cnt = pci_map_sg(prm->tgt->ha->pdev, cmd->sg, + cmd->sg_cnt, cmd->dma_data_direction); + if (unlikely(prm->seg_cnt == 0)) + goto out_err; + + prm->cmd->sg_mapped = 1; + + if (cmd->se_cmd.prot_op == TARGET_PROT_NORMAL) { + /* + * If greater than four sg entries then we need to allocate + * the continuation entries + */ + if (prm->seg_cnt > prm->tgt->datasegs_per_cmd) + prm->req_cnt += DIV_ROUND_UP(prm->seg_cnt - + prm->tgt->datasegs_per_cmd, + prm->tgt->datasegs_per_cont); + } else { + /* DIF */ + if ((cmd->se_cmd.prot_op == TARGET_PROT_DIN_INSERT) || + (cmd->se_cmd.prot_op == TARGET_PROT_DOUT_STRIP)) { + prm->seg_cnt = DIV_ROUND_UP(cmd->bufflen, cmd->blk_sz); + prm->tot_dsds = prm->seg_cnt; + } else + prm->tot_dsds = prm->seg_cnt; + + if (cmd->prot_sg_cnt) { + prm->prot_sg = cmd->prot_sg; + prm->prot_seg_cnt = pci_map_sg(prm->tgt->ha->pdev, + cmd->prot_sg, cmd->prot_sg_cnt, + cmd->dma_data_direction); + if (unlikely(prm->prot_seg_cnt == 0)) + goto out_err; + + if ((cmd->se_cmd.prot_op == TARGET_PROT_DIN_INSERT) || + (cmd->se_cmd.prot_op == TARGET_PROT_DOUT_STRIP)) { + /* Dif Bundling not support here */ + prm->prot_seg_cnt = DIV_ROUND_UP(cmd->bufflen, + cmd->blk_sz); + prm->tot_dsds += prm->prot_seg_cnt; + } else + prm->tot_dsds += prm->prot_seg_cnt; + } + } + + ql_dbg(ql_dbg_tgt, prm->cmd->vha, 0xe009, "seg_cnt=%d, req_cnt=%d\n", + prm->seg_cnt, prm->req_cnt); + return 0; + +out_err: + ql_dbg(ql_dbg_tgt, prm->cmd->vha, 0xe04d, + "qla_target(%d): PCI mapping failed: sg_cnt=%d", + 0, prm->cmd->sg_cnt); + return -1; +} + +static inline void qlt_unmap_sg(struct scsi_qla_host *vha, + struct qla_tgt_cmd *cmd) +{ + struct qla_hw_data *ha = vha->hw; + + BUG_ON(!cmd->sg_mapped); + pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, cmd->dma_data_direction); + cmd->sg_mapped = 0; + + if (cmd->prot_sg_cnt) + pci_unmap_sg(ha->pdev, cmd->prot_sg, cmd->prot_sg_cnt, + cmd->dma_data_direction); + + if (cmd->ctx_dsd_alloced) + qla2x00_clean_dsd_pool(ha, NULL, cmd); + + if (cmd->ctx) + dma_pool_free(ha->dl_dma_pool, cmd->ctx, cmd->ctx->crc_ctx_dma); +} + +static int qlt_check_reserve_free_req(struct scsi_qla_host *vha, + uint32_t req_cnt) +{ + struct qla_hw_data *ha = vha->hw; + device_reg_t __iomem *reg = ha->iobase; + uint32_t cnt; + + if (vha->req->cnt < (req_cnt + 2)) { + cnt = (uint16_t)RD_REG_DWORD(®->isp24.req_q_out); + + ql_dbg(ql_dbg_tgt, vha, 0xe00a, + "Request ring circled: cnt=%d, vha->->ring_index=%d, " + "vha->req->cnt=%d, req_cnt=%d\n", cnt, + vha->req->ring_index, vha->req->cnt, req_cnt); + if (vha->req->ring_index < cnt) + vha->req->cnt = cnt - vha->req->ring_index; + else + vha->req->cnt = vha->req->length - + (vha->req->ring_index - cnt); + } + + if (unlikely(vha->req->cnt < (req_cnt + 2))) { + ql_dbg(ql_dbg_tgt, vha, 0xe00b, + "qla_target(%d): There is no room in the " + "request ring: vha->req->ring_index=%d, vha->req->cnt=%d, " + "req_cnt=%d\n", vha->vp_idx, vha->req->ring_index, + vha->req->cnt, req_cnt); + return -EAGAIN; + } + vha->req->cnt -= req_cnt; + + return 0; +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static inline void *qlt_get_req_pkt(struct scsi_qla_host *vha) +{ + /* Adjust ring index. */ + vha->req->ring_index++; + if (vha->req->ring_index == vha->req->length) { + vha->req->ring_index = 0; + vha->req->ring_ptr = vha->req->ring; + } else { + vha->req->ring_ptr++; + } + return (cont_entry_t *)vha->req->ring_ptr; +} + +/* ha->hardware_lock supposed to be held on entry */ +static inline uint32_t qlt_make_handle(struct scsi_qla_host *vha) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t h; + + h = ha->tgt.current_handle; + /* always increment cmd handle */ + do { + ++h; + if (h > DEFAULT_OUTSTANDING_COMMANDS) + h = 1; /* 0 is QLA_TGT_NULL_HANDLE */ + if (h == ha->tgt.current_handle) { + ql_dbg(ql_dbg_tgt, vha, 0xe04e, + "qla_target(%d): Ran out of " + "empty cmd slots in ha %p\n", vha->vp_idx, ha); + h = QLA_TGT_NULL_HANDLE; + break; + } + } while ((h == QLA_TGT_NULL_HANDLE) || + (h == QLA_TGT_SKIP_HANDLE) || + (ha->tgt.cmds[h-1] != NULL)); + + if (h != QLA_TGT_NULL_HANDLE) + ha->tgt.current_handle = h; + + return h; +} + +/* ha->hardware_lock supposed to be held on entry */ +static int qlt_24xx_build_ctio_pkt(struct qla_tgt_prm *prm, + struct scsi_qla_host *vha) +{ + uint32_t h; + struct ctio7_to_24xx *pkt; + struct qla_hw_data *ha = vha->hw; + struct atio_from_isp *atio = &prm->cmd->atio; + uint16_t temp; + + pkt = (struct ctio7_to_24xx *)vha->req->ring_ptr; + prm->pkt = pkt; + memset(pkt, 0, sizeof(*pkt)); + + pkt->entry_type = CTIO_TYPE7; + pkt->entry_count = (uint8_t)prm->req_cnt; + pkt->vp_index = vha->vp_idx; + + h = qlt_make_handle(vha); + if (unlikely(h == QLA_TGT_NULL_HANDLE)) { + /* + * CTIO type 7 from the firmware doesn't provide a way to + * know the initiator's LOOP ID, hence we can't find + * the session and, so, the command. + */ + return -EAGAIN; + } else + ha->tgt.cmds[h-1] = prm->cmd; + + pkt->handle = h | CTIO_COMPLETION_HANDLE_MARK; + pkt->nport_handle = prm->cmd->loop_id; + pkt->timeout = __constant_cpu_to_le16(QLA_TGT_TIMEOUT); + pkt->initiator_id[0] = atio->u.isp24.fcp_hdr.s_id[2]; + pkt->initiator_id[1] = atio->u.isp24.fcp_hdr.s_id[1]; + pkt->initiator_id[2] = atio->u.isp24.fcp_hdr.s_id[0]; + pkt->exchange_addr = atio->u.isp24.exchange_addr; + pkt->u.status0.flags |= (atio->u.isp24.attr << 9); + temp = be16_to_cpu(atio->u.isp24.fcp_hdr.ox_id); + pkt->u.status0.ox_id = cpu_to_le16(temp); + pkt->u.status0.relative_offset = cpu_to_le32(prm->cmd->offset); + + ql_dbg(ql_dbg_tgt, vha, 0xe00c, + "qla_target(%d): handle(cmd) -> %08x, timeout %d, ox_id %#x\n", + vha->vp_idx, pkt->handle, QLA_TGT_TIMEOUT, temp); + return 0; +} + +/* + * ha->hardware_lock supposed to be held on entry. We have already made sure + * that there is sufficient amount of request entries to not drop it. + */ +static void qlt_load_cont_data_segments(struct qla_tgt_prm *prm, + struct scsi_qla_host *vha) +{ + int cnt; + uint32_t *dword_ptr; + int enable_64bit_addressing = prm->tgt->tgt_enable_64bit_addr; + + /* Build continuation packets */ + while (prm->seg_cnt > 0) { + cont_a64_entry_t *cont_pkt64 = + (cont_a64_entry_t *)qlt_get_req_pkt(vha); + + /* + * Make sure that from cont_pkt64 none of + * 64-bit specific fields used for 32-bit + * addressing. Cast to (cont_entry_t *) for + * that. + */ + + memset(cont_pkt64, 0, sizeof(*cont_pkt64)); + + cont_pkt64->entry_count = 1; + cont_pkt64->sys_define = 0; + + if (enable_64bit_addressing) { + cont_pkt64->entry_type = CONTINUE_A64_TYPE; + dword_ptr = + (uint32_t *)&cont_pkt64->dseg_0_address; + } else { + cont_pkt64->entry_type = CONTINUE_TYPE; + dword_ptr = + (uint32_t *)&((cont_entry_t *) + cont_pkt64)->dseg_0_address; + } + + /* Load continuation entry data segments */ + for (cnt = 0; + cnt < prm->tgt->datasegs_per_cont && prm->seg_cnt; + cnt++, prm->seg_cnt--) { + *dword_ptr++ = + cpu_to_le32(pci_dma_lo32 + (sg_dma_address(prm->sg))); + if (enable_64bit_addressing) { + *dword_ptr++ = + cpu_to_le32(pci_dma_hi32 + (sg_dma_address + (prm->sg))); + } + *dword_ptr++ = cpu_to_le32(sg_dma_len(prm->sg)); + + ql_dbg(ql_dbg_tgt, vha, 0xe00d, + "S/G Segment Cont. phys_addr=%llx:%llx, len=%d\n", + (long long unsigned int) + pci_dma_hi32(sg_dma_address(prm->sg)), + (long long unsigned int) + pci_dma_lo32(sg_dma_address(prm->sg)), + (int)sg_dma_len(prm->sg)); + + prm->sg = sg_next(prm->sg); + } + } +} + +/* + * ha->hardware_lock supposed to be held on entry. We have already made sure + * that there is sufficient amount of request entries to not drop it. + */ +static void qlt_load_data_segments(struct qla_tgt_prm *prm, + struct scsi_qla_host *vha) +{ + int cnt; + uint32_t *dword_ptr; + int enable_64bit_addressing = prm->tgt->tgt_enable_64bit_addr; + struct ctio7_to_24xx *pkt24 = (struct ctio7_to_24xx *)prm->pkt; + + ql_dbg(ql_dbg_tgt, vha, 0xe00e, + "iocb->scsi_status=%x, iocb->flags=%x\n", + le16_to_cpu(pkt24->u.status0.scsi_status), + le16_to_cpu(pkt24->u.status0.flags)); + + pkt24->u.status0.transfer_length = cpu_to_le32(prm->cmd->bufflen); + + /* Setup packet address segment pointer */ + dword_ptr = pkt24->u.status0.dseg_0_address; + + /* Set total data segment count */ + if (prm->seg_cnt) + pkt24->dseg_count = cpu_to_le16(prm->seg_cnt); + + if (prm->seg_cnt == 0) { + /* No data transfer */ + *dword_ptr++ = 0; + *dword_ptr = 0; + return; + } + + /* If scatter gather */ + ql_dbg(ql_dbg_tgt, vha, 0xe00f, "%s", "Building S/G data segments..."); + + /* Load command entry data segments */ + for (cnt = 0; + (cnt < prm->tgt->datasegs_per_cmd) && prm->seg_cnt; + cnt++, prm->seg_cnt--) { + *dword_ptr++ = + cpu_to_le32(pci_dma_lo32(sg_dma_address(prm->sg))); + if (enable_64bit_addressing) { + *dword_ptr++ = + cpu_to_le32(pci_dma_hi32( + sg_dma_address(prm->sg))); + } + *dword_ptr++ = cpu_to_le32(sg_dma_len(prm->sg)); + + ql_dbg(ql_dbg_tgt, vha, 0xe010, + "S/G Segment phys_addr=%llx:%llx, len=%d\n", + (long long unsigned int)pci_dma_hi32(sg_dma_address( + prm->sg)), + (long long unsigned int)pci_dma_lo32(sg_dma_address( + prm->sg)), + (int)sg_dma_len(prm->sg)); + + prm->sg = sg_next(prm->sg); + } + + qlt_load_cont_data_segments(prm, vha); +} + +static inline int qlt_has_data(struct qla_tgt_cmd *cmd) +{ + return cmd->bufflen > 0; +} + +/* + * Called without ha->hardware_lock held + */ +static int qlt_pre_xmit_response(struct qla_tgt_cmd *cmd, + struct qla_tgt_prm *prm, int xmit_type, uint8_t scsi_status, + uint32_t *full_req_cnt) +{ + struct qla_tgt *tgt = cmd->tgt; + struct scsi_qla_host *vha = tgt->vha; + struct qla_hw_data *ha = vha->hw; + struct se_cmd *se_cmd = &cmd->se_cmd; + + if (unlikely(cmd->aborted)) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf014, + "qla_target(%d): terminating exchange " + "for aborted cmd=%p (se_cmd=%p, tag=%d)", vha->vp_idx, cmd, + se_cmd, cmd->tag); + + cmd->state = QLA_TGT_STATE_ABORTED; + + qlt_send_term_exchange(vha, cmd, &cmd->atio, 0); + + /* !! At this point cmd could be already freed !! */ + return QLA_TGT_PRE_XMIT_RESP_CMD_ABORTED; + } + + ql_dbg(ql_dbg_tgt, vha, 0xe011, "qla_target(%d): tag=%u ox_id %04x\n", + vha->vp_idx, cmd->tag, + be16_to_cpu(cmd->atio.u.isp24.fcp_hdr.ox_id)); + + prm->cmd = cmd; + prm->tgt = tgt; + prm->rq_result = scsi_status; + prm->sense_buffer = &cmd->sense_buffer[0]; + prm->sense_buffer_len = TRANSPORT_SENSE_BUFFER; + prm->sg = NULL; + prm->seg_cnt = -1; + prm->req_cnt = 1; + prm->add_status_pkt = 0; + + ql_dbg(ql_dbg_tgt, vha, 0xe012, "rq_result=%x, xmit_type=%x\n", + prm->rq_result, xmit_type); + + /* Send marker if required */ + if (qlt_issue_marker(vha, 0) != QLA_SUCCESS) + return -EFAULT; + + ql_dbg(ql_dbg_tgt, vha, 0xe013, "CTIO start: vha(%d)\n", vha->vp_idx); + + if ((xmit_type & QLA_TGT_XMIT_DATA) && qlt_has_data(cmd)) { + if (qlt_pci_map_calc_cnt(prm) != 0) + return -EAGAIN; + } + + *full_req_cnt = prm->req_cnt; + + if (se_cmd->se_cmd_flags & SCF_UNDERFLOW_BIT) { + prm->residual = se_cmd->residual_count; + ql_dbg(ql_dbg_tgt, vha, 0xe014, + "Residual underflow: %d (tag %d, " + "op %x, bufflen %d, rq_result %x)\n", prm->residual, + cmd->tag, se_cmd->t_task_cdb ? se_cmd->t_task_cdb[0] : 0, + cmd->bufflen, prm->rq_result); + prm->rq_result |= SS_RESIDUAL_UNDER; + } else if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) { + prm->residual = se_cmd->residual_count; + ql_dbg(ql_dbg_tgt, vha, 0xe015, + "Residual overflow: %d (tag %d, " + "op %x, bufflen %d, rq_result %x)\n", prm->residual, + cmd->tag, se_cmd->t_task_cdb ? se_cmd->t_task_cdb[0] : 0, + cmd->bufflen, prm->rq_result); + prm->rq_result |= SS_RESIDUAL_OVER; + } + + if (xmit_type & QLA_TGT_XMIT_STATUS) { + /* + * If QLA_TGT_XMIT_DATA is not set, add_status_pkt will be + * ignored in *xmit_response() below + */ + if (qlt_has_data(cmd)) { + if (QLA_TGT_SENSE_VALID(prm->sense_buffer) || + (IS_FWI2_CAPABLE(ha) && + (prm->rq_result != 0))) { + prm->add_status_pkt = 1; + (*full_req_cnt)++; + } + } + } + + ql_dbg(ql_dbg_tgt, vha, 0xe016, + "req_cnt=%d, full_req_cnt=%d, add_status_pkt=%d\n", + prm->req_cnt, *full_req_cnt, prm->add_status_pkt); + + return 0; +} + +static inline int qlt_need_explicit_conf(struct qla_hw_data *ha, + struct qla_tgt_cmd *cmd, int sending_sense) +{ + if (ha->tgt.enable_class_2) + return 0; + + if (sending_sense) + return cmd->conf_compl_supported; + else + return ha->tgt.enable_explicit_conf && + cmd->conf_compl_supported; +} + +#ifdef CONFIG_QLA_TGT_DEBUG_SRR +/* + * Original taken from the XFS code + */ +static unsigned long qlt_srr_random(void) +{ + static int Inited; + static unsigned long RandomValue; + static DEFINE_SPINLOCK(lock); + /* cycles pseudo-randomly through all values between 1 and 2^31 - 2 */ + register long rv; + register long lo; + register long hi; + unsigned long flags; + + spin_lock_irqsave(&lock, flags); + if (!Inited) { + RandomValue = jiffies; + Inited = 1; + } + rv = RandomValue; + hi = rv / 127773; + lo = rv % 127773; + rv = 16807 * lo - 2836 * hi; + if (rv <= 0) + rv += 2147483647; + RandomValue = rv; + spin_unlock_irqrestore(&lock, flags); + return rv; +} + +static void qlt_check_srr_debug(struct qla_tgt_cmd *cmd, int *xmit_type) +{ +#if 0 /* This is not a real status packets lost, so it won't lead to SRR */ + if ((*xmit_type & QLA_TGT_XMIT_STATUS) && (qlt_srr_random() % 200) + == 50) { + *xmit_type &= ~QLA_TGT_XMIT_STATUS; + ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf015, + "Dropping cmd %p (tag %d) status", cmd, cmd->tag); + } +#endif + /* + * It's currently not possible to simulate SRRs for FCP_WRITE without + * a physical link layer failure, so don't even try here.. + */ + if (cmd->dma_data_direction != DMA_FROM_DEVICE) + return; + + if (qlt_has_data(cmd) && (cmd->sg_cnt > 1) && + ((qlt_srr_random() % 100) == 20)) { + int i, leave = 0; + unsigned int tot_len = 0; + + while (leave == 0) + leave = qlt_srr_random() % cmd->sg_cnt; + + for (i = 0; i < leave; i++) + tot_len += cmd->sg[i].length; + + ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf016, + "Cutting cmd %p (tag %d) buffer" + " tail to len %d, sg_cnt %d (cmd->bufflen %d," + " cmd->sg_cnt %d)", cmd, cmd->tag, tot_len, leave, + cmd->bufflen, cmd->sg_cnt); + + cmd->bufflen = tot_len; + cmd->sg_cnt = leave; + } + + if (qlt_has_data(cmd) && ((qlt_srr_random() % 100) == 70)) { + unsigned int offset = qlt_srr_random() % cmd->bufflen; + + ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf017, + "Cutting cmd %p (tag %d) buffer head " + "to offset %d (cmd->bufflen %d)", cmd, cmd->tag, offset, + cmd->bufflen); + if (offset == 0) + *xmit_type &= ~QLA_TGT_XMIT_DATA; + else if (qlt_set_data_offset(cmd, offset)) { + ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf018, + "qlt_set_data_offset() failed (tag %d)", cmd->tag); + } + } +} +#else +static inline void qlt_check_srr_debug(struct qla_tgt_cmd *cmd, int *xmit_type) +{} +#endif + +static void qlt_24xx_init_ctio_to_isp(struct ctio7_to_24xx *ctio, + struct qla_tgt_prm *prm) +{ + prm->sense_buffer_len = min_t(uint32_t, prm->sense_buffer_len, + (uint32_t)sizeof(ctio->u.status1.sense_data)); + ctio->u.status0.flags |= + __constant_cpu_to_le16(CTIO7_FLAGS_SEND_STATUS); + if (qlt_need_explicit_conf(prm->tgt->ha, prm->cmd, 0)) { + ctio->u.status0.flags |= __constant_cpu_to_le16( + CTIO7_FLAGS_EXPLICIT_CONFORM | + CTIO7_FLAGS_CONFORM_REQ); + } + ctio->u.status0.residual = cpu_to_le32(prm->residual); + ctio->u.status0.scsi_status = cpu_to_le16(prm->rq_result); + if (QLA_TGT_SENSE_VALID(prm->sense_buffer)) { + int i; + + if (qlt_need_explicit_conf(prm->tgt->ha, prm->cmd, 1)) { + if (prm->cmd->se_cmd.scsi_status != 0) { + ql_dbg(ql_dbg_tgt, prm->cmd->vha, 0xe017, + "Skipping EXPLICIT_CONFORM and " + "CTIO7_FLAGS_CONFORM_REQ for FCP READ w/ " + "non GOOD status\n"); + goto skip_explict_conf; + } + ctio->u.status1.flags |= __constant_cpu_to_le16( + CTIO7_FLAGS_EXPLICIT_CONFORM | + CTIO7_FLAGS_CONFORM_REQ); + } +skip_explict_conf: + ctio->u.status1.flags &= + ~__constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_0); + ctio->u.status1.flags |= + __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1); + ctio->u.status1.scsi_status |= + __constant_cpu_to_le16(SS_SENSE_LEN_VALID); + ctio->u.status1.sense_length = + cpu_to_le16(prm->sense_buffer_len); + for (i = 0; i < prm->sense_buffer_len/4; i++) + ((uint32_t *)ctio->u.status1.sense_data)[i] = + cpu_to_be32(((uint32_t *)prm->sense_buffer)[i]); +#if 0 + if (unlikely((prm->sense_buffer_len % 4) != 0)) { + static int q; + if (q < 10) { + ql_dbg(ql_dbg_tgt, vha, 0xe04f, + "qla_target(%d): %d bytes of sense " + "lost", prm->tgt->ha->vp_idx, + prm->sense_buffer_len % 4); + q++; + } + } +#endif + } else { + ctio->u.status1.flags &= + ~__constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_0); + ctio->u.status1.flags |= + __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1); + ctio->u.status1.sense_length = 0; + memset(ctio->u.status1.sense_data, 0, + sizeof(ctio->u.status1.sense_data)); + } + + /* Sense with len > 24, is it possible ??? */ +} + + + +/* diff */ +static inline int +qlt_hba_err_chk_enabled(struct se_cmd *se_cmd) +{ + /* + * Uncomment when corresponding SCSI changes are done. + * + if (!sp->cmd->prot_chk) + return 0; + * + */ + switch (se_cmd->prot_op) { + case TARGET_PROT_DOUT_INSERT: + case TARGET_PROT_DIN_STRIP: + if (ql2xenablehba_err_chk >= 1) + return 1; + break; + case TARGET_PROT_DOUT_PASS: + case TARGET_PROT_DIN_PASS: + if (ql2xenablehba_err_chk >= 2) + return 1; + break; + case TARGET_PROT_DIN_INSERT: + case TARGET_PROT_DOUT_STRIP: + return 1; + default: + break; + } + return 0; +} + +/* + * qla24xx_set_t10dif_tags_from_cmd - Extract Ref and App tags from SCSI command + * + */ +static inline void +qlt_set_t10dif_tags(struct se_cmd *se_cmd, struct crc_context *ctx) +{ + uint32_t lba = 0xffffffff & se_cmd->t_task_lba; + + /* wait til Mode Sense/Select cmd, modepage Ah, subpage 2 + * have been immplemented by TCM, before AppTag is avail. + * Look for modesense_handlers[] + */ + ctx->app_tag = 0; + ctx->app_tag_mask[0] = 0x0; + ctx->app_tag_mask[1] = 0x0; + + switch (se_cmd->prot_type) { + case TARGET_DIF_TYPE0_PROT: + /* + * No check for ql2xenablehba_err_chk, as it would be an + * I/O error if hba tag generation is not done. + */ + ctx->ref_tag = cpu_to_le32(lba); + + if (!qlt_hba_err_chk_enabled(se_cmd)) + break; + + /* enable ALL bytes of the ref tag */ + ctx->ref_tag_mask[0] = 0xff; + ctx->ref_tag_mask[1] = 0xff; + ctx->ref_tag_mask[2] = 0xff; + ctx->ref_tag_mask[3] = 0xff; + break; + /* + * For TYpe 1 protection: 16 bit GUARD tag, 32 bit REF tag, and + * 16 bit app tag. + */ + case TARGET_DIF_TYPE1_PROT: + ctx->ref_tag = cpu_to_le32(lba); + + if (!qlt_hba_err_chk_enabled(se_cmd)) + break; + + /* enable ALL bytes of the ref tag */ + ctx->ref_tag_mask[0] = 0xff; + ctx->ref_tag_mask[1] = 0xff; + ctx->ref_tag_mask[2] = 0xff; + ctx->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 TARGET_DIF_TYPE2_PROT: + ctx->ref_tag = cpu_to_le32(lba); + + if (!qlt_hba_err_chk_enabled(se_cmd)) + break; + + /* enable ALL bytes of the ref tag */ + ctx->ref_tag_mask[0] = 0xff; + ctx->ref_tag_mask[1] = 0xff; + ctx->ref_tag_mask[2] = 0xff; + ctx->ref_tag_mask[3] = 0xff; + break; + + /* For Type 3 protection: 16 bit GUARD only */ + case TARGET_DIF_TYPE3_PROT: + ctx->ref_tag_mask[0] = ctx->ref_tag_mask[1] = + ctx->ref_tag_mask[2] = ctx->ref_tag_mask[3] = 0x00; + break; + } +} + + +static inline int +qlt_build_ctio_crc2_pkt(struct qla_tgt_prm *prm, scsi_qla_host_t *vha) +{ + uint32_t *cur_dsd; + int sgc; + uint32_t transfer_length = 0; + uint32_t data_bytes; + uint32_t dif_bytes; + uint8_t bundling = 1; + uint8_t *clr_ptr; + struct crc_context *crc_ctx_pkt = NULL; + struct qla_hw_data *ha; + struct ctio_crc2_to_fw *pkt; + dma_addr_t crc_ctx_dma; + uint16_t fw_prot_opts = 0; + struct qla_tgt_cmd *cmd = prm->cmd; + struct se_cmd *se_cmd = &cmd->se_cmd; + uint32_t h; + struct atio_from_isp *atio = &prm->cmd->atio; + uint16_t t16; + + sgc = 0; + ha = vha->hw; + + pkt = (struct ctio_crc2_to_fw *)vha->req->ring_ptr; + prm->pkt = pkt; + memset(pkt, 0, sizeof(*pkt)); + + ql_dbg(ql_dbg_tgt, vha, 0xe071, + "qla_target(%d):%s: se_cmd[%p] CRC2 prot_op[0x%x] cmd prot sg:cnt[%p:%x] lba[%llu]\n", + vha->vp_idx, __func__, se_cmd, se_cmd->prot_op, + prm->prot_sg, prm->prot_seg_cnt, se_cmd->t_task_lba); + + if ((se_cmd->prot_op == TARGET_PROT_DIN_INSERT) || + (se_cmd->prot_op == TARGET_PROT_DOUT_STRIP)) + bundling = 0; + + /* Compute dif len and adjust data len to incude protection */ + data_bytes = cmd->bufflen; + dif_bytes = (data_bytes / cmd->blk_sz) * 8; + + switch (se_cmd->prot_op) { + case TARGET_PROT_DIN_INSERT: + case TARGET_PROT_DOUT_STRIP: + transfer_length = data_bytes; + data_bytes += dif_bytes; + break; + + case TARGET_PROT_DIN_STRIP: + case TARGET_PROT_DOUT_INSERT: + case TARGET_PROT_DIN_PASS: + case TARGET_PROT_DOUT_PASS: + transfer_length = data_bytes + dif_bytes; + break; + + default: + BUG(); + break; + } + + if (!qlt_hba_err_chk_enabled(se_cmd)) + fw_prot_opts |= 0x10; /* Disable Guard tag checking */ + /* HBA error checking enabled */ + else if (IS_PI_UNINIT_CAPABLE(ha)) { + if ((se_cmd->prot_type == TARGET_DIF_TYPE1_PROT) || + (se_cmd->prot_type == TARGET_DIF_TYPE2_PROT)) + fw_prot_opts |= PO_DIS_VALD_APP_ESC; + else if (se_cmd->prot_type == TARGET_DIF_TYPE3_PROT) + fw_prot_opts |= PO_DIS_VALD_APP_REF_ESC; + } + + switch (se_cmd->prot_op) { + case TARGET_PROT_DIN_INSERT: + case TARGET_PROT_DOUT_INSERT: + fw_prot_opts |= PO_MODE_DIF_INSERT; + break; + case TARGET_PROT_DIN_STRIP: + case TARGET_PROT_DOUT_STRIP: + fw_prot_opts |= PO_MODE_DIF_REMOVE; + break; + case TARGET_PROT_DIN_PASS: + case TARGET_PROT_DOUT_PASS: + fw_prot_opts |= PO_MODE_DIF_PASS; + /* FUTURE: does tcm require T10CRC<->IPCKSUM conversion? */ + break; + default:/* Normal Request */ + fw_prot_opts |= PO_MODE_DIF_PASS; + break; + } + + + /* ---- PKT ---- */ + /* Update entry type to indicate Command Type CRC_2 IOCB */ + pkt->entry_type = CTIO_CRC2; + pkt->entry_count = 1; + pkt->vp_index = vha->vp_idx; + + h = qlt_make_handle(vha); + if (unlikely(h == QLA_TGT_NULL_HANDLE)) { + /* + * CTIO type 7 from the firmware doesn't provide a way to + * know the initiator's LOOP ID, hence we can't find + * the session and, so, the command. + */ + return -EAGAIN; + } else + ha->tgt.cmds[h-1] = prm->cmd; + + + pkt->handle = h | CTIO_COMPLETION_HANDLE_MARK; + pkt->nport_handle = prm->cmd->loop_id; + pkt->timeout = __constant_cpu_to_le16(QLA_TGT_TIMEOUT); + pkt->initiator_id[0] = atio->u.isp24.fcp_hdr.s_id[2]; + pkt->initiator_id[1] = atio->u.isp24.fcp_hdr.s_id[1]; + pkt->initiator_id[2] = atio->u.isp24.fcp_hdr.s_id[0]; + pkt->exchange_addr = atio->u.isp24.exchange_addr; + + /* silence compile warning */ + t16 = be16_to_cpu(atio->u.isp24.fcp_hdr.ox_id); + pkt->ox_id = cpu_to_le16(t16); + + t16 = (atio->u.isp24.attr << 9); + pkt->flags |= cpu_to_le16(t16); + pkt->relative_offset = cpu_to_le32(prm->cmd->offset); + + /* Set transfer direction */ + if (cmd->dma_data_direction == DMA_TO_DEVICE) + pkt->flags = __constant_cpu_to_le16(CTIO7_FLAGS_DATA_IN); + else if (cmd->dma_data_direction == DMA_FROM_DEVICE) + pkt->flags = __constant_cpu_to_le16(CTIO7_FLAGS_DATA_OUT); + + + pkt->dseg_count = prm->tot_dsds; + /* Fibre channel byte count */ + pkt->transfer_length = cpu_to_le32(transfer_length); + + + /* ----- CRC context -------- */ + + /* Allocate CRC context from global pool */ + crc_ctx_pkt = cmd->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; + INIT_LIST_HEAD(&crc_ctx_pkt->dsd_list); + + /* Set handle */ + crc_ctx_pkt->handle = pkt->handle; + + qlt_set_t10dif_tags(se_cmd, crc_ctx_pkt); + + pkt->crc_context_address[0] = cpu_to_le32(LSD(crc_ctx_dma)); + pkt->crc_context_address[1] = cpu_to_le32(MSD(crc_ctx_dma)); + pkt->crc_context_len = CRC_CONTEXT_LEN_FW; + + + 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(prm->tot_dsds - prm->prot_seg_cnt); + 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(cmd->blk_sz); + 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); + + + /* Walks data segments */ + pkt->flags |= __constant_cpu_to_le16(CTIO7_FLAGS_DSD_PTR); + + if (!bundling && prm->prot_seg_cnt) { + if (qla24xx_walk_and_build_sglist_no_difb(ha, NULL, cur_dsd, + prm->tot_dsds, cmd)) + goto crc_queuing_error; + } else if (qla24xx_walk_and_build_sglist(ha, NULL, cur_dsd, + (prm->tot_dsds - prm->prot_seg_cnt), cmd)) + goto crc_queuing_error; + + if (bundling && prm->prot_seg_cnt) { + /* Walks dif segments */ + pkt->add_flags |= CTIO_CRC2_AF_DIF_DSD_ENA; + + cur_dsd = (uint32_t *) &crc_ctx_pkt->u.bundling.dif_address; + if (qla24xx_walk_and_build_prot_sglist(ha, NULL, cur_dsd, + prm->prot_seg_cnt, cmd)) + goto crc_queuing_error; + } + return QLA_SUCCESS; + +crc_queuing_error: + /* Cleanup will be performed by the caller */ + + return QLA_FUNCTION_FAILED; +} + + +/* + * Callback to setup response of xmit_type of QLA_TGT_XMIT_DATA and * + * QLA_TGT_XMIT_STATUS for >= 24xx silicon + */ +int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, + uint8_t scsi_status) +{ + struct scsi_qla_host *vha = cmd->vha; + struct qla_hw_data *ha = vha->hw; + struct ctio7_to_24xx *pkt; + struct qla_tgt_prm prm; + uint32_t full_req_cnt = 0; + unsigned long flags = 0; + int res; + + memset(&prm, 0, sizeof(prm)); + qlt_check_srr_debug(cmd, &xmit_type); + + ql_dbg(ql_dbg_tgt, cmd->vha, 0xe018, + "is_send_status=%d, cmd->bufflen=%d, cmd->sg_cnt=%d, cmd->dma_data_direction=%d se_cmd[%p]\n", + (xmit_type & QLA_TGT_XMIT_STATUS) ? + 1 : 0, cmd->bufflen, cmd->sg_cnt, cmd->dma_data_direction, + &cmd->se_cmd); + + res = qlt_pre_xmit_response(cmd, &prm, xmit_type, scsi_status, + &full_req_cnt); + if (unlikely(res != 0)) { + if (res == QLA_TGT_PRE_XMIT_RESP_CMD_ABORTED) + return 0; + + return res; + } + + spin_lock_irqsave(&ha->hardware_lock, flags); + + /* Does F/W have an IOCBs for this request */ + res = qlt_check_reserve_free_req(vha, full_req_cnt); + if (unlikely(res)) + goto out_unmap_unlock; + + if (cmd->se_cmd.prot_op && (xmit_type & QLA_TGT_XMIT_DATA)) + res = qlt_build_ctio_crc2_pkt(&prm, vha); + else + res = qlt_24xx_build_ctio_pkt(&prm, vha); + if (unlikely(res != 0)) + goto out_unmap_unlock; + + + pkt = (struct ctio7_to_24xx *)prm.pkt; + + if (qlt_has_data(cmd) && (xmit_type & QLA_TGT_XMIT_DATA)) { + pkt->u.status0.flags |= + __constant_cpu_to_le16(CTIO7_FLAGS_DATA_IN | + CTIO7_FLAGS_STATUS_MODE_0); + + if (cmd->se_cmd.prot_op == TARGET_PROT_NORMAL) + qlt_load_data_segments(&prm, vha); + + if (prm.add_status_pkt == 0) { + if (xmit_type & QLA_TGT_XMIT_STATUS) { + pkt->u.status0.scsi_status = + cpu_to_le16(prm.rq_result); + pkt->u.status0.residual = + cpu_to_le32(prm.residual); + pkt->u.status0.flags |= __constant_cpu_to_le16( + CTIO7_FLAGS_SEND_STATUS); + if (qlt_need_explicit_conf(ha, cmd, 0)) { + pkt->u.status0.flags |= + __constant_cpu_to_le16( + CTIO7_FLAGS_EXPLICIT_CONFORM | + CTIO7_FLAGS_CONFORM_REQ); + } + } + + } else { + /* + * We have already made sure that there is sufficient + * amount of request entries to not drop HW lock in + * req_pkt(). + */ + struct ctio7_to_24xx *ctio = + (struct ctio7_to_24xx *)qlt_get_req_pkt(vha); + + ql_dbg(ql_dbg_tgt, vha, 0xe019, + "Building additional status packet\n"); + + /* + * T10Dif: ctio_crc2_to_fw overlay ontop of + * ctio7_to_24xx + */ + memcpy(ctio, pkt, sizeof(*ctio)); + /* reset back to CTIO7 */ + ctio->entry_count = 1; + ctio->entry_type = CTIO_TYPE7; + ctio->dseg_count = 0; + ctio->u.status1.flags &= ~__constant_cpu_to_le16( + CTIO7_FLAGS_DATA_IN); + + /* Real finish is ctio_m1's finish */ + pkt->handle |= CTIO_INTERMEDIATE_HANDLE_MARK; + pkt->u.status0.flags |= __constant_cpu_to_le16( + CTIO7_FLAGS_DONT_RET_CTIO); + + /* qlt_24xx_init_ctio_to_isp will correct + * all neccessary fields that's part of CTIO7. + * There should be no residual of CTIO-CRC2 data. + */ + qlt_24xx_init_ctio_to_isp((struct ctio7_to_24xx *)ctio, + &prm); + pr_debug("Status CTIO7: %p\n", ctio); + } + } else + qlt_24xx_init_ctio_to_isp(pkt, &prm); + + + cmd->state = QLA_TGT_STATE_PROCESSED; /* Mid-level is done processing */ + + ql_dbg(ql_dbg_tgt, vha, 0xe01a, + "Xmitting CTIO7 response pkt for 24xx: %p scsi_status: 0x%02x\n", + pkt, scsi_status); + + qla2x00_start_iocbs(vha, vha->req); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return 0; + +out_unmap_unlock: + if (cmd->sg_mapped) + qlt_unmap_sg(vha, cmd); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return res; +} +EXPORT_SYMBOL(qlt_xmit_response); + +int qlt_rdy_to_xfer(struct qla_tgt_cmd *cmd) +{ + struct ctio7_to_24xx *pkt; + struct scsi_qla_host *vha = cmd->vha; + struct qla_hw_data *ha = vha->hw; + struct qla_tgt *tgt = cmd->tgt; + struct qla_tgt_prm prm; + unsigned long flags; + int res = 0; + + memset(&prm, 0, sizeof(prm)); + prm.cmd = cmd; + prm.tgt = tgt; + prm.sg = NULL; + prm.req_cnt = 1; + + /* Send marker if required */ + if (qlt_issue_marker(vha, 0) != QLA_SUCCESS) + return -EIO; + + ql_dbg(ql_dbg_tgt, vha, 0xe01b, + "%s: CTIO_start: vha(%d) se_cmd %p ox_id %04x\n", + __func__, (int)vha->vp_idx, &cmd->se_cmd, + be16_to_cpu(cmd->atio.u.isp24.fcp_hdr.ox_id)); + + /* Calculate number of entries and segments required */ + if (qlt_pci_map_calc_cnt(&prm) != 0) + return -EAGAIN; + + spin_lock_irqsave(&ha->hardware_lock, flags); + + /* Does F/W have an IOCBs for this request */ + res = qlt_check_reserve_free_req(vha, prm.req_cnt); + if (res != 0) + goto out_unlock_free_unmap; + if (cmd->se_cmd.prot_op) + res = qlt_build_ctio_crc2_pkt(&prm, vha); + else + res = qlt_24xx_build_ctio_pkt(&prm, vha); + + if (unlikely(res != 0)) + goto out_unlock_free_unmap; + pkt = (struct ctio7_to_24xx *)prm.pkt; + pkt->u.status0.flags |= __constant_cpu_to_le16(CTIO7_FLAGS_DATA_OUT | + CTIO7_FLAGS_STATUS_MODE_0); + + if (cmd->se_cmd.prot_op == TARGET_PROT_NORMAL) + qlt_load_data_segments(&prm, vha); + + cmd->state = QLA_TGT_STATE_NEED_DATA; + + qla2x00_start_iocbs(vha, vha->req); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return res; + +out_unlock_free_unmap: + if (cmd->sg_mapped) + qlt_unmap_sg(vha, cmd); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return res; +} +EXPORT_SYMBOL(qlt_rdy_to_xfer); + + +/* + * Checks the guard or meta-data for the type of error + * detected by the HBA. + */ +static inline int +qlt_handle_dif_error(struct scsi_qla_host *vha, struct qla_tgt_cmd *cmd, + struct ctio_crc_from_fw *sts) +{ + uint8_t *ap = &sts->actual_dif[0]; + uint8_t *ep = &sts->expected_dif[0]; + uint32_t e_ref_tag, a_ref_tag; + uint16_t e_app_tag, a_app_tag; + uint16_t e_guard, a_guard; + uint64_t lba = cmd->se_cmd.t_task_lba; + + a_guard = be16_to_cpu(*(uint16_t *)(ap + 0)); + a_app_tag = be16_to_cpu(*(uint16_t *)(ap + 2)); + a_ref_tag = be32_to_cpu(*(uint32_t *)(ap + 4)); + + e_guard = be16_to_cpu(*(uint16_t *)(ep + 0)); + e_app_tag = be16_to_cpu(*(uint16_t *)(ep + 2)); + e_ref_tag = be32_to_cpu(*(uint32_t *)(ep + 4)); + + ql_dbg(ql_dbg_tgt, vha, 0xe075, + "iocb(s) %p Returned STATUS.\n", sts); + + ql_dbg(ql_dbg_tgt, vha, 0xf075, + "dif check TGT cdb 0x%x lba 0x%llu: [Actual|Expected] Ref Tag[0x%x|0x%x], App Tag [0x%x|0x%x], Guard [0x%x|0x%x]\n", + cmd->atio.u.isp24.fcp_cmnd.cdb[0], lba, + 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) && + ((cmd->se_cmd.prot_type != TARGET_DIF_TYPE3_PROT) || + (a_ref_tag == 0xffffffff))) { + uint32_t blocks_done; + + /* 2TB boundary case covered automatically with this */ + blocks_done = e_ref_tag - (uint32_t)lba + 1; + cmd->se_cmd.bad_sector = e_ref_tag; + cmd->se_cmd.pi_err = 0; + ql_dbg(ql_dbg_tgt, vha, 0xf074, + "need to return scsi good\n"); + + /* Update protection tag */ + if (cmd->prot_sg_cnt) { + uint32_t i, j = 0, k = 0, num_ent; + struct scatterlist *sg, *sgl; + + + sgl = cmd->prot_sg; + + /* Patch the corresponding protection tags */ + for_each_sg(sgl, sg, cmd->prot_sg_cnt, 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, 0xf076, + "unexpected tag values tag:lba=%u:%llu)\n", + e_ref_tag, (unsigned long long)lba); + goto out; + } + +#if 0 + struct sd_dif_tuple *spt; + /* TODO: + * This section came from initiator. Is it valid here? + * should ulp be override with actual val??? + */ + spt = page_address(sg_page(sg)) + sg->offset; + spt += j; + + spt->app_tag = 0xffff; + if (cmd->se_cmd.prot_type == SCSI_PROT_DIF_TYPE3) + spt->ref_tag = 0xffffffff; +#endif + } + + return 0; + } + + /* check guard */ + if (e_guard != a_guard) { + cmd->se_cmd.pi_err = TCM_LOGICAL_BLOCK_GUARD_CHECK_FAILED; + cmd->se_cmd.bad_sector = cmd->se_cmd.t_task_lba; + + ql_log(ql_log_warn, vha, 0xe076, + "Guard ERR: cdb 0x%x lba 0x%llx: [Actual|Expected] Ref Tag[0x%x|0x%x], App Tag [0x%x|0x%x], Guard [0x%x|0x%x] cmd=%p\n", + cmd->atio.u.isp24.fcp_cmnd.cdb[0], lba, + a_ref_tag, e_ref_tag, a_app_tag, e_app_tag, + a_guard, e_guard, cmd); + goto out; + } + + /* check ref tag */ + if (e_ref_tag != a_ref_tag) { + cmd->se_cmd.pi_err = TCM_LOGICAL_BLOCK_REF_TAG_CHECK_FAILED; + cmd->se_cmd.bad_sector = e_ref_tag; + + ql_log(ql_log_warn, vha, 0xe077, + "Ref Tag ERR: cdb 0x%x lba 0x%llx: [Actual|Expected] Ref Tag[0x%x|0x%x], App Tag [0x%x|0x%x], Guard [0x%x|0x%x] cmd=%p\n", + cmd->atio.u.isp24.fcp_cmnd.cdb[0], lba, + a_ref_tag, e_ref_tag, a_app_tag, e_app_tag, + a_guard, e_guard, cmd); + goto out; + } + + /* check appl tag */ + if (e_app_tag != a_app_tag) { + cmd->se_cmd.pi_err = TCM_LOGICAL_BLOCK_APP_TAG_CHECK_FAILED; + cmd->se_cmd.bad_sector = cmd->se_cmd.t_task_lba; + + ql_log(ql_log_warn, vha, 0xe078, + "App Tag ERR: cdb 0x%x lba 0x%llx: [Actual|Expected] Ref Tag[0x%x|0x%x], App Tag [0x%x|0x%x], Guard [0x%x|0x%x] cmd=%p\n", + cmd->atio.u.isp24.fcp_cmnd.cdb[0], lba, + a_ref_tag, e_ref_tag, a_app_tag, e_app_tag, + a_guard, e_guard, cmd); + goto out; + } +out: + return 1; +} + + +/* If hardware_lock held on entry, might drop it, then reaquire */ +/* This function sends the appropriate CTIO to ISP 2xxx or 24xx */ +static int __qlt_send_term_exchange(struct scsi_qla_host *vha, + struct qla_tgt_cmd *cmd, + struct atio_from_isp *atio) +{ + struct ctio7_to_24xx *ctio24; + struct qla_hw_data *ha = vha->hw; + request_t *pkt; + int ret = 0; + uint16_t temp; + + ql_dbg(ql_dbg_tgt, vha, 0xe01c, "Sending TERM EXCH CTIO (ha=%p)\n", ha); + + pkt = (request_t *)qla2x00_alloc_iocbs(vha, NULL); + if (pkt == NULL) { + ql_dbg(ql_dbg_tgt, vha, 0xe050, + "qla_target(%d): %s failed: unable to allocate " + "request packet\n", vha->vp_idx, __func__); + return -ENOMEM; + } + + if (cmd != NULL) { + if (cmd->state < QLA_TGT_STATE_PROCESSED) { + ql_dbg(ql_dbg_tgt, vha, 0xe051, + "qla_target(%d): Terminating cmd %p with " + "incorrect state %d\n", vha->vp_idx, cmd, + cmd->state); + } else + ret = 1; + } + + pkt->entry_count = 1; + pkt->handle = QLA_TGT_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; + + ctio24 = (struct ctio7_to_24xx *)pkt; + ctio24->entry_type = CTIO_TYPE7; + ctio24->nport_handle = cmd ? cmd->loop_id : CTIO7_NHANDLE_UNRECOGNIZED; + ctio24->timeout = __constant_cpu_to_le16(QLA_TGT_TIMEOUT); + ctio24->vp_index = vha->vp_idx; + ctio24->initiator_id[0] = atio->u.isp24.fcp_hdr.s_id[2]; + ctio24->initiator_id[1] = atio->u.isp24.fcp_hdr.s_id[1]; + ctio24->initiator_id[2] = atio->u.isp24.fcp_hdr.s_id[0]; + ctio24->exchange_addr = atio->u.isp24.exchange_addr; + ctio24->u.status1.flags = (atio->u.isp24.attr << 9) | + __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1 | + CTIO7_FLAGS_TERMINATE); + temp = be16_to_cpu(atio->u.isp24.fcp_hdr.ox_id); + ctio24->u.status1.ox_id = cpu_to_le16(temp); + + /* Most likely, it isn't needed */ + ctio24->u.status1.residual = get_unaligned((uint32_t *) + &atio->u.isp24.fcp_cmnd.add_cdb[ + atio->u.isp24.fcp_cmnd.add_cdb_len]); + if (ctio24->u.status1.residual != 0) + ctio24->u.status1.scsi_status |= SS_RESIDUAL_UNDER; + + qla2x00_start_iocbs(vha, vha->req); + return ret; +} + +static void qlt_send_term_exchange(struct scsi_qla_host *vha, + struct qla_tgt_cmd *cmd, struct atio_from_isp *atio, int ha_locked) +{ + unsigned long flags; + int rc; + + if (qlt_issue_marker(vha, ha_locked) < 0) + return; + + if (ha_locked) { + rc = __qlt_send_term_exchange(vha, cmd, atio); + goto done; + } + spin_lock_irqsave(&vha->hw->hardware_lock, flags); + rc = __qlt_send_term_exchange(vha, cmd, atio); + spin_unlock_irqrestore(&vha->hw->hardware_lock, flags); +done: + /* + * Terminate exchange will tell fw to release any active CTIO + * that's in FW posession and cleanup the exchange. + * + * "cmd->state == QLA_TGT_STATE_ABORTED" means CTIO is still + * down at FW. Free the cmd later when CTIO comes back later + * w/aborted(0x2) status. + * + * "cmd->state != QLA_TGT_STATE_ABORTED" means CTIO is already + * back w/some err. Free the cmd now. + */ + if ((rc == 1) && (cmd->state != QLA_TGT_STATE_ABORTED)) { + if (!ha_locked && !in_interrupt()) + msleep(250); /* just in case */ + + if (cmd->sg_mapped) + qlt_unmap_sg(vha, cmd); + vha->hw->tgt.tgt_ops->free_cmd(cmd); + } + return; +} + +void qlt_free_cmd(struct qla_tgt_cmd *cmd) +{ + struct qla_tgt_sess *sess = cmd->sess; + + ql_dbg(ql_dbg_tgt, cmd->vha, 0xe074, + "%s: se_cmd[%p] ox_id %04x\n", + __func__, &cmd->se_cmd, + be16_to_cpu(cmd->atio.u.isp24.fcp_hdr.ox_id)); + + BUG_ON(cmd->sg_mapped); + if (unlikely(cmd->free_sg)) + kfree(cmd->sg); + + if (!sess || !sess->se_sess) { + WARN_ON(1); + return; + } + percpu_ida_free(&sess->se_sess->sess_tag_pool, cmd->se_cmd.map_tag); +} +EXPORT_SYMBOL(qlt_free_cmd); + +/* ha->hardware_lock supposed to be held on entry */ +static int qlt_prepare_srr_ctio(struct scsi_qla_host *vha, + struct qla_tgt_cmd *cmd, void *ctio) +{ + struct qla_tgt_srr_ctio *sc; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; + struct qla_tgt_srr_imm *imm; + + tgt->ctio_srr_id++; + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf019, + "qla_target(%d): CTIO with SRR status received\n", vha->vp_idx); + + if (!ctio) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf055, + "qla_target(%d): SRR CTIO, but ctio is NULL\n", + vha->vp_idx); + return -EINVAL; + } + + sc = kzalloc(sizeof(*sc), GFP_ATOMIC); + if (sc != NULL) { + sc->cmd = cmd; + /* IRQ is already OFF */ + spin_lock(&tgt->srr_lock); + sc->srr_id = tgt->ctio_srr_id; + list_add_tail(&sc->srr_list_entry, + &tgt->srr_ctio_list); + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01a, + "CTIO SRR %p added (id %d)\n", sc, sc->srr_id); + if (tgt->imm_srr_id == tgt->ctio_srr_id) { + int found = 0; + list_for_each_entry(imm, &tgt->srr_imm_list, + srr_list_entry) { + if (imm->srr_id == sc->srr_id) { + found = 1; + break; + } + } + if (found) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01b, + "Scheduling srr work\n"); + schedule_work(&tgt->srr_work); + } else { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf056, + "qla_target(%d): imm_srr_id " + "== ctio_srr_id (%d), but there is no " + "corresponding SRR IMM, deleting CTIO " + "SRR %p\n", vha->vp_idx, + tgt->ctio_srr_id, sc); + list_del(&sc->srr_list_entry); + spin_unlock(&tgt->srr_lock); + + kfree(sc); + return -EINVAL; + } + } + spin_unlock(&tgt->srr_lock); + } else { + struct qla_tgt_srr_imm *ti; + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf057, + "qla_target(%d): Unable to allocate SRR CTIO entry\n", + vha->vp_idx); + spin_lock(&tgt->srr_lock); + list_for_each_entry_safe(imm, ti, &tgt->srr_imm_list, + srr_list_entry) { + if (imm->srr_id == tgt->ctio_srr_id) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01c, + "IMM SRR %p deleted (id %d)\n", + imm, imm->srr_id); + list_del(&imm->srr_list_entry); + qlt_reject_free_srr_imm(vha, imm, 1); + } + } + spin_unlock(&tgt->srr_lock); + + return -ENOMEM; + } + + return 0; +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static int qlt_term_ctio_exchange(struct scsi_qla_host *vha, void *ctio, + struct qla_tgt_cmd *cmd, uint32_t status) +{ + int term = 0; + + if (ctio != NULL) { + struct ctio7_from_24xx *c = (struct ctio7_from_24xx *)ctio; + term = !(c->flags & + __constant_cpu_to_le16(OF_TERM_EXCH)); + } else + term = 1; + + if (term) + qlt_send_term_exchange(vha, cmd, &cmd->atio, 1); + + return term; +} + +/* ha->hardware_lock supposed to be held on entry */ +static inline struct qla_tgt_cmd *qlt_get_cmd(struct scsi_qla_host *vha, + uint32_t handle) +{ + struct qla_hw_data *ha = vha->hw; + + handle--; + if (ha->tgt.cmds[handle] != NULL) { + struct qla_tgt_cmd *cmd = ha->tgt.cmds[handle]; + ha->tgt.cmds[handle] = NULL; + return cmd; + } else + return NULL; +} + +/* ha->hardware_lock supposed to be held on entry */ +static struct qla_tgt_cmd *qlt_ctio_to_cmd(struct scsi_qla_host *vha, + uint32_t handle, void *ctio) +{ + struct qla_tgt_cmd *cmd = NULL; + + /* Clear out internal marks */ + handle &= ~(CTIO_COMPLETION_HANDLE_MARK | + CTIO_INTERMEDIATE_HANDLE_MARK); + + if (handle != QLA_TGT_NULL_HANDLE) { + if (unlikely(handle == QLA_TGT_SKIP_HANDLE)) { + ql_dbg(ql_dbg_tgt, vha, 0xe01d, "%s", + "SKIP_HANDLE CTIO\n"); + return NULL; + } + /* handle-1 is actually used */ + if (unlikely(handle > DEFAULT_OUTSTANDING_COMMANDS)) { + ql_dbg(ql_dbg_tgt, vha, 0xe052, + "qla_target(%d): Wrong handle %x received\n", + vha->vp_idx, handle); + return NULL; + } + cmd = qlt_get_cmd(vha, handle); + if (unlikely(cmd == NULL)) { + ql_dbg(ql_dbg_tgt, vha, 0xe053, + "qla_target(%d): Suspicious: unable to " + "find the command with handle %x\n", vha->vp_idx, + handle); + return NULL; + } + } else if (ctio != NULL) { + /* We can't get loop ID from CTIO7 */ + ql_dbg(ql_dbg_tgt, vha, 0xe054, + "qla_target(%d): Wrong CTIO received: QLA24xx doesn't " + "support NULL handles\n", vha->vp_idx); + return NULL; + } + + return cmd; +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle, + uint32_t status, void *ctio) +{ + struct qla_hw_data *ha = vha->hw; + struct se_cmd *se_cmd; + struct target_core_fabric_ops *tfo; + struct qla_tgt_cmd *cmd; + + ql_dbg(ql_dbg_tgt, vha, 0xe01e, + "qla_target(%d): handle(ctio %p status %#x) <- %08x\n", + vha->vp_idx, ctio, status, handle); + + if (handle & CTIO_INTERMEDIATE_HANDLE_MARK) { + /* That could happen only in case of an error/reset/abort */ + if (status != CTIO_SUCCESS) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01d, + "Intermediate CTIO received" + " (status %x)\n", status); + } + return; + } + + cmd = qlt_ctio_to_cmd(vha, handle, ctio); + if (cmd == NULL) + return; + + se_cmd = &cmd->se_cmd; + tfo = se_cmd->se_tfo; + + if (cmd->sg_mapped) + qlt_unmap_sg(vha, cmd); + + if (unlikely(status != CTIO_SUCCESS)) { + switch (status & 0xFFFF) { + case CTIO_LIP_RESET: + case CTIO_TARGET_RESET: + case CTIO_ABORTED: + /* driver request abort via Terminate exchange */ + case CTIO_TIMEOUT: + case CTIO_INVALID_RX_ID: + /* They are OK */ + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf058, + "qla_target(%d): CTIO with " + "status %#x received, state %x, se_cmd %p, " + "(LIP_RESET=e, ABORTED=2, TARGET_RESET=17, " + "TIMEOUT=b, INVALID_RX_ID=8)\n", vha->vp_idx, + status, cmd->state, se_cmd); + break; + + case CTIO_PORT_LOGGED_OUT: + case CTIO_PORT_UNAVAILABLE: + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf059, + "qla_target(%d): CTIO with PORT LOGGED " + "OUT (29) or PORT UNAVAILABLE (28) status %x " + "received (state %x, se_cmd %p)\n", vha->vp_idx, + status, cmd->state, se_cmd); + break; + + case CTIO_SRR_RECEIVED: + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05a, + "qla_target(%d): CTIO with SRR_RECEIVED" + " status %x received (state %x, se_cmd %p)\n", + vha->vp_idx, status, cmd->state, se_cmd); + if (qlt_prepare_srr_ctio(vha, cmd, ctio) != 0) + break; + else + return; + + case CTIO_DIF_ERROR: { + struct ctio_crc_from_fw *crc = + (struct ctio_crc_from_fw *)ctio; + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf073, + "qla_target(%d): CTIO with DIF_ERROR status %x received (state %x, se_cmd %p) actual_dif[0x%llx] expect_dif[0x%llx]\n", + vha->vp_idx, status, cmd->state, se_cmd, + *((u64 *)&crc->actual_dif[0]), + *((u64 *)&crc->expected_dif[0])); + + if (qlt_handle_dif_error(vha, cmd, ctio)) { + if (cmd->state == QLA_TGT_STATE_NEED_DATA) { + /* scsi Write/xfer rdy complete */ + goto skip_term; + } else { + /* scsi read/xmit respond complete + * call handle dif to send scsi status + * rather than terminate exchange. + */ + cmd->state = QLA_TGT_STATE_PROCESSED; + ha->tgt.tgt_ops->handle_dif_err(cmd); + return; + } + } else { + /* Need to generate a SCSI good completion. + * because FW did not send scsi status. + */ + status = 0; + goto skip_term; + } + break; + } + default: + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05b, + "qla_target(%d): CTIO with error status 0x%x received (state %x, se_cmd %p\n", + vha->vp_idx, status, cmd->state, se_cmd); + break; + } + + + /* "cmd->state == QLA_TGT_STATE_ABORTED" means + * cmd is already aborted/terminated, we don't + * need to terminate again. The exchange is already + * cleaned up/freed at FW level. Just cleanup at driver + * level. + */ + if ((cmd->state != QLA_TGT_STATE_NEED_DATA) && + (cmd->state != QLA_TGT_STATE_ABORTED)) { + if (qlt_term_ctio_exchange(vha, ctio, cmd, status)) + return; + } + } +skip_term: + + if (cmd->state == QLA_TGT_STATE_PROCESSED) { + ql_dbg(ql_dbg_tgt, vha, 0xe01f, "Command %p finished\n", cmd); + } else if (cmd->state == QLA_TGT_STATE_NEED_DATA) { + int rx_status = 0; + + cmd->state = QLA_TGT_STATE_DATA_IN; + + if (unlikely(status != CTIO_SUCCESS)) + rx_status = -EIO; + else + cmd->write_data_transferred = 1; + + ql_dbg(ql_dbg_tgt, vha, 0xe020, + "Data received, context %x, rx_status %d\n", + 0x0, rx_status); + + ha->tgt.tgt_ops->handle_data(cmd); + return; + } else if (cmd->state == QLA_TGT_STATE_ABORTED) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01e, + "Aborted command %p (tag %d) finished\n", cmd, cmd->tag); + } else { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05c, + "qla_target(%d): A command in state (%d) should " + "not return a CTIO complete\n", vha->vp_idx, cmd->state); + } + + if (unlikely(status != CTIO_SUCCESS) && + (cmd->state != QLA_TGT_STATE_ABORTED)) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf01f, "Finishing failed CTIO\n"); + dump_stack(); + } + + ha->tgt.tgt_ops->free_cmd(cmd); +} + +static inline int qlt_get_fcp_task_attr(struct scsi_qla_host *vha, + uint8_t task_codes) +{ + int fcp_task_attr; + + switch (task_codes) { + case ATIO_SIMPLE_QUEUE: + fcp_task_attr = MSG_SIMPLE_TAG; + break; + case ATIO_HEAD_OF_QUEUE: + fcp_task_attr = MSG_HEAD_TAG; + break; + case ATIO_ORDERED_QUEUE: + fcp_task_attr = MSG_ORDERED_TAG; + break; + case ATIO_ACA_QUEUE: + fcp_task_attr = MSG_ACA_TAG; + break; + case ATIO_UNTAGGED: + fcp_task_attr = MSG_SIMPLE_TAG; + break; + default: + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05d, + "qla_target: unknown task code %x, use ORDERED instead\n", + task_codes); + fcp_task_attr = MSG_ORDERED_TAG; + break; + } + + return fcp_task_attr; +} + +static struct qla_tgt_sess *qlt_make_local_sess(struct scsi_qla_host *, + uint8_t *); +/* + * Process context for I/O path into tcm_qla2xxx code + */ +static void __qlt_do_work(struct qla_tgt_cmd *cmd) +{ + scsi_qla_host_t *vha = cmd->vha; + struct qla_hw_data *ha = vha->hw; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; + struct qla_tgt_sess *sess = cmd->sess; + struct atio_from_isp *atio = &cmd->atio; + unsigned char *cdb; + unsigned long flags; + uint32_t data_length; + int ret, fcp_task_attr, data_dir, bidi = 0; + + if (tgt->tgt_stop) + goto out_term; + + cdb = &atio->u.isp24.fcp_cmnd.cdb[0]; + cmd->tag = atio->u.isp24.exchange_addr; + cmd->unpacked_lun = scsilun_to_int( + (struct scsi_lun *)&atio->u.isp24.fcp_cmnd.lun); + + if (atio->u.isp24.fcp_cmnd.rddata && + atio->u.isp24.fcp_cmnd.wrdata) { + bidi = 1; + data_dir = DMA_TO_DEVICE; + } else if (atio->u.isp24.fcp_cmnd.rddata) + data_dir = DMA_FROM_DEVICE; + else if (atio->u.isp24.fcp_cmnd.wrdata) + data_dir = DMA_TO_DEVICE; + else + data_dir = DMA_NONE; + + fcp_task_attr = qlt_get_fcp_task_attr(vha, + atio->u.isp24.fcp_cmnd.task_attr); + data_length = be32_to_cpu(get_unaligned((uint32_t *) + &atio->u.isp24.fcp_cmnd.add_cdb[ + atio->u.isp24.fcp_cmnd.add_cdb_len])); + + ql_dbg(ql_dbg_tgt, vha, 0xe022, + "qla_target: START qla cmd: %p se_cmd %p lun: 0x%04x (tag %d) len(%d) ox_id %x\n", + cmd, &cmd->se_cmd, cmd->unpacked_lun, cmd->tag, data_length, + cmd->atio.u.isp24.fcp_hdr.ox_id); + + ret = ha->tgt.tgt_ops->handle_cmd(vha, cmd, cdb, data_length, + fcp_task_attr, data_dir, bidi); + if (ret != 0) + goto out_term; + /* + * Drop extra session reference from qla_tgt_handle_cmd_for_atio*( + */ + spin_lock_irqsave(&ha->hardware_lock, flags); + ha->tgt.tgt_ops->put_sess(sess); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + return; + +out_term: + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf020, "Terminating work cmd %p", cmd); + /* + * cmd has not sent to target yet, so pass NULL as the second + * argument to qlt_send_term_exchange() and free the memory here. + */ + spin_lock_irqsave(&ha->hardware_lock, flags); + qlt_send_term_exchange(vha, NULL, &cmd->atio, 1); + percpu_ida_free(&sess->se_sess->sess_tag_pool, cmd->se_cmd.map_tag); + ha->tgt.tgt_ops->put_sess(sess); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + +static void qlt_do_work(struct work_struct *work) +{ + struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work); + + __qlt_do_work(cmd); +} + +static struct qla_tgt_cmd *qlt_get_tag(scsi_qla_host_t *vha, + struct qla_tgt_sess *sess, + struct atio_from_isp *atio) +{ + struct se_session *se_sess = sess->se_sess; + struct qla_tgt_cmd *cmd; + int tag; + + tag = percpu_ida_alloc(&se_sess->sess_tag_pool, TASK_RUNNING); + if (tag < 0) + return NULL; + + cmd = &((struct qla_tgt_cmd *)se_sess->sess_cmd_map)[tag]; + memset(cmd, 0, sizeof(struct qla_tgt_cmd)); + + memcpy(&cmd->atio, atio, sizeof(*atio)); + cmd->state = QLA_TGT_STATE_NEW; + cmd->tgt = vha->vha_tgt.qla_tgt; + cmd->vha = vha; + cmd->se_cmd.map_tag = tag; + cmd->sess = sess; + cmd->loop_id = sess->loop_id; + cmd->conf_compl_supported = sess->conf_compl_supported; + + return cmd; +} + +static void qlt_send_busy(struct scsi_qla_host *, struct atio_from_isp *, + uint16_t); + +static void qlt_create_sess_from_atio(struct work_struct *work) +{ + struct qla_tgt_sess_op *op = container_of(work, + struct qla_tgt_sess_op, work); + scsi_qla_host_t *vha = op->vha; + struct qla_hw_data *ha = vha->hw; + struct qla_tgt_sess *sess; + struct qla_tgt_cmd *cmd; + unsigned long flags; + uint8_t *s_id = op->atio.u.isp24.fcp_hdr.s_id; + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf022, + "qla_target(%d): Unable to find wwn login" + " (s_id %x:%x:%x), trying to create it manually\n", + vha->vp_idx, s_id[0], s_id[1], s_id[2]); + + if (op->atio.u.raw.entry_count > 1) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf023, + "Dropping multy entry atio %p\n", &op->atio); + goto out_term; + } + + mutex_lock(&vha->vha_tgt.tgt_mutex); + sess = qlt_make_local_sess(vha, s_id); + /* sess has an extra creation ref. */ + mutex_unlock(&vha->vha_tgt.tgt_mutex); + + if (!sess) + goto out_term; + /* + * Now obtain a pre-allocated session tag using the original op->atio + * packet header, and dispatch into __qlt_do_work() using the existing + * process context. + */ + cmd = qlt_get_tag(vha, sess, &op->atio); + if (!cmd) { + spin_lock_irqsave(&ha->hardware_lock, flags); + qlt_send_busy(vha, &op->atio, SAM_STAT_BUSY); + ha->tgt.tgt_ops->put_sess(sess); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + kfree(op); + return; + } + /* + * __qlt_do_work() will call ha->tgt.tgt_ops->put_sess() to release + * the extra reference taken above by qlt_make_local_sess() + */ + __qlt_do_work(cmd); + kfree(op); + return; + +out_term: + spin_lock_irqsave(&ha->hardware_lock, flags); + qlt_send_term_exchange(vha, NULL, &op->atio, 1); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + kfree(op); + +} + +/* ha->hardware_lock supposed to be held on entry */ +static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, + struct atio_from_isp *atio) +{ + struct qla_hw_data *ha = vha->hw; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; + struct qla_tgt_sess *sess; + struct qla_tgt_cmd *cmd; + + if (unlikely(tgt->tgt_stop)) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf021, + "New command while device %p is shutting down\n", tgt); + return -EFAULT; + } + + sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, atio->u.isp24.fcp_hdr.s_id); + if (unlikely(!sess)) { + struct qla_tgt_sess_op *op = kzalloc(sizeof(struct qla_tgt_sess_op), + GFP_ATOMIC); + if (!op) + return -ENOMEM; + + memcpy(&op->atio, atio, sizeof(*atio)); + INIT_WORK(&op->work, qlt_create_sess_from_atio); + queue_work(qla_tgt_wq, &op->work); + return 0; + } + /* + * Do kref_get() before returning + dropping qla_hw_data->hardware_lock. + */ + kref_get(&sess->se_sess->sess_kref); + + cmd = qlt_get_tag(vha, sess, atio); + if (!cmd) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05e, + "qla_target(%d): Allocation of cmd failed\n", vha->vp_idx); + ha->tgt.tgt_ops->put_sess(sess); + return -ENOMEM; + } + + INIT_WORK(&cmd->work, qlt_do_work); + queue_work(qla_tgt_wq, &cmd->work); + return 0; + +} + +/* ha->hardware_lock supposed to be held on entry */ +static int qlt_issue_task_mgmt(struct qla_tgt_sess *sess, uint32_t lun, + int fn, void *iocb, int flags) +{ + struct scsi_qla_host *vha = sess->vha; + struct qla_hw_data *ha = vha->hw; + struct qla_tgt_mgmt_cmd *mcmd; + int res; + uint8_t tmr_func; + + mcmd = mempool_alloc(qla_tgt_mgmt_cmd_mempool, GFP_ATOMIC); + if (!mcmd) { + ql_dbg(ql_dbg_tgt_tmr, vha, 0x10009, + "qla_target(%d): Allocation of management " + "command failed, some commands and their data could " + "leak\n", vha->vp_idx); + return -ENOMEM; + } + memset(mcmd, 0, sizeof(*mcmd)); + mcmd->sess = sess; + + if (iocb) { + memcpy(&mcmd->orig_iocb.imm_ntfy, iocb, + sizeof(mcmd->orig_iocb.imm_ntfy)); + } + mcmd->tmr_func = fn; + mcmd->flags = flags; + + switch (fn) { + case QLA_TGT_CLEAR_ACA: + ql_dbg(ql_dbg_tgt_tmr, vha, 0x10000, + "qla_target(%d): CLEAR_ACA received\n", sess->vha->vp_idx); + tmr_func = TMR_CLEAR_ACA; + break; + + case QLA_TGT_TARGET_RESET: + ql_dbg(ql_dbg_tgt_tmr, vha, 0x10001, + "qla_target(%d): TARGET_RESET received\n", + sess->vha->vp_idx); + tmr_func = TMR_TARGET_WARM_RESET; + break; + + case QLA_TGT_LUN_RESET: + ql_dbg(ql_dbg_tgt_tmr, vha, 0x10002, + "qla_target(%d): LUN_RESET received\n", sess->vha->vp_idx); + tmr_func = TMR_LUN_RESET; + break; + + case QLA_TGT_CLEAR_TS: + ql_dbg(ql_dbg_tgt_tmr, vha, 0x10003, + "qla_target(%d): CLEAR_TS received\n", sess->vha->vp_idx); + tmr_func = TMR_CLEAR_TASK_SET; + break; + + case QLA_TGT_ABORT_TS: + ql_dbg(ql_dbg_tgt_tmr, vha, 0x10004, + "qla_target(%d): ABORT_TS received\n", sess->vha->vp_idx); + tmr_func = TMR_ABORT_TASK_SET; + break; +#if 0 + case QLA_TGT_ABORT_ALL: + ql_dbg(ql_dbg_tgt_tmr, vha, 0x10005, + "qla_target(%d): Doing ABORT_ALL_TASKS\n", + sess->vha->vp_idx); + tmr_func = 0; + break; + + case QLA_TGT_ABORT_ALL_SESS: + ql_dbg(ql_dbg_tgt_tmr, vha, 0x10006, + "qla_target(%d): Doing ABORT_ALL_TASKS_SESS\n", + sess->vha->vp_idx); + tmr_func = 0; + break; + + case QLA_TGT_NEXUS_LOSS_SESS: + ql_dbg(ql_dbg_tgt_tmr, vha, 0x10007, + "qla_target(%d): Doing NEXUS_LOSS_SESS\n", + sess->vha->vp_idx); + tmr_func = 0; + break; + + case QLA_TGT_NEXUS_LOSS: + ql_dbg(ql_dbg_tgt_tmr, vha, 0x10008, + "qla_target(%d): Doing NEXUS_LOSS\n", sess->vha->vp_idx); + tmr_func = 0; + break; +#endif + default: + ql_dbg(ql_dbg_tgt_tmr, vha, 0x1000a, + "qla_target(%d): Unknown task mgmt fn 0x%x\n", + sess->vha->vp_idx, fn); + mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool); + return -ENOSYS; + } + + res = ha->tgt.tgt_ops->handle_tmr(mcmd, lun, tmr_func, 0); + if (res != 0) { + ql_dbg(ql_dbg_tgt_tmr, vha, 0x1000b, + "qla_target(%d): tgt.tgt_ops->handle_tmr() failed: %d\n", + sess->vha->vp_idx, res); + mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool); + return -EFAULT; + } + + return 0; +} + +/* ha->hardware_lock supposed to be held on entry */ +static int qlt_handle_task_mgmt(struct scsi_qla_host *vha, void *iocb) +{ + struct atio_from_isp *a = (struct atio_from_isp *)iocb; + struct qla_hw_data *ha = vha->hw; + struct qla_tgt *tgt; + struct qla_tgt_sess *sess; + uint32_t lun, unpacked_lun; + int lun_size, fn; + + tgt = vha->vha_tgt.qla_tgt; + + lun = a->u.isp24.fcp_cmnd.lun; + lun_size = sizeof(a->u.isp24.fcp_cmnd.lun); + fn = a->u.isp24.fcp_cmnd.task_mgmt_flags; + sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, + a->u.isp24.fcp_hdr.s_id); + unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun); + + if (!sess) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf024, + "qla_target(%d): task mgmt fn 0x%x for " + "non-existant session\n", vha->vp_idx, fn); + return qlt_sched_sess_work(tgt, QLA_TGT_SESS_WORK_TM, iocb, + sizeof(struct atio_from_isp)); + } + + return qlt_issue_task_mgmt(sess, unpacked_lun, fn, iocb, 0); +} + +/* ha->hardware_lock supposed to be held on entry */ +static int __qlt_abort_task(struct scsi_qla_host *vha, + struct imm_ntfy_from_isp *iocb, struct qla_tgt_sess *sess) +{ + struct atio_from_isp *a = (struct atio_from_isp *)iocb; + struct qla_hw_data *ha = vha->hw; + struct qla_tgt_mgmt_cmd *mcmd; + uint32_t lun, unpacked_lun; + int rc; + + mcmd = mempool_alloc(qla_tgt_mgmt_cmd_mempool, GFP_ATOMIC); + if (mcmd == NULL) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf05f, + "qla_target(%d): %s: Allocation of ABORT cmd failed\n", + vha->vp_idx, __func__); + return -ENOMEM; + } + memset(mcmd, 0, sizeof(*mcmd)); + + mcmd->sess = sess; + memcpy(&mcmd->orig_iocb.imm_ntfy, iocb, + sizeof(mcmd->orig_iocb.imm_ntfy)); + + lun = a->u.isp24.fcp_cmnd.lun; + unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun); + + rc = ha->tgt.tgt_ops->handle_tmr(mcmd, unpacked_lun, TMR_ABORT_TASK, + le16_to_cpu(iocb->u.isp2x.seq_id)); + if (rc != 0) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf060, + "qla_target(%d): tgt_ops->handle_tmr() failed: %d\n", + vha->vp_idx, rc); + mempool_free(mcmd, qla_tgt_mgmt_cmd_mempool); + return -EFAULT; + } + + return 0; +} + +/* ha->hardware_lock supposed to be held on entry */ +static int qlt_abort_task(struct scsi_qla_host *vha, + struct imm_ntfy_from_isp *iocb) +{ + struct qla_hw_data *ha = vha->hw; + struct qla_tgt_sess *sess; + int loop_id; + + loop_id = GET_TARGET_ID(ha, (struct atio_from_isp *)iocb); + + sess = ha->tgt.tgt_ops->find_sess_by_loop_id(vha, loop_id); + if (sess == NULL) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf025, + "qla_target(%d): task abort for unexisting " + "session\n", vha->vp_idx); + return qlt_sched_sess_work(vha->vha_tgt.qla_tgt, + QLA_TGT_SESS_WORK_ABORT, iocb, sizeof(*iocb)); + } + + return __qlt_abort_task(vha, iocb, sess); +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static int qlt_24xx_handle_els(struct scsi_qla_host *vha, + struct imm_ntfy_from_isp *iocb) +{ + int res = 0; + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf026, + "qla_target(%d): Port ID: 0x%3phC ELS opcode: 0x%02x\n", + vha->vp_idx, iocb->u.isp24.port_id, iocb->u.isp24.status_subcode); + + switch (iocb->u.isp24.status_subcode) { + case ELS_PLOGI: + case ELS_FLOGI: + case ELS_PRLI: + case ELS_LOGO: + case ELS_PRLO: + res = qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS); + break; + case ELS_PDISC: + case ELS_ADISC: + { + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; + if (tgt->link_reinit_iocb_pending) { + qlt_send_notify_ack(vha, &tgt->link_reinit_iocb, + 0, 0, 0, 0, 0, 0); + tgt->link_reinit_iocb_pending = 0; + } + res = 1; /* send notify ack */ + break; + } + + default: + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf061, + "qla_target(%d): Unsupported ELS command %x " + "received\n", vha->vp_idx, iocb->u.isp24.status_subcode); + res = qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS); + break; + } + + return res; +} + +static int qlt_set_data_offset(struct qla_tgt_cmd *cmd, uint32_t offset) +{ + struct scatterlist *sg, *sgp, *sg_srr, *sg_srr_start = NULL; + size_t first_offset = 0, rem_offset = offset, tmp = 0; + int i, sg_srr_cnt, bufflen = 0; + + ql_dbg(ql_dbg_tgt, cmd->vha, 0xe023, + "Entering qla_tgt_set_data_offset: cmd: %p, cmd->sg: %p, " + "cmd->sg_cnt: %u, direction: %d\n", + cmd, cmd->sg, cmd->sg_cnt, cmd->dma_data_direction); + + /* + * FIXME: Reject non zero SRR relative offset until we can test + * this code properly. + */ + pr_debug("Rejecting non zero SRR rel_offs: %u\n", offset); + return -1; + + if (!cmd->sg || !cmd->sg_cnt) { + ql_dbg(ql_dbg_tgt, cmd->vha, 0xe055, + "Missing cmd->sg or zero cmd->sg_cnt in" + " qla_tgt_set_data_offset\n"); + return -EINVAL; + } + /* + * Walk the current cmd->sg list until we locate the new sg_srr_start + */ + for_each_sg(cmd->sg, sg, cmd->sg_cnt, i) { + ql_dbg(ql_dbg_tgt, cmd->vha, 0xe024, + "sg[%d]: %p page: %p, length: %d, offset: %d\n", + i, sg, sg_page(sg), sg->length, sg->offset); + + if ((sg->length + tmp) > offset) { + first_offset = rem_offset; + sg_srr_start = sg; + ql_dbg(ql_dbg_tgt, cmd->vha, 0xe025, + "Found matching sg[%d], using %p as sg_srr_start, " + "and using first_offset: %zu\n", i, sg, + first_offset); + break; + } + tmp += sg->length; + rem_offset -= sg->length; + } + + if (!sg_srr_start) { + ql_dbg(ql_dbg_tgt, cmd->vha, 0xe056, + "Unable to locate sg_srr_start for offset: %u\n", offset); + return -EINVAL; + } + sg_srr_cnt = (cmd->sg_cnt - i); + + sg_srr = kzalloc(sizeof(struct scatterlist) * sg_srr_cnt, GFP_KERNEL); + if (!sg_srr) { + ql_dbg(ql_dbg_tgt, cmd->vha, 0xe057, + "Unable to allocate sgp\n"); + return -ENOMEM; + } + sg_init_table(sg_srr, sg_srr_cnt); + sgp = &sg_srr[0]; + /* + * Walk the remaining list for sg_srr_start, mapping to the newly + * allocated sg_srr taking first_offset into account. + */ + for_each_sg(sg_srr_start, sg, sg_srr_cnt, i) { + if (first_offset) { + sg_set_page(sgp, sg_page(sg), + (sg->length - first_offset), first_offset); + first_offset = 0; + } else { + sg_set_page(sgp, sg_page(sg), sg->length, 0); + } + bufflen += sgp->length; + + sgp = sg_next(sgp); + if (!sgp) + break; + } + + cmd->sg = sg_srr; + cmd->sg_cnt = sg_srr_cnt; + cmd->bufflen = bufflen; + cmd->offset += offset; + cmd->free_sg = 1; + + ql_dbg(ql_dbg_tgt, cmd->vha, 0xe026, "New cmd->sg: %p\n", cmd->sg); + ql_dbg(ql_dbg_tgt, cmd->vha, 0xe027, "New cmd->sg_cnt: %u\n", + cmd->sg_cnt); + ql_dbg(ql_dbg_tgt, cmd->vha, 0xe028, "New cmd->bufflen: %u\n", + cmd->bufflen); + ql_dbg(ql_dbg_tgt, cmd->vha, 0xe029, "New cmd->offset: %u\n", + cmd->offset); + + if (cmd->sg_cnt < 0) + BUG(); + + if (cmd->bufflen < 0) + BUG(); + + return 0; +} + +static inline int qlt_srr_adjust_data(struct qla_tgt_cmd *cmd, + uint32_t srr_rel_offs, int *xmit_type) +{ + int res = 0, rel_offs; + + rel_offs = srr_rel_offs - cmd->offset; + ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf027, "srr_rel_offs=%d, rel_offs=%d", + srr_rel_offs, rel_offs); + + *xmit_type = QLA_TGT_XMIT_ALL; + + if (rel_offs < 0) { + ql_dbg(ql_dbg_tgt_mgt, cmd->vha, 0xf062, + "qla_target(%d): SRR rel_offs (%d) < 0", + cmd->vha->vp_idx, rel_offs); + res = -1; + } else if (rel_offs == cmd->bufflen) + *xmit_type = QLA_TGT_XMIT_STATUS; + else if (rel_offs > 0) + res = qlt_set_data_offset(cmd, rel_offs); + + return res; +} + +/* No locks, thread context */ +static void qlt_handle_srr(struct scsi_qla_host *vha, + struct qla_tgt_srr_ctio *sctio, struct qla_tgt_srr_imm *imm) +{ + struct imm_ntfy_from_isp *ntfy = + (struct imm_ntfy_from_isp *)&imm->imm_ntfy; + struct qla_hw_data *ha = vha->hw; + struct qla_tgt_cmd *cmd = sctio->cmd; + struct se_cmd *se_cmd = &cmd->se_cmd; + unsigned long flags; + int xmit_type = 0, resp = 0; + uint32_t offset; + uint16_t srr_ui; + + offset = le32_to_cpu(ntfy->u.isp24.srr_rel_offs); + srr_ui = ntfy->u.isp24.srr_ui; + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf028, "SRR cmd %p, srr_ui %x\n", + cmd, srr_ui); + + switch (srr_ui) { + case SRR_IU_STATUS: + spin_lock_irqsave(&ha->hardware_lock, flags); + qlt_send_notify_ack(vha, ntfy, + 0, 0, 0, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + xmit_type = QLA_TGT_XMIT_STATUS; + resp = 1; + break; + case SRR_IU_DATA_IN: + if (!cmd->sg || !cmd->sg_cnt) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf063, + "Unable to process SRR_IU_DATA_IN due to" + " missing cmd->sg, state: %d\n", cmd->state); + dump_stack(); + goto out_reject; + } + if (se_cmd->scsi_status != 0) { + ql_dbg(ql_dbg_tgt, vha, 0xe02a, + "Rejecting SRR_IU_DATA_IN with non GOOD " + "scsi_status\n"); + goto out_reject; + } + cmd->bufflen = se_cmd->data_length; + + if (qlt_has_data(cmd)) { + if (qlt_srr_adjust_data(cmd, offset, &xmit_type) != 0) + goto out_reject; + spin_lock_irqsave(&ha->hardware_lock, flags); + qlt_send_notify_ack(vha, ntfy, + 0, 0, 0, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + resp = 1; + } else { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf064, + "qla_target(%d): SRR for in data for cmd " + "without them (tag %d, SCSI status %d), " + "reject", vha->vp_idx, cmd->tag, + cmd->se_cmd.scsi_status); + goto out_reject; + } + break; + case SRR_IU_DATA_OUT: + if (!cmd->sg || !cmd->sg_cnt) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf065, + "Unable to process SRR_IU_DATA_OUT due to" + " missing cmd->sg\n"); + dump_stack(); + goto out_reject; + } + if (se_cmd->scsi_status != 0) { + ql_dbg(ql_dbg_tgt, vha, 0xe02b, + "Rejecting SRR_IU_DATA_OUT" + " with non GOOD scsi_status\n"); + goto out_reject; + } + cmd->bufflen = se_cmd->data_length; + + if (qlt_has_data(cmd)) { + if (qlt_srr_adjust_data(cmd, offset, &xmit_type) != 0) + goto out_reject; + spin_lock_irqsave(&ha->hardware_lock, flags); + qlt_send_notify_ack(vha, ntfy, + 0, 0, 0, NOTIFY_ACK_SRR_FLAGS_ACCEPT, 0, 0); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + if (xmit_type & QLA_TGT_XMIT_DATA) + qlt_rdy_to_xfer(cmd); + } else { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf066, + "qla_target(%d): SRR for out data for cmd " + "without them (tag %d, SCSI status %d), " + "reject", vha->vp_idx, cmd->tag, + cmd->se_cmd.scsi_status); + goto out_reject; + } + break; + default: + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf067, + "qla_target(%d): Unknown srr_ui value %x", + vha->vp_idx, srr_ui); + goto out_reject; + } + + /* Transmit response in case of status and data-in cases */ + if (resp) + qlt_xmit_response(cmd, xmit_type, se_cmd->scsi_status); + + return; + +out_reject: + spin_lock_irqsave(&ha->hardware_lock, flags); + qlt_send_notify_ack(vha, ntfy, 0, 0, 0, + NOTIFY_ACK_SRR_FLAGS_REJECT, + NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM, + NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); + if (cmd->state == QLA_TGT_STATE_NEED_DATA) { + cmd->state = QLA_TGT_STATE_DATA_IN; + dump_stack(); + } else + qlt_send_term_exchange(vha, cmd, &cmd->atio, 1); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + +static void qlt_reject_free_srr_imm(struct scsi_qla_host *vha, + struct qla_tgt_srr_imm *imm, int ha_locked) +{ + struct qla_hw_data *ha = vha->hw; + unsigned long flags = 0; + + if (!ha_locked) + spin_lock_irqsave(&ha->hardware_lock, flags); + + qlt_send_notify_ack(vha, (void *)&imm->imm_ntfy, 0, 0, 0, + NOTIFY_ACK_SRR_FLAGS_REJECT, + NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM, + NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); + + if (!ha_locked) + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + kfree(imm); +} + +static void qlt_handle_srr_work(struct work_struct *work) +{ + struct qla_tgt *tgt = container_of(work, struct qla_tgt, srr_work); + struct scsi_qla_host *vha = tgt->vha; + struct qla_tgt_srr_ctio *sctio; + unsigned long flags; + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf029, "Entering SRR work (tgt %p)\n", + tgt); + +restart: + spin_lock_irqsave(&tgt->srr_lock, flags); + list_for_each_entry(sctio, &tgt->srr_ctio_list, srr_list_entry) { + struct qla_tgt_srr_imm *imm, *i, *ti; + struct qla_tgt_cmd *cmd; + struct se_cmd *se_cmd; + + imm = NULL; + list_for_each_entry_safe(i, ti, &tgt->srr_imm_list, + srr_list_entry) { + if (i->srr_id == sctio->srr_id) { + list_del(&i->srr_list_entry); + if (imm) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf068, + "qla_target(%d): There must be " + "only one IMM SRR per CTIO SRR " + "(IMM SRR %p, id %d, CTIO %p\n", + vha->vp_idx, i, i->srr_id, sctio); + qlt_reject_free_srr_imm(tgt->vha, i, 0); + } else + imm = i; + } + } + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02a, + "IMM SRR %p, CTIO SRR %p (id %d)\n", imm, sctio, + sctio->srr_id); + + if (imm == NULL) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02b, + "Not found matching IMM for SRR CTIO (id %d)\n", + sctio->srr_id); + continue; + } else + list_del(&sctio->srr_list_entry); + + spin_unlock_irqrestore(&tgt->srr_lock, flags); + + cmd = sctio->cmd; + /* + * Reset qla_tgt_cmd SRR values and SGL pointer+count to follow + * tcm_qla2xxx_write_pending() and tcm_qla2xxx_queue_data_in() + * logic.. + */ + cmd->offset = 0; + if (cmd->free_sg) { + kfree(cmd->sg); + cmd->sg = NULL; + cmd->free_sg = 0; + } + se_cmd = &cmd->se_cmd; + + cmd->sg_cnt = se_cmd->t_data_nents; + cmd->sg = se_cmd->t_data_sg; + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02c, + "SRR cmd %p (se_cmd %p, tag %d, op %x), " + "sg_cnt=%d, offset=%d", cmd, &cmd->se_cmd, cmd->tag, + se_cmd->t_task_cdb ? se_cmd->t_task_cdb[0] : 0, + cmd->sg_cnt, cmd->offset); + + qlt_handle_srr(vha, sctio, imm); + + kfree(imm); + kfree(sctio); + goto restart; + } + spin_unlock_irqrestore(&tgt->srr_lock, flags); +} + +/* ha->hardware_lock supposed to be held on entry */ +static void qlt_prepare_srr_imm(struct scsi_qla_host *vha, + struct imm_ntfy_from_isp *iocb) +{ + struct qla_tgt_srr_imm *imm; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; + struct qla_tgt_srr_ctio *sctio; + + tgt->imm_srr_id++; + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02d, "qla_target(%d): SRR received\n", + vha->vp_idx); + + imm = kzalloc(sizeof(*imm), GFP_ATOMIC); + if (imm != NULL) { + memcpy(&imm->imm_ntfy, iocb, sizeof(imm->imm_ntfy)); + + /* IRQ is already OFF */ + spin_lock(&tgt->srr_lock); + imm->srr_id = tgt->imm_srr_id; + list_add_tail(&imm->srr_list_entry, + &tgt->srr_imm_list); + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02e, + "IMM NTFY SRR %p added (id %d, ui %x)\n", + imm, imm->srr_id, iocb->u.isp24.srr_ui); + if (tgt->imm_srr_id == tgt->ctio_srr_id) { + int found = 0; + list_for_each_entry(sctio, &tgt->srr_ctio_list, + srr_list_entry) { + if (sctio->srr_id == imm->srr_id) { + found = 1; + break; + } + } + if (found) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf02f, "%s", + "Scheduling srr work\n"); + schedule_work(&tgt->srr_work); + } else { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf030, + "qla_target(%d): imm_srr_id " + "== ctio_srr_id (%d), but there is no " + "corresponding SRR CTIO, deleting IMM " + "SRR %p\n", vha->vp_idx, tgt->ctio_srr_id, + imm); + list_del(&imm->srr_list_entry); + + kfree(imm); + + spin_unlock(&tgt->srr_lock); + goto out_reject; + } + } + spin_unlock(&tgt->srr_lock); + } else { + struct qla_tgt_srr_ctio *ts; + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf069, + "qla_target(%d): Unable to allocate SRR IMM " + "entry, SRR request will be rejected\n", vha->vp_idx); + + /* IRQ is already OFF */ + spin_lock(&tgt->srr_lock); + list_for_each_entry_safe(sctio, ts, &tgt->srr_ctio_list, + srr_list_entry) { + if (sctio->srr_id == tgt->imm_srr_id) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf031, + "CTIO SRR %p deleted (id %d)\n", + sctio, sctio->srr_id); + list_del(&sctio->srr_list_entry); + qlt_send_term_exchange(vha, sctio->cmd, + &sctio->cmd->atio, 1); + kfree(sctio); + } + } + spin_unlock(&tgt->srr_lock); + goto out_reject; + } + + return; + +out_reject: + qlt_send_notify_ack(vha, iocb, 0, 0, 0, + NOTIFY_ACK_SRR_FLAGS_REJECT, + NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM, + NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL); +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +static void qlt_handle_imm_notify(struct scsi_qla_host *vha, + struct imm_ntfy_from_isp *iocb) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t add_flags = 0; + int send_notify_ack = 1; + uint16_t status; + + status = le16_to_cpu(iocb->u.isp2x.status); + switch (status) { + case IMM_NTFY_LIP_RESET: + { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf032, + "qla_target(%d): LIP reset (loop %#x), subcode %x\n", + vha->vp_idx, le16_to_cpu(iocb->u.isp24.nport_handle), + iocb->u.isp24.status_subcode); + + if (qlt_reset(vha, iocb, QLA_TGT_ABORT_ALL) == 0) + send_notify_ack = 0; + break; + } + + case IMM_NTFY_LIP_LINK_REINIT: + { + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf033, + "qla_target(%d): LINK REINIT (loop %#x, " + "subcode %x)\n", vha->vp_idx, + le16_to_cpu(iocb->u.isp24.nport_handle), + iocb->u.isp24.status_subcode); + if (tgt->link_reinit_iocb_pending) { + qlt_send_notify_ack(vha, &tgt->link_reinit_iocb, + 0, 0, 0, 0, 0, 0); + } + memcpy(&tgt->link_reinit_iocb, iocb, sizeof(*iocb)); + tgt->link_reinit_iocb_pending = 1; + /* + * QLogic requires to wait after LINK REINIT for possible + * PDISC or ADISC ELS commands + */ + send_notify_ack = 0; + break; + } + + case IMM_NTFY_PORT_LOGOUT: + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf034, + "qla_target(%d): Port logout (loop " + "%#x, subcode %x)\n", vha->vp_idx, + le16_to_cpu(iocb->u.isp24.nport_handle), + iocb->u.isp24.status_subcode); + + if (qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS_SESS) == 0) + send_notify_ack = 0; + /* The sessions will be cleared in the callback, if needed */ + break; + + case IMM_NTFY_GLBL_TPRLO: + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf035, + "qla_target(%d): Global TPRLO (%x)\n", vha->vp_idx, status); + if (qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS) == 0) + send_notify_ack = 0; + /* The sessions will be cleared in the callback, if needed */ + break; + + case IMM_NTFY_PORT_CONFIG: + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf036, + "qla_target(%d): Port config changed (%x)\n", vha->vp_idx, + status); + if (qlt_reset(vha, iocb, QLA_TGT_ABORT_ALL) == 0) + send_notify_ack = 0; + /* The sessions will be cleared in the callback, if needed */ + break; + + case IMM_NTFY_GLBL_LOGO: + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06a, + "qla_target(%d): Link failure detected\n", + vha->vp_idx); + /* I_T nexus loss */ + if (qlt_reset(vha, iocb, QLA_TGT_NEXUS_LOSS) == 0) + send_notify_ack = 0; + break; + + case IMM_NTFY_IOCB_OVERFLOW: + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06b, + "qla_target(%d): Cannot provide requested " + "capability (IOCB overflowed the immediate notify " + "resource count)\n", vha->vp_idx); + break; + + case IMM_NTFY_ABORT_TASK: + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf037, + "qla_target(%d): Abort Task (S %08x I %#x -> " + "L %#x)\n", vha->vp_idx, + le16_to_cpu(iocb->u.isp2x.seq_id), + GET_TARGET_ID(ha, (struct atio_from_isp *)iocb), + le16_to_cpu(iocb->u.isp2x.lun)); + if (qlt_abort_task(vha, iocb) == 0) + send_notify_ack = 0; + break; + + case IMM_NTFY_RESOURCE: + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06c, + "qla_target(%d): Out of resources, host %ld\n", + vha->vp_idx, vha->host_no); + break; + + case IMM_NTFY_MSG_RX: + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf038, + "qla_target(%d): Immediate notify task %x\n", + vha->vp_idx, iocb->u.isp2x.task_flags); + if (qlt_handle_task_mgmt(vha, iocb) == 0) + send_notify_ack = 0; + break; + + case IMM_NTFY_ELS: + if (qlt_24xx_handle_els(vha, iocb) == 0) + send_notify_ack = 0; + break; + + case IMM_NTFY_SRR: + qlt_prepare_srr_imm(vha, iocb); + send_notify_ack = 0; + break; + + default: + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06d, + "qla_target(%d): Received unknown immediate " + "notify status %x\n", vha->vp_idx, status); + break; + } + + if (send_notify_ack) + qlt_send_notify_ack(vha, iocb, add_flags, 0, 0, 0, 0, 0); +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + * This function sends busy to ISP 2xxx or 24xx. + */ +static void qlt_send_busy(struct scsi_qla_host *vha, + struct atio_from_isp *atio, uint16_t status) +{ + struct ctio7_to_24xx *ctio24; + struct qla_hw_data *ha = vha->hw; + request_t *pkt; + struct qla_tgt_sess *sess = NULL; + + sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, + atio->u.isp24.fcp_hdr.s_id); + if (!sess) { + qlt_send_term_exchange(vha, NULL, atio, 1); + return; + } + /* Sending marker isn't necessary, since we called from ISR */ + + pkt = (request_t *)qla2x00_alloc_iocbs(vha, NULL); + if (!pkt) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06e, + "qla_target(%d): %s failed: unable to allocate " + "request packet", vha->vp_idx, __func__); + return; + } + + pkt->entry_count = 1; + pkt->handle = QLA_TGT_SKIP_HANDLE | CTIO_COMPLETION_HANDLE_MARK; + + ctio24 = (struct ctio7_to_24xx *)pkt; + ctio24->entry_type = CTIO_TYPE7; + ctio24->nport_handle = sess->loop_id; + ctio24->timeout = __constant_cpu_to_le16(QLA_TGT_TIMEOUT); + ctio24->vp_index = vha->vp_idx; + ctio24->initiator_id[0] = atio->u.isp24.fcp_hdr.s_id[2]; + ctio24->initiator_id[1] = atio->u.isp24.fcp_hdr.s_id[1]; + ctio24->initiator_id[2] = atio->u.isp24.fcp_hdr.s_id[0]; + ctio24->exchange_addr = atio->u.isp24.exchange_addr; + ctio24->u.status1.flags = (atio->u.isp24.attr << 9) | + __constant_cpu_to_le16( + CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_SEND_STATUS | + CTIO7_FLAGS_DONT_RET_CTIO); + /* + * CTIO from fw w/o se_cmd doesn't provide enough info to retry it, + * if the explicit conformation is used. + */ + ctio24->u.status1.ox_id = swab16(atio->u.isp24.fcp_hdr.ox_id); + ctio24->u.status1.scsi_status = cpu_to_le16(status); + ctio24->u.status1.residual = get_unaligned((uint32_t *) + &atio->u.isp24.fcp_cmnd.add_cdb[ + atio->u.isp24.fcp_cmnd.add_cdb_len]); + if (ctio24->u.status1.residual != 0) + ctio24->u.status1.scsi_status |= SS_RESIDUAL_UNDER; + + qla2x00_start_iocbs(vha, vha->req); +} + +/* ha->hardware_lock supposed to be held on entry */ +/* called via callback from qla2xxx */ +static void qlt_24xx_atio_pkt(struct scsi_qla_host *vha, + struct atio_from_isp *atio) +{ + struct qla_hw_data *ha = vha->hw; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; + int rc; + + if (unlikely(tgt == NULL)) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf039, + "ATIO pkt, but no tgt (ha %p)", ha); + return; + } + ql_dbg(ql_dbg_tgt, vha, 0xe02c, + "qla_target(%d): ATIO pkt %p: type %02x count %02x", + vha->vp_idx, atio, atio->u.raw.entry_type, + atio->u.raw.entry_count); + /* + * In tgt_stop mode we also should allow all requests to pass. + * Otherwise, some commands can stuck. + */ + + tgt->irq_cmd_count++; + + switch (atio->u.raw.entry_type) { + case ATIO_TYPE7: + ql_dbg(ql_dbg_tgt, vha, 0xe02d, + "ATIO_TYPE7 instance %d, lun %Lx, read/write %d/%d, cdb %x, add_cdb_len %x, data_length %04x, s_id %02x%02x%02x\n", + vha->vp_idx, atio->u.isp24.fcp_cmnd.lun, + atio->u.isp24.fcp_cmnd.rddata, + atio->u.isp24.fcp_cmnd.wrdata, + atio->u.isp24.fcp_cmnd.cdb[0], + atio->u.isp24.fcp_cmnd.add_cdb_len, + be32_to_cpu(get_unaligned((uint32_t *) + &atio->u.isp24.fcp_cmnd.add_cdb[ + atio->u.isp24.fcp_cmnd.add_cdb_len])), + atio->u.isp24.fcp_hdr.s_id[0], + atio->u.isp24.fcp_hdr.s_id[1], + atio->u.isp24.fcp_hdr.s_id[2]); + + if (unlikely(atio->u.isp24.exchange_addr == + ATIO_EXCHANGE_ADDRESS_UNKNOWN)) { + ql_dbg(ql_dbg_tgt, vha, 0xe058, + "qla_target(%d): ATIO_TYPE7 " + "received with UNKNOWN exchange address, " + "sending QUEUE_FULL\n", vha->vp_idx); + qlt_send_busy(vha, atio, SAM_STAT_TASK_SET_FULL); + break; + } + if (likely(atio->u.isp24.fcp_cmnd.task_mgmt_flags == 0)) + rc = qlt_handle_cmd_for_atio(vha, atio); + else + rc = qlt_handle_task_mgmt(vha, atio); + if (unlikely(rc != 0)) { + if (rc == -ESRCH) { +#if 1 /* With TERM EXCHANGE some FC cards refuse to boot */ + qlt_send_busy(vha, atio, SAM_STAT_BUSY); +#else + qlt_send_term_exchange(vha, NULL, atio, 1); +#endif + } else { + if (tgt->tgt_stop) { + ql_dbg(ql_dbg_tgt, vha, 0xe059, + "qla_target: Unable to send " + "command to target for req, " + "ignoring.\n"); + } else { + ql_dbg(ql_dbg_tgt, vha, 0xe05a, + "qla_target(%d): Unable to send " + "command to target, sending BUSY " + "status.\n", vha->vp_idx); + qlt_send_busy(vha, atio, SAM_STAT_BUSY); + } + } + } + break; + + case IMMED_NOTIFY_TYPE: + { + if (unlikely(atio->u.isp2x.entry_status != 0)) { + ql_dbg(ql_dbg_tgt, vha, 0xe05b, + "qla_target(%d): Received ATIO packet %x " + "with error status %x\n", vha->vp_idx, + atio->u.raw.entry_type, + atio->u.isp2x.entry_status); + break; + } + ql_dbg(ql_dbg_tgt, vha, 0xe02e, "%s", "IMMED_NOTIFY ATIO"); + qlt_handle_imm_notify(vha, (struct imm_ntfy_from_isp *)atio); + break; + } + + default: + ql_dbg(ql_dbg_tgt, vha, 0xe05c, + "qla_target(%d): Received unknown ATIO atio " + "type %x\n", vha->vp_idx, atio->u.raw.entry_type); + break; + } + + tgt->irq_cmd_count--; +} + +/* ha->hardware_lock supposed to be held on entry */ +/* called via callback from qla2xxx */ +static void qlt_response_pkt(struct scsi_qla_host *vha, response_t *pkt) +{ + struct qla_hw_data *ha = vha->hw; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; + + if (unlikely(tgt == NULL)) { + ql_dbg(ql_dbg_tgt, vha, 0xe05d, + "qla_target(%d): Response pkt %x received, but no " + "tgt (ha %p)\n", vha->vp_idx, pkt->entry_type, ha); + return; + } + + ql_dbg(ql_dbg_tgt, vha, 0xe02f, + "qla_target(%d): response pkt %p: T %02x C %02x S %02x " + "handle %#x\n", vha->vp_idx, pkt, pkt->entry_type, + pkt->entry_count, pkt->entry_status, pkt->handle); + + /* + * In tgt_stop mode we also should allow all requests to pass. + * Otherwise, some commands can stuck. + */ + + tgt->irq_cmd_count++; + + switch (pkt->entry_type) { + case CTIO_CRC2: + case CTIO_TYPE7: + { + struct ctio7_from_24xx *entry = (struct ctio7_from_24xx *)pkt; + ql_dbg(ql_dbg_tgt, vha, 0xe030, + "CTIO[0x%x] 12/CTIO7 7A/CRC2: instance %d\n", + entry->entry_type, vha->vp_idx); + qlt_do_ctio_completion(vha, entry->handle, + le16_to_cpu(entry->status)|(pkt->entry_status << 16), + entry); + break; + } + + case ACCEPT_TGT_IO_TYPE: + { + struct atio_from_isp *atio = (struct atio_from_isp *)pkt; + int rc; + ql_dbg(ql_dbg_tgt, vha, 0xe031, + "ACCEPT_TGT_IO instance %d status %04x " + "lun %04x read/write %d data_length %04x " + "target_id %02x rx_id %04x\n ", vha->vp_idx, + le16_to_cpu(atio->u.isp2x.status), + le16_to_cpu(atio->u.isp2x.lun), + atio->u.isp2x.execution_codes, + le32_to_cpu(atio->u.isp2x.data_length), GET_TARGET_ID(ha, + atio), atio->u.isp2x.rx_id); + if (atio->u.isp2x.status != + __constant_cpu_to_le16(ATIO_CDB_VALID)) { + ql_dbg(ql_dbg_tgt, vha, 0xe05e, + "qla_target(%d): ATIO with error " + "status %x received\n", vha->vp_idx, + le16_to_cpu(atio->u.isp2x.status)); + break; + } + ql_dbg(ql_dbg_tgt, vha, 0xe032, + "FCP CDB: 0x%02x, sizeof(cdb): %lu", + atio->u.isp2x.cdb[0], (unsigned long + int)sizeof(atio->u.isp2x.cdb)); + + rc = qlt_handle_cmd_for_atio(vha, atio); + if (unlikely(rc != 0)) { + if (rc == -ESRCH) { +#if 1 /* With TERM EXCHANGE some FC cards refuse to boot */ + qlt_send_busy(vha, atio, 0); +#else + qlt_send_term_exchange(vha, NULL, atio, 1); +#endif + } else { + if (tgt->tgt_stop) { + ql_dbg(ql_dbg_tgt, vha, 0xe05f, + "qla_target: Unable to send " + "command to target, sending TERM " + "EXCHANGE for rsp\n"); + qlt_send_term_exchange(vha, NULL, + atio, 1); + } else { + ql_dbg(ql_dbg_tgt, vha, 0xe060, + "qla_target(%d): Unable to send " + "command to target, sending BUSY " + "status\n", vha->vp_idx); + qlt_send_busy(vha, atio, 0); + } + } + } + } + break; + + case CONTINUE_TGT_IO_TYPE: + { + struct ctio_to_2xxx *entry = (struct ctio_to_2xxx *)pkt; + ql_dbg(ql_dbg_tgt, vha, 0xe033, + "CONTINUE_TGT_IO: instance %d\n", vha->vp_idx); + qlt_do_ctio_completion(vha, entry->handle, + le16_to_cpu(entry->status)|(pkt->entry_status << 16), + entry); + break; + } + + case CTIO_A64_TYPE: + { + struct ctio_to_2xxx *entry = (struct ctio_to_2xxx *)pkt; + ql_dbg(ql_dbg_tgt, vha, 0xe034, "CTIO_A64: instance %d\n", + vha->vp_idx); + qlt_do_ctio_completion(vha, entry->handle, + le16_to_cpu(entry->status)|(pkt->entry_status << 16), + entry); + break; + } + + case IMMED_NOTIFY_TYPE: + ql_dbg(ql_dbg_tgt, vha, 0xe035, "%s", "IMMED_NOTIFY\n"); + qlt_handle_imm_notify(vha, (struct imm_ntfy_from_isp *)pkt); + break; + + case NOTIFY_ACK_TYPE: + if (tgt->notify_ack_expected > 0) { + struct nack_to_isp *entry = (struct nack_to_isp *)pkt; + ql_dbg(ql_dbg_tgt, vha, 0xe036, + "NOTIFY_ACK seq %08x status %x\n", + le16_to_cpu(entry->u.isp2x.seq_id), + le16_to_cpu(entry->u.isp2x.status)); + tgt->notify_ack_expected--; + if (entry->u.isp2x.status != + __constant_cpu_to_le16(NOTIFY_ACK_SUCCESS)) { + ql_dbg(ql_dbg_tgt, vha, 0xe061, + "qla_target(%d): NOTIFY_ACK " + "failed %x\n", vha->vp_idx, + le16_to_cpu(entry->u.isp2x.status)); + } + } else { + ql_dbg(ql_dbg_tgt, vha, 0xe062, + "qla_target(%d): Unexpected NOTIFY_ACK received\n", + vha->vp_idx); + } + break; + + case ABTS_RECV_24XX: + ql_dbg(ql_dbg_tgt, vha, 0xe037, + "ABTS_RECV_24XX: instance %d\n", vha->vp_idx); + qlt_24xx_handle_abts(vha, (struct abts_recv_from_24xx *)pkt); + break; + + case ABTS_RESP_24XX: + if (tgt->abts_resp_expected > 0) { + struct abts_resp_from_24xx_fw *entry = + (struct abts_resp_from_24xx_fw *)pkt; + ql_dbg(ql_dbg_tgt, vha, 0xe038, + "ABTS_RESP_24XX: compl_status %x\n", + entry->compl_status); + tgt->abts_resp_expected--; + if (le16_to_cpu(entry->compl_status) != + ABTS_RESP_COMPL_SUCCESS) { + if ((entry->error_subcode1 == 0x1E) && + (entry->error_subcode2 == 0)) { + /* + * We've got a race here: aborted + * exchange not terminated, i.e. + * response for the aborted command was + * sent between the abort request was + * received and processed. + * Unfortunately, the firmware has a + * silly requirement that all aborted + * exchanges must be explicitely + * terminated, otherwise it refuses to + * send responses for the abort + * requests. So, we have to + * (re)terminate the exchange and retry + * the abort response. + */ + qlt_24xx_retry_term_exchange(vha, + entry); + } else + ql_dbg(ql_dbg_tgt, vha, 0xe063, + "qla_target(%d): ABTS_RESP_24XX " + "failed %x (subcode %x:%x)", + vha->vp_idx, entry->compl_status, + entry->error_subcode1, + entry->error_subcode2); + } + } else { + ql_dbg(ql_dbg_tgt, vha, 0xe064, + "qla_target(%d): Unexpected ABTS_RESP_24XX " + "received\n", vha->vp_idx); + } + break; + + default: + ql_dbg(ql_dbg_tgt, vha, 0xe065, + "qla_target(%d): Received unknown response pkt " + "type %x\n", vha->vp_idx, pkt->entry_type); + break; + } + + tgt->irq_cmd_count--; +} + +/* + * ha->hardware_lock supposed to be held on entry. Might drop it, then reaquire + */ +void qlt_async_event(uint16_t code, struct scsi_qla_host *vha, + uint16_t *mailbox) +{ + struct qla_hw_data *ha = vha->hw; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; + int login_code; + + ql_dbg(ql_dbg_tgt, vha, 0xe039, + "scsi(%ld): ha state %d init_done %d oper_mode %d topo %d\n", + vha->host_no, atomic_read(&vha->loop_state), vha->flags.init_done, + ha->operating_mode, ha->current_topology); + + if (!ha->tgt.tgt_ops) + return; + + if (unlikely(tgt == NULL)) { + ql_dbg(ql_dbg_tgt, vha, 0xe03a, + "ASYNC EVENT %#x, but no tgt (ha %p)\n", code, ha); + return; + } + + if (((code == MBA_POINT_TO_POINT) || (code == MBA_CHG_IN_CONNECTION)) && + IS_QLA2100(ha)) + return; + /* + * In tgt_stop mode we also should allow all requests to pass. + * Otherwise, some commands can stuck. + */ + + tgt->irq_cmd_count++; + + switch (code) { + case MBA_RESET: /* Reset */ + case MBA_SYSTEM_ERR: /* System Error */ + case MBA_REQ_TRANSFER_ERR: /* Request Transfer Error */ + case MBA_RSP_TRANSFER_ERR: /* Response Transfer Error */ + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03a, + "qla_target(%d): System error async event %#x " + "occurred", vha->vp_idx, code); + break; + case MBA_WAKEUP_THRES: /* Request Queue Wake-up. */ + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + break; + + case MBA_LOOP_UP: + { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03b, + "qla_target(%d): Async LOOP_UP occurred " + "(m[0]=%x, m[1]=%x, m[2]=%x, m[3]=%x)", vha->vp_idx, + le16_to_cpu(mailbox[0]), le16_to_cpu(mailbox[1]), + le16_to_cpu(mailbox[2]), le16_to_cpu(mailbox[3])); + if (tgt->link_reinit_iocb_pending) { + qlt_send_notify_ack(vha, (void *)&tgt->link_reinit_iocb, + 0, 0, 0, 0, 0, 0); + tgt->link_reinit_iocb_pending = 0; + } + break; + } + + case MBA_LIP_OCCURRED: + case MBA_LOOP_DOWN: + case MBA_LIP_RESET: + case MBA_RSCN_UPDATE: + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03c, + "qla_target(%d): Async event %#x occurred " + "(m[0]=%x, m[1]=%x, m[2]=%x, m[3]=%x)", vha->vp_idx, code, + le16_to_cpu(mailbox[0]), le16_to_cpu(mailbox[1]), + le16_to_cpu(mailbox[2]), le16_to_cpu(mailbox[3])); + break; + + case MBA_PORT_UPDATE: + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03d, + "qla_target(%d): Port update async event %#x " + "occurred: updating the ports database (m[0]=%x, m[1]=%x, " + "m[2]=%x, m[3]=%x)", vha->vp_idx, code, + le16_to_cpu(mailbox[0]), le16_to_cpu(mailbox[1]), + le16_to_cpu(mailbox[2]), le16_to_cpu(mailbox[3])); + + login_code = le16_to_cpu(mailbox[2]); + if (login_code == 0x4) + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03e, + "Async MB 2: Got PLOGI Complete\n"); + else if (login_code == 0x7) + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf03f, + "Async MB 2: Port Logged Out\n"); + break; + + default: + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf040, + "qla_target(%d): Async event %#x occurred: " + "ignore (m[0]=%x, m[1]=%x, m[2]=%x, m[3]=%x)", vha->vp_idx, + code, le16_to_cpu(mailbox[0]), le16_to_cpu(mailbox[1]), + le16_to_cpu(mailbox[2]), le16_to_cpu(mailbox[3])); + break; + } + + tgt->irq_cmd_count--; +} + +static fc_port_t *qlt_get_port_database(struct scsi_qla_host *vha, + uint16_t loop_id) +{ + fc_port_t *fcport; + int rc; + + fcport = kzalloc(sizeof(*fcport), GFP_KERNEL); + if (!fcport) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf06f, + "qla_target(%d): Allocation of tmp FC port failed", + vha->vp_idx); + return NULL; + } + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf041, "loop_id %d", loop_id); + + fcport->loop_id = loop_id; + + rc = qla2x00_get_port_database(vha, fcport, 0); + if (rc != QLA_SUCCESS) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf070, + "qla_target(%d): Failed to retrieve fcport " + "information -- get_port_database() returned %x " + "(loop_id=0x%04x)", vha->vp_idx, rc, loop_id); + kfree(fcport); + return NULL; + } + + return fcport; +} + +/* Must be called under tgt_mutex */ +static struct qla_tgt_sess *qlt_make_local_sess(struct scsi_qla_host *vha, + uint8_t *s_id) +{ + struct qla_tgt_sess *sess = NULL; + fc_port_t *fcport = NULL; + int rc, global_resets; + uint16_t loop_id = 0; + +retry: + global_resets = + atomic_read(&vha->vha_tgt.qla_tgt->tgt_global_resets_count); + + rc = qla24xx_get_loop_id(vha, s_id, &loop_id); + if (rc != 0) { + if ((s_id[0] == 0xFF) && + (s_id[1] == 0xFC)) { + /* + * This is Domain Controller, so it should be + * OK to drop SCSI commands from it. + */ + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf042, + "Unable to find initiator with S_ID %x:%x:%x", + s_id[0], s_id[1], s_id[2]); + } else + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf071, + "qla_target(%d): Unable to find " + "initiator with S_ID %x:%x:%x", + vha->vp_idx, s_id[0], s_id[1], + s_id[2]); + return NULL; + } + + fcport = qlt_get_port_database(vha, loop_id); + if (!fcport) + return NULL; + + if (global_resets != + atomic_read(&vha->vha_tgt.qla_tgt->tgt_global_resets_count)) { + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf043, + "qla_target(%d): global reset during session discovery " + "(counter was %d, new %d), retrying", vha->vp_idx, + global_resets, + atomic_read(&vha->vha_tgt. + qla_tgt->tgt_global_resets_count)); + goto retry; + } + + sess = qlt_create_sess(vha, fcport, true); + + kfree(fcport); + return sess; +} + +static void qlt_abort_work(struct qla_tgt *tgt, + struct qla_tgt_sess_work_param *prm) +{ + struct scsi_qla_host *vha = tgt->vha; + struct qla_hw_data *ha = vha->hw; + struct qla_tgt_sess *sess = NULL; + unsigned long flags; + uint32_t be_s_id; + uint8_t s_id[3]; + int rc; + + spin_lock_irqsave(&ha->hardware_lock, flags); + + if (tgt->tgt_stop) + goto out_term; + + s_id[0] = prm->abts.fcp_hdr_le.s_id[2]; + s_id[1] = prm->abts.fcp_hdr_le.s_id[1]; + s_id[2] = prm->abts.fcp_hdr_le.s_id[0]; + + sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, + (unsigned char *)&be_s_id); + if (!sess) { + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + mutex_lock(&vha->vha_tgt.tgt_mutex); + sess = qlt_make_local_sess(vha, s_id); + /* sess has got an extra creation ref */ + mutex_unlock(&vha->vha_tgt.tgt_mutex); + + spin_lock_irqsave(&ha->hardware_lock, flags); + if (!sess) + goto out_term; + } else { + kref_get(&sess->se_sess->sess_kref); + } + + if (tgt->tgt_stop) + goto out_term; + + rc = __qlt_24xx_handle_abts(vha, &prm->abts, sess); + if (rc != 0) + goto out_term; + + ha->tgt.tgt_ops->put_sess(sess); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + return; + +out_term: + qlt_24xx_send_abts_resp(vha, &prm->abts, FCP_TMF_REJECTED, false); + if (sess) + ha->tgt.tgt_ops->put_sess(sess); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + +static void qlt_tmr_work(struct qla_tgt *tgt, + struct qla_tgt_sess_work_param *prm) +{ + struct atio_from_isp *a = &prm->tm_iocb2; + struct scsi_qla_host *vha = tgt->vha; + struct qla_hw_data *ha = vha->hw; + struct qla_tgt_sess *sess = NULL; + unsigned long flags; + uint8_t *s_id = NULL; /* to hide compiler warnings */ + int rc; + uint32_t lun, unpacked_lun; + int lun_size, fn; + void *iocb; + + spin_lock_irqsave(&ha->hardware_lock, flags); + + if (tgt->tgt_stop) + goto out_term; + + s_id = prm->tm_iocb2.u.isp24.fcp_hdr.s_id; + sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, s_id); + if (!sess) { + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + mutex_lock(&vha->vha_tgt.tgt_mutex); + sess = qlt_make_local_sess(vha, s_id); + /* sess has got an extra creation ref */ + mutex_unlock(&vha->vha_tgt.tgt_mutex); + + spin_lock_irqsave(&ha->hardware_lock, flags); + if (!sess) + goto out_term; + } else { + kref_get(&sess->se_sess->sess_kref); + } + + iocb = a; + lun = a->u.isp24.fcp_cmnd.lun; + lun_size = sizeof(lun); + fn = a->u.isp24.fcp_cmnd.task_mgmt_flags; + unpacked_lun = scsilun_to_int((struct scsi_lun *)&lun); + + rc = qlt_issue_task_mgmt(sess, unpacked_lun, fn, iocb, 0); + if (rc != 0) + goto out_term; + + ha->tgt.tgt_ops->put_sess(sess); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + return; + +out_term: + qlt_send_term_exchange(vha, NULL, &prm->tm_iocb2, 1); + if (sess) + ha->tgt.tgt_ops->put_sess(sess); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + +static void qlt_sess_work_fn(struct work_struct *work) +{ + struct qla_tgt *tgt = container_of(work, struct qla_tgt, sess_work); + struct scsi_qla_host *vha = tgt->vha; + unsigned long flags; + + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf000, "Sess work (tgt %p)", tgt); + + spin_lock_irqsave(&tgt->sess_work_lock, flags); + while (!list_empty(&tgt->sess_works_list)) { + struct qla_tgt_sess_work_param *prm = list_entry( + tgt->sess_works_list.next, typeof(*prm), + sess_works_list_entry); + + /* + * This work can be scheduled on several CPUs at time, so we + * must delete the entry to eliminate double processing + */ + list_del(&prm->sess_works_list_entry); + + spin_unlock_irqrestore(&tgt->sess_work_lock, flags); + + switch (prm->type) { + case QLA_TGT_SESS_WORK_ABORT: + qlt_abort_work(tgt, prm); + break; + case QLA_TGT_SESS_WORK_TM: + qlt_tmr_work(tgt, prm); + break; + default: + BUG_ON(1); + break; + } + + spin_lock_irqsave(&tgt->sess_work_lock, flags); + + kfree(prm); + } + spin_unlock_irqrestore(&tgt->sess_work_lock, flags); +} + +/* Must be called under tgt_host_action_mutex */ +int qlt_add_target(struct qla_hw_data *ha, struct scsi_qla_host *base_vha) +{ + struct qla_tgt *tgt; + + if (!QLA_TGT_MODE_ENABLED()) + return 0; + + if (!IS_TGT_MODE_CAPABLE(ha)) { + ql_log(ql_log_warn, base_vha, 0xe070, + "This adapter does not support target mode.\n"); + return 0; + } + + ql_dbg(ql_dbg_tgt, base_vha, 0xe03b, + "Registering target for host %ld(%p).\n", base_vha->host_no, ha); + + BUG_ON(base_vha->vha_tgt.qla_tgt != NULL); + + tgt = kzalloc(sizeof(struct qla_tgt), GFP_KERNEL); + if (!tgt) { + ql_dbg(ql_dbg_tgt, base_vha, 0xe066, + "Unable to allocate struct qla_tgt\n"); + return -ENOMEM; + } + + if (!(base_vha->host->hostt->supported_mode & MODE_TARGET)) + base_vha->host->hostt->supported_mode |= MODE_TARGET; + + tgt->ha = ha; + tgt->vha = base_vha; + init_waitqueue_head(&tgt->waitQ); + INIT_LIST_HEAD(&tgt->sess_list); + INIT_LIST_HEAD(&tgt->del_sess_list); + INIT_DELAYED_WORK(&tgt->sess_del_work, + (void (*)(struct work_struct *))qlt_del_sess_work_fn); + spin_lock_init(&tgt->sess_work_lock); + INIT_WORK(&tgt->sess_work, qlt_sess_work_fn); + INIT_LIST_HEAD(&tgt->sess_works_list); + spin_lock_init(&tgt->srr_lock); + INIT_LIST_HEAD(&tgt->srr_ctio_list); + INIT_LIST_HEAD(&tgt->srr_imm_list); + INIT_WORK(&tgt->srr_work, qlt_handle_srr_work); + atomic_set(&tgt->tgt_global_resets_count, 0); + + base_vha->vha_tgt.qla_tgt = tgt; + + ql_dbg(ql_dbg_tgt, base_vha, 0xe067, + "qla_target(%d): using 64 Bit PCI addressing", + base_vha->vp_idx); + tgt->tgt_enable_64bit_addr = 1; + /* 3 is reserved */ + tgt->sg_tablesize = QLA_TGT_MAX_SG_24XX(base_vha->req->length - 3); + tgt->datasegs_per_cmd = QLA_TGT_DATASEGS_PER_CMD_24XX; + tgt->datasegs_per_cont = QLA_TGT_DATASEGS_PER_CONT_24XX; + + if (base_vha->fc_vport) + return 0; + + mutex_lock(&qla_tgt_mutex); + list_add_tail(&tgt->tgt_list_entry, &qla_tgt_glist); + mutex_unlock(&qla_tgt_mutex); + + return 0; +} + +/* Must be called under tgt_host_action_mutex */ +int qlt_remove_target(struct qla_hw_data *ha, struct scsi_qla_host *vha) +{ + if (!vha->vha_tgt.qla_tgt) + return 0; + + if (vha->fc_vport) { + qlt_release(vha->vha_tgt.qla_tgt); + return 0; + } + mutex_lock(&qla_tgt_mutex); + list_del(&vha->vha_tgt.qla_tgt->tgt_list_entry); + mutex_unlock(&qla_tgt_mutex); + + ql_dbg(ql_dbg_tgt, vha, 0xe03c, "Unregistering target for host %ld(%p)", + vha->host_no, ha); + qlt_release(vha->vha_tgt.qla_tgt); + + return 0; +} + +static void qlt_lport_dump(struct scsi_qla_host *vha, u64 wwpn, + unsigned char *b) +{ + int i; + + pr_debug("qla2xxx HW vha->node_name: "); + for (i = 0; i < WWN_SIZE; i++) + pr_debug("%02x ", vha->node_name[i]); + pr_debug("\n"); + pr_debug("qla2xxx HW vha->port_name: "); + for (i = 0; i < WWN_SIZE; i++) + pr_debug("%02x ", vha->port_name[i]); + pr_debug("\n"); + + pr_debug("qla2xxx passed configfs WWPN: "); + put_unaligned_be64(wwpn, b); + for (i = 0; i < WWN_SIZE; i++) + pr_debug("%02x ", b[i]); + pr_debug("\n"); +} + +/** + * qla_tgt_lport_register - register lport with external module + * + * @qla_tgt_ops: Pointer for tcm_qla2xxx qla_tgt_ops + * @wwpn: Passwd FC target WWPN + * @callback: lport initialization callback for tcm_qla2xxx code + * @target_lport_ptr: pointer for tcm_qla2xxx specific lport data + */ +int qlt_lport_register(void *target_lport_ptr, u64 phys_wwpn, + u64 npiv_wwpn, u64 npiv_wwnn, + int (*callback)(struct scsi_qla_host *, void *, u64, u64)) +{ + struct qla_tgt *tgt; + struct scsi_qla_host *vha; + struct qla_hw_data *ha; + struct Scsi_Host *host; + unsigned long flags; + int rc; + u8 b[WWN_SIZE]; + + mutex_lock(&qla_tgt_mutex); + list_for_each_entry(tgt, &qla_tgt_glist, tgt_list_entry) { + vha = tgt->vha; + ha = vha->hw; + + host = vha->host; + if (!host) + continue; + + if (!(host->hostt->supported_mode & MODE_TARGET)) + continue; + + spin_lock_irqsave(&ha->hardware_lock, flags); + if ((!npiv_wwpn || !npiv_wwnn) && host->active_mode & MODE_TARGET) { + pr_debug("MODE_TARGET already active on qla2xxx(%d)\n", + host->host_no); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + continue; + } + if (tgt->tgt_stop) { + pr_debug("MODE_TARGET in shutdown on qla2xxx(%d)\n", + host->host_no); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + continue; + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + if (!scsi_host_get(host)) { + ql_dbg(ql_dbg_tgt, vha, 0xe068, + "Unable to scsi_host_get() for" + " qla2xxx scsi_host\n"); + continue; + } + qlt_lport_dump(vha, phys_wwpn, b); + + if (memcmp(vha->port_name, b, WWN_SIZE)) { + scsi_host_put(host); + continue; + } + rc = (*callback)(vha, target_lport_ptr, npiv_wwpn, npiv_wwnn); + if (rc != 0) + scsi_host_put(host); + + mutex_unlock(&qla_tgt_mutex); + return rc; + } + mutex_unlock(&qla_tgt_mutex); + + return -ENODEV; +} +EXPORT_SYMBOL(qlt_lport_register); + +/** + * qla_tgt_lport_deregister - Degister lport + * + * @vha: Registered scsi_qla_host pointer + */ +void qlt_lport_deregister(struct scsi_qla_host *vha) +{ + struct qla_hw_data *ha = vha->hw; + struct Scsi_Host *sh = vha->host; + /* + * Clear the target_lport_ptr qla_target_template pointer in qla_hw_data + */ + vha->vha_tgt.target_lport_ptr = NULL; + ha->tgt.tgt_ops = NULL; + /* + * Release the Scsi_Host reference for the underlying qla2xxx host + */ + scsi_host_put(sh); +} +EXPORT_SYMBOL(qlt_lport_deregister); + +/* Must be called under HW lock */ +void qlt_set_mode(struct scsi_qla_host *vha) +{ + struct qla_hw_data *ha = vha->hw; + + switch (ql2x_ini_mode) { + case QLA2XXX_INI_MODE_DISABLED: + case QLA2XXX_INI_MODE_EXCLUSIVE: + vha->host->active_mode = MODE_TARGET; + break; + case QLA2XXX_INI_MODE_ENABLED: + vha->host->active_mode |= MODE_TARGET; + break; + default: + break; + } + + if (ha->tgt.ini_mode_force_reverse) + qla_reverse_ini_mode(vha); +} + +/* Must be called under HW lock */ +void qlt_clear_mode(struct scsi_qla_host *vha) +{ + struct qla_hw_data *ha = vha->hw; + + switch (ql2x_ini_mode) { + case QLA2XXX_INI_MODE_DISABLED: + vha->host->active_mode = MODE_UNKNOWN; + break; + case QLA2XXX_INI_MODE_EXCLUSIVE: + vha->host->active_mode = MODE_INITIATOR; + break; + case QLA2XXX_INI_MODE_ENABLED: + vha->host->active_mode &= ~MODE_TARGET; + break; + default: + break; + } + + if (ha->tgt.ini_mode_force_reverse) + qla_reverse_ini_mode(vha); +} + +/* + * qla_tgt_enable_vha - NO LOCK HELD + * + * host_reset, bring up w/ Target Mode Enabled + */ +void +qlt_enable_vha(struct scsi_qla_host *vha) +{ + struct qla_hw_data *ha = vha->hw; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; + unsigned long flags; + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); + + if (!tgt) { + ql_dbg(ql_dbg_tgt, vha, 0xe069, + "Unable to locate qla_tgt pointer from" + " struct qla_hw_data\n"); + dump_stack(); + return; + } + + spin_lock_irqsave(&ha->hardware_lock, flags); + tgt->tgt_stopped = 0; + qlt_set_mode(vha); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + if (vha->vp_idx) { + qla24xx_disable_vp(vha); + qla24xx_enable_vp(vha); + } else { + set_bit(ISP_ABORT_NEEDED, &base_vha->dpc_flags); + qla2xxx_wake_dpc(base_vha); + qla2x00_wait_for_hba_online(base_vha); + } +} +EXPORT_SYMBOL(qlt_enable_vha); + +/* + * qla_tgt_disable_vha - NO LOCK HELD + * + * Disable Target Mode and reset the adapter + */ +void +qlt_disable_vha(struct scsi_qla_host *vha) +{ + struct qla_hw_data *ha = vha->hw; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; + unsigned long flags; + + if (!tgt) { + ql_dbg(ql_dbg_tgt, vha, 0xe06a, + "Unable to locate qla_tgt pointer from" + " struct qla_hw_data\n"); + dump_stack(); + return; + } + + spin_lock_irqsave(&ha->hardware_lock, flags); + qlt_clear_mode(vha); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + qla2x00_wait_for_hba_online(vha); +} + +/* + * Called from qla_init.c:qla24xx_vport_create() contex to setup + * the target mode specific struct scsi_qla_host and struct qla_hw_data + * members. + */ +void +qlt_vport_create(struct scsi_qla_host *vha, struct qla_hw_data *ha) +{ + if (!qla_tgt_mode_enabled(vha)) + return; + + vha->vha_tgt.qla_tgt = NULL; + + mutex_init(&vha->vha_tgt.tgt_mutex); + mutex_init(&vha->vha_tgt.tgt_host_action_mutex); + + qlt_clear_mode(vha); + + /* + * NOTE: Currently the value is kept the same for <24xx and + * >=24xx ISPs. If it is necessary to change it, + * the check should be added for specific ISPs, + * assigning the value appropriately. + */ + ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX; + + qlt_add_target(ha, vha); +} + +void +qlt_rff_id(struct scsi_qla_host *vha, struct ct_sns_req *ct_req) +{ + /* + * FC-4 Feature bit 0 indicates target functionality to the name server. + */ + if (qla_tgt_mode_enabled(vha)) { + if (qla_ini_mode_enabled(vha)) + ct_req->req.rff_id.fc4_feature = BIT_0 | BIT_1; + else + ct_req->req.rff_id.fc4_feature = BIT_0; + } else if (qla_ini_mode_enabled(vha)) { + ct_req->req.rff_id.fc4_feature = BIT_1; + } +} + +/* + * qlt_init_atio_q_entries() - Initializes ATIO queue entries. + * @ha: HA context + * + * Beginning of ATIO ring has initialization control block already built + * by nvram config routine. + * + * Returns 0 on success. + */ +void +qlt_init_atio_q_entries(struct scsi_qla_host *vha) +{ + struct qla_hw_data *ha = vha->hw; + uint16_t cnt; + struct atio_from_isp *pkt = (struct atio_from_isp *)ha->tgt.atio_ring; + + if (!qla_tgt_mode_enabled(vha)) + return; + + for (cnt = 0; cnt < ha->tgt.atio_q_length; cnt++) { + pkt->u.raw.signature = ATIO_PROCESSED; + pkt++; + } + +} + +/* + * qlt_24xx_process_atio_queue() - Process ATIO queue entries. + * @ha: SCSI driver HA context + */ +void +qlt_24xx_process_atio_queue(struct scsi_qla_host *vha) +{ + struct qla_hw_data *ha = vha->hw; + struct atio_from_isp *pkt; + int cnt, i; + + if (!vha->flags.online) + return; + + while (ha->tgt.atio_ring_ptr->signature != ATIO_PROCESSED) { + pkt = (struct atio_from_isp *)ha->tgt.atio_ring_ptr; + cnt = pkt->u.raw.entry_count; + + qlt_24xx_atio_pkt_all_vps(vha, (struct atio_from_isp *)pkt); + + for (i = 0; i < cnt; i++) { + ha->tgt.atio_ring_index++; + if (ha->tgt.atio_ring_index == ha->tgt.atio_q_length) { + ha->tgt.atio_ring_index = 0; + ha->tgt.atio_ring_ptr = ha->tgt.atio_ring; + } else + ha->tgt.atio_ring_ptr++; + + pkt->u.raw.signature = ATIO_PROCESSED; + pkt = (struct atio_from_isp *)ha->tgt.atio_ring_ptr; + } + wmb(); + } + + /* Adjust ring index */ + WRT_REG_DWORD(ISP_ATIO_Q_OUT(vha), ha->tgt.atio_ring_index); +} + +void +qlt_24xx_config_rings(struct scsi_qla_host *vha) +{ + struct qla_hw_data *ha = vha->hw; + if (!QLA_TGT_MODE_ENABLED()) + return; + + WRT_REG_DWORD(ISP_ATIO_Q_IN(vha), 0); + WRT_REG_DWORD(ISP_ATIO_Q_OUT(vha), 0); + RD_REG_DWORD(ISP_ATIO_Q_OUT(vha)); + + if (IS_ATIO_MSIX_CAPABLE(ha)) { + struct qla_msix_entry *msix = &ha->msix_entries[2]; + struct init_cb_24xx *icb = (struct init_cb_24xx *)ha->init_cb; + + icb->msix_atio = cpu_to_le16(msix->entry); + ql_dbg(ql_dbg_init, vha, 0xf072, + "Registering ICB vector 0x%x for atio que.\n", + msix->entry); + } +} + +void +qlt_24xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_24xx *nv) +{ + struct qla_hw_data *ha = vha->hw; + + if (qla_tgt_mode_enabled(vha)) { + if (!ha->tgt.saved_set) { + /* We save only once */ + ha->tgt.saved_exchange_count = nv->exchange_count; + ha->tgt.saved_firmware_options_1 = + nv->firmware_options_1; + ha->tgt.saved_firmware_options_2 = + nv->firmware_options_2; + ha->tgt.saved_firmware_options_3 = + nv->firmware_options_3; + ha->tgt.saved_set = 1; + } + + nv->exchange_count = __constant_cpu_to_le16(0xFFFF); + + /* Enable target mode */ + nv->firmware_options_1 |= __constant_cpu_to_le32(BIT_4); + + /* Disable ini mode, if requested */ + if (!qla_ini_mode_enabled(vha)) + nv->firmware_options_1 |= __constant_cpu_to_le32(BIT_5); + + /* Disable Full Login after LIP */ + nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_13); + /* Enable initial LIP */ + nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_9); + /* Enable FC tapes support */ + nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_12); + /* Disable Full Login after LIP */ + nv->host_p &= __constant_cpu_to_le32(~BIT_10); + /* Enable target PRLI control */ + nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_14); + } else { + if (ha->tgt.saved_set) { + nv->exchange_count = ha->tgt.saved_exchange_count; + nv->firmware_options_1 = + ha->tgt.saved_firmware_options_1; + nv->firmware_options_2 = + ha->tgt.saved_firmware_options_2; + nv->firmware_options_3 = + ha->tgt.saved_firmware_options_3; + } + return; + } + + /* out-of-order frames reassembly */ + nv->firmware_options_3 |= BIT_6|BIT_9; + + if (ha->tgt.enable_class_2) { + if (vha->flags.init_done) + fc_host_supported_classes(vha->host) = + FC_COS_CLASS2 | FC_COS_CLASS3; + + nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_8); + } else { + if (vha->flags.init_done) + fc_host_supported_classes(vha->host) = FC_COS_CLASS3; + + nv->firmware_options_2 &= ~__constant_cpu_to_le32(BIT_8); + } +} + +void +qlt_24xx_config_nvram_stage2(struct scsi_qla_host *vha, + struct init_cb_24xx *icb) +{ + struct qla_hw_data *ha = vha->hw; + + if (ha->tgt.node_name_set) { + memcpy(icb->node_name, ha->tgt.tgt_node_name, WWN_SIZE); + icb->firmware_options_1 |= __constant_cpu_to_le32(BIT_14); + } +} + +void +qlt_81xx_config_nvram_stage1(struct scsi_qla_host *vha, struct nvram_81xx *nv) +{ + struct qla_hw_data *ha = vha->hw; + + if (!QLA_TGT_MODE_ENABLED()) + return; + + if (qla_tgt_mode_enabled(vha)) { + if (!ha->tgt.saved_set) { + /* We save only once */ + ha->tgt.saved_exchange_count = nv->exchange_count; + ha->tgt.saved_firmware_options_1 = + nv->firmware_options_1; + ha->tgt.saved_firmware_options_2 = + nv->firmware_options_2; + ha->tgt.saved_firmware_options_3 = + nv->firmware_options_3; + ha->tgt.saved_set = 1; + } + + nv->exchange_count = __constant_cpu_to_le16(0xFFFF); + + /* Enable target mode */ + nv->firmware_options_1 |= __constant_cpu_to_le32(BIT_4); + + /* Disable ini mode, if requested */ + if (!qla_ini_mode_enabled(vha)) + nv->firmware_options_1 |= + __constant_cpu_to_le32(BIT_5); + + /* Disable Full Login after LIP */ + nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_13); + /* Enable initial LIP */ + nv->firmware_options_1 &= __constant_cpu_to_le32(~BIT_9); + /* Enable FC tapes support */ + nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_12); + /* Disable Full Login after LIP */ + nv->host_p &= __constant_cpu_to_le32(~BIT_10); + /* Enable target PRLI control */ + nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_14); + } else { + if (ha->tgt.saved_set) { + nv->exchange_count = ha->tgt.saved_exchange_count; + nv->firmware_options_1 = + ha->tgt.saved_firmware_options_1; + nv->firmware_options_2 = + ha->tgt.saved_firmware_options_2; + nv->firmware_options_3 = + ha->tgt.saved_firmware_options_3; + } + return; + } + + /* out-of-order frames reassembly */ + nv->firmware_options_3 |= BIT_6|BIT_9; + + if (ha->tgt.enable_class_2) { + if (vha->flags.init_done) + fc_host_supported_classes(vha->host) = + FC_COS_CLASS2 | FC_COS_CLASS3; + + nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_8); + } else { + if (vha->flags.init_done) + fc_host_supported_classes(vha->host) = FC_COS_CLASS3; + + nv->firmware_options_2 &= ~__constant_cpu_to_le32(BIT_8); + } +} + +void +qlt_81xx_config_nvram_stage2(struct scsi_qla_host *vha, + struct init_cb_81xx *icb) +{ + struct qla_hw_data *ha = vha->hw; + + if (!QLA_TGT_MODE_ENABLED()) + return; + + if (ha->tgt.node_name_set) { + memcpy(icb->node_name, ha->tgt.tgt_node_name, WWN_SIZE); + icb->firmware_options_1 |= __constant_cpu_to_le32(BIT_14); + } +} + +void +qlt_83xx_iospace_config(struct qla_hw_data *ha) +{ + if (!QLA_TGT_MODE_ENABLED()) + return; + + ha->msix_count += 1; /* For ATIO Q */ +} + +int +qlt_24xx_process_response_error(struct scsi_qla_host *vha, + struct sts_entry_24xx *pkt) +{ + switch (pkt->entry_type) { + case ABTS_RECV_24XX: + case ABTS_RESP_24XX: + case CTIO_TYPE7: + case NOTIFY_ACK_TYPE: + case CTIO_CRC2: + return 1; + default: + return 0; + } +} + +void +qlt_modify_vp_config(struct scsi_qla_host *vha, + struct vp_config_entry_24xx *vpmod) +{ + if (qla_tgt_mode_enabled(vha)) + vpmod->options_idx1 &= ~BIT_5; + /* Disable ini mode, if requested */ + if (!qla_ini_mode_enabled(vha)) + vpmod->options_idx1 &= ~BIT_4; +} + +void +qlt_probe_one_stage1(struct scsi_qla_host *base_vha, struct qla_hw_data *ha) +{ + if (!QLA_TGT_MODE_ENABLED()) + return; + + if (ha->mqenable || IS_QLA83XX(ha)) { + ISP_ATIO_Q_IN(base_vha) = &ha->mqiobase->isp25mq.atio_q_in; + ISP_ATIO_Q_OUT(base_vha) = &ha->mqiobase->isp25mq.atio_q_out; + } else { + ISP_ATIO_Q_IN(base_vha) = &ha->iobase->isp24.atio_q_in; + ISP_ATIO_Q_OUT(base_vha) = &ha->iobase->isp24.atio_q_out; + } + + mutex_init(&base_vha->vha_tgt.tgt_mutex); + mutex_init(&base_vha->vha_tgt.tgt_host_action_mutex); + qlt_clear_mode(base_vha); +} + +irqreturn_t +qla83xx_msix_atio_q(int irq, void *dev_id) +{ + struct rsp_que *rsp; + scsi_qla_host_t *vha; + struct qla_hw_data *ha; + unsigned long flags; + + rsp = (struct rsp_que *) dev_id; + ha = rsp->hw; + vha = pci_get_drvdata(ha->pdev); + + spin_lock_irqsave(&ha->hardware_lock, flags); + + qlt_24xx_process_atio_queue(vha); + qla24xx_process_response_queue(vha, rsp); + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return IRQ_HANDLED; +} + +int +qlt_mem_alloc(struct qla_hw_data *ha) +{ + if (!QLA_TGT_MODE_ENABLED()) + return 0; + + ha->tgt.tgt_vp_map = kzalloc(sizeof(struct qla_tgt_vp_map) * + MAX_MULTI_ID_FABRIC, GFP_KERNEL); + if (!ha->tgt.tgt_vp_map) + return -ENOMEM; + + ha->tgt.atio_ring = dma_alloc_coherent(&ha->pdev->dev, + (ha->tgt.atio_q_length + 1) * sizeof(struct atio_from_isp), + &ha->tgt.atio_dma, GFP_KERNEL); + if (!ha->tgt.atio_ring) { + kfree(ha->tgt.tgt_vp_map); + return -ENOMEM; + } + return 0; +} + +void +qlt_mem_free(struct qla_hw_data *ha) +{ + if (!QLA_TGT_MODE_ENABLED()) + return; + + if (ha->tgt.atio_ring) { + dma_free_coherent(&ha->pdev->dev, (ha->tgt.atio_q_length + 1) * + sizeof(struct atio_from_isp), ha->tgt.atio_ring, + ha->tgt.atio_dma); + } + kfree(ha->tgt.tgt_vp_map); +} + +/* vport_slock to be held by the caller */ +void +qlt_update_vp_map(struct scsi_qla_host *vha, int cmd) +{ + if (!QLA_TGT_MODE_ENABLED()) + return; + + switch (cmd) { + case SET_VP_IDX: + vha->hw->tgt.tgt_vp_map[vha->vp_idx].vha = vha; + break; + case SET_AL_PA: + vha->hw->tgt.tgt_vp_map[vha->d_id.b.al_pa].idx = vha->vp_idx; + break; + case RESET_VP_IDX: + vha->hw->tgt.tgt_vp_map[vha->vp_idx].vha = NULL; + break; + case RESET_AL_PA: + vha->hw->tgt.tgt_vp_map[vha->d_id.b.al_pa].idx = 0; + break; + } +} + +static int __init qlt_parse_ini_mode(void) +{ + if (strcasecmp(qlini_mode, QLA2XXX_INI_MODE_STR_EXCLUSIVE) == 0) + ql2x_ini_mode = QLA2XXX_INI_MODE_EXCLUSIVE; + else if (strcasecmp(qlini_mode, QLA2XXX_INI_MODE_STR_DISABLED) == 0) + ql2x_ini_mode = QLA2XXX_INI_MODE_DISABLED; + else if (strcasecmp(qlini_mode, QLA2XXX_INI_MODE_STR_ENABLED) == 0) + ql2x_ini_mode = QLA2XXX_INI_MODE_ENABLED; + else + return false; + + return true; +} + +int __init qlt_init(void) +{ + int ret; + + if (!qlt_parse_ini_mode()) { + ql_log(ql_log_fatal, NULL, 0xe06b, + "qlt_parse_ini_mode() failed\n"); + return -EINVAL; + } + + if (!QLA_TGT_MODE_ENABLED()) + return 0; + + qla_tgt_mgmt_cmd_cachep = kmem_cache_create("qla_tgt_mgmt_cmd_cachep", + sizeof(struct qla_tgt_mgmt_cmd), __alignof__(struct + qla_tgt_mgmt_cmd), 0, NULL); + if (!qla_tgt_mgmt_cmd_cachep) { + ql_log(ql_log_fatal, NULL, 0xe06d, + "kmem_cache_create for qla_tgt_mgmt_cmd_cachep failed\n"); + return -ENOMEM; + } + + qla_tgt_mgmt_cmd_mempool = mempool_create(25, mempool_alloc_slab, + mempool_free_slab, qla_tgt_mgmt_cmd_cachep); + if (!qla_tgt_mgmt_cmd_mempool) { + ql_log(ql_log_fatal, NULL, 0xe06e, + "mempool_create for qla_tgt_mgmt_cmd_mempool failed\n"); + ret = -ENOMEM; + goto out_mgmt_cmd_cachep; + } + + qla_tgt_wq = alloc_workqueue("qla_tgt_wq", 0, 0); + if (!qla_tgt_wq) { + ql_log(ql_log_fatal, NULL, 0xe06f, + "alloc_workqueue for qla_tgt_wq failed\n"); + ret = -ENOMEM; + goto out_cmd_mempool; + } + /* + * Return 1 to signal that initiator-mode is being disabled + */ + return (ql2x_ini_mode == QLA2XXX_INI_MODE_DISABLED) ? 1 : 0; + +out_cmd_mempool: + mempool_destroy(qla_tgt_mgmt_cmd_mempool); +out_mgmt_cmd_cachep: + kmem_cache_destroy(qla_tgt_mgmt_cmd_cachep); + return ret; +} + +void qlt_exit(void) +{ + if (!QLA_TGT_MODE_ENABLED()) + return; + + destroy_workqueue(qla_tgt_wq); + mempool_destroy(qla_tgt_mgmt_cmd_mempool); + kmem_cache_destroy(qla_tgt_mgmt_cmd_cachep); +} diff --git a/drivers/scsi/qla2xxx/qla_target.h b/drivers/scsi/qla2xxx/qla_target.h new file mode 100644 index 00000000000..d1d24fb0160 --- /dev/null +++ b/drivers/scsi/qla2xxx/qla_target.h @@ -0,0 +1,1093 @@ +/* + * Copyright (C) 2004 - 2010 Vladislav Bolkhovitin <vst@vlnb.net> + * Copyright (C) 2004 - 2005 Leonid Stoljar + * Copyright (C) 2006 Nathaniel Clark <nate@misrule.us> + * Copyright (C) 2007 - 2010 ID7 Ltd. + * + * Forward port and refactoring to modern qla2xxx and target/configfs + * + * Copyright (C) 2010-2011 Nicholas A. Bellinger <nab@kernel.org> + * + * Additional file for the target driver support. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ +/* + * This is the global def file that is useful for including from the + * target portion. + */ + +#ifndef __QLA_TARGET_H +#define __QLA_TARGET_H + +#include "qla_def.h" + +/* + * Must be changed on any change in any initiator visible interfaces or + * data in the target add-on + */ +#define QLA2XXX_TARGET_MAGIC 269 + +/* + * Must be changed on any change in any target visible interfaces or + * data in the initiator + */ +#define QLA2XXX_INITIATOR_MAGIC 57222 + +#define QLA2XXX_INI_MODE_STR_EXCLUSIVE "exclusive" +#define QLA2XXX_INI_MODE_STR_DISABLED "disabled" +#define QLA2XXX_INI_MODE_STR_ENABLED "enabled" + +#define QLA2XXX_INI_MODE_EXCLUSIVE 0 +#define QLA2XXX_INI_MODE_DISABLED 1 +#define QLA2XXX_INI_MODE_ENABLED 2 + +#define QLA2XXX_COMMAND_COUNT_INIT 250 +#define QLA2XXX_IMMED_NOTIFY_COUNT_INIT 250 + +/* + * Used to mark which completion handles (for RIO Status's) are for CTIO's + * vs. regular (non-target) info. This is checked for in + * qla2x00_process_response_queue() to see if a handle coming back in a + * multi-complete should come to the tgt driver or be handled there by qla2xxx + */ +#define CTIO_COMPLETION_HANDLE_MARK BIT_29 +#if (CTIO_COMPLETION_HANDLE_MARK <= DEFAULT_OUTSTANDING_COMMANDS) +#error "CTIO_COMPLETION_HANDLE_MARK not larger than " + "DEFAULT_OUTSTANDING_COMMANDS" +#endif +#define HANDLE_IS_CTIO_COMP(h) (h & CTIO_COMPLETION_HANDLE_MARK) + +/* Used to mark CTIO as intermediate */ +#define CTIO_INTERMEDIATE_HANDLE_MARK BIT_30 + +#ifndef OF_SS_MODE_0 +/* + * ISP target entries - Flags bit definitions. + */ +#define OF_SS_MODE_0 0 +#define OF_SS_MODE_1 1 +#define OF_SS_MODE_2 2 +#define OF_SS_MODE_3 3 + +#define OF_EXPL_CONF BIT_5 /* Explicit Confirmation Requested */ +#define OF_DATA_IN BIT_6 /* Data in to initiator */ + /* (data from target to initiator) */ +#define OF_DATA_OUT BIT_7 /* Data out from initiator */ + /* (data from initiator to target) */ +#define OF_NO_DATA (BIT_7 | BIT_6) +#define OF_INC_RC BIT_8 /* Increment command resource count */ +#define OF_FAST_POST BIT_9 /* Enable mailbox fast posting. */ +#define OF_CONF_REQ BIT_13 /* Confirmation Requested */ +#define OF_TERM_EXCH BIT_14 /* Terminate exchange */ +#define OF_SSTS BIT_15 /* Send SCSI status */ +#endif + +#ifndef QLA_TGT_DATASEGS_PER_CMD32 +#define QLA_TGT_DATASEGS_PER_CMD32 3 +#define QLA_TGT_DATASEGS_PER_CONT32 7 +#define QLA_TGT_MAX_SG32(ql) \ + (((ql) > 0) ? (QLA_TGT_DATASEGS_PER_CMD32 + \ + QLA_TGT_DATASEGS_PER_CONT32*((ql) - 1)) : 0) + +#define QLA_TGT_DATASEGS_PER_CMD64 2 +#define QLA_TGT_DATASEGS_PER_CONT64 5 +#define QLA_TGT_MAX_SG64(ql) \ + (((ql) > 0) ? (QLA_TGT_DATASEGS_PER_CMD64 + \ + QLA_TGT_DATASEGS_PER_CONT64*((ql) - 1)) : 0) +#endif + +#ifndef QLA_TGT_DATASEGS_PER_CMD_24XX +#define QLA_TGT_DATASEGS_PER_CMD_24XX 1 +#define QLA_TGT_DATASEGS_PER_CONT_24XX 5 +#define QLA_TGT_MAX_SG_24XX(ql) \ + (min(1270, ((ql) > 0) ? (QLA_TGT_DATASEGS_PER_CMD_24XX + \ + QLA_TGT_DATASEGS_PER_CONT_24XX*((ql) - 1)) : 0)) +#endif +#endif + +#define GET_TARGET_ID(ha, iocb) ((HAS_EXTENDED_IDS(ha)) \ + ? le16_to_cpu((iocb)->u.isp2x.target.extended) \ + : (uint16_t)(iocb)->u.isp2x.target.id.standard) + +#ifndef IMMED_NOTIFY_TYPE +#define IMMED_NOTIFY_TYPE 0x0D /* Immediate notify entry. */ +/* + * ISP queue - immediate notify entry structure definition. + * This is sent by the ISP to the Target driver. + * This IOCB would have report of events sent by the + * initiator, that needs to be handled by the target + * driver immediately. + */ +struct imm_ntfy_from_isp { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + union { + struct { + uint32_t sys_define_2; /* System defined. */ + target_id_t target; + uint16_t lun; + uint8_t target_id; + uint8_t reserved_1; + uint16_t status_modifier; + uint16_t status; + uint16_t task_flags; + uint16_t seq_id; + uint16_t srr_rx_id; + uint32_t srr_rel_offs; + uint16_t srr_ui; +#define SRR_IU_DATA_IN 0x1 +#define SRR_IU_DATA_OUT 0x5 +#define SRR_IU_STATUS 0x7 + uint16_t srr_ox_id; + uint8_t reserved_2[28]; + } isp2x; + struct { + uint32_t reserved; + uint16_t nport_handle; + uint16_t reserved_2; + uint16_t flags; +#define NOTIFY24XX_FLAGS_GLOBAL_TPRLO BIT_1 +#define NOTIFY24XX_FLAGS_PUREX_IOCB BIT_0 + uint16_t srr_rx_id; + uint16_t status; + uint8_t status_subcode; + uint8_t fw_handle; + uint32_t exchange_address; + uint32_t srr_rel_offs; + uint16_t srr_ui; + uint16_t srr_ox_id; + uint8_t reserved_4[19]; + uint8_t vp_index; + uint32_t reserved_5; + uint8_t port_id[3]; + uint8_t reserved_6; + } isp24; + } u; + uint16_t reserved_7; + uint16_t ox_id; +} __packed; +#endif + +#ifndef NOTIFY_ACK_TYPE +#define NOTIFY_ACK_TYPE 0x0E /* Notify acknowledge entry. */ +/* + * ISP queue - notify acknowledge entry structure definition. + * This is sent to the ISP from the target driver. + */ +struct nack_to_isp { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + union { + struct { + uint32_t sys_define_2; /* System defined. */ + target_id_t target; + uint8_t target_id; + uint8_t reserved_1; + uint16_t flags; + uint16_t resp_code; + uint16_t status; + uint16_t task_flags; + uint16_t seq_id; + uint16_t srr_rx_id; + uint32_t srr_rel_offs; + uint16_t srr_ui; + uint16_t srr_flags; + uint16_t srr_reject_code; + uint8_t srr_reject_vendor_uniq; + uint8_t srr_reject_code_expl; + uint8_t reserved_2[24]; + } isp2x; + struct { + uint32_t handle; + uint16_t nport_handle; + uint16_t reserved_1; + uint16_t flags; + uint16_t srr_rx_id; + uint16_t status; + uint8_t status_subcode; + uint8_t fw_handle; + uint32_t exchange_address; + uint32_t srr_rel_offs; + uint16_t srr_ui; + uint16_t srr_flags; + uint8_t reserved_4[19]; + uint8_t vp_index; + uint8_t srr_reject_vendor_uniq; + uint8_t srr_reject_code_expl; + uint8_t srr_reject_code; + uint8_t reserved_5[5]; + } isp24; + } u; + uint8_t reserved[2]; + uint16_t ox_id; +} __packed; +#define NOTIFY_ACK_SRR_FLAGS_ACCEPT 0 +#define NOTIFY_ACK_SRR_FLAGS_REJECT 1 + +#define NOTIFY_ACK_SRR_REJECT_REASON_UNABLE_TO_PERFORM 0x9 + +#define NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_NO_EXPL 0 +#define NOTIFY_ACK_SRR_FLAGS_REJECT_EXPL_UNABLE_TO_SUPPLY_DATA 0x2a + +#define NOTIFY_ACK_SUCCESS 0x01 +#endif + +#ifndef ACCEPT_TGT_IO_TYPE +#define ACCEPT_TGT_IO_TYPE 0x16 /* Accept target I/O entry. */ +#endif + +#ifndef CONTINUE_TGT_IO_TYPE +#define CONTINUE_TGT_IO_TYPE 0x17 +/* + * ISP queue - Continue Target I/O (CTIO) entry for status mode 0 structure. + * This structure is sent to the ISP 2xxx from target driver. + */ +struct ctio_to_2xxx { + 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 defined handle */ + target_id_t target; + uint16_t rx_id; + uint16_t flags; + uint16_t status; + uint16_t timeout; /* 0 = 30 seconds, 0xFFFF = disable */ + uint16_t dseg_count; /* Data segment count. */ + uint32_t relative_offset; + uint32_t residual; + uint16_t reserved_1[3]; + uint16_t scsi_status; + uint32_t transfer_length; + 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. */ +} __packed; +#define ATIO_PATH_INVALID 0x07 +#define ATIO_CANT_PROV_CAP 0x16 +#define ATIO_CDB_VALID 0x3D + +#define ATIO_EXEC_READ BIT_1 +#define ATIO_EXEC_WRITE BIT_0 +#endif + +#ifndef CTIO_A64_TYPE +#define CTIO_A64_TYPE 0x1F +#define CTIO_SUCCESS 0x01 +#define CTIO_ABORTED 0x02 +#define CTIO_INVALID_RX_ID 0x08 +#define CTIO_TIMEOUT 0x0B +#define CTIO_DIF_ERROR 0x0C /* DIF error detected */ +#define CTIO_LIP_RESET 0x0E +#define CTIO_TARGET_RESET 0x17 +#define CTIO_PORT_UNAVAILABLE 0x28 +#define CTIO_PORT_LOGGED_OUT 0x29 +#define CTIO_PORT_CONF_CHANGED 0x2A +#define CTIO_SRR_RECEIVED 0x45 +#endif + +#ifndef CTIO_RET_TYPE +#define CTIO_RET_TYPE 0x17 /* CTIO return entry */ +#define ATIO_TYPE7 0x06 /* Accept target I/O entry for 24xx */ + +struct fcp_hdr { + uint8_t r_ctl; + uint8_t d_id[3]; + uint8_t cs_ctl; + uint8_t s_id[3]; + uint8_t type; + uint8_t f_ctl[3]; + uint8_t seq_id; + uint8_t df_ctl; + uint16_t seq_cnt; + __be16 ox_id; + uint16_t rx_id; + uint32_t parameter; +} __packed; + +struct fcp_hdr_le { + uint8_t d_id[3]; + uint8_t r_ctl; + uint8_t s_id[3]; + uint8_t cs_ctl; + uint8_t f_ctl[3]; + uint8_t type; + uint16_t seq_cnt; + uint8_t df_ctl; + uint8_t seq_id; + uint16_t rx_id; + uint16_t ox_id; + uint32_t parameter; +} __packed; + +#define F_CTL_EXCH_CONTEXT_RESP BIT_23 +#define F_CTL_SEQ_CONTEXT_RESIP BIT_22 +#define F_CTL_LAST_SEQ BIT_20 +#define F_CTL_END_SEQ BIT_19 +#define F_CTL_SEQ_INITIATIVE BIT_16 + +#define R_CTL_BASIC_LINK_SERV 0x80 +#define R_CTL_B_ACC 0x4 +#define R_CTL_B_RJT 0x5 + +struct atio7_fcp_cmnd { + uint64_t lun; + uint8_t cmnd_ref; + uint8_t task_attr:3; + uint8_t reserved:5; + uint8_t task_mgmt_flags; +#define FCP_CMND_TASK_MGMT_CLEAR_ACA 6 +#define FCP_CMND_TASK_MGMT_TARGET_RESET 5 +#define FCP_CMND_TASK_MGMT_LU_RESET 4 +#define FCP_CMND_TASK_MGMT_CLEAR_TASK_SET 2 +#define FCP_CMND_TASK_MGMT_ABORT_TASK_SET 1 + uint8_t wrdata:1; + uint8_t rddata:1; + uint8_t add_cdb_len:6; + uint8_t cdb[16]; + /* + * add_cdb is optional and can absent from struct atio7_fcp_cmnd. Size 4 + * only to make sizeof(struct atio7_fcp_cmnd) be as expected by + * BUILD_BUG_ON in qlt_init(). + */ + uint8_t add_cdb[4]; + /* uint32_t data_length; */ +} __packed; + +/* + * ISP queue - Accept Target I/O (ATIO) type entry IOCB structure. + * This is sent from the ISP to the target driver. + */ +struct atio_from_isp { + union { + struct { + uint16_t entry_hdr; + uint8_t sys_define; /* System defined. */ + uint8_t entry_status; /* Entry Status. */ + uint32_t sys_define_2; /* System defined. */ + target_id_t target; + uint16_t rx_id; + uint16_t flags; + uint16_t status; + uint8_t command_ref; + uint8_t task_codes; + uint8_t task_flags; + uint8_t execution_codes; + uint8_t cdb[MAX_CMDSZ]; + uint32_t data_length; + uint16_t lun; + uint8_t initiator_port_name[WWN_SIZE]; /* on qla23xx */ + uint16_t reserved_32[6]; + uint16_t ox_id; + } isp2x; + struct { + uint16_t entry_hdr; + uint8_t fcp_cmnd_len_low; + uint8_t fcp_cmnd_len_high:4; + uint8_t attr:4; + uint32_t exchange_addr; +#define ATIO_EXCHANGE_ADDRESS_UNKNOWN 0xFFFFFFFF + struct fcp_hdr fcp_hdr; + struct atio7_fcp_cmnd fcp_cmnd; + } isp24; + struct { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t data[58]; + uint32_t signature; +#define ATIO_PROCESSED 0xDEADDEAD /* Signature */ + } raw; + } u; +} __packed; + +#define CTIO_TYPE7 0x12 /* Continue target I/O entry (for 24xx) */ + +/* + * ISP queue - Continue Target I/O (ATIO) type 7 entry (for 24xx) structure. + * This structure is sent to the ISP 24xx from the target driver. + */ + +struct ctio7_to_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 defined handle */ + uint16_t nport_handle; +#define CTIO7_NHANDLE_UNRECOGNIZED 0xFFFF + uint16_t timeout; + uint16_t dseg_count; /* Data segment count. */ + uint8_t vp_index; + uint8_t add_flags; + uint8_t initiator_id[3]; + uint8_t reserved; + uint32_t exchange_addr; + union { + struct { + uint16_t reserved1; + __le16 flags; + uint32_t residual; + __le16 ox_id; + uint16_t scsi_status; + uint32_t relative_offset; + uint32_t reserved2; + uint32_t transfer_length; + uint32_t reserved3; + /* Data segment 0 address. */ + uint32_t dseg_0_address[2]; + /* Data segment 0 length. */ + uint32_t dseg_0_length; + } status0; + struct { + uint16_t sense_length; + uint16_t flags; + uint32_t residual; + __le16 ox_id; + uint16_t scsi_status; + uint16_t response_len; + uint16_t reserved; + uint8_t sense_data[24]; + } status1; + } u; +} __packed; + +/* + * ISP queue - CTIO type 7 from ISP 24xx to target driver + * returned entry structure. + */ +struct ctio7_from_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 defined handle */ + uint16_t status; + uint16_t timeout; + uint16_t dseg_count; /* Data segment count. */ + uint8_t vp_index; + uint8_t reserved1[5]; + uint32_t exchange_address; + uint16_t reserved2; + uint16_t flags; + uint32_t residual; + uint16_t ox_id; + uint16_t reserved3; + uint32_t relative_offset; + uint8_t reserved4[24]; +} __packed; + +/* CTIO7 flags values */ +#define CTIO7_FLAGS_SEND_STATUS BIT_15 +#define CTIO7_FLAGS_TERMINATE BIT_14 +#define CTIO7_FLAGS_CONFORM_REQ BIT_13 +#define CTIO7_FLAGS_DONT_RET_CTIO BIT_8 +#define CTIO7_FLAGS_STATUS_MODE_0 0 +#define CTIO7_FLAGS_STATUS_MODE_1 BIT_6 +#define CTIO7_FLAGS_STATUS_MODE_2 BIT_7 +#define CTIO7_FLAGS_EXPLICIT_CONFORM BIT_5 +#define CTIO7_FLAGS_CONFIRM_SATISF BIT_4 +#define CTIO7_FLAGS_DSD_PTR BIT_2 +#define CTIO7_FLAGS_DATA_IN BIT_1 /* data to initiator */ +#define CTIO7_FLAGS_DATA_OUT BIT_0 /* data from initiator */ + +#define ELS_PLOGI 0x3 +#define ELS_FLOGI 0x4 +#define ELS_LOGO 0x5 +#define ELS_PRLI 0x20 +#define ELS_PRLO 0x21 +#define ELS_TPRLO 0x24 +#define ELS_PDISC 0x50 +#define ELS_ADISC 0x52 + +/* + *CTIO Type CRC_2 IOCB + */ +struct ctio_crc2_to_fw { + uint8_t entry_type; /* Entry type. */ +#define CTIO_CRC2 0x7A + 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. */ + __le16 timeout; /* Command timeout. */ + + uint16_t dseg_count; /* Data segment count. */ + uint8_t vp_index; + uint8_t add_flags; /* additional flags */ +#define CTIO_CRC2_AF_DIF_DSD_ENA BIT_3 + + uint8_t initiator_id[3]; /* initiator ID */ + uint8_t reserved1; + uint32_t exchange_addr; /* rcv exchange address */ + uint16_t reserved2; + __le16 flags; /* refer to CTIO7 flags values */ + uint32_t residual; + __le16 ox_id; + uint16_t scsi_status; + __le32 relative_offset; + uint32_t reserved5; + __le32 transfer_length; /* total fc transfer length */ + uint32_t reserved6; + __le32 crc_context_address[2];/* Data segment address. */ + uint16_t crc_context_len; /* Data segment length. */ + uint16_t reserved_1; /* MUST be set to 0. */ +} __packed; + +/* CTIO Type CRC_x Status IOCB */ +struct ctio_crc_from_fw { + 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 status; + uint16_t timeout; /* Command timeout. */ + uint16_t dseg_count; /* Data segment count. */ + uint32_t reserved1; + uint16_t state_flags; +#define CTIO_CRC_SF_DIF_CHOPPED BIT_4 + + uint32_t exchange_address; /* rcv exchange address */ + uint16_t reserved2; + uint16_t flags; + uint32_t resid_xfer_length; + uint16_t ox_id; + uint8_t reserved3[12]; + uint16_t runt_guard; /* reported runt blk guard */ + uint8_t actual_dif[8]; + uint8_t expected_dif[8]; +} __packed; + +/* + * ISP queue - ABTS received/response entries structure definition for 24xx. + */ +#define ABTS_RECV_24XX 0x54 /* ABTS received (for 24xx) */ +#define ABTS_RESP_24XX 0x55 /* ABTS responce (for 24xx) */ + +/* + * ISP queue - ABTS received IOCB entry structure definition for 24xx. + * The ABTS BLS received from the wire is sent to the + * target driver by the ISP 24xx. + * The IOCB is placed on the response queue. + */ +struct abts_recv_from_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. */ + uint8_t reserved_1[6]; + uint16_t nport_handle; + uint8_t reserved_2[2]; + uint8_t vp_index; + uint8_t reserved_3:4; + uint8_t sof_type:4; + uint32_t exchange_address; + struct fcp_hdr_le fcp_hdr_le; + uint8_t reserved_4[16]; + uint32_t exchange_addr_to_abort; +} __packed; + +#define ABTS_PARAM_ABORT_SEQ BIT_0 + +struct ba_acc_le { + uint16_t reserved; + uint8_t seq_id_last; + uint8_t seq_id_valid; +#define SEQ_ID_VALID 0x80 +#define SEQ_ID_INVALID 0x00 + uint16_t rx_id; + uint16_t ox_id; + uint16_t high_seq_cnt; + uint16_t low_seq_cnt; +} __packed; + +struct ba_rjt_le { + uint8_t vendor_uniq; + uint8_t reason_expl; + uint8_t reason_code; +#define BA_RJT_REASON_CODE_INVALID_COMMAND 0x1 +#define BA_RJT_REASON_CODE_UNABLE_TO_PERFORM 0x9 + uint8_t reserved; +} __packed; + +/* + * ISP queue - ABTS Response IOCB entry structure definition for 24xx. + * The ABTS response to the ABTS received is sent by the + * target driver to the ISP 24xx. + * The IOCB is placed on the request queue. + */ +struct abts_resp_to_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; + uint16_t reserved_1; + uint16_t nport_handle; + uint16_t control_flags; +#define ABTS_CONTR_FLG_TERM_EXCHG BIT_0 + uint8_t vp_index; + uint8_t reserved_3:4; + uint8_t sof_type:4; + uint32_t exchange_address; + struct fcp_hdr_le fcp_hdr_le; + union { + struct ba_acc_le ba_acct; + struct ba_rjt_le ba_rjt; + } __packed payload; + uint32_t reserved_4; + uint32_t exchange_addr_to_abort; +} __packed; + +/* + * ISP queue - ABTS Response IOCB from ISP24xx Firmware entry structure. + * The ABTS response with completion status to the ABTS response + * (sent by the target driver to the ISP 24xx) is sent by the + * ISP24xx firmware to the target driver. + * The IOCB is placed on the response queue. + */ +struct abts_resp_from_24xx_fw { + 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; + uint16_t compl_status; +#define ABTS_RESP_COMPL_SUCCESS 0 +#define ABTS_RESP_COMPL_SUBCODE_ERROR 0x31 + uint16_t nport_handle; + uint16_t reserved_1; + uint8_t reserved_2; + uint8_t reserved_3:4; + uint8_t sof_type:4; + uint32_t exchange_address; + struct fcp_hdr_le fcp_hdr_le; + uint8_t reserved_4[8]; + uint32_t error_subcode1; +#define ABTS_RESP_SUBCODE_ERR_ABORTED_EXCH_NOT_TERM 0x1E + uint32_t error_subcode2; + uint32_t exchange_addr_to_abort; +} __packed; + +/********************************************************************\ + * Type Definitions used by initiator & target halves +\********************************************************************/ + +struct qla_tgt_mgmt_cmd; +struct qla_tgt_sess; + +/* + * This structure provides a template of function calls that the + * target driver (from within qla_target.c) can issue to the + * target module (tcm_qla2xxx). + */ +struct qla_tgt_func_tmpl { + + int (*handle_cmd)(struct scsi_qla_host *, struct qla_tgt_cmd *, + unsigned char *, uint32_t, int, int, int); + void (*handle_data)(struct qla_tgt_cmd *); + void (*handle_dif_err)(struct qla_tgt_cmd *); + int (*handle_tmr)(struct qla_tgt_mgmt_cmd *, uint32_t, uint8_t, + uint32_t); + void (*free_cmd)(struct qla_tgt_cmd *); + void (*free_mcmd)(struct qla_tgt_mgmt_cmd *); + void (*free_session)(struct qla_tgt_sess *); + + int (*check_initiator_node_acl)(struct scsi_qla_host *, unsigned char *, + void *, uint8_t *, uint16_t); + void (*update_sess)(struct qla_tgt_sess *, port_id_t, uint16_t, bool); + struct qla_tgt_sess *(*find_sess_by_loop_id)(struct scsi_qla_host *, + const uint16_t); + struct qla_tgt_sess *(*find_sess_by_s_id)(struct scsi_qla_host *, + const uint8_t *); + void (*clear_nacl_from_fcport_map)(struct qla_tgt_sess *); + void (*put_sess)(struct qla_tgt_sess *); + void (*shutdown_sess)(struct qla_tgt_sess *); +}; + +int qla2x00_wait_for_hba_online(struct scsi_qla_host *); + +#include <target/target_core_base.h> + +#define QLA_TGT_TIMEOUT 10 /* in seconds */ + +#define QLA_TGT_MAX_HW_PENDING_TIME 60 /* in seconds */ + +/* Immediate notify status constants */ +#define IMM_NTFY_LIP_RESET 0x000E +#define IMM_NTFY_LIP_LINK_REINIT 0x000F +#define IMM_NTFY_IOCB_OVERFLOW 0x0016 +#define IMM_NTFY_ABORT_TASK 0x0020 +#define IMM_NTFY_PORT_LOGOUT 0x0029 +#define IMM_NTFY_PORT_CONFIG 0x002A +#define IMM_NTFY_GLBL_TPRLO 0x002D +#define IMM_NTFY_GLBL_LOGO 0x002E +#define IMM_NTFY_RESOURCE 0x0034 +#define IMM_NTFY_MSG_RX 0x0036 +#define IMM_NTFY_SRR 0x0045 +#define IMM_NTFY_ELS 0x0046 + +/* Immediate notify task flags */ +#define IMM_NTFY_TASK_MGMT_SHIFT 8 + +#define QLA_TGT_CLEAR_ACA 0x40 +#define QLA_TGT_TARGET_RESET 0x20 +#define QLA_TGT_LUN_RESET 0x10 +#define QLA_TGT_CLEAR_TS 0x04 +#define QLA_TGT_ABORT_TS 0x02 +#define QLA_TGT_ABORT_ALL_SESS 0xFFFF +#define QLA_TGT_ABORT_ALL 0xFFFE +#define QLA_TGT_NEXUS_LOSS_SESS 0xFFFD +#define QLA_TGT_NEXUS_LOSS 0xFFFC + +/* Notify Acknowledge flags */ +#define NOTIFY_ACK_RES_COUNT BIT_8 +#define NOTIFY_ACK_CLEAR_LIP_RESET BIT_5 +#define NOTIFY_ACK_TM_RESP_CODE_VALID BIT_4 + +/* Command's states */ +#define QLA_TGT_STATE_NEW 0 /* New command + target processing */ +#define QLA_TGT_STATE_NEED_DATA 1 /* target needs data to continue */ +#define QLA_TGT_STATE_DATA_IN 2 /* Data arrived + target processing */ +#define QLA_TGT_STATE_PROCESSED 3 /* target done processing */ +#define QLA_TGT_STATE_ABORTED 4 /* Command aborted */ + +/* Special handles */ +#define QLA_TGT_NULL_HANDLE 0 +#define QLA_TGT_SKIP_HANDLE (0xFFFFFFFF & ~CTIO_COMPLETION_HANDLE_MARK) + +/* ATIO task_codes field */ +#define ATIO_SIMPLE_QUEUE 0 +#define ATIO_HEAD_OF_QUEUE 1 +#define ATIO_ORDERED_QUEUE 2 +#define ATIO_ACA_QUEUE 4 +#define ATIO_UNTAGGED 5 + +/* TM failed response codes, see FCP (9.4.11 FCP_RSP_INFO) */ +#define FC_TM_SUCCESS 0 +#define FC_TM_BAD_FCP_DATA 1 +#define FC_TM_BAD_CMD 2 +#define FC_TM_FCP_DATA_MISMATCH 3 +#define FC_TM_REJECT 4 +#define FC_TM_FAILED 5 + +/* + * Error code of qlt_pre_xmit_response() meaning that cmd's exchange was + * terminated, so no more actions is needed and success should be returned + * to target. + */ +#define QLA_TGT_PRE_XMIT_RESP_CMD_ABORTED 0x1717 + +#if (BITS_PER_LONG > 32) || defined(CONFIG_HIGHMEM64G) +#define pci_dma_lo32(a) (a & 0xffffffff) +#define pci_dma_hi32(a) ((((a) >> 16)>>16) & 0xffffffff) +#else +#define pci_dma_lo32(a) (a & 0xffffffff) +#define pci_dma_hi32(a) 0 +#endif + +#define QLA_TGT_SENSE_VALID(sense) ((sense != NULL) && \ + (((const uint8_t *)(sense))[0] & 0x70) == 0x70) + +struct qla_port_24xx_data { + uint8_t port_name[WWN_SIZE]; + uint16_t loop_id; + uint16_t reserved; +}; + +struct qla_tgt { + struct scsi_qla_host *vha; + struct qla_hw_data *ha; + + /* + * To sync between IRQ handlers and qlt_target_release(). Needed, + * because req_pkt() can drop/reaquire HW lock inside. Protected by + * HW lock. + */ + int irq_cmd_count; + + int datasegs_per_cmd, datasegs_per_cont, sg_tablesize; + + /* Target's flags, serialized by pha->hardware_lock */ + unsigned int tgt_enable_64bit_addr:1; /* 64-bits PCI addr enabled */ + unsigned int link_reinit_iocb_pending:1; + + /* + * Protected by tgt_mutex AND hardware_lock for writing and tgt_mutex + * OR hardware_lock for reading. + */ + int tgt_stop; /* the target mode driver is being stopped */ + int tgt_stopped; /* the target mode driver has been stopped */ + + /* Count of sessions refering qla_tgt. Protected by hardware_lock. */ + int sess_count; + + /* Protected by hardware_lock. Addition also protected by tgt_mutex. */ + struct list_head sess_list; + + /* Protected by hardware_lock */ + struct list_head del_sess_list; + struct delayed_work sess_del_work; + + spinlock_t sess_work_lock; + struct list_head sess_works_list; + struct work_struct sess_work; + + struct imm_ntfy_from_isp link_reinit_iocb; + wait_queue_head_t waitQ; + int notify_ack_expected; + int abts_resp_expected; + int modify_lun_expected; + + int ctio_srr_id; + int imm_srr_id; + spinlock_t srr_lock; + struct list_head srr_ctio_list; + struct list_head srr_imm_list; + struct work_struct srr_work; + + atomic_t tgt_global_resets_count; + + struct list_head tgt_list_entry; +}; + +struct qla_tgt_sess_op { + struct scsi_qla_host *vha; + struct atio_from_isp atio; + struct work_struct work; +}; + +/* + * Equivilant to IT Nexus (Initiator-Target) + */ +struct qla_tgt_sess { + uint16_t loop_id; + port_id_t s_id; + + unsigned int conf_compl_supported:1; + unsigned int deleted:1; + unsigned int local:1; + + struct se_session *se_sess; + struct scsi_qla_host *vha; + struct qla_tgt *tgt; + + struct list_head sess_list_entry; + unsigned long expires; + struct list_head del_list_entry; + + uint8_t port_name[WWN_SIZE]; + struct work_struct free_work; +}; + +struct qla_tgt_cmd { + struct se_cmd se_cmd; + struct qla_tgt_sess *sess; + int state; + struct work_struct free_work; + struct work_struct work; + /* Sense buffer that will be mapped into outgoing status */ + unsigned char sense_buffer[TRANSPORT_SENSE_BUFFER]; + + /* to save extra sess dereferences */ + unsigned int conf_compl_supported:1; + unsigned int sg_mapped:1; + unsigned int free_sg:1; + unsigned int aborted:1; /* Needed in case of SRR */ + unsigned int write_data_transferred:1; + unsigned int ctx_dsd_alloced:1; + + struct scatterlist *sg; /* cmd data buffer SG vector */ + int sg_cnt; /* SG segments count */ + int bufflen; /* cmd buffer length */ + int offset; + uint32_t tag; + uint32_t unpacked_lun; + enum dma_data_direction dma_data_direction; + + uint16_t loop_id; /* to save extra sess dereferences */ + struct qla_tgt *tgt; /* to save extra sess dereferences */ + struct scsi_qla_host *vha; + + struct atio_from_isp atio; + /* t10dif */ + struct scatterlist *prot_sg; + uint32_t prot_sg_cnt; + uint32_t blk_sz; + struct crc_context *ctx; + +}; + +struct qla_tgt_sess_work_param { + struct list_head sess_works_list_entry; + +#define QLA_TGT_SESS_WORK_ABORT 1 +#define QLA_TGT_SESS_WORK_TM 2 + int type; + + union { + struct abts_recv_from_24xx abts; + struct imm_ntfy_from_isp tm_iocb; + struct atio_from_isp tm_iocb2; + }; +}; + +struct qla_tgt_mgmt_cmd { + uint8_t tmr_func; + uint8_t fc_tm_rsp; + struct qla_tgt_sess *sess; + struct se_cmd se_cmd; + struct work_struct free_work; + unsigned int flags; +#define QLA24XX_MGMT_SEND_NACK 1 + union { + struct atio_from_isp atio; + struct imm_ntfy_from_isp imm_ntfy; + struct abts_recv_from_24xx abts; + } __packed orig_iocb; +}; + +struct qla_tgt_prm { + struct qla_tgt_cmd *cmd; + struct qla_tgt *tgt; + void *pkt; + struct scatterlist *sg; /* cmd data buffer SG vector */ + int seg_cnt; + int req_cnt; + uint16_t rq_result; + uint16_t scsi_status; + unsigned char *sense_buffer; + int sense_buffer_len; + int residual; + int add_status_pkt; + /* dif */ + struct scatterlist *prot_sg; + uint16_t prot_seg_cnt; + uint16_t tot_dsds; +}; + +struct qla_tgt_srr_imm { + struct list_head srr_list_entry; + int srr_id; + struct imm_ntfy_from_isp imm_ntfy; +}; + +struct qla_tgt_srr_ctio { + struct list_head srr_list_entry; + int srr_id; + struct qla_tgt_cmd *cmd; +}; + +#define QLA_TGT_XMIT_DATA 1 +#define QLA_TGT_XMIT_STATUS 2 +#define QLA_TGT_XMIT_ALL (QLA_TGT_XMIT_STATUS|QLA_TGT_XMIT_DATA) + + +extern struct qla_tgt_data qla_target; +/* + * Internal function prototypes + */ +void qlt_disable_vha(struct scsi_qla_host *); + +/* + * Function prototypes for qla_target.c logic used by qla2xxx LLD code. + */ +extern int qlt_add_target(struct qla_hw_data *, struct scsi_qla_host *); +extern int qlt_remove_target(struct qla_hw_data *, struct scsi_qla_host *); +extern int qlt_lport_register(void *, u64, u64, u64, + int (*callback)(struct scsi_qla_host *, void *, u64, u64)); +extern void qlt_lport_deregister(struct scsi_qla_host *); +extern void qlt_unreg_sess(struct qla_tgt_sess *); +extern void qlt_fc_port_added(struct scsi_qla_host *, fc_port_t *); +extern void qlt_fc_port_deleted(struct scsi_qla_host *, fc_port_t *); +extern void qlt_set_mode(struct scsi_qla_host *ha); +extern void qlt_clear_mode(struct scsi_qla_host *ha); +extern int __init qlt_init(void); +extern void qlt_exit(void); +extern void qlt_update_vp_map(struct scsi_qla_host *, int); + +/* + * This macro is used during early initializations when host->active_mode + * is not set. Right now, ha value is ignored. + */ +#define QLA_TGT_MODE_ENABLED() (ql2x_ini_mode != QLA2XXX_INI_MODE_ENABLED) +extern int ql2x_ini_mode; + +static inline bool qla_tgt_mode_enabled(struct scsi_qla_host *ha) +{ + return ha->host->active_mode & MODE_TARGET; +} + +static inline bool qla_ini_mode_enabled(struct scsi_qla_host *ha) +{ + return ha->host->active_mode & MODE_INITIATOR; +} + +static inline void qla_reverse_ini_mode(struct scsi_qla_host *ha) +{ + if (ha->host->active_mode & MODE_INITIATOR) + ha->host->active_mode &= ~MODE_INITIATOR; + else + ha->host->active_mode |= MODE_INITIATOR; +} + +/* + * Exported symbols from qla_target.c LLD logic used by qla2xxx code.. + */ +extern void qlt_24xx_atio_pkt_all_vps(struct scsi_qla_host *, + struct atio_from_isp *); +extern void qlt_response_pkt_all_vps(struct scsi_qla_host *, response_t *); +extern int qlt_rdy_to_xfer(struct qla_tgt_cmd *); +extern int qlt_xmit_response(struct qla_tgt_cmd *, int, uint8_t); +extern int qlt_rdy_to_xfer_dif(struct qla_tgt_cmd *); +extern int qlt_xmit_response_dif(struct qla_tgt_cmd *, int, uint8_t); +extern void qlt_xmit_tm_rsp(struct qla_tgt_mgmt_cmd *); +extern void qlt_free_mcmd(struct qla_tgt_mgmt_cmd *); +extern void qlt_free_cmd(struct qla_tgt_cmd *cmd); +extern void qlt_async_event(uint16_t, struct scsi_qla_host *, uint16_t *); +extern void qlt_enable_vha(struct scsi_qla_host *); +extern void qlt_vport_create(struct scsi_qla_host *, struct qla_hw_data *); +extern void qlt_rff_id(struct scsi_qla_host *, struct ct_sns_req *); +extern void qlt_init_atio_q_entries(struct scsi_qla_host *); +extern void qlt_24xx_process_atio_queue(struct scsi_qla_host *); +extern void qlt_24xx_config_rings(struct scsi_qla_host *); +extern void qlt_24xx_config_nvram_stage1(struct scsi_qla_host *, + struct nvram_24xx *); +extern void qlt_24xx_config_nvram_stage2(struct scsi_qla_host *, + struct init_cb_24xx *); +extern void qlt_81xx_config_nvram_stage2(struct scsi_qla_host *, + struct init_cb_81xx *); +extern void qlt_81xx_config_nvram_stage1(struct scsi_qla_host *, + struct nvram_81xx *); +extern int qlt_24xx_process_response_error(struct scsi_qla_host *, + struct sts_entry_24xx *); +extern void qlt_modify_vp_config(struct scsi_qla_host *, + struct vp_config_entry_24xx *); +extern void qlt_probe_one_stage1(struct scsi_qla_host *, struct qla_hw_data *); +extern int qlt_mem_alloc(struct qla_hw_data *); +extern void qlt_mem_free(struct qla_hw_data *); +extern int qlt_stop_phase1(struct qla_tgt *); +extern void qlt_stop_phase2(struct qla_tgt *); +extern irqreturn_t qla83xx_msix_atio_q(int, void *); +extern void qlt_83xx_iospace_config(struct qla_hw_data *); + +#endif /* __QLA_TARGET_H */ diff --git a/drivers/scsi/qla2xxx/qla_tmpl.c b/drivers/scsi/qla2xxx/qla_tmpl.c new file mode 100644 index 00000000000..cb9a0c4bc41 --- /dev/null +++ b/drivers/scsi/qla2xxx/qla_tmpl.c @@ -0,0 +1,956 @@ +/* + * QLogic Fibre Channel HBA Driver + * Copyright (c) 2003-2014 QLogic Corporation + * + * See LICENSE.qla2xxx for copyright and licensing details. + */ +#include "qla_def.h" +#include "qla_tmpl.h" + +/* note default template is in big endian */ +static const uint32_t ql27xx_fwdt_default_template[] = { + 0x63000000, 0xa4000000, 0x7c050000, 0x00000000, + 0x30000000, 0x01000000, 0x00000000, 0xc0406eb4, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x00000000, 0x00000000, 0x00000000, + 0x00000000, 0x04010000, 0x14000000, 0x00000000, + 0x02000000, 0x44000000, 0x09010000, 0x10000000, + 0x00000000, 0x02000000, 0x01010000, 0x1c000000, + 0x00000000, 0x02000000, 0x00600000, 0x00000000, + 0xc0000000, 0x01010000, 0x1c000000, 0x00000000, + 0x02000000, 0x00600000, 0x00000000, 0xcc000000, + 0x01010000, 0x1c000000, 0x00000000, 0x02000000, + 0x10600000, 0x00000000, 0xd4000000, 0x01010000, + 0x1c000000, 0x00000000, 0x02000000, 0x700f0000, + 0x00000060, 0xf0000000, 0x00010000, 0x18000000, + 0x00000000, 0x02000000, 0x00700000, 0x041000c0, + 0x00010000, 0x18000000, 0x00000000, 0x02000000, + 0x10700000, 0x041000c0, 0x00010000, 0x18000000, + 0x00000000, 0x02000000, 0x40700000, 0x041000c0, + 0x01010000, 0x1c000000, 0x00000000, 0x02000000, + 0x007c0000, 0x01000000, 0xc0000000, 0x00010000, + 0x18000000, 0x00000000, 0x02000000, 0x007c0000, + 0x040300c4, 0x00010000, 0x18000000, 0x00000000, + 0x02000000, 0x007c0000, 0x040100c0, 0x01010000, + 0x1c000000, 0x00000000, 0x02000000, 0x007c0000, + 0x00000000, 0xc0000000, 0x00010000, 0x18000000, + 0x00000000, 0x02000000, 0x007c0000, 0x04200000, + 0x0b010000, 0x18000000, 0x00000000, 0x02000000, + 0x0c000000, 0x00000000, 0x02010000, 0x20000000, + 0x00000000, 0x02000000, 0x700f0000, 0x040100fc, + 0xf0000000, 0x000000b0, 0x02010000, 0x20000000, + 0x00000000, 0x02000000, 0x700f0000, 0x040100fc, + 0xf0000000, 0x000010b0, 0x02010000, 0x20000000, + 0x00000000, 0x02000000, 0x700f0000, 0x040100fc, + 0xf0000000, 0x000020b0, 0x02010000, 0x20000000, + 0x00000000, 0x02000000, 0x700f0000, 0x040100fc, + 0xf0000000, 0x000030b0, 0x02010000, 0x20000000, + 0x00000000, 0x02000000, 0x700f0000, 0x040100fc, + 0xf0000000, 0x000040b0, 0x02010000, 0x20000000, + 0x00000000, 0x02000000, 0x700f0000, 0x040100fc, + 0xf0000000, 0x000050b0, 0x02010000, 0x20000000, + 0x00000000, 0x02000000, 0x700f0000, 0x040100fc, + 0xf0000000, 0x000060b0, 0x02010000, 0x20000000, + 0x00000000, 0x02000000, 0x700f0000, 0x040100fc, + 0xf0000000, 0x000070b0, 0x02010000, 0x20000000, + 0x00000000, 0x02000000, 0x700f0000, 0x040100fc, + 0xf0000000, 0x000080b0, 0x02010000, 0x20000000, + 0x00000000, 0x02000000, 0x700f0000, 0x040100fc, + 0xf0000000, 0x000090b0, 0x02010000, 0x20000000, + 0x00000000, 0x02000000, 0x700f0000, 0x040100fc, + 0xf0000000, 0x0000a0b0, 0x00010000, 0x18000000, + 0x00000000, 0x02000000, 0x0a000000, 0x040100c0, + 0x00010000, 0x18000000, 0x00000000, 0x02000000, + 0x0a000000, 0x04200080, 0x00010000, 0x18000000, + 0x00000000, 0x02000000, 0x00be0000, 0x041000c0, + 0x00010000, 0x18000000, 0x00000000, 0x02000000, + 0x10be0000, 0x041000c0, 0x00010000, 0x18000000, + 0x00000000, 0x02000000, 0x20be0000, 0x041000c0, + 0x00010000, 0x18000000, 0x00000000, 0x02000000, + 0x30be0000, 0x041000c0, 0x00010000, 0x18000000, + 0x00000000, 0x02000000, 0x00b00000, 0x041000c0, + 0x00010000, 0x18000000, 0x00000000, 0x02000000, + 0x10b00000, 0x041000c0, 0x00010000, 0x18000000, + 0x00000000, 0x02000000, 0x20b00000, 0x041000c0, + 0x00010000, 0x18000000, 0x00000000, 0x02000000, + 0x30b00000, 0x041000c0, 0x00010000, 0x18000000, + 0x00000000, 0x02000000, 0x00300000, 0x041000c0, + 0x00010000, 0x18000000, 0x00000000, 0x02000000, + 0x10300000, 0x041000c0, 0x00010000, 0x18000000, + 0x00000000, 0x02000000, 0x20300000, 0x041000c0, + 0x00010000, 0x18000000, 0x00000000, 0x02000000, + 0x30300000, 0x041000c0, 0x0a010000, 0x10000000, + 0x00000000, 0x02000000, 0x06010000, 0x1c000000, + 0x00000000, 0x02000000, 0x01000000, 0x00000200, + 0xff230200, 0x06010000, 0x1c000000, 0x00000000, + 0x02000000, 0x02000000, 0x00001000, 0x00000000, + 0x07010000, 0x18000000, 0x00000000, 0x02000000, + 0x00000000, 0x01000000, 0x07010000, 0x18000000, + 0x00000000, 0x02000000, 0x00000000, 0x02000000, + 0x07010000, 0x18000000, 0x00000000, 0x02000000, + 0x00000000, 0x03000000, 0x0d010000, 0x14000000, + 0x00000000, 0x02000000, 0x00000000, 0xff000000, + 0x10000000, 0x00000000, 0x00000080, +}; + +static inline void __iomem * +qla27xx_isp_reg(struct scsi_qla_host *vha) +{ + return &vha->hw->iobase->isp24; +} + +static inline void +qla27xx_insert16(uint16_t value, void *buf, ulong *len) +{ + if (buf) { + buf += *len; + *(__le16 *)buf = cpu_to_le16(value); + } + *len += sizeof(value); +} + +static inline void +qla27xx_insert32(uint32_t value, void *buf, ulong *len) +{ + if (buf) { + buf += *len; + *(__le32 *)buf = cpu_to_le32(value); + } + *len += sizeof(value); +} + +static inline void +qla27xx_insertbuf(void *mem, ulong size, void *buf, ulong *len) +{ + ulong cnt = size; + + if (buf && mem) { + buf += *len; + while (cnt >= sizeof(uint32_t)) { + *(__le32 *)buf = cpu_to_le32p(mem); + buf += sizeof(uint32_t); + mem += sizeof(uint32_t); + cnt -= sizeof(uint32_t); + } + if (cnt) + memcpy(buf, mem, cnt); + } + *len += size; +} + +static inline void +qla27xx_read8(void *window, void *buf, ulong *len) +{ + uint8_t value = ~0; + + if (buf) { + value = RD_REG_BYTE((__iomem void *)window); + ql_dbg(ql_dbg_misc, NULL, 0xd011, + "%s: -> %x\n", __func__, value); + } + qla27xx_insert32(value, buf, len); +} + +static inline void +qla27xx_read16(void *window, void *buf, ulong *len) +{ + uint16_t value = ~0; + + if (buf) { + value = RD_REG_WORD((__iomem void *)window); + ql_dbg(ql_dbg_misc, NULL, 0xd012, + "%s: -> %x\n", __func__, value); + } + qla27xx_insert32(value, buf, len); +} + +static inline void +qla27xx_read32(void *window, void *buf, ulong *len) +{ + uint32_t value = ~0; + + if (buf) { + value = RD_REG_DWORD((__iomem void *)window); + ql_dbg(ql_dbg_misc, NULL, 0xd013, + "%s: -> %x\n", __func__, value); + } + qla27xx_insert32(value, buf, len); +} + +static inline void (*qla27xx_read_vector(uint width))(void *, void *, ulong *) +{ + return + (width == 1) ? qla27xx_read8 : + (width == 2) ? qla27xx_read16 : + qla27xx_read32; +} + +static inline void +qla27xx_read_reg(__iomem struct device_reg_24xx *reg, + uint offset, void *buf, ulong *len) +{ + void *window = (void *)reg + offset; + + if (buf) { + ql_dbg(ql_dbg_misc, NULL, 0xd014, + "%s: @%x\n", __func__, offset); + } + qla27xx_read32(window, buf, len); +} + +static inline void +qla27xx_write_reg(__iomem struct device_reg_24xx *reg, + uint offset, uint32_t data, void *buf) +{ + __iomem void *window = reg + offset; + + if (buf) { + ql_dbg(ql_dbg_misc, NULL, 0xd015, + "%s: @%x <- %x\n", __func__, offset, data); + WRT_REG_DWORD(window, data); + } +} + +static inline void +qla27xx_read_window(__iomem struct device_reg_24xx *reg, + uint32_t addr, uint offset, uint count, uint width, void *buf, + ulong *len) +{ + void *window = (void *)reg + offset; + void (*readn)(void *, void *, ulong *) = qla27xx_read_vector(width); + + if (buf) { + ql_dbg(ql_dbg_misc, NULL, 0xd016, + "%s: base=%x offset=%x count=%x width=%x\n", + __func__, addr, offset, count, width); + } + qla27xx_write_reg(reg, IOBASE_ADDR, addr, buf); + while (count--) { + qla27xx_insert32(addr, buf, len); + readn(window, buf, len); + window += width; + addr++; + } +} + +static inline void +qla27xx_skip_entry(struct qla27xx_fwdt_entry *ent, void *buf) +{ + if (buf) + ent->hdr.driver_flags |= DRIVER_FLAG_SKIP_ENTRY; +} + +static int +qla27xx_fwdt_entry_t0(struct scsi_qla_host *vha, + struct qla27xx_fwdt_entry *ent, void *buf, ulong *len) +{ + ql_dbg(ql_dbg_misc, vha, 0xd100, + "%s: nop [%lx]\n", __func__, *len); + qla27xx_skip_entry(ent, buf); + + return false; +} + +static int +qla27xx_fwdt_entry_t255(struct scsi_qla_host *vha, + struct qla27xx_fwdt_entry *ent, void *buf, ulong *len) +{ + ql_dbg(ql_dbg_misc, vha, 0xd1ff, + "%s: end [%lx]\n", __func__, *len); + qla27xx_skip_entry(ent, buf); + + /* terminate */ + return true; +} + +static int +qla27xx_fwdt_entry_t256(struct scsi_qla_host *vha, + struct qla27xx_fwdt_entry *ent, void *buf, ulong *len) +{ + struct device_reg_24xx __iomem *reg = qla27xx_isp_reg(vha); + + ql_dbg(ql_dbg_misc, vha, 0xd200, + "%s: rdio t1 [%lx]\n", __func__, *len); + qla27xx_read_window(reg, ent->t256.base_addr, ent->t256.pci_offset, + ent->t256.reg_count, ent->t256.reg_width, buf, len); + + return false; +} + +static int +qla27xx_fwdt_entry_t257(struct scsi_qla_host *vha, + struct qla27xx_fwdt_entry *ent, void *buf, ulong *len) +{ + struct device_reg_24xx __iomem *reg = qla27xx_isp_reg(vha); + + ql_dbg(ql_dbg_misc, vha, 0xd201, + "%s: wrio t1 [%lx]\n", __func__, *len); + qla27xx_write_reg(reg, IOBASE_ADDR, ent->t257.base_addr, buf); + qla27xx_write_reg(reg, ent->t257.pci_offset, ent->t257.write_data, buf); + + return false; +} + +static int +qla27xx_fwdt_entry_t258(struct scsi_qla_host *vha, + struct qla27xx_fwdt_entry *ent, void *buf, ulong *len) +{ + struct device_reg_24xx __iomem *reg = qla27xx_isp_reg(vha); + + ql_dbg(ql_dbg_misc, vha, 0xd202, + "%s: rdio t2 [%lx]\n", __func__, *len); + qla27xx_write_reg(reg, ent->t258.banksel_offset, ent->t258.bank, buf); + qla27xx_read_window(reg, ent->t258.base_addr, ent->t258.pci_offset, + ent->t258.reg_count, ent->t258.reg_width, buf, len); + + return false; +} + +static int +qla27xx_fwdt_entry_t259(struct scsi_qla_host *vha, + struct qla27xx_fwdt_entry *ent, void *buf, ulong *len) +{ + struct device_reg_24xx __iomem *reg = qla27xx_isp_reg(vha); + + ql_dbg(ql_dbg_misc, vha, 0xd203, + "%s: wrio t2 [%lx]\n", __func__, *len); + qla27xx_write_reg(reg, IOBASE_ADDR, ent->t259.base_addr, buf); + qla27xx_write_reg(reg, ent->t259.banksel_offset, ent->t259.bank, buf); + qla27xx_write_reg(reg, ent->t259.pci_offset, ent->t259.write_data, buf); + + return false; +} + +static int +qla27xx_fwdt_entry_t260(struct scsi_qla_host *vha, + struct qla27xx_fwdt_entry *ent, void *buf, ulong *len) +{ + struct device_reg_24xx __iomem *reg = qla27xx_isp_reg(vha); + + ql_dbg(ql_dbg_misc, vha, 0xd204, + "%s: rdpci [%lx]\n", __func__, *len); + qla27xx_insert32(ent->t260.pci_offset, buf, len); + qla27xx_read_reg(reg, ent->t260.pci_offset, buf, len); + + return false; +} + +static int +qla27xx_fwdt_entry_t261(struct scsi_qla_host *vha, + struct qla27xx_fwdt_entry *ent, void *buf, ulong *len) +{ + struct device_reg_24xx __iomem *reg = qla27xx_isp_reg(vha); + + ql_dbg(ql_dbg_misc, vha, 0xd205, + "%s: wrpci [%lx]\n", __func__, *len); + qla27xx_write_reg(reg, ent->t261.pci_offset, ent->t261.write_data, buf); + + return false; +} + +static int +qla27xx_fwdt_entry_t262(struct scsi_qla_host *vha, + struct qla27xx_fwdt_entry *ent, void *buf, ulong *len) +{ + ulong dwords; + ulong start; + ulong end; + + ql_dbg(ql_dbg_misc, vha, 0xd206, + "%s: rdram(%x) [%lx]\n", __func__, ent->t262.ram_area, *len); + start = ent->t262.start_addr; + end = ent->t262.end_addr; + + if (ent->t262.ram_area == T262_RAM_AREA_CRITICAL_RAM) { + ; + } else if (ent->t262.ram_area == T262_RAM_AREA_EXTERNAL_RAM) { + end = vha->hw->fw_memory_size; + if (buf) + ent->t262.end_addr = end; + } else if (ent->t262.ram_area == T262_RAM_AREA_SHARED_RAM) { + start = vha->hw->fw_shared_ram_start; + end = vha->hw->fw_shared_ram_end; + if (buf) { + ent->t262.start_addr = start; + ent->t262.end_addr = end; + } + } else if (ent->t262.ram_area == T262_RAM_AREA_DDR_RAM) { + ql_dbg(ql_dbg_misc, vha, 0xd021, + "%s: unsupported ddr ram\n", __func__); + qla27xx_skip_entry(ent, buf); + goto done; + } else { + ql_dbg(ql_dbg_misc, vha, 0xd022, + "%s: unknown area %u\n", __func__, ent->t262.ram_area); + qla27xx_skip_entry(ent, buf); + goto done; + } + + if (end < start || end == 0) { + ql_dbg(ql_dbg_misc, vha, 0xd023, + "%s: unusable range (start=%x end=%x)\n", __func__, + ent->t262.end_addr, ent->t262.start_addr); + qla27xx_skip_entry(ent, buf); + goto done; + } + + dwords = end - start + 1; + if (buf) { + ql_dbg(ql_dbg_misc, vha, 0xd024, + "%s: @%lx -> (%lx dwords)\n", __func__, start, dwords); + buf += *len; + qla24xx_dump_ram(vha->hw, start, buf, dwords, &buf); + } + *len += dwords * sizeof(uint32_t); +done: + return false; +} + +static int +qla27xx_fwdt_entry_t263(struct scsi_qla_host *vha, + struct qla27xx_fwdt_entry *ent, void *buf, ulong *len) +{ + uint count = 0; + uint i; + uint length; + + ql_dbg(ql_dbg_misc, vha, 0xd207, + "%s: getq(%x) [%lx]\n", __func__, ent->t263.queue_type, *len); + if (ent->t263.queue_type == T263_QUEUE_TYPE_REQ) { + for (i = 0; i < vha->hw->max_req_queues; i++) { + struct req_que *req = vha->hw->req_q_map[i]; + if (req || !buf) { + length = req ? + req->length : REQUEST_ENTRY_CNT_24XX; + qla27xx_insert16(i, buf, len); + qla27xx_insert16(length, buf, len); + qla27xx_insertbuf(req ? req->ring : NULL, + length * sizeof(*req->ring), buf, len); + count++; + } + } + } else if (ent->t263.queue_type == T263_QUEUE_TYPE_RSP) { + for (i = 0; i < vha->hw->max_rsp_queues; i++) { + struct rsp_que *rsp = vha->hw->rsp_q_map[i]; + if (rsp || !buf) { + length = rsp ? + rsp->length : RESPONSE_ENTRY_CNT_MQ; + qla27xx_insert16(i, buf, len); + qla27xx_insert16(length, buf, len); + qla27xx_insertbuf(rsp ? rsp->ring : NULL, + length * sizeof(*rsp->ring), buf, len); + count++; + } + } + } else if (ent->t263.queue_type == T263_QUEUE_TYPE_ATIO) { + ql_dbg(ql_dbg_misc, vha, 0xd025, + "%s: unsupported atio queue\n", __func__); + qla27xx_skip_entry(ent, buf); + } else { + ql_dbg(ql_dbg_misc, vha, 0xd026, + "%s: unknown queue %u\n", __func__, ent->t263.queue_type); + qla27xx_skip_entry(ent, buf); + } + + if (buf) + ent->t263.num_queues = count; + + return false; +} + +static int +qla27xx_fwdt_entry_t264(struct scsi_qla_host *vha, + struct qla27xx_fwdt_entry *ent, void *buf, ulong *len) +{ + ql_dbg(ql_dbg_misc, vha, 0xd208, + "%s: getfce [%lx]\n", __func__, *len); + if (vha->hw->fce) { + if (buf) { + ent->t264.fce_trace_size = FCE_SIZE; + ent->t264.write_pointer = vha->hw->fce_wr; + ent->t264.base_pointer = vha->hw->fce_dma; + ent->t264.fce_enable_mb0 = vha->hw->fce_mb[0]; + ent->t264.fce_enable_mb2 = vha->hw->fce_mb[2]; + ent->t264.fce_enable_mb3 = vha->hw->fce_mb[3]; + ent->t264.fce_enable_mb4 = vha->hw->fce_mb[4]; + ent->t264.fce_enable_mb5 = vha->hw->fce_mb[5]; + ent->t264.fce_enable_mb6 = vha->hw->fce_mb[6]; + } + qla27xx_insertbuf(vha->hw->fce, FCE_SIZE, buf, len); + } else { + ql_dbg(ql_dbg_misc, vha, 0xd027, + "%s: missing fce\n", __func__); + qla27xx_skip_entry(ent, buf); + } + + return false; +} + +static int +qla27xx_fwdt_entry_t265(struct scsi_qla_host *vha, + struct qla27xx_fwdt_entry *ent, void *buf, ulong *len) +{ + struct device_reg_24xx __iomem *reg = qla27xx_isp_reg(vha); + + ql_dbg(ql_dbg_misc, vha, 0xd209, + "%s: pause risc [%lx]\n", __func__, *len); + if (buf) + qla24xx_pause_risc(reg, vha->hw); + + return false; +} + +static int +qla27xx_fwdt_entry_t266(struct scsi_qla_host *vha, + struct qla27xx_fwdt_entry *ent, void *buf, ulong *len) +{ + ql_dbg(ql_dbg_misc, vha, 0xd20a, + "%s: reset risc [%lx]\n", __func__, *len); + if (buf) + qla24xx_soft_reset(vha->hw); + + return false; +} + +static int +qla27xx_fwdt_entry_t267(struct scsi_qla_host *vha, + struct qla27xx_fwdt_entry *ent, void *buf, ulong *len) +{ + struct device_reg_24xx __iomem *reg = qla27xx_isp_reg(vha); + + ql_dbg(ql_dbg_misc, vha, 0xd20b, + "%s: dis intr [%lx]\n", __func__, *len); + qla27xx_write_reg(reg, ent->t267.pci_offset, ent->t267.data, buf); + + return false; +} + +static int +qla27xx_fwdt_entry_t268(struct scsi_qla_host *vha, + struct qla27xx_fwdt_entry *ent, void *buf, ulong *len) +{ + ql_dbg(ql_dbg_misc, vha, 0xd20c, + "%s: gethb(%x) [%lx]\n", __func__, ent->t268.buf_type, *len); + if (ent->t268.buf_type == T268_BUF_TYPE_EXTD_TRACE) { + if (vha->hw->eft) { + if (buf) { + ent->t268.buf_size = EFT_SIZE; + ent->t268.start_addr = vha->hw->eft_dma; + } + qla27xx_insertbuf(vha->hw->eft, EFT_SIZE, buf, len); + } else { + ql_dbg(ql_dbg_misc, vha, 0xd028, + "%s: missing eft\n", __func__); + qla27xx_skip_entry(ent, buf); + } + } else if (ent->t268.buf_type == T268_BUF_TYPE_EXCH_BUFOFF) { + ql_dbg(ql_dbg_misc, vha, 0xd029, + "%s: unsupported exchange offload buffer\n", __func__); + qla27xx_skip_entry(ent, buf); + } else if (ent->t268.buf_type == T268_BUF_TYPE_EXTD_LOGIN) { + ql_dbg(ql_dbg_misc, vha, 0xd02a, + "%s: unsupported extended login buffer\n", __func__); + qla27xx_skip_entry(ent, buf); + } else { + ql_dbg(ql_dbg_misc, vha, 0xd02b, + "%s: unknown buf %x\n", __func__, ent->t268.buf_type); + qla27xx_skip_entry(ent, buf); + } + + return false; +} + +static int +qla27xx_fwdt_entry_t269(struct scsi_qla_host *vha, + struct qla27xx_fwdt_entry *ent, void *buf, ulong *len) +{ + ql_dbg(ql_dbg_misc, vha, 0xd20d, + "%s: scratch [%lx]\n", __func__, *len); + qla27xx_insert32(0xaaaaaaaa, buf, len); + qla27xx_insert32(0xbbbbbbbb, buf, len); + qla27xx_insert32(0xcccccccc, buf, len); + qla27xx_insert32(0xdddddddd, buf, len); + qla27xx_insert32(*len + sizeof(uint32_t), buf, len); + if (buf) + ent->t269.scratch_size = 5 * sizeof(uint32_t); + + return false; +} + +static int +qla27xx_fwdt_entry_t270(struct scsi_qla_host *vha, + struct qla27xx_fwdt_entry *ent, void *buf, ulong *len) +{ + struct device_reg_24xx __iomem *reg = qla27xx_isp_reg(vha); + ulong dwords = ent->t270.count; + ulong addr = ent->t270.addr; + + ql_dbg(ql_dbg_misc, vha, 0xd20e, + "%s: rdremreg [%lx]\n", __func__, *len); + qla27xx_write_reg(reg, IOBASE_ADDR, 0x40, buf); + while (dwords--) { + qla27xx_write_reg(reg, 0xc0, addr|0x80000000, buf); + qla27xx_insert32(addr, buf, len); + qla27xx_read_reg(reg, 0xc4, buf, len); + addr += sizeof(uint32_t); + } + + return false; +} + +static int +qla27xx_fwdt_entry_t271(struct scsi_qla_host *vha, + struct qla27xx_fwdt_entry *ent, void *buf, ulong *len) +{ + struct device_reg_24xx __iomem *reg = qla27xx_isp_reg(vha); + ulong addr = ent->t271.addr; + ulong data = ent->t271.data; + + ql_dbg(ql_dbg_misc, vha, 0xd20f, + "%s: wrremreg [%lx]\n", __func__, *len); + qla27xx_write_reg(reg, IOBASE_ADDR, 0x40, buf); + qla27xx_write_reg(reg, 0xc4, data, buf); + qla27xx_write_reg(reg, 0xc0, addr, buf); + + return false; +} + +static int +qla27xx_fwdt_entry_t272(struct scsi_qla_host *vha, + struct qla27xx_fwdt_entry *ent, void *buf, ulong *len) +{ + ulong dwords = ent->t272.count; + ulong start = ent->t272.addr; + + ql_dbg(ql_dbg_misc, vha, 0xd210, + "%s: rdremram [%lx]\n", __func__, *len); + if (buf) { + ql_dbg(ql_dbg_misc, vha, 0xd02c, + "%s: @%lx -> (%lx dwords)\n", __func__, start, dwords); + buf += *len; + qla27xx_dump_mpi_ram(vha->hw, start, buf, dwords, &buf); + } + *len += dwords * sizeof(uint32_t); + + return false; +} + +static int +qla27xx_fwdt_entry_t273(struct scsi_qla_host *vha, + struct qla27xx_fwdt_entry *ent, void *buf, ulong *len) +{ + ulong dwords = ent->t273.count; + ulong addr = ent->t273.addr; + uint32_t value; + + ql_dbg(ql_dbg_misc, vha, 0xd211, + "%s: pcicfg [%lx]\n", __func__, *len); + while (dwords--) { + value = ~0; + if (pci_read_config_dword(vha->hw->pdev, addr, &value)) + ql_dbg(ql_dbg_misc, vha, 0xd02d, + "%s: failed pcicfg read at %lx\n", __func__, addr); + qla27xx_insert32(addr, buf, len); + qla27xx_insert32(value, buf, len); + addr += sizeof(uint32_t); + } + + return false; +} + +static int +qla27xx_fwdt_entry_t274(struct scsi_qla_host *vha, + struct qla27xx_fwdt_entry *ent, void *buf, ulong *len) +{ + uint count = 0; + uint i; + + ql_dbg(ql_dbg_misc, vha, 0xd212, + "%s: getqsh(%x) [%lx]\n", __func__, ent->t274.queue_type, *len); + if (ent->t274.queue_type == T274_QUEUE_TYPE_REQ_SHAD) { + for (i = 0; i < vha->hw->max_req_queues; i++) { + struct req_que *req = vha->hw->req_q_map[i]; + if (req || !buf) { + qla27xx_insert16(i, buf, len); + qla27xx_insert16(1, buf, len); + qla27xx_insert32(req && req->out_ptr ? + *req->out_ptr : 0, buf, len); + count++; + } + } + } else if (ent->t274.queue_type == T274_QUEUE_TYPE_RSP_SHAD) { + for (i = 0; i < vha->hw->max_rsp_queues; i++) { + struct rsp_que *rsp = vha->hw->rsp_q_map[i]; + if (rsp || !buf) { + qla27xx_insert16(i, buf, len); + qla27xx_insert16(1, buf, len); + qla27xx_insert32(rsp && rsp->in_ptr ? + *rsp->in_ptr : 0, buf, len); + count++; + } + } + } else if (ent->t274.queue_type == T274_QUEUE_TYPE_ATIO_SHAD) { + ql_dbg(ql_dbg_misc, vha, 0xd02e, + "%s: unsupported atio queue\n", __func__); + qla27xx_skip_entry(ent, buf); + } else { + ql_dbg(ql_dbg_misc, vha, 0xd02f, + "%s: unknown queue %u\n", __func__, ent->t274.queue_type); + qla27xx_skip_entry(ent, buf); + } + + if (buf) + ent->t274.num_queues = count; + + if (!count) + qla27xx_skip_entry(ent, buf); + + return false; +} + +static int +qla27xx_fwdt_entry_other(struct scsi_qla_host *vha, + struct qla27xx_fwdt_entry *ent, void *buf, ulong *len) +{ + ql_dbg(ql_dbg_misc, vha, 0xd2ff, + "%s: type %x [%lx]\n", __func__, ent->hdr.entry_type, *len); + qla27xx_skip_entry(ent, buf); + + return false; +} + +struct qla27xx_fwdt_entry_call { + int type; + int (*call)( + struct scsi_qla_host *, + struct qla27xx_fwdt_entry *, + void *, + ulong *); +}; + +static struct qla27xx_fwdt_entry_call ql27xx_fwdt_entry_call_list[] = { + { ENTRY_TYPE_NOP , qla27xx_fwdt_entry_t0 } , + { ENTRY_TYPE_TMP_END , qla27xx_fwdt_entry_t255 } , + { ENTRY_TYPE_RD_IOB_T1 , qla27xx_fwdt_entry_t256 } , + { ENTRY_TYPE_WR_IOB_T1 , qla27xx_fwdt_entry_t257 } , + { ENTRY_TYPE_RD_IOB_T2 , qla27xx_fwdt_entry_t258 } , + { ENTRY_TYPE_WR_IOB_T2 , qla27xx_fwdt_entry_t259 } , + { ENTRY_TYPE_RD_PCI , qla27xx_fwdt_entry_t260 } , + { ENTRY_TYPE_WR_PCI , qla27xx_fwdt_entry_t261 } , + { ENTRY_TYPE_RD_RAM , qla27xx_fwdt_entry_t262 } , + { ENTRY_TYPE_GET_QUEUE , qla27xx_fwdt_entry_t263 } , + { ENTRY_TYPE_GET_FCE , qla27xx_fwdt_entry_t264 } , + { ENTRY_TYPE_PSE_RISC , qla27xx_fwdt_entry_t265 } , + { ENTRY_TYPE_RST_RISC , qla27xx_fwdt_entry_t266 } , + { ENTRY_TYPE_DIS_INTR , qla27xx_fwdt_entry_t267 } , + { ENTRY_TYPE_GET_HBUF , qla27xx_fwdt_entry_t268 } , + { ENTRY_TYPE_SCRATCH , qla27xx_fwdt_entry_t269 } , + { ENTRY_TYPE_RDREMREG , qla27xx_fwdt_entry_t270 } , + { ENTRY_TYPE_WRREMREG , qla27xx_fwdt_entry_t271 } , + { ENTRY_TYPE_RDREMRAM , qla27xx_fwdt_entry_t272 } , + { ENTRY_TYPE_PCICFG , qla27xx_fwdt_entry_t273 } , + { ENTRY_TYPE_GET_SHADOW , qla27xx_fwdt_entry_t274 } , + { -1 , qla27xx_fwdt_entry_other } +}; + +static inline int (*qla27xx_find_entry(int type)) + (struct scsi_qla_host *, struct qla27xx_fwdt_entry *, void *, ulong *) +{ + struct qla27xx_fwdt_entry_call *list = ql27xx_fwdt_entry_call_list; + + while (list->type != -1 && list->type != type) + list++; + + return list->call; +} + +static inline void * +qla27xx_next_entry(void *p) +{ + struct qla27xx_fwdt_entry *ent = p; + + return p + ent->hdr.entry_size; +} + +static void +qla27xx_walk_template(struct scsi_qla_host *vha, + struct qla27xx_fwdt_template *tmp, void *buf, ulong *len) +{ + struct qla27xx_fwdt_entry *ent = (void *)tmp + tmp->entry_offset; + ulong count = tmp->entry_count; + + ql_dbg(ql_dbg_misc, vha, 0xd01a, + "%s: entry count %lx\n", __func__, count); + while (count--) { + if (qla27xx_find_entry(ent->hdr.entry_type)(vha, ent, buf, len)) + break; + ent = qla27xx_next_entry(ent); + } + ql_dbg(ql_dbg_misc, vha, 0xd01b, + "%s: len=%lx\n", __func__, *len); +} + +static void +qla27xx_time_stamp(struct qla27xx_fwdt_template *tmp) +{ + tmp->capture_timestamp = jiffies; +} + +static void +qla27xx_driver_info(struct qla27xx_fwdt_template *tmp) +{ + uint8_t v[] = { 0, 0, 0, 0, 0, 0 }; + int rval = 0; + + rval = sscanf(qla2x00_version_str, "%hhu.%hhu.%hhu.%hhu.%hhu.%hhu", + v+0, v+1, v+2, v+3, v+4, v+5); + + tmp->driver_info[0] = v[3] << 24 | v[2] << 16 | v[1] << 8 | v[0]; + tmp->driver_info[1] = v[5] << 8 | v[4]; + tmp->driver_info[2] = 0x12345678; +} + +static void +qla27xx_firmware_info(struct qla27xx_fwdt_template *tmp, + struct scsi_qla_host *vha) +{ + tmp->firmware_version[0] = vha->hw->fw_major_version; + tmp->firmware_version[1] = vha->hw->fw_minor_version; + tmp->firmware_version[2] = vha->hw->fw_subminor_version; + tmp->firmware_version[3] = + vha->hw->fw_attributes_h << 16 | vha->hw->fw_attributes; + tmp->firmware_version[4] = + vha->hw->fw_attributes_ext[1] << 16 | vha->hw->fw_attributes_ext[0]; +} + +static void +ql27xx_edit_template(struct scsi_qla_host *vha, + struct qla27xx_fwdt_template *tmp) +{ + qla27xx_time_stamp(tmp); + qla27xx_driver_info(tmp); + qla27xx_firmware_info(tmp, vha); +} + +static inline uint32_t +qla27xx_template_checksum(void *p, ulong size) +{ + uint32_t *buf = p; + uint64_t sum = 0; + + size /= sizeof(*buf); + + while (size--) + sum += *buf++; + + sum = (sum & 0xffffffff) + (sum >> 32); + + return ~sum; +} + +static inline int +qla27xx_verify_template_checksum(struct qla27xx_fwdt_template *tmp) +{ + return qla27xx_template_checksum(tmp, tmp->template_size) == 0; +} + +static inline int +qla27xx_verify_template_header(struct qla27xx_fwdt_template *tmp) +{ + return tmp->template_type == TEMPLATE_TYPE_FWDUMP; +} + +static void +qla27xx_execute_fwdt_template(struct scsi_qla_host *vha) +{ + struct qla27xx_fwdt_template *tmp = vha->hw->fw_dump_template; + ulong len; + + if (qla27xx_fwdt_template_valid(tmp)) { + len = tmp->template_size; + tmp = memcpy(vha->hw->fw_dump, tmp, len); + ql27xx_edit_template(vha, tmp); + qla27xx_walk_template(vha, tmp, tmp, &len); + vha->hw->fw_dump_len = len; + vha->hw->fw_dumped = 1; + } +} + +ulong +qla27xx_fwdt_calculate_dump_size(struct scsi_qla_host *vha) +{ + struct qla27xx_fwdt_template *tmp = vha->hw->fw_dump_template; + ulong len = 0; + + if (qla27xx_fwdt_template_valid(tmp)) { + len = tmp->template_size; + qla27xx_walk_template(vha, tmp, NULL, &len); + } + + return len; +} + +ulong +qla27xx_fwdt_template_size(void *p) +{ + struct qla27xx_fwdt_template *tmp = p; + + return tmp->template_size; +} + +ulong +qla27xx_fwdt_template_default_size(void) +{ + return sizeof(ql27xx_fwdt_default_template); +} + +const void * +qla27xx_fwdt_template_default(void) +{ + return ql27xx_fwdt_default_template; +} + +int +qla27xx_fwdt_template_valid(void *p) +{ + struct qla27xx_fwdt_template *tmp = p; + + if (!qla27xx_verify_template_header(tmp)) { + ql_log(ql_log_warn, NULL, 0xd01c, + "%s: template type %x\n", __func__, tmp->template_type); + return false; + } + + if (!qla27xx_verify_template_checksum(tmp)) { + ql_log(ql_log_warn, NULL, 0xd01d, + "%s: failed template checksum\n", __func__); + return false; + } + + return true; +} + +void +qla27xx_fwdump(scsi_qla_host_t *vha, int hardware_locked) +{ + ulong flags = 0; + + if (!hardware_locked) + spin_lock_irqsave(&vha->hw->hardware_lock, flags); + + if (!vha->hw->fw_dump) + ql_log(ql_log_warn, vha, 0xd01e, "fwdump buffer missing.\n"); + else if (!vha->hw->fw_dump_template) + ql_log(ql_log_warn, vha, 0xd01f, "fwdump template missing.\n"); + else + qla27xx_execute_fwdt_template(vha); + + if (!hardware_locked) + spin_unlock_irqrestore(&vha->hw->hardware_lock, flags); +} diff --git a/drivers/scsi/qla2xxx/qla_tmpl.h b/drivers/scsi/qla2xxx/qla_tmpl.h new file mode 100644 index 00000000000..1967424c8e6 --- /dev/null +++ b/drivers/scsi/qla2xxx/qla_tmpl.h @@ -0,0 +1,216 @@ +/* + * QLogic Fibre Channel HBA Driver + * Copyright (c) 2003-2014 QLogic Corporation + * + * See LICENSE.qla2xxx for copyright and licensing details. + */ + +#ifndef __QLA_DMP27_H__ +#define __QLA_DMP27_H__ + +#define IOBASE_ADDR offsetof(struct device_reg_24xx, iobase_addr) + +struct __packed qla27xx_fwdt_template { + uint32_t template_type; + uint32_t entry_offset; + uint32_t template_size; + uint32_t reserved_1; + + uint32_t entry_count; + uint32_t template_version; + uint32_t capture_timestamp; + uint32_t template_checksum; + + uint32_t reserved_2; + uint32_t driver_info[3]; + + uint32_t saved_state[16]; + + uint32_t reserved_3[8]; + uint32_t firmware_version[5]; +}; + +#define TEMPLATE_TYPE_FWDUMP 99 + +#define ENTRY_TYPE_NOP 0 +#define ENTRY_TYPE_TMP_END 255 +#define ENTRY_TYPE_RD_IOB_T1 256 +#define ENTRY_TYPE_WR_IOB_T1 257 +#define ENTRY_TYPE_RD_IOB_T2 258 +#define ENTRY_TYPE_WR_IOB_T2 259 +#define ENTRY_TYPE_RD_PCI 260 +#define ENTRY_TYPE_WR_PCI 261 +#define ENTRY_TYPE_RD_RAM 262 +#define ENTRY_TYPE_GET_QUEUE 263 +#define ENTRY_TYPE_GET_FCE 264 +#define ENTRY_TYPE_PSE_RISC 265 +#define ENTRY_TYPE_RST_RISC 266 +#define ENTRY_TYPE_DIS_INTR 267 +#define ENTRY_TYPE_GET_HBUF 268 +#define ENTRY_TYPE_SCRATCH 269 +#define ENTRY_TYPE_RDREMREG 270 +#define ENTRY_TYPE_WRREMREG 271 +#define ENTRY_TYPE_RDREMRAM 272 +#define ENTRY_TYPE_PCICFG 273 +#define ENTRY_TYPE_GET_SHADOW 274 + +#define CAPTURE_FLAG_PHYS_ONLY BIT_0 +#define CAPTURE_FLAG_PHYS_VIRT BIT_1 + +#define DRIVER_FLAG_SKIP_ENTRY BIT_7 + +struct __packed qla27xx_fwdt_entry { + struct __packed { + uint32_t entry_type; + uint32_t entry_size; + uint32_t reserved_1; + + uint8_t capture_flags; + uint8_t reserved_2[2]; + uint8_t driver_flags; + } hdr; + union __packed { + struct __packed { + } t0; + + struct __packed { + } t255; + + struct __packed { + uint32_t base_addr; + uint8_t reg_width; + uint16_t reg_count; + uint8_t pci_offset; + } t256; + + struct __packed { + uint32_t base_addr; + uint32_t write_data; + uint8_t pci_offset; + uint8_t reserved[3]; + } t257; + + struct __packed { + uint32_t base_addr; + uint8_t reg_width; + uint16_t reg_count; + uint8_t pci_offset; + uint8_t banksel_offset; + uint8_t reserved[3]; + uint32_t bank; + } t258; + + struct __packed { + uint32_t base_addr; + uint32_t write_data; + uint8_t reserved[2]; + uint8_t pci_offset; + uint8_t banksel_offset; + uint32_t bank; + } t259; + + struct __packed { + uint8_t pci_offset; + uint8_t reserved[3]; + } t260; + + struct __packed { + uint8_t pci_offset; + uint8_t reserved[3]; + uint32_t write_data; + } t261; + + struct __packed { + uint8_t ram_area; + uint8_t reserved[3]; + uint32_t start_addr; + uint32_t end_addr; + } t262; + + struct __packed { + uint32_t num_queues; + uint8_t queue_type; + uint8_t reserved[3]; + } t263; + + struct __packed { + uint32_t fce_trace_size; + uint64_t write_pointer; + uint64_t base_pointer; + uint32_t fce_enable_mb0; + uint32_t fce_enable_mb2; + uint32_t fce_enable_mb3; + uint32_t fce_enable_mb4; + uint32_t fce_enable_mb5; + uint32_t fce_enable_mb6; + } t264; + + struct __packed { + } t265; + + struct __packed { + } t266; + + struct __packed { + uint8_t pci_offset; + uint8_t reserved[3]; + uint32_t data; + } t267; + + struct __packed { + uint8_t buf_type; + uint8_t reserved[3]; + uint32_t buf_size; + uint64_t start_addr; + } t268; + + struct __packed { + uint32_t scratch_size; + } t269; + + struct __packed { + uint32_t addr; + uint32_t count; + } t270; + + struct __packed { + uint32_t addr; + uint32_t data; + } t271; + + struct __packed { + uint32_t addr; + uint32_t count; + } t272; + + struct __packed { + uint32_t addr; + uint32_t count; + } t273; + + struct __packed { + uint32_t num_queues; + uint8_t queue_type; + uint8_t reserved[3]; + } t274; + }; +}; + +#define T262_RAM_AREA_CRITICAL_RAM 1 +#define T262_RAM_AREA_EXTERNAL_RAM 2 +#define T262_RAM_AREA_SHARED_RAM 3 +#define T262_RAM_AREA_DDR_RAM 4 + +#define T263_QUEUE_TYPE_REQ 1 +#define T263_QUEUE_TYPE_RSP 2 +#define T263_QUEUE_TYPE_ATIO 3 + +#define T268_BUF_TYPE_EXTD_TRACE 1 +#define T268_BUF_TYPE_EXCH_BUFOFF 2 +#define T268_BUF_TYPE_EXTD_LOGIN 3 + +#define T274_QUEUE_TYPE_REQ_SHAD 1 +#define T274_QUEUE_TYPE_RSP_SHAD 2 +#define T274_QUEUE_TYPE_ATIO_SHAD 3 + +#endif diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index 062c97bf62f..4d2c98cbec4 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -1,15 +1,15 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ /* * Driver version */ -#define QLA2XXX_VERSION "8.03.07.03-k" +#define QLA2XXX_VERSION "8.07.00.08-k" #define QLA_DRIVER_MAJOR_VER 8 -#define QLA_DRIVER_MINOR_VER 3 -#define QLA_DRIVER_PATCH_VER 7 -#define QLA_DRIVER_BETA_VER 3 +#define QLA_DRIVER_MINOR_VER 7 +#define QLA_DRIVER_PATCH_VER 0 +#define QLA_DRIVER_BETA_VER 0 diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.c b/drivers/scsi/qla2xxx/tcm_qla2xxx.c new file mode 100644 index 00000000000..e2beab96209 --- /dev/null +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -0,0 +1,2146 @@ +/******************************************************************************* + * This file contains tcm implementation using v4 configfs fabric infrastructure + * for QLogic target mode HBAs + * + * (c) Copyright 2010-2013 Datera, Inc. + * + * Author: Nicholas A. Bellinger <nab@daterainc.com> + * + * tcm_qla2xxx_parse_wwn() and tcm_qla2xxx_format_wwn() contains code from + * the TCM_FC / Open-FCoE.org fabric module. + * + * Copyright (c) 2010 Cisco Systems, Inc + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + ****************************************************************************/ + + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <generated/utsrelease.h> +#include <linux/utsname.h> +#include <linux/init.h> +#include <linux/list.h> +#include <linux/slab.h> +#include <linux/kthread.h> +#include <linux/types.h> +#include <linux/string.h> +#include <linux/configfs.h> +#include <linux/ctype.h> +#include <asm/unaligned.h> +#include <scsi/scsi.h> +#include <scsi/scsi_host.h> +#include <scsi/scsi_device.h> +#include <scsi/scsi_cmnd.h> +#include <target/target_core_base.h> +#include <target/target_core_fabric.h> +#include <target/target_core_fabric_configfs.h> +#include <target/target_core_configfs.h> +#include <target/configfs_macros.h> + +#include "qla_def.h" +#include "qla_target.h" +#include "tcm_qla2xxx.h" + +struct workqueue_struct *tcm_qla2xxx_free_wq; +struct workqueue_struct *tcm_qla2xxx_cmd_wq; + +/* + * Parse WWN. + * If strict, we require lower-case hex and colon separators to be sure + * the name is the same as what would be generated by ft_format_wwn() + * so the name and wwn are mapped one-to-one. + */ +static ssize_t tcm_qla2xxx_parse_wwn(const char *name, u64 *wwn, int strict) +{ + const char *cp; + char c; + u32 nibble; + u32 byte = 0; + u32 pos = 0; + u32 err; + + *wwn = 0; + for (cp = name; cp < &name[TCM_QLA2XXX_NAMELEN - 1]; cp++) { + c = *cp; + if (c == '\n' && cp[1] == '\0') + continue; + if (strict && pos++ == 2 && byte++ < 7) { + pos = 0; + if (c == ':') + continue; + err = 1; + goto fail; + } + if (c == '\0') { + err = 2; + if (strict && byte != 8) + goto fail; + return cp - name; + } + err = 3; + if (isdigit(c)) + nibble = c - '0'; + else if (isxdigit(c) && (islower(c) || !strict)) + nibble = tolower(c) - 'a' + 10; + else + goto fail; + *wwn = (*wwn << 4) | nibble; + } + err = 4; +fail: + pr_debug("err %u len %zu pos %u byte %u\n", + err, cp - name, pos, byte); + return -1; +} + +static ssize_t tcm_qla2xxx_format_wwn(char *buf, size_t len, u64 wwn) +{ + u8 b[8]; + + put_unaligned_be64(wwn, b); + return snprintf(buf, len, + "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x", + b[0], b[1], b[2], b[3], b[4], b[5], b[6], b[7]); +} + +static char *tcm_qla2xxx_get_fabric_name(void) +{ + return "qla2xxx"; +} + +/* + * From drivers/scsi/scsi_transport_fc.c:fc_parse_wwn + */ +static int tcm_qla2xxx_npiv_extract_wwn(const char *ns, u64 *nm) +{ + unsigned int i, j; + u8 wwn[8]; + + memset(wwn, 0, sizeof(wwn)); + + /* Validate and store the new name */ + for (i = 0, j = 0; i < 16; i++) { + int value; + + value = hex_to_bin(*ns++); + if (value >= 0) + j = (j << 4) | value; + else + return -EINVAL; + + if (i % 2) { + wwn[i/2] = j & 0xff; + j = 0; + } + } + + *nm = wwn_to_u64(wwn); + return 0; +} + +/* + * This parsing logic follows drivers/scsi/scsi_transport_fc.c: + * store_fc_host_vport_create() + */ +static int tcm_qla2xxx_npiv_parse_wwn( + const char *name, + size_t count, + u64 *wwpn, + u64 *wwnn) +{ + unsigned int cnt = count; + int rc; + + *wwpn = 0; + *wwnn = 0; + + /* count may include a LF at end of string */ + if (name[cnt-1] == '\n' || name[cnt-1] == 0) + cnt--; + + /* validate we have enough characters for WWPN */ + if ((cnt != (16+1+16)) || (name[16] != ':')) + return -EINVAL; + + rc = tcm_qla2xxx_npiv_extract_wwn(&name[0], wwpn); + if (rc != 0) + return rc; + + rc = tcm_qla2xxx_npiv_extract_wwn(&name[17], wwnn); + if (rc != 0) + return rc; + + return 0; +} + +static char *tcm_qla2xxx_npiv_get_fabric_name(void) +{ + return "qla2xxx_npiv"; +} + +static u8 tcm_qla2xxx_get_fabric_proto_ident(struct se_portal_group *se_tpg) +{ + struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, + struct tcm_qla2xxx_tpg, se_tpg); + struct tcm_qla2xxx_lport *lport = tpg->lport; + u8 proto_id; + + switch (lport->lport_proto_id) { + case SCSI_PROTOCOL_FCP: + default: + proto_id = fc_get_fabric_proto_ident(se_tpg); + break; + } + + return proto_id; +} + +static char *tcm_qla2xxx_get_fabric_wwn(struct se_portal_group *se_tpg) +{ + struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, + struct tcm_qla2xxx_tpg, se_tpg); + struct tcm_qla2xxx_lport *lport = tpg->lport; + + return lport->lport_naa_name; +} + +static u16 tcm_qla2xxx_get_tag(struct se_portal_group *se_tpg) +{ + struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, + struct tcm_qla2xxx_tpg, se_tpg); + return tpg->lport_tpgt; +} + +static u32 tcm_qla2xxx_get_default_depth(struct se_portal_group *se_tpg) +{ + return 1; +} + +static u32 tcm_qla2xxx_get_pr_transport_id( + struct se_portal_group *se_tpg, + struct se_node_acl *se_nacl, + struct t10_pr_registration *pr_reg, + int *format_code, + unsigned char *buf) +{ + struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, + struct tcm_qla2xxx_tpg, se_tpg); + struct tcm_qla2xxx_lport *lport = tpg->lport; + int ret = 0; + + switch (lport->lport_proto_id) { + case SCSI_PROTOCOL_FCP: + default: + ret = fc_get_pr_transport_id(se_tpg, se_nacl, pr_reg, + format_code, buf); + break; + } + + return ret; +} + +static u32 tcm_qla2xxx_get_pr_transport_id_len( + struct se_portal_group *se_tpg, + struct se_node_acl *se_nacl, + struct t10_pr_registration *pr_reg, + int *format_code) +{ + struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, + struct tcm_qla2xxx_tpg, se_tpg); + struct tcm_qla2xxx_lport *lport = tpg->lport; + int ret = 0; + + switch (lport->lport_proto_id) { + case SCSI_PROTOCOL_FCP: + default: + ret = fc_get_pr_transport_id_len(se_tpg, se_nacl, pr_reg, + format_code); + break; + } + + return ret; +} + +static char *tcm_qla2xxx_parse_pr_out_transport_id( + struct se_portal_group *se_tpg, + const char *buf, + u32 *out_tid_len, + char **port_nexus_ptr) +{ + struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, + struct tcm_qla2xxx_tpg, se_tpg); + struct tcm_qla2xxx_lport *lport = tpg->lport; + char *tid = NULL; + + switch (lport->lport_proto_id) { + case SCSI_PROTOCOL_FCP: + default: + tid = fc_parse_pr_out_transport_id(se_tpg, buf, out_tid_len, + port_nexus_ptr); + break; + } + + return tid; +} + +static int tcm_qla2xxx_check_demo_mode(struct se_portal_group *se_tpg) +{ + struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, + struct tcm_qla2xxx_tpg, se_tpg); + + return tpg->tpg_attrib.generate_node_acls; +} + +static int tcm_qla2xxx_check_demo_mode_cache(struct se_portal_group *se_tpg) +{ + struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, + struct tcm_qla2xxx_tpg, se_tpg); + + return tpg->tpg_attrib.cache_dynamic_acls; +} + +static int tcm_qla2xxx_check_demo_write_protect(struct se_portal_group *se_tpg) +{ + struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, + struct tcm_qla2xxx_tpg, se_tpg); + + return tpg->tpg_attrib.demo_mode_write_protect; +} + +static int tcm_qla2xxx_check_prod_write_protect(struct se_portal_group *se_tpg) +{ + struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, + struct tcm_qla2xxx_tpg, se_tpg); + + return tpg->tpg_attrib.prod_mode_write_protect; +} + +static int tcm_qla2xxx_check_demo_mode_login_only(struct se_portal_group *se_tpg) +{ + struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, + struct tcm_qla2xxx_tpg, se_tpg); + + return tpg->tpg_attrib.demo_mode_login_only; +} + +static struct se_node_acl *tcm_qla2xxx_alloc_fabric_acl( + struct se_portal_group *se_tpg) +{ + struct tcm_qla2xxx_nacl *nacl; + + nacl = kzalloc(sizeof(struct tcm_qla2xxx_nacl), GFP_KERNEL); + if (!nacl) { + pr_err("Unable to allocate struct tcm_qla2xxx_nacl\n"); + return NULL; + } + + return &nacl->se_node_acl; +} + +static void tcm_qla2xxx_release_fabric_acl( + struct se_portal_group *se_tpg, + struct se_node_acl *se_nacl) +{ + struct tcm_qla2xxx_nacl *nacl = container_of(se_nacl, + struct tcm_qla2xxx_nacl, se_node_acl); + kfree(nacl); +} + +static u32 tcm_qla2xxx_tpg_get_inst_index(struct se_portal_group *se_tpg) +{ + struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, + struct tcm_qla2xxx_tpg, se_tpg); + + return tpg->lport_tpgt; +} + +static void tcm_qla2xxx_complete_mcmd(struct work_struct *work) +{ + struct qla_tgt_mgmt_cmd *mcmd = container_of(work, + struct qla_tgt_mgmt_cmd, free_work); + + transport_generic_free_cmd(&mcmd->se_cmd, 0); +} + +/* + * Called from qla_target_template->free_mcmd(), and will call + * tcm_qla2xxx_release_cmd() via normal struct target_core_fabric_ops + * release callback. qla_hw_data->hardware_lock is expected to be held + */ +static void tcm_qla2xxx_free_mcmd(struct qla_tgt_mgmt_cmd *mcmd) +{ + INIT_WORK(&mcmd->free_work, tcm_qla2xxx_complete_mcmd); + queue_work(tcm_qla2xxx_free_wq, &mcmd->free_work); +} + +static void tcm_qla2xxx_complete_free(struct work_struct *work) +{ + struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work); + + transport_generic_free_cmd(&cmd->se_cmd, 0); +} + +/* + * Called from qla_target_template->free_cmd(), and will call + * tcm_qla2xxx_release_cmd via normal struct target_core_fabric_ops + * release callback. qla_hw_data->hardware_lock is expected to be held + */ +static void tcm_qla2xxx_free_cmd(struct qla_tgt_cmd *cmd) +{ + INIT_WORK(&cmd->work, tcm_qla2xxx_complete_free); + queue_work(tcm_qla2xxx_free_wq, &cmd->work); +} + +/* + * Called from struct target_core_fabric_ops->check_stop_free() context + */ +static int tcm_qla2xxx_check_stop_free(struct se_cmd *se_cmd) +{ + return target_put_sess_cmd(se_cmd->se_sess, se_cmd); +} + +/* tcm_qla2xxx_release_cmd - Callback from TCM Core to release underlying + * fabric descriptor @se_cmd command to release + */ +static void tcm_qla2xxx_release_cmd(struct se_cmd *se_cmd) +{ + struct qla_tgt_cmd *cmd; + + if (se_cmd->se_cmd_flags & SCF_SCSI_TMR_CDB) { + struct qla_tgt_mgmt_cmd *mcmd = container_of(se_cmd, + struct qla_tgt_mgmt_cmd, se_cmd); + qlt_free_mcmd(mcmd); + return; + } + + cmd = container_of(se_cmd, struct qla_tgt_cmd, se_cmd); + qlt_free_cmd(cmd); +} + +static int tcm_qla2xxx_shutdown_session(struct se_session *se_sess) +{ + struct qla_tgt_sess *sess = se_sess->fabric_sess_ptr; + struct scsi_qla_host *vha; + unsigned long flags; + + BUG_ON(!sess); + vha = sess->vha; + + spin_lock_irqsave(&vha->hw->hardware_lock, flags); + target_sess_cmd_list_set_waiting(se_sess); + spin_unlock_irqrestore(&vha->hw->hardware_lock, flags); + + return 1; +} + +static void tcm_qla2xxx_close_session(struct se_session *se_sess) +{ + struct qla_tgt_sess *sess = se_sess->fabric_sess_ptr; + struct scsi_qla_host *vha; + unsigned long flags; + + BUG_ON(!sess); + vha = sess->vha; + + spin_lock_irqsave(&vha->hw->hardware_lock, flags); + qlt_unreg_sess(sess); + spin_unlock_irqrestore(&vha->hw->hardware_lock, flags); +} + +static u32 tcm_qla2xxx_sess_get_index(struct se_session *se_sess) +{ + return 0; +} + +static int tcm_qla2xxx_write_pending(struct se_cmd *se_cmd) +{ + struct qla_tgt_cmd *cmd = container_of(se_cmd, + struct qla_tgt_cmd, se_cmd); + + cmd->bufflen = se_cmd->data_length; + cmd->dma_data_direction = target_reverse_dma_direction(se_cmd); + + cmd->sg_cnt = se_cmd->t_data_nents; + cmd->sg = se_cmd->t_data_sg; + + cmd->prot_sg_cnt = se_cmd->t_prot_nents; + cmd->prot_sg = se_cmd->t_prot_sg; + cmd->blk_sz = se_cmd->se_dev->dev_attrib.block_size; + se_cmd->pi_err = 0; + + /* + * qla_target.c:qlt_rdy_to_xfer() will call pci_map_sg() to setup + * the SGL mappings into PCIe memory for incoming FCP WRITE data. + */ + return qlt_rdy_to_xfer(cmd); +} + +static int tcm_qla2xxx_write_pending_status(struct se_cmd *se_cmd) +{ + unsigned long flags; + /* + * Check for WRITE_PENDING status to determine if we need to wait for + * CTIO aborts to be posted via hardware in tcm_qla2xxx_handle_data(). + */ + spin_lock_irqsave(&se_cmd->t_state_lock, flags); + if (se_cmd->t_state == TRANSPORT_WRITE_PENDING || + se_cmd->t_state == TRANSPORT_COMPLETE_QF_WP) { + spin_unlock_irqrestore(&se_cmd->t_state_lock, flags); + wait_for_completion_timeout(&se_cmd->t_transport_stop_comp, + 3000); + return 0; + } + spin_unlock_irqrestore(&se_cmd->t_state_lock, flags); + + return 0; +} + +static void tcm_qla2xxx_set_default_node_attrs(struct se_node_acl *nacl) +{ + return; +} + +static u32 tcm_qla2xxx_get_task_tag(struct se_cmd *se_cmd) +{ + struct qla_tgt_cmd *cmd = container_of(se_cmd, + struct qla_tgt_cmd, se_cmd); + + return cmd->tag; +} + +static int tcm_qla2xxx_get_cmd_state(struct se_cmd *se_cmd) +{ + return 0; +} + +/* + * Called from process context in qla_target.c:qlt_do_work() code + */ +static int tcm_qla2xxx_handle_cmd(scsi_qla_host_t *vha, struct qla_tgt_cmd *cmd, + unsigned char *cdb, uint32_t data_length, int fcp_task_attr, + int data_dir, int bidi) +{ + struct se_cmd *se_cmd = &cmd->se_cmd; + struct se_session *se_sess; + struct qla_tgt_sess *sess; + int flags = TARGET_SCF_ACK_KREF; + + if (bidi) + flags |= TARGET_SCF_BIDI_OP; + + sess = cmd->sess; + if (!sess) { + pr_err("Unable to locate struct qla_tgt_sess from qla_tgt_cmd\n"); + return -EINVAL; + } + + se_sess = sess->se_sess; + if (!se_sess) { + pr_err("Unable to locate active struct se_session\n"); + return -EINVAL; + } + + return target_submit_cmd(se_cmd, se_sess, cdb, &cmd->sense_buffer[0], + cmd->unpacked_lun, data_length, fcp_task_attr, + data_dir, flags); +} + +static void tcm_qla2xxx_handle_data_work(struct work_struct *work) +{ + struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work); + + /* + * Ensure that the complete FCP WRITE payload has been received. + * Otherwise return an exception via CHECK_CONDITION status. + */ + if (!cmd->write_data_transferred) { + /* + * Check if se_cmd has already been aborted via LUN_RESET, and + * waiting upon completion in tcm_qla2xxx_write_pending_status() + */ + if (cmd->se_cmd.transport_state & CMD_T_ABORTED) { + complete(&cmd->se_cmd.t_transport_stop_comp); + return; + } + + if (cmd->se_cmd.pi_err) + transport_generic_request_failure(&cmd->se_cmd, + cmd->se_cmd.pi_err); + else + transport_generic_request_failure(&cmd->se_cmd, + TCM_CHECK_CONDITION_ABORT_CMD); + + return; + } + + return target_execute_cmd(&cmd->se_cmd); +} + +/* + * Called from qla_target.c:qlt_do_ctio_completion() + */ +static void tcm_qla2xxx_handle_data(struct qla_tgt_cmd *cmd) +{ + INIT_WORK(&cmd->work, tcm_qla2xxx_handle_data_work); + queue_work(tcm_qla2xxx_free_wq, &cmd->work); +} + +static void tcm_qla2xxx_handle_dif_work(struct work_struct *work) +{ + struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work); + + /* take an extra kref to prevent cmd free too early. + * need to wait for SCSI status/check condition to + * finish responding generate by transport_generic_request_failure. + */ + kref_get(&cmd->se_cmd.cmd_kref); + transport_generic_request_failure(&cmd->se_cmd, cmd->se_cmd.pi_err); +} + +/* + * Called from qla_target.c:qlt_do_ctio_completion() + */ +static void tcm_qla2xxx_handle_dif_err(struct qla_tgt_cmd *cmd) +{ + INIT_WORK(&cmd->work, tcm_qla2xxx_handle_dif_work); + queue_work(tcm_qla2xxx_free_wq, &cmd->work); +} + +/* + * Called from qla_target.c:qlt_issue_task_mgmt() + */ +static int tcm_qla2xxx_handle_tmr(struct qla_tgt_mgmt_cmd *mcmd, uint32_t lun, + uint8_t tmr_func, uint32_t tag) +{ + struct qla_tgt_sess *sess = mcmd->sess; + struct se_cmd *se_cmd = &mcmd->se_cmd; + + return target_submit_tmr(se_cmd, sess->se_sess, NULL, lun, mcmd, + tmr_func, GFP_ATOMIC, tag, TARGET_SCF_ACK_KREF); +} + +static int tcm_qla2xxx_queue_data_in(struct se_cmd *se_cmd) +{ + struct qla_tgt_cmd *cmd = container_of(se_cmd, + struct qla_tgt_cmd, se_cmd); + + cmd->bufflen = se_cmd->data_length; + cmd->dma_data_direction = target_reverse_dma_direction(se_cmd); + cmd->aborted = (se_cmd->transport_state & CMD_T_ABORTED); + + cmd->sg_cnt = se_cmd->t_data_nents; + cmd->sg = se_cmd->t_data_sg; + cmd->offset = 0; + + cmd->prot_sg_cnt = se_cmd->t_prot_nents; + cmd->prot_sg = se_cmd->t_prot_sg; + cmd->blk_sz = se_cmd->se_dev->dev_attrib.block_size; + se_cmd->pi_err = 0; + + /* + * Now queue completed DATA_IN the qla2xxx LLD and response ring + */ + return qlt_xmit_response(cmd, QLA_TGT_XMIT_DATA|QLA_TGT_XMIT_STATUS, + se_cmd->scsi_status); +} + +static int tcm_qla2xxx_queue_status(struct se_cmd *se_cmd) +{ + struct qla_tgt_cmd *cmd = container_of(se_cmd, + struct qla_tgt_cmd, se_cmd); + int xmit_type = QLA_TGT_XMIT_STATUS; + + cmd->bufflen = se_cmd->data_length; + cmd->sg = NULL; + cmd->sg_cnt = 0; + cmd->offset = 0; + cmd->dma_data_direction = target_reverse_dma_direction(se_cmd); + cmd->aborted = (se_cmd->transport_state & CMD_T_ABORTED); + + if (se_cmd->data_direction == DMA_FROM_DEVICE) { + /* + * For FCP_READ with CHECK_CONDITION status, clear cmd->bufflen + * for qla_tgt_xmit_response LLD code + */ + if (se_cmd->se_cmd_flags & SCF_OVERFLOW_BIT) { + se_cmd->se_cmd_flags &= ~SCF_OVERFLOW_BIT; + se_cmd->residual_count = 0; + } + se_cmd->se_cmd_flags |= SCF_UNDERFLOW_BIT; + se_cmd->residual_count += se_cmd->data_length; + + cmd->bufflen = 0; + } + /* + * Now queue status response to qla2xxx LLD code and response ring + */ + return qlt_xmit_response(cmd, xmit_type, se_cmd->scsi_status); +} + +static void tcm_qla2xxx_queue_tm_rsp(struct se_cmd *se_cmd) +{ + struct se_tmr_req *se_tmr = se_cmd->se_tmr_req; + struct qla_tgt_mgmt_cmd *mcmd = container_of(se_cmd, + struct qla_tgt_mgmt_cmd, se_cmd); + + pr_debug("queue_tm_rsp: mcmd: %p func: 0x%02x response: 0x%02x\n", + mcmd, se_tmr->function, se_tmr->response); + /* + * Do translation between TCM TM response codes and + * QLA2xxx FC TM response codes. + */ + switch (se_tmr->response) { + case TMR_FUNCTION_COMPLETE: + mcmd->fc_tm_rsp = FC_TM_SUCCESS; + break; + case TMR_TASK_DOES_NOT_EXIST: + mcmd->fc_tm_rsp = FC_TM_BAD_CMD; + break; + case TMR_FUNCTION_REJECTED: + mcmd->fc_tm_rsp = FC_TM_REJECT; + break; + case TMR_LUN_DOES_NOT_EXIST: + default: + mcmd->fc_tm_rsp = FC_TM_FAILED; + break; + } + /* + * Queue the TM response to QLA2xxx LLD to build a + * CTIO response packet. + */ + qlt_xmit_tm_rsp(mcmd); +} + +static void tcm_qla2xxx_aborted_task(struct se_cmd *se_cmd) +{ + struct qla_tgt_cmd *cmd = container_of(se_cmd, + struct qla_tgt_cmd, se_cmd); + struct scsi_qla_host *vha = cmd->vha; + struct qla_hw_data *ha = vha->hw; + + if (!cmd->sg_mapped) + return; + + pci_unmap_sg(ha->pdev, cmd->sg, cmd->sg_cnt, cmd->dma_data_direction); + cmd->sg_mapped = 0; +} + +/* Local pointer to allocated TCM configfs fabric module */ +struct target_fabric_configfs *tcm_qla2xxx_fabric_configfs; +struct target_fabric_configfs *tcm_qla2xxx_npiv_fabric_configfs; + +static void tcm_qla2xxx_clear_sess_lookup(struct tcm_qla2xxx_lport *, + struct tcm_qla2xxx_nacl *, struct qla_tgt_sess *); +/* + * Expected to be called with struct qla_hw_data->hardware_lock held + */ +static void tcm_qla2xxx_clear_nacl_from_fcport_map(struct qla_tgt_sess *sess) +{ + struct se_node_acl *se_nacl = sess->se_sess->se_node_acl; + struct se_portal_group *se_tpg = se_nacl->se_tpg; + struct se_wwn *se_wwn = se_tpg->se_tpg_wwn; + struct tcm_qla2xxx_lport *lport = container_of(se_wwn, + struct tcm_qla2xxx_lport, lport_wwn); + struct tcm_qla2xxx_nacl *nacl = container_of(se_nacl, + struct tcm_qla2xxx_nacl, se_node_acl); + void *node; + + pr_debug("fc_rport domain: port_id 0x%06x\n", nacl->nport_id); + + node = btree_remove32(&lport->lport_fcport_map, nacl->nport_id); + WARN_ON(node && (node != se_nacl)); + + pr_debug("Removed from fcport_map: %p for WWNN: 0x%016LX, port_id: 0x%06x\n", + se_nacl, nacl->nport_wwnn, nacl->nport_id); + /* + * Now clear the se_nacl and session pointers from our HW lport lookup + * table mapping for this initiator's fabric S_ID and LOOP_ID entries. + * + * This is done ahead of callbacks into tcm_qla2xxx_free_session() -> + * target_wait_for_sess_cmds() before the session waits for outstanding + * I/O to complete, to avoid a race between session shutdown execution + * and incoming ATIOs or TMRs picking up a stale se_node_act reference. + */ + tcm_qla2xxx_clear_sess_lookup(lport, nacl, sess); +} + +static void tcm_qla2xxx_release_session(struct kref *kref) +{ + struct se_session *se_sess = container_of(kref, + struct se_session, sess_kref); + + qlt_unreg_sess(se_sess->fabric_sess_ptr); +} + +static void tcm_qla2xxx_put_session(struct se_session *se_sess) +{ + struct qla_tgt_sess *sess = se_sess->fabric_sess_ptr; + struct qla_hw_data *ha = sess->vha->hw; + unsigned long flags; + + spin_lock_irqsave(&ha->hardware_lock, flags); + kref_put(&se_sess->sess_kref, tcm_qla2xxx_release_session); + spin_unlock_irqrestore(&ha->hardware_lock, flags); +} + +static void tcm_qla2xxx_put_sess(struct qla_tgt_sess *sess) +{ + if (!sess) + return; + + assert_spin_locked(&sess->vha->hw->hardware_lock); + kref_put(&sess->se_sess->sess_kref, tcm_qla2xxx_release_session); +} + +static void tcm_qla2xxx_shutdown_sess(struct qla_tgt_sess *sess) +{ + assert_spin_locked(&sess->vha->hw->hardware_lock); + target_sess_cmd_list_set_waiting(sess->se_sess); +} + +static struct se_node_acl *tcm_qla2xxx_make_nodeacl( + struct se_portal_group *se_tpg, + struct config_group *group, + const char *name) +{ + struct se_node_acl *se_nacl, *se_nacl_new; + struct tcm_qla2xxx_nacl *nacl; + u64 wwnn; + u32 qla2xxx_nexus_depth; + + if (tcm_qla2xxx_parse_wwn(name, &wwnn, 1) < 0) + return ERR_PTR(-EINVAL); + + se_nacl_new = tcm_qla2xxx_alloc_fabric_acl(se_tpg); + if (!se_nacl_new) + return ERR_PTR(-ENOMEM); +/* #warning FIXME: Hardcoded qla2xxx_nexus depth in tcm_qla2xxx_make_nodeacl */ + qla2xxx_nexus_depth = 1; + + /* + * se_nacl_new may be released by core_tpg_add_initiator_node_acl() + * when converting a NodeACL from demo mode -> explict + */ + se_nacl = core_tpg_add_initiator_node_acl(se_tpg, se_nacl_new, + name, qla2xxx_nexus_depth); + if (IS_ERR(se_nacl)) { + tcm_qla2xxx_release_fabric_acl(se_tpg, se_nacl_new); + return se_nacl; + } + /* + * Locate our struct tcm_qla2xxx_nacl and set the FC Nport WWPN + */ + nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl); + nacl->nport_wwnn = wwnn; + tcm_qla2xxx_format_wwn(&nacl->nport_name[0], TCM_QLA2XXX_NAMELEN, wwnn); + + return se_nacl; +} + +static void tcm_qla2xxx_drop_nodeacl(struct se_node_acl *se_acl) +{ + struct se_portal_group *se_tpg = se_acl->se_tpg; + struct tcm_qla2xxx_nacl *nacl = container_of(se_acl, + struct tcm_qla2xxx_nacl, se_node_acl); + + core_tpg_del_initiator_node_acl(se_tpg, se_acl, 1); + kfree(nacl); +} + +/* Start items for tcm_qla2xxx_tpg_attrib_cit */ + +#define DEF_QLA_TPG_ATTRIB(name) \ + \ +static ssize_t tcm_qla2xxx_tpg_attrib_show_##name( \ + struct se_portal_group *se_tpg, \ + char *page) \ +{ \ + struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, \ + struct tcm_qla2xxx_tpg, se_tpg); \ + \ + return sprintf(page, "%u\n", tpg->tpg_attrib.name); \ +} \ + \ +static ssize_t tcm_qla2xxx_tpg_attrib_store_##name( \ + struct se_portal_group *se_tpg, \ + const char *page, \ + size_t count) \ +{ \ + struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, \ + struct tcm_qla2xxx_tpg, se_tpg); \ + unsigned long val; \ + int ret; \ + \ + ret = kstrtoul(page, 0, &val); \ + if (ret < 0) { \ + pr_err("kstrtoul() failed with" \ + " ret: %d\n", ret); \ + return -EINVAL; \ + } \ + ret = tcm_qla2xxx_set_attrib_##name(tpg, val); \ + \ + return (!ret) ? count : -EINVAL; \ +} + +#define DEF_QLA_TPG_ATTR_BOOL(_name) \ + \ +static int tcm_qla2xxx_set_attrib_##_name( \ + struct tcm_qla2xxx_tpg *tpg, \ + unsigned long val) \ +{ \ + struct tcm_qla2xxx_tpg_attrib *a = &tpg->tpg_attrib; \ + \ + if ((val != 0) && (val != 1)) { \ + pr_err("Illegal boolean value %lu\n", val); \ + return -EINVAL; \ + } \ + \ + a->_name = val; \ + return 0; \ +} + +#define QLA_TPG_ATTR(_name, _mode) \ + TF_TPG_ATTRIB_ATTR(tcm_qla2xxx, _name, _mode); + +/* + * Define tcm_qla2xxx_tpg_attrib_s_generate_node_acls + */ +DEF_QLA_TPG_ATTR_BOOL(generate_node_acls); +DEF_QLA_TPG_ATTRIB(generate_node_acls); +QLA_TPG_ATTR(generate_node_acls, S_IRUGO | S_IWUSR); + +/* + Define tcm_qla2xxx_attrib_s_cache_dynamic_acls + */ +DEF_QLA_TPG_ATTR_BOOL(cache_dynamic_acls); +DEF_QLA_TPG_ATTRIB(cache_dynamic_acls); +QLA_TPG_ATTR(cache_dynamic_acls, S_IRUGO | S_IWUSR); + +/* + * Define tcm_qla2xxx_tpg_attrib_s_demo_mode_write_protect + */ +DEF_QLA_TPG_ATTR_BOOL(demo_mode_write_protect); +DEF_QLA_TPG_ATTRIB(demo_mode_write_protect); +QLA_TPG_ATTR(demo_mode_write_protect, S_IRUGO | S_IWUSR); + +/* + * Define tcm_qla2xxx_tpg_attrib_s_prod_mode_write_protect + */ +DEF_QLA_TPG_ATTR_BOOL(prod_mode_write_protect); +DEF_QLA_TPG_ATTRIB(prod_mode_write_protect); +QLA_TPG_ATTR(prod_mode_write_protect, S_IRUGO | S_IWUSR); + +/* + * Define tcm_qla2xxx_tpg_attrib_s_demo_mode_login_only + */ +DEF_QLA_TPG_ATTR_BOOL(demo_mode_login_only); +DEF_QLA_TPG_ATTRIB(demo_mode_login_only); +QLA_TPG_ATTR(demo_mode_login_only, S_IRUGO | S_IWUSR); + +static struct configfs_attribute *tcm_qla2xxx_tpg_attrib_attrs[] = { + &tcm_qla2xxx_tpg_attrib_generate_node_acls.attr, + &tcm_qla2xxx_tpg_attrib_cache_dynamic_acls.attr, + &tcm_qla2xxx_tpg_attrib_demo_mode_write_protect.attr, + &tcm_qla2xxx_tpg_attrib_prod_mode_write_protect.attr, + &tcm_qla2xxx_tpg_attrib_demo_mode_login_only.attr, + NULL, +}; + +/* End items for tcm_qla2xxx_tpg_attrib_cit */ + +static ssize_t tcm_qla2xxx_tpg_show_enable( + struct se_portal_group *se_tpg, + char *page) +{ + struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, + struct tcm_qla2xxx_tpg, se_tpg); + + return snprintf(page, PAGE_SIZE, "%d\n", + atomic_read(&tpg->lport_tpg_enabled)); +} + +static void tcm_qla2xxx_depend_tpg(struct work_struct *work) +{ + struct tcm_qla2xxx_tpg *base_tpg = container_of(work, + struct tcm_qla2xxx_tpg, tpg_base_work); + struct se_portal_group *se_tpg = &base_tpg->se_tpg; + struct scsi_qla_host *base_vha = base_tpg->lport->qla_vha; + + if (!configfs_depend_item(se_tpg->se_tpg_tfo->tf_subsys, + &se_tpg->tpg_group.cg_item)) { + atomic_set(&base_tpg->lport_tpg_enabled, 1); + qlt_enable_vha(base_vha); + } + complete(&base_tpg->tpg_base_comp); +} + +static void tcm_qla2xxx_undepend_tpg(struct work_struct *work) +{ + struct tcm_qla2xxx_tpg *base_tpg = container_of(work, + struct tcm_qla2xxx_tpg, tpg_base_work); + struct se_portal_group *se_tpg = &base_tpg->se_tpg; + struct scsi_qla_host *base_vha = base_tpg->lport->qla_vha; + + if (!qlt_stop_phase1(base_vha->vha_tgt.qla_tgt)) { + atomic_set(&base_tpg->lport_tpg_enabled, 0); + configfs_undepend_item(se_tpg->se_tpg_tfo->tf_subsys, + &se_tpg->tpg_group.cg_item); + } + complete(&base_tpg->tpg_base_comp); +} + +static ssize_t tcm_qla2xxx_tpg_store_enable( + struct se_portal_group *se_tpg, + const char *page, + size_t count) +{ + struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, + struct tcm_qla2xxx_tpg, se_tpg); + unsigned long op; + int rc; + + rc = kstrtoul(page, 0, &op); + if (rc < 0) { + pr_err("kstrtoul() returned %d\n", rc); + return -EINVAL; + } + if ((op != 1) && (op != 0)) { + pr_err("Illegal value for tpg_enable: %lu\n", op); + return -EINVAL; + } + if (op) { + if (atomic_read(&tpg->lport_tpg_enabled)) + return -EEXIST; + + INIT_WORK(&tpg->tpg_base_work, tcm_qla2xxx_depend_tpg); + } else { + if (!atomic_read(&tpg->lport_tpg_enabled)) + return count; + + INIT_WORK(&tpg->tpg_base_work, tcm_qla2xxx_undepend_tpg); + } + init_completion(&tpg->tpg_base_comp); + schedule_work(&tpg->tpg_base_work); + wait_for_completion(&tpg->tpg_base_comp); + + if (op) { + if (!atomic_read(&tpg->lport_tpg_enabled)) + return -ENODEV; + } else { + if (atomic_read(&tpg->lport_tpg_enabled)) + return -EPERM; + } + return count; +} + +TF_TPG_BASE_ATTR(tcm_qla2xxx, enable, S_IRUGO | S_IWUSR); + +static struct configfs_attribute *tcm_qla2xxx_tpg_attrs[] = { + &tcm_qla2xxx_tpg_enable.attr, + NULL, +}; + +static struct se_portal_group *tcm_qla2xxx_make_tpg( + struct se_wwn *wwn, + struct config_group *group, + const char *name) +{ + struct tcm_qla2xxx_lport *lport = container_of(wwn, + struct tcm_qla2xxx_lport, lport_wwn); + struct tcm_qla2xxx_tpg *tpg; + unsigned long tpgt; + int ret; + + if (strstr(name, "tpgt_") != name) + return ERR_PTR(-EINVAL); + if (kstrtoul(name + 5, 10, &tpgt) || tpgt > USHRT_MAX) + return ERR_PTR(-EINVAL); + + if ((tpgt != 1)) { + pr_err("In non NPIV mode, a single TPG=1 is used for HW port mappings\n"); + return ERR_PTR(-ENOSYS); + } + + tpg = kzalloc(sizeof(struct tcm_qla2xxx_tpg), GFP_KERNEL); + if (!tpg) { + pr_err("Unable to allocate struct tcm_qla2xxx_tpg\n"); + return ERR_PTR(-ENOMEM); + } + tpg->lport = lport; + tpg->lport_tpgt = tpgt; + /* + * By default allow READ-ONLY TPG demo-mode access w/ cached dynamic + * NodeACLs + */ + tpg->tpg_attrib.generate_node_acls = 1; + tpg->tpg_attrib.demo_mode_write_protect = 1; + tpg->tpg_attrib.cache_dynamic_acls = 1; + tpg->tpg_attrib.demo_mode_login_only = 1; + + ret = core_tpg_register(&tcm_qla2xxx_fabric_configfs->tf_ops, wwn, + &tpg->se_tpg, tpg, TRANSPORT_TPG_TYPE_NORMAL); + if (ret < 0) { + kfree(tpg); + return NULL; + } + + lport->tpg_1 = tpg; + + return &tpg->se_tpg; +} + +static void tcm_qla2xxx_drop_tpg(struct se_portal_group *se_tpg) +{ + struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, + struct tcm_qla2xxx_tpg, se_tpg); + struct tcm_qla2xxx_lport *lport = tpg->lport; + struct scsi_qla_host *vha = lport->qla_vha; + /* + * Call into qla2x_target.c LLD logic to shutdown the active + * FC Nexuses and disable target mode operation for this qla_hw_data + */ + if (vha->vha_tgt.qla_tgt && !vha->vha_tgt.qla_tgt->tgt_stop) + qlt_stop_phase1(vha->vha_tgt.qla_tgt); + + core_tpg_deregister(se_tpg); + /* + * Clear local TPG=1 pointer for non NPIV mode. + */ + lport->tpg_1 = NULL; + kfree(tpg); +} + +static ssize_t tcm_qla2xxx_npiv_tpg_show_enable( + struct se_portal_group *se_tpg, + char *page) +{ + return tcm_qla2xxx_tpg_show_enable(se_tpg, page); +} + +static ssize_t tcm_qla2xxx_npiv_tpg_store_enable( + struct se_portal_group *se_tpg, + const char *page, + size_t count) +{ + struct se_wwn *se_wwn = se_tpg->se_tpg_wwn; + struct tcm_qla2xxx_lport *lport = container_of(se_wwn, + struct tcm_qla2xxx_lport, lport_wwn); + struct scsi_qla_host *vha = lport->qla_vha; + struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, + struct tcm_qla2xxx_tpg, se_tpg); + unsigned long op; + int rc; + + rc = kstrtoul(page, 0, &op); + if (rc < 0) { + pr_err("kstrtoul() returned %d\n", rc); + return -EINVAL; + } + if ((op != 1) && (op != 0)) { + pr_err("Illegal value for tpg_enable: %lu\n", op); + return -EINVAL; + } + if (op) { + if (atomic_read(&tpg->lport_tpg_enabled)) + return -EEXIST; + + atomic_set(&tpg->lport_tpg_enabled, 1); + qlt_enable_vha(vha); + } else { + if (!atomic_read(&tpg->lport_tpg_enabled)) + return count; + + atomic_set(&tpg->lport_tpg_enabled, 0); + qlt_stop_phase1(vha->vha_tgt.qla_tgt); + } + + return count; +} + +TF_TPG_BASE_ATTR(tcm_qla2xxx_npiv, enable, S_IRUGO | S_IWUSR); + +static struct configfs_attribute *tcm_qla2xxx_npiv_tpg_attrs[] = { + &tcm_qla2xxx_npiv_tpg_enable.attr, + NULL, +}; + +static struct se_portal_group *tcm_qla2xxx_npiv_make_tpg( + struct se_wwn *wwn, + struct config_group *group, + const char *name) +{ + struct tcm_qla2xxx_lport *lport = container_of(wwn, + struct tcm_qla2xxx_lport, lport_wwn); + struct tcm_qla2xxx_tpg *tpg; + unsigned long tpgt; + int ret; + + if (strstr(name, "tpgt_") != name) + return ERR_PTR(-EINVAL); + if (kstrtoul(name + 5, 10, &tpgt) || tpgt > USHRT_MAX) + return ERR_PTR(-EINVAL); + + tpg = kzalloc(sizeof(struct tcm_qla2xxx_tpg), GFP_KERNEL); + if (!tpg) { + pr_err("Unable to allocate struct tcm_qla2xxx_tpg\n"); + return ERR_PTR(-ENOMEM); + } + tpg->lport = lport; + tpg->lport_tpgt = tpgt; + + /* + * By default allow READ-ONLY TPG demo-mode access w/ cached dynamic + * NodeACLs + */ + tpg->tpg_attrib.generate_node_acls = 1; + tpg->tpg_attrib.demo_mode_write_protect = 1; + tpg->tpg_attrib.cache_dynamic_acls = 1; + tpg->tpg_attrib.demo_mode_login_only = 1; + + ret = core_tpg_register(&tcm_qla2xxx_npiv_fabric_configfs->tf_ops, wwn, + &tpg->se_tpg, tpg, TRANSPORT_TPG_TYPE_NORMAL); + if (ret < 0) { + kfree(tpg); + return NULL; + } + lport->tpg_1 = tpg; + return &tpg->se_tpg; +} + +/* + * Expected to be called with struct qla_hw_data->hardware_lock held + */ +static struct qla_tgt_sess *tcm_qla2xxx_find_sess_by_s_id( + scsi_qla_host_t *vha, + const uint8_t *s_id) +{ + struct tcm_qla2xxx_lport *lport; + struct se_node_acl *se_nacl; + struct tcm_qla2xxx_nacl *nacl; + u32 key; + + lport = vha->vha_tgt.target_lport_ptr; + if (!lport) { + pr_err("Unable to locate struct tcm_qla2xxx_lport\n"); + dump_stack(); + return NULL; + } + + key = (((unsigned long)s_id[0] << 16) | + ((unsigned long)s_id[1] << 8) | + (unsigned long)s_id[2]); + pr_debug("find_sess_by_s_id: 0x%06x\n", key); + + se_nacl = btree_lookup32(&lport->lport_fcport_map, key); + if (!se_nacl) { + pr_debug("Unable to locate s_id: 0x%06x\n", key); + return NULL; + } + pr_debug("find_sess_by_s_id: located se_nacl: %p, initiatorname: %s\n", + se_nacl, se_nacl->initiatorname); + + nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl); + if (!nacl->qla_tgt_sess) { + pr_err("Unable to locate struct qla_tgt_sess\n"); + return NULL; + } + + return nacl->qla_tgt_sess; +} + +/* + * Expected to be called with struct qla_hw_data->hardware_lock held + */ +static void tcm_qla2xxx_set_sess_by_s_id( + struct tcm_qla2xxx_lport *lport, + struct se_node_acl *new_se_nacl, + struct tcm_qla2xxx_nacl *nacl, + struct se_session *se_sess, + struct qla_tgt_sess *qla_tgt_sess, + uint8_t *s_id) +{ + u32 key; + void *slot; + int rc; + + key = (((unsigned long)s_id[0] << 16) | + ((unsigned long)s_id[1] << 8) | + (unsigned long)s_id[2]); + pr_debug("set_sess_by_s_id: %06x\n", key); + + slot = btree_lookup32(&lport->lport_fcport_map, key); + if (!slot) { + if (new_se_nacl) { + pr_debug("Setting up new fc_port entry to new_se_nacl\n"); + nacl->nport_id = key; + rc = btree_insert32(&lport->lport_fcport_map, key, + new_se_nacl, GFP_ATOMIC); + if (rc) + printk(KERN_ERR "Unable to insert s_id into fcport_map: %06x\n", + (int)key); + } else { + pr_debug("Wiping nonexisting fc_port entry\n"); + } + + qla_tgt_sess->se_sess = se_sess; + nacl->qla_tgt_sess = qla_tgt_sess; + return; + } + + if (nacl->qla_tgt_sess) { + if (new_se_nacl == NULL) { + pr_debug("Clearing existing nacl->qla_tgt_sess and fc_port entry\n"); + btree_remove32(&lport->lport_fcport_map, key); + nacl->qla_tgt_sess = NULL; + return; + } + pr_debug("Replacing existing nacl->qla_tgt_sess and fc_port entry\n"); + btree_update32(&lport->lport_fcport_map, key, new_se_nacl); + qla_tgt_sess->se_sess = se_sess; + nacl->qla_tgt_sess = qla_tgt_sess; + return; + } + + if (new_se_nacl == NULL) { + pr_debug("Clearing existing fc_port entry\n"); + btree_remove32(&lport->lport_fcport_map, key); + return; + } + + pr_debug("Replacing existing fc_port entry w/o active nacl->qla_tgt_sess\n"); + btree_update32(&lport->lport_fcport_map, key, new_se_nacl); + qla_tgt_sess->se_sess = se_sess; + nacl->qla_tgt_sess = qla_tgt_sess; + + pr_debug("Setup nacl->qla_tgt_sess %p by s_id for se_nacl: %p, initiatorname: %s\n", + nacl->qla_tgt_sess, new_se_nacl, new_se_nacl->initiatorname); +} + +/* + * Expected to be called with struct qla_hw_data->hardware_lock held + */ +static struct qla_tgt_sess *tcm_qla2xxx_find_sess_by_loop_id( + scsi_qla_host_t *vha, + const uint16_t loop_id) +{ + struct tcm_qla2xxx_lport *lport; + struct se_node_acl *se_nacl; + struct tcm_qla2xxx_nacl *nacl; + struct tcm_qla2xxx_fc_loopid *fc_loopid; + + lport = vha->vha_tgt.target_lport_ptr; + if (!lport) { + pr_err("Unable to locate struct tcm_qla2xxx_lport\n"); + dump_stack(); + return NULL; + } + + pr_debug("find_sess_by_loop_id: Using loop_id: 0x%04x\n", loop_id); + + fc_loopid = lport->lport_loopid_map + loop_id; + se_nacl = fc_loopid->se_nacl; + if (!se_nacl) { + pr_debug("Unable to locate se_nacl by loop_id: 0x%04x\n", + loop_id); + return NULL; + } + + nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl); + + if (!nacl->qla_tgt_sess) { + pr_err("Unable to locate struct qla_tgt_sess\n"); + return NULL; + } + + return nacl->qla_tgt_sess; +} + +/* + * Expected to be called with struct qla_hw_data->hardware_lock held + */ +static void tcm_qla2xxx_set_sess_by_loop_id( + struct tcm_qla2xxx_lport *lport, + struct se_node_acl *new_se_nacl, + struct tcm_qla2xxx_nacl *nacl, + struct se_session *se_sess, + struct qla_tgt_sess *qla_tgt_sess, + uint16_t loop_id) +{ + struct se_node_acl *saved_nacl; + struct tcm_qla2xxx_fc_loopid *fc_loopid; + + pr_debug("set_sess_by_loop_id: Using loop_id: 0x%04x\n", loop_id); + + fc_loopid = &((struct tcm_qla2xxx_fc_loopid *) + lport->lport_loopid_map)[loop_id]; + + saved_nacl = fc_loopid->se_nacl; + if (!saved_nacl) { + pr_debug("Setting up new fc_loopid->se_nacl to new_se_nacl\n"); + fc_loopid->se_nacl = new_se_nacl; + if (qla_tgt_sess->se_sess != se_sess) + qla_tgt_sess->se_sess = se_sess; + if (nacl->qla_tgt_sess != qla_tgt_sess) + nacl->qla_tgt_sess = qla_tgt_sess; + return; + } + + if (nacl->qla_tgt_sess) { + if (new_se_nacl == NULL) { + pr_debug("Clearing nacl->qla_tgt_sess and fc_loopid->se_nacl\n"); + fc_loopid->se_nacl = NULL; + nacl->qla_tgt_sess = NULL; + return; + } + + pr_debug("Replacing existing nacl->qla_tgt_sess and fc_loopid->se_nacl\n"); + fc_loopid->se_nacl = new_se_nacl; + if (qla_tgt_sess->se_sess != se_sess) + qla_tgt_sess->se_sess = se_sess; + if (nacl->qla_tgt_sess != qla_tgt_sess) + nacl->qla_tgt_sess = qla_tgt_sess; + return; + } + + if (new_se_nacl == NULL) { + pr_debug("Clearing fc_loopid->se_nacl\n"); + fc_loopid->se_nacl = NULL; + return; + } + + pr_debug("Replacing existing fc_loopid->se_nacl w/o active nacl->qla_tgt_sess\n"); + fc_loopid->se_nacl = new_se_nacl; + if (qla_tgt_sess->se_sess != se_sess) + qla_tgt_sess->se_sess = se_sess; + if (nacl->qla_tgt_sess != qla_tgt_sess) + nacl->qla_tgt_sess = qla_tgt_sess; + + pr_debug("Setup nacl->qla_tgt_sess %p by loop_id for se_nacl: %p, initiatorname: %s\n", + nacl->qla_tgt_sess, new_se_nacl, new_se_nacl->initiatorname); +} + +/* + * Should always be called with qla_hw_data->hardware_lock held. + */ +static void tcm_qla2xxx_clear_sess_lookup(struct tcm_qla2xxx_lport *lport, + struct tcm_qla2xxx_nacl *nacl, struct qla_tgt_sess *sess) +{ + struct se_session *se_sess = sess->se_sess; + unsigned char be_sid[3]; + + be_sid[0] = sess->s_id.b.domain; + be_sid[1] = sess->s_id.b.area; + be_sid[2] = sess->s_id.b.al_pa; + + tcm_qla2xxx_set_sess_by_s_id(lport, NULL, nacl, se_sess, + sess, be_sid); + tcm_qla2xxx_set_sess_by_loop_id(lport, NULL, nacl, se_sess, + sess, sess->loop_id); +} + +static void tcm_qla2xxx_free_session(struct qla_tgt_sess *sess) +{ + struct qla_tgt *tgt = sess->tgt; + struct qla_hw_data *ha = tgt->ha; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + struct se_session *se_sess; + struct se_node_acl *se_nacl; + struct tcm_qla2xxx_lport *lport; + struct tcm_qla2xxx_nacl *nacl; + + BUG_ON(in_interrupt()); + + se_sess = sess->se_sess; + if (!se_sess) { + pr_err("struct qla_tgt_sess->se_sess is NULL\n"); + dump_stack(); + return; + } + se_nacl = se_sess->se_node_acl; + nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl); + + lport = vha->vha_tgt.target_lport_ptr; + if (!lport) { + pr_err("Unable to locate struct tcm_qla2xxx_lport\n"); + dump_stack(); + return; + } + target_wait_for_sess_cmds(se_sess); + + transport_deregister_session_configfs(sess->se_sess); + transport_deregister_session(sess->se_sess); +} + +/* + * Called via qlt_create_sess():ha->qla2x_tmpl->check_initiator_node_acl() + * to locate struct se_node_acl + */ +static int tcm_qla2xxx_check_initiator_node_acl( + scsi_qla_host_t *vha, + unsigned char *fc_wwpn, + void *qla_tgt_sess, + uint8_t *s_id, + uint16_t loop_id) +{ + struct qla_hw_data *ha = vha->hw; + struct tcm_qla2xxx_lport *lport; + struct tcm_qla2xxx_tpg *tpg; + struct tcm_qla2xxx_nacl *nacl; + struct se_portal_group *se_tpg; + struct se_node_acl *se_nacl; + struct se_session *se_sess; + struct qla_tgt_sess *sess = qla_tgt_sess; + unsigned char port_name[36]; + unsigned long flags; + int num_tags = (ha->fw_xcb_count) ? ha->fw_xcb_count : + TCM_QLA2XXX_DEFAULT_TAGS; + + lport = vha->vha_tgt.target_lport_ptr; + if (!lport) { + pr_err("Unable to locate struct tcm_qla2xxx_lport\n"); + dump_stack(); + return -EINVAL; + } + /* + * Locate the TPG=1 reference.. + */ + tpg = lport->tpg_1; + if (!tpg) { + pr_err("Unable to lcoate struct tcm_qla2xxx_lport->tpg_1\n"); + return -EINVAL; + } + se_tpg = &tpg->se_tpg; + + se_sess = transport_init_session_tags(num_tags, + sizeof(struct qla_tgt_cmd), + TARGET_PROT_NORMAL); + if (IS_ERR(se_sess)) { + pr_err("Unable to initialize struct se_session\n"); + return PTR_ERR(se_sess); + } + /* + * Format the FCP Initiator port_name into colon seperated values to + * match the format by tcm_qla2xxx explict ConfigFS NodeACLs. + */ + memset(&port_name, 0, 36); + snprintf(port_name, 36, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + fc_wwpn[0], fc_wwpn[1], fc_wwpn[2], fc_wwpn[3], fc_wwpn[4], + fc_wwpn[5], fc_wwpn[6], fc_wwpn[7]); + /* + * Locate our struct se_node_acl either from an explict NodeACL created + * via ConfigFS, or via running in TPG demo mode. + */ + se_sess->se_node_acl = core_tpg_check_initiator_node_acl(se_tpg, + port_name); + if (!se_sess->se_node_acl) { + transport_free_session(se_sess); + return -EINVAL; + } + se_nacl = se_sess->se_node_acl; + nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl); + /* + * And now setup the new se_nacl and session pointers into our HW lport + * mappings for fabric S_ID and LOOP_ID. + */ + spin_lock_irqsave(&ha->hardware_lock, flags); + tcm_qla2xxx_set_sess_by_s_id(lport, se_nacl, nacl, se_sess, + qla_tgt_sess, s_id); + tcm_qla2xxx_set_sess_by_loop_id(lport, se_nacl, nacl, se_sess, + qla_tgt_sess, loop_id); + spin_unlock_irqrestore(&ha->hardware_lock, flags); + /* + * Finally register the new FC Nexus with TCM + */ + __transport_register_session(se_nacl->se_tpg, se_nacl, se_sess, sess); + + return 0; +} + +static void tcm_qla2xxx_update_sess(struct qla_tgt_sess *sess, port_id_t s_id, + uint16_t loop_id, bool conf_compl_supported) +{ + struct qla_tgt *tgt = sess->tgt; + struct qla_hw_data *ha = tgt->ha; + scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + struct tcm_qla2xxx_lport *lport = vha->vha_tgt.target_lport_ptr; + struct se_node_acl *se_nacl = sess->se_sess->se_node_acl; + struct tcm_qla2xxx_nacl *nacl = container_of(se_nacl, + struct tcm_qla2xxx_nacl, se_node_acl); + u32 key; + + + if (sess->loop_id != loop_id || sess->s_id.b24 != s_id.b24) + pr_info("Updating session %p from port %8phC loop_id %d -> %d s_id %x:%x:%x -> %x:%x:%x\n", + sess, sess->port_name, + sess->loop_id, loop_id, sess->s_id.b.domain, + sess->s_id.b.area, sess->s_id.b.al_pa, s_id.b.domain, + s_id.b.area, s_id.b.al_pa); + + if (sess->loop_id != loop_id) { + /* + * Because we can shuffle loop IDs around and we + * update different sessions non-atomically, we might + * have overwritten this session's old loop ID + * already, and we might end up overwriting some other + * session that will be updated later. So we have to + * be extra careful and we can't warn about those things... + */ + if (lport->lport_loopid_map[sess->loop_id].se_nacl == se_nacl) + lport->lport_loopid_map[sess->loop_id].se_nacl = NULL; + + lport->lport_loopid_map[loop_id].se_nacl = se_nacl; + + sess->loop_id = loop_id; + } + + if (sess->s_id.b24 != s_id.b24) { + key = (((u32) sess->s_id.b.domain << 16) | + ((u32) sess->s_id.b.area << 8) | + ((u32) sess->s_id.b.al_pa)); + + if (btree_lookup32(&lport->lport_fcport_map, key)) + WARN(btree_remove32(&lport->lport_fcport_map, key) != se_nacl, + "Found wrong se_nacl when updating s_id %x:%x:%x\n", + sess->s_id.b.domain, sess->s_id.b.area, sess->s_id.b.al_pa); + else + WARN(1, "No lport_fcport_map entry for s_id %x:%x:%x\n", + sess->s_id.b.domain, sess->s_id.b.area, sess->s_id.b.al_pa); + + key = (((u32) s_id.b.domain << 16) | + ((u32) s_id.b.area << 8) | + ((u32) s_id.b.al_pa)); + + if (btree_lookup32(&lport->lport_fcport_map, key)) { + WARN(1, "Already have lport_fcport_map entry for s_id %x:%x:%x\n", + s_id.b.domain, s_id.b.area, s_id.b.al_pa); + btree_update32(&lport->lport_fcport_map, key, se_nacl); + } else { + btree_insert32(&lport->lport_fcport_map, key, se_nacl, GFP_ATOMIC); + } + + sess->s_id = s_id; + nacl->nport_id = key; + } + + sess->conf_compl_supported = conf_compl_supported; +} + +/* + * Calls into tcm_qla2xxx used by qla2xxx LLD I/O path. + */ +static struct qla_tgt_func_tmpl tcm_qla2xxx_template = { + .handle_cmd = tcm_qla2xxx_handle_cmd, + .handle_data = tcm_qla2xxx_handle_data, + .handle_dif_err = tcm_qla2xxx_handle_dif_err, + .handle_tmr = tcm_qla2xxx_handle_tmr, + .free_cmd = tcm_qla2xxx_free_cmd, + .free_mcmd = tcm_qla2xxx_free_mcmd, + .free_session = tcm_qla2xxx_free_session, + .update_sess = tcm_qla2xxx_update_sess, + .check_initiator_node_acl = tcm_qla2xxx_check_initiator_node_acl, + .find_sess_by_s_id = tcm_qla2xxx_find_sess_by_s_id, + .find_sess_by_loop_id = tcm_qla2xxx_find_sess_by_loop_id, + .clear_nacl_from_fcport_map = tcm_qla2xxx_clear_nacl_from_fcport_map, + .put_sess = tcm_qla2xxx_put_sess, + .shutdown_sess = tcm_qla2xxx_shutdown_sess, +}; + +static int tcm_qla2xxx_init_lport(struct tcm_qla2xxx_lport *lport) +{ + int rc; + + rc = btree_init32(&lport->lport_fcport_map); + if (rc) { + pr_err("Unable to initialize lport->lport_fcport_map btree\n"); + return rc; + } + + lport->lport_loopid_map = vmalloc(sizeof(struct tcm_qla2xxx_fc_loopid) * + 65536); + if (!lport->lport_loopid_map) { + pr_err("Unable to allocate lport->lport_loopid_map of %zu bytes\n", + sizeof(struct tcm_qla2xxx_fc_loopid) * 65536); + btree_destroy32(&lport->lport_fcport_map); + return -ENOMEM; + } + memset(lport->lport_loopid_map, 0, sizeof(struct tcm_qla2xxx_fc_loopid) + * 65536); + pr_debug("qla2xxx: Allocated lport_loopid_map of %zu bytes\n", + sizeof(struct tcm_qla2xxx_fc_loopid) * 65536); + return 0; +} + +static int tcm_qla2xxx_lport_register_cb(struct scsi_qla_host *vha, + void *target_lport_ptr, + u64 npiv_wwpn, u64 npiv_wwnn) +{ + struct qla_hw_data *ha = vha->hw; + struct tcm_qla2xxx_lport *lport = + (struct tcm_qla2xxx_lport *)target_lport_ptr; + /* + * Setup tgt_ops, local pointer to vha and target_lport_ptr + */ + ha->tgt.tgt_ops = &tcm_qla2xxx_template; + vha->vha_tgt.target_lport_ptr = target_lport_ptr; + lport->qla_vha = vha; + + return 0; +} + +static struct se_wwn *tcm_qla2xxx_make_lport( + struct target_fabric_configfs *tf, + struct config_group *group, + const char *name) +{ + struct tcm_qla2xxx_lport *lport; + u64 wwpn; + int ret = -ENODEV; + + if (tcm_qla2xxx_parse_wwn(name, &wwpn, 1) < 0) + return ERR_PTR(-EINVAL); + + lport = kzalloc(sizeof(struct tcm_qla2xxx_lport), GFP_KERNEL); + if (!lport) { + pr_err("Unable to allocate struct tcm_qla2xxx_lport\n"); + return ERR_PTR(-ENOMEM); + } + lport->lport_wwpn = wwpn; + tcm_qla2xxx_format_wwn(&lport->lport_name[0], TCM_QLA2XXX_NAMELEN, + wwpn); + sprintf(lport->lport_naa_name, "naa.%016llx", (unsigned long long) wwpn); + + ret = tcm_qla2xxx_init_lport(lport); + if (ret != 0) + goto out; + + ret = qlt_lport_register(lport, wwpn, 0, 0, + tcm_qla2xxx_lport_register_cb); + if (ret != 0) + goto out_lport; + + return &lport->lport_wwn; +out_lport: + vfree(lport->lport_loopid_map); + btree_destroy32(&lport->lport_fcport_map); +out: + kfree(lport); + return ERR_PTR(ret); +} + +static void tcm_qla2xxx_drop_lport(struct se_wwn *wwn) +{ + struct tcm_qla2xxx_lport *lport = container_of(wwn, + struct tcm_qla2xxx_lport, lport_wwn); + struct scsi_qla_host *vha = lport->qla_vha; + struct se_node_acl *node; + u32 key = 0; + + /* + * Call into qla2x_target.c LLD logic to complete the + * shutdown of struct qla_tgt after the call to + * qlt_stop_phase1() from tcm_qla2xxx_drop_tpg() above.. + */ + if (vha->vha_tgt.qla_tgt && !vha->vha_tgt.qla_tgt->tgt_stopped) + qlt_stop_phase2(vha->vha_tgt.qla_tgt); + + qlt_lport_deregister(vha); + + vfree(lport->lport_loopid_map); + btree_for_each_safe32(&lport->lport_fcport_map, key, node) + btree_remove32(&lport->lport_fcport_map, key); + btree_destroy32(&lport->lport_fcport_map); + kfree(lport); +} + +static int tcm_qla2xxx_lport_register_npiv_cb(struct scsi_qla_host *base_vha, + void *target_lport_ptr, + u64 npiv_wwpn, u64 npiv_wwnn) +{ + struct fc_vport *vport; + struct Scsi_Host *sh = base_vha->host; + struct scsi_qla_host *npiv_vha; + struct tcm_qla2xxx_lport *lport = + (struct tcm_qla2xxx_lport *)target_lport_ptr; + struct tcm_qla2xxx_lport *base_lport = + (struct tcm_qla2xxx_lport *)base_vha->vha_tgt.target_lport_ptr; + struct tcm_qla2xxx_tpg *base_tpg; + struct fc_vport_identifiers vport_id; + + if (!qla_tgt_mode_enabled(base_vha)) { + pr_err("qla2xxx base_vha not enabled for target mode\n"); + return -EPERM; + } + + if (!base_lport || !base_lport->tpg_1 || + !atomic_read(&base_lport->tpg_1->lport_tpg_enabled)) { + pr_err("qla2xxx base_lport or tpg_1 not available\n"); + return -EPERM; + } + base_tpg = base_lport->tpg_1; + + memset(&vport_id, 0, sizeof(vport_id)); + vport_id.port_name = npiv_wwpn; + vport_id.node_name = npiv_wwnn; + vport_id.roles = FC_PORT_ROLE_FCP_INITIATOR; + vport_id.vport_type = FC_PORTTYPE_NPIV; + vport_id.disable = false; + + vport = fc_vport_create(sh, 0, &vport_id); + if (!vport) { + pr_err("fc_vport_create failed for qla2xxx_npiv\n"); + return -ENODEV; + } + /* + * Setup local pointer to NPIV vhba + target_lport_ptr + */ + npiv_vha = (struct scsi_qla_host *)vport->dd_data; + npiv_vha->vha_tgt.target_lport_ptr = target_lport_ptr; + lport->qla_vha = npiv_vha; + scsi_host_get(npiv_vha->host); + return 0; +} + + +static struct se_wwn *tcm_qla2xxx_npiv_make_lport( + struct target_fabric_configfs *tf, + struct config_group *group, + const char *name) +{ + struct tcm_qla2xxx_lport *lport; + u64 phys_wwpn, npiv_wwpn, npiv_wwnn; + char *p, tmp[128]; + int ret; + + snprintf(tmp, 128, "%s", name); + + p = strchr(tmp, '@'); + if (!p) { + pr_err("Unable to locate NPIV '@' seperator\n"); + return ERR_PTR(-EINVAL); + } + *p++ = '\0'; + + if (tcm_qla2xxx_parse_wwn(tmp, &phys_wwpn, 1) < 0) + return ERR_PTR(-EINVAL); + + if (tcm_qla2xxx_npiv_parse_wwn(p, strlen(p)+1, + &npiv_wwpn, &npiv_wwnn) < 0) + return ERR_PTR(-EINVAL); + + lport = kzalloc(sizeof(struct tcm_qla2xxx_lport), GFP_KERNEL); + if (!lport) { + pr_err("Unable to allocate struct tcm_qla2xxx_lport for NPIV\n"); + return ERR_PTR(-ENOMEM); + } + lport->lport_npiv_wwpn = npiv_wwpn; + lport->lport_npiv_wwnn = npiv_wwnn; + sprintf(lport->lport_naa_name, "naa.%016llx", (unsigned long long) npiv_wwpn); + + ret = tcm_qla2xxx_init_lport(lport); + if (ret != 0) + goto out; + + ret = qlt_lport_register(lport, phys_wwpn, npiv_wwpn, npiv_wwnn, + tcm_qla2xxx_lport_register_npiv_cb); + if (ret != 0) + goto out_lport; + + return &lport->lport_wwn; +out_lport: + vfree(lport->lport_loopid_map); + btree_destroy32(&lport->lport_fcport_map); +out: + kfree(lport); + return ERR_PTR(ret); +} + +static void tcm_qla2xxx_npiv_drop_lport(struct se_wwn *wwn) +{ + struct tcm_qla2xxx_lport *lport = container_of(wwn, + struct tcm_qla2xxx_lport, lport_wwn); + struct scsi_qla_host *npiv_vha = lport->qla_vha; + struct qla_hw_data *ha = npiv_vha->hw; + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); + + scsi_host_put(npiv_vha->host); + /* + * Notify libfc that we want to release the vha->fc_vport + */ + fc_vport_terminate(npiv_vha->fc_vport); + scsi_host_put(base_vha->host); + kfree(lport); +} + + +static ssize_t tcm_qla2xxx_wwn_show_attr_version( + struct target_fabric_configfs *tf, + char *page) +{ + return sprintf(page, + "TCM QLOGIC QLA2XXX NPIV capable fabric module %s on %s/%s on " + UTS_RELEASE"\n", TCM_QLA2XXX_VERSION, utsname()->sysname, + utsname()->machine); +} + +TF_WWN_ATTR_RO(tcm_qla2xxx, version); + +static struct configfs_attribute *tcm_qla2xxx_wwn_attrs[] = { + &tcm_qla2xxx_wwn_version.attr, + NULL, +}; + +static struct target_core_fabric_ops tcm_qla2xxx_ops = { + .get_fabric_name = tcm_qla2xxx_get_fabric_name, + .get_fabric_proto_ident = tcm_qla2xxx_get_fabric_proto_ident, + .tpg_get_wwn = tcm_qla2xxx_get_fabric_wwn, + .tpg_get_tag = tcm_qla2xxx_get_tag, + .tpg_get_default_depth = tcm_qla2xxx_get_default_depth, + .tpg_get_pr_transport_id = tcm_qla2xxx_get_pr_transport_id, + .tpg_get_pr_transport_id_len = tcm_qla2xxx_get_pr_transport_id_len, + .tpg_parse_pr_out_transport_id = tcm_qla2xxx_parse_pr_out_transport_id, + .tpg_check_demo_mode = tcm_qla2xxx_check_demo_mode, + .tpg_check_demo_mode_cache = tcm_qla2xxx_check_demo_mode_cache, + .tpg_check_demo_mode_write_protect = + tcm_qla2xxx_check_demo_write_protect, + .tpg_check_prod_mode_write_protect = + tcm_qla2xxx_check_prod_write_protect, + .tpg_check_demo_mode_login_only = tcm_qla2xxx_check_demo_mode_login_only, + .tpg_alloc_fabric_acl = tcm_qla2xxx_alloc_fabric_acl, + .tpg_release_fabric_acl = tcm_qla2xxx_release_fabric_acl, + .tpg_get_inst_index = tcm_qla2xxx_tpg_get_inst_index, + .check_stop_free = tcm_qla2xxx_check_stop_free, + .release_cmd = tcm_qla2xxx_release_cmd, + .put_session = tcm_qla2xxx_put_session, + .shutdown_session = tcm_qla2xxx_shutdown_session, + .close_session = tcm_qla2xxx_close_session, + .sess_get_index = tcm_qla2xxx_sess_get_index, + .sess_get_initiator_sid = NULL, + .write_pending = tcm_qla2xxx_write_pending, + .write_pending_status = tcm_qla2xxx_write_pending_status, + .set_default_node_attributes = tcm_qla2xxx_set_default_node_attrs, + .get_task_tag = tcm_qla2xxx_get_task_tag, + .get_cmd_state = tcm_qla2xxx_get_cmd_state, + .queue_data_in = tcm_qla2xxx_queue_data_in, + .queue_status = tcm_qla2xxx_queue_status, + .queue_tm_rsp = tcm_qla2xxx_queue_tm_rsp, + .aborted_task = tcm_qla2xxx_aborted_task, + /* + * Setup function pointers for generic logic in + * target_core_fabric_configfs.c + */ + .fabric_make_wwn = tcm_qla2xxx_make_lport, + .fabric_drop_wwn = tcm_qla2xxx_drop_lport, + .fabric_make_tpg = tcm_qla2xxx_make_tpg, + .fabric_drop_tpg = tcm_qla2xxx_drop_tpg, + .fabric_post_link = NULL, + .fabric_pre_unlink = NULL, + .fabric_make_np = NULL, + .fabric_drop_np = NULL, + .fabric_make_nodeacl = tcm_qla2xxx_make_nodeacl, + .fabric_drop_nodeacl = tcm_qla2xxx_drop_nodeacl, +}; + +static struct target_core_fabric_ops tcm_qla2xxx_npiv_ops = { + .get_fabric_name = tcm_qla2xxx_npiv_get_fabric_name, + .get_fabric_proto_ident = tcm_qla2xxx_get_fabric_proto_ident, + .tpg_get_wwn = tcm_qla2xxx_get_fabric_wwn, + .tpg_get_tag = tcm_qla2xxx_get_tag, + .tpg_get_default_depth = tcm_qla2xxx_get_default_depth, + .tpg_get_pr_transport_id = tcm_qla2xxx_get_pr_transport_id, + .tpg_get_pr_transport_id_len = tcm_qla2xxx_get_pr_transport_id_len, + .tpg_parse_pr_out_transport_id = tcm_qla2xxx_parse_pr_out_transport_id, + .tpg_check_demo_mode = tcm_qla2xxx_check_demo_mode, + .tpg_check_demo_mode_cache = tcm_qla2xxx_check_demo_mode_cache, + .tpg_check_demo_mode_write_protect = tcm_qla2xxx_check_demo_mode, + .tpg_check_prod_mode_write_protect = + tcm_qla2xxx_check_prod_write_protect, + .tpg_check_demo_mode_login_only = tcm_qla2xxx_check_demo_mode_login_only, + .tpg_alloc_fabric_acl = tcm_qla2xxx_alloc_fabric_acl, + .tpg_release_fabric_acl = tcm_qla2xxx_release_fabric_acl, + .tpg_get_inst_index = tcm_qla2xxx_tpg_get_inst_index, + .check_stop_free = tcm_qla2xxx_check_stop_free, + .release_cmd = tcm_qla2xxx_release_cmd, + .put_session = tcm_qla2xxx_put_session, + .shutdown_session = tcm_qla2xxx_shutdown_session, + .close_session = tcm_qla2xxx_close_session, + .sess_get_index = tcm_qla2xxx_sess_get_index, + .sess_get_initiator_sid = NULL, + .write_pending = tcm_qla2xxx_write_pending, + .write_pending_status = tcm_qla2xxx_write_pending_status, + .set_default_node_attributes = tcm_qla2xxx_set_default_node_attrs, + .get_task_tag = tcm_qla2xxx_get_task_tag, + .get_cmd_state = tcm_qla2xxx_get_cmd_state, + .queue_data_in = tcm_qla2xxx_queue_data_in, + .queue_status = tcm_qla2xxx_queue_status, + .queue_tm_rsp = tcm_qla2xxx_queue_tm_rsp, + .aborted_task = tcm_qla2xxx_aborted_task, + /* + * Setup function pointers for generic logic in + * target_core_fabric_configfs.c + */ + .fabric_make_wwn = tcm_qla2xxx_npiv_make_lport, + .fabric_drop_wwn = tcm_qla2xxx_npiv_drop_lport, + .fabric_make_tpg = tcm_qla2xxx_npiv_make_tpg, + .fabric_drop_tpg = tcm_qla2xxx_drop_tpg, + .fabric_post_link = NULL, + .fabric_pre_unlink = NULL, + .fabric_make_np = NULL, + .fabric_drop_np = NULL, + .fabric_make_nodeacl = tcm_qla2xxx_make_nodeacl, + .fabric_drop_nodeacl = tcm_qla2xxx_drop_nodeacl, +}; + +static int tcm_qla2xxx_register_configfs(void) +{ + struct target_fabric_configfs *fabric, *npiv_fabric; + int ret; + + pr_debug("TCM QLOGIC QLA2XXX fabric module %s on %s/%s on " + UTS_RELEASE"\n", TCM_QLA2XXX_VERSION, utsname()->sysname, + utsname()->machine); + /* + * Register the top level struct config_item_type with TCM core + */ + fabric = target_fabric_configfs_init(THIS_MODULE, "qla2xxx"); + if (IS_ERR(fabric)) { + pr_err("target_fabric_configfs_init() failed\n"); + return PTR_ERR(fabric); + } + /* + * Setup fabric->tf_ops from our local tcm_qla2xxx_ops + */ + fabric->tf_ops = tcm_qla2xxx_ops; + /* + * Setup default attribute lists for various fabric->tf_cit_tmpl + */ + fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = tcm_qla2xxx_wwn_attrs; + fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = tcm_qla2xxx_tpg_attrs; + fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = + tcm_qla2xxx_tpg_attrib_attrs; + fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL; + fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL; + fabric->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs = NULL; + fabric->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = NULL; + fabric->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = NULL; + fabric->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = NULL; + /* + * Register the fabric for use within TCM + */ + ret = target_fabric_configfs_register(fabric); + if (ret < 0) { + pr_err("target_fabric_configfs_register() failed for TCM_QLA2XXX\n"); + return ret; + } + /* + * Setup our local pointer to *fabric + */ + tcm_qla2xxx_fabric_configfs = fabric; + pr_debug("TCM_QLA2XXX[0] - Set fabric -> tcm_qla2xxx_fabric_configfs\n"); + + /* + * Register the top level struct config_item_type for NPIV with TCM core + */ + npiv_fabric = target_fabric_configfs_init(THIS_MODULE, "qla2xxx_npiv"); + if (IS_ERR(npiv_fabric)) { + pr_err("target_fabric_configfs_init() failed\n"); + ret = PTR_ERR(npiv_fabric); + goto out_fabric; + } + /* + * Setup fabric->tf_ops from our local tcm_qla2xxx_npiv_ops + */ + npiv_fabric->tf_ops = tcm_qla2xxx_npiv_ops; + /* + * Setup default attribute lists for various npiv_fabric->tf_cit_tmpl + */ + npiv_fabric->tf_cit_tmpl.tfc_wwn_cit.ct_attrs = tcm_qla2xxx_wwn_attrs; + npiv_fabric->tf_cit_tmpl.tfc_tpg_base_cit.ct_attrs = + tcm_qla2xxx_npiv_tpg_attrs; + npiv_fabric->tf_cit_tmpl.tfc_tpg_attrib_cit.ct_attrs = NULL; + npiv_fabric->tf_cit_tmpl.tfc_tpg_param_cit.ct_attrs = NULL; + npiv_fabric->tf_cit_tmpl.tfc_tpg_np_base_cit.ct_attrs = NULL; + npiv_fabric->tf_cit_tmpl.tfc_tpg_nacl_base_cit.ct_attrs = NULL; + npiv_fabric->tf_cit_tmpl.tfc_tpg_nacl_attrib_cit.ct_attrs = NULL; + npiv_fabric->tf_cit_tmpl.tfc_tpg_nacl_auth_cit.ct_attrs = NULL; + npiv_fabric->tf_cit_tmpl.tfc_tpg_nacl_param_cit.ct_attrs = NULL; + /* + * Register the npiv_fabric for use within TCM + */ + ret = target_fabric_configfs_register(npiv_fabric); + if (ret < 0) { + pr_err("target_fabric_configfs_register() failed for TCM_QLA2XXX\n"); + goto out_fabric; + } + /* + * Setup our local pointer to *npiv_fabric + */ + tcm_qla2xxx_npiv_fabric_configfs = npiv_fabric; + pr_debug("TCM_QLA2XXX[0] - Set fabric -> tcm_qla2xxx_npiv_fabric_configfs\n"); + + tcm_qla2xxx_free_wq = alloc_workqueue("tcm_qla2xxx_free", + WQ_MEM_RECLAIM, 0); + if (!tcm_qla2xxx_free_wq) { + ret = -ENOMEM; + goto out_fabric_npiv; + } + + tcm_qla2xxx_cmd_wq = alloc_workqueue("tcm_qla2xxx_cmd", 0, 0); + if (!tcm_qla2xxx_cmd_wq) { + ret = -ENOMEM; + goto out_free_wq; + } + + return 0; + +out_free_wq: + destroy_workqueue(tcm_qla2xxx_free_wq); +out_fabric_npiv: + target_fabric_configfs_deregister(tcm_qla2xxx_npiv_fabric_configfs); +out_fabric: + target_fabric_configfs_deregister(tcm_qla2xxx_fabric_configfs); + return ret; +} + +static void tcm_qla2xxx_deregister_configfs(void) +{ + destroy_workqueue(tcm_qla2xxx_cmd_wq); + destroy_workqueue(tcm_qla2xxx_free_wq); + + target_fabric_configfs_deregister(tcm_qla2xxx_fabric_configfs); + tcm_qla2xxx_fabric_configfs = NULL; + pr_debug("TCM_QLA2XXX[0] - Cleared tcm_qla2xxx_fabric_configfs\n"); + + target_fabric_configfs_deregister(tcm_qla2xxx_npiv_fabric_configfs); + tcm_qla2xxx_npiv_fabric_configfs = NULL; + pr_debug("TCM_QLA2XXX[0] - Cleared tcm_qla2xxx_npiv_fabric_configfs\n"); +} + +static int __init tcm_qla2xxx_init(void) +{ + int ret; + + ret = tcm_qla2xxx_register_configfs(); + if (ret < 0) + return ret; + + return 0; +} + +static void __exit tcm_qla2xxx_exit(void) +{ + tcm_qla2xxx_deregister_configfs(); +} + +MODULE_DESCRIPTION("TCM QLA2XXX series NPIV enabled fabric driver"); +MODULE_LICENSE("GPL"); +module_init(tcm_qla2xxx_init); +module_exit(tcm_qla2xxx_exit); diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.h b/drivers/scsi/qla2xxx/tcm_qla2xxx.h new file mode 100644 index 00000000000..10c00214564 --- /dev/null +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.h @@ -0,0 +1,83 @@ +#include <target/target_core_base.h> +#include <linux/btree.h> + +#define TCM_QLA2XXX_VERSION "v0.1" +/* length of ASCII WWPNs including pad */ +#define TCM_QLA2XXX_NAMELEN 32 +/* + * Number of pre-allocated per-session tags, based upon the worst-case + * per port number of iocbs + */ +#define TCM_QLA2XXX_DEFAULT_TAGS 2088 + +#include "qla_target.h" + +struct tcm_qla2xxx_nacl { + /* From libfc struct fc_rport->port_id */ + u32 nport_id; + /* Binary World Wide unique Node Name for remote FC Initiator Nport */ + u64 nport_wwnn; + /* ASCII formatted WWPN for FC Initiator Nport */ + char nport_name[TCM_QLA2XXX_NAMELEN]; + /* Pointer to qla_tgt_sess */ + struct qla_tgt_sess *qla_tgt_sess; + /* Pointer to TCM FC nexus */ + struct se_session *nport_nexus; + /* Returned by tcm_qla2xxx_make_nodeacl() */ + struct se_node_acl se_node_acl; +}; + +struct tcm_qla2xxx_tpg_attrib { + int generate_node_acls; + int cache_dynamic_acls; + int demo_mode_write_protect; + int prod_mode_write_protect; + int demo_mode_login_only; +}; + +struct tcm_qla2xxx_tpg { + /* FC lport target portal group tag for TCM */ + u16 lport_tpgt; + /* Atomic bit to determine TPG active status */ + atomic_t lport_tpg_enabled; + /* Pointer back to tcm_qla2xxx_lport */ + struct tcm_qla2xxx_lport *lport; + /* Used by tcm_qla2xxx_tpg_attrib_cit */ + struct tcm_qla2xxx_tpg_attrib tpg_attrib; + /* Returned by tcm_qla2xxx_make_tpg() */ + struct se_portal_group se_tpg; + /* Items for dealing with configfs_depend_item */ + struct completion tpg_base_comp; + struct work_struct tpg_base_work; +}; + +struct tcm_qla2xxx_fc_loopid { + struct se_node_acl *se_nacl; +}; + +struct tcm_qla2xxx_lport { + /* SCSI protocol the lport is providing */ + u8 lport_proto_id; + /* Binary World Wide unique Port Name for FC Target Lport */ + u64 lport_wwpn; + /* Binary World Wide unique Port Name for FC NPIV Target Lport */ + u64 lport_npiv_wwpn; + /* Binary World Wide unique Node Name for FC NPIV Target Lport */ + u64 lport_npiv_wwnn; + /* ASCII formatted WWPN for FC Target Lport */ + char lport_name[TCM_QLA2XXX_NAMELEN]; + /* ASCII formatted naa WWPN for VPD page 83 etc */ + char lport_naa_name[TCM_QLA2XXX_NAMELEN]; + /* map for fc_port pointers in 24-bit FC Port ID space */ + struct btree_head32 lport_fcport_map; + /* vmalloc-ed memory for fc_port pointers for 16-bit FC loop ID */ + struct tcm_qla2xxx_fc_loopid *lport_loopid_map; + /* Pointer to struct scsi_qla_host from qla2xxx LLD */ + struct scsi_qla_host *qla_vha; + /* Pointer to struct qla_tgt pointer */ + struct qla_tgt lport_qla_tgt; + /* Pointer to TPG=1 for non NPIV mode */ + struct tcm_qla2xxx_tpg *tpg_1; + /* Returned by tcm_qla2xxx_make_lport() */ + struct se_wwn lport_wwn; +}; |
