+
+void
+qla24xx_reset_adapter(scsi_qla_host_t *ha)
+{
+ unsigned long flags = 0;
+ struct device_reg_24xx __iomem *reg = &ha->iobase->isp24;
+
+ ha->flags.online = 0;
+ ha->isp_ops.disable_intrs(ha);
+
+ spin_lock_irqsave(&ha->hardware_lock, flags);
+ WRT_REG_DWORD(®->hccr, HCCRX_SET_RISC_RESET);
+ RD_REG_DWORD(®->hccr);
+ WRT_REG_DWORD(®->hccr, HCCRX_REL_RISC_PAUSE);
+ RD_REG_DWORD(®->hccr);
+ spin_unlock_irqrestore(&ha->hardware_lock, flags);
+}
+
+int
+qla24xx_nvram_config(scsi_qla_host_t *ha)
+{
+ int rval;
+ struct init_cb_24xx *icb;
+ struct nvram_24xx *nv;
+ uint32_t *dptr;
+ uint8_t *dptr1, *dptr2;
+ uint32_t chksum;
+ uint16_t cnt;
+
+ rval = QLA_SUCCESS;
+ icb = (struct init_cb_24xx *)ha->init_cb;
+ nv = (struct nvram_24xx *)ha->request_ring;
+
+ /* Determine NVRAM starting address. */
+ ha->nvram_size = sizeof(struct nvram_24xx);
+ ha->nvram_base = FA_NVRAM_FUNC0_ADDR;
+ ha->vpd_size = FA_NVRAM_VPD_SIZE;
+ ha->vpd_base = FA_NVRAM_VPD0_ADDR;
+ if (PCI_FUNC(ha->pdev->devfn)) {
+ ha->nvram_base = FA_NVRAM_FUNC1_ADDR;
+ ha->vpd_base = FA_NVRAM_VPD1_ADDR;
+ }
+
+ /* Get NVRAM data and calculate checksum. */
+ dptr = (uint32_t *)nv;
+ ha->isp_ops.read_nvram(ha, (uint8_t *)dptr, ha->nvram_base,
+ ha->nvram_size);
+ for (cnt = 0, chksum = 0; cnt < ha->nvram_size >> 2; cnt++)
+ chksum += le32_to_cpu(*dptr++);
+
+ DEBUG5(printk("scsi(%ld): Contents of NVRAM\n", ha->host_no));
+ DEBUG5(qla2x00_dump_buffer((uint8_t *)ha->request_ring,
+ ha->nvram_size));
+
+ /* Bad NVRAM data, set defaults parameters. */
+ if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' || nv->id[2] != 'P'
+ || nv->id[3] != ' ' ||
+ nv->nvram_version < __constant_cpu_to_le16(ICB_VERSION)) {
+ /* Reset NVRAM data. */
+ qla_printk(KERN_WARNING, ha, "Inconsistent NVRAM detected: "
+ "checksum=0x%x id=%c version=0x%x.\n", chksum, nv->id[0],
+ le16_to_cpu(nv->nvram_version));
+ qla_printk(KERN_WARNING, ha, "Falling back to functioning (yet "
+ "invalid -- WWPN) defaults.\n");
+
+ /*
+ * Set default initialization control block.
+ */
+ memset(nv, 0, ha->nvram_size);
+ nv->nvram_version = __constant_cpu_to_le16(ICB_VERSION);
+ nv->version = __constant_cpu_to_le16(ICB_VERSION);
+ nv->frame_payload_size = __constant_cpu_to_le16(2048);
+ nv->execution_throttle = __constant_cpu_to_le16(0xFFFF);
+ nv->exchange_count = __constant_cpu_to_le16(0);
+ nv->hard_address = __constant_cpu_to_le16(124);
+ nv->port_name[0] = 0x21;
+ nv->port_name[1] = 0x00 + PCI_FUNC(ha->pdev->devfn);
+ nv->port_name[2] = 0x00;
+ nv->port_name[3] = 0xe0;
+ nv->port_name[4] = 0x8b;
+ nv->port_name[5] = 0x1c;
+ nv->port_name[6] = 0x55;
+ nv->port_name[7] = 0x86;
+ nv->node_name[0] = 0x20;
+ nv->node_name[1] = 0x00;
+ nv->node_name[2] = 0x00;
+ nv->node_name[3] = 0xe0;
+ nv->node_name[4] = 0x8b;
+ nv->node_name[5] = 0x1c;
+ nv->node_name[6] = 0x55;
+ nv->node_name[7] = 0x86;
+ nv->login_retry_count = __constant_cpu_to_le16(8);
+ nv->interrupt_delay_timer = __constant_cpu_to_le16(0);
+ nv->login_timeout = __constant_cpu_to_le16(0);
+ nv->firmware_options_1 =
+ __constant_cpu_to_le32(BIT_14|BIT_13|BIT_2|BIT_1);
+ nv->firmware_options_2 = __constant_cpu_to_le32(2 << 4);
+ nv->firmware_options_2 |= __constant_cpu_to_le32(BIT_12);
+ nv->firmware_options_3 = __constant_cpu_to_le32(2 << 13);
+ nv->host_p = __constant_cpu_to_le32(BIT_11|BIT_10);
+ nv->efi_parameters = __constant_cpu_to_le32(0);
+ nv->reset_delay = 5;
+ nv->max_luns_per_target = __constant_cpu_to_le16(128);
+ nv->port_down_retry_count = __constant_cpu_to_le16(30);
+ nv->link_down_timeout = __constant_cpu_to_le16(30);
+
+ rval = 1;
+ }
+
+ /* Reset Initialization control block */
+ memset(icb, 0, sizeof(struct init_cb_24xx));
+
+ /* Copy 1st segment. */
+ dptr1 = (uint8_t *)icb;
+ dptr2 = (uint8_t *)&nv->version;
+ cnt = (uint8_t *)&icb->response_q_inpointer - (uint8_t *)&icb->version;
+ while (cnt--)
+ *dptr1++ = *dptr2++;
+
+ icb->login_retry_count = nv->login_retry_count;
+ icb->link_down_on_nos = nv->link_down_on_nos;
+
+ /* Copy 2nd segment. */
+ dptr1 = (uint8_t *)&icb->interrupt_delay_timer;
+ dptr2 = (uint8_t *)&nv->interrupt_delay_timer;
+ cnt = (uint8_t *)&icb->reserved_3 -
+ (uint8_t *)&icb->interrupt_delay_timer;
+ while (cnt--)
+ *dptr1++ = *dptr2++;
+
+ /*
+ * Setup driver NVRAM options.
+ */
+ if (memcmp(nv->model_name, BINZERO, sizeof(nv->model_name)) != 0) {
+ char *st, *en;
+ uint16_t index;
+
+ strncpy(ha->model_number, nv->model_name,
+ sizeof(nv->model_name));
+ st = en = ha->model_number;
+ en += sizeof(nv->model_name) - 1;
+ while (en > st) {
+ if (*en != 0x20 && *en != 0x00)
+ break;
+ *en-- = '\0';
+ }
+
+ index = (ha->pdev->subsystem_device & 0xff);
+ if (index < QLA_MODEL_NAMES)
+ ha->model_desc = qla2x00_model_name[index * 2 + 1];
+ } else
+ strcpy(ha->model_number, "QLA2462");
+
+ /* Use alternate WWN? */
+ if (nv->host_p & __constant_cpu_to_le32(BIT_15)) {
+ memcpy(icb->node_name, nv->alternate_node_name, WWN_SIZE);
+ memcpy(icb->port_name, nv->alternate_port_name, WWN_SIZE);
+ }
+
+ /* Prepare nodename */
+ if ((icb->firmware_options_1 & __constant_cpu_to_le32(BIT_14)) == 0) {
+ /*
+ * Firmware will apply the following mask if the nodename was
+ * not provided.
+ */
+ memcpy(icb->node_name, icb->port_name, WWN_SIZE);
+ icb->node_name[0] &= 0xF0;
+ }
+
+ /* Set host adapter parameters. */
+ ha->flags.disable_risc_code_load = 0;
+ ha->flags.enable_lip_reset = 0;
+ ha->flags.enable_lip_full_login =
+ le32_to_cpu(nv->host_p) & BIT_10 ? 1: 0;
+ ha->flags.enable_target_reset =
+ le32_to_cpu(nv->host_p) & BIT_11 ? 1: 0;
+ ha->flags.enable_led_scheme = 0;
+ ha->flags.disable_serdes = le32_to_cpu(nv->host_p) & BIT_5 ? 1: 0;
+
+ ha->operating_mode = (le32_to_cpu(icb->firmware_options_2) &
+ (BIT_6 | BIT_5 | BIT_4)) >> 4;
+
+ memcpy(ha->fw_seriallink_options24, nv->seriallink_options,
+ sizeof(ha->fw_seriallink_options24));
+
+ /* save HBA serial number */
+ ha->serial0 = icb->port_name[5];
+ ha->serial1 = icb->port_name[6];
+ ha->serial2 = icb->port_name[7];
+ ha->node_name = icb->node_name;
+ ha->port_name = icb->port_name;
+
+ icb->execution_throttle = __constant_cpu_to_le16(0xFFFF);
+
+ ha->retry_count = le16_to_cpu(nv->login_retry_count);
+
+ /* Set minimum login_timeout to 4 seconds. */
+ if (le16_to_cpu(nv->login_timeout) < ql2xlogintimeout)
+ nv->login_timeout = cpu_to_le16(ql2xlogintimeout);
+ if (le16_to_cpu(nv->login_timeout) < 4)
+ nv->login_timeout = __constant_cpu_to_le16(4);
+ ha->login_timeout = le16_to_cpu(nv->login_timeout);
+ icb->login_timeout = cpu_to_le16(nv->login_timeout);
+
+ /* Set minimum RATOV to 200 tenths of a second. */
+ ha->r_a_tov = 200;
+
+ ha->loop_reset_delay = nv->reset_delay;
+
+ /* Link Down Timeout = 0:
+ *
+ * When Port Down timer expires we will start returning
+ * I/O's to OS with "DID_NO_CONNECT".
+ *
+ * Link Down Timeout != 0:
+ *
+ * The driver waits for the link to come up after link down
+ * before returning I/Os to OS with "DID_NO_CONNECT".
+ */
+ if (le16_to_cpu(nv->link_down_timeout) == 0) {
+ ha->loop_down_abort_time =
+ (LOOP_DOWN_TIME - LOOP_DOWN_TIMEOUT);
+ } else {
+ ha->link_down_timeout = le16_to_cpu(nv->link_down_timeout);
+ ha->loop_down_abort_time =
+ (LOOP_DOWN_TIME - ha->link_down_timeout);
+ }
+
+ /* Need enough time to try and get the port back. */
+ ha->port_down_retry_count = le16_to_cpu(nv->port_down_retry_count);
+ if (qlport_down_retry)
+ ha->port_down_retry_count = qlport_down_retry;
+
+ /* Set login_retry_count */
+ ha->login_retry_count = le16_to_cpu(nv->login_retry_count);
+ if (ha->port_down_retry_count ==
+ le16_to_cpu(nv->port_down_retry_count) &&
+ ha->port_down_retry_count > 3)
+ ha->login_retry_count = ha->port_down_retry_count;
+ else if (ha->port_down_retry_count > (int)ha->login_retry_count)
+ ha->login_retry_count = ha->port_down_retry_count;
+ if (ql2xloginretrycount)
+ ha->login_retry_count = ql2xloginretrycount;
+
+ /* Enable ZIO. */
+ if (!ha->flags.init_done) {
+ ha->zio_mode = le32_to_cpu(icb->firmware_options_2) &
+ (BIT_3 | BIT_2 | BIT_1 | BIT_0);
+ ha->zio_timer = le16_to_cpu(icb->interrupt_delay_timer) ?
+ le16_to_cpu(icb->interrupt_delay_timer): 2;
+ }
+ icb->firmware_options_2 &= __constant_cpu_to_le32(
+ ~(BIT_3 | BIT_2 | BIT_1 | BIT_0));
+ ha->flags.process_response_queue = 0;
+ if (ha->zio_mode != QLA_ZIO_DISABLED) {
+ ha->zio_mode = QLA_ZIO_MODE_6;
+
+ DEBUG2(printk("scsi(%ld): ZIO mode %d enabled; timer delay "
+ "(%d us).\n", ha->host_no, ha->zio_mode,
+ ha->zio_timer * 100));
+ qla_printk(KERN_INFO, ha,
+ "ZIO mode %d enabled; timer delay (%d us).\n",
+ ha->zio_mode, ha->zio_timer * 100);
+
+ icb->firmware_options_2 |= cpu_to_le32(
+ (uint32_t)ha->zio_mode);
+ icb->interrupt_delay_timer = cpu_to_le16(ha->zio_timer);
+ ha->flags.process_response_queue = 1;
+ }
+
+ if (rval) {
+ DEBUG2_3(printk(KERN_WARNING
+ "scsi(%ld): NVRAM configuration failed!\n", ha->host_no));
+ }
+ return (rval);
+}
+
+static int
+qla24xx_load_risc_flash(scsi_qla_host_t *ha, uint32_t *srisc_addr)
+{
+ int rval;
+ int segments, fragment;
+ uint32_t faddr;
+ uint32_t *dcode, dlen;
+ uint32_t risc_addr;
+ uint32_t risc_size;
+ uint32_t i;
+
+ rval = QLA_SUCCESS;
+
+ segments = FA_RISC_CODE_SEGMENTS;
+ faddr = FA_RISC_CODE_ADDR;
+ dcode = (uint32_t *)ha->request_ring;
+ *srisc_addr = 0;
+
+ /* Validate firmware image by checking version. */
+ qla24xx_read_flash_data(ha, dcode, faddr + 4, 4);
+ for (i = 0; i < 4; i++)
+ dcode[i] = be32_to_cpu(dcode[i]);
+ if ((dcode[0] == 0xffffffff && dcode[1] == 0xffffffff &&
+ dcode[2] == 0xffffffff && dcode[3] == 0xffffffff) ||
+ (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 &&
+ dcode[3] == 0)) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to verify integrity of flash firmware image!\n");
+ qla_printk(KERN_WARNING, ha,
+ "Firmware data: %08x %08x %08x %08x!\n", dcode[0],
+ dcode[1], dcode[2], dcode[3]);
+
+ return QLA_FUNCTION_FAILED;
+ }
+
+ while (segments && rval == QLA_SUCCESS) {
+ /* Read segment's load information. */
+ qla24xx_read_flash_data(ha, dcode, faddr, 4);
+
+ risc_addr = be32_to_cpu(dcode[2]);
+ *srisc_addr = *srisc_addr == 0 ? risc_addr : *srisc_addr;
+ risc_size = be32_to_cpu(dcode[3]);
+
+ fragment = 0;
+ while (risc_size > 0 && rval == QLA_SUCCESS) {
+ dlen = (uint32_t)(ha->fw_transfer_size >> 2);
+ if (dlen > risc_size)
+ dlen = risc_size;
+
+ DEBUG7(printk("scsi(%ld): Loading risc segment@ risc "
+ "addr %x, number of dwords 0x%x, offset 0x%x.\n",
+ ha->host_no, risc_addr, dlen, faddr));
+
+ qla24xx_read_flash_data(ha, dcode, faddr, dlen);
+ for (i = 0; i < dlen; i++)
+ dcode[i] = swab32(dcode[i]);
+
+ rval = qla2x00_load_ram(ha, ha->request_dma, risc_addr,
+ dlen);
+ if (rval) {
+ DEBUG(printk("scsi(%ld):[ERROR] Failed to load "
+ "segment %d of firmware\n", ha->host_no,
+ fragment));
+ qla_printk(KERN_WARNING, ha,
+ "[ERROR] Failed to load segment %d of "
+ "firmware\n", fragment);
+ break;
+ }
+
+ faddr += dlen;
+ risc_addr += dlen;
+ risc_size -= dlen;
+ fragment++;
+ }
+
+ /* Next segment. */
+ segments--;
+ }
+
+ return rval;
+}
+
+#define QLA_FW_URL "ftp://ftp.qlogic.com/outgoing/linux/firmware/"
+
+int
+qla2x00_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr)
+{
+ int rval;
+ int i, fragment;
+ uint16_t *wcode, *fwcode;
+ uint32_t risc_addr, risc_size, fwclen, wlen, *seg;
+ struct fw_blob *blob;
+
+ /* Load firmware blob. */
+ blob = qla2x00_request_firmware(ha);
+ if (!blob) {
+ qla_printk(KERN_ERR, ha, "Firmware image unavailable.\n");
+ qla_printk(KERN_ERR, ha, "Firmware images can be retrieved "
+ "from: " QLA_FW_URL ".\n");
+ return QLA_FUNCTION_FAILED;
+ }
+
+ rval = QLA_SUCCESS;
+
+ wcode = (uint16_t *)ha->request_ring;
+ *srisc_addr = 0;
+ fwcode = (uint16_t *)blob->fw->data;
+ fwclen = 0;
+
+ /* Validate firmware image by checking version. */
+ if (blob->fw->size < 8 * sizeof(uint16_t)) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to verify integrity of firmware image (%Zd)!\n",
+ blob->fw->size);
+ goto fail_fw_integrity;
+ }
+ for (i = 0; i < 4; i++)
+ wcode[i] = be16_to_cpu(fwcode[i + 4]);
+ if ((wcode[0] == 0xffff && wcode[1] == 0xffff && wcode[2] == 0xffff &&
+ wcode[3] == 0xffff) || (wcode[0] == 0 && wcode[1] == 0 &&
+ wcode[2] == 0 && wcode[3] == 0)) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to verify integrity of firmware image!\n");
+ qla_printk(KERN_WARNING, ha,
+ "Firmware data: %04x %04x %04x %04x!\n", wcode[0],
+ wcode[1], wcode[2], wcode[3]);
+ goto fail_fw_integrity;
+ }
+
+ seg = blob->segs;
+ while (*seg && rval == QLA_SUCCESS) {
+ risc_addr = *seg;
+ *srisc_addr = *srisc_addr == 0 ? *seg : *srisc_addr;
+ risc_size = be16_to_cpu(fwcode[3]);
+
+ /* Validate firmware image size. */
+ fwclen += risc_size * sizeof(uint16_t);
+ if (blob->fw->size < fwclen) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to verify integrity of firmware image "
+ "(%Zd)!\n", blob->fw->size);
+ goto fail_fw_integrity;
+ }
+
+ fragment = 0;
+ while (risc_size > 0 && rval == QLA_SUCCESS) {
+ wlen = (uint16_t)(ha->fw_transfer_size >> 1);
+ if (wlen > risc_size)
+ wlen = risc_size;
+
+ DEBUG7(printk("scsi(%ld): Loading risc segment@ risc "
+ "addr %x, number of words 0x%x.\n", ha->host_no,
+ risc_addr, wlen));
+
+ for (i = 0; i < wlen; i++)
+ wcode[i] = swab16(fwcode[i]);
+
+ rval = qla2x00_load_ram(ha, ha->request_dma, risc_addr,
+ wlen);
+ if (rval) {
+ DEBUG(printk("scsi(%ld):[ERROR] Failed to load "
+ "segment %d of firmware\n", ha->host_no,
+ fragment));
+ qla_printk(KERN_WARNING, ha,
+ "[ERROR] Failed to load segment %d of "
+ "firmware\n", fragment);
+ break;
+ }
+
+ fwcode += wlen;
+ risc_addr += wlen;
+ risc_size -= wlen;
+ fragment++;
+ }
+
+ /* Next segment. */
+ seg++;
+ }
+ return rval;
+
+fail_fw_integrity:
+ return QLA_FUNCTION_FAILED;
+}
+
+int
+qla24xx_load_risc(scsi_qla_host_t *ha, uint32_t *srisc_addr)
+{
+ int rval;
+ int segments, fragment;
+ uint32_t *dcode, dlen;
+ uint32_t risc_addr;
+ uint32_t risc_size;
+ uint32_t i;
+ struct fw_blob *blob;
+ uint32_t *fwcode, fwclen;
+
+ /* Load firmware blob. */
+ blob = qla2x00_request_firmware(ha);
+ if (!blob) {
+ qla_printk(KERN_ERR, ha, "Firmware image unavailable.\n");
+ qla_printk(KERN_ERR, ha, "Firmware images can be retrieved "
+ "from: " QLA_FW_URL ".\n");
+
+ /* 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);
+ }
+
+ rval = QLA_SUCCESS;
+
+ segments = FA_RISC_CODE_SEGMENTS;
+ dcode = (uint32_t *)ha->request_ring;
+ *srisc_addr = 0;
+ fwcode = (uint32_t *)blob->fw->data;
+ fwclen = 0;
+
+ /* Validate firmware image by checking version. */
+ if (blob->fw->size < 8 * sizeof(uint32_t)) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to verify integrity of firmware image (%Zd)!\n",
+ blob->fw->size);
+ goto fail_fw_integrity;
+ }
+ for (i = 0; i < 4; i++)
+ dcode[i] = be32_to_cpu(fwcode[i + 4]);
+ if ((dcode[0] == 0xffffffff && dcode[1] == 0xffffffff &&
+ dcode[2] == 0xffffffff && dcode[3] == 0xffffffff) ||
+ (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 &&
+ dcode[3] == 0)) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to verify integrity of firmware image!\n");
+ qla_printk(KERN_WARNING, ha,
+ "Firmware data: %08x %08x %08x %08x!\n", dcode[0],
+ dcode[1], dcode[2], dcode[3]);
+ goto fail_fw_integrity;
+ }
+
+ while (segments && rval == QLA_SUCCESS) {
+ risc_addr = be32_to_cpu(fwcode[2]);
+ *srisc_addr = *srisc_addr == 0 ? risc_addr : *srisc_addr;
+ risc_size = be32_to_cpu(fwcode[3]);
+
+ /* Validate firmware image size. */
+ fwclen += risc_size * sizeof(uint32_t);
+ if (blob->fw->size < fwclen) {
+ qla_printk(KERN_WARNING, ha,
+ "Unable to verify integrity of firmware image "
+ "(%Zd)!\n", blob->fw->size);
+
+ goto fail_fw_integrity;
+ }
+
+ fragment = 0;
+ while (risc_size > 0 && rval == QLA_SUCCESS) {
+ dlen = (uint32_t)(ha->fw_transfer_size >> 2);
+ if (dlen > risc_size)
+ dlen = risc_size;
+
+ DEBUG7(printk("scsi(%ld): Loading risc segment@ risc "
+ "addr %x, number of dwords 0x%x.\n", ha->host_no,
+ risc_addr, dlen));
+
+ for (i = 0; i < dlen; i++)
+ dcode[i] = swab32(fwcode[i]);
+
+ rval = qla2x00_load_ram(ha, ha->request_dma, risc_addr,
+ dlen);
+ if (rval) {
+ DEBUG(printk("scsi(%ld):[ERROR] Failed to load "
+ "segment %d of firmware\n", ha->host_no,
+ fragment));
+ qla_printk(KERN_WARNING, ha,
+ "[ERROR] Failed to load segment %d of "
+ "firmware\n", fragment);
+ break;
+ }
+
+ fwcode += dlen;
+ risc_addr += dlen;
+ risc_size -= dlen;
+ fragment++;
+ }
+
+ /* Next segment. */
+ segments--;
+ }
+ return rval;
+
+fail_fw_integrity:
+ return QLA_FUNCTION_FAILED;
+}
+
+void
+qla2x00_try_to_stop_firmware(scsi_qla_host_t *ha)
+{
+ int ret, retries;
+
+ if (!IS_QLA24XX(ha) && !IS_QLA54XX(ha))
+ return;
+
+ ret = qla2x00_stop_firmware(ha);
+ for (retries = 5; ret != QLA_SUCCESS && retries ; retries--) {
+ qla2x00_reset_chip(ha);
+ if (qla2x00_chip_diag(ha) != QLA_SUCCESS)
+ continue;
+ if (qla2x00_setup_chip(ha) != QLA_SUCCESS)
+ continue;
+ qla_printk(KERN_INFO, ha,
+ "Attempting retry of stop-firmware command...\n");
+ ret = qla2x00_stop_firmware(ha);
+ }
+}