Add igc(4), a driver for the Intel 2.5Gb Ethernet controllers.
authorpatrick <patrick@openbsd.org>
Sun, 31 Oct 2021 14:52:57 +0000 (14:52 +0000)
committerpatrick <patrick@openbsd.org>
Sun, 31 Oct 2021 14:52:57 +0000 (14:52 +0000)
Ported by kevlo@
ok jmatthew@

18 files changed:
sys/dev/pci/files.pci
sys/dev/pci/if_igc.c [new file with mode: 0644]
sys/dev/pci/if_igc.h [new file with mode: 0644]
sys/dev/pci/igc_api.c [new file with mode: 0644]
sys/dev/pci/igc_api.h [new file with mode: 0644]
sys/dev/pci/igc_base.c [new file with mode: 0644]
sys/dev/pci/igc_base.h [new file with mode: 0644]
sys/dev/pci/igc_defines.h [new file with mode: 0644]
sys/dev/pci/igc_hw.h [new file with mode: 0644]
sys/dev/pci/igc_i225.c [new file with mode: 0644]
sys/dev/pci/igc_i225.h [new file with mode: 0644]
sys/dev/pci/igc_mac.c [new file with mode: 0644]
sys/dev/pci/igc_mac.h [new file with mode: 0644]
sys/dev/pci/igc_nvm.c [new file with mode: 0644]
sys/dev/pci/igc_nvm.h [new file with mode: 0644]
sys/dev/pci/igc_phy.c [new file with mode: 0644]
sys/dev/pci/igc_phy.h [new file with mode: 0644]
sys/dev/pci/igc_regs.h [new file with mode: 0644]

index a204597..e461143 100644 (file)
@@ -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 (file)
index 0000000..ed1c2f1
--- /dev/null
@@ -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 (file)
index 0000000..984ae6f
--- /dev/null
@@ -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 (file)
index 0000000..125eaa8
--- /dev/null
@@ -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 (file)
index 0000000..5cf1d12
--- /dev/null
@@ -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 (file)
index 0000000..fc441e9
--- /dev/null
@@ -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 (file)
index 0000000..aec33e0
--- /dev/null
@@ -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 (file)
index 0000000..3618aa4
--- /dev/null
@@ -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 (file)
index 0000000..4224afb
--- /dev/null
@@ -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 (file)
index 0000000..bc532e2
--- /dev/null
@@ -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 (file)
index 0000000..cb28541
--- /dev/null
@@ -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 (file)
index 0000000..32fd4ce
--- /dev/null
@@ -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 (file)
index 0000000..0b800e9
--- /dev/null
@@ -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 (file)
index 0000000..2659b59
--- /dev/null
@@ -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 (file)
index 0000000..39404ce
--- /dev/null
@@ -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 (file)
index 0000000..7b7d320
--- /dev/null
@@ -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 (file)
index 0000000..ded2589
--- /dev/null
@@ -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 (file)
index 0000000..b3d4b8e
--- /dev/null
@@ -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_ */