From 3efa9d8cd1f65f1cf524a041cf68a87df44c1080 Mon Sep 17 00:00:00 2001 From: Russ Meyerriecks Date: Wed, 11 Dec 2013 16:46:36 -0600 Subject: [PATCH] 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 Acked-by: Shaun Ruffell --- drivers/dahdi/wcaxx-base.c | 2 +- drivers/dahdi/wcte13xp-base.c | 35 +++++++++++------- drivers/dahdi/wcte43x-base.c | 2 +- drivers/dahdi/wcxb.c | 68 ++++++++++++++++++++++++++++------- drivers/dahdi/wcxb.h | 10 +++++- 5 files changed, 90 insertions(+), 27 deletions(-) diff --git a/drivers/dahdi/wcaxx-base.c b/drivers/dahdi/wcaxx-base.c index c17f1bc..d90f014 100644 --- a/drivers/dahdi/wcaxx-base.c +++ b/drivers/dahdi/wcaxx-base.c @@ -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) diff --git a/drivers/dahdi/wcte13xp-base.c b/drivers/dahdi/wcte13xp-base.c index 726a63c..5c61309 100644 --- a/drivers/dahdi/wcte13xp-base.c +++ b/drivers/dahdi/wcte13xp-base.c @@ -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); - 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; - } + res = te13xp_check_firmware(wc); + if (res) + goto fail_exit; wc->ddev->hardware_id = t13x_read_serial(wc); if (!wc->ddev->hardware_id) { diff --git a/drivers/dahdi/wcte43x-base.c b/drivers/dahdi/wcte43x-base.c index af7b787..a285fa2 100644 --- a/drivers/dahdi/wcte43x-base.c +++ b/drivers/dahdi/wcte43x-base.c @@ -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; diff --git a/drivers/dahdi/wcxb.c b/drivers/dahdi/wcxb.c index 5ae5325..3373723 100644 --- a/drivers/dahdi/wcxb.c +++ b/drivers/dahdi/wcxb.c @@ -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)); - /* Reset fpga after loading firmware */ - dev_info(&xb->pdev->dev, "Firmware load complete. Reseting device.\n"); - tdm_control = ioread32be(xb->membase + TDM_CONTROL); + if (WCXB_RESET_NOW == reset) { + /* Reset fpga after loading firmware */ + 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(tdm_control, xb->membase + TDM_CONTROL); + 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 diff --git a/drivers/dahdi/wcxb.h b/drivers/dahdi/wcxb.h index 268e6c2..af7828b 100644 --- a/drivers/dahdi/wcxb.h +++ b/drivers/dahdi/wcxb.h @@ -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);