diff options
author | David S. Miller <davem@davemloft.net> | 2018-03-29 14:29:56 -0400 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2018-03-29 14:29:56 -0400 |
commit | 5593e3340f32f43eede742a96cbf9603fbe6003b (patch) | |
tree | 3bc06b346cdbcfb07b2c51f62122eb4b6df775fa | |
parent | 50bc60cb155c813157fdca5b3b05194cd325d3e9 (diff) | |
parent | ccfa110cfecd1a200dc302aedbbda859fb237a09 (diff) | |
download | linux-5593e3340f32f43eede742a96cbf9603fbe6003b.tar.gz linux-5593e3340f32f43eede742a96cbf9603fbe6003b.tar.bz2 linux-5593e3340f32f43eede742a96cbf9603fbe6003b.zip |
Merge branch 'qed-flash-upgrade-support'
Sudarsana Reddy Kalluru says:
====================
qed*: Flash upgrade support.
The patch series adds adapter flash upgrade support for qed/qede drivers.
Please consider applying it to net-next branch.
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/net/ethernet/qlogic/qed/qed.h | 15 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qed/qed_dev.c | 24 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qed/qed_hsi.h | 7 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qed/qed_main.c | 338 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qed/qed_mcp.c | 219 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qed/qed_mcp.h | 56 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qed/qed_selftest.c | 9 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qede/qede_ethtool.c | 9 | ||||
-rw-r--r-- | include/linux/qed/qed_if.h | 19 |
9 files changed, 656 insertions, 40 deletions
diff --git a/drivers/net/ethernet/qlogic/qed/qed.h b/drivers/net/ethernet/qlogic/qed/qed.h index 69488554f4b9..e07460a68d30 100644 --- a/drivers/net/ethernet/qlogic/qed/qed.h +++ b/drivers/net/ethernet/qlogic/qed/qed.h @@ -81,6 +81,13 @@ enum qed_coalescing_mode { QED_COAL_MODE_ENABLE }; +enum qed_nvm_cmd { + QED_PUT_FILE_BEGIN = DRV_MSG_CODE_NVM_PUT_FILE_BEGIN, + QED_PUT_FILE_DATA = DRV_MSG_CODE_NVM_PUT_FILE_DATA, + QED_NVM_WRITE_NVRAM = DRV_MSG_CODE_NVM_WRITE_NVRAM, + QED_GET_MCP_NVM_RESP = 0xFFFFFF00 +}; + struct qed_eth_cb_ops; struct qed_dev_info; union qed_mcp_protocol_stats; @@ -437,6 +444,11 @@ enum BAR_ID { BAR_ID_1 /* Used for doorbells */ }; +struct qed_nvm_image_info { + u32 num_images; + struct bist_nvm_image_att *image_att; +}; + #define DRV_MODULE_VERSION \ __stringify(QED_MAJOR_VERSION) "." \ __stringify(QED_MINOR_VERSION) "." \ @@ -561,6 +573,9 @@ struct qed_hwfn { /* L2-related */ struct qed_l2_info *p_l2_info; + /* Nvm images number and attributes */ + struct qed_nvm_image_info nvm_info; + struct qed_ptt *p_arfs_ptt; struct qed_simd_fp_handler simd_proto_handler[64]; diff --git a/drivers/net/ethernet/qlogic/qed/qed_dev.c b/drivers/net/ethernet/qlogic/qed/qed_dev.c index de5527c0c1c7..d2ad5e92c74f 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_dev.c +++ b/drivers/net/ethernet/qlogic/qed/qed_dev.c @@ -2932,6 +2932,12 @@ static int qed_get_dev_info(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) return 0; } +static void qed_nvm_info_free(struct qed_hwfn *p_hwfn) +{ + kfree(p_hwfn->nvm_info.image_att); + p_hwfn->nvm_info.image_att = NULL; +} + static int qed_hw_prepare_single(struct qed_hwfn *p_hwfn, void __iomem *p_regview, void __iomem *p_doorbells, @@ -2995,12 +3001,25 @@ static int qed_hw_prepare_single(struct qed_hwfn *p_hwfn, DP_NOTICE(p_hwfn, "Failed to initiate PF FLR\n"); } + /* NVRAM info initialization and population */ + if (IS_LEAD_HWFN(p_hwfn)) { + rc = qed_mcp_nvm_info_populate(p_hwfn); + if (rc) { + DP_NOTICE(p_hwfn, + "Failed to populate nvm info shadow\n"); + goto err2; + } + } + /* Allocate the init RT array and initialize the init-ops engine */ rc = qed_init_alloc(p_hwfn); if (rc) - goto err2; + goto err3; return rc; +err3: + if (IS_LEAD_HWFN(p_hwfn)) + qed_nvm_info_free(p_hwfn); err2: if (IS_LEAD_HWFN(p_hwfn)) qed_iov_free_hw_info(p_hwfn->cdev); @@ -3056,6 +3075,7 @@ int qed_hw_prepare(struct qed_dev *cdev, if (rc) { if (IS_PF(cdev)) { qed_init_free(p_hwfn); + qed_nvm_info_free(p_hwfn); qed_mcp_free(p_hwfn); qed_hw_hwfn_free(p_hwfn); } @@ -3088,6 +3108,8 @@ void qed_hw_remove(struct qed_dev *cdev) } qed_iov_free_hw_info(cdev); + + qed_nvm_info_free(p_hwfn); } static void qed_chain_free_next_ptr(struct qed_dev *cdev, diff --git a/drivers/net/ethernet/qlogic/qed/qed_hsi.h b/drivers/net/ethernet/qlogic/qed/qed_hsi.h index 2c6a679166e1..7f5ec42dde48 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_hsi.h +++ b/drivers/net/ethernet/qlogic/qed/qed_hsi.h @@ -12268,8 +12268,11 @@ struct public_drv_mb { #define DRV_MSG_CODE_VF_DISABLED_DONE 0xc0000000 #define DRV_MSG_CODE_CFG_VF_MSIX 0xc0010000 #define DRV_MSG_CODE_CFG_PF_VFS_MSIX 0xc0020000 +#define DRV_MSG_CODE_NVM_PUT_FILE_BEGIN 0x00010000 +#define DRV_MSG_CODE_NVM_PUT_FILE_DATA 0x00020000 #define DRV_MSG_CODE_NVM_GET_FILE_ATT 0x00030000 #define DRV_MSG_CODE_NVM_READ_NVRAM 0x00050000 +#define DRV_MSG_CODE_NVM_WRITE_NVRAM 0x00060000 #define DRV_MSG_CODE_MCP_RESET 0x00090000 #define DRV_MSG_CODE_SET_VERSION 0x000f0000 #define DRV_MSG_CODE_MCP_HALT 0x00100000 @@ -12323,7 +12326,6 @@ struct public_drv_mb { #define DRV_MSG_CODE_FEATURE_SUPPORT 0x00300000 #define DRV_MSG_CODE_GET_MFW_FEATURE_SUPPORT 0x00310000 - #define DRV_MSG_SEQ_NUMBER_MASK 0x0000ffff u32 drv_mb_param; @@ -12435,7 +12437,10 @@ struct public_drv_mb { #define FW_MSG_CODE_DRV_CFG_VF_MSIX_DONE 0xb0010000 #define FW_MSG_CODE_NVM_OK 0x00010000 +#define FW_MSG_CODE_NVM_PUT_FILE_FINISH_OK 0x00400000 +#define FW_MSG_CODE_PHY_OK 0x00110000 #define FW_MSG_CODE_OK 0x00160000 +#define FW_MSG_CODE_ERROR 0x00170000 #define FW_MSG_CODE_OS_WOL_SUPPORTED 0x00800000 #define FW_MSG_CODE_OS_WOL_NOT_SUPPORTED 0x00810000 diff --git a/drivers/net/ethernet/qlogic/qed/qed_main.c b/drivers/net/ethernet/qlogic/qed/qed_main.c index 27832885a87f..9854aa9139af 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_main.c +++ b/drivers/net/ethernet/qlogic/qed/qed_main.c @@ -45,6 +45,7 @@ #include <linux/etherdevice.h> #include <linux/vmalloc.h> #include <linux/crash_dump.h> +#include <linux/crc32.h> #include <linux/qed/qed_if.h> #include <linux/qed/qed_ll2_if.h> @@ -1553,6 +1554,342 @@ static int qed_drain(struct qed_dev *cdev) 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, 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, .chain_alloc = &qed_chain_alloc, .chain_free = &qed_chain_free, + .nvm_flash = &qed_nvm_flash, .nvm_get_image = &qed_nvm_get_image, .set_coalesce = &qed_set_coalesce, .set_led = &qed_set_led, diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.c b/drivers/net/ethernet/qlogic/qed/qed_mcp.c index 6f46cb11f349..ec0d425766a7 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.c +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.c @@ -569,6 +569,31 @@ int qed_mcp_cmd(struct qed_hwfn *p_hwfn, return 0; } +int qed_mcp_nvm_wr_cmd(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u32 cmd, + u32 param, + u32 *o_mcp_resp, + u32 *o_mcp_param, u32 i_txn_size, u32 *i_buf) +{ + struct qed_mcp_mb_params mb_params; + int rc; + + memset(&mb_params, 0, sizeof(mb_params)); + mb_params.cmd = cmd; + mb_params.param = param; + mb_params.p_data_src = i_buf; + mb_params.data_src_size = (u8)i_txn_size; + rc = qed_mcp_cmd_and_union(p_hwfn, p_ptt, &mb_params); + if (rc) + return rc; + + *o_mcp_resp = mb_params.mcp_resp; + *o_mcp_param = mb_params.mcp_param; + + return 0; +} + int qed_mcp_nvm_rd_cmd(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, u32 cmd, @@ -2261,6 +2286,102 @@ int qed_mcp_nvm_read(struct qed_dev *cdev, u32 addr, u8 *p_buf, u32 len) return rc; } +int qed_mcp_nvm_resp(struct qed_dev *cdev, u8 *p_buf) +{ + struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev); + struct qed_ptt *p_ptt; + + p_ptt = qed_ptt_acquire(p_hwfn); + if (!p_ptt) + return -EBUSY; + + memcpy(p_buf, &cdev->mcp_nvm_resp, sizeof(cdev->mcp_nvm_resp)); + qed_ptt_release(p_hwfn, p_ptt); + + return 0; +} + +int qed_mcp_nvm_put_file_begin(struct qed_dev *cdev, u32 addr) +{ + struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev); + struct qed_ptt *p_ptt; + u32 resp, param; + int rc; + + p_ptt = qed_ptt_acquire(p_hwfn); + if (!p_ptt) + return -EBUSY; + rc = qed_mcp_cmd(p_hwfn, p_ptt, DRV_MSG_CODE_NVM_PUT_FILE_BEGIN, addr, + &resp, ¶m); + cdev->mcp_nvm_resp = resp; + qed_ptt_release(p_hwfn, p_ptt); + + return rc; +} + +int qed_mcp_nvm_write(struct qed_dev *cdev, + u32 cmd, u32 addr, u8 *p_buf, u32 len) +{ + u32 buf_idx = 0, buf_size, nvm_cmd, nvm_offset, resp = 0, param; + struct qed_hwfn *p_hwfn = QED_LEADING_HWFN(cdev); + struct qed_ptt *p_ptt; + int rc = -EINVAL; + + p_ptt = qed_ptt_acquire(p_hwfn); + if (!p_ptt) + return -EBUSY; + + switch (cmd) { + case QED_PUT_FILE_DATA: + nvm_cmd = DRV_MSG_CODE_NVM_PUT_FILE_DATA; + break; + case QED_NVM_WRITE_NVRAM: + nvm_cmd = DRV_MSG_CODE_NVM_WRITE_NVRAM; + break; + default: + DP_NOTICE(p_hwfn, "Invalid nvm write command 0x%x\n", cmd); + rc = -EINVAL; + goto out; + } + + while (buf_idx < len) { + buf_size = min_t(u32, (len - buf_idx), MCP_DRV_NVM_BUF_LEN); + nvm_offset = ((buf_size << DRV_MB_PARAM_NVM_LEN_OFFSET) | + addr) + buf_idx; + rc = qed_mcp_nvm_wr_cmd(p_hwfn, p_ptt, nvm_cmd, nvm_offset, + &resp, ¶m, buf_size, + (u32 *)&p_buf[buf_idx]); + if (rc) { + DP_NOTICE(cdev, "nvm write failed, rc = %d\n", rc); + resp = FW_MSG_CODE_ERROR; + break; + } + + if (resp != FW_MSG_CODE_OK && + resp != FW_MSG_CODE_NVM_OK && + resp != FW_MSG_CODE_NVM_PUT_FILE_FINISH_OK) { + DP_NOTICE(cdev, + "nvm write failed, resp = 0x%08x\n", resp); + rc = -EINVAL; + break; + } + + /* This can be a lengthy process, and it's possible scheduler + * isn't pre-emptable. Sleep a bit to prevent CPU hogging. + */ + if (buf_idx % 0x1000 > (buf_idx + buf_size) % 0x1000) + usleep_range(1000, 2000); + + buf_idx += buf_size; + } + + cdev->mcp_nvm_resp = resp; +out: + qed_ptt_release(p_hwfn, p_ptt); + + return rc; +} + int qed_mcp_bist_register_test(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) { u32 drv_mb_param = 0, rsp, param; @@ -2303,9 +2424,9 @@ int qed_mcp_bist_clock_test(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) return rc; } -int qed_mcp_bist_nvm_test_get_num_images(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, - u32 *num_images) +int qed_mcp_bist_nvm_get_num_images(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u32 *num_images) { u32 drv_mb_param = 0, rsp; int rc = 0; @@ -2324,10 +2445,10 @@ int qed_mcp_bist_nvm_test_get_num_images(struct qed_hwfn *p_hwfn, return rc; } -int qed_mcp_bist_nvm_test_get_image_att(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, - struct bist_nvm_image_att *p_image_att, - u32 image_index) +int qed_mcp_bist_nvm_get_image_att(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct bist_nvm_image_att *p_image_att, + u32 image_index) { u32 buf_size = 0, param, resp = 0, resp_param = 0; int rc; @@ -2351,16 +2472,71 @@ int qed_mcp_bist_nvm_test_get_image_att(struct qed_hwfn *p_hwfn, return rc; } +int qed_mcp_nvm_info_populate(struct qed_hwfn *p_hwfn) +{ + struct qed_nvm_image_info *nvm_info = &p_hwfn->nvm_info; + struct qed_ptt *p_ptt; + int rc; + u32 i; + + p_ptt = qed_ptt_acquire(p_hwfn); + if (!p_ptt) { + DP_ERR(p_hwfn, "failed to acquire ptt\n"); + return -EBUSY; + } + + /* Acquire from MFW the amount of available images */ + nvm_info->num_images = 0; + rc = qed_mcp_bist_nvm_get_num_images(p_hwfn, + p_ptt, &nvm_info->num_images); + if (rc == -EOPNOTSUPP) { + DP_INFO(p_hwfn, "DRV_MSG_CODE_BIST_TEST is not supported\n"); + goto out; + } else if (rc || !nvm_info->num_images) { + DP_ERR(p_hwfn, "Failed getting number of images\n"); + goto err0; + } + + nvm_info->image_att = kmalloc(nvm_info->num_images * + sizeof(struct bist_nvm_image_att), + GFP_KERNEL); + if (!nvm_info->image_att) { + rc = -ENOMEM; + goto err0; + } + + /* Iterate over images and get their attributes */ + for (i = 0; i < nvm_info->num_images; i++) { + rc = qed_mcp_bist_nvm_get_image_att(p_hwfn, p_ptt, + &nvm_info->image_att[i], i); + if (rc) { + DP_ERR(p_hwfn, + "Failed getting image index %d attributes\n", i); + goto err1; + } + + DP_VERBOSE(p_hwfn, QED_MSG_SP, "image index %d, size %x\n", i, + nvm_info->image_att[i].len); + } +out: + qed_ptt_release(p_hwfn, p_ptt); + return 0; + +err1: + kfree(nvm_info->image_att); +err0: + qed_ptt_release(p_hwfn, p_ptt); + return rc; +} + static int qed_mcp_get_nvm_image_att(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt, enum qed_nvm_images image_id, struct qed_nvm_image_att *p_image_att) { - struct bist_nvm_image_att mfw_image_att; enum nvm_image_type type; - u32 num_images, i; - int rc; + u32 i; /* Translate image_id into MFW definitions */ switch (image_id) { @@ -2376,29 +2552,18 @@ qed_mcp_get_nvm_image_att(struct qed_hwfn *p_hwfn, return -EINVAL; } - /* Learn number of images, then traverse and see if one fits */ - rc = qed_mcp_bist_nvm_test_get_num_images(p_hwfn, p_ptt, &num_images); - if (rc || !num_images) - return -EINVAL; - - for (i = 0; i < num_images; i++) { - rc = qed_mcp_bist_nvm_test_get_image_att(p_hwfn, p_ptt, - &mfw_image_att, i); - if (rc) - return rc; - - if (type == mfw_image_att.image_type) + for (i = 0; i < p_hwfn->nvm_info.num_images; i++) + if (type == p_hwfn->nvm_info.image_att[i].image_type) break; - } - if (i == num_images) { + if (i == p_hwfn->nvm_info.num_images) { DP_VERBOSE(p_hwfn, QED_MSG_STORAGE, "Failed to find nvram image of type %08x\n", image_id); - return -EINVAL; + return -ENOENT; } - p_image_att->start_addr = mfw_image_att.nvm_start_addr; - p_image_att->length = mfw_image_att.len; + p_image_att->start_addr = p_hwfn->nvm_info.image_att[i].nvm_start_addr; + p_image_att->length = p_hwfn->nvm_info.image_att[i].len; return 0; } diff --git a/drivers/net/ethernet/qlogic/qed/qed_mcp.h b/drivers/net/ethernet/qlogic/qed/qed_mcp.h index c7ec2395d1ce..8a5c988d0c3c 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_mcp.h +++ b/drivers/net/ethernet/qlogic/qed/qed_mcp.h @@ -443,6 +443,40 @@ int qed_mcp_set_led(struct qed_hwfn *p_hwfn, */ int qed_mcp_nvm_read(struct qed_dev *cdev, u32 addr, u8 *p_buf, u32 len); +/** + * @brief Write to nvm + * + * @param cdev + * @param addr - nvm offset + * @param cmd - nvm command + * @param p_buf - nvm write buffer + * @param len - buffer len + * + * @return int - 0 - operation was successful. + */ +int qed_mcp_nvm_write(struct qed_dev *cdev, + u32 cmd, u32 addr, u8 *p_buf, u32 len); + +/** + * @brief Put file begin + * + * @param cdev + * @param addr - nvm offset + * + * @return int - 0 - operation was successful. + */ +int qed_mcp_nvm_put_file_begin(struct qed_dev *cdev, u32 addr); + +/** + * @brief Check latest response + * + * @param cdev + * @param p_buf - nvm write buffer + * + * @return int - 0 - operation was successful. + */ +int qed_mcp_nvm_resp(struct qed_dev *cdev, u8 *p_buf); + struct qed_nvm_image_att { u32 start_addr; u32 length; @@ -496,9 +530,9 @@ int qed_mcp_bist_clock_test(struct qed_hwfn *p_hwfn, * * @return int - 0 - operation was successful. */ -int qed_mcp_bist_nvm_test_get_num_images(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, - u32 *num_images); +int qed_mcp_bist_nvm_get_num_images(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + u32 *num_images); /** * @brief Bist nvm test - get image attributes by index @@ -510,10 +544,10 @@ int qed_mcp_bist_nvm_test_get_num_images(struct qed_hwfn *p_hwfn, * * @return int - 0 - operation was successful. */ -int qed_mcp_bist_nvm_test_get_image_att(struct qed_hwfn *p_hwfn, - struct qed_ptt *p_ptt, - struct bist_nvm_image_att *p_image_att, - u32 image_index); +int qed_mcp_bist_nvm_get_image_att(struct qed_hwfn *p_hwfn, + struct qed_ptt *p_ptt, + struct bist_nvm_image_att *p_image_att, + u32 image_index); /* Using hwfn number (and not pf_num) is required since in CMT mode, * same pf_num may be used by two different hwfn @@ -957,4 +991,12 @@ int qed_mcp_get_capabilities(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); * @param p_ptt */ int qed_mcp_set_capabilities(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); + +/** + * @brief Populate the nvm info shadow in the given hardware function + * + * @param p_hwfn + */ +int qed_mcp_nvm_info_populate(struct qed_hwfn *p_hwfn); + #endif diff --git a/drivers/net/ethernet/qlogic/qed/qed_selftest.c b/drivers/net/ethernet/qlogic/qed/qed_selftest.c index 1bafc05db2b8..cf1d4476f9d8 100644 --- a/drivers/net/ethernet/qlogic/qed/qed_selftest.c +++ b/drivers/net/ethernet/qlogic/qed/qed_selftest.c @@ -125,10 +125,11 @@ int qed_selftest_nvram(struct qed_dev *cdev) } /* Acquire from MFW the amount of available images */ - rc = qed_mcp_bist_nvm_test_get_num_images(p_hwfn, p_ptt, &num_images); + rc = qed_mcp_bist_nvm_get_num_images(p_hwfn, p_ptt, &num_images); if (rc || !num_images) { DP_ERR(p_hwfn, "Failed getting number of images\n"); - return -EINVAL; + rc = -EINVAL; + goto err0; } /* Iterate over images and validate CRC */ @@ -136,8 +137,8 @@ int qed_selftest_nvram(struct qed_dev *cdev) /* This mailbox returns information about the image required for * reading it. */ - rc = qed_mcp_bist_nvm_test_get_image_att(p_hwfn, p_ptt, - &image_att, i); + rc = qed_mcp_bist_nvm_get_image_att(p_hwfn, p_ptt, + &image_att, i); if (rc) { DP_ERR(p_hwfn, "Failed getting image index %d attributes\n", diff --git a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c index 4ca3847fffd4..ecbf1ded7a39 100644 --- a/drivers/net/ethernet/qlogic/qede/qede_ethtool.c +++ b/drivers/net/ethernet/qlogic/qede/qede_ethtool.c @@ -699,6 +699,14 @@ static u32 qede_get_link(struct net_device *dev) return current_link.link_up; } +static int qede_flash_device(struct net_device *dev, + struct ethtool_flash *flash) +{ + struct qede_dev *edev = netdev_priv(dev); + + return edev->ops->common->nvm_flash(edev->cdev, flash->data); +} + static int qede_get_coalesce(struct net_device *dev, struct ethtool_coalesce *coal) { @@ -1806,6 +1814,7 @@ static const struct ethtool_ops qede_ethtool_ops = { .get_tunable = qede_get_tunable, .set_tunable = qede_set_tunable, + .flash_device = qede_flash_device, }; static const struct ethtool_ops qede_vf_ethtool_ops = { diff --git a/include/linux/qed/qed_if.h b/include/linux/qed/qed_if.h index 15e398c7230e..b5b2bc9eacca 100644 --- a/include/linux/qed/qed_if.h +++ b/include/linux/qed/qed_if.h @@ -483,6 +483,15 @@ struct qed_int_info { u8 used_cnt; }; +#define QED_NVM_SIGNATURE 0x12435687 + +enum qed_nvm_flash_cmd { + QED_NVM_FLASH_CMD_FILE_DATA = 0x2, + QED_NVM_FLASH_CMD_FILE_START = 0x3, + QED_NVM_FLASH_CMD_NVM_CHANGE = 0x4, + QED_NVM_FLASH_CMD_NVM_MAX, +}; + struct qed_common_cb_ops { void (*arfs_filter_op)(void *dev, void *fltr, u8 fw_rc); void (*link_update)(void *dev, @@ -658,6 +667,16 @@ struct qed_common_ops { struct qed_chain *p_chain); /** + * @brief nvm_flash - Flash nvm data. + * + * @param cdev + * @param name - file containing the data + * + * @return 0 on success, error otherwise. + */ + int (*nvm_flash)(struct qed_dev *cdev, const char *name); + +/** * @brief nvm_get_image - reads an entire image from nvram * * @param cdev |