From 265aeb511bd590f193661c6cffff43a8a80755e4 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:22 -0700 Subject: [PATCH 01/14] nfp: add support for .get_link_ksettings() Read link speed from the BAR. This provides very basic information and works for both PFs and VFs. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfp_net_ctrl.h | 13 +++++ .../ethernet/netronome/nfp/nfp_net_ethtool.c | 49 +++++++++++++++++++ 2 files changed, 62 insertions(+) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h index 71d86171b4ee..d04ccc9f6116 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ctrl.h @@ -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 diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index ed22a813e579..d3cec0d4a978 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -173,6 +173,54 @@ 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 (!netif_carrier_ok(netdev)) + 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 void nfp_net_get_ringparam(struct net_device *netdev, struct ethtool_ringparam *ring) { @@ -814,6 +862,7 @@ 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, }; void nfp_net_set_ethtool_ops(struct net_device *netdev) From b9de00770db50ce11f64cd7676dbcaf295a1926f Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:23 -0700 Subject: [PATCH 02/14] nfp: don't spawn netdevs for reconfigured ports After port reconfiguration (port split, media type change) firmware will continue to report old configuration until reboot. NSP will inform us that reconfiguration is pending. To avoid user confusion refuse to spawn netdevs until the new configuration is applied (reboot). We need to split the netdev to eth_table port matching from MAC search and move it earlier in the probe() flow. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_main.h | 5 +- .../net/ethernet/netronome/nfp/nfp_net_main.c | 79 +++++++++++-------- .../netronome/nfp/nfpcore/nfp_nsp_eth.c | 12 ++- .../netronome/nfp/nfpcore/nfp_nsp_eth.h | 3 + 4 files changed, 62 insertions(+), 37 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.h b/drivers/net/ethernet/netronome/nfp/nfp_main.h index 39105d0435e9..bb15a5724bf7 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.h @@ -64,7 +64,8 @@ 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) */ struct nfp_pf { @@ -88,6 +89,8 @@ struct nfp_pf { struct dentry *ddir; unsigned int num_ports; + unsigned int num_netdevs; + struct list_head ports; }; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index 2025cb7c6d90..1644954f52cd 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -129,14 +129,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 +174,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; + if (pf->eth_tbl->ports[i].eth_index == id) + return &pf->eth_tbl->ports[i]; - nn->eth_port = &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 +282,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 +291,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 +313,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 +325,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 +350,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 +366,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 +422,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 +432,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; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c index 38bd80077e33..932772fbd27e 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c @@ -62,6 +62,7 @@ #define NSP_ETH_STATE_TX_ENABLED BIT_ULL(2) #define NSP_ETH_STATE_RX_ENABLED BIT_ULL(3) #define NSP_ETH_STATE_RATE GENMASK_ULL(11, 8) +#define NSP_ETH_STATE_OVRD_CHNG BIT_ULL(22) #define NSP_ETH_CTRL_ENABLED BIT_ULL(1) #define NSP_ETH_CTRL_TX_ENABLED BIT_ULL(2) @@ -110,8 +111,8 @@ static void nfp_eth_copy_mac_reverse(u8 *dst, const u8 *src) } static void -nfp_eth_port_translate(const struct eth_table_entry *src, unsigned int index, - struct nfp_eth_table_port *dst) +nfp_eth_port_translate(struct nfp_nsp *nsp, const struct eth_table_entry *src, + unsigned int index, struct nfp_eth_table_port *dst) { unsigned int rate; u64 port, state; @@ -136,6 +137,11 @@ nfp_eth_port_translate(const struct eth_table_entry *src, unsigned int index, dst->label_port = FIELD_GET(NSP_ETH_PORT_PHYLABEL, port); dst->label_subport = FIELD_GET(NSP_ETH_PORT_LABEL, port); + + if (nfp_nsp_get_abi_ver_minor(nsp) < 17) + return; + + dst->override_changed = FIELD_GET(NSP_ETH_STATE_OVRD_CHNG, state); } static void @@ -225,7 +231,7 @@ __nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp) table->count = cnt; for (i = 0, j = 0; i < NSP_ETH_MAX_COUNT; i++) if (entries[i].port & NSP_ETH_PORT_LANES_MASK) - nfp_eth_port_translate(&entries[i], i, + nfp_eth_port_translate(nsp, &entries[i], i, &table->ports[j++]); nfp_eth_mark_split_ports(cpp, table); diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h index 325e841ca90a..6838741fadd7 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h @@ -54,6 +54,7 @@ * @enabled: is enabled? * @tx_enabled: is TX enabled? * @rx_enabled: is RX enabled? + * @override_changed: is media reconfig pending? * * @is_split: is interface part of a split port */ @@ -76,6 +77,8 @@ struct nfp_eth_table { bool tx_enabled; bool rx_enabled; + bool override_changed; + /* Computed fields */ bool is_split; } ports[0]; From d12537df343ec21054769f59bda3b15879644f52 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:24 -0700 Subject: [PATCH 03/14] nfp: add mutex protection for the port list We will want to unregister netdevs after their port got reconfigured. For that we need to make sure manipulations of port list from the port reconfiguration flow will not race with driver's .remove() callback. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_main.c | 3 +-- drivers/net/ethernet/netronome/nfp/nfp_main.h | 3 +++ .../net/ethernet/netronome/nfp/nfp_net_main.c | 19 +++++++++++++++++-- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.c b/drivers/net/ethernet/netronome/nfp/nfp_main.c index dedac720fb29..96266796fd09 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.c @@ -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); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.h b/drivers/net/ethernet/netronome/nfp/nfp_main.h index bb15a5724bf7..b7ceec9a5783 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.h @@ -42,6 +42,7 @@ #include #include #include +#include #include struct dentry; @@ -67,6 +68,7 @@ struct nfp_eth_table; * @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 */ struct nfp_pf { struct pci_dev *pdev; @@ -92,6 +94,7 @@ struct nfp_pf { unsigned int num_netdevs; struct list_head ports; + struct mutex port_lock; }; extern struct pci_driver nfp_netvf_pci_driver; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index 1644954f52cd..4d602b1ddc90 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -481,17 +481,22 @@ int nfp_net_pci_probe(struct nfp_pf *pf) int stride; int err; + 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) { @@ -565,6 +570,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: @@ -574,6 +581,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; } @@ -581,6 +590,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); @@ -597,4 +610,6 @@ void nfp_net_pci_remove(struct nfp_pf *pf) nfp_cpp_area_release_free(pf->rx_area); nfp_cpp_area_release_free(pf->tx_area); nfp_cpp_area_release_free(pf->ctrl_area); +out: + mutex_unlock(&pf->port_lock); } From cee4295133ba3b85b2f22b542d8855232deba4a3 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:25 -0700 Subject: [PATCH 04/14] nfp: track link state changes For caching link settings - remember if we have seen link events since the last time the eth_port information was refreshed. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net.h | 6 +++++- .../net/ethernet/netronome/nfp/nfp_net_common.c | 14 ++++++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 8e04aa0e6e87..91e963b5104f 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -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,8 @@ 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); + #ifdef CONFIG_NFP_DEBUG void nfp_net_debugfs_create(void); void nfp_net_debugfs_destroy(void); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 8f2da128ce0f..36028b752503 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -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); From 172f638c93dd0b90b231d07662e64025b1e53e38 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:26 -0700 Subject: [PATCH 05/14] nfp: add port state refresh We will need a way of refreshing port state for link settings get/set. For get we need to refresh port speed and type. When settings are changed the reconfiguration may require reboot before it's effective. Unregister netdevs affected by reconfiguration from a workqueue. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_main.h | 3 + drivers/net/ethernet/netronome/nfp/nfp_net.h | 1 + .../net/ethernet/netronome/nfp/nfp_net_main.c | 89 +++++++++++++++++-- 3 files changed, 85 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.h b/drivers/net/ethernet/netronome/nfp/nfp_main.h index b7ceec9a5783..b57de047b002 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.h @@ -44,6 +44,7 @@ #include #include #include +#include struct dentry; struct pci_dev; @@ -69,6 +70,7 @@ struct nfp_eth_table; * @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; @@ -94,6 +96,7 @@ struct nfp_pf { unsigned int num_netdevs; struct list_head ports; + struct work_struct port_refresh_work; struct mutex port_lock; }; diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net.h b/drivers/net/ethernet/netronome/nfp/nfp_net.h index 91e963b5104f..052db9208fbb 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net.h +++ b/drivers/net/ethernet/netronome/nfp/nfp_net.h @@ -813,6 +813,7 @@ 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); diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index 4d602b1ddc90..8e975c36877c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -47,6 +47,7 @@ #include #include #include +#include #include "nfpcore/nfp.h" #include "nfpcore/nfp_cpp.h" @@ -468,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 */ @@ -481,6 +558,7 @@ 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 */ @@ -602,14 +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_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); + nfp_net_pci_remove_finish(pf); out: mutex_unlock(&pf->port_lock); + + cancel_work_sync(&pf->port_refresh_work); } From 21d529d5eb31f905f8c028956204a0d1bdafbc4b Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:27 -0700 Subject: [PATCH 06/14] nfp: report link speed from NSP On the PF prefer the link speed value provided by the NSP. Refresh port table if needed. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index d3cec0d4a978..0fdc14e7b576 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -49,6 +49,7 @@ #include #include "nfpcore/nfp.h" +#include "nfpcore/nfp_nsp_eth.h" #include "nfp_net_ctrl.h" #include "nfp_net.h" @@ -205,6 +206,16 @@ nfp_net_get_link_ksettings(struct net_device *netdev, 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.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); From 42b1e6aa4672beba79882673c687985b884f7904 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:28 -0700 Subject: [PATCH 07/14] nfp: report auto-negotiation in ethtool NSP ABI version 0.17 is exposing the autonegotiation settings. Report whether autoneg is on via ethtool. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c | 4 ++++ .../net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c | 2 ++ .../net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h | 11 +++++++++++ 3 files changed, 17 insertions(+) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 0fdc14e7b576..563ced3c99e1 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -203,6 +203,10 @@ nfp_net_get_link_ksettings(struct net_device *netdev, 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; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c index 932772fbd27e..dcb1bc81e554 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c @@ -63,6 +63,7 @@ #define NSP_ETH_STATE_RX_ENABLED BIT_ULL(3) #define NSP_ETH_STATE_RATE GENMASK_ULL(11, 8) #define NSP_ETH_STATE_OVRD_CHNG BIT_ULL(22) +#define NSP_ETH_STATE_ANEG GENMASK_ULL(25, 23) #define NSP_ETH_CTRL_ENABLED BIT_ULL(1) #define NSP_ETH_CTRL_TX_ENABLED BIT_ULL(2) @@ -142,6 +143,7 @@ nfp_eth_port_translate(struct nfp_nsp *nsp, const struct eth_table_entry *src, return; dst->override_changed = FIELD_GET(NSP_ETH_STATE_OVRD_CHNG, state); + dst->aneg = FIELD_GET(NSP_ETH_STATE_ANEG, state); } static void diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h index 6838741fadd7..6b3e954e70b3 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h @@ -37,6 +37,14 @@ #include #include +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,6 +56,7 @@ * @base: first channel index (within NBI) * @lanes: number of channels * @speed: interface speed (in Mbps) + * @aneg: auto negotiation mode * @mac_addr: interface MAC address * @label_port: port id * @label_subport: id of interface within port (for split ports) @@ -68,6 +77,8 @@ struct nfp_eth_table { unsigned int lanes; unsigned int speed; + enum nfp_eth_aneg aneg; + u8 mac_addr[ETH_ALEN]; u8 label_port; From 9f9e0da57ef1207b26b13c243c5f398c9432156c Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:29 -0700 Subject: [PATCH 08/14] nfp: report port type in ethtool Service process firmware provides us with information about media and interface (SFP module) plugged in, translate that to Linux's PORT_* defines and report via ethtool. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- .../ethernet/netronome/nfp/nfp_net_ethtool.c | 1 + .../netronome/nfp/nfpcore/nfp_nsp_eth.c | 21 ++++++++++++++++ .../netronome/nfp/nfpcore/nfp_nsp_eth.h | 24 +++++++++++++++++++ 3 files changed, 46 insertions(+) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 563ced3c99e1..3b2a09821a59 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -215,6 +215,7 @@ nfp_net_get_link_ksettings(struct net_device *netdev, 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; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c index dcb1bc81e554..07b4ded01514 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c @@ -62,6 +62,8 @@ #define NSP_ETH_STATE_TX_ENABLED BIT_ULL(2) #define NSP_ETH_STATE_RX_ENABLED BIT_ULL(3) #define NSP_ETH_STATE_RATE GENMASK_ULL(11, 8) +#define NSP_ETH_STATE_INTERFACE GENMASK_ULL(19, 12) +#define NSP_ETH_STATE_MEDIA GENMASK_ULL(21, 20) #define NSP_ETH_STATE_OVRD_CHNG BIT_ULL(22) #define NSP_ETH_STATE_ANEG GENMASK_ULL(25, 23) @@ -134,6 +136,9 @@ nfp_eth_port_translate(struct nfp_nsp *nsp, const struct eth_table_entry *src, rate = nfp_eth_rate(FIELD_GET(NSP_ETH_STATE_RATE, state)); dst->speed = dst->lanes * rate; + dst->interface = FIELD_GET(NSP_ETH_STATE_INTERFACE, state); + dst->media = FIELD_GET(NSP_ETH_STATE_MEDIA, state); + nfp_eth_copy_mac_reverse(dst->mac_addr, src->mac_addr); dst->label_port = FIELD_GET(NSP_ETH_PORT_PHYLABEL, port); @@ -170,6 +175,20 @@ nfp_eth_mark_split_ports(struct nfp_cpp *cpp, struct nfp_eth_table *table) } } +static void +nfp_eth_calc_port_type(struct nfp_cpp *cpp, struct nfp_eth_table_port *entry) +{ + if (entry->interface == NFP_INTERFACE_NONE) { + entry->port_type = PORT_NONE; + return; + } + + if (entry->media == NFP_MEDIA_FIBRE) + entry->port_type = PORT_FIBRE; + else + entry->port_type = PORT_DA; +} + /** * nfp_eth_read_ports() - retrieve port information * @cpp: NFP CPP handle @@ -237,6 +256,8 @@ __nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp) &table->ports[j++]); nfp_eth_mark_split_ports(cpp, table); + for (i = 0; i < table->count; i++) + nfp_eth_calc_port_type(cpp, &table->ports[i]); kfree(entries); diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h index 6b3e954e70b3..57eb3cfa6a0a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h @@ -37,6 +37,22 @@ #include #include +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, @@ -56,6 +72,8 @@ enum nfp_eth_aneg { * @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 @@ -65,6 +83,7 @@ enum nfp_eth_aneg { * @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 { @@ -77,6 +96,9 @@ 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]; @@ -91,6 +113,8 @@ struct nfp_eth_table { bool override_changed; /* Computed fields */ + u8 port_type; + bool is_split; } ports[0]; }; From ce22f5a2cbe3c62746da7d3f8272abd03e8c975a Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:30 -0700 Subject: [PATCH 09/14] nfp: separate high level and low level NSP headers We will soon add more NSP commands and structure definitions. Move all high-level NSP header contents to a common nfp_nsp.h file. Right now it mostly boils down to renaming nfp_nsp_eth.h and moving some functions from nfp.h there. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- drivers/net/ethernet/netronome/nfp/nfp_main.c | 2 +- .../ethernet/netronome/nfp/nfp_net_common.c | 2 +- .../ethernet/netronome/nfp/nfp_net_ethtool.c | 2 +- .../net/ethernet/netronome/nfp/nfp_net_main.c | 2 +- .../net/ethernet/netronome/nfp/nfpcore/nfp.h | 10 +--------- .../ethernet/netronome/nfp/nfpcore/nfp_nsp.c | 1 + .../nfp/nfpcore/{nfp_nsp_eth.h => nfp_nsp.h} | 19 ++++++++++++++----- .../netronome/nfp/nfpcore/nfp_nsp_eth.c | 2 +- 8 files changed, 21 insertions(+), 19 deletions(-) rename drivers/net/ethernet/netronome/nfp/nfpcore/{nfp_nsp_eth.h => nfp_nsp.h} (89%) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_main.c b/drivers/net/ethernet/netronome/nfp/nfp_main.c index 96266796fd09..bea2a1a6c211 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_main.c @@ -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" diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c index 36028b752503..61e5741f935c 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_common.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_common.c @@ -67,7 +67,7 @@ #include #include -#include "nfpcore/nfp_nsp_eth.h" +#include "nfpcore/nfp_nsp.h" #include "nfp_net_ctrl.h" #include "nfp_net.h" diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 3b2a09821a59..963d6dd97cec 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -49,7 +49,7 @@ #include #include "nfpcore/nfp.h" -#include "nfpcore/nfp_nsp_eth.h" +#include "nfpcore/nfp_nsp.h" #include "nfp_net_ctrl.h" #include "nfp_net.h" diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c index 8e975c36877c..3e1f97e88710 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_main.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_main.c @@ -52,7 +52,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" #include "nfp_net_ctrl.h" diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h index f7ca8e374923..778bd9424d5d 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h @@ -48,18 +48,10 @@ 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); 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); diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c index 17822ae4a17f..6482831282b2 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c @@ -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 diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h similarity index 89% rename from drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h rename to drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h index 57eb3cfa6a0a..e3baec3cccc2 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h @@ -31,12 +31,24 @@ * SOFTWARE. */ -#ifndef NSP_NSP_ETH_H -#define NSP_NSP_ETH_H 1 +#ifndef NSP_NSP_H +#define NSP_NSP_H 1 #include #include +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, @@ -119,9 +131,6 @@ struct nfp_eth_table { } 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); diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c index 07b4ded01514..837de15ed720 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c @@ -43,7 +43,7 @@ #include #include "nfp.h" -#include "nfp_nsp_eth.h" +#include "nfp_nsp.h" #include "nfp6000/nfp6000.h" #define NSP_ETH_NBI_PORT_COUNT 24 From 30a029217de994db889b7e598b54b674b2ad86b4 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:31 -0700 Subject: [PATCH 10/14] nfp: allow multi-stage NSP configuration NSP commands may be slow to respond, we should try to avoid doing a command-per-item when user requested to change multiple parameters for instance with an ethtool .set_settings() command. Introduce a way of internal NSP code to carry state in NSP structure and add start/finish calls to perform the initialization and kick off of the configuration request, with potentially many parameters being modified in between. nfp_eth_set_mod_enable() will make use of the new code internally, other "set" functions to follow. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- .../net/ethernet/netronome/nfp/nfpcore/nfp.h | 8 ++ .../ethernet/netronome/nfp/nfpcore/nfp_nsp.c | 43 ++++++ .../ethernet/netronome/nfp/nfpcore/nfp_nsp.h | 4 + .../netronome/nfp/nfpcore/nfp_nsp_eth.c | 122 +++++++++++++----- 4 files changed, 142 insertions(+), 35 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h index 778bd9424d5d..8afef7593f13 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp.h @@ -52,6 +52,14 @@ const char *nfp_hwinfo_lookup(struct nfp_cpp *cpp, const char *lookup); struct nfp_nsp; +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); diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c index 6482831282b2..225d07815375 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c @@ -104,8 +104,51 @@ 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 int nfp_nsp_check(struct nfp_nsp *state) { struct nfp_cpp *cpp = state->cpp; diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h index e3baec3cccc2..c452ad311993 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h @@ -136,4 +136,8 @@ 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); +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); + #endif diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c index 837de15ed720..55d8e073ccbd 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c @@ -268,6 +268,82 @@ __nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp) return NULL; } +struct nfp_nsp *nfp_eth_config_start(struct nfp_cpp *cpp, unsigned int idx) +{ + struct eth_table_entry *entries; + struct nfp_nsp *nsp; + int ret; + + entries = kzalloc(NSP_ETH_TABLE_SIZE, GFP_KERNEL); + if (!entries) + return ERR_PTR(-ENOMEM); + + nsp = nfp_nsp_open(cpp); + if (IS_ERR(nsp)) { + kfree(entries); + return nsp; + } + + ret = nfp_nsp_read_eth_table(nsp, entries, NSP_ETH_TABLE_SIZE); + if (ret < 0) { + nfp_err(cpp, "reading port table failed %d\n", ret); + goto err; + } + + if (!(entries[idx].port & NSP_ETH_PORT_LANES_MASK)) { + nfp_warn(cpp, "trying to set port state on disabled port %d\n", + idx); + goto err; + } + + nfp_nsp_config_set_state(nsp, entries, idx); + return nsp; + +err: + nfp_nsp_close(nsp); + kfree(entries); + return ERR_PTR(-EIO); +} + +void nfp_eth_config_cleanup_end(struct nfp_nsp *nsp) +{ + struct eth_table_entry *entries = nfp_nsp_config_entries(nsp); + + nfp_nsp_config_set_modified(nsp, false); + nfp_nsp_config_clear_state(nsp); + nfp_nsp_close(nsp); + kfree(entries); +} + +/** + * nfp_eth_config_commit_end() - perform recorded configuration changes + * @nsp: NFP NSP handle returned from nfp_eth_config_start() + * + * Perform the configuration which was requested with __nfp_eth_set_*() + * helpers and recorded in @nsp state. If device was already configured + * as requested or no __nfp_eth_set_*() operations were made no NSP command + * will be performed. + * + * Return: + * 0 - configuration successful; + * 1 - no changes were needed; + * -ERRNO - configuration failed. + */ +int nfp_eth_config_commit_end(struct nfp_nsp *nsp) +{ + struct eth_table_entry *entries = nfp_nsp_config_entries(nsp); + int ret = 1; + + if (nfp_nsp_config_modified(nsp)) { + ret = nfp_nsp_write_eth_table(nsp, entries, NSP_ETH_TABLE_SIZE); + ret = ret < 0 ? ret : 0; + } + + nfp_eth_config_cleanup_end(nsp); + + return ret; +} + /** * nfp_eth_set_mod_enable() - set PHY module enable control bit * @cpp: NFP CPP handle @@ -284,47 +360,23 @@ int nfp_eth_set_mod_enable(struct nfp_cpp *cpp, unsigned int idx, bool enable) struct eth_table_entry *entries; struct nfp_nsp *nsp; u64 reg; - int ret; - entries = kzalloc(NSP_ETH_TABLE_SIZE, GFP_KERNEL); - if (!entries) - return -ENOMEM; - - nsp = nfp_nsp_open(cpp); - if (IS_ERR(nsp)) { - kfree(entries); + nsp = nfp_eth_config_start(cpp, idx); + if (IS_ERR(nsp)) return PTR_ERR(nsp); - } - ret = nfp_nsp_read_eth_table(nsp, entries, NSP_ETH_TABLE_SIZE); - if (ret < 0) { - nfp_err(cpp, "reading port table failed %d\n", ret); - goto exit_close_nsp; - } - - if (!(entries[idx].port & NSP_ETH_PORT_LANES_MASK)) { - nfp_warn(cpp, "trying to set port state on disabled port %d\n", - idx); - ret = -EINVAL; - goto exit_close_nsp; - } + entries = nfp_nsp_config_entries(nsp); /* Check if we are already in requested state */ reg = le64_to_cpu(entries[idx].state); - if (enable == FIELD_GET(NSP_ETH_CTRL_ENABLED, reg)) { - ret = 0; - goto exit_close_nsp; + if (enable != FIELD_GET(NSP_ETH_CTRL_ENABLED, reg)) { + reg = le64_to_cpu(entries[idx].control); + reg &= ~NSP_ETH_CTRL_ENABLED; + reg |= FIELD_PREP(NSP_ETH_CTRL_ENABLED, enable); + entries[idx].control = cpu_to_le64(reg); + + nfp_nsp_config_set_modified(nsp, true); } - reg = le64_to_cpu(entries[idx].control); - reg &= ~NSP_ETH_CTRL_ENABLED; - reg |= FIELD_PREP(NSP_ETH_CTRL_ENABLED, enable); - entries[idx].control = cpu_to_le64(reg); - - ret = nfp_nsp_write_eth_table(nsp, entries, NSP_ETH_TABLE_SIZE); -exit_close_nsp: - nfp_nsp_close(nsp); - kfree(entries); - - return ret < 0 ? ret : 0; + return nfp_eth_config_commit_end(nsp); } From e890ae8e497a535a8ab692a66f7fc2aee4361728 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:32 -0700 Subject: [PATCH 11/14] nfp: turn NSP port entry into a union Make NSP port structure a union to simplify accessing the fields from generic macros. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- .../netronome/nfp/nfpcore/nfp_nsp_eth.c | 38 ++++++++++++------- 1 file changed, 25 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c index 55d8e073ccbd..ca5c041e64a4 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c @@ -49,7 +49,7 @@ #define NSP_ETH_NBI_PORT_COUNT 24 #define NSP_ETH_MAX_COUNT (2 * NSP_ETH_NBI_PORT_COUNT) #define NSP_ETH_TABLE_SIZE (NSP_ETH_MAX_COUNT * \ - sizeof(struct eth_table_entry)) + sizeof(union eth_table_entry)) #define NSP_ETH_PORT_LANES GENMASK_ULL(3, 0) #define NSP_ETH_PORT_INDEX GENMASK_ULL(15, 8) @@ -71,6 +71,15 @@ #define NSP_ETH_CTRL_TX_ENABLED BIT_ULL(2) #define NSP_ETH_CTRL_RX_ENABLED BIT_ULL(3) +enum nfp_eth_raw { + NSP_ETH_RAW_PORT = 0, + NSP_ETH_RAW_STATE, + NSP_ETH_RAW_MAC, + NSP_ETH_RAW_CONTROL, + + NSP_ETH_NUM_RAW +}; + enum nfp_eth_rate { RATE_INVALID = 0, RATE_10M, @@ -80,12 +89,15 @@ enum nfp_eth_rate { RATE_25G, }; -struct eth_table_entry { - __le64 port; - __le64 state; - u8 mac_addr[6]; - u8 resv[2]; - __le64 control; +union eth_table_entry { + struct { + __le64 port; + __le64 state; + u8 mac_addr[6]; + u8 resv[2]; + __le64 control; + }; + __le64 raw[NSP_ETH_NUM_RAW]; }; static unsigned int nfp_eth_rate(enum nfp_eth_rate rate) @@ -114,7 +126,7 @@ static void nfp_eth_copy_mac_reverse(u8 *dst, const u8 *src) } static void -nfp_eth_port_translate(struct nfp_nsp *nsp, const struct eth_table_entry *src, +nfp_eth_port_translate(struct nfp_nsp *nsp, const union eth_table_entry *src, unsigned int index, struct nfp_eth_table_port *dst) { unsigned int rate; @@ -216,7 +228,7 @@ 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) { - struct eth_table_entry *entries; + union eth_table_entry *entries; struct nfp_eth_table *table; int i, j, ret, cnt = 0; @@ -270,7 +282,7 @@ __nfp_eth_read_ports(struct nfp_cpp *cpp, struct nfp_nsp *nsp) struct nfp_nsp *nfp_eth_config_start(struct nfp_cpp *cpp, unsigned int idx) { - struct eth_table_entry *entries; + union eth_table_entry *entries; struct nfp_nsp *nsp; int ret; @@ -307,7 +319,7 @@ struct nfp_nsp *nfp_eth_config_start(struct nfp_cpp *cpp, unsigned int idx) void nfp_eth_config_cleanup_end(struct nfp_nsp *nsp) { - struct eth_table_entry *entries = nfp_nsp_config_entries(nsp); + union eth_table_entry *entries = nfp_nsp_config_entries(nsp); nfp_nsp_config_set_modified(nsp, false); nfp_nsp_config_clear_state(nsp); @@ -331,7 +343,7 @@ void nfp_eth_config_cleanup_end(struct nfp_nsp *nsp) */ int nfp_eth_config_commit_end(struct nfp_nsp *nsp) { - struct eth_table_entry *entries = nfp_nsp_config_entries(nsp); + union eth_table_entry *entries = nfp_nsp_config_entries(nsp); int ret = 1; if (nfp_nsp_config_modified(nsp)) { @@ -357,7 +369,7 @@ int nfp_eth_config_commit_end(struct nfp_nsp *nsp) */ int nfp_eth_set_mod_enable(struct nfp_cpp *cpp, unsigned int idx, bool enable) { - struct eth_table_entry *entries; + union eth_table_entry *entries; struct nfp_nsp *nsp; u64 reg; From 85eb97dd2f073e72784505e51b048171ab9bda61 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:33 -0700 Subject: [PATCH 12/14] nfp: add extended error messages Allow NSP to set option code even when error is reported. This provides a way for NSP to give user more precise information about why command failed. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- .../ethernet/netronome/nfp/nfpcore/nfp_nsp.c | 37 +++++++++++++++---- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c index 225d07815375..96bb5f6bd87b 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c @@ -97,6 +97,13 @@ enum nfp_nsp_cmd { __MAX_SPCODE, }; +static const struct { + int code; + const char *msg; +} nsp_errors[] = { + { 0, "success" } /* placeholder to avoid warnings */ +}; + struct nfp_nsp { struct nfp_cpp *cpp; struct nfp_resource *res; @@ -149,6 +156,18 @@ void nfp_nsp_config_clear_state(struct nfp_nsp *state) 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; @@ -282,7 +301,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; @@ -335,18 +354,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, ®); - 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, From 5a560832ebf8317eb2af0493b259aba37af05bb1 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:34 -0700 Subject: [PATCH 13/14] nfp: NSP backend for link configuration operations Add NSP backend for upcoming link configuration operations. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- .../ethernet/netronome/nfp/nfpcore/nfp_nsp.c | 6 +- .../ethernet/netronome/nfp/nfpcore/nfp_nsp.h | 7 + .../netronome/nfp/nfpcore/nfp_nsp_eth.c | 180 ++++++++++++++++-- 3 files changed, 179 insertions(+), 14 deletions(-) diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c index 96bb5f6bd87b..4635f42e15b0 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.c @@ -101,7 +101,11 @@ static const struct { int code; const char *msg; } nsp_errors[] = { - { 0, "success" } /* placeholder to avoid warnings */ + { 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 { diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h index c452ad311993..7d34ff145fd7 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp.h @@ -134,10 +134,17 @@ struct nfp_eth_table { 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 diff --git a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c index ca5c041e64a4..639438d8313a 100644 --- a/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c +++ b/drivers/net/ethernet/netronome/nfp/nfpcore/nfp_nsp_eth.c @@ -58,6 +58,7 @@ #define NSP_ETH_PORT_LANES_MASK cpu_to_le64(NSP_ETH_PORT_LANES) +#define NSP_ETH_STATE_CONFIGURED BIT_ULL(0) #define NSP_ETH_STATE_ENABLED BIT_ULL(1) #define NSP_ETH_STATE_TX_ENABLED BIT_ULL(2) #define NSP_ETH_STATE_RX_ENABLED BIT_ULL(3) @@ -67,9 +68,13 @@ #define NSP_ETH_STATE_OVRD_CHNG BIT_ULL(22) #define NSP_ETH_STATE_ANEG GENMASK_ULL(25, 23) +#define NSP_ETH_CTRL_CONFIGURED BIT_ULL(0) #define NSP_ETH_CTRL_ENABLED BIT_ULL(1) #define NSP_ETH_CTRL_TX_ENABLED BIT_ULL(2) #define NSP_ETH_CTRL_RX_ENABLED BIT_ULL(3) +#define NSP_ETH_CTRL_SET_RATE BIT_ULL(4) +#define NSP_ETH_CTRL_SET_LANES BIT_ULL(5) +#define NSP_ETH_CTRL_SET_ANEG BIT_ULL(6) enum nfp_eth_raw { NSP_ETH_RAW_PORT = 0, @@ -100,21 +105,38 @@ union eth_table_entry { __le64 raw[NSP_ETH_NUM_RAW]; }; -static unsigned int nfp_eth_rate(enum nfp_eth_rate rate) +static const struct { + enum nfp_eth_rate rate; + unsigned int speed; +} nsp_eth_rate_tbl[] = { + { RATE_INVALID, 0, }, + { RATE_10M, SPEED_10, }, + { RATE_100M, SPEED_100, }, + { RATE_1G, SPEED_1000, }, + { RATE_10G, SPEED_10000, }, + { RATE_25G, SPEED_25000, }, +}; + +static unsigned int nfp_eth_rate2speed(enum nfp_eth_rate rate) { - unsigned int rate_xlate[] = { - [RATE_INVALID] = 0, - [RATE_10M] = SPEED_10, - [RATE_100M] = SPEED_100, - [RATE_1G] = SPEED_1000, - [RATE_10G] = SPEED_10000, - [RATE_25G] = SPEED_25000, - }; + int i; - if (rate >= ARRAY_SIZE(rate_xlate)) - return 0; + for (i = 0; i < ARRAY_SIZE(nsp_eth_rate_tbl); i++) + if (nsp_eth_rate_tbl[i].rate == rate) + return nsp_eth_rate_tbl[i].speed; - return rate_xlate[rate]; + return 0; +} + +static unsigned int nfp_eth_speed2rate(unsigned int speed) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(nsp_eth_rate_tbl); i++) + if (nsp_eth_rate_tbl[i].speed == speed) + return nsp_eth_rate_tbl[i].rate; + + return RATE_INVALID; } static void nfp_eth_copy_mac_reverse(u8 *dst, const u8 *src) @@ -145,7 +167,7 @@ nfp_eth_port_translate(struct nfp_nsp *nsp, const union eth_table_entry *src, dst->tx_enabled = FIELD_GET(NSP_ETH_STATE_TX_ENABLED, state); dst->rx_enabled = FIELD_GET(NSP_ETH_STATE_RX_ENABLED, state); - rate = nfp_eth_rate(FIELD_GET(NSP_ETH_STATE_RATE, state)); + rate = nfp_eth_rate2speed(FIELD_GET(NSP_ETH_STATE_RATE, state)); dst->speed = dst->lanes * rate; dst->interface = FIELD_GET(NSP_ETH_STATE_INTERFACE, state); @@ -392,3 +414,135 @@ int nfp_eth_set_mod_enable(struct nfp_cpp *cpp, unsigned int idx, bool enable) return nfp_eth_config_commit_end(nsp); } + +/** + * nfp_eth_set_configured() - set PHY module configured control bit + * @cpp: NFP CPP handle + * @idx: NFP chip-wide port index + * @configed: Desired state + * + * Set the ifup/ifdown state on the PHY. + * + * Return: 0 or -ERRNO. + */ +int nfp_eth_set_configured(struct nfp_cpp *cpp, unsigned int idx, bool configed) +{ + union eth_table_entry *entries; + struct nfp_nsp *nsp; + u64 reg; + + nsp = nfp_eth_config_start(cpp, idx); + if (IS_ERR(nsp)) + return PTR_ERR(nsp); + + entries = nfp_nsp_config_entries(nsp); + + /* Check if we are already in requested state */ + reg = le64_to_cpu(entries[idx].state); + if (configed != FIELD_GET(NSP_ETH_STATE_CONFIGURED, reg)) { + reg = le64_to_cpu(entries[idx].control); + reg &= ~NSP_ETH_CTRL_CONFIGURED; + reg |= FIELD_PREP(NSP_ETH_CTRL_CONFIGURED, configed); + entries[idx].control = cpu_to_le64(reg); + + nfp_nsp_config_set_modified(nsp, true); + } + + return nfp_eth_config_commit_end(nsp); +} + +/* Force inline, FIELD_* macroes require masks to be compilation-time known */ +static __always_inline int +nfp_eth_set_bit_config(struct nfp_nsp *nsp, unsigned int raw_idx, + const u64 mask, unsigned int val, const u64 ctrl_bit) +{ + union eth_table_entry *entries = nfp_nsp_config_entries(nsp); + unsigned int idx = nfp_nsp_config_idx(nsp); + u64 reg; + + /* Note: set features were added in ABI 0.14 but the error + * codes were initially not populated correctly. + */ + if (nfp_nsp_get_abi_ver_minor(nsp) < 17) { + nfp_err(nfp_nsp_cpp(nsp), + "set operations not supported, please update flash\n"); + return -EOPNOTSUPP; + } + + /* Check if we are already in requested state */ + reg = le64_to_cpu(entries[idx].raw[raw_idx]); + if (val == FIELD_GET(mask, reg)) + return 0; + + reg &= ~mask; + reg |= FIELD_PREP(mask, val); + entries[idx].raw[raw_idx] = cpu_to_le64(reg); + + entries[idx].control |= cpu_to_le64(ctrl_bit); + + nfp_nsp_config_set_modified(nsp, true); + + return 0; +} + +/** + * __nfp_eth_set_aneg() - set PHY autonegotiation control bit + * @nsp: NFP NSP handle returned from nfp_eth_config_start() + * @mode: Desired autonegotiation mode + * + * Allow/disallow PHY module to advertise/perform autonegotiation. + * Will write to hwinfo overrides in the flash (persistent config). + * + * Return: 0 or -ERRNO. + */ +int __nfp_eth_set_aneg(struct nfp_nsp *nsp, enum nfp_eth_aneg mode) +{ + return nfp_eth_set_bit_config(nsp, NSP_ETH_RAW_STATE, + NSP_ETH_STATE_ANEG, mode, + NSP_ETH_CTRL_SET_ANEG); +} + +/** + * __nfp_eth_set_speed() - set interface speed/rate + * @nsp: NFP NSP handle returned from nfp_eth_config_start() + * @speed: Desired speed (per lane) + * + * Set lane speed. Provided @speed value should be subport speed divided + * by number of lanes this subport is spanning (i.e. 10000 for 40G, 25000 for + * 50G, etc.) + * Will write to hwinfo overrides in the flash (persistent config). + * + * Return: 0 or -ERRNO. + */ +int __nfp_eth_set_speed(struct nfp_nsp *nsp, unsigned int speed) +{ + enum nfp_eth_rate rate; + + rate = nfp_eth_speed2rate(speed); + if (rate == RATE_INVALID) { + nfp_warn(nfp_nsp_cpp(nsp), + "could not find matching lane rate for speed %u\n", + speed); + return -EINVAL; + } + + return nfp_eth_set_bit_config(nsp, NSP_ETH_RAW_STATE, + NSP_ETH_STATE_RATE, rate, + NSP_ETH_CTRL_SET_RATE); +} + +/** + * __nfp_eth_set_split() - set interface lane split + * @nsp: NFP NSP handle returned from nfp_eth_config_start() + * @lanes: Desired lanes per port + * + * Set number of lanes in the port. + * Will write to hwinfo overrides in the flash (persistent config). + * + * Return: 0 or -ERRNO. + */ +int __nfp_eth_set_split(struct nfp_nsp *nsp, unsigned int lanes) +{ + return nfp_eth_set_bit_config(nsp, NSP_ETH_RAW_PORT, NSP_ETH_PORT_LANES, + lanes, NSP_ETH_CTRL_SET_LANES); +} From 7c698737270fee01963f26ea5d168a6a6c4b1269 Mon Sep 17 00:00:00 2001 From: Jakub Kicinski Date: Tue, 4 Apr 2017 16:12:35 -0700 Subject: [PATCH 14/14] nfp: add support for .set_link_ksettings() Support setting link speed and autonegotiation through set_link_ksettings() ethtool op. If the port is reconfigured in incompatible way and reboot is required the netdev will get unregistered and not come back until user reboots the system. Signed-off-by: Jakub Kicinski Reviewed-by: Simon Horman Signed-off-by: David S. Miller --- .../ethernet/netronome/nfp/nfp_net_ethtool.c | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) diff --git a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c index 963d6dd97cec..3328041ec290 100644 --- a/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c +++ b/drivers/net/ethernet/netronome/nfp/nfp_net_ethtool.c @@ -237,6 +237,51 @@ nfp_net_get_link_ksettings(struct net_device *netdev, 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) { @@ -879,6 +924,7 @@ static const struct ethtool_ops nfp_net_ethtool_ops = { .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)