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 @@
#include "nfpcore/nfp.h"
#include "nfpcore/nfp_cpp.h"
#include "nfpcore/nfp_nffw.h"
#include "nfpcore/nfp_nsp_eth.h"
#include "nfpcore/nfp_nsp.h"
#include "nfpcore/nfp6000_pcie.h"
......@@ -385,8 +385,7 @@ static void nfp_pci_remove(struct pci_dev *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);
......
......@@ -42,7 +42,9 @@
#include <linux/list.h>
#include <linux/types.h>
#include <linux/msi.h>
#include <linux/mutex.h>
#include <linux/pci.h>
#include <linux/workqueue.h>
struct dentry;
struct pci_dev;
......@@ -64,8 +66,11 @@ struct nfp_eth_table;
* @fw_loaded: Is the firmware loaded?
* @eth_tbl: NSP ETH table
* @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)
* @port_lock: Protects @ports, @num_ports, @num_netdevs
* @port_refresh_work: Work entry for taking netdevs out
*/
struct nfp_pf {
struct pci_dev *pdev;
......@@ -88,7 +93,11 @@ struct nfp_pf {
struct dentry *ddir;
unsigned int num_ports;
unsigned int num_netdevs;
struct list_head ports;
struct work_struct port_refresh_work;
struct mutex port_lock;
};
extern struct pci_driver nfp_netvf_pci_driver;
......
......@@ -523,7 +523,8 @@ struct nfp_net_dp {
* @reconfig_sync_present: Some thread is performing synchronous reconfig
* @reconfig_timer: Timer for async reading of reconfig results
* @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_max_frames: RX interrupt moderation frame count parameter
* @tx_coalesce_usecs: TX interrupt moderation usecs delay parameter
......@@ -580,6 +581,7 @@ struct nfp_net {
u32 me_freq_mhz;
bool link_up;
bool link_changed;
spinlock_t link_status_lock;
spinlock_t reconfig_lock;
......@@ -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);
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
void nfp_net_debugfs_create(void);
void nfp_net_debugfs_destroy(void);
......
......@@ -67,7 +67,7 @@
#include <net/pkt_cls.h>
#include <net/vxlan.h>
#include "nfpcore/nfp_nsp_eth.h"
#include "nfpcore/nfp_nsp.h"
#include "nfp_net_ctrl.h"
#include "nfp_net.h"
......@@ -376,6 +376,19 @@ static irqreturn_t nfp_net_irq_rxtx(int irq, void *data)
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
* @nn: NFP Network structure
......@@ -395,6 +408,7 @@ static void nfp_net_read_link_status(struct nfp_net *nn)
goto out;
nn->link_up = link_up;
nn->link_changed = true;
if (nn->link_up) {
netif_carrier_on(nn->dp.netdev);
......
......@@ -177,6 +177,19 @@
#define NFP_NET_CFG_VERSION_MINOR(x) (((x) & 0xff) << 0)
#define NFP_NET_CFG_STS 0x0034
#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_MAX_TXRINGS 0x003c
#define NFP_NET_CFG_MAX_RXRINGS 0x0040
......
......@@ -49,6 +49,7 @@
#include <linux/ethtool.h>
#include "nfpcore/nfp.h"
#include "nfpcore/nfp_nsp.h"
#include "nfp_net_ctrl.h"
#include "nfp_net.h"
......@@ -173,6 +174,114 @@ static void nfp_net_get_drvinfo(struct net_device *netdev,
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,
struct ethtool_ringparam *ring)
{
......@@ -814,6 +923,8 @@ static const struct ethtool_ops nfp_net_ethtool_ops = {
.set_coalesce = nfp_net_set_coalesce,
.get_channels = nfp_net_get_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)
......
......@@ -47,11 +47,12 @@
#include <linux/pci_regs.h>
#include <linux/msi.h>
#include <linux/random.h>
#include <linux/rtnetlink.h>
#include "nfpcore/nfp.h"
#include "nfpcore/nfp_cpp.h"
#include "nfpcore/nfp_nffw.h"
#include "nfpcore/nfp_nsp_eth.h"
#include "nfpcore/nfp_nsp.h"
#include "nfpcore/nfp6000_pcie.h"
#include "nfp_net_ctrl.h"
......@@ -129,14 +130,29 @@ static u8 __iomem *nfp_net_map_area(struct nfp_cpp *cpp,
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
nfp_net_get_mac_addr_hwinfo(struct nfp_net_dp *dp, struct nfp_cpp *cpp,
unsigned int id)
nfp_net_get_mac_addr(struct nfp_net *nn, struct nfp_cpp *cpp, unsigned int id)
{
struct nfp_net_dp *dp = &nn->dp;
u8 mac_addr[ETH_ALEN];
const char *mac_str;
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);
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,
ether_addr_copy(dp->netdev->perm_addr, mac_addr);
}
/**
* nfp_net_get_mac_addr() - Get the MAC address.
* @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)
static struct nfp_eth_table_port *
nfp_net_find_port(struct nfp_pf *pf, unsigned int id)
{
int i;
for (i = 0; pf->eth_tbl && i < pf->eth_tbl->count; i++)
if (pf->eth_tbl->ports[i].eth_index == id) {
const u8 *mac_addr = pf->eth_tbl->ports[i].mac_addr;
nn->eth_port = &pf->eth_tbl->ports[i];
if (pf->eth_tbl->ports[i].eth_index == id)
return &pf->eth_tbl->ports[i];
ether_addr_copy(nn->dp.netdev->dev_addr, mac_addr);
ether_addr_copy(nn->dp.netdev->perm_addr, mac_addr);
return;
}
nfp_net_get_mac_addr_hwinfo(&nn->dp, pf->cpp, id);
return NULL;
}
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)
while (!list_empty(&pf->ports)) {
nn = list_first_entry(&pf->ports, struct nfp_net, port_list);
list_del(&nn->port_list);
pf->num_netdevs--;
nfp_net_netdev_free(nn);
}
......@@ -291,7 +292,8 @@ static void nfp_net_pf_free_netdevs(struct nfp_pf *pf)
static struct nfp_net *
nfp_net_pf_alloc_port_netdev(struct nfp_pf *pf, void __iomem *ctrl_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;
struct nfp_net *nn;
......@@ -312,6 +314,7 @@ nfp_net_pf_alloc_port_netdev(struct nfp_pf *pf, void __iomem *ctrl_bar,
nn->dp.is_vf = 0;
nn->stride_rx = stride;
nn->stride_tx = stride;
nn->eth_port = eth_port;
return nn;
}
......@@ -323,7 +326,7 @@ nfp_net_pf_init_port_netdev(struct nfp_pf *pf, struct nfp_net *nn,
int err;
/* 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
* 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,
int stride, struct nfp_net_fw_version *fw_ver)
{
u32 prev_tx_base, prev_rx_base, tgt_tx_base, tgt_rx_base;
struct nfp_eth_table_port *eth_port;
struct nfp_net *nn;
unsigned int i;
int err;
......@@ -363,17 +367,27 @@ nfp_net_pf_alloc_netdevs(struct nfp_pf *pf, void __iomem *ctrl_bar,
prev_tx_base = tgt_tx_base;
prev_rx_base = tgt_rx_base;
nn = nfp_net_pf_alloc_port_netdev(pf, ctrl_bar, tx_bar, rx_bar,
stride, fw_ver);
if (IS_ERR(nn)) {
err = PTR_ERR(nn);
goto err_free_prev;
eth_port = nfp_net_find_port(pf, i);
if (eth_port && eth_port->override_changed) {
nfp_warn(pf->cpp, "Config changed for port #%d, reboot required before port will be operational\n", i);
} else {
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;
}
if (list_empty(&pf->ports))
return -ENODEV;
return 0;
err_free_prev:
......@@ -409,7 +423,7 @@ nfp_net_pf_spawn_netdevs(struct nfp_pf *pf,
}
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);
if (!num_irqs) {
nn_warn(nn, "Unable to allocate MSI-X Vectors. Exiting\n");
......@@ -419,7 +433,7 @@ nfp_net_pf_spawn_netdevs(struct nfp_pf *pf,
/* Distribute IRQs to ports */
irqs_left = num_irqs;
ports_left = pf->num_ports;
ports_left = pf->num_netdevs;
list_for_each_entry(nn, &pf->ports, port_list) {
unsigned int n;
......@@ -455,6 +469,82 @@ nfp_net_pf_spawn_netdevs(struct nfp_pf *pf,
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
*/
......@@ -468,17 +558,23 @@ int nfp_net_pci_probe(struct nfp_pf *pf)
int stride;
int err;
INIT_WORK(&pf->port_refresh_work, nfp_net_refresh_netdevs);
mutex_init(&pf->port_lock);
/* Verify that the board has completed initialization */
if (!nfp_is_ready(pf->cpp)) {
nfp_err(pf->cpp, "NFP is not ready for NIC operation.\n");
return -EINVAL;
}
mutex_lock(&pf->port_lock);
pf->num_ports = nfp_net_pf_get_num_ports(pf);
ctrl_bar = nfp_net_pf_map_ctrl_bar(pf);
if (!ctrl_bar)
return pf->fw_loaded ? -EINVAL : -EPROBE_DEFER;
if (!ctrl_bar) {
err = pf->fw_loaded ? -EINVAL : -EPROBE_DEFER;
goto err_unlock;
}
nfp_net_get_fw_version(&fw_ver, ctrl_bar);
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)
if (err)
goto err_clean_ddir;
mutex_unlock(&pf->port_lock);
return 0;
err_clean_ddir:
......@@ -561,6 +659,8 @@ int nfp_net_pci_probe(struct nfp_pf *pf)
nfp_cpp_area_release_free(pf->tx_area);
err_ctrl_unmap:
nfp_cpp_area_release_free(pf->ctrl_area);
err_unlock:
mutex_unlock(&pf->port_lock);
return err;
}
......@@ -568,6 +668,10 @@ void nfp_net_pci_remove(struct nfp_pf *pf)
{
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) {
nfp_net_debugfs_dir_clean(&nn->debugfs_dir);
......@@ -576,12 +680,9 @@ void nfp_net_pci_remove(struct nfp_pf *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);
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);
cancel_work_sync(&pf->port_refresh_work);
}
......@@ -48,18 +48,18 @@
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 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);
struct nfp_cpp *nfp_nsp_cpp(struct nfp_nsp *state);
bool nfp_nsp_config_modified(struct nfp_nsp *state);
void nfp_nsp_config_set_modified(struct nfp_nsp *state, bool modified);
void *nfp_nsp_config_entries(struct nfp_nsp *state);
unsigned int nfp_nsp_config_idx(struct nfp_nsp *state);
void nfp_nsp_config_set_state(struct nfp_nsp *state, void *entries,
unsigned int idx);
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_write_eth_table(struct nfp_nsp *state,
const void *buf, unsigned int size);
......
......@@ -49,6 +49,7 @@
#include "nfp.h"
#include "nfp_cpp.h"
#include "nfp_nsp.h"
/* Offsets relative to the CSR base */
#define NSP_STATUS 0x00
......@@ -96,6 +97,17 @@ enum nfp_nsp_cmd {
__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_cpp *cpp;
struct nfp_resource *res;
......@@ -103,8 +115,63 @@ struct nfp_nsp {
u16 major;
u16 minor;
} 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)
{
struct nfp_cpp *cpp = state->cpp;
......@@ -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,
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;
u32 nsp_cpp;
int err;
......@@ -291,18 +358,20 @@ static int nfp_nsp_command(struct nfp_nsp *state, u16 code, u32 option,
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);
if (err) {
nfp_warn(cpp, "Result (error) code set: %d command: %d\n",
-err, code);
nfp_warn(cpp, "Result (error) code set: %d (%d) command: %d\n",
-err, (int)ret_val, code);
nfp_nsp_print_extended_error(state, ret_val);
return -err;
}
err = nfp_cpp_readq(cpp, nsp_cpp, nsp_command, &reg);
if (err < 0)
return err;
return FIELD_GET(NSP_COMMAND_OPTION, reg);
return ret_val;
}
static int nfp_nsp_command_buf(struct nfp_nsp *nsp, u16 code, u32 option,
......
......@@ -31,12 +31,48 @@
* SOFTWARE.
*/
#ifndef NSP_NSP_ETH_H
#define NSP_NSP_ETH_H 1
#ifndef NSP_NSP_H
#define NSP_NSP_H 1
#include <linux/types.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
* @count: number of table entries
......@@ -48,13 +84,18 @@
* @base: first channel index (within NBI)
* @lanes: number of channels
* @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
* @label_port: port id
* @label_subport: id of interface within port (for split ports)
* @enabled: is enabled?
* @tx_enabled: is TX 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
*/
struct nfp_eth_table {
......@@ -67,6 +108,11 @@ struct nfp_eth_table {
unsigned int lanes;
unsigned int speed;
unsigned int interface;
enum nfp_eth_media media;
enum nfp_eth_aneg aneg;
u8 mac_addr[ETH_ALEN];
u8 label_port;
......@@ -76,17 +122,29 @@ struct nfp_eth_table {
bool tx_enabled;
bool rx_enabled;
bool override_changed;
/* Computed fields */
u8 port_type;
bool is_split;
} 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_nsp *nsp);
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
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