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>
This commit is contained in:
commit
5593e3340f
|
@ -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];
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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 = {
|
||||
|
|
|
@ -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,
|
||||
|
@ -657,6 +666,16 @@ struct qed_common_ops {
|
|||
void (*chain_free)(struct qed_dev *cdev,
|
||||
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
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue