| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201 |
- // SPDX-License-Identifier: GPL-2.0
- /* Copyright(c) 2020 Pensando Systems, Inc */
- #include <linux/kernel.h>
- #include <linux/types.h>
- #include <linux/errno.h>
- #include <linux/firmware.h>
- #include "ionic.h"
- #include "ionic_dev.h"
- #include "ionic_lif.h"
- #include "ionic_devlink.h"
- /* The worst case wait for the install activity is about 25 minutes when
- * installing a new CPLD, which is very seldom. Normal is about 30-35
- * seconds. Since the driver can't tell if a CPLD update will happen we
- * set the timeout for the ugly case.
- */
- #define IONIC_FW_INSTALL_TIMEOUT (25 * 60)
- #define IONIC_FW_SELECT_TIMEOUT 30
- /* Number of periodic log updates during fw file download */
- #define IONIC_FW_INTERVAL_FRACTION 32
- static void ionic_dev_cmd_firmware_download(struct ionic_dev *idev, u64 addr,
- u32 offset, u32 length)
- {
- union ionic_dev_cmd cmd = {
- .fw_download.opcode = IONIC_CMD_FW_DOWNLOAD,
- .fw_download.offset = cpu_to_le32(offset),
- .fw_download.addr = cpu_to_le64(addr),
- .fw_download.length = cpu_to_le32(length),
- };
- ionic_dev_cmd_go(idev, &cmd);
- }
- static void ionic_dev_cmd_firmware_install(struct ionic_dev *idev)
- {
- union ionic_dev_cmd cmd = {
- .fw_control.opcode = IONIC_CMD_FW_CONTROL,
- .fw_control.oper = IONIC_FW_INSTALL_ASYNC
- };
- ionic_dev_cmd_go(idev, &cmd);
- }
- static void ionic_dev_cmd_firmware_activate(struct ionic_dev *idev, u8 slot)
- {
- union ionic_dev_cmd cmd = {
- .fw_control.opcode = IONIC_CMD_FW_CONTROL,
- .fw_control.oper = IONIC_FW_ACTIVATE_ASYNC,
- .fw_control.slot = slot
- };
- ionic_dev_cmd_go(idev, &cmd);
- }
- static int ionic_fw_status_long_wait(struct ionic *ionic,
- const char *label,
- unsigned long timeout,
- u8 fw_cmd,
- struct netlink_ext_ack *extack)
- {
- union ionic_dev_cmd cmd = {
- .fw_control.opcode = IONIC_CMD_FW_CONTROL,
- .fw_control.oper = fw_cmd,
- };
- unsigned long start_time;
- unsigned long end_time;
- int err;
- start_time = jiffies;
- end_time = start_time + (timeout * HZ);
- do {
- mutex_lock(&ionic->dev_cmd_lock);
- ionic_dev_cmd_go(&ionic->idev, &cmd);
- err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
- mutex_unlock(&ionic->dev_cmd_lock);
- msleep(20);
- } while (time_before(jiffies, end_time) && (err == -EAGAIN || err == -ETIMEDOUT));
- if (err == -EAGAIN || err == -ETIMEDOUT) {
- NL_SET_ERR_MSG_MOD(extack, "Firmware wait timed out");
- dev_err(ionic->dev, "DEV_CMD firmware wait %s timed out\n", label);
- } else if (err) {
- NL_SET_ERR_MSG_MOD(extack, "Firmware wait failed");
- }
- return err;
- }
- int ionic_firmware_update(struct ionic_lif *lif, const struct firmware *fw,
- struct netlink_ext_ack *extack)
- {
- struct ionic_dev *idev = &lif->ionic->idev;
- struct net_device *netdev = lif->netdev;
- struct ionic *ionic = lif->ionic;
- union ionic_dev_cmd_comp comp;
- u32 buf_sz, copy_sz, offset;
- struct devlink *dl;
- int next_interval;
- int err = 0;
- u8 fw_slot;
- netdev_info(netdev, "Installing firmware\n");
- dl = priv_to_devlink(ionic);
- devlink_flash_update_status_notify(dl, "Preparing to flash", NULL, 0, 0);
- if (!idev->dev_cmd_regs) {
- err = -ENXIO;
- goto err_out;
- }
- buf_sz = sizeof(idev->dev_cmd_regs->data);
- netdev_dbg(netdev,
- "downloading firmware - size %d part_sz %d nparts %lu\n",
- (int)fw->size, buf_sz, DIV_ROUND_UP(fw->size, buf_sz));
- offset = 0;
- next_interval = 0;
- while (offset < fw->size) {
- if (offset >= next_interval) {
- devlink_flash_update_status_notify(dl, "Downloading", NULL,
- offset, fw->size);
- next_interval = offset + (fw->size / IONIC_FW_INTERVAL_FRACTION);
- }
- copy_sz = min_t(unsigned int, buf_sz, fw->size - offset);
- mutex_lock(&ionic->dev_cmd_lock);
- memcpy_toio(&idev->dev_cmd_regs->data, fw->data + offset, copy_sz);
- ionic_dev_cmd_firmware_download(idev,
- offsetof(union ionic_dev_cmd_regs, data),
- offset, copy_sz);
- err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
- mutex_unlock(&ionic->dev_cmd_lock);
- if (err) {
- netdev_err(netdev,
- "download failed offset 0x%x addr 0x%lx len 0x%x\n",
- offset, offsetof(union ionic_dev_cmd_regs, data),
- copy_sz);
- NL_SET_ERR_MSG_MOD(extack, "Segment download failed");
- goto err_out;
- }
- offset += copy_sz;
- }
- devlink_flash_update_status_notify(dl, "Downloading", NULL,
- fw->size, fw->size);
- devlink_flash_update_timeout_notify(dl, "Installing", NULL,
- IONIC_FW_INSTALL_TIMEOUT);
- mutex_lock(&ionic->dev_cmd_lock);
- ionic_dev_cmd_firmware_install(idev);
- err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
- ionic_dev_cmd_comp(idev, (union ionic_dev_cmd_comp *)&comp);
- fw_slot = comp.fw_control.slot;
- mutex_unlock(&ionic->dev_cmd_lock);
- if (err) {
- NL_SET_ERR_MSG_MOD(extack, "Failed to start firmware install");
- goto err_out;
- }
- err = ionic_fw_status_long_wait(ionic, "Installing",
- IONIC_FW_INSTALL_TIMEOUT,
- IONIC_FW_INSTALL_STATUS,
- extack);
- if (err)
- goto err_out;
- devlink_flash_update_timeout_notify(dl, "Selecting", NULL,
- IONIC_FW_SELECT_TIMEOUT);
- mutex_lock(&ionic->dev_cmd_lock);
- ionic_dev_cmd_firmware_activate(idev, fw_slot);
- err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);
- mutex_unlock(&ionic->dev_cmd_lock);
- if (err) {
- NL_SET_ERR_MSG_MOD(extack, "Failed to start firmware select");
- goto err_out;
- }
- err = ionic_fw_status_long_wait(ionic, "Selecting",
- IONIC_FW_SELECT_TIMEOUT,
- IONIC_FW_ACTIVATE_STATUS,
- extack);
- if (err)
- goto err_out;
- netdev_info(netdev, "Firmware update completed\n");
- err_out:
- if (err)
- devlink_flash_update_status_notify(dl, "Flash failed", NULL, 0, 0);
- else
- devlink_flash_update_status_notify(dl, "Flash done", NULL, 0, 0);
- return err;
- }
|