linux_dsm_epyc7002/drivers/net/ethernet/netronome/nfp/nfp_main.c
Dirk van der Merwe 790d23e7c5 nfp: implement PCI driver shutdown callback
Device may be shutdown without the hardware being reinitialized, in
which case we want to ensure we cleanup properly.

This is especially important for kexec with traffic flowing.

The shutdown procedures resembles the remove procedures, so we can reuse
those common tasks.

Signed-off-by: Dirk van der Merwe <dirk.vandermerwe@netronome.com>
Reviewed-by: Jakub Kicinski <jakub.kicinski@netronome.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2019-04-26 12:08:13 -04:00

828 lines
19 KiB
C

// SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
/* Copyright (C) 2015-2018 Netronome Systems, Inc. */
/*
* nfp_main.c
* Authors: Jakub Kicinski <jakub.kicinski@netronome.com>
* Alejandro Lucero <alejandro.lucero@netronome.com>
* Jason McMullan <jason.mcmullan@netronome.com>
* Rolf Neugebauer <rolf.neugebauer@netronome.com>
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/firmware.h>
#include <linux/vermagic.h>
#include <linux/vmalloc.h>
#include <net/devlink.h>
#include "nfpcore/nfp.h"
#include "nfpcore/nfp_cpp.h"
#include "nfpcore/nfp_nffw.h"
#include "nfpcore/nfp_nsp.h"
#include "nfpcore/nfp6000_pcie.h"
#include "nfp_abi.h"
#include "nfp_app.h"
#include "nfp_main.h"
#include "nfp_net.h"
static const char nfp_driver_name[] = "nfp";
const char nfp_driver_version[] = VERMAGIC_STRING;
static const struct pci_device_id nfp_pci_device_ids[] = {
{ PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NETRONOME_NFP6000,
PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID,
PCI_ANY_ID, 0,
},
{ PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NETRONOME_NFP5000,
PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID,
PCI_ANY_ID, 0,
},
{ PCI_VENDOR_ID_NETRONOME, PCI_DEVICE_ID_NETRONOME_NFP4000,
PCI_VENDOR_ID_NETRONOME, PCI_ANY_ID,
PCI_ANY_ID, 0,
},
{ 0, } /* Required last entry. */
};
MODULE_DEVICE_TABLE(pci, nfp_pci_device_ids);
int nfp_pf_rtsym_read_optional(struct nfp_pf *pf, const char *format,
unsigned int default_val)
{
char name[256];
int err = 0;
u64 val;
snprintf(name, sizeof(name), format, nfp_cppcore_pcie_unit(pf->cpp));
val = nfp_rtsym_read_le(pf->rtbl, name, &err);
if (err) {
if (err == -ENOENT)
return default_val;
nfp_err(pf->cpp, "Unable to read symbol %s\n", name);
return err;
}
return val;
}
u8 __iomem *
nfp_pf_map_rtsym(struct nfp_pf *pf, const char *name, const char *sym_fmt,
unsigned int min_size, struct nfp_cpp_area **area)
{
char pf_symbol[256];
snprintf(pf_symbol, sizeof(pf_symbol), sym_fmt,
nfp_cppcore_pcie_unit(pf->cpp));
return nfp_rtsym_map(pf->rtbl, pf_symbol, name, min_size, area);
}
/* Callers should hold the devlink instance lock */
int nfp_mbox_cmd(struct nfp_pf *pf, u32 cmd, void *in_data, u64 in_length,
void *out_data, u64 out_length)
{
unsigned long err_at;
u64 max_data_sz;
u32 val = 0;
int n, err;
if (!pf->mbox)
return -EOPNOTSUPP;
max_data_sz = nfp_rtsym_size(pf->mbox) - NFP_MBOX_SYM_MIN_SIZE;
/* Check if cmd field is clear */
err = nfp_rtsym_readl(pf->cpp, pf->mbox, NFP_MBOX_CMD, &val);
if (err || val) {
nfp_warn(pf->cpp, "failed to issue command (%u): %u, err: %d\n",
cmd, val, err);
return err ?: -EBUSY;
}
in_length = min(in_length, max_data_sz);
n = nfp_rtsym_write(pf->cpp, pf->mbox, NFP_MBOX_DATA, in_data,
in_length);
if (n != in_length)
return -EIO;
/* Write data_len and wipe reserved */
err = nfp_rtsym_writeq(pf->cpp, pf->mbox, NFP_MBOX_DATA_LEN, in_length);
if (err)
return err;
/* Read back for ordering */
err = nfp_rtsym_readl(pf->cpp, pf->mbox, NFP_MBOX_DATA_LEN, &val);
if (err)
return err;
/* Write cmd and wipe return value */
err = nfp_rtsym_writeq(pf->cpp, pf->mbox, NFP_MBOX_CMD, cmd);
if (err)
return err;
err_at = jiffies + 5 * HZ;
while (true) {
/* Wait for command to go to 0 (NFP_MBOX_NO_CMD) */
err = nfp_rtsym_readl(pf->cpp, pf->mbox, NFP_MBOX_CMD, &val);
if (err)
return err;
if (!val)
break;
if (time_is_before_eq_jiffies(err_at))
return -ETIMEDOUT;
msleep(5);
}
/* Copy output if any (could be error info, do it before reading ret) */
err = nfp_rtsym_readl(pf->cpp, pf->mbox, NFP_MBOX_DATA_LEN, &val);
if (err)
return err;
out_length = min_t(u32, val, min(out_length, max_data_sz));
n = nfp_rtsym_read(pf->cpp, pf->mbox, NFP_MBOX_DATA,
out_data, out_length);
if (n != out_length)
return -EIO;
/* Check if there is an error */
err = nfp_rtsym_readl(pf->cpp, pf->mbox, NFP_MBOX_RET, &val);
if (err)
return err;
if (val)
return -val;
return out_length;
}
static bool nfp_board_ready(struct nfp_pf *pf)
{
const char *cp;
long state;
int err;
cp = nfp_hwinfo_lookup(pf->hwinfo, "board.state");
if (!cp)
return false;
err = kstrtol(cp, 0, &state);
if (err < 0)
return false;
return state == 15;
}
static int nfp_pf_board_state_wait(struct nfp_pf *pf)
{
const unsigned long wait_until = jiffies + 10 * HZ;
while (!nfp_board_ready(pf)) {
if (time_is_before_eq_jiffies(wait_until)) {
nfp_err(pf->cpp, "NFP board initialization timeout\n");
return -EINVAL;
}
nfp_info(pf->cpp, "waiting for board initialization\n");
if (msleep_interruptible(500))
return -ERESTARTSYS;
/* Refresh cached information */
kfree(pf->hwinfo);
pf->hwinfo = nfp_hwinfo_read(pf->cpp);
}
return 0;
}
static int nfp_pcie_sriov_read_nfd_limit(struct nfp_pf *pf)
{
int err;
pf->limit_vfs = nfp_rtsym_read_le(pf->rtbl, "nfd_vf_cfg_max_vfs", &err);
if (err) {
/* For backwards compatibility if symbol not found allow all */
pf->limit_vfs = ~0;
if (err == -ENOENT)
return 0;
nfp_warn(pf->cpp, "Warning: VF limit read failed: %d\n", err);
return err;
}
err = pci_sriov_set_totalvfs(pf->pdev, pf->limit_vfs);
if (err)
nfp_warn(pf->cpp, "Failed to set VF count in sysfs: %d\n", err);
return 0;
}
static int nfp_pcie_sriov_enable(struct pci_dev *pdev, int num_vfs)
{
#ifdef CONFIG_PCI_IOV
struct nfp_pf *pf = pci_get_drvdata(pdev);
int err;
if (num_vfs > pf->limit_vfs) {
nfp_info(pf->cpp, "Firmware limits number of VFs to %u\n",
pf->limit_vfs);
return -EINVAL;
}
err = pci_enable_sriov(pdev, num_vfs);
if (err) {
dev_warn(&pdev->dev, "Failed to enable PCI SR-IOV: %d\n", err);
return err;
}
mutex_lock(&pf->lock);
err = nfp_app_sriov_enable(pf->app, num_vfs);
if (err) {
dev_warn(&pdev->dev,
"App specific PCI SR-IOV configuration failed: %d\n",
err);
goto err_sriov_disable;
}
pf->num_vfs = num_vfs;
dev_dbg(&pdev->dev, "Created %d VFs.\n", pf->num_vfs);
mutex_unlock(&pf->lock);
return num_vfs;
err_sriov_disable:
mutex_unlock(&pf->lock);
pci_disable_sriov(pdev);
return err;
#endif
return 0;
}
static int nfp_pcie_sriov_disable(struct pci_dev *pdev)
{
#ifdef CONFIG_PCI_IOV
struct nfp_pf *pf = pci_get_drvdata(pdev);
mutex_lock(&pf->lock);
/* If the VFs are assigned we cannot shut down SR-IOV without
* causing issues, so just leave the hardware available but
* disabled
*/
if (pci_vfs_assigned(pdev)) {
dev_warn(&pdev->dev, "Disabling while VFs assigned - VFs will not be deallocated\n");
mutex_unlock(&pf->lock);
return -EPERM;
}
nfp_app_sriov_disable(pf->app);
pf->num_vfs = 0;
mutex_unlock(&pf->lock);
pci_disable_sriov(pdev);
dev_dbg(&pdev->dev, "Removed VFs.\n");
#endif
return 0;
}
static int nfp_pcie_sriov_configure(struct pci_dev *pdev, int num_vfs)
{
if (!pci_get_drvdata(pdev))
return -ENOENT;
if (num_vfs == 0)
return nfp_pcie_sriov_disable(pdev);
else
return nfp_pcie_sriov_enable(pdev, num_vfs);
}
int nfp_flash_update_common(struct nfp_pf *pf, const char *path,
struct netlink_ext_ack *extack)
{
struct device *dev = &pf->pdev->dev;
const struct firmware *fw;
struct nfp_nsp *nsp;
int err;
nsp = nfp_nsp_open(pf->cpp);
if (IS_ERR(nsp)) {
err = PTR_ERR(nsp);
if (extack)
NL_SET_ERR_MSG_MOD(extack, "can't access NSP");
else
dev_err(dev, "Failed to access the NSP: %d\n", err);
return err;
}
err = request_firmware_direct(&fw, path, dev);
if (err) {
NL_SET_ERR_MSG_MOD(extack,
"unable to read flash file from disk");
goto exit_close_nsp;
}
dev_info(dev, "Please be patient while writing flash image: %s\n",
path);
err = nfp_nsp_write_flash(nsp, fw);
if (err < 0)
goto exit_release_fw;
dev_info(dev, "Finished writing flash image\n");
err = 0;
exit_release_fw:
release_firmware(fw);
exit_close_nsp:
nfp_nsp_close(nsp);
return err;
}
static const struct firmware *
nfp_net_fw_request(struct pci_dev *pdev, struct nfp_pf *pf, const char *name)
{
const struct firmware *fw = NULL;
int err;
err = request_firmware_direct(&fw, name, &pdev->dev);
nfp_info(pf->cpp, " %s: %s\n",
name, err ? "not found" : "found, loading...");
if (err)
return NULL;
return fw;
}
/**
* nfp_net_fw_find() - Find the correct firmware image for netdev mode
* @pdev: PCI Device structure
* @pf: NFP PF Device structure
*
* Return: firmware if found and requested successfully.
*/
static const struct firmware *
nfp_net_fw_find(struct pci_dev *pdev, struct nfp_pf *pf)
{
struct nfp_eth_table_port *port;
const struct firmware *fw;
const char *fw_model;
char fw_name[256];
const u8 *serial;
u16 interface;
int spc, i, j;
nfp_info(pf->cpp, "Looking for firmware file in order of priority:\n");
/* First try to find a firmware image specific for this device */
interface = nfp_cpp_interface(pf->cpp);
nfp_cpp_serial(pf->cpp, &serial);
sprintf(fw_name, "netronome/serial-%pMF-%02hhx-%02hhx.nffw",
serial, interface >> 8, interface & 0xff);
fw = nfp_net_fw_request(pdev, pf, fw_name);
if (fw)
return fw;
/* Then try the PCI name */
sprintf(fw_name, "netronome/pci-%s.nffw", pci_name(pdev));
fw = nfp_net_fw_request(pdev, pf, fw_name);
if (fw)
return fw;
/* Finally try the card type and media */
if (!pf->eth_tbl) {
dev_err(&pdev->dev, "Error: can't identify media config\n");
return NULL;
}
fw_model = nfp_hwinfo_lookup(pf->hwinfo, "assembly.partno");
if (!fw_model) {
dev_err(&pdev->dev, "Error: can't read part number\n");
return NULL;
}
spc = ARRAY_SIZE(fw_name);
spc -= snprintf(fw_name, spc, "netronome/nic_%s", fw_model);
for (i = 0; spc > 0 && i < pf->eth_tbl->count; i += j) {
port = &pf->eth_tbl->ports[i];
j = 1;
while (i + j < pf->eth_tbl->count &&
port->speed == port[j].speed)
j++;
spc -= snprintf(&fw_name[ARRAY_SIZE(fw_name) - spc], spc,
"_%dx%d", j, port->speed / 1000);
}
if (spc <= 0)
return NULL;
spc -= snprintf(&fw_name[ARRAY_SIZE(fw_name) - spc], spc, ".nffw");
if (spc <= 0)
return NULL;
return nfp_net_fw_request(pdev, pf, fw_name);
}
/**
* nfp_net_fw_load() - Load the firmware image
* @pdev: PCI Device structure
* @pf: NFP PF Device structure
* @nsp: NFP SP handle
*
* Return: -ERRNO, 0 for no firmware loaded, 1 for firmware loaded
*/
static int
nfp_fw_load(struct pci_dev *pdev, struct nfp_pf *pf, struct nfp_nsp *nsp)
{
const struct firmware *fw;
u16 interface;
int err;
interface = nfp_cpp_interface(pf->cpp);
if (NFP_CPP_INTERFACE_UNIT_of(interface) != 0) {
/* Only Unit 0 should reset or load firmware */
dev_info(&pdev->dev, "Firmware will be loaded by partner\n");
return 0;
}
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;
}
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;
}
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");
exit_release_fw:
release_firmware(fw);
return err < 0 ? err : 1;
}
static void
nfp_nsp_init_ports(struct pci_dev *pdev, struct nfp_pf *pf,
struct nfp_nsp *nsp)
{
bool needs_reinit = false;
int i;
pf->eth_tbl = __nfp_eth_read_ports(pf->cpp, nsp);
if (!pf->eth_tbl)
return;
if (!nfp_nsp_has_mac_reinit(nsp))
return;
for (i = 0; i < pf->eth_tbl->count; i++)
needs_reinit |= pf->eth_tbl->ports[i].override_changed;
if (!needs_reinit)
return;
kfree(pf->eth_tbl);
if (nfp_nsp_mac_reinit(nsp))
dev_warn(&pdev->dev, "MAC reinit failed\n");
pf->eth_tbl = __nfp_eth_read_ports(pf->cpp, nsp);
}
static int nfp_nsp_init(struct pci_dev *pdev, struct nfp_pf *pf)
{
struct nfp_nsp *nsp;
int err;
err = nfp_resource_wait(pf->cpp, NFP_RESOURCE_NSP, 30);
if (err)
return err;
nsp = nfp_nsp_open(pf->cpp);
if (IS_ERR(nsp)) {
err = PTR_ERR(nsp);
dev_err(&pdev->dev, "Failed to access the NSP: %d\n", err);
return err;
}
err = nfp_nsp_wait(nsp);
if (err < 0)
goto exit_close_nsp;
nfp_nsp_init_ports(pdev, pf, nsp);
pf->nspi = __nfp_nsp_identify(nsp);
if (pf->nspi)
dev_info(&pdev->dev, "BSP: %s\n", pf->nspi->version);
err = nfp_fw_load(pdev, pf, nsp);
if (err < 0) {
kfree(pf->nspi);
kfree(pf->eth_tbl);
dev_err(&pdev->dev, "Failed to load FW\n");
goto exit_close_nsp;
}
pf->fw_loaded = !!err;
err = 0;
exit_close_nsp:
nfp_nsp_close(nsp);
return err;
}
static void nfp_fw_unload(struct nfp_pf *pf)
{
struct nfp_nsp *nsp;
int err;
nsp = nfp_nsp_open(pf->cpp);
if (IS_ERR(nsp)) {
nfp_err(pf->cpp, "Reset failed, can't open NSP\n");
return;
}
err = nfp_nsp_device_soft_reset(nsp);
if (err < 0)
dev_warn(&pf->pdev->dev, "Couldn't unload firmware: %d\n", err);
else
dev_info(&pf->pdev->dev, "Firmware safely unloaded\n");
nfp_nsp_close(nsp);
}
static int nfp_pf_find_rtsyms(struct nfp_pf *pf)
{
char pf_symbol[256];
unsigned int pf_id;
pf_id = nfp_cppcore_pcie_unit(pf->cpp);
/* Optional per-PCI PF mailbox */
snprintf(pf_symbol, sizeof(pf_symbol), NFP_MBOX_SYM_NAME, pf_id);
pf->mbox = nfp_rtsym_lookup(pf->rtbl, pf_symbol);
if (pf->mbox && nfp_rtsym_size(pf->mbox) < NFP_MBOX_SYM_MIN_SIZE) {
nfp_err(pf->cpp, "PF mailbox symbol too small: %llu < %d\n",
nfp_rtsym_size(pf->mbox), NFP_MBOX_SYM_MIN_SIZE);
return -EINVAL;
}
return 0;
}
static int nfp_pci_probe(struct pci_dev *pdev,
const struct pci_device_id *pci_id)
{
struct devlink *devlink;
struct nfp_pf *pf;
int err;
err = pci_enable_device(pdev);
if (err < 0)
return err;
pci_set_master(pdev);
err = dma_set_mask_and_coherent(&pdev->dev,
DMA_BIT_MASK(NFP_NET_MAX_DMA_BITS));
if (err)
goto err_pci_disable;
err = pci_request_regions(pdev, nfp_driver_name);
if (err < 0) {
dev_err(&pdev->dev, "Unable to reserve pci resources.\n");
goto err_pci_disable;
}
devlink = devlink_alloc(&nfp_devlink_ops, sizeof(*pf));
if (!devlink) {
err = -ENOMEM;
goto err_rel_regions;
}
pf = devlink_priv(devlink);
INIT_LIST_HEAD(&pf->vnics);
INIT_LIST_HEAD(&pf->ports);
mutex_init(&pf->lock);
pci_set_drvdata(pdev, pf);
pf->pdev = pdev;
pf->wq = alloc_workqueue("nfp-%s", 0, 2, pci_name(pdev));
if (!pf->wq) {
err = -ENOMEM;
goto err_pci_priv_unset;
}
pf->cpp = nfp_cpp_from_nfp6000_pcie(pdev);
if (IS_ERR_OR_NULL(pf->cpp)) {
err = PTR_ERR(pf->cpp);
if (err >= 0)
err = -ENOMEM;
goto err_disable_msix;
}
err = nfp_resource_table_init(pf->cpp);
if (err)
goto err_cpp_free;
pf->hwinfo = nfp_hwinfo_read(pf->cpp);
dev_info(&pdev->dev, "Assembly: %s%s%s-%s CPLD: %s\n",
nfp_hwinfo_lookup(pf->hwinfo, "assembly.vendor"),
nfp_hwinfo_lookup(pf->hwinfo, "assembly.partno"),
nfp_hwinfo_lookup(pf->hwinfo, "assembly.serial"),
nfp_hwinfo_lookup(pf->hwinfo, "assembly.revision"),
nfp_hwinfo_lookup(pf->hwinfo, "cpld.version"));
err = nfp_pf_board_state_wait(pf);
if (err)
goto err_hwinfo_free;
err = nfp_nsp_init(pdev, pf);
if (err)
goto err_hwinfo_free;
pf->mip = nfp_mip_open(pf->cpp);
pf->rtbl = __nfp_rtsym_table_read(pf->cpp, pf->mip);
err = nfp_pf_find_rtsyms(pf);
if (err)
goto err_fw_unload;
pf->dump_flag = NFP_DUMP_NSP_DIAG;
pf->dumpspec = nfp_net_dump_load_dumpspec(pf->cpp, pf->rtbl);
err = nfp_pcie_sriov_read_nfd_limit(pf);
if (err)
goto err_fw_unload;
pf->num_vfs = pci_num_vf(pdev);
if (pf->num_vfs > pf->limit_vfs) {
dev_err(&pdev->dev,
"Error: %d VFs already enabled, but loaded FW can only support %d\n",
pf->num_vfs, pf->limit_vfs);
err = -EINVAL;
goto err_fw_unload;
}
err = nfp_net_pci_probe(pf);
if (err)
goto err_fw_unload;
err = nfp_hwmon_register(pf);
if (err) {
dev_err(&pdev->dev, "Failed to register hwmon info\n");
goto err_net_remove;
}
return 0;
err_net_remove:
nfp_net_pci_remove(pf);
err_fw_unload:
kfree(pf->rtbl);
nfp_mip_close(pf->mip);
if (pf->fw_loaded)
nfp_fw_unload(pf);
kfree(pf->eth_tbl);
kfree(pf->nspi);
vfree(pf->dumpspec);
err_hwinfo_free:
kfree(pf->hwinfo);
err_cpp_free:
nfp_cpp_free(pf->cpp);
err_disable_msix:
destroy_workqueue(pf->wq);
err_pci_priv_unset:
pci_set_drvdata(pdev, NULL);
mutex_destroy(&pf->lock);
devlink_free(devlink);
err_rel_regions:
pci_release_regions(pdev);
err_pci_disable:
pci_disable_device(pdev);
return err;
}
static void __nfp_pci_shutdown(struct pci_dev *pdev, bool unload_fw)
{
struct nfp_pf *pf;
pf = pci_get_drvdata(pdev);
if (!pf)
return;
nfp_hwmon_unregister(pf);
nfp_pcie_sriov_disable(pdev);
nfp_net_pci_remove(pf);
vfree(pf->dumpspec);
kfree(pf->rtbl);
nfp_mip_close(pf->mip);
if (unload_fw && pf->fw_loaded)
nfp_fw_unload(pf);
destroy_workqueue(pf->wq);
pci_set_drvdata(pdev, NULL);
kfree(pf->hwinfo);
nfp_cpp_free(pf->cpp);
kfree(pf->eth_tbl);
kfree(pf->nspi);
mutex_destroy(&pf->lock);
devlink_free(priv_to_devlink(pf));
pci_release_regions(pdev);
pci_disable_device(pdev);
}
static void nfp_pci_remove(struct pci_dev *pdev)
{
__nfp_pci_shutdown(pdev, true);
}
static void nfp_pci_shutdown(struct pci_dev *pdev)
{
__nfp_pci_shutdown(pdev, false);
}
static struct pci_driver nfp_pci_driver = {
.name = nfp_driver_name,
.id_table = nfp_pci_device_ids,
.probe = nfp_pci_probe,
.remove = nfp_pci_remove,
.shutdown = nfp_pci_shutdown,
.sriov_configure = nfp_pcie_sriov_configure,
};
static int __init nfp_main_init(void)
{
int err;
pr_info("%s: NFP PCIe Driver, Copyright (C) 2014-2017 Netronome Systems\n",
nfp_driver_name);
nfp_net_debugfs_create();
err = pci_register_driver(&nfp_pci_driver);
if (err < 0)
goto err_destroy_debugfs;
err = pci_register_driver(&nfp_netvf_pci_driver);
if (err)
goto err_unreg_pf;
return err;
err_unreg_pf:
pci_unregister_driver(&nfp_pci_driver);
err_destroy_debugfs:
nfp_net_debugfs_destroy();
return err;
}
static void __exit nfp_main_exit(void)
{
pci_unregister_driver(&nfp_netvf_pci_driver);
pci_unregister_driver(&nfp_pci_driver);
nfp_net_debugfs_destroy();
}
module_init(nfp_main_init);
module_exit(nfp_main_exit);
MODULE_FIRMWARE("netronome/nic_AMDA0081-0001_1x40.nffw");
MODULE_FIRMWARE("netronome/nic_AMDA0081-0001_4x10.nffw");
MODULE_FIRMWARE("netronome/nic_AMDA0096-0001_2x10.nffw");
MODULE_FIRMWARE("netronome/nic_AMDA0097-0001_2x40.nffw");
MODULE_FIRMWARE("netronome/nic_AMDA0097-0001_4x10_1x40.nffw");
MODULE_FIRMWARE("netronome/nic_AMDA0097-0001_8x10.nffw");
MODULE_FIRMWARE("netronome/nic_AMDA0099-0001_2x10.nffw");
MODULE_FIRMWARE("netronome/nic_AMDA0099-0001_2x25.nffw");
MODULE_FIRMWARE("netronome/nic_AMDA0099-0001_1x10_1x25.nffw");
MODULE_AUTHOR("Netronome Systems <oss-drivers@netronome.com>");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("The Netronome Flow Processor (NFP) driver.");
MODULE_VERSION(UTS_RELEASE);