wcte13xp: wcxb: Add delayed reset firmware feature

Allow certain older firmwares to delay the hard reset until a full power cycle.
This way we can "preload" newer firmware images, without requiring the user to
physically power off/on their machine.

Signed-off-by: Russ Meyerriecks <rmeyerriecks@digium.com>
Acked-by: Shaun Ruffell <sruffell@digium.com>
This commit is contained in:
Russ Meyerriecks 2013-12-11 16:46:36 -06:00
parent 4cd09feb54
commit 3efa9d8cd1
5 changed files with 90 additions and 27 deletions

View File

@ -3909,7 +3909,7 @@ static int wcaxx_check_firmware(struct wcaxx *wc)
} }
return wcxb_check_firmware(&wc->xb, firmware_version, return wcxb_check_firmware(&wc->xb, firmware_version,
filename, force_firmware); filename, force_firmware, WCXB_RESET_NOW);
} }
static void wcaxx_check_sethook(struct wcaxx *wc, struct wcaxx_module *mod) static void wcaxx_check_sethook(struct wcaxx *wc, struct wcaxx_module *mod)

View File

@ -2431,6 +2431,26 @@ error_exit:
return serial; return serial;
} }
static int te13xp_check_firmware(struct t13x *wc)
{
const char *filename;
enum wcxb_reset_option reset;
if (is_pcie(wc))
filename = TE133_FW_FILENAME;
else
filename = TE134_FW_FILENAME;
/* Specific firmware requires power cycle to properly reset */
if (0x6f0017 == wcxb_get_firmware_version(&wc->xb))
reset = WCXB_RESET_LATER;
else
reset = WCXB_RESET_NOW;
return wcxb_check_firmware(&wc->xb, TE13X_FW_VERSION, filename,
force_firmware, reset);
}
static int __devinit te13xp_init_one(struct pci_dev *pdev, static int __devinit te13xp_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent) const struct pci_device_id *ent)
{ {
@ -2513,18 +2533,9 @@ static int __devinit te13xp_init_one(struct pci_dev *pdev,
if (res) if (res)
goto fail_exit; goto fail_exit;
/* Check for field updatable firmware */ res = te13xp_check_firmware(wc);
if (is_pcie(wc)) { if (res)
res = wcxb_check_firmware(&wc->xb, TE13X_FW_VERSION, goto fail_exit;
TE133_FW_FILENAME, force_firmware);
if (res)
goto fail_exit;
} else {
res = wcxb_check_firmware(&wc->xb, TE13X_FW_VERSION,
TE134_FW_FILENAME, force_firmware);
if (res)
goto fail_exit;
}
wc->ddev->hardware_id = t13x_read_serial(wc); wc->ddev->hardware_id = t13x_read_serial(wc);
if (!wc->ddev->hardware_id) { if (!wc->ddev->hardware_id) {

View File

@ -3357,7 +3357,7 @@ static int __devinit t43x_init_one(struct pci_dev *pdev,
/* Check for field updatable firmware */ /* Check for field updatable firmware */
res = wcxb_check_firmware(&wc->xb, TE435_VERSION, res = wcxb_check_firmware(&wc->xb, TE435_VERSION,
TE435_FW_FILENAME, force_firmware); TE435_FW_FILENAME, force_firmware, WCXB_RESET_NOW);
if (res) if (res)
goto fail_exit; goto fail_exit;

View File

@ -792,7 +792,7 @@ struct wcxb_firm_header {
__le32 version; __le32 version;
} __packed; } __packed;
static u32 wcxb_get_firmware_version(struct wcxb *xb) u32 wcxb_get_firmware_version(struct wcxb *xb)
{ {
u32 version = 0; u32 version = 0;
@ -807,7 +807,8 @@ static u32 wcxb_get_firmware_version(struct wcxb *xb)
} }
static int wcxb_update_firmware(struct wcxb *xb, const struct firmware *fw, static int wcxb_update_firmware(struct wcxb *xb, const struct firmware *fw,
const char *filename) const char *filename,
enum wcxb_reset_option reset)
{ {
u32 tdm_control; u32 tdm_control;
static const int APPLICATION_ADDRESS = 0x200000; static const int APPLICATION_ADDRESS = 0x200000;
@ -859,14 +860,20 @@ static int wcxb_update_firmware(struct wcxb *xb, const struct firmware *fw,
APPLICATION_ADDRESS + META_BLOCK_OFFSET, APPLICATION_ADDRESS + META_BLOCK_OFFSET,
&meta, sizeof(meta)); &meta, sizeof(meta));
/* Reset fpga after loading firmware */ if (WCXB_RESET_NOW == reset) {
dev_info(&xb->pdev->dev, "Firmware load complete. Reseting device.\n"); /* Reset fpga after loading firmware */
tdm_control = ioread32be(xb->membase + TDM_CONTROL); dev_info(&xb->pdev->dev,
"Firmware load complete. Reseting device.\n");
tdm_control = ioread32be(xb->membase + TDM_CONTROL);
wcxb_hard_reset(xb); wcxb_hard_reset(xb);
iowrite32be(0, xb->membase + 0x04); iowrite32be(0, xb->membase + 0x04);
iowrite32be(tdm_control, xb->membase + TDM_CONTROL); iowrite32be(tdm_control, xb->membase + TDM_CONTROL);
} else {
dev_info(&xb->pdev->dev,
"Delaying reset. Firmware load requires a power cycle\n");
}
wcxb_spi_device_destroy(flash_spi_device); wcxb_spi_device_destroy(flash_spi_device);
wcxb_spi_master_destroy(flash_spi_master); wcxb_spi_master_destroy(flash_spi_master);
@ -874,10 +881,16 @@ static int wcxb_update_firmware(struct wcxb *xb, const struct firmware *fw,
} }
int wcxb_check_firmware(struct wcxb *xb, const u32 expected_version, int wcxb_check_firmware(struct wcxb *xb, const u32 expected_version,
const char *firmware_filename, bool force_firmware) const char *firmware_filename, bool force_firmware,
enum wcxb_reset_option reset)
{ {
const struct firmware *fw; const struct firmware *fw;
const struct wcxb_firm_header *header; const struct wcxb_firm_header *header;
static const int APPLICATION_ADDRESS = 0x200000;
static const int META_BLOCK_OFFSET = 0x170000;
struct wcxb_spi_master *flash_spi_master;
struct wcxb_spi_device *flash_spi_device;
struct wcxb_meta_block meta;
int res = 0; int res = 0;
u32 crc; u32 crc;
u32 version = 0; u32 version = 0;
@ -896,6 +909,27 @@ int wcxb_check_firmware(struct wcxb *xb, const u32 expected_version,
return 0; return 0;
} }
/* Check meta firmware version for a not-booted application image */
flash_spi_master = wcxb_spi_master_create(&xb->pdev->dev,
xb->membase + FLASH_SPI_BASE,
false);
flash_spi_device = wcxb_spi_device_create(flash_spi_master, 0);
res = wcxb_flash_read(flash_spi_device,
APPLICATION_ADDRESS + META_BLOCK_OFFSET,
&meta, sizeof(meta));
if (res) {
dev_info(&xb->pdev->dev, "Unable to read flash\n");
return -EIO;
}
if ((meta.version == cpu_to_le32(expected_version))
&& !force_firmware) {
dev_info(&xb->pdev->dev,
"Detected previous firmware updated to current version %x, but not running. You likely need to power cycle your system.\n",
expected_version);
return 0;
}
if (force_firmware) { if (force_firmware) {
dev_info(&xb->pdev->dev, dev_info(&xb->pdev->dev,
"force_firmware module parameter is set. Forcing firmware load, regardless of version\n"); "force_firmware module parameter is set. Forcing firmware load, regardless of version\n");
@ -938,12 +972,22 @@ int wcxb_check_firmware(struct wcxb *xb, const u32 expected_version,
dev_info(&xb->pdev->dev, "Found %s (version: %x) Preparing for flash\n", dev_info(&xb->pdev->dev, "Found %s (version: %x) Preparing for flash\n",
firmware_filename, header->version); firmware_filename, header->version);
res = wcxb_update_firmware(xb, fw, firmware_filename); res = wcxb_update_firmware(xb, fw, firmware_filename, reset);
version = wcxb_get_firmware_version(xb); version = wcxb_get_firmware_version(xb);
dev_info(&xb->pdev->dev, "Reset into firmware version: %x\n", version); if (WCXB_RESET_NOW == reset) {
dev_info(&xb->pdev->dev,
"Reset into firmware version: %x\n", version);
} else {
dev_info(&xb->pdev->dev,
"Running firmware version: %x\n", version);
dev_info(&xb->pdev->dev,
"Loaded firmware version: %x (Will load after next power cycle)\n",
header->version);
}
if ((expected_version != version) && !force_firmware) { if ((WCXB_RESET_NOW == reset) && (expected_version != version)
&& !force_firmware) {
/* On the off chance that the interface is in a state where it /* On the off chance that the interface is in a state where it
* cannot boot into the updated firmware image, power cycling * cannot boot into the updated firmware image, power cycling
* the card can recover. A simple "reset" of the computer is not * the card can recover. A simple "reset" of the computer is not

View File

@ -105,9 +105,17 @@ static inline void wcxb_disable_timing_header_driver(struct wcxb *xb)
xb->flags.drive_timing_cable = 0; xb->flags.drive_timing_cable = 0;
} }
enum wcxb_reset_option {
WCXB_RESET_NOW,
WCXB_RESET_LATER
};
extern u32 wcxb_get_firmware_version(struct wcxb *xb);
extern int wcxb_check_firmware(struct wcxb *xb, const u32 expected_version, extern int wcxb_check_firmware(struct wcxb *xb, const u32 expected_version,
const char *firmware_filename, const char *firmware_filename,
bool force_firmware); bool force_firmware,
enum wcxb_reset_option reset);
extern void wcxb_stop_dma(struct wcxb *xb); extern void wcxb_stop_dma(struct wcxb *xb);
extern void wcxb_disable_interrupts(struct wcxb *xb); extern void wcxb_disable_interrupts(struct wcxb *xb);