diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -224,6 +224,7 @@ iicmux.4 \ iicsmb.4 \ iir.4 \ + ${_igc.4} \ ${_imcsmb.4} \ inet.4 \ inet6.4 \ @@ -814,6 +815,7 @@ _if_urtw.4= if_urtw.4 _if_vmx.4= if_vmx.4 _if_wpi.4= if_wpi.4 +_igc.4= igc.4 _imcsmb.4= imcsmb.4 _ipmi.4= ipmi.4 _io.4= io.4 diff --git a/share/man/man4/igc.4 b/share/man/man4/igc.4 new file mode 100644 --- /dev/null +++ b/share/man/man4/igc.4 @@ -0,0 +1,167 @@ +.\"- +.\" Copyright 2021 Intel Corp +.\" Copyright 2021 Rubicon Communications, LLC (Netgate) +.\" SPDX-License-Identifier: BSD-3-Clause +.\" +.\" $FreeBSD$ +.\" +.Dd May 10, 2021 +.Dt IGC +.Os +.Sh NAME +.Nm igc +.Nd "Intel Ethernet Controller I225 driver" +.Sh SYNOPSIS +To compile this driver into the kernel, +place the following lines in your +kernel configuration file: +.Bd -ragged -offset indent +.Cd "device iflib" +.Cd "device igc" +.Ed +.Pp +Alternatively, to load the driver as a +module at boot time, place the following line in +.Xr loader.conf 5 : +.Bd -literal -offset indent +if_igc_load="YES" +.Ed +.Sh DESCRIPTION +The +.Nm +driver provides support for any PCI Express adapter or LOM (LAN +On Motherboard) based on the Intel I225 Multi Gigabit Controller. +The driver supports Transmit/Receive checksum offload, Jumbo Frames, +MSI/MSI-X, TSO, and RSS. +.Pp +Support for Jumbo Frames is provided via the interface MTU setting. +Selecting an MTU larger than 1500 bytes with the +.Xr ifconfig 8 +utility +configures the adapter to receive and transmit Jumbo Frames. +The maximum MTU size for Jumbo Frames is 9216 bytes. +.Pp +This driver version supports VLAN hardware insertion / extraction, and +VLAN checksum offload. +For information on enabling VLANs, see +.Xr ifconfig 8 . +The +.Nm +driver supports the following media types: +.Bl -tag -width ".Cm 10baseT/UTP" +.It Cm autoselect +Enables auto-negotiation for speed and duplex. +.It Cm 10baseT/UTP +Sets 10Mbps operation. +Use the +.Cm mediaopt +option to select +.Cm half-duplex +mode. +.It Cm 100baseTX +Sets 100Mbps operation. +Use the +.Cm mediaopt +option to select +.Cm half-duplex +mode. +.It Cm 1000baseT +Sets 1000Mbps operation. +Only +.Cm full-duplex +mode is supported at this speed. +.It Cm 2500baseT +Sets 2500Mbps operation. +Only +.Cm full-duplex +mode is supported at this speed. +.El +.Pp +.Sh HARDWARE +The +.Nm +driver supports the following models: +.Pp +.Bl -bullet -compact +.It +I225-LM +.It +I225-V +.It +I225-IT +.It +I225-K +.El +.Sh LOADER TUNABLES +Tunables can be set at the +.Xr loader 8 +prompt before booting the kernel or stored in +.Xr loader.conf 5 . +.Bl -tag -width indent +.It Va hw.igc.igc_disable_crc_stripping +Disable or enable hardware stripping of CRC field. +This is mostly useful on BMC/IPMI shared interfaces where stripping the +CRC causes remote access over IPMI to fail. +Default 0 (enabled). +.It Va hw.igc.rx_int_delay +This value delays the generation of receive interrupts in units +of 1.024 microseconds. +The default value is 0, since adapters may hang with this feature being +enabled. +.It Va hw.igc.rx_abs_int_delay +If hw.igc.rx_int_delay is non-zero, this tunable limits the +maximum delay in which a receive interrupt is generated. +.It Va hw.igc.tx_int_delay +This value delays the generation of transmit interrupts in units +of 1.024 microseconds. +The default value is 64. +.It Va hw.igc.tx_abs_int_delay +If hw.igc.tx_int_delay is non-zero, this tunable limits the +maximum delay in which a transmit interrupt is generated. +.It Va hw.igc.sbp +Show bad packets when in promiscuous mode. +Default is false. +.It Va hw.igc.rx_process_limit +Maximum number of received packets to process at a time. +Default is 100. +A value of -1 means unlimited. +.It Va hw.igc.eee_setting +Disable or enable Energy Efficient Ethernet. +Default 1 (disabled). +.It Va hw.igc.max_interrupt_rate +Maximum device interrupts per second. +The default is 8000. +.El +.Sh DIAGNOSTICS +.Bl -diag +.It "igc%d: Hardware Initialization Failed" +A fatal initialization error has occurred. +.It "igc%d: Unable to allocate bus resource: memory" +A fatal initialization error has occurred. +.It "igc%d: Invalid MAC address" +The MAC address programmed into the EEPROM is either empty or a multicast/broadcast +address. +.El +.Sh SEE ALSO +.Xr altq 4 , +.Xr arp 4 , +.Xr iflib 4 , +.Xr netintro 4 , +.Xr ng_ether 4 , +.Xr vlan 4 , +.Xr ifconfig 8 +.Sh HISTORY +The +.Nm +device driver first appeared in +.Fx 14.0 . +.Sh AUTHORS +.An -nosplit +The +.Nm +was originally written by +.An Intel Corporation +and converted to the +.Xr iflib 4 +framework by +.An Netgate . diff --git a/sys/amd64/conf/GENERIC b/sys/amd64/conf/GENERIC --- a/sys/amd64/conf/GENERIC +++ b/sys/amd64/conf/GENERIC @@ -249,6 +249,7 @@ # PCI/PCI-X/PCIe Ethernet NICs that use iflib infrastructure device iflib device em # Intel PRO/1000 Gigabit Ethernet Family +device igc # Intel I225 2.5G Ethernet device ix # Intel PRO/10GbE PCIE PF Ethernet device ixv # Intel PRO/10GbE PCIE VF Ethernet device ixl # Intel 700 Series Physical Function diff --git a/sys/amd64/conf/NOTES b/sys/amd64/conf/NOTES --- a/sys/amd64/conf/NOTES +++ b/sys/amd64/conf/NOTES @@ -287,6 +287,7 @@ # adapters. # ice: Intel 800 Series Physical Function # Requires the ice_ddp module for full functionality +# igc: Intel I225 2.5Gb Ethernet adapter # ipw: Intel PRO/Wireless 2100 IEEE 802.11 adapter # Requires the ipw firmware module # iwi: Intel PRO/Wireless 2200BG/2225BG/2915ABG IEEE 802.11 adapters @@ -309,6 +310,7 @@ options ED_3C503 options ED_HPP options ED_SIC +device igc # Intel I225 2.5G Ethernet device ipw # Intel 2100 wireless NICs. device iwi # Intel 2200BG/2225BG/2915ABG wireless NICs. device iwn # Intel 4965/1000/5000/6000 wireless NICs. diff --git a/sys/conf/files b/sys/conf/files --- a/sys/conf/files +++ b/sys/conf/files @@ -1885,6 +1885,14 @@ dev/iir/iir.c optional iir dev/iir/iir_ctrl.c optional iir dev/iir/iir_pci.c optional iir pci +dev/igc/if_igc.c optional igc iflib pci +dev/igc/igc_api.c optional igc iflib pci +dev/igc/igc_base.c optional igc iflib pci +dev/igc/igc_i225.c optional igc iflib pci +dev/igc/igc_mac.c optional igc iflib pci +dev/igc/igc_nvm.c optional igc iflib pci +dev/igc/igc_phy.c optional igc iflib pci +dev/igc/igc_txrx.c optional igc iflib pci dev/intpm/intpm.c optional intpm pci # XXX Work around clang warning, until maintainer approves fix. dev/ips/ips.c optional ips \ diff --git a/sys/dev/igc/if_igc.h b/sys/dev/igc/if_igc.h new file mode 100644 --- /dev/null +++ b/sys/dev/igc/if_igc.h @@ -0,0 +1,430 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2016 Nicole Graziano + * 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$ + */ + +#include "opt_ddb.h" +#include "opt_inet.h" +#include "opt_inet6.h" +#include "opt_rss.h" + +#ifdef HAVE_KERNEL_OPTION_HEADERS +#include "opt_device_polling.h" +#endif + +#include +#include +#ifdef DDB +#include +#include +#endif +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "igc_api.h" +#include "igc_i225.h" +#include "ifdi_if.h" + + +#ifndef _IGC_H_DEFINED_ +#define _IGC_H_DEFINED_ + + +/* Tunables */ + +/* + * 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 + +/* Tunables -- End */ + +#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 + +#define IGC_MSIX_BAR 3 + +/* Defines for printing debug information */ +#define DEBUG_INIT 0 +#define DEBUG_IOCTL 0 +#define DEBUG_HW 0 + +#define INIT_DEBUGOUT(S) if (DEBUG_INIT) printf(S "\n") +#define INIT_DEBUGOUT1(S, A) if (DEBUG_INIT) printf(S "\n", A) +#define INIT_DEBUGOUT2(S, A, B) if (DEBUG_INIT) printf(S "\n", A, B) +#define IOCTL_DEBUGOUT(S) if (DEBUG_IOCTL) printf(S "\n") +#define IOCTL_DEBUGOUT1(S, A) if (DEBUG_IOCTL) printf(S "\n", A) +#define IOCTL_DEBUGOUT2(S, A, B) if (DEBUG_IOCTL) printf(S "\n", A, B) +#define HW_DEBUGOUT(S) if (DEBUG_HW) printf(S "\n") +#define HW_DEBUGOUT1(S, A) if (DEBUG_HW) printf(S "\n", A) +#define HW_DEBUGOUT2(S, A, B) if (DEBUG_HW) printf(S "\n", A, B) + +#define IGC_MAX_SCATTER 40 +#define IGC_VFTA_SIZE 128 +#define IGC_TSO_SIZE 65535 +#define IGC_TSO_SEG_SIZE 4096 /* Max dma segment size */ +#define IGC_CSUM_OFFLOAD (CSUM_IP | CSUM_IP_UDP | CSUM_IP_TCP | \ + CSUM_IP_SCTP | CSUM_IP6_UDP | CSUM_IP6_TCP | \ + CSUM_IP6_SCTP) /* Offload bits in mbuf flag */ + +struct igc_adapter; + +struct igc_int_delay_info { + struct igc_adapter *adapter; /* Back-pointer to the adapter struct */ + int offset; /* Register offset to read/write */ + int value; /* Current value in usecs */ +}; + +/* + * The transmit ring, one per tx queue + */ +struct tx_ring { + struct igc_adapter *adapter; + struct igc_tx_desc *tx_base; + uint64_t tx_paddr; + qidx_t *tx_rsq; + uint8_t me; + qidx_t tx_rs_cidx; + qidx_t tx_rs_pidx; + qidx_t tx_cidx_processed; + /* Interrupt resources */ + void *tag; + struct resource *res; + unsigned long tx_irq; + + /* Saved csum offloading context information */ + int csum_flags; + int csum_lhlen; + int csum_iphlen; + + int csum_thlen; + int csum_mss; + int csum_pktlen; + + uint32_t csum_txd_upper; + uint32_t csum_txd_lower; /* last field */ +}; + +/* + * The Receive ring, one per rx queue + */ +struct rx_ring { + struct igc_adapter *adapter; + struct igc_rx_queue *que; + u32 me; + u32 payload; + union igc_rx_desc_extended *rx_base; + uint64_t rx_paddr; + + /* Interrupt resources */ + void *tag; + struct resource *res; + + /* Soft stats */ + unsigned long rx_irq; + unsigned long rx_discarded; + unsigned long rx_packets; + unsigned long rx_bytes; +}; + +struct igc_tx_queue { + struct igc_adapter *adapter; + u32 msix; + u32 eims; /* This queue's EIMS bit */ + u32 me; + struct tx_ring txr; +}; + +struct igc_rx_queue { + struct igc_adapter *adapter; + u32 me; + u32 msix; + u32 eims; + struct rx_ring rxr; + u64 irqs; + struct if_irq que_irq; +}; + +/* Our adapter structure */ +struct igc_adapter { + struct ifnet *ifp; + struct igc_hw hw; + + if_softc_ctx_t shared; + if_ctx_t ctx; +#define tx_num_queues shared->isc_ntxqsets +#define rx_num_queues shared->isc_nrxqsets +#define intr_type shared->isc_intr + /* FreeBSD operating-system-specific structures. */ + struct igc_osdep osdep; + device_t dev; + struct cdev *led_dev; + + struct igc_tx_queue *tx_queues; + struct igc_rx_queue *rx_queues; + struct if_irq irq; + + struct resource *memory; + struct resource *flash; + struct resource *ioport; + + struct resource *res; + void *tag; + u32 linkvec; + u32 ivars; + + struct ifmedia *media; + int msix; + int if_flags; + int igc_insert_vlan_header; + u32 ims; + + u32 flags; + /* Task for FAST handling */ + struct grouptask link_task; + + u16 num_vlans; + u32 txd_cmd; + + u32 tx_process_limit; + u32 rx_process_limit; + u32 rx_mbuf_sz; + + /* Management and WOL features */ + u32 wol; + + /* Multicast array memory */ + u8 *mta; + + /* + ** Shadow VFTA table, this is needed because + ** the real vlan filter table gets cleared during + ** a soft reset and the driver needs to be able + ** to repopulate it. + */ + u32 shadow_vfta[IGC_VFTA_SIZE]; + + /* Info about the interface */ + u16 link_active; + u16 fc; + u16 link_speed; + u16 link_duplex; + u32 smartspeed; + u32 dmac; + int link_mask; + + u64 que_mask; + + struct igc_int_delay_info tx_int_delay; + struct igc_int_delay_info tx_abs_int_delay; + struct igc_int_delay_info rx_int_delay; + struct igc_int_delay_info rx_abs_int_delay; + struct igc_int_delay_info tx_itr; + + /* Misc stats maintained by the driver */ + unsigned long dropped_pkts; + unsigned long link_irq; + unsigned long rx_overruns; + unsigned long watchdog_events; + + struct igc_hw_stats stats; + u16 vf_ifp; +}; + +void igc_dump_rs(struct igc_adapter *); + +#define IGC_RSSRK_SIZE 4 +#define IGC_RSSRK_VAL(key, i) (key[(i) * IGC_RSSRK_SIZE] | \ + key[(i) * IGC_RSSRK_SIZE + 1] << 8 | \ + key[(i) * IGC_RSSRK_SIZE + 2] << 16 | \ + key[(i) * IGC_RSSRK_SIZE + 3] << 24) +#endif /* _IGC_H_DEFINED_ */ diff --git a/sys/dev/igc/if_igc.c b/sys/dev/igc/if_igc.c new file mode 100644 --- /dev/null +++ b/sys/dev/igc/if_igc.c @@ -0,0 +1,2984 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2016 Nicole Graziano + * 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 +__FBSDID("$FreeBSD$"); + +#include "if_igc.h" +#include +#include + +#ifdef RSS +#include +#include +#endif + +/********************************************************************* + * PCI Device ID Table + * + * Used by probe to select devices to load on + * Last entry must be all 0s + * + * { Vendor ID, Device ID, String } + *********************************************************************/ + +static pci_vendor_info_t igc_vendor_info_array[] = +{ + /* Intel(R) PRO/1000 Network Connection - igc */ + PVID(0x8086, IGC_DEV_ID_I225_LM, "Intel(R) Ethernet Controller I225-LM"), + PVID(0x8086, IGC_DEV_ID_I225_V, "Intel(R) Ethernet Controller I225-V"), + PVID(0x8086, IGC_DEV_ID_I225_K, "Intel(R) Ethernet Controller I225-K"), + PVID(0x8086, IGC_DEV_ID_I225_I, "Intel(R) Ethernet Controller I225-I"), + PVID(0x8086, IGC_DEV_ID_I220_V, "Intel(R) Ethernet Controller I220-V"), + PVID(0x8086, IGC_DEV_ID_I225_K2, "Intel(R) Ethernet Controller I225-K(2)"), + PVID(0x8086, IGC_DEV_ID_I225_LMVP, "Intel(R) Ethernet Controller I225-LMvP(2)"), + PVID(0x8086, IGC_DEV_ID_I226_K, "Intel(R) Ethernet Controller I226-K"), + PVID(0x8086, IGC_DEV_ID_I225_IT, "Intel(R) Ethernet Controller I225-IT(2)"), + PVID(0x8086, IGC_DEV_ID_I226_LM, "Intel(R) Ethernet Controller I226-LM"), + PVID(0x8086, IGC_DEV_ID_I226_V, "Intel(R) Ethernet Controller I226-V"), + PVID(0x8086, IGC_DEV_ID_I226_IT, "Intel(R) Ethernet Controller I226-IT"), + PVID(0x8086, IGC_DEV_ID_I221_V, "Intel(R) Ethernet Controller I221-V"), + PVID(0x8086, IGC_DEV_ID_I226_BLANK_NVM, "Intel(R) Ethernet Controller I226(blankNVM)"), + PVID(0x8086, IGC_DEV_ID_I225_BLANK_NVM, "Intel(R) Ethernet Controller I225(blankNVM)"), + /* required last entry */ + PVID_END +}; + +/********************************************************************* + * Function prototypes + *********************************************************************/ +static void *igc_register(device_t dev); +static int igc_if_attach_pre(if_ctx_t ctx); +static int igc_if_attach_post(if_ctx_t ctx); +static int igc_if_detach(if_ctx_t ctx); +static int igc_if_shutdown(if_ctx_t ctx); +static int igc_if_suspend(if_ctx_t ctx); +static int igc_if_resume(if_ctx_t ctx); + +static int igc_if_tx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs, int ntxqs, int ntxqsets); +static int igc_if_rx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs, int nrxqs, int nrxqsets); +static void igc_if_queues_free(if_ctx_t ctx); + +static uint64_t igc_if_get_counter(if_ctx_t, ift_counter); +static void igc_if_init(if_ctx_t ctx); +static void igc_if_stop(if_ctx_t ctx); +static void igc_if_media_status(if_ctx_t, struct ifmediareq *); +static int igc_if_media_change(if_ctx_t ctx); +static int igc_if_mtu_set(if_ctx_t ctx, uint32_t mtu); +static void igc_if_timer(if_ctx_t ctx, uint16_t qid); +static void igc_if_vlan_register(if_ctx_t ctx, u16 vtag); +static void igc_if_vlan_unregister(if_ctx_t ctx, u16 vtag); +static void igc_if_watchdog_reset(if_ctx_t ctx); +static bool igc_if_needs_restart(if_ctx_t ctx, enum iflib_restart_event event); + +static void igc_identify_hardware(if_ctx_t ctx); +static int igc_allocate_pci_resources(if_ctx_t ctx); +static void igc_free_pci_resources(if_ctx_t ctx); +static void igc_reset(if_ctx_t ctx); +static int igc_setup_interface(if_ctx_t ctx); +static int igc_setup_msix(if_ctx_t ctx); + +static void igc_initialize_transmit_unit(if_ctx_t ctx); +static void igc_initialize_receive_unit(if_ctx_t ctx); + +static void igc_if_intr_enable(if_ctx_t ctx); +static void igc_if_intr_disable(if_ctx_t ctx); +static int igc_if_rx_queue_intr_enable(if_ctx_t ctx, uint16_t rxqid); +static int igc_if_tx_queue_intr_enable(if_ctx_t ctx, uint16_t txqid); +static void igc_if_multi_set(if_ctx_t ctx); +static void igc_if_update_admin_status(if_ctx_t ctx); +static void igc_if_debug(if_ctx_t ctx); +static void igc_update_stats_counters(struct igc_adapter *); +static void igc_add_hw_stats(struct igc_adapter *adapter); +static int igc_if_set_promisc(if_ctx_t ctx, int flags); +static void igc_setup_vlan_hw_support(struct igc_adapter *); +static int igc_sysctl_nvm_info(SYSCTL_HANDLER_ARGS); +static void igc_print_nvm_info(struct igc_adapter *); +static int igc_sysctl_debug_info(SYSCTL_HANDLER_ARGS); +static int igc_get_rs(SYSCTL_HANDLER_ARGS); +static void igc_print_debug_info(struct igc_adapter *); +static int igc_is_valid_ether_addr(u8 *); +static int igc_sysctl_int_delay(SYSCTL_HANDLER_ARGS); +static void igc_add_int_delay_sysctl(struct igc_adapter *, const char *, + const char *, struct igc_int_delay_info *, int, int); +/* Management and WOL Support */ +static void igc_get_hw_control(struct igc_adapter *); +static void igc_release_hw_control(struct igc_adapter *); +static void igc_get_wakeup(if_ctx_t ctx); +static void igc_enable_wakeup(if_ctx_t ctx); + +int igc_intr(void *arg); + +/* MSI-X handlers */ +static int igc_if_msix_intr_assign(if_ctx_t, int); +static int igc_msix_link(void *); +static void igc_handle_link(void *context); + +static int igc_set_flowcntl(SYSCTL_HANDLER_ARGS); +static int igc_sysctl_eee(SYSCTL_HANDLER_ARGS); + +static int igc_get_regs(SYSCTL_HANDLER_ARGS); + +static void igc_configure_queues(struct igc_adapter *adapter); + + +/********************************************************************* + * FreeBSD Device Interface Entry Points + *********************************************************************/ +static device_method_t igc_methods[] = { + /* Device interface */ + DEVMETHOD(device_register, igc_register), + DEVMETHOD(device_probe, iflib_device_probe), + DEVMETHOD(device_attach, iflib_device_attach), + DEVMETHOD(device_detach, iflib_device_detach), + DEVMETHOD(device_shutdown, iflib_device_shutdown), + DEVMETHOD(device_suspend, iflib_device_suspend), + DEVMETHOD(device_resume, iflib_device_resume), + DEVMETHOD_END +}; + +static driver_t igc_driver = { + "igc", igc_methods, sizeof(struct igc_adapter), +}; + +static devclass_t igc_devclass; +DRIVER_MODULE(igc, pci, igc_driver, igc_devclass, 0, 0); + +MODULE_DEPEND(igc, pci, 1, 1, 1); +MODULE_DEPEND(igc, ether, 1, 1, 1); +MODULE_DEPEND(igc, iflib, 1, 1, 1); + +IFLIB_PNP_INFO(pci, igc, igc_vendor_info_array); + +static device_method_t igc_if_methods[] = { + DEVMETHOD(ifdi_attach_pre, igc_if_attach_pre), + DEVMETHOD(ifdi_attach_post, igc_if_attach_post), + DEVMETHOD(ifdi_detach, igc_if_detach), + DEVMETHOD(ifdi_shutdown, igc_if_shutdown), + DEVMETHOD(ifdi_suspend, igc_if_suspend), + DEVMETHOD(ifdi_resume, igc_if_resume), + DEVMETHOD(ifdi_init, igc_if_init), + DEVMETHOD(ifdi_stop, igc_if_stop), + DEVMETHOD(ifdi_msix_intr_assign, igc_if_msix_intr_assign), + DEVMETHOD(ifdi_intr_enable, igc_if_intr_enable), + DEVMETHOD(ifdi_intr_disable, igc_if_intr_disable), + DEVMETHOD(ifdi_tx_queues_alloc, igc_if_tx_queues_alloc), + DEVMETHOD(ifdi_rx_queues_alloc, igc_if_rx_queues_alloc), + DEVMETHOD(ifdi_queues_free, igc_if_queues_free), + DEVMETHOD(ifdi_update_admin_status, igc_if_update_admin_status), + DEVMETHOD(ifdi_multi_set, igc_if_multi_set), + DEVMETHOD(ifdi_media_status, igc_if_media_status), + DEVMETHOD(ifdi_media_change, igc_if_media_change), + DEVMETHOD(ifdi_mtu_set, igc_if_mtu_set), + DEVMETHOD(ifdi_promisc_set, igc_if_set_promisc), + DEVMETHOD(ifdi_timer, igc_if_timer), + DEVMETHOD(ifdi_watchdog_reset, igc_if_watchdog_reset), + DEVMETHOD(ifdi_vlan_register, igc_if_vlan_register), + DEVMETHOD(ifdi_vlan_unregister, igc_if_vlan_unregister), + DEVMETHOD(ifdi_get_counter, igc_if_get_counter), + DEVMETHOD(ifdi_rx_queue_intr_enable, igc_if_rx_queue_intr_enable), + DEVMETHOD(ifdi_tx_queue_intr_enable, igc_if_tx_queue_intr_enable), + DEVMETHOD(ifdi_debug, igc_if_debug), + DEVMETHOD(ifdi_needs_restart, igc_if_needs_restart), + DEVMETHOD_END +}; + +static driver_t igc_if_driver = { + "igc_if", igc_if_methods, sizeof(struct igc_adapter) +}; + +/********************************************************************* + * Tunable default values. + *********************************************************************/ + +#define IGC_TICKS_TO_USECS(ticks) ((1024 * (ticks) + 500) / 1000) +#define IGC_USECS_TO_TICKS(usecs) ((1000 * (usecs) + 512) / 1024) + +#define MAX_INTS_PER_SEC 8000 +#define DEFAULT_ITR (1000000000/(MAX_INTS_PER_SEC * 256)) + +/* Allow common code without TSO */ +#ifndef CSUM_TSO +#define CSUM_TSO 0 +#endif + +static SYSCTL_NODE(_hw, OID_AUTO, igc, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, + "igc driver parameters"); + +static int igc_disable_crc_stripping = 0; +SYSCTL_INT(_hw_igc, OID_AUTO, disable_crc_stripping, CTLFLAG_RDTUN, + &igc_disable_crc_stripping, 0, "Disable CRC Stripping"); + +static int igc_tx_int_delay_dflt = IGC_TICKS_TO_USECS(IGC_TIDV_VAL); +static int igc_rx_int_delay_dflt = IGC_TICKS_TO_USECS(IGC_RDTR_VAL); +SYSCTL_INT(_hw_igc, OID_AUTO, tx_int_delay, CTLFLAG_RDTUN, &igc_tx_int_delay_dflt, + 0, "Default transmit interrupt delay in usecs"); +SYSCTL_INT(_hw_igc, OID_AUTO, rx_int_delay, CTLFLAG_RDTUN, &igc_rx_int_delay_dflt, + 0, "Default receive interrupt delay in usecs"); + +static int igc_tx_abs_int_delay_dflt = IGC_TICKS_TO_USECS(IGC_TADV_VAL); +static int igc_rx_abs_int_delay_dflt = IGC_TICKS_TO_USECS(IGC_RADV_VAL); +SYSCTL_INT(_hw_igc, OID_AUTO, tx_abs_int_delay, CTLFLAG_RDTUN, + &igc_tx_abs_int_delay_dflt, 0, + "Default transmit interrupt delay limit in usecs"); +SYSCTL_INT(_hw_igc, OID_AUTO, rx_abs_int_delay, CTLFLAG_RDTUN, + &igc_rx_abs_int_delay_dflt, 0, + "Default receive interrupt delay limit in usecs"); + +static int igc_smart_pwr_down = false; +SYSCTL_INT(_hw_igc, OID_AUTO, smart_pwr_down, CTLFLAG_RDTUN, &igc_smart_pwr_down, + 0, "Set to true to leave smart power down enabled on newer adapters"); + +/* Controls whether promiscuous also shows bad packets */ +static int igc_debug_sbp = true; +SYSCTL_INT(_hw_igc, OID_AUTO, sbp, CTLFLAG_RDTUN, &igc_debug_sbp, 0, + "Show bad packets in promiscuous mode"); + +/* How many packets rxeof tries to clean at a time */ +static int igc_rx_process_limit = 100; +SYSCTL_INT(_hw_igc, OID_AUTO, rx_process_limit, CTLFLAG_RDTUN, + &igc_rx_process_limit, 0, + "Maximum number of received packets to process " + "at a time, -1 means unlimited"); + +/* Energy efficient ethernet - default to OFF */ +static int igc_eee_setting = 1; +SYSCTL_INT(_hw_igc, OID_AUTO, eee_setting, CTLFLAG_RDTUN, &igc_eee_setting, 0, + "Enable Energy Efficient Ethernet"); + +/* +** Tuneable Interrupt rate +*/ +static int igc_max_interrupt_rate = 8000; +SYSCTL_INT(_hw_igc, OID_AUTO, max_interrupt_rate, CTLFLAG_RDTUN, + &igc_max_interrupt_rate, 0, "Maximum interrupts per second"); + +extern struct if_txrx igc_txrx; + +static struct if_shared_ctx igc_sctx_init = { + .isc_magic = IFLIB_MAGIC, + .isc_q_align = PAGE_SIZE, + .isc_tx_maxsize = IGC_TSO_SIZE + sizeof(struct ether_vlan_header), + .isc_tx_maxsegsize = PAGE_SIZE, + .isc_tso_maxsize = IGC_TSO_SIZE + sizeof(struct ether_vlan_header), + .isc_tso_maxsegsize = IGC_TSO_SEG_SIZE, + .isc_rx_maxsize = MAX_JUMBO_FRAME_SIZE, + .isc_rx_nsegments = 1, + .isc_rx_maxsegsize = MJUM9BYTES, + .isc_nfl = 1, + .isc_nrxqs = 1, + .isc_ntxqs = 1, + .isc_admin_intrcnt = 1, + .isc_vendor_info = igc_vendor_info_array, + .isc_driver_version = "1", + .isc_driver = &igc_if_driver, + .isc_flags = IFLIB_NEED_SCRATCH | IFLIB_TSO_INIT_IP | IFLIB_NEED_ZERO_CSUM, + + .isc_nrxd_min = {IGC_MIN_RXD}, + .isc_ntxd_min = {IGC_MIN_TXD}, + .isc_nrxd_max = {IGC_MAX_RXD}, + .isc_ntxd_max = {IGC_MAX_TXD}, + .isc_nrxd_default = {IGC_DEFAULT_RXD}, + .isc_ntxd_default = {IGC_DEFAULT_TXD}, +}; + +/***************************************************************** + * + * Dump Registers + * + ****************************************************************/ +#define IGC_REGS_LEN 739 + +static int igc_get_regs(SYSCTL_HANDLER_ARGS) +{ + struct igc_adapter *adapter = (struct igc_adapter *)arg1; + struct igc_hw *hw = &adapter->hw; + struct sbuf *sb; + u32 *regs_buff; + int rc; + + regs_buff = malloc(sizeof(u32) * IGC_REGS_LEN, M_DEVBUF, M_WAITOK); + memset(regs_buff, 0, IGC_REGS_LEN * sizeof(u32)); + + rc = sysctl_wire_old_buffer(req, 0); + MPASS(rc == 0); + if (rc != 0) { + free(regs_buff, M_DEVBUF); + return (rc); + } + + sb = sbuf_new_for_sysctl(NULL, NULL, 32*400, req); + MPASS(sb != NULL); + if (sb == NULL) { + free(regs_buff, M_DEVBUF); + return (ENOMEM); + } + + /* General Registers */ + regs_buff[0] = IGC_READ_REG(hw, IGC_CTRL); + regs_buff[1] = IGC_READ_REG(hw, IGC_STATUS); + regs_buff[2] = IGC_READ_REG(hw, IGC_CTRL_EXT); + regs_buff[3] = IGC_READ_REG(hw, IGC_ICR); + regs_buff[4] = IGC_READ_REG(hw, IGC_RCTL); + regs_buff[5] = IGC_READ_REG(hw, IGC_RDLEN(0)); + regs_buff[6] = IGC_READ_REG(hw, IGC_RDH(0)); + regs_buff[7] = IGC_READ_REG(hw, IGC_RDT(0)); + regs_buff[8] = IGC_READ_REG(hw, IGC_RXDCTL(0)); + regs_buff[9] = IGC_READ_REG(hw, IGC_RDBAL(0)); + regs_buff[10] = IGC_READ_REG(hw, IGC_RDBAH(0)); + regs_buff[11] = IGC_READ_REG(hw, IGC_TCTL); + regs_buff[12] = IGC_READ_REG(hw, IGC_TDBAL(0)); + regs_buff[13] = IGC_READ_REG(hw, IGC_TDBAH(0)); + regs_buff[14] = IGC_READ_REG(hw, IGC_TDLEN(0)); + regs_buff[15] = IGC_READ_REG(hw, IGC_TDH(0)); + regs_buff[16] = IGC_READ_REG(hw, IGC_TDT(0)); + regs_buff[17] = IGC_READ_REG(hw, IGC_TXDCTL(0)); + + sbuf_printf(sb, "General Registers\n"); + sbuf_printf(sb, "\tCTRL\t %08x\n", regs_buff[0]); + sbuf_printf(sb, "\tSTATUS\t %08x\n", regs_buff[1]); + sbuf_printf(sb, "\tCTRL_EXIT\t %08x\n\n", regs_buff[2]); + + sbuf_printf(sb, "Interrupt Registers\n"); + sbuf_printf(sb, "\tICR\t %08x\n\n", regs_buff[3]); + + sbuf_printf(sb, "RX Registers\n"); + sbuf_printf(sb, "\tRCTL\t %08x\n", regs_buff[4]); + sbuf_printf(sb, "\tRDLEN\t %08x\n", regs_buff[5]); + sbuf_printf(sb, "\tRDH\t %08x\n", regs_buff[6]); + sbuf_printf(sb, "\tRDT\t %08x\n", regs_buff[7]); + sbuf_printf(sb, "\tRXDCTL\t %08x\n", regs_buff[8]); + sbuf_printf(sb, "\tRDBAL\t %08x\n", regs_buff[9]); + sbuf_printf(sb, "\tRDBAH\t %08x\n\n", regs_buff[10]); + + sbuf_printf(sb, "TX Registers\n"); + sbuf_printf(sb, "\tTCTL\t %08x\n", regs_buff[11]); + sbuf_printf(sb, "\tTDBAL\t %08x\n", regs_buff[12]); + sbuf_printf(sb, "\tTDBAH\t %08x\n", regs_buff[13]); + sbuf_printf(sb, "\tTDLEN\t %08x\n", regs_buff[14]); + sbuf_printf(sb, "\tTDH\t %08x\n", regs_buff[15]); + sbuf_printf(sb, "\tTDT\t %08x\n", regs_buff[16]); + sbuf_printf(sb, "\tTXDCTL\t %08x\n", regs_buff[17]); + sbuf_printf(sb, "\tTDFH\t %08x\n", regs_buff[18]); + sbuf_printf(sb, "\tTDFT\t %08x\n", regs_buff[19]); + sbuf_printf(sb, "\tTDFHS\t %08x\n", regs_buff[20]); + sbuf_printf(sb, "\tTDFPC\t %08x\n\n", regs_buff[21]); + + free(regs_buff, M_DEVBUF); + +#ifdef DUMP_DESCS + { + if_softc_ctx_t scctx = adapter->shared; + struct rx_ring *rxr = &rx_que->rxr; + struct tx_ring *txr = &tx_que->txr; + int ntxd = scctx->isc_ntxd[0]; + int nrxd = scctx->isc_nrxd[0]; + int j; + + for (j = 0; j < nrxd; j++) { + u32 staterr = le32toh(rxr->rx_base[j].wb.upper.status_error); + u32 length = le32toh(rxr->rx_base[j].wb.upper.length); + sbuf_printf(sb, "\tReceive Descriptor Address %d: %08" PRIx64 " Error:%d Length:%d\n", j, rxr->rx_base[j].read.buffer_addr, staterr, length); + } + + for (j = 0; j < min(ntxd, 256); j++) { + unsigned int *ptr = (unsigned int *)&txr->tx_base[j]; + + sbuf_printf(sb, "\tTXD[%03d] [0]: %08x [1]: %08x [2]: %08x [3]: %08x eop: %d DD=%d\n", + j, ptr[0], ptr[1], ptr[2], ptr[3], buf->eop, + buf->eop != -1 ? txr->tx_base[buf->eop].upper.fields.status & IGC_TXD_STAT_DD : 0); + + } + } +#endif + + rc = sbuf_finish(sb); + sbuf_delete(sb); + return(rc); +} + +static void * +igc_register(device_t dev) +{ + return (&igc_sctx_init); +} + +static int +igc_set_num_queues(if_ctx_t ctx) +{ + int maxqueues; + + maxqueues = 4; + + return (maxqueues); +} + +#define IGC_CAPS \ + IFCAP_HWCSUM | IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING | \ + IFCAP_VLAN_HWCSUM | IFCAP_WOL | IFCAP_VLAN_HWFILTER | IFCAP_TSO4 | \ + IFCAP_LRO | IFCAP_VLAN_HWTSO | IFCAP_JUMBO_MTU | IFCAP_HWCSUM_IPV6 |\ + IFCAP_TSO6 + +/********************************************************************* + * 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 + *********************************************************************/ +static int +igc_if_attach_pre(if_ctx_t ctx) +{ + struct igc_adapter *adapter; + if_softc_ctx_t scctx; + device_t dev; + struct igc_hw *hw; + int error = 0; + + INIT_DEBUGOUT("igc_if_attach_pre: begin"); + dev = iflib_get_dev(ctx); + adapter = iflib_get_softc(ctx); + + adapter->ctx = adapter->osdep.ctx = ctx; + adapter->dev = adapter->osdep.dev = dev; + scctx = adapter->shared = iflib_get_softc_ctx(ctx); + adapter->media = iflib_get_media(ctx); + hw = &adapter->hw; + + adapter->tx_process_limit = scctx->isc_ntxd[0]; + + /* SYSCTL stuff */ + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "nvm", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, + adapter, 0, igc_sysctl_nvm_info, "I", "NVM Information"); + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "debug", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, + adapter, 0, igc_sysctl_debug_info, "I", "Debug Information"); + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "fc", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, + adapter, 0, igc_set_flowcntl, "I", "Flow Control"); + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "reg_dump", + CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, adapter, 0, + igc_get_regs, "A", "Dump Registers"); + + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "rs_dump", + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, adapter, 0, + igc_get_rs, "I", "Dump RS indexes"); + + /* Determine hardware and mac info */ + igc_identify_hardware(ctx); + + scctx->isc_tx_nsegments = IGC_MAX_SCATTER; + scctx->isc_nrxqsets_max = scctx->isc_ntxqsets_max = igc_set_num_queues(ctx); + if (bootverbose) + device_printf(dev, "attach_pre capping queues at %d\n", + scctx->isc_ntxqsets_max); + + scctx->isc_txqsizes[0] = roundup2(scctx->isc_ntxd[0] * sizeof(union igc_adv_tx_desc), IGC_DBA_ALIGN); + scctx->isc_rxqsizes[0] = roundup2(scctx->isc_nrxd[0] * sizeof(union igc_adv_rx_desc), IGC_DBA_ALIGN); + scctx->isc_txd_size[0] = sizeof(union igc_adv_tx_desc); + scctx->isc_rxd_size[0] = sizeof(union igc_adv_rx_desc); + scctx->isc_txrx = &igc_txrx; + scctx->isc_tx_tso_segments_max = IGC_MAX_SCATTER; + scctx->isc_tx_tso_size_max = IGC_TSO_SIZE; + scctx->isc_tx_tso_segsize_max = IGC_TSO_SEG_SIZE; + scctx->isc_capabilities = scctx->isc_capenable = IGC_CAPS; + scctx->isc_tx_csum_flags = CSUM_TCP | CSUM_UDP | CSUM_TSO | + CSUM_IP6_TCP | CSUM_IP6_UDP | CSUM_SCTP | CSUM_IP6_SCTP; + + /* + ** Some new devices, as with ixgbe, now may + ** use a different BAR, so we need to keep + ** track of which is used. + */ + scctx->isc_msix_bar = PCIR_BAR(IGC_MSIX_BAR); + if (pci_read_config(dev, scctx->isc_msix_bar, 4) == 0) + scctx->isc_msix_bar += 4; + + /* Setup PCI resources */ + if (igc_allocate_pci_resources(ctx)) { + device_printf(dev, "Allocation of PCI resources failed\n"); + error = ENXIO; + goto err_pci; + } + + /* Do Shared Code initialization */ + error = igc_setup_init_funcs(hw, true); + if (error) { + device_printf(dev, "Setup of Shared code failed, error %d\n", + error); + error = ENXIO; + goto err_pci; + } + + igc_setup_msix(ctx); + igc_get_bus_info(hw); + + /* Set up some sysctls for the tunable interrupt delays */ + igc_add_int_delay_sysctl(adapter, "rx_int_delay", + "receive interrupt delay in usecs", &adapter->rx_int_delay, + IGC_REGISTER(hw, IGC_RDTR), igc_rx_int_delay_dflt); + igc_add_int_delay_sysctl(adapter, "tx_int_delay", + "transmit interrupt delay in usecs", &adapter->tx_int_delay, + IGC_REGISTER(hw, IGC_TIDV), igc_tx_int_delay_dflt); + igc_add_int_delay_sysctl(adapter, "rx_abs_int_delay", + "receive interrupt delay limit in usecs", + &adapter->rx_abs_int_delay, + IGC_REGISTER(hw, IGC_RADV), + igc_rx_abs_int_delay_dflt); + igc_add_int_delay_sysctl(adapter, "tx_abs_int_delay", + "transmit interrupt delay limit in usecs", + &adapter->tx_abs_int_delay, + IGC_REGISTER(hw, IGC_TADV), + igc_tx_abs_int_delay_dflt); + igc_add_int_delay_sysctl(adapter, "itr", + "interrupt delay limit in usecs/4", + &adapter->tx_itr, + IGC_REGISTER(hw, IGC_ITR), + DEFAULT_ITR); + + 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 frame limits assuming + * standard ethernet sized frames. + */ + scctx->isc_max_frame_size = adapter->hw.mac.max_frame_size = + ETHERMTU + ETHER_HDR_LEN + ETHERNET_FCS_SIZE; + + /* Allocate multicast array memory. */ + adapter->mta = malloc(sizeof(u8) * ETHER_ADDR_LEN * + MAX_NUM_MULTICAST_ADDRESSES, M_DEVBUF, M_NOWAIT); + if (adapter->mta == NULL) { + device_printf(dev, "Can not allocate multicast setup array\n"); + error = ENOMEM; + goto err_late; + } + + /* Check SOL/IDER usage */ + if (igc_check_reset_block(hw)) + device_printf(dev, "PHY reset is blocked" + " due to SOL/IDER session.\n"); + + /* Sysctl for setting Energy Efficient Ethernet */ + adapter->hw.dev_spec._i225.eee_disable = igc_eee_setting; + SYSCTL_ADD_PROC(device_get_sysctl_ctx(dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), + OID_AUTO, "eee_control", + CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, + adapter, 0, igc_sysctl_eee, "I", + "Disable Energy Efficient Ethernet"); + + /* + ** Start from a known state, this is + ** important in reading the nvm and + ** mac from that. + */ + 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) { + device_printf(dev, + "The EEPROM Checksum Is Not Valid\n"); + error = EIO; + goto err_late; + } + } + + /* Copy the permanent MAC address out of the EEPROM */ + if (igc_read_mac_addr(hw) < 0) { + device_printf(dev, "EEPROM read error while reading MAC" + " address\n"); + error = EIO; + goto err_late; + } + + if (!igc_is_valid_ether_addr(hw->mac.addr)) { + device_printf(dev, "Invalid MAC address\n"); + error = EIO; + goto err_late; + } + + /* + * Get Wake-on-Lan and Management info for later use + */ + igc_get_wakeup(ctx); + + /* Enable only WOL MAGIC by default */ + scctx->isc_capenable &= ~IFCAP_WOL; + if (adapter->wol != 0) + scctx->isc_capenable |= IFCAP_WOL_MAGIC; + + iflib_set_mac(ctx, hw->mac.addr); + + return (0); + +err_late: + igc_release_hw_control(adapter); +err_pci: + igc_free_pci_resources(ctx); + free(adapter->mta, M_DEVBUF); + + return (error); +} + +static int +igc_if_attach_post(if_ctx_t ctx) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + struct igc_hw *hw = &adapter->hw; + int error = 0; + + /* Setup OS specific network interface */ + error = igc_setup_interface(ctx); + if (error != 0) { + goto err_late; + } + + igc_reset(ctx); + + /* Initialize statistics */ + igc_update_stats_counters(adapter); + hw->mac.get_link_status = true; + igc_if_update_admin_status(ctx); + igc_add_hw_stats(adapter); + + /* the driver can now take control from firmware */ + igc_get_hw_control(adapter); + + INIT_DEBUGOUT("igc_if_attach_post: end"); + + return (error); + +err_late: + igc_release_hw_control(adapter); + igc_free_pci_resources(ctx); + igc_if_queues_free(ctx); + free(adapter->mta, M_DEVBUF); + + return (error); +} + +/********************************************************************* + * 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 + *********************************************************************/ +static int +igc_if_detach(if_ctx_t ctx) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + + INIT_DEBUGOUT("igc_if_detach: begin"); + + igc_phy_hw_reset(&adapter->hw); + + igc_release_hw_control(adapter); + igc_free_pci_resources(ctx); + + return (0); +} + +/********************************************************************* + * + * Shutdown entry point + * + **********************************************************************/ + +static int +igc_if_shutdown(if_ctx_t ctx) +{ + return igc_if_suspend(ctx); +} + +/* + * Suspend/resume device methods. + */ +static int +igc_if_suspend(if_ctx_t ctx) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + + igc_release_hw_control(adapter); + igc_enable_wakeup(ctx); + return (0); +} + +static int +igc_if_resume(if_ctx_t ctx) +{ + igc_if_init(ctx); + + return(0); +} + +static int +igc_if_mtu_set(if_ctx_t ctx, uint32_t mtu) +{ + int max_frame_size; + struct igc_adapter *adapter = iflib_get_softc(ctx); + if_softc_ctx_t scctx = iflib_get_softc_ctx(ctx); + + IOCTL_DEBUGOUT("ioctl rcv'd: SIOCSIFMTU (Set Interface MTU)"); + + /* 9K Jumbo Frame size */ + max_frame_size = 9234; + + if (mtu > max_frame_size - ETHER_HDR_LEN - ETHER_CRC_LEN) { + return (EINVAL); + } + + scctx->isc_max_frame_size = adapter->hw.mac.max_frame_size = + mtu + ETHER_HDR_LEN + ETHER_CRC_LEN; + return (0); +} + +/********************************************************************* + * Init entry point + * + * This routine is used in two ways. It is used by the stack as + * init entry point in network interface structure. It is also used + * by the driver as a hw/sw initialization routine to get to a + * consistent state. + * + **********************************************************************/ +static void +igc_if_init(if_ctx_t ctx) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + if_softc_ctx_t scctx = adapter->shared; + struct ifnet *ifp = iflib_get_ifp(ctx); + struct igc_tx_queue *tx_que; + int i; + + INIT_DEBUGOUT("igc_if_init: begin"); + + /* Get the latest mac address, User can use a LAA */ + bcopy(if_getlladdr(ifp), adapter->hw.mac.addr, + ETHER_ADDR_LEN); + + /* Put the address into the Receive Address Array */ + igc_rar_set(&adapter->hw, adapter->hw.mac.addr, 0); + + /* Initialize the hardware */ + igc_reset(ctx); + igc_if_update_admin_status(ctx); + + for (i = 0, tx_que = adapter->tx_queues; i < adapter->tx_num_queues; i++, tx_que++) { + struct tx_ring *txr = &tx_que->txr; + + txr->tx_rs_cidx = txr->tx_rs_pidx; + + /* Initialize the last processed descriptor to be the end of + * the ring, rather than the start, so that we avoid an + * off-by-one error when calculating how many descriptors are + * done in the credits_update function. + */ + txr->tx_cidx_processed = scctx->isc_ntxd[0] - 1; + } + + /* Setup VLAN support, basic and offload if available */ + IGC_WRITE_REG(&adapter->hw, IGC_VET, ETHERTYPE_VLAN); + + /* Prepare transmit descriptors and buffers */ + igc_initialize_transmit_unit(ctx); + + /* Setup Multicast table */ + igc_if_multi_set(ctx); + + adapter->rx_mbuf_sz = iflib_get_rx_mbuf_sz(ctx); + igc_initialize_receive_unit(ctx); + + /* Use real VLAN Filter support? */ + if (if_getcapenable(ifp) & IFCAP_VLAN_HWTAGGING) { + if (if_getcapenable(ifp) & IFCAP_VLAN_HWFILTER) + /* Use real VLAN Filter support */ + igc_setup_vlan_hw_support(adapter); + else { + u32 ctrl; + ctrl = IGC_READ_REG(&adapter->hw, IGC_CTRL); + ctrl |= IGC_CTRL_VME; + IGC_WRITE_REG(&adapter->hw, IGC_CTRL, ctrl); + } + } + + /* Don't lose promiscuous settings */ + igc_if_set_promisc(ctx, IFF_PROMISC); + igc_clear_hw_cntrs_base_generic(&adapter->hw); + + if (adapter->intr_type == IFLIB_INTR_MSIX) /* Set up queue routing */ + igc_configure_queues(adapter); + + /* this clears any pending interrupts */ + IGC_READ_REG(&adapter->hw, IGC_ICR); + IGC_WRITE_REG(&adapter->hw, IGC_ICS, IGC_ICS_LSC); + + /* the driver can now take control from firmware */ + igc_get_hw_control(adapter); + + /* Set Energy Efficient Ethernet */ + igc_set_eee_i225(&adapter->hw, true, true, true); +} + +/********************************************************************* + * + * Fast Legacy/MSI Combined Interrupt Service routine + * + *********************************************************************/ +int +igc_intr(void *arg) +{ + struct igc_adapter *adapter = arg; + if_ctx_t ctx = adapter->ctx; + u32 reg_icr; + + reg_icr = IGC_READ_REG(&adapter->hw, IGC_ICR); + + /* Hot eject? */ + if (reg_icr == 0xffffffff) + return FILTER_STRAY; + + /* Definitely not our interrupt. */ + if (reg_icr == 0x0) + return FILTER_STRAY; + + if ((reg_icr & IGC_ICR_INT_ASSERTED) == 0) + return FILTER_STRAY; + + /* + * Only MSI-X interrupts have one-shot behavior by taking advantage + * of the EIAC register. Thus, explicitly disable interrupts. This + * also works around the MSI message reordering errata on certain + * systems. + */ + IFDI_INTR_DISABLE(ctx); + + /* Link status change */ + if (reg_icr & (IGC_ICR_RXSEQ | IGC_ICR_LSC)) + igc_handle_link(ctx); + + if (reg_icr & IGC_ICR_RXO) + adapter->rx_overruns++; + + return (FILTER_SCHEDULE_THREAD); +} + +static int +igc_if_rx_queue_intr_enable(if_ctx_t ctx, uint16_t rxqid) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + struct igc_rx_queue *rxq = &adapter->rx_queues[rxqid]; + + IGC_WRITE_REG(&adapter->hw, IGC_EIMS, rxq->eims); + return (0); +} + +static int +igc_if_tx_queue_intr_enable(if_ctx_t ctx, uint16_t txqid) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + struct igc_tx_queue *txq = &adapter->tx_queues[txqid]; + + IGC_WRITE_REG(&adapter->hw, IGC_EIMS, txq->eims); + return (0); +} + +/********************************************************************* + * + * MSI-X RX Interrupt Service routine + * + **********************************************************************/ +static int +igc_msix_que(void *arg) +{ + struct igc_rx_queue *que = arg; + + ++que->irqs; + + return (FILTER_SCHEDULE_THREAD); +} + +/********************************************************************* + * + * MSI-X Link Fast Interrupt Service routine + * + **********************************************************************/ +static int +igc_msix_link(void *arg) +{ + struct igc_adapter *adapter = arg; + u32 reg_icr; + + ++adapter->link_irq; + MPASS(adapter->hw.back != NULL); + reg_icr = IGC_READ_REG(&adapter->hw, IGC_ICR); + + if (reg_icr & IGC_ICR_RXO) + adapter->rx_overruns++; + + if (reg_icr & (IGC_ICR_RXSEQ | IGC_ICR_LSC)) { + igc_handle_link(adapter->ctx); + } + + IGC_WRITE_REG(&adapter->hw, IGC_IMS, IGC_IMS_LSC); + IGC_WRITE_REG(&adapter->hw, IGC_EIMS, adapter->link_mask); + + return (FILTER_HANDLED); +} + +static void +igc_handle_link(void *context) +{ + if_ctx_t ctx = context; + struct igc_adapter *adapter = iflib_get_softc(ctx); + + adapter->hw.mac.get_link_status = true; + iflib_admin_intr_deferred(ctx); +} + +/********************************************************************* + * + * Media Ioctl callback + * + * This routine is called whenever the user queries the status of + * the interface using ifconfig. + * + **********************************************************************/ +static void +igc_if_media_status(if_ctx_t ctx, struct ifmediareq *ifmr) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + + INIT_DEBUGOUT("igc_if_media_status: begin"); + + iflib_admin_intr_deferred(ctx); + + ifmr->ifm_status = IFM_AVALID; + ifmr->ifm_active = IFM_ETHER; + + if (!adapter->link_active) { + return; + } + + ifmr->ifm_status |= IFM_ACTIVE; + + switch (adapter->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 (adapter->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. + * + **********************************************************************/ +static int +igc_if_media_change(if_ctx_t ctx) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + struct ifmedia *ifm = iflib_get_media(ctx); + + INIT_DEBUGOUT("igc_if_media_change: begin"); + + if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) + return (EINVAL); + + adapter->hw.mac.autoneg = DO_AUTO_NEG; + + switch (IFM_SUBTYPE(ifm->ifm_media)) { + case IFM_AUTO: + adapter->hw.phy.autoneg_advertised = AUTONEG_ADV_DEFAULT; + break; + case IFM_2500_T: + adapter->hw.phy.autoneg_advertised = ADVERTISE_2500_FULL; + break; + case IFM_1000_T: + adapter->hw.phy.autoneg_advertised = ADVERTISE_1000_FULL; + break; + case IFM_100_TX: + if ((ifm->ifm_media & IFM_GMASK) == IFM_HDX) + adapter->hw.phy.autoneg_advertised = ADVERTISE_100_HALF; + else + adapter->hw.phy.autoneg_advertised = ADVERTISE_100_FULL; + break; + case IFM_10_T: + if ((ifm->ifm_media & IFM_GMASK) == IFM_HDX) + adapter->hw.phy.autoneg_advertised = ADVERTISE_10_HALF; + else + adapter->hw.phy.autoneg_advertised = ADVERTISE_10_FULL; + break; + default: + device_printf(adapter->dev, "Unsupported media type\n"); + } + + igc_if_init(ctx); + + return (0); +} + +static int +igc_if_set_promisc(if_ctx_t ctx, int flags) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + struct ifnet *ifp = iflib_get_ifp(ctx); + u32 reg_rctl; + int mcnt = 0; + + reg_rctl = IGC_READ_REG(&adapter->hw, IGC_RCTL); + reg_rctl &= ~(IGC_RCTL_SBP | IGC_RCTL_UPE); + if (flags & IFF_ALLMULTI) + mcnt = MAX_NUM_MULTICAST_ADDRESSES; + else + mcnt = min(if_llmaddr_count(ifp), MAX_NUM_MULTICAST_ADDRESSES); + + /* Don't disable if in MAX groups */ + if (mcnt < MAX_NUM_MULTICAST_ADDRESSES) + reg_rctl &= (~IGC_RCTL_MPE); + IGC_WRITE_REG(&adapter->hw, IGC_RCTL, reg_rctl); + + if (flags & IFF_PROMISC) { + reg_rctl |= (IGC_RCTL_UPE | IGC_RCTL_MPE); + /* Turn this on if you want to see bad packets */ + if (igc_debug_sbp) + reg_rctl |= IGC_RCTL_SBP; + IGC_WRITE_REG(&adapter->hw, IGC_RCTL, reg_rctl); + } else if (flags & IFF_ALLMULTI) { + reg_rctl |= IGC_RCTL_MPE; + reg_rctl &= ~IGC_RCTL_UPE; + IGC_WRITE_REG(&adapter->hw, IGC_RCTL, reg_rctl); + } + return (0); +} + +static u_int +igc_copy_maddr(void *arg, struct sockaddr_dl *sdl, u_int idx) +{ + u8 *mta = arg; + + if (idx == MAX_NUM_MULTICAST_ADDRESSES) + return (0); + + bcopy(LLADDR(sdl), &mta[idx * ETHER_ADDR_LEN], ETHER_ADDR_LEN); + + return (1); +} + +/********************************************************************* + * Multicast Update + * + * This routine is called whenever multicast address list is updated. + * + **********************************************************************/ + +static void +igc_if_multi_set(if_ctx_t ctx) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + struct ifnet *ifp = iflib_get_ifp(ctx); + u8 *mta; /* Multicast array memory */ + u32 reg_rctl = 0; + int mcnt = 0; + + IOCTL_DEBUGOUT("igc_set_multi: begin"); + + mta = adapter->mta; + bzero(mta, sizeof(u8) * ETHER_ADDR_LEN * MAX_NUM_MULTICAST_ADDRESSES); + + mcnt = if_foreach_llmaddr(ifp, igc_copy_maddr, mta); + + reg_rctl = IGC_READ_REG(&adapter->hw, IGC_RCTL); + + if (if_getflags(ifp) & IFF_PROMISC) { + reg_rctl |= (IGC_RCTL_UPE | IGC_RCTL_MPE); + /* Turn this on if you want to see bad packets */ + if (igc_debug_sbp) + reg_rctl |= IGC_RCTL_SBP; + } else if (mcnt >= MAX_NUM_MULTICAST_ADDRESSES || + if_getflags(ifp) & IFF_ALLMULTI) { + reg_rctl |= IGC_RCTL_MPE; + reg_rctl &= ~IGC_RCTL_UPE; + } else + reg_rctl = ~(IGC_RCTL_UPE | IGC_RCTL_MPE); + + IGC_WRITE_REG(&adapter->hw, IGC_RCTL, reg_rctl); + + if (mcnt < MAX_NUM_MULTICAST_ADDRESSES) + igc_update_mc_addr_list(&adapter->hw, mta, mcnt); +} + +/********************************************************************* + * Timer routine + * + * This routine schedules igc_if_update_admin_status() to check for + * link status and to gather statistics as well as to perform some + * controller-specific hardware patting. + * + **********************************************************************/ +static void +igc_if_timer(if_ctx_t ctx, uint16_t qid) +{ + + if (qid != 0) + return; + + iflib_admin_intr_deferred(ctx); +} + +static void +igc_if_update_admin_status(if_ctx_t ctx) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + struct igc_hw *hw = &adapter->hw; + device_t dev = iflib_get_dev(ctx); + u32 link_check, thstat, ctrl; + + link_check = thstat = ctrl = 0; + /* Get the cached link value or read phy for real */ + switch (hw->phy.media_type) { + case igc_media_type_copper: + if (hw->mac.get_link_status == true) { + /* Do the work to read phy */ + igc_check_for_link(hw); + link_check = !hw->mac.get_link_status; + } else + link_check = true; + break; + case igc_media_type_unknown: + igc_check_for_link(hw); + link_check = !hw->mac.get_link_status; + /* FALLTHROUGH */ + default: + break; + } + + /* Now check for a transition */ + if (link_check && (adapter->link_active == 0)) { + igc_get_speed_and_duplex(hw, &adapter->link_speed, + &adapter->link_duplex); + if (bootverbose) + device_printf(dev, "Link is up %d Mbps %s\n", + adapter->link_speed, + ((adapter->link_duplex == FULL_DUPLEX) ? + "Full Duplex" : "Half Duplex")); + adapter->link_active = 1; + iflib_link_state_change(ctx, LINK_STATE_UP, + IF_Mbps(adapter->link_speed)); + } else if (!link_check && (adapter->link_active == 1)) { + adapter->link_speed = 0; + adapter->link_duplex = 0; + adapter->link_active = 0; + iflib_link_state_change(ctx, LINK_STATE_DOWN, 0); + } + igc_update_stats_counters(adapter); +} + +static void +igc_if_watchdog_reset(if_ctx_t ctx) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + + /* + * Just count the event; iflib(4) will already trigger a + * sufficient reset of the controller. + */ + adapter->watchdog_events++; +} + +/********************************************************************* + * + * This routine disables all traffic on the adapter by issuing a + * global reset on the MAC. + * + **********************************************************************/ +static void +igc_if_stop(if_ctx_t ctx) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + + INIT_DEBUGOUT("igc_if_stop: begin"); + + igc_reset_hw(&adapter->hw); + IGC_WRITE_REG(&adapter->hw, IGC_WUC, 0); +} + +/********************************************************************* + * + * Determine hardware revision. + * + **********************************************************************/ +static void +igc_identify_hardware(if_ctx_t ctx) +{ + device_t dev = iflib_get_dev(ctx); + struct igc_adapter *adapter = iflib_get_softc(ctx); + + /* Make sure our PCI config space has the necessary stuff set */ + adapter->hw.bus.pci_cmd_word = pci_read_config(dev, PCIR_COMMAND, 2); + + /* Save off the information about this board */ + adapter->hw.vendor_id = pci_get_vendor(dev); + adapter->hw.device_id = pci_get_device(dev); + adapter->hw.revision_id = pci_read_config(dev, PCIR_REVID, 1); + adapter->hw.subsystem_vendor_id = + pci_read_config(dev, PCIR_SUBVEND_0, 2); + adapter->hw.subsystem_device_id = + pci_read_config(dev, PCIR_SUBDEV_0, 2); + + /* Do Shared Code Init and Setup */ + if (igc_set_mac_type(&adapter->hw)) { + device_printf(dev, "Setup init failure\n"); + return; + } +} + +static int +igc_allocate_pci_resources(if_ctx_t ctx) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + device_t dev = iflib_get_dev(ctx); + int rid; + + rid = PCIR_BAR(0); + adapter->memory = bus_alloc_resource_any(dev, SYS_RES_MEMORY, + &rid, RF_ACTIVE); + if (adapter->memory == NULL) { + device_printf(dev, "Unable to allocate bus resource: memory\n"); + return (ENXIO); + } + adapter->osdep.mem_bus_space_tag = rman_get_bustag(adapter->memory); + adapter->osdep.mem_bus_space_handle = + rman_get_bushandle(adapter->memory); + adapter->hw.hw_addr = (u8 *)&adapter->osdep.mem_bus_space_handle; + + adapter->hw.back = &adapter->osdep; + + return (0); +} + +/********************************************************************* + * + * Set up the MSI-X Interrupt handlers + * + **********************************************************************/ +static int +igc_if_msix_intr_assign(if_ctx_t ctx, int msix) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + struct igc_rx_queue *rx_que = adapter->rx_queues; + struct igc_tx_queue *tx_que = adapter->tx_queues; + int error, rid, i, vector = 0, rx_vectors; + char buf[16]; + + /* First set up ring resources */ + for (i = 0; i < adapter->rx_num_queues; i++, rx_que++, vector++) { + rid = vector + 1; + snprintf(buf, sizeof(buf), "rxq%d", i); + error = iflib_irq_alloc_generic(ctx, &rx_que->que_irq, rid, IFLIB_INTR_RXTX, igc_msix_que, rx_que, rx_que->me, buf); + if (error) { + device_printf(iflib_get_dev(ctx), "Failed to allocate que int %d err: %d", i, error); + adapter->rx_num_queues = i + 1; + goto fail; + } + + rx_que->msix = vector; + + /* + * Set the bit to enable interrupt + * in IGC_IMS -- bits 20 and 21 + * are for RX0 and RX1, note this has + * NOTHING to do with the MSI-X vector + */ + rx_que->eims = 1 << vector; + } + rx_vectors = vector; + + vector = 0; + for (i = 0; i < adapter->tx_num_queues; i++, tx_que++, vector++) { + snprintf(buf, sizeof(buf), "txq%d", i); + tx_que = &adapter->tx_queues[i]; + iflib_softirq_alloc_generic(ctx, + &adapter->rx_queues[i % adapter->rx_num_queues].que_irq, + IFLIB_INTR_TX, tx_que, tx_que->me, buf); + + tx_que->msix = (vector % adapter->rx_num_queues); + + /* + * Set the bit to enable interrupt + * in IGC_IMS -- bits 22 and 23 + * are for TX0 and TX1, note this has + * NOTHING to do with the MSI-X vector + */ + tx_que->eims = 1 << i; + } + + /* Link interrupt */ + rid = rx_vectors + 1; + error = iflib_irq_alloc_generic(ctx, &adapter->irq, rid, IFLIB_INTR_ADMIN, igc_msix_link, adapter, 0, "aq"); + + if (error) { + device_printf(iflib_get_dev(ctx), "Failed to register admin handler"); + goto fail; + } + adapter->linkvec = rx_vectors; + return (0); +fail: + iflib_irq_free(ctx, &adapter->irq); + rx_que = adapter->rx_queues; + for (int i = 0; i < adapter->rx_num_queues; i++, rx_que++) + iflib_irq_free(ctx, &rx_que->que_irq); + return (error); +} + +static void +igc_configure_queues(struct igc_adapter *adapter) +{ + struct igc_hw *hw = &adapter->hw; + struct igc_rx_queue *rx_que; + struct igc_tx_queue *tx_que; + u32 ivar = 0, newitr = 0; + + /* First turn on RSS capability */ + IGC_WRITE_REG(hw, IGC_GPIE, + IGC_GPIE_MSIX_MODE | IGC_GPIE_EIAME | IGC_GPIE_PBA | + IGC_GPIE_NSICR); + + /* Turn on MSI-X */ + /* RX entries */ + for (int i = 0; i < adapter->rx_num_queues; i++) { + u32 index = i >> 1; + ivar = IGC_READ_REG_ARRAY(hw, IGC_IVAR0, index); + rx_que = &adapter->rx_queues[i]; + if (i & 1) { + ivar &= 0xFF00FFFF; + ivar |= (rx_que->msix | IGC_IVAR_VALID) << 16; + } else { + ivar &= 0xFFFFFF00; + ivar |= rx_que->msix | IGC_IVAR_VALID; + } + IGC_WRITE_REG_ARRAY(hw, IGC_IVAR0, index, ivar); + } + /* TX entries */ + for (int i = 0; i < adapter->tx_num_queues; i++) { + u32 index = i >> 1; + ivar = IGC_READ_REG_ARRAY(hw, IGC_IVAR0, index); + tx_que = &adapter->tx_queues[i]; + if (i & 1) { + ivar &= 0x00FFFFFF; + ivar |= (tx_que->msix | IGC_IVAR_VALID) << 24; + } else { + ivar &= 0xFFFF00FF; + ivar |= (tx_que->msix | IGC_IVAR_VALID) << 8; + } + IGC_WRITE_REG_ARRAY(hw, IGC_IVAR0, index, ivar); + adapter->que_mask |= tx_que->eims; + } + + /* And for the link interrupt */ + ivar = (adapter->linkvec | IGC_IVAR_VALID) << 8; + adapter->link_mask = 1 << adapter->linkvec; + IGC_WRITE_REG(hw, IGC_IVAR_MISC, ivar); + + /* Set the starting interrupt rate */ + if (igc_max_interrupt_rate > 0) + newitr = (4000000 / igc_max_interrupt_rate) & 0x7FFC; + + newitr |= IGC_EITR_CNT_IGNR; + + for (int i = 0; i < adapter->rx_num_queues; i++) { + rx_que = &adapter->rx_queues[i]; + IGC_WRITE_REG(hw, IGC_EITR(rx_que->msix), newitr); + } + + return; +} + +static void +igc_free_pci_resources(if_ctx_t ctx) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + struct igc_rx_queue *que = adapter->rx_queues; + device_t dev = iflib_get_dev(ctx); + + /* Release all MSI-X queue resources */ + if (adapter->intr_type == IFLIB_INTR_MSIX) + iflib_irq_free(ctx, &adapter->irq); + + for (int i = 0; i < adapter->rx_num_queues; i++, que++) { + iflib_irq_free(ctx, &que->que_irq); + } + + if (adapter->memory != NULL) { + bus_release_resource(dev, SYS_RES_MEMORY, + rman_get_rid(adapter->memory), adapter->memory); + adapter->memory = NULL; + } + + if (adapter->flash != NULL) { + bus_release_resource(dev, SYS_RES_MEMORY, + rman_get_rid(adapter->flash), adapter->flash); + adapter->flash = NULL; + } + + if (adapter->ioport != NULL) { + bus_release_resource(dev, SYS_RES_IOPORT, + rman_get_rid(adapter->ioport), adapter->ioport); + adapter->ioport = NULL; + } +} + +/* Set up MSI or MSI-X */ +static int +igc_setup_msix(if_ctx_t ctx) +{ + return (0); +} + +/********************************************************************* + * + * Initialize the DMA Coalescing feature + * + **********************************************************************/ +static void +igc_init_dmac(struct igc_adapter *adapter, u32 pba) +{ + device_t dev = adapter->dev; + struct igc_hw *hw = &adapter->hw; + u32 dmac, reg = ~IGC_DMACR_DMAC_EN; + u16 hwm; + u16 max_frame_size; + int status; + + max_frame_size = adapter->shared->isc_max_frame_size; + + if (adapter->dmac == 0) { /* Disabling it */ + IGC_WRITE_REG(hw, IGC_DMACR, reg); + return; + } else + device_printf(dev, "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 |= ((adapter->dmac * 5) >> 6); + else + reg |= (adapter->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); +} + +/********************************************************************* + * + * Initialize the hardware to a configuration as specified by the + * adapter structure. + * + **********************************************************************/ +static void +igc_reset(if_ctx_t ctx) +{ + device_t dev = iflib_get_dev(ctx); + struct igc_adapter *adapter = iflib_get_softc(ctx); + struct igc_hw *hw = &adapter->hw; + u16 rx_buffer_size; + u32 pba; + + INIT_DEBUGOUT("igc_reset: begin"); + /* Let the firmware know the OS is in control */ + igc_get_hw_control(adapter); + + /* + * 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; + + INIT_DEBUGOUT1("igc_reset: pba=%dK",pba); + + /* + * 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(adapter->hw.mac.max_frame_size, 1024); + /* 16-byte granularity */ + hw->fc.low_water = hw->fc.high_water - 16; + + if (adapter->fc) /* locally set flow control value? */ + hw->fc.requested_mode = adapter->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) { + device_printf(dev, "Hardware Initialization Failed\n"); + return; + } + + /* Setup DMA Coalescing */ + igc_init_dmac(adapter, pba); + + IGC_WRITE_REG(hw, IGC_VET, ETHERTYPE_VLAN); + igc_get_phy_info(hw); + igc_check_for_link(hw); +} + +/* + * Initialise the RSS mapping for NICs that support multiple transmit/ + * receive rings. + */ + +#define RSSKEYLEN 10 +static void +igc_initialize_rss_mapping(struct igc_adapter *adapter) +{ + struct igc_hw *hw = &adapter->hw; + int i; + int queue_id; + u32 reta; + u32 rss_key[RSSKEYLEN], mrqc, shift = 0; + + /* + * 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++) { +#ifdef RSS + queue_id = rss_get_indirection_to_bucket(i); + /* + * If we have more queues than buckets, we'll + * end up mapping buckets to a subset of the + * queues. + * + * If we have more buckets than queues, we'll + * end up instead assigning multiple buckets + * to queues. + * + * Both are suboptimal, but we need to handle + * the case so we don't go out of bounds + * indexing arrays and such. + */ + queue_id = queue_id % adapter->rx_num_queues; +#else + queue_id = (i % adapter->rx_num_queues); +#endif + /* 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; + } + } + + /* Now fill in hash table */ + + /* + * MRQC: Multiple Receive Queues Command + * Set queuing to RSS control, number depends on the device. + */ + mrqc = IGC_MRQC_ENABLE_RSS_4Q; + +#ifdef RSS + /* XXX ew typecasting */ + rss_getkey((uint8_t *) &rss_key); +#else + arc4rand(&rss_key, sizeof(rss_key), 0); +#endif + for (i = 0; i < RSSKEYLEN; 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_IPV4_UDP | + IGC_MRQC_RSS_FIELD_IPV6_UDP); + mrqc |=( IGC_MRQC_RSS_FIELD_IPV6_UDP_EX | + IGC_MRQC_RSS_FIELD_IPV6_TCP_EX); + + IGC_WRITE_REG(hw, IGC_MRQC, mrqc); +} + +/********************************************************************* + * + * Setup networking device structure and register interface media. + * + **********************************************************************/ +static int +igc_setup_interface(if_ctx_t ctx) +{ + struct ifnet *ifp = iflib_get_ifp(ctx); + struct igc_adapter *adapter = iflib_get_softc(ctx); + if_softc_ctx_t scctx = adapter->shared; + + INIT_DEBUGOUT("igc_setup_interface: begin"); + + /* Single Queue */ + if (adapter->tx_num_queues == 1) { + if_setsendqlen(ifp, scctx->isc_ntxd[0] - 1); + if_setsendqready(ifp); + } + + /* + * Specify the media types supported by this adapter and register + * callbacks to update media and link information + */ + ifmedia_add(adapter->media, IFM_ETHER | IFM_10_T, 0, NULL); + ifmedia_add(adapter->media, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL); + ifmedia_add(adapter->media, IFM_ETHER | IFM_100_TX, 0, NULL); + ifmedia_add(adapter->media, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL); + ifmedia_add(adapter->media, IFM_ETHER | IFM_1000_T | IFM_FDX, 0, NULL); + ifmedia_add(adapter->media, IFM_ETHER | IFM_1000_T, 0, NULL); + ifmedia_add(adapter->media, IFM_ETHER | IFM_2500_T, 0, NULL); + + ifmedia_add(adapter->media, IFM_ETHER | IFM_AUTO, 0, NULL); + ifmedia_set(adapter->media, IFM_ETHER | IFM_AUTO); + return (0); +} + +static int +igc_if_tx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs, int ntxqs, int ntxqsets) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + if_softc_ctx_t scctx = adapter->shared; + int error = IGC_SUCCESS; + struct igc_tx_queue *que; + int i, j; + + MPASS(adapter->tx_num_queues > 0); + MPASS(adapter->tx_num_queues == ntxqsets); + + /* First allocate the top level queue structs */ + if (!(adapter->tx_queues = + (struct igc_tx_queue *) malloc(sizeof(struct igc_tx_queue) * + adapter->tx_num_queues, M_DEVBUF, M_NOWAIT | M_ZERO))) { + device_printf(iflib_get_dev(ctx), "Unable to allocate queue memory\n"); + return(ENOMEM); + } + + for (i = 0, que = adapter->tx_queues; i < adapter->tx_num_queues; i++, que++) { + /* Set up some basics */ + + struct tx_ring *txr = &que->txr; + txr->adapter = que->adapter = adapter; + que->me = txr->me = i; + + /* Allocate report status array */ + if (!(txr->tx_rsq = (qidx_t *) malloc(sizeof(qidx_t) * scctx->isc_ntxd[0], M_DEVBUF, M_NOWAIT | M_ZERO))) { + device_printf(iflib_get_dev(ctx), "failed to allocate rs_idxs memory\n"); + error = ENOMEM; + goto fail; + } + for (j = 0; j < scctx->isc_ntxd[0]; j++) + txr->tx_rsq[j] = QIDX_INVALID; + /* get the virtual and physical address of the hardware queues */ + txr->tx_base = (struct igc_tx_desc *)vaddrs[i*ntxqs]; + txr->tx_paddr = paddrs[i*ntxqs]; + } + + if (bootverbose) + device_printf(iflib_get_dev(ctx), + "allocated for %d tx_queues\n", adapter->tx_num_queues); + return (0); +fail: + igc_if_queues_free(ctx); + return (error); +} + +static int +igc_if_rx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs, int nrxqs, int nrxqsets) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + int error = IGC_SUCCESS; + struct igc_rx_queue *que; + int i; + + MPASS(adapter->rx_num_queues > 0); + MPASS(adapter->rx_num_queues == nrxqsets); + + /* First allocate the top level queue structs */ + if (!(adapter->rx_queues = + (struct igc_rx_queue *) malloc(sizeof(struct igc_rx_queue) * + adapter->rx_num_queues, M_DEVBUF, M_NOWAIT | M_ZERO))) { + device_printf(iflib_get_dev(ctx), "Unable to allocate queue memory\n"); + error = ENOMEM; + goto fail; + } + + for (i = 0, que = adapter->rx_queues; i < nrxqsets; i++, que++) { + /* Set up some basics */ + struct rx_ring *rxr = &que->rxr; + rxr->adapter = que->adapter = adapter; + rxr->que = que; + que->me = rxr->me = i; + + /* get the virtual and physical address of the hardware queues */ + rxr->rx_base = (union igc_rx_desc_extended *)vaddrs[i*nrxqs]; + rxr->rx_paddr = paddrs[i*nrxqs]; + } + + if (bootverbose) + device_printf(iflib_get_dev(ctx), + "allocated for %d rx_queues\n", adapter->rx_num_queues); + + return (0); +fail: + igc_if_queues_free(ctx); + return (error); +} + +static void +igc_if_queues_free(if_ctx_t ctx) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + struct igc_tx_queue *tx_que = adapter->tx_queues; + struct igc_rx_queue *rx_que = adapter->rx_queues; + + if (tx_que != NULL) { + for (int i = 0; i < adapter->tx_num_queues; i++, tx_que++) { + struct tx_ring *txr = &tx_que->txr; + if (txr->tx_rsq == NULL) + break; + + free(txr->tx_rsq, M_DEVBUF); + txr->tx_rsq = NULL; + } + free(adapter->tx_queues, M_DEVBUF); + adapter->tx_queues = NULL; + } + + if (rx_que != NULL) { + free(adapter->rx_queues, M_DEVBUF); + adapter->rx_queues = NULL; + } + + igc_release_hw_control(adapter); + + if (adapter->mta != NULL) { + free(adapter->mta, M_DEVBUF); + } +} + +/********************************************************************* + * + * Enable transmit unit. + * + **********************************************************************/ +static void +igc_initialize_transmit_unit(if_ctx_t ctx) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + if_softc_ctx_t scctx = adapter->shared; + struct igc_tx_queue *que; + struct tx_ring *txr; + struct igc_hw *hw = &adapter->hw; + u32 tctl, txdctl = 0; + + INIT_DEBUGOUT("igc_initialize_transmit_unit: begin"); + + for (int i = 0; i < adapter->tx_num_queues; i++, txr++) { + u64 bus_addr; + caddr_t offp, endp; + + que = &adapter->tx_queues[i]; + txr = &que->txr; + bus_addr = txr->tx_paddr; + + /* Clear checksum offload context. */ + offp = (caddr_t)&txr->csum_flags; + endp = (caddr_t)(txr + 1); + bzero(offp, endp - offp); + + /* Base and Len of TX Ring */ + IGC_WRITE_REG(hw, IGC_TDLEN(i), + scctx->isc_ntxd[0] * sizeof(struct igc_tx_desc)); + IGC_WRITE_REG(hw, IGC_TDBAH(i), + (u32)(bus_addr >> 32)); + IGC_WRITE_REG(hw, IGC_TDBAL(i), + (u32)bus_addr); + /* Init the HEAD/TAIL indices */ + IGC_WRITE_REG(hw, IGC_TDT(i), 0); + IGC_WRITE_REG(hw, IGC_TDH(i), 0); + + HW_DEBUGOUT2("Base = %x, Length = %x\n", + IGC_READ_REG(&adapter->hw, IGC_TDBAL(i)), + IGC_READ_REG(&adapter->hw, IGC_TDLEN(i))); + + 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); + } + + /* Program the Transmit Control Register */ + tctl = IGC_READ_REG(&adapter->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(&adapter->hw, IGC_TCTL, tctl); +} + +/********************************************************************* + * + * Enable receive unit. + * + **********************************************************************/ + +static void +igc_initialize_receive_unit(if_ctx_t ctx) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + if_softc_ctx_t scctx = adapter->shared; + struct ifnet *ifp = iflib_get_ifp(ctx); + struct igc_hw *hw = &adapter->hw; + struct igc_rx_queue *que; + int i; + u32 psize, rctl, rxcsum, srrctl = 0; + + INIT_DEBUGOUT("igc_initialize_receive_units: begin"); + + /* + * 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); + + /* Do not store bad packets */ + rctl &= ~IGC_RCTL_SBP; + + /* Enable Long Packet receive */ + if (if_getmtu(ifp) > ETHERMTU) + rctl |= IGC_RCTL_LPE; + else + rctl &= ~IGC_RCTL_LPE; + + /* Strip the CRC */ + if (!igc_disable_crc_stripping) + 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); + if (if_getcapenable(ifp) & IFCAP_RXCSUM) { + rxcsum |= IGC_RXCSUM_CRCOFL; + if (adapter->tx_num_queues > 1) + rxcsum |= IGC_RXCSUM_PCSD; + else + rxcsum |= IGC_RXCSUM_IPPCSE; + } else { + if (adapter->tx_num_queues > 1) + rxcsum |= IGC_RXCSUM_PCSD; + else + rxcsum &= ~IGC_RXCSUM_TUOFL; + } + IGC_WRITE_REG(hw, IGC_RXCSUM, rxcsum); + + if (adapter->rx_num_queues > 1) + igc_initialize_rss_mapping(adapter); + + if (if_getmtu(ifp) > ETHERMTU) { + /* Set maximum packet len */ + if (adapter->rx_mbuf_sz <= 4096) { + srrctl |= 4096 >> IGC_SRRCTL_BSIZEPKT_SHIFT; + rctl |= IGC_RCTL_SZ_4096 | IGC_RCTL_BSEX; + } else if (adapter->rx_mbuf_sz > 4096) { + srrctl |= 8192 >> IGC_SRRCTL_BSIZEPKT_SHIFT; + rctl |= IGC_RCTL_SZ_8192 | IGC_RCTL_BSEX; + } + psize = scctx->isc_max_frame_size; + /* are we on a vlan? */ + if (ifp->if_vlantrunk != NULL) + psize += VLAN_TAG_SIZE; + IGC_WRITE_REG(&adapter->hw, IGC_RLPML, psize); + } else { + 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 ((adapter->rx_num_queues > 1) && + (adapter->fc == igc_fc_none || + adapter->fc == igc_fc_rx_pause)) { + srrctl |= IGC_SRRCTL_DROP_EN; + } + + /* Setup the Base and Length of the Rx Descriptor Rings */ + for (i = 0, que = adapter->rx_queues; i < adapter->rx_num_queues; i++, que++) { + struct rx_ring *rxr = &que->rxr; + u64 bus_addr = rxr->rx_paddr; + u32 rxdctl; + +#ifdef notyet + /* Configure for header split? -- ignore for now */ + rxr->hdr_split = igc_header_split; +#else + srrctl |= IGC_SRRCTL_DESCTYPE_ADV_ONEBUF; +#endif + + IGC_WRITE_REG(hw, IGC_RDLEN(i), + scctx->isc_nrxd[0] * sizeof(struct igc_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); + + return; +} + +static void +igc_if_vlan_register(if_ctx_t ctx, u16 vtag) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + u32 index, bit; + + index = (vtag >> 5) & 0x7F; + bit = vtag & 0x1F; + adapter->shadow_vfta[index] |= (1 << bit); + ++adapter->num_vlans; +} + +static void +igc_if_vlan_unregister(if_ctx_t ctx, u16 vtag) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + u32 index, bit; + + index = (vtag >> 5) & 0x7F; + bit = vtag & 0x1F; + adapter->shadow_vfta[index] &= ~(1 << bit); + --adapter->num_vlans; +} + +static void +igc_setup_vlan_hw_support(struct igc_adapter *adapter) +{ + struct igc_hw *hw = &adapter->hw; + u32 reg; + + /* + * We get here thru init_locked, meaning + * a soft reset, this has already cleared + * the VFTA and other state, so if there + * have been no vlan's registered do nothing. + */ + if (adapter->num_vlans == 0) + return; + + /* + * A soft reset zero's out the VFTA, so + * we need to repopulate it now. + */ + for (int i = 0; i < IGC_VFTA_SIZE; i++) + if (adapter->shadow_vfta[i] != 0) + IGC_WRITE_REG_ARRAY(hw, IGC_VFTA, + i, adapter->shadow_vfta[i]); + + reg = IGC_READ_REG(hw, IGC_CTRL); + reg |= IGC_CTRL_VME; + IGC_WRITE_REG(hw, IGC_CTRL, reg); + + /* Enable the Filter Table */ + reg = IGC_READ_REG(hw, IGC_RCTL); + reg &= ~IGC_RCTL_CFIEN; + reg |= IGC_RCTL_VFE; + IGC_WRITE_REG(hw, IGC_RCTL, reg); +} + +static void +igc_if_intr_enable(if_ctx_t ctx) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + struct igc_hw *hw = &adapter->hw; + u32 mask; + + if (__predict_true(adapter->intr_type == IFLIB_INTR_MSIX)) { + mask = (adapter->que_mask | adapter->link_mask); + 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); + } else + IGC_WRITE_REG(hw, IGC_IMS, IMS_ENABLE_MASK); + IGC_WRITE_FLUSH(hw); +} + +static void +igc_if_intr_disable(if_ctx_t ctx) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + struct igc_hw *hw = &adapter->hw; + + if (__predict_true(adapter->intr_type == IFLIB_INTR_MSIX)) { + 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); +} + +/* + * 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. + */ +static void +igc_get_hw_control(struct igc_adapter *adapter) +{ + u32 ctrl_ext; + + if (adapter->vf_ifp) + return; + + ctrl_ext = IGC_READ_REG(&adapter->hw, IGC_CTRL_EXT); + IGC_WRITE_REG(&adapter->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. + */ +static void +igc_release_hw_control(struct igc_adapter *adapter) +{ + u32 ctrl_ext; + + ctrl_ext = IGC_READ_REG(&adapter->hw, IGC_CTRL_EXT); + IGC_WRITE_REG(&adapter->hw, IGC_CTRL_EXT, + ctrl_ext & ~IGC_CTRL_EXT_DRV_LOAD); + return; +} + +static int +igc_is_valid_ether_addr(u8 *addr) +{ + char zero_addr[6] = { 0, 0, 0, 0, 0, 0 }; + + if ((addr[0] & 1) || (!bcmp(addr, zero_addr, ETHER_ADDR_LEN))) { + return (false); + } + + return (true); +} + +/* +** Parse the interface capabilities with regard +** to both system management and wake-on-lan for +** later use. +*/ +static void +igc_get_wakeup(if_ctx_t ctx) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + u16 eeprom_data = 0, apme_mask; + + apme_mask = IGC_WUC_APME; + eeprom_data = IGC_READ_REG(&adapter->hw, IGC_WUC); + + if (eeprom_data & apme_mask) + adapter->wol = IGC_WUFC_LNKC; +} + + +/* + * Enable PCI Wake On Lan capability + */ +static void +igc_enable_wakeup(if_ctx_t ctx) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + device_t dev = iflib_get_dev(ctx); + if_t ifp = iflib_get_ifp(ctx); + int error = 0; + u32 pmc, ctrl, rctl; + u16 status; + + if (pci_find_cap(dev, PCIY_PMG, &pmc) != 0) + return; + + /* + * Determine type of Wakeup: note that wol + * is set with all bits on by default. + */ + if ((if_getcapenable(ifp) & IFCAP_WOL_MAGIC) == 0) + adapter->wol &= ~IGC_WUFC_MAG; + + if ((if_getcapenable(ifp) & IFCAP_WOL_UCAST) == 0) + adapter->wol &= ~IGC_WUFC_EX; + + if ((if_getcapenable(ifp) & IFCAP_WOL_MCAST) == 0) + adapter->wol &= ~IGC_WUFC_MC; + else { + rctl = IGC_READ_REG(&adapter->hw, IGC_RCTL); + rctl |= IGC_RCTL_MPE; + IGC_WRITE_REG(&adapter->hw, IGC_RCTL, rctl); + } + + if (!(adapter->wol & (IGC_WUFC_EX | IGC_WUFC_MAG | IGC_WUFC_MC))) + goto pme; + + /* Advertise the wakeup capability */ + ctrl = IGC_READ_REG(&adapter->hw, IGC_CTRL); + ctrl |= IGC_CTRL_ADVD3WUC; + IGC_WRITE_REG(&adapter->hw, IGC_CTRL, ctrl); + + /* Enable wakeup by the MAC */ + IGC_WRITE_REG(&adapter->hw, IGC_WUC, IGC_WUC_PME_EN); + IGC_WRITE_REG(&adapter->hw, IGC_WUFC, adapter->wol); + +pme: + status = pci_read_config(dev, pmc + PCIR_POWER_STATUS, 2); + status &= ~(PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE); + if (!error && (if_getcapenable(ifp) & IFCAP_WOL)) + status |= PCIM_PSTAT_PME | PCIM_PSTAT_PMEENABLE; + pci_write_config(dev, pmc + PCIR_POWER_STATUS, status, 2); + + return; +} + +/********************************************************************** + * + * Update the board statistics counters. + * + **********************************************************************/ +static void +igc_update_stats_counters(struct igc_adapter *adapter) +{ + u64 prev_xoffrxc = adapter->stats.xoffrxc; + + adapter->stats.crcerrs += IGC_READ_REG(&adapter->hw, IGC_CRCERRS); + adapter->stats.mpc += IGC_READ_REG(&adapter->hw, IGC_MPC); + adapter->stats.scc += IGC_READ_REG(&adapter->hw, IGC_SCC); + adapter->stats.ecol += IGC_READ_REG(&adapter->hw, IGC_ECOL); + + adapter->stats.mcc += IGC_READ_REG(&adapter->hw, IGC_MCC); + adapter->stats.latecol += IGC_READ_REG(&adapter->hw, IGC_LATECOL); + adapter->stats.colc += IGC_READ_REG(&adapter->hw, IGC_COLC); + adapter->stats.colc += IGC_READ_REG(&adapter->hw, IGC_RERC); + adapter->stats.dc += IGC_READ_REG(&adapter->hw, IGC_DC); + adapter->stats.rlec += IGC_READ_REG(&adapter->hw, IGC_RLEC); + adapter->stats.xonrxc += IGC_READ_REG(&adapter->hw, IGC_XONRXC); + adapter->stats.xontxc += IGC_READ_REG(&adapter->hw, IGC_XONTXC); + adapter->stats.xoffrxc += IGC_READ_REG(&adapter->hw, IGC_XOFFRXC); + /* + * For watchdog management we need to know if we have been + * paused during the last interval, so capture that here. + */ + if (adapter->stats.xoffrxc != prev_xoffrxc) + adapter->shared->isc_pause_frames = 1; + adapter->stats.xofftxc += IGC_READ_REG(&adapter->hw, IGC_XOFFTXC); + adapter->stats.fcruc += IGC_READ_REG(&adapter->hw, IGC_FCRUC); + adapter->stats.prc64 += IGC_READ_REG(&adapter->hw, IGC_PRC64); + adapter->stats.prc127 += IGC_READ_REG(&adapter->hw, IGC_PRC127); + adapter->stats.prc255 += IGC_READ_REG(&adapter->hw, IGC_PRC255); + adapter->stats.prc511 += IGC_READ_REG(&adapter->hw, IGC_PRC511); + adapter->stats.prc1023 += IGC_READ_REG(&adapter->hw, IGC_PRC1023); + adapter->stats.prc1522 += IGC_READ_REG(&adapter->hw, IGC_PRC1522); + adapter->stats.tlpic += IGC_READ_REG(&adapter->hw, IGC_TLPIC); + adapter->stats.rlpic += IGC_READ_REG(&adapter->hw, IGC_RLPIC); + adapter->stats.gprc += IGC_READ_REG(&adapter->hw, IGC_GPRC); + adapter->stats.bprc += IGC_READ_REG(&adapter->hw, IGC_BPRC); + adapter->stats.mprc += IGC_READ_REG(&adapter->hw, IGC_MPRC); + adapter->stats.gptc += IGC_READ_REG(&adapter->hw, IGC_GPTC); + + /* For the 64-bit byte counters the low dword must be read first. */ + /* Both registers clear on the read of the high dword */ + + adapter->stats.gorc += IGC_READ_REG(&adapter->hw, IGC_GORCL) + + ((u64)IGC_READ_REG(&adapter->hw, IGC_GORCH) << 32); + adapter->stats.gotc += IGC_READ_REG(&adapter->hw, IGC_GOTCL) + + ((u64)IGC_READ_REG(&adapter->hw, IGC_GOTCH) << 32); + + adapter->stats.rnbc += IGC_READ_REG(&adapter->hw, IGC_RNBC); + adapter->stats.ruc += IGC_READ_REG(&adapter->hw, IGC_RUC); + adapter->stats.rfc += IGC_READ_REG(&adapter->hw, IGC_RFC); + adapter->stats.roc += IGC_READ_REG(&adapter->hw, IGC_ROC); + adapter->stats.rjc += IGC_READ_REG(&adapter->hw, IGC_RJC); + + adapter->stats.tor += IGC_READ_REG(&adapter->hw, IGC_TORH); + adapter->stats.tot += IGC_READ_REG(&adapter->hw, IGC_TOTH); + + adapter->stats.tpr += IGC_READ_REG(&adapter->hw, IGC_TPR); + adapter->stats.tpt += IGC_READ_REG(&adapter->hw, IGC_TPT); + adapter->stats.ptc64 += IGC_READ_REG(&adapter->hw, IGC_PTC64); + adapter->stats.ptc127 += IGC_READ_REG(&adapter->hw, IGC_PTC127); + adapter->stats.ptc255 += IGC_READ_REG(&adapter->hw, IGC_PTC255); + adapter->stats.ptc511 += IGC_READ_REG(&adapter->hw, IGC_PTC511); + adapter->stats.ptc1023 += IGC_READ_REG(&adapter->hw, IGC_PTC1023); + adapter->stats.ptc1522 += IGC_READ_REG(&adapter->hw, IGC_PTC1522); + adapter->stats.mptc += IGC_READ_REG(&adapter->hw, IGC_MPTC); + adapter->stats.bptc += IGC_READ_REG(&adapter->hw, IGC_BPTC); + + /* Interrupt Counts */ + adapter->stats.iac += IGC_READ_REG(&adapter->hw, IGC_IAC); + adapter->stats.rxdmtc += IGC_READ_REG(&adapter->hw, IGC_RXDMTC); + + adapter->stats.algnerrc += IGC_READ_REG(&adapter->hw, IGC_ALGNERRC); + adapter->stats.tncrs += IGC_READ_REG(&adapter->hw, IGC_TNCRS); + adapter->stats.htdpmc += IGC_READ_REG(&adapter->hw, IGC_HTDPMC); + adapter->stats.tsctc += IGC_READ_REG(&adapter->hw, IGC_TSCTC); +} + +static uint64_t +igc_if_get_counter(if_ctx_t ctx, ift_counter cnt) +{ + struct igc_adapter *adapter = iflib_get_softc(ctx); + struct ifnet *ifp = iflib_get_ifp(ctx); + + switch (cnt) { + case IFCOUNTER_COLLISIONS: + return (adapter->stats.colc); + case IFCOUNTER_IERRORS: + return (adapter->dropped_pkts + adapter->stats.rxerrc + + adapter->stats.crcerrs + adapter->stats.algnerrc + + adapter->stats.ruc + adapter->stats.roc + + adapter->stats.mpc + adapter->stats.htdpmc); + case IFCOUNTER_OERRORS: + return (adapter->stats.ecol + adapter->stats.latecol + + adapter->watchdog_events); + default: + return (if_get_counter_default(ifp, cnt)); + } +} + +/* igc_if_needs_restart - Tell iflib when the driver needs to be reinitialized + * @ctx: iflib context + * @event: event code to check + * + * Defaults to returning true for unknown events. + * + * @returns true if iflib needs to reinit the interface + */ +static bool +igc_if_needs_restart(if_ctx_t ctx __unused, enum iflib_restart_event event) +{ + switch (event) { + case IFLIB_RESTART_VLAN_CONFIG: + default: + return (true); + } +} + +/* Export a single 32-bit register via a read-only sysctl. */ +static int +igc_sysctl_reg_handler(SYSCTL_HANDLER_ARGS) +{ + struct igc_adapter *adapter; + u_int val; + + adapter = oidp->oid_arg1; + val = IGC_READ_REG(&adapter->hw, oidp->oid_arg2); + return (sysctl_handle_int(oidp, &val, 0, req)); +} + +/* + * Add sysctl variables, one per statistic, to the system. + */ +static void +igc_add_hw_stats(struct igc_adapter *adapter) +{ + device_t dev = iflib_get_dev(adapter->ctx); + struct igc_tx_queue *tx_que = adapter->tx_queues; + struct igc_rx_queue *rx_que = adapter->rx_queues; + + struct sysctl_ctx_list *ctx = device_get_sysctl_ctx(dev); + struct sysctl_oid *tree = device_get_sysctl_tree(dev); + struct sysctl_oid_list *child = SYSCTL_CHILDREN(tree); + struct igc_hw_stats *stats = &adapter->stats; + + struct sysctl_oid *stat_node, *queue_node, *int_node; + struct sysctl_oid_list *stat_list, *queue_list, *int_list; + +#define QUEUE_NAME_LEN 32 + char namebuf[QUEUE_NAME_LEN]; + + /* Driver Statistics */ + SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "dropped", + CTLFLAG_RD, &adapter->dropped_pkts, + "Driver dropped packets"); + SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "link_irq", + CTLFLAG_RD, &adapter->link_irq, + "Link MSI-X IRQ Handled"); + SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_overruns", + CTLFLAG_RD, &adapter->rx_overruns, + "RX overruns"); + SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "watchdog_timeouts", + CTLFLAG_RD, &adapter->watchdog_events, + "Watchdog timeouts"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "device_control", + CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, + adapter, IGC_CTRL, igc_sysctl_reg_handler, "IU", + "Device Control Register"); + SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rx_control", + CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, + adapter, IGC_RCTL, igc_sysctl_reg_handler, "IU", + "Receiver Control Register"); + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "fc_high_water", + CTLFLAG_RD, &adapter->hw.fc.high_water, 0, + "Flow Control High Watermark"); + SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "fc_low_water", + CTLFLAG_RD, &adapter->hw.fc.low_water, 0, + "Flow Control Low Watermark"); + + for (int i = 0; i < adapter->tx_num_queues; i++, tx_que++) { + struct tx_ring *txr = &tx_que->txr; + snprintf(namebuf, QUEUE_NAME_LEN, "queue_tx_%d", i); + queue_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, namebuf, + CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "TX Queue Name"); + queue_list = SYSCTL_CHILDREN(queue_node); + + SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "txd_head", + CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, adapter, + IGC_TDH(txr->me), igc_sysctl_reg_handler, "IU", + "Transmit Descriptor Head"); + SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "txd_tail", + CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, adapter, + IGC_TDT(txr->me), igc_sysctl_reg_handler, "IU", + "Transmit Descriptor Tail"); + SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "tx_irq", + CTLFLAG_RD, &txr->tx_irq, + "Queue MSI-X Transmit Interrupts"); + } + + for (int j = 0; j < adapter->rx_num_queues; j++, rx_que++) { + struct rx_ring *rxr = &rx_que->rxr; + snprintf(namebuf, QUEUE_NAME_LEN, "queue_rx_%d", j); + queue_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, namebuf, + CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "RX Queue Name"); + queue_list = SYSCTL_CHILDREN(queue_node); + + SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "rxd_head", + CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, adapter, + IGC_RDH(rxr->me), igc_sysctl_reg_handler, "IU", + "Receive Descriptor Head"); + SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "rxd_tail", + CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, adapter, + IGC_RDT(rxr->me), igc_sysctl_reg_handler, "IU", + "Receive Descriptor Tail"); + SYSCTL_ADD_ULONG(ctx, queue_list, OID_AUTO, "rx_irq", + CTLFLAG_RD, &rxr->rx_irq, + "Queue MSI-X Receive Interrupts"); + } + + /* MAC stats get their own sub node */ + + stat_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "mac_stats", + CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Statistics"); + stat_list = SYSCTL_CHILDREN(stat_node); + + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "excess_coll", + CTLFLAG_RD, &stats->ecol, + "Excessive collisions"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "single_coll", + CTLFLAG_RD, &stats->scc, + "Single collisions"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "multiple_coll", + CTLFLAG_RD, &stats->mcc, + "Multiple collisions"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "late_coll", + CTLFLAG_RD, &stats->latecol, + "Late collisions"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "collision_count", + CTLFLAG_RD, &stats->colc, + "Collision Count"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "symbol_errors", + CTLFLAG_RD, &adapter->stats.symerrs, + "Symbol Errors"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "sequence_errors", + CTLFLAG_RD, &adapter->stats.sec, + "Sequence Errors"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "defer_count", + CTLFLAG_RD, &adapter->stats.dc, + "Defer Count"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "missed_packets", + CTLFLAG_RD, &adapter->stats.mpc, + "Missed Packets"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "recv_no_buff", + CTLFLAG_RD, &adapter->stats.rnbc, + "Receive No Buffers"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "recv_undersize", + CTLFLAG_RD, &adapter->stats.ruc, + "Receive Undersize"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "recv_fragmented", + CTLFLAG_RD, &adapter->stats.rfc, + "Fragmented Packets Received "); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "recv_oversize", + CTLFLAG_RD, &adapter->stats.roc, + "Oversized Packets Received"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "recv_jabber", + CTLFLAG_RD, &adapter->stats.rjc, + "Recevied Jabber"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "recv_errs", + CTLFLAG_RD, &adapter->stats.rxerrc, + "Receive Errors"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "crc_errs", + CTLFLAG_RD, &adapter->stats.crcerrs, + "CRC errors"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "alignment_errs", + CTLFLAG_RD, &adapter->stats.algnerrc, + "Alignment Errors"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "xon_recvd", + CTLFLAG_RD, &adapter->stats.xonrxc, + "XON Received"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "xon_txd", + CTLFLAG_RD, &adapter->stats.xontxc, + "XON Transmitted"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "xoff_recvd", + CTLFLAG_RD, &adapter->stats.xoffrxc, + "XOFF Received"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "xoff_txd", + CTLFLAG_RD, &adapter->stats.xofftxc, + "XOFF Transmitted"); + + /* Packet Reception Stats */ + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "total_pkts_recvd", + CTLFLAG_RD, &adapter->stats.tpr, + "Total Packets Received "); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "good_pkts_recvd", + CTLFLAG_RD, &adapter->stats.gprc, + "Good Packets Received"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "bcast_pkts_recvd", + CTLFLAG_RD, &adapter->stats.bprc, + "Broadcast Packets Received"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "mcast_pkts_recvd", + CTLFLAG_RD, &adapter->stats.mprc, + "Multicast Packets Received"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_64", + CTLFLAG_RD, &adapter->stats.prc64, + "64 byte frames received "); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_65_127", + CTLFLAG_RD, &adapter->stats.prc127, + "65-127 byte frames received"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_128_255", + CTLFLAG_RD, &adapter->stats.prc255, + "128-255 byte frames received"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_256_511", + CTLFLAG_RD, &adapter->stats.prc511, + "256-511 byte frames received"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_512_1023", + CTLFLAG_RD, &adapter->stats.prc1023, + "512-1023 byte frames received"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_1024_1522", + CTLFLAG_RD, &adapter->stats.prc1522, + "1023-1522 byte frames received"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "good_octets_recvd", + CTLFLAG_RD, &adapter->stats.gorc, + "Good Octets Received"); + + /* Packet Transmission Stats */ + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "good_octets_txd", + CTLFLAG_RD, &adapter->stats.gotc, + "Good Octets Transmitted"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "total_pkts_txd", + CTLFLAG_RD, &adapter->stats.tpt, + "Total Packets Transmitted"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "good_pkts_txd", + CTLFLAG_RD, &adapter->stats.gptc, + "Good Packets Transmitted"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "bcast_pkts_txd", + CTLFLAG_RD, &adapter->stats.bptc, + "Broadcast Packets Transmitted"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "mcast_pkts_txd", + CTLFLAG_RD, &adapter->stats.mptc, + "Multicast Packets Transmitted"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_64", + CTLFLAG_RD, &adapter->stats.ptc64, + "64 byte frames transmitted "); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_65_127", + CTLFLAG_RD, &adapter->stats.ptc127, + "65-127 byte frames transmitted"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_128_255", + CTLFLAG_RD, &adapter->stats.ptc255, + "128-255 byte frames transmitted"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_256_511", + CTLFLAG_RD, &adapter->stats.ptc511, + "256-511 byte frames transmitted"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_512_1023", + CTLFLAG_RD, &adapter->stats.ptc1023, + "512-1023 byte frames transmitted"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_1024_1522", + CTLFLAG_RD, &adapter->stats.ptc1522, + "1024-1522 byte frames transmitted"); + SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tso_txd", + CTLFLAG_RD, &adapter->stats.tsctc, + "TSO Contexts Transmitted"); + + /* Interrupt Stats */ + + int_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "interrupts", + CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Interrupt Statistics"); + int_list = SYSCTL_CHILDREN(int_node); + + SYSCTL_ADD_UQUAD(ctx, int_list, OID_AUTO, "asserts", + CTLFLAG_RD, &adapter->stats.iac, + "Interrupt Assertion Count"); + + SYSCTL_ADD_UQUAD(ctx, int_list, OID_AUTO, "rx_desc_min_thresh", + CTLFLAG_RD, &adapter->stats.rxdmtc, + "Rx Desc Min Thresh Count"); +} + +/********************************************************************** + * + * This routine provides a way to dump out the adapter eeprom, + * often a useful debug/service tool. This only dumps the first + * 32 words, stuff that matters is in that extent. + * + **********************************************************************/ +static int +igc_sysctl_nvm_info(SYSCTL_HANDLER_ARGS) +{ + struct igc_adapter *adapter = (struct igc_adapter *)arg1; + int error; + int result; + + result = -1; + error = sysctl_handle_int(oidp, &result, 0, req); + + if (error || !req->newptr) + return (error); + + /* + * This value will cause a hex dump of the + * first 32 16-bit words of the EEPROM to + * the screen. + */ + if (result == 1) + igc_print_nvm_info(adapter); + + return (error); +} + +static void +igc_print_nvm_info(struct igc_adapter *adapter) +{ + u16 eeprom_data; + int i, j, row = 0; + + /* Its a bit crude, but it gets the job done */ + printf("\nInterface EEPROM Dump:\n"); + printf("Offset\n0x0000 "); + for (i = 0, j = 0; i < 32; i++, j++) { + if (j == 8) { /* Make the offset block */ + j = 0; ++row; + printf("\n0x00%x0 ",row); + } + igc_read_nvm(&adapter->hw, i, 1, &eeprom_data); + printf("%04x ", eeprom_data); + } + printf("\n"); +} + +static int +igc_sysctl_int_delay(SYSCTL_HANDLER_ARGS) +{ + struct igc_int_delay_info *info; + struct igc_adapter *adapter; + u32 regval; + int error, usecs, ticks; + + info = (struct igc_int_delay_info *) arg1; + usecs = info->value; + error = sysctl_handle_int(oidp, &usecs, 0, req); + if (error != 0 || req->newptr == NULL) + return (error); + if (usecs < 0 || usecs > IGC_TICKS_TO_USECS(65535)) + return (EINVAL); + info->value = usecs; + ticks = IGC_USECS_TO_TICKS(usecs); + if (info->offset == IGC_ITR) /* units are 256ns here */ + ticks *= 4; + + adapter = info->adapter; + + regval = IGC_READ_OFFSET(&adapter->hw, info->offset); + regval = (regval & ~0xffff) | (ticks & 0xffff); + /* Handle a few special cases. */ + switch (info->offset) { + case IGC_RDTR: + break; + case IGC_TIDV: + if (ticks == 0) { + adapter->txd_cmd &= ~IGC_TXD_CMD_IDE; + /* Don't write 0 into the TIDV register. */ + regval++; + } else + adapter->txd_cmd |= IGC_TXD_CMD_IDE; + break; + } + IGC_WRITE_OFFSET(&adapter->hw, info->offset, regval); + return (0); +} + +static void +igc_add_int_delay_sysctl(struct igc_adapter *adapter, const char *name, + const char *description, struct igc_int_delay_info *info, + int offset, int value) +{ + info->adapter = adapter; + info->offset = offset; + info->value = value; + SYSCTL_ADD_PROC(device_get_sysctl_ctx(adapter->dev), + SYSCTL_CHILDREN(device_get_sysctl_tree(adapter->dev)), + OID_AUTO, name, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, + info, 0, igc_sysctl_int_delay, "I", description); +} + +/* + * Set flow control using sysctl: + * Flow control values: + * 0 - off + * 1 - rx pause + * 2 - tx pause + * 3 - full + */ +static int +igc_set_flowcntl(SYSCTL_HANDLER_ARGS) +{ + int error; + static int input = 3; /* default is full */ + struct igc_adapter *adapter = (struct igc_adapter *) arg1; + + error = sysctl_handle_int(oidp, &input, 0, req); + + if ((error) || (req->newptr == NULL)) + return (error); + + if (input == adapter->fc) /* no change? */ + return (error); + + switch (input) { + case igc_fc_rx_pause: + case igc_fc_tx_pause: + case igc_fc_full: + case igc_fc_none: + adapter->hw.fc.requested_mode = input; + adapter->fc = input; + break; + default: + /* Do nothing */ + return (error); + } + + adapter->hw.fc.current_mode = adapter->hw.fc.requested_mode; + igc_force_mac_fc(&adapter->hw); + return (error); +} + +/* + * Manage Energy Efficient Ethernet: + * Control values: + * 0/1 - enabled/disabled + */ +static int +igc_sysctl_eee(SYSCTL_HANDLER_ARGS) +{ + struct igc_adapter *adapter = (struct igc_adapter *) arg1; + int error, value; + + value = adapter->hw.dev_spec._i225.eee_disable; + error = sysctl_handle_int(oidp, &value, 0, req); + if (error || req->newptr == NULL) + return (error); + + adapter->hw.dev_spec._i225.eee_disable = (value != 0); + igc_if_init(adapter->ctx); + + return (0); +} + +static int +igc_sysctl_debug_info(SYSCTL_HANDLER_ARGS) +{ + struct igc_adapter *adapter; + int error; + int result; + + result = -1; + error = sysctl_handle_int(oidp, &result, 0, req); + + if (error || !req->newptr) + return (error); + + if (result == 1) { + adapter = (struct igc_adapter *) arg1; + igc_print_debug_info(adapter); + } + + return (error); +} + +static int +igc_get_rs(SYSCTL_HANDLER_ARGS) +{ + struct igc_adapter *adapter = (struct igc_adapter *) arg1; + int error; + int result; + + result = 0; + error = sysctl_handle_int(oidp, &result, 0, req); + + if (error || !req->newptr || result != 1) + return (error); + igc_dump_rs(adapter); + + return (error); +} + +static void +igc_if_debug(if_ctx_t ctx) +{ + igc_dump_rs(iflib_get_softc(ctx)); +} + +/* + * This routine is meant to be fluid, add whatever is + * needed for debugging a problem. -jfv + */ +static void +igc_print_debug_info(struct igc_adapter *adapter) +{ + device_t dev = iflib_get_dev(adapter->ctx); + struct ifnet *ifp = iflib_get_ifp(adapter->ctx); + struct tx_ring *txr = &adapter->tx_queues->txr; + struct rx_ring *rxr = &adapter->rx_queues->rxr; + + if (if_getdrvflags(ifp) & IFF_DRV_RUNNING) + printf("Interface is RUNNING "); + else + printf("Interface is NOT RUNNING\n"); + + if (if_getdrvflags(ifp) & IFF_DRV_OACTIVE) + printf("and INACTIVE\n"); + else + printf("and ACTIVE\n"); + + for (int i = 0; i < adapter->tx_num_queues; i++, txr++) { + device_printf(dev, "TX Queue %d ------\n", i); + device_printf(dev, "hw tdh = %d, hw tdt = %d\n", + IGC_READ_REG(&adapter->hw, IGC_TDH(i)), + IGC_READ_REG(&adapter->hw, IGC_TDT(i))); + + } + for (int j=0; j < adapter->rx_num_queues; j++, rxr++) { + device_printf(dev, "RX Queue %d ------\n", j); + device_printf(dev, "hw rdh = %d, hw rdt = %d\n", + IGC_READ_REG(&adapter->hw, IGC_RDH(j)), + IGC_READ_REG(&adapter->hw, IGC_RDT(j))); + } +} diff --git a/sys/dev/igc/igc_api.h b/sys/dev/igc/igc_api.h new file mode 100644 --- /dev/null +++ b/sys/dev/igc/igc_api.h @@ -0,0 +1,58 @@ +/*- + * 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 "igc_hw.h" + +extern void igc_init_function_pointers_i225(struct igc_hw *hw); + +s32 igc_set_mac_type(struct igc_hw *hw); +s32 igc_setup_init_funcs(struct igc_hw *hw, bool init_device); +s32 igc_init_mac_params(struct igc_hw *hw); +s32 igc_init_nvm_params(struct igc_hw *hw); +s32 igc_init_phy_params(struct igc_hw *hw); +s32 igc_get_bus_info(struct igc_hw *hw); +void igc_clear_vfta(struct igc_hw *hw); +void igc_write_vfta(struct igc_hw *hw, u32 offset, u32 value); +s32 igc_force_mac_fc(struct igc_hw *hw); +s32 igc_check_for_link(struct igc_hw *hw); +s32 igc_reset_hw(struct igc_hw *hw); +s32 igc_init_hw(struct igc_hw *hw); +s32 igc_setup_link(struct igc_hw *hw); +s32 igc_get_speed_and_duplex(struct igc_hw *hw, u16 *speed, u16 *duplex); +s32 igc_disable_pcie_master(struct igc_hw *hw); +void igc_config_collision_dist(struct igc_hw *hw); +int igc_rar_set(struct igc_hw *hw, u8 *addr, u32 index); +u32 igc_hash_mc_addr(struct igc_hw *hw, u8 *mc_addr); +void igc_update_mc_addr_list(struct igc_hw *hw, u8 *mc_addr_list, + u32 mc_addr_count); +s32 igc_check_reset_block(struct igc_hw *hw); +s32 igc_get_cable_length(struct igc_hw *hw); +s32 igc_validate_mdi_setting(struct igc_hw *hw); +s32 igc_read_phy_reg(struct igc_hw *hw, u32 offset, u16 *data); +s32 igc_write_phy_reg(struct igc_hw *hw, u32 offset, u16 data); +s32 igc_get_phy_info(struct igc_hw *hw); +void igc_release_phy(struct igc_hw *hw); +s32 igc_acquire_phy(struct igc_hw *hw); +s32 igc_phy_hw_reset(struct igc_hw *hw); +s32 igc_phy_commit(struct igc_hw *hw); +void igc_power_up_phy(struct igc_hw *hw); +void igc_power_down_phy(struct igc_hw *hw); +s32 igc_read_mac_addr(struct igc_hw *hw); +s32 igc_read_pba_string(struct igc_hw *hw, u8 *pba_num, u32 pba_num_size); +void igc_reload_nvm(struct igc_hw *hw); +s32 igc_update_nvm_checksum(struct igc_hw *hw); +s32 igc_validate_nvm_checksum(struct igc_hw *hw); +s32 igc_read_nvm(struct igc_hw *hw, u16 offset, u16 words, u16 *data); +s32 igc_write_nvm(struct igc_hw *hw, u16 offset, u16 words, u16 *data); +s32 igc_set_d3_lplu_state(struct igc_hw *hw, bool active); +s32 igc_set_d0_lplu_state(struct igc_hw *hw, bool active); + +#endif /* _IGC_API_H_ */ diff --git a/sys/dev/igc/igc_api.c b/sys/dev/igc/igc_api.c new file mode 100644 --- /dev/null +++ b/sys/dev/igc/igc_api.c @@ -0,0 +1,735 @@ +/*- + * Copyright 2021 Intel Corp + * Copyright 2021 Rubicon Communications, LLC (Netgate) + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "igc_api.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. + **/ +s32 igc_init_mac_params(struct igc_hw *hw) +{ + s32 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. + **/ +s32 igc_init_nvm_params(struct igc_hw *hw) +{ + s32 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. + **/ +s32 igc_init_phy_params(struct igc_hw *hw) +{ + s32 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()). + **/ +s32 igc_set_mac_type(struct igc_hw *hw) +{ + struct igc_mac_info *mac = &hw->mac; + s32 ret_val = IGC_SUCCESS; + + DEBUGFUNC("igc_set_mac_type"); + + switch (hw->device_id) { + case IGC_DEV_ID_I225_LM: + case IGC_DEV_ID_I225_V: + case IGC_DEV_ID_I225_K: + case IGC_DEV_ID_I225_I: + case IGC_DEV_ID_I220_V: + case IGC_DEV_ID_I225_K2: + case IGC_DEV_ID_I225_LMVP: + case IGC_DEV_ID_I225_IT: + case IGC_DEV_ID_I226_LM: + case IGC_DEV_ID_I226_V: + case IGC_DEV_ID_I226_IT: + case IGC_DEV_ID_I221_V: + case IGC_DEV_ID_I226_BLANK_NVM: + case IGC_DEV_ID_I225_BLANK_NVM: + 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. + **/ +s32 igc_setup_init_funcs(struct igc_hw *hw, bool init_device) +{ + s32 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_get_bus_info - Obtain bus information for adapter + * @hw: pointer to the HW structure + * + * This will obtain information about the HW bus for which the + * adapter is attached and stores it in the hw structure. This is a + * function pointer entry point called by drivers. + **/ +s32 igc_get_bus_info(struct igc_hw *hw) +{ + if (hw->mac.ops.get_bus_info) + return hw->mac.ops.get_bus_info(hw); + + return IGC_SUCCESS; +} + +/** + * igc_clear_vfta - Clear VLAN filter table + * @hw: pointer to the HW structure + * + * This clears the VLAN filter table on the adapter. This is a function + * pointer entry point called by drivers. + **/ +void igc_clear_vfta(struct igc_hw *hw) +{ + if (hw->mac.ops.clear_vfta) + hw->mac.ops.clear_vfta(hw); +} + +/** + * igc_write_vfta - Write value to VLAN filter table + * @hw: pointer to the HW structure + * @offset: the 32-bit offset in which to write the value to. + * @value: the 32-bit value to write at location offset. + * + * This writes a 32-bit value to a 32-bit offset in the VLAN filter + * table. This is a function pointer entry point called by drivers. + **/ +void igc_write_vfta(struct igc_hw *hw, u32 offset, u32 value) +{ + if (hw->mac.ops.write_vfta) + hw->mac.ops.write_vfta(hw, offset, value); +} + +/** + * 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, u8 *mc_addr_list, + u32 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_force_mac_fc - Force MAC flow control + * @hw: pointer to the HW structure + * + * Force the MAC's flow control settings. Currently no func pointer exists + * and all implementations are handled in the generic version of this + * function. + **/ +s32 igc_force_mac_fc(struct igc_hw *hw) +{ + return igc_force_mac_fc_generic(hw); +} + +/** + * 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. + **/ +s32 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. + **/ +s32 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. + **/ +s32 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_setup_link - Configures link and flow control + * @hw: pointer to the HW structure + * + * This configures link and flow control settings for the adapter. This + * is a function pointer entry point called by drivers. While modules can + * also call this, they probably call their own version of this function. + **/ +s32 igc_setup_link(struct igc_hw *hw) +{ + if (hw->mac.ops.setup_link) + return hw->mac.ops.setup_link(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. + **/ +s32 igc_get_speed_and_duplex(struct igc_hw *hw, u16 *speed, u16 *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_disable_pcie_master - Disable PCI-Express master access + * @hw: pointer to the HW structure + * + * Disables PCI-Express master access and verifies there are no pending + * requests. Currently no func pointer exists and all implementations are + * handled in the generic version of this function. + **/ +s32 igc_disable_pcie_master(struct igc_hw *hw) +{ + return igc_disable_pcie_master_generic(hw); +} + +/** + * igc_config_collision_dist - 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(struct igc_hw *hw) +{ + if (hw->mac.ops.config_collision_dist) + hw->mac.ops.config_collision_dist(hw); +} + +/** + * 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, u8 *addr, u32 index) +{ + if (hw->mac.ops.rar_set) + return hw->mac.ops.rar_set(hw, addr, index); + + return IGC_SUCCESS; +} + +/** + * igc_validate_mdi_setting - Ensures valid MDI/MDIX SW state + * @hw: pointer to the HW structure + * + * Ensures that the MDI/MDIX SW state is valid. + **/ +s32 igc_validate_mdi_setting(struct igc_hw *hw) +{ + if (hw->mac.ops.validate_mdi_setting) + return hw->mac.ops.validate_mdi_setting(hw); + + return IGC_SUCCESS; +} + +/** + * igc_hash_mc_addr - Determines address location in multicast table + * @hw: pointer to the HW structure + * @mc_addr: Multicast address to hash. + * + * This hashes an address to determine its location in the multicast + * table. Currently no func pointer exists and all implementations + * are handled in the generic version of this function. + **/ +u32 igc_hash_mc_addr(struct igc_hw *hw, u8 *mc_addr) +{ + return igc_hash_mc_addr_generic(hw, mc_addr); +} + +/** + * 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. + **/ +s32 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_read_phy_reg - Reads PHY register + * @hw: pointer to the HW structure + * @offset: the register to read + * @data: the buffer to store the 16-bit read. + * + * Reads the PHY register and returns the value in data. + * This is a function pointer entry point called by drivers. + **/ +s32 igc_read_phy_reg(struct igc_hw *hw, u32 offset, u16 *data) +{ + if (hw->phy.ops.read_reg) + return hw->phy.ops.read_reg(hw, offset, data); + + return IGC_SUCCESS; +} + +/** + * igc_write_phy_reg - Writes PHY register + * @hw: pointer to the HW structure + * @offset: the register to write + * @data: the value to write. + * + * Writes the PHY register at offset with the value in data. + * This is a function pointer entry point called by drivers. + **/ +s32 igc_write_phy_reg(struct igc_hw *hw, u32 offset, u16 data) +{ + if (hw->phy.ops.write_reg) + return hw->phy.ops.write_reg(hw, offset, data); + + return IGC_SUCCESS; +} + +/** + * igc_release_phy - Generic release PHY + * @hw: pointer to the HW structure + * + * Return if silicon family does not require a semaphore when accessing the + * PHY. + **/ +void igc_release_phy(struct igc_hw *hw) +{ + if (hw->phy.ops.release) + hw->phy.ops.release(hw); +} + +/** + * igc_acquire_phy - Generic acquire PHY + * @hw: pointer to the HW structure + * + * Return success if silicon family does not require a semaphore when + * accessing the PHY. + **/ +s32 igc_acquire_phy(struct igc_hw *hw) +{ + if (hw->phy.ops.acquire) + return hw->phy.ops.acquire(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. + **/ +s32 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. + **/ +s32 igc_phy_hw_reset(struct igc_hw *hw) +{ + if (hw->phy.ops.reset) + return hw->phy.ops.reset(hw); + + return IGC_SUCCESS; +} + +/** + * igc_phy_commit - Soft PHY reset + * @hw: pointer to the HW structure + * + * Performs a soft PHY reset on those that apply. This is a function pointer + * entry point called by drivers. + **/ +s32 igc_phy_commit(struct igc_hw *hw) +{ + if (hw->phy.ops.commit) + return hw->phy.ops.commit(hw); + + return IGC_SUCCESS; +} + +/** + * igc_set_d0_lplu_state - Sets low power link up state for D0 + * @hw: pointer to the HW structure + * @active: boolean used to enable/disable lplu + * + * Success returns 0, Failure returns 1 + * + * The low power link up (lplu) state is set to the power management level D0 + * and SmartSpeed is disabled when active is true, else clear lplu for D0 + * and enable Smartspeed. LPLU and Smartspeed are mutually exclusive. LPLU + * is used during Dx states where the power conservation is most important. + * During driver activity, SmartSpeed should be enabled so performance is + * maintained. This is a function pointer entry point called by drivers. + **/ +s32 igc_set_d0_lplu_state(struct igc_hw *hw, bool active) +{ + if (hw->phy.ops.set_d0_lplu_state) + return hw->phy.ops.set_d0_lplu_state(hw, active); + + return IGC_SUCCESS; +} + +/** + * igc_set_d3_lplu_state - Sets low power link up state for D3 + * @hw: pointer to the HW structure + * @active: boolean used to enable/disable lplu + * + * Success returns 0, Failure returns 1 + * + * The low power link up (lplu) state is set to the power management level D3 + * and SmartSpeed is disabled when active is true, else clear lplu for D3 + * and enable Smartspeed. LPLU and Smartspeed are mutually exclusive. LPLU + * is used during Dx states where the power conservation is most important. + * During driver activity, SmartSpeed should be enabled so performance is + * maintained. This is a function pointer entry point called by drivers. + **/ +s32 igc_set_d3_lplu_state(struct igc_hw *hw, bool active) +{ + if (hw->phy.ops.set_d3_lplu_state) + return hw->phy.ops.set_d3_lplu_state(hw, active); + + 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. + **/ +s32 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_read_pba_string - Read device part number string + * @hw: pointer to the HW structure + * @pba_num: pointer to device part number + * @pba_num_size: size of part number buffer + * + * Reads the product board assembly (PBA) number from the EEPROM and stores + * the value in pba_num. + * Currently no func pointer exists and all implementations are handled in the + * generic version of this function. + **/ +s32 igc_read_pba_string(struct igc_hw *hw, u8 *pba_num, u32 pba_num_size) +{ + return igc_read_pba_string_generic(hw, pba_num, pba_num_size); +} + +/** + * 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. + **/ +s32 igc_validate_nvm_checksum(struct igc_hw *hw) +{ + if (hw->nvm.ops.validate) + return hw->nvm.ops.validate(hw); + + return -IGC_ERR_CONFIG; +} + +/** + * igc_update_nvm_checksum - Updates NVM (EEPROM) checksum + * @hw: pointer to the HW structure + * + * Updates the NVM checksum. Currently no func pointer exists and all + * implementations are handled in the generic version of this function. + **/ +s32 igc_update_nvm_checksum(struct igc_hw *hw) +{ + if (hw->nvm.ops.update) + return hw->nvm.ops.update(hw); + + return -IGC_ERR_CONFIG; +} + +/** + * igc_reload_nvm - 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(struct igc_hw *hw) +{ + if (hw->nvm.ops.reload) + hw->nvm.ops.reload(hw); +} + +/** + * igc_read_nvm - Reads NVM (EEPROM) + * @hw: pointer to the HW structure + * @offset: the word offset to read + * @words: number of 16-bit words to read + * @data: pointer to the properly sized buffer for the data. + * + * Reads 16-bit chunks of data from the NVM (EEPROM). This is a function + * pointer entry point called by drivers. + **/ +s32 igc_read_nvm(struct igc_hw *hw, u16 offset, u16 words, u16 *data) +{ + if (hw->nvm.ops.read) + return hw->nvm.ops.read(hw, offset, words, data); + + return -IGC_ERR_CONFIG; +} + +/** + * igc_write_nvm - Writes to NVM (EEPROM) + * @hw: pointer to the HW structure + * @offset: the word offset to read + * @words: number of 16-bit words to write + * @data: pointer to the properly sized buffer for the data. + * + * Writes 16-bit chunks of data to the NVM (EEPROM). This is a function + * pointer entry point called by drivers. + **/ +s32 igc_write_nvm(struct igc_hw *hw, u16 offset, u16 words, u16 *data) +{ + if (hw->nvm.ops.write) + return hw->nvm.ops.write(hw, offset, words, data); + + return IGC_SUCCESS; +} + +/** + * igc_power_up_phy - Restores link in case of PHY power down + * @hw: pointer to the HW structure + * + * The phy may be powered down to save power, to turn off link when the + * driver is unloaded, or wake on lan is not enabled (among others). + **/ +void igc_power_up_phy(struct igc_hw *hw) +{ + if (hw->phy.ops.power_up) + hw->phy.ops.power_up(hw); + + igc_setup_link(hw); +} + +/** + * igc_power_down_phy - Power down PHY + * @hw: pointer to the HW structure + * + * The phy may be powered down to save power, to turn off link when the + * driver is unloaded, or wake on lan is not enabled (among others). + **/ +void igc_power_down_phy(struct igc_hw *hw) +{ + if (hw->phy.ops.power_down) + hw->phy.ops.power_down(hw); +} + diff --git a/sys/dev/igc/igc_base.h b/sys/dev/igc/igc_base.h new file mode 100644 --- /dev/null +++ b/sys/dev/igc/igc_base.h @@ -0,0 +1,131 @@ +/*- + * 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 */ +s32 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); +s32 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 { + __le64 buffer_addr; /* Address of descriptor's data buf */ + __le32 cmd_type_len; + __le32 olinfo_status; + } read; + struct { + __le64 rsvd; /* Reserved */ + __le32 nxtseq_seed; + __le32 status; + } wb; +}; + +/* Context descriptors */ +struct igc_adv_tx_context_desc { + __le32 vlan_macip_lens; + union { + __le32 launch_time; + __le32 seqnum_seed; + }; + __le32 type_tucmd_mlhl; + __le32 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 { + __le64 pkt_addr; /* Packet buffer address */ + __le64 hdr_addr; /* Header buffer address */ + } read; + struct { + struct { + union { + __le32 data; + struct { + __le16 pkt_info; /*RSS type, Pkt type*/ + /* Split Header, header buffer len */ + __le16 hdr_info; + } hs_rss; + } lo_dword; + union { + __le32 rss; /* RSS Hash */ + struct { + __le16 ip_id; /* IP id */ + __le16 csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + __le32 status_error; /* ext status/error */ + __le16 length; /* Packet length */ + __le16 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/igc/igc_base.c b/sys/dev/igc/igc_base.c new file mode 100644 --- /dev/null +++ b/sys/dev/igc/igc_base.c @@ -0,0 +1,188 @@ +/*- + * Copyright 2021 Intel Corp + * Copyright 2021 Rubicon Communications, LLC (Netgate) + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "igc_hw.h" +#include "igc_i225.h" +#include "igc_mac.h" +#include "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. + **/ +s32 igc_acquire_phy_base(struct igc_hw *hw) +{ + u16 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) +{ + u16 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. + **/ +s32 igc_init_hw_base(struct igc_hw *hw) +{ + struct igc_mac_info *mac = &hw->mac; + s32 ret_val; + u16 i, rar_count = mac->rar_entry_count; + + 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; +} + +/** + * igc_rx_fifo_flush_base - Clean Rx FIFO after Rx enable + * @hw: pointer to the HW structure + * + * After Rx enable, if manageability is enabled then there is likely some + * bad data at the start of the FIFO and possibly in the DMA FIFO. This + * function clears the FIFOs and flushes any packets that came in as Rx was + * being enabled. + **/ +void igc_rx_fifo_flush_base(struct igc_hw *hw) +{ + u32 rctl, rlpml, rxdctl[4], rfctl, temp_rctl, rx_enabled; + int i, ms_wait; + + DEBUGFUNC("igc_rx_fifo_flush_base"); + + /* disable IPv6 options as per hardware errata */ + rfctl = IGC_READ_REG(hw, IGC_RFCTL); + rfctl |= IGC_RFCTL_IPV6_EX_DIS; + IGC_WRITE_REG(hw, IGC_RFCTL, rfctl); + + if (!(IGC_READ_REG(hw, IGC_MANC) & IGC_MANC_RCV_TCO_EN)) + return; + + /* Disable all Rx queues */ + for (i = 0; i < 4; i++) { + rxdctl[i] = IGC_READ_REG(hw, IGC_RXDCTL(i)); + IGC_WRITE_REG(hw, IGC_RXDCTL(i), + rxdctl[i] & ~IGC_RXDCTL_QUEUE_ENABLE); + } + /* Poll all queues to verify they have shut down */ + for (ms_wait = 0; ms_wait < 10; ms_wait++) { + msec_delay(1); + rx_enabled = 0; + for (i = 0; i < 4; i++) + rx_enabled |= IGC_READ_REG(hw, IGC_RXDCTL(i)); + if (!(rx_enabled & IGC_RXDCTL_QUEUE_ENABLE)) + break; + } + + if (ms_wait == 10) + DEBUGOUT("Queue disable timed out after 10ms\n"); + + /* Clear RLPML, RCTL.SBP, RFCTL.LEF, and set RCTL.LPE so that all + * incoming packets are rejected. Set enable and wait 2ms so that + * any packet that was coming in as RCTL.EN was set is flushed + */ + IGC_WRITE_REG(hw, IGC_RFCTL, rfctl & ~IGC_RFCTL_LEF); + + rlpml = IGC_READ_REG(hw, IGC_RLPML); + IGC_WRITE_REG(hw, IGC_RLPML, 0); + + rctl = IGC_READ_REG(hw, IGC_RCTL); + temp_rctl = rctl & ~(IGC_RCTL_EN | IGC_RCTL_SBP); + temp_rctl |= IGC_RCTL_LPE; + + IGC_WRITE_REG(hw, IGC_RCTL, temp_rctl); + IGC_WRITE_REG(hw, IGC_RCTL, temp_rctl | IGC_RCTL_EN); + IGC_WRITE_FLUSH(hw); + msec_delay(2); + + /* Enable Rx queues that were previously enabled and restore our + * previous state + */ + for (i = 0; i < 4; i++) + IGC_WRITE_REG(hw, IGC_RXDCTL(i), rxdctl[i]); + IGC_WRITE_REG(hw, IGC_RCTL, rctl); + IGC_WRITE_FLUSH(hw); + + IGC_WRITE_REG(hw, IGC_RLPML, rlpml); + IGC_WRITE_REG(hw, IGC_RFCTL, rfctl); + + /* Flush receive errors generated by workaround */ + IGC_READ_REG(hw, IGC_ROC); + IGC_READ_REG(hw, IGC_RNBC); + IGC_READ_REG(hw, IGC_MPC); +} diff --git a/sys/dev/igc/igc_defines.h b/sys/dev/igc/igc_defines.h new file mode 100644 --- /dev/null +++ b/sys/dev/igc/igc_defines.h @@ -0,0 +1,1347 @@ +/*- + * 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_FORCE_SMBUS 0x00000800 /* Force SMBus mode */ +#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) + +#if !defined(EXTERNAL_RELEASE) || defined(IGCE_MQ) +#define IGC_MRQC_ENABLE_RSS_2Q 0x00000001 +#endif /* !EXTERNAL_RELEASE || IGCE_MQ */ +#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 0x1 +#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 indication for I225 */ +#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 0xE +#define IGC_LEDCTL_MODE_LED_OFF 0xF + +/* 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 MJUM9BYTES +#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 + +#ifndef ETH_ADDR_LEN +#define ETH_ADDR_LEN 6 +#endif + +#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 +#ifndef ERROR_REPORT +#define ERROR_REPORT(fmt) do { } while (0) +#endif /* ERROR_REPORT */ +#endif /* _IGC_DEFINES_H_ */ diff --git a/sys/dev/igc/igc_hw.h b/sys/dev/igc/igc_hw.h new file mode 100644 --- /dev/null +++ b/sys/dev/igc/igc_hw.h @@ -0,0 +1,548 @@ +/*- + * 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 "igc_osdep.h" +#include "igc_regs.h" +#include "igc_defines.h" + +struct igc_hw; + +#define IGC_DEV_ID_I225_LM 0x15F2 +#define IGC_DEV_ID_I225_V 0x15F3 +#define IGC_DEV_ID_I225_K 0x3100 +#define IGC_DEV_ID_I225_I 0x15F8 +#define IGC_DEV_ID_I220_V 0x15F7 +#define IGC_DEV_ID_I225_K2 0x3101 +#define IGC_DEV_ID_I225_LMVP 0x5502 +#define IGC_DEV_ID_I226_K 0x5504 +#define IGC_DEV_ID_I225_IT 0x0D9F +#define IGC_DEV_ID_I226_LM 0x125B +#define IGC_DEV_ID_I226_V 0x125C +#define IGC_DEV_ID_I226_IT 0x125D +#define IGC_DEV_ID_I221_V 0x125E +#define IGC_DEV_ID_I226_BLANK_NVM 0x125F +#define IGC_DEV_ID_I225_BLANK_NVM 0x15FD + +#define IGC_REVISION_0 0 +#define IGC_REVISION_1 1 +#define IGC_REVISION_2 2 +#define IGC_REVISION_3 3 +#define IGC_REVISION_4 4 + +#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 +}; + +#define __le16 u16 +#define __le32 u32 +#define __le64 u64 +/* Receive Descriptor */ +struct igc_rx_desc { + __le64 buffer_addr; /* Address of the descriptor's data buffer */ + __le16 length; /* Length of data DMAed into data buffer */ + __le16 csum; /* Packet checksum */ + u8 status; /* Descriptor status */ + u8 errors; /* Descriptor Errors */ + __le16 special; +}; + +/* Receive Descriptor - Extended */ +union igc_rx_desc_extended { + struct { + __le64 buffer_addr; + __le64 reserved; + } read; + struct { + struct { + __le32 mrq; /* Multiple Rx Queues */ + union { + __le32 rss; /* RSS Hash */ + struct { + __le16 ip_id; /* IP id */ + __le16 csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + __le32 status_error; /* ext status/error */ + __le16 length; + __le16 vlan; /* VLAN tag */ + } upper; + } wb; /* writeback */ +}; + +#define MAX_PS_BUFFERS 4 + +/* Number of packet split data buffers (not including the header buffer) */ +#define PS_PAGE_BUFFERS (MAX_PS_BUFFERS - 1) + +/* Receive Descriptor - Packet Split */ +union igc_rx_desc_packet_split { + struct { + /* one buffer for protocol header(s), three data buffers */ + __le64 buffer_addr[MAX_PS_BUFFERS]; + } read; + struct { + struct { + __le32 mrq; /* Multiple Rx Queues */ + union { + __le32 rss; /* RSS Hash */ + struct { + __le16 ip_id; /* IP id */ + __le16 csum; /* Packet Checksum */ + } csum_ip; + } hi_dword; + } lower; + struct { + __le32 status_error; /* ext status/error */ + __le16 length0; /* length of buffer 0 */ + __le16 vlan; /* VLAN tag */ + } middle; + struct { + __le16 header_status; + /* length of buffers 1-3 */ + __le16 length[PS_PAGE_BUFFERS]; + } upper; + __le64 reserved; + } wb; /* writeback */ +}; + +/* Transmit Descriptor */ +struct igc_tx_desc { + __le64 buffer_addr; /* Address of the descriptor's data buffer */ + union { + __le32 data; + struct { + __le16 length; /* Data buffer length */ + u8 cso; /* Checksum offset */ + u8 cmd; /* Descriptor control */ + } flags; + } lower; + union { + __le32 data; + struct { + u8 status; /* Descriptor status */ + u8 css; /* Checksum start */ + __le16 special; + } fields; + } upper; +}; + +/* Offload Context Descriptor */ +struct igc_context_desc { + union { + __le32 ip_config; + struct { + u8 ipcss; /* IP checksum start */ + u8 ipcso; /* IP checksum offset */ + __le16 ipcse; /* IP checksum end */ + } ip_fields; + } lower_setup; + union { + __le32 tcp_config; + struct { + u8 tucss; /* TCP checksum start */ + u8 tucso; /* TCP checksum offset */ + __le16 tucse; /* TCP checksum end */ + } tcp_fields; + } upper_setup; + __le32 cmd_and_length; + union { + __le32 data; + struct { + u8 status; /* Descriptor status */ + u8 hdr_len; /* Header length */ + __le16 mss; /* Maximum segment size */ + } fields; + } tcp_seg_setup; +}; + +/* Offload data descriptor */ +struct igc_data_desc { + __le64 buffer_addr; /* Address of the descriptor's buffer address */ + union { + __le32 data; + struct { + __le16 length; /* Data buffer length */ + u8 typ_len_ext; + u8 cmd; + } flags; + } lower; + union { + __le32 data; + struct { + u8 status; /* Descriptor status */ + u8 popts; /* Packet Options */ + __le16 special; + } fields; + } upper; +}; + +/* Statistics counters collected by the MAC */ +struct igc_hw_stats { + u64 crcerrs; + u64 algnerrc; + u64 symerrs; + u64 rxerrc; + u64 mpc; + u64 scc; + u64 ecol; + u64 mcc; + u64 latecol; + u64 colc; + u64 dc; + u64 tncrs; + u64 sec; + u64 rlec; + u64 xonrxc; + u64 xontxc; + u64 xoffrxc; + u64 xofftxc; + u64 fcruc; + u64 prc64; + u64 prc127; + u64 prc255; + u64 prc511; + u64 prc1023; + u64 prc1522; + u64 tlpic; + u64 rlpic; + u64 gprc; + u64 bprc; + u64 mprc; + u64 gptc; + u64 gorc; + u64 gotc; + u64 rnbc; + u64 ruc; + u64 rfc; + u64 roc; + u64 rjc; + u64 mgprc; + u64 mgpdc; + u64 mgptc; + u64 tor; + u64 tot; + u64 tpr; + u64 tpt; + u64 ptc64; + u64 ptc127; + u64 ptc255; + u64 ptc511; + u64 ptc1023; + u64 ptc1522; + u64 mptc; + u64 bptc; + u64 tsctc; + u64 iac; + u64 rxdmtc; + u64 htdpmc; + u64 rpthc; + u64 hgptc; + u64 hgorc; + u64 hgotc; + u64 lenerrs; + u64 scvpc; + u64 hrmpc; + u64 doosync; + u64 o2bgptc; + u64 o2bspc; + u64 b2ospc; + u64 b2ogprc; +}; + +#include "igc_mac.h" +#include "igc_phy.h" +#include "igc_nvm.h" + +/* Function pointers for the MAC. */ +struct igc_mac_operations { + s32 (*init_params)(struct igc_hw *); + s32 (*check_for_link)(struct igc_hw *); + void (*clear_hw_cntrs)(struct igc_hw *); + void (*clear_vfta)(struct igc_hw *); + s32 (*get_bus_info)(struct igc_hw *); + void (*set_lan_id)(struct igc_hw *); + s32 (*get_link_up_info)(struct igc_hw *, u16 *, u16 *); + void (*update_mc_addr_list)(struct igc_hw *, u8 *, u32); + s32 (*reset_hw)(struct igc_hw *); + s32 (*init_hw)(struct igc_hw *); + s32 (*setup_link)(struct igc_hw *); + s32 (*setup_physical_interface)(struct igc_hw *); + void (*write_vfta)(struct igc_hw *, u32, u32); + void (*config_collision_dist)(struct igc_hw *); + int (*rar_set)(struct igc_hw *, u8*, u32); + s32 (*read_mac_addr)(struct igc_hw *); + s32 (*validate_mdi_setting)(struct igc_hw *); + s32 (*acquire_swfw_sync)(struct igc_hw *, u16); + void (*release_swfw_sync)(struct igc_hw *, u16); +}; + +/* 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 { + s32 (*init_params)(struct igc_hw *); + s32 (*acquire)(struct igc_hw *); + s32 (*check_reset_block)(struct igc_hw *); + s32 (*commit)(struct igc_hw *); + s32 (*force_speed_duplex)(struct igc_hw *); + s32 (*get_info)(struct igc_hw *); + s32 (*set_page)(struct igc_hw *, u16); + s32 (*read_reg)(struct igc_hw *, u32, u16 *); + s32 (*read_reg_locked)(struct igc_hw *, u32, u16 *); + s32 (*read_reg_page)(struct igc_hw *, u32, u16 *); + void (*release)(struct igc_hw *); + s32 (*reset)(struct igc_hw *); + s32 (*set_d0_lplu_state)(struct igc_hw *, bool); + s32 (*set_d3_lplu_state)(struct igc_hw *, bool); + s32 (*write_reg)(struct igc_hw *, u32, u16); + s32 (*write_reg_locked)(struct igc_hw *, u32, u16); + s32 (*write_reg_page)(struct igc_hw *, u32, u16); + void (*power_up)(struct igc_hw *); + void (*power_down)(struct igc_hw *); +}; + +/* Function pointers for the NVM. */ +struct igc_nvm_operations { + s32 (*init_params)(struct igc_hw *); + s32 (*acquire)(struct igc_hw *); + s32 (*read)(struct igc_hw *, u16, u16, u16 *); + void (*release)(struct igc_hw *); + void (*reload)(struct igc_hw *); + s32 (*update)(struct igc_hw *); + s32 (*validate)(struct igc_hw *); + s32 (*write)(struct igc_hw *, u16, u16, u16 *); +}; + +struct igc_info { + s32 (*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; + u8 addr[ETH_ADDR_LEN]; + u8 perm_addr[ETH_ADDR_LEN]; + + enum igc_mac_type type; + + u32 mc_filter_type; + + u16 current_ifs_val; + u16 ifs_max_val; + u16 ifs_min_val; + u16 ifs_ratio; + u16 ifs_step_size; + u16 mta_reg_count; + u16 uta_reg_count; + + /* Maximum size of the MTA register table in all supported adapters */ +#define MAX_MTA_REG 128 + u32 mta_shadow[MAX_MTA_REG]; + u16 rar_entry_count; + + u8 forced_speed_duplex; + + bool asf_firmware_present; + bool autoneg; + bool get_link_status; + u32 max_frame_size; +}; + +struct igc_phy_info { + struct igc_phy_operations ops; + enum igc_phy_type type; + + enum igc_smart_speed smart_speed; + + u32 addr; + u32 id; + u32 reset_delay_us; /* in usec */ + u32 revision; + + enum igc_media_type media_type; + + u16 autoneg_advertised; + u16 autoneg_mask; + + u8 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; + + u16 word_size; + u16 delay_usec; + u16 address_bits; + u16 opcode_bits; + u16 page_size; +}; + +struct igc_bus_info { + enum igc_bus_type type; + enum igc_bus_speed speed; + enum igc_bus_width width; + + u16 func; + u16 pci_cmd_word; +}; + +struct igc_fc_info { + u32 high_water; /* Flow control high-water mark */ + u32 low_water; /* Flow control low-water mark */ + u16 pause_time; /* Flow control pause timer */ + u16 refresh_time; /* Flow control refresh timer */ + bool send_xon; /* Flow control send XON */ + bool strict_ieee; /* Strict IEEE mode */ + enum igc_fc_mode current_mode; /* FC mode in effect */ + enum igc_fc_mode requested_mode; /* FC mode requested by caller */ +}; + +struct igc_dev_spec_i225 { + bool eee_disable; + bool clear_semaphore_once; + u32 mtu; +}; + +struct igc_hw { + void *back; + + u8 *hw_addr; + u8 *flash_address; + unsigned long io_base; + + 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; + + u16 device_id; + u16 subsystem_vendor_id; + u16 subsystem_device_id; + u16 vendor_id; + + u8 revision_id; +}; + +#include "igc_i225.h" +#include "igc_base.h" + +/* These functions must be implemented by drivers */ +s32 igc_read_pcie_cap_reg(struct igc_hw *hw, u32 reg, u16 *value); +s32 igc_write_pcie_cap_reg(struct igc_hw *hw, u32 reg, u16 *value); +void igc_read_pci_cfg(struct igc_hw *hw, u32 reg, u16 *value); +void igc_write_pci_cfg(struct igc_hw *hw, u32 reg, u16 *value); + +#endif diff --git a/sys/dev/igc/igc_i225.h b/sys/dev/igc/igc_i225.h new file mode 100644 --- /dev/null +++ b/sys/dev/igc/igc_i225.h @@ -0,0 +1,112 @@ +/*- + * 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_ + +bool igc_get_flash_presence_i225(struct igc_hw *hw); +s32 igc_update_flash_i225(struct igc_hw *hw); +s32 igc_update_nvm_checksum_i225(struct igc_hw *hw); +s32 igc_validate_nvm_checksum_i225(struct igc_hw *hw); +s32 igc_write_nvm_srwr_i225(struct igc_hw *hw, u16 offset, + u16 words, u16 *data); +s32 igc_read_nvm_srrd_i225(struct igc_hw *hw, u16 offset, + u16 words, u16 *data); +s32 igc_set_flsw_flash_burst_counter_i225(struct igc_hw *hw, + u32 burst_counter); +s32 igc_write_erase_flash_command_i225(struct igc_hw *hw, u32 opcode, + u32 address); +s32 igc_check_for_link_i225(struct igc_hw *hw); +s32 igc_acquire_swfw_sync_i225(struct igc_hw *hw, u16 mask); +void igc_release_swfw_sync_i225(struct igc_hw *hw, u16 mask); +s32 igc_init_hw_i225(struct igc_hw *hw); +s32 igc_setup_copper_link_i225(struct igc_hw *hw); +s32 igc_set_d0_lplu_state_i225(struct igc_hw *hw, bool active); +s32 igc_set_d3_lplu_state_i225(struct igc_hw *hw, bool active); +s32 igc_set_eee_i225(struct igc_hw *hw, bool adv2p5G, bool adv1G, + bool adv100M); + +#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 diff --git a/sys/dev/igc/igc_i225.c b/sys/dev/igc/igc_i225.c new file mode 100644 --- /dev/null +++ b/sys/dev/igc/igc_i225.c @@ -0,0 +1,1232 @@ +/*- + * Copyright 2021 Intel Corp + * Copyright 2021 Rubicon Communications, LLC (Netgate) + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "igc_api.h" + +static s32 igc_init_nvm_params_i225(struct igc_hw *hw); +static s32 igc_init_mac_params_i225(struct igc_hw *hw); +static s32 igc_init_phy_params_i225(struct igc_hw *hw); +static s32 igc_reset_hw_i225(struct igc_hw *hw); +static s32 igc_acquire_nvm_i225(struct igc_hw *hw); +static void igc_release_nvm_i225(struct igc_hw *hw); +static s32 igc_get_hw_semaphore_i225(struct igc_hw *hw); +static s32 __igc_write_nvm_srwr(struct igc_hw *hw, u16 offset, u16 words, + u16 *data); +static s32 igc_pool_flash_update_done_i225(struct igc_hw *hw); + +/** + * igc_init_nvm_params_i225 - Init NVM func ptrs. + * @hw: pointer to the HW structure + **/ +static s32 igc_init_nvm_params_i225(struct igc_hw *hw) +{ + struct igc_nvm_info *nvm = &hw->nvm; + u32 eecd = IGC_READ_REG(hw, IGC_EECD); + u16 size; + + DEBUGFUNC("igc_init_nvm_params_i225"); + + size = (u16)((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 + **/ +static s32 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 + **/ +static s32 igc_init_phy_params_i225(struct igc_hw *hw) +{ + struct igc_phy_info *phy = &hw->phy; + s32 ret_val = IGC_SUCCESS; + u32 ctrl_ext; + + 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. + **/ +static s32 igc_reset_hw_i225(struct igc_hw *hw) +{ + u32 ctrl; + s32 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). + */ +static s32 igc_acquire_nvm_i225(struct igc_hw *hw) +{ + s32 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. + */ +static 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. + */ +s32 igc_acquire_swfw_sync_i225(struct igc_hw *hw, u16 mask) +{ + u32 swfw_sync; + u32 swmask = mask; + u32 fwmask = mask << 16; + s32 ret_val = IGC_SUCCESS; + s32 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_irq(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, u16 mask) +{ + u32 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. + */ +s32 igc_setup_copper_link_i225(struct igc_hw *hw) +{ + u32 phpm_reg; + s32 ret_val; + u32 ctrl; + + 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 + */ +static s32 igc_get_hw_semaphore_i225(struct igc_hw *hw) +{ + u32 swsm; + s32 timeout = hw->nvm.word_size + 1; + s32 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; + + usec_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; + + usec_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; + + usec_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. + */ +s32 igc_read_nvm_srrd_i225(struct igc_hw *hw, u16 offset, u16 words, + u16 *data) +{ + s32 status = IGC_SUCCESS; + u16 i, count; + + 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. + */ +s32 igc_write_nvm_srwr_i225(struct igc_hw *hw, u16 offset, u16 words, + u16 *data) +{ + s32 status = IGC_SUCCESS; + u16 i, count; + + 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. + */ +static s32 __igc_write_nvm_srwr(struct igc_hw *hw, u16 offset, u16 words, + u16 *data) +{ + struct igc_nvm_info *nvm = &hw->nvm; + u32 i, k, eewr = 0; + u32 attempts = 100000; + s32 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; + } + usec_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. + */ +s32 igc_validate_nvm_checksum_i225(struct igc_hw *hw) +{ + s32 status = IGC_SUCCESS; + s32 (*read_op_ptr)(struct igc_hw *, u16, u16, u16 *); + + 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. + */ +s32 igc_update_nvm_checksum_i225(struct igc_hw *hw) +{ + s32 ret_val; + u16 checksum = 0; + u16 i, nvm_data; + + 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 = (u16)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) +{ + u32 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 + */ +s32 igc_set_flsw_flash_burst_counter_i225(struct igc_hw *hw, + u32 burst_counter) +{ + s32 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 + */ +s32 igc_write_erase_flash_command_i225(struct igc_hw *hw, u32 opcode, + u32 address) +{ + u32 flswctl = 0; + s32 timeout = IGC_NVM_GRANT_ATTEMPTS; + s32 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; + usec_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 + */ +s32 igc_update_flash_i225(struct igc_hw *hw) +{ + u16 current_offset_data = 0; + u32 block_sw_protect = 1; + u16 base_address = 0x0; + u32 i, fw_valid_bit; + u16 current_offset; + s32 ret_val = 0; + u32 flup; + + 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; + + usec_delay(1000); + } + } +out: + return ret_val; +} + +/* igc_pool_flash_update_done_i225 - Pool FLUDONE status. + * @hw: pointer to the HW structure + */ +s32 igc_pool_flash_update_done_i225(struct igc_hw *hw) +{ + s32 ret_val = -IGC_ERR_NVM; + u32 i, reg; + + 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; + } + usec_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. + */ +static s32 igc_set_ltr_i225(struct igc_hw *hw, bool link) +{ + u16 speed, duplex; + u32 tw_system, ltrc, ltrv, ltr_min, ltr_max, scale_min, scale_max; + s32 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. + */ +s32 igc_check_for_link_i225(struct igc_hw *hw) +{ + struct igc_mac_info *mac = &hw->mac; + s32 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. + */ +s32 igc_init_hw_i225(struct igc_hw *hw) +{ + s32 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. + */ +s32 igc_set_d0_lplu_state_i225(struct igc_hw *hw, bool active) +{ + u32 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. + */ +s32 igc_set_d3_lplu_state_i225(struct igc_hw *hw, bool active) +{ + u32 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. + * + **/ +s32 igc_set_eee_i225(struct igc_hw *hw, bool adv2p5G, bool adv1G, + bool adv100M) +{ + u32 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)) { + u32 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/igc/igc_mac.h b/sys/dev/igc/igc_mac.h new file mode 100644 --- /dev/null +++ b/sys/dev/igc/igc_mac.h @@ -0,0 +1,48 @@ +/*- + * 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 *hw); +void igc_null_mac_generic(struct igc_hw *hw); +s32 igc_null_ops_generic(struct igc_hw *hw); +s32 igc_null_link_info(struct igc_hw *hw, u16 *s, u16 *d); +bool igc_null_mng_mode(struct igc_hw *hw); +void igc_null_update_mc(struct igc_hw *hw, u8 *h, u32 a); +void igc_null_write_vfta(struct igc_hw *hw, u32 a, u32 b); +int igc_null_rar_set(struct igc_hw *hw, u8 *h, u32 a); +s32 igc_check_for_copper_link_generic(struct igc_hw *hw); +s32 igc_config_fc_after_link_up_generic(struct igc_hw *hw); +s32 igc_disable_pcie_master_generic(struct igc_hw *hw); +s32 igc_force_mac_fc_generic(struct igc_hw *hw); +s32 igc_get_auto_rd_done_generic(struct igc_hw *hw); +s32 igc_get_bus_info_pcie_generic(struct igc_hw *hw); +void igc_set_lan_id_single_port(struct igc_hw *hw); +s32 igc_get_hw_semaphore_generic(struct igc_hw *hw); +s32 igc_get_speed_and_duplex_copper_generic(struct igc_hw *hw, u16 *speed, + u16 *duplex); +void igc_update_mc_addr_list_generic(struct igc_hw *hw, + u8 *mc_addr_list, u32 mc_addr_count); +int igc_rar_set_generic(struct igc_hw *hw, u8 *addr, u32 index); +s32 igc_set_fc_watermarks_generic(struct igc_hw *hw); +s32 igc_setup_link_generic(struct igc_hw *hw); +s32 igc_validate_mdi_setting_crossover_generic(struct igc_hw *hw); + +u32 igc_hash_mc_addr_generic(struct igc_hw *hw, u8 *mc_addr); + +void igc_clear_hw_cntrs_base_generic(struct igc_hw *hw); +void igc_clear_vfta_generic(struct igc_hw *hw); +void igc_init_rx_addrs_generic(struct igc_hw *hw, u16 rar_count); +void igc_pcix_mmrbc_workaround_generic(struct igc_hw *hw); +void igc_put_hw_semaphore_generic(struct igc_hw *hw); +s32 igc_check_alt_mac_addr_generic(struct igc_hw *hw); +void igc_set_pcie_no_snoop_generic(struct igc_hw *hw, u32 no_snoop); +void igc_write_vfta_generic(struct igc_hw *hw, u32 offset, u32 value); + +#endif diff --git a/sys/dev/igc/igc_mac.c b/sys/dev/igc/igc_mac.c new file mode 100644 --- /dev/null +++ b/sys/dev/igc/igc_mac.c @@ -0,0 +1,1050 @@ +/*- + * Copyright 2021 Intel Corp + * Copyright 2021 Rubicon Communications, LLC (Netgate) + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "igc_api.h" + +static void igc_config_collision_dist_generic(struct igc_hw *hw); + +/** + * 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 + **/ +s32 igc_null_ops_generic(struct igc_hw IGC_UNUSEDARG *hw) +{ + DEBUGFUNC("igc_null_ops_generic"); + return IGC_SUCCESS; +} + +/** + * igc_null_mac_generic - No-op function, return void + * @hw: pointer to the HW structure + **/ +void igc_null_mac_generic(struct igc_hw IGC_UNUSEDARG *hw) +{ + DEBUGFUNC("igc_null_mac_generic"); + return; +} + +/** + * igc_null_link_info - No-op function, return 0 + * @hw: pointer to the HW structure + * @s: dummy variable + * @d: dummy variable + **/ +s32 igc_null_link_info(struct igc_hw IGC_UNUSEDARG *hw, + u16 IGC_UNUSEDARG *s, u16 IGC_UNUSEDARG *d) +{ + DEBUGFUNC("igc_null_link_info"); + return IGC_SUCCESS; +} + +/** + * igc_null_mng_mode - No-op function, return false + * @hw: pointer to the HW structure + **/ +bool igc_null_mng_mode(struct igc_hw IGC_UNUSEDARG *hw) +{ + DEBUGFUNC("igc_null_mng_mode"); + return false; +} + +/** + * igc_null_update_mc - No-op function, return void + * @hw: pointer to the HW structure + * @h: dummy variable + * @a: dummy variable + **/ +void igc_null_update_mc(struct igc_hw IGC_UNUSEDARG *hw, + u8 IGC_UNUSEDARG *h, u32 IGC_UNUSEDARG a) +{ + DEBUGFUNC("igc_null_update_mc"); + return; +} + +/** + * igc_null_write_vfta - No-op function, return void + * @hw: pointer to the HW structure + * @a: dummy variable + * @b: dummy variable + **/ +void igc_null_write_vfta(struct igc_hw IGC_UNUSEDARG *hw, + u32 IGC_UNUSEDARG a, u32 IGC_UNUSEDARG b) +{ + DEBUGFUNC("igc_null_write_vfta"); + return; +} + +/** + * igc_null_rar_set - No-op function, return 0 + * @hw: pointer to the HW structure + * @h: dummy variable + * @a: dummy variable + **/ +int igc_null_rar_set(struct igc_hw IGC_UNUSEDARG *hw, + u8 IGC_UNUSEDARG *h, u32 IGC_UNUSEDARG a) +{ + DEBUGFUNC("igc_null_rar_set"); + return IGC_SUCCESS; +} + +/** + * igc_set_lan_id_single_port - Set LAN id for a single port device + * @hw: pointer to the HW structure + * + * Sets the LAN function id to zero for a single port device. + **/ +void igc_set_lan_id_single_port(struct igc_hw *hw) +{ + struct igc_bus_info *bus = &hw->bus; + + bus->func = 0; +} + +/** + * igc_clear_vfta_generic - Clear VLAN filter table + * @hw: pointer to the HW structure + * + * Clears the register array which contains the VLAN filter table by + * setting all the values to 0. + **/ +void igc_clear_vfta_generic(struct igc_hw *hw) +{ + u32 offset; + + DEBUGFUNC("igc_clear_vfta_generic"); + + for (offset = 0; offset < IGC_VLAN_FILTER_TBL_SIZE; offset++) { + IGC_WRITE_REG_ARRAY(hw, IGC_VFTA, offset, 0); + IGC_WRITE_FLUSH(hw); + } +} + +/** + * 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, u32 offset, u32 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, u16 rar_count) +{ + u32 i; + u8 mac_addr[ETH_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. + **/ +s32 igc_check_alt_mac_addr_generic(struct igc_hw *hw) +{ + u32 i; + s32 ret_val; + u16 offset, nvm_alt_mac_addr_offset, nvm_data; + u8 alt_mac_addr[ETH_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 < ETH_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] = (u8)(nvm_data & 0xFF); + alt_mac_addr[i + 1] = (u8)(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, u8 *addr, u32 index) +{ + u32 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 = ((u32) addr[0] | ((u32) addr[1] << 8) | + ((u32) addr[2] << 16) | ((u32) addr[3] << 24)); + + rar_high = ((u32) addr[4] | ((u32) 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. + **/ +u32 igc_hash_mc_addr_generic(struct igc_hw *hw, u8 *mc_addr) +{ + u32 hash_value, hash_mask; + u8 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)) | + (((u16) 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, + u8 *mc_addr_list, u32 mc_addr_count) +{ + u32 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; (u32) 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 += (ETH_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_check_for_copper_link_generic - Check for link (Copper) + * @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. + **/ +s32 igc_check_for_copper_link_generic(struct igc_hw *hw) +{ + struct igc_mac_info *mac = &hw->mac; + s32 ret_val; + bool link = false; + + DEBUGFUNC("igc_check_for_copper_link"); + + /* 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) + return IGC_SUCCESS; + + /* 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) + return ret_val; + + if (!link) + return IGC_SUCCESS; /* 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) + return -IGC_ERR_CONFIG; + + /* 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"); + + return ret_val; +} + +/** + * 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. + **/ +s32 igc_setup_link_generic(struct igc_hw *hw) +{ + s32 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. + */ + DEBUGOUT("Initializing the Flow Control address, type and timer regs\n"); + 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. + **/ +static void igc_config_collision_dist_generic(struct igc_hw *hw) +{ + u32 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. + **/ +s32 igc_set_fc_watermarks_generic(struct igc_hw *hw) +{ + u32 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. + **/ +s32 igc_force_mac_fc_generic(struct igc_hw *hw) +{ + u32 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. + **/ +s32 igc_config_fc_after_link_up_generic(struct igc_hw *hw) +{ + struct igc_mac_info *mac = &hw->mac; + s32 ret_val = IGC_SUCCESS; + u16 mii_status_reg, mii_nway_adv_reg, mii_nway_lp_ability_reg; + u16 speed, duplex; + + 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)) { + DEBUGOUT("Copper PHY and Auto Neg has not completed.\n"); + 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; + DEBUGOUT("Flow Control = FULL.\n"); + } else { + hw->fc.current_mode = igc_fc_rx_pause; + DEBUGOUT("Flow Control = Rx PAUSE frames only.\n"); + } + } + /* 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; + DEBUGOUT("Flow Control = Tx PAUSE frames only.\n"); + } + /* 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; + DEBUGOUT("Flow Control = Rx PAUSE frames only.\n"); + } 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. + **/ +s32 igc_get_speed_and_duplex_copper_generic(struct igc_hw *hw, u16 *speed, + u16 *duplex) +{ + u32 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_get_hw_semaphore_generic - Acquire hardware semaphore + * @hw: pointer to the HW structure + * + * Acquire the HW semaphore to access the PHY or NVM + **/ +s32 igc_get_hw_semaphore_generic(struct igc_hw *hw) +{ + u32 swsm; + s32 timeout = hw->nvm.word_size + 1; + s32 i = 0; + + DEBUGFUNC("igc_get_hw_semaphore_generic"); + + /* Get the SW semaphore */ + while (i < timeout) { + swsm = IGC_READ_REG(hw, IGC_SWSM); + if (!(swsm & IGC_SWSM_SMBI)) + break; + + usec_delay(50); + i++; + } + + if (i == timeout) { + DEBUGOUT("Driver can't access device - 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; + + usec_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_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) +{ + u32 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. + **/ +s32 igc_get_auto_rd_done_generic(struct igc_hw *hw) +{ + s32 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. + **/ +s32 igc_disable_pcie_master_generic(struct igc_hw *hw) +{ + u32 ctrl; + s32 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; + usec_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/igc/igc_nvm.h b/sys/dev/igc/igc_nvm.h new file mode 100644 --- /dev/null +++ b/sys/dev/igc/igc_nvm.h @@ -0,0 +1,32 @@ +/*- + * 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 *hw); +s32 igc_null_read_nvm(struct igc_hw *hw, u16 a, u16 b, u16 *c); +void igc_null_nvm_generic(struct igc_hw *hw); +s32 igc_null_led_default(struct igc_hw *hw, u16 *data); +s32 igc_null_write_nvm(struct igc_hw *hw, u16 a, u16 b, u16 *c); +s32 igc_acquire_nvm_generic(struct igc_hw *hw); + +s32 igc_poll_eerd_eewr_done(struct igc_hw *hw, int ee_reg); +s32 igc_read_mac_addr_generic(struct igc_hw *hw); +s32 igc_read_pba_string_generic(struct igc_hw *hw, u8 *pba_num, + u32 pba_num_size); +s32 igc_read_nvm_eerd(struct igc_hw *hw, u16 offset, u16 words, + u16 *data); +s32 igc_valid_led_default_generic(struct igc_hw *hw, u16 *data); +s32 igc_validate_nvm_checksum_generic(struct igc_hw *hw); +s32 igc_write_nvm_spi(struct igc_hw *hw, u16 offset, u16 words, + u16 *data); +s32 igc_update_nvm_checksum_generic(struct igc_hw *hw); +void igc_release_nvm_generic(struct igc_hw *hw); + +#endif diff --git a/sys/dev/igc/igc_nvm.c b/sys/dev/igc/igc_nvm.c new file mode 100644 --- /dev/null +++ b/sys/dev/igc/igc_nvm.c @@ -0,0 +1,721 @@ +/*- + * Copyright 2021 Intel Corp + * Copyright 2021 Rubicon Communications, LLC (Netgate) + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "igc_api.h" + +static void igc_reload_nvm_generic(struct igc_hw *hw); + +/** + * 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 + **/ +s32 igc_null_read_nvm(struct igc_hw IGC_UNUSEDARG *hw, + u16 IGC_UNUSEDARG a, u16 IGC_UNUSEDARG b, + u16 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_null_write_nvm - No-op function, return 0 + * @hw: pointer to the HW structure + * @a: dummy variable + * @b: dummy variable + * @c: dummy variable + **/ +s32 igc_null_write_nvm(struct igc_hw IGC_UNUSEDARG *hw, + u16 IGC_UNUSEDARG a, u16 IGC_UNUSEDARG b, + u16 IGC_UNUSEDARG *c) +{ + DEBUGFUNC("igc_null_write_nvm"); + return IGC_SUCCESS; +} + +/** + * igc_raise_eec_clk - Raise EEPROM clock + * @hw: pointer to the HW structure + * @eecd: pointer to the EEPROM + * + * Enable/Raise the EEPROM clock bit. + **/ +static void igc_raise_eec_clk(struct igc_hw *hw, u32 *eecd) +{ + *eecd = *eecd | IGC_EECD_SK; + IGC_WRITE_REG(hw, IGC_EECD, *eecd); + IGC_WRITE_FLUSH(hw); + usec_delay(hw->nvm.delay_usec); +} + +/** + * igc_lower_eec_clk - Lower EEPROM clock + * @hw: pointer to the HW structure + * @eecd: pointer to the EEPROM + * + * Clear/Lower the EEPROM clock bit. + **/ +static void igc_lower_eec_clk(struct igc_hw *hw, u32 *eecd) +{ + *eecd = *eecd & ~IGC_EECD_SK; + IGC_WRITE_REG(hw, IGC_EECD, *eecd); + IGC_WRITE_FLUSH(hw); + usec_delay(hw->nvm.delay_usec); +} + +/** + * igc_shift_out_eec_bits - Shift data bits our to the EEPROM + * @hw: pointer to the HW structure + * @data: data to send to the EEPROM + * @count: number of bits to shift out + * + * We need to shift 'count' bits out to the EEPROM. So, the value in the + * "data" parameter will be shifted out to the EEPROM one bit at a time. + * In order to do this, "data" must be broken down into bits. + **/ +static void igc_shift_out_eec_bits(struct igc_hw *hw, u16 data, u16 count) +{ + struct igc_nvm_info *nvm = &hw->nvm; + u32 eecd = IGC_READ_REG(hw, IGC_EECD); + u32 mask; + + DEBUGFUNC("igc_shift_out_eec_bits"); + + mask = 0x01 << (count - 1); + if (nvm->type == igc_nvm_eeprom_spi) + eecd |= IGC_EECD_DO; + + do { + eecd &= ~IGC_EECD_DI; + + if (data & mask) + eecd |= IGC_EECD_DI; + + IGC_WRITE_REG(hw, IGC_EECD, eecd); + IGC_WRITE_FLUSH(hw); + + usec_delay(nvm->delay_usec); + + igc_raise_eec_clk(hw, &eecd); + igc_lower_eec_clk(hw, &eecd); + + mask >>= 1; + } while (mask); + + eecd &= ~IGC_EECD_DI; + IGC_WRITE_REG(hw, IGC_EECD, eecd); +} + +/** + * igc_shift_in_eec_bits - Shift data bits in from the EEPROM + * @hw: pointer to the HW structure + * @count: number of bits to shift in + * + * In order to read a register from the EEPROM, we need to shift 'count' bits + * in from the EEPROM. Bits are "shifted in" by raising the clock input to + * the EEPROM (setting the SK bit), and then reading the value of the data out + * "DO" bit. During this "shifting in" process the data in "DI" bit should + * always be clear. + **/ +static u16 igc_shift_in_eec_bits(struct igc_hw *hw, u16 count) +{ + u32 eecd; + u32 i; + u16 data; + + DEBUGFUNC("igc_shift_in_eec_bits"); + + eecd = IGC_READ_REG(hw, IGC_EECD); + + eecd &= ~(IGC_EECD_DO | IGC_EECD_DI); + data = 0; + + for (i = 0; i < count; i++) { + data <<= 1; + igc_raise_eec_clk(hw, &eecd); + + eecd = IGC_READ_REG(hw, IGC_EECD); + + eecd &= ~IGC_EECD_DI; + if (eecd & IGC_EECD_DO) + data |= 1; + + igc_lower_eec_clk(hw, &eecd); + } + + return data; +} + +/** + * 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'. + **/ +s32 igc_poll_eerd_eewr_done(struct igc_hw *hw, int ee_reg) +{ + u32 attempts = 100000; + u32 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; + + usec_delay(5); + } + + return -IGC_ERR_NVM; +} + +/** + * igc_acquire_nvm_generic - Generic request for access to EEPROM + * @hw: pointer to the HW structure + * + * 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). + **/ +s32 igc_acquire_nvm_generic(struct igc_hw *hw) +{ + u32 eecd = IGC_READ_REG(hw, IGC_EECD); + s32 timeout = IGC_NVM_GRANT_ATTEMPTS; + + DEBUGFUNC("igc_acquire_nvm_generic"); + + IGC_WRITE_REG(hw, IGC_EECD, eecd | IGC_EECD_REQ); + eecd = IGC_READ_REG(hw, IGC_EECD); + + while (timeout) { + if (eecd & IGC_EECD_GNT) + break; + usec_delay(5); + eecd = IGC_READ_REG(hw, IGC_EECD); + timeout--; + } + + if (!timeout) { + eecd &= ~IGC_EECD_REQ; + IGC_WRITE_REG(hw, IGC_EECD, eecd); + DEBUGOUT("Could not acquire NVM grant\n"); + return -IGC_ERR_NVM; + } + + return IGC_SUCCESS; +} + +/** + * igc_standby_nvm - Return EEPROM to standby state + * @hw: pointer to the HW structure + * + * Return the EEPROM to a standby state. + **/ +static void igc_standby_nvm(struct igc_hw *hw) +{ + struct igc_nvm_info *nvm = &hw->nvm; + u32 eecd = IGC_READ_REG(hw, IGC_EECD); + + DEBUGFUNC("igc_standby_nvm"); + + if (nvm->type == igc_nvm_eeprom_spi) { + /* Toggle CS to flush commands */ + eecd |= IGC_EECD_CS; + IGC_WRITE_REG(hw, IGC_EECD, eecd); + IGC_WRITE_FLUSH(hw); + usec_delay(nvm->delay_usec); + eecd &= ~IGC_EECD_CS; + IGC_WRITE_REG(hw, IGC_EECD, eecd); + IGC_WRITE_FLUSH(hw); + usec_delay(nvm->delay_usec); + } +} + +/** + * igc_stop_nvm - Terminate EEPROM command + * @hw: pointer to the HW structure + * + * Terminates the current command by inverting the EEPROM's chip select pin. + **/ +static void igc_stop_nvm(struct igc_hw *hw) +{ + u32 eecd; + + DEBUGFUNC("igc_stop_nvm"); + + eecd = IGC_READ_REG(hw, IGC_EECD); + if (hw->nvm.type == igc_nvm_eeprom_spi) { + /* Pull CS high */ + eecd |= IGC_EECD_CS; + igc_lower_eec_clk(hw, &eecd); + } +} + +/** + * igc_release_nvm_generic - Release exclusive access to EEPROM + * @hw: pointer to the HW structure + * + * Stop any current commands to the EEPROM and clear the EEPROM request bit. + **/ +void igc_release_nvm_generic(struct igc_hw *hw) +{ + u32 eecd; + + DEBUGFUNC("igc_release_nvm_generic"); + + igc_stop_nvm(hw); + + eecd = IGC_READ_REG(hw, IGC_EECD); + eecd &= ~IGC_EECD_REQ; + IGC_WRITE_REG(hw, IGC_EECD, eecd); +} + +/** + * igc_ready_nvm_eeprom - Prepares EEPROM for read/write + * @hw: pointer to the HW structure + * + * Setups the EEPROM for reading and writing. + **/ +static s32 igc_ready_nvm_eeprom(struct igc_hw *hw) +{ + struct igc_nvm_info *nvm = &hw->nvm; + u32 eecd = IGC_READ_REG(hw, IGC_EECD); + u8 spi_stat_reg; + + DEBUGFUNC("igc_ready_nvm_eeprom"); + + if (nvm->type == igc_nvm_eeprom_spi) { + u16 timeout = NVM_MAX_RETRY_SPI; + + /* Clear SK and CS */ + eecd &= ~(IGC_EECD_CS | IGC_EECD_SK); + IGC_WRITE_REG(hw, IGC_EECD, eecd); + IGC_WRITE_FLUSH(hw); + usec_delay(1); + + /* Read "Status Register" repeatedly until the LSB is cleared. + * The EEPROM will signal that the command has been completed + * by clearing bit 0 of the internal status register. If it's + * not cleared within 'timeout', then error out. + */ + while (timeout) { + igc_shift_out_eec_bits(hw, NVM_RDSR_OPCODE_SPI, + hw->nvm.opcode_bits); + spi_stat_reg = (u8)igc_shift_in_eec_bits(hw, 8); + if (!(spi_stat_reg & NVM_STATUS_RDY_SPI)) + break; + + usec_delay(5); + igc_standby_nvm(hw); + timeout--; + } + + if (!timeout) { + DEBUGOUT("SPI NVM Status error\n"); + return -IGC_ERR_NVM; + } + } + + return IGC_SUCCESS; +} + +/** + * 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. + **/ +s32 igc_read_nvm_eerd(struct igc_hw *hw, u16 offset, u16 words, u16 *data) +{ + struct igc_nvm_info *nvm = &hw->nvm; + u32 i, eerd = 0; + s32 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_write_nvm_spi - Write to EEPROM using SPI + * @hw: pointer to the HW structure + * @offset: offset within the EEPROM to be written to + * @words: number of words to write + * @data: 16 bit word(s) to be written to the EEPROM + * + * Writes data to EEPROM at offset using SPI interface. + * + * If igc_update_nvm_checksum is not called after this function , the + * EEPROM will most likely contain an invalid checksum. + **/ +s32 igc_write_nvm_spi(struct igc_hw *hw, u16 offset, u16 words, u16 *data) +{ + struct igc_nvm_info *nvm = &hw->nvm; + s32 ret_val = -IGC_ERR_NVM; + u16 widx = 0; + + DEBUGFUNC("igc_write_nvm_spi"); + + /* A check for invalid values: offset too large, too many words, + * 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; + } + + while (widx < words) { + u8 write_opcode = NVM_WRITE_OPCODE_SPI; + + ret_val = nvm->ops.acquire(hw); + if (ret_val) + return ret_val; + + ret_val = igc_ready_nvm_eeprom(hw); + if (ret_val) { + nvm->ops.release(hw); + return ret_val; + } + + igc_standby_nvm(hw); + + /* Send the WRITE ENABLE command (8 bit opcode) */ + igc_shift_out_eec_bits(hw, NVM_WREN_OPCODE_SPI, + nvm->opcode_bits); + + igc_standby_nvm(hw); + + /* Some SPI eeproms use the 8th address bit embedded in the + * opcode + */ + if ((nvm->address_bits == 8) && (offset >= 128)) + write_opcode |= NVM_A8_OPCODE_SPI; + + /* Send the Write command (8-bit opcode + addr) */ + igc_shift_out_eec_bits(hw, write_opcode, nvm->opcode_bits); + igc_shift_out_eec_bits(hw, (u16)((offset + widx) * 2), + nvm->address_bits); + + /* Loop to allow for up to whole page write of eeprom */ + while (widx < words) { + u16 word_out = data[widx]; + word_out = (word_out >> 8) | (word_out << 8); + igc_shift_out_eec_bits(hw, word_out, 16); + widx++; + + if ((((offset + widx) * 2) % nvm->page_size) == 0) { + igc_standby_nvm(hw); + break; + } + } + msec_delay(10); + nvm->ops.release(hw); + } + + return ret_val; +} + +/** + * igc_read_pba_string_generic - Read device part number + * @hw: pointer to the HW structure + * @pba_num: pointer to device part number + * @pba_num_size: size of part number buffer + * + * Reads the product board assembly (PBA) number from the EEPROM and stores + * the value in pba_num. + **/ +s32 igc_read_pba_string_generic(struct igc_hw *hw, u8 *pba_num, + u32 pba_num_size) +{ + s32 ret_val; + u16 nvm_data; + u16 pba_ptr; + u16 offset; + u16 length; + + DEBUGFUNC("igc_read_pba_string_generic"); + + if (pba_num == NULL) { + DEBUGOUT("PBA string buffer was null\n"); + return -IGC_ERR_INVALID_ARGUMENT; + } + + ret_val = hw->nvm.ops.read(hw, NVM_PBA_OFFSET_0, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + ret_val = hw->nvm.ops.read(hw, NVM_PBA_OFFSET_1, 1, &pba_ptr); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + /* if nvm_data is not ptr guard the PBA must be in legacy format which + * means pba_ptr is actually our second data word for the PBA number + * and we can decode it into an ascii string + */ + if (nvm_data != NVM_PBA_PTR_GUARD) { + DEBUGOUT("NVM PBA number is not stored as string\n"); + + /* make sure callers buffer is big enough to store the PBA */ + if (pba_num_size < IGC_PBANUM_LENGTH) { + DEBUGOUT("PBA string buffer too small\n"); + return IGC_ERR_NO_SPACE; + } + + /* extract hex string from data and pba_ptr */ + pba_num[0] = (nvm_data >> 12) & 0xF; + pba_num[1] = (nvm_data >> 8) & 0xF; + pba_num[2] = (nvm_data >> 4) & 0xF; + pba_num[3] = nvm_data & 0xF; + pba_num[4] = (pba_ptr >> 12) & 0xF; + pba_num[5] = (pba_ptr >> 8) & 0xF; + pba_num[6] = '-'; + pba_num[7] = 0; + pba_num[8] = (pba_ptr >> 4) & 0xF; + pba_num[9] = pba_ptr & 0xF; + + /* put a null character on the end of our string */ + pba_num[10] = '\0'; + + /* switch all the data but the '-' to hex char */ + for (offset = 0; offset < 10; offset++) { + if (pba_num[offset] < 0xA) + pba_num[offset] += '0'; + else if (pba_num[offset] < 0x10) + pba_num[offset] += 'A' - 0xA; + } + + return IGC_SUCCESS; + } + + ret_val = hw->nvm.ops.read(hw, pba_ptr, 1, &length); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + + if (length == 0xFFFF || length == 0) { + DEBUGOUT("NVM PBA number section invalid length\n"); + return -IGC_ERR_NVM_PBA_SECTION; + } + /* check if pba_num buffer is big enough */ + if (pba_num_size < (((u32)length * 2) - 1)) { + DEBUGOUT("PBA string buffer too small\n"); + return -IGC_ERR_NO_SPACE; + } + + /* trim pba length from start of string */ + pba_ptr++; + length--; + + for (offset = 0; offset < length; offset++) { + ret_val = hw->nvm.ops.read(hw, pba_ptr + offset, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error\n"); + return ret_val; + } + pba_num[offset * 2] = (u8)(nvm_data >> 8); + pba_num[(offset * 2) + 1] = (u8)(nvm_data & 0xFF); + } + pba_num[offset * 2] = '\0'; + + return IGC_SUCCESS; +} + + + + + +/** + * 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. + **/ +s32 igc_read_mac_addr_generic(struct igc_hw *hw) +{ + u32 rar_high; + u32 rar_low; + u16 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] = (u8)(rar_low >> (i*8)); + + for (i = 0; i < IGC_RAH_MAC_ADDR_LEN; i++) + hw->mac.perm_addr[i+4] = (u8)(rar_high >> (i*8)); + + for (i = 0; i < ETH_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. + **/ +s32 igc_validate_nvm_checksum_generic(struct igc_hw *hw) +{ + s32 ret_val; + u16 checksum = 0; + u16 i, nvm_data; + + 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 != (u16) NVM_SUM) { + DEBUGOUT("NVM Checksum Invalid\n"); + return -IGC_ERR_NVM; + } + + return IGC_SUCCESS; +} + +/** + * igc_update_nvm_checksum_generic - 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. + **/ +s32 igc_update_nvm_checksum_generic(struct igc_hw *hw) +{ + s32 ret_val; + u16 checksum = 0; + u16 i, nvm_data; + + DEBUGFUNC("igc_update_nvm_checksum"); + + for (i = 0; i < NVM_CHECKSUM_REG; i++) { + ret_val = hw->nvm.ops.read(hw, i, 1, &nvm_data); + if (ret_val) { + DEBUGOUT("NVM Read Error while updating checksum.\n"); + return ret_val; + } + checksum += nvm_data; + } + checksum = (u16) NVM_SUM - checksum; + ret_val = hw->nvm.ops.write(hw, NVM_CHECKSUM_REG, 1, &checksum); + if (ret_val) + DEBUGOUT("NVM Write Error while updating checksum.\n"); + + return ret_val; +} + +/** + * 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. + **/ +static void igc_reload_nvm_generic(struct igc_hw *hw) +{ + u32 ctrl_ext; + + DEBUGFUNC("igc_reload_nvm_generic"); + + usec_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); +} + + diff --git a/sys/dev/igc/igc_osdep.h b/sys/dev/igc/igc_osdep.h new file mode 100644 --- /dev/null +++ b/sys/dev/igc/igc_osdep.h @@ -0,0 +1,133 @@ +/*- + * Copyright 2021 Intel Corp + * Copyright 2021 Rubicon Communications, LLC (Netgate) + * SPDX-License-Identifier: BSD-3-Clause + * + * $FreeBSD$ + */ + +#ifndef _FREEBSD_OS_H_ +#define _FREEBSD_OS_H_ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#define usec_delay(x) DELAY(x) +#define usec_delay_irq(x) usec_delay(x) +#define msec_delay(x) DELAY(1000*(x)) +#define msec_delay_irq(x) DELAY(1000*(x)) + +/* 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") + +typedef uint64_t u64; +typedef uint32_t u32; +typedef uint16_t u16; +typedef uint8_t u8; +typedef int64_t s64; +typedef int32_t s32; +typedef int16_t s16; +typedef int8_t s8; + +#define __le16 u16 +#define __le32 u32 +#define __le64 u64 + +struct igc_osdep +{ + bus_space_tag_t mem_bus_space_tag; + bus_space_handle_t mem_bus_space_handle; + bus_space_tag_t io_bus_space_tag; + bus_space_handle_t io_bus_space_handle; + bus_space_tag_t flash_bus_space_tag; + bus_space_handle_t flash_bus_space_handle; + device_t dev; + if_ctx_t ctx; +}; + +#define IGC_REGISTER(hw, reg) reg + +#define IGC_WRITE_FLUSH(a) IGC_READ_REG(a, IGC_STATUS) + +/* Read from an absolute offset in the adapter's memory space */ +#define IGC_READ_OFFSET(hw, offset) \ + bus_space_read_4(((struct igc_osdep *)(hw)->back)->mem_bus_space_tag, \ + ((struct igc_osdep *)(hw)->back)->mem_bus_space_handle, offset) + +/* Write to an absolute offset in the adapter's memory space */ +#define IGC_WRITE_OFFSET(hw, offset, value) \ + bus_space_write_4(((struct igc_osdep *)(hw)->back)->mem_bus_space_tag, \ + ((struct igc_osdep *)(hw)->back)->mem_bus_space_handle, offset, value) + +/* Register READ/WRITE macros */ + +#define IGC_READ_REG(hw, reg) \ + bus_space_read_4(((struct igc_osdep *)(hw)->back)->mem_bus_space_tag, \ + ((struct igc_osdep *)(hw)->back)->mem_bus_space_handle, \ + IGC_REGISTER(hw, reg)) + +#define IGC_WRITE_REG(hw, reg, value) \ + bus_space_write_4(((struct igc_osdep *)(hw)->back)->mem_bus_space_tag, \ + ((struct igc_osdep *)(hw)->back)->mem_bus_space_handle, \ + IGC_REGISTER(hw, reg), value) + +#define IGC_READ_REG_ARRAY(hw, reg, index) \ + bus_space_read_4(((struct igc_osdep *)(hw)->back)->mem_bus_space_tag, \ + ((struct igc_osdep *)(hw)->back)->mem_bus_space_handle, \ + IGC_REGISTER(hw, reg) + ((index)<< 2)) + +#define IGC_WRITE_REG_ARRAY(hw, reg, index, value) \ + bus_space_write_4(((struct igc_osdep *)(hw)->back)->mem_bus_space_tag, \ + ((struct igc_osdep *)(hw)->back)->mem_bus_space_handle, \ + IGC_REGISTER(hw, reg) + ((index)<< 2), value) + +#define IGC_READ_REG_ARRAY_DWORD IGC_READ_REG_ARRAY +#define IGC_WRITE_REG_ARRAY_DWORD IGC_WRITE_REG_ARRAY + +#define IGC_READ_REG_ARRAY_BYTE(hw, reg, index) \ + bus_space_read_1(((struct igc_osdep *)(hw)->back)->mem_bus_space_tag, \ + ((struct igc_osdep *)(hw)->back)->mem_bus_space_handle, \ + IGC_REGISTER(hw, reg) + index) + +#define IGC_WRITE_REG_ARRAY_BYTE(hw, reg, index, value) \ + bus_space_write_1(((struct igc_osdep *)(hw)->back)->mem_bus_space_tag, \ + ((struct igc_osdep *)(hw)->back)->mem_bus_space_handle, \ + IGC_REGISTER(hw, reg) + index, value) + +#define IGC_WRITE_REG_ARRAY_WORD(hw, reg, index, value) \ + bus_space_write_2(((struct igc_osdep *)(hw)->back)->mem_bus_space_tag, \ + ((struct igc_osdep *)(hw)->back)->mem_bus_space_handle, \ + IGC_REGISTER(hw, reg) + (index << 1), value) + +#endif /* _FREEBSD_OS_H_ */ diff --git a/sys/dev/igc/igc_phy.h b/sys/dev/igc/igc_phy.h new file mode 100644 --- /dev/null +++ b/sys/dev/igc/igc_phy.h @@ -0,0 +1,134 @@ +/*- + * 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 *hw); +s32 igc_null_read_reg(struct igc_hw *hw, u32 offset, u16 *data); +void igc_null_phy_generic(struct igc_hw *hw); +s32 igc_null_lplu_state(struct igc_hw *hw, bool active); +s32 igc_null_write_reg(struct igc_hw *hw, u32 offset, u16 data); +s32 igc_null_set_page(struct igc_hw *hw, u16 data); +s32 igc_check_downshift_generic(struct igc_hw *hw); +s32 igc_check_reset_block_generic(struct igc_hw *hw); +s32 igc_get_phy_id(struct igc_hw *hw); +s32 igc_phy_sw_reset_generic(struct igc_hw *hw); +void igc_phy_force_speed_duplex_setup(struct igc_hw *hw, u16 *phy_ctrl); +s32 igc_phy_hw_reset_generic(struct igc_hw *hw); +s32 igc_phy_reset_dsp_generic(struct igc_hw *hw); +s32 igc_set_d3_lplu_state_generic(struct igc_hw *hw, bool active); +s32 igc_setup_copper_link_generic(struct igc_hw *hw); +s32 igc_phy_has_link_generic(struct igc_hw *hw, u32 iterations, + u32 usec_interval, bool *success); +enum igc_phy_type igc_get_phy_type_from_id(u32 phy_id); +s32 igc_determine_phy_address(struct igc_hw *hw); +s32 igc_enable_phy_wakeup_reg_access_bm(struct igc_hw *hw, u16 *phy_reg); +s32 igc_disable_phy_wakeup_reg_access_bm(struct igc_hw *hw, u16 *phy_reg); +void igc_power_up_phy_copper(struct igc_hw *hw); +void igc_power_down_phy_copper(struct igc_hw *hw); +s32 igc_read_phy_reg_mdic(struct igc_hw *hw, u32 offset, u16 *data); +s32 igc_write_phy_reg_mdic(struct igc_hw *hw, u32 offset, u16 data); + +s32 igc_read_xmdio_reg(struct igc_hw *hw, u16 addr, u8 dev_addr, + u16 *data); +s32 igc_write_xmdio_reg(struct igc_hw *hw, u16 addr, u8 dev_addr, + u16 data); +s32 igc_write_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 data); +s32 igc_read_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 *data); + +#define IGC_MAX_PHY_ADDR 8 + +/* 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 diff --git a/sys/dev/igc/igc_phy.c b/sys/dev/igc/igc_phy.c new file mode 100644 --- /dev/null +++ b/sys/dev/igc/igc_phy.c @@ -0,0 +1,1109 @@ +/*- + * Copyright 2021 Intel Corp + * Copyright 2021 Rubicon Communications, LLC (Netgate) + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +__FBSDID("$FreeBSD$"); + +#include "igc_api.h" + +static s32 igc_wait_autoneg(struct igc_hw *hw); + +/** + * 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 + **/ +s32 igc_null_set_page(struct igc_hw IGC_UNUSEDARG *hw, + u16 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 + **/ +s32 igc_null_read_reg(struct igc_hw IGC_UNUSEDARG *hw, + u32 IGC_UNUSEDARG offset, u16 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 + **/ +s32 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 + **/ +s32 igc_null_write_reg(struct igc_hw IGC_UNUSEDARG *hw, + u32 IGC_UNUSEDARG offset, u16 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). + **/ +s32 igc_check_reset_block_generic(struct igc_hw *hw) +{ + u32 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. + **/ +s32 igc_get_phy_id(struct igc_hw *hw) +{ + struct igc_phy_info *phy = &hw->phy; + s32 ret_val = IGC_SUCCESS; + u16 phy_id; + + 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 = (u32)(phy_id << 16); + usec_delay(20); + ret_val = phy->ops.read_reg(hw, PHY_ID2, &phy_id); + if (ret_val) + return ret_val; + + phy->id |= (u32)(phy_id & PHY_REVISION_MASK); + phy->revision = (u32)(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. + **/ +s32 igc_read_phy_reg_mdic(struct igc_hw *hw, u32 offset, u16 *data) +{ + struct igc_phy_info *phy = &hw->phy; + u32 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++) { + usec_delay_irq(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 = (u16) 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. + **/ +s32 igc_write_phy_reg_mdic(struct igc_hw *hw, u32 offset, u16 data) +{ + struct igc_phy_info *phy = &hw->phy; + u32 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 = (((u32)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++) { + usec_delay_irq(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) { + DEBUGOUT2("MDI Write offset error - requested %d, returned %d\n", + offset, + (mdic & IGC_MDIC_REG_MASK) >> IGC_MDIC_REG_SHIFT); + 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. + **/ +static s32 igc_phy_setup_autoneg(struct igc_hw *hw) +{ + struct igc_phy_info *phy = &hw->phy; + s32 ret_val; + u16 mii_autoneg_adv_reg; + u16 mii_1000t_ctrl_reg = 0; + u16 aneg_multigbt_an_ctrl = 0; + + 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. + **/ +static s32 igc_copper_link_autoneg(struct igc_hw *hw) +{ + struct igc_phy_info *phy = &hw->phy; + s32 ret_val; + u16 phy_ctrl; + + 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) { + DEBUGOUT("Error while waiting for autoneg to complete\n"); + 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). + **/ +s32 igc_setup_copper_link_generic(struct igc_hw *hw) +{ + s32 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_phy_force_speed_duplex_setup - Configure forced PHY speed/duplex + * @hw: pointer to the HW structure + * @phy_ctrl: pointer to current value of PHY_CONTROL + * + * Forces speed and duplex on the PHY by doing the following: disable flow + * control, force speed/duplex on the MAC, disable auto speed detection, + * disable auto-negotiation, configure duplex, configure speed, configure + * the collision distance, write configuration to CTRL register. The + * caller must write to the PHY_CONTROL register for these settings to + * take affect. + **/ +void igc_phy_force_speed_duplex_setup(struct igc_hw *hw, u16 *phy_ctrl) +{ + struct igc_mac_info *mac = &hw->mac; + u32 ctrl; + + DEBUGFUNC("igc_phy_force_speed_duplex_setup"); + + /* Turn off flow control when forcing speed/duplex */ + hw->fc.current_mode = igc_fc_none; + + /* Force speed/duplex on the mac */ + ctrl = IGC_READ_REG(hw, IGC_CTRL); + ctrl |= (IGC_CTRL_FRCSPD | IGC_CTRL_FRCDPX); + ctrl &= ~IGC_CTRL_SPD_SEL; + + /* Disable Auto Speed Detection */ + ctrl &= ~IGC_CTRL_ASDE; + + /* Disable autoneg on the phy */ + *phy_ctrl &= ~MII_CR_AUTO_NEG_EN; + + /* Forcing Full or Half Duplex? */ + if (mac->forced_speed_duplex & IGC_ALL_HALF_DUPLEX) { + ctrl &= ~IGC_CTRL_FD; + *phy_ctrl &= ~MII_CR_FULL_DUPLEX; + DEBUGOUT("Half Duplex\n"); + } else { + ctrl |= IGC_CTRL_FD; + *phy_ctrl |= MII_CR_FULL_DUPLEX; + DEBUGOUT("Full Duplex\n"); + } + + /* Forcing 10mb or 100mb? */ + if (mac->forced_speed_duplex & IGC_ALL_100_SPEED) { + ctrl |= IGC_CTRL_SPD_100; + *phy_ctrl |= MII_CR_SPEED_100; + *phy_ctrl &= ~MII_CR_SPEED_1000; + DEBUGOUT("Forcing 100mb\n"); + } else { + ctrl &= ~(IGC_CTRL_SPD_1000 | IGC_CTRL_SPD_100); + *phy_ctrl &= ~(MII_CR_SPEED_1000 | MII_CR_SPEED_100); + DEBUGOUT("Forcing 10mb\n"); + } + + hw->mac.ops.config_collision_dist(hw); + + IGC_WRITE_REG(hw, IGC_CTRL, ctrl); +} + +/** + * igc_set_d3_lplu_state_generic - Sets low power link up state for D3 + * @hw: pointer to the HW structure + * @active: boolean used to enable/disable lplu + * + * Success returns 0, Failure returns 1 + * + * The low power link up (lplu) state is set to the power management level D3 + * and SmartSpeed is disabled when active is true, else clear lplu for D3 + * and enable Smartspeed. LPLU and Smartspeed are mutually exclusive. LPLU + * is used during Dx states where the power conservation is most important. + * During driver activity, SmartSpeed should be enabled so performance is + * maintained. + **/ +s32 igc_set_d3_lplu_state_generic(struct igc_hw *hw, bool active) +{ + struct igc_phy_info *phy = &hw->phy; + s32 ret_val; + u16 data; + + DEBUGFUNC("igc_set_d3_lplu_state_generic"); + + if (!hw->phy.ops.read_reg) + return IGC_SUCCESS; + + ret_val = phy->ops.read_reg(hw, IGP02IGC_PHY_POWER_MGMT, &data); + if (ret_val) + return ret_val; + + if (!active) { + data &= ~IGP02IGC_PM_D3_LPLU; + ret_val = phy->ops.write_reg(hw, IGP02IGC_PHY_POWER_MGMT, + data); + if (ret_val) + return ret_val; + /* LPLU and SmartSpeed are mutually exclusive. LPLU is used + * during Dx states where the power conservation is most + * important. During driver activity we should enable + * SmartSpeed, so performance is maintained. + */ + if (phy->smart_speed == igc_smart_speed_on) { + ret_val = phy->ops.read_reg(hw, + IGP01IGC_PHY_PORT_CONFIG, + &data); + if (ret_val) + return ret_val; + + data |= IGP01IGC_PSCFR_SMART_SPEED; + ret_val = phy->ops.write_reg(hw, + IGP01IGC_PHY_PORT_CONFIG, + data); + if (ret_val) + return ret_val; + } else if (phy->smart_speed == igc_smart_speed_off) { + ret_val = phy->ops.read_reg(hw, + IGP01IGC_PHY_PORT_CONFIG, + &data); + if (ret_val) + return ret_val; + + data &= ~IGP01IGC_PSCFR_SMART_SPEED; + ret_val = phy->ops.write_reg(hw, + IGP01IGC_PHY_PORT_CONFIG, + data); + if (ret_val) + return ret_val; + } + } else if ((phy->autoneg_advertised == IGC_ALL_SPEED_DUPLEX) || + (phy->autoneg_advertised == IGC_ALL_NOT_GIG) || + (phy->autoneg_advertised == IGC_ALL_10_SPEED)) { + data |= IGP02IGC_PM_D3_LPLU; + ret_val = phy->ops.write_reg(hw, IGP02IGC_PHY_POWER_MGMT, + data); + if (ret_val) + return ret_val; + + /* When LPLU is enabled, we should disable SmartSpeed */ + ret_val = phy->ops.read_reg(hw, IGP01IGC_PHY_PORT_CONFIG, + &data); + if (ret_val) + return ret_val; + + data &= ~IGP01IGC_PSCFR_SMART_SPEED; + ret_val = phy->ops.write_reg(hw, IGP01IGC_PHY_PORT_CONFIG, + data); + } + + 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. + **/ +s32 igc_check_downshift_generic(struct igc_hw *hw) +{ + struct igc_phy_info *phy = &hw->phy; + s32 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. + **/ +static s32 igc_wait_autoneg(struct igc_hw *hw) +{ + s32 ret_val = IGC_SUCCESS; + u16 i, phy_status; + + 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. + **/ +s32 igc_phy_has_link_generic(struct igc_hw *hw, u32 iterations, + u32 usec_interval, bool *success) +{ + s32 ret_val = IGC_SUCCESS; + u16 i, phy_status; + + 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 + usec_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 + usec_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. + **/ +s32 igc_phy_sw_reset_generic(struct igc_hw *hw) +{ + s32 ret_val; + u16 phy_ctrl; + + 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; + + usec_delay(1); + + return ret_val; +} + +/** + * igc_phy_hw_reset_generic - PHY hardware reset + * @hw: pointer to the HW structure + * + * Verify the reset block is not blocking us from resetting. Acquire + * semaphore (if necessary) and read/set/write the device control reset + * bit in the PHY. Wait the appropriate delay time for the device to + * reset and release the semaphore (if necessary). + **/ +s32 igc_phy_hw_reset_generic(struct igc_hw *hw) +{ + struct igc_phy_info *phy = &hw->phy; + s32 ret_val; + u32 ctrl, timeout = 10000, phpm = 0; + + DEBUGFUNC("igc_phy_hw_reset_generic"); + + if (phy->ops.check_reset_block) { + ret_val = phy->ops.check_reset_block(hw); + if (ret_val) + return IGC_SUCCESS; + } + + ret_val = phy->ops.acquire(hw); + if (ret_val) + return ret_val; + + phpm = IGC_READ_REG(hw, IGC_I225_PHPM); + + ctrl = IGC_READ_REG(hw, IGC_CTRL); + IGC_WRITE_REG(hw, IGC_CTRL, ctrl | IGC_CTRL_PHY_RST); + IGC_WRITE_FLUSH(hw); + + usec_delay(phy->reset_delay_us); + + IGC_WRITE_REG(hw, IGC_CTRL, ctrl); + IGC_WRITE_FLUSH(hw); + + usec_delay(150); + + do { + phpm = IGC_READ_REG(hw, IGC_I225_PHPM); + timeout--; + usec_delay(1); + } while (!(phpm & IGC_I225_PHPM_RST_COMPL) && timeout); + + if (!timeout) + DEBUGOUT("Timeout expired after a phy reset\n"); + + phy->ops.release(hw); + + 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) +{ + u16 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); + usec_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) +{ + u16 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. + **/ +s32 igc_write_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 data) +{ + s32 ret_val; + u8 dev_addr = (offset & GPY_MMD_MASK) >> GPY_MMD_SHIFT; + + 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, (u16)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. + **/ +s32 igc_read_phy_reg_gpy(struct igc_hw *hw, u32 offset, u16 *data) +{ + s32 ret_val; + u8 dev_addr = (offset & GPY_MMD_MASK) >> GPY_MMD_SHIFT; + + 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, (u16)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 + **/ +static s32 __igc_access_xmdio_reg(struct igc_hw *hw, u16 address, + u8 dev_addr, u16 *data, bool read) +{ + s32 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 + **/ +s32 igc_read_xmdio_reg(struct igc_hw *hw, u16 addr, u8 dev_addr, u16 *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 + **/ +s32 igc_write_xmdio_reg(struct igc_hw *hw, u16 addr, u8 dev_addr, u16 data) +{ + DEBUGFUNC("igc_write_xmdio_reg"); + + return __igc_access_xmdio_reg(hw, addr, dev_addr, &data, false); +} diff --git a/sys/dev/igc/igc_regs.h b/sys/dev/igc/igc_regs.h new file mode 100644 --- /dev/null +++ b/sys/dev/igc/igc_regs.h @@ -0,0 +1,424 @@ +/*- + * 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 - RW */ +#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 - RW */ +#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 diff --git a/sys/dev/igc/igc_txrx.c b/sys/dev/igc/igc_txrx.c new file mode 100644 --- /dev/null +++ b/sys/dev/igc/igc_txrx.c @@ -0,0 +1,580 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2016 Matthew Macy + * 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 +__FBSDID("$FreeBSD$"); + +#include "if_igc.h" + +#ifdef RSS +#include +#include +#endif + +#ifdef VERBOSE_DEBUG +#define DPRINTF device_printf +#else +#define DPRINTF(...) +#endif + +/********************************************************************* + * Local Function prototypes + *********************************************************************/ +static int igc_isc_txd_encap(void *arg, if_pkt_info_t pi); +static void igc_isc_txd_flush(void *arg, uint16_t txqid, qidx_t pidx); +static int igc_isc_txd_credits_update(void *arg, uint16_t txqid, bool clear); + +static void igc_isc_rxd_refill(void *arg, if_rxd_update_t iru); + +static void igc_isc_rxd_flush(void *arg, uint16_t rxqid, uint8_t flid __unused, qidx_t pidx); +static int igc_isc_rxd_available(void *arg, uint16_t rxqid, qidx_t idx, qidx_t budget); + +static int igc_isc_rxd_pkt_get(void *arg, if_rxd_info_t ri); + +static int igc_tx_ctx_setup(struct tx_ring *txr, if_pkt_info_t pi, u32 *cmd_type_len, u32 *olinfo_status); +static int igc_tso_setup(struct tx_ring *txr, if_pkt_info_t pi, u32 *cmd_type_len, u32 *olinfo_status); + +static void igc_rx_checksum(u32 staterr, if_rxd_info_t ri, u32 ptype); +static int igc_determine_rsstype(u16 pkt_info); + +extern void igc_if_enable_intr(if_ctx_t ctx); +extern int igc_intr(void *arg); + +struct if_txrx igc_txrx = { + .ift_txd_encap = igc_isc_txd_encap, + .ift_txd_flush = igc_isc_txd_flush, + .ift_txd_credits_update = igc_isc_txd_credits_update, + .ift_rxd_available = igc_isc_rxd_available, + .ift_rxd_pkt_get = igc_isc_rxd_pkt_get, + .ift_rxd_refill = igc_isc_rxd_refill, + .ift_rxd_flush = igc_isc_rxd_flush, + .ift_legacy_intr = igc_intr +}; + +void +igc_dump_rs(struct igc_adapter *adapter) +{ + if_softc_ctx_t scctx = adapter->shared; + struct igc_tx_queue *que; + struct tx_ring *txr; + qidx_t i, ntxd, qid, cur; + int16_t rs_cidx; + uint8_t status; + + printf("\n"); + ntxd = scctx->isc_ntxd[0]; + for (qid = 0; qid < adapter->tx_num_queues; qid++) { + que = &adapter->tx_queues[qid]; + txr = &que->txr; + rs_cidx = txr->tx_rs_cidx; + if (rs_cidx != txr->tx_rs_pidx) { + cur = txr->tx_rsq[rs_cidx]; + status = txr->tx_base[cur].upper.fields.status; + if (!(status & IGC_TXD_STAT_DD)) + printf("qid[%d]->tx_rsq[%d]: %d clear ", qid, rs_cidx, cur); + } else { + rs_cidx = (rs_cidx-1)&(ntxd-1); + cur = txr->tx_rsq[rs_cidx]; + printf("qid[%d]->tx_rsq[rs_cidx-1=%d]: %d ", qid, rs_cidx, cur); + } + printf("cidx_prev=%d rs_pidx=%d ",txr->tx_cidx_processed, txr->tx_rs_pidx); + for (i = 0; i < ntxd; i++) { + if (txr->tx_base[i].upper.fields.status & IGC_TXD_STAT_DD) + printf("%d set ", i); + } + printf("\n"); + } +} + +/********************************************************************** + * + * Setup work for hardware segmentation offload (TSO) on + * adapters using advanced tx descriptors + * + **********************************************************************/ +static int +igc_tso_setup(struct tx_ring *txr, if_pkt_info_t pi, u32 *cmd_type_len, u32 *olinfo_status) +{ + struct igc_adv_tx_context_desc *TXD; + u32 type_tucmd_mlhl = 0, vlan_macip_lens = 0; + u32 mss_l4len_idx = 0; + u32 paylen; + + switch(pi->ipi_etype) { + case ETHERTYPE_IPV6: + type_tucmd_mlhl |= IGC_ADVTXD_TUCMD_IPV6; + break; + case ETHERTYPE_IP: + type_tucmd_mlhl |= IGC_ADVTXD_TUCMD_IPV4; + /* Tell transmit desc to also do IPv4 checksum. */ + *olinfo_status |= IGC_TXD_POPTS_IXSM << 8; + break; + default: + panic("%s: CSUM_TSO but no supported IP version (0x%04x)", + __func__, ntohs(pi->ipi_etype)); + break; + } + + TXD = (struct igc_adv_tx_context_desc *) &txr->tx_base[pi->ipi_pidx]; + + /* This is used in the transmit desc in encap */ + paylen = pi->ipi_len - pi->ipi_ehdrlen - pi->ipi_ip_hlen - pi->ipi_tcp_hlen; + + /* VLAN MACLEN IPLEN */ + if (pi->ipi_mflags & M_VLANTAG) { + vlan_macip_lens |= (pi->ipi_vtag << IGC_ADVTXD_VLAN_SHIFT); + } + + vlan_macip_lens |= pi->ipi_ehdrlen << IGC_ADVTXD_MACLEN_SHIFT; + vlan_macip_lens |= pi->ipi_ip_hlen; + TXD->vlan_macip_lens = htole32(vlan_macip_lens); + + /* ADV DTYPE TUCMD */ + type_tucmd_mlhl |= IGC_ADVTXD_DCMD_DEXT | IGC_ADVTXD_DTYP_CTXT; + type_tucmd_mlhl |= IGC_ADVTXD_TUCMD_L4T_TCP; + TXD->type_tucmd_mlhl = htole32(type_tucmd_mlhl); + + /* MSS L4LEN IDX */ + mss_l4len_idx |= (pi->ipi_tso_segsz << IGC_ADVTXD_MSS_SHIFT); + mss_l4len_idx |= (pi->ipi_tcp_hlen << IGC_ADVTXD_L4LEN_SHIFT); + TXD->mss_l4len_idx = htole32(mss_l4len_idx); + + TXD->seqnum_seed = htole32(0); + *cmd_type_len |= IGC_ADVTXD_DCMD_TSE; + *olinfo_status |= IGC_TXD_POPTS_TXSM << 8; + *olinfo_status |= paylen << IGC_ADVTXD_PAYLEN_SHIFT; + + return (1); +} + +/********************************************************************* + * + * Advanced Context Descriptor setup for VLAN, CSUM or TSO + * + **********************************************************************/ +static int +igc_tx_ctx_setup(struct tx_ring *txr, if_pkt_info_t pi, u32 *cmd_type_len, u32 *olinfo_status) +{ + struct igc_adv_tx_context_desc *TXD; + u32 vlan_macip_lens, type_tucmd_mlhl; + u32 mss_l4len_idx; + mss_l4len_idx = vlan_macip_lens = type_tucmd_mlhl = 0; + + /* First check if TSO is to be used */ + if (pi->ipi_csum_flags & CSUM_TSO) + return (igc_tso_setup(txr, pi, cmd_type_len, olinfo_status)); + + /* Indicate the whole packet as payload when not doing TSO */ + *olinfo_status |= pi->ipi_len << IGC_ADVTXD_PAYLEN_SHIFT; + + /* Now ready a context descriptor */ + TXD = (struct igc_adv_tx_context_desc *) &txr->tx_base[pi->ipi_pidx]; + + /* + ** In advanced descriptors the vlan tag must + ** be placed into the context descriptor. Hence + ** we need to make one even if not doing offloads. + */ + if (pi->ipi_mflags & M_VLANTAG) { + vlan_macip_lens |= (pi->ipi_vtag << IGC_ADVTXD_VLAN_SHIFT); + } else if ((pi->ipi_csum_flags & IGC_CSUM_OFFLOAD) == 0) { + return (0); + } + + /* Set the ether header length */ + vlan_macip_lens |= pi->ipi_ehdrlen << IGC_ADVTXD_MACLEN_SHIFT; + + switch(pi->ipi_etype) { + case ETHERTYPE_IP: + type_tucmd_mlhl |= IGC_ADVTXD_TUCMD_IPV4; + break; + case ETHERTYPE_IPV6: + type_tucmd_mlhl |= IGC_ADVTXD_TUCMD_IPV6; + break; + default: + break; + } + + vlan_macip_lens |= pi->ipi_ip_hlen; + type_tucmd_mlhl |= IGC_ADVTXD_DCMD_DEXT | IGC_ADVTXD_DTYP_CTXT; + + switch (pi->ipi_ipproto) { + case IPPROTO_TCP: + if (pi->ipi_csum_flags & (CSUM_IP_TCP | CSUM_IP6_TCP)) { + type_tucmd_mlhl |= IGC_ADVTXD_TUCMD_L4T_TCP; + *olinfo_status |= IGC_TXD_POPTS_TXSM << 8; + } + break; + case IPPROTO_UDP: + if (pi->ipi_csum_flags & (CSUM_IP_UDP | CSUM_IP6_UDP)) { + type_tucmd_mlhl |= IGC_ADVTXD_TUCMD_L4T_UDP; + *olinfo_status |= IGC_TXD_POPTS_TXSM << 8; + } + break; + case IPPROTO_SCTP: + if (pi->ipi_csum_flags & (CSUM_IP_SCTP | CSUM_IP6_SCTP)) { + type_tucmd_mlhl |= IGC_ADVTXD_TUCMD_L4T_SCTP; + *olinfo_status |= IGC_TXD_POPTS_TXSM << 8; + } + break; + default: + break; + } + + /* Now copy bits into descriptor */ + TXD->vlan_macip_lens = htole32(vlan_macip_lens); + TXD->type_tucmd_mlhl = htole32(type_tucmd_mlhl); + TXD->seqnum_seed = htole32(0); + TXD->mss_l4len_idx = htole32(mss_l4len_idx); + + return (1); +} + +static int +igc_isc_txd_encap(void *arg, if_pkt_info_t pi) +{ + struct igc_adapter *sc = arg; + if_softc_ctx_t scctx = sc->shared; + struct igc_tx_queue *que = &sc->tx_queues[pi->ipi_qsidx]; + struct tx_ring *txr = &que->txr; + int nsegs = pi->ipi_nsegs; + bus_dma_segment_t *segs = pi->ipi_segs; + union igc_adv_tx_desc *txd = NULL; + int i, j, pidx_last; + u32 olinfo_status, cmd_type_len, txd_flags; + qidx_t ntxd; + + pidx_last = olinfo_status = 0; + /* Basic descriptor defines */ + cmd_type_len = (IGC_ADVTXD_DTYP_DATA | + IGC_ADVTXD_DCMD_IFCS | IGC_ADVTXD_DCMD_DEXT); + + if (pi->ipi_mflags & M_VLANTAG) + cmd_type_len |= IGC_ADVTXD_DCMD_VLE; + + i = pi->ipi_pidx; + ntxd = scctx->isc_ntxd[0]; + txd_flags = pi->ipi_flags & IPI_TX_INTR ? IGC_ADVTXD_DCMD_RS : 0; + /* Consume the first descriptor */ + i += igc_tx_ctx_setup(txr, pi, &cmd_type_len, &olinfo_status); + if (i == scctx->isc_ntxd[0]) + i = 0; + + for (j = 0; j < nsegs; j++) { + bus_size_t seglen; + bus_addr_t segaddr; + + txd = (union igc_adv_tx_desc *)&txr->tx_base[i]; + seglen = segs[j].ds_len; + segaddr = htole64(segs[j].ds_addr); + + txd->read.buffer_addr = segaddr; + txd->read.cmd_type_len = htole32(IGC_ADVTXD_DCMD_IFCS | + cmd_type_len | seglen); + txd->read.olinfo_status = htole32(olinfo_status); + pidx_last = i; + if (++i == scctx->isc_ntxd[0]) { + i = 0; + } + } + if (txd_flags) { + txr->tx_rsq[txr->tx_rs_pidx] = pidx_last; + txr->tx_rs_pidx = (txr->tx_rs_pidx+1) & (ntxd-1); + MPASS(txr->tx_rs_pidx != txr->tx_rs_cidx); + } + + txd->read.cmd_type_len |= htole32(IGC_ADVTXD_DCMD_EOP | txd_flags); + pi->ipi_new_pidx = i; + + return (0); +} + +static void +igc_isc_txd_flush(void *arg, uint16_t txqid, qidx_t pidx) +{ + struct igc_adapter *adapter = arg; + struct igc_tx_queue *que = &adapter->tx_queues[txqid]; + struct tx_ring *txr = &que->txr; + + IGC_WRITE_REG(&adapter->hw, IGC_TDT(txr->me), pidx); +} + +static int +igc_isc_txd_credits_update(void *arg, uint16_t txqid, bool clear) +{ + struct igc_adapter *adapter = arg; + if_softc_ctx_t scctx = adapter->shared; + struct igc_tx_queue *que = &adapter->tx_queues[txqid]; + struct tx_ring *txr = &que->txr; + + qidx_t processed = 0; + int updated; + qidx_t cur, prev, ntxd, rs_cidx; + int32_t delta; + uint8_t status; + + rs_cidx = txr->tx_rs_cidx; + if (rs_cidx == txr->tx_rs_pidx) + return (0); + cur = txr->tx_rsq[rs_cidx]; + status = ((union igc_adv_tx_desc *)&txr->tx_base[cur])->wb.status; + updated = !!(status & IGC_TXD_STAT_DD); + + if (!updated) + return (0); + + /* If clear is false just let caller know that there + * are descriptors to reclaim */ + if (!clear) + return (1); + + prev = txr->tx_cidx_processed; + ntxd = scctx->isc_ntxd[0]; + do { + MPASS(prev != cur); + delta = (int32_t)cur - (int32_t)prev; + if (delta < 0) + delta += ntxd; + MPASS(delta > 0); + + processed += delta; + prev = cur; + rs_cidx = (rs_cidx + 1) & (ntxd-1); + if (rs_cidx == txr->tx_rs_pidx) + break; + cur = txr->tx_rsq[rs_cidx]; + status = ((union igc_adv_tx_desc *)&txr->tx_base[cur])->wb.status; + } while ((status & IGC_TXD_STAT_DD)); + + txr->tx_rs_cidx = rs_cidx; + txr->tx_cidx_processed = prev; + return (processed); +} + +static void +igc_isc_rxd_refill(void *arg, if_rxd_update_t iru) +{ + struct igc_adapter *sc = arg; + if_softc_ctx_t scctx = sc->shared; + uint16_t rxqid = iru->iru_qsidx; + struct igc_rx_queue *que = &sc->rx_queues[rxqid]; + union igc_adv_rx_desc *rxd; + struct rx_ring *rxr = &que->rxr; + uint64_t *paddrs; + uint32_t next_pidx, pidx; + uint16_t count; + int i; + + paddrs = iru->iru_paddrs; + pidx = iru->iru_pidx; + count = iru->iru_count; + + for (i = 0, next_pidx = pidx; i < count; i++) { + rxd = (union igc_adv_rx_desc *)&rxr->rx_base[next_pidx]; + + rxd->read.pkt_addr = htole64(paddrs[i]); + if (++next_pidx == scctx->isc_nrxd[0]) + next_pidx = 0; + } +} + +static void +igc_isc_rxd_flush(void *arg, uint16_t rxqid, uint8_t flid __unused, qidx_t pidx) +{ + struct igc_adapter *sc = arg; + struct igc_rx_queue *que = &sc->rx_queues[rxqid]; + struct rx_ring *rxr = &que->rxr; + + IGC_WRITE_REG(&sc->hw, IGC_RDT(rxr->me), pidx); +} + +static int +igc_isc_rxd_available(void *arg, uint16_t rxqid, qidx_t idx, qidx_t budget) +{ + struct igc_adapter *sc = arg; + if_softc_ctx_t scctx = sc->shared; + struct igc_rx_queue *que = &sc->rx_queues[rxqid]; + struct rx_ring *rxr = &que->rxr; + union igc_adv_rx_desc *rxd; + u32 staterr = 0; + int cnt, i; + + for (cnt = 0, i = idx; cnt < scctx->isc_nrxd[0] && cnt <= budget;) { + rxd = (union igc_adv_rx_desc *)&rxr->rx_base[i]; + staterr = le32toh(rxd->wb.upper.status_error); + + if ((staterr & IGC_RXD_STAT_DD) == 0) + break; + if (++i == scctx->isc_nrxd[0]) + i = 0; + if (staterr & IGC_RXD_STAT_EOP) + cnt++; + } + return (cnt); +} + +/**************************************************************** + * Routine sends data which has been dma'ed into host memory + * to upper layer. Initialize ri structure. + * + * Returns 0 upon success, errno on failure + ***************************************************************/ + +static int +igc_isc_rxd_pkt_get(void *arg, if_rxd_info_t ri) +{ + struct igc_adapter *adapter = arg; + if_softc_ctx_t scctx = adapter->shared; + struct igc_rx_queue *que = &adapter->rx_queues[ri->iri_qsidx]; + struct rx_ring *rxr = &que->rxr; + struct ifnet *ifp = iflib_get_ifp(adapter->ctx); + union igc_adv_rx_desc *rxd; + + u16 pkt_info, len; + u16 vtag = 0; + u32 ptype; + u32 staterr = 0; + bool eop; + int i = 0; + int cidx = ri->iri_cidx; + + do { + rxd = (union igc_adv_rx_desc *)&rxr->rx_base[cidx]; + staterr = le32toh(rxd->wb.upper.status_error); + pkt_info = le16toh(rxd->wb.lower.lo_dword.hs_rss.pkt_info); + + MPASS ((staterr & IGC_RXD_STAT_DD) != 0); + + len = le16toh(rxd->wb.upper.length); + ptype = le32toh(rxd->wb.lower.lo_dword.data) & IGC_PKTTYPE_MASK; + + ri->iri_len += len; + rxr->rx_bytes += ri->iri_len; + + rxd->wb.upper.status_error = 0; + eop = ((staterr & IGC_RXD_STAT_EOP) == IGC_RXD_STAT_EOP); + + vtag = le16toh(rxd->wb.upper.vlan); + + /* Make sure bad packets are discarded */ + if (eop && ((staterr & IGC_RXDEXT_STATERR_RXE) != 0)) { + adapter->dropped_pkts++; + ++rxr->rx_discarded; + return (EBADMSG); + } + ri->iri_frags[i].irf_flid = 0; + ri->iri_frags[i].irf_idx = cidx; + ri->iri_frags[i].irf_len = len; + + if (++cidx == scctx->isc_nrxd[0]) + cidx = 0; +#ifdef notyet + if (rxr->hdr_split == true) { + ri->iri_frags[i].irf_flid = 1; + ri->iri_frags[i].irf_idx = cidx; + if (++cidx == scctx->isc_nrxd[0]) + cidx = 0; + } +#endif + i++; + } while (!eop); + + rxr->rx_packets++; + + if ((ifp->if_capenable & IFCAP_RXCSUM) != 0) + igc_rx_checksum(staterr, ri, ptype); + + if ((ifp->if_capenable & IFCAP_VLAN_HWTAGGING) != 0 && + (staterr & IGC_RXD_STAT_VP) != 0) { + ri->iri_vtag = vtag; + ri->iri_flags |= M_VLANTAG; + } + ri->iri_flowid = + le32toh(rxd->wb.lower.hi_dword.rss); + ri->iri_rsstype = igc_determine_rsstype(pkt_info); + ri->iri_nfrags = i; + + return (0); +} + +/********************************************************************* + * + * 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. + * + *********************************************************************/ +static void +igc_rx_checksum(u32 staterr, if_rxd_info_t ri, u32 ptype) +{ + u16 status = (u16)staterr; + u8 errors = (u8) (staterr >> 24); + + /* Ignore Checksum bit is set */ + if (status & IGC_RXD_STAT_IXSM) { + ri->iri_csum_flags = 0; + return; + } + + if (status & (IGC_RXD_STAT_TCPCS | IGC_RXD_STAT_UDPCS)) { + u64 type = (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); + /* Did it pass? */ + if (!(errors & IGC_RXD_ERR_TCPE)) { + ri->iri_csum_flags |= type; + ri->iri_csum_data = htons(0xffff); + } + } + return; +} + +/******************************************************************** + * + * Parse the packet type to determine the appropriate hash + * + ******************************************************************/ +static int +igc_determine_rsstype(u16 pkt_info) +{ + switch (pkt_info & IGC_RXDADV_RSSTYPE_MASK) { + case IGC_RXDADV_RSSTYPE_IPV4_TCP: + return M_HASHTYPE_RSS_TCP_IPV4; + case IGC_RXDADV_RSSTYPE_IPV4: + return M_HASHTYPE_RSS_IPV4; + case IGC_RXDADV_RSSTYPE_IPV6_TCP: + return M_HASHTYPE_RSS_TCP_IPV6; + case IGC_RXDADV_RSSTYPE_IPV6_EX: + return M_HASHTYPE_RSS_IPV6_EX; + case IGC_RXDADV_RSSTYPE_IPV6: + return M_HASHTYPE_RSS_IPV6; + case IGC_RXDADV_RSSTYPE_IPV6_TCP_EX: + return M_HASHTYPE_RSS_TCP_IPV6_EX; + default: + return M_HASHTYPE_OPAQUE; + } +} diff --git a/sys/i386/conf/GENERIC b/sys/i386/conf/GENERIC --- a/sys/i386/conf/GENERIC +++ b/sys/i386/conf/GENERIC @@ -221,6 +221,7 @@ # PCI/PCI-X/PCIe Ethernet NICs that use iflib infrastructure device iflib +device igc # Intel I225 2.5G Ethernet device em # Intel PRO/1000 Gigabit Ethernet Family device vmx # VMware VMXNET3 Ethernet diff --git a/sys/i386/conf/NOTES b/sys/i386/conf/NOTES --- a/sys/i386/conf/NOTES +++ b/sys/i386/conf/NOTES @@ -496,6 +496,7 @@ # V.35/RS-232/RS-530/RS-449/X.21/G.703/E1/E3/T3/STS-1 # serial adaptor (requires sppp (default), or NETGRAPH if # NETGRAPH_CRONYX is configured) +# igc: Intel I225 2.5G Ethernet adapter # ipw: Intel PRO/Wireless 2100 IEEE 802.11 adapter # iwi: Intel PRO/Wireless 2200BG/2225BG/2915ABG IEEE 802.11 adapters # Requires the iwi firmware module @@ -526,6 +527,7 @@ envvar hint.ed.0.port="0x280" envvar hint.ed.0.irq="5" envvar hint.ed.0.maddr="0xd8000" +device igc # Intel I225 2.5G Ethernet device ipw # Intel 2100 wireless NICs. device iwi # Intel 2200BG/2225BG/2915ABG wireless NICs. device iwn # Intel 4965/1000/5000/6000 wireless NICs. diff --git a/sys/modules/Makefile b/sys/modules/Makefile --- a/sys/modules/Makefile +++ b/sys/modules/Makefile @@ -164,6 +164,7 @@ if_vlan \ if_vxlan \ iflib \ + ${_igc} \ ${_iir} \ imgact_binmisc \ ${_intelspi} \ @@ -634,6 +635,7 @@ _et= et _ftwd= ftwd _exca= exca +_igc= igc _io= io _itwd= itwd _ix= ix diff --git a/sys/modules/igc/Makefile b/sys/modules/igc/Makefile new file mode 100644 --- /dev/null +++ b/sys/modules/igc/Makefile @@ -0,0 +1,11 @@ +# $FreeBSD$ + +.PATH: ${SRCTOP}/sys/dev/igc + +KMOD = if_igc +SRCS = device_if.h bus_if.h pci_if.h ifdi_if.h +SRCS += opt_ddb.h opt_inet.h opt_inet6.h opt_rss.h +SRCS += if_igc.c igc_api.c igc_base.c igc_i225.c igc_mac.c igc_nvm.c +SRCS += igc_phy.c igc_txrx.c + +.include