From: Marcus Barrow <mbarrow@redhat.com> Date: Thu, 9 Apr 2009 14:07:19 -0400 Subject: [scsi] qla2xxx : updates and fixes from upstream, part 2 Message-id: 20090409180719.3930.80533.sendpatchset@file.bos.redhat.com O-Subject: [rhel 5.4 patch] qla2xxx : updates and fixes from upstream, part 2 Bugzilla: 495092 RH-Acked-by: Pete Zaitcev <zaitcev@redhat.com> RH-Acked-by: Tomas Henzl <thenzl@redhat.com> BZ 495092 - qla2xxx - updates and fixes from upstream, part 2 This group of patches if the first part of update for rhel 5.4 found during QLogic and partner testing. These patches apply and build cleanly to 2.6.18-137. These changes, where appropriate have been pushed upstream or are queued for upstream. They have been tested by QLogic and partners. - Added abort_target for 81xx - Firmware load sequence for 81xx - Do not restrict flash read to 4K alignment - Extend address range of option-rom update for recent ISPs - Add FW reset attribute in sysfs. - Ignore terminate rport io & tmo callback in reset. - Change reset_fw to reset. - Serialize mbx cmds during reset also. - Use correct mode for reset attribute. - Use byte-address while reading FC boot code versions from flash - Correct qla_flt_location structure - Update drivers FW version for ISP81xx - For ISP81XX do not cache VPD - Always update FW versions and NPIV support in reset path. - Do not check for ISP81XX FW in qla2x00_request_fw. - Remove unused reqest_firmware and release_fw calls. - Remove compiler warnings for ull and size_t - Add EDC-update support diff --git a/drivers/scsi/qla2xxx/Makefile b/drivers/scsi/qla2xxx/Makefile index 7578c39..bcc2dfc 100644 --- a/drivers/scsi/qla2xxx/Makefile +++ b/drivers/scsi/qla2xxx/Makefile @@ -1,6 +1,6 @@ EXTRA_CFLAGS += -DNETLINK_FCTRANSPORT=20 qla2xxx-y := qla_os.o qla_init.o qla_mbx.o qla_iocb.o qla_isr.o qla_gs.o \ qla_dbg.o qla_sup.o qla_attr.o qla_mid.o qla_nlnk.o ql2100_fw.o \ - ql2200_fw.o ql2300_fw.o ql2322_fw.o ql2400_fw.o ql2500_fw.o ql8100_fw.o + ql2200_fw.o ql2300_fw.o ql2322_fw.o ql2400_fw.o ql2500_fw.o obj-$(CONFIG_SCSI_QLA_FC) += qla2xxx.o diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 31fb491..28df87c 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -85,6 +85,71 @@ static struct bin_attribute sysfs_fw_dump_attr = { .write = qla2x00_sysfs_write_fw_dump, }; +#define RESET_FC_FW 0x01 +#define RESET_MPI 0x02 + +static ssize_t +qla2x00_sysfs_write_reset(struct kobject *kobj, char *buf, loff_t off, + size_t count) +{ + struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj, + struct device, kobj))); + scsi_qla_host_t *pha = to_qla_parent(ha); + int reset_type; + + reset_type = simple_strtol(buf, NULL, 10); + switch (reset_type) { + case RESET_FC_FW: + qla_printk(KERN_INFO, ha, + "Issuing ISP abort on host%ld.\n", ha->host_no); + if (ha != pha) { + if (qla2x00_vp_abort_isp(ha)) { + DEBUG2(qla_printk(KERN_INFO, ha, "VPort reset failed\n")); + } + } else { + + scsi_block_requests(ha->host); + set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + qla2xxx_wake_dpc(ha); + qla2x00_wait_for_chip_reset(ha); + scsi_unblock_requests(ha->host); + } + break; + case RESET_MPI: + if(!IS_QLA81XX(ha) || ha != pha) { + qla_printk(KERN_INFO, ha, + "MPI reset not supported for host%ld.\n", ha->host_no); + return (count); + } + qla_printk(KERN_INFO, ha, + "Issuing MPI reset on host%ld.\n", ha->host_no); + + /* Make sure FC side is not in reset */ + qla2x00_wait_for_hba_online(ha); + /* Issue MPI reset */ + scsi_block_requests(ha->host); + + if (qla81xx_restart_mpi_firmware(ha) != QLA_SUCCESS) { + qla_printk(KERN_INFO, ha, + "MPI reset failed for host%ld.\n", ha->host_no); + } + scsi_unblock_requests(ha->host); + break; + } + return (count); +} + +static struct bin_attribute sysfs_fw_reset_attr = { + .attr = { + .name = "reset", + .mode = S_IWUSR, + .owner = THIS_MODULE, + }, + .size = 0, + .write = qla2x00_sysfs_write_reset, +}; + + static ssize_t qla2x00_sysfs_read_nvram(struct kobject *kobj, char *buf, loff_t off, size_t count) @@ -257,12 +322,6 @@ qla2x00_sysfs_write_optrom_ctl(struct kobject *kobj, char *buf, loff_t off, if (ha->optrom_state != QLA_SWAITING) break; - if (start & 0xfff) { - qla_printk(KERN_WARNING, ha, - "Invalid start region 0x%x/0x%x.\n", start, size); - return -EINVAL; - } - ha->optrom_region_start = start; ha->optrom_region_size = start + size > ha->optrom_size ? ha->optrom_size - start : size; @@ -304,11 +363,6 @@ qla2x00_sysfs_write_optrom_ctl(struct kobject *kobj, char *buf, loff_t off, * 0x000000 -> 0x07ffff -- Boot code. * 0x080000 -> 0x0fffff -- Firmware. * - * ISP25xx type boards: - * - * 0x000000 -> 0x07ffff -- Boot code. - * 0x080000 -> 0x0fffff -- Firmware. - * 0x120000 -> 0x12ffff -- VPD and HBA parameters. */ valid = 0; if (ha->optrom_size == OPTROM_SIZE_2300 && start == 0) @@ -316,8 +370,7 @@ qla2x00_sysfs_write_optrom_ctl(struct kobject *kobj, char *buf, loff_t off, else if (start == (ha->flt_region_boot * 4) || start == (ha->flt_region_fw * 4)) valid = 1; - else if ((IS_QLA25XX(ha) || IS_QLA81XX(ha)) && - start == (ha->flt_region_vpd_nvram * 4)) + else if ((IS_QLA25XX(ha) || IS_QLA81XX(ha))) valid = 1; if (!valid) { qla_printk(KERN_WARNING, ha, @@ -395,7 +448,10 @@ qla2x00_sysfs_read_vpd(struct kobject *kobj, char *buf, loff_t off, count = size; } - /* Read NVRAM data from cache. */ + if (IS_NOCACHE_VPD_TYPE(ha)) + ha->isp_ops->read_nvram(ha, (uint8_t *)ha->vpd, ha->vpd_base, + count); + memcpy(buf, &vpd_cache[off], count); return count; @@ -548,7 +604,7 @@ qla2x00_sysfs_write_els(struct kobject *kobj, char *buf, loff_t off, if (count < sizeof(request->ct_iu)) { DEBUG2(qla_printk(KERN_WARNING, ha, - "Passthru ELS buffer insufficient size %ld...\n", count)); + "Passthru ELS buffer insufficient size %zu...\n", count)); goto els_error0; } @@ -584,7 +640,7 @@ qla2x00_sysfs_write_els(struct kobject *kobj, char *buf, loff_t off, if (count > PAGE_SIZE) { DEBUG2(qla_printk(KERN_INFO, ha, - "Passthru ELS request excessive size %ld...\n", count)); + "Passthru ELS request excessive size %zu...\n", count)); count = PAGE_SIZE; } @@ -679,7 +735,7 @@ qla2x00_sysfs_write_ct(struct kobject *kobj, char *buf, loff_t off, if (count < sizeof(request->ct_iu)) { DEBUG2(qla_printk(KERN_WARNING, ha, - "Passthru CT buffer insufficient size %ld...\n", count)); + "Passthru CT buffer insufficient size %zu...\n", count)); goto ct_error0; } @@ -802,6 +858,138 @@ static struct bin_attribute sysfs_ct_attr = { .write = qla2x00_sysfs_write_ct, }; +static ssize_t +qla2x00_sysfs_write_edc(struct kobject *kobj, char *buf, loff_t off, + size_t count) +{ + struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj, + struct device, kobj))); + uint16_t dev, adr, opt, len; + int rval; + + ha->edc_data_len = 0; + + if (!capable(CAP_SYS_ADMIN) || off != 0 || count < 8) + return 0; + + if (!ha->edc_data) { + ha->edc_data = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, + &ha->edc_data_dma); + if (!ha->edc_data) { + DEBUG2(qla_printk(KERN_INFO, ha, + "Unable to allocate memory for EDC write.\n")); + return 0; + } + } + + dev = le16_to_cpup((void *)&buf[0]); + adr = le16_to_cpup((void *)&buf[2]); + opt = le16_to_cpup((void *)&buf[4]); + len = le16_to_cpup((void *)&buf[6]); + + if (!(opt & BIT_0)) + if (len == 0 || len > DMA_POOL_SIZE || len > count - 8) + return -EINVAL; + + memcpy(ha->edc_data, &buf[8], len); + + rval = qla2x00_write_edc(ha, dev, adr, ha->edc_data_dma, + ha->edc_data, len, opt); + if (rval != QLA_SUCCESS) { + DEBUG2(qla_printk(KERN_INFO, ha, + "Unable to write EDC (%x) %02x:%02x:%04x:%02x:%02x.\n", + rval, dev, adr, opt, len, *buf)); + return 0; + } + + return count; +} + +static struct bin_attribute sysfs_edc_attr = { + .attr = { + .name = "edc", + .mode = S_IWUSR, + }, + .size = 0, + .write = qla2x00_sysfs_write_edc, +}; + +static ssize_t +qla2x00_sysfs_write_edc_status(struct kobject *kobj, char *buf, loff_t off, + size_t count) +{ + struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj, + struct device, kobj))); + uint16_t dev, adr, opt, len; + int rval; + + ha->edc_data_len = 0; + + if (!capable(CAP_SYS_ADMIN) || off != 0 || count < 8) + return 0; + + if (!ha->edc_data) { + ha->edc_data = dma_pool_alloc(ha->s_dma_pool, GFP_KERNEL, + &ha->edc_data_dma); + if (!ha->edc_data) { + DEBUG2(qla_printk(KERN_INFO, ha, + "Unable to allocate memory for EDC status.\n")); + return 0; + } + } + + dev = le16_to_cpup((void *)&buf[0]); + adr = le16_to_cpup((void *)&buf[2]); + opt = le16_to_cpup((void *)&buf[4]); + len = le16_to_cpup((void *)&buf[6]); + + if (!(opt & BIT_0)) + if (len == 0 || len > DMA_POOL_SIZE) + return -EINVAL; + + memset(ha->edc_data, 0, len); + rval = qla2x00_read_edc(ha, dev, adr, ha->edc_data_dma, + ha->edc_data, len, opt); + if (rval != QLA_SUCCESS) { + DEBUG2(qla_printk(KERN_INFO, ha, + "Unable to write EDC status (%x) %02x:%02x:%04x:%02x.\n", + rval, dev, adr, opt, len)); + return 0; + } + + ha->edc_data_len = len; + + return count; +} + +static ssize_t +qla2x00_sysfs_read_edc_status(struct kobject *kobj, char *buf, loff_t off, + size_t count) +{ + struct scsi_qla_host *ha = to_qla_host(dev_to_shost(container_of(kobj, + struct device, kobj))); + + if (!capable(CAP_SYS_ADMIN) || off != 0 || count == 0) + return 0; + + if (!ha->edc_data || ha->edc_data_len == 0 || ha->edc_data_len > count) + return -EINVAL; + + memcpy(buf, ha->edc_data, ha->edc_data_len); + + return ha->edc_data_len; +} + +static struct bin_attribute sysfs_edc_status_attr = { + .attr = { + .name = "edc_status", + .mode = S_IRUSR | S_IWUSR, + }, + .size = 0, + .write = qla2x00_sysfs_write_edc_status, + .read = qla2x00_sysfs_read_edc_status, +}; + static struct sysfs_entry { char *name; struct bin_attribute *attr; @@ -815,6 +1003,9 @@ static struct sysfs_entry { { "sfp", &sysfs_sfp_attr, 1 }, { "els", &sysfs_els_attr, 1 }, { "ct", &sysfs_ct_attr, }, + { "reset", &sysfs_fw_reset_attr, }, + { "edc", &sysfs_edc_attr, 2 }, + { "edc_status", &sysfs_edc_status_attr, 2 }, { NULL }, }; @@ -828,6 +1019,8 @@ qla2x00_alloc_sysfs_attr(scsi_qla_host_t *ha) for (iter = bin_file_entries; iter->name; iter++) { if (iter->is4GBp_only && !IS_FWI2_CAPABLE(ha)) continue; + if (iter->is4GBp_only == 2 && !IS_QLA25XX(ha)) + continue; ret = sysfs_create_bin_file(&host->shost_gendev.kobj, iter->attr); @@ -847,6 +1040,8 @@ qla2x00_free_sysfs_attr(scsi_qla_host_t *ha) for (iter = bin_file_entries; iter->name; iter++) { if (iter->is4GBp_only && !IS_FWI2_CAPABLE(ha)) continue; + if (iter->is4GBp_only == 2 && !IS_QLA25XX(ha)) + continue; sysfs_remove_bin_file(&host->shost_gendev.kobj, iter->attr); @@ -1769,11 +1964,18 @@ qla2x00_set_rport_loss_tmo(struct fc_rport *rport, uint32_t timeout) static void qla2x00_dev_loss_tmo_callbk(struct fc_rport *rport) { + scsi_qla_host_t *ha = NULL; struct Scsi_Host *host = rport_to_shost(rport); fc_port_t *fcport = *(fc_port_t **)rport->dd_data; if (!fcport) return; + + ha = to_qla_parent(fcport->ha); + if (test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags)) { + return ; + } + /* * At this point all fcport's software-states are cleared. Perform any * final cleanup of firmware resources (PCBs and XCBs). @@ -1800,10 +2002,16 @@ qla2x00_dev_loss_tmo_callbk(struct fc_rport *rport) static void qla2x00_terminate_rport_io(struct fc_rport *rport) { + scsi_qla_host_t *ha = NULL; fc_port_t *fcport = *(fc_port_t **)rport->dd_data; if (!fcport) return; + + ha = to_qla_parent(fcport->ha); + if (test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags)) { + return ; + } /* * At this point all fcport's software-states are cleared. Perform any * final cleanup of firmware resources (PCBs and XCBs). diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 38d5029..32e0038 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -696,6 +696,7 @@ typedef struct { #define MBC_GET_TIMEOUT_PARAMS 0x22 /* Get FW timeouts. */ #define MBC_TRACE_CONTROL 0x27 /* Trace control command. */ #define MBC_GEN_SYSTEM_ERROR 0x2a /* Generate System Error. */ +#define MBC_WRITE_SFP 0x30 /* Write SFP Data. */ #define MBC_READ_SFP 0x31 /* Read SFP Data. */ #define MBC_SET_TIMEOUT_PARAMS 0x32 /* Set FW timeouts. */ #define MBC_MID_INITIALIZE_FIRMWARE 0x48 /* MID Initialize firmware. */ @@ -706,6 +707,7 @@ typedef struct { #define MBC_GET_LINK_PRIV_STATS 0x6d /* Get link & private data. */ #define MBC_SET_VENDOR_ID 0x76 /* Set Vendor ID. */ #define MBC_IDC_ACK 0x101 /* Ack IDC */ +#define MBC_RESTART_MPI_FW 0x03d /* Reset MPI */ #define TC_ENABLE 4 #define TC_DISABLE 5 @@ -2292,6 +2294,7 @@ typedef struct scsi_qla_host { uint32_t npiv_supported :1; uint32_t pci_channel_io_perm_failure :1; uint32_t chip_reset_done :1; + uint32_t running_gold_fw :1; } flags; atomic_t loop_state; @@ -2393,6 +2396,7 @@ typedef struct scsi_qla_host { #define IS_QLA24XX_TYPE(ha) (IS_QLA24XX(ha) || IS_QLA54XX(ha) || \ IS_QLA84XX(ha)) #define IS_QLA81XX(ha) (IS_QLA8001(ha)) +#define IS_NOCACHE_VPD_TYPE(ha) (IS_QLA81XX(ha)) #define IS_IIDMA_CAPABLE(ha) ((ha)->device_type & DT_IIDMA) #define IS_FWI2_CAPABLE(ha) ((ha)->device_type & DT_FWI2) @@ -2527,6 +2531,10 @@ typedef struct scsi_qla_host { void *sfp_data; dma_addr_t sfp_data_dma; + uint8_t *edc_data; + dma_addr_t edc_data_dma; + uint16_t edc_data_len; + struct task_struct *dpc_thread; uint8_t dpc_active; /* DPC routine is active */ @@ -2656,6 +2664,7 @@ typedef struct scsi_qla_host { uint32_t flt_region_fw; uint32_t flt_region_vpd_nvram; uint32_t flt_region_npiv_conf; + uint32_t flt_region_gold_fw; /* Needed for BEACON */ uint16_t beacon_blink_led; diff --git a/drivers/scsi/qla2xxx/qla_fw.h b/drivers/scsi/qla2xxx/qla_fw.h index 309c7b7..16b4bf8 100644 --- a/drivers/scsi/qla2xxx/qla_fw.h +++ b/drivers/scsi/qla2xxx/qla_fw.h @@ -1204,9 +1204,10 @@ struct qla_fdt_layout { struct qla_flt_location { uint8_t sig[4]; - uint32_t start_lo; - uint32_t start_hi; - uint16_t unused; + uint16_t start_lo; + uint16_t start_hi; + uint8_t version; + uint8_t unused[5]; uint16_t checksum; }; @@ -1229,6 +1230,7 @@ struct qla_flt_header { #define FLT_REG_HW_EVENT_1 0x1f #define FLT_REG_NPIV_CONF_0 0x29 #define FLT_REG_NPIV_CONF_1 0x2a +#define FLT_REG_GOLD_FW 0x2f struct qla_flt_region { uint32_t code; diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index 22cee28..00c16ad 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -34,6 +34,7 @@ extern void qla24xx_update_fw_options(scsi_qla_host_t *); extern void qla81xx_update_fw_options(scsi_qla_host_t *); extern int qla2x00_load_risc(struct scsi_qla_host *, uint32_t *); extern int qla24xx_load_risc(scsi_qla_host_t *, uint32_t *); +extern int qla81xx_load_risc(scsi_qla_host_t *, uint32_t *); extern int qla2x00_loop_resync(scsi_qla_host_t *); @@ -55,6 +56,7 @@ extern void qla2x00_try_to_stop_firmware(scsi_qla_host_t *); extern void qla84xx_put_chip(struct scsi_qla_host *); extern int qla2x00_post_idc_ack_work(struct scsi_qla_host *, uint16_t *); +extern int qla81xx_restart_mpi_firmware(scsi_qla_host_t *); /* * Global Data in qla_os.c source file. @@ -258,6 +260,14 @@ extern int qla2x00_read_sfp(scsi_qla_host_t *, dma_addr_t, uint16_t, uint16_t, uint16_t); extern int +qla2x00_read_edc(scsi_qla_host_t *, uint16_t, uint16_t, dma_addr_t, + uint8_t *, uint16_t, uint16_t); + +extern int +qla2x00_write_edc(scsi_qla_host_t *, uint16_t, uint16_t, dma_addr_t, + uint8_t *, uint16_t, uint16_t); + +extern int qla2x00_set_idma_speed(scsi_qla_host_t *, uint16_t, uint16_t, uint16_t *); extern int qla84xx_verify_chip(struct scsi_qla_host *, uint16_t *); diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index 404d31e..e1be63b 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -935,6 +935,7 @@ static int qla2x00_setup_chip(scsi_qla_host_t *ha) { int rval; + uint16_t fw_major_version; uint32_t srisc_address = 0; /* Load firmware sequences */ @@ -951,14 +952,16 @@ qla2x00_setup_chip(scsi_qla_host_t *ha) rval = qla2x00_execute_fw(ha, srisc_address); /* Retrieve firmware information. */ - if (rval == QLA_SUCCESS && ha->fw_major_version == 0) { + if (rval == QLA_SUCCESS) { + fw_major_version = ha->fw_major_version; + qla2x00_get_fw_version(ha, &ha->fw_major_version, &ha->fw_minor_version, &ha->fw_subminor_version, &ha->fw_attributes, &ha->fw_memory_size, ha->mpi_version, &ha->mpi_capabilities); - qla2x00_resize_request_q(ha); + ha->flags.npiv_supported = 0; if ((IS_QLA24XX_TYPE(ha) || IS_QLA25XX(ha) || IS_QLA84XX(ha) || IS_QLA81XX(ha)) && @@ -971,8 +974,12 @@ qla2x00_setup_chip(scsi_qla_host_t *ha) MIN_MULTI_ID_FABRIC - 1; } - if (ql2xallocfwdump) - qla2x00_alloc_fw_dump(ha); + if (fw_major_version == 0) { + qla2x00_resize_request_q(ha); + + if (ql2xallocfwdump) + qla2x00_alloc_fw_dump(ha); + } } } else { DEBUG2(printk(KERN_INFO @@ -3641,11 +3648,11 @@ qla24xx_nvram_config(scsi_qla_host_t *ha) } static int -qla24xx_load_risc_flash(scsi_qla_host_t *ha, uint32_t *srisc_addr) +qla24xx_load_risc_flash(scsi_qla_host_t *ha, uint32_t *srisc_addr, + uint32_t faddr) { int rval; int segments, fragment; - uint32_t faddr; uint32_t *dcode, dlen; uint32_t risc_addr; uint32_t risc_size; @@ -3654,7 +3661,6 @@ qla24xx_load_risc_flash(scsi_qla_host_t *ha, uint32_t *srisc_addr) rval = QLA_SUCCESS; segments = FA_RISC_CODE_SEGMENTS; - faddr = ha->flt_region_fw; dcode = (uint32_t *)ha->request_ring; *srisc_addr = 0; @@ -3825,7 +3831,7 @@ fail_fw_integrity: } int -qla24xx_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr) +qla24xx_load_risc_blob(scsi_qla_host_t *ha, uint32_t *srisc_addr) { int rval; int segments, fragment; @@ -3843,10 +3849,7 @@ qla24xx_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr) qla_printk(KERN_ERR, ha, "Firmware images can be retrieved " "from: " QLA_FW_URL ".\n"); - /* Try to load RISC code from flash. */ - qla_printk(KERN_ERR, ha, "Attempting to load (potentially " - "outdated) firmware from flash.\n"); - return qla24xx_load_risc_flash(ha, srisc_addr); + return QLA_FUNCTION_FAILED; } rval = QLA_SUCCESS; @@ -3933,6 +3936,52 @@ fail_fw_integrity: return QLA_FUNCTION_FAILED; } +int +qla24xx_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr) +{ + int rval; + + /* + * FW Load priority: + * 1) Firmware via request-firmware interface (.bin file with driver). + * 2) Firmware residing in flash. + */ + rval = qla24xx_load_risc_blob(ha, srisc_addr); + if (rval == QLA_SUCCESS) + return rval; + + return qla24xx_load_risc_flash(ha, srisc_addr, ha->flt_region_fw); +} + +int +qla81xx_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr) +{ + int rval; + + /* + * FW Load priority: + * 1) Operational firmware residing in flash. + * 2) Golden firmware residing in flash. + */ + rval = qla24xx_load_risc_flash(ha, srisc_addr, ha->flt_region_fw); + if (rval == QLA_SUCCESS) + return rval; + + qla_printk(KERN_ERR, ha, "Unable to load operational firmware.\n"); + if (ha->flt_region_gold_fw) { + qla_printk(KERN_ERR, ha, "Falling back to golden firmware.\n"); + qla_printk(KERN_ERR, ha, "Please update the operational firmware.\n"); + rval = qla24xx_load_risc_flash(ha, srisc_addr, + ha->flt_region_gold_fw); + if (rval == QLA_SUCCESS) + ha->flags.running_gold_fw = 1; + else + qla_printk(KERN_ERR, ha, "Unable to load golden firmware," + " disabling adapter.\n"); + } + return rval; +} + void qla2x00_try_to_stop_firmware(scsi_qla_host_t *ha) { diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index a007cce..34ee8de 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -57,7 +57,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp) * seconds. This is to serialize actual issuing of mailbox cmds during * non ISP abort time. */ - if (!abort_active && !ha->flags.pci_channel_io_perm_failure) { + if (!ha->flags.pci_channel_io_perm_failure) { if (!wait_for_completion_timeout(&ha->mbx_cmd_comp, mcp->tov * HZ)) { /* Timeout occurred. Return error. */ @@ -262,8 +262,7 @@ qla2x00_mailbox_command(scsi_qla_host_t *pvha, mbx_cmd_t *mcp) } /* Allow next mbx cmd to come in. */ - if (!abort_active) - complete(&ha->mbx_cmd_comp); + complete(&ha->mbx_cmd_comp); if (rval) { DEBUG2_3_11(printk("%s(%ld): **** FAILED. mbx0=%x, mbx1=%x, " @@ -2966,3 +2965,108 @@ qla81xx_idc_ack(scsi_qla_host_t *vha, uint16_t *mb) return rval; } + +int +qla81xx_restart_mpi_firmware(scsi_qla_host_t *ha) +{ + int rval = 0; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + DEBUG11(printk("%s(%ld): entered.\n", __func__, vha->host_no)); + + mcp->mb[0] = MBC_RESTART_MPI_FW; + mcp->out_mb = MBX_0; + mcp->in_mb = MBX_0|MBX_1; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(ha, mcp); + + if (rval != QLA_SUCCESS) { + DEBUG2_3_11(printk("%s(%ld): failed=%x (mb[0]=0x%x mb[1]=0x%x).\n", + __func__, ha->host_no, rval, mcp->mb[0], mcp->mb[1])); + } else { + DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + } + + return rval; +} + + +int +qla2x00_read_edc(scsi_qla_host_t *ha, uint16_t dev, uint16_t adr, + dma_addr_t sfp_dma, uint8_t *sfp, uint16_t len, uint16_t opt) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + + mcp->mb[0] = MBC_READ_SFP; + mcp->mb[1] = dev; + mcp->mb[2] = MSW(sfp_dma); + mcp->mb[3] = LSW(sfp_dma); + mcp->mb[6] = MSW(MSD(sfp_dma)); + mcp->mb[7] = LSW(MSD(sfp_dma)); + mcp->mb[8] = len; + mcp->mb[9] = adr; + mcp->mb[10] = opt; + mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(ha, mcp); + + if (opt & BIT_0) + if (sfp) + *sfp = mcp->mb[8]; + + if (rval != QLA_SUCCESS) { + DEBUG2_3_11(printk("%s(%ld): failed=%x (%x).\n", __func__, + ha->host_no, rval, mcp->mb[0])); + } else { + DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + } + + return rval; +} + +int +qla2x00_write_edc(scsi_qla_host_t *ha, uint16_t dev, uint16_t adr, + dma_addr_t sfp_dma, uint8_t *sfp, uint16_t len, uint16_t opt) +{ + int rval; + mbx_cmd_t mc; + mbx_cmd_t *mcp = &mc; + + DEBUG11(printk("%s(%ld): entered.\n", __func__, ha->host_no)); + + if (opt & BIT_0) + if (sfp) + len = *sfp; + + mcp->mb[0] = MBC_WRITE_SFP; + mcp->mb[1] = dev; + mcp->mb[2] = MSW(sfp_dma); + mcp->mb[3] = LSW(sfp_dma); + mcp->mb[6] = MSW(MSD(sfp_dma)); + mcp->mb[7] = LSW(MSD(sfp_dma)); + mcp->mb[8] = len; + mcp->mb[9] = adr; + mcp->mb[10] = opt; + mcp->out_mb = MBX_10|MBX_9|MBX_8|MBX_7|MBX_6|MBX_3|MBX_2|MBX_1|MBX_0; + mcp->in_mb = MBX_0; + mcp->tov = MBX_TOV_SECONDS; + mcp->flags = 0; + rval = qla2x00_mailbox_command(ha, mcp); + + if (rval != QLA_SUCCESS) { + DEBUG2_3_11(printk("%s(%ld): failed=%x (%x).\n", __func__, + ha->host_no, rval, mcp->mb[0])); + } else { + DEBUG11(printk("%s(%ld): done.\n", __func__, ha->host_no)); + } + + return rval; +} diff --git a/drivers/scsi/qla2xxx/qla_nlnk.h b/drivers/scsi/qla2xxx/qla_nlnk.h index 0a1ea3a..91ff73f 100644 --- a/drivers/scsi/qla2xxx/qla_nlnk.h +++ b/drivers/scsi/qla2xxx/qla_nlnk.h @@ -132,7 +132,7 @@ struct msg_update_fw { struct qla_fc_msg { uint64_t magic; -#define QL_FC_NL_MAGIC 0x107784DDFCAB1FC1 +#define QL_FC_NL_MAGIC 0x107784DDFCAB1FC1ULL uint16_t host_no; uint16_t vmsg_datalen; diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index e006599..c4968a4 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -67,6 +67,12 @@ MODULE_PARM_DESC(ql2xextended_error_logging, "Option to enable extended error logging, " "Default is 0 - no logging. 1 - log errors."); +int ql2xdevdiscgoldfw = 0; +module_param(ql2xdevdiscgoldfw, int, S_IRUGO|S_IRUSR); +MODULE_PARM_DESC(ql2xdevdiscgoldfw, + "Option to enable device discovery with golden firmware " + "Applicable to ISP81XX based CNA only. " + "Default is 0 - no discovery. 1 - discover device."); static void qla2x00_free_device(scsi_qla_host_t *); @@ -1496,13 +1502,14 @@ static struct isp_operations qla81xx_isp_ops = { .reset_adapter = qla24xx_reset_adapter, .nvram_config = qla81xx_nvram_config, .update_fw_options = qla81xx_update_fw_options, - .load_risc = qla24xx_load_risc, + .load_risc = qla81xx_load_risc, .pci_info_str = qla24xx_pci_info_str, .fw_version_str = qla24xx_fw_version_str, .intr_handler = qla24xx_intr_handler, .enable_intrs = qla24xx_enable_intrs, .disable_intrs = qla24xx_disable_intrs, .abort_command = qla24xx_abort_command, + .abort_target = qla24xx_abort_target, .fabric_login = qla24xx_login_fabric, .fabric_logout = qla24xx_fabric_logout, .calc_req_entries = NULL, @@ -1675,6 +1682,8 @@ qla2xxx_scan_start(struct Scsi_Host *shost) { scsi_qla_host_t *ha = (scsi_qla_host_t *)shost->hostdata; + if (ha->flags.running_gold_fw && !ql2xdevdiscgoldfw) + return; set_bit(LOOP_RESYNC_NEEDED, &ha->dpc_flags); set_bit(LOCAL_LOOP_UPDATE, &ha->dpc_flags); set_bit(RSCN_UPDATE, &ha->dpc_flags); @@ -1887,16 +1896,18 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) goto probe_failed; } - /* - * Startup the kernel thread for this host adapter - */ - ha->dpc_thread = kthread_create(qla2x00_do_dpc, ha, - "%s_dpc", ha->host_str); - if (IS_ERR(ha->dpc_thread)) { - qla_printk(KERN_WARNING, ha, - "Unable to start DPC thread!\n"); - ret = PTR_ERR(ha->dpc_thread); - goto probe_failed; + if (!ha->flags.running_gold_fw || ql2xdevdiscgoldfw) { + /* + * Startup the kernel thread for this host adapter + */ + ha->dpc_thread = kthread_create(qla2x00_do_dpc, ha, + "%s_dpc", ha->host_str); + if (IS_ERR(ha->dpc_thread)) { + qla_printk(KERN_WARNING, ha, + "Unable to start DPC thread!\n"); + ret = PTR_ERR(ha->dpc_thread); + goto probe_failed; + } } host->this_id = 255; @@ -2474,6 +2485,9 @@ qla2x00_mem_free(scsi_qla_host_t *ha) if (ha->sfp_data) dma_pool_free(ha->s_dma_pool, ha->sfp_data, ha->sfp_data_dma); + if (ha->edc_data) + dma_pool_free(ha->s_dma_pool, ha->edc_data, ha->edc_data_dma); + if (ha->ms_iocb) dma_pool_free(ha->s_dma_pool, ha->ms_iocb, ha->ms_iocb_dma); @@ -3062,19 +3076,16 @@ qla2x00_timer(scsi_qla_host_t *ha) /* Firmware interface routines. */ -#define FW_BLOBS 7 +#define FW_BLOBS 6 #define FW_ISP21XX 0 #define FW_ISP22XX 1 #define FW_ISP2300 2 #define FW_ISP2322 3 #define FW_ISP24XX 4 #define FW_ISP25XX 5 -#define FW_ISP81XX 6 - -static DECLARE_MUTEX(qla_fw_lock); extern struct firmware ql2100_fw, ql2200_fw, ql2300_fw, ql2322_fw, ql2400_fw, - ql2500_fw, ql8100_fw; + ql2500_fw; static struct fw_blob qla_fw_blobs[FW_BLOBS] = { { .name = "ql2100_fw.bin", .segs = { 0x1000, 0 }, .fw = &ql2100_fw }, @@ -3083,7 +3094,6 @@ static struct fw_blob qla_fw_blobs[FW_BLOBS] = { { .name = "ql2322_fw.bin", .segs = { 0x800, 0x1c000, 0x1e000, 0 }, .fw = &ql2322_fw }, { .name = "ql2400_fw.bin", .fw = &ql2400_fw }, { .name = "ql2500_fw.bin", .fw = &ql2500_fw }, - { .name = "ql8100_fw.bin", .fw = &ql8100_fw }, }; struct fw_blob * @@ -3104,40 +3114,10 @@ qla2x00_request_firmware(scsi_qla_host_t *ha) blob = &qla_fw_blobs[FW_ISP24XX]; } else if (IS_QLA25XX(ha)) { blob = &qla_fw_blobs[FW_ISP25XX]; - } else if (IS_QLA81XX(ha)) { - blob = &qla_fw_blobs[FW_ISP81XX]; - } - - - down(&qla_fw_lock); - if (blob->fw) - goto out; - - if (request_firmware(&blob->fw, blob->name, &ha->pdev->dev)) { - DEBUG2(printk("scsi(%ld): Failed to load firmware image " - "(%s).\n", ha->host_no, blob->name)); - blob->fw = NULL; - blob = NULL; - goto out; } - -out: - up(&qla_fw_lock); return blob; } -static void -qla2x00_release_firmware(void) -{ - int idx; - - down(&qla_fw_lock); - for (idx = 0; idx < FW_BLOBS; idx++) - if (qla_fw_blobs[idx].fw) - release_firmware(qla_fw_blobs[idx].fw); - up(&qla_fw_lock); -} - static pci_ers_result_t qla2xxx_pci_error_detected(struct pci_dev *pdev, pci_channel_state_t state) { diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index fb8f4a7..3a793b3 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -707,6 +707,9 @@ qla2xxx_get_flt_info(scsi_qla_host_t *ha, uint32_t flt_addr) if (PCI_FUNC(ha->pdev->devfn) & 1) ha->flt_region_npiv_conf = start; break; + case FLT_REG_GOLD_FW: + ha->flt_region_gold_fw = start; + break; } } goto done; @@ -2476,7 +2479,7 @@ qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf) dcode = mbuf; /* Begin with first PCI expansion ROM header. */ - pcihdr = ha->flt_region_boot; + pcihdr = ha->flt_region_boot << 2; last_image = 1; do { /* Verify PCI expansion ROM header. */