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:
parent
4cd09feb54
commit
3efa9d8cd1
@ -3909,7 +3909,7 @@ static int wcaxx_check_firmware(struct wcaxx *wc)
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -2431,6 +2431,26 @@ error_exit:
|
||||
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,
|
||||
const struct pci_device_id *ent)
|
||||
{
|
||||
@ -2513,18 +2533,9 @@ static int __devinit te13xp_init_one(struct pci_dev *pdev,
|
||||
if (res)
|
||||
goto fail_exit;
|
||||
|
||||
/* Check for field updatable firmware */
|
||||
if (is_pcie(wc)) {
|
||||
res = wcxb_check_firmware(&wc->xb, TE13X_FW_VERSION,
|
||||
TE133_FW_FILENAME, force_firmware);
|
||||
res = te13xp_check_firmware(wc);
|
||||
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);
|
||||
if (!wc->ddev->hardware_id) {
|
||||
|
@ -3357,7 +3357,7 @@ static int __devinit t43x_init_one(struct pci_dev *pdev,
|
||||
|
||||
/* Check for field updatable firmware */
|
||||
res = wcxb_check_firmware(&wc->xb, TE435_VERSION,
|
||||
TE435_FW_FILENAME, force_firmware);
|
||||
TE435_FW_FILENAME, force_firmware, WCXB_RESET_NOW);
|
||||
if (res)
|
||||
goto fail_exit;
|
||||
|
||||
|
@ -792,7 +792,7 @@ struct wcxb_firm_header {
|
||||
__le32 version;
|
||||
} __packed;
|
||||
|
||||
static u32 wcxb_get_firmware_version(struct wcxb *xb)
|
||||
u32 wcxb_get_firmware_version(struct wcxb *xb)
|
||||
{
|
||||
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,
|
||||
const char *filename)
|
||||
const char *filename,
|
||||
enum wcxb_reset_option reset)
|
||||
{
|
||||
u32 tdm_control;
|
||||
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,
|
||||
&meta, sizeof(meta));
|
||||
|
||||
if (WCXB_RESET_NOW == reset) {
|
||||
/* Reset fpga after loading firmware */
|
||||
dev_info(&xb->pdev->dev, "Firmware load complete. Reseting device.\n");
|
||||
dev_info(&xb->pdev->dev,
|
||||
"Firmware load complete. Reseting device.\n");
|
||||
tdm_control = ioread32be(xb->membase + TDM_CONTROL);
|
||||
|
||||
wcxb_hard_reset(xb);
|
||||
|
||||
iowrite32be(0, xb->membase + 0x04);
|
||||
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_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,
|
||||
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 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;
|
||||
u32 crc;
|
||||
u32 version = 0;
|
||||
@ -896,6 +909,27 @@ int wcxb_check_firmware(struct wcxb *xb, const u32 expected_version,
|
||||
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) {
|
||||
dev_info(&xb->pdev->dev,
|
||||
"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",
|
||||
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);
|
||||
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
|
||||
* cannot boot into the updated firmware image, power cycling
|
||||
* the card can recover. A simple "reset" of the computer is not
|
||||
|
@ -105,9 +105,17 @@ static inline void wcxb_disable_timing_header_driver(struct wcxb *xb)
|
||||
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,
|
||||
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_disable_interrupts(struct wcxb *xb);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user