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,
|
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)
|
||||||
|
@ -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) {
|
||||||
|
@ -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;
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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);
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user