Commit 5593e334 authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 50bc60cb ccfa110c
...@@ -81,6 +81,13 @@ enum qed_coalescing_mode { ...@@ -81,6 +81,13 @@ enum qed_coalescing_mode {
QED_COAL_MODE_ENABLE 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_eth_cb_ops;
struct qed_dev_info; struct qed_dev_info;
union qed_mcp_protocol_stats; union qed_mcp_protocol_stats;
...@@ -437,6 +444,11 @@ enum BAR_ID { ...@@ -437,6 +444,11 @@ enum BAR_ID {
BAR_ID_1 /* Used for doorbells */ 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 \ #define DRV_MODULE_VERSION \
__stringify(QED_MAJOR_VERSION) "." \ __stringify(QED_MAJOR_VERSION) "." \
__stringify(QED_MINOR_VERSION) "." \ __stringify(QED_MINOR_VERSION) "." \
...@@ -561,6 +573,9 @@ struct qed_hwfn { ...@@ -561,6 +573,9 @@ struct qed_hwfn {
/* L2-related */ /* L2-related */
struct qed_l2_info *p_l2_info; 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_ptt *p_arfs_ptt;
struct qed_simd_fp_handler simd_proto_handler[64]; struct qed_simd_fp_handler simd_proto_handler[64];
......
...@@ -2932,6 +2932,12 @@ static int qed_get_dev_info(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) ...@@ -2932,6 +2932,12 @@ static int qed_get_dev_info(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
return 0; 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, static int qed_hw_prepare_single(struct qed_hwfn *p_hwfn,
void __iomem *p_regview, void __iomem *p_regview,
void __iomem *p_doorbells, void __iomem *p_doorbells,
...@@ -2995,12 +3001,25 @@ static int qed_hw_prepare_single(struct qed_hwfn *p_hwfn, ...@@ -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"); 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 */ /* Allocate the init RT array and initialize the init-ops engine */
rc = qed_init_alloc(p_hwfn); rc = qed_init_alloc(p_hwfn);
if (rc) if (rc)
goto err2; goto err3;
return rc; return rc;
err3:
if (IS_LEAD_HWFN(p_hwfn))
qed_nvm_info_free(p_hwfn);
err2: err2:
if (IS_LEAD_HWFN(p_hwfn)) if (IS_LEAD_HWFN(p_hwfn))
qed_iov_free_hw_info(p_hwfn->cdev); qed_iov_free_hw_info(p_hwfn->cdev);
...@@ -3056,6 +3075,7 @@ int qed_hw_prepare(struct qed_dev *cdev, ...@@ -3056,6 +3075,7 @@ int qed_hw_prepare(struct qed_dev *cdev,
if (rc) { if (rc) {
if (IS_PF(cdev)) { if (IS_PF(cdev)) {
qed_init_free(p_hwfn); qed_init_free(p_hwfn);
qed_nvm_info_free(p_hwfn);
qed_mcp_free(p_hwfn); qed_mcp_free(p_hwfn);
qed_hw_hwfn_free(p_hwfn); qed_hw_hwfn_free(p_hwfn);
} }
...@@ -3088,6 +3108,8 @@ void qed_hw_remove(struct qed_dev *cdev) ...@@ -3088,6 +3108,8 @@ void qed_hw_remove(struct qed_dev *cdev)
} }
qed_iov_free_hw_info(cdev); qed_iov_free_hw_info(cdev);
qed_nvm_info_free(p_hwfn);
} }
static void qed_chain_free_next_ptr(struct qed_dev *cdev, static void qed_chain_free_next_ptr(struct qed_dev *cdev,
......
...@@ -12268,8 +12268,11 @@ struct public_drv_mb { ...@@ -12268,8 +12268,11 @@ struct public_drv_mb {
#define DRV_MSG_CODE_VF_DISABLED_DONE 0xc0000000 #define DRV_MSG_CODE_VF_DISABLED_DONE 0xc0000000
#define DRV_MSG_CODE_CFG_VF_MSIX 0xc0010000 #define DRV_MSG_CODE_CFG_VF_MSIX 0xc0010000
#define DRV_MSG_CODE_CFG_PF_VFS_MSIX 0xc0020000 #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_GET_FILE_ATT 0x00030000
#define DRV_MSG_CODE_NVM_READ_NVRAM 0x00050000 #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_MCP_RESET 0x00090000
#define DRV_MSG_CODE_SET_VERSION 0x000f0000 #define DRV_MSG_CODE_SET_VERSION 0x000f0000
#define DRV_MSG_CODE_MCP_HALT 0x00100000 #define DRV_MSG_CODE_MCP_HALT 0x00100000
...@@ -12323,7 +12326,6 @@ struct public_drv_mb { ...@@ -12323,7 +12326,6 @@ struct public_drv_mb {
#define DRV_MSG_CODE_FEATURE_SUPPORT 0x00300000 #define DRV_MSG_CODE_FEATURE_SUPPORT 0x00300000
#define DRV_MSG_CODE_GET_MFW_FEATURE_SUPPORT 0x00310000 #define DRV_MSG_CODE_GET_MFW_FEATURE_SUPPORT 0x00310000
#define DRV_MSG_SEQ_NUMBER_MASK 0x0000ffff #define DRV_MSG_SEQ_NUMBER_MASK 0x0000ffff
u32 drv_mb_param; u32 drv_mb_param;
...@@ -12435,7 +12437,10 @@ struct public_drv_mb { ...@@ -12435,7 +12437,10 @@ struct public_drv_mb {
#define FW_MSG_CODE_DRV_CFG_VF_MSIX_DONE 0xb0010000 #define FW_MSG_CODE_DRV_CFG_VF_MSIX_DONE 0xb0010000
#define FW_MSG_CODE_NVM_OK 0x00010000 #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_OK 0x00160000
#define FW_MSG_CODE_ERROR 0x00170000
#define FW_MSG_CODE_OS_WOL_SUPPORTED 0x00800000 #define FW_MSG_CODE_OS_WOL_SUPPORTED 0x00800000
#define FW_MSG_CODE_OS_WOL_NOT_SUPPORTED 0x00810000 #define FW_MSG_CODE_OS_WOL_NOT_SUPPORTED 0x00810000
......
...@@ -45,6 +45,7 @@ ...@@ -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) ...@@ -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 = { ...@@ -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,
......
...@@ -569,6 +569,31 @@ int qed_mcp_cmd(struct qed_hwfn *p_hwfn, ...@@ -569,6 +569,31 @@ int qed_mcp_cmd(struct qed_hwfn *p_hwfn,
return 0; 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, int qed_mcp_nvm_rd_cmd(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, struct qed_ptt *p_ptt,
u32 cmd, u32 cmd,
...@@ -2261,6 +2286,102 @@ int qed_mcp_nvm_read(struct qed_dev *cdev, u32 addr, u8 *p_buf, u32 len) ...@@ -2261,6 +2286,102 @@ int qed_mcp_nvm_read(struct qed_dev *cdev, u32 addr, u8 *p_buf, u32 len)
return rc; 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, &param);
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, &param, 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) int qed_mcp_bist_register_test(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
{ {
u32 drv_mb_param = 0, rsp, param; u32 drv_mb_param = 0, rsp, param;
...@@ -2303,7 +2424,7 @@ int qed_mcp_bist_clock_test(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt) ...@@ -2303,7 +2424,7 @@ int qed_mcp_bist_clock_test(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt)
return rc; return rc;
} }
int qed_mcp_bist_nvm_test_get_num_images(struct qed_hwfn *p_hwfn, int qed_mcp_bist_nvm_get_num_images(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, struct qed_ptt *p_ptt,
u32 *num_images) u32 *num_images)
{ {
...@@ -2324,7 +2445,7 @@ int qed_mcp_bist_nvm_test_get_num_images(struct qed_hwfn *p_hwfn, ...@@ -2324,7 +2445,7 @@ int qed_mcp_bist_nvm_test_get_num_images(struct qed_hwfn *p_hwfn,
return rc; return rc;
} }
int qed_mcp_bist_nvm_test_get_image_att(struct qed_hwfn *p_hwfn, int qed_mcp_bist_nvm_get_image_att(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, struct qed_ptt *p_ptt,
struct bist_nvm_image_att *p_image_att, struct bist_nvm_image_att *p_image_att,
u32 image_index) u32 image_index)
...@@ -2351,16 +2472,71 @@ int qed_mcp_bist_nvm_test_get_image_att(struct qed_hwfn *p_hwfn, ...@@ -2351,16 +2472,71 @@ int qed_mcp_bist_nvm_test_get_image_att(struct qed_hwfn *p_hwfn,
return rc; 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 static int
qed_mcp_get_nvm_image_att(struct qed_hwfn *p_hwfn, qed_mcp_get_nvm_image_att(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, struct qed_ptt *p_ptt,
enum qed_nvm_images image_id, enum qed_nvm_images image_id,
struct qed_nvm_image_att *p_image_att) struct qed_nvm_image_att *p_image_att)
{ {
struct bist_nvm_image_att mfw_image_att;
enum nvm_image_type type; enum nvm_image_type type;
u32 num_images, i; u32 i;
int rc;
/* Translate image_id into MFW definitions */ /* Translate image_id into MFW definitions */
switch (image_id) { switch (image_id) {
...@@ -2376,29 +2552,18 @@ qed_mcp_get_nvm_image_att(struct qed_hwfn *p_hwfn, ...@@ -2376,29 +2552,18 @@ qed_mcp_get_nvm_image_att(struct qed_hwfn *p_hwfn,
return -EINVAL; return -EINVAL;
} }
/* Learn number of images, then traverse and see if one fits */ for (i = 0; i < p_hwfn->nvm_info.num_images; i++)
rc = qed_mcp_bist_nvm_test_get_num_images(p_hwfn, p_ptt, &num_images); if (type == p_hwfn->nvm_info.image_att[i].image_type)
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)
break; break;
} if (i == p_hwfn->nvm_info.num_images) {
if (i == num_images) {
DP_VERBOSE(p_hwfn, QED_MSG_STORAGE, DP_VERBOSE(p_hwfn, QED_MSG_STORAGE,
"Failed to find nvram image of type %08x\n", "Failed to find nvram image of type %08x\n",
image_id); image_id);
return -EINVAL; return -ENOENT;
} }
p_image_att->start_addr = mfw_image_att.nvm_start_addr; p_image_att->start_addr = p_hwfn->nvm_info.image_att[i].nvm_start_addr;
p_image_att->length = mfw_image_att.len; p_image_att->length = p_hwfn->nvm_info.image_att[i].len;
return 0; return 0;
} }
......
...@@ -443,6 +443,40 @@ int qed_mcp_set_led(struct qed_hwfn *p_hwfn, ...@@ -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); 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 { struct qed_nvm_image_att {
u32 start_addr; u32 start_addr;
u32 length; u32 length;
...@@ -496,7 +530,7 @@ int qed_mcp_bist_clock_test(struct qed_hwfn *p_hwfn, ...@@ -496,7 +530,7 @@ int qed_mcp_bist_clock_test(struct qed_hwfn *p_hwfn,
* *
* @return int - 0 - operation was successful. * @return int - 0 - operation was successful.
*/ */
int qed_mcp_bist_nvm_test_get_num_images(struct qed_hwfn *p_hwfn, int qed_mcp_bist_nvm_get_num_images(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, struct qed_ptt *p_ptt,
u32 *num_images); u32 *num_images);
...@@ -510,7 +544,7 @@ int qed_mcp_bist_nvm_test_get_num_images(struct qed_hwfn *p_hwfn, ...@@ -510,7 +544,7 @@ int qed_mcp_bist_nvm_test_get_num_images(struct qed_hwfn *p_hwfn,
* *
* @return int - 0 - operation was successful. * @return int - 0 - operation was successful.
*/ */
int qed_mcp_bist_nvm_test_get_image_att(struct qed_hwfn *p_hwfn, int qed_mcp_bist_nvm_get_image_att(struct qed_hwfn *p_hwfn,
struct qed_ptt *p_ptt, struct qed_ptt *p_ptt,
struct bist_nvm_image_att *p_image_att, struct bist_nvm_image_att *p_image_att,
u32 image_index); u32 image_index);
...@@ -957,4 +991,12 @@ int qed_mcp_get_capabilities(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt); ...@@ -957,4 +991,12 @@ int qed_mcp_get_capabilities(struct qed_hwfn *p_hwfn, struct qed_ptt *p_ptt);
* @param p_ptt * @param p_ptt
*/ */
int qed_mcp_set_capabilities(struct qed_hwfn *p_hwfn, struct qed_ptt *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 #endif
...@@ -125,10 +125,11 @@ int qed_selftest_nvram(struct qed_dev *cdev) ...@@ -125,10 +125,11 @@ int qed_selftest_nvram(struct qed_dev *cdev)
} }
/* Acquire from MFW the amount of available images */ /* 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) { if (rc || !num_images) {
DP_ERR(p_hwfn, "Failed getting number of images\n"); DP_ERR(p_hwfn, "Failed getting number of images\n");
return -EINVAL; rc = -EINVAL;
goto err0;
} }
/* Iterate over images and validate CRC */ /* Iterate over images and validate CRC */
...@@ -136,7 +137,7 @@ int qed_selftest_nvram(struct qed_dev *cdev) ...@@ -136,7 +137,7 @@ int qed_selftest_nvram(struct qed_dev *cdev)
/* This mailbox returns information about the image required for /* This mailbox returns information about the image required for
* reading it. * reading it.
*/ */
rc = qed_mcp_bist_nvm_test_get_image_att(p_hwfn, p_ptt, rc = qed_mcp_bist_nvm_get_image_att(p_hwfn, p_ptt,
&image_att, i); &image_att, i);
if (rc) { if (rc) {
DP_ERR(p_hwfn, DP_ERR(p_hwfn,
......
...@@ -699,6 +699,14 @@ static u32 qede_get_link(struct net_device *dev) ...@@ -699,6 +699,14 @@ static u32 qede_get_link(struct net_device *dev)
return current_link.link_up; 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, static int qede_get_coalesce(struct net_device *dev,
struct ethtool_coalesce *coal) struct ethtool_coalesce *coal)
{ {
...@@ -1806,6 +1814,7 @@ static const struct ethtool_ops qede_ethtool_ops = { ...@@ -1806,6 +1814,7 @@ static const struct ethtool_ops qede_ethtool_ops = {
.get_tunable = qede_get_tunable, .get_tunable = qede_get_tunable,
.set_tunable = qede_set_tunable, .set_tunable = qede_set_tunable,
.flash_device = qede_flash_device,
}; };
static const struct ethtool_ops qede_vf_ethtool_ops = { static const struct ethtool_ops qede_vf_ethtool_ops = {
......
...@@ -483,6 +483,15 @@ struct qed_int_info { ...@@ -483,6 +483,15 @@ struct qed_int_info {
u8 used_cnt; 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 { struct qed_common_cb_ops {
void (*arfs_filter_op)(void *dev, void *fltr, u8 fw_rc); void (*arfs_filter_op)(void *dev, void *fltr, u8 fw_rc);
void (*link_update)(void *dev, void (*link_update)(void *dev,
...@@ -657,6 +666,16 @@ struct qed_common_ops { ...@@ -657,6 +666,16 @@ struct qed_common_ops {
void (*chain_free)(struct qed_dev *cdev, void (*chain_free)(struct qed_dev *cdev,
struct qed_chain *p_chain); 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 * @brief nvm_get_image - reads an entire image from nvram
* *
......
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