Thomas Bogendoerfer 62cde4
From: Sudarsana Reddy Kalluru <sudarsana.kalluru@cavium.com>
Thomas Bogendoerfer 62cde4
Date: Wed, 28 Mar 2018 05:14:22 -0700
Thomas Bogendoerfer 62cde4
Subject: qed: Adapter flash update support.
Thomas Bogendoerfer 62cde4
Patch-mainline: v4.17-rc1
Thomas Bogendoerfer 62cde4
Git-commit: 3a69cae80cdd1b5c8b23137cba2a80ecfec4cef5
Thomas Bogendoerfer 62cde4
References: bsc#1086314 FATE#324886 bsc#1086313 FATE#324885 bsc#1086301 FATE#3248881
Thomas Bogendoerfer 62cde4
Thomas Bogendoerfer 62cde4
This patch adds the required driver support for updating the flash or
Thomas Bogendoerfer 62cde4
non volatile memory of the adapter. At highlevel, flash upgrade comprises
Thomas Bogendoerfer 62cde4
of reading the flash images from the input file, validating the images and
Thomas Bogendoerfer 62cde4
writing them to the respective paritions.
Thomas Bogendoerfer 62cde4
Thomas Bogendoerfer 62cde4
Signed-off-by: Sudarsana Reddy Kalluru <Sudarsana.Kalluru@cavium.com>
Thomas Bogendoerfer 62cde4
Signed-off-by: Ariel Elior <ariel.elior@cavium.com>
Thomas Bogendoerfer 62cde4
Signed-off-by: David S. Miller <davem@davemloft.net>
Thomas Bogendoerfer 62cde4
Acked-by: Thomas Bogendoerfer <tbogendoerfer@suse.de>
Thomas Bogendoerfer 62cde4
---
Thomas Bogendoerfer 62cde4
 drivers/net/ethernet/qlogic/qed/qed_main.c |  338 +++++++++++++++++++++++++++++
Thomas Bogendoerfer 62cde4
 include/linux/qed/qed_if.h                 |   19 +
Thomas Bogendoerfer 62cde4
 2 files changed, 357 insertions(+)
Thomas Bogendoerfer 62cde4
Thomas Bogendoerfer 62cde4
--- a/drivers/net/ethernet/qlogic/qed/qed_main.c
Thomas Bogendoerfer 62cde4
+++ b/drivers/net/ethernet/qlogic/qed/qed_main.c
Thomas Bogendoerfer 62cde4
@@ -45,6 +45,7 @@
Thomas Bogendoerfer 62cde4
 #include <linux/etherdevice.h>
Thomas Bogendoerfer 62cde4
 #include <linux/vmalloc.h>
Thomas Bogendoerfer 62cde4
 #include <linux/crash_dump.h>
Thomas Bogendoerfer 62cde4
+#include <linux/crc32.h>
Thomas Bogendoerfer 62cde4
 #include <linux/qed/qed_if.h>
Thomas Bogendoerfer 62cde4
 #include <linux/qed/qed_ll2_if.h>
Thomas Bogendoerfer 62cde4
 
Thomas Bogendoerfer 62cde4
@@ -1553,6 +1554,342 @@ static int qed_drain(struct qed_dev *cde
Thomas Bogendoerfer 62cde4
 	return 0;
Thomas Bogendoerfer 62cde4
 }
Thomas Bogendoerfer 62cde4
 
Thomas Bogendoerfer 62cde4
+static u32 qed_nvm_flash_image_access_crc(struct qed_dev *cdev,
Thomas Bogendoerfer 62cde4
+					  struct qed_nvm_image_att *nvm_image,
Thomas Bogendoerfer 62cde4
+					  u32 *crc)
Thomas Bogendoerfer 62cde4
+{
Thomas Bogendoerfer 62cde4
+	u8 *buf = NULL;
Thomas Bogendoerfer 62cde4
+	int rc, j;
Thomas Bogendoerfer 62cde4
+	u32 val;
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	/* Allocate a buffer for holding the nvram image */
Thomas Bogendoerfer 62cde4
+	buf = kzalloc(nvm_image->length, GFP_KERNEL);
Thomas Bogendoerfer 62cde4
+	if (!buf)
Thomas Bogendoerfer 62cde4
+		return -ENOMEM;
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	/* Read image into buffer */
Thomas Bogendoerfer 62cde4
+	rc = qed_mcp_nvm_read(cdev, nvm_image->start_addr,
Thomas Bogendoerfer 62cde4
+			      buf, nvm_image->length);
Thomas Bogendoerfer 62cde4
+	if (rc) {
Thomas Bogendoerfer 62cde4
+		DP_ERR(cdev, "Failed reading image from nvm\n");
Thomas Bogendoerfer 62cde4
+		goto out;
Thomas Bogendoerfer 62cde4
+	}
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	/* Convert the buffer into big-endian format (excluding the
Thomas Bogendoerfer 62cde4
+	 * closing 4 bytes of CRC).
Thomas Bogendoerfer 62cde4
+	 */
Thomas Bogendoerfer 62cde4
+	for (j = 0; j < nvm_image->length - 4; j += 4) {
Thomas Bogendoerfer 62cde4
+		val = cpu_to_be32(*(u32 *)&buf[j]);
Thomas Bogendoerfer 62cde4
+		*(u32 *)&buf[j] = val;
Thomas Bogendoerfer 62cde4
+	}
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	/* Calc CRC for the "actual" image buffer, i.e. not including
Thomas Bogendoerfer 62cde4
+	 * the last 4 CRC bytes.
Thomas Bogendoerfer 62cde4
+	 */
Thomas Bogendoerfer 62cde4
+	*crc = (~cpu_to_be32(crc32(0xffffffff, buf, nvm_image->length - 4)));
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+out:
Thomas Bogendoerfer 62cde4
+	kfree(buf);
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	return rc;
Thomas Bogendoerfer 62cde4
+}
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+/* Binary file format -
Thomas Bogendoerfer 62cde4
+ *     /----------------------------------------------------------------------\
Thomas Bogendoerfer 62cde4
+ * 0B  |                       0x4 [command index]                            |
Thomas Bogendoerfer 62cde4
+ * 4B  | image_type     | Options        |  Number of register settings       |
Thomas Bogendoerfer 62cde4
+ * 8B  |                       Value                                          |
Thomas Bogendoerfer 62cde4
+ * 12B |                       Mask                                           |
Thomas Bogendoerfer 62cde4
+ * 16B |                       Offset                                         |
Thomas Bogendoerfer 62cde4
+ *     \----------------------------------------------------------------------/
Thomas Bogendoerfer 62cde4
+ * There can be several Value-Mask-Offset sets as specified by 'Number of...'.
Thomas Bogendoerfer 62cde4
+ * Options - 0'b - Calculate & Update CRC for image
Thomas Bogendoerfer 62cde4
+ */
Thomas Bogendoerfer 62cde4
+static int qed_nvm_flash_image_access(struct qed_dev *cdev, const u8 **data,
Thomas Bogendoerfer 62cde4
+				      bool *check_resp)
Thomas Bogendoerfer 62cde4
+{
Thomas Bogendoerfer 62cde4
+	struct qed_nvm_image_att nvm_image;
Thomas Bogendoerfer 62cde4
+	struct qed_hwfn *p_hwfn;
Thomas Bogendoerfer 62cde4
+	bool is_crc = false;
Thomas Bogendoerfer 62cde4
+	u32 image_type;
Thomas Bogendoerfer 62cde4
+	int rc = 0, i;
Thomas Bogendoerfer 62cde4
+	u16 len;
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	*data += 4;
Thomas Bogendoerfer 62cde4
+	image_type = **data;
Thomas Bogendoerfer 62cde4
+	p_hwfn = QED_LEADING_HWFN(cdev);
Thomas Bogendoerfer 62cde4
+	for (i = 0; i < p_hwfn->nvm_info.num_images; i++)
Thomas Bogendoerfer 62cde4
+		if (image_type == p_hwfn->nvm_info.image_att[i].image_type)
Thomas Bogendoerfer 62cde4
+			break;
Thomas Bogendoerfer 62cde4
+	if (i == p_hwfn->nvm_info.num_images) {
Thomas Bogendoerfer 62cde4
+		DP_ERR(cdev, "Failed to find nvram image of type %08x\n",
Thomas Bogendoerfer 62cde4
+		       image_type);
Thomas Bogendoerfer 62cde4
+		return -ENOENT;
Thomas Bogendoerfer 62cde4
+	}
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	nvm_image.start_addr = p_hwfn->nvm_info.image_att[i].nvm_start_addr;
Thomas Bogendoerfer 62cde4
+	nvm_image.length = p_hwfn->nvm_info.image_att[i].len;
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	DP_VERBOSE(cdev, NETIF_MSG_DRV,
Thomas Bogendoerfer 62cde4
+		   "Read image %02x; type = %08x; NVM [%08x,...,%08x]\n",
Thomas Bogendoerfer 62cde4
+		   **data, image_type, nvm_image.start_addr,
Thomas Bogendoerfer 62cde4
+		   nvm_image.start_addr + nvm_image.length - 1);
Thomas Bogendoerfer 62cde4
+	(*data)++;
Thomas Bogendoerfer 62cde4
+	is_crc = !!(**data & BIT(0));
Thomas Bogendoerfer 62cde4
+	(*data)++;
Thomas Bogendoerfer 62cde4
+	len = *((u16 *)*data);
Thomas Bogendoerfer 62cde4
+	*data += 2;
Thomas Bogendoerfer 62cde4
+	if (is_crc) {
Thomas Bogendoerfer 62cde4
+		u32 crc = 0;
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+		rc = qed_nvm_flash_image_access_crc(cdev, &nvm_image, &crc;;
Thomas Bogendoerfer 62cde4
+		if (rc) {
Thomas Bogendoerfer 62cde4
+			DP_ERR(cdev, "Failed calculating CRC, rc = %d\n", rc);
Thomas Bogendoerfer 62cde4
+			goto exit;
Thomas Bogendoerfer 62cde4
+		}
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+		rc = qed_mcp_nvm_write(cdev, QED_NVM_WRITE_NVRAM,
Thomas Bogendoerfer 62cde4
+				       (nvm_image.start_addr +
Thomas Bogendoerfer 62cde4
+					nvm_image.length - 4), (u8 *)&crc, 4);
Thomas Bogendoerfer 62cde4
+		if (rc)
Thomas Bogendoerfer 62cde4
+			DP_ERR(cdev, "Failed writing to %08x, rc = %d\n",
Thomas Bogendoerfer 62cde4
+			       nvm_image.start_addr + nvm_image.length - 4, rc);
Thomas Bogendoerfer 62cde4
+		goto exit;
Thomas Bogendoerfer 62cde4
+	}
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	/* Iterate over the values for setting */
Thomas Bogendoerfer 62cde4
+	while (len) {
Thomas Bogendoerfer 62cde4
+		u32 offset, mask, value, cur_value;
Thomas Bogendoerfer 62cde4
+		u8 buf[4];
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+		value = *((u32 *)*data);
Thomas Bogendoerfer 62cde4
+		*data += 4;
Thomas Bogendoerfer 62cde4
+		mask = *((u32 *)*data);
Thomas Bogendoerfer 62cde4
+		*data += 4;
Thomas Bogendoerfer 62cde4
+		offset = *((u32 *)*data);
Thomas Bogendoerfer 62cde4
+		*data += 4;
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+		rc = qed_mcp_nvm_read(cdev, nvm_image.start_addr + offset, buf,
Thomas Bogendoerfer 62cde4
+				      4);
Thomas Bogendoerfer 62cde4
+		if (rc) {
Thomas Bogendoerfer 62cde4
+			DP_ERR(cdev, "Failed reading from %08x\n",
Thomas Bogendoerfer 62cde4
+			       nvm_image.start_addr + offset);
Thomas Bogendoerfer 62cde4
+			goto exit;
Thomas Bogendoerfer 62cde4
+		}
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+		cur_value = le32_to_cpu(*((__le32 *)buf));
Thomas Bogendoerfer 62cde4
+		DP_VERBOSE(cdev, NETIF_MSG_DRV,
Thomas Bogendoerfer 62cde4
+			   "NVM %08x: %08x -> %08x [Value %08x Mask %08x]\n",
Thomas Bogendoerfer 62cde4
+			   nvm_image.start_addr + offset, cur_value,
Thomas Bogendoerfer 62cde4
+			   (cur_value & ~mask) | (value & mask), value, mask);
Thomas Bogendoerfer 62cde4
+		value = (value & mask) | (cur_value & ~mask);
Thomas Bogendoerfer 62cde4
+		rc = qed_mcp_nvm_write(cdev, QED_NVM_WRITE_NVRAM,
Thomas Bogendoerfer 62cde4
+				       nvm_image.start_addr + offset,
Thomas Bogendoerfer 62cde4
+				       (u8 *)&value, 4);
Thomas Bogendoerfer 62cde4
+		if (rc) {
Thomas Bogendoerfer 62cde4
+			DP_ERR(cdev, "Failed writing to %08x\n",
Thomas Bogendoerfer 62cde4
+			       nvm_image.start_addr + offset);
Thomas Bogendoerfer 62cde4
+			goto exit;
Thomas Bogendoerfer 62cde4
+		}
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+		len--;
Thomas Bogendoerfer 62cde4
+	}
Thomas Bogendoerfer 62cde4
+exit:
Thomas Bogendoerfer 62cde4
+	return rc;
Thomas Bogendoerfer 62cde4
+}
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+/* Binary file format -
Thomas Bogendoerfer 62cde4
+ *     /----------------------------------------------------------------------\
Thomas Bogendoerfer 62cde4
+ * 0B  |                       0x3 [command index]                            |
Thomas Bogendoerfer 62cde4
+ * 4B  | b'0: check_response?   | b'1-31  reserved                            |
Thomas Bogendoerfer 62cde4
+ * 8B  | File-type |                   reserved                               |
Thomas Bogendoerfer 62cde4
+ *     \----------------------------------------------------------------------/
Thomas Bogendoerfer 62cde4
+ *     Start a new file of the provided type
Thomas Bogendoerfer 62cde4
+ */
Thomas Bogendoerfer 62cde4
+static int qed_nvm_flash_image_file_start(struct qed_dev *cdev,
Thomas Bogendoerfer 62cde4
+					  const u8 **data, bool *check_resp)
Thomas Bogendoerfer 62cde4
+{
Thomas Bogendoerfer 62cde4
+	int rc;
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	*data += 4;
Thomas Bogendoerfer 62cde4
+	*check_resp = !!(**data & BIT(0));
Thomas Bogendoerfer 62cde4
+	*data += 4;
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	DP_VERBOSE(cdev, NETIF_MSG_DRV,
Thomas Bogendoerfer 62cde4
+		   "About to start a new file of type %02x\n", **data);
Thomas Bogendoerfer 62cde4
+	rc = qed_mcp_nvm_put_file_begin(cdev, **data);
Thomas Bogendoerfer 62cde4
+	*data += 4;
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	return rc;
Thomas Bogendoerfer 62cde4
+}
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+/* Binary file format -
Thomas Bogendoerfer 62cde4
+ *     /----------------------------------------------------------------------\
Thomas Bogendoerfer 62cde4
+ * 0B  |                       0x2 [command index]                            |
Thomas Bogendoerfer 62cde4
+ * 4B  |                       Length in bytes                                |
Thomas Bogendoerfer 62cde4
+ * 8B  | b'0: check_response?   | b'1-31  reserved                            |
Thomas Bogendoerfer 62cde4
+ * 12B |                       Offset in bytes                                |
Thomas Bogendoerfer 62cde4
+ * 16B |                       Data ...                                       |
Thomas Bogendoerfer 62cde4
+ *     \----------------------------------------------------------------------/
Thomas Bogendoerfer 62cde4
+ *     Write data as part of a file that was previously started. Data should be
Thomas Bogendoerfer 62cde4
+ *     of length equal to that provided in the message
Thomas Bogendoerfer 62cde4
+ */
Thomas Bogendoerfer 62cde4
+static int qed_nvm_flash_image_file_data(struct qed_dev *cdev,
Thomas Bogendoerfer 62cde4
+					 const u8 **data, bool *check_resp)
Thomas Bogendoerfer 62cde4
+{
Thomas Bogendoerfer 62cde4
+	u32 offset, len;
Thomas Bogendoerfer 62cde4
+	int rc;
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	*data += 4;
Thomas Bogendoerfer 62cde4
+	len = *((u32 *)(*data));
Thomas Bogendoerfer 62cde4
+	*data += 4;
Thomas Bogendoerfer 62cde4
+	*check_resp = !!(**data & BIT(0));
Thomas Bogendoerfer 62cde4
+	*data += 4;
Thomas Bogendoerfer 62cde4
+	offset = *((u32 *)(*data));
Thomas Bogendoerfer 62cde4
+	*data += 4;
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	DP_VERBOSE(cdev, NETIF_MSG_DRV,
Thomas Bogendoerfer 62cde4
+		   "About to write File-data: %08x bytes to offset %08x\n",
Thomas Bogendoerfer 62cde4
+		   len, offset);
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	rc = qed_mcp_nvm_write(cdev, QED_PUT_FILE_DATA, offset,
Thomas Bogendoerfer 62cde4
+			       (char *)(*data), len);
Thomas Bogendoerfer 62cde4
+	*data += len;
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	return rc;
Thomas Bogendoerfer 62cde4
+}
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+/* Binary file format [General header] -
Thomas Bogendoerfer 62cde4
+ *     /----------------------------------------------------------------------\
Thomas Bogendoerfer 62cde4
+ * 0B  |                       QED_NVM_SIGNATURE                              |
Thomas Bogendoerfer 62cde4
+ * 4B  |                       Length in bytes                                |
Thomas Bogendoerfer 62cde4
+ * 8B  | Highest command in this batchfile |          Reserved                |
Thomas Bogendoerfer 62cde4
+ *     \----------------------------------------------------------------------/
Thomas Bogendoerfer 62cde4
+ */
Thomas Bogendoerfer 62cde4
+static int qed_nvm_flash_image_validate(struct qed_dev *cdev,
Thomas Bogendoerfer 62cde4
+					const struct firmware *image,
Thomas Bogendoerfer 62cde4
+					const u8 **data)
Thomas Bogendoerfer 62cde4
+{
Thomas Bogendoerfer 62cde4
+	u32 signature, len;
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	/* Check minimum size */
Thomas Bogendoerfer 62cde4
+	if (image->size < 12) {
Thomas Bogendoerfer 62cde4
+		DP_ERR(cdev, "Image is too short [%08x]\n", (u32)image->size);
Thomas Bogendoerfer 62cde4
+		return -EINVAL;
Thomas Bogendoerfer 62cde4
+	}
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	/* Check signature */
Thomas Bogendoerfer 62cde4
+	signature = *((u32 *)(*data));
Thomas Bogendoerfer 62cde4
+	if (signature != QED_NVM_SIGNATURE) {
Thomas Bogendoerfer 62cde4
+		DP_ERR(cdev, "Wrong signature '%08x'\n", signature);
Thomas Bogendoerfer 62cde4
+		return -EINVAL;
Thomas Bogendoerfer 62cde4
+	}
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	*data += 4;
Thomas Bogendoerfer 62cde4
+	/* Validate internal size equals the image-size */
Thomas Bogendoerfer 62cde4
+	len = *((u32 *)(*data));
Thomas Bogendoerfer 62cde4
+	if (len != image->size) {
Thomas Bogendoerfer 62cde4
+		DP_ERR(cdev, "Size mismatch: internal = %08x image = %08x\n",
Thomas Bogendoerfer 62cde4
+		       len, (u32)image->size);
Thomas Bogendoerfer 62cde4
+		return -EINVAL;
Thomas Bogendoerfer 62cde4
+	}
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	*data += 4;
Thomas Bogendoerfer 62cde4
+	/* Make sure driver familiar with all commands necessary for this */
Thomas Bogendoerfer 62cde4
+	if (*((u16 *)(*data)) >= QED_NVM_FLASH_CMD_NVM_MAX) {
Thomas Bogendoerfer 62cde4
+		DP_ERR(cdev, "File contains unsupported commands [Need %04x]\n",
Thomas Bogendoerfer 62cde4
+		       *((u16 *)(*data)));
Thomas Bogendoerfer 62cde4
+		return -EINVAL;
Thomas Bogendoerfer 62cde4
+	}
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	*data += 4;
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	return 0;
Thomas Bogendoerfer 62cde4
+}
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+static int qed_nvm_flash(struct qed_dev *cdev, const char *name)
Thomas Bogendoerfer 62cde4
+{
Thomas Bogendoerfer 62cde4
+	const struct firmware *image;
Thomas Bogendoerfer 62cde4
+	const u8 *data, *data_end;
Thomas Bogendoerfer 62cde4
+	u32 cmd_type;
Thomas Bogendoerfer 62cde4
+	int rc;
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	rc = request_firmware(&image, name, &cdev->pdev->dev);
Thomas Bogendoerfer 62cde4
+	if (rc) {
Thomas Bogendoerfer 62cde4
+		DP_ERR(cdev, "Failed to find '%s'\n", name);
Thomas Bogendoerfer 62cde4
+		return rc;
Thomas Bogendoerfer 62cde4
+	}
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	DP_VERBOSE(cdev, NETIF_MSG_DRV,
Thomas Bogendoerfer 62cde4
+		   "Flashing '%s' - firmware's data at %p, size is %08x\n",
Thomas Bogendoerfer 62cde4
+		   name, image->data, (u32)image->size);
Thomas Bogendoerfer 62cde4
+	data = image->data;
Thomas Bogendoerfer 62cde4
+	data_end = data + image->size;
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	rc = qed_nvm_flash_image_validate(cdev, image, &data);
Thomas Bogendoerfer 62cde4
+	if (rc)
Thomas Bogendoerfer 62cde4
+		goto exit;
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	while (data < data_end) {
Thomas Bogendoerfer 62cde4
+		bool check_resp = false;
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+		/* Parse the actual command */
Thomas Bogendoerfer 62cde4
+		cmd_type = *((u32 *)data);
Thomas Bogendoerfer 62cde4
+		switch (cmd_type) {
Thomas Bogendoerfer 62cde4
+		case QED_NVM_FLASH_CMD_FILE_DATA:
Thomas Bogendoerfer 62cde4
+			rc = qed_nvm_flash_image_file_data(cdev, &data,
Thomas Bogendoerfer 62cde4
+							   &check_resp);
Thomas Bogendoerfer 62cde4
+			break;
Thomas Bogendoerfer 62cde4
+		case QED_NVM_FLASH_CMD_FILE_START:
Thomas Bogendoerfer 62cde4
+			rc = qed_nvm_flash_image_file_start(cdev, &data,
Thomas Bogendoerfer 62cde4
+							    &check_resp);
Thomas Bogendoerfer 62cde4
+			break;
Thomas Bogendoerfer 62cde4
+		case QED_NVM_FLASH_CMD_NVM_CHANGE:
Thomas Bogendoerfer 62cde4
+			rc = qed_nvm_flash_image_access(cdev, &data,
Thomas Bogendoerfer 62cde4
+							&check_resp);
Thomas Bogendoerfer 62cde4
+			break;
Thomas Bogendoerfer 62cde4
+		default:
Thomas Bogendoerfer 62cde4
+			DP_ERR(cdev, "Unknown command %08x\n", cmd_type);
Thomas Bogendoerfer 62cde4
+			rc = -EINVAL;
Thomas Bogendoerfer 62cde4
+			goto exit;
Thomas Bogendoerfer 62cde4
+		}
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+		if (rc) {
Thomas Bogendoerfer 62cde4
+			DP_ERR(cdev, "Command %08x failed\n", cmd_type);
Thomas Bogendoerfer 62cde4
+			goto exit;
Thomas Bogendoerfer 62cde4
+		}
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+		/* Check response if needed */
Thomas Bogendoerfer 62cde4
+		if (check_resp) {
Thomas Bogendoerfer 62cde4
+			u32 mcp_response = 0;
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+			if (qed_mcp_nvm_resp(cdev, (u8 *)&mcp_response)) {
Thomas Bogendoerfer 62cde4
+				DP_ERR(cdev, "Failed getting MCP response\n");
Thomas Bogendoerfer 62cde4
+				rc = -EINVAL;
Thomas Bogendoerfer 62cde4
+				goto exit;
Thomas Bogendoerfer 62cde4
+			}
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+			switch (mcp_response & FW_MSG_CODE_MASK) {
Thomas Bogendoerfer 62cde4
+			case FW_MSG_CODE_OK:
Thomas Bogendoerfer 62cde4
+			case FW_MSG_CODE_NVM_OK:
Thomas Bogendoerfer 62cde4
+			case FW_MSG_CODE_NVM_PUT_FILE_FINISH_OK:
Thomas Bogendoerfer 62cde4
+			case FW_MSG_CODE_PHY_OK:
Thomas Bogendoerfer 62cde4
+				break;
Thomas Bogendoerfer 62cde4
+			default:
Thomas Bogendoerfer 62cde4
+				DP_ERR(cdev, "MFW returns error: %08x\n",
Thomas Bogendoerfer 62cde4
+				       mcp_response);
Thomas Bogendoerfer 62cde4
+				rc = -EINVAL;
Thomas Bogendoerfer 62cde4
+				goto exit;
Thomas Bogendoerfer 62cde4
+			}
Thomas Bogendoerfer 62cde4
+		}
Thomas Bogendoerfer 62cde4
+	}
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+exit:
Thomas Bogendoerfer 62cde4
+	release_firmware(image);
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+	return rc;
Thomas Bogendoerfer 62cde4
+}
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
 static int qed_nvm_get_image(struct qed_dev *cdev, enum qed_nvm_images type,
Thomas Bogendoerfer 62cde4
 			     u8 *buf, u16 len)
Thomas Bogendoerfer 62cde4
 {
Thomas Bogendoerfer 62cde4
@@ -1719,6 +2056,7 @@ const struct qed_common_ops qed_common_o
Thomas Bogendoerfer 62cde4
 	.dbg_all_data_size = &qed_dbg_all_data_size,
Thomas Bogendoerfer 62cde4
 	.chain_alloc = &qed_chain_alloc,
Thomas Bogendoerfer 62cde4
 	.chain_free = &qed_chain_free,
Thomas Bogendoerfer 62cde4
+	.nvm_flash = &qed_nvm_flash,
Thomas Bogendoerfer 62cde4
 	.nvm_get_image = &qed_nvm_get_image,
Thomas Bogendoerfer 62cde4
 	.set_coalesce = &qed_set_coalesce,
Thomas Bogendoerfer 62cde4
 	.set_led = &qed_set_led,
Thomas Bogendoerfer 62cde4
--- a/include/linux/qed/qed_if.h
Thomas Bogendoerfer 62cde4
+++ b/include/linux/qed/qed_if.h
Thomas Bogendoerfer 62cde4
@@ -483,6 +483,15 @@ struct qed_int_info {
Thomas Bogendoerfer 62cde4
 	u8			used_cnt;
Thomas Bogendoerfer 62cde4
 };
Thomas Bogendoerfer 62cde4
 
Thomas Bogendoerfer 62cde4
+#define QED_NVM_SIGNATURE 0x12435687
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+enum qed_nvm_flash_cmd {
Thomas Bogendoerfer 62cde4
+	QED_NVM_FLASH_CMD_FILE_DATA = 0x2,
Thomas Bogendoerfer 62cde4
+	QED_NVM_FLASH_CMD_FILE_START = 0x3,
Thomas Bogendoerfer 62cde4
+	QED_NVM_FLASH_CMD_NVM_CHANGE = 0x4,
Thomas Bogendoerfer 62cde4
+	QED_NVM_FLASH_CMD_NVM_MAX,
Thomas Bogendoerfer 62cde4
+};
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
 struct qed_common_cb_ops {
Thomas Bogendoerfer 62cde4
 	void (*arfs_filter_op)(void *dev, void *fltr, u8 fw_rc);
Thomas Bogendoerfer 62cde4
 	void	(*link_update)(void			*dev,
Thomas Bogendoerfer 62cde4
@@ -658,6 +667,16 @@ struct qed_common_ops {
Thomas Bogendoerfer 62cde4
 				      struct qed_chain *p_chain);
Thomas Bogendoerfer 62cde4
 
Thomas Bogendoerfer 62cde4
 /**
Thomas Bogendoerfer 62cde4
+ * @brief nvm_flash - Flash nvm data.
Thomas Bogendoerfer 62cde4
+ *
Thomas Bogendoerfer 62cde4
+ * @param cdev
Thomas Bogendoerfer 62cde4
+ * @param name - file containing the data
Thomas Bogendoerfer 62cde4
+ *
Thomas Bogendoerfer 62cde4
+ * @return 0 on success, error otherwise.
Thomas Bogendoerfer 62cde4
+ */
Thomas Bogendoerfer 62cde4
+	int (*nvm_flash)(struct qed_dev *cdev, const char *name);
Thomas Bogendoerfer 62cde4
+
Thomas Bogendoerfer 62cde4
+/**
Thomas Bogendoerfer 62cde4
  * @brief nvm_get_image - reads an entire image from nvram
Thomas Bogendoerfer 62cde4
  *
Thomas Bogendoerfer 62cde4
  * @param cdev