|
@@ -45,6 +45,7 @@
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/etherdevice.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/vmalloc.h>
|
|
#include <linux/crash_dump.h>
|
|
#include <linux/crash_dump.h>
|
|
|
|
+#include <linux/crc32.h>
|
|
#include <linux/qed/qed_if.h>
|
|
#include <linux/qed/qed_if.h>
|
|
#include <linux/qed/qed_ll2_if.h>
|
|
#include <linux/qed/qed_ll2_if.h>
|
|
|
|
|
|
@@ -1553,6 +1554,342 @@ static int qed_drain(struct qed_dev *cdev)
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static u32 qed_nvm_flash_image_access_crc(struct qed_dev *cdev,
|
|
|
|
+ struct qed_nvm_image_att *nvm_image,
|
|
|
|
+ u32 *crc)
|
|
|
|
+{
|
|
|
|
+ u8 *buf = NULL;
|
|
|
|
+ int rc, j;
|
|
|
|
+ u32 val;
|
|
|
|
+
|
|
|
|
+ /* Allocate a buffer for holding the nvram image */
|
|
|
|
+ buf = kzalloc(nvm_image->length, GFP_KERNEL);
|
|
|
|
+ if (!buf)
|
|
|
|
+ return -ENOMEM;
|
|
|
|
+
|
|
|
|
+ /* Read image into buffer */
|
|
|
|
+ rc = qed_mcp_nvm_read(cdev, nvm_image->start_addr,
|
|
|
|
+ buf, nvm_image->length);
|
|
|
|
+ if (rc) {
|
|
|
|
+ DP_ERR(cdev, "Failed reading image from nvm\n");
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Convert the buffer into big-endian format (excluding the
|
|
|
|
+ * closing 4 bytes of CRC).
|
|
|
|
+ */
|
|
|
|
+ for (j = 0; j < nvm_image->length - 4; j += 4) {
|
|
|
|
+ val = cpu_to_be32(*(u32 *)&buf[j]);
|
|
|
|
+ *(u32 *)&buf[j] = val;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Calc CRC for the "actual" image buffer, i.e. not including
|
|
|
|
+ * the last 4 CRC bytes.
|
|
|
|
+ */
|
|
|
|
+ *crc = (~cpu_to_be32(crc32(0xffffffff, buf, nvm_image->length - 4)));
|
|
|
|
+
|
|
|
|
+out:
|
|
|
|
+ kfree(buf);
|
|
|
|
+
|
|
|
|
+ return rc;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Binary file format -
|
|
|
|
+ * /----------------------------------------------------------------------\
|
|
|
|
+ * 0B | 0x4 [command index] |
|
|
|
|
+ * 4B | image_type | Options | Number of register settings |
|
|
|
|
+ * 8B | Value |
|
|
|
|
+ * 12B | Mask |
|
|
|
|
+ * 16B | Offset |
|
|
|
|
+ * \----------------------------------------------------------------------/
|
|
|
|
+ * There can be several Value-Mask-Offset sets as specified by 'Number of...'.
|
|
|
|
+ * Options - 0'b - Calculate & Update CRC for image
|
|
|
|
+ */
|
|
|
|
+static int qed_nvm_flash_image_access(struct qed_dev *cdev, const u8 **data,
|
|
|
|
+ bool *check_resp)
|
|
|
|
+{
|
|
|
|
+ struct qed_nvm_image_att nvm_image;
|
|
|
|
+ struct qed_hwfn *p_hwfn;
|
|
|
|
+ bool is_crc = false;
|
|
|
|
+ u32 image_type;
|
|
|
|
+ int rc = 0, i;
|
|
|
|
+ u16 len;
|
|
|
|
+
|
|
|
|
+ *data += 4;
|
|
|
|
+ image_type = **data;
|
|
|
|
+ p_hwfn = QED_LEADING_HWFN(cdev);
|
|
|
|
+ for (i = 0; i < p_hwfn->nvm_info.num_images; i++)
|
|
|
|
+ if (image_type == p_hwfn->nvm_info.image_att[i].image_type)
|
|
|
|
+ break;
|
|
|
|
+ if (i == p_hwfn->nvm_info.num_images) {
|
|
|
|
+ DP_ERR(cdev, "Failed to find nvram image of type %08x\n",
|
|
|
|
+ image_type);
|
|
|
|
+ return -ENOENT;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ nvm_image.start_addr = p_hwfn->nvm_info.image_att[i].nvm_start_addr;
|
|
|
|
+ nvm_image.length = p_hwfn->nvm_info.image_att[i].len;
|
|
|
|
+
|
|
|
|
+ DP_VERBOSE(cdev, NETIF_MSG_DRV,
|
|
|
|
+ "Read image %02x; type = %08x; NVM [%08x,...,%08x]\n",
|
|
|
|
+ **data, image_type, nvm_image.start_addr,
|
|
|
|
+ nvm_image.start_addr + nvm_image.length - 1);
|
|
|
|
+ (*data)++;
|
|
|
|
+ is_crc = !!(**data & BIT(0));
|
|
|
|
+ (*data)++;
|
|
|
|
+ len = *((u16 *)*data);
|
|
|
|
+ *data += 2;
|
|
|
|
+ if (is_crc) {
|
|
|
|
+ u32 crc = 0;
|
|
|
|
+
|
|
|
|
+ rc = qed_nvm_flash_image_access_crc(cdev, &nvm_image, &crc);
|
|
|
|
+ if (rc) {
|
|
|
|
+ DP_ERR(cdev, "Failed calculating CRC, rc = %d\n", rc);
|
|
|
|
+ goto exit;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ rc = qed_mcp_nvm_write(cdev, QED_NVM_WRITE_NVRAM,
|
|
|
|
+ (nvm_image.start_addr +
|
|
|
|
+ nvm_image.length - 4), (u8 *)&crc, 4);
|
|
|
|
+ if (rc)
|
|
|
|
+ DP_ERR(cdev, "Failed writing to %08x, rc = %d\n",
|
|
|
|
+ nvm_image.start_addr + nvm_image.length - 4, rc);
|
|
|
|
+ goto exit;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Iterate over the values for setting */
|
|
|
|
+ while (len) {
|
|
|
|
+ u32 offset, mask, value, cur_value;
|
|
|
|
+ u8 buf[4];
|
|
|
|
+
|
|
|
|
+ value = *((u32 *)*data);
|
|
|
|
+ *data += 4;
|
|
|
|
+ mask = *((u32 *)*data);
|
|
|
|
+ *data += 4;
|
|
|
|
+ offset = *((u32 *)*data);
|
|
|
|
+ *data += 4;
|
|
|
|
+
|
|
|
|
+ rc = qed_mcp_nvm_read(cdev, nvm_image.start_addr + offset, buf,
|
|
|
|
+ 4);
|
|
|
|
+ if (rc) {
|
|
|
|
+ DP_ERR(cdev, "Failed reading from %08x\n",
|
|
|
|
+ nvm_image.start_addr + offset);
|
|
|
|
+ goto exit;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ cur_value = le32_to_cpu(*((__le32 *)buf));
|
|
|
|
+ DP_VERBOSE(cdev, NETIF_MSG_DRV,
|
|
|
|
+ "NVM %08x: %08x -> %08x [Value %08x Mask %08x]\n",
|
|
|
|
+ nvm_image.start_addr + offset, cur_value,
|
|
|
|
+ (cur_value & ~mask) | (value & mask), value, mask);
|
|
|
|
+ value = (value & mask) | (cur_value & ~mask);
|
|
|
|
+ rc = qed_mcp_nvm_write(cdev, QED_NVM_WRITE_NVRAM,
|
|
|
|
+ nvm_image.start_addr + offset,
|
|
|
|
+ (u8 *)&value, 4);
|
|
|
|
+ if (rc) {
|
|
|
|
+ DP_ERR(cdev, "Failed writing to %08x\n",
|
|
|
|
+ nvm_image.start_addr + offset);
|
|
|
|
+ goto exit;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ len--;
|
|
|
|
+ }
|
|
|
|
+exit:
|
|
|
|
+ return rc;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Binary file format -
|
|
|
|
+ * /----------------------------------------------------------------------\
|
|
|
|
+ * 0B | 0x3 [command index] |
|
|
|
|
+ * 4B | b'0: check_response? | b'1-31 reserved |
|
|
|
|
+ * 8B | File-type | reserved |
|
|
|
|
+ * \----------------------------------------------------------------------/
|
|
|
|
+ * Start a new file of the provided type
|
|
|
|
+ */
|
|
|
|
+static int qed_nvm_flash_image_file_start(struct qed_dev *cdev,
|
|
|
|
+ const u8 **data, bool *check_resp)
|
|
|
|
+{
|
|
|
|
+ int rc;
|
|
|
|
+
|
|
|
|
+ *data += 4;
|
|
|
|
+ *check_resp = !!(**data & BIT(0));
|
|
|
|
+ *data += 4;
|
|
|
|
+
|
|
|
|
+ DP_VERBOSE(cdev, NETIF_MSG_DRV,
|
|
|
|
+ "About to start a new file of type %02x\n", **data);
|
|
|
|
+ rc = qed_mcp_nvm_put_file_begin(cdev, **data);
|
|
|
|
+ *data += 4;
|
|
|
|
+
|
|
|
|
+ return rc;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Binary file format -
|
|
|
|
+ * /----------------------------------------------------------------------\
|
|
|
|
+ * 0B | 0x2 [command index] |
|
|
|
|
+ * 4B | Length in bytes |
|
|
|
|
+ * 8B | b'0: check_response? | b'1-31 reserved |
|
|
|
|
+ * 12B | Offset in bytes |
|
|
|
|
+ * 16B | Data ... |
|
|
|
|
+ * \----------------------------------------------------------------------/
|
|
|
|
+ * Write data as part of a file that was previously started. Data should be
|
|
|
|
+ * of length equal to that provided in the message
|
|
|
|
+ */
|
|
|
|
+static int qed_nvm_flash_image_file_data(struct qed_dev *cdev,
|
|
|
|
+ const u8 **data, bool *check_resp)
|
|
|
|
+{
|
|
|
|
+ u32 offset, len;
|
|
|
|
+ int rc;
|
|
|
|
+
|
|
|
|
+ *data += 4;
|
|
|
|
+ len = *((u32 *)(*data));
|
|
|
|
+ *data += 4;
|
|
|
|
+ *check_resp = !!(**data & BIT(0));
|
|
|
|
+ *data += 4;
|
|
|
|
+ offset = *((u32 *)(*data));
|
|
|
|
+ *data += 4;
|
|
|
|
+
|
|
|
|
+ DP_VERBOSE(cdev, NETIF_MSG_DRV,
|
|
|
|
+ "About to write File-data: %08x bytes to offset %08x\n",
|
|
|
|
+ len, offset);
|
|
|
|
+
|
|
|
|
+ rc = qed_mcp_nvm_write(cdev, QED_PUT_FILE_DATA, offset,
|
|
|
|
+ (char *)(*data), len);
|
|
|
|
+ *data += len;
|
|
|
|
+
|
|
|
|
+ return rc;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+/* Binary file format [General header] -
|
|
|
|
+ * /----------------------------------------------------------------------\
|
|
|
|
+ * 0B | QED_NVM_SIGNATURE |
|
|
|
|
+ * 4B | Length in bytes |
|
|
|
|
+ * 8B | Highest command in this batchfile | Reserved |
|
|
|
|
+ * \----------------------------------------------------------------------/
|
|
|
|
+ */
|
|
|
|
+static int qed_nvm_flash_image_validate(struct qed_dev *cdev,
|
|
|
|
+ const struct firmware *image,
|
|
|
|
+ const u8 **data)
|
|
|
|
+{
|
|
|
|
+ u32 signature, len;
|
|
|
|
+
|
|
|
|
+ /* Check minimum size */
|
|
|
|
+ if (image->size < 12) {
|
|
|
|
+ DP_ERR(cdev, "Image is too short [%08x]\n", (u32)image->size);
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Check signature */
|
|
|
|
+ signature = *((u32 *)(*data));
|
|
|
|
+ if (signature != QED_NVM_SIGNATURE) {
|
|
|
|
+ DP_ERR(cdev, "Wrong signature '%08x'\n", signature);
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ *data += 4;
|
|
|
|
+ /* Validate internal size equals the image-size */
|
|
|
|
+ len = *((u32 *)(*data));
|
|
|
|
+ if (len != image->size) {
|
|
|
|
+ DP_ERR(cdev, "Size mismatch: internal = %08x image = %08x\n",
|
|
|
|
+ len, (u32)image->size);
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ *data += 4;
|
|
|
|
+ /* Make sure driver familiar with all commands necessary for this */
|
|
|
|
+ if (*((u16 *)(*data)) >= QED_NVM_FLASH_CMD_NVM_MAX) {
|
|
|
|
+ DP_ERR(cdev, "File contains unsupported commands [Need %04x]\n",
|
|
|
|
+ *((u16 *)(*data)));
|
|
|
|
+ return -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ *data += 4;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int qed_nvm_flash(struct qed_dev *cdev, const char *name)
|
|
|
|
+{
|
|
|
|
+ const struct firmware *image;
|
|
|
|
+ const u8 *data, *data_end;
|
|
|
|
+ u32 cmd_type;
|
|
|
|
+ int rc;
|
|
|
|
+
|
|
|
|
+ rc = request_firmware(&image, name, &cdev->pdev->dev);
|
|
|
|
+ if (rc) {
|
|
|
|
+ DP_ERR(cdev, "Failed to find '%s'\n", name);
|
|
|
|
+ return rc;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ DP_VERBOSE(cdev, NETIF_MSG_DRV,
|
|
|
|
+ "Flashing '%s' - firmware's data at %p, size is %08x\n",
|
|
|
|
+ name, image->data, (u32)image->size);
|
|
|
|
+ data = image->data;
|
|
|
|
+ data_end = data + image->size;
|
|
|
|
+
|
|
|
|
+ rc = qed_nvm_flash_image_validate(cdev, image, &data);
|
|
|
|
+ if (rc)
|
|
|
|
+ goto exit;
|
|
|
|
+
|
|
|
|
+ while (data < data_end) {
|
|
|
|
+ bool check_resp = false;
|
|
|
|
+
|
|
|
|
+ /* Parse the actual command */
|
|
|
|
+ cmd_type = *((u32 *)data);
|
|
|
|
+ switch (cmd_type) {
|
|
|
|
+ case QED_NVM_FLASH_CMD_FILE_DATA:
|
|
|
|
+ rc = qed_nvm_flash_image_file_data(cdev, &data,
|
|
|
|
+ &check_resp);
|
|
|
|
+ break;
|
|
|
|
+ case QED_NVM_FLASH_CMD_FILE_START:
|
|
|
|
+ rc = qed_nvm_flash_image_file_start(cdev, &data,
|
|
|
|
+ &check_resp);
|
|
|
|
+ break;
|
|
|
|
+ case QED_NVM_FLASH_CMD_NVM_CHANGE:
|
|
|
|
+ rc = qed_nvm_flash_image_access(cdev, &data,
|
|
|
|
+ &check_resp);
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ DP_ERR(cdev, "Unknown command %08x\n", cmd_type);
|
|
|
|
+ rc = -EINVAL;
|
|
|
|
+ goto exit;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ if (rc) {
|
|
|
|
+ DP_ERR(cdev, "Command %08x failed\n", cmd_type);
|
|
|
|
+ goto exit;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /* Check response if needed */
|
|
|
|
+ if (check_resp) {
|
|
|
|
+ u32 mcp_response = 0;
|
|
|
|
+
|
|
|
|
+ if (qed_mcp_nvm_resp(cdev, (u8 *)&mcp_response)) {
|
|
|
|
+ DP_ERR(cdev, "Failed getting MCP response\n");
|
|
|
|
+ rc = -EINVAL;
|
|
|
|
+ goto exit;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ switch (mcp_response & FW_MSG_CODE_MASK) {
|
|
|
|
+ case FW_MSG_CODE_OK:
|
|
|
|
+ case FW_MSG_CODE_NVM_OK:
|
|
|
|
+ case FW_MSG_CODE_NVM_PUT_FILE_FINISH_OK:
|
|
|
|
+ case FW_MSG_CODE_PHY_OK:
|
|
|
|
+ break;
|
|
|
|
+ default:
|
|
|
|
+ DP_ERR(cdev, "MFW returns error: %08x\n",
|
|
|
|
+ mcp_response);
|
|
|
|
+ rc = -EINVAL;
|
|
|
|
+ goto exit;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+exit:
|
|
|
|
+ release_firmware(image);
|
|
|
|
+
|
|
|
|
+ return rc;
|
|
|
|
+}
|
|
|
|
+
|
|
static int qed_nvm_get_image(struct qed_dev *cdev, enum qed_nvm_images type,
|
|
static int qed_nvm_get_image(struct qed_dev *cdev, enum qed_nvm_images type,
|
|
u8 *buf, u16 len)
|
|
u8 *buf, u16 len)
|
|
{
|
|
{
|
|
@@ -1719,6 +2056,7 @@ const struct qed_common_ops qed_common_ops_pass = {
|
|
.dbg_all_data_size = &qed_dbg_all_data_size,
|
|
.dbg_all_data_size = &qed_dbg_all_data_size,
|
|
.chain_alloc = &qed_chain_alloc,
|
|
.chain_alloc = &qed_chain_alloc,
|
|
.chain_free = &qed_chain_free,
|
|
.chain_free = &qed_chain_free,
|
|
|
|
+ .nvm_flash = &qed_nvm_flash,
|
|
.nvm_get_image = &qed_nvm_get_image,
|
|
.nvm_get_image = &qed_nvm_get_image,
|
|
.set_coalesce = &qed_set_coalesce,
|
|
.set_coalesce = &qed_set_coalesce,
|
|
.set_led = &qed_set_led,
|
|
.set_led = &qed_set_led,
|