summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYASUOKA Masahiko <yasuoka@cvs.openbsd.org>2024-11-02 04:37:21 +0000
committerYASUOKA Masahiko <yasuoka@cvs.openbsd.org>2024-11-02 04:37:21 +0000
commit4f7f276494e2f0894ee5b8a73031df85e0d65474 (patch)
tree5f0a1af6f8ec65e68f194e2794f3253e581e5126
parent8b888e6b8fba8f91bf27465970f5c209a57c333d (diff)
Add ixv(4), a driver for virtual functions of Intel 82598EB, 82559,
and X540. The driver was originally written by Intel and released as ixv driver version 1.5.32 and ported to OpenBSD by Yuichiro NAITO, naito.yuichiro at gmail.com. tested by henson at acm.org and many others. ok jmatthew
-rw-r--r--share/man/man4/Makefile4
-rw-r--r--share/man/man4/ixv.457
-rw-r--r--sys/arch/amd64/conf/GENERIC3
-rw-r--r--sys/dev/pci/files.pci22
-rw-r--r--sys/dev/pci/if_ixv.c1573
-rw-r--r--sys/dev/pci/ixgbe_vf.c799
6 files changed, 2447 insertions, 11 deletions
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index fbef6aff9eb..8a8004bd8b8 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.851 2024/09/04 11:12:53 jsg Exp $
+# $OpenBSD: Makefile,v 1.852 2024/11/02 04:37:20 yasuoka Exp $
MAN= aac.4 abcrtc.4 abl.4 ac97.4 acphy.4 acrtc.4 \
acpi.4 acpiac.4 acpials.4 acpiasus.4 acpibat.4 \
@@ -50,7 +50,7 @@ MAN= aac.4 abcrtc.4 abl.4 ac97.4 acphy.4 acrtc.4 \
inet.4 inet6.4 inphy.4 intelpmc.4 \
iophy.4 iosf.4 ip.4 ip6.4 ipcomp.4 ipgphy.4 \
ipmi.4 ips.4 ipsec.4 ipw.4 isa.4 isagpio.4 isapnp.4 islrtc.4 \
- it.4 itherm.4 iwi.4 iwn.4 iwm.4 iwx.4 ix.4 ixgb.4 ixl.4 \
+ it.4 itherm.4 iwi.4 iwn.4 iwm.4 iwx.4 ix.4 ixv.4 ixgb.4 ixl.4 \
jmb.4 jme.4 jmphy.4 \
kate.4 kcov.4 km.4 ksmn.4 kstat.4 ksyms.4 kubsan.4 kue.4 \
lc.4 lge.4 lii.4 \
diff --git a/share/man/man4/ixv.4 b/share/man/man4/ixv.4
new file mode 100644
index 00000000000..2a3585618f6
--- /dev/null
+++ b/share/man/man4/ixv.4
@@ -0,0 +1,57 @@
+.\" $OpenBSD: ixv.4,v 1.1 2024/11/02 04:37:20 yasuoka Exp $
+.\"
+.\" Copyright (c) 2024 YASUOKA Masahiko <yasuoka@yasuoka.net>
+.\" Copyright (c) 2019 Jonathan Matthew <jmatthew@openbsd.org>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: November 2 2024 $
+.Dt IXV 4
+.Os
+.Sh NAME
+.Nm ixv
+.Nd Intel 10Gb Ethernet Virtual Function device
+.Sh SYNOPSIS
+.Cd "ixv* at pci?"
+.Sh DESCRIPTION
+The
+.Nm
+driver supports the SR-IOV Virtual Functions of Intel 82598EB,
+82559, and X540-based Ethernet controller devices.
+.Sh SEE ALSO
+.Xr arp 4 ,
+.Xr ifmedia 4 ,
+.Xr intro 4 ,
+.Xr netintro 4 ,
+.Xr pci 4 ,
+.Xr hostname.if 5 ,
+.Xr ifconfig 8
+.Sh BUGS
+The VF relies on
+.Dq vlan-filter
+to receive VLAN-tagged packets.
+Because the current driver doesn't have a way to configure it,
+any VLAN-tagged packets will not be passed.
+Alternatively,
+on the host running the primary function,
+there might be a way to configure a VLAN ID.
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Ox 7.7 .
+.Sh AUTHORS
+The
+.Nm
+driver was written by
+Intel Corporation and ported to OpenBSD by NAITO Yuichiro.
diff --git a/sys/arch/amd64/conf/GENERIC b/sys/arch/amd64/conf/GENERIC
index b78703c62c1..f376faf9541 100644
--- a/sys/arch/amd64/conf/GENERIC
+++ b/sys/arch/amd64/conf/GENERIC
@@ -1,4 +1,4 @@
-# $OpenBSD: GENERIC,v 1.526 2024/09/04 07:45:08 jsg Exp $
+# $OpenBSD: GENERIC,v 1.527 2024/11/02 04:37:20 yasuoka Exp $
#
# For further information on compiling OpenBSD kernels, see the config(8)
# man page.
@@ -529,6 +529,7 @@ msk* at mskc? # each port of above
em* at pci? # Intel Pro/1000 ethernet
ixgb* at pci? # Intel Pro/10Gb ethernet
ix* at pci? # Intel 82598EB 10Gb ethernet
+#ixv* at pci? # Virtual Function of Intel 82599
myx* at pci? # Myricom Myri-10G 10Gb ethernet
oce* at pci? # Emulex OneConnect 10Gb ethernet
txp* at pci? # 3com 3CR990
diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci
index 42c5d61f7f5..a5d98a8fae9 100644
--- a/sys/dev/pci/files.pci
+++ b/sys/dev/pci/files.pci
@@ -1,4 +1,4 @@
-# $OpenBSD: files.pci,v 1.367 2024/10/24 18:52:59 bluhm Exp $
+# $OpenBSD: files.pci,v 1.368 2024/11/02 04:37:20 yasuoka Exp $
# $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $
#
# Config file and device description for machine-independent PCI code.
@@ -350,13 +350,19 @@ file dev/pci/ixgb_hw.c ixgb
# Intel 82598 10GbE
device ix: ether, ifnet, ifmedia, intrmap, stoeplitz
attach ix at pci
-file dev/pci/if_ix.c ix
-file dev/pci/ixgbe.c ix
-file dev/pci/ixgbe_82598.c ix
-file dev/pci/ixgbe_82599.c ix
-file dev/pci/ixgbe_x540.c ix
-file dev/pci/ixgbe_x550.c ix
-file dev/pci/ixgbe_phy.c ix
+file dev/pci/if_ix.c ix | ixv
+file dev/pci/ixgbe.c ix | ixv
+file dev/pci/ixgbe_82598.c ix | ixv
+file dev/pci/ixgbe_82599.c ix | ixv
+file dev/pci/ixgbe_x540.c ix | ixv
+file dev/pci/ixgbe_x550.c ix | ixv
+file dev/pci/ixgbe_phy.c ix | ixv
+
+# Virtual Function of i82599.
+device ixv: ether, ifnet, ifmedia, intrmap, stoeplitz
+attach ixv at pci
+file dev/pci/if_ixv.c ixv
+file dev/pci/ixgbe_vf.c ixv
# Intel Ethernet 700 Series
device ixl: ether, ifnet, ifmedia, intrmap, stoeplitz
diff --git a/sys/dev/pci/if_ixv.c b/sys/dev/pci/if_ixv.c
new file mode 100644
index 00000000000..f5525a40035
--- /dev/null
+++ b/sys/dev/pci/if_ixv.c
@@ -0,0 +1,1573 @@
+/* $OpenBSD: if_ixv.c,v 1.1 2024/11/02 04:37:20 yasuoka Exp $ */
+
+/******************************************************************************
+
+ Copyright (c) 2001-2017, Intel Corporation
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+#include <dev/pci/if_ix.h>
+#include <dev/pci/ixgbe_type.h>
+#include <dev/pci/ixgbe.h>
+
+/************************************************************************
+ * Driver version
+ ************************************************************************/
+char ixv_driver_version[] = "1.5.32";
+
+/************************************************************************
+ * PCI Device ID Table
+ *
+ * Used by probe to select devices to load on
+ *
+ * { Vendor ID, Device ID }
+ ************************************************************************/
+const struct pci_matchid ixv_devices[] = {
+ {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_82599VF},
+ {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_X540_VF},
+ {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_X550_VF},
+ {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_X550EM_X_VF},
+ {PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_X550EM_A_VF}
+};
+
+/************************************************************************
+ * Function prototypes
+ ************************************************************************/
+static int ixv_probe(struct device *, void *, void *);
+static void ixv_identify_hardware(struct ix_softc *sc);
+static void ixv_attach(struct device *, struct device *, void *);
+static int ixv_detach(struct device *, int);
+static int ixv_ioctl(struct ifnet *, u_long, caddr_t);
+static void ixv_watchdog(struct ifnet *);
+static void ixv_init(struct ix_softc *);
+static void ixv_stop(void *);
+static int ixv_allocate_msix(struct ix_softc *);
+static void ixv_setup_interface(struct device *, struct ix_softc *);
+static int ixv_negotiate_api(struct ix_softc *);
+
+static void ixv_initialize_transmit_units(struct ix_softc *);
+static void ixv_initialize_receive_units(struct ix_softc *);
+static void ixv_initialize_rss_mapping(struct ix_softc *);
+
+static void ixv_enable_intr(struct ix_softc *);
+static void ixv_disable_intr(struct ix_softc *);
+static void ixv_iff(struct ix_softc *);
+static void ixv_set_ivar(struct ix_softc *, uint8_t, uint8_t, int8_t);
+static void ixv_configure_ivars(struct ix_softc *);
+static uint8_t *ixv_mc_array_itr(struct ixgbe_hw *, uint8_t **, uint32_t *);
+
+static void ixv_setup_vlan_support(struct ix_softc *);
+
+/* The MSI-X Interrupt handlers */
+static int ixv_msix_que(void *);
+static int ixv_msix_mbx(void *);
+
+/* Share functions between ixv and ix. */
+void ixgbe_start(struct ifqueue *ifq);
+int ixgbe_activate(struct device *, int);
+int ixgbe_allocate_queues(struct ix_softc *);
+int ixgbe_setup_transmit_structures(struct ix_softc *);
+int ixgbe_setup_receive_structures(struct ix_softc *);
+void ixgbe_free_transmit_structures(struct ix_softc *);
+void ixgbe_free_receive_structures(struct ix_softc *);
+int ixgbe_txeof(struct ix_txring *);
+int ixgbe_rxeof(struct ix_rxring *);
+void ixgbe_rxrefill(void *);
+void ixgbe_update_link_status(struct ix_softc *);
+int ixgbe_allocate_pci_resources(struct ix_softc *);
+void ixgbe_free_pci_resources(struct ix_softc *);
+void ixgbe_media_status(struct ifnet *, struct ifmediareq *);
+int ixgbe_media_change(struct ifnet *);
+void ixgbe_add_media_types(struct ix_softc *);
+int ixgbe_get_sffpage(struct ix_softc *, struct if_sffpage *);
+int ixgbe_rxrinfo(struct ix_softc *, struct if_rxrinfo *);
+
+#if NKSTAT > 0
+static void ixv_kstats(struct ix_softc *);
+static void ixv_rxq_kstats(struct ix_softc *, struct ix_rxring *);
+static void ixv_txq_kstats(struct ix_softc *, struct ix_txring *);
+static void ixv_kstats_tick(void *);
+#endif
+
+/************************************************************************
+ * Value Definitions
+ ************************************************************************/
+/*
+ Default value for Extended Interrupt Throttling Register.
+ 128 * 2.048 uSec will be minimum interrupt iterval for 10GbE link.
+ Minimum interrupt interval can be set from 0 to 2044 in increments of 4.
+ */
+#define IXGBE_EITR_DEFAULT 128
+
+/*********************************************************************
+ * OpenBSD Device Interface Entry Points
+ *********************************************************************/
+
+struct cfdriver ixv_cd = {
+ NULL, "ixv", DV_IFNET
+};
+
+const struct cfattach ixv_ca = {
+ sizeof(struct ix_softc), ixv_probe, ixv_attach, ixv_detach,
+ ixgbe_activate
+};
+
+/************************************************************************
+ * ixv_probe - Device identification routine
+ *
+ * Determines if the driver should be loaded on
+ * adapter based on its PCI vendor/device ID.
+ *
+ * return BUS_PROBE_DEFAULT on success, positive on failure
+ ************************************************************************/
+static int
+ixv_probe(struct device *parent, void *match, void *aux)
+{
+ INIT_DEBUGOUT("ixv_probe: begin");
+
+ return (pci_matchbyid((struct pci_attach_args *)aux, ixv_devices,
+ nitems(ixv_devices)));
+}
+
+/*********************************************************************
+ *
+ * Determine hardware revision.
+ *
+ **********************************************************************/
+static void
+ixv_identify_hardware(struct ix_softc *sc)
+{
+ struct ixgbe_osdep *os = &sc->osdep;
+ struct pci_attach_args *pa = &os->os_pa;
+ uint32_t reg;
+
+ /* Save off the information about this board */
+ sc->hw.vendor_id = PCI_VENDOR(pa->pa_id);
+ sc->hw.device_id = PCI_PRODUCT(pa->pa_id);
+
+ reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_CLASS_REG);
+ sc->hw.revision_id = PCI_REVISION(reg);
+
+ reg = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_SUBSYS_ID_REG);
+ sc->hw.subsystem_vendor_id = PCI_VENDOR(reg);
+ sc->hw.subsystem_device_id = PCI_PRODUCT(reg);
+
+ sc->num_segs = IXGBE_82599_SCATTER;
+}
+
+/************************************************************************
+ * ixv_attach - Device initialization routine
+ *
+ * Called when the driver is being loaded.
+ * Identifies the type of hardware, allocates all resources
+ * and initializes the hardware.
+ *
+ * return 0 on success, positive on failure
+ ************************************************************************/
+static void
+ixv_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct pci_attach_args *pa = (struct pci_attach_args *)aux;
+ struct ix_softc *sc = (struct ix_softc *)self;
+ struct ixgbe_hw *hw;
+ int error;
+
+ INIT_DEBUGOUT("ixv_attach: begin");
+
+ sc->osdep.os_sc = sc;
+ sc->osdep.os_pa = *pa;
+
+ rw_init(&sc->sfflock, "ixvsff");
+
+ /* Allocate, clear, and link in our adapter structure */
+ sc->dev = *self;
+ sc->hw.back = sc;
+ hw = &sc->hw;
+
+ /* Indicate to RX setup to use Jumbo Clusters */
+ sc->num_tx_desc = DEFAULT_TXD;
+ sc->num_rx_desc = DEFAULT_RXD;
+
+ ixv_identify_hardware(sc);
+
+#if NKSTAT > 0
+ ixv_kstats(sc);
+#endif
+
+ /* Allocate multicast array memory */
+ sc->mta = mallocarray(IXGBE_ETH_LENGTH_OF_ADDRESS,
+ IXGBE_MAX_MULTICAST_ADDRESSES_VF, M_DEVBUF, M_NOWAIT);
+ if (sc->mta == NULL) {
+ printf("Can not allocate multicast setup array\n");
+ return;
+ }
+
+ /* Do base PCI setup - map BAR0 */
+ if (ixgbe_allocate_pci_resources(sc)) {
+ printf("ixgbe_allocate_pci_resources() failed!\n");
+ goto err_out;
+ }
+
+ /* Allocate our TX/RX Queues */
+ if (ixgbe_allocate_queues(sc)) {
+ printf("ixgbe_allocate_queues() failed!\n");
+ goto err_out;
+ }
+
+ /* A subset of set_mac_type */
+ switch (hw->device_id) {
+ case IXGBE_DEV_ID_82599_VF:
+ hw->mac.type = ixgbe_mac_82599_vf;
+ break;
+ case IXGBE_DEV_ID_X540_VF:
+ hw->mac.type = ixgbe_mac_X540_vf;
+ break;
+ case IXGBE_DEV_ID_X550_VF:
+ hw->mac.type = ixgbe_mac_X550_vf;
+ break;
+ case IXGBE_DEV_ID_X550EM_X_VF:
+ hw->mac.type = ixgbe_mac_X550EM_x_vf;
+ break;
+ case IXGBE_DEV_ID_X550EM_A_VF:
+ hw->mac.type = ixgbe_mac_X550EM_a_vf;
+ break;
+ default:
+ /* Shouldn't get here since probe succeeded */
+ printf("Unknown device ID!\n");
+ goto err_out;
+ }
+
+ /* Initialize the shared code */
+ if (ixgbe_init_ops_vf(hw)) {
+ printf("ixgbe_init_ops_vf() failed!\n");
+ goto err_out;
+ }
+
+ /* Setup the mailbox */
+ ixgbe_init_mbx_params_vf(hw);
+
+ /* Set the right number of segments */
+ sc->num_segs = IXGBE_82599_SCATTER;
+
+ error = hw->mac.ops.reset_hw(hw);
+ switch (error) {
+ case 0:
+ break;
+ case IXGBE_ERR_RESET_FAILED:
+ printf("...reset_hw() failure: Reset Failed!\n");
+ goto err_out;
+ default:
+ printf("...reset_hw() failed with error %d\n",
+ error);
+ goto err_out;
+ }
+
+ error = hw->mac.ops.init_hw(hw);
+ if (error) {
+ printf("...init_hw() failed with error %d\n",
+ error);
+ goto err_out;
+ }
+
+ /* Negotiate mailbox API version */
+ if (ixv_negotiate_api(sc)) {
+ printf("Mailbox API negotiation failed during attach!\n");
+ goto err_out;
+ }
+
+ /* If no mac address was assigned, make a random one */
+ if (memcmp(hw->mac.addr, etheranyaddr, ETHER_ADDR_LEN) == 0) {
+ ether_fakeaddr(&sc->arpcom.ac_if);
+ bcopy(sc->arpcom.ac_enaddr, hw->mac.addr, ETHER_ADDR_LEN);
+ bcopy(sc->arpcom.ac_enaddr, hw->mac.perm_addr, ETHER_ADDR_LEN);
+ } else
+ bcopy(hw->mac.addr, sc->arpcom.ac_enaddr, ETHER_ADDR_LEN);
+
+ /* Setup OS specific network interface */
+ ixv_setup_interface(self, sc);
+
+ /* Setup MSI-X */
+ if (ixv_allocate_msix(sc)) {
+ printf("ixv_allocate_msix() failed!\n");
+ goto err_late;
+ }
+
+ /* Check if VF was disabled by PF */
+ if (hw->mac.ops.get_link_state(hw, &sc->link_enabled)) {
+ /* PF is not capable of controlling VF state. Enable the link. */
+ sc->link_enabled = TRUE;
+ }
+
+ /* Set an initial default flow control value */
+ sc->fc = ixgbe_fc_full;
+
+ INIT_DEBUGOUT("ixv_attach: end");
+
+ return;
+
+err_late:
+ ixgbe_free_transmit_structures(sc);
+ ixgbe_free_receive_structures(sc);
+err_out:
+ ixgbe_free_pci_resources(sc);
+ free(sc->mta, M_DEVBUF, IXGBE_ETH_LENGTH_OF_ADDRESS *
+ IXGBE_MAX_MULTICAST_ADDRESSES_VF);
+} /* ixv_attach */
+
+/************************************************************************
+ * ixv_detach - Device removal routine
+ *
+ * Called when the driver is being removed.
+ * Stops the adapter and deallocates all the resources
+ * that were allocated for driver operation.
+ *
+ * return 0 on success, positive on failure
+ ************************************************************************/
+static int
+ixv_detach(struct device *self, int flags)
+{
+ struct ix_softc *sc = (struct ix_softc *)self;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+
+ INIT_DEBUGOUT("ixv_detach: begin");
+
+ ixv_stop(sc);
+ ether_ifdetach(ifp);
+ if_detach(ifp);
+
+ free(sc->mta, M_DEVBUF, IXGBE_ETH_LENGTH_OF_ADDRESS *
+ IXGBE_MAX_MULTICAST_ADDRESSES_VF);
+
+ ixgbe_free_pci_resources(sc);
+
+ ixgbe_free_transmit_structures(sc);
+ ixgbe_free_receive_structures(sc);
+
+ return (0);
+} /* ixv_detach */
+
+/*********************************************************************
+ * Watchdog entry point
+ *
+ **********************************************************************/
+static void
+ixv_watchdog(struct ifnet * ifp)
+{
+ struct ix_softc *sc = (struct ix_softc *)ifp->if_softc;
+ struct ix_txring *txr = sc->tx_rings;
+ struct ixgbe_hw *hw = &sc->hw;
+ int tx_hang = FALSE;
+ int i;
+
+ /*
+ * The timer is set to 5 every time ixgbe_start() queues a packet.
+ * Anytime all descriptors are clean the timer is set to 0.
+ */
+ for (i = 0; i < sc->num_queues; i++, txr++) {
+ if (txr->watchdog_timer == 0 || --txr->watchdog_timer)
+ continue;
+ else {
+ tx_hang = TRUE;
+ break;
+ }
+ }
+ if (tx_hang == FALSE)
+ return;
+
+
+ printf("%s: Watchdog timeout -- resetting\n", ifp->if_xname);
+ for (i = 0; i < sc->num_queues; i++, txr++) {
+ printf("%s: Queue(%d) tdh = %d, hw tdt = %d\n", ifp->if_xname, i,
+ IXGBE_READ_REG(hw, IXGBE_VFTDH(i)),
+ IXGBE_READ_REG(hw, txr->tail));
+ printf("%s: TX(%d) Next TX to Clean = %d\n", ifp->if_xname,
+ i, txr->next_to_clean);
+ }
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ ixv_init(sc);
+}
+
+/************************************************************************
+ * ixv_init - Init entry point
+ *
+ * Used in two ways: It is used by the stack as an init entry
+ * point in network interface structure. It is also used
+ * by the driver as a hw/sw initialization routine to get
+ * to a consistent state.
+ *
+ * return 0 on success, positive on failure
+ ************************************************************************/
+void
+ixv_init(struct ix_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct ixgbe_hw *hw = &sc->hw;
+ struct ix_queue *que = sc->queues;
+ uint32_t mask;
+ int i, s, error = 0;
+
+ INIT_DEBUGOUT("ixv_init: begin");
+
+ s = splnet();
+
+ hw->adapter_stopped = FALSE;
+ hw->mac.ops.stop_adapter(hw);
+
+ /* reprogram the RAR[0] in case user changed it. */
+ hw->mac.ops.set_rar(hw, 0, hw->mac.addr, 0, IXGBE_RAH_AV);
+
+ /* Get the latest mac address, User can use a LAA */
+ bcopy(sc->arpcom.ac_enaddr, sc->hw.mac.addr,
+ IXGBE_ETH_LENGTH_OF_ADDRESS);
+
+ sc->max_frame_size = ifp->if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN;
+
+ hw->mac.ops.set_rar(hw, 0, hw->mac.addr, 0, 1);
+
+ /* Prepare transmit descriptors and buffers */
+ if (ixgbe_setup_transmit_structures(sc)) {
+ printf("Could not setup transmit structures\n");
+ ixv_stop(sc);
+ splx(s);
+ return;
+ }
+
+ /* Reset VF and renegotiate mailbox API version */
+ hw->mac.ops.reset_hw(hw);
+ error = ixv_negotiate_api(sc);
+ if (error) {
+ printf("Mailbox API negotiation failed in init!\n");
+ splx(s);
+ return;
+ }
+
+ ixv_initialize_transmit_units(sc);
+
+ /* Setup Multicast table */
+ ixv_iff(sc);
+
+ /* Use 2k clusters, even for jumbo frames */
+ sc->rx_mbuf_sz = MCLBYTES + ETHER_ALIGN;
+
+ /* Prepare receive descriptors and buffers */
+ if (ixgbe_setup_receive_structures(sc)) {
+ printf("Could not setup receive structures\n");
+ ixv_stop(sc);
+ splx(s);
+ return;
+ }
+
+ /* Configure RX settings */
+ ixv_initialize_receive_units(sc);
+
+ /* Set up VLAN offload and filter */
+ ixv_setup_vlan_support(sc);
+
+ /* Set up MSI-X routing */
+ ixv_configure_ivars(sc);
+
+ /* Set up auto-mask */
+ mask = (1 << sc->linkvec);
+ for (i = 0; i < sc->num_queues; i++, que++)
+ mask |= (1 << que->msix);
+ IXGBE_WRITE_REG(hw, IXGBE_VTEIAM, mask);
+
+ /* Set moderation on the Link interrupt */
+ IXGBE_WRITE_REG(&sc->hw, IXGBE_VTEITR(sc->linkvec),
+ IXGBE_LINK_ITR);
+
+ /* Config/Enable Link */
+ error = hw->mac.ops.get_link_state(hw, &sc->link_enabled);
+ if (error) {
+ /* PF is not capable of controlling VF state. Enable the link. */
+ sc->link_enabled = TRUE;
+ } else if (sc->link_enabled == FALSE)
+ printf("VF is disabled by PF\n");
+
+ hw->mac.ops.check_link(hw, &sc->link_speed, &sc->link_up,
+ FALSE);
+
+ /* And now turn on interrupts */
+ ixv_enable_intr(sc);
+
+ /* Now inform the stack we're ready */
+ ifp->if_flags |= IFF_RUNNING;
+ for (i = 0; i < sc->num_queues; i++)
+ ifq_clr_oactive(ifp->if_ifqs[i]);
+
+ splx(s);
+} /* ixv_init */
+
+/*
+ * MSI-X Interrupt Handlers and Tasklets
+ */
+
+static inline void
+ixv_enable_queue(struct ix_softc *sc, uint32_t vector)
+{
+ struct ixgbe_hw *hw = &sc->hw;
+ uint32_t queue = 1 << vector;
+ uint32_t mask;
+
+ mask = (IXGBE_EIMS_RTX_QUEUE & queue);
+ IXGBE_WRITE_REG(hw, IXGBE_VTEIMS, mask);
+} /* ixv_enable_queue */
+
+static inline void
+ixv_disable_queue(struct ix_softc *sc, uint32_t vector)
+{
+ struct ixgbe_hw *hw = &sc->hw;
+ uint64_t queue = (1ULL << vector);
+ uint32_t mask;
+
+ mask = (IXGBE_EIMS_RTX_QUEUE & queue);
+ IXGBE_WRITE_REG(hw, IXGBE_VTEIMC, mask);
+} /* ixv_disable_queue */
+
+/************************************************************************
+ * ixv_msix_que - MSI Queue Interrupt Service routine
+ ************************************************************************/
+int
+ixv_msix_que(void *arg)
+{
+ struct ix_queue *que = arg;
+ struct ix_softc *sc = que->sc;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct ix_txring *txr = que->txr;
+ struct ix_rxring *rxr = que->rxr;
+
+ if ((ifp->if_flags & IFF_RUNNING) == 0)
+ return 1;
+
+ ixv_disable_queue(sc, que->msix);
+
+ ixgbe_rxeof(rxr);
+ ixgbe_txeof(txr);
+ ixgbe_rxrefill(rxr);
+
+ /* Reenable this interrupt */
+ ixv_enable_queue(sc, que->msix);
+
+ return 1;
+} /* ixv_msix_que */
+
+
+/************************************************************************
+ * ixv_msix_mbx
+ ************************************************************************/
+static int
+ixv_msix_mbx(void *arg)
+{
+ struct ix_softc *sc = arg;
+ struct ixgbe_hw *hw = &sc->hw;
+
+ sc->hw.mac.get_link_status = TRUE;
+ KERNEL_LOCK();
+ ixgbe_update_link_status(sc);
+ KERNEL_UNLOCK();
+
+ IXGBE_WRITE_REG(hw, IXGBE_VTEIMS, (1 << sc->linkvec));
+
+
+ return 1;
+} /* ixv_msix_mbx */
+
+/************************************************************************
+ * ixv_negotiate_api
+ *
+ * Negotiate the Mailbox API with the PF;
+ * start with the most featured API first.
+ ************************************************************************/
+static int
+ixv_negotiate_api(struct ix_softc *sc)
+{
+ struct ixgbe_hw *hw = &sc->hw;
+ int mbx_api[] = { ixgbe_mbox_api_12,
+ ixgbe_mbox_api_11,
+ ixgbe_mbox_api_10,
+ ixgbe_mbox_api_unknown };
+ int i = 0;
+
+ while (mbx_api[i] != ixgbe_mbox_api_unknown) {
+ if (ixgbevf_negotiate_api_version(hw, mbx_api[i]) == 0)
+ return (0);
+ i++;
+ }
+
+ return (EINVAL);
+} /* ixv_negotiate_api */
+
+
+/************************************************************************
+ * ixv_iff - Multicast Update
+ *
+ * Called whenever multicast address list is updated.
+ ************************************************************************/
+static void
+ixv_iff(struct ix_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct ixgbe_hw *hw = &sc->hw;
+ struct arpcom *ac = &sc->arpcom;
+ uint8_t *mta, *update_ptr;
+ struct ether_multi *enm;
+ struct ether_multistep step;
+ int xcast_mode, mcnt = 0;
+
+ IOCTL_DEBUGOUT("ixv_iff: begin");
+
+ mta = sc->mta;
+ bzero(mta, sizeof(uint8_t) * IXGBE_ETH_LENGTH_OF_ADDRESS *
+ IXGBE_MAX_MULTICAST_ADDRESSES_VF);
+
+ ifp->if_flags &= ~IFF_ALLMULTI;
+ if (ifp->if_flags & IFF_PROMISC || ac->ac_multirangecnt > 0 ||
+ ac->ac_multicnt > IXGBE_MAX_MULTICAST_ADDRESSES_VF) {
+ ifp->if_flags |= IFF_ALLMULTI;
+ } else {
+ ETHER_FIRST_MULTI(step, &sc->arpcom, enm);
+ while (enm != NULL) {
+ bcopy(enm->enm_addrlo,
+ &mta[mcnt * IXGBE_ETH_LENGTH_OF_ADDRESS],
+ IXGBE_ETH_LENGTH_OF_ADDRESS);
+ mcnt++;
+
+ ETHER_NEXT_MULTI(step, enm);
+ }
+
+ update_ptr = mta;
+ hw->mac.ops.update_mc_addr_list(hw, update_ptr, mcnt,
+ ixv_mc_array_itr, TRUE);
+ }
+
+ /* request the most inclusive mode we need */
+ if (ISSET(ifp->if_flags, IFF_PROMISC))
+ xcast_mode = IXGBEVF_XCAST_MODE_PROMISC;
+ else if (ISSET(ifp->if_flags, IFF_ALLMULTI))
+ xcast_mode = IXGBEVF_XCAST_MODE_ALLMULTI;
+ else if (ISSET(ifp->if_flags, (IFF_BROADCAST | IFF_MULTICAST)))
+ xcast_mode = IXGBEVF_XCAST_MODE_MULTI;
+ else
+ xcast_mode = IXGBEVF_XCAST_MODE_NONE;
+
+ hw->mac.ops.update_xcast_mode(hw, xcast_mode);
+
+
+} /* ixv_iff */
+
+/************************************************************************
+ * ixv_mc_array_itr
+ *
+ * An iterator function needed by the multicast shared code.
+ * It feeds the shared code routine the addresses in the
+ * array of ixv_iff() one by one.
+ ************************************************************************/
+static uint8_t *
+ixv_mc_array_itr(struct ixgbe_hw *hw, uint8_t **update_ptr, uint32_t *vmdq)
+{
+ uint8_t *mta = *update_ptr;
+
+ *vmdq = 0;
+ *update_ptr = mta + IXGBE_ETH_LENGTH_OF_ADDRESS;
+
+ return (mta);
+} /* ixv_mc_array_itr */
+
+/************************************************************************
+ * ixv_stop - Stop the hardware
+ *
+ * Disables all traffic on the adapter by issuing a
+ * global reset on the MAC and deallocates TX/RX buffers.
+ ************************************************************************/
+static void
+ixv_stop(void *arg)
+{
+ struct ix_softc *sc = arg;
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct ixgbe_hw *hw = &sc->hw;
+ int i;
+
+ INIT_DEBUGOUT("ixv_stop: begin\n");
+#if NKSTAT > 0
+ timeout_del(&sc->sc_kstat_tmo);
+#endif
+ ixv_disable_intr(sc);
+
+
+ /* Tell the stack that the interface is no longer active */
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ hw->mac.ops.reset_hw(hw);
+ sc->hw.adapter_stopped = FALSE;
+ hw->mac.ops.stop_adapter(hw);
+
+ /* reprogram the RAR[0] in case user changed it. */
+ hw->mac.ops.set_rar(hw, 0, hw->mac.addr, 0, IXGBE_RAH_AV);
+
+ intr_barrier(sc->tag);
+ for (i = 0; i < sc->num_queues; i++) {
+ struct ifqueue *ifq = ifp->if_ifqs[i];
+ ifq_barrier(ifq);
+ ifq_clr_oactive(ifq);
+
+ if (sc->queues[i].tag != NULL)
+ intr_barrier(sc->queues[i].tag);
+ timeout_del(&sc->rx_rings[i].rx_refill);
+ }
+
+ KASSERT((ifp->if_flags & IFF_RUNNING) == 0);
+
+ /* Should we really clear all structures on stop? */
+ ixgbe_free_transmit_structures(sc);
+ ixgbe_free_receive_structures(sc);
+
+ ixgbe_update_link_status(sc);
+} /* ixv_stop */
+
+/************************************************************************
+ * ixv_setup_interface
+ *
+ * Setup networking device structure and register an interface.
+ ************************************************************************/
+static void
+ixv_setup_interface(struct device *dev, struct ix_softc *sc)
+{
+ struct ifnet *ifp;
+ int i;
+
+ ifp = &sc->arpcom.ac_if;
+
+ strlcpy(ifp->if_xname, sc->dev.dv_xname, IFNAMSIZ);
+ ifp->if_softc = sc;
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_xflags = IFXF_MPSAFE;
+ ifp->if_ioctl = ixv_ioctl;
+ ifp->if_qstart = ixgbe_start;
+ ifp->if_timer = 0;
+ ifp->if_watchdog = ixv_watchdog;
+ ifp->if_hardmtu = IXGBE_MAX_FRAME_SIZE -
+ ETHER_HDR_LEN - ETHER_CRC_LEN;
+ ifq_init_maxlen(&ifp->if_snd, sc->num_tx_desc - 1);
+
+ ifp->if_capabilities = IFCAP_VLAN_MTU;
+
+#if NVLAN > 0
+ ifp->if_capabilities |= IFCAP_VLAN_HWTAGGING;
+#endif
+
+ ifp->if_capabilities |= IFCAP_CSUM_TCPv4 | IFCAP_CSUM_UDPv4;
+ ifp->if_capabilities |= IFCAP_CSUM_TCPv6 | IFCAP_CSUM_UDPv6;
+ ifp->if_capabilities |= IFCAP_CSUM_IPv4;
+
+ /*
+ * Specify the media types supported by this sc and register
+ * callbacks to update media and link information
+ */
+ ifmedia_init(&sc->media, IFM_IMASK, ixgbe_media_change,
+ ixgbe_media_status);
+ ixgbe_add_media_types(sc);
+ ifmedia_set(&sc->media, IFM_ETHER | IFM_AUTO);
+
+ if_attach(ifp);
+ ether_ifattach(ifp);
+
+ if_attach_queues(ifp, sc->num_queues);
+ if_attach_iqueues(ifp, sc->num_queues);
+ for (i = 0; i < sc->num_queues; i++) {
+ struct ifqueue *ifq = ifp->if_ifqs[i];
+ struct ifiqueue *ifiq = ifp->if_iqs[i];
+ struct ix_txring *txr = &sc->tx_rings[i];
+ struct ix_rxring *rxr = &sc->rx_rings[i];
+
+ ifq->ifq_softc = txr;
+ txr->ifq = ifq;
+
+ ifiq->ifiq_softc = rxr;
+ rxr->ifiq = ifiq;
+
+#if NKSTAT > 0
+ ixv_txq_kstats(sc, txr);
+ ixv_rxq_kstats(sc, rxr);
+#endif
+ }
+
+ sc->max_frame_size = IXGBE_MAX_FRAME_SIZE;
+} /* ixv_setup_interface */
+
+/************************************************************************
+ * ixv_initialize_transmit_units - Enable transmit unit.
+ ************************************************************************/
+static void
+ixv_initialize_transmit_units(struct ix_softc *sc)
+{
+ struct ifnet *ifp = &sc->arpcom.ac_if;
+ struct ix_txring *txr;
+ struct ixgbe_hw *hw = &sc->hw;
+ uint64_t tdba;
+ uint32_t txctrl, txdctl;
+ int i;
+
+ for (i = 0; i < sc->num_queues; i++) {
+ txr = &sc->tx_rings[i];
+ tdba = txr->txdma.dma_map->dm_segs[0].ds_addr;
+
+ /* Set WTHRESH to 8, burst writeback */
+ txdctl = IXGBE_READ_REG(hw, IXGBE_VFTXDCTL(i));
+ txdctl |= (8 << 16);
+ IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(i), txdctl);
+
+ /* Set Tx Tail register */
+ txr->tail = IXGBE_VFTDT(i);
+
+ /* Set the HW Tx Head and Tail indices */
+ IXGBE_WRITE_REG(&sc->hw, IXGBE_VFTDH(i), 0);
+ IXGBE_WRITE_REG(&sc->hw, txr->tail, 0);
+
+ /* Setup Transmit Descriptor Cmd Settings */
+ txr->txd_cmd = IXGBE_TXD_CMD_IFCS;
+ txr->queue_status = IXGBE_QUEUE_IDLE;
+ txr->watchdog_timer = 0;
+
+ /* Set Ring parameters */
+ IXGBE_WRITE_REG(hw, IXGBE_VFTDBAL(i),
+ (tdba & 0x00000000ffffffffULL));
+ IXGBE_WRITE_REG(hw, IXGBE_VFTDBAH(i), (tdba >> 32));
+ IXGBE_WRITE_REG(hw, IXGBE_VFTDLEN(i),
+ sc->num_tx_desc * sizeof(struct ixgbe_legacy_tx_desc));
+ txctrl = IXGBE_READ_REG(hw, IXGBE_VFDCA_TXCTRL(i));
+ txctrl &= ~IXGBE_DCA_TXCTRL_DESC_WRO_EN;
+ IXGBE_WRITE_REG(hw, IXGBE_VFDCA_TXCTRL(i), txctrl);
+
+ /* Now enable */
+ txdctl = IXGBE_READ_REG(hw, IXGBE_VFTXDCTL(i));
+ txdctl |= IXGBE_TXDCTL_ENABLE;
+ IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(i), txdctl);
+ }
+ ifp->if_timer = 0;
+
+ return;
+} /* ixv_initialize_transmit_units */
+
+/************************************************************************
+ * ixv_initialize_rss_mapping
+ ************************************************************************/
+static void
+ixv_initialize_rss_mapping(struct ix_softc *sc)
+{
+ struct ixgbe_hw *hw = &sc->hw;
+ uint32_t reta = 0, mrqc, rss_key[10];
+ int queue_id;
+ int i, j;
+
+ /* set up random bits */
+ stoeplitz_to_key(&rss_key, sizeof(rss_key));
+
+ /* Now fill out hash function seeds */
+ for (i = 0; i < 10; i++)
+ IXGBE_WRITE_REG(hw, IXGBE_VFRSSRK(i), rss_key[i]);
+
+ /* Set up the redirection table */
+ for (i = 0, j = 0; i < 64; i++, j++) {
+ if (j == sc->num_queues)
+ j = 0;
+
+ /*
+ * Fetch the RSS bucket id for the given indirection
+ * entry. Cap it at the number of configured buckets
+ * (which is num_queues.)
+ */
+ queue_id = queue_id % sc->num_queues;
+
+ /*
+ * The low 8 bits are for hash value (n+0);
+ * The next 8 bits are for hash value (n+1), etc.
+ */
+ reta >>= 8;
+ reta |= ((uint32_t)queue_id) << 24;
+ if ((i & 3) == 3) {
+ IXGBE_WRITE_REG(hw, IXGBE_VFRETA(i >> 2), reta);
+ reta = 0;
+ }
+ }
+
+ /*
+ * Disable UDP - IP fragments aren't currently being handled
+ * and so we end up with a mix of 2-tuple and 4-tuple
+ * traffic.
+ */
+ mrqc = IXGBE_MRQC_RSSEN
+ | IXGBE_MRQC_RSS_FIELD_IPV4
+ | IXGBE_MRQC_RSS_FIELD_IPV4_TCP
+ | IXGBE_MRQC_RSS_FIELD_IPV6_EX_TCP
+ | IXGBE_MRQC_RSS_FIELD_IPV6_EX
+ | IXGBE_MRQC_RSS_FIELD_IPV6
+ | IXGBE_MRQC_RSS_FIELD_IPV6_TCP
+ ;
+ IXGBE_WRITE_REG(hw, IXGBE_VFMRQC, mrqc);
+} /* ixv_initialize_rss_mapping */
+
+
+/************************************************************************
+ * ixv_initialize_receive_units - Setup receive registers and features.
+ ************************************************************************/
+static void
+ixv_initialize_receive_units(struct ix_softc *sc)
+{
+ struct ix_rxring *rxr = sc->rx_rings;
+ struct ixgbe_hw *hw = &sc->hw;
+ uint64_t rdba;
+ uint32_t reg, rxdctl, bufsz, psrtype;
+ int i, j, k;
+
+ bufsz = (sc->rx_mbuf_sz - ETHER_ALIGN) >> IXGBE_SRRCTL_BSIZEPKT_SHIFT;
+
+ psrtype = IXGBE_PSRTYPE_TCPHDR
+ | IXGBE_PSRTYPE_UDPHDR
+ | IXGBE_PSRTYPE_IPV4HDR
+ | IXGBE_PSRTYPE_IPV6HDR
+ | IXGBE_PSRTYPE_L2HDR;
+
+ if (sc->num_queues > 1)
+ psrtype |= 1 << 29;
+
+ IXGBE_WRITE_REG(hw, IXGBE_VFPSRTYPE, psrtype);
+
+ /* Tell PF our max_frame size */
+ if (ixgbevf_rlpml_set_vf(hw, sc->max_frame_size) != 0) {
+ printf("There is a problem with the PF setup."
+ " It is likely the receive unit for this VF will not function correctly.\n");
+ }
+
+ for (i = 0; i < sc->num_queues; i++, rxr++) {
+ rdba = rxr->rxdma.dma_map->dm_segs[0].ds_addr;
+
+ /* Disable the queue */
+ rxdctl = IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(i));
+ rxdctl &= ~IXGBE_RXDCTL_ENABLE;
+ IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(i), rxdctl);
+ for (j = 0; j < 10; j++) {
+ if (IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(i)) &
+ IXGBE_RXDCTL_ENABLE)
+ msec_delay(1);
+ else
+ break;
+ }
+
+ /* Setup the Base and Length of the Rx Descriptor Ring */
+ IXGBE_WRITE_REG(hw, IXGBE_VFRDBAL(i),
+ (rdba & 0x00000000ffffffffULL));
+ IXGBE_WRITE_REG(hw, IXGBE_VFRDBAH(i), (rdba >> 32));
+ IXGBE_WRITE_REG(hw, IXGBE_VFRDLEN(i),
+ sc->num_rx_desc * sizeof(union ixgbe_adv_rx_desc));
+
+ /* Capture Rx Tail index */
+ rxr->tail = IXGBE_VFRDT(rxr->me);
+
+ /* Reset the ring indices */
+ IXGBE_WRITE_REG(hw, IXGBE_VFRDH(rxr->me), 0);
+ IXGBE_WRITE_REG(hw, rxr->tail, 0);
+
+ /* Set up the SRRCTL register */
+ reg = IXGBE_READ_REG(hw, IXGBE_VFSRRCTL(i));
+ reg &= ~IXGBE_SRRCTL_BSIZEHDR_MASK;
+ reg &= ~IXGBE_SRRCTL_BSIZEPKT_MASK;
+ reg |= bufsz;
+ reg |= IXGBE_SRRCTL_DESCTYPE_ADV_ONEBUF;
+ IXGBE_WRITE_REG(hw, IXGBE_VFSRRCTL(i), reg);
+
+ /* Do the queue enabling last */
+ rxdctl |= IXGBE_RXDCTL_ENABLE | IXGBE_RXDCTL_VME;
+ IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(i), rxdctl);
+ for (k = 0; k < 10; k++) {
+ if (IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(i)) &
+ IXGBE_RXDCTL_ENABLE)
+ break;
+ msec_delay(1);
+ }
+
+ /* Set the Tail Pointer */
+ IXGBE_WRITE_REG(hw, IXGBE_VFRDT(rxr->me),
+ sc->num_rx_desc - 1);
+ }
+
+ /*
+ * Do not touch RSS and RETA settings for older hardware
+ * as those are shared among PF and all VF.
+ */
+ if (sc->hw.mac.type >= ixgbe_mac_X550_vf)
+ ixv_initialize_rss_mapping(sc);
+
+ return;
+} /* ixv_initialize_receive_units */
+
+/************************************************************************
+ * ixv_setup_vlan_support
+ ************************************************************************/
+static void
+ixv_setup_vlan_support(struct ix_softc *sc)
+{
+ struct ixgbe_hw *hw = &sc->hw;
+ uint32_t ctrl, vid, vfta, retry;
+ int i, j;
+
+ /*
+ * We get here thru init, meaning
+ * a soft reset, this has already cleared
+ * the VFTA and other state, so if there
+ * have been no vlan's registered do nothing.
+ */
+ if (sc->num_vlans == 0)
+ return;
+
+ /* Enable the queues */
+ for (i = 0; i < sc->num_queues; i++) {
+ ctrl = IXGBE_READ_REG(hw, IXGBE_VFRXDCTL(i));
+ ctrl |= IXGBE_RXDCTL_VME;
+ IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(i), ctrl);
+ /*
+ * Let Rx path know that it needs to store VLAN tag
+ * as part of extra mbuf info.
+ */
+ }
+
+ /*
+ * A soft reset zero's out the VFTA, so
+ * we need to repopulate it now.
+ */
+ for (i = 0; i < IXGBE_VFTA_SIZE; i++) {
+ if (sc->shadow_vfta[i] == 0)
+ continue;
+ vfta = sc->shadow_vfta[i];
+ /*
+ * Reconstruct the vlan id's
+ * based on the bits set in each
+ * of the array ints.
+ */
+ for (j = 0; j < 32; j++) {
+ retry = 0;
+ if ((vfta & (1 << j)) == 0)
+ continue;
+ vid = (i * 32) + j;
+ /* Call the shared code mailbox routine */
+ while (hw->mac.ops.set_vfta(hw, vid, 0, TRUE, FALSE)) {
+ if (++retry > 5)
+ break;
+ }
+ }
+ }
+} /* ixv_setup_vlan_support */
+
+/************************************************************************
+ * ixv_enable_intr
+ ************************************************************************/
+static void
+ixv_enable_intr(struct ix_softc *sc)
+{
+ struct ixgbe_hw *hw = &sc->hw;
+ struct ix_queue *que = sc->queues;
+ uint32_t mask;
+ int i;
+
+ /* For VTEIAC */
+ mask = (1 << sc->linkvec);
+ for (i = 0; i < sc->num_queues; i++, que++)
+ mask |= (1 << que->msix);
+ IXGBE_WRITE_REG(hw, IXGBE_VTEIAC, mask);
+
+ /* For VTEIMS */
+ IXGBE_WRITE_REG(hw, IXGBE_VTEIMS, (1 << sc->linkvec));
+ que = sc->queues;
+ for (i = 0; i < sc->num_queues; i++, que++)
+ ixv_enable_queue(sc, que->msix);
+
+ IXGBE_WRITE_FLUSH(hw);
+
+ return;
+} /* ixv_enable_intr */
+
+/************************************************************************
+ * ixv_disable_intr
+ ************************************************************************/
+static void
+ixv_disable_intr(struct ix_softc *sc)
+{
+ IXGBE_WRITE_REG(&sc->hw, IXGBE_VTEIAC, 0);
+ IXGBE_WRITE_REG(&sc->hw, IXGBE_VTEIMC, ~0);
+ IXGBE_WRITE_FLUSH(&sc->hw);
+
+ return;
+} /* ixv_disable_intr */
+
+/************************************************************************
+ * ixv_set_ivar
+ *
+ * Setup the correct IVAR register for a particular MSI-X interrupt
+ * - entry is the register array entry
+ * - vector is the MSI-X vector for this queue
+ * - type is RX/TX/MISC
+ ************************************************************************/
+static void
+ixv_set_ivar(struct ix_softc *sc, uint8_t entry, uint8_t vector, int8_t type)
+{
+ struct ixgbe_hw *hw = &sc->hw;
+ uint32_t ivar, index;
+
+ vector |= IXGBE_IVAR_ALLOC_VAL;
+
+ if (type == -1) { /* MISC IVAR */
+ ivar = IXGBE_READ_REG(hw, IXGBE_VTIVAR_MISC);
+ ivar &= ~0xFF;
+ ivar |= vector;
+ IXGBE_WRITE_REG(hw, IXGBE_VTIVAR_MISC, ivar);
+ } else { /* RX/TX IVARS */
+ index = (16 * (entry & 1)) + (8 * type);
+ ivar = IXGBE_READ_REG(hw, IXGBE_VTIVAR(entry >> 1));
+ ivar &= ~(0xFF << index);
+ ivar |= (vector << index);
+ IXGBE_WRITE_REG(hw, IXGBE_VTIVAR(entry >> 1), ivar);
+ }
+} /* ixv_set_ivar */
+
+/************************************************************************
+ * ixv_configure_ivars
+ ************************************************************************/
+static void
+ixv_configure_ivars(struct ix_softc *sc)
+{
+ struct ix_queue *que = sc->queues;
+ int i;
+
+ for (i = 0; i < sc->num_queues; i++, que++) {
+ /* First the RX queue entry */
+ ixv_set_ivar(sc, i, que->msix, 0);
+ /* ... and the TX */
+ ixv_set_ivar(sc, i, que->msix, 1);
+ /* Set an initial value in EITR */
+ IXGBE_WRITE_REG(&sc->hw, IXGBE_VTEITR(que->msix),
+ IXGBE_EITR_DEFAULT);
+ }
+
+ /* For the mailbox interrupt */
+ ixv_set_ivar(sc, 1, sc->linkvec, -1);
+} /* ixv_configure_ivars */
+
+/************************************************************************
+ * ixv_ioctl - Ioctl entry point
+ *
+ * Called when the user wants to configure the interface.
+ *
+ * return 0 on success, positive on failure
+ ************************************************************************/
+static int
+ixv_ioctl(struct ifnet *ifp, u_long command, caddr_t data)
+{
+ struct ix_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ int s, error = 0;
+
+ s = splnet();
+
+ switch (command) {
+ case SIOCSIFADDR:
+ IOCTL_DEBUGOUT("ioctl: SIOCxIFADDR (Get/Set Interface Addr)");
+ ifp->if_flags |= IFF_UP;
+ if (!(ifp->if_flags & IFF_RUNNING))
+ ixv_init(sc);
+ break;
+
+ case SIOCSIFFLAGS:
+ IOCTL_DEBUGOUT("ioctl: SIOCSIFFLAGS (Set Interface Flags)");
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_flags & IFF_RUNNING)
+ error = ENETRESET;
+ else
+ ixv_init(sc);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ ixv_stop(sc);
+ }
+ break;
+
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ IOCTL_DEBUGOUT("ioctl: SIOCxIFMEDIA (Get/Set Interface Media)");
+ error = ifmedia_ioctl(ifp, ifr, &sc->media, command);
+ break;
+
+ case SIOCGIFRXR:
+ error = ixgbe_rxrinfo(sc, (struct if_rxrinfo *)ifr->ifr_data);
+ break;
+
+ default:
+ error = ether_ioctl(ifp, &sc->arpcom, command, data);
+ }
+
+ switch (error) {
+ case 0:
+ if (command == SIOCSIFMTU)
+ ixv_init(sc);
+ break;
+ case ENETRESET:
+ if (ifp->if_flags & IFF_RUNNING) {
+ ixv_disable_intr(sc);
+ ixv_iff(sc);
+ ixv_enable_intr(sc);
+ }
+ error = 0;
+ }
+
+ splx(s);
+ return (error);
+} /* ixv_ioctl */
+
+/************************************************************************
+ * ixv_allocate_msix - Setup MSI-X Interrupt resources and handlers
+ ************************************************************************/
+static int
+ixv_allocate_msix(struct ix_softc *sc)
+{
+ struct ixgbe_osdep *os = &sc->osdep;
+ struct pci_attach_args *pa = &os->os_pa;
+ int i = 0, error = 0, off;
+ struct ix_queue *que;
+ pci_intr_handle_t ih;
+ pcireg_t reg;
+
+ for (i = 0, que = sc->queues; i < sc->num_queues; i++, que++) {
+ if (pci_intr_map_msix(pa, i, &ih)) {
+ printf("ixv_allocate_msix: "
+ "pci_intr_map_msix vec %d failed\n", i);
+ error = ENOMEM;
+ goto fail;
+ }
+
+ que->tag = pci_intr_establish_cpu(pa->pa_pc, ih,
+ IPL_NET | IPL_MPSAFE, intrmap_cpu(sc->sc_intrmap, i),
+ ixv_msix_que, que, que->name);
+ if (que->tag == NULL) {
+ printf("ixv_allocate_msix: "
+ "pci_intr_establish vec %d failed\n", i);
+ error = ENOMEM;
+ goto fail;
+ }
+
+ que->msix = i;
+ }
+
+ /* and Mailbox */
+ if (pci_intr_map_msix(pa, i, &ih)) {
+ printf("ixgbe_allocate_msix: "
+ "pci_intr_map_msix mbox vector failed\n");
+ error = ENOMEM;
+ goto fail;
+ }
+
+ sc->tag = pci_intr_establish(pa->pa_pc, ih, IPL_NET | IPL_MPSAFE,
+ ixv_msix_mbx, sc, sc->dev.dv_xname);
+ if (sc->tag == NULL) {
+ printf("ixv_allocate_msix: "
+ "pci_intr_establish mbox vector failed\n");
+ error = ENOMEM;
+ goto fail;
+ }
+ sc->linkvec = i;
+
+ /*
+ * Due to a broken design QEMU will fail to properly
+ * enable the guest for MSI-X unless the vectors in
+ * the table are all set up, so we must rewrite the
+ * ENABLE in the MSI-X control register again at this
+ * point to cause it to successfully initialize us.
+ */
+ if (sc->hw.mac.type == ixgbe_mac_82599_vf) {
+ pci_get_capability(pa->pa_pc, pa->pa_tag, PCI_CAP_MSIX, &off, NULL);
+ reg = pci_conf_read(pa->pa_pc, pa->pa_tag, off);
+ pci_conf_write(pa->pa_pc, pa->pa_tag, off, reg | PCI_MSIX_MC_MSIXE);
+ }
+
+ printf(", %s, %d queue%s\n", pci_intr_string(pa->pa_pc, ih),
+ i, (i > 1) ? "s" : "");
+
+ return (0);
+
+fail:
+ for (que = sc->queues; i > 0; i--, que++) {
+ if (que->tag == NULL)
+ continue;
+ pci_intr_disestablish(pa->pa_pc, que->tag);
+ que->tag = NULL;
+ }
+ return (error);
+} /* ixv_allocate_msix */
+
+#if NKSTAT > 0
+enum ixv_counter_idx {
+ ixv_good_packets_received_count,
+ ixv_good_packets_transmitted_count,
+ ixv_good_octets_received_count,
+ ixv_good_octets_transmitted_count,
+ ixv_multicast_packets_received_count,
+
+ ixv_counter_num,
+};
+
+CTASSERT(KSTAT_KV_U_PACKETS <= 0xff);
+CTASSERT(KSTAT_KV_U_BYTES <= 0xff);
+
+struct ixv_counter {
+ char name[KSTAT_KV_NAMELEN];
+ uint32_t reg;
+ uint8_t width;
+ uint8_t unit;
+};
+
+static const struct ixv_counter ixv_counters[ixv_counter_num] = {
+ [ixv_good_packets_received_count] = { "rx good", IXGBE_VFGPRC, 32, KSTAT_KV_U_PACKETS },
+ [ixv_good_packets_transmitted_count] = { "tx good", IXGBE_VFGPTC, 32, KSTAT_KV_U_PACKETS },
+ [ixv_good_octets_received_count] = { "rx total", IXGBE_VFGORC_LSB, 36, KSTAT_KV_U_BYTES },
+ [ixv_good_octets_transmitted_count] = { "tx total", IXGBE_VFGOTC_LSB, 36, KSTAT_KV_U_BYTES },
+ [ixv_multicast_packets_received_count] = { "rx mcast", IXGBE_VFMPRC, 32, KSTAT_KV_U_PACKETS },
+};
+
+struct ixv_rxq_kstats {
+ struct kstat_kv qprc;
+ struct kstat_kv qbrc;
+ struct kstat_kv qprdc;
+};
+
+static const struct ixv_rxq_kstats ixv_rxq_kstats_tpl = {
+ KSTAT_KV_UNIT_INITIALIZER("packets",
+ KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS),
+ KSTAT_KV_UNIT_INITIALIZER("bytes",
+ KSTAT_KV_T_COUNTER64, KSTAT_KV_U_BYTES),
+ KSTAT_KV_UNIT_INITIALIZER("qdrops",
+ KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS),
+};
+
+struct ixv_txq_kstats {
+ struct kstat_kv qptc;
+ struct kstat_kv qbtc;
+};
+
+static const struct ixv_txq_kstats ixv_txq_kstats_tpl = {
+ KSTAT_KV_UNIT_INITIALIZER("packets",
+ KSTAT_KV_T_COUNTER64, KSTAT_KV_U_PACKETS),
+ KSTAT_KV_UNIT_INITIALIZER("bytes",
+ KSTAT_KV_T_COUNTER64, KSTAT_KV_U_BYTES),
+};
+
+static int ixv_kstats_read(struct kstat *ks);
+static int ixv_rxq_kstats_read(struct kstat *ks);
+static int ixv_txq_kstats_read(struct kstat *ks);
+
+static void
+ixv_kstats(struct ix_softc *sc)
+{
+ struct kstat *ks;
+ struct kstat_kv *kvs;
+ unsigned int i;
+
+ mtx_init(&sc->sc_kstat_mtx, IPL_SOFTCLOCK);
+ timeout_set(&sc->sc_kstat_tmo, ixv_kstats_tick, sc);
+
+ ks = kstat_create(sc->dev.dv_xname, 0, "ixv-stats", 0,
+ KSTAT_T_KV, 0);
+ if (ks == NULL)
+ return;
+
+ kvs = mallocarray(nitems(ixv_counters), sizeof(*kvs),
+ M_DEVBUF, M_WAITOK|M_ZERO);
+
+ for (i = 0; i < nitems(ixv_counters); i++) {
+ const struct ixv_counter *ixc = &ixv_counters[i];
+
+ kstat_kv_unit_init(&kvs[i], ixc->name,
+ KSTAT_KV_T_COUNTER64, ixc->unit);
+ }
+
+ kstat_set_mutex(ks, &sc->sc_kstat_mtx);
+ ks->ks_softc = sc;
+ ks->ks_data = kvs;
+ ks->ks_datalen = nitems(ixv_counters) * sizeof(*kvs);
+ ks->ks_read = ixv_kstats_read;
+
+ sc->sc_kstat = ks;
+ kstat_install(ks);
+}
+
+static void
+ixv_rxq_kstats(struct ix_softc *sc, struct ix_rxring *rxr)
+{
+ struct ixv_rxq_kstats *stats;
+ struct kstat *ks;
+
+ ks = kstat_create(sc->dev.dv_xname, 0, "ixv-rxq", rxr->me,
+ KSTAT_T_KV, 0);
+ if (ks == NULL)
+ return;
+
+ stats = malloc(sizeof(*stats), M_DEVBUF, M_WAITOK|M_ZERO);
+ *stats = ixv_rxq_kstats_tpl;
+
+ kstat_set_mutex(ks, &sc->sc_kstat_mtx);
+ ks->ks_softc = rxr;
+ ks->ks_data = stats;
+ ks->ks_datalen = sizeof(*stats);
+ ks->ks_read = ixv_rxq_kstats_read;
+
+ rxr->kstat = ks;
+ kstat_install(ks);
+}
+
+static void
+ixv_txq_kstats(struct ix_softc *sc, struct ix_txring *txr)
+{
+ struct ixv_txq_kstats *stats;
+ struct kstat *ks;
+
+ ks = kstat_create(sc->dev.dv_xname, 0, "ixv-txq", txr->me,
+ KSTAT_T_KV, 0);
+ if (ks == NULL)
+ return;
+
+ stats = malloc(sizeof(*stats), M_DEVBUF, M_WAITOK|M_ZERO);
+ *stats = ixv_txq_kstats_tpl;
+
+ kstat_set_mutex(ks, &sc->sc_kstat_mtx);
+ ks->ks_softc = txr;
+ ks->ks_data = stats;
+ ks->ks_datalen = sizeof(*stats);
+ ks->ks_read = ixv_txq_kstats_read;
+
+ txr->kstat = ks;
+ kstat_install(ks);
+}
+
+/**********************************************************************
+ *
+ * Update the board statistics counters.
+ *
+ **********************************************************************/
+
+static void
+ixv_kstats_tick(void *arg)
+{
+ struct ix_softc *sc = arg;
+ int i;
+
+ timeout_add_sec(&sc->sc_kstat_tmo, 1);
+
+ mtx_enter(&sc->sc_kstat_mtx);
+ ixv_kstats_read(sc->sc_kstat);
+ for (i = 0; i < sc->num_queues; i++) {
+ ixv_rxq_kstats_read(sc->rx_rings[i].kstat);
+ ixv_txq_kstats_read(sc->tx_rings[i].kstat);
+ }
+ mtx_leave(&sc->sc_kstat_mtx);
+}
+
+static uint64_t
+ixv_read36(struct ixgbe_hw *hw, bus_size_t loreg, bus_size_t hireg)
+{
+ uint64_t lo, hi;
+
+ lo = IXGBE_READ_REG(hw, loreg);
+ hi = IXGBE_READ_REG(hw, hireg);
+
+ return (((hi & 0xf) << 32) | lo);
+}
+
+static int
+ixv_kstats_read(struct kstat *ks)
+{
+ struct ix_softc *sc = ks->ks_softc;
+ struct kstat_kv *kvs = ks->ks_data;
+ struct ixgbe_hw *hw = &sc->hw;
+ unsigned int i;
+
+ for (i = 0; i < nitems(ixv_counters); i++) {
+ const struct ixv_counter *ixc = &ixv_counters[i];
+ uint32_t reg = ixc->reg;
+ uint64_t v;
+
+ if (reg == 0)
+ continue;
+
+ if (ixc->width > 32)
+ v = ixv_read36(hw, reg, reg + 4);
+ else
+ v = IXGBE_READ_REG(hw, reg);
+
+ kstat_kv_u64(&kvs[i]) = v;
+ }
+
+ getnanouptime(&ks->ks_updated);
+
+ return (0);
+}
+
+int
+ixv_rxq_kstats_read(struct kstat *ks)
+{
+ struct ixv_rxq_kstats *stats = ks->ks_data;
+ struct ix_rxring *rxr = ks->ks_softc;
+ struct ix_softc *sc = rxr->sc;
+ struct ixgbe_hw *hw = &sc->hw;
+ uint32_t i = rxr->me;
+
+ kstat_kv_u64(&stats->qprc) += IXGBE_READ_REG(hw, IXGBE_QPRC(i));
+ kstat_kv_u64(&stats->qprdc) += IXGBE_READ_REG(hw, IXGBE_QPRDC(i));
+ kstat_kv_u64(&stats->qbrc) +=
+ ixv_read36(hw, IXGBE_QBRC_L(i), IXGBE_QBRC_H(i));
+
+ getnanouptime(&ks->ks_updated);
+
+ return (0);
+}
+
+int
+ixv_txq_kstats_read(struct kstat *ks)
+{
+ struct ixv_txq_kstats *stats = ks->ks_data;
+ struct ix_txring *txr = ks->ks_softc;
+ struct ix_softc *sc = txr->sc;
+ struct ixgbe_hw *hw = &sc->hw;
+ uint32_t i = txr->me;
+
+ kstat_kv_u64(&stats->qptc) += IXGBE_READ_REG(hw, IXGBE_QPTC(i));
+ kstat_kv_u64(&stats->qbtc) +=
+ ixv_read36(hw, IXGBE_QBTC_L(i), IXGBE_QBTC_H(i));
+
+ getnanouptime(&ks->ks_updated);
+
+ return (0);
+}
+#endif /* NKVSTAT > 0 */
diff --git a/sys/dev/pci/ixgbe_vf.c b/sys/dev/pci/ixgbe_vf.c
new file mode 100644
index 00000000000..844e371ce23
--- /dev/null
+++ b/sys/dev/pci/ixgbe_vf.c
@@ -0,0 +1,799 @@
+/* $OpenBSD: ixgbe_vf.c,v 1.1 2024/11/02 04:37:20 yasuoka Exp $ */
+
+/******************************************************************************
+
+ Copyright (c) 2001-2017, Intel Corporation
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ 3. Neither the name of the Intel Corporation nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
+ LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+******************************************************************************/
+
+#include <dev/pci/ixgbe.h>
+#include <dev/pci/ixgbe_type.h>
+
+#ifndef IXGBE_VFWRITE_REG
+#define IXGBE_VFWRITE_REG IXGBE_WRITE_REG
+#endif
+#ifndef IXGBE_VFREAD_REG
+#define IXGBE_VFREAD_REG IXGBE_READ_REG
+#endif
+
+/**
+ Dummy handlers.
+ They are called from ix driver code,
+ and there is nothing to do for VF.
+ */
+static uint64_t
+ixgbe_dummy_uint64_handler_vf(struct ixgbe_hw *hw)
+{
+ return 0;
+}
+
+static int32_t
+ixgbe_dummy_handler_vf(struct ixgbe_hw *hw)
+{
+ return 0;
+}
+
+static void
+ixgbe_dummy_void_handler_vf(struct ixgbe_hw *hw)
+{
+ return;
+}
+
+/**
+ * ixgbe_init_ops_vf - Initialize the pointers for vf
+ * @hw: pointer to hardware structure
+ *
+ * This will assign function pointers, adapter-specific functions can
+ * override the assignment of generic function pointers by assigning
+ * their own adapter-specific function pointers.
+ * Does not touch the hardware.
+ **/
+int32_t ixgbe_init_ops_vf(struct ixgbe_hw *hw)
+{
+ /* MAC */
+ hw->mac.ops.init_hw = ixgbe_init_hw_vf;
+ hw->mac.ops.reset_hw = ixgbe_reset_hw_vf;
+ hw->mac.ops.start_hw = ixgbe_start_hw_vf;
+ /* Cannot clear stats on VF */
+ hw->mac.ops.clear_hw_cntrs = NULL;
+ hw->mac.ops.get_media_type = NULL;
+ hw->mac.ops.get_supported_physical_layer =
+ ixgbe_dummy_uint64_handler_vf;
+ hw->mac.ops.get_mac_addr = ixgbe_get_mac_addr_vf;
+ hw->mac.ops.stop_adapter = ixgbe_stop_adapter_vf;
+ hw->mac.ops.get_bus_info = NULL;
+ hw->mac.ops.negotiate_api_version = ixgbevf_negotiate_api_version;
+
+ /* Link */
+ hw->mac.ops.setup_link = ixgbe_setup_mac_link_vf;
+ hw->mac.ops.check_link = ixgbe_check_mac_link_vf;
+ hw->mac.ops.get_link_capabilities = NULL;
+
+ /* RAR, Multicast, VLAN */
+ hw->mac.ops.set_rar = ixgbe_set_rar_vf;
+ hw->mac.ops.set_uc_addr = ixgbevf_set_uc_addr_vf;
+ hw->mac.ops.init_rx_addrs = NULL;
+ hw->mac.ops.update_mc_addr_list = ixgbe_update_mc_addr_list_vf;
+ hw->mac.ops.update_xcast_mode = ixgbevf_update_xcast_mode;
+ hw->mac.ops.get_link_state = ixgbe_get_link_state_vf;
+ hw->mac.ops.enable_mc = NULL;
+ hw->mac.ops.disable_mc = NULL;
+ hw->mac.ops.clear_vfta = NULL;
+ hw->mac.ops.set_vfta = ixgbe_set_vfta_vf;
+ hw->mac.ops.set_rlpml = ixgbevf_rlpml_set_vf;
+
+ /* Flow Control */
+ hw->mac.ops.fc_enable = ixgbe_dummy_handler_vf;
+ hw->mac.ops.setup_fc = ixgbe_dummy_handler_vf;
+ hw->mac.ops.fc_autoneg = ixgbe_dummy_void_handler_vf;
+
+ hw->mac.max_tx_queues = 1;
+ hw->mac.max_rx_queues = 1;
+
+ hw->mbx.ops.init_params = ixgbe_init_mbx_params_vf;
+
+ return IXGBE_SUCCESS;
+}
+
+/* ixgbe_virt_clr_reg - Set register to default (power on) state.
+ * @hw: pointer to hardware structure
+ */
+static void ixgbe_virt_clr_reg(struct ixgbe_hw *hw)
+{
+ int i;
+ uint32_t vfsrrctl;
+ uint32_t vfdca_rxctrl;
+ uint32_t vfdca_txctrl;
+
+ /* VRSRRCTL default values (BSIZEPACKET = 2048, BSIZEHEADER = 256) */
+ vfsrrctl = 0x100 << IXGBE_SRRCTL_BSIZEHDRSIZE_SHIFT;
+ vfsrrctl |= 0x800 >> IXGBE_SRRCTL_BSIZEPKT_SHIFT;
+
+ /* DCA_RXCTRL default value */
+ vfdca_rxctrl = IXGBE_DCA_RXCTRL_DESC_RRO_EN |
+ IXGBE_DCA_RXCTRL_DATA_WRO_EN |
+ IXGBE_DCA_RXCTRL_HEAD_WRO_EN;
+
+ /* DCA_TXCTRL default value */
+ vfdca_txctrl = IXGBE_DCA_TXCTRL_DESC_RRO_EN |
+ IXGBE_DCA_TXCTRL_DESC_WRO_EN |
+ IXGBE_DCA_TXCTRL_DATA_RRO_EN;
+
+ IXGBE_WRITE_REG(hw, IXGBE_VFPSRTYPE, 0);
+
+ for (i = 0; i < 7; i++) {
+ IXGBE_WRITE_REG(hw, IXGBE_VFRDH(i), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_VFRDT(i), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_VFRXDCTL(i), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_VFSRRCTL(i), vfsrrctl);
+ IXGBE_WRITE_REG(hw, IXGBE_VFTDH(i), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_VFTDT(i), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_VFTXDCTL(i), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_VFTDWBAH(i), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_VFTDWBAL(i), 0);
+ IXGBE_WRITE_REG(hw, IXGBE_VFDCA_RXCTRL(i), vfdca_rxctrl);
+ IXGBE_WRITE_REG(hw, IXGBE_VFDCA_TXCTRL(i), vfdca_txctrl);
+ }
+
+ IXGBE_WRITE_FLUSH(hw);
+}
+
+/**
+ * ixgbe_start_hw_vf - Prepare hardware for Tx/Rx
+ * @hw: pointer to hardware structure
+ *
+ * Starts the hardware by filling the bus info structure and media type, clears
+ * all on chip counters, initializes receive address registers, multicast
+ * table, VLAN filter table, calls routine to set up link and flow control
+ * settings, and leaves transmit and receive units disabled and uninitialized
+ **/
+int32_t ixgbe_start_hw_vf(struct ixgbe_hw *hw)
+{
+ /* Clear adapter stopped flag */
+ hw->adapter_stopped = FALSE;
+
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_init_hw_vf - virtual function hardware initialization
+ * @hw: pointer to hardware structure
+ *
+ * Initialize the hardware by resetting the hardware and then starting
+ * the hardware
+ **/
+int32_t ixgbe_init_hw_vf(struct ixgbe_hw *hw)
+{
+ int32_t status = hw->mac.ops.start_hw(hw);
+
+ hw->mac.ops.get_mac_addr(hw, hw->mac.addr);
+
+ return status;
+}
+
+/**
+ * ixgbe_reset_hw_vf - Performs hardware reset
+ * @hw: pointer to hardware structure
+ *
+ * Resets the hardware by resetting the transmit and receive units, masks and
+ * clears all interrupts.
+ **/
+int32_t ixgbe_reset_hw_vf(struct ixgbe_hw *hw)
+{
+ struct ixgbe_mbx_info *mbx = &hw->mbx;
+ uint32_t timeout = IXGBE_VF_INIT_TIMEOUT;
+ int32_t ret_val = IXGBE_ERR_INVALID_MAC_ADDR;
+ uint32_t msgbuf[IXGBE_VF_PERMADDR_MSG_LEN];
+ uint8_t *addr = (uint8_t *)(&msgbuf[1]);
+
+ DEBUGFUNC("ixgbevf_reset_hw_vf");
+
+ /* Call adapter stop to disable tx/rx and clear interrupts */
+ hw->mac.ops.stop_adapter(hw);
+
+ /* reset the api version */
+ hw->api_version = ixgbe_mbox_api_10;
+ ixgbe_init_mbx_params_vf(hw);
+
+ DEBUGOUT("Issuing a function level reset to MAC\n");
+
+ IXGBE_VFWRITE_REG(hw, IXGBE_VFCTRL, IXGBE_CTRL_RST);
+ IXGBE_WRITE_FLUSH(hw);
+
+ msec_delay(50);
+
+ /* we cannot reset while the RSTI / RSTD bits are asserted */
+ while (!mbx->ops.check_for_rst(hw, 0) && timeout) {
+ timeout--;
+ usec_delay(5);
+ }
+
+ if (!timeout)
+ return IXGBE_ERR_RESET_FAILED;
+
+ /* Reset VF registers to initial values */
+ ixgbe_virt_clr_reg(hw);
+
+ /* mailbox timeout can now become active */
+ mbx->timeout = IXGBE_VF_MBX_INIT_TIMEOUT;
+
+ msgbuf[0] = IXGBE_VF_RESET;
+ ixgbe_write_mbx(hw, msgbuf, 1, 0);
+
+ msec_delay(10);
+
+ /*
+ * set our "perm_addr" based on info provided by PF
+ * also set up the mc_filter_type which is piggy backed
+ * on the mac address in word 3
+ */
+ ret_val = ixgbe_poll_mbx(hw, msgbuf,
+ IXGBE_VF_PERMADDR_MSG_LEN, 0);
+ if (ret_val)
+ return ret_val;
+
+ if (msgbuf[0] != (IXGBE_VF_RESET | IXGBE_VT_MSGTYPE_SUCCESS) &&
+ msgbuf[0] != (IXGBE_VF_RESET | IXGBE_VT_MSGTYPE_FAILURE))
+ return IXGBE_ERR_INVALID_MAC_ADDR;
+
+ if (msgbuf[0] == (IXGBE_VF_RESET | IXGBE_VT_MSGTYPE_SUCCESS))
+ memcpy(hw->mac.perm_addr, addr, IXGBE_ETH_LENGTH_OF_ADDRESS);
+
+ hw->mac.mc_filter_type = msgbuf[IXGBE_VF_MC_TYPE_WORD];
+
+ return ret_val;
+}
+
+/**
+ * ixgbe_stop_adapter_vf - Generic stop Tx/Rx units
+ * @hw: pointer to hardware structure
+ *
+ * Sets the adapter_stopped flag within ixgbe_hw struct. Clears interrupts,
+ * disables transmit and receive units. The adapter_stopped flag is used by
+ * the shared code and drivers to determine if the adapter is in a stopped
+ * state and should not touch the hardware.
+ **/
+int32_t ixgbe_stop_adapter_vf(struct ixgbe_hw *hw)
+{
+ uint32_t reg_val;
+ uint16_t i;
+
+ /*
+ * Set the adapter_stopped flag so other driver functions stop touching
+ * the hardware
+ */
+ hw->adapter_stopped = TRUE;
+
+ /* Clear interrupt mask to stop from interrupts being generated */
+ IXGBE_VFWRITE_REG(hw, IXGBE_VTEIMC, IXGBE_VF_IRQ_CLEAR_MASK);
+
+ /* Clear any pending interrupts, flush previous writes */
+ IXGBE_VFREAD_REG(hw, IXGBE_VTEICR);
+
+ /* Disable the transmit unit. Each queue must be disabled. */
+ for (i = 0; i < hw->mac.max_tx_queues; i++)
+ IXGBE_VFWRITE_REG(hw, IXGBE_VFTXDCTL(i), IXGBE_TXDCTL_SWFLSH);
+
+ /* Disable the receive unit by stopping each queue */
+ for (i = 0; i < hw->mac.max_rx_queues; i++) {
+ reg_val = IXGBE_VFREAD_REG(hw, IXGBE_VFRXDCTL(i));
+ reg_val &= ~IXGBE_RXDCTL_ENABLE;
+ IXGBE_VFWRITE_REG(hw, IXGBE_VFRXDCTL(i), reg_val);
+ }
+ /* Clear packet split and pool config */
+ IXGBE_WRITE_REG(hw, IXGBE_VFPSRTYPE, 0);
+
+ /* flush all queues disables */
+ IXGBE_WRITE_FLUSH(hw);
+ msec_delay(2);
+
+ return IXGBE_SUCCESS;
+}
+
+static int32_t ixgbevf_write_msg_read_ack(struct ixgbe_hw *hw, uint32_t *msg,
+ uint32_t *retmsg, uint16_t size)
+{
+ int32_t retval = ixgbe_write_mbx(hw, msg, size, 0);
+
+ if (retval)
+ return retval;
+
+ return ixgbe_poll_mbx(hw, retmsg, size, 0);
+}
+
+/**
+ * ixgbe_set_rar_vf - set device MAC address
+ * @hw: pointer to hardware structure
+ * @index: Receive address register to write
+ * @addr: Address to put into receive address register
+ * @vmdq: VMDq "set" or "pool" index
+ * @enable_addr: set flag that address is active
+ **/
+int32_t ixgbe_set_rar_vf(struct ixgbe_hw *hw, uint32_t index, uint8_t *addr,
+ uint32_t vmdq, uint32_t enable_addr)
+{
+ uint32_t msgbuf[3];
+ uint8_t *msg_addr = (uint8_t *)(&msgbuf[1]);
+ int32_t ret_val;
+
+ memset(msgbuf, 0, 12);
+ msgbuf[0] = IXGBE_VF_SET_MAC_ADDR;
+ memcpy(msg_addr, addr, 6);
+ ret_val = ixgbevf_write_msg_read_ack(hw, msgbuf, msgbuf, 3);
+
+ msgbuf[0] &= ~IXGBE_VT_MSGTYPE_CTS;
+
+ /* if nacked the address was rejected, use "perm_addr" */
+ if (!ret_val &&
+ (msgbuf[0] == (IXGBE_VF_SET_MAC_ADDR | IXGBE_VT_MSGTYPE_FAILURE))) {
+ ixgbe_get_mac_addr_vf(hw, hw->mac.addr);
+ return IXGBE_ERR_MBX;
+ }
+
+ return ret_val;
+}
+
+/**
+ * ixgbe_update_mc_addr_list_vf - Update Multicast addresses
+ * @hw: pointer to the HW structure
+ * @mc_addr_list: array of multicast addresses to program
+ * @mc_addr_count: number of multicast addresses to program
+ * @next: caller supplied function to return next address in list
+ * @clear: unused
+ *
+ * Updates the Multicast Table Array.
+ **/
+int32_t ixgbe_update_mc_addr_list_vf(struct ixgbe_hw *hw, uint8_t *mc_addr_list,
+ uint32_t mc_addr_count, ixgbe_mc_addr_itr next,
+ bool clear)
+{
+ uint32_t msgbuf[IXGBE_VFMAILBOX_SIZE];
+ uint16_t *vector_list = (uint16_t *)&msgbuf[1];
+ uint32_t vector;
+ uint32_t cnt, i;
+ uint32_t vmdq;
+
+ DEBUGFUNC("ixgbe_update_mc_addr_list_vf");
+
+ /* Each entry in the list uses 1 16 bit word. We have 30
+ * 16 bit words available in our HW msg buffer (minus 1 for the
+ * msg type). That's 30 hash values if we pack 'em right. If
+ * there are more than 30 MC addresses to add then punt the
+ * extras for now and then add code to handle more than 30 later.
+ * It would be unusual for a server to request that many multi-cast
+ * addresses except for in large enterprise network environments.
+ */
+
+ DEBUGOUT1("MC Addr Count = %d\n", mc_addr_count);
+
+ cnt = (mc_addr_count > IXGBE_MAX_MULTICAST_ADDRESSES_VF) ? IXGBE_MAX_MULTICAST_ADDRESSES_VF : mc_addr_count;
+ msgbuf[0] = IXGBE_VF_SET_MULTICAST;
+ msgbuf[0] |= cnt << IXGBE_VT_MSGINFO_SHIFT;
+
+ for (i = 0; i < cnt; i++) {
+ vector = ixgbe_mta_vector(hw, next(hw, &mc_addr_list, &vmdq));
+ DEBUGOUT1("Hash value = 0x%03X\n", vector);
+ vector_list[i] = (uint16_t)vector;
+ }
+
+ return ixgbevf_write_msg_read_ack(hw, msgbuf, msgbuf, IXGBE_VFMAILBOX_SIZE);
+}
+
+/**
+ * ixgbevf_update_xcast_mode - Update Multicast mode
+ * @hw: pointer to the HW structure
+ * @xcast_mode: new multicast mode
+ *
+ * Updates the Multicast Mode of VF.
+ **/
+int32_t ixgbevf_update_xcast_mode(struct ixgbe_hw *hw, int xcast_mode)
+{
+ uint32_t msgbuf[2];
+ int32_t err;
+
+ switch (hw->api_version) {
+ case ixgbe_mbox_api_12:
+ /* New modes were introduced in 1.3 version */
+ if (xcast_mode > IXGBEVF_XCAST_MODE_ALLMULTI)
+ return IXGBE_ERR_FEATURE_NOT_SUPPORTED;
+ /* Fall through */
+ case ixgbe_mbox_api_13:
+ case ixgbe_mbox_api_15:
+ break;
+ default:
+ return IXGBE_ERR_FEATURE_NOT_SUPPORTED;
+ }
+
+ msgbuf[0] = IXGBE_VF_UPDATE_XCAST_MODE;
+ msgbuf[1] = xcast_mode;
+
+ err = ixgbevf_write_msg_read_ack(hw, msgbuf, msgbuf, 2);
+ if (err)
+ return err;
+
+ msgbuf[0] &= ~IXGBE_VT_MSGTYPE_CTS;
+ if (msgbuf[0] == (IXGBE_VF_UPDATE_XCAST_MODE | IXGBE_VT_MSGTYPE_FAILURE))
+ return IXGBE_ERR_FEATURE_NOT_SUPPORTED;
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_get_link_state_vf - Get VF link state from PF
+ * @hw: pointer to the HW structure
+ * @link_state: link state storage
+ *
+ * Returns state of the operation error or success.
+ **/
+int32_t ixgbe_get_link_state_vf(struct ixgbe_hw *hw, bool *link_state)
+{
+ uint32_t msgbuf[2];
+ int32_t err;
+ int32_t ret_val;
+
+ msgbuf[0] = IXGBE_VF_GET_LINK_STATE;
+ msgbuf[1] = 0x0;
+
+ err = ixgbevf_write_msg_read_ack(hw, msgbuf, msgbuf, 2);
+
+ if (err || (msgbuf[0] & IXGBE_VT_MSGTYPE_FAILURE)) {
+ ret_val = IXGBE_ERR_MBX;
+ } else {
+ ret_val = IXGBE_SUCCESS;
+ *link_state = msgbuf[1];
+ }
+
+ return ret_val;
+}
+
+/**
+ * ixgbe_set_vfta_vf - Set/Unset vlan filter table address
+ * @hw: pointer to the HW structure
+ * @vlan: 12 bit VLAN ID
+ * @vind: unused by VF drivers
+ * @vlan_on: if TRUE then set bit, else clear bit
+ * @vlvf_bypass: boolean flag indicating updating default pool is okay
+ *
+ * Turn on/off specified VLAN in the VLAN filter table.
+ **/
+int32_t ixgbe_set_vfta_vf(struct ixgbe_hw *hw, uint32_t vlan, uint32_t vind,
+ bool vlan_on, bool vlvf_bypass)
+{
+ uint32_t msgbuf[2];
+ int32_t ret_val;
+
+ msgbuf[0] = IXGBE_VF_SET_VLAN;
+ msgbuf[1] = vlan;
+ /* Setting the 8 bit field MSG INFO to TRUE indicates "add" */
+ msgbuf[0] |= vlan_on << IXGBE_VT_MSGINFO_SHIFT;
+
+ ret_val = ixgbevf_write_msg_read_ack(hw, msgbuf, msgbuf, 2);
+ if (!ret_val && (msgbuf[0] & IXGBE_VT_MSGTYPE_SUCCESS))
+ return IXGBE_SUCCESS;
+
+ return ret_val | (msgbuf[0] & IXGBE_VT_MSGTYPE_FAILURE);
+}
+
+/**
+ * ixgbe_get_num_of_tx_queues_vf - Get number of TX queues
+ * @hw: pointer to hardware structure
+ *
+ * Returns the number of transmit queues for the given adapter.
+ **/
+uint32_t ixgbe_get_num_of_tx_queues_vf(struct ixgbe_hw *hw)
+{
+ return IXGBE_VF_MAX_TX_QUEUES;
+}
+
+/**
+ * ixgbe_get_num_of_rx_queues_vf - Get number of RX queues
+ * @hw: pointer to hardware structure
+ *
+ * Returns the number of receive queues for the given adapter.
+ **/
+uint32_t ixgbe_get_num_of_rx_queues_vf(struct ixgbe_hw *hw)
+{
+ return IXGBE_VF_MAX_RX_QUEUES;
+}
+
+/**
+ * ixgbe_get_mac_addr_vf - Read device MAC address
+ * @hw: pointer to the HW structure
+ * @mac_addr: the MAC address
+ **/
+int32_t ixgbe_get_mac_addr_vf(struct ixgbe_hw *hw, uint8_t *mac_addr)
+{
+ int i;
+
+ for (i = 0; i < IXGBE_ETH_LENGTH_OF_ADDRESS; i++)
+ mac_addr[i] = hw->mac.perm_addr[i];
+
+ return IXGBE_SUCCESS;
+}
+
+int32_t ixgbevf_set_uc_addr_vf(struct ixgbe_hw *hw, uint32_t index, uint8_t *addr)
+{
+ uint32_t msgbuf[3], msgbuf_chk;
+ uint8_t *msg_addr = (uint8_t *)(&msgbuf[1]);
+ int32_t ret_val;
+
+ memset(msgbuf, 0, sizeof(msgbuf));
+ /*
+ * If index is one then this is the start of a new list and needs
+ * indication to the PF so it can do it's own list management.
+ * If it is zero then that tells the PF to just clear all of
+ * this VF's macvlans and there is no new list.
+ */
+ msgbuf[0] |= index << IXGBE_VT_MSGINFO_SHIFT;
+ msgbuf[0] |= IXGBE_VF_SET_MACVLAN;
+ msgbuf_chk = msgbuf[0];
+ if (addr)
+ memcpy(msg_addr, addr, 6);
+
+ ret_val = ixgbevf_write_msg_read_ack(hw, msgbuf, msgbuf, 3);
+ if (!ret_val) {
+ msgbuf[0] &= ~IXGBE_VT_MSGTYPE_CTS;
+
+ if (msgbuf[0] == (msgbuf_chk | IXGBE_VT_MSGTYPE_FAILURE))
+ return IXGBE_ERR_OUT_OF_MEM;
+ }
+
+ return ret_val;
+}
+
+/**
+ * ixgbe_setup_mac_link_vf - Setup MAC link settings
+ * @hw: pointer to hardware structure
+ * @speed: new link speed
+ * @autoneg_wait_to_complete: TRUE when waiting for completion is needed
+ *
+ * Set the link speed in the AUTOC register and restarts link.
+ **/
+int32_t ixgbe_setup_mac_link_vf(struct ixgbe_hw *hw, ixgbe_link_speed speed,
+ bool autoneg_wait_to_complete)
+{
+ return IXGBE_SUCCESS;
+}
+
+/**
+ * ixgbe_check_mac_link_vf - Get link/speed status
+ * @hw: pointer to hardware structure
+ * @speed: pointer to link speed
+ * @link_up: TRUE is link is up, FALSE otherwise
+ * @autoneg_wait_to_complete: TRUE when waiting for completion is needed
+ *
+ * Reads the links register to determine if link is up and the current speed
+ **/
+int32_t ixgbe_check_mac_link_vf(struct ixgbe_hw *hw, ixgbe_link_speed *speed,
+ bool *link_up, bool autoneg_wait_to_complete)
+{
+ struct ixgbe_mbx_info *mbx = &hw->mbx;
+ struct ixgbe_mac_info *mac = &hw->mac;
+ int32_t ret_val = IXGBE_SUCCESS;
+ uint32_t in_msg = 0;
+ uint32_t links_reg;
+
+ /* If we were hit with a reset drop the link */
+ if (!mbx->ops.check_for_rst(hw, 0) || !mbx->timeout)
+ mac->get_link_status = TRUE;
+
+ if (!mac->get_link_status)
+ goto out;
+
+ /* if link status is down no point in checking to see if pf is up */
+ links_reg = IXGBE_READ_REG(hw, IXGBE_VFLINKS);
+ if (!(links_reg & IXGBE_LINKS_UP))
+ goto out;
+
+ /* for SFP+ modules and DA cables on 82599 it can take up to 500usecs
+ * before the link status is correct
+ */
+ if (mac->type == ixgbe_mac_82599_vf) {
+ int i;
+
+ for (i = 0; i < 5; i++) {
+ usec_delay(100);
+ links_reg = IXGBE_READ_REG(hw, IXGBE_VFLINKS);
+
+ if (!(links_reg & IXGBE_LINKS_UP))
+ goto out;
+ }
+ }
+
+ switch (links_reg & IXGBE_LINKS_SPEED_82599) {
+ case IXGBE_LINKS_SPEED_10G_82599:
+ *speed = IXGBE_LINK_SPEED_10GB_FULL;
+ if (hw->mac.type >= ixgbe_mac_X550_vf) {
+ if (links_reg & IXGBE_LINKS_SPEED_NON_STD)
+ *speed = IXGBE_LINK_SPEED_2_5GB_FULL;
+ }
+ break;
+ case IXGBE_LINKS_SPEED_1G_82599:
+ *speed = IXGBE_LINK_SPEED_1GB_FULL;
+ break;
+ case IXGBE_LINKS_SPEED_100_82599:
+ *speed = IXGBE_LINK_SPEED_100_FULL;
+ if (hw->mac.type == ixgbe_mac_X550_vf) {
+ if (links_reg & IXGBE_LINKS_SPEED_NON_STD)
+ *speed = IXGBE_LINK_SPEED_5GB_FULL;
+ }
+ break;
+ case IXGBE_LINKS_SPEED_10_X550EM_A:
+ *speed = IXGBE_LINK_SPEED_UNKNOWN;
+ /* Since Reserved in older MAC's */
+ if (hw->mac.type >= ixgbe_mac_X550_vf)
+ *speed = IXGBE_LINK_SPEED_10_FULL;
+ break;
+ default:
+ *speed = IXGBE_LINK_SPEED_UNKNOWN;
+ }
+
+ /* if the read failed it could just be a mailbox collision, best wait
+ * until we are called again and don't report an error
+ */
+ if (ixgbe_read_mbx(hw, &in_msg, 1, 0)) {
+ if (hw->api_version >= ixgbe_mbox_api_15)
+ mac->get_link_status = FALSE;
+ goto out;
+ }
+
+ if (!(in_msg & IXGBE_VT_MSGTYPE_CTS)) {
+ /* msg is not CTS and is NACK we must have lost CTS status */
+ if (in_msg & IXGBE_VT_MSGTYPE_FAILURE)
+ ret_val = IXGBE_ERR_MBX;
+ goto out;
+ }
+
+ /* the pf is talking, if we timed out in the past we reinit */
+ if (!mbx->timeout) {
+ ret_val = IXGBE_ERR_TIMEOUT;
+ goto out;
+ }
+
+ /* if we passed all the tests above then the link is up and we no
+ * longer need to check for link
+ */
+ mac->get_link_status = FALSE;
+
+out:
+ *link_up = !mac->get_link_status;
+ return ret_val;
+}
+
+/**
+ * ixgbevf_rlpml_set_vf - Set the maximum receive packet length
+ * @hw: pointer to the HW structure
+ * @max_size: value to assign to max frame size
+ **/
+int32_t ixgbevf_rlpml_set_vf(struct ixgbe_hw *hw, uint16_t max_size)
+{
+ uint32_t msgbuf[2];
+ int32_t retval;
+
+ msgbuf[0] = IXGBE_VF_SET_LPE;
+ msgbuf[1] = max_size;
+
+ retval = ixgbevf_write_msg_read_ack(hw, msgbuf, msgbuf, 2);
+ if (retval)
+ return retval;
+ if ((msgbuf[0] & IXGBE_VF_SET_LPE) &&
+ (msgbuf[0] & IXGBE_VT_MSGTYPE_FAILURE))
+ return IXGBE_ERR_MBX;
+
+ return 0;
+}
+
+/**
+ * ixgbevf_negotiate_api_version - Negotiate supported API version
+ * @hw: pointer to the HW structure
+ * @api: integer containing requested API version
+ **/
+int ixgbevf_negotiate_api_version(struct ixgbe_hw *hw, int api)
+{
+ int err;
+ uint32_t msg[3];
+
+ /* Negotiate the mailbox API version */
+ msg[0] = IXGBE_VF_API_NEGOTIATE;
+ msg[1] = api;
+ msg[2] = 0;
+
+ err = ixgbevf_write_msg_read_ack(hw, msg, msg, 3);
+ if (!err) {
+ msg[0] &= ~IXGBE_VT_MSGTYPE_CTS;
+
+ /* Store value and return 0 on success */
+ if (msg[0] == (IXGBE_VF_API_NEGOTIATE | IXGBE_VT_MSGTYPE_SUCCESS)) {
+ hw->api_version = api;
+ return 0;
+ }
+
+ err = IXGBE_ERR_INVALID_ARGUMENT;
+ }
+
+ return err;
+}
+
+int ixgbevf_get_queues(struct ixgbe_hw *hw, unsigned int *num_tcs,
+ unsigned int *default_tc)
+{
+ int err;
+ uint32_t msg[5];
+
+ /* do nothing if API doesn't support ixgbevf_get_queues */
+ switch (hw->api_version) {
+ case ixgbe_mbox_api_11:
+ case ixgbe_mbox_api_12:
+ case ixgbe_mbox_api_13:
+ case ixgbe_mbox_api_15:
+ break;
+ default:
+ return 0;
+ }
+
+ /* Fetch queue configuration from the PF */
+ msg[0] = IXGBE_VF_GET_QUEUES;
+ msg[1] = msg[2] = msg[3] = msg[4] = 0;
+
+ err = ixgbevf_write_msg_read_ack(hw, msg, msg, 5);
+ if (!err) {
+ msg[0] &= ~IXGBE_VT_MSGTYPE_CTS;
+
+ /*
+ * if we didn't get a SUCCESS there must have been
+ * some sort of mailbox error so we should treat it
+ * as such
+ */
+ if (msg[0] != (IXGBE_VF_GET_QUEUES | IXGBE_VT_MSGTYPE_SUCCESS))
+ return IXGBE_ERR_MBX;
+
+ /* record and validate values from message */
+ hw->mac.max_tx_queues = msg[IXGBE_VF_TX_QUEUES];
+ if (hw->mac.max_tx_queues == 0 ||
+ hw->mac.max_tx_queues > IXGBE_VF_MAX_TX_QUEUES)
+ hw->mac.max_tx_queues = IXGBE_VF_MAX_TX_QUEUES;
+
+ hw->mac.max_rx_queues = msg[IXGBE_VF_RX_QUEUES];
+ if (hw->mac.max_rx_queues == 0 ||
+ hw->mac.max_rx_queues > IXGBE_VF_MAX_RX_QUEUES)
+ hw->mac.max_rx_queues = IXGBE_VF_MAX_RX_QUEUES;
+
+ *num_tcs = msg[IXGBE_VF_TRANS_VLAN];
+ /* in case of unknown state assume we cannot tag frames */
+ if (*num_tcs > hw->mac.max_rx_queues)
+ *num_tcs = 1;
+
+ *default_tc = msg[IXGBE_VF_DEF_QUEUE];
+ /* default to queue 0 on out-of-bounds queue number */
+ if (*default_tc >= hw->mac.max_tx_queues)
+ *default_tc = 0;
+ }
+
+ return err;
+}