summaryrefslogtreecommitdiffstats
path: root/src/drivers
diff options
context:
space:
mode:
authorSimon Rettberg2019-08-02 10:04:22 +0200
committerSimon Rettberg2019-08-02 10:04:22 +0200
commitbebc4ff01ee9ca2b042cf165b56bf9d3b5cb5333 (patch)
tree6a7538ee3a4e5daf2d98d76b47e95b859c5aa265 /src/drivers
parent[efi] Add very ugly hack to use HW RNG on EFI (diff)
parent[build] Do not apply WORKAROUND_CFLAGS for host compiler (diff)
downloadipxe-bebc4ff01ee9ca2b042cf165b56bf9d3b5cb5333.tar.gz
ipxe-bebc4ff01ee9ca2b042cf165b56bf9d3b5cb5333.tar.xz
ipxe-bebc4ff01ee9ca2b042cf165b56bf9d3b5cb5333.zip
Merge branch 'master' into openslx
Diffstat (limited to 'src/drivers')
-rw-r--r--src/drivers/bus/pcimsix.c251
-rwxr-xr-xsrc/drivers/infiniband/golan.c3
-rw-r--r--src/drivers/infiniband/hermon.c18
-rw-r--r--src/drivers/net/intel.c8
-rw-r--r--src/drivers/net/intelxl.c768
-rw-r--r--src/drivers/net/intelxl.h373
-rw-r--r--src/drivers/net/intelxlvf.c719
-rw-r--r--src/drivers/net/intelxlvf.h86
-rw-r--r--src/drivers/net/lan78xx.c4
-rw-r--r--src/drivers/net/smsc95xx.c4
-rw-r--r--src/drivers/net/smscusb.c34
-rw-r--r--src/drivers/net/smscusb.h1
12 files changed, 1989 insertions, 280 deletions
diff --git a/src/drivers/bus/pcimsix.c b/src/drivers/bus/pcimsix.c
new file mode 100644
index 000000000..80893c418
--- /dev/null
+++ b/src/drivers/bus/pcimsix.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (C) 2019 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <errno.h>
+#include <assert.h>
+#include <ipxe/pci.h>
+#include <ipxe/pcimsix.h>
+
+/** @file
+ *
+ * PCI MSI-X interrupts
+ *
+ */
+
+/**
+ * Get MSI-X descriptor name (for debugging)
+ *
+ * @v cfg Configuration space offset
+ * @ret name Descriptor name
+ */
+static const char * pci_msix_name ( unsigned int cfg ) {
+
+ switch ( cfg ) {
+ case PCI_MSIX_DESC_TABLE: return "table";
+ case PCI_MSIX_DESC_PBA: return "PBA";
+ default: return "<UNKNOWN>";
+ }
+}
+
+/**
+ * Map MSI-X BAR portion
+ *
+ * @v pci PCI device
+ * @v msix MSI-X capability
+ * @v cfg Configuration space offset
+ * @ret io I/O address
+ */
+static void * pci_msix_ioremap ( struct pci_device *pci, struct pci_msix *msix,
+ unsigned int cfg ) {
+ uint32_t desc;
+ unsigned int bar;
+ unsigned long start;
+ unsigned long offset;
+ unsigned long base;
+ void *io;
+
+ /* Read descriptor */
+ pci_read_config_dword ( pci, ( msix->cap + cfg ), &desc );
+
+ /* Get BAR */
+ bar = PCI_MSIX_DESC_BIR ( desc );
+ offset = PCI_MSIX_DESC_OFFSET ( desc );
+ start = pci_bar_start ( pci, PCI_BASE_ADDRESS ( bar ) );
+ if ( ! start ) {
+ DBGC ( msix, "MSI-X %p %s could not find BAR%d\n",
+ msix, pci_msix_name ( cfg ), bar );
+ return NULL;
+ }
+ base = ( start + offset );
+ DBGC ( msix, "MSI-X %p %s at %#08lx (BAR%d+%#lx)\n",
+ msix, pci_msix_name ( cfg ), base, bar, offset );
+
+ /* Map BAR portion */
+ io = ioremap ( ( start + offset ), PCI_MSIX_LEN );
+ if ( ! io ) {
+ DBGC ( msix, "MSI-X %p %s could not map %#08lx\n",
+ msix, pci_msix_name ( cfg ), base );
+ return NULL;
+ }
+
+ return io;
+}
+
+/**
+ * Enable MSI-X interrupts
+ *
+ * @v pci PCI device
+ * @v msix MSI-X capability
+ * @ret rc Return status code
+ */
+int pci_msix_enable ( struct pci_device *pci, struct pci_msix *msix ) {
+ uint16_t ctrl;
+ int rc;
+
+ /* Locate capability */
+ msix->cap = pci_find_capability ( pci, PCI_CAP_ID_MSIX );
+ if ( ! msix->cap ) {
+ DBGC ( msix, "MSI-X %p found no MSI-X capability in "
+ PCI_FMT "\n", msix, PCI_ARGS ( pci ) );
+ rc = -ENOENT;
+ goto err_cap;
+ }
+
+ /* Extract interrupt count */
+ pci_read_config_word ( pci, ( msix->cap + PCI_MSIX_CTRL ), &ctrl );
+ msix->count = ( PCI_MSIX_CTRL_SIZE ( ctrl ) + 1 );
+ DBGC ( msix, "MSI-X %p has %d vectors for " PCI_FMT "\n",
+ msix, msix->count, PCI_ARGS ( pci ) );
+
+ /* Map MSI-X table */
+ msix->table = pci_msix_ioremap ( pci, msix, PCI_MSIX_DESC_TABLE );
+ if ( ! msix->table ) {
+ rc = -ENOENT;
+ goto err_table;
+ }
+
+ /* Map pending bit array */
+ msix->pba = pci_msix_ioremap ( pci, msix, PCI_MSIX_DESC_PBA );
+ if ( ! msix->pba ) {
+ rc = -ENOENT;
+ goto err_pba;
+ }
+
+ /* Enable MSI-X */
+ ctrl &= ~PCI_MSIX_CTRL_MASK;
+ ctrl |= PCI_MSIX_CTRL_ENABLE;
+ pci_write_config_word ( pci, ( msix->cap + PCI_MSIX_CTRL ), ctrl );
+
+ return 0;
+
+ iounmap ( msix->pba );
+ err_pba:
+ iounmap ( msix->table );
+ err_table:
+ err_cap:
+ return rc;
+}
+
+/**
+ * Disable MSI-X interrupts
+ *
+ * @v pci PCI device
+ * @v msix MSI-X capability
+ */
+void pci_msix_disable ( struct pci_device *pci, struct pci_msix *msix ) {
+ uint16_t ctrl;
+
+ /* Disable MSI-X */
+ pci_read_config_word ( pci, ( msix->cap + PCI_MSIX_CTRL ), &ctrl );
+ ctrl &= ~PCI_MSIX_CTRL_ENABLE;
+ pci_write_config_word ( pci, ( msix->cap + PCI_MSIX_CTRL ), ctrl );
+
+ /* Unmap pending bit array */
+ iounmap ( msix->pba );
+
+ /* Unmap MSI-X table */
+ iounmap ( msix->table );
+}
+
+/**
+ * Map MSI-X interrupt vector
+ *
+ * @v msix MSI-X capability
+ * @v vector MSI-X vector
+ * @v address Message address
+ * @v data Message data
+ */
+void pci_msix_map ( struct pci_msix *msix, unsigned int vector,
+ physaddr_t address, uint32_t data ) {
+ void *base;
+
+ /* Sanity check */
+ assert ( vector < msix->count );
+
+ /* Map interrupt vector */
+ base = ( msix->table + PCI_MSIX_VECTOR ( vector ) );
+ writel ( ( address & 0xffffffffUL ), ( base + PCI_MSIX_ADDRESS_LO ) );
+ if ( sizeof ( address ) > sizeof ( uint32_t ) ) {
+ writel ( ( ( ( uint64_t ) address ) >> 32 ),
+ ( base + PCI_MSIX_ADDRESS_HI ) );
+ } else {
+ writel ( 0, ( base + PCI_MSIX_ADDRESS_HI ) );
+ }
+ writel ( data, ( base + PCI_MSIX_DATA ) );
+}
+
+/**
+ * Control MSI-X interrupt vector
+ *
+ * @v msix MSI-X capability
+ * @v vector MSI-X vector
+ * @v mask Control mask
+ */
+void pci_msix_control ( struct pci_msix *msix, unsigned int vector,
+ uint32_t mask ) {
+ void *base;
+ uint32_t ctrl;
+
+ /* Mask/unmask interrupt vector */
+ base = ( msix->table + PCI_MSIX_VECTOR ( vector ) );
+ ctrl = readl ( base + PCI_MSIX_CONTROL );
+ ctrl &= ~PCI_MSIX_CONTROL_MASK;
+ ctrl |= mask;
+ writel ( ctrl, ( base + PCI_MSIX_CONTROL ) );
+}
+
+/**
+ * Dump MSI-X interrupt state (for debugging)
+ *
+ * @v msix MSI-X capability
+ * @v vector MSI-X vector
+ */
+void pci_msix_dump ( struct pci_msix *msix, unsigned int vector ) {
+ void *base;
+ uint32_t address_hi;
+ uint32_t address_lo;
+ physaddr_t address;
+ uint32_t data;
+ uint32_t ctrl;
+ uint32_t pba;
+
+ /* Do nothing in non-debug builds */
+ if ( ! DBG_LOG )
+ return;
+
+ /* Mask/unmask interrupt vector */
+ base = ( msix->table + PCI_MSIX_VECTOR ( vector ) );
+ address_hi = readl ( base + PCI_MSIX_ADDRESS_HI );
+ address_lo = readl ( base + PCI_MSIX_ADDRESS_LO );
+ data = readl ( base + PCI_MSIX_DATA );
+ ctrl = readl ( base + PCI_MSIX_CONTROL );
+ pba = readl ( msix->pba );
+ address = ( ( ( ( uint64_t ) address_hi ) << 32 ) | address_lo );
+ DBGC ( msix, "MSI-X %p vector %d %#08x => %#08lx%s%s\n",
+ msix, vector, data, address,
+ ( ( ctrl & PCI_MSIX_CONTROL_MASK ) ? " (masked)" : "" ),
+ ( ( pba & ( 1 << vector ) ) ? " (pending)" : "" ) );
+}
diff --git a/src/drivers/infiniband/golan.c b/src/drivers/infiniband/golan.c
index 18ebfb1e9..e96ba2698 100755
--- a/src/drivers/infiniband/golan.c
+++ b/src/drivers/infiniband/golan.c
@@ -2640,6 +2640,9 @@ static struct pci_device_id golan_nics[] = {
PCI_ROM ( 0x15b3, 0x1015, "ConnectX-4Lx", "ConnectX-4Lx HCA driver, DevID 4117", 0 ),
PCI_ROM ( 0x15b3, 0x1017, "ConnectX-5", "ConnectX-5 HCA driver, DevID 4119", 0 ),
PCI_ROM ( 0x15b3, 0x1019, "ConnectX-5EX", "ConnectX-5EX HCA driver, DevID 4121", 0 ),
+ PCI_ROM ( 0x15b3, 0x101b, "ConnectX-6", "ConnectX-6 HCA driver, DevID 4123", 0 ),
+ PCI_ROM ( 0x15b3, 0x101d, "ConnectX-6DX", "ConnectX-6DX HCA driver, DevID 4125", 0 ),
+ PCI_ROM ( 0x15b3, 0xa2d2, "BlueField", "BlueField integrated ConnectX-5 network controller HCA driver, DevID 41682", 0 ),
};
struct pci_driver golan_driver __pci_driver = {
diff --git a/src/drivers/infiniband/hermon.c b/src/drivers/infiniband/hermon.c
index a1d2a3bd5..9675c156b 100644
--- a/src/drivers/infiniband/hermon.c
+++ b/src/drivers/infiniband/hermon.c
@@ -3207,22 +3207,16 @@ static void hermon_eth_complete_recv ( struct ib_device *ibdev __unused,
struct ib_address_vector *source,
struct io_buffer *iobuf, int rc ) {
struct net_device *netdev = ib_qp_get_ownerdata ( qp );
- struct net_device *vlan;
-
- /* Find VLAN device, if applicable */
- if ( source->vlan_present ) {
- if ( ( vlan = vlan_find ( netdev, source->vlan ) ) != NULL ) {
- netdev = vlan;
- } else if ( rc == 0 ) {
- rc = -ENODEV;
- }
- }
+ unsigned int tag;
+
+ /* Identify VLAN tag, if applicable */
+ tag = ( source->vlan_present ? source->vlan : 0 );
/* Hand off to network layer */
if ( rc == 0 ) {
- netdev_rx ( netdev, iobuf );
+ vlan_netdev_rx ( netdev, tag, iobuf );
} else {
- netdev_rx_err ( netdev, iobuf, rc );
+ vlan_netdev_rx_err ( netdev, tag, iobuf, rc );
}
}
diff --git a/src/drivers/net/intel.c b/src/drivers/net/intel.c
index a2e6d535b..bb0b673b9 100644
--- a/src/drivers/net/intel.c
+++ b/src/drivers/net/intel.c
@@ -1146,9 +1146,17 @@ static struct pci_device_id intel_nics[] = {
PCI_ROM ( 0x8086, 0x15b7, "i219lm-2", "I219-LM (2)", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x15b8, "i219v-2", "I219-V (2)", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x15b9, "i219lm-3", "I219-LM (3)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x15bb, "i219lm-7", "I219-LM (7)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x15bc, "i219v-7", "I219-V (7)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x15bd, "i219lm-6", "I219-LM (6)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x15be, "i219v-6", "I219-V (6)", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x15d6, "i219v-5", "I219-V (5)", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x15d7, "i219lm-4", "I219-LM (4)", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x15d8, "i219v-4", "I219-V (4)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x15df, "i219lm-8", "I219-LM (8)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x15e0, "i219v-8", "I219-V (8)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x15e1, "i219lm-9", "I219-LM (9)", INTEL_I219 ),
+ PCI_ROM ( 0x8086, 0x15e2, "i219v-9", "I219-V (9)", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x15e3, "i219lm-5", "I219-LM (5)", INTEL_I219 ),
PCI_ROM ( 0x8086, 0x1f41, "i354", "I354", INTEL_NO_ASDE ),
PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ),
diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c
index 3e40fa4ae..c98ba265c 100644
--- a/src/drivers/net/intelxl.c
+++ b/src/drivers/net/intelxl.c
@@ -32,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/netdevice.h>
#include <ipxe/ethernet.h>
#include <ipxe/if_ether.h>
+#include <ipxe/vlan.h>
#include <ipxe/iobuf.h>
#include <ipxe/malloc.h>
#include <ipxe/pci.h>
@@ -117,98 +118,239 @@ static int intelxl_fetch_mac ( struct intelxl_nic *intelxl,
/******************************************************************************
*
+ * MSI-X interrupts
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Enable MSI-X dummy interrupt
+ *
+ * @v intelxl Intel device
+ * @v pci PCI device
+ * @ret rc Return status code
+ */
+int intelxl_msix_enable ( struct intelxl_nic *intelxl,
+ struct pci_device *pci ) {
+ int rc;
+
+ /* Enable MSI-X capability */
+ if ( ( rc = pci_msix_enable ( pci, &intelxl->msix ) ) != 0 ) {
+ DBGC ( intelxl, "INTELXL %p could not enable MSI-X: %s\n",
+ intelxl, strerror ( rc ) );
+ return rc;
+ }
+
+ /* Configure interrupt zero to write to dummy location */
+ pci_msix_map ( &intelxl->msix, 0, virt_to_bus ( &intelxl->msg ), 0 );
+
+ /* Enable dummy interrupt zero */
+ pci_msix_unmask ( &intelxl->msix, 0 );
+
+ return 0;
+}
+
+/**
+ * Disable MSI-X dummy interrupt
+ *
+ * @v intelxl Intel device
+ * @v pci PCI device
+ */
+void intelxl_msix_disable ( struct intelxl_nic *intelxl,
+ struct pci_device *pci ) {
+
+ /* Disable dummy interrupt zero */
+ pci_msix_mask ( &intelxl->msix, 0 );
+
+ /* Disable MSI-X capability */
+ pci_msix_disable ( pci, &intelxl->msix );
+}
+
+/******************************************************************************
+ *
* Admin queue
*
******************************************************************************
*/
+/** Admin queue register offsets */
+static const struct intelxl_admin_offsets intelxl_admin_offsets = {
+ .bal = INTELXL_ADMIN_BAL,
+ .bah = INTELXL_ADMIN_BAH,
+ .len = INTELXL_ADMIN_LEN,
+ .head = INTELXL_ADMIN_HEAD,
+ .tail = INTELXL_ADMIN_TAIL,
+};
+
/**
- * Create admin queue
+ * Allocate admin queue
*
* @v intelxl Intel device
* @v admin Admin queue
* @ret rc Return status code
*/
-static int intelxl_create_admin ( struct intelxl_nic *intelxl,
- struct intelxl_admin *admin ) {
+static int intelxl_alloc_admin ( struct intelxl_nic *intelxl,
+ struct intelxl_admin *admin ) {
+ size_t buf_len = ( sizeof ( admin->buf[0] ) * INTELXL_ADMIN_NUM_DESC );
size_t len = ( sizeof ( admin->desc[0] ) * INTELXL_ADMIN_NUM_DESC );
- void *admin_regs = ( intelxl->regs + admin->reg );
- physaddr_t address;
/* Allocate admin queue */
- admin->desc = malloc_dma ( ( len + sizeof ( *admin->buffer ) ),
- INTELXL_ALIGN );
- if ( ! admin->desc )
+ admin->buf = malloc_dma ( ( buf_len + len ), INTELXL_ALIGN );
+ if ( ! admin->buf )
return -ENOMEM;
- admin->buffer = ( ( ( void * ) admin->desc ) + len );
+ admin->desc = ( ( ( void * ) admin->buf ) + buf_len );
+
+ DBGC ( intelxl, "INTELXL %p A%cQ is at [%08llx,%08llx) buf "
+ "[%08llx,%08llx)\n", intelxl,
+ ( ( admin == &intelxl->command ) ? 'T' : 'R' ),
+ ( ( unsigned long long ) virt_to_bus ( admin->desc ) ),
+ ( ( unsigned long long ) ( virt_to_bus ( admin->desc ) + len ) ),
+ ( ( unsigned long long ) virt_to_bus ( admin->buf ) ),
+ ( ( unsigned long long ) ( virt_to_bus ( admin->buf ) +
+ buf_len ) ) );
+ return 0;
+}
+
+/**
+ * Enable admin queue
+ *
+ * @v intelxl Intel device
+ * @v admin Admin queue
+ */
+static void intelxl_enable_admin ( struct intelxl_nic *intelxl,
+ struct intelxl_admin *admin ) {
+ size_t len = ( sizeof ( admin->desc[0] ) * INTELXL_ADMIN_NUM_DESC );
+ const struct intelxl_admin_offsets *regs = admin->regs;
+ void *admin_regs = ( intelxl->regs + admin->base );
+ physaddr_t address;
/* Initialise admin queue */
memset ( admin->desc, 0, len );
/* Reset head and tail registers */
- writel ( 0, admin_regs + INTELXL_ADMIN_HEAD );
- writel ( 0, admin_regs + INTELXL_ADMIN_TAIL );
+ writel ( 0, admin_regs + regs->head );
+ writel ( 0, admin_regs + regs->tail );
/* Reset queue index */
admin->index = 0;
/* Program queue address */
address = virt_to_bus ( admin->desc );
- writel ( ( address & 0xffffffffUL ), admin_regs + INTELXL_ADMIN_BAL );
+ writel ( ( address & 0xffffffffUL ), admin_regs + regs->bal );
if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) {
writel ( ( ( ( uint64_t ) address ) >> 32 ),
- admin_regs + INTELXL_ADMIN_BAH );
+ admin_regs + regs->bah );
} else {
- writel ( 0, admin_regs + INTELXL_ADMIN_BAH );
+ writel ( 0, admin_regs + regs->bah );
}
/* Program queue length and enable queue */
writel ( ( INTELXL_ADMIN_LEN_LEN ( INTELXL_ADMIN_NUM_DESC ) |
INTELXL_ADMIN_LEN_ENABLE ),
- admin_regs + INTELXL_ADMIN_LEN );
-
- DBGC ( intelxl, "INTELXL %p A%cQ is at [%08llx,%08llx) buf "
- "[%08llx,%08llx)\n", intelxl,
- ( ( admin->reg == INTELXL_ADMIN_CMD ) ? 'T' : 'R' ),
- ( ( unsigned long long ) address ),
- ( ( unsigned long long ) address + len ),
- ( ( unsigned long long ) virt_to_bus ( admin->buffer ) ),
- ( ( unsigned long long ) ( virt_to_bus ( admin->buffer ) +
- sizeof ( admin->buffer[0] ) ) ) );
- return 0;
+ admin_regs + regs->len );
}
/**
- * Destroy admin queue
+ * Disable admin queue
*
* @v intelxl Intel device
* @v admin Admin queue
*/
-static void intelxl_destroy_admin ( struct intelxl_nic *intelxl,
+static void intelxl_disable_admin ( struct intelxl_nic *intelxl,
struct intelxl_admin *admin ) {
- size_t len = ( sizeof ( admin->desc[0] ) * INTELXL_ADMIN_NUM_DESC );
- void *admin_regs = ( intelxl->regs + admin->reg );
+ const struct intelxl_admin_offsets *regs = admin->regs;
+ void *admin_regs = ( intelxl->regs + admin->base );
/* Disable queue */
- writel ( 0, admin_regs + INTELXL_ADMIN_LEN );
+ writel ( 0, admin_regs + regs->len );
+}
+
+/**
+ * Free admin queue
+ *
+ * @v intelxl Intel device
+ * @v admin Admin queue
+ */
+static void intelxl_free_admin ( struct intelxl_nic *intelxl __unused,
+ struct intelxl_admin *admin ) {
+ size_t buf_len = ( sizeof ( admin->buf[0] ) * INTELXL_ADMIN_NUM_DESC );
+ size_t len = ( sizeof ( admin->desc[0] ) * INTELXL_ADMIN_NUM_DESC );
/* Free queue */
- free_dma ( admin->desc, ( len + sizeof ( *admin->buffer ) ) );
+ free_dma ( admin->buf, ( buf_len + len ) );
+}
+
+/**
+ * Get next admin command queue descriptor
+ *
+ * @v intelxl Intel device
+ * @ret cmd Command descriptor
+ */
+struct intelxl_admin_descriptor *
+intelxl_admin_command_descriptor ( struct intelxl_nic *intelxl ) {
+ struct intelxl_admin *admin = &intelxl->command;
+ struct intelxl_admin_descriptor *cmd;
+
+ /* Get and initialise next descriptor */
+ cmd = &admin->desc[ admin->index % INTELXL_ADMIN_NUM_DESC ];
+ memset ( cmd, 0, sizeof ( *cmd ) );
+ return cmd;
+}
+
+/**
+ * Get next admin command queue data buffer
+ *
+ * @v intelxl Intel device
+ * @ret buf Data buffer
+ */
+union intelxl_admin_buffer *
+intelxl_admin_command_buffer ( struct intelxl_nic *intelxl ) {
+ struct intelxl_admin *admin = &intelxl->command;
+ union intelxl_admin_buffer *buf;
+
+ /* Get next data buffer */
+ buf = &admin->buf[ admin->index % INTELXL_ADMIN_NUM_DESC ];
+ memset ( buf, 0, sizeof ( *buf ) );
+ return buf;
+}
+
+/**
+ * Initialise admin event queue descriptor
+ *
+ * @v intelxl Intel device
+ * @v index Event queue index
+ */
+static void intelxl_admin_event_init ( struct intelxl_nic *intelxl,
+ unsigned int index ) {
+ struct intelxl_admin *admin = &intelxl->event;
+ struct intelxl_admin_descriptor *evt;
+ union intelxl_admin_buffer *buf;
+ uint64_t address;
+
+ /* Initialise descriptor */
+ evt = &admin->desc[ index % INTELXL_ADMIN_NUM_DESC ];
+ buf = &admin->buf[ index % INTELXL_ADMIN_NUM_DESC ];
+ address = virt_to_bus ( buf );
+ evt->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF );
+ evt->len = cpu_to_le16 ( sizeof ( *buf ) );
+ evt->params.buffer.high = cpu_to_le32 ( address >> 32 );
+ evt->params.buffer.low = cpu_to_le32 ( address & 0xffffffffUL );
}
/**
* Issue admin queue command
*
* @v intelxl Intel device
- * @v cmd Command descriptor
* @ret rc Return status code
*/
-static int intelxl_admin_command ( struct intelxl_nic *intelxl,
- struct intelxl_admin_descriptor *cmd ) {
+int intelxl_admin_command ( struct intelxl_nic *intelxl ) {
struct intelxl_admin *admin = &intelxl->command;
- void *admin_regs = ( intelxl->regs + admin->reg );
- struct intelxl_admin_descriptor *desc;
- uint64_t buffer;
+ const struct intelxl_admin_offsets *regs = admin->regs;
+ void *admin_regs = ( intelxl->regs + admin->base );
+ struct intelxl_admin_descriptor *cmd;
+ union intelxl_admin_buffer *buf;
+ uint64_t address;
+ uint32_t cookie;
unsigned int index;
unsigned int tail;
unsigned int i;
@@ -217,69 +359,74 @@ static int intelxl_admin_command ( struct intelxl_nic *intelxl,
/* Get next queue entry */
index = admin->index++;
tail = ( admin->index % INTELXL_ADMIN_NUM_DESC );
- desc = &admin->desc[index % INTELXL_ADMIN_NUM_DESC];
-
- /* Clear must-be-zero flags */
- cmd->flags &= ~cpu_to_le16 ( INTELXL_ADMIN_FL_DD |
- INTELXL_ADMIN_FL_CMP |
- INTELXL_ADMIN_FL_ERR );
-
- /* Clear return value */
- cmd->ret = 0;
-
- /* Populate cookie */
- cmd->cookie = cpu_to_le32 ( index );
+ cmd = &admin->desc[ index % INTELXL_ADMIN_NUM_DESC ];
+ buf = &admin->buf[ index % INTELXL_ADMIN_NUM_DESC ];
+ DBGC2 ( intelxl, "INTELXL %p admin command %#x opcode %#04x",
+ intelxl, index, le16_to_cpu ( cmd->opcode ) );
+ if ( cmd->vopcode )
+ DBGC2 ( intelxl, "/%#08x", le32_to_cpu ( cmd->vopcode ) );
+ DBGC2 ( intelxl, ":\n" );
+
+ /* Sanity checks */
+ assert ( ! ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_DD ) ) );
+ assert ( ! ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_CMP ) ) );
+ assert ( ! ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_ERR ) ) );
+ assert ( cmd->ret == 0 );
/* Populate data buffer address if applicable */
if ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ) ) {
- buffer = virt_to_bus ( admin->buffer );
- cmd->params.buffer.high = cpu_to_le32 ( buffer >> 32 );
- cmd->params.buffer.low = cpu_to_le32 ( buffer & 0xffffffffUL );
+ address = virt_to_bus ( buf );
+ cmd->params.buffer.high = cpu_to_le32 ( address >> 32 );
+ cmd->params.buffer.low = cpu_to_le32 ( address & 0xffffffffUL );
}
- /* Copy command descriptor to queue entry */
- memcpy ( desc, cmd, sizeof ( *desc ) );
- DBGC2 ( intelxl, "INTELXL %p admin command %#x:\n", intelxl, index );
- DBGC2_HDA ( intelxl, virt_to_phys ( desc ), desc, sizeof ( *desc ) );
+ /* Populate cookie, if not being (ab)used for VF opcode */
+ if ( ! cmd->vopcode )
+ cmd->cookie = cpu_to_le32 ( index );
+
+ /* Record cookie */
+ cookie = cmd->cookie;
/* Post command descriptor */
+ DBGC2_HDA ( intelxl, virt_to_phys ( cmd ), cmd, sizeof ( *cmd ) );
+ if ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ) ) {
+ DBGC2_HDA ( intelxl, virt_to_phys ( buf ), buf,
+ le16_to_cpu ( cmd->len ) );
+ }
wmb();
- writel ( tail, admin_regs + INTELXL_ADMIN_TAIL );
+ writel ( tail, admin_regs + regs->tail );
/* Wait for completion */
for ( i = 0 ; i < INTELXL_ADMIN_MAX_WAIT_MS ; i++ ) {
/* If response is not complete, delay 1ms and retry */
- if ( ! ( desc->flags & INTELXL_ADMIN_FL_DD ) ) {
+ if ( ! ( cmd->flags & INTELXL_ADMIN_FL_DD ) ) {
mdelay ( 1 );
continue;
}
DBGC2 ( intelxl, "INTELXL %p admin command %#x response:\n",
intelxl, index );
- DBGC2_HDA ( intelxl, virt_to_phys ( desc ), desc,
- sizeof ( *desc ) );
+ DBGC2_HDA ( intelxl, virt_to_phys ( cmd ), cmd,
+ sizeof ( *cmd ) );
/* Check for cookie mismatch */
- if ( desc->cookie != cmd->cookie ) {
+ if ( cmd->cookie != cookie ) {
DBGC ( intelxl, "INTELXL %p admin command %#x bad "
"cookie %#x\n", intelxl, index,
- le32_to_cpu ( desc->cookie ) );
+ le32_to_cpu ( cmd->cookie ) );
rc = -EPROTO;
goto err;
}
/* Check for errors */
- if ( desc->ret != 0 ) {
+ if ( cmd->ret != 0 ) {
DBGC ( intelxl, "INTELXL %p admin command %#x error "
"%d\n", intelxl, index,
- le16_to_cpu ( desc->ret ) );
+ le16_to_cpu ( cmd->ret ) );
rc = -EIO;
goto err;
}
- /* Copy response back to command descriptor */
- memcpy ( cmd, desc, sizeof ( *cmd ) );
-
/* Success */
return 0;
}
@@ -288,8 +435,7 @@ static int intelxl_admin_command ( struct intelxl_nic *intelxl,
DBGC ( intelxl, "INTELXL %p timed out waiting for admin command %#x:\n",
intelxl, index );
err:
- DBGC_HDA ( intelxl, virt_to_phys ( desc ), cmd, sizeof ( *cmd ) );
- DBGC_HDA ( intelxl, virt_to_phys ( desc ), desc, sizeof ( *desc ) );
+ DBGC_HDA ( intelxl, virt_to_phys ( cmd ), cmd, sizeof ( *cmd ) );
return rc;
}
@@ -300,17 +446,18 @@ static int intelxl_admin_command ( struct intelxl_nic *intelxl,
* @ret rc Return status code
*/
static int intelxl_admin_version ( struct intelxl_nic *intelxl ) {
- struct intelxl_admin_descriptor cmd;
- struct intelxl_admin_version_params *version = &cmd.params.version;
+ struct intelxl_admin_descriptor *cmd;
+ struct intelxl_admin_version_params *version;
unsigned int api;
int rc;
/* Populate descriptor */
- memset ( &cmd, 0, sizeof ( cmd ) );
- cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_VERSION );
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_VERSION );
+ version = &cmd->params.version;
/* Issue command */
- if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 )
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
return rc;
api = le16_to_cpu ( version->api.major );
DBGC ( intelxl, "INTELXL %p firmware v%d.%d API v%d.%d\n",
@@ -335,24 +482,25 @@ static int intelxl_admin_version ( struct intelxl_nic *intelxl ) {
* @ret rc Return status code
*/
static int intelxl_admin_driver ( struct intelxl_nic *intelxl ) {
- struct intelxl_admin_descriptor cmd;
- struct intelxl_admin_driver_params *driver = &cmd.params.driver;
- struct intelxl_admin_driver_buffer *buf =
- &intelxl->command.buffer->driver;
+ struct intelxl_admin_descriptor *cmd;
+ struct intelxl_admin_driver_params *driver;
+ union intelxl_admin_buffer *buf;
int rc;
/* Populate descriptor */
- memset ( &cmd, 0, sizeof ( cmd ) );
- cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_DRIVER );
- cmd.flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
- cmd.len = cpu_to_le16 ( sizeof ( *buf ) );
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_DRIVER );
+ cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
+ cmd->len = cpu_to_le16 ( sizeof ( buf->driver ) );
+ driver = &cmd->params.driver;
driver->major = product_major_version;
driver->minor = product_minor_version;
- snprintf ( buf->name, sizeof ( buf->name ), "%s",
+ buf = intelxl_admin_command_buffer ( intelxl );
+ snprintf ( buf->driver.name, sizeof ( buf->driver.name ), "%s",
( product_name[0] ? product_name : product_short_name ) );
/* Issue command */
- if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 )
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
return rc;
return 0;
@@ -365,17 +513,51 @@ static int intelxl_admin_driver ( struct intelxl_nic *intelxl ) {
* @ret rc Return status code
*/
static int intelxl_admin_shutdown ( struct intelxl_nic *intelxl ) {
- struct intelxl_admin_descriptor cmd;
- struct intelxl_admin_shutdown_params *shutdown = &cmd.params.shutdown;
+ struct intelxl_admin_descriptor *cmd;
+ struct intelxl_admin_shutdown_params *shutdown;
int rc;
/* Populate descriptor */
- memset ( &cmd, 0, sizeof ( cmd ) );
- cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_SHUTDOWN );
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_SHUTDOWN );
+ shutdown = &cmd->params.shutdown;
shutdown->unloading = INTELXL_ADMIN_SHUTDOWN_UNLOADING;
/* Issue command */
- if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 )
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Clear PXE mode
+ *
+ * @v intelxl Intel device
+ * @ret rc Return status code
+ */
+static int intelxl_admin_clear_pxe ( struct intelxl_nic *intelxl ) {
+ struct intelxl_admin_descriptor *cmd;
+ struct intelxl_admin_clear_pxe_params *pxe;
+ uint32_t gllan_rctl_0;
+ int rc;
+
+ /* Do nothing if device is already out of PXE mode */
+ gllan_rctl_0 = readl ( intelxl->regs + INTELXL_GLLAN_RCTL_0 );
+ if ( ! ( gllan_rctl_0 & INTELXL_GLLAN_RCTL_0_PXE_MODE ) ) {
+ DBGC2 ( intelxl, "INTELXL %p already in non-PXE mode\n",
+ intelxl );
+ return 0;
+ }
+
+ /* Populate descriptor */
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_CLEAR_PXE );
+ pxe = &cmd->params.pxe;
+ pxe->magic = INTELXL_ADMIN_CLEAR_PXE_MAGIC;
+
+ /* Issue command */
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
return rc;
return 0;
@@ -388,36 +570,38 @@ static int intelxl_admin_shutdown ( struct intelxl_nic *intelxl ) {
* @ret rc Return status code
*/
static int intelxl_admin_switch ( struct intelxl_nic *intelxl ) {
- struct intelxl_admin_descriptor cmd;
- struct intelxl_admin_switch_params *sw = &cmd.params.sw;
- struct intelxl_admin_switch_buffer *buf = &intelxl->command.buffer->sw;
- struct intelxl_admin_switch_config *cfg = &buf->cfg;
+ struct intelxl_admin_descriptor *cmd;
+ struct intelxl_admin_switch_params *sw;
+ union intelxl_admin_buffer *buf;
int rc;
/* Populate descriptor */
- memset ( &cmd, 0, sizeof ( cmd ) );
- cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_SWITCH );
- cmd.flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF );
- cmd.len = cpu_to_le16 ( sizeof ( *buf ) );
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_SWITCH );
+ cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF );
+ cmd->len = cpu_to_le16 ( sizeof ( buf->sw ) );
+ sw = &cmd->params.sw;
+ buf = intelxl_admin_command_buffer ( intelxl );
/* Get each configuration in turn */
do {
/* Issue command */
- if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 )
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
return rc;
/* Dump raw configuration */
DBGC2 ( intelxl, "INTELXL %p SEID %#04x:\n",
- intelxl, le16_to_cpu ( cfg->seid ) );
- DBGC2_HDA ( intelxl, 0, cfg, sizeof ( *cfg ) );
+ intelxl, le16_to_cpu ( buf->sw.cfg.seid ) );
+ DBGC2_HDA ( intelxl, 0, &buf->sw.cfg, sizeof ( buf->sw.cfg ) );
/* Parse response */
- if ( cfg->type == INTELXL_ADMIN_SWITCH_TYPE_VSI ) {
- intelxl->vsi = le16_to_cpu ( cfg->seid );
+ if ( buf->sw.cfg.type == INTELXL_ADMIN_SWITCH_TYPE_VSI ) {
+ intelxl->vsi = le16_to_cpu ( buf->sw.cfg.seid );
DBGC ( intelxl, "INTELXL %p VSI %#04x uplink %#04x "
"downlink %#04x conn %#02x\n", intelxl,
- intelxl->vsi, le16_to_cpu ( cfg->uplink ),
- le16_to_cpu ( cfg->downlink ), cfg->connection );
+ intelxl->vsi, le16_to_cpu ( buf->sw.cfg.uplink ),
+ le16_to_cpu ( buf->sw.cfg.downlink ),
+ buf->sw.cfg.connection );
}
} while ( sw->next );
@@ -438,25 +622,27 @@ static int intelxl_admin_switch ( struct intelxl_nic *intelxl ) {
* @ret rc Return status code
*/
static int intelxl_admin_vsi ( struct intelxl_nic *intelxl ) {
- struct intelxl_admin_descriptor cmd;
- struct intelxl_admin_vsi_params *vsi = &cmd.params.vsi;
- struct intelxl_admin_vsi_buffer *buf = &intelxl->command.buffer->vsi;
+ struct intelxl_admin_descriptor *cmd;
+ struct intelxl_admin_vsi_params *vsi;
+ union intelxl_admin_buffer *buf;
int rc;
/* Populate descriptor */
- memset ( &cmd, 0, sizeof ( cmd ) );
- cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_VSI );
- cmd.flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF );
- cmd.len = cpu_to_le16 ( sizeof ( *buf ) );
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_VSI );
+ cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF );
+ cmd->len = cpu_to_le16 ( sizeof ( buf->vsi ) );
+ vsi = &cmd->params.vsi;
vsi->vsi = cpu_to_le16 ( intelxl->vsi );
+ buf = intelxl_admin_command_buffer ( intelxl );
/* Issue command */
- if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 )
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
return rc;
/* Parse response */
- intelxl->queue = le16_to_cpu ( buf->queue[0] );
- intelxl->qset = le16_to_cpu ( buf->qset[0] );
+ intelxl->queue = le16_to_cpu ( buf->vsi.queue[0] );
+ intelxl->qset = le16_to_cpu ( buf->vsi.qset[0] );
DBGC ( intelxl, "INTELXL %p VSI %#04x queue %#04x qset %#04x\n",
intelxl, intelxl->vsi, intelxl->queue, intelxl->qset );
@@ -470,24 +656,25 @@ static int intelxl_admin_vsi ( struct intelxl_nic *intelxl ) {
* @ret rc Return status code
*/
static int intelxl_admin_promisc ( struct intelxl_nic *intelxl ) {
- struct intelxl_admin_descriptor cmd;
- struct intelxl_admin_promisc_params *promisc = &cmd.params.promisc;
+ struct intelxl_admin_descriptor *cmd;
+ struct intelxl_admin_promisc_params *promisc;
uint16_t flags;
int rc;
/* Populate descriptor */
- memset ( &cmd, 0, sizeof ( cmd ) );
- cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_PROMISC );
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_PROMISC );
flags = ( INTELXL_ADMIN_PROMISC_FL_UNICAST |
INTELXL_ADMIN_PROMISC_FL_MULTICAST |
INTELXL_ADMIN_PROMISC_FL_BROADCAST |
INTELXL_ADMIN_PROMISC_FL_VLAN );
+ promisc = &cmd->params.promisc;
promisc->flags = cpu_to_le16 ( flags );
promisc->valid = cpu_to_le16 ( flags );
promisc->vsi = cpu_to_le16 ( intelxl->vsi );
/* Issue command */
- if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 )
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
return rc;
return 0;
@@ -500,18 +687,19 @@ static int intelxl_admin_promisc ( struct intelxl_nic *intelxl ) {
* @ret rc Return status code
*/
static int intelxl_admin_autoneg ( struct intelxl_nic *intelxl ) {
- struct intelxl_admin_descriptor cmd;
- struct intelxl_admin_autoneg_params *autoneg = &cmd.params.autoneg;
+ struct intelxl_admin_descriptor *cmd;
+ struct intelxl_admin_autoneg_params *autoneg;
int rc;
/* Populate descriptor */
- memset ( &cmd, 0, sizeof ( cmd ) );
- cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_AUTONEG );
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_AUTONEG );
+ autoneg = &cmd->params.autoneg;
autoneg->flags = ( INTELXL_ADMIN_AUTONEG_FL_RESTART |
INTELXL_ADMIN_AUTONEG_FL_ENABLE );
/* Issue command */
- if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 )
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
return rc;
return 0;
@@ -525,17 +713,18 @@ static int intelxl_admin_autoneg ( struct intelxl_nic *intelxl ) {
*/
static int intelxl_admin_link ( struct net_device *netdev ) {
struct intelxl_nic *intelxl = netdev->priv;
- struct intelxl_admin_descriptor cmd;
- struct intelxl_admin_link_params *link = &cmd.params.link;
+ struct intelxl_admin_descriptor *cmd;
+ struct intelxl_admin_link_params *link;
int rc;
/* Populate descriptor */
- memset ( &cmd, 0, sizeof ( cmd ) );
- cmd.opcode = cpu_to_le16 ( INTELXL_ADMIN_LINK );
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_LINK );
+ link = &cmd->params.link;
link->notify = INTELXL_ADMIN_LINK_NOTIFY;
/* Issue command */
- if ( ( rc = intelxl_admin_command ( intelxl, &cmd ) ) != 0 )
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
return rc;
DBGC ( intelxl, "INTELXL %p PHY %#02x speed %#02x status %#02x\n",
intelxl, link->phy, link->speed, link->status );
@@ -551,19 +740,36 @@ static int intelxl_admin_link ( struct net_device *netdev ) {
}
/**
+ * Handle virtual function event (when VF driver is not present)
+ *
+ * @v netdev Network device
+ * @v evt Admin queue event descriptor
+ * @v buf Admin queue event data buffer
+ */
+__weak void
+intelxlvf_admin_event ( struct net_device *netdev __unused,
+ struct intelxl_admin_descriptor *evt __unused,
+ union intelxl_admin_buffer *buf __unused ) {
+
+ /* Nothing to do */
+}
+
+/**
* Refill admin event queue
*
* @v intelxl Intel device
*/
static void intelxl_refill_admin ( struct intelxl_nic *intelxl ) {
struct intelxl_admin *admin = &intelxl->event;
- void *admin_regs = ( intelxl->regs + admin->reg );
+ const struct intelxl_admin_offsets *regs = admin->regs;
+ void *admin_regs = ( intelxl->regs + admin->base );
unsigned int tail;
/* Update tail pointer */
tail = ( ( admin->index + INTELXL_ADMIN_NUM_DESC - 1 ) %
INTELXL_ADMIN_NUM_DESC );
- writel ( tail, admin_regs + INTELXL_ADMIN_TAIL );
+ wmb();
+ writel ( tail, admin_regs + regs->tail );
}
/**
@@ -571,42 +777,48 @@ static void intelxl_refill_admin ( struct intelxl_nic *intelxl ) {
*
* @v netdev Network device
*/
-static void intelxl_poll_admin ( struct net_device *netdev ) {
+void intelxl_poll_admin ( struct net_device *netdev ) {
struct intelxl_nic *intelxl = netdev->priv;
struct intelxl_admin *admin = &intelxl->event;
- struct intelxl_admin_descriptor *desc;
+ struct intelxl_admin_descriptor *evt;
+ union intelxl_admin_buffer *buf;
/* Check for events */
while ( 1 ) {
- /* Get next event descriptor */
- desc = &admin->desc[admin->index % INTELXL_ADMIN_NUM_DESC];
+ /* Get next event descriptor and data buffer */
+ evt = &admin->desc[ admin->index % INTELXL_ADMIN_NUM_DESC ];
+ buf = &admin->buf[ admin->index % INTELXL_ADMIN_NUM_DESC ];
/* Stop if descriptor is not yet completed */
- if ( ! ( desc->flags & INTELXL_ADMIN_FL_DD ) )
+ if ( ! ( evt->flags & INTELXL_ADMIN_FL_DD ) )
return;
DBGC2 ( intelxl, "INTELXL %p admin event %#x:\n",
intelxl, admin->index );
- DBGC2_HDA ( intelxl, virt_to_phys ( desc ), desc,
- sizeof ( *desc ) );
+ DBGC2_HDA ( intelxl, virt_to_phys ( evt ), evt,
+ sizeof ( *evt ) );
+ if ( evt->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ) ) {
+ DBGC2_HDA ( intelxl, virt_to_phys ( buf ), buf,
+ le16_to_cpu ( evt->len ) );
+ }
/* Handle event */
- switch ( desc->opcode ) {
+ switch ( evt->opcode ) {
case cpu_to_le16 ( INTELXL_ADMIN_LINK ):
intelxl_admin_link ( netdev );
break;
+ case cpu_to_le16 ( INTELXL_ADMIN_SEND_TO_VF ):
+ intelxlvf_admin_event ( netdev, evt, buf );
+ break;
default:
DBGC ( intelxl, "INTELXL %p admin event %#x "
"unrecognised opcode %#04x\n", intelxl,
- admin->index, le16_to_cpu ( desc->opcode ) );
+ admin->index, le16_to_cpu ( evt->opcode ) );
break;
}
- /* Clear event completion flag */
- desc->flags = 0;
- wmb();
-
- /* Update index and refill queue */
+ /* Reset descriptor and refill queue */
+ intelxl_admin_event_init ( intelxl, admin->index );
admin->index++;
intelxl_refill_admin ( intelxl );
}
@@ -618,19 +830,19 @@ static void intelxl_poll_admin ( struct net_device *netdev ) {
* @v intelxl Intel device
* @ret rc Return status code
*/
-static int intelxl_open_admin ( struct intelxl_nic *intelxl ) {
+int intelxl_open_admin ( struct intelxl_nic *intelxl ) {
int rc;
- /* Create admin event queue */
- if ( ( rc = intelxl_create_admin ( intelxl, &intelxl->event ) ) != 0 )
- goto err_create_event;
+ /* Allocate admin event queue */
+ if ( ( rc = intelxl_alloc_admin ( intelxl, &intelxl->event ) ) != 0 )
+ goto err_alloc_event;
- /* Create admin command queue */
- if ( ( rc = intelxl_create_admin ( intelxl, &intelxl->command ) ) != 0 )
- goto err_create_command;
+ /* Allocate admin command queue */
+ if ( ( rc = intelxl_alloc_admin ( intelxl, &intelxl->command ) ) != 0 )
+ goto err_alloc_command;
- /* Post all descriptors to event queue */
- intelxl_refill_admin ( intelxl );
+ /* (Re)open admin queues */
+ intelxl_reopen_admin ( intelxl );
/* Get firmware version */
if ( ( rc = intelxl_admin_version ( intelxl ) ) != 0 )
@@ -644,28 +856,54 @@ static int intelxl_open_admin ( struct intelxl_nic *intelxl ) {
err_driver:
err_version:
- intelxl_destroy_admin ( intelxl, &intelxl->command );
- err_create_command:
- intelxl_destroy_admin ( intelxl, &intelxl->event );
- err_create_event:
+ intelxl_disable_admin ( intelxl, &intelxl->command );
+ intelxl_disable_admin ( intelxl, &intelxl->event );
+ intelxl_free_admin ( intelxl, &intelxl->command );
+ err_alloc_command:
+ intelxl_free_admin ( intelxl, &intelxl->event );
+ err_alloc_event:
return rc;
}
/**
+ * Reopen admin queues (after virtual function reset)
+ *
+ * @v intelxl Intel device
+ */
+void intelxl_reopen_admin ( struct intelxl_nic *intelxl ) {
+ unsigned int i;
+
+ /* Enable admin event queue */
+ intelxl_enable_admin ( intelxl, &intelxl->event );
+
+ /* Enable admin command queue */
+ intelxl_enable_admin ( intelxl, &intelxl->command );
+
+ /* Initialise all admin event queue descriptors */
+ for ( i = 0 ; i < INTELXL_ADMIN_NUM_DESC ; i++ )
+ intelxl_admin_event_init ( intelxl, i );
+
+ /* Post all descriptors to event queue */
+ intelxl_refill_admin ( intelxl );
+}
+
+/**
* Close admin queues
*
* @v intelxl Intel device
*/
-static void intelxl_close_admin ( struct intelxl_nic *intelxl ) {
+void intelxl_close_admin ( struct intelxl_nic *intelxl ) {
/* Shut down admin queues */
intelxl_admin_shutdown ( intelxl );
- /* Destroy admin command queue */
- intelxl_destroy_admin ( intelxl, &intelxl->command );
+ /* Disable admin queues */
+ intelxl_disable_admin ( intelxl, &intelxl->command );
+ intelxl_disable_admin ( intelxl, &intelxl->event );
- /* Destroy admin event queue */
- intelxl_destroy_admin ( intelxl, &intelxl->event );
+ /* Free admin queues */
+ intelxl_free_admin ( intelxl, &intelxl->command );
+ intelxl_free_admin ( intelxl, &intelxl->event );
}
/******************************************************************************
@@ -676,6 +914,62 @@ static void intelxl_close_admin ( struct intelxl_nic *intelxl ) {
*/
/**
+ * Allocate descriptor ring
+ *
+ * @v intelxl Intel device
+ * @v ring Descriptor ring
+ * @ret rc Return status code
+ */
+int intelxl_alloc_ring ( struct intelxl_nic *intelxl,
+ struct intelxl_ring *ring ) {
+ physaddr_t address;
+ int rc;
+
+ /* Allocate descriptor ring */
+ ring->desc.raw = malloc_dma ( ring->len, INTELXL_ALIGN );
+ if ( ! ring->desc.raw ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ address = virt_to_bus ( ring->desc.raw );
+
+ /* Initialise descriptor ring */
+ memset ( ring->desc.raw, 0, ring->len );
+
+ /* Reset tail pointer */
+ writel ( 0, ( intelxl->regs + ring->tail ) );
+
+ /* Reset counters */
+ ring->prod = 0;
+ ring->cons = 0;
+
+ DBGC ( intelxl, "INTELXL %p ring %06x is at [%08llx,%08llx)\n",
+ intelxl, ( ring->reg + ring->tail ),
+ ( ( unsigned long long ) address ),
+ ( ( unsigned long long ) address + ring->len ) );
+
+ return 0;
+
+ free_dma ( ring->desc.raw, ring->len );
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Free descriptor ring
+ *
+ * @v intelxl Intel device
+ * @v ring Descriptor ring
+ */
+void intelxl_free_ring ( struct intelxl_nic *intelxl __unused,
+ struct intelxl_ring *ring ) {
+
+ /* Free descriptor ring */
+ free_dma ( ring->desc.raw, ring->len );
+ ring->desc.raw = NULL;
+}
+
+/**
* Dump queue context (for debugging)
*
* @v intelxl Intel device
@@ -861,7 +1155,7 @@ static int intelxl_context_rx ( struct intelxl_nic *intelxl,
base_count = INTELXL_CTX_RX_BASE_COUNT ( address, INTELXL_RX_NUM_DESC );
ctx.rx.base_count = cpu_to_le64 ( base_count );
ctx.rx.len = cpu_to_le16 ( INTELXL_CTX_RX_LEN ( intelxl->mfs ) );
- ctx.rx.flags = INTELXL_CTX_RX_FL_CRCSTRIP;
+ ctx.rx.flags = ( INTELXL_CTX_RX_FL_DSIZE | INTELXL_CTX_RX_FL_CRCSTRIP );
ctx.rx.mfs = cpu_to_le16 ( INTELXL_CTX_RX_MFS ( intelxl->mfs ) );
/* Program context */
@@ -939,25 +1233,15 @@ static int intelxl_disable_ring ( struct intelxl_nic *intelxl,
*/
static int intelxl_create_ring ( struct intelxl_nic *intelxl,
struct intelxl_ring *ring ) {
- void *ring_regs = ( intelxl->regs + ring->reg );
physaddr_t address;
int rc;
/* Allocate descriptor ring */
- ring->desc = malloc_dma ( ring->len, INTELXL_ALIGN );
- if ( ! ring->desc ) {
- rc = -ENOMEM;
+ if ( ( rc = intelxl_alloc_ring ( intelxl, ring ) ) != 0 )
goto err_alloc;
- }
-
- /* Initialise descriptor ring */
- memset ( ring->desc, 0, ring->len );
-
- /* Reset tail pointer */
- writel ( 0, ( ring_regs + INTELXL_QXX_TAIL ) );
/* Program queue context */
- address = virt_to_bus ( ring->desc );
+ address = virt_to_bus ( ring->desc.raw );
if ( ( rc = ring->context ( intelxl, address ) ) != 0 )
goto err_context;
@@ -965,20 +1249,12 @@ static int intelxl_create_ring ( struct intelxl_nic *intelxl,
if ( ( rc = intelxl_enable_ring ( intelxl, ring ) ) != 0 )
goto err_enable;
- /* Reset counters */
- ring->prod = 0;
- ring->cons = 0;
-
- DBGC ( intelxl, "INTELXL %p ring %06x is at [%08llx,%08llx)\n",
- intelxl, ring->reg, ( ( unsigned long long ) address ),
- ( ( unsigned long long ) address + ring->len ) );
-
return 0;
intelxl_disable_ring ( intelxl, ring );
err_enable:
err_context:
- free_dma ( ring->desc, ring->len );
+ intelxl_free_ring ( intelxl, ring );
err_alloc:
return rc;
}
@@ -1000,8 +1276,7 @@ static void intelxl_destroy_ring ( struct intelxl_nic *intelxl,
}
/* Free descriptor ring */
- free_dma ( ring->desc, ring->len );
- ring->desc = NULL;
+ intelxl_free_ring ( intelxl, ring );
}
/**
@@ -1029,7 +1304,7 @@ static void intelxl_refill_rx ( struct intelxl_nic *intelxl ) {
/* Get next receive descriptor */
rx_idx = ( intelxl->rx.prod++ % INTELXL_RX_NUM_DESC );
- rx = &intelxl->rx.desc[rx_idx].rx;
+ rx = &intelxl->rx.desc.rx[rx_idx].data;
/* Populate receive descriptor */
address = virt_to_bus ( iobuf->data );
@@ -1050,8 +1325,23 @@ static void intelxl_refill_rx ( struct intelxl_nic *intelxl ) {
if ( refilled ) {
wmb();
rx_tail = ( intelxl->rx.prod % INTELXL_RX_NUM_DESC );
- writel ( rx_tail,
- ( intelxl->regs + intelxl->rx.reg + INTELXL_QXX_TAIL));
+ writel ( rx_tail, ( intelxl->regs + intelxl->rx.tail ) );
+ }
+}
+
+/**
+ * Discard unused receive I/O buffers
+ *
+ * @v intelxl Intel device
+ */
+void intelxl_empty_rx ( struct intelxl_nic *intelxl ) {
+ unsigned int i;
+
+ /* Discard any unused receive buffers */
+ for ( i = 0 ; i < INTELXL_RX_NUM_DESC ; i++ ) {
+ if ( intelxl->rx_iobuf[i] )
+ free_iob ( intelxl->rx_iobuf[i] );
+ intelxl->rx_iobuf[i] = NULL;
}
}
@@ -1141,7 +1431,6 @@ static int intelxl_open ( struct net_device *netdev ) {
static void intelxl_close ( struct net_device *netdev ) {
struct intelxl_nic *intelxl = netdev->priv;
unsigned int queue;
- unsigned int i;
/* Dump contexts (for debugging) */
intelxl_context_dump ( intelxl, INTELXL_PFCM_LANCTXCTL_TYPE_TX,
@@ -1163,11 +1452,7 @@ static void intelxl_close ( struct net_device *netdev ) {
intelxl_destroy_ring ( intelxl, &intelxl->rx );
/* Discard any unused receive buffers */
- for ( i = 0 ; i < INTELXL_RX_NUM_DESC ; i++ ) {
- if ( intelxl->rx_iobuf[i] )
- free_iob ( intelxl->rx_iobuf[i] );
- intelxl->rx_iobuf[i] = NULL;
- }
+ intelxl_empty_rx ( intelxl );
}
/**
@@ -1177,8 +1462,7 @@ static void intelxl_close ( struct net_device *netdev ) {
* @v iobuf I/O buffer
* @ret rc Return status code
*/
-static int intelxl_transmit ( struct net_device *netdev,
- struct io_buffer *iobuf ) {
+int intelxl_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) {
struct intelxl_nic *intelxl = netdev->priv;
struct intelxl_tx_data_descriptor *tx;
unsigned int tx_idx;
@@ -1194,7 +1478,7 @@ static int intelxl_transmit ( struct net_device *netdev,
}
tx_idx = ( intelxl->tx.prod++ % INTELXL_TX_NUM_DESC );
tx_tail = ( intelxl->tx.prod % INTELXL_TX_NUM_DESC );
- tx = &intelxl->tx.desc[tx_idx].tx;
+ tx = &intelxl->tx.desc.tx[tx_idx].data;
/* Populate transmit descriptor */
address = virt_to_bus ( iobuf->data );
@@ -1206,8 +1490,7 @@ static int intelxl_transmit ( struct net_device *netdev,
wmb();
/* Notify card that there are packets ready to transmit */
- writel ( tx_tail,
- ( intelxl->regs + intelxl->tx.reg + INTELXL_QXX_TAIL ) );
+ writel ( tx_tail, ( intelxl->regs + intelxl->tx.tail ) );
DBGC2 ( intelxl, "INTELXL %p TX %d is [%llx,%llx)\n", intelxl, tx_idx,
( ( unsigned long long ) address ),
@@ -1230,7 +1513,7 @@ static void intelxl_poll_tx ( struct net_device *netdev ) {
/* Get next transmit descriptor */
tx_idx = ( intelxl->tx.cons % INTELXL_TX_NUM_DESC );
- tx_wb = &intelxl->tx.desc[tx_idx].tx_wb;
+ tx_wb = &intelxl->tx.desc.tx[tx_idx].wb;
/* Stop if descriptor is still in use */
if ( ! ( tx_wb->flags & INTELXL_TX_WB_FL_DD ) )
@@ -1254,6 +1537,7 @@ static void intelxl_poll_rx ( struct net_device *netdev ) {
struct intelxl_rx_writeback_descriptor *rx_wb;
struct io_buffer *iobuf;
unsigned int rx_idx;
+ unsigned int tag;
size_t len;
/* Check for received packets */
@@ -1261,7 +1545,7 @@ static void intelxl_poll_rx ( struct net_device *netdev ) {
/* Get next receive descriptor */
rx_idx = ( intelxl->rx.cons % INTELXL_RX_NUM_DESC );
- rx_wb = &intelxl->rx.desc[rx_idx].rx_wb;
+ rx_wb = &intelxl->rx.desc.rx[rx_idx].wb;
/* Stop if descriptor is still in use */
if ( ! ( rx_wb->flags & cpu_to_le32 ( INTELXL_RX_WB_FL_DD ) ) )
@@ -1273,16 +1557,23 @@ static void intelxl_poll_rx ( struct net_device *netdev ) {
len = INTELXL_RX_WB_LEN ( le32_to_cpu ( rx_wb->len ) );
iob_put ( iobuf, len );
+ /* Find VLAN device, if applicable */
+ if ( rx_wb->flags & cpu_to_le32 ( INTELXL_RX_WB_FL_VLAN ) ) {
+ tag = VLAN_TAG ( le16_to_cpu ( rx_wb->vlan ) );
+ } else {
+ tag = 0;
+ }
+
/* Hand off to network stack */
if ( rx_wb->flags & cpu_to_le32 ( INTELXL_RX_WB_FL_RXE ) ) {
DBGC ( intelxl, "INTELXL %p RX %d error (length %zd, "
"flags %08x)\n", intelxl, rx_idx, len,
le32_to_cpu ( rx_wb->flags ) );
- netdev_rx_err ( netdev, iobuf, -EIO );
+ vlan_netdev_rx_err ( netdev, tag, iobuf, -EIO );
} else {
DBGC2 ( intelxl, "INTELXL %p RX %d complete (length "
"%zd)\n", intelxl, rx_idx, len );
- netdev_rx ( netdev, iobuf );
+ vlan_netdev_rx ( netdev, tag, iobuf );
}
intelxl->rx.cons++;
}
@@ -1293,16 +1584,9 @@ static void intelxl_poll_rx ( struct net_device *netdev ) {
*
* @v netdev Network device
*/
-static void intelxl_poll ( struct net_device *netdev ) {
+void intelxl_poll ( struct net_device *netdev ) {
struct intelxl_nic *intelxl = netdev->priv;
- /* Acknowledge interrupts, if applicable */
- if ( netdev_irq_enabled ( netdev ) ) {
- writel ( ( INTELXL_PFINT_DYN_CTL0_CLEARPBA |
- INTELXL_PFINT_DYN_CTL0_INTENA_MASK ),
- intelxl->regs + INTELXL_PFINT_DYN_CTL0 );
- }
-
/* Poll for completed packets */
intelxl_poll_tx ( netdev );
@@ -1314,23 +1598,23 @@ static void intelxl_poll ( struct net_device *netdev ) {
/* Refill RX ring */
intelxl_refill_rx ( intelxl );
-}
-
-/**
- * Enable or disable interrupts
- *
- * @v netdev Network device
- * @v enable Interrupts should be enabled
- */
-static void intelxl_irq ( struct net_device *netdev, int enable ) {
- struct intelxl_nic *intelxl = netdev->priv;
- if ( enable ) {
- writel ( INTELXL_PFINT_DYN_CTL0_INTENA,
- intelxl->regs + INTELXL_PFINT_DYN_CTL0 );
- } else {
- writel ( 0, intelxl->regs + INTELXL_PFINT_DYN_CTL0 );
- }
+ /* Rearm interrupt, since otherwise receive descriptors will
+ * be written back only after a complete cacheline (four
+ * packets) have been received.
+ *
+ * There is unfortunately no efficient way to determine
+ * whether or not rearming the interrupt is necessary. If we
+ * are running inside a hypervisor (e.g. using a VF or PF as a
+ * passed-through PCI device), then the MSI-X write is
+ * redirected by the hypervisor to the real host APIC and the
+ * host ISR then raises an interrupt within the guest. We
+ * therefore cannot poll the nominal MSI-X target location to
+ * watch for the value being written. We could read from the
+ * INT_DYN_CTL register, but this is even less efficient than
+ * just unconditionally rearming the interrupt.
+ */
+ writel ( INTELXL_INT_DYN_CTL_INTENA, intelxl->regs + intelxl->intr );
}
/** Network device operations */
@@ -1339,7 +1623,6 @@ static struct net_device_operations intelxl_operations = {
.close = intelxl_close,
.transmit = intelxl_transmit,
.poll = intelxl_poll,
- .irq = intelxl_irq,
};
/******************************************************************************
@@ -1374,11 +1657,16 @@ static int intelxl_probe ( struct pci_device *pci ) {
netdev->dev = &pci->dev;
memset ( intelxl, 0, sizeof ( *intelxl ) );
intelxl->pf = PCI_FUNC ( pci->busdevfn );
- intelxl_init_admin ( &intelxl->command, INTELXL_ADMIN_CMD );
- intelxl_init_admin ( &intelxl->event, INTELXL_ADMIN_EVT );
+ intelxl->intr = INTELXL_PFINT_DYN_CTL0;
+ intelxl_init_admin ( &intelxl->command, INTELXL_ADMIN_CMD,
+ &intelxl_admin_offsets );
+ intelxl_init_admin ( &intelxl->event, INTELXL_ADMIN_EVT,
+ &intelxl_admin_offsets );
intelxl_init_ring ( &intelxl->tx, INTELXL_TX_NUM_DESC,
+ sizeof ( intelxl->tx.desc.tx[0] ),
intelxl_context_tx );
intelxl_init_ring ( &intelxl->rx, INTELXL_RX_NUM_DESC,
+ sizeof ( intelxl->rx.desc.rx[0] ),
intelxl_context_rx );
/* Fix up PCI device */
@@ -1408,10 +1696,18 @@ static int intelxl_probe ( struct pci_device *pci ) {
if ( ( rc = intelxl_fetch_mac ( intelxl, netdev ) ) != 0 )
goto err_fetch_mac;
+ /* Enable MSI-X dummy interrupt */
+ if ( ( rc = intelxl_msix_enable ( intelxl, pci ) ) != 0 )
+ goto err_msix;
+
/* Open admin queues */
if ( ( rc = intelxl_open_admin ( intelxl ) ) != 0 )
goto err_open_admin;
+ /* Clear PXE mode */
+ if ( ( rc = intelxl_admin_clear_pxe ( intelxl ) ) != 0 )
+ goto err_admin_clear_pxe;
+
/* Get switch configuration */
if ( ( rc = intelxl_admin_switch ( intelxl ) ) != 0 )
goto err_admin_switch;
@@ -1426,7 +1722,9 @@ static int intelxl_probe ( struct pci_device *pci ) {
/* Configure queue register addresses */
intelxl->tx.reg = INTELXL_QTX ( intelxl->queue );
+ intelxl->tx.tail = ( intelxl->tx.reg + INTELXL_QXX_TAIL );
intelxl->rx.reg = INTELXL_QRX ( intelxl->queue );
+ intelxl->rx.tail = ( intelxl->rx.reg + INTELXL_QXX_TAIL );
/* Configure interrupt causes */
writel ( ( INTELXL_QINT_TQCTL_NEXTQ_INDX_NONE |
@@ -1456,8 +1754,11 @@ static int intelxl_probe ( struct pci_device *pci ) {
err_admin_promisc:
err_admin_vsi:
err_admin_switch:
+ err_admin_clear_pxe:
intelxl_close_admin ( intelxl );
err_open_admin:
+ intelxl_msix_disable ( intelxl, pci );
+ err_msix:
err_fetch_mac:
intelxl_reset ( intelxl );
err_reset:
@@ -1484,6 +1785,9 @@ static void intelxl_remove ( struct pci_device *pci ) {
/* Close admin queues */
intelxl_close_admin ( intelxl );
+ /* Disable MSI-X dummy interrupt */
+ intelxl_msix_disable ( intelxl, pci );
+
/* Reset the NIC */
intelxl_reset ( intelxl );
diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h
index 02d9b98a2..80586cef0 100644
--- a/src/drivers/net/intelxl.h
+++ b/src/drivers/net/intelxl.h
@@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdint.h>
#include <ipxe/if_ether.h>
+#include <ipxe/pcimsix.h>
struct intelxl_nic;
@@ -19,9 +20,9 @@ struct intelxl_nic;
/** Alignment
*
- * No data structure requires greater than 128 byte alignment.
+ * No data structure requires greater than 256 byte alignment.
*/
-#define INTELXL_ALIGN 128
+#define INTELXL_ALIGN 256
/******************************************************************************
*
@@ -53,6 +54,24 @@ struct intelxl_nic;
/** Admin Queue Tail Register (offset) */
#define INTELXL_ADMIN_TAIL 0x400
+/** Admin queue register offsets
+ *
+ * The physical and virtual function register maps have no discernible
+ * relationship.
+ */
+struct intelxl_admin_offsets {
+ /** Base Address Low Register offset */
+ unsigned int bal;
+ /** Base Address High Register offset */
+ unsigned int bah;
+ /** Length Register offset */
+ unsigned int len;
+ /** Head Register offset */
+ unsigned int head;
+ /** Tail Register offset */
+ unsigned int tail;
+};
+
/** Admin queue data buffer command parameters */
struct intelxl_admin_buffer_params {
/** Reserved */
@@ -125,6 +144,20 @@ struct intelxl_admin_shutdown_params {
/** Driver is unloading */
#define INTELXL_ADMIN_SHUTDOWN_UNLOADING 0x01
+/** Admin queue Clear PXE Mode command */
+#define INTELXL_ADMIN_CLEAR_PXE 0x0110
+
+/** Admin queue Clear PXE Mode command parameters */
+struct intelxl_admin_clear_pxe_params {
+ /** Magic value */
+ uint8_t magic;
+ /** Reserved */
+ uint8_t reserved[15];
+} __attribute__ (( packed ));
+
+/** Clear PXE Mode magic value */
+#define INTELXL_ADMIN_CLEAR_PXE_MAGIC 0x02
+
/** Admin queue Get Switch Configuration command */
#define INTELXL_ADMIN_SWITCH 0x0200
@@ -271,6 +304,172 @@ struct intelxl_admin_link_params {
/** Link is up */
#define INTELXL_ADMIN_LINK_UP 0x01
+/** Admin queue Send Message to PF command */
+#define INTELXL_ADMIN_SEND_TO_PF 0x0801
+
+/** Admin queue Send Message to VF command */
+#define INTELXL_ADMIN_SEND_TO_VF 0x0802
+
+/** Admin Queue VF Reset opcode */
+#define INTELXL_ADMIN_VF_RESET 0x00000002
+
+/** Admin Queue VF Get Resources opcode */
+#define INTELXL_ADMIN_VF_GET_RESOURCES 0x00000003
+
+/** Admin Queue VF Get Resources data buffer */
+struct intelxl_admin_vf_get_resources_buffer {
+ /** Reserved */
+ uint8_t reserved_a[20];
+ /** VSI switching element ID */
+ uint16_t vsi;
+ /** Reserved */
+ uint8_t reserved_b[8];
+ /** MAC address */
+ uint8_t mac[ETH_ALEN];
+} __attribute__ (( packed ));
+
+/** Admin Queue VF Status Change Event opcode */
+#define INTELXL_ADMIN_VF_STATUS 0x00000011
+
+/** Link status change event type */
+#define INTELXL_ADMIN_VF_STATUS_LINK 0x00000001
+
+/** Link status change event data */
+struct intelxl_admin_vf_status_link {
+ /** Link speed */
+ uint32_t speed;
+ /** Link status */
+ uint8_t status;
+ /** Reserved */
+ uint8_t reserved[3];
+} __attribute__ (( packed ));
+
+/** Admin Queue VF Status Change Event data buffer */
+struct intelxl_admin_vf_status_buffer {
+ /** Event type */
+ uint32_t event;
+ /** Event data */
+ union {
+ /** Link change event data */
+ struct intelxl_admin_vf_status_link link;
+ } data;
+ /** Reserved */
+ uint8_t reserved[4];
+} __attribute__ (( packed ));
+
+/** Admin Queue VF Configure Queues opcode */
+#define INTELXL_ADMIN_VF_CONFIGURE 0x00000006
+
+/** Admin Queue VF Configure Queues data buffer */
+struct intelxl_admin_vf_configure_buffer {
+ /** VSI switching element ID */
+ uint16_t vsi;
+ /** Number of queue pairs */
+ uint16_t count;
+ /** Reserved */
+ uint8_t reserved_a[4];
+ /** Transmit queue */
+ struct {
+ /** VSI switching element ID */
+ uint16_t vsi;
+ /** Queue ID */
+ uint16_t id;
+ /** Queue count */
+ uint16_t count;
+ /** Reserved */
+ uint8_t reserved_a[2];
+ /** Base address */
+ uint64_t base;
+ /** Reserved */
+ uint8_t reserved_b[8];
+ } __attribute__ (( packed )) tx;
+ /** Receive queue */
+ struct {
+ /** VSI switching element ID */
+ uint16_t vsi;
+ /** Queue ID */
+ uint16_t id;
+ /** Queue count */
+ uint32_t count;
+ /** Reserved */
+ uint8_t reserved_a[4];
+ /** Data buffer length */
+ uint32_t len;
+ /** Maximum frame size */
+ uint32_t mfs;
+ /** Reserved */
+ uint8_t reserved_b[4];
+ /** Base address */
+ uint64_t base;
+ /** Reserved */
+ uint8_t reserved_c[8];
+ } __attribute__ (( packed )) rx;
+ /** Reserved
+ *
+ * This field exists only due to a bug in the PF driver's
+ * message validation logic, which causes it to miscalculate
+ * the expected message length.
+ */
+ uint8_t reserved_b[64];
+} __attribute__ (( packed ));
+
+/** Admin Queue VF IRQ Map opcode */
+#define INTELXL_ADMIN_VF_IRQ_MAP 0x00000007
+
+/** Admin Queue VF IRQ Map data buffer */
+struct intelxl_admin_vf_irq_map_buffer {
+ /** Number of interrupt vectors */
+ uint16_t count;
+ /** VSI switching element ID */
+ uint16_t vsi;
+ /** Interrupt vector ID */
+ uint16_t vec;
+ /** Receive queue bitmap */
+ uint16_t rxmap;
+ /** Transmit queue bitmap */
+ uint16_t txmap;
+ /** Receive interrupt throttling index */
+ uint16_t rxitr;
+ /** Transmit interrupt throttling index */
+ uint16_t txitr;
+ /** Reserved
+ *
+ * This field exists only due to a bug in the PF driver's
+ * message validation logic, which causes it to miscalculate
+ * the expected message length.
+ */
+ uint8_t reserved[12];
+} __attribute__ (( packed ));
+
+/** Admin Queue VF Enable Queues opcode */
+#define INTELXL_ADMIN_VF_ENABLE 0x00000008
+
+/** Admin Queue VF Disable Queues opcode */
+#define INTELXL_ADMIN_VF_DISABLE 0x00000009
+
+/** Admin Queue VF Enable/Disable Queues data buffer */
+struct intelxl_admin_vf_queues_buffer {
+ /** VSI switching element ID */
+ uint16_t vsi;
+ /** Reserved */
+ uint8_t reserved[2];
+ /** Receive queue bitmask */
+ uint32_t rx;
+ /** Transmit queue bitmask */
+ uint32_t tx;
+} __attribute__ (( packed ));
+
+/** Admin Queue VF Configure Promiscuous Mode opcode */
+#define INTELXL_ADMIN_VF_PROMISC 0x0000000e
+
+/** Admin Queue VF Configure Promiscuous Mode data buffer */
+struct intelxl_admin_vf_promisc_buffer {
+ /** VSI switching element ID */
+ uint16_t vsi;
+ /** Flags */
+ uint16_t flags;
+} __attribute__ (( packed ));
+
/** Admin queue command parameters */
union intelxl_admin_params {
/** Additional data buffer command parameters */
@@ -281,6 +480,8 @@ union intelxl_admin_params {
struct intelxl_admin_driver_params driver;
/** Shutdown command parameters */
struct intelxl_admin_shutdown_params shutdown;
+ /** Clear PXE Mode command parameters */
+ struct intelxl_admin_clear_pxe_params pxe;
/** Get Switch Configuration command parameters */
struct intelxl_admin_switch_params sw;
/** Get VSI Parameters command parameters */
@@ -301,6 +502,20 @@ union intelxl_admin_buffer {
struct intelxl_admin_switch_buffer sw;
/** Get VSI Parameters data buffer */
struct intelxl_admin_vsi_buffer vsi;
+ /** VF Get Resources data buffer */
+ struct intelxl_admin_vf_get_resources_buffer res;
+ /** VF Status Change Event data buffer */
+ struct intelxl_admin_vf_status_buffer stat;
+ /** VF Configure Queues data buffer */
+ struct intelxl_admin_vf_configure_buffer cfg;
+ /** VF Enable/Disable Queues data buffer */
+ struct intelxl_admin_vf_queues_buffer queues;
+ /** VF Configure Promiscuous Mode data buffer */
+ struct intelxl_admin_vf_promisc_buffer promisc;
+ /*** VF IRQ Map data buffer */
+ struct intelxl_admin_vf_irq_map_buffer irq;
+ /** Alignment padding */
+ uint8_t pad[INTELXL_ALIGN];
} __attribute__ (( packed ));
/** Admin queue descriptor */
@@ -313,10 +528,15 @@ struct intelxl_admin_descriptor {
uint16_t len;
/** Return value */
uint16_t ret;
- /** Cookie */
- uint32_t cookie;
- /** Reserved */
- uint32_t reserved;
+ /** Opaque cookie / VF opcode */
+ union {
+ /** Cookie */
+ uint32_t cookie;
+ /** VF opcode */
+ uint32_t vopcode;
+ };
+ /** VF return value */
+ int32_t vret;
/** Parameters */
union intelxl_admin_params params;
} __attribute__ (( packed ));
@@ -340,25 +560,30 @@ struct intelxl_admin_descriptor {
struct intelxl_admin {
/** Descriptors */
struct intelxl_admin_descriptor *desc;
+ /** Data buffers */
+ union intelxl_admin_buffer *buf;
/** Queue index */
unsigned int index;
- /** Register block */
- unsigned int reg;
- /** Data buffer */
- union intelxl_admin_buffer *buffer;
+ /** Register block base */
+ unsigned int base;
+ /** Register offsets */
+ const struct intelxl_admin_offsets *regs;
};
/**
* Initialise admin queue
*
* @v admin Admin queue
- * @v reg Register block
+ * @v base Register block base
+ * @v regs Register offsets
*/
static inline __attribute__ (( always_inline )) void
-intelxl_init_admin ( struct intelxl_admin *admin, unsigned int reg ) {
+intelxl_init_admin ( struct intelxl_admin *admin, unsigned int base,
+ const struct intelxl_admin_offsets *regs ) {
- admin->reg = reg;
+ admin->base = base;
+ admin->regs = regs;
}
/** Number of admin queue descriptors */
@@ -466,6 +691,9 @@ struct intelxl_context_rx {
/** Receive queue data buffer length */
#define INTELXL_CTX_RX_LEN( len ) ( (len) >> 1 )
+/** Use 32-byte receive descriptors */
+#define INTELXL_CTX_RX_FL_DSIZE 0x10
+
/** Strip CRC from received packets */
#define INTELXL_CTX_RX_FL_CRCSTRIP 0x20
@@ -524,6 +752,10 @@ struct intelxl_context_rx {
/** Queue Tail Pointer Register (offset) */
#define INTELXL_QXX_TAIL 0x8000
+/** Global RLAN Control 0 register */
+#define INTELXL_GLLAN_RCTL_0 0x12a500
+#define INTELXL_GLLAN_RCTL_0_PXE_MODE 0x00000001UL /**< PXE mode */
+
/** Transmit data descriptor */
struct intelxl_tx_data_descriptor {
/** Buffer address */
@@ -569,6 +801,14 @@ struct intelxl_tx_writeback_descriptor {
/** Transmit writeback descriptor complete */
#define INTELXL_TX_WB_FL_DD 0x01
+/** Transmit descriptor */
+union intelxl_tx_descriptor {
+ /** Transmit data descriptor */
+ struct intelxl_tx_data_descriptor data;
+ /** Transmit writeback descriptor */
+ struct intelxl_tx_writeback_descriptor wb;
+};
+
/** Receive data descriptor */
struct intelxl_rx_data_descriptor {
/** Buffer address */
@@ -576,22 +816,31 @@ struct intelxl_rx_data_descriptor {
/** Flags */
uint32_t flags;
/** Reserved */
- uint8_t reserved[4];
+ uint8_t reserved[20];
} __attribute__ (( packed ));
/** Receive writeback descriptor */
struct intelxl_rx_writeback_descriptor {
/** Reserved */
- uint8_t reserved[8];
+ uint8_t reserved_a[2];
+ /** VLAN tag */
+ uint16_t vlan;
+ /** Reserved */
+ uint8_t reserved_b[4];
/** Flags */
uint32_t flags;
/** Length */
uint32_t len;
+ /** Reserved */
+ uint8_t reserved_c[16];
} __attribute__ (( packed ));
/** Receive writeback descriptor complete */
#define INTELXL_RX_WB_FL_DD 0x00000001UL
+/** Receive writeback descriptor VLAN tag present */
+#define INTELXL_RX_WB_FL_VLAN 0x00000004UL
+
/** Receive writeback descriptor error */
#define INTELXL_RX_WB_FL_RXE 0x00080000UL
@@ -599,21 +848,24 @@ struct intelxl_rx_writeback_descriptor {
#define INTELXL_RX_WB_LEN(len) ( ( (len) >> 6 ) & 0x3fff )
/** Packet descriptor */
-union intelxl_descriptor {
- /** Transmit data descriptor */
- struct intelxl_tx_data_descriptor tx;
- /** Transmit writeback descriptor */
- struct intelxl_tx_writeback_descriptor tx_wb;
+union intelxl_rx_descriptor {
/** Receive data descriptor */
- struct intelxl_rx_data_descriptor rx;
+ struct intelxl_rx_data_descriptor data;
/** Receive writeback descriptor */
- struct intelxl_rx_writeback_descriptor rx_wb;
+ struct intelxl_rx_writeback_descriptor wb;
};
/** Descriptor ring */
struct intelxl_ring {
/** Descriptors */
- union intelxl_descriptor *desc;
+ union {
+ /** Transmit descriptors */
+ union intelxl_tx_descriptor *tx;
+ /** Receive descriptors */
+ union intelxl_rx_descriptor *rx;
+ /** Raw data */
+ void *raw;
+ } desc;
/** Producer index */
unsigned int prod;
/** Consumer index */
@@ -621,6 +873,8 @@ struct intelxl_ring {
/** Register block */
unsigned int reg;
+ /** Tail register */
+ unsigned int tail;
/** Length (in bytes) */
size_t len;
/** Program queue context
@@ -636,33 +890,39 @@ struct intelxl_ring {
*
* @v ring Descriptor ring
* @v count Number of descriptors
+ * @v len Length of a single descriptor
* @v context Method to program queue context
*/
static inline __attribute__ (( always_inline)) void
-intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count,
+intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, size_t len,
int ( * context ) ( struct intelxl_nic *intelxl,
physaddr_t address ) ) {
- ring->len = ( count * sizeof ( ring->desc[0] ) );
+ ring->len = ( count * len );
ring->context = context;
}
-/** Number of transmit descriptors */
-#define INTELXL_TX_NUM_DESC 16
+/** Number of transmit descriptors
+ *
+ * Chosen to exceed the receive ring fill level, in order to avoid
+ * running out of transmit descriptors when sending TCP ACKs.
+ */
+#define INTELXL_TX_NUM_DESC 64
/** Transmit descriptor ring maximum fill level */
#define INTELXL_TX_FILL ( INTELXL_TX_NUM_DESC - 1 )
/** Number of receive descriptors
*
- * In PXE mode (i.e. able to post single receive descriptors), 8
- * descriptors is the only permitted value covering all possible
- * numbers of PFs.
+ * Must be a multiple of 32.
*/
-#define INTELXL_RX_NUM_DESC 8
+#define INTELXL_RX_NUM_DESC 32
-/** Receive descriptor ring fill level */
-#define INTELXL_RX_FILL ( INTELXL_RX_NUM_DESC - 1 )
+/** Receive descriptor ring fill level
+ *
+ * Must be a multiple of 8 and greater than 8.
+ */
+#define INTELXL_RX_FILL 16
/******************************************************************************
*
@@ -673,9 +933,9 @@ intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count,
/** PF Interrupt Zero Dynamic Control Register */
#define INTELXL_PFINT_DYN_CTL0 0x038480
-#define INTELXL_PFINT_DYN_CTL0_INTENA 0x00000001UL /**< Enable */
-#define INTELXL_PFINT_DYN_CTL0_CLEARPBA 0x00000002UL /**< Acknowledge */
-#define INTELXL_PFINT_DYN_CTL0_INTENA_MASK 0x80000000UL /**< Ignore enable */
+#define INTELXL_INT_DYN_CTL_INTENA 0x00000001UL /**< Enable */
+#define INTELXL_INT_DYN_CTL_CLEARPBA 0x00000002UL /**< Acknowledge */
+#define INTELXL_INT_DYN_CTL_INTENA_MASK 0x80000000UL /**< Ignore enable */
/** PF Interrupt Zero Linked List Register */
#define INTELXL_PFINT_LNKLST0 0x038500
@@ -773,12 +1033,27 @@ struct intelxl_nic {
unsigned int vsi;
/** Queue set handle */
unsigned int qset;
+ /** Interrupt control register */
+ unsigned int intr;
+ /** MSI-X capability */
+ struct pci_msix msix;
+ /** MSI-X dummy interrupt target */
+ uint32_t msg;
+ /** PCI Express capability offset */
+ unsigned int exp;
/** Admin command queue */
struct intelxl_admin command;
/** Admin event queue */
struct intelxl_admin event;
+ /** Current VF opcode */
+ unsigned int vopcode;
+ /** Current VF return value */
+ int vret;
+ /** Current VF event data buffer */
+ union intelxl_admin_buffer vbuf;
+
/** Transmit descriptor ring */
struct intelxl_ring tx;
/** Receive descriptor ring */
@@ -787,4 +1062,30 @@ struct intelxl_nic {
struct io_buffer *rx_iobuf[INTELXL_RX_NUM_DESC];
};
+extern int intelxl_msix_enable ( struct intelxl_nic *intelxl,
+ struct pci_device *pci );
+extern void intelxl_msix_disable ( struct intelxl_nic *intelxl,
+ struct pci_device *pci );
+extern struct intelxl_admin_descriptor *
+intelxl_admin_command_descriptor ( struct intelxl_nic *intelxl );
+extern union intelxl_admin_buffer *
+intelxl_admin_command_buffer ( struct intelxl_nic *intelxl );
+extern int intelxl_admin_command ( struct intelxl_nic *intelxl );
+extern void intelxl_poll_admin ( struct net_device *netdev );
+extern int intelxl_open_admin ( struct intelxl_nic *intelxl );
+extern void intelxl_reopen_admin ( struct intelxl_nic *intelxl );
+extern void intelxl_close_admin ( struct intelxl_nic *intelxl );
+extern int intelxl_alloc_ring ( struct intelxl_nic *intelxl,
+ struct intelxl_ring *ring );
+extern void intelxl_free_ring ( struct intelxl_nic *intelxl,
+ struct intelxl_ring *ring );
+extern void intelxl_empty_rx ( struct intelxl_nic *intelxl );
+extern int intelxl_transmit ( struct net_device *netdev,
+ struct io_buffer *iobuf );
+extern void intelxl_poll ( struct net_device *netdev );
+
+extern void intelxlvf_admin_event ( struct net_device *netdev,
+ struct intelxl_admin_descriptor *evt,
+ union intelxl_admin_buffer *buf );
+
#endif /* _INTELXL_H */
diff --git a/src/drivers/net/intelxlvf.c b/src/drivers/net/intelxlvf.c
new file mode 100644
index 000000000..8f76daf3d
--- /dev/null
+++ b/src/drivers/net/intelxlvf.c
@@ -0,0 +1,719 @@
+/*
+ * Copyright (C) 2019 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <ipxe/pci.h>
+#include <ipxe/netdevice.h>
+#include <ipxe/ethernet.h>
+#include "intelxlvf.h"
+
+/** @file
+ *
+ * Intel 40 Gigabit Ethernet virtual function network card driver
+ *
+ */
+
+/******************************************************************************
+ *
+ * Device reset
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Reset hardware via PCIe function-level reset
+ *
+ * @v intelxl Intel device
+ */
+static void intelxlvf_reset_flr ( struct intelxl_nic *intelxl,
+ struct pci_device *pci ) {
+ uint16_t control;
+
+ /* Perform a PCIe function-level reset */
+ pci_read_config_word ( pci, ( intelxl->exp + PCI_EXP_DEVCTL ),
+ &control );
+ pci_write_config_word ( pci, ( intelxl->exp + PCI_EXP_DEVCTL ),
+ ( control | PCI_EXP_DEVCTL_FLR ) );
+ mdelay ( INTELXL_RESET_DELAY_MS );
+}
+
+/**
+ * Wait for admin event queue to be torn down
+ *
+ * @v intelxl Intel device
+ * @ret rc Return status code
+ */
+static int intelxlvf_reset_wait_teardown ( struct intelxl_nic *intelxl ) {
+ uint32_t admin_evt_len;
+ unsigned int i;
+
+ /* Wait for admin event queue to be torn down */
+ for ( i = 0 ; i < INTELXLVF_RESET_MAX_WAIT_MS ; i++ ) {
+
+ /* Check admin event queue length register */
+ admin_evt_len = readl ( intelxl->regs + INTELXLVF_ADMIN +
+ INTELXLVF_ADMIN_EVT_LEN );
+ if ( ! ( admin_evt_len & INTELXL_ADMIN_LEN_ENABLE ) )
+ return 0;
+
+ /* Delay */
+ mdelay ( 1 );
+ }
+
+ DBGC ( intelxl, "INTELXL %p timed out waiting for teardown (%#08x)\n",
+ intelxl, admin_evt_len );
+ return -ETIMEDOUT;
+}
+
+/**
+ * Wait for virtual function to be marked as active
+ *
+ * @v intelxl Intel device
+ * @ret rc Return status code
+ */
+static int intelxlvf_reset_wait_active ( struct intelxl_nic *intelxl ) {
+ uint32_t vfgen_rstat;
+ unsigned int vfr_state;
+ unsigned int i;
+
+ /* Wait for virtual function to be marked as active */
+ for ( i = 0 ; i < INTELXLVF_RESET_MAX_WAIT_MS ; i++ ) {
+
+ /* Check status as written by physical function driver */
+ vfgen_rstat = readl ( intelxl->regs + INTELXLVF_VFGEN_RSTAT );
+ vfr_state = INTELXLVF_VFGEN_RSTAT_VFR_STATE ( vfgen_rstat );
+ if ( vfr_state == INTELXLVF_VFGEN_RSTAT_VFR_STATE_ACTIVE )
+ return 0;
+
+ /* Delay */
+ mdelay ( 1 );
+ }
+
+ DBGC ( intelxl, "INTELXL %p timed out waiting for activation "
+ "(%#08x)\n", intelxl, vfgen_rstat );
+ return -ETIMEDOUT;
+}
+
+/**
+ * Reset hardware via admin queue
+ *
+ * @v intelxl Intel device
+ * @ret rc Return status code
+ */
+static int intelxlvf_reset_admin ( struct intelxl_nic *intelxl ) {
+ struct intelxl_admin_descriptor *cmd;
+ int rc;
+
+ /* Populate descriptor */
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_SEND_TO_PF );
+ cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_RESET );
+
+ /* Issue command */
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
+ goto err_command;
+
+ /* Wait for minimum reset time */
+ mdelay ( INTELXL_RESET_DELAY_MS );
+
+ /* Wait for reset to take effect */
+ if ( ( rc = intelxlvf_reset_wait_teardown ( intelxl ) ) != 0 )
+ goto err_teardown;
+
+ /* Wait for virtual function to become active */
+ if ( ( rc = intelxlvf_reset_wait_active ( intelxl ) ) != 0 )
+ goto err_active;
+
+ err_active:
+ err_teardown:
+ intelxl_reopen_admin ( intelxl );
+ err_command:
+ return rc;
+}
+
+/******************************************************************************
+ *
+ * Admin queue
+ *
+ ******************************************************************************
+ */
+
+/** Admin command queue register offsets */
+static const struct intelxl_admin_offsets intelxlvf_admin_command_offsets = {
+ .bal = INTELXLVF_ADMIN_CMD_BAL,
+ .bah = INTELXLVF_ADMIN_CMD_BAH,
+ .len = INTELXLVF_ADMIN_CMD_LEN,
+ .head = INTELXLVF_ADMIN_CMD_HEAD,
+ .tail = INTELXLVF_ADMIN_CMD_TAIL,
+};
+
+/** Admin event queue register offsets */
+static const struct intelxl_admin_offsets intelxlvf_admin_event_offsets = {
+ .bal = INTELXLVF_ADMIN_EVT_BAL,
+ .bah = INTELXLVF_ADMIN_EVT_BAH,
+ .len = INTELXLVF_ADMIN_EVT_LEN,
+ .head = INTELXLVF_ADMIN_EVT_HEAD,
+ .tail = INTELXLVF_ADMIN_EVT_TAIL,
+};
+
+/**
+ * Issue admin queue virtual function command
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int intelxlvf_admin_command ( struct net_device *netdev ) {
+ struct intelxl_nic *intelxl = netdev->priv;
+ struct intelxl_admin *admin = &intelxl->command;
+ struct intelxl_admin_descriptor *cmd;
+ unsigned int i;
+ int rc;
+
+ /* Populate descriptor */
+ cmd = &admin->desc[ admin->index % INTELXL_ADMIN_NUM_DESC ];
+ cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_SEND_TO_PF );
+
+ /* Record opcode */
+ intelxl->vopcode = le32_to_cpu ( cmd->vopcode );
+
+ /* Issue command */
+ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
+ goto err_command;
+
+ /* Wait for response */
+ for ( i = 0 ; i < INTELXLVF_ADMIN_MAX_WAIT_MS ; i++ ) {
+
+ /* Poll admin event queue */
+ intelxl_poll_admin ( netdev );
+
+ /* If response has not arrived, delay 1ms and retry */
+ if ( intelxl->vopcode ) {
+ mdelay ( 1 );
+ continue;
+ }
+
+ /* Check for errors */
+ if ( intelxl->vret != 0 )
+ return -EIO;
+
+ return 0;
+ }
+
+ rc = -ETIMEDOUT;
+ DBGC ( intelxl, "INTELXL %p timed out waiting for admin VF command "
+ "%#x\n", intelxl, intelxl->vopcode );
+ err_command:
+ intelxl->vopcode = 0;
+ return rc;
+}
+
+/**
+ * Handle link status event
+ *
+ * @v netdev Network device
+ * @v link Link status
+ */
+static void intelxlvf_admin_link ( struct net_device *netdev,
+ struct intelxl_admin_vf_status_link *link ) {
+ struct intelxl_nic *intelxl = netdev->priv;
+
+ DBGC ( intelxl, "INTELXL %p link %#02x speed %#02x\n", intelxl,
+ link->status, link->speed );
+
+ /* Update network device */
+ if ( link->status ) {
+ netdev_link_up ( netdev );
+ } else {
+ netdev_link_down ( netdev );
+ }
+}
+
+/**
+ * Handle status change event
+ *
+ * @v netdev Network device
+ * @v stat Status change event
+ */
+static void
+intelxlvf_admin_status ( struct net_device *netdev,
+ struct intelxl_admin_vf_status_buffer *stat ) {
+ struct intelxl_nic *intelxl = netdev->priv;
+
+ /* Handle event */
+ switch ( stat->event ) {
+ case cpu_to_le32 ( INTELXL_ADMIN_VF_STATUS_LINK ):
+ intelxlvf_admin_link ( netdev, &stat->data.link );
+ break;
+ default:
+ DBGC ( intelxl, "INTELXL %p unrecognised status change "
+ "event %#x:\n", intelxl, le32_to_cpu ( stat->event ) );
+ DBGC_HDA ( intelxl, 0, stat, sizeof ( *stat ) );
+ break;
+ }
+}
+
+/**
+ * Handle virtual function event
+ *
+ * @v netdev Network device
+ * @v evt Admin queue event descriptor
+ * @v buf Admin queue event data buffer
+ */
+void intelxlvf_admin_event ( struct net_device *netdev,
+ struct intelxl_admin_descriptor *evt,
+ union intelxl_admin_buffer *buf ) {
+ struct intelxl_nic *intelxl = netdev->priv;
+ unsigned int vopcode = le32_to_cpu ( evt->vopcode );
+
+ /* Record command response if applicable */
+ if ( vopcode == intelxl->vopcode ) {
+ memcpy ( &intelxl->vbuf, buf, sizeof ( intelxl->vbuf ) );
+ intelxl->vopcode = 0;
+ intelxl->vret = le32_to_cpu ( evt->vret );
+ if ( intelxl->vret != 0 ) {
+ DBGC ( intelxl, "INTELXL %p admin VF command %#x "
+ "error %d\n", intelxl, vopcode, intelxl->vret );
+ DBGC_HDA ( intelxl, virt_to_bus ( evt ), evt,
+ sizeof ( *evt ) );
+ DBGC_HDA ( intelxl, virt_to_bus ( buf ), buf,
+ le16_to_cpu ( evt->len ) );
+ }
+ return;
+ }
+
+ /* Handle unsolicited events */
+ switch ( vopcode ) {
+ case INTELXL_ADMIN_VF_STATUS:
+ intelxlvf_admin_status ( netdev, &buf->stat );
+ break;
+ default:
+ DBGC ( intelxl, "INTELXL %p unrecognised VF event %#x:\n",
+ intelxl, vopcode );
+ DBGC_HDA ( intelxl, 0, evt, sizeof ( *evt ) );
+ DBGC_HDA ( intelxl, 0, buf, le16_to_cpu ( evt->len ) );
+ break;
+ }
+}
+
+/**
+ * Get resources
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int intelxlvf_admin_get_resources ( struct net_device *netdev ) {
+ struct intelxl_nic *intelxl = netdev->priv;
+ struct intelxl_admin_descriptor *cmd;
+ struct intelxl_admin_vf_get_resources_buffer *res;
+ int rc;
+
+ /* Populate descriptor */
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_GET_RESOURCES );
+
+ /* Issue command */
+ if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 )
+ return rc;
+
+ /* Parse response */
+ res = &intelxl->vbuf.res;
+ intelxl->vsi = le16_to_cpu ( res->vsi );
+ memcpy ( netdev->hw_addr, res->mac, ETH_ALEN );
+ DBGC ( intelxl, "INTELXL %p VSI %#04x\n", intelxl, intelxl->vsi );
+
+ return 0;
+}
+
+/******************************************************************************
+ *
+ * Network device interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Configure queues
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int intelxlvf_admin_configure ( struct net_device *netdev ) {
+ struct intelxl_nic *intelxl = netdev->priv;
+ struct intelxl_admin_descriptor *cmd;
+ union intelxl_admin_buffer *buf;
+ int rc;
+
+ /* Populate descriptor */
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_CONFIGURE );
+ cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
+ cmd->len = cpu_to_le16 ( sizeof ( buf->cfg ) );
+ buf = intelxl_admin_command_buffer ( intelxl );
+ buf->cfg.vsi = cpu_to_le16 ( intelxl->vsi );
+ buf->cfg.count = cpu_to_le16 ( 1 );
+ buf->cfg.tx.vsi = cpu_to_le16 ( intelxl->vsi );
+ buf->cfg.tx.count = cpu_to_le16 ( INTELXL_TX_NUM_DESC );
+ buf->cfg.tx.base = cpu_to_le64 ( virt_to_bus ( intelxl->tx.desc.raw ) );
+ buf->cfg.rx.vsi = cpu_to_le16 ( intelxl->vsi );
+ buf->cfg.rx.count = cpu_to_le32 ( INTELXL_RX_NUM_DESC );
+ buf->cfg.rx.len = cpu_to_le32 ( intelxl->mfs );
+ buf->cfg.rx.mfs = cpu_to_le32 ( intelxl->mfs );
+ buf->cfg.rx.base = cpu_to_le64 ( virt_to_bus ( intelxl->rx.desc.raw ) );
+
+ /* Issue command */
+ if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Configure IRQ mapping
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int intelxlvf_admin_irq_map ( struct net_device *netdev ) {
+ struct intelxl_nic *intelxl = netdev->priv;
+ struct intelxl_admin_descriptor *cmd;
+ union intelxl_admin_buffer *buf;
+ int rc;
+
+ /* Populate descriptor */
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_IRQ_MAP );
+ cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
+ cmd->len = cpu_to_le16 ( sizeof ( buf->irq ) );
+ buf = intelxl_admin_command_buffer ( intelxl );
+ buf->irq.count = cpu_to_le16 ( 1 );
+ buf->irq.vsi = cpu_to_le16 ( intelxl->vsi );
+ buf->irq.rxmap = cpu_to_le16 ( 0x0001 );
+ buf->irq.txmap = cpu_to_le16 ( 0x0001 );
+
+ /* Issue command */
+ if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Enable/disable queues
+ *
+ * @v netdev Network device
+ * @v enable Enable queues
+ * @ret rc Return status code
+ */
+static int intelxlvf_admin_queues ( struct net_device *netdev, int enable ) {
+ struct intelxl_nic *intelxl = netdev->priv;
+ struct intelxl_admin_descriptor *cmd;
+ union intelxl_admin_buffer *buf;
+ int rc;
+
+ /* Populate descriptor */
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->vopcode = ( enable ? cpu_to_le32 ( INTELXL_ADMIN_VF_ENABLE ) :
+ cpu_to_le32 ( INTELXL_ADMIN_VF_DISABLE ) );
+ cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
+ cmd->len = cpu_to_le16 ( sizeof ( buf->queues ) );
+ buf = intelxl_admin_command_buffer ( intelxl );
+ buf->queues.vsi = cpu_to_le16 ( intelxl->vsi );
+ buf->queues.rx = cpu_to_le32 ( 1 );
+ buf->queues.tx = cpu_to_le32 ( 1 );
+
+ /* Issue command */
+ if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Configure promiscuous mode
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int intelxlvf_admin_promisc ( struct net_device *netdev ) {
+ struct intelxl_nic *intelxl = netdev->priv;
+ struct intelxl_admin_descriptor *cmd;
+ union intelxl_admin_buffer *buf;
+ int rc;
+
+ /* Populate descriptor */
+ cmd = intelxl_admin_command_descriptor ( intelxl );
+ cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_PROMISC );
+ cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
+ cmd->len = cpu_to_le16 ( sizeof ( buf->promisc ) );
+ buf = intelxl_admin_command_buffer ( intelxl );
+ buf->promisc.vsi = cpu_to_le16 ( intelxl->vsi );
+ buf->promisc.flags = cpu_to_le16 ( INTELXL_ADMIN_PROMISC_FL_UNICAST |
+ INTELXL_ADMIN_PROMISC_FL_MULTICAST );
+
+ /* Issue command */
+ if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 )
+ return rc;
+
+ return 0;
+}
+
+/**
+ * Open network device
+ *
+ * @v netdev Network device
+ * @ret rc Return status code
+ */
+static int intelxlvf_open ( struct net_device *netdev ) {
+ struct intelxl_nic *intelxl = netdev->priv;
+ int rc;
+
+ /* Calculate maximum frame size */
+ intelxl->mfs = ( ( ETH_HLEN + netdev->mtu + 4 /* CRC */ +
+ INTELXL_ALIGN - 1 ) & ~( INTELXL_ALIGN - 1 ) );
+
+ /* Allocate transmit descriptor ring */
+ if ( ( rc = intelxl_alloc_ring ( intelxl, &intelxl->tx ) ) != 0 )
+ goto err_alloc_tx;
+
+ /* Allocate receive descriptor ring */
+ if ( ( rc = intelxl_alloc_ring ( intelxl, &intelxl->rx ) ) != 0 )
+ goto err_alloc_rx;
+
+ /* Configure queues */
+ if ( ( rc = intelxlvf_admin_configure ( netdev ) ) != 0 )
+ goto err_configure;
+
+ /* Configure IRQ map */
+ if ( ( rc = intelxlvf_admin_irq_map ( netdev ) ) != 0 )
+ goto err_irq_map;
+
+ /* Enable queues */
+ if ( ( rc = intelxlvf_admin_queues ( netdev, 1 ) ) != 0 )
+ goto err_enable;
+
+ /* Configure promiscuous mode */
+ if ( ( rc = intelxlvf_admin_promisc ( netdev ) ) != 0 )
+ goto err_promisc;
+
+ return 0;
+
+ err_promisc:
+ intelxlvf_admin_queues ( netdev, INTELXL_ADMIN_VF_DISABLE );
+ err_enable:
+ err_irq_map:
+ err_configure:
+ intelxl_free_ring ( intelxl, &intelxl->rx );
+ err_alloc_rx:
+ intelxl_free_ring ( intelxl, &intelxl->tx );
+ err_alloc_tx:
+ return rc;
+}
+
+/**
+ * Close network device
+ *
+ * @v netdev Network device
+ */
+static void intelxlvf_close ( struct net_device *netdev ) {
+ struct intelxl_nic *intelxl = netdev->priv;
+ int rc;
+
+ /* Disable queues */
+ if ( ( rc = intelxlvf_admin_queues ( netdev, 0 ) ) != 0 ) {
+ /* Leak memory; there's nothing else we can do */
+ return;
+ }
+
+ /* Free receive descriptor ring */
+ intelxl_free_ring ( intelxl, &intelxl->rx );
+
+ /* Free transmit descriptor ring */
+ intelxl_free_ring ( intelxl, &intelxl->tx );
+
+ /* Discard any unused receive buffers */
+ intelxl_empty_rx ( intelxl );
+}
+
+/** Network device operations */
+static struct net_device_operations intelxlvf_operations = {
+ .open = intelxlvf_open,
+ .close = intelxlvf_close,
+ .transmit = intelxl_transmit,
+ .poll = intelxl_poll,
+};
+
+/******************************************************************************
+ *
+ * PCI interface
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Probe PCI device
+ *
+ * @v pci PCI device
+ * @ret rc Return status code
+ */
+static int intelxlvf_probe ( struct pci_device *pci ) {
+ struct net_device *netdev;
+ struct intelxl_nic *intelxl;
+ int rc;
+
+ /* Allocate and initialise net device */
+ netdev = alloc_etherdev ( sizeof ( *intelxl ) );
+ if ( ! netdev ) {
+ rc = -ENOMEM;
+ goto err_alloc;
+ }
+ netdev_init ( netdev, &intelxlvf_operations );
+ intelxl = netdev->priv;
+ pci_set_drvdata ( pci, netdev );
+ netdev->dev = &pci->dev;
+ memset ( intelxl, 0, sizeof ( *intelxl ) );
+ intelxl->intr = INTELXLVF_VFINT_DYN_CTL0;
+ intelxl_init_admin ( &intelxl->command, INTELXLVF_ADMIN,
+ &intelxlvf_admin_command_offsets );
+ intelxl_init_admin ( &intelxl->event, INTELXLVF_ADMIN,
+ &intelxlvf_admin_event_offsets );
+ intelxlvf_init_ring ( &intelxl->tx, INTELXL_TX_NUM_DESC,
+ sizeof ( intelxl->tx.desc.tx[0] ),
+ INTELXLVF_QTX_TAIL );
+ intelxlvf_init_ring ( &intelxl->rx, INTELXL_RX_NUM_DESC,
+ sizeof ( intelxl->rx.desc.rx[0] ),
+ INTELXLVF_QRX_TAIL );
+
+ /* Fix up PCI device */
+ adjust_pci_device ( pci );
+
+ /* Map registers */
+ intelxl->regs = ioremap ( pci->membase, INTELXLVF_BAR_SIZE );
+ if ( ! intelxl->regs ) {
+ rc = -ENODEV;
+ goto err_ioremap;
+ }
+
+ /* Locate PCI Express capability */
+ intelxl->exp = pci_find_capability ( pci, PCI_CAP_ID_EXP );
+ if ( ! intelxl->exp ) {
+ DBGC ( intelxl, "INTELXL %p missing PCIe capability\n",
+ intelxl );
+ rc = -ENXIO;
+ goto err_exp;
+ }
+
+ /* Reset the function via PCIe FLR */
+ intelxlvf_reset_flr ( intelxl, pci );
+
+ /* Enable MSI-X dummy interrupt */
+ if ( ( rc = intelxl_msix_enable ( intelxl, pci ) ) != 0 )
+ goto err_msix;
+
+ /* Open admin queues */
+ if ( ( rc = intelxl_open_admin ( intelxl ) ) != 0 )
+ goto err_open_admin;
+
+ /* Reset the function via admin queue */
+ if ( ( rc = intelxlvf_reset_admin ( intelxl ) ) != 0 )
+ goto err_reset_admin;
+
+ /* Get MAC address */
+ if ( ( rc = intelxlvf_admin_get_resources ( netdev ) ) != 0 )
+ goto err_get_resources;
+
+ /* Register network device */
+ if ( ( rc = register_netdev ( netdev ) ) != 0 )
+ goto err_register_netdev;
+
+ return 0;
+
+ unregister_netdev ( netdev );
+ err_register_netdev:
+ err_get_resources:
+ err_reset_admin:
+ intelxl_close_admin ( intelxl );
+ err_open_admin:
+ intelxl_msix_disable ( intelxl, pci );
+ err_msix:
+ intelxlvf_reset_flr ( intelxl, pci );
+ err_exp:
+ iounmap ( intelxl->regs );
+ err_ioremap:
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+ err_alloc:
+ return rc;
+}
+
+/**
+ * Remove PCI device
+ *
+ * @v pci PCI device
+ */
+static void intelxlvf_remove ( struct pci_device *pci ) {
+ struct net_device *netdev = pci_get_drvdata ( pci );
+ struct intelxl_nic *intelxl = netdev->priv;
+
+ /* Unregister network device */
+ unregister_netdev ( netdev );
+
+ /* Reset the function via admin queue */
+ intelxlvf_reset_admin ( intelxl );
+
+ /* Close admin queues */
+ intelxl_close_admin ( intelxl );
+
+ /* Disable MSI-X dummy interrupt */
+ intelxl_msix_disable ( intelxl, pci );
+
+ /* Reset the function via PCIe FLR */
+ intelxlvf_reset_flr ( intelxl, pci );
+
+ /* Free network device */
+ iounmap ( intelxl->regs );
+ netdev_nullify ( netdev );
+ netdev_put ( netdev );
+}
+
+/** PCI device IDs */
+static struct pci_device_id intelxlvf_nics[] = {
+ PCI_ROM ( 0x8086, 0x154c, "xl710-vf", "XL710 VF", 0 ),
+ PCI_ROM ( 0x8086, 0x1571, "xl710-vf-hv", "XL710 VF (Hyper-V)", 0 ),
+ PCI_ROM ( 0x8086, 0x1889, "xl710-vf-ad", "XL710 VF (adaptive)", 0 ),
+ PCI_ROM ( 0x8086, 0x37cd, "x722-vf", "X722 VF", 0 ),
+ PCI_ROM ( 0x8086, 0x37d9, "x722-vf-hv", "X722 VF (Hyper-V)", 0 ),
+};
+
+/** PCI driver */
+struct pci_driver intelxlvf_driver __pci_driver = {
+ .ids = intelxlvf_nics,
+ .id_count = ( sizeof ( intelxlvf_nics ) /
+ sizeof ( intelxlvf_nics[0] ) ),
+ .probe = intelxlvf_probe,
+ .remove = intelxlvf_remove,
+};
diff --git a/src/drivers/net/intelxlvf.h b/src/drivers/net/intelxlvf.h
new file mode 100644
index 000000000..ffcae5674
--- /dev/null
+++ b/src/drivers/net/intelxlvf.h
@@ -0,0 +1,86 @@
+#ifndef _INTELXLVF_H
+#define _INTELXLVF_H
+
+/** @file
+ *
+ * Intel 40 Gigabit Ethernet virtual function network card driver
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include "intelxl.h"
+
+/** BAR size */
+#define INTELXLVF_BAR_SIZE 0x10000
+
+/** Transmit Queue Tail Register */
+#define INTELXLVF_QTX_TAIL 0x00000
+
+/** Receive Queue Tail Register */
+#define INTELXLVF_QRX_TAIL 0x02000
+
+/** VF Interrupt Zero Dynamic Control Register */
+#define INTELXLVF_VFINT_DYN_CTL0 0x5c00
+
+/** VF Admin Queue register block */
+#define INTELXLVF_ADMIN 0x6000
+
+/** Admin Command Queue Base Address Low Register (offset) */
+#define INTELXLVF_ADMIN_CMD_BAL 0x1c00
+
+/** Admin Command Queue Base Address High Register (offset) */
+#define INTELXLVF_ADMIN_CMD_BAH 0x1800
+
+/** Admin Command Queue Length Register (offset) */
+#define INTELXLVF_ADMIN_CMD_LEN 0x0800
+
+/** Admin Command Queue Head Register (offset) */
+#define INTELXLVF_ADMIN_CMD_HEAD 0x0400
+
+/** Admin Command Queue Tail Register (offset) */
+#define INTELXLVF_ADMIN_CMD_TAIL 0x2400
+
+/** Admin Event Queue Base Address Low Register (offset) */
+#define INTELXLVF_ADMIN_EVT_BAL 0x0c00
+
+/** Admin Event Queue Base Address High Register (offset) */
+#define INTELXLVF_ADMIN_EVT_BAH 0x0000
+
+/** Admin Event Queue Length Register (offset) */
+#define INTELXLVF_ADMIN_EVT_LEN 0x2000
+
+/** Admin Event Queue Head Register (offset) */
+#define INTELXLVF_ADMIN_EVT_HEAD 0x1400
+
+/** Admin Event Queue Tail Register (offset) */
+#define INTELXLVF_ADMIN_EVT_TAIL 0x1000
+
+/** Maximum time to wait for a VF admin request to complete */
+#define INTELXLVF_ADMIN_MAX_WAIT_MS 2000
+
+/** VF Reset Status Register */
+#define INTELXLVF_VFGEN_RSTAT 0x8800
+#define INTELXLVF_VFGEN_RSTAT_VFR_STATE(x) ( (x) & 0x3 )
+#define INTELXLVF_VFGEN_RSTAT_VFR_STATE_ACTIVE 0x2
+
+/** Maximum time to wait for reset to complete */
+#define INTELXLVF_RESET_MAX_WAIT_MS 1000
+
+/**
+ * Initialise descriptor ring
+ *
+ * @v ring Descriptor ring
+ * @v count Number of descriptors
+ * @v len Length of a single descriptor
+ * @v tail Tail register offset
+ */
+static inline __attribute__ (( always_inline)) void
+intelxlvf_init_ring ( struct intelxl_ring *ring, unsigned int count,
+ size_t len, unsigned int tail ) {
+
+ ring->len = ( count * len );
+ ring->tail = tail;
+}
+
+#endif /* _INTELXLVF_H */
diff --git a/src/drivers/net/lan78xx.c b/src/drivers/net/lan78xx.c
index 3f705203e..a8cf57408 100644
--- a/src/drivers/net/lan78xx.c
+++ b/src/drivers/net/lan78xx.c
@@ -97,6 +97,10 @@ static int lan78xx_fetch_mac ( struct smscusb_device *smscusb ) {
if ( ( rc = smscusb_otp_fetch_mac ( smscusb, LAN78XX_OTP_BASE ) ) == 0 )
return 0;
+ /* Read MAC address from device tree, if present */
+ if ( ( rc = smscusb_fdt_fetch_mac ( smscusb ) ) == 0 )
+ return 0;
+
/* Otherwise, generate a random MAC address */
eth_random_addr ( netdev->hw_addr );
DBGC ( smscusb, "LAN78XX %p using random MAC %s\n",
diff --git a/src/drivers/net/smsc95xx.c b/src/drivers/net/smsc95xx.c
index 9b09657db..3ec49584d 100644
--- a/src/drivers/net/smsc95xx.c
+++ b/src/drivers/net/smsc95xx.c
@@ -173,6 +173,10 @@ static int smsc95xx_fetch_mac ( struct smscusb_device *smscusb ) {
SMSC95XX_E2P_BASE ) ) == 0 )
return 0;
+ /* Read MAC address from device tree, if present */
+ if ( ( rc = smscusb_fdt_fetch_mac ( smscusb ) ) == 0 )
+ return 0;
+
/* Construct MAC address for Honeywell VM3, if applicable */
if ( ( rc = smsc95xx_vm3_fetch_mac ( smscusb ) ) == 0 )
return 0;
diff --git a/src/drivers/net/smscusb.c b/src/drivers/net/smscusb.c
index 538d338c4..c639c58c1 100644
--- a/src/drivers/net/smscusb.c
+++ b/src/drivers/net/smscusb.c
@@ -30,6 +30,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/usbnet.h>
#include <ipxe/ethernet.h>
#include <ipxe/profile.h>
+#include <ipxe/fdt.h>
#include "smscusb.h"
/** @file
@@ -441,6 +442,39 @@ int smscusb_otp_fetch_mac ( struct smscusb_device *smscusb,
/******************************************************************************
*
+ * Device tree
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Fetch MAC address from device tree
+ *
+ * @v smscusb SMSC USB device
+ * @ret rc Return status code
+ */
+int smscusb_fdt_fetch_mac ( struct smscusb_device *smscusb ) {
+ struct net_device *netdev = smscusb->netdev;
+ unsigned int offset;
+ int rc;
+
+ /* Look for "ethernet[0]" alias */
+ if ( ( rc = fdt_alias ( "ethernet", &offset ) != 0 ) &&
+ ( rc = fdt_alias ( "ethernet0", &offset ) != 0 ) ) {
+ return rc;
+ }
+
+ /* Fetch MAC address */
+ if ( ( rc = fdt_mac ( offset, netdev ) ) != 0 )
+ return rc;
+
+ DBGC ( smscusb, "SMSCUSB %p using FDT MAC %s\n",
+ smscusb, eth_ntoa ( netdev->hw_addr ) );
+ return 0;
+}
+
+/******************************************************************************
+ *
* MII access
*
******************************************************************************
diff --git a/src/drivers/net/smscusb.h b/src/drivers/net/smscusb.h
index b5d9ad3fc..e866bb747 100644
--- a/src/drivers/net/smscusb.h
+++ b/src/drivers/net/smscusb.h
@@ -287,6 +287,7 @@ extern int smscusb_eeprom_fetch_mac ( struct smscusb_device *smscusb,
unsigned int e2p_base );
extern int smscusb_otp_fetch_mac ( struct smscusb_device *smscusb,
unsigned int otp_base );
+extern int smscusb_fdt_fetch_mac ( struct smscusb_device *smscusb );
extern int smscusb_mii_check_link ( struct smscusb_device *smscusb );
extern int smscusb_mii_open ( struct smscusb_device *smscusb,
unsigned int phy_mask, unsigned int intrs );