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: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
074be7fd99
|
@ -0,0 +1,133 @@
|
|||
.. 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.
|
|
@ -0,0 +1,5 @@
|
|||
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 \
|
||||
|
|
|
@ -0,0 +1,254 @@
|
|||
// 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);
|
||||
|
||||
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);
|
||||
goto exit_release_fw;
|
||||
}
|
||||
}
|
||||
|
||||
dev_info(&pdev->dev, "Soft-reset, loading FW image\n");
|
||||
err = nfp_nsp_device_soft_reset(nsp);
|
||||
if (err < 0) {
|
||||
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);
|
||||
goto exit_release_fw;
|
||||
}
|
||||
err = nfp_nsp_load_fw(nsp, fw);
|
||||
if (err < 0) {
|
||||
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)) {
|
||||
|
||||
dev_info(&pdev->dev, "Finished loading FW image\n");
|
||||
/* 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 @@ err_net_remove:
|
|||
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 @@ err_free_vnics:
|
|||
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) {
|
||||
nfp_warn(cpp, "Result (error) code set: %d (%d) command: %d\n",
|
||||
-err, (int)ret_val, arg->code);
|
||||
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)
|
||||
|
|
Loading…
Reference in New Issue