[SCSI] qla2xxx: Support for loading Unified ROM Image (URI) format firmware file.

Used bootloder address from FLT while loading FW from flash as well.

Signed-off-by: Giridhar Malavali <giridhar.malavali@qlogic.com>
Signed-off-by: James Bottomley <James.Bottomley@suse.de>
This commit is contained in:
Harish Zunjarrao 2010-05-28 15:08:23 -07:00 committed by James Bottomley
parent b0cd579cde
commit 9c2b297572
3 changed files with 215 additions and 7 deletions

View file

@ -2788,6 +2788,9 @@ struct qla_hw_data {
uint16_t gbl_dsd_avail;
struct list_head gbl_dsd_list;
#define NUM_DSD_CHAIN 4096
uint8_t fw_type;
__le32 file_prd_off; /* File firmware product offset */
};
/*

View file

@ -1407,7 +1407,8 @@ qla82xx_fw_load_from_flash(struct qla_hw_data *ha)
{
int i;
long size = 0;
long flashaddr = BOOTLD_START, memaddr = BOOTLD_START;
long flashaddr = ha->flt_region_bootload << 2;
long memaddr = BOOTLD_START;
u64 data;
u32 high, low;
size = (IMAGE_START - BOOTLD_START) / 8;
@ -1677,6 +1678,94 @@ qla82xx_pci_mem_write_2M(struct qla_hw_data *ha,
return ret;
}
static struct qla82xx_uri_table_desc *
qla82xx_get_table_desc(const u8 *unirom, int section)
{
uint32_t i;
struct qla82xx_uri_table_desc *directory =
(struct qla82xx_uri_table_desc *)&unirom[0];
__le32 offset;
__le32 tab_type;
__le32 entries = cpu_to_le32(directory->num_entries);
for (i = 0; i < entries; i++) {
offset = cpu_to_le32(directory->findex) +
(i * cpu_to_le32(directory->entry_size));
tab_type = cpu_to_le32(*((u32 *)&unirom[offset] + 8));
if (tab_type == section)
return (struct qla82xx_uri_table_desc *)&unirom[offset];
}
return NULL;
}
static struct qla82xx_uri_data_desc *
qla82xx_get_data_desc(struct qla_hw_data *ha,
u32 section, u32 idx_offset)
{
const u8 *unirom = ha->hablob->fw->data;
int idx = cpu_to_le32(*((int *)&unirom[ha->file_prd_off] + idx_offset));
struct qla82xx_uri_table_desc *tab_desc = NULL;
__le32 offset;
tab_desc = qla82xx_get_table_desc(unirom, section);
if (!tab_desc)
return NULL;
offset = cpu_to_le32(tab_desc->findex) +
(cpu_to_le32(tab_desc->entry_size) * idx);
return (struct qla82xx_uri_data_desc *)&unirom[offset];
}
static u8 *
qla82xx_get_bootld_offset(struct qla_hw_data *ha)
{
u32 offset = BOOTLD_START;
struct qla82xx_uri_data_desc *uri_desc = NULL;
if (ha->fw_type == QLA82XX_UNIFIED_ROMIMAGE) {
uri_desc = qla82xx_get_data_desc(ha,
QLA82XX_URI_DIR_SECT_BOOTLD, QLA82XX_URI_BOOTLD_IDX_OFF);
if (uri_desc)
offset = cpu_to_le32(uri_desc->findex);
}
return (u8 *)&ha->hablob->fw->data[offset];
}
static __le32
qla82xx_get_fw_size(struct qla_hw_data *ha)
{
struct qla82xx_uri_data_desc *uri_desc = NULL;
if (ha->fw_type == QLA82XX_UNIFIED_ROMIMAGE) {
uri_desc = qla82xx_get_data_desc(ha, QLA82XX_URI_DIR_SECT_FW,
QLA82XX_URI_FIRMWARE_IDX_OFF);
if (uri_desc)
return cpu_to_le32(uri_desc->size);
}
return cpu_to_le32(*(u32 *)&ha->hablob->fw->data[FW_SIZE_OFFSET]);
}
static u8 *
qla82xx_get_fw_offs(struct qla_hw_data *ha)
{
u32 offset = IMAGE_START;
struct qla82xx_uri_data_desc *uri_desc = NULL;
if (ha->fw_type == QLA82XX_UNIFIED_ROMIMAGE) {
uri_desc = qla82xx_get_data_desc(ha, QLA82XX_URI_DIR_SECT_FW,
QLA82XX_URI_FIRMWARE_IDX_OFF);
if (uri_desc)
offset = cpu_to_le32(uri_desc->findex);
}
return (u8 *)&ha->hablob->fw->data[offset];
}
/* PCI related functions */
char *
qla82xx_pci_info_str(struct scsi_qla_host *vha, char *str)
@ -1878,19 +1967,19 @@ int qla82xx_fw_load_from_blob(struct qla_hw_data *ha)
size = (IMAGE_START - BOOTLD_START) / 8;
ptr64 = (u64 *)&ha->hablob->fw->data[BOOTLD_START];
ptr64 = (u64 *)qla82xx_get_bootld_offset(ha);
flashaddr = BOOTLD_START;
for (i = 0; i < size; i++) {
data = cpu_to_le64(ptr64[i]);
qla82xx_pci_mem_write_2M(ha, flashaddr, &data, 8);
if (qla82xx_pci_mem_write_2M(ha, flashaddr, &data, 8))
return -EIO;
flashaddr += 8;
}
size = *(u32 *)&ha->hablob->fw->data[FW_SIZE_OFFSET];
size = (__force u32)cpu_to_le32(size) / 8;
ptr64 = (u64 *)&ha->hablob->fw->data[IMAGE_START];
flashaddr = FLASH_ADDR_START;
size = (__force u32)qla82xx_get_fw_size(ha) / 8;
ptr64 = (u64 *)qla82xx_get_fw_offs(ha);
for (i = 0; i < size; i++) {
data = cpu_to_le64(ptr64[i]);
@ -1899,19 +1988,88 @@ int qla82xx_fw_load_from_blob(struct qla_hw_data *ha)
return -EIO;
flashaddr += 8;
}
udelay(100);
/* Write a magic value to CAMRAM register
* at a specified offset to indicate
* that all data is written and
* ready for firmware to initialize.
*/
qla82xx_wr_32(ha, QLA82XX_CAM_RAM(0x1fc), 0x12345678);
qla82xx_wr_32(ha, QLA82XX_CAM_RAM(0x1fc), QLA82XX_BDINFO_MAGIC);
read_lock(&ha->hw_lock);
if (QLA82XX_IS_REVISION_P3PLUS(ha->chip_revision)) {
qla82xx_wr_32(ha, QLA82XX_CRB_PEG_NET_0 + 0x18, 0x1020);
qla82xx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, 0x80001e);
} else
qla82xx_wr_32(ha, QLA82XX_ROMUSB_GLB_SW_RESET, 0x80001d);
read_unlock(&ha->hw_lock);
return 0;
}
static int
qla82xx_set_product_offset(struct qla_hw_data *ha)
{
struct qla82xx_uri_table_desc *ptab_desc = NULL;
const uint8_t *unirom = ha->hablob->fw->data;
uint32_t i;
__le32 entries;
__le32 flags, file_chiprev, offset;
uint8_t chiprev = ha->chip_revision;
/* Hardcoding mn_present flag for P3P */
int mn_present = 0;
uint32_t flagbit;
ptab_desc = qla82xx_get_table_desc(unirom,
QLA82XX_URI_DIR_SECT_PRODUCT_TBL);
if (!ptab_desc)
return -1;
entries = cpu_to_le32(ptab_desc->num_entries);
for (i = 0; i < entries; i++) {
offset = cpu_to_le32(ptab_desc->findex) +
(i * cpu_to_le32(ptab_desc->entry_size));
flags = cpu_to_le32(*((int *)&unirom[offset] +
QLA82XX_URI_FLAGS_OFF));
file_chiprev = cpu_to_le32(*((int *)&unirom[offset] +
QLA82XX_URI_CHIP_REV_OFF));
flagbit = mn_present ? 1 : 2;
if ((chiprev == file_chiprev) && ((1ULL << flagbit) & flags)) {
ha->file_prd_off = offset;
return 0;
}
}
return -1;
}
int
qla82xx_validate_firmware_blob(scsi_qla_host_t *vha, uint8_t fw_type)
{
__le32 val;
uint32_t min_size;
struct qla_hw_data *ha = vha->hw;
const struct firmware *fw = ha->hablob->fw;
ha->fw_type = fw_type;
if (fw_type == QLA82XX_UNIFIED_ROMIMAGE) {
if (qla82xx_set_product_offset(ha))
return -EINVAL;
min_size = QLA82XX_URI_FW_MIN_SIZE;
} else {
val = cpu_to_le32(*(u32 *)&fw->data[QLA82XX_FW_MAGIC_OFFSET]);
if ((__force u32)val != QLA82XX_BDINFO_MAGIC)
return -EINVAL;
min_size = QLA82XX_FW_MIN_SIZE;
}
if (fw->size < min_size)
return -EINVAL;
return 0;
}
@ -2470,6 +2628,18 @@ int qla82xx_load_fw(scsi_qla_host_t *vha)
goto fw_load_failed;
}
/* Validating firmware blob */
if (qla82xx_validate_firmware_blob(vha,
QLA82XX_FLASH_ROMIMAGE)) {
/* Fallback to URI format */
if (qla82xx_validate_firmware_blob(vha,
QLA82XX_UNIFIED_ROMIMAGE)) {
qla_printk(KERN_ERR, ha,
"No valid firmware image found!!!");
return QLA_FUNCTION_FAILED;
}
}
if (qla82xx_fw_load_from_blob(ha) == QLA_SUCCESS) {
qla_printk(KERN_ERR, ha,
"%s: Firmware loaded successfully "

View file

@ -773,13 +773,48 @@ struct qla82xx_legacy_intr_set {
.pci_int_reg = ISR_MSI_INT_TRIGGER(7) }, \
}
#define BRDCFG_START 0x4000
#define BOOTLD_START 0x10000
#define IMAGE_START 0x100000
#define FLASH_ADDR_START 0x43000
/* Magic number to let user know flash is programmed */
#define QLA82XX_BDINFO_MAGIC 0x12345678
#define QLA82XX_FW_MAGIC_OFFSET (BRDCFG_START + 0x128)
#define FW_SIZE_OFFSET (0x3e840c)
#define QLA82XX_FW_MIN_SIZE 0x3fffff
/* UNIFIED ROMIMAGE START */
#define QLA82XX_URI_FW_MIN_SIZE 0xc8000
#define QLA82XX_URI_DIR_SECT_PRODUCT_TBL 0x0
#define QLA82XX_URI_DIR_SECT_BOOTLD 0x6
#define QLA82XX_URI_DIR_SECT_FW 0x7
/* Offsets */
#define QLA82XX_URI_CHIP_REV_OFF 10
#define QLA82XX_URI_FLAGS_OFF 11
#define QLA82XX_URI_BIOS_VERSION_OFF 12
#define QLA82XX_URI_BOOTLD_IDX_OFF 27
#define QLA82XX_URI_FIRMWARE_IDX_OFF 29
struct qla82xx_uri_table_desc{
uint32_t findex;
uint32_t num_entries;
uint32_t entry_size;
uint32_t reserved[5];
};
struct qla82xx_uri_data_desc{
uint32_t findex;
uint32_t size;
uint32_t reserved[5];
};
/* UNIFIED ROMIMAGE END */
#define QLA82XX_UNIFIED_ROMIMAGE 3
#define QLA82XX_FLASH_ROMIMAGE 4
#define QLA82XX_UNKNOWN_ROMIMAGE 0xff
#define QLA82XX_IS_REVISION_P3PLUS(_rev_) ((_rev_) >= 0x50)
#define MIU_TEST_AGT_WRDATA_UPPER_LO (0x0b0)