Commit a4b7c07f authored by David S. Miller's avatar David S. Miller

Merge branch 'nfp-ksettings'

Jakub Kicinski says:

====================
nfp: ethtool link settings

This series adds support for getting and setting link settings
via the (moderately) new ethtool ksettings ops.

First patch introduces minimal speed and duplex reporting using
the information directly provided in PCI BAR0 memory.

Next few changes deal with the need to refresh port state read
from the service process and patch 6 finally uses that information
to provide link speed and duplex.  Patches 7 and 8 add auto
negotiation and port type reporting.

Remaining changes provide the set support for speed and auto
negotiation.  An upcoming series will also add port splitting
support via devlink.

Quite a bit of churn in this series is caused by the fact that
currently port speed and split changes will usually require a
reboot to take effect.  Current service process code is not capable
of performing MAC reinitialization after chip has been passing
traffic.  To make sure user is aware of this limitation we refuse
the configuration unless netdev is down, print warning to the logs
and if configuration was performed but did take effect we unregister
the netdev.  Service process has a "reboot needed" sticky bit, so
reloading the driver will not bring the netdev back.

Note that there is a helper in patch 13 which is marked as
__always_inline, because the FIELD_* macros require the parameters
to be known at compilation time.  I hope that is OK.
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 18148f09 7c698737
...@@ -48,7 +48,7 @@ ...@@ -48,7 +48,7 @@
#include "nfpcore/nfp.h" #include "nfpcore/nfp.h"
#include "nfpcore/nfp_cpp.h" #include "nfpcore/nfp_cpp.h"
#include "nfpcore/nfp_nffw.h" #include "nfpcore/nfp_nffw.h"
#include "nfpcore/nfp_nsp_eth.h" #include "nfpcore/nfp_nsp.h"
#include "nfpcore/nfp6000_pcie.h" #include "nfpcore/nfp6000_pcie.h"
...@@ -385,8 +385,7 @@ static void nfp_pci_remove(struct pci_dev *pdev) ...@@ -385,8 +385,7 @@ static void nfp_pci_remove(struct pci_dev *pdev)
{ {
struct nfp_pf *pf = pci_get_drvdata(pdev); struct nfp_pf *pf = pci_get_drvdata(pdev);
if (!list_empty(&pf->ports)) nfp_net_pci_remove(pf);
nfp_net_pci_remove(pf);
nfp_pcie_sriov_disable(pdev); nfp_pcie_sriov_disable(pdev);
......
...@@ -42,7 +42,9 @@ ...@@ -42,7 +42,9 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/msi.h> #include <linux/msi.h>
#include <linux/mutex.h>
#include <linux/pci.h> #include <linux/pci.h>
#include <linux/workqueue.h>
struct dentry; struct dentry;
struct pci_dev; struct pci_dev;
...@@ -64,8 +66,11 @@ struct nfp_eth_table; ...@@ -64,8 +66,11 @@ struct nfp_eth_table;
* @fw_loaded: Is the firmware loaded? * @fw_loaded: Is the firmware loaded?
* @eth_tbl: NSP ETH table * @eth_tbl: NSP ETH table
* @ddir: Per-device debugfs directory * @ddir: Per-device debugfs directory
* @num_ports: Number of adapter ports * @num_ports: Number of adapter ports app firmware supports
* @num_netdevs: Number of netdevs spawned
* @ports: Linked list of port structures (struct nfp_net) * @ports: Linked list of port structures (struct nfp_net)
* @port_lock: Protects @ports, @num_ports, @num_netdevs
* @port_refresh_work: Work entry for taking netdevs out
*/ */
struct nfp_pf { struct nfp_pf {
struct pci_dev *pdev; struct pci_dev *pdev;
...@@ -88,7 +93,11 @@ struct nfp_pf { ...@@ -88,7 +93,11 @@ struct nfp_pf {
struct dentry *ddir; struct dentry *ddir;
unsigned int num_ports; unsigned int num_ports;
unsigned int num_netdevs;
struct list_head ports; struct list_head ports;
struct work_struct port_refresh_work;
struct mutex port_lock;
}; };
extern struct pci_driver nfp_netvf_pci_driver; extern struct pci_driver nfp_netvf_pci_driver;
......
...@@ -523,7 +523,8 @@ struct nfp_net_dp { ...@@ -523,7 +523,8 @@ struct nfp_net_dp {
* @reconfig_sync_present: Some thread is performing synchronous reconfig * @reconfig_sync_present: Some thread is performing synchronous reconfig
* @reconfig_timer: Timer for async reading of reconfig results * @reconfig_timer: Timer for async reading of reconfig results
* @link_up: Is the link up? * @link_up: Is the link up?
* @link_status_lock: Protects @link_up and ensures atomicity with BAR reading * @link_changed: Has link state changes since last port refresh?
* @link_status_lock: Protects @link_* and ensures atomicity with BAR reading
* @rx_coalesce_usecs: RX interrupt moderation usecs delay parameter * @rx_coalesce_usecs: RX interrupt moderation usecs delay parameter
* @rx_coalesce_max_frames: RX interrupt moderation frame count parameter * @rx_coalesce_max_frames: RX interrupt moderation frame count parameter
* @tx_coalesce_usecs: TX interrupt moderation usecs delay parameter * @tx_coalesce_usecs: TX interrupt moderation usecs delay parameter
...@@ -580,6 +581,7 @@ struct nfp_net { ...@@ -580,6 +581,7 @@ struct nfp_net {
u32 me_freq_mhz; u32 me_freq_mhz;
bool link_up; bool link_up;
bool link_changed;
spinlock_t link_status_lock; spinlock_t link_status_lock;
spinlock_t reconfig_lock; spinlock_t reconfig_lock;
...@@ -810,6 +812,9 @@ nfp_net_irqs_assign(struct nfp_net *nn, struct msix_entry *irq_entries, ...@@ -810,6 +812,9 @@ nfp_net_irqs_assign(struct nfp_net *nn, struct msix_entry *irq_entries,
struct nfp_net_dp *nfp_net_clone_dp(struct nfp_net *nn); struct nfp_net_dp *nfp_net_clone_dp(struct nfp_net *nn);
int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *new); int nfp_net_ring_reconfig(struct nfp_net *nn, struct nfp_net_dp *new);
bool nfp_net_link_changed_read_clear(struct nfp_net *nn);
void nfp_net_refresh_port_config(struct nfp_net *nn);
#ifdef CONFIG_NFP_DEBUG #ifdef CONFIG_NFP_DEBUG
void nfp_net_debugfs_create(void); void nfp_net_debugfs_create(void);
void nfp_net_debugfs_destroy(void); void nfp_net_debugfs_destroy(void);
......
...@@ -67,7 +67,7 @@ ...@@ -67,7 +67,7 @@
#include <net/pkt_cls.h> #include <net/pkt_cls.h>
#include <net/vxlan.h> #include <net/vxlan.h>
#include "nfpcore/nfp_nsp_eth.h" #include "nfpcore/nfp_nsp.h"
#include "nfp_net_ctrl.h" #include "nfp_net_ctrl.h"
#include "nfp_net.h" #include "nfp_net.h"
...@@ -376,6 +376,19 @@ static irqreturn_t nfp_net_irq_rxtx(int irq, void *data) ...@@ -376,6 +376,19 @@ static irqreturn_t nfp_net_irq_rxtx(int irq, void *data)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
bool nfp_net_link_changed_read_clear(struct nfp_net *nn)
{
unsigned long flags;
bool ret;
spin_lock_irqsave(&nn->link_status_lock, flags);
ret = nn->link_changed;
nn->link_changed = false;
spin_unlock_irqrestore(&nn->link_status_lock, flags);
return ret;
}
/** /**
* nfp_net_read_link_status() - Reread link status from control BAR * nfp_net_read_link_status() - Reread link status from control BAR
* @nn: NFP Network structure * @nn: NFP Network structure
...@@ -395,6 +408,7 @@ static void nfp_net_read_link_status(struct nfp_net *nn) ...@@ -395,6 +408,7 @@ static void nfp_net_read_link_status(struct nfp_net *nn)
goto out; goto out;
nn->link_up = link_up; nn->link_up = link_up;
nn->link_changed = true;
if (nn->link_up) { if (nn->link_up) {
netif_carrier_on(nn->dp.netdev); netif_carrier_on(nn->dp.netdev);
......
...@@ -177,6 +177,19 @@ ...@@ -177,6 +177,19 @@
#define NFP_NET_CFG_VERSION_MINOR(x) (((x) & 0xff) << 0) #define NFP_NET_CFG_VERSION_MINOR(x) (((x) & 0xff) << 0)
#define NFP_NET_CFG_STS 0x0034 #define NFP_NET_CFG_STS 0x0034
#define NFP_NET_CFG_STS_LINK (0x1 << 0) /* Link up or down */ #define NFP_NET_CFG_STS_LINK (0x1 << 0) /* Link up or down */
/* Link rate */
#define NFP_NET_CFG_STS_LINK_RATE_SHIFT 1
#define NFP_NET_CFG_STS_LINK_RATE_MASK 0xF
#define NFP_NET_CFG_STS_LINK_RATE \
(NFP_NET_CFG_STS_LINK_RATE_MASK << NFP_NET_CFG_STS_LINK_RATE_SHIFT)
#define NFP_NET_CFG_STS_LINK_RATE_UNSUPPORTED 0
#define NFP_NET_CFG_STS_LINK_RATE_UNKNOWN 1
#define NFP_NET_CFG_STS_LINK_RATE_1G 2
#define NFP_NET_CFG_STS_LINK_RATE_10G 3
#define NFP_NET_CFG_STS_LINK_RATE_25G 4
#define NFP_NET_CFG_STS_LINK_RATE_40G 5
#define NFP_NET_CFG_STS_LINK_RATE_50G 6
#define NFP_NET_CFG_STS_LINK_RATE_100G 7
#define NFP_NET_CFG_CAP 0x0038 #define NFP_NET_CFG_CAP 0x0038
#define NFP_NET_CFG_MAX_TXRINGS 0x003c #define NFP_NET_CFG_MAX_TXRINGS 0x003c
#define NFP_NET_CFG_MAX_RXRINGS 0x0040 #define NFP_NET_CFG_MAX_RXRINGS 0x0040
......
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
#include <linux/ethtool.h> #include <linux/ethtool.h>
#include "nfpcore/nfp.h" #include "nfpcore/nfp.h"
#include "nfpcore/nfp_nsp.h"
#include "nfp_net_ctrl.h" #include "nfp_net_ctrl.h"
#include "nfp_net.h" #include "nfp_net.h"
...@@ -173,6 +174,114 @@ static void nfp_net_get_drvinfo(struct net_device *netdev, ...@@ -173,6 +174,114 @@ static void nfp_net_get_drvinfo(struct net_device *netdev,
drvinfo->regdump_len = NFP_NET_CFG_BAR_SZ; drvinfo->regdump_len = NFP_NET_CFG_BAR_SZ;
} }
/**
* nfp_net_get_link_ksettings - Get Link Speed settings
* @netdev: network interface device structure
* @cmd: ethtool command
*
* Reports speed settings based on info in the BAR provided by the fw.
*/
static int
nfp_net_get_link_ksettings(struct net_device *netdev,
struct ethtool_link_ksettings *cmd)
{
static const u32 ls_to_ethtool[] = {
[NFP_NET_CFG_STS_LINK_RATE_UNSUPPORTED] = 0,
[NFP_NET_CFG_STS_LINK_RATE_UNKNOWN] = SPEED_UNKNOWN,
[NFP_NET_CFG_STS_LINK_RATE_1G] = SPEED_1000,
[NFP_NET_CFG_STS_LINK_RATE_10G] = SPEED_10000,
[NFP_NET_CFG_STS_LINK_RATE_25G] = SPEED_25000,
[NFP_NET_CFG_STS_LINK_RATE_40G] = SPEED_40000,
[NFP_NET_CFG_STS_LINK_RATE_50G] = SPEED_50000,
[NFP_NET_CFG_STS_LINK_RATE_100G] = SPEED_100000,
};
struct nfp_net *nn = netdev_priv(netdev);
u32 sts, ls;
ethtool_link_ksettings_add_link_mode(cmd, supported, FIBRE);
cmd->base.port = PORT_OTHER;
cmd->base.speed = SPEED_UNKNOWN;
cmd->base.duplex = DUPLEX_UNKNOWN;
if (nn->eth_port)
cmd->base.autoneg = nn->eth_port->aneg != NFP_ANEG_DISABLED ?
AUTONEG_ENABLE : AUTONEG_DISABLE;
if (!netif_carrier_ok(netdev))
return 0;
/* Use link speed from ETH table if available, otherwise try the BAR */
if (nn->eth_port && nfp_net_link_changed_read_clear(nn))
nfp_net_refresh_port_config(nn);
/* Separate if - on FW error the port could've disappeared from table */
if (nn->eth_port) {
cmd->base.port = nn->eth_port->port_type;
cmd->base.speed = nn->eth_port->speed;
cmd->base.duplex = DUPLEX_FULL;
return 0;
}
sts = nn_readl(nn, NFP_NET_CFG_STS);
ls = FIELD_GET(NFP_NET_CFG_STS_LINK_RATE, sts);
if (ls == NFP_NET_CFG_STS_LINK_RATE_UNSUPPORTED)
return -EOPNOTSUPP;
if (ls == NFP_NET_CFG_STS_LINK_RATE_UNKNOWN ||
ls >= ARRAY_SIZE(ls_to_ethtool))
return 0;
cmd->base.speed = ls_to_ethtool[sts];
cmd->base.duplex = DUPLEX_FULL;
return 0;
}
static int
nfp_net_set_link_ksettings(struct net_device *netdev,
const struct ethtool_link_ksettings *cmd)
{
struct nfp_net *nn = netdev_priv(netdev);
struct nfp_nsp *nsp;
int err;
if (!nn->eth_port)
return -EOPNOTSUPP;
if (netif_running(netdev)) {
nn_warn(nn, "Changing settings not allowed on an active interface. It may cause the port to be disabled until reboot.\n");
return -EBUSY;
}
nsp = nfp_eth_config_start(nn->cpp, nn->eth_port->index);
if (IS_ERR(nsp))
return PTR_ERR(nsp);
err = __nfp_eth_set_aneg(nsp, cmd->base.autoneg == AUTONEG_ENABLE ?
NFP_ANEG_AUTO : NFP_ANEG_DISABLED);
if (err)
goto err_bad_set;
if (cmd->base.speed != SPEED_UNKNOWN) {
u32 speed = cmd->base.speed / nn->eth_port->lanes;
err = __nfp_eth_set_speed(nsp, speed);
if (err)
goto err_bad_set;
}
err = nfp_eth_config_commit_end(nsp);
if (err > 0)
return 0; /* no change */
nfp_net_refresh_port_config(nn);
return err;
err_bad_set:
nfp_eth_config_cleanup_end(nsp);
return err;
}
static void nfp_net_get_ringparam(struct net_device *netdev, static void nfp_net_get_ringparam(struct net_device *netdev,
struct ethtool_ringparam *ring) struct ethtool_ringparam *ring)
{ {
...@@ -814,6 +923,8 @@ static const struct ethtool_ops nfp_net_ethtool_ops = { ...@@ -814,6 +923,8 @@ static const struct ethtool_ops nfp_net_ethtool_ops = {
.set_coalesce = nfp_net_set_coalesce, .set_coalesce = nfp_net_set_coalesce,
.get_channels = nfp_net_get_channels, .get_channels = nfp_net_get_channels,
.set_channels = nfp_net_set_channels, .set_channels = nfp_net_set_channels,
.get_link_ksettings = nfp_net_get_link_ksettings,
.set_link_ksettings = nfp_net_set_link_ksettings,
}; };
void nfp_net_set_ethtool_ops(struct net_device *netdev) void nfp_net_set_ethtool_ops(struct net_device *netdev)
......
...@@ -47,11 +47,12 @@ ...@@ -47,11 +47,12 @@
#include <linux/pci_regs.h> #include <linux/pci_regs.h>
#include <linux/msi.h> #include <linux/msi.h>
#include <linux/random.h> #include <linux/random.h>
#include <linux/rtnetlink.h>
#include "nfpcore/nfp.h" #include "nfpcore/nfp.h"
#include "nfpcore/nfp_cpp.h" #include "nfpcore/nfp_cpp.h"
#include "nfpcore/nfp_nffw.h" #include "nfpcore/nfp_nffw.h"
#include "nfpcore/nfp_nsp_eth.h" #include "nfpcore/nfp_nsp.h"
#include "nfpcore/nfp6000_pcie.h" #include "nfpcore/nfp6000_pcie.h"
#include "nfp_net_ctrl.h" #include "nfp_net_ctrl.h"
...@@ -129,14 +130,29 @@ static u8 __iomem *nfp_net_map_area(struct nfp_cpp *cpp, ...@@ -129,14 +130,29 @@ static u8 __iomem *nfp_net_map_area(struct nfp_cpp *cpp,
return (u8 __iomem *)ERR_PTR(err); return (u8 __iomem *)ERR_PTR(err);
} }
/**
* nfp_net_get_mac_addr() - Get the MAC address.
* @nn: NFP Network structure
* @cpp: NFP CPP handle
* @id: NFP port id
*
* First try to get the MAC address from NSP ETH table. If that
* fails try HWInfo. As a last resort generate a random address.
*/
static void static void
nfp_net_get_mac_addr_hwinfo(struct nfp_net_dp *dp, struct nfp_cpp *cpp, nfp_net_get_mac_addr(struct nfp_net *nn, struct nfp_cpp *cpp, unsigned int id)
unsigned int id)
{ {
struct nfp_net_dp *dp = &nn->dp;
u8 mac_addr[ETH_ALEN]; u8 mac_addr[ETH_ALEN];
const char *mac_str; const char *mac_str;
char name[32]; char name[32];
if (nn->eth_port) {
ether_addr_copy(dp->netdev->dev_addr, nn->eth_port->mac_addr);
ether_addr_copy(dp->netdev->perm_addr, nn->eth_port->mac_addr);
return;
}
snprintf(name, sizeof(name), "eth%d.mac", id); snprintf(name, sizeof(name), "eth%d.mac", id);
mac_str = nfp_hwinfo_lookup(cpp, name); mac_str = nfp_hwinfo_lookup(cpp, name);
...@@ -159,32 +175,16 @@ nfp_net_get_mac_addr_hwinfo(struct nfp_net_dp *dp, struct nfp_cpp *cpp, ...@@ -159,32 +175,16 @@ nfp_net_get_mac_addr_hwinfo(struct nfp_net_dp *dp, struct nfp_cpp *cpp,
ether_addr_copy(dp->netdev->perm_addr, mac_addr); ether_addr_copy(dp->netdev->perm_addr, mac_addr);
} }
/** static struct nfp_eth_table_port *
* nfp_net_get_mac_addr() - Get the MAC address. nfp_net_find_port(struct nfp_pf *pf, unsigned int id)
* @nn: NFP Network structure
* @pf: NFP PF device structure
* @id: NFP port id
*
* First try to get the MAC address from NSP ETH table. If that
* fails try HWInfo. As a last resort generate a random address.
*/
static void
nfp_net_get_mac_addr(struct nfp_net *nn, struct nfp_pf *pf, unsigned int id)
{ {
int i; int i;
for (i = 0; pf->eth_tbl && i < pf->eth_tbl->count; i++) for (i = 0; pf->eth_tbl && i < pf->eth_tbl->count; i++)
if (pf->eth_tbl->ports[i].eth_index == id) { if (pf->eth_tbl->ports[i].eth_index == id)
const u8 *mac_addr = pf->eth_tbl->ports[i].mac_addr; return &pf->eth_tbl->ports[i];
nn->eth_port = &pf->eth_tbl->ports[i];
ether_addr_copy(nn->dp.netdev->dev_addr, mac_addr); return NULL;
ether_addr_copy(nn->dp.netdev->perm_addr, mac_addr);
return;
}
nfp_net_get_mac_addr_hwinfo(&nn->dp, pf->cpp, id);
} }
static unsigned int nfp_net_pf_get_num_ports(struct nfp_pf *pf) static unsigned int nfp_net_pf_get_num_ports(struct nfp_pf *pf)
...@@ -283,6 +283,7 @@ static void nfp_net_pf_free_netdevs(struct nfp_pf *pf) ...@@ -283,6 +283,7 @@ static void nfp_net_pf_free_netdevs(struct nfp_pf *pf)
while (!list_empty(&pf->ports)) { while (!list_empty(&pf->ports)) {
nn = list_first_entry(&pf->ports, struct nfp_net, port_list); nn = list_first_entry(&pf->ports, struct nfp_net, port_list);
list_del(&nn->port_list); list_del(&nn->port_list);
pf->num_netdevs--;
nfp_net_netdev_free(nn); nfp_net_netdev_free(nn);
} }
...@@ -291,7 +292,8 @@ static void nfp_net_pf_free_netdevs(struct nfp_pf *pf) ...@@ -291,7 +292,8 @@ static void nfp_net_pf_free_netdevs(struct nfp_pf *pf)
static struct nfp_net * static struct nfp_net *
nfp_net_pf_alloc_port_netdev(struct nfp_pf *pf, void __iomem *ctrl_bar, nfp_net_pf_alloc_port_netdev(struct nfp_pf *pf, void __iomem *ctrl_bar,
void __iomem *tx_bar, void __iomem *rx_bar, void __iomem *tx_bar, void __iomem *rx_bar,
int stride, struct nfp_net_fw_version *fw_ver) int stride, struct nfp_net_fw_version *fw_ver,
struct nfp_eth_table_port *eth_port)
{ {
u32 n_tx_rings, n_rx_rings; u32 n_tx_rings, n_rx_rings;
struct nfp_net *nn; struct nfp_net *nn;
...@@ -312,6 +314,7 @@ nfp_net_pf_alloc_port_netdev(struct nfp_pf *pf, void __iomem *ctrl_bar, ...@@ -312,6 +314,7 @@ nfp_net_pf_alloc_port_netdev(struct nfp_pf *pf, void __iomem *ctrl_bar,
nn->dp.is_vf = 0; nn->dp.is_vf = 0;
nn->stride_rx = stride; nn->stride_rx = stride;
nn->stride_tx = stride; nn->stride_tx = stride;
nn->eth_port = eth_port;
return nn; return nn;
} }
...@@ -323,7 +326,7 @@ nfp_net_pf_init_port_netdev(struct nfp_pf *pf, struct nfp_net *nn, ...@@ -323,7 +326,7 @@ nfp_net_pf_init_port_netdev(struct nfp_pf *pf, struct nfp_net *nn,
int err; int err;
/* Get MAC address */ /* Get MAC address */
nfp_net_get_mac_addr(nn, pf, id); nfp_net_get_mac_addr(nn, pf->cpp, id);
/* Get ME clock frequency from ctrl BAR /* Get ME clock frequency from ctrl BAR
* XXX for now frequency is hardcoded until we figure out how * XXX for now frequency is hardcoded until we figure out how
...@@ -348,6 +351,7 @@ nfp_net_pf_alloc_netdevs(struct nfp_pf *pf, void __iomem *ctrl_bar, ...@@ -348,6 +351,7 @@ nfp_net_pf_alloc_netdevs(struct nfp_pf *pf, void __iomem *ctrl_bar,
int stride, struct nfp_net_fw_version *fw_ver) int stride, struct nfp_net_fw_version *fw_ver)
{ {
u32 prev_tx_base, prev_rx_base, tgt_tx_base, tgt_rx_base; u32 prev_tx_base, prev_rx_base, tgt_tx_base, tgt_rx_base;
struct nfp_eth_table_port *eth_port;
struct nfp_net *nn; struct nfp_net *nn;
unsigned int i; unsigned int i;
int err; int err;
...@@ -363,17 +367,27 @@ nfp_net_pf_alloc_netdevs(struct nfp_pf *pf, void __iomem *ctrl_bar, ...@@ -363,17 +367,27 @@ nfp_net_pf_alloc_netdevs(struct nfp_pf *pf, void __iomem *ctrl_bar,
prev_tx_base = tgt_tx_base; prev_tx_base = tgt_tx_base;
prev_rx_base = tgt_rx_base; prev_rx_base = tgt_rx_base;
nn = nfp_net_pf_alloc_port_netdev(pf, ctrl_bar, tx_bar, rx_bar, eth_port = nfp_net_find_port(pf, i);
stride, fw_ver); if (eth_port && eth_port->override_changed) {
if (IS_ERR(nn)) { nfp_warn(pf->cpp, "Config changed for port #%d, reboot required before port will be operational\n", i);
err = PTR_ERR(nn); } else {
goto err_free_prev; nn = nfp_net_pf_alloc_port_netdev(pf, ctrl_bar, tx_bar,
rx_bar, stride,
fw_ver, eth_port);
if (IS_ERR(nn)) {
err = PTR_ERR(nn);
goto err_free_prev;
}
list_add_tail(&nn->port_list, &pf->ports);
pf->num_netdevs++;
} }
list_add_tail(&nn->port_list, &pf->ports);
ctrl_bar += NFP_PF_CSR_SLICE_SIZE; ctrl_bar += NFP_PF_CSR_SLICE_SIZE;
} }
if (list_empty(&pf->ports))
return -ENODEV;
return 0; return 0;
err_free_prev: err_free_prev:
...@@ -409,7 +423,7 @@ nfp_net_pf_spawn_netdevs(struct nfp_pf *pf, ...@@ -409,7 +423,7 @@ nfp_net_pf_spawn_netdevs(struct nfp_pf *pf,
} }
num_irqs = nfp_net_irqs_alloc(pf->pdev, pf->irq_entries, num_irqs = nfp_net_irqs_alloc(pf->pdev, pf->irq_entries,
NFP_NET_MIN_PORT_IRQS * pf->num_ports, NFP_NET_MIN_PORT_IRQS * pf->num_netdevs,
wanted_irqs); wanted_irqs);
if (!num_irqs) { if (!num_irqs) {
nn_warn(nn, "Unable to allocate MSI-X Vectors. Exiting\n"); nn_warn(nn, "Unable to allocate MSI-X Vectors. Exiting\n");
...@@ -419,7 +433,7 @@ nfp_net_pf_spawn_netdevs(struct nfp_pf *pf, ...@@ -419,7 +433,7 @@ nfp_net_pf_spawn_netdevs(struct nfp_pf *pf,
/* Distribute IRQs to ports */ /* Distribute IRQs to ports */
irqs_left = num_irqs; irqs_left = num_irqs;
ports_left = pf->num_ports; ports_left = pf->num_netdevs;
list_for_each_entry(nn, &pf->ports, port_list) { list_for_each_entry(nn, &pf->ports, port_list) {
unsigned int n; unsigned int n;
...@@ -455,6 +469,82 @@ nfp_net_pf_spawn_netdevs(struct nfp_pf *pf, ...@@ -455,6 +469,82 @@ nfp_net_pf_spawn_netdevs(struct nfp_pf *pf,
return err; return err;
} }
static void nfp_net_pci_remove_finish(struct nfp_pf *pf)
{
nfp_net_debugfs_dir_clean(&pf->ddir);
nfp_net_irqs_disable(pf->pdev);
kfree(pf->irq_entries);
nfp_cpp_area_release_free(pf->rx_area);
nfp_cpp_area_release_free(pf->tx_area);
nfp_cpp_area_release_free(pf->ctrl_area);
}
static void nfp_net_refresh_netdevs(struct work_struct *work)
{
struct nfp_pf *pf = container_of(work, struct nfp_pf,
port_refresh_work);
struct nfp_net *nn, *next;
mutex_lock(&pf->port_lock);
/* Check for nfp_net_pci_remove() racing against us */
if (list_empty(&pf->ports))
goto out;
list_for_each_entry_safe(nn, next, &pf->ports, port_list) {
if (!nn->eth_port) {
nfp_warn(pf->cpp, "Warning: port %d not present after reconfig\n",
nn->eth_port->eth_index);
continue;
}
if (!nn->eth_port->override_changed)
continue;
nn_warn(nn, "Port config changed, unregistering. Reboot required before port will be operational again.\n");
nfp_net_debugfs_dir_clean(&nn->debugfs_dir);
nfp_net_netdev_clean(nn->dp.netdev);
list_del(&nn->port_list);
pf->num_netdevs--;
nfp_net_netdev_free(nn);
}
if (list_empty(&pf->ports))
nfp_net_pci_remove_finish(pf);
out:
mutex_unlock(&pf->port_lock);
}
void nfp_net_refresh_port_config(struct nfp_net *nn)
{
struct nfp_pf *pf = pci_get_drvdata(nn->pdev);
struct nfp_eth_table *old_table;
ASSERT_RTNL();
old_table = pf->eth_tbl;
list_for_each_entry(nn, &pf->ports, port_list)
nfp_net_link_changed_read_clear(nn);
pf->eth_tbl = nfp_eth_read_ports(pf->cpp);
if (!pf->eth_tbl) {
pf->eth_tbl = old_table;
nfp_err(pf->cpp, "Error refreshing port config!\n");
return;
}
list_for_each_entry(nn, &pf->ports, port_list)
nn->eth_port = nfp_net_find_port(pf, nn->eth_port->eth_index);
kfree(old_table);
schedule_work(&pf->port_refresh_work);
}
/* /*
* PCI device functions * PCI device functions
*/ */
...@@ -468,17 +558,23 @@ int nfp_net_pci_probe(struct nfp_pf *pf) ...@@ -468,17 +558,23 @@ int nfp_net_pci_probe(struct nfp_pf *pf)
int stride; int stride;
int err; int err;
INIT_WORK(&pf->port_refresh_work, nfp_net_refresh_netdevs);
mutex_init(&pf->port_lock);
/* Verify that the board has completed initialization */ /* Verify that the board has completed initialization */
if (!nfp_is_ready(pf->cpp)) { if (!nfp_is_ready(pf->cpp)) {
nfp_err(pf->cpp, "NFP is not ready for NIC operation.\n"); nfp_err(pf->cpp, "NFP is not ready for NIC operation.\n");
return -EINVAL; return -EINVAL;
} }
mutex_lock(&pf->port_lock);
pf->num_ports = nfp_net_pf_get_num_ports(pf); pf->num_ports = nfp_net_pf_get_num_ports(pf);
ctrl_bar = nfp_net_pf_map_ctrl_bar(pf); ctrl_bar = nfp_net_pf_map_ctrl_bar(pf);
if (!ctrl_bar) if (!ctrl_bar) {
return pf->fw_loaded ? -EINVAL : -EPROBE_DEFER; err = pf->fw_loaded ? -EINVAL : -EPROBE_DEFER;
goto err_unlock;
}
nfp_net_get_fw_version(&fw_ver, ctrl_bar); nfp_net_get_fw_version(&fw_ver, ctrl_bar);
if (fw_ver.resv || fw_ver.class != NFP_NET_CFG_VERSION_CLASS_GENERIC) { if (fw_ver.resv || fw_ver.class != NFP_NET_CFG_VERSION_CLASS_GENERIC) {
...@@ -552,6 +648,8 @@ int nfp_net_pci_probe(struct nfp_pf *pf) ...@@ -552,6 +648,8 @@ int nfp_net_pci_probe(struct nfp_pf *pf)
if (err) if (err)
goto err_clean_ddir; goto err_clean_ddir;
mutex_unlock(&pf->port_lock);
return 0; return 0;
err_clean_ddir: err_clean_ddir:
...@@ -561,6 +659,8 @@ int nfp_net_pci_probe(struct nfp_pf *pf) ...@@ -561,6 +659,8 @@ int nfp_net_pci_probe(struct nfp_pf *pf)
nfp_cpp_area_release_free(pf->tx_area); nfp_cpp_area_release_free(pf->tx_area);
err_ctrl_unmap: err_ctrl_unmap:
nfp_cpp_area_release_free(pf->ctrl_area); nfp_cpp_area_release_free(pf->ctrl_area);
err_unlock:
mutex_unlock(&pf->port_lock);
return err; return err;
} }
...@@ -568,6 +668,10 @@ void nfp_net_pci_remove(struct nfp_pf *pf) ...@@ -568,6 +668,10 @@ void nfp_net_pci_remove(struct nfp_pf *pf)
{ {
struct nfp_net *nn; struct nfp_net *nn;
mutex_lock(&pf->port_lock);
if (list_empty(&pf->ports))
goto out;
list_for_each_entry(nn, &pf->ports, port_list) { list_for_each_entry(nn, &pf->ports, port_list) {
nfp_net_debugfs_dir_clean(&nn->debugfs_dir); nfp_net_debugfs_dir_clean(&nn->debugfs_dir);
...@@ -576,12 +680,9 @@ void nfp_net_pci_remove(struct nfp_pf *pf) ...@@ -576,12 +680,9 @@ void nfp_net_pci_remove(struct nfp_pf *pf)
nfp_net_pf_free_netdevs(pf); nfp_net_pf_free_netdevs(pf);
nfp_net_debugfs_dir_clean(&pf->ddir); nfp_net_pci_remove_finish(pf);
out:
mutex_unlock(&pf->port_lock);
nfp_net_irqs_disable(pf->pdev); cancel_work_sync(&pf->port_refresh_work);
kfree(pf->irq_entries);
nfp_cpp_area_release_free(pf->rx_area);
nfp_cpp_area_release_free(pf->tx_area);
nfp_cpp_area_release_free(pf->ctrl_area);
} }
...@@ -48,18 +48,18 @@ ...@@ -48,18 +48,18 @@
const char *nfp_hwinfo_lookup(struct nfp_cpp *cpp, const char *lookup); const char *nfp_hwinfo_lookup(struct nfp_cpp *cpp, const char *lookup);
/* Implemented in nfp_nsp.c */ /* Implemented in nfp_nsp.c, low level functions */
struct nfp_nsp; struct nfp_nsp;
struct firmware;
struct nfp_cpp *nfp_nsp_cpp(struct nfp_nsp *state);
struct nfp_nsp *nfp_nsp_open(struct nfp_cpp *cpp); bool nfp_nsp_config_modified(struct nfp_nsp *state);
void nfp_nsp_close(struct nfp_nsp *state); void nfp_nsp_config_set_modified(struct nfp_nsp *state, bool modified);
u16 nfp_nsp_get_abi_ver_major(struct nfp_nsp *state); void *nfp_nsp_config_entries(struct nfp_nsp *state);
u16 nfp_nsp_get_abi_ver_minor(struct nfp_nsp *state); unsigned int nfp_nsp_config_idx(struct nfp_nsp *state);
int nfp_nsp_wait(struct nfp_nsp *state); void nfp_nsp_config_set_state(struct nfp_nsp *state, void *entries,
int nfp_nsp_device_soft_reset(struct nfp_nsp *state); unsigned int idx);
int nfp_nsp_load_fw(struct nfp_nsp *state, const struct firmware *fw); void nfp_nsp_config_clear_state(struct nfp_nsp *state);
int nfp_nsp_read_eth_table(struct nfp_nsp *state, void *buf, unsigned int size); int nfp_nsp_read_eth_table(struct nfp_nsp *state, void *buf, unsigned int size);
int nfp_nsp_write_eth_table(struct nfp_nsp *state, int nfp_nsp_write_eth_table(struct nfp_nsp *state,
const void *buf, unsigned int size); const void *buf, unsigned int size);
......
...@@ -49,6 +49,7 @@ ...@@ -49,6 +49,7 @@
#include "nfp.h" #include "nfp.h"
#include "nfp_cpp.h" #include "nfp_cpp.h"
#include "nfp_nsp.h"
/* Offsets relative to the CSR base */ /* Offsets relative to the CSR base */
#define NSP_STATUS 0x00 #define NSP_STATUS 0x00
...@@ -96,6 +97,17 @@ enum nfp_nsp_cmd { ...@@ -96,6 +97,17 @@ enum nfp_nsp_cmd {
__MAX_SPCODE, __MAX_SPCODE,
}; };
static const struct {
int code;
const char *msg;
} nsp_errors[] = {
{ 6010, "could not map to phy for port" },
{ 6011, "not an allowed rate/lanes for port" },
{ 6012, "not an allowed rate/lanes for port" },
{ 6013, "high/low error, change other port first" },
{ 6014, "config not found in flash" },
};
struct nfp_nsp { struct nfp_nsp {
struct nfp_cpp *cpp; struct nfp_cpp *cpp;
struct nfp_resource *res; struct nfp_resource *res;
...@@ -103,8 +115,63 @@ struct nfp_nsp { ...@@ -103,8 +115,63 @@ struct nfp_nsp {
u16 major; u16 major;
u16 minor; u16 minor;
} ver; } ver;
/* Eth table config state */
bool modified;
unsigned int idx;
void *entries;
}; };
struct nfp_cpp *nfp_nsp_cpp(struct nfp_nsp *state)
{
return state->cpp;
}
bool nfp_nsp_config_modified(struct nfp_nsp *state)
{
return state->modified;
}
void nfp_nsp_config_set_modified(struct nfp_nsp *state, bool modified)
{
state->modified = modified;
}
void *nfp_nsp_config_entries(struct nfp_nsp *state)
{
return state->entries;
}
unsigned int nfp_nsp_config_idx(struct nfp_nsp *state)
{
return state->idx;
}
void
nfp_nsp_config_set_state(struct nfp_nsp *state, void *entries, unsigned int idx)
{
state->entries = entries;
state->idx = idx;
}
void nfp_nsp_config_clear_state(struct nfp_nsp *state)
{
state->entries = NULL;
state->idx = 0;
}
static void nfp_nsp_print_extended_error(struct nfp_nsp *state, u32 ret_val)
{
int i;
if (!ret_val)
return;
for (i = 0; i < ARRAY_SIZE(nsp_errors); i++)
if (ret_val == nsp_errors[i].code)
nfp_err(state->cpp, "err msg: %s\n", nsp_errors[i].msg);
}
static int nfp_nsp_check(struct nfp_nsp *state) static int nfp_nsp_check(struct nfp_nsp *state)
{ {
struct nfp_cpp *cpp = state->cpp; struct nfp_cpp *cpp = state->cpp;
...@@ -238,7 +305,7 @@ nfp_nsp_wait_reg(struct nfp_cpp *cpp, u64 *reg, ...@@ -238,7 +305,7 @@ nfp_nsp_wait_reg(struct nfp_cpp *cpp, u64 *reg,
static int nfp_nsp_command(struct nfp_nsp *state, u16 code, u32 option, static int nfp_nsp_command(struct nfp_nsp *state, u16 code, u32 option,
u32 buff_cpp, u64 buff_addr) u32 buff_cpp, u64 buff_addr)
{ {
u64 reg, nsp_base, nsp_buffer, nsp_status, nsp_command; u64 reg, ret_val, nsp_base, nsp_buffer, nsp_status, nsp_command;
struct nfp_cpp *cpp = state->cpp; struct nfp_cpp *cpp = state->cpp;
u32 nsp_cpp; u32 nsp_cpp;
int err; int err;
...@@ -291,18 +358,20 @@ static int nfp_nsp_command(struct nfp_nsp *state, u16 code, u32 option, ...@@ -291,18 +358,20 @@ static int nfp_nsp_command(struct nfp_nsp *state, u16 code, u32 option,
return err; return err;
} }
err = nfp_cpp_readq(cpp, nsp_cpp, nsp_command, &ret_val);
if (err < 0)
return err;
ret_val = FIELD_GET(NSP_COMMAND_OPTION, ret_val);
err = FIELD_GET(NSP_STATUS_RESULT, reg); err = FIELD_GET(NSP_STATUS_RESULT, reg);
if (err) { if (err) {
nfp_warn(cpp, "Result (error) code set: %d command: %d\n", nfp_warn(cpp, "Result (error) code set: %d (%d) command: %d\n",
-err, code); -err, (int)ret_val, code);
nfp_nsp_print_extended_error(state, ret_val);
return -err; return -err;
} }
err = nfp_cpp_readq(cpp, nsp_cpp, nsp_command, &reg); return ret_val;
if (err < 0)
return err;
return FIELD_GET(NSP_COMMAND_OPTION, reg);
} }
static int nfp_nsp_command_buf(struct nfp_nsp *nsp, u16 code, u32 option, static int nfp_nsp_command_buf(struct nfp_nsp *nsp, u16 code, u32 option,
......
...@@ -31,12 +31,48 @@ ...@@ -31,12 +31,48 @@
* SOFTWARE. * SOFTWARE.
*/ */
#ifndef NSP_NSP_ETH_H #ifndef NSP_NSP_H
#define NSP_NSP_ETH_H 1 #define NSP_NSP_H 1
#include <linux/types.h> #include <linux/types.h>
#include <linux/if_ether.h> #include <linux/if_ether.h>
struct firmware;
struct nfp_cpp;
struct nfp_nsp;
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);
enum nfp_eth_interface {
NFP_INTERFACE_NONE = 0,
NFP_INTERFACE_SFP = 1,
NFP_INTERFACE_SFPP = 10,
NFP_INTERFACE_SFP28 = 28,
NFP_INTERFACE_QSFP = 40,
NFP_INTERFACE_CXP = 100,
NFP_INTERFACE_QSFP28 = 112,
};
enum nfp_eth_media {
NFP_MEDIA_DAC_PASSIVE = 0,
NFP_MEDIA_DAC_ACTIVE,
NFP_MEDIA_FIBRE,
};
enum nfp_eth_aneg {
NFP_ANEG_AUTO = 0,
NFP_ANEG_SEARCH,
NFP_ANEG_25G_CONSORTIUM,
NFP_ANEG_25G_IEEE,
NFP_ANEG_DISABLED,
};
/** /**
* struct nfp_eth_table - ETH table information * struct nfp_eth_table - ETH table information
* @count: number of table entries * @count: number of table entries
...@@ -48,13 +84,18 @@ ...@@ -48,13 +84,18 @@
* @base: first channel index (within NBI) * @base: first channel index (within NBI)
* @lanes: number of channels * @lanes: number of channels
* @speed: interface speed (in Mbps) * @speed: interface speed (in Mbps)
* @interface: interface (module) plugged in
* @media: media type of the @interface
* @aneg: auto negotiation mode
* @mac_addr: interface MAC address * @mac_addr: interface MAC address
* @label_port: port id * @label_port: port id
* @label_subport: id of interface within port (for split ports) * @label_subport: id of interface within port (for split ports)
* @enabled: is enabled? * @enabled: is enabled?
* @tx_enabled: is TX enabled? * @tx_enabled: is TX enabled?
* @rx_enabled: is RX enabled? * @rx_enabled: is RX enabled?
* @override_changed: is media reconfig pending?
* *
* @port_type: one of %PORT_* defines for ethtool
* @is_split: is interface part of a split port * @is_split: is interface part of a split port
*/ */
struct nfp_eth_table { struct nfp_eth_table {
...@@ -67,6 +108,11 @@ struct nfp_eth_table { ...@@ -67,6 +108,11 @@ struct nfp_eth_table {
unsigned int lanes; unsigned int lanes;
unsigned int speed; unsigned int speed;
unsigned int interface;
enum nfp_eth_media media;
enum nfp_eth_aneg aneg;
u8 mac_addr[ETH_ALEN]; u8 mac_addr[ETH_ALEN];
u8 label_port; u8 label_port;
...@@ -76,17 +122,29 @@ struct nfp_eth_table { ...@@ -76,17 +122,29 @@ struct nfp_eth_table {
bool tx_enabled; bool tx_enabled;
bool rx_enabled; bool rx_enabled;
bool override_changed;
/* Computed fields */ /* Computed fields */
u8 port_type;
bool is_split; bool is_split;
} ports[0]; } ports[0];
}; };
struct nfp_cpp;
struct nfp_nsp;
struct nfp_eth_table *nfp_eth_read_ports(struct nfp_cpp *cpp); struct nfp_eth_table *nfp_eth_read_ports(struct nfp_cpp *cpp);
struct nfp_eth_table * struct nfp_eth_table *
__nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp); __nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp);
int nfp_eth_set_mod_enable(struct nfp_cpp *cpp, unsigned int idx, bool enable); int nfp_eth_set_mod_enable(struct nfp_cpp *cpp, unsigned int idx, bool enable);
int nfp_eth_set_configured(struct nfp_cpp *cpp, unsigned int idx,
bool configed);
struct nfp_nsp *nfp_eth_config_start(struct nfp_cpp *cpp, unsigned int idx);
int nfp_eth_config_commit_end(struct nfp_nsp *nsp);
void nfp_eth_config_cleanup_end(struct nfp_nsp *nsp);
int __nfp_eth_set_aneg(struct nfp_nsp *nsp, enum nfp_eth_aneg mode);
int __nfp_eth_set_speed(struct nfp_nsp *nsp, unsigned int speed);
int __nfp_eth_set_split(struct nfp_nsp *nsp, unsigned int lanes);
#endif #endif
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