Skip to content

Commit

Permalink
csiostor:firmware upgrade fix
Browse files Browse the repository at this point in the history
This patch fixes removes older means of upgrading Firmware using MAJOR version
and adds newer interface version checking mechanism.

Please apply this patch on net-next since it depends on previous commits.

Signed-off-by: Praveen Madhavan <praveenm@chelsio.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
  • Loading branch information
Praveen Madhavan authored and David S. Miller committed Jan 9, 2015
1 parent 3552c31 commit f40e74f
Show file tree
Hide file tree
Showing 3 changed files with 247 additions and 82 deletions.
277 changes: 201 additions & 76 deletions drivers/scsi/csiostor/csio_hw.c
Original file line number Diff line number Diff line change
Expand Up @@ -681,43 +681,6 @@ csio_hw_get_tp_version(struct csio_hw *hw, u32 *vers)
vers, 0);
}

/*
* csio_hw_check_fw_version - check if the FW is compatible with
* this driver
* @hw: HW module
*
* Checks if an adapter's FW is compatible with the driver. Returns 0
* if there's exact match, a negative error if the version could not be
* read or there's a major/minor version mismatch/minor.
*/
static int
csio_hw_check_fw_version(struct csio_hw *hw)
{
int ret, major, minor, micro;

ret = csio_hw_get_fw_version(hw, &hw->fwrev);
if (!ret)
ret = csio_hw_get_tp_version(hw, &hw->tp_vers);
if (ret)
return ret;

major = FW_HDR_FW_VER_MAJOR_G(hw->fwrev);
minor = FW_HDR_FW_VER_MINOR_G(hw->fwrev);
micro = FW_HDR_FW_VER_MICRO_G(hw->fwrev);

if (major != FW_VERSION_MAJOR(hw)) { /* major mismatch - fail */
csio_err(hw, "card FW has major version %u, driver wants %u\n",
major, FW_VERSION_MAJOR(hw));
return -EINVAL;
}

if (minor == FW_VERSION_MINOR(hw) && micro == FW_VERSION_MICRO(hw))
return 0; /* perfect match */

/* Minor/micro version mismatch */
return -EINVAL;
}

/*
* csio_hw_fw_dload - download firmware.
* @hw: HW module
Expand Down Expand Up @@ -1967,55 +1930,224 @@ csio_hw_no_fwconfig(struct csio_hw *hw, int reset)
return rv;
}

/* Is the given firmware API compatible with the one the driver was compiled
* with?
*/
static int fw_compatible(const struct fw_hdr *hdr1, const struct fw_hdr *hdr2)
{

/* short circuit if it's the exact same firmware version */
if (hdr1->chip == hdr2->chip && hdr1->fw_ver == hdr2->fw_ver)
return 1;

#define SAME_INTF(x) (hdr1->intfver_##x == hdr2->intfver_##x)
if (hdr1->chip == hdr2->chip && SAME_INTF(nic) && SAME_INTF(vnic) &&
SAME_INTF(ri) && SAME_INTF(iscsi) && SAME_INTF(fcoe))
return 1;
#undef SAME_INTF

return 0;
}

/* The firmware in the filesystem is usable, but should it be installed?
* This routine explains itself in detail if it indicates the filesystem
* firmware should be installed.
*/
static int csio_should_install_fs_fw(struct csio_hw *hw, int card_fw_usable,
int k, int c)
{
const char *reason;

if (!card_fw_usable) {
reason = "incompatible or unusable";
goto install;
}

if (k > c) {
reason = "older than the version supported with this driver";
goto install;
}

return 0;

install:
csio_err(hw, "firmware on card (%u.%u.%u.%u) is %s, "
"installing firmware %u.%u.%u.%u on card.\n",
FW_HDR_FW_VER_MAJOR_G(c), FW_HDR_FW_VER_MINOR_G(c),
FW_HDR_FW_VER_MICRO_G(c), FW_HDR_FW_VER_BUILD_G(c), reason,
FW_HDR_FW_VER_MAJOR_G(k), FW_HDR_FW_VER_MINOR_G(k),
FW_HDR_FW_VER_MICRO_G(k), FW_HDR_FW_VER_BUILD_G(k));

return 1;
}

static struct fw_info fw_info_array[] = {
{
.chip = CHELSIO_T5,
.fs_name = FW_CFG_NAME_T5,
.fw_mod_name = FW_FNAME_T5,
.fw_hdr = {
.chip = FW_HDR_CHIP_T5,
.fw_ver = __cpu_to_be32(FW_VERSION(T5)),
.intfver_nic = FW_INTFVER(T5, NIC),
.intfver_vnic = FW_INTFVER(T5, VNIC),
.intfver_ri = FW_INTFVER(T5, RI),
.intfver_iscsi = FW_INTFVER(T5, ISCSI),
.intfver_fcoe = FW_INTFVER(T5, FCOE),
},
}
};

static struct fw_info *find_fw_info(int chip)
{
int i;

for (i = 0; i < ARRAY_SIZE(fw_info_array); i++) {
if (fw_info_array[i].chip == chip)
return &fw_info_array[i];
}
return NULL;
}

int csio_hw_prep_fw(struct csio_hw *hw, struct fw_info *fw_info,
const u8 *fw_data, unsigned int fw_size,
struct fw_hdr *card_fw, enum csio_dev_state state,
int *reset)
{
int ret, card_fw_usable, fs_fw_usable;
const struct fw_hdr *fs_fw;
const struct fw_hdr *drv_fw;

drv_fw = &fw_info->fw_hdr;

/* Read the header of the firmware on the card */
ret = csio_hw_read_flash(hw, FLASH_FW_START,
sizeof(*card_fw) / sizeof(uint32_t),
(uint32_t *)card_fw, 1);
if (ret == 0) {
card_fw_usable = fw_compatible(drv_fw, (const void *)card_fw);
} else {
csio_err(hw,
"Unable to read card's firmware header: %d\n", ret);
card_fw_usable = 0;
}

if (fw_data != NULL) {
fs_fw = (const void *)fw_data;
fs_fw_usable = fw_compatible(drv_fw, fs_fw);
} else {
fs_fw = NULL;
fs_fw_usable = 0;
}

if (card_fw_usable && card_fw->fw_ver == drv_fw->fw_ver &&
(!fs_fw_usable || fs_fw->fw_ver == drv_fw->fw_ver)) {
/* Common case: the firmware on the card is an exact match and
* the filesystem one is an exact match too, or the filesystem
* one is absent/incompatible.
*/
} else if (fs_fw_usable && state == CSIO_DEV_STATE_UNINIT &&
csio_should_install_fs_fw(hw, card_fw_usable,
be32_to_cpu(fs_fw->fw_ver),
be32_to_cpu(card_fw->fw_ver))) {
ret = csio_hw_fw_upgrade(hw, hw->pfn, fw_data,
fw_size, 0);
if (ret != 0) {
csio_err(hw,
"failed to install firmware: %d\n", ret);
goto bye;
}

/* Installed successfully, update the cached header too. */
memcpy(card_fw, fs_fw, sizeof(*card_fw));
card_fw_usable = 1;
*reset = 0; /* already reset as part of load_fw */
}

if (!card_fw_usable) {
uint32_t d, c, k;

d = be32_to_cpu(drv_fw->fw_ver);
c = be32_to_cpu(card_fw->fw_ver);
k = fs_fw ? be32_to_cpu(fs_fw->fw_ver) : 0;

csio_err(hw, "Cannot find a usable firmware: "
"chip state %d, "
"driver compiled with %d.%d.%d.%d, "
"card has %d.%d.%d.%d, filesystem has %d.%d.%d.%d\n",
state,
FW_HDR_FW_VER_MAJOR_G(d), FW_HDR_FW_VER_MINOR_G(d),
FW_HDR_FW_VER_MICRO_G(d), FW_HDR_FW_VER_BUILD_G(d),
FW_HDR_FW_VER_MAJOR_G(c), FW_HDR_FW_VER_MINOR_G(c),
FW_HDR_FW_VER_MICRO_G(c), FW_HDR_FW_VER_BUILD_G(c),
FW_HDR_FW_VER_MAJOR_G(k), FW_HDR_FW_VER_MINOR_G(k),
FW_HDR_FW_VER_MICRO_G(k), FW_HDR_FW_VER_BUILD_G(k));
ret = EINVAL;
goto bye;
}

/* We're using whatever's on the card and it's known to be good. */
hw->fwrev = be32_to_cpu(card_fw->fw_ver);
hw->tp_vers = be32_to_cpu(card_fw->tp_microcode_ver);

bye:
return ret;
}

/*
* Returns -EINVAL if attempts to flash the firmware failed
* else returns 0,
* if flashing was not attempted because the card had the
* latest firmware ECANCELED is returned
*/
static int
csio_hw_flash_fw(struct csio_hw *hw)
csio_hw_flash_fw(struct csio_hw *hw, int *reset)
{
int ret = -ECANCELED;
const struct firmware *fw;
const struct fw_hdr *hdr;
u32 fw_ver;
struct fw_info *fw_info;
struct fw_hdr *card_fw;
struct pci_dev *pci_dev = hw->pdev;
struct device *dev = &pci_dev->dev ;
const u8 *fw_data = NULL;
unsigned int fw_size = 0;

/* This is the firmware whose headers the driver was compiled
* against
*/
fw_info = find_fw_info(CHELSIO_CHIP_VERSION(hw->chip_id));
if (fw_info == NULL) {
csio_err(hw,
"unable to get firmware info for chip %d.\n",
CHELSIO_CHIP_VERSION(hw->chip_id));
return -EINVAL;
}

if (request_firmware(&fw, CSIO_FW_FNAME(hw), dev) < 0) {
csio_err(hw, "could not find firmware image %s, err: %d\n",
CSIO_FW_FNAME(hw), ret);
return -EINVAL;
}

hdr = (const struct fw_hdr *)fw->data;
fw_ver = ntohl(hdr->fw_ver);
if (FW_HDR_FW_VER_MAJOR_G(fw_ver) != FW_VERSION_MAJOR(hw))
return -EINVAL; /* wrong major version, won't do */

/*
* If the flash FW is unusable or we found something newer, load it.
/* allocate memory to read the header of the firmware on the
* card
*/
if (FW_HDR_FW_VER_MAJOR_G(hw->fwrev) != FW_VERSION_MAJOR(hw) ||
fw_ver > hw->fwrev) {
ret = csio_hw_fw_upgrade(hw, hw->pfn, fw->data, fw->size,
/*force=*/false);
if (!ret)
csio_info(hw,
"firmware upgraded to version %pI4 from %s\n",
&hdr->fw_ver, CSIO_FW_FNAME(hw));
else
csio_err(hw, "firmware upgrade failed! err=%d\n", ret);
} else
ret = -EINVAL;
card_fw = kmalloc(sizeof(*card_fw), GFP_KERNEL);

release_firmware(fw);
fw_data = fw->data;
fw_size = fw->size;

/* upgrade FW logic */
ret = csio_hw_prep_fw(hw, fw_info, fw_data, fw_size, card_fw,
hw->fw_state, reset);

/* Cleaning up */
if (fw != NULL)
release_firmware(fw);
kfree(card_fw);
return ret;
}


/*
* csio_hw_configure - Configure HW
* @hw - HW module
Expand Down Expand Up @@ -2071,25 +2203,18 @@ csio_hw_configure(struct csio_hw *hw)
if (rv != 0)
goto out;

csio_hw_get_fw_version(hw, &hw->fwrev);
csio_hw_get_tp_version(hw, &hw->tp_vers);
if (csio_is_hw_master(hw) && hw->fw_state != CSIO_DEV_STATE_INIT) {
rv = csio_hw_check_fw_version(hw);
if (rv == -EINVAL) {

/* Do firmware update */
spin_unlock_irq(&hw->lock);
rv = csio_hw_flash_fw(hw);
spin_lock_irq(&hw->lock);
spin_unlock_irq(&hw->lock);
rv = csio_hw_flash_fw(hw, &reset);
spin_lock_irq(&hw->lock);

if (rv != 0)
goto out;

if (rv == 0) {
reset = 0;
/*
* Note that the chip was reset as part of the
* firmware upgrade so we don't reset it again
* below and grab the new firmware version.
*/
rv = csio_hw_check_fw_version(hw);
}
}
/*
* If the firmware doesn't support Configuration
* Files, use the old Driver-based, hard-wired
Expand Down
5 changes: 2 additions & 3 deletions drivers/scsi/csiostor/csio_hw.h
Original file line number Diff line number Diff line change
Expand Up @@ -201,9 +201,8 @@ enum {
SF_ERASE_SECTOR = 0xd8, /* erase sector */

FW_START_SEC = 8, /* first flash sector for FW */
FW_END_SEC = 15, /* last flash sector for FW */
FW_IMG_START = FW_START_SEC * SF_SEC_SIZE,
FW_MAX_SIZE = (FW_END_SEC - FW_START_SEC + 1) * SF_SEC_SIZE,
FW_MAX_SIZE = 16 * SF_SEC_SIZE,

FLASH_CFG_MAX_SIZE = 0x10000 , /* max size of the flash config file*/
FLASH_CFG_OFFSET = 0x1f0000,
Expand All @@ -221,7 +220,7 @@ enum {
* Location of firmware image in FLASH.
*/
FLASH_FW_START_SEC = 8,
FLASH_FW_NSECS = 8,
FLASH_FW_NSECS = 16,
FLASH_FW_START = FLASH_START(FLASH_FW_START_SEC),
FLASH_FW_MAX_SIZE = FLASH_MAX_SIZE(FLASH_FW_NSECS),

Expand Down
Loading

0 comments on commit f40e74f

Please sign in to comment.