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

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.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents 2aa899eb a86b313e
Loading
Loading
Loading
Loading
+139 −75
Original line number Diff line number Diff line
@@ -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;