mirror of https://github.com/ipxe/ipxe.git
[intelxl] Add driver for Intel 40 Gigabit Ethernet NIC virtual functions
Signed-off-by: Michael Brown <mcb30@ipxe.org>pull/92/head
parent
92b46b7858
commit
a95966955c
|
@ -20,9 +20,9 @@ struct intelxl_nic;
|
|||
|
||||
/** Alignment
|
||||
*
|
||||
* No data structure requires greater than 128 byte alignment.
|
||||
* No data structure requires greater than 256 byte alignment.
|
||||
*/
|
||||
#define INTELXL_ALIGN 128
|
||||
#define INTELXL_ALIGN 256
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
|
@ -310,6 +310,166 @@ struct intelxl_admin_link_params {
|
|||
/** 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 */
|
||||
|
@ -342,6 +502,18 @@ union intelxl_admin_buffer {
|
|||
struct intelxl_admin_switch_buffer sw;
|
||||
/** Get VSI Parameters data buffer */
|
||||
struct intelxl_admin_vsi_buffer vsi;
|
||||
/** VF Get Resources data buffer */
|
||||
struct intelxl_admin_vf_get_resources_buffer res;
|
||||
/** VF Status Change Event data buffer */
|
||||
struct intelxl_admin_vf_status_buffer stat;
|
||||
/** VF Configure Queues data buffer */
|
||||
struct intelxl_admin_vf_configure_buffer cfg;
|
||||
/** VF Enable/Disable Queues data buffer */
|
||||
struct intelxl_admin_vf_queues_buffer queues;
|
||||
/** VF Configure Promiscuous Mode data buffer */
|
||||
struct intelxl_admin_vf_promisc_buffer promisc;
|
||||
/*** VF IRQ Map data buffer */
|
||||
struct intelxl_admin_vf_irq_map_buffer irq;
|
||||
/** Alignment padding */
|
||||
uint8_t pad[INTELXL_ALIGN];
|
||||
} __attribute__ (( packed ));
|
||||
|
@ -867,12 +1039,21 @@ struct intelxl_nic {
|
|||
struct pci_msix msix;
|
||||
/** MSI-X dummy interrupt target */
|
||||
uint32_t msg;
|
||||
/** PCI Express capability offset */
|
||||
unsigned int exp;
|
||||
|
||||
/** Admin command queue */
|
||||
struct intelxl_admin command;
|
||||
/** Admin event queue */
|
||||
struct intelxl_admin event;
|
||||
|
||||
/** Current VF opcode */
|
||||
unsigned int vopcode;
|
||||
/** Current VF return value */
|
||||
int vret;
|
||||
/** Current VF event data buffer */
|
||||
union intelxl_admin_buffer vbuf;
|
||||
|
||||
/** Transmit descriptor ring */
|
||||
struct intelxl_ring tx;
|
||||
/** Receive descriptor ring */
|
||||
|
|
|
@ -0,0 +1,719 @@
|
|||
/*
|
||||
* Copyright (C) 2019 Michael Brown <mbrown@fensystems.co.uk>.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
||||
* 02110-1301, USA.
|
||||
*
|
||||
* You can also choose to distribute this program under the terms of
|
||||
* the Unmodified Binary Distribution Licence (as given in the file
|
||||
* COPYING.UBDL), provided that you have satisfied its requirements.
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <byteswap.h>
|
||||
#include <ipxe/pci.h>
|
||||
#include <ipxe/netdevice.h>
|
||||
#include <ipxe/ethernet.h>
|
||||
#include "intelxlvf.h"
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Intel 40 Gigabit Ethernet virtual function network card driver
|
||||
*
|
||||
*/
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Device reset
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* Reset hardware via PCIe function-level reset
|
||||
*
|
||||
* @v intelxl Intel device
|
||||
*/
|
||||
static void intelxlvf_reset_flr ( struct intelxl_nic *intelxl,
|
||||
struct pci_device *pci ) {
|
||||
uint16_t control;
|
||||
|
||||
/* Perform a PCIe function-level reset */
|
||||
pci_read_config_word ( pci, ( intelxl->exp + PCI_EXP_DEVCTL ),
|
||||
&control );
|
||||
pci_write_config_word ( pci, ( intelxl->exp + PCI_EXP_DEVCTL ),
|
||||
( control | PCI_EXP_DEVCTL_FLR ) );
|
||||
mdelay ( INTELXL_RESET_DELAY_MS );
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for admin event queue to be torn down
|
||||
*
|
||||
* @v intelxl Intel device
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int intelxlvf_reset_wait_teardown ( struct intelxl_nic *intelxl ) {
|
||||
uint32_t admin_evt_len;
|
||||
unsigned int i;
|
||||
|
||||
/* Wait for admin event queue to be torn down */
|
||||
for ( i = 0 ; i < INTELXLVF_RESET_MAX_WAIT_MS ; i++ ) {
|
||||
|
||||
/* Check admin event queue length register */
|
||||
admin_evt_len = readl ( intelxl->regs + INTELXLVF_ADMIN +
|
||||
INTELXLVF_ADMIN_EVT_LEN );
|
||||
if ( ! ( admin_evt_len & INTELXL_ADMIN_LEN_ENABLE ) )
|
||||
return 0;
|
||||
|
||||
/* Delay */
|
||||
mdelay ( 1 );
|
||||
}
|
||||
|
||||
DBGC ( intelxl, "INTELXL %p timed out waiting for teardown (%#08x)\n",
|
||||
intelxl, admin_evt_len );
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wait for virtual function to be marked as active
|
||||
*
|
||||
* @v intelxl Intel device
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int intelxlvf_reset_wait_active ( struct intelxl_nic *intelxl ) {
|
||||
uint32_t vfgen_rstat;
|
||||
unsigned int vfr_state;
|
||||
unsigned int i;
|
||||
|
||||
/* Wait for virtual function to be marked as active */
|
||||
for ( i = 0 ; i < INTELXLVF_RESET_MAX_WAIT_MS ; i++ ) {
|
||||
|
||||
/* Check status as written by physical function driver */
|
||||
vfgen_rstat = readl ( intelxl->regs + INTELXLVF_VFGEN_RSTAT );
|
||||
vfr_state = INTELXLVF_VFGEN_RSTAT_VFR_STATE ( vfgen_rstat );
|
||||
if ( vfr_state == INTELXLVF_VFGEN_RSTAT_VFR_STATE_ACTIVE )
|
||||
return 0;
|
||||
|
||||
/* Delay */
|
||||
mdelay ( 1 );
|
||||
}
|
||||
|
||||
DBGC ( intelxl, "INTELXL %p timed out waiting for activation "
|
||||
"(%#08x)\n", intelxl, vfgen_rstat );
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset hardware via admin queue
|
||||
*
|
||||
* @v intelxl Intel device
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int intelxlvf_reset_admin ( struct intelxl_nic *intelxl ) {
|
||||
struct intelxl_admin_descriptor *cmd;
|
||||
int rc;
|
||||
|
||||
/* Populate descriptor */
|
||||
cmd = intelxl_admin_command_descriptor ( intelxl );
|
||||
cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_SEND_TO_PF );
|
||||
cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_RESET );
|
||||
|
||||
/* Issue command */
|
||||
if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
|
||||
goto err_command;
|
||||
|
||||
/* Wait for minimum reset time */
|
||||
mdelay ( INTELXL_RESET_DELAY_MS );
|
||||
|
||||
/* Wait for reset to take effect */
|
||||
if ( ( rc = intelxlvf_reset_wait_teardown ( intelxl ) ) != 0 )
|
||||
goto err_teardown;
|
||||
|
||||
/* Wait for virtual function to become active */
|
||||
if ( ( rc = intelxlvf_reset_wait_active ( intelxl ) ) != 0 )
|
||||
goto err_active;
|
||||
|
||||
err_active:
|
||||
err_teardown:
|
||||
intelxl_reopen_admin ( intelxl );
|
||||
err_command:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Admin queue
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/** Admin command queue register offsets */
|
||||
static const struct intelxl_admin_offsets intelxlvf_admin_command_offsets = {
|
||||
.bal = INTELXLVF_ADMIN_CMD_BAL,
|
||||
.bah = INTELXLVF_ADMIN_CMD_BAH,
|
||||
.len = INTELXLVF_ADMIN_CMD_LEN,
|
||||
.head = INTELXLVF_ADMIN_CMD_HEAD,
|
||||
.tail = INTELXLVF_ADMIN_CMD_TAIL,
|
||||
};
|
||||
|
||||
/** Admin event queue register offsets */
|
||||
static const struct intelxl_admin_offsets intelxlvf_admin_event_offsets = {
|
||||
.bal = INTELXLVF_ADMIN_EVT_BAL,
|
||||
.bah = INTELXLVF_ADMIN_EVT_BAH,
|
||||
.len = INTELXLVF_ADMIN_EVT_LEN,
|
||||
.head = INTELXLVF_ADMIN_EVT_HEAD,
|
||||
.tail = INTELXLVF_ADMIN_EVT_TAIL,
|
||||
};
|
||||
|
||||
/**
|
||||
* Issue admin queue virtual function command
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int intelxlvf_admin_command ( struct net_device *netdev ) {
|
||||
struct intelxl_nic *intelxl = netdev->priv;
|
||||
struct intelxl_admin *admin = &intelxl->command;
|
||||
struct intelxl_admin_descriptor *cmd;
|
||||
unsigned int i;
|
||||
int rc;
|
||||
|
||||
/* Populate descriptor */
|
||||
cmd = &admin->desc[ admin->index % INTELXL_ADMIN_NUM_DESC ];
|
||||
cmd->opcode = cpu_to_le16 ( INTELXL_ADMIN_SEND_TO_PF );
|
||||
|
||||
/* Record opcode */
|
||||
intelxl->vopcode = le32_to_cpu ( cmd->vopcode );
|
||||
|
||||
/* Issue command */
|
||||
if ( ( rc = intelxl_admin_command ( intelxl ) ) != 0 )
|
||||
goto err_command;
|
||||
|
||||
/* Wait for response */
|
||||
for ( i = 0 ; i < INTELXLVF_ADMIN_MAX_WAIT_MS ; i++ ) {
|
||||
|
||||
/* Poll admin event queue */
|
||||
intelxl_poll_admin ( netdev );
|
||||
|
||||
/* If response has not arrived, delay 1ms and retry */
|
||||
if ( intelxl->vopcode ) {
|
||||
mdelay ( 1 );
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Check for errors */
|
||||
if ( intelxl->vret != 0 )
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = -ETIMEDOUT;
|
||||
DBGC ( intelxl, "INTELXL %p timed out waiting for admin VF command "
|
||||
"%#x\n", intelxl, intelxl->vopcode );
|
||||
err_command:
|
||||
intelxl->vopcode = 0;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle link status event
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @v link Link status
|
||||
*/
|
||||
static void intelxlvf_admin_link ( struct net_device *netdev,
|
||||
struct intelxl_admin_vf_status_link *link ) {
|
||||
struct intelxl_nic *intelxl = netdev->priv;
|
||||
|
||||
DBGC ( intelxl, "INTELXL %p link %#02x speed %#02x\n", intelxl,
|
||||
link->status, link->speed );
|
||||
|
||||
/* Update network device */
|
||||
if ( link->status ) {
|
||||
netdev_link_up ( netdev );
|
||||
} else {
|
||||
netdev_link_down ( netdev );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle status change event
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @v stat Status change event
|
||||
*/
|
||||
static void
|
||||
intelxlvf_admin_status ( struct net_device *netdev,
|
||||
struct intelxl_admin_vf_status_buffer *stat ) {
|
||||
struct intelxl_nic *intelxl = netdev->priv;
|
||||
|
||||
/* Handle event */
|
||||
switch ( stat->event ) {
|
||||
case cpu_to_le32 ( INTELXL_ADMIN_VF_STATUS_LINK ):
|
||||
intelxlvf_admin_link ( netdev, &stat->data.link );
|
||||
break;
|
||||
default:
|
||||
DBGC ( intelxl, "INTELXL %p unrecognised status change "
|
||||
"event %#x:\n", intelxl, le32_to_cpu ( stat->event ) );
|
||||
DBGC_HDA ( intelxl, 0, stat, sizeof ( *stat ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle virtual function event
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @v evt Admin queue event descriptor
|
||||
* @v buf Admin queue event data buffer
|
||||
*/
|
||||
void intelxlvf_admin_event ( struct net_device *netdev,
|
||||
struct intelxl_admin_descriptor *evt,
|
||||
union intelxl_admin_buffer *buf ) {
|
||||
struct intelxl_nic *intelxl = netdev->priv;
|
||||
unsigned int vopcode = le32_to_cpu ( evt->vopcode );
|
||||
|
||||
/* Record command response if applicable */
|
||||
if ( vopcode == intelxl->vopcode ) {
|
||||
memcpy ( &intelxl->vbuf, buf, sizeof ( intelxl->vbuf ) );
|
||||
intelxl->vopcode = 0;
|
||||
intelxl->vret = le32_to_cpu ( evt->vret );
|
||||
if ( intelxl->vret != 0 ) {
|
||||
DBGC ( intelxl, "INTELXL %p admin VF command %#x "
|
||||
"error %d\n", intelxl, vopcode, intelxl->vret );
|
||||
DBGC_HDA ( intelxl, virt_to_bus ( evt ), evt,
|
||||
sizeof ( *evt ) );
|
||||
DBGC_HDA ( intelxl, virt_to_bus ( buf ), buf,
|
||||
le16_to_cpu ( evt->len ) );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/* Handle unsolicited events */
|
||||
switch ( vopcode ) {
|
||||
case INTELXL_ADMIN_VF_STATUS:
|
||||
intelxlvf_admin_status ( netdev, &buf->stat );
|
||||
break;
|
||||
default:
|
||||
DBGC ( intelxl, "INTELXL %p unrecognised VF event %#x:\n",
|
||||
intelxl, vopcode );
|
||||
DBGC_HDA ( intelxl, 0, evt, sizeof ( *evt ) );
|
||||
DBGC_HDA ( intelxl, 0, buf, le16_to_cpu ( evt->len ) );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get resources
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int intelxlvf_admin_get_resources ( struct net_device *netdev ) {
|
||||
struct intelxl_nic *intelxl = netdev->priv;
|
||||
struct intelxl_admin_descriptor *cmd;
|
||||
struct intelxl_admin_vf_get_resources_buffer *res;
|
||||
int rc;
|
||||
|
||||
/* Populate descriptor */
|
||||
cmd = intelxl_admin_command_descriptor ( intelxl );
|
||||
cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_GET_RESOURCES );
|
||||
|
||||
/* Issue command */
|
||||
if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
/* Parse response */
|
||||
res = &intelxl->vbuf.res;
|
||||
intelxl->vsi = le16_to_cpu ( res->vsi );
|
||||
memcpy ( netdev->hw_addr, res->mac, ETH_ALEN );
|
||||
DBGC ( intelxl, "INTELXL %p VSI %#04x\n", intelxl, intelxl->vsi );
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* Network device interface
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* Configure queues
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int intelxlvf_admin_configure ( struct net_device *netdev ) {
|
||||
struct intelxl_nic *intelxl = netdev->priv;
|
||||
struct intelxl_admin_descriptor *cmd;
|
||||
union intelxl_admin_buffer *buf;
|
||||
int rc;
|
||||
|
||||
/* Populate descriptor */
|
||||
cmd = intelxl_admin_command_descriptor ( intelxl );
|
||||
cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_CONFIGURE );
|
||||
cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
|
||||
cmd->len = cpu_to_le16 ( sizeof ( buf->cfg ) );
|
||||
buf = intelxl_admin_command_buffer ( intelxl );
|
||||
buf->cfg.vsi = cpu_to_le16 ( intelxl->vsi );
|
||||
buf->cfg.count = cpu_to_le16 ( 1 );
|
||||
buf->cfg.tx.vsi = cpu_to_le16 ( intelxl->vsi );
|
||||
buf->cfg.tx.count = cpu_to_le16 ( INTELXL_TX_NUM_DESC );
|
||||
buf->cfg.tx.base = cpu_to_le64 ( virt_to_bus ( intelxl->tx.desc.raw ) );
|
||||
buf->cfg.rx.vsi = cpu_to_le16 ( intelxl->vsi );
|
||||
buf->cfg.rx.count = cpu_to_le32 ( INTELXL_RX_NUM_DESC );
|
||||
buf->cfg.rx.len = cpu_to_le32 ( intelxl->mfs );
|
||||
buf->cfg.rx.mfs = cpu_to_le32 ( intelxl->mfs );
|
||||
buf->cfg.rx.base = cpu_to_le64 ( virt_to_bus ( intelxl->rx.desc.raw ) );
|
||||
|
||||
/* Issue command */
|
||||
if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure IRQ mapping
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int intelxlvf_admin_irq_map ( struct net_device *netdev ) {
|
||||
struct intelxl_nic *intelxl = netdev->priv;
|
||||
struct intelxl_admin_descriptor *cmd;
|
||||
union intelxl_admin_buffer *buf;
|
||||
int rc;
|
||||
|
||||
/* Populate descriptor */
|
||||
cmd = intelxl_admin_command_descriptor ( intelxl );
|
||||
cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_IRQ_MAP );
|
||||
cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
|
||||
cmd->len = cpu_to_le16 ( sizeof ( buf->irq ) );
|
||||
buf = intelxl_admin_command_buffer ( intelxl );
|
||||
buf->irq.count = cpu_to_le16 ( 1 );
|
||||
buf->irq.vsi = cpu_to_le16 ( intelxl->vsi );
|
||||
buf->irq.rxmap = cpu_to_le16 ( 0x0001 );
|
||||
buf->irq.txmap = cpu_to_le16 ( 0x0001 );
|
||||
|
||||
/* Issue command */
|
||||
if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enable/disable queues
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @v enable Enable queues
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int intelxlvf_admin_queues ( struct net_device *netdev, int enable ) {
|
||||
struct intelxl_nic *intelxl = netdev->priv;
|
||||
struct intelxl_admin_descriptor *cmd;
|
||||
union intelxl_admin_buffer *buf;
|
||||
int rc;
|
||||
|
||||
/* Populate descriptor */
|
||||
cmd = intelxl_admin_command_descriptor ( intelxl );
|
||||
cmd->vopcode = ( enable ? cpu_to_le32 ( INTELXL_ADMIN_VF_ENABLE ) :
|
||||
cpu_to_le32 ( INTELXL_ADMIN_VF_DISABLE ) );
|
||||
cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
|
||||
cmd->len = cpu_to_le16 ( sizeof ( buf->queues ) );
|
||||
buf = intelxl_admin_command_buffer ( intelxl );
|
||||
buf->queues.vsi = cpu_to_le16 ( intelxl->vsi );
|
||||
buf->queues.rx = cpu_to_le32 ( 1 );
|
||||
buf->queues.tx = cpu_to_le32 ( 1 );
|
||||
|
||||
/* Issue command */
|
||||
if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure promiscuous mode
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int intelxlvf_admin_promisc ( struct net_device *netdev ) {
|
||||
struct intelxl_nic *intelxl = netdev->priv;
|
||||
struct intelxl_admin_descriptor *cmd;
|
||||
union intelxl_admin_buffer *buf;
|
||||
int rc;
|
||||
|
||||
/* Populate descriptor */
|
||||
cmd = intelxl_admin_command_descriptor ( intelxl );
|
||||
cmd->vopcode = cpu_to_le32 ( INTELXL_ADMIN_VF_PROMISC );
|
||||
cmd->flags = cpu_to_le16 ( INTELXL_ADMIN_FL_RD | INTELXL_ADMIN_FL_BUF );
|
||||
cmd->len = cpu_to_le16 ( sizeof ( buf->promisc ) );
|
||||
buf = intelxl_admin_command_buffer ( intelxl );
|
||||
buf->promisc.vsi = cpu_to_le16 ( intelxl->vsi );
|
||||
buf->promisc.flags = cpu_to_le16 ( INTELXL_ADMIN_PROMISC_FL_UNICAST |
|
||||
INTELXL_ADMIN_PROMISC_FL_MULTICAST );
|
||||
|
||||
/* Issue command */
|
||||
if ( ( rc = intelxlvf_admin_command ( netdev ) ) != 0 )
|
||||
return rc;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Open network device
|
||||
*
|
||||
* @v netdev Network device
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int intelxlvf_open ( struct net_device *netdev ) {
|
||||
struct intelxl_nic *intelxl = netdev->priv;
|
||||
int rc;
|
||||
|
||||
/* Calculate maximum frame size */
|
||||
intelxl->mfs = ( ( ETH_HLEN + netdev->mtu + 4 /* CRC */ +
|
||||
INTELXL_ALIGN - 1 ) & ~( INTELXL_ALIGN - 1 ) );
|
||||
|
||||
/* Allocate transmit descriptor ring */
|
||||
if ( ( rc = intelxl_alloc_ring ( intelxl, &intelxl->tx ) ) != 0 )
|
||||
goto err_alloc_tx;
|
||||
|
||||
/* Allocate receive descriptor ring */
|
||||
if ( ( rc = intelxl_alloc_ring ( intelxl, &intelxl->rx ) ) != 0 )
|
||||
goto err_alloc_rx;
|
||||
|
||||
/* Configure queues */
|
||||
if ( ( rc = intelxlvf_admin_configure ( netdev ) ) != 0 )
|
||||
goto err_configure;
|
||||
|
||||
/* Configure IRQ map */
|
||||
if ( ( rc = intelxlvf_admin_irq_map ( netdev ) ) != 0 )
|
||||
goto err_irq_map;
|
||||
|
||||
/* Enable queues */
|
||||
if ( ( rc = intelxlvf_admin_queues ( netdev, 1 ) ) != 0 )
|
||||
goto err_enable;
|
||||
|
||||
/* Configure promiscuous mode */
|
||||
if ( ( rc = intelxlvf_admin_promisc ( netdev ) ) != 0 )
|
||||
goto err_promisc;
|
||||
|
||||
return 0;
|
||||
|
||||
err_promisc:
|
||||
intelxlvf_admin_queues ( netdev, INTELXL_ADMIN_VF_DISABLE );
|
||||
err_enable:
|
||||
err_irq_map:
|
||||
err_configure:
|
||||
intelxl_free_ring ( intelxl, &intelxl->rx );
|
||||
err_alloc_rx:
|
||||
intelxl_free_ring ( intelxl, &intelxl->tx );
|
||||
err_alloc_tx:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Close network device
|
||||
*
|
||||
* @v netdev Network device
|
||||
*/
|
||||
static void intelxlvf_close ( struct net_device *netdev ) {
|
||||
struct intelxl_nic *intelxl = netdev->priv;
|
||||
int rc;
|
||||
|
||||
/* Disable queues */
|
||||
if ( ( rc = intelxlvf_admin_queues ( netdev, 0 ) ) != 0 ) {
|
||||
/* Leak memory; there's nothing else we can do */
|
||||
return;
|
||||
}
|
||||
|
||||
/* Free receive descriptor ring */
|
||||
intelxl_free_ring ( intelxl, &intelxl->rx );
|
||||
|
||||
/* Free transmit descriptor ring */
|
||||
intelxl_free_ring ( intelxl, &intelxl->tx );
|
||||
|
||||
/* Discard any unused receive buffers */
|
||||
intelxl_empty_rx ( intelxl );
|
||||
}
|
||||
|
||||
/** Network device operations */
|
||||
static struct net_device_operations intelxlvf_operations = {
|
||||
.open = intelxlvf_open,
|
||||
.close = intelxlvf_close,
|
||||
.transmit = intelxl_transmit,
|
||||
.poll = intelxl_poll,
|
||||
};
|
||||
|
||||
/******************************************************************************
|
||||
*
|
||||
* PCI interface
|
||||
*
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
/**
|
||||
* Probe PCI device
|
||||
*
|
||||
* @v pci PCI device
|
||||
* @ret rc Return status code
|
||||
*/
|
||||
static int intelxlvf_probe ( struct pci_device *pci ) {
|
||||
struct net_device *netdev;
|
||||
struct intelxl_nic *intelxl;
|
||||
int rc;
|
||||
|
||||
/* Allocate and initialise net device */
|
||||
netdev = alloc_etherdev ( sizeof ( *intelxl ) );
|
||||
if ( ! netdev ) {
|
||||
rc = -ENOMEM;
|
||||
goto err_alloc;
|
||||
}
|
||||
netdev_init ( netdev, &intelxlvf_operations );
|
||||
intelxl = netdev->priv;
|
||||
pci_set_drvdata ( pci, netdev );
|
||||
netdev->dev = &pci->dev;
|
||||
memset ( intelxl, 0, sizeof ( *intelxl ) );
|
||||
intelxl->intr = INTELXLVF_VFINT_DYN_CTL0;
|
||||
intelxl_init_admin ( &intelxl->command, INTELXLVF_ADMIN,
|
||||
&intelxlvf_admin_command_offsets );
|
||||
intelxl_init_admin ( &intelxl->event, INTELXLVF_ADMIN,
|
||||
&intelxlvf_admin_event_offsets );
|
||||
intelxlvf_init_ring ( &intelxl->tx, INTELXL_TX_NUM_DESC,
|
||||
sizeof ( intelxl->tx.desc.tx[0] ),
|
||||
INTELXLVF_QTX_TAIL );
|
||||
intelxlvf_init_ring ( &intelxl->rx, INTELXL_RX_NUM_DESC,
|
||||
sizeof ( intelxl->rx.desc.rx[0] ),
|
||||
INTELXLVF_QRX_TAIL );
|
||||
|
||||
/* Fix up PCI device */
|
||||
adjust_pci_device ( pci );
|
||||
|
||||
/* Map registers */
|
||||
intelxl->regs = ioremap ( pci->membase, INTELXLVF_BAR_SIZE );
|
||||
if ( ! intelxl->regs ) {
|
||||
rc = -ENODEV;
|
||||
goto err_ioremap;
|
||||
}
|
||||
|
||||
/* Locate PCI Express capability */
|
||||
intelxl->exp = pci_find_capability ( pci, PCI_CAP_ID_EXP );
|
||||
if ( ! intelxl->exp ) {
|
||||
DBGC ( intelxl, "INTELXL %p missing PCIe capability\n",
|
||||
intelxl );
|
||||
rc = -ENXIO;
|
||||
goto err_exp;
|
||||
}
|
||||
|
||||
/* Reset the function via PCIe FLR */
|
||||
intelxlvf_reset_flr ( intelxl, pci );
|
||||
|
||||
/* Enable MSI-X dummy interrupt */
|
||||
if ( ( rc = intelxl_msix_enable ( intelxl, pci ) ) != 0 )
|
||||
goto err_msix;
|
||||
|
||||
/* Open admin queues */
|
||||
if ( ( rc = intelxl_open_admin ( intelxl ) ) != 0 )
|
||||
goto err_open_admin;
|
||||
|
||||
/* Reset the function via admin queue */
|
||||
if ( ( rc = intelxlvf_reset_admin ( intelxl ) ) != 0 )
|
||||
goto err_reset_admin;
|
||||
|
||||
/* Get MAC address */
|
||||
if ( ( rc = intelxlvf_admin_get_resources ( netdev ) ) != 0 )
|
||||
goto err_get_resources;
|
||||
|
||||
/* Register network device */
|
||||
if ( ( rc = register_netdev ( netdev ) ) != 0 )
|
||||
goto err_register_netdev;
|
||||
|
||||
return 0;
|
||||
|
||||
unregister_netdev ( netdev );
|
||||
err_register_netdev:
|
||||
err_get_resources:
|
||||
err_reset_admin:
|
||||
intelxl_close_admin ( intelxl );
|
||||
err_open_admin:
|
||||
intelxl_msix_disable ( intelxl, pci );
|
||||
err_msix:
|
||||
intelxlvf_reset_flr ( intelxl, pci );
|
||||
err_exp:
|
||||
iounmap ( intelxl->regs );
|
||||
err_ioremap:
|
||||
netdev_nullify ( netdev );
|
||||
netdev_put ( netdev );
|
||||
err_alloc:
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove PCI device
|
||||
*
|
||||
* @v pci PCI device
|
||||
*/
|
||||
static void intelxlvf_remove ( struct pci_device *pci ) {
|
||||
struct net_device *netdev = pci_get_drvdata ( pci );
|
||||
struct intelxl_nic *intelxl = netdev->priv;
|
||||
|
||||
/* Unregister network device */
|
||||
unregister_netdev ( netdev );
|
||||
|
||||
/* Reset the function via admin queue */
|
||||
intelxlvf_reset_admin ( intelxl );
|
||||
|
||||
/* Close admin queues */
|
||||
intelxl_close_admin ( intelxl );
|
||||
|
||||
/* Disable MSI-X dummy interrupt */
|
||||
intelxl_msix_disable ( intelxl, pci );
|
||||
|
||||
/* Reset the function via PCIe FLR */
|
||||
intelxlvf_reset_flr ( intelxl, pci );
|
||||
|
||||
/* Free network device */
|
||||
iounmap ( intelxl->regs );
|
||||
netdev_nullify ( netdev );
|
||||
netdev_put ( netdev );
|
||||
}
|
||||
|
||||
/** PCI device IDs */
|
||||
static struct pci_device_id intelxlvf_nics[] = {
|
||||
PCI_ROM ( 0x8086, 0x154c, "xl710-vf", "XL710 VF", 0 ),
|
||||
PCI_ROM ( 0x8086, 0x1571, "xl710-vf-hv", "XL710 VF (Hyper-V)", 0 ),
|
||||
PCI_ROM ( 0x8086, 0x1889, "xl710-vf-ad", "XL710 VF (adaptive)", 0 ),
|
||||
PCI_ROM ( 0x8086, 0x37cd, "x722-vf", "X722 VF", 0 ),
|
||||
PCI_ROM ( 0x8086, 0x37d9, "x722-vf-hv", "X722 VF (Hyper-V)", 0 ),
|
||||
};
|
||||
|
||||
/** PCI driver */
|
||||
struct pci_driver intelxlvf_driver __pci_driver = {
|
||||
.ids = intelxlvf_nics,
|
||||
.id_count = ( sizeof ( intelxlvf_nics ) /
|
||||
sizeof ( intelxlvf_nics[0] ) ),
|
||||
.probe = intelxlvf_probe,
|
||||
.remove = intelxlvf_remove,
|
||||
};
|
|
@ -0,0 +1,86 @@
|
|||
#ifndef _INTELXLVF_H
|
||||
#define _INTELXLVF_H
|
||||
|
||||
/** @file
|
||||
*
|
||||
* Intel 40 Gigabit Ethernet virtual function network card driver
|
||||
*
|
||||
*/
|
||||
|
||||
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
||||
|
||||
#include "intelxl.h"
|
||||
|
||||
/** BAR size */
|
||||
#define INTELXLVF_BAR_SIZE 0x10000
|
||||
|
||||
/** Transmit Queue Tail Register */
|
||||
#define INTELXLVF_QTX_TAIL 0x00000
|
||||
|
||||
/** Receive Queue Tail Register */
|
||||
#define INTELXLVF_QRX_TAIL 0x02000
|
||||
|
||||
/** VF Interrupt Zero Dynamic Control Register */
|
||||
#define INTELXLVF_VFINT_DYN_CTL0 0x5c00
|
||||
|
||||
/** VF Admin Queue register block */
|
||||
#define INTELXLVF_ADMIN 0x6000
|
||||
|
||||
/** Admin Command Queue Base Address Low Register (offset) */
|
||||
#define INTELXLVF_ADMIN_CMD_BAL 0x1c00
|
||||
|
||||
/** Admin Command Queue Base Address High Register (offset) */
|
||||
#define INTELXLVF_ADMIN_CMD_BAH 0x1800
|
||||
|
||||
/** Admin Command Queue Length Register (offset) */
|
||||
#define INTELXLVF_ADMIN_CMD_LEN 0x0800
|
||||
|
||||
/** Admin Command Queue Head Register (offset) */
|
||||
#define INTELXLVF_ADMIN_CMD_HEAD 0x0400
|
||||
|
||||
/** Admin Command Queue Tail Register (offset) */
|
||||
#define INTELXLVF_ADMIN_CMD_TAIL 0x2400
|
||||
|
||||
/** Admin Event Queue Base Address Low Register (offset) */
|
||||
#define INTELXLVF_ADMIN_EVT_BAL 0x0c00
|
||||
|
||||
/** Admin Event Queue Base Address High Register (offset) */
|
||||
#define INTELXLVF_ADMIN_EVT_BAH 0x0000
|
||||
|
||||
/** Admin Event Queue Length Register (offset) */
|
||||
#define INTELXLVF_ADMIN_EVT_LEN 0x2000
|
||||
|
||||
/** Admin Event Queue Head Register (offset) */
|
||||
#define INTELXLVF_ADMIN_EVT_HEAD 0x1400
|
||||
|
||||
/** Admin Event Queue Tail Register (offset) */
|
||||
#define INTELXLVF_ADMIN_EVT_TAIL 0x1000
|
||||
|
||||
/** Maximum time to wait for a VF admin request to complete */
|
||||
#define INTELXLVF_ADMIN_MAX_WAIT_MS 2000
|
||||
|
||||
/** VF Reset Status Register */
|
||||
#define INTELXLVF_VFGEN_RSTAT 0x8800
|
||||
#define INTELXLVF_VFGEN_RSTAT_VFR_STATE(x) ( (x) & 0x3 )
|
||||
#define INTELXLVF_VFGEN_RSTAT_VFR_STATE_ACTIVE 0x2
|
||||
|
||||
/** Maximum time to wait for reset to complete */
|
||||
#define INTELXLVF_RESET_MAX_WAIT_MS 1000
|
||||
|
||||
/**
|
||||
* Initialise descriptor ring
|
||||
*
|
||||
* @v ring Descriptor ring
|
||||
* @v count Number of descriptors
|
||||
* @v len Length of a single descriptor
|
||||
* @v tail Tail register offset
|
||||
*/
|
||||
static inline __attribute__ (( always_inline)) void
|
||||
intelxlvf_init_ring ( struct intelxl_ring *ring, unsigned int count,
|
||||
size_t len, unsigned int tail ) {
|
||||
|
||||
ring->len = ( count * len );
|
||||
ring->tail = tail;
|
||||
}
|
||||
|
||||
#endif /* _INTELXLVF_H */
|
|
@ -206,6 +206,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
|
|||
#define ERRFILE_icplus ( ERRFILE_DRIVER | 0x00ca0000 )
|
||||
#define ERRFILE_intelxl ( ERRFILE_DRIVER | 0x00cb0000 )
|
||||
#define ERRFILE_pcimsix ( ERRFILE_DRIVER | 0x00cc0000 )
|
||||
#define ERRFILE_intelxlvf ( ERRFILE_DRIVER | 0x00cd0000 )
|
||||
|
||||
#define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 )
|
||||
#define ERRFILE_arp ( ERRFILE_NET | 0x00010000 )
|
||||
|
|
Loading…
Reference in New Issue