aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJakub Kicinski <jakub.kicinski@netronome.com>2018-08-28 13:20:35 -0700
committerDavid S. Miller <davem@davemloft.net>2018-08-28 16:01:46 -0700
commitac86da05463dc91af3741540663d864ba9287071 (patch)
treee5c8cb63704da2a7cb0dcc086599d4ffca41e479
parentnfp: attempt FW load from flash (diff)
downloadlinux-dev-ac86da05463dc91af3741540663d864ba9287071.tar.xz
linux-dev-ac86da05463dc91af3741540663d864ba9287071.zip
nfp: interpret extended FW load result codes
To enable easier FW distribution NFP can now automatically select between FW stored on the flash and loaded from the kernel. If FW loading policy is set to auto it will compare the versions of FW from the host and from the flash and load the newer one. If FW type doesn't match (e.g. one advanced application vs another) the FW from the host takes precedence, unless one of them is the basic NIC firmware, in which case the non-basic-NIC FW is selected. This automatic selection mechanism requires we inform user what the verdict was. Print a message to the logs explaining the decision and the reason. Signed-off-by: Jakub Kicinski <jakub.kicinski@netronome.com> Reviewed-by: Dirk van der Merwe <dirk.vandermerwe@netronome.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpp.h3
-rw-r--r--drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c85
2 files changed, 85 insertions, 3 deletions
diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpp.h
index c338d539fa96..3b5182143ec7 100644
--- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpp.h
+++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_cpp.h
@@ -56,6 +56,9 @@
dev_info(nfp_cpp_device(cpp)->parent, NFP_SUBSYS ": " fmt, ## args)
#define nfp_dbg(cpp, fmt, args...) \
dev_dbg(nfp_cpp_device(cpp)->parent, NFP_SUBSYS ": " fmt, ## args)
+#define nfp_printk(level, cpp, fmt, args...) \
+ dev_printk(level, nfp_cpp_device(cpp)->parent, \
+ NFP_SUBSYS ": " fmt, ## args)
#define PCI_64BIT_BAR_COUNT 3
diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c
index 0cdaa1fd6bcf..9eb7b5a91bb1 100644
--- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c
+++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c
@@ -87,6 +87,9 @@
#define NSP_CODE_MAJOR GENMASK(15, 12)
#define NSP_CODE_MINOR GENMASK(11, 0)
+#define NFP_FW_LOAD_RET_MAJOR GENMASK(15, 8)
+#define NFP_FW_LOAD_RET_MINOR GENMASK(23, 16)
+
enum nfp_nsp_cmd {
SPCODE_NOOP = 0, /* No operation */
SPCODE_SOFT_RESET = 1, /* Soft reset the NFP */
@@ -135,6 +138,7 @@ struct nfp_nsp {
* @option: NFP SP Command Argument
* @buff_cpp: NFP SP Buffer CPP Address info
* @buff_addr: NFP SP Buffer Host address
+ * @error_cb: Callback for interpreting option if error occurred
*/
struct nfp_nsp_command_arg {
u16 code;
@@ -142,6 +146,7 @@ struct nfp_nsp_command_arg {
u32 option;
u32 buff_cpp;
u64 buff_addr;
+ void (*error_cb)(struct nfp_nsp *state, u32 ret_val);
};
/**
@@ -401,7 +406,10 @@ __nfp_nsp_command(struct nfp_nsp *state, const struct nfp_nsp_command_arg *arg)
if (err) {
nfp_warn(cpp, "Result (error) code set: %d (%d) command: %d\n",
-err, (int)ret_val, arg->code);
- nfp_nsp_print_extended_error(state, ret_val);
+ if (arg->error_cb)
+ arg->error_cb(state, ret_val);
+ else
+ nfp_nsp_print_extended_error(state, ret_val);
return -err;
}
@@ -530,18 +538,78 @@ int nfp_nsp_mac_reinit(struct nfp_nsp *state)
return nfp_nsp_command(state, SPCODE_MAC_INIT);
}
+static void nfp_nsp_load_fw_extended_msg(struct nfp_nsp *state, u32 ret_val)
+{
+ static const char * const major_msg[] = {
+ /* 0 */ "Firmware from driver loaded",
+ /* 1 */ "Firmware from flash loaded",
+ /* 2 */ "Firmware loading failure",
+ };
+ static const char * const minor_msg[] = {
+ /* 0 */ "",
+ /* 1 */ "no named partition on flash",
+ /* 2 */ "error reading from flash",
+ /* 3 */ "can not deflate",
+ /* 4 */ "not a trusted file",
+ /* 5 */ "can not parse FW file",
+ /* 6 */ "MIP not found in FW file",
+ /* 7 */ "null firmware name in MIP",
+ /* 8 */ "FW version none",
+ /* 9 */ "FW build number none",
+ /* 10 */ "no FW selection policy HWInfo key found",
+ /* 11 */ "static FW selection policy",
+ /* 12 */ "FW version has precedence",
+ /* 13 */ "different FW application load requested",
+ /* 14 */ "development build",
+ };
+ unsigned int major, minor;
+ const char *level;
+
+ major = FIELD_GET(NFP_FW_LOAD_RET_MAJOR, ret_val);
+ minor = FIELD_GET(NFP_FW_LOAD_RET_MINOR, ret_val);
+
+ if (!nfp_nsp_has_stored_fw_load(state))
+ return;
+
+ /* Lower the message level in legacy case */
+ if (major == 0 && (minor == 0 || minor == 10))
+ level = KERN_DEBUG;
+ else if (major == 2)
+ level = KERN_ERR;
+ else
+ level = KERN_INFO;
+
+ if (major >= ARRAY_SIZE(major_msg))
+ nfp_printk(level, state->cpp, "FW loading status: %x\n",
+ ret_val);
+ else if (minor >= ARRAY_SIZE(minor_msg))
+ nfp_printk(level, state->cpp, "%s, reason code: %d\n",
+ major_msg[major], minor);
+ else
+ nfp_printk(level, state->cpp, "%s%c %s\n",
+ major_msg[major], minor ? ',' : '.',
+ minor_msg[minor]);
+}
+
int nfp_nsp_load_fw(struct nfp_nsp *state, const struct firmware *fw)
{
struct nfp_nsp_command_buf_arg load_fw = {
{
.code = SPCODE_FW_LOAD,
.option = fw->size,
+ .error_cb = nfp_nsp_load_fw_extended_msg,
},
.in_buf = fw->data,
.in_size = fw->size,
};
+ int ret;
- return nfp_nsp_command_buf(state, &load_fw);
+ ret = nfp_nsp_command_buf(state, &load_fw);
+ if (ret < 0)
+ return ret;
+
+ nfp_nsp_load_fw_extended_msg(state, ret);
+ return 0;
}
int nfp_nsp_write_flash(struct nfp_nsp *state, const struct firmware *fw)
@@ -622,5 +690,16 @@ int nfp_nsp_read_sensors(struct nfp_nsp *state, unsigned int sensor_mask,
int nfp_nsp_load_stored_fw(struct nfp_nsp *state)
{
- return nfp_nsp_command(state, SPCODE_FW_STORED);
+ const struct nfp_nsp_command_arg arg = {
+ .code = SPCODE_FW_STORED,
+ .error_cb = nfp_nsp_load_fw_extended_msg,
+ };
+ int ret;
+
+ ret = __nfp_nsp_command(state, &arg);
+ if (ret < 0)
+ return ret;
+
+ nfp_nsp_load_fw_extended_msg(state, ret);
+ return 0;
}