diff options
Diffstat (limited to 'src/drivers')
| -rw-r--r-- | src/drivers/bus/pcimsix.c | 251 | ||||
| -rwxr-xr-x | src/drivers/infiniband/golan.c | 3 | ||||
| -rw-r--r-- | src/drivers/infiniband/hermon.c | 18 | ||||
| -rw-r--r-- | src/drivers/net/intel.c | 8 | ||||
| -rw-r--r-- | src/drivers/net/intelxl.c | 768 | ||||
| -rw-r--r-- | src/drivers/net/intelxl.h | 373 | ||||
| -rw-r--r-- | src/drivers/net/intelxlvf.c | 719 | ||||
| -rw-r--r-- | src/drivers/net/intelxlvf.h | 86 | ||||
| -rw-r--r-- | src/drivers/net/lan78xx.c | 4 | ||||
| -rw-r--r-- | src/drivers/net/smsc95xx.c | 4 | ||||
| -rw-r--r-- | src/drivers/net/smscusb.c | 34 | ||||
| -rw-r--r-- | src/drivers/net/smscusb.h | 1 |
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 ); |
