summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
authorPatrick Wildt <patrick@cvs.openbsd.org>2021-10-31 14:52:58 +0000
committerPatrick Wildt <patrick@cvs.openbsd.org>2021-10-31 14:52:58 +0000
commit988d3dd74581ae00d8550effe8c6b1a8a0b62673 (patch)
tree27441cf620b8cd2b85384933dd4315f65da41aff /sys
parent5b4c4b53809471fce76b1ea4639a2828da20f5e2 (diff)
Add igc(4), a driver for the Intel 2.5Gb Ethernet controllers.
Ported by kevlo@ ok jmatthew@
Diffstat (limited to 'sys')
-rw-r--r--sys/dev/pci/files.pci13
-rw-r--r--sys/dev/pci/if_igc.c2150
-rw-r--r--sys/dev/pci/if_igc.h343
-rw-r--r--sys/dev/pci/igc_api.c390
-rw-r--r--sys/dev/pci/igc_api.h55
-rw-r--r--sys/dev/pci/igc_base.c113
-rw-r--r--sys/dev/pci/igc_base.h134
-rw-r--r--sys/dev/pci/igc_defines.h1338
-rw-r--r--sys/dev/pci/igc_hw.h394
-rw-r--r--sys/dev/pci/igc_i225.c1238
-rw-r--r--sys/dev/pci/igc_i225.h111
-rw-r--r--sys/dev/pci/igc_mac.c833
-rw-r--r--sys/dev/pci/igc_mac.h46
-rw-r--r--sys/dev/pci/igc_nvm.c232
-rw-r--r--sys/dev/pci/igc_nvm.h29
-rw-r--r--sys/dev/pci/igc_phy.c908
-rw-r--r--sys/dev/pci/igc_phy.h128
-rw-r--r--sys/dev/pci/igc_regs.h433
18 files changed, 8887 insertions, 1 deletions
diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci
index a20459798b9..e4611434165 100644
--- a/sys/dev/pci/files.pci
+++ b/sys/dev/pci/files.pci
@@ -1,4 +1,4 @@
-# $OpenBSD: files.pci,v 1.356 2021/10/21 18:36:42 bluhm Exp $
+# $OpenBSD: files.pci,v 1.357 2021/10/31 14:52:57 patrick 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.
@@ -835,6 +835,17 @@ device rge: ether, ifnet, ifmedia
attach rge at pci
file dev/pci/if_rge.c rge
+# Intel Ethernet I225 Series
+device igc: ether, ifnet, ifmedia, intrmap, stoeplitz
+attach igc at pci
+file dev/pci/if_igc.c igc
+file dev/pci/igc_api.c igc
+file dev/pci/igc_base.c igc
+file dev/pci/igc_i225.c igc
+file dev/pci/igc_mac.c igc
+file dev/pci/igc_nvm.c igc
+file dev/pci/igc_phy.c igc
+
# NS16550 compatible UART
attach com at pci with com_pci
file dev/pci/com_pci.c com_pci
diff --git a/sys/dev/pci/if_igc.c b/sys/dev/pci/if_igc.c
new file mode 100644
index 00000000000..ed1c2f15867
--- /dev/null
+++ b/sys/dev/pci/if_igc.c
@@ -0,0 +1,2150 @@
+/* $OpenBSD: if_igc.c,v 1.1 2021/10/31 14:52:57 patrick Exp $ */
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2016 Nicole Graziano <nicole@nextbsd.org>
+ * All rights reserved.
+ * Copyright (c) 2021 Rubicon Communications, LLC (Netgate)
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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 "bpfilter.h"
+#include "vlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/device.h>
+#include <sys/endian.h>
+#include <sys/intrmap.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/toeplitz.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcidevs.h>
+#include <dev/pci/if_igc.h>
+#include <dev/pci/igc_hw.h>
+
+const struct pci_matchid igc_devices[] = {
+ { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_I220_V },
+ { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_I221_V },
+ { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_I225_BLANK_NVM },
+ { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_I225_I },
+ { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_I225_IT },
+ { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_I225_K },
+ { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_I225_K2 },
+ { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_I225_LM },
+ { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_I225_LMVP },
+ { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_I225_V },
+ { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_I226_BLANK_NVM },
+ { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_I226_IT },
+ { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_I226_LM },
+ { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_I226_K },
+ { PCI_VENDOR_INTEL, PCI_PRODUCT_INTEL_I226_V }
+};
+
+/*********************************************************************
+ * Function Prototypes
+ *********************************************************************/
+int igc_match(struct device *, void *, void *);
+void igc_attach(struct device *, struct device *, void *);
+int igc_detach(struct device *, int);
+
+void igc_identify_hardware(struct igc_softc *);
+int igc_allocate_pci_resources(struct igc_softc *);
+int igc_allocate_queues(struct igc_softc *);
+void igc_free_pci_resources(struct igc_softc *);
+void igc_reset(struct igc_softc *);
+void igc_init_dmac(struct igc_softc *, uint32_t);
+int igc_allocate_msix(struct igc_softc *);
+void igc_setup_msix(struct igc_softc *);
+int igc_dma_malloc(struct igc_softc *, bus_size_t, struct igc_dma_alloc *);
+void igc_dma_free(struct igc_softc *, struct igc_dma_alloc *);
+void igc_setup_interface(struct igc_softc *);
+
+void igc_init(void *);
+void igc_start(struct ifqueue *);
+void igc_stop(struct igc_softc *);
+int igc_ioctl(struct ifnet *, u_long, caddr_t);
+int igc_rxrinfo(struct igc_softc *, struct if_rxrinfo *);
+int igc_rxfill(struct rx_ring *);
+void igc_rxrefill(void *);
+int igc_rxeof(struct rx_ring *);
+void igc_rx_checksum(uint32_t, struct mbuf *, uint32_t);
+void igc_watchdog(struct ifnet *);
+void igc_media_status(struct ifnet *, struct ifmediareq *);
+int igc_media_change(struct ifnet *);
+void igc_iff(struct igc_softc *);
+void igc_update_link_status(struct igc_softc *);
+int igc_get_buf(struct rx_ring *, int);
+
+void igc_configure_queues(struct igc_softc *);
+void igc_set_queues(struct igc_softc *, uint32_t, uint32_t, int);
+void igc_enable_queue(struct igc_softc *, uint32_t);
+void igc_enable_intr(struct igc_softc *);
+void igc_disable_intr(struct igc_softc *);
+int igc_intr_link(void *);
+int igc_intr_queue(void *);
+
+int igc_allocate_transmit_buffers(struct tx_ring *);
+int igc_setup_transmit_structures(struct igc_softc *);
+int igc_setup_transmit_ring(struct tx_ring *);
+void igc_initialize_transmit_unit(struct igc_softc *);
+void igc_free_transmit_structures(struct igc_softc *);
+void igc_free_transmit_buffers(struct tx_ring *);
+int igc_allocate_receive_buffers(struct rx_ring *);
+int igc_setup_receive_structures(struct igc_softc *);
+int igc_setup_receive_ring(struct rx_ring *);
+void igc_initialize_receive_unit(struct igc_softc *);
+void igc_free_receive_structures(struct igc_softc *);
+void igc_free_receive_buffers(struct rx_ring *);
+void igc_initialize_rss_mapping(struct igc_softc *);
+
+void igc_get_hw_control(struct igc_softc *);
+void igc_release_hw_control(struct igc_softc *);
+int igc_is_valid_ether_addr(uint8_t *);
+
+/*********************************************************************
+ * OpenBSD Device Interface Entry Points
+ *********************************************************************/
+
+struct cfdriver igc_cd = {
+ NULL, "igc", DV_IFNET
+};
+
+struct cfattach igc_ca = {
+ sizeof(struct igc_softc), igc_match, igc_attach, igc_detach
+};
+
+/*********************************************************************
+ * Device identification routine
+ *
+ * igc_match determines if the driver should be loaded on
+ * adapter based on PCI vendor/device id of the adapter.
+ *
+ * return 0 on success, positive on failure
+ *********************************************************************/
+int
+igc_match(struct device *parent, void *match, void *aux)
+{
+ return pci_matchbyid((struct pci_attach_args *)aux, igc_devices,
+ nitems(igc_devices));
+}
+
+/*********************************************************************
+ * Device initialization routine
+ *
+ * The attach entry point is called when the driver is being loaded.
+ * This routine identifies the type of hardware, allocates all resources
+ * and initializes the hardware.
+ *
+ * return 0 on success, positive on failure
+ *********************************************************************/
+void
+igc_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct pci_attach_args *pa = (struct pci_attach_args *)aux;
+ struct igc_softc *sc = (struct igc_softc *)self;
+ struct igc_hw *hw = &sc->hw;
+
+ sc->osdep.os_sc = sc;
+ sc->osdep.os_pa = *pa;
+
+ /* Determine hardware and mac info */
+ igc_identify_hardware(sc);
+
+ sc->num_tx_desc = IGC_DEFAULT_TXD;
+ sc->num_rx_desc = IGC_DEFAULT_RXD;
+
+ /* Setup PCI resources */
+ if (igc_allocate_pci_resources(sc))
+ goto err_pci;
+
+ /* Allocate TX/RX queues */
+ if (igc_allocate_queues(sc))
+ goto err_pci;
+
+ /* Do shared code initialization */
+ if (igc_setup_init_funcs(hw, true)) {
+ printf(": Setup of shared code failed\n");
+ goto err_pci;
+ }
+
+ hw->mac.autoneg = DO_AUTO_NEG;
+ hw->phy.autoneg_wait_to_complete = false;
+ hw->phy.autoneg_advertised = AUTONEG_ADV_DEFAULT;
+
+ /* Copper options. */
+ if (hw->phy.media_type == igc_media_type_copper)
+ hw->phy.mdix = AUTO_ALL_MODES;
+
+ /* Set the max frame size. */
+ sc->hw.mac.max_frame_size = 9234;
+
+ /* Allocate multicast array memory. */
+ sc->mta = mallocarray(ETHER_ADDR_LEN, MAX_NUM_MULTICAST_ADDRESSES,
+ M_DEVBUF, M_NOWAIT);
+ if (sc->mta == NULL) {
+ printf(": Can not allocate multicast setup array\n");
+ goto err_late;
+ }
+
+ /* Check SOL/IDER usage. */
+ if (igc_check_reset_block(hw))
+ printf(": PHY reset is blocked due to SOL/IDER session\n");
+
+ /* Enable Energy Efficient Ethernet. */
+ sc->hw.dev_spec._i225.eee_disable = true;
+
+ igc_reset_hw(hw);
+
+ /* Make sure we have a good EEPROM before we read from it. */
+ if (igc_validate_nvm_checksum(hw) < 0) {
+ /*
+ * Some PCI-E parts fail the first check due to
+ * the link being in sleep state, call it again,
+ * if it fails a second time its a real issue.
+ */
+ if (igc_validate_nvm_checksum(hw) < 0) {
+ printf(": The EEPROM checksum is not valid\n");
+ goto err_late;
+ }
+ }
+
+ /* Copy the permanent MAC address out of the EEPROM. */
+ if (igc_read_mac_addr(hw) < 0) {
+ printf(": EEPROM read error while reading MAC address\n");
+ goto err_late;
+ }
+
+ if (!igc_is_valid_ether_addr(hw->mac.addr)) {
+ printf(": Invalid MAC address\n");
+ goto err_late;
+ }
+
+ memcpy(sc->sc_ac.ac_enaddr, sc->hw.mac.addr, ETHER_ADDR_LEN);
+
+ if (igc_allocate_msix(sc))
+ goto err_late;
+
+ /* Setup OS specific network interface. */
+ igc_setup_interface(sc);
+
+ igc_reset(sc);
+ hw->mac.get_link_status = true;
+ igc_update_link_status(sc);
+
+ /* The driver can now take control from firmware. */
+ igc_get_hw_control(sc);
+
+ printf(", address %s\n", ether_sprintf(sc->hw.mac.addr));
+ return;
+
+err_late:
+ igc_release_hw_control(sc);
+err_pci:
+ igc_free_pci_resources(sc);
+ free(sc->mta, M_DEVBUF, ETHER_ADDR_LEN * MAX_NUM_MULTICAST_ADDRESSES);
+}
+
+/*********************************************************************
+ * Device removal routine
+ *
+ * The detach entry point is called when the driver is being removed.
+ * This routine stops the adapter and deallocates all the resources
+ * that were allocated for driver operation.
+ *
+ * return 0 on success, positive on failure
+ *********************************************************************/
+int
+igc_detach(struct device *self, int flags)
+{
+ struct igc_softc *sc = (struct igc_softc *)self;
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+
+ igc_stop(sc);
+
+ igc_phy_hw_reset(&sc->hw);
+ igc_release_hw_control(sc);
+
+ ether_ifdetach(ifp);
+ if_detach(ifp);
+
+ igc_free_pci_resources(sc);
+
+ igc_free_transmit_structures(sc);
+ igc_free_receive_structures(sc);
+ free(sc->mta, M_DEVBUF, ETHER_ADDR_LEN * MAX_NUM_MULTICAST_ADDRESSES);
+
+ return 0;
+}
+
+void
+igc_identify_hardware(struct igc_softc *sc)
+{
+ struct igc_osdep *os = &sc->osdep;
+ struct pci_attach_args *pa = &os->os_pa;
+
+ /* Save off the information about this board. */
+ sc->hw.device_id = PCI_PRODUCT(pa->pa_id);
+
+ /* Do shared code init and setup. */
+ if (igc_set_mac_type(&sc->hw)) {
+ printf(": Setup init failure\n");
+ return;
+ }
+}
+
+int
+igc_allocate_pci_resources(struct igc_softc *sc)
+{
+ struct igc_osdep *os = &sc->osdep;
+ struct pci_attach_args *pa = &os->os_pa;
+ pcireg_t memtype;
+
+ memtype = pci_mapreg_type(pa->pa_pc, pa->pa_tag, IGC_PCIREG);
+ if (pci_mapreg_map(pa, IGC_PCIREG, memtype, 0, &os->os_memt,
+ &os->os_memh, &os->os_membase, &os->os_memsize, 0)) {
+ printf(": unable to map registers\n");
+ return ENXIO;
+ }
+ sc->hw.hw_addr = (uint8_t *)os->os_membase;
+ sc->hw.back = os;
+
+ igc_setup_msix(sc);
+
+ return 0;
+}
+
+int
+igc_allocate_queues(struct igc_softc *sc)
+{
+ struct igc_queue *iq;
+ struct tx_ring *txr;
+ struct rx_ring *rxr;
+ int i, rsize, rxconf, tsize, txconf;
+
+ /* Allocate the top level queue structs. */
+ sc->queues = mallocarray(sc->sc_nqueues, sizeof(struct igc_queue),
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (sc->queues == NULL) {
+ printf("%s: unable to allocate queue\n", DEVNAME(sc));
+ goto fail;
+ }
+
+ /* Allocate the TX ring. */
+ sc->tx_rings = mallocarray(sc->sc_nqueues, sizeof(struct tx_ring),
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (sc->tx_rings == NULL) {
+ printf("%s: unable to allocate TX ring\n", DEVNAME(sc));
+ goto fail;
+ }
+
+ /* Allocate the RX ring. */
+ sc->rx_rings = mallocarray(sc->sc_nqueues, sizeof(struct rx_ring),
+ M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (sc->rx_rings == NULL) {
+ printf("%s: unable to allocate RX ring\n", DEVNAME(sc));
+ goto rx_fail;
+ }
+
+ txconf = rxconf = 0;
+
+ /* Set up the TX queues. */
+ tsize = roundup2(sc->num_tx_desc * sizeof(union igc_adv_tx_desc),
+ IGC_DBA_ALIGN);
+ for (i = 0; i < sc->sc_nqueues; i++, txconf++) {
+ txr = &sc->tx_rings[i];
+ txr->sc = sc;
+ txr->me = i;
+
+ if (igc_dma_malloc(sc, tsize, &txr->txdma)) {
+ printf("%s: unable to allocate TX descriptor\n",
+ DEVNAME(sc));
+ goto err_tx_desc;
+ }
+ txr->tx_base = (union igc_adv_tx_desc *)txr->txdma.dma_vaddr;
+ bzero((void *)txr->tx_base, tsize);
+ }
+
+ /* Set up the RX queues. */
+ rsize = roundup2(sc->num_rx_desc * sizeof(union igc_adv_rx_desc),
+ IGC_DBA_ALIGN);
+ for (i = 0; i < sc->sc_nqueues; i++, rxconf++) {
+ rxr = &sc->rx_rings[i];
+ rxr->sc = sc;
+ rxr->me = i;
+ timeout_set(&rxr->rx_refill, igc_rxrefill, rxr);
+
+ if (igc_dma_malloc(sc, rsize, &rxr->rxdma)) {
+ printf("%s: unable to allocate RX descriptor\n",
+ DEVNAME(sc));
+ goto err_rx_desc;
+ }
+ rxr->rx_base = (union igc_adv_rx_desc *)rxr->rxdma.dma_vaddr;
+ bzero((void *)rxr->rx_base, rsize);
+ }
+
+ /* Set up the queue holding structs. */
+ for (i = 0; i < sc->sc_nqueues; i++) {
+ iq = &sc->queues[i];
+ iq->sc = sc;
+ iq->txr = &sc->tx_rings[i];
+ iq->rxr = &sc->rx_rings[i];
+ snprintf(iq->name, sizeof(iq->name), "%s:%d", DEVNAME(sc), i);
+ }
+
+ return 0;
+
+err_rx_desc:
+ for (rxr = sc->rx_rings; rxconf > 0; rxr++, rxconf--)
+ igc_dma_free(sc, &rxr->rxdma);
+err_tx_desc:
+ for (txr = sc->tx_rings; txconf > 0; txr++, txconf--)
+ igc_dma_free(sc, &txr->txdma);
+ free(sc->rx_rings, M_DEVBUF, sc->sc_nqueues * sizeof(struct rx_ring));
+ sc->rx_rings = NULL;
+rx_fail:
+ free(sc->tx_rings, M_DEVBUF, sc->sc_nqueues * sizeof(struct tx_ring));
+ sc->tx_rings = NULL;
+fail:
+ return ENOMEM;
+}
+
+void
+igc_free_pci_resources(struct igc_softc *sc)
+{
+ struct igc_osdep *os = &sc->osdep;
+ struct pci_attach_args *pa = &os->os_pa;
+ struct igc_queue *iq = sc->queues;
+ int i;
+
+ /* Release all msix queue resources. */
+ for (i = 0; i < sc->sc_nqueues; i++, iq++) {
+ if (iq->tag)
+ pci_intr_disestablish(pa->pa_pc, iq->tag);
+ iq->tag = NULL;
+ }
+
+ if (sc->tag)
+ pci_intr_disestablish(pa->pa_pc, sc->tag);
+ sc->tag = NULL;
+ if (os->os_membase != 0)
+ bus_space_unmap(os->os_memt, os->os_memh, os->os_memsize);
+ os->os_membase = 0;
+}
+
+/*********************************************************************
+ *
+ * Initialize the hardware to a configuration as specified by the
+ * adapter structure.
+ *
+ **********************************************************************/
+void
+igc_reset(struct igc_softc *sc)
+{
+ struct igc_hw *hw = &sc->hw;
+ uint32_t pba;
+ uint16_t rx_buffer_size;
+
+ /* Let the firmware know the OS is in control */
+ igc_get_hw_control(sc);
+
+ /*
+ * Packet Buffer Allocation (PBA)
+ * Writing PBA sets the receive portion of the buffer
+ * the remainder is used for the transmit buffer.
+ */
+ pba = IGC_PBA_34K;
+
+ /*
+ * These parameters control the automatic generation (Tx) and
+ * response (Rx) to Ethernet PAUSE frames.
+ * - High water mark should allow for at least two frames to be
+ * received after sending an XOFF.
+ * - Low water mark works best when it is very near the high water mark.
+ * This allows the receiver to restart by sending XON when it has
+ * drained a bit. Here we use an arbitrary value of 1500 which will
+ * restart after one full frame is pulled from the buffer. There
+ * could be several smaller frames in the buffer and if so they will
+ * not trigger the XON until their total number reduces the buffer
+ * by 1500.
+ * - The pause time is fairly large at 1000 x 512ns = 512 usec.
+ */
+ rx_buffer_size = (pba & 0xffff) << 10;
+ hw->fc.high_water = rx_buffer_size -
+ roundup2(sc->hw.mac.max_frame_size, 1024);
+ /* 16-byte granularity */
+ hw->fc.low_water = hw->fc.high_water - 16;
+
+ if (sc->fc) /* locally set flow control value? */
+ hw->fc.requested_mode = sc->fc;
+ else
+ hw->fc.requested_mode = igc_fc_full;
+
+ hw->fc.pause_time = IGC_FC_PAUSE_TIME;
+
+ hw->fc.send_xon = true;
+
+ /* Issue a global reset */
+ igc_reset_hw(hw);
+ IGC_WRITE_REG(hw, IGC_WUC, 0);
+
+ /* and a re-init */
+ if (igc_init_hw(hw) < 0) {
+ printf(": Hardware Initialization Failed\n");
+ return;
+ }
+
+ /* Setup DMA Coalescing */
+ igc_init_dmac(sc, pba);
+
+ IGC_WRITE_REG(hw, IGC_VET, ETHERTYPE_VLAN);
+ igc_get_phy_info(hw);
+ igc_check_for_link(hw);
+}
+
+/*********************************************************************
+ *
+ * Initialize the DMA Coalescing feature
+ *
+ **********************************************************************/
+void
+igc_init_dmac(struct igc_softc *sc, uint32_t pba)
+{
+ struct igc_hw *hw = &sc->hw;
+ uint32_t dmac, reg = ~IGC_DMACR_DMAC_EN;
+ uint16_t hwm, max_frame_size;
+ int status;
+
+ max_frame_size = sc->hw.mac.max_frame_size;
+
+ if (sc->dmac == 0) { /* Disabling it */
+ IGC_WRITE_REG(hw, IGC_DMACR, reg);
+ return;
+ } else
+ printf(": DMA Coalescing enabled\n");
+
+ /* Set starting threshold */
+ IGC_WRITE_REG(hw, IGC_DMCTXTH, 0);
+
+ hwm = 64 * pba - max_frame_size / 16;
+ if (hwm < 64 * (pba - 6))
+ hwm = 64 * (pba - 6);
+ reg = IGC_READ_REG(hw, IGC_FCRTC);
+ reg &= ~IGC_FCRTC_RTH_COAL_MASK;
+ reg |= ((hwm << IGC_FCRTC_RTH_COAL_SHIFT)
+ & IGC_FCRTC_RTH_COAL_MASK);
+ IGC_WRITE_REG(hw, IGC_FCRTC, reg);
+
+ dmac = pba - max_frame_size / 512;
+ if (dmac < pba - 10)
+ dmac = pba - 10;
+ reg = IGC_READ_REG(hw, IGC_DMACR);
+ reg &= ~IGC_DMACR_DMACTHR_MASK;
+ reg |= ((dmac << IGC_DMACR_DMACTHR_SHIFT)
+ & IGC_DMACR_DMACTHR_MASK);
+
+ /* transition to L0x or L1 if available..*/
+ reg |= (IGC_DMACR_DMAC_EN | IGC_DMACR_DMAC_LX_MASK);
+
+ /* Check if status is 2.5Gb backplane connection
+ * before configuration of watchdog timer, which is
+ * in msec values in 12.8usec intervals
+ * watchdog timer= msec values in 32usec intervals
+ * for non 2.5Gb connection
+ */
+ status = IGC_READ_REG(hw, IGC_STATUS);
+ if ((status & IGC_STATUS_2P5_SKU) &&
+ (!(status & IGC_STATUS_2P5_SKU_OVER)))
+ reg |= ((sc->dmac * 5) >> 6);
+ else
+ reg |= (sc->dmac >> 5);
+
+ IGC_WRITE_REG(hw, IGC_DMACR, reg);
+
+ IGC_WRITE_REG(hw, IGC_DMCRTRH, 0);
+
+ /* Set the interval before transition */
+ reg = IGC_READ_REG(hw, IGC_DMCTLX);
+ reg |= IGC_DMCTLX_DCFLUSH_DIS;
+
+ /*
+ ** in 2.5Gb connection, TTLX unit is 0.4 usec
+ ** which is 0x4*2 = 0xA. But delay is still 4 usec
+ */
+ status = IGC_READ_REG(hw, IGC_STATUS);
+ if ((status & IGC_STATUS_2P5_SKU) &&
+ (!(status & IGC_STATUS_2P5_SKU_OVER)))
+ reg |= 0xA;
+ else
+ reg |= 0x4;
+
+ IGC_WRITE_REG(hw, IGC_DMCTLX, reg);
+
+ /* free space in tx packet buffer to wake from DMA coal */
+ IGC_WRITE_REG(hw, IGC_DMCTXTH, (IGC_TXPBSIZE -
+ (2 * max_frame_size)) >> 6);
+
+ /* make low power state decision controlled by DMA coal */
+ reg = IGC_READ_REG(hw, IGC_PCIEMISC);
+ reg &= ~IGC_PCIEMISC_LX_DECISION;
+ IGC_WRITE_REG(hw, IGC_PCIEMISC, reg);
+}
+
+int
+igc_allocate_msix(struct igc_softc *sc)
+{
+ struct igc_osdep *os = &sc->osdep;
+ struct pci_attach_args *pa = &os->os_pa;
+ struct igc_queue *iq;
+ pci_intr_handle_t ih;
+ int i, error = 0;
+
+ for (i = 0, iq = sc->queues; i < sc->sc_nqueues; i++, iq++) {
+ if (pci_intr_map_msix(pa, i, &ih)) {
+ printf("%s: unable to map msi-x vector %d\n",
+ DEVNAME(sc), i);
+ error = ENOMEM;
+ goto fail;
+ }
+
+ iq->tag = pci_intr_establish_cpu(pa->pa_pc, ih,
+ IPL_NET | IPL_MPSAFE, intrmap_cpu(sc->sc_intrmap, i),
+ igc_intr_queue, iq, iq->name);
+ if (iq->tag == NULL) {
+ printf("%s: unable to establish interrupt %d\n",
+ DEVNAME(sc), i);
+ error = ENOMEM;
+ goto fail;
+ }
+
+ iq->msix = i;
+ iq->eims = 1 << i;
+ }
+
+ /* Now the link status/control last MSI-X vector. */
+ if (pci_intr_map_msix(pa, i, &ih)) {
+ printf("%s: unable to map link vector\n", DEVNAME(sc));
+ error = ENOMEM;
+ goto fail;
+ }
+
+ sc->tag = pci_intr_establish(pa->pa_pc, ih, IPL_NET | IPL_MPSAFE,
+ igc_intr_link, sc, sc->sc_dev.dv_xname);
+ if (sc->tag == NULL) {
+ printf("%s: unable to establish link interrupt\n", DEVNAME(sc));
+ error = ENOMEM;
+ goto fail;
+ }
+
+ sc->linkvec = i;
+ printf(", %s, %d queue%s", pci_intr_string(pa->pa_pc, ih),
+ i, (i > 1) ? "s" : "");
+
+ return 0;
+fail:
+ for (iq = sc->queues; i > 0; i--, iq++) {
+ if (iq->tag == NULL)
+ continue;
+ pci_intr_disestablish(pa->pa_pc, iq->tag);
+ iq->tag = NULL;
+ }
+
+ return error;
+}
+
+void
+igc_setup_msix(struct igc_softc *sc)
+{
+ struct igc_osdep *os = &sc->osdep;
+ struct pci_attach_args *pa = &os->os_pa;
+ int nmsix;
+
+ nmsix = pci_intr_msix_count(pa);
+ if (nmsix <= 1)
+ printf(": not enough msi-x vectors\n");
+
+ /* Give one vector to events. */
+ nmsix--;
+
+ sc->sc_intrmap = intrmap_create(&sc->sc_dev, nmsix, IGC_MAX_VECTORS,
+ INTRMAP_POWEROF2);
+ sc->sc_nqueues = intrmap_count(sc->sc_intrmap);
+}
+
+int
+igc_dma_malloc(struct igc_softc *sc, bus_size_t size, struct igc_dma_alloc *dma)
+{
+ struct igc_osdep *os = &sc->osdep;
+
+ dma->dma_tag = os->os_pa.pa_dmat;
+
+ if (bus_dmamap_create(dma->dma_tag, size, 1, size, 0, BUS_DMA_NOWAIT,
+ &dma->dma_map))
+ return 1;
+ if (bus_dmamem_alloc(dma->dma_tag, size, PAGE_SIZE, 0, &dma->dma_seg,
+ 1, &dma->dma_nseg, BUS_DMA_NOWAIT))
+ goto destroy;
+ if (bus_dmamem_map(dma->dma_tag, &dma->dma_seg, dma->dma_nseg, size,
+ &dma->dma_vaddr, BUS_DMA_NOWAIT))
+ goto free;
+ if (bus_dmamap_load(dma->dma_tag, dma->dma_map, dma->dma_vaddr, size,
+ NULL, BUS_DMA_NOWAIT))
+ goto unmap;
+
+ dma->dma_size = size;
+
+ return 0;
+unmap:
+ bus_dmamem_unmap(dma->dma_tag, dma->dma_vaddr, size);
+free:
+ bus_dmamem_free(dma->dma_tag, &dma->dma_seg, dma->dma_nseg);
+destroy:
+ bus_dmamap_destroy(dma->dma_tag, dma->dma_map);
+ dma->dma_map = NULL;
+ dma->dma_tag = NULL;
+ return 1;
+}
+
+void
+igc_dma_free(struct igc_softc *sc, struct igc_dma_alloc *dma)
+{
+ if (dma->dma_tag == NULL)
+ return;
+
+ if (dma->dma_map != NULL) {
+ bus_dmamap_sync(dma->dma_tag, dma->dma_map, 0,
+ dma->dma_map->dm_mapsize,
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(dma->dma_tag, dma->dma_map);
+ bus_dmamem_unmap(dma->dma_tag, dma->dma_vaddr, dma->dma_size);
+ bus_dmamem_free(dma->dma_tag, &dma->dma_seg, dma->dma_nseg);
+ bus_dmamap_destroy(dma->dma_tag, dma->dma_map);
+ dma->dma_map = NULL;
+ }
+}
+
+/*********************************************************************
+ *
+ * Setup networking device structure and register an interface.
+ *
+ **********************************************************************/
+void
+igc_setup_interface(struct igc_softc *sc)
+{
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+ int i;
+
+ ifp->if_softc = sc;
+ strlcpy(ifp->if_xname, DEVNAME(sc), IFNAMSIZ);
+ ifp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST;
+ ifp->if_xflags = IFXF_MPSAFE;
+ ifp->if_ioctl = igc_ioctl;
+ ifp->if_qstart = igc_start;
+ ifp->if_watchdog = igc_watchdog;
+ ifp->if_hardmtu = sc->hw.mac.max_frame_size - ETHER_HDR_LEN -
+ ETHER_CRC_LEN;
+ ifq_set_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;
+
+ /* Initialize ifmedia structures. */
+ ifmedia_init(&sc->media, IFM_IMASK, igc_media_change, igc_media_status);
+ ifmedia_add(&sc->media, IFM_ETHER | IFM_10_T, 0, NULL);
+ ifmedia_add(&sc->media, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL);
+ ifmedia_add(&sc->media, IFM_ETHER | IFM_100_TX, 0, NULL);
+ ifmedia_add(&sc->media, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL);
+ ifmedia_add(&sc->media, IFM_ETHER | IFM_1000_T | IFM_FDX, 0, NULL);
+ ifmedia_add(&sc->media, IFM_ETHER | IFM_1000_T, 0, NULL);
+ ifmedia_add(&sc->media, IFM_ETHER | IFM_2500_T, 0, NULL);
+
+ ifmedia_add(&sc->media, IFM_ETHER | IFM_AUTO, 0, NULL);
+ ifmedia_set(&sc->media, IFM_ETHER | IFM_AUTO);
+
+ if_attach(ifp);
+ ether_ifattach(ifp);
+
+ if_attach_queues(ifp, sc->sc_nqueues);
+ if_attach_iqueues(ifp, sc->sc_nqueues);
+ for (i = 0; i < sc->sc_nqueues; i++) {
+ struct ifqueue *ifq = ifp->if_ifqs[i];
+ struct ifiqueue *ifiq = ifp->if_iqs[i];
+ struct tx_ring *txr = &sc->tx_rings[i];
+ struct rx_ring *rxr = &sc->rx_rings[i];
+
+ ifq->ifq_softc = txr;
+ txr->ifq = ifq;
+
+ ifiq->ifiq_softc = rxr;
+ rxr->ifiq = ifiq;
+ }
+}
+
+void
+igc_init(void *arg)
+{
+ struct igc_softc *sc = (struct igc_softc *)arg;
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+ uint32_t ctrl = 0;
+ int i, s;
+
+ s = splnet();
+
+ igc_stop(sc);
+
+ /* Get the latest mac address, user can use a LAA. */
+ bcopy(sc->sc_ac.ac_enaddr, sc->hw.mac.addr, ETHER_ADDR_LEN);
+
+ /* Put the address into the receive address array. */
+ igc_rar_set(&sc->hw, sc->hw.mac.addr, 0);
+
+ /* Initialize the hardware. */
+ igc_reset(sc);
+ igc_update_link_status(sc);
+
+ /* Setup VLAN support, basic and offload if available. */
+ IGC_WRITE_REG(&sc->hw, IGC_VET, ETHERTYPE_VLAN);
+
+ /* Prepare transmit descriptors and buffers. */
+ if (igc_setup_transmit_structures(sc)) {
+ printf("%s: Could not setup transmit structures\n",
+ DEVNAME(sc));
+ igc_stop(sc);
+ splx(s);
+ return;
+ }
+ igc_initialize_transmit_unit(sc);
+
+ sc->rx_mbuf_sz = MCLBYTES + ETHER_ALIGN;
+ /* Prepare receive descriptors and buffers. */
+ if (igc_setup_receive_structures(sc)) {
+ printf("%s: Could not setup receive structures\n",
+ DEVNAME(sc));
+ igc_stop(sc);
+ splx(s);
+ return;
+ }
+ igc_initialize_receive_unit(sc);
+
+ if (ifp->if_capabilities & IFCAP_VLAN_HWTAGGING) {
+ ctrl = IGC_READ_REG(&sc->hw, IGC_CTRL);
+ ctrl |= IGC_CTRL_VME;
+ IGC_WRITE_REG(&sc->hw, IGC_CTRL, ctrl);
+ }
+
+ /* Setup multicast table. */
+ igc_iff(sc);
+
+ igc_clear_hw_cntrs_base_generic(&sc->hw);
+
+ igc_configure_queues(sc);
+
+ /* This clears any pending interrupts */
+ IGC_READ_REG(&sc->hw, IGC_ICR);
+ IGC_WRITE_REG(&sc->hw, IGC_ICS, IGC_ICS_LSC);
+
+ /* The driver can now take control from firmware. */
+ igc_get_hw_control(sc);
+
+ /* Set Energy Efficient Ethernet. */
+ igc_set_eee_i225(&sc->hw, true, true, true);
+
+ igc_enable_intr(sc);
+
+ ifp->if_flags |= IFF_RUNNING;
+ for (i = 0; i < sc->sc_nqueues; i++)
+ ifq_clr_oactive(ifp->if_ifqs[i]);
+
+ splx(s);
+}
+
+void
+igc_start(struct ifqueue *ifq)
+{
+ struct ifnet *ifp = ifq->ifq_if;
+ struct igc_softc *sc = ifp->if_softc;
+
+ if (!sc->link_active)
+ return;
+}
+
+/*********************************************************************
+ *
+ * This routine disables all traffic on the adapter by issuing a
+ * global reset on the MAC.
+ *
+ **********************************************************************/
+void
+igc_stop(struct igc_softc *sc)
+{
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+ int i;
+
+ /* Tell the stack that the interface is no longer active. */
+ ifp->if_flags &= ~IFF_RUNNING;
+
+ igc_disable_intr(sc);
+
+ igc_reset_hw(&sc->hw);
+ IGC_WRITE_REG(&sc->hw, IGC_WUC, 0);
+
+ intr_barrier(sc->tag);
+ for (i = 0; i < sc->sc_nqueues; 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);
+ }
+
+ igc_free_transmit_structures(sc);
+ igc_free_receive_structures(sc);
+
+ igc_update_link_status(sc);
+}
+
+/*********************************************************************
+ * Ioctl entry point
+ *
+ * igc_ioctl is called when the user wants to configure the
+ * interface.
+ *
+ * return 0 on success, positive on failure
+ **********************************************************************/
+int
+igc_ioctl(struct ifnet * ifp, u_long cmd, caddr_t data)
+{
+ struct igc_softc *sc = ifp->if_softc;
+ struct ifreq *ifr = (struct ifreq *)data;
+ int s, error = 0;
+
+ s = splnet();
+
+ switch (cmd) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ if (!(ifp->if_flags & IFF_RUNNING))
+ igc_init(sc);
+ break;
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_UP) {
+ if (ifp->if_flags & IFF_RUNNING)
+ error = ENETRESET;
+ else
+ igc_init(sc);
+ } else {
+ if (ifp->if_flags & IFF_RUNNING)
+ igc_stop(sc);
+ }
+ break;
+ case SIOCSIFMEDIA:
+ case SIOCGIFMEDIA:
+ error = ifmedia_ioctl(ifp, ifr, &sc->media, cmd);
+ break;
+ case SIOCGIFRXR:
+ error = igc_rxrinfo(sc, (struct if_rxrinfo *)ifr->ifr_data);
+ break;
+ default:
+ error = ether_ioctl(ifp, &sc->sc_ac, cmd, data);
+ }
+
+ if (error == ENETRESET) {
+ if (ifp->if_flags & IFF_RUNNING) {
+ igc_disable_intr(sc);
+ igc_iff(sc);
+ igc_enable_intr(sc);
+ }
+ error = 0;
+ }
+
+ splx(s);
+ return error;
+}
+
+int
+igc_rxrinfo(struct igc_softc *sc, struct if_rxrinfo *ifri)
+{
+ struct if_rxring_info *ifr, ifr1;
+ struct rx_ring *rxr;
+ int error, i, n = 0;
+
+ if (sc->sc_nqueues > 1) {
+ if ((ifr = mallocarray(sc->sc_nqueues, sizeof(*ifr), M_DEVBUF,
+ M_WAITOK | M_ZERO)) == NULL)
+ return ENOMEM;
+ } else
+ ifr = &ifr1;
+
+ for (i = 0; i < sc->sc_nqueues; i++) {
+ rxr = &sc->rx_rings[i];
+ ifr[n].ifr_size = MCLBYTES;
+ snprintf(ifr[n].ifr_name, sizeof(ifr[n].ifr_name), "%d", i);
+ ifr[n].ifr_info = rxr->rx_ring;
+ n++;
+ }
+
+ error = if_rxr_info_ioctl(ifri, sc->sc_nqueues, ifr);
+ if (sc->sc_nqueues > 1)
+ free(ifr, M_DEVBUF, sc->sc_nqueues * sizeof(*ifr));
+
+ return error;
+}
+
+int
+igc_rxfill(struct rx_ring *rxr)
+{
+ struct igc_softc *sc = rxr->sc;
+ int i, post = 0;
+ u_int slots;
+
+ bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map, 0,
+ rxr->rxdma.dma_map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
+
+ i = rxr->last_desc_filled;
+ for (slots = if_rxr_get(&rxr->rx_ring, sc->num_rx_desc); slots > 0;
+ slots--) {
+ if (++i == sc->num_rx_desc)
+ i = 0;
+
+ if (igc_get_buf(rxr, i) != 0)
+ break;
+
+ rxr->last_desc_filled = i;
+ post = 1;
+ }
+
+ bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map, 0,
+ rxr->rxdma.dma_map->dm_mapsize, BUS_DMASYNC_PREWRITE);
+
+ if_rxr_put(&rxr->rx_ring, slots);
+
+ return post;
+}
+
+void
+igc_rxrefill(void *xrxr)
+{
+ struct rx_ring *rxr = xrxr;
+ struct igc_softc *sc = rxr->sc;
+
+ if (igc_rxfill(rxr))
+ IGC_WRITE_REG(&sc->hw, IGC_RDT(rxr->me), rxr->last_desc_filled);
+ else if (if_rxr_inuse(&rxr->rx_ring) == 0)
+ timeout_add(&rxr->rx_refill, 1);
+}
+
+/*********************************************************************
+ *
+ * This routine executes in interrupt context. It replenishes
+ * the mbufs in the descriptor and sends data which has been
+ * dma'ed into host memory to upper layer.
+ *
+ *********************************************************************/
+int
+igc_rxeof(struct rx_ring *rxr)
+{
+ struct igc_softc *sc = rxr->sc;
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+ struct mbuf_list ml = MBUF_LIST_INITIALIZER();
+ struct mbuf *mp, *m;
+ struct igc_rx_buf *rxbuf, *nxbuf;
+ union igc_adv_rx_desc *rxdesc;
+ uint32_t ptype, staterr = 0;
+ uint16_t len, vtag;
+ uint8_t eop = 0;
+ int i;
+
+ if (!ISSET(ifp->if_flags, IFF_RUNNING))
+ return 0;
+
+ i = rxr->next_to_check;
+ while (if_rxr_inuse(&rxr->rx_ring) > 0) {
+ uint32_t hash;
+ uint16_t hashtype;
+
+ bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map,
+ i * sizeof(union igc_adv_rx_desc),
+ sizeof(union igc_adv_rx_desc), BUS_DMASYNC_POSTREAD);
+
+ rxdesc = &rxr->rx_base[i];
+ staterr = letoh32(rxdesc->wb.upper.status_error);
+ if (!ISSET(staterr, IGC_RXD_STAT_DD)) {
+ bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map,
+ i * sizeof(union igc_adv_rx_desc),
+ sizeof(union igc_adv_rx_desc), BUS_DMASYNC_PREREAD);
+ break;
+ }
+
+ /* Zero out the receive descriptors status. */
+ rxdesc->wb.upper.status_error = 0;
+ rxbuf = &rxr->rx_buffers[i];
+
+ /* Pull the mbuf off the ring. */
+ bus_dmamap_sync(rxr->rxdma.dma_tag, rxbuf->map, 0,
+ rxbuf->map->dm_mapsize, BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(rxr->rxdma.dma_tag, rxbuf->map);
+
+ mp = rxbuf->buf;
+ len = letoh16(rxdesc->wb.upper.length);
+ vtag = letoh16(rxdesc->wb.upper.vlan);
+ eop = ((staterr & IGC_RXD_STAT_EOP) == IGC_RXD_STAT_EOP);
+ ptype = letoh32(rxdesc->wb.lower.lo_dword.data) &
+ IGC_PKTTYPE_MASK;
+ hash = letoh32(rxdesc->wb.lower.hi_dword.rss);
+ hashtype = le16toh(rxdesc->wb.lower.lo_dword.hs_rss.pkt_info) &
+ IGC_RXDADV_RSSTYPE_MASK;
+
+ if (staterr & IGC_RXDEXT_STATERR_RXE) {
+ if (rxbuf->fmp) {
+ m_freem(rxbuf->fmp);
+ rxbuf->fmp = NULL;
+ }
+
+ m_freem(mp);
+ rxbuf->buf = NULL;
+ goto next_desc;
+ }
+
+ if (mp == NULL) {
+ panic("%s: igc_rxeof: NULL mbuf in slot %d "
+ "(nrx %d, filled %d)", DEVNAME(sc), i,
+ if_rxr_inuse(&rxr->rx_ring), rxr->last_desc_filled);
+ }
+
+ mp->m_len = len;
+
+ m = rxbuf->fmp;
+ rxbuf->buf = rxbuf->fmp = NULL;
+
+ if (m != NULL)
+ m->m_pkthdr.len += mp->m_len;
+ else {
+ m = mp;
+ m->m_pkthdr.len = mp->m_len;
+#if NVLAN > 0
+ if (staterr & IGC_RXD_STAT_VP) {
+ m->m_pkthdr.ether_vtag = vtag;
+ m->m_flags |= M_VLANTAG;
+ }
+#endif
+ }
+
+ /* Pass the head pointer on */
+ if (eop == 0) {
+ nxbuf->fmp = m;
+ m = NULL;
+ mp->m_next = nxbuf->buf;
+ } else {
+ igc_rx_checksum(staterr, m, ptype);
+
+ if (hashtype != IGC_RXDADV_RSSTYPE_NONE) {
+ m->m_pkthdr.ph_flowid = hash;
+ SET(m->m_pkthdr.csum_flags, M_FLOWID);
+ }
+
+ ml_enqueue(&ml, m);
+ }
+next_desc:
+ if_rxr_put(&rxr->rx_ring, 1);
+ bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map,
+ i * sizeof(union igc_adv_rx_desc),
+ sizeof(union igc_adv_rx_desc), BUS_DMASYNC_PREREAD);
+
+ /* Advance our pointers to the next descriptor. */
+ if (++i == sc->num_rx_desc)
+ i = 0;
+ }
+ rxr->next_to_check = i;
+
+ if (ifiq_input(rxr->ifiq, &ml))
+ if_rxr_livelocked(&rxr->rx_ring);
+
+ if (!(staterr & IGC_RXD_STAT_DD))
+ return 0;
+
+ return 1;
+}
+
+/*********************************************************************
+ *
+ * Verify that the hardware indicated that the checksum is valid.
+ * Inform the stack about the status of checksum so that stack
+ * doesn't spend time verifying the checksum.
+ *
+ *********************************************************************/
+void
+igc_rx_checksum(uint32_t staterr, struct mbuf *m, uint32_t ptype)
+{
+ uint16_t status = (uint16_t)staterr;
+ uint8_t errors = (uint8_t)(staterr >> 24);
+
+ if (status & IGC_RXD_STAT_IPCS) {
+ if (!(errors & IGC_RXD_ERR_IPE)) {
+ /* IP Checksum Good */
+ m->m_pkthdr.csum_flags = M_IPV4_CSUM_IN_OK;
+ } else
+ m->m_pkthdr.csum_flags = 0;
+ }
+
+ if (status & (IGC_RXD_STAT_TCPCS | IGC_RXD_STAT_UDPCS)) {
+ if (!(errors & IGC_RXD_ERR_TCPE))
+ m->m_pkthdr.csum_flags |=
+ M_TCP_CSUM_IN_OK | M_UDP_CSUM_IN_OK;
+ }
+}
+
+void
+igc_watchdog(struct ifnet * ifp)
+{
+}
+
+/*********************************************************************
+ *
+ * Media Ioctl callback
+ *
+ * This routine is called whenever the user queries the status of
+ * the interface using ifconfig.
+ *
+ **********************************************************************/
+void
+igc_media_status(struct ifnet *ifp, struct ifmediareq *ifmr)
+{
+ struct igc_softc *sc = ifp->if_softc;
+
+ igc_update_link_status(sc);
+
+ ifmr->ifm_status = IFM_AVALID;
+ ifmr->ifm_active = IFM_ETHER;
+
+ if (!sc->link_active) {
+ ifmr->ifm_active |= IFM_NONE;
+ return;
+ }
+
+ ifmr->ifm_status |= IFM_ACTIVE;
+
+ switch (sc->link_speed) {
+ case 10:
+ ifmr->ifm_active |= IFM_10_T;
+ break;
+ case 100:
+ ifmr->ifm_active |= IFM_100_TX;
+ break;
+ case 1000:
+ ifmr->ifm_active |= IFM_1000_T;
+ break;
+ case 2500:
+ ifmr->ifm_active |= IFM_2500_T;
+ break;
+ }
+
+ if (sc->link_duplex == FULL_DUPLEX)
+ ifmr->ifm_active |= IFM_FDX;
+ else
+ ifmr->ifm_active |= IFM_HDX;
+}
+
+/*********************************************************************
+ *
+ * Media Ioctl callback
+ *
+ * This routine is called when the user changes speed/duplex using
+ * media/mediopt option with ifconfig.
+ *
+ **********************************************************************/
+int
+igc_media_change(struct ifnet *ifp)
+{
+ struct igc_softc *sc = ifp->if_softc;
+ struct ifmedia *ifm = &sc->media;
+
+ if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER)
+ return (EINVAL);
+
+ sc->hw.mac.autoneg = DO_AUTO_NEG;
+
+ switch (IFM_SUBTYPE(ifm->ifm_media)) {
+ case IFM_AUTO:
+ sc->hw.phy.autoneg_advertised = AUTONEG_ADV_DEFAULT;
+ break;
+ case IFM_2500_T:
+ sc->hw.phy.autoneg_advertised = ADVERTISE_2500_FULL;
+ break;
+ case IFM_1000_T:
+ sc->hw.phy.autoneg_advertised = ADVERTISE_1000_FULL;
+ break;
+ case IFM_100_TX:
+ if ((ifm->ifm_media & IFM_GMASK) == IFM_HDX)
+ sc->hw.phy.autoneg_advertised = ADVERTISE_100_HALF;
+ else
+ sc->hw.phy.autoneg_advertised = ADVERTISE_100_FULL;
+ break;
+ case IFM_10_T:
+ if ((ifm->ifm_media & IFM_GMASK) == IFM_HDX)
+ sc->hw.phy.autoneg_advertised = ADVERTISE_10_HALF;
+ else
+ sc->hw.phy.autoneg_advertised = ADVERTISE_10_FULL;
+ break;
+ default:
+ return EINVAL;
+ }
+
+ igc_init(sc);
+
+ return 0;
+}
+
+void
+igc_iff(struct igc_softc *sc)
+{
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+ struct arpcom *ac = &sc->sc_ac;
+ struct ether_multi *enm;
+ struct ether_multistep step;
+ uint32_t reg_rctl = 0;
+ uint8_t *mta;
+ int mcnt = 0;
+
+ mta = sc->mta;
+ bzero(mta, sizeof(uint8_t) * ETHER_ADDR_LEN *
+ MAX_NUM_MULTICAST_ADDRESSES);
+
+ reg_rctl = IGC_READ_REG(&sc->hw, IGC_RCTL);
+ reg_rctl &= ~(IGC_RCTL_UPE | IGC_RCTL_MPE);
+ ifp->if_flags &= ~IFF_ALLMULTI;
+
+ if (ifp->if_flags & IFF_PROMISC || ac->ac_multirangecnt > 0 ||
+ ac->ac_multicnt > MAX_NUM_MULTICAST_ADDRESSES) {
+ ifp->if_flags |= IFF_ALLMULTI;
+ reg_rctl |= IGC_RCTL_MPE;
+ if (ifp->if_flags & IFF_PROMISC)
+ reg_rctl |= IGC_RCTL_UPE;
+ } else {
+ ETHER_FIRST_MULTI(step, ac, enm);
+ while (enm != NULL) {
+ bcopy(enm->enm_addrlo,
+ &mta[mcnt * ETHER_ADDR_LEN], ETHER_ADDR_LEN);
+ mcnt++;
+
+ ETHER_NEXT_MULTI(step, enm);
+ }
+
+ igc_update_mc_addr_list(&sc->hw, mta, mcnt);
+ }
+
+ IGC_WRITE_REG(&sc->hw, IGC_RCTL, reg_rctl);
+}
+
+void
+igc_update_link_status(struct igc_softc *sc)
+{
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+ struct igc_hw *hw = &sc->hw;
+ int link_state;
+
+ if (IGC_READ_REG(&sc->hw, IGC_STATUS) & IGC_STATUS_LU) {
+ if (sc->link_active == 0) {
+ igc_get_speed_and_duplex(hw, &sc->link_speed,
+ &sc->link_duplex);
+ sc->link_active = 1;
+ ifp->if_baudrate = IF_Mbps(sc->link_speed);
+ }
+ link_state = (sc->link_duplex == FULL_DUPLEX) ?
+ LINK_STATE_FULL_DUPLEX : LINK_STATE_HALF_DUPLEX;
+ } else {
+ if (sc->link_active == 1) {
+ ifp->if_baudrate = sc->link_speed = 0;
+ sc->link_duplex = 0;
+ sc->link_active = 0;
+ }
+ link_state = LINK_STATE_DOWN;
+ }
+ if (ifp->if_link_state != link_state) {
+ ifp->if_link_state = link_state;
+ if_link_state_change(ifp);
+ }
+}
+
+/*********************************************************************
+ *
+ * Get a buffer from system mbuf buffer pool.
+ *
+ **********************************************************************/
+int
+igc_get_buf(struct rx_ring *rxr, int i)
+{
+ struct igc_softc *sc = rxr->sc;
+ struct igc_rx_buf *rxbuf;
+ struct mbuf *m;
+ union igc_adv_rx_desc *rxdesc;
+ int error;
+
+ rxbuf = &rxr->rx_buffers[i];
+ rxdesc = &rxr->rx_base[i];
+ if (rxbuf->buf) {
+ printf("%s: slot %d already has an mbuf\n", DEVNAME(sc), i);
+ return ENOBUFS;
+ }
+
+ m = MCLGETL(NULL, M_DONTWAIT, sc->rx_mbuf_sz);
+ if (!m)
+ return ENOBUFS;
+
+ m->m_data += (m->m_ext.ext_size - sc->rx_mbuf_sz);
+ m->m_len = m->m_pkthdr.len = sc->rx_mbuf_sz;
+
+ error = bus_dmamap_load_mbuf(rxr->rxdma.dma_tag, rxbuf->map, m,
+ BUS_DMA_NOWAIT);
+ if (error) {
+ m_freem(m);
+ return error;
+ }
+
+ bus_dmamap_sync(rxr->rxdma.dma_tag, rxbuf->map, 0,
+ rxbuf->map->dm_mapsize, BUS_DMASYNC_PREREAD);
+ rxbuf->buf = m;
+
+ rxdesc->read.pkt_addr = htole64(rxbuf->map->dm_segs[0].ds_addr);
+
+ return 0;
+}
+
+void
+igc_configure_queues(struct igc_softc *sc)
+{
+ struct igc_hw *hw = &sc->hw;
+ struct igc_queue *iq = sc->queues;
+ uint32_t ivar, newitr = 0;
+ int i;
+
+ /* First turn on RSS capability */
+ IGC_WRITE_REG(hw, IGC_GPIE, IGC_GPIE_MSIX_MODE | IGC_GPIE_EIAME |
+ IGC_GPIE_PBA | IGC_GPIE_NSICR);
+
+ /* Set the starting interrupt rate */
+ newitr = (4000000 / MAX_INTS_PER_SEC) & 0x7FFC;
+
+ newitr |= IGC_EITR_CNT_IGNR;
+
+ /* Turn on MSI-X */
+ for (i = 0; i < sc->sc_nqueues; i++, iq++) {
+ /* RX entries */
+ igc_set_queues(sc, i, iq->msix, 0);
+ /* TX entries */
+ igc_set_queues(sc, i, iq->msix, 1);
+ sc->msix_queuesmask |= iq->eims;
+ IGC_WRITE_REG(hw, IGC_EITR(iq->msix), newitr);
+ }
+
+ /* And for the link interrupt */
+ ivar = (sc->linkvec | IGC_IVAR_VALID) << 8;
+ sc->msix_linkmask = 1 << sc->linkvec;
+ IGC_WRITE_REG(hw, IGC_IVAR_MISC, ivar);
+}
+
+void
+igc_set_queues(struct igc_softc *sc, uint32_t entry, uint32_t vector, int type)
+{
+ struct igc_hw *hw = &sc->hw;
+ uint32_t ivar, index;
+
+ index = entry >> 1;
+ ivar = IGC_READ_REG_ARRAY(hw, IGC_IVAR0, index);
+ if (type) {
+ if (entry & 1) {
+ ivar &= 0x00FFFFFF;
+ ivar |= (vector | IGC_IVAR_VALID) << 24;
+ } else {
+ ivar &= 0xFFFF00FF;
+ ivar |= (vector | IGC_IVAR_VALID) << 8;
+ }
+ } else {
+ if (entry & 1) {
+ ivar &= 0xFF00FFFF;
+ ivar |= (vector | IGC_IVAR_VALID) << 16;
+ } else {
+ ivar &= 0xFFFFFF00;
+ ivar |= vector | IGC_IVAR_VALID;
+ }
+ }
+ IGC_WRITE_REG_ARRAY(hw, IGC_IVAR0, index, ivar);
+}
+
+void
+igc_enable_queue(struct igc_softc *sc, uint32_t eims)
+{
+ IGC_WRITE_REG(&sc->hw, IGC_EIMS, eims);
+}
+
+void
+igc_enable_intr(struct igc_softc *sc)
+{
+ struct igc_hw *hw = &sc->hw;
+ uint32_t mask;
+
+ mask = (sc->msix_queuesmask | sc->msix_linkmask);
+ IGC_WRITE_REG(hw, IGC_EIAC, mask);
+ IGC_WRITE_REG(hw, IGC_EIAM, mask);
+ IGC_WRITE_REG(hw, IGC_EIMS, mask);
+ IGC_WRITE_REG(hw, IGC_IMS, IGC_IMS_LSC);
+ IGC_WRITE_FLUSH(hw);
+}
+
+void
+igc_disable_intr(struct igc_softc *sc)
+{
+ struct igc_hw *hw = &sc->hw;
+
+ IGC_WRITE_REG(hw, IGC_EIMC, 0xffffffff);
+ IGC_WRITE_REG(hw, IGC_EIAC, 0);
+ IGC_WRITE_REG(hw, IGC_IMC, 0xffffffff);
+ IGC_WRITE_FLUSH(hw);
+}
+
+int
+igc_intr_link(void *arg)
+{
+ struct igc_softc *sc = (struct igc_softc *)arg;
+ uint32_t reg_icr;
+
+ if (reg_icr & IGC_ICR_LSC) {
+ KERNEL_LOCK();
+ sc->hw.mac.get_link_status = true;
+ igc_update_link_status(sc);
+ KERNEL_UNLOCK();
+ }
+
+ IGC_WRITE_REG(&sc->hw, IGC_IMS, IGC_IMS_LSC);
+ IGC_WRITE_REG(&sc->hw, IGC_EIMS, sc->msix_linkmask);
+
+ return 1;
+}
+
+int
+igc_intr_queue(void *arg)
+{
+ struct igc_queue *iq = arg;
+ struct igc_softc *sc = iq->sc;
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+ struct rx_ring *rxr = iq->rxr;
+
+ if (ifp->if_flags & IFF_RUNNING) {
+ igc_rxeof(rxr);
+ igc_rxrefill(rxr);
+ }
+
+ igc_enable_queue(sc, iq->eims);
+
+ return 1;
+}
+
+/*********************************************************************
+ *
+ * Allocate memory for tx_buffer structures. The tx_buffer stores all
+ * the information needed to transmit a packet on the wire.
+ *
+ **********************************************************************/
+int
+igc_allocate_transmit_buffers(struct tx_ring *txr)
+{
+ struct igc_softc *sc = txr->sc;
+ struct igc_tx_buf *txbuf;
+ int error, i;
+
+ txr->tx_buffers = mallocarray(sc->num_tx_desc,
+ sizeof(struct igc_tx_buf), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (txr->tx_buffers == NULL) {
+ printf("%s: Unable to allocate tx_buffer memory\n",
+ DEVNAME(sc));
+ error = ENOMEM;
+ goto fail;
+ }
+ txr->txtag = txr->txdma.dma_tag;
+
+ /* Create the descriptor buffer dma maps. */
+ for (i = 0; i < sc->num_tx_desc; i++) {
+ txbuf = &txr->tx_buffers[i];
+ error = bus_dmamap_create(txr->txdma.dma_tag, IGC_TSO_SIZE,
+ IGC_MAX_SCATTER, PAGE_SIZE, 0, BUS_DMA_NOWAIT, &txbuf->map);
+ if (error != 0) {
+ printf("%s: Unable to create TX DMA map\n",
+ DEVNAME(sc));
+ goto fail;
+ }
+ }
+
+ return 0;
+fail:
+ return error;
+}
+
+
+/*********************************************************************
+ *
+ * Allocate and initialize transmit structures.
+ *
+ **********************************************************************/
+int
+igc_setup_transmit_structures(struct igc_softc *sc)
+{
+ struct tx_ring *txr = sc->tx_rings;
+ int i;
+
+ for (i = 0; i < sc->sc_nqueues; i++, txr++) {
+ if (igc_setup_transmit_ring(txr))
+ goto fail;
+ }
+
+ return 0;
+fail:
+ igc_free_transmit_structures(sc);
+ return ENOBUFS;
+}
+
+/*********************************************************************
+ *
+ * Initialize a transmit ring.
+ *
+ **********************************************************************/
+int
+igc_setup_transmit_ring(struct tx_ring *txr)
+{
+ struct igc_softc *sc = txr->sc;
+
+ /* Now allocate transmit buffers for the ring. */
+ if (igc_allocate_transmit_buffers(txr))
+ return ENOMEM;
+
+ /* Clear the old ring contents */
+ bzero((void *)txr->tx_base,
+ (sizeof(union igc_adv_tx_desc)) * sc->num_tx_desc);
+
+ /* Reset indices. */
+ txr->next_avail_desc = 0;
+ txr->next_to_clean = 0;
+
+ bus_dmamap_sync(txr->txdma.dma_tag, txr->txdma.dma_map, 0,
+ txr->txdma.dma_map->dm_mapsize,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+
+ return 0;
+}
+
+/*********************************************************************
+ *
+ * Enable transmit unit.
+ *
+ **********************************************************************/
+void
+igc_initialize_transmit_unit(struct igc_softc *sc)
+{
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+ struct tx_ring *txr;
+ struct igc_hw *hw = &sc->hw;
+ uint64_t bus_addr;
+ uint32_t tctl, txdctl = 0;
+ int i;
+
+ /* Setup the Base and Length of the TX descriptor ring. */
+ for (i = 0; i < sc->sc_nqueues; i++) {
+ txr = &sc->tx_rings[i];
+
+ bus_addr = txr->txdma.dma_map->dm_segs[0].ds_addr;
+
+ /* Base and len of TX ring */
+ IGC_WRITE_REG(hw, IGC_TDLEN(i),
+ sc->num_tx_desc * sizeof(union igc_adv_tx_desc));
+ IGC_WRITE_REG(hw, IGC_TDBAH(i), (uint32_t)(bus_addr >> 32));
+ IGC_WRITE_REG(hw, IGC_TDBAL(i), (uint32_t)bus_addr);
+
+ /* Init the HEAD/TAIL indices */
+ IGC_WRITE_REG(hw, IGC_TDT(i), 0);
+ IGC_WRITE_REG(hw, IGC_TDH(i), 0);
+
+ txr->watchdog_timer = 0;
+
+ txdctl = 0; /* Clear txdctl */
+ txdctl |= 0x1f; /* PTHRESH */
+ txdctl |= 1 << 8; /* HTHRESH */
+ txdctl |= 1 << 16; /* WTHRESH */
+ txdctl |= 1 << 22; /* Reserved bit 22 must always be 1 */
+ txdctl |= IGC_TXDCTL_GRAN;
+ txdctl |= 1 << 25; /* LWTHRESH */
+
+ IGC_WRITE_REG(hw, IGC_TXDCTL(i), txdctl);
+ }
+ ifp->if_timer = 0;
+
+ /* Program the Transmit Control Register */
+ tctl = IGC_READ_REG(&sc->hw, IGC_TCTL);
+ tctl &= ~IGC_TCTL_CT;
+ tctl |= (IGC_TCTL_PSP | IGC_TCTL_RTLC | IGC_TCTL_EN |
+ (IGC_COLLISION_THRESHOLD << IGC_CT_SHIFT));
+
+ /* This write will effectively turn on the transmit unit. */
+ IGC_WRITE_REG(&sc->hw, IGC_TCTL, tctl);
+}
+
+/*********************************************************************
+ *
+ * Free all transmit rings.
+ *
+ **********************************************************************/
+void
+igc_free_transmit_structures(struct igc_softc *sc)
+{
+ struct tx_ring *txr = sc->tx_rings;
+ int i;
+
+ for (i = 0; i < sc->sc_nqueues; i++, txr++)
+ igc_free_transmit_buffers(txr);
+}
+
+/*********************************************************************
+ *
+ * Free transmit ring related data structures.
+ *
+ **********************************************************************/
+void
+igc_free_transmit_buffers(struct tx_ring *txr)
+{
+ struct igc_softc *sc = txr->sc;
+ struct igc_tx_buf *txbuf;
+ int i;
+
+ if (txr->tx_buffers == NULL)
+ return;
+
+ txbuf = txr->tx_buffers;
+ for (i = 0; i < sc->num_tx_desc; i++, txbuf++) {
+ if (txbuf->map != NULL && txbuf->map->dm_nsegs > 0) {
+ bus_dmamap_sync(txr->txdma.dma_tag, txbuf->map,
+ 0, txbuf->map->dm_mapsize, BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(txr->txdma.dma_tag, txbuf->map);
+ }
+ if (txbuf->m_head != NULL) {
+ m_freem(txbuf->m_head);
+ txbuf->m_head = NULL;
+ }
+ if (txbuf->map != NULL) {
+ bus_dmamap_destroy(txr->txdma.dma_tag, txbuf->map);
+ txbuf->map = NULL;
+ }
+ }
+
+ if (txr->tx_buffers != NULL)
+ free(txr->tx_buffers, M_DEVBUF,
+ sc->num_tx_desc * sizeof(struct igc_tx_buf));
+ txr->tx_buffers = NULL;
+ txr->txtag = NULL;
+}
+
+/*********************************************************************
+ *
+ * Allocate memory for rx_buffer structures. Since we use one
+ * rx_buffer per received packet, the maximum number of rx_buffer's
+ * that we'll need is equal to the number of receive descriptors
+ * that we've allocated.
+ *
+ **********************************************************************/
+int
+igc_allocate_receive_buffers(struct rx_ring *rxr)
+{
+ struct igc_softc *sc = rxr->sc;
+ struct igc_rx_buf *rxbuf;
+ int i, error;
+
+ rxr->rx_buffers = mallocarray(sc->num_rx_desc,
+ sizeof(struct igc_rx_buf), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (rxr->rx_buffers == NULL) {
+ printf("%s: Unable to allocate rx_buffer memory\n",
+ DEVNAME(sc));
+ error = ENOMEM;
+ goto fail;
+ }
+
+ rxbuf = rxr->rx_buffers;
+ for (i = 0; i < sc->num_rx_desc; i++, rxbuf++) {
+ error = bus_dmamap_create(rxr->rxdma.dma_tag,
+ MAX_JUMBO_FRAME_SIZE, 1, MAX_JUMBO_FRAME_SIZE, 0,
+ BUS_DMA_NOWAIT, &rxbuf->map);
+ if (error) {
+ printf("%s: Unable to create RX DMA map\n",
+ DEVNAME(sc));
+ goto fail;
+ }
+ }
+ bus_dmamap_sync(rxr->rxdma.dma_tag, rxr->rxdma.dma_map, 0,
+ rxr->rxdma.dma_map->dm_mapsize,
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+
+ return 0;
+fail:
+ return error;
+}
+
+/*********************************************************************
+ *
+ * Allocate and initialize receive structures.
+ *
+ **********************************************************************/
+int
+igc_setup_receive_structures(struct igc_softc *sc)
+{
+ struct rx_ring *rxr = sc->rx_rings;
+ int i;
+
+ for (i = 0; i < sc->sc_nqueues; i++, rxr++) {
+ if (igc_setup_receive_ring(rxr))
+ goto fail;
+ }
+
+ return 0;
+fail:
+ igc_free_receive_structures(sc);
+ return ENOBUFS;
+}
+
+/*********************************************************************
+ *
+ * Initialize a receive ring and its buffers.
+ *
+ **********************************************************************/
+int
+igc_setup_receive_ring(struct rx_ring *rxr)
+{
+ struct igc_softc *sc = rxr->sc;
+ struct ifnet *ifp = &sc->sc_ac.ac_if;
+ int rsize;
+
+ rsize = roundup2(sc->num_rx_desc * sizeof(union igc_adv_rx_desc),
+ IGC_DBA_ALIGN);
+
+ /* Clear the ring contents. */
+ bzero((void *)rxr->rx_base, rsize);
+
+ if (igc_allocate_receive_buffers(rxr))
+ return ENOMEM;
+
+ /* Setup our descriptor indices. */
+ rxr->next_to_check = 0;
+ rxr->last_desc_filled = sc->num_rx_desc - 1;
+
+ if_rxr_init(&rxr->rx_ring, 2 * ((ifp->if_hardmtu / MCLBYTES) + 1),
+ sc->num_rx_desc - 1);
+
+ igc_rxfill(rxr);
+ if (if_rxr_inuse(&rxr->rx_ring) == 0) {
+ printf("%s: Unable to fill any rx descriptors\n", DEVNAME(sc));
+ return ENOBUFS;
+ }
+
+ return 0;
+}
+
+/*********************************************************************
+ *
+ * Enable receive unit.
+ *
+ **********************************************************************/
+void
+igc_initialize_receive_unit(struct igc_softc *sc)
+{
+ struct rx_ring *rxr = sc->rx_rings;
+ struct igc_hw *hw = &sc->hw;
+ uint32_t rctl, rxcsum, srrctl = 0;
+ int i;
+
+ /*
+ * Make sure receives are disabled while setting
+ * up the descriptor ring.
+ */
+ rctl = IGC_READ_REG(hw, IGC_RCTL);
+ IGC_WRITE_REG(hw, IGC_RCTL, rctl & ~IGC_RCTL_EN);
+
+ /* Setup the Receive Control Register */
+ rctl &= ~(3 << IGC_RCTL_MO_SHIFT);
+ rctl |= IGC_RCTL_EN | IGC_RCTL_BAM | IGC_RCTL_LBM_NO |
+ IGC_RCTL_RDMTS_HALF | (hw->mac.mc_filter_type << IGC_RCTL_MO_SHIFT);
+
+ /* Enable Long Packet receive */
+ if (sc->hw.mac.max_frame_size != ETHER_MAX_LEN)
+ rctl |= IGC_RCTL_LPE;
+
+ /* Strip the CRC */
+ rctl |= IGC_RCTL_SECRC;
+
+ /*
+ * Set the interrupt throttling rate. Value is calculated
+ * as DEFAULT_ITR = 1/(MAX_INTS_PER_SEC * 256ns)
+ */
+ IGC_WRITE_REG(hw, IGC_ITR, DEFAULT_ITR);
+
+ rxcsum = IGC_READ_REG(hw, IGC_RXCSUM);
+ rxcsum &= ~IGC_RXCSUM_PCSD;
+
+ igc_initialize_rss_mapping(sc);
+
+ if (sc->sc_nqueues > 1)
+ rxcsum |= IGC_RXCSUM_PCSD;
+
+ IGC_WRITE_REG(hw, IGC_RXCSUM, rxcsum);
+
+#if 0
+ srrctl |= 4096 >> IGC_SRRCTL_BSIZEPKT_SHIFT;
+ rctl |= IGC_RCTL_SZ_4096 | IGC_RCTL_BSEX;
+#endif
+
+ srrctl |= 2048 >> IGC_SRRCTL_BSIZEPKT_SHIFT;
+ rctl |= IGC_RCTL_SZ_2048;
+
+ /*
+ * If TX flow control is disabled and there's > 1 queue defined,
+ * enable DROP.
+ *
+ * This drops frames rather than hanging the RX MAC for all queues.
+ */
+ if ((sc->sc_nqueues > 1) && (sc->fc == igc_fc_none ||
+ sc->fc == igc_fc_rx_pause)) {
+ srrctl |= IGC_SRRCTL_DROP_EN;
+ }
+
+ /* Setup the Base and Length of the RX descriptor rings. */
+ for (i = 0; i < sc->sc_nqueues; i++, rxr++) {
+ IGC_WRITE_REG(hw, IGC_RXDCTL(i), 0);
+ uint64_t bus_addr = rxr->rxdma.dma_map->dm_segs[0].ds_addr;
+ uint32_t rxdctl;
+
+ srrctl |= IGC_SRRCTL_DESCTYPE_ADV_ONEBUF;
+
+ IGC_WRITE_REG(hw, IGC_RDLEN(i),
+ sc->num_rx_desc * sizeof(union igc_adv_rx_desc));
+ IGC_WRITE_REG(hw, IGC_RDBAH(i), (uint32_t)(bus_addr >> 32));
+ IGC_WRITE_REG(hw, IGC_RDBAL(i), (uint32_t)bus_addr);
+ IGC_WRITE_REG(hw, IGC_SRRCTL(i), srrctl);
+
+ /* Setup the Head and Tail Descriptor Pointers */
+ IGC_WRITE_REG(hw, IGC_RDH(i), 0);
+ IGC_WRITE_REG(hw, IGC_RDT(i), 0);
+
+ /* Enable this Queue */
+ rxdctl = IGC_READ_REG(hw, IGC_RXDCTL(i));
+ rxdctl |= IGC_RXDCTL_QUEUE_ENABLE;
+ rxdctl &= 0xFFF00000;
+ rxdctl |= IGC_RX_PTHRESH;
+ rxdctl |= IGC_RX_HTHRESH << 8;
+ rxdctl |= IGC_RX_WTHRESH << 16;
+ IGC_WRITE_REG(hw, IGC_RXDCTL(i), rxdctl);
+ }
+
+ /* Make sure VLAN Filters are off */
+ rctl &= ~IGC_RCTL_VFE;
+
+ /* Write out the settings */
+ IGC_WRITE_REG(hw, IGC_RCTL, rctl);
+}
+
+/*********************************************************************
+ *
+ * Free all receive rings.
+ *
+ **********************************************************************/
+void
+igc_free_receive_structures(struct igc_softc *sc)
+{
+ struct rx_ring *rxr;
+ int i;
+
+ for (i = 0, rxr = sc->rx_rings; i < sc->sc_nqueues; i++, rxr++)
+ if_rxr_init(&rxr->rx_ring, 0, 0);
+
+ for (i = 0, rxr = sc->rx_rings; i < sc->sc_nqueues; i++, rxr++)
+ igc_free_receive_buffers(rxr);
+}
+
+/*********************************************************************
+ *
+ * Free receive ring data structures
+ *
+ **********************************************************************/
+void
+igc_free_receive_buffers(struct rx_ring *rxr)
+{
+ struct igc_softc *sc = rxr->sc;
+ struct igc_rx_buf *rxbuf;
+ int i;
+
+ if (rxr->rx_buffers != NULL) {
+ for (i = 0; i < sc->num_rx_desc; i++) {
+ rxbuf = &rxr->rx_buffers[i];
+ if (rxbuf->buf != NULL) {
+ bus_dmamap_sync(rxr->rxdma.dma_tag, rxbuf->map,
+ 0, rxbuf->map->dm_mapsize,
+ BUS_DMASYNC_POSTREAD);
+ bus_dmamap_unload(rxr->rxdma.dma_tag,
+ rxbuf->map);
+ m_freem(rxbuf->buf);
+ rxbuf->buf = NULL;
+ }
+ bus_dmamap_destroy(rxr->rxdma.dma_tag, rxbuf->map);
+ rxbuf->map = NULL;
+ }
+ free(rxr->rx_buffers, M_DEVBUF,
+ sc->num_rx_desc * sizeof(struct igc_rx_buf));
+ rxr->rx_buffers = NULL;
+ }
+}
+
+/*
+ * Initialise the RSS mapping for NICs that support multiple transmit/
+ * receive rings.
+ */
+void
+igc_initialize_rss_mapping(struct igc_softc *sc)
+{
+ struct igc_hw *hw = &sc->hw;
+ uint32_t rss_key[10], mrqc, reta, shift = 0;
+ int i, queue_id;
+
+ /*
+ * The redirection table controls which destination
+ * queue each bucket redirects traffic to.
+ * Each DWORD represents four queues, with the LSB
+ * being the first queue in the DWORD.
+ *
+ * This just allocates buckets to queues using round-robin
+ * allocation.
+ *
+ * NOTE: It Just Happens to line up with the default
+ * RSS allocation method.
+ */
+
+ /* Warning FM follows */
+ reta = 0;
+ for (i = 0; i < 128; i++) {
+ queue_id = (i % sc->sc_nqueues);
+ /* Adjust if required */
+ queue_id = queue_id << shift;
+
+ /*
+ * The low 8 bits are for hash value (n+0);
+ * The next 8 bits are for hash value (n+1), etc.
+ */
+ reta = reta >> 8;
+ reta = reta | ( ((uint32_t) queue_id) << 24);
+ if ((i & 3) == 3) {
+ IGC_WRITE_REG(hw, IGC_RETA(i >> 2), reta);
+ reta = 0;
+ }
+ }
+
+ /*
+ * MRQC: Multiple Receive Queues Command
+ * Set queuing to RSS control, number depends on the device.
+ */
+ mrqc = IGC_MRQC_ENABLE_RSS_4Q;
+
+ /* Set up random bits */
+ stoeplitz_to_key(&rss_key, sizeof(rss_key));
+
+ /* Now fill our hash function seeds */
+ for (i = 0; i < 10; i++)
+ IGC_WRITE_REG_ARRAY(hw, IGC_RSSRK(0), i, rss_key[i]);
+
+ /*
+ * Configure the RSS fields to hash upon.
+ */
+ mrqc |= (IGC_MRQC_RSS_FIELD_IPV4 | IGC_MRQC_RSS_FIELD_IPV4_TCP);
+ mrqc |= (IGC_MRQC_RSS_FIELD_IPV6 | IGC_MRQC_RSS_FIELD_IPV6_TCP);
+ mrqc |= IGC_MRQC_RSS_FIELD_IPV6_TCP_EX;
+
+ IGC_WRITE_REG(hw, IGC_MRQC, mrqc);
+}
+
+/*
+ * igc_get_hw_control sets the {CTRL_EXT|FWSM}:DRV_LOAD bit.
+ * For ASF and Pass Through versions of f/w this means
+ * that the driver is loaded. For AMT version type f/w
+ * this means that the network i/f is open.
+ */
+void
+igc_get_hw_control(struct igc_softc *sc)
+{
+ uint32_t ctrl_ext;
+
+ ctrl_ext = IGC_READ_REG(&sc->hw, IGC_CTRL_EXT);
+ IGC_WRITE_REG(&sc->hw, IGC_CTRL_EXT, ctrl_ext | IGC_CTRL_EXT_DRV_LOAD);
+}
+
+/*
+ * igc_release_hw_control resets {CTRL_EXT|FWSM}:DRV_LOAD bit.
+ * For ASF and Pass Through versions of f/w this means that
+ * the driver is no longer loaded. For AMT versions of the
+ * f/w this means that the network i/f is closed.
+ */
+void
+igc_release_hw_control(struct igc_softc *sc)
+{
+ uint32_t ctrl_ext;
+
+ ctrl_ext = IGC_READ_REG(&sc->hw, IGC_CTRL_EXT);
+ IGC_WRITE_REG(&sc->hw, IGC_CTRL_EXT, ctrl_ext & ~IGC_CTRL_EXT_DRV_LOAD);
+}
+
+int
+igc_is_valid_ether_addr(uint8_t *addr)
+{
+ char zero_addr[6] = { 0, 0, 0, 0, 0, 0 };
+
+ if ((addr[0] & 1) || (!bcmp(addr, zero_addr, ETHER_ADDR_LEN))) {
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/sys/dev/pci/if_igc.h b/sys/dev/pci/if_igc.h
new file mode 100644
index 00000000000..984ae6f5737
--- /dev/null
+++ b/sys/dev/pci/if_igc.h
@@ -0,0 +1,343 @@
+/* $OpenBSD: if_igc.h,v 1.1 2021/10/31 14:52:57 patrick Exp $ */
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2016 Nicole Graziano <nicole@nextbsd.org>
+ * All rights reserved.
+ * Copyright (c) 2021 Rubicon Communications, LLC (Netgate)
+ *
+ * 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _IGC_H_
+#define _IGC_H_
+
+#include <dev/pci/igc_api.h>
+#include <dev/pci/igc_i225.h>
+
+/*
+ * IGC_MAX_TXD: Maximum number of Transmit Descriptors
+ * Valid Range: 128-4096
+ * Default Value: 1024
+ * This value is the number of transmit descriptors allocated by the driver.
+ * Increasing this value allows the driver to queue more transmits. Each
+ * descriptor is 16 bytes.
+ * Since TDLEN should be multiple of 128bytes, the number of transmit
+ * desscriptors should meet the following condition.
+ * (num_tx_desc * sizeof(struct igc_tx_desc)) % 128 == 0
+ */
+#define IGC_MIN_TXD 128
+#define IGC_MAX_TXD 4096
+#define IGC_DEFAULT_TXD 1024
+#define IGC_DEFAULT_MULTI_TXD 4096
+#define IGC_MAX_TXD 4096
+
+/*
+ * IGC_MAX_RXD - Maximum number of receive Descriptors
+ * Valid Range: 128-4096
+ * Default Value: 1024
+ * This value is the number of receive descriptors allocated by the driver.
+ * Increasing this value allows the driver to buffer more incoming packets.
+ * Each descriptor is 16 bytes. A receive buffer is also allocated for each
+ * descriptor. The maximum MTU size is 16110.
+ * Since TDLEN should be multiple of 128bytes, the number of transmit
+ * desscriptors should meet the following condition.
+ * (num_tx_desc * sizeof(struct igc_tx_desc)) % 128 == 0
+ */
+#define IGC_MIN_RXD 128
+#define IGC_MAX_RXD 4096
+#define IGC_DEFAULT_RXD 1024
+#define IGC_DEFAULT_MULTI_RXD 4096
+#define IGC_MAX_RXD 4096
+
+/*
+ * IGC_TIDV_VAL - Transmit Interrupt Delay Value
+ * Valid Range: 0-65535 (0=off)
+ * Default Value: 64
+ * This value delays the generation of transmit interrupts in units of
+ * 1.024 microseconds. Transmit interrupt reduction can improve CPU
+ * efficiency if properly tuned for specific network traffic. If the
+ * system is reporting dropped transmits, this value may be set too high
+ * causing the driver to run out of available transmit descriptors.
+ */
+#define IGC_TIDV_VAL 64
+
+/*
+ * IGC_TADV_VAL - Transmit Absolute Interrupt Delay Value
+ * Valid Range: 0-65535 (0=off)
+ * Default Value: 64
+ * This value, in units of 1.024 microseconds, limits the delay in which a
+ * transmit interrupt is generated. Useful only if IGC_TIDV is non-zero,
+ * this value ensures that an interrupt is generated after the initial
+ * packet is sent on the wire within the set amount of time. Proper tuning,
+ * along with IGC_TIDV_VAL, may improve traffic throughput in specific
+ * network conditions.
+ */
+#define IGC_TADV_VAL 64
+
+/*
+ * IGC_RDTR_VAL - Receive Interrupt Delay Timer (Packet Timer)
+ * Valid Range: 0-65535 (0=off)
+ * Default Value: 0
+ * This value delays the generation of receive interrupts in units of 1.024
+ * microseconds. Receive interrupt reduction can improve CPU efficiency if
+ * properly tuned for specific network traffic. Increasing this value adds
+ * extra latency to frame reception and can end up decreasing the throughput
+ * of TCP traffic. If the system is reporting dropped receives, this value
+ * may be set too high, causing the driver to run out of available receive
+ * descriptors.
+ *
+ * CAUTION: When setting IGC_RDTR to a value other than 0, adapters
+ * may hang (stop transmitting) under certain network conditions.
+ * If this occurs a WATCHDOG message is logged in the system
+ * event log. In addition, the controller is automatically reset,
+ * restoring the network connection. To eliminate the potential
+ * for the hang ensure that IGC_RDTR is set to 0.
+ */
+#define IGC_RDTR_VAL 0
+
+/*
+ * Receive Interrupt Absolute Delay Timer
+ * Valid Range: 0-65535 (0=off)
+ * Default Value: 64
+ * This value, in units of 1.024 microseconds, limits the delay in which a
+ * receive interrupt is generated. Useful only if IGC_RDTR is non-zero,
+ * this value ensures that an interrupt is generated after the initial
+ * packet is received within the set amount of time. Proper tuning,
+ * along with IGC_RDTR, may improve traffic throughput in specific network
+ * conditions.
+ */
+#define IGC_RADV_VAL 64
+
+/*
+ * This parameter controls whether or not autonegotation is enabled.
+ * 0 - Disable autonegotiation
+ * 1 - Enable autonegotiation
+ */
+#define DO_AUTO_NEG true
+
+#define AUTONEG_ADV_DEFAULT \
+ (ADVERTISE_10_HALF | ADVERTISE_10_FULL | ADVERTISE_100_HALF | \
+ ADVERTISE_100_FULL | ADVERTISE_1000_FULL | ADVERTISE_2500_FULL)
+
+#define AUTO_ALL_MODES 0
+
+/*
+ * Micellaneous constants
+ */
+#define MAX_NUM_MULTICAST_ADDRESSES 128
+#define IGC_FC_PAUSE_TIME 0x0680
+
+#define IGC_TXPBSIZE 20408
+#define IGC_PKTTYPE_MASK 0x0000FFF0
+#define IGC_DMCTLX_DCFLUSH_DIS 0x80000000 /* Disable DMA Coalesce Flush */
+
+#define IGC_RX_PTHRESH 8
+#define IGC_RX_HTHRESH 8
+#define IGC_RX_WTHRESH 4
+
+#define IGC_TX_PTHRESH 8
+#define IGC_TX_HTHRESH 1
+
+/*
+ * TDBA/RDBA should be aligned on 16 byte boundary. But TDLEN/RDLEN should be
+ * multiple of 128 bytes. So we align TDBA/RDBA on 128 byte boundary. This will
+ * also optimize cache line size effect. H/W supports up to cache line size 128.
+ */
+#define IGC_DBA_ALIGN 128
+
+/*
+ * This parameter controls the duration of transmit watchdog timer.
+ */
+#define IGC_TX_TIMEOUT 5 /* set to 5 seconds */
+
+#define IGC_PCIREG PCI_MAPREG_START
+
+#define IGC_MAX_VECTORS 8
+
+/* Enable/disable debugging statements in shared code */
+#define DBG 0
+
+#define DEBUGOUT(...) \
+ do { if (DBG) printf(__VA_ARGS__); } while (0)
+#define DEBUGOUT1(...) DEBUGOUT(__VA_ARGS__)
+#define DEBUGOUT2(...) DEBUGOUT(__VA_ARGS__)
+#define DEBUGOUT3(...) DEBUGOUT(__VA_ARGS__)
+#define DEBUGOUT7(...) DEBUGOUT(__VA_ARGS__)
+#define DEBUGFUNC(F) DEBUGOUT(F "\n")
+
+/* Compatibility glue. */
+#define roundup2(size, unit) (((size) + (unit) - 1) & ~((unit) - 1))
+#define msec_delay(x) DELAY(1000 * (x))
+
+#define IGC_MAX_SCATTER 40
+#define IGC_TSO_SIZE 65535
+
+#define MAX_INTS_PER_SEC 8000
+#define DEFAULT_ITR (1000000000/(MAX_INTS_PER_SEC * 256))
+
+/* Forward declaration. */
+struct igc_hw;
+
+struct igc_osdep {
+ bus_dma_tag_t os_dmat;
+ bus_space_tag_t os_memt;
+ bus_space_handle_t os_memh;
+
+ bus_size_t os_memsize;
+ bus_addr_t os_membase;
+
+ void *os_sc;
+ struct pci_attach_args os_pa;
+};
+
+
+struct igc_tx_buf {
+ uint32_t eop_index;
+ struct mbuf *m_head;
+ bus_dmamap_t map;
+};
+
+struct igc_rx_buf {
+ struct mbuf *buf;
+ struct mbuf *fmp; /* First mbuf pointers. */
+ bus_dmamap_t map;
+};
+
+/*
+ * Bus dma allocation structure used by igc_dma_malloc and igc_dma_free.
+ */
+struct igc_dma_alloc {
+ caddr_t dma_vaddr;
+ bus_dma_tag_t dma_tag;
+ bus_dmamap_t dma_map;
+ bus_dma_segment_t dma_seg;
+ bus_size_t dma_size;
+ int dma_nseg;
+};
+
+/*
+ * Driver queue struct: this is the interrupt container
+ * for the associated tx and rx ring.
+ */
+struct igc_queue {
+ struct igc_softc *sc;
+ uint32_t msix;
+ uint32_t eims;
+ uint32_t eitr_setting;
+ char name[16];
+ pci_intr_handle_t ih;
+ void *tag;
+ struct tx_ring *txr;
+ struct rx_ring *rxr;
+};
+
+/*
+ * The transmit ring, one per tx queue.
+ */
+struct tx_ring {
+ struct igc_softc *sc;
+ struct ifqueue *ifq;
+ uint32_t me;
+ uint32_t watchdog_timer;
+ union igc_adv_tx_desc *tx_base;
+ struct igc_tx_buf *tx_buffers;
+ struct igc_dma_alloc txdma;
+ uint32_t next_avail_desc;
+ uint32_t next_to_clean;
+ bus_dma_tag_t txtag;
+};
+
+/*
+ * The Receive ring, one per rx queue.
+ */
+struct rx_ring {
+ struct igc_softc *sc;
+ struct ifiqueue *ifiq;
+ uint32_t me;
+ union igc_adv_rx_desc *rx_base;
+ struct igc_rx_buf *rx_buffers;
+ struct igc_dma_alloc rxdma;
+ uint32_t last_desc_filled;
+ uint32_t next_to_check;
+ struct timeout rx_refill;
+ struct if_rxring rx_ring;
+};
+
+/* Our adapter structure. */
+struct igc_softc {
+ struct device sc_dev;
+ struct arpcom sc_ac;
+ struct ifmedia media;
+ struct intrmap *sc_intrmap;
+
+ struct igc_osdep osdep;
+ struct igc_hw hw;
+
+ uint16_t fc;
+ uint16_t link_active;
+ uint16_t link_speed;
+ uint16_t link_duplex;
+ uint32_t dmac;
+
+ void *tag;
+
+ int num_tx_desc;
+ int num_rx_desc;
+
+ uint32_t max_frame_size;
+ uint32_t rx_mbuf_sz;
+ uint32_t linkvec;
+ uint32_t msix_linkmask;
+ uint32_t msix_queuesmask;
+
+ unsigned int sc_nqueues;
+ struct igc_queue *queues;
+
+ struct tx_ring *tx_rings;
+ struct rx_ring *rx_rings;
+
+ /* Multicast array memory */
+ uint8_t *mta;
+};
+
+#define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname)
+
+/* Register READ/WRITE macros */
+#define IGC_WRITE_FLUSH(a) IGC_READ_REG(a, IGC_STATUS)
+#define IGC_READ_REG(a, reg) \
+ bus_space_read_4(((struct igc_osdep *)(a)->back)->os_memt, \
+ ((struct igc_osdep *)(a)->back)->os_memh, reg)
+#define IGC_WRITE_REG(a, reg, value) \
+ bus_space_write_4(((struct igc_osdep *)(a)->back)->os_memt, \
+ ((struct igc_osdep *)(a)->back)->os_memh, reg, value)
+#define IGC_READ_REG_ARRAY(a, reg, off) \
+ bus_space_read_4(((struct igc_osdep *)(a)->back)->os_memt, \
+ ((struct igc_osdep *)(a)->back)->os_memh, (reg + ((off) << 2)))
+#define IGC_WRITE_REG_ARRAY(a, reg, off, value) \
+ bus_space_write_4(((struct igc_osdep *)(a)->back)->os_memt, \
+ ((struct igc_osdep *)(a)->back)->os_memh, \
+ (reg + ((off) << 2)),value)
+
+#endif /* _IGC_H_ */
diff --git a/sys/dev/pci/igc_api.c b/sys/dev/pci/igc_api.c
new file mode 100644
index 00000000000..125eaa89322
--- /dev/null
+++ b/sys/dev/pci/igc_api.c
@@ -0,0 +1,390 @@
+/* $OpenBSD: igc_api.c,v 1.1 2021/10/31 14:52:57 patrick Exp $ */
+/*-
+ * Copyright 2021 Intel Corp
+ * Copyright 2021 Rubicon Communications, LLC (Netgate)
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <dev/pci/igc_api.h>
+#include <dev/pci/igc_hw.h>
+
+/**
+ * igc_init_mac_params - Initialize MAC function pointers
+ * @hw: pointer to the HW structure
+ *
+ * This function initializes the function pointers for the MAC
+ * set of functions. Called by drivers or by igc_setup_init_funcs.
+ **/
+int
+igc_init_mac_params(struct igc_hw *hw)
+{
+ int ret_val = IGC_SUCCESS;
+
+ if (hw->mac.ops.init_params) {
+ ret_val = hw->mac.ops.init_params(hw);
+ if (ret_val) {
+ DEBUGOUT("MAC Initialization Error\n");
+ goto out;
+ }
+ } else {
+ DEBUGOUT("mac.init_mac_params was NULL\n");
+ ret_val = -IGC_ERR_CONFIG;
+ }
+out:
+ return ret_val;
+}
+
+/**
+ * igc_init_nvm_params - Initialize NVM function pointers
+ * @hw: pointer to the HW structure
+ *
+ * This function initializes the function pointers for the NVM
+ * set of functions. Called by drivers or by igc_setup_init_funcs.
+ **/
+int
+igc_init_nvm_params(struct igc_hw *hw)
+{
+ int ret_val = IGC_SUCCESS;
+
+ if (hw->nvm.ops.init_params) {
+ ret_val = hw->nvm.ops.init_params(hw);
+ if (ret_val) {
+ DEBUGOUT("NVM Initialization Error\n");
+ goto out;
+ }
+ } else {
+ DEBUGOUT("nvm.init_nvm_params was NULL\n");
+ ret_val = -IGC_ERR_CONFIG;
+ }
+out:
+ return ret_val;
+}
+
+/**
+ * igc_init_phy_params - Initialize PHY function pointers
+ * @hw: pointer to the HW structure
+ *
+ * This function initializes the function pointers for the PHY
+ * set of functions. Called by drivers or by igc_setup_init_funcs.
+ **/
+int
+igc_init_phy_params(struct igc_hw *hw)
+{
+ int ret_val = IGC_SUCCESS;
+
+ if (hw->phy.ops.init_params) {
+ ret_val = hw->phy.ops.init_params(hw);
+ if (ret_val) {
+ DEBUGOUT("PHY Initialization Error\n");
+ goto out;
+ }
+ } else {
+ DEBUGOUT("phy.init_phy_params was NULL\n");
+ ret_val = -IGC_ERR_CONFIG;
+ }
+out:
+ return ret_val;
+}
+
+/**
+ * igc_set_mac_type - Sets MAC type
+ * @hw: pointer to the HW structure
+ *
+ * This function sets the mac type of the adapter based on the
+ * device ID stored in the hw structure.
+ * MUST BE FIRST FUNCTION CALLED (explicitly or through
+ * igc_setup_init_funcs()).
+ **/
+int
+igc_set_mac_type(struct igc_hw *hw)
+{
+ struct igc_mac_info *mac = &hw->mac;
+ int ret_val = IGC_SUCCESS;
+
+ DEBUGFUNC("igc_set_mac_type");
+
+ switch (hw->device_id) {
+ case PCI_PRODUCT_INTEL_I220_V:
+ case PCI_PRODUCT_INTEL_I221_V:
+ case PCI_PRODUCT_INTEL_I225_BLANK_NVM:
+ case PCI_PRODUCT_INTEL_I225_I:
+ case PCI_PRODUCT_INTEL_I225_IT:
+ case PCI_PRODUCT_INTEL_I225_K:
+ case PCI_PRODUCT_INTEL_I225_K2:
+ case PCI_PRODUCT_INTEL_I225_LM:
+ case PCI_PRODUCT_INTEL_I225_LMVP:
+ case PCI_PRODUCT_INTEL_I225_V:
+ case PCI_PRODUCT_INTEL_I226_BLANK_NVM:
+ case PCI_PRODUCT_INTEL_I226_IT:
+ case PCI_PRODUCT_INTEL_I226_LM:
+ case PCI_PRODUCT_INTEL_I226_K:
+ case PCI_PRODUCT_INTEL_I226_V:
+ mac->type = igc_i225;
+ break;
+ default:
+ /* Should never have loaded on this device */
+ ret_val = -IGC_ERR_MAC_INIT;
+ break;
+ }
+
+ return ret_val;
+}
+
+/**
+ * igc_setup_init_funcs - Initializes function pointers
+ * @hw: pointer to the HW structure
+ * @init_device: true will initialize the rest of the function pointers
+ * getting the device ready for use. FALSE will only set
+ * MAC type and the function pointers for the other init
+ * functions. Passing FALSE will not generate any hardware
+ * reads or writes.
+ *
+ * This function must be called by a driver in order to use the rest
+ * of the 'shared' code files. Called by drivers only.
+ **/
+int
+igc_setup_init_funcs(struct igc_hw *hw, bool init_device)
+{
+ int ret_val;
+
+ /* Can't do much good without knowing the MAC type. */
+ ret_val = igc_set_mac_type(hw);
+ if (ret_val) {
+ DEBUGOUT("ERROR: MAC type could not be set properly.\n");
+ goto out;
+ }
+
+ if (!hw->hw_addr) {
+ DEBUGOUT("ERROR: Registers not mapped\n");
+ ret_val = -IGC_ERR_CONFIG;
+ goto out;
+ }
+
+ /*
+ * Init function pointers to generic implementations. We do this first
+ * allowing a driver module to override it afterward.
+ */
+ igc_init_mac_ops_generic(hw);
+ igc_init_phy_ops_generic(hw);
+ igc_init_nvm_ops_generic(hw);
+
+ /*
+ * Set up the init function pointers. These are functions within the
+ * adapter family file that sets up function pointers for the rest of
+ * the functions in that family.
+ */
+ switch (hw->mac.type) {
+ case igc_i225:
+ igc_init_function_pointers_i225(hw);
+ break;
+ default:
+ DEBUGOUT("Hardware not supported\n");
+ ret_val = -IGC_ERR_CONFIG;
+ break;
+ }
+
+ /*
+ * Initialize the rest of the function pointers. These require some
+ * register reads/writes in some cases.
+ */
+ if (!(ret_val) && init_device) {
+ ret_val = igc_init_mac_params(hw);
+ if (ret_val)
+ goto out;
+
+ ret_val = igc_init_nvm_params(hw);
+ if (ret_val)
+ goto out;
+
+ ret_val = igc_init_phy_params(hw);
+ if (ret_val)
+ goto out;
+ }
+out:
+ return ret_val;
+}
+
+/**
+ * igc_update_mc_addr_list - 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
+ *
+ * Updates the Multicast Table Array.
+ * The caller must have a packed mc_addr_list of multicast addresses.
+ **/
+void
+igc_update_mc_addr_list(struct igc_hw *hw, uint8_t *mc_addr_list,
+ uint32_t mc_addr_count)
+{
+ if (hw->mac.ops.update_mc_addr_list)
+ hw->mac.ops.update_mc_addr_list(hw, mc_addr_list,
+ mc_addr_count);
+}
+
+/**
+ * igc_check_for_link - Check/Store link connection
+ * @hw: pointer to the HW structure
+ *
+ * This checks the link condition of the adapter and stores the
+ * results in the hw->mac structure. This is a function pointer entry
+ * point called by drivers.
+ **/
+int
+igc_check_for_link(struct igc_hw *hw)
+{
+ if (hw->mac.ops.check_for_link)
+ return hw->mac.ops.check_for_link(hw);
+
+ return -IGC_ERR_CONFIG;
+}
+
+/**
+ * igc_reset_hw - Reset hardware
+ * @hw: pointer to the HW structure
+ *
+ * This resets the hardware into a known state. This is a function pointer
+ * entry point called by drivers.
+ **/
+int
+igc_reset_hw(struct igc_hw *hw)
+{
+ if (hw->mac.ops.reset_hw)
+ return hw->mac.ops.reset_hw(hw);
+
+ return -IGC_ERR_CONFIG;
+}
+
+/**
+ * igc_init_hw - Initialize hardware
+ * @hw: pointer to the HW structure
+ *
+ * This inits the hardware readying it for operation. This is a function
+ * pointer entry point called by drivers.
+ **/
+int
+igc_init_hw(struct igc_hw *hw)
+{
+ if (hw->mac.ops.init_hw)
+ return hw->mac.ops.init_hw(hw);
+
+ return -IGC_ERR_CONFIG;
+}
+
+/**
+ * igc_get_speed_and_duplex - Returns current speed and duplex
+ * @hw: pointer to the HW structure
+ * @speed: pointer to a 16-bit value to store the speed
+ * @duplex: pointer to a 16-bit value to store the duplex.
+ *
+ * This returns the speed and duplex of the adapter in the two 'out'
+ * variables passed in. This is a function pointer entry point called
+ * by drivers.
+ **/
+int
+igc_get_speed_and_duplex(struct igc_hw *hw, uint16_t *speed, uint16_t *duplex)
+{
+ if (hw->mac.ops.get_link_up_info)
+ return hw->mac.ops.get_link_up_info(hw, speed, duplex);
+
+ return -IGC_ERR_CONFIG;
+}
+
+/**
+ * igc_rar_set - Sets a receive address register
+ * @hw: pointer to the HW structure
+ * @addr: address to set the RAR to
+ * @index: the RAR to set
+ *
+ * Sets a Receive Address Register (RAR) to the specified address.
+ **/
+int
+igc_rar_set(struct igc_hw *hw, uint8_t *addr, uint32_t index)
+{
+ if (hw->mac.ops.rar_set)
+ return hw->mac.ops.rar_set(hw, addr, index);
+
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_check_reset_block - Verifies PHY can be reset
+ * @hw: pointer to the HW structure
+ *
+ * Checks if the PHY is in a state that can be reset or if manageability
+ * has it tied up. This is a function pointer entry point called by drivers.
+ **/
+int
+igc_check_reset_block(struct igc_hw *hw)
+{
+ if (hw->phy.ops.check_reset_block)
+ return hw->phy.ops.check_reset_block(hw);
+
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_get_phy_info - Retrieves PHY information from registers
+ * @hw: pointer to the HW structure
+ *
+ * This function gets some information from various PHY registers and
+ * populates hw->phy values with it. This is a function pointer entry
+ * point called by drivers.
+ **/
+int
+igc_get_phy_info(struct igc_hw *hw)
+{
+ if (hw->phy.ops.get_info)
+ return hw->phy.ops.get_info(hw);
+
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_phy_hw_reset - Hard PHY reset
+ * @hw: pointer to the HW structure
+ *
+ * Performs a hard PHY reset. This is a function pointer entry point called
+ * by drivers.
+ **/
+int
+igc_phy_hw_reset(struct igc_hw *hw)
+{
+ if (hw->phy.ops.reset)
+ return hw->phy.ops.reset(hw);
+
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_read_mac_addr - Reads MAC address
+ * @hw: pointer to the HW structure
+ *
+ * Reads the MAC address out of the adapter and stores it in the HW structure.
+ * Currently no func pointer exists and all implementations are handled in the
+ * generic version of this function.
+ **/
+int
+igc_read_mac_addr(struct igc_hw *hw)
+{
+ if (hw->mac.ops.read_mac_addr)
+ return hw->mac.ops.read_mac_addr(hw);
+
+ return igc_read_mac_addr_generic(hw);
+}
+
+/**
+ * igc_validate_nvm_checksum - Verifies NVM (EEPROM) checksum
+ * @hw: pointer to the HW structure
+ *
+ * Validates the NVM checksum is correct. This is a function pointer entry
+ * point called by drivers.
+ **/
+int
+igc_validate_nvm_checksum(struct igc_hw *hw)
+{
+ if (hw->nvm.ops.validate)
+ return hw->nvm.ops.validate(hw);
+
+ return -IGC_ERR_CONFIG;
+}
diff --git a/sys/dev/pci/igc_api.h b/sys/dev/pci/igc_api.h
new file mode 100644
index 00000000000..5cf1d122a11
--- /dev/null
+++ b/sys/dev/pci/igc_api.h
@@ -0,0 +1,55 @@
+/* $OpenBSD: igc_api.h,v 1.1 2021/10/31 14:52:57 patrick Exp $ */
+/*-
+ * Copyright 2021 Intel Corp
+ * Copyright 2021 Rubicon Communications, LLC (Netgate)
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _IGC_API_H_
+#define _IGC_API_H_
+
+#include <dev/pci/if_igc.h>
+#include <dev/pci/igc_hw.h>
+
+extern void igc_init_function_pointers_i225(struct igc_hw *);
+
+int igc_set_mac_type(struct igc_hw *);
+int igc_setup_init_funcs(struct igc_hw *, bool);
+int igc_init_mac_params(struct igc_hw *);
+int igc_init_nvm_params(struct igc_hw *);
+int igc_init_phy_params(struct igc_hw *);
+int igc_get_bus_info(struct igc_hw *);
+void igc_clear_vfta(struct igc_hw *);
+void igc_write_vfta(struct igc_hw *, uint32_t, uint32_t);
+int igc_force_mac_fc(struct igc_hw *);
+int igc_check_for_link(struct igc_hw *);
+int igc_reset_hw(struct igc_hw *);
+int igc_init_hw(struct igc_hw *);
+int igc_setup_link(struct igc_hw *);
+int igc_get_speed_and_duplex(struct igc_hw *, uint16_t *,
+ uint16_t *);
+int igc_disable_pcie_master(struct igc_hw *);
+void igc_config_collision_dist(struct igc_hw *);
+int igc_rar_set(struct igc_hw *, uint8_t *, uint32_t);
+uint32_t igc_hash_mc_addr(struct igc_hw *, uint8_t *);
+void igc_update_mc_addr_list(struct igc_hw *, uint8_t *, uint32_t);
+int igc_check_reset_block(struct igc_hw *);
+int igc_get_cable_length(struct igc_hw *);
+int igc_validate_mdi_setting(struct igc_hw *);
+int igc_get_phy_info(struct igc_hw *);
+int igc_phy_hw_reset(struct igc_hw *);
+int igc_phy_commit(struct igc_hw *);
+void igc_power_up_phy(struct igc_hw *);
+void igc_power_down_phy(struct igc_hw *);
+int igc_read_mac_addr(struct igc_hw *);
+int igc_read_pba_string(struct igc_hw *, uint8_t *, uint32_t);
+void igc_reload_nvm(struct igc_hw *);
+int igc_validate_nvm_checksum(struct igc_hw *);
+int igc_read_nvm(struct igc_hw *, uint16_t, uint16_t, uint16_t *);
+int igc_write_nvm(struct igc_hw *, uint16_t, uint16_t, uint16_t *);
+int igc_set_d3_lplu_state(struct igc_hw *, bool);
+int igc_set_d0_lplu_state(struct igc_hw *, bool);
+
+#endif /* _IGC_API_H_ */
diff --git a/sys/dev/pci/igc_base.c b/sys/dev/pci/igc_base.c
new file mode 100644
index 00000000000..fc441e942f9
--- /dev/null
+++ b/sys/dev/pci/igc_base.c
@@ -0,0 +1,113 @@
+/* $OpenBSD: igc_base.c,v 1.1 2021/10/31 14:52:57 patrick Exp $ */
+/*-
+ * Copyright 2021 Intel Corp
+ * Copyright 2021 Rubicon Communications, LLC (Netgate)
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <dev/pci/igc_hw.h>
+#include <dev/pci/igc_i225.h>
+#include <dev/pci/if_igc.h>
+#include <dev/pci/igc_mac.h>
+#include <dev/pci/igc_base.h>
+
+/**
+ * igc_acquire_phy_base - Acquire rights to access PHY
+ * @hw: pointer to the HW structure
+ *
+ * Acquire access rights to the correct PHY.
+ **/
+int
+igc_acquire_phy_base(struct igc_hw *hw)
+{
+ uint16_t mask = IGC_SWFW_PHY0_SM;
+
+ DEBUGFUNC("igc_acquire_phy_base");
+
+ if (hw->bus.func == IGC_FUNC_1)
+ mask = IGC_SWFW_PHY1_SM;
+
+ return hw->mac.ops.acquire_swfw_sync(hw, mask);
+}
+
+/**
+ * igc_release_phy_base - Release rights to access PHY
+ * @hw: pointer to the HW structure
+ *
+ * A wrapper to release access rights to the correct PHY.
+ **/
+void
+igc_release_phy_base(struct igc_hw *hw)
+{
+ uint16_t mask = IGC_SWFW_PHY0_SM;
+
+ DEBUGFUNC("igc_release_phy_base");
+
+ if (hw->bus.func == IGC_FUNC_1)
+ mask = IGC_SWFW_PHY1_SM;
+
+ hw->mac.ops.release_swfw_sync(hw, mask);
+}
+
+/**
+ * igc_init_hw_base - Initialize hardware
+ * @hw: pointer to the HW structure
+ *
+ * This inits the hardware readying it for operation.
+ **/
+int
+igc_init_hw_base(struct igc_hw *hw)
+{
+ struct igc_mac_info *mac = &hw->mac;
+ uint16_t i, rar_count = mac->rar_entry_count;
+ int ret_val;
+
+ DEBUGFUNC("igc_init_hw_base");
+
+ /* Setup the receive address */
+ igc_init_rx_addrs_generic(hw, rar_count);
+
+ /* Zero out the Multicast HASH table */
+ DEBUGOUT("Zeroing the MTA\n");
+ for (i = 0; i < mac->mta_reg_count; i++)
+ IGC_WRITE_REG_ARRAY(hw, IGC_MTA, i, 0);
+
+ /* Zero out the Unicast HASH table */
+ DEBUGOUT("Zeroing the UTA\n");
+ for (i = 0; i < mac->uta_reg_count; i++)
+ IGC_WRITE_REG_ARRAY(hw, IGC_UTA, i, 0);
+
+ /* Setup link and flow control */
+ ret_val = mac->ops.setup_link(hw);
+ /*
+ * Clear all of the statistics registers (clear on read). It is
+ * important that we do this after we have tried to establish link
+ * because the symbol error count will increment wildly if there
+ * is no link.
+ */
+ igc_clear_hw_cntrs_base_generic(hw);
+
+ return ret_val;
+}
+
+/**
+ * igc_power_down_phy_copper_base - Remove link during PHY power down
+ * @hw: pointer to the HW structure
+ *
+ * In the case of a PHY power down to save power, or to turn off link during a
+ * driver unload, or wake on lan is not enabled, remove the link.
+ **/
+void
+igc_power_down_phy_copper_base(struct igc_hw *hw)
+{
+ struct igc_phy_info *phy = &hw->phy;
+
+ if (!(phy->ops.check_reset_block))
+ return;
+
+ /* If the management interface is not enabled, then power down */
+ if (phy->ops.check_reset_block(hw))
+ igc_power_down_phy_copper(hw);
+
+ return;
+}
diff --git a/sys/dev/pci/igc_base.h b/sys/dev/pci/igc_base.h
new file mode 100644
index 00000000000..aec33e0d40f
--- /dev/null
+++ b/sys/dev/pci/igc_base.h
@@ -0,0 +1,134 @@
+/* $OpenBSD: igc_base.h,v 1.1 2021/10/31 14:52:57 patrick Exp $ */
+/*-
+ * Copyright 2021 Intel Corp
+ * Copyright 2021 Rubicon Communications, LLC (Netgate)
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _IGC_BASE_H_
+#define _IGC_BASE_H_
+
+/* Forward declaration */
+struct igc_hw;
+
+int igc_init_hw_base(struct igc_hw *hw);
+void igc_power_down_phy_copper_base(struct igc_hw *hw);
+extern void igc_rx_fifo_flush_base(struct igc_hw *hw);
+int igc_acquire_phy_base(struct igc_hw *hw);
+void igc_release_phy_base(struct igc_hw *hw);
+
+/* Transmit Descriptor - Advanced */
+union igc_adv_tx_desc {
+ struct {
+ uint64_t buffer_addr; /* Address of descriptor's data buf */
+ uint32_t cmd_type_len;
+ uint32_t olinfo_status;
+ } read;
+ struct {
+ uint64_t rsvd; /* Reserved */
+ uint32_t nxtseq_seed;
+ uint32_t status;
+ } wb;
+};
+
+/* Context descriptors */
+struct igc_adv_tx_context_desc {
+ uint32_t vlan_macip_lens;
+ union {
+ uint32_t launch_time;
+ uint32_t seqnum_seed;
+ };
+ uint32_t type_tucmd_mlhl;
+ uint32_t mss_l4len_idx;
+};
+
+/* Adv Transmit Descriptor Config Masks */
+#define IGC_ADVTXD_DTYP_CTXT 0x00200000 /* Advanced Context Descriptor */
+#define IGC_ADVTXD_DTYP_DATA 0x00300000 /* Advanced Data Descriptor */
+#define IGC_ADVTXD_DCMD_EOP 0x01000000 /* End of Packet */
+#define IGC_ADVTXD_DCMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */
+#define IGC_ADVTXD_DCMD_RS 0x08000000 /* Report Status */
+#define IGC_ADVTXD_DCMD_DDTYP_ISCSI 0x10000000 /* DDP hdr type or iSCSI */
+#define IGC_ADVTXD_DCMD_DEXT 0x20000000 /* Descriptor extension (1=Adv) */
+#define IGC_ADVTXD_DCMD_VLE 0x40000000 /* VLAN pkt enable */
+#define IGC_ADVTXD_DCMD_TSE 0x80000000 /* TCP Seg enable */
+#define IGC_ADVTXD_MAC_LINKSEC 0x00040000 /* Apply LinkSec on pkt */
+#define IGC_ADVTXD_MAC_TSTAMP 0x00080000 /* IEEE1588 Timestamp pkt */
+#define IGC_ADVTXD_STAT_SN_CRC 0x00000002 /* NXTSEQ/SEED prsnt in WB */
+#define IGC_ADVTXD_IDX_SHIFT 4 /* Adv desc Index shift */
+#define IGC_ADVTXD_POPTS_ISCO_1ST 0x00000000 /* 1st TSO of iSCSI PDU */
+#define IGC_ADVTXD_POPTS_ISCO_MDL 0x00000800 /* Middle TSO of iSCSI PDU */
+#define IGC_ADVTXD_POPTS_ISCO_LAST 0x00001000 /* Last TSO of iSCSI PDU */
+/* 1st & Last TSO-full iSCSI PDU*/
+#define IGC_ADVTXD_POPTS_ISCO_FULL 0x00001800
+#define IGC_ADVTXD_POPTS_IPSEC 0x00000400 /* IPSec offload request */
+#define IGC_ADVTXD_PAYLEN_SHIFT 14 /* Adv desc PAYLEN shift */
+
+/* Advanced Transmit Context Descriptor Config */
+#define IGC_ADVTXD_MACLEN_SHIFT 9 /* Adv ctxt desc mac len shift */
+#define IGC_ADVTXD_VLAN_SHIFT 16 /* Adv ctxt vlan tag shift */
+#define IGC_ADVTXD_TUCMD_IPV4 0x00000400 /* IP Packet Type: 1=IPv4 */
+#define IGC_ADVTXD_TUCMD_IPV6 0x00000000 /* IP Packet Type: 0=IPv6 */
+#define IGC_ADVTXD_TUCMD_L4T_UDP 0x00000000 /* L4 Packet TYPE of UDP */
+#define IGC_ADVTXD_TUCMD_L4T_TCP 0x00000800 /* L4 Packet TYPE of TCP */
+#define IGC_ADVTXD_TUCMD_L4T_SCTP 0x00001000 /* L4 Packet TYPE of SCTP */
+#define IGC_ADVTXD_TUCMD_IPSEC_TYPE_ESP 0x00002000 /* IPSec Type ESP */
+/* IPSec Encrypt Enable for ESP */
+#define IGC_ADVTXD_TUCMD_IPSEC_ENCRYPT_EN 0x00004000
+/* Req requires Markers and CRC */
+#define IGC_ADVTXD_TUCMD_MKRREQ 0x00002000
+#define IGC_ADVTXD_L4LEN_SHIFT 8 /* Adv ctxt L4LEN shift */
+#define IGC_ADVTXD_MSS_SHIFT 16 /* Adv ctxt MSS shift */
+/* Adv ctxt IPSec SA IDX mask */
+#define IGC_ADVTXD_IPSEC_SA_INDEX_MASK 0x000000FF
+/* Adv ctxt IPSec ESP len mask */
+#define IGC_ADVTXD_IPSEC_ESP_LEN_MASK 0x000000FF
+
+#define IGC_RAR_ENTRIES_BASE 16
+
+/* Receive Descriptor - Advanced */
+union igc_adv_rx_desc {
+ struct {
+ uint64_t pkt_addr; /* Packet buffer address */
+ uint64_t hdr_addr; /* Header buffer address */
+ } read;
+ struct {
+ struct {
+ union {
+ uint32_t data;
+ struct {
+ uint16_t pkt_info; /* Pkt type */
+ /* Split Header, header buffer len */
+ uint16_t hdr_info;
+ } hs_rss;
+ } lo_dword;
+ union {
+ uint32_t rss; /* RSS hash */
+ struct {
+ uint16_t ip_id; /* IP id */
+ uint16_t csum; /* Packet checksum */
+ } csum_ip;
+ } hi_dword;
+ } lower;
+ struct {
+ uint32_t status_error; /* ext status/error */
+ uint16_t length; /* Packet length */
+ uint16_t vlan; /* VLAN tag */
+ } upper;
+ } wb; /* writeback */
+};
+
+/* Additional Transmit Descriptor Control definitions */
+#define IGC_TXDCTL_QUEUE_ENABLE 0x02000000 /* Ena specific Tx Queue */
+
+/* Additional Receive Descriptor Control definitions */
+#define IGC_RXDCTL_QUEUE_ENABLE 0x02000000 /* Ena specific Rx Queue */
+
+/* SRRCTL bit definitions */
+#define IGC_SRRCTL_BSIZEPKT_SHIFT 10 /* Shift _right_ */
+#define IGC_SRRCTL_BSIZEHDRSIZE_SHIFT 2 /* Shift _left_ */
+#define IGC_SRRCTL_DESCTYPE_ADV_ONEBUF 0x02000000
+
+#endif /* _IGC_BASE_H_ */
diff --git a/sys/dev/pci/igc_defines.h b/sys/dev/pci/igc_defines.h
new file mode 100644
index 00000000000..3618aa45aff
--- /dev/null
+++ b/sys/dev/pci/igc_defines.h
@@ -0,0 +1,1338 @@
+/* $OpenBSD: igc_defines.h,v 1.1 2021/10/31 14:52:57 patrick Exp $ */
+
+/*-
+ * Copyright 2021 Intel Corp
+ * Copyright 2021 Rubicon Communications, LLC (Netgate)
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _IGC_DEFINES_H_
+#define _IGC_DEFINES_H_
+
+/* Number of Transmit and Receive Descriptors must be a multiple of 8 */
+#define REQ_TX_DESCRIPTOR_MULTIPLE 8
+#define REQ_RX_DESCRIPTOR_MULTIPLE 8
+
+/* Definitions for power management and wakeup registers */
+/* Wake Up Control */
+#define IGC_WUC_APME 0x00000001 /* APM Enable */
+#define IGC_WUC_PME_EN 0x00000002 /* PME Enable */
+#define IGC_WUC_PME_STATUS 0x00000004 /* PME Status */
+#define IGC_WUC_APMPME 0x00000008 /* Assert PME on APM Wakeup */
+#define IGC_WUC_PHY_WAKE 0x00000100 /* if PHY supports wakeup */
+
+/* Wake Up Filter Control */
+#define IGC_WUFC_LNKC 0x00000001 /* Link Status Change Wakeup Enable */
+#define IGC_WUFC_MAG 0x00000002 /* Magic Packet Wakeup Enable */
+#define IGC_WUFC_EX 0x00000004 /* Directed Exact Wakeup Enable */
+#define IGC_WUFC_MC 0x00000008 /* Directed Multicast Wakeup Enable */
+#define IGC_WUFC_BC 0x00000010 /* Broadcast Wakeup Enable */
+#define IGC_WUFC_ARP 0x00000020 /* ARP Request Packet Wakeup Enable */
+#define IGC_WUFC_IPV4 0x00000040 /* Directed IPv4 Packet Wakeup Enable */
+
+/* Wake Up Status */
+#define IGC_WUS_LNKC IGC_WUFC_LNKC
+#define IGC_WUS_MAG IGC_WUFC_MAG
+#define IGC_WUS_EX IGC_WUFC_EX
+#define IGC_WUS_MC IGC_WUFC_MC
+#define IGC_WUS_BC IGC_WUFC_BC
+
+/* Packet types that are enabled for wake packet delivery */
+#define WAKE_PKT_WUS ( \
+ IGC_WUS_EX | \
+ IGC_WUS_ARPD | \
+ IGC_WUS_IPV4 | \
+ IGC_WUS_IPV6 | \
+ IGC_WUS_NSD)
+
+/* Wake Up Packet Length */
+#define IGC_WUPL_MASK 0x00000FFF
+
+/* Wake Up Packet Memory stores the first 128 bytes of the wake up packet */
+#define IGC_WUPM_BYTES 128
+
+#define IGC_WUS_ARPD 0x00000020 /* Directed ARP Request */
+#define IGC_WUS_IPV4 0x00000040 /* Directed IPv4 */
+#define IGC_WUS_IPV6 0x00000080 /* Directed IPv6 */
+#define IGC_WUS_NSD 0x00000400 /* Directed IPv6 Neighbor Solicitation */
+
+/* Extended Device Control */
+#define IGC_CTRL_EXT_LPCD 0x00000004 /* LCD Power Cycle Done */
+#define IGC_CTRL_EXT_SDP4_DATA 0x00000010 /* SW Definable Pin 4 data */
+#define IGC_CTRL_EXT_SDP6_DATA 0x00000040 /* SW Definable Pin 6 data */
+#define IGC_CTRL_EXT_SDP3_DATA 0x00000080 /* SW Definable Pin 3 data */
+#define IGC_CTRL_EXT_SDP6_DIR 0x00000400 /* Direction of SDP6 0=in 1=out */
+#define IGC_CTRL_EXT_SDP3_DIR 0x00000800 /* Direction of SDP3 0=in 1=out */
+#define IGC_CTRL_EXT_EE_RST 0x00002000 /* Reinitialize from EEPROM */
+#define IGC_CTRL_EXT_SPD_BYPS 0x00008000 /* Speed Select Bypass */
+#define IGC_CTRL_EXT_RO_DIS 0x00020000 /* Relaxed Ordering disable */
+#define IGC_CTRL_EXT_DMA_DYN_CLK_EN 0x00080000 /* DMA Dynamic Clk Gating */
+#define IGC_CTRL_EXT_LINK_MODE_PCIE_SERDES 0x00C00000
+#define IGC_CTRL_EXT_EIAME 0x01000000
+#define IGC_CTRL_EXT_DRV_LOAD 0x10000000 /* Drv loaded bit for FW */
+#define IGC_CTRL_EXT_IAME 0x08000000 /* Int ACK Auto-mask */
+#define IGC_CTRL_EXT_PBA_CLR 0x80000000 /* PBA Clear */
+#define IGC_CTRL_EXT_PHYPDEN 0x00100000
+#define IGC_IVAR_VALID 0x80
+#define IGC_GPIE_NSICR 0x00000001
+#define IGC_GPIE_MSIX_MODE 0x00000010
+#define IGC_GPIE_EIAME 0x40000000
+#define IGC_GPIE_PBA 0x80000000
+
+/* Receive Descriptor bit definitions */
+#define IGC_RXD_STAT_DD 0x01 /* Descriptor Done */
+#define IGC_RXD_STAT_EOP 0x02 /* End of Packet */
+#define IGC_RXD_STAT_IXSM 0x04 /* Ignore checksum */
+#define IGC_RXD_STAT_VP 0x08 /* IEEE VLAN Packet */
+#define IGC_RXD_STAT_UDPCS 0x10 /* UDP xsum calculated */
+#define IGC_RXD_STAT_TCPCS 0x20 /* TCP xsum calculated */
+#define IGC_RXD_STAT_IPCS 0x40 /* IP xsum calculated */
+#define IGC_RXD_STAT_PIF 0x80 /* passed in-exact filter */
+#define IGC_RXD_STAT_IPIDV 0x200 /* IP identification valid */
+#define IGC_RXD_STAT_UDPV 0x400 /* Valid UDP checksum */
+#define IGC_RXD_ERR_CE 0x01 /* CRC Error */
+#define IGC_RXD_ERR_SE 0x02 /* Symbol Error */
+#define IGC_RXD_ERR_SEQ 0x04 /* Sequence Error */
+#define IGC_RXD_ERR_CXE 0x10 /* Carrier Extension Error */
+#define IGC_RXD_ERR_TCPE 0x20 /* TCP/UDP Checksum Error */
+#define IGC_RXD_ERR_IPE 0x40 /* IP Checksum Error */
+#define IGC_RXD_ERR_RXE 0x80 /* Rx Data Error */
+#define IGC_RXD_SPC_VLAN_MASK 0x0FFF /* VLAN ID is in lower 12 bits */
+
+#define IGC_RXDEXT_STATERR_TST 0x00000100 /* Time Stamp taken */
+#define IGC_RXDEXT_STATERR_LB 0x00040000
+#define IGC_RXDEXT_STATERR_L4E 0x20000000
+#define IGC_RXDEXT_STATERR_IPE 0x40000000
+#define IGC_RXDEXT_STATERR_RXE 0x80000000
+
+/* Same mask, but for extended and packet split descriptors */
+#define IGC_RXDEXT_ERR_FRAME_ERR_MASK ( \
+ IGC_RXDEXT_STATERR_CE | \
+ IGC_RXDEXT_STATERR_SE | \
+ IGC_RXDEXT_STATERR_SEQ | \
+ IGC_RXDEXT_STATERR_CXE | \
+ IGC_RXDEXT_STATERR_RXE)
+
+#define IGC_MRQC_RSS_FIELD_MASK 0xFFFF0000
+#define IGC_MRQC_RSS_FIELD_IPV4_TCP 0x00010000
+#define IGC_MRQC_RSS_FIELD_IPV4 0x00020000
+#define IGC_MRQC_RSS_FIELD_IPV6_TCP_EX 0x00040000
+#define IGC_MRQC_RSS_FIELD_IPV6 0x00100000
+#define IGC_MRQC_RSS_FIELD_IPV6_TCP 0x00200000
+
+#define IGC_RXDPS_HDRSTAT_HDRSP 0x00008000
+
+/* Management Control */
+#define IGC_MANC_SMBUS_EN 0x00000001 /* SMBus Enabled - RO */
+#define IGC_MANC_ASF_EN 0x00000002 /* ASF Enabled - RO */
+#define IGC_MANC_ARP_EN 0x00002000 /* Enable ARP Request Filtering */
+#define IGC_MANC_RCV_TCO_EN 0x00020000 /* Receive TCO Packets Enabled */
+#define IGC_MANC_BLK_PHY_RST_ON_IDE 0x00040000 /* Block phy resets */
+/* Enable MAC address filtering */
+#define IGC_MANC_EN_MAC_ADDR_FILTER 0x00100000
+/* Enable MNG packets to host memory */
+#define IGC_MANC_EN_MNG2HOST 0x00200000
+
+#define IGC_MANC2H_PORT_623 0x00000020 /* Port 0x26f */
+#define IGC_MANC2H_PORT_664 0x00000040 /* Port 0x298 */
+#define IGC_MDEF_PORT_623 0x00000800 /* Port 0x26f */
+#define IGC_MDEF_PORT_664 0x00000400 /* Port 0x298 */
+
+/* Receive Control */
+#define IGC_RCTL_RST 0x00000001 /* Software reset */
+#define IGC_RCTL_EN 0x00000002 /* enable */
+#define IGC_RCTL_SBP 0x00000004 /* store bad packet */
+#define IGC_RCTL_UPE 0x00000008 /* unicast promisc enable */
+#define IGC_RCTL_MPE 0x00000010 /* multicast promisc enable */
+#define IGC_RCTL_LPE 0x00000020 /* long packet enable */
+#define IGC_RCTL_LBM_NO 0x00000000 /* no loopback mode */
+#define IGC_RCTL_LBM_MAC 0x00000040 /* MAC loopback mode */
+#define IGC_RCTL_LBM_TCVR 0x000000C0 /* tcvr loopback mode */
+#define IGC_RCTL_DTYP_PS 0x00000400 /* Packet Split descriptor */
+#define IGC_RCTL_RDMTS_HALF 0x00000000 /* Rx desc min thresh size */
+#define IGC_RCTL_RDMTS_HEX 0x00010000
+#define IGC_RCTL_RDMTS1_HEX IGC_RCTL_RDMTS_HEX
+#define IGC_RCTL_MO_SHIFT 12 /* multicast offset shift */
+#define IGC_RCTL_MO_3 0x00003000 /* multicast offset 15:4 */
+#define IGC_RCTL_BAM 0x00008000 /* broadcast enable */
+/* these buffer sizes are valid if IGC_RCTL_BSEX is 0 */
+#define IGC_RCTL_SZ_2048 0x00000000 /* Rx buffer size 2048 */
+#define IGC_RCTL_SZ_1024 0x00010000 /* Rx buffer size 1024 */
+#define IGC_RCTL_SZ_512 0x00020000 /* Rx buffer size 512 */
+#define IGC_RCTL_SZ_256 0x00030000 /* Rx buffer size 256 */
+/* these buffer sizes are valid if IGC_RCTL_BSEX is 1 */
+#define IGC_RCTL_SZ_16384 0x00010000 /* Rx buffer size 16384 */
+#define IGC_RCTL_SZ_8192 0x00020000 /* Rx buffer size 8192 */
+#define IGC_RCTL_SZ_4096 0x00030000 /* Rx buffer size 4096 */
+#define IGC_RCTL_VFE 0x00040000 /* vlan filter enable */
+#define IGC_RCTL_CFIEN 0x00080000 /* canonical form enable */
+#define IGC_RCTL_CFI 0x00100000 /* canonical form indicator */
+#define IGC_RCTL_DPF 0x00400000 /* discard pause frames */
+#define IGC_RCTL_PMCF 0x00800000 /* pass MAC control frames */
+#define IGC_RCTL_BSEX 0x02000000 /* Buffer size extension */
+#define IGC_RCTL_SECRC 0x04000000 /* Strip Ethernet CRC */
+
+/* Use byte values for the following shift parameters
+ * Usage:
+ * psrctl |= (((ROUNDUP(value0, 128) >> IGC_PSRCTL_BSIZE0_SHIFT) &
+ * IGC_PSRCTL_BSIZE0_MASK) |
+ * ((ROUNDUP(value1, 1024) >> IGC_PSRCTL_BSIZE1_SHIFT) &
+ * IGC_PSRCTL_BSIZE1_MASK) |
+ * ((ROUNDUP(value2, 1024) << IGC_PSRCTL_BSIZE2_SHIFT) &
+ * IGC_PSRCTL_BSIZE2_MASK) |
+ * ((ROUNDUP(value3, 1024) << IGC_PSRCTL_BSIZE3_SHIFT) |;
+ * IGC_PSRCTL_BSIZE3_MASK))
+ * where value0 = [128..16256], default=256
+ * value1 = [1024..64512], default=4096
+ * value2 = [0..64512], default=4096
+ * value3 = [0..64512], default=0
+ */
+
+#define IGC_PSRCTL_BSIZE0_MASK 0x0000007F
+#define IGC_PSRCTL_BSIZE1_MASK 0x00003F00
+#define IGC_PSRCTL_BSIZE2_MASK 0x003F0000
+#define IGC_PSRCTL_BSIZE3_MASK 0x3F000000
+
+#define IGC_PSRCTL_BSIZE0_SHIFT 7 /* Shift _right_ 7 */
+#define IGC_PSRCTL_BSIZE1_SHIFT 2 /* Shift _right_ 2 */
+#define IGC_PSRCTL_BSIZE2_SHIFT 6 /* Shift _left_ 6 */
+#define IGC_PSRCTL_BSIZE3_SHIFT 14 /* Shift _left_ 14 */
+
+/* SWFW_SYNC Definitions */
+#define IGC_SWFW_EEP_SM 0x01
+#define IGC_SWFW_PHY0_SM 0x02
+#define IGC_SWFW_PHY1_SM 0x04
+#define IGC_SWFW_CSR_SM 0x08
+#define IGC_SWFW_SW_MNG_SM 0x400
+
+/* Device Control */
+#define IGC_CTRL_FD 0x00000001 /* Full duplex.0=half; 1=full */
+#define IGC_CTRL_PRIOR 0x00000004 /* Priority on PCI. 0=rx,1=fair */
+#define IGC_CTRL_GIO_MASTER_DISABLE 0x00000004 /*Blocks new Master reqs */
+#define IGC_CTRL_LRST 0x00000008 /* Link reset. 0=normal,1=reset */
+#define IGC_CTRL_ASDE 0x00000020 /* Auto-speed detect enable */
+#define IGC_CTRL_SLU 0x00000040 /* Set link up (Force Link) */
+#define IGC_CTRL_ILOS 0x00000080 /* Invert Loss-Of Signal */
+#define IGC_CTRL_SPD_SEL 0x00000300 /* Speed Select Mask */
+#define IGC_CTRL_SPD_10 0x00000000 /* Force 10Mb */
+#define IGC_CTRL_SPD_100 0x00000100 /* Force 100Mb */
+#define IGC_CTRL_SPD_1000 0x00000200 /* Force 1Gb */
+#define IGC_CTRL_FRCSPD 0x00000800 /* Force Speed */
+#define IGC_CTRL_FRCDPX 0x00001000 /* Force Duplex */
+#define IGC_CTRL_SWDPIN0 0x00040000 /* SWDPIN 0 value */
+#define IGC_CTRL_SWDPIN1 0x00080000 /* SWDPIN 1 value */
+#define IGC_CTRL_SWDPIN2 0x00100000 /* SWDPIN 2 value */
+#define IGC_CTRL_ADVD3WUC 0x00100000 /* D3 WUC */
+#define IGC_CTRL_SWDPIN3 0x00200000 /* SWDPIN 3 value */
+#define IGC_CTRL_SWDPIO0 0x00400000 /* SWDPIN 0 Input or output */
+#define IGC_CTRL_DEV_RST 0x20000000 /* Device reset */
+#define IGC_CTRL_RST 0x04000000 /* Global reset */
+#define IGC_CTRL_RFCE 0x08000000 /* Receive Flow Control enable */
+#define IGC_CTRL_TFCE 0x10000000 /* Transmit flow control enable */
+#define IGC_CTRL_VME 0x40000000 /* IEEE VLAN mode enable */
+#define IGC_CTRL_PHY_RST 0x80000000 /* PHY Reset */
+
+
+#define IGC_CONNSW_AUTOSENSE_EN 0x01
+#define IGC_PCS_LCTL_FORCE_FCTRL 0x80
+
+#define IGC_PCS_LSTS_AN_COMPLETE 0x10000
+
+/* Device Status */
+#define IGC_STATUS_FD 0x00000001 /* Duplex 0=half 1=full */
+#define IGC_STATUS_LU 0x00000002 /* Link up.0=no,1=link */
+#define IGC_STATUS_FUNC_MASK 0x0000000C /* PCI Function Mask */
+#define IGC_STATUS_FUNC_SHIFT 2
+#define IGC_STATUS_FUNC_1 0x00000004 /* Function 1 */
+#define IGC_STATUS_TXOFF 0x00000010 /* transmission paused */
+#define IGC_STATUS_SPEED_MASK 0x000000C0
+#define IGC_STATUS_SPEED_10 0x00000000 /* Speed 10Mb/s */
+#define IGC_STATUS_SPEED_100 0x00000040 /* Speed 100Mb/s */
+#define IGC_STATUS_SPEED_1000 0x00000080 /* Speed 1000Mb/s */
+#define IGC_STATUS_SPEED_2500 0x00400000 /* Speed 2.5Gb/s */
+#define IGC_STATUS_LAN_INIT_DONE 0x00000200 /* Lan Init Compltn by NVM */
+#define IGC_STATUS_PHYRA 0x00000400 /* PHY Reset Asserted */
+#define IGC_STATUS_GIO_MASTER_ENABLE 0x00080000 /* Master request status */
+#define IGC_STATUS_2P5_SKU 0x00001000 /* Val of 2.5GBE SKU strap */
+#define IGC_STATUS_2P5_SKU_OVER 0x00002000 /* Val of 2.5GBE SKU Over */
+#define IGC_STATUS_PCIM_STATE 0x40000000 /* PCIm function state */
+
+#define SPEED_10 10
+#define SPEED_100 100
+#define SPEED_1000 1000
+#define SPEED_2500 2500
+#define HALF_DUPLEX 1
+#define FULL_DUPLEX 2
+
+#define ADVERTISE_10_HALF 0x0001
+#define ADVERTISE_10_FULL 0x0002
+#define ADVERTISE_100_HALF 0x0004
+#define ADVERTISE_100_FULL 0x0008
+#define ADVERTISE_1000_HALF 0x0010 /* Not used, just FYI */
+#define ADVERTISE_1000_FULL 0x0020
+#define ADVERTISE_2500_HALF 0x0040 /* NOT used, just FYI */
+#define ADVERTISE_2500_FULL 0x0080
+
+/* 1000/H is not supported, nor spec-compliant. */
+#define IGC_ALL_SPEED_DUPLEX ( \
+ ADVERTISE_10_HALF | ADVERTISE_10_FULL | ADVERTISE_100_HALF | \
+ ADVERTISE_100_FULL | ADVERTISE_1000_FULL)
+#define IGC_ALL_SPEED_DUPLEX_2500 ( \
+ ADVERTISE_10_HALF | ADVERTISE_10_FULL | ADVERTISE_100_HALF | \
+ ADVERTISE_100_FULL | ADVERTISE_1000_FULL | ADVERTISE_2500_FULL)
+#define IGC_ALL_NOT_GIG ( \
+ ADVERTISE_10_HALF | ADVERTISE_10_FULL | ADVERTISE_100_HALF | \
+ ADVERTISE_100_FULL)
+#define IGC_ALL_100_SPEED (ADVERTISE_100_HALF | ADVERTISE_100_FULL)
+#define IGC_ALL_10_SPEED (ADVERTISE_10_HALF | ADVERTISE_10_FULL)
+#define IGC_ALL_HALF_DUPLEX (ADVERTISE_10_HALF | ADVERTISE_100_HALF)
+
+#define AUTONEG_ADVERTISE_SPEED_DEFAULT IGC_ALL_SPEED_DUPLEX
+#define AUTONEG_ADVERTISE_SPEED_DEFAULT_2500 IGC_ALL_SPEED_DUPLEX_2500
+
+/* LED Control */
+#define IGC_LEDCTL_LED0_MODE_MASK 0x0000000F
+#define IGC_LEDCTL_LED0_MODE_SHIFT 0
+#define IGC_LEDCTL_LED0_IVRT 0x00000040
+#define IGC_LEDCTL_LED0_BLINK 0x00000080
+
+#define IGC_LEDCTL_MODE_LED_ON 0x0E
+#define IGC_LEDCTL_MODE_LED_OFF 0x0F
+
+/* Transmit Descriptor bit definitions */
+#define IGC_TXD_DTYP_D 0x00100000 /* Data Descriptor */
+#define IGC_TXD_DTYP_C 0x00000000 /* Context Descriptor */
+#define IGC_TXD_POPTS_IXSM 0x01 /* Insert IP checksum */
+#define IGC_TXD_POPTS_TXSM 0x02 /* Insert TCP/UDP checksum */
+#define IGC_TXD_CMD_EOP 0x01000000 /* End of Packet */
+#define IGC_TXD_CMD_IFCS 0x02000000 /* Insert FCS (Ethernet CRC) */
+#define IGC_TXD_CMD_IC 0x04000000 /* Insert Checksum */
+#define IGC_TXD_CMD_RS 0x08000000 /* Report Status */
+#define IGC_TXD_CMD_RPS 0x10000000 /* Report Packet Sent */
+#define IGC_TXD_CMD_DEXT 0x20000000 /* Desc extension (0 = legacy) */
+#define IGC_TXD_CMD_VLE 0x40000000 /* Add VLAN tag */
+#define IGC_TXD_CMD_IDE 0x80000000 /* Enable Tidv register */
+#define IGC_TXD_STAT_DD 0x00000001 /* Descriptor Done */
+#define IGC_TXD_CMD_TCP 0x01000000 /* TCP packet */
+#define IGC_TXD_CMD_IP 0x02000000 /* IP packet */
+#define IGC_TXD_CMD_TSE 0x04000000 /* TCP Seg enable */
+#define IGC_TXD_EXTCMD_TSTAMP 0x00000010 /* IEEE1588 Timestamp packet */
+
+/* Transmit Control */
+#define IGC_TCTL_EN 0x00000002 /* enable Tx */
+#define IGC_TCTL_PSP 0x00000008 /* pad short packets */
+#define IGC_TCTL_CT 0x00000ff0 /* collision threshold */
+#define IGC_TCTL_COLD 0x003ff000 /* collision distance */
+#define IGC_TCTL_RTLC 0x01000000 /* Re-transmit on late collision */
+#define IGC_TCTL_MULR 0x10000000 /* Multiple request support */
+
+/* Transmit Arbitration Count */
+#define IGC_TARC0_ENABLE 0x00000400 /* Enable Tx Queue 0 */
+
+/* SerDes Control */
+#define IGC_SCTL_DISABLE_SERDES_LOOPBACK 0x0400
+#define IGC_SCTL_ENABLE_SERDES_LOOPBACK 0x0410
+
+/* Receive Checksum Control */
+#define IGC_RXCSUM_IPOFL 0x00000100 /* IPv4 checksum offload */
+#define IGC_RXCSUM_TUOFL 0x00000200 /* TCP / UDP checksum offload */
+#define IGC_RXCSUM_CRCOFL 0x00000800 /* CRC32 offload enable */
+#define IGC_RXCSUM_IPPCSE 0x00001000 /* IP payload checksum enable */
+#define IGC_RXCSUM_PCSD 0x00002000 /* packet checksum disabled */
+
+/* GPY211 - I225 defines */
+#define GPY_MMD_MASK 0xFFFF0000
+#define GPY_MMD_SHIFT 16
+#define GPY_REG_MASK 0x0000FFFF
+/* Header split receive */
+#define IGC_RFCTL_NFSW_DIS 0x00000040
+#define IGC_RFCTL_NFSR_DIS 0x00000080
+#define IGC_RFCTL_ACK_DIS 0x00001000
+#define IGC_RFCTL_EXTEN 0x00008000
+#define IGC_RFCTL_IPV6_EX_DIS 0x00010000
+#define IGC_RFCTL_NEW_IPV6_EXT_DIS 0x00020000
+#define IGC_RFCTL_LEF 0x00040000
+
+/* Collision related configuration parameters */
+#define IGC_CT_SHIFT 4
+#define IGC_COLLISION_THRESHOLD 15
+#define IGC_COLLISION_DISTANCE 63
+#define IGC_COLD_SHIFT 12
+
+/* Default values for the transmit IPG register */
+#define DEFAULT_82543_TIPG_IPGT_FIBER 9
+#define DEFAULT_82543_TIPG_IPGT_COPPER 8
+
+#define IGC_TIPG_IPGT_MASK 0x000003FF
+
+#define DEFAULT_82543_TIPG_IPGR1 8
+#define IGC_TIPG_IPGR1_SHIFT 10
+
+#define DEFAULT_82543_TIPG_IPGR2 6
+#define DEFAULT_80003ES2LAN_TIPG_IPGR2 7
+#define IGC_TIPG_IPGR2_SHIFT 20
+
+/* Ethertype field values */
+#define ETHERNET_IEEE_VLAN_TYPE 0x8100 /* 802.3ac packet */
+
+#define ETHERNET_FCS_SIZE 4
+#define MAX_JUMBO_FRAME_SIZE 9216
+#define IGC_TX_PTR_GAP 0x1F
+
+/* Extended Configuration Control and Size */
+#define IGC_EXTCNF_CTRL_MDIO_SW_OWNERSHIP 0x00000020
+#define IGC_EXTCNF_CTRL_LCD_WRITE_ENABLE 0x00000001
+#define IGC_EXTCNF_CTRL_OEM_WRITE_ENABLE 0x00000008
+#define IGC_EXTCNF_CTRL_SWFLAG 0x00000020
+#define IGC_EXTCNF_CTRL_GATE_PHY_CFG 0x00000080
+#define IGC_EXTCNF_SIZE_EXT_PCIE_LENGTH_MASK 0x00FF0000
+#define IGC_EXTCNF_SIZE_EXT_PCIE_LENGTH_SHIFT 16
+#define IGC_EXTCNF_CTRL_EXT_CNF_POINTER_MASK 0x0FFF0000
+#define IGC_EXTCNF_CTRL_EXT_CNF_POINTER_SHIFT 16
+
+#define IGC_PHY_CTRL_D0A_LPLU 0x00000002
+#define IGC_PHY_CTRL_NOND0A_LPLU 0x00000004
+#define IGC_PHY_CTRL_NOND0A_GBE_DISABLE 0x00000008
+#define IGC_PHY_CTRL_GBE_DISABLE 0x00000040
+
+#define IGC_KABGTXD_BGSQLBIAS 0x00050000
+
+/* PBA constants */
+#define IGC_PBA_8K 0x0008 /* 8KB */
+#define IGC_PBA_10K 0x000A /* 10KB */
+#define IGC_PBA_12K 0x000C /* 12KB */
+#define IGC_PBA_14K 0x000E /* 14KB */
+#define IGC_PBA_16K 0x0010 /* 16KB */
+#define IGC_PBA_18K 0x0012
+#define IGC_PBA_20K 0x0014
+#define IGC_PBA_22K 0x0016
+#define IGC_PBA_24K 0x0018
+#define IGC_PBA_26K 0x001A
+#define IGC_PBA_30K 0x001E
+#define IGC_PBA_32K 0x0020
+#define IGC_PBA_34K 0x0022
+#define IGC_PBA_35K 0x0023
+#define IGC_PBA_38K 0x0026
+#define IGC_PBA_40K 0x0028
+#define IGC_PBA_48K 0x0030 /* 48KB */
+#define IGC_PBA_64K 0x0040 /* 64KB */
+
+#define IGC_PBA_RXA_MASK 0xFFFF
+
+#define IGC_PBS_16K IGC_PBA_16K
+
+/* Uncorrectable/correctable ECC Error counts and enable bits */
+#define IGC_PBECCSTS_CORR_ERR_CNT_MASK 0x000000FF
+#define IGC_PBECCSTS_UNCORR_ERR_CNT_MASK 0x0000FF00
+#define IGC_PBECCSTS_UNCORR_ERR_CNT_SHIFT 8
+#define IGC_PBECCSTS_ECC_ENABLE 0x00010000
+
+#define IFS_MAX 80
+#define IFS_MIN 40
+#define IFS_RATIO 4
+#define IFS_STEP 10
+#define MIN_NUM_XMITS 1000
+
+/* SW Semaphore Register */
+#define IGC_SWSM_SMBI 0x00000001 /* Driver Semaphore bit */
+#define IGC_SWSM_SWESMBI 0x00000002 /* FW Semaphore bit */
+#define IGC_SWSM_DRV_LOAD 0x00000008 /* Driver Loaded Bit */
+
+#define IGC_SWSM2_LOCK 0x00000002 /* Secondary driver semaphore bit */
+
+/* Interrupt Cause Read */
+#define IGC_ICR_TXDW 0x00000001 /* Transmit desc written back */
+#define IGC_ICR_TXQE 0x00000002 /* Transmit Queue empty */
+#define IGC_ICR_LSC 0x00000004 /* Link Status Change */
+#define IGC_ICR_RXSEQ 0x00000008 /* Rx sequence error */
+#define IGC_ICR_RXDMT0 0x00000010 /* Rx desc min. threshold (0) */
+#define IGC_ICR_RXO 0x00000040 /* Rx overrun */
+#define IGC_ICR_RXT0 0x00000080 /* Rx timer intr (ring 0) */
+#define IGC_ICR_RXCFG 0x00000400 /* Rx /c/ ordered set */
+#define IGC_ICR_GPI_EN0 0x00000800 /* GP Int 0 */
+#define IGC_ICR_GPI_EN1 0x00001000 /* GP Int 1 */
+#define IGC_ICR_GPI_EN2 0x00002000 /* GP Int 2 */
+#define IGC_ICR_GPI_EN3 0x00004000 /* GP Int 3 */
+#define IGC_ICR_TXD_LOW 0x00008000
+#define IGC_ICR_ECCER 0x00400000 /* Uncorrectable ECC Error */
+#define IGC_ICR_TS 0x00080000 /* Time Sync Interrupt */
+#define IGC_ICR_DRSTA 0x40000000 /* Device Reset Asserted */
+/* If this bit asserted, the driver should claim the interrupt */
+#define IGC_ICR_INT_ASSERTED 0x80000000
+#define IGC_ICR_DOUTSYNC 0x10000000 /* NIC DMA out of sync */
+#define IGC_ICR_FER 0x00400000 /* Fatal Error */
+
+
+
+/* Extended Interrupt Cause Read */
+#define IGC_EICR_RX_QUEUE0 0x00000001 /* Rx Queue 0 Interrupt */
+#define IGC_EICR_RX_QUEUE1 0x00000002 /* Rx Queue 1 Interrupt */
+#define IGC_EICR_RX_QUEUE2 0x00000004 /* Rx Queue 2 Interrupt */
+#define IGC_EICR_RX_QUEUE3 0x00000008 /* Rx Queue 3 Interrupt */
+#define IGC_EICR_TX_QUEUE0 0x00000100 /* Tx Queue 0 Interrupt */
+#define IGC_EICR_TX_QUEUE1 0x00000200 /* Tx Queue 1 Interrupt */
+#define IGC_EICR_TX_QUEUE2 0x00000400 /* Tx Queue 2 Interrupt */
+#define IGC_EICR_TX_QUEUE3 0x00000800 /* Tx Queue 3 Interrupt */
+#define IGC_EICR_TCP_TIMER 0x40000000 /* TCP Timer */
+#define IGC_EICR_OTHER 0x80000000 /* Interrupt Cause Active */
+/* TCP Timer */
+#define IGC_TCPTIMER_KS 0x00000100 /* KickStart */
+#define IGC_TCPTIMER_COUNT_ENABLE 0x00000200 /* Count Enable */
+#define IGC_TCPTIMER_COUNT_FINISH 0x00000400 /* Count finish */
+#define IGC_TCPTIMER_LOOP 0x00000800 /* Loop */
+
+/* This defines the bits that are set in the Interrupt Mask
+ * Set/Read Register. Each bit is documented below:
+ * o RXT0 = Receiver Timer Interrupt (ring 0)
+ * o TXDW = Transmit Descriptor Written Back
+ * o RXDMT0 = Receive Descriptor Minimum Threshold hit (ring 0)
+ * o RXSEQ = Receive Sequence Error
+ * o LSC = Link Status Change
+ */
+#define IMS_ENABLE_MASK ( \
+ IGC_IMS_RXT0 | \
+ IGC_IMS_TXDW | \
+ IGC_IMS_RXDMT0 | \
+ IGC_IMS_RXSEQ | \
+ IGC_IMS_LSC)
+
+/* Interrupt Mask Set */
+#define IGC_IMS_TXDW IGC_ICR_TXDW /* Tx desc written back */
+#define IGC_IMS_LSC IGC_ICR_LSC /* Link Status Change */
+#define IGC_IMS_RXSEQ IGC_ICR_RXSEQ /* Rx sequence error */
+#define IGC_IMS_RXDMT0 IGC_ICR_RXDMT0 /* Rx desc min. threshold */
+#define IGC_QVECTOR_MASK 0x7FFC /* Q-vector mask */
+#define IGC_ITR_VAL_MASK 0x04 /* ITR value mask */
+#define IGC_IMS_RXO IGC_ICR_RXO /* Rx overrun */
+#define IGC_IMS_RXT0 IGC_ICR_RXT0 /* Rx timer intr */
+#define IGC_IMS_TXD_LOW IGC_ICR_TXD_LOW
+#define IGC_IMS_ECCER IGC_ICR_ECCER /* Uncorrectable ECC Error */
+#define IGC_IMS_TS IGC_ICR_TS /* Time Sync Interrupt */
+#define IGC_IMS_DRSTA IGC_ICR_DRSTA /* Device Reset Asserted */
+#define IGC_IMS_DOUTSYNC IGC_ICR_DOUTSYNC /* NIC DMA out of sync */
+#define IGC_IMS_FER IGC_ICR_FER /* Fatal Error */
+
+#define IGC_IMS_THS IGC_ICR_THS /* ICR.TS: Thermal Sensor Event*/
+#define IGC_IMS_MDDET IGC_ICR_MDDET /* Malicious Driver Detect */
+/* Extended Interrupt Mask Set */
+#define IGC_EIMS_RX_QUEUE0 IGC_EICR_RX_QUEUE0 /* Rx Queue 0 Interrupt */
+#define IGC_EIMS_RX_QUEUE1 IGC_EICR_RX_QUEUE1 /* Rx Queue 1 Interrupt */
+#define IGC_EIMS_RX_QUEUE2 IGC_EICR_RX_QUEUE2 /* Rx Queue 2 Interrupt */
+#define IGC_EIMS_RX_QUEUE3 IGC_EICR_RX_QUEUE3 /* Rx Queue 3 Interrupt */
+#define IGC_EIMS_TX_QUEUE0 IGC_EICR_TX_QUEUE0 /* Tx Queue 0 Interrupt */
+#define IGC_EIMS_TX_QUEUE1 IGC_EICR_TX_QUEUE1 /* Tx Queue 1 Interrupt */
+#define IGC_EIMS_TX_QUEUE2 IGC_EICR_TX_QUEUE2 /* Tx Queue 2 Interrupt */
+#define IGC_EIMS_TX_QUEUE3 IGC_EICR_TX_QUEUE3 /* Tx Queue 3 Interrupt */
+#define IGC_EIMS_TCP_TIMER IGC_EICR_TCP_TIMER /* TCP Timer */
+#define IGC_EIMS_OTHER IGC_EICR_OTHER /* Interrupt Cause Active */
+
+/* Interrupt Cause Set */
+#define IGC_ICS_LSC IGC_ICR_LSC /* Link Status Change */
+#define IGC_ICS_RXSEQ IGC_ICR_RXSEQ /* Rx sequence error */
+#define IGC_ICS_RXDMT0 IGC_ICR_RXDMT0 /* Rx desc min. threshold */
+
+/* Extended Interrupt Cause Set */
+#define IGC_EICS_RX_QUEUE0 IGC_EICR_RX_QUEUE0 /* Rx Queue 0 Interrupt */
+#define IGC_EICS_RX_QUEUE1 IGC_EICR_RX_QUEUE1 /* Rx Queue 1 Interrupt */
+#define IGC_EICS_RX_QUEUE2 IGC_EICR_RX_QUEUE2 /* Rx Queue 2 Interrupt */
+#define IGC_EICS_RX_QUEUE3 IGC_EICR_RX_QUEUE3 /* Rx Queue 3 Interrupt */
+#define IGC_EICS_TX_QUEUE0 IGC_EICR_TX_QUEUE0 /* Tx Queue 0 Interrupt */
+#define IGC_EICS_TX_QUEUE1 IGC_EICR_TX_QUEUE1 /* Tx Queue 1 Interrupt */
+#define IGC_EICS_TX_QUEUE2 IGC_EICR_TX_QUEUE2 /* Tx Queue 2 Interrupt */
+#define IGC_EICS_TX_QUEUE3 IGC_EICR_TX_QUEUE3 /* Tx Queue 3 Interrupt */
+#define IGC_EICS_TCP_TIMER IGC_EICR_TCP_TIMER /* TCP Timer */
+#define IGC_EICS_OTHER IGC_EICR_OTHER /* Interrupt Cause Active */
+
+#define IGC_EITR_ITR_INT_MASK 0x0000FFFF
+#define IGC_EITR_INTERVAL 0x00007FFC
+/* IGC_EITR_CNT_IGNR is only for 82576 and newer */
+#define IGC_EITR_CNT_IGNR 0x80000000 /* Don't reset counters on write */
+
+/* Transmit Descriptor Control */
+#define IGC_TXDCTL_PTHRESH 0x0000003F /* TXDCTL Prefetch Threshold */
+#define IGC_TXDCTL_HTHRESH 0x00003F00 /* TXDCTL Host Threshold */
+#define IGC_TXDCTL_WTHRESH 0x003F0000 /* TXDCTL Writeback Threshold */
+#define IGC_TXDCTL_GRAN 0x01000000 /* TXDCTL Granularity */
+#define IGC_TXDCTL_FULL_TX_DESC_WB 0x01010000 /* GRAN=1, WTHRESH=1 */
+#define IGC_TXDCTL_MAX_TX_DESC_PREFETCH 0x0100001F /* GRAN=1, PTHRESH=31 */
+/* Enable the counting of descriptors still to be processed. */
+#define IGC_TXDCTL_COUNT_DESC 0x00400000
+
+/* Flow Control Constants */
+#define FLOW_CONTROL_ADDRESS_LOW 0x00C28001
+#define FLOW_CONTROL_ADDRESS_HIGH 0x00000100
+#define FLOW_CONTROL_TYPE 0x8808
+
+/* 802.1q VLAN Packet Size */
+#define VLAN_TAG_SIZE 4 /* 802.3ac tag (not DMA'd) */
+#define IGC_VLAN_FILTER_TBL_SIZE 128 /* VLAN Filter Table (4096 bits) */
+
+/* Receive Address
+ * Number of high/low register pairs in the RAR. The RAR (Receive Address
+ * Registers) holds the directed and multicast addresses that we monitor.
+ * Technically, we have 16 spots. However, we reserve one of these spots
+ * (RAR[15]) for our directed address used by controllers with
+ * manageability enabled, allowing us room for 15 multicast addresses.
+ */
+#define IGC_RAR_ENTRIES 15
+#define IGC_RAH_AV 0x80000000 /* Receive descriptor valid */
+#define IGC_RAL_MAC_ADDR_LEN 4
+#define IGC_RAH_MAC_ADDR_LEN 2
+
+/* Error Codes */
+#define IGC_SUCCESS 0
+#define IGC_ERR_NVM 1
+#define IGC_ERR_PHY 2
+#define IGC_ERR_CONFIG 3
+#define IGC_ERR_PARAM 4
+#define IGC_ERR_MAC_INIT 5
+#define IGC_ERR_PHY_TYPE 6
+#define IGC_ERR_RESET 9
+#define IGC_ERR_MASTER_REQUESTS_PENDING 10
+#define IGC_ERR_HOST_INTERFACE_COMMAND 11
+#define IGC_BLK_PHY_RESET 12
+#define IGC_ERR_SWFW_SYNC 13
+#define IGC_NOT_IMPLEMENTED 14
+#define IGC_ERR_MBX 15
+#define IGC_ERR_INVALID_ARGUMENT 16
+#define IGC_ERR_NO_SPACE 17
+#define IGC_ERR_NVM_PBA_SECTION 18
+#define IGC_ERR_INVM_VALUE_NOT_FOUND 20
+
+/* Loop limit on how long we wait for auto-negotiation to complete */
+#define COPPER_LINK_UP_LIMIT 10
+#define PHY_AUTO_NEG_LIMIT 45
+/* Number of 100 microseconds we wait for PCI Express master disable */
+#define MASTER_DISABLE_TIMEOUT 800
+/* Number of milliseconds we wait for PHY configuration done after MAC reset */
+#define PHY_CFG_TIMEOUT 100
+/* Number of 2 milliseconds we wait for acquiring MDIO ownership. */
+#define MDIO_OWNERSHIP_TIMEOUT 10
+/* Number of milliseconds for NVM auto read done after MAC reset. */
+#define AUTO_READ_DONE_TIMEOUT 10
+
+/* Flow Control */
+#define IGC_FCRTH_RTH 0x0000FFF8 /* Mask Bits[15:3] for RTH */
+#define IGC_FCRTL_RTL 0x0000FFF8 /* Mask Bits[15:3] for RTL */
+#define IGC_FCRTL_XONE 0x80000000 /* Enable XON frame transmission */
+
+/* Transmit Configuration Word */
+#define IGC_TXCW_FD 0x00000020 /* TXCW full duplex */
+#define IGC_TXCW_PAUSE 0x00000080 /* TXCW sym pause request */
+#define IGC_TXCW_ASM_DIR 0x00000100 /* TXCW astm pause direction */
+#define IGC_TXCW_PAUSE_MASK 0x00000180 /* TXCW pause request mask */
+#define IGC_TXCW_ANE 0x80000000 /* Auto-neg enable */
+
+/* Receive Configuration Word */
+#define IGC_RXCW_CW 0x0000ffff /* RxConfigWord mask */
+#define IGC_RXCW_IV 0x08000000 /* Receive config invalid */
+#define IGC_RXCW_C 0x20000000 /* Receive config */
+#define IGC_RXCW_SYNCH 0x40000000 /* Receive config synch */
+
+#define IGC_TSYNCTXCTL_TXTT_0 0x00000001 /* Tx timestamp reg 0 valid */
+#define IGC_TSYNCTXCTL_ENABLED 0x00000010 /* enable Tx timestamping */
+
+#define IGC_TSYNCRXCTL_VALID 0x00000001 /* Rx timestamp valid */
+#define IGC_TSYNCRXCTL_TYPE_MASK 0x0000000E /* Rx type mask */
+#define IGC_TSYNCRXCTL_TYPE_L2_V2 0x00
+#define IGC_TSYNCRXCTL_TYPE_L4_V1 0x02
+#define IGC_TSYNCRXCTL_TYPE_L2_L4_V2 0x04
+#define IGC_TSYNCRXCTL_TYPE_ALL 0x08
+#define IGC_TSYNCRXCTL_TYPE_EVENT_V2 0x0A
+#define IGC_TSYNCRXCTL_ENABLED 0x00000010 /* enable Rx timestamping */
+#define IGC_TSYNCRXCTL_SYSCFI 0x00000020 /* Sys clock frequency */
+
+#define IGC_TSYNCRXCFG_PTP_V1_CTRLT_MASK 0x000000FF
+#define IGC_TSYNCRXCFG_PTP_V1_SYNC_MESSAGE 0x00
+#define IGC_TSYNCRXCFG_PTP_V1_DELAY_REQ_MESSAGE 0x01
+#define IGC_TSYNCRXCFG_PTP_V1_FOLLOWUP_MESSAGE 0x02
+#define IGC_TSYNCRXCFG_PTP_V1_DELAY_RESP_MESSAGE 0x03
+#define IGC_TSYNCRXCFG_PTP_V1_MANAGEMENT_MESSAGE 0x04
+
+#define IGC_TSYNCRXCFG_PTP_V2_MSGID_MASK 0x00000F00
+#define IGC_TSYNCRXCFG_PTP_V2_SYNC_MESSAGE 0x0000
+#define IGC_TSYNCRXCFG_PTP_V2_DELAY_REQ_MESSAGE 0x0100
+#define IGC_TSYNCRXCFG_PTP_V2_PATH_DELAY_REQ_MESSAGE 0x0200
+#define IGC_TSYNCRXCFG_PTP_V2_PATH_DELAY_RESP_MESSAGE 0x0300
+#define IGC_TSYNCRXCFG_PTP_V2_FOLLOWUP_MESSAGE 0x0800
+#define IGC_TSYNCRXCFG_PTP_V2_DELAY_RESP_MESSAGE 0x0900
+#define IGC_TSYNCRXCFG_PTP_V2_PATH_DELAY_FOLLOWUP_MESSAGE 0x0A00
+#define IGC_TSYNCRXCFG_PTP_V2_ANNOUNCE_MESSAGE 0x0B00
+#define IGC_TSYNCRXCFG_PTP_V2_SIGNALLING_MESSAGE 0x0C00
+#define IGC_TSYNCRXCFG_PTP_V2_MANAGEMENT_MESSAGE 0x0D00
+
+#define IGC_TIMINCA_16NS_SHIFT 24
+#define IGC_TIMINCA_INCPERIOD_SHIFT 24
+#define IGC_TIMINCA_INCVALUE_MASK 0x00FFFFFF
+
+/* Time Sync Interrupt Cause/Mask Register Bits */
+#define TSINTR_SYS_WRAP (1 << 0) /* SYSTIM Wrap around. */
+#define TSINTR_TXTS (1 << 1) /* Transmit Timestamp. */
+#define TSINTR_TT0 (1 << 3) /* Target Time 0 Trigger. */
+#define TSINTR_TT1 (1 << 4) /* Target Time 1 Trigger. */
+#define TSINTR_AUTT0 (1 << 5) /* Auxiliary Timestamp 0 Taken. */
+#define TSINTR_AUTT1 (1 << 6) /* Auxiliary Timestamp 1 Taken. */
+
+#define TSYNC_INTERRUPTS TSINTR_TXTS
+
+/* TSAUXC Configuration Bits */
+#define TSAUXC_EN_TT0 (1 << 0) /* Enable target time 0. */
+#define TSAUXC_EN_TT1 (1 << 1) /* Enable target time 1. */
+#define TSAUXC_EN_CLK0 (1 << 2) /* Enable Configurable Frequency Clock 0. */
+#define TSAUXC_ST0 (1 << 4) /* Start Clock 0 Toggle on Target Time 0. */
+#define TSAUXC_EN_CLK1 (1 << 5) /* Enable Configurable Frequency Clock 1. */
+#define TSAUXC_ST1 (1 << 7) /* Start Clock 1 Toggle on Target Time 1. */
+#define TSAUXC_EN_TS0 (1 << 8) /* Enable hardware timestamp 0. */
+#define TSAUXC_EN_TS1 (1 << 10) /* Enable hardware timestamp 0. */
+
+/* SDP Configuration Bits */
+#define AUX0_SEL_SDP0 (0u << 0) /* Assign SDP0 to auxiliary time stamp 0. */
+#define AUX0_SEL_SDP1 (1u << 0) /* Assign SDP1 to auxiliary time stamp 0. */
+#define AUX0_SEL_SDP2 (2u << 0) /* Assign SDP2 to auxiliary time stamp 0. */
+#define AUX0_SEL_SDP3 (3u << 0) /* Assign SDP3 to auxiliary time stamp 0. */
+#define AUX0_TS_SDP_EN (1u << 2) /* Enable auxiliary time stamp trigger 0. */
+#define AUX1_SEL_SDP0 (0u << 3) /* Assign SDP0 to auxiliary time stamp 1. */
+#define AUX1_SEL_SDP1 (1u << 3) /* Assign SDP1 to auxiliary time stamp 1. */
+#define AUX1_SEL_SDP2 (2u << 3) /* Assign SDP2 to auxiliary time stamp 1. */
+#define AUX1_SEL_SDP3 (3u << 3) /* Assign SDP3 to auxiliary time stamp 1. */
+#define AUX1_TS_SDP_EN (1u << 5) /* Enable auxiliary time stamp trigger 1. */
+#define TS_SDP0_EN (1u << 8) /* SDP0 is assigned to Tsync. */
+#define TS_SDP1_EN (1u << 11) /* SDP1 is assigned to Tsync. */
+#define TS_SDP2_EN (1u << 14) /* SDP2 is assigned to Tsync. */
+#define TS_SDP3_EN (1u << 17) /* SDP3 is assigned to Tsync. */
+#define TS_SDP0_SEL_TT0 (0u << 6) /* Target time 0 is output on SDP0. */
+#define TS_SDP0_SEL_TT1 (1u << 6) /* Target time 1 is output on SDP0. */
+#define TS_SDP1_SEL_TT0 (0u << 9) /* Target time 0 is output on SDP1. */
+#define TS_SDP1_SEL_TT1 (1u << 9) /* Target time 1 is output on SDP1. */
+#define TS_SDP0_SEL_FC0 (2u << 6) /* Freq clock 0 is output on SDP0. */
+#define TS_SDP0_SEL_FC1 (3u << 6) /* Freq clock 1 is output on SDP0. */
+#define TS_SDP1_SEL_FC0 (2u << 9) /* Freq clock 0 is output on SDP1. */
+#define TS_SDP1_SEL_FC1 (3u << 9) /* Freq clock 1 is output on SDP1. */
+#define TS_SDP2_SEL_TT0 (0u << 12) /* Target time 0 is output on SDP2. */
+#define TS_SDP2_SEL_TT1 (1u << 12) /* Target time 1 is output on SDP2. */
+#define TS_SDP2_SEL_FC0 (2u << 12) /* Freq clock 0 is output on SDP2. */
+#define TS_SDP2_SEL_FC1 (3u << 12) /* Freq clock 1 is output on SDP2. */
+#define TS_SDP3_SEL_TT0 (0u << 15) /* Target time 0 is output on SDP3. */
+#define TS_SDP3_SEL_TT1 (1u << 15) /* Target time 1 is output on SDP3. */
+#define TS_SDP3_SEL_FC0 (2u << 15) /* Freq clock 0 is output on SDP3. */
+#define TS_SDP3_SEL_FC1 (3u << 15) /* Freq clock 1 is output on SDP3. */
+
+#define IGC_CTRL_SDP0_DIR 0x00400000 /* SDP0 Data direction */
+#define IGC_CTRL_SDP1_DIR 0x00800000 /* SDP1 Data direction */
+
+/* Extended Device Control */
+#define IGC_CTRL_EXT_SDP2_DIR 0x00000400 /* SDP2 Data direction */
+
+/* ETQF register bit definitions */
+#define IGC_ETQF_1588 (1 << 30)
+#define IGC_FTQF_VF_BP 0x00008000
+#define IGC_FTQF_1588_TIME_STAMP 0x08000000
+#define IGC_FTQF_MASK 0xF0000000
+#define IGC_FTQF_MASK_PROTO_BP 0x10000000
+/* Immediate Interrupt Rx (A.K.A. Low Latency Interrupt) */
+#define IGC_IMIREXT_CTRL_BP 0x00080000 /* Bypass check of ctrl bits */
+#define IGC_IMIREXT_SIZE_BP 0x00001000 /* Packet size bypass */
+
+#define IGC_RXDADV_STAT_TSIP 0x08000 /* timestamp in packet */
+#define IGC_TSICR_TXTS 0x00000002
+#define IGC_TSIM_TXTS 0x00000002
+/* TUPLE Filtering Configuration */
+#define IGC_TTQF_DISABLE_MASK 0xF0008000 /* TTQF Disable Mask */
+#define IGC_TTQF_QUEUE_ENABLE 0x100 /* TTQF Queue Enable Bit */
+#define IGC_TTQF_PROTOCOL_MASK 0xFF /* TTQF Protocol Mask */
+/* TTQF TCP Bit, shift with IGC_TTQF_PROTOCOL SHIFT */
+#define IGC_TTQF_PROTOCOL_TCP 0x0
+/* TTQF UDP Bit, shift with IGC_TTQF_PROTOCOL_SHIFT */
+#define IGC_TTQF_PROTOCOL_UDP 0x1
+/* TTQF SCTP Bit, shift with IGC_TTQF_PROTOCOL_SHIFT */
+#define IGC_TTQF_PROTOCOL_SCTP 0x2
+#define IGC_TTQF_PROTOCOL_SHIFT 5 /* TTQF Protocol Shift */
+#define IGC_TTQF_QUEUE_SHIFT 16 /* TTQF Queue Shfit */
+#define IGC_TTQF_RX_QUEUE_MASK 0x70000 /* TTQF Queue Mask */
+#define IGC_TTQF_MASK_ENABLE 0x10000000 /* TTQF Mask Enable Bit */
+#define IGC_IMIR_CLEAR_MASK 0xF001FFFF /* IMIR Reg Clear Mask */
+#define IGC_IMIR_PORT_BYPASS 0x20000 /* IMIR Port Bypass Bit */
+#define IGC_IMIR_PRIORITY_SHIFT 29 /* IMIR Priority Shift */
+#define IGC_IMIREXT_CLEAR_MASK 0x7FFFF /* IMIREXT Reg Clear Mask */
+
+#define IGC_MDICNFG_EXT_MDIO 0x80000000 /* MDI ext/int destination */
+#define IGC_MDICNFG_COM_MDIO 0x40000000 /* MDI shared w/ lan 0 */
+#define IGC_MDICNFG_PHY_MASK 0x03E00000
+#define IGC_MDICNFG_PHY_SHIFT 21
+
+#define IGC_MEDIA_PORT_COPPER 1
+#define IGC_MEDIA_PORT_OTHER 2
+#define IGC_M88E1112_AUTO_COPPER_SGMII 0x2
+#define IGC_M88E1112_AUTO_COPPER_BASEX 0x3
+#define IGC_M88E1112_STATUS_LINK 0x0004 /* Interface Link Bit */
+#define IGC_M88E1112_MAC_CTRL_1 0x10
+#define IGC_M88E1112_MAC_CTRL_1_MODE_MASK 0x0380 /* Mode Select */
+#define IGC_M88E1112_MAC_CTRL_1_MODE_SHIFT 7
+#define IGC_M88E1112_PAGE_ADDR 0x16
+#define IGC_M88E1112_STATUS 0x01
+
+#define IGC_THSTAT_LOW_EVENT 0x20000000 /* Low thermal threshold */
+#define IGC_THSTAT_MID_EVENT 0x00200000 /* Mid thermal threshold */
+#define IGC_THSTAT_HIGH_EVENT 0x00002000 /* High thermal threshold */
+#define IGC_THSTAT_PWR_DOWN 0x00000001 /* Power Down Event */
+#define IGC_THSTAT_LINK_THROTTLE 0x00000002 /* Link Spd Throttle Event */
+
+/* EEE defines */
+#define IGC_IPCNFG_EEE_2_5G_AN 0x00000010 /* IPCNFG EEE Ena 2.5G AN */
+#define IGC_IPCNFG_EEE_1G_AN 0x00000008 /* IPCNFG EEE Ena 1G AN */
+#define IGC_IPCNFG_EEE_100M_AN 0x00000004 /* IPCNFG EEE Ena 100M AN */
+#define IGC_EEER_TX_LPI_EN 0x00010000 /* EEER Tx LPI Enable */
+#define IGC_EEER_RX_LPI_EN 0x00020000 /* EEER Rx LPI Enable */
+#define IGC_EEER_LPI_FC 0x00040000 /* EEER Ena on Flow Cntrl */
+/* EEE status */
+#define IGC_EEER_EEE_NEG 0x20000000 /* EEE capability nego */
+#define IGC_EEER_RX_LPI_STATUS 0x40000000 /* Rx in LPI state */
+#define IGC_EEER_TX_LPI_STATUS 0x80000000 /* Tx in LPI state */
+#define IGC_EEE_LP_ADV_ADDR_I350 0x040F /* EEE LP Advertisement */
+#define IGC_M88E1543_PAGE_ADDR 0x16 /* Page Offset Register */
+#define IGC_M88E1543_EEE_CTRL_1 0x0
+#define IGC_M88E1543_EEE_CTRL_1_MS 0x0001 /* EEE Master/Slave */
+#define IGC_M88E1543_FIBER_CTRL 0x0 /* Fiber Control Register */
+#define IGC_EEE_ADV_DEV_I354 7
+#define IGC_EEE_ADV_ADDR_I354 60
+#define IGC_EEE_ADV_100_SUPPORTED (1 << 1) /* 100BaseTx EEE Supported */
+#define IGC_EEE_ADV_1000_SUPPORTED (1 << 2) /* 1000BaseT EEE Supported */
+#define IGC_PCS_STATUS_DEV_I354 3
+#define IGC_PCS_STATUS_ADDR_I354 1
+#define IGC_PCS_STATUS_RX_LPI_RCVD 0x0400
+#define IGC_PCS_STATUS_TX_LPI_RCVD 0x0800
+#define IGC_M88E1512_CFG_REG_1 0x0010
+#define IGC_M88E1512_CFG_REG_2 0x0011
+#define IGC_M88E1512_CFG_REG_3 0x0007
+#define IGC_M88E1512_MODE 0x0014
+#define IGC_EEE_SU_LPI_CLK_STP 0x00800000 /* EEE LPI Clock Stop */
+#define IGC_EEE_LP_ADV_DEV_I225 7 /* EEE LP Adv Device */
+#define IGC_EEE_LP_ADV_ADDR_I225 61 /* EEE LP Adv Register */
+
+#define IGC_MMDAC_FUNC_DATA 0x4000 /* Data, no post increment */
+
+/* PHY Control Register */
+#define MII_CR_SPEED_SELECT_MSB 0x0040 /* bits 6,13: 10=1000, 01=100, 00=10 */
+#define MII_CR_COLL_TEST_ENABLE 0x0080 /* Collision test enable */
+#define MII_CR_FULL_DUPLEX 0x0100 /* FDX =1, half duplex =0 */
+#define MII_CR_RESTART_AUTO_NEG 0x0200 /* Restart auto negotiation */
+#define MII_CR_ISOLATE 0x0400 /* Isolate PHY from MII */
+#define MII_CR_POWER_DOWN 0x0800 /* Power down */
+#define MII_CR_AUTO_NEG_EN 0x1000 /* Auto Neg Enable */
+#define MII_CR_SPEED_SELECT_LSB 0x2000 /* bits 6,13: 10=1000, 01=100, 00=10 */
+#define MII_CR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */
+#define MII_CR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */
+#define MII_CR_SPEED_1000 0x0040
+#define MII_CR_SPEED_100 0x2000
+#define MII_CR_SPEED_10 0x0000
+
+/* PHY Status Register */
+#define MII_SR_EXTENDED_CAPS 0x0001 /* Extended register capabilities */
+#define MII_SR_JABBER_DETECT 0x0002 /* Jabber Detected */
+#define MII_SR_LINK_STATUS 0x0004 /* Link Status 1 = link */
+#define MII_SR_AUTONEG_CAPS 0x0008 /* Auto Neg Capable */
+#define MII_SR_REMOTE_FAULT 0x0010 /* Remote Fault Detect */
+#define MII_SR_AUTONEG_COMPLETE 0x0020 /* Auto Neg Complete */
+#define MII_SR_PREAMBLE_SUPPRESS 0x0040 /* Preamble may be suppressed */
+#define MII_SR_EXTENDED_STATUS 0x0100 /* Ext. status info in Reg 0x0F */
+#define MII_SR_100T2_HD_CAPS 0x0200 /* 100T2 Half Duplex Capable */
+#define MII_SR_100T2_FD_CAPS 0x0400 /* 100T2 Full Duplex Capable */
+#define MII_SR_10T_HD_CAPS 0x0800 /* 10T Half Duplex Capable */
+#define MII_SR_10T_FD_CAPS 0x1000 /* 10T Full Duplex Capable */
+#define MII_SR_100X_HD_CAPS 0x2000 /* 100X Half Duplex Capable */
+#define MII_SR_100X_FD_CAPS 0x4000 /* 100X Full Duplex Capable */
+#define MII_SR_100T4_CAPS 0x8000 /* 100T4 Capable */
+
+/* Autoneg Advertisement Register */
+#define NWAY_AR_SELECTOR_FIELD 0x0001 /* indicates IEEE 802.3 CSMA/CD */
+#define NWAY_AR_10T_HD_CAPS 0x0020 /* 10T Half Duplex Capable */
+#define NWAY_AR_10T_FD_CAPS 0x0040 /* 10T Full Duplex Capable */
+#define NWAY_AR_100TX_HD_CAPS 0x0080 /* 100TX Half Duplex Capable */
+#define NWAY_AR_100TX_FD_CAPS 0x0100 /* 100TX Full Duplex Capable */
+#define NWAY_AR_100T4_CAPS 0x0200 /* 100T4 Capable */
+#define NWAY_AR_PAUSE 0x0400 /* Pause operation desired */
+#define NWAY_AR_ASM_DIR 0x0800 /* Asymmetric Pause Direction bit */
+#define NWAY_AR_REMOTE_FAULT 0x2000 /* Remote Fault detected */
+#define NWAY_AR_NEXT_PAGE 0x8000 /* Next Page ability supported */
+
+/* Link Partner Ability Register (Base Page) */
+#define NWAY_LPAR_SELECTOR_FIELD 0x0000 /* LP protocol selector field */
+#define NWAY_LPAR_10T_HD_CAPS 0x0020 /* LP 10T Half Dplx Capable */
+#define NWAY_LPAR_10T_FD_CAPS 0x0040 /* LP 10T Full Dplx Capable */
+#define NWAY_LPAR_100TX_HD_CAPS 0x0080 /* LP 100TX Half Dplx Capable */
+#define NWAY_LPAR_100TX_FD_CAPS 0x0100 /* LP 100TX Full Dplx Capable */
+#define NWAY_LPAR_100T4_CAPS 0x0200 /* LP is 100T4 Capable */
+#define NWAY_LPAR_PAUSE 0x0400 /* LP Pause operation desired */
+#define NWAY_LPAR_ASM_DIR 0x0800 /* LP Asym Pause Direction bit */
+#define NWAY_LPAR_REMOTE_FAULT 0x2000 /* LP detected Remote Fault */
+#define NWAY_LPAR_ACKNOWLEDGE 0x4000 /* LP rx'd link code word */
+#define NWAY_LPAR_NEXT_PAGE 0x8000 /* Next Page ability supported */
+
+/* Autoneg Expansion Register */
+#define NWAY_ER_LP_NWAY_CAPS 0x0001 /* LP has Auto Neg Capability */
+#define NWAY_ER_PAGE_RXD 0x0002 /* LP 10T Half Dplx Capable */
+#define NWAY_ER_NEXT_PAGE_CAPS 0x0004 /* LP 10T Full Dplx Capable */
+#define NWAY_ER_LP_NEXT_PAGE_CAPS 0x0008 /* LP 100TX Half Dplx Capable */
+#define NWAY_ER_PAR_DETECT_FAULT 0x0010 /* LP 100TX Full Dplx Capable */
+
+/* 1000BASE-T Control Register */
+#define CR_1000T_ASYM_PAUSE 0x0080 /* Advertise asymmetric pause bit */
+#define CR_1000T_HD_CAPS 0x0100 /* Advertise 1000T HD capability */
+#define CR_1000T_FD_CAPS 0x0200 /* Advertise 1000T FD capability */
+/* 1=Repeater/switch device port 0=DTE device */
+#define CR_1000T_REPEATER_DTE 0x0400
+/* 1=Configure PHY as Master 0=Configure PHY as Slave */
+#define CR_1000T_MS_VALUE 0x0800
+/* 1=Master/Slave manual config value 0=Automatic Master/Slave config */
+#define CR_1000T_MS_ENABLE 0x1000
+#define CR_1000T_TEST_MODE_NORMAL 0x0000 /* Normal Operation */
+#define CR_1000T_TEST_MODE_1 0x2000 /* Transmit Waveform test */
+#define CR_1000T_TEST_MODE_2 0x4000 /* Master Transmit Jitter test */
+#define CR_1000T_TEST_MODE_3 0x6000 /* Slave Transmit Jitter test */
+#define CR_1000T_TEST_MODE_4 0x8000 /* Transmitter Distortion test */
+
+/* 1000BASE-T Status Register */
+#define SR_1000T_IDLE_ERROR_CNT 0x00FF /* Num idle err since last rd */
+#define SR_1000T_ASYM_PAUSE_DIR 0x0100 /* LP asym pause direction bit */
+#define SR_1000T_LP_HD_CAPS 0x0400 /* LP is 1000T HD capable */
+#define SR_1000T_LP_FD_CAPS 0x0800 /* LP is 1000T FD capable */
+#define SR_1000T_REMOTE_RX_STATUS 0x1000 /* Remote receiver OK */
+#define SR_1000T_LOCAL_RX_STATUS 0x2000 /* Local receiver OK */
+#define SR_1000T_MS_CONFIG_RES 0x4000 /* 1=Local Tx Master, 0=Slave */
+#define SR_1000T_MS_CONFIG_FAULT 0x8000 /* Master/Slave config fault */
+
+#define SR_1000T_PHY_EXCESSIVE_IDLE_ERR_COUNT 5
+
+/* PHY 1000 MII Register/Bit Definitions */
+/* PHY Registers defined by IEEE */
+#define PHY_CONTROL 0x00 /* Control Register */
+#define PHY_STATUS 0x01 /* Status Register */
+#define PHY_ID1 0x02 /* Phy Id Reg (word 1) */
+#define PHY_ID2 0x03 /* Phy Id Reg (word 2) */
+#define PHY_AUTONEG_ADV 0x04 /* Autoneg Advertisement */
+#define PHY_LP_ABILITY 0x05 /* Link Partner Ability (Base Page) */
+#define PHY_AUTONEG_EXP 0x06 /* Autoneg Expansion Reg */
+#define PHY_NEXT_PAGE_TX 0x07 /* Next Page Tx */
+#define PHY_LP_NEXT_PAGE 0x08 /* Link Partner Next Page */
+#define PHY_1000T_CTRL 0x09 /* 1000Base-T Control Reg */
+#define PHY_1000T_STATUS 0x0A /* 1000Base-T Status Reg */
+#define PHY_EXT_STATUS 0x0F /* Extended Status Reg */
+
+/* PHY GPY 211 registers */
+#define STANDARD_AN_REG_MASK 0x0007 /* MMD */
+#define ANEG_MULTIGBT_AN_CTRL 0x0020 /* MULTI GBT AN Control Register */
+#define MMD_DEVADDR_SHIFT 16 /* Shift MMD to higher bits */
+#define CR_2500T_FD_CAPS 0x0080 /* Advertise 2500T FD capability */
+
+#define PHY_CONTROL_LB 0x4000 /* PHY Loopback bit */
+
+/* NVM Control */
+#define IGC_EECD_SK 0x00000001 /* NVM Clock */
+#define IGC_EECD_CS 0x00000002 /* NVM Chip Select */
+#define IGC_EECD_DI 0x00000004 /* NVM Data In */
+#define IGC_EECD_DO 0x00000008 /* NVM Data Out */
+#define IGC_EECD_REQ 0x00000040 /* NVM Access Request */
+#define IGC_EECD_GNT 0x00000080 /* NVM Access Grant */
+#define IGC_EECD_PRES 0x00000100 /* NVM Present */
+#define IGC_EECD_SIZE 0x00000200 /* NVM Size (0=64 word 1=256 word) */
+/* NVM Addressing bits based on type 0=small, 1=large */
+#define IGC_EECD_ADDR_BITS 0x00000400
+#define IGC_NVM_GRANT_ATTEMPTS 1000 /* NVM # attempts to gain grant */
+#define IGC_EECD_AUTO_RD 0x00000200 /* NVM Auto Read done */
+#define IGC_EECD_SIZE_EX_MASK 0x00007800 /* NVM Size */
+#define IGC_EECD_SIZE_EX_SHIFT 11
+#define IGC_EECD_FLUPD 0x00080000 /* Update FLASH */
+#define IGC_EECD_AUPDEN 0x00100000 /* Ena Auto FLASH update */
+#define IGC_EECD_SEC1VAL 0x00400000 /* Sector One Valid */
+#define IGC_EECD_SEC1VAL_VALID_MASK (IGC_EECD_AUTO_RD | IGC_EECD_PRES)
+
+#define IGC_EECD_FLUPD_I225 0x00800000 /* Update FLASH */
+#define IGC_EECD_FLUDONE_I225 0x04000000 /* Update FLASH done */
+#define IGC_EECD_FLASH_DETECTED_I225 0x00080000 /* FLASH detected */
+#define IGC_FLUDONE_ATTEMPTS 20000
+#define IGC_EERD_EEWR_MAX_COUNT 512 /* buffered EEPROM words rw */
+#define IGC_EECD_SEC1VAL_I225 0x02000000 /* Sector One Valid */
+#define IGC_FLSECU_BLK_SW_ACCESS_I225 0x00000004 /* Block SW access */
+#define IGC_FWSM_FW_VALID_I225 0x8000 /* FW valid bit */
+
+#define IGC_NVM_RW_REG_DATA 16 /* Offset to data in NVM read/write regs */
+#define IGC_NVM_RW_REG_DONE 2 /* Offset to READ/WRITE done bit */
+#define IGC_NVM_RW_REG_START 1 /* Start operation */
+#define IGC_NVM_RW_ADDR_SHIFT 2 /* Shift to the address bits */
+#define IGC_NVM_POLL_WRITE 1 /* Flag for polling for write complete */
+#define IGC_NVM_POLL_READ 0 /* Flag for polling for read complete */
+#define IGC_FLASH_UPDATES 2000
+
+/* NVM Word Offsets */
+#define NVM_COMPAT 0x0003
+#define NVM_ID_LED_SETTINGS 0x0004
+#define NVM_FUTURE_INIT_WORD1 0x0019
+#define NVM_COMPAT_VALID_CSUM 0x0001
+#define NVM_FUTURE_INIT_WORD1_VALID_CSUM 0x0040
+
+#define NVM_INIT_CONTROL2_REG 0x000F
+#define NVM_INIT_CONTROL3_PORT_B 0x0014
+#define NVM_INIT_3GIO_3 0x001A
+#define NVM_SWDEF_PINS_CTRL_PORT_0 0x0020
+#define NVM_INIT_CONTROL3_PORT_A 0x0024
+#define NVM_CFG 0x0012
+#define NVM_ALT_MAC_ADDR_PTR 0x0037
+#define NVM_CHECKSUM_REG 0x003F
+
+#define IGC_NVM_CFG_DONE_PORT_0 0x040000 /* MNG config cycle done */
+#define IGC_NVM_CFG_DONE_PORT_1 0x080000 /* ...for second port */
+
+/* Mask bits for fields in Word 0x0f of the NVM */
+#define NVM_WORD0F_PAUSE_MASK 0x3000
+#define NVM_WORD0F_PAUSE 0x1000
+#define NVM_WORD0F_ASM_DIR 0x2000
+
+/* Mask bits for fields in Word 0x1a of the NVM */
+#define NVM_WORD1A_ASPM_MASK 0x000C
+
+/* Mask bits for fields in Word 0x03 of the EEPROM */
+#define NVM_COMPAT_LOM 0x0800
+
+/* length of string needed to store PBA number */
+#define IGC_PBANUM_LENGTH 11
+
+/* For checksumming, the sum of all words in the NVM should equal 0xBABA. */
+#define NVM_SUM 0xBABA
+
+/* PBA (printed board assembly) number words */
+#define NVM_PBA_OFFSET_0 8
+#define NVM_PBA_OFFSET_1 9
+#define NVM_PBA_PTR_GUARD 0xFAFA
+#define NVM_WORD_SIZE_BASE_SHIFT 6
+
+/* NVM Commands - Microwire */
+#define NVM_READ_OPCODE_MICROWIRE 0x6 /* NVM read opcode */
+#define NVM_WRITE_OPCODE_MICROWIRE 0x5 /* NVM write opcode */
+#define NVM_ERASE_OPCODE_MICROWIRE 0x7 /* NVM erase opcode */
+#define NVM_EWEN_OPCODE_MICROWIRE 0x13 /* NVM erase/write enable */
+#define NVM_EWDS_OPCODE_MICROWIRE 0x10 /* NVM erase/write disable */
+
+/* NVM Commands - SPI */
+#define NVM_MAX_RETRY_SPI 5000 /* Max wait of 5ms, for RDY signal */
+#define NVM_READ_OPCODE_SPI 0x03 /* NVM read opcode */
+#define NVM_WRITE_OPCODE_SPI 0x02 /* NVM write opcode */
+#define NVM_A8_OPCODE_SPI 0x08 /* opcode bit-3 = address bit-8 */
+#define NVM_WREN_OPCODE_SPI 0x06 /* NVM set Write Enable latch */
+#define NVM_RDSR_OPCODE_SPI 0x05 /* NVM read Status register */
+
+/* SPI NVM Status Register */
+#define NVM_STATUS_RDY_SPI 0x01
+
+/* Word definitions for ID LED Settings */
+#define ID_LED_RESERVED_0000 0x0000
+#define ID_LED_RESERVED_FFFF 0xFFFF
+#define ID_LED_DEFAULT ((ID_LED_OFF1_ON2 << 12) | \
+ (ID_LED_OFF1_OFF2 << 8) | \
+ (ID_LED_DEF1_DEF2 << 4) | \
+ (ID_LED_DEF1_DEF2))
+#define ID_LED_DEF1_DEF2 0x1
+#define ID_LED_DEF1_ON2 0x2
+#define ID_LED_DEF1_OFF2 0x3
+#define ID_LED_ON1_DEF2 0x4
+#define ID_LED_ON1_ON2 0x5
+#define ID_LED_ON1_OFF2 0x6
+#define ID_LED_OFF1_DEF2 0x7
+#define ID_LED_OFF1_ON2 0x8
+#define ID_LED_OFF1_OFF2 0x9
+
+#define IGP_ACTIVITY_LED_MASK 0xFFFFF0FF
+#define IGP_ACTIVITY_LED_ENABLE 0x0300
+#define IGP_LED3_MODE 0x07000000
+
+/* PCI/PCI-X/PCI-EX Config space */
+#define PCIX_COMMAND_REGISTER 0xE6
+#define PCIX_STATUS_REGISTER_LO 0xE8
+#define PCIX_STATUS_REGISTER_HI 0xEA
+#define PCI_HEADER_TYPE_REGISTER 0x0E
+#define PCIE_LINK_STATUS 0x12
+
+#define PCIX_COMMAND_MMRBC_MASK 0x000C
+#define PCIX_COMMAND_MMRBC_SHIFT 0x2
+#define PCIX_STATUS_HI_MMRBC_MASK 0x0060
+#define PCIX_STATUS_HI_MMRBC_SHIFT 0x5
+#define PCIX_STATUS_HI_MMRBC_4K 0x3
+#define PCIX_STATUS_HI_MMRBC_2K 0x2
+#define PCIX_STATUS_LO_FUNC_MASK 0x7
+#define PCI_HEADER_TYPE_MULTIFUNC 0x80
+#define PCIE_LINK_WIDTH_MASK 0x3F0
+#define PCIE_LINK_WIDTH_SHIFT 4
+#define PCIE_LINK_SPEED_MASK 0x0F
+#define PCIE_LINK_SPEED_2500 0x01
+#define PCIE_LINK_SPEED_5000 0x02
+
+#define PHY_REVISION_MASK 0xFFFFFFF0
+#define MAX_PHY_REG_ADDRESS 0x1F /* 5 bit address bus (0-0x1F) */
+#define MAX_PHY_MULTI_PAGE_REG 0xF
+
+/* Bit definitions for valid PHY IDs.
+ * I = Integrated
+ * E = External
+ */
+#define M88IGC_E_PHY_ID 0x01410C50
+#define M88IGC_I_PHY_ID 0x01410C30
+#define M88E1011_I_PHY_ID 0x01410C20
+#define IGP01IGC_I_PHY_ID 0x02A80380
+#define M88E1111_I_PHY_ID 0x01410CC0
+#define GG82563_E_PHY_ID 0x01410CA0
+#define IGP03IGC_E_PHY_ID 0x02A80390
+#define IFE_E_PHY_ID 0x02A80330
+#define IFE_PLUS_E_PHY_ID 0x02A80320
+#define IFE_C_E_PHY_ID 0x02A80310
+#define I225_I_PHY_ID 0x67C9DC00
+
+/* M88IGC Specific Registers */
+#define M88IGC_PHY_SPEC_CTRL 0x10 /* PHY Specific Control Reg */
+#define M88IGC_PHY_SPEC_STATUS 0x11 /* PHY Specific Status Reg */
+#define M88IGC_EXT_PHY_SPEC_CTRL 0x14 /* Extended PHY Specific Cntrl */
+#define M88IGC_RX_ERR_CNTR 0x15 /* Receive Error Counter */
+
+#define M88IGC_PHY_PAGE_SELECT 0x1D /* Reg 29 for pg number setting */
+#define M88IGC_PHY_GEN_CONTROL 0x1E /* meaning depends on reg 29 */
+
+/* M88IGC PHY Specific Control Register */
+#define M88IGC_PSCR_POLARITY_REVERSAL 0x0002 /* 1=Polarity Reverse enabled */
+/* MDI Crossover Mode bits 6:5 Manual MDI configuration */
+#define M88IGC_PSCR_MDI_MANUAL_MODE 0x0000
+#define M88IGC_PSCR_MDIX_MANUAL_MODE 0x0020 /* Manual MDIX configuration */
+/* 1000BASE-T: Auto crossover, 100BASE-TX/10BASE-T: MDI Mode */
+#define M88IGC_PSCR_AUTO_X_1000T 0x0040
+/* Auto crossover enabled all speeds */
+#define M88IGC_PSCR_AUTO_X_MODE 0x0060
+#define M88IGC_PSCR_ASSERT_CRS_ON_TX 0x0800 /* 1=Assert CRS on Tx */
+
+/* M88IGC PHY Specific Status Register */
+#define M88IGC_PSSR_REV_POLARITY 0x0002 /* 1=Polarity reversed */
+#define M88IGC_PSSR_DOWNSHIFT 0x0020 /* 1=Downshifted */
+#define M88IGC_PSSR_MDIX 0x0040 /* 1=MDIX; 0=MDI */
+/* 0 = <50M
+ * 1 = 50-80M
+ * 2 = 80-110M
+ * 3 = 110-140M
+ * 4 = >140M
+ */
+#define M88IGC_PSSR_CABLE_LENGTH 0x0380
+#define M88IGC_PSSR_LINK 0x0400 /* 1=Link up, 0=Link down */
+#define M88IGC_PSSR_SPD_DPLX_RESOLVED 0x0800 /* 1=Speed & Duplex resolved */
+#define M88IGC_PSSR_SPEED 0xC000 /* Speed, bits 14:15 */
+#define M88IGC_PSSR_1000MBS 0x8000 /* 10=1000Mbs */
+
+#define M88IGC_PSSR_CABLE_LENGTH_SHIFT 7
+
+/* Number of times we will attempt to autonegotiate before downshifting if we
+ * are the master
+ */
+#define M88IGC_EPSCR_MASTER_DOWNSHIFT_MASK 0x0C00
+#define M88IGC_EPSCR_MASTER_DOWNSHIFT_1X 0x0000
+/* Number of times we will attempt to autonegotiate before downshifting if we
+ * are the slave
+ */
+#define M88IGC_EPSCR_SLAVE_DOWNSHIFT_MASK 0x0300
+#define M88IGC_EPSCR_SLAVE_DOWNSHIFT_1X 0x0100
+#define M88IGC_EPSCR_TX_CLK_25 0x0070 /* 25 MHz TX_CLK */
+
+
+/* M88EC018 Rev 2 specific DownShift settings */
+#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_MASK 0x0E00
+#define M88EC018_EPSCR_DOWNSHIFT_COUNTER_5X 0x0800
+
+/* Bits...
+ * 15-5: page
+ * 4-0: register offset
+ */
+#define GG82563_PAGE_SHIFT 5
+#define GG82563_REG(page, reg) \
+ (((page) << GG82563_PAGE_SHIFT) | ((reg) & MAX_PHY_REG_ADDRESS))
+#define GG82563_MIN_ALT_REG 30
+
+/* GG82563 Specific Registers */
+#define GG82563_PHY_SPEC_CTRL GG82563_REG(0, 16) /* PHY Spec Cntrl */
+#define GG82563_PHY_PAGE_SELECT GG82563_REG(0, 22) /* Page Select */
+#define GG82563_PHY_SPEC_CTRL_2 GG82563_REG(0, 26) /* PHY Spec Cntrl2 */
+#define GG82563_PHY_PAGE_SELECT_ALT GG82563_REG(0, 29) /* Alt Page Select */
+
+/* MAC Specific Control Register */
+#define GG82563_PHY_MAC_SPEC_CTRL GG82563_REG(2, 21)
+
+#define GG82563_PHY_DSP_DISTANCE GG82563_REG(5, 26) /* DSP Distance */
+
+/* Page 193 - Port Control Registers */
+/* Kumeran Mode Control */
+#define GG82563_PHY_KMRN_MODE_CTRL GG82563_REG(193, 16)
+#define GG82563_PHY_PWR_MGMT_CTRL GG82563_REG(193, 20) /* Pwr Mgt Ctrl */
+
+/* Page 194 - KMRN Registers */
+#define GG82563_PHY_INBAND_CTRL GG82563_REG(194, 18) /* Inband Ctrl */
+
+/* MDI Control */
+#define IGC_MDIC_DATA_MASK 0x0000FFFF
+#define IGC_MDIC_INT_EN 0x20000000
+#define IGC_MDIC_REG_MASK 0x001F0000
+#define IGC_MDIC_REG_SHIFT 16
+#define IGC_MDIC_PHY_SHIFT 21
+#define IGC_MDIC_OP_WRITE 0x04000000
+#define IGC_MDIC_OP_READ 0x08000000
+#define IGC_MDIC_READY 0x10000000
+#define IGC_MDIC_ERROR 0x40000000
+
+#define IGC_N0_QUEUE -1
+
+#define IGC_MAX_MAC_HDR_LEN 127
+#define IGC_MAX_NETWORK_HDR_LEN 511
+
+#define IGC_VLANPQF_QUEUE_SEL(_n, q_idx) ((q_idx) << ((_n) * 4))
+#define IGC_VLANPQF_P_VALID(_n) (0x1 << (3 + (_n) * 4))
+#define IGC_VLANPQF_QUEUE_MASK 0x03
+#define IGC_VFTA_BLOCK_SIZE 8
+/* SerDes Control */
+#define IGC_GEN_POLL_TIMEOUT 640
+
+/* DMA Coalescing register fields */
+/* DMA Coalescing Watchdog Timer */
+#define IGC_DMACR_DMACWT_MASK 0x00003FFF
+/* DMA Coalescing Rx Threshold */
+#define IGC_DMACR_DMACTHR_MASK 0x00FF0000
+#define IGC_DMACR_DMACTHR_SHIFT 16
+/* Lx when no PCIe transactions */
+#define IGC_DMACR_DMAC_LX_MASK 0x30000000
+#define IGC_DMACR_DMAC_LX_SHIFT 28
+#define IGC_DMACR_DMAC_EN 0x80000000 /* Enable DMA Coalescing */
+/* DMA Coalescing BMC-to-OS Watchdog Enable */
+#define IGC_DMACR_DC_BMC2OSW_EN 0x00008000
+
+/* DMA Coalescing Transmit Threshold */
+#define IGC_DMCTXTH_DMCTTHR_MASK 0x00000FFF
+
+#define IGC_DMCTLX_TTLX_MASK 0x00000FFF /* Time to LX request */
+
+/* Rx Traffic Rate Threshold */
+#define IGC_DMCRTRH_UTRESH_MASK 0x0007FFFF
+/* Rx packet rate in current window */
+#define IGC_DMCRTRH_LRPRCW 0x80000000
+
+/* DMA Coal Rx Traffic Current Count */
+#define IGC_DMCCNT_CCOUNT_MASK 0x01FFFFFF
+
+/* Flow ctrl Rx Threshold High val */
+#define IGC_FCRTC_RTH_COAL_MASK 0x0003FFF0
+#define IGC_FCRTC_RTH_COAL_SHIFT 4
+/* Lx power decision based on DMA coal */
+#define IGC_PCIEMISC_LX_DECISION 0x00000080
+
+#define IGC_RXPBS_CFG_TS_EN 0x80000000 /* Timestamp in Rx buffer */
+#define IGC_RXPBS_SIZE_I210_MASK 0x0000003F /* Rx packet buffer size */
+#define IGC_TXPB0S_SIZE_I210_MASK 0x0000003F /* Tx packet buffer 0 size */
+#define I210_RXPBSIZE_DEFAULT 0x000000A2 /* RXPBSIZE default */
+#define I210_TXPBSIZE_DEFAULT 0x04000014 /* TXPBSIZE default */
+
+#define IGC_LTRC_EEEMS_EN 0x00000020 /* Enable EEE LTR max send */
+/* Minimum time for 1000BASE-T where no data will be transmit following move out
+ * of EEE LPI Tx state
+ */
+#define IGC_TW_SYSTEM_1000_MASK 0x000000FF
+/* Minimum time for 100BASE-T where no data will be transmit following move out
+ * of EEE LPI Tx state
+ */
+#define IGC_TW_SYSTEM_100_MASK 0x0000FF00
+#define IGC_TW_SYSTEM_100_SHIFT 8
+#define IGC_LTRMINV_LTRV_MASK 0x000003FF /* LTR minimum value */
+#define IGC_LTRMAXV_LTRV_MASK 0x000003FF /* LTR maximum value */
+#define IGC_LTRMINV_SCALE_MASK 0x00001C00 /* LTR minimum scale */
+#define IGC_LTRMINV_SCALE_SHIFT 10
+/* Reg val to set scale to 1024 nsec */
+#define IGC_LTRMINV_SCALE_1024 2
+/* Reg val to set scale to 32768 nsec */
+#define IGC_LTRMINV_SCALE_32768 3
+#define IGC_LTRMINV_LSNP_REQ 0x00008000 /* LTR Snoop Requirement */
+#define IGC_LTRMAXV_SCALE_MASK 0x00001C00 /* LTR maximum scale */
+#define IGC_LTRMAXV_SCALE_SHIFT 10
+/* Reg val to set scale to 1024 nsec */
+#define IGC_LTRMAXV_SCALE_1024 2
+/* Reg val to set scale to 32768 nsec */
+#define IGC_LTRMAXV_SCALE_32768 3
+#define IGC_LTRMAXV_LSNP_REQ 0x00008000 /* LTR Snoop Requirement */
+
+#define I225_RXPBSIZE_DEFAULT 0x000000A2 /* RXPBSIZE default */
+#define I225_TXPBSIZE_DEFAULT 0x04000014 /* TXPBSIZE default */
+#define IGC_RXPBS_SIZE_I225_MASK 0x0000003F /* Rx packet buffer size */
+#define IGC_TXPB0S_SIZE_I225_MASK 0x0000003F /* Tx packet buffer 0 size */
+#define IGC_STM_OPCODE 0xDB00
+#define IGC_EEPROM_FLASH_SIZE_WORD 0x11
+#define INVM_DWORD_TO_RECORD_TYPE(invm_dword) \
+ (u8)((invm_dword) & 0x7)
+#define INVM_DWORD_TO_WORD_ADDRESS(invm_dword) \
+ (u8)(((invm_dword) & 0x0000FE00) >> 9)
+#define INVM_DWORD_TO_WORD_DATA(invm_dword) \
+ (u16)(((invm_dword) & 0xFFFF0000) >> 16)
+#define IGC_INVM_RSA_KEY_SHA256_DATA_SIZE_IN_DWORDS 8
+#define IGC_INVM_CSR_AUTOLOAD_DATA_SIZE_IN_DWORDS 1
+#define IGC_INVM_ULT_BYTES_SIZE 8
+#define IGC_INVM_RECORD_SIZE_IN_BYTES 4
+#define IGC_INVM_VER_FIELD_ONE 0x1FF8
+#define IGC_INVM_VER_FIELD_TWO 0x7FE000
+#define IGC_INVM_IMGTYPE_FIELD 0x1F800000
+
+#define IGC_INVM_MAJOR_MASK 0x3F0
+#define IGC_INVM_MINOR_MASK 0xF
+#define IGC_INVM_MAJOR_SHIFT 4
+
+/* PLL Defines */
+#define IGC_PCI_PMCSR 0x44
+#define IGC_PCI_PMCSR_D3 0x03
+#define IGC_MAX_PLL_TRIES 5
+#define IGC_PHY_PLL_UNCONF 0xFF
+#define IGC_PHY_PLL_FREQ_PAGE 0xFC0000
+#define IGC_PHY_PLL_FREQ_REG 0x000E
+#define IGC_INVM_DEFAULT_AL 0x202F
+#define IGC_INVM_AUTOLOAD 0x0A
+#define IGC_INVM_PLL_WO_VAL 0x0010
+
+/* Proxy Filter Control Extended */
+#define IGC_PROXYFCEX_MDNS 0x00000001 /* mDNS */
+#define IGC_PROXYFCEX_MDNS_M 0x00000002 /* mDNS Multicast */
+#define IGC_PROXYFCEX_MDNS_U 0x00000004 /* mDNS Unicast */
+#define IGC_PROXYFCEX_IPV4_M 0x00000008 /* IPv4 Multicast */
+#define IGC_PROXYFCEX_IPV6_M 0x00000010 /* IPv6 Multicast */
+#define IGC_PROXYFCEX_IGMP 0x00000020 /* IGMP */
+#define IGC_PROXYFCEX_IGMP_M 0x00000040 /* IGMP Multicast */
+#define IGC_PROXYFCEX_ARPRES 0x00000080 /* ARP Response */
+#define IGC_PROXYFCEX_ARPRES_D 0x00000100 /* ARP Response Directed */
+#define IGC_PROXYFCEX_ICMPV4 0x00000200 /* ICMPv4 */
+#define IGC_PROXYFCEX_ICMPV4_D 0x00000400 /* ICMPv4 Directed */
+#define IGC_PROXYFCEX_ICMPV6 0x00000800 /* ICMPv6 */
+#define IGC_PROXYFCEX_ICMPV6_D 0x00001000 /* ICMPv6 Directed */
+#define IGC_PROXYFCEX_DNS 0x00002000 /* DNS */
+
+/* Proxy Filter Control */
+#define IGC_PROXYFC_D0 0x00000001 /* Enable offload in D0 */
+#define IGC_PROXYFC_EX 0x00000004 /* Directed exact proxy */
+#define IGC_PROXYFC_MC 0x00000008 /* Directed MC Proxy */
+#define IGC_PROXYFC_BC 0x00000010 /* Broadcast Proxy Enable */
+#define IGC_PROXYFC_ARP_DIRECTED 0x00000020 /* Directed ARP Proxy Ena */
+#define IGC_PROXYFC_IPV4 0x00000040 /* Directed IPv4 Enable */
+#define IGC_PROXYFC_IPV6 0x00000080 /* Directed IPv6 Enable */
+#define IGC_PROXYFC_NS 0x00000200 /* IPv6 Neighbor Solicitation */
+#define IGC_PROXYFC_NS_DIRECTED 0x00000400 /* Directed NS Proxy Ena */
+#define IGC_PROXYFC_ARP 0x00000800 /* ARP Request Proxy Ena */
+/* Proxy Status */
+#define IGC_PROXYS_CLEAR 0xFFFFFFFF /* Clear */
+
+/* Firmware Status */
+#define IGC_FWSTS_FWRI 0x80000000 /* FW Reset Indication */
+/* VF Control */
+#define IGC_VTCTRL_RST 0x04000000 /* Reset VF */
+
+#define IGC_STATUS_LAN_ID_MASK 0x00000000C /* Mask for Lan ID field */
+/* Lan ID bit field offset in status register */
+#define IGC_STATUS_LAN_ID_OFFSET 2
+#define IGC_VFTA_ENTRIES 128
+
+#define IGC_UNUSEDARG
+
+#endif /* _IGC_DEFINES_H_ */
diff --git a/sys/dev/pci/igc_hw.h b/sys/dev/pci/igc_hw.h
new file mode 100644
index 00000000000..4224afb4945
--- /dev/null
+++ b/sys/dev/pci/igc_hw.h
@@ -0,0 +1,394 @@
+/* $OpenBSD: igc_hw.h,v 1.1 2021/10/31 14:52:57 patrick Exp $ */
+/*-
+ * Copyright 2021 Intel Corp
+ * Copyright 2021 Rubicon Communications, LLC (Netgate)
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _IGC_HW_H_
+#define _IGC_HW_H_
+
+#include "bpfilter.h"
+#include "vlan.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/sockio.h>
+#include <sys/mbuf.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/socket.h>
+#include <sys/device.h>
+#include <sys/endian.h>
+#include <sys/intrmap.h>
+
+#include <net/if.h>
+#include <net/if_media.h>
+#include <net/toeplitz.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#if NBPFILTER > 0
+#include <net/bpf.h>
+#endif
+
+#include <machine/bus.h>
+#include <machine/intr.h>
+
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcidevs.h>
+
+#include <dev/pci/igc_base.h>
+#include <dev/pci/igc_defines.h>
+#include <dev/pci/igc_i225.h>
+#include <dev/pci/igc_mac.h>
+#include <dev/pci/igc_nvm.h>
+#include <dev/pci/igc_phy.h>
+#include <dev/pci/igc_regs.h>
+
+#if 0
+/* Enable/disable debugging statements in shared code */
+#define DBG 0
+
+#define DEBUGOUT(...) \
+ do { if (DBG) printf(__VA_ARGS__); } while (0)
+#define DEBUGOUT1(...) DEBUGOUT(__VA_ARGS__)
+#define DEBUGOUT2(...) DEBUGOUT(__VA_ARGS__)
+#define DEBUGOUT3(...) DEBUGOUT(__VA_ARGS__)
+#define DEBUGOUT7(...) DEBUGOUT(__VA_ARGS__)
+#define DEBUGFUNC(F) DEBUGOUT(F "\n")
+#endif
+
+struct igc_hw;
+
+#define IGC_FUNC_1 1
+
+#define IGC_ALT_MAC_ADDRESS_OFFSET_LAN0 0
+#define IGC_ALT_MAC_ADDRESS_OFFSET_LAN1 3
+
+enum igc_mac_type {
+ igc_undefined = 0,
+ igc_i225,
+ igc_num_macs /* List is 1-based, so subtract 1 for TRUE count. */
+};
+
+enum igc_media_type {
+ igc_media_type_unknown = 0,
+ igc_media_type_copper = 1,
+ igc_num_media_types
+};
+
+enum igc_nvm_type {
+ igc_nvm_unknown = 0,
+ igc_nvm_eeprom_spi,
+ igc_nvm_flash_hw,
+ igc_nvm_invm
+};
+
+enum igc_phy_type {
+ igc_phy_unknown = 0,
+ igc_phy_none,
+ igc_phy_i225
+};
+
+enum igc_bus_type {
+ igc_bus_type_unknown = 0,
+ igc_bus_type_pci,
+ igc_bus_type_pcix,
+ igc_bus_type_pci_express,
+ igc_bus_type_reserved
+};
+
+enum igc_bus_speed {
+ igc_bus_speed_unknown = 0,
+ igc_bus_speed_33,
+ igc_bus_speed_66,
+ igc_bus_speed_100,
+ igc_bus_speed_120,
+ igc_bus_speed_133,
+ igc_bus_speed_2500,
+ igc_bus_speed_5000,
+ igc_bus_speed_reserved
+};
+
+enum igc_bus_width {
+ igc_bus_width_unknown = 0,
+ igc_bus_width_pcie_x1,
+ igc_bus_width_pcie_x2,
+ igc_bus_width_pcie_x4 = 4,
+ igc_bus_width_pcie_x8 = 8,
+ igc_bus_width_32,
+ igc_bus_width_64,
+ igc_bus_width_reserved
+};
+
+enum igc_fc_mode {
+ igc_fc_none = 0,
+ igc_fc_rx_pause,
+ igc_fc_tx_pause,
+ igc_fc_full,
+ igc_fc_default = 0xFF
+};
+
+enum igc_ms_type {
+ igc_ms_hw_default = 0,
+ igc_ms_force_master,
+ igc_ms_force_slave,
+ igc_ms_auto
+};
+
+enum igc_smart_speed {
+ igc_smart_speed_default = 0,
+ igc_smart_speed_on,
+ igc_smart_speed_off
+};
+
+/* Receive Descriptor */
+struct igc_rx_desc {
+ uint64_t buffer_addr; /* Address of the descriptor's data buffer */
+ uint64_t length; /* Length of data DMAed into data buffer */
+ uint16_t csum; /* Packet checksum */
+ uint8_t status; /* Descriptor status */
+ uint8_t errors; /* Descriptor errors */
+ uint16_t special;
+};
+
+/* Receive Descriptor - Extended */
+union igc_rx_desc_extended {
+ struct {
+ uint64_t buffer_addr;
+ uint64_t reserved;
+ } read;
+ struct {
+ struct {
+ uint32_t mrq; /* Multiple Rx queues */
+ union {
+ uint32_t rss; /* RSS hash */
+ struct {
+ uint16_t ip_id; /* IP id */
+ uint16_t csum; /* Packet checksum */
+ } csum_ip;
+ } hi_dword;
+ } lower;
+ struct {
+ uint32_t status_error; /* ext status/error */
+ uint16_t length;
+ uint16_t vlan; /* VLAN tag */
+ } upper;
+ } wb; /* writeback */
+};
+
+/* Transmit Descriptor */
+struct igc_tx_desc {
+ uint64_t buffer_addr; /* Address of the descriptor's data buffer */
+ union {
+ uint32_t data;
+ struct {
+ uint16_t length; /* Data buffer length */
+ uint8_t cso; /* Checksum offset */
+ uint8_t cmd; /* Descriptor control */
+ } flags;
+ } lower;
+ union {
+ uint32_t data;
+ struct {
+ uint8_t status; /* Descriptor status */
+ uint8_t css; /* Checksum start */
+ uint16_t special;
+ } fields;
+ } upper;
+};
+
+/* Function pointers for the MAC. */
+struct igc_mac_operations {
+ int (*init_params)(struct igc_hw *);
+ int (*check_for_link)(struct igc_hw *);
+ void (*clear_hw_cntrs)(struct igc_hw *);
+ void (*clear_vfta)(struct igc_hw *);
+ int (*get_bus_info)(struct igc_hw *);
+ void (*set_lan_id)(struct igc_hw *);
+ int (*get_link_up_info)(struct igc_hw *, uint16_t *, uint16_t *);
+ void (*update_mc_addr_list)(struct igc_hw *, uint8_t *, uint32_t);
+ int (*reset_hw)(struct igc_hw *);
+ int (*init_hw)(struct igc_hw *);
+ int (*setup_link)(struct igc_hw *);
+ int (*setup_physical_interface)(struct igc_hw *);
+ void (*write_vfta)(struct igc_hw *, uint32_t, uint32_t);
+ void (*config_collision_dist)(struct igc_hw *);
+ int (*rar_set)(struct igc_hw *, uint8_t *, uint32_t);
+ int (*read_mac_addr)(struct igc_hw *);
+ int (*validate_mdi_setting)(struct igc_hw *);
+ int (*acquire_swfw_sync)(struct igc_hw *, uint16_t);
+ void (*release_swfw_sync)(struct igc_hw *, uint16_t);
+};
+
+/* When to use various PHY register access functions:
+ *
+ * Func Caller
+ * Function Does Does When to use
+ * ~~~~~~~~~~~~ ~~~~~ ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ * X_reg L,P,A n/a for simple PHY reg accesses
+ * X_reg_locked P,A L for multiple accesses of different regs
+ * on different pages
+ * X_reg_page A L,P for multiple accesses of different regs
+ * on the same page
+ *
+ * Where X=[read|write], L=locking, P=sets page, A=register access
+ *
+ */
+struct igc_phy_operations {
+ int (*init_params)(struct igc_hw *);
+ int (*acquire)(struct igc_hw *);
+ int (*check_reset_block)(struct igc_hw *);
+ int (*commit)(struct igc_hw *);
+ int (*force_speed_duplex)(struct igc_hw *);
+ int (*get_info)(struct igc_hw *);
+ int (*set_page)(struct igc_hw *, uint16_t);
+ int (*read_reg)(struct igc_hw *, uint32_t, uint16_t *);
+ int (*read_reg_locked)(struct igc_hw *, uint32_t, uint16_t *);
+ int (*read_reg_page)(struct igc_hw *, uint32_t, uint16_t *);
+ void (*release)(struct igc_hw *);
+ int (*reset)(struct igc_hw *);
+ int (*set_d0_lplu_state)(struct igc_hw *, bool);
+ int (*set_d3_lplu_state)(struct igc_hw *, bool);
+ int (*write_reg)(struct igc_hw *, uint32_t, uint16_t);
+ int (*write_reg_locked)(struct igc_hw *, uint32_t, uint16_t);
+ int (*write_reg_page)(struct igc_hw *, uint32_t, uint16_t);
+ void (*power_up)(struct igc_hw *);
+ void (*power_down)(struct igc_hw *);
+};
+
+/* Function pointers for the NVM. */
+struct igc_nvm_operations {
+ int (*init_params)(struct igc_hw *);
+ int (*acquire)(struct igc_hw *);
+ int (*read)(struct igc_hw *, uint16_t, uint16_t, uint16_t *);
+ void (*release)(struct igc_hw *);
+ void (*reload)(struct igc_hw *);
+ int (*update)(struct igc_hw *);
+ int (*validate)(struct igc_hw *);
+ int (*write)(struct igc_hw *, uint16_t, uint16_t, uint16_t *);
+};
+
+struct igc_info {
+ int (*get_invariants)(struct igc_hw *hw);
+ struct igc_mac_operations *mac_ops;
+ const struct igc_phy_operations *phy_ops;
+ struct igc_nvm_operations *nvm_ops;
+};
+
+extern const struct igc_info igc_i225_info;
+
+struct igc_mac_info {
+ struct igc_mac_operations ops;
+ uint8_t addr[ETHER_ADDR_LEN];
+ uint8_t perm_addr[ETHER_ADDR_LEN];
+
+ enum igc_mac_type type;
+
+ uint32_t mc_filter_type;
+
+ uint16_t current_ifs_val;
+ uint16_t ifs_max_val;
+ uint16_t ifs_min_val;
+ uint16_t ifs_ratio;
+ uint16_t ifs_step_size;
+ uint16_t mta_reg_count;
+ uint16_t uta_reg_count;
+
+ /* Maximum size of the MTA register table in all supported adapters */
+#define MAX_MTA_REG 128
+ uint32_t mta_shadow[MAX_MTA_REG];
+ uint16_t rar_entry_count;
+
+ uint8_t forced_speed_duplex;
+
+ bool asf_firmware_present;
+ bool autoneg;
+ bool get_link_status;
+ uint32_t max_frame_size;
+};
+
+struct igc_phy_info {
+ struct igc_phy_operations ops;
+ enum igc_phy_type type;
+
+ enum igc_smart_speed smart_speed;
+
+ uint32_t addr;
+ uint32_t id;
+ uint32_t reset_delay_us; /* in usec */
+ uint32_t revision;
+
+ enum igc_media_type media_type;
+
+ uint16_t autoneg_advertised;
+ uint16_t autoneg_mask;
+
+ uint8_t mdix;
+
+ bool polarity_correction;
+ bool speed_downgraded;
+ bool autoneg_wait_to_complete;
+};
+
+struct igc_nvm_info {
+ struct igc_nvm_operations ops;
+ enum igc_nvm_type type;
+
+ uint16_t word_size;
+ uint16_t delay_usec;
+ uint16_t address_bits;
+ uint16_t opcode_bits;
+ uint16_t page_size;
+};
+
+struct igc_bus_info {
+ enum igc_bus_type type;
+ enum igc_bus_speed speed;
+ enum igc_bus_width width;
+
+ uint16_t func;
+ uint16_t pci_cmd_word;
+};
+
+struct igc_fc_info {
+ uint32_t high_water;
+ uint32_t low_water;
+ uint16_t pause_time;
+ uint16_t refresh_time;
+ bool send_xon;
+ bool strict_ieee;
+ enum igc_fc_mode current_mode;
+ enum igc_fc_mode requested_mode;
+};
+
+struct igc_dev_spec_i225 {
+ bool eee_disable;
+ bool clear_semaphore_once;
+ uint32_t mtu;
+};
+
+struct igc_hw {
+ void *back;
+
+ uint8_t *hw_addr;
+
+ struct igc_mac_info mac;
+ struct igc_fc_info fc;
+ struct igc_phy_info phy;
+ struct igc_nvm_info nvm;
+ struct igc_bus_info bus;
+
+ union {
+ struct igc_dev_spec_i225 _i225;
+ } dev_spec;
+
+ uint16_t device_id;
+};
+
+#endif /* _IGC_HW_H_ */
diff --git a/sys/dev/pci/igc_i225.c b/sys/dev/pci/igc_i225.c
new file mode 100644
index 00000000000..bc532e2c73b
--- /dev/null
+++ b/sys/dev/pci/igc_i225.c
@@ -0,0 +1,1238 @@
+/* $OpenBSD: igc_i225.c,v 1.1 2021/10/31 14:52:57 patrick Exp $ */
+/*-
+ * Copyright 2021 Intel Corp
+ * Copyright 2021 Rubicon Communications, LLC (Netgate)
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <dev/pci/igc_api.h>
+
+int igc_init_nvm_params_i225(struct igc_hw *);
+int igc_init_mac_params_i225(struct igc_hw *);
+int igc_init_phy_params_i225(struct igc_hw *);
+int igc_reset_hw_i225(struct igc_hw *);
+int igc_acquire_nvm_i225(struct igc_hw *);
+void igc_release_nvm_i225(struct igc_hw *);
+int igc_get_hw_semaphore_i225(struct igc_hw *);
+int __igc_write_nvm_srwr(struct igc_hw *, uint16_t, uint16_t, uint16_t *);
+int igc_pool_flash_update_done_i225(struct igc_hw *);
+
+/**
+ * igc_init_nvm_params_i225 - Init NVM func ptrs.
+ * @hw: pointer to the HW structure
+ **/
+int
+igc_init_nvm_params_i225(struct igc_hw *hw)
+{
+ struct igc_nvm_info *nvm = &hw->nvm;
+ uint32_t eecd = IGC_READ_REG(hw, IGC_EECD);
+ uint16_t size;
+
+ DEBUGFUNC("igc_init_nvm_params_i225");
+
+ size = (uint16_t)((eecd & IGC_EECD_SIZE_EX_MASK) >>
+ IGC_EECD_SIZE_EX_SHIFT);
+ /*
+ * Added to a constant, "size" becomes the left-shift value
+ * for setting word_size.
+ */
+ size += NVM_WORD_SIZE_BASE_SHIFT;
+
+ /* Just in case size is out of range, cap it to the largest
+ * EEPROM size supported.
+ */
+ if (size > 15)
+ size = 15;
+
+ nvm->word_size = 1 << size;
+ nvm->opcode_bits = 8;
+ nvm->delay_usec = 1;
+ nvm->type = igc_nvm_eeprom_spi;
+
+ nvm->page_size = eecd & IGC_EECD_ADDR_BITS ? 32 : 8;
+ nvm->address_bits = eecd & IGC_EECD_ADDR_BITS ? 16 : 8;
+
+ if (nvm->word_size == (1 << 15))
+ nvm->page_size = 128;
+
+ nvm->ops.acquire = igc_acquire_nvm_i225;
+ nvm->ops.release = igc_release_nvm_i225;
+ if (igc_get_flash_presence_i225(hw)) {
+ hw->nvm.type = igc_nvm_flash_hw;
+ nvm->ops.read = igc_read_nvm_srrd_i225;
+ nvm->ops.write = igc_write_nvm_srwr_i225;
+ nvm->ops.validate = igc_validate_nvm_checksum_i225;
+ nvm->ops.update = igc_update_nvm_checksum_i225;
+ } else {
+ hw->nvm.type = igc_nvm_invm;
+ nvm->ops.write = igc_null_write_nvm;
+ nvm->ops.validate = igc_null_ops_generic;
+ nvm->ops.update = igc_null_ops_generic;
+ }
+
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_init_mac_params_i225 - Init MAC func ptrs.
+ * @hw: pointer to the HW structure
+ **/
+int
+igc_init_mac_params_i225(struct igc_hw *hw)
+{
+ struct igc_mac_info *mac = &hw->mac;
+ struct igc_dev_spec_i225 *dev_spec = &hw->dev_spec._i225;
+
+ DEBUGFUNC("igc_init_mac_params_i225");
+
+ /* Initialize function pointer */
+ igc_init_mac_ops_generic(hw);
+
+ /* Set media type */
+ hw->phy.media_type = igc_media_type_copper;
+ /* Set mta register count */
+ mac->mta_reg_count = 128;
+ /* Set rar entry count */
+ mac->rar_entry_count = IGC_RAR_ENTRIES_BASE;
+
+ /* reset */
+ mac->ops.reset_hw = igc_reset_hw_i225;
+ /* hw initialization */
+ mac->ops.init_hw = igc_init_hw_i225;
+ /* link setup */
+ mac->ops.setup_link = igc_setup_link_generic;
+ /* check for link */
+ mac->ops.check_for_link = igc_check_for_link_i225;
+ /* link info */
+ mac->ops.get_link_up_info = igc_get_speed_and_duplex_copper_generic;
+ /* acquire SW_FW sync */
+ mac->ops.acquire_swfw_sync = igc_acquire_swfw_sync_i225;
+ /* release SW_FW sync */
+ mac->ops.release_swfw_sync = igc_release_swfw_sync_i225;
+
+ /* Allow a single clear of the SW semaphore on I225 */
+ dev_spec->clear_semaphore_once = true;
+ mac->ops.setup_physical_interface = igc_setup_copper_link_i225;
+
+ /* Set if part includes ASF firmware */
+ mac->asf_firmware_present = true;
+
+ /* multicast address update */
+ mac->ops.update_mc_addr_list = igc_update_mc_addr_list_generic;
+
+ mac->ops.write_vfta = igc_write_vfta_generic;
+
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_init_phy_params_i225 - Init PHY func ptrs.
+ * @hw: pointer to the HW structure
+ **/
+int
+igc_init_phy_params_i225(struct igc_hw *hw)
+{
+ struct igc_phy_info *phy = &hw->phy;
+ uint32_t ctrl_ext;
+ int ret_val = IGC_SUCCESS;
+
+ DEBUGFUNC("igc_init_phy_params_i225");
+
+ if (hw->phy.media_type != igc_media_type_copper) {
+ phy->type = igc_phy_none;
+ goto out;
+ }
+
+ phy->ops.power_up = igc_power_up_phy_copper;
+ phy->ops.power_down = igc_power_down_phy_copper_base;
+ phy->autoneg_mask = AUTONEG_ADVERTISE_SPEED_DEFAULT_2500;
+ phy->reset_delay_us = 100;
+ phy->ops.acquire = igc_acquire_phy_base;
+ phy->ops.check_reset_block = igc_check_reset_block_generic;
+ phy->ops.commit = igc_phy_sw_reset_generic;
+ phy->ops.release = igc_release_phy_base;
+
+ ctrl_ext = IGC_READ_REG(hw, IGC_CTRL_EXT);
+
+ /* Make sure the PHY is in a good state. Several people have reported
+ * firmware leaving the PHY's page select register set to something
+ * other than the default of zero, which causes the PHY ID read to
+ * access something other than the intended register.
+ */
+ ret_val = hw->phy.ops.reset(hw);
+ if (ret_val)
+ goto out;
+
+ IGC_WRITE_REG(hw, IGC_CTRL_EXT, ctrl_ext);
+ phy->ops.read_reg = igc_read_phy_reg_gpy;
+ phy->ops.write_reg = igc_write_phy_reg_gpy;
+
+ ret_val = igc_get_phy_id(hw);
+ /* Verify phy id and set remaining function pointers */
+ switch (phy->id) {
+ case I225_I_PHY_ID:
+ phy->type = igc_phy_i225;
+ phy->ops.set_d0_lplu_state = igc_set_d0_lplu_state_i225;
+ phy->ops.set_d3_lplu_state = igc_set_d3_lplu_state_i225;
+ /* TODO - complete with GPY PHY information */
+ break;
+ default:
+ ret_val = -IGC_ERR_PHY;
+ goto out;
+ }
+
+out:
+ return ret_val;
+}
+
+/**
+ * igc_reset_hw_i225 - Reset hardware
+ * @hw: pointer to the HW structure
+ *
+ * This resets the hardware into a known state.
+ **/
+int
+igc_reset_hw_i225(struct igc_hw *hw)
+{
+ uint32_t ctrl;
+ int ret_val;
+
+ DEBUGFUNC("igc_reset_hw_i225");
+
+ /*
+ * Prevent the PCI-E bus from sticking if there is no TLP connection
+ * on the last TLP read/write transaction when MAC is reset.
+ */
+ ret_val = igc_disable_pcie_master_generic(hw);
+ if (ret_val)
+ DEBUGOUT("PCI-E Master disable polling has failed.\n");
+
+ DEBUGOUT("Masking off all interrupts\n");
+ IGC_WRITE_REG(hw, IGC_IMC, 0xffffffff);
+
+ IGC_WRITE_REG(hw, IGC_RCTL, 0);
+ IGC_WRITE_REG(hw, IGC_TCTL, IGC_TCTL_PSP);
+ IGC_WRITE_FLUSH(hw);
+
+ msec_delay(10);
+
+ ctrl = IGC_READ_REG(hw, IGC_CTRL);
+
+ DEBUGOUT("Issuing a global reset to MAC\n");
+ IGC_WRITE_REG(hw, IGC_CTRL, ctrl | IGC_CTRL_DEV_RST);
+
+ ret_val = igc_get_auto_rd_done_generic(hw);
+ if (ret_val) {
+ /*
+ * When auto config read does not complete, do not
+ * return with an error. This can happen in situations
+ * where there is no eeprom and prevents getting link.
+ */
+ DEBUGOUT("Auto Read Done did not complete\n");
+ }
+
+ /* Clear any pending interrupt events. */
+ IGC_WRITE_REG(hw, IGC_IMC, 0xffffffff);
+ IGC_READ_REG(hw, IGC_ICR);
+
+ /* Install any alternate MAC address into RAR0 */
+ ret_val = igc_check_alt_mac_addr_generic(hw);
+
+ return ret_val;
+}
+
+/* igc_acquire_nvm_i225 - Request for access to EEPROM
+ * @hw: pointer to the HW structure
+ *
+ * Acquire the necessary semaphores for exclusive access to the EEPROM.
+ * Set the EEPROM access request bit and wait for EEPROM access grant bit.
+ * Return successful if access grant bit set, else clear the request for
+ * EEPROM access and return -IGC_ERR_NVM (-1).
+ */
+int
+igc_acquire_nvm_i225(struct igc_hw *hw)
+{
+ int ret_val;
+
+ DEBUGFUNC("igc_acquire_nvm_i225");
+
+ ret_val = igc_acquire_swfw_sync_i225(hw, IGC_SWFW_EEP_SM);
+
+ return ret_val;
+}
+
+/* igc_release_nvm_i225 - Release exclusive access to EEPROM
+ * @hw: pointer to the HW structure
+ *
+ * Stop any current commands to the EEPROM and clear the EEPROM request bit,
+ * then release the semaphores acquired.
+ */
+void
+igc_release_nvm_i225(struct igc_hw *hw)
+{
+ DEBUGFUNC("igc_release_nvm_i225");
+
+ igc_release_swfw_sync_i225(hw, IGC_SWFW_EEP_SM);
+}
+
+/* igc_acquire_swfw_sync_i225 - Acquire SW/FW semaphore
+ * @hw: pointer to the HW structure
+ * @mask: specifies which semaphore to acquire
+ *
+ * Acquire the SW/FW semaphore to access the PHY or NVM. The mask
+ * will also specify which port we're acquiring the lock for.
+ */
+int
+igc_acquire_swfw_sync_i225(struct igc_hw *hw, uint16_t mask)
+{
+ uint32_t swfw_sync;
+ uint32_t swmask = mask;
+ uint32_t fwmask = mask << 16;
+ int ret_val = IGC_SUCCESS;
+ int i = 0, timeout = 200; /* FIXME: find real value to use here */
+
+ DEBUGFUNC("igc_acquire_swfw_sync_i225");
+
+ while (i < timeout) {
+ if (igc_get_hw_semaphore_i225(hw)) {
+ ret_val = -IGC_ERR_SWFW_SYNC;
+ goto out;
+ }
+
+ swfw_sync = IGC_READ_REG(hw, IGC_SW_FW_SYNC);
+ if (!(swfw_sync & (fwmask | swmask)))
+ break;
+
+ /* Firmware currently using resource (fwmask)
+ * or other software thread using resource (swmask)
+ */
+ igc_put_hw_semaphore_generic(hw);
+ msec_delay(5);
+ i++;
+ }
+
+ if (i == timeout) {
+ DEBUGOUT("Driver can't access resource, SW_FW_SYNC timeout.\n");
+ ret_val = -IGC_ERR_SWFW_SYNC;
+ goto out;
+ }
+
+ swfw_sync |= swmask;
+ IGC_WRITE_REG(hw, IGC_SW_FW_SYNC, swfw_sync);
+
+ igc_put_hw_semaphore_generic(hw);
+
+out:
+ return ret_val;
+}
+
+/* igc_release_swfw_sync_i225 - Release SW/FW semaphore
+ * @hw: pointer to the HW structure
+ * @mask: specifies which semaphore to acquire
+ *
+ * Release the SW/FW semaphore used to access the PHY or NVM. The mask
+ * will also specify which port we're releasing the lock for.
+ */
+void
+igc_release_swfw_sync_i225(struct igc_hw *hw, uint16_t mask)
+{
+ uint32_t swfw_sync;
+
+ DEBUGFUNC("igc_release_swfw_sync_i225");
+
+ while (igc_get_hw_semaphore_i225(hw) != IGC_SUCCESS)
+ ; /* Empty */
+
+ swfw_sync = IGC_READ_REG(hw, IGC_SW_FW_SYNC);
+ swfw_sync &= ~mask;
+ IGC_WRITE_REG(hw, IGC_SW_FW_SYNC, swfw_sync);
+
+ igc_put_hw_semaphore_generic(hw);
+}
+
+/*
+ * igc_setup_copper_link_i225 - Configure copper link settings
+ * @hw: pointer to the HW structure
+ *
+ * Configures the link for auto-neg or forced speed and duplex. Then we check
+ * for link, once link is established calls to configure collision distance
+ * and flow control are called.
+ */
+int
+igc_setup_copper_link_i225(struct igc_hw *hw)
+{
+ uint32_t ctrl, phpm_reg;
+ int ret_val;
+
+ DEBUGFUNC("igc_setup_copper_link_i225");
+
+ ctrl = IGC_READ_REG(hw, IGC_CTRL);
+ ctrl |= IGC_CTRL_SLU;
+ ctrl &= ~(IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX);
+ IGC_WRITE_REG(hw, IGC_CTRL, ctrl);
+
+ phpm_reg = IGC_READ_REG(hw, IGC_I225_PHPM);
+ phpm_reg &= ~IGC_I225_PHPM_GO_LINKD;
+ IGC_WRITE_REG(hw, IGC_I225_PHPM, phpm_reg);
+
+ ret_val = igc_setup_copper_link_generic(hw);
+
+ return ret_val;
+}
+
+/* igc_get_hw_semaphore_i225 - Acquire hardware semaphore
+ * @hw: pointer to the HW structure
+ *
+ * Acquire the HW semaphore to access the PHY or NVM
+ */
+int
+igc_get_hw_semaphore_i225(struct igc_hw *hw)
+{
+ uint32_t swsm;
+ int timeout = hw->nvm.word_size + 1;
+ int i = 0;
+
+ DEBUGFUNC("igc_get_hw_semaphore_i225");
+
+ /* Get the SW semaphore */
+ while (i < timeout) {
+ swsm = IGC_READ_REG(hw, IGC_SWSM);
+ if (!(swsm & IGC_SWSM_SMBI))
+ break;
+
+ DELAY(50);
+ i++;
+ }
+
+ if (i == timeout) {
+ /* In rare circumstances, the SW semaphore may already be held
+ * unintentionally. Clear the semaphore once before giving up.
+ */
+ if (hw->dev_spec._i225.clear_semaphore_once) {
+ hw->dev_spec._i225.clear_semaphore_once = false;
+ igc_put_hw_semaphore_generic(hw);
+ for (i = 0; i < timeout; i++) {
+ swsm = IGC_READ_REG(hw, IGC_SWSM);
+ if (!(swsm & IGC_SWSM_SMBI))
+ break;
+
+ DELAY(50);
+ }
+ }
+
+ /* If we do not have the semaphore here, we have to give up. */
+ if (i == timeout) {
+ DEBUGOUT("Driver can't access device -\n");
+ DEBUGOUT("SMBI bit is set.\n");
+ return -IGC_ERR_NVM;
+ }
+ }
+
+ /* Get the FW semaphore. */
+ for (i = 0; i < timeout; i++) {
+ swsm = IGC_READ_REG(hw, IGC_SWSM);
+ IGC_WRITE_REG(hw, IGC_SWSM, swsm | IGC_SWSM_SWESMBI);
+
+ /* Semaphore acquired if bit latched */
+ if (IGC_READ_REG(hw, IGC_SWSM) & IGC_SWSM_SWESMBI)
+ break;
+
+ DELAY(50);
+ }
+
+ if (i == timeout) {
+ /* Release semaphores */
+ igc_put_hw_semaphore_generic(hw);
+ DEBUGOUT("Driver can't access the NVM\n");
+ return -IGC_ERR_NVM;
+ }
+
+ return IGC_SUCCESS;
+}
+
+/* igc_read_nvm_srrd_i225 - Reads Shadow Ram using EERD register
+ * @hw: pointer to the HW structure
+ * @offset: offset of word in the Shadow Ram to read
+ * @words: number of words to read
+ * @data: word read from the Shadow Ram
+ *
+ * Reads a 16 bit word from the Shadow Ram using the EERD register.
+ * Uses necessary synchronization semaphores.
+ */
+int
+igc_read_nvm_srrd_i225(struct igc_hw *hw, uint16_t offset, uint16_t words,
+ uint16_t *data)
+{
+ uint16_t i, count;
+ int status = IGC_SUCCESS;
+
+ DEBUGFUNC("igc_read_nvm_srrd_i225");
+
+ /* We cannot hold synchronization semaphores for too long,
+ * because of forceful takeover procedure. However it is more efficient
+ * to read in bursts than synchronizing access for each word.
+ */
+ for (i = 0; i < words; i += IGC_EERD_EEWR_MAX_COUNT) {
+ count = (words - i) / IGC_EERD_EEWR_MAX_COUNT > 0 ?
+ IGC_EERD_EEWR_MAX_COUNT : (words - i);
+ if (hw->nvm.ops.acquire(hw) == IGC_SUCCESS) {
+ status = igc_read_nvm_eerd(hw, offset, count, data + i);
+ hw->nvm.ops.release(hw);
+ } else {
+ status = IGC_ERR_SWFW_SYNC;
+ }
+
+ if (status != IGC_SUCCESS)
+ break;
+ }
+
+ return status;
+}
+
+/* igc_write_nvm_srwr_i225 - Write to Shadow RAM using EEWR
+ * @hw: pointer to the HW structure
+ * @offset: offset within the Shadow RAM to be written to
+ * @words: number of words to write
+ * @data: 16 bit word(s) to be written to the Shadow RAM
+ *
+ * Writes data to Shadow RAM at offset using EEWR register.
+ *
+ * If igc_update_nvm_checksum is not called after this function , the
+ * data will not be committed to FLASH and also Shadow RAM will most likely
+ * contain an invalid checksum.
+ *
+ * If error code is returned, data and Shadow RAM may be inconsistent - buffer
+ * partially written.
+ */
+int
+igc_write_nvm_srwr_i225(struct igc_hw *hw, uint16_t offset, uint16_t words,
+ uint16_t *data)
+{
+ uint16_t i, count;
+ int status = IGC_SUCCESS;
+
+ DEBUGFUNC("igc_write_nvm_srwr_i225");
+
+ /* We cannot hold synchronization semaphores for too long,
+ * because of forceful takeover procedure. However it is more efficient
+ * to write in bursts than synchronizing access for each word.
+ */
+ for (i = 0; i < words; i += IGC_EERD_EEWR_MAX_COUNT) {
+ count = (words - i) / IGC_EERD_EEWR_MAX_COUNT > 0 ?
+ IGC_EERD_EEWR_MAX_COUNT : (words - i);
+ if (hw->nvm.ops.acquire(hw) == IGC_SUCCESS) {
+ status = __igc_write_nvm_srwr(hw, offset, count,
+ data + i);
+ hw->nvm.ops.release(hw);
+ } else
+ status = IGC_ERR_SWFW_SYNC;
+
+ if (status != IGC_SUCCESS)
+ break;
+ }
+
+ return status;
+}
+
+/* __igc_write_nvm_srwr - Write to Shadow Ram using EEWR
+ * @hw: pointer to the HW structure
+ * @offset: offset within the Shadow Ram to be written to
+ * @words: number of words to write
+ * @data: 16 bit word(s) to be written to the Shadow Ram
+ *
+ * Writes data to Shadow Ram at offset using EEWR register.
+ *
+ * If igc_update_nvm_checksum is not called after this function , the
+ * Shadow Ram will most likely contain an invalid checksum.
+ */
+int
+__igc_write_nvm_srwr(struct igc_hw *hw, uint16_t offset, uint16_t words,
+ uint16_t *data)
+{
+ struct igc_nvm_info *nvm = &hw->nvm;
+ uint32_t i, k, eewr = 0;
+ uint32_t attempts = 100000;
+ int ret_val = IGC_SUCCESS;
+
+ DEBUGFUNC("__igc_write_nvm_srwr");
+
+ /* A check for invalid values: offset too large, too many words,
+ * too many words for the offset, and not enough words.
+ */
+ if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) ||
+ (words == 0)) {
+ DEBUGOUT("nvm parameter(s) out of bounds\n");
+ ret_val = -IGC_ERR_NVM;
+ goto out;
+ }
+
+ for (i = 0; i < words; i++) {
+ eewr = ((offset + i) << IGC_NVM_RW_ADDR_SHIFT) |
+ (data[i] << IGC_NVM_RW_REG_DATA) | IGC_NVM_RW_REG_START;
+
+ IGC_WRITE_REG(hw, IGC_SRWR, eewr);
+
+ for (k = 0; k < attempts; k++) {
+ if (IGC_NVM_RW_REG_DONE & IGC_READ_REG(hw, IGC_SRWR)) {
+ ret_val = IGC_SUCCESS;
+ break;
+ }
+ DELAY(5);
+ }
+
+ if (ret_val != IGC_SUCCESS) {
+ DEBUGOUT("Shadow RAM write EEWR timed out\n");
+ break;
+ }
+ }
+
+out:
+ return ret_val;
+}
+
+/* igc_validate_nvm_checksum_i225 - Validate EEPROM checksum
+ * @hw: pointer to the HW structure
+ *
+ * Calculates the EEPROM checksum by reading/adding each word of the EEPROM
+ * and then verifies that the sum of the EEPROM is equal to 0xBABA.
+ */
+int
+igc_validate_nvm_checksum_i225(struct igc_hw *hw)
+{
+ int status = IGC_SUCCESS;
+ int (*read_op_ptr)(struct igc_hw *, uint16_t, uint16_t, uint16_t *);
+
+ DEBUGFUNC("igc_validate_nvm_checksum_i225");
+
+ if (hw->nvm.ops.acquire(hw) == IGC_SUCCESS) {
+ /* Replace the read function with semaphore grabbing with
+ * the one that skips this for a while.
+ * We have semaphore taken already here.
+ */
+ read_op_ptr = hw->nvm.ops.read;
+ hw->nvm.ops.read = igc_read_nvm_eerd;
+
+ status = igc_validate_nvm_checksum_generic(hw);
+
+ /* Revert original read operation. */
+ hw->nvm.ops.read = read_op_ptr;
+
+ hw->nvm.ops.release(hw);
+ } else {
+ status = IGC_ERR_SWFW_SYNC;
+ }
+
+ return status;
+}
+
+/* igc_update_nvm_checksum_i225 - Update EEPROM checksum
+ * @hw: pointer to the HW structure
+ *
+ * Updates the EEPROM checksum by reading/adding each word of the EEPROM
+ * up to the checksum. Then calculates the EEPROM checksum and writes the
+ * value to the EEPROM. Next commit EEPROM data onto the Flash.
+ */
+int
+igc_update_nvm_checksum_i225(struct igc_hw *hw)
+{
+ uint16_t checksum = 0;
+ uint16_t i, nvm_data;
+ int ret_val;
+
+ DEBUGFUNC("igc_update_nvm_checksum_i225");
+
+ /* Read the first word from the EEPROM. If this times out or fails, do
+ * not continue or we could be in for a very long wait while every
+ * EEPROM read fails
+ */
+ ret_val = igc_read_nvm_eerd(hw, 0, 1, &nvm_data);
+ if (ret_val != IGC_SUCCESS) {
+ DEBUGOUT("EEPROM read failed\n");
+ goto out;
+ }
+
+ if (hw->nvm.ops.acquire(hw) == IGC_SUCCESS) {
+ /* Do not use hw->nvm.ops.write, hw->nvm.ops.read
+ * because we do not want to take the synchronization
+ * semaphores twice here.
+ */
+
+ for (i = 0; i < NVM_CHECKSUM_REG; i++) {
+ ret_val = igc_read_nvm_eerd(hw, i, 1, &nvm_data);
+ if (ret_val) {
+ hw->nvm.ops.release(hw);
+ DEBUGOUT("NVM Read Error while updating\n");
+ DEBUGOUT("checksum.\n");
+ goto out;
+ }
+ checksum += nvm_data;
+ }
+ checksum = (uint16_t)NVM_SUM - checksum;
+ ret_val = __igc_write_nvm_srwr(hw, NVM_CHECKSUM_REG, 1,
+ &checksum);
+ if (ret_val != IGC_SUCCESS) {
+ hw->nvm.ops.release(hw);
+ DEBUGOUT("NVM Write Error while updating checksum.\n");
+ goto out;
+ }
+
+ hw->nvm.ops.release(hw);
+
+ ret_val = igc_update_flash_i225(hw);
+ } else {
+ ret_val = IGC_ERR_SWFW_SYNC;
+ }
+out:
+ return ret_val;
+}
+
+/* igc_get_flash_presence_i225 - Check if flash device is detected.
+ * @hw: pointer to the HW structure
+ */
+bool
+igc_get_flash_presence_i225(struct igc_hw *hw)
+{
+ uint32_t eec = 0;
+ bool ret_val = false;
+
+ DEBUGFUNC("igc_get_flash_presence_i225");
+
+ eec = IGC_READ_REG(hw, IGC_EECD);
+
+ if (eec & IGC_EECD_FLASH_DETECTED_I225)
+ ret_val = true;
+
+ return ret_val;
+}
+
+/* igc_set_flsw_flash_burst_counter_i225 - sets FLSW NVM Burst
+ * Counter in FLSWCNT register.
+ *
+ * @hw: pointer to the HW structure
+ * @burst_counter: size in bytes of the Flash burst to read or write
+ */
+int
+igc_set_flsw_flash_burst_counter_i225(struct igc_hw *hw, uint32_t burst_counter)
+{
+ int ret_val = IGC_SUCCESS;
+
+ DEBUGFUNC("igc_set_flsw_flash_burst_counter_i225");
+
+ /* Validate input data */
+ if (burst_counter < IGC_I225_SHADOW_RAM_SIZE) {
+ /* Write FLSWCNT - burst counter */
+ IGC_WRITE_REG(hw, IGC_I225_FLSWCNT, burst_counter);
+ } else {
+ ret_val = IGC_ERR_INVALID_ARGUMENT;
+ }
+
+ return ret_val;
+}
+
+
+/* igc_write_erase_flash_command_i225 - write/erase to a sector
+ * region on a given address.
+ *
+ * @hw: pointer to the HW structure
+ * @opcode: opcode to be used for the write command
+ * @address: the offset to write into the FLASH image
+ */
+int
+igc_write_erase_flash_command_i225(struct igc_hw *hw, uint32_t opcode,
+ uint32_t address)
+{
+ uint32_t flswctl = 0;
+ int timeout = IGC_NVM_GRANT_ATTEMPTS;
+ int ret_val = IGC_SUCCESS;
+
+ DEBUGFUNC("igc_write_erase_flash_command_i225");
+
+ flswctl = IGC_READ_REG(hw, IGC_I225_FLSWCTL);
+ /* Polling done bit on FLSWCTL register */
+ while (timeout) {
+ if (flswctl & IGC_FLSWCTL_DONE)
+ break;
+ DELAY(5);
+ flswctl = IGC_READ_REG(hw, IGC_I225_FLSWCTL);
+ timeout--;
+ }
+
+ if (!timeout) {
+ DEBUGOUT("Flash transaction was not done\n");
+ return -IGC_ERR_NVM;
+ }
+
+ /* Build and issue command on FLSWCTL register */
+ flswctl = address | opcode;
+ IGC_WRITE_REG(hw, IGC_I225_FLSWCTL, flswctl);
+
+ /* Check if issued command is valid on FLSWCTL register */
+ flswctl = IGC_READ_REG(hw, IGC_I225_FLSWCTL);
+ if (!(flswctl & IGC_FLSWCTL_CMDV)) {
+ DEBUGOUT("Write flash command failed\n");
+ ret_val = IGC_ERR_INVALID_ARGUMENT;
+ }
+
+ return ret_val;
+}
+
+/* igc_update_flash_i225 - Commit EEPROM to the flash
+ * if fw_valid_bit is set, FW is active. setting FLUPD bit in EEC
+ * register makes the FW load the internal shadow RAM into the flash.
+ * Otherwise, fw_valid_bit is 0. if FL_SECU.block_prtotected_sw = 0
+ * then FW is not active so the SW is responsible shadow RAM dump.
+ *
+ * @hw: pointer to the HW structure
+ */
+int
+igc_update_flash_i225(struct igc_hw *hw)
+{
+ uint32_t block_sw_protect = 1;
+ uint32_t i, flup, fw_valid_bit;
+ uint16_t current_offset;
+ uint16_t base_address = 0x0;
+ uint16_t current_offset_data = 0;
+ int ret_val = 0;
+
+ DEBUGFUNC("igc_update_flash_i225");
+
+ block_sw_protect = IGC_READ_REG(hw, IGC_I225_FLSECU) &
+ IGC_FLSECU_BLK_SW_ACCESS_I225;
+
+ fw_valid_bit = IGC_READ_REG(hw, IGC_FWSM) & IGC_FWSM_FW_VALID_I225;
+ if (fw_valid_bit) {
+ ret_val = igc_pool_flash_update_done_i225(hw);
+ if (ret_val == -IGC_ERR_NVM) {
+ DEBUGOUT("Flash update time out\n");
+ goto out;
+ }
+
+ flup = IGC_READ_REG(hw, IGC_EECD) | IGC_EECD_FLUPD_I225;
+ IGC_WRITE_REG(hw, IGC_EECD, flup);
+
+ ret_val = igc_pool_flash_update_done_i225(hw);
+ if (ret_val == IGC_SUCCESS)
+ DEBUGOUT("Flash update complete\n");
+ else
+ DEBUGOUT("Flash update time out\n");
+ } else if (!block_sw_protect) {
+ /* FW is not active and security protection is disabled.
+ * therefore, SW is in charge of shadow RAM dump.
+ * Check which sector is valid. if sector 0 is valid,
+ * base address remains 0x0. otherwise, sector 1 is
+ * valid and it's base address is 0x1000
+ */
+ if (IGC_READ_REG(hw, IGC_EECD) & IGC_EECD_SEC1VAL_I225)
+ base_address = 0x1000;
+
+ /* Valid sector erase */
+ ret_val = igc_write_erase_flash_command_i225(hw,
+ IGC_I225_ERASE_CMD_OPCODE, base_address);
+ if (!ret_val) {
+ DEBUGOUT("Sector erase failed\n");
+ goto out;
+ }
+
+ current_offset = base_address;
+
+ /* Write */
+ for (i = 0; i < IGC_I225_SHADOW_RAM_SIZE / 2; i++) {
+ /* Set burst write length */
+ ret_val = igc_set_flsw_flash_burst_counter_i225(hw,
+ 0x2);
+ if (ret_val != IGC_SUCCESS)
+ break;
+
+ /* Set address and opcode */
+ ret_val = igc_write_erase_flash_command_i225(hw,
+ IGC_I225_WRITE_CMD_OPCODE, 2 * current_offset);
+ if (ret_val != IGC_SUCCESS)
+ break;
+
+ ret_val = igc_read_nvm_eerd(hw, current_offset, 1,
+ &current_offset_data);
+ if (ret_val) {
+ DEBUGOUT("Failed to read from EEPROM\n");
+ goto out;
+ }
+
+ /* Write CurrentOffseData to FLSWDATA register */
+ IGC_WRITE_REG(hw, IGC_I225_FLSWDATA,
+ current_offset_data);
+ current_offset++;
+
+ /* Wait till operation has finished */
+ ret_val = igc_poll_eerd_eewr_done(hw,
+ IGC_NVM_POLL_READ);
+ if (ret_val)
+ break;
+
+ DELAY(1000);
+ }
+ }
+out:
+ return ret_val;
+}
+
+/* igc_pool_flash_update_done_i225 - Pool FLUDONE status.
+ * @hw: pointer to the HW structure
+ */
+int
+igc_pool_flash_update_done_i225(struct igc_hw *hw)
+{
+ uint32_t i, reg;
+ int ret_val = -IGC_ERR_NVM;
+
+ DEBUGFUNC("igc_pool_flash_update_done_i225");
+
+ for (i = 0; i < IGC_FLUDONE_ATTEMPTS; i++) {
+ reg = IGC_READ_REG(hw, IGC_EECD);
+ if (reg & IGC_EECD_FLUDONE_I225) {
+ ret_val = IGC_SUCCESS;
+ break;
+ }
+ DELAY(5);
+ }
+
+ return ret_val;
+}
+
+/* igc_set_ltr_i225 - Set Latency Tolerance Reporting thresholds.
+ * @hw: pointer to the HW structure
+ * @link: bool indicating link status
+ *
+ * Set the LTR thresholds based on the link speed (Mbps), EEE, and DMAC
+ * settings, otherwise specify that there is no LTR requirement.
+ */
+int
+igc_set_ltr_i225(struct igc_hw *hw, bool link)
+{
+ uint16_t speed, duplex;
+ uint32_t tw_system, ltrc, ltrv, ltr_min, ltr_max, scale_min, scale_max;
+ int size;
+
+ DEBUGFUNC("igc_set_ltr_i225");
+
+ /* If we do not have link, LTR thresholds are zero. */
+ if (link) {
+ hw->mac.ops.get_link_up_info(hw, &speed, &duplex);
+
+ /* Check if using copper interface with EEE enabled or if the
+ * link speed is 10 Mbps.
+ */
+ if ((hw->phy.media_type == igc_media_type_copper) &&
+ !(hw->dev_spec._i225.eee_disable) &&
+ (speed != SPEED_10)) {
+ /* EEE enabled, so send LTRMAX threshold. */
+ ltrc = IGC_READ_REG(hw, IGC_LTRC) | IGC_LTRC_EEEMS_EN;
+ IGC_WRITE_REG(hw, IGC_LTRC, ltrc);
+
+ /* Calculate tw_system (nsec). */
+ if (speed == SPEED_100) {
+ tw_system = ((IGC_READ_REG(hw, IGC_EEE_SU) &
+ IGC_TW_SYSTEM_100_MASK) >>
+ IGC_TW_SYSTEM_100_SHIFT) * 500;
+ } else {
+ tw_system = (IGC_READ_REG(hw, IGC_EEE_SU) &
+ IGC_TW_SYSTEM_1000_MASK) * 500;
+ }
+ } else {
+ tw_system = 0;
+ }
+
+ /* Get the Rx packet buffer size. */
+ size = IGC_READ_REG(hw, IGC_RXPBS) & IGC_RXPBS_SIZE_I225_MASK;
+
+ /* Calculations vary based on DMAC settings. */
+ if (IGC_READ_REG(hw, IGC_DMACR) & IGC_DMACR_DMAC_EN) {
+ size -= (IGC_READ_REG(hw, IGC_DMACR) &
+ IGC_DMACR_DMACTHR_MASK) >> IGC_DMACR_DMACTHR_SHIFT;
+ /* Convert size to bits. */
+ size *= 1024 * 8;
+ } else {
+ /* Convert size to bytes, subtract the MTU, and then
+ * convert the size to bits.
+ */
+ size *= 1024;
+ size -= hw->dev_spec._i225.mtu;
+ size *= 8;
+ }
+
+ if (size < 0) {
+ DEBUGOUT1("Invalid effective Rx buffer size %d\n",
+ size);
+ return -IGC_ERR_CONFIG;
+ }
+
+ /* Calculate the thresholds. Since speed is in Mbps, simplify
+ * the calculation by multiplying size/speed by 1000 for result
+ * to be in nsec before dividing by the scale in nsec. Set the
+ * scale such that the LTR threshold fits in the register.
+ */
+ ltr_min = (1000 * size) / speed;
+ ltr_max = ltr_min + tw_system;
+ scale_min = (ltr_min / 1024) < 1024 ? IGC_LTRMINV_SCALE_1024 :
+ IGC_LTRMINV_SCALE_32768;
+ scale_max = (ltr_max / 1024) < 1024 ? IGC_LTRMAXV_SCALE_1024 :
+ IGC_LTRMAXV_SCALE_32768;
+ ltr_min /= scale_min == IGC_LTRMINV_SCALE_1024 ? 1024 : 32768;
+ ltr_max /= scale_max == IGC_LTRMAXV_SCALE_1024 ? 1024 : 32768;
+
+ /* Only write the LTR thresholds if they differ from before. */
+ ltrv = IGC_READ_REG(hw, IGC_LTRMINV);
+ if (ltr_min != (ltrv & IGC_LTRMINV_LTRV_MASK)) {
+ ltrv = IGC_LTRMINV_LSNP_REQ | ltr_min |
+ (scale_min << IGC_LTRMINV_SCALE_SHIFT);
+ IGC_WRITE_REG(hw, IGC_LTRMINV, ltrv);
+ }
+
+ ltrv = IGC_READ_REG(hw, IGC_LTRMAXV);
+ if (ltr_max != (ltrv & IGC_LTRMAXV_LTRV_MASK)) {
+ ltrv = IGC_LTRMAXV_LSNP_REQ | ltr_max |
+ (scale_min << IGC_LTRMAXV_SCALE_SHIFT);
+ IGC_WRITE_REG(hw, IGC_LTRMAXV, ltrv);
+ }
+ }
+
+ return IGC_SUCCESS;
+}
+
+/* igc_check_for_link_i225 - Check for link
+ * @hw: pointer to the HW structure
+ *
+ * Checks to see of the link status of the hardware has changed. If a
+ * change in link status has been detected, then we read the PHY registers
+ * to get the current speed/duplex if link exists.
+ */
+int
+igc_check_for_link_i225(struct igc_hw *hw)
+{
+ struct igc_mac_info *mac = &hw->mac;
+ int ret_val;
+ bool link = false;
+
+ DEBUGFUNC("igc_check_for_link_i225");
+
+ /* We only want to go out to the PHY registers to see if
+ * Auto-Neg has completed and/or if our link status has
+ * changed. The get_link_status flag is set upon receiving
+ * a Link Status Change or Rx Sequence Error interrupt.
+ */
+ if (!mac->get_link_status) {
+ ret_val = IGC_SUCCESS;
+ goto out;
+ }
+
+ /* First we want to see if the MII Status Register reports
+ * link. If so, then we want to get the current speed/duplex
+ * of the PHY.
+ */
+ ret_val = igc_phy_has_link_generic(hw, 1, 0, &link);
+ if (ret_val)
+ goto out;
+
+ if (!link)
+ goto out; /* No link detected */
+
+ /* First we want to see if the MII Status Register reports
+ * link. If so, then we want to get the current speed/duplex
+ * of the PHY.
+ */
+ ret_val = igc_phy_has_link_generic(hw, 1, 0, &link);
+ if (ret_val)
+ goto out;
+
+ if (!link)
+ goto out; /* No link detected */
+
+ mac->get_link_status = false;
+
+ /* Check if there was DownShift, must be checked
+ * immediately after link-up
+ */
+ igc_check_downshift_generic(hw);
+
+ /* If we are forcing speed/duplex, then we simply return since
+ * we have already determined whether we have link or not.
+ */
+ if (!mac->autoneg)
+ goto out;
+
+ /* Auto-Neg is enabled. Auto Speed Detection takes care
+ * of MAC speed/duplex configuration. So we only need to
+ * configure Collision Distance in the MAC.
+ */
+ mac->ops.config_collision_dist(hw);
+
+ /* Configure Flow Control now that Auto-Neg has completed.
+ * First, we need to restore the desired flow control
+ * settings because we may have had to re-autoneg with a
+ * different link partner.
+ */
+ ret_val = igc_config_fc_after_link_up_generic(hw);
+ if (ret_val)
+ DEBUGOUT("Error configuring flow control\n");
+out:
+ /* Now that we are aware of our link settings, we can set the LTR
+ * thresholds.
+ */
+ ret_val = igc_set_ltr_i225(hw, link);
+
+ return ret_val;
+}
+
+/* igc_init_function_pointers_i225 - Init func ptrs.
+ * @hw: pointer to the HW structure
+ *
+ * Called to initialize all function pointers and parameters.
+ */
+void
+igc_init_function_pointers_i225(struct igc_hw *hw)
+{
+ igc_init_mac_ops_generic(hw);
+ igc_init_phy_ops_generic(hw);
+ igc_init_nvm_ops_generic(hw);
+ hw->mac.ops.init_params = igc_init_mac_params_i225;
+ hw->nvm.ops.init_params = igc_init_nvm_params_i225;
+ hw->phy.ops.init_params = igc_init_phy_params_i225;
+}
+
+/* igc_init_hw_i225 - Init hw for I225
+ * @hw: pointer to the HW structure
+ *
+ * Called to initialize hw for i225 hw family.
+ */
+int
+igc_init_hw_i225(struct igc_hw *hw)
+{
+ int ret_val;
+
+ DEBUGFUNC("igc_init_hw_i225");
+
+ ret_val = igc_init_hw_base(hw);
+ return ret_val;
+}
+
+/*
+ * igc_set_d0_lplu_state_i225 - Set Low-Power-Link-Up (LPLU) D0 state
+ * @hw: pointer to the HW structure
+ * @active: true to enable LPLU, false to disable
+ *
+ * Note: since I225 does not actually support LPLU, this function
+ * simply enables/disables 1G and 2.5G speeds in D0.
+ */
+int
+igc_set_d0_lplu_state_i225(struct igc_hw *hw, bool active)
+{
+ uint32_t data;
+
+ DEBUGFUNC("igc_set_d0_lplu_state_i225");
+
+ data = IGC_READ_REG(hw, IGC_I225_PHPM);
+
+ if (active) {
+ data |= IGC_I225_PHPM_DIS_1000;
+ data |= IGC_I225_PHPM_DIS_2500;
+ } else {
+ data &= ~IGC_I225_PHPM_DIS_1000;
+ data &= ~IGC_I225_PHPM_DIS_2500;
+ }
+
+ IGC_WRITE_REG(hw, IGC_I225_PHPM, data);
+ return IGC_SUCCESS;
+}
+
+/*
+ * igc_set_d3_lplu_state_i225 - Set Low-Power-Link-Up (LPLU) D3 state
+ * @hw: pointer to the HW structure
+ * @active: true to enable LPLU, false to disable
+ *
+ * Note: since I225 does not actually support LPLU, this function
+ * simply enables/disables 100M, 1G and 2.5G speeds in D3.
+ */
+int
+igc_set_d3_lplu_state_i225(struct igc_hw *hw, bool active)
+{
+ uint32_t data;
+
+ DEBUGFUNC("igc_set_d3_lplu_state_i225");
+
+ data = IGC_READ_REG(hw, IGC_I225_PHPM);
+
+ if (active) {
+ data |= IGC_I225_PHPM_DIS_100_D3;
+ data |= IGC_I225_PHPM_DIS_1000_D3;
+ data |= IGC_I225_PHPM_DIS_2500_D3;
+ } else {
+ data &= ~IGC_I225_PHPM_DIS_100_D3;
+ data &= ~IGC_I225_PHPM_DIS_1000_D3;
+ data &= ~IGC_I225_PHPM_DIS_2500_D3;
+ }
+
+ IGC_WRITE_REG(hw, IGC_I225_PHPM, data);
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_set_eee_i225 - Enable/disable EEE support
+ * @hw: pointer to the HW structure
+ * @adv2p5G: boolean flag enabling 2.5G EEE advertisement
+ * @adv1G: boolean flag enabling 1G EEE advertisement
+ * @adv100M: boolean flag enabling 100M EEE advertisement
+ *
+ * Enable/disable EEE based on setting in dev_spec structure.
+ *
+ **/
+int
+igc_set_eee_i225(struct igc_hw *hw, bool adv2p5G, bool adv1G,
+ bool adv100M)
+{
+ uint32_t ipcnfg, eeer;
+
+ DEBUGFUNC("igc_set_eee_i225");
+
+ if (hw->mac.type != igc_i225 ||
+ hw->phy.media_type != igc_media_type_copper)
+ goto out;
+ ipcnfg = IGC_READ_REG(hw, IGC_IPCNFG);
+ eeer = IGC_READ_REG(hw, IGC_EEER);
+
+ /* enable or disable per user setting */
+ if (!(hw->dev_spec._i225.eee_disable)) {
+ uint32_t eee_su = IGC_READ_REG(hw, IGC_EEE_SU);
+
+ if (adv100M)
+ ipcnfg |= IGC_IPCNFG_EEE_100M_AN;
+ else
+ ipcnfg &= ~IGC_IPCNFG_EEE_100M_AN;
+
+ if (adv1G)
+ ipcnfg |= IGC_IPCNFG_EEE_1G_AN;
+ else
+ ipcnfg &= ~IGC_IPCNFG_EEE_1G_AN;
+
+ if (adv2p5G)
+ ipcnfg |= IGC_IPCNFG_EEE_2_5G_AN;
+ else
+ ipcnfg &= ~IGC_IPCNFG_EEE_2_5G_AN;
+
+ eeer |= (IGC_EEER_TX_LPI_EN | IGC_EEER_RX_LPI_EN |
+ IGC_EEER_LPI_FC);
+
+ /* This bit should not be set in normal operation. */
+ if (eee_su & IGC_EEE_SU_LPI_CLK_STP)
+ DEBUGOUT("LPI Clock Stop Bit should not be set!\n");
+ } else {
+ ipcnfg &= ~(IGC_IPCNFG_EEE_2_5G_AN | IGC_IPCNFG_EEE_1G_AN |
+ IGC_IPCNFG_EEE_100M_AN);
+ eeer &= ~(IGC_EEER_TX_LPI_EN | IGC_EEER_RX_LPI_EN |
+ IGC_EEER_LPI_FC);
+ }
+ IGC_WRITE_REG(hw, IGC_IPCNFG, ipcnfg);
+ IGC_WRITE_REG(hw, IGC_EEER, eeer);
+ IGC_READ_REG(hw, IGC_IPCNFG);
+ IGC_READ_REG(hw, IGC_EEER);
+out:
+
+ return IGC_SUCCESS;
+}
diff --git a/sys/dev/pci/igc_i225.h b/sys/dev/pci/igc_i225.h
new file mode 100644
index 00000000000..cb28541ac5c
--- /dev/null
+++ b/sys/dev/pci/igc_i225.h
@@ -0,0 +1,111 @@
+/* $OpenBSD: igc_i225.h,v 1.1 2021/10/31 14:52:57 patrick Exp $ */
+/*-
+ * Copyright 2021 Intel Corp
+ * Copyright 2021 Rubicon Communications, LLC (Netgate)
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _IGC_I225_H_
+#define _IGC_I225_H_
+
+#include <dev/pci/igc_hw.h>
+
+bool igc_get_flash_presence_i225(struct igc_hw *);
+int igc_update_flash_i225(struct igc_hw *);
+int igc_update_nvm_checksum_i225(struct igc_hw *);
+int igc_validate_nvm_checksum_i225(struct igc_hw *);
+int igc_write_nvm_srwr_i225(struct igc_hw *, uint16_t, uint16_t,
+ uint16_t *);
+int igc_read_nvm_srrd_i225(struct igc_hw *, uint16_t, uint16_t, uint16_t *);
+int igc_set_flsw_flash_burst_counter_i225(struct igc_hw *, uint32_t);
+int igc_write_erase_flash_command_i225(struct igc_hw *, uint32_t, uint32_t);
+int igc_check_for_link_i225(struct igc_hw *);
+int igc_acquire_swfw_sync_i225(struct igc_hw *, uint16_t);
+void igc_release_swfw_sync_i225(struct igc_hw *, uint16_t);
+int igc_set_ltr_i225(struct igc_hw *, bool);
+int igc_init_hw_i225(struct igc_hw *);
+int igc_setup_copper_link_i225(struct igc_hw *);
+int igc_set_d0_lplu_state_i225(struct igc_hw *, bool);
+int igc_set_d3_lplu_state_i225(struct igc_hw *, bool);
+int igc_set_eee_i225(struct igc_hw *, bool, bool, bool);
+
+#define ID_LED_DEFAULT_I225 \
+ ((ID_LED_OFF1_ON2 << 8) | (ID_LED_DEF1_DEF2 << 4) | (ID_LED_OFF1_OFF2))
+
+#define ID_LED_DEFAULT_I225_SERDES \
+ ((ID_LED_DEF1_DEF2 << 8) | (ID_LED_DEF1_DEF2 << 4) | (ID_LED_OFF1_ON2))
+
+/* NVM offset defaults for I225 devices */
+#define NVM_INIT_CTRL_2_DEFAULT_I225 0x7243
+#define NVM_INIT_CTRL_4_DEFAULT_I225 0x00C1
+#define NVM_LED_1_CFG_DEFAULT_I225 0x0184
+#define NVM_LED_0_2_CFG_DEFAULT_I225 0x200C
+
+#define IGC_MRQC_ENABLE_RSS_4Q 0x00000002
+#define IGC_MRQC_ENABLE_VMDQ 0x00000003
+#define IGC_MRQC_ENABLE_VMDQ_RSS_2Q 0x00000005
+#define IGC_MRQC_RSS_FIELD_IPV4_UDP 0x00400000
+#define IGC_MRQC_RSS_FIELD_IPV6_UDP 0x00800000
+#define IGC_MRQC_RSS_FIELD_IPV6_UDP_EX 0x01000000
+#define IGC_I225_SHADOW_RAM_SIZE 4096
+#define IGC_I225_ERASE_CMD_OPCODE 0x02000000
+#define IGC_I225_WRITE_CMD_OPCODE 0x01000000
+#define IGC_FLSWCTL_DONE 0x40000000
+#define IGC_FLSWCTL_CMDV 0x10000000
+
+/* SRRCTL bit definitions */
+#define IGC_SRRCTL_BSIZEHDRSIZE_MASK 0x00000F00
+#define IGC_SRRCTL_DESCTYPE_LEGACY 0x00000000
+#define IGC_SRRCTL_DESCTYPE_HDR_SPLIT 0x04000000
+#define IGC_SRRCTL_DESCTYPE_HDR_SPLIT_ALWAYS 0x0A000000
+#define IGC_SRRCTL_DESCTYPE_HDR_REPLICATION 0x06000000
+#define IGC_SRRCTL_DESCTYPE_HDR_REPLICATION_LARGE_PKT 0x08000000
+#define IGC_SRRCTL_DESCTYPE_MASK 0x0E000000
+#define IGC_SRRCTL_DROP_EN 0x80000000
+#define IGC_SRRCTL_BSIZEPKT_MASK 0x0000007F
+#define IGC_SRRCTL_BSIZEHDR_MASK 0x00003F00
+
+#define IGC_RXDADV_RSSTYPE_MASK 0x0000000F
+#define IGC_RXDADV_RSSTYPE_SHIFT 12
+#define IGC_RXDADV_HDRBUFLEN_MASK 0x7FE0
+#define IGC_RXDADV_HDRBUFLEN_SHIFT 5
+#define IGC_RXDADV_SPLITHEADER_EN 0x00001000
+#define IGC_RXDADV_SPH 0x8000
+#define IGC_RXDADV_STAT_TS 0x10000 /* Pkt was time stamped */
+#define IGC_RXDADV_ERR_HBO 0x00800000
+
+/* RSS Hash results */
+#define IGC_RXDADV_RSSTYPE_NONE 0x00000000
+#define IGC_RXDADV_RSSTYPE_IPV4_TCP 0x00000001
+#define IGC_RXDADV_RSSTYPE_IPV4 0x00000002
+#define IGC_RXDADV_RSSTYPE_IPV6_TCP 0x00000003
+#define IGC_RXDADV_RSSTYPE_IPV6_EX 0x00000004
+#define IGC_RXDADV_RSSTYPE_IPV6 0x00000005
+#define IGC_RXDADV_RSSTYPE_IPV6_TCP_EX 0x00000006
+#define IGC_RXDADV_RSSTYPE_IPV4_UDP 0x00000007
+#define IGC_RXDADV_RSSTYPE_IPV6_UDP 0x00000008
+#define IGC_RXDADV_RSSTYPE_IPV6_UDP_EX 0x00000009
+
+/* RSS Packet Types as indicated in the receive descriptor */
+#define IGC_RXDADV_PKTTYPE_ILMASK 0x000000F0
+#define IGC_RXDADV_PKTTYPE_TLMASK 0x00000F00
+#define IGC_RXDADV_PKTTYPE_NONE 0x00000000
+#define IGC_RXDADV_PKTTYPE_IPV4 0x00000010 /* IPV4 hdr present */
+#define IGC_RXDADV_PKTTYPE_IPV4_EX 0x00000020 /* IPV4 hdr + extensions */
+#define IGC_RXDADV_PKTTYPE_IPV6 0x00000040 /* IPV6 hdr present */
+#define IGC_RXDADV_PKTTYPE_IPV6_EX 0x00000080 /* IPV6 hdr + extensions */
+#define IGC_RXDADV_PKTTYPE_TCP 0x00000100 /* TCP hdr present */
+#define IGC_RXDADV_PKTTYPE_UDP 0x00000200 /* UDP hdr present */
+#define IGC_RXDADV_PKTTYPE_SCTP 0x00000400 /* SCTP hdr present */
+#define IGC_RXDADV_PKTTYPE_NFS 0x00000800 /* NFS hdr present */
+
+#define IGC_RXDADV_PKTTYPE_IPSEC_ESP 0x00001000 /* IPSec ESP */
+#define IGC_RXDADV_PKTTYPE_IPSEC_AH 0x00002000 /* IPSec AH */
+#define IGC_RXDADV_PKTTYPE_LINKSEC 0x00004000 /* LinkSec Encap */
+#define IGC_RXDADV_PKTTYPE_ETQF 0x00008000 /* PKTTYPE is ETQF index */
+#define IGC_RXDADV_PKTTYPE_ETQF_MASK 0x00000070 /* ETQF has 8 indices */
+#define IGC_RXDADV_PKTTYPE_ETQF_SHIFT 4 /* Right-shift 4 bits */
+
+#endif /* _IGC_I225_H_ */
diff --git a/sys/dev/pci/igc_mac.c b/sys/dev/pci/igc_mac.c
new file mode 100644
index 00000000000..32fd4ce8a96
--- /dev/null
+++ b/sys/dev/pci/igc_mac.c
@@ -0,0 +1,833 @@
+/* $OpenBSD: igc_mac.c,v 1.1 2021/10/31 14:52:57 patrick Exp $ */
+/*-
+ * Copyright 2021 Intel Corp
+ * Copyright 2021 Rubicon Communications, LLC (Netgate)
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <dev/pci/igc_api.h>
+
+/**
+ * igc_init_mac_ops_generic - Initialize MAC function pointers
+ * @hw: pointer to the HW structure
+ *
+ * Setups up the function pointers to no-op functions
+ **/
+void
+igc_init_mac_ops_generic(struct igc_hw *hw)
+{
+ struct igc_mac_info *mac = &hw->mac;
+ DEBUGFUNC("igc_init_mac_ops_generic");
+
+ /* General Setup */
+ mac->ops.init_params = igc_null_ops_generic;
+ mac->ops.config_collision_dist = igc_config_collision_dist_generic;
+ mac->ops.rar_set = igc_rar_set_generic;
+}
+
+/**
+ * igc_null_ops_generic - No-op function, returns 0
+ * @hw: pointer to the HW structure
+ **/
+int
+igc_null_ops_generic(struct igc_hw IGC_UNUSEDARG *hw)
+{
+ DEBUGFUNC("igc_null_ops_generic");
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_write_vfta_generic - Write value to VLAN filter table
+ * @hw: pointer to the HW structure
+ * @offset: register offset in VLAN filter table
+ * @value: register value written to VLAN filter table
+ *
+ * Writes value at the given offset in the register array which stores
+ * the VLAN filter table.
+ **/
+void
+igc_write_vfta_generic(struct igc_hw *hw, uint32_t offset, uint32_t value)
+{
+ DEBUGFUNC("igc_write_vfta_generic");
+
+ IGC_WRITE_REG_ARRAY(hw, IGC_VFTA, offset, value);
+ IGC_WRITE_FLUSH(hw);
+}
+
+/**
+ * igc_init_rx_addrs_generic - Initialize receive address's
+ * @hw: pointer to the HW structure
+ * @rar_count: receive address registers
+ *
+ * Setup the receive address registers by setting the base receive address
+ * register to the devices MAC address and clearing all the other receive
+ * address registers to 0.
+ **/
+void
+igc_init_rx_addrs_generic(struct igc_hw *hw, uint16_t rar_count)
+{
+ uint32_t i;
+ uint8_t mac_addr[ETHER_ADDR_LEN] = {0};
+
+ DEBUGFUNC("igc_init_rx_addrs_generic");
+
+ /* Setup the receive address */
+ DEBUGOUT("Programming MAC Address into RAR[0]\n");
+
+ hw->mac.ops.rar_set(hw, hw->mac.addr, 0);
+
+ /* Zero out the other (rar_entry_count - 1) receive addresses */
+ DEBUGOUT1("Clearing RAR[1-%u]\n", rar_count-1);
+ for (i = 1; i < rar_count; i++)
+ hw->mac.ops.rar_set(hw, mac_addr, i);
+}
+
+/**
+ * igc_check_alt_mac_addr_generic - Check for alternate MAC addr
+ * @hw: pointer to the HW structure
+ *
+ * Checks the nvm for an alternate MAC address. An alternate MAC address
+ * can be setup by pre-boot software and must be treated like a permanent
+ * address and must override the actual permanent MAC address. If an
+ * alternate MAC address is found it is programmed into RAR0, replacing
+ * the permanent address that was installed into RAR0 by the Si on reset.
+ * This function will return SUCCESS unless it encounters an error while
+ * reading the EEPROM.
+ **/
+int
+igc_check_alt_mac_addr_generic(struct igc_hw *hw)
+{
+ uint32_t i;
+ int ret_val;
+ uint16_t offset, nvm_alt_mac_addr_offset, nvm_data;
+ uint8_t alt_mac_addr[ETHER_ADDR_LEN];
+
+ DEBUGFUNC("igc_check_alt_mac_addr_generic");
+
+ ret_val = hw->nvm.ops.read(hw, NVM_COMPAT, 1, &nvm_data);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = hw->nvm.ops.read(hw, NVM_ALT_MAC_ADDR_PTR, 1,
+ &nvm_alt_mac_addr_offset);
+ if (ret_val) {
+ DEBUGOUT("NVM Read Error\n");
+ return ret_val;
+ }
+
+ if ((nvm_alt_mac_addr_offset == 0xFFFF) ||
+ (nvm_alt_mac_addr_offset == 0x0000))
+ /* There is no Alternate MAC Address */
+ return IGC_SUCCESS;
+
+ if (hw->bus.func == IGC_FUNC_1)
+ nvm_alt_mac_addr_offset += IGC_ALT_MAC_ADDRESS_OFFSET_LAN1;
+ for (i = 0; i < ETHER_ADDR_LEN; i += 2) {
+ offset = nvm_alt_mac_addr_offset + (i >> 1);
+ ret_val = hw->nvm.ops.read(hw, offset, 1, &nvm_data);
+ if (ret_val) {
+ DEBUGOUT("NVM Read Error\n");
+ return ret_val;
+ }
+
+ alt_mac_addr[i] = (uint8_t)(nvm_data & 0xFF);
+ alt_mac_addr[i + 1] = (uint8_t)(nvm_data >> 8);
+ }
+
+ /* if multicast bit is set, the alternate address will not be used */
+ if (alt_mac_addr[0] & 0x01) {
+ DEBUGOUT("Ignoring Alternate Mac Address with MC bit set\n");
+ return IGC_SUCCESS;
+ }
+
+ /* We have a valid alternate MAC address, and we want to treat it the
+ * same as the normal permanent MAC address stored by the HW into the
+ * RAR. Do this by mapping this address into RAR0.
+ */
+ hw->mac.ops.rar_set(hw, alt_mac_addr, 0);
+
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_rar_set_generic - Set receive address register
+ * @hw: pointer to the HW structure
+ * @addr: pointer to the receive address
+ * @index: receive address array register
+ *
+ * Sets the receive address array register at index to the address passed
+ * in by addr.
+ **/
+int
+igc_rar_set_generic(struct igc_hw *hw, uint8_t *addr, uint32_t index)
+{
+ uint32_t rar_low, rar_high;
+
+ DEBUGFUNC("igc_rar_set_generic");
+
+ /* HW expects these in little endian so we reverse the byte order
+ * from network order (big endian) to little endian
+ */
+ rar_low = ((uint32_t) addr[0] | ((uint32_t) addr[1] << 8) |
+ ((uint32_t) addr[2] << 16) | ((uint32_t) addr[3] << 24));
+
+ rar_high = ((uint32_t) addr[4] | ((uint32_t) addr[5] << 8));
+
+ /* If MAC address zero, no need to set the AV bit */
+ if (rar_low || rar_high)
+ rar_high |= IGC_RAH_AV;
+
+ /* Some bridges will combine consecutive 32-bit writes into
+ * a single burst write, which will malfunction on some parts.
+ * The flushes avoid this.
+ */
+ IGC_WRITE_REG(hw, IGC_RAL(index), rar_low);
+ IGC_WRITE_FLUSH(hw);
+ IGC_WRITE_REG(hw, IGC_RAH(index), rar_high);
+ IGC_WRITE_FLUSH(hw);
+
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_hash_mc_addr_generic - Generate a multicast hash value
+ * @hw: pointer to the HW structure
+ * @mc_addr: pointer to a multicast address
+ *
+ * Generates a multicast address hash value which is used to determine
+ * the multicast filter table array address and new table value.
+ **/
+int
+igc_hash_mc_addr_generic(struct igc_hw *hw, uint8_t *mc_addr)
+{
+ uint32_t hash_value, hash_mask;
+ uint8_t bit_shift = 0;
+
+ DEBUGFUNC("igc_hash_mc_addr_generic");
+
+ /* Register count multiplied by bits per register */
+ hash_mask = (hw->mac.mta_reg_count * 32) - 1;
+
+ /* For a mc_filter_type of 0, bit_shift is the number of left-shifts
+ * where 0xFF would still fall within the hash mask.
+ */
+ while (hash_mask >> bit_shift != 0xFF)
+ bit_shift++;
+
+ /* The portion of the address that is used for the hash table
+ * is determined by the mc_filter_type setting.
+ * The algorithm is such that there is a total of 8 bits of shifting.
+ * The bit_shift for a mc_filter_type of 0 represents the number of
+ * left-shifts where the MSB of mc_addr[5] would still fall within
+ * the hash_mask. Case 0 does this exactly. Since there are a total
+ * of 8 bits of shifting, then mc_addr[4] will shift right the
+ * remaining number of bits. Thus 8 - bit_shift. The rest of the
+ * cases are a variation of this algorithm...essentially raising the
+ * number of bits to shift mc_addr[5] left, while still keeping the
+ * 8-bit shifting total.
+ *
+ * For example, given the following Destination MAC Address and an
+ * mta register count of 128 (thus a 4096-bit vector and 0xFFF mask),
+ * we can see that the bit_shift for case 0 is 4. These are the hash
+ * values resulting from each mc_filter_type...
+ * [0] [1] [2] [3] [4] [5]
+ * 01 AA 00 12 34 56
+ * LSB MSB
+ *
+ * case 0: hash_value = ((0x34 >> 4) | (0x56 << 4)) & 0xFFF = 0x563
+ * case 1: hash_value = ((0x34 >> 3) | (0x56 << 5)) & 0xFFF = 0xAC6
+ * case 2: hash_value = ((0x34 >> 2) | (0x56 << 6)) & 0xFFF = 0x163
+ * case 3: hash_value = ((0x34 >> 0) | (0x56 << 8)) & 0xFFF = 0x634
+ */
+ switch (hw->mac.mc_filter_type) {
+ default:
+ case 0:
+ break;
+ case 1:
+ bit_shift += 1;
+ break;
+ case 2:
+ bit_shift += 2;
+ break;
+ case 3:
+ bit_shift += 4;
+ break;
+ }
+
+ hash_value = hash_mask & (((mc_addr[4] >> (8 - bit_shift)) |
+ (((uint16_t) mc_addr[5]) << bit_shift)));
+
+ return hash_value;
+}
+
+/**
+ * igc_update_mc_addr_list_generic - 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
+ *
+ * Updates entire Multicast Table Array.
+ * The caller must have a packed mc_addr_list of multicast addresses.
+ **/
+void
+igc_update_mc_addr_list_generic(struct igc_hw *hw, uint8_t *mc_addr_list,
+ uint32_t mc_addr_count)
+{
+ uint32_t hash_value, hash_bit, hash_reg;
+ int i;
+
+ DEBUGFUNC("igc_update_mc_addr_list_generic");
+
+ /* clear mta_shadow */
+ memset(&hw->mac.mta_shadow, 0, sizeof(hw->mac.mta_shadow));
+
+ /* update mta_shadow from mc_addr_list */
+ for (i = 0; (uint32_t)i < mc_addr_count; i++) {
+ hash_value = igc_hash_mc_addr_generic(hw, mc_addr_list);
+
+ hash_reg = (hash_value >> 5) & (hw->mac.mta_reg_count - 1);
+ hash_bit = hash_value & 0x1F;
+
+ hw->mac.mta_shadow[hash_reg] |= (1 << hash_bit);
+ mc_addr_list += (ETHER_ADDR_LEN);
+ }
+
+ /* replace the entire MTA table */
+ for (i = hw->mac.mta_reg_count - 1; i >= 0; i--)
+ IGC_WRITE_REG_ARRAY(hw, IGC_MTA, i, hw->mac.mta_shadow[i]);
+ IGC_WRITE_FLUSH(hw);
+}
+
+/**
+ * igc_clear_hw_cntrs_base_generic - Clear base hardware counters
+ * @hw: pointer to the HW structure
+ *
+ * Clears the base hardware counters by reading the counter registers.
+ **/
+void
+igc_clear_hw_cntrs_base_generic(struct igc_hw *hw)
+{
+ DEBUGFUNC("igc_clear_hw_cntrs_base_generic");
+
+ IGC_READ_REG(hw, IGC_CRCERRS);
+ IGC_READ_REG(hw, IGC_MPC);
+ IGC_READ_REG(hw, IGC_SCC);
+ IGC_READ_REG(hw, IGC_ECOL);
+ IGC_READ_REG(hw, IGC_MCC);
+ IGC_READ_REG(hw, IGC_LATECOL);
+ IGC_READ_REG(hw, IGC_COLC);
+ IGC_READ_REG(hw, IGC_RERC);
+ IGC_READ_REG(hw, IGC_DC);
+ IGC_READ_REG(hw, IGC_RLEC);
+ IGC_READ_REG(hw, IGC_XONRXC);
+ IGC_READ_REG(hw, IGC_XONTXC);
+ IGC_READ_REG(hw, IGC_XOFFRXC);
+ IGC_READ_REG(hw, IGC_XOFFTXC);
+ IGC_READ_REG(hw, IGC_FCRUC);
+ IGC_READ_REG(hw, IGC_GPRC);
+ IGC_READ_REG(hw, IGC_BPRC);
+ IGC_READ_REG(hw, IGC_MPRC);
+ IGC_READ_REG(hw, IGC_GPTC);
+ IGC_READ_REG(hw, IGC_GORCL);
+ IGC_READ_REG(hw, IGC_GORCH);
+ IGC_READ_REG(hw, IGC_GOTCL);
+ IGC_READ_REG(hw, IGC_GOTCH);
+ IGC_READ_REG(hw, IGC_RNBC);
+ IGC_READ_REG(hw, IGC_RUC);
+ IGC_READ_REG(hw, IGC_RFC);
+ IGC_READ_REG(hw, IGC_ROC);
+ IGC_READ_REG(hw, IGC_RJC);
+ IGC_READ_REG(hw, IGC_TORL);
+ IGC_READ_REG(hw, IGC_TORH);
+ IGC_READ_REG(hw, IGC_TOTL);
+ IGC_READ_REG(hw, IGC_TOTH);
+ IGC_READ_REG(hw, IGC_TPR);
+ IGC_READ_REG(hw, IGC_TPT);
+ IGC_READ_REG(hw, IGC_MPTC);
+ IGC_READ_REG(hw, IGC_BPTC);
+ IGC_READ_REG(hw, IGC_TLPIC);
+ IGC_READ_REG(hw, IGC_RLPIC);
+ IGC_READ_REG(hw, IGC_RXDMTC);
+}
+
+/**
+ * igc_setup_link_generic - Setup flow control and link settings
+ * @hw: pointer to the HW structure
+ *
+ * Determines which flow control settings to use, then configures flow
+ * control. Calls the appropriate media-specific link configuration
+ * function. Assuming the adapter has a valid link partner, a valid link
+ * should be established. Assumes the hardware has previously been reset
+ * and the transmitter and receiver are not enabled.
+ **/
+int
+igc_setup_link_generic(struct igc_hw *hw)
+{
+ int ret_val;
+
+ DEBUGFUNC("igc_setup_link_generic");
+
+ /* In the case of the phy reset being blocked, we already have a link.
+ * We do not need to set it up again.
+ */
+ if (hw->phy.ops.check_reset_block && hw->phy.ops.check_reset_block(hw))
+ return IGC_SUCCESS;
+
+ /* If requested flow control is set to default, set flow control
+ * for both 'rx' and 'tx' pause frames.
+ */
+ if (hw->fc.requested_mode == igc_fc_default) {
+ hw->fc.requested_mode = igc_fc_full;
+ }
+
+ /* Save off the requested flow control mode for use later. Depending
+ * on the link partner's capabilities, we may or may not use this mode.
+ */
+ hw->fc.current_mode = hw->fc.requested_mode;
+
+ DEBUGOUT1("After fix-ups FlowControl is now = %x\n",
+ hw->fc.current_mode);
+
+ /* Call the necessary media_type subroutine to configure the link. */
+ ret_val = hw->mac.ops.setup_physical_interface(hw);
+ if (ret_val)
+ return ret_val;
+
+ /* Initialize the flow control address, type, and PAUSE timer
+ * registers to their default values. This is done even if flow
+ * control is disabled, because it does not hurt anything to
+ * initialize these registers.
+ */
+ IGC_WRITE_REG(hw, IGC_FCT, FLOW_CONTROL_TYPE);
+ IGC_WRITE_REG(hw, IGC_FCAH, FLOW_CONTROL_ADDRESS_HIGH);
+ IGC_WRITE_REG(hw, IGC_FCAL, FLOW_CONTROL_ADDRESS_LOW);
+
+ IGC_WRITE_REG(hw, IGC_FCTTV, hw->fc.pause_time);
+
+ return igc_set_fc_watermarks_generic(hw);
+}
+
+/**
+ * igc_config_collision_dist_generic - Configure collision distance
+ * @hw: pointer to the HW structure
+ *
+ * Configures the collision distance to the default value and is used
+ * during link setup.
+ **/
+void
+igc_config_collision_dist_generic(struct igc_hw *hw)
+{
+ uint32_t tctl;
+
+ DEBUGFUNC("igc_config_collision_dist_generic");
+
+ tctl = IGC_READ_REG(hw, IGC_TCTL);
+
+ tctl &= ~IGC_TCTL_COLD;
+ tctl |= IGC_COLLISION_DISTANCE << IGC_COLD_SHIFT;
+
+ IGC_WRITE_REG(hw, IGC_TCTL, tctl);
+ IGC_WRITE_FLUSH(hw);
+}
+
+/**
+ * igc_set_fc_watermarks_generic - Set flow control high/low watermarks
+ * @hw: pointer to the HW structure
+ *
+ * Sets the flow control high/low threshold (watermark) registers. If
+ * flow control XON frame transmission is enabled, then set XON frame
+ * transmission as well.
+ **/
+int
+igc_set_fc_watermarks_generic(struct igc_hw *hw)
+{
+ uint32_t fcrtl = 0, fcrth = 0;
+
+ DEBUGFUNC("igc_set_fc_watermarks_generic");
+
+ /* Set the flow control receive threshold registers. Normally,
+ * these registers will be set to a default threshold that may be
+ * adjusted later by the driver's runtime code. However, if the
+ * ability to transmit pause frames is not enabled, then these
+ * registers will be set to 0.
+ */
+ if (hw->fc.current_mode & igc_fc_tx_pause) {
+ /* We need to set up the Receive Threshold high and low water
+ * marks as well as (optionally) enabling the transmission of
+ * XON frames.
+ */
+ fcrtl = hw->fc.low_water;
+ if (hw->fc.send_xon)
+ fcrtl |= IGC_FCRTL_XONE;
+
+ fcrth = hw->fc.high_water;
+ }
+ IGC_WRITE_REG(hw, IGC_FCRTL, fcrtl);
+ IGC_WRITE_REG(hw, IGC_FCRTH, fcrth);
+
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_force_mac_fc_generic - Force the MAC's flow control settings
+ * @hw: pointer to the HW structure
+ *
+ * Force the MAC's flow control settings. Sets the TFCE and RFCE bits in the
+ * device control register to reflect the adapter settings. TFCE and RFCE
+ * need to be explicitly set by software when a copper PHY is used because
+ * autonegotiation is managed by the PHY rather than the MAC. Software must
+ * also configure these bits when link is forced on a fiber connection.
+ **/
+int
+igc_force_mac_fc_generic(struct igc_hw *hw)
+{
+ uint32_t ctrl;
+
+ DEBUGFUNC("igc_force_mac_fc_generic");
+
+ ctrl = IGC_READ_REG(hw, IGC_CTRL);
+
+ /* Because we didn't get link via the internal auto-negotiation
+ * mechanism (we either forced link or we got link via PHY
+ * auto-neg), we have to manually enable/disable transmit an
+ * receive flow control.
+ *
+ * The "Case" statement below enables/disable flow control
+ * according to the "hw->fc.current_mode" parameter.
+ *
+ * The possible values of the "fc" parameter are:
+ * 0: Flow control is completely disabled
+ * 1: Rx flow control is enabled (we can receive pause
+ * frames but not send pause frames).
+ * 2: Tx flow control is enabled (we can send pause frames
+ * frames but we do not receive pause frames).
+ * 3: Both Rx and Tx flow control (symmetric) is enabled.
+ * other: No other values should be possible at this point.
+ */
+ DEBUGOUT1("hw->fc.current_mode = %u\n", hw->fc.current_mode);
+
+ switch (hw->fc.current_mode) {
+ case igc_fc_none:
+ ctrl &= (~(IGC_CTRL_TFCE | IGC_CTRL_RFCE));
+ break;
+ case igc_fc_rx_pause:
+ ctrl &= (~IGC_CTRL_TFCE);
+ ctrl |= IGC_CTRL_RFCE;
+ break;
+ case igc_fc_tx_pause:
+ ctrl &= (~IGC_CTRL_RFCE);
+ ctrl |= IGC_CTRL_TFCE;
+ break;
+ case igc_fc_full:
+ ctrl |= (IGC_CTRL_TFCE | IGC_CTRL_RFCE);
+ break;
+ default:
+ DEBUGOUT("Flow control param set incorrectly\n");
+ return -IGC_ERR_CONFIG;
+ }
+
+ IGC_WRITE_REG(hw, IGC_CTRL, ctrl);
+
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_config_fc_after_link_up_generic - Configures flow control after link
+ * @hw: pointer to the HW structure
+ *
+ * Checks the status of auto-negotiation after link up to ensure that the
+ * speed and duplex were not forced. If the link needed to be forced, then
+ * flow control needs to be forced also. If auto-negotiation is enabled
+ * and did not fail, then we configure flow control based on our link
+ * partner.
+ **/
+int
+igc_config_fc_after_link_up_generic(struct igc_hw *hw)
+{
+ struct igc_mac_info *mac = &hw->mac;
+ uint16_t mii_status_reg, mii_nway_adv_reg, mii_nway_lp_ability_reg;
+ uint16_t speed, duplex;
+ int ret_val = IGC_SUCCESS;
+
+ DEBUGFUNC("igc_config_fc_after_link_up_generic");
+
+ if (ret_val) {
+ DEBUGOUT("Error forcing flow control settings\n");
+ return ret_val;
+ }
+
+ /* Check for the case where we have copper media and auto-neg is
+ * enabled. In this case, we need to check and see if Auto-Neg
+ * has completed, and if so, how the PHY and link partner has
+ * flow control configured.
+ */
+ if (mac->autoneg) {
+ /* Read the MII Status Register and check to see if AutoNeg
+ * has completed. We read this twice because this reg has
+ * some "sticky" (latched) bits.
+ */
+ ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &mii_status_reg);
+ if (ret_val)
+ return ret_val;
+ ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &mii_status_reg);
+ if (ret_val)
+ return ret_val;
+
+ if (!(mii_status_reg & MII_SR_AUTONEG_COMPLETE))
+ return ret_val;
+
+ /* The AutoNeg process has completed, so we now need to
+ * read both the Auto Negotiation Advertisement
+ * Register (Address 4) and the Auto_Negotiation Base
+ * Page Ability Register (Address 5) to determine how
+ * flow control was negotiated.
+ */
+ ret_val = hw->phy.ops.read_reg(hw, PHY_AUTONEG_ADV,
+ &mii_nway_adv_reg);
+ if (ret_val)
+ return ret_val;
+ ret_val = hw->phy.ops.read_reg(hw, PHY_LP_ABILITY,
+ &mii_nway_lp_ability_reg);
+ if (ret_val)
+ return ret_val;
+
+ /* Two bits in the Auto Negotiation Advertisement Register
+ * (Address 4) and two bits in the Auto Negotiation Base
+ * Page Ability Register (Address 5) determine flow control
+ * for both the PHY and the link partner. The following
+ * table, taken out of the IEEE 802.3ab/D6.0 dated March 25,
+ * 1999, describes these PAUSE resolution bits and how flow
+ * control is determined based upon these settings.
+ * NOTE: DC = Don't Care
+ *
+ * LOCAL DEVICE | LINK PARTNER
+ * PAUSE | ASM_DIR | PAUSE | ASM_DIR | NIC Resolution
+ *-------|---------|-------|---------|--------------------
+ * 0 | 0 | DC | DC | igc_fc_none
+ * 0 | 1 | 0 | DC | igc_fc_none
+ * 0 | 1 | 1 | 0 | igc_fc_none
+ * 0 | 1 | 1 | 1 | igc_fc_tx_pause
+ * 1 | 0 | 0 | DC | igc_fc_none
+ * 1 | DC | 1 | DC | igc_fc_full
+ * 1 | 1 | 0 | 0 | igc_fc_none
+ * 1 | 1 | 0 | 1 | igc_fc_rx_pause
+ *
+ * Are both PAUSE bits set to 1? If so, this implies
+ * Symmetric Flow Control is enabled at both ends. The
+ * ASM_DIR bits are irrelevant per the spec.
+ *
+ * For Symmetric Flow Control:
+ *
+ * LOCAL DEVICE | LINK PARTNER
+ * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
+ *-------|---------|-------|---------|--------------------
+ * 1 | DC | 1 | DC | IGC_fc_full
+ *
+ */
+ if ((mii_nway_adv_reg & NWAY_AR_PAUSE) &&
+ (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE)) {
+ /* Now we need to check if the user selected Rx ONLY
+ * of pause frames. In this case, we had to advertise
+ * FULL flow control because we could not advertise Rx
+ * ONLY. Hence, we must now check to see if we need to
+ * turn OFF the TRANSMISSION of PAUSE frames.
+ */
+ if (hw->fc.requested_mode == igc_fc_full)
+ hw->fc.current_mode = igc_fc_full;
+ else
+ hw->fc.current_mode = igc_fc_rx_pause;
+ }
+ /* For receiving PAUSE frames ONLY.
+ *
+ * LOCAL DEVICE | LINK PARTNER
+ * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
+ *-------|---------|-------|---------|--------------------
+ * 0 | 1 | 1 | 1 | igc_fc_tx_pause
+ */
+ else if (!(mii_nway_adv_reg & NWAY_AR_PAUSE) &&
+ (mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&
+ (mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
+ (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
+ hw->fc.current_mode = igc_fc_tx_pause;
+ }
+ /* For transmitting PAUSE frames ONLY.
+ *
+ * LOCAL DEVICE | LINK PARTNER
+ * PAUSE | ASM_DIR | PAUSE | ASM_DIR | Result
+ *-------|---------|-------|---------|--------------------
+ * 1 | 1 | 0 | 1 | igc_fc_rx_pause
+ */
+ else if ((mii_nway_adv_reg & NWAY_AR_PAUSE) &&
+ (mii_nway_adv_reg & NWAY_AR_ASM_DIR) &&
+ !(mii_nway_lp_ability_reg & NWAY_LPAR_PAUSE) &&
+ (mii_nway_lp_ability_reg & NWAY_LPAR_ASM_DIR)) {
+ hw->fc.current_mode = igc_fc_rx_pause;
+ } else {
+ /* Per the IEEE spec, at this point flow control
+ * should be disabled.
+ */
+ hw->fc.current_mode = igc_fc_none;
+ DEBUGOUT("Flow Control = NONE.\n");
+ }
+
+ /* Now we need to do one last check... If we auto-
+ * negotiated to HALF DUPLEX, flow control should not be
+ * enabled per IEEE 802.3 spec.
+ */
+ ret_val = mac->ops.get_link_up_info(hw, &speed, &duplex);
+ if (ret_val) {
+ DEBUGOUT("Error getting link speed and duplex\n");
+ return ret_val;
+ }
+
+ if (duplex == HALF_DUPLEX)
+ hw->fc.current_mode = igc_fc_none;
+
+ /* Now we call a subroutine to actually force the MAC
+ * controller to use the correct flow control settings.
+ */
+ ret_val = igc_force_mac_fc_generic(hw);
+ if (ret_val) {
+ DEBUGOUT("Error forcing flow control settings\n");
+ return ret_val;
+ }
+ }
+
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_get_speed_and_duplex_copper_generic - Retrieve current speed/duplex
+ * @hw: pointer to the HW structure
+ * @speed: stores the current speed
+ * @duplex: stores the current duplex
+ *
+ * Read the status register for the current speed/duplex and store the current
+ * speed and duplex for copper connections.
+ **/
+int
+igc_get_speed_and_duplex_copper_generic(struct igc_hw *hw, uint16_t *speed,
+ uint16_t *duplex)
+{
+ uint32_t status;
+
+ DEBUGFUNC("igc_get_speed_and_duplex_copper_generic");
+
+ status = IGC_READ_REG(hw, IGC_STATUS);
+ if (status & IGC_STATUS_SPEED_1000) {
+ /* For I225, STATUS will indicate 1G speed in both 1 Gbps
+ * and 2.5 Gbps link modes. An additional bit is used
+ * to differentiate between 1 Gbps and 2.5 Gbps.
+ */
+ if ((hw->mac.type == igc_i225) &&
+ (status & IGC_STATUS_SPEED_2500)) {
+ *speed = SPEED_2500;
+ DEBUGOUT("2500 Mbs, ");
+ } else {
+ *speed = SPEED_1000;
+ DEBUGOUT("1000 Mbs, ");
+ }
+ } else if (status & IGC_STATUS_SPEED_100) {
+ *speed = SPEED_100;
+ DEBUGOUT("100 Mbs, ");
+ } else {
+ *speed = SPEED_10;
+ DEBUGOUT("10 Mbs, ");
+ }
+
+ if (status & IGC_STATUS_FD) {
+ *duplex = FULL_DUPLEX;
+ DEBUGOUT("Full Duplex\n");
+ } else {
+ *duplex = HALF_DUPLEX;
+ DEBUGOUT("Half Duplex\n");
+ }
+
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_put_hw_semaphore_generic - Release hardware semaphore
+ * @hw: pointer to the HW structure
+ *
+ * Release hardware semaphore used to access the PHY or NVM
+ **/
+void
+igc_put_hw_semaphore_generic(struct igc_hw *hw)
+{
+ uint32_t swsm;
+
+ DEBUGFUNC("igc_put_hw_semaphore_generic");
+
+ swsm = IGC_READ_REG(hw, IGC_SWSM);
+ swsm &= ~(IGC_SWSM_SMBI | IGC_SWSM_SWESMBI);
+
+ IGC_WRITE_REG(hw, IGC_SWSM, swsm);
+}
+
+/**
+ * igc_get_auto_rd_done_generic - Check for auto read completion
+ * @hw: pointer to the HW structure
+ *
+ * Check EEPROM for Auto Read done bit.
+ **/
+int
+igc_get_auto_rd_done_generic(struct igc_hw *hw)
+{
+ int i = 0;
+
+ DEBUGFUNC("igc_get_auto_rd_done_generic");
+
+ while (i < AUTO_READ_DONE_TIMEOUT) {
+ if (IGC_READ_REG(hw, IGC_EECD) & IGC_EECD_AUTO_RD)
+ break;
+ msec_delay(1);
+ i++;
+ }
+
+ if (i == AUTO_READ_DONE_TIMEOUT) {
+ DEBUGOUT("Auto read by HW from NVM has not completed.\n");
+ return -IGC_ERR_RESET;
+ }
+
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_disable_pcie_master_generic - Disables PCI-express master access
+ * @hw: pointer to the HW structure
+ *
+ * Returns IGC_SUCCESS if successful, else returns -10
+ * (-IGC_ERR_MASTER_REQUESTS_PENDING) if master disable bit has not caused
+ * the master requests to be disabled.
+ *
+ * Disables PCI-Express master access and verifies there are no pending
+ * requests.
+ **/
+int
+igc_disable_pcie_master_generic(struct igc_hw *hw)
+{
+ uint32_t ctrl;
+ int timeout = MASTER_DISABLE_TIMEOUT;
+
+ DEBUGFUNC("igc_disable_pcie_master_generic");
+
+ ctrl = IGC_READ_REG(hw, IGC_CTRL);
+ ctrl |= IGC_CTRL_GIO_MASTER_DISABLE;
+ IGC_WRITE_REG(hw, IGC_CTRL, ctrl);
+
+ while (timeout) {
+ if (!(IGC_READ_REG(hw, IGC_STATUS) &
+ IGC_STATUS_GIO_MASTER_ENABLE))
+ break;
+ DELAY(100);
+ timeout--;
+ }
+
+ if (!timeout) {
+ DEBUGOUT("Master requests are pending.\n");
+ return -IGC_ERR_MASTER_REQUESTS_PENDING;
+ }
+
+ return IGC_SUCCESS;
+}
diff --git a/sys/dev/pci/igc_mac.h b/sys/dev/pci/igc_mac.h
new file mode 100644
index 00000000000..0b800e9a1f9
--- /dev/null
+++ b/sys/dev/pci/igc_mac.h
@@ -0,0 +1,46 @@
+/* $OpenBSD: igc_mac.h,v 1.1 2021/10/31 14:52:57 patrick Exp $ */
+/*-
+ * Copyright 2021 Intel Corp
+ * Copyright 2021 Rubicon Communications, LLC (Netgate)
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _IGC_MAC_H_
+#define _IGC_MAC_H_
+
+void igc_init_mac_ops_generic(struct igc_hw *);
+int igc_null_ops_generic(struct igc_hw *);
+int igc_null_link_info(struct igc_hw *, uint16_t *, uint16_t *);
+bool igc_null_mng_mode(struct igc_hw *);
+void igc_null_update_mc(struct igc_hw *, uint8_t *h, uint32_t);
+void igc_null_write_vfta(struct igc_hw *, uint32_t a, uint32_t);
+int igc_check_for_copper_link_generic(struct igc_hw *);
+int igc_config_fc_after_link_up_generic(struct igc_hw *);
+int igc_disable_pcie_master_generic(struct igc_hw *);
+int igc_force_mac_fc_generic(struct igc_hw *);
+int igc_get_auto_rd_done_generic(struct igc_hw *);
+int igc_get_bus_info_pcie_generic(struct igc_hw *);
+void igc_set_lan_id_single_port(struct igc_hw *);
+int igc_get_speed_and_duplex_copper_generic(struct igc_hw *, uint16_t *,
+ uint16_t *);
+void igc_update_mc_addr_list_generic(struct igc_hw *, uint8_t *, uint32_t);
+int igc_rar_set_generic(struct igc_hw *, uint8_t *, uint32_t);
+int igc_set_fc_watermarks_generic(struct igc_hw *);
+int igc_setup_link_generic(struct igc_hw *);
+int igc_validate_mdi_setting_crossover_generic(struct igc_hw *);
+
+int igc_hash_mc_addr_generic(struct igc_hw *, uint8_t *);
+
+void igc_clear_hw_cntrs_base_generic(struct igc_hw *);
+void igc_clear_vfta_generic(struct igc_hw *);
+void igc_init_rx_addrs_generic(struct igc_hw *, uint16_t);
+void igc_pcix_mmrbc_workaround_generic(struct igc_hw *);
+void igc_put_hw_semaphore_generic(struct igc_hw *);
+int igc_check_alt_mac_addr_generic(struct igc_hw *);
+void igc_set_pcie_no_snoop_generic(struct igc_hw *, uint32_t);
+void igc_write_vfta_generic(struct igc_hw *, uint32_t, uint32_t);
+void igc_config_collision_dist_generic(struct igc_hw *);
+
+#endif /* _IGC_MAC_H_ */
diff --git a/sys/dev/pci/igc_nvm.c b/sys/dev/pci/igc_nvm.c
new file mode 100644
index 00000000000..2659b59eb6e
--- /dev/null
+++ b/sys/dev/pci/igc_nvm.c
@@ -0,0 +1,232 @@
+/* $OpenBSD: igc_nvm.c,v 1.1 2021/10/31 14:52:57 patrick Exp $ */
+/*-
+ * Copyright 2021 Intel Corp
+ * Copyright 2021 Rubicon Communications, LLC (Netgate)
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <dev/pci/igc_api.h>
+
+/**
+ * igc_init_nvm_ops_generic - Initialize NVM function pointers
+ * @hw: pointer to the HW structure
+ *
+ * Setups up the function pointers to no-op functions
+ **/
+void
+igc_init_nvm_ops_generic(struct igc_hw *hw)
+{
+ struct igc_nvm_info *nvm = &hw->nvm;
+ DEBUGFUNC("igc_init_nvm_ops_generic");
+
+ /* Initialize function pointers */
+ nvm->ops.init_params = igc_null_ops_generic;
+ nvm->ops.acquire = igc_null_ops_generic;
+ nvm->ops.read = igc_null_read_nvm;
+ nvm->ops.release = igc_null_nvm_generic;
+ nvm->ops.reload = igc_reload_nvm_generic;
+ nvm->ops.update = igc_null_ops_generic;
+ nvm->ops.validate = igc_null_ops_generic;
+ nvm->ops.write = igc_null_write_nvm;
+}
+
+/**
+ * igc_null_nvm_read - No-op function, return 0
+ * @hw: pointer to the HW structure
+ * @a: dummy variable
+ * @b: dummy variable
+ * @c: dummy variable
+ **/
+int
+igc_null_read_nvm(struct igc_hw IGC_UNUSEDARG *hw, uint16_t IGC_UNUSEDARG a,
+ uint16_t IGC_UNUSEDARG b, uint16_t IGC_UNUSEDARG *c)
+{
+ DEBUGFUNC("igc_null_read_nvm");
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_null_nvm_generic - No-op function, return void
+ * @hw: pointer to the HW structure
+ **/
+void
+igc_null_nvm_generic(struct igc_hw IGC_UNUSEDARG *hw)
+{
+ DEBUGFUNC("igc_null_nvm_generic");
+ return;
+}
+
+/**
+ * igc_reload_nvm_generic - Reloads EEPROM
+ * @hw: pointer to the HW structure
+ *
+ * Reloads the EEPROM by setting the "Reinitialize from EEPROM" bit in the
+ * extended control register.
+ **/
+void
+igc_reload_nvm_generic(struct igc_hw *hw)
+{
+ uint32_t ctrl_ext;
+
+ DEBUGFUNC("igc_reload_nvm_generic");
+
+ DELAY(10);
+ ctrl_ext = IGC_READ_REG(hw, IGC_CTRL_EXT);
+ ctrl_ext |= IGC_CTRL_EXT_EE_RST;
+ IGC_WRITE_REG(hw, IGC_CTRL_EXT, ctrl_ext);
+ IGC_WRITE_FLUSH(hw);
+}
+
+/**
+ * igc_null_write_nvm - No-op function, return 0
+ * @hw: pointer to the HW structure
+ * @a: dummy variable
+ * @b: dummy variable
+ * @c: dummy variable
+ **/
+int
+igc_null_write_nvm(struct igc_hw IGC_UNUSEDARG *hw, uint16_t IGC_UNUSEDARG a,
+ uint16_t IGC_UNUSEDARG b, uint16_t IGC_UNUSEDARG *c)
+{
+ DEBUGFUNC("igc_null_write_nvm");
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_poll_eerd_eewr_done - Poll for EEPROM read/write completion
+ * @hw: pointer to the HW structure
+ * @ee_reg: EEPROM flag for polling
+ *
+ * Polls the EEPROM status bit for either read or write completion based
+ * upon the value of 'ee_reg'.
+ **/
+int
+igc_poll_eerd_eewr_done(struct igc_hw *hw, int ee_reg)
+{
+ uint32_t attempts = 100000;
+ uint32_t i, reg = 0;
+
+ DEBUGFUNC("igc_poll_eerd_eewr_done");
+
+ for (i = 0; i < attempts; i++) {
+ if (ee_reg == IGC_NVM_POLL_READ)
+ reg = IGC_READ_REG(hw, IGC_EERD);
+ else
+ reg = IGC_READ_REG(hw, IGC_EEWR);
+
+ if (reg & IGC_NVM_RW_REG_DONE)
+ return IGC_SUCCESS;
+
+ DELAY(5);
+ }
+
+ return -IGC_ERR_NVM;
+}
+
+/**
+ * igc_read_nvm_eerd - Reads EEPROM using EERD register
+ * @hw: pointer to the HW structure
+ * @offset: offset of word in the EEPROM to read
+ * @words: number of words to read
+ * @data: word read from the EEPROM
+ *
+ * Reads a 16 bit word from the EEPROM using the EERD register.
+ **/
+int
+igc_read_nvm_eerd(struct igc_hw *hw, uint16_t offset, uint16_t words,
+ uint16_t *data)
+{
+ struct igc_nvm_info *nvm = &hw->nvm;
+ uint32_t i, eerd = 0;
+ int ret_val = IGC_SUCCESS;
+
+ DEBUGFUNC("igc_read_nvm_eerd");
+
+ /* A check for invalid values: offset too large, too many words,
+ * too many words for the offset, and not enough words.
+ */
+ if ((offset >= nvm->word_size) || (words > (nvm->word_size - offset)) ||
+ (words == 0)) {
+ DEBUGOUT("nvm parameter(s) out of bounds\n");
+ return -IGC_ERR_NVM;
+ }
+
+ for (i = 0; i < words; i++) {
+ eerd = ((offset + i) << IGC_NVM_RW_ADDR_SHIFT) +
+ IGC_NVM_RW_REG_START;
+
+ IGC_WRITE_REG(hw, IGC_EERD, eerd);
+ ret_val = igc_poll_eerd_eewr_done(hw, IGC_NVM_POLL_READ);
+ if (ret_val)
+ break;
+
+ data[i] = (IGC_READ_REG(hw, IGC_EERD) >> IGC_NVM_RW_REG_DATA);
+ }
+
+ if (ret_val)
+ DEBUGOUT1("NVM read error: %d\n", ret_val);
+
+ return ret_val;
+}
+
+/**
+ * igc_read_mac_addr_generic - Read device MAC address
+ * @hw: pointer to the HW structure
+ *
+ * Reads the device MAC address from the EEPROM and stores the value.
+ * Since devices with two ports use the same EEPROM, we increment the
+ * last bit in the MAC address for the second port.
+ **/
+int
+igc_read_mac_addr_generic(struct igc_hw *hw)
+{
+ uint32_t rar_high, rar_low;
+ uint16_t i;
+
+ rar_high = IGC_READ_REG(hw, IGC_RAH(0));
+ rar_low = IGC_READ_REG(hw, IGC_RAL(0));
+
+ for (i = 0; i < IGC_RAL_MAC_ADDR_LEN; i++)
+ hw->mac.perm_addr[i] = (uint8_t)(rar_low >> (i * 8));
+
+ for (i = 0; i < IGC_RAH_MAC_ADDR_LEN; i++)
+ hw->mac.perm_addr[i+4] = (uint8_t)(rar_high >> (i * 8));
+
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ hw->mac.addr[i] = hw->mac.perm_addr[i];
+
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_validate_nvm_checksum_generic - Validate EEPROM checksum
+ * @hw: pointer to the HW structure
+ *
+ * Calculates the EEPROM checksum by reading/adding each word of the EEPROM
+ * and then verifies that the sum of the EEPROM is equal to 0xBABA.
+ **/
+int
+igc_validate_nvm_checksum_generic(struct igc_hw *hw)
+{
+ uint16_t checksum = 0;
+ uint16_t i, nvm_data;
+ int ret_val;
+
+ DEBUGFUNC("igc_validate_nvm_checksum_generic");
+
+ for (i = 0; i < (NVM_CHECKSUM_REG + 1); i++) {
+ ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data);
+ if (ret_val) {
+ DEBUGOUT("NVM Read Error\n");
+ return ret_val;
+ }
+ checksum += nvm_data;
+ }
+
+ if (checksum != (uint16_t) NVM_SUM) {
+ DEBUGOUT("NVM Checksum Invalid\n");
+ return -IGC_ERR_NVM;
+ }
+
+ return IGC_SUCCESS;
+}
diff --git a/sys/dev/pci/igc_nvm.h b/sys/dev/pci/igc_nvm.h
new file mode 100644
index 00000000000..39404cee085
--- /dev/null
+++ b/sys/dev/pci/igc_nvm.h
@@ -0,0 +1,29 @@
+/* $OpenBSD: igc_nvm.h,v 1.1 2021/10/31 14:52:57 patrick Exp $ */
+/*-
+ * Copyright 2021 Intel Corp
+ * Copyright 2021 Rubicon Communications, LLC (Netgate)
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _IGC_NVM_H_
+#define _IGC_NVM_H_
+
+void igc_init_nvm_ops_generic(struct igc_hw *);
+int igc_null_read_nvm(struct igc_hw *, uint16_t, uint16_t, uint16_t *);
+void igc_null_nvm_generic(struct igc_hw *);
+int igc_null_led_default(struct igc_hw *, uint16_t *);
+int igc_null_write_nvm(struct igc_hw *, uint16_t, uint16_t, uint16_t *);
+int igc_poll_eerd_eewr_done(struct igc_hw *, int);
+int igc_read_mac_addr_generic(struct igc_hw *);
+int igc_read_pba_string_generic(struct igc_hw *, uint8_t *, uint32_t);
+int igc_read_nvm_eerd(struct igc_hw *, uint16_t, uint16_t, uint16_t *);
+int igc_valid_led_default_generic(struct igc_hw *, uint16_t *);
+int igc_validate_nvm_checksum_generic(struct igc_hw *);
+int igc_write_nvm_spi(struct igc_hw *, uint16_t, uint16_t, uint16_t *);
+int igc_update_nvm_checksum_generic(struct igc_hw *);
+void igc_release_nvm_generic(struct igc_hw *);
+void igc_reload_nvm_generic(struct igc_hw *);
+
+#endif /* _IGC_NVM_H_ */
diff --git a/sys/dev/pci/igc_phy.c b/sys/dev/pci/igc_phy.c
new file mode 100644
index 00000000000..7b7d3209087
--- /dev/null
+++ b/sys/dev/pci/igc_phy.c
@@ -0,0 +1,908 @@
+/* $OpenBSD: igc_phy.c,v 1.1 2021/10/31 14:52:57 patrick Exp $ */
+/*-
+ * Copyright 2021 Intel Corp
+ * Copyright 2021 Rubicon Communications, LLC (Netgate)
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <dev/pci/igc_api.h>
+
+/**
+ * igc_init_phy_ops_generic - Initialize PHY function pointers
+ * @hw: pointer to the HW structure
+ *
+ * Setups up the function pointers to no-op functions
+ **/
+void
+igc_init_phy_ops_generic(struct igc_hw *hw)
+{
+ struct igc_phy_info *phy = &hw->phy;
+ DEBUGFUNC("igc_init_phy_ops_generic");
+
+ /* Initialize function pointers */
+ phy->ops.init_params = igc_null_ops_generic;
+ phy->ops.acquire = igc_null_ops_generic;
+ phy->ops.check_reset_block = igc_null_ops_generic;
+ phy->ops.commit = igc_null_ops_generic;
+ phy->ops.force_speed_duplex = igc_null_ops_generic;
+ phy->ops.get_info = igc_null_ops_generic;
+ phy->ops.set_page = igc_null_set_page;
+ phy->ops.read_reg = igc_null_read_reg;
+ phy->ops.read_reg_locked = igc_null_read_reg;
+ phy->ops.read_reg_page = igc_null_read_reg;
+ phy->ops.release = igc_null_phy_generic;
+ phy->ops.reset = igc_null_ops_generic;
+ phy->ops.set_d0_lplu_state = igc_null_lplu_state;
+ phy->ops.set_d3_lplu_state = igc_null_lplu_state;
+ phy->ops.write_reg = igc_null_write_reg;
+ phy->ops.write_reg_locked = igc_null_write_reg;
+ phy->ops.write_reg_page = igc_null_write_reg;
+ phy->ops.power_up = igc_null_phy_generic;
+ phy->ops.power_down = igc_null_phy_generic;
+}
+
+/**
+ * igc_null_set_page - No-op function, return 0
+ * @hw: pointer to the HW structure
+ * @data: dummy variable
+ **/
+int
+igc_null_set_page(struct igc_hw IGC_UNUSEDARG *hw, uint16_t IGC_UNUSEDARG data)
+{
+ DEBUGFUNC("igc_null_set_page");
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_null_read_reg - No-op function, return 0
+ * @hw: pointer to the HW structure
+ * @offset: dummy variable
+ * @data: dummy variable
+ **/
+int
+igc_null_read_reg(struct igc_hw IGC_UNUSEDARG *hw,
+ uint32_t IGC_UNUSEDARG offset, uint16_t IGC_UNUSEDARG *data)
+{
+ DEBUGFUNC("igc_null_read_reg");
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_null_phy_generic - No-op function, return void
+ * @hw: pointer to the HW structure
+ **/
+void
+igc_null_phy_generic(struct igc_hw IGC_UNUSEDARG *hw)
+{
+ DEBUGFUNC("igc_null_phy_generic");
+ return;
+}
+
+/**
+ * igc_null_lplu_state - No-op function, return 0
+ * @hw: pointer to the HW structure
+ * @active: dummy variable
+ **/
+int
+igc_null_lplu_state(struct igc_hw IGC_UNUSEDARG *hw, bool IGC_UNUSEDARG active)
+{
+ DEBUGFUNC("igc_null_lplu_state");
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_null_write_reg - No-op function, return 0
+ * @hw: pointer to the HW structure
+ * @offset: dummy variable
+ * @data: dummy variable
+ **/
+int
+igc_null_write_reg(struct igc_hw IGC_UNUSEDARG *hw,
+ uint32_t IGC_UNUSEDARG offset, uint16_t IGC_UNUSEDARG data)
+{
+ DEBUGFUNC("igc_null_write_reg");
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_check_reset_block_generic - Check if PHY reset is blocked
+ * @hw: pointer to the HW structure
+ *
+ * Read the PHY management control register and check whether a PHY reset
+ * is blocked. If a reset is not blocked return IGC_SUCCESS, otherwise
+ * return IGC_BLK_PHY_RESET (12).
+ **/
+int
+igc_check_reset_block_generic(struct igc_hw *hw)
+{
+ uint32_t manc;
+
+ DEBUGFUNC("igc_check_reset_block");
+
+ manc = IGC_READ_REG(hw, IGC_MANC);
+
+ return (manc & IGC_MANC_BLK_PHY_RST_ON_IDE) ?
+ IGC_BLK_PHY_RESET : IGC_SUCCESS;
+}
+
+/**
+ * igc_get_phy_id - Retrieve the PHY ID and revision
+ * @hw: pointer to the HW structure
+ *
+ * Reads the PHY registers and stores the PHY ID and possibly the PHY
+ * revision in the hardware structure.
+ **/
+int
+igc_get_phy_id(struct igc_hw *hw)
+{
+ struct igc_phy_info *phy = &hw->phy;
+ uint16_t phy_id;
+ int ret_val = IGC_SUCCESS;
+
+ DEBUGFUNC("igc_get_phy_id");
+
+ if (!phy->ops.read_reg)
+ return IGC_SUCCESS;
+
+ ret_val = phy->ops.read_reg(hw, PHY_ID1, &phy_id);
+ if (ret_val)
+ return ret_val;
+
+ phy->id = (uint32_t)(phy_id << 16);
+ DELAY(20);
+ ret_val = phy->ops.read_reg(hw, PHY_ID2, &phy_id);
+ if (ret_val)
+ return ret_val;
+
+ phy->id |= (uint32_t)(phy_id & PHY_REVISION_MASK);
+ phy->revision = (uint32_t)(phy_id & ~PHY_REVISION_MASK);
+
+
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_read_phy_reg_mdic - Read MDI control register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to be read
+ * @data: pointer to the read data
+ *
+ * Reads the MDI control register in the PHY at offset and stores the
+ * information read to data.
+ **/
+int
+igc_read_phy_reg_mdic(struct igc_hw *hw, uint32_t offset, uint16_t *data)
+{
+ struct igc_phy_info *phy = &hw->phy;
+ uint32_t i, mdic = 0;
+
+ DEBUGFUNC("igc_read_phy_reg_mdic");
+
+ if (offset > MAX_PHY_REG_ADDRESS) {
+ DEBUGOUT1("PHY Address %d is out of range\n", offset);
+ return -IGC_ERR_PARAM;
+ }
+
+ /* Set up Op-code, Phy Address, and register offset in the MDI
+ * Control register. The MAC will take care of interfacing with the
+ * PHY to retrieve the desired data.
+ */
+ mdic = ((offset << IGC_MDIC_REG_SHIFT) |
+ (phy->addr << IGC_MDIC_PHY_SHIFT) | (IGC_MDIC_OP_READ));
+
+ IGC_WRITE_REG(hw, IGC_MDIC, mdic);
+
+ /* Poll the ready bit to see if the MDI read completed
+ * Increasing the time out as testing showed failures with
+ * the lower time out
+ */
+ for (i = 0; i < (IGC_GEN_POLL_TIMEOUT * 3); i++) {
+ DELAY(50);
+ mdic = IGC_READ_REG(hw, IGC_MDIC);
+ if (mdic & IGC_MDIC_READY)
+ break;
+ }
+ if (!(mdic & IGC_MDIC_READY)) {
+ DEBUGOUT("MDI Read did not complete\n");
+ return -IGC_ERR_PHY;
+ }
+ if (mdic & IGC_MDIC_ERROR) {
+ DEBUGOUT("MDI Error\n");
+ return -IGC_ERR_PHY;
+ }
+ if (((mdic & IGC_MDIC_REG_MASK) >> IGC_MDIC_REG_SHIFT) != offset) {
+ DEBUGOUT2("MDI Read offset error - requested %d, returned %d\n",
+ offset, (mdic & IGC_MDIC_REG_MASK) >> IGC_MDIC_REG_SHIFT);
+ return -IGC_ERR_PHY;
+ }
+ *data = (uint16_t)mdic;
+
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_write_phy_reg_mdic - Write MDI control register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write to register at offset
+ *
+ * Writes data to MDI control register in the PHY at offset.
+ **/
+int
+igc_write_phy_reg_mdic(struct igc_hw *hw, uint32_t offset, uint16_t data)
+{
+ struct igc_phy_info *phy = &hw->phy;
+ uint32_t i, mdic = 0;
+
+ DEBUGFUNC("igc_write_phy_reg_mdic");
+
+ if (offset > MAX_PHY_REG_ADDRESS) {
+ DEBUGOUT1("PHY Address %d is out of range\n", offset);
+ return -IGC_ERR_PARAM;
+ }
+
+ /* Set up Op-code, Phy Address, and register offset in the MDI
+ * Control register. The MAC will take care of interfacing with the
+ * PHY to retrieve the desired data.
+ */
+ mdic = (((uint32_t)data) | (offset << IGC_MDIC_REG_SHIFT) |
+ (phy->addr << IGC_MDIC_PHY_SHIFT) | (IGC_MDIC_OP_WRITE));
+
+ IGC_WRITE_REG(hw, IGC_MDIC, mdic);
+
+ /* Poll the ready bit to see if the MDI read completed
+ * Increasing the time out as testing showed failures with
+ * the lower time out
+ */
+ for (i = 0; i < (IGC_GEN_POLL_TIMEOUT * 3); i++) {
+ DELAY(50);
+ mdic = IGC_READ_REG(hw, IGC_MDIC);
+ if (mdic & IGC_MDIC_READY)
+ break;
+ }
+ if (!(mdic & IGC_MDIC_READY)) {
+ DEBUGOUT("MDI Write did not complete\n");
+ return -IGC_ERR_PHY;
+ }
+ if (mdic & IGC_MDIC_ERROR) {
+ DEBUGOUT("MDI Error\n");
+ return -IGC_ERR_PHY;
+ }
+ if (((mdic & IGC_MDIC_REG_MASK) >> IGC_MDIC_REG_SHIFT) != offset)
+ return -IGC_ERR_PHY;
+
+ return IGC_SUCCESS;
+}
+
+/**
+ * igc_phy_setup_autoneg - Configure PHY for auto-negotiation
+ * @hw: pointer to the HW structure
+ *
+ * Reads the MII auto-neg advertisement register and/or the 1000T control
+ * register and if the PHY is already setup for auto-negotiation, then
+ * return successful. Otherwise, setup advertisement and flow control to
+ * the appropriate values for the wanted auto-negotiation.
+ **/
+int
+igc_phy_setup_autoneg(struct igc_hw *hw)
+{
+ struct igc_phy_info *phy = &hw->phy;
+ uint16_t mii_autoneg_adv_reg;
+ uint16_t mii_1000t_ctrl_reg = 0;
+ uint16_t aneg_multigbt_an_ctrl = 0;
+ int ret_val;
+
+ DEBUGFUNC("igc_phy_setup_autoneg");
+
+ phy->autoneg_advertised &= phy->autoneg_mask;
+
+ /* Read the MII Auto-Neg Advertisement Register (Address 4). */
+ ret_val = phy->ops.read_reg(hw, PHY_AUTONEG_ADV, &mii_autoneg_adv_reg);
+ if (ret_val)
+ return ret_val;
+
+ if (phy->autoneg_mask & ADVERTISE_1000_FULL) {
+ /* Read the MII 1000Base-T Control Register (Address 9). */
+ ret_val = phy->ops.read_reg(hw, PHY_1000T_CTRL,
+ &mii_1000t_ctrl_reg);
+ if (ret_val)
+ return ret_val;
+ }
+
+ if ((phy->autoneg_mask & ADVERTISE_2500_FULL) &&
+ hw->phy.id == I225_I_PHY_ID) {
+ /* Read the MULTI GBT AN Control Register - reg 7.32 */
+ ret_val = phy->ops.read_reg(hw, (STANDARD_AN_REG_MASK <<
+ MMD_DEVADDR_SHIFT) | ANEG_MULTIGBT_AN_CTRL,
+ &aneg_multigbt_an_ctrl);
+ if (ret_val)
+ return ret_val;
+ }
+
+ /* Need to parse both autoneg_advertised and fc and set up
+ * the appropriate PHY registers. First we will parse for
+ * autoneg_advertised software override. Since we can advertise
+ * a plethora of combinations, we need to check each bit
+ * individually.
+ */
+
+ /* First we clear all the 10/100 mb speed bits in the Auto-Neg
+ * Advertisement Register (Address 4) and the 1000 mb speed bits in
+ * the 1000Base-T Control Register (Address 9).
+ */
+ mii_autoneg_adv_reg &= ~(NWAY_AR_100TX_FD_CAPS | NWAY_AR_100TX_HD_CAPS |
+ NWAY_AR_10T_FD_CAPS | NWAY_AR_10T_HD_CAPS);
+ mii_1000t_ctrl_reg &= ~(CR_1000T_HD_CAPS | CR_1000T_FD_CAPS);
+
+ DEBUGOUT1("autoneg_advertised %x\n", phy->autoneg_advertised);
+
+ /* Do we want to advertise 10 Mb Half Duplex? */
+ if (phy->autoneg_advertised & ADVERTISE_10_HALF) {
+ DEBUGOUT("Advertise 10mb Half duplex\n");
+ mii_autoneg_adv_reg |= NWAY_AR_10T_HD_CAPS;
+ }
+
+ /* Do we want to advertise 10 Mb Full Duplex? */
+ if (phy->autoneg_advertised & ADVERTISE_10_FULL) {
+ DEBUGOUT("Advertise 10mb Full duplex\n");
+ mii_autoneg_adv_reg |= NWAY_AR_10T_FD_CAPS;
+ }
+
+ /* Do we want to advertise 100 Mb Half Duplex? */
+ if (phy->autoneg_advertised & ADVERTISE_100_HALF) {
+ DEBUGOUT("Advertise 100mb Half duplex\n");
+ mii_autoneg_adv_reg |= NWAY_AR_100TX_HD_CAPS;
+ }
+
+ /* Do we want to advertise 100 Mb Full Duplex? */
+ if (phy->autoneg_advertised & ADVERTISE_100_FULL) {
+ DEBUGOUT("Advertise 100mb Full duplex\n");
+ mii_autoneg_adv_reg |= NWAY_AR_100TX_FD_CAPS;
+ }
+
+ /* We do not allow the Phy to advertise 1000 Mb Half Duplex */
+ if (phy->autoneg_advertised & ADVERTISE_1000_HALF)
+ DEBUGOUT("Advertise 1000mb Half duplex request denied!\n");
+
+ /* Do we want to advertise 1000 Mb Full Duplex? */
+ if (phy->autoneg_advertised & ADVERTISE_1000_FULL) {
+ DEBUGOUT("Advertise 1000mb Full duplex\n");
+ mii_1000t_ctrl_reg |= CR_1000T_FD_CAPS;
+ }
+
+ /* We do not allow the Phy to advertise 2500 Mb Half Duplex */
+ if (phy->autoneg_advertised & ADVERTISE_2500_HALF)
+ DEBUGOUT("Advertise 2500mb Half duplex request denied!\n");
+
+ /* Do we want to advertise 2500 Mb Full Duplex? */
+ if (phy->autoneg_advertised & ADVERTISE_2500_FULL) {
+ DEBUGOUT("Advertise 2500mb Full duplex\n");
+ aneg_multigbt_an_ctrl |= CR_2500T_FD_CAPS;
+ } else
+ aneg_multigbt_an_ctrl &= ~CR_2500T_FD_CAPS;
+
+ /* Check for a software override of the flow control settings, and
+ * setup the PHY advertisement registers accordingly. If
+ * auto-negotiation is enabled, then software will have to set the
+ * "PAUSE" bits to the correct value in the Auto-Negotiation
+ * Advertisement Register (PHY_AUTONEG_ADV) and re-start auto-
+ * negotiation.
+ *
+ * The possible values of the "fc" parameter are:
+ * 0: Flow control is completely disabled
+ * 1: Rx flow control is enabled (we can receive pause frames
+ * but not send pause frames).
+ * 2: Tx flow control is enabled (we can send pause frames
+ * but we do not support receiving pause frames).
+ * 3: Both Rx and Tx flow control (symmetric) are enabled.
+ * other: No software override. The flow control configuration
+ * in the EEPROM is used.
+ */
+ switch (hw->fc.current_mode) {
+ case igc_fc_none:
+ /* Flow control (Rx & Tx) is completely disabled by a
+ * software over-ride.
+ */
+ mii_autoneg_adv_reg &= ~(NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
+ break;
+ case igc_fc_rx_pause:
+ /* Rx Flow control is enabled, and Tx Flow control is
+ * disabled, by a software over-ride.
+ *
+ * Since there really isn't a way to advertise that we are
+ * capable of Rx Pause ONLY, we will advertise that we
+ * support both symmetric and asymmetric Rx PAUSE. Later
+ * (in igc_config_fc_after_link_up) we will disable the
+ * hw's ability to send PAUSE frames.
+ */
+ mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
+ break;
+ case igc_fc_tx_pause:
+ /* Tx Flow control is enabled, and Rx Flow control is
+ * disabled, by a software over-ride.
+ */
+ mii_autoneg_adv_reg |= NWAY_AR_ASM_DIR;
+ mii_autoneg_adv_reg &= ~NWAY_AR_PAUSE;
+ break;
+ case igc_fc_full:
+ /* Flow control (both Rx and Tx) is enabled by a software
+ * over-ride.
+ */
+ mii_autoneg_adv_reg |= (NWAY_AR_ASM_DIR | NWAY_AR_PAUSE);
+ break;
+ default:
+ DEBUGOUT("Flow control param set incorrectly\n");
+ return -IGC_ERR_CONFIG;
+ }
+
+ ret_val = phy->ops.write_reg(hw, PHY_AUTONEG_ADV, mii_autoneg_adv_reg);
+ if (ret_val)
+ return ret_val;
+
+ DEBUGOUT1("Auto-Neg Advertising %x\n", mii_autoneg_adv_reg);
+
+ if (phy->autoneg_mask & ADVERTISE_1000_FULL)
+ ret_val = phy->ops.write_reg(hw, PHY_1000T_CTRL,
+ mii_1000t_ctrl_reg);
+
+ if ((phy->autoneg_mask & ADVERTISE_2500_FULL) &&
+ hw->phy.id == I225_I_PHY_ID)
+ ret_val = phy->ops.write_reg(hw,
+ (STANDARD_AN_REG_MASK << MMD_DEVADDR_SHIFT) |
+ ANEG_MULTIGBT_AN_CTRL, aneg_multigbt_an_ctrl);
+
+ return ret_val;
+}
+
+/**
+ * igc_copper_link_autoneg - Setup/Enable autoneg for copper link
+ * @hw: pointer to the HW structure
+ *
+ * Performs initial bounds checking on autoneg advertisement parameter, then
+ * configure to advertise the full capability. Setup the PHY to autoneg
+ * and restart the negotiation process between the link partner. If
+ * autoneg_wait_to_complete, then wait for autoneg to complete before exiting.
+ **/
+int
+igc_copper_link_autoneg(struct igc_hw *hw)
+{
+ struct igc_phy_info *phy = &hw->phy;
+ uint16_t phy_ctrl;
+ int ret_val;
+
+ DEBUGFUNC("igc_copper_link_autoneg");
+
+ /* Perform some bounds checking on the autoneg advertisement
+ * parameter.
+ */
+ phy->autoneg_advertised &= phy->autoneg_mask;
+
+ /* If autoneg_advertised is zero, we assume it was not defaulted
+ * by the calling code so we set to advertise full capability.
+ */
+ if (!phy->autoneg_advertised)
+ phy->autoneg_advertised = phy->autoneg_mask;
+
+ DEBUGOUT("Reconfiguring auto-neg advertisement params\n");
+ ret_val = igc_phy_setup_autoneg(hw);
+ if (ret_val) {
+ DEBUGOUT("Error Setting up Auto-Negotiation\n");
+ return ret_val;
+ }
+ DEBUGOUT("Restarting Auto-Neg\n");
+
+ /* Restart auto-negotiation by setting the Auto Neg Enable bit and
+ * the Auto Neg Restart bit in the PHY control register.
+ */
+ ret_val = phy->ops.read_reg(hw, PHY_CONTROL, &phy_ctrl);
+ if (ret_val)
+ return ret_val;
+
+ phy_ctrl |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG);
+ ret_val = phy->ops.write_reg(hw, PHY_CONTROL, phy_ctrl);
+ if (ret_val)
+ return ret_val;
+
+ /* Does the user want to wait for Auto-Neg to complete here, or
+ * check at a later time (for example, callback routine).
+ */
+ if (phy->autoneg_wait_to_complete) {
+ ret_val = igc_wait_autoneg(hw);
+ if (ret_val)
+ return ret_val;
+ }
+
+ hw->mac.get_link_status = true;
+
+ return ret_val;
+}
+
+/**
+ * igc_setup_copper_link_generic - Configure copper link settings
+ * @hw: pointer to the HW structure
+ *
+ * Calls the appropriate function to configure the link for auto-neg or forced
+ * speed and duplex. Then we check for link, once link is established calls
+ * to configure collision distance and flow control are called. If link is
+ * not established, we return -IGC_ERR_PHY (-2).
+ **/
+int
+igc_setup_copper_link_generic(struct igc_hw *hw)
+{
+ int ret_val;
+ bool link;
+
+ DEBUGFUNC("igc_setup_copper_link_generic");
+
+ if (hw->mac.autoneg) {
+ /* Setup autoneg and flow control advertisement and perform
+ * autonegotiation.
+ */
+ ret_val = igc_copper_link_autoneg(hw);
+ if (ret_val)
+ return ret_val;
+ } else {
+ /* PHY will be set to 10H, 10F, 100H or 100F
+ * depending on user settings.
+ */
+ DEBUGOUT("Forcing Speed and Duplex\n");
+ ret_val = hw->phy.ops.force_speed_duplex(hw);
+ if (ret_val) {
+ DEBUGOUT("Error Forcing Speed and Duplex\n");
+ return ret_val;
+ }
+ }
+
+ /* Check link status. Wait up to 100 microseconds for link to become
+ * valid.
+ */
+ ret_val = igc_phy_has_link_generic(hw, COPPER_LINK_UP_LIMIT, 10,
+ &link);
+ if (ret_val)
+ return ret_val;
+
+ if (link) {
+ DEBUGOUT("Valid link established!!!\n");
+ hw->mac.ops.config_collision_dist(hw);
+ ret_val = igc_config_fc_after_link_up_generic(hw);
+ } else
+ DEBUGOUT("Unable to establish link!!!\n");
+
+ return ret_val;
+}
+
+/**
+ * igc_check_downshift_generic - Checks whether a downshift in speed occurred
+ * @hw: pointer to the HW structure
+ *
+ * Success returns 0, Failure returns 1
+ *
+ * A downshift is detected by querying the PHY link health.
+ **/
+int
+igc_check_downshift_generic(struct igc_hw *hw)
+{
+ struct igc_phy_info *phy = &hw->phy;
+ int ret_val;
+
+ DEBUGFUNC("igc_check_downshift_generic");
+
+ switch (phy->type) {
+ case igc_phy_i225:
+ default:
+ /* speed downshift not supported */
+ phy->speed_downgraded = false;
+ return IGC_SUCCESS;
+ }
+
+ return ret_val;
+}
+
+/**
+ * igc_wait_autoneg - Wait for auto-neg completion
+ * @hw: pointer to the HW structure
+ *
+ * Waits for auto-negotiation to complete or for the auto-negotiation time
+ * limit to expire, which ever happens first.
+ **/
+int
+igc_wait_autoneg(struct igc_hw *hw)
+{
+ uint16_t i, phy_status;
+ int ret_val = IGC_SUCCESS;
+
+ DEBUGFUNC("igc_wait_autoneg");
+
+ if (!hw->phy.ops.read_reg)
+ return IGC_SUCCESS;
+
+ /* Break after autoneg completes or PHY_AUTO_NEG_LIMIT expires. */
+ for (i = PHY_AUTO_NEG_LIMIT; i > 0; i--) {
+ ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status);
+ if (ret_val)
+ break;
+ ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status);
+ if (ret_val)
+ break;
+ if (phy_status & MII_SR_AUTONEG_COMPLETE)
+ break;
+ msec_delay(100);
+ }
+
+ /* PHY_AUTO_NEG_TIME expiration doesn't guarantee auto-negotiation
+ * has completed.
+ */
+ return ret_val;
+}
+
+/**
+ * igc_phy_has_link_generic - Polls PHY for link
+ * @hw: pointer to the HW structure
+ * @iterations: number of times to poll for link
+ * @usec_interval: delay between polling attempts
+ * @success: pointer to whether polling was successful or not
+ *
+ * Polls the PHY status register for link, 'iterations' number of times.
+ **/
+int
+igc_phy_has_link_generic(struct igc_hw *hw, uint32_t iterations,
+ uint32_t usec_interval, bool *success)
+{
+ uint16_t i, phy_status;
+ int ret_val = IGC_SUCCESS;
+
+ DEBUGFUNC("igc_phy_has_link_generic");
+
+ if (!hw->phy.ops.read_reg)
+ return IGC_SUCCESS;
+
+ for (i = 0; i < iterations; i++) {
+ /* Some PHYs require the PHY_STATUS register to be read
+ * twice due to the link bit being sticky. No harm doing
+ * it across the board.
+ */
+ ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status);
+ if (ret_val) {
+ /* If the first read fails, another entity may have
+ * ownership of the resources, wait and try again to
+ * see if they have relinquished the resources yet.
+ */
+ if (usec_interval >= 1000)
+ msec_delay(usec_interval/1000);
+ else
+ DELAY(usec_interval);
+ }
+ ret_val = hw->phy.ops.read_reg(hw, PHY_STATUS, &phy_status);
+ if (ret_val)
+ break;
+ if (phy_status & MII_SR_LINK_STATUS)
+ break;
+ if (usec_interval >= 1000)
+ msec_delay(usec_interval/1000);
+ else
+ DELAY(usec_interval);
+ }
+
+ *success = (i < iterations);
+
+ return ret_val;
+}
+
+/**
+ * igc_phy_sw_reset_generic - PHY software reset
+ * @hw: pointer to the HW structure
+ *
+ * Does a software reset of the PHY by reading the PHY control register and
+ * setting/write the control register reset bit to the PHY.
+ **/
+int
+igc_phy_sw_reset_generic(struct igc_hw *hw)
+{
+ uint16_t phy_ctrl;
+ int ret_val;
+
+ DEBUGFUNC("igc_phy_sw_reset_generic");
+
+ if (!hw->phy.ops.read_reg)
+ return IGC_SUCCESS;
+
+ ret_val = hw->phy.ops.read_reg(hw, PHY_CONTROL, &phy_ctrl);
+ if (ret_val)
+ return ret_val;
+
+ phy_ctrl |= MII_CR_RESET;
+ ret_val = hw->phy.ops.write_reg(hw, PHY_CONTROL, phy_ctrl);
+ if (ret_val)
+ return ret_val;
+
+ DELAY(1);
+
+ return ret_val;
+}
+
+/**
+ * igc_power_up_phy_copper - Restore copper link in case of PHY power down
+ * @hw: pointer to the HW structure
+ *
+ * In the case of a PHY power down to save power, or to turn off link during a
+ * driver unload, or wake on lan is not enabled, restore the link to previous
+ * settings.
+ **/
+void
+igc_power_up_phy_copper(struct igc_hw *hw)
+{
+ uint16_t mii_reg = 0;
+
+ /* The PHY will retain its settings across a power down/up cycle */
+ hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg);
+ mii_reg &= ~MII_CR_POWER_DOWN;
+ hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg);
+ DELAY(300);
+}
+
+/**
+ * igc_power_down_phy_copper - Restore copper link in case of PHY power down
+ * @hw: pointer to the HW structure
+ *
+ * In the case of a PHY power down to save power, or to turn off link during a
+ * driver unload, or wake on lan is not enabled, restore the link to previous
+ * settings.
+ **/
+void
+igc_power_down_phy_copper(struct igc_hw *hw)
+{
+ uint16_t mii_reg = 0;
+
+ /* The PHY will retain its settings across a power down/up cycle */
+ hw->phy.ops.read_reg(hw, PHY_CONTROL, &mii_reg);
+ mii_reg |= MII_CR_POWER_DOWN;
+ hw->phy.ops.write_reg(hw, PHY_CONTROL, mii_reg);
+ msec_delay(1);
+}
+
+/**
+ * igc_write_phy_reg_gpy - Write GPY PHY register
+ * @hw: pointer to the HW structure
+ * @offset: register offset to write to
+ * @data: data to write at register offset
+ *
+ * Acquires semaphore, if necessary, then writes the data to PHY register
+ * at the offset. Release any acquired semaphores before exiting.
+ **/
+int
+igc_write_phy_reg_gpy(struct igc_hw *hw, uint32_t offset, uint16_t data)
+{
+ uint8_t dev_addr = (offset & GPY_MMD_MASK) >> GPY_MMD_SHIFT;
+ int ret_val;
+
+ DEBUGFUNC("igc_write_phy_reg_gpy");
+
+ offset = offset & GPY_REG_MASK;
+
+ if (!dev_addr) {
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ return ret_val;
+ ret_val = igc_write_phy_reg_mdic(hw, offset, data);
+ if (ret_val)
+ return ret_val;
+ hw->phy.ops.release(hw);
+ } else {
+ ret_val = igc_write_xmdio_reg(hw, (uint16_t)offset, dev_addr,
+ data);
+ }
+
+ return ret_val;
+}
+
+/**
+ * igc_read_phy_reg_gpy - Read GPY PHY register
+ * @hw: pointer to the HW structure
+ * @offset: lower half is register offset to read to
+ * upper half is MMD to use.
+ * @data: data to read at register offset
+ *
+ * Acquires semaphore, if necessary, then reads the data in the PHY register
+ * at the offset. Release any acquired semaphores before exiting.
+ **/
+int
+igc_read_phy_reg_gpy(struct igc_hw *hw, uint32_t offset, uint16_t *data)
+{
+ uint8_t dev_addr = (offset & GPY_MMD_MASK) >> GPY_MMD_SHIFT;
+ int ret_val;
+
+ DEBUGFUNC("igc_read_phy_reg_gpy");
+
+ offset = offset & GPY_REG_MASK;
+
+ if (!dev_addr) {
+ ret_val = hw->phy.ops.acquire(hw);
+ if (ret_val)
+ return ret_val;
+ ret_val = igc_read_phy_reg_mdic(hw, offset, data);
+ if (ret_val)
+ return ret_val;
+ hw->phy.ops.release(hw);
+ } else {
+ ret_val = igc_read_xmdio_reg(hw, (uint16_t)offset, dev_addr,
+ data);
+ }
+
+ return ret_val;
+}
+
+/**
+ * __igc_access_xmdio_reg - Read/write XMDIO register
+ * @hw: pointer to the HW structure
+ * @address: XMDIO address to program
+ * @dev_addr: device address to program
+ * @data: pointer to value to read/write from/to the XMDIO address
+ * @read: boolean flag to indicate read or write
+ **/
+int
+__igc_access_xmdio_reg(struct igc_hw *hw, uint16_t address, uint8_t dev_addr,
+ uint16_t *data, bool read)
+{
+ int ret_val;
+
+ DEBUGFUNC("__igc_access_xmdio_reg");
+
+ ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAC, dev_addr);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAAD, address);
+ if (ret_val)
+ return ret_val;
+
+ ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAC, IGC_MMDAC_FUNC_DATA |
+ dev_addr);
+ if (ret_val)
+ return ret_val;
+
+ if (read)
+ ret_val = hw->phy.ops.read_reg(hw, IGC_MMDAAD, data);
+ else
+ ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAAD, *data);
+ if (ret_val)
+ return ret_val;
+
+ /* Recalibrate the device back to 0 */
+ ret_val = hw->phy.ops.write_reg(hw, IGC_MMDAC, 0);
+ if (ret_val)
+ return ret_val;
+
+ return ret_val;
+}
+
+/**
+ * igc_read_xmdio_reg - Read XMDIO register
+ * @hw: pointer to the HW structure
+ * @addr: XMDIO address to program
+ * @dev_addr: device address to program
+ * @data: value to be read from the EMI address
+ **/
+int
+igc_read_xmdio_reg(struct igc_hw *hw, uint16_t addr, uint8_t dev_addr,
+ uint16_t *data)
+{
+ DEBUGFUNC("igc_read_xmdio_reg");
+
+ return __igc_access_xmdio_reg(hw, addr, dev_addr, data, true);
+}
+
+/**
+ * igc_write_xmdio_reg - Write XMDIO register
+ * @hw: pointer to the HW structure
+ * @addr: XMDIO address to program
+ * @dev_addr: device address to program
+ * @data: value to be written to the XMDIO address
+ **/
+int
+igc_write_xmdio_reg(struct igc_hw *hw, uint16_t addr, uint8_t dev_addr,
+ uint16_t data)
+{
+ DEBUGFUNC("igc_write_xmdio_reg");
+
+ return __igc_access_xmdio_reg(hw, addr, dev_addr, &data, false);
+}
diff --git a/sys/dev/pci/igc_phy.h b/sys/dev/pci/igc_phy.h
new file mode 100644
index 00000000000..ded2589d690
--- /dev/null
+++ b/sys/dev/pci/igc_phy.h
@@ -0,0 +1,128 @@
+/* $OpenBSD: igc_phy.h,v 1.1 2021/10/31 14:52:57 patrick Exp $ */
+/*-
+ * Copyright 2021 Intel Corp
+ * Copyright 2021 Rubicon Communications, LLC (Netgate)
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _IGC_PHY_H_
+#define _IGC_PHY_H_
+
+void igc_init_phy_ops_generic(struct igc_hw *);
+int igc_null_read_reg(struct igc_hw *, uint32_t, uint16_t *);
+void igc_null_phy_generic(struct igc_hw *);
+int igc_null_lplu_state(struct igc_hw *, bool);
+int igc_null_write_reg(struct igc_hw *, uint32_t, uint16_t);
+int igc_null_set_page(struct igc_hw *, uint16_t);
+int igc_check_downshift_generic(struct igc_hw *);
+int igc_check_reset_block_generic(struct igc_hw *);
+int igc_get_phy_id(struct igc_hw *);
+int igc_phy_sw_reset_generic(struct igc_hw *);
+int igc_phy_hw_reset_generic(struct igc_hw *);
+int igc_phy_reset_dsp_generic(struct igc_hw *);
+int igc_set_d3_lplu_state_generic(struct igc_hw *, bool);
+int igc_setup_copper_link_generic(struct igc_hw *);
+int igc_phy_has_link_generic(struct igc_hw *, uint32_t, uint32_t, bool *);
+int igc_determine_phy_address(struct igc_hw *);
+int igc_enable_phy_wakeup_reg_access_bm(struct igc_hw *, uint16_t *);
+int igc_disable_phy_wakeup_reg_access_bm(struct igc_hw *, uint16_t *);
+void igc_power_up_phy_copper(struct igc_hw *);
+void igc_power_down_phy_copper(struct igc_hw *);
+int igc_read_phy_reg_mdic(struct igc_hw *, uint32_t offset, uint16_t *);
+int igc_write_phy_reg_mdic(struct igc_hw *, uint32_t offset, uint16_t);
+int igc_read_xmdio_reg(struct igc_hw *, uint16_t, uint8_t, uint16_t *);
+int igc_write_xmdio_reg(struct igc_hw *, uint16_t, uint8_t, uint16_t);
+int igc_write_phy_reg_gpy(struct igc_hw *, uint32_t, uint16_t);
+int igc_read_phy_reg_gpy(struct igc_hw *, uint32_t, uint16_t *);
+int igc_wait_autoneg(struct igc_hw *);
+
+/* IGP01IGC Specific Registers */
+#define IGP01IGC_PHY_PORT_CONFIG 0x10 /* Port Config */
+#define IGP01IGC_PHY_PORT_STATUS 0x11 /* Status */
+#define IGP01IGC_PHY_PORT_CTRL 0x12 /* Control */
+#define IGP01IGC_PHY_LINK_HEALTH 0x13 /* PHY Link Health */
+#define IGP02IGC_PHY_POWER_MGMT 0x19 /* Power Management */
+#define IGP01IGC_PHY_PAGE_SELECT 0x1F /* Page Select */
+#define BM_PHY_PAGE_SELECT 22 /* Page Select for BM */
+#define IGP_PAGE_SHIFT 5
+#define PHY_REG_MASK 0x1F
+#define IGC_I225_PHPM 0x0E14 /* I225 PHY Power Management */
+#define IGC_I225_PHPM_DIS_1000_D3 0x0008 /* Disable 1G in D3 */
+#define IGC_I225_PHPM_LINK_ENERGY 0x0010 /* Link Energy Detect */
+#define IGC_I225_PHPM_GO_LINKD 0x0020 /* Go Link Disconnect */
+#define IGC_I225_PHPM_DIS_1000 0x0040 /* Disable 1G globally */
+#define IGC_I225_PHPM_SPD_B2B_EN 0x0080 /* Smart Power Down Back2Back */
+#define IGC_I225_PHPM_RST_COMPL 0x0100 /* PHY Reset Completed */
+#define IGC_I225_PHPM_DIS_100_D3 0x0200 /* Disable 100M in D3 */
+#define IGC_I225_PHPM_ULP 0x0400 /* Ultra Low-Power Mode */
+#define IGC_I225_PHPM_DIS_2500 0x0800 /* Disable 2.5G globally */
+#define IGC_I225_PHPM_DIS_2500_D3 0x1000 /* Disable 2.5G in D3 */
+/* GPY211 - I225 defines */
+#define GPY_MMD_MASK 0xFFFF0000
+#define GPY_MMD_SHIFT 16
+#define GPY_REG_MASK 0x0000FFFF
+#define IGP01IGC_PHY_PCS_INIT_REG 0x00B4
+#define IGP01IGC_PHY_POLARITY_MASK 0x0078
+
+#define IGP01IGC_PSCR_AUTO_MDIX 0x1000
+#define IGP01IGC_PSCR_FORCE_MDI_MDIX 0x2000 /* 0=MDI, 1=MDIX */
+
+#define IGP01IGC_PSCFR_SMART_SPEED 0x0080
+
+#define IGP02IGC_PM_SPD 0x0001 /* Smart Power Down */
+#define IGP02IGC_PM_D0_LPLU 0x0002 /* For D0a states */
+#define IGP02IGC_PM_D3_LPLU 0x0004 /* For all other states */
+
+#define IGP01IGC_PLHR_SS_DOWNGRADE 0x8000
+
+#define IGP01IGC_PSSR_POLARITY_REVERSED 0x0002
+#define IGP01IGC_PSSR_MDIX 0x0800
+#define IGP01IGC_PSSR_SPEED_MASK 0xC000
+#define IGP01IGC_PSSR_SPEED_1000MBPS 0xC000
+
+#define IGP02IGC_PHY_CHANNEL_NUM 4
+#define IGP02IGC_PHY_AGC_A 0x11B1
+#define IGP02IGC_PHY_AGC_B 0x12B1
+#define IGP02IGC_PHY_AGC_C 0x14B1
+#define IGP02IGC_PHY_AGC_D 0x18B1
+
+#define IGP02IGC_AGC_LENGTH_SHIFT 9 /* Course=15:13, Fine=12:9 */
+#define IGP02IGC_AGC_LENGTH_MASK 0x7F
+#define IGP02IGC_AGC_RANGE 15
+
+#define IGC_CABLE_LENGTH_UNDEFINED 0xFF
+
+#define IGC_KMRNCTRLSTA_OFFSET 0x001F0000
+#define IGC_KMRNCTRLSTA_OFFSET_SHIFT 16
+#define IGC_KMRNCTRLSTA_REN 0x00200000
+#define IGC_KMRNCTRLSTA_DIAG_OFFSET 0x3 /* Kumeran Diagnostic */
+#define IGC_KMRNCTRLSTA_TIMEOUTS 0x4 /* Kumeran Timeouts */
+#define IGC_KMRNCTRLSTA_INBAND_PARAM 0x9 /* Kumeran InBand Parameters */
+#define IGC_KMRNCTRLSTA_IBIST_DISABLE 0x0200 /* Kumeran IBIST Disable */
+#define IGC_KMRNCTRLSTA_DIAG_NELPBK 0x1000 /* Nearend Loopback mode */
+
+#define IFE_PHY_EXTENDED_STATUS_CONTROL 0x10
+#define IFE_PHY_SPECIAL_CONTROL 0x11 /* 100BaseTx PHY Special Ctrl */
+#define IFE_PHY_SPECIAL_CONTROL_LED 0x1B /* PHY Special and LED Ctrl */
+#define IFE_PHY_MDIX_CONTROL 0x1C /* MDI/MDI-X Control */
+
+/* IFE PHY Extended Status Control */
+#define IFE_PESC_POLARITY_REVERSED 0x0100
+
+/* IFE PHY Special Control */
+#define IFE_PSC_AUTO_POLARITY_DISABLE 0x0010
+#define IFE_PSC_FORCE_POLARITY 0x0020
+
+/* IFE PHY Special Control and LED Control */
+#define IFE_PSCL_PROBE_MODE 0x0020
+#define IFE_PSCL_PROBE_LEDS_OFF 0x0006 /* Force LEDs 0 and 2 off */
+#define IFE_PSCL_PROBE_LEDS_ON 0x0007 /* Force LEDs 0 and 2 on */
+
+/* IFE PHY MDIX Control */
+#define IFE_PMC_MDIX_STATUS 0x0020 /* 1=MDI-X, 0=MDI */
+#define IFE_PMC_FORCE_MDIX 0x0040 /* 1=force MDI-X, 0=force MDI */
+#define IFE_PMC_AUTO_MDIX 0x0080 /* 1=enable auto, 0=disable */
+
+#endif /* _IGC_PHY_H_ */
diff --git a/sys/dev/pci/igc_regs.h b/sys/dev/pci/igc_regs.h
new file mode 100644
index 00000000000..b3d4b8e7aee
--- /dev/null
+++ b/sys/dev/pci/igc_regs.h
@@ -0,0 +1,433 @@
+/* $OpenBSD: igc_regs.h,v 1.1 2021/10/31 14:52:57 patrick Exp $ */
+/*-
+ * Copyright 2021 Intel Corp
+ * Copyright 2021 Rubicon Communications, LLC (Netgate)
+ * SPDX-License-Identifier: BSD-3-Clause
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _IGC_REGS_H_
+#define _IGC_REGS_H_
+
+/* General Register Descriptions */
+#define IGC_CTRL 0x00000 /* Device Control - RW */
+#define IGC_STATUS 0x00008 /* Device Status - RO */
+#define IGC_EECD 0x00010 /* EEPROM/Flash Control - RW */
+/* NVM Register Descriptions */
+#define IGC_EERD 0x12014 /* EEprom mode read - RW */
+#define IGC_EEWR 0x12018 /* EEprom mode write - RW */
+#define IGC_CTRL_EXT 0x00018 /* Extended Device Control - RW */
+#define IGC_MDIC 0x00020 /* MDI Control - RW */
+#define IGC_MDICNFG 0x00E04 /* MDI Config - RW */
+#define IGC_FCAL 0x00028 /* Flow Control Address Low - RW */
+#define IGC_FCAH 0x0002C /* Flow Control Address High -RW */
+#define IGC_I225_FLSWCTL 0x12048 /* FLASH control register */
+#define IGC_I225_FLSWDATA 0x1204C /* FLASH data register */
+#define IGC_I225_FLSWCNT 0x12050 /* FLASH Access Counter */
+#define IGC_I225_FLSECU 0x12114 /* FLASH Security */
+#define IGC_FCT 0x00030 /* Flow Control Type - RW */
+#define IGC_CONNSW 0x00034 /* Copper/Fiber switch control - RW */
+#define IGC_VET 0x00038 /* VLAN Ether Type - RW */
+#define IGC_ICR 0x01500 /* Intr Cause Read - RC/W1C */
+#define IGC_ITR 0x000C4 /* Interrupt Throttling Rate - RW */
+#define IGC_ICS 0x01504 /* Intr Cause Set - WO */
+#define IGC_IMS 0x01508 /* Intr Mask Set/Read - RW */
+#define IGC_IMC 0x0150C /* Intr Mask Clear - WO */
+#define IGC_IAM 0x01510 /* Intr Ack Auto Mask- RW */
+#define IGC_RCTL 0x00100 /* Rx Control - RW */
+#define IGC_FCTTV 0x00170 /* Flow Control Transmit Timer Value */
+#define IGC_TXCW 0x00178 /* Tx Configuration Word - RW */
+#define IGC_RXCW 0x00180 /* Rx Configuration Word - RO */
+#define IGC_EICR 0x01580 /* Ext. Interrupt Cause Read - R/clr */
+#define IGC_EITR(_n) (0x01680 + (0x4 * (_n)))
+#define IGC_EICS 0x01520 /* Ext. Interrupt Cause Set - W0 */
+#define IGC_EIMS 0x01524 /* Ext. Interrupt Mask Set/Read - RW */
+#define IGC_EIMC 0x01528 /* Ext. Interrupt Mask Clear - WO */
+#define IGC_EIAC 0x0152C /* Ext. Interrupt Auto Clear - RW */
+#define IGC_EIAM 0x01530 /* Ext. Interrupt Ack Auto Clear
+ * Mask */
+#define IGC_GPIE 0x01514 /* General Purpose Interrupt Enable
+ * - RW */
+#define IGC_IVAR0 0x01700 /* Interrupt Vector Allocation (array)
+ * - RW */
+#define IGC_IVAR_MISC 0x01740 /* IVAR for "other" causes - RW */
+#define IGC_TCTL 0x00400 /* Tx Control - RW */
+#define IGC_TCTL_EXT 0x00404 /* Extended Tx Control - RW */
+#define IGC_TIPG 0x00410 /* Tx Inter-packet gap -RW */
+#define IGC_AIT 0x00458 /* Adaptive Interframe Spacing
+ * Throttle - RW */
+#define IGC_LEDCTL 0x00E00 /* LED Control - RW */
+#define IGC_LEDMUX 0x08130 /* LED MUX Control */
+#define IGC_EXTCNF_CTRL 0x00F00 /* Extended Configuration Control */
+#define IGC_EXTCNF_SIZE 0x00F08 /* Extended Configuration Size */
+#define IGC_PHY_CTRL 0x00F10 /* PHY Control Register in CSR */
+#define IGC_PBA 0x01000 /* Packet Buffer Allocation - RW */
+#define IGC_PBS 0x01008 /* Packet Buffer Size */
+#define IGC_EEMNGCTL 0x01010 /* MNG EEprom Control */
+#define IGC_EEMNGCTL_I225 0x01010 /* i225 MNG EEprom Mode Control */
+#define IGC_EEARBC_I225 0x12024 /* EEPROM Auto Read Bus Control */
+#define IGC_FLOP 0x0103C /* FLASH Opcode Register */
+#define IGC_WDSTP 0x01040 /* Watchdog Setup - RW */
+#define IGC_SWDSTS 0x01044 /* SW Device Status - RW */
+#define IGC_FRTIMER 0x01048 /* Free Running Timer - RW */
+#define IGC_TCPTIMER 0x0104C /* TCP Timer - RW */
+#define IGC_ERT 0x02008 /* Early Rx Threshold - RW */
+#define IGC_FCRTL 0x02160 /* Flow Control Receive Threshold Low
+ * - RW */
+#define IGC_FCRTH 0x02168 /* Flow Control Receive Threshold High
+ * - RW */
+#define IGC_PSRCTL 0x02170 /* Packet Split Receive Control - RW */
+#define IGC_RDFH 0x02410 /* Rx Data FIFO Head - RW */
+#define IGC_RDFT 0x02418 /* Rx Data FIFO Tail - RW */
+#define IGC_RDFHS 0x02420 /* Rx Data FIFO Head Saved - RW */
+#define IGC_RDFTS 0x02428 /* Rx Data FIFO Tail Saved - RW */
+#define IGC_RDFPC 0x02430 /* Rx Data FIFO Packet Count - RW */
+#define IGC_PBRTH 0x02458 /* PB Rx Arbitration Threshold - RW */
+#define IGC_FCRTV 0x02460 /* Flow Control Refresh Timer Value
+ * - RW */
+/* Split and Replication Rx Control - RW */
+#define IGC_RXPBS 0x02404 /* Rx Packet Buffer Size - RW */
+#define IGC_RDTR 0x02820 /* Rx Delay Timer - RW */
+#define IGC_RADV 0x0282C /* Rx Interrupt Absolute Delay Timer
+ * - RW */
+/* Shadow Ram Write Register - RW */
+#define IGC_SRWR 0x12018
+#define IGC_EEC_REG 0x12010
+
+
+#define IGC_SHADOWINF 0x12068
+#define IGC_FLFWUPDATE 0x12108
+
+#define IGC_INVM_DATA_REG(_n) (0x12120 + 4*(_n))
+#define IGC_INVM_SIZE 64 /* Number of INVM Data Registers */
+
+#define IGC_MMDAC 13 /* MMD Access Control */
+#define IGC_MMDAAD 14 /* MMD Access Address/Data */
+/* Convenience macros
+ *
+ * Note: "_n" is the queue number of the register to be written to.
+ *
+ * Example usage:
+ * IGC_RDBAL_REG(current_rx_queue)
+ */
+#define IGC_RDBAL(_n) ((_n) < 4 ? (0x02800 + ((_n) * 0x100)) : \
+ (0x0C000 + ((_n) * 0x40)))
+#define IGC_RDBAH(_n) ((_n) < 4 ? (0x02804 + ((_n) * 0x100)) : \
+ (0x0C004 + ((_n) * 0x40)))
+#define IGC_RDLEN(_n) ((_n) < 4 ? (0x02808 + ((_n) * 0x100)) : \
+ (0x0C008 + ((_n) * 0x40)))
+#define IGC_SRRCTL(_n) ((_n) < 4 ? (0x0280C + ((_n) * 0x100)) : \
+ (0x0C00C + ((_n) * 0x40)))
+#define IGC_RDH(_n) ((_n) < 4 ? (0x02810 + ((_n) * 0x100)) : \
+ (0x0C010 + ((_n) * 0x40)))
+#define IGC_RDT(_n) ((_n) < 4 ? (0x02818 + ((_n) * 0x100)) : \
+ (0x0C018 + ((_n) * 0x40)))
+#define IGC_RXDCTL(_n) ((_n) < 4 ? (0x02828 + ((_n) * 0x100)) : \
+ (0x0C028 + ((_n) * 0x40)))
+#define IGC_RQDPC(_n) ((_n) < 4 ? (0x02830 + ((_n) * 0x100)) : \
+ (0x0C030 + ((_n) * 0x40)))
+#define IGC_TDBAL(_n) ((_n) < 4 ? (0x03800 + ((_n) * 0x100)) : \
+ (0x0E000 + ((_n) * 0x40)))
+#define IGC_TDBAH(_n) ((_n) < 4 ? (0x03804 + ((_n) * 0x100)) : \
+ (0x0E004 + ((_n) * 0x40)))
+#define IGC_TDLEN(_n) ((_n) < 4 ? (0x03808 + ((_n) * 0x100)) : \
+ (0x0E008 + ((_n) * 0x40)))
+#define IGC_TDH(_n) ((_n) < 4 ? (0x03810 + ((_n) * 0x100)) : \
+ (0x0E010 + ((_n) * 0x40)))
+#define IGC_TDT(_n) ((_n) < 4 ? (0x03818 + ((_n) * 0x100)) : \
+ (0x0E018 + ((_n) * 0x40)))
+#define IGC_TXDCTL(_n) ((_n) < 4 ? (0x03828 + ((_n) * 0x100)) : \
+ (0x0E028 + ((_n) * 0x40)))
+#define IGC_TARC(_n) (0x03840 + ((_n) * 0x100))
+#define IGC_RSRPD 0x02C00 /* Rx Small Packet Detect - RW */
+#define IGC_RAID 0x02C08 /* Receive Ack Interrupt Delay - RW */
+#define IGC_KABGTXD 0x03004 /* AFE Band Gap Transmit Ref Data */
+#define IGC_PSRTYPE(_i) (0x05480 + ((_i) * 4))
+#define IGC_RAL(_i) (((_i) <= 15) ? (0x05400 + ((_i) * 8)) : \
+ (0x054E0 + ((_i - 16) * 8)))
+#define IGC_RAH(_i) (((_i) <= 15) ? (0x05404 + ((_i) * 8)) : \
+ (0x054E4 + ((_i - 16) * 8)))
+#define IGC_VLANPQF 0x055B0 /* VLAN Priority Queue Filter VLAPQF */
+
+#define IGC_SHRAL(_i) (0x05438 + ((_i) * 8))
+#define IGC_SHRAH(_i) (0x0543C + ((_i) * 8))
+#define IGC_IP4AT_REG(_i) (0x05840 + ((_i) * 8))
+#define IGC_IP6AT_REG(_i) (0x05880 + ((_i) * 4))
+#define IGC_WUPM_REG(_i) (0x05A00 + ((_i) * 4))
+#define IGC_FFMT_REG(_i) (0x09000 + ((_i) * 8))
+#define IGC_FFVT_REG(_i) (0x09800 + ((_i) * 8))
+#define IGC_FFLT_REG(_i) (0x05F00 + ((_i) * 8))
+#define IGC_TXPBS 0x03404 /* Tx Packet Buffer Size - RW */
+#define IGC_TIDV 0x03820 /* Tx Interrupt Delay Value - RW */
+#define IGC_TADV 0x0382C /* Tx Interrupt Absolute Delay Val - RW */
+/* Statistics Register Descriptions */
+#define IGC_CRCERRS 0x04000 /* CRC Error Count - R/clr */
+#define IGC_ALGNERRC 0x04004 /* Alignment Error Count - R/clr */
+#define IGC_MPC 0x04010 /* Missed Packet Count - R/clr */
+#define IGC_SCC 0x04014 /* Single Collision Count - R/clr */
+#define IGC_ECOL 0x04018 /* Excessive Collision Count - R/clr */
+#define IGC_MCC 0x0401C /* Multiple Collision Count - R/clr */
+#define IGC_LATECOL 0x04020 /* Late Collision Count - R/clr */
+#define IGC_COLC 0x04028 /* Collision Count - R/clr */
+#define IGC_RERC 0x0402C /* Receive Error Count - R/clr */
+#define IGC_DC 0x04030 /* Defer Count - R/clr */
+#define IGC_TNCRS 0x04034 /* Tx-No CRS - R/clr */
+#define IGC_HTDPMC 0x0403C /* Host Transmit Discarded by MAC - R/clr */
+#define IGC_RLEC 0x04040 /* Receive Length Error Count - R/clr */
+#define IGC_XONRXC 0x04048 /* XON Rx Count - R/clr */
+#define IGC_XONTXC 0x0404C /* XON Tx Count - R/clr */
+#define IGC_XOFFRXC 0x04050 /* XOFF Rx Count - R/clr */
+#define IGC_XOFFTXC 0x04054 /* XOFF Tx Count - R/clr */
+#define IGC_FCRUC 0x04058 /* Flow Control Rx Unsupported Count- R/clr */
+#define IGC_PRC64 0x0405C /* Packets Rx (64 bytes) - R/clr */
+#define IGC_PRC127 0x04060 /* Packets Rx (65-127 bytes) - R/clr */
+#define IGC_PRC255 0x04064 /* Packets Rx (128-255 bytes) - R/clr */
+#define IGC_PRC511 0x04068 /* Packets Rx (255-511 bytes) - R/clr */
+#define IGC_PRC1023 0x0406C /* Packets Rx (512-1023 bytes) - R/clr */
+#define IGC_PRC1522 0x04070 /* Packets Rx (1024-1522 bytes) - R/clr */
+#define IGC_GPRC 0x04074 /* Good Packets Rx Count - R/clr */
+#define IGC_BPRC 0x04078 /* Broadcast Packets Rx Count - R/clr */
+#define IGC_MPRC 0x0407C /* Multicast Packets Rx Count - R/clr */
+#define IGC_GPTC 0x04080 /* Good Packets Tx Count - R/clr */
+#define IGC_GORCL 0x04088 /* Good Octets Rx Count Low - R/clr */
+#define IGC_GORCH 0x0408C /* Good Octets Rx Count High - R/clr */
+#define IGC_GOTCL 0x04090 /* Good Octets Tx Count Low - R/clr */
+#define IGC_GOTCH 0x04094 /* Good Octets Tx Count High - R/clr */
+#define IGC_RNBC 0x040A0 /* Rx No Buffers Count - R/clr */
+#define IGC_RUC 0x040A4 /* Rx Undersize Count - R/clr */
+#define IGC_RFC 0x040A8 /* Rx Fragment Count - R/clr */
+#define IGC_ROC 0x040AC /* Rx Oversize Count - R/clr */
+#define IGC_RJC 0x040B0 /* Rx Jabber Count - R/clr */
+#define IGC_MGTPRC 0x040B4 /* Management Packets Rx Count - R/clr */
+#define IGC_MGTPDC 0x040B8 /* Management Packets Dropped Count - R/clr */
+#define IGC_MGTPTC 0x040BC /* Management Packets Tx Count - R/clr */
+#define IGC_TORL 0x040C0 /* Total Octets Rx Low - R/clr */
+#define IGC_TORH 0x040C4 /* Total Octets Rx High - R/clr */
+#define IGC_TOTL 0x040C8 /* Total Octets Tx Low - R/clr */
+#define IGC_TOTH 0x040CC /* Total Octets Tx High - R/clr */
+#define IGC_TPR 0x040D0 /* Total Packets Rx - R/clr */
+#define IGC_TPT 0x040D4 /* Total Packets Tx - R/clr */
+#define IGC_PTC64 0x040D8 /* Packets Tx (64 bytes) - R/clr */
+#define IGC_PTC127 0x040DC /* Packets Tx (65-127 bytes) - R/clr */
+#define IGC_PTC255 0x040E0 /* Packets Tx (128-255 bytes) - R/clr */
+#define IGC_PTC511 0x040E4 /* Packets Tx (256-511 bytes) - R/clr */
+#define IGC_PTC1023 0x040E8 /* Packets Tx (512-1023 bytes) - R/clr */
+#define IGC_PTC1522 0x040EC /* Packets Tx (1024-1522 Bytes) - R/clr */
+#define IGC_MPTC 0x040F0 /* Multicast Packets Tx Count - R/clr */
+#define IGC_BPTC 0x040F4 /* Broadcast Packets Tx Count - R/clr */
+#define IGC_TSCTC 0x040F8 /* TCP Segmentation Context Tx - R/clr */
+#define IGC_IAC 0x04100 /* Interrupt Assertion Count */
+#define IGC_RXDMTC 0x04120 /* Rx Descriptor Minimum Threshold Count */
+
+#define IGC_VFGPRC 0x00F10
+#define IGC_VFGORC 0x00F18
+#define IGC_VFMPRC 0x00F3C
+#define IGC_VFGPTC 0x00F14
+#define IGC_VFGOTC 0x00F34
+#define IGC_VFGOTLBC 0x00F50
+#define IGC_VFGPTLBC 0x00F44
+#define IGC_VFGORLBC 0x00F48
+#define IGC_VFGPRLBC 0x00F40
+#define IGC_HGORCL 0x04128 /* Host Good Octets Received Count Low */
+#define IGC_HGORCH 0x0412C /* Host Good Octets Received Count High */
+#define IGC_HGOTCL 0x04130 /* Host Good Octets Transmit Count Low */
+#define IGC_HGOTCH 0x04134 /* Host Good Octets Transmit Count High */
+#define IGC_LENERRS 0x04138 /* Length Errors Count */
+#define IGC_PCS_ANADV 0x04218 /* AN advertisement - RW */
+#define IGC_PCS_LPAB 0x0421C /* Link Partner Ability - RW */
+#define IGC_RXCSUM 0x05000 /* Rx Checksum Control - RW */
+#define IGC_RLPML 0x05004 /* Rx Long Packet Max Length */
+#define IGC_RFCTL 0x05008 /* Receive Filter Control*/
+#define IGC_MTA 0x05200 /* Multicast Table Array - RW Array */
+#define IGC_RA 0x05400 /* Receive Address - RW Array */
+#define IGC_VFTA 0x05600 /* VLAN Filter Table Array - RW Array */
+#define IGC_WUC 0x05800 /* Wakeup Control - RW */
+#define IGC_WUFC 0x05808 /* Wakeup Filter Control - RW */
+#define IGC_WUS 0x05810 /* Wakeup Status - RO */
+/* Management registers */
+#define IGC_MANC 0x05820 /* Management Control - RW */
+#define IGC_IPAV 0x05838 /* IP Address Valid - RW */
+#define IGC_IP4AT 0x05840 /* IPv4 Address Table - RW Array */
+#define IGC_IP6AT 0x05880 /* IPv6 Address Table - RW Array */
+#define IGC_WUPL 0x05900 /* Wakeup Packet Length - RW */
+#define IGC_WUPM 0x05A00 /* Wakeup Packet Memory - RO A */
+#define IGC_WUPM_EXT 0x0B800 /* Wakeup Packet Memory Extended - RO Array */
+#define IGC_WUFC_EXT 0x0580C /* Wakeup Filter Control Extended - RW */
+#define IGC_WUS_EXT 0x05814 /* Wakeup Status Extended - RW1C */
+#define IGC_FHFTSL 0x05804 /* Flex Filter Indirect Table Select - RW */
+#define IGC_PROXYFCEX 0x05590 /* Proxy Filter Control Extended - RW1C */
+#define IGC_PROXYEXS 0x05594 /* Proxy Extended Status - RO */
+#define IGC_WFUTPF 0x05500 /* Wake Flex UDP TCP Port Filter - RW Array */
+#define IGC_RFUTPF 0x05580 /* Range Flex UDP TCP Port Filter - RW */
+#define IGC_RWPFC 0x05584 /* Range Wake Port Filter Control - RW */
+#define IGC_WFUTPS 0x05588 /* Wake Filter UDP TCP Status - RW1C */
+#define IGC_WCS 0x0558C /* Wake Control Status - RW1C */
+/* MSI-X Table Register Descriptions */
+#define IGC_PBACL 0x05B68 /* MSIx PBA Clear - Read/Write 1's to clear */
+#define IGC_FFLT 0x05F00 /* Flexible Filter Length Table - RW Array */
+#define IGC_HOST_IF 0x08800 /* Host Interface */
+/* Flexible Host Filter Table */
+#define IGC_FHFT(_n) (0x09000 + ((_n) * 0x100))
+/* Ext Flexible Host Filter Table */
+#define IGC_FHFT_EXT(_n) (0x09A00 + ((_n) * 0x100))
+
+
+#define IGC_KMRNCTRLSTA 0x00034 /* MAC-PHY interface - RW */
+#define IGC_MANC2H 0x05860 /* Management Control To Host - RW */
+/* Management Decision Filters */
+#define IGC_MDEF(_n) (0x05890 + (4 * (_n)))
+/* Semaphore registers */
+#define IGC_SW_FW_SYNC 0x05B5C /* SW-FW Synchronization - RW */
+/* Function Active and Power State to MNG */
+#define IGC_FACTPS 0x05B30
+#define IGC_SWSM 0x05B50 /* SW Semaphore */
+#define IGC_FWSM 0x05B54 /* FW Semaphore */
+/* Driver-only SW semaphore (not used by BOOT agents) */
+#define IGC_SWSM2 0x05B58
+#define IGC_FFLT_DBG 0x05F04 /* Debug Register */
+#define IGC_HICR 0x08F00 /* Host Interface Control */
+#define IGC_FWSTS 0x08F0C /* FW Status */
+
+/* RSS registers */
+#define IGC_MRQC 0x05818 /* Multiple Receive Control - RW */
+#define IGC_IMIR(_i) (0x05A80 + ((_i) * 4)) /* Immediate Interrupt */
+#define IGC_IMIREXT(_i) (0x05AA0 + ((_i) * 4)) /* Immediate INTR Ext*/
+#define IGC_IMIRVP 0x05AC0 /* Immediate INT Rx VLAN Priority -RW */
+#define IGC_MSIXBM(_i) (0x01600 + ((_i) * 4)) /* MSI-X Alloc Reg -RW */
+/* Redirection Table - RW Array */
+#define IGC_RETA(_i) (0x05C00 + ((_i) * 4))
+/* RSS Random Key - RW Array */
+#define IGC_RSSRK(_i) (0x05C80 + ((_i) * 4))
+#define IGC_RSSIM 0x05864 /* RSS Interrupt Mask */
+#define IGC_RSSIR 0x05868 /* RSS Interrupt Request */
+#define IGC_UTA 0x0A000 /* Unicast Table Array - RW */
+#define IGC_TSYNCRXCTL 0x0B620 /* Rx Time Sync Control register - RW */
+#define IGC_TSYNCTXCTL 0x0B614 /* Tx Time Sync Control register - RW */
+#define IGC_TSYNCRXCFG 0x05F50 /* Time Sync Rx Configuration - RW */
+#define IGC_RXSTMPL 0x0B624 /* Rx timestamp Low - RO */
+#define IGC_RXSTMPH 0x0B628 /* Rx timestamp High - RO */
+#define IGC_RXSATRL 0x0B62C /* Rx timestamp attribute low - RO */
+#define IGC_RXSATRH 0x0B630 /* Rx timestamp attribute high - RO */
+#define IGC_TXSTMPL 0x0B618 /* Tx timestamp value Low - RO */
+#define IGC_TXSTMPH 0x0B61C /* Tx timestamp value High - RO */
+#define IGC_SYSTIML 0x0B600 /* System time register Low - RO */
+#define IGC_SYSTIMH 0x0B604 /* System time register High - RO */
+#define IGC_TIMINCA 0x0B608 /* Increment attributes register - RW */
+#define IGC_TIMADJL 0x0B60C /* Time sync time adjustment offset Low - RW */
+#define IGC_TIMADJH 0x0B610 /* Time sync time adjustment offset High - RW */
+#define IGC_TSAUXC 0x0B640 /* Timesync Auxiliary Control register */
+#define IGC_SYSTIMR 0x0B6F8 /* System time register Residue */
+#define IGC_TSICR 0x0B66C /* Interrupt Cause Register */
+#define IGC_TSIM 0x0B674 /* Interrupt Mask Register */
+
+/* Filtering Registers */
+#define IGC_SAQF(_n) (0x05980 + (4 * (_n))) /* Source Address Queue Fltr */
+#define IGC_DAQF(_n) (0x059A0 + (4 * (_n))) /* Dest Address Queue Fltr */
+#define IGC_SPQF(_n) (0x059C0 + (4 * (_n))) /* Source Port Queue Fltr */
+#define IGC_FTQF(_n) (0x059E0 + (4 * (_n))) /* 5-tuple Queue Fltr */
+#define IGC_TTQF(_n) (0x059E0 + (4 * (_n))) /* 2-tuple Queue Fltr */
+#define IGC_SYNQF(_n) (0x055FC + (4 * (_n))) /* SYN Packet Queue Fltr */
+#define IGC_ETQF(_n) (0x05CB0 + (4 * (_n))) /* EType Queue Fltr */
+
+/* ETQF register bit definitions */
+#define IGC_ETQF_FILTER_ENABLE (1 << 26)
+#define IGC_ETQF_IMM_INT (1 << 29)
+#define IGC_ETQF_QUEUE_ENABLE (1 << 31)
+#define IGC_ETQF_QUEUE_SHIFT 16
+#define IGC_ETQF_QUEUE_MASK 0x00070000
+#define IGC_ETQF_ETYPE_MASK 0x0000FFFF
+
+#define IGC_RTTDCS 0x3600 /* Reedtown Tx Desc plane control and status */
+#define IGC_RTTPCS 0x3474 /* Reedtown Tx Packet Plane control and status */
+#define IGC_RTRPCS 0x2474 /* Rx packet plane control and status */
+#define IGC_RTRUP2TC 0x05AC4 /* Rx User Priority to Traffic Class */
+#define IGC_RTTUP2TC 0x0418 /* Transmit User Priority to Traffic Class */
+/* Tx Desc plane TC Rate-scheduler config */
+#define IGC_RTTDTCRC(_n) (0x3610 + ((_n) * 4))
+/* Tx Packet plane TC Rate-Scheduler Config */
+#define IGC_RTTPTCRC(_n) (0x3480 + ((_n) * 4))
+/* Rx Packet plane TC Rate-Scheduler Config */
+#define IGC_RTRPTCRC(_n) (0x2480 + ((_n) * 4))
+/* Tx Desc Plane TC Rate-Scheduler Status */
+#define IGC_RTTDTCRS(_n) (0x3630 + ((_n) * 4))
+/* Tx Desc Plane TC Rate-Scheduler MMW */
+#define IGC_RTTDTCRM(_n) (0x3650 + ((_n) * 4))
+/* Tx Packet plane TC Rate-Scheduler Status */
+#define IGC_RTTPTCRS(_n) (0x34A0 + ((_n) * 4))
+/* Tx Packet plane TC Rate-scheduler MMW */
+#define IGC_RTTPTCRM(_n) (0x34C0 + ((_n) * 4))
+/* Rx Packet plane TC Rate-Scheduler Status */
+#define IGC_RTRPTCRS(_n) (0x24A0 + ((_n) * 4))
+/* Rx Packet plane TC Rate-Scheduler MMW */
+#define IGC_RTRPTCRM(_n) (0x24C0 + ((_n) * 4))
+/* Tx Desc plane VM Rate-Scheduler MMW*/
+#define IGC_RTTDVMRM(_n) (0x3670 + ((_n) * 4))
+/* Tx BCN Rate-Scheduler MMW */
+#define IGC_RTTBCNRM(_n) (0x3690 + ((_n) * 4))
+#define IGC_RTTDQSEL 0x3604 /* Tx Desc Plane Queue Select */
+#define IGC_RTTDVMRC 0x3608 /* Tx Desc Plane VM Rate-Scheduler Config */
+#define IGC_RTTDVMRS 0x360C /* Tx Desc Plane VM Rate-Scheduler Status */
+#define IGC_RTTBCNRC 0x36B0 /* Tx BCN Rate-Scheduler Config */
+#define IGC_RTTBCNRS 0x36B4 /* Tx BCN Rate-Scheduler Status */
+#define IGC_RTTBCNCR 0xB200 /* Tx BCN Control Register */
+#define IGC_RTTBCNTG 0x35A4 /* Tx BCN Tagging */
+#define IGC_RTTBCNCP 0xB208 /* Tx BCN Congestion point */
+#define IGC_RTRBCNCR 0xB20C /* Rx BCN Control Register */
+#define IGC_RTTBCNRD 0x36B8 /* Tx BCN Rate Drift */
+#define IGC_PFCTOP 0x1080 /* Priority Flow Control Type and Opcode */
+#define IGC_RTTBCNIDX 0xB204 /* Tx BCN Congestion Point */
+#define IGC_RTTBCNACH 0x0B214 /* Tx BCN Control High */
+#define IGC_RTTBCNACL 0x0B210 /* Tx BCN Control Low */
+
+/* DMA Coalescing registers */
+#define IGC_DMACR 0x02508 /* Control Register */
+#define IGC_DMCTXTH 0x03550 /* Transmit Threshold */
+#define IGC_DMCTLX 0x02514 /* Time to Lx Request */
+#define IGC_DMCRTRH 0x05DD0 /* Receive Packet Rate Threshold */
+#define IGC_DMCCNT 0x05DD4 /* Current Rx Count */
+#define IGC_FCRTC 0x02170 /* Flow Control Rx high watermark */
+#define IGC_PCIEMISC 0x05BB8 /* PCIE misc config register */
+
+/* PCIe Parity Status Register */
+#define IGC_PCIEERRSTS 0x05BA8
+
+#define IGC_PROXYS 0x5F64 /* Proxying Status */
+#define IGC_PROXYFC 0x5F60 /* Proxying Filter Control */
+/* Thermal sensor configuration and status registers */
+#define IGC_THMJT 0x08100 /* Junction Temperature */
+#define IGC_THLOWTC 0x08104 /* Low Threshold Control */
+#define IGC_THMIDTC 0x08108 /* Mid Threshold Control */
+#define IGC_THHIGHTC 0x0810C /* High Threshold Control */
+#define IGC_THSTAT 0x08110 /* Thermal Sensor Status */
+
+/* Energy Efficient Ethernet "EEE" registers */
+#define IGC_IPCNFG 0x0E38 /* Internal PHY Configuration */
+#define IGC_LTRC 0x01A0 /* Latency Tolerance Reporting Control */
+#define IGC_EEER 0x0E30 /* Energy Efficient Ethernet "EEE"*/
+#define IGC_EEE_SU 0x0E34 /* EEE Setup */
+#define IGC_EEE_SU_2P5 0x0E3C /* EEE 2.5G Setup */
+#define IGC_TLPIC 0x4148 /* EEE Tx LPI Count - TLPIC */
+#define IGC_RLPIC 0x414C /* EEE Rx LPI Count - RLPIC */
+
+/* OS2BMC Registers */
+#define IGC_B2OSPC 0x08FE0 /* BMC2OS packets sent by BMC */
+#define IGC_B2OGPRC 0x04158 /* BMC2OS packets received by host */
+#define IGC_O2BGPTC 0x08FE4 /* OS2BMC packets received by BMC */
+#define IGC_O2BSPC 0x0415C /* OS2BMC packets transmitted by host */
+
+#define IGC_LTRMINV 0x5BB0 /* LTR Minimum Value */
+#define IGC_LTRMAXV 0x5BB4 /* LTR Maximum Value */
+
+
+/* IEEE 1588 TIMESYNCH */
+#define IGC_TRGTTIML0 0x0B644 /* Target Time Register 0 Low - RW */
+#define IGC_TRGTTIMH0 0x0B648 /* Target Time Register 0 High - RW */
+#define IGC_TRGTTIML1 0x0B64C /* Target Time Register 1 Low - RW */
+#define IGC_TRGTTIMH1 0x0B650 /* Target Time Register 1 High - RW */
+#define IGC_FREQOUT0 0x0B654 /* Frequency Out 0 Control Register - RW */
+#define IGC_FREQOUT1 0x0B658 /* Frequency Out 1 Control Register - RW */
+#define IGC_TSSDP 0x0003C /* Time Sync SDP Configuration Register - RW */
+
+
+#endif /* _IGC_REGS_H_ */