Commit 074be7fd authored by David S. Miller's avatar David S. Miller

Merge branch 'nfp-implement-firmware-loading-policy'

Simon Horman says:

====================
nfp: implement firmware loading policy

Dirk says:

This series adds configuration capabilities to the firmware loading policy of
the NFP driver.

NFP firmware loading is controlled via three HWinfo keys which can be set per
device: 'abi_drv_reset', 'abi_drv_load_ifc' and 'app_fw_from_flash'.
Refer to patch #11 for more detail on how these control the firmware loading.

In order to configure the full extend of FW loading policy, a new devlink
parameter has been introduced, 'reset_dev_on_drv_probe', which controls if the
driver should reset the device when it's probed. This, in conjunction with the
existing 'fw_load_policy' (extended to include a 'disk' option) provides the
means to tweak the NFP HWinfo keys as required by users.

Patches 1 and 2 adds the devlink modifications and patches 3 through 9 adds the
support into the NFP driver. Furthermore, the last 2 patches are documentation
only.

v2:
  Renamed all 'reset_dev_on_drv_probe' defines the same as the devlink parameter
  name (Jiri)
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 4bb2f84a 40a962be
.. SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
=============================================
Netronome Flow Processor (NFP) Kernel Drivers
=============================================
Copyright (c) 2019, Netronome Systems, Inc.
Contents
========
- `Overview`_
- `Acquiring Firmware`_
Overview
========
This driver supports Netronome's line of Flow Processor devices,
including the NFP4000, NFP5000, and NFP6000 models, which are also
incorporated in the company's family of Agilio SmartNICs. The SR-IOV
physical and virtual functions for these devices are supported by
the driver.
Acquiring Firmware
==================
The NFP4000 and NFP6000 devices require application specific firmware
to function. Application firmware can be located either on the host file system
or in the device flash (if supported by management firmware).
Firmware files on the host filesystem contain card type (`AMDA-*` string), media
config etc. They should be placed in `/lib/firmware/netronome` directory to
load firmware from the host file system.
Firmware for basic NIC operation is available in the upstream
`linux-firmware.git` repository.
Firmware in NVRAM
-----------------
Recent versions of management firmware supports loading application
firmware from flash when the host driver gets probed. The firmware loading
policy configuration may be used to configure this feature appropriately.
Devlink or ethtool can be used to update the application firmware on the device
flash by providing the appropriate `nic_AMDA*.nffw` file to the respective
command. Users need to take care to write the correct firmware image for the
card and media configuration to flash.
Available storage space in flash depends on the card being used.
Dealing with multiple projects
------------------------------
NFP hardware is fully programmable therefore there can be different
firmware images targeting different applications.
When using application firmware from host, we recommend placing
actual firmware files in application-named subdirectories in
`/lib/firmware/netronome` and linking the desired files, e.g.::
$ tree /lib/firmware/netronome/
/lib/firmware/netronome/
├── bpf
│   ├── nic_AMDA0081-0001_1x40.nffw
│   └── nic_AMDA0081-0001_4x10.nffw
├── flower
│   ├── nic_AMDA0081-0001_1x40.nffw
│   └── nic_AMDA0081-0001_4x10.nffw
├── nic
│   ├── nic_AMDA0081-0001_1x40.nffw
│   └── nic_AMDA0081-0001_4x10.nffw
├── nic_AMDA0081-0001_1x40.nffw -> bpf/nic_AMDA0081-0001_1x40.nffw
└── nic_AMDA0081-0001_4x10.nffw -> bpf/nic_AMDA0081-0001_4x10.nffw
3 directories, 8 files
You may need to use hard instead of symbolic links on distributions
which use old `mkinitrd` command instead of `dracut` (e.g. Ubuntu).
After changing firmware files you may need to regenerate the initramfs
image. Initramfs contains drivers and firmware files your system may
need to boot. Refer to the documentation of your distribution to find
out how to update initramfs. Good indication of stale initramfs
is system loading wrong driver or firmware on boot, but when driver is
later reloaded manually everything works correctly.
Selecting firmware per device
-----------------------------
Most commonly all cards on the system use the same type of firmware.
If you want to load specific firmware image for a specific card, you
can use either the PCI bus address or serial number. Driver will print
which files it's looking for when it recognizes a NFP device::
nfp: Looking for firmware file in order of priority:
nfp: netronome/serial-00-12-34-aa-bb-cc-10-ff.nffw: not found
nfp: netronome/pci-0000:02:00.0.nffw: not found
nfp: netronome/nic_AMDA0081-0001_1x40.nffw: found, loading...
In this case if file (or link) called *serial-00-12-34-aa-bb-5d-10-ff.nffw*
or *pci-0000:02:00.0.nffw* is present in `/lib/firmware/netronome` this
firmware file will take precedence over `nic_AMDA*` files.
Note that `serial-*` and `pci-*` files are **not** automatically included
in initramfs, you will have to refer to documentation of appropriate tools
to find out how to include them.
Firmware loading policy
-----------------------
Firmware loading policy is controlled via three HWinfo parameters
stored as key value pairs in the device flash:
app_fw_from_flash
Defines which firmware should take precedence, 'Disk' (0), 'Flash' (1) or
the 'Preferred' (2) firmware. When 'Preferred' is selected, the management
firmware makes the decision over which firmware will be loaded by comparing
versions of the flash firmware and the host supplied firmware.
This variable is configurable using the 'fw_load_policy'
devlink parameter.
abi_drv_reset
Defines if the driver should reset the firmware when
the driver is probed, either 'Disk' (0) if firmware was found on disk,
'Always' (1) reset or 'Never' (2) reset. Note that the device is always
reset on driver unload if firmware was loaded when the driver was probed.
This variable is configurable using the 'reset_dev_on_drv_probe'
devlink parameter.
abi_drv_load_ifc
Defines a list of PF devices allowed to load FW on the device.
This variable is not currently user configurable.
fw_load_policy [DEVICE, GENERIC]
Configuration mode: permanent
reset_dev_on_drv_probe [DEVICE, GENERIC]
Configuration mode: permanent
......@@ -48,4 +48,20 @@ fw_load_policy [DEVICE, GENERIC]
Load firmware version preferred by the driver.
* DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH (1)
Load firmware currently stored in flash.
* DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DISK (2)
Load firmware currently available on host's disk.
Type: u8
reset_dev_on_drv_probe [DEVICE, GENERIC]
Controls the device's reset policy on driver probe.
Valid values:
* DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_UNKNOWN (0)
Unknown or invalid value.
* DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_ALWAYS (1)
Always reset device on driver probe.
* DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_NEVER (2)
Never reset device on driver probe.
* DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_DISK (3)
Reset only if device firmware can be found in the
filesystem.
Type: u8
......@@ -17,6 +17,7 @@ nfp-objs := \
nfpcore/nfp_target.o \
ccm.o \
ccm_mbox.o \
devlink_param.o \
nfp_asm.o \
nfp_app.o \
nfp_app_nic.o \
......
// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/* Copyright (C) 2019 Netronome Systems, Inc. */
#include <net/devlink.h>
#include "nfpcore/nfp.h"
#include "nfpcore/nfp_nsp.h"
#include "nfp_main.h"
/**
* struct nfp_devlink_param_u8_arg - Devlink u8 parameter get/set arguments
* @hwinfo_name: HWinfo key name
* @default_hi_val: Default HWinfo value if HWinfo doesn't exist
* @invalid_dl_val: Devlink value to use if HWinfo is unknown/invalid.
* -errno if there is no unknown/invalid value available
* @hi_to_dl: HWinfo to devlink value mapping
* @dl_to_hi: Devlink to hwinfo value mapping
* @max_dl_val: Maximum devlink value supported, for validation only
* @max_hi_val: Maximum HWinfo value supported, for validation only
*/
struct nfp_devlink_param_u8_arg {
const char *hwinfo_name;
const char *default_hi_val;
int invalid_dl_val;
u8 hi_to_dl[4];
u8 dl_to_hi[4];
u8 max_dl_val;
u8 max_hi_val;
};
static const struct nfp_devlink_param_u8_arg nfp_devlink_u8_args[] = {
[DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY] = {
.hwinfo_name = "app_fw_from_flash",
.default_hi_val = NFP_NSP_APP_FW_LOAD_DEFAULT,
.invalid_dl_val = -EINVAL,
.hi_to_dl = {
[NFP_NSP_APP_FW_LOAD_DISK] =
DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DISK,
[NFP_NSP_APP_FW_LOAD_FLASH] =
DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH,
[NFP_NSP_APP_FW_LOAD_PREF] =
DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER,
},
.dl_to_hi = {
[DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER] =
NFP_NSP_APP_FW_LOAD_PREF,
[DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH] =
NFP_NSP_APP_FW_LOAD_FLASH,
[DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DISK] =
NFP_NSP_APP_FW_LOAD_DISK,
},
.max_dl_val = DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DISK,
.max_hi_val = NFP_NSP_APP_FW_LOAD_PREF,
},
[DEVLINK_PARAM_GENERIC_ID_RESET_DEV_ON_DRV_PROBE] = {
.hwinfo_name = "abi_drv_reset",
.default_hi_val = NFP_NSP_DRV_RESET_DEFAULT,
.invalid_dl_val =
DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_UNKNOWN,
.hi_to_dl = {
[NFP_NSP_DRV_RESET_ALWAYS] =
DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_ALWAYS,
[NFP_NSP_DRV_RESET_NEVER] =
DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_NEVER,
[NFP_NSP_DRV_RESET_DISK] =
DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_DISK,
},
.dl_to_hi = {
[DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_ALWAYS] =
NFP_NSP_DRV_RESET_ALWAYS,
[DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_NEVER] =
NFP_NSP_DRV_RESET_NEVER,
[DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_DISK] =
NFP_NSP_DRV_RESET_DISK,
},
.max_dl_val = DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_DISK,
.max_hi_val = NFP_NSP_DRV_RESET_NEVER,
}
};
static int
nfp_devlink_param_u8_get(struct devlink *devlink, u32 id,
struct devlink_param_gset_ctx *ctx)
{
const struct nfp_devlink_param_u8_arg *arg;
struct nfp_pf *pf = devlink_priv(devlink);
struct nfp_nsp *nsp;
char hwinfo[32];
long value;
int err;
if (id >= ARRAY_SIZE(nfp_devlink_u8_args))
return -EOPNOTSUPP;
arg = &nfp_devlink_u8_args[id];
nsp = nfp_nsp_open(pf->cpp);
if (IS_ERR(nsp)) {
err = PTR_ERR(nsp);
nfp_warn(pf->cpp, "can't access NSP: %d\n", err);
return err;
}
snprintf(hwinfo, sizeof(hwinfo), arg->hwinfo_name);
err = nfp_nsp_hwinfo_lookup_optional(nsp, hwinfo, sizeof(hwinfo),
arg->default_hi_val);
if (err) {
nfp_warn(pf->cpp, "HWinfo lookup failed: %d\n", err);
goto exit_close_nsp;
}
err = kstrtol(hwinfo, 0, &value);
if (err || value < 0 || value > arg->max_hi_val) {
nfp_warn(pf->cpp, "HWinfo '%s' value %li invalid\n",
arg->hwinfo_name, value);
if (arg->invalid_dl_val >= 0)
ctx->val.vu8 = arg->invalid_dl_val;
else
err = arg->invalid_dl_val;
goto exit_close_nsp;
}
ctx->val.vu8 = arg->hi_to_dl[value];
exit_close_nsp:
nfp_nsp_close(nsp);
return err;
}
static int
nfp_devlink_param_u8_set(struct devlink *devlink, u32 id,
struct devlink_param_gset_ctx *ctx)
{
const struct nfp_devlink_param_u8_arg *arg;
struct nfp_pf *pf = devlink_priv(devlink);
struct nfp_nsp *nsp;
char hwinfo[32];
int err;
if (id >= ARRAY_SIZE(nfp_devlink_u8_args))
return -EOPNOTSUPP;
arg = &nfp_devlink_u8_args[id];
nsp = nfp_nsp_open(pf->cpp);
if (IS_ERR(nsp)) {
err = PTR_ERR(nsp);
nfp_warn(pf->cpp, "can't access NSP: %d\n", err);
return err;
}
/* Note the value has already been validated. */
snprintf(hwinfo, sizeof(hwinfo), "%s=%u",
arg->hwinfo_name, arg->dl_to_hi[ctx->val.vu8]);
err = nfp_nsp_hwinfo_set(nsp, hwinfo, sizeof(hwinfo));
if (err) {
nfp_warn(pf->cpp, "HWinfo set failed: %d\n", err);
goto exit_close_nsp;
}
exit_close_nsp:
nfp_nsp_close(nsp);
return err;
}
static int
nfp_devlink_param_u8_validate(struct devlink *devlink, u32 id,
union devlink_param_value val,
struct netlink_ext_ack *extack)
{
const struct nfp_devlink_param_u8_arg *arg;
if (id >= ARRAY_SIZE(nfp_devlink_u8_args))
return -EOPNOTSUPP;
arg = &nfp_devlink_u8_args[id];
if (val.vu8 > arg->max_dl_val) {
NL_SET_ERR_MSG_MOD(extack, "parameter out of range");
return -EINVAL;
}
if (val.vu8 == arg->invalid_dl_val) {
NL_SET_ERR_MSG_MOD(extack, "unknown/invalid value specified");
return -EINVAL;
}
return 0;
}
static const struct devlink_param nfp_devlink_params[] = {
DEVLINK_PARAM_GENERIC(FW_LOAD_POLICY,
BIT(DEVLINK_PARAM_CMODE_PERMANENT),
nfp_devlink_param_u8_get,
nfp_devlink_param_u8_set,
nfp_devlink_param_u8_validate),
DEVLINK_PARAM_GENERIC(RESET_DEV_ON_DRV_PROBE,
BIT(DEVLINK_PARAM_CMODE_PERMANENT),
nfp_devlink_param_u8_get,
nfp_devlink_param_u8_set,
nfp_devlink_param_u8_validate),
};
static int nfp_devlink_supports_params(struct nfp_pf *pf)
{
struct nfp_nsp *nsp;
bool supported;
int err;
nsp = nfp_nsp_open(pf->cpp);
if (IS_ERR(nsp)) {
err = PTR_ERR(nsp);
dev_err(&pf->pdev->dev, "Failed to access the NSP: %d\n", err);
return err;
}
supported = nfp_nsp_has_hwinfo_lookup(nsp) &&
nfp_nsp_has_hwinfo_set(nsp);
nfp_nsp_close(nsp);
return supported;
}
int nfp_devlink_params_register(struct nfp_pf *pf)
{
struct devlink *devlink = priv_to_devlink(pf);
int err;
err = nfp_devlink_supports_params(pf);
if (err <= 0)
return err;
err = devlink_params_register(devlink, nfp_devlink_params,
ARRAY_SIZE(nfp_devlink_params));
if (err)
return err;
devlink_params_publish(devlink);
return 0;
}
void nfp_devlink_params_unregister(struct nfp_pf *pf)
{
int err;
err = nfp_devlink_supports_params(pf);
if (err <= 0)
return;
devlink_params_unregister(priv_to_devlink(pf), nfp_devlink_params,
ARRAY_SIZE(nfp_devlink_params));
}
......@@ -352,7 +352,7 @@ nfp_net_fw_request(struct pci_dev *pdev, struct nfp_pf *pf, const char *name)
err = request_firmware_direct(&fw, name, &pdev->dev);
nfp_info(pf->cpp, " %s: %s\n",
name, err ? "not found" : "found, loading...");
name, err ? "not found" : "found");
if (err)
return NULL;
......@@ -430,8 +430,35 @@ nfp_net_fw_find(struct pci_dev *pdev, struct nfp_pf *pf)
return nfp_net_fw_request(pdev, pf, fw_name);
}
static int
nfp_get_fw_policy_value(struct pci_dev *pdev, struct nfp_nsp *nsp,
const char *key, const char *default_val, int max_val,
int *value)
{
char hwinfo[64];
long hi_val;
int err;
snprintf(hwinfo, sizeof(hwinfo), key);
err = nfp_nsp_hwinfo_lookup_optional(nsp, hwinfo, sizeof(hwinfo),
default_val);
if (err)
return err;
err = kstrtol(hwinfo, 0, &hi_val);
if (err || hi_val < 0 || hi_val > max_val) {
dev_warn(&pdev->dev,
"Invalid value '%s' from '%s', ignoring\n",
hwinfo, key);
err = kstrtol(default_val, 0, &hi_val);
}
*value = hi_val;
return err;
}
/**
* nfp_net_fw_load() - Load the firmware image
* nfp_fw_load() - Load the firmware image
* @pdev: PCI Device structure
* @pf: NFP PF Device structure
* @nsp: NFP SP handle
......@@ -441,44 +468,107 @@ nfp_net_fw_find(struct pci_dev *pdev, struct nfp_pf *pf)
static int
nfp_fw_load(struct pci_dev *pdev, struct nfp_pf *pf, struct nfp_nsp *nsp)
{
const struct firmware *fw;
bool do_reset, fw_loaded = false;
const struct firmware *fw = NULL;
int err, reset, policy, ifcs = 0;
char *token, *ptr;
char hwinfo[64];
u16 interface;
int err;
snprintf(hwinfo, sizeof(hwinfo), "abi_drv_load_ifc");
err = nfp_nsp_hwinfo_lookup_optional(nsp, hwinfo, sizeof(hwinfo),
NFP_NSP_DRV_LOAD_IFC_DEFAULT);
if (err)
return err;
interface = nfp_cpp_interface(pf->cpp);
if (NFP_CPP_INTERFACE_UNIT_of(interface) != 0) {
/* Only Unit 0 should reset or load firmware */
ptr = hwinfo;
while ((token = strsep(&ptr, ","))) {
unsigned long interface_hi;
err = kstrtoul(token, 0, &interface_hi);
if (err) {
dev_err(&pdev->dev,
"Failed to parse interface '%s': %d\n",
token, err);
return err;
}
ifcs++;
if (interface == interface_hi)
break;
}
if (!token) {
dev_info(&pdev->dev, "Firmware will be loaded by partner\n");
return 0;
}
err = nfp_get_fw_policy_value(pdev, nsp, "abi_drv_reset",
NFP_NSP_DRV_RESET_DEFAULT,
NFP_NSP_DRV_RESET_NEVER, &reset);
if (err)
return err;
err = nfp_get_fw_policy_value(pdev, nsp, "app_fw_from_flash",
NFP_NSP_APP_FW_LOAD_DEFAULT,
NFP_NSP_APP_FW_LOAD_PREF, &policy);
if (err)
return err;
fw = nfp_net_fw_find(pdev, pf);
if (!fw) {
if (nfp_nsp_has_stored_fw_load(nsp))
nfp_nsp_load_stored_fw(nsp);
return 0;
}
do_reset = reset == NFP_NSP_DRV_RESET_ALWAYS ||
(fw && reset == NFP_NSP_DRV_RESET_DISK);
dev_info(&pdev->dev, "Soft-reset, loading FW image\n");
if (do_reset) {
dev_info(&pdev->dev, "Soft-resetting the NFP\n");
err = nfp_nsp_device_soft_reset(nsp);
if (err < 0) {
dev_err(&pdev->dev, "Failed to soft reset the NFP: %d\n",
err);
dev_err(&pdev->dev,
"Failed to soft reset the NFP: %d\n", err);
goto exit_release_fw;
}
}
if (fw && policy != NFP_NSP_APP_FW_LOAD_FLASH) {
if (nfp_nsp_has_fw_loaded(nsp) && nfp_nsp_fw_loaded(nsp))
goto exit_release_fw;
err = nfp_nsp_load_fw(nsp, fw);
if (err < 0) {
dev_err(&pdev->dev, "FW loading failed: %d\n", err);
dev_err(&pdev->dev, "FW loading failed: %d\n",
err);
goto exit_release_fw;
}
dev_info(&pdev->dev, "Finished loading FW image\n");
fw_loaded = true;
} else if (policy != NFP_NSP_APP_FW_LOAD_DISK &&
nfp_nsp_has_stored_fw_load(nsp)) {
/* Don't propagate this error to stick with legacy driver
* behavior, failure will be detected later during init.
*/
if (!nfp_nsp_load_stored_fw(nsp))
dev_info(&pdev->dev, "Finished loading stored FW image\n");
/* Don't flag the fw_loaded in this case since other devices
* may reuse the firmware when configured this way
*/
} else {
dev_warn(&pdev->dev, "Didn't load firmware, please update flash or reconfigure card\n");
}
exit_release_fw:
release_firmware(fw);
return err < 0 ? err : 1;
/* We don't want to unload firmware when other devices may still be
* dependent on it, which could be the case if there are multiple
* devices that could load firmware.
*/
if (fw_loaded && ifcs == 1)
pf->unload_fw_on_remove = true;
return err < 0 ? err : fw_loaded;
}
static void
......@@ -704,7 +794,7 @@ static int nfp_pci_probe(struct pci_dev *pdev,
err_fw_unload:
kfree(pf->rtbl);
nfp_mip_close(pf->mip);
if (pf->fw_loaded)
if (pf->unload_fw_on_remove)
nfp_fw_unload(pf);
kfree(pf->eth_tbl);
kfree(pf->nspi);
......@@ -744,7 +834,7 @@ static void __nfp_pci_shutdown(struct pci_dev *pdev, bool unload_fw)
vfree(pf->dumpspec);
kfree(pf->rtbl);
nfp_mip_close(pf->mip);
if (unload_fw && pf->fw_loaded)
if (unload_fw && pf->unload_fw_on_remove)
nfp_fw_unload(pf);
destroy_workqueue(pf->wq);
......
......@@ -64,6 +64,7 @@ struct nfp_dumpspec {
* @limit_vfs: Number of VFs supported by firmware (~0 for PCI limit)
* @num_vfs: Number of SR-IOV VFs enabled
* @fw_loaded: Is the firmware loaded?
* @unload_fw_on_remove:Do we need to unload firmware on driver removal?
* @ctrl_vnic: Pointer to the control vNIC if available
* @mip: MIP handle
* @rtbl: RTsym table
......@@ -110,6 +111,7 @@ struct nfp_pf {
unsigned int num_vfs;
bool fw_loaded;
bool unload_fw_on_remove;
struct nfp_net *ctrl_vnic;
......@@ -185,4 +187,7 @@ int nfp_shared_buf_pool_get(struct nfp_pf *pf, unsigned int sb, u16 pool_index,
int nfp_shared_buf_pool_set(struct nfp_pf *pf, unsigned int sb,
u16 pool_index, u32 size,
enum devlink_sb_threshold_type threshold_type);
int nfp_devlink_params_register(struct nfp_pf *pf);
void nfp_devlink_params_unregister(struct nfp_pf *pf);
#endif /* NFP_MAIN_H */
......@@ -709,6 +709,10 @@ int nfp_net_pci_probe(struct nfp_pf *pf)
if (err)
goto err_devlink_unreg;
err = nfp_devlink_params_register(pf);
if (err)
goto err_shared_buf_unreg;
mutex_lock(&pf->lock);
pf->ddir = nfp_net_debugfs_device_add(pf->pdev);
......@@ -742,6 +746,8 @@ int nfp_net_pci_probe(struct nfp_pf *pf)
err_clean_ddir:
nfp_net_debugfs_dir_clean(&pf->ddir);
mutex_unlock(&pf->lock);
nfp_devlink_params_unregister(pf);
err_shared_buf_unreg:
nfp_shared_buf_unregister(pf);
err_devlink_unreg:
cancel_work_sync(&pf->port_refresh_work);
......@@ -771,6 +777,7 @@ void nfp_net_pci_remove(struct nfp_pf *pf)
mutex_unlock(&pf->lock);
nfp_devlink_params_unregister(pf);
nfp_shared_buf_unregister(pf);
devlink_unregister(priv_to_devlink(pf));
......
......@@ -96,6 +96,8 @@ enum nfp_nsp_cmd {
SPCODE_NSP_IDENTIFY = 13, /* Read NSP version */
SPCODE_FW_STORED = 16, /* If no FW loaded, load flash app FW */
SPCODE_HWINFO_LOOKUP = 17, /* Lookup HWinfo with overwrites etc. */
SPCODE_HWINFO_SET = 18, /* Set HWinfo entry */
SPCODE_FW_LOADED = 19, /* Is application firmware loaded */
SPCODE_VERSIONS = 21, /* Report FW versions */
SPCODE_READ_SFF_EEPROM = 22, /* Read module EEPROM */
};
......@@ -143,6 +145,8 @@ struct nfp_nsp {
* @option: NFP SP Command Argument
* @buf: NFP SP Buffer Address
* @error_cb: Callback for interpreting option if error occurred
* @error_quiet:Don't print command error/warning. Protocol errors are still
* logged.
*/
struct nfp_nsp_command_arg {
u16 code;
......@@ -151,6 +155,7 @@ struct nfp_nsp_command_arg {
u32 option;
u64 buf;
void (*error_cb)(struct nfp_nsp *state, u32 ret_val);
bool error_quiet;
};
/**
......@@ -405,8 +410,10 @@ __nfp_nsp_command(struct nfp_nsp *state, const struct nfp_nsp_command_arg *arg)
err = FIELD_GET(NSP_STATUS_RESULT, reg);
if (err) {
if (!arg->error_quiet)
nfp_warn(cpp, "Result (error) code set: %d (%d) command: %d\n",
-err, (int)ret_val, arg->code);
if (arg->error_cb)
arg->error_cb(state, ret_val);
else
......@@ -891,12 +898,14 @@ int nfp_nsp_load_stored_fw(struct nfp_nsp *state)
}
static int
__nfp_nsp_hwinfo_lookup(struct nfp_nsp *state, void *buf, unsigned int size)
__nfp_nsp_hwinfo_lookup(struct nfp_nsp *state, void *buf, unsigned int size,
bool optional)
{
struct nfp_nsp_command_buf_arg hwinfo_lookup = {
{
.code = SPCODE_HWINFO_LOOKUP,
.option = size,
.error_quiet = optional,
},
.in_buf = buf,
.in_size = size,
......@@ -913,7 +922,7 @@ int nfp_nsp_hwinfo_lookup(struct nfp_nsp *state, void *buf, unsigned int size)
size = min_t(u32, size, NFP_HWINFO_LOOKUP_SIZE);
err = __nfp_nsp_hwinfo_lookup(state, buf, size);
err = __nfp_nsp_hwinfo_lookup(state, buf, size, false);
if (err)
return err;
......@@ -925,6 +934,66 @@ int nfp_nsp_hwinfo_lookup(struct nfp_nsp *state, void *buf, unsigned int size)
return 0;
}
int nfp_nsp_hwinfo_lookup_optional(struct nfp_nsp *state, void *buf,
unsigned int size, const char *default_val)
{
int err;
/* Ensure that the default value is usable irrespective of whether
* it is actually going to be used.
*/
if (strnlen(default_val, size) == size)
return -EINVAL;
if (!nfp_nsp_has_hwinfo_lookup(state)) {
strcpy(buf, default_val);
return 0;
}
size = min_t(u32, size, NFP_HWINFO_LOOKUP_SIZE);
err = __nfp_nsp_hwinfo_lookup(state, buf, size, true);
if (err) {
if (err == -ENOENT) {
strcpy(buf, default_val);
return 0;
}
nfp_err(state->cpp, "NSP HWinfo lookup failed: %d\n", err);
return err;
}
if (strnlen(buf, size) == size) {
nfp_err(state->cpp, "NSP HWinfo value not NULL-terminated\n");
return -EINVAL;
}
return 0;
}
int nfp_nsp_hwinfo_set(struct nfp_nsp *state, void *buf, unsigned int size)
{
struct nfp_nsp_command_buf_arg hwinfo_set = {
{
.code = SPCODE_HWINFO_SET,
.option = size,
},
.in_buf = buf,
.in_size = size,
};
return nfp_nsp_command_buf(state, &hwinfo_set);
}
int nfp_nsp_fw_loaded(struct nfp_nsp *state)
{
const struct nfp_nsp_command_arg arg = {
.code = SPCODE_FW_LOADED,
};
return __nfp_nsp_command(state, &arg);
}
int nfp_nsp_versions(struct nfp_nsp *state, void *buf, unsigned int size)
{
struct nfp_nsp_command_buf_arg versions = {
......
......@@ -22,6 +22,10 @@ int nfp_nsp_write_flash(struct nfp_nsp *state, const struct firmware *fw);
int nfp_nsp_mac_reinit(struct nfp_nsp *state);
int nfp_nsp_load_stored_fw(struct nfp_nsp *state);
int nfp_nsp_hwinfo_lookup(struct nfp_nsp *state, void *buf, unsigned int size);
int nfp_nsp_hwinfo_lookup_optional(struct nfp_nsp *state, void *buf,
unsigned int size, const char *default_val);
int nfp_nsp_hwinfo_set(struct nfp_nsp *state, void *buf, unsigned int size);
int nfp_nsp_fw_loaded(struct nfp_nsp *state);
int nfp_nsp_read_module_eeprom(struct nfp_nsp *state, int eth_index,
unsigned int offset, void *data,
unsigned int len, unsigned int *read_len);
......@@ -41,6 +45,16 @@ static inline bool nfp_nsp_has_hwinfo_lookup(struct nfp_nsp *state)
return nfp_nsp_get_abi_ver_minor(state) > 24;
}
static inline bool nfp_nsp_has_hwinfo_set(struct nfp_nsp *state)
{
return nfp_nsp_get_abi_ver_minor(state) > 25;
}
static inline bool nfp_nsp_has_fw_loaded(struct nfp_nsp *state)
{
return nfp_nsp_get_abi_ver_minor(state) > 25;
}
static inline bool nfp_nsp_has_versions(struct nfp_nsp *state)
{
return nfp_nsp_get_abi_ver_minor(state) > 27;
......@@ -88,6 +102,21 @@ enum nfp_eth_fec {
#define NFP_FEC_REED_SOLOMON BIT(NFP_FEC_REED_SOLOMON_BIT)
#define NFP_FEC_DISABLED BIT(NFP_FEC_DISABLED_BIT)
/* Defines the valid values of the 'abi_drv_reset' hwinfo key */
#define NFP_NSP_DRV_RESET_DISK 0
#define NFP_NSP_DRV_RESET_ALWAYS 1
#define NFP_NSP_DRV_RESET_NEVER 2
#define NFP_NSP_DRV_RESET_DEFAULT "0"
/* Defines the valid values of the 'app_fw_from_flash' hwinfo key */
#define NFP_NSP_APP_FW_LOAD_DISK 0
#define NFP_NSP_APP_FW_LOAD_FLASH 1
#define NFP_NSP_APP_FW_LOAD_PREF 2
#define NFP_NSP_APP_FW_LOAD_DEFAULT "2"
/* Define the default value for the 'abi_drv_load_ifc' key */
#define NFP_NSP_DRV_LOAD_IFC_DEFAULT "0x10ff"
/**
* struct nfp_eth_table - ETH table information
* @count: number of table entries
......
......@@ -398,6 +398,7 @@ enum devlink_param_generic_id {
DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MAX,
DEVLINK_PARAM_GENERIC_ID_MSIX_VEC_PER_PF_MIN,
DEVLINK_PARAM_GENERIC_ID_FW_LOAD_POLICY,
DEVLINK_PARAM_GENERIC_ID_RESET_DEV_ON_DRV_PROBE,
/* add new param generic ids above here*/
__DEVLINK_PARAM_GENERIC_ID_MAX,
......@@ -428,6 +429,10 @@ enum devlink_param_generic_id {
#define DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_NAME "fw_load_policy"
#define DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_TYPE DEVLINK_PARAM_TYPE_U8
#define DEVLINK_PARAM_GENERIC_RESET_DEV_ON_DRV_PROBE_NAME \
"reset_dev_on_drv_probe"
#define DEVLINK_PARAM_GENERIC_RESET_DEV_ON_DRV_PROBE_TYPE DEVLINK_PARAM_TYPE_U8
#define DEVLINK_PARAM_GENERIC(_id, _cmodes, _get, _set, _validate) \
{ \
.id = DEVLINK_PARAM_GENERIC_ID_##_id, \
......
......@@ -202,6 +202,14 @@ enum devlink_param_cmode {
enum devlink_param_fw_load_policy_value {
DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DRIVER,
DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_FLASH,
DEVLINK_PARAM_FW_LOAD_POLICY_VALUE_DISK,
};
enum devlink_param_reset_dev_on_drv_probe_value {
DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_UNKNOWN,
DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_ALWAYS,
DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_NEVER,
DEVLINK_PARAM_RESET_DEV_ON_DRV_PROBE_VALUE_DISK,
};
enum {
......
......@@ -2852,6 +2852,11 @@ static const struct devlink_param devlink_param_generic[] = {
.name = DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_NAME,
.type = DEVLINK_PARAM_GENERIC_FW_LOAD_POLICY_TYPE,
},
{
.id = DEVLINK_PARAM_GENERIC_ID_RESET_DEV_ON_DRV_PROBE,
.name = DEVLINK_PARAM_GENERIC_RESET_DEV_ON_DRV_PROBE_NAME,
.type = DEVLINK_PARAM_GENERIC_RESET_DEV_ON_DRV_PROBE_TYPE,
},
};
static int devlink_param_generic_verify(const struct devlink_param *param)
......
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