diff options
Diffstat (limited to 'drivers/scsi/qla2xxx')
-rw-r--r-- | drivers/scsi/qla2xxx/qla_attr.c | 68 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_def.h | 38 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_gbl.h | 6 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_init.c | 218 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_isr.c | 245 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_mbx.c | 18 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_os.c | 24 | ||||
-rw-r--r-- | drivers/scsi/qla2xxx/qla_sup.c | 348 |
8 files changed, 744 insertions, 221 deletions
diff --git a/drivers/scsi/qla2xxx/qla_attr.c b/drivers/scsi/qla2xxx/qla_attr.c index 7b18a6c7b7e..8081b637d97 100644 --- a/drivers/scsi/qla2xxx/qla_attr.c +++ b/drivers/scsi/qla2xxx/qla_attr.c @@ -140,6 +140,8 @@ qla2x00_sysfs_write_nvram(struct kobject *kobj, char *buf, loff_t off, ha->isp_ops.write_nvram(ha, (uint8_t *)buf, ha->nvram_base, count); spin_unlock_irqrestore(&ha->hardware_lock, flags); + set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + return (count); } @@ -653,6 +655,43 @@ qla2x00_beacon_store(struct class_device *cdev, const char *buf, return count; } +static ssize_t +qla2x00_optrom_bios_version_show(struct class_device *cdev, char *buf) +{ + scsi_qla_host_t *ha = to_qla_host(class_to_shost(cdev)); + + return snprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->bios_revision[1], + ha->bios_revision[0]); +} + +static ssize_t +qla2x00_optrom_efi_version_show(struct class_device *cdev, char *buf) +{ + scsi_qla_host_t *ha = to_qla_host(class_to_shost(cdev)); + + return snprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->efi_revision[1], + ha->efi_revision[0]); +} + +static ssize_t +qla2x00_optrom_fcode_version_show(struct class_device *cdev, char *buf) +{ + scsi_qla_host_t *ha = to_qla_host(class_to_shost(cdev)); + + return snprintf(buf, PAGE_SIZE, "%d.%02d\n", ha->fcode_revision[1], + ha->fcode_revision[0]); +} + +static ssize_t +qla2x00_optrom_fw_version_show(struct class_device *cdev, char *buf) +{ + scsi_qla_host_t *ha = to_qla_host(class_to_shost(cdev)); + + return snprintf(buf, PAGE_SIZE, "%d.%02d.%02d %d\n", + ha->fw_revision[0], ha->fw_revision[1], ha->fw_revision[2], + ha->fw_revision[3]); +} + static CLASS_DEVICE_ATTR(driver_version, S_IRUGO, qla2x00_drvr_version_show, NULL); static CLASS_DEVICE_ATTR(fw_version, S_IRUGO, qla2x00_fw_version_show, NULL); @@ -669,6 +708,14 @@ static CLASS_DEVICE_ATTR(zio_timer, S_IRUGO | S_IWUSR, qla2x00_zio_timer_show, qla2x00_zio_timer_store); static CLASS_DEVICE_ATTR(beacon, S_IRUGO | S_IWUSR, qla2x00_beacon_show, qla2x00_beacon_store); +static CLASS_DEVICE_ATTR(optrom_bios_version, S_IRUGO, + qla2x00_optrom_bios_version_show, NULL); +static CLASS_DEVICE_ATTR(optrom_efi_version, S_IRUGO, + qla2x00_optrom_efi_version_show, NULL); +static CLASS_DEVICE_ATTR(optrom_fcode_version, S_IRUGO, + qla2x00_optrom_fcode_version_show, NULL); +static CLASS_DEVICE_ATTR(optrom_fw_version, S_IRUGO, + qla2x00_optrom_fw_version_show, NULL); struct class_device_attribute *qla2x00_host_attrs[] = { &class_device_attr_driver_version, @@ -683,6 +730,10 @@ struct class_device_attribute *qla2x00_host_attrs[] = { &class_device_attr_zio, &class_device_attr_zio_timer, &class_device_attr_beacon, + &class_device_attr_optrom_bios_version, + &class_device_attr_optrom_efi_version, + &class_device_attr_optrom_fcode_version, + &class_device_attr_optrom_fw_version, NULL, }; @@ -836,21 +887,24 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost) link_stat_t stat_buf; struct fc_host_statistics *pfc_host_stat; + rval = QLA_FUNCTION_FAILED; pfc_host_stat = &ha->fc_host_stat; memset(pfc_host_stat, -1, sizeof(struct fc_host_statistics)); if (IS_QLA24XX(ha) || IS_QLA54XX(ha)) { rval = qla24xx_get_isp_stats(ha, (uint32_t *)&stat_buf, sizeof(stat_buf) / 4, mb_stat); - } else { + } else if (atomic_read(&ha->loop_state) == LOOP_READY && + !test_bit(ABORT_ISP_ACTIVE, &ha->dpc_flags) && + !test_bit(ISP_ABORT_NEEDED, &ha->dpc_flags) && + !ha->dpc_active) { + /* Must be in a 'READY' state for statistics retrieval. */ rval = qla2x00_get_link_status(ha, ha->loop_id, &stat_buf, mb_stat); } - if (rval != 0) { - qla_printk(KERN_WARNING, ha, - "Unable to retrieve host statistics (%d).\n", mb_stat[0]); - return pfc_host_stat; - } + + if (rval != QLA_SUCCESS) + goto done; pfc_host_stat->link_failure_count = stat_buf.link_fail_cnt; pfc_host_stat->loss_of_sync_count = stat_buf.loss_sync_cnt; @@ -858,7 +912,7 @@ qla2x00_get_fc_host_stats(struct Scsi_Host *shost) pfc_host_stat->prim_seq_protocol_err_count = stat_buf.prim_seq_err_cnt; pfc_host_stat->invalid_tx_word_count = stat_buf.inval_xmit_word_cnt; pfc_host_stat->invalid_crc_count = stat_buf.inval_crc_cnt; - +done: return pfc_host_stat; } diff --git a/drivers/scsi/qla2xxx/qla_def.h b/drivers/scsi/qla2xxx/qla_def.h index 2c10130d9e0..05f4f2a378e 100644 --- a/drivers/scsi/qla2xxx/qla_def.h +++ b/drivers/scsi/qla2xxx/qla_def.h @@ -2045,6 +2045,29 @@ struct isp_operations { uint32_t, uint32_t); int (*write_optrom) (struct scsi_qla_host *, uint8_t *, uint32_t, uint32_t); + + int (*get_flash_version) (struct scsi_qla_host *, void *); +}; + +/* MSI-X Support *************************************************************/ + +#define QLA_MSIX_CHIP_REV_24XX 3 +#define QLA_MSIX_FW_MODE(m) (((m) & (BIT_7|BIT_8|BIT_9)) >> 7) +#define QLA_MSIX_FW_MODE_1(m) (QLA_MSIX_FW_MODE(m) == 1) + +#define QLA_MSIX_DEFAULT 0x00 +#define QLA_MSIX_RSP_Q 0x01 + +#define QLA_MSIX_ENTRIES 2 +#define QLA_MIDX_DEFAULT 0 +#define QLA_MIDX_RSP_Q 1 + +struct scsi_qla_host; + +struct qla_msix_entry { + int have_irq; + uint16_t msix_vector; + uint16_t msix_entry; }; /* @@ -2077,6 +2100,7 @@ typedef struct scsi_qla_host { uint32_t enable_lip_full_login :1; uint32_t enable_target_reset :1; uint32_t enable_led_scheme :1; + uint32_t inta_enabled :1; uint32_t msi_enabled :1; uint32_t msix_enabled :1; uint32_t disable_serdes :1; @@ -2316,8 +2340,6 @@ typedef struct scsi_qla_host { #define MBX_INTR_WAIT 2 #define MBX_UPDATE_FLASH_ACTIVE 3 - spinlock_t mbx_reg_lock; /* Mbx Cmd Register Lock */ - struct semaphore mbx_cmd_sem; /* Serialialize mbx access */ struct semaphore mbx_intr_sem; /* Used for completion notification */ @@ -2358,6 +2380,7 @@ typedef struct scsi_qla_host { uint8_t host_str[16]; uint32_t pci_attr; + uint16_t chip_revision; uint16_t product_id[4]; @@ -2379,6 +2402,15 @@ typedef struct scsi_qla_host { #define QLA_SREADING 1 #define QLA_SWRITING 2 + /* PCI expansion ROM image information. */ +#define ROM_CODE_TYPE_BIOS 0 +#define ROM_CODE_TYPE_FCODE 1 +#define ROM_CODE_TYPE_EFI 3 + uint8_t bios_revision[2]; + uint8_t efi_revision[2]; + uint8_t fcode_revision[16]; + uint32_t fw_revision[4]; + /* Needed for BEACON */ uint16_t beacon_blink_led; uint8_t beacon_color_state; @@ -2391,6 +2423,8 @@ typedef struct scsi_qla_host { uint16_t zio_mode; uint16_t zio_timer; struct fc_host_statistics fc_host_stat; + + struct qla_msix_entry msix_entries[QLA_MSIX_ENTRIES]; } scsi_qla_host_t; diff --git a/drivers/scsi/qla2xxx/qla_gbl.h b/drivers/scsi/qla2xxx/qla_gbl.h index e4dd12f4b80..74544ae4b0e 100644 --- a/drivers/scsi/qla2xxx/qla_gbl.h +++ b/drivers/scsi/qla2xxx/qla_gbl.h @@ -224,6 +224,9 @@ extern irqreturn_t qla24xx_intr_handler(int, void *); extern void qla2x00_process_response_queue(struct scsi_qla_host *); extern void qla24xx_process_response_queue(struct scsi_qla_host *); +extern int qla2x00_request_irqs(scsi_qla_host_t *); +extern void qla2x00_free_irqs(scsi_qla_host_t *); + /* * Global Function Prototypes in qla_sup.c source file. */ @@ -259,6 +262,9 @@ extern uint8_t *qla24xx_read_optrom_data(struct scsi_qla_host *, uint8_t *, extern int qla24xx_write_optrom_data(struct scsi_qla_host *, uint8_t *, uint32_t, uint32_t); +extern int qla2x00_get_flash_version(scsi_qla_host_t *, void *); +extern int qla24xx_get_flash_version(scsi_qla_host_t *, void *); + /* * Global Function Prototypes in qla_dbg.c source file. */ diff --git a/drivers/scsi/qla2xxx/qla_init.c b/drivers/scsi/qla2xxx/qla_init.c index b3dac26ddba..98c01cd5e1a 100644 --- a/drivers/scsi/qla2xxx/qla_init.c +++ b/drivers/scsi/qla2xxx/qla_init.c @@ -65,7 +65,7 @@ qla2x00_initialize_adapter(scsi_qla_host_t *ha) ha->flags.reset_active = 0; atomic_set(&ha->loop_down_timer, LOOP_DOWN_TIME); atomic_set(&ha->loop_state, LOOP_DOWN); - ha->device_flags = 0; + ha->device_flags = DFLG_NO_CABLE; ha->dpc_flags = 0; ha->flags.management_server_logged_in = 0; ha->marker_needed = 0; @@ -77,16 +77,23 @@ qla2x00_initialize_adapter(scsi_qla_host_t *ha) qla_printk(KERN_INFO, ha, "Configuring PCI space...\n"); rval = ha->isp_ops.pci_config(ha); if (rval) { - DEBUG2(printk("scsi(%ld): Unable to configure PCI space=n", + DEBUG2(printk("scsi(%ld): Unable to configure PCI space.\n", ha->host_no)); return (rval); } ha->isp_ops.reset_chip(ha); + ha->isp_ops.get_flash_version(ha, ha->request_ring); + qla_printk(KERN_INFO, ha, "Configure NVRAM parameters...\n"); - ha->isp_ops.nvram_config(ha); + rval = ha->isp_ops.nvram_config(ha); + if (rval) { + DEBUG2(printk("scsi(%ld): Unable to verify NVRAM data.\n", + ha->host_no)); + return rval; + } if (ha->flags.disable_serdes) { /* Mask HBA via NVRAM settings? */ @@ -293,6 +300,8 @@ qla24xx_pci_config(scsi_qla_host_t *ha) d &= ~PCI_ROM_ADDRESS_ENABLE; pci_write_config_dword(ha->pdev, PCI_ROM_ADDRESS, d); + pci_read_config_word(ha->pdev, PCI_REVISION_ID, &ha->chip_revision); + /* Get PCI bus information. */ spin_lock_irqsave(&ha->hardware_lock, flags); ha->pci_attr = RD_REG_DWORD(®->ctrl_status); @@ -1351,6 +1360,39 @@ qla2x00_configure_hba(scsi_qla_host_t *ha) return(rval); } +static inline void +qla2x00_set_model_info(scsi_qla_host_t *ha, uint8_t *model, size_t len, char *def) +{ + char *st, *en; + uint16_t index; + + if (memcmp(model, BINZERO, len) != 0) { + strncpy(ha->model_number, model, len); + st = en = ha->model_number; + en += len - 1; + while (en > st) { + if (*en != 0x20 && *en != 0x00) + break; + *en-- = '\0'; + } + + index = (ha->pdev->subsystem_device & 0xff); + if (ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC && + index < QLA_MODEL_NAMES) + ha->model_desc = qla2x00_model_name[index * 2 + 1]; + } else { + index = (ha->pdev->subsystem_device & 0xff); + if (ha->pdev->subsystem_vendor == PCI_VENDOR_ID_QLOGIC && + index < QLA_MODEL_NAMES) { + strcpy(ha->model_number, + qla2x00_model_name[index * 2]); + ha->model_desc = qla2x00_model_name[index * 2 + 1]; + } else { + strcpy(ha->model_number, def); + } + } +} + /* * NVRAM configuration for ISP 2xxx * @@ -1367,7 +1409,6 @@ qla2x00_configure_hba(scsi_qla_host_t *ha) int qla2x00_nvram_config(scsi_qla_host_t *ha) { - int rval; uint8_t chksum = 0; uint16_t cnt; uint8_t *dptr1, *dptr2; @@ -1376,8 +1417,6 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) uint8_t *ptr = (uint8_t *)ha->request_ring; struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; - rval = QLA_SUCCESS; - /* Determine NVRAM starting address. */ ha->nvram_size = sizeof(nvram_t); ha->nvram_base = 0; @@ -1401,55 +1440,7 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) qla_printk(KERN_WARNING, ha, "Inconsistent NVRAM detected: " "checksum=0x%x id=%c version=0x%x.\n", chksum, nv->id[0], nv->nvram_version); - qla_printk(KERN_WARNING, ha, "Falling back to functioning (yet " - "invalid -- WWPN) defaults.\n"); - - /* - * Set default initialization control block. - */ - memset(nv, 0, ha->nvram_size); - nv->parameter_block_version = ICB_VERSION; - - if (IS_QLA23XX(ha)) { - nv->firmware_options[0] = BIT_2 | BIT_1; - nv->firmware_options[1] = BIT_7 | BIT_5; - nv->add_firmware_options[0] = BIT_5; - nv->add_firmware_options[1] = BIT_5 | BIT_4; - nv->frame_payload_size = __constant_cpu_to_le16(2048); - nv->special_options[1] = BIT_7; - } else if (IS_QLA2200(ha)) { - nv->firmware_options[0] = BIT_2 | BIT_1; - nv->firmware_options[1] = BIT_7 | BIT_5; - nv->add_firmware_options[0] = BIT_5; - nv->add_firmware_options[1] = BIT_5 | BIT_4; - nv->frame_payload_size = __constant_cpu_to_le16(1024); - } else if (IS_QLA2100(ha)) { - nv->firmware_options[0] = BIT_3 | BIT_1; - nv->firmware_options[1] = BIT_5; - nv->frame_payload_size = __constant_cpu_to_le16(1024); - } - - nv->max_iocb_allocation = __constant_cpu_to_le16(256); - nv->execution_throttle = __constant_cpu_to_le16(16); - nv->retry_count = 8; - nv->retry_delay = 1; - - nv->port_name[0] = 33; - nv->port_name[3] = 224; - nv->port_name[4] = 139; - - nv->login_timeout = 4; - - /* - * Set default host adapter parameters - */ - nv->host_p[1] = BIT_2; - nv->reset_delay = 5; - nv->port_down_retry_count = 8; - nv->max_luns_per_target = __constant_cpu_to_le16(8); - nv->link_down_timeout = 60; - - rval = 1; + return QLA_FUNCTION_FAILED; } #if defined(CONFIG_IA64_GENERIC) || defined(CONFIG_IA64_SGI_SN2) @@ -1489,33 +1480,8 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) strcpy(ha->model_number, "QLA2300"); } } else { - if (rval == 0 && - memcmp(nv->model_number, BINZERO, - sizeof(nv->model_number)) != 0) { - char *st, *en; - - strncpy(ha->model_number, nv->model_number, - sizeof(nv->model_number)); - st = en = ha->model_number; - en += sizeof(nv->model_number) - 1; - while (en > st) { - if (*en != 0x20 && *en != 0x00) - break; - *en-- = '\0'; - } - } else { - uint16_t index; - - index = (ha->pdev->subsystem_device & 0xff); - if (index < QLA_MODEL_NAMES) { - strcpy(ha->model_number, - qla2x00_model_name[index * 2]); - ha->model_desc = - qla2x00_model_name[index * 2 + 1]; - } else { - strcpy(ha->model_number, "QLA23xx"); - } - } + qla2x00_set_model_info(ha, nv->model_number, + sizeof(nv->model_number), "QLA23xx"); } } else if (IS_QLA2200(ha)) { nv->firmware_options[0] |= BIT_2; @@ -1687,11 +1653,7 @@ qla2x00_nvram_config(scsi_qla_host_t *ha) } } - if (rval) { - DEBUG2_3(printk(KERN_WARNING - "scsi(%ld): NVRAM configuration failed!\n", ha->host_no)); - } - return (rval); + return QLA_SUCCESS; } static void @@ -3107,7 +3069,11 @@ qla2x00_abort_isp(scsi_qla_host_t *ha) } spin_unlock_irqrestore(&ha->hardware_lock, flags); - ha->isp_ops.nvram_config(ha); + ha->isp_ops.get_flash_version(ha, ha->request_ring); + + rval = ha->isp_ops.nvram_config(ha); + if (rval) + goto isp_abort_retry; if (!qla2x00_restart_isp(ha)) { clear_bit(RESET_MARKER_NEEDED, &ha->dpc_flags); @@ -3137,6 +3103,7 @@ qla2x00_abort_isp(scsi_qla_host_t *ha) } } } else { /* failed the ISP abort */ +isp_abort_retry: ha->flags.online = 1; if (test_bit(ISP_ABORT_RETRY, &ha->dpc_flags)) { if (ha->isp_abort_cnt == 0) { @@ -3326,7 +3293,6 @@ qla24xx_reset_adapter(scsi_qla_host_t *ha) int qla24xx_nvram_config(scsi_qla_host_t *ha) { - int rval; struct init_cb_24xx *icb; struct nvram_24xx *nv; uint32_t *dptr; @@ -3334,7 +3300,6 @@ qla24xx_nvram_config(scsi_qla_host_t *ha) uint32_t chksum; uint16_t cnt; - rval = QLA_SUCCESS; icb = (struct init_cb_24xx *)ha->init_cb; nv = (struct nvram_24xx *)ha->request_ring; @@ -3367,51 +3332,7 @@ qla24xx_nvram_config(scsi_qla_host_t *ha) 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; + return QLA_FUNCTION_FAILED; } /* Reset Initialization control block */ @@ -3438,25 +3359,8 @@ qla24xx_nvram_config(scsi_qla_host_t *ha) /* * 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"); + qla2x00_set_model_info(ha, nv->model_name, sizeof(nv->model_name), + "QLA2462"); /* Use alternate WWN? */ if (nv->host_p & __constant_cpu_to_le32(BIT_15)) { @@ -3575,11 +3479,7 @@ qla24xx_nvram_config(scsi_qla_host_t *ha) ha->flags.process_response_queue = 1; } - if (rval) { - DEBUG2_3(printk(KERN_WARNING - "scsi(%ld): NVRAM configuration failed!\n", ha->host_no)); - } - return (rval); + return QLA_SUCCESS; } static int diff --git a/drivers/scsi/qla2xxx/qla_isr.c b/drivers/scsi/qla2xxx/qla_isr.c index 39fd17b05be..d4885616cd3 100644 --- a/drivers/scsi/qla2xxx/qla_isr.c +++ b/drivers/scsi/qla2xxx/qla_isr.c @@ -86,12 +86,8 @@ qla2100_intr_handler(int irq, void *dev_id) if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) && (status & MBX_INTERRUPT) && ha->flags.mbox_int) { - spin_lock_irqsave(&ha->mbx_reg_lock, flags); - set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); up(&ha->mbx_intr_sem); - - spin_unlock_irqrestore(&ha->mbx_reg_lock, flags); } return (IRQ_HANDLED); @@ -199,12 +195,8 @@ qla2300_intr_handler(int irq, void *dev_id) if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) && (status & MBX_INTERRUPT) && ha->flags.mbox_int) { - spin_lock_irqsave(&ha->mbx_reg_lock, flags); - set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); up(&ha->mbx_intr_sem); - - spin_unlock_irqrestore(&ha->mbx_reg_lock, flags); } return (IRQ_HANDLED); @@ -654,10 +646,8 @@ qla2x00_ramp_up_queue_depth(scsi_qla_host_t *ha, srb_t *sp) fcport->last_queue_full + ql2xqfullrampup * HZ)) return; - spin_unlock_irq(&ha->hardware_lock); starget_for_each_device(sdev->sdev_target, fcport, qla2x00_adjust_sdev_qdepth_up); - spin_lock_irq(&ha->hardware_lock); } /** @@ -927,10 +917,8 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt) /* Adjust queue depth for all luns on the port. */ fcport->last_queue_full = jiffies; - spin_unlock_irq(&ha->hardware_lock); starget_for_each_device(cp->device->sdev_target, fcport, qla2x00_adjust_sdev_qdepth_down); - spin_lock_irq(&ha->hardware_lock); break; } if (lscsi_status != SS_CHECK_CONDITION) @@ -995,6 +983,22 @@ qla2x00_status_entry(scsi_qla_host_t *ha, void *pkt) if (lscsi_status != 0) { cp->result = DID_OK << 16 | lscsi_status; + if (lscsi_status == SAM_STAT_TASK_SET_FULL) { + DEBUG2(printk(KERN_INFO + "scsi(%ld): QUEUE FULL status detected " + "0x%x-0x%x.\n", ha->host_no, comp_status, + scsi_status)); + + /* + * Adjust queue depth for all luns on the + * port. + */ + fcport->last_queue_full = jiffies; + starget_for_each_device( + cp->device->sdev_target, fcport, + qla2x00_adjust_sdev_qdepth_down); + break; + } if (lscsi_status != SS_CHECK_CONDITION) break; @@ -1482,12 +1486,8 @@ qla24xx_intr_handler(int irq, void *dev_id) if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) && (status & MBX_INTERRUPT) && ha->flags.mbox_int) { - spin_lock_irqsave(&ha->mbx_reg_lock, flags); - set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); up(&ha->mbx_intr_sem); - - spin_unlock_irqrestore(&ha->mbx_reg_lock, flags); } return IRQ_HANDLED; @@ -1536,3 +1536,216 @@ qla24xx_ms_entry(scsi_qla_host_t *ha, struct ct_entry_24xx *pkt) qla2x00_sp_compl(ha, sp); } +static irqreturn_t +qla24xx_msix_rsp_q(int irq, void *dev_id) +{ + scsi_qla_host_t *ha; + struct device_reg_24xx __iomem *reg; + unsigned long flags; + + ha = dev_id; + reg = &ha->iobase->isp24; + + spin_lock_irqsave(&ha->hardware_lock, flags); + + qla24xx_process_response_queue(ha); + + WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); + RD_REG_DWORD_RELAXED(®->hccr); + + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + return IRQ_HANDLED; +} + +static irqreturn_t +qla24xx_msix_default(int irq, void *dev_id) +{ + scsi_qla_host_t *ha; + struct device_reg_24xx __iomem *reg; + int status; + unsigned long flags; + unsigned long iter; + uint32_t stat; + uint32_t hccr; + uint16_t mb[4]; + + ha = dev_id; + reg = &ha->iobase->isp24; + status = 0; + + spin_lock_irqsave(&ha->hardware_lock, flags); + for (iter = 50; iter--; ) { + stat = RD_REG_DWORD(®->host_status); + if (stat & HSRX_RISC_PAUSED) { + hccr = RD_REG_DWORD(®->hccr); + + qla_printk(KERN_INFO, ha, "RISC paused -- HCCR=%x, " + "Dumping firmware!\n", hccr); + ha->isp_ops.fw_dump(ha, 1); + set_bit(ISP_ABORT_NEEDED, &ha->dpc_flags); + break; + } else if ((stat & HSRX_RISC_INT) == 0) + break; + + switch (stat & 0xff) { + case 0x1: + case 0x2: + case 0x10: + case 0x11: + qla24xx_mbx_completion(ha, MSW(stat)); + status |= MBX_INTERRUPT; + + break; + case 0x12: + 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(ha, mb); + break; + case 0x13: + qla24xx_process_response_queue(ha); + break; + default: + DEBUG2(printk("scsi(%ld): Unrecognized interrupt type " + "(%d).\n", + ha->host_no, stat & 0xff)); + break; + } + WRT_REG_DWORD(®->hccr, HCCRX_CLR_RISC_INT); + RD_REG_DWORD_RELAXED(®->hccr); + } + spin_unlock_irqrestore(&ha->hardware_lock, flags); + + if (test_bit(MBX_INTR_WAIT, &ha->mbx_cmd_flags) && + (status & MBX_INTERRUPT) && ha->flags.mbox_int) { + set_bit(MBX_INTERRUPT, &ha->mbx_cmd_flags); + up(&ha->mbx_intr_sem); + } + + return IRQ_HANDLED; +} + +/* Interrupt handling helpers. */ + +struct qla_init_msix_entry { + uint16_t entry; + uint16_t index; + const char *name; + irqreturn_t (*handler)(int, void *); +}; + +static struct qla_init_msix_entry imsix_entries[QLA_MSIX_ENTRIES] = { + { QLA_MSIX_DEFAULT, QLA_MIDX_DEFAULT, + "qla2xxx (default)", qla24xx_msix_default }, + + { QLA_MSIX_RSP_Q, QLA_MIDX_RSP_Q, + "qla2xxx (rsp_q)", qla24xx_msix_rsp_q }, +}; + +static void +qla24xx_disable_msix(scsi_qla_host_t *ha) +{ + int i; + struct qla_msix_entry *qentry; + + for (i = 0; i < QLA_MSIX_ENTRIES; i++) { + qentry = &ha->msix_entries[imsix_entries[i].index]; + if (qentry->have_irq) + free_irq(qentry->msix_vector, ha); + } + pci_disable_msix(ha->pdev); +} + +static int +qla24xx_enable_msix(scsi_qla_host_t *ha) +{ + int i, ret; + struct msix_entry entries[QLA_MSIX_ENTRIES]; + struct qla_msix_entry *qentry; + + for (i = 0; i < QLA_MSIX_ENTRIES; i++) + entries[i].entry = imsix_entries[i].entry; + + ret = pci_enable_msix(ha->pdev, entries, ARRAY_SIZE(entries)); + if (ret) { + qla_printk(KERN_WARNING, ha, + "MSI-X: Failed to enable support -- %d/%d\n", + QLA_MSIX_ENTRIES, ret); + goto msix_out; + } + ha->flags.msix_enabled = 1; + + for (i = 0; i < QLA_MSIX_ENTRIES; i++) { + qentry = &ha->msix_entries[imsix_entries[i].index]; + qentry->msix_vector = entries[i].vector; + qentry->msix_entry = entries[i].entry; + qentry->have_irq = 0; + ret = request_irq(qentry->msix_vector, + imsix_entries[i].handler, 0, imsix_entries[i].name, ha); + if (ret) { + qla_printk(KERN_WARNING, ha, + "MSI-X: Unable to register handler -- %x/%d.\n", + imsix_entries[i].index, ret); + qla24xx_disable_msix(ha); + goto msix_out; + } + qentry->have_irq = 1; + } + +msix_out: + return ret; +} + +int +qla2x00_request_irqs(scsi_qla_host_t *ha) +{ + int ret; + + /* If possible, enable MSI-X. */ + if (!IS_QLA2432(ha)) + goto skip_msix; + + if (ha->chip_revision < QLA_MSIX_CHIP_REV_24XX || + !QLA_MSIX_FW_MODE_1(ha->fw_attributes)) { + DEBUG2(qla_printk(KERN_WARNING, ha, + "MSI-X: Unsupported ISP2432 (0x%X, 0x%X).\n", + ha->chip_revision, ha->fw_attributes)); + + goto skip_msix; + } + + ret = qla24xx_enable_msix(ha); + if (!ret) { + DEBUG2(qla_printk(KERN_INFO, ha, + "MSI-X: Enabled (0x%X, 0x%X).\n", ha->chip_revision, + ha->fw_attributes)); + return ret; + } + qla_printk(KERN_WARNING, ha, + "MSI-X: Falling back-to INTa mode -- %d.\n", ret); +skip_msix: + ret = request_irq(ha->pdev->irq, ha->isp_ops.intr_handler, + IRQF_DISABLED|IRQF_SHARED, QLA2XXX_DRIVER_NAME, ha); + if (!ret) { + ha->flags.inta_enabled = 1; + ha->host->irq = ha->pdev->irq; + } else { + qla_printk(KERN_WARNING, ha, + "Failed to reserve interrupt %d already in use.\n", + ha->pdev->irq); + } + + return ret; +} + +void +qla2x00_free_irqs(scsi_qla_host_t *ha) +{ + + if (ha->flags.msix_enabled) + qla24xx_disable_msix(ha); + else if (ha->flags.inta_enabled) + free_irq(ha->host->irq, ha); +} diff --git a/drivers/scsi/qla2xxx/qla_mbx.c b/drivers/scsi/qla2xxx/qla_mbx.c index 077e5789bee..83376f6ac3d 100644 --- a/drivers/scsi/qla2xxx/qla_mbx.c +++ b/drivers/scsi/qla2xxx/qla_mbx.c @@ -55,7 +55,6 @@ qla2x00_mailbox_command(scsi_qla_host_t *ha, mbx_cmd_t *mcp) uint16_t __iomem *optr; uint32_t cnt; uint32_t mboxes; - unsigned long mbx_flags = 0; unsigned long wait_time; rval = QLA_SUCCESS; @@ -81,10 +80,6 @@ qla2x00_mailbox_command(scsi_qla_host_t *ha, mbx_cmd_t *mcp) /* Save mailbox command for debug */ ha->mcp = mcp; - /* Try to get mailbox register access */ - if (!abort_active) - spin_lock_irqsave(&ha->mbx_reg_lock, mbx_flags); - DEBUG11(printk("scsi(%ld): prepare to issue mbox cmd=0x%x.\n", ha->host_no, mcp->mb[0])); @@ -161,9 +156,6 @@ qla2x00_mailbox_command(scsi_qla_host_t *ha, mbx_cmd_t *mcp) WRT_REG_WORD(®->isp.hccr, HCCR_SET_HOST_INT); spin_unlock_irqrestore(&ha->hardware_lock, flags); - if (!abort_active) - spin_unlock_irqrestore(&ha->mbx_reg_lock, mbx_flags); - /* Wait for either the timer to expire * or the mbox completion interrupt */ @@ -184,8 +176,6 @@ qla2x00_mailbox_command(scsi_qla_host_t *ha, mbx_cmd_t *mcp) else WRT_REG_WORD(®->isp.hccr, HCCR_SET_HOST_INT); spin_unlock_irqrestore(&ha->hardware_lock, flags); - if (!abort_active) - spin_unlock_irqrestore(&ha->mbx_reg_lock, mbx_flags); wait_time = jiffies + mcp->tov * HZ; /* wait at most tov secs */ while (!ha->flags.mbox_int) { @@ -201,9 +191,6 @@ qla2x00_mailbox_command(scsi_qla_host_t *ha, mbx_cmd_t *mcp) } /* while */ } - if (!abort_active) - spin_lock_irqsave(&ha->mbx_reg_lock, mbx_flags); - /* Check whether we timed out */ if (ha->flags.mbox_int) { uint16_t *iptr2; @@ -256,9 +243,6 @@ qla2x00_mailbox_command(scsi_qla_host_t *ha, mbx_cmd_t *mcp) rval = QLA_FUNCTION_TIMEOUT; } - if (!abort_active) - spin_unlock_irqrestore(&ha->mbx_reg_lock, mbx_flags); - ha->flags.mbox_busy = 0; /* Clean up */ @@ -1713,7 +1697,7 @@ qla24xx_fabric_logout(scsi_qla_host_t *ha, uint16_t loop_id, uint8_t domain, lg->entry_count = 1; lg->nport_handle = cpu_to_le16(loop_id); lg->control_flags = - __constant_cpu_to_le16(LCF_COMMAND_LOGO|LCF_EXPL_LOGO); + __constant_cpu_to_le16(LCF_COMMAND_LOGO|LCF_IMPL_LOGO); lg->port_id[0] = al_pa; lg->port_id[1] = area; lg->port_id[2] = domain; diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index d6445ae841b..68f5d24b938 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -1485,6 +1485,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->isp_ops.fw_dump = qla2100_fw_dump; ha->isp_ops.read_optrom = qla2x00_read_optrom_data; ha->isp_ops.write_optrom = qla2x00_write_optrom_data; + ha->isp_ops.get_flash_version = qla2x00_get_flash_version; if (IS_QLA2100(ha)) { host->max_id = MAX_TARGETS_2100; ha->mbx_count = MAILBOX_REGISTER_COUNT_2100; @@ -1550,6 +1551,7 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) ha->isp_ops.beacon_on = qla24xx_beacon_on; ha->isp_ops.beacon_off = qla24xx_beacon_off; ha->isp_ops.beacon_blink = qla24xx_beacon_blink; + ha->isp_ops.get_flash_version = qla24xx_get_flash_version; ha->gid_list_info_size = 8; ha->optrom_size = OPTROM_SIZE_24XX; } @@ -1564,14 +1566,6 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) INIT_LIST_HEAD(&ha->list); INIT_LIST_HEAD(&ha->fcports); - /* - * These locks are used to prevent more than one CPU - * from modifying the queue at the same time. The - * higher level "host_lock" will reduce most - * contention for these locks. - */ - spin_lock_init(&ha->mbx_reg_lock); - qla2x00_config_dma_addressing(ha); if (qla2x00_mem_alloc(ha)) { qla_printk(KERN_WARNING, ha, @@ -1615,15 +1609,9 @@ qla2x00_probe_one(struct pci_dev *pdev, const struct pci_device_id *id) host->max_lun = MAX_LUNS; host->transportt = qla2xxx_transport_template; - ret = request_irq(pdev->irq, ha->isp_ops.intr_handler, - IRQF_DISABLED|IRQF_SHARED, QLA2XXX_DRIVER_NAME, ha); - if (ret) { - qla_printk(KERN_WARNING, ha, - "Failed to reserve interrupt %d already in use.\n", - pdev->irq); + ret = qla2x00_request_irqs(ha); + if (ret) goto probe_failed; - } - host->irq = pdev->irq; /* Initialized the timer */ qla2x00_start_timer(ha, qla2x00_timer, WATCH_INTERVAL); @@ -1753,9 +1741,7 @@ qla2x00_free_device(scsi_qla_host_t *ha) qla2x00_mem_free(ha); - /* Detach interrupts */ - if (ha->host->irq) - free_irq(ha->host->irq, ha); + qla2x00_free_irqs(ha); /* release io space registers */ if (ha->iobase) diff --git a/drivers/scsi/qla2xxx/qla_sup.c b/drivers/scsi/qla2xxx/qla_sup.c index 15390ad8745..ff1dd4175a7 100644 --- a/drivers/scsi/qla2xxx/qla_sup.c +++ b/drivers/scsi/qla2xxx/qla_sup.c @@ -611,7 +611,6 @@ qla24xx_write_flash_data(scsi_qla_host_t *ha, uint32_t *dwptr, uint32_t faddr, flash_conf_to_access_addr(0x0339), (fdata & 0xff00) | ((fdata << 16) & 0xff0000) | ((fdata >> 16) & 0xff)); - fdata = (faddr & sec_mask) << 2; ret = qla24xx_write_flash_dword(ha, conf_addr, (fdata & 0xff00) |((fdata << 16) & 0xff0000) | ((fdata >> 16) & 0xff)); @@ -1383,6 +1382,29 @@ qla2x00_get_flash_manufacturer(scsi_qla_host_t *ha, uint8_t *man_id, qla2x00_write_flash_byte(ha, 0x5555, 0xf0); } +static void +qla2x00_read_flash_data(scsi_qla_host_t *ha, uint8_t *tmp_buf, uint32_t saddr, + uint32_t length) +{ + struct device_reg_2xxx __iomem *reg = &ha->iobase->isp; + uint32_t midpoint, ilength; + uint8_t data; + + midpoint = length / 2; + + WRT_REG_WORD(®->nvram, 0); + RD_REG_WORD(®->nvram); + for (ilength = 0; ilength < length; saddr++, ilength++, tmp_buf++) { + if (ilength == midpoint) { + WRT_REG_WORD(®->nvram, NVR_SELECT); + RD_REG_WORD(®->nvram); + } + data = qla2x00_read_flash_byte(ha, saddr); + if (saddr % 100) + udelay(10); + *tmp_buf = data; + } +} static inline void qla2x00_suspend_hba(struct scsi_qla_host *ha) @@ -1722,3 +1744,327 @@ qla24xx_write_optrom_data(struct scsi_qla_host *ha, uint8_t *buf, return rval; } + +/** + * qla2x00_get_fcode_version() - Determine an FCODE image's version. + * @ha: HA context + * @pcids: Pointer to the FCODE PCI data structure + * + * The process of retrieving the FCODE version information is at best + * described as interesting. + * + * Within the first 100h bytes of the image an ASCII string is present + * which contains several pieces of information including the FCODE + * version. Unfortunately it seems the only reliable way to retrieve + * the version is by scanning for another sentinel within the string, + * the FCODE build date: + * + * ... 2.00.02 10/17/02 ... + * + * Returns QLA_SUCCESS on successful retrieval of version. + */ +static void +qla2x00_get_fcode_version(scsi_qla_host_t *ha, uint32_t pcids) +{ + int ret = QLA_FUNCTION_FAILED; + uint32_t istart, iend, iter, vend; + uint8_t do_next, rbyte, *vbyte; + + memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision)); + + /* Skip the PCI data structure. */ + istart = pcids + + ((qla2x00_read_flash_byte(ha, pcids + 0x0B) << 8) | + qla2x00_read_flash_byte(ha, pcids + 0x0A)); + iend = istart + 0x100; + do { + /* Scan for the sentinel date string...eeewww. */ + do_next = 0; + iter = istart; + while ((iter < iend) && !do_next) { + iter++; + if (qla2x00_read_flash_byte(ha, iter) == '/') { + if (qla2x00_read_flash_byte(ha, iter + 2) == + '/') + do_next++; + else if (qla2x00_read_flash_byte(ha, + iter + 3) == '/') + do_next++; + } + } + if (!do_next) + break; + + /* Backtrack to previous ' ' (space). */ + do_next = 0; + while ((iter > istart) && !do_next) { + iter--; + if (qla2x00_read_flash_byte(ha, iter) == ' ') + do_next++; + } + if (!do_next) + break; + + /* + * Mark end of version tag, and find previous ' ' (space) or + * string length (recent FCODE images -- major hack ahead!!!). + */ + vend = iter - 1; + do_next = 0; + while ((iter > istart) && !do_next) { + iter--; + rbyte = qla2x00_read_flash_byte(ha, iter); + if (rbyte == ' ' || rbyte == 0xd || rbyte == 0x10) + do_next++; + } + if (!do_next) + break; + + /* Mark beginning of version tag, and copy data. */ + iter++; + if ((vend - iter) && + ((vend - iter) < sizeof(ha->fcode_revision))) { + vbyte = ha->fcode_revision; + while (iter <= vend) { + *vbyte++ = qla2x00_read_flash_byte(ha, iter); + iter++; + } + ret = QLA_SUCCESS; + } + } while (0); + + if (ret != QLA_SUCCESS) + memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision)); +} + +int +qla2x00_get_flash_version(scsi_qla_host_t *ha, void *mbuf) +{ + int ret = QLA_SUCCESS; + uint8_t code_type, last_image; + uint32_t pcihdr, pcids; + uint8_t *dbyte; + uint16_t *dcode; + + if (!ha->pio_address || !mbuf) + return QLA_FUNCTION_FAILED; + + memset(ha->bios_revision, 0, sizeof(ha->bios_revision)); + memset(ha->efi_revision, 0, sizeof(ha->efi_revision)); + memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision)); + memset(ha->fw_revision, 0, sizeof(ha->fw_revision)); + + qla2x00_flash_enable(ha); + + /* Begin with first PCI expansion ROM header. */ + pcihdr = 0; + last_image = 1; + do { + /* Verify PCI expansion ROM header. */ + if (qla2x00_read_flash_byte(ha, pcihdr) != 0x55 || + qla2x00_read_flash_byte(ha, pcihdr + 0x01) != 0xaa) { + /* No signature */ + DEBUG2(printk("scsi(%ld): No matching ROM " + "signature.\n", ha->host_no)); + ret = QLA_FUNCTION_FAILED; + break; + } + + /* Locate PCI data structure. */ + pcids = pcihdr + + ((qla2x00_read_flash_byte(ha, pcihdr + 0x19) << 8) | + qla2x00_read_flash_byte(ha, pcihdr + 0x18)); + + /* Validate signature of PCI data structure. */ + if (qla2x00_read_flash_byte(ha, pcids) != 'P' || + qla2x00_read_flash_byte(ha, pcids + 0x1) != 'C' || + qla2x00_read_flash_byte(ha, pcids + 0x2) != 'I' || + qla2x00_read_flash_byte(ha, pcids + 0x3) != 'R') { + /* Incorrect header. */ + DEBUG2(printk("%s(): PCI data struct not found " + "pcir_adr=%x.\n", __func__, pcids)); + ret = QLA_FUNCTION_FAILED; + break; + } + + /* Read version */ + code_type = qla2x00_read_flash_byte(ha, pcids + 0x14); + switch (code_type) { + case ROM_CODE_TYPE_BIOS: + /* Intel x86, PC-AT compatible. */ + ha->bios_revision[0] = + qla2x00_read_flash_byte(ha, pcids + 0x12); + ha->bios_revision[1] = + qla2x00_read_flash_byte(ha, pcids + 0x13); + DEBUG3(printk("%s(): read BIOS %d.%d.\n", __func__, + ha->bios_revision[1], ha->bios_revision[0])); + break; + case ROM_CODE_TYPE_FCODE: + /* Open Firmware standard for PCI (FCode). */ + /* Eeeewww... */ + qla2x00_get_fcode_version(ha, pcids); + break; + case ROM_CODE_TYPE_EFI: + /* Extensible Firmware Interface (EFI). */ + ha->efi_revision[0] = + qla2x00_read_flash_byte(ha, pcids + 0x12); + ha->efi_revision[1] = + qla2x00_read_flash_byte(ha, pcids + 0x13); + DEBUG3(printk("%s(): read EFI %d.%d.\n", __func__, + ha->efi_revision[1], ha->efi_revision[0])); + break; + default: + DEBUG2(printk("%s(): Unrecognized code type %x at " + "pcids %x.\n", __func__, code_type, pcids)); + break; + } + + last_image = qla2x00_read_flash_byte(ha, pcids + 0x15) & BIT_7; + + /* Locate next PCI expansion ROM. */ + pcihdr += ((qla2x00_read_flash_byte(ha, pcids + 0x11) << 8) | + qla2x00_read_flash_byte(ha, pcids + 0x10)) * 512; + } while (!last_image); + + if (IS_QLA2322(ha)) { + /* Read firmware image information. */ + memset(ha->fw_revision, 0, sizeof(ha->fw_revision)); + dbyte = mbuf; + memset(dbyte, 0, 8); + dcode = (uint16_t *)dbyte; + + qla2x00_read_flash_data(ha, dbyte, FA_RISC_CODE_ADDR * 4 + 10, + 8); + DEBUG3(printk("%s(%ld): dumping fw ver from flash:\n", + __func__, ha->host_no)); + DEBUG3(qla2x00_dump_buffer((uint8_t *)dbyte, 8)); + + if ((dcode[0] == 0xffff && dcode[1] == 0xffff && + dcode[2] == 0xffff && dcode[3] == 0xffff) || + (dcode[0] == 0 && dcode[1] == 0 && dcode[2] == 0 && + dcode[3] == 0)) { + DEBUG2(printk("%s(): Unrecognized fw revision at " + "%x.\n", __func__, FA_RISC_CODE_ADDR * 4)); + } else { + /* values are in big endian */ + ha->fw_revision[0] = dbyte[0] << 16 | dbyte[1]; + ha->fw_revision[1] = dbyte[2] << 16 | dbyte[3]; + ha->fw_revision[2] = dbyte[4] << 16 | dbyte[5]; + } + } + + qla2x00_flash_disable(ha); + + return ret; +} + +int +qla24xx_get_flash_version(scsi_qla_host_t *ha, void *mbuf) +{ + int ret = QLA_SUCCESS; + uint32_t pcihdr, pcids; + uint32_t *dcode; + uint8_t *bcode; + uint8_t code_type, last_image; + int i; + + if (!mbuf) + return QLA_FUNCTION_FAILED; + + memset(ha->bios_revision, 0, sizeof(ha->bios_revision)); + memset(ha->efi_revision, 0, sizeof(ha->efi_revision)); + memset(ha->fcode_revision, 0, sizeof(ha->fcode_revision)); + memset(ha->fw_revision, 0, sizeof(ha->fw_revision)); + + dcode = mbuf; + + /* Begin with first PCI expansion ROM header. */ + pcihdr = 0; + last_image = 1; + do { + /* Verify PCI expansion ROM header. */ + qla24xx_read_flash_data(ha, dcode, pcihdr >> 2, 0x20); + bcode = mbuf + (pcihdr % 4); + if (bcode[0x0] != 0x55 || bcode[0x1] != 0xaa) { + /* No signature */ + DEBUG2(printk("scsi(%ld): No matching ROM " + "signature.\n", ha->host_no)); + ret = QLA_FUNCTION_FAILED; + break; + } + + /* Locate PCI data structure. */ + pcids = pcihdr + ((bcode[0x19] << 8) | bcode[0x18]); + + qla24xx_read_flash_data(ha, dcode, pcids >> 2, 0x20); + bcode = mbuf + (pcihdr % 4); + + /* Validate signature of PCI data structure. */ + if (bcode[0x0] != 'P' || bcode[0x1] != 'C' || + bcode[0x2] != 'I' || bcode[0x3] != 'R') { + /* Incorrect header. */ + DEBUG2(printk("%s(): PCI data struct not found " + "pcir_adr=%x.\n", __func__, pcids)); + ret = QLA_FUNCTION_FAILED; + break; + } + + /* Read version */ + code_type = bcode[0x14]; + switch (code_type) { + case ROM_CODE_TYPE_BIOS: + /* Intel x86, PC-AT compatible. */ + ha->bios_revision[0] = bcode[0x12]; + ha->bios_revision[1] = bcode[0x13]; + DEBUG3(printk("%s(): read BIOS %d.%d.\n", __func__, + ha->bios_revision[1], ha->bios_revision[0])); + break; + case ROM_CODE_TYPE_FCODE: + /* Open Firmware standard for PCI (FCode). */ + ha->fcode_revision[0] = bcode[0x12]; + ha->fcode_revision[1] = bcode[0x13]; + DEBUG3(printk("%s(): read FCODE %d.%d.\n", __func__, + ha->fcode_revision[1], ha->fcode_revision[0])); + break; + case ROM_CODE_TYPE_EFI: + /* Extensible Firmware Interface (EFI). */ + ha->efi_revision[0] = bcode[0x12]; + ha->efi_revision[1] = bcode[0x13]; + DEBUG3(printk("%s(): read EFI %d.%d.\n", __func__, + ha->efi_revision[1], ha->efi_revision[0])); + break; + default: + DEBUG2(printk("%s(): Unrecognized code type %x at " + "pcids %x.\n", __func__, code_type, pcids)); + break; + } + + last_image = bcode[0x15] & BIT_7; + + /* Locate next PCI expansion ROM. */ + pcihdr += ((bcode[0x11] << 8) | bcode[0x10]) * 512; + } while (!last_image); + + /* Read firmware image information. */ + memset(ha->fw_revision, 0, sizeof(ha->fw_revision)); + dcode = mbuf; + + qla24xx_read_flash_data(ha, dcode, FA_RISC_CODE_ADDR + 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)) { + DEBUG2(printk("%s(): Unrecognized fw version at %x.\n", + __func__, FA_RISC_CODE_ADDR)); + } else { + ha->fw_revision[0] = dcode[0]; + ha->fw_revision[1] = dcode[1]; + ha->fw_revision[2] = dcode[2]; + ha->fw_revision[3] = dcode[3]; + } + + return ret; +} |