diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.c b/drivers/net/ethernet/netronome/nfp/nfp_main.c index db52b6a53c6f..dedac720fb29 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.c @@ -47,6 +47,7 @@ #include "nfpcore/nfp.h" #include "nfpcore/nfp_cpp.h" +#include "nfpcore/nfp_nffw.h" #include "nfpcore/nfp_nsp_eth.h" #include "nfpcore/nfp6000_pcie.h" @@ -70,12 +71,34 @@ static const struct pci_device_id nfp_pci_device_ids[] = { }; MODULE_DEVICE_TABLE(pci, nfp_pci_device_ids); +static void nfp_pcie_sriov_read_nfd_limit(struct nfp_pf *pf) +{ +#ifdef CONFIG_PCI_IOV + int err; + + pf->limit_vfs = nfp_rtsym_read_le(pf->cpp, "nfd_vf_cfg_max_vfs", &err); + if (!err) + return; + + pf->limit_vfs = ~0; + /* Allow any setting for backwards compatibility if symbol not found */ + if (err != -ENOENT) + nfp_warn(pf->cpp, "Warning: VF limit read failed: %d\n", err); +#endif +} + 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 sriov: %d\n", err); @@ -228,6 +251,40 @@ exit_release_fw: return err < 0 ? err : 1; } +static int nfp_nsp_init(struct pci_dev *pdev, struct nfp_pf *pf) +{ + struct nfp_nsp *nsp; + int 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; + + pf->eth_tbl = __nfp_eth_read_ports(pf->cpp, nsp); + + err = nfp_fw_load(pdev, pf, nsp); + if (err < 0) { + 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; @@ -251,7 +308,6 @@ static void nfp_fw_unload(struct nfp_pf *pf) static int nfp_pci_probe(struct pci_dev *pdev, const struct pci_device_id *pci_id) { - struct nfp_nsp *nsp; struct nfp_pf *pf; int err; @@ -289,28 +345,18 @@ static int nfp_pci_probe(struct pci_dev *pdev, goto err_disable_msix; } - nsp = nfp_nsp_open(pf->cpp); - if (IS_ERR(nsp)) { - err = PTR_ERR(nsp); + dev_info(&pdev->dev, "Assembly: %s%s%s-%s CPLD: %s\n", + nfp_hwinfo_lookup(pf->cpp, "assembly.vendor"), + nfp_hwinfo_lookup(pf->cpp, "assembly.partno"), + nfp_hwinfo_lookup(pf->cpp, "assembly.serial"), + nfp_hwinfo_lookup(pf->cpp, "assembly.revision"), + nfp_hwinfo_lookup(pf->cpp, "cpld.version")); + + err = nfp_nsp_init(pdev, pf); + if (err) goto err_cpp_free; - } - err = nfp_nsp_wait(nsp); - if (err < 0) { - nfp_nsp_close(nsp); - goto err_cpp_free; - } - - pf->eth_tbl = __nfp_eth_read_ports(pf->cpp, nsp); - - err = nfp_fw_load(pdev, pf, nsp); - nfp_nsp_close(nsp); - if (err < 0) { - dev_err(&pdev->dev, "Failed to load FW\n"); - goto err_eth_tbl_free; - } - - pf->fw_loaded = !!err; + nfp_pcie_sriov_read_nfd_limit(pf); err = nfp_net_pci_probe(pf); if (err) @@ -321,7 +367,6 @@ static int nfp_pci_probe(struct pci_dev *pdev, err_fw_unload: if (pf->fw_loaded) nfp_fw_unload(pf); -err_eth_tbl_free: kfree(pf->eth_tbl); err_cpp_free: nfp_cpp_free(pf->cpp); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.h b/drivers/net/ethernet/netronome/nfp/nfp_main.h index 6c40fa322da3..39105d0435e9 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.h @@ -59,6 +59,7 @@ struct nfp_eth_table; * @tx_area: Pointer to the CPP area for the TX queues * @rx_area: Pointer to the CPP area for the FL/RX queues * @irq_entries: Array of MSI-X entries for all ports + * @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? * @eth_tbl: NSP ETH table @@ -77,6 +78,7 @@ struct nfp_pf { struct msix_entry *irq_entries; + unsigned int limit_vfs; unsigned int num_vfs; bool fw_loaded; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index d37d2391b4fe..e614a376b595 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -111,6 +111,7 @@ SKB_DATA_ALIGN(sizeof(struct skb_shared_info))) /* Forward declarations */ +struct nfp_cpp; struct nfp_net; struct nfp_net_r_vector; @@ -491,7 +492,9 @@ struct nfp_stat_pair { * @tx_bar: Pointer to mapped TX queues * @rx_bar: Pointer to mapped FL/RX queues * @debugfs_dir: Device directory in debugfs + * @ethtool_dump_flag: Ethtool dump flag * @port_list: Entry on device port list + * @cpp: CPP device handle if available */ struct nfp_net { struct pci_dev *pdev; @@ -577,8 +580,11 @@ struct nfp_net { u8 __iomem *rx_bar; struct dentry *debugfs_dir; + u32 ethtool_dump_flag; struct list_head port_list; + + struct nfp_cpp *cpp; }; struct nfp_net_ring_set { diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 255f30252550..2649f7523c81 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -47,9 +47,14 @@ #include #include +#include "nfpcore/nfp.h" #include "nfp_net_ctrl.h" #include "nfp_net.h" +enum nfp_dump_diag { + NFP_DUMP_NSP_DIAG = 0, +}; + /* Support for stats. Returns netdev, driver, and device stats */ enum { NETDEV_ET_STATS, NFP_NET_DRV_ET_STATS, NFP_NET_DEV_ET_STATS }; struct _nfp_net_et_stats { @@ -127,19 +132,39 @@ static const struct _nfp_net_et_stats nfp_net_et_stats[] = { #define NN_ET_STATS_LEN (NN_ET_GLOBAL_STATS_LEN + NN_ET_RVEC_GATHER_STATS + \ NN_ET_RVEC_STATS_LEN + NN_ET_QUEUE_STATS_LEN) +static void nfp_net_get_nspinfo(struct nfp_net *nn, char *version) +{ + struct nfp_nsp *nsp; + + if (!nn->cpp) + return; + + nsp = nfp_nsp_open(nn->cpp); + if (IS_ERR(nsp)) + return; + + snprintf(version, ETHTOOL_FWVERS_LEN, "sp:%hu.%hu", + nfp_nsp_get_abi_ver_major(nsp), + nfp_nsp_get_abi_ver_minor(nsp)); + + nfp_nsp_close(nsp); +} + static void nfp_net_get_drvinfo(struct net_device *netdev, struct ethtool_drvinfo *drvinfo) { + char nsp_version[ETHTOOL_FWVERS_LEN] = {}; struct nfp_net *nn = netdev_priv(netdev); strlcpy(drvinfo->driver, nn->pdev->driver->name, sizeof(drvinfo->driver)); strlcpy(drvinfo->version, nfp_driver_version, sizeof(drvinfo->version)); + nfp_net_get_nspinfo(nn, nsp_version); snprintf(drvinfo->fw_version, sizeof(drvinfo->fw_version), - "%d.%d.%d.%d", + "%d.%d.%d.%d %s", nn->fw_ver.resv, nn->fw_ver.class, - nn->fw_ver.major, nn->fw_ver.minor); + nn->fw_ver.major, nn->fw_ver.minor, nsp_version); strlcpy(drvinfo->bus_info, pci_name(nn->pdev), sizeof(drvinfo->bus_info)); @@ -558,6 +583,75 @@ static int nfp_net_get_coalesce(struct net_device *netdev, return 0; } +/* Other debug dumps + */ +static int +nfp_dump_nsp_diag(struct nfp_net *nn, struct ethtool_dump *dump, void *buffer) +{ + struct nfp_resource *res; + int ret; + + if (!nn->cpp) + return -EOPNOTSUPP; + + dump->version = 1; + dump->flag = NFP_DUMP_NSP_DIAG; + + res = nfp_resource_acquire(nn->cpp, NFP_RESOURCE_NSP_DIAG); + if (IS_ERR(res)) + return PTR_ERR(res); + + if (buffer) { + if (dump->len != nfp_resource_size(res)) { + ret = -EINVAL; + goto exit_release; + } + + ret = nfp_cpp_read(nn->cpp, nfp_resource_cpp_id(res), + nfp_resource_address(res), + buffer, dump->len); + if (ret != dump->len) + ret = ret < 0 ? ret : -EIO; + else + ret = 0; + } else { + dump->len = nfp_resource_size(res); + ret = 0; + } +exit_release: + nfp_resource_release(res); + + return ret; +} + +static int nfp_net_set_dump(struct net_device *netdev, struct ethtool_dump *val) +{ + struct nfp_net *nn = netdev_priv(netdev); + + if (!nn->cpp) + return -EOPNOTSUPP; + + if (val->flag != NFP_DUMP_NSP_DIAG) + return -EINVAL; + + nn->ethtool_dump_flag = val->flag; + + return 0; +} + +static int +nfp_net_get_dump_flag(struct net_device *netdev, struct ethtool_dump *dump) +{ + return nfp_dump_nsp_diag(netdev_priv(netdev), dump, NULL); +} + +static int +nfp_net_get_dump_data(struct net_device *netdev, struct ethtool_dump *dump, + void *buffer) +{ + return nfp_dump_nsp_diag(netdev_priv(netdev), dump, buffer); +} + static int nfp_net_set_coalesce(struct net_device *netdev, struct ethtool_coalesce *ec) { @@ -722,6 +816,9 @@ static const struct ethtool_ops nfp_net_ethtool_ops = { .set_rxfh = nfp_net_set_rxfh, .get_regs_len = nfp_net_get_regs_len, .get_regs = nfp_net_get_regs, + .set_dump = nfp_net_set_dump, + .get_dump_flag = nfp_net_get_dump_flag, + .get_dump_data = nfp_net_get_dump_data, .get_coalesce = nfp_net_get_coalesce, .set_coalesce = nfp_net_set_coalesce, .get_channels = nfp_net_get_channels, diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index eccd31003b0d..3afcdc11480c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -303,6 +303,7 @@ nfp_net_pf_alloc_port_netdev(struct nfp_pf *pf, void __iomem *ctrl_bar, if (IS_ERR(nn)) return nn; + nn->cpp = pf->cpp; nn->fw_ver = *fw_ver; nn->ctrl_bar = ctrl_bar; nn->tx_bar = tx_bar; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h index 3d70a8578a98..42cb720b696d 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h @@ -40,6 +40,7 @@ #define __NFP_H__ #include +#include #include "nfp_cpp.h" @@ -54,6 +55,8 @@ struct firmware; struct nfp_nsp *nfp_nsp_open(struct nfp_cpp *cpp); void nfp_nsp_close(struct nfp_nsp *state); +u16 nfp_nsp_get_abi_ver_major(struct nfp_nsp *state); +u16 nfp_nsp_get_abi_ver_minor(struct nfp_nsp *state); int nfp_nsp_wait(struct nfp_nsp *state); int nfp_nsp_device_soft_reset(struct nfp_nsp *state); int nfp_nsp_load_fw(struct nfp_nsp *state, const struct firmware *fw); @@ -83,6 +86,7 @@ int nfp_nsp_write_eth_table(struct nfp_nsp *state, /* Service Processor */ #define NFP_RESOURCE_NSP "nfp.sp" +#define NFP_RESOURCE_NSP_DIAG "arm.diag" /* Netronone Flow Firmware Table */ #define NFP_RESOURCE_NFP_NFFW "nfp.nffw" diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c index f07f2fc3fba0..34c50987c377 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c @@ -99,6 +99,10 @@ enum nfp_nsp_cmd { struct nfp_nsp { struct nfp_cpp *cpp; struct nfp_resource *res; + struct { + u16 major; + u16 minor; + } ver; }; static int nfp_nsp_check(struct nfp_nsp *state) @@ -120,11 +124,12 @@ static int nfp_nsp_check(struct nfp_nsp *state) return -ENODEV; } - if (FIELD_GET(NSP_STATUS_MAJOR, reg) != NSP_MAJOR || - FIELD_GET(NSP_STATUS_MINOR, reg) < NSP_MINOR) { - nfp_err(cpp, "Unsupported ABI %lld.%lld\n", - FIELD_GET(NSP_STATUS_MAJOR, reg), - FIELD_GET(NSP_STATUS_MINOR, reg)); + state->ver.major = FIELD_GET(NSP_STATUS_MAJOR, reg); + state->ver.minor = FIELD_GET(NSP_STATUS_MINOR, reg); + + if (state->ver.major != NSP_MAJOR || state->ver.minor < NSP_MINOR) { + nfp_err(cpp, "Unsupported ABI %hu.%hu\n", + state->ver.major, state->ver.minor); return -EINVAL; } @@ -177,6 +182,16 @@ void nfp_nsp_close(struct nfp_nsp *state) kfree(state); } +u16 nfp_nsp_get_abi_ver_major(struct nfp_nsp *state) +{ + return state->ver.major; +} + +u16 nfp_nsp_get_abi_ver_minor(struct nfp_nsp *state) +{ + return state->ver.minor; +} + static int nfp_nsp_wait_reg(struct nfp_cpp *cpp, u64 *reg, u32 nsp_cpp, u64 addr, u64 mask, u64 val) @@ -301,15 +316,9 @@ static int nfp_nsp_command_buf(struct nfp_nsp *nsp, u16 code, u32 option, int ret, err; u32 cpp_id; - err = nfp_cpp_readq(cpp, nfp_resource_cpp_id(nsp->res), - nfp_resource_address(nsp->res) + NSP_STATUS, ®); - if (err < 0) - return err; - - if (FIELD_GET(NSP_STATUS_MINOR, reg) < 13) { - nfp_err(cpp, "NSP: Code 0x%04x with buffer not supported (ABI %lld.%lld)\n", - code, FIELD_GET(NSP_STATUS_MAJOR, reg), - FIELD_GET(NSP_STATUS_MINOR, reg)); + if (nsp->ver.minor < 13) { + nfp_err(cpp, "NSP: Code 0x%04x with buffer not supported (ABI %hu.%hu)\n", + code, nsp->ver.major, nsp->ver.minor); return -EOPNOTSUPP; } diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_rtsym.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_rtsym.c index c659b1d999be..0e3870ecfb8c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_rtsym.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_rtsym.c @@ -269,8 +269,10 @@ u64 nfp_rtsym_read_le(struct nfp_cpp *cpp, const char *name, int *error) int err; sym = nfp_rtsym_lookup(cpp, name); - if (!sym) - return -ENOENT; + if (!sym) { + err = -ENOENT; + goto exit; + } id = NFP_CPP_ISLAND_ID(sym->target, NFP_CPP_ACTION_RW, 0, sym->domain); @@ -294,7 +296,7 @@ u64 nfp_rtsym_read_le(struct nfp_cpp *cpp, const char *name, int *error) err = 0; else if (err >= 0) err = -EIO; - +exit: if (error) *error = err;