Commit 22f07b86 authored by Jakub Kicinski's avatar Jakub Kicinski

Merge branch 'bnxt_en-improve-firmware-flashing'

Michael Chan says:

====================
bnxt_en: Improve firmware flashing.

This patchset improves firmware flashing in 2 ways:

- If firmware returns NO_SPACE error during flashing, the driver will
create the UPDATE directory with more staging area and retry.
- Instead of allocating a big DMA buffer for the entire contents of
the firmware package size, fallback to a smaller buffer to DMA the
contents in multiple DMA operations.
====================

Link: https://lore.kernel.org/r/1607860306-17244-1-git-send-email-michael.chan@broadcom.comSigned-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 2aa899eb a86b313e
......@@ -2100,19 +2100,16 @@ static int bnxt_find_nvram_item(struct net_device *dev, u16 type, u16 ordinal,
u16 ext, u16 *index, u32 *item_length,
u32 *data_length);
static int bnxt_flash_nvram(struct net_device *dev,
u16 dir_type,
u16 dir_ordinal,
u16 dir_ext,
u16 dir_attr,
const u8 *data,
static int __bnxt_flash_nvram(struct net_device *dev, u16 dir_type,
u16 dir_ordinal, u16 dir_ext, u16 dir_attr,
u32 dir_item_len, const u8 *data,
size_t data_len)
{
struct bnxt *bp = netdev_priv(dev);
int rc;
struct hwrm_nvm_write_input req = {0};
dma_addr_t dma_handle;
u8 *kmem;
u8 *kmem = NULL;
bnxt_hwrm_cmd_hdr_init(bp, &req, HWRM_NVM_WRITE, -1, -1);
......@@ -2120,19 +2117,21 @@ static int bnxt_flash_nvram(struct net_device *dev,
req.dir_ordinal = cpu_to_le16(dir_ordinal);
req.dir_ext = cpu_to_le16(dir_ext);
req.dir_attr = cpu_to_le16(dir_attr);
req.dir_item_length = cpu_to_le32(dir_item_len);
if (data_len && data) {
req.dir_data_length = cpu_to_le32(data_len);
kmem = dma_alloc_coherent(&bp->pdev->dev, data_len, &dma_handle,
GFP_KERNEL);
if (!kmem) {
netdev_err(dev, "dma_alloc_coherent failure, length = %u\n",
(unsigned)data_len);
if (!kmem)
return -ENOMEM;
}
memcpy(kmem, data, data_len);
req.host_src_addr = cpu_to_le64(dma_handle);
}
rc = hwrm_send_message(bp, &req, sizeof(req), FLASH_NVRAM_TIMEOUT);
rc = _hwrm_send_message(bp, &req, sizeof(req), FLASH_NVRAM_TIMEOUT);
if (kmem)
dma_free_coherent(&bp->pdev->dev, data_len, kmem, dma_handle);
if (rc == -EACCES)
......@@ -2140,6 +2139,20 @@ static int bnxt_flash_nvram(struct net_device *dev,
return rc;
}
static int bnxt_flash_nvram(struct net_device *dev, u16 dir_type,
u16 dir_ordinal, u16 dir_ext, u16 dir_attr,
const u8 *data, size_t data_len)
{
struct bnxt *bp = netdev_priv(dev);
int rc;
mutex_lock(&bp->hwrm_cmd_lock);
rc = __bnxt_flash_nvram(dev, dir_type, dir_ordinal, dir_ext, dir_attr,
0, data, data_len);
mutex_unlock(&bp->hwrm_cmd_lock);
return rc;
}
static int bnxt_hwrm_firmware_reset(struct net_device *dev, u8 proc_type,
u8 self_reset, u8 flags)
{
......@@ -2419,90 +2432,141 @@ static int bnxt_flash_firmware_from_file(struct net_device *dev,
return rc;
}
#define BNXT_PKG_DMA_SIZE 0x40000
#define BNXT_NVM_MORE_FLAG (cpu_to_le16(NVM_MODIFY_REQ_FLAGS_BATCH_MODE))
#define BNXT_NVM_LAST_FLAG (cpu_to_le16(NVM_MODIFY_REQ_FLAGS_BATCH_LAST))
int bnxt_flash_package_from_fw_obj(struct net_device *dev, const struct firmware *fw,
u32 install_type)
{
struct bnxt *bp = netdev_priv(dev);
struct hwrm_nvm_install_update_output *resp = bp->hwrm_cmd_resp_addr;
struct hwrm_nvm_install_update_input install = {0};
struct hwrm_nvm_install_update_output resp = {0};
struct hwrm_nvm_modify_input modify = {0};
struct bnxt *bp = netdev_priv(dev);
bool defrag_attempted = false;
dma_addr_t dma_handle;
u8 *kmem = NULL;
u32 modify_len;
u32 item_len;
int rc = 0;
u16 index;
bnxt_hwrm_fw_set_time(bp);
bnxt_hwrm_cmd_hdr_init(bp, &modify, HWRM_NVM_MODIFY, -1, -1);
/* Try allocating a large DMA buffer first. Older fw will
* cause excessive NVRAM erases when using small blocks.
*/
modify_len = roundup_pow_of_two(fw->size);
modify_len = min_t(u32, modify_len, BNXT_PKG_DMA_SIZE);
while (1) {
kmem = dma_alloc_coherent(&bp->pdev->dev, modify_len,
&dma_handle, GFP_KERNEL);
if (!kmem && modify_len > PAGE_SIZE)
modify_len /= 2;
else
break;
}
if (!kmem)
return -ENOMEM;
modify.host_src_addr = cpu_to_le64(dma_handle);
bnxt_hwrm_cmd_hdr_init(bp, &install, HWRM_NVM_INSTALL_UPDATE, -1, -1);
if ((install_type & 0xffff) == 0)
install_type >>= 16;
install.install_type = cpu_to_le32(install_type);
do {
u32 copied = 0, len = modify_len;
rc = bnxt_find_nvram_item(dev, BNX_DIR_TYPE_UPDATE,
BNX_DIR_ORDINAL_FIRST, BNX_DIR_EXT_NONE,
BNX_DIR_ORDINAL_FIRST,
BNX_DIR_EXT_NONE,
&index, &item_len, NULL);
if (rc) {
netdev_err(dev, "PKG update area not created in nvram\n");
return rc;
break;
}
if (fw->size > item_len) {
netdev_err(dev, "PKG insufficient update area in nvram: %lu\n",
(unsigned long)fw->size);
rc = -EFBIG;
} else {
dma_addr_t dma_handle;
u8 *kmem;
struct hwrm_nvm_modify_input modify = {0};
bnxt_hwrm_cmd_hdr_init(bp, &modify, HWRM_NVM_MODIFY, -1, -1);
break;
}
modify.dir_idx = cpu_to_le16(index);
modify.len = cpu_to_le32(fw->size);
kmem = dma_alloc_coherent(&bp->pdev->dev, fw->size,
&dma_handle, GFP_KERNEL);
if (!kmem) {
netdev_err(dev,
"dma_alloc_coherent failure, length = %u\n",
(unsigned int)fw->size);
rc = -ENOMEM;
} else {
memcpy(kmem, fw->data, fw->size);
modify.host_src_addr = cpu_to_le64(dma_handle);
if (fw->size > modify_len)
modify.flags = BNXT_NVM_MORE_FLAG;
while (copied < fw->size) {
u32 balance = fw->size - copied;
if (balance <= modify_len) {
len = balance;
if (copied)
modify.flags |= BNXT_NVM_LAST_FLAG;
}
memcpy(kmem, fw->data + copied, len);
modify.len = cpu_to_le32(len);
modify.offset = cpu_to_le32(copied);
rc = hwrm_send_message(bp, &modify, sizeof(modify),
FLASH_PACKAGE_TIMEOUT);
dma_free_coherent(&bp->pdev->dev, fw->size, kmem,
dma_handle);
}
}
if (rc)
goto err_exit;
if ((install_type & 0xffff) == 0)
install_type >>= 16;
bnxt_hwrm_cmd_hdr_init(bp, &install, HWRM_NVM_INSTALL_UPDATE, -1, -1);
install.install_type = cpu_to_le32(install_type);
goto pkg_abort;
copied += len;
}
mutex_lock(&bp->hwrm_cmd_lock);
rc = _hwrm_send_message(bp, &install, sizeof(install),
rc = _hwrm_send_message_silent(bp, &install, sizeof(install),
INSTALL_PACKAGE_TIMEOUT);
if (rc) {
u8 error_code = ((struct hwrm_err_output *)resp)->cmd_err;
memcpy(&resp, bp->hwrm_cmd_resp_addr, sizeof(resp));
if (resp->error_code && error_code ==
if (defrag_attempted) {
/* We have tried to defragment already in the previous
* iteration. Return with the result for INSTALL_UPDATE
*/
mutex_unlock(&bp->hwrm_cmd_lock);
break;
}
if (rc && ((struct hwrm_err_output *)&resp)->cmd_err ==
NVM_INSTALL_UPDATE_CMD_ERR_CODE_FRAG_ERR) {
install.flags |= cpu_to_le16(
NVM_INSTALL_UPDATE_REQ_FLAGS_ALLOWED_TO_DEFRAG);
rc = _hwrm_send_message(bp, &install, sizeof(install),
install.flags |=
cpu_to_le16(NVM_INSTALL_UPDATE_REQ_FLAGS_ALLOWED_TO_DEFRAG);
rc = _hwrm_send_message_silent(bp, &install,
sizeof(install),
INSTALL_PACKAGE_TIMEOUT);
memcpy(&resp, bp->hwrm_cmd_resp_addr, sizeof(resp));
if (rc && ((struct hwrm_err_output *)&resp)->cmd_err ==
NVM_INSTALL_UPDATE_CMD_ERR_CODE_NO_SPACE) {
/* FW has cleared NVM area, driver will create
* UPDATE directory and try the flash again
*/
defrag_attempted = true;
rc = __bnxt_flash_nvram(bp->dev,
BNX_DIR_TYPE_UPDATE,
BNX_DIR_ORDINAL_FIRST,
0, 0, item_len, NULL,
0);
} else if (rc) {
netdev_err(dev, "HWRM_NVM_INSTALL_UPDATE failure rc :%x\n", rc);
}
if (rc)
goto flash_pkg_exit;
} else if (rc) {
netdev_err(dev, "HWRM_NVM_INSTALL_UPDATE failure rc :%x\n", rc);
}
mutex_unlock(&bp->hwrm_cmd_lock);
} while (defrag_attempted && !rc);
if (resp->result) {
pkg_abort:
dma_free_coherent(&bp->pdev->dev, modify_len, kmem, dma_handle);
if (resp.result) {
netdev_err(dev, "PKG install error = %d, problem_item = %d\n",
(s8)resp->result, (int)resp->problem_item);
(s8)resp.result, (int)resp.problem_item);
rc = -ENOPKG;
}
flash_pkg_exit:
mutex_unlock(&bp->hwrm_cmd_lock);
err_exit:
if (rc == -EACCES)
bnxt_print_admin_err(bp);
return rc;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment