202 lines · c
1// SPDX-License-Identifier: GPL-2.02/* Copyright(c) 2020 Pensando Systems, Inc */3 4#include <linux/kernel.h>5#include <linux/types.h>6#include <linux/errno.h>7#include <linux/firmware.h>8 9#include "ionic.h"10#include "ionic_dev.h"11#include "ionic_lif.h"12#include "ionic_devlink.h"13 14/* The worst case wait for the install activity is about 25 minutes when15 * installing a new CPLD, which is very seldom. Normal is about 30-3516 * seconds. Since the driver can't tell if a CPLD update will happen we17 * set the timeout for the ugly case.18 */19#define IONIC_FW_INSTALL_TIMEOUT (25 * 60)20#define IONIC_FW_SELECT_TIMEOUT 3021 22/* Number of periodic log updates during fw file download */23#define IONIC_FW_INTERVAL_FRACTION 3224 25static void ionic_dev_cmd_firmware_download(struct ionic_dev *idev, u64 addr,26 u32 offset, u32 length)27{28 union ionic_dev_cmd cmd = {29 .fw_download.opcode = IONIC_CMD_FW_DOWNLOAD,30 .fw_download.offset = cpu_to_le32(offset),31 .fw_download.addr = cpu_to_le64(addr),32 .fw_download.length = cpu_to_le32(length),33 };34 35 ionic_dev_cmd_go(idev, &cmd);36}37 38static void ionic_dev_cmd_firmware_install(struct ionic_dev *idev)39{40 union ionic_dev_cmd cmd = {41 .fw_control.opcode = IONIC_CMD_FW_CONTROL,42 .fw_control.oper = IONIC_FW_INSTALL_ASYNC43 };44 45 ionic_dev_cmd_go(idev, &cmd);46}47 48static void ionic_dev_cmd_firmware_activate(struct ionic_dev *idev, u8 slot)49{50 union ionic_dev_cmd cmd = {51 .fw_control.opcode = IONIC_CMD_FW_CONTROL,52 .fw_control.oper = IONIC_FW_ACTIVATE_ASYNC,53 .fw_control.slot = slot54 };55 56 ionic_dev_cmd_go(idev, &cmd);57}58 59static int ionic_fw_status_long_wait(struct ionic *ionic,60 const char *label,61 unsigned long timeout,62 u8 fw_cmd,63 struct netlink_ext_ack *extack)64{65 union ionic_dev_cmd cmd = {66 .fw_control.opcode = IONIC_CMD_FW_CONTROL,67 .fw_control.oper = fw_cmd,68 };69 unsigned long start_time;70 unsigned long end_time;71 int err;72 73 start_time = jiffies;74 end_time = start_time + (timeout * HZ);75 do {76 mutex_lock(&ionic->dev_cmd_lock);77 ionic_dev_cmd_go(&ionic->idev, &cmd);78 err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);79 mutex_unlock(&ionic->dev_cmd_lock);80 81 msleep(20);82 } while (time_before(jiffies, end_time) && (err == -EAGAIN || err == -ETIMEDOUT));83 84 if (err == -EAGAIN || err == -ETIMEDOUT) {85 NL_SET_ERR_MSG_MOD(extack, "Firmware wait timed out");86 dev_err(ionic->dev, "DEV_CMD firmware wait %s timed out\n", label);87 } else if (err) {88 NL_SET_ERR_MSG_MOD(extack, "Firmware wait failed");89 }90 91 return err;92}93 94int ionic_firmware_update(struct ionic_lif *lif, const struct firmware *fw,95 struct netlink_ext_ack *extack)96{97 struct ionic_dev *idev = &lif->ionic->idev;98 struct net_device *netdev = lif->netdev;99 struct ionic *ionic = lif->ionic;100 union ionic_dev_cmd_comp comp;101 u32 buf_sz, copy_sz, offset;102 struct devlink *dl;103 int next_interval;104 int err = 0;105 u8 fw_slot;106 107 netdev_info(netdev, "Installing firmware\n");108 109 dl = priv_to_devlink(ionic);110 devlink_flash_update_status_notify(dl, "Preparing to flash", NULL, 0, 0);111 112 if (!idev->dev_cmd_regs) {113 err = -ENXIO;114 goto err_out;115 }116 117 buf_sz = sizeof(idev->dev_cmd_regs->data);118 119 netdev_dbg(netdev,120 "downloading firmware - size %d part_sz %d nparts %lu\n",121 (int)fw->size, buf_sz, DIV_ROUND_UP(fw->size, buf_sz));122 123 offset = 0;124 next_interval = 0;125 while (offset < fw->size) {126 if (offset >= next_interval) {127 devlink_flash_update_status_notify(dl, "Downloading", NULL,128 offset, fw->size);129 next_interval = offset + (fw->size / IONIC_FW_INTERVAL_FRACTION);130 }131 132 copy_sz = min_t(unsigned int, buf_sz, fw->size - offset);133 mutex_lock(&ionic->dev_cmd_lock);134 memcpy_toio(&idev->dev_cmd_regs->data, fw->data + offset, copy_sz);135 ionic_dev_cmd_firmware_download(idev,136 offsetof(union ionic_dev_cmd_regs, data),137 offset, copy_sz);138 err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);139 mutex_unlock(&ionic->dev_cmd_lock);140 if (err) {141 netdev_err(netdev,142 "download failed offset 0x%x addr 0x%lx len 0x%x\n",143 offset, offsetof(union ionic_dev_cmd_regs, data),144 copy_sz);145 NL_SET_ERR_MSG_MOD(extack, "Segment download failed");146 goto err_out;147 }148 offset += copy_sz;149 }150 devlink_flash_update_status_notify(dl, "Downloading", NULL,151 fw->size, fw->size);152 153 devlink_flash_update_timeout_notify(dl, "Installing", NULL,154 IONIC_FW_INSTALL_TIMEOUT);155 156 mutex_lock(&ionic->dev_cmd_lock);157 ionic_dev_cmd_firmware_install(idev);158 err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);159 ionic_dev_cmd_comp(idev, (union ionic_dev_cmd_comp *)&comp);160 fw_slot = comp.fw_control.slot;161 mutex_unlock(&ionic->dev_cmd_lock);162 if (err) {163 NL_SET_ERR_MSG_MOD(extack, "Failed to start firmware install");164 goto err_out;165 }166 167 err = ionic_fw_status_long_wait(ionic, "Installing",168 IONIC_FW_INSTALL_TIMEOUT,169 IONIC_FW_INSTALL_STATUS,170 extack);171 if (err)172 goto err_out;173 174 devlink_flash_update_timeout_notify(dl, "Selecting", NULL,175 IONIC_FW_SELECT_TIMEOUT);176 177 mutex_lock(&ionic->dev_cmd_lock);178 ionic_dev_cmd_firmware_activate(idev, fw_slot);179 err = ionic_dev_cmd_wait(ionic, DEVCMD_TIMEOUT);180 mutex_unlock(&ionic->dev_cmd_lock);181 if (err) {182 NL_SET_ERR_MSG_MOD(extack, "Failed to start firmware select");183 goto err_out;184 }185 186 err = ionic_fw_status_long_wait(ionic, "Selecting",187 IONIC_FW_SELECT_TIMEOUT,188 IONIC_FW_ACTIVATE_STATUS,189 extack);190 if (err)191 goto err_out;192 193 netdev_info(netdev, "Firmware update completed\n");194 195err_out:196 if (err)197 devlink_flash_update_status_notify(dl, "Flash failed", NULL, 0, 0);198 else199 devlink_flash_update_status_notify(dl, "Flash done", NULL, 0, 0);200 return err;201}202