diff options
Diffstat (limited to 'drivers/scsi/qla2xxx')
33 files changed, 10546 insertions, 1968 deletions
diff --git a/drivers/scsi/qla2xxx/Makefile b/drivers/scsi/qla2xxx/Makefile index c37b244cf8a..44def6bb4bb 100644 --- a/drivers/scsi/qla2xxx/Makefile +++ b/drivers/scsi/qla2xxx/Makefile @@ -1,6 +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_mr.o qla_target.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 d7a99ae7f39..16fe5196e6d 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2013 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -29,7 +29,7 @@ qla2x00_sysfs_read_fw_dump(struct file *filp, struct kobject *kobj, if (!(ha->fw_dump_reading || ha->mctp_dump_reading)) return 0; - if (IS_QLA82XX(ha)) { + 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); @@ -71,7 +71,7 @@ qla2x00_sysfs_write_fw_dump(struct file *filp, struct kobject *kobj, ql_log(ql_log_info, vha, 0x705d, "Firmware dump cleared on (%ld).\n", vha->host_no); - if (IS_QLA82XX(vha->hw)) { + if (IS_P3P_TYPE(ha)) { qla82xx_md_free(vha); qla82xx_md_prep(vha); } @@ -95,11 +95,15 @@ qla2x00_sysfs_write_fw_dump(struct file *filp, struct kobject *kobj, 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_QLA82XX(ha)) { + if (IS_P3P_TYPE(ha)) { if (ha->md_tmplt_hdr) ql_dbg(ql_dbg_user, vha, 0x705b, "MiniDump supported with this firmware.\n"); @@ -109,7 +113,7 @@ qla2x00_sysfs_write_fw_dump(struct file *filp, struct kobject *kobj, } break; case 5: - if (IS_QLA82XX(ha)) + if (IS_P3P_TYPE(ha)) set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); break; case 6: @@ -143,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) @@ -237,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 @@ -261,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; } @@ -284,10 +381,10 @@ 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 -EINVAL; @@ -300,12 +397,14 @@ qla2x00_sysfs_write_optrom_ctl(struct file *filp, struct kobject *kobj, 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) - return -EINVAL; - + ha->optrom_state != QLA_SWRITING) { + rval = -EINVAL; + goto out; + } ha->optrom_state = QLA_SWAITING; ql_dbg(ql_dbg_user, vha, 0x7061, @@ -316,8 +415,10 @@ qla2x00_sysfs_write_optrom_ctl(struct file *filp, struct kobject *kobj, ha->optrom_buffer = NULL; break; case 1: - if (ha->optrom_state != QLA_SWAITING) - return -EINVAL; + if (ha->optrom_state != QLA_SWAITING) { + rval = -EINVAL; + goto out; + } ha->optrom_region_start = start; ha->optrom_region_size = start + size > ha->optrom_size ? @@ -331,13 +432,15 @@ qla2x00_sysfs_write_optrom_ctl(struct file *filp, struct kobject *kobj, "(%x).\n", ha->optrom_region_size); ha->optrom_state = QLA_SWAITING; - return -ENOMEM; + rval = -ENOMEM; + goto out; } if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) { ql_log(ql_log_warn, vha, 0x7063, "HBA not online, failing NVRAM update.\n"); - return -EAGAIN; + rval = -EAGAIN; + goto out; } ql_dbg(ql_dbg_user, vha, 0x7064, @@ -349,8 +452,10 @@ qla2x00_sysfs_write_optrom_ctl(struct file *filp, struct kobject *kobj, ha->optrom_region_start, ha->optrom_region_size); break; case 2: - if (ha->optrom_state != QLA_SWAITING) - return -EINVAL; + if (ha->optrom_state != QLA_SWAITING) { + rval = -EINVAL; + goto out; + } /* * We need to be more restrictive on which FLASH regions are @@ -384,7 +489,8 @@ qla2x00_sysfs_write_optrom_ctl(struct file *filp, struct kobject *kobj, if (!valid) { 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; @@ -399,7 +505,8 @@ qla2x00_sysfs_write_optrom_ctl(struct file *filp, struct kobject *kobj, "(%x)\n", ha->optrom_region_size); ha->optrom_state = QLA_SWAITING; - return -ENOMEM; + rval = -ENOMEM; + goto out; } ql_dbg(ql_dbg_user, vha, 0x7067, @@ -409,13 +516,16 @@ qla2x00_sysfs_write_optrom_ctl(struct file *filp, struct kobject *kobj, memset(ha->optrom_buffer, 0, ha->optrom_region_size); break; case 3: - if (ha->optrom_state != QLA_SWRITING) - return -EINVAL; + if (ha->optrom_state != QLA_SWRITING) { + rval = -EINVAL; + goto out; + } if (qla2x00_wait_for_hba_online(vha) != QLA_SUCCESS) { ql_log(ql_log_warn, vha, 0x7068, "HBA not online, failing flash update.\n"); - return -EAGAIN; + rval = -EAGAIN; + goto out; } ql_dbg(ql_dbg_user, vha, 0x7069, @@ -426,9 +536,12 @@ qla2x00_sysfs_write_optrom_ctl(struct file *filp, struct kobject *kobj, ha->optrom_region_start, ha->optrom_region_size); break; default: - return -EINVAL; + rval = -EINVAL; } - return count; + +out: + mutex_unlock(&ha->optrom_mutex); + return rval; } static struct bin_attribute sysfs_optrom_ctl_attr = { @@ -551,7 +664,7 @@ 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) { ql_log(ql_log_warn, vha, 0x706d, "Unable to read SFP data (%x/%x/%x).\n", rval, @@ -586,7 +699,7 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj, 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 -EINVAL; @@ -597,14 +710,23 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj, "Issuing ISP reset.\n"); scsi_block_requests(vha->host); - set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); 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); } - qla2xxx_wake_dpc(vha); qla2x00_wait_for_chip_reset(vha); scsi_unblock_requests(vha->host); break; @@ -640,7 +762,7 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj, break; } case 0x2025e: - if (!IS_QLA82XX(ha) || vha != base_vha) { + if (!IS_P3P_TYPE(ha) || vha != base_vha) { ql_log(ql_log_info, vha, 0x7071, "FCoE ctx reset no supported.\n"); return -EPERM; @@ -674,7 +796,19 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj, __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; } @@ -797,6 +931,7 @@ 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, }, @@ -822,6 +957,8 @@ qla2x00_alloc_sysfs_attr(scsi_qla_host_t *vha) continue; 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); @@ -837,7 +974,7 @@ qla2x00_alloc_sysfs_attr(scsi_qla_host_t *vha) } 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; @@ -855,7 +992,7 @@ qla2x00_free_sysfs_attr(scsi_qla_host_t *vha) iter->attr); } - if (ha->beacon_blink_led == 1) + if (stop_beacon && ha->beacon_blink_led == 1) ha->isp_ops->beacon_off(vha); } @@ -865,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 @@ -876,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)); } @@ -889,15 +1026,15 @@ qla2x00_serial_num_show(struct device *dev, struct device_attribute *attr, uint32_t sn; if (IS_QLAFX00(vha->hw)) { - return snprintf(buf, PAGE_SIZE, "%s\n", + 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); - return snprintf(buf, PAGE_SIZE, "%s\n", buf); + 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); } @@ -906,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 @@ -917,10 +1054,10 @@ qla2x00_isp_id_show(struct device *dev, struct device_attribute *attr, struct qla_hw_data *ha = vha->hw; if (IS_QLAFX00(vha->hw)) - return snprintf(buf, PAGE_SIZE, "%s\n", + return scnprintf(buf, PAGE_SIZE, "%s\n", vha->hw->mr.hw_version); - return snprintf(buf, PAGE_SIZE, "%04x %04x %04x %04x\n", + 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]); } @@ -931,11 +1068,7 @@ qla2x00_model_name_show(struct device *dev, struct device_attribute *attr, { scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); - if (IS_QLAFX00(vha->hw)) - return snprintf(buf, PAGE_SIZE, "%s\n", - vha->hw->mr.product_name); - - 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 @@ -943,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 : ""); } @@ -954,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)); } @@ -969,29 +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 || qla2x00_reset_active(vha)) - len = snprintf(buf, PAGE_SIZE, "Unknown Link State\n"); + 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; } } @@ -1007,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; @@ -1050,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 @@ -1080,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; } @@ -1124,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]); } @@ -1134,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]); } @@ -1144,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]); } @@ -1154,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]); } @@ -1166,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) && !IS_QLA83XX(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]); } @@ -1179,7 +1312,7 @@ 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)); - return snprintf(buf, PAGE_SIZE, "%d\n", + return scnprintf(buf, PAGE_SIZE, "%d\n", vha->qla_stats.total_isp_aborts); } @@ -1193,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 @@ -1212,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) && !IS_QLA8031(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); } @@ -1228,9 +1361,9 @@ qla2x00_phy_version_show(struct device *dev, struct device_attribute *attr, struct qla_hw_data *ha = vha->hw; if (!IS_QLA81XX(ha) && !IS_QLA8031(ha)) - return snprintf(buf, PAGE_SIZE, "\n"); + 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]); } @@ -1241,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 @@ -1251,9 +1384,9 @@ qla2x00_vlan_id_show(struct device *dev, struct device_attribute *attr, scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); if (!IS_CNA_CAPABLE(vha->hw)) - return snprintf(buf, PAGE_SIZE, "\n"); + 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 @@ -1263,12 +1396,9 @@ qla2x00_vn_port_mac_address_show(struct device *dev, scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); if (!IS_CNA_CAPABLE(vha->hw)) - return snprintf(buf, PAGE_SIZE, "\n"); + 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 @@ -1277,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 @@ -1287,12 +1417,6 @@ qla2x00_thermal_temp_show(struct device *dev, scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); uint16_t temp = 0; - if (!vha->hw->thermal_support) { - ql_log(ql_log_warn, vha, 0x70db, - "Thermal not supported by this card.\n"); - goto done; - } - if (qla2x00_reset_active(vha)) { ql_log(ql_log_warn, vha, 0x70dc, "ISP reset active.\n"); goto done; @@ -1304,10 +1428,10 @@ qla2x00_thermal_temp_show(struct device *dev, } if (qla2x00_get_thermal_temp(vha, &temp) == QLA_SUCCESS) - return snprintf(buf, PAGE_SIZE, "%d\n", temp); + return scnprintf(buf, PAGE_SIZE, "%d\n", temp); done: - return snprintf(buf, PAGE_SIZE, "\n"); + return scnprintf(buf, PAGE_SIZE, "\n"); } static ssize_t @@ -1321,7 +1445,7 @@ qla2x00_fw_state_show(struct device *dev, struct device_attribute *attr, if (IS_QLAFX00(vha->hw)) { pstate = qlafx00_fw_state_show(dev, attr, buf); - return snprintf(buf, PAGE_SIZE, "0x%x\n", pstate); + return scnprintf(buf, PAGE_SIZE, "0x%x\n", pstate); } if (qla2x00_reset_active(vha)) @@ -1332,7 +1456,7 @@ qla2x00_fw_state_show(struct device *dev, struct device_attribute *attr, 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]); } @@ -1343,9 +1467,9 @@ qla2x00_diag_requests_show(struct device *dev, scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); if (!IS_BIDI_CAPABLE(vha->hw)) - return snprintf(buf, PAGE_SIZE, "\n"); + return scnprintf(buf, PAGE_SIZE, "\n"); - return snprintf(buf, PAGE_SIZE, "%llu\n", vha->bidi_stats.io_count); + return scnprintf(buf, PAGE_SIZE, "%llu\n", vha->bidi_stats.io_count); } static ssize_t @@ -1355,9 +1479,9 @@ qla2x00_diag_megabytes_show(struct device *dev, scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); if (!IS_BIDI_CAPABLE(vha->hw)) - return snprintf(buf, PAGE_SIZE, "\n"); + return scnprintf(buf, PAGE_SIZE, "\n"); - return snprintf(buf, PAGE_SIZE, "%llu\n", + return scnprintf(buf, PAGE_SIZE, "%llu\n", vha->bidi_stats.transfer_bytes >> 20); } @@ -1371,12 +1495,43 @@ qla2x00_fw_dump_size_show(struct device *dev, struct device_attribute *attr, if (!ha->fw_dumped) size = 0; - else if (IS_QLA82XX(ha)) + else if (IS_P3P_TYPE(ha)) size = ha->md_template_size + ha->md_dump_size; else size = ha->fw_dump_len; - return snprintf(buf, PAGE_SIZE, "%d\n", size); + 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); @@ -1420,6 +1575,9 @@ 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, @@ -1452,6 +1610,7 @@ struct device_attribute *qla2x00_host_attrs[] = { &dev_attr_diag_requests, &dev_attr_diag_megabytes, &dev_attr_fw_dump_size, + &dev_attr_allow_cna_fw_dump, NULL, }; @@ -1497,6 +1656,9 @@ qla2x00_get_host_speed(struct Scsi_Host *shost) case PORT_SPEED_16GB: speed = FC_PORTSPEED_16GBIT; break; + case PORT_SPEED_32GB: + speed = FC_PORTSPEED_32GBIT; + break; } fc_host_speed(shost) = speed; } @@ -1725,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_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); @@ -1738,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); @@ -1958,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); @@ -2043,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, @@ -2089,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, }; @@ -2123,6 +2310,9 @@ qla2x00_init_host_attr(scsi_qla_host_t *vha) 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 417eaad50ae..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-2012 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -125,7 +125,7 @@ qla24xx_proc_fcp_prio_cfg_cmd(struct fc_bsg_job *bsg_job) uint32_t len; uint32_t oper; - if (!(IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) || IS_QLA82XX(ha))) { + if (!(IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) || IS_P3P_TYPE(ha))) { ret = -EINVAL; goto exit_fcp_prio_cfg; } @@ -559,7 +559,7 @@ qla81xx_reset_loopback_mode(scsi_qla_host_t *vha, uint16_t *config, uint16_t new_config[4]; struct qla_hw_data *ha = vha->hw; - if (!IS_QLA81XX(ha) && !IS_QLA8031(ha)) + if (!IS_QLA81XX(ha) && !IS_QLA8031(ha) && !IS_QLA8044(ha)) goto done_reset_internal; memset(new_config, 0 , sizeof(new_config)); @@ -627,9 +627,10 @@ qla81xx_set_loopback_mode(scsi_qla_host_t *vha, uint16_t *config, { 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)) + if (!IS_QLA81XX(ha) && !IS_QLA8031(ha) && !IS_QLA8044(ha)) goto done_set_internal; if (mode == INTERNAL_LOOPBACK) @@ -652,8 +653,19 @@ qla81xx_set_loopback_mode(scsi_qla_host_t *vha, uint16_t *config, } /* Wait for DCBX complete event */ - if (!wait_for_completion_timeout(&ha->dcbx_comp, - (DCBX_COMP_TIMEOUT * HZ))) { + 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); @@ -678,6 +690,7 @@ qla81xx_set_loopback_mode(scsi_qla_host_t *vha, uint16_t *config, } ha->notify_dcbx_comp = 0; + ha->idc_extend_tmo = 0; done_set_internal: return rval; @@ -773,7 +786,7 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job) if (atomic_read(&vha->loop_state) == LOOP_READY && (ha->current_topology == ISP_CFG_F || - ((IS_QLA81XX(ha) || IS_QLA8031(ha)) && + ((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) { @@ -783,7 +796,7 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job) command_sent = INT_DEF_LB_ECHO_CMD; rval = qla2x00_echo_test(vha, &elreq, response); } else { - if (IS_QLA81XX(ha) || IS_QLA8031(ha)) { + if (IS_QLA81XX(ha) || IS_QLA8031(ha) || IS_QLA8044(ha)) { memset(config, 0, sizeof(config)); memset(new_config, 0, sizeof(new_config)); @@ -806,7 +819,7 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job) "elreq.options=%04x\n", elreq.options); if (elreq.options == EXTERNAL_LOOPBACK) - if (IS_QLA8031(ha)) + if (IS_QLA8031(ha) || IS_QLA8044(ha)) rval = qla81xx_set_loopback_mode(vha, config, new_config, elreq.options); else @@ -1266,6 +1279,7 @@ qla24xx_iidma(struct fc_bsg_job *bsg_job) 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; @@ -1288,10 +1302,12 @@ 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) { + if (!found) { ql_log(ql_log_warn, vha, 0x7049, "Failed to find port.\n"); return -EINVAL; @@ -1318,12 +1334,9 @@ qla24xx_iidma(struct fc_bsg_job *bsg_job) if (rval) { ql_log(ql_log_warn, vha, 0x704c, - "iIDMA cmd failed for %02x%02x%02x%02x%02x%02x%02x%02x -- " - "%04x %x %04x %04x.\n", fcport->port_name[0], - fcport->port_name[1], fcport->port_name[2], - fcport->port_name[3], fcport->port_name[4], - fcport->port_name[5], fcport->port_name[6], - fcport->port_name[7], rval, fcport->fp_speed, mb[0], mb[1]); + "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) { @@ -1424,9 +1437,12 @@ qla2x00_read_optrom(struct fc_bsg_job *bsg_job) 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) + 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); @@ -1440,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; } @@ -1452,9 +1469,12 @@ qla2x00_update_optrom(struct fc_bsg_job *bsg_job) struct qla_hw_data *ha = vha->hw; int rval = 0; + mutex_lock(&ha->optrom_mutex); rval = qla2x00_optrom_setup(bsg_job, vha, 1); - if (rval) + 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; @@ -1470,6 +1490,7 @@ 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; } @@ -1882,7 +1903,7 @@ done: bsg_job->reply->reply_payload_rcv_len = 0; bsg_job->reply->result = (DID_OK) << 16; bsg_job->job_done(bsg_job); - /* Always retrun success, vendor rsp carries correct status */ + /* Always return success, vendor rsp carries correct status */ return 0; } @@ -2009,6 +2030,86 @@ done: } 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]) { @@ -2056,6 +2157,13 @@ qla2x00_process_vendor_specific(struct fc_bsg_job *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: return -ENOSYS; } diff --git a/drivers/scsi/qla2xxx/qla_bsg.h b/drivers/scsi/qla2xxx/qla_bsg.h index 04f770332c2..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-2013 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -23,6 +23,8 @@ #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 @@ -212,4 +214,22 @@ struct qla_i2c_access { 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 df132fec6d8..c72ee97bf3f 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.c +++ b/drivers/scsi/qla2xxx/qla_dbg.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2013 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -11,42 +11,67 @@ * ---------------------------------------------------------------------- * | Level | Last Value Used | Holes | * ---------------------------------------------------------------------- - * | Module Init and Probe | 0x014f | 0x4b,0xba,0xfa | - * | Mailbox commands | 0x117a | 0x111a-0x111b | + * | 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 | 0x3058 | 0x3006-0x300b | + * | Queue Command and IO tracing | 0x3059 | 0x3006-0x300b | * | | | 0x3027-0x3028 | * | | | 0x303d-0x3041 | * | | | 0x302d,0x3033 | * | | | 0x3036,0x3038 | * | | | 0x303a | - * | DPC Thread | 0x4022 | 0x4002,0x4013 | - * | Async Events | 0x5081 | 0x502b-0x502f | + * | DPC Thread | 0x4023 | 0x4002,0x4013 | + * | Async Events | 0x5087 | 0x502b-0x502f | * | | | 0x5047,0x5052 | - * | | | 0x5040,0x5075 | - * | Timer Routines | 0x6011 | | - * | User Space Interactions | 0x70dd | 0x7018,0x702e, | - * | | | 0x7020,0x7024, | - * | | | 0x7039,0x7045, | - * | | | 0x7073-0x7075, | - * | | | 0x707b,0x708c, | - * | | | 0x70a5,0x70a6, | - * | | | 0x70a8,0x70ab, | - * | | | 0x70ad-0x70ae, | - * | | | 0x70d1-0x70da, | - * | | | 0x7047,0x703b | - * | Task Management | 0x803c | 0x8025-0x8026 | - * | | | 0x800b,0x8039 | + * | | | 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 | 0xb086 | 0xb002,0xb024 | + * | 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 | 0xd010 | | - * | Target Mode | 0xe070 | | - * | Target Mode Management | 0xf072 | | + * | 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 | | * ---------------------------------------------------------------------- */ @@ -88,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) { @@ -123,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); @@ -148,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; } @@ -173,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 * @@ -192,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) @@ -227,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++) { @@ -249,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; } @@ -519,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 || IS_QLA83XX(ha)) + if (!ha->mqenable || IS_QLA83XX(ha) || IS_QLA27XX(ha)) return ptr; mq = ptr; @@ -533,13 +643,16 @@ 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 __iomem *) - (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); @@ -552,12 +665,13 @@ qla2xxx_dump_post_process(scsi_qla_host_t *vha, int rval) if (rval != QLA_SUCCESS) { ql_log(ql_log_warn, vha, 0xd000, - "Failed to dump firmware (%x).\n", rval); + "Failed to dump firmware (%x), dump status flags (0x%lx).\n", + rval, ha->fw_dump_cap_flags); ha->fw_dumped = 0; } else { ql_log(ql_log_info, vha, 0xd001, - "Firmware dump saved to temp buffer (%ld/%p).\n", - vha->host_no, ha->fw_dump); + "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); } @@ -941,11 +1055,12 @@ qla24xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) 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); @@ -968,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; @@ -1195,6 +1311,7 @@ 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); @@ -1218,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; @@ -1512,6 +1630,7 @@ 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); @@ -1534,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; @@ -1831,6 +1951,7 @@ qla83xx_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); @@ -1852,10 +1973,11 @@ qla83xx_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 qla83xx_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); WRT_REG_DWORD(®->iobase_addr, 0x6000); dmp_reg = ®->iobase_window; @@ -2278,9 +2400,11 @@ qla83xx_fw_dump(scsi_qla_host_t *vha, int hardware_locked) nxt += sizeof(fw->code_ram); nxt += (ha->fw_memory_size - 0x100000 + 1); goto copy_queue; - } else + } 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), @@ -2530,7 +2654,7 @@ ql_dump_regs(uint32_t level, scsi_qla_host_t *vha, int32_t id) if (!ql_mask_match(level)) return; - if (IS_QLA82XX(ha)) + if (IS_P3P_TYPE(ha)) mbx_reg = ®82->mailbox_in[0]; else if (IS_FWI2_CAPABLE(ha)) mbx_reg = ®24->mailbox0; diff --git a/drivers/scsi/qla2xxx/qla_dbg.h b/drivers/scsi/qla2xxx/qla_dbg.h index 35e20b4f8b6..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-2013 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -348,3 +348,11 @@ ql_log_pci(uint32_t, struct pci_dev *pdev, int32_t, const char *fmt, ...); #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 95ca32a71e7..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-2013 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -35,6 +35,7 @@ #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" @@ -642,6 +643,7 @@ struct device_reg_fx00 { uint32_t initval6; /* C8 */ uint32_t initval7; /* CC */ uint32_t fwheartbeat; /* D0 */ + uint32_t pseudoaen; /* D4 */ }; @@ -652,7 +654,7 @@ typedef union { struct device_reg_25xxmq isp25mq; struct device_reg_82xx isp82; struct device_reg_fx00 ispfx00; -} device_reg_t; +} __iomem device_reg_t; #define ISP_REQ_Q_IN(ha, reg) \ (IS_QLA2100(ha) || IS_QLA2200(ha) ? \ @@ -805,7 +807,8 @@ struct mbx_cmd_32 { #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 */ @@ -859,7 +862,6 @@ struct mbx_cmd_32 { */ #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. */ @@ -934,6 +936,9 @@ struct mbx_cmd_32 { /* * 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. */ @@ -960,6 +965,13 @@ struct mbx_cmd_32 { */ #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 @@ -997,6 +1009,7 @@ struct mbx_cmd_32 { #define MBX_1 BIT_1 #define MBX_0 BIT_0 +#define RNID_TYPE_SET_VERSION 0x9 #define RNID_TYPE_ASIC_TEMP 0xC /* @@ -1192,30 +1205,6 @@ typedef struct { uint8_t reserved_3[26]; } init_cb_t; - -struct init_cb_fx { - uint16_t version; - uint16_t reserved_1[13]; - __le16 request_q_outpointer; - __le16 response_q_inpointer; - uint16_t reserved_2[2]; - __le16 response_q_length; - __le16 request_q_length; - uint16_t reserved_3[2]; - __le32 request_q_address[2]; - __le32 response_q_address[2]; - uint16_t reserved_4[4]; - uint8_t response_q_msivec; - uint8_t reserved_5[19]; - uint16_t interrupt_delay_timer; - uint16_t reserved_6; - uint32_t fwoptions1; - uint32_t fwoptions2; - uint32_t fwoptions3; - uint8_t reserved_7[24]; -}; - - /* * Get Link Status mailbox command return buffer. */ @@ -1233,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; }; @@ -1639,25 +1629,35 @@ typedef struct { #define PO_MODE_DIF_PASS 2 #define PO_MODE_DIF_REPLACE 3 #define PO_MODE_DIF_TCP_CKSUM 6 -#define PO_ENABLE_DIF_BUNDLING BIT_8 #define PO_ENABLE_INCR_GUARD_SEED BIT_3 -#define PO_DISABLE_INCR_REF_TAG BIT_5 #define PO_DISABLE_GUARD_CHECK BIT_4 +#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 { @@ -1671,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; @@ -1765,6 +1765,8 @@ 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 */ @@ -2166,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 { @@ -2656,6 +2659,11 @@ 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 { @@ -2669,10 +2677,9 @@ struct bidi_statistics { #define QLA_MQ_SIZE 32 #define QLA_MAX_QUEUES 256 #define ISP_QUE_REG(ha, id) \ - ((ha->mqenable || IS_QLA83XX(ha)) ? \ - ((device_reg_t __iomem *)(ha->mqiobase) +\ - (QLA_QUE_PAGE * id)) :\ - ((device_reg_t __iomem *)(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 @@ -2688,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; @@ -2714,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; @@ -2725,7 +2734,6 @@ struct req_que { srb_t **outstanding_cmds; uint32_t current_outstanding_cmd; uint16_t num_outstanding_cmds; -#define MAX_Q_DEPTH 32 int max_q_depth; dma_addr_t dma_fx00; @@ -2741,6 +2749,13 @@ 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; @@ -2756,15 +2771,11 @@ struct qlt_hw_data { uint32_t __iomem *atio_q_in; uint32_t __iomem *atio_q_out; - void *target_lport_ptr; struct qla_tgt_func_tmpl *tgt_ops; - struct qla_tgt *qla_tgt; struct qla_tgt_cmd *cmds[DEFAULT_OUTSTANDING_COMMANDS]; uint16_t current_handle; struct qla_tgt_vp_map *tgt_vp_map; - struct mutex tgt_mutex; - struct mutex tgt_host_action_mutex; int saved_set; uint16_t saved_exchange_count; @@ -2806,7 +2817,6 @@ 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; @@ -2837,7 +2847,7 @@ 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 @@ -2856,8 +2866,8 @@ struct qla_hw_data { uint32_t rsp_que_off; /* Multi queue data structs */ - device_reg_t __iomem *mqiobase; - device_reg_t __iomem *msixbase; + device_reg_t *mqiobase; + device_reg_t *msixbase; uint16_t msix_count; uint8_t mqenable; struct req_que **req_q_map; @@ -2893,6 +2903,7 @@ struct qla_hw_data { #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 */ @@ -2916,6 +2927,9 @@ struct qla_hw_data { #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 @@ -2935,7 +2949,10 @@ struct qla_hw_data { #define DT_ISP2031 BIT_15 #define DT_ISP8031 BIT_16 #define DT_ISPFX00 BIT_17 -#define DT_ISP_LAST (DT_ISPFX00 << 1) +#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 @@ -2961,9 +2978,12 @@ struct qla_hw_data { #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)) @@ -2972,18 +2992,22 @@ struct qla_hw_data { #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_CNA_CAPABLE(ha) (IS_QLA81XX(ha) || IS_QLA82XX(ha) || \ - IS_QLA8031(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) || IS_QLA83XX(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_QLA25XX(ha) || IS_QLA81XX(ha) || \ - IS_QLA83XX(ha)) && (ha)->flags.msix_enabled) -#define IS_FAC_REQUIRED(ha) (IS_QLA81XX(ha) || IS_QLA83XX(ha)) -#define IS_NOCACHE_VPD_TYPE(ha) (IS_QLA81XX(ha) || IS_QLA83XX(ha)) +#define IS_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) @@ -2993,7 +3017,8 @@ struct qla_hw_data { #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)) +#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) && \ @@ -3006,6 +3031,7 @@ struct qla_hw_data { (((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; @@ -3118,6 +3144,9 @@ struct qla_hw_data { 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]; uint16_t fw_seriallink_options24[4]; @@ -3126,11 +3155,22 @@ 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 */ @@ -3168,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 @@ -3187,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; @@ -3277,6 +3320,7 @@ struct qla_hw_data { /* QLA83XX IDC specific fields */ uint32_t idc_audit_ts; + uint32_t idc_extend_tmo; /* DPC low-priority workqueue */ struct workqueue_struct *dpc_lp_wq; @@ -3286,19 +3330,12 @@ struct qla_hw_data { struct work_struct nic_core_reset; struct work_struct idc_state_handler; struct work_struct nic_core_unrecoverable; - -#define HOST_QUEUE_RAMPDOWN_INTERVAL (60 * HZ) -#define HOST_QUEUE_RAMPUP_INTERVAL (30 * HZ) - unsigned long host_last_rampdown_time; - unsigned long host_last_rampup_time; - int cfg_lun_q_depth; + struct work_struct board_disable; struct mr_data_fx00 mr; struct qlt_hw_data tgt; - uint16_t thermal_support; -#define THERMAL_SUPPORT_I2C BIT_0 -#define THERMAL_SUPPORT_ISP BIT_1 + int allow_cna_fw_dump; }; /* @@ -3359,11 +3396,11 @@ typedef struct scsi_qla_host { #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 HOST_RAMP_DOWN_QUEUE_DEPTH 22 -#define HOST_RAMP_UP_QUEUE_DEPTH 23 -#define PORT_UPDATE_NEEDED 24 -#define FX00_RESET_RECOVERY 25 -#define FX00_TARGET_SCAN 26 +#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 @@ -3402,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 */ @@ -3427,6 +3464,7 @@ 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; @@ -3435,6 +3473,7 @@ typedef struct scsi_qla_host { struct bidi_statistics bidi_stats; atomic_t vref_count; + struct qla8044_reset_template reset_tmplt; } scsi_qla_host_t; #define SET_VP_IDX 1 diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c index 792a29294b6..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-2013 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -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) && !IS_QLA83XX(ha)) + if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha) && + !IS_QLA27XX(ha)) goto out; if (!ha->fce) goto out; diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h index 1ac2b0e3a0e..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-2013 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -371,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; @@ -1134,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; @@ -1378,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 @@ -1387,6 +1387,8 @@ 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 diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 2d98232a08e..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-2013 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -98,7 +98,6 @@ extern int qlport_down_retry; extern int ql2xplogiabsentdevice; extern int ql2xloginretrycount; extern int ql2xfdmienable; -extern int ql2xmaxqdepth; extern int ql2xallocfwdump; extern int ql2xextended_error_logging; extern int ql2xiidmaenable; @@ -160,6 +159,9 @@ 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. */ @@ -218,6 +220,13 @@ 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 *); + /* * Global Function Prototypes in qla_mbx.c source file. @@ -328,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 @@ -339,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 @@ -357,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); @@ -435,20 +461,21 @@ qla2x00_process_completed_request(struct scsi_qla_host *, struct req_que *, */ 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 *); @@ -463,21 +490,25 @@ 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 *); + 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); @@ -493,12 +524,22 @@ 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); + uint8_t *, uint32_t); extern void qla2xxx_dump_post_process(scsi_qla_host_t *, int); /* @@ -531,10 +572,9 @@ 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 *); @@ -577,13 +617,13 @@ 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_command(srb_t *); 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 *); @@ -601,6 +641,7 @@ 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 */ @@ -619,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 *); @@ -662,7 +703,7 @@ 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(scsi_qla_host_t *); @@ -674,6 +715,8 @@ 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 *); @@ -695,5 +738,31 @@ 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 0926451980e..a0df3b1b382 100644 --- a/drivers/scsi/qla2xxx/qla_gs.c +++ b/drivers/scsi/qla2xxx/qla_gs.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2013 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -49,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); } @@ -87,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); } @@ -226,17 +230,9 @@ qla2x00_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport) fcport->d_id.b.domain = 0xf0; ql_dbg(ql_dbg_disc, vha, 0x2063, - "GA_NXT entry - nn %02x%02x%02x%02x%02x%02x%02x%02x " - "pn %02x%02x%02x%02x%02x%02x%02x%02x " + "GA_NXT entry - nn %8phN pn %8phN " "port_id=%02x%02x%02x.\n", - fcport->node_name[0], fcport->node_name[1], - fcport->node_name[2], fcport->node_name[3], - fcport->node_name[4], fcport->node_name[5], - fcport->node_name[6], fcport->node_name[7], - fcport->port_name[0], fcport->port_name[1], - fcport->port_name[2], fcport->port_name[3], - fcport->port_name[4], fcport->port_name[5], - fcport->port_name[6], fcport->port_name[7], + fcport->node_name, fcport->port_name, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa); } @@ -447,17 +443,9 @@ qla2x00_gnn_id(scsi_qla_host_t *vha, sw_info_t *list) ct_rsp->rsp.gnn_id.node_name, WWN_SIZE); ql_dbg(ql_dbg_disc, vha, 0x2058, - "GID_PT entry - nn %02x%02x%02x%02x%02x%02x%02X%02x " - "pn %02x%02x%02x%02x%02x%02x%02X%02x " + "GID_PT entry - nn %8phN pn %8phN " "portid=%02x%02x%02x.\n", - list[i].node_name[0], list[i].node_name[1], - list[i].node_name[2], list[i].node_name[3], - list[i].node_name[4], list[i].node_name[5], - list[i].node_name[6], list[i].node_name[7], - list[i].port_name[0], list[i].port_name[1], - list[i].port_name[2], list[i].port_name[3], - list[i].port_name[4], list[i].port_name[5], - list[i].port_name[6], list[i].port_name[7], + list[i].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); } @@ -739,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); } @@ -796,17 +786,9 @@ qla2x00_sns_ga_nxt(scsi_qla_host_t *vha, fc_port_t *fcport) fcport->d_id.b.domain = 0xf0; ql_dbg(ql_dbg_disc, vha, 0x2061, - "GA_NXT entry - nn %02x%02x%02x%02x%02x%02x%02x%02x " - "pn %02x%02x%02x%02x%02x%02x%02x%02x " + "GA_NXT entry - nn %8phN pn %8phN " "port_id=%02x%02x%02x.\n", - fcport->node_name[0], fcport->node_name[1], - fcport->node_name[2], fcport->node_name[3], - fcport->node_name[4], fcport->node_name[5], - fcport->node_name[6], fcport->node_name[7], - fcport->port_name[0], fcport->port_name[1], - fcport->port_name[2], fcport->port_name[3], - fcport->port_name[4], fcport->port_name[5], - fcport->port_name[6], fcport->port_name[7], + fcport->node_name, fcport->port_name, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa); } @@ -991,17 +973,9 @@ qla2x00_sns_gnn_id(scsi_qla_host_t *vha, sw_info_t *list) WWN_SIZE); ql_dbg(ql_dbg_disc, vha, 0x206e, - "GID_PT entry - nn %02x%02x%02x%02x%02x%02x%02x%02x " - "pn %02x%02x%02x%02x%02x%02x%02x%02x " + "GID_PT entry - nn %8phN pn %8phN " "port_id=%02x%02x%02x.\n", - list[i].node_name[0], list[i].node_name[1], - list[i].node_name[2], list[i].node_name[3], - list[i].node_name[4], list[i].node_name[5], - list[i].node_name[6], list[i].node_name[7], - list[i].port_name[0], list[i].port_name[1], - list[i].port_name[2], list[i].port_name[3], - list[i].port_name[4], list[i].port_name[5], - list[i].port_name[6], list[i].port_name[7], + list[i].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); } @@ -1321,11 +1295,7 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *vha) size += 4 + WWN_SIZE; ql_dbg(ql_dbg_disc, vha, 0x2025, - "NodeName = %02x%02x%02x%02x%02x%02x%02x%02x.\n", - eiter->a.node_name[0], eiter->a.node_name[1], - eiter->a.node_name[2], eiter->a.node_name[3], - eiter->a.node_name[4], eiter->a.node_name[5], - eiter->a.node_name[6], eiter->a.node_name[7]); + "NodeName = %8phN.\n", eiter->a.node_name); /* Manufacturer. */ eiter = (struct ct_fdmi_hba_attr *) (entries + size); @@ -1428,16 +1398,8 @@ qla2x00_fdmi_rhba(scsi_qla_host_t *vha) qla2x00_update_ms_fdmi_iocb(vha, size + 16); ql_dbg(ql_dbg_disc, vha, 0x202e, - "RHBA identifier = " - "%02x%02x%02x%02x%02x%02x%02x%02x size=%d.\n", - ct_req->req.rhba.hba_identifier[0], - ct_req->req.rhba.hba_identifier[1], - ct_req->req.rhba.hba_identifier[2], - ct_req->req.rhba.hba_identifier[3], - ct_req->req.rhba.hba_identifier[4], - ct_req->req.rhba.hba_identifier[5], - ct_req->req.rhba.hba_identifier[6], - ct_req->req.rhba.hba_identifier[7], size); + "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); @@ -1494,11 +1456,7 @@ qla2x00_fdmi_dhba(scsi_qla_host_t *vha) memcpy(ct_req->req.dhba.port_name, vha->port_name, WWN_SIZE); ql_dbg(ql_dbg_disc, vha, 0x2036, - "DHBA portname = %02x%02x%02x%02x%02x%02x%02x%02x.\n", - ct_req->req.dhba.port_name[0], ct_req->req.dhba.port_name[1], - ct_req->req.dhba.port_name[2], ct_req->req.dhba.port_name[3], - ct_req->req.dhba.port_name[4], ct_req->req.dhba.port_name[5], - ct_req->req.dhba.port_name[6], ct_req->req.dhba.port_name[7]); + "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, @@ -1574,6 +1532,10 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *vha) 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| @@ -1622,6 +1584,10 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *vha) 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); @@ -1678,12 +1644,7 @@ qla2x00_fdmi_rpa(scsi_qla_host_t *vha) qla2x00_update_ms_fdmi_iocb(vha, size + 16); ql_dbg(ql_dbg_disc, vha, 0x203e, - "RPA portname= %02x%02x%02x%02x%02X%02x%02x%02x size=%d.\n", - ct_req->req.rpa.port_name[0], ct_req->req.rpa.port_name[1], - ct_req->req.rpa.port_name[2], ct_req->req.rpa.port_name[3], - ct_req->req.rpa.port_name[4], ct_req->req.rpa.port_name[5], - ct_req->req.rpa.port_name[6], ct_req->req.rpa.port_name[7], - size); + "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); @@ -1936,20 +1897,15 @@ qla2x00_gpsc(scsi_qla_host_t *vha, sw_info_t *list) case BIT_10: list[i].fp_speed = PORT_SPEED_16GB; break; + case BIT_8: + list[i].fp_speed = PORT_SPEED_32GB; + break; } ql_dbg(ql_dbg_disc, vha, 0x205b, "GPSC ext entry - fpn " - "%02x%02x%02x%02x%02x%02x%02x%02x speeds=%04x " - "speed=%04x.\n", - list[i].fabric_port_name[0], - list[i].fabric_port_name[1], - list[i].fabric_port_name[2], - list[i].fabric_port_name[3], - list[i].fabric_port_name[4], - list[i].fabric_port_name[5], - list[i].fabric_port_name[6], - list[i].fabric_port_name[7], + "%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)); } diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index f2216ed2ad8..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-2013 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -271,56 +271,46 @@ done: } static void -qla2x00_async_tm_cmd_done(void *data, void *ptr, int res) +qla2x00_tmf_iocb_timeout(void *data) { - srb_t *sp = (srb_t *)ptr; - struct srb_iocb *iocb = &sp->u.iocb_cmd; - struct scsi_qla_host *vha = (scsi_qla_host_t *)data; - uint32_t flags; - uint16_t lun; - int rval; - - if (!test_bit(UNLOADING, &vha->dpc_flags)) { - flags = iocb->u.tmf.flags; - lun = (uint16_t)iocb->u.tmf.lun; + srb_t *sp = (srb_t *)data; + struct srb_iocb *tmf = &sp->u.iocb_cmd; - /* Issue Marker IOCB */ - rval = qla2x00_marker(vha, vha->hw->req_q_map[0], - vha->hw->rsp_q_map[0], sp->fcport->loop_id, lun, - flags == TCF_LUN_RESET ? MK_SYNC_ID_LUN : MK_SYNC_ID); + tmf->u.tmf.comp_status = CS_TIMEOUT; + complete(&tmf->u.tmf.comp); +} - if ((rval != QLA_SUCCESS) || iocb->u.tmf.data) { - ql_dbg(ql_dbg_taskm, vha, 0x8030, - "TM IOCB failed (%x).\n", rval); - } - } - sp->free(sp->fcport->vha, sp); +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 -qla2x00_async_tm_cmd(fc_port_t *fcport, uint32_t tm_flags, uint32_t lun, +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_iocb *tcf; - int rval; + int rval = QLA_FUNCTION_FAILED; - rval = QLA_FUNCTION_FAILED; sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); if (!sp) goto done; + tm_iocb = &sp->u.iocb_cmd; sp->type = SRB_TM_CMD; sp->name = "tmf"; - qla2x00_init_timer(sp, qla2x00_get_async_timeout(vha) + 2); - - tcf = &sp->u.iocb_cmd; - tcf->u.tmf.flags = tm_flags; - tcf->u.tmf.lun = lun; - tcf->u.tmf.data = tag; - tcf->timeout = qla2x00_async_iocb_timeout; - sp->done = qla2x00_async_tm_cmd_done; + 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) @@ -330,14 +320,121 @@ qla2x00_async_tm_cmd(fc_port_t *fcport, uint32_t tm_flags, uint32_t lun, "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: - sp->free(fcport->vha, 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) @@ -524,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->thermal_support = THERMAL_SUPPORT_I2C|THERMAL_SUPPORT_ISP; + 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; @@ -552,7 +649,18 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha) if (rval) { ql_log(ql_log_fatal, vha, 0x004f, "Unable to validate FLASH data.\n"); - return (rval); + return rval; + } + + if (IS_QLA8044(ha)) { + qla8044_read_reset_template(vha); + + /* 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); @@ -564,12 +672,7 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha) if (ha->flags.disable_serdes) { /* Mask HBA via NVRAM settings? */ ql_log(ql_log_info, vha, 0x0077, - "Masking HBA WWPN " - "%02x%02x%02x%02x%02x%02x%02x%02x (via NVRAM).\n", - vha->port_name[0], vha->port_name[1], - vha->port_name[2], vha->port_name[3], - vha->port_name[4], vha->port_name[5], - vha->port_name[6], vha->port_name[7]); + "Masking HBA WWPN %8phN (via NVRAM).\n", vha->port_name); return QLA_FUNCTION_FAILED; } @@ -620,6 +723,11 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha) 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); } @@ -1332,7 +1440,7 @@ 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; @@ -1368,7 +1476,13 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) } 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)) { @@ -1384,6 +1498,7 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) 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) { @@ -1401,9 +1516,16 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) 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) && !IS_QLA83XX(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) { @@ -1431,7 +1553,12 @@ qla2x00_alloc_fw_dump(scsi_qla_host_t *vha) 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); @@ -1458,15 +1585,28 @@ try_eft: 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) { ql_log(ql_log_warn, vha, 0x00c4, @@ -1488,10 +1628,13 @@ cont_alloc: } return; } + ha->fw_dump_len = dump_size; ql_dbg(ql_dbg_init, vha, 0x00c5, "Allocated (%d KB) for firmware dump.\n", dump_size / 1024); - ha->fw_dump_len = dump_size; + if (IS_QLA27XX(ha)) + return; + ha->fw_dump->signature[0] = 'Q'; ha->fw_dump->signature[1] = 'L'; ha->fw_dump->signature[2] = 'G'; @@ -1615,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); @@ -1651,7 +1794,7 @@ qla2x00_setup_chip(scsi_qla_host_t *vha) if (rval == QLA_SUCCESS) { enable_82xx_npiv: fw_major_version = ha->fw_major_version; - if (IS_QLA82XX(ha)) + if (IS_P3P_TYPE(ha)) qla82xx_check_md_needed(vha); else rval = qla2x00_get_fw_version(vha); @@ -1681,8 +1824,10 @@ enable_82xx_npiv: goto failed; if (!fw_major_version && ql2xallocfwdump - && !IS_QLA82XX(ha)) + && !(IS_P3P_TYPE(ha))) qla2x00_alloc_fw_dump(vha); + } else { + goto failed; } } else { ql_log(ql_log_fatal, vha, 0x00cd, @@ -1705,9 +1850,6 @@ enable_82xx_npiv: spin_unlock_irqrestore(&ha->hardware_lock, flags); } - if (IS_QLA83XX(ha)) - goto skip_fac_check; - if (rval == QLA_SUCCESS && IS_FAC_REQUIRED(ha)) { uint32_t size; @@ -1720,8 +1862,8 @@ enable_82xx_npiv: "Unsupported FAC firmware (%d.%02d.%02d).\n", ha->fw_major_version, ha->fw_minor_version, ha->fw_subminor_version); -skip_fac_check: - if (IS_QLA83XX(ha)) { + + if (IS_QLA83XX(ha) || IS_QLA27XX(ha)) { ha->flags.fac_supported = 0; rval = QLA_SUCCESS; } @@ -1849,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. */ @@ -1920,7 +2062,11 @@ qla24xx_config_rings(struct scsi_qla_host *vha) 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 (ha->mqenable || IS_QLA83XX(ha)) { + 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) { @@ -1997,6 +2143,8 @@ qla2x00_init_rings(scsi_qla_host_t *vha) req = ha->req_q_map[que]; if (!req) continue; + 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; @@ -2012,6 +2160,8 @@ 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 */ if (IS_QLAFX00(ha)) qlafx00_init_response_q_entries(rsp); @@ -3061,22 +3211,13 @@ qla2x00_iidma_fcport(scsi_qla_host_t *vha, fc_port_t *fcport) mb); if (rval != QLA_SUCCESS) { ql_dbg(ql_dbg_disc, vha, 0x2004, - "Unable to adjust iIDMA " - "%02x%02x%02x%02x%02x%02x%02x%02x -- %04x %x %04x " - "%04x.\n", fcport->port_name[0], fcport->port_name[1], - fcport->port_name[2], fcport->port_name[3], - fcport->port_name[4], fcport->port_name[5], - fcport->port_name[6], fcport->port_name[7], rval, - fcport->fp_speed, mb[0], mb[1]); + "Unable to adjust iIDMA %8phN -- %04x %x %04x %04x.\n", + fcport->port_name, rval, fcport->fp_speed, mb[0], mb[1]); } else { ql_dbg(ql_dbg_disc, vha, 0x2005, - "iIDMA adjusted to %s GB/s " - "on %02x%02x%02x%02x%02x%02x%02x%02x.\n", + "iIDMA adjusted to %s GB/s on %8phN.\n", qla2x00_get_link_speed_str(ha, fcport->fp_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]); + fcport->port_name); } } @@ -3274,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); } } } @@ -4007,10 +4148,18 @@ qla83xx_reset_ownership(scsi_qla_host_t *vha) uint32_t class_type_mask = 0x3; uint16_t fcoe_other_function = 0xffff, i; - 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); + 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) && @@ -4347,7 +4496,7 @@ 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); @@ -4360,7 +4509,7 @@ qla2x00_abort_isp_cleanup(scsi_qla_host_t *vha) * 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); @@ -4403,7 +4552,7 @@ 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"); @@ -4587,7 +4736,6 @@ 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]; @@ -4604,14 +4752,12 @@ 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) { - ql_dbg(ql_dbg_taskm, vha, 0x8031, - "Start configure loop status = %d.\n", status); - /* Issue a marker after FW becomes ready. */ qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL); @@ -4626,24 +4772,12 @@ qla2x00_restart_isp(scsi_qla_host_t *vha) qlt_24xx_process_atio_queue(vha); spin_unlock_irqrestore(&ha->hardware_lock, flags); - /* 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; - - ql_dbg(ql_dbg_taskm, vha, 0x8032, - "Configure loop done, status = 0x%x.\n", status); } return (status); } @@ -4723,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; @@ -4780,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; @@ -4832,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; @@ -5107,6 +5240,99 @@ 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; } @@ -5221,7 +5447,8 @@ 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]; @@ -5253,7 +5480,7 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr) 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]); @@ -5267,7 +5494,7 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr) ql_log(ql_log_fatal, vha, 0x0095, "Firmware data: %08x %08x %08x %08x.\n", dcode[0], dcode[1], dcode[2], dcode[3]); - goto fail_fw_integrity; + return QLA_FUNCTION_FAILED; } while (segments && rval == QLA_SUCCESS) { @@ -5281,8 +5508,7 @@ qla24xx_load_risc_blob(scsi_qla_host_t *vha, uint32_t *srisc_addr) ql_log(ql_log_fatal, vha, 0x0096, "Unable to verify integrity of firmware image " "(%Zd).\n", blob->fw->size); - - goto fail_fw_integrity; + return QLA_FUNCTION_FAILED; } fragment = 0; @@ -5316,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 @@ -5552,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; @@ -5593,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; @@ -5627,7 +5945,7 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) 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; } @@ -5665,7 +5983,7 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) 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. */ @@ -5734,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: @@ -5768,7 +6086,7 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) ha->login_retry_count = ql2xloginretrycount; /* if not running MSI-X we need handshaking on interrupts */ - if (!vha->hw->flags.msix_enabled && IS_QLA83XX(ha)) + if (!vha->hw->flags.msix_enabled && (IS_QLA83XX(ha) || IS_QLA27XX(ha))) icb->firmware_options_2 |= __constant_cpu_to_le32(BIT_22); /* Enable ZIO. */ @@ -5806,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]; @@ -5820,31 +6137,15 @@ qla82xx_restart_isp(scsi_qla_host_t *vha) status = qla2x00_fw_ready(vha); if (!status) { - ql_log(ql_log_info, vha, 0x803c, - "Start configure loop, status =%d.\n", status); - /* Issue a marker after FW becomes ready. */ qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL); - vha->flags.online = 1; - /* Wait at most MAX_TARGET RSCNs for a stable link. */ - wait_time = 256; - do { - clear_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags); - qla2x00_configure_loop(vha); - wait_time--; - } while (!atomic_read(&vha->loop_down_timer) && - !(test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) && - wait_time && - (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags))); + 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; - - ql_log(ql_log_info, vha, 0x8000, - "Configure loop done, status = 0x%x.\n", status); } if (!status) { @@ -5858,8 +6159,6 @@ 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; @@ -6061,7 +6360,7 @@ qla24xx_update_fcport_fcp_prio(scsi_qla_host_t *vha, fc_port_t *fcport) if (priority < 0) return QLA_FUNCTION_FAILED; - if (IS_QLA82XX(vha->hw)) { + if (IS_P3P_TYPE(vha->hw)) { fcport->fcp_prio = priority & 0xf; return QLA_SUCCESS; } diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h index 28c38b4929c..b3b1d6fc2d6 100644 --- a/drivers/scsi/qla2xxx/qla_inline.h +++ b/drivers/scsi/qla2xxx/qla_inline.h @@ -1,10 +1,11 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2013 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. @@ -59,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); @@ -128,12 +129,20 @@ qla2x00_clear_loop_id(fc_port_t *fcport) { } static inline void -qla2x00_clean_dsd_pool(struct qla_hw_data *ha, srb_t *sp) +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; - ctx = (struct crc_context *)GET_CMD_CTX_SP(sp); + 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, @@ -261,25 +270,6 @@ qla2x00_gid_list_size(struct qla_hw_data *ha) } static inline void -qla2x00_do_host_ramp_up(scsi_qla_host_t *vha) -{ - if (vha->hw->cfg_lun_q_depth >= ql2xmaxqdepth) - return; - - /* Wait at least HOST_QUEUE_RAMPDOWN_INTERVAL before ramping up */ - if (time_before(jiffies, (vha->hw->host_last_rampdown_time + - HOST_QUEUE_RAMPDOWN_INTERVAL))) - return; - - /* Wait at least HOST_QUEUE_RAMPUP_INTERVAL between each ramp up */ - if (time_before(jiffies, (vha->hw->host_last_rampup_time + - HOST_QUEUE_RAMPUP_INTERVAL))) - return; - - set_bit(HOST_RAMP_UP_QUEUE_DEPTH, &vha->dpc_flags); -} - -static inline void qla2x00_handle_mbx_completion(struct qla_hw_data *ha, int status) { if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) && diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index ef0a5481b9d..76093152959 100644 --- a/drivers/scsi/qla2xxx/qla_iocb.c +++ b/drivers/scsi/qla2xxx/qla_iocb.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2013 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -32,9 +32,11 @@ qla2x00_get_cmd_direction(srb_t *sp) if (cmd->sc_data_direction == DMA_TO_DEVICE) { cflags = CF_WRITE; 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; vha->qla_stats.input_bytes += scsi_bufflen(cmd); + vha->qla_stats.input_requests++; } return (cflags); } @@ -474,7 +476,7 @@ qla2x00_start_iocbs(struct scsi_qla_host *vha, struct req_que *req) struct qla_hw_data *ha = vha->hw; device_reg_t __iomem *reg = ISP_QUE_REG(ha, req->id); - if (IS_QLA82XX(ha)) { + if (IS_P3P_TYPE(ha)) { qla82xx_start_iocbs(vha); } else { /* Adjust ring index. */ @@ -486,7 +488,7 @@ qla2x00_start_iocbs(struct scsi_qla_host *vha, struct req_que *req) req->ring_ptr++; /* Set chip new ring index. */ - if (ha->mqenable || IS_QLA83XX(ha)) { + 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)) { @@ -522,7 +524,6 @@ __qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req, { mrk_entry_t *mrk; struct mrk_entry_24xx *mrk24 = NULL; - struct mrk_entry_fx00 *mrkfx = NULL; struct qla_hw_data *ha = vha->hw; scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); @@ -539,15 +540,7 @@ __qla2x00_marker(struct scsi_qla_host *vha, struct req_que *req, mrk->entry_type = MARKER_TYPE; mrk->modifier = type; if (type != MK_SYNC_ALL) { - if (IS_QLAFX00(ha)) { - mrkfx = (struct mrk_entry_fx00 *) mrk; - mrkfx->handle = MAKE_HANDLE(req->id, mrkfx->handle); - mrkfx->handle_hi = 0; - mrkfx->tgt_id = cpu_to_le16(loop_id); - mrkfx->lun[1] = LSB(lun); - mrkfx->lun[2] = MSB(lun); - host_to_fcp_swap(mrkfx->lun, sizeof(mrkfx->lun)); - } else if (IS_FWI2_CAPABLE(ha)) { + if (IS_FWI2_CAPABLE(ha)) { mrk24 = (struct mrk_entry_24xx *) mrk; mrk24->nport_handle = cpu_to_le16(loop_id); mrk24->lun[1] = LSB(lun); @@ -642,10 +635,12 @@ qla24xx_build_scsi_type_6_iocbs(srb_t *sp, struct cmd_type_6 *cmd_pkt, 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); @@ -758,10 +753,12 @@ qla24xx_build_scsi_iocbs(srb_t *sp, struct cmd_type_7 *cmd_pkt, cmd_pkt->task_mgmt_flags = __constant_cpu_to_le16(TMF_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->task_mgmt_flags = __constant_cpu_to_le16(TMF_READ_DATA); vha->qla_stats.input_bytes += scsi_bufflen(cmd); + vha->qla_stats.input_requests++; } /* One DSD is available in the Command Type 3 IOCB */ @@ -939,9 +936,9 @@ qla24xx_get_one_block_sg(uint32_t blk_sz, struct qla2_sgx *sgx, return 1; } -static int +int qla24xx_walk_and_build_sglist_no_difb(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; @@ -951,21 +948,35 @@ qla24xx_walk_and_build_sglist_no_difb(struct qla_hw_data *ha, srb_t *sp, uint32_t *cur_dsd = dsd; uint16_t used_dsds = tot_dsds; - uint32_t prot_int; + 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 = GET_CMD_SP(sp); - - prot_int = cmd->device->sector_size; + struct scsi_cmnd *cmd; + struct scsi_qla_host *vha; memset(&sgx, 0, sizeof(struct qla2_sgx)); - sgx.tot_bytes = scsi_bufflen(cmd); - sgx.cur_sg = scsi_sglist(cmd); - sgx.sp = sp; - - sg_prot = scsi_prot_sglist(cmd); + 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)) { @@ -998,10 +1009,18 @@ alloc_and_fill: return 1; } - list_add_tail(&dsd_ptr->list, - &((struct crc_context *)sp->u.scmd.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; + } else { + list_add_tail(&dsd_ptr->list, + &(tc->ctx->dsd_list)); + tc->ctx_dsd_alloced = 1; + } - sp->flags |= SRB_CRC_CTX_DSD_VALID; /* add new list to cmd iocb or last list */ *cur_dsd++ = cpu_to_le32(LSD(dsd_ptr->dsd_list_dma)); @@ -1036,21 +1055,35 @@ alloc_and_fill: return 0; } -static int +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 = GET_CMD_SP(sp); + 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; + } - scsi_for_each_sg(cmd, sg, tot_dsds, i) { + + for_each_sg(sgl, sg, tot_dsds, i) { dma_addr_t sle_dma; /* Allocate additional continuation packets? */ @@ -1079,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->u.scmd.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)); @@ -1105,23 +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; + 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; + } - cmd = GET_CMD_SP(sp); - scsi_for_each_prot_sg(cmd, sg, tot_dsds, i) { + ql_dbg(ql_dbg_tgt, vha, 0xe021, + "%s: enter\n", __func__); + + for_each_sg(sgl, sg, tot_dsds, i) { dma_addr_t sle_dma; /* Allocate additional continuation packets? */ @@ -1150,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->u.scmd.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)); @@ -1389,10 +1450,10 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, if (!bundling && tot_prot_dsds) { if (qla24xx_walk_and_build_sglist_no_difb(ha, sp, - cur_dsd, tot_dsds)) + 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))) + (tot_dsds - tot_prot_dsds), NULL)) goto crc_queuing_error; if (bundling && tot_prot_dsds) { @@ -1401,7 +1462,7 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, __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; @@ -1481,8 +1542,8 @@ qla24xx_start_scsi(srb_t *sp) 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); - + 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 @@ -1700,8 +1761,8 @@ 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 @@ -1817,7 +1878,7 @@ 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; req->num_outstanding_cmds; index++) { + for (index = 1; index < req->num_outstanding_cmds; index++) { handle++; if (handle == req->num_outstanding_cmds) handle = 1; @@ -1842,9 +1903,9 @@ qla2x00_alloc_iocbs(scsi_qla_host_t *vha, srb_t *sp) skip_cmd_array: /* Check for room on request queue. */ if (req->cnt < req_cnt) { - if (ha->mqenable || IS_QLA83XX(ha)) + 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); @@ -2056,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 @@ -2133,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 @@ -2584,6 +2649,29 @@ queuing_error: 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) { @@ -2637,7 +2725,9 @@ qla2x00_start_sp(srb_t *sp) qlafx00_fxdisc_iocb(sp, pkt); break; case SRB_ABT_CMD: - qlafx00_abort_iocb(sp, pkt); + IS_QLAFX00(ha) ? + qlafx00_abort_iocb(sp, pkt) : + qla24xx_abort_iocb(sp, pkt); break; default: break; @@ -2685,6 +2775,9 @@ qla25xx_build_bidir_iocb(srb_t *sp, struct scsi_qla_host *vha, 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 */ @@ -2796,8 +2889,8 @@ qla2x00_start_bidir(srb_t *sp, struct scsi_qla_host *vha, uint32_t tot_dsds) /* Check for room on request queue. */ 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 diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 2d8e7b81235..a56825c73c3 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2013 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -56,6 +56,16 @@ 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; @@ -110,6 +120,22 @@ qla2100_intr_handler(int irq, void *dev_id) 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: @@ -148,11 +174,14 @@ 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)) ql_log(ql_log_warn, vha, 0x5026, "Parity error -- HCCR=%x, Dumping " @@ -269,11 +298,18 @@ 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); @@ -282,40 +318,54 @@ qla81xx_idc_event(scsi_qla_host_t *vha, uint16_t aen, uint16_t descr) "%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]); - if ((aen == MBA_IDC_COMPLETE && mb[1] >> 15)) { - vha->hw->flags.idc_compl_status = 1; - if (vha->hw->notify_dcbx_comp) - complete(&vha->hw->dcbx_comp); - } - - /* Acknowledgement needed? [Notify && non-zero timeout]. */ - timeout = (descr >> 8) & 0xf; - if (aen != MBA_IDC_NOTIFY || !timeout) - return; + 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; - ql_dbg(ql_dbg_async, vha, 0x5022, - "%lu Inter-Driver Communication %s -- ACK timeout=%d.\n", - vha->host_no, event[aen & 0xff], timeout); + 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); - 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"); + 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; + } } #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", "10" + 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[6]; - else if (speed < 6) + return link_speeds[QLA_LAST_SPEED]; + else if (speed < QLA_LAST_SPEED) return link_speeds[speed]; else return link_speeds[LS_UNKNOWN]; @@ -600,7 +650,7 @@ skip_rio: break; case MBA_SYSTEM_ERR: /* System Error */ - mbx = (IS_QLA81XX(ha) || IS_QLA83XX(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 " @@ -617,7 +667,7 @@ skip_rio: 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); @@ -691,7 +741,8 @@ skip_rio: case MBA_LOOP_DOWN: /* Loop Down Event */ mbx = (IS_QLA81XX(ha) || IS_QLA8031(ha)) ? RD_REG_WORD(®24->mailbox4) : 0; - mbx = IS_QLA82XX(ha) ? RD_REG_WORD(®82->mailbox_out[4]) : mbx; + 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); @@ -740,11 +791,11 @@ skip_rio: if (IS_QLA2100(ha)) break; - if (IS_QLA81XX(ha) || IS_QLA82XX(ha) || IS_QLA8031(ha)) { + 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) + if (ha->notify_dcbx_comp && !vha->vp_idx) complete(&ha->dcbx_comp); } else @@ -1002,7 +1053,7 @@ skip_rio: mb[1], mb[2], mb[3]); break; case MBA_IDC_NOTIFY: - if (IS_QLA8031(vha->hw)) { + 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) && @@ -1018,11 +1069,12 @@ skip_rio: } } case MBA_IDC_COMPLETE: - if (ha->notify_lb_portup_comp) + if (ha->notify_lb_portup_comp && !vha->vp_idx) complete(&ha->lb_portup_comp); /* Fallthru */ case MBA_IDC_TIME_EXT: - if (IS_QLA81XX(vha->hw) || IS_QLA8031(vha->hw)) + if (IS_QLA81XX(vha->hw) || IS_QLA8031(vha->hw) || + IS_QLA8044(ha)) qla81xx_idc_event(vha, mb[0], mb[1]); break; @@ -1063,7 +1115,7 @@ qla2x00_process_completed_request(struct scsi_qla_host *vha, 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); @@ -1080,7 +1132,7 @@ qla2x00_process_completed_request(struct scsi_qla_host *vha, } else { 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); @@ -1100,7 +1152,7 @@ qla2x00_get_sp_from_handle(scsi_qla_host_t *vha, const char *func, if (index >= req->num_outstanding_cmds) { ql_log(ql_log_warn, vha, 0x5031, "Invalid 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); @@ -1446,8 +1498,7 @@ logio_done: } 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; @@ -1455,7 +1506,6 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, srb_t *sp; struct srb_iocb *iocb; struct sts_entry_24xx *sts = (struct sts_entry_24xx *)tsk; - int error = 1; sp = qla2x00_get_sp_from_handle(vha, func, req, tsk); if (!sp) @@ -1464,37 +1514,35 @@ qla24xx_tm_iocb_entry(scsi_qla_host_t *vha, struct req_que *req, iocb = &sp->u.iocb_cmd; type = sp->name; fcport = sp->fcport; + iocb->u.tmf.data = QLA_SUCCESS; if (sts->entry_status) { ql_log(ql_log_warn, fcport->vha, 0x5038, "Async-%s error - hdl=%x entry-status(%x).\n", type, sp->handle, sts->entry_status); + iocb->u.tmf.data = QLA_FUNCTION_FAILED; } else if (sts->comp_status != __constant_cpu_to_le16(CS_COMPLETE)) { ql_log(ql_log_warn, fcport->vha, 0x5039, "Async-%s error - hdl=%x completion status(%x).\n", type, sp->handle, sts->comp_status); - } else if (!(le16_to_cpu(sts->scsi_status) & + iocb->u.tmf.data = QLA_FUNCTION_FAILED; + } else if ((le16_to_cpu(sts->scsi_status) & SS_RESPONSE_INFO_LEN_VALID)) { - ql_log(ql_log_warn, fcport->vha, 0x503a, - "Async-%s error - hdl=%x no response info(%x).\n", - type, sp->handle, sts->scsi_status); - } else if (le32_to_cpu(sts->rsp_data_len) < 4) { - ql_log(ql_log_warn, fcport->vha, 0x503b, - "Async-%s error - hdl=%x not enough response(%d).\n", - type, sp->handle, sts->rsp_data_len); - } else if (sts->data[3]) { - ql_log(ql_log_warn, fcport->vha, 0x503c, - "Async-%s error - hdl=%x response(%x).\n", - type, sp->handle, sts->data[3]); - } else { - error = 0; + if (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; + if (iocb->u.tmf.data != QLA_SUCCESS) ql_dump_buffer(ql_dbg_async + ql_dbg_buffer, vha, 0x5055, (uint8_t *)sts, sizeof(*sts)); - } sp->done(vha, sp, 0); } @@ -1805,6 +1853,9 @@ qla25xx_process_bidir_status_iocb(scsi_qla_host_t *vha, void *pkt, 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; @@ -1939,6 +1990,15 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) que = MSW(sts->handle); req = ha->req_q_map[que]; + /* 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 < req->num_outstanding_cmds) sp = req->outstanding_cmds[handle]; @@ -1949,11 +2009,13 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) ql_dbg(ql_dbg_io, vha, 0x3017, "Invalid status handle (0x%x).\n", sts->handle); - if (IS_QLA82XX(ha)) - set_bit(FCOE_CTX_RESET_NEEDED, &vha->dpc_flags); - else - set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); - qla2xxx_wake_dpc(vha); + 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; } @@ -1962,9 +2024,14 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) 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_do_host_ramp_up(vha); qla2x00_process_completed_request(vha, req, handle); return; @@ -2176,8 +2243,10 @@ check_scsi_status: } ql_dbg(ql_dbg_io, fcport->vha, 0x3021, - "Port down status: port-state=0x%x.\n", - atomic_read(&fcport->state)); + "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); @@ -2212,21 +2281,15 @@ check_scsi_status: out: if (logit) ql_dbg(ql_dbg_io, fcport->vha, 0x3022, - "FCP command status: 0x%x-0x%x (0x%x) " - "nexus=%ld:%d:%d portid=%02x%02x%02x oxid=0x%x " - "cdb=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x len=0x%x " + "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[0], cp->cmnd[1], cp->cmnd[2], cp->cmnd[3], - cp->cmnd[4], cp->cmnd[5], cp->cmnd[6], cp->cmnd[7], - cp->cmnd[8], cp->cmnd[9], scsi_bufflen(cp), rsp_info_len, + cp->cmnd, scsi_bufflen(cp), rsp_info_len, resid_len, fw_resid_len); - if (!res) - qla2x00_do_host_ramp_up(vha); - if (rsp->status_srb == NULL) sp->done(ha, sp, res); } @@ -2324,7 +2387,7 @@ fatal: ql_log(ql_log_warn, vha, 0x5030, "Error entry - invalid handle/queue.\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); @@ -2367,6 +2430,23 @@ qla24xx_mbx_completion(scsi_qla_host_t *vha, uint16_t mb0) } } +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); +} + /** * qla24xx_process_response_queue() - Process response queue entries. * @ha: SCSI driver HA context @@ -2394,12 +2474,14 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, if (pkt->entry_status != 0) { qla2x00_error_entry(vha, rsp, (sts_entry_t *) pkt); - (void)qlt_24xx_process_response_error(vha, 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: @@ -2416,14 +2498,10 @@ 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); 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: @@ -2432,6 +2510,7 @@ void qla24xx_process_response_queue(struct scsi_qla_host *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: @@ -2439,6 +2518,10 @@ void qla24xx_process_response_queue(struct scsi_qla_host *vha, * 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. */ ql_dbg(ql_dbg_async, vha, 0x5042, @@ -2452,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 @@ -2467,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) && !IS_QLA83XX(ha)) + if (!IS_QLA25XX(ha) && !IS_QLA81XX(ha) && !IS_QLA83XX(ha) && + !IS_QLA27XX(ha)) return; rval = QLA_SUCCESS; @@ -2549,6 +2633,8 @@ 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; @@ -2618,6 +2704,7 @@ 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) { @@ -2631,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; @@ -2645,9 +2740,11 @@ 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) { @@ -2656,17 +2753,21 @@ qla25xx_msix_rsp_q(int irq, void *dev_id) return IRQ_NONE; } ha = rsp->hw; + vha = pci_get_drvdata(ha->pdev); /* Clear the interrupt, if enabled, for this response queue */ if (!ha->flags.disable_msix_handshake) { reg = &ha->iobase->isp24; spin_lock_irqsave(&ha->hardware_lock, flags); WRT_REG_DWORD(®->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; } @@ -2697,6 +2798,8 @@ 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; @@ -2803,6 +2906,7 @@ 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; @@ -2859,36 +2963,49 @@ msix_failed: } /* Enable MSI-X vectors for the base queue */ - for (i = 0; i < ha->msix_count; i++) { + for (i = 0; i < 2; i++) { qentry = &ha->msix_entries[i]; - if (QLA_TGT_MODE_ENABLED() && IS_ATIO_MSIX_CAPABLE(ha)) { - ret = request_irq(qentry->vector, - qla83xx_msix_entries[i].handler, - 0, qla83xx_msix_entries[i].name, rsp); - } else 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) { - 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; - } + 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 (IS_QLA83XX(ha)) { + 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; @@ -2911,13 +3028,14 @@ 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_CNA_CAPABLE(ha) && !IS_QLA2031(ha) && !IS_QLAFX00(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 && @@ -2945,12 +3063,15 @@ qla2x00_request_irqs(struct qla_hw_data *ha, struct rsp_que *rsp) ha->chip_revision, ha->fw_attributes); goto clear_risc_ints; } - ql_log(ql_log_info, vha, 0x0037, - "MSI-X Falling back-to MSI mode -%d.\n", ret); + skip_msix: + 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_QLA82XX(ha) && !IS_QLAFX00(ha)) + !IS_QLA8001(ha) && !IS_P3P_TYPE(ha) && !IS_QLAFX00(ha) && + !IS_QLA27XX(ha)) goto skip_msi; ret = pci_enable_msi(ha->pdev); @@ -2960,14 +3081,13 @@ skip_msix: ha->flags.msi_enabled = 1; } else ql_log(ql_log_warn, vha, 0x0039, - "MSI-X; Falling back-to INTa mode -- %d.\n", ret); + "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; -skip_msi: - ret = request_irq(ha->pdev->irq, ha->isp_ops->intr_handler, ha->flags.msi_enabled ? 0 : IRQF_SHARED, QLA2XXX_DRIVER_NAME, rsp); diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 7257c3c4f2d..1c33a77db5c 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2013 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -35,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; @@ -75,7 +75,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) return QLA_FUNCTION_TIMEOUT; } - if (IS_QLA82XX(ha) && 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; ql_log(ql_log_warn, vha, 0x1004, @@ -106,9 +106,9 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) 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); @@ -117,33 +117,25 @@ 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++; } - ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1111, - "Loaded MBX registers (displayed in bytes) =.\n"); - ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1112, - (uint8_t *)mcp->mb, 16); - ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1113, - ".\n"); - ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1114, - ((uint8_t *)mcp->mb + 0x10), 16); - ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1115, - ".\n"); - ql_dump_buffer(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1116, - ((uint8_t *)mcp->mb + 0x20), 8); ql_dbg(ql_dbg_mbx + ql_dbg_buffer, vha, 0x1117, "I/O Address = %p.\n", optr); - ql_dump_regs(ql_dbg_mbx + ql_dbg_buffer, vha, 0x100e); /* Issue set host interrupt command to send cmd out. */ ha->flags.mbox_int = 0; @@ -159,7 +151,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) 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, @@ -189,7 +181,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) 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, @@ -236,7 +228,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) ha->flags.mbox_int = 0; clear_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); - if ((IS_QLA82XX(ha) && 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; @@ -254,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++; @@ -470,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) || IS_QLA83XX(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); @@ -537,10 +536,12 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha) mcp->mb[0] = MBC_GET_FIRMWARE_VERSION; mcp->out_mb = MBX_0; mcp->in_mb = MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; - if (IS_QLA81XX(vha->hw) || IS_QLA8031(ha)) + 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); @@ -556,7 +557,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha) ha->fw_memory_size = 0x1FFFF; /* Defaults to 128KB. */ else ha->fw_memory_size = (mcp->mb[5] << 16) | mcp->mb[4]; - if (IS_QLA81XX(vha->hw) || IS_QLA8031(vha->hw)) { + 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; @@ -576,6 +577,10 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha) "%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) { @@ -1201,7 +1206,7 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size) 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))); @@ -1216,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) || IS_QLA83XX(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); @@ -1227,7 +1232,7 @@ qla2x00_init_firmware(scsi_qla_host_t *vha, uint16_t size) } /* 1 and 2 should normally be captured. */ mcp->in_mb = MBX_2|MBX_1|MBX_0; - if (IS_QLA83XX(ha)) + if (IS_QLA83XX(ha) || IS_QLA27XX(ha)) /* mb3 is additional info about the installed SFP. */ mcp->in_mb |= MBX_3; mcp->buf_size = size; @@ -1314,7 +1319,7 @@ qla2x00_get_node_name_list(scsi_qla_host_t *vha, void **out_data, int *out_len) left = 0; - list = kzalloc(dma_size, GFP_KERNEL); + list = kmemdup(pmap, dma_size, GFP_KERNEL); if (!list) { ql_log(ql_log_warn, vha, 0x1140, "%s(%ld): failed to allocate node names list " @@ -1323,7 +1328,6 @@ qla2x00_get_node_name_list(scsi_qla_host_t *vha, void **out_data, int *out_len) goto out_free; } - memcpy(list, pmap, dma_size); restart: dma_free_coherent(&ha->pdev->dev, dma_size, pmap, pmap_dma); } @@ -1667,7 +1671,11 @@ qla24xx_link_initialize(scsi_qla_host_t *vha) return QLA_FUNCTION_FAILED; mcp->mb[0] = MBC_LINK_INITIALIZATION; - mcp->mb[1] = BIT_6|BIT_4; + 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; @@ -2347,7 +2355,7 @@ qla2x00_get_resource_cnts(scsi_qla_host_t *vha, uint16_t *cur_xchg_cnt, mcp->mb[0] = MBC_GET_RESOURCE_COUNTS; mcp->out_mb = MBX_0; mcp->in_mb = MBX_11|MBX_10|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; - if (IS_QLA81XX(vha->hw) || IS_QLA83XX(vha->hw)) + 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; @@ -2588,6 +2596,9 @@ qla24xx_abort_command(srb_t *sp) 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 < req->num_outstanding_cmds; handle++) { if (req->outstanding_cmds[handle] == sp) @@ -2632,7 +2643,10 @@ qla24xx_abort_command(srb_t *sp) ql_dbg(ql_dbg_mbx, vha, 0x1090, "Failed to complete IOCB -- completion status (%x).\n", le16_to_cpu(abt->nport_handle)); - rval = QLA_FUNCTION_FAILED; + if (abt->nport_handle == CS_IOCB_ERROR) + rval = QLA_FUNCTION_PARAMETER_ERROR; + else + rval = QLA_FUNCTION_FAILED; } else { ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1091, "Done %s.\n", __func__); @@ -2798,6 +2812,147 @@ qla2x00_system_error(scsi_qla_host_t *vha) 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 { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x118b, + "Done %s.\n", __func__); + } + + return rval; +} + /** * qla2x00_set_serdes_params() - * @ha: HA context @@ -2961,7 +3116,7 @@ qla2x00_enable_fce_trace(scsi_qla_host_t *vha, dma_addr_t fce_dma, "Entered %s.\n", __func__); if (!IS_QLA25XX(vha->hw) && !IS_QLA81XX(vha->hw) && - !IS_QLA83XX(vha->hw)) + !IS_QLA83XX(vha->hw) && !IS_QLA27XX(vha->hw)) return QLA_FUNCTION_FAILED; if (unlikely(pci_channel_offline(vha->hw->pdev))) @@ -3574,12 +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)); @@ -3592,26 +3749,23 @@ 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; - if (IS_QLA83XX(ha)) + if (IS_QLA83XX(ha) || IS_QLA27XX(ha)) mcp->mb[15] = 0; - reg = (struct device_reg_25xxmq __iomem *)((ha->mqiobase) + - QLA_QUE_PAGE * req->id); - mcp->mb[4] = req->id; /* que in ptr index */ mcp->mb[8] = 0; /* que out ptr index */ - mcp->mb[9] = 0; + mcp->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 = MBX_TOV_SECONDS * 2; - if (IS_QLA81XX(ha) || IS_QLA83XX(ha)) + if (IS_QLA81XX(ha) || IS_QLA83XX(ha) || IS_QLA27XX(ha)) mcp->in_mb |= MBX_1; - if (IS_QLA83XX(ha)) { + 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; @@ -3619,12 +3773,10 @@ qla25xx_init_req_que(struct scsi_qla_host *vha, struct req_que *req) spin_lock_irqsave(&ha->hardware_lock, flags); if (!(req->options & BIT_0)) { - WRT_REG_DWORD(®->req_q_in, 0); - if (!IS_QLA83XX(ha)) - 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); @@ -3646,12 +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)); @@ -3661,15 +3815,12 @@ 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; - if (IS_QLA83XX(ha)) + if (IS_QLA83XX(ha) || IS_QLA27XX(ha)) mcp->mb[15] = 0; - reg = (struct device_reg_25xxmq __iomem *)((ha->mqiobase) + - QLA_QUE_PAGE * rsp->id); - 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 @@ -3681,7 +3832,7 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp) if (IS_QLA81XX(ha)) { mcp->out_mb |= MBX_12|MBX_11|MBX_10; mcp->in_mb |= MBX_1; - } else if (IS_QLA83XX(ha)) { + } 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 */ @@ -3690,9 +3841,9 @@ qla25xx_init_rsp_que(struct scsi_qla_host *vha, struct rsp_que *rsp) spin_lock_irqsave(&ha->hardware_lock, flags); if (!(rsp->options & BIT_0)) { - WRT_REG_DWORD(®->rsp_q_out, 0); + WRT_REG_DWORD(rsp->rsp_q_out, 0); if (!IS_QLA83XX(ha)) - WRT_REG_DWORD(®->rsp_q_in, 0); + WRT_REG_DWORD(rsp->rsp_q_in, 0); } spin_unlock_irqrestore(&ha->hardware_lock, flags); @@ -3748,7 +3899,8 @@ qla81xx_fac_get_sector_size(scsi_qla_host_t *vha, uint32_t *sector_size) ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10dc, "Entered %s.\n", __func__); - if (!IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw)) + if (!IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw) && + !IS_QLA27XX(vha->hw)) return QLA_FUNCTION_FAILED; mcp->mb[0] = MBC_FLASH_ACCESS_CTRL; @@ -3779,7 +3931,8 @@ 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) && !IS_QLA83XX(vha->hw)) + if (!IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw) && + !IS_QLA27XX(vha->hw)) return QLA_FUNCTION_FAILED; ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10df, @@ -3813,7 +3966,8 @@ 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) && !IS_QLA83XX(vha->hw)) + if (!IS_QLA81XX(vha->hw) && !IS_QLA83XX(vha->hw) && + !IS_QLA27XX(vha->hw)) return QLA_FUNCTION_FAILED; ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10e2, @@ -3872,6 +4026,112 @@ qla81xx_restart_mpi_firmware(scsi_qla_host_t *vha) 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) { @@ -4378,7 +4638,7 @@ qla2x00_get_data_rate(scsi_qla_host_t *vha) mcp->mb[1] = 0; mcp->out_mb = MBX_1|MBX_0; mcp->in_mb = MBX_2|MBX_1|MBX_0; - if (IS_QLA83XX(ha)) + if (IS_QLA83XX(ha) || IS_QLA27XX(ha)) mcp->in_mb |= MBX_3; mcp->tov = MBX_TOV_SECONDS; mcp->flags = 0; @@ -4407,7 +4667,8 @@ qla81xx_get_port_config(scsi_qla_host_t *vha, uint16_t *mb) ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1109, "Entered %s.\n", __func__); - if (!IS_QLA81XX(ha) && !IS_QLA83XX(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; @@ -4512,40 +4773,43 @@ qla2x00_get_thermal_temp(scsi_qla_host_t *vha, uint16_t *temp) struct qla_hw_data *ha = vha->hw; uint8_t byte; - ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x10ca, - "Entered %s.\n", __func__); - - if (ha->thermal_support & THERMAL_SUPPORT_I2C) { - rval = qla2x00_read_sfp(vha, 0, &byte, - 0x98, 0x1, 1, BIT_13|BIT_12|BIT_0); - *temp = byte; - if (rval == QLA_SUCCESS) - goto done; - - ql_log(ql_log_warn, vha, 0x10c9, - "Thermal not supported through I2C bus, trying alternate " - "method (ISP access).\n"); - ha->thermal_support &= ~THERMAL_SUPPORT_I2C; + 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; } - if (ha->thermal_support & THERMAL_SUPPORT_ISP) { - rval = qla2x00_read_asic_temperature(vha, temp); - if (rval == QLA_SUCCESS) - goto done; - - ql_log(ql_log_warn, vha, 0x1019, - "Thermal not supported through ISP.\n"); - ha->thermal_support &= ~THERMAL_SUPPORT_ISP; + 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; } - ql_log(ql_log_warn, vha, 0x1150, - "Thermal not supported by this card " - "(ignoring further requests).\n"); - return rval; + 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; + } -done: - ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1018, - "Done %s.\n", __func__); + rval = qla2x00_read_asic_temperature(vha, temp); return rval; } @@ -4595,7 +4859,7 @@ qla82xx_mbx_intr_disable(scsi_qla_host_t *vha) ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x100d, "Entered %s.\n", __func__); - if (!IS_QLA82XX(ha)) + if (!IS_P3P_TYPE(ha)) return QLA_FUNCTION_FAILED; memset(mcp, 0, sizeof(mbx_cmd_t)); @@ -4713,6 +4977,60 @@ qla82xx_md_get_template(scsi_qla_host_t *vha) } 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; @@ -4808,7 +5126,7 @@ qla82xx_mbx_beacon_ctl(scsi_qla_host_t *vha, int enable) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_QLA82XX(ha)) + if (!IS_P3P_TYPE(ha)) return QLA_FUNCTION_FAILED; ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1127, @@ -4846,7 +5164,7 @@ qla83xx_wr_reg(scsi_qla_host_t *vha, uint32_t reg, uint32_t data) mbx_cmd_t mc; mbx_cmd_t *mcp = &mc; - if (!IS_QLA83XX(ha)) + if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha)) return QLA_FUNCTION_FAILED; ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1130, @@ -4921,7 +5239,7 @@ qla83xx_rd_reg(scsi_qla_host_t *vha, uint32_t reg, uint32_t *data) struct qla_hw_data *ha = vha->hw; unsigned long retry_max_time = jiffies + (2 * HZ); - if (!IS_QLA83XX(ha)) + if (!IS_QLA83XX(ha) && !IS_QLA27XX(ha)) return QLA_FUNCTION_FAILED; ql_dbg(ql_dbg_mbx, vha, 0x114b, "Entered %s.\n", __func__); diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c index f868a9f98af..89998244f48 100644 --- a/drivers/scsi/qla2xxx/qla_mid.c +++ b/drivers/scsi/qla2xxx/qla_mid.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2013 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -630,7 +630,7 @@ 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); @@ -699,6 +699,8 @@ 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, @@ -752,7 +754,7 @@ 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) { diff --git a/drivers/scsi/qla2xxx/qla_mr.c b/drivers/scsi/qla2xxx/qla_mr.c index d7993797f46..abeb3901498 100644 --- a/drivers/scsi/qla2xxx/qla_mr.c +++ b/drivers/scsi/qla2xxx/qla_mr.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2013 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -40,7 +40,7 @@ qlafx00_mailbox_command(scsi_qla_host_t *vha, struct mbx_cmd_32 *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; @@ -294,7 +294,7 @@ premature_exit: * Context: * Kernel context. */ -static int +int qlafx00_driver_shutdown(scsi_qla_host_t *vha, int tmo) { int rval; @@ -507,7 +507,7 @@ qlafx00_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)) + if (pci_is_pcie(ha->pdev)) pcie_set_readrq(ha->pdev, 2048); ha->chip_revision = ha->pdev->revision; @@ -527,21 +527,63 @@ qlafx00_soc_cpu_reset(scsi_qla_host_t *vha) 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)); - } - - /* Set all 4 core Clock gating control */ - for (i = 0; i < 4; i++) { 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, (0x11F0101)); + 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++) { @@ -564,20 +606,19 @@ qlafx00_soc_cpu_reset(scsi_qla_host_t *vha) QLAFX00_SET_HBA_SOC_REG(ha, SOC_FABRIC_CONTROL_REG, (0x2)); QLAFX00_SET_HBA_SOC_REG(ha, SOC_FABRIC_CONFIG_REG, (0x3)); - spin_lock_irqsave(&ha->hardware_lock, flags); - /* 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(); } - spin_unlock_irqrestore(&ha->hardware_lock, flags); } /** @@ -597,7 +638,6 @@ qlafx00_soft_reset(scsi_qla_host_t *vha) ha->isp_ops->disable_intrs(ha); qlafx00_soc_cpu_reset(vha); - ha->isp_ops->enable_intrs(ha); } /** @@ -631,20 +671,6 @@ qlafx00_config_rings(struct scsi_qla_host *vha) { struct qla_hw_data *ha = vha->hw; struct device_reg_fx00 __iomem *reg = &ha->iobase->ispfx00; - struct init_cb_fx *icb; - struct req_que *req = ha->req_q_map[0]; - struct rsp_que *rsp = ha->rsp_q_map[0]; - - /* Setup ring parameters in initialization control block. */ - icb = (struct init_cb_fx *)ha->init_cb; - icb->request_q_outpointer = __constant_cpu_to_le16(0); - icb->response_q_inpointer = __constant_cpu_to_le16(0); - icb->request_q_length = cpu_to_le16(req->length); - icb->response_q_length = cpu_to_le16(rsp->length); - icb->request_q_address[0] = cpu_to_le32(LSD(req->dma)); - icb->request_q_address[1] = cpu_to_le32(MSD(req->dma)); - icb->response_q_address[0] = cpu_to_le32(LSD(rsp->dma)); - icb->response_q_address[1] = cpu_to_le32(MSD(rsp->dma)); WRT_REG_DWORD(®->req_q_in, 0); WRT_REG_DWORD(®->req_q_out, 0); @@ -660,10 +686,8 @@ char * qlafx00_pci_info_str(struct scsi_qla_host *vha, char *str) { struct qla_hw_data *ha = vha->hw; - int pcie_reg; - pcie_reg = pci_find_capability(ha->pdev, PCI_CAP_ID_EXP); - if (pcie_reg) { + if (pci_is_pcie(ha->pdev)) { strcpy(str, "PCIe iSA"); return str; } @@ -701,78 +725,39 @@ qlafx00_disable_intrs(struct qla_hw_data *ha) spin_unlock_irqrestore(&ha->hardware_lock, flags); } -static void -qlafx00_tmf_iocb_timeout(void *data) -{ - srb_t *sp = (srb_t *)data; - struct srb_iocb *tmf = &sp->u.iocb_cmd; - - tmf->u.tmf.comp_status = cpu_to_le16((uint16_t)CS_TIMEOUT); - complete(&tmf->u.tmf.comp); -} - -static void -qlafx00_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); -} - -static int -qlafx00_async_tm_cmd(fc_port_t *fcport, uint32_t flags, - uint32_t lun, uint32_t tag) +int +qlafx00_abort_target(fc_port_t *fcport, unsigned int l, int tag) { - scsi_qla_host_t *vha = fcport->vha; - struct srb_iocb *tm_iocb; - srb_t *sp; - int rval = QLA_FUNCTION_FAILED; - - sp = qla2x00_get_sp(vha, fcport, GFP_KERNEL); - if (!sp) - goto 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 = qlafx00_tmf_sp_done; - tm_iocb->timeout = qlafx00_tmf_iocb_timeout; - init_completion(&tm_iocb->u.tmf.comp); - - rval = qla2x00_start_sp(sp); - if (rval != QLA_SUCCESS) - goto done_free_sp; - - ql_dbg(ql_dbg_async, vha, 0x507b, - "Task management command issued target_id=%x\n", - fcport->tgt_id); - - wait_for_completion(&tm_iocb->u.tmf.comp); - - rval = tm_iocb->u.tmf.comp_status == CS_COMPLETE ? - QLA_SUCCESS : QLA_FUNCTION_FAILED; - -done_free_sp: - sp->free(vha, sp); -done: - return rval; + return qla2x00_async_tm_cmd(fcport, TCF_TARGET_RESET, l, tag); } int -qlafx00_abort_target(fc_port_t *fcport, unsigned int l, int tag) +qlafx00_lun_reset(fc_port_t *fcport, unsigned int l, int tag) { - return qlafx00_async_tm_cmd(fcport, TCF_TARGET_RESET, l, tag); + return qla2x00_async_tm_cmd(fcport, TCF_LUN_RESET, l, tag); } int -qlafx00_lun_reset(fc_port_t *fcport, unsigned int l, int tag) +qlafx00_loop_reset(scsi_qla_host_t *vha) { - return qlafx00_async_tm_cmd(fcport, TCF_LUN_RESET, l, tag); + 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 @@ -918,12 +903,23 @@ qlafx00_init_fw_ready(scsi_qla_host_t *vha) 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 { @@ -965,6 +961,9 @@ qlafx00_init_fw_ready(scsi_qla_host_t *vha) 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 @@ -1349,21 +1348,22 @@ qlafx00_configure_devices(scsi_qla_host_t *vha) } static void -qlafx00_abort_isp_cleanup(scsi_qla_host_t *vha) +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->flags.chip_reset_done = 0; ha->mr.fw_hbt_en = 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 (!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); @@ -1383,12 +1383,19 @@ qlafx00_abort_isp_cleanup(scsi_qla_host_t *vha) } if (!ha->flags.eeh_busy) { - /* Requeue all commands in outstanding command list. */ - qla2x00_abort_all_cmds(vha, DID_RESET << 16); + 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); - set_bit(FX00_RESET_RECOVERY, &vha->dpc_flags); + 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); @@ -1475,6 +1482,7 @@ qlafx00_timer_routine(scsi_qla_host_t *vha) 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) @@ -1539,10 +1547,52 @@ qlafx00_timer_routine(scsi_qla_host_t *vha) } 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--; + } + } + } /* @@ -1570,7 +1620,7 @@ qlafx00_reset_initialize(scsi_qla_host_t *vha) if (vha->flags.online) { scsi_block_requests(vha->host); - qlafx00_abort_isp_cleanup(vha); + qlafx00_abort_isp_cleanup(vha, false); } ql_log(ql_log_info, vha, 0x0143, @@ -1602,7 +1652,15 @@ qlafx00_abort_isp(scsi_qla_host_t *vha) } scsi_block_requests(vha->host); - qlafx00_abort_isp_cleanup(vha); + 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, @@ -1688,6 +1746,15 @@ qlafx00_process_aen(struct scsi_qla_host *vha, struct qla_work_evt *evt) 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(), @@ -1779,10 +1846,11 @@ qlafx00_fx_disc(scsi_qla_host_t *vha, fc_port_t *fcport, uint16_t fx_type) p_sysid = utsname(); if (!p_sysid) { ql_log(ql_log_warn, vha, 0x303c, - "Not able to get the system informtion\n"); + "Not able to get the system information\n"); goto done_free_sp; } break; + case FXDISC_ABORT_IOCTL: default: break; } @@ -1804,6 +1872,8 @@ qlafx00_fx_disc(scsi_qla_host_t *vha, fc_port_t *fcport, uint16_t fx_type) 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, @@ -1864,8 +1934,8 @@ qlafx00_fx_disc(scsi_qla_host_t *vha, fc_port_t *fcport, uint16_t fx_type) if (fx_type == FXDISC_GET_CONFIG_INFO) { struct config_info_data *pinfo = (struct config_info_data *) fdisc->u.fxiocb.rsp_addr; - memcpy(&vha->hw->mr.product_name, pinfo->product_name, - sizeof(vha->hw->mr.product_name)); + 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, @@ -1879,6 +1949,11 @@ qlafx00_fx_disc(scsi_qla_host_t *vha, fc_port_t *fcport, uint16_t fx_type) 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; @@ -1904,7 +1979,12 @@ qlafx00_fx_disc(scsi_qla_host_t *vha, fc_port_t *fcport, uint16_t fx_type) 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: @@ -1922,90 +2002,6 @@ done: return rval; } -static void -qlafx00_abort_iocb_timeout(void *data) -{ - srb_t *sp = (srb_t *)data; - struct srb_iocb *abt = &sp->u.iocb_cmd; - - abt->u.abt.comp_status = cpu_to_le16((uint16_t)CS_TIMEOUT); - complete(&abt->u.abt.comp); -} - -static void -qlafx00_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 -qlafx00_async_abt_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, FXDISC_TIMEOUT); - abt_iocb->u.abt.cmd_hndl = cmd_sp->handle; - sp->done = qlafx00_abort_sp_done; - abt_iocb->timeout = qlafx00_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: - sp->free(vha, sp); -done: - return rval; -} - -int -qlafx00_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 < DEFAULT_OUTSTANDING_COMMANDS; handle++) { - if (req->outstanding_cmds[handle] == sp) - break; - } - spin_unlock_irqrestore(&ha->hardware_lock, flags); - if (handle == DEFAULT_OUTSTANDING_COMMANDS) { - /* Command not found. */ - return QLA_FUNCTION_FAILED; - } - return qlafx00_async_abt_cmd(sp); -} - /* * qlafx00_initialize_adapter * Initialize board. @@ -2021,6 +2017,7 @@ 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; @@ -2028,13 +2025,11 @@ qlafx00_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->thermal_support = 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; - vha->marker_needed = 0; ha->isp_abort_cnt = 0; ha->beacon_blink_led = 0; @@ -2072,6 +2067,11 @@ qlafx00_initialize_adapter(scsi_qla_host_t *vha) 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; } @@ -2233,8 +2233,7 @@ qlafx00_ioctl_iosb_entry(scsi_qla_host_t *vha, struct req_que *req, fstatus.ioctl_flags = pkt->fw_iotcl_flags; fstatus.ioctl_data = pkt->dataword_r; fstatus.adapid = pkt->adapid; - fstatus.adapid_hi = pkt->adapid_hi; - fstatus.reserved_2 = pkt->reserved_1; + fstatus.reserved_2 = pkt->dataword_r_extra; fstatus.res_count = pkt->residuallen; fstatus.status = pkt->status; fstatus.seq_number = pkt->seq_no; @@ -2325,7 +2324,6 @@ qlafx00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) /* Fast path completion. */ if (comp_status == CS_COMPLETE && scsi_status == 0) { - qla2x00_do_host_ramp_up(vha); qla2x00_process_completed_request(vha, req, handle); return; } @@ -2526,22 +2524,16 @@ check_scsi_status: 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=%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x 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", + "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[0], cp->cmnd[1], cp->cmnd[2], - cp->cmnd[3], cp->cmnd[4], cp->cmnd[5], cp->cmnd[6], - cp->cmnd[7], cp->cmnd[8], cp->cmnd[9], scsi_bufflen(cp), + lscsi_status, cp->cmnd, scsi_bufflen(cp), rsp_info_len, resid_len, fw_resid_len, sense_len, par_sense_len, rsp_info_len); - if (!res) - qla2x00_do_host_ramp_up(vha); - if (rsp->status_srb == NULL) sp->done(ha, sp, res); } @@ -2690,7 +2682,7 @@ qlafx00_error_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, srb_t *sp; struct qla_hw_data *ha = vha->hw; const char func[] = "ERROR-IOCB"; - uint16_t que = MSW(pkt->handle); + uint16_t que = 0; struct req_que *req = NULL; int res = DID_ERROR << 16; @@ -2719,19 +2711,22 @@ qlafx00_process_response_queue(struct scsi_qla_host *vha, { struct sts_entry_fx00 *pkt; response_t *lptr; + uint16_t lreq_q_in = 0; + uint16_t lreq_q_out = 0; - if (!vha->flags.online) - return; + lreq_q_in = RD_REG_DWORD(rsp->rsp_q_in); + lreq_q_out = rsp->ring_index; - while (RD_REG_DWORD((void __iomem *)&(rsp->ring_ptr->signature)) != - RESPONSE_PROCESSED) { + 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 { @@ -2743,7 +2738,6 @@ qlafx00_process_response_queue(struct scsi_qla_host *vha, qlafx00_error_entry(vha, rsp, (struct sts_entry_fx00 *)pkt, pkt->entry_status, pkt->entry_type); - goto next_iter; continue; } @@ -2777,10 +2771,6 @@ qlafx00_process_response_queue(struct scsi_qla_host *vha, pkt->entry_type, pkt->entry_status); break; } -next_iter: - WRT_REG_DWORD((void __iomem *)&lptr->signature, - RESPONSE_PROCESSED); - wmb(); } /* Adjust ring index */ @@ -2815,15 +2805,37 @@ qlafx00_async_event(scsi_qla_host_t *vha) break; case QLAFX00_MBA_PORT_UPDATE: /* Port database update */ - ha->aenmb[1] = RD_REG_WORD(®->aenmailbox1); - ha->aenmb[2] = RD_REG_WORD(®->aenmailbox2); - ha->aenmb[3] = RD_REG_WORD(®->aenmailbox3); + 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); @@ -2852,7 +2864,7 @@ static void qlafx00_mbx_completion(scsi_qla_host_t *vha, uint32_t mb0) { uint16_t cnt; - uint16_t __iomem *wptr; + uint32_t __iomem *wptr; struct qla_hw_data *ha = vha->hw; struct device_reg_fx00 __iomem *reg = &ha->iobase->ispfx00; @@ -2862,10 +2874,10 @@ qlafx00_mbx_completion(scsi_qla_host_t *vha, uint32_t mb0) /* Load return mailbox registers. */ ha->flags.mbox_int = 1; ha->mailbox_out32[0] = mb0; - wptr = (uint16_t __iomem *)®->mailbox17; + wptr = (uint32_t __iomem *)®->mailbox17; for (cnt = 1; cnt < ha->mbx_count; cnt++) { - ha->mailbox_out32[cnt] = RD_REG_WORD(wptr); + ha->mailbox_out32[cnt] = RD_REG_DWORD(wptr); wptr++; } } @@ -2892,6 +2904,7 @@ qlafx00_intr_handler(int irq, void *dev_id) 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) { @@ -2911,34 +2924,28 @@ qlafx00_intr_handler(int irq, void *dev_id) vha = pci_get_drvdata(ha->pdev); for (iter = 50; iter--; clr_intr = 0) { stat = QLAFX00_RD_INTR_REG(ha); - if ((stat & QLAFX00_HST_INT_STS_BITS) == 0) + if (qla2x00_check_reg_for_disconnect(vha, stat)) + break; + intr_stat = stat & QLAFX00_HST_INT_STS_BITS; + if (!intr_stat) break; - switch (stat & QLAFX00_HST_INT_STS_BITS) { - case QLAFX00_INTR_MB_CMPLT: - case QLAFX00_INTR_MB_RSP_CMPLT: - case QLAFX00_INTR_MB_ASYNC_CMPLT: - case QLAFX00_INTR_ALL_CMPLT: + 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; - break; - case QLAFX00_INTR_ASYNC_CMPLT: - case QLAFX00_INTR_RSP_ASYNC_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; - break; - case QLAFX00_INTR_RSP_CMPLT: + } + if (intr_stat & QLAFX00_INTR_RSP_CMPLT) { qlafx00_process_response_queue(vha, rsp); clr_intr |= QLAFX00_INTR_RSP_CMPLT; - break; - default: - ql_dbg(ql_dbg_async, vha, 0x507a, - "Unrecognized interrupt type (%d).\n", stat); - break; } + QLAFX00_CLR_INTR_REG(ha, clr_intr); QLAFX00_RD_INTR_REG(ha); } @@ -3088,17 +3095,6 @@ qlafx00_start_scsi(srb_t *sp) /* So we know we haven't pci_map'ed anything yet */ tot_dsds = 0; - /* Forcing marker needed for now */ - vha->marker_needed = 0; - - /* Send marker if required */ - if (vha->marker_needed != 0) { - if (qla2x00_marker(vha, req, rsp, 0, 0, MK_SYNC_ALL) != - QLA_SUCCESS) - return QLA_FUNCTION_FAILED; - vha->marker_needed = 0; - } - /* Acquire ring specific lock */ spin_lock_irqsave(&ha->hardware_lock, flags); @@ -3149,7 +3145,9 @@ qlafx00_start_scsi(srb_t *sp) memset(&lcmd_pkt, 0, REQUEST_ENTRY_SIZE); lcmd_pkt.handle = MAKE_HANDLE(req->id, sp->handle); - lcmd_pkt.handle_hi = 0; + 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); @@ -3229,8 +3227,7 @@ qlafx00_tm_iocb(srb_t *sp, struct tsk_mgmt_entry_fx00 *ptm_iocb) 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.handle_hi = 0; - tm_iocb.timeout = cpu_to_le16(qla2x00_get_async_timeout(vha) + 2); + 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)) { @@ -3469,7 +3466,7 @@ qlafx00_fxdisc_iocb(srb_t *sp, struct fxdisc_entry_fx00 *pfxiocb) sp->fcport->vha, 0x3047, (uint8_t *)&fx_iocb, sizeof(struct fxdisc_entry_fx00)); - memcpy((void *)pfxiocb, &fx_iocb, + 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 index 1a092af0e2c..aeaa1b40b1f 100644 --- a/drivers/scsi/qla2xxx/qla_mr.h +++ b/drivers/scsi/qla2xxx/qla_mr.h @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2013 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -22,13 +22,16 @@ struct cmd_type_7_fx00 { uint8_t entry_status; /* Entry Status. */ uint32_t handle; /* System handle. */ - uint32_t handle_hi; + 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. */ - uint16_t scsi_rsp_dsd_len; + uint8_t scsi_rsp_dsd_len; + uint8_t reserved_2; struct scsi_lun lun; /* LUN (LE). */ @@ -47,30 +50,6 @@ struct cmd_type_7_fx00 { uint32_t dseg_0_len; /* Data segment 0 length. */ }; -/* - * ISP queue - marker entry structure definition. - */ -struct mrk_entry_fx00 { - uint8_t entry_type; /* Entry type. */ - uint8_t entry_count; /* Entry count. */ - uint8_t handle_count; /* Handle count. */ - uint8_t entry_status; /* Entry Status. */ - - uint32_t handle; /* System handle. */ - uint32_t handle_hi; /* System handle. */ - - uint16_t tgt_id; /* Target ID. */ - - uint8_t modifier; /* Modifier (7-0). */ - uint8_t reserved_1; - - uint8_t reserved_2[5]; - - uint8_t lun[8]; /* FCP LUN (BE). */ - uint8_t reserved_3[36]; -}; - - #define STATUS_TYPE_FX00 0x01 /* Status entry. */ struct sts_entry_fx00 { uint8_t entry_type; /* Entry type. */ @@ -79,7 +58,7 @@ struct sts_entry_fx00 { uint8_t entry_status; /* Entry Status. */ uint32_t handle; /* System handle. */ - uint32_t handle_hi; /* System handle. */ + uint32_t reserved_3; /* System handle. */ __le16 comp_status; /* Completion status. */ uint16_t reserved_0; /* OX_ID used by the firmware. */ @@ -102,7 +81,7 @@ struct sts_entry_fx00 { struct multi_sts_entry_fx00 { uint8_t entry_type; /* Entry type. */ - uint8_t sys_define; /* System defined. */ + uint8_t entry_count; /* Entry count. */ uint8_t handle_count; uint8_t entry_status; @@ -118,15 +97,13 @@ struct tsk_mgmt_entry_fx00 { __le32 handle; /* System handle. */ - uint32_t handle_hi; /* System handle. */ + uint32_t reserved_0; __le16 tgt_id; /* Target Idx. */ uint16_t reserved_1; - - uint16_t delay; /* Activity delay in seconds. */ - - __le16 timeout; /* Command timeout. */ + uint16_t reserved_3; + uint16_t reserved_4; struct scsi_lun lun; /* LUN (LE). */ @@ -144,13 +121,13 @@ struct abort_iocb_entry_fx00 { uint8_t entry_status; /* Entry Status. */ __le32 handle; /* System handle. */ - __le32 handle_hi; /* System handle. */ + __le32 reserved_0; __le16 tgt_id_sts; /* Completion status. */ __le16 options; __le32 abort_handle; /* System handle. */ - __le32 abort_handle_hi; /* System handle. */ + __le32 reserved_2; __le16 req_que_no; uint8_t reserved_1[38]; @@ -171,8 +148,7 @@ struct ioctl_iocb_entry_fx00 { __le32 dataword_r; /* Data word returned */ uint32_t adapid; /* Adapter ID */ - uint32_t adapid_hi; /* Adapter ID high */ - uint32_t reserved_1; + uint32_t dataword_r_extra; __le32 seq_no; uint8_t reserved_2[20]; @@ -304,7 +280,9 @@ struct register_host_info { #define QLAFX00_TGT_NODE_LIST_SIZE (sizeof(uint32_t) * 32) struct config_info_data { - uint8_t product_name[256]; + 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]; @@ -329,11 +307,13 @@ struct config_info_data { uint64_t adapter_id; uint32_t cluster_key_len; - uint8_t cluster_key[10]; + 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 @@ -341,11 +321,13 @@ struct config_info_data { #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 0x21B08 +#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 @@ -354,13 +336,12 @@ struct config_info_data { #define QLAFX00_INTR_MB_CMPLT 0x1 #define QLAFX00_INTR_RSP_CMPLT 0x2 -#define QLAFX00_INTR_MB_RSP_CMPLT 0x3 #define QLAFX00_INTR_ASYNC_CMPLT 0x4 -#define QLAFX00_INTR_MB_ASYNC_CMPLT 0x5 -#define QLAFX00_INTR_RSP_ASYNC_CMPLT 0x6 -#define QLAFX00_INTR_ALL_CMPLT 0x7 #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 @@ -370,6 +351,7 @@ struct config_info_data { #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 @@ -434,9 +416,11 @@ struct qla_mt_iocb_rqst_fx00 { __le32 dataword_extra; - __le32 req_len; + __le16 req_len; + __le16 reserved_2; - __le32 rsp_len; + __le16 rsp_len; + __le16 reserved_3; }; struct qla_mt_iocb_rsp_fx00 { @@ -482,7 +466,6 @@ struct qla_mt_iocb_rsp_fx00 { #define FX00_DEF_RATOV 10 struct mr_data_fx00 { - uint8_t product_name[256]; uint8_t symbolic_name[64]; uint8_t serial_num[32]; uint8_t hw_version[16]; @@ -499,12 +482,46 @@ struct mr_data_fx00 { 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 cce0cd0d7ec..58f3c912d96 100644 --- a/drivers/scsi/qla2xxx/qla_nx.c +++ b/drivers/scsi/qla2xxx/qla_nx.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2013 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -857,20 +857,21 @@ qla82xx_rom_lock(struct qla_hw_data *ha) break; if (timeout >= qla82xx_rom_lock_timeout) { lock_owner = qla82xx_rd_32(ha, QLA82XX_ROM_LOCK_ID); - ql_dbg(ql_dbg_p3p, vha, 0xb085, - "Failed to acquire rom lock, acquired by %d.\n", - lock_owner); + 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)); } @@ -954,6 +955,7 @@ 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)) { @@ -962,8 +964,10 @@ qla82xx_rom_fast_read(struct qla_hw_data *ha, int addr, int *valp) loops++; } if (loops >= 50000) { + lock_owner = qla82xx_rd_32(ha, QLA82XX_ROM_LOCK_ID); ql_log(ql_log_fatal, vha, 0x00b9, - "Failed to acquire SEM2 lock.\n"); + "Failed to acquire SEM2 lock, Lock Owner %u.\n", + lock_owner); return -1; } ret = qla82xx_do_rom_fast_read(ha, addr, valp); @@ -1061,6 +1065,7 @@ 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)) { @@ -1069,8 +1074,9 @@ ql82xx_rom_lock_d(struct qla_hw_data *ha) loops++; } if (loops >= 50000) { + lock_owner = qla82xx_rd_32(ha, QLA82XX_ROM_LOCK_ID); ql_log(ql_log_warn, vha, 0xb010, - "ROM lock failed.\n"); + "ROM lock failed, Lock Owner %u.\n", lock_owner); return -1; } return 0; @@ -1666,8 +1672,14 @@ qla82xx_iospace_config(struct qla_hw_data *ha) } /* 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 = @@ -1967,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; @@ -2075,13 +2087,6 @@ qla82xx_intr_handler(int irq, void *dev_id) WRT_REG_DWORD(®->host_int, 0); } -#ifdef QL_DEBUG_LEVEL_17 - if (!irq && ha->flags.eeh_busy) - ql_log(ql_log_warn, vha, 0x503d, - "isr:status %x, cmd_flags %lx, mbox_int %x, stat %x.\n", - status, ha->mbx_cmd_flags, ha->flags.mbox_int, stat); -#endif - qla2x00_handle_mbx_completion(ha, status); spin_unlock_irqrestore(&ha->hardware_lock, flags); @@ -2101,6 +2106,7 @@ qla82xx_msix_default(int irq, void *dev_id) int status = 0; unsigned long flags; uint32_t stat = 0; + uint32_t host_int = 0; uint16_t mb[4]; rsp = (struct rsp_que *) dev_id; @@ -2116,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) { @@ -2147,13 +2156,6 @@ qla82xx_msix_default(int irq, void *dev_id) WRT_REG_DWORD(®->host_int, 0); } while (0); -#ifdef QL_DEBUG_LEVEL_17 - if (!irq && ha->flags.eeh_busy) - ql_log(ql_log_warn, vha, 0x5044, - "isr:status %x, cmd_flags %lx, mbox_int %x, stat %x.\n", - status, ha->mbx_cmd_flags, ha->flags.mbox_int, stat); -#endif - qla2x00_handle_mbx_completion(ha, status); spin_unlock_irqrestore(&ha->hardware_lock, flags); @@ -2168,6 +2170,7 @@ qla82xx_msix_rsp_q(int irq, void *dev_id) 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) { @@ -2180,8 +2183,12 @@ qla82xx_msix_rsp_q(int irq, void *dev_id) reg = &ha->iobase->isp82; 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); +out: spin_unlock_irqrestore(&ha->hardware_lock, flags); return IRQ_HANDLED; } @@ -2195,6 +2202,7 @@ 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; @@ -2210,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: @@ -2236,8 +2247,9 @@ qla82xx_poll(int irq, void *dev_id) stat * 0xff); break; } + WRT_REG_DWORD(®->host_int, 0); } - WRT_REG_DWORD(®->host_int, 0); +out: spin_unlock_irqrestore(&ha->hardware_lock, flags); } @@ -2247,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; } @@ -2258,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; } @@ -2803,12 +2821,14 @@ static void qla82xx_rom_lock_recovery(struct qla_hw_data *ha) { scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); + uint32_t lock_owner = 0; - if (qla82xx_rom_lock(ha)) + 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.\n"); - + "Resetting rom_lock, Lock Owner %u.\n", lock_owner); + } /* * Either we got the lock, or someone * else died while holding it. @@ -2832,47 +2852,30 @@ 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, - QLA8XXX_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 */ ql_log(ql_log_info, vha, 0x009e, "HW State: INITIALIZING.\n"); @@ -3008,6 +3011,9 @@ qla8xxx_dev_failed_handler(scsi_qla_host_t *vha) 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 */ @@ -3131,18 +3137,18 @@ qla82xx_check_md_needed(scsi_qla_host_t *vha) if (ql2xmdenable) { if (!ha->fw_dumped) { - if (fw_major_version != ha->fw_major_version || + if ((fw_major_version != ha->fw_major_version || fw_minor_version != ha->fw_minor_version || - fw_subminor_version != ha->fw_subminor_version) { - ql_log(ql_log_info, vha, 0xb02d, - "Firmware version differs " - "Previous version: %d:%d:%d - " - "New version: %d:%d:%d\n", + 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->fw_subminor_version, + ha->prev_minidump_failed); /* Release MiniDump resources */ qla82xx_md_free(vha); /* ALlocate MiniDump resources */ @@ -3330,6 +3336,14 @@ static int qla82xx_check_temp(scsi_qla_host_t *vha) 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; @@ -3423,8 +3437,18 @@ 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; } @@ -3432,17 +3456,25 @@ void qla82xx_set_reset_owner(scsi_qla_host_t *vha) { struct qla_hw_data *ha = vha->hw; - uint32_t dev_state; + 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); - dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); if (dev_state == QLA8XXX_DEV_READY) { ql_log(ql_log_info, vha, 0xb02f, "HW State: NEED RESET\n"); - 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); + 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", @@ -3463,7 +3495,7 @@ qla82xx_set_reset_owner(scsi_qla_host_t *vha) int qla82xx_abort_isp(scsi_qla_host_t *vha) { - int rval; + int rval = -1; struct qla_hw_data *ha = vha->hw; if (vha->device_flags & DFLG_DEV_FAILED) { @@ -3477,7 +3509,15 @@ qla82xx_abort_isp(scsi_qla_host_t *vha) 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); @@ -3597,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; @@ -3608,7 +3648,11 @@ 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; qla82xx_clear_pending_mbx(vha); break; @@ -3633,8 +3677,10 @@ qla82xx_chip_reset_cleanup(scsi_qla_host_t *vha) for (cnt = 1; cnt < req->num_outstanding_cmds; cnt++) { sp = req->outstanding_cmds[cnt]; if (sp) { - if (!sp->u.scmd.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)) { @@ -4072,7 +4118,7 @@ qla82xx_minidump_process_rdmem(scsi_qla_host_t *vha, return QLA_SUCCESS; } -static int +int qla82xx_validate_template_chksum(scsi_qla_host_t *vha) { struct qla_hw_data *ha = vha->hw; @@ -4384,7 +4430,11 @@ qla82xx_md_prep(scsi_qla_host_t *vha) ha->md_template_size / 1024); /* Get Minidump template */ - rval = qla82xx_md_get_template(vha); + 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"); @@ -4449,3 +4499,20 @@ 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 d268e8406fd..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-2013 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -333,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) @@ -589,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 @@ -954,6 +952,11 @@ struct ct6_dsd { #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. @@ -1180,6 +1183,7 @@ static const int MD_MIU_TEST_AGT_RDDATA[] = { 0x410000A8, 0x410000AC, #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)) @@ -1191,4 +1195,8 @@ enum { 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 3e21e9fc9d9..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-2013 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -110,7 +110,8 @@ MODULE_PARM_DESC(ql2xfdmienable, "Enables FDMI registrations. " "0 - no FDMI. Default is 1 - perform FDMI."); -int ql2xmaxqdepth = MAX_Q_DEPTH; +#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 set for each LUN. " @@ -119,15 +120,17 @@ MODULE_PARM_DESC(ql2xmaxqdepth, 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" - ", 2 - Enable DIF for all types, except Type 0."); + " 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 = 2; module_param(ql2xenablehba_err_chk, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(ql2xenablehba_err_chk, " Enable T10-CRC-DIF Error isolation by HBA:\n" - " Default is 1.\n" + " 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"); @@ -494,18 +497,14 @@ 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_pcie_cap(ha->pdev); - 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 += PCI_EXP_LNKCAP; - 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 ("); switch (lspeed) { @@ -617,7 +616,7 @@ qla2x00_sp_free_dma(void *vha, void *ptr) if (sp->flags & SRB_CRC_CTX_DSD_VALID) { /* List assured to be having elements */ - qla2x00_clean_dsd_pool(ha, sp); + qla2x00_clean_dsd_pool(ha, sp, NULL); sp->flags &= ~SRB_CRC_CTX_DSD_VALID; } @@ -732,10 +731,8 @@ qla2xxx_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) } sp = qla2x00_get_sp(vha, fcport, GFP_ATOMIC); - if (!sp) { - set_bit(HOST_RAMP_DOWN_QUEUE_DEPTH, &vha->dpc_flags); + if (!sp) goto qc24_host_busy; - } sp->u.scmd.cmd = cmd; sp->type = SRB_SCSI_CMD; @@ -748,7 +745,6 @@ qla2xxx_queuecommand(struct Scsi_Host *host, struct scsi_cmnd *cmd) 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); - set_bit(HOST_RAMP_DOWN_QUEUE_DEPTH, &vha->dpc_flags); goto qc24_host_busy_free_sp; } @@ -785,7 +781,7 @@ 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; @@ -848,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 @@ -861,35 +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; - - ql_dbg(ql_dbg_taskm, vha, 0x8019, - "%s return status=%d.\n", __func__, return_status); - - return return_status; } int @@ -949,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)) @@ -978,10 +951,20 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) sp_get(sp); spin_unlock_irqrestore(&ha->hardware_lock, flags); - if (ha->isp_ops->abort_command(sp)) { - 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.\n", cmd); + "Abort command mbx failed cmd=%p, rval=%x.\n", cmd, rval); } else { ql_dbg(ql_dbg_taskm, vha, 0x8004, "Abort command mbx success cmd=%p.\n", cmd); @@ -989,6 +972,12 @@ qla2xxx_eh_abort(struct scsi_cmnd *cmd) } 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); @@ -1240,14 +1229,18 @@ qla2xxx_eh_host_reset(struct scsi_cmnd *cmd) ql_log(ql_log_info, vha, 0x8018, "ADAPTER RESET ISSUED nexus=%ld:%d:%d.\n", vha->host_no, id, lun); - if (qla2x00_wait_for_reset_ready(vha) != QLA_SUCCESS) + /* + * No point in issuing another reset if one is active. Also do not + * attempt a reset if we are updating flash. + */ + 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; @@ -1303,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) @@ -1311,14 +1308,12 @@ qla2x00_loop_reset(scsi_qla_host_t *vha) ret = ha->isp_ops->target_reset(fcport, 0, 0); if (ret != QLA_SUCCESS) { ql_dbg(ql_dbg_taskm, vha, 0x802c, - "Bus Reset failed: Target Reset=%d " + "Bus Reset failed: Reset=%d " "d_id=%x.\n", ret, fcport->d_id.b24); } } } - if (IS_QLAFX00(ha)) - return QLA_SUCCESS; if (ha->flags.enable_lip_full_login && !IS_CNA_CAPABLE(ha)) { atomic_set(&vha->loop_state, LOOP_DOWN); @@ -1476,81 +1471,6 @@ qla2x00_change_queue_type(struct scsi_device *sdev, int tag_type) return tag_type; } -static void -qla2x00_host_ramp_down_queuedepth(scsi_qla_host_t *vha) -{ - scsi_qla_host_t *vp; - struct Scsi_Host *shost; - struct scsi_device *sdev; - struct qla_hw_data *ha = vha->hw; - unsigned long flags; - - ha->host_last_rampdown_time = jiffies; - - if (ha->cfg_lun_q_depth <= vha->host->cmd_per_lun) - return; - - if ((ha->cfg_lun_q_depth / 2) < vha->host->cmd_per_lun) - ha->cfg_lun_q_depth = vha->host->cmd_per_lun; - else - ha->cfg_lun_q_depth = ha->cfg_lun_q_depth / 2; - - /* - * Geometrically ramp down the queue depth for all devices on this - * adapter - */ - spin_lock_irqsave(&ha->vport_slock, flags); - list_for_each_entry(vp, &ha->vp_list, list) { - shost = vp->host; - shost_for_each_device(sdev, shost) { - if (sdev->queue_depth > shost->cmd_per_lun) { - if (sdev->queue_depth < ha->cfg_lun_q_depth) - continue; - ql_log(ql_log_warn, vp, 0x3031, - "%ld:%d:%d: Ramping down queue depth to %d", - vp->host_no, sdev->id, sdev->lun, - ha->cfg_lun_q_depth); - qla2x00_change_queue_depth(sdev, - ha->cfg_lun_q_depth, SCSI_QDEPTH_DEFAULT); - } - } - } - spin_unlock_irqrestore(&ha->vport_slock, flags); - - return; -} - -static void -qla2x00_host_ramp_up_queuedepth(scsi_qla_host_t *vha) -{ - scsi_qla_host_t *vp; - struct Scsi_Host *shost; - struct scsi_device *sdev; - struct qla_hw_data *ha = vha->hw; - unsigned long flags; - - ha->host_last_rampup_time = jiffies; - ha->cfg_lun_q_depth++; - - /* - * Linearly ramp up the queue depth for all devices on this - * adapter - */ - spin_lock_irqsave(&ha->vport_slock, flags); - list_for_each_entry(vp, &ha->vp_list, list) { - shost = vp->host; - shost_for_each_device(sdev, shost) { - if (sdev->queue_depth > ha->cfg_lun_q_depth) - continue; - qla2x00_change_queue_depth(sdev, ha->cfg_lun_q_depth, - SCSI_QDEPTH_RAMP_UP); - } - } - spin_unlock_irqrestore(&ha->vport_slock, flags); - - return; -} - /** * qla2x00_config_dma_addressing() - Configure OS DMA addressing method. * @ha: HA context @@ -1911,7 +1831,7 @@ 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, + .iospace_config = qla2x00_iospace_config, .initialize_adapter = qla2x00_initialize_adapter, }; @@ -1949,7 +1869,7 @@ 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, + .iospace_config = qla2x00_iospace_config, .initialize_adapter = qla2x00_initialize_adapter, }; @@ -1987,7 +1907,7 @@ 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, + .iospace_config = qla2x00_iospace_config, .initialize_adapter = qla2x00_initialize_adapter, }; @@ -2025,7 +1945,7 @@ static struct isp_operations qla81xx_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, + .iospace_config = qla2x00_iospace_config, .initialize_adapter = qla2x00_initialize_adapter, }; @@ -2054,19 +1974,57 @@ 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, + .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, @@ -2119,7 +2077,7 @@ static struct isp_operations qlafx00_isp_ops = { .intr_handler = qlafx00_intr_handler, .enable_intrs = qlafx00_enable_intrs, .disable_intrs = qlafx00_disable_intrs, - .abort_command = qlafx00_abort_command, + .abort_command = qla24xx_async_abort_command, .target_reset = qlafx00_abort_target, .lun_reset = qlafx00_lun_reset, .fabric_login = NULL, @@ -2143,6 +2101,44 @@ static struct isp_operations qlafx00_isp_ops = { .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 qla2x00_set_isp_flags(struct qla_hw_data *ha) { @@ -2237,6 +2233,14 @@ 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; @@ -2256,21 +2260,36 @@ qla2x00_set_isp_flags(struct qla_hw_data *ha) 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 (IS_QLA27XX(ha)) + ha->port_no--; + else + ha->port_no = !(ha->port_no & 1); + } - if (ha->port_no & 1) - ha->flags.port0 = 1; - else - ha->flags.port0 = 0; ql_dbg_pci(ql_dbg_init, ha->pdev, 0x000b, "device_type=0x%x port=%d fw_srisc_address=0x%x.\n", - ha->device_type, ha->flags.port0, ha->fw_srisc_address); + ha->device_type, ha->port_no, ha->fw_srisc_address); } static void @@ -2317,7 +2336,6 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) 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 || @@ -2330,7 +2348,10 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) 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_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, @@ -2367,20 +2388,20 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) 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) || - IS_QLA83XX(ha)) + IS_QLA83XX(ha) || IS_QLA27XX(ha)) pdev->needs_freset = 1; ha->prev_topology = 0; ha->init_cb_size = sizeof(init_cb_t); ha->link_data_rate = PORT_SPEED_UNKNOWN; ha->optrom_size = OPTROM_SIZE_2300; - ha->cfg_lun_q_depth = ql2xmaxqdepth; /* Assign ISP specific operations. */ if (IS_QLA2100(ha)) { @@ -2484,6 +2505,21 @@ 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; @@ -2507,12 +2543,30 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->aen_mbx_count = AEN_MAILBOX_REGISTER_COUNT_FX00; req_length = REQUEST_ENTRY_CNT_FX00; rsp_length = RESPONSE_ENTRY_CNT_FX00; - ha->init_cb_size = sizeof(struct init_cb_fx); 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, @@ -2552,7 +2606,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->flags.enable_64bit_addressing ? "enable" : "disable"); ret = qla2x00_mem_alloc(ha, req_length, rsp_length, &req, &rsp); - if (!ret) { + if (ret) { ql_log_pci(ql_log_fatal, pdev, 0x0031, "Failed to allocate memory for adapter, aborting.\n"); @@ -2577,10 +2631,6 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) host = base_vha->host; base_vha->req = req; - if (IS_QLAFX00(ha)) - host->can_queue = 1024; - else - host->can_queue = req->length + 128; if (IS_QLA2XXX_MIDTYPE(ha)) base_vha->mgmt_svr_loop_id = 10 + base_vha->vp_idx; else @@ -2603,11 +2653,6 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) if (!IS_QLA82XX(ha)) host->sg_tablesize = QLA_SG_ALL; } - ql_dbg(ql_dbg_init, base_vha, 0x0032, - "can_queue=%d, req=%p, " - "mgmt_svr_loop_id=%d, sg_tablesize=%d.\n", - host->can_queue, base_vha->req, - base_vha->mgmt_svr_loop_id, host->sg_tablesize); host->max_id = ha->max_fibre_devices; host->cmd_per_lun = 3; host->unique_id = host->host_no; @@ -2662,7 +2707,7 @@ que_init: req->req_q_out = &ha->iobase->isp24.req_q_out; rsp->rsp_q_in = &ha->iobase->isp24.rsp_q_in; rsp->rsp_q_out = &ha->iobase->isp24.rsp_q_out; - if (ha->mqenable || IS_QLA83XX(ha)) { + 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; @@ -2676,7 +2721,7 @@ que_init: rsp->rsp_q_out = &ha->iobase->ispfx00.rsp_q_out; } - if (IS_QLA82XX(ha)) { + 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]; @@ -2709,12 +2754,30 @@ que_init: qla82xx_idc_unlock(ha); 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)) { ql_log(ql_log_warn, base_vha, 0x00ec, @@ -2748,6 +2811,8 @@ que_init: */ 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); @@ -2804,12 +2869,20 @@ skip_dpc: 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; ql_dbg(ql_dbg_init, base_vha, 0x00f2, "Init done and hba is online.\n"); @@ -2824,9 +2897,6 @@ skip_dpc: if (IS_QLAFX00(ha)) { ret = qlafx00_fx_disc(base_vha, - &base_vha->hw->mr.fcport, FXDISC_GET_CONFIG_INFO); - - ret = qlafx00_fx_disc(base_vha, &base_vha->hw->mr.fcport, FXDISC_GET_PORT_INFO); /* Register system information */ @@ -2881,12 +2951,17 @@ probe_hw_failed: qla82xx_clear_drv_active(ha); qla82xx_idc_unlock(ha); } + if (IS_QLA8044(ha)) { + qla8044_idc_lock(ha); + qla8044_clear_drv_active(ha); + qla8044_idc_unlock(ha); + } iospace_config_failed: - if (IS_QLA82XX(ha)) { + if (IS_P3P_TYPE(ha)) { if (!ha->nx_pcibase) - iounmap((device_reg_t __iomem *)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); @@ -2903,22 +2978,6 @@ probe_out: } static void -qla2x00_stop_dpc_thread(scsi_qla_host_t *vha) -{ - struct qla_hw_data *ha = vha->hw; - struct task_struct *t = ha->dpc_thread; - - if (ha->dpc_thread == NULL) - return; - /* - * qla2xxx_wake_dpc checks for ->dpc_thread - * so we need to zero it out. - */ - ha->dpc_thread = NULL; - kthread_stop(t); -} - -static void qla2x00_shutdown(struct pci_dev *pdev) { scsi_qla_host_t *vha; @@ -2930,6 +2989,10 @@ qla2x00_shutdown(struct pci_dev *pdev) 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); @@ -2957,26 +3020,14 @@ 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; - /* - * 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; - - ha->flags.host_shutting_down = 1; - - set_bit(UNLOADING, &base_vha->dpc_flags); mutex_lock(&ha->vport_lock); while (ha->cur_vport_count) { spin_lock_irqsave(&ha->vport_slock, flags); @@ -2984,7 +3035,7 @@ qla2x00_remove_one(struct pci_dev *pdev) BUG_ON(base_vha->list.next == &ha->vp_list); /* This assumes first entry in ha->vp_list is always base vha */ vha = list_first_entry(&base_vha->list, scsi_qla_host_t, list); - scsi_host_get(vha->host); + scsi_host = scsi_host_get(vha->host); spin_unlock_irqrestore(&ha->vport_slock, flags); mutex_unlock(&ha->vport_lock); @@ -2995,27 +3046,12 @@ qla2x00_remove_one(struct pci_dev *pdev) mutex_lock(&ha->vport_lock); } mutex_unlock(&ha->vport_lock); +} - 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; - +/* 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); @@ -3049,26 +3085,16 @@ qla2x00_remove_one(struct pci_dev *pdev) ha->dpc_thread = NULL; kthread_stop(t); } - qlt_remove_target(ha, base_vha); - - qla2x00_free_sysfs_attr(base_vha); - - fc_remove_host(base_vha->host); - - scsi_remove_host(base_vha->host); - - qla2x00_free_device(base_vha); - - scsi_host_put(base_vha->host); +} +static void +qla2x00_unmap_iobases(struct qla_hw_data *ha) +{ 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); + 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); @@ -3079,9 +3105,89 @@ qla2x00_remove_one(struct pci_dev *pdev) if (ha->mqiobase) iounmap(ha->mqiobase); - if (IS_QLA83XX(ha) && ha->msixbase) + if ((IS_QLA83XX(ha) || IS_QLA27XX(ha)) && ha->msixbase) iounmap(ha->msixbase); } +} + +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); + } +} + +static void +qla2x00_remove_one(struct pci_dev *pdev) +{ + scsi_qla_host_t *base_vha; + struct qla_hw_data *ha; + + /* + * 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); @@ -3090,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 @@ -3104,9 +3209,8 @@ qla2x00_free_device(scsi_qla_host_t *vha) if (vha->timer_active) qla2x00_stop_timer(vha); - qla2x00_stop_dpc_thread(vha); - qla25xx_delete_queues(vha); + if (ha->flags.fce_enabled) qla2x00_disable_fce_trace(vha, NULL, NULL); @@ -3210,14 +3314,8 @@ void qla2x00_mark_device_lost(scsi_qla_host_t *vha, fc_port_t *fcport, set_bit(RELOGIN_NEEDED, &vha->dpc_flags); ql_dbg(ql_dbg_disc, vha, 0x2067, - "Port login retry " - "%02x%02x%02x%02x%02x%02x%02x%02x, " - "id = 0x%04x retry cnt=%d.\n", - fcport->port_name[0], fcport->port_name[1], - fcport->port_name[2], fcport->port_name[3], - fcport->port_name[4], fcport->port_name[5], - fcport->port_name[6], fcport->port_name[7], - fcport->loop_id, fcport->login_retry); + "Port login retry %8phN, id = 0x%04x retry cnt=%d.\n", + fcport->port_name, fcport->loop_id, fcport->login_retry); } } @@ -3290,7 +3388,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, 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", @@ -3324,7 +3422,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, "init_cb=%p gid_list=%p, srb_mempool=%p s_dma_pool=%p.\n", ha->init_cb, ha->gid_list, ha->srb_mempool, ha->s_dma_pool); - if (IS_QLA82XX(ha) || ql2xenabledif) { + 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) { @@ -3423,7 +3521,7 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, ha->npiv_info = NULL; /* Get consistent memory allocated for EX-INIT-CB. */ - if (IS_CNA_CAPABLE(ha) || IS_QLA2031(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) @@ -3454,10 +3552,10 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, 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); + "loop_id_map=%p.\n", ha->loop_id_map); } - return 1; + return 0; fail_async_pd: dma_pool_free(ha->s_dma_pool, ha->ex_init_cb, ha->ex_init_cb_dma); @@ -3532,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; } /* @@ -4649,6 +4754,66 @@ 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 @@ -4672,7 +4837,7 @@ 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()) { @@ -4699,17 +4864,33 @@ qla2x00_do_dpc(void *data) 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, - QLA8XXX_DEV_FAILED); - qla82xx_idc_unlock(ha); - ql_log(ql_log_info, base_vha, 0x4004, - "HW State: FAILED.\n"); - qla82xx_device_state_handler(base_vha); - continue; + if (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, @@ -4741,12 +4922,13 @@ qla2x00_do_dpc(void *data) if (qlafx00_reset_initialize(base_vha)) { /* Failed. Abort isp later. */ if (!test_bit(UNLOADING, - &base_vha->dpc_flags)) + &base_vha->dpc_flags)) { set_bit(ISP_UNRECOVERABLE, &base_vha->dpc_flags); ql_dbg(ql_dbg_dpc, base_vha, 0x4021, "Reset Recovery Failed\n"); + } } } @@ -4765,6 +4947,14 @@ qla2x00_do_dpc(void *data) 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, @@ -4809,16 +4999,26 @@ qla2x00_do_dpc(void *data) if (test_bit(ISP_QUIESCE_NEEDED, &base_vha->dpc_flags)) { ql_dbg(ql_dbg_dpc, base_vha, 0x4009, "Quiescence mode scheduled.\n"); - if (IS_QLA82XX(ha)) { - qla82xx_device_state_handler(base_vha); + 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); - - qla82xx_idc_lock(ha); - qla82xx_clear_qsnt_ready(base_vha); - qla82xx_idc_unlock(ha); + 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, @@ -4882,24 +5082,15 @@ loop_resync_check: qla2xxx_flash_npiv_conf(base_vha); } - if (test_and_clear_bit(HOST_RAMP_DOWN_QUEUE_DEPTH, - &base_vha->dpc_flags)) { - /* Prevents simultaneous ramp up and down */ - clear_bit(HOST_RAMP_UP_QUEUE_DEPTH, - &base_vha->dpc_flags); - qla2x00_host_ramp_down_queuedepth(base_vha); - } - - if (test_and_clear_bit(HOST_RAMP_UP_QUEUE_DEPTH, - &base_vha->dpc_flags)) - qla2x00_host_ramp_up_queuedepth(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); + } if (!IS_QLAFX00(ha)) qla2x00_do_dpc_all_vps(base_vha); @@ -4987,15 +5178,29 @@ qla2x00_timer(scsi_qla_host_t *vha) 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)) @@ -5071,11 +5276,10 @@ qla2x00_timer(scsi_qla_host_t *vha) "Loop down - seconds remaining %d.\n", atomic_read(&vha->loop_down_timer)); } - /* Check if beacon LED needs to be blinked for physical host only */ if (!vha->vp_idx && (ha->beacon_blink_led == 1)) { /* There is no beacon_blink function for ISP82xx */ - if (!IS_QLA82XX(ha)) { + if (!IS_P3P_TYPE(ha)) { set_bit(BEACON_BLINK_NEEDED, &vha->dpc_flags); start_dpc++; } @@ -5095,9 +5299,7 @@ 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(HOST_RAMP_DOWN_QUEUE_DEPTH, &vha->dpc_flags) || - test_bit(HOST_RAMP_UP_QUEUE_DEPTH, &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 " @@ -5110,15 +5312,12 @@ qla2x00_timer(scsi_qla_host_t *vha) 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, host_ramp_down_needed=%d " - "host_ramp_up_needed=%d.\n", + "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), - test_bit(HOST_RAMP_UP_QUEUE_DEPTH, &vha->dpc_flags), - test_bit(HOST_RAMP_DOWN_QUEUE_DEPTH, &vha->dpc_flags)); + test_bit(RELOGIN_NEEDED, &vha->dpc_flags)); qla2xxx_wake_dpc(vha); } @@ -5127,7 +5326,7 @@ qla2x00_timer(scsi_qla_host_t *vha) /* Firmware interface routines. */ -#define FW_BLOBS 10 +#define FW_BLOBS 11 #define FW_ISP21XX 0 #define FW_ISP22XX 1 #define FW_ISP2300 2 @@ -5138,6 +5337,7 @@ qla2x00_timer(scsi_qla_host_t *vha) #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" @@ -5149,6 +5349,8 @@ qla2x00_timer(scsi_qla_host_t *vha) #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); @@ -5163,6 +5365,7 @@ static struct fw_blob qla_fw_blobs[FW_BLOBS] = { { .name = FW_FILE_ISP82XX, }, { .name = FW_FILE_ISP2031, }, { .name = FW_FILE_ISP8031, }, + { .name = FW_FILE_ISP27XX, }, }; struct fw_blob * @@ -5191,6 +5394,8 @@ qla2x00_request_firmware(scsi_qla_host_t *vha) 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; } @@ -5519,6 +5724,9 @@ static struct pci_device_id qla2xxx_pci_tbl[] = { { 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); diff --git a/drivers/scsi/qla2xxx/qla_settings.h b/drivers/scsi/qla2xxx/qla_settings.h index 46ef0ac48f4..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-2013 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 3bef6736d88..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-2013 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -565,10 +565,10 @@ 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)) { + } else if (IS_QLA83XX(ha) || IS_QLA27XX(ha)) { *start = FA_FLASH_LAYOUT_ADDR_83; goto end; } @@ -682,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; @@ -719,7 +719,7 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr) start = le32_to_cpu(region->start) >> 2; ql_dbg(ql_dbg_init, vha, 0x0049, "FLT[%02x]: start=0x%x " - "end=0x%x size=0x%x.\n", le32_to_cpu(region->code), + "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)); @@ -741,57 +741,89 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr) 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) || IS_QLA8031(ha)) + if (IS_P3P_TYPE(ha) || IS_QLA8031(ha)) break; - if (!ha->flags.port0) + if (ha->port_no == 1) + ha->flt_region_vpd = start; + break; + case FLT_REG_VPD_2: + if (!IS_QLA27XX(ha)) + break; + 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 (IS_QLA8031(ha)) break; - if (ha->flags.port0) + if (ha->port_no == 0) ha->flt_region_nvram = start; break; case FLT_REG_NVRAM_1: if (IS_QLA8031(ha)) break; - if (!ha->flags.port0) + 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; @@ -803,15 +835,15 @@ qla2xxx_get_flt_info(scsi_qla_host_t *vha, uint32_t flt_addr) ha->flt_region_vpd = start; break; case FLT_REG_FCOE_NVRAM_0: - if (!IS_QLA8031(ha)) + if (!(IS_QLA8031(ha) || IS_QLA8044(ha))) break; - if (ha->flags.port0) + if (ha->port_no == 0) ha->flt_region_nvram = start; break; case FLT_REG_FCOE_NVRAM_1: - if (!IS_QLA8031(ha)) + if (!(IS_QLA8031(ha) || IS_QLA8044(ha))) break; - if (!ha->flags.port0) + if (ha->port_no == 1) ha->flt_region_nvram = start; break; } @@ -824,12 +856,12 @@ 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: ql_dbg(ql_dbg_init, vha, 0x004a, @@ -883,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 | @@ -895,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; } @@ -946,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; @@ -975,7 +1013,7 @@ qla2xxx_get_flash_info(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; if (!IS_QLA24XX_TYPE(ha) && !IS_QLA25XX(ha) && - !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha)) + !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha) && !IS_QLA27XX(ha)) return QLA_SUCCESS; ret = qla2xxx_find_flt_start(vha, &flt_addr); @@ -1008,6 +1046,9 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha) if (ha->flags.nic_core_reset_hdlr_active) return; + if (IS_QLA8044(ha)) + return; + ha->isp_ops->read_optrom(vha, (uint8_t *)&hdr, ha->flt_region_npiv_conf << 2, sizeof(struct qla_npiv_header)); if (hdr.version == __constant_cpu_to_le16(0xffff)) @@ -1175,7 +1216,8 @@ 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) || IS_QLA83XX(ha)) && + 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); @@ -1302,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. */ @@ -1360,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. */ @@ -1474,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); @@ -1658,7 +1700,7 @@ qla83xx_select_led_port(struct qla_hw_data *ha) if (!IS_QLA83XX(ha)) goto out; - if (ha->flags.port0) + if (ha->port_no == 0) led_select_value = QLA83XX_LED_PORT0; else led_select_value = QLA83XX_LED_PORT1; @@ -1685,11 +1727,8 @@ qla83xx_beacon_blink(struct scsi_qla_host *vha) if (IS_QLA2031(ha)) { led_select_value = qla83xx_select_led_port(ha); - qla83xx_wr_reg(vha, led_select_value, 0x40002000); - qla83xx_wr_reg(vha, led_select_value + 4, 0x40002000); - msleep(1000); - qla83xx_wr_reg(vha, led_select_value, 0x40004000); - qla83xx_wr_reg(vha, led_select_value + 4, 0x40004000); + 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); @@ -1752,7 +1791,7 @@ 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)) @@ -1804,7 +1843,7 @@ 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; @@ -2315,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 @@ -2541,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; @@ -2822,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; @@ -2832,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) diff --git a/drivers/scsi/qla2xxx/qla_target.c b/drivers/scsi/qla2xxx/qla_target.c index 83a8f7a9ec7..e632e14180c 100644 --- a/drivers/scsi/qla2xxx/qla_target.c +++ b/drivers/scsi/qla2xxx/qla_target.c @@ -10,7 +10,7 @@ * * Forward port and refactoring to modern qla2xxx and target/configfs * - * Copyright (C) 2010-2011 Nicholas A. Bellinger <nab@kernel.org> + * 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 @@ -104,7 +104,6 @@ static void qlt_reject_free_srr_imm(struct scsi_qla_host *ha, /* * Global Variables */ -static struct kmem_cache *qla_tgt_cmd_cachep; static struct kmem_cache *qla_tgt_mgmt_cmd_cachep; static mempool_t *qla_tgt_mgmt_cmd_mempool; static struct workqueue_struct *qla_tgt_wq; @@ -182,6 +181,11 @@ struct scsi_qla_host *qlt_find_host_by_vp_idx(struct scsi_qla_host *vha, 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: { @@ -236,6 +240,10 @@ void qlt_24xx_atio_pkt_all_vps(struct scsi_qla_host *vha, 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; @@ -430,13 +438,8 @@ static int qlt_reset(struct scsi_qla_host *vha, void *iocb, int mcmd) } ql_dbg(ql_dbg_tgt, vha, 0xe047, - "scsi(%ld): resetting (session %p from port " - "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x, " - "mcmd %x, loop_id %d)\n", vha->host_no, sess, - sess->port_name[0], sess->port_name[1], - sess->port_name[2], sess->port_name[3], - sess->port_name[4], sess->port_name[5], - sess->port_name[6], sess->port_name[7], + "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; @@ -467,21 +470,16 @@ static void qlt_schedule_sess_for_deletion(struct qla_tgt_sess *sess, sess->expires = jiffies + dev_loss_tmo * HZ; ql_dbg(ql_dbg_tgt, sess->vha, 0xe048, - "qla_target(%d): session for port %02x:%02x:%02x:" - "%02x:%02x:%02x:%02x:%02x (loop ID %d) scheduled for " + "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[0], sess->port_name[1], - sess->port_name[2], sess->port_name[3], - sess->port_name[4], sess->port_name[5], - sess->port_name[6], sess->port_name[7], - sess->loop_id, dev_loss_tmo, sess->expires, immediate); + 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, - jiffies - sess->expires); + sess->expires - jiffies); } /* ha->hardware_lock supposed to be held on entry */ @@ -560,13 +558,14 @@ static void qlt_del_sess_work_fn(struct delayed_work *work) struct scsi_qla_host *vha = tgt->vha; struct qla_hw_data *ha = vha->hw; struct qla_tgt_sess *sess; - unsigned long flags; + 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); - if (time_after_eq(jiffies, sess->expires)) { + elapsed = jiffies; + if (time_after_eq(elapsed, sess->expires)) { qlt_undelete_sess(sess); ql_dbg(ql_dbg_tgt_mgt, vha, 0xf004, @@ -576,7 +575,7 @@ static void qlt_del_sess_work_fn(struct delayed_work *work) ha->tgt.tgt_ops->put_sess(sess); } else { schedule_delayed_work(&tgt->sess_del_work, - jiffies - sess->expires); + sess->expires - elapsed); break; } } @@ -599,7 +598,7 @@ static struct qla_tgt_sess *qlt_create_sess( /* Check to avoid double sessions */ spin_lock_irqsave(&ha->hardware_lock, flags); - list_for_each_entry(sess, &ha->tgt.qla_tgt->sess_list, + 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, @@ -630,17 +629,13 @@ static struct qla_tgt_sess *qlt_create_sess( 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 %02x:%02x:%02x:%02x:" - "%02x:%02x:%02x:%02x will be refused", vha->vp_idx, - 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]); + "qla_target(%u): session allocation failed, all commands " + "from port %8phC will be refused", vha->vp_idx, + fcport->port_name); return NULL; } - sess->tgt = ha->tgt.qla_tgt; + sess->tgt = vha->vha_tgt.qla_tgt; sess->vha = vha; sess->s_id = fcport->d_id; sess->loop_id = fcport->loop_id; @@ -648,7 +643,7 @@ static struct qla_tgt_sess *qlt_create_sess( ql_dbg(ql_dbg_tgt_mgt, vha, 0xf006, "Adding sess %p to tgt %p via ->check_initiator_node_acl()\n", - sess, ha->tgt.qla_tgt); + sess, vha->vha_tgt.qla_tgt); be_sid[0] = sess->s_id.b.domain; be_sid[1] = sess->s_id.b.area; @@ -675,20 +670,16 @@ static struct qla_tgt_sess *qlt_create_sess( 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, &ha->tgt.qla_tgt->sess_list); - ha->tgt.qla_tgt->sess_count++; + 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 %02x:%02x:%02x:%02x:" - "%02x:%02x:%02x:%02x (loop_id %d, s_id %x:%x:%x, confirmed" - " completion %ssupported) added\n", - vha->vp_idx, local ? "local " : "", 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, sess->s_id.b.domain, - sess->s_id.b.area, sess->s_id.b.al_pa, sess->conf_compl_supported ? - "" : "not "); + "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; } @@ -699,7 +690,7 @@ static struct qla_tgt_sess *qlt_create_sess( 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 = ha->tgt.qla_tgt; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; struct qla_tgt_sess *sess; unsigned long flags; @@ -709,6 +700,9 @@ void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport) 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); @@ -718,9 +712,9 @@ void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport) if (!sess) { spin_unlock_irqrestore(&ha->hardware_lock, flags); - mutex_lock(&ha->tgt.tgt_mutex); + mutex_lock(&vha->vha_tgt.tgt_mutex); sess = qlt_create_sess(vha, fcport, false); - mutex_unlock(&ha->tgt.tgt_mutex); + mutex_unlock(&vha->vha_tgt.tgt_mutex); spin_lock_irqsave(&ha->hardware_lock, flags); } else { @@ -730,13 +724,9 @@ void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport) qlt_undelete_sess(sess); ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04c, - "qla_target(%u): %ssession for port %02x:" - "%02x:%02x:%02x:%02x:%02x:%02x:%02x (loop ID %d) " - "reappeared\n", vha->vp_idx, sess->local ? "local " - : "", sess->port_name[0], sess->port_name[1], - sess->port_name[2], sess->port_name[3], - sess->port_name[4], sess->port_name[5], - sess->port_name[6], sess->port_name[7], + "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, @@ -749,13 +739,8 @@ void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport) if (sess && sess->local) { ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04d, "qla_target(%u): local session for " - "port %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x " - "(loop ID %d) became global\n", vha->vp_idx, - 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], - sess->loop_id); + "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); @@ -765,7 +750,7 @@ void qlt_fc_port_added(struct scsi_qla_host *vha, fc_port_t *fcport) 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 = ha->tgt.qla_tgt; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; struct qla_tgt_sess *sess; unsigned long flags; @@ -813,17 +798,32 @@ static inline int test_tgt_sess_count(struct qla_tgt *tgt) } /* Called by tcm_qla2xxx configfs code */ -void qlt_stop_phase1(struct qla_tgt *tgt) +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"); - dump_stack(); - return; + mutex_unlock(&qla_tgt_mutex); + return -EPERM; } ql_dbg(ql_dbg_tgt, vha, 0xe003, "Stopping target for host %ld(%p)\n", @@ -832,12 +832,13 @@ void qlt_stop_phase1(struct qla_tgt *tgt) * Mutex needed to sync with qla_tgt_fc_port_[added,deleted]. * Lock is needed, because we still can get an incoming packet. */ - mutex_lock(&ha->tgt.tgt_mutex); + 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(&ha->tgt.tgt_mutex); + mutex_unlock(&vha->vha_tgt.tgt_mutex); + mutex_unlock(&qla_tgt_mutex); flush_delayed_work(&tgt->sess_del_work); @@ -864,6 +865,7 @@ void qlt_stop_phase1(struct qla_tgt *tgt) /* 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); @@ -871,20 +873,21 @@ EXPORT_SYMBOL(qlt_stop_phase1); 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, tgt->vha, 0xf04f, + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf04f, "Already in tgt->tgt_stopped state\n"); dump_stack(); return; } - ql_dbg(ql_dbg_tgt_mgt, tgt->vha, 0xf00b, + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00b, "Waiting for %d IRQ commands to complete (tgt %p)", tgt->irq_cmd_count, tgt); - mutex_lock(&ha->tgt.tgt_mutex); + 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); @@ -894,9 +897,9 @@ void qlt_stop_phase2(struct qla_tgt *tgt) tgt->tgt_stop = 0; tgt->tgt_stopped = 1; spin_unlock_irqrestore(&ha->hardware_lock, flags); - mutex_unlock(&ha->tgt.tgt_mutex); + mutex_unlock(&vha->vha_tgt.tgt_mutex); - ql_dbg(ql_dbg_tgt_mgt, tgt->vha, 0xf00c, "Stop of tgt %p finished", + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00c, "Stop of tgt %p finished", tgt); } EXPORT_SYMBOL(qlt_stop_phase2); @@ -904,14 +907,14 @@ EXPORT_SYMBOL(qlt_stop_phase2); /* Called from qlt_remove_target() -> qla2x00_remove_one() */ static void qlt_release(struct qla_tgt *tgt) { - struct qla_hw_data *ha = tgt->ha; + scsi_qla_host_t *vha = tgt->vha; - if ((ha->tgt.qla_tgt != NULL) && !tgt->tgt_stopped) + if ((vha->vha_tgt.qla_tgt != NULL) && !tgt->tgt_stopped) qlt_stop_phase2(tgt); - ha->tgt.qla_tgt = NULL; + vha->vha_tgt.qla_tgt = NULL; - ql_dbg(ql_dbg_tgt_mgt, tgt->vha, 0xf00d, + ql_dbg(ql_dbg_tgt_mgt, vha, 0xf00d, "Release of tgt %p finished\n", tgt); kfree(tgt); @@ -975,8 +978,8 @@ static void qlt_send_notify_ack(struct scsi_qla_host *vha, return; } - if (ha->tgt.qla_tgt != NULL) - ha->tgt.qla_tgt->notify_ack_expected++; + 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; @@ -1080,7 +1083,7 @@ static void qlt_24xx_send_abts_resp(struct scsi_qla_host *vha, /* Other bytes are zero */ } - ha->tgt.qla_tgt->abts_resp_expected++; + vha->vha_tgt.qla_tgt->abts_resp_expected++; qla2x00_start_iocbs(vha, vha->req); } @@ -1125,7 +1128,7 @@ static void qlt_24xx_retry_term_exchange(struct scsi_qla_host *vha, ctio->u.status1.flags = __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_TERMINATE); - ctio->u.status1.ox_id = entry->fcp_hdr_le.ox_id; + ctio->u.status1.ox_id = cpu_to_le16(entry->fcp_hdr_le.ox_id); qla2x00_start_iocbs(vha, vha->req); @@ -1232,7 +1235,7 @@ static void qlt_24xx_handle_abts(struct scsi_qla_host *vha, 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(ha->tgt.qla_tgt, + 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, @@ -1259,6 +1262,7 @@ static void qlt_24xx_send_task_mgmt_ctio(struct scsi_qla_host *ha, { 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", @@ -1289,7 +1293,8 @@ static void qlt_24xx_send_task_mgmt_ctio(struct scsi_qla_host *ha, ctio->u.status1.flags = (atio->u.isp24.attr << 9) | __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_SEND_STATUS); - ctio->u.status1.ox_id = swab16(atio->u.isp24.fcp_hdr.ox_id); + 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); @@ -1355,13 +1360,42 @@ static int qlt_pci_map_calc_cnt(struct qla_tgt_prm *prm) prm->cmd->sg_mapped = 1; - /* - * 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); + 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); @@ -1382,6 +1416,16 @@ static inline void qlt_unmap_sg(struct scsi_qla_host *vha, 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, @@ -1471,6 +1515,7 @@ static int qlt_24xx_build_ctio_pkt(struct qla_tgt_prm *prm, 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; @@ -1499,13 +1544,13 @@ static int qlt_24xx_build_ctio_pkt(struct qla_tgt_prm *prm, 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); - pkt->u.status0.ox_id = swab16(atio->u.isp24.fcp_hdr.ox_id); + 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, - le16_to_cpu(pkt->u.status0.ox_id)); + vha->vp_idx, pkt->handle, QLA_TGT_TIMEOUT, temp); return 0; } @@ -1670,8 +1715,9 @@ static int qlt_pre_xmit_response(struct qla_tgt_cmd *cmd, return QLA_TGT_PRE_XMIT_RESP_CMD_ABORTED; } - ql_dbg(ql_dbg_tgt, vha, 0xe011, "qla_target(%d): tag=%u\n", - vha->vp_idx, cmd->tag); + 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; @@ -1907,6 +1953,328 @@ skip_explict_conf: /* 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 @@ -1926,9 +2294,10 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, 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\n", (xmit_type & QLA_TGT_XMIT_STATUS) ? - 1 : 0, cmd->bufflen, cmd->sg_cnt, cmd->dma_data_direction); + "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); @@ -1946,7 +2315,10 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, if (unlikely(res)) goto out_unmap_unlock; - res = qlt_24xx_build_ctio_pkt(&prm, vha); + 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; @@ -1958,7 +2330,8 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, __constant_cpu_to_le16(CTIO7_FLAGS_DATA_IN | CTIO7_FLAGS_STATUS_MODE_0); - qlt_load_data_segments(&prm, vha); + 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) { @@ -1988,8 +2361,14 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, 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); @@ -1998,6 +2377,11 @@ int qlt_xmit_response(struct qla_tgt_cmd *cmd, int xmit_type, 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); @@ -2046,8 +2430,10 @@ int qlt_rdy_to_xfer(struct qla_tgt_cmd *cmd) if (qlt_issue_marker(vha, 0) != QLA_SUCCESS) return -EIO; - ql_dbg(ql_dbg_tgt, vha, 0xe01b, "CTIO_start: vha(%d)", - (int)vha->vp_idx); + 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) @@ -2059,14 +2445,19 @@ int qlt_rdy_to_xfer(struct qla_tgt_cmd *cmd) 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); - 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); - qlt_load_data_segments(&prm, vha); + + if (cmd->se_cmd.prot_op == TARGET_PROT_NORMAL) + qlt_load_data_segments(&prm, vha); cmd->state = QLA_TGT_STATE_NEED_DATA; @@ -2084,6 +2475,143 @@ out_unlock_free_unmap: } 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, @@ -2094,6 +2622,7 @@ static int __qlt_send_term_exchange(struct scsi_qla_host *vha, 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); @@ -2130,7 +2659,8 @@ static int __qlt_send_term_exchange(struct scsi_qla_host *vha, ctio24->u.status1.flags = (atio->u.isp24.attr << 9) | __constant_cpu_to_le16(CTIO7_FLAGS_STATUS_MODE_1 | CTIO7_FLAGS_TERMINATE); - ctio24->u.status1.ox_id = swab16(atio->u.isp24.fcp_hdr.ox_id); + 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 *) @@ -2160,21 +2690,46 @@ static void qlt_send_term_exchange(struct scsi_qla_host *vha, rc = __qlt_send_term_exchange(vha, cmd, atio); spin_unlock_irqrestore(&vha->hw->hardware_lock, flags); done: - if (rc == 1) { + /* + * 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) { - BUG_ON(cmd->sg_mapped); + 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); - kmem_cache_free(qla_tgt_cmd_cachep, cmd); + + 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); @@ -2183,8 +2738,7 @@ 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_hw_data *ha = vha->hw; - struct qla_tgt *tgt = ha->tgt.qla_tgt; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; struct qla_tgt_srr_imm *imm; tgt->ctio_srr_id++; @@ -2380,6 +2934,7 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle, 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 */ @@ -2410,18 +2965,58 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle, 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", + "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; } - if (cmd->state != QLA_TGT_STATE_NEED_DATA) + + /* "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); @@ -2450,7 +3045,8 @@ static void qlt_do_ctio_completion(struct scsi_qla_host *vha, uint32_t handle, "not return a CTIO complete\n", vha->vp_idx, cmd->state); } - if (unlikely(status != CTIO_SUCCESS)) { + 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(); } @@ -2495,13 +3091,12 @@ static struct qla_tgt_sess *qlt_make_local_sess(struct scsi_qla_host *, /* * Process context for I/O path into tcm_qla2xxx code */ -static void qlt_do_work(struct work_struct *work) +static void __qlt_do_work(struct qla_tgt_cmd *cmd) { - struct qla_tgt_cmd *cmd = container_of(work, struct qla_tgt_cmd, work); scsi_qla_host_t *vha = cmd->vha; struct qla_hw_data *ha = vha->hw; - struct qla_tgt *tgt = ha->tgt.qla_tgt; - struct qla_tgt_sess *sess = NULL; + 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; @@ -2511,41 +3106,6 @@ static void qlt_do_work(struct work_struct *work) if (tgt->tgt_stop) goto out_term; - spin_lock_irqsave(&ha->hardware_lock, flags); - sess = ha->tgt.tgt_ops->find_sess_by_s_id(vha, - atio->u.isp24.fcp_hdr.s_id); - /* Do kref_get() before dropping qla_hw_data->hardware_lock. */ - if (sess) - kref_get(&sess->se_sess->sess_kref); - spin_unlock_irqrestore(&ha->hardware_lock, flags); - - if (unlikely(!sess)) { - uint8_t *s_id = 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 (atio->u.raw.entry_count > 1) { - ql_dbg(ql_dbg_tgt_mgt, vha, 0xf023, - "Dropping multy entry cmd %p\n", cmd); - goto out_term; - } - - mutex_lock(&ha->tgt.tgt_mutex); - sess = qlt_make_local_sess(vha, s_id); - /* sess has an extra creation ref. */ - mutex_unlock(&ha->tgt.tgt_mutex); - - if (!sess) - goto out_term; - } - - cmd->sess = sess; - cmd->loop_id = sess->loop_id; - cmd->conf_compl_supported = sess->conf_compl_supported; - cdb = &atio->u.isp24.fcp_cmnd.cdb[0]; cmd->tag = atio->u.isp24.exchange_addr; cmd->unpacked_lun = scsilun_to_int( @@ -2569,11 +3129,12 @@ static void qlt_do_work(struct work_struct *work) atio->u.isp24.fcp_cmnd.add_cdb_len])); ql_dbg(ql_dbg_tgt, vha, 0xe022, - "qla_target: START qla command: %p lun: 0x%04x (tag %d)\n", - cmd, cmd->unpacked_lun, cmd->tag); + "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 = vha->hw->tgt.tgt_ops->handle_cmd(vha, cmd, cdb, data_length, - fcp_task_attr, data_dir, bidi); + ret = ha->tgt.tgt_ops->handle_cmd(vha, cmd, cdb, data_length, + fcp_task_attr, data_dir, bidi); if (ret != 0) goto out_term; /* @@ -2592,10 +3153,105 @@ out_term: */ spin_lock_irqsave(&ha->hardware_lock, flags); qlt_send_term_exchange(vha, NULL, &cmd->atio, 1); - kmem_cache_free(qla_tgt_cmd_cachep, cmd); - if (sess) + 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 */ @@ -2603,7 +3259,8 @@ 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 = ha->tgt.qla_tgt; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; + struct qla_tgt_sess *sess; struct qla_tgt_cmd *cmd; if (unlikely(tgt->tgt_stop)) { @@ -2612,20 +3269,31 @@ static int qlt_handle_cmd_for_atio(struct scsi_qla_host *vha, return -EFAULT; } - cmd = kmem_cache_zalloc(qla_tgt_cmd_cachep, GFP_ATOMIC); + 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_LIST_HEAD(&cmd->cmd_list); - - memcpy(&cmd->atio, atio, sizeof(*atio)); - cmd->state = QLA_TGT_STATE_NEW; - cmd->tgt = ha->tgt.qla_tgt; - cmd->vha = vha; - INIT_WORK(&cmd->work, qlt_do_work); queue_work(qla_tgt_wq, &cmd->work); return 0; @@ -2749,7 +3417,7 @@ static int qlt_handle_task_mgmt(struct scsi_qla_host *vha, void *iocb) uint32_t lun, unpacked_lun; int lun_size, fn; - tgt = ha->tgt.qla_tgt; + tgt = vha->vha_tgt.qla_tgt; lun = a->u.isp24.fcp_cmnd.lun; lun_size = sizeof(a->u.isp24.fcp_cmnd.lun); @@ -2823,7 +3491,7 @@ static int qlt_abort_task(struct scsi_qla_host *vha, 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(ha->tgt.qla_tgt, + return qlt_sched_sess_work(vha->vha_tgt.qla_tgt, QLA_TGT_SESS_WORK_ABORT, iocb, sizeof(*iocb)); } @@ -2836,14 +3504,11 @@ static int qlt_abort_task(struct scsi_qla_host *vha, static int qlt_24xx_handle_els(struct scsi_qla_host *vha, struct imm_ntfy_from_isp *iocb) { - struct qla_hw_data *ha = vha->hw; int res = 0; ql_dbg(ql_dbg_tgt_mgt, vha, 0xf026, - "qla_target(%d): Port ID: 0x%02x:%02x:%02x" - " ELS opcode: 0x%02x\n", vha->vp_idx, iocb->u.isp24.port_id[0], - iocb->u.isp24.port_id[1], iocb->u.isp24.port_id[2], - iocb->u.isp24.status_subcode); + "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: @@ -2856,7 +3521,7 @@ static int qlt_24xx_handle_els(struct scsi_qla_host *vha, case ELS_PDISC: case ELS_ADISC: { - struct qla_tgt *tgt = ha->tgt.qla_tgt; + 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); @@ -3214,7 +3879,8 @@ restart: 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[0], cmd->sg_cnt, cmd->offset); + se_cmd->t_task_cdb ? se_cmd->t_task_cdb[0] : 0, + cmd->sg_cnt, cmd->offset); qlt_handle_srr(vha, sctio, imm); @@ -3230,8 +3896,7 @@ 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_hw_data *ha = vha->hw; - struct qla_tgt *tgt = ha->tgt.qla_tgt; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; struct qla_tgt_srr_ctio *sctio; tgt->imm_srr_id++; @@ -3341,7 +4006,7 @@ static void qlt_handle_imm_notify(struct scsi_qla_host *vha, case IMM_NTFY_LIP_LINK_REINIT: { - struct qla_tgt *tgt = ha->tgt.qla_tgt; + 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, @@ -3517,7 +4182,7 @@ 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 = ha->tgt.qla_tgt; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; int rc; if (unlikely(tgt == NULL)) { @@ -3539,11 +4204,11 @@ static void qlt_24xx_atio_pkt(struct scsi_qla_host *vha, 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, " - "add_cdb_len %d, data_length %04x, s_id %x:%x:%x\n", + "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[ @@ -3619,7 +4284,7 @@ static void qlt_24xx_atio_pkt(struct scsi_qla_host *vha, static void qlt_response_pkt(struct scsi_qla_host *vha, response_t *pkt) { struct qla_hw_data *ha = vha->hw; - struct qla_tgt *tgt = ha->tgt.qla_tgt; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; if (unlikely(tgt == NULL)) { ql_dbg(ql_dbg_tgt, vha, 0xe05d, @@ -3641,11 +4306,13 @@ static void qlt_response_pkt(struct scsi_qla_host *vha, response_t *pkt) 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_TYPE7: instance %d\n", - vha->vp_idx); + 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); @@ -3822,7 +4489,7 @@ 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 = ha->tgt.qla_tgt; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; int login_code; ql_dbg(ql_dbg_tgt, vha, 0xe039, @@ -3952,14 +4619,14 @@ static fc_port_t *qlt_get_port_database(struct scsi_qla_host *vha, static struct qla_tgt_sess *qlt_make_local_sess(struct scsi_qla_host *vha, uint8_t *s_id) { - struct qla_hw_data *ha = vha->hw; 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(&ha->tgt.qla_tgt->tgt_global_resets_count); + 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) { @@ -3986,12 +4653,13 @@ retry: return NULL; if (global_resets != - atomic_read(&ha->tgt.qla_tgt->tgt_global_resets_count)) { + 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(&ha->tgt.qla_tgt->tgt_global_resets_count)); + atomic_read(&vha->vha_tgt. + qla_tgt->tgt_global_resets_count)); goto retry; } @@ -4026,10 +4694,10 @@ static void qlt_abort_work(struct qla_tgt *tgt, if (!sess) { spin_unlock_irqrestore(&ha->hardware_lock, flags); - mutex_lock(&ha->tgt.tgt_mutex); + mutex_lock(&vha->vha_tgt.tgt_mutex); sess = qlt_make_local_sess(vha, s_id); /* sess has got an extra creation ref */ - mutex_unlock(&ha->tgt.tgt_mutex); + mutex_unlock(&vha->vha_tgt.tgt_mutex); spin_lock_irqsave(&ha->hardware_lock, flags); if (!sess) @@ -4080,10 +4748,10 @@ static void qlt_tmr_work(struct qla_tgt *tgt, if (!sess) { spin_unlock_irqrestore(&ha->hardware_lock, flags); - mutex_lock(&ha->tgt.tgt_mutex); + mutex_lock(&vha->vha_tgt.tgt_mutex); sess = qlt_make_local_sess(vha, s_id); /* sess has got an extra creation ref */ - mutex_unlock(&ha->tgt.tgt_mutex); + mutex_unlock(&vha->vha_tgt.tgt_mutex); spin_lock_irqsave(&ha->hardware_lock, flags); if (!sess) @@ -4169,9 +4837,9 @@ int qlt_add_target(struct qla_hw_data *ha, struct scsi_qla_host *base_vha) } ql_dbg(ql_dbg_tgt, base_vha, 0xe03b, - "Registering target for host %ld(%p)", base_vha->host_no, ha); + "Registering target for host %ld(%p).\n", base_vha->host_no, ha); - BUG_ON((ha->tgt.qla_tgt != NULL) || (ha->tgt.tgt_ops != NULL)); + BUG_ON(base_vha->vha_tgt.qla_tgt != NULL); tgt = kzalloc(sizeof(struct qla_tgt), GFP_KERNEL); if (!tgt) { @@ -4199,7 +4867,7 @@ int qlt_add_target(struct qla_hw_data *ha, struct scsi_qla_host *base_vha) INIT_WORK(&tgt->srr_work, qlt_handle_srr_work); atomic_set(&tgt->tgt_global_resets_count, 0); - ha->tgt.qla_tgt = tgt; + base_vha->vha_tgt.qla_tgt = tgt; ql_dbg(ql_dbg_tgt, base_vha, 0xe067, "qla_target(%d): using 64 Bit PCI addressing", @@ -4210,6 +4878,9 @@ int qlt_add_target(struct qla_hw_data *ha, struct scsi_qla_host *base_vha) 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); @@ -4220,16 +4891,20 @@ int qlt_add_target(struct qla_hw_data *ha, struct scsi_qla_host *base_vha) /* Must be called under tgt_host_action_mutex */ int qlt_remove_target(struct qla_hw_data *ha, struct scsi_qla_host *vha) { - if (!ha->tgt.qla_tgt) + 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(&ha->tgt.qla_tgt->tgt_list_entry); + 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(ha->tgt.qla_tgt); + qlt_release(vha->vha_tgt.qla_tgt); return 0; } @@ -4263,8 +4938,9 @@ static void qlt_lport_dump(struct scsi_qla_host *vha, u64 wwpn, * @callback: lport initialization callback for tcm_qla2xxx code * @target_lport_ptr: pointer for tcm_qla2xxx specific lport data */ -int qlt_lport_register(struct qla_tgt_func_tmpl *qla_tgt_ops, u64 wwpn, - int (*callback)(struct scsi_qla_host *), void *target_lport_ptr) +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; @@ -4283,19 +4959,22 @@ int qlt_lport_register(struct qla_tgt_func_tmpl *qla_tgt_ops, u64 wwpn, if (!host) continue; - if (ha->tgt.tgt_ops != NULL) - continue; - if (!(host->hostt->supported_mode & MODE_TARGET)) continue; spin_lock_irqsave(&ha->hardware_lock, flags); - if (host->active_mode & MODE_TARGET) { + 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)) { @@ -4304,22 +4983,16 @@ int qlt_lport_register(struct qla_tgt_func_tmpl *qla_tgt_ops, u64 wwpn, " qla2xxx scsi_host\n"); continue; } - qlt_lport_dump(vha, wwpn, b); + qlt_lport_dump(vha, phys_wwpn, b); if (memcmp(vha->port_name, b, WWN_SIZE)) { scsi_host_put(host); continue; } - /* - * Setup passed parameters ahead of invoking callback - */ - ha->tgt.tgt_ops = qla_tgt_ops; - ha->tgt.target_lport_ptr = target_lport_ptr; - rc = (*callback)(vha); - if (rc != 0) { - ha->tgt.tgt_ops = NULL; - ha->tgt.target_lport_ptr = NULL; - } + rc = (*callback)(vha, target_lport_ptr, npiv_wwpn, npiv_wwnn); + if (rc != 0) + scsi_host_put(host); + mutex_unlock(&qla_tgt_mutex); return rc; } @@ -4341,7 +5014,7 @@ void qlt_lport_deregister(struct scsi_qla_host *vha) /* * Clear the target_lport_ptr qla_target_template pointer in qla_hw_data */ - ha->tgt.target_lport_ptr = NULL; + vha->vha_tgt.target_lport_ptr = NULL; ha->tgt.tgt_ops = NULL; /* * Release the Scsi_Host reference for the underlying qla2xxx host @@ -4403,8 +5076,9 @@ void qlt_enable_vha(struct scsi_qla_host *vha) { struct qla_hw_data *ha = vha->hw; - struct qla_tgt *tgt = ha->tgt.qla_tgt; + 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, @@ -4419,9 +5093,14 @@ qlt_enable_vha(struct scsi_qla_host *vha) qlt_set_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); + 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); @@ -4434,7 +5113,7 @@ void qlt_disable_vha(struct scsi_qla_host *vha) { struct qla_hw_data *ha = vha->hw; - struct qla_tgt *tgt = ha->tgt.qla_tgt; + struct qla_tgt *tgt = vha->vha_tgt.qla_tgt; unsigned long flags; if (!tgt) { @@ -4465,8 +5144,10 @@ qlt_vport_create(struct scsi_qla_host *vha, struct qla_hw_data *ha) if (!qla_tgt_mode_enabled(vha)) return; - mutex_init(&ha->tgt.tgt_mutex); - mutex_init(&ha->tgt.tgt_host_action_mutex); + 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); @@ -4477,6 +5158,8 @@ qlt_vport_create(struct scsi_qla_host *vha, struct qla_hw_data *ha) * assigning the value appropriately. */ ha->tgt.atio_q_length = ATIO_ENTRY_CNT_24XX; + + qlt_add_target(ha, vha); } void @@ -4764,6 +5447,7 @@ qlt_24xx_process_response_error(struct scsi_qla_host *vha, case ABTS_RESP_24XX: case CTIO_TYPE7: case NOTIFY_ACK_TYPE: + case CTIO_CRC2: return 1; default: return 0; @@ -4795,8 +5479,8 @@ qlt_probe_one_stage1(struct scsi_qla_host *base_vha, struct qla_hw_data *ha) ISP_ATIO_Q_OUT(base_vha) = &ha->iobase->isp24.atio_q_out; } - mutex_init(&ha->tgt.tgt_mutex); - mutex_init(&ha->tgt.tgt_host_action_mutex); + mutex_init(&base_vha->vha_tgt.tgt_mutex); + mutex_init(&base_vha->vha_tgt.tgt_host_action_mutex); qlt_clear_mode(base_vha); } @@ -4907,23 +5591,13 @@ int __init qlt_init(void) if (!QLA_TGT_MODE_ENABLED()) return 0; - qla_tgt_cmd_cachep = kmem_cache_create("qla_tgt_cmd_cachep", - sizeof(struct qla_tgt_cmd), __alignof__(struct qla_tgt_cmd), 0, - NULL); - if (!qla_tgt_cmd_cachep) { - ql_log(ql_log_fatal, NULL, 0xe06c, - "kmem_cache_create for qla_tgt_cmd_cachep failed\n"); - return -ENOMEM; - } - 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"); - ret = -ENOMEM; - goto out; + return -ENOMEM; } qla_tgt_mgmt_cmd_mempool = mempool_create(25, mempool_alloc_slab, @@ -4951,8 +5625,6 @@ out_cmd_mempool: mempool_destroy(qla_tgt_mgmt_cmd_mempool); out_mgmt_cmd_cachep: kmem_cache_destroy(qla_tgt_mgmt_cmd_cachep); -out: - kmem_cache_destroy(qla_tgt_cmd_cachep); return ret; } @@ -4964,5 +5636,4 @@ void qlt_exit(void) destroy_workqueue(qla_tgt_wq); mempool_destroy(qla_tgt_mgmt_cmd_mempool); kmem_cache_destroy(qla_tgt_mgmt_cmd_cachep); - kmem_cache_destroy(qla_tgt_cmd_cachep); } diff --git a/drivers/scsi/qla2xxx/qla_target.h b/drivers/scsi/qla2xxx/qla_target.h index b33e411f28a..d1d24fb0160 100644 --- a/drivers/scsi/qla2xxx/qla_target.h +++ b/drivers/scsi/qla2xxx/qla_target.h @@ -293,6 +293,7 @@ struct ctio_to_2xxx { #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 @@ -315,7 +316,7 @@ struct fcp_hdr { uint8_t seq_id; uint8_t df_ctl; uint16_t seq_cnt; - uint16_t ox_id; + __be16 ox_id; uint16_t rx_id; uint32_t parameter; } __packed; @@ -440,9 +441,9 @@ struct ctio7_to_24xx { union { struct { uint16_t reserved1; - uint16_t flags; + __le16 flags; uint32_t residual; - uint16_t ox_id; + __le16 ox_id; uint16_t scsi_status; uint32_t relative_offset; uint32_t reserved2; @@ -457,7 +458,7 @@ struct ctio7_to_24xx { uint16_t sense_length; uint16_t flags; uint32_t residual; - uint16_t ox_id; + __le16 ox_id; uint16_t scsi_status; uint16_t response_len; uint16_t reserved; @@ -498,11 +499,12 @@ struct ctio7_from_24xx { #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 -#define CTIO7_FLAGS_DATA_OUT BIT_0 +#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 @@ -514,6 +516,68 @@ struct ctio7_from_24xx { #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) */ @@ -641,6 +705,7 @@ 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 *); @@ -805,6 +870,12 @@ struct qla_tgt { 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) */ @@ -829,9 +900,9 @@ struct qla_tgt_sess { }; struct qla_tgt_cmd { + struct se_cmd se_cmd; struct qla_tgt_sess *sess; int state; - struct se_cmd se_cmd; struct work_struct free_work; struct work_struct work; /* Sense buffer that will be mapped into outgoing status */ @@ -843,6 +914,7 @@ struct qla_tgt_cmd { 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 */ @@ -855,9 +927,14 @@ struct qla_tgt_cmd { uint16_t loop_id; /* to save extra sess dereferences */ struct qla_tgt *tgt; /* to save extra sess dereferences */ struct scsi_qla_host *vha; - struct list_head cmd_list; 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 { @@ -902,6 +979,10 @@ struct qla_tgt_prm { 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 { @@ -932,8 +1013,8 @@ void qlt_disable_vha(struct scsi_qla_host *); */ 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(struct qla_tgt_func_tmpl *, u64, - int (*callback)(struct scsi_qla_host *), void *); +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 *); @@ -977,6 +1058,8 @@ extern void qlt_24xx_atio_pkt_all_vps(struct scsi_qla_host *, 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); @@ -1002,7 +1085,7 @@ extern void qlt_modify_vp_config(struct scsi_qla_host *, 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 void qlt_stop_phase1(struct qla_tgt *); +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 *); 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 6c66d22eb1b..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-2013 QLogic Corporation + * Copyright (c) 2003-2014 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ /* * Driver version */ -#define QLA2XXX_VERSION "8.05.00.03-k" +#define QLA2XXX_VERSION "8.07.00.08-k" #define QLA_DRIVER_MAJOR_VER 8 -#define QLA_DRIVER_MINOR_VER 5 +#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 index f790fcaf87d..e2beab96209 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.c +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.c @@ -2,12 +2,9 @@ * This file contains tcm implementation using v4 configfs fabric infrastructure * for QLogic target mode HBAs * - * ?? Copyright 2010-2011 RisingTide Systems LLC. + * (c) Copyright 2010-2013 Datera, Inc. * - * Licensed to the Linux Foundation under the General Public License (GPL) - * version 2. - * - * Author: Nicholas A. Bellinger <nab@risingtidesystems.com> + * 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. @@ -56,16 +53,6 @@ struct workqueue_struct *tcm_qla2xxx_free_wq; struct workqueue_struct *tcm_qla2xxx_cmd_wq; -static int tcm_qla2xxx_check_true(struct se_portal_group *se_tpg) -{ - return 1; -} - -static int tcm_qla2xxx_check_false(struct se_portal_group *se_tpg) -{ - return 0; -} - /* * Parse WWN. * If strict, we require lower-case hex and colon separators to be sure @@ -177,7 +164,7 @@ static int tcm_qla2xxx_npiv_parse_wwn( *wwnn = 0; /* count may include a LF at end of string */ - if (name[cnt-1] == '\n') + if (name[cnt-1] == '\n' || name[cnt-1] == 0) cnt--; /* validate we have enough characters for WWPN */ @@ -195,20 +182,6 @@ static int tcm_qla2xxx_npiv_parse_wwn( return 0; } -static ssize_t tcm_qla2xxx_npiv_format_wwn(char *buf, size_t len, - u64 wwpn, u64 wwnn) -{ - u8 b[8], b2[8]; - - put_unaligned_be64(wwpn, b); - put_unaligned_be64(wwnn, b2); - return snprintf(buf, len, - "%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x," - "%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], - b2[0], b2[1], b2[2], b2[3], b2[4], b2[5], b2[6], b2[7]); -} - static char *tcm_qla2xxx_npiv_get_fabric_name(void) { return "qla2xxx_npiv"; @@ -240,15 +213,6 @@ static char *tcm_qla2xxx_get_fabric_wwn(struct se_portal_group *se_tpg) return lport->lport_naa_name; } -static char *tcm_qla2xxx_npiv_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_npiv_name[0]; -} - static u16 tcm_qla2xxx_get_tag(struct se_portal_group *se_tpg) { struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, @@ -333,7 +297,7 @@ 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 QLA_TPG_ATTRIB(tpg)->generate_node_acls; + return tpg->tpg_attrib.generate_node_acls; } static int tcm_qla2xxx_check_demo_mode_cache(struct se_portal_group *se_tpg) @@ -341,7 +305,7 @@ 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 QLA_TPG_ATTRIB(tpg)->cache_dynamic_acls; + return tpg->tpg_attrib.cache_dynamic_acls; } static int tcm_qla2xxx_check_demo_write_protect(struct se_portal_group *se_tpg) @@ -349,7 +313,7 @@ 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 QLA_TPG_ATTRIB(tpg)->demo_mode_write_protect; + return tpg->tpg_attrib.demo_mode_write_protect; } static int tcm_qla2xxx_check_prod_write_protect(struct se_portal_group *se_tpg) @@ -357,7 +321,7 @@ 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 QLA_TPG_ATTRIB(tpg)->prod_mode_write_protect; + return tpg->tpg_attrib.prod_mode_write_protect; } static int tcm_qla2xxx_check_demo_mode_login_only(struct se_portal_group *se_tpg) @@ -365,7 +329,7 @@ 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 QLA_TPG_ATTRIB(tpg)->demo_mode_login_only; + return tpg->tpg_attrib.demo_mode_login_only; } static struct se_node_acl *tcm_qla2xxx_alloc_fabric_acl( @@ -508,6 +472,11 @@ static int tcm_qla2xxx_write_pending(struct se_cmd *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. @@ -603,8 +572,13 @@ static void tcm_qla2xxx_handle_data_work(struct work_struct *work) return; } - transport_generic_request_failure(&cmd->se_cmd, - TCM_CHECK_CONDITION_ABORT_CMD); + 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; } @@ -620,6 +594,27 @@ static void tcm_qla2xxx_handle_data(struct qla_tgt_cmd *cmd) 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() */ @@ -646,6 +641,11 @@ static int tcm_qla2xxx_queue_data_in(struct se_cmd *se_cmd) 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 */ @@ -720,6 +720,20 @@ static void tcm_qla2xxx_queue_tm_rsp(struct se_cmd *se_cmd) 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; @@ -780,6 +794,9 @@ static void tcm_qla2xxx_put_session(struct se_session *se_sess) 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); } @@ -850,7 +867,7 @@ static ssize_t tcm_qla2xxx_tpg_attrib_show_##name( \ struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, \ struct tcm_qla2xxx_tpg, se_tpg); \ \ - return sprintf(page, "%u\n", QLA_TPG_ATTRIB(tpg)->name); \ + return sprintf(page, "%u\n", tpg->tpg_attrib.name); \ } \ \ static ssize_t tcm_qla2xxx_tpg_attrib_store_##name( \ @@ -951,16 +968,41 @@ static ssize_t tcm_qla2xxx_tpg_show_enable( 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 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 qla_hw_data *ha = vha->hw; struct tcm_qla2xxx_tpg *tpg = container_of(se_tpg, struct tcm_qla2xxx_tpg, se_tpg); unsigned long op; @@ -975,19 +1017,28 @@ static ssize_t tcm_qla2xxx_tpg_store_enable( pr_err("Illegal value for tpg_enable: %lu\n", op); return -EINVAL; } - if (op) { - atomic_set(&tpg->lport_tpg_enabled, 1); - qlt_enable_vha(vha); + if (atomic_read(&tpg->lport_tpg_enabled)) + return -EEXIST; + + INIT_WORK(&tpg->tpg_base_work, tcm_qla2xxx_depend_tpg); } else { - if (!ha->tgt.qla_tgt) { - pr_err("truct qla_hw_data *ha->tgt.qla_tgt is NULL\n"); - return -ENODEV; - } - atomic_set(&tpg->lport_tpg_enabled, 0); - qlt_stop_phase1(ha->tgt.qla_tgt); + 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; } @@ -1014,7 +1065,7 @@ static struct se_portal_group *tcm_qla2xxx_make_tpg( if (kstrtoul(name + 5, 10, &tpgt) || tpgt > USHRT_MAX) return ERR_PTR(-EINVAL); - if (!lport->qla_npiv_vp && (tpgt != 1)) { + if ((tpgt != 1)) { pr_err("In non NPIV mode, a single TPG=1 is used for HW port mappings\n"); return ERR_PTR(-ENOSYS); } @@ -1030,10 +1081,10 @@ static struct se_portal_group *tcm_qla2xxx_make_tpg( * By default allow READ-ONLY TPG demo-mode access w/ cached dynamic * NodeACLs */ - QLA_TPG_ATTRIB(tpg)->generate_node_acls = 1; - QLA_TPG_ATTRIB(tpg)->demo_mode_write_protect = 1; - QLA_TPG_ATTRIB(tpg)->cache_dynamic_acls = 1; - QLA_TPG_ATTRIB(tpg)->demo_mode_login_only = 1; + 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); @@ -1041,11 +1092,8 @@ static struct se_portal_group *tcm_qla2xxx_make_tpg( kfree(tpg); return NULL; } - /* - * Setup local TPG=1 pointer for non NPIV mode. - */ - if (lport->qla_npiv_vp == NULL) - lport->tpg_1 = tpg; + + lport->tpg_1 = tpg; return &tpg->se_tpg; } @@ -1056,24 +1104,75 @@ static void tcm_qla2xxx_drop_tpg(struct se_portal_group *se_tpg) struct tcm_qla2xxx_tpg, se_tpg); struct tcm_qla2xxx_lport *lport = tpg->lport; struct scsi_qla_host *vha = lport->qla_vha; - struct qla_hw_data *ha = vha->hw; /* * Call into qla2x_target.c LLD logic to shutdown the active * FC Nexuses and disable target mode operation for this qla_hw_data */ - if (ha->tgt.qla_tgt && !ha->tgt.qla_tgt->tgt_stop) - qlt_stop_phase1(ha->tgt.qla_tgt); + 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. */ - if (lport->qla_npiv_vp == NULL) - lport->tpg_1 = NULL; - + 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, @@ -1098,12 +1197,22 @@ static struct se_portal_group *tcm_qla2xxx_npiv_make_tpg( 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; } @@ -1114,13 +1223,12 @@ static struct qla_tgt_sess *tcm_qla2xxx_find_sess_by_s_id( scsi_qla_host_t *vha, const uint8_t *s_id) { - struct qla_hw_data *ha = vha->hw; struct tcm_qla2xxx_lport *lport; struct se_node_acl *se_nacl; struct tcm_qla2xxx_nacl *nacl; u32 key; - lport = ha->tgt.target_lport_ptr; + lport = vha->vha_tgt.target_lport_ptr; if (!lport) { pr_err("Unable to locate struct tcm_qla2xxx_lport\n"); dump_stack(); @@ -1224,13 +1332,12 @@ static struct qla_tgt_sess *tcm_qla2xxx_find_sess_by_loop_id( scsi_qla_host_t *vha, const uint16_t loop_id) { - struct qla_hw_data *ha = vha->hw; struct tcm_qla2xxx_lport *lport; struct se_node_acl *se_nacl; struct tcm_qla2xxx_nacl *nacl; struct tcm_qla2xxx_fc_loopid *fc_loopid; - lport = ha->tgt.target_lport_ptr; + lport = vha->vha_tgt.target_lport_ptr; if (!lport) { pr_err("Unable to locate struct tcm_qla2xxx_lport\n"); dump_stack(); @@ -1344,6 +1451,7 @@ 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; @@ -1360,7 +1468,7 @@ static void tcm_qla2xxx_free_session(struct qla_tgt_sess *sess) se_nacl = se_sess->se_node_acl; nacl = container_of(se_nacl, struct tcm_qla2xxx_nacl, se_node_acl); - lport = ha->tgt.target_lport_ptr; + lport = vha->vha_tgt.target_lport_ptr; if (!lport) { pr_err("Unable to locate struct tcm_qla2xxx_lport\n"); dump_stack(); @@ -1393,8 +1501,10 @@ static int tcm_qla2xxx_check_initiator_node_acl( 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 = ha->tgt.target_lport_ptr; + lport = vha->vha_tgt.target_lport_ptr; if (!lport) { pr_err("Unable to locate struct tcm_qla2xxx_lport\n"); dump_stack(); @@ -1410,7 +1520,9 @@ static int tcm_qla2xxx_check_initiator_node_acl( } se_tpg = &tpg->se_tpg; - se_sess = transport_init_session(); + 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); @@ -1458,7 +1570,8 @@ static void tcm_qla2xxx_update_sess(struct qla_tgt_sess *sess, port_id_t s_id, { struct qla_tgt *tgt = sess->tgt; struct qla_hw_data *ha = tgt->ha; - struct tcm_qla2xxx_lport *lport = ha->tgt.target_lport_ptr; + 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); @@ -1466,15 +1579,11 @@ static void tcm_qla2xxx_update_sess(struct qla_tgt_sess *sess, port_id_t s_id, if (sess->loop_id != loop_id || sess->s_id.b24 != s_id.b24) - pr_info("Updating session %p from port %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x loop_id %d -> %d s_id %x:%x:%x -> %x:%x:%x\n", - sess, - sess->port_name[0], sess->port_name[1], - sess->port_name[2], sess->port_name[3], - sess->port_name[4], sess->port_name[5], - sess->port_name[6], sess->port_name[7], - 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); + 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) { /* @@ -1531,6 +1640,7 @@ static void tcm_qla2xxx_update_sess(struct qla_tgt_sess *sess, port_id_t s_id, 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, @@ -1569,15 +1679,18 @@ static int tcm_qla2xxx_init_lport(struct tcm_qla2xxx_lport *lport) return 0; } -static int tcm_qla2xxx_lport_register_cb(struct scsi_qla_host *vha) +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 *lport = + (struct tcm_qla2xxx_lport *)target_lport_ptr; /* - * Setup local pointer to vha, NPIV VP pointer (if present) and - * vha->tcm_lport pointer + * Setup tgt_ops, local pointer to vha and target_lport_ptr */ - lport = (struct tcm_qla2xxx_lport *)ha->tgt.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; @@ -1609,8 +1722,8 @@ static struct se_wwn *tcm_qla2xxx_make_lport( if (ret != 0) goto out; - ret = qlt_lport_register(&tcm_qla2xxx_template, wwpn, - tcm_qla2xxx_lport_register_cb, lport); + ret = qlt_lport_register(lport, wwpn, 0, 0, + tcm_qla2xxx_lport_register_cb); if (ret != 0) goto out_lport; @@ -1628,7 +1741,6 @@ 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 qla_hw_data *ha = vha->hw; struct se_node_acl *node; u32 key = 0; @@ -1637,8 +1749,8 @@ static void tcm_qla2xxx_drop_lport(struct se_wwn *wwn) * shutdown of struct qla_tgt after the call to * qlt_stop_phase1() from tcm_qla2xxx_drop_tpg() above.. */ - if (ha->tgt.qla_tgt && !ha->tgt.qla_tgt->tgt_stopped) - qlt_stop_phase2(ha->tgt.qla_tgt); + 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); @@ -1649,17 +1761,79 @@ static void tcm_qla2xxx_drop_lport(struct se_wwn *wwn) 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 npiv_wwpn, npiv_wwnn; + u64 phys_wwpn, npiv_wwpn, npiv_wwnn; + char *p, tmp[128]; int ret; - if (tcm_qla2xxx_npiv_parse_wwn(name, strlen(name)+1, - &npiv_wwpn, &npiv_wwnn) < 0) + 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); @@ -1669,16 +1843,21 @@ static struct se_wwn *tcm_qla2xxx_npiv_make_lport( } lport->lport_npiv_wwpn = npiv_wwpn; lport->lport_npiv_wwnn = npiv_wwnn; - tcm_qla2xxx_npiv_format_wwn(&lport->lport_npiv_name[0], - TCM_QLA2XXX_NAMELEN, npiv_wwpn, npiv_wwnn); sprintf(lport->lport_naa_name, "naa.%016llx", (unsigned long long) npiv_wwpn); -/* FIXME: tcm_qla2xxx_npiv_make_lport */ - ret = -ENOSYS; + 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); @@ -1688,14 +1867,16 @@ 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 *vha = lport->qla_vha; - struct Scsi_Host *sh = vha->host; + 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 lport->npiv_vport + * Notify libfc that we want to release the vha->fc_vport */ - fc_vport_terminate(lport->npiv_vport); - - scsi_host_put(sh); + fc_vport_terminate(npiv_vha->fc_vport); + scsi_host_put(base_vha->host); kfree(lport); } @@ -1751,6 +1932,7 @@ static struct target_core_fabric_ops tcm_qla2xxx_ops = { .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 @@ -1770,20 +1952,22 @@ static struct target_core_fabric_ops tcm_qla2xxx_ops = { 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_npiv_get_fabric_wwn, + .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_false, - .tpg_check_demo_mode_cache = tcm_qla2xxx_check_true, - .tpg_check_demo_mode_write_protect = tcm_qla2xxx_check_true, - .tpg_check_prod_mode_write_protect = tcm_qla2xxx_check_false, + .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, @@ -1798,6 +1982,7 @@ static struct target_core_fabric_ops tcm_qla2xxx_npiv_ops = { .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 @@ -1837,16 +2022,16 @@ static int tcm_qla2xxx_register_configfs(void) /* * Setup default attribute lists for various fabric->tf_cit_tmpl */ - TF_CIT_TMPL(fabric)->tfc_wwn_cit.ct_attrs = tcm_qla2xxx_wwn_attrs; - TF_CIT_TMPL(fabric)->tfc_tpg_base_cit.ct_attrs = tcm_qla2xxx_tpg_attrs; - TF_CIT_TMPL(fabric)->tfc_tpg_attrib_cit.ct_attrs = + 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; - TF_CIT_TMPL(fabric)->tfc_tpg_param_cit.ct_attrs = NULL; - TF_CIT_TMPL(fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL; - TF_CIT_TMPL(fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL; - TF_CIT_TMPL(fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL; - TF_CIT_TMPL(fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL; - TF_CIT_TMPL(fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL; + 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 */ @@ -1877,15 +2062,16 @@ static int tcm_qla2xxx_register_configfs(void) /* * Setup default attribute lists for various npiv_fabric->tf_cit_tmpl */ - TF_CIT_TMPL(npiv_fabric)->tfc_wwn_cit.ct_attrs = tcm_qla2xxx_wwn_attrs; - TF_CIT_TMPL(npiv_fabric)->tfc_tpg_base_cit.ct_attrs = NULL; - TF_CIT_TMPL(npiv_fabric)->tfc_tpg_attrib_cit.ct_attrs = NULL; - TF_CIT_TMPL(npiv_fabric)->tfc_tpg_param_cit.ct_attrs = NULL; - TF_CIT_TMPL(npiv_fabric)->tfc_tpg_np_base_cit.ct_attrs = NULL; - TF_CIT_TMPL(npiv_fabric)->tfc_tpg_nacl_base_cit.ct_attrs = NULL; - TF_CIT_TMPL(npiv_fabric)->tfc_tpg_nacl_attrib_cit.ct_attrs = NULL; - TF_CIT_TMPL(npiv_fabric)->tfc_tpg_nacl_auth_cit.ct_attrs = NULL; - TF_CIT_TMPL(npiv_fabric)->tfc_tpg_nacl_param_cit.ct_attrs = NULL; + 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 */ diff --git a/drivers/scsi/qla2xxx/tcm_qla2xxx.h b/drivers/scsi/qla2xxx/tcm_qla2xxx.h index 329327528a5..10c00214564 100644 --- a/drivers/scsi/qla2xxx/tcm_qla2xxx.h +++ b/drivers/scsi/qla2xxx/tcm_qla2xxx.h @@ -4,8 +4,11 @@ #define TCM_QLA2XXX_VERSION "v0.1" /* length of ASCII WWPNs including pad */ #define TCM_QLA2XXX_NAMELEN 32 -/* lenth of ASCII NPIV 'WWPN+WWNN' including pad */ -#define TCM_QLA2XXX_NPIV_NAMELEN 66 +/* + * 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" @@ -43,10 +46,11 @@ struct tcm_qla2xxx_tpg { 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; }; -#define QLA_TPG_ATTRIB(tpg) (&(tpg)->tpg_attrib) - struct tcm_qla2xxx_fc_loopid { struct se_node_acl *se_nacl; }; @@ -64,20 +68,14 @@ struct tcm_qla2xxx_lport { char lport_name[TCM_QLA2XXX_NAMELEN]; /* ASCII formatted naa WWPN for VPD page 83 etc */ char lport_naa_name[TCM_QLA2XXX_NAMELEN]; - /* ASCII formatted WWPN+WWNN for NPIV FC Target Lport */ - char lport_npiv_name[TCM_QLA2XXX_NPIV_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 scsi_qla_host for NPIV VP from qla2xxx LLD */ - struct scsi_qla_host *qla_npiv_vp; /* Pointer to struct qla_tgt pointer */ struct qla_tgt lport_qla_tgt; - /* Pointer to struct fc_vport for NPIV vport from libfc */ - struct fc_vport *npiv_vport; /* Pointer to TPG=1 for non NPIV mode */ struct tcm_qla2xxx_tpg *tpg_1; /* Returned by tcm_qla2xxx_make_lport() */ |
