aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Brown <mcb30@ipxe.org>2022-08-08 15:02:44 +0100
committerMichael Brown <mcb30@ipxe.org>2022-08-12 16:15:17 +0100
commitcad1cc6b449b63415ffdad8e12f13df4256106fb (patch)
tree3eee6a3efda0cb709e632f209073cffb19cc2192
parent6871a7de705b6f6a4046f0d19da9bcd689c3bc8e (diff)
downloadipxe-cad1cc6b449b63415ffdad8e12f13df4256106fb.zip
ipxe-cad1cc6b449b63415ffdad8e12f13df4256106fb.tar.gz
ipxe-cad1cc6b449b63415ffdad8e12f13df4256106fb.tar.bz2
[intelxl] Add driver for Intel 100 Gigabit Ethernet NICs
Add a driver for the E810 family of 100 Gigabit Ethernet NICs. The core datapath is identical to that of the 40 Gigabit XL710, and this part of the code is shared between both drivers. The admin queue mechanism is sufficiently similar to make it worth reusing substantial portions of the code, with separate implementations for several commands to handle the (unnecessarily) breaking changes in data structure layouts. The major differences are in the mechanisms for programming queue contexts (where the E810 abandons TX/RX symmetry) and for configuring the transmit scheduler and receive filters: these portions are sufficiently different to justify a separate driver. Signed-off-by: Michael Brown <mcb30@ipxe.org>
-rw-r--r--src/drivers/net/ice.c986
-rw-r--r--src/drivers/net/ice.h565
-rw-r--r--src/drivers/net/intelxl.c14
-rw-r--r--src/drivers/net/intelxl.h10
-rw-r--r--src/include/ipxe/errfile.h1
5 files changed, 1569 insertions, 7 deletions
diff --git a/src/drivers/net/ice.c b/src/drivers/net/ice.c
new file mode 100644
index 0000000..b5d66f1
--- /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 0000000..26291a7
--- /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 c8c527d..82b0783 100644
--- a/src/drivers/net/intelxl.c
+++ b/src/drivers/net/intelxl.c
@@ -123,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,
@@ -575,7 +575,7 @@ static int intelxl_admin_mac_write ( struct net_device *netdev ) {
* @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;
int rc;
@@ -726,7 +726,7 @@ static int intelxl_admin_promisc ( struct intelxl_nic *intelxl ) {
* @v intelxl Intel device
* @ret rc Return status code
*/
-static int intelxl_admin_mac_config ( struct intelxl_nic *intelxl ) {
+int intelxl_admin_mac_config ( struct intelxl_nic *intelxl ) {
struct intelxl_admin_descriptor *cmd;
struct intelxl_admin_mac_config_params *config;
int rc;
@@ -1280,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;
@@ -1314,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 */
diff --git a/src/drivers/net/intelxl.h b/src/drivers/net/intelxl.h
index ad15ca9..0db16b5 100644
--- a/src/drivers/net/intelxl.h
+++ b/src/drivers/net/intelxl.h
@@ -925,6 +925,8 @@ struct intelxl_nic {
unsigned int vsi;
/** Queue set handle */
unsigned int qset;
+ /** Transmit element ID */
+ uint32_t teid;
/** Interrupt control register */
unsigned int intr;
/** PCI Express capability offset */
@@ -959,6 +961,8 @@ struct intelxl_nic {
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,
unsigned int vector );
@@ -970,6 +974,8 @@ 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 );
@@ -978,6 +984,10 @@ 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 );
diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h
index 81f5557..359e4d2 100644
--- a/src/include/ipxe/errfile.h
+++ b/src/include/ipxe/errfile.h
@@ -215,6 +215,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_iphone ( ERRFILE_DRIVER | 0x00cf0000 )
#define ERRFILE_slirp ( ERRFILE_DRIVER | 0x00d00000 )
#define ERRFILE_rdc ( ERRFILE_DRIVER | 0x00d10000 )
+#define ERRFILE_ice ( ERRFILE_DRIVER | 0x00d20000 )
#define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 )
#define ERRFILE_arp ( ERRFILE_NET | 0x00010000 )