diff options
| author | Simon Rettberg | 2022-10-21 17:22:09 +0200 |
|---|---|---|
| committer | Simon Rettberg | 2022-10-21 17:22:09 +0200 |
| commit | 80109b804a700384799d54134ceebf4f40a8b7a3 (patch) | |
| tree | bbdf311ed02dd2c5c1f0a8d1997519ed2c24d93e /src/drivers/net | |
| parent | Merge branch 'master' into openslx (diff) | |
| parent | [tls] Add support for Ephemeral Diffie-Hellman key exchange (diff) | |
| download | ipxe-80109b804a700384799d54134ceebf4f40a8b7a3.tar.gz ipxe-80109b804a700384799d54134ceebf4f40a8b7a3.tar.xz ipxe-80109b804a700384799d54134ceebf4f40a8b7a3.zip | |
Merge branch 'master' into openslx
Diffstat (limited to 'src/drivers/net')
| -rw-r--r-- | src/drivers/net/ecm.c | 28 | ||||
| -rw-r--r-- | src/drivers/net/ecm.h | 2 | ||||
| -rw-r--r-- | src/drivers/net/ena.c | 186 | ||||
| -rw-r--r-- | src/drivers/net/ena.h | 137 | ||||
| -rw-r--r-- | src/drivers/net/ice.c | 986 | ||||
| -rw-r--r-- | src/drivers/net/ice.h | 565 | ||||
| -rw-r--r-- | src/drivers/net/intelxl.c | 398 | ||||
| -rw-r--r-- | src/drivers/net/intelxl.h | 355 | ||||
| -rw-r--r-- | src/drivers/net/intelxlvf.c | 346 | ||||
| -rw-r--r-- | src/drivers/net/intelxlvf.h | 335 | ||||
| -rw-r--r-- | src/drivers/net/ncm.c | 2 |
11 files changed, 2802 insertions, 538 deletions
diff --git a/src/drivers/net/ecm.c b/src/drivers/net/ecm.c index 826b3b16d..68ac962ab 100644 --- a/src/drivers/net/ecm.c +++ b/src/drivers/net/ecm.c @@ -84,24 +84,18 @@ ecm_ethernet_descriptor ( struct usb_configuration_descriptor *config, * * @v func USB function * @v desc Ethernet functional descriptor - * @v hw_addr Hardware address to fill in + * @v netdev Network device * @ret rc Return status code */ int ecm_fetch_mac ( struct usb_function *func, - struct ecm_ethernet_descriptor *desc, uint8_t *hw_addr ) { + struct ecm_ethernet_descriptor *desc, + struct net_device *netdev ) { struct usb_device *usb = func->usb; char buf[ base16_encoded_len ( ETH_ALEN ) + 1 /* NUL */ ]; + uint8_t amac[ETH_ALEN]; int len; int rc; - /* Use system-specific MAC address, if present and not already used */ - if ( ( ( rc = acpi_mac ( hw_addr ) ) == 0 ) && - ! find_netdev_by_ll_addr ( ðernet_protocol, hw_addr ) ) { - DBGC ( usb, "USB %s using system-specific MAC %s\n", - func->name, eth_ntoa ( hw_addr ) ); - return 0; - } - /* Fetch MAC address string */ len = usb_get_string_descriptor ( usb, desc->mac, 0, buf, sizeof ( buf ) ); @@ -118,7 +112,7 @@ int ecm_fetch_mac ( struct usb_function *func, } /* Decode MAC address */ - len = base16_decode ( buf, hw_addr, ETH_ALEN ); + len = base16_decode ( buf, netdev->hw_addr, ETH_ALEN ); if ( len < 0 ) { rc = len; DBGC ( usb, "USB %s could not decode ECM MAC \"%s\": %s\n", @@ -126,6 +120,16 @@ int ecm_fetch_mac ( struct usb_function *func, return rc; } + /* Apply system-specific MAC address as current link-layer + * address, if present and not already used. + */ + if ( ( ( rc = acpi_mac ( amac ) ) == 0 ) && + ! find_netdev_by_ll_addr ( ðernet_protocol, amac ) ) { + memcpy ( netdev->ll_addr, amac, ETH_ALEN ); + DBGC ( usb, "USB %s using system-specific MAC %s\n", + func->name, eth_ntoa ( netdev->ll_addr ) ); + } + return 0; } @@ -474,7 +478,7 @@ static int ecm_probe ( struct usb_function *func, } /* Fetch MAC address */ - if ( ( rc = ecm_fetch_mac ( func, ethernet, netdev->hw_addr ) ) != 0 ) { + if ( ( rc = ecm_fetch_mac ( func, ethernet, netdev ) ) != 0 ) { DBGC ( ecm, "ECM %p could not fetch MAC address: %s\n", ecm, strerror ( rc ) ); goto err_fetch_mac; diff --git a/src/drivers/net/ecm.h b/src/drivers/net/ecm.h index 0ad3ddb95..a7d03cf94 100644 --- a/src/drivers/net/ecm.h +++ b/src/drivers/net/ecm.h @@ -88,6 +88,6 @@ ecm_ethernet_descriptor ( struct usb_configuration_descriptor *config, struct usb_interface_descriptor *interface ); extern int ecm_fetch_mac ( struct usb_function *func, struct ecm_ethernet_descriptor *desc, - uint8_t *hw_addr ); + struct net_device *netdev ); #endif /* _ECM_H */ diff --git a/src/drivers/net/ena.c b/src/drivers/net/ena.c index 85da1c090..22e7e1e30 100644 --- a/src/drivers/net/ena.c +++ b/src/drivers/net/ena.c @@ -24,6 +24,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> +#include <stdio.h> #include <string.h> #include <unistd.h> #include <errno.h> @@ -34,6 +35,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/iobuf.h> #include <ipxe/malloc.h> #include <ipxe/pci.h> +#include <ipxe/pcibridge.h> +#include <ipxe/version.h> #include "ena.h" /** @file @@ -359,6 +362,7 @@ static int ena_create_sq ( struct ena_nic *ena, struct ena_sq *sq, struct ena_cq *cq ) { union ena_aq_req *req; union ena_acq_rsp *rsp; + unsigned int i; int rc; /* Allocate submission queue entries */ @@ -391,11 +395,20 @@ static int ena_create_sq ( struct ena_nic *ena, struct ena_sq *sq, sq->prod = 0; sq->phase = ENA_SQE_PHASE; - DBGC ( ena, "ENA %p %s SQ%d at [%08lx,%08lx) db +%04x CQ%d\n", + /* Calculate fill level */ + sq->fill = sq->max; + if ( sq->fill > cq->actual ) + sq->fill = cq->actual; + + /* Initialise buffer ID ring */ + for ( i = 0 ; i < sq->count ; i++ ) + sq->ids[i] = i; + + DBGC ( ena, "ENA %p %s SQ%d at [%08lx,%08lx) fill %d db +%04x CQ%d\n", ena, ena_direction ( sq->direction ), sq->id, virt_to_phys ( sq->sqe.raw ), ( virt_to_phys ( sq->sqe.raw ) + sq->len ), - sq->doorbell, cq->id ); + sq->fill, sq->doorbell, cq->id ); return 0; err_admin: @@ -459,6 +472,7 @@ static int ena_create_cq ( struct ena_nic *ena, struct ena_cq *cq ) { req->header.opcode = ENA_CREATE_CQ; req->create_cq.size = cq->size; req->create_cq.count = cpu_to_le16 ( cq->requested ); + req->create_cq.vector = cpu_to_le32 ( ENA_MSIX_NONE ); req->create_cq.address = cpu_to_le64 ( virt_to_bus ( cq->cqe.raw ) ); /* Issue request */ @@ -597,6 +611,32 @@ static int ena_get_device_attributes ( struct net_device *netdev ) { } /** + * Set host attributes + * + * @v ena ENA device + * @ret rc Return status code + */ +static int ena_set_host_attributes ( struct ena_nic *ena ) { + union ena_aq_req *req; + union ena_acq_rsp *rsp; + union ena_feature *feature; + int rc; + + /* Construct request */ + req = ena_admin_req ( ena ); + req->header.opcode = ENA_SET_FEATURE; + req->set_feature.id = ENA_HOST_ATTRIBUTES; + feature = &req->set_feature.feature; + feature->host.info = cpu_to_le64 ( virt_to_bus ( ena->info ) ); + + /* Issue request */ + if ( ( rc = ena_admin ( ena, req, &rsp ) ) != 0 ) + return rc; + + return 0; +} + +/** * Get statistics (for debugging) * * @v ena ENA device @@ -652,13 +692,14 @@ static void ena_refill_rx ( struct net_device *netdev ) { struct ena_nic *ena = netdev->priv; struct io_buffer *iobuf; struct ena_rx_sqe *sqe; - unsigned int index; physaddr_t address; size_t len = netdev->max_pkt_len; unsigned int refilled = 0; + unsigned int index; + unsigned int id; /* Refill queue */ - while ( ( ena->rx.sq.prod - ena->rx.cq.cons ) < ENA_RX_COUNT ) { + while ( ( ena->rx.sq.prod - ena->rx.cq.cons ) < ena->rx.sq.fill ) { /* Allocate I/O buffer */ iobuf = alloc_iob ( len ); @@ -667,14 +708,15 @@ static void ena_refill_rx ( struct net_device *netdev ) { break; } - /* Get next submission queue entry */ + /* Get next submission queue entry and buffer ID */ index = ( ena->rx.sq.prod % ENA_RX_COUNT ); sqe = &ena->rx.sq.sqe.rx[index]; + id = ena->rx_ids[index]; /* Construct submission queue entry */ address = virt_to_bus ( iobuf->data ); sqe->len = cpu_to_le16 ( len ); - sqe->id = cpu_to_le16 ( ena->rx.sq.prod ); + sqe->id = cpu_to_le16 ( id ); sqe->address = cpu_to_le64 ( address ); wmb(); sqe->flags = ( ENA_SQE_FIRST | ENA_SQE_LAST | ENA_SQE_CPL | @@ -686,10 +728,10 @@ static void ena_refill_rx ( struct net_device *netdev ) { ena->rx.sq.phase ^= ENA_SQE_PHASE; /* Record I/O buffer */ - assert ( ena->rx_iobuf[index] == NULL ); - ena->rx_iobuf[index] = iobuf; + assert ( ena->rx_iobuf[id] == NULL ); + ena->rx_iobuf[id] = iobuf; - DBGC2 ( ena, "ENA %p RX %d at [%08llx,%08llx)\n", ena, sqe->id, + DBGC2 ( ena, "ENA %p RX %d at [%08llx,%08llx)\n", ena, id, ( ( unsigned long long ) address ), ( ( unsigned long long ) address + len ) ); refilled++; @@ -778,23 +820,25 @@ static void ena_close ( struct net_device *netdev ) { static int ena_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { struct ena_nic *ena = netdev->priv; struct ena_tx_sqe *sqe; - unsigned int index; physaddr_t address; + unsigned int index; + unsigned int id; size_t len; /* Get next submission queue entry */ - if ( ( ena->tx.sq.prod - ena->tx.cq.cons ) >= ENA_TX_COUNT ) { + if ( ( ena->tx.sq.prod - ena->tx.cq.cons ) >= ena->tx.sq.fill ) { DBGC ( ena, "ENA %p out of transmit descriptors\n", ena ); return -ENOBUFS; } index = ( ena->tx.sq.prod % ENA_TX_COUNT ); sqe = &ena->tx.sq.sqe.tx[index]; + id = ena->tx_ids[index]; /* Construct submission queue entry */ address = virt_to_bus ( iobuf->data ); len = iob_len ( iobuf ); sqe->len = cpu_to_le16 ( len ); - sqe->id = ena->tx.sq.prod; + sqe->id = cpu_to_le16 ( id ); sqe->address = cpu_to_le64 ( address ); wmb(); sqe->flags = ( ENA_SQE_FIRST | ENA_SQE_LAST | ENA_SQE_CPL | @@ -806,10 +850,14 @@ static int ena_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { if ( ( ena->tx.sq.prod % ENA_TX_COUNT ) == 0 ) ena->tx.sq.phase ^= ENA_SQE_PHASE; + /* Record I/O buffer */ + assert ( ena->tx_iobuf[id] == NULL ); + ena->tx_iobuf[id] = iobuf; + /* Ring doorbell */ writel ( ena->tx.sq.prod, ( ena->regs + ena->tx.sq.doorbell ) ); - DBGC2 ( ena, "ENA %p TX %d at [%08llx,%08llx)\n", ena, sqe->id, + DBGC2 ( ena, "ENA %p TX %d at [%08llx,%08llx)\n", ena, id, ( ( unsigned long long ) address ), ( ( unsigned long long ) address + len ) ); return 0; @@ -823,7 +871,9 @@ static int ena_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { static void ena_poll_tx ( struct net_device *netdev ) { struct ena_nic *ena = netdev->priv; struct ena_tx_cqe *cqe; + struct io_buffer *iobuf; unsigned int index; + unsigned int id; /* Check for completed packets */ while ( ena->tx.cq.cons != ena->tx.sq.prod ) { @@ -835,16 +885,24 @@ static void ena_poll_tx ( struct net_device *netdev ) { /* Stop if completion queue entry is empty */ if ( ( cqe->flags ^ ena->tx.cq.phase ) & ENA_CQE_PHASE ) return; - DBGC2 ( ena, "ENA %p TX %d complete\n", ena, - ( le16_to_cpu ( cqe->id ) >> 2 /* Don't ask */ ) ); /* Increment consumer counter */ ena->tx.cq.cons++; if ( ! ( ena->tx.cq.cons & ena->tx.cq.mask ) ) ena->tx.cq.phase ^= ENA_CQE_PHASE; + /* Identify and free buffer ID */ + id = ENA_TX_CQE_ID ( le16_to_cpu ( cqe->id ) ); + ena->tx_ids[index] = id; + + /* Identify I/O buffer */ + iobuf = ena->tx_iobuf[id]; + assert ( iobuf != NULL ); + ena->tx_iobuf[id] = NULL; + /* Complete transmit */ - netdev_tx_complete_next ( netdev ); + DBGC2 ( ena, "ENA %p TX %d complete\n", ena, id ); + netdev_tx_complete ( netdev, iobuf ); } } @@ -858,13 +916,14 @@ static void ena_poll_rx ( struct net_device *netdev ) { struct ena_rx_cqe *cqe; struct io_buffer *iobuf; unsigned int index; + unsigned int id; size_t len; /* Check for received packets */ while ( ena->rx.cq.cons != ena->rx.sq.prod ) { /* Get next completion queue entry */ - index = ( ena->rx.cq.cons % ENA_RX_COUNT ); + index = ( ena->rx.cq.cons & ena->rx.cq.mask ); cqe = &ena->rx.cq.cqe.rx[index]; /* Stop if completion queue entry is empty */ @@ -876,15 +935,20 @@ static void ena_poll_rx ( struct net_device *netdev ) { if ( ! ( ena->rx.cq.cons & ena->rx.cq.mask ) ) ena->rx.cq.phase ^= ENA_CQE_PHASE; + /* Identify and free buffer ID */ + id = le16_to_cpu ( cqe->id ); + ena->rx_ids[index] = id; + /* Populate I/O buffer */ - iobuf = ena->rx_iobuf[index]; - ena->rx_iobuf[index] = NULL; + iobuf = ena->rx_iobuf[id]; + assert ( iobuf != NULL ); + ena->rx_iobuf[id] = NULL; len = le16_to_cpu ( cqe->len ); iob_put ( iobuf, len ); /* Hand off to network stack */ DBGC2 ( ena, "ENA %p RX %d complete (length %zd)\n", - ena, le16_to_cpu ( cqe->id ), len ); + ena, id, len ); netdev_rx ( netdev, iobuf ); } } @@ -922,6 +986,45 @@ static struct net_device_operations ena_operations = { */ /** + * Assign memory BAR + * + * @v ena ENA device + * @v pci PCI device + * @ret rc Return status code + * + * Some BIOSes in AWS EC2 are observed to fail to assign a base + * address to the ENA device. The device is the only device behind + * its bridge, and the BIOS does assign a memory window to the bridge. + * We therefore place the device at the start of the memory window. + */ +static int ena_membase ( struct ena_nic *ena, struct pci_device *pci ) { + struct pci_bridge *bridge; + + /* Locate PCI bridge */ + bridge = pcibridge_find ( pci ); + if ( ! bridge ) { + DBGC ( ena, "ENA %p found no PCI bridge\n", ena ); + return -ENOTCONN; + } + + /* Sanity check */ + if ( PCI_SLOT ( pci->busdevfn ) || PCI_FUNC ( pci->busdevfn ) ) { + DBGC ( ena, "ENA %p at " PCI_FMT " may not be only device " + "on bus\n", ena, PCI_ARGS ( pci ) ); + return -ENOTSUP; + } + + /* Place device at start of memory window */ + pci_write_config_dword ( pci, PCI_BASE_ADDRESS_0, bridge->membase ); + pci->membase = bridge->membase; + DBGC ( ena, "ENA %p at " PCI_FMT " claiming bridge " PCI_FMT " mem " + "%08x\n", ena, PCI_ARGS ( pci ), PCI_ARGS ( bridge->pci ), + bridge->membase ); + + return 0; +} + +/** * Probe PCI device * * @v pci PCI device @@ -930,6 +1033,7 @@ static struct net_device_operations ena_operations = { static int ena_probe ( struct pci_device *pci ) { struct net_device *netdev; struct ena_nic *ena; + struct ena_host_info *info; int rc; /* Allocate and initialise net device */ @@ -946,16 +1050,20 @@ static int ena_probe ( struct pci_device *pci ) { ena->acq.phase = ENA_ACQ_PHASE; ena_cq_init ( &ena->tx.cq, ENA_TX_COUNT, sizeof ( ena->tx.cq.cqe.tx[0] ) ); - ena_sq_init ( &ena->tx.sq, ENA_SQ_TX, ENA_TX_COUNT, - sizeof ( ena->tx.sq.sqe.tx[0] ) ); + ena_sq_init ( &ena->tx.sq, ENA_SQ_TX, ENA_TX_COUNT, ENA_TX_COUNT, + sizeof ( ena->tx.sq.sqe.tx[0] ), ena->tx_ids ); ena_cq_init ( &ena->rx.cq, ENA_RX_COUNT, sizeof ( ena->rx.cq.cqe.rx[0] ) ); - ena_sq_init ( &ena->rx.sq, ENA_SQ_RX, ENA_RX_COUNT, - sizeof ( ena->rx.sq.sqe.rx[0] ) ); + ena_sq_init ( &ena->rx.sq, ENA_SQ_RX, ENA_RX_COUNT, ENA_RX_FILL, + sizeof ( ena->rx.sq.sqe.rx[0] ), ena->rx_ids ); /* Fix up PCI device */ adjust_pci_device ( pci ); + /* Fix up PCI BAR if left unassigned by BIOS */ + if ( ( ! pci->membase ) && ( ( rc = ena_membase ( ena, pci ) ) != 0 ) ) + goto err_membase; + /* Map registers */ ena->regs = pci_ioremap ( pci, pci->membase, ENA_BAR_SIZE ); if ( ! ena->regs ) { @@ -963,6 +1071,25 @@ static int ena_probe ( struct pci_device *pci ) { goto err_ioremap; } + /* Allocate and initialise host info */ + info = malloc_phys ( PAGE_SIZE, PAGE_SIZE ); + if ( ! info ) { + rc = -ENOMEM; + goto err_info; + } + ena->info = info; + memset ( info, 0, PAGE_SIZE ); + info->type = cpu_to_le32 ( ENA_HOST_INFO_TYPE_LINUX ); + snprintf ( info->dist_str, sizeof ( info->dist_str ), "%s", + ( product_name[0] ? product_name : product_short_name ) ); + snprintf ( info->kernel_str, sizeof ( info->kernel_str ), "%s", + product_version ); + info->version = cpu_to_le32 ( ENA_HOST_INFO_VERSION_WTF ); + info->spec = cpu_to_le16 ( ENA_HOST_INFO_SPEC_2_0 ); + info->busdevfn = cpu_to_le16 ( pci->busdevfn ); + DBGC2 ( ena, "ENA %p host info:\n", ena ); + DBGC2_HDA ( ena, virt_to_phys ( info ), info, sizeof ( *info ) ); + /* Reset the NIC */ if ( ( rc = ena_reset ( ena ) ) != 0 ) goto err_reset; @@ -971,6 +1098,10 @@ static int ena_probe ( struct pci_device *pci ) { if ( ( rc = ena_create_admin ( ena ) ) != 0 ) goto err_create_admin; + /* Set host attributes */ + if ( ( rc = ena_set_host_attributes ( ena ) ) != 0 ) + goto err_set_host_attributes; + /* Fetch MAC address */ if ( ( rc = ena_get_device_attributes ( netdev ) ) != 0 ) goto err_get_device_attributes; @@ -989,12 +1120,16 @@ static int ena_probe ( struct pci_device *pci ) { unregister_netdev ( netdev ); err_register_netdev: err_get_device_attributes: + err_set_host_attributes: ena_destroy_admin ( ena ); err_create_admin: ena_reset ( ena ); err_reset: + free_phys ( ena->info, PAGE_SIZE ); + err_info: iounmap ( ena->regs ); err_ioremap: + err_membase: netdev_nullify ( netdev ); netdev_put ( netdev ); err_alloc: @@ -1019,6 +1154,9 @@ static void ena_remove ( struct pci_device *pci ) { /* Reset card */ ena_reset ( ena ); + /* Free host info */ + free_phys ( ena->info, PAGE_SIZE ); + /* Free network device */ iounmap ( ena->regs ); netdev_nullify ( netdev ); diff --git a/src/drivers/net/ena.h b/src/drivers/net/ena.h index 676c5b878..4e1896e86 100644 --- a/src/drivers/net/ena.h +++ b/src/drivers/net/ena.h @@ -28,7 +28,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ENA_TX_COUNT 16 /** Number of receive queue entries */ -#define ENA_RX_COUNT 16 +#define ENA_RX_COUNT 128 + +/** Receive queue maximum fill level */ +#define ENA_RX_FILL 16 /** Base address low register offset */ #define ENA_BASE_LO 0x0 @@ -127,10 +130,86 @@ struct ena_device_attributes { uint32_t mtu; } __attribute__ (( packed )); +/** Host attributes */ +#define ENA_HOST_ATTRIBUTES 28 + +/** Host attributes */ +struct ena_host_attributes { + /** Host info base address */ + uint64_t info; + /** Debug area base address */ + uint64_t debug; + /** Debug area size */ + uint32_t debug_len; +} __attribute__ (( packed )); + +/** Host information */ +struct ena_host_info { + /** Operating system type */ + uint32_t type; + /** Operating system distribution (string) */ + char dist_str[128]; + /** Operating system distribution (numeric) */ + uint32_t dist; + /** Kernel version (string) */ + char kernel_str[32]; + /** Kernel version (numeric) */ + uint32_t kernel; + /** Driver version */ + uint32_t version; + /** Linux network device features */ + uint64_t linux_features; + /** ENA specification version */ + uint16_t spec; + /** PCI bus:dev.fn address */ + uint16_t busdevfn; + /** Number of CPUs */ + uint16_t cpus; + /** Reserved */ + uint8_t reserved_a[2]; + /** Supported features */ + uint32_t features; +} __attribute__ (( packed )); + +/** Linux operating system type + * + * There is a defined "iPXE" operating system type (with value 5). + * However, some very broken versions of the ENA firmware will refuse + * to allow a completion queue to be created if the "iPXE" type is + * used. + */ +#define ENA_HOST_INFO_TYPE_LINUX 1 + +/** Driver version + * + * The driver version field is nominally used to report a version + * number outside of the VM for consumption by humans (and potentially + * by automated monitoring tools that could e.g. check for outdated + * versions with known security flaws). + * + * However, at some point in the development of the ENA firmware, some + * unknown person at AWS thought it would be sensible to apply a + * machine interpretation to this field and adjust the behaviour of + * the firmware based on its value, thereby creating a maintenance and + * debugging nightmare for all existing and future drivers. + * + * Hint to engineers: if you ever find yourself writing code of the + * form "if (version == SOME_MAGIC_NUMBER)" then something has gone + * very, very wrong. This *always* indicates that something is + * broken, either in your own code or in the code with which you are + * forced to interact. + */ +#define ENA_HOST_INFO_VERSION_WTF 0x00000002UL + +/** ENA specification version */ +#define ENA_HOST_INFO_SPEC_2_0 0x0200 + /** Feature */ union ena_feature { /** Device attributes */ struct ena_device_attributes device; + /** Host attributes */ + struct ena_host_attributes host; }; /** Submission queue direction */ @@ -230,6 +309,14 @@ struct ena_create_cq_req { uint64_t address; } __attribute__ (( packed )); +/** Empty MSI-X vector + * + * Some versions of the ENA firmware will complain if the completion + * queue's MSI-X vector field is left empty, even though the queue + * configuration specifies that interrupts are not used. + */ +#define ENA_MSIX_NONE 0xffffffffUL + /** Create completion queue response */ struct ena_create_cq_rsp { /** Header */ @@ -292,6 +379,27 @@ struct ena_get_feature_rsp { union ena_feature feature; } __attribute__ (( packed )); +/** Set feature */ +#define ENA_SET_FEATURE 9 + +/** Set feature request */ +struct ena_set_feature_req { + /** Header */ + struct ena_aq_header header; + /** Length */ + uint32_t len; + /** Address */ + uint64_t address; + /** Flags */ + uint8_t flags; + /** Feature identifier */ + uint8_t id; + /** Reserved */ + uint8_t reserved[2]; + /** Feature */ + union ena_feature feature; +} __attribute__ (( packed )); + /** Get statistics */ #define ENA_GET_STATS 11 @@ -352,6 +460,8 @@ union ena_aq_req { struct ena_destroy_cq_req destroy_cq; /** Get feature */ struct ena_get_feature_req get_feature; + /** Set feature */ + struct ena_set_feature_req set_feature; /** Get statistics */ struct ena_get_stats_req get_stats; /** Padding */ @@ -454,6 +564,9 @@ struct ena_tx_cqe { uint16_t cons; } __attribute__ (( packed )); +/** Transmit completion request identifier */ +#define ENA_TX_CQE_ID(id) ( (id) >> 2 ) + /** Receive completion queue entry */ struct ena_rx_cqe { /** Reserved */ @@ -482,6 +595,8 @@ struct ena_sq { /** Raw data */ void *raw; } sqe; + /** Buffer IDs */ + uint8_t *ids; /** Doorbell register offset */ unsigned int doorbell; /** Total length of entries */ @@ -496,6 +611,10 @@ struct ena_sq { uint8_t direction; /** Number of entries */ uint8_t count; + /** Maximum fill level */ + uint8_t max; + /** Fill level (limited to completion queue size) */ + uint8_t fill; }; /** @@ -504,15 +623,19 @@ struct ena_sq { * @v sq Submission queue * @v direction Direction * @v count Number of entries + * @v max Maximum fill level * @v size Size of each entry + * @v ids Buffer IDs */ static inline __attribute__ (( always_inline )) void ena_sq_init ( struct ena_sq *sq, unsigned int direction, unsigned int count, - size_t size ) { + unsigned int max, size_t size, uint8_t *ids ) { sq->len = ( count * size ); sq->direction = direction; sq->count = count; + sq->max = max; + sq->ids = ids; } /** Completion queue */ @@ -573,6 +696,8 @@ struct ena_qp { struct ena_nic { /** Registers */ void *regs; + /** Host info */ + struct ena_host_info *info; /** Admin queue */ struct ena_aq aq; /** Admin completion queue */ @@ -581,7 +706,13 @@ struct ena_nic { struct ena_qp tx; /** Receive queue */ struct ena_qp rx; - /** Receive I/O buffers */ + /** Transmit buffer IDs */ + uint8_t tx_ids[ENA_TX_COUNT]; + /** Transmit I/O buffers, indexed by buffer ID */ + struct io_buffer *tx_iobuf[ENA_TX_COUNT]; + /** Receive buffer IDs */ + uint8_t rx_ids[ENA_RX_COUNT]; + /** Receive I/O buffers, indexed by buffer ID */ struct io_buffer *rx_iobuf[ENA_RX_COUNT]; }; diff --git a/src/drivers/net/ice.c b/src/drivers/net/ice.c new file mode 100644 index 000000000..b5d66f1bb --- /dev/null +++ b/src/drivers/net/ice.c @@ -0,0 +1,986 @@ +/* + * Copyright (C) 2022 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 <string.h> +#include <unistd.h> +#include <errno.h> +#include <byteswap.h> +#include <ipxe/netdevice.h> +#include <ipxe/ethernet.h> +#include <ipxe/if_ether.h> +#include <ipxe/iobuf.h> +#include <ipxe/pci.h> +#include "ice.h" + +/** @file + * + * Intel 100 Gigabit Ethernet network card driver + * + */ + +/** + * Magic MAC address + * + * Used as the source address and promiscuous unicast destination + * address in the "add switch rules" command. + */ +static uint8_t ice_magic_mac[ETH_HLEN] = { 0x02, 0x00, 0x00, 0x00, 0x00, 0x00 }; + +/****************************************************************************** + * + * Admin queue + * + ****************************************************************************** + */ + +/** + * Get firmware version + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int ice_admin_version ( struct intelxl_nic *intelxl ) { + struct ice_admin_descriptor *cmd; + struct ice_admin_version_params *version; + unsigned int api; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_VERSION ); + version = &cmd->params.version; + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + api = version->api.major; + DBGC ( intelxl, "ICE %p firmware v%d/%d.%d.%d API v%d/%d.%d.%d\n", + intelxl, version->firmware.branch, version->firmware.major, + version->firmware.minor, version->firmware.patch, + version->api.branch, version->api.major, version->api.minor, + version->api.patch ); + + /* Check for API compatibility */ + if ( api > INTELXL_ADMIN_API_MAJOR ) { + DBGC ( intelxl, "ICE %p unsupported API v%d\n", intelxl, api ); + return -ENOTSUP; + } + + return 0; +} + +/** + * Get MAC address + * + * @v netdev Network device + * @ret rc Return status code + */ +static int ice_admin_mac_read ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct ice_admin_descriptor *cmd; + struct ice_admin_mac_read_params *read; + struct ice_admin_mac_read_address *mac; + union ice_admin_buffer *buf; + unsigned int i; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_MAC_READ ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->mac_read ) ); + read = &cmd->params.mac_read; + buf = ice_admin_command_buffer ( intelxl ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + /* Check that MAC address is present in response */ + if ( ! ( read->valid & INTELXL_ADMIN_MAC_READ_VALID_LAN ) ) { + DBGC ( intelxl, "ICE %p has no MAC address\n", intelxl ); + return -ENOENT; + } + + /* Identify MAC address */ + for ( i = 0 ; i < read->count ; i++ ) { + + /* Check for a LAN MAC address */ + mac = &buf->mac_read.mac[i]; + if ( mac->type != ICE_ADMIN_MAC_READ_TYPE_LAN ) + continue; + + /* Check that address is valid */ + if ( ! is_valid_ether_addr ( mac->mac ) ) { + DBGC ( intelxl, "ICE %p has invalid MAC address " + "(%s)\n", intelxl, eth_ntoa ( mac->mac ) ); + return -EINVAL; + } + + /* Copy MAC address */ + DBGC ( intelxl, "ICE %p has MAC address %s\n", + intelxl, eth_ntoa ( mac->mac ) ); + memcpy ( netdev->hw_addr, mac->mac, ETH_ALEN ); + + return 0; + } + + /* Missing LAN MAC address */ + DBGC ( intelxl, "ICE %p has no LAN MAC address\n", + intelxl ); + return -ENOENT; +} + +/** + * Set MAC address + * + * @v netdev Network device + * @ret rc Return status code + */ +static int ice_admin_mac_write ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct ice_admin_descriptor *cmd; + struct ice_admin_mac_write_params *write; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_MAC_WRITE ); + write = &cmd->params.mac_write; + memcpy ( write->mac, netdev->ll_addr, ETH_ALEN ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Get switch configuration + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int ice_admin_switch ( struct intelxl_nic *intelxl ) { + struct ice_admin_descriptor *cmd; + struct ice_admin_switch_params *sw; + union ice_admin_buffer *buf; + uint16_t next = 0; + uint16_t seid; + uint16_t type; + int rc; + + /* Get each configuration in turn */ + do { + /* Populate descriptor */ + cmd = ice_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; + sw->next = next; + buf = ice_admin_command_buffer ( intelxl ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + seid = le16_to_cpu ( buf->sw.cfg[0].seid ); + + /* Dump raw configuration */ + DBGC2 ( intelxl, "ICE %p SEID %#04x:\n", intelxl, seid ); + DBGC2_HDA ( intelxl, 0, &buf->sw.cfg[0], + sizeof ( buf->sw.cfg[0] ) ); + + /* Parse response */ + type = ( seid & ICE_ADMIN_SWITCH_TYPE_MASK ); + if ( type == ICE_ADMIN_SWITCH_TYPE_VSI ) { + intelxl->vsi = ( seid & ~ICE_ADMIN_SWITCH_TYPE_MASK ); + DBGC ( intelxl, "ICE %p VSI %#04x uplink %#04x func " + "%#04x\n", intelxl, intelxl->vsi, + le16_to_cpu ( buf->sw.cfg[0].uplink ), + le16_to_cpu ( buf->sw.cfg[0].func ) ); + } + + } while ( ( next = sw->next ) ); + + /* Check that we found a VSI */ + if ( ! intelxl->vsi ) { + DBGC ( intelxl, "ICE %p has no VSI\n", intelxl ); + return -ENOENT; + } + + return 0; +} + +/** + * Add switch rules + * + * @v intelxl Intel device + * @v mac MAC address + * @ret rc Return status code + */ +static int ice_admin_rules ( struct intelxl_nic *intelxl, uint8_t *mac ) { + struct ice_admin_descriptor *cmd; + struct ice_admin_rules_params *rules; + union ice_admin_buffer *buf; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( ICE_ADMIN_ADD_RULES ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF | INTELXL_ADMIN_FL_RD ); + cmd->len = cpu_to_le16 ( sizeof ( buf->rules ) ); + rules = &cmd->params.rules; + rules->count = cpu_to_le16 ( 1 ); + buf = ice_admin_command_buffer ( intelxl ); + buf->rules.recipe = cpu_to_le16 ( ICE_ADMIN_RULES_RECIPE_PROMISC ); + buf->rules.port = cpu_to_le16 ( intelxl->port ); + buf->rules.action = + cpu_to_le32 ( ICE_ADMIN_RULES_ACTION_VALID | + ICE_ADMIN_RULES_ACTION_VSI ( intelxl->vsi ) ); + buf->rules.len = cpu_to_le16 ( sizeof ( buf->rules.hdr ) ); + memcpy ( buf->rules.hdr.eth.h_dest, mac, ETH_ALEN ); + memcpy ( buf->rules.hdr.eth.h_source, ice_magic_mac, ETH_ALEN ); + buf->rules.hdr.eth.h_protocol = htons ( ETH_P_8021Q ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Check if scheduler node is a parent (i.e. non-leaf) node + * + * @v branch Scheduler topology branch + * @v node Scheduler topology node + * @ret child Any child node, or NULL if not found + */ +static struct ice_admin_schedule_node * +ice_admin_schedule_is_parent ( struct ice_admin_schedule_branch *branch, + struct ice_admin_schedule_node *node ) { + unsigned int count = le16_to_cpu ( branch->count ); + struct ice_admin_schedule_node *child; + unsigned int i; + + /* Find a child element, if any */ + for ( i = 0 ; i < count ; i++ ) { + child = &branch->node[i]; + if ( child->parent == node->teid ) + return child; + } + + return NULL; +} + +/** + * Query default scheduling tree topology + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int ice_admin_schedule ( struct intelxl_nic *intelxl ) { + struct ice_admin_descriptor *cmd; + struct ice_admin_schedule_params *sched; + struct ice_admin_schedule_branch *branch; + struct ice_admin_schedule_node *node; + union ice_admin_buffer *buf; + int i; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( ICE_ADMIN_SCHEDULE ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->sched ) ); + sched = &cmd->params.sched; + buf = ice_admin_command_buffer ( intelxl ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + /* Sanity checks */ + if ( ! sched->branches ) { + DBGC ( intelxl, "ICE %p topology has no branches\n", intelxl ); + return -EINVAL; + } + branch = buf->sched.branch; + + /* Identify leaf node */ + for ( i = ( le16_to_cpu ( branch->count ) - 1 ) ; i >= 0 ; i-- ) { + node = &branch->node[i]; + if ( ! ice_admin_schedule_is_parent ( branch, node ) ) { + intelxl->teid = le32_to_cpu ( node->teid ); + DBGC2 ( intelxl, "ICE %p TEID %#08x type %d\n", + intelxl, intelxl->teid, node->config.type ); + break; + } + } + if ( ! intelxl->teid ) { + DBGC ( intelxl, "ICE %p found no leaf TEID\n", intelxl ); + return -EINVAL; + } + + return 0; +} + +/** + * Restart autonegotiation + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int ice_admin_autoneg ( struct intelxl_nic *intelxl ) { + struct ice_admin_descriptor *cmd; + struct ice_admin_autoneg_params *autoneg; + int rc; + + /* Populate descriptor */ + cmd = ice_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 ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Get link status + * + * @v netdev Network device + * @ret rc Return status code + */ +static int ice_admin_link ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct ice_admin_descriptor *cmd; + struct ice_admin_link_params *link; + union ice_admin_buffer *buf; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_LINK ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->link ) ); + link = &cmd->params.link; + link->notify = INTELXL_ADMIN_LINK_NOTIFY; + buf = ice_admin_command_buffer ( intelxl ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + DBGC ( intelxl, "ICE %p speed %#02x status %#02x\n", + intelxl, le16_to_cpu ( buf->link.speed ), buf->link.status ); + + /* Update network device */ + if ( buf->link.status & INTELXL_ADMIN_LINK_UP ) { + netdev_link_up ( netdev ); + } else { + netdev_link_down ( netdev ); + } + + return 0; +} + +/** + * Handle admin event + * + * @v netdev Network device + * @v xlevt Event descriptor + * @v xlbuf Data buffer + */ +static void ice_admin_event ( struct net_device *netdev, + struct intelxl_admin_descriptor *xlevt, + union intelxl_admin_buffer *xlbuf __unused ) { + struct intelxl_nic *intelxl = netdev->priv; + struct ice_admin_descriptor *evt = + container_of ( xlevt, struct ice_admin_descriptor, xl ); + + /* Ignore unrecognised events */ + if ( evt->opcode != cpu_to_le16 ( INTELXL_ADMIN_LINK ) ) { + DBGC ( intelxl, "INTELXL %p unrecognised event opcode " + "%#04x\n", intelxl, le16_to_cpu ( evt->opcode ) ); + return; + } + + /* Update link status */ + ice_admin_link ( netdev ); +} + +/** + * Add transmit queue + * + * @v intelxl Intel device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int ice_admin_add_txq ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ) { + struct ice_admin_descriptor *cmd; + struct ice_admin_add_txq_params *add_txq; + union ice_admin_buffer *buf; + struct ice_context_tx *ctx; + struct ice_schedule_tx *sched; + physaddr_t address; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( ICE_ADMIN_ADD_TXQ ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->add_txq ) ); + add_txq = &cmd->params.add_txq; + add_txq->count = 1; + buf = ice_admin_command_buffer ( intelxl ); + buf->add_txq.parent = cpu_to_le32 ( intelxl->teid ); + buf->add_txq.count = 1; + ctx = &buf->add_txq.ctx; + address = dma ( &ring->map, ring->desc.raw ); + ctx->base_port = + cpu_to_le64 ( ICE_TXQ_BASE_PORT ( address, intelxl->port ) ); + ctx->pf_type = cpu_to_le16 ( ICE_TXQ_PF_TYPE ( intelxl->pf ) ); + ctx->vsi = cpu_to_le16 ( intelxl->vsi ); + ctx->len = cpu_to_le16 ( ICE_TXQ_LEN ( INTELXL_TX_NUM_DESC ) ); + ctx->flags = cpu_to_le16 ( ICE_TXQ_FL_TSO | ICE_TXQ_FL_LEGACY ); + sched = &buf->add_txq.sched; + sched->sections = ( ICE_SCHEDULE_GENERIC | ICE_SCHEDULE_COMMIT | + ICE_SCHEDULE_EXCESS ); + sched->commit_weight = cpu_to_le16 ( ICE_SCHEDULE_WEIGHT ); + sched->excess_weight = cpu_to_le16 ( ICE_SCHEDULE_WEIGHT ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + DBGC ( intelxl, "ICE %p added TEID %#04x\n", + intelxl, le32_to_cpu ( buf->add_txq.teid ) ); + + return 0; +} + +/** + * Disable transmit queue + * + * @v intelxl Intel device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int ice_admin_disable_txq ( struct intelxl_nic *intelxl ) { + struct ice_admin_descriptor *cmd; + struct ice_admin_disable_txq_params *disable_txq; + union ice_admin_buffer *buf; + int rc; + + /* Populate descriptor */ + cmd = ice_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( ICE_ADMIN_DISABLE_TXQ ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->disable_txq ) ); + disable_txq = &cmd->params.disable_txq; + disable_txq->flags = ICE_TXQ_FL_FLUSH; + disable_txq->count = 1; + disable_txq->timeout = ICE_TXQ_TIMEOUT; + buf = ice_admin_command_buffer ( intelxl ); + buf->disable_txq.parent = cpu_to_le32 ( intelxl->teid ); + buf->disable_txq.count = 1; + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + return 0; +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Dump transmit queue context (for debugging) + * + * @v intelxl Intel device + */ +static void ice_dump_tx ( struct intelxl_nic *intelxl ) { + uint32_t ctx[ sizeof ( struct ice_context_tx ) / sizeof ( uint32_t ) ]; + uint32_t stat; + unsigned int i; + + /* Do nothing unless debug output is enabled */ + if ( ! DBG_EXTRA ) + return; + + /* Trigger reading of transmit context */ + writel ( ( ICE_GLCOMM_QTX_CNTX_CTL_CMD_READ | + ICE_GLCOMM_QTX_CNTX_CTL_EXEC ), + intelxl->regs + ICE_GLCOMM_QTX_CNTX_CTL ); + + /* Wait for operation to complete */ + for ( i = 0 ; i < INTELXL_CTX_MAX_WAIT_MS ; i++ ) { + + /* Check if operation is complete */ + stat = readl ( intelxl->regs + ICE_GLCOMM_QTX_CNTX_STAT ); + if ( ! ( stat & ICE_GLCOMM_QTX_CNTX_BUSY ) ) + break; + + /* Delay */ + mdelay ( 1 ); + } + + /* Read context registers */ + for ( i = 0 ; i < ( sizeof ( ctx ) / sizeof ( ctx[0] ) ) ; i++ ) { + ctx[i] = cpu_to_le32 ( readl ( intelxl->regs + + ICE_GLCOMM_QTX_CNTX_DATA ( i ))); + } + + /* Dump context */ + DBGC2 ( intelxl, "ICE %p TX context:\n", intelxl ); + DBGC2_HDA ( intelxl, 0, ctx, sizeof ( ctx ) ); +} + +/** + * Dump receive queue context (for debugging) + * + * @v intelxl Intel device + */ +static void ice_dump_rx ( struct intelxl_nic *intelxl ) { + uint32_t ctx[ sizeof ( struct intelxl_context_rx ) / + sizeof ( uint32_t ) ]; + unsigned int i; + + /* Do nothing unless debug output is enabled */ + if ( ! DBG_EXTRA ) + return; + + /* Read context registers */ + for ( i = 0 ; i < ( sizeof ( ctx ) / sizeof ( ctx[0] ) ) ; i++ ) { + ctx[i] = cpu_to_le32 ( readl ( intelxl->regs + + ICE_QRX_CONTEXT ( i ) ) ); + } + + /* Dump context */ + DBGC2 ( intelxl, "ICE %p RX context:\n", intelxl ); + DBGC2_HDA ( intelxl, 0, ctx, sizeof ( ctx ) ); +} + +/** + * Create transmit queue + * + * @v intelxl Intel device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int ice_create_tx ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ) { + int rc; + + /* Allocate descriptor ring */ + if ( ( rc = intelxl_alloc_ring ( intelxl, ring ) ) != 0 ) + goto err_alloc; + + /* Add transmit queue */ + if ( ( rc = ice_admin_add_txq ( intelxl, ring ) ) != 0 ) + goto err_add_txq; + + return 0; + + err_add_txq: + intelxl_free_ring ( intelxl, ring ); + err_alloc: + return rc; +} + +/** + * Destroy transmit queue + * + * @v intelxl Intel device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static void ice_destroy_tx ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ) { + int rc; + + /* Disable transmit queue */ + if ( ( rc = ice_admin_disable_txq ( intelxl ) ) != 0 ) { + /* Leak memory; there's nothing else we can do */ + return; + } + + /* Free descriptor ring */ + intelxl_free_ring ( intelxl, ring ); +} + +/** + * Program receive queue context + * + * @v intelxl Intel device + * @v address Descriptor ring base address + * @ret rc Return status code + */ +static int ice_context_rx ( struct intelxl_nic *intelxl, + physaddr_t address ) { + union { + struct intelxl_context_rx rx; + uint32_t raw[ sizeof ( struct intelxl_context_rx ) / + sizeof ( uint32_t ) ]; + } ctx; + uint64_t base_count; + unsigned int i; + + /* Initialise context */ + memset ( &ctx, 0, sizeof ( ctx ) ); + 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_DSIZE | INTELXL_CTX_RX_FL_CRCSTRIP ); + ctx.rx.mfs = cpu_to_le16 ( INTELXL_CTX_RX_MFS ( intelxl->mfs ) ); + + /* Write context registers */ + for ( i = 0 ; i < ( sizeof ( ctx ) / sizeof ( ctx.raw[0] ) ) ; i++ ) { + writel ( le32_to_cpu ( ctx.raw[i] ), + ( intelxl->regs + ICE_QRX_CONTEXT ( i ) ) ); + } + + return 0; +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int ice_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 ) ); + + /* Set MAC address */ + if ( ( rc = ice_admin_mac_write ( netdev ) ) != 0 ) + goto err_mac_write; + + /* Set maximum frame size */ + if ( ( rc = intelxl_admin_mac_config ( intelxl ) ) != 0 ) + goto err_mac_config; + + /* Create receive descriptor ring */ + if ( ( rc = intelxl_create_ring ( intelxl, &intelxl->rx ) ) != 0 ) + goto err_create_rx; + + /* Create transmit descriptor ring */ + if ( ( rc = ice_create_tx ( intelxl, &intelxl->tx ) ) != 0 ) + goto err_create_tx; + + /* Restart autonegotiation */ + ice_admin_autoneg ( intelxl ); + + /* Update link state */ + ice_admin_link ( netdev ); + + return 0; + + ice_destroy_tx ( intelxl, &intelxl->tx ); + err_create_tx: + intelxl_destroy_ring ( intelxl, &intelxl->rx ); + err_create_rx: + err_mac_config: + err_mac_write: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void ice_close ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + + /* Dump contexts (for debugging) */ + ice_dump_tx ( intelxl ); + ice_dump_rx ( intelxl ); + + /* Destroy transmit descriptor ring */ + ice_destroy_tx ( intelxl, &intelxl->tx ); + + /* Destroy receive descriptor ring */ + intelxl_destroy_ring ( intelxl, &intelxl->rx ); + + /* Discard any unused receive buffers */ + intelxl_empty_rx ( intelxl ); +} + +/** Network device operations */ +static struct net_device_operations ice_operations = { + .open = ice_open, + .close = ice_close, + .transmit = intelxl_transmit, + .poll = intelxl_poll, +}; + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int ice_probe ( struct pci_device *pci ) { + struct net_device *netdev; + struct intelxl_nic *intelxl; + uint32_t pffunc_rid; + uint32_t pfgen_portnum; + int rc; + + /* Allocate and initialise net device */ + netdev = alloc_etherdev ( sizeof ( *intelxl ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &ice_operations ); + netdev->max_pkt_len = INTELXL_MAX_PKT_LEN; + intelxl = netdev->priv; + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + memset ( intelxl, 0, sizeof ( *intelxl ) ); + intelxl->intr = ICE_GLINT_DYN_CTL; + intelxl->handle = ice_admin_event; + 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] ), NULL ); + intelxl_init_ring ( &intelxl->rx, INTELXL_RX_NUM_DESC, + sizeof ( intelxl->rx.desc.rx[0] ), + ice_context_rx ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + intelxl->regs = pci_ioremap ( pci, pci->membase, ICE_BAR_SIZE ); + if ( ! intelxl->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Configure DMA */ + intelxl->dma = &pci->dma; + dma_set_mask_64bit ( intelxl->dma ); + netdev->dma = intelxl->dma; + + /* Locate PCI Express capability */ + intelxl->exp = pci_find_capability ( pci, PCI_CAP_ID_EXP ); + if ( ! intelxl->exp ) { + DBGC ( intelxl, "ICE %p missing PCIe capability\n", + intelxl ); + rc = -ENXIO; + goto err_exp; + } + + /* Reset the function via PCIe FLR */ + pci_reset ( pci, intelxl->exp ); + + /* Get function and port number */ + pffunc_rid = readl ( intelxl->regs + ICE_PFFUNC_RID ); + intelxl->pf = ICE_PFFUNC_RID_FUNC_NUM ( pffunc_rid ); + pfgen_portnum = readl ( intelxl->regs + ICE_PFGEN_PORTNUM ); + intelxl->port = ICE_PFGEN_PORTNUM_PORT_NUM ( pfgen_portnum ); + DBGC ( intelxl, "ICE %p PF %d using port %d\n", + intelxl, intelxl->pf, intelxl->port ); + + /* Enable MSI-X dummy interrupt */ + if ( ( rc = intelxl_msix_enable ( intelxl, pci, + INTELXL_MSIX_VECTOR ) ) != 0 ) + goto err_msix; + + /* Open admin queues */ + if ( ( rc = intelxl_open_admin ( intelxl ) ) != 0 ) + goto err_open_admin; + + /* Get firmware version */ + if ( ( rc = ice_admin_version ( intelxl ) ) != 0 ) + goto err_admin_version; + + /* Clear PXE mode */ + if ( ( rc = intelxl_admin_clear_pxe ( intelxl ) ) != 0 ) + goto err_admin_clear_pxe; + + /* Get switch configuration */ + if ( ( rc = ice_admin_switch ( intelxl ) ) != 0 ) + goto err_admin_switch; + + /* Add broadcast address */ + if ( ( rc = ice_admin_rules ( intelxl, eth_broadcast ) ) != 0 ) + goto err_admin_rules_broadcast; + + /* Add promiscuous unicast address */ + if ( ( rc = ice_admin_rules ( intelxl, ice_magic_mac ) ) != 0 ) + goto err_admin_rules_magic; + + /* Query scheduler topology */ + if ( ( rc = ice_admin_schedule ( intelxl ) ) != 0 ) + goto err_admin_schedule; + + /* Get MAC address */ + if ( ( rc = ice_admin_mac_read ( netdev ) ) != 0 ) + goto err_admin_mac_read; + + /* Configure queue register addresses */ + intelxl->tx.tail = ICE_QTX_COMM_DBELL; + intelxl->rx.reg = ICE_QRX_CTRL; + intelxl->rx.tail = ICE_QRX_TAIL; + + /* Configure interrupt causes */ + writel ( ( ICE_QINT_TQCTL_ITR_INDX_NONE | ICE_QINT_TQCTL_CAUSE_ENA ), + intelxl->regs + ICE_QINT_TQCTL ); + writel ( ( ICE_QINT_RQCTL_ITR_INDX_NONE | ICE_QINT_RQCTL_CAUSE_ENA ), + intelxl->regs + ICE_QINT_RQCTL ); + + /* Set a default value for the queue context flex extension, + * since this register erroneously retains its value across at + * least a PCIe FLR. + */ + writel ( ( ICE_QRX_FLXP_CNTXT_RXDID_IDX_LEGACY_32 | + ICE_QRX_FLXP_CNTXT_RXDID_PRIO_MAX ), + intelxl->regs + ICE_QRX_FLXP_CNTXT ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + /* Set initial link state */ + ice_admin_link ( netdev ); + + return 0; + + unregister_netdev ( netdev ); + err_register_netdev: + err_admin_mac_read: + err_admin_schedule: + err_admin_rules_magic: + err_admin_rules_broadcast: + err_admin_switch: + err_admin_clear_pxe: + err_admin_version: + intelxl_close_admin ( intelxl ); + err_open_admin: + intelxl_msix_disable ( intelxl, pci, INTELXL_MSIX_VECTOR ); + err_msix: + pci_reset ( pci, intelxl->exp ); + 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 ice_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 ); + + /* Close admin queues */ + intelxl_close_admin ( intelxl ); + + /* Disable MSI-X dummy interrupt */ + intelxl_msix_disable ( intelxl, pci, INTELXL_MSIX_VECTOR ); + + /* Reset the NIC */ + pci_reset ( pci, intelxl->exp ); + + /* Free network device */ + iounmap ( intelxl->regs ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** PCI device IDs */ +static struct pci_device_id ice_nics[] = { + PCI_ROM ( 0x8086, 0x124c, "e823l-bp", "E823-L backplane", 0 ), + PCI_ROM ( 0x8086, 0x124d, "e823l-sfp", "E823-L SFP", 0 ), + PCI_ROM ( 0x8086, 0x124e, "e823l-10gt", "E823-L 10GBASE-T", 0 ), + PCI_ROM ( 0x8086, 0x124f, "e823l-1g", "E823-L 1GbE", 0 ), + PCI_ROM ( 0x8086, 0x151d, "e823l-qsfp", "E823-L QSFP", 0 ), + PCI_ROM ( 0x8086, 0x1591, "e810c-bp", "E810-C backplane", 0 ), + PCI_ROM ( 0x8086, 0x1592, "e810c-qsfp", "E810-C QSFP", 0 ), + PCI_ROM ( 0x8086, 0x1593, "e810c-sfp", "E810-C SFP", 0 ), + PCI_ROM ( 0x8086, 0x1599, "e810-xxv-bp", "E810-XXV backplane", 0 ), + PCI_ROM ( 0x8086, 0x159a, "e810-xxv-qsfp", "E810-XXV QSFP", 0 ), + PCI_ROM ( 0x8086, 0x159b, "e810-xxv-sfp", "E810-XXV SFP", 0 ), + PCI_ROM ( 0x8086, 0x188a, "e823c-bp", "E823-C backplane", 0 ), + PCI_ROM ( 0x8086, 0x188b, "e823c-qsfp", "E823-C QSFP", 0 ), + PCI_ROM ( 0x8086, 0x188c, "e823c-sfp", "E823-C SFP", 0 ), + PCI_ROM ( 0x8086, 0x188d, "e823c-10gt", "E823-C 10GBASE-T", 0 ), + PCI_ROM ( 0x8086, 0x188e, "e823c-1g", "E823-C 1GbE", 0 ), + PCI_ROM ( 0x8086, 0x1890, "e822c-bp", "E822-C backplane", 0 ), + PCI_ROM ( 0x8086, 0x1891, "e822c-qsfp", "E822-C QSFP", 0 ), + PCI_ROM ( 0x8086, 0x1892, "e822c-sfp", "E822-C SFP", 0 ), + PCI_ROM ( 0x8086, 0x1893, "e822c-10gt", "E822-C 10GBASE-T", 0 ), + PCI_ROM ( 0x8086, 0x1894, "e822c-1g", "E822-C 1GbE", 0 ), + PCI_ROM ( 0x8086, 0x1897, "e822l-bp", "E822-L backplane", 0 ), + PCI_ROM ( 0x8086, 0x1898, "e822l-sfp", "E822-L SFP", 0 ), + PCI_ROM ( 0x8086, 0x1899, "e822l-10gt", "E822-L 10GBASE-T", 0 ), + PCI_ROM ( 0x8086, 0x189a, "e822l-1g", "E822-L 1GbE", 0 ), +}; + +/** PCI driver */ +struct pci_driver ice_driver __pci_driver = { + .ids = ice_nics, + .id_count = ( sizeof ( ice_nics ) / sizeof ( ice_nics[0] ) ), + .probe = ice_probe, + .remove = ice_remove, +}; diff --git a/src/drivers/net/ice.h b/src/drivers/net/ice.h new file mode 100644 index 000000000..26291a7a1 --- /dev/null +++ b/src/drivers/net/ice.h @@ -0,0 +1,565 @@ +#ifndef _ICE_H +#define _ICE_H + +/** @file + * + * Intel 100 Gigabit Ethernet network card driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/if_ether.h> +#include "intelxl.h" + +/** BAR size */ +#define ICE_BAR_SIZE 0x800000 + +/****************************************************************************** + * + * Transmit and receive datapaths + * + ****************************************************************************** + */ + +/** Transmit queue context */ +struct ice_context_tx { + /** Base address */ + uint64_t base_port; + /** PF number and queue type */ + uint16_t pf_type; + /** Source VSI */ + uint16_t vsi; + /** Reserved */ + uint8_t reserved_c[5]; + /** Queue length */ + uint16_t len; + /** Flags */ + uint16_t flags; + /** Reserved */ + uint8_t reserved_d[3]; +} __attribute__ (( packed )); + +/** Transmit scheduler configuration */ +struct ice_schedule_tx { + /** Node type */ + uint8_t type; + /** Valid sections */ + uint8_t sections; + /** Generic information */ + uint8_t generic; + /** Flags */ + uint8_t flags; + /** Committed bandwidth profile ID */ + uint16_t commit_id; + /** Committeed bandwidth weight */ + uint16_t commit_weight; + /** Excess bandwidth profile ID */ + uint16_t excess_id; + /** Excess bandwidth weight */ + uint16_t excess_weight; + /** Shared rate limit profile ID */ + uint16_t shared; + /** Reserved */ + uint16_t reserved; +} __attribute__ (( packed )); + +/** Global Receive Queue Control Register */ +#define ICE_QRX_CTRL 0x120000 + +/** Receive Queue Context Registers */ +#define ICE_QRX_CONTEXT(x) ( 0x280000 + ( 0x2000 * (x) ) ) + +/** Receive Queue Tail Register */ +#define ICE_QRX_TAIL 0x290000 + +/** Transmit Comm Scheduler Queue Doorbell Register */ +#define ICE_QTX_COMM_DBELL 0x2c0000 + +/** Transmit Comm Scheduler Queue Context Data Registers */ +#define ICE_GLCOMM_QTX_CNTX_DATA(x) ( 0x2d2d40 + ( 0x4 * (x) ) ) + +/** Transmit Comm Scheduler Queue Context Control Register */ +#define ICE_GLCOMM_QTX_CNTX_CTL 0x2d2dc8 +#define ICE_GLCOMM_QTX_CNTX_CTL_CMD(x) ( (x) << 16 ) /**< Command */ +#define ICE_GLCOMM_QTX_CNTX_CTL_CMD_READ \ + ICE_GLCOMM_QTX_CNTX_CTL_CMD ( 0 ) /**< Read context */ +#define ICE_GLCOMM_QTX_CNTX_CTL_EXEC 0x00080000UL /**< Execute */ + +/** Transmit Comm Scheduler Queue Context Status Register */ +#define ICE_GLCOMM_QTX_CNTX_STAT 0x2d2dcc +#define ICE_GLCOMM_QTX_CNTX_BUSY 0x00000001UL /**< In progress */ + +/** Queue Context Flex Extension Register */ +#define ICE_QRX_FLXP_CNTXT 0x480000 +#define ICE_QRX_FLXP_CNTXT_RXDID_IDX(x) ( (x) << 0 ) /**< RX profile */ +#define ICE_QRX_FLXP_CNTXT_RXDID_IDX_LEGACY_32 \ + ICE_QRX_FLXP_CNTXT_RXDID_IDX ( 1 ) /**< 32-byte legacy */ +#define ICE_QRX_FLXP_CNTXT_RXDID_PRIO(x) ( (x) << 8 ) /**< Priority */ +#define ICE_QRX_FLXP_CNTXT_RXDID_PRIO_MAX \ + ICE_QRX_FLXP_CNTXT_RXDID_PRIO ( 7 ) /**< Maximum priority */ + +/****************************************************************************** + * + * Admin queue + * + ****************************************************************************** + */ + +/** Admin queue version number */ +struct ice_admin_version { + /** Branch identifier */ + uint8_t branch; + /** Major version number */ + uint8_t major; + /** Minor version number */ + uint8_t minor; + /** Patch level */ + uint8_t patch; +} __attribute__ (( packed )); + +/** Admin queue Get Version command parameters */ +struct ice_admin_version_params { + /** ROM version */ + uint32_t rom; + /** Firmware build ID */ + uint32_t build; + /** Firmware version */ + struct ice_admin_version firmware; + /** API version */ + struct ice_admin_version api; +} __attribute__ (( packed )); + +/** Admin queue Manage MAC Address Read command parameters */ +struct ice_admin_mac_read_params { + /** Valid addresses */ + uint8_t valid; + /** Reserved */ + uint8_t reserved_a[3]; + /** Number of addresses in response */ + uint8_t count; + /** Reserved */ + uint8_t reserved_b[11]; +} __attribute__ (( packed )); + +/** MAC Address description */ +struct ice_admin_mac_read_address { + /** Port number */ + uint8_t port; + /** Address type */ + uint8_t type; + /** MAC address */ + uint8_t mac[ETH_ALEN]; +} __attribute__ (( packed )); + +/** LAN MAC address type */ +#define ICE_ADMIN_MAC_READ_TYPE_LAN 0 + +/** Admin queue Manage MAC Address Read data buffer */ +struct ice_admin_mac_read_buffer { + /** MAC addresses */ + struct ice_admin_mac_read_address mac[4]; +} __attribute__ (( packed )); + +/** Admin queue Manage MAC Address Write command parameters */ +struct ice_admin_mac_write_params { + /** Reserved */ + uint8_t reserved_a[1]; + /** Write type */ + uint8_t type; + /** MAC address */ + uint8_t mac[ETH_ALEN]; + /** Reserved */ + uint8_t reserved_b[8]; +} __attribute__ (( packed )); + +/** Admin queue Get Switch Configuration command parameters */ +struct ice_admin_switch_params { + /** Reserved */ + uint8_t reserved_a[2]; + /** Starting switching element identifier */ + uint16_t next; + /** Reserved */ + uint8_t reserved[4]; + /** Data buffer address */ + uint64_t address; +} __attribute__ (( packed )); + +/** Switching element configuration */ +struct ice_admin_switch_config { + /** Switching element ID and flags */ + uint16_t seid; + /** Uplink switching element ID */ + uint16_t uplink; + /** PF/VF number */ + uint16_t func; +} __attribute__ (( packed )); + +/** Switching element ID type mask */ +#define ICE_ADMIN_SWITCH_TYPE_MASK 0xc000 + +/** Virtual Station Interface element type */ +#define ICE_ADMIN_SWITCH_TYPE_VSI 0x8000 + +/** Admin queue Get Switch Configuration data buffer */ +struct ice_admin_switch_buffer { + /** Switch configuration */ + struct ice_admin_switch_config cfg[1]; +}; + +/** Admin queue Add Switch Rules command */ +#define ICE_ADMIN_ADD_RULES 0x02a0 + +/** Admin queue Add Switch Rules command parameters */ +struct ice_admin_rules_params { + /** Number of rules */ + uint16_t count; + /** Reserved */ + uint8_t reserved[6]; +} __attribute__ (( packed )); + +/** Admin queue Add Switch Rules data buffer */ +struct ice_admin_rules_buffer { + /** Type */ + uint16_t type; + /** Return status */ + uint16_t status; + /** Receipt ID */ + uint16_t recipe; + /** Source port */ + uint16_t port; + /** Action */ + uint32_t action; + /** Lookup table index */ + uint16_t index; + /** Header length */ + uint16_t len; + /** Header data */ + union { + /** Ethernet header */ + struct ethhdr eth; + /** Raw data */ + uint8_t raw[16]; + } __attribute__ (( packed )) hdr; +} __attribute__ (( packed )); + +/** Switch rule promiscuous recipe ID */ +#define ICE_ADMIN_RULES_RECIPE_PROMISC 0x0003 + +/** Switch rule action valid */ +#define ICE_ADMIN_RULES_ACTION_VALID 0x00020000UL + +/** Switch rule VSI number */ +#define ICE_ADMIN_RULES_ACTION_VSI(x) ( (x) << 4 ) + +/** Admin queue Query Default Scheduling Tree Topology command */ +#define ICE_ADMIN_SCHEDULE 0x0400 + +/** Admin queue Query Default Scheduling Tree Topology command parameters */ +struct ice_admin_schedule_params { + /** Reserved */ + uint8_t reserved_a; + /** Total branches */ + uint8_t branches; + /** Reserved */ + uint8_t reserved_b[6]; +} __attribute__ (( packed )); + +/** Transmit scheduler configuration generic section is valid */ +#define ICE_SCHEDULE_GENERIC 0x01 + +/** Transmit scheduler configuration committed bandwidth section is valid */ +#define ICE_SCHEDULE_COMMIT 0x02 + +/** Transmit scheduler configuration excess bandwidth section is valid */ +#define ICE_SCHEDULE_EXCESS 0x04 + +/** Transmit scheduler configuration default weight */ +#define ICE_SCHEDULE_WEIGHT 0x0004 + +/** Admin queue Query Default Scheduling Tree Topology node */ +struct ice_admin_schedule_node { + /** Parent TEID */ + uint32_t parent; + /** Node TEID */ + uint32_t teid; + /** Scheduler configuration */ + struct ice_schedule_tx config; +} __attribute__ (( packed )); + +/** Admin queue Query Default Scheduling Tree Topology branch */ +struct ice_admin_schedule_branch { + /** Reserved */ + uint8_t reserved_a[4]; + /** Number of nodes */ + uint16_t count; + /** Reserved */ + uint8_t reserved_b[2]; + /** Nodes */ + struct ice_admin_schedule_node node[0]; +} __attribute__ (( packed )); + +/** Admin queue Query Default Scheduling Tree Topology data buffer */ +union ice_admin_schedule_buffer { + /** Branches */ + struct ice_admin_schedule_branch branch[0]; + /** Padding */ + uint8_t pad[INTELXL_ADMIN_BUFFER_SIZE]; +} __attribute__ (( packed )); + +/** Admin queue Restart Autonegotiation command parameters */ +struct ice_admin_autoneg_params { + /** Reserved */ + uint8_t reserved_a[2]; + /** Flags */ + uint8_t flags; + /** Reserved */ + uint8_t reserved_b[13]; +} __attribute__ (( packed )); + +/** Admin queue Get Link Status command parameters */ +struct ice_admin_link_params { + /** Logical port number */ + uint8_t port; + /** Reserved */ + uint8_t reserved_a; + /** Link status notification */ + uint8_t notify; + /** Reserved */ + uint8_t reserved_b[13]; +} __attribute__ (( packed )); + +/** Admin queue Get Link Status data buffer */ +struct ice_admin_link_buffer { + /** Topology conflicts */ + uint8_t conflict; + /** Configuration errors */ + uint8_t error; + /** Link status */ + uint8_t status; + /** Reserved */ + uint8_t reserved_a[7]; + /** Link speed */ + uint16_t speed; + /** Reserved */ + uint8_t reserved_b[20]; +} __attribute__ (( packed )); + +/** Admin queue Add Transmit Queues command */ +#define ICE_ADMIN_ADD_TXQ 0x0c30 + +/** Admin queue Add Transmit Queues command parameters */ +struct ice_admin_add_txq_params { + /** Number of queue groups */ + uint8_t count; + /** Reserved */ + uint8_t reserved[7]; +} __attribute__ (( packed )); + +/** Admin queue Add Transmit Queues data buffer */ +struct ice_admin_add_txq_buffer { + /** Parent TEID */ + uint32_t parent; + /** Number of queues */ + uint8_t count; + /** Reserved */ + uint8_t reserved_a[3]; + /** Transmit queue ID */ + uint16_t id; + /** Reserved */ + uint8_t reserved_b[2]; + /** Queue TEID */ + uint32_t teid; + /** Transmit queue context */ + struct ice_context_tx ctx; + /** Scheduler configuration */ + struct ice_schedule_tx sched; +} __attribute__ (( packed )); + +/** Transmit queue base address and port number */ +#define ICE_TXQ_BASE_PORT( addr, port ) \ + ( ( (addr) >> 7 ) | ( ( ( uint64_t ) (port) ) << 57 ) ) + +/** Transmit queue PF number */ +#define ICE_TXQ_PF_TYPE( pf ) ( ( (pf) << 1 ) | ( 0x2 << 14 ) ) + +/** Transmit queue length */ +#define ICE_TXQ_LEN( count ) ( (count) >> 1 ) + +/** Transmit queue uses TSO */ +#define ICE_TXQ_FL_TSO 0x0001 + +/** Transmit queue uses legacy mode*/ +#define ICE_TXQ_FL_LEGACY 0x1000 + +/** Admin queue Disable Transmit Queues command */ +#define ICE_ADMIN_DISABLE_TXQ 0x0c31 + +/** Admin queue Disable Transmit Queues command parameters */ +struct ice_admin_disable_txq_params { + /** Flags */ + uint8_t flags; + /** Number of queue groups */ + uint8_t count; + /** Reserved */ + uint8_t reserved_a; + /** Timeout */ + uint8_t timeout; + /** Reserved */ + uint8_t reserved_b[4]; +} __attribute__ (( packed )); + +/** Disable queue and flush pipe */ +#define ICE_TXQ_FL_FLUSH 0x08 + +/** Disable queue timeout */ +#define ICE_TXQ_TIMEOUT 0xc8 + +/** Admin queue Disable Transmit Queues data buffer */ +struct ice_admin_disable_txq_buffer { + /** Parent TEID */ + uint32_t parent; + /** Number of queues */ + uint8_t count; + /** Reserved */ + uint8_t reserved; + /** Transmit queue ID */ + uint16_t id; +} __attribute__ (( packed )); + +/** Admin queue command parameters */ +union ice_admin_params { + /** Additional data buffer command parameters */ + struct intelxl_admin_buffer_params buffer; + /** Get Version command parameters */ + struct ice_admin_version_params version; + /** Manage MAC Address Read command parameters */ + struct ice_admin_mac_read_params mac_read; + /** Manage MAC Address Write command parameters */ + struct ice_admin_mac_write_params mac_write; + /** Get Switch Configuration command parameters */ + struct ice_admin_switch_params sw; + /** Add Switch Rules command parameters */ + struct ice_admin_rules_params rules; + /** Query Default Scheduling Tree Topology command parameters */ + struct ice_admin_schedule_params sched; + /** Restart Autonegotiation command parameters */ + struct ice_admin_autoneg_params autoneg; + /** Get Link Status command parameters */ + struct ice_admin_link_params link; + /** Add Transmit Queue command parameters */ + struct ice_admin_add_txq_params add_txq; + /** Disable Transmit Queue command parameters */ + struct ice_admin_disable_txq_params disable_txq; +} __attribute__ (( packed )); + + +/** Admin queue data buffer */ +union ice_admin_buffer { + /** Original 40 Gigabit Ethernet data buffer */ + union intelxl_admin_buffer xl; + /** Manage MAC Address Read data buffer */ + struct ice_admin_mac_read_buffer mac_read; + /** Get Switch Configuration data buffer */ + struct ice_admin_switch_buffer sw; + /** Add Switch Rules data buffer */ + struct ice_admin_rules_buffer rules; + /** Query Default Scheduling Tree Topology data buffer */ + union ice_admin_schedule_buffer sched; + /** Get Link Status data buffer */ + struct ice_admin_link_buffer link; + /** Add Transmit Queue data buffer */ + struct ice_admin_add_txq_buffer add_txq; + /** Disable Transmit Queue data buffer */ + struct ice_admin_disable_txq_buffer disable_txq; +} __attribute__ (( packed )); + +/** Admin queue descriptor */ +struct ice_admin_descriptor { + /** Transparent union */ + union { + /** Original 40 Gigabit Ethernet descriptor */ + struct intelxl_admin_descriptor xl; + /** Transparent struct */ + struct { + /** Flags */ + uint16_t flags; + /** Opcode */ + uint16_t opcode; + /** Data length */ + uint16_t len; + /** Return value */ + uint16_t ret; + /** Opaque cookie */ + uint32_t cookie; + /** Reserved */ + uint8_t reserved[4]; + /** Parameters */ + union ice_admin_params params; + } __attribute__ (( packed )); + } __attribute__ (( packed )); +} __attribute__ (( packed )); + +/** + * Get next admin command queue descriptor + * + * @v intelxl Intel device + * @ret cmd Command descriptor + */ +struct ice_admin_descriptor * +ice_admin_command_descriptor ( struct intelxl_nic *intelxl ) { + struct intelxl_admin_descriptor *xlcmd = + intelxl_admin_command_descriptor ( intelxl ); + + return container_of ( xlcmd, struct ice_admin_descriptor, xl ); +} + +/** + * Get next admin command queue data buffer + * + * @v intelxl Intel device + * @ret buf Data buffer + */ +static inline __attribute__ (( always_inline )) union ice_admin_buffer * +ice_admin_command_buffer ( struct intelxl_nic *intelxl ) { + union intelxl_admin_buffer *xlbuf = + intelxl_admin_command_buffer ( intelxl ); + + return container_of ( xlbuf, union ice_admin_buffer, xl ); +} + +/****************************************************************************** + * + * Top level + * + ****************************************************************************** + */ + +/** Function Requester ID Information Register */ +#define ICE_PFFUNC_RID 0x09e880 +#define ICE_PFFUNC_RID_FUNC_NUM(x) \ + ( ( (x) >> 0 ) & 0x7 ) /**< Function number */ + +/** PF LAN Port Number Register */ +#define ICE_PFGEN_PORTNUM 0x1d2400 +#define ICE_PFGEN_PORTNUM_PORT_NUM(x) \ + ( ( (x) >> 0 ) & 0x7 ) /**< Port number */ + +/** Transmit Queue Interrupt Cause Control Register */ +#define ICE_QINT_TQCTL 0x140000 +#define ICE_QINT_TQCTL_ITR_INDX(x) ( (x) << 11 ) /**< Throttling */ +#define ICE_QINT_TQCTL_ITR_INDX_NONE \ + ICE_QINT_TQCTL_ITR_INDX ( 0x3 ) /**< No throttling */ +#define ICE_QINT_TQCTL_CAUSE_ENA 0x40000000UL /**< Enable */ + +/** Receive Queue Interrupt Cause Control Register */ +#define ICE_QINT_RQCTL 0x150000 +#define ICE_QINT_RQCTL_ITR_INDX(x) ( (x) << 11 ) /**< Throttling */ +#define ICE_QINT_RQCTL_ITR_INDX_NONE \ + ICE_QINT_RQCTL_ITR_INDX ( 0x3 ) /**< No throttling */ +#define ICE_QINT_RQCTL_CAUSE_ENA 0x40000000UL /**< Enable */ + +/** Global Interrupt Dynamic Control Register */ +#define ICE_GLINT_DYN_CTL 0x160000 + +#endif /* _ICE_H */ diff --git a/src/drivers/net/intelxl.c b/src/drivers/net/intelxl.c index ac9e37c5a..82b07833c 100644 --- a/src/drivers/net/intelxl.c +++ b/src/drivers/net/intelxl.c @@ -46,79 +46,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /****************************************************************************** * - * Device reset - * - ****************************************************************************** - */ - -/** - * Reset hardware - * - * @v intelxl Intel device - * @ret rc Return status code - */ -static int intelxl_reset ( struct intelxl_nic *intelxl ) { - uint32_t pfgen_ctrl; - - /* Perform a global software reset */ - pfgen_ctrl = readl ( intelxl->regs + INTELXL_PFGEN_CTRL ); - writel ( ( pfgen_ctrl | INTELXL_PFGEN_CTRL_PFSWR ), - intelxl->regs + INTELXL_PFGEN_CTRL ); - mdelay ( INTELXL_RESET_DELAY_MS ); - - return 0; -} - -/****************************************************************************** - * - * MAC address - * - ****************************************************************************** - */ - -/** - * Fetch initial MAC address and maximum frame size - * - * @v intelxl Intel device - * @v netdev Network device - * @ret rc Return status code - */ -static int intelxl_fetch_mac ( struct intelxl_nic *intelxl, - struct net_device *netdev ) { - union intelxl_receive_address mac; - uint32_t prtpm_sal; - uint32_t prtpm_sah; - uint32_t prtgl_sah; - size_t mfs; - - /* Read NVM-loaded address */ - prtpm_sal = readl ( intelxl->regs + INTELXL_PRTPM_SAL ); - prtpm_sah = readl ( intelxl->regs + INTELXL_PRTPM_SAH ); - mac.reg.low = cpu_to_le32 ( prtpm_sal ); - mac.reg.high = cpu_to_le32 ( prtpm_sah ); - - /* Check that address is valid */ - if ( ! is_valid_ether_addr ( mac.raw ) ) { - DBGC ( intelxl, "INTELXL %p has invalid MAC address (%s)\n", - intelxl, eth_ntoa ( mac.raw ) ); - return -ENOENT; - } - - /* Copy MAC address */ - DBGC ( intelxl, "INTELXL %p has autoloaded MAC address %s\n", - intelxl, eth_ntoa ( mac.raw ) ); - memcpy ( netdev->hw_addr, mac.raw, ETH_ALEN ); - - /* Get maximum frame size */ - prtgl_sah = readl ( intelxl->regs + INTELXL_PRTGL_SAH ); - mfs = INTELXL_PRTGL_SAH_MFS_GET ( prtgl_sah ); - netdev->max_pkt_len = ( mfs - 4 /* CRC */ ); - - return 0; -} - -/****************************************************************************** - * * MSI-X interrupts * ****************************************************************************** @@ -129,10 +56,11 @@ static int intelxl_fetch_mac ( struct intelxl_nic *intelxl, * * @v intelxl Intel device * @v pci PCI device + * @v vector MSI-X vector * @ret rc Return status code */ int intelxl_msix_enable ( struct intelxl_nic *intelxl, - struct pci_device *pci ) { + struct pci_device *pci, unsigned int vector ) { int rc; /* Map dummy target location */ @@ -151,12 +79,12 @@ int intelxl_msix_enable ( struct intelxl_nic *intelxl, goto err_enable; } - /* Configure interrupt zero to write to dummy location */ - pci_msix_map ( &intelxl->msix.cap, 0, + /* Configure interrupt to write to dummy location */ + pci_msix_map ( &intelxl->msix.cap, vector, dma ( &intelxl->msix.map, &intelxl->msix.msg ), 0 ); - /* Enable dummy interrupt zero */ - pci_msix_unmask ( &intelxl->msix.cap, 0 ); + /* Enable dummy interrupt */ + pci_msix_unmask ( &intelxl->msix.cap, vector ); return 0; @@ -172,12 +100,13 @@ int intelxl_msix_enable ( struct intelxl_nic *intelxl, * * @v intelxl Intel device * @v pci PCI device + * @v vector MSI-X vector */ void intelxl_msix_disable ( struct intelxl_nic *intelxl, - struct pci_device *pci ) { + struct pci_device *pci, unsigned int vector ) { - /* Disable dummy interrupt zero */ - pci_msix_mask ( &intelxl->msix.cap, 0 ); + /* Disable dummy interrupts */ + pci_msix_mask ( &intelxl->msix.cap, vector ); /* Disable MSI-X capability */ pci_msix_disable ( pci, &intelxl->msix.cap ); @@ -194,7 +123,7 @@ void intelxl_msix_disable ( struct intelxl_nic *intelxl, */ /** Admin queue register offsets */ -static const struct intelxl_admin_offsets intelxl_admin_offsets = { +const struct intelxl_admin_offsets intelxl_admin_offsets = { .bal = INTELXL_ADMIN_BAL, .bah = INTELXL_ADMIN_BAH, .len = INTELXL_ADMIN_LEN, @@ -371,6 +300,7 @@ int intelxl_admin_command ( struct intelxl_nic *intelxl ) { union intelxl_admin_buffer *buf; uint64_t address; uint32_t cookie; + uint16_t silence; unsigned int index; unsigned int tail; unsigned int i; @@ -383,15 +313,18 @@ int intelxl_admin_command ( struct intelxl_nic *intelxl ) { 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 ) ); + if ( cmd->cookie ) + DBGC2 ( intelxl, "/%#08x", le32_to_cpu ( cmd->cookie ) ); DBGC2 ( intelxl, ":\n" ); + /* Allow expected errors to be silenced */ + silence = cmd->ret; + cmd->ret = 0; + /* 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 ) ) { @@ -401,7 +334,7 @@ int intelxl_admin_command ( struct intelxl_nic *intelxl ) { } /* Populate cookie, if not being (ab)used for VF opcode */ - if ( ! cmd->vopcode ) + if ( ! cmd->cookie ) cmd->cookie = cpu_to_le32 ( index ); /* Record cookie */ @@ -409,7 +342,7 @@ int intelxl_admin_command ( struct intelxl_nic *intelxl ) { /* Post command descriptor */ DBGC2_HDA ( intelxl, virt_to_phys ( cmd ), cmd, sizeof ( *cmd ) ); - if ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ) ) { + if ( cmd->flags & cpu_to_le16 ( INTELXL_ADMIN_FL_RD ) ) { DBGC2_HDA ( intelxl, virt_to_phys ( buf ), buf, le16_to_cpu ( cmd->len ) ); } @@ -428,6 +361,10 @@ int intelxl_admin_command ( struct intelxl_nic *intelxl ) { intelxl, index ); 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 ) ); + } /* Check for cookie mismatch */ if ( cmd->cookie != cookie ) { @@ -438,8 +375,8 @@ int intelxl_admin_command ( struct intelxl_nic *intelxl ) { goto err; } - /* Check for errors */ - if ( cmd->ret != 0 ) { + /* Check for unexpected errors */ + if ( ( cmd->ret != 0 ) && ( cmd->ret != silence ) ) { DBGC ( intelxl, "INTELXL %p admin command %#x error " "%d\n", intelxl, index, le16_to_cpu ( cmd->ret ) ); @@ -551,28 +488,102 @@ static int intelxl_admin_shutdown ( struct intelxl_nic *intelxl ) { } /** + * Get MAC address + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxl_admin_mac_read ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxl_admin_descriptor *cmd; + struct intelxl_admin_mac_read_params *read; + union intelxl_admin_buffer *buf; + uint8_t *mac; + int rc; + + /* Populate descriptor */ + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_MAC_READ ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->mac_read ) ); + read = &cmd->params.mac_read; + buf = intelxl_admin_command_buffer ( intelxl ); + mac = buf->mac_read.pf; + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + /* Check that MAC address is present in response */ + if ( ! ( read->valid & INTELXL_ADMIN_MAC_READ_VALID_LAN ) ) { + DBGC ( intelxl, "INTELXL %p has no MAC address\n", intelxl ); + return -ENOENT; + } + + /* Check that address is valid */ + if ( ! is_valid_ether_addr ( mac ) ) { + DBGC ( intelxl, "INTELXL %p has invalid MAC address (%s)\n", + intelxl, eth_ntoa ( mac ) ); + return -ENOENT; + } + + /* Copy MAC address */ + DBGC ( intelxl, "INTELXL %p has MAC address %s\n", + intelxl, eth_ntoa ( mac ) ); + memcpy ( netdev->hw_addr, mac, ETH_ALEN ); + + return 0; +} + +/** + * Set MAC address + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxl_admin_mac_write ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxl_admin_descriptor *cmd; + struct intelxl_admin_mac_write_params *write; + union { + uint8_t raw[ETH_ALEN]; + struct { + uint16_t high; + uint32_t low; + } __attribute__ (( packed )); + } mac; + int rc; + + /* Populate descriptor */ + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_MAC_WRITE ); + write = &cmd->params.mac_write; + memcpy ( mac.raw, netdev->ll_addr, ETH_ALEN ); + write->high = bswap_16 ( mac.high ); + write->low = bswap_32 ( mac.low ); + + /* Issue command */ + 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 ) { +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 ); + cmd->ret = cpu_to_le16 ( INTELXL_ADMIN_EEXIST ); pxe = &cmd->params.pxe; pxe->magic = INTELXL_ADMIN_CLEAR_PXE_MAGIC; @@ -580,6 +591,13 @@ static int intelxl_admin_clear_pxe ( struct intelxl_nic *intelxl ) { if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) return rc; + /* Check for expected errors */ + if ( cmd->ret == cpu_to_le16 ( INTELXL_ADMIN_EEXIST ) ) { + DBGC ( intelxl, "INTELXL %p already in non-PXE mode\n", + intelxl ); + return 0; + } + return 0; } @@ -593,18 +611,20 @@ static int intelxl_admin_switch ( struct intelxl_nic *intelxl ) { struct intelxl_admin_descriptor *cmd; struct intelxl_admin_switch_params *sw; union intelxl_admin_buffer *buf; + uint16_t next = 0; int rc; - /* Populate descriptor */ - 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 { + /* Populate descriptor */ + 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; + sw->next = next; + buf = intelxl_admin_command_buffer ( intelxl ); + /* Issue command */ if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) return rc; @@ -624,7 +644,7 @@ static int intelxl_admin_switch ( struct intelxl_nic *intelxl ) { buf->sw.cfg.connection ); } - } while ( sw->next ); + } while ( ( next = sw->next ) ); /* Check that we found a VSI */ if ( ! intelxl->vsi ) { @@ -701,6 +721,31 @@ static int intelxl_admin_promisc ( struct intelxl_nic *intelxl ) { } /** + * Set MAC configuration + * + * @v intelxl Intel device + * @ret rc Return status code + */ +int intelxl_admin_mac_config ( struct intelxl_nic *intelxl ) { + struct intelxl_admin_descriptor *cmd; + struct intelxl_admin_mac_config_params *config; + int rc; + + /* Populate descriptor */ + cmd = intelxl_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_MAC_CONFIG ); + config = &cmd->params.mac_config; + config->mfs = cpu_to_le16 ( intelxl->mfs ); + config->flags = INTELXL_ADMIN_MAC_CONFIG_FL_CRC; + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + return 0; +} + +/** * Restart autonegotiation * * @v intelxl Intel device @@ -760,18 +805,26 @@ static int intelxl_admin_link ( struct net_device *netdev ) { } /** - * Handle virtual function event (when VF driver is not present) + * Handle admin event * * @v netdev Network device - * @v evt Admin queue event descriptor - * @v buf Admin queue event data buffer + * @v evt Event descriptor + * @v buf Data buffer */ -__weak void -intelxlvf_admin_event ( struct net_device *netdev __unused, - struct intelxl_admin_descriptor *evt __unused, - union intelxl_admin_buffer *buf __unused ) { +static void intelxl_admin_event ( struct net_device *netdev, + struct intelxl_admin_descriptor *evt, + union intelxl_admin_buffer *buf __unused ) { + struct intelxl_nic *intelxl = netdev->priv; - /* Nothing to do */ + /* Ignore unrecognised events */ + if ( evt->opcode != cpu_to_le16 ( INTELXL_ADMIN_LINK ) ) { + DBGC ( intelxl, "INTELXL %p unrecognised event opcode " + "%#04x\n", intelxl, le16_to_cpu ( evt->opcode ) ); + return; + } + + /* Update link status */ + intelxl_admin_link ( netdev ); } /** @@ -823,19 +876,7 @@ void intelxl_poll_admin ( struct net_device *netdev ) { } /* Handle event */ - 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 ( evt->opcode ) ); - break; - } + intelxl->handle ( netdev, evt, buf ); /* Reset descriptor and refill queue */ intelxl_admin_event_init ( intelxl, admin->index ); @@ -864,18 +905,8 @@ int intelxl_open_admin ( struct intelxl_nic *intelxl ) { /* (Re)open admin queues */ intelxl_reopen_admin ( intelxl ); - /* Get firmware version */ - if ( ( rc = intelxl_admin_version ( intelxl ) ) != 0 ) - goto err_version; - - /* Report driver version */ - if ( ( rc = intelxl_admin_driver ( intelxl ) ) != 0 ) - goto err_driver; - return 0; - err_driver: - err_version: intelxl_disable_admin ( intelxl, &intelxl->command ); intelxl_disable_admin ( intelxl, &intelxl->event ); intelxl_free_admin ( intelxl, &intelxl->command ); @@ -963,8 +994,7 @@ int intelxl_alloc_ring ( struct intelxl_nic *intelxl, ring->cons = 0; DBGC ( intelxl, "INTELXL %p ring %06x is at [%08lx,%08lx)\n", - intelxl, ( ring->reg + ring->tail ), - virt_to_phys ( ring->desc.raw ), + intelxl, ring->tail, virt_to_phys ( ring->desc.raw ), ( virt_to_phys ( ring->desc.raw ) + ring->len ) ); return 0; @@ -1203,7 +1233,7 @@ static int intelxl_enable_ring ( struct intelxl_nic *intelxl, qxx_ena = readl ( ring_regs + INTELXL_QXX_ENA ); if ( ! ( qxx_ena & INTELXL_QXX_ENA_STAT ) ) { DBGC ( intelxl, "INTELXL %p ring %06x failed to enable: " - "%#08x\n", intelxl, ring->reg, qxx_ena ); + "%#08x\n", intelxl, ring->tail, qxx_ena ); return -EIO; } @@ -1239,7 +1269,7 @@ static int intelxl_disable_ring ( struct intelxl_nic *intelxl, } DBGC ( intelxl, "INTELXL %p ring %06x timed out waiting for disable: " - "%#08x\n", intelxl, ring->reg, qxx_ena ); + "%#08x\n", intelxl, ring->tail, qxx_ena ); return -ETIMEDOUT; } @@ -1250,8 +1280,8 @@ static int intelxl_disable_ring ( struct intelxl_nic *intelxl, * @v ring Descriptor ring * @ret rc Return status code */ -static int intelxl_create_ring ( struct intelxl_nic *intelxl, - struct intelxl_ring *ring ) { +int intelxl_create_ring ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ) { physaddr_t address; int rc; @@ -1284,8 +1314,8 @@ static int intelxl_create_ring ( struct intelxl_nic *intelxl, * @v intelxl Intel device * @v ring Descriptor ring */ -static void intelxl_destroy_ring ( struct intelxl_nic *intelxl, - struct intelxl_ring *ring ) { +void intelxl_destroy_ring ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ) { int rc; /* Disable ring */ @@ -1377,24 +1407,20 @@ void intelxl_empty_rx ( struct intelxl_nic *intelxl ) { */ static int intelxl_open ( struct net_device *netdev ) { struct intelxl_nic *intelxl = netdev->priv; - union intelxl_receive_address mac; unsigned int queue; - uint32_t prtgl_sal; - uint32_t prtgl_sah; int rc; /* Calculate maximum frame size */ intelxl->mfs = ( ( ETH_HLEN + netdev->mtu + 4 /* CRC */ + INTELXL_ALIGN - 1 ) & ~( INTELXL_ALIGN - 1 ) ); - /* Program MAC address and maximum frame size */ - memset ( &mac, 0, sizeof ( mac ) ); - memcpy ( mac.raw, netdev->ll_addr, sizeof ( mac.raw ) ); - prtgl_sal = le32_to_cpu ( mac.reg.low ); - prtgl_sah = ( le32_to_cpu ( mac.reg.high ) | - INTELXL_PRTGL_SAH_MFS_SET ( intelxl->mfs ) ); - writel ( prtgl_sal, intelxl->regs + INTELXL_PRTGL_SAL ); - writel ( prtgl_sah, intelxl->regs + INTELXL_PRTGL_SAH ); + /* Set MAC address */ + if ( ( rc = intelxl_admin_mac_write ( netdev ) ) != 0 ) + goto err_mac_write; + + /* Set maximum frame size */ + if ( ( rc = intelxl_admin_mac_config ( intelxl ) ) != 0 ) + goto err_mac_config; /* Associate transmit queue to PF */ writel ( ( INTELXL_QXX_CTL_PFVF_Q_PF | @@ -1437,6 +1463,8 @@ static int intelxl_open ( struct net_device *netdev ) { err_create_tx: intelxl_destroy_ring ( intelxl, &intelxl->rx ); err_create_rx: + err_mac_config: + err_mac_write: return rc; } @@ -1668,11 +1696,13 @@ static int intelxl_probe ( struct pci_device *pci ) { goto err_alloc; } netdev_init ( netdev, &intelxl_operations ); + netdev->max_pkt_len = INTELXL_MAX_PKT_LEN; intelxl = netdev->priv; pci_set_drvdata ( pci, netdev ); netdev->dev = &pci->dev; memset ( intelxl, 0, sizeof ( *intelxl ) ); intelxl->intr = INTELXL_PFINT_DYN_CTL0; + intelxl->handle = intelxl_admin_event; intelxl_init_admin ( &intelxl->command, INTELXL_ADMIN_CMD, &intelxl_admin_offsets ); intelxl_init_admin ( &intelxl->event, INTELXL_ADMIN_EVT, @@ -1699,9 +1729,17 @@ static int intelxl_probe ( struct pci_device *pci ) { dma_set_mask_64bit ( intelxl->dma ); netdev->dma = intelxl->dma; - /* Reset the NIC */ - if ( ( rc = intelxl_reset ( intelxl ) ) != 0 ) - goto err_reset; + /* 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 */ + pci_reset ( pci, intelxl->exp ); /* Get function number, port number and base queue number */ pffunc_rid = readl ( intelxl->regs + INTELXL_PFFUNC_RID ); @@ -1714,18 +1752,23 @@ static int intelxl_probe ( struct pci_device *pci ) { intelxl, intelxl->pf, intelxl->port, intelxl->base, INTELXL_PFLAN_QALLOC_LASTQ ( pflan_qalloc ) ); - /* Fetch MAC address and maximum frame size */ - 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 ) + if ( ( rc = intelxl_msix_enable ( intelxl, pci, + INTELXL_MSIX_VECTOR ) ) != 0 ) goto err_msix; /* Open admin queues */ if ( ( rc = intelxl_open_admin ( intelxl ) ) != 0 ) goto err_open_admin; + /* Get firmware version */ + if ( ( rc = intelxl_admin_version ( intelxl ) ) != 0 ) + goto err_admin_version; + + /* Report driver version */ + if ( ( rc = intelxl_admin_driver ( intelxl ) ) != 0 ) + goto err_admin_driver; + /* Clear PXE mode */ if ( ( rc = intelxl_admin_clear_pxe ( intelxl ) ) != 0 ) goto err_admin_clear_pxe; @@ -1742,6 +1785,10 @@ static int intelxl_probe ( struct pci_device *pci ) { if ( ( rc = intelxl_admin_promisc ( intelxl ) ) != 0 ) goto err_admin_promisc; + /* Get MAC address */ + if ( ( rc = intelxl_admin_mac_read ( netdev ) ) != 0 ) + goto err_admin_mac_read; + /* Configure queue register addresses */ intelxl->tx.reg = INTELXL_QTX ( intelxl->queue ); intelxl->tx.tail = ( intelxl->tx.reg + INTELXL_QXX_TAIL ); @@ -1773,17 +1820,19 @@ static int intelxl_probe ( struct pci_device *pci ) { unregister_netdev ( netdev ); err_register_netdev: + err_admin_mac_read: err_admin_promisc: err_admin_vsi: err_admin_switch: err_admin_clear_pxe: + err_admin_driver: + err_admin_version: intelxl_close_admin ( intelxl ); err_open_admin: - intelxl_msix_disable ( intelxl, pci ); + intelxl_msix_disable ( intelxl, pci, INTELXL_MSIX_VECTOR ); err_msix: - err_fetch_mac: - intelxl_reset ( intelxl ); - err_reset: + pci_reset ( pci, intelxl->exp ); + err_exp: iounmap ( intelxl->regs ); err_ioremap: netdev_nullify ( netdev ); @@ -1808,10 +1857,10 @@ static void intelxl_remove ( struct pci_device *pci ) { intelxl_close_admin ( intelxl ); /* Disable MSI-X dummy interrupt */ - intelxl_msix_disable ( intelxl, pci ); + intelxl_msix_disable ( intelxl, pci, INTELXL_MSIX_VECTOR ); /* Reset the NIC */ - intelxl_reset ( intelxl ); + pci_reset ( pci, intelxl->exp ); /* Free network device */ iounmap ( intelxl->regs ); @@ -1821,6 +1870,10 @@ static void intelxl_remove ( struct pci_device *pci ) { /** PCI device IDs */ static struct pci_device_id intelxl_nics[] = { + PCI_ROM ( 0x8086, 0x0cf8, "x710-n3000", "X710 FPGA N3000", 0 ), + PCI_ROM ( 0x8086, 0x0d58, "xxv710-n3000", "XXV710 FPGA N3000", 0 ), + PCI_ROM ( 0x8086, 0x104e, "x710-sfp-b", "X710 10GbE SFP+", 0 ), + PCI_ROM ( 0x8086, 0x104f, "x710-kx-b", "X710 10GbE backplane", 0 ), PCI_ROM ( 0x8086, 0x1572, "x710-sfp", "X710 10GbE SFP+", 0 ), PCI_ROM ( 0x8086, 0x1574, "xl710-qemu", "Virtual XL710", 0 ), PCI_ROM ( 0x8086, 0x1580, "xl710-kx-b", "XL710 40GbE backplane", 0 ), @@ -1834,6 +1887,7 @@ static struct pci_device_id intelxl_nics[] = { PCI_ROM ( 0x8086, 0x1589, "x710-10gt4", "X710 10GBASE-T4", 0 ), PCI_ROM ( 0x8086, 0x158a, "xxv710", "XXV710 25GbE backplane", 0 ), PCI_ROM ( 0x8086, 0x158b, "xxv710-sfp28", "XXV710 25GbE SFP28", 0 ), + PCI_ROM ( 0x8086, 0x15ff, "x710-10gt-b", "X710 10GBASE-T", 0 ), PCI_ROM ( 0x8086, 0x37ce, "x722-kx", "X722 10GbE backplane", 0 ), PCI_ROM ( 0x8086, 0x37cf, "x722-qsfp", "X722 10GbE QSFP+", 0 ), PCI_ROM ( 0x8086, 0x37d0, "x722-sfp", "X722 10GbE SFP+", 0 ), diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h index a4a776d28..6c7865aa2 100644 --- a/src/drivers/net/intelxl.h +++ b/src/drivers/net/intelxl.h @@ -145,6 +145,49 @@ struct intelxl_admin_shutdown_params { /** Driver is unloading */ #define INTELXL_ADMIN_SHUTDOWN_UNLOADING 0x01 +/** Admin queue Manage MAC Address Read command */ +#define INTELXL_ADMIN_MAC_READ 0x0107 + +/** Admin queue Manage MAC Address Read command parameters */ +struct intelxl_admin_mac_read_params { + /** Valid addresses */ + uint8_t valid; + /** Reserved */ + uint8_t reserved[15]; +} __attribute__ (( packed )); + +/** LAN MAC address is valid */ +#define INTELXL_ADMIN_MAC_READ_VALID_LAN 0x10 + +/** Admin queue Manage MAC Address Read data buffer */ +struct intelxl_admin_mac_read_buffer { + /** Physical function MAC address */ + uint8_t pf[ETH_ALEN]; + /** Reserved */ + uint8_t reserved[ETH_ALEN]; + /** Port MAC address */ + uint8_t port[ETH_ALEN]; + /** Physical function wake-on-LAN MAC address */ + uint8_t wol[ETH_ALEN]; +} __attribute__ (( packed )); + +/** Admin queue Manage MAC Address Write command */ +#define INTELXL_ADMIN_MAC_WRITE 0x0108 + +/** Admin queue Manage MAC Address Write command parameters */ +struct intelxl_admin_mac_write_params { + /** Reserved */ + uint8_t reserved_a[1]; + /** Write type */ + uint8_t type; + /** MAC address first 16 bits, byte-swapped */ + uint16_t high; + /** MAC address last 32 bits, byte-swapped */ + uint32_t low; + /** Reserved */ + uint8_t reserved_b[8]; +} __attribute__ (( packed )); + /** Admin queue Clear PXE Mode command */ #define INTELXL_ADMIN_CLEAR_PXE 0x0110 @@ -263,6 +306,22 @@ struct intelxl_admin_promisc_params { /** Promiscuous VLAN mode */ #define INTELXL_ADMIN_PROMISC_FL_VLAN 0x0010 +/** Admin queue Set MAC Configuration command */ +#define INTELXL_ADMIN_MAC_CONFIG 0x0603 + +/** Admin queue Set MAC Configuration command parameters */ +struct intelxl_admin_mac_config_params { + /** Maximum frame size */ + uint16_t mfs; + /** Flags */ + uint8_t flags; + /** Reserved */ + uint8_t reserved[13]; +} __attribute__ (( packed )); + +/** Append CRC on transmit */ +#define INTELXL_ADMIN_MAC_CONFIG_FL_CRC 0x04 + /** Admin queue Restart Autonegotiation command */ #define INTELXL_ADMIN_AUTONEG 0x0605 @@ -305,172 +364,6 @@ 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 */ @@ -481,6 +374,10 @@ union intelxl_admin_params { struct intelxl_admin_driver_params driver; /** Shutdown command parameters */ struct intelxl_admin_shutdown_params shutdown; + /** Manage MAC Address Read command parameters */ + struct intelxl_admin_mac_read_params mac_read; + /** Manage MAC Address Write command parameters */ + struct intelxl_admin_mac_write_params mac_write; /** Clear PXE Mode command parameters */ struct intelxl_admin_clear_pxe_params pxe; /** Get Switch Configuration command parameters */ @@ -489,34 +386,29 @@ union intelxl_admin_params { struct intelxl_admin_vsi_params vsi; /** Set VSI Promiscuous Modes command parameters */ struct intelxl_admin_promisc_params promisc; + /** Set MAC Configuration command parameters */ + struct intelxl_admin_mac_config_params mac_config; /** Restart Autonegotiation command parameters */ struct intelxl_admin_autoneg_params autoneg; /** Get Link Status command parameters */ struct intelxl_admin_link_params link; } __attribute__ (( packed )); +/** Maximum size of a data buffer */ +#define INTELXL_ADMIN_BUFFER_SIZE 0x1000 + /** Admin queue data buffer */ union intelxl_admin_buffer { /** Driver Version data buffer */ struct intelxl_admin_driver_buffer driver; + /** Manage MAC Address Read data buffer */ + struct intelxl_admin_mac_read_buffer mac_read; /** Get Switch Configuration data 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]; + /** Maximum buffer size */ + uint8_t pad[INTELXL_ADMIN_BUFFER_SIZE]; } __attribute__ (( packed )); /** Admin queue descriptor */ @@ -529,15 +421,10 @@ struct intelxl_admin_descriptor { uint16_t len; /** Return value */ uint16_t ret; - /** Opaque cookie / VF opcode */ - union { - /** Cookie */ - uint32_t cookie; - /** VF opcode */ - uint32_t vopcode; - }; - /** VF return value */ - int32_t vret; + /** Opaque cookie */ + uint32_t cookie; + /** Reserved */ + uint8_t reserved[4]; /** Parameters */ union intelxl_admin_params params; } __attribute__ (( packed )); @@ -557,6 +444,9 @@ struct intelxl_admin_descriptor { /** Admin descriptor uses data buffer */ #define INTELXL_ADMIN_FL_BUF 0x1000 +/** Error: attempt to create something that already exists */ +#define INTELXL_ADMIN_EEXIST 13 + /** Admin queue */ struct intelxl_admin { /** Descriptors */ @@ -685,6 +575,8 @@ struct intelxl_context_rx { uint8_t reserved_b[7]; /** Maximum frame size */ uint16_t mfs; + /** Reserved */ + uint8_t reserved_c[8]; } __attribute__ (( packed )); /** Receive queue base address and queue count */ @@ -755,10 +647,6 @@ 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 */ @@ -919,9 +807,9 @@ intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, size_t len, /** Number of receive descriptors * - * Must be a multiple of 32. + * Must be a multiple of 32 and greater than or equal to 64. */ -#define INTELXL_RX_NUM_DESC 32 +#define INTELXL_RX_NUM_DESC 64 /** Receive descriptor ring fill level * @@ -929,6 +817,9 @@ intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, size_t len, */ #define INTELXL_RX_FILL 16 +/** Maximum packet length (excluding CRC) */ +#define INTELXL_MAX_PKT_LEN ( 9728 - 4 /* CRC */ ) + /****************************************************************************** * * Top level @@ -983,17 +874,10 @@ intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, size_t len, INTELXL_QINT_TQCTL_NEXTQ_TYPE ( 0x1 ) /**< Transmit queue */ #define INTELXL_QINT_TQCTL_CAUSE_ENA 0x40000000UL /**< Enable */ -/** PF Control Register */ -#define INTELXL_PFGEN_CTRL 0x092400 -#define INTELXL_PFGEN_CTRL_PFSWR 0x00000001UL /**< Software Reset */ - -/** Time to delay for device reset, in milliseconds */ -#define INTELXL_RESET_DELAY_MS 100 - /** Function Requester ID Information Register */ #define INTELXL_PFFUNC_RID 0x09c000 #define INTELXL_PFFUNC_RID_FUNC_NUM(x) \ - ( ( (x) >> 0 ) & 0x3 ) /**< Function number */ + ( ( (x) >> 0 ) & 0x7 ) /**< Function number */ /** PF Queue Allocation Register */ #define INTELXL_PFLAN_QALLOC 0x1c0400 @@ -1007,29 +891,6 @@ intelxl_init_ring ( struct intelxl_ring *ring, unsigned int count, size_t len, #define INTELXL_PFGEN_PORTNUM_PORT_NUM(x) \ ( ( (x) >> 0 ) & 0x3 ) /**< Port number */ -/** Port MAC Address Low Register */ -#define INTELXL_PRTGL_SAL 0x1e2120 - -/** Port MAC Address High Register */ -#define INTELXL_PRTGL_SAH 0x1e2140 -#define INTELXL_PRTGL_SAH_MFS_GET(x) ( (x) >> 16 ) /**< Max frame size */ -#define INTELXL_PRTGL_SAH_MFS_SET(x) ( (x) << 16 ) /**< Max frame size */ - -/** Physical Function MAC Address Low Register */ -#define INTELXL_PRTPM_SAL 0x1e4440 - -/** Physical Function MAC Address High Register */ -#define INTELXL_PRTPM_SAH 0x1e44c0 - -/** Receive address */ -union intelxl_receive_address { - struct { - uint32_t low; - uint32_t high; - } __attribute__ (( packed )) reg; - uint8_t raw[ETH_ALEN]; -}; - /** MSI-X interrupt */ struct intelxl_msix { /** PCI capability */ @@ -1040,7 +901,10 @@ struct intelxl_msix { struct dma_mapping map; }; -/** An Intel 40Gigabit network card */ +/** MSI-X interrupt vector */ +#define INTELXL_MSIX_VECTOR 0 + +/** An Intel 40 Gigabit network card */ struct intelxl_nic { /** Registers */ void *regs; @@ -1061,6 +925,10 @@ struct intelxl_nic { unsigned int vsi; /** Queue set handle */ unsigned int qset; + /** Transmit element ID */ + uint32_t teid; + /** Device capabilities */ + uint32_t caps; /** Interrupt control register */ unsigned int intr; /** PCI Express capability offset */ @@ -1075,10 +943,6 @@ struct intelxl_nic { /** 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; @@ -1086,17 +950,34 @@ struct intelxl_nic { struct intelxl_ring rx; /** Receive I/O buffers */ struct io_buffer *rx_iobuf[INTELXL_RX_NUM_DESC]; + + /** + * Handle admin event + * + * @v netdev Network device + * @v evt Event descriptor + * @v buf Data buffer + */ + void ( * handle ) ( struct net_device *netdev, + struct intelxl_admin_descriptor *evt, + union intelxl_admin_buffer *buf ); }; +extern const struct intelxl_admin_offsets intelxl_admin_offsets; + extern int intelxl_msix_enable ( struct intelxl_nic *intelxl, - struct pci_device *pci ); + struct pci_device *pci, + unsigned int vector ); extern void intelxl_msix_disable ( struct intelxl_nic *intelxl, - struct pci_device *pci ); + struct pci_device *pci, + unsigned int vector ); 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 int intelxl_admin_clear_pxe ( struct intelxl_nic *intelxl ); +extern int intelxl_admin_mac_config ( 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 ); @@ -1105,13 +986,13 @@ 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 int intelxl_create_ring ( struct intelxl_nic *intelxl, + struct intelxl_ring *ring ); +extern void intelxl_destroy_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 index 752de7815..083195513 100644 --- a/src/drivers/net/intelxlvf.c +++ b/src/drivers/net/intelxlvf.c @@ -46,23 +46,6 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); */ /** - * 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 @@ -120,26 +103,16 @@ static int intelxlvf_reset_wait_active ( struct intelxl_nic *intelxl ) { } /** - * Reset hardware via admin queue + * Wait for reset to complete * * @v intelxl Intel device * @ret rc Return status code */ -static int intelxlvf_reset_admin ( struct intelxl_nic *intelxl ) { - struct intelxl_admin_descriptor *cmd; +static int intelxlvf_reset_wait ( struct intelxl_nic *intelxl ) { 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 ); + mdelay ( INTELXLVF_RESET_DELAY_MS ); /* Wait for reset to take effect */ if ( ( rc = intelxlvf_reset_wait_teardown ( intelxl ) ) != 0 ) @@ -152,10 +125,35 @@ static int intelxlvf_reset_admin ( struct intelxl_nic *intelxl ) { err_active: err_teardown: intelxl_reopen_admin ( intelxl ); - err_command: return rc; } +/** + * Reset hardware via admin queue + * + * @v intelxl Intel device + * @ret rc Return status code + */ +static int intelxlvf_reset_admin ( struct intelxl_nic *intelxl ) { + struct intelxlvf_admin_descriptor *cmd; + int rc; + + /* Populate descriptor */ + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXLVF_ADMIN_SEND_TO_PF ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_RESET ); + + /* Issue command */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + /* Wait for reset to complete */ + if ( ( rc = intelxlvf_reset_wait ( intelxl ) ) != 0 ) + return rc; + + return 0; +} + /****************************************************************************** * * Admin queue @@ -190,13 +188,15 @@ static const struct intelxl_admin_offsets intelxlvf_admin_event_offsets = { 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; + struct intelxl_admin_descriptor *xlcmd; + struct intelxlvf_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 ); + xlcmd = &admin->desc[ admin->index % INTELXL_ADMIN_NUM_DESC ]; + cmd = container_of ( xlcmd, struct intelxlvf_admin_descriptor, xl ); + cmd->opcode = cpu_to_le16 ( INTELXLVF_ADMIN_SEND_TO_PF ); /* Record opcode */ intelxl->vopcode = le32_to_cpu ( cmd->vopcode ); @@ -218,7 +218,7 @@ static int intelxlvf_admin_command ( struct net_device *netdev ) { } /* Check for errors */ - if ( intelxl->vret != 0 ) + if ( cmd->vret != 0 ) return -EIO; return 0; @@ -239,7 +239,7 @@ static int intelxlvf_admin_command ( struct net_device *netdev ) { * @v link Link status */ static void intelxlvf_admin_link ( struct net_device *netdev, - struct intelxl_admin_vf_status_link *link ) { + struct intelxlvf_admin_status_link *link ) { struct intelxl_nic *intelxl = netdev->priv; DBGC ( intelxl, "INTELXL %p link %#02x speed %#02x\n", intelxl, @@ -261,12 +261,12 @@ static void intelxlvf_admin_link ( struct net_device *netdev, */ static void intelxlvf_admin_status ( struct net_device *netdev, - struct intelxl_admin_vf_status_buffer *stat ) { + struct intelxlvf_admin_status_buffer *stat ) { struct intelxl_nic *intelxl = netdev->priv; /* Handle event */ switch ( stat->event ) { - case cpu_to_le32 ( INTELXL_ADMIN_VF_STATUS_LINK ): + case cpu_to_le32 ( INTELXLVF_ADMIN_STATUS_LINK ): intelxlvf_admin_link ( netdev, &stat->data.link ); break; default: @@ -278,26 +278,42 @@ intelxlvf_admin_status ( struct net_device *netdev, } /** - * Handle virtual function event + * Handle admin event * * @v netdev Network device - * @v evt Admin queue event descriptor - * @v buf Admin queue event data buffer + * @v xlevt Admin queue event descriptor + * @v xlbuf Admin queue event data buffer */ -void intelxlvf_admin_event ( struct net_device *netdev, - struct intelxl_admin_descriptor *evt, - union intelxl_admin_buffer *buf ) { +static void intelxlvf_admin_event ( struct net_device *netdev, + struct intelxl_admin_descriptor *xlevt, + union intelxl_admin_buffer *xlbuf ) { struct intelxl_nic *intelxl = netdev->priv; - unsigned int vopcode = le32_to_cpu ( evt->vopcode ); + struct intelxl_admin *admin = &intelxl->command; + struct intelxlvf_admin_descriptor *evt = + container_of ( xlevt, struct intelxlvf_admin_descriptor, xl ); + union intelxlvf_admin_buffer *buf = + container_of ( xlbuf, union intelxlvf_admin_buffer, xl ); + unsigned int vopcode; + unsigned int index; + + /* Ignore unrecognised events */ + if ( evt->opcode != cpu_to_le16 ( INTELXLVF_ADMIN_SEND_TO_VF ) ) { + DBGC ( intelxl, "INTELXL %p unrecognised event opcode " + "%#04x\n", intelxl, le16_to_cpu ( evt->opcode ) ); + return; + } /* Record command response if applicable */ + vopcode = le32_to_cpu ( evt->vopcode ); if ( vopcode == intelxl->vopcode ) { - memcpy ( &intelxl->vbuf, buf, sizeof ( intelxl->vbuf ) ); + index = ( ( admin->index - 1 ) % INTELXL_ADMIN_NUM_DESC ); + memcpy ( &admin->desc[index], evt, sizeof ( *evt ) ); + memcpy ( &admin->buf[index], buf, sizeof ( *buf ) ); intelxl->vopcode = 0; - intelxl->vret = le32_to_cpu ( evt->vret ); - if ( intelxl->vret != 0 ) { + if ( evt->vret != 0 ) { DBGC ( intelxl, "INTELXL %p admin VF command %#x " - "error %d\n", intelxl, vopcode, intelxl->vret ); + "error %d\n", intelxl, vopcode, + le32_to_cpu ( evt->vret ) ); DBGC_HDA ( intelxl, virt_to_phys ( evt ), evt, sizeof ( *evt ) ); DBGC_HDA ( intelxl, virt_to_phys ( buf ), buf, @@ -308,7 +324,7 @@ void intelxlvf_admin_event ( struct net_device *netdev, /* Handle unsolicited events */ switch ( vopcode ) { - case INTELXL_ADMIN_VF_STATUS: + case INTELXLVF_ADMIN_STATUS: intelxlvf_admin_status ( netdev, &buf->stat ); break; default: @@ -323,6 +339,45 @@ void intelxlvf_admin_event ( struct net_device *netdev, } /** + * Negotiate API version + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxlvf_admin_version ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_admin_buffer *buf; + unsigned int api; + int rc; + + /* Populate descriptor */ + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_VERSION ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->ver ) ); + buf = intelxlvf_admin_command_buffer ( intelxl ); + buf->ver.major = cpu_to_le32 ( INTELXLVF_ADMIN_API_MAJOR ); + buf->ver.minor = cpu_to_le32 ( INTELXLVF_ADMIN_API_MINOR ); + + /* Issue command */ + if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 ) + return rc; + api = le32_to_cpu ( buf->ver.major ); + DBGC ( intelxl, "INTELXL %p API v%d.%d\n", + intelxl, api, le32_to_cpu ( buf->ver.minor ) ); + + /* Check for API compatibility */ + if ( api > INTELXLVF_ADMIN_API_MAJOR ) { + DBGC ( intelxl, "INTELXL %p unsupported API v%d\n", + intelxl, api ); + return -ENOTSUP; + } + + return 0; +} + +/** * Get resources * * @v netdev Network device @@ -330,23 +385,116 @@ void intelxlvf_admin_event ( struct net_device *netdev, */ 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; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_admin_buffer *buf; int rc; /* Populate descriptor */ - cmd = intelxl_admin_command_descriptor ( intelxl ); - cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_GET_RESOURCES ); + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_GET_RESOURCES ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->caps ) ); + buf = intelxlvf_admin_command_buffer ( intelxl ); + buf->caps.caps = cpu_to_le32 ( INTELXLVF_ADMIN_CAP_L2 | + INTELXLVF_ADMIN_CAP_RQPS ); /* 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 ); + intelxl->caps = le32_to_cpu ( buf->res.caps ); + intelxl->vsi = le16_to_cpu ( buf->res.vsi ); + memcpy ( netdev->hw_addr, buf->res.mac, ETH_ALEN ); + DBGC ( intelxl, "INTELXL %p capabilities %#08x VSI %#04x\n", + intelxl, intelxl->caps, intelxl->vsi ); + + return 0; +} + +/** + * Get statistics (for debugging) + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxlvf_admin_stats ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_admin_buffer *buf; + struct intelxlvf_admin_stats *tx; + struct intelxlvf_admin_stats *rx; + int rc; + + /* Populate descriptor */ + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_GET_STATS ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->queues ) ); + buf = intelxlvf_admin_command_buffer ( intelxl ); + buf->queues.vsi = cpu_to_le16 ( intelxl->vsi ); + tx = &buf->stats.tx; + rx = &buf->stats.rx; + + /* Issue command */ + if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 ) + return rc; + DBGC ( intelxl, "INTELXL %p TX bytes %#llx discards %#llx errors " + "%#llx\n", intelxl, + ( ( unsigned long long ) le64_to_cpu ( tx->bytes ) ), + ( ( unsigned long long ) le64_to_cpu ( tx->discards ) ), + ( ( unsigned long long ) le64_to_cpu ( tx->errors ) ) ); + DBGC ( intelxl, "INTELXL %p TX unicasts %#llx multicasts %#llx " + "broadcasts %#llx\n", intelxl, + ( ( unsigned long long ) le64_to_cpu ( tx->unicasts ) ), + ( ( unsigned long long ) le64_to_cpu ( tx->multicasts ) ), + ( ( unsigned long long ) le64_to_cpu ( tx->broadcasts ) ) ); + DBGC ( intelxl, "INTELXL %p RX bytes %#llx discards %#llx errors " + "%#llx\n", intelxl, + ( ( unsigned long long ) le64_to_cpu ( rx->bytes ) ), + ( ( unsigned long long ) le64_to_cpu ( rx->discards ) ), + ( ( unsigned long long ) le64_to_cpu ( rx->errors ) ) ); + DBGC ( intelxl, "INTELXL %p RX unicasts %#llx multicasts %#llx " + "broadcasts %#llx\n", intelxl, + ( ( unsigned long long ) le64_to_cpu ( rx->unicasts ) ), + ( ( unsigned long long ) le64_to_cpu ( rx->multicasts ) ), + ( ( unsigned long long ) le64_to_cpu ( rx->broadcasts ) ) ); + + return 0; +} + +/** + * Configure number of queue pairs + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxlvf_admin_request_qps ( struct net_device *netdev ) { + struct intelxl_nic *intelxl = netdev->priv; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_admin_buffer *buf; + int rc; + + /* Populate descriptor */ + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->opcode = cpu_to_le16 ( INTELXLVF_ADMIN_SEND_TO_PF ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_REQUEST_QPS ); + cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF ); + cmd->len = cpu_to_le16 ( sizeof ( buf->rqps ) ); + buf = intelxlvf_admin_command_buffer ( intelxl ); + buf->rqps.count = cpu_to_le16 ( 1 ); + + /* Issue command (which will trigger a reset) */ + if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 ) + return rc; + + /* Wait for reset to complete */ + if ( ( rc = intelxlvf_reset_wait ( intelxl ) ) != 0 ) + return rc; + + /* Reestablish capabilities to reactivate VF after reset */ + if ( ( rc = intelxlvf_admin_get_resources ( netdev ) ) != 0 ) + return rc; return 0; } @@ -366,16 +514,16 @@ static int intelxlvf_admin_get_resources ( struct net_device *netdev ) { */ 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; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_admin_buffer *buf; int rc; /* Populate descriptor */ - cmd = intelxl_admin_command_descriptor ( intelxl ); - cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_CONFIGURE ); + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_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 = intelxlvf_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 ); @@ -404,18 +552,19 @@ static int intelxlvf_admin_configure ( struct net_device *netdev ) { */ 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; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_admin_buffer *buf; int rc; /* Populate descriptor */ - cmd = intelxl_admin_command_descriptor ( intelxl ); - cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_IRQ_MAP ); + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_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 = intelxlvf_admin_command_buffer ( intelxl ); buf->irq.count = cpu_to_le16 ( 1 ); buf->irq.vsi = cpu_to_le16 ( intelxl->vsi ); + buf->irq.vec = cpu_to_le16 ( INTELXLVF_MSIX_VECTOR ); buf->irq.rxmap = cpu_to_le16 ( 0x0001 ); buf->irq.txmap = cpu_to_le16 ( 0x0001 ); @@ -435,17 +584,17 @@ static int intelxlvf_admin_irq_map ( struct net_device *netdev ) { */ 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; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_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 = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->vopcode = ( enable ? cpu_to_le32 ( INTELXLVF_ADMIN_ENABLE ) : + cpu_to_le32 ( INTELXLVF_ADMIN_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 = intelxlvf_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 ); @@ -465,16 +614,16 @@ static int intelxlvf_admin_queues ( struct net_device *netdev, int enable ) { */ 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; + struct intelxlvf_admin_descriptor *cmd; + union intelxlvf_admin_buffer *buf; int rc; /* Populate descriptor */ - cmd = intelxl_admin_command_descriptor ( intelxl ); - cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_PROMISC ); + cmd = intelxlvf_admin_command_descriptor ( intelxl ); + cmd->vopcode = cpu_to_le32 ( INTELXLVF_ADMIN_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 = intelxlvf_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 ); @@ -524,10 +673,14 @@ static int intelxlvf_open ( struct net_device *netdev ) { if ( ( rc = intelxlvf_admin_promisc ( netdev ) ) != 0 ) goto err_promisc; + /* Reset statistics counters (if debugging) */ + if ( DBG_LOG ) + intelxlvf_admin_stats ( netdev ); + return 0; err_promisc: - intelxlvf_admin_queues ( netdev, INTELXL_ADMIN_VF_DISABLE ); + intelxlvf_admin_queues ( netdev, 0 ); err_enable: err_irq_map: err_configure: @@ -547,6 +700,10 @@ static void intelxlvf_close ( struct net_device *netdev ) { struct intelxl_nic *intelxl = netdev->priv; int rc; + /* Show statistics (if debugging) */ + if ( DBG_LOG ) + intelxlvf_admin_stats ( netdev ); + /* Disable queues */ if ( ( rc = intelxlvf_admin_queues ( netdev, 0 ) ) != 0 ) { /* Leak memory; there's nothing else we can do */ @@ -600,7 +757,8 @@ static int intelxlvf_probe ( struct pci_device *pci ) { pci_set_drvdata ( pci, netdev ); netdev->dev = &pci->dev; memset ( intelxl, 0, sizeof ( *intelxl ) ); - intelxl->intr = INTELXLVF_VFINT_DYN_CTL0; + intelxl->intr = INTELXLVF_VFINT_DYN_CTLN ( INTELXLVF_MSIX_VECTOR ); + intelxl->handle = intelxlvf_admin_event; intelxl_init_admin ( &intelxl->command, INTELXLVF_ADMIN, &intelxlvf_admin_command_offsets ); intelxl_init_admin ( &intelxl->event, INTELXLVF_ADMIN, @@ -637,10 +795,11 @@ static int intelxlvf_probe ( struct pci_device *pci ) { } /* Reset the function via PCIe FLR */ - intelxlvf_reset_flr ( intelxl, pci ); + pci_reset ( pci, intelxl->exp ); /* Enable MSI-X dummy interrupt */ - if ( ( rc = intelxl_msix_enable ( intelxl, pci ) ) != 0 ) + if ( ( rc = intelxl_msix_enable ( intelxl, pci, + INTELXLVF_MSIX_VECTOR ) ) != 0 ) goto err_msix; /* Open admin queues */ @@ -651,10 +810,19 @@ static int intelxlvf_probe ( struct pci_device *pci ) { if ( ( rc = intelxlvf_reset_admin ( intelxl ) ) != 0 ) goto err_reset_admin; + /* Negotiate API version */ + if ( ( rc = intelxlvf_admin_version ( netdev ) ) != 0 ) + goto err_version; + /* Get MAC address */ if ( ( rc = intelxlvf_admin_get_resources ( netdev ) ) != 0 ) goto err_get_resources; + /* Configure number of queue pairs, if applicable */ + if ( ( intelxl->caps & INTELXLVF_ADMIN_CAP_RQPS ) && + ( ( rc = intelxlvf_admin_request_qps ( netdev ) ) != 0 ) ) + goto err_rqps; + /* Register network device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) goto err_register_netdev; @@ -663,13 +831,15 @@ static int intelxlvf_probe ( struct pci_device *pci ) { unregister_netdev ( netdev ); err_register_netdev: + err_rqps: err_get_resources: + err_version: err_reset_admin: intelxl_close_admin ( intelxl ); err_open_admin: - intelxl_msix_disable ( intelxl, pci ); + intelxl_msix_disable ( intelxl, pci, INTELXLVF_MSIX_VECTOR ); err_msix: - intelxlvf_reset_flr ( intelxl, pci ); + pci_reset ( pci, intelxl->exp ); err_exp: iounmap ( intelxl->regs ); err_ioremap: @@ -698,10 +868,10 @@ static void intelxlvf_remove ( struct pci_device *pci ) { intelxl_close_admin ( intelxl ); /* Disable MSI-X dummy interrupt */ - intelxl_msix_disable ( intelxl, pci ); + intelxl_msix_disable ( intelxl, pci, INTELXLVF_MSIX_VECTOR ); /* Reset the function via PCIe FLR */ - intelxlvf_reset_flr ( intelxl, pci ); + pci_reset ( pci, intelxl->exp ); /* Free network device */ iounmap ( intelxl->regs ); @@ -713,7 +883,7 @@ static void intelxlvf_remove ( struct pci_device *pci ) { 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, 0x1889, "iavf", "Intel adaptive VF", 0 ), PCI_ROM ( 0x8086, 0x37cd, "x722-vf", "X722 VF", 0 ), PCI_ROM ( 0x8086, 0x37d9, "x722-vf-hv", "X722 VF (Hyper-V)", 0 ), }; diff --git a/src/drivers/net/intelxlvf.h b/src/drivers/net/intelxlvf.h index ffcae5674..95ddf9474 100644 --- a/src/drivers/net/intelxlvf.h +++ b/src/drivers/net/intelxlvf.h @@ -14,12 +14,23 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** BAR size */ #define INTELXLVF_BAR_SIZE 0x10000 +/** MSI-X vector + * + * The 100 Gigabit physical function driver requires a virtual + * function driver to request that transmit and receive queues are + * mapped to MSI-X vector 1 or higher. + */ +#define INTELXLVF_MSIX_VECTOR 1 + /** Transmit Queue Tail Register */ #define INTELXLVF_QTX_TAIL 0x00000 /** Receive Queue Tail Register */ #define INTELXLVF_QRX_TAIL 0x02000 +/** VF Interrupt N Dynamic Control Register */ +#define INTELXLVF_VFINT_DYN_CTLN( x ) ( 0x3800 + ( 0x4 * ( (x) - 1 ) ) ) + /** VF Interrupt Zero Dynamic Control Register */ #define INTELXLVF_VFINT_DYN_CTL0 0x5c00 @@ -59,11 +70,335 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Maximum time to wait for a VF admin request to complete */ #define INTELXLVF_ADMIN_MAX_WAIT_MS 2000 +/** Admin queue Send Message to PF command */ +#define INTELXLVF_ADMIN_SEND_TO_PF 0x0801 + +/** Admin queue Send Message to VF command */ +#define INTELXLVF_ADMIN_SEND_TO_VF 0x0802 + +/** Admin Queue VF Version opcode */ +#define INTELXLVF_ADMIN_VERSION 0x00000001 + +/** Admin Queue VF Version data buffer */ +struct intelxlvf_admin_version_buffer { + /** Major version */ + uint32_t major; + /** Minor version */ + uint32_t minor; +} __attribute__ (( packed )); + +/** Admin queue VF API major version */ +#define INTELXLVF_ADMIN_API_MAJOR 1 + +/** Admin queue VF API minor version */ +#define INTELXLVF_ADMIN_API_MINOR 1 + +/** Admin Queue VF Reset opcode */ +#define INTELXLVF_ADMIN_RESET 0x00000002 + +/** Admin Queue VF Get Resources opcode */ +#define INTELXLVF_ADMIN_GET_RESOURCES 0x00000003 + +/** Admin Queue VF Capabilities data buffer */ +struct intelxlvf_admin_capabilities_buffer { + /** Capabilities */ + uint32_t caps; +} __attribute__ (( packed )); + +/** Admin Queue VF Get Resources data buffer */ +struct intelxlvf_admin_get_resources_buffer { + /** Number of VSIs */ + uint16_t vsis; + /** Number of queue pairs */ + uint16_t qps; + /** Number of MSI-X vectors */ + uint16_t vectors; + /** Maximum MTU */ + uint16_t mtu; + /** Capabilities */ + uint32_t caps; + /** Reserved */ + uint8_t reserved_a[8]; + /** VSI switching element ID */ + uint16_t vsi; + /** Reserved */ + uint8_t reserved_b[8]; + /** MAC address */ + uint8_t mac[ETH_ALEN]; +} __attribute__ (( packed )); + +/** Layer 2 capabilities (add/remove MAC, configure promiscuous mode) */ +#define INTELXLVF_ADMIN_CAP_L2 0x00000001 + +/** Request Queues capabilities */ +#define INTELXLVF_ADMIN_CAP_RQPS 0x00000040 + +/** Admin Queue VF Status Change Event opcode */ +#define INTELXLVF_ADMIN_STATUS 0x00000011 + +/** Link status change event type */ +#define INTELXLVF_ADMIN_STATUS_LINK 0x00000001 + +/** Link status change event data */ +struct intelxlvf_admin_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 intelxlvf_admin_status_buffer { + /** Event type */ + uint32_t event; + /** Event data */ + union { + /** Link change event data */ + struct intelxlvf_admin_status_link link; + } data; + /** Reserved */ + uint8_t reserved[4]; +} __attribute__ (( packed )); + +/** Admin Queue VF Configure Queues opcode */ +#define INTELXLVF_ADMIN_CONFIGURE 0x00000006 + +/** Admin Queue VF Configure Queues data buffer */ +struct intelxlvf_admin_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 INTELXLVF_ADMIN_IRQ_MAP 0x00000007 + +/** Admin Queue VF IRQ Map data buffer */ +struct intelxlvf_admin_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 INTELXLVF_ADMIN_ENABLE 0x00000008 + +/** Admin Queue VF Disable Queues opcode */ +#define INTELXLVF_ADMIN_DISABLE 0x00000009 + +/** Admin Queue VF Enable/Disable Queues data buffer */ +struct intelxlvf_admin_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 INTELXLVF_ADMIN_PROMISC 0x0000000e + +/** Admin Queue VF Configure Promiscuous Mode data buffer */ +struct intelxlvf_admin_promisc_buffer { + /** VSI switching element ID */ + uint16_t vsi; + /** Flags */ + uint16_t flags; +} __attribute__ (( packed )); + +/** Admin Queue VF Get Statistics opcode */ +#define INTELXLVF_ADMIN_GET_STATS 0x0000000f + +/** VF statistics */ +struct intelxlvf_admin_stats { + /** Bytes */ + uint64_t bytes; + /** Unicast packets */ + uint64_t unicasts; + /** Multicast packets */ + uint64_t multicasts; + /** Broadcast packets */ + uint64_t broadcasts; + /** Discarded packets */ + uint64_t discards; + /** Errors */ + uint64_t errors; +} __attribute__ (( packed )); + +/** Admin Queue VF Get Statistics data buffer */ +struct intelxlvf_admin_stats_buffer { + /** Receive statistics */ + struct intelxlvf_admin_stats rx; + /** Transmit statistics */ + struct intelxlvf_admin_stats tx; +} __attribute__ (( packed )); + +/** Admin Queue VF Request Queues opcode */ +#define INTELXLVF_ADMIN_REQUEST_QPS 0x0000001d + +/** Admin Queue VF Request Queues data buffer */ +struct intelxlvf_admin_request_qps_buffer { + /** Number of queue pairs */ + uint16_t count; +} __attribute__ (( packed )); + +/** Admin queue data buffer */ +union intelxlvf_admin_buffer { + /** Original 40 Gigabit Ethernet data buffer */ + union intelxl_admin_buffer xl; + /** VF Version data buffer */ + struct intelxlvf_admin_version_buffer ver; + /** VF Capabilities data buffer */ + struct intelxlvf_admin_capabilities_buffer caps; + /** VF Get Resources data buffer */ + struct intelxlvf_admin_get_resources_buffer res; + /** VF Status Change Event data buffer */ + struct intelxlvf_admin_status_buffer stat; + /** VF Configure Queues data buffer */ + struct intelxlvf_admin_configure_buffer cfg; + /** VF Enable/Disable Queues data buffer */ + struct intelxlvf_admin_queues_buffer queues; + /** VF Configure Promiscuous Mode data buffer */ + struct intelxlvf_admin_promisc_buffer promisc; + /** VF IRQ Map data buffer */ + struct intelxlvf_admin_irq_map_buffer irq; + /** VF Get Statistics data buffer */ + struct intelxlvf_admin_stats_buffer stats; + /** VF Request Queues data buffer */ + struct intelxlvf_admin_request_qps_buffer rqps; +} __attribute__ (( packed )); + +/** Admin queue descriptor */ +struct intelxlvf_admin_descriptor { + /** Transparent union */ + union { + /** Original 40 Gigabit Ethernet descriptor */ + struct intelxl_admin_descriptor xl; + /** Transparent struct */ + struct { + /** Flags */ + uint16_t flags; + /** Opcode */ + uint16_t opcode; + /** Data length */ + uint16_t len; + /** Return value */ + uint16_t ret; + /** VF opcode */ + uint32_t vopcode; + /** VF return value */ + int32_t vret; + /** Parameters */ + union intelxl_admin_params params; + } __attribute__ (( packed )); + } __attribute__ (( packed )); +} __attribute__ (( packed )); + +/** + * Get next admin command queue descriptor + * + * @v intelxl Intel device + * @ret cmd Command descriptor + */ +struct intelxlvf_admin_descriptor * +intelxlvf_admin_command_descriptor ( struct intelxl_nic *intelxl ) { + struct intelxl_admin_descriptor *xlcmd = + intelxl_admin_command_descriptor ( intelxl ); + + return container_of ( xlcmd, struct intelxlvf_admin_descriptor, xl ); +} + +/** + * Get next admin command queue data buffer + * + * @v intelxl Intel device + * @ret buf Data buffer + */ +static inline __attribute__ (( always_inline )) union intelxlvf_admin_buffer * +intelxlvf_admin_command_buffer ( struct intelxl_nic *intelxl ) { + union intelxl_admin_buffer *xlbuf = + intelxl_admin_command_buffer ( intelxl ); + + return container_of ( xlbuf, union intelxlvf_admin_buffer, xl ); +} + /** 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 +/** Minimum time to wait for reset to complete */ +#define INTELXLVF_RESET_DELAY_MS 100 + /** Maximum time to wait for reset to complete */ #define INTELXLVF_RESET_MAX_WAIT_MS 1000 diff --git a/src/drivers/net/ncm.c b/src/drivers/net/ncm.c index 1e8088d76..2c0f91e21 100644 --- a/src/drivers/net/ncm.c +++ b/src/drivers/net/ncm.c @@ -598,7 +598,7 @@ static int ncm_probe ( struct usb_function *func, } /* Fetch MAC address */ - if ( ( rc = ecm_fetch_mac ( func, ethernet, netdev->hw_addr ) ) != 0 ) { + if ( ( rc = ecm_fetch_mac ( func, ethernet, netdev ) ) != 0 ) { DBGC ( ncm, "NCM %p could not fetch MAC address: %s\n", ncm, strerror ( rc ) ); goto err_fetch_mac; |
