summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEdward Cree <ecree@solarflare.com>2020-07-27 12:55:55 +0100
committerDavid S. Miller <davem@davemloft.net>2020-07-27 12:26:55 -0700
commit51b35a454efdcd86f578e61ec8bf7596299c5f80 (patch)
tree601ce4c9a7d89a13d07c194331434b6ea9a7eaba
parent61060c5dc5c5734942528f31c094606539fffb8b (diff)
sfc: skeleton EF100 PF driver
No TX or RX path, no MCDI, not even an ifup/down handler. Besides stubs, the bulk of the patch deals with reading the Xilinx extended PCIe capability, which tells us where to find our BAR. Though in the same module, EF100 has its own struct pci_driver, which is named sfc_ef100. A small number of additional nic_type methods are added; those in the TX (tx_enqueue) and RX (rx_packet) paths are called through indirect call wrappers to minimise the performance impact. Signed-off-by: Edward Cree <ecree@solarflare.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--drivers/net/ethernet/sfc/Kconfig5
-rw-r--r--drivers/net/ethernet/sfc/Makefile4
-rw-r--r--drivers/net/ethernet/sfc/ef10.c7
-rw-r--r--drivers/net/ethernet/sfc/ef100.c541
-rw-r--r--drivers/net/ethernet/sfc/ef100.h12
-rw-r--r--drivers/net/ethernet/sfc/ef100_ethtool.c24
-rw-r--r--drivers/net/ethernet/sfc/ef100_ethtool.h12
-rw-r--r--drivers/net/ethernet/sfc/ef100_netdev.c129
-rw-r--r--drivers/net/ethernet/sfc/ef100_netdev.h17
-rw-r--r--drivers/net/ethernet/sfc/ef100_nic.c174
-rw-r--r--drivers/net/ethernet/sfc/ef100_nic.h26
-rw-r--r--drivers/net/ethernet/sfc/ef100_rx.c25
-rw-r--r--drivers/net/ethernet/sfc/ef100_rx.h19
-rw-r--r--drivers/net/ethernet/sfc/ef100_tx.c32
-rw-r--r--drivers/net/ethernet/sfc/ef100_tx.h18
-rw-r--r--drivers/net/ethernet/sfc/efx.c8
-rw-r--r--drivers/net/ethernet/sfc/efx.h16
-rw-r--r--drivers/net/ethernet/sfc/ethtool.c2
-rw-r--r--drivers/net/ethernet/sfc/ethtool_common.c2
-rw-r--r--drivers/net/ethernet/sfc/ethtool_common.h2
-rw-r--r--drivers/net/ethernet/sfc/mcdi.c2
-rw-r--r--drivers/net/ethernet/sfc/net_driver.h16
-rw-r--r--drivers/net/ethernet/sfc/nic_common.h6
-rw-r--r--drivers/net/ethernet/sfc/siena.c3
-rw-r--r--drivers/net/ethernet/sfc/tx.c4
-rw-r--r--drivers/net/ethernet/sfc/tx_common.h2
26 files changed, 1095 insertions, 13 deletions
diff --git a/drivers/net/ethernet/sfc/Kconfig b/drivers/net/ethernet/sfc/Kconfig
index 81b0f7d3a025..5e37c8313725 100644
--- a/drivers/net/ethernet/sfc/Kconfig
+++ b/drivers/net/ethernet/sfc/Kconfig
@@ -17,7 +17,7 @@ config NET_VENDOR_SOLARFLARE
if NET_VENDOR_SOLARFLARE
config SFC
- tristate "Solarflare SFC9000/SFC9100-family support"
+ tristate "Solarflare SFC9000/SFC9100/EF100-family support"
depends on PCI
select MDIO
select CRC32
@@ -26,6 +26,9 @@ config SFC
This driver supports 10/40-gigabit Ethernet cards based on
the Solarflare SFC9000-family and SFC9100-family controllers.
+ It also supports 10/25/40/100-gigabit Ethernet cards based
+ on the Solarflare EF100 networking IP in Xilinx FPGAs.
+
To compile this driver as a module, choose M here. The module
will be called sfc.
config SFC_MTD
diff --git a/drivers/net/ethernet/sfc/Makefile b/drivers/net/ethernet/sfc/Makefile
index 87d093da22ca..8bd01c429f91 100644
--- a/drivers/net/ethernet/sfc/Makefile
+++ b/drivers/net/ethernet/sfc/Makefile
@@ -4,7 +4,9 @@ sfc-y += efx.o efx_common.o efx_channels.o nic.o \
tx.o tx_common.o tx_tso.o rx.o rx_common.o \
selftest.o ethtool.o ethtool_common.o ptp.o \
mcdi.o mcdi_port.o mcdi_port_common.o \
- mcdi_functions.o mcdi_filters.o mcdi_mon.o
+ mcdi_functions.o mcdi_filters.o mcdi_mon.o \
+ ef100.o ef100_nic.o ef100_netdev.o \
+ ef100_ethtool.o ef100_rx.o ef100_tx.o
sfc-$(CONFIG_SFC_MTD) += mtd.o
sfc-$(CONFIG_SFC_SRIOV) += sriov.o siena_sriov.o ef10_sriov.o
diff --git a/drivers/net/ethernet/sfc/ef10.c b/drivers/net/ethernet/sfc/ef10.c
index fa7229fff2ff..4b0b2cf026a5 100644
--- a/drivers/net/ethernet/sfc/ef10.c
+++ b/drivers/net/ethernet/sfc/ef10.c
@@ -6,6 +6,7 @@
#include "net_driver.h"
#include "rx_common.h"
+#include "tx_common.h"
#include "ef10_regs.h"
#include "io.h"
#include "mcdi.h"
@@ -3977,6 +3978,7 @@ const struct efx_nic_type efx_hunt_a0_vf_nic_type = {
.tx_remove = efx_mcdi_tx_remove,
.tx_write = efx_ef10_tx_write,
.tx_limit_len = efx_ef10_tx_limit_len,
+ .tx_enqueue = __efx_enqueue_skb,
.rx_push_rss_config = efx_mcdi_vf_rx_push_rss_config,
.rx_pull_rss_config = efx_mcdi_rx_pull_rss_config,
.rx_probe = efx_mcdi_rx_probe,
@@ -3984,6 +3986,7 @@ const struct efx_nic_type efx_hunt_a0_vf_nic_type = {
.rx_remove = efx_mcdi_rx_remove,
.rx_write = efx_ef10_rx_write,
.rx_defer_refill = efx_ef10_rx_defer_refill,
+ .rx_packet = __efx_rx_packet,
.ev_probe = efx_mcdi_ev_probe,
.ev_init = efx_ef10_ev_init,
.ev_fini = efx_mcdi_ev_fini,
@@ -4038,6 +4041,7 @@ const struct efx_nic_type efx_hunt_a0_vf_nic_type = {
.rx_hash_key_size = 40,
.check_caps = ef10_check_caps,
.print_additional_fwver = efx_ef10_print_additional_fwver,
+ .sensor_event = efx_mcdi_sensor_event,
};
const struct efx_nic_type efx_hunt_a0_nic_type = {
@@ -4087,6 +4091,7 @@ const struct efx_nic_type efx_hunt_a0_nic_type = {
.tx_remove = efx_mcdi_tx_remove,
.tx_write = efx_ef10_tx_write,
.tx_limit_len = efx_ef10_tx_limit_len,
+ .tx_enqueue = __efx_enqueue_skb,
.rx_push_rss_config = efx_mcdi_pf_rx_push_rss_config,
.rx_pull_rss_config = efx_mcdi_rx_pull_rss_config,
.rx_push_rss_context_config = efx_mcdi_rx_push_rss_context_config,
@@ -4097,6 +4102,7 @@ const struct efx_nic_type efx_hunt_a0_nic_type = {
.rx_remove = efx_mcdi_rx_remove,
.rx_write = efx_ef10_rx_write,
.rx_defer_refill = efx_ef10_rx_defer_refill,
+ .rx_packet = __efx_rx_packet,
.ev_probe = efx_mcdi_ev_probe,
.ev_init = efx_ef10_ev_init,
.ev_fini = efx_mcdi_ev_fini,
@@ -4172,4 +4178,5 @@ const struct efx_nic_type efx_hunt_a0_nic_type = {
.rx_hash_key_size = 40,
.check_caps = ef10_check_caps,
.print_additional_fwver = efx_ef10_print_additional_fwver,
+ .sensor_event = efx_mcdi_sensor_event,
};
diff --git a/drivers/net/ethernet/sfc/ef100.c b/drivers/net/ethernet/sfc/ef100.c
new file mode 100644
index 000000000000..de611c0f94e7
--- /dev/null
+++ b/drivers/net/ethernet/sfc/ef100.c
@@ -0,0 +1,541 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/****************************************************************************
+ * Driver for Solarflare network controllers and boards
+ * Copyright 2005-2018 Solarflare Communications Inc.
+ * Copyright 2019-2020 Xilinx Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include "net_driver.h"
+#include <linux/module.h>
+#include <linux/aer.h>
+#include "efx_common.h"
+#include "efx_channels.h"
+#include "io.h"
+#include "ef100_nic.h"
+#include "ef100_netdev.h"
+#include "ef100_regs.h"
+#include "ef100.h"
+
+#define EFX_EF100_PCI_DEFAULT_BAR 2
+
+/* Number of bytes at start of vendor specified extended capability that indicate
+ * that the capability is vendor specified. i.e. offset from value returned by
+ * pci_find_next_ext_capability() to beginning of vendor specified capability
+ * header.
+ */
+#define PCI_EXT_CAP_HDR_LENGTH 4
+
+/* Expected size of a Xilinx continuation address table entry. */
+#define ESE_GZ_CFGBAR_CONT_CAP_MIN_LENGTH 16
+
+struct ef100_func_ctl_window {
+ bool valid;
+ unsigned int bar;
+ u64 offset;
+};
+
+static int ef100_pci_walk_xilinx_table(struct efx_nic *efx, u64 offset,
+ struct ef100_func_ctl_window *result);
+
+/* Number of bytes to offset when reading bit position x with dword accessors. */
+#define ROUND_DOWN_TO_DWORD(x) (((x) & (~31)) >> 3)
+
+#define EXTRACT_BITS(x, lbn, width) \
+ (((x) >> ((lbn) & 31)) & ((1ull << (width)) - 1))
+
+static u32 _ef100_pci_get_bar_bits_with_width(struct efx_nic *efx,
+ int structure_start,
+ int lbn, int width)
+{
+ efx_dword_t dword;
+
+ efx_readd(efx, &dword, structure_start + ROUND_DOWN_TO_DWORD(lbn));
+
+ return EXTRACT_BITS(le32_to_cpu(dword.u32[0]), lbn, width);
+}
+
+#define ef100_pci_get_bar_bits(efx, entry_location, bitdef) \
+ _ef100_pci_get_bar_bits_with_width(efx, entry_location, \
+ ESF_GZ_CFGBAR_ ## bitdef ## _LBN, \
+ ESF_GZ_CFGBAR_ ## bitdef ## _WIDTH)
+
+static int ef100_pci_parse_ef100_entry(struct efx_nic *efx, int entry_location,
+ struct ef100_func_ctl_window *result)
+{
+ u64 offset = ef100_pci_get_bar_bits(efx, entry_location, EF100_FUNC_CTL_WIN_OFF) <<
+ ESE_GZ_EF100_FUNC_CTL_WIN_OFF_SHIFT;
+ u32 bar = ef100_pci_get_bar_bits(efx, entry_location, EF100_BAR);
+
+ netif_dbg(efx, probe, efx->net_dev,
+ "Found EF100 function control window bar=%d offset=0x%llx\n",
+ bar, offset);
+
+ if (result->valid) {
+ netif_err(efx, probe, efx->net_dev,
+ "Duplicated EF100 table entry.\n");
+ return -EINVAL;
+ }
+
+ if (bar == ESE_GZ_CFGBAR_EF100_BAR_NUM_EXPANSION_ROM ||
+ bar == ESE_GZ_CFGBAR_EF100_BAR_NUM_INVALID) {
+ netif_err(efx, probe, efx->net_dev,
+ "Bad BAR value of %d in Xilinx capabilities EF100 entry.\n",
+ bar);
+ return -EINVAL;
+ }
+
+ result->bar = bar;
+ result->offset = offset;
+ result->valid = true;
+ return 0;
+}
+
+static bool ef100_pci_does_bar_overflow(struct efx_nic *efx, int bar,
+ u64 next_entry)
+{
+ return next_entry + ESE_GZ_CFGBAR_ENTRY_HEADER_SIZE >
+ pci_resource_len(efx->pci_dev, bar);
+}
+
+/* Parse a Xilinx capabilities table entry describing a continuation to a new
+ * sub-table.
+ */
+static int ef100_pci_parse_continue_entry(struct efx_nic *efx, int entry_location,
+ struct ef100_func_ctl_window *result)
+{
+ unsigned int previous_bar;
+ efx_oword_t entry;
+ u64 offset;
+ int rc = 0;
+ u32 bar;
+
+ efx_reado(efx, &entry, entry_location);
+
+ bar = EFX_OWORD_FIELD32(entry, ESF_GZ_CFGBAR_CONT_CAP_BAR);
+
+ offset = EFX_OWORD_FIELD64(entry, ESF_GZ_CFGBAR_CONT_CAP_OFFSET) <<
+ ESE_GZ_CONT_CAP_OFFSET_BYTES_SHIFT;
+
+ previous_bar = efx->mem_bar;
+
+ if (bar == ESE_GZ_VSEC_BAR_NUM_EXPANSION_ROM ||
+ bar == ESE_GZ_VSEC_BAR_NUM_INVALID) {
+ netif_err(efx, probe, efx->net_dev,
+ "Bad BAR value of %d in Xilinx capabilities sub-table.\n",
+ bar);
+ return -EINVAL;
+ }
+
+ if (bar != previous_bar) {
+ efx_fini_io(efx);
+
+ if (ef100_pci_does_bar_overflow(efx, bar, offset)) {
+ netif_err(efx, probe, efx->net_dev,
+ "Xilinx table will overrun BAR[%d] offset=0x%llx\n",
+ bar, offset);
+ return -EINVAL;
+ }
+
+ /* Temporarily map new BAR. */
+ rc = efx_init_io(efx, bar,
+ DMA_BIT_MASK(ESF_GZ_TX_SEND_ADDR_WIDTH),
+ pci_resource_len(efx->pci_dev, bar));
+ if (rc) {
+ netif_err(efx, probe, efx->net_dev,
+ "Mapping new BAR for Xilinx table failed, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ rc = ef100_pci_walk_xilinx_table(efx, offset, result);
+ if (rc)
+ return rc;
+
+ if (bar != previous_bar) {
+ efx_fini_io(efx);
+
+ /* Put old BAR back. */
+ rc = efx_init_io(efx, previous_bar,
+ DMA_BIT_MASK(ESF_GZ_TX_SEND_ADDR_WIDTH),
+ pci_resource_len(efx->pci_dev, previous_bar));
+ if (rc) {
+ netif_err(efx, probe, efx->net_dev,
+ "Putting old BAR back failed, rc=%d\n", rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/* Iterate over the Xilinx capabilities table in the currently mapped BAR and
+ * call ef100_pci_parse_ef100_entry() on any EF100 entries and
+ * ef100_pci_parse_continue_entry() on any table continuations.
+ */
+static int ef100_pci_walk_xilinx_table(struct efx_nic *efx, u64 offset,
+ struct ef100_func_ctl_window *result)
+{
+ u64 current_entry = offset;
+ int rc = 0;
+
+ while (true) {
+ u32 id = ef100_pci_get_bar_bits(efx, current_entry, ENTRY_FORMAT);
+ u32 last = ef100_pci_get_bar_bits(efx, current_entry, ENTRY_LAST);
+ u32 rev = ef100_pci_get_bar_bits(efx, current_entry, ENTRY_REV);
+ u32 entry_size;
+
+ if (id == ESE_GZ_CFGBAR_ENTRY_LAST)
+ return 0;
+
+ entry_size = ef100_pci_get_bar_bits(efx, current_entry, ENTRY_SIZE);
+
+ netif_dbg(efx, probe, efx->net_dev,
+ "Seen Xilinx table entry 0x%x size 0x%x at 0x%llx in BAR[%d]\n",
+ id, entry_size, current_entry, efx->mem_bar);
+
+ if (entry_size < sizeof(u32) * 2) {
+ netif_err(efx, probe, efx->net_dev,
+ "Xilinx table entry too short len=0x%x\n", entry_size);
+ return -EINVAL;
+ }
+
+ switch (id) {
+ case ESE_GZ_CFGBAR_ENTRY_EF100:
+ if (rev != ESE_GZ_CFGBAR_ENTRY_REV_EF100 ||
+ entry_size < ESE_GZ_CFGBAR_ENTRY_SIZE_EF100) {
+ netif_err(efx, probe, efx->net_dev,
+ "Bad length or rev for EF100 entry in Xilinx capabilities table. entry_size=%d rev=%d.\n",
+ entry_size, rev);
+ return -EINVAL;
+ }
+
+ rc = ef100_pci_parse_ef100_entry(efx, current_entry,
+ result);
+ if (rc)
+ return rc;
+ break;
+ case ESE_GZ_CFGBAR_ENTRY_CONT_CAP_ADDR:
+ if (rev != 0 || entry_size < ESE_GZ_CFGBAR_CONT_CAP_MIN_LENGTH) {
+ netif_err(efx, probe, efx->net_dev,
+ "Bad length or rev for continue entry in Xilinx capabilities table. entry_size=%d rev=%d.\n",
+ entry_size, rev);
+ return -EINVAL;
+ }
+
+ rc = ef100_pci_parse_continue_entry(efx, current_entry, result);
+ if (rc)
+ return rc;
+ break;
+ default:
+ /* Ignore unknown table entries. */
+ break;
+ }
+
+ if (last)
+ return 0;
+
+ current_entry += entry_size;
+
+ if (ef100_pci_does_bar_overflow(efx, efx->mem_bar, current_entry)) {
+ netif_err(efx, probe, efx->net_dev,
+ "Xilinx table overrun at position=0x%llx.\n",
+ current_entry);
+ return -EINVAL;
+ }
+ }
+}
+
+static int _ef100_pci_get_config_bits_with_width(struct efx_nic *efx,
+ int structure_start, int lbn,
+ int width, u32 *result)
+{
+ int rc, pos = structure_start + ROUND_DOWN_TO_DWORD(lbn);
+ u32 temp;
+
+ rc = pci_read_config_dword(efx->pci_dev, pos, &temp);
+ if (rc) {
+ netif_err(efx, probe, efx->net_dev,
+ "Failed to read PCI config dword at %d\n",
+ pos);
+ return rc;
+ }
+
+ *result = EXTRACT_BITS(temp, lbn, width);
+
+ return 0;
+}
+
+#define ef100_pci_get_config_bits(efx, entry_location, bitdef, result) \
+ _ef100_pci_get_config_bits_with_width(efx, entry_location, \
+ ESF_GZ_VSEC_ ## bitdef ## _LBN, \
+ ESF_GZ_VSEC_ ## bitdef ## _WIDTH, result)
+
+/* Call ef100_pci_walk_xilinx_table() for the Xilinx capabilities table pointed
+ * to by this PCI_EXT_CAP_ID_VNDR.
+ */
+static int ef100_pci_parse_xilinx_cap(struct efx_nic *efx, int vndr_cap,
+ bool has_offset_hi,
+ struct ef100_func_ctl_window *result)
+{
+ u32 offset_high = 0;
+ u32 offset_lo = 0;
+ u64 offset = 0;
+ u32 bar = 0;
+ int rc = 0;
+
+ rc = ef100_pci_get_config_bits(efx, vndr_cap, TBL_BAR, &bar);
+ if (rc) {
+ netif_err(efx, probe, efx->net_dev,
+ "Failed to read ESF_GZ_VSEC_TBL_BAR, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ if (bar == ESE_GZ_CFGBAR_CONT_CAP_BAR_NUM_EXPANSION_ROM ||
+ bar == ESE_GZ_CFGBAR_CONT_CAP_BAR_NUM_INVALID) {
+ netif_err(efx, probe, efx->net_dev,
+ "Bad BAR value of %d in Xilinx capabilities sub-table.\n",
+ bar);
+ return -EINVAL;
+ }
+
+ rc = ef100_pci_get_config_bits(efx, vndr_cap, TBL_OFF_LO, &offset_lo);
+ if (rc) {
+ netif_err(efx, probe, efx->net_dev,
+ "Failed to read ESF_GZ_VSEC_TBL_OFF_LO, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* Get optional extension to 64bit offset. */
+ if (has_offset_hi) {
+ rc = ef100_pci_get_config_bits(efx, vndr_cap, TBL_OFF_HI, &offset_high);
+ if (rc) {
+ netif_err(efx, probe, efx->net_dev,
+ "Failed to read ESF_GZ_VSEC_TBL_OFF_HI, rc=%d\n",
+ rc);
+ return rc;
+ }
+ }
+
+ offset = (((u64)offset_lo) << ESE_GZ_VSEC_TBL_OFF_LO_BYTES_SHIFT) |
+ (((u64)offset_high) << ESE_GZ_VSEC_TBL_OFF_HI_BYTES_SHIFT);
+
+ if (offset > pci_resource_len(efx->pci_dev, bar) - sizeof(u32) * 2) {
+ netif_err(efx, probe, efx->net_dev,
+ "Xilinx table will overrun BAR[%d] offset=0x%llx\n",
+ bar, offset);
+ return -EINVAL;
+ }
+
+ /* Temporarily map BAR. */
+ rc = efx_init_io(efx, bar,
+ DMA_BIT_MASK(ESF_GZ_TX_SEND_ADDR_WIDTH),
+ pci_resource_len(efx->pci_dev, bar));
+ if (rc) {
+ netif_err(efx, probe, efx->net_dev,
+ "efx_init_io failed, rc=%d\n", rc);
+ return rc;
+ }
+
+ rc = ef100_pci_walk_xilinx_table(efx, offset, result);
+
+ /* Unmap temporarily mapped BAR. */
+ efx_fini_io(efx);
+ return rc;
+}
+
+/* Call ef100_pci_parse_ef100_entry() for each Xilinx PCI_EXT_CAP_ID_VNDR
+ * capability.
+ */
+static int ef100_pci_find_func_ctrl_window(struct efx_nic *efx,
+ struct ef100_func_ctl_window *result)
+{
+ int num_xilinx_caps = 0;
+ int cap = 0;
+
+ result->valid = false;
+
+ while ((cap = pci_find_next_ext_capability(efx->pci_dev, cap, PCI_EXT_CAP_ID_VNDR)) != 0) {
+ int vndr_cap = cap + PCI_EXT_CAP_HDR_LENGTH;
+ u32 vsec_ver = 0;
+ u32 vsec_len = 0;
+ u32 vsec_id = 0;
+ int rc = 0;
+
+ num_xilinx_caps++;
+
+ rc = ef100_pci_get_config_bits(efx, vndr_cap, ID, &vsec_id);
+ if (rc) {
+ netif_err(efx, probe, efx->net_dev,
+ "Failed to read ESF_GZ_VSEC_ID, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ rc = ef100_pci_get_config_bits(efx, vndr_cap, VER, &vsec_ver);
+ if (rc) {
+ netif_err(efx, probe, efx->net_dev,
+ "Failed to read ESF_GZ_VSEC_VER, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ /* Get length of whole capability - i.e. starting at cap */
+ rc = ef100_pci_get_config_bits(efx, vndr_cap, LEN, &vsec_len);
+ if (rc) {
+ netif_err(efx, probe, efx->net_dev,
+ "Failed to read ESF_GZ_VSEC_LEN, rc=%d\n",
+ rc);
+ return rc;
+ }
+
+ if (vsec_id == ESE_GZ_XILINX_VSEC_ID &&
+ vsec_ver == ESE_GZ_VSEC_VER_XIL_CFGBAR &&
+ vsec_len >= ESE_GZ_VSEC_LEN_MIN) {
+ bool has_offset_hi = (vsec_len >= ESE_GZ_VSEC_LEN_HIGH_OFFT);
+
+ rc = ef100_pci_parse_xilinx_cap(efx, vndr_cap,
+ has_offset_hi, result);
+ if (rc)
+ return rc;
+ }
+ }
+
+ if (num_xilinx_caps && !result->valid) {
+ netif_err(efx, probe, efx->net_dev,
+ "Seen %d Xilinx tables, but no EF100 entry.\n",
+ num_xilinx_caps);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/* Final NIC shutdown
+ * This is called only at module unload (or hotplug removal). A PF can call
+ * this on its VFs to ensure they are unbound first.
+ */
+static void ef100_pci_remove(struct pci_dev *pci_dev)
+{
+ struct efx_nic *efx;
+
+ efx = pci_get_drvdata(pci_dev);
+ if (!efx)
+ return;
+
+ rtnl_lock();
+ dev_close(efx->net_dev);
+ rtnl_unlock();
+
+ /* Unregistering our netdev notifier triggers unbinding of TC indirect
+ * blocks, so we have to do it before PCI removal.
+ */
+ unregister_netdevice_notifier(&efx->netdev_notifier);
+ ef100_remove(efx);
+ efx_fini_io(efx);
+ netif_dbg(efx, drv, efx->net_dev, "shutdown successful\n");
+
+ pci_set_drvdata(pci_dev, NULL);
+ efx_fini_struct(efx);
+ free_netdev(efx->net_dev);
+
+ pci_disable_pcie_error_reporting(pci_dev);
+};
+
+static int ef100_pci_probe(struct pci_dev *pci_dev,
+ const struct pci_device_id *entry)
+{
+ struct ef100_func_ctl_window fcw = { 0 };
+ struct net_device *net_dev;
+ struct efx_nic *efx;
+ int rc;
+
+ /* Allocate and initialise a struct net_device and struct efx_nic */
+ net_dev = alloc_etherdev_mq(sizeof(*efx), EFX_MAX_CORE_TX_QUEUES);
+ if (!net_dev)
+ return -ENOMEM;
+ efx = netdev_priv(net_dev);
+ efx->type = (const struct efx_nic_type *)entry->driver_data;
+
+ pci_set_drvdata(pci_dev, efx);
+ SET_NETDEV_DEV(net_dev, &pci_dev->dev);
+ rc = efx_init_struct(efx, pci_dev, net_dev);
+ if (rc)
+ goto fail;
+
+ efx->vi_stride = EF100_DEFAULT_VI_STRIDE;
+ netif_info(efx, probe, efx->net_dev,
+ "Solarflare EF100 NIC detected\n");
+
+ rc = ef100_pci_find_func_ctrl_window(efx, &fcw);
+ if (rc) {
+ netif_err(efx, probe, efx->net_dev,
+ "Error looking for ef100 function control window, rc=%d\n",
+ rc);
+ goto fail;
+ }
+
+ if (!fcw.valid) {
+ /* Extended capability not found - use defaults. */
+ fcw.bar = EFX_EF100_PCI_DEFAULT_BAR;
+ fcw.offset = 0;
+ fcw.valid = true;
+ }
+
+ if (fcw.offset > pci_resource_len(efx->pci_dev, fcw.bar) - ESE_GZ_FCW_LEN) {
+ netif_err(efx, probe, efx->net_dev,
+ "Func control window overruns BAR\n");
+ goto fail;
+ }
+
+ /* Set up basic I/O (BAR mappings etc) */
+ rc = efx_init_io(efx, fcw.bar,
+ DMA_BIT_MASK(ESF_GZ_TX_SEND_ADDR_WIDTH),
+ pci_resource_len(efx->pci_dev, fcw.bar));
+ if (rc)
+ goto fail;
+
+ efx->reg_base = fcw.offset;
+
+ efx->netdev_notifier.notifier_call = ef100_netdev_event;
+ rc = register_netdevice_notifier(&efx->netdev_notifier);
+ if (rc) {
+ netif_err(efx, probe, efx->net_dev,
+ "Failed to register netdevice notifier, rc=%d\n", rc);
+ goto fail;
+ }
+
+ rc = efx->type->probe(efx);
+ if (rc)
+ goto fail;
+
+ netif_dbg(efx, probe, efx->net_dev, "initialisation successful\n");
+
+ return 0;
+
+fail:
+ ef100_pci_remove(pci_dev);
+ return rc;
+}
+
+/* PCI device ID table */
+static const struct pci_device_id ef100_pci_table[] = {
+ {PCI_DEVICE(PCI_VENDOR_ID_XILINX, 0x0100), /* Riverhead PF */
+ .driver_data = (unsigned long) &ef100_pf_nic_type },
+ {0} /* end of list */
+};
+
+struct pci_driver ef100_pci_driver = {
+ .name = "sfc_ef100",
+ .id_table = ef100_pci_table,
+ .probe = ef100_pci_probe,
+ .remove = ef100_pci_remove,
+ .err_handler = &efx_err_handlers,
+};
+
+MODULE_DEVICE_TABLE(pci, ef100_pci_table);
diff --git a/drivers/net/ethernet/sfc/ef100.h b/drivers/net/ethernet/sfc/ef100.h
new file mode 100644
index 000000000000..a63f0ff6641b
--- /dev/null
+++ b/drivers/net/ethernet/sfc/ef100.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/****************************************************************************
+ * Driver for Solarflare network controllers and boards
+ * Copyright 2018 Solarflare Communications Inc.
+ * Copyright 2019-2020 Xilinx Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+extern struct pci_driver ef100_pci_driver;
diff --git a/drivers/net/ethernet/sfc/ef100_ethtool.c b/drivers/net/ethernet/sfc/ef100_ethtool.c
new file mode 100644
index 000000000000..729c425d0f78
--- /dev/null
+++ b/drivers/net/ethernet/sfc/ef100_ethtool.c
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/****************************************************************************
+ * Driver for Solarflare network controllers and boards
+ * Copyright 2018 Solarflare Communications Inc.
+ * Copyright 2019-2020 Xilinx Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+#include <linux/module.h>
+#include <linux/netdevice.h>
+#include "net_driver.h"
+#include "efx.h"
+#include "mcdi_port_common.h"
+#include "ethtool_common.h"
+#include "ef100_ethtool.h"
+#include "mcdi_functions.h"
+
+/* Ethtool options available
+ */
+const struct ethtool_ops ef100_ethtool_ops = {
+ .get_drvinfo = efx_ethtool_get_drvinfo,
+};
diff --git a/drivers/net/ethernet/sfc/ef100_ethtool.h b/drivers/net/ethernet/sfc/ef100_ethtool.h
new file mode 100644
index 000000000000..6efda72dfc6c
--- /dev/null
+++ b/drivers/net/ethernet/sfc/ef100_ethtool.h
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/****************************************************************************
+ * Driver for Solarflare network controllers and boards
+ * Copyright 2018 Solarflare Communications Inc.
+ * Copyright 2019-2020 Xilinx Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+extern const struct ethtool_ops ef100_ethtool_ops;
diff --git a/drivers/net/ethernet/sfc/ef100_netdev.c b/drivers/net/ethernet/sfc/ef100_netdev.c
new file mode 100644
index 000000000000..f900b375d9b1
--- /dev/null
+++ b/drivers/net/ethernet/sfc/ef100_netdev.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/****************************************************************************
+ * Driver for Solarflare network controllers and boards
+ * Copyright 2018 Solarflare Communications Inc.
+ * Copyright 2019-2020 Xilinx Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+#include "net_driver.h"
+#include "mcdi_port_common.h"
+#include "mcdi_functions.h"
+#include "efx_common.h"
+#include "efx_channels.h"
+#include "tx_common.h"
+#include "ef100_netdev.h"
+#include "ef100_ethtool.h"
+#include "efx_common.h"
+#include "nic_common.h"
+#include "ef100_nic.h"
+#include "ef100_tx.h"
+#include "ef100_regs.h"
+#include "mcdi_filters.h"
+#include "rx_common.h"
+
+static void ef100_update_name(struct efx_nic *efx)
+{
+ strcpy(efx->name, efx->net_dev->name);
+}
+
+/* Initiate a packet transmission. We use one channel per CPU
+ * (sharing when we have more CPUs than channels).
+ *
+ * Context: non-blocking.
+ * Note that returning anything other than NETDEV_TX_OK will cause the
+ * OS to free the skb.
+ */
+static netdev_tx_t ef100_hard_start_xmit(struct sk_buff *skb,
+ struct net_device *net_dev)
+{
+ struct efx_nic *efx = netdev_priv(net_dev);
+ struct efx_tx_queue *tx_queue;
+ struct efx_channel *channel;
+ int rc;
+
+ channel = efx_get_tx_channel(efx, skb_get_queue_mapping(skb));
+ netif_vdbg(efx, tx_queued, efx->net_dev,
+ "%s len %d data %d channel %d\n", __func__,
+ skb->len, skb->data_len, channel->channel);
+ if (!efx->n_channels || !efx->n_tx_channels || !channel) {
+ netif_stop_queue(net_dev);
+ goto err;
+ }
+
+ tx_queue = &channel->tx_queue[0];
+ rc = ef100_enqueue_skb(tx_queue, skb);
+ if (rc == 0)
+ return NETDEV_TX_OK;
+
+err:
+ net_dev->stats.tx_dropped++;
+ return NETDEV_TX_OK;
+}
+
+static const struct net_device_ops ef100_netdev_ops = {
+ .ndo_start_xmit = ef100_hard_start_xmit,
+};
+
+/* Netdev registration
+ */
+int ef100_netdev_event(struct notifier_block *this,
+ unsigned long event, void *ptr)
+{
+ struct efx_nic *efx = container_of(this, struct efx_nic, netdev_notifier);
+ struct net_device *net_dev = netdev_notifier_info_to_dev(ptr);
+
+ if (netdev_priv(net_dev) == efx && event == NETDEV_CHANGENAME)
+ ef100_update_name(efx);
+
+ return NOTIFY_DONE;
+}
+
+int ef100_register_netdev(struct efx_nic *efx)
+{
+ struct net_device *net_dev = efx->net_dev;
+ int rc;
+
+ net_dev->watchdog_timeo = 5 * HZ;
+ net_dev->irq = efx->pci_dev->irq;
+ net_dev->netdev_ops = &ef100_netdev_ops;
+ net_dev->min_mtu = EFX_MIN_MTU;
+ net_dev->max_mtu = EFX_MAX_MTU;
+ net_dev->ethtool_ops = &ef100_ethtool_ops;
+
+ rtnl_lock();
+
+ rc = dev_alloc_name(net_dev, net_dev->name);
+ if (rc < 0)
+ goto fail_locked;
+ ef100_update_name(efx);
+
+ rc = register_netdevice(net_dev);
+ if (rc)
+ goto fail_locked;
+
+ /* Always start with carrier off; PHY events will detect the link */
+ netif_carrier_off(net_dev);
+
+ efx->state = STATE_READY;
+ rtnl_unlock();
+ efx_init_mcdi_logging(efx);
+
+ return 0;
+
+fail_locked:
+ rtnl_unlock();
+ netif_err(efx, drv, efx->net_dev, "could not register net dev\n");
+ return rc;
+}
+
+void ef100_unregister_netdev(struct efx_nic *efx)
+{
+ if (efx_dev_registered(efx)) {
+ efx_fini_mcdi_logging(efx);
+ efx->state = STATE_UNINIT;
+ unregister_netdev(efx->net_dev);
+ }
+}
diff --git a/drivers/net/ethernet/sfc/ef100_netdev.h b/drivers/net/ethernet/sfc/ef100_netdev.h
new file mode 100644
index 000000000000..d40abb7cc086
--- /dev/null
+++ b/drivers/net/ethernet/sfc/ef100_netdev.h
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/****************************************************************************
+ * Driver for Solarflare network controllers and boards
+ * Copyright 2018 Solarflare Communications Inc.
+ * Copyright 2019-2020 Xilinx Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include <linux/netdevice.h>
+
+int ef100_netdev_event(struct notifier_block *this,
+ unsigned long event, void *ptr);
+int ef100_register_netdev(struct efx_nic *efx);
+void ef100_unregister_netdev(struct efx_nic *efx);
diff --git a/drivers/net/ethernet/sfc/ef100_nic.c b/drivers/net/ethernet/sfc/ef100_nic.c
new file mode 100644
index 000000000000..7bb304bc148e
--- /dev/null
+++ b/drivers/net/ethernet/sfc/ef100_nic.c
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/****************************************************************************
+ * Driver for Solarflare network controllers and boards
+ * Copyright 2018 Solarflare Communications Inc.
+ * Copyright 2019-2020 Xilinx Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published
+ * by the Free Software Foundation, incorporated herein by reference.
+ */
+
+#include "ef100_nic.h"
+#include "efx_common.h"
+#include "efx_channels.h"
+#include "io.h"
+#include "selftest.h"
+#include "ef100_regs.h"
+#include "mcdi.h"
+#include "mcdi_pcol.h"
+#include "mcdi_port_common.h"
+#include "mcdi_functions.h"
+#include "mcdi_filters.h"
+#include "ef100_rx.h"
+#include