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