diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-02 19:01:32 -0700 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2012-10-02 19:01:32 -0700 |
commit | 3151367f8778a1789d6f6e6f6c642681b6cd6d64 (patch) | |
tree | 1869d5429a25abd994ae94079808b8db060ec6f3 /drivers/scsi/qla2xxx | |
parent | 16642a2e7be23bbda013fc32d8f6c68982eab603 (diff) | |
parent | fe709ed827d370e6b0c0a9f9456da1c22bdcd118 (diff) | |
download | linux-3151367f8778a1789d6f6e6f6c642681b6cd6d64.tar.bz2 |
Merge tag 'scsi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi
Pull first round of SCSI updates from James Bottomley:
"This is a large set of updates, mostly for drivers (qla2xxx [including
support for new 83xx based card], qla4xxx, mpt2sas, bfa, zfcp, hpsa,
be2iscsi, isci, lpfc, ipr, ibmvfc, ibmvscsi, megaraid_sas).
There's also a rework for tape adding virtually unlimited numbers of
tape drives plus a set of dif fixes for sd and a fix for a live lock
on hot remove of SCSI devices.
This round includes a signed tag pull of isci-for-3.6
Signed-off-by: James Bottomley <JBottomley@Parallels.com>"
Fix up trivial conflict in drivers/scsi/qla2xxx/qla_nx.c due to new PCI
helper function use in a function that was removed by this pull.
* tag 'scsi-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi: (198 commits)
[SCSI] st: remove st_mutex
[SCSI] sd: Ensure we correctly disable devices with unknown protection type
[SCSI] hpsa: gen8plus Smart Array IDs
[SCSI] qla4xxx: Update driver version to 5.03.00-k1
[SCSI] qla4xxx: Disable generating pause frames for ISP83XX
[SCSI] qla4xxx: Fix double clearing of risc_intr for ISP83XX
[SCSI] qla4xxx: IDC implementation for Loopback
[SCSI] qla4xxx: update copyrights in LICENSE.qla4xxx
[SCSI] qla4xxx: Fix panic while rmmod
[SCSI] qla4xxx: Fail probe_adapter if IRQ allocation fails
[SCSI] qla4xxx: Prevent MSI/MSI-X falling back to INTx for ISP82XX
[SCSI] qla4xxx: Update idc reg in case of PCI AER
[SCSI] qla4xxx: Fix double IDC locking in qla4_8xxx_error_recovery
[SCSI] qla4xxx: Clear interrupt while unloading driver for ISP83XX
[SCSI] qla4xxx: Print correct IDC version
[SCSI] qla4xxx: Added new mbox cmd to pass driver version to FW
[SCSI] scsi_dh_alua: Enable STPG for unavailable ports
[SCSI] scsi_remove_target: fix softlockup regression on hot remove
[SCSI] ibmvscsi: Fix host config length field overflow
[SCSI] ibmvscsi: Remove backend abstraction
...
Diffstat (limited to 'drivers/scsi/qla2xxx')
-rw-r--r-- | drivers/scsi/qla2xxx/qla_attr.c | 147 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_bsg.c | 376 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_bsg.h | 30 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_dbg.c | 29 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_dbg.h | 2 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_def.h | 166 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_dfs.c | 2 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_fw.h | 49 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_gbl.h | 39 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_gs.c | 4 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_init.c | 565 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_inline.h | 28 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_iocb.c | 261 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_isr.c | 437 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_mbx.c | 224 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_mid.c | 5 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_nx.c | 151 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_nx.h | 17 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_os.c | 770 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_settings.h | 2 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_sup.c | 67 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_version.h | 6 |
22 files changed, 2961 insertions, 416 deletions
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 5ab953029f8d..1c28215f8bed 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-2011 QLogic Corporation + * Copyright (c) 2003-2012 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -26,7 +26,7 @@ qla2x00_sysfs_read_fw_dump(struct file *filp, struct kobject *kobj, struct qla_hw_data *ha = vha->hw; int rval = 0; - if (ha->fw_dump_reading == 0) + if (!(ha->fw_dump_reading || ha->mctp_dump_reading)) return 0; if (IS_QLA82XX(ha)) { @@ -39,9 +39,14 @@ qla2x00_sysfs_read_fw_dump(struct file *filp, struct kobject *kobj, rval = memory_read_from_buffer(buf, count, &off, ha->md_dump, ha->md_dump_size); return rval; - } else + } else if (ha->mctp_dumped && ha->mctp_dump_reading) + return memory_read_from_buffer(buf, count, &off, ha->mctp_dump, + MCTP_DUMP_SIZE); + else if (ha->fw_dump_reading) return memory_read_from_buffer(buf, count, &off, ha->fw_dump, ha->fw_dump_len); + else + return 0; } static ssize_t @@ -107,6 +112,22 @@ qla2x00_sysfs_write_fw_dump(struct file *filp, struct kobject *kobj, if (IS_QLA82XX(ha)) set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); break; + case 6: + if (!ha->mctp_dump_reading) + break; + ql_log(ql_log_info, vha, 0x70c1, + "MCTP dump cleared on (%ld).\n", vha->host_no); + ha->mctp_dump_reading = 0; + ha->mctp_dumped = 0; + break; + case 7: + if (ha->mctp_dumped && !ha->mctp_dump_reading) { + ha->mctp_dump_reading = 1; + ql_log(ql_log_info, vha, 0x70c2, + "Raw mctp dump ready for read on (%ld).\n", + vha->host_no); + } + break; } return count; } @@ -564,6 +585,7 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj, struct qla_hw_data *ha = vha->hw; struct scsi_qla_host *base_vha = pci_get_drvdata(ha->pdev); int type; + uint32_t idc_control; if (off != 0) return -EINVAL; @@ -587,22 +609,36 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj, scsi_unblock_requests(vha->host); break; case 0x2025d: - if (!IS_QLA81XX(ha) || !IS_QLA8031(ha)) + if (!IS_QLA81XX(ha) && !IS_QLA83XX(ha)) return -EPERM; ql_log(ql_log_info, vha, 0x706f, "Issuing MPI reset.\n"); - /* Make sure FC side is not in reset */ - qla2x00_wait_for_hba_online(vha); - - /* Issue MPI reset */ - scsi_block_requests(vha->host); - if (qla81xx_restart_mpi_firmware(vha) != QLA_SUCCESS) - ql_log(ql_log_warn, vha, 0x7070, - "MPI reset failed.\n"); - scsi_unblock_requests(vha->host); - break; + if (IS_QLA83XX(ha)) { + uint32_t idc_control; + + qla83xx_idc_lock(vha, 0); + __qla83xx_get_idc_control(vha, &idc_control); + idc_control |= QLA83XX_IDC_GRACEFUL_RESET; + __qla83xx_set_idc_control(vha, idc_control); + qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, + QLA8XXX_DEV_NEED_RESET); + qla83xx_idc_audit(vha, IDC_AUDIT_TIMESTAMP); + qla83xx_idc_unlock(vha, 0); + break; + } else { + /* Make sure FC side is not in reset */ + qla2x00_wait_for_hba_online(vha); + + /* Issue MPI reset */ + scsi_block_requests(vha->host); + if (qla81xx_restart_mpi_firmware(vha) != QLA_SUCCESS) + ql_log(ql_log_warn, vha, 0x7070, + "MPI reset failed.\n"); + scsi_unblock_requests(vha->host); + break; + } case 0x2025e: if (!IS_QLA82XX(ha) || vha != base_vha) { ql_log(ql_log_info, vha, 0x7071, @@ -616,6 +652,29 @@ qla2x00_sysfs_write_reset(struct file *filp, struct kobject *kobj, qla2xxx_wake_dpc(vha); qla2x00_wait_for_fcoe_ctx_reset(vha); break; + case 0x2025f: + if (!IS_QLA8031(ha)) + return -EPERM; + ql_log(ql_log_info, vha, 0x70bc, + "Disabling Reset by IDC control\n"); + qla83xx_idc_lock(vha, 0); + __qla83xx_get_idc_control(vha, &idc_control); + idc_control |= QLA83XX_IDC_RESET_DISABLED; + __qla83xx_set_idc_control(vha, idc_control); + qla83xx_idc_unlock(vha, 0); + break; + case 0x20260: + if (!IS_QLA8031(ha)) + return -EPERM; + ql_log(ql_log_info, vha, 0x70bd, + "Enabling Reset by IDC control\n"); + qla83xx_idc_lock(vha, 0); + __qla83xx_get_idc_control(vha, &idc_control); + idc_control &= ~QLA83XX_IDC_RESET_DISABLED; + __qla83xx_set_idc_control(vha, idc_control); + qla83xx_idc_unlock(vha, 0); + break; + } return count; } @@ -1251,6 +1310,49 @@ qla2x00_fw_state_show(struct device *dev, struct device_attribute *attr, state[1], state[2], state[3], state[4]); } +static ssize_t +qla2x00_diag_requests_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + + if (!IS_BIDI_CAPABLE(vha->hw)) + return snprintf(buf, PAGE_SIZE, "\n"); + + return snprintf(buf, PAGE_SIZE, "%llu\n", vha->bidi_stats.io_count); +} + +static ssize_t +qla2x00_diag_megabytes_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + + if (!IS_BIDI_CAPABLE(vha->hw)) + return snprintf(buf, PAGE_SIZE, "\n"); + + return snprintf(buf, PAGE_SIZE, "%llu\n", + vha->bidi_stats.transfer_bytes >> 20); +} + +static ssize_t +qla2x00_fw_dump_size_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + scsi_qla_host_t *vha = shost_priv(class_to_shost(dev)); + struct qla_hw_data *ha = vha->hw; + uint32_t size; + + if (!ha->fw_dumped) + size = 0; + else if (IS_QLA82XX(ha)) + size = ha->md_template_size + ha->md_dump_size; + else + size = ha->fw_dump_len; + + return snprintf(buf, PAGE_SIZE, "%d\n", size); +} + static DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show, NULL); static DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL); static DEVICE_ATTR(serial_num, S_IRUGO, qla2x00_serial_num_show, NULL); @@ -1289,6 +1391,9 @@ static DEVICE_ATTR(vn_port_mac_address, S_IRUGO, static DEVICE_ATTR(fabric_param, S_IRUGO, qla2x00_fabric_param_show, NULL); static DEVICE_ATTR(fw_state, S_IRUGO, qla2x00_fw_state_show, NULL); static DEVICE_ATTR(thermal_temp, S_IRUGO, qla2x00_thermal_temp_show, NULL); +static DEVICE_ATTR(diag_requests, S_IRUGO, qla2x00_diag_requests_show, NULL); +static DEVICE_ATTR(diag_megabytes, S_IRUGO, qla2x00_diag_megabytes_show, NULL); +static DEVICE_ATTR(fw_dump_size, S_IRUGO, qla2x00_fw_dump_size_show, NULL); struct device_attribute *qla2x00_host_attrs[] = { &dev_attr_driver_version, @@ -1318,6 +1423,9 @@ struct device_attribute *qla2x00_host_attrs[] = { &dev_attr_fw_state, &dev_attr_optrom_gold_fw_version, &dev_attr_thermal_temp, + &dev_attr_diag_requests, + &dev_attr_diag_megabytes, + &dev_attr_fw_dump_size, NULL, }; @@ -1704,7 +1812,7 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable) if (IS_T10_PI_CAPABLE(ha) && ql2xenabledif) { if (ha->fw_attributes & BIT_4) { - int prot = 0; + int prot = 0, guard; vha->flags.difdix_supported = 1; ql_dbg(ql_dbg_user, vha, 0x7082, "Registered for DIF/DIX type 1 and 3 protection.\n"); @@ -1717,7 +1825,14 @@ qla24xx_vport_create(struct fc_vport *fc_vport, bool disable) | SHOST_DIX_TYPE1_PROTECTION | SHOST_DIX_TYPE2_PROTECTION | SHOST_DIX_TYPE3_PROTECTION); - scsi_host_set_guard(vha->host, SHOST_DIX_GUARD_CRC); + + guard = SHOST_DIX_GUARD_CRC; + + if (IS_PI_IPGUARD_CAPABLE(ha) && + (ql2xenabledif > 1 || IS_PI_DIFB_DIX0_CAPABLE(ha))) + guard |= SHOST_DIX_GUARD_IP; + + scsi_host_set_guard(vha->host, guard); } else vha->flags.difdix_supported = 0; } diff --git a/drivers/scsi/qla2xxx/qla_bsg.c b/drivers/scsi/qla2xxx/qla_bsg.c index c68883806c54..2f9bddd3c616 100644 --- a/drivers/scsi/qla2xxx/qla_bsg.c +++ b/drivers/scsi/qla2xxx/qla_bsg.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2012 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -530,13 +530,13 @@ done_unmap_sg: done: return rval; } - -/* Set the port configuration to enable the - * internal loopback on ISP81XX +/* + * Set the port configuration to enable the internal or external loopback + * depending on the loopback mode. */ static inline int -qla81xx_set_internal_loopback(scsi_qla_host_t *vha, uint16_t *config, - uint16_t *new_config) +qla81xx_set_loopback_mode(scsi_qla_host_t *vha, uint16_t *config, + uint16_t *new_config, uint16_t mode) { int ret = 0; int rval = 0; @@ -545,8 +545,14 @@ qla81xx_set_internal_loopback(scsi_qla_host_t *vha, uint16_t *config, if (!IS_QLA81XX(ha) && !IS_QLA8031(ha)) goto done_set_internal; - new_config[0] = config[0] | (ENABLE_INTERNAL_LOOPBACK << 1); - memcpy(&new_config[1], &config[1], sizeof(uint16_t) * 3) ; + if (mode == INTERNAL_LOOPBACK) + new_config[0] = config[0] | (ENABLE_INTERNAL_LOOPBACK << 1); + else if (mode == EXTERNAL_LOOPBACK) + new_config[0] = config[0] | (ENABLE_EXTERNAL_LOOPBACK << 1); + ql_dbg(ql_dbg_user, vha, 0x70be, + "new_config[0]=%02x\n", (new_config[0] & INTERNAL_LOOPBACK_MASK)); + + memcpy(&new_config[1], &config[1], sizeof(uint16_t) * 3); ha->notify_dcbx_comp = 1; ret = qla81xx_set_port_config(vha, new_config); @@ -562,9 +568,17 @@ qla81xx_set_internal_loopback(scsi_qla_host_t *vha, uint16_t *config, if (!wait_for_completion_timeout(&ha->dcbx_comp, (20 * HZ))) { ql_dbg(ql_dbg_user, vha, 0x7022, "State change notification not received.\n"); - } else - ql_dbg(ql_dbg_user, vha, 0x7023, - "State change received.\n"); + rval = -EINVAL; + } else { + if (ha->flags.idc_compl_status) { + ql_dbg(ql_dbg_user, vha, 0x70c3, + "Bad status in IDC Completion AEN\n"); + rval = -EINVAL; + ha->flags.idc_compl_status = 0; + } else + ql_dbg(ql_dbg_user, vha, 0x7023, + "State change received.\n"); + } ha->notify_dcbx_comp = 0; @@ -572,11 +586,9 @@ done_set_internal: return rval; } -/* Set the port configuration to disable the - * internal loopback on ISP81XX - */ +/* Disable loopback mode */ static inline int -qla81xx_reset_internal_loopback(scsi_qla_host_t *vha, uint16_t *config, +qla81xx_reset_loopback_mode(scsi_qla_host_t *vha, uint16_t *config, int wait) { int ret = 0; @@ -589,8 +601,12 @@ qla81xx_reset_internal_loopback(scsi_qla_host_t *vha, uint16_t *config, memset(new_config, 0 , sizeof(new_config)); if ((config[0] & INTERNAL_LOOPBACK_MASK) >> 1 == - ENABLE_INTERNAL_LOOPBACK) { + ENABLE_INTERNAL_LOOPBACK || + (config[0] & INTERNAL_LOOPBACK_MASK) >> 1 == + ENABLE_EXTERNAL_LOOPBACK) { new_config[0] = config[0] & ~INTERNAL_LOOPBACK_MASK; + ql_dbg(ql_dbg_user, vha, 0x70bf, "new_config[0]=%02x\n", + (new_config[0] & INTERNAL_LOOPBACK_MASK)); memcpy(&new_config[1], &config[1], sizeof(uint16_t) * 3) ; ha->notify_dcbx_comp = wait; @@ -707,7 +723,8 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job) elreq.options = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; - if ((ha->current_topology == ISP_CFG_F || + if (atomic_read(&vha->loop_state) == LOOP_READY && + (ha->current_topology == ISP_CFG_F || ((IS_QLA81XX(ha) || IS_QLA8031(ha)) && le32_to_cpu(*(uint32_t *)req_data) == ELS_OPCODE_BYTE && req_data_len == MAX_ELS_FRAME_PAYLOAD)) && @@ -729,30 +746,24 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job) goto done_free_dma_req; } - if (elreq.options != EXTERNAL_LOOPBACK) { - ql_dbg(ql_dbg_user, vha, 0x7020, - "Internal: current port config = %x\n", - config[0]); - if (qla81xx_set_internal_loopback(vha, config, - new_config)) { - ql_log(ql_log_warn, vha, 0x7024, - "Internal loopback failed.\n"); - bsg_job->reply->result = - (DID_ERROR << 16); - rval = -EPERM; - goto done_free_dma_req; - } - } else { - /* For external loopback to work - * ensure internal loopback is disabled - */ - if (qla81xx_reset_internal_loopback(vha, - config, 1)) { - bsg_job->reply->result = - (DID_ERROR << 16); - rval = -EPERM; - goto done_free_dma_req; - } + ql_dbg(ql_dbg_user, vha, 0x70c0, + "elreq.options=%04x\n", elreq.options); + + if (elreq.options == EXTERNAL_LOOPBACK) + if (IS_QLA8031(ha)) + rval = qla81xx_set_loopback_mode(vha, + config, new_config, elreq.options); + else + rval = qla81xx_reset_loopback_mode(vha, + config, 1); + else + rval = qla81xx_set_loopback_mode(vha, config, + new_config, elreq.options); + + if (rval) { + bsg_job->reply->result = (DID_ERROR << 16); + rval = -EPERM; + goto done_free_dma_req; } type = "FC_BSG_HST_VENDOR_LOOPBACK"; @@ -766,7 +777,7 @@ qla2x00_process_loopback(struct fc_bsg_job *bsg_job) /* Revert back to original port config * Also clear internal loopback */ - qla81xx_reset_internal_loopback(vha, + qla81xx_reset_loopback_mode(vha, new_config, 0); } @@ -1364,7 +1375,7 @@ qla2x00_read_optrom(struct fc_bsg_job *bsg_job) struct qla_hw_data *ha = vha->hw; int rval = 0; - if (ha->flags.isp82xx_reset_hdlr_active) + if (ha->flags.nic_core_reset_hdlr_active) return -EBUSY; rval = qla2x00_optrom_setup(bsg_job, vha, 0); @@ -1560,6 +1571,276 @@ done: } static int +qla2x00_write_i2c(struct fc_bsg_job *bsg_job) +{ + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + int rval = 0; + uint8_t bsg[DMA_POOL_SIZE]; + struct qla_i2c_access *i2c = (void *)bsg; + dma_addr_t sfp_dma; + uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); + if (!sfp) { + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_NO_MEMORY; + goto done; + } + + sg_copy_to_buffer(bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, i2c, sizeof(*i2c)); + + memcpy(sfp, i2c->buffer, i2c->length); + rval = qla2x00_write_sfp(vha, sfp_dma, sfp, + i2c->device, i2c->offset, i2c->length, i2c->option); + + if (rval) { + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_MAILBOX; + goto dealloc; + } + + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = 0; + +dealloc: + dma_pool_free(ha->s_dma_pool, sfp, sfp_dma); + +done: + bsg_job->reply_len = sizeof(struct fc_bsg_reply); + bsg_job->reply->result = DID_OK << 16; + bsg_job->job_done(bsg_job); + + return 0; +} + +static int +qla2x00_read_i2c(struct fc_bsg_job *bsg_job) +{ + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + int rval = 0; + uint8_t bsg[DMA_POOL_SIZE]; + struct qla_i2c_access *i2c = (void *)bsg; + dma_addr_t sfp_dma; + uint8_t *sfp = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, &sfp_dma); + if (!sfp) { + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_NO_MEMORY; + goto done; + } + + sg_copy_to_buffer(bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, i2c, sizeof(*i2c)); + + rval = qla2x00_read_sfp(vha, sfp_dma, sfp, + i2c->device, i2c->offset, i2c->length, i2c->option); + + if (rval) { + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = + EXT_STATUS_MAILBOX; + goto dealloc; + } + + memcpy(i2c->buffer, sfp, i2c->length); + sg_copy_from_buffer(bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, i2c, sizeof(*i2c)); + + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = 0; + +dealloc: + dma_pool_free(ha->s_dma_pool, sfp, sfp_dma); + +done: + bsg_job->reply_len = sizeof(struct fc_bsg_reply); + bsg_job->reply->reply_payload_rcv_len = sizeof(*i2c); + bsg_job->reply->result = DID_OK << 16; + bsg_job->job_done(bsg_job); + + return 0; +} + +static int +qla24xx_process_bidir_cmd(struct fc_bsg_job *bsg_job) +{ + struct Scsi_Host *host = bsg_job->shost; + scsi_qla_host_t *vha = shost_priv(host); + struct qla_hw_data *ha = vha->hw; + uint16_t thread_id; + uint32_t rval = EXT_STATUS_OK; + uint16_t req_sg_cnt = 0; + uint16_t rsp_sg_cnt = 0; + uint16_t nextlid = 0; + uint32_t tot_dsds; + srb_t *sp = NULL; + uint32_t req_data_len = 0; + uint32_t rsp_data_len = 0; + + /* Check the type of the adapter */ + if (!IS_BIDI_CAPABLE(ha)) { + ql_log(ql_log_warn, vha, 0x70a0, + "This adapter is not supported\n"); + rval = EXT_STATUS_NOT_SUPPORTED; + goto done; + } + + if (test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags) || + test_bit(ABORT_ISP_ACTIVE, &vha->dpc_flags) || + test_bit(ISP_ABORT_RETRY, &vha->dpc_flags)) { + rval = EXT_STATUS_BUSY; + goto done; + } + + /* Check if host is online */ + if (!vha->flags.online) { + ql_log(ql_log_warn, vha, 0x70a1, + "Host is not online\n"); + rval = EXT_STATUS_DEVICE_OFFLINE; + goto done; + } + + /* Check if cable is plugged in or not */ + if (vha->device_flags & DFLG_NO_CABLE) { + ql_log(ql_log_warn, vha, 0x70a2, + "Cable is unplugged...\n"); + rval = EXT_STATUS_INVALID_CFG; + goto done; + } + + /* Check if the switch is connected or not */ + if (ha->current_topology != ISP_CFG_F) { + ql_log(ql_log_warn, vha, 0x70a3, + "Host is not connected to the switch\n"); + rval = EXT_STATUS_INVALID_CFG; + goto done; + } + + /* Check if operating mode is P2P */ + if (ha->operating_mode != P2P) { + ql_log(ql_log_warn, vha, 0x70a4, + "Host is operating mode is not P2p\n"); + rval = EXT_STATUS_INVALID_CFG; + goto done; + } + + thread_id = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; + + mutex_lock(&ha->selflogin_lock); + if (vha->self_login_loop_id == 0) { + /* Initialize all required fields of fcport */ + vha->bidir_fcport.vha = vha; + vha->bidir_fcport.d_id.b.al_pa = vha->d_id.b.al_pa; + vha->bidir_fcport.d_id.b.area = vha->d_id.b.area; + vha->bidir_fcport.d_id.b.domain = vha->d_id.b.domain; + vha->bidir_fcport.loop_id = vha->loop_id; + + if (qla2x00_fabric_login(vha, &(vha->bidir_fcport), &nextlid)) { + ql_log(ql_log_warn, vha, 0x70a7, + "Failed to login port %06X for bidirectional IOCB\n", + vha->bidir_fcport.d_id.b24); + mutex_unlock(&ha->selflogin_lock); + rval = EXT_STATUS_MAILBOX; + goto done; + } + vha->self_login_loop_id = nextlid - 1; + + } + /* Assign the self login loop id to fcport */ + mutex_unlock(&ha->selflogin_lock); + + vha->bidir_fcport.loop_id = vha->self_login_loop_id; + + req_sg_cnt = dma_map_sg(&ha->pdev->dev, + bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, + DMA_TO_DEVICE); + + if (!req_sg_cnt) { + rval = EXT_STATUS_NO_MEMORY; + goto done; + } + + rsp_sg_cnt = dma_map_sg(&ha->pdev->dev, + bsg_job->reply_payload.sg_list, bsg_job->reply_payload.sg_cnt, + DMA_FROM_DEVICE); + + if (!rsp_sg_cnt) { + rval = EXT_STATUS_NO_MEMORY; + goto done_unmap_req_sg; + } + + if ((req_sg_cnt != bsg_job->request_payload.sg_cnt) || + (rsp_sg_cnt != bsg_job->reply_payload.sg_cnt)) { + ql_dbg(ql_dbg_user, vha, 0x70a9, + "Dma mapping resulted in different sg counts " + "[request_sg_cnt: %x dma_request_sg_cnt: %x reply_sg_cnt: " + "%x dma_reply_sg_cnt: %x]\n", + bsg_job->request_payload.sg_cnt, req_sg_cnt, + bsg_job->reply_payload.sg_cnt, rsp_sg_cnt); + rval = EXT_STATUS_NO_MEMORY; + goto done_unmap_sg; + } + + if (req_data_len != rsp_data_len) { + rval = EXT_STATUS_BUSY; + ql_log(ql_log_warn, vha, 0x70aa, + "req_data_len != rsp_data_len\n"); + goto done_unmap_sg; + } + + req_data_len = bsg_job->request_payload.payload_len; + rsp_data_len = bsg_job->reply_payload.payload_len; + + + /* Alloc SRB structure */ + sp = qla2x00_get_sp(vha, &(vha->bidir_fcport), GFP_KERNEL); + if (!sp) { + ql_dbg(ql_dbg_user, vha, 0x70ac, + "Alloc SRB structure failed\n"); + rval = EXT_STATUS_NO_MEMORY; + goto done_unmap_sg; + } + + /*Populate srb->ctx with bidir ctx*/ + sp->u.bsg_job = bsg_job; + sp->free = qla2x00_bsg_sp_free; + sp->type = SRB_BIDI_CMD; + sp->done = qla2x00_bsg_job_done; + + /* Add the read and write sg count */ + tot_dsds = rsp_sg_cnt + req_sg_cnt; + + rval = qla2x00_start_bidir(sp, vha, tot_dsds); + if (rval != EXT_STATUS_OK) + goto done_free_srb; + /* the bsg request will be completed in the interrupt handler */ + return rval; + +done_free_srb: + mempool_free(sp, ha->srb_mempool); +done_unmap_sg: + dma_unmap_sg(&ha->pdev->dev, + bsg_job->reply_payload.sg_list, + bsg_job->reply_payload.sg_cnt, DMA_FROM_DEVICE); +done_unmap_req_sg: + dma_unmap_sg(&ha->pdev->dev, + bsg_job->request_payload.sg_list, + bsg_job->request_payload.sg_cnt, DMA_TO_DEVICE); +done: + + /* Return an error vendor specific response + * and complete the bsg request + */ + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = rval; + bsg_job->reply_len = sizeof(struct fc_bsg_reply); + bsg_job->reply->reply_payload_rcv_len = 0; + bsg_job->reply->result = (DID_OK) << 16; + bsg_job->job_done(bsg_job); + /* Always retrun success, vendor rsp carries correct status */ + 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]) { @@ -1596,6 +1877,15 @@ qla2x00_process_vendor_specific(struct fc_bsg_job *bsg_job) case QL_VND_WRITE_FRU_STATUS: return qla2x00_write_fru_status(bsg_job); + case QL_VND_WRITE_I2C: + return qla2x00_write_i2c(bsg_job); + + case QL_VND_READ_I2C: + return qla2x00_read_i2c(bsg_job); + + case QL_VND_DIAG_IO_CMD: + return qla24xx_process_bidir_cmd(bsg_job); + default: bsg_job->reply->result = (DID_ERROR << 16); bsg_job->job_done(bsg_job); diff --git a/drivers/scsi/qla2xxx/qla_bsg.h b/drivers/scsi/qla2xxx/qla_bsg.h index 70caa63a8930..37b8b7ba7421 100644 --- a/drivers/scsi/qla2xxx/qla_bsg.h +++ b/drivers/scsi/qla2xxx/qla_bsg.h @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2012 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -19,21 +19,41 @@ #define QL_VND_SET_FRU_VERSION 0x0B #define QL_VND_READ_FRU_STATUS 0x0C #define QL_VND_WRITE_FRU_STATUS 0x0D +#define QL_VND_DIAG_IO_CMD 0x0A +#define QL_VND_WRITE_I2C 0x10 +#define QL_VND_READ_I2C 0x11 /* BSG Vendor specific subcode returns */ #define EXT_STATUS_OK 0 #define EXT_STATUS_ERR 1 +#define EXT_STATUS_BUSY 2 #define EXT_STATUS_INVALID_PARAM 6 +#define EXT_STATUS_DATA_OVERRUN 7 +#define EXT_STATUS_DATA_UNDERRUN 8 #define EXT_STATUS_MAILBOX 11 #define EXT_STATUS_NO_MEMORY 17 +#define EXT_STATUS_DEVICE_OFFLINE 22 + +/* + * To support bidirectional iocb + * BSG Vendor specific returns + */ +#define EXT_STATUS_NOT_SUPPORTED 27 +#define EXT_STATUS_INVALID_CFG 28 +#define EXT_STATUS_DMA_ERR 29 +#define EXT_STATUS_TIMEOUT 30 +#define EXT_STATUS_THREAD_FAILED 31 +#define EXT_STATUS_DATA_CMP_FAILED 32 /* BSG definations for interpreting CommandSent field */ #define INT_DEF_LB_LOOPBACK_CMD 0 #define INT_DEF_LB_ECHO_CMD 1 /* Loopback related definations */ +#define INTERNAL_LOOPBACK 0xF1 #define EXTERNAL_LOOPBACK 0xF2 #define ENABLE_INTERNAL_LOOPBACK 0x02 +#define ENABLE_EXTERNAL_LOOPBACK 0x04 #define INTERNAL_LOOPBACK_MASK 0x000E #define MAX_ELS_FRAME_PAYLOAD 252 #define ELS_OPCODE_BYTE 0x10 @@ -183,4 +203,12 @@ struct qla_status_reg { uint8_t reserved[7]; } __packed; +struct qla_i2c_access { + uint16_t device; + uint16_t offset; + uint16_t option; + uint16_t length; + uint8_t buffer[0x40]; +} __packed; + #endif diff --git a/drivers/scsi/qla2xxx/qla_dbg.c b/drivers/scsi/qla2xxx/qla_dbg.c index fdee5611f3e2..44efe3cc79e6 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-2011 QLogic Corporation + * Copyright (c) 2003-2012 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -11,26 +11,31 @@ * ---------------------------------------------------------------------- * | Level | Last Value Used | Holes | * ---------------------------------------------------------------------- - * | Module Init and Probe | 0x0122 | 0x4b,0xba,0xfa | - * | Mailbox commands | 0x1140 | 0x111a-0x111b | + * | Module Init and Probe | 0x0124 | 0x4b,0xba,0xfa | + * | Mailbox commands | 0x114f | 0x111a-0x111b | * | | | 0x112c-0x112e | * | | | 0x113a | - * | Device Discovery | 0x2086 | 0x2020-0x2022 | - * | Queue Command and IO tracing | 0x3030 | 0x3006,0x3008 | + * | Device Discovery | 0x2087 | 0x2020-0x2022, | + * | | | 0x2016 | + * | Queue Command and IO tracing | 0x3030 | 0x3006-0x300b | + * | | | 0x3027-0x3028 | * | | | 0x302d-0x302e | - * | DPC Thread | 0x401c | 0x4002,0x4013 | - * | Async Events | 0x505f | 0x502b-0x502f | + * | DPC Thread | 0x401d | 0x4002,0x4013 | + * | Async Events | 0x5071 | 0x502b-0x502f | * | | | 0x5047,0x5052 | * | Timer Routines | 0x6011 | | - * | User Space Interactions | 0x709f | 0x7018,0x702e, | + * | User Space Interactions | 0x70c3 | 0x7018,0x702e, | * | | | 0x7039,0x7045, | * | | | 0x7073-0x7075, | - * | | | 0x708c | + * | | | 0x708c, | + * | | | 0x70a5,0x70a6, | + * | | | 0x70a8,0x70ab, | + * | | | 0x70ad-0x70ae | * | Task Management | 0x803c | 0x8025-0x8026 | * | | | 0x800b,0x8039 | * | AER/EEH | 0x9011 | | * | Virtual Port | 0xa007 | | - * | ISP82XX Specific | 0xb054 | 0xb024 | + * | ISP82XX Specific | 0xb084 | 0xb002,0xb024 | * | MultiQ | 0xc00c | | * | Misc | 0xd010 | | * | Target Mode | 0xe06f | | @@ -2357,7 +2362,7 @@ ql_dbg(uint32_t level, scsi_qla_host_t *vha, int32_t id, const char *fmt, ...) /* * This function is for formatting and logging debug information. - * It is to be used when vha is not available and pci is availble, + * It is to be used when vha is not available and pci is available, * i.e., before host allocation. It formats the message and logs it * to the messages file. * parameters: @@ -2452,7 +2457,7 @@ ql_log(uint32_t level, scsi_qla_host_t *vha, int32_t id, const char *fmt, ...) /* * This function is for formatting and logging log messages. - * It is to be used when vha is not available and pci is availble, + * It is to be used when vha is not available and pci is available, * i.e., before host allocation. It formats the message and logs * it to the messages file. All the messages are logged irrespective * of the value of ql2xextended_error_logging. diff --git a/drivers/scsi/qla2xxx/qla_dbg.h b/drivers/scsi/qla2xxx/qla_dbg.h index f278df8cce0f..8f911c0b1e74 100644 --- a/drivers/scsi/qla2xxx/qla_dbg.h +++ b/drivers/scsi/qla2xxx/qla_dbg.h @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2012 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 39007f53aec0..a9725bf5527b 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2012 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -115,6 +115,82 @@ #define WRT_REG_DWORD(addr, data) writel(data,addr) /* + * ISP83XX specific remote register addresses + */ +#define QLA83XX_LED_PORT0 0x00201320 +#define QLA83XX_LED_PORT1 0x00201328 +#define QLA83XX_IDC_DEV_STATE 0x22102384 +#define QLA83XX_IDC_MAJOR_VERSION 0x22102380 +#define QLA83XX_IDC_MINOR_VERSION 0x22102398 +#define QLA83XX_IDC_DRV_PRESENCE 0x22102388 +#define QLA83XX_IDC_DRIVER_ACK 0x2210238c +#define QLA83XX_IDC_CONTROL 0x22102390 +#define QLA83XX_IDC_AUDIT 0x22102394 +#define QLA83XX_IDC_LOCK_RECOVERY 0x2210239c +#define QLA83XX_DRIVER_LOCKID 0x22102104 +#define QLA83XX_DRIVER_LOCK 0x8111c028 +#define QLA83XX_DRIVER_UNLOCK 0x8111c02c +#define QLA83XX_FLASH_LOCKID 0x22102100 +#define QLA83XX_FLASH_LOCK 0x8111c010 +#define QLA83XX_FLASH_UNLOCK 0x8111c014 +#define QLA83XX_DEV_PARTINFO1 0x221023e0 +#define QLA83XX_DEV_PARTINFO2 0x221023e4 +#define QLA83XX_FW_HEARTBEAT 0x221020b0 +#define QLA83XX_PEG_HALT_STATUS1 0x221020a8 +#define QLA83XX_PEG_HALT_STATUS2 0x221020ac + +/* 83XX: Macros defining 8200 AEN Reason codes */ +#define IDC_DEVICE_STATE_CHANGE BIT_0 +#define IDC_PEG_HALT_STATUS_CHANGE BIT_1 +#define IDC_NIC_FW_REPORTED_FAILURE BIT_2 +#define IDC_HEARTBEAT_FAILURE BIT_3 + +/* 83XX: Macros defining 8200 AEN Error-levels */ +#define ERR_LEVEL_NON_FATAL 0x1 +#define ERR_LEVEL_RECOVERABLE_FATAL 0x2 +#define ERR_LEVEL_UNRECOVERABLE_FATAL 0x4 + +/* 83XX: Macros for IDC Version */ +#define QLA83XX_SUPP_IDC_MAJOR_VERSION 0x01 +#define QLA83XX_SUPP_IDC_MINOR_VERSION 0x0 + +/* 83XX: Macros for scheduling dpc tasks */ +#define QLA83XX_NIC_CORE_RESET 0x1 +#define QLA83XX_IDC_STATE_HANDLER 0x2 +#define QLA83XX_NIC_CORE_UNRECOVERABLE 0x3 + +/* 83XX: Macros for defining IDC-Control bits */ +#define QLA83XX_IDC_RESET_DISABLED BIT_0 +#define QLA83XX_IDC_GRACEFUL_RESET BIT_1 + +/* 83XX: Macros for different timeouts */ +#define QLA83XX_IDC_INITIALIZATION_TIMEOUT 30 +#define QLA83XX_IDC_RESET_ACK_TIMEOUT 10 +#define QLA83XX_MAX_LOCK_RECOVERY_WAIT (2 * HZ) + +/* 83XX: Macros for defining class in DEV-Partition Info register */ +#define QLA83XX_CLASS_TYPE_NONE 0x0 +#define QLA83XX_CLASS_TYPE_NIC 0x1 +#define QLA83XX_CLASS_TYPE_FCOE 0x2 +#define QLA83XX_CLASS_TYPE_ISCSI 0x3 + +/* 83XX: Macros for IDC Lock-Recovery stages */ +#define IDC_LOCK_RECOVERY_STAGE1 0x1 /* Stage1: Intent for + * lock-recovery + */ +#define IDC_LOCK_RECOVERY_STAGE2 0x2 /* Stage2: Perform lock-recovery */ + +/* 83XX: Macros for IDC Audit type */ +#define IDC_AUDIT_TIMESTAMP 0x0 /* IDC-AUDIT: Record timestamp of + * dev-state change to NEED-RESET + * or NEED-QUIESCENT + */ +#define IDC_AUDIT_COMPLETION 0x1 /* IDC-AUDIT: Record duration of + * reset-recovery completion is + * second + */ + +/* * The ISP2312 v2 chip cannot access the FLASH/GPIO registers via MMIO in an * 133Mhz slot. */ @@ -129,6 +205,7 @@ #define MAX_FIBRE_DEVICES_2400 2048 #define MAX_FIBRE_DEVICES_LOOP 128 #define MAX_FIBRE_DEVICES_MAX MAX_FIBRE_DEVICES_2400 +#define LOOPID_MAP_SIZE (ha->max_fibre_devices) #define MAX_FIBRE_LUNS 0xFFFF #define MAX_HOST_COUNT 16 @@ -259,6 +336,7 @@ struct srb_iocb { #define SRB_ADISC_CMD 6 #define SRB_TM_CMD 7 #define SRB_SCSI_CMD 8 +#define SRB_BIDI_CMD 9 typedef struct srb { atomic_t ref_count; @@ -594,6 +672,20 @@ typedef struct { #define MBA_DISCARD_RND_FRAME 0x8048 /* discard RND frame due to error. */ #define MBA_REJECTED_FCP_CMD 0x8049 /* rejected FCP_CMD. */ +/* 83XX FCoE specific */ +#define MBA_IDC_AEN 0x8200 /* FCoE: NIC Core state change AEN */ + +/* Interrupt type codes */ +#define INTR_ROM_MB_SUCCESS 0x1 +#define INTR_ROM_MB_FAILED 0x2 +#define INTR_MB_SUCCESS 0x10 +#define INTR_MB_FAILED 0x11 +#define INTR_ASYNC_EVENT 0x12 +#define INTR_RSP_QUE_UPDATE 0x13 +#define INTR_RSP_QUE_UPDATE_83XX 0x14 +#define INTR_ATIO_QUE_UPDATE 0x1C +#define INTR_ATIO_RSP_QUE_UPDATE 0x1D + /* ISP mailbox loopback echo diagnostic error code */ #define MBS_LB_RESET 0x17 /* @@ -718,6 +810,7 @@ typedef struct { #define MBC_SEND_RNFT_ELS 0x5e /* Send RNFT ELS request */ #define MBC_GET_LINK_PRIV_STATS 0x6d /* Get link & private data. */ #define MBC_SET_VENDOR_ID 0x76 /* Set Vendor ID. */ +#define MBC_PORT_RESET 0x120 /* Port Reset */ #define MBC_SET_PORT_CONFIG 0x122 /* Set port configuration */ #define MBC_GET_PORT_CONFIG 0x123 /* Get port configuration */ @@ -1375,9 +1468,10 @@ typedef struct { } cont_a64_entry_t; #define PO_MODE_DIF_INSERT 0 -#define PO_MODE_DIF_REMOVE BIT_0 -#define PO_MODE_DIF_PASS BIT_1 -#define PO_MODE_DIF_REPLACE (BIT_0 + BIT_1) +#define PO_MODE_DIF_REMOVE 1 +#define PO_MODE_DIF_PASS 2 +#define PO_MODE_DIF_REPLACE 3 +#define PO_MODE_DIF_TCP_CKSUM 6 #define PO_ENABLE_DIF_BUNDLING BIT_8 #define PO_ENABLE_INCR_GUARD_SEED BIT_3 #define PO_DISABLE_INCR_REF_TAG BIT_5 @@ -1509,6 +1603,13 @@ typedef struct { #define CS_RETRY 0x82 /* Driver defined */ #define CS_LOOP_DOWN_ABORT 0x83 /* Driver defined */ +#define CS_BIDIR_RD_OVERRUN 0x700 +#define CS_BIDIR_RD_WR_OVERRUN 0x707 +#define CS_BIDIR_RD_OVERRUN_WR_UNDERRUN 0x715 +#define CS_BIDIR_RD_UNDERRUN 0x1500 +#define CS_BIDIR_RD_UNDERRUN_WR_OVERRUN 0x1507 +#define CS_BIDIR_RD_WR_UNDERRUN 0x1515 +#define CS_BIDIR_DMA 0x200 /* * Status entry status flags */ @@ -2373,6 +2474,11 @@ struct qla_statistics { uint64_t output_bytes; }; +struct bidi_statistics { + unsigned long long io_count; + unsigned long long transfer_bytes; +}; + /* Multi queue support */ #define MBC_INITIALIZE_MULTIQ 0x1f #define QLA_QUE_PAGE 0X1000 @@ -2509,14 +2615,16 @@ struct qla_hw_data { uint32_t disable_msix_handshake :1; uint32_t fcp_prio_enabled :1; uint32_t isp82xx_fw_hung:1; + uint32_t nic_core_hung:1; uint32_t quiesce_owner:1; uint32_t thermal_supported:1; - uint32_t isp82xx_reset_hdlr_active:1; - uint32_t isp82xx_reset_owner:1; + uint32_t nic_core_reset_hdlr_active:1; + uint32_t nic_core_reset_owner:1; uint32_t isp82xx_no_md_cap:1; uint32_t host_shutting_down:1; - /* 30 bits */ + uint32_t idc_compl_status:1; + /* 32 bits */ } flags; /* This spinlock is used to protect "io transactions", you must @@ -2670,6 +2778,16 @@ struct qla_hw_data { #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_BIDI_CAPABLE(ha) ((IS_QLA25XX(ha) || IS_QLA2031(ha))) +/* Bit 21 of fw_attributes decides the MCTP capabilities */ +#define IS_MCTP_CAPABLE(ha) (IS_QLA2031(ha) && \ + ((ha)->fw_attributes_ext[0] & BIT_0)) +#define IS_PI_UNINIT_CAPABLE(ha) (IS_QLA83XX(ha)) +#define IS_PI_IPGUARD_CAPABLE(ha) (IS_QLA83XX(ha)) +#define IS_PI_DIFB_DIX0_CAPABLE(ha) (0) +#define IS_PI_SPLIT_DET_CAPABLE_HBA(ha) (IS_QLA83XX(ha)) +#define IS_PI_SPLIT_DET_CAPABLE(ha) (IS_PI_SPLIT_DET_CAPABLE_HBA(ha) && \ + (((ha)->fw_attributes_h << 16 | (ha)->fw_attributes) & BIT_22)) /* HBA serial number */ uint8_t serial0; @@ -2753,6 +2871,7 @@ struct qla_hw_data { struct completion mbx_intr_comp; /* Used for completion notification */ struct completion dcbx_comp; /* For set port config notification */ int notify_dcbx_comp; + struct mutex selflogin_lock; /* Basic firmware related information. */ uint16_t fw_major_version; @@ -2784,7 +2903,12 @@ struct qla_hw_data { int fw_dump_reading; dma_addr_t eft_dma; void *eft; - +/* Current size of mctp dump is 0x086064 bytes */ +#define MCTP_DUMP_SIZE 0x086064 + dma_addr_t mctp_dump_dma; + void *mctp_dump; + int mctp_dumped; + int mctp_dump_reading; uint32_t chain_offset; struct dentry *dfs_dir; struct dentry *dfs_fce; @@ -2896,8 +3020,8 @@ struct qla_hw_data { unsigned long mn_win_crb; unsigned long ms_win_crb; int qdr_sn_window; - uint32_t nx_dev_init_timeout; - uint32_t nx_reset_timeout; + uint32_t fcoe_dev_init_timeout; + uint32_t fcoe_reset_timeout; rwlock_t hw_lock; uint16_t portnum; /* port number */ int link_width; @@ -2918,6 +3042,20 @@ struct qla_hw_data { void *md_dump; uint32_t md_dump_size; + void *loop_id_map; + + /* QLA83XX IDC specific fields */ + uint32_t idc_audit_ts; + + /* DPC low-priority workqueue */ + struct workqueue_struct *dpc_lp_wq; + struct work_struct idc_aen; + /* DPC high-priority workqueue */ + struct workqueue_struct *dpc_hp_wq; + struct work_struct nic_core_reset; + struct work_struct idc_state_handler; + struct work_struct nic_core_unrecoverable; + struct qlt_hw_data tgt; }; @@ -2985,6 +3123,13 @@ typedef struct scsi_qla_host { /* ISP configuration data. */ uint16_t loop_id; /* Host adapter loop id */ + uint16_t self_login_loop_id; /* host adapter loop id + * get it on self login + */ + fc_port_t bidir_fcport; /* fcport used for bidir cmnds + * no need of allocating it for + * each command + */ port_id_t d_id; /* Host adapter port id */ uint8_t marker_needed; @@ -3038,6 +3183,7 @@ typedef struct scsi_qla_host { int seconds_since_last_heartbeat; struct fc_host_statistics fc_host_stat; struct qla_statistics qla_stats; + struct bidi_statistics bidi_stats; atomic_t vref_count; } scsi_qla_host_t; diff --git a/drivers/scsi/qla2xxx/qla_dfs.c b/drivers/scsi/qla2xxx/qla_dfs.c index 499c74e39ee5..706c4f7bc7c9 100644 --- a/drivers/scsi/qla2xxx/qla_dfs.c +++ b/drivers/scsi/qla2xxx/qla_dfs.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2012 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h index 6d7d7758c797..59524aa0ab32 100644 --- a/drivers/scsi/qla2xxx/qla_fw.h +++ b/drivers/scsi/qla2xxx/qla_fw.h @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2012 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -381,6 +381,44 @@ struct init_cb_24xx { /* * ISP queue - command entry structure definition. */ +#define COMMAND_BIDIRECTIONAL 0x75 +struct cmd_bidir { + uint8_t entry_type; /* Entry type. */ + uint8_t entry_count; /* Entry count. */ + uint8_t sys_define; /* System defined */ + uint8_t entry_status; /* Entry status. */ + + uint32_t handle; /* System handle. */ + + uint16_t nport_handle; /* N_PORT hanlde. */ + + uint16_t timeout; /* Commnad timeout. */ + + uint16_t wr_dseg_count; /* Write Data segment count. */ + uint16_t rd_dseg_count; /* Read Data segment count. */ + + struct scsi_lun lun; /* FCP LUN (BE). */ + + uint16_t control_flags; /* Control flags. */ +#define BD_WRAP_BACK BIT_3 +#define BD_READ_DATA BIT_1 +#define BD_WRITE_DATA BIT_0 + + uint16_t fcp_cmnd_dseg_len; /* Data segment length. */ + uint32_t fcp_cmnd_dseg_address[2]; /* Data segment address. */ + + uint16_t reserved[2]; /* Reserved */ + + uint32_t rd_byte_count; /* Total Byte count Read. */ + uint32_t wr_byte_count; /* Total Byte count write. */ + + uint8_t port_id[3]; /* PortID of destination port.*/ + uint8_t vp_index; + + uint32_t fcp_data_dseg_address[2]; /* Data segment address. */ + uint16_t fcp_data_dseg_len; /* Data segment length. */ +}; + #define COMMAND_TYPE_6 0x48 /* Command Type 6 entry */ struct cmd_type_6 { uint8_t entry_type; /* Entry type. */ @@ -1130,7 +1168,7 @@ struct mid_db_entry_24xx { /* * Virtual Port Control IOCB */ -#define VP_CTRL_IOCB_TYPE 0x30 /* Vitual Port Control entry. */ +#define VP_CTRL_IOCB_TYPE 0x30 /* Virtual Port Control entry. */ struct vp_ctrl_entry_24xx { uint8_t entry_type; /* Entry type. */ uint8_t entry_count; /* Entry count. */ @@ -1166,7 +1204,7 @@ struct vp_ctrl_entry_24xx { /* * Modify Virtual Port Configuration IOCB */ -#define VP_CONFIG_IOCB_TYPE 0x31 /* Vitual Port Config entry. */ +#define VP_CONFIG_IOCB_TYPE 0x31 /* Virtual Port Config entry. */ struct vp_config_entry_24xx { uint8_t entry_type; /* Entry type. */ uint8_t entry_count; /* Entry count. */ @@ -1502,7 +1540,10 @@ struct access_chip_rsp_84xx { /* * ISP83xx mailbox commands */ -#define MBC_WRITE_REMOTE_REG 0x0001 /* Write remote register */ +#define MBC_WRITE_REMOTE_REG 0x0001 /* Write remote register */ +#define MBC_READ_REMOTE_REG 0x0009 /* Read remote register */ +#define MBC_RESTART_NIC_FIRMWARE 0x003d /* Restart NIC firmware */ +#define MBC_SET_ACCESS_CONTROL 0x003e /* Access control command */ /* Flash access control option field bit definitions */ #define FAC_OPT_FORCE_SEMAPHORE BIT_15 diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 9eacd2df111b..6acb39785a46 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2012 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -48,7 +48,7 @@ extern void qla2x00_update_fcports(scsi_qla_host_t *); extern int qla2x00_abort_isp(scsi_qla_host_t *); extern void qla2x00_abort_isp_cleanup(scsi_qla_host_t *); -extern void qla82xx_quiescent_state_cleanup(scsi_qla_host_t *); +extern void qla2x00_quiesce_io(scsi_qla_host_t *); extern void qla2x00_update_fcport(scsi_qla_host_t *, fc_port_t *); @@ -76,6 +76,14 @@ extern int qla24xx_update_fcport_fcp_prio(scsi_qla_host_t *, fc_port_t *); extern fc_port_t * qla2x00_alloc_fcport(scsi_qla_host_t *, gfp_t ); + +extern int __qla83xx_set_idc_control(scsi_qla_host_t *, uint32_t); +extern int __qla83xx_get_idc_control(scsi_qla_host_t *, uint32_t *); +extern void qla83xx_idc_audit(scsi_qla_host_t *, int); +extern int qla83xx_nic_core_reset(scsi_qla_host_t *); +extern void qla83xx_reset_ownership(scsi_qla_host_t *); +extern int qla2xxx_mctp_dump(scsi_qla_host_t *); + /* * Global Data in qla_os.c source file. */ @@ -133,6 +141,20 @@ extern void qla2x00_relogin(struct scsi_qla_host *); extern void qla2x00_do_work(struct scsi_qla_host *); extern void qla2x00_free_fcports(struct scsi_qla_host *); +extern void qla83xx_schedule_work(scsi_qla_host_t *, int); +extern void qla83xx_service_idc_aen(struct work_struct *); +extern void qla83xx_nic_core_unrecoverable_work(struct work_struct *); +extern void qla83xx_idc_state_handler_work(struct work_struct *); +extern void qla83xx_nic_core_reset_work(struct work_struct *); + +extern void qla83xx_idc_lock(scsi_qla_host_t *, uint16_t); +extern void qla83xx_idc_unlock(scsi_qla_host_t *, uint16_t); +extern int qla83xx_idc_state_handler(scsi_qla_host_t *); +extern int qla83xx_set_drv_presence(scsi_qla_host_t *vha); +extern int __qla83xx_set_drv_presence(scsi_qla_host_t *vha); +extern int qla83xx_clear_drv_presence(scsi_qla_host_t *vha); +extern int __qla83xx_clear_drv_presence(scsi_qla_host_t *vha); + /* * Global Functions in qla_mid.c source file. */ @@ -188,6 +210,8 @@ extern int qla2x00_start_sp(srb_t *); extern uint16_t qla24xx_calc_iocbs(scsi_qla_host_t *, uint16_t); extern void qla24xx_build_scsi_iocbs(srb_t *, struct cmd_type_7 *, uint16_t); extern int qla24xx_dif_start_scsi(srb_t *); +extern int qla2x00_start_bidir(srb_t *, struct scsi_qla_host *, uint32_t); +extern unsigned long qla2x00_get_async_timeout(struct scsi_qla_host *); extern void *qla2x00_alloc_iocbs(scsi_qla_host_t *, srb_t *); extern int qla2x00_issue_marker(scsi_qla_host_t *, int); @@ -376,6 +400,9 @@ qla81xx_set_port_config(scsi_qla_host_t *, uint16_t *); extern int qla2x00_port_logout(scsi_qla_host_t *, struct fc_port *); +extern int +qla2x00_dump_mctp_data(scsi_qla_host_t *, dma_addr_t, uint32_t, uint32_t); + /* * Global Function Prototypes in qla_isr.c source file. */ @@ -419,7 +446,11 @@ extern void qla24xx_beacon_blink(struct scsi_qla_host *); extern void qla83xx_beacon_blink(struct scsi_qla_host *); extern int qla82xx_beacon_on(struct scsi_qla_host *); extern int qla82xx_beacon_off(struct scsi_qla_host *); -extern int qla83xx_write_remote_reg(struct scsi_qla_host *, uint32_t, uint32_t); +extern int qla83xx_wr_reg(scsi_qla_host_t *, uint32_t, uint32_t); +extern int qla83xx_rd_reg(scsi_qla_host_t *, uint32_t, uint32_t *); +extern int qla83xx_restart_nic_firmware(scsi_qla_host_t *); +extern int qla83xx_access_control(scsi_qla_host_t *, uint16_t, uint32_t, + uint32_t, uint16_t *); extern uint8_t *qla2x00_read_optrom_data(struct scsi_qla_host *, uint8_t *, uint32_t, uint32_t); @@ -527,7 +558,6 @@ extern void qla24xx_wrt_rsp_reg(struct qla_hw_data *, uint16_t, uint16_t); /* PCI related functions */ extern int qla82xx_pci_config(struct scsi_qla_host *); extern int qla82xx_pci_mem_read_2M(struct qla_hw_data *, u64, void *, int); -extern char *qla82xx_pci_info_str(struct scsi_qla_host *, char *); extern int qla82xx_pci_region_offset(struct pci_dev *, int); extern int qla82xx_iospace_config(struct qla_hw_data *); @@ -580,6 +610,7 @@ extern uint32_t qla82xx_wait_for_state_change(scsi_qla_host_t *, uint32_t); extern int qla82xx_idc_lock(struct qla_hw_data *); extern void qla82xx_idc_unlock(struct qla_hw_data *); extern int qla82xx_device_state_handler(scsi_qla_host_t *); +extern void qla8xxx_dev_failed_handler(scsi_qla_host_t *); extern void qla82xx_clear_qsnt_ready(scsi_qla_host_t *); extern void qla2x00_set_model_info(scsi_qla_host_t *, uint8_t *, diff --git a/drivers/scsi/qla2xxx/qla_gs.c b/drivers/scsi/qla2xxx/qla_gs.c index 05260d25fe46..f4e4bd7c3f4d 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-2011 QLogic Corporation + * Copyright (c) 2003-2012 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -1131,7 +1131,7 @@ qla2x00_mgmt_svr_login(scsi_qla_host_t *vha) return ret; rval = ha->isp_ops->fabric_login(vha, vha->mgmt_svr_loop_id, 0xff, 0xff, - 0xfa, mb, BIT_1|BIT_0); + 0xfa, mb, BIT_1); if (rval != QLA_SUCCESS || mb[0] != MBS_COMMAND_COMPLETE) { if (rval == QLA_MEMORY_ALLOC_FAILED) ql_dbg(ql_dbg_disc, vha, 0x2085, diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index a44653b42161..799a58bb9859 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2012 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -77,7 +77,7 @@ qla2x00_sp_free(void *data, void *ptr) /* Asynchronous Login/Logout Routines -------------------------------------- */ -static inline unsigned long +unsigned long qla2x00_get_async_timeout(struct scsi_qla_host *vha) { unsigned long tmo; @@ -429,6 +429,79 @@ qla2x00_async_adisc_done(struct scsi_qla_host *vha, fc_port_t *fcport, /* QLogic ISP2x00 Hardware Support Functions. */ /****************************************************************************/ +int +qla83xx_nic_core_fw_load(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + uint32_t idc_major_ver, idc_minor_ver; + uint16_t config[4]; + + qla83xx_idc_lock(vha, 0); + + /* SV: TODO: Assign initialization timeout from + * flash-info / other param + */ + ha->fcoe_dev_init_timeout = QLA83XX_IDC_INITIALIZATION_TIMEOUT; + ha->fcoe_reset_timeout = QLA83XX_IDC_RESET_ACK_TIMEOUT; + + /* Set our fcoe function presence */ + if (__qla83xx_set_drv_presence(vha) != QLA_SUCCESS) { + ql_dbg(ql_dbg_p3p, vha, 0xb077, + "Error while setting DRV-Presence.\n"); + rval = QLA_FUNCTION_FAILED; + goto exit; + } + + /* Decide the reset ownership */ + qla83xx_reset_ownership(vha); + + /* + * On first protocol driver load: + * Init-Owner: Set IDC-Major-Version and Clear IDC-Lock-Recovery + * register. + * Others: Check compatibility with current IDC Major version. + */ + qla83xx_rd_reg(vha, QLA83XX_IDC_MAJOR_VERSION, &idc_major_ver); + if (ha->flags.nic_core_reset_owner) { + /* Set IDC Major version */ + idc_major_ver = QLA83XX_SUPP_IDC_MAJOR_VERSION; + qla83xx_wr_reg(vha, QLA83XX_IDC_MAJOR_VERSION, idc_major_ver); + + /* Clearing IDC-Lock-Recovery register */ + qla83xx_wr_reg(vha, QLA83XX_IDC_LOCK_RECOVERY, 0); + } else if (idc_major_ver != QLA83XX_SUPP_IDC_MAJOR_VERSION) { + /* + * Clear further IDC participation if we are not compatible with + * the current IDC Major Version. + */ + ql_log(ql_log_warn, vha, 0xb07d, + "Failing load, idc_major_ver=%d, expected_major_ver=%d.\n", + idc_major_ver, QLA83XX_SUPP_IDC_MAJOR_VERSION); + __qla83xx_clear_drv_presence(vha); + rval = QLA_FUNCTION_FAILED; + goto exit; + } + /* Each function sets its supported Minor version. */ + qla83xx_rd_reg(vha, QLA83XX_IDC_MINOR_VERSION, &idc_minor_ver); + idc_minor_ver |= (QLA83XX_SUPP_IDC_MINOR_VERSION << (ha->portnum * 2)); + qla83xx_wr_reg(vha, QLA83XX_IDC_MINOR_VERSION, idc_minor_ver); + + if (ha->flags.nic_core_reset_owner) { + memset(config, 0, sizeof(config)); + if (!qla81xx_get_port_config(vha, config)) + qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, + QLA8XXX_DEV_READY); + } + + rval = qla83xx_idc_state_handler(vha); + +exit: + qla83xx_idc_unlock(vha, 0); + + return rval; +} + /* * qla2x00_initialize_adapter * Initialize board. @@ -537,6 +610,14 @@ qla2x00_initialize_adapter(scsi_qla_host_t *vha) } } + /* Load the NIC Core f/w if we are the first protocol driver. */ + if (IS_QLA8031(ha)) { + rval = qla83xx_nic_core_fw_load(vha); + if (rval) + ql_log(ql_log_warn, vha, 0x0124, + "Error in initializing NIC Core f/w.\n"); + } + if (IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha)) qla24xx_read_fcp_prio_cfg(vha); @@ -686,7 +767,7 @@ qla24xx_pci_config(scsi_qla_host_t *vha) /* PCIe -- adjust Maximum Read Request Size (2048). */ if (pci_is_pcie(ha->pdev)) - pcie_set_readrq(ha->pdev, 2048); + pcie_set_readrq(ha->pdev, 4096); pci_disable_rom(ha->pdev); @@ -722,7 +803,7 @@ qla25xx_pci_config(scsi_qla_host_t *vha) /* PCIe -- adjust Maximum Read Request Size (2048). */ if (pci_is_pcie(ha->pdev)) - pcie_set_readrq(ha->pdev, 2048); + pcie_set_readrq(ha->pdev, 4096); pci_disable_rom(ha->pdev); @@ -1480,7 +1561,8 @@ enable_82xx_npiv: "ISP Firmware failed checksum.\n"); goto failed; } - } + } else + goto failed; if (!IS_FWI2_CAPABLE(ha) && !IS_QLA2100(ha) && !IS_QLA2200(ha)) { /* Enable proper parity. */ @@ -1825,7 +1907,7 @@ qla2x00_init_rings(scsi_qla_host_t *vha) ql_dbg(ql_dbg_init, vha, 0x00d1, "Issue init firmware.\n"); if (ha->flags.npiv_supported) { - if (ha->operating_mode == LOOP) + if (ha->operating_mode == LOOP && !IS_CNA_CAPABLE(ha)) ha->max_npiv_vports = MIN_MULTI_ID_FABRIC - 1; mid_init_cb->count = cpu_to_le16(ha->max_npiv_vports); } @@ -2682,11 +2764,6 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha) new_fcport = NULL; entries = MAX_FIBRE_DEVICES_LOOP; - ql_dbg(ql_dbg_disc, vha, 0x2016, - "Getting FCAL position map.\n"); - if (ql2xextended_error_logging & ql_dbg_disc) - qla2x00_get_fcal_position_map(vha, NULL); - /* Get list of logged in devices. */ memset(ha->gid_list, 0, qla2x00_gid_list_size(ha)); rval = qla2x00_get_id_list(vha, ha->gid_list, ha->gid_list_dma, @@ -2753,6 +2830,8 @@ qla2x00_configure_local_loop(scsi_qla_host_t *vha) if (loop_id > LAST_LOCAL_LOOP_ID) continue; + memset(new_fcport, 0, sizeof(fc_port_t)); + /* Fill in member data. */ new_fcport->d_id.b.domain = domain; new_fcport->d_id.b.area = area; @@ -3285,7 +3364,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, */ if ((fcport->flags & FCF_FABRIC_DEVICE) == 0) { fcport->d_id.b24 = new_fcport->d_id.b24; - fcport->loop_id = FC_NO_LOOP_ID; + qla2x00_clear_loop_id(fcport); fcport->flags |= (FCF_FABRIC_DEVICE | FCF_LOGIN_NEEDED); break; @@ -3306,7 +3385,7 @@ qla2x00_find_all_fabric_devs(scsi_qla_host_t *vha, ha->isp_ops->fabric_logout(vha, fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa); - fcport->loop_id = FC_NO_LOOP_ID; + qla2x00_clear_loop_id(fcport); } break; @@ -3352,71 +3431,32 @@ int qla2x00_find_new_loop_id(scsi_qla_host_t *vha, fc_port_t *dev) { int rval; - int found; - fc_port_t *fcport; - uint16_t first_loop_id; struct qla_hw_data *ha = vha->hw; - struct scsi_qla_host *vp; - struct scsi_qla_host *tvp; unsigned long flags = 0; rval = QLA_SUCCESS; - /* Save starting loop ID. */ - first_loop_id = dev->loop_id; - - for (;;) { - /* Skip loop ID if already used by adapter. */ - if (dev->loop_id == vha->loop_id) - dev->loop_id++; - - /* Skip reserved loop IDs. */ - while (qla2x00_is_reserved_id(vha, dev->loop_id)) - dev->loop_id++; - - /* Reset loop ID if passed the end. */ - if (dev->loop_id > ha->max_loop_id) { - /* first loop ID. */ - dev->loop_id = ha->min_external_loopid; - } - - /* Check for loop ID being already in use. */ - found = 0; - fcport = NULL; - - spin_lock_irqsave(&ha->vport_slock, flags); - list_for_each_entry_safe(vp, tvp, &ha->vp_list, list) { - list_for_each_entry(fcport, &vp->vp_fcports, list) { - if (fcport->loop_id == dev->loop_id && - fcport != dev) { - /* ID possibly in use */ - found++; - break; - } - } - if (found) - break; - } - spin_unlock_irqrestore(&ha->vport_slock, flags); + spin_lock_irqsave(&ha->vport_slock, flags); - /* If not in use then it is free to use. */ - if (!found) { - ql_dbg(ql_dbg_disc, dev->vha, 0x2086, - "Assigning new loopid=%x, portid=%x.\n", - dev->loop_id, dev->d_id.b24); - break; - } + dev->loop_id = find_first_zero_bit(ha->loop_id_map, + LOOPID_MAP_SIZE); + if (dev->loop_id >= LOOPID_MAP_SIZE || + qla2x00_is_reserved_id(vha, dev->loop_id)) { + dev->loop_id = FC_NO_LOOP_ID; + rval = QLA_FUNCTION_FAILED; + } else + set_bit(dev->loop_id, ha->loop_id_map); - /* ID in use. Try next value. */ - dev->loop_id++; + spin_unlock_irqrestore(&ha->vport_slock, flags); - /* If wrap around. No free ID to use. */ - if (dev->loop_id == first_loop_id) { - dev->loop_id = FC_NO_LOOP_ID; - rval = QLA_FUNCTION_FAILED; - break; - } - } + if (rval == QLA_SUCCESS) + ql_dbg(ql_dbg_disc, dev->vha, 0x2086, + "Assigning new loopid=%x, portid=%x.\n", + dev->loop_id, dev->d_id.b24); + else + ql_log(ql_log_warn, dev->vha, 0x2087, + "No loop_id's available, portid=%x.\n", + dev->d_id.b24); return (rval); } @@ -3616,7 +3656,7 @@ qla2x00_fabric_login(scsi_qla_host_t *vha, fc_port_t *fcport, ha->isp_ops->fabric_logout(vha, fcport->loop_id, fcport->d_id.b.domain, fcport->d_id.b.area, fcport->d_id.b.al_pa); - fcport->loop_id = FC_NO_LOOP_ID; + qla2x00_clear_loop_id(fcport); fcport->login_retry = 0; rval = 3; @@ -3775,8 +3815,363 @@ qla2x00_update_fcports(scsi_qla_host_t *base_vha) spin_unlock_irqrestore(&ha->vport_slock, flags); } +/* Assumes idc_lock always held on entry */ +void +qla83xx_reset_ownership(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t drv_presence, drv_presence_mask; + uint32_t dev_part_info1, dev_part_info2, class_type; + uint32_t class_type_mask = 0x3; + uint16_t fcoe_other_function = 0xffff, i; + + qla83xx_rd_reg(vha, QLA83XX_IDC_DRV_PRESENCE, &drv_presence); + + qla83xx_rd_reg(vha, QLA83XX_DEV_PARTINFO1, &dev_part_info1); + qla83xx_rd_reg(vha, QLA83XX_DEV_PARTINFO2, &dev_part_info2); + for (i = 0; i < 8; i++) { + class_type = ((dev_part_info1 >> (i * 4)) & class_type_mask); + if ((class_type == QLA83XX_CLASS_TYPE_FCOE) && + (i != ha->portnum)) { + fcoe_other_function = i; + break; + } + } + if (fcoe_other_function == 0xffff) { + for (i = 0; i < 8; i++) { + class_type = ((dev_part_info2 >> (i * 4)) & + class_type_mask); + if ((class_type == QLA83XX_CLASS_TYPE_FCOE) && + ((i + 8) != ha->portnum)) { + fcoe_other_function = i + 8; + break; + } + } + } + /* + * Prepare drv-presence mask based on fcoe functions present. + * However consider only valid physical fcoe function numbers (0-15). + */ + drv_presence_mask = ~((1 << (ha->portnum)) | + ((fcoe_other_function == 0xffff) ? + 0 : (1 << (fcoe_other_function)))); + + /* We are the reset owner iff: + * - No other protocol drivers present. + * - This is the lowest among fcoe functions. */ + if (!(drv_presence & drv_presence_mask) && + (ha->portnum < fcoe_other_function)) { + ql_dbg(ql_dbg_p3p, vha, 0xb07f, + "This host is Reset owner.\n"); + ha->flags.nic_core_reset_owner = 1; + } +} + +int +__qla83xx_set_drv_ack(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + uint32_t drv_ack; + + rval = qla83xx_rd_reg(vha, QLA83XX_IDC_DRIVER_ACK, &drv_ack); + if (rval == QLA_SUCCESS) { + drv_ack |= (1 << ha->portnum); + rval = qla83xx_wr_reg(vha, QLA83XX_IDC_DRIVER_ACK, drv_ack); + } + + return rval; +} + +int +qla83xx_set_drv_ack(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + + qla83xx_idc_lock(vha, 0); + rval = __qla83xx_set_drv_ack(vha); + qla83xx_idc_unlock(vha, 0); + + return rval; +} + +int +__qla83xx_clear_drv_ack(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + uint32_t drv_ack; + + rval = qla83xx_rd_reg(vha, QLA83XX_IDC_DRIVER_ACK, &drv_ack); + if (rval == QLA_SUCCESS) { + drv_ack &= ~(1 << ha->portnum); + rval = qla83xx_wr_reg(vha, QLA83XX_IDC_DRIVER_ACK, drv_ack); + } + + return rval; +} + +int +qla83xx_clear_drv_ack(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + + qla83xx_idc_lock(vha, 0); + rval = __qla83xx_clear_drv_ack(vha); + qla83xx_idc_unlock(vha, 0); + + return rval; +} + +const char * +qla83xx_dev_state_to_string(uint32_t dev_state) +{ + switch (dev_state) { + case QLA8XXX_DEV_COLD: + return "COLD/RE-INIT"; + case QLA8XXX_DEV_INITIALIZING: + return "INITIALIZING"; + case QLA8XXX_DEV_READY: + return "READY"; + case QLA8XXX_DEV_NEED_RESET: + return "NEED RESET"; + case QLA8XXX_DEV_NEED_QUIESCENT: + return "NEED QUIESCENT"; + case QLA8XXX_DEV_FAILED: + return "FAILED"; + case QLA8XXX_DEV_QUIESCENT: + return "QUIESCENT"; + default: + return "Unknown"; + } +} + +/* Assumes idc-lock always held on entry */ +void +qla83xx_idc_audit(scsi_qla_host_t *vha, int audit_type) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t idc_audit_reg = 0, duration_secs = 0; + + switch (audit_type) { + case IDC_AUDIT_TIMESTAMP: + ha->idc_audit_ts = (jiffies_to_msecs(jiffies) / 1000); + idc_audit_reg = (ha->portnum) | + (IDC_AUDIT_TIMESTAMP << 7) | (ha->idc_audit_ts << 8); + qla83xx_wr_reg(vha, QLA83XX_IDC_AUDIT, idc_audit_reg); + break; + + case IDC_AUDIT_COMPLETION: + duration_secs = ((jiffies_to_msecs(jiffies) - + jiffies_to_msecs(ha->idc_audit_ts)) / 1000); + idc_audit_reg = (ha->portnum) | + (IDC_AUDIT_COMPLETION << 7) | (duration_secs << 8); + qla83xx_wr_reg(vha, QLA83XX_IDC_AUDIT, idc_audit_reg); + break; + + default: + ql_log(ql_log_warn, vha, 0xb078, + "Invalid audit type specified.\n"); + break; + } +} + +/* Assumes idc_lock always held on entry */ +int +qla83xx_initiating_reset(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t idc_control, dev_state; + + __qla83xx_get_idc_control(vha, &idc_control); + if ((idc_control & QLA83XX_IDC_RESET_DISABLED)) { + ql_log(ql_log_info, vha, 0xb080, + "NIC Core reset has been disabled. idc-control=0x%x\n", + idc_control); + return QLA_FUNCTION_FAILED; + } + + /* Set NEED-RESET iff in READY state and we are the reset-owner */ + qla83xx_rd_reg(vha, QLA83XX_IDC_DEV_STATE, &dev_state); + if (ha->flags.nic_core_reset_owner && dev_state == QLA8XXX_DEV_READY) { + qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, + QLA8XXX_DEV_NEED_RESET); + ql_log(ql_log_info, vha, 0xb056, "HW State: NEED RESET.\n"); + qla83xx_idc_audit(vha, IDC_AUDIT_TIMESTAMP); + } else { + const char *state = qla83xx_dev_state_to_string(dev_state); + ql_log(ql_log_info, vha, 0xb057, "HW State: %s.\n", state); + + /* SV: XXX: Is timeout required here? */ + /* Wait for IDC state change READY -> NEED_RESET */ + while (dev_state == QLA8XXX_DEV_READY) { + qla83xx_idc_unlock(vha, 0); + msleep(200); + qla83xx_idc_lock(vha, 0); + qla83xx_rd_reg(vha, QLA83XX_IDC_DEV_STATE, &dev_state); + } + } + + /* Send IDC ack by writing to drv-ack register */ + __qla83xx_set_drv_ack(vha); + + return QLA_SUCCESS; +} + +int +__qla83xx_set_idc_control(scsi_qla_host_t *vha, uint32_t idc_control) +{ + return qla83xx_wr_reg(vha, QLA83XX_IDC_CONTROL, idc_control); +} + +int +qla83xx_set_idc_control(scsi_qla_host_t *vha, uint32_t idc_control) +{ + int rval = QLA_SUCCESS; + + qla83xx_idc_lock(vha, 0); + rval = __qla83xx_set_idc_control(vha, idc_control); + qla83xx_idc_unlock(vha, 0); + + return rval; +} + +int +__qla83xx_get_idc_control(scsi_qla_host_t *vha, uint32_t *idc_control) +{ + return qla83xx_rd_reg(vha, QLA83XX_IDC_CONTROL, idc_control); +} + +int +qla83xx_get_idc_control(scsi_qla_host_t *vha, uint32_t *idc_control) +{ + int rval = QLA_SUCCESS; + + qla83xx_idc_lock(vha, 0); + rval = __qla83xx_get_idc_control(vha, idc_control); + qla83xx_idc_unlock(vha, 0); + + return rval; +} + +int +qla83xx_check_driver_presence(scsi_qla_host_t *vha) +{ + uint32_t drv_presence = 0; + struct qla_hw_data *ha = vha->hw; + + qla83xx_rd_reg(vha, QLA83XX_IDC_DRV_PRESENCE, &drv_presence); + if (drv_presence & (1 << ha->portnum)) + return QLA_SUCCESS; + else + return QLA_TEST_FAILED; +} + +int +qla83xx_nic_core_reset(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + + ql_dbg(ql_dbg_p3p, vha, 0xb058, + "Entered %s().\n", __func__); + + if (vha->device_flags & DFLG_DEV_FAILED) { + ql_log(ql_log_warn, vha, 0xb059, + "Device in unrecoverable FAILED state.\n"); + return QLA_FUNCTION_FAILED; + } + + qla83xx_idc_lock(vha, 0); + + if (qla83xx_check_driver_presence(vha) != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0xb05a, + "Function=0x%x has been removed from IDC participation.\n", + ha->portnum); + rval = QLA_FUNCTION_FAILED; + goto exit; + } + + qla83xx_reset_ownership(vha); + + rval = qla83xx_initiating_reset(vha); + + /* + * Perform reset if we are the reset-owner, + * else wait till IDC state changes to READY/FAILED. + */ + if (rval == QLA_SUCCESS) { + rval = qla83xx_idc_state_handler(vha); + + if (rval == QLA_SUCCESS) + ha->flags.nic_core_hung = 0; + __qla83xx_clear_drv_ack(vha); + } + +exit: + qla83xx_idc_unlock(vha, 0); + + ql_dbg(ql_dbg_p3p, vha, 0xb05b, "Exiting %s.\n", __func__); + + return rval; +} + +int +qla2xxx_mctp_dump(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + int rval = QLA_FUNCTION_FAILED; + + if (!IS_MCTP_CAPABLE(ha)) { + /* This message can be removed from the final version */ + ql_log(ql_log_info, vha, 0x506d, + "This board is not MCTP capable\n"); + return rval; + } + + if (!ha->mctp_dump) { + ha->mctp_dump = dma_alloc_coherent(&ha->pdev->dev, + MCTP_DUMP_SIZE, &ha->mctp_dump_dma, GFP_KERNEL); + + if (!ha->mctp_dump) { + ql_log(ql_log_warn, vha, 0x506e, + "Failed to allocate memory for mctp dump\n"); + return rval; + } + } + +#define MCTP_DUMP_STR_ADDR 0x00000000 + rval = qla2x00_dump_mctp_data(vha, ha->mctp_dump_dma, + MCTP_DUMP_STR_ADDR, MCTP_DUMP_SIZE/4); + if (rval != QLA_SUCCESS) { + ql_log(ql_log_warn, vha, 0x506f, + "Failed to capture mctp dump\n"); + } else { + ql_log(ql_log_info, vha, 0x5070, + "Mctp dump capture for host (%ld/%p).\n", + vha->host_no, ha->mctp_dump); + ha->mctp_dumped = 1; + } + + if (!ha->flags.nic_core_reset_hdlr_active && !ha->portnum) { + ha->flags.nic_core_reset_hdlr_active = 1; + rval = qla83xx_restart_nic_firmware(vha); + if (rval) + /* NIC Core reset failed. */ + ql_log(ql_log_warn, vha, 0x5071, + "Failed to restart nic firmware\n"); + else + ql_dbg(ql_dbg_p3p, vha, 0xb084, + "Restarted NIC firmware successfully.\n"); + ha->flags.nic_core_reset_hdlr_active = 0; + } + + return rval; + +} + /* -* qla82xx_quiescent_state_cleanup +* qla2x00_quiesce_io * Description: This function will block the new I/Os * Its not aborting any I/Os as context * is not destroyed during quiescence @@ -3784,20 +4179,20 @@ qla2x00_update_fcports(scsi_qla_host_t *base_vha) * return : void */ void -qla82xx_quiescent_state_cleanup(scsi_qla_host_t *vha) +qla2x00_quiesce_io(scsi_qla_host_t *vha) { struct qla_hw_data *ha = vha->hw; struct scsi_qla_host *vp; - ql_dbg(ql_dbg_p3p, vha, 0xb002, - "Performing ISP error recovery - ha=%p.\n", ha); + ql_dbg(ql_dbg_dpc, vha, 0x401d, + "Quiescing I/O - ha=%p.\n", ha); atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); if (atomic_read(&vha->loop_state) != LOOP_DOWN) { atomic_set(&vha->loop_state, LOOP_DOWN); qla2x00_mark_all_devices_lost(vha, 0); list_for_each_entry(vp, &ha->vp_list, list) - qla2x00_mark_all_devices_lost(vha, 0); + qla2x00_mark_all_devices_lost(vp, 0); } else { if (!atomic_read(&vha->loop_down_timer)) atomic_set(&vha->loop_down_timer, @@ -3913,6 +4308,14 @@ qla2x00_abort_isp(scsi_qla_host_t *vha) if (vha->flags.online) { qla2x00_abort_isp_cleanup(vha); + if (IS_QLA8031(ha)) { + ql_dbg(ql_dbg_p3p, vha, 0xb05c, + "Clearing fcoe driver presence.\n"); + if (qla83xx_clear_drv_presence(vha) != QLA_SUCCESS) + ql_dbg(ql_dbg_p3p, vha, 0xb073, + "Error while clearing DRV-Presence.\n"); + } + if (unlikely(pci_channel_offline(ha->pdev) && ha->flags.pci_channel_io_perm_failure)) { clear_bit(ISP_ABORT_RETRY, &vha->dpc_flags); @@ -4021,6 +4424,13 @@ qla2x00_abort_isp(scsi_qla_host_t *vha) } spin_unlock_irqrestore(&ha->vport_slock, flags); + if (IS_QLA8031(ha)) { + ql_dbg(ql_dbg_p3p, vha, 0xb05d, + "Setting back fcoe driver presence.\n"); + if (qla83xx_set_drv_presence(vha) != QLA_SUCCESS) + ql_dbg(ql_dbg_p3p, vha, 0xb074, + "Error while setting DRV-Presence.\n"); + } } else { ql_log(ql_log_warn, vha, 0x8023, "%s **** FAILED ****.\n", __func__); @@ -5088,6 +5498,9 @@ qla81xx_nvram_config(scsi_qla_host_t *vha) rval = 1; } + if (IS_T10_PI_CAPABLE(ha)) + nv->frame_payload_size &= ~7; + /* Reset Initialization control block */ memset(icb, 0, ha->init_cb_size); diff --git a/drivers/scsi/qla2xxx/qla_inline.h b/drivers/scsi/qla2xxx/qla_inline.h index 6e457643c639..c0462c04c885 100644 --- a/drivers/scsi/qla2xxx/qla_inline.h +++ b/drivers/scsi/qla2xxx/qla_inline.h @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2012 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -57,6 +57,20 @@ host_to_fcp_swap(uint8_t *fcp, uint32_t bsize) return fcp; } +static inline void +qla2x00_set_reserved_loop_ids(struct qla_hw_data *ha) +{ + int i; + + if (IS_FWI2_CAPABLE(ha)) + return; + + for (i = 0; i < SNS_FIRST_LOOP_ID; i++) + set_bit(i, ha->loop_id_map); + set_bit(MANAGEMENT_SERVER, ha->loop_id_map); + set_bit(BROADCAST, ha->loop_id_map); +} + static inline int qla2x00_is_reserved_id(scsi_qla_host_t *vha, uint16_t loop_id) { @@ -69,6 +83,18 @@ qla2x00_is_reserved_id(scsi_qla_host_t *vha, uint16_t loop_id) } static inline void +qla2x00_clear_loop_id(fc_port_t *fcport) { + struct qla_hw_data *ha = fcport->vha->hw; + + if (fcport->loop_id == FC_NO_LOOP_ID || + qla2x00_is_reserved_id(fcport->vha, fcport->loop_id)) + return; + + clear_bit(fcport->loop_id, ha->loop_id_map); + fcport->loop_id = FC_NO_LOOP_ID; +} + +static inline void qla2x00_clean_dsd_pool(struct qla_hw_data *ha, srb_t *sp) { struct dsd_dma *dsd_ptr, *tdsd_ptr; diff --git a/drivers/scsi/qla2xxx/qla_iocb.c b/drivers/scsi/qla2xxx/qla_iocb.c index 70dbf53d9e0f..03b752632839 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-2011 QLogic Corporation + * Copyright (c) 2003-2012 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -147,13 +147,6 @@ qla24xx_configure_prot_mode(srb_t *sp, uint16_t *fw_prot_opts) struct scsi_cmnd *cmd = GET_CMD_SP(sp); uint8_t guard = scsi_host_get_guard(cmd->device->host); - /* We only support T10 DIF right now */ - if (guard != SHOST_DIX_GUARD_CRC) { - ql_dbg(ql_dbg_io, sp->fcport->vha, 0x3007, - "Unsupported guard: %d for cmd=%p.\n", guard, cmd); - return 0; - } - /* We always use DIFF Bundling for best performance */ *fw_prot_opts = 0; @@ -172,10 +165,11 @@ qla24xx_configure_prot_mode(srb_t *sp, uint16_t *fw_prot_opts) *fw_prot_opts |= PO_MODE_DIF_REMOVE; break; case SCSI_PROT_READ_PASS: - *fw_prot_opts |= PO_MODE_DIF_PASS; - break; case SCSI_PROT_WRITE_PASS: - *fw_prot_opts |= PO_MODE_DIF_PASS; + if (guard & SHOST_DIX_GUARD_IP) + *fw_prot_opts |= PO_MODE_DIF_TCP_CKSUM; + else + *fw_prot_opts |= PO_MODE_DIF_PASS; break; default: /* Normal Request */ *fw_prot_opts |= PO_MODE_DIF_PASS; @@ -821,7 +815,6 @@ qla24xx_set_t10dif_tags(srb_t *sp, struct fw_dif_context *pkt, unsigned int protcnt) { struct scsi_cmnd *cmd = GET_CMD_SP(sp); - scsi_qla_host_t *vha = shost_priv(cmd->device->host); switch (scsi_get_prot_type(cmd)) { case SCSI_PROT_DIF_TYPE0: @@ -891,12 +884,6 @@ qla24xx_set_t10dif_tags(srb_t *sp, struct fw_dif_context *pkt, pkt->ref_tag_mask[3] = 0xff; break; } - - ql_dbg(ql_dbg_io, vha, 0x3009, - "Setting protection Tags: (BIG) ref tag = 0x%x, app tag = 0x%x, " - "prot SG count %d, cmd lba 0x%x, prot_type=%u cmd=%p.\n", - pkt->ref_tag, pkt->app_tag, protcnt, (int)scsi_get_lba(cmd), - scsi_get_prot_type(cmd), cmd); } struct qla2_sgx { @@ -1068,9 +1055,6 @@ qla24xx_walk_and_build_sglist(struct qla_hw_data *ha, srb_t *sp, uint32_t *dsd, int i; uint16_t used_dsds = tot_dsds; struct scsi_cmnd *cmd = GET_CMD_SP(sp); - scsi_qla_host_t *vha = shost_priv(cmd->device->host); - - uint8_t *cp; scsi_for_each_sg(cmd, sg, tot_dsds, i) { dma_addr_t sle_dma; @@ -1113,19 +1097,12 @@ qla24xx_walk_and_build_sglist(struct qla_hw_data *ha, srb_t *sp, uint32_t *dsd, cur_dsd = (uint32_t *)next_dsd; } sle_dma = sg_dma_address(sg); - ql_dbg(ql_dbg_io, vha, 0x300a, - "sg entry %d - addr=0x%x 0x%x, " "len=%d for cmd=%p.\n", - i, LSD(sle_dma), MSD(sle_dma), sg_dma_len(sg), cmd); + *cur_dsd++ = cpu_to_le32(LSD(sle_dma)); *cur_dsd++ = cpu_to_le32(MSD(sle_dma)); *cur_dsd++ = cpu_to_le32(sg_dma_len(sg)); avail_dsds--; - if (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_PASS) { - cp = page_address(sg_page(sg)) + sg->offset; - ql_dbg(ql_dbg_io, vha, 0x300b, - "User data buffer=%p for cmd=%p.\n", cp, cmd); - } } /* Null termination */ *cur_dsd++ = 0; @@ -1148,8 +1125,6 @@ qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *ha, srb_t *sp, struct scsi_cmnd *cmd; uint32_t *cur_dsd = dsd; uint16_t used_dsds = tot_dsds; - scsi_qla_host_t *vha = pci_get_drvdata(ha->pdev); - uint8_t *cp; cmd = GET_CMD_SP(sp); scsi_for_each_prot_sg(cmd, sg, tot_dsds, i) { @@ -1193,23 +1168,11 @@ qla24xx_walk_and_build_prot_sglist(struct qla_hw_data *ha, srb_t *sp, cur_dsd = (uint32_t *)next_dsd; } sle_dma = sg_dma_address(sg); - if (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_PASS) { - ql_dbg(ql_dbg_io, vha, 0x3027, - "%s(): %p, sg_entry %d - " - "addr=0x%x0x%x, len=%d.\n", - __func__, cur_dsd, i, - LSD(sle_dma), MSD(sle_dma), sg_dma_len(sg)); - } + *cur_dsd++ = cpu_to_le32(LSD(sle_dma)); *cur_dsd++ = cpu_to_le32(MSD(sle_dma)); *cur_dsd++ = cpu_to_le32(sg_dma_len(sg)); - if (scsi_get_prot_op(cmd) == SCSI_PROT_WRITE_PASS) { - cp = page_address(sg_page(sg)) + sg->offset; - ql_dbg(ql_dbg_io, vha, 0x3028, - "%s(): Protection Data buffer = %p.\n", __func__, - cp); - } avail_dsds--; } /* Null termination */ @@ -1386,6 +1349,16 @@ qla24xx_build_scsi_crc_2_iocbs(srb_t *sp, struct cmd_type_crc_2 *cmd_pkt, if (!qla2x00_hba_err_chk_enabled(sp)) fw_prot_opts |= 0x10; /* Disable Guard tag checking */ + /* HBA error checking enabled */ + else if (IS_PI_UNINIT_CAPABLE(ha)) { + if ((scsi_get_prot_type(GET_CMD_SP(sp)) == SCSI_PROT_DIF_TYPE1) + || (scsi_get_prot_type(GET_CMD_SP(sp)) == + SCSI_PROT_DIF_TYPE2)) + fw_prot_opts |= BIT_10; + else if (scsi_get_prot_type(GET_CMD_SP(sp)) == + SCSI_PROT_DIF_TYPE3) + fw_prot_opts |= BIT_11; + } if (!bundling) { cur_dsd = (uint32_t *) &crc_ctx_pkt->u.nobundling.data_address; @@ -1858,7 +1831,7 @@ qla2x00_alloc_iocbs(scsi_qla_host_t *vha, srb_t *sp) } if (index == MAX_OUTSTANDING_COMMANDS) { ql_log(ql_log_warn, vha, 0x700b, - "No room on oustanding cmd array.\n"); + "No room on outstanding cmd array.\n"); goto queuing_error; } @@ -2665,3 +2638,201 @@ done: spin_unlock_irqrestore(&ha->hardware_lock, flags); return rval; } + +static void +qla25xx_build_bidir_iocb(srb_t *sp, struct scsi_qla_host *vha, + struct cmd_bidir *cmd_pkt, uint32_t tot_dsds) +{ + uint16_t avail_dsds; + uint32_t *cur_dsd; + uint32_t req_data_len = 0; + uint32_t rsp_data_len = 0; + struct scatterlist *sg; + int index; + int entry_count = 1; + struct fc_bsg_job *bsg_job = sp->u.bsg_job; + + /*Update entry type to indicate bidir command */ + *((uint32_t *)(&cmd_pkt->entry_type)) = + __constant_cpu_to_le32(COMMAND_BIDIRECTIONAL); + + /* Set the transfer direction, in this set both flags + * Also set the BD_WRAP_BACK flag, firmware will take care + * assigning DID=SID for outgoing pkts. + */ + cmd_pkt->wr_dseg_count = cpu_to_le16(bsg_job->request_payload.sg_cnt); + cmd_pkt->rd_dseg_count = cpu_to_le16(bsg_job->reply_payload.sg_cnt); + cmd_pkt->control_flags = + __constant_cpu_to_le16(BD_WRITE_DATA | BD_READ_DATA | + BD_WRAP_BACK); + + req_data_len = rsp_data_len = bsg_job->request_payload.payload_len; + cmd_pkt->wr_byte_count = cpu_to_le32(req_data_len); + cmd_pkt->rd_byte_count = cpu_to_le32(rsp_data_len); + cmd_pkt->timeout = cpu_to_le16(qla2x00_get_async_timeout(vha) + 2); + + vha->bidi_stats.transfer_bytes += req_data_len; + vha->bidi_stats.io_count++; + + /* Only one dsd is available for bidirectional IOCB, remaining dsds + * are bundled in continuation iocb + */ + avail_dsds = 1; + cur_dsd = (uint32_t *)&cmd_pkt->fcp_data_dseg_address; + + index = 0; + + for_each_sg(bsg_job->request_payload.sg_list, sg, + bsg_job->request_payload.sg_cnt, index) { + dma_addr_t sle_dma; + cont_a64_entry_t *cont_pkt; + + /* Allocate additional continuation packets */ + if (avail_dsds == 0) { + /* Continuation type 1 IOCB can accomodate + * 5 DSDS + */ + cont_pkt = qla2x00_prep_cont_type1_iocb(vha, vha->req); + cur_dsd = (uint32_t *) cont_pkt->dseg_0_address; + avail_dsds = 5; + entry_count++; + } + sle_dma = sg_dma_address(sg); + *cur_dsd++ = cpu_to_le32(LSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(MSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(sg_dma_len(sg)); + avail_dsds--; + } + /* For read request DSD will always goes to continuation IOCB + * and follow the write DSD. If there is room on the current IOCB + * then it is added to that IOCB else new continuation IOCB is + * allocated. + */ + for_each_sg(bsg_job->reply_payload.sg_list, sg, + bsg_job->reply_payload.sg_cnt, index) { + dma_addr_t sle_dma; + cont_a64_entry_t *cont_pkt; + + /* Allocate additional continuation packets */ + if (avail_dsds == 0) { + /* Continuation type 1 IOCB can accomodate + * 5 DSDS + */ + cont_pkt = qla2x00_prep_cont_type1_iocb(vha, vha->req); + cur_dsd = (uint32_t *) cont_pkt->dseg_0_address; + avail_dsds = 5; + entry_count++; + } + sle_dma = sg_dma_address(sg); + *cur_dsd++ = cpu_to_le32(LSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(MSD(sle_dma)); + *cur_dsd++ = cpu_to_le32(sg_dma_len(sg)); + avail_dsds--; + } + /* This value should be same as number of IOCB required for this cmd */ + cmd_pkt->entry_count = entry_count; +} + +int +qla2x00_start_bidir(srb_t *sp, struct scsi_qla_host *vha, uint32_t tot_dsds) +{ + + struct qla_hw_data *ha = vha->hw; + unsigned long flags; + uint32_t handle; + uint32_t index; + uint16_t req_cnt; + uint16_t cnt; + uint32_t *clr_ptr; + struct cmd_bidir *cmd_pkt = NULL; + struct rsp_que *rsp; + struct req_que *req; + int rval = EXT_STATUS_OK; + device_reg_t __iomem *reg = ISP_QUE_REG(ha, vha->req->id); + + rval = QLA_SUCCESS; + + rsp = ha->rsp_q_map[0]; + req = vha->req; + + /* Send marker if required */ + if (vha->marker_needed != 0) { + if (qla2x00_marker(vha, req, + rsp, 0, 0, MK_SYNC_ALL) != QLA_SUCCESS) + return EXT_STATUS_MAILBOX; + vha->marker_needed = 0; + } + + /* Acquire ring specific lock */ + spin_lock_irqsave(&ha->hardware_lock, flags); + + /* Check for room in outstanding command list. */ + handle = req->current_outstanding_cmd; + for (index = 1; index < MAX_OUTSTANDING_COMMANDS; index++) { + handle++; + if (handle == MAX_OUTSTANDING_COMMANDS) + handle = 1; + if (!req->outstanding_cmds[handle]) + break; + } + + if (index == MAX_OUTSTANDING_COMMANDS) { + rval = EXT_STATUS_BUSY; + goto queuing_error; + } + + /* Calculate number of IOCB required */ + req_cnt = qla24xx_calc_iocbs(vha, tot_dsds); + + /* Check for room on request queue. */ + if (req->cnt < req_cnt + 2) { + if (ha->mqenable) + cnt = RD_REG_DWORD(®->isp25mq.req_q_out); + else if (IS_QLA82XX(ha)) + cnt = RD_REG_DWORD(®->isp82.req_q_out); + else if (IS_FWI2_CAPABLE(ha)) + cnt = RD_REG_DWORD(®->isp24.req_q_out); + else + cnt = qla2x00_debounce_register( + ISP_REQ_Q_OUT(ha, ®->isp)); + + if (req->ring_index < cnt) + req->cnt = cnt - req->ring_index; + else + req->cnt = req->length - + (req->ring_index - cnt); + } + if (req->cnt < req_cnt + 2) { + rval = EXT_STATUS_BUSY; + goto queuing_error; + } + + cmd_pkt = (struct cmd_bidir *)req->ring_ptr; + cmd_pkt->handle = MAKE_HANDLE(req->id, handle); + + /* Zero out remaining portion of packet. */ + /* tagged queuing modifier -- default is TSK_SIMPLE (0).*/ + clr_ptr = (uint32_t *)cmd_pkt + 2; + memset(clr_ptr, 0, REQUEST_ENTRY_SIZE - 8); + + /* Set NPORT-ID (of vha)*/ + cmd_pkt->nport_handle = cpu_to_le16(vha->self_login_loop_id); + cmd_pkt->port_id[0] = vha->d_id.b.al_pa; + cmd_pkt->port_id[1] = vha->d_id.b.area; + cmd_pkt->port_id[2] = vha->d_id.b.domain; + + qla25xx_build_bidir_iocb(sp, vha, cmd_pkt, tot_dsds); + cmd_pkt->entry_status = (uint8_t) rsp->id; + /* Build command packet. */ + req->current_outstanding_cmd = handle; + req->outstanding_cmds[handle] = sp; + sp->handle = handle; + req->cnt -= req_cnt; + + /* Send the command to the firmware */ + wmb(); + qla2x00_start_iocbs(vha, req); +queuing_error: + spin_unlock_irqrestore(&ha->hardware_lock, flags); + return rval; +} diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 6f67a9d4998b..5733811ce8e7 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-2011 QLogic Corporation + * Copyright (c) 2003-2012 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -294,6 +294,11 @@ 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; @@ -332,6 +337,166 @@ qla2x00_get_link_speed_str(struct qla_hw_data *ha) return link_speed; } +void +qla83xx_handle_8200_aen(scsi_qla_host_t *vha, uint16_t *mb) +{ + struct qla_hw_data *ha = vha->hw; + + /* + * 8200 AEN Interpretation: + * mb[0] = AEN code + * mb[1] = AEN Reason code + * mb[2] = LSW of Peg-Halt Status-1 Register + * mb[6] = MSW of Peg-Halt Status-1 Register + * mb[3] = LSW of Peg-Halt Status-2 register + * mb[7] = MSW of Peg-Halt Status-2 register + * mb[4] = IDC Device-State Register value + * mb[5] = IDC Driver-Presence Register value + */ + ql_dbg(ql_dbg_async, vha, 0x506b, "AEN Code: mb[0] = 0x%x AEN reason: " + "mb[1] = 0x%x PH-status1: mb[2] = 0x%x PH-status1: mb[6] = 0x%x.\n", + mb[0], mb[1], mb[2], mb[6]); + ql_dbg(ql_dbg_async, vha, 0x506c, "PH-status2: mb[3] = 0x%x " + "PH-status2: mb[7] = 0x%x Device-State: mb[4] = 0x%x " + "Drv-Presence: mb[5] = 0x%x.\n", mb[3], mb[7], mb[4], mb[5]); + + if (mb[1] & (IDC_PEG_HALT_STATUS_CHANGE | IDC_NIC_FW_REPORTED_FAILURE | + IDC_HEARTBEAT_FAILURE)) { + ha->flags.nic_core_hung = 1; + ql_log(ql_log_warn, vha, 0x5060, + "83XX: F/W Error Reported: Check if reset required.\n"); + + if (mb[1] & IDC_PEG_HALT_STATUS_CHANGE) { + uint32_t protocol_engine_id, fw_err_code, err_level; + + /* + * IDC_PEG_HALT_STATUS_CHANGE interpretation: + * - PEG-Halt Status-1 Register: + * (LSW = mb[2], MSW = mb[6]) + * Bits 0-7 = protocol-engine ID + * Bits 8-28 = f/w error code + * Bits 29-31 = Error-level + * Error-level 0x1 = Non-Fatal error + * Error-level 0x2 = Recoverable Fatal error + * Error-level 0x4 = UnRecoverable Fatal error + * - PEG-Halt Status-2 Register: + * (LSW = mb[3], MSW = mb[7]) + */ + protocol_engine_id = (mb[2] & 0xff); + fw_err_code = (((mb[2] & 0xff00) >> 8) | + ((mb[6] & 0x1fff) << 8)); + err_level = ((mb[6] & 0xe000) >> 13); + ql_log(ql_log_warn, vha, 0x5061, "PegHalt Status-1 " + "Register: protocol_engine_id=0x%x " + "fw_err_code=0x%x err_level=0x%x.\n", + protocol_engine_id, fw_err_code, err_level); + ql_log(ql_log_warn, vha, 0x5062, "PegHalt Status-2 " + "Register: 0x%x%x.\n", mb[7], mb[3]); + if (err_level == ERR_LEVEL_NON_FATAL) { + ql_log(ql_log_warn, vha, 0x5063, + "Not a fatal error, f/w has recovered " + "iteself.\n"); + } else if (err_level == ERR_LEVEL_RECOVERABLE_FATAL) { + ql_log(ql_log_fatal, vha, 0x5064, + "Recoverable Fatal error: Chip reset " + "required.\n"); + qla83xx_schedule_work(vha, + QLA83XX_NIC_CORE_RESET); + } else if (err_level == ERR_LEVEL_UNRECOVERABLE_FATAL) { + ql_log(ql_log_fatal, vha, 0x5065, + "Unrecoverable Fatal error: Set FAILED " + "state, reboot required.\n"); + qla83xx_schedule_work(vha, + QLA83XX_NIC_CORE_UNRECOVERABLE); + } + } + + if (mb[1] & IDC_NIC_FW_REPORTED_FAILURE) { + uint16_t peg_fw_state, nw_interface_link_up; + uint16_t nw_interface_signal_detect, sfp_status; + uint16_t htbt_counter, htbt_monitor_enable; + uint16_t sfp_additonal_info, sfp_multirate; + uint16_t sfp_tx_fault, link_speed, dcbx_status; + + /* + * IDC_NIC_FW_REPORTED_FAILURE interpretation: + * - PEG-to-FC Status Register: + * (LSW = mb[2], MSW = mb[6]) + * Bits 0-7 = Peg-Firmware state + * Bit 8 = N/W Interface Link-up + * Bit 9 = N/W Interface signal detected + * Bits 10-11 = SFP Status + * SFP Status 0x0 = SFP+ transceiver not expected + * SFP Status 0x1 = SFP+ transceiver not present + * SFP Status 0x2 = SFP+ transceiver invalid + * SFP Status 0x3 = SFP+ transceiver present and + * valid + * Bits 12-14 = Heartbeat Counter + * Bit 15 = Heartbeat Monitor Enable + * Bits 16-17 = SFP Additional Info + * SFP info 0x0 = Unregocnized transceiver for + * Ethernet + * SFP info 0x1 = SFP+ brand validation failed + * SFP info 0x2 = SFP+ speed validation failed + * SFP info 0x3 = SFP+ access error + * Bit 18 = SFP Multirate + * Bit 19 = SFP Tx Fault + * Bits 20-22 = Link Speed + * Bits 23-27 = Reserved + * Bits 28-30 = DCBX Status + * DCBX Status 0x0 = DCBX Disabled + * DCBX Status 0x1 = DCBX Enabled + * DCBX Status 0x2 = DCBX Exchange error + * Bit 31 = Reserved + */ + peg_fw_state = (mb[2] & 0x00ff); + nw_interface_link_up = ((mb[2] & 0x0100) >> 8); + nw_interface_signal_detect = ((mb[2] & 0x0200) >> 9); + sfp_status = ((mb[2] & 0x0c00) >> 10); + htbt_counter = ((mb[2] & 0x7000) >> 12); + htbt_monitor_enable = ((mb[2] & 0x8000) >> 15); + sfp_additonal_info = (mb[6] & 0x0003); + sfp_multirate = ((mb[6] & 0x0004) >> 2); + sfp_tx_fault = ((mb[6] & 0x0008) >> 3); + link_speed = ((mb[6] & 0x0070) >> 4); + dcbx_status = ((mb[6] & 0x7000) >> 12); + + ql_log(ql_log_warn, vha, 0x5066, + "Peg-to-Fc Status Register:\n" + "peg_fw_state=0x%x, nw_interface_link_up=0x%x, " + "nw_interface_signal_detect=0x%x" + "\nsfp_statis=0x%x.\n ", peg_fw_state, + nw_interface_link_up, nw_interface_signal_detect, + sfp_status); + ql_log(ql_log_warn, vha, 0x5067, + "htbt_counter=0x%x, htbt_monitor_enable=0x%x, " + "sfp_additonal_info=0x%x, sfp_multirate=0x%x.\n ", + htbt_counter, htbt_monitor_enable, + sfp_additonal_info, sfp_multirate); + ql_log(ql_log_warn, vha, 0x5068, + "sfp_tx_fault=0x%x, link_state=0x%x, " + "dcbx_status=0x%x.\n", sfp_tx_fault, link_speed, + dcbx_status); + + qla83xx_schedule_work(vha, QLA83XX_NIC_CORE_RESET); + } + + if (mb[1] & IDC_HEARTBEAT_FAILURE) { + ql_log(ql_log_warn, vha, 0x5069, + "Heartbeat Failure encountered, chip reset " + "required.\n"); + + qla83xx_schedule_work(vha, QLA83XX_NIC_CORE_RESET); + } + } + + if (mb[1] & IDC_DEVICE_STATE_CHANGE) { + ql_log(ql_log_info, vha, 0x506a, + "IDC Device-State changed = 0x%x.\n", mb[4]); + qla83xx_schedule_work(vha, MBA_IDC_AEN); + } +} + /** * qla2x00_async_event() - Process aynchronous events. * @ha: SCSI driver HA context @@ -681,8 +846,7 @@ skip_rio: * it. Otherwise ignore it and Wait for RSCN to come in. */ atomic_set(&vha->loop_down_timer, 0); - if (atomic_read(&vha->loop_state) != LOOP_DOWN && - atomic_read(&vha->loop_state) != LOOP_DEAD) { + if (mb[1] != 0xffff || (mb[2] != 0x6 && mb[2] != 0x4)) { ql_dbg(ql_dbg_async, vha, 0x5011, "Asynchronous PORT UPDATE ignored %04x/%04x/%04x.\n", mb[1], mb[2], mb[3]); @@ -822,11 +986,28 @@ skip_rio: "FCF Configuration Error -- %04x %04x %04x.\n", mb[1], mb[2], mb[3]); break; - case MBA_IDC_COMPLETE: case MBA_IDC_NOTIFY: + /* See if we need to quiesce any I/O */ + if (IS_QLA8031(vha->hw)) + if ((mb[2] & 0x7fff) == MBC_PORT_RESET || + (mb[2] & 0x7fff) == MBC_SET_PORT_CONFIG) { + set_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags); + qla2xxx_wake_dpc(vha); + } + case MBA_IDC_COMPLETE: case MBA_IDC_TIME_EXT: - qla81xx_idc_event(vha, mb[0], mb[1]); + if (IS_QLA81XX(vha->hw) || IS_QLA8031(vha->hw)) + qla81xx_idc_event(vha, mb[0], mb[1]); break; + + case MBA_IDC_AEN: + mb[4] = RD_REG_WORD(®24->mailbox4); + mb[5] = RD_REG_WORD(®24->mailbox5); + mb[6] = RD_REG_WORD(®24->mailbox6); + mb[7] = RD_REG_WORD(®24->mailbox7); + qla83xx_handle_8200_aen(vha, mb); + break; + default: ql_dbg(ql_dbg_async, vha, 0x5057, "Unknown AEN:%04x %04x %04x %04x\n", @@ -1414,7 +1595,7 @@ qla2x00_handle_sense(srb_t *sp, uint8_t *sense_data, uint32_t par_sense_len, struct scsi_dif_tuple { __be16 guard; /* Checksum */ - __be16 app_tag; /* APPL identifer */ + __be16 app_tag; /* APPL identifier */ __be32 ref_tag; /* Target LBA or indirect LBA */ }; @@ -1546,6 +1727,149 @@ qla2x00_handle_dif_error(srb_t *sp, struct sts_entry_24xx *sts24) return 1; } +static void +qla25xx_process_bidir_status_iocb(scsi_qla_host_t *vha, void *pkt, + struct req_que *req, uint32_t index) +{ + struct qla_hw_data *ha = vha->hw; + srb_t *sp; + uint16_t comp_status; + uint16_t scsi_status; + uint16_t thread_id; + uint32_t rval = EXT_STATUS_OK; + struct fc_bsg_job *bsg_job = NULL; + sts_entry_t *sts; + struct sts_entry_24xx *sts24; + sts = (sts_entry_t *) pkt; + sts24 = (struct sts_entry_24xx *) pkt; + + /* Validate handle. */ + if (index >= MAX_OUTSTANDING_COMMANDS) { + ql_log(ql_log_warn, vha, 0x70af, + "Invalid SCSI completion handle 0x%x.\n", index); + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + return; + } + + sp = req->outstanding_cmds[index]; + if (sp) { + /* Free outstanding command slot. */ + req->outstanding_cmds[index] = NULL; + bsg_job = sp->u.bsg_job; + } else { + ql_log(ql_log_warn, vha, 0x70b0, + "Req:%d: Invalid ISP SCSI completion handle(0x%x)\n", + req->id, index); + + set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); + return; + } + + if (IS_FWI2_CAPABLE(ha)) { + comp_status = le16_to_cpu(sts24->comp_status); + scsi_status = le16_to_cpu(sts24->scsi_status) & SS_MASK; + } else { + comp_status = le16_to_cpu(sts->comp_status); + scsi_status = le16_to_cpu(sts->scsi_status) & SS_MASK; + } + + thread_id = bsg_job->request->rqst_data.h_vendor.vendor_cmd[1]; + switch (comp_status) { + case CS_COMPLETE: + if (scsi_status == 0) { + bsg_job->reply->reply_payload_rcv_len = + bsg_job->reply_payload.payload_len; + rval = EXT_STATUS_OK; + } + goto done; + + case CS_DATA_OVERRUN: + ql_dbg(ql_dbg_user, vha, 0x70b1, + "Command completed with date overrun thread_id=%d\n", + thread_id); + rval = EXT_STATUS_DATA_OVERRUN; + break; + + case CS_DATA_UNDERRUN: + ql_dbg(ql_dbg_user, vha, 0x70b2, + "Command completed with date underrun thread_id=%d\n", + thread_id); + rval = EXT_STATUS_DATA_UNDERRUN; + break; + case CS_BIDIR_RD_OVERRUN: + ql_dbg(ql_dbg_user, vha, 0x70b3, + "Command completed with read data overrun thread_id=%d\n", + thread_id); + rval = EXT_STATUS_DATA_OVERRUN; + break; + + case CS_BIDIR_RD_WR_OVERRUN: + ql_dbg(ql_dbg_user, vha, 0x70b4, + "Command completed with read and write data overrun " + "thread_id=%d\n", thread_id); + rval = EXT_STATUS_DATA_OVERRUN; + break; + + case CS_BIDIR_RD_OVERRUN_WR_UNDERRUN: + ql_dbg(ql_dbg_user, vha, 0x70b5, + "Command completed with read data over and write data " + "underrun thread_id=%d\n", thread_id); + rval = EXT_STATUS_DATA_OVERRUN; + break; + + case CS_BIDIR_RD_UNDERRUN: + ql_dbg(ql_dbg_user, vha, 0x70b6, + "Command completed with read data data underrun " + "thread_id=%d\n", thread_id); + rval = EXT_STATUS_DATA_UNDERRUN; + break; + + case CS_BIDIR_RD_UNDERRUN_WR_OVERRUN: + ql_dbg(ql_dbg_user, vha, 0x70b7, + "Command completed with read data under and write data " + "overrun thread_id=%d\n", thread_id); + rval = EXT_STATUS_DATA_UNDERRUN; + break; + + case CS_BIDIR_RD_WR_UNDERRUN: + ql_dbg(ql_dbg_user, vha, 0x70b8, + "Command completed with read and write data underrun " + "thread_id=%d\n", thread_id); + rval = EXT_STATUS_DATA_UNDERRUN; + break; + + case CS_BIDIR_DMA: + ql_dbg(ql_dbg_user, vha, 0x70b9, + "Command completed with data DMA error thread_id=%d\n", + thread_id); + rval = EXT_STATUS_DMA_ERR; + break; + + case CS_TIMEOUT: + ql_dbg(ql_dbg_user, vha, 0x70ba, + "Command completed with timeout thread_id=%d\n", + thread_id); + rval = EXT_STATUS_TIMEOUT; + break; + default: + ql_dbg(ql_dbg_user, vha, 0x70bb, + "Command completed with completion status=0x%x " + "thread_id=%d\n", comp_status, thread_id); + rval = EXT_STATUS_ERR; + break; + } + bsg_job->reply->reply_payload_rcv_len = 0; + +done: + /* Return the vendor specific reply to API */ + bsg_job->reply->reply_data.vendor_reply.vendor_rsp[0] = rval; + bsg_job->reply_len = sizeof(struct fc_bsg_reply); + /* Always return DID_OK, bsg will send the vendor specific response + * in this case only */ + sp->done(vha, sp, (DID_OK << 6)); + +} + /** * qla2x00_status_entry() - Process a Status IOCB entry. * @ha: SCSI driver HA context @@ -1573,12 +1897,14 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) struct req_que *req; int logit = 1; int res = 0; + uint16_t state_flags = 0; sts = (sts_entry_t *) pkt; sts24 = (struct sts_entry_24xx *) pkt; if (IS_FWI2_CAPABLE(ha)) { comp_status = le16_to_cpu(sts24->comp_status); scsi_status = le16_to_cpu(sts24->scsi_status) & SS_MASK; + state_flags = le16_to_cpu(sts24->state_flags); } else { comp_status = le16_to_cpu(sts->comp_status); scsi_status = le16_to_cpu(sts->scsi_status) & SS_MASK; @@ -1587,17 +1913,9 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) que = MSW(sts->handle); req = ha->req_q_map[que]; - /* Fast path completion. */ - if (comp_status == CS_COMPLETE && scsi_status == 0) { - qla2x00_process_completed_request(vha, req, handle); - - return; - } - /* Validate handle. */ if (handle < MAX_OUTSTANDING_COMMANDS) { sp = req->outstanding_cmds[handle]; - req->outstanding_cmds[handle] = NULL; } else sp = NULL; @@ -1612,6 +1930,20 @@ qla2x00_status_entry(scsi_qla_host_t *vha, struct rsp_que *rsp, void *pkt) qla2xxx_wake_dpc(vha); return; } + + if (unlikely((state_flags & BIT_1) && (sp->type == SRB_BIDI_CMD))) { + qla25xx_process_bidir_status_iocb(vha, pkt, req, handle); + return; + } + + /* Fast path completion. */ + if (comp_status == CS_COMPLETE && scsi_status == 0) { + qla2x00_process_completed_request(vha, req, handle); + + return; + } + + req->outstanding_cmds[handle] = NULL; cp = GET_CMD_SP(sp); if (cp == NULL) { ql_dbg(ql_dbg_io, vha, 0x3018, @@ -1830,7 +2162,21 @@ check_scsi_status: case CS_DIF_ERROR: logit = qla2x00_handle_dif_error(sp, sts24); + res = cp->result; break; + + case CS_TRANSPORT: + res = DID_ERROR << 16; + + if (!IS_PI_SPLIT_DET_CAPABLE(ha)) + break; + + if (state_flags & BIT_4) + scmd_printk(KERN_WARNING, cp, + "Unsupported device '%s' found.\n", + cp->device->vendor); + break; + default: res = DID_ERROR << 16; break; @@ -2150,7 +2496,7 @@ qla24xx_intr_handler(int irq, void *dev_id) unsigned long iter; uint32_t stat; uint32_t hccr; - uint16_t mb[4]; + uint16_t mb[8]; struct rsp_que *rsp; unsigned long flags; @@ -2191,29 +2537,29 @@ qla24xx_intr_handler(int irq, void *dev_id) break; switch (stat & 0xff) { - case 0x1: - case 0x2: - case 0x10: - case 0x11: + case INTR_ROM_MB_SUCCESS: + case INTR_ROM_MB_FAILED: + case INTR_MB_SUCCESS: + case INTR_MB_FAILED: qla24xx_mbx_completion(vha, MSW(stat)); status |= MBX_INTERRUPT; break; - case 0x12: + case INTR_ASYNC_EVENT: mb[0] = MSW(stat); mb[1] = RD_REG_WORD(®->mailbox1); mb[2] = RD_REG_WORD(®->mailbox2); mb[3] = RD_REG_WORD(®->mailbox3); qla2x00_async_event(vha, rsp, mb); break; - case 0x13: - case 0x14: + case INTR_RSP_QUE_UPDATE: + case INTR_RSP_QUE_UPDATE_83XX: qla24xx_process_response_queue(vha, rsp); break; - case 0x1C: /* ATIO queue updated */ + case INTR_ATIO_QUE_UPDATE: qlt_24xx_process_atio_queue(vha); break; - case 0x1D: /* ATIO and response queues updated */ + case INTR_ATIO_RSP_QUE_UPDATE: qlt_24xx_process_atio_queue(vha); qla24xx_process_response_queue(vha, rsp); break; @@ -2224,6 +2570,8 @@ qla24xx_intr_handler(int irq, void *dev_id) } WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); RD_REG_DWORD_RELAXED(®->hccr); + if (unlikely(IS_QLA83XX(ha) && (ha->pdev->revision == 1))) + ndelay(3500); } spin_unlock_irqrestore(&ha->hardware_lock, flags); @@ -2306,7 +2654,7 @@ qla24xx_msix_default(int irq, void *dev_id) int status; uint32_t stat; uint32_t hccr; - uint16_t mb[4]; + uint16_t mb[8]; unsigned long flags; rsp = (struct rsp_que *) dev_id; @@ -2342,29 +2690,29 @@ qla24xx_msix_default(int irq, void *dev_id) break; switch (stat & 0xff) { - case 0x1: - case 0x2: - case 0x10: - case 0x11: + case INTR_ROM_MB_SUCCESS: + case INTR_ROM_MB_FAILED: + case INTR_MB_SUCCESS: + case INTR_MB_FAILED: qla24xx_mbx_completion(vha, MSW(stat)); status |= MBX_INTERRUPT; break; - case 0x12: + case INTR_ASYNC_EVENT: mb[0] = MSW(stat); mb[1] = RD_REG_WORD(®->mailbox1); mb[2] = RD_REG_WORD(®->mailbox2); mb[3] = RD_REG_WORD(®->mailbox3); qla2x00_async_event(vha, rsp, mb); break; - case 0x13: - case 0x14: + case INTR_RSP_QUE_UPDATE: + case INTR_RSP_QUE_UPDATE_83XX: qla24xx_process_response_queue(vha, rsp); break; - case 0x1C: /* ATIO queue updated */ + case INTR_ATIO_QUE_UPDATE: qlt_24xx_process_atio_queue(vha); break; - case 0x1D: /* ATIO and response queues updated */ + case INTR_ATIO_RSP_QUE_UPDATE: qlt_24xx_process_atio_queue(vha); qla24xx_process_response_queue(vha, rsp); break; @@ -2570,7 +2918,7 @@ qla2x00_request_irqs(struct qla_hw_data *ha, struct rsp_que *rsp) skip_msix: if (!IS_QLA24XX(ha) && !IS_QLA2532(ha) && !IS_QLA8432(ha) && - !IS_QLA8001(ha)) + !IS_QLA8001(ha) && !IS_QLA82XX(ha)) goto skip_msi; ret = pci_enable_msi(ha->pdev); @@ -2581,6 +2929,11 @@ skip_msix: } else ql_log(ql_log_warn, vha, 0x0039, "MSI-X; Falling back-to INTa mode -- %d.\n", ret); + + /* 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, @@ -2595,21 +2948,9 @@ skip_msi: clear_risc_ints: - /* - * FIXME: Noted that 8014s were being dropped during NK testing. - * Timing deltas during MSI-X/INTa transitions? - */ - if (IS_QLA81XX(ha) || IS_QLA82XX(ha) || IS_QLA83XX(ha)) - goto fail; spin_lock_irq(&ha->hardware_lock); - if (IS_FWI2_CAPABLE(ha)) { - WRT_REG_DWORD(®->isp24.hccr, HCCRX_CLR_HOST_INT); - WRT_REG_DWORD(®->isp24.hccr, HCCRX_CLR_RISC_INT); - } else { + if (!IS_FWI2_CAPABLE(ha)) WRT_REG_WORD(®->isp.semaphore, 0); - WRT_REG_WORD(®->isp.hccr, HCCR_CLR_RISC_INT); - WRT_REG_WORD(®->isp.hccr, HCCR_CLR_HOST_INT); - } spin_unlock_irq(&ha->hardware_lock); fail: diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index d5ce92c0a8fc..18c509fae555 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-2011 QLogic Corporation + * Copyright (c) 2003-2012 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -75,7 +75,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *vha, mbx_cmd_t *mcp) return QLA_FUNCTION_TIMEOUT; } - if (ha->flags.isp82xx_fw_hung) { + if (IS_QLA82XX(ha) && ha->flags.isp82xx_fw_hung) { /* Setting Link-Down error */ mcp->mb[0] = MBS_LINK_DOWN_ERROR; ql_log(ql_log_warn, vha, 0x1004, @@ -232,7 +232,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 (ha->flags.isp82xx_fw_hung) { + if ((IS_QLA82XX(ha) && ha->flags.isp82xx_fw_hung)) { ha->flags.mbox_busy = 0; /* Setting Link-Down error */ mcp->mb[0] = MBS_LINK_DOWN_ERROR; @@ -369,7 +369,7 @@ premature_exit: mbx_done: if (rval) { - ql_dbg(ql_dbg_mbx, base_vha, 0x1020, + ql_log(ql_log_warn, base_vha, 0x1020, "**** Failed mbx[0]=%x, mb[1]=%x, mb[2]=%x, mb[3]=%x, cmd=%x ****.\n", mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3], command); } else { @@ -533,7 +533,7 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha) mcp->in_mb = MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1|MBX_0; if (IS_QLA81XX(vha->hw) || IS_QLA8031(ha)) mcp->in_mb |= MBX_13|MBX_12|MBX_11|MBX_10|MBX_9|MBX_8; - if (IS_QLA83XX(vha->hw)) + if (IS_FWI2_CAPABLE(ha)) mcp->in_mb |= MBX_17|MBX_16|MBX_15; mcp->flags = 0; mcp->tov = MBX_TOV_SECONDS; @@ -559,18 +559,16 @@ qla2x00_get_fw_version(scsi_qla_host_t *vha) ha->phy_version[1] = mcp->mb[9] >> 8; ha->phy_version[2] = mcp->mb[9] & 0xff; } - if (IS_QLA83XX(ha)) { - if (mcp->mb[6] & BIT_15) { - ha->fw_attributes_h = mcp->mb[15]; - ha->fw_attributes_ext[0] = mcp->mb[16]; - ha->fw_attributes_ext[1] = mcp->mb[17]; - ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1139, - "%s: FW_attributes Upper: 0x%x, Lower: 0x%x.\n", - __func__, mcp->mb[15], mcp->mb[6]); - } else - ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x112f, - "%s: FwAttributes [Upper] invalid, MB6:%04x\n", - __func__, mcp->mb[6]); + if (IS_FWI2_CAPABLE(ha)) { + ha->fw_attributes_h = mcp->mb[15]; + ha->fw_attributes_ext[0] = mcp->mb[16]; + ha->fw_attributes_ext[1] = mcp->mb[17]; + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x1139, + "%s: FW_attributes Upper: 0x%x, Lower: 0x%x.\n", + __func__, mcp->mb[15], mcp->mb[6]); + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x112f, + "%s: Ext_FwAttributes Upper: 0x%x, Lower: 0x%x.\n", + __func__, mcp->mb[17], mcp->mb[16]); } failed: @@ -3408,7 +3406,6 @@ qla2x00_dump_ram(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t addr, return rval; } - /* 84XX Support **************************************************************/ struct cs84xx_mgmt_cmd { @@ -4428,7 +4425,8 @@ qla2x00_get_thermal_temp(scsi_qla_host_t *vha, uint16_t *temp, uint16_t *frac) "Entered %s.\n", __func__); /* Integer part */ - rval = qla2x00_read_sfp(vha, 0, &byte, 0x98, 0x01, 1, BIT_13|BIT_0); + rval = qla2x00_read_sfp(vha, 0, &byte, 0x98, 0x01, 1, + BIT_13|BIT_12|BIT_0); if (rval != QLA_SUCCESS) { ql_dbg(ql_dbg_mbx, vha, 0x10c9, "Failed=%x.\n", rval); ha->flags.thermal_supported = 0; @@ -4437,7 +4435,8 @@ qla2x00_get_thermal_temp(scsi_qla_host_t *vha, uint16_t *temp, uint16_t *frac) *temp = byte; /* Fraction part */ - rval = qla2x00_read_sfp(vha, 0, &byte, 0x98, 0x10, 1, BIT_13|BIT_0); + rval = qla2x00_read_sfp(vha, 0, &byte, 0x98, 0x10, 1, + BIT_13|BIT_12|BIT_0); if (rval != QLA_SUCCESS) { ql_dbg(ql_dbg_mbx, vha, 0x1019, "Failed=%x.\n", rval); ha->flags.thermal_supported = 0; @@ -4741,7 +4740,7 @@ qla82xx_mbx_beacon_ctl(scsi_qla_host_t *vha, int enable) } int -qla83xx_write_remote_reg(scsi_qla_host_t *vha, uint32_t reg, uint32_t data) +qla83xx_wr_reg(scsi_qla_host_t *vha, uint32_t reg, uint32_t data) { int rval; struct qla_hw_data *ha = vha->hw; @@ -4814,3 +4813,186 @@ qla2x00_port_logout(scsi_qla_host_t *vha, struct fc_port *fcport) return rval; } +int +qla83xx_rd_reg(scsi_qla_host_t *vha, uint32_t reg, uint32_t *data) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + struct qla_hw_data *ha = vha->hw; + unsigned long retry_max_time = jiffies + (2 * HZ); + + if (!IS_QLA83XX(ha)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx, vha, 0x114b, "Entered %s.\n", __func__); + +retry_rd_reg: + mcp->mb[0] = MBC_READ_REMOTE_REG; + mcp->mb[1] = LSW(reg); + mcp->mb[2] = MSW(reg); + mcp->out_mb = MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_4|MBX_3|MBX_1|MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x114c, + "Failed=%x mb[0]=%x mb[1]=%x.\n", + rval, mcp->mb[0], mcp->mb[1]); + } else { + *data = (mcp->mb[3] | (mcp->mb[4] << 16)); + if (*data == QLA8XXX_BAD_VALUE) { + /* + * During soft-reset CAMRAM register reads might + * return 0xbad0bad0. So retry for MAX of 2 sec + * while reading camram registers. + */ + if (time_after(jiffies, retry_max_time)) { + ql_dbg(ql_dbg_mbx, vha, 0x1141, + "Failure to read CAMRAM register. " + "data=0x%x.\n", *data); + return QLA_FUNCTION_FAILED; + } + msleep(100); + goto retry_rd_reg; + } + ql_dbg(ql_dbg_mbx, vha, 0x1142, "Done %s.\n", __func__); + } + + return rval; +} + +int +qla83xx_restart_nic_firmware(scsi_qla_host_t *vha) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + struct qla_hw_data *ha = vha->hw; + + if (!IS_QLA83XX(ha)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx, vha, 0x1143, "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_RESTART_NIC_FIRMWARE; + mcp->out_mb = MBX_0; + mcp->in_mb = MBX_1|MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x1144, + "Failed=%x mb[0]=%x mb[1]=%x.\n", + rval, mcp->mb[0], mcp->mb[1]); + ha->isp_ops->fw_dump(vha, 0); + } else { + ql_dbg(ql_dbg_mbx, vha, 0x1145, "Done %s.\n", __func__); + } + + return rval; +} + +int +qla83xx_access_control(scsi_qla_host_t *vha, uint16_t options, + uint32_t start_addr, uint32_t end_addr, uint16_t *sector_size) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + uint8_t subcode = (uint8_t)options; + struct qla_hw_data *ha = vha->hw; + + if (!IS_QLA8031(ha)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx, vha, 0x1146, "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_SET_ACCESS_CONTROL; + mcp->mb[1] = options; + mcp->out_mb = MBX_1|MBX_0; + if (subcode & BIT_2) { + mcp->mb[2] = LSW(start_addr); + mcp->mb[3] = MSW(start_addr); + mcp->mb[4] = LSW(end_addr); + mcp->mb[5] = MSW(end_addr); + mcp->out_mb |= MBX_5|MBX_4|MBX_3|MBX_2; + } + mcp->in_mb = MBX_2|MBX_1|MBX_0; + if (!(subcode & (BIT_2 | BIT_5))) + mcp->in_mb |= MBX_4|MBX_3; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x1147, + "Failed=%x mb[0]=%x mb[1]=%x mb[2]=%x mb[3]=%x mb[4]=%x.\n", + rval, mcp->mb[0], mcp->mb[1], mcp->mb[2], mcp->mb[3], + mcp->mb[4]); + ha->isp_ops->fw_dump(vha, 0); + } else { + if (subcode & BIT_5) + *sector_size = mcp->mb[1]; + else if (subcode & (BIT_6 | BIT_7)) { + ql_dbg(ql_dbg_mbx, vha, 0x1148, + "Driver-lock id=%x%x", mcp->mb[4], mcp->mb[3]); + } else if (subcode & (BIT_3 | BIT_4)) { + ql_dbg(ql_dbg_mbx, vha, 0x1149, + "Flash-lock id=%x%x", mcp->mb[4], mcp->mb[3]); + } + ql_dbg(ql_dbg_mbx, vha, 0x114a, "Done %s.\n", __func__); + } + + return rval; +} + +int +qla2x00_dump_mctp_data(scsi_qla_host_t *vha, dma_addr_t req_dma, uint32_t addr, + uint32_t size) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + if (!IS_MCTP_CAPABLE(vha->hw)) + return QLA_FUNCTION_FAILED; + + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x114f, + "Entered %s.\n", __func__); + + mcp->mb[0] = MBC_DUMP_RISC_RAM_EXTENDED; + mcp->mb[1] = LSW(addr); + mcp->mb[2] = MSW(req_dma); + mcp->mb[3] = LSW(req_dma); + mcp->mb[4] = MSW(size); + mcp->mb[5] = LSW(size); + mcp->mb[6] = MSW(MSD(req_dma)); + mcp->mb[7] = LSW(MSD(req_dma)); + mcp->mb[8] = MSW(addr); + /* Setting RAM ID to valid */ + mcp->mb[10] |= BIT_7; + /* For MCTP RAM ID is 0x40 */ + mcp->mb[10] |= 0x40; + + mcp->out_mb |= MBX_10|MBX_8|MBX_7|MBX_6|MBX_5|MBX_4|MBX_3|MBX_2|MBX_1| + MBX_0; + + mcp->in_mb = MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(vha, mcp); + + if (rval != QLA_SUCCESS) { + ql_dbg(ql_dbg_mbx, vha, 0x114e, + "Failed=%x mb[0]=%x.\n", rval, mcp->mb[0]); + } else { + ql_dbg(ql_dbg_mbx + ql_dbg_verbose, vha, 0x114d, + "Done %s.\n", __func__); + } + + return rval; +} diff --git a/drivers/scsi/qla2xxx/qla_mid.c b/drivers/scsi/qla2xxx/qla_mid.c index 3e8b32419e68..bd4708a422cd 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-2011 QLogic Corporation + * Copyright (c) 2003-2012 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -476,7 +476,6 @@ qla24xx_create_vhost(struct fc_vport *fc_vport) vha->req = base_vha->req; host->can_queue = base_vha->req->length + 128; - host->this_id = 255; host->cmd_per_lun = 3; if (IS_T10_PI_CAPABLE(ha) && ql2xenabledif) host->max_cmd_len = 32; @@ -643,7 +642,7 @@ qla25xx_create_req_que(struct qla_hw_data *ha, uint16_t options, &req->dma, GFP_KERNEL); if (req->ring == NULL) { ql_log(ql_log_fatal, base_vha, 0x00da, - "Failed to allocte memory for request_ring.\n"); + "Failed to allocate memory for request_ring.\n"); goto que_failed; } diff --git a/drivers/scsi/qla2xxx/qla_nx.c b/drivers/scsi/qla2xxx/qla_nx.c index 7cfdf2bd8edb..14cd361742fa 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-2011 QLogic Corporation + * Copyright (c) 2003-2012 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -1612,23 +1612,6 @@ qla82xx_get_fw_offs(struct qla_hw_data *ha) } /* PCI related functions */ -char * -qla82xx_pci_info_str(struct scsi_qla_host *vha, char *str) -{ - struct qla_hw_data *ha = vha->hw; - char lwstr[6]; - uint16_t lnk; - - pcie_capability_read_word(ha->pdev, PCI_EXP_LNKSTA, &lnk); - ha->link_width = (lnk >> 4) & 0x3f; - - strcpy(str, "PCIe ("); - strcat(str, "2.5Gb/s "); - snprintf(lwstr, sizeof(lwstr), "x%d)", ha->link_width); - strcat(str, lwstr); - return str; -} - int qla82xx_pci_region_offset(struct pci_dev *pdev, int region) { unsigned long val = 0; @@ -2320,6 +2303,29 @@ void qla82xx_init_flags(struct qla_hw_data *ha) } inline void +qla82xx_set_idc_version(scsi_qla_host_t *vha) +{ + int idc_ver; + uint32_t drv_active; + struct qla_hw_data *ha = vha->hw; + + drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); + if (drv_active == (QLA82XX_DRV_ACTIVE << (ha->portnum * 4))) { + qla82xx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION, + QLA82XX_IDC_VERSION); + ql_log(ql_log_info, vha, 0xb082, + "IDC version updated to %d\n", QLA82XX_IDC_VERSION); + } else { + idc_ver = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_IDC_VERSION); + if (idc_ver != QLA82XX_IDC_VERSION) + ql_log(ql_log_info, vha, 0xb083, + "qla2xxx driver IDC version %d is not compatible " + "with IDC version %d of the other drivers\n", + QLA82XX_IDC_VERSION, idc_ver); + } +} + +inline void qla82xx_set_drv_active(scsi_qla_host_t *vha) { uint32_t drv_active; @@ -2353,7 +2359,7 @@ qla82xx_need_reset(struct qla_hw_data *ha) uint32_t drv_state; int rval; - if (ha->flags.isp82xx_reset_owner) + if (ha->flags.nic_core_reset_owner) return 1; else { drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); @@ -2860,7 +2866,7 @@ qla82xx_device_bootstrap(scsi_qla_host_t *vha) timeout = msleep_interruptible(200); if (timeout) { qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, - QLA82XX_DEV_FAILED); + QLA8XXX_DEV_FAILED); return QLA_FUNCTION_FAILED; } @@ -2891,10 +2897,7 @@ dev_initialize: /* set to DEV_INITIALIZING */ ql_log(ql_log_info, vha, 0x009e, "HW State: INITIALIZING.\n"); - qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_INITIALIZING); - - /* Driver that sets device state to initializating sets IDC version */ - qla82xx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION, QLA82XX_IDC_VERSION); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA8XXX_DEV_INITIALIZING); qla82xx_idc_unlock(ha); rval = qla82xx_start_firmware(vha); @@ -2904,14 +2907,14 @@ dev_initialize: ql_log(ql_log_fatal, vha, 0x00ad, "HW State: FAILED.\n"); qla82xx_clear_drv_active(ha); - qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_FAILED); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA8XXX_DEV_FAILED); return rval; } dev_ready: ql_log(ql_log_info, vha, 0x00ae, "HW State: READY.\n"); - qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_READY); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA8XXX_DEV_READY); return QLA_SUCCESS; } @@ -2935,7 +2938,7 @@ qla82xx_need_qsnt_handler(scsi_qla_host_t *vha) if (vha->flags.online) { /*Block any further I/O and wait for pending cmnds to complete*/ - qla82xx_quiescent_state_cleanup(vha); + qla2x00_quiesce_io(vha); } /* Set the quiescence ready bit */ @@ -2960,7 +2963,7 @@ qla82xx_need_qsnt_handler(scsi_qla_host_t *vha) "DRV_STATE:%d.\n", QLA2XXX_DRIVER_NAME, drv_active, drv_state); qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, - QLA82XX_DEV_READY); + QLA8XXX_DEV_READY); ql_log(ql_log_info, vha, 0xb025, "HW State: DEV_READY.\n"); qla82xx_idc_unlock(ha); @@ -2981,10 +2984,10 @@ qla82xx_need_qsnt_handler(scsi_qla_host_t *vha) } dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); /* everyone acked so set the state to DEV_QUIESCENCE */ - if (dev_state == QLA82XX_DEV_NEED_QUIESCENT) { + if (dev_state == QLA8XXX_DEV_NEED_QUIESCENT) { ql_log(ql_log_info, vha, 0xb026, "HW State: DEV_QUIESCENT.\n"); - qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_QUIESCENT); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA8XXX_DEV_QUIESCENT); } } @@ -3014,8 +3017,8 @@ qla82xx_wait_for_state_change(scsi_qla_host_t *vha, uint32_t curr_state) return dev_state; } -static void -qla82xx_dev_failed_handler(scsi_qla_host_t *vha) +void +qla8xxx_dev_failed_handler(scsi_qla_host_t *vha) { struct qla_hw_data *ha = vha->hw; @@ -3023,9 +3026,10 @@ qla82xx_dev_failed_handler(scsi_qla_host_t *vha) ql_log(ql_log_fatal, vha, 0x00b8, "Disabling the board.\n"); - qla82xx_idc_lock(ha); - qla82xx_clear_drv_active(ha); - qla82xx_idc_unlock(ha); + if (IS_QLA82XX(ha)) { + qla82xx_clear_drv_active(ha); + qla82xx_idc_unlock(ha); + } /* Set DEV_FAILED flag to disable timer */ vha->device_flags |= DFLG_DEV_FAILED; @@ -3064,7 +3068,7 @@ qla82xx_need_reset_handler(scsi_qla_host_t *vha) } drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); - if (!ha->flags.isp82xx_reset_owner) { + if (!ha->flags.nic_core_reset_owner) { ql_dbg(ql_dbg_p3p, vha, 0xb028, "reset_acknowledged by 0x%x\n", ha->portnum); qla82xx_set_rst_ready(ha); @@ -3076,7 +3080,7 @@ qla82xx_need_reset_handler(scsi_qla_host_t *vha) } /* wait for 10 seconds for reset ack from all functions */ - reset_timeout = jiffies + (ha->nx_reset_timeout * HZ); + reset_timeout = jiffies + (ha->fcoe_reset_timeout * HZ); drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); @@ -3088,7 +3092,7 @@ qla82xx_need_reset_handler(scsi_qla_host_t *vha) drv_state, drv_active, dev_state, active_mask); while (drv_state != drv_active && - dev_state != QLA82XX_DEV_INITIALIZING) { + dev_state != QLA8XXX_DEV_INITIALIZING) { if (time_after_eq(jiffies, reset_timeout)) { ql_log(ql_log_warn, vha, 0x00b5, "Reset timeout.\n"); @@ -3099,7 +3103,7 @@ qla82xx_need_reset_handler(scsi_qla_host_t *vha) qla82xx_idc_lock(ha); drv_state = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_STATE); drv_active = qla82xx_rd_32(ha, QLA82XX_CRB_DRV_ACTIVE); - if (ha->flags.isp82xx_reset_owner) + if (ha->flags.nic_core_reset_owner) drv_active &= active_mask; dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); } @@ -3115,11 +3119,11 @@ qla82xx_need_reset_handler(scsi_qla_host_t *vha) dev_state < MAX_STATES ? qdev_state(dev_state) : "Unknown"); /* Force to DEV_COLD unless someone else is starting a reset */ - if (dev_state != QLA82XX_DEV_INITIALIZING && - dev_state != QLA82XX_DEV_COLD) { + if (dev_state != QLA8XXX_DEV_INITIALIZING && + dev_state != QLA8XXX_DEV_COLD) { ql_log(ql_log_info, vha, 0x00b7, "HW State: COLD/RE-INIT.\n"); - qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA82XX_DEV_COLD); + qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, QLA8XXX_DEV_COLD); qla82xx_set_rst_ready(ha); if (ql2xmdenable) { if (qla82xx_md_collect(vha)) @@ -3226,8 +3230,10 @@ qla82xx_device_state_handler(scsi_qla_host_t *vha) int loopcount = 0; qla82xx_idc_lock(ha); - if (!vha->flags.init_done) + if (!vha->flags.init_done) { qla82xx_set_drv_active(vha); + qla82xx_set_idc_version(vha); + } dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); old_dev_state = dev_state; @@ -3237,7 +3243,7 @@ qla82xx_device_state_handler(scsi_qla_host_t *vha) dev_state < MAX_STATES ? qdev_state(dev_state) : "Unknown"); /* wait for 30 seconds for device to go ready */ - dev_init_timeout = jiffies + (ha->nx_dev_init_timeout * HZ); + dev_init_timeout = jiffies + (ha->fcoe_dev_init_timeout * HZ); while (1) { @@ -3261,18 +3267,18 @@ qla82xx_device_state_handler(scsi_qla_host_t *vha) } switch (dev_state) { - case QLA82XX_DEV_READY: - ha->flags.isp82xx_reset_owner = 0; - goto exit; - case QLA82XX_DEV_COLD: + case QLA8XXX_DEV_READY: + ha->flags.nic_core_reset_owner = 0; + goto rel_lock; + case QLA8XXX_DEV_COLD: rval = qla82xx_device_bootstrap(vha); break; - case QLA82XX_DEV_INITIALIZING: + case QLA8XXX_DEV_INITIALIZING: qla82xx_idc_unlock(ha); msleep(1000); qla82xx_idc_lock(ha); break; - case QLA82XX_DEV_NEED_RESET: + case QLA8XXX_DEV_NEED_RESET: if (!ql2xdontresethba) qla82xx_need_reset_handler(vha); else { @@ -3281,31 +3287,31 @@ qla82xx_device_state_handler(scsi_qla_host_t *vha) qla82xx_idc_lock(ha); } dev_init_timeout = jiffies + - (ha->nx_dev_init_timeout * HZ); + (ha->fcoe_dev_init_timeout * HZ); break; - case QLA82XX_DEV_NEED_QUIESCENT: + case QLA8XXX_DEV_NEED_QUIESCENT: qla82xx_need_qsnt_handler(vha); /* Reset timeout value after quiescence handler */ - dev_init_timeout = jiffies + (ha->nx_dev_init_timeout\ + dev_init_timeout = jiffies + (ha->fcoe_dev_init_timeout\ * HZ); break; - case QLA82XX_DEV_QUIESCENT: + case QLA8XXX_DEV_QUIESCENT: /* Owner will exit and other will wait for the state * to get changed */ if (ha->flags.quiesce_owner) - goto exit; + goto rel_lock; qla82xx_idc_unlock(ha); msleep(1000); qla82xx_idc_lock(ha); /* Reset timeout value after quiescence handler */ - dev_init_timeout = jiffies + (ha->nx_dev_init_timeout\ + dev_init_timeout = jiffies + (ha->fcoe_dev_init_timeout\ * HZ); break; - case QLA82XX_DEV_FAILED: - qla82xx_dev_failed_handler(vha); + case QLA8XXX_DEV_FAILED: + qla8xxx_dev_failed_handler(vha); rval = QLA_FUNCTION_FAILED; goto exit; default: @@ -3315,8 +3321,9 @@ qla82xx_device_state_handler(scsi_qla_host_t *vha) } loopcount++; } -exit: +rel_lock: qla82xx_idc_unlock(ha); +exit: return rval; } @@ -3364,22 +3371,30 @@ void qla82xx_watchdog(scsi_qla_host_t *vha) struct qla_hw_data *ha = vha->hw; /* don't poll if reset is going on */ - if (!ha->flags.isp82xx_reset_hdlr_active) { + if (!ha->flags.nic_core_reset_hdlr_active) { dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); if (qla82xx_check_temp(vha)) { set_bit(ISP_UNRECOVERABLE, &vha->dpc_flags); ha->flags.isp82xx_fw_hung = 1; qla82xx_clear_pending_mbx(vha); - } else if (dev_state == QLA82XX_DEV_NEED_RESET && + } else if (dev_state == QLA8XXX_DEV_NEED_RESET && !test_bit(ISP_ABORT_NEEDED, &vha->dpc_flags)) { ql_log(ql_log_warn, vha, 0x6001, "Adapter reset needed.\n"); set_bit(ISP_ABORT_NEEDED, &vha->dpc_flags); - } else if (dev_state == QLA82XX_DEV_NEED_QUIESCENT && + } else if (dev_state == QLA8XXX_DEV_NEED_QUIESCENT && !test_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags)) { ql_log(ql_log_warn, vha, 0x6002, "Quiescent needed.\n"); set_bit(ISP_QUIESCE_NEEDED, &vha->dpc_flags); + } else if (dev_state == QLA8XXX_DEV_FAILED && + !test_bit(ISP_UNRECOVERABLE, &vha->dpc_flags) && + vha->flags.online == 1) { + ql_log(ql_log_warn, vha, 0xb055, + "Adapter state is failed. Offlining.\n"); + set_bit(ISP_UNRECOVERABLE, &vha->dpc_flags); + ha->flags.isp82xx_fw_hung = 1; + qla82xx_clear_pending_mbx(vha); } else { if (qla82xx_check_fw_alive(vha)) { ql_dbg(ql_dbg_timer, vha, 0x6011, @@ -3441,12 +3456,12 @@ qla82xx_set_reset_owner(scsi_qla_host_t *vha) uint32_t dev_state; dev_state = qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE); - if (dev_state == QLA82XX_DEV_READY) { + 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, - QLA82XX_DEV_NEED_RESET); - ha->flags.isp82xx_reset_owner = 1; + 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 @@ -3477,7 +3492,7 @@ qla82xx_abort_isp(scsi_qla_host_t *vha) "Device in failed state, exiting.\n"); return QLA_SUCCESS; } - ha->flags.isp82xx_reset_hdlr_active = 1; + ha->flags.nic_core_reset_hdlr_active = 1; qla82xx_idc_lock(ha); qla82xx_set_reset_owner(vha); @@ -3491,7 +3506,7 @@ qla82xx_abort_isp(scsi_qla_host_t *vha) if (rval == QLA_SUCCESS) { ha->flags.isp82xx_fw_hung = 0; - ha->flags.isp82xx_reset_hdlr_active = 0; + ha->flags.nic_core_reset_hdlr_active = 0; qla82xx_restart_isp(vha); } @@ -4026,7 +4041,7 @@ qla82xx_minidump_process_rdmem(scsi_qla_host_t *vha, if (r_addr & 0xf) { ql_log(ql_log_warn, vha, 0xb033, - "Read addr 0x%x not 16 bytes alligned\n", r_addr); + "Read addr 0x%x not 16 bytes aligned\n", r_addr); return rval; } diff --git a/drivers/scsi/qla2xxx/qla_nx.h b/drivers/scsi/qla2xxx/qla_nx.h index 6eb210e3cc63..6c953e8c08f0 100644 --- a/drivers/scsi/qla2xxx/qla_nx.h +++ b/drivers/scsi/qla2xxx/qla_nx.h @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2012 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -542,14 +542,15 @@ #define QLA82XX_CRB_DRV_IDC_VERSION (QLA82XX_CAM_RAM(0x174)) /* Every driver should use these Device State */ -#define QLA82XX_DEV_COLD 1 -#define QLA82XX_DEV_INITIALIZING 2 -#define QLA82XX_DEV_READY 3 -#define QLA82XX_DEV_NEED_RESET 4 -#define QLA82XX_DEV_NEED_QUIESCENT 5 -#define QLA82XX_DEV_FAILED 6 -#define QLA82XX_DEV_QUIESCENT 7 +#define QLA8XXX_DEV_COLD 1 +#define QLA8XXX_DEV_INITIALIZING 2 +#define QLA8XXX_DEV_READY 3 +#define QLA8XXX_DEV_NEED_RESET 4 +#define QLA8XXX_DEV_NEED_QUIESCENT 5 +#define QLA8XXX_DEV_FAILED 6 +#define QLA8XXX_DEV_QUIESCENT 7 #define MAX_STATES 8 /* Increment if new state added */ +#define QLA8XXX_BAD_VALUE 0xbad0bad0 #define QLA82XX_IDC_VERSION 1 #define QLA82XX_ROM_DEV_INIT_TIMEOUT 30 diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index d3052622e77a..d501bf5f806b 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2012 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -113,11 +113,11 @@ MODULE_PARM_DESC(ql2xfdmienable, static int ql2xmaxqdepth = MAX_Q_DEPTH; module_param(ql2xmaxqdepth, int, S_IRUGO|S_IWUSR); MODULE_PARM_DESC(ql2xmaxqdepth, - "Maximum queue depth to report for target devices."); + "Maximum queue depth to set for each LUN. " + "Default is 32."); -/* Do not change the value of this after module load */ -int ql2xenabledif = 0; -module_param(ql2xenabledif, int, S_IRUGO|S_IWUSR); +int ql2xenabledif = 2; +module_param(ql2xenabledif, int, S_IRUGO); MODULE_PARM_DESC(ql2xenabledif, " Enable T10-CRC-DIF " " Default is 0 - No DIF Support. 1 - Enable it" @@ -1078,7 +1078,7 @@ __qla2xxx_eh_generic_reset(char *name, enum nexus_wait_type type, if (qla2x00_eh_wait_for_pending_commands(vha, cmd->device->id, cmd->device->lun, type) != QLA_SUCCESS) { ql_log(ql_log_warn, vha, 0x800d, - "wait for peding cmds failed for cmd=%p.\n", cmd); + "wait for pending cmds failed for cmd=%p.\n", cmd); goto eh_reset_failed; } @@ -1177,7 +1177,7 @@ qla2xxx_eh_bus_reset(struct scsi_cmnd *cmd) eh_bus_reset_done: ql_log(ql_log_warn, vha, 0x802b, "BUS RESET %s nexus=%ld:%d:%d.\n", - (ret == FAILED) ? "FAILED" : "SUCCEDED", vha->host_no, id, lun); + (ret == FAILED) ? "FAILED" : "SUCCEEDED", vha->host_no, id, lun); return ret; } @@ -1357,6 +1357,9 @@ qla2xxx_slave_configure(struct scsi_device *sdev) scsi_qla_host_t *vha = shost_priv(sdev->host); struct req_que *req = vha->req; + if (IS_T10_PI_CAPABLE(vha->hw)) + blk_queue_update_dma_alignment(sdev->request_queue, 0x7); + if (sdev->tagged_supported) scsi_activate_tcq(sdev, req->max_q_depth); else @@ -1919,7 +1922,7 @@ static struct isp_operations qla82xx_isp_ops = { .nvram_config = qla81xx_nvram_config, .update_fw_options = qla24xx_update_fw_options, .load_risc = qla82xx_load_risc, - .pci_info_str = qla82xx_pci_info_str, + .pci_info_str = qla24xx_pci_info_str, .fw_version_str = qla24xx_fw_version_str, .intr_handler = qla82xx_intr_handler, .enable_intrs = qla82xx_enable_intrs, @@ -2149,7 +2152,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) scsi_qla_host_t *base_vha = NULL; struct qla_hw_data *ha; char pci_info[30]; - char fw_str[30]; + char fw_str[30], wq_name[30]; struct scsi_host_template *sht; int bars, mem_only = 0; uint16_t req_length = 0, rsp_length = 0; @@ -2203,12 +2206,14 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->mem_only = mem_only; spin_lock_init(&ha->hardware_lock); spin_lock_init(&ha->vport_slock); + mutex_init(&ha->selflogin_lock); /* Set ISP-type information. */ qla2x00_set_isp_flags(ha); /* Set EEH reset type to fundamental if required by hba */ - if (IS_QLA24XX(ha) || IS_QLA25XX(ha) || IS_QLA81XX(ha)) + if (IS_QLA24XX(ha) || IS_QLA25XX(ha) || IS_QLA81XX(ha) || + IS_QLA83XX(ha)) pdev->needs_freset = 1; ha->prev_topology = 0; @@ -2318,6 +2323,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->nvram_conf_off = FARX_ACCESS_NVRAM_CONF; ha->nvram_data_off = FARX_ACCESS_NVRAM_DATA; } else if (IS_QLA83XX(ha)) { + ha->portnum = PCI_FUNC(ha->pdev->devfn); ha->max_fibre_devices = MAX_FIBRE_DEVICES_2400; ha->mbx_count = MAILBOX_REGISTER_COUNT; req_length = REQUEST_ENTRY_CNT_24XX; @@ -2416,7 +2422,6 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) host->can_queue, base_vha->req, base_vha->mgmt_svr_loop_id, host->sg_tablesize); host->max_id = ha->max_fibre_devices; - host->this_id = 255; host->cmd_per_lun = 3; host->unique_id = host->host_no; if (IS_T10_PI_CAPABLE(ha) && ql2xenabledif) @@ -2499,7 +2504,7 @@ que_init: if (IS_QLA82XX(ha)) { qla82xx_idc_lock(ha); qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, - QLA82XX_DEV_FAILED); + QLA8XXX_DEV_FAILED); qla82xx_idc_unlock(ha); ql_log(ql_log_fatal, base_vha, 0x00d7, "HW State: FAILED.\n"); @@ -2542,6 +2547,20 @@ que_init: */ qla2xxx_wake_dpc(base_vha); + if (IS_QLA8031(ha) || IS_MCTP_CAPABLE(ha)) { + sprintf(wq_name, "qla2xxx_%lu_dpc_lp_wq", base_vha->host_no); + ha->dpc_lp_wq = create_singlethread_workqueue(wq_name); + INIT_WORK(&ha->idc_aen, qla83xx_service_idc_aen); + + sprintf(wq_name, "qla2xxx_%lu_dpc_hp_wq", base_vha->host_no); + ha->dpc_hp_wq = create_singlethread_workqueue(wq_name); + INIT_WORK(&ha->nic_core_reset, qla83xx_nic_core_reset_work); + INIT_WORK(&ha->idc_state_handler, + qla83xx_idc_state_handler_work); + INIT_WORK(&ha->nic_core_unrecoverable, + qla83xx_nic_core_unrecoverable_work); + } + skip_dpc: list_add_tail(&base_vha->list, &ha->vp_list); base_vha->host->irq = ha->pdev->irq; @@ -2557,7 +2576,7 @@ skip_dpc: if (IS_T10_PI_CAPABLE(ha) && ql2xenabledif) { if (ha->fw_attributes & BIT_4) { - int prot = 0; + int prot = 0, guard; base_vha->flags.difdix_supported = 1; ql_dbg(ql_dbg_init, base_vha, 0x00f1, "Registering for DIF/DIX type 1 and 3 protection.\n"); @@ -2570,7 +2589,14 @@ skip_dpc: | SHOST_DIX_TYPE1_PROTECTION | SHOST_DIX_TYPE2_PROTECTION | SHOST_DIX_TYPE3_PROTECTION); - scsi_host_set_guard(host, SHOST_DIX_GUARD_CRC); + + guard = SHOST_DIX_GUARD_CRC; + + if (IS_PI_IPGUARD_CAPABLE(ha) && + (ql2xenabledif > 1 || IS_PI_DIFB_DIX0_CAPABLE(ha))) + guard |= SHOST_DIX_GUARD_IP; + + scsi_host_set_guard(host, guard); } else base_vha->flags.difdix_supported = 0; } @@ -2750,6 +2776,14 @@ qla2x00_remove_one(struct pci_dev *pdev) } 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"); + } + set_bit(UNLOADING, &base_vha->dpc_flags); qla2x00_abort_all_cmds(base_vha, DID_NO_CONNECT << 16); @@ -2771,6 +2805,21 @@ qla2x00_remove_one(struct pci_dev *pdev) ha->wq = NULL; } + /* Cancel all work and destroy DPC workqueues */ + if (ha->dpc_lp_wq) { + cancel_work_sync(&ha->idc_aen); + destroy_workqueue(ha->dpc_lp_wq); + ha->dpc_lp_wq = NULL; + } + + if (ha->dpc_hp_wq) { + cancel_work_sync(&ha->nic_core_reset); + cancel_work_sync(&ha->idc_state_handler); + cancel_work_sync(&ha->nic_core_unrecoverable); + destroy_workqueue(ha->dpc_hp_wq); + ha->dpc_hp_wq = NULL; + } + /* Kill the kernel thread for this host */ if (ha->dpc_thread) { struct task_struct *t = ha->dpc_thread; @@ -2837,7 +2886,6 @@ qla2x00_free_device(scsi_qla_host_t *vha) qla2x00_stop_dpc_thread(vha); qla25xx_delete_queues(vha); - if (ha->flags.fce_enabled) qla2x00_disable_fce_trace(vha, NULL, NULL); @@ -2872,6 +2920,7 @@ void qla2x00_free_fcports(struct scsi_qla_host *vha) list_for_each_entry_safe(fcport, tfcport, &vha->vp_fcports, list) { list_del(&fcport->list); + qla2x00_clear_loop_id(fcport); kfree(fcport); fcport = NULL; } @@ -3169,6 +3218,18 @@ qla2x00_mem_alloc(struct qla_hw_data *ha, uint16_t req_len, uint16_t rsp_len, } INIT_LIST_HEAD(&ha->vp_list); + + /* Allocate memory for our loop_id bitmap */ + ha->loop_id_map = kzalloc(BITS_TO_LONGS(LOOPID_MAP_SIZE) * sizeof(long), + GFP_KERNEL); + if (!ha->loop_id_map) + goto fail_async_pd; + else { + qla2x00_set_reserved_loop_ids(ha); + ql_dbg_pci(ql_dbg_init, ha->pdev, 0x0123, + "loop_id_map=%p. \n", ha->loop_id_map); + } + return 1; fail_async_pd: @@ -3280,6 +3341,10 @@ qla2x00_mem_free(struct qla_hw_data *ha) { qla2x00_free_fw_dump(ha); + if (ha->mctp_dump) + dma_free_coherent(&ha->pdev->dev, MCTP_DUMP_SIZE, ha->mctp_dump, + ha->mctp_dump_dma); + if (ha->srb_mempool) mempool_destroy(ha->srb_mempool); @@ -3352,6 +3417,7 @@ qla2x00_mem_free(struct qla_hw_data *ha) kfree(ha->nvram); kfree(ha->npiv_info); kfree(ha->swl); + kfree(ha->loop_id_map); ha->srb_mempool = NULL; ha->ctx_mempool = NULL; @@ -3687,13 +3753,651 @@ void qla2x00_relogin(struct scsi_qla_host *vha) } if (fcport->login_retry == 0 && status != QLA_SUCCESS) - fcport->loop_id = FC_NO_LOOP_ID; + qla2x00_clear_loop_id(fcport); } if (test_bit(LOOP_RESYNC_NEEDED, &vha->dpc_flags)) break; } } +/* Schedule work on any of the dpc-workqueues */ +void +qla83xx_schedule_work(scsi_qla_host_t *base_vha, int work_code) +{ + struct qla_hw_data *ha = base_vha->hw; + + switch (work_code) { + case MBA_IDC_AEN: /* 0x8200 */ + if (ha->dpc_lp_wq) + queue_work(ha->dpc_lp_wq, &ha->idc_aen); + break; + + case QLA83XX_NIC_CORE_RESET: /* 0x1 */ + if (!ha->flags.nic_core_reset_hdlr_active) { + if (ha->dpc_hp_wq) + queue_work(ha->dpc_hp_wq, &ha->nic_core_reset); + } else + ql_dbg(ql_dbg_p3p, base_vha, 0xb05e, + "NIC Core reset is already active. Skip " + "scheduling it again.\n"); + break; + case QLA83XX_IDC_STATE_HANDLER: /* 0x2 */ + if (ha->dpc_hp_wq) + queue_work(ha->dpc_hp_wq, &ha->idc_state_handler); + break; + case QLA83XX_NIC_CORE_UNRECOVERABLE: /* 0x3 */ + if (ha->dpc_hp_wq) + queue_work(ha->dpc_hp_wq, &ha->nic_core_unrecoverable); + break; + default: + ql_log(ql_log_warn, base_vha, 0xb05f, + "Unknow work-code=0x%x.\n", work_code); + } + + return; +} + +/* Work: Perform NIC Core Unrecoverable state handling */ +void +qla83xx_nic_core_unrecoverable_work(struct work_struct *work) +{ + struct qla_hw_data *ha = + container_of(work, struct qla_hw_data, nic_core_unrecoverable); + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); + uint32_t dev_state = 0; + + qla83xx_idc_lock(base_vha, 0); + qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, &dev_state); + qla83xx_reset_ownership(base_vha); + if (ha->flags.nic_core_reset_owner) { + ha->flags.nic_core_reset_owner = 0; + qla83xx_wr_reg(base_vha, QLA83XX_IDC_DEV_STATE, + QLA8XXX_DEV_FAILED); + ql_log(ql_log_info, base_vha, 0xb060, "HW State: FAILED.\n"); + qla83xx_schedule_work(base_vha, QLA83XX_IDC_STATE_HANDLER); + } + qla83xx_idc_unlock(base_vha, 0); +} + +/* Work: Execute IDC state handler */ +void +qla83xx_idc_state_handler_work(struct work_struct *work) +{ + struct qla_hw_data *ha = + container_of(work, struct qla_hw_data, idc_state_handler); + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); + uint32_t dev_state = 0; + + qla83xx_idc_lock(base_vha, 0); + qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, &dev_state); + if (dev_state == QLA8XXX_DEV_FAILED || + dev_state == QLA8XXX_DEV_NEED_QUIESCENT) + qla83xx_idc_state_handler(base_vha); + qla83xx_idc_unlock(base_vha, 0); +} + +int +qla83xx_check_nic_core_fw_alive(scsi_qla_host_t *base_vha) +{ + int rval = QLA_SUCCESS; + unsigned long heart_beat_wait = jiffies + (1 * HZ); + uint32_t heart_beat_counter1, heart_beat_counter2; + + do { + if (time_after(jiffies, heart_beat_wait)) { + ql_dbg(ql_dbg_p3p, base_vha, 0xb07c, + "Nic Core f/w is not alive.\n"); + rval = QLA_FUNCTION_FAILED; + break; + } + + qla83xx_idc_lock(base_vha, 0); + qla83xx_rd_reg(base_vha, QLA83XX_FW_HEARTBEAT, + &heart_beat_counter1); + qla83xx_idc_unlock(base_vha, 0); + msleep(100); + qla83xx_idc_lock(base_vha, 0); + qla83xx_rd_reg(base_vha, QLA83XX_FW_HEARTBEAT, + &heart_beat_counter2); + qla83xx_idc_unlock(base_vha, 0); + } while (heart_beat_counter1 == heart_beat_counter2); + + return rval; +} + +/* Work: Perform NIC Core Reset handling */ +void +qla83xx_nic_core_reset_work(struct work_struct *work) +{ + struct qla_hw_data *ha = + container_of(work, struct qla_hw_data, nic_core_reset); + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); + uint32_t dev_state = 0; + + if (IS_QLA2031(ha)) { + if (qla2xxx_mctp_dump(base_vha) != QLA_SUCCESS) + ql_log(ql_log_warn, base_vha, 0xb081, + "Failed to dump mctp\n"); + return; + } + + if (!ha->flags.nic_core_reset_hdlr_active) { + if (qla83xx_check_nic_core_fw_alive(base_vha) == QLA_SUCCESS) { + qla83xx_idc_lock(base_vha, 0); + qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, + &dev_state); + qla83xx_idc_unlock(base_vha, 0); + if (dev_state != QLA8XXX_DEV_NEED_RESET) { + ql_dbg(ql_dbg_p3p, base_vha, 0xb07a, + "Nic Core f/w is alive.\n"); + return; + } + } + + ha->flags.nic_core_reset_hdlr_active = 1; + if (qla83xx_nic_core_reset(base_vha)) { + /* NIC Core reset failed. */ + ql_dbg(ql_dbg_p3p, base_vha, 0xb061, + "NIC Core reset failed.\n"); + } + ha->flags.nic_core_reset_hdlr_active = 0; + } +} + +/* Work: Handle 8200 IDC aens */ +void +qla83xx_service_idc_aen(struct work_struct *work) +{ + struct qla_hw_data *ha = + container_of(work, struct qla_hw_data, idc_aen); + scsi_qla_host_t *base_vha = pci_get_drvdata(ha->pdev); + uint32_t dev_state, idc_control; + + qla83xx_idc_lock(base_vha, 0); + qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, &dev_state); + qla83xx_rd_reg(base_vha, QLA83XX_IDC_CONTROL, &idc_control); + qla83xx_idc_unlock(base_vha, 0); + if (dev_state == QLA8XXX_DEV_NEED_RESET) { + if (idc_control & QLA83XX_IDC_GRACEFUL_RESET) { + ql_dbg(ql_dbg_p3p, base_vha, 0xb062, + "Application requested NIC Core Reset.\n"); + qla83xx_schedule_work(base_vha, QLA83XX_NIC_CORE_RESET); + } else if (qla83xx_check_nic_core_fw_alive(base_vha) == + QLA_SUCCESS) { + ql_dbg(ql_dbg_p3p, base_vha, 0xb07b, + "Other protocol driver requested NIC Core Reset.\n"); + qla83xx_schedule_work(base_vha, QLA83XX_NIC_CORE_RESET); + } + } else if (dev_state == QLA8XXX_DEV_FAILED || + dev_state == QLA8XXX_DEV_NEED_QUIESCENT) { + qla83xx_schedule_work(base_vha, QLA83XX_IDC_STATE_HANDLER); + } +} + +static void +qla83xx_wait_logic(void) +{ + int i; + + /* Yield CPU */ + if (!in_interrupt()) { + /* + * Wait about 200ms before retrying again. + * This controls the number of retries for single + * lock operation. + */ + msleep(100); + schedule(); + } else { + for (i = 0; i < 20; i++) + cpu_relax(); /* This a nop instr on i386 */ + } +} + +int +qla83xx_force_lock_recovery(scsi_qla_host_t *base_vha) +{ + int rval; + uint32_t data; + uint32_t idc_lck_rcvry_stage_mask = 0x3; + uint32_t idc_lck_rcvry_owner_mask = 0x3c; + struct qla_hw_data *ha = base_vha->hw; + + rval = qla83xx_rd_reg(base_vha, QLA83XX_IDC_LOCK_RECOVERY, &data); + if (rval) + return rval; + + if ((data & idc_lck_rcvry_stage_mask) > 0) { + return QLA_SUCCESS; + } else { + data = (IDC_LOCK_RECOVERY_STAGE1) | (ha->portnum << 2); + rval = qla83xx_wr_reg(base_vha, QLA83XX_IDC_LOCK_RECOVERY, + data); + if (rval) + return rval; + + msleep(200); + + rval = qla83xx_rd_reg(base_vha, QLA83XX_IDC_LOCK_RECOVERY, + &data); + if (rval) + return rval; + + if (((data & idc_lck_rcvry_owner_mask) >> 2) == ha->portnum) { + data &= (IDC_LOCK_RECOVERY_STAGE2 | + ~(idc_lck_rcvry_stage_mask)); + rval = qla83xx_wr_reg(base_vha, + QLA83XX_IDC_LOCK_RECOVERY, data); + if (rval) + return rval; + + /* Forcefully perform IDC UnLock */ + rval = qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_UNLOCK, + &data); + if (rval) + return rval; + /* Clear lock-id by setting 0xff */ + rval = qla83xx_wr_reg(base_vha, QLA83XX_DRIVER_LOCKID, + 0xff); + if (rval) + return rval; + /* Clear lock-recovery by setting 0x0 */ + rval = qla83xx_wr_reg(base_vha, + QLA83XX_IDC_LOCK_RECOVERY, 0x0); + if (rval) + return rval; + } else + return QLA_SUCCESS; + } + + return rval; +} + +int +qla83xx_idc_lock_recovery(scsi_qla_host_t *base_vha) +{ + int rval = QLA_SUCCESS; + uint32_t o_drv_lockid, n_drv_lockid; + unsigned long lock_recovery_timeout; + + lock_recovery_timeout = jiffies + QLA83XX_MAX_LOCK_RECOVERY_WAIT; +retry_lockid: + rval = qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCKID, &o_drv_lockid); + if (rval) + goto exit; + + /* MAX wait time before forcing IDC Lock recovery = 2 secs */ + if (time_after_eq(jiffies, lock_recovery_timeout)) { + if (qla83xx_force_lock_recovery(base_vha) == QLA_SUCCESS) + return QLA_SUCCESS; + else + return QLA_FUNCTION_FAILED; + } + + rval = qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCKID, &n_drv_lockid); + if (rval) + goto exit; + + if (o_drv_lockid == n_drv_lockid) { + qla83xx_wait_logic(); + goto retry_lockid; + } else + return QLA_SUCCESS; + +exit: + return rval; +} + +void +qla83xx_idc_lock(scsi_qla_host_t *base_vha, uint16_t requester_id) +{ + uint16_t options = (requester_id << 15) | BIT_6; + uint32_t data; + struct qla_hw_data *ha = base_vha->hw; + + /* IDC-lock implementation using driver-lock/lock-id remote registers */ +retry_lock: + if (qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCK, &data) + == QLA_SUCCESS) { + if (data) { + /* Setting lock-id to our function-number */ + qla83xx_wr_reg(base_vha, QLA83XX_DRIVER_LOCKID, + ha->portnum); + } else { + ql_dbg(ql_dbg_p3p, base_vha, 0xb063, + "Failed to acquire IDC lock. retrying...\n"); + + /* Retry/Perform IDC-Lock recovery */ + if (qla83xx_idc_lock_recovery(base_vha) + == QLA_SUCCESS) { + qla83xx_wait_logic(); + goto retry_lock; + } else + ql_log(ql_log_warn, base_vha, 0xb075, + "IDC Lock recovery FAILED.\n"); + } + + } + + return; + + /* XXX: IDC-lock implementation using access-control mbx */ +retry_lock2: + if (qla83xx_access_control(base_vha, options, 0, 0, NULL)) { + ql_dbg(ql_dbg_p3p, base_vha, 0xb072, + "Failed to acquire IDC lock. retrying...\n"); + /* Retry/Perform IDC-Lock recovery */ + if (qla83xx_idc_lock_recovery(base_vha) == QLA_SUCCESS) { + qla83xx_wait_logic(); + goto retry_lock2; + } else + ql_log(ql_log_warn, base_vha, 0xb076, + "IDC Lock recovery FAILED.\n"); + } + + return; +} + +void +qla83xx_idc_unlock(scsi_qla_host_t *base_vha, uint16_t requester_id) +{ + uint16_t options = (requester_id << 15) | BIT_7, retry; + uint32_t data; + struct qla_hw_data *ha = base_vha->hw; + + /* IDC-unlock implementation using driver-unlock/lock-id + * remote registers + */ + retry = 0; +retry_unlock: + if (qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_LOCKID, &data) + == QLA_SUCCESS) { + if (data == ha->portnum) { + qla83xx_rd_reg(base_vha, QLA83XX_DRIVER_UNLOCK, &data); + /* Clearing lock-id by setting 0xff */ + qla83xx_wr_reg(base_vha, QLA83XX_DRIVER_LOCKID, 0xff); + } else if (retry < 10) { + /* SV: XXX: IDC unlock retrying needed here? */ + + /* Retry for IDC-unlock */ + qla83xx_wait_logic(); + retry++; + ql_dbg(ql_dbg_p3p, base_vha, 0xb064, + "Failed to release IDC lock, retyring=%d\n", retry); + goto retry_unlock; + } + } else if (retry < 10) { + /* Retry for IDC-unlock */ + qla83xx_wait_logic(); + retry++; + ql_dbg(ql_dbg_p3p, base_vha, 0xb065, + "Failed to read drv-lockid, retyring=%d\n", retry); + goto retry_unlock; + } + + return; + + /* XXX: IDC-unlock implementation using access-control mbx */ + retry = 0; +retry_unlock2: + if (qla83xx_access_control(base_vha, options, 0, 0, NULL)) { + if (retry < 10) { + /* Retry for IDC-unlock */ + qla83xx_wait_logic(); + retry++; + ql_dbg(ql_dbg_p3p, base_vha, 0xb066, + "Failed to release IDC lock, retyring=%d\n", retry); + goto retry_unlock2; + } + } + + return; +} + +int +__qla83xx_set_drv_presence(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + uint32_t drv_presence; + + rval = qla83xx_rd_reg(vha, QLA83XX_IDC_DRV_PRESENCE, &drv_presence); + if (rval == QLA_SUCCESS) { + drv_presence |= (1 << ha->portnum); + rval = qla83xx_wr_reg(vha, QLA83XX_IDC_DRV_PRESENCE, + drv_presence); + } + + return rval; +} + +int +qla83xx_set_drv_presence(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + + qla83xx_idc_lock(vha, 0); + rval = __qla83xx_set_drv_presence(vha); + qla83xx_idc_unlock(vha, 0); + + return rval; +} + +int +__qla83xx_clear_drv_presence(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + struct qla_hw_data *ha = vha->hw; + uint32_t drv_presence; + + rval = qla83xx_rd_reg(vha, QLA83XX_IDC_DRV_PRESENCE, &drv_presence); + if (rval == QLA_SUCCESS) { + drv_presence &= ~(1 << ha->portnum); + rval = qla83xx_wr_reg(vha, QLA83XX_IDC_DRV_PRESENCE, + drv_presence); + } + + return rval; +} + +int +qla83xx_clear_drv_presence(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + + qla83xx_idc_lock(vha, 0); + rval = __qla83xx_clear_drv_presence(vha); + qla83xx_idc_unlock(vha, 0); + + return rval; +} + +void +qla83xx_need_reset_handler(scsi_qla_host_t *vha) +{ + struct qla_hw_data *ha = vha->hw; + uint32_t drv_ack, drv_presence; + unsigned long ack_timeout; + + /* Wait for IDC ACK from all functions (DRV-ACK == DRV-PRESENCE) */ + ack_timeout = jiffies + (ha->fcoe_reset_timeout * HZ); + while (1) { + qla83xx_rd_reg(vha, QLA83XX_IDC_DRIVER_ACK, &drv_ack); + qla83xx_rd_reg(vha, QLA83XX_IDC_DRV_PRESENCE, &drv_presence); + if (drv_ack == drv_presence) + break; + + if (time_after_eq(jiffies, ack_timeout)) { + ql_log(ql_log_warn, vha, 0xb067, + "RESET ACK TIMEOUT! drv_presence=0x%x " + "drv_ack=0x%x\n", drv_presence, drv_ack); + /* + * The function(s) which did not ack in time are forced + * to withdraw any further participation in the IDC + * reset. + */ + if (drv_ack != drv_presence) + qla83xx_wr_reg(vha, QLA83XX_IDC_DRV_PRESENCE, + drv_ack); + break; + } + + qla83xx_idc_unlock(vha, 0); + msleep(1000); + qla83xx_idc_lock(vha, 0); + } + + qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, QLA8XXX_DEV_COLD); + ql_log(ql_log_info, vha, 0xb068, "HW State: COLD/RE-INIT.\n"); +} + +int +qla83xx_device_bootstrap(scsi_qla_host_t *vha) +{ + int rval = QLA_SUCCESS; + uint32_t idc_control; + + qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, QLA8XXX_DEV_INITIALIZING); + ql_log(ql_log_info, vha, 0xb069, "HW State: INITIALIZING.\n"); + + /* Clearing IDC-Control Graceful-Reset Bit before resetting f/w */ + __qla83xx_get_idc_control(vha, &idc_control); + idc_control &= ~QLA83XX_IDC_GRACEFUL_RESET; + __qla83xx_set_idc_control(vha, 0); + + qla83xx_idc_unlock(vha, 0); + rval = qla83xx_restart_nic_firmware(vha); + qla83xx_idc_lock(vha, 0); + + if (rval != QLA_SUCCESS) { + ql_log(ql_log_fatal, vha, 0xb06a, + "Failed to restart NIC f/w.\n"); + qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, QLA8XXX_DEV_FAILED); + ql_log(ql_log_info, vha, 0xb06b, "HW State: FAILED.\n"); + } else { + ql_dbg(ql_dbg_p3p, vha, 0xb06c, + "Success in restarting nic f/w.\n"); + qla83xx_wr_reg(vha, QLA83XX_IDC_DEV_STATE, QLA8XXX_DEV_READY); + ql_log(ql_log_info, vha, 0xb06d, "HW State: READY.\n"); + } + + return rval; +} + +/* Assumes idc_lock always held on entry */ +int +qla83xx_idc_state_handler(scsi_qla_host_t *base_vha) +{ + struct qla_hw_data *ha = base_vha->hw; + int rval = QLA_SUCCESS; + unsigned long dev_init_timeout; + uint32_t dev_state; + + /* Wait for MAX-INIT-TIMEOUT for the device to go ready */ + dev_init_timeout = jiffies + (ha->fcoe_dev_init_timeout * HZ); + + while (1) { + + if (time_after_eq(jiffies, dev_init_timeout)) { + ql_log(ql_log_warn, base_vha, 0xb06e, + "Initialization TIMEOUT!\n"); + /* Init timeout. Disable further NIC Core + * communication. + */ + qla83xx_wr_reg(base_vha, QLA83XX_IDC_DEV_STATE, + QLA8XXX_DEV_FAILED); + ql_log(ql_log_info, base_vha, 0xb06f, + "HW State: FAILED.\n"); + } + + qla83xx_rd_reg(base_vha, QLA83XX_IDC_DEV_STATE, &dev_state); + switch (dev_state) { + case QLA8XXX_DEV_READY: + if (ha->flags.nic_core_reset_owner) + qla83xx_idc_audit(base_vha, + IDC_AUDIT_COMPLETION); + ha->flags.nic_core_reset_owner = 0; + ql_dbg(ql_dbg_p3p, base_vha, 0xb070, + "Reset_owner reset by 0x%x.\n", + ha->portnum); + goto exit; + case QLA8XXX_DEV_COLD: + if (ha->flags.nic_core_reset_owner) + rval = qla83xx_device_bootstrap(base_vha); + else { + /* Wait for AEN to change device-state */ + qla83xx_idc_unlock(base_vha, 0); + msleep(1000); + qla83xx_idc_lock(base_vha, 0); + } + break; + case QLA8XXX_DEV_INITIALIZING: + /* Wait for AEN to change device-state */ + qla83xx_idc_unlock(base_vha, 0); + msleep(1000); + qla83xx_idc_lock(base_vha, 0); + break; + case QLA8XXX_DEV_NEED_RESET: + if (!ql2xdontresethba && ha->flags.nic_core_reset_owner) + qla83xx_need_reset_handler(base_vha); + else { + /* Wait for AEN to change device-state */ + qla83xx_idc_unlock(base_vha, 0); + msleep(1000); + qla83xx_idc_lock(base_vha, 0); + } + /* reset timeout value after need reset handler */ + dev_init_timeout = jiffies + + (ha->fcoe_dev_init_timeout * HZ); + break; + case QLA8XXX_DEV_NEED_QUIESCENT: + /* XXX: DEBUG for now */ + qla83xx_idc_unlock(base_vha, 0); + msleep(1000); + qla83xx_idc_lock(base_vha, 0); + break; + case QLA8XXX_DEV_QUIESCENT: + /* XXX: DEBUG for now */ + if (ha->flags.quiesce_owner) + goto exit; + + qla83xx_idc_unlock(base_vha, 0); + msleep(1000); + qla83xx_idc_lock(base_vha, 0); + dev_init_timeout = jiffies + + (ha->fcoe_dev_init_timeout * HZ); + break; + case QLA8XXX_DEV_FAILED: + if (ha->flags.nic_core_reset_owner) + qla83xx_idc_audit(base_vha, + IDC_AUDIT_COMPLETION); + ha->flags.nic_core_reset_owner = 0; + __qla83xx_clear_drv_presence(base_vha); + qla83xx_idc_unlock(base_vha, 0); + qla8xxx_dev_failed_handler(base_vha); + rval = QLA_FUNCTION_FAILED; + qla83xx_idc_lock(base_vha, 0); + goto exit; + case QLA8XXX_BAD_VALUE: + qla83xx_idc_unlock(base_vha, 0); + msleep(1000); + qla83xx_idc_lock(base_vha, 0); + break; + default: + ql_log(ql_log_warn, base_vha, 0xb071, + "Unknow Device State: %x.\n", dev_state); + qla83xx_idc_unlock(base_vha, 0); + qla8xxx_dev_failed_handler(base_vha); + rval = QLA_FUNCTION_FAILED; + qla83xx_idc_lock(base_vha, 0); + goto exit; + } + } + +exit: + return rval; +} + /************************************************************************** * qla2x00_do_dpc * This kernel thread is a task that is schedule by the interrupt handler @@ -3749,7 +4453,7 @@ qla2x00_do_dpc(void *data) &base_vha->dpc_flags)) { qla82xx_idc_lock(ha); qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, - QLA82XX_DEV_FAILED); + QLA8XXX_DEV_FAILED); qla82xx_idc_unlock(ha); ql_log(ql_log_info, base_vha, 0x4004, "HW State: FAILED.\n"); @@ -3819,14 +4523,21 @@ 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"); - qla82xx_device_state_handler(base_vha); - clear_bit(ISP_QUIESCE_NEEDED, &base_vha->dpc_flags); - if (!ha->flags.quiesce_owner) { - qla2x00_perform_loop_resync(base_vha); - - qla82xx_idc_lock(ha); - qla82xx_clear_qsnt_ready(base_vha); - qla82xx_idc_unlock(ha); + if (IS_QLA82XX(ha)) { + qla82xx_device_state_handler(base_vha); + clear_bit(ISP_QUIESCE_NEEDED, + &base_vha->dpc_flags); + if (!ha->flags.quiesce_owner) { + qla2x00_perform_loop_resync(base_vha); + + qla82xx_idc_lock(ha); + qla82xx_clear_qsnt_ready(base_vha); + qla82xx_idc_unlock(ha); + } + } else { + clear_bit(ISP_QUIESCE_NEEDED, + &base_vha->dpc_flags); + qla2x00_quiesce_io(base_vha); } ql_dbg(ql_dbg_dpc, base_vha, 0x400a, "Quiescence mode end.\n"); @@ -4326,7 +5037,7 @@ uint32_t qla82xx_error_recovery(scsi_qla_host_t *base_vha) qla82xx_idc_lock(ha); qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, - QLA82XX_DEV_INITIALIZING); + QLA8XXX_DEV_INITIALIZING); qla82xx_wr_32(ha, QLA82XX_CRB_DRV_IDC_VERSION, QLA82XX_IDC_VERSION); @@ -4350,12 +5061,12 @@ uint32_t qla82xx_error_recovery(scsi_qla_host_t *base_vha) "HW State: FAILED.\n"); qla82xx_clear_drv_active(ha); qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, - QLA82XX_DEV_FAILED); + QLA8XXX_DEV_FAILED); } else { ql_log(ql_log_info, base_vha, 0x900c, "HW State: READY.\n"); qla82xx_wr_32(ha, QLA82XX_CRB_DEV_STATE, - QLA82XX_DEV_READY); + QLA8XXX_DEV_READY); qla82xx_idc_unlock(ha); ha->flags.isp82xx_fw_hung = 0; rval = qla82xx_restart_isp(base_vha); @@ -4370,7 +5081,7 @@ uint32_t qla82xx_error_recovery(scsi_qla_host_t *base_vha) "This devfn is not reset owner = 0x%x.\n", ha->pdev->devfn); if ((qla82xx_rd_32(ha, QLA82XX_CRB_DEV_STATE) == - QLA82XX_DEV_READY)) { + QLA8XXX_DEV_READY)) { ha->flags.isp82xx_fw_hung = 0; rval = qla82xx_restart_isp(base_vha); qla82xx_idc_lock(ha); @@ -4495,6 +5206,7 @@ static struct pci_device_id qla2xxx_pci_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP2031) }, { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8001) }, { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8021) }, + { PCI_DEVICE(PCI_VENDOR_ID_QLOGIC, PCI_DEVICE_ID_QLOGIC_ISP8031) }, { 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 d70f03008981..892a81e457bc 100644 --- a/drivers/scsi/qla2xxx/qla_settings.h +++ b/drivers/scsi/qla2xxx/qla_settings.h @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2012 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 a683e766d1ae..32fdc2a66dd1 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -1,6 +1,6 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2012 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ @@ -966,16 +966,16 @@ qla2xxx_get_idc_param(scsi_qla_host_t *vha) QLA82XX_IDC_PARAM_ADDR , 8); if (*wptr == __constant_cpu_to_le32(0xffffffff)) { - ha->nx_dev_init_timeout = QLA82XX_ROM_DEV_INIT_TIMEOUT; - ha->nx_reset_timeout = QLA82XX_ROM_DRV_RESET_ACK_TIMEOUT; + ha->fcoe_dev_init_timeout = QLA82XX_ROM_DEV_INIT_TIMEOUT; + ha->fcoe_reset_timeout = QLA82XX_ROM_DRV_RESET_ACK_TIMEOUT; } else { - ha->nx_dev_init_timeout = le32_to_cpu(*wptr++); - ha->nx_reset_timeout = le32_to_cpu(*wptr); + ha->fcoe_dev_init_timeout = le32_to_cpu(*wptr++); + ha->fcoe_reset_timeout = le32_to_cpu(*wptr); } ql_dbg(ql_dbg_init, vha, 0x004e, - "nx_dev_init_timeout=%d " - "nx_reset_timeout=%d.\n", ha->nx_dev_init_timeout, - ha->nx_reset_timeout); + "fcoe_dev_init_timeout=%d " + "fcoe_reset_timeout=%d.\n", ha->fcoe_dev_init_timeout, + ha->fcoe_reset_timeout); return; } @@ -1017,7 +1017,7 @@ qla2xxx_flash_npiv_conf(scsi_qla_host_t *vha) !IS_CNA_CAPABLE(ha) && !IS_QLA2031(ha)) return; - if (ha->flags.isp82xx_reset_hdlr_active) + if (ha->flags.nic_core_reset_hdlr_active) return; ha->isp_ops->read_optrom(vha, (uint8_t *)&hdr, @@ -1662,6 +1662,23 @@ qla24xx_beacon_blink(struct scsi_qla_host *vha) spin_unlock_irqrestore(&ha->hardware_lock, flags); } +static uint32_t +qla83xx_select_led_port(struct qla_hw_data *ha) +{ + uint32_t led_select_value = 0; + + if (!IS_QLA83XX(ha)) + goto out; + + if (ha->flags.port0) + led_select_value = QLA83XX_LED_PORT0; + else + led_select_value = QLA83XX_LED_PORT1; + +out: + return led_select_value; +} + void qla83xx_beacon_blink(struct scsi_qla_host *vha) { @@ -1669,22 +1686,34 @@ qla83xx_beacon_blink(struct scsi_qla_host *vha) struct qla_hw_data *ha = vha->hw; uint16_t led_cfg[6]; uint16_t orig_led_cfg[6]; + uint32_t led_10_value, led_43_value; if (!IS_QLA83XX(ha) && !IS_QLA81XX(ha)) return; - if (IS_QLA2031(ha) && ha->beacon_blink_led) { - if (ha->flags.port0) - led_select_value = 0x00201320; - else - led_select_value = 0x00201328; + if (!ha->beacon_blink_led) + return; + + if (IS_QLA2031(ha)) { + led_select_value = qla83xx_select_led_port(ha); - qla83xx_write_remote_reg(vha, led_select_value, 0x40002000); - qla83xx_write_remote_reg(vha, led_select_value + 4, 0x40002000); + 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); + } else if (IS_QLA8031(ha)) { + led_select_value = qla83xx_select_led_port(ha); + + qla83xx_rd_reg(vha, led_select_value, &led_10_value); + qla83xx_rd_reg(vha, led_select_value + 0x10, &led_43_value); + qla83xx_wr_reg(vha, led_select_value, 0x01f44000); + msleep(500); + qla83xx_wr_reg(vha, led_select_value, 0x400001f4); msleep(1000); - qla83xx_write_remote_reg(vha, led_select_value, 0x40004000); - qla83xx_write_remote_reg(vha, led_select_value + 4, 0x40004000); - } else if ((IS_QLA8031(ha) || IS_QLA81XX(ha)) && ha->beacon_blink_led) { + qla83xx_wr_reg(vha, led_select_value, led_10_value); + qla83xx_wr_reg(vha, led_select_value + 0x10, led_43_value); + } else if (IS_QLA81XX(ha)) { int rval; /* Save Current */ diff --git a/drivers/scsi/qla2xxx/qla_version.h b/drivers/scsi/qla2xxx/qla_version.h index f5fdb16bec9b..cfe934e1af42 100644 --- a/drivers/scsi/qla2xxx/qla_version.h +++ b/drivers/scsi/qla2xxx/qla_version.h @@ -1,15 +1,15 @@ /* * QLogic Fibre Channel HBA Driver - * Copyright (c) 2003-2011 QLogic Corporation + * Copyright (c) 2003-2012 QLogic Corporation * * See LICENSE.qla2xxx for copyright and licensing details. */ /* * Driver version */ -#define QLA2XXX_VERSION "8.04.00.03-k" +#define QLA2XXX_VERSION "8.04.00.07-k" #define QLA_DRIVER_MAJOR_VER 8 #define QLA_DRIVER_MINOR_VER 4 #define QLA_DRIVER_PATCH_VER 0 -#define QLA_DRIVER_BETA_VER 3 +#define QLA_DRIVER_BETA_VER 0 |