130b5191aSShannon Nelson // SPDX-License-Identifier: GPL-2.0 230b5191aSShannon Nelson /* Copyright(c) 2020 Pensando Systems, Inc */ 330b5191aSShannon Nelson 430b5191aSShannon Nelson #include <linux/kernel.h> 530b5191aSShannon Nelson #include <linux/types.h> 630b5191aSShannon Nelson #include <linux/errno.h> 730b5191aSShannon Nelson #include <linux/firmware.h> 830b5191aSShannon Nelson 930b5191aSShannon Nelson #include "ionic.h" 1030b5191aSShannon Nelson #include "ionic_dev.h" 1130b5191aSShannon Nelson #include "ionic_lif.h" 1230b5191aSShannon Nelson #include "ionic_devlink.h" 1330b5191aSShannon Nelson 1430b5191aSShannon Nelson /* The worst case wait for the install activity is about 25 minutes when 1530b5191aSShannon Nelson * installing a new CPLD, which is very seldom. Normal is about 30-35 1630b5191aSShannon Nelson * seconds. Since the driver can't tell if a CPLD update will happen we 1730b5191aSShannon Nelson * set the timeout for the ugly case. 1830b5191aSShannon Nelson */ 1930b5191aSShannon Nelson #define IONIC_FW_INSTALL_TIMEOUT (25 * 60) 2030b5191aSShannon Nelson #define IONIC_FW_SELECT_TIMEOUT 30 2130b5191aSShannon Nelson 2230b5191aSShannon Nelson /* Number of periodic log updates during fw file download */ 2330b5191aSShannon Nelson #define IONIC_FW_INTERVAL_FRACTION 32 2430b5191aSShannon Nelson 2530b5191aSShannon Nelson static void ionic_dev_cmd_firmware_download(struct ionic_dev *idev, u64 addr, 2630b5191aSShannon Nelson u32 offset, u32 length) 2730b5191aSShannon Nelson { 2830b5191aSShannon Nelson union ionic_dev_cmd cmd = { 2930b5191aSShannon Nelson .fw_download.opcode = IONIC_CMD_FW_DOWNLOAD, 30d701ec32SShannon Nelson .fw_download.offset = cpu_to_le32(offset), 31d701ec32SShannon Nelson .fw_download.addr = cpu_to_le64(addr), 32d701ec32SShannon Nelson .fw_download.length = cpu_to_le32(length), 3330b5191aSShannon Nelson }; 3430b5191aSShannon Nelson 3530b5191aSShannon Nelson ionic_dev_cmd_go(idev, &cmd); 3630b5191aSShannon Nelson } 3730b5191aSShannon Nelson 3830b5191aSShannon Nelson static void ionic_dev_cmd_firmware_install(struct ionic_dev *idev) 3930b5191aSShannon Nelson { 4030b5191aSShannon Nelson union ionic_dev_cmd cmd = { 4130b5191aSShannon Nelson .fw_control.opcode = IONIC_CMD_FW_CONTROL, 4230b5191aSShannon Nelson .fw_control.oper = IONIC_FW_INSTALL_ASYNC 4330b5191aSShannon Nelson }; 4430b5191aSShannon Nelson 4530b5191aSShannon Nelson ionic_dev_cmd_go(idev, &cmd); 4630b5191aSShannon Nelson } 4730b5191aSShannon Nelson 4830b5191aSShannon Nelson static void ionic_dev_cmd_firmware_activate(struct ionic_dev *idev, u8 slot) 4930b5191aSShannon Nelson { 5030b5191aSShannon Nelson union ionic_dev_cmd cmd = { 5130b5191aSShannon Nelson .fw_control.opcode = IONIC_CMD_FW_CONTROL, 5230b5191aSShannon Nelson .fw_control.oper = IONIC_FW_ACTIVATE_ASYNC, 5330b5191aSShannon Nelson .fw_control.slot = slot 5430b5191aSShannon Nelson }; 5530b5191aSShannon Nelson 5630b5191aSShannon Nelson ionic_dev_cmd_go(idev, &cmd); 5730b5191aSShannon Nelson } 5830b5191aSShannon Nelson 5930b5191aSShannon Nelson static int ionic_fw_status_long_wait(struct ionic *ionic, 6030b5191aSShannon Nelson const char *label, 6130b5191aSShannon Nelson unsigned long timeout, 6230b5191aSShannon Nelson u8 fw_cmd, 6330b5191aSShannon Nelson struct netlink_ext_ack *extack) 6430b5191aSShannon Nelson { 6530b5191aSShannon Nelson union ionic_dev_cmd cmd = { 6630b5191aSShannon Nelson .fw_control.opcode = IONIC_CMD_FW_CONTROL, 6730b5191aSShannon Nelson .fw_control.oper = fw_cmd, 6830b5191aSShannon Nelson }; 6930b5191aSShannon Nelson unsigned long start_time; 7030b5191aSShannon Nelson unsigned long end_time; 7130b5191aSShannon Nelson int err; 7230b5191aSShannon Nelson 7330b5191aSShannon Nelson start_time = jiffies; 7430b5191aSShannon Nelson end_time = start_time + (timeout * HZ); 7530b5191aSShannon Nelson do { 7630b5191aSShannon Nelson mutex_lock(&ionic->dev_cmd_lock); 7730b5191aSShannon Nelson ionic_dev_cmd_go(&ionic->idev, &cmd); 7830b5191aSShannon Nelson err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); 7930b5191aSShannon Nelson mutex_unlock(&ionic->dev_cmd_lock); 8030b5191aSShannon Nelson 8130b5191aSShannon Nelson msleep(20); 8230b5191aSShannon Nelson } while (time_before(jiffies, end_time) && (err == -EAGAIN || err == -ETIMEDOUT)); 8330b5191aSShannon Nelson 8430b5191aSShannon Nelson if (err == -EAGAIN || err == -ETIMEDOUT) { 8530b5191aSShannon Nelson NL_SET_ERR_MSG_MOD(extack, "Firmware wait timed out"); 8630b5191aSShannon Nelson dev_err(ionic->dev, "DEV_CMD firmware wait %s timed out\n", label); 8730b5191aSShannon Nelson } else if (err) { 8830b5191aSShannon Nelson NL_SET_ERR_MSG_MOD(extack, "Firmware wait failed"); 8930b5191aSShannon Nelson } 9030b5191aSShannon Nelson 9130b5191aSShannon Nelson return err; 9230b5191aSShannon Nelson } 9330b5191aSShannon Nelson 94*b44cfd4fSJacob Keller int ionic_firmware_update(struct ionic_lif *lif, const struct firmware *fw, 9530b5191aSShannon Nelson struct netlink_ext_ack *extack) 9630b5191aSShannon Nelson { 9730b5191aSShannon Nelson struct ionic_dev *idev = &lif->ionic->idev; 9830b5191aSShannon Nelson struct net_device *netdev = lif->netdev; 9930b5191aSShannon Nelson struct ionic *ionic = lif->ionic; 10030b5191aSShannon Nelson union ionic_dev_cmd_comp comp; 10130b5191aSShannon Nelson u32 buf_sz, copy_sz, offset; 10230b5191aSShannon Nelson struct devlink *dl; 10330b5191aSShannon Nelson int next_interval; 10430b5191aSShannon Nelson int err = 0; 10530b5191aSShannon Nelson u8 fw_slot; 10630b5191aSShannon Nelson 107*b44cfd4fSJacob Keller netdev_info(netdev, "Installing firmware\n"); 10830b5191aSShannon Nelson 10930b5191aSShannon Nelson dl = priv_to_devlink(ionic); 11030b5191aSShannon Nelson devlink_flash_update_begin_notify(dl); 11130b5191aSShannon Nelson devlink_flash_update_status_notify(dl, "Preparing to flash", NULL, 0, 0); 11230b5191aSShannon Nelson 11330b5191aSShannon Nelson buf_sz = sizeof(idev->dev_cmd_regs->data); 11430b5191aSShannon Nelson 11530b5191aSShannon Nelson netdev_dbg(netdev, 11630b5191aSShannon Nelson "downloading firmware - size %d part_sz %d nparts %lu\n", 11730b5191aSShannon Nelson (int)fw->size, buf_sz, DIV_ROUND_UP(fw->size, buf_sz)); 11830b5191aSShannon Nelson 11930b5191aSShannon Nelson offset = 0; 12030b5191aSShannon Nelson next_interval = 0; 12130b5191aSShannon Nelson while (offset < fw->size) { 12230b5191aSShannon Nelson if (offset >= next_interval) { 12330b5191aSShannon Nelson devlink_flash_update_status_notify(dl, "Downloading", NULL, 12430b5191aSShannon Nelson offset, fw->size); 12530b5191aSShannon Nelson next_interval = offset + (fw->size / IONIC_FW_INTERVAL_FRACTION); 12630b5191aSShannon Nelson } 12730b5191aSShannon Nelson 12830b5191aSShannon Nelson copy_sz = min_t(unsigned int, buf_sz, fw->size - offset); 12930b5191aSShannon Nelson mutex_lock(&ionic->dev_cmd_lock); 13030b5191aSShannon Nelson memcpy_toio(&idev->dev_cmd_regs->data, fw->data + offset, copy_sz); 13130b5191aSShannon Nelson ionic_dev_cmd_firmware_download(idev, 13230b5191aSShannon Nelson offsetof(union ionic_dev_cmd_regs, data), 13330b5191aSShannon Nelson offset, copy_sz); 13430b5191aSShannon Nelson err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); 13530b5191aSShannon Nelson mutex_unlock(&ionic->dev_cmd_lock); 13630b5191aSShannon Nelson if (err) { 13730b5191aSShannon Nelson netdev_err(netdev, 13830b5191aSShannon Nelson "download failed offset 0x%x addr 0x%lx len 0x%x\n", 13930b5191aSShannon Nelson offset, offsetof(union ionic_dev_cmd_regs, data), 14030b5191aSShannon Nelson copy_sz); 14130b5191aSShannon Nelson NL_SET_ERR_MSG_MOD(extack, "Segment download failed"); 14230b5191aSShannon Nelson goto err_out; 14330b5191aSShannon Nelson } 14430b5191aSShannon Nelson offset += copy_sz; 14530b5191aSShannon Nelson } 14630b5191aSShannon Nelson devlink_flash_update_status_notify(dl, "Downloading", NULL, 14730b5191aSShannon Nelson fw->size, fw->size); 14830b5191aSShannon Nelson 14930b5191aSShannon Nelson devlink_flash_update_timeout_notify(dl, "Installing", NULL, 15030b5191aSShannon Nelson IONIC_FW_INSTALL_TIMEOUT); 15130b5191aSShannon Nelson 15230b5191aSShannon Nelson mutex_lock(&ionic->dev_cmd_lock); 15330b5191aSShannon Nelson ionic_dev_cmd_firmware_install(idev); 15430b5191aSShannon Nelson err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); 15530b5191aSShannon Nelson ionic_dev_cmd_comp(idev, (union ionic_dev_cmd_comp *)&comp); 15630b5191aSShannon Nelson fw_slot = comp.fw_control.slot; 15730b5191aSShannon Nelson mutex_unlock(&ionic->dev_cmd_lock); 15830b5191aSShannon Nelson if (err) { 15930b5191aSShannon Nelson NL_SET_ERR_MSG_MOD(extack, "Failed to start firmware install"); 16030b5191aSShannon Nelson goto err_out; 16130b5191aSShannon Nelson } 16230b5191aSShannon Nelson 16330b5191aSShannon Nelson err = ionic_fw_status_long_wait(ionic, "Installing", 16430b5191aSShannon Nelson IONIC_FW_INSTALL_TIMEOUT, 16530b5191aSShannon Nelson IONIC_FW_INSTALL_STATUS, 16630b5191aSShannon Nelson extack); 16730b5191aSShannon Nelson if (err) 16830b5191aSShannon Nelson goto err_out; 16930b5191aSShannon Nelson 17030b5191aSShannon Nelson devlink_flash_update_timeout_notify(dl, "Selecting", NULL, 17130b5191aSShannon Nelson IONIC_FW_SELECT_TIMEOUT); 17230b5191aSShannon Nelson 17330b5191aSShannon Nelson mutex_lock(&ionic->dev_cmd_lock); 17430b5191aSShannon Nelson ionic_dev_cmd_firmware_activate(idev, fw_slot); 17530b5191aSShannon Nelson err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT); 17630b5191aSShannon Nelson mutex_unlock(&ionic->dev_cmd_lock); 17730b5191aSShannon Nelson if (err) { 17830b5191aSShannon Nelson NL_SET_ERR_MSG_MOD(extack, "Failed to start firmware select"); 17930b5191aSShannon Nelson goto err_out; 18030b5191aSShannon Nelson } 18130b5191aSShannon Nelson 18230b5191aSShannon Nelson err = ionic_fw_status_long_wait(ionic, "Selecting", 18330b5191aSShannon Nelson IONIC_FW_SELECT_TIMEOUT, 18430b5191aSShannon Nelson IONIC_FW_ACTIVATE_STATUS, 18530b5191aSShannon Nelson extack); 18630b5191aSShannon Nelson if (err) 18730b5191aSShannon Nelson goto err_out; 18830b5191aSShannon Nelson 18930b5191aSShannon Nelson netdev_info(netdev, "Firmware update completed\n"); 19030b5191aSShannon Nelson 19130b5191aSShannon Nelson err_out: 19230b5191aSShannon Nelson if (err) 19330b5191aSShannon Nelson devlink_flash_update_status_notify(dl, "Flash failed", NULL, 0, 0); 19430b5191aSShannon Nelson else 19530b5191aSShannon Nelson devlink_flash_update_status_notify(dl, "Flash done", NULL, 0, 0); 19630b5191aSShannon Nelson devlink_flash_update_end_notify(dl); 19730b5191aSShannon Nelson return err; 19830b5191aSShannon Nelson } 199