diff --git a/sys/dev/e1000/if_em.c b/sys/dev/e1000/if_em.c index 3aae0ef4377e..8cf130048128 100644 --- a/sys/dev/e1000/if_em.c +++ b/sys/dev/e1000/if_em.c @@ -1,5095 +1,5094 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2016 Nicole Graziano * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 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 "if_em.h" #include #include #define em_mac_min e1000_82571 #define igb_mac_min e1000_82575 /********************************************************************* * Driver version: *********************************************************************/ char em_driver_version[] = "7.7.8-fbsd"; char igb_driver_version[] = "2.5.19-fbsd"; /********************************************************************* * PCI Device ID Table * * Used by probe to select devices to load on * Last field stores an index into e1000_strings * Last entry must be all 0s * * { Vendor ID, Device ID, SubVendor ID, SubDevice ID, String Index } *********************************************************************/ static pci_vendor_info_t em_vendor_info_array[] = { /* Intel(R) - lem-class legacy devices */ PVID(0x8086, E1000_DEV_ID_82540EM, "Intel(R) Legacy PRO/1000 MT 82540EM"), PVID(0x8086, E1000_DEV_ID_82540EM_LOM, "Intel(R) Legacy PRO/1000 MT 82540EM (LOM)"), PVID(0x8086, E1000_DEV_ID_82540EP, "Intel(R) Legacy PRO/1000 MT 82540EP"), PVID(0x8086, E1000_DEV_ID_82540EP_LOM, "Intel(R) Legacy PRO/1000 MT 82540EP (LOM)"), PVID(0x8086, E1000_DEV_ID_82540EP_LP, "Intel(R) Legacy PRO/1000 MT 82540EP (Mobile)"), PVID(0x8086, E1000_DEV_ID_82541EI, "Intel(R) Legacy PRO/1000 MT 82541EI (Copper)"), PVID(0x8086, E1000_DEV_ID_82541ER, "Intel(R) Legacy PRO/1000 82541ER"), PVID(0x8086, E1000_DEV_ID_82541ER_LOM, "Intel(R) Legacy PRO/1000 MT 82541ER"), PVID(0x8086, E1000_DEV_ID_82541EI_MOBILE, "Intel(R) Legacy PRO/1000 MT 82541EI (Mobile)"), PVID(0x8086, E1000_DEV_ID_82541GI, "Intel(R) Legacy PRO/1000 MT 82541GI"), PVID(0x8086, E1000_DEV_ID_82541GI_LF, "Intel(R) Legacy PRO/1000 GT 82541PI"), PVID(0x8086, E1000_DEV_ID_82541GI_MOBILE, "Intel(R) Legacy PRO/1000 MT 82541GI (Mobile)"), PVID(0x8086, E1000_DEV_ID_82542, "Intel(R) Legacy PRO/1000 82542 (Fiber)"), PVID(0x8086, E1000_DEV_ID_82543GC_FIBER, "Intel(R) Legacy PRO/1000 F 82543GC (Fiber)"), PVID(0x8086, E1000_DEV_ID_82543GC_COPPER, "Intel(R) Legacy PRO/1000 T 82543GC (Copper)"), PVID(0x8086, E1000_DEV_ID_82544EI_COPPER, "Intel(R) Legacy PRO/1000 XT 82544EI (Copper)"), PVID(0x8086, E1000_DEV_ID_82544EI_FIBER, "Intel(R) Legacy PRO/1000 XF 82544EI (Fiber)"), PVID(0x8086, E1000_DEV_ID_82544GC_COPPER, "Intel(R) Legacy PRO/1000 T 82544GC (Copper)"), PVID(0x8086, E1000_DEV_ID_82544GC_LOM, "Intel(R) Legacy PRO/1000 XT 82544GC (LOM)"), PVID(0x8086, E1000_DEV_ID_82545EM_COPPER, "Intel(R) Legacy PRO/1000 MT 82545EM (Copper)"), PVID(0x8086, E1000_DEV_ID_82545EM_FIBER, "Intel(R) Legacy PRO/1000 MF 82545EM (Fiber)"), PVID(0x8086, E1000_DEV_ID_82545GM_COPPER, "Intel(R) Legacy PRO/1000 MT 82545GM (Copper)"), PVID(0x8086, E1000_DEV_ID_82545GM_FIBER, "Intel(R) Legacy PRO/1000 MF 82545GM (Fiber)"), PVID(0x8086, E1000_DEV_ID_82545GM_SERDES, "Intel(R) Legacy PRO/1000 MB 82545GM (SERDES)"), PVID(0x8086, E1000_DEV_ID_82546EB_COPPER, "Intel(R) Legacy PRO/1000 MT 82546EB (Copper)"), PVID(0x8086, E1000_DEV_ID_82546EB_FIBER, "Intel(R) Legacy PRO/1000 MF 82546EB (Fiber)"), PVID(0x8086, E1000_DEV_ID_82546EB_QUAD_COPPER, "Intel(R) Legacy PRO/1000 MT 82546EB (Quad Copper"), PVID(0x8086, E1000_DEV_ID_82546GB_COPPER, "Intel(R) Legacy PRO/1000 MT 82546GB (Copper)"), PVID(0x8086, E1000_DEV_ID_82546GB_FIBER, "Intel(R) Legacy PRO/1000 MF 82546GB (Fiber)"), PVID(0x8086, E1000_DEV_ID_82546GB_SERDES, "Intel(R) Legacy PRO/1000 MB 82546GB (SERDES)"), PVID(0x8086, E1000_DEV_ID_82546GB_PCIE, "Intel(R) Legacy PRO/1000 P 82546GB (PCIe)"), PVID(0x8086, E1000_DEV_ID_82546GB_QUAD_COPPER, "Intel(R) Legacy PRO/1000 GT 82546GB (Quad Copper)"), PVID(0x8086, E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3, "Intel(R) Legacy PRO/1000 GT 82546GB (Quad Copper)"), PVID(0x8086, E1000_DEV_ID_82547EI, "Intel(R) Legacy PRO/1000 CT 82547EI"), PVID(0x8086, E1000_DEV_ID_82547EI_MOBILE, "Intel(R) Legacy PRO/1000 CT 82547EI (Mobile)"), PVID(0x8086, E1000_DEV_ID_82547GI, "Intel(R) Legacy PRO/1000 CT 82547GI"), /* Intel(R) - em-class devices */ PVID(0x8086, E1000_DEV_ID_82571EB_COPPER, "Intel(R) PRO/1000 PT 82571EB/82571GB (Copper)"), PVID(0x8086, E1000_DEV_ID_82571EB_FIBER, "Intel(R) PRO/1000 PF 82571EB/82571GB (Fiber)"), PVID(0x8086, E1000_DEV_ID_82571EB_SERDES, "Intel(R) PRO/1000 PB 82571EB (SERDES)"), PVID(0x8086, E1000_DEV_ID_82571EB_SERDES_DUAL, "Intel(R) PRO/1000 82571EB (Dual Mezzanine)"), PVID(0x8086, E1000_DEV_ID_82571EB_SERDES_QUAD, "Intel(R) PRO/1000 82571EB (Quad Mezzanine)"), PVID(0x8086, E1000_DEV_ID_82571EB_QUAD_COPPER, "Intel(R) PRO/1000 PT 82571EB/82571GB (Quad Copper)"), PVID(0x8086, E1000_DEV_ID_82571EB_QUAD_COPPER_LP, "Intel(R) PRO/1000 PT 82571EB/82571GB (Quad Copper)"), PVID(0x8086, E1000_DEV_ID_82571EB_QUAD_FIBER, "Intel(R) PRO/1000 PF 82571EB (Quad Fiber)"), PVID(0x8086, E1000_DEV_ID_82571PT_QUAD_COPPER, "Intel(R) PRO/1000 PT 82571PT (Quad Copper)"), PVID(0x8086, E1000_DEV_ID_82572EI, "Intel(R) PRO/1000 PT 82572EI (Copper)"), PVID(0x8086, E1000_DEV_ID_82572EI_COPPER, "Intel(R) PRO/1000 PT 82572EI (Copper)"), PVID(0x8086, E1000_DEV_ID_82572EI_FIBER, "Intel(R) PRO/1000 PF 82572EI (Fiber)"), PVID(0x8086, E1000_DEV_ID_82572EI_SERDES, "Intel(R) PRO/1000 82572EI (SERDES)"), PVID(0x8086, E1000_DEV_ID_82573E, "Intel(R) PRO/1000 82573E (Copper)"), PVID(0x8086, E1000_DEV_ID_82573E_IAMT, "Intel(R) PRO/1000 82573E AMT (Copper)"), PVID(0x8086, E1000_DEV_ID_82573L, "Intel(R) PRO/1000 82573L"), PVID(0x8086, E1000_DEV_ID_82583V, "Intel(R) 82583V"), PVID(0x8086, E1000_DEV_ID_80003ES2LAN_COPPER_SPT, "Intel(R) 80003ES2LAN (Copper)"), PVID(0x8086, E1000_DEV_ID_80003ES2LAN_SERDES_SPT, "Intel(R) 80003ES2LAN (SERDES)"), PVID(0x8086, E1000_DEV_ID_80003ES2LAN_COPPER_DPT, "Intel(R) 80003ES2LAN (Dual Copper)"), PVID(0x8086, E1000_DEV_ID_80003ES2LAN_SERDES_DPT, "Intel(R) 80003ES2LAN (Dual SERDES)"), PVID(0x8086, E1000_DEV_ID_ICH8_IGP_M_AMT, "Intel(R) 82566MM ICH8 AMT (Mobile)"), PVID(0x8086, E1000_DEV_ID_ICH8_IGP_AMT, "Intel(R) 82566DM ICH8 AMT"), PVID(0x8086, E1000_DEV_ID_ICH8_IGP_C, "Intel(R) 82566DC ICH8"), PVID(0x8086, E1000_DEV_ID_ICH8_IFE, "Intel(R) 82562V ICH8"), PVID(0x8086, E1000_DEV_ID_ICH8_IFE_GT, "Intel(R) 82562GT ICH8"), PVID(0x8086, E1000_DEV_ID_ICH8_IFE_G, "Intel(R) 82562G ICH8"), PVID(0x8086, E1000_DEV_ID_ICH8_IGP_M, "Intel(R) 82566MC ICH8"), PVID(0x8086, E1000_DEV_ID_ICH8_82567V_3, "Intel(R) 82567V-3 ICH8"), PVID(0x8086, E1000_DEV_ID_ICH9_IGP_M_AMT, "Intel(R) 82567LM ICH9 AMT"), PVID(0x8086, E1000_DEV_ID_ICH9_IGP_AMT, "Intel(R) 82566DM-2 ICH9 AMT"), PVID(0x8086, E1000_DEV_ID_ICH9_IGP_C, "Intel(R) 82566DC-2 ICH9"), PVID(0x8086, E1000_DEV_ID_ICH9_IGP_M, "Intel(R) 82567LF ICH9"), PVID(0x8086, E1000_DEV_ID_ICH9_IGP_M_V, "Intel(R) 82567V ICH9"), PVID(0x8086, E1000_DEV_ID_ICH9_IFE, "Intel(R) 82562V-2 ICH9"), PVID(0x8086, E1000_DEV_ID_ICH9_IFE_GT, "Intel(R) 82562GT-2 ICH9"), PVID(0x8086, E1000_DEV_ID_ICH9_IFE_G, "Intel(R) 82562G-2 ICH9"), PVID(0x8086, E1000_DEV_ID_ICH9_BM, "Intel(R) 82567LM-4 ICH9"), PVID(0x8086, E1000_DEV_ID_82574L, "Intel(R) Gigabit CT 82574L"), PVID(0x8086, E1000_DEV_ID_82574LA, "Intel(R) 82574L-Apple"), PVID(0x8086, E1000_DEV_ID_ICH10_R_BM_LM, "Intel(R) 82567LM-2 ICH10"), PVID(0x8086, E1000_DEV_ID_ICH10_R_BM_LF, "Intel(R) 82567LF-2 ICH10"), PVID(0x8086, E1000_DEV_ID_ICH10_R_BM_V, "Intel(R) 82567V-2 ICH10"), PVID(0x8086, E1000_DEV_ID_ICH10_D_BM_LM, "Intel(R) 82567LM-3 ICH10"), PVID(0x8086, E1000_DEV_ID_ICH10_D_BM_LF, "Intel(R) 82567LF-3 ICH10"), PVID(0x8086, E1000_DEV_ID_ICH10_D_BM_V, "Intel(R) 82567V-4 ICH10"), PVID(0x8086, E1000_DEV_ID_PCH_M_HV_LM, "Intel(R) 82577LM"), PVID(0x8086, E1000_DEV_ID_PCH_M_HV_LC, "Intel(R) 82577LC"), PVID(0x8086, E1000_DEV_ID_PCH_D_HV_DM, "Intel(R) 82578DM"), PVID(0x8086, E1000_DEV_ID_PCH_D_HV_DC, "Intel(R) 82578DC"), PVID(0x8086, E1000_DEV_ID_PCH2_LV_LM, "Intel(R) 82579LM"), PVID(0x8086, E1000_DEV_ID_PCH2_LV_V, "Intel(R) 82579V"), PVID(0x8086, E1000_DEV_ID_PCH_LPT_I217_LM, "Intel(R) I217-LM LPT"), PVID(0x8086, E1000_DEV_ID_PCH_LPT_I217_V, "Intel(R) I217-V LPT"), PVID(0x8086, E1000_DEV_ID_PCH_LPTLP_I218_LM, "Intel(R) I218-LM LPTLP"), PVID(0x8086, E1000_DEV_ID_PCH_LPTLP_I218_V, "Intel(R) I218-V LPTLP"), PVID(0x8086, E1000_DEV_ID_PCH_I218_LM2, "Intel(R) I218-LM (2)"), PVID(0x8086, E1000_DEV_ID_PCH_I218_V2, "Intel(R) I218-V (2)"), PVID(0x8086, E1000_DEV_ID_PCH_I218_LM3, "Intel(R) I218-LM (3)"), PVID(0x8086, E1000_DEV_ID_PCH_I218_V3, "Intel(R) I218-V (3)"), PVID(0x8086, E1000_DEV_ID_PCH_SPT_I219_LM, "Intel(R) I219-LM SPT"), PVID(0x8086, E1000_DEV_ID_PCH_SPT_I219_V, "Intel(R) I219-V SPT"), PVID(0x8086, E1000_DEV_ID_PCH_SPT_I219_LM2, "Intel(R) I219-LM SPT-H(2)"), PVID(0x8086, E1000_DEV_ID_PCH_SPT_I219_V2, "Intel(R) I219-V SPT-H(2)"), PVID(0x8086, E1000_DEV_ID_PCH_LBG_I219_LM3, "Intel(R) I219-LM LBG(3)"), PVID(0x8086, E1000_DEV_ID_PCH_SPT_I219_LM4, "Intel(R) I219-LM SPT(4)"), PVID(0x8086, E1000_DEV_ID_PCH_SPT_I219_V4, "Intel(R) I219-V SPT(4)"), PVID(0x8086, E1000_DEV_ID_PCH_SPT_I219_LM5, "Intel(R) I219-LM SPT(5)"), PVID(0x8086, E1000_DEV_ID_PCH_SPT_I219_V5, "Intel(R) I219-V SPT(5)"), PVID(0x8086, E1000_DEV_ID_PCH_CNP_I219_LM6, "Intel(R) I219-LM CNP(6)"), PVID(0x8086, E1000_DEV_ID_PCH_CNP_I219_V6, "Intel(R) I219-V CNP(6)"), PVID(0x8086, E1000_DEV_ID_PCH_CNP_I219_LM7, "Intel(R) I219-LM CNP(7)"), PVID(0x8086, E1000_DEV_ID_PCH_CNP_I219_V7, "Intel(R) I219-V CNP(7)"), PVID(0x8086, E1000_DEV_ID_PCH_ICP_I219_LM8, "Intel(R) I219-LM ICP(8)"), PVID(0x8086, E1000_DEV_ID_PCH_ICP_I219_V8, "Intel(R) I219-V ICP(8)"), PVID(0x8086, E1000_DEV_ID_PCH_ICP_I219_LM9, "Intel(R) I219-LM ICP(9)"), PVID(0x8086, E1000_DEV_ID_PCH_ICP_I219_V9, "Intel(R) I219-V ICP(9)"), PVID(0x8086, E1000_DEV_ID_PCH_CMP_I219_LM10, "Intel(R) I219-LM CMP(10)"), PVID(0x8086, E1000_DEV_ID_PCH_CMP_I219_V10, "Intel(R) I219-V CMP(10)"), PVID(0x8086, E1000_DEV_ID_PCH_CMP_I219_LM11, "Intel(R) I219-LM CMP(11)"), PVID(0x8086, E1000_DEV_ID_PCH_CMP_I219_V11, "Intel(R) I219-V CMP(11)"), PVID(0x8086, E1000_DEV_ID_PCH_CMP_I219_LM12, "Intel(R) I219-LM CMP(12)"), PVID(0x8086, E1000_DEV_ID_PCH_CMP_I219_V12, "Intel(R) I219-V CMP(12)"), PVID(0x8086, E1000_DEV_ID_PCH_TGP_I219_LM13, "Intel(R) I219-LM TGP(13)"), PVID(0x8086, E1000_DEV_ID_PCH_TGP_I219_V13, "Intel(R) I219-V TGP(13)"), PVID(0x8086, E1000_DEV_ID_PCH_TGP_I219_LM14, "Intel(R) I219-LM TGP(14)"), PVID(0x8086, E1000_DEV_ID_PCH_TGP_I219_V14, "Intel(R) I219-V GTP(14)"), PVID(0x8086, E1000_DEV_ID_PCH_TGP_I219_LM15, "Intel(R) I219-LM TGP(15)"), PVID(0x8086, E1000_DEV_ID_PCH_TGP_I219_V15, "Intel(R) I219-V TGP(15)"), PVID(0x8086, E1000_DEV_ID_PCH_ADL_I219_LM16, "Intel(R) I219-LM ADL(16)"), PVID(0x8086, E1000_DEV_ID_PCH_ADL_I219_V16, "Intel(R) I219-V ADL(16)"), PVID(0x8086, E1000_DEV_ID_PCH_ADL_I219_LM17, "Intel(R) I219-LM ADL(17)"), PVID(0x8086, E1000_DEV_ID_PCH_ADL_I219_V17, "Intel(R) I219-V ADL(17)"), PVID(0x8086, E1000_DEV_ID_PCH_MTP_I219_LM18, "Intel(R) I219-LM MTP(18)"), PVID(0x8086, E1000_DEV_ID_PCH_MTP_I219_V18, "Intel(R) I219-V MTP(18)"), PVID(0x8086, E1000_DEV_ID_PCH_MTP_I219_LM19, "Intel(R) I219-LM MTP(19)"), PVID(0x8086, E1000_DEV_ID_PCH_MTP_I219_V19, "Intel(R) I219-V MTP(19)"), PVID(0x8086, E1000_DEV_ID_PCH_LNL_I219_LM20, "Intel(R) I219-LM LNL(20)"), PVID(0x8086, E1000_DEV_ID_PCH_LNL_I219_V20, "Intel(R) I219-V LNL(20)"), PVID(0x8086, E1000_DEV_ID_PCH_LNL_I219_LM21, "Intel(R) I219-LM LNL(21)"), PVID(0x8086, E1000_DEV_ID_PCH_LNL_I219_V21, "Intel(R) I219-V LNL(21)"), PVID(0x8086, E1000_DEV_ID_PCH_RPL_I219_LM22, "Intel(R) I219-LM RPL(22)"), PVID(0x8086, E1000_DEV_ID_PCH_RPL_I219_V22, "Intel(R) I219-V RPL(22)"), PVID(0x8086, E1000_DEV_ID_PCH_RPL_I219_LM23, "Intel(R) I219-LM RPL(23)"), PVID(0x8086, E1000_DEV_ID_PCH_RPL_I219_V23, "Intel(R) I219-V RPL(23)"), PVID(0x8086, E1000_DEV_ID_PCH_ARL_I219_LM24, "Intel(R) I219-LM ARL(24)"), PVID(0x8086, E1000_DEV_ID_PCH_ARL_I219_V24, "Intel(R) I219-V ARL(24)"), PVID(0x8086, E1000_DEV_ID_PCH_PTP_I219_LM25, "Intel(R) I219-LM PTP(25)"), PVID(0x8086, E1000_DEV_ID_PCH_PTP_I219_V25, "Intel(R) I219-V PTP(25)"), PVID(0x8086, E1000_DEV_ID_PCH_PTP_I219_LM26, "Intel(R) I219-LM PTP(26)"), PVID(0x8086, E1000_DEV_ID_PCH_PTP_I219_V26, "Intel(R) I219-V PTP(26)"), PVID(0x8086, E1000_DEV_ID_PCH_PTP_I219_LM27, "Intel(R) I219-LM PTP(27)"), PVID(0x8086, E1000_DEV_ID_PCH_PTP_I219_V27, "Intel(R) I219-V PTP(27)"), /* required last entry */ PVID_END }; static pci_vendor_info_t igb_vendor_info_array[] = { /* Intel(R) - igb-class devices */ PVID(0x8086, E1000_DEV_ID_82575EB_COPPER, "Intel(R) PRO/1000 82575EB (Copper)"), PVID(0x8086, E1000_DEV_ID_82575EB_FIBER_SERDES, "Intel(R) PRO/1000 82575EB (SERDES)"), PVID(0x8086, E1000_DEV_ID_82575GB_QUAD_COPPER, "Intel(R) PRO/1000 VT 82575GB (Quad Copper)"), PVID(0x8086, E1000_DEV_ID_82576, "Intel(R) PRO/1000 82576"), PVID(0x8086, E1000_DEV_ID_82576_NS, "Intel(R) PRO/1000 82576NS"), PVID(0x8086, E1000_DEV_ID_82576_NS_SERDES, "Intel(R) PRO/1000 82576NS (SERDES)"), PVID(0x8086, E1000_DEV_ID_82576_FIBER, "Intel(R) PRO/1000 EF 82576 (Dual Fiber)"), PVID(0x8086, E1000_DEV_ID_82576_SERDES, "Intel(R) PRO/1000 82576 (Dual SERDES)"), PVID(0x8086, E1000_DEV_ID_82576_SERDES_QUAD, "Intel(R) PRO/1000 ET 82576 (Quad SERDES)"), PVID(0x8086, E1000_DEV_ID_82576_QUAD_COPPER, "Intel(R) PRO/1000 ET 82576 (Quad Copper)"), PVID(0x8086, E1000_DEV_ID_82576_QUAD_COPPER_ET2, "Intel(R) PRO/1000 ET(2) 82576 (Quad Copper)"), PVID(0x8086, E1000_DEV_ID_82576_VF, "Intel(R) PRO/1000 82576 Virtual Function"), PVID(0x8086, E1000_DEV_ID_82580_COPPER, "Intel(R) I340 82580 (Copper)"), PVID(0x8086, E1000_DEV_ID_82580_FIBER, "Intel(R) I340 82580 (Fiber)"), PVID(0x8086, E1000_DEV_ID_82580_SERDES, "Intel(R) I340 82580 (SERDES)"), PVID(0x8086, E1000_DEV_ID_82580_SGMII, "Intel(R) I340 82580 (SGMII)"), PVID(0x8086, E1000_DEV_ID_82580_COPPER_DUAL, "Intel(R) I340-T2 82580 (Dual Copper)"), PVID(0x8086, E1000_DEV_ID_82580_QUAD_FIBER, "Intel(R) I340-F4 82580 (Quad Fiber)"), PVID(0x8086, E1000_DEV_ID_DH89XXCC_SERDES, "Intel(R) DH89XXCC (SERDES)"), PVID(0x8086, E1000_DEV_ID_DH89XXCC_SGMII, "Intel(R) I347-AT4 DH89XXCC"), PVID(0x8086, E1000_DEV_ID_DH89XXCC_SFP, "Intel(R) DH89XXCC (SFP)"), PVID(0x8086, E1000_DEV_ID_DH89XXCC_BACKPLANE, "Intel(R) DH89XXCC (Backplane)"), PVID(0x8086, E1000_DEV_ID_I350_COPPER, "Intel(R) I350 (Copper)"), PVID(0x8086, E1000_DEV_ID_I350_FIBER, "Intel(R) I350 (Fiber)"), PVID(0x8086, E1000_DEV_ID_I350_SERDES, "Intel(R) I350 (SERDES)"), PVID(0x8086, E1000_DEV_ID_I350_SGMII, "Intel(R) I350 (SGMII)"), PVID(0x8086, E1000_DEV_ID_I350_VF, "Intel(R) I350 Virtual Function"), PVID(0x8086, E1000_DEV_ID_I210_COPPER, "Intel(R) I210 (Copper)"), PVID(0x8086, E1000_DEV_ID_I210_COPPER_IT, "Intel(R) I210 IT (Copper)"), PVID(0x8086, E1000_DEV_ID_I210_COPPER_OEM1, "Intel(R) I210 (OEM)"), PVID(0x8086, E1000_DEV_ID_I210_COPPER_FLASHLESS, "Intel(R) I210 Flashless (Copper)"), PVID(0x8086, E1000_DEV_ID_I210_SERDES_FLASHLESS, "Intel(R) I210 Flashless (SERDES)"), PVID(0x8086, E1000_DEV_ID_I210_SGMII_FLASHLESS, "Intel(R) I210 Flashless (SGMII)"), PVID(0x8086, E1000_DEV_ID_I210_FIBER, "Intel(R) I210 (Fiber)"), PVID(0x8086, E1000_DEV_ID_I210_SERDES, "Intel(R) I210 (SERDES)"), PVID(0x8086, E1000_DEV_ID_I210_SGMII, "Intel(R) I210 (SGMII)"), PVID(0x8086, E1000_DEV_ID_I211_COPPER, "Intel(R) I211 (Copper)"), PVID(0x8086, E1000_DEV_ID_I354_BACKPLANE_1GBPS, "Intel(R) I354 (1.0 GbE Backplane)"), PVID(0x8086, E1000_DEV_ID_I354_BACKPLANE_2_5GBPS, "Intel(R) I354 (2.5 GbE Backplane)"), PVID(0x8086, E1000_DEV_ID_I354_SGMII, "Intel(R) I354 (SGMII)"), /* required last entry */ PVID_END }; /********************************************************************* * Function prototypes *********************************************************************/ static void *em_register(device_t); static void *igb_register(device_t); static int em_if_attach_pre(if_ctx_t); static int em_if_attach_post(if_ctx_t); static int em_if_detach(if_ctx_t); static int em_if_shutdown(if_ctx_t); static int em_if_suspend(if_ctx_t); static int em_if_resume(if_ctx_t); static int em_if_tx_queues_alloc(if_ctx_t, caddr_t *, uint64_t *, int, int); static int em_if_rx_queues_alloc(if_ctx_t, caddr_t *, uint64_t *, int, int); static void em_if_queues_free(if_ctx_t); static uint64_t em_if_get_counter(if_ctx_t, ift_counter); static void em_if_init(if_ctx_t); static void em_if_stop(if_ctx_t); static void em_if_media_status(if_ctx_t, struct ifmediareq *); static int em_if_media_change(if_ctx_t); static int em_if_mtu_set(if_ctx_t, uint32_t); static void em_if_timer(if_ctx_t, uint16_t); static void em_if_vlan_register(if_ctx_t, u16); static void em_if_vlan_unregister(if_ctx_t, u16); static void em_if_watchdog_reset(if_ctx_t); static bool em_if_needs_restart(if_ctx_t, enum iflib_restart_event); static void em_identify_hardware(if_ctx_t); static int em_allocate_pci_resources(if_ctx_t); static void em_free_pci_resources(if_ctx_t); static void em_reset(if_ctx_t); static int em_setup_interface(if_ctx_t); static int em_setup_msix(if_ctx_t); static void em_initialize_transmit_unit(if_ctx_t); static void em_initialize_receive_unit(if_ctx_t); static void em_if_intr_enable(if_ctx_t); static void em_if_intr_disable(if_ctx_t); static void igb_if_intr_enable(if_ctx_t); static void igb_if_intr_disable(if_ctx_t); static int em_if_rx_queue_intr_enable(if_ctx_t, uint16_t); static int em_if_tx_queue_intr_enable(if_ctx_t, uint16_t); static int igb_if_rx_queue_intr_enable(if_ctx_t, uint16_t); static int igb_if_tx_queue_intr_enable(if_ctx_t, uint16_t); static void em_if_multi_set(if_ctx_t); static void em_if_update_admin_status(if_ctx_t); static void em_if_debug(if_ctx_t); static void em_update_stats_counters(struct e1000_softc *); static void em_add_hw_stats(struct e1000_softc *); static int em_if_set_promisc(if_ctx_t, int); static bool em_if_vlan_filter_capable(if_ctx_t); static bool em_if_vlan_filter_used(if_ctx_t); static void em_if_vlan_filter_enable(struct e1000_softc *); static void em_if_vlan_filter_disable(struct e1000_softc *); static void em_if_vlan_filter_write(struct e1000_softc *); static void em_setup_vlan_hw_support(if_ctx_t ctx); static int em_sysctl_nvm_info(SYSCTL_HANDLER_ARGS); static void em_print_nvm_info(struct e1000_softc *); static void em_fw_version_locked(if_ctx_t); static void em_sbuf_fw_version(struct e1000_fw_version *, struct sbuf *); static void em_print_fw_version(struct e1000_softc *); static int em_sysctl_print_fw_version(SYSCTL_HANDLER_ARGS); static int em_sysctl_debug_info(SYSCTL_HANDLER_ARGS); static int em_get_rs(SYSCTL_HANDLER_ARGS); static void em_print_debug_info(struct e1000_softc *); static int em_is_valid_ether_addr(u8 *); static bool em_automask_tso(if_ctx_t); static int em_sysctl_int_delay(SYSCTL_HANDLER_ARGS); static void em_add_int_delay_sysctl(struct e1000_softc *, const char *, const char *, struct em_int_delay_info *, int, int); /* Management and WOL Support */ static void em_init_manageability(struct e1000_softc *); static void em_release_manageability(struct e1000_softc *); static void em_get_hw_control(struct e1000_softc *); static void em_release_hw_control(struct e1000_softc *); static void em_get_wakeup(if_ctx_t); static void em_enable_wakeup(if_ctx_t); static int em_enable_phy_wakeup(struct e1000_softc *); static void em_disable_aspm(struct e1000_softc *); int em_intr(void *); /* MSI-X handlers */ static int em_if_msix_intr_assign(if_ctx_t, int); static int em_msix_link(void *); static void em_handle_link(void *); static void em_enable_vectors_82574(if_ctx_t); static int em_set_flowcntl(SYSCTL_HANDLER_ARGS); static int em_sysctl_eee(SYSCTL_HANDLER_ARGS); static void em_if_led_func(if_ctx_t, int); static int em_get_regs(SYSCTL_HANDLER_ARGS); static void lem_smartspeed(struct e1000_softc *); static void igb_configure_queues(struct e1000_softc *); static void em_flush_desc_rings(struct e1000_softc *); /********************************************************************* * FreeBSD Device Interface Entry Points *********************************************************************/ static device_method_t em_methods[] = { /* Device interface */ DEVMETHOD(device_register, em_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 device_method_t igb_methods[] = { /* Device interface */ DEVMETHOD(device_register, igb_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 em_driver = { "em", em_methods, sizeof(struct e1000_softc), }; static devclass_t em_devclass; DRIVER_MODULE(em, pci, em_driver, em_devclass, 0, 0); MODULE_DEPEND(em, pci, 1, 1, 1); MODULE_DEPEND(em, ether, 1, 1, 1); MODULE_DEPEND(em, iflib, 1, 1, 1); IFLIB_PNP_INFO(pci, em, em_vendor_info_array); static driver_t igb_driver = { "igb", igb_methods, sizeof(struct e1000_softc), }; static devclass_t igb_devclass; DRIVER_MODULE(igb, pci, igb_driver, igb_devclass, 0, 0); MODULE_DEPEND(igb, pci, 1, 1, 1); MODULE_DEPEND(igb, ether, 1, 1, 1); MODULE_DEPEND(igb, iflib, 1, 1, 1); IFLIB_PNP_INFO(pci, igb, igb_vendor_info_array); static device_method_t em_if_methods[] = { DEVMETHOD(ifdi_attach_pre, em_if_attach_pre), DEVMETHOD(ifdi_attach_post, em_if_attach_post), DEVMETHOD(ifdi_detach, em_if_detach), DEVMETHOD(ifdi_shutdown, em_if_shutdown), DEVMETHOD(ifdi_suspend, em_if_suspend), DEVMETHOD(ifdi_resume, em_if_resume), DEVMETHOD(ifdi_init, em_if_init), DEVMETHOD(ifdi_stop, em_if_stop), DEVMETHOD(ifdi_msix_intr_assign, em_if_msix_intr_assign), DEVMETHOD(ifdi_intr_enable, em_if_intr_enable), DEVMETHOD(ifdi_intr_disable, em_if_intr_disable), DEVMETHOD(ifdi_tx_queues_alloc, em_if_tx_queues_alloc), DEVMETHOD(ifdi_rx_queues_alloc, em_if_rx_queues_alloc), DEVMETHOD(ifdi_queues_free, em_if_queues_free), DEVMETHOD(ifdi_update_admin_status, em_if_update_admin_status), DEVMETHOD(ifdi_multi_set, em_if_multi_set), DEVMETHOD(ifdi_media_status, em_if_media_status), DEVMETHOD(ifdi_media_change, em_if_media_change), DEVMETHOD(ifdi_mtu_set, em_if_mtu_set), DEVMETHOD(ifdi_promisc_set, em_if_set_promisc), DEVMETHOD(ifdi_timer, em_if_timer), DEVMETHOD(ifdi_watchdog_reset, em_if_watchdog_reset), DEVMETHOD(ifdi_vlan_register, em_if_vlan_register), DEVMETHOD(ifdi_vlan_unregister, em_if_vlan_unregister), DEVMETHOD(ifdi_get_counter, em_if_get_counter), DEVMETHOD(ifdi_led_func, em_if_led_func), DEVMETHOD(ifdi_rx_queue_intr_enable, em_if_rx_queue_intr_enable), DEVMETHOD(ifdi_tx_queue_intr_enable, em_if_tx_queue_intr_enable), DEVMETHOD(ifdi_debug, em_if_debug), DEVMETHOD(ifdi_needs_restart, em_if_needs_restart), DEVMETHOD_END }; static driver_t em_if_driver = { "em_if", em_if_methods, sizeof(struct e1000_softc) }; static device_method_t igb_if_methods[] = { DEVMETHOD(ifdi_attach_pre, em_if_attach_pre), DEVMETHOD(ifdi_attach_post, em_if_attach_post), DEVMETHOD(ifdi_detach, em_if_detach), DEVMETHOD(ifdi_shutdown, em_if_shutdown), DEVMETHOD(ifdi_suspend, em_if_suspend), DEVMETHOD(ifdi_resume, em_if_resume), DEVMETHOD(ifdi_init, em_if_init), DEVMETHOD(ifdi_stop, em_if_stop), DEVMETHOD(ifdi_msix_intr_assign, em_if_msix_intr_assign), DEVMETHOD(ifdi_intr_enable, igb_if_intr_enable), DEVMETHOD(ifdi_intr_disable, igb_if_intr_disable), DEVMETHOD(ifdi_tx_queues_alloc, em_if_tx_queues_alloc), DEVMETHOD(ifdi_rx_queues_alloc, em_if_rx_queues_alloc), DEVMETHOD(ifdi_queues_free, em_if_queues_free), DEVMETHOD(ifdi_update_admin_status, em_if_update_admin_status), DEVMETHOD(ifdi_multi_set, em_if_multi_set), DEVMETHOD(ifdi_media_status, em_if_media_status), DEVMETHOD(ifdi_media_change, em_if_media_change), DEVMETHOD(ifdi_mtu_set, em_if_mtu_set), DEVMETHOD(ifdi_promisc_set, em_if_set_promisc), DEVMETHOD(ifdi_timer, em_if_timer), DEVMETHOD(ifdi_watchdog_reset, em_if_watchdog_reset), DEVMETHOD(ifdi_vlan_register, em_if_vlan_register), DEVMETHOD(ifdi_vlan_unregister, em_if_vlan_unregister), DEVMETHOD(ifdi_get_counter, em_if_get_counter), DEVMETHOD(ifdi_led_func, em_if_led_func), DEVMETHOD(ifdi_rx_queue_intr_enable, igb_if_rx_queue_intr_enable), DEVMETHOD(ifdi_tx_queue_intr_enable, igb_if_tx_queue_intr_enable), DEVMETHOD(ifdi_debug, em_if_debug), DEVMETHOD(ifdi_needs_restart, em_if_needs_restart), DEVMETHOD_END }; static driver_t igb_if_driver = { "igb_if", igb_if_methods, sizeof(struct e1000_softc) }; /********************************************************************* * Tunable default values. *********************************************************************/ #define EM_TICKS_TO_USECS(ticks) ((1024 * (ticks) + 500) / 1000) #define EM_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, em, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "EM driver parameters"); static int em_disable_crc_stripping = 0; SYSCTL_INT(_hw_em, OID_AUTO, disable_crc_stripping, CTLFLAG_RDTUN, &em_disable_crc_stripping, 0, "Disable CRC Stripping"); static int em_tx_int_delay_dflt = EM_TICKS_TO_USECS(EM_TIDV); static int em_rx_int_delay_dflt = EM_TICKS_TO_USECS(EM_RDTR); SYSCTL_INT(_hw_em, OID_AUTO, tx_int_delay, CTLFLAG_RDTUN, &em_tx_int_delay_dflt, 0, "Default transmit interrupt delay in usecs"); SYSCTL_INT(_hw_em, OID_AUTO, rx_int_delay, CTLFLAG_RDTUN, &em_rx_int_delay_dflt, 0, "Default receive interrupt delay in usecs"); static int em_tx_abs_int_delay_dflt = EM_TICKS_TO_USECS(EM_TADV); static int em_rx_abs_int_delay_dflt = EM_TICKS_TO_USECS(EM_RADV); SYSCTL_INT(_hw_em, OID_AUTO, tx_abs_int_delay, CTLFLAG_RDTUN, &em_tx_abs_int_delay_dflt, 0, "Default transmit interrupt delay limit in usecs"); SYSCTL_INT(_hw_em, OID_AUTO, rx_abs_int_delay, CTLFLAG_RDTUN, &em_rx_abs_int_delay_dflt, 0, "Default receive interrupt delay limit in usecs"); static int em_smart_pwr_down = false; SYSCTL_INT(_hw_em, OID_AUTO, smart_pwr_down, CTLFLAG_RDTUN, &em_smart_pwr_down, 0, "Set to true to leave smart power down enabled on newer adapters"); static bool em_unsupported_tso = false; SYSCTL_BOOL(_hw_em, OID_AUTO, unsupported_tso, CTLFLAG_RDTUN, &em_unsupported_tso, 0, "Allow unsupported em(4) TSO configurations"); /* Controls whether promiscuous also shows bad packets */ static int em_debug_sbp = false; SYSCTL_INT(_hw_em, OID_AUTO, sbp, CTLFLAG_RDTUN, &em_debug_sbp, 0, "Show bad packets in promiscuous mode"); /* How many packets rxeof tries to clean at a time */ static int em_rx_process_limit = 100; SYSCTL_INT(_hw_em, OID_AUTO, rx_process_limit, CTLFLAG_RDTUN, &em_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 eee_setting = 1; SYSCTL_INT(_hw_em, OID_AUTO, eee_setting, CTLFLAG_RDTUN, &eee_setting, 0, "Enable Energy Efficient Ethernet"); /* ** Tuneable Interrupt rate */ static int em_max_interrupt_rate = 8000; SYSCTL_INT(_hw_em, OID_AUTO, max_interrupt_rate, CTLFLAG_RDTUN, &em_max_interrupt_rate, 0, "Maximum interrupts per second"); /* Global used in WOL setup with multiport cards */ static int global_quad_port_a = 0; extern struct if_txrx igb_txrx; extern struct if_txrx em_txrx; extern struct if_txrx lem_txrx; static struct if_shared_ctx em_sctx_init = { .isc_magic = IFLIB_MAGIC, .isc_q_align = PAGE_SIZE, .isc_tx_maxsize = EM_TSO_SIZE + sizeof(struct ether_vlan_header), .isc_tx_maxsegsize = PAGE_SIZE, .isc_tso_maxsize = EM_TSO_SIZE + sizeof(struct ether_vlan_header), .isc_tso_maxsegsize = EM_TSO_SEG_SIZE, .isc_rx_maxsize = MJUM9BYTES, .isc_rx_nsegments = 1, .isc_rx_maxsegsize = MJUM9BYTES, .isc_nfl = 1, .isc_nrxqs = 1, .isc_ntxqs = 1, .isc_admin_intrcnt = 1, .isc_vendor_info = em_vendor_info_array, .isc_driver_version = em_driver_version, .isc_driver = &em_if_driver, .isc_flags = IFLIB_NEED_SCRATCH | IFLIB_TSO_INIT_IP | IFLIB_NEED_ZERO_CSUM, .isc_nrxd_min = {EM_MIN_RXD}, .isc_ntxd_min = {EM_MIN_TXD}, .isc_nrxd_max = {EM_MAX_RXD}, .isc_ntxd_max = {EM_MAX_TXD}, .isc_nrxd_default = {EM_DEFAULT_RXD}, .isc_ntxd_default = {EM_DEFAULT_TXD}, }; static struct if_shared_ctx igb_sctx_init = { .isc_magic = IFLIB_MAGIC, .isc_q_align = PAGE_SIZE, .isc_tx_maxsize = EM_TSO_SIZE + sizeof(struct ether_vlan_header), .isc_tx_maxsegsize = PAGE_SIZE, .isc_tso_maxsize = EM_TSO_SIZE + sizeof(struct ether_vlan_header), .isc_tso_maxsegsize = EM_TSO_SEG_SIZE, .isc_rx_maxsize = MJUM9BYTES, .isc_rx_nsegments = 1, .isc_rx_maxsegsize = MJUM9BYTES, .isc_nfl = 1, .isc_nrxqs = 1, .isc_ntxqs = 1, .isc_admin_intrcnt = 1, .isc_vendor_info = igb_vendor_info_array, .isc_driver_version = igb_driver_version, .isc_driver = &igb_if_driver, .isc_flags = IFLIB_NEED_SCRATCH | IFLIB_TSO_INIT_IP | IFLIB_NEED_ZERO_CSUM, .isc_nrxd_min = {EM_MIN_RXD}, .isc_ntxd_min = {EM_MIN_TXD}, .isc_nrxd_max = {IGB_MAX_RXD}, .isc_ntxd_max = {IGB_MAX_TXD}, .isc_nrxd_default = {EM_DEFAULT_RXD}, .isc_ntxd_default = {EM_DEFAULT_TXD}, }; /***************************************************************** * * Dump Registers * ****************************************************************/ #define IGB_REGS_LEN 739 static int em_get_regs(SYSCTL_HANDLER_ARGS) { struct e1000_softc *sc = (struct e1000_softc *)arg1; struct e1000_hw *hw = &sc->hw; struct sbuf *sb; u32 *regs_buff; int rc; regs_buff = malloc(sizeof(u32) * IGB_REGS_LEN, M_DEVBUF, M_WAITOK); memset(regs_buff, 0, IGB_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] = E1000_READ_REG(hw, E1000_CTRL); regs_buff[1] = E1000_READ_REG(hw, E1000_STATUS); regs_buff[2] = E1000_READ_REG(hw, E1000_CTRL_EXT); regs_buff[3] = E1000_READ_REG(hw, E1000_ICR); regs_buff[4] = E1000_READ_REG(hw, E1000_RCTL); regs_buff[5] = E1000_READ_REG(hw, E1000_RDLEN(0)); regs_buff[6] = E1000_READ_REG(hw, E1000_RDH(0)); regs_buff[7] = E1000_READ_REG(hw, E1000_RDT(0)); regs_buff[8] = E1000_READ_REG(hw, E1000_RXDCTL(0)); regs_buff[9] = E1000_READ_REG(hw, E1000_RDBAL(0)); regs_buff[10] = E1000_READ_REG(hw, E1000_RDBAH(0)); regs_buff[11] = E1000_READ_REG(hw, E1000_TCTL); regs_buff[12] = E1000_READ_REG(hw, E1000_TDBAL(0)); regs_buff[13] = E1000_READ_REG(hw, E1000_TDBAH(0)); regs_buff[14] = E1000_READ_REG(hw, E1000_TDLEN(0)); regs_buff[15] = E1000_READ_REG(hw, E1000_TDH(0)); regs_buff[16] = E1000_READ_REG(hw, E1000_TDT(0)); regs_buff[17] = E1000_READ_REG(hw, E1000_TXDCTL(0)); regs_buff[18] = E1000_READ_REG(hw, E1000_TDFH); regs_buff[19] = E1000_READ_REG(hw, E1000_TDFT); regs_buff[20] = E1000_READ_REG(hw, E1000_TDFHS); regs_buff[21] = E1000_READ_REG(hw, E1000_TDFPC); 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_EXT\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 = sc->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 & E1000_TXD_STAT_DD : 0); } } #endif rc = sbuf_finish(sb); sbuf_delete(sb); return(rc); } static void * em_register(device_t dev) { return (&em_sctx_init); } static void * igb_register(device_t dev) { return (&igb_sctx_init); } static int em_set_num_queues(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); int maxqueues; /* Sanity check based on HW */ switch (sc->hw.mac.type) { case e1000_82576: case e1000_82580: case e1000_i350: case e1000_i354: maxqueues = 8; break; case e1000_i210: case e1000_82575: maxqueues = 4; break; case e1000_i211: case e1000_82574: maxqueues = 2; break; default: maxqueues = 1; break; } return (maxqueues); } #define LEM_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 #define EM_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 #define IGB_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 em_if_attach_pre(if_ctx_t ctx) { struct e1000_softc *sc; if_softc_ctx_t scctx; device_t dev; struct e1000_hw *hw; struct sysctl_oid_list *child; struct sysctl_ctx_list *ctx_list; int error = 0; INIT_DEBUGOUT("em_if_attach_pre: begin"); dev = iflib_get_dev(ctx); sc = iflib_get_softc(ctx); sc->ctx = sc->osdep.ctx = ctx; sc->dev = sc->osdep.dev = dev; scctx = sc->shared = iflib_get_softc_ctx(ctx); sc->media = iflib_get_media(ctx); hw = &sc->hw; sc->tx_process_limit = scctx->isc_ntxd[0]; /* Determine hardware and mac info */ em_identify_hardware(ctx); /* SYSCTL stuff */ ctx_list = device_get_sysctl_ctx(dev); child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "nvm", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, em_sysctl_nvm_info, "I", "NVM Information"); SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "fw_version", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0, em_sysctl_print_fw_version, "A", "Prints FW/NVM Versions"); SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "debug", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, em_sysctl_debug_info, "I", "Debug Information"); SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "fc", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, em_set_flowcntl, "I", "Flow Control"); SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "reg_dump", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0, em_get_regs, "A", "Dump Registers"); SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "rs_dump", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, em_get_rs, "I", "Dump RS indexes"); scctx->isc_tx_nsegments = EM_MAX_SCATTER; scctx->isc_nrxqsets_max = scctx->isc_ntxqsets_max = em_set_num_queues(ctx); if (bootverbose) device_printf(dev, "attach_pre capping queues at %d\n", scctx->isc_ntxqsets_max); if (hw->mac.type >= igb_mac_min) { scctx->isc_txqsizes[0] = roundup2(scctx->isc_ntxd[0] * sizeof(union e1000_adv_tx_desc), EM_DBA_ALIGN); scctx->isc_rxqsizes[0] = roundup2(scctx->isc_nrxd[0] * sizeof(union e1000_adv_rx_desc), EM_DBA_ALIGN); scctx->isc_txd_size[0] = sizeof(union e1000_adv_tx_desc); scctx->isc_rxd_size[0] = sizeof(union e1000_adv_rx_desc); scctx->isc_txrx = &igb_txrx; scctx->isc_tx_tso_segments_max = EM_MAX_SCATTER; scctx->isc_tx_tso_size_max = EM_TSO_SIZE; scctx->isc_tx_tso_segsize_max = EM_TSO_SEG_SIZE; scctx->isc_capabilities = scctx->isc_capenable = IGB_CAPS; scctx->isc_tx_csum_flags = CSUM_TCP | CSUM_UDP | CSUM_TSO | CSUM_IP6_TCP | CSUM_IP6_UDP; if (hw->mac.type != e1000_82575) scctx->isc_tx_csum_flags |= 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 = pci_msix_table_bar(dev); } else if (hw->mac.type >= em_mac_min) { scctx->isc_txqsizes[0] = roundup2(scctx->isc_ntxd[0]* sizeof(struct e1000_tx_desc), EM_DBA_ALIGN); scctx->isc_rxqsizes[0] = roundup2(scctx->isc_nrxd[0] * sizeof(union e1000_rx_desc_extended), EM_DBA_ALIGN); scctx->isc_txd_size[0] = sizeof(struct e1000_tx_desc); scctx->isc_rxd_size[0] = sizeof(union e1000_rx_desc_extended); scctx->isc_txrx = &em_txrx; scctx->isc_tx_tso_segments_max = EM_MAX_SCATTER; scctx->isc_tx_tso_size_max = EM_TSO_SIZE; scctx->isc_tx_tso_segsize_max = EM_TSO_SEG_SIZE; scctx->isc_capabilities = scctx->isc_capenable = EM_CAPS; scctx->isc_tx_csum_flags = CSUM_TCP | CSUM_UDP | CSUM_IP_TSO | CSUM_IP6_TCP | CSUM_IP6_UDP; /* Disable TSO on all em(4) until ring stalls can be debugged */ scctx->isc_capenable &= ~IFCAP_TSO; /* * Disable TSO on SPT due to errata that downclocks DMA performance * i218-i219 Specification Update 1.5.4.5 */ if (hw->mac.type == e1000_pch_spt) scctx->isc_capenable &= ~IFCAP_TSO; /* * We support MSI-X with 82574 only, but indicate to iflib(4) * that it shall give MSI at least a try with other devices. */ if (hw->mac.type == e1000_82574) { scctx->isc_msix_bar = pci_msix_table_bar(dev);; } else { scctx->isc_msix_bar = -1; scctx->isc_disable_msix = 1; } } else { scctx->isc_txqsizes[0] = roundup2((scctx->isc_ntxd[0] + 1) * sizeof(struct e1000_tx_desc), EM_DBA_ALIGN); scctx->isc_rxqsizes[0] = roundup2((scctx->isc_nrxd[0] + 1) * sizeof(struct e1000_rx_desc), EM_DBA_ALIGN); scctx->isc_txd_size[0] = sizeof(struct e1000_tx_desc); scctx->isc_rxd_size[0] = sizeof(struct e1000_rx_desc); scctx->isc_txrx = &lem_txrx; scctx->isc_tx_tso_segments_max = EM_MAX_SCATTER; scctx->isc_tx_tso_size_max = EM_TSO_SIZE; scctx->isc_tx_tso_segsize_max = EM_TSO_SEG_SIZE; scctx->isc_capabilities = scctx->isc_capenable = LEM_CAPS; if (em_unsupported_tso) scctx->isc_capabilities |= IFCAP_TSO6; scctx->isc_tx_csum_flags = CSUM_TCP | CSUM_UDP | CSUM_IP_TSO | CSUM_IP6_TCP | CSUM_IP6_UDP; /* Disable TSO on all lem(4) until ring stalls can be debugged */ scctx->isc_capenable &= ~IFCAP_TSO; /* 82541ER doesn't do HW tagging */ if (hw->device_id == E1000_DEV_ID_82541ER || hw->device_id == E1000_DEV_ID_82541ER_LOM) { scctx->isc_capabilities &= ~IFCAP_VLAN_HWTAGGING; scctx->isc_capenable = scctx->isc_capabilities; } /* This is the first e1000 chip and it does not do offloads */ if (hw->mac.type == e1000_82542) { scctx->isc_capabilities &= ~(IFCAP_HWCSUM | IFCAP_VLAN_HWCSUM | IFCAP_HWCSUM_IPV6 | IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWFILTER | IFCAP_TSO | IFCAP_VLAN_HWTSO); scctx->isc_capenable = scctx->isc_capabilities; } /* These can't do TSO for various reasons */ if (hw->mac.type < e1000_82544 || hw->mac.type == e1000_82547 || hw->mac.type == e1000_82547_rev_2) { scctx->isc_capabilities &= ~(IFCAP_TSO | IFCAP_VLAN_HWTSO); scctx->isc_capenable = scctx->isc_capabilities; } /* XXXKB: No IPv6 before this? */ if (hw->mac.type < e1000_82545){ scctx->isc_capabilities &= ~IFCAP_HWCSUM_IPV6; scctx->isc_capenable = scctx->isc_capabilities; } /* "PCI/PCI-X SDM 4.0" page 33 (b) - FDX requirement on these chips */ if (hw->mac.type == e1000_82547 || hw->mac.type == e1000_82547_rev_2) scctx->isc_capenable &= ~(IFCAP_HWCSUM | IFCAP_VLAN_HWCSUM | IFCAP_HWCSUM_IPV6); /* INTx only */ scctx->isc_msix_bar = 0; } /* Setup PCI resources */ if (em_allocate_pci_resources(ctx)) { device_printf(dev, "Allocation of PCI resources failed\n"); error = ENXIO; goto err_pci; } /* ** For ICH8 and family we need to ** map the flash memory, and this ** must happen after the MAC is ** identified */ if ((hw->mac.type == e1000_ich8lan) || (hw->mac.type == e1000_ich9lan) || (hw->mac.type == e1000_ich10lan) || (hw->mac.type == e1000_pchlan) || (hw->mac.type == e1000_pch2lan) || (hw->mac.type == e1000_pch_lpt)) { int rid = EM_BAR_TYPE_FLASH; sc->flash = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->flash == NULL) { device_printf(dev, "Mapping of Flash failed\n"); error = ENXIO; goto err_pci; } /* This is used in the shared code */ hw->flash_address = (u8 *)sc->flash; sc->osdep.flash_bus_space_tag = rman_get_bustag(sc->flash); sc->osdep.flash_bus_space_handle = rman_get_bushandle(sc->flash); } /* ** In the new SPT device flash is not a ** separate BAR, rather it is also in BAR0, ** so use the same tag and an offset handle for the ** FLASH read/write macros in the shared code. */ else if (hw->mac.type >= e1000_pch_spt) { sc->osdep.flash_bus_space_tag = sc->osdep.mem_bus_space_tag; sc->osdep.flash_bus_space_handle = sc->osdep.mem_bus_space_handle + E1000_FLASH_BASE_ADDR; } /* Do Shared Code initialization */ error = e1000_setup_init_funcs(hw, true); if (error) { device_printf(dev, "Setup of Shared code failed, error %d\n", error); error = ENXIO; goto err_pci; } em_setup_msix(ctx); e1000_get_bus_info(hw); /* Set up some sysctls for the tunable interrupt delays */ em_add_int_delay_sysctl(sc, "rx_int_delay", "receive interrupt delay in usecs", &sc->rx_int_delay, E1000_REGISTER(hw, E1000_RDTR), em_rx_int_delay_dflt); em_add_int_delay_sysctl(sc, "tx_int_delay", "transmit interrupt delay in usecs", &sc->tx_int_delay, E1000_REGISTER(hw, E1000_TIDV), em_tx_int_delay_dflt); em_add_int_delay_sysctl(sc, "rx_abs_int_delay", "receive interrupt delay limit in usecs", &sc->rx_abs_int_delay, E1000_REGISTER(hw, E1000_RADV), em_rx_abs_int_delay_dflt); em_add_int_delay_sysctl(sc, "tx_abs_int_delay", "transmit interrupt delay limit in usecs", &sc->tx_abs_int_delay, E1000_REGISTER(hw, E1000_TADV), em_tx_abs_int_delay_dflt); em_add_int_delay_sysctl(sc, "itr", "interrupt delay limit in usecs/4", &sc->tx_itr, E1000_REGISTER(hw, E1000_ITR), DEFAULT_ITR); hw->mac.autoneg = DO_AUTO_NEG; hw->phy.autoneg_wait_to_complete = false; hw->phy.autoneg_advertised = AUTONEG_ADV_DEFAULT; if (hw->mac.type < em_mac_min) { e1000_init_script_state_82541(hw, true); e1000_set_tbi_compatibility_82543(hw, true); } /* Copper options */ if (hw->phy.media_type == e1000_media_type_copper) { hw->phy.mdix = AUTO_ALL_MODES; hw->phy.disable_polarity_correction = false; hw->phy.ms_type = EM_MASTER_SLAVE; } /* * Set the frame limits assuming * standard ethernet sized frames. */ scctx->isc_max_frame_size = hw->mac.max_frame_size = ETHERMTU + ETHER_HDR_LEN + ETHERNET_FCS_SIZE; /* * This controls when hardware reports transmit completion * status. */ hw->mac.report_tx_early = 1; /* Allocate multicast array memory. */ sc->mta = malloc(sizeof(u8) * ETHER_ADDR_LEN * MAX_NUM_MULTICAST_ADDRESSES, M_DEVBUF, M_NOWAIT); if (sc->mta == NULL) { device_printf(dev, "Can not allocate multicast setup array\n"); error = ENOMEM; goto err_late; } /* Clear the IFCAP_TSO auto mask */ sc->tso_automasked = 0; /* Check SOL/IDER usage */ if (e1000_check_reset_block(hw)) device_printf(dev, "PHY reset is blocked" " due to SOL/IDER session.\n"); /* Sysctl for setting Energy Efficient Ethernet */ hw->dev_spec.ich8lan.eee_disable = eee_setting; SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "eee_control", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, em_sysctl_eee, "I", "Disable Energy Efficient Ethernet"); /* ** Start from a known state, this is ** important in reading the nvm and ** mac from that. */ e1000_reset_hw(hw); /* Make sure we have a good EEPROM before we read from it */ if (e1000_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 (e1000_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 (e1000_read_mac_addr(hw) < 0) { device_printf(dev, "EEPROM read error while reading MAC" " address\n"); error = EIO; goto err_late; } if (!em_is_valid_ether_addr(hw->mac.addr)) { if (sc->vf_ifp) { ether_gen_addr(iflib_get_ifp(ctx), (struct ether_addr *)hw->mac.addr); } else { device_printf(dev, "Invalid MAC address\n"); error = EIO; goto err_late; } } /* Save the EEPROM/NVM versions, must be done under IFLIB_CTX_LOCK */ em_fw_version_locked(ctx); em_print_fw_version(sc); /* * Get Wake-on-Lan and Management info for later use */ em_get_wakeup(ctx); /* Enable only WOL MAGIC by default */ scctx->isc_capenable &= ~IFCAP_WOL; if (sc->wol != 0) scctx->isc_capenable |= IFCAP_WOL_MAGIC; iflib_set_mac(ctx, hw->mac.addr); return (0); err_late: em_release_hw_control(sc); err_pci: em_free_pci_resources(ctx); free(sc->mta, M_DEVBUF); return (error); } static int em_if_attach_post(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); struct e1000_hw *hw = &sc->hw; int error = 0; /* Setup OS specific network interface */ error = em_setup_interface(ctx); if (error != 0) { device_printf(sc->dev, "Interface setup failed: %d\n", error); goto err_late; } em_reset(ctx); /* Initialize statistics */ em_update_stats_counters(sc); hw->mac.get_link_status = 1; em_if_update_admin_status(ctx); em_add_hw_stats(sc); /* Non-AMT based hardware can now take control from firmware */ if (sc->has_manage && !sc->has_amt) em_get_hw_control(sc); INIT_DEBUGOUT("em_if_attach_post: end"); return (0); err_late: /* upon attach_post() error, iflib calls _if_detach() to free resources. */ 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 em_if_detach(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); INIT_DEBUGOUT("em_if_detach: begin"); e1000_phy_hw_reset(&sc->hw); em_release_manageability(sc); em_release_hw_control(sc); em_free_pci_resources(ctx); free(sc->mta, M_DEVBUF); sc->mta = NULL; return (0); } /********************************************************************* * * Shutdown entry point * **********************************************************************/ static int em_if_shutdown(if_ctx_t ctx) { return em_if_suspend(ctx); } /* * Suspend/resume device methods. */ static int em_if_suspend(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); em_release_manageability(sc); em_release_hw_control(sc); em_enable_wakeup(ctx); return (0); } static int em_if_resume(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); if (sc->hw.mac.type == e1000_pch2lan) e1000_resume_workarounds_pchlan(&sc->hw); em_if_init(ctx); em_init_manageability(sc); return(0); } static int em_if_mtu_set(if_ctx_t ctx, uint32_t mtu) { int max_frame_size; struct e1000_softc *sc = iflib_get_softc(ctx); if_softc_ctx_t scctx = iflib_get_softc_ctx(ctx); IOCTL_DEBUGOUT("ioctl rcv'd: SIOCSIFMTU (Set Interface MTU)"); switch (sc->hw.mac.type) { case e1000_82571: case e1000_82572: case e1000_ich9lan: case e1000_ich10lan: case e1000_pch2lan: case e1000_pch_lpt: case e1000_pch_spt: case e1000_pch_cnp: case e1000_pch_tgp: case e1000_pch_adp: case e1000_pch_mtp: case e1000_pch_ptp: case e1000_82574: case e1000_82583: case e1000_80003es2lan: /* 9K Jumbo Frame size */ max_frame_size = 9234; break; case e1000_pchlan: max_frame_size = 4096; break; case e1000_82542: case e1000_ich8lan: /* Adapters that do not support jumbo frames */ max_frame_size = ETHER_MAX_LEN; break; default: if (sc->hw.mac.type >= igb_mac_min) max_frame_size = 9234; else /* lem */ max_frame_size = MAX_JUMBO_FRAME_SIZE; } if (mtu > max_frame_size - ETHER_HDR_LEN - ETHER_CRC_LEN) { return (EINVAL); } scctx->isc_max_frame_size = sc->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 em_if_init(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); if_softc_ctx_t scctx = sc->shared; struct ifnet *ifp = iflib_get_ifp(ctx); struct em_tx_queue *tx_que; int i; INIT_DEBUGOUT("em_if_init: begin"); /* Get the latest mac address, User can use a LAA */ bcopy(if_getlladdr(ifp), sc->hw.mac.addr, ETHER_ADDR_LEN); /* Put the address into the Receive Address Array */ e1000_rar_set(&sc->hw, sc->hw.mac.addr, 0); /* * With the 82571 adapter, RAR[0] may be overwritten * when the other port is reset, we make a duplicate * in RAR[14] for that eventuality, this assures * the interface continues to function. */ if (sc->hw.mac.type == e1000_82571) { e1000_set_laa_state_82571(&sc->hw, true); e1000_rar_set(&sc->hw, sc->hw.mac.addr, E1000_RAR_ENTRIES - 1); } /* Initialize the hardware */ em_reset(ctx); em_if_update_admin_status(ctx); for (i = 0, tx_que = sc->tx_queues; i < sc->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 */ E1000_WRITE_REG(&sc->hw, E1000_VET, ETHERTYPE_VLAN); /* Clear bad data from Rx FIFOs */ if (sc->hw.mac.type >= igb_mac_min) e1000_rx_fifo_flush_base(&sc->hw); /* Configure for OS presence */ em_init_manageability(sc); /* Prepare transmit descriptors and buffers */ em_initialize_transmit_unit(ctx); /* Setup Multicast table */ em_if_multi_set(ctx); sc->rx_mbuf_sz = iflib_get_rx_mbuf_sz(ctx); em_initialize_receive_unit(ctx); /* Set up VLAN support and filter */ em_setup_vlan_hw_support(ctx); /* Don't lose promiscuous settings */ em_if_set_promisc(ctx, if_getflags(ifp)); e1000_clear_hw_cntrs_base_generic(&sc->hw); /* MSI-X configuration for 82574 */ if (sc->hw.mac.type == e1000_82574) { int tmp = E1000_READ_REG(&sc->hw, E1000_CTRL_EXT); tmp |= E1000_CTRL_EXT_PBA_CLR; E1000_WRITE_REG(&sc->hw, E1000_CTRL_EXT, tmp); /* Set the IVAR - interrupt vector routing. */ E1000_WRITE_REG(&sc->hw, E1000_IVAR, sc->ivars); } else if (sc->intr_type == IFLIB_INTR_MSIX) /* Set up queue routing */ igb_configure_queues(sc); /* this clears any pending interrupts */ E1000_READ_REG(&sc->hw, E1000_ICR); E1000_WRITE_REG(&sc->hw, E1000_ICS, E1000_ICS_LSC); /* AMT based hardware can now take control from firmware */ if (sc->has_manage && sc->has_amt) em_get_hw_control(sc); /* Set Energy Efficient Ethernet */ if (sc->hw.mac.type >= igb_mac_min && sc->hw.phy.media_type == e1000_media_type_copper) { if (sc->hw.mac.type == e1000_i354) e1000_set_eee_i354(&sc->hw, true, true); else e1000_set_eee_i350(&sc->hw, true, true); } } /********************************************************************* * * Fast Legacy/MSI Combined Interrupt Service routine * *********************************************************************/ int em_intr(void *arg) { struct e1000_softc *sc = arg; if_ctx_t ctx = sc->ctx; u32 reg_icr; reg_icr = E1000_READ_REG(&sc->hw, E1000_ICR); /* Hot eject? */ if (reg_icr == 0xffffffff) return FILTER_STRAY; /* Definitely not our interrupt. */ if (reg_icr == 0x0) return FILTER_STRAY; /* * Starting with the 82571 chip, bit 31 should be used to * determine whether the interrupt belongs to us. */ if (sc->hw.mac.type >= e1000_82571 && (reg_icr & E1000_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 & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) em_handle_link(ctx); if (reg_icr & E1000_ICR_RXO) sc->rx_overruns++; return (FILTER_SCHEDULE_THREAD); } static int em_if_rx_queue_intr_enable(if_ctx_t ctx, uint16_t rxqid) { struct e1000_softc *sc = iflib_get_softc(ctx); struct em_rx_queue *rxq = &sc->rx_queues[rxqid]; E1000_WRITE_REG(&sc->hw, E1000_IMS, rxq->eims); return (0); } static int em_if_tx_queue_intr_enable(if_ctx_t ctx, uint16_t txqid) { struct e1000_softc *sc = iflib_get_softc(ctx); struct em_tx_queue *txq = &sc->tx_queues[txqid]; E1000_WRITE_REG(&sc->hw, E1000_IMS, txq->eims); return (0); } static int igb_if_rx_queue_intr_enable(if_ctx_t ctx, uint16_t rxqid) { struct e1000_softc *sc = iflib_get_softc(ctx); struct em_rx_queue *rxq = &sc->rx_queues[rxqid]; E1000_WRITE_REG(&sc->hw, E1000_EIMS, rxq->eims); return (0); } static int igb_if_tx_queue_intr_enable(if_ctx_t ctx, uint16_t txqid) { struct e1000_softc *sc = iflib_get_softc(ctx); struct em_tx_queue *txq = &sc->tx_queues[txqid]; E1000_WRITE_REG(&sc->hw, E1000_EIMS, txq->eims); return (0); } /********************************************************************* * * MSI-X RX Interrupt Service routine * **********************************************************************/ static int em_msix_que(void *arg) { struct em_rx_queue *que = arg; ++que->irqs; return (FILTER_SCHEDULE_THREAD); } /********************************************************************* * * MSI-X Link Fast Interrupt Service routine * **********************************************************************/ static int em_msix_link(void *arg) { struct e1000_softc *sc = arg; u32 reg_icr; ++sc->link_irq; MPASS(sc->hw.back != NULL); reg_icr = E1000_READ_REG(&sc->hw, E1000_ICR); if (reg_icr & E1000_ICR_RXO) sc->rx_overruns++; if (reg_icr & (E1000_ICR_RXSEQ | E1000_ICR_LSC)) em_handle_link(sc->ctx); /* Re-arm unconditionally */ if (sc->hw.mac.type >= igb_mac_min) { E1000_WRITE_REG(&sc->hw, E1000_IMS, E1000_IMS_LSC); E1000_WRITE_REG(&sc->hw, E1000_EIMS, sc->link_mask); } else if (sc->hw.mac.type == e1000_82574) { E1000_WRITE_REG(&sc->hw, E1000_IMS, E1000_IMS_LSC | E1000_IMS_OTHER); /* * Because we must read the ICR for this interrupt it may * clear other causes using autoclear, for this reason we * simply create a soft interrupt for all these vectors. */ if (reg_icr) E1000_WRITE_REG(&sc->hw, E1000_ICS, sc->ims); } else E1000_WRITE_REG(&sc->hw, E1000_IMS, E1000_IMS_LSC); return (FILTER_HANDLED); } static void em_handle_link(void *context) { if_ctx_t ctx = context; struct e1000_softc *sc = iflib_get_softc(ctx); sc->hw.mac.get_link_status = 1; 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 em_if_media_status(if_ctx_t ctx, struct ifmediareq *ifmr) { struct e1000_softc *sc = iflib_get_softc(ctx); u_char fiber_type = IFM_1000_SX; INIT_DEBUGOUT("em_if_media_status: begin"); iflib_admin_intr_deferred(ctx); ifmr->ifm_status = IFM_AVALID; ifmr->ifm_active = IFM_ETHER; if (!sc->link_active) { return; } ifmr->ifm_status |= IFM_ACTIVE; if ((sc->hw.phy.media_type == e1000_media_type_fiber) || (sc->hw.phy.media_type == e1000_media_type_internal_serdes)) { if (sc->hw.mac.type == e1000_82545) fiber_type = IFM_1000_LX; ifmr->ifm_active |= fiber_type | IFM_FDX; } else { switch (sc->link_speed) { case 10: ifmr->ifm_active |= IFM_10_T; break; case 100: ifmr->ifm_active |= IFM_100_TX; break; case 1000: ifmr->ifm_active |= IFM_1000_T; break; } if (sc->link_duplex == FULL_DUPLEX) ifmr->ifm_active |= IFM_FDX; else ifmr->ifm_active |= IFM_HDX; } } /********************************************************************* * * Media Ioctl callback * * This routine is called when the user changes speed/duplex using * media/mediopt option with ifconfig. * **********************************************************************/ static int em_if_media_change(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); struct ifmedia *ifm = iflib_get_media(ctx); INIT_DEBUGOUT("em_if_media_change: begin"); if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) return (EINVAL); switch (IFM_SUBTYPE(ifm->ifm_media)) { case IFM_AUTO: sc->hw.mac.autoneg = DO_AUTO_NEG; sc->hw.phy.autoneg_advertised = AUTONEG_ADV_DEFAULT; break; case IFM_1000_LX: case IFM_1000_SX: case IFM_1000_T: sc->hw.mac.autoneg = DO_AUTO_NEG; sc->hw.phy.autoneg_advertised = ADVERTISE_1000_FULL; break; case IFM_100_TX: sc->hw.mac.autoneg = false; sc->hw.phy.autoneg_advertised = 0; if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) sc->hw.mac.forced_speed_duplex = ADVERTISE_100_FULL; else sc->hw.mac.forced_speed_duplex = ADVERTISE_100_HALF; break; case IFM_10_T: sc->hw.mac.autoneg = false; sc->hw.phy.autoneg_advertised = 0; if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) sc->hw.mac.forced_speed_duplex = ADVERTISE_10_FULL; else sc->hw.mac.forced_speed_duplex = ADVERTISE_10_HALF; break; default: device_printf(sc->dev, "Unsupported media type\n"); } em_if_init(ctx); return (0); } static int em_if_set_promisc(if_ctx_t ctx, int flags) { struct e1000_softc *sc = iflib_get_softc(ctx); struct ifnet *ifp = iflib_get_ifp(ctx); u32 reg_rctl; int mcnt = 0; reg_rctl = E1000_READ_REG(&sc->hw, E1000_RCTL); reg_rctl &= ~(E1000_RCTL_SBP | E1000_RCTL_UPE); if (flags & IFF_ALLMULTI) mcnt = MAX_NUM_MULTICAST_ADDRESSES; else mcnt = min(if_llmaddr_count(ifp), MAX_NUM_MULTICAST_ADDRESSES); if (mcnt < MAX_NUM_MULTICAST_ADDRESSES) reg_rctl &= (~E1000_RCTL_MPE); E1000_WRITE_REG(&sc->hw, E1000_RCTL, reg_rctl); if (flags & IFF_PROMISC) { reg_rctl |= (E1000_RCTL_UPE | E1000_RCTL_MPE); em_if_vlan_filter_disable(sc); /* Turn this on if you want to see bad packets */ if (em_debug_sbp) reg_rctl |= E1000_RCTL_SBP; E1000_WRITE_REG(&sc->hw, E1000_RCTL, reg_rctl); } else { if (flags & IFF_ALLMULTI) { reg_rctl |= E1000_RCTL_MPE; reg_rctl &= ~E1000_RCTL_UPE; E1000_WRITE_REG(&sc->hw, E1000_RCTL, reg_rctl); } if (em_if_vlan_filter_used(ctx)) em_if_vlan_filter_enable(sc); } return (0); } static u_int em_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 em_if_multi_set(if_ctx_t ctx) { struct e1000_softc *sc = 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("em_set_multi: begin"); mta = sc->mta; bzero(mta, sizeof(u8) * ETHER_ADDR_LEN * MAX_NUM_MULTICAST_ADDRESSES); if (sc->hw.mac.type == e1000_82542 && sc->hw.revision_id == E1000_REVISION_2) { reg_rctl = E1000_READ_REG(&sc->hw, E1000_RCTL); if (sc->hw.bus.pci_cmd_word & CMD_MEM_WRT_INVALIDATE) e1000_pci_clear_mwi(&sc->hw); reg_rctl |= E1000_RCTL_RST; E1000_WRITE_REG(&sc->hw, E1000_RCTL, reg_rctl); msec_delay(5); } mcnt = if_foreach_llmaddr(ifp, em_copy_maddr, mta); if (mcnt < MAX_NUM_MULTICAST_ADDRESSES) e1000_update_mc_addr_list(&sc->hw, mta, mcnt); reg_rctl = E1000_READ_REG(&sc->hw, E1000_RCTL); if (if_getflags(ifp) & IFF_PROMISC) reg_rctl |= (E1000_RCTL_UPE | E1000_RCTL_MPE); else if (mcnt >= MAX_NUM_MULTICAST_ADDRESSES || if_getflags(ifp) & IFF_ALLMULTI) { reg_rctl |= E1000_RCTL_MPE; reg_rctl &= ~E1000_RCTL_UPE; } else reg_rctl &= ~(E1000_RCTL_UPE | E1000_RCTL_MPE); E1000_WRITE_REG(&sc->hw, E1000_RCTL, reg_rctl); if (sc->hw.mac.type == e1000_82542 && sc->hw.revision_id == E1000_REVISION_2) { reg_rctl = E1000_READ_REG(&sc->hw, E1000_RCTL); reg_rctl &= ~E1000_RCTL_RST; E1000_WRITE_REG(&sc->hw, E1000_RCTL, reg_rctl); msec_delay(5); if (sc->hw.bus.pci_cmd_word & CMD_MEM_WRT_INVALIDATE) e1000_pci_set_mwi(&sc->hw); } } /********************************************************************* * Timer routine * * This routine schedules em_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 em_if_timer(if_ctx_t ctx, uint16_t qid) { if (qid != 0) return; iflib_admin_intr_deferred(ctx); } static void em_if_update_admin_status(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); struct e1000_hw *hw = &sc->hw; device_t dev = iflib_get_dev(ctx); u32 link_check, thstat, ctrl; bool automasked = false; link_check = thstat = ctrl = 0; /* Get the cached link value or read phy for real */ switch (hw->phy.media_type) { case e1000_media_type_copper: if (hw->mac.get_link_status) { if (hw->mac.type == e1000_pch_spt) msec_delay(50); /* Do the work to read phy */ e1000_check_for_link(hw); link_check = !hw->mac.get_link_status; if (link_check) /* ESB2 fix */ e1000_cfg_on_link_up(hw); } else { link_check = true; } break; case e1000_media_type_fiber: e1000_check_for_link(hw); link_check = (E1000_READ_REG(hw, E1000_STATUS) & E1000_STATUS_LU); break; case e1000_media_type_internal_serdes: e1000_check_for_link(hw); link_check = hw->mac.serdes_has_link; break; /* VF device is type_unknown */ case e1000_media_type_unknown: e1000_check_for_link(hw); link_check = !hw->mac.get_link_status; /* FALLTHROUGH */ default: break; } /* Check for thermal downshift or shutdown */ if (hw->mac.type == e1000_i350) { thstat = E1000_READ_REG(hw, E1000_THSTAT); ctrl = E1000_READ_REG(hw, E1000_CTRL_EXT); } /* Now check for a transition */ if (link_check && (sc->link_active == 0)) { e1000_get_speed_and_duplex(hw, &sc->link_speed, &sc->link_duplex); /* Check if we must disable SPEED_MODE bit on PCI-E */ if ((sc->link_speed != SPEED_1000) && ((hw->mac.type == e1000_82571) || (hw->mac.type == e1000_82572))) { int tarc0; tarc0 = E1000_READ_REG(hw, E1000_TARC(0)); tarc0 &= ~TARC_SPEED_MODE_BIT; E1000_WRITE_REG(hw, E1000_TARC(0), tarc0); } if (bootverbose) device_printf(dev, "Link is up %d Mbps %s\n", sc->link_speed, ((sc->link_duplex == FULL_DUPLEX) ? "Full Duplex" : "Half Duplex")); sc->link_active = 1; sc->smartspeed = 0; if ((ctrl & E1000_CTRL_EXT_LINK_MODE_MASK) == E1000_CTRL_EXT_LINK_MODE_GMII && (thstat & E1000_THSTAT_LINK_THROTTLE)) device_printf(dev, "Link: thermal downshift\n"); /* Delay Link Up for Phy update */ if (((hw->mac.type == e1000_i210) || (hw->mac.type == e1000_i211)) && (hw->phy.id == I210_I_PHY_ID)) msec_delay(I210_LINK_DELAY); /* Reset if the media type changed. */ if (hw->dev_spec._82575.media_changed && hw->mac.type >= igb_mac_min) { hw->dev_spec._82575.media_changed = false; sc->flags |= IGB_MEDIA_RESET; em_reset(ctx); } /* Only do TSO on gigabit Ethernet for older chips due to errata */ if (hw->mac.type < igb_mac_min) automasked = em_automask_tso(ctx); /* Automasking resets the interface, so don't mark it up yet */ if (!automasked) iflib_link_state_change(ctx, LINK_STATE_UP, IF_Mbps(sc->link_speed)); } else if (!link_check && (sc->link_active == 1)) { sc->link_speed = 0; sc->link_duplex = 0; sc->link_active = 0; iflib_link_state_change(ctx, LINK_STATE_DOWN, 0); } em_update_stats_counters(sc); /* Reset LAA into RAR[0] on 82571 */ if (hw->mac.type == e1000_82571 && e1000_get_laa_state_82571(hw)) e1000_rar_set(hw, hw->mac.addr, 0); if (hw->mac.type < em_mac_min) lem_smartspeed(sc); } static void em_if_watchdog_reset(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); /* * Just count the event; iflib(4) will already trigger a * sufficient reset of the controller. */ sc->watchdog_events++; } /********************************************************************* * * This routine disables all traffic on the adapter by issuing a * global reset on the MAC. * **********************************************************************/ static void em_if_stop(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); INIT_DEBUGOUT("em_if_stop: begin"); /* I219 needs special flushing to avoid hangs */ if (sc->hw.mac.type >= e1000_pch_spt && sc->hw.mac.type < igb_mac_min) em_flush_desc_rings(sc); e1000_reset_hw(&sc->hw); if (sc->hw.mac.type >= e1000_82544) E1000_WRITE_REG(&sc->hw, E1000_WUFC, 0); e1000_led_off(&sc->hw); e1000_cleanup_led(&sc->hw); } /********************************************************************* * * Determine hardware revision. * **********************************************************************/ static void em_identify_hardware(if_ctx_t ctx) { device_t dev = iflib_get_dev(ctx); struct e1000_softc *sc = iflib_get_softc(ctx); /* Make sure our PCI config space has the necessary stuff set */ sc->hw.bus.pci_cmd_word = pci_read_config(dev, PCIR_COMMAND, 2); /* Save off the information about this board */ sc->hw.vendor_id = pci_get_vendor(dev); sc->hw.device_id = pci_get_device(dev); sc->hw.revision_id = pci_read_config(dev, PCIR_REVID, 1); sc->hw.subsystem_vendor_id = pci_read_config(dev, PCIR_SUBVEND_0, 2); sc->hw.subsystem_device_id = pci_read_config(dev, PCIR_SUBDEV_0, 2); /* Do Shared Code Init and Setup */ if (e1000_set_mac_type(&sc->hw)) { device_printf(dev, "Setup init failure\n"); return; } /* Are we a VF device? */ if ((sc->hw.mac.type == e1000_vfadapt) || (sc->hw.mac.type == e1000_vfadapt_i350)) sc->vf_ifp = 1; else sc->vf_ifp = 0; } static int em_allocate_pci_resources(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); device_t dev = iflib_get_dev(ctx); int rid, val; rid = PCIR_BAR(0); sc->memory = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->memory == NULL) { device_printf(dev, "Unable to allocate bus resource: memory\n"); return (ENXIO); } sc->osdep.mem_bus_space_tag = rman_get_bustag(sc->memory); sc->osdep.mem_bus_space_handle = rman_get_bushandle(sc->memory); sc->hw.hw_addr = (u8 *)&sc->osdep.mem_bus_space_handle; /* Only older adapters use IO mapping */ if (sc->hw.mac.type < em_mac_min && sc->hw.mac.type > e1000_82543) { /* Figure our where our IO BAR is ? */ for (rid = PCIR_BAR(0); rid < PCIR_CIS;) { val = pci_read_config(dev, rid, 4); if (EM_BAR_TYPE(val) == EM_BAR_TYPE_IO) { break; } rid += 4; /* check for 64bit BAR */ if (EM_BAR_MEM_TYPE(val) == EM_BAR_MEM_TYPE_64BIT) rid += 4; } if (rid >= PCIR_CIS) { device_printf(dev, "Unable to locate IO BAR\n"); return (ENXIO); } sc->ioport = bus_alloc_resource_any(dev, SYS_RES_IOPORT, &rid, RF_ACTIVE); if (sc->ioport == NULL) { device_printf(dev, "Unable to allocate bus resource: " "ioport\n"); return (ENXIO); } sc->hw.io_base = 0; sc->osdep.io_bus_space_tag = rman_get_bustag(sc->ioport); sc->osdep.io_bus_space_handle = rman_get_bushandle(sc->ioport); } sc->hw.back = &sc->osdep; return (0); } /********************************************************************* * * Set up the MSI-X Interrupt handlers * **********************************************************************/ static int em_if_msix_intr_assign(if_ctx_t ctx, int msix) { struct e1000_softc *sc = iflib_get_softc(ctx); struct em_rx_queue *rx_que = sc->rx_queues; struct em_tx_queue *tx_que = sc->tx_queues; int error, rid, i, vector = 0, rx_vectors; char buf[16]; /* First set up ring resources */ for (i = 0; i < sc->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, em_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); sc->rx_num_queues = i + 1; goto fail; } rx_que->msix = vector; /* * Set the bit to enable interrupt * in E1000_IMS -- bits 20 and 21 * are for RX0 and RX1, note this has * NOTHING to do with the MSI-X vector */ if (sc->hw.mac.type == e1000_82574) { rx_que->eims = 1 << (20 + i); sc->ims |= rx_que->eims; sc->ivars |= (8 | rx_que->msix) << (i * 4); } else if (sc->hw.mac.type == e1000_82575) rx_que->eims = E1000_EICR_TX_QUEUE0 << vector; else rx_que->eims = 1 << vector; } rx_vectors = vector; vector = 0; for (i = 0; i < sc->tx_num_queues; i++, tx_que++, vector++) { snprintf(buf, sizeof(buf), "txq%d", i); tx_que = &sc->tx_queues[i]; iflib_softirq_alloc_generic(ctx, &sc->rx_queues[i % sc->rx_num_queues].que_irq, IFLIB_INTR_TX, tx_que, tx_que->me, buf); tx_que->msix = (vector % sc->rx_num_queues); /* * Set the bit to enable interrupt * in E1000_IMS -- bits 22 and 23 * are for TX0 and TX1, note this has * NOTHING to do with the MSI-X vector */ if (sc->hw.mac.type == e1000_82574) { tx_que->eims = 1 << (22 + i); sc->ims |= tx_que->eims; sc->ivars |= (8 | tx_que->msix) << (8 + (i * 4)); } else if (sc->hw.mac.type == e1000_82575) { tx_que->eims = E1000_EICR_TX_QUEUE0 << i; } else { tx_que->eims = 1 << i; } } /* Link interrupt */ rid = rx_vectors + 1; error = iflib_irq_alloc_generic(ctx, &sc->irq, rid, IFLIB_INTR_ADMIN, em_msix_link, sc, 0, "aq"); if (error) { device_printf(iflib_get_dev(ctx), "Failed to register admin handler"); goto fail; } sc->linkvec = rx_vectors; if (sc->hw.mac.type < igb_mac_min) { sc->ivars |= (8 | rx_vectors) << 16; sc->ivars |= 0x80000000; /* Enable the "Other" interrupt type for link status change */ sc->ims |= E1000_IMS_OTHER; } return (0); fail: iflib_irq_free(ctx, &sc->irq); rx_que = sc->rx_queues; for (int i = 0; i < sc->rx_num_queues; i++, rx_que++) iflib_irq_free(ctx, &rx_que->que_irq); return (error); } static void igb_configure_queues(struct e1000_softc *sc) { struct e1000_hw *hw = &sc->hw; struct em_rx_queue *rx_que; struct em_tx_queue *tx_que; u32 tmp, ivar = 0, newitr = 0; /* First turn on RSS capability */ if (hw->mac.type != e1000_82575) E1000_WRITE_REG(hw, E1000_GPIE, E1000_GPIE_MSIX_MODE | E1000_GPIE_EIAME | E1000_GPIE_PBA | E1000_GPIE_NSICR); /* Turn on MSI-X */ switch (hw->mac.type) { case e1000_82580: case e1000_i350: case e1000_i354: case e1000_i210: case e1000_i211: case e1000_vfadapt: case e1000_vfadapt_i350: /* RX entries */ for (int i = 0; i < sc->rx_num_queues; i++) { u32 index = i >> 1; ivar = E1000_READ_REG_ARRAY(hw, E1000_IVAR0, index); rx_que = &sc->rx_queues[i]; if (i & 1) { ivar &= 0xFF00FFFF; ivar |= (rx_que->msix | E1000_IVAR_VALID) << 16; } else { ivar &= 0xFFFFFF00; ivar |= rx_que->msix | E1000_IVAR_VALID; } E1000_WRITE_REG_ARRAY(hw, E1000_IVAR0, index, ivar); } /* TX entries */ for (int i = 0; i < sc->tx_num_queues; i++) { u32 index = i >> 1; ivar = E1000_READ_REG_ARRAY(hw, E1000_IVAR0, index); tx_que = &sc->tx_queues[i]; if (i & 1) { ivar &= 0x00FFFFFF; ivar |= (tx_que->msix | E1000_IVAR_VALID) << 24; } else { ivar &= 0xFFFF00FF; ivar |= (tx_que->msix | E1000_IVAR_VALID) << 8; } E1000_WRITE_REG_ARRAY(hw, E1000_IVAR0, index, ivar); sc->que_mask |= tx_que->eims; } /* And for the link interrupt */ ivar = (sc->linkvec | E1000_IVAR_VALID) << 8; sc->link_mask = 1 << sc->linkvec; E1000_WRITE_REG(hw, E1000_IVAR_MISC, ivar); break; case e1000_82576: /* RX entries */ for (int i = 0; i < sc->rx_num_queues; i++) { u32 index = i & 0x7; /* Each IVAR has two entries */ ivar = E1000_READ_REG_ARRAY(hw, E1000_IVAR0, index); rx_que = &sc->rx_queues[i]; if (i < 8) { ivar &= 0xFFFFFF00; ivar |= rx_que->msix | E1000_IVAR_VALID; } else { ivar &= 0xFF00FFFF; ivar |= (rx_que->msix | E1000_IVAR_VALID) << 16; } E1000_WRITE_REG_ARRAY(hw, E1000_IVAR0, index, ivar); sc->que_mask |= rx_que->eims; } /* TX entries */ for (int i = 0; i < sc->tx_num_queues; i++) { u32 index = i & 0x7; /* Each IVAR has two entries */ ivar = E1000_READ_REG_ARRAY(hw, E1000_IVAR0, index); tx_que = &sc->tx_queues[i]; if (i < 8) { ivar &= 0xFFFF00FF; ivar |= (tx_que->msix | E1000_IVAR_VALID) << 8; } else { ivar &= 0x00FFFFFF; ivar |= (tx_que->msix | E1000_IVAR_VALID) << 24; } E1000_WRITE_REG_ARRAY(hw, E1000_IVAR0, index, ivar); sc->que_mask |= tx_que->eims; } /* And for the link interrupt */ ivar = (sc->linkvec | E1000_IVAR_VALID) << 8; sc->link_mask = 1 << sc->linkvec; E1000_WRITE_REG(hw, E1000_IVAR_MISC, ivar); break; case e1000_82575: /* enable MSI-X support*/ tmp = E1000_READ_REG(hw, E1000_CTRL_EXT); tmp |= E1000_CTRL_EXT_PBA_CLR; /* Auto-Mask interrupts upon ICR read. */ tmp |= E1000_CTRL_EXT_EIAME; tmp |= E1000_CTRL_EXT_IRCA; E1000_WRITE_REG(hw, E1000_CTRL_EXT, tmp); /* Queues */ for (int i = 0; i < sc->rx_num_queues; i++) { rx_que = &sc->rx_queues[i]; tmp = E1000_EICR_RX_QUEUE0 << i; tmp |= E1000_EICR_TX_QUEUE0 << i; rx_que->eims = tmp; E1000_WRITE_REG_ARRAY(hw, E1000_MSIXBM(0), i, rx_que->eims); sc->que_mask |= rx_que->eims; } /* Link */ E1000_WRITE_REG(hw, E1000_MSIXBM(sc->linkvec), E1000_EIMS_OTHER); sc->link_mask |= E1000_EIMS_OTHER; default: break; } /* Set the starting interrupt rate */ if (em_max_interrupt_rate > 0) newitr = (4000000 / em_max_interrupt_rate) & 0x7FFC; if (hw->mac.type == e1000_82575) newitr |= newitr << 16; else newitr |= E1000_EITR_CNT_IGNR; for (int i = 0; i < sc->rx_num_queues; i++) { rx_que = &sc->rx_queues[i]; E1000_WRITE_REG(hw, E1000_EITR(rx_que->msix), newitr); } return; } static void em_free_pci_resources(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); struct em_rx_queue *que = sc->rx_queues; device_t dev = iflib_get_dev(ctx); /* Release all MSI-X queue resources */ if (sc->intr_type == IFLIB_INTR_MSIX) iflib_irq_free(ctx, &sc->irq); if (que != NULL) { for (int i = 0; i < sc->rx_num_queues; i++, que++) { iflib_irq_free(ctx, &que->que_irq); } } if (sc->memory != NULL) { bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->memory), sc->memory); sc->memory = NULL; } if (sc->flash != NULL) { bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->flash), sc->flash); sc->flash = NULL; } if (sc->ioport != NULL) { bus_release_resource(dev, SYS_RES_IOPORT, rman_get_rid(sc->ioport), sc->ioport); sc->ioport = NULL; } } /* Set up MSI or MSI-X */ static int em_setup_msix(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); if (sc->hw.mac.type == e1000_82574) { em_enable_vectors_82574(ctx); } return (0); } /********************************************************************* * * Workaround for SmartSpeed on 82541 and 82547 controllers * **********************************************************************/ static void lem_smartspeed(struct e1000_softc *sc) { u16 phy_tmp; if (sc->link_active || (sc->hw.phy.type != e1000_phy_igp) || sc->hw.mac.autoneg == 0 || (sc->hw.phy.autoneg_advertised & ADVERTISE_1000_FULL) == 0) return; if (sc->smartspeed == 0) { /* If Master/Slave config fault is asserted twice, * we assume back-to-back */ e1000_read_phy_reg(&sc->hw, PHY_1000T_STATUS, &phy_tmp); if (!(phy_tmp & SR_1000T_MS_CONFIG_FAULT)) return; e1000_read_phy_reg(&sc->hw, PHY_1000T_STATUS, &phy_tmp); if (phy_tmp & SR_1000T_MS_CONFIG_FAULT) { e1000_read_phy_reg(&sc->hw, PHY_1000T_CTRL, &phy_tmp); if(phy_tmp & CR_1000T_MS_ENABLE) { phy_tmp &= ~CR_1000T_MS_ENABLE; e1000_write_phy_reg(&sc->hw, PHY_1000T_CTRL, phy_tmp); sc->smartspeed++; if(sc->hw.mac.autoneg && !e1000_copper_link_autoneg(&sc->hw) && !e1000_read_phy_reg(&sc->hw, PHY_CONTROL, &phy_tmp)) { phy_tmp |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); e1000_write_phy_reg(&sc->hw, PHY_CONTROL, phy_tmp); } } } return; } else if(sc->smartspeed == EM_SMARTSPEED_DOWNSHIFT) { /* If still no link, perhaps using 2/3 pair cable */ e1000_read_phy_reg(&sc->hw, PHY_1000T_CTRL, &phy_tmp); phy_tmp |= CR_1000T_MS_ENABLE; e1000_write_phy_reg(&sc->hw, PHY_1000T_CTRL, phy_tmp); if(sc->hw.mac.autoneg && !e1000_copper_link_autoneg(&sc->hw) && !e1000_read_phy_reg(&sc->hw, PHY_CONTROL, &phy_tmp)) { phy_tmp |= (MII_CR_AUTO_NEG_EN | MII_CR_RESTART_AUTO_NEG); e1000_write_phy_reg(&sc->hw, PHY_CONTROL, phy_tmp); } } /* Restart process after EM_SMARTSPEED_MAX iterations */ if(sc->smartspeed++ == EM_SMARTSPEED_MAX) sc->smartspeed = 0; } /********************************************************************* * * Initialize the DMA Coalescing feature * **********************************************************************/ static void igb_init_dmac(struct e1000_softc *sc, u32 pba) { device_t dev = sc->dev; struct e1000_hw *hw = &sc->hw; u32 dmac, reg = ~E1000_DMACR_DMAC_EN; u16 hwm; u16 max_frame_size; if (hw->mac.type == e1000_i211) return; max_frame_size = sc->shared->isc_max_frame_size; if (hw->mac.type > e1000_82580) { if (sc->dmac == 0) { /* Disabling it */ E1000_WRITE_REG(hw, E1000_DMACR, reg); return; } else device_printf(dev, "DMA Coalescing enabled\n"); /* Set starting threshold */ E1000_WRITE_REG(hw, E1000_DMCTXTH, 0); hwm = 64 * pba - max_frame_size / 16; if (hwm < 64 * (pba - 6)) hwm = 64 * (pba - 6); reg = E1000_READ_REG(hw, E1000_FCRTC); reg &= ~E1000_FCRTC_RTH_COAL_MASK; reg |= ((hwm << E1000_FCRTC_RTH_COAL_SHIFT) & E1000_FCRTC_RTH_COAL_MASK); E1000_WRITE_REG(hw, E1000_FCRTC, reg); dmac = pba - max_frame_size / 512; if (dmac < pba - 10) dmac = pba - 10; reg = E1000_READ_REG(hw, E1000_DMACR); reg &= ~E1000_DMACR_DMACTHR_MASK; reg |= ((dmac << E1000_DMACR_DMACTHR_SHIFT) & E1000_DMACR_DMACTHR_MASK); /* transition to L0x or L1 if available..*/ reg |= (E1000_DMACR_DMAC_EN | E1000_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 */ if (hw->mac.type == e1000_i354) { int status = E1000_READ_REG(hw, E1000_STATUS); if ((status & E1000_STATUS_2P5_SKU) && (!(status & E1000_STATUS_2P5_SKU_OVER))) reg |= ((sc->dmac * 5) >> 6); else reg |= (sc->dmac >> 5); } else { reg |= (sc->dmac >> 5); } E1000_WRITE_REG(hw, E1000_DMACR, reg); E1000_WRITE_REG(hw, E1000_DMCRTRH, 0); /* Set the interval before transition */ reg = E1000_READ_REG(hw, E1000_DMCTLX); if (hw->mac.type == e1000_i350) reg |= IGB_DMCTLX_DCFLUSH_DIS; /* ** in 2.5Gb connection, TTLX unit is 0.4 usec ** which is 0x4*2 = 0xA. But delay is still 4 usec */ if (hw->mac.type == e1000_i354) { int status = E1000_READ_REG(hw, E1000_STATUS); if ((status & E1000_STATUS_2P5_SKU) && (!(status & E1000_STATUS_2P5_SKU_OVER))) reg |= 0xA; else reg |= 0x4; } else { reg |= 0x4; } E1000_WRITE_REG(hw, E1000_DMCTLX, reg); /* free space in tx packet buffer to wake from DMA coal */ E1000_WRITE_REG(hw, E1000_DMCTXTH, (IGB_TXPBSIZE - (2 * max_frame_size)) >> 6); /* make low power state decision controlled by DMA coal */ reg = E1000_READ_REG(hw, E1000_PCIEMISC); reg &= ~E1000_PCIEMISC_LX_DECISION; E1000_WRITE_REG(hw, E1000_PCIEMISC, reg); } else if (hw->mac.type == e1000_82580) { u32 reg = E1000_READ_REG(hw, E1000_PCIEMISC); E1000_WRITE_REG(hw, E1000_PCIEMISC, reg & ~E1000_PCIEMISC_LX_DECISION); E1000_WRITE_REG(hw, E1000_DMACR, 0); } } /********************************************************************* * The 3 following flush routines are used as a workaround in the * I219 client parts and only for them. * * em_flush_tx_ring - remove all descriptors from the tx_ring * * We want to clear all pending descriptors from the TX ring. * zeroing happens when the HW reads the regs. We assign the ring itself as * the data of the next descriptor. We don't care about the data we are about * to reset the HW. **********************************************************************/ static void em_flush_tx_ring(struct e1000_softc *sc) { struct e1000_hw *hw = &sc->hw; struct tx_ring *txr = &sc->tx_queues->txr; struct e1000_tx_desc *txd; u32 tctl, txd_lower = E1000_TXD_CMD_IFCS; u16 size = 512; tctl = E1000_READ_REG(hw, E1000_TCTL); E1000_WRITE_REG(hw, E1000_TCTL, tctl | E1000_TCTL_EN); txd = &txr->tx_base[txr->tx_cidx_processed]; /* Just use the ring as a dummy buffer addr */ txd->buffer_addr = txr->tx_paddr; txd->lower.data = htole32(txd_lower | size); txd->upper.data = 0; /* flush descriptors to memory before notifying the HW */ wmb(); E1000_WRITE_REG(hw, E1000_TDT(0), txr->tx_cidx_processed); mb(); usec_delay(250); } /********************************************************************* * em_flush_rx_ring - remove all descriptors from the rx_ring * * Mark all descriptors in the RX ring as consumed and disable the rx ring **********************************************************************/ static void em_flush_rx_ring(struct e1000_softc *sc) { struct e1000_hw *hw = &sc->hw; u32 rctl, rxdctl; rctl = E1000_READ_REG(hw, E1000_RCTL); E1000_WRITE_REG(hw, E1000_RCTL, rctl & ~E1000_RCTL_EN); E1000_WRITE_FLUSH(hw); usec_delay(150); rxdctl = E1000_READ_REG(hw, E1000_RXDCTL(0)); /* zero the lower 14 bits (prefetch and host thresholds) */ rxdctl &= 0xffffc000; /* * update thresholds: prefetch threshold to 31, host threshold to 1 * and make sure the granularity is "descriptors" and not "cache lines" */ rxdctl |= (0x1F | (1 << 8) | E1000_RXDCTL_THRESH_UNIT_DESC); E1000_WRITE_REG(hw, E1000_RXDCTL(0), rxdctl); /* momentarily enable the RX ring for the changes to take effect */ E1000_WRITE_REG(hw, E1000_RCTL, rctl | E1000_RCTL_EN); E1000_WRITE_FLUSH(hw); usec_delay(150); E1000_WRITE_REG(hw, E1000_RCTL, rctl & ~E1000_RCTL_EN); } /********************************************************************* * em_flush_desc_rings - remove all descriptors from the descriptor rings * * In I219, the descriptor rings must be emptied before resetting the HW * or before changing the device state to D3 during runtime (runtime PM). * * Failure to do this will cause the HW to enter a unit hang state which can * only be released by PCI reset on the device * **********************************************************************/ static void em_flush_desc_rings(struct e1000_softc *sc) { struct e1000_hw *hw = &sc->hw; device_t dev = sc->dev; u16 hang_state; u32 fext_nvm11, tdlen; /* First, disable MULR fix in FEXTNVM11 */ fext_nvm11 = E1000_READ_REG(hw, E1000_FEXTNVM11); fext_nvm11 |= E1000_FEXTNVM11_DISABLE_MULR_FIX; E1000_WRITE_REG(hw, E1000_FEXTNVM11, fext_nvm11); /* do nothing if we're not in faulty state, or if the queue is empty */ tdlen = E1000_READ_REG(hw, E1000_TDLEN(0)); hang_state = pci_read_config(dev, PCICFG_DESC_RING_STATUS, 2); if (!(hang_state & FLUSH_DESC_REQUIRED) || !tdlen) return; em_flush_tx_ring(sc); /* recheck, maybe the fault is caused by the rx ring */ hang_state = pci_read_config(dev, PCICFG_DESC_RING_STATUS, 2); if (hang_state & FLUSH_DESC_REQUIRED) em_flush_rx_ring(sc); } /********************************************************************* * * Initialize the hardware to a configuration as specified by the * sc structure. * **********************************************************************/ static void em_reset(if_ctx_t ctx) { device_t dev = iflib_get_dev(ctx); struct e1000_softc *sc = iflib_get_softc(ctx); struct ifnet *ifp = iflib_get_ifp(ctx); struct e1000_hw *hw = &sc->hw; u32 rx_buffer_size; u32 pba; INIT_DEBUGOUT("em_reset: begin"); /* Let the firmware know the OS is in control */ em_get_hw_control(sc); /* Set up smart power down as default off on newer adapters. */ if (!em_smart_pwr_down && (hw->mac.type == e1000_82571 || hw->mac.type == e1000_82572)) { u16 phy_tmp = 0; /* Speed up time to link by disabling smart power down. */ e1000_read_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, &phy_tmp); phy_tmp &= ~IGP02E1000_PM_SPD; e1000_write_phy_reg(hw, IGP02E1000_PHY_POWER_MGMT, phy_tmp); } /* * Packet Buffer Allocation (PBA) * Writing PBA sets the receive portion of the buffer * the remainder is used for the transmit buffer. */ switch (hw->mac.type) { /* 82547: Total Packet Buffer is 40K */ case e1000_82547: case e1000_82547_rev_2: if (hw->mac.max_frame_size > 8192) pba = E1000_PBA_22K; /* 22K for Rx, 18K for Tx */ else pba = E1000_PBA_30K; /* 30K for Rx, 10K for Tx */ break; /* 82571/82572/80003es2lan: Total Packet Buffer is 48K */ case e1000_82571: case e1000_82572: case e1000_80003es2lan: pba = E1000_PBA_32K; /* 32K for Rx, 16K for Tx */ break; /* 82573: Total Packet Buffer is 32K */ case e1000_82573: pba = E1000_PBA_12K; /* 12K for Rx, 20K for Tx */ break; case e1000_82574: case e1000_82583: pba = E1000_PBA_20K; /* 20K for Rx, 20K for Tx */ break; case e1000_ich8lan: pba = E1000_PBA_8K; break; case e1000_ich9lan: case e1000_ich10lan: /* Boost Receive side for jumbo frames */ if (hw->mac.max_frame_size > 4096) pba = E1000_PBA_14K; else pba = E1000_PBA_10K; break; case e1000_pchlan: case e1000_pch2lan: case e1000_pch_lpt: case e1000_pch_spt: case e1000_pch_cnp: case e1000_pch_tgp: case e1000_pch_adp: case e1000_pch_mtp: case e1000_pch_ptp: pba = E1000_PBA_26K; break; case e1000_82575: pba = E1000_PBA_32K; break; case e1000_82576: case e1000_vfadapt: pba = E1000_READ_REG(hw, E1000_RXPBS); pba &= E1000_RXPBS_SIZE_MASK_82576; break; case e1000_82580: case e1000_i350: case e1000_i354: case e1000_vfadapt_i350: pba = E1000_READ_REG(hw, E1000_RXPBS); pba = e1000_rxpbs_adjust_82580(pba); break; case e1000_i210: case e1000_i211: pba = E1000_PBA_34K; break; default: /* Remaining devices assumed to have a Packet Buffer of 64K. */ if (hw->mac.max_frame_size > 8192) pba = E1000_PBA_40K; /* 40K for Rx, 24K for Tx */ else pba = E1000_PBA_48K; /* 48K for Rx, 16K for Tx */ } /* Special needs in case of Jumbo frames */ if ((hw->mac.type == e1000_82575) && (ifp->if_mtu > ETHERMTU)) { u32 tx_space, min_tx, min_rx; pba = E1000_READ_REG(hw, E1000_PBA); tx_space = pba >> 16; pba &= 0xffff; min_tx = (hw->mac.max_frame_size + sizeof(struct e1000_tx_desc) - ETHERNET_FCS_SIZE) * 2; min_tx = roundup2(min_tx, 1024); min_tx >>= 10; min_rx = hw->mac.max_frame_size; min_rx = roundup2(min_rx, 1024); min_rx >>= 10; if (tx_space < min_tx && ((min_tx - tx_space) < pba)) { pba = pba - (min_tx - tx_space); /* * if short on rx space, rx wins * and must trump tx adjustment */ if (pba < min_rx) pba = min_rx; } E1000_WRITE_REG(hw, E1000_PBA, pba); } if (hw->mac.type < igb_mac_min) E1000_WRITE_REG(hw, E1000_PBA, pba); INIT_DEBUGOUT1("em_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(hw->mac.max_frame_size, 1024); hw->fc.low_water = hw->fc.high_water - 1500; if (sc->fc) /* locally set flow control value? */ hw->fc.requested_mode = sc->fc; else hw->fc.requested_mode = e1000_fc_full; if (hw->mac.type == e1000_80003es2lan) hw->fc.pause_time = 0xFFFF; else hw->fc.pause_time = EM_FC_PAUSE_TIME; hw->fc.send_xon = true; /* Device specific overrides/settings */ switch (hw->mac.type) { case e1000_pchlan: /* Workaround: no TX flow ctrl for PCH */ hw->fc.requested_mode = e1000_fc_rx_pause; hw->fc.pause_time = 0xFFFF; /* override */ if (if_getmtu(ifp) > ETHERMTU) { hw->fc.high_water = 0x3500; hw->fc.low_water = 0x1500; } else { hw->fc.high_water = 0x5000; hw->fc.low_water = 0x3000; } hw->fc.refresh_time = 0x1000; break; case e1000_pch2lan: case e1000_pch_lpt: case e1000_pch_spt: case e1000_pch_cnp: case e1000_pch_tgp: case e1000_pch_adp: case e1000_pch_mtp: case e1000_pch_ptp: hw->fc.high_water = 0x5C20; hw->fc.low_water = 0x5048; hw->fc.pause_time = 0x0650; hw->fc.refresh_time = 0x0400; /* Jumbos need adjusted PBA */ if (if_getmtu(ifp) > ETHERMTU) E1000_WRITE_REG(hw, E1000_PBA, 12); else E1000_WRITE_REG(hw, E1000_PBA, 26); break; case e1000_82575: case e1000_82576: /* 8-byte granularity */ hw->fc.low_water = hw->fc.high_water - 8; break; case e1000_82580: case e1000_i350: case e1000_i354: case e1000_i210: case e1000_i211: case e1000_vfadapt: case e1000_vfadapt_i350: /* 16-byte granularity */ hw->fc.low_water = hw->fc.high_water - 16; break; case e1000_ich9lan: case e1000_ich10lan: if (if_getmtu(ifp) > ETHERMTU) { hw->fc.high_water = 0x2800; hw->fc.low_water = hw->fc.high_water - 8; break; } /* FALLTHROUGH */ default: if (hw->mac.type == e1000_80003es2lan) hw->fc.pause_time = 0xFFFF; break; } /* I219 needs some special flushing to avoid hangs */ if (sc->hw.mac.type >= e1000_pch_spt && sc->hw.mac.type < igb_mac_min) em_flush_desc_rings(sc); /* Issue a global reset */ e1000_reset_hw(hw); if (hw->mac.type >= igb_mac_min) { E1000_WRITE_REG(hw, E1000_WUC, 0); } else { E1000_WRITE_REG(hw, E1000_WUFC, 0); em_disable_aspm(sc); } if (sc->flags & IGB_MEDIA_RESET) { e1000_setup_init_funcs(hw, true); e1000_get_bus_info(hw); sc->flags &= ~IGB_MEDIA_RESET; } /* and a re-init */ if (e1000_init_hw(hw) < 0) { device_printf(dev, "Hardware Initialization Failed\n"); return; } if (hw->mac.type >= igb_mac_min) igb_init_dmac(sc, pba); E1000_WRITE_REG(hw, E1000_VET, ETHERTYPE_VLAN); e1000_get_phy_info(hw); e1000_check_for_link(hw); } /* * Initialise the RSS mapping for NICs that support multiple transmit/ * receive rings. */ #define RSSKEYLEN 10 static void em_initialize_rss_mapping(struct e1000_softc *sc) { uint8_t rss_key[4 * RSSKEYLEN]; uint32_t reta = 0; struct e1000_hw *hw = &sc->hw; int i; /* * Configure RSS key */ arc4rand(rss_key, sizeof(rss_key), 0); for (i = 0; i < RSSKEYLEN; ++i) { uint32_t rssrk = 0; rssrk = EM_RSSRK_VAL(rss_key, i); E1000_WRITE_REG(hw,E1000_RSSRK(i), rssrk); } /* * Configure RSS redirect table in following fashion: * (hash & ring_cnt_mask) == rdr_table[(hash & rdr_table_mask)] */ for (i = 0; i < sizeof(reta); ++i) { uint32_t q; q = (i % sc->rx_num_queues) << 7; reta |= q << (8 * i); } for (i = 0; i < 32; ++i) E1000_WRITE_REG(hw, E1000_RETA(i), reta); E1000_WRITE_REG(hw, E1000_MRQC, E1000_MRQC_RSS_ENABLE_2Q | E1000_MRQC_RSS_FIELD_IPV4_TCP | E1000_MRQC_RSS_FIELD_IPV4 | E1000_MRQC_RSS_FIELD_IPV6_TCP_EX | E1000_MRQC_RSS_FIELD_IPV6_EX | E1000_MRQC_RSS_FIELD_IPV6); } static void igb_initialize_rss_mapping(struct e1000_softc *sc) { struct e1000_hw *hw = &sc->hw; int i; int queue_id; u32 reta; u32 rss_key[10], mrqc, shift = 0; /* XXX? */ if (hw->mac.type == e1000_82575) shift = 6; /* * 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 % sc->rx_num_queues; #else queue_id = (i % sc->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) { E1000_WRITE_REG(hw, E1000_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 = E1000_MRQC_ENABLE_RSS_MQ; #ifdef RSS /* XXX ew typecasting */ rss_getkey((uint8_t *) &rss_key); #else arc4rand(&rss_key, sizeof(rss_key), 0); #endif for (i = 0; i < 10; i++) E1000_WRITE_REG_ARRAY(hw, E1000_RSSRK(0), i, rss_key[i]); /* * Configure the RSS fields to hash upon. */ mrqc |= (E1000_MRQC_RSS_FIELD_IPV4 | E1000_MRQC_RSS_FIELD_IPV4_TCP); mrqc |= (E1000_MRQC_RSS_FIELD_IPV6 | E1000_MRQC_RSS_FIELD_IPV6_TCP); mrqc |=( E1000_MRQC_RSS_FIELD_IPV4_UDP | E1000_MRQC_RSS_FIELD_IPV6_UDP); mrqc |=( E1000_MRQC_RSS_FIELD_IPV6_UDP_EX | E1000_MRQC_RSS_FIELD_IPV6_TCP_EX); E1000_WRITE_REG(hw, E1000_MRQC, mrqc); } /********************************************************************* * * Setup networking device structure and register interface media. * **********************************************************************/ static int em_setup_interface(if_ctx_t ctx) { struct ifnet *ifp = iflib_get_ifp(ctx); struct e1000_softc *sc = iflib_get_softc(ctx); if_softc_ctx_t scctx = sc->shared; INIT_DEBUGOUT("em_setup_interface: begin"); /* Single Queue */ if (sc->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 */ if (sc->hw.phy.media_type == e1000_media_type_fiber || sc->hw.phy.media_type == e1000_media_type_internal_serdes) { u_char fiber_type = IFM_1000_SX; /* default type */ if (sc->hw.mac.type == e1000_82545) fiber_type = IFM_1000_LX; ifmedia_add(sc->media, IFM_ETHER | fiber_type | IFM_FDX, 0, NULL); ifmedia_add(sc->media, IFM_ETHER | fiber_type, 0, NULL); } else { ifmedia_add(sc->media, IFM_ETHER | IFM_10_T, 0, NULL); ifmedia_add(sc->media, IFM_ETHER | IFM_10_T | IFM_FDX, 0, NULL); ifmedia_add(sc->media, IFM_ETHER | IFM_100_TX, 0, NULL); ifmedia_add(sc->media, IFM_ETHER | IFM_100_TX | IFM_FDX, 0, NULL); if (sc->hw.phy.type != e1000_phy_ife) { ifmedia_add(sc->media, IFM_ETHER | IFM_1000_T | IFM_FDX, 0, NULL); ifmedia_add(sc->media, IFM_ETHER | IFM_1000_T, 0, NULL); } } ifmedia_add(sc->media, IFM_ETHER | IFM_AUTO, 0, NULL); ifmedia_set(sc->media, IFM_ETHER | IFM_AUTO); return (0); } static int em_if_tx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs, int ntxqs, int ntxqsets) { struct e1000_softc *sc = iflib_get_softc(ctx); if_softc_ctx_t scctx = sc->shared; int error = E1000_SUCCESS; struct em_tx_queue *que; int i, j; MPASS(sc->tx_num_queues > 0); MPASS(sc->tx_num_queues == ntxqsets); /* First allocate the top level queue structs */ if (!(sc->tx_queues = (struct em_tx_queue *) malloc(sizeof(struct em_tx_queue) * sc->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 = sc->tx_queues; i < sc->tx_num_queues; i++, que++) { /* Set up some basics */ struct tx_ring *txr = &que->txr; txr->sc = que->sc = sc; 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 e1000_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", sc->tx_num_queues); return (0); fail: em_if_queues_free(ctx); return (error); } static int em_if_rx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs, int nrxqs, int nrxqsets) { struct e1000_softc *sc = iflib_get_softc(ctx); int error = E1000_SUCCESS; struct em_rx_queue *que; int i; MPASS(sc->rx_num_queues > 0); MPASS(sc->rx_num_queues == nrxqsets); /* First allocate the top level queue structs */ if (!(sc->rx_queues = (struct em_rx_queue *) malloc(sizeof(struct em_rx_queue) * sc->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 = sc->rx_queues; i < nrxqsets; i++, que++) { /* Set up some basics */ struct rx_ring *rxr = &que->rxr; rxr->sc = que->sc = sc; rxr->que = que; que->me = rxr->me = i; /* get the virtual and physical address of the hardware queues */ rxr->rx_base = (union e1000_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", sc->rx_num_queues); return (0); fail: em_if_queues_free(ctx); return (error); } static void em_if_queues_free(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); struct em_tx_queue *tx_que = sc->tx_queues; struct em_rx_queue *rx_que = sc->rx_queues; if (tx_que != NULL) { for (int i = 0; i < sc->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(sc->tx_queues, M_DEVBUF); sc->tx_queues = NULL; } if (rx_que != NULL) { free(sc->rx_queues, M_DEVBUF); sc->rx_queues = NULL; } } /********************************************************************* * * Enable transmit unit. * **********************************************************************/ static void em_initialize_transmit_unit(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); if_softc_ctx_t scctx = sc->shared; struct em_tx_queue *que; struct tx_ring *txr; struct e1000_hw *hw = &sc->hw; u32 tctl, txdctl = 0, tarc, tipg = 0; INIT_DEBUGOUT("em_initialize_transmit_unit: begin"); for (int i = 0; i < sc->tx_num_queues; i++, txr++) { u64 bus_addr; caddr_t offp, endp; que = &sc->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 */ E1000_WRITE_REG(hw, E1000_TDLEN(i), scctx->isc_ntxd[0] * sizeof(struct e1000_tx_desc)); E1000_WRITE_REG(hw, E1000_TDBAH(i), (u32)(bus_addr >> 32)); E1000_WRITE_REG(hw, E1000_TDBAL(i), (u32)bus_addr); /* Init the HEAD/TAIL indices */ E1000_WRITE_REG(hw, E1000_TDT(i), 0); E1000_WRITE_REG(hw, E1000_TDH(i), 0); HW_DEBUGOUT2("Base = %x, Length = %x\n", E1000_READ_REG(hw, E1000_TDBAL(i)), E1000_READ_REG(hw, E1000_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 |= E1000_TXDCTL_GRAN; txdctl |= 1 << 25; /* LWTHRESH */ E1000_WRITE_REG(hw, E1000_TXDCTL(i), txdctl); } /* Set the default values for the Tx Inter Packet Gap timer */ switch (hw->mac.type) { case e1000_80003es2lan: tipg = DEFAULT_82543_TIPG_IPGR1; tipg |= DEFAULT_80003ES2LAN_TIPG_IPGR2 << E1000_TIPG_IPGR2_SHIFT; break; case e1000_82542: tipg = DEFAULT_82542_TIPG_IPGT; tipg |= DEFAULT_82542_TIPG_IPGR1 << E1000_TIPG_IPGR1_SHIFT; tipg |= DEFAULT_82542_TIPG_IPGR2 << E1000_TIPG_IPGR2_SHIFT; break; default: if (hw->phy.media_type == e1000_media_type_fiber || hw->phy.media_type == e1000_media_type_internal_serdes) tipg = DEFAULT_82543_TIPG_IPGT_FIBER; else tipg = DEFAULT_82543_TIPG_IPGT_COPPER; tipg |= DEFAULT_82543_TIPG_IPGR1 << E1000_TIPG_IPGR1_SHIFT; tipg |= DEFAULT_82543_TIPG_IPGR2 << E1000_TIPG_IPGR2_SHIFT; } E1000_WRITE_REG(hw, E1000_TIPG, tipg); E1000_WRITE_REG(hw, E1000_TIDV, sc->tx_int_delay.value); if(hw->mac.type >= e1000_82540) E1000_WRITE_REG(hw, E1000_TADV, sc->tx_abs_int_delay.value); if (hw->mac.type == e1000_82571 || hw->mac.type == e1000_82572) { tarc = E1000_READ_REG(hw, E1000_TARC(0)); tarc |= TARC_SPEED_MODE_BIT; E1000_WRITE_REG(hw, E1000_TARC(0), tarc); } else if (hw->mac.type == e1000_80003es2lan) { /* errata: program both queues to unweighted RR */ tarc = E1000_READ_REG(hw, E1000_TARC(0)); tarc |= 1; E1000_WRITE_REG(hw, E1000_TARC(0), tarc); tarc = E1000_READ_REG(hw, E1000_TARC(1)); tarc |= 1; E1000_WRITE_REG(hw, E1000_TARC(1), tarc); } else if (hw->mac.type == e1000_82574) { tarc = E1000_READ_REG(hw, E1000_TARC(0)); tarc |= TARC_ERRATA_BIT; if ( sc->tx_num_queues > 1) { tarc |= (TARC_COMPENSATION_MODE | TARC_MQ_FIX); E1000_WRITE_REG(hw, E1000_TARC(0), tarc); E1000_WRITE_REG(hw, E1000_TARC(1), tarc); } else E1000_WRITE_REG(hw, E1000_TARC(0), tarc); } if (sc->tx_int_delay.value > 0) sc->txd_cmd |= E1000_TXD_CMD_IDE; /* Program the Transmit Control Register */ tctl = E1000_READ_REG(hw, E1000_TCTL); tctl &= ~E1000_TCTL_CT; tctl |= (E1000_TCTL_PSP | E1000_TCTL_RTLC | E1000_TCTL_EN | (E1000_COLLISION_THRESHOLD << E1000_CT_SHIFT)); if (hw->mac.type >= e1000_82571) tctl |= E1000_TCTL_MULR; /* This write will effectively turn on the transmit unit. */ E1000_WRITE_REG(hw, E1000_TCTL, tctl); /* SPT and KBL errata workarounds */ if (hw->mac.type == e1000_pch_spt) { u32 reg; reg = E1000_READ_REG(hw, E1000_IOSFPC); reg |= E1000_RCTL_RDMTS_HEX; E1000_WRITE_REG(hw, E1000_IOSFPC, reg); /* i218-i219 Specification Update 1.5.4.5 */ reg = E1000_READ_REG(hw, E1000_TARC(0)); reg &= ~E1000_TARC0_CB_MULTIQ_3_REQ; reg |= E1000_TARC0_CB_MULTIQ_2_REQ; E1000_WRITE_REG(hw, E1000_TARC(0), reg); } } /********************************************************************* * * Enable receive unit. * **********************************************************************/ #define BSIZEPKT_ROUNDUP ((1<shared; struct ifnet *ifp = iflib_get_ifp(ctx); struct e1000_hw *hw = &sc->hw; struct em_rx_queue *que; int i; uint32_t rctl, rxcsum; INIT_DEBUGOUT("em_initialize_receive_units: begin"); /* * Make sure receives are disabled while setting * up the descriptor ring */ rctl = E1000_READ_REG(hw, E1000_RCTL); /* Do not disable if ever enabled on this hardware */ if ((hw->mac.type != e1000_82574) && (hw->mac.type != e1000_82583)) E1000_WRITE_REG(hw, E1000_RCTL, rctl & ~E1000_RCTL_EN); /* Setup the Receive Control Register */ rctl &= ~(3 << E1000_RCTL_MO_SHIFT); rctl |= E1000_RCTL_EN | E1000_RCTL_BAM | E1000_RCTL_LBM_NO | E1000_RCTL_RDMTS_HALF | (hw->mac.mc_filter_type << E1000_RCTL_MO_SHIFT); /* Do not store bad packets */ rctl &= ~E1000_RCTL_SBP; /* Enable Long Packet receive */ if (if_getmtu(ifp) > ETHERMTU) rctl |= E1000_RCTL_LPE; else rctl &= ~E1000_RCTL_LPE; /* Strip the CRC */ if (!em_disable_crc_stripping) rctl |= E1000_RCTL_SECRC; if (hw->mac.type >= e1000_82540) { E1000_WRITE_REG(hw, E1000_RADV, sc->rx_abs_int_delay.value); /* * Set the interrupt throttling rate. Value is calculated * as DEFAULT_ITR = 1/(MAX_INTS_PER_SEC * 256ns) */ E1000_WRITE_REG(hw, E1000_ITR, DEFAULT_ITR); } E1000_WRITE_REG(hw, E1000_RDTR, sc->rx_int_delay.value); if (hw->mac.type >= em_mac_min) { uint32_t rfctl; /* Use extended rx descriptor formats */ rfctl = E1000_READ_REG(hw, E1000_RFCTL); rfctl |= E1000_RFCTL_EXTEN; /* * When using MSI-X interrupts we need to throttle * using the EITR register (82574 only) */ if (hw->mac.type == e1000_82574) { for (int i = 0; i < 4; i++) E1000_WRITE_REG(hw, E1000_EITR_82574(i), DEFAULT_ITR); /* Disable accelerated acknowledge */ rfctl |= E1000_RFCTL_ACK_DIS; } E1000_WRITE_REG(hw, E1000_RFCTL, rfctl); } /* Set up L3 and L4 csum Rx descriptor offloads */ rxcsum = E1000_READ_REG(hw, E1000_RXCSUM); if (if_getcapenable(ifp) & IFCAP_RXCSUM) { rxcsum |= E1000_RXCSUM_TUOFL | E1000_RXCSUM_IPOFL; if (hw->mac.type > e1000_82575) rxcsum |= E1000_RXCSUM_CRCOFL; else if (hw->mac.type < em_mac_min && if_getcapenable(ifp) & IFCAP_HWCSUM_IPV6) rxcsum |= E1000_RXCSUM_IPV6OFL; } else { rxcsum &= ~(E1000_RXCSUM_IPOFL | E1000_RXCSUM_TUOFL); if (hw->mac.type > e1000_82575) rxcsum &= ~E1000_RXCSUM_CRCOFL; else if (hw->mac.type < em_mac_min) rxcsum &= ~E1000_RXCSUM_IPV6OFL; } if (sc->rx_num_queues > 1) { /* RSS hash needed in the Rx descriptor */ rxcsum |= E1000_RXCSUM_PCSD; if (hw->mac.type >= igb_mac_min) igb_initialize_rss_mapping(sc); else em_initialize_rss_mapping(sc); } E1000_WRITE_REG(hw, E1000_RXCSUM, rxcsum); /* * XXX TEMPORARY WORKAROUND: on some systems with 82573 * long latencies are observed, like Lenovo X60. This * change eliminates the problem, but since having positive * values in RDTR is a known source of problems on other * platforms another solution is being sought. */ if (hw->mac.type == e1000_82573) E1000_WRITE_REG(hw, E1000_RDTR, 0x20); for (i = 0, que = sc->rx_queues; i < sc->rx_num_queues; i++, que++) { struct rx_ring *rxr = &que->rxr; /* Setup the Base and Length of the Rx Descriptor Ring */ u64 bus_addr = rxr->rx_paddr; #if 0 u32 rdt = sc->rx_num_queues -1; /* default */ #endif E1000_WRITE_REG(hw, E1000_RDLEN(i), scctx->isc_nrxd[0] * sizeof(union e1000_rx_desc_extended)); E1000_WRITE_REG(hw, E1000_RDBAH(i), (u32)(bus_addr >> 32)); E1000_WRITE_REG(hw, E1000_RDBAL(i), (u32)bus_addr); /* Setup the Head and Tail Descriptor Pointers */ E1000_WRITE_REG(hw, E1000_RDH(i), 0); E1000_WRITE_REG(hw, E1000_RDT(i), 0); } /* * Set PTHRESH for improved jumbo performance * According to 10.2.5.11 of Intel 82574 Datasheet, * RXDCTL(1) is written whenever RXDCTL(0) is written. * Only write to RXDCTL(1) if there is a need for different * settings. */ if ((hw->mac.type == e1000_ich9lan || hw->mac.type == e1000_pch2lan || hw->mac.type == e1000_ich10lan) && if_getmtu(ifp) > ETHERMTU) { u32 rxdctl = E1000_READ_REG(hw, E1000_RXDCTL(0)); E1000_WRITE_REG(hw, E1000_RXDCTL(0), rxdctl | 3); } else if (hw->mac.type == e1000_82574) { for (int i = 0; i < sc->rx_num_queues; i++) { u32 rxdctl = E1000_READ_REG(hw, E1000_RXDCTL(i)); rxdctl |= 0x20; /* PTHRESH */ rxdctl |= 4 << 8; /* HTHRESH */ rxdctl |= 4 << 16;/* WTHRESH */ rxdctl |= 1 << 24; /* Switch to granularity */ E1000_WRITE_REG(hw, E1000_RXDCTL(i), rxdctl); } } else if (hw->mac.type >= igb_mac_min) { u32 psize, srrctl = 0; if (if_getmtu(ifp) > ETHERMTU) { psize = scctx->isc_max_frame_size; /* are we on a vlan? */ if (ifp->if_vlantrunk != NULL) psize += VLAN_TAG_SIZE; if (sc->vf_ifp) e1000_rlpml_set_vf(hw, psize); else E1000_WRITE_REG(hw, E1000_RLPML, psize); } /* Set maximum packet buffer len */ srrctl |= (sc->rx_mbuf_sz + BSIZEPKT_ROUNDUP) >> E1000_SRRCTL_BSIZEPKT_SHIFT; /* * If TX flow control is disabled and there's >1 queue defined, * enable DROP. * * This drops frames rather than hanging the RX MAC for all queues. */ if ((sc->rx_num_queues > 1) && (sc->fc == e1000_fc_none || sc->fc == e1000_fc_rx_pause)) { srrctl |= E1000_SRRCTL_DROP_EN; } /* Setup the Base and Length of the Rx Descriptor Rings */ for (i = 0, que = sc->rx_queues; i < sc->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 = igb_header_split; #else srrctl |= E1000_SRRCTL_DESCTYPE_ADV_ONEBUF; #endif E1000_WRITE_REG(hw, E1000_RDLEN(i), scctx->isc_nrxd[0] * sizeof(struct e1000_rx_desc)); E1000_WRITE_REG(hw, E1000_RDBAH(i), (uint32_t)(bus_addr >> 32)); E1000_WRITE_REG(hw, E1000_RDBAL(i), (uint32_t)bus_addr); E1000_WRITE_REG(hw, E1000_SRRCTL(i), srrctl); /* Enable this Queue */ rxdctl = E1000_READ_REG(hw, E1000_RXDCTL(i)); rxdctl |= E1000_RXDCTL_QUEUE_ENABLE; rxdctl &= 0xFFF00000; rxdctl |= IGB_RX_PTHRESH; rxdctl |= IGB_RX_HTHRESH << 8; rxdctl |= IGB_RX_WTHRESH << 16; E1000_WRITE_REG(hw, E1000_RXDCTL(i), rxdctl); } } else if (hw->mac.type >= e1000_pch2lan) { if (if_getmtu(ifp) > ETHERMTU) e1000_lv_jumbo_workaround_ich8lan(hw, true); else e1000_lv_jumbo_workaround_ich8lan(hw, false); } /* Make sure VLAN Filters are off */ rctl &= ~E1000_RCTL_VFE; /* Set up packet buffer size, overridden by per queue srrctl on igb */ if (hw->mac.type < igb_mac_min) { if (sc->rx_mbuf_sz > 2048 && sc->rx_mbuf_sz <= 4096) rctl |= E1000_RCTL_SZ_4096 | E1000_RCTL_BSEX; else if (sc->rx_mbuf_sz > 4096 && sc->rx_mbuf_sz <= 8192) rctl |= E1000_RCTL_SZ_8192 | E1000_RCTL_BSEX; else if (sc->rx_mbuf_sz > 8192) rctl |= E1000_RCTL_SZ_16384 | E1000_RCTL_BSEX; else { rctl |= E1000_RCTL_SZ_2048; rctl &= ~E1000_RCTL_BSEX; } } else rctl |= E1000_RCTL_SZ_2048; /* * rctl bits 11:10 are as follows * lem: reserved * em: DTYPE * igb: reserved * and should be 00 on all of the above */ rctl &= ~0x00000C00; /* Write out the settings */ E1000_WRITE_REG(hw, E1000_RCTL, rctl); return; } static void em_if_vlan_register(if_ctx_t ctx, u16 vtag) { struct e1000_softc *sc = iflib_get_softc(ctx); u32 index, bit; index = (vtag >> 5) & 0x7F; bit = vtag & 0x1F; sc->shadow_vfta[index] |= (1 << bit); ++sc->num_vlans; em_if_vlan_filter_write(sc); } static void em_if_vlan_unregister(if_ctx_t ctx, u16 vtag) { struct e1000_softc *sc = iflib_get_softc(ctx); u32 index, bit; index = (vtag >> 5) & 0x7F; bit = vtag & 0x1F; sc->shadow_vfta[index] &= ~(1 << bit); --sc->num_vlans; em_if_vlan_filter_write(sc); } static bool em_if_vlan_filter_capable(if_ctx_t ctx) { if_t ifp = iflib_get_ifp(ctx); if ((if_getcapenable(ifp) & IFCAP_VLAN_HWFILTER) && !em_disable_crc_stripping) return (true); return (false); } static bool em_if_vlan_filter_used(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); if (!em_if_vlan_filter_capable(ctx)) return (false); for (int i = 0; i < EM_VFTA_SIZE; i++) if (sc->shadow_vfta[i] != 0) return (true); return (false); } static void em_if_vlan_filter_enable(struct e1000_softc *sc) { struct e1000_hw *hw = &sc->hw; u32 reg; reg = E1000_READ_REG(hw, E1000_RCTL); reg &= ~E1000_RCTL_CFIEN; reg |= E1000_RCTL_VFE; E1000_WRITE_REG(hw, E1000_RCTL, reg); } static void em_if_vlan_filter_disable(struct e1000_softc *sc) { struct e1000_hw *hw = &sc->hw; u32 reg; reg = E1000_READ_REG(hw, E1000_RCTL); reg &= ~(E1000_RCTL_VFE | E1000_RCTL_CFIEN); E1000_WRITE_REG(hw, E1000_RCTL, reg); } static void em_if_vlan_filter_write(struct e1000_softc *sc) { struct e1000_hw *hw = &sc->hw; if (sc->vf_ifp) return; /* Disable interrupts for lem-class devices during the filter change */ if (hw->mac.type < em_mac_min) em_if_intr_disable(sc->ctx); for (int i = 0; i < EM_VFTA_SIZE; i++) if (sc->shadow_vfta[i] != 0) { /* XXXKB: incomplete VF support, we return early above */ if (sc->vf_ifp) e1000_vfta_set_vf(hw, sc->shadow_vfta[i], true); else e1000_write_vfta(hw, i, sc->shadow_vfta[i]); } /* Re-enable interrupts for lem-class devices */ if (hw->mac.type < em_mac_min) em_if_intr_enable(sc->ctx); } static void em_setup_vlan_hw_support(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); struct e1000_hw *hw = &sc->hw; struct ifnet *ifp = iflib_get_ifp(ctx); u32 reg; /* XXXKB: Return early if we are a VF until VF decap and filter management * is ready and tested. */ if (sc->vf_ifp) return; if (if_getcapenable(ifp) & IFCAP_VLAN_HWTAGGING && !em_disable_crc_stripping) { reg = E1000_READ_REG(hw, E1000_CTRL); reg |= E1000_CTRL_VME; E1000_WRITE_REG(hw, E1000_CTRL, reg); } else { reg = E1000_READ_REG(hw, E1000_CTRL); reg &= ~E1000_CTRL_VME; E1000_WRITE_REG(hw, E1000_CTRL, reg); } /* If we aren't doing HW filtering, we're done */ if (!em_if_vlan_filter_capable(ctx)) { em_if_vlan_filter_disable(sc); return; } /* * A soft reset zero's out the VFTA, so * we need to repopulate it now. * We also insert VLAN 0 in the filter list, so we pass VLAN 0 tagged * traffic through. This will write the entire table. */ em_if_vlan_register(ctx, 0); /* Enable the Filter Table */ em_if_vlan_filter_enable(sc); } static void em_if_intr_enable(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); struct e1000_hw *hw = &sc->hw; u32 ims_mask = IMS_ENABLE_MASK; if (sc->intr_type == IFLIB_INTR_MSIX) { E1000_WRITE_REG(hw, EM_EIAC, sc->ims); ims_mask |= sc->ims; } E1000_WRITE_REG(hw, E1000_IMS, ims_mask); E1000_WRITE_FLUSH(hw); } static void em_if_intr_disable(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); struct e1000_hw *hw = &sc->hw; if (sc->intr_type == IFLIB_INTR_MSIX) E1000_WRITE_REG(hw, EM_EIAC, 0); E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); E1000_WRITE_FLUSH(hw); } static void igb_if_intr_enable(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); struct e1000_hw *hw = &sc->hw; u32 mask; if (__predict_true(sc->intr_type == IFLIB_INTR_MSIX)) { mask = (sc->que_mask | sc->link_mask); E1000_WRITE_REG(hw, E1000_EIAC, mask); E1000_WRITE_REG(hw, E1000_EIAM, mask); E1000_WRITE_REG(hw, E1000_EIMS, mask); E1000_WRITE_REG(hw, E1000_IMS, E1000_IMS_LSC); } else E1000_WRITE_REG(hw, E1000_IMS, IMS_ENABLE_MASK); E1000_WRITE_FLUSH(hw); } static void igb_if_intr_disable(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); struct e1000_hw *hw = &sc->hw; if (__predict_true(sc->intr_type == IFLIB_INTR_MSIX)) { E1000_WRITE_REG(hw, E1000_EIMC, 0xffffffff); E1000_WRITE_REG(hw, E1000_EIAC, 0); } E1000_WRITE_REG(hw, E1000_IMC, 0xffffffff); E1000_WRITE_FLUSH(hw); } /* * Bit of a misnomer, what this really means is * to enable OS management of the system... aka * to disable special hardware management features */ static void em_init_manageability(struct e1000_softc *sc) { /* A shared code workaround */ #define E1000_82542_MANC2H E1000_MANC2H if (sc->has_manage) { int manc2h = E1000_READ_REG(&sc->hw, E1000_MANC2H); int manc = E1000_READ_REG(&sc->hw, E1000_MANC); /* disable hardware interception of ARP */ manc &= ~(E1000_MANC_ARP_EN); /* enable receiving management packets to the host */ manc |= E1000_MANC_EN_MNG2HOST; #define E1000_MNG2HOST_PORT_623 (1 << 5) #define E1000_MNG2HOST_PORT_664 (1 << 6) manc2h |= E1000_MNG2HOST_PORT_623; manc2h |= E1000_MNG2HOST_PORT_664; E1000_WRITE_REG(&sc->hw, E1000_MANC2H, manc2h); E1000_WRITE_REG(&sc->hw, E1000_MANC, manc); } } /* * Give control back to hardware management * controller if there is one. */ static void em_release_manageability(struct e1000_softc *sc) { if (sc->has_manage) { int manc = E1000_READ_REG(&sc->hw, E1000_MANC); /* re-enable hardware interception of ARP */ manc |= E1000_MANC_ARP_EN; manc &= ~E1000_MANC_EN_MNG2HOST; E1000_WRITE_REG(&sc->hw, E1000_MANC, manc); } } /* * em_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 em_get_hw_control(struct e1000_softc *sc) { u32 ctrl_ext, swsm; if (sc->vf_ifp) return; if (sc->hw.mac.type == e1000_82573) { swsm = E1000_READ_REG(&sc->hw, E1000_SWSM); E1000_WRITE_REG(&sc->hw, E1000_SWSM, swsm | E1000_SWSM_DRV_LOAD); return; } /* else */ ctrl_ext = E1000_READ_REG(&sc->hw, E1000_CTRL_EXT); E1000_WRITE_REG(&sc->hw, E1000_CTRL_EXT, ctrl_ext | E1000_CTRL_EXT_DRV_LOAD); } /* * em_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 em_release_hw_control(struct e1000_softc *sc) { u32 ctrl_ext, swsm; if (!sc->has_manage) return; if (sc->hw.mac.type == e1000_82573) { swsm = E1000_READ_REG(&sc->hw, E1000_SWSM); E1000_WRITE_REG(&sc->hw, E1000_SWSM, swsm & ~E1000_SWSM_DRV_LOAD); return; } /* else */ ctrl_ext = E1000_READ_REG(&sc->hw, E1000_CTRL_EXT); E1000_WRITE_REG(&sc->hw, E1000_CTRL_EXT, ctrl_ext & ~E1000_CTRL_EXT_DRV_LOAD); return; } static int em_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); } static bool em_automask_tso(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); if_softc_ctx_t scctx = iflib_get_softc_ctx(ctx); if_t ifp = iflib_get_ifp(ctx); if (!em_unsupported_tso && sc->link_speed && sc->link_speed != SPEED_1000 && scctx->isc_capenable & IFCAP_TSO) { device_printf(sc->dev, "Disabling TSO for 10/100 Ethernet.\n"); sc->tso_automasked = scctx->isc_capenable & IFCAP_TSO; scctx->isc_capenable &= ~IFCAP_TSO; if_setcapenablebit(ifp, 0, IFCAP_TSO); /* iflib_init_locked handles ifnet hwassistbits */ iflib_request_reset(ctx); return true; } else if (sc->link_speed == SPEED_1000 && sc->tso_automasked) { device_printf(sc->dev, "Re-enabling TSO for GbE.\n"); scctx->isc_capenable |= sc->tso_automasked; if_setcapenablebit(ifp, sc->tso_automasked, 0); sc->tso_automasked = 0; /* iflib_init_locked handles ifnet hwassistbits */ iflib_request_reset(ctx); return true; } return false; } /* ** Parse the interface capabilities with regard ** to both system management and wake-on-lan for ** later use. */ static void em_get_wakeup(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); device_t dev = iflib_get_dev(ctx); u16 eeprom_data = 0, device_id, apme_mask; sc->has_manage = e1000_enable_mng_pass_thru(&sc->hw); apme_mask = EM_EEPROM_APME; switch (sc->hw.mac.type) { case e1000_82542: case e1000_82543: break; case e1000_82544: e1000_read_nvm(&sc->hw, NVM_INIT_CONTROL2_REG, 1, &eeprom_data); apme_mask = EM_82544_APME; break; case e1000_82546: case e1000_82546_rev_3: if (sc->hw.bus.func == 1) { e1000_read_nvm(&sc->hw, NVM_INIT_CONTROL3_PORT_B, 1, &eeprom_data); break; } else e1000_read_nvm(&sc->hw, NVM_INIT_CONTROL3_PORT_A, 1, &eeprom_data); break; case e1000_82573: case e1000_82583: sc->has_amt = true; /* FALLTHROUGH */ case e1000_82571: case e1000_82572: case e1000_80003es2lan: if (sc->hw.bus.func == 1) { e1000_read_nvm(&sc->hw, NVM_INIT_CONTROL3_PORT_B, 1, &eeprom_data); break; } else e1000_read_nvm(&sc->hw, NVM_INIT_CONTROL3_PORT_A, 1, &eeprom_data); break; case e1000_ich8lan: case e1000_ich9lan: case e1000_ich10lan: case e1000_pchlan: case e1000_pch2lan: case e1000_pch_lpt: case e1000_pch_spt: case e1000_82575: /* listing all igb devices */ case e1000_82576: case e1000_82580: case e1000_i350: case e1000_i354: case e1000_i210: case e1000_i211: case e1000_vfadapt: case e1000_vfadapt_i350: apme_mask = E1000_WUC_APME; sc->has_amt = true; eeprom_data = E1000_READ_REG(&sc->hw, E1000_WUC); break; default: e1000_read_nvm(&sc->hw, NVM_INIT_CONTROL3_PORT_A, 1, &eeprom_data); break; } if (eeprom_data & apme_mask) sc->wol = (E1000_WUFC_MAG | E1000_WUFC_MC); /* * We have the eeprom settings, now apply the special cases * where the eeprom may be wrong or the board won't support * wake on lan on a particular port */ device_id = pci_get_device(dev); switch (device_id) { case E1000_DEV_ID_82546GB_PCIE: sc->wol = 0; break; case E1000_DEV_ID_82546EB_FIBER: case E1000_DEV_ID_82546GB_FIBER: /* Wake events only supported on port A for dual fiber * regardless of eeprom setting */ if (E1000_READ_REG(&sc->hw, E1000_STATUS) & E1000_STATUS_FUNC_1) sc->wol = 0; break; case E1000_DEV_ID_82546GB_QUAD_COPPER_KSP3: /* if quad port adapter, disable WoL on all but port A */ if (global_quad_port_a != 0) sc->wol = 0; /* Reset for multiple quad port adapters */ if (++global_quad_port_a == 4) global_quad_port_a = 0; break; case E1000_DEV_ID_82571EB_FIBER: /* Wake events only supported on port A for dual fiber * regardless of eeprom setting */ if (E1000_READ_REG(&sc->hw, E1000_STATUS) & E1000_STATUS_FUNC_1) sc->wol = 0; break; case E1000_DEV_ID_82571EB_QUAD_COPPER: case E1000_DEV_ID_82571EB_QUAD_FIBER: case E1000_DEV_ID_82571EB_QUAD_COPPER_LP: /* if quad port adapter, disable WoL on all but port A */ if (global_quad_port_a != 0) sc->wol = 0; /* Reset for multiple quad port adapters */ if (++global_quad_port_a == 4) global_quad_port_a = 0; break; } return; } /* * Enable PCI Wake On Lan capability */ static void em_enable_wakeup(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); device_t dev = iflib_get_dev(ctx); if_t ifp = iflib_get_ifp(ctx); int error = 0; u32 pmc, ctrl, ctrl_ext, 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) sc->wol &= ~E1000_WUFC_MAG; if ((if_getcapenable(ifp) & IFCAP_WOL_UCAST) == 0) sc->wol &= ~E1000_WUFC_EX; if ((if_getcapenable(ifp) & IFCAP_WOL_MCAST) == 0) sc->wol &= ~E1000_WUFC_MC; else { rctl = E1000_READ_REG(&sc->hw, E1000_RCTL); rctl |= E1000_RCTL_MPE; E1000_WRITE_REG(&sc->hw, E1000_RCTL, rctl); } if (!(sc->wol & (E1000_WUFC_EX | E1000_WUFC_MAG | E1000_WUFC_MC))) goto pme; /* Advertise the wakeup capability */ ctrl = E1000_READ_REG(&sc->hw, E1000_CTRL); ctrl |= (E1000_CTRL_SWDPIN2 | E1000_CTRL_SWDPIN3); E1000_WRITE_REG(&sc->hw, E1000_CTRL, ctrl); /* Keep the laser running on Fiber adapters */ if (sc->hw.phy.media_type == e1000_media_type_fiber || sc->hw.phy.media_type == e1000_media_type_internal_serdes) { ctrl_ext = E1000_READ_REG(&sc->hw, E1000_CTRL_EXT); ctrl_ext |= E1000_CTRL_EXT_SDP3_DATA; E1000_WRITE_REG(&sc->hw, E1000_CTRL_EXT, ctrl_ext); } if ((sc->hw.mac.type == e1000_ich8lan) || (sc->hw.mac.type == e1000_pchlan) || (sc->hw.mac.type == e1000_ich9lan) || (sc->hw.mac.type == e1000_ich10lan)) e1000_suspend_workarounds_ich8lan(&sc->hw); if ( sc->hw.mac.type >= e1000_pchlan) { error = em_enable_phy_wakeup(sc); if (error) goto pme; } else { /* Enable wakeup by the MAC */ E1000_WRITE_REG(&sc->hw, E1000_WUC, E1000_WUC_PME_EN); E1000_WRITE_REG(&sc->hw, E1000_WUFC, sc->wol); } if (sc->hw.phy.type == e1000_phy_igp_3) e1000_igp3_phy_powerdown_workaround_ich8lan(&sc->hw); 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; } /* * WOL in the newer chipset interfaces (pchlan) * require thing to be copied into the phy */ static int em_enable_phy_wakeup(struct e1000_softc *sc) { struct e1000_hw *hw = &sc->hw; u32 mreg, ret = 0; u16 preg; /* copy MAC RARs to PHY RARs */ e1000_copy_rx_addrs_to_phy_ich8lan(hw); /* copy MAC MTA to PHY MTA */ for (int i = 0; i < hw->mac.mta_reg_count; i++) { mreg = E1000_READ_REG_ARRAY(hw, E1000_MTA, i); e1000_write_phy_reg(hw, BM_MTA(i), (u16)(mreg & 0xFFFF)); e1000_write_phy_reg(hw, BM_MTA(i) + 1, (u16)((mreg >> 16) & 0xFFFF)); } /* configure PHY Rx Control register */ e1000_read_phy_reg(hw, BM_RCTL, &preg); mreg = E1000_READ_REG(hw, E1000_RCTL); if (mreg & E1000_RCTL_UPE) preg |= BM_RCTL_UPE; if (mreg & E1000_RCTL_MPE) preg |= BM_RCTL_MPE; preg &= ~(BM_RCTL_MO_MASK); if (mreg & E1000_RCTL_MO_3) preg |= (((mreg & E1000_RCTL_MO_3) >> E1000_RCTL_MO_SHIFT) << BM_RCTL_MO_SHIFT); if (mreg & E1000_RCTL_BAM) preg |= BM_RCTL_BAM; if (mreg & E1000_RCTL_PMCF) preg |= BM_RCTL_PMCF; mreg = E1000_READ_REG(hw, E1000_CTRL); if (mreg & E1000_CTRL_RFCE) preg |= BM_RCTL_RFCE; e1000_write_phy_reg(hw, BM_RCTL, preg); /* enable PHY wakeup in MAC register */ E1000_WRITE_REG(hw, E1000_WUC, E1000_WUC_PHY_WAKE | E1000_WUC_PME_EN | E1000_WUC_APME); E1000_WRITE_REG(hw, E1000_WUFC, sc->wol); /* configure and enable PHY wakeup in PHY registers */ e1000_write_phy_reg(hw, BM_WUFC, sc->wol); e1000_write_phy_reg(hw, BM_WUC, E1000_WUC_PME_EN); /* activate PHY wakeup */ ret = hw->phy.ops.acquire(hw); if (ret) { printf("Could not acquire PHY\n"); return ret; } e1000_write_phy_reg_mdic(hw, IGP01E1000_PHY_PAGE_SELECT, (BM_WUC_ENABLE_PAGE << IGP_PAGE_SHIFT)); ret = e1000_read_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, &preg); if (ret) { printf("Could not read PHY page 769\n"); goto out; } preg |= BM_WUC_ENABLE_BIT | BM_WUC_HOST_WU_BIT; ret = e1000_write_phy_reg_mdic(hw, BM_WUC_ENABLE_REG, preg); if (ret) printf("Could not set PHY Host Wakeup bit\n"); out: hw->phy.ops.release(hw); return ret; } static void em_if_led_func(if_ctx_t ctx, int onoff) { struct e1000_softc *sc = iflib_get_softc(ctx); if (onoff) { e1000_setup_led(&sc->hw); e1000_led_on(&sc->hw); } else { e1000_led_off(&sc->hw); e1000_cleanup_led(&sc->hw); } } /* * Disable the L0S and L1 LINK states */ static void em_disable_aspm(struct e1000_softc *sc) { int base, reg; u16 link_cap,link_ctrl; device_t dev = sc->dev; switch (sc->hw.mac.type) { case e1000_82573: case e1000_82574: case e1000_82583: break; default: return; } if (pci_find_cap(dev, PCIY_EXPRESS, &base) != 0) return; reg = base + PCIER_LINK_CAP; link_cap = pci_read_config(dev, reg, 2); if ((link_cap & PCIEM_LINK_CAP_ASPM) == 0) return; reg = base + PCIER_LINK_CTL; link_ctrl = pci_read_config(dev, reg, 2); link_ctrl &= ~PCIEM_LINK_CTL_ASPMC; pci_write_config(dev, reg, link_ctrl, 2); return; } /********************************************************************** * * Update the board statistics counters. * **********************************************************************/ static void em_update_stats_counters(struct e1000_softc *sc) { u64 prev_xoffrxc = sc->stats.xoffrxc; if(sc->hw.phy.media_type == e1000_media_type_copper || (E1000_READ_REG(&sc->hw, E1000_STATUS) & E1000_STATUS_LU)) { sc->stats.symerrs += E1000_READ_REG(&sc->hw, E1000_SYMERRS); sc->stats.sec += E1000_READ_REG(&sc->hw, E1000_SEC); } sc->stats.crcerrs += E1000_READ_REG(&sc->hw, E1000_CRCERRS); sc->stats.mpc += E1000_READ_REG(&sc->hw, E1000_MPC); sc->stats.scc += E1000_READ_REG(&sc->hw, E1000_SCC); sc->stats.ecol += E1000_READ_REG(&sc->hw, E1000_ECOL); sc->stats.mcc += E1000_READ_REG(&sc->hw, E1000_MCC); sc->stats.latecol += E1000_READ_REG(&sc->hw, E1000_LATECOL); sc->stats.colc += E1000_READ_REG(&sc->hw, E1000_COLC); sc->stats.dc += E1000_READ_REG(&sc->hw, E1000_DC); sc->stats.rlec += E1000_READ_REG(&sc->hw, E1000_RLEC); sc->stats.xonrxc += E1000_READ_REG(&sc->hw, E1000_XONRXC); sc->stats.xontxc += E1000_READ_REG(&sc->hw, E1000_XONTXC); sc->stats.xoffrxc += E1000_READ_REG(&sc->hw, E1000_XOFFRXC); /* ** For watchdog management we need to know if we have been ** paused during the last interval, so capture that here. */ if (sc->stats.xoffrxc != prev_xoffrxc) sc->shared->isc_pause_frames = 1; sc->stats.xofftxc += E1000_READ_REG(&sc->hw, E1000_XOFFTXC); sc->stats.fcruc += E1000_READ_REG(&sc->hw, E1000_FCRUC); sc->stats.prc64 += E1000_READ_REG(&sc->hw, E1000_PRC64); sc->stats.prc127 += E1000_READ_REG(&sc->hw, E1000_PRC127); sc->stats.prc255 += E1000_READ_REG(&sc->hw, E1000_PRC255); sc->stats.prc511 += E1000_READ_REG(&sc->hw, E1000_PRC511); sc->stats.prc1023 += E1000_READ_REG(&sc->hw, E1000_PRC1023); sc->stats.prc1522 += E1000_READ_REG(&sc->hw, E1000_PRC1522); sc->stats.gprc += E1000_READ_REG(&sc->hw, E1000_GPRC); sc->stats.bprc += E1000_READ_REG(&sc->hw, E1000_BPRC); sc->stats.mprc += E1000_READ_REG(&sc->hw, E1000_MPRC); sc->stats.gptc += E1000_READ_REG(&sc->hw, E1000_GPTC); /* For the 64-bit byte counters the low dword must be read first. */ /* Both registers clear on the read of the high dword */ sc->stats.gorc += E1000_READ_REG(&sc->hw, E1000_GORCL) + ((u64)E1000_READ_REG(&sc->hw, E1000_GORCH) << 32); sc->stats.gotc += E1000_READ_REG(&sc->hw, E1000_GOTCL) + ((u64)E1000_READ_REG(&sc->hw, E1000_GOTCH) << 32); sc->stats.rnbc += E1000_READ_REG(&sc->hw, E1000_RNBC); sc->stats.ruc += E1000_READ_REG(&sc->hw, E1000_RUC); sc->stats.rfc += E1000_READ_REG(&sc->hw, E1000_RFC); sc->stats.roc += E1000_READ_REG(&sc->hw, E1000_ROC); sc->stats.rjc += E1000_READ_REG(&sc->hw, E1000_RJC); sc->stats.tor += E1000_READ_REG(&sc->hw, E1000_TORH); sc->stats.tot += E1000_READ_REG(&sc->hw, E1000_TOTH); sc->stats.tpr += E1000_READ_REG(&sc->hw, E1000_TPR); sc->stats.tpt += E1000_READ_REG(&sc->hw, E1000_TPT); sc->stats.ptc64 += E1000_READ_REG(&sc->hw, E1000_PTC64); sc->stats.ptc127 += E1000_READ_REG(&sc->hw, E1000_PTC127); sc->stats.ptc255 += E1000_READ_REG(&sc->hw, E1000_PTC255); sc->stats.ptc511 += E1000_READ_REG(&sc->hw, E1000_PTC511); sc->stats.ptc1023 += E1000_READ_REG(&sc->hw, E1000_PTC1023); sc->stats.ptc1522 += E1000_READ_REG(&sc->hw, E1000_PTC1522); sc->stats.mptc += E1000_READ_REG(&sc->hw, E1000_MPTC); sc->stats.bptc += E1000_READ_REG(&sc->hw, E1000_BPTC); /* Interrupt Counts */ sc->stats.iac += E1000_READ_REG(&sc->hw, E1000_IAC); sc->stats.icrxptc += E1000_READ_REG(&sc->hw, E1000_ICRXPTC); sc->stats.icrxatc += E1000_READ_REG(&sc->hw, E1000_ICRXATC); sc->stats.ictxptc += E1000_READ_REG(&sc->hw, E1000_ICTXPTC); sc->stats.ictxatc += E1000_READ_REG(&sc->hw, E1000_ICTXATC); sc->stats.ictxqec += E1000_READ_REG(&sc->hw, E1000_ICTXQEC); sc->stats.ictxqmtc += E1000_READ_REG(&sc->hw, E1000_ICTXQMTC); sc->stats.icrxdmtc += E1000_READ_REG(&sc->hw, E1000_ICRXDMTC); sc->stats.icrxoc += E1000_READ_REG(&sc->hw, E1000_ICRXOC); if (sc->hw.mac.type >= e1000_82543) { sc->stats.algnerrc += E1000_READ_REG(&sc->hw, E1000_ALGNERRC); sc->stats.rxerrc += E1000_READ_REG(&sc->hw, E1000_RXERRC); sc->stats.tncrs += E1000_READ_REG(&sc->hw, E1000_TNCRS); sc->stats.cexterr += E1000_READ_REG(&sc->hw, E1000_CEXTERR); sc->stats.tsctc += E1000_READ_REG(&sc->hw, E1000_TSCTC); sc->stats.tsctfc += E1000_READ_REG(&sc->hw, E1000_TSCTFC); } } static uint64_t em_if_get_counter(if_ctx_t ctx, ift_counter cnt) { struct e1000_softc *sc = iflib_get_softc(ctx); struct ifnet *ifp = iflib_get_ifp(ctx); switch (cnt) { case IFCOUNTER_COLLISIONS: return (sc->stats.colc); case IFCOUNTER_IERRORS: return (sc->dropped_pkts + sc->stats.rxerrc + sc->stats.crcerrs + sc->stats.algnerrc + sc->stats.ruc + sc->stats.roc + sc->stats.mpc + sc->stats.cexterr); case IFCOUNTER_OERRORS: return (sc->stats.ecol + sc->stats.latecol + sc->watchdog_events); default: return (if_get_counter_default(ifp, cnt)); } } /* em_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. + * Defaults to returning false for unknown events. * * @returns true if iflib needs to reinit the interface */ static bool em_if_needs_restart(if_ctx_t ctx __unused, enum iflib_restart_event event) { switch (event) { case IFLIB_RESTART_VLAN_CONFIG: - return (false); default: - return (true); + return (false); } } /* Export a single 32-bit register via a read-only sysctl. */ static int em_sysctl_reg_handler(SYSCTL_HANDLER_ARGS) { struct e1000_softc *sc; u_int val; sc = oidp->oid_arg1; val = E1000_READ_REG(&sc->hw, oidp->oid_arg2); return (sysctl_handle_int(oidp, &val, 0, req)); } /* * Add sysctl variables, one per statistic, to the system. */ static void em_add_hw_stats(struct e1000_softc *sc) { device_t dev = iflib_get_dev(sc->ctx); struct em_tx_queue *tx_que = sc->tx_queues; struct em_rx_queue *rx_que = sc->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 e1000_hw_stats *stats = &sc->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, &sc->dropped_pkts, "Driver dropped packets"); SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "link_irq", CTLFLAG_RD, &sc->link_irq, "Link MSI-X IRQ Handled"); SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "rx_overruns", CTLFLAG_RD, &sc->rx_overruns, "RX overruns"); SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "watchdog_timeouts", CTLFLAG_RD, &sc->watchdog_events, "Watchdog timeouts"); SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "device_control", CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, E1000_CTRL, em_sysctl_reg_handler, "IU", "Device Control Register"); SYSCTL_ADD_PROC(ctx, child, OID_AUTO, "rx_control", CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, E1000_RCTL, em_sysctl_reg_handler, "IU", "Receiver Control Register"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "fc_high_water", CTLFLAG_RD, &sc->hw.fc.high_water, 0, "Flow Control High Watermark"); SYSCTL_ADD_UINT(ctx, child, OID_AUTO, "fc_low_water", CTLFLAG_RD, &sc->hw.fc.low_water, 0, "Flow Control Low Watermark"); for (int i = 0; i < sc->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, sc, E1000_TDH(txr->me), em_sysctl_reg_handler, "IU", "Transmit Descriptor Head"); SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "txd_tail", CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, E1000_TDT(txr->me), em_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 < sc->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, sc, E1000_RDH(rxr->me), em_sysctl_reg_handler, "IU", "Receive Descriptor Head"); SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "rxd_tail", CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, E1000_RDT(rxr->me), em_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, &sc->stats.symerrs, "Symbol Errors"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "sequence_errors", CTLFLAG_RD, &sc->stats.sec, "Sequence Errors"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "defer_count", CTLFLAG_RD, &sc->stats.dc, "Defer Count"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "missed_packets", CTLFLAG_RD, &sc->stats.mpc, "Missed Packets"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "recv_no_buff", CTLFLAG_RD, &sc->stats.rnbc, "Receive No Buffers"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "recv_undersize", CTLFLAG_RD, &sc->stats.ruc, "Receive Undersize"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "recv_fragmented", CTLFLAG_RD, &sc->stats.rfc, "Fragmented Packets Received "); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "recv_oversize", CTLFLAG_RD, &sc->stats.roc, "Oversized Packets Received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "recv_jabber", CTLFLAG_RD, &sc->stats.rjc, "Recevied Jabber"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "recv_errs", CTLFLAG_RD, &sc->stats.rxerrc, "Receive Errors"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "crc_errs", CTLFLAG_RD, &sc->stats.crcerrs, "CRC errors"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "alignment_errs", CTLFLAG_RD, &sc->stats.algnerrc, "Alignment Errors"); /* On 82575 these are collision counts */ SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "coll_ext_errs", CTLFLAG_RD, &sc->stats.cexterr, "Collision/Carrier extension errors"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "xon_recvd", CTLFLAG_RD, &sc->stats.xonrxc, "XON Received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "xon_txd", CTLFLAG_RD, &sc->stats.xontxc, "XON Transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "xoff_recvd", CTLFLAG_RD, &sc->stats.xoffrxc, "XOFF Received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "xoff_txd", CTLFLAG_RD, &sc->stats.xofftxc, "XOFF Transmitted"); /* Packet Reception Stats */ SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "total_pkts_recvd", CTLFLAG_RD, &sc->stats.tpr, "Total Packets Received "); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "good_pkts_recvd", CTLFLAG_RD, &sc->stats.gprc, "Good Packets Received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "bcast_pkts_recvd", CTLFLAG_RD, &sc->stats.bprc, "Broadcast Packets Received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "mcast_pkts_recvd", CTLFLAG_RD, &sc->stats.mprc, "Multicast Packets Received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_64", CTLFLAG_RD, &sc->stats.prc64, "64 byte frames received "); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_65_127", CTLFLAG_RD, &sc->stats.prc127, "65-127 byte frames received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_128_255", CTLFLAG_RD, &sc->stats.prc255, "128-255 byte frames received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_256_511", CTLFLAG_RD, &sc->stats.prc511, "256-511 byte frames received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_512_1023", CTLFLAG_RD, &sc->stats.prc1023, "512-1023 byte frames received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_1024_1522", CTLFLAG_RD, &sc->stats.prc1522, "1023-1522 byte frames received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "good_octets_recvd", CTLFLAG_RD, &sc->stats.gorc, "Good Octets Received"); /* Packet Transmission Stats */ SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "good_octets_txd", CTLFLAG_RD, &sc->stats.gotc, "Good Octets Transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "total_pkts_txd", CTLFLAG_RD, &sc->stats.tpt, "Total Packets Transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "good_pkts_txd", CTLFLAG_RD, &sc->stats.gptc, "Good Packets Transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "bcast_pkts_txd", CTLFLAG_RD, &sc->stats.bptc, "Broadcast Packets Transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "mcast_pkts_txd", CTLFLAG_RD, &sc->stats.mptc, "Multicast Packets Transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_64", CTLFLAG_RD, &sc->stats.ptc64, "64 byte frames transmitted "); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_65_127", CTLFLAG_RD, &sc->stats.ptc127, "65-127 byte frames transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_128_255", CTLFLAG_RD, &sc->stats.ptc255, "128-255 byte frames transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_256_511", CTLFLAG_RD, &sc->stats.ptc511, "256-511 byte frames transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_512_1023", CTLFLAG_RD, &sc->stats.ptc1023, "512-1023 byte frames transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_1024_1522", CTLFLAG_RD, &sc->stats.ptc1522, "1024-1522 byte frames transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tso_txd", CTLFLAG_RD, &sc->stats.tsctc, "TSO Contexts Transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tso_ctx_fail", CTLFLAG_RD, &sc->stats.tsctfc, "TSO Contexts Failed"); /* 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, &sc->stats.iac, "Interrupt Assertion Count"); SYSCTL_ADD_UQUAD(ctx, int_list, OID_AUTO, "rx_pkt_timer", CTLFLAG_RD, &sc->stats.icrxptc, "Interrupt Cause Rx Pkt Timer Expire Count"); SYSCTL_ADD_UQUAD(ctx, int_list, OID_AUTO, "rx_abs_timer", CTLFLAG_RD, &sc->stats.icrxatc, "Interrupt Cause Rx Abs Timer Expire Count"); SYSCTL_ADD_UQUAD(ctx, int_list, OID_AUTO, "tx_pkt_timer", CTLFLAG_RD, &sc->stats.ictxptc, "Interrupt Cause Tx Pkt Timer Expire Count"); SYSCTL_ADD_UQUAD(ctx, int_list, OID_AUTO, "tx_abs_timer", CTLFLAG_RD, &sc->stats.ictxatc, "Interrupt Cause Tx Abs Timer Expire Count"); SYSCTL_ADD_UQUAD(ctx, int_list, OID_AUTO, "tx_queue_empty", CTLFLAG_RD, &sc->stats.ictxqec, "Interrupt Cause Tx Queue Empty Count"); SYSCTL_ADD_UQUAD(ctx, int_list, OID_AUTO, "tx_queue_min_thresh", CTLFLAG_RD, &sc->stats.ictxqmtc, "Interrupt Cause Tx Queue Min Thresh Count"); SYSCTL_ADD_UQUAD(ctx, int_list, OID_AUTO, "rx_desc_min_thresh", CTLFLAG_RD, &sc->stats.icrxdmtc, "Interrupt Cause Rx Desc Min Thresh Count"); SYSCTL_ADD_UQUAD(ctx, int_list, OID_AUTO, "rx_overrun", CTLFLAG_RD, &sc->stats.icrxoc, "Interrupt Cause Receiver Overrun Count"); } static void em_fw_version_locked(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); struct e1000_hw *hw = &sc->hw; struct e1000_fw_version *fw_ver = &sc->fw_ver; uint16_t eep = 0; /* * em_fw_version_locked() must run under the IFLIB_CTX_LOCK to meet the * NVM locking model, so we do it in em_if_attach_pre() and store the * info in the softc */ ASSERT_CTX_LOCK_HELD(hw); *fw_ver = (struct e1000_fw_version){0}; if (hw->mac.type >= igb_mac_min) { /* * Use the Shared Code for igb(4) */ e1000_get_fw_version(hw, fw_ver); } else { /* * Otherwise, EEPROM version should be present on (almost?) all * devices here */ if(e1000_read_nvm(hw, NVM_VERSION, 1, &eep)) { INIT_DEBUGOUT("can't get EEPROM version"); return; } fw_ver->eep_major = (eep & NVM_MAJOR_MASK) >> NVM_MAJOR_SHIFT; fw_ver->eep_minor = (eep & NVM_MINOR_MASK) >> NVM_MINOR_SHIFT; fw_ver->eep_build = (eep & NVM_IMAGE_ID_MASK); } } static void em_sbuf_fw_version(struct e1000_fw_version *fw_ver, struct sbuf *buf) { const char *space = ""; if (fw_ver->eep_major || fw_ver->eep_minor || fw_ver->eep_build) { sbuf_printf(buf, "EEPROM V%d.%d-%d", fw_ver->eep_major, fw_ver->eep_minor, fw_ver->eep_build); space = " "; } if (fw_ver->invm_major || fw_ver->invm_minor || fw_ver->invm_img_type) { sbuf_printf(buf, "%sNVM V%d.%d imgtype%d", space, fw_ver->invm_major, fw_ver->invm_minor, fw_ver->invm_img_type); space = " "; } if (fw_ver->or_valid) { sbuf_printf(buf, "%sOption ROM V%d-b%d-p%d", space, fw_ver->or_major, fw_ver->or_build, fw_ver->or_patch); space = " "; } if (fw_ver->etrack_id) sbuf_printf(buf, "%seTrack 0x%08x", space, fw_ver->etrack_id); } static void em_print_fw_version(struct e1000_softc *sc ) { device_t dev = sc->dev; struct sbuf *buf; int error = 0; buf = sbuf_new_auto(); if (!buf) { device_printf(dev, "Could not allocate sbuf for output.\n"); return; } em_sbuf_fw_version(&sc->fw_ver, buf); error = sbuf_finish(buf); if (error) device_printf(dev, "Error finishing sbuf: %d\n", error); else if (sbuf_len(buf)) device_printf(dev, "%s\n", sbuf_data(buf)); sbuf_delete(buf); } static int em_sysctl_print_fw_version(SYSCTL_HANDLER_ARGS) { struct e1000_softc *sc = (struct e1000_softc *)arg1; device_t dev = sc->dev; struct sbuf *buf; int error = 0; buf = sbuf_new_for_sysctl(NULL, NULL, 128, req); if (!buf) { device_printf(dev, "Could not allocate sbuf for output.\n"); return (ENOMEM); } em_sbuf_fw_version(&sc->fw_ver, buf); error = sbuf_finish(buf); if (error) device_printf(dev, "Error finishing sbuf: %d\n", error); sbuf_delete(buf); return (0); } /********************************************************************** * * 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 em_sysctl_nvm_info(SYSCTL_HANDLER_ARGS) { struct e1000_softc *sc = (struct e1000_softc *)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) em_print_nvm_info(sc); return (error); } static void em_print_nvm_info(struct e1000_softc *sc) { struct e1000_hw *hw = &sc->hw; struct sx *iflib_ctx_lock = iflib_ctx_lock_get(sc->ctx); 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 "); /* We rely on the IFLIB_CTX_LOCK as part of NVM locking model */ sx_xlock(iflib_ctx_lock); ASSERT_CTX_LOCK_HELD(hw); for (i = 0, j = 0; i < 32; i++, j++) { if (j == 8) { /* Make the offset block */ j = 0; ++row; printf("\n0x00%x0 ",row); } e1000_read_nvm(hw, i, 1, &eeprom_data); printf("%04x ", eeprom_data); } sx_xunlock(iflib_ctx_lock); printf("\n"); } static int em_sysctl_int_delay(SYSCTL_HANDLER_ARGS) { struct em_int_delay_info *info; struct e1000_softc *sc; u32 regval; int error, usecs, ticks; info = (struct em_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 > EM_TICKS_TO_USECS(65535)) return (EINVAL); info->value = usecs; ticks = EM_USECS_TO_TICKS(usecs); if (info->offset == E1000_ITR) /* units are 256ns here */ ticks *= 4; sc = info->sc; regval = E1000_READ_OFFSET(&sc->hw, info->offset); regval = (regval & ~0xffff) | (ticks & 0xffff); /* Handle a few special cases. */ switch (info->offset) { case E1000_RDTR: break; case E1000_TIDV: if (ticks == 0) { sc->txd_cmd &= ~E1000_TXD_CMD_IDE; /* Don't write 0 into the TIDV register. */ regval++; } else sc->txd_cmd |= E1000_TXD_CMD_IDE; break; } E1000_WRITE_OFFSET(&sc->hw, info->offset, regval); return (0); } static void em_add_int_delay_sysctl(struct e1000_softc *sc, const char *name, const char *description, struct em_int_delay_info *info, int offset, int value) { info->sc = sc; info->offset = offset; info->value = value; SYSCTL_ADD_PROC(device_get_sysctl_ctx(sc->dev), SYSCTL_CHILDREN(device_get_sysctl_tree(sc->dev)), OID_AUTO, name, CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, info, 0, em_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 em_set_flowcntl(SYSCTL_HANDLER_ARGS) { int error; static int input = 3; /* default is full */ struct e1000_softc *sc = (struct e1000_softc *) arg1; error = sysctl_handle_int(oidp, &input, 0, req); if ((error) || (req->newptr == NULL)) return (error); if (input == sc->fc) /* no change? */ return (error); switch (input) { case e1000_fc_rx_pause: case e1000_fc_tx_pause: case e1000_fc_full: case e1000_fc_none: sc->hw.fc.requested_mode = input; sc->fc = input; break; default: /* Do nothing */ return (error); } sc->hw.fc.current_mode = sc->hw.fc.requested_mode; e1000_force_mac_fc(&sc->hw); return (error); } /* * Manage Energy Efficient Ethernet: * Control values: * 0/1 - enabled/disabled */ static int em_sysctl_eee(SYSCTL_HANDLER_ARGS) { struct e1000_softc *sc = (struct e1000_softc *) arg1; int error, value; value = sc->hw.dev_spec.ich8lan.eee_disable; error = sysctl_handle_int(oidp, &value, 0, req); if (error || req->newptr == NULL) return (error); sc->hw.dev_spec.ich8lan.eee_disable = (value != 0); em_if_init(sc->ctx); return (0); } static int em_sysctl_debug_info(SYSCTL_HANDLER_ARGS) { struct e1000_softc *sc; int error; int result; result = -1; error = sysctl_handle_int(oidp, &result, 0, req); if (error || !req->newptr) return (error); if (result == 1) { sc = (struct e1000_softc *) arg1; em_print_debug_info(sc); } return (error); } static int em_get_rs(SYSCTL_HANDLER_ARGS) { struct e1000_softc *sc = (struct e1000_softc *) arg1; int error; int result; result = 0; error = sysctl_handle_int(oidp, &result, 0, req); if (error || !req->newptr || result != 1) return (error); em_dump_rs(sc); return (error); } static void em_if_debug(if_ctx_t ctx) { em_dump_rs(iflib_get_softc(ctx)); } /* * This routine is meant to be fluid, add whatever is * needed for debugging a problem. -jfv */ static void em_print_debug_info(struct e1000_softc *sc) { device_t dev = iflib_get_dev(sc->ctx); struct ifnet *ifp = iflib_get_ifp(sc->ctx); struct tx_ring *txr = &sc->tx_queues->txr; struct rx_ring *rxr = &sc->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 < sc->tx_num_queues; i++, txr++) { device_printf(dev, "TX Queue %d ------\n", i); device_printf(dev, "hw tdh = %d, hw tdt = %d\n", E1000_READ_REG(&sc->hw, E1000_TDH(i)), E1000_READ_REG(&sc->hw, E1000_TDT(i))); } for (int j=0; j < sc->rx_num_queues; j++, rxr++) { device_printf(dev, "RX Queue %d ------\n", j); device_printf(dev, "hw rdh = %d, hw rdt = %d\n", E1000_READ_REG(&sc->hw, E1000_RDH(j)), E1000_READ_REG(&sc->hw, E1000_RDT(j))); } } /* * 82574 only: * Write a new value to the EEPROM increasing the number of MSI-X * vectors from 3 to 5, for proper multiqueue support. */ static void em_enable_vectors_82574(if_ctx_t ctx) { struct e1000_softc *sc = iflib_get_softc(ctx); struct e1000_hw *hw = &sc->hw; device_t dev = iflib_get_dev(ctx); u16 edata; e1000_read_nvm(hw, EM_NVM_PCIE_CTRL, 1, &edata); if (bootverbose) device_printf(dev, "EM_NVM_PCIE_CTRL = %#06x\n", edata); if (((edata & EM_NVM_MSIX_N_MASK) >> EM_NVM_MSIX_N_SHIFT) != 4) { device_printf(dev, "Writing to eeprom: increasing " "reported MSI-X vectors from 3 to 5...\n"); edata &= ~(EM_NVM_MSIX_N_MASK); edata |= 4 << EM_NVM_MSIX_N_SHIFT; e1000_write_nvm(hw, EM_NVM_PCIE_CTRL, 1, &edata); e1000_update_nvm_checksum(hw); device_printf(dev, "Writing to eeprom: done\n"); } } diff --git a/sys/dev/igc/if_igc.c b/sys/dev/igc/if_igc.c index 4680dd434174..82fb0d72070b 100644 --- a/sys/dev/igc/if_igc.c +++ b/sys/dev/igc/if_igc.c @@ -1,2928 +1,2927 @@ /*- * 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 #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_I226_LMVP, "Intel(R) Ethernet Controller I226-LMvP"), 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_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(if_ctx_t ctx); 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_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_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); /* Set up VLAN support */ igc_setup_vlan_hw_support(ctx); /* Don't lose promiscuous settings */ igc_if_set_promisc(ctx, if_getflags(ifp)); 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_FDX) adapter->hw.phy.autoneg_advertised = ADVERTISE_100_FULL; else adapter->hw.phy.autoneg_advertised = ADVERTISE_100_HALF; break; case IFM_10_T: if ((ifm->ifm_media & IFM_GMASK) == IFM_FDX) adapter->hw.phy.autoneg_advertised = ADVERTISE_10_FULL; else adapter->hw.phy.autoneg_advertised = ADVERTISE_10_HALF; 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); if (mcnt < MAX_NUM_MULTICAST_ADDRESSES) igc_update_mc_addr_list(&adapter->hw, mta, mcnt); IGC_WRITE_REG(&adapter->hw, IGC_RCTL, reg_rctl); } /********************************************************************* * 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; u32 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. * **********************************************************************/ #define BSIZEPKT_ROUNDUP ((1<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) { 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); } /* Set maximum packet buffer len */ srrctl |= (adapter->rx_mbuf_sz + BSIZEPKT_ROUNDUP) >> IGC_SRRCTL_BSIZEPKT_SHIFT; /* srrctl above overrides this but set the register to a sane value */ 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_setup_vlan_hw_support(if_ctx_t ctx) { struct igc_adapter *adapter = iflib_get_softc(ctx); struct igc_hw *hw = &adapter->hw; struct ifnet *ifp = iflib_get_ifp(ctx); u32 reg; /* igc hardware doesn't seem to implement VFTA for HWFILTER */ if (if_getcapenable(ifp) & IFCAP_VLAN_HWTAGGING && !igc_disable_crc_stripping) { reg = IGC_READ_REG(hw, IGC_CTRL); reg |= IGC_CTRL_VME; IGC_WRITE_REG(hw, IGC_CTRL, reg); } else { reg = IGC_READ_REG(hw, IGC_CTRL); reg &= ~IGC_CTRL_VME; IGC_WRITE_REG(hw, IGC_CTRL, 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. + * Defaults to returning false 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: - return (false); default: - return (true); + return (false); } } /* 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/ixgbe/if_ix.c b/sys/dev/ixgbe/if_ix.c index db54b0edf7be..b1cba9f24929 100644 --- a/sys/dev/ixgbe/if_ix.c +++ b/sys/dev/ixgbe/if_ix.c @@ -1,4850 +1,4849 @@ /****************************************************************************** Copyright (c) 2001-2017, Intel Corporation All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the Intel Corporation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ******************************************************************************/ #include "opt_inet.h" #include "opt_inet6.h" #include "opt_rss.h" #include "ixgbe.h" #include "ixgbe_sriov.h" #include "ifdi_if.h" #include #include /************************************************************************ * Driver version ************************************************************************/ char ixgbe_driver_version[] = "4.0.1-k"; /************************************************************************ * PCI Device ID Table * * Used by probe to select devices to load on * Last field stores an index into ixgbe_strings * Last entry must be all 0s * * { Vendor ID, Device ID, SubVendor ID, SubDevice ID, String Index } ************************************************************************/ static pci_vendor_info_t ixgbe_vendor_info_array[] = { PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82598AF_DUAL_PORT, "Intel(R) 82598EB AF (Dual Fiber)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82598AF_SINGLE_PORT, "Intel(R) 82598EB AF (Fiber)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82598EB_CX4, "Intel(R) 82598EB AT (CX4)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82598AT, "Intel(R) 82598EB AT"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82598AT2, "Intel(R) 82598EB AT2"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82598, "Intel(R) 82598"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82598_DA_DUAL_PORT, "Intel(R) 82598EB AF DA (Dual Fiber)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82598_CX4_DUAL_PORT, "Intel(R) 82598EB AT (Dual CX4)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82598EB_XF_LR, "Intel(R) 82598EB AF (Dual Fiber LR)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82598_SR_DUAL_PORT_EM, "Intel(R) 82598EB AF (Dual Fiber SR)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82598EB_SFP_LOM, "Intel(R) 82598EB LOM"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_KX4, "Intel(R) X520 82599 (KX4)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_KX4_MEZZ, "Intel(R) X520 82599 (KX4 Mezzanine)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_SFP, "Intel(R) X520 82599ES (SFI/SFP+)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_XAUI_LOM, "Intel(R) X520 82599 (XAUI/BX4)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_CX4, "Intel(R) X520 82599 (Dual CX4)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_T3_LOM, "Intel(R) X520-T 82599 LOM"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_LS, "Intel(R) X520 82599 LS"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_COMBO_BACKPLANE, "Intel(R) X520 82599 (Combined Backplane)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_BACKPLANE_FCOE, "Intel(R) X520 82599 (Backplane w/FCoE)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_SFP_SF2, "Intel(R) X520 82599 (Dual SFP+)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_SFP_FCOE, "Intel(R) X520 82599 (Dual SFP+ w/FCoE)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599EN_SFP, "Intel(R) X520-1 82599EN (SFP+)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_SFP_SF_QP, "Intel(R) X520-4 82599 (Quad SFP+)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_QSFP_SF_QP, "Intel(R) X520-Q1 82599 (QSFP+)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X540T, "Intel(R) X540-AT2"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X540T1, "Intel(R) X540-T1"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550T, "Intel(R) X550-T2"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550T1, "Intel(R) X550-T1"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550EM_X_KR, "Intel(R) X552 (KR Backplane)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550EM_X_KX4, "Intel(R) X552 (KX4 Backplane)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550EM_X_10G_T, "Intel(R) X552/X557-AT (10GBASE-T)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550EM_X_1G_T, "Intel(R) X552 (1000BASE-T)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550EM_X_SFP, "Intel(R) X552 (SFP+)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550EM_A_KR, "Intel(R) X553 (KR Backplane)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550EM_A_KR_L, "Intel(R) X553 L (KR Backplane)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550EM_A_SFP, "Intel(R) X553 (SFP+)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550EM_A_SFP_N, "Intel(R) X553 N (SFP+)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550EM_A_SGMII, "Intel(R) X553 (1GbE SGMII)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550EM_A_SGMII_L, "Intel(R) X553 L (1GbE SGMII)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550EM_A_10G_T, "Intel(R) X553/X557-AT (10GBASE-T)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550EM_A_1G_T, "Intel(R) X553 (1GbE)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X550EM_A_1G_T_L, "Intel(R) X553 L (1GbE)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_X540_BYPASS, "Intel(R) X540-T2 (Bypass)"), PVID(IXGBE_INTEL_VENDOR_ID, IXGBE_DEV_ID_82599_BYPASS, "Intel(R) X520 82599 (Bypass)"), /* required last entry */ PVID_END }; static void *ixgbe_register(device_t); static int ixgbe_if_attach_pre(if_ctx_t); static int ixgbe_if_attach_post(if_ctx_t); static int ixgbe_if_detach(if_ctx_t); static int ixgbe_if_shutdown(if_ctx_t); static int ixgbe_if_suspend(if_ctx_t); static int ixgbe_if_resume(if_ctx_t); static void ixgbe_if_stop(if_ctx_t); void ixgbe_if_enable_intr(if_ctx_t); static void ixgbe_if_disable_intr(if_ctx_t); static void ixgbe_link_intr_enable(if_ctx_t); static int ixgbe_if_rx_queue_intr_enable(if_ctx_t, uint16_t); static void ixgbe_if_media_status(if_ctx_t, struct ifmediareq *); static int ixgbe_if_media_change(if_ctx_t); static int ixgbe_if_msix_intr_assign(if_ctx_t, int); static int ixgbe_if_mtu_set(if_ctx_t, uint32_t); static void ixgbe_if_crcstrip_set(if_ctx_t, int, int); static void ixgbe_if_multi_set(if_ctx_t); static int ixgbe_if_promisc_set(if_ctx_t, int); static int ixgbe_if_tx_queues_alloc(if_ctx_t, caddr_t *, uint64_t *, int, int); static int ixgbe_if_rx_queues_alloc(if_ctx_t, caddr_t *, uint64_t *, int, int); static void ixgbe_if_queues_free(if_ctx_t); static void ixgbe_if_timer(if_ctx_t, uint16_t); static void ixgbe_if_update_admin_status(if_ctx_t); static void ixgbe_if_vlan_register(if_ctx_t, u16); static void ixgbe_if_vlan_unregister(if_ctx_t, u16); static int ixgbe_if_i2c_req(if_ctx_t, struct ifi2creq *); static bool ixgbe_if_needs_restart(if_ctx_t, enum iflib_restart_event); int ixgbe_intr(void *); /************************************************************************ * Function prototypes ************************************************************************/ static uint64_t ixgbe_if_get_counter(if_ctx_t, ift_counter); static void ixgbe_enable_queue(struct ixgbe_softc *, u32); static void ixgbe_disable_queue(struct ixgbe_softc *, u32); static void ixgbe_add_device_sysctls(if_ctx_t); static int ixgbe_allocate_pci_resources(if_ctx_t); static int ixgbe_setup_low_power_mode(if_ctx_t); static void ixgbe_config_dmac(struct ixgbe_softc *); static void ixgbe_configure_ivars(struct ixgbe_softc *); static void ixgbe_set_ivar(struct ixgbe_softc *, u8, u8, s8); static u8 *ixgbe_mc_array_itr(struct ixgbe_hw *, u8 **, u32 *); static bool ixgbe_sfp_probe(if_ctx_t); static void ixgbe_free_pci_resources(if_ctx_t); static int ixgbe_msix_link(void *); static int ixgbe_msix_que(void *); static void ixgbe_initialize_rss_mapping(struct ixgbe_softc *); static void ixgbe_initialize_receive_units(if_ctx_t); static void ixgbe_initialize_transmit_units(if_ctx_t); static int ixgbe_setup_interface(if_ctx_t); static void ixgbe_init_device_features(struct ixgbe_softc *); static void ixgbe_check_fan_failure(struct ixgbe_softc *, u32, bool); static void ixgbe_sbuf_fw_version(struct ixgbe_hw *, struct sbuf *); static void ixgbe_print_fw_version(if_ctx_t); static void ixgbe_add_media_types(if_ctx_t); static void ixgbe_update_stats_counters(struct ixgbe_softc *); static void ixgbe_config_link(if_ctx_t); static void ixgbe_get_slot_info(struct ixgbe_softc *); static void ixgbe_check_wol_support(struct ixgbe_softc *); static void ixgbe_enable_rx_drop(struct ixgbe_softc *); static void ixgbe_disable_rx_drop(struct ixgbe_softc *); static void ixgbe_add_hw_stats(struct ixgbe_softc *); static int ixgbe_set_flowcntl(struct ixgbe_softc *, int); static int ixgbe_set_advertise(struct ixgbe_softc *, int); static int ixgbe_get_default_advertise(struct ixgbe_softc *); static void ixgbe_setup_vlan_hw_support(if_ctx_t); static void ixgbe_config_gpie(struct ixgbe_softc *); static void ixgbe_config_delay_values(struct ixgbe_softc *); /* Sysctl handlers */ static int ixgbe_sysctl_flowcntl(SYSCTL_HANDLER_ARGS); static int ixgbe_sysctl_advertise(SYSCTL_HANDLER_ARGS); static int ixgbe_sysctl_interrupt_rate_handler(SYSCTL_HANDLER_ARGS); static int ixgbe_sysctl_dmac(SYSCTL_HANDLER_ARGS); static int ixgbe_sysctl_phy_temp(SYSCTL_HANDLER_ARGS); static int ixgbe_sysctl_phy_overtemp_occurred(SYSCTL_HANDLER_ARGS); static int ixgbe_sysctl_print_fw_version(SYSCTL_HANDLER_ARGS); #ifdef IXGBE_DEBUG static int ixgbe_sysctl_power_state(SYSCTL_HANDLER_ARGS); static int ixgbe_sysctl_print_rss_config(SYSCTL_HANDLER_ARGS); #endif static int ixgbe_sysctl_rdh_handler(SYSCTL_HANDLER_ARGS); static int ixgbe_sysctl_rdt_handler(SYSCTL_HANDLER_ARGS); static int ixgbe_sysctl_tdt_handler(SYSCTL_HANDLER_ARGS); static int ixgbe_sysctl_tdh_handler(SYSCTL_HANDLER_ARGS); static int ixgbe_sysctl_eee_state(SYSCTL_HANDLER_ARGS); static int ixgbe_sysctl_wol_enable(SYSCTL_HANDLER_ARGS); static int ixgbe_sysctl_wufc(SYSCTL_HANDLER_ARGS); /* Deferred interrupt tasklets */ static void ixgbe_handle_msf(void *); static void ixgbe_handle_mod(void *); static void ixgbe_handle_phy(void *); /************************************************************************ * FreeBSD Device Interface Entry Points ************************************************************************/ static device_method_t ix_methods[] = { /* Device interface */ DEVMETHOD(device_register, ixgbe_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), #ifdef PCI_IOV DEVMETHOD(pci_iov_init, iflib_device_iov_init), DEVMETHOD(pci_iov_uninit, iflib_device_iov_uninit), DEVMETHOD(pci_iov_add_vf, iflib_device_iov_add_vf), #endif /* PCI_IOV */ DEVMETHOD_END }; static driver_t ix_driver = { "ix", ix_methods, sizeof(struct ixgbe_softc), }; devclass_t ix_devclass; DRIVER_MODULE(ix, pci, ix_driver, ix_devclass, 0, 0); IFLIB_PNP_INFO(pci, ix_driver, ixgbe_vendor_info_array); MODULE_DEPEND(ix, pci, 1, 1, 1); MODULE_DEPEND(ix, ether, 1, 1, 1); MODULE_DEPEND(ix, iflib, 1, 1, 1); static device_method_t ixgbe_if_methods[] = { DEVMETHOD(ifdi_attach_pre, ixgbe_if_attach_pre), DEVMETHOD(ifdi_attach_post, ixgbe_if_attach_post), DEVMETHOD(ifdi_detach, ixgbe_if_detach), DEVMETHOD(ifdi_shutdown, ixgbe_if_shutdown), DEVMETHOD(ifdi_suspend, ixgbe_if_suspend), DEVMETHOD(ifdi_resume, ixgbe_if_resume), DEVMETHOD(ifdi_init, ixgbe_if_init), DEVMETHOD(ifdi_stop, ixgbe_if_stop), DEVMETHOD(ifdi_msix_intr_assign, ixgbe_if_msix_intr_assign), DEVMETHOD(ifdi_intr_enable, ixgbe_if_enable_intr), DEVMETHOD(ifdi_intr_disable, ixgbe_if_disable_intr), DEVMETHOD(ifdi_link_intr_enable, ixgbe_link_intr_enable), DEVMETHOD(ifdi_tx_queue_intr_enable, ixgbe_if_rx_queue_intr_enable), DEVMETHOD(ifdi_rx_queue_intr_enable, ixgbe_if_rx_queue_intr_enable), DEVMETHOD(ifdi_tx_queues_alloc, ixgbe_if_tx_queues_alloc), DEVMETHOD(ifdi_rx_queues_alloc, ixgbe_if_rx_queues_alloc), DEVMETHOD(ifdi_queues_free, ixgbe_if_queues_free), DEVMETHOD(ifdi_update_admin_status, ixgbe_if_update_admin_status), DEVMETHOD(ifdi_multi_set, ixgbe_if_multi_set), DEVMETHOD(ifdi_mtu_set, ixgbe_if_mtu_set), DEVMETHOD(ifdi_crcstrip_set, ixgbe_if_crcstrip_set), DEVMETHOD(ifdi_media_status, ixgbe_if_media_status), DEVMETHOD(ifdi_media_change, ixgbe_if_media_change), DEVMETHOD(ifdi_promisc_set, ixgbe_if_promisc_set), DEVMETHOD(ifdi_timer, ixgbe_if_timer), DEVMETHOD(ifdi_vlan_register, ixgbe_if_vlan_register), DEVMETHOD(ifdi_vlan_unregister, ixgbe_if_vlan_unregister), DEVMETHOD(ifdi_get_counter, ixgbe_if_get_counter), DEVMETHOD(ifdi_i2c_req, ixgbe_if_i2c_req), DEVMETHOD(ifdi_needs_restart, ixgbe_if_needs_restart), #ifdef PCI_IOV DEVMETHOD(ifdi_iov_init, ixgbe_if_iov_init), DEVMETHOD(ifdi_iov_uninit, ixgbe_if_iov_uninit), DEVMETHOD(ifdi_iov_vf_add, ixgbe_if_iov_vf_add), #endif /* PCI_IOV */ DEVMETHOD_END }; /* * TUNEABLE PARAMETERS: */ static SYSCTL_NODE(_hw, OID_AUTO, ix, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "IXGBE driver parameters"); static driver_t ixgbe_if_driver = { "ixgbe_if", ixgbe_if_methods, sizeof(struct ixgbe_softc) }; static int ixgbe_max_interrupt_rate = (4000000 / IXGBE_LOW_LATENCY); SYSCTL_INT(_hw_ix, OID_AUTO, max_interrupt_rate, CTLFLAG_RDTUN, &ixgbe_max_interrupt_rate, 0, "Maximum interrupts per second"); /* Flow control setting, default to full */ static int ixgbe_flow_control = ixgbe_fc_full; SYSCTL_INT(_hw_ix, OID_AUTO, flow_control, CTLFLAG_RDTUN, &ixgbe_flow_control, 0, "Default flow control used for all adapters"); /* Advertise Speed, default to 0 (auto) */ static int ixgbe_advertise_speed = 0; SYSCTL_INT(_hw_ix, OID_AUTO, advertise_speed, CTLFLAG_RDTUN, &ixgbe_advertise_speed, 0, "Default advertised speed for all adapters"); /* * Smart speed setting, default to on * this only works as a compile option * right now as its during attach, set * this to 'ixgbe_smart_speed_off' to * disable. */ static int ixgbe_smart_speed = ixgbe_smart_speed_on; /* * MSI-X should be the default for best performance, * but this allows it to be forced off for testing. */ static int ixgbe_enable_msix = 1; SYSCTL_INT(_hw_ix, OID_AUTO, enable_msix, CTLFLAG_RDTUN, &ixgbe_enable_msix, 0, "Enable MSI-X interrupts"); /* * Defining this on will allow the use * of unsupported SFP+ modules, note that * doing so you are on your own :) */ static int allow_unsupported_sfp = false; SYSCTL_INT(_hw_ix, OID_AUTO, unsupported_sfp, CTLFLAG_RDTUN, &allow_unsupported_sfp, 0, "Allow unsupported SFP modules...use at your own risk"); /* * Not sure if Flow Director is fully baked, * so we'll default to turning it off. */ static int ixgbe_enable_fdir = 0; SYSCTL_INT(_hw_ix, OID_AUTO, enable_fdir, CTLFLAG_RDTUN, &ixgbe_enable_fdir, 0, "Enable Flow Director"); /* Receive-Side Scaling */ static int ixgbe_enable_rss = 1; SYSCTL_INT(_hw_ix, OID_AUTO, enable_rss, CTLFLAG_RDTUN, &ixgbe_enable_rss, 0, "Enable Receive-Side Scaling (RSS)"); /* * AIM: Adaptive Interrupt Moderation * which means that the interrupt rate * is varied over time based on the * traffic for that interrupt vector */ static int ixgbe_enable_aim = false; SYSCTL_INT(_hw_ix, OID_AUTO, enable_aim, CTLFLAG_RWTUN, &ixgbe_enable_aim, 0, "Enable adaptive interrupt moderation"); #if 0 /* Keep running tab on them for sanity check */ static int ixgbe_total_ports; #endif MALLOC_DEFINE(M_IXGBE, "ix", "ix driver allocations"); /* * For Flow Director: this is the number of TX packets we sample * for the filter pool, this means every 20th packet will be probed. * * This feature can be disabled by setting this to 0. */ static int atr_sample_rate = 20; extern struct if_txrx ixgbe_txrx; static struct if_shared_ctx ixgbe_sctx_init = { .isc_magic = IFLIB_MAGIC, .isc_q_align = PAGE_SIZE,/* max(DBA_ALIGN, PAGE_SIZE) */ .isc_tx_maxsize = IXGBE_TSO_SIZE + sizeof(struct ether_vlan_header), .isc_tx_maxsegsize = PAGE_SIZE, .isc_tso_maxsize = IXGBE_TSO_SIZE + sizeof(struct ether_vlan_header), .isc_tso_maxsegsize = PAGE_SIZE, .isc_rx_maxsize = PAGE_SIZE*4, .isc_rx_nsegments = 1, .isc_rx_maxsegsize = PAGE_SIZE*4, .isc_nfl = 1, .isc_ntxqs = 1, .isc_nrxqs = 1, .isc_admin_intrcnt = 1, .isc_vendor_info = ixgbe_vendor_info_array, .isc_driver_version = ixgbe_driver_version, .isc_driver = &ixgbe_if_driver, .isc_flags = IFLIB_TSO_INIT_IP, .isc_nrxd_min = {MIN_RXD}, .isc_ntxd_min = {MIN_TXD}, .isc_nrxd_max = {MAX_RXD}, .isc_ntxd_max = {MAX_TXD}, .isc_nrxd_default = {DEFAULT_RXD}, .isc_ntxd_default = {DEFAULT_TXD}, }; /************************************************************************ * ixgbe_if_tx_queues_alloc ************************************************************************/ static int ixgbe_if_tx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs, int ntxqs, int ntxqsets) { struct ixgbe_softc *sc = iflib_get_softc(ctx); if_softc_ctx_t scctx = sc->shared; struct ix_tx_queue *que; int i, j, error; MPASS(sc->num_tx_queues > 0); MPASS(sc->num_tx_queues == ntxqsets); MPASS(ntxqs == 1); /* Allocate queue structure memory */ sc->tx_queues = (struct ix_tx_queue *)malloc(sizeof(struct ix_tx_queue) * ntxqsets, M_IXGBE, M_NOWAIT | M_ZERO); if (!sc->tx_queues) { device_printf(iflib_get_dev(ctx), "Unable to allocate TX ring memory\n"); return (ENOMEM); } for (i = 0, que = sc->tx_queues; i < ntxqsets; i++, que++) { struct tx_ring *txr = &que->txr; /* In case SR-IOV is enabled, align the index properly */ txr->me = ixgbe_vf_que_index(sc->iov_mode, sc->pool, i); txr->sc = que->sc = sc; /* Allocate report status array */ txr->tx_rsq = (qidx_t *)malloc(sizeof(qidx_t) * scctx->isc_ntxd[0], M_IXGBE, M_NOWAIT | M_ZERO); if (txr->tx_rsq == NULL) { 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->tail = IXGBE_TDT(txr->me); txr->tx_base = (union ixgbe_adv_tx_desc *)vaddrs[i]; txr->tx_paddr = paddrs[i]; txr->bytes = 0; txr->total_packets = 0; /* Set the rate at which we sample packets */ if (sc->feat_en & IXGBE_FEATURE_FDIR) txr->atr_sample = atr_sample_rate; } device_printf(iflib_get_dev(ctx), "allocated for %d queues\n", sc->num_tx_queues); return (0); fail: ixgbe_if_queues_free(ctx); return (error); } /* ixgbe_if_tx_queues_alloc */ /************************************************************************ * ixgbe_if_rx_queues_alloc ************************************************************************/ static int ixgbe_if_rx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs, int nrxqs, int nrxqsets) { struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ix_rx_queue *que; int i; MPASS(sc->num_rx_queues > 0); MPASS(sc->num_rx_queues == nrxqsets); MPASS(nrxqs == 1); /* Allocate queue structure memory */ sc->rx_queues = (struct ix_rx_queue *)malloc(sizeof(struct ix_rx_queue)*nrxqsets, M_IXGBE, M_NOWAIT | M_ZERO); if (!sc->rx_queues) { device_printf(iflib_get_dev(ctx), "Unable to allocate TX ring memory\n"); return (ENOMEM); } for (i = 0, que = sc->rx_queues; i < nrxqsets; i++, que++) { struct rx_ring *rxr = &que->rxr; /* In case SR-IOV is enabled, align the index properly */ rxr->me = ixgbe_vf_que_index(sc->iov_mode, sc->pool, i); rxr->sc = que->sc = sc; /* get the virtual and physical address of the hw queues */ rxr->tail = IXGBE_RDT(rxr->me); rxr->rx_base = (union ixgbe_adv_rx_desc *)vaddrs[i]; rxr->rx_paddr = paddrs[i]; rxr->bytes = 0; rxr->que = que; } device_printf(iflib_get_dev(ctx), "allocated for %d rx queues\n", sc->num_rx_queues); return (0); } /* ixgbe_if_rx_queues_alloc */ /************************************************************************ * ixgbe_if_queues_free ************************************************************************/ static void ixgbe_if_queues_free(if_ctx_t ctx) { struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ix_tx_queue *tx_que = sc->tx_queues; struct ix_rx_queue *rx_que = sc->rx_queues; int i; if (tx_que != NULL) { for (i = 0; i < sc->num_tx_queues; i++, tx_que++) { struct tx_ring *txr = &tx_que->txr; if (txr->tx_rsq == NULL) break; free(txr->tx_rsq, M_IXGBE); txr->tx_rsq = NULL; } free(sc->tx_queues, M_IXGBE); sc->tx_queues = NULL; } if (rx_que != NULL) { free(sc->rx_queues, M_IXGBE); sc->rx_queues = NULL; } } /* ixgbe_if_queues_free */ /************************************************************************ * ixgbe_initialize_rss_mapping ************************************************************************/ static void ixgbe_initialize_rss_mapping(struct ixgbe_softc *sc) { struct ixgbe_hw *hw = &sc->hw; u32 reta = 0, mrqc, rss_key[10]; int queue_id, table_size, index_mult; int i, j; u32 rss_hash_config; if (sc->feat_en & IXGBE_FEATURE_RSS) { /* Fetch the configured RSS key */ rss_getkey((uint8_t *)&rss_key); } else { /* set up random bits */ arc4rand(&rss_key, sizeof(rss_key), 0); } /* Set multiplier for RETA setup and table size based on MAC */ index_mult = 0x1; table_size = 128; switch (sc->hw.mac.type) { case ixgbe_mac_82598EB: index_mult = 0x11; break; case ixgbe_mac_X550: case ixgbe_mac_X550EM_x: case ixgbe_mac_X550EM_a: table_size = 512; break; default: break; } /* Set up the redirection table */ for (i = 0, j = 0; i < table_size; i++, j++) { if (j == sc->num_rx_queues) j = 0; if (sc->feat_en & IXGBE_FEATURE_RSS) { /* * Fetch the RSS bucket id for the given indirection * entry. Cap it at the number of configured buckets * (which is num_rx_queues.) */ queue_id = rss_get_indirection_to_bucket(i); queue_id = queue_id % sc->num_rx_queues; } else queue_id = (j * index_mult); /* * 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) { if (i < 128) IXGBE_WRITE_REG(hw, IXGBE_RETA(i >> 2), reta); else IXGBE_WRITE_REG(hw, IXGBE_ERETA((i >> 2) - 32), reta); reta = 0; } } /* Now fill our hash function seeds */ for (i = 0; i < 10; i++) IXGBE_WRITE_REG(hw, IXGBE_RSSRK(i), rss_key[i]); /* Perform hash on these packet types */ if (sc->feat_en & IXGBE_FEATURE_RSS) rss_hash_config = rss_gethashconfig(); else { /* * Disable UDP - IP fragments aren't currently being handled * and so we end up with a mix of 2-tuple and 4-tuple * traffic. */ rss_hash_config = RSS_HASHTYPE_RSS_IPV4 | RSS_HASHTYPE_RSS_TCP_IPV4 | RSS_HASHTYPE_RSS_IPV6 | RSS_HASHTYPE_RSS_TCP_IPV6 | RSS_HASHTYPE_RSS_IPV6_EX | RSS_HASHTYPE_RSS_TCP_IPV6_EX; } mrqc = IXGBE_MRQC_RSSEN; if (rss_hash_config & RSS_HASHTYPE_RSS_IPV4) mrqc |= IXGBE_MRQC_RSS_FIELD_IPV4; if (rss_hash_config & RSS_HASHTYPE_RSS_TCP_IPV4) mrqc |= IXGBE_MRQC_RSS_FIELD_IPV4_TCP; if (rss_hash_config & RSS_HASHTYPE_RSS_IPV6) mrqc |= IXGBE_MRQC_RSS_FIELD_IPV6; if (rss_hash_config & RSS_HASHTYPE_RSS_TCP_IPV6) mrqc |= IXGBE_MRQC_RSS_FIELD_IPV6_TCP; if (rss_hash_config & RSS_HASHTYPE_RSS_IPV6_EX) mrqc |= IXGBE_MRQC_RSS_FIELD_IPV6_EX; if (rss_hash_config & RSS_HASHTYPE_RSS_TCP_IPV6_EX) mrqc |= IXGBE_MRQC_RSS_FIELD_IPV6_EX_TCP; if (rss_hash_config & RSS_HASHTYPE_RSS_UDP_IPV4) mrqc |= IXGBE_MRQC_RSS_FIELD_IPV4_UDP; if (rss_hash_config & RSS_HASHTYPE_RSS_UDP_IPV6) mrqc |= IXGBE_MRQC_RSS_FIELD_IPV6_UDP; if (rss_hash_config & RSS_HASHTYPE_RSS_UDP_IPV6_EX) mrqc |= IXGBE_MRQC_RSS_FIELD_IPV6_EX_UDP; mrqc |= ixgbe_get_mrqc(sc->iov_mode); IXGBE_WRITE_REG(hw, IXGBE_MRQC, mrqc); } /* ixgbe_initialize_rss_mapping */ /************************************************************************ * ixgbe_initialize_receive_units - Setup receive registers and features. ************************************************************************/ #define BSIZEPKT_ROUNDUP ((1<shared; struct ixgbe_hw *hw = &sc->hw; struct ifnet *ifp = iflib_get_ifp(ctx); struct ix_rx_queue *que; int i, j; u32 bufsz, fctrl, srrctl, rxcsum; u32 hlreg; /* * Make sure receives are disabled while * setting up the descriptor ring */ ixgbe_disable_rx(hw); /* Enable broadcasts */ fctrl = IXGBE_READ_REG(hw, IXGBE_FCTRL); fctrl |= IXGBE_FCTRL_BAM; if (sc->hw.mac.type == ixgbe_mac_82598EB) { fctrl |= IXGBE_FCTRL_DPF; fctrl |= IXGBE_FCTRL_PMCF; } IXGBE_WRITE_REG(hw, IXGBE_FCTRL, fctrl); /* Set for Jumbo Frames? */ hlreg = IXGBE_READ_REG(hw, IXGBE_HLREG0); if (ifp->if_mtu > ETHERMTU) hlreg |= IXGBE_HLREG0_JUMBOEN; else hlreg &= ~IXGBE_HLREG0_JUMBOEN; IXGBE_WRITE_REG(hw, IXGBE_HLREG0, hlreg); bufsz = (sc->rx_mbuf_sz + BSIZEPKT_ROUNDUP) >> IXGBE_SRRCTL_BSIZEPKT_SHIFT; /* Setup the Base and Length of the Rx Descriptor Ring */ for (i = 0, que = sc->rx_queues; i < sc->num_rx_queues; i++, que++) { struct rx_ring *rxr = &que->rxr; u64 rdba = rxr->rx_paddr; j = rxr->me; /* Setup the Base and Length of the Rx Descriptor Ring */ IXGBE_WRITE_REG(hw, IXGBE_RDBAL(j), (rdba & 0x00000000ffffffffULL)); IXGBE_WRITE_REG(hw, IXGBE_RDBAH(j), (rdba >> 32)); IXGBE_WRITE_REG(hw, IXGBE_RDLEN(j), scctx->isc_nrxd[0] * sizeof(union ixgbe_adv_rx_desc)); /* Set up the SRRCTL register */ srrctl = IXGBE_READ_REG(hw, IXGBE_SRRCTL(j)); srrctl &= ~IXGBE_SRRCTL_BSIZEHDR_MASK; srrctl &= ~IXGBE_SRRCTL_BSIZEPKT_MASK; srrctl |= bufsz; srrctl |= IXGBE_SRRCTL_DESCTYPE_ADV_ONEBUF; /* * Set DROP_EN iff we have no flow control and >1 queue. * Note that srrctl was cleared shortly before during reset, * so we do not need to clear the bit, but do it just in case * this code is moved elsewhere. */ if (sc->num_rx_queues > 1 && sc->hw.fc.requested_mode == ixgbe_fc_none) { srrctl |= IXGBE_SRRCTL_DROP_EN; } else { srrctl &= ~IXGBE_SRRCTL_DROP_EN; } IXGBE_WRITE_REG(hw, IXGBE_SRRCTL(j), srrctl); /* Setup the HW Rx Head and Tail Descriptor Pointers */ IXGBE_WRITE_REG(hw, IXGBE_RDH(j), 0); IXGBE_WRITE_REG(hw, IXGBE_RDT(j), 0); /* Set the driver rx tail address */ rxr->tail = IXGBE_RDT(rxr->me); } if (sc->hw.mac.type != ixgbe_mac_82598EB) { u32 psrtype = IXGBE_PSRTYPE_TCPHDR | IXGBE_PSRTYPE_UDPHDR | IXGBE_PSRTYPE_IPV4HDR | IXGBE_PSRTYPE_IPV6HDR; IXGBE_WRITE_REG(hw, IXGBE_PSRTYPE(0), psrtype); } rxcsum = IXGBE_READ_REG(hw, IXGBE_RXCSUM); ixgbe_initialize_rss_mapping(sc); if (sc->feat_en & IXGBE_FEATURE_RSS) { /* RSS and RX IPP Checksum are mutually exclusive */ rxcsum |= IXGBE_RXCSUM_PCSD; } if (ifp->if_capenable & IFCAP_RXCSUM) rxcsum |= IXGBE_RXCSUM_PCSD; /* This is useful for calculating UDP/IP fragment checksums */ if (!(rxcsum & IXGBE_RXCSUM_PCSD)) rxcsum |= IXGBE_RXCSUM_IPPCSE; IXGBE_WRITE_REG(hw, IXGBE_RXCSUM, rxcsum); } /* ixgbe_initialize_receive_units */ /************************************************************************ * ixgbe_initialize_transmit_units - Enable transmit units. ************************************************************************/ static void ixgbe_initialize_transmit_units(if_ctx_t ctx) { struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ixgbe_hw *hw = &sc->hw; if_softc_ctx_t scctx = sc->shared; struct ix_tx_queue *que; int i; /* Setup the Base and Length of the Tx Descriptor Ring */ for (i = 0, que = sc->tx_queues; i < sc->num_tx_queues; i++, que++) { struct tx_ring *txr = &que->txr; u64 tdba = txr->tx_paddr; u32 txctrl = 0; int j = txr->me; IXGBE_WRITE_REG(hw, IXGBE_TDBAL(j), (tdba & 0x00000000ffffffffULL)); IXGBE_WRITE_REG(hw, IXGBE_TDBAH(j), (tdba >> 32)); IXGBE_WRITE_REG(hw, IXGBE_TDLEN(j), scctx->isc_ntxd[0] * sizeof(union ixgbe_adv_tx_desc)); /* Setup the HW Tx Head and Tail descriptor pointers */ IXGBE_WRITE_REG(hw, IXGBE_TDH(j), 0); IXGBE_WRITE_REG(hw, IXGBE_TDT(j), 0); /* Cache the tail address */ txr->tail = IXGBE_TDT(txr->me); txr->tx_rs_cidx = txr->tx_rs_pidx; txr->tx_cidx_processed = scctx->isc_ntxd[0] - 1; for (int k = 0; k < scctx->isc_ntxd[0]; k++) txr->tx_rsq[k] = QIDX_INVALID; /* Disable Head Writeback */ /* * Note: for X550 series devices, these registers are actually * prefixed with TPH_ isntead of DCA_, but the addresses and * fields remain the same. */ switch (hw->mac.type) { case ixgbe_mac_82598EB: txctrl = IXGBE_READ_REG(hw, IXGBE_DCA_TXCTRL(j)); break; default: txctrl = IXGBE_READ_REG(hw, IXGBE_DCA_TXCTRL_82599(j)); break; } txctrl &= ~IXGBE_DCA_TXCTRL_DESC_WRO_EN; switch (hw->mac.type) { case ixgbe_mac_82598EB: IXGBE_WRITE_REG(hw, IXGBE_DCA_TXCTRL(j), txctrl); break; default: IXGBE_WRITE_REG(hw, IXGBE_DCA_TXCTRL_82599(j), txctrl); break; } } if (hw->mac.type != ixgbe_mac_82598EB) { u32 dmatxctl, rttdcs; dmatxctl = IXGBE_READ_REG(hw, IXGBE_DMATXCTL); dmatxctl |= IXGBE_DMATXCTL_TE; IXGBE_WRITE_REG(hw, IXGBE_DMATXCTL, dmatxctl); /* Disable arbiter to set MTQC */ rttdcs = IXGBE_READ_REG(hw, IXGBE_RTTDCS); rttdcs |= IXGBE_RTTDCS_ARBDIS; IXGBE_WRITE_REG(hw, IXGBE_RTTDCS, rttdcs); IXGBE_WRITE_REG(hw, IXGBE_MTQC, ixgbe_get_mtqc(sc->iov_mode)); rttdcs &= ~IXGBE_RTTDCS_ARBDIS; IXGBE_WRITE_REG(hw, IXGBE_RTTDCS, rttdcs); } } /* ixgbe_initialize_transmit_units */ /************************************************************************ * ixgbe_register ************************************************************************/ static void * ixgbe_register(device_t dev) { return (&ixgbe_sctx_init); } /* ixgbe_register */ /************************************************************************ * ixgbe_if_attach_pre - Device initialization routine, part 1 * * Called when the driver is being loaded. * Identifies the type of hardware, initializes the hardware, * and initializes iflib structures. * * return 0 on success, positive on failure ************************************************************************/ static int ixgbe_if_attach_pre(if_ctx_t ctx) { struct ixgbe_softc *sc; device_t dev; if_softc_ctx_t scctx; struct ixgbe_hw *hw; int error = 0; u32 ctrl_ext; INIT_DEBUGOUT("ixgbe_attach: begin"); /* Allocate, clear, and link in our adapter structure */ dev = iflib_get_dev(ctx); sc = iflib_get_softc(ctx); sc->hw.back = sc; sc->ctx = ctx; sc->dev = dev; scctx = sc->shared = iflib_get_softc_ctx(ctx); sc->media = iflib_get_media(ctx); hw = &sc->hw; /* Determine hardware revision */ hw->vendor_id = pci_get_vendor(dev); hw->device_id = pci_get_device(dev); hw->revision_id = pci_get_revid(dev); hw->subsystem_vendor_id = pci_get_subvendor(dev); hw->subsystem_device_id = pci_get_subdevice(dev); /* Do base PCI setup - map BAR0 */ if (ixgbe_allocate_pci_resources(ctx)) { device_printf(dev, "Allocation of PCI resources failed\n"); return (ENXIO); } /* let hardware know driver is loaded */ ctrl_ext = IXGBE_READ_REG(hw, IXGBE_CTRL_EXT); ctrl_ext |= IXGBE_CTRL_EXT_DRV_LOAD; IXGBE_WRITE_REG(hw, IXGBE_CTRL_EXT, ctrl_ext); /* * Initialize the shared code */ if (ixgbe_init_shared_code(hw) != 0) { device_printf(dev, "Unable to initialize the shared code\n"); error = ENXIO; goto err_pci; } if (hw->mac.ops.fw_recovery_mode && hw->mac.ops.fw_recovery_mode(hw)) { device_printf(dev, "Firmware recovery mode detected. Limiting " "functionality.\nRefer to the Intel(R) Ethernet Adapters " "and Devices User Guide for details on firmware recovery " "mode."); error = ENOSYS; goto err_pci; } if (hw->mbx.ops.init_params) hw->mbx.ops.init_params(hw); hw->allow_unsupported_sfp = allow_unsupported_sfp; if (hw->mac.type != ixgbe_mac_82598EB) hw->phy.smart_speed = ixgbe_smart_speed; ixgbe_init_device_features(sc); /* Enable WoL (if supported) */ ixgbe_check_wol_support(sc); /* Verify adapter fan is still functional (if applicable) */ if (sc->feat_en & IXGBE_FEATURE_FAN_FAIL) { u32 esdp = IXGBE_READ_REG(hw, IXGBE_ESDP); ixgbe_check_fan_failure(sc, esdp, false); } /* Ensure SW/FW semaphore is free */ ixgbe_init_swfw_semaphore(hw); /* Set an initial default flow control value */ hw->fc.requested_mode = ixgbe_flow_control; hw->phy.reset_if_overtemp = true; error = ixgbe_reset_hw(hw); hw->phy.reset_if_overtemp = false; if (error == IXGBE_ERR_SFP_NOT_PRESENT) { /* * No optics in this port, set up * so the timer routine will probe * for later insertion. */ sc->sfp_probe = true; error = 0; } else if (error == IXGBE_ERR_SFP_NOT_SUPPORTED) { device_printf(dev, "Unsupported SFP+ module detected!\n"); error = EIO; goto err_pci; } else if (error) { device_printf(dev, "Hardware initialization failed\n"); error = EIO; goto err_pci; } /* Make sure we have a good EEPROM before we read from it */ if (ixgbe_validate_eeprom_checksum(&sc->hw, NULL) < 0) { device_printf(dev, "The EEPROM Checksum Is Not Valid\n"); error = EIO; goto err_pci; } error = ixgbe_start_hw(hw); switch (error) { case IXGBE_ERR_EEPROM_VERSION: device_printf(dev, "This device is a pre-production adapter/LOM. Please be aware there may be issues associated with your hardware.\nIf you are experiencing problems please contact your Intel or hardware representative who provided you with this hardware.\n"); break; case IXGBE_ERR_SFP_NOT_SUPPORTED: device_printf(dev, "Unsupported SFP+ Module\n"); error = EIO; goto err_pci; case IXGBE_ERR_SFP_NOT_PRESENT: device_printf(dev, "No SFP+ Module found\n"); /* falls thru */ default: break; } /* Most of the iflib initialization... */ iflib_set_mac(ctx, hw->mac.addr); switch (sc->hw.mac.type) { case ixgbe_mac_X550: case ixgbe_mac_X550EM_x: case ixgbe_mac_X550EM_a: scctx->isc_rss_table_size = 512; scctx->isc_ntxqsets_max = scctx->isc_nrxqsets_max = 64; break; default: scctx->isc_rss_table_size = 128; scctx->isc_ntxqsets_max = scctx->isc_nrxqsets_max = 16; } /* Allow legacy interrupts */ ixgbe_txrx.ift_legacy_intr = ixgbe_intr; scctx->isc_txqsizes[0] = roundup2(scctx->isc_ntxd[0] * sizeof(union ixgbe_adv_tx_desc) + sizeof(u32), DBA_ALIGN), scctx->isc_rxqsizes[0] = roundup2(scctx->isc_nrxd[0] * sizeof(union ixgbe_adv_rx_desc), DBA_ALIGN); /* XXX */ scctx->isc_tx_csum_flags = CSUM_IP | CSUM_TCP | CSUM_UDP | CSUM_TSO | CSUM_IP6_TCP | CSUM_IP6_UDP | CSUM_IP6_TSO; if (sc->hw.mac.type == ixgbe_mac_82598EB) { scctx->isc_tx_nsegments = IXGBE_82598_SCATTER; } else { scctx->isc_tx_csum_flags |= CSUM_SCTP |CSUM_IP6_SCTP; scctx->isc_tx_nsegments = IXGBE_82599_SCATTER; } scctx->isc_msix_bar = pci_msix_table_bar(dev); scctx->isc_tx_tso_segments_max = scctx->isc_tx_nsegments; scctx->isc_tx_tso_size_max = IXGBE_TSO_SIZE; scctx->isc_tx_tso_segsize_max = PAGE_SIZE; scctx->isc_txrx = &ixgbe_txrx; scctx->isc_capabilities = scctx->isc_capenable = IXGBE_CAPS; return (0); err_pci: ctrl_ext = IXGBE_READ_REG(&sc->hw, IXGBE_CTRL_EXT); ctrl_ext &= ~IXGBE_CTRL_EXT_DRV_LOAD; IXGBE_WRITE_REG(&sc->hw, IXGBE_CTRL_EXT, ctrl_ext); ixgbe_free_pci_resources(ctx); return (error); } /* ixgbe_if_attach_pre */ /********************************************************************* * ixgbe_if_attach_post - Device initialization routine, part 2 * * Called during driver load, but after interrupts and * resources have been allocated and configured. * Sets up some data structures not relevant to iflib. * * return 0 on success, positive on failure *********************************************************************/ static int ixgbe_if_attach_post(if_ctx_t ctx) { device_t dev; struct ixgbe_softc *sc; struct ixgbe_hw *hw; int error = 0; dev = iflib_get_dev(ctx); sc = iflib_get_softc(ctx); hw = &sc->hw; if (sc->intr_type == IFLIB_INTR_LEGACY && (sc->feat_cap & IXGBE_FEATURE_LEGACY_IRQ) == 0) { device_printf(dev, "Device does not support legacy interrupts"); error = ENXIO; goto err; } /* Allocate multicast array memory. */ sc->mta = malloc(sizeof(*sc->mta) * MAX_NUM_MULTICAST_ADDRESSES, M_IXGBE, M_NOWAIT); if (sc->mta == NULL) { device_printf(dev, "Can not allocate multicast setup array\n"); error = ENOMEM; goto err; } /* hw.ix defaults init */ ixgbe_set_advertise(sc, ixgbe_advertise_speed); /* Enable the optics for 82599 SFP+ fiber */ ixgbe_enable_tx_laser(hw); /* Enable power to the phy. */ ixgbe_set_phy_power(hw, true); ixgbe_initialize_iov(sc); error = ixgbe_setup_interface(ctx); if (error) { device_printf(dev, "Interface setup failed: %d\n", error); goto err; } ixgbe_if_update_admin_status(ctx); /* Initialize statistics */ ixgbe_update_stats_counters(sc); ixgbe_add_hw_stats(sc); /* Check PCIE slot type/speed/width */ ixgbe_get_slot_info(sc); /* * Do time init and sysctl init here, but * only on the first port of a bypass sc. */ ixgbe_bypass_init(sc); /* Display NVM and Option ROM versions */ ixgbe_print_fw_version(ctx); /* Set an initial dmac value */ sc->dmac = 0; /* Set initial advertised speeds (if applicable) */ sc->advertise = ixgbe_get_default_advertise(sc); if (sc->feat_cap & IXGBE_FEATURE_SRIOV) ixgbe_define_iov_schemas(dev, &error); /* Add sysctls */ ixgbe_add_device_sysctls(ctx); return (0); err: return (error); } /* ixgbe_if_attach_post */ /************************************************************************ * ixgbe_check_wol_support * * Checks whether the adapter's ports are capable of * Wake On LAN by reading the adapter's NVM. * * Sets each port's hw->wol_enabled value depending * on the value read here. ************************************************************************/ static void ixgbe_check_wol_support(struct ixgbe_softc *sc) { struct ixgbe_hw *hw = &sc->hw; u16 dev_caps = 0; /* Find out WoL support for port */ sc->wol_support = hw->wol_enabled = 0; ixgbe_get_device_caps(hw, &dev_caps); if ((dev_caps & IXGBE_DEVICE_CAPS_WOL_PORT0_1) || ((dev_caps & IXGBE_DEVICE_CAPS_WOL_PORT0) && hw->bus.func == 0)) sc->wol_support = hw->wol_enabled = 1; /* Save initial wake up filter configuration */ sc->wufc = IXGBE_READ_REG(hw, IXGBE_WUFC); return; } /* ixgbe_check_wol_support */ /************************************************************************ * ixgbe_setup_interface * * Setup networking device structure and register an interface. ************************************************************************/ static int ixgbe_setup_interface(if_ctx_t ctx) { struct ifnet *ifp = iflib_get_ifp(ctx); struct ixgbe_softc *sc = iflib_get_softc(ctx); INIT_DEBUGOUT("ixgbe_setup_interface: begin"); if_setbaudrate(ifp, IF_Gbps(10)); sc->max_frame_size = ifp->if_mtu + ETHER_HDR_LEN + ETHER_CRC_LEN; sc->phy_layer = ixgbe_get_supported_physical_layer(&sc->hw); ixgbe_add_media_types(ctx); /* Autoselect media by default */ ifmedia_set(sc->media, IFM_ETHER | IFM_AUTO); return (0); } /* ixgbe_setup_interface */ /************************************************************************ * ixgbe_if_get_counter ************************************************************************/ static uint64_t ixgbe_if_get_counter(if_ctx_t ctx, ift_counter cnt) { struct ixgbe_softc *sc = iflib_get_softc(ctx); if_t ifp = iflib_get_ifp(ctx); switch (cnt) { case IFCOUNTER_IPACKETS: return (sc->ipackets); case IFCOUNTER_OPACKETS: return (sc->opackets); case IFCOUNTER_IBYTES: return (sc->ibytes); case IFCOUNTER_OBYTES: return (sc->obytes); case IFCOUNTER_IMCASTS: return (sc->imcasts); case IFCOUNTER_OMCASTS: return (sc->omcasts); case IFCOUNTER_COLLISIONS: return (0); case IFCOUNTER_IQDROPS: return (sc->iqdrops); case IFCOUNTER_OQDROPS: return (0); case IFCOUNTER_IERRORS: return (sc->ierrors); default: return (if_get_counter_default(ifp, cnt)); } } /* ixgbe_if_get_counter */ /************************************************************************ * ixgbe_if_i2c_req ************************************************************************/ static int ixgbe_if_i2c_req(if_ctx_t ctx, struct ifi2creq *req) { struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ixgbe_hw *hw = &sc->hw; int i; if (hw->phy.ops.read_i2c_byte == NULL) return (ENXIO); for (i = 0; i < req->len; i++) hw->phy.ops.read_i2c_byte(hw, req->offset + i, req->dev_addr, &req->data[i]); return (0); } /* ixgbe_if_i2c_req */ /* ixgbe_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. + * Defaults to returning false for unknown events. * * @returns true if iflib needs to reinit the interface */ static bool ixgbe_if_needs_restart(if_ctx_t ctx __unused, enum iflib_restart_event event) { switch (event) { case IFLIB_RESTART_VLAN_CONFIG: - return (false); default: - return (true); + return (false); } } /************************************************************************ * ixgbe_add_media_types ************************************************************************/ static void ixgbe_add_media_types(if_ctx_t ctx) { struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ixgbe_hw *hw = &sc->hw; device_t dev = iflib_get_dev(ctx); u64 layer; layer = sc->phy_layer = ixgbe_get_supported_physical_layer(hw); /* Media types with matching FreeBSD media defines */ if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_T) ifmedia_add(sc->media, IFM_ETHER | IFM_10G_T, 0, NULL); if (layer & IXGBE_PHYSICAL_LAYER_1000BASE_T) ifmedia_add(sc->media, IFM_ETHER | IFM_1000_T, 0, NULL); if (layer & IXGBE_PHYSICAL_LAYER_100BASE_TX) ifmedia_add(sc->media, IFM_ETHER | IFM_100_TX, 0, NULL); if (layer & IXGBE_PHYSICAL_LAYER_10BASE_T) ifmedia_add(sc->media, IFM_ETHER | IFM_10_T, 0, NULL); if (hw->mac.type == ixgbe_mac_X550) { ifmedia_add(sc->media, IFM_ETHER | IFM_2500_T, 0, NULL); ifmedia_add(sc->media, IFM_ETHER | IFM_5000_T, 0, NULL); } if (layer & IXGBE_PHYSICAL_LAYER_SFP_PLUS_CU || layer & IXGBE_PHYSICAL_LAYER_SFP_ACTIVE_DA) ifmedia_add(sc->media, IFM_ETHER | IFM_10G_TWINAX, 0, NULL); if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_LR) { ifmedia_add(sc->media, IFM_ETHER | IFM_10G_LR, 0, NULL); if (hw->phy.multispeed_fiber) ifmedia_add(sc->media, IFM_ETHER | IFM_1000_LX, 0, NULL); } if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_SR) { ifmedia_add(sc->media, IFM_ETHER | IFM_10G_SR, 0, NULL); if (hw->phy.multispeed_fiber) ifmedia_add(sc->media, IFM_ETHER | IFM_1000_SX, 0, NULL); } else if (layer & IXGBE_PHYSICAL_LAYER_1000BASE_SX) ifmedia_add(sc->media, IFM_ETHER | IFM_1000_SX, 0, NULL); if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_CX4) ifmedia_add(sc->media, IFM_ETHER | IFM_10G_CX4, 0, NULL); #ifdef IFM_ETH_XTYPE if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_KR) ifmedia_add(sc->media, IFM_ETHER | IFM_10G_KR, 0, NULL); if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_KX4) ifmedia_add( sc->media, IFM_ETHER | IFM_10G_KX4, 0, NULL); if (layer & IXGBE_PHYSICAL_LAYER_1000BASE_KX) ifmedia_add(sc->media, IFM_ETHER | IFM_1000_KX, 0, NULL); if (layer & IXGBE_PHYSICAL_LAYER_2500BASE_KX) ifmedia_add(sc->media, IFM_ETHER | IFM_2500_KX, 0, NULL); #else if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_KR) { device_printf(dev, "Media supported: 10GbaseKR\n"); device_printf(dev, "10GbaseKR mapped to 10GbaseSR\n"); ifmedia_add(sc->media, IFM_ETHER | IFM_10G_SR, 0, NULL); } if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_KX4) { device_printf(dev, "Media supported: 10GbaseKX4\n"); device_printf(dev, "10GbaseKX4 mapped to 10GbaseCX4\n"); ifmedia_add(sc->media, IFM_ETHER | IFM_10G_CX4, 0, NULL); } if (layer & IXGBE_PHYSICAL_LAYER_1000BASE_KX) { device_printf(dev, "Media supported: 1000baseKX\n"); device_printf(dev, "1000baseKX mapped to 1000baseCX\n"); ifmedia_add(sc->media, IFM_ETHER | IFM_1000_CX, 0, NULL); } if (layer & IXGBE_PHYSICAL_LAYER_2500BASE_KX) { device_printf(dev, "Media supported: 2500baseKX\n"); device_printf(dev, "2500baseKX mapped to 2500baseSX\n"); ifmedia_add(sc->media, IFM_ETHER | IFM_2500_SX, 0, NULL); } #endif if (layer & IXGBE_PHYSICAL_LAYER_1000BASE_BX) device_printf(dev, "Media supported: 1000baseBX\n"); if (hw->device_id == IXGBE_DEV_ID_82598AT) { ifmedia_add(sc->media, IFM_ETHER | IFM_1000_T | IFM_FDX, 0, NULL); ifmedia_add(sc->media, IFM_ETHER | IFM_1000_T, 0, NULL); } ifmedia_add(sc->media, IFM_ETHER | IFM_AUTO, 0, NULL); } /* ixgbe_add_media_types */ /************************************************************************ * ixgbe_is_sfp ************************************************************************/ static inline bool ixgbe_is_sfp(struct ixgbe_hw *hw) { switch (hw->mac.type) { case ixgbe_mac_82598EB: if (hw->phy.type == ixgbe_phy_nl) return (true); return (false); case ixgbe_mac_82599EB: switch (hw->mac.ops.get_media_type(hw)) { case ixgbe_media_type_fiber: case ixgbe_media_type_fiber_qsfp: return (true); default: return (false); } case ixgbe_mac_X550EM_x: case ixgbe_mac_X550EM_a: if (hw->mac.ops.get_media_type(hw) == ixgbe_media_type_fiber) return (true); return (false); default: return (false); } } /* ixgbe_is_sfp */ /************************************************************************ * ixgbe_config_link ************************************************************************/ static void ixgbe_config_link(if_ctx_t ctx) { struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ixgbe_hw *hw = &sc->hw; u32 autoneg, err = 0; bool sfp, negotiate; sfp = ixgbe_is_sfp(hw); if (sfp) { sc->task_requests |= IXGBE_REQUEST_TASK_MOD; iflib_admin_intr_deferred(ctx); } else { if (hw->mac.ops.check_link) err = ixgbe_check_link(hw, &sc->link_speed, &sc->link_up, false); if (err) return; autoneg = hw->phy.autoneg_advertised; if ((!autoneg) && (hw->mac.ops.get_link_capabilities)) err = hw->mac.ops.get_link_capabilities(hw, &autoneg, &negotiate); if (err) return; if (hw->mac.type == ixgbe_mac_X550 && hw->phy.autoneg_advertised == 0) { /* * 2.5G and 5G autonegotiation speeds on X550 * are disabled by default due to reported * interoperability issues with some switches. * * The second condition checks if any operations * involving setting autonegotiation speeds have * been performed prior to this ixgbe_config_link() * call. * * If hw->phy.autoneg_advertised does not * equal 0, this means that the user might have * set autonegotiation speeds via the sysctl * before bringing the interface up. In this * case, we should not disable 2.5G and 5G * since that speeds might be selected by the * user. * * Otherwise (i.e. if hw->phy.autoneg_advertised * is set to 0), it is the first time we set * autonegotiation preferences and the default * set of speeds should exclude 2.5G and 5G. */ autoneg &= ~(IXGBE_LINK_SPEED_2_5GB_FULL | IXGBE_LINK_SPEED_5GB_FULL); } if (hw->mac.ops.setup_link) err = hw->mac.ops.setup_link(hw, autoneg, sc->link_up); } } /* ixgbe_config_link */ /************************************************************************ * ixgbe_update_stats_counters - Update board statistics counters. ************************************************************************/ static void ixgbe_update_stats_counters(struct ixgbe_softc *sc) { struct ixgbe_hw *hw = &sc->hw; struct ixgbe_hw_stats *stats = &sc->stats.pf; u32 missed_rx = 0, bprc, lxon, lxoff, total; u32 lxoffrxc; u64 total_missed_rx = 0; stats->crcerrs += IXGBE_READ_REG(hw, IXGBE_CRCERRS); stats->illerrc += IXGBE_READ_REG(hw, IXGBE_ILLERRC); stats->errbc += IXGBE_READ_REG(hw, IXGBE_ERRBC); stats->mspdc += IXGBE_READ_REG(hw, IXGBE_MSPDC); stats->mpc[0] += IXGBE_READ_REG(hw, IXGBE_MPC(0)); for (int i = 0; i < 16; i++) { stats->qprc[i] += IXGBE_READ_REG(hw, IXGBE_QPRC(i)); stats->qptc[i] += IXGBE_READ_REG(hw, IXGBE_QPTC(i)); stats->qprdc[i] += IXGBE_READ_REG(hw, IXGBE_QPRDC(i)); } stats->mlfc += IXGBE_READ_REG(hw, IXGBE_MLFC); stats->mrfc += IXGBE_READ_REG(hw, IXGBE_MRFC); stats->rlec += IXGBE_READ_REG(hw, IXGBE_RLEC); /* Hardware workaround, gprc counts missed packets */ stats->gprc += IXGBE_READ_REG(hw, IXGBE_GPRC); stats->gprc -= missed_rx; if (hw->mac.type != ixgbe_mac_82598EB) { stats->gorc += IXGBE_READ_REG(hw, IXGBE_GORCL) + ((u64)IXGBE_READ_REG(hw, IXGBE_GORCH) << 32); stats->gotc += IXGBE_READ_REG(hw, IXGBE_GOTCL) + ((u64)IXGBE_READ_REG(hw, IXGBE_GOTCH) << 32); stats->tor += IXGBE_READ_REG(hw, IXGBE_TORL) + ((u64)IXGBE_READ_REG(hw, IXGBE_TORH) << 32); stats->lxonrxc += IXGBE_READ_REG(hw, IXGBE_LXONRXCNT); lxoffrxc = IXGBE_READ_REG(hw, IXGBE_LXOFFRXCNT); stats->lxoffrxc += lxoffrxc; } else { stats->lxonrxc += IXGBE_READ_REG(hw, IXGBE_LXONRXC); lxoffrxc = IXGBE_READ_REG(hw, IXGBE_LXOFFRXC); stats->lxoffrxc += lxoffrxc; /* 82598 only has a counter in the high register */ stats->gorc += IXGBE_READ_REG(hw, IXGBE_GORCH); stats->gotc += IXGBE_READ_REG(hw, IXGBE_GOTCH); stats->tor += IXGBE_READ_REG(hw, IXGBE_TORH); } /* * For watchdog management we need to know if we have been paused * during the last interval, so capture that here. */ if (lxoffrxc) sc->shared->isc_pause_frames = 1; /* * Workaround: mprc hardware is incorrectly counting * broadcasts, so for now we subtract those. */ bprc = IXGBE_READ_REG(hw, IXGBE_BPRC); stats->bprc += bprc; stats->mprc += IXGBE_READ_REG(hw, IXGBE_MPRC); if (hw->mac.type == ixgbe_mac_82598EB) stats->mprc -= bprc; stats->prc64 += IXGBE_READ_REG(hw, IXGBE_PRC64); stats->prc127 += IXGBE_READ_REG(hw, IXGBE_PRC127); stats->prc255 += IXGBE_READ_REG(hw, IXGBE_PRC255); stats->prc511 += IXGBE_READ_REG(hw, IXGBE_PRC511); stats->prc1023 += IXGBE_READ_REG(hw, IXGBE_PRC1023); stats->prc1522 += IXGBE_READ_REG(hw, IXGBE_PRC1522); lxon = IXGBE_READ_REG(hw, IXGBE_LXONTXC); stats->lxontxc += lxon; lxoff = IXGBE_READ_REG(hw, IXGBE_LXOFFTXC); stats->lxofftxc += lxoff; total = lxon + lxoff; stats->gptc += IXGBE_READ_REG(hw, IXGBE_GPTC); stats->mptc += IXGBE_READ_REG(hw, IXGBE_MPTC); stats->ptc64 += IXGBE_READ_REG(hw, IXGBE_PTC64); stats->gptc -= total; stats->mptc -= total; stats->ptc64 -= total; stats->gotc -= total * ETHER_MIN_LEN; stats->ruc += IXGBE_READ_REG(hw, IXGBE_RUC); stats->rfc += IXGBE_READ_REG(hw, IXGBE_RFC); stats->roc += IXGBE_READ_REG(hw, IXGBE_ROC); stats->rjc += IXGBE_READ_REG(hw, IXGBE_RJC); stats->mngprc += IXGBE_READ_REG(hw, IXGBE_MNGPRC); stats->mngpdc += IXGBE_READ_REG(hw, IXGBE_MNGPDC); stats->mngptc += IXGBE_READ_REG(hw, IXGBE_MNGPTC); stats->tpr += IXGBE_READ_REG(hw, IXGBE_TPR); stats->tpt += IXGBE_READ_REG(hw, IXGBE_TPT); stats->ptc127 += IXGBE_READ_REG(hw, IXGBE_PTC127); stats->ptc255 += IXGBE_READ_REG(hw, IXGBE_PTC255); stats->ptc511 += IXGBE_READ_REG(hw, IXGBE_PTC511); stats->ptc1023 += IXGBE_READ_REG(hw, IXGBE_PTC1023); stats->ptc1522 += IXGBE_READ_REG(hw, IXGBE_PTC1522); stats->bptc += IXGBE_READ_REG(hw, IXGBE_BPTC); stats->xec += IXGBE_READ_REG(hw, IXGBE_XEC); stats->fccrc += IXGBE_READ_REG(hw, IXGBE_FCCRC); stats->fclast += IXGBE_READ_REG(hw, IXGBE_FCLAST); /* Only read FCOE on 82599 */ if (hw->mac.type != ixgbe_mac_82598EB) { stats->fcoerpdc += IXGBE_READ_REG(hw, IXGBE_FCOERPDC); stats->fcoeprc += IXGBE_READ_REG(hw, IXGBE_FCOEPRC); stats->fcoeptc += IXGBE_READ_REG(hw, IXGBE_FCOEPTC); stats->fcoedwrc += IXGBE_READ_REG(hw, IXGBE_FCOEDWRC); stats->fcoedwtc += IXGBE_READ_REG(hw, IXGBE_FCOEDWTC); } /* Fill out the OS statistics structure */ IXGBE_SET_IPACKETS(sc, stats->gprc); IXGBE_SET_OPACKETS(sc, stats->gptc); IXGBE_SET_IBYTES(sc, stats->gorc); IXGBE_SET_OBYTES(sc, stats->gotc); IXGBE_SET_IMCASTS(sc, stats->mprc); IXGBE_SET_OMCASTS(sc, stats->mptc); IXGBE_SET_COLLISIONS(sc, 0); IXGBE_SET_IQDROPS(sc, total_missed_rx); /* * Aggregate following types of errors as RX errors: * - CRC error count, * - illegal byte error count, * - checksum error count, * - missed packets count, * - length error count, * - undersized packets count, * - fragmented packets count, * - oversized packets count, * - jabber count. * * Ignore XEC errors for 82599 to workaround errata about * UDP frames with zero checksum. */ IXGBE_SET_IERRORS(sc, stats->crcerrs + stats->illerrc + (hw->mac.type != ixgbe_mac_82599EB ? stats->xec : 0) + stats->mpc[0] + stats->rlec + stats->ruc + stats->rfc + stats->roc + stats->rjc); } /* ixgbe_update_stats_counters */ /************************************************************************ * ixgbe_add_hw_stats * * Add sysctl variables, one per statistic, to the system. ************************************************************************/ static void ixgbe_add_hw_stats(struct ixgbe_softc *sc) { device_t dev = iflib_get_dev(sc->ctx); struct ix_rx_queue *rx_que; struct ix_tx_queue *tx_que; 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 ixgbe_hw_stats *stats = &sc->stats.pf; struct sysctl_oid *stat_node, *queue_node; struct sysctl_oid_list *stat_list, *queue_list; int i; #define QUEUE_NAME_LEN 32 char namebuf[QUEUE_NAME_LEN]; /* Driver Statistics */ SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "dropped", CTLFLAG_RD, &sc->dropped_pkts, "Driver dropped packets"); SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "watchdog_events", CTLFLAG_RD, &sc->watchdog_events, "Watchdog timeouts"); SYSCTL_ADD_ULONG(ctx, child, OID_AUTO, "link_irq", CTLFLAG_RD, &sc->link_irq, "Link MSI-X IRQ Handled"); for (i = 0, tx_que = sc->tx_queues; i < sc->num_tx_queues; i++, tx_que++) { struct tx_ring *txr = &tx_que->txr; snprintf(namebuf, QUEUE_NAME_LEN, "queue%d", i); queue_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, namebuf, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Queue Name"); queue_list = SYSCTL_CHILDREN(queue_node); SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "txd_head", CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, txr, 0, ixgbe_sysctl_tdh_handler, "IU", "Transmit Descriptor Head"); SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "txd_tail", CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, txr, 0, ixgbe_sysctl_tdt_handler, "IU", "Transmit Descriptor Tail"); SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "tso_tx", CTLFLAG_RD, &txr->tso_tx, "TSO"); SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "tx_packets", CTLFLAG_RD, &txr->total_packets, "Queue Packets Transmitted"); } for (i = 0, rx_que = sc->rx_queues; i < sc->num_rx_queues; i++, rx_que++) { struct rx_ring *rxr = &rx_que->rxr; snprintf(namebuf, QUEUE_NAME_LEN, "queue%d", i); queue_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, namebuf, CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "Queue Name"); queue_list = SYSCTL_CHILDREN(queue_node); SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "interrupt_rate", CTLTYPE_UINT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, &sc->rx_queues[i], 0, ixgbe_sysctl_interrupt_rate_handler, "IU", "Interrupt Rate"); SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "irqs", CTLFLAG_RD, &(sc->rx_queues[i].irqs), "irqs on this queue"); SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "rxd_head", CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, rxr, 0, ixgbe_sysctl_rdh_handler, "IU", "Receive Descriptor Head"); SYSCTL_ADD_PROC(ctx, queue_list, OID_AUTO, "rxd_tail", CTLTYPE_UINT | CTLFLAG_RD | CTLFLAG_NEEDGIANT, rxr, 0, ixgbe_sysctl_rdt_handler, "IU", "Receive Descriptor Tail"); SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "rx_packets", CTLFLAG_RD, &rxr->rx_packets, "Queue Packets Received"); SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "rx_bytes", CTLFLAG_RD, &rxr->rx_bytes, "Queue Bytes Received"); SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "rx_copies", CTLFLAG_RD, &rxr->rx_copies, "Copied RX Frames"); SYSCTL_ADD_UQUAD(ctx, queue_list, OID_AUTO, "rx_discarded", CTLFLAG_RD, &rxr->rx_discarded, "Discarded RX packets"); } /* MAC stats get their own sub node */ stat_node = SYSCTL_ADD_NODE(ctx, child, OID_AUTO, "mac_stats", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "MAC Statistics"); stat_list = SYSCTL_CHILDREN(stat_node); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_errs", CTLFLAG_RD, &sc->ierrors, IXGBE_SYSCTL_DESC_RX_ERRS); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "crc_errs", CTLFLAG_RD, &stats->crcerrs, "CRC Errors"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "ill_errs", CTLFLAG_RD, &stats->illerrc, "Illegal Byte Errors"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "byte_errs", CTLFLAG_RD, &stats->errbc, "Byte Errors"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "short_discards", CTLFLAG_RD, &stats->mspdc, "MAC Short Packets Discarded"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "local_faults", CTLFLAG_RD, &stats->mlfc, "MAC Local Faults"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "remote_faults", CTLFLAG_RD, &stats->mrfc, "MAC Remote Faults"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rec_len_errs", CTLFLAG_RD, &stats->rlec, "Receive Length Errors"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_missed_packets", CTLFLAG_RD, &stats->mpc[0], "RX Missed Packet Count"); /* Flow Control stats */ SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "xon_txd", CTLFLAG_RD, &stats->lxontxc, "Link XON Transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "xon_recvd", CTLFLAG_RD, &stats->lxonrxc, "Link XON Received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "xoff_txd", CTLFLAG_RD, &stats->lxofftxc, "Link XOFF Transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "xoff_recvd", CTLFLAG_RD, &stats->lxoffrxc, "Link XOFF Received"); /* Packet Reception Stats */ SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "total_octets_rcvd", CTLFLAG_RD, &stats->tor, "Total Octets Received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "good_octets_rcvd", CTLFLAG_RD, &stats->gorc, "Good Octets Received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "total_pkts_rcvd", CTLFLAG_RD, &stats->tpr, "Total Packets Received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "good_pkts_rcvd", CTLFLAG_RD, &stats->gprc, "Good Packets Received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "mcast_pkts_rcvd", CTLFLAG_RD, &stats->mprc, "Multicast Packets Received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "bcast_pkts_rcvd", CTLFLAG_RD, &stats->bprc, "Broadcast Packets Received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_64", CTLFLAG_RD, &stats->prc64, "64 byte frames received "); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_65_127", CTLFLAG_RD, &stats->prc127, "65-127 byte frames received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_128_255", CTLFLAG_RD, &stats->prc255, "128-255 byte frames received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_256_511", CTLFLAG_RD, &stats->prc511, "256-511 byte frames received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_512_1023", CTLFLAG_RD, &stats->prc1023, "512-1023 byte frames received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "rx_frames_1024_1522", CTLFLAG_RD, &stats->prc1522, "1023-1522 byte frames received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "recv_undersized", CTLFLAG_RD, &stats->ruc, "Receive Undersized"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "recv_fragmented", CTLFLAG_RD, &stats->rfc, "Fragmented Packets Received "); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "recv_oversized", CTLFLAG_RD, &stats->roc, "Oversized Packets Received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "recv_jabberd", CTLFLAG_RD, &stats->rjc, "Received Jabber"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "management_pkts_rcvd", CTLFLAG_RD, &stats->mngprc, "Management Packets Received"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "management_pkts_drpd", CTLFLAG_RD, &stats->mngptc, "Management Packets Dropped"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "checksum_errs", CTLFLAG_RD, &stats->xec, "Checksum Errors"); /* Packet Transmission Stats */ SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "good_octets_txd", CTLFLAG_RD, &stats->gotc, "Good Octets Transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "total_pkts_txd", CTLFLAG_RD, &stats->tpt, "Total Packets Transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "good_pkts_txd", CTLFLAG_RD, &stats->gptc, "Good Packets Transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "bcast_pkts_txd", CTLFLAG_RD, &stats->bptc, "Broadcast Packets Transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "mcast_pkts_txd", CTLFLAG_RD, &stats->mptc, "Multicast Packets Transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "management_pkts_txd", CTLFLAG_RD, &stats->mngptc, "Management Packets Transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_64", CTLFLAG_RD, &stats->ptc64, "64 byte frames transmitted "); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_65_127", CTLFLAG_RD, &stats->ptc127, "65-127 byte frames transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_128_255", CTLFLAG_RD, &stats->ptc255, "128-255 byte frames transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_256_511", CTLFLAG_RD, &stats->ptc511, "256-511 byte frames transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_512_1023", CTLFLAG_RD, &stats->ptc1023, "512-1023 byte frames transmitted"); SYSCTL_ADD_UQUAD(ctx, stat_list, OID_AUTO, "tx_frames_1024_1522", CTLFLAG_RD, &stats->ptc1522, "1024-1522 byte frames transmitted"); } /* ixgbe_add_hw_stats */ /************************************************************************ * ixgbe_sysctl_tdh_handler - Transmit Descriptor Head handler function * * Retrieves the TDH value from the hardware ************************************************************************/ static int ixgbe_sysctl_tdh_handler(SYSCTL_HANDLER_ARGS) { struct tx_ring *txr = ((struct tx_ring *)oidp->oid_arg1); int error; unsigned int val; if (!txr) return (0); val = IXGBE_READ_REG(&txr->sc->hw, IXGBE_TDH(txr->me)); error = sysctl_handle_int(oidp, &val, 0, req); if (error || !req->newptr) return error; return (0); } /* ixgbe_sysctl_tdh_handler */ /************************************************************************ * ixgbe_sysctl_tdt_handler - Transmit Descriptor Tail handler function * * Retrieves the TDT value from the hardware ************************************************************************/ static int ixgbe_sysctl_tdt_handler(SYSCTL_HANDLER_ARGS) { struct tx_ring *txr = ((struct tx_ring *)oidp->oid_arg1); int error; unsigned int val; if (!txr) return (0); val = IXGBE_READ_REG(&txr->sc->hw, IXGBE_TDT(txr->me)); error = sysctl_handle_int(oidp, &val, 0, req); if (error || !req->newptr) return error; return (0); } /* ixgbe_sysctl_tdt_handler */ /************************************************************************ * ixgbe_sysctl_rdh_handler - Receive Descriptor Head handler function * * Retrieves the RDH value from the hardware ************************************************************************/ static int ixgbe_sysctl_rdh_handler(SYSCTL_HANDLER_ARGS) { struct rx_ring *rxr = ((struct rx_ring *)oidp->oid_arg1); int error; unsigned int val; if (!rxr) return (0); val = IXGBE_READ_REG(&rxr->sc->hw, IXGBE_RDH(rxr->me)); error = sysctl_handle_int(oidp, &val, 0, req); if (error || !req->newptr) return error; return (0); } /* ixgbe_sysctl_rdh_handler */ /************************************************************************ * ixgbe_sysctl_rdt_handler - Receive Descriptor Tail handler function * * Retrieves the RDT value from the hardware ************************************************************************/ static int ixgbe_sysctl_rdt_handler(SYSCTL_HANDLER_ARGS) { struct rx_ring *rxr = ((struct rx_ring *)oidp->oid_arg1); int error; unsigned int val; if (!rxr) return (0); val = IXGBE_READ_REG(&rxr->sc->hw, IXGBE_RDT(rxr->me)); error = sysctl_handle_int(oidp, &val, 0, req); if (error || !req->newptr) return error; return (0); } /* ixgbe_sysctl_rdt_handler */ /************************************************************************ * ixgbe_if_vlan_register * * Run via vlan config EVENT, it enables us to use the * HW Filter table since we can get the vlan id. This * just creates the entry in the soft version of the * VFTA, init will repopulate the real table. ************************************************************************/ static void ixgbe_if_vlan_register(if_ctx_t ctx, u16 vtag) { struct ixgbe_softc *sc = iflib_get_softc(ctx); u16 index, bit; index = (vtag >> 5) & 0x7F; bit = vtag & 0x1F; sc->shadow_vfta[index] |= (1 << bit); ++sc->num_vlans; ixgbe_setup_vlan_hw_support(ctx); } /* ixgbe_if_vlan_register */ /************************************************************************ * ixgbe_if_vlan_unregister * * Run via vlan unconfig EVENT, remove our entry in the soft vfta. ************************************************************************/ static void ixgbe_if_vlan_unregister(if_ctx_t ctx, u16 vtag) { struct ixgbe_softc *sc = iflib_get_softc(ctx); u16 index, bit; index = (vtag >> 5) & 0x7F; bit = vtag & 0x1F; sc->shadow_vfta[index] &= ~(1 << bit); --sc->num_vlans; /* Re-init to load the changes */ ixgbe_setup_vlan_hw_support(ctx); } /* ixgbe_if_vlan_unregister */ /************************************************************************ * ixgbe_setup_vlan_hw_support ************************************************************************/ static void ixgbe_setup_vlan_hw_support(if_ctx_t ctx) { struct ifnet *ifp = iflib_get_ifp(ctx); struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ixgbe_hw *hw = &sc->hw; struct rx_ring *rxr; int i; u32 ctrl; /* * 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 (sc->num_vlans == 0) return; /* Setup the queues for vlans */ if (ifp->if_capenable & IFCAP_VLAN_HWTAGGING) { for (i = 0; i < sc->num_rx_queues; i++) { rxr = &sc->rx_queues[i].rxr; /* On 82599 the VLAN enable is per/queue in RXDCTL */ if (hw->mac.type != ixgbe_mac_82598EB) { ctrl = IXGBE_READ_REG(hw, IXGBE_RXDCTL(rxr->me)); ctrl |= IXGBE_RXDCTL_VME; IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(rxr->me), ctrl); } rxr->vtag_strip = true; } } if ((ifp->if_capenable & IFCAP_VLAN_HWFILTER) == 0) return; /* * A soft reset zero's out the VFTA, so * we need to repopulate it now. */ for (i = 0; i < IXGBE_VFTA_SIZE; i++) if (sc->shadow_vfta[i] != 0) IXGBE_WRITE_REG(hw, IXGBE_VFTA(i), sc->shadow_vfta[i]); ctrl = IXGBE_READ_REG(hw, IXGBE_VLNCTRL); /* Enable the Filter Table if enabled */ if (ifp->if_capenable & IFCAP_VLAN_HWFILTER) { ctrl &= ~IXGBE_VLNCTRL_CFIEN; ctrl |= IXGBE_VLNCTRL_VFE; } if (hw->mac.type == ixgbe_mac_82598EB) ctrl |= IXGBE_VLNCTRL_VME; IXGBE_WRITE_REG(hw, IXGBE_VLNCTRL, ctrl); } /* ixgbe_setup_vlan_hw_support */ /************************************************************************ * ixgbe_get_slot_info * * Get the width and transaction speed of * the slot this adapter is plugged into. ************************************************************************/ static void ixgbe_get_slot_info(struct ixgbe_softc *sc) { device_t dev = iflib_get_dev(sc->ctx); struct ixgbe_hw *hw = &sc->hw; int bus_info_valid = true; u32 offset; u16 link; /* Some devices are behind an internal bridge */ switch (hw->device_id) { case IXGBE_DEV_ID_82599_SFP_SF_QP: case IXGBE_DEV_ID_82599_QSFP_SF_QP: goto get_parent_info; default: break; } ixgbe_get_bus_info(hw); /* * Some devices don't use PCI-E, but there is no need * to display "Unknown" for bus speed and width. */ switch (hw->mac.type) { case ixgbe_mac_X550EM_x: case ixgbe_mac_X550EM_a: return; default: goto display; } get_parent_info: /* * For the Quad port adapter we need to parse back * up the PCI tree to find the speed of the expansion * slot into which this adapter is plugged. A bit more work. */ dev = device_get_parent(device_get_parent(dev)); #ifdef IXGBE_DEBUG device_printf(dev, "parent pcib = %x,%x,%x\n", pci_get_bus(dev), pci_get_slot(dev), pci_get_function(dev)); #endif dev = device_get_parent(device_get_parent(dev)); #ifdef IXGBE_DEBUG device_printf(dev, "slot pcib = %x,%x,%x\n", pci_get_bus(dev), pci_get_slot(dev), pci_get_function(dev)); #endif /* Now get the PCI Express Capabilities offset */ if (pci_find_cap(dev, PCIY_EXPRESS, &offset)) { /* * Hmm...can't get PCI-Express capabilities. * Falling back to default method. */ bus_info_valid = false; ixgbe_get_bus_info(hw); goto display; } /* ...and read the Link Status Register */ link = pci_read_config(dev, offset + PCIER_LINK_STA, 2); ixgbe_set_pci_config_data_generic(hw, link); display: device_printf(dev, "PCI Express Bus: Speed %s %s\n", ((hw->bus.speed == ixgbe_bus_speed_8000) ? "8.0GT/s" : (hw->bus.speed == ixgbe_bus_speed_5000) ? "5.0GT/s" : (hw->bus.speed == ixgbe_bus_speed_2500) ? "2.5GT/s" : "Unknown"), ((hw->bus.width == ixgbe_bus_width_pcie_x8) ? "Width x8" : (hw->bus.width == ixgbe_bus_width_pcie_x4) ? "Width x4" : (hw->bus.width == ixgbe_bus_width_pcie_x1) ? "Width x1" : "Unknown")); if (bus_info_valid) { if ((hw->device_id != IXGBE_DEV_ID_82599_SFP_SF_QP) && ((hw->bus.width <= ixgbe_bus_width_pcie_x4) && (hw->bus.speed == ixgbe_bus_speed_2500))) { device_printf(dev, "PCI-Express bandwidth available for this card\n is not sufficient for optimal performance.\n"); device_printf(dev, "For optimal performance a x8 PCIE, or x4 PCIE Gen2 slot is required.\n"); } if ((hw->device_id == IXGBE_DEV_ID_82599_SFP_SF_QP) && ((hw->bus.width <= ixgbe_bus_width_pcie_x8) && (hw->bus.speed < ixgbe_bus_speed_8000))) { device_printf(dev, "PCI-Express bandwidth available for this card\n is not sufficient for optimal performance.\n"); device_printf(dev, "For optimal performance a x8 PCIE Gen3 slot is required.\n"); } } else device_printf(dev, "Unable to determine slot speed/width. The speed/width reported are that of the internal switch.\n"); return; } /* ixgbe_get_slot_info */ /************************************************************************ * ixgbe_if_msix_intr_assign * * Setup MSI-X Interrupt resources and handlers ************************************************************************/ static int ixgbe_if_msix_intr_assign(if_ctx_t ctx, int msix) { struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ix_rx_queue *rx_que = sc->rx_queues; struct ix_tx_queue *tx_que; int error, rid, vector = 0; char buf[16]; /* Admin Que is vector 0*/ rid = vector + 1; for (int i = 0; i < sc->num_rx_queues; i++, vector++, rx_que++) { rid = vector + 1; snprintf(buf, sizeof(buf), "rxq%d", i); error = iflib_irq_alloc_generic(ctx, &rx_que->que_irq, rid, IFLIB_INTR_RXTX, ixgbe_msix_que, rx_que, rx_que->rxr.me, buf); if (error) { device_printf(iflib_get_dev(ctx), "Failed to allocate que int %d err: %d", i, error); sc->num_rx_queues = i + 1; goto fail; } rx_que->msix = vector; } for (int i = 0; i < sc->num_tx_queues; i++) { snprintf(buf, sizeof(buf), "txq%d", i); tx_que = &sc->tx_queues[i]; tx_que->msix = i % sc->num_rx_queues; iflib_softirq_alloc_generic(ctx, &sc->rx_queues[tx_que->msix].que_irq, IFLIB_INTR_TX, tx_que, tx_que->txr.me, buf); } rid = vector + 1; error = iflib_irq_alloc_generic(ctx, &sc->irq, rid, IFLIB_INTR_ADMIN, ixgbe_msix_link, sc, 0, "aq"); if (error) { device_printf(iflib_get_dev(ctx), "Failed to register admin handler"); return (error); } sc->vector = vector; return (0); fail: iflib_irq_free(ctx, &sc->irq); rx_que = sc->rx_queues; for (int i = 0; i < sc->num_rx_queues; i++, rx_que++) iflib_irq_free(ctx, &rx_que->que_irq); return (error); } /* ixgbe_if_msix_intr_assign */ static inline void ixgbe_perform_aim(struct ixgbe_softc *sc, struct ix_rx_queue *que) { uint32_t newitr = 0; struct rx_ring *rxr = &que->rxr; /* * Do Adaptive Interrupt Moderation: * - Write out last calculated setting * - Calculate based on average size over * the last interval. */ if (que->eitr_setting) { IXGBE_WRITE_REG(&sc->hw, IXGBE_EITR(que->msix), que->eitr_setting); } que->eitr_setting = 0; /* Idle, do nothing */ if (rxr->bytes == 0) { return; } if ((rxr->bytes) && (rxr->packets)) { newitr = (rxr->bytes / rxr->packets); } newitr += 24; /* account for hardware frame, crc */ /* set an upper boundary */ newitr = min(newitr, 3000); /* Be nice to the mid range */ if ((newitr > 300) && (newitr < 1200)) { newitr = (newitr / 3); } else { newitr = (newitr / 2); } if (sc->hw.mac.type == ixgbe_mac_82598EB) { newitr |= newitr << 16; } else { newitr |= IXGBE_EITR_CNT_WDIS; } /* save for next interrupt */ que->eitr_setting = newitr; /* Reset state */ rxr->bytes = 0; rxr->packets = 0; return; } /********************************************************************* * ixgbe_msix_que - MSI-X Queue Interrupt Service routine **********************************************************************/ static int ixgbe_msix_que(void *arg) { struct ix_rx_queue *que = arg; struct ixgbe_softc *sc = que->sc; struct ifnet *ifp = iflib_get_ifp(que->sc->ctx); /* Protect against spurious interrupts */ if ((ifp->if_drv_flags & IFF_DRV_RUNNING) == 0) return (FILTER_HANDLED); ixgbe_disable_queue(sc, que->msix); ++que->irqs; /* Check for AIM */ if (sc->enable_aim) { ixgbe_perform_aim(sc, que); } return (FILTER_SCHEDULE_THREAD); } /* ixgbe_msix_que */ /************************************************************************ * ixgbe_media_status - Media Ioctl callback * * Called whenever the user queries the status of * the interface using ifconfig. ************************************************************************/ static void ixgbe_if_media_status(if_ctx_t ctx, struct ifmediareq * ifmr) { struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ixgbe_hw *hw = &sc->hw; int layer; INIT_DEBUGOUT("ixgbe_if_media_status: begin"); ifmr->ifm_status = IFM_AVALID; ifmr->ifm_active = IFM_ETHER; if (!sc->link_active) return; ifmr->ifm_status |= IFM_ACTIVE; layer = sc->phy_layer; if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_T || layer & IXGBE_PHYSICAL_LAYER_1000BASE_T || layer & IXGBE_PHYSICAL_LAYER_100BASE_TX || layer & IXGBE_PHYSICAL_LAYER_10BASE_T) switch (sc->link_speed) { case IXGBE_LINK_SPEED_10GB_FULL: ifmr->ifm_active |= IFM_10G_T | IFM_FDX; break; case IXGBE_LINK_SPEED_1GB_FULL: ifmr->ifm_active |= IFM_1000_T | IFM_FDX; break; case IXGBE_LINK_SPEED_100_FULL: ifmr->ifm_active |= IFM_100_TX | IFM_FDX; break; case IXGBE_LINK_SPEED_10_FULL: ifmr->ifm_active |= IFM_10_T | IFM_FDX; break; } if (hw->mac.type == ixgbe_mac_X550) switch (sc->link_speed) { case IXGBE_LINK_SPEED_5GB_FULL: ifmr->ifm_active |= IFM_5000_T | IFM_FDX; break; case IXGBE_LINK_SPEED_2_5GB_FULL: ifmr->ifm_active |= IFM_2500_T | IFM_FDX; break; } if (layer & IXGBE_PHYSICAL_LAYER_SFP_PLUS_CU || layer & IXGBE_PHYSICAL_LAYER_SFP_ACTIVE_DA) switch (sc->link_speed) { case IXGBE_LINK_SPEED_10GB_FULL: ifmr->ifm_active |= IFM_10G_TWINAX | IFM_FDX; break; } if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_LR) switch (sc->link_speed) { case IXGBE_LINK_SPEED_10GB_FULL: ifmr->ifm_active |= IFM_10G_LR | IFM_FDX; break; case IXGBE_LINK_SPEED_1GB_FULL: ifmr->ifm_active |= IFM_1000_LX | IFM_FDX; break; } if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_LRM) switch (sc->link_speed) { case IXGBE_LINK_SPEED_10GB_FULL: ifmr->ifm_active |= IFM_10G_LRM | IFM_FDX; break; case IXGBE_LINK_SPEED_1GB_FULL: ifmr->ifm_active |= IFM_1000_LX | IFM_FDX; break; } if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_SR || layer & IXGBE_PHYSICAL_LAYER_1000BASE_SX) switch (sc->link_speed) { case IXGBE_LINK_SPEED_10GB_FULL: ifmr->ifm_active |= IFM_10G_SR | IFM_FDX; break; case IXGBE_LINK_SPEED_1GB_FULL: ifmr->ifm_active |= IFM_1000_SX | IFM_FDX; break; } if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_CX4) switch (sc->link_speed) { case IXGBE_LINK_SPEED_10GB_FULL: ifmr->ifm_active |= IFM_10G_CX4 | IFM_FDX; break; } /* * XXX: These need to use the proper media types once * they're added. */ #ifndef IFM_ETH_XTYPE if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_KR) switch (sc->link_speed) { case IXGBE_LINK_SPEED_10GB_FULL: ifmr->ifm_active |= IFM_10G_SR | IFM_FDX; break; case IXGBE_LINK_SPEED_2_5GB_FULL: ifmr->ifm_active |= IFM_2500_SX | IFM_FDX; break; case IXGBE_LINK_SPEED_1GB_FULL: ifmr->ifm_active |= IFM_1000_CX | IFM_FDX; break; } else if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_KX4 || layer & IXGBE_PHYSICAL_LAYER_2500BASE_KX || layer & IXGBE_PHYSICAL_LAYER_1000BASE_KX) switch (sc->link_speed) { case IXGBE_LINK_SPEED_10GB_FULL: ifmr->ifm_active |= IFM_10G_CX4 | IFM_FDX; break; case IXGBE_LINK_SPEED_2_5GB_FULL: ifmr->ifm_active |= IFM_2500_SX | IFM_FDX; break; case IXGBE_LINK_SPEED_1GB_FULL: ifmr->ifm_active |= IFM_1000_CX | IFM_FDX; break; } #else if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_KR) switch (sc->link_speed) { case IXGBE_LINK_SPEED_10GB_FULL: ifmr->ifm_active |= IFM_10G_KR | IFM_FDX; break; case IXGBE_LINK_SPEED_2_5GB_FULL: ifmr->ifm_active |= IFM_2500_KX | IFM_FDX; break; case IXGBE_LINK_SPEED_1GB_FULL: ifmr->ifm_active |= IFM_1000_KX | IFM_FDX; break; } else if (layer & IXGBE_PHYSICAL_LAYER_10GBASE_KX4 || layer & IXGBE_PHYSICAL_LAYER_2500BASE_KX || layer & IXGBE_PHYSICAL_LAYER_1000BASE_KX) switch (sc->link_speed) { case IXGBE_LINK_SPEED_10GB_FULL: ifmr->ifm_active |= IFM_10G_KX4 | IFM_FDX; break; case IXGBE_LINK_SPEED_2_5GB_FULL: ifmr->ifm_active |= IFM_2500_KX | IFM_FDX; break; case IXGBE_LINK_SPEED_1GB_FULL: ifmr->ifm_active |= IFM_1000_KX | IFM_FDX; break; } #endif /* If nothing is recognized... */ if (IFM_SUBTYPE(ifmr->ifm_active) == 0) ifmr->ifm_active |= IFM_UNKNOWN; /* Display current flow control setting used on link */ if (hw->fc.current_mode == ixgbe_fc_rx_pause || hw->fc.current_mode == ixgbe_fc_full) ifmr->ifm_active |= IFM_ETH_RXPAUSE; if (hw->fc.current_mode == ixgbe_fc_tx_pause || hw->fc.current_mode == ixgbe_fc_full) ifmr->ifm_active |= IFM_ETH_TXPAUSE; } /* ixgbe_media_status */ /************************************************************************ * ixgbe_media_change - Media Ioctl callback * * Called when the user changes speed/duplex using * media/mediopt option with ifconfig. ************************************************************************/ static int ixgbe_if_media_change(if_ctx_t ctx) { struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ifmedia *ifm = iflib_get_media(ctx); struct ixgbe_hw *hw = &sc->hw; ixgbe_link_speed speed = 0; INIT_DEBUGOUT("ixgbe_if_media_change: begin"); if (IFM_TYPE(ifm->ifm_media) != IFM_ETHER) return (EINVAL); if (hw->phy.media_type == ixgbe_media_type_backplane) return (EPERM); /* * We don't actually need to check against the supported * media types of the adapter; ifmedia will take care of * that for us. */ switch (IFM_SUBTYPE(ifm->ifm_media)) { case IFM_AUTO: case IFM_10G_T: speed |= IXGBE_LINK_SPEED_100_FULL; speed |= IXGBE_LINK_SPEED_1GB_FULL; speed |= IXGBE_LINK_SPEED_10GB_FULL; break; case IFM_10G_LRM: case IFM_10G_LR: #ifndef IFM_ETH_XTYPE case IFM_10G_SR: /* KR, too */ case IFM_10G_CX4: /* KX4 */ #else case IFM_10G_KR: case IFM_10G_KX4: #endif speed |= IXGBE_LINK_SPEED_1GB_FULL; speed |= IXGBE_LINK_SPEED_10GB_FULL; break; #ifndef IFM_ETH_XTYPE case IFM_1000_CX: /* KX */ #else case IFM_1000_KX: #endif case IFM_1000_LX: case IFM_1000_SX: speed |= IXGBE_LINK_SPEED_1GB_FULL; break; case IFM_1000_T: speed |= IXGBE_LINK_SPEED_100_FULL; speed |= IXGBE_LINK_SPEED_1GB_FULL; break; case IFM_10G_TWINAX: speed |= IXGBE_LINK_SPEED_10GB_FULL; break; case IFM_5000_T: speed |= IXGBE_LINK_SPEED_5GB_FULL; break; case IFM_2500_T: speed |= IXGBE_LINK_SPEED_2_5GB_FULL; break; case IFM_100_TX: speed |= IXGBE_LINK_SPEED_100_FULL; break; case IFM_10_T: speed |= IXGBE_LINK_SPEED_10_FULL; break; default: goto invalid; } hw->mac.autotry_restart = true; hw->mac.ops.setup_link(hw, speed, true); sc->advertise = ((speed & IXGBE_LINK_SPEED_10GB_FULL) ? 0x4 : 0) | ((speed & IXGBE_LINK_SPEED_5GB_FULL) ? 0x20 : 0) | ((speed & IXGBE_LINK_SPEED_2_5GB_FULL) ? 0x10 : 0) | ((speed & IXGBE_LINK_SPEED_1GB_FULL) ? 0x2 : 0) | ((speed & IXGBE_LINK_SPEED_100_FULL) ? 0x1 : 0) | ((speed & IXGBE_LINK_SPEED_10_FULL) ? 0x8 : 0); return (0); invalid: device_printf(iflib_get_dev(ctx), "Invalid media type!\n"); return (EINVAL); } /* ixgbe_if_media_change */ /************************************************************************ * ixgbe_set_promisc ************************************************************************/ static int ixgbe_if_promisc_set(if_ctx_t ctx, int flags) { struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ifnet *ifp = iflib_get_ifp(ctx); u32 rctl; int mcnt = 0; rctl = IXGBE_READ_REG(&sc->hw, IXGBE_FCTRL); rctl &= (~IXGBE_FCTRL_UPE); if (ifp->if_flags & IFF_ALLMULTI) mcnt = MAX_NUM_MULTICAST_ADDRESSES; else { mcnt = min(if_llmaddr_count(ifp), MAX_NUM_MULTICAST_ADDRESSES); } if (mcnt < MAX_NUM_MULTICAST_ADDRESSES) rctl &= (~IXGBE_FCTRL_MPE); IXGBE_WRITE_REG(&sc->hw, IXGBE_FCTRL, rctl); if (ifp->if_flags & IFF_PROMISC) { rctl |= (IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE); IXGBE_WRITE_REG(&sc->hw, IXGBE_FCTRL, rctl); } else if (ifp->if_flags & IFF_ALLMULTI) { rctl |= IXGBE_FCTRL_MPE; rctl &= ~IXGBE_FCTRL_UPE; IXGBE_WRITE_REG(&sc->hw, IXGBE_FCTRL, rctl); } return (0); } /* ixgbe_if_promisc_set */ /************************************************************************ * ixgbe_msix_link - Link status change ISR (MSI/MSI-X) ************************************************************************/ static int ixgbe_msix_link(void *arg) { struct ixgbe_softc *sc = arg; struct ixgbe_hw *hw = &sc->hw; u32 eicr, eicr_mask; s32 retval; ++sc->link_irq; /* Pause other interrupts */ IXGBE_WRITE_REG(hw, IXGBE_EIMC, IXGBE_EIMC_OTHER); /* First get the cause */ eicr = IXGBE_READ_REG(hw, IXGBE_EICS); /* Be sure the queue bits are not cleared */ eicr &= ~IXGBE_EICR_RTX_QUEUE; /* Clear interrupt with write */ IXGBE_WRITE_REG(hw, IXGBE_EICR, eicr); /* Link status change */ if (eicr & IXGBE_EICR_LSC) { IXGBE_WRITE_REG(hw, IXGBE_EIMC, IXGBE_EIMC_LSC); sc->task_requests |= IXGBE_REQUEST_TASK_LSC; } if (sc->hw.mac.type != ixgbe_mac_82598EB) { if ((sc->feat_en & IXGBE_FEATURE_FDIR) && (eicr & IXGBE_EICR_FLOW_DIR)) { /* This is probably overkill :) */ if (!atomic_cmpset_int(&sc->fdir_reinit, 0, 1)) return (FILTER_HANDLED); /* Disable the interrupt */ IXGBE_WRITE_REG(hw, IXGBE_EIMC, IXGBE_EICR_FLOW_DIR); sc->task_requests |= IXGBE_REQUEST_TASK_FDIR; } else if (eicr & IXGBE_EICR_ECC) { device_printf(iflib_get_dev(sc->ctx), "Received ECC Err, initiating reset\n"); hw->mac.flags |= ~IXGBE_FLAGS_DOUBLE_RESET_REQUIRED; ixgbe_reset_hw(hw); IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_ECC); } /* Check for over temp condition */ if (sc->feat_en & IXGBE_FEATURE_TEMP_SENSOR) { switch (sc->hw.mac.type) { case ixgbe_mac_X550EM_a: if (!(eicr & IXGBE_EICR_GPI_SDP0_X550EM_a)) break; IXGBE_WRITE_REG(hw, IXGBE_EIMC, IXGBE_EICR_GPI_SDP0_X550EM_a); IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_GPI_SDP0_X550EM_a); retval = hw->phy.ops.check_overtemp(hw); if (retval != IXGBE_ERR_OVERTEMP) break; device_printf(iflib_get_dev(sc->ctx), "\nCRITICAL: OVER TEMP!! PHY IS SHUT DOWN!!\n"); device_printf(iflib_get_dev(sc->ctx), "System shutdown required!\n"); break; default: if (!(eicr & IXGBE_EICR_TS)) break; retval = hw->phy.ops.check_overtemp(hw); if (retval != IXGBE_ERR_OVERTEMP) break; device_printf(iflib_get_dev(sc->ctx), "\nCRITICAL: OVER TEMP!! PHY IS SHUT DOWN!!\n"); device_printf(iflib_get_dev(sc->ctx), "System shutdown required!\n"); IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_TS); break; } } /* Check for VF message */ if ((sc->feat_en & IXGBE_FEATURE_SRIOV) && (eicr & IXGBE_EICR_MAILBOX)) sc->task_requests |= IXGBE_REQUEST_TASK_MBX; } if (ixgbe_is_sfp(hw)) { /* Pluggable optics-related interrupt */ if (hw->mac.type >= ixgbe_mac_X540) eicr_mask = IXGBE_EICR_GPI_SDP0_X540; else eicr_mask = IXGBE_EICR_GPI_SDP2_BY_MAC(hw); if (eicr & eicr_mask) { IXGBE_WRITE_REG(hw, IXGBE_EICR, eicr_mask); sc->task_requests |= IXGBE_REQUEST_TASK_MOD; } if ((hw->mac.type == ixgbe_mac_82599EB) && (eicr & IXGBE_EICR_GPI_SDP1_BY_MAC(hw))) { IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_GPI_SDP1_BY_MAC(hw)); sc->task_requests |= IXGBE_REQUEST_TASK_MSF; } } /* Check for fan failure */ if (sc->feat_en & IXGBE_FEATURE_FAN_FAIL) { ixgbe_check_fan_failure(sc, eicr, true); IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_GPI_SDP1_BY_MAC(hw)); } /* External PHY interrupt */ if ((hw->phy.type == ixgbe_phy_x550em_ext_t) && (eicr & IXGBE_EICR_GPI_SDP0_X540)) { IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_GPI_SDP0_X540); sc->task_requests |= IXGBE_REQUEST_TASK_PHY; } return (sc->task_requests != 0) ? FILTER_SCHEDULE_THREAD : FILTER_HANDLED; } /* ixgbe_msix_link */ /************************************************************************ * ixgbe_sysctl_interrupt_rate_handler ************************************************************************/ static int ixgbe_sysctl_interrupt_rate_handler(SYSCTL_HANDLER_ARGS) { struct ix_rx_queue *que = ((struct ix_rx_queue *)oidp->oid_arg1); int error; unsigned int reg, usec, rate; reg = IXGBE_READ_REG(&que->sc->hw, IXGBE_EITR(que->msix)); usec = ((reg & 0x0FF8) >> 3); if (usec > 0) rate = 500000 / usec; else rate = 0; error = sysctl_handle_int(oidp, &rate, 0, req); if (error || !req->newptr) return error; reg &= ~0xfff; /* default, no limitation */ ixgbe_max_interrupt_rate = 0; if (rate > 0 && rate < 500000) { if (rate < 1000) rate = 1000; ixgbe_max_interrupt_rate = rate; reg |= ((4000000/rate) & 0xff8); } IXGBE_WRITE_REG(&que->sc->hw, IXGBE_EITR(que->msix), reg); return (0); } /* ixgbe_sysctl_interrupt_rate_handler */ /************************************************************************ * ixgbe_add_device_sysctls ************************************************************************/ static void ixgbe_add_device_sysctls(if_ctx_t ctx) { struct ixgbe_softc *sc = iflib_get_softc(ctx); device_t dev = iflib_get_dev(ctx); struct ixgbe_hw *hw = &sc->hw; struct sysctl_oid_list *child; struct sysctl_ctx_list *ctx_list; ctx_list = device_get_sysctl_ctx(dev); child = SYSCTL_CHILDREN(device_get_sysctl_tree(dev)); /* Sysctls for all devices */ SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "fc", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ixgbe_sysctl_flowcntl, "I", IXGBE_SYSCTL_DESC_SET_FC); SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "advertise_speed", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ixgbe_sysctl_advertise, "I", IXGBE_SYSCTL_DESC_ADV_SPEED); sc->enable_aim = ixgbe_enable_aim; SYSCTL_ADD_INT(ctx_list, child, OID_AUTO, "enable_aim", CTLFLAG_RW, &sc->enable_aim, 0, "Interrupt Moderation"); SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "fw_version", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0, ixgbe_sysctl_print_fw_version, "A", "Prints FW/NVM Versions"); #ifdef IXGBE_DEBUG /* testing sysctls (for all devices) */ SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "power_state", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ixgbe_sysctl_power_state, "I", "PCI Power State"); SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "print_rss_config", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0, ixgbe_sysctl_print_rss_config, "A", "Prints RSS Configuration"); #endif /* for X550 series devices */ if (hw->mac.type >= ixgbe_mac_X550) SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "dmac", CTLTYPE_U16 | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ixgbe_sysctl_dmac, "I", "DMA Coalesce"); /* for WoL-capable devices */ if (hw->device_id == IXGBE_DEV_ID_X550EM_X_10G_T) { SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "wol_enable", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ixgbe_sysctl_wol_enable, "I", "Enable/Disable Wake on LAN"); SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "wufc", CTLTYPE_U32 | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ixgbe_sysctl_wufc, "I", "Enable/Disable Wake Up Filters"); } /* for X552/X557-AT devices */ if (hw->device_id == IXGBE_DEV_ID_X550EM_X_10G_T) { struct sysctl_oid *phy_node; struct sysctl_oid_list *phy_list; phy_node = SYSCTL_ADD_NODE(ctx_list, child, OID_AUTO, "phy", CTLFLAG_RD | CTLFLAG_MPSAFE, NULL, "External PHY sysctls"); phy_list = SYSCTL_CHILDREN(phy_node); SYSCTL_ADD_PROC(ctx_list, phy_list, OID_AUTO, "temp", CTLTYPE_U16 | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0, ixgbe_sysctl_phy_temp, "I", "Current External PHY Temperature (Celsius)"); SYSCTL_ADD_PROC(ctx_list, phy_list, OID_AUTO, "overtemp_occurred", CTLTYPE_U16 | CTLFLAG_RD | CTLFLAG_NEEDGIANT, sc, 0, ixgbe_sysctl_phy_overtemp_occurred, "I", "External PHY High Temperature Event Occurred"); } if (sc->feat_cap & IXGBE_FEATURE_EEE) { SYSCTL_ADD_PROC(ctx_list, child, OID_AUTO, "eee_state", CTLTYPE_INT | CTLFLAG_RW | CTLFLAG_NEEDGIANT, sc, 0, ixgbe_sysctl_eee_state, "I", "EEE Power Save State"); } } /* ixgbe_add_device_sysctls */ /************************************************************************ * ixgbe_allocate_pci_resources ************************************************************************/ static int ixgbe_allocate_pci_resources(if_ctx_t ctx) { struct ixgbe_softc *sc = iflib_get_softc(ctx); device_t dev = iflib_get_dev(ctx); int rid; rid = PCIR_BAR(0); sc->pci_mem = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (!(sc->pci_mem)) { device_printf(dev, "Unable to allocate bus resource: memory\n"); return (ENXIO); } /* Save bus_space values for READ/WRITE_REG macros */ sc->osdep.mem_bus_space_tag = rman_get_bustag(sc->pci_mem); sc->osdep.mem_bus_space_handle = rman_get_bushandle(sc->pci_mem); /* Set hw values for shared code */ sc->hw.hw_addr = (u8 *)&sc->osdep.mem_bus_space_handle; return (0); } /* ixgbe_allocate_pci_resources */ /************************************************************************ * ixgbe_detach - Device removal routine * * Called when the driver is being removed. * Stops the adapter and deallocates all the resources * that were allocated for driver operation. * * return 0 on success, positive on failure ************************************************************************/ static int ixgbe_if_detach(if_ctx_t ctx) { struct ixgbe_softc *sc = iflib_get_softc(ctx); device_t dev = iflib_get_dev(ctx); u32 ctrl_ext; INIT_DEBUGOUT("ixgbe_detach: begin"); if (ixgbe_pci_iov_detach(dev) != 0) { device_printf(dev, "SR-IOV in use; detach first.\n"); return (EBUSY); } ixgbe_setup_low_power_mode(ctx); /* let hardware know driver is unloading */ ctrl_ext = IXGBE_READ_REG(&sc->hw, IXGBE_CTRL_EXT); ctrl_ext &= ~IXGBE_CTRL_EXT_DRV_LOAD; IXGBE_WRITE_REG(&sc->hw, IXGBE_CTRL_EXT, ctrl_ext); ixgbe_free_pci_resources(ctx); free(sc->mta, M_IXGBE); return (0); } /* ixgbe_if_detach */ /************************************************************************ * ixgbe_setup_low_power_mode - LPLU/WoL preparation * * Prepare the adapter/port for LPLU and/or WoL ************************************************************************/ static int ixgbe_setup_low_power_mode(if_ctx_t ctx) { struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ixgbe_hw *hw = &sc->hw; device_t dev = iflib_get_dev(ctx); s32 error = 0; if (!hw->wol_enabled) ixgbe_set_phy_power(hw, false); /* Limit power management flow to X550EM baseT */ if (hw->device_id == IXGBE_DEV_ID_X550EM_X_10G_T && hw->phy.ops.enter_lplu) { /* Turn off support for APM wakeup. (Using ACPI instead) */ IXGBE_WRITE_REG(hw, IXGBE_GRC, IXGBE_READ_REG(hw, IXGBE_GRC) & ~(u32)2); /* * Clear Wake Up Status register to prevent any previous wakeup * events from waking us up immediately after we suspend. */ IXGBE_WRITE_REG(hw, IXGBE_WUS, 0xffffffff); /* * Program the Wakeup Filter Control register with user filter * settings */ IXGBE_WRITE_REG(hw, IXGBE_WUFC, sc->wufc); /* Enable wakeups and power management in Wakeup Control */ IXGBE_WRITE_REG(hw, IXGBE_WUC, IXGBE_WUC_WKEN | IXGBE_WUC_PME_EN); /* X550EM baseT adapters need a special LPLU flow */ hw->phy.reset_disable = true; ixgbe_if_stop(ctx); error = hw->phy.ops.enter_lplu(hw); if (error) device_printf(dev, "Error entering LPLU: %d\n", error); hw->phy.reset_disable = false; } else { /* Just stop for other adapters */ ixgbe_if_stop(ctx); } return error; } /* ixgbe_setup_low_power_mode */ /************************************************************************ * ixgbe_shutdown - Shutdown entry point ************************************************************************/ static int ixgbe_if_shutdown(if_ctx_t ctx) { int error = 0; INIT_DEBUGOUT("ixgbe_shutdown: begin"); error = ixgbe_setup_low_power_mode(ctx); return (error); } /* ixgbe_if_shutdown */ /************************************************************************ * ixgbe_suspend * * From D0 to D3 ************************************************************************/ static int ixgbe_if_suspend(if_ctx_t ctx) { int error = 0; INIT_DEBUGOUT("ixgbe_suspend: begin"); error = ixgbe_setup_low_power_mode(ctx); return (error); } /* ixgbe_if_suspend */ /************************************************************************ * ixgbe_resume * * From D3 to D0 ************************************************************************/ static int ixgbe_if_resume(if_ctx_t ctx) { struct ixgbe_softc *sc = iflib_get_softc(ctx); device_t dev = iflib_get_dev(ctx); struct ifnet *ifp = iflib_get_ifp(ctx); struct ixgbe_hw *hw = &sc->hw; u32 wus; INIT_DEBUGOUT("ixgbe_resume: begin"); /* Read & clear WUS register */ wus = IXGBE_READ_REG(hw, IXGBE_WUS); if (wus) device_printf(dev, "Woken up by (WUS): %#010x\n", IXGBE_READ_REG(hw, IXGBE_WUS)); IXGBE_WRITE_REG(hw, IXGBE_WUS, 0xffffffff); /* And clear WUFC until next low-power transition */ IXGBE_WRITE_REG(hw, IXGBE_WUFC, 0); /* * Required after D3->D0 transition; * will re-advertise all previous advertised speeds */ if (ifp->if_flags & IFF_UP) ixgbe_if_init(ctx); return (0); } /* ixgbe_if_resume */ /************************************************************************ * ixgbe_if_mtu_set - Ioctl mtu entry point * * Return 0 on success, EINVAL on failure ************************************************************************/ static int ixgbe_if_mtu_set(if_ctx_t ctx, uint32_t mtu) { struct ixgbe_softc *sc = iflib_get_softc(ctx); int error = 0; IOCTL_DEBUGOUT("ioctl: SIOCIFMTU (Set Interface MTU)"); if (mtu > IXGBE_MAX_MTU) { error = EINVAL; } else { sc->max_frame_size = mtu + IXGBE_MTU_HDR; } return error; } /* ixgbe_if_mtu_set */ /************************************************************************ * ixgbe_if_crcstrip_set ************************************************************************/ static void ixgbe_if_crcstrip_set(if_ctx_t ctx, int onoff, int crcstrip) { struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ixgbe_hw *hw = &sc->hw; /* crc stripping is set in two places: * IXGBE_HLREG0 (modified on init_locked and hw reset) * IXGBE_RDRXCTL (set by the original driver in * ixgbe_setup_hw_rsc() called in init_locked. * We disable the setting when netmap is compiled in). * We update the values here, but also in ixgbe.c because * init_locked sometimes is called outside our control. */ uint32_t hl, rxc; hl = IXGBE_READ_REG(hw, IXGBE_HLREG0); rxc = IXGBE_READ_REG(hw, IXGBE_RDRXCTL); #ifdef NETMAP if (netmap_verbose) D("%s read HLREG 0x%x rxc 0x%x", onoff ? "enter" : "exit", hl, rxc); #endif /* hw requirements ... */ rxc &= ~IXGBE_RDRXCTL_RSCFRSTSIZE; rxc |= IXGBE_RDRXCTL_RSCACKC; if (onoff && !crcstrip) { /* keep the crc. Fast rx */ hl &= ~IXGBE_HLREG0_RXCRCSTRP; rxc &= ~IXGBE_RDRXCTL_CRCSTRIP; } else { /* reset default mode */ hl |= IXGBE_HLREG0_RXCRCSTRP; rxc |= IXGBE_RDRXCTL_CRCSTRIP; } #ifdef NETMAP if (netmap_verbose) D("%s write HLREG 0x%x rxc 0x%x", onoff ? "enter" : "exit", hl, rxc); #endif IXGBE_WRITE_REG(hw, IXGBE_HLREG0, hl); IXGBE_WRITE_REG(hw, IXGBE_RDRXCTL, rxc); } /* ixgbe_if_crcstrip_set */ /********************************************************************* * ixgbe_if_init - Init entry point * * Used in two ways: It is used by the stack as an init * entry point in network interface structure. It is also * used by the driver as a hw/sw initialization routine to * get to a consistent state. * * Return 0 on success, positive on failure **********************************************************************/ void ixgbe_if_init(if_ctx_t ctx) { struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ifnet *ifp = iflib_get_ifp(ctx); device_t dev = iflib_get_dev(ctx); struct ixgbe_hw *hw = &sc->hw; struct ix_rx_queue *rx_que; struct ix_tx_queue *tx_que; u32 txdctl, mhadd; u32 rxdctl, rxctrl; u32 ctrl_ext; int i, j, err; INIT_DEBUGOUT("ixgbe_if_init: begin"); /* Queue indices may change with IOV mode */ ixgbe_align_all_queue_indices(sc); /* reprogram the RAR[0] in case user changed it. */ ixgbe_set_rar(hw, 0, hw->mac.addr, sc->pool, IXGBE_RAH_AV); /* Get the latest mac address, User can use a LAA */ bcopy(IF_LLADDR(ifp), hw->mac.addr, IXGBE_ETH_LENGTH_OF_ADDRESS); ixgbe_set_rar(hw, 0, hw->mac.addr, sc->pool, 1); hw->addr_ctrl.rar_used_count = 1; ixgbe_init_hw(hw); ixgbe_initialize_iov(sc); ixgbe_initialize_transmit_units(ctx); /* Setup Multicast table */ ixgbe_if_multi_set(ctx); /* Determine the correct mbuf pool, based on frame size */ sc->rx_mbuf_sz = iflib_get_rx_mbuf_sz(ctx); /* Configure RX settings */ ixgbe_initialize_receive_units(ctx); /* * Initialize variable holding task enqueue requests * from MSI-X interrupts */ sc->task_requests = 0; /* Enable SDP & MSI-X interrupts based on adapter */ ixgbe_config_gpie(sc); /* Set MTU size */ if (ifp->if_mtu > ETHERMTU) { /* aka IXGBE_MAXFRS on 82599 and newer */ mhadd = IXGBE_READ_REG(hw, IXGBE_MHADD); mhadd &= ~IXGBE_MHADD_MFS_MASK; mhadd |= sc->max_frame_size << IXGBE_MHADD_MFS_SHIFT; IXGBE_WRITE_REG(hw, IXGBE_MHADD, mhadd); } /* Now enable all the queues */ for (i = 0, tx_que = sc->tx_queues; i < sc->num_tx_queues; i++, tx_que++) { struct tx_ring *txr = &tx_que->txr; txdctl = IXGBE_READ_REG(hw, IXGBE_TXDCTL(txr->me)); txdctl |= IXGBE_TXDCTL_ENABLE; /* Set WTHRESH to 8, burst writeback */ txdctl |= (8 << 16); /* * When the internal queue falls below PTHRESH (32), * start prefetching as long as there are at least * HTHRESH (1) buffers ready. The values are taken * from the Intel linux driver 3.8.21. * Prefetching enables tx line rate even with 1 queue. */ txdctl |= (32 << 0) | (1 << 8); IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(txr->me), txdctl); } for (i = 0, rx_que = sc->rx_queues; i < sc->num_rx_queues; i++, rx_que++) { struct rx_ring *rxr = &rx_que->rxr; rxdctl = IXGBE_READ_REG(hw, IXGBE_RXDCTL(rxr->me)); if (hw->mac.type == ixgbe_mac_82598EB) { /* * PTHRESH = 21 * HTHRESH = 4 * WTHRESH = 8 */ rxdctl &= ~0x3FFFFF; rxdctl |= 0x080420; } rxdctl |= IXGBE_RXDCTL_ENABLE; IXGBE_WRITE_REG(hw, IXGBE_RXDCTL(rxr->me), rxdctl); for (j = 0; j < 10; j++) { if (IXGBE_READ_REG(hw, IXGBE_RXDCTL(rxr->me)) & IXGBE_RXDCTL_ENABLE) break; else msec_delay(1); } wmb(); } /* Enable Receive engine */ rxctrl = IXGBE_READ_REG(hw, IXGBE_RXCTRL); if (hw->mac.type == ixgbe_mac_82598EB) rxctrl |= IXGBE_RXCTRL_DMBYPS; rxctrl |= IXGBE_RXCTRL_RXEN; ixgbe_enable_rx_dma(hw, rxctrl); /* Set up MSI/MSI-X routing */ if (ixgbe_enable_msix) { ixgbe_configure_ivars(sc); /* Set up auto-mask */ if (hw->mac.type == ixgbe_mac_82598EB) IXGBE_WRITE_REG(hw, IXGBE_EIAM, IXGBE_EICS_RTX_QUEUE); else { IXGBE_WRITE_REG(hw, IXGBE_EIAM_EX(0), 0xFFFFFFFF); IXGBE_WRITE_REG(hw, IXGBE_EIAM_EX(1), 0xFFFFFFFF); } } else { /* Simple settings for Legacy/MSI */ ixgbe_set_ivar(sc, 0, 0, 0); ixgbe_set_ivar(sc, 0, 0, 1); IXGBE_WRITE_REG(hw, IXGBE_EIAM, IXGBE_EICS_RTX_QUEUE); } ixgbe_init_fdir(sc); /* * Check on any SFP devices that * need to be kick-started */ if (hw->phy.type == ixgbe_phy_none) { err = hw->phy.ops.identify(hw); if (err == IXGBE_ERR_SFP_NOT_SUPPORTED) { device_printf(dev, "Unsupported SFP+ module type was detected.\n"); return; } } /* Set moderation on the Link interrupt */ IXGBE_WRITE_REG(hw, IXGBE_EITR(sc->vector), IXGBE_LINK_ITR); /* Enable power to the phy. */ ixgbe_set_phy_power(hw, true); /* Config/Enable Link */ ixgbe_config_link(ctx); /* Hardware Packet Buffer & Flow Control setup */ ixgbe_config_delay_values(sc); /* Initialize the FC settings */ ixgbe_start_hw(hw); /* Set up VLAN support and filter */ ixgbe_setup_vlan_hw_support(ctx); /* Setup DMA Coalescing */ ixgbe_config_dmac(sc); /* And now turn on interrupts */ ixgbe_if_enable_intr(ctx); /* Enable the use of the MBX by the VF's */ if (sc->feat_en & IXGBE_FEATURE_SRIOV) { ctrl_ext = IXGBE_READ_REG(hw, IXGBE_CTRL_EXT); ctrl_ext |= IXGBE_CTRL_EXT_PFRSTD; IXGBE_WRITE_REG(hw, IXGBE_CTRL_EXT, ctrl_ext); } } /* ixgbe_init_locked */ /************************************************************************ * ixgbe_set_ivar * * Setup the correct IVAR register for a particular MSI-X interrupt * (yes this is all very magic and confusing :) * - entry is the register array entry * - vector is the MSI-X vector for this queue * - type is RX/TX/MISC ************************************************************************/ static void ixgbe_set_ivar(struct ixgbe_softc *sc, u8 entry, u8 vector, s8 type) { struct ixgbe_hw *hw = &sc->hw; u32 ivar, index; vector |= IXGBE_IVAR_ALLOC_VAL; switch (hw->mac.type) { case ixgbe_mac_82598EB: if (type == -1) entry = IXGBE_IVAR_OTHER_CAUSES_INDEX; else entry += (type * 64); index = (entry >> 2) & 0x1F; ivar = IXGBE_READ_REG(hw, IXGBE_IVAR(index)); ivar &= ~(0xFF << (8 * (entry & 0x3))); ivar |= (vector << (8 * (entry & 0x3))); IXGBE_WRITE_REG(&sc->hw, IXGBE_IVAR(index), ivar); break; case ixgbe_mac_82599EB: case ixgbe_mac_X540: case ixgbe_mac_X550: case ixgbe_mac_X550EM_x: case ixgbe_mac_X550EM_a: if (type == -1) { /* MISC IVAR */ index = (entry & 1) * 8; ivar = IXGBE_READ_REG(hw, IXGBE_IVAR_MISC); ivar &= ~(0xFF << index); ivar |= (vector << index); IXGBE_WRITE_REG(hw, IXGBE_IVAR_MISC, ivar); } else { /* RX/TX IVARS */ index = (16 * (entry & 1)) + (8 * type); ivar = IXGBE_READ_REG(hw, IXGBE_IVAR(entry >> 1)); ivar &= ~(0xFF << index); ivar |= (vector << index); IXGBE_WRITE_REG(hw, IXGBE_IVAR(entry >> 1), ivar); } default: break; } } /* ixgbe_set_ivar */ /************************************************************************ * ixgbe_configure_ivars ************************************************************************/ static void ixgbe_configure_ivars(struct ixgbe_softc *sc) { struct ix_rx_queue *rx_que = sc->rx_queues; struct ix_tx_queue *tx_que = sc->tx_queues; u32 newitr; if (ixgbe_max_interrupt_rate > 0) newitr = (4000000 / ixgbe_max_interrupt_rate) & 0x0FF8; else { /* * Disable DMA coalescing if interrupt moderation is * disabled. */ sc->dmac = 0; newitr = 0; } for (int i = 0; i < sc->num_rx_queues; i++, rx_que++) { struct rx_ring *rxr = &rx_que->rxr; /* First the RX queue entry */ ixgbe_set_ivar(sc, rxr->me, rx_que->msix, 0); /* Set an Initial EITR value */ IXGBE_WRITE_REG(&sc->hw, IXGBE_EITR(rx_que->msix), newitr); } for (int i = 0; i < sc->num_tx_queues; i++, tx_que++) { struct tx_ring *txr = &tx_que->txr; /* ... and the TX */ ixgbe_set_ivar(sc, txr->me, tx_que->msix, 1); } /* For the Link interrupt */ ixgbe_set_ivar(sc, 1, sc->vector, -1); } /* ixgbe_configure_ivars */ /************************************************************************ * ixgbe_config_gpie ************************************************************************/ static void ixgbe_config_gpie(struct ixgbe_softc *sc) { struct ixgbe_hw *hw = &sc->hw; u32 gpie; gpie = IXGBE_READ_REG(hw, IXGBE_GPIE); if (sc->intr_type == IFLIB_INTR_MSIX) { /* Enable Enhanced MSI-X mode */ gpie |= IXGBE_GPIE_MSIX_MODE | IXGBE_GPIE_EIAME | IXGBE_GPIE_PBA_SUPPORT | IXGBE_GPIE_OCD; } /* Fan Failure Interrupt */ if (sc->feat_en & IXGBE_FEATURE_FAN_FAIL) gpie |= IXGBE_SDP1_GPIEN; /* Thermal Sensor Interrupt */ if (sc->feat_en & IXGBE_FEATURE_TEMP_SENSOR) gpie |= IXGBE_SDP0_GPIEN_X540; /* Link detection */ switch (hw->mac.type) { case ixgbe_mac_82599EB: gpie |= IXGBE_SDP1_GPIEN | IXGBE_SDP2_GPIEN; break; case ixgbe_mac_X550EM_x: case ixgbe_mac_X550EM_a: gpie |= IXGBE_SDP0_GPIEN_X540; break; default: break; } IXGBE_WRITE_REG(hw, IXGBE_GPIE, gpie); } /* ixgbe_config_gpie */ /************************************************************************ * ixgbe_config_delay_values * * Requires sc->max_frame_size to be set. ************************************************************************/ static void ixgbe_config_delay_values(struct ixgbe_softc *sc) { struct ixgbe_hw *hw = &sc->hw; u32 rxpb, frame, size, tmp; frame = sc->max_frame_size; /* Calculate High Water */ switch (hw->mac.type) { case ixgbe_mac_X540: case ixgbe_mac_X550: case ixgbe_mac_X550EM_x: case ixgbe_mac_X550EM_a: tmp = IXGBE_DV_X540(frame, frame); break; default: tmp = IXGBE_DV(frame, frame); break; } size = IXGBE_BT2KB(tmp); rxpb = IXGBE_READ_REG(hw, IXGBE_RXPBSIZE(0)) >> 10; hw->fc.high_water[0] = rxpb - size; /* Now calculate Low Water */ switch (hw->mac.type) { case ixgbe_mac_X540: case ixgbe_mac_X550: case ixgbe_mac_X550EM_x: case ixgbe_mac_X550EM_a: tmp = IXGBE_LOW_DV_X540(frame); break; default: tmp = IXGBE_LOW_DV(frame); break; } hw->fc.low_water[0] = IXGBE_BT2KB(tmp); hw->fc.pause_time = IXGBE_FC_PAUSE; hw->fc.send_xon = true; } /* ixgbe_config_delay_values */ /************************************************************************ * ixgbe_set_multi - Multicast Update * * Called whenever multicast address list is updated. ************************************************************************/ static u_int ixgbe_mc_filter_apply(void *arg, struct sockaddr_dl *sdl, u_int idx) { struct ixgbe_softc *sc = arg; struct ixgbe_mc_addr *mta = sc->mta; if (idx == MAX_NUM_MULTICAST_ADDRESSES) return (0); bcopy(LLADDR(sdl), mta[idx].addr, IXGBE_ETH_LENGTH_OF_ADDRESS); mta[idx].vmdq = sc->pool; return (1); } /* ixgbe_mc_filter_apply */ static void ixgbe_if_multi_set(if_ctx_t ctx) { struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ixgbe_mc_addr *mta; struct ifnet *ifp = iflib_get_ifp(ctx); u8 *update_ptr; u32 fctrl; u_int mcnt; IOCTL_DEBUGOUT("ixgbe_if_multi_set: begin"); mta = sc->mta; bzero(mta, sizeof(*mta) * MAX_NUM_MULTICAST_ADDRESSES); mcnt = if_foreach_llmaddr(iflib_get_ifp(ctx), ixgbe_mc_filter_apply, sc); if (mcnt < MAX_NUM_MULTICAST_ADDRESSES) { update_ptr = (u8 *)mta; ixgbe_update_mc_addr_list(&sc->hw, update_ptr, mcnt, ixgbe_mc_array_itr, true); } fctrl = IXGBE_READ_REG(&sc->hw, IXGBE_FCTRL); if (ifp->if_flags & IFF_PROMISC) fctrl |= (IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE); else if (mcnt >= MAX_NUM_MULTICAST_ADDRESSES || ifp->if_flags & IFF_ALLMULTI) { fctrl |= IXGBE_FCTRL_MPE; fctrl &= ~IXGBE_FCTRL_UPE; } else fctrl &= ~(IXGBE_FCTRL_UPE | IXGBE_FCTRL_MPE); IXGBE_WRITE_REG(&sc->hw, IXGBE_FCTRL, fctrl); } /* ixgbe_if_multi_set */ /************************************************************************ * ixgbe_mc_array_itr * * An iterator function needed by the multicast shared code. * It feeds the shared code routine the addresses in the * array of ixgbe_set_multi() one by one. ************************************************************************/ static u8 * ixgbe_mc_array_itr(struct ixgbe_hw *hw, u8 **update_ptr, u32 *vmdq) { struct ixgbe_mc_addr *mta; mta = (struct ixgbe_mc_addr *)*update_ptr; *vmdq = mta->vmdq; *update_ptr = (u8*)(mta + 1); return (mta->addr); } /* ixgbe_mc_array_itr */ /************************************************************************ * ixgbe_local_timer - Timer routine * * Checks for link status, updates statistics, * and runs the watchdog check. ************************************************************************/ static void ixgbe_if_timer(if_ctx_t ctx, uint16_t qid) { struct ixgbe_softc *sc = iflib_get_softc(ctx); if (qid != 0) return; /* Check for pluggable optics */ if (sc->sfp_probe) if (!ixgbe_sfp_probe(ctx)) return; /* Nothing to do */ ixgbe_check_link(&sc->hw, &sc->link_speed, &sc->link_up, 0); /* Fire off the adminq task */ iflib_admin_intr_deferred(ctx); } /* ixgbe_if_timer */ /************************************************************************ * ixgbe_sfp_probe * * Determine if a port had optics inserted. ************************************************************************/ static bool ixgbe_sfp_probe(if_ctx_t ctx) { struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ixgbe_hw *hw = &sc->hw; device_t dev = iflib_get_dev(ctx); bool result = false; if ((hw->phy.type == ixgbe_phy_nl) && (hw->phy.sfp_type == ixgbe_sfp_type_not_present)) { s32 ret = hw->phy.ops.identify_sfp(hw); if (ret) goto out; ret = hw->phy.ops.reset(hw); sc->sfp_probe = false; if (ret == IXGBE_ERR_SFP_NOT_SUPPORTED) { device_printf(dev, "Unsupported SFP+ module detected!"); device_printf(dev, "Reload driver with supported module.\n"); goto out; } else device_printf(dev, "SFP+ module detected!\n"); /* We now have supported optics */ result = true; } out: return (result); } /* ixgbe_sfp_probe */ /************************************************************************ * ixgbe_handle_mod - Tasklet for SFP module interrupts ************************************************************************/ static void ixgbe_handle_mod(void *context) { if_ctx_t ctx = context; struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ixgbe_hw *hw = &sc->hw; device_t dev = iflib_get_dev(ctx); u32 err, cage_full = 0; if (sc->hw.need_crosstalk_fix) { switch (hw->mac.type) { case ixgbe_mac_82599EB: cage_full = IXGBE_READ_REG(hw, IXGBE_ESDP) & IXGBE_ESDP_SDP2; break; case ixgbe_mac_X550EM_x: case ixgbe_mac_X550EM_a: cage_full = IXGBE_READ_REG(hw, IXGBE_ESDP) & IXGBE_ESDP_SDP0; break; default: break; } if (!cage_full) goto handle_mod_out; } err = hw->phy.ops.identify_sfp(hw); if (err == IXGBE_ERR_SFP_NOT_SUPPORTED) { device_printf(dev, "Unsupported SFP+ module type was detected.\n"); goto handle_mod_out; } if (hw->mac.type == ixgbe_mac_82598EB) err = hw->phy.ops.reset(hw); else err = hw->mac.ops.setup_sfp(hw); if (err == IXGBE_ERR_SFP_NOT_SUPPORTED) { device_printf(dev, "Setup failure - unsupported SFP+ module type.\n"); goto handle_mod_out; } sc->task_requests |= IXGBE_REQUEST_TASK_MSF; return; handle_mod_out: sc->task_requests &= ~(IXGBE_REQUEST_TASK_MSF); } /* ixgbe_handle_mod */ /************************************************************************ * ixgbe_handle_msf - Tasklet for MSF (multispeed fiber) interrupts ************************************************************************/ static void ixgbe_handle_msf(void *context) { if_ctx_t ctx = context; struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ixgbe_hw *hw = &sc->hw; u32 autoneg; bool negotiate; /* get_supported_phy_layer will call hw->phy.ops.identify_sfp() */ sc->phy_layer = ixgbe_get_supported_physical_layer(hw); autoneg = hw->phy.autoneg_advertised; if ((!autoneg) && (hw->mac.ops.get_link_capabilities)) hw->mac.ops.get_link_capabilities(hw, &autoneg, &negotiate); if (hw->mac.ops.setup_link) hw->mac.ops.setup_link(hw, autoneg, true); /* Adjust media types shown in ifconfig */ ifmedia_removeall(sc->media); ixgbe_add_media_types(sc->ctx); ifmedia_set(sc->media, IFM_ETHER | IFM_AUTO); } /* ixgbe_handle_msf */ /************************************************************************ * ixgbe_handle_phy - Tasklet for external PHY interrupts ************************************************************************/ static void ixgbe_handle_phy(void *context) { if_ctx_t ctx = context; struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ixgbe_hw *hw = &sc->hw; int error; error = hw->phy.ops.handle_lasi(hw); if (error == IXGBE_ERR_OVERTEMP) device_printf(sc->dev, "CRITICAL: EXTERNAL PHY OVER TEMP!! PHY will downshift to lower power state!\n"); else if (error) device_printf(sc->dev, "Error handling LASI interrupt: %d\n", error); } /* ixgbe_handle_phy */ /************************************************************************ * ixgbe_if_stop - Stop the hardware * * Disables all traffic on the adapter by issuing a * global reset on the MAC and deallocates TX/RX buffers. ************************************************************************/ static void ixgbe_if_stop(if_ctx_t ctx) { struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ixgbe_hw *hw = &sc->hw; INIT_DEBUGOUT("ixgbe_if_stop: begin\n"); ixgbe_reset_hw(hw); hw->adapter_stopped = false; ixgbe_stop_adapter(hw); if (hw->mac.type == ixgbe_mac_82599EB) ixgbe_stop_mac_link_on_d3_82599(hw); /* Turn off the laser - noop with no optics */ ixgbe_disable_tx_laser(hw); /* Update the stack */ sc->link_up = false; ixgbe_if_update_admin_status(ctx); /* reprogram the RAR[0] in case user changed it. */ ixgbe_set_rar(&sc->hw, 0, sc->hw.mac.addr, 0, IXGBE_RAH_AV); return; } /* ixgbe_if_stop */ /************************************************************************ * ixgbe_update_link_status - Update OS on link state * * Note: Only updates the OS on the cached link state. * The real check of the hardware only happens with * a link interrupt. ************************************************************************/ static void ixgbe_if_update_admin_status(if_ctx_t ctx) { struct ixgbe_softc *sc = iflib_get_softc(ctx); device_t dev = iflib_get_dev(ctx); if (sc->link_up) { if (sc->link_active == false) { if (bootverbose) device_printf(dev, "Link is up %d Gbps %s \n", ((sc->link_speed == 128) ? 10 : 1), "Full Duplex"); sc->link_active = true; /* Update any Flow Control changes */ ixgbe_fc_enable(&sc->hw); /* Update DMA coalescing config */ ixgbe_config_dmac(sc); /* should actually be negotiated value */ iflib_link_state_change(ctx, LINK_STATE_UP, IF_Gbps(10)); if (sc->feat_en & IXGBE_FEATURE_SRIOV) ixgbe_ping_all_vfs(sc); } } else { /* Link down */ if (sc->link_active == true) { if (bootverbose) device_printf(dev, "Link is Down\n"); iflib_link_state_change(ctx, LINK_STATE_DOWN, 0); sc->link_active = false; if (sc->feat_en & IXGBE_FEATURE_SRIOV) ixgbe_ping_all_vfs(sc); } } /* Handle task requests from msix_link() */ if (sc->task_requests & IXGBE_REQUEST_TASK_MOD) ixgbe_handle_mod(ctx); if (sc->task_requests & IXGBE_REQUEST_TASK_MSF) ixgbe_handle_msf(ctx); if (sc->task_requests & IXGBE_REQUEST_TASK_MBX) ixgbe_handle_mbx(ctx); if (sc->task_requests & IXGBE_REQUEST_TASK_FDIR) ixgbe_reinit_fdir(ctx); if (sc->task_requests & IXGBE_REQUEST_TASK_PHY) ixgbe_handle_phy(ctx); sc->task_requests = 0; ixgbe_update_stats_counters(sc); } /* ixgbe_if_update_admin_status */ /************************************************************************ * ixgbe_config_dmac - Configure DMA Coalescing ************************************************************************/ static void ixgbe_config_dmac(struct ixgbe_softc *sc) { struct ixgbe_hw *hw = &sc->hw; struct ixgbe_dmac_config *dcfg = &hw->mac.dmac_config; if (hw->mac.type < ixgbe_mac_X550 || !hw->mac.ops.dmac_config) return; if (dcfg->watchdog_timer ^ sc->dmac || dcfg->link_speed ^ sc->link_speed) { dcfg->watchdog_timer = sc->dmac; dcfg->fcoe_en = false; dcfg->link_speed = sc->link_speed; dcfg->num_tcs = 1; INIT_DEBUGOUT2("dmac settings: watchdog %d, link speed %d\n", dcfg->watchdog_timer, dcfg->link_speed); hw->mac.ops.dmac_config(hw); } } /* ixgbe_config_dmac */ /************************************************************************ * ixgbe_if_enable_intr ************************************************************************/ void ixgbe_if_enable_intr(if_ctx_t ctx) { struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ixgbe_hw *hw = &sc->hw; struct ix_rx_queue *que = sc->rx_queues; u32 mask, fwsm; mask = (IXGBE_EIMS_ENABLE_MASK & ~IXGBE_EIMS_RTX_QUEUE); switch (sc->hw.mac.type) { case ixgbe_mac_82599EB: mask |= IXGBE_EIMS_ECC; /* Temperature sensor on some scs */ mask |= IXGBE_EIMS_GPI_SDP0; /* SFP+ (RX_LOS_N & MOD_ABS_N) */ mask |= IXGBE_EIMS_GPI_SDP1; mask |= IXGBE_EIMS_GPI_SDP2; break; case ixgbe_mac_X540: /* Detect if Thermal Sensor is enabled */ fwsm = IXGBE_READ_REG(hw, IXGBE_FWSM); if (fwsm & IXGBE_FWSM_TS_ENABLED) mask |= IXGBE_EIMS_TS; mask |= IXGBE_EIMS_ECC; break; case ixgbe_mac_X550: /* MAC thermal sensor is automatically enabled */ mask |= IXGBE_EIMS_TS; mask |= IXGBE_EIMS_ECC; break; case ixgbe_mac_X550EM_x: case ixgbe_mac_X550EM_a: /* Some devices use SDP0 for important information */ if (hw->device_id == IXGBE_DEV_ID_X550EM_X_SFP || hw->device_id == IXGBE_DEV_ID_X550EM_A_SFP || hw->device_id == IXGBE_DEV_ID_X550EM_A_SFP_N || hw->device_id == IXGBE_DEV_ID_X550EM_X_10G_T) mask |= IXGBE_EIMS_GPI_SDP0_BY_MAC(hw); if (hw->phy.type == ixgbe_phy_x550em_ext_t) mask |= IXGBE_EICR_GPI_SDP0_X540; mask |= IXGBE_EIMS_ECC; break; default: break; } /* Enable Fan Failure detection */ if (sc->feat_en & IXGBE_FEATURE_FAN_FAIL) mask |= IXGBE_EIMS_GPI_SDP1; /* Enable SR-IOV */ if (sc->feat_en & IXGBE_FEATURE_SRIOV) mask |= IXGBE_EIMS_MAILBOX; /* Enable Flow Director */ if (sc->feat_en & IXGBE_FEATURE_FDIR) mask |= IXGBE_EIMS_FLOW_DIR; IXGBE_WRITE_REG(hw, IXGBE_EIMS, mask); /* With MSI-X we use auto clear */ if (sc->intr_type == IFLIB_INTR_MSIX) { mask = IXGBE_EIMS_ENABLE_MASK; /* Don't autoclear Link */ mask &= ~IXGBE_EIMS_OTHER; mask &= ~IXGBE_EIMS_LSC; if (sc->feat_cap & IXGBE_FEATURE_SRIOV) mask &= ~IXGBE_EIMS_MAILBOX; IXGBE_WRITE_REG(hw, IXGBE_EIAC, mask); } /* * Now enable all queues, this is done separately to * allow for handling the extended (beyond 32) MSI-X * vectors that can be used by 82599 */ for (int i = 0; i < sc->num_rx_queues; i++, que++) ixgbe_enable_queue(sc, que->msix); IXGBE_WRITE_FLUSH(hw); } /* ixgbe_if_enable_intr */ /************************************************************************ * ixgbe_disable_intr ************************************************************************/ static void ixgbe_if_disable_intr(if_ctx_t ctx) { struct ixgbe_softc *sc = iflib_get_softc(ctx); if (sc->intr_type == IFLIB_INTR_MSIX) IXGBE_WRITE_REG(&sc->hw, IXGBE_EIAC, 0); if (sc->hw.mac.type == ixgbe_mac_82598EB) { IXGBE_WRITE_REG(&sc->hw, IXGBE_EIMC, ~0); } else { IXGBE_WRITE_REG(&sc->hw, IXGBE_EIMC, 0xFFFF0000); IXGBE_WRITE_REG(&sc->hw, IXGBE_EIMC_EX(0), ~0); IXGBE_WRITE_REG(&sc->hw, IXGBE_EIMC_EX(1), ~0); } IXGBE_WRITE_FLUSH(&sc->hw); } /* ixgbe_if_disable_intr */ /************************************************************************ * ixgbe_link_intr_enable ************************************************************************/ static void ixgbe_link_intr_enable(if_ctx_t ctx) { struct ixgbe_hw *hw = &((struct ixgbe_softc *)iflib_get_softc(ctx))->hw; /* Re-enable other interrupts */ IXGBE_WRITE_REG(hw, IXGBE_EIMS, IXGBE_EIMS_OTHER | IXGBE_EIMS_LSC); } /* ixgbe_link_intr_enable */ /************************************************************************ * ixgbe_if_rx_queue_intr_enable ************************************************************************/ static int ixgbe_if_rx_queue_intr_enable(if_ctx_t ctx, uint16_t rxqid) { struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ix_rx_queue *que = &sc->rx_queues[rxqid]; ixgbe_enable_queue(sc, que->msix); return (0); } /* ixgbe_if_rx_queue_intr_enable */ /************************************************************************ * ixgbe_enable_queue ************************************************************************/ static void ixgbe_enable_queue(struct ixgbe_softc *sc, u32 vector) { struct ixgbe_hw *hw = &sc->hw; u64 queue = 1ULL << vector; u32 mask; if (hw->mac.type == ixgbe_mac_82598EB) { mask = (IXGBE_EIMS_RTX_QUEUE & queue); IXGBE_WRITE_REG(hw, IXGBE_EIMS, mask); } else { mask = (queue & 0xFFFFFFFF); if (mask) IXGBE_WRITE_REG(hw, IXGBE_EIMS_EX(0), mask); mask = (queue >> 32); if (mask) IXGBE_WRITE_REG(hw, IXGBE_EIMS_EX(1), mask); } } /* ixgbe_enable_queue */ /************************************************************************ * ixgbe_disable_queue ************************************************************************/ static void ixgbe_disable_queue(struct ixgbe_softc *sc, u32 vector) { struct ixgbe_hw *hw = &sc->hw; u64 queue = 1ULL << vector; u32 mask; if (hw->mac.type == ixgbe_mac_82598EB) { mask = (IXGBE_EIMS_RTX_QUEUE & queue); IXGBE_WRITE_REG(hw, IXGBE_EIMC, mask); } else { mask = (queue & 0xFFFFFFFF); if (mask) IXGBE_WRITE_REG(hw, IXGBE_EIMC_EX(0), mask); mask = (queue >> 32); if (mask) IXGBE_WRITE_REG(hw, IXGBE_EIMC_EX(1), mask); } } /* ixgbe_disable_queue */ /************************************************************************ * ixgbe_intr - Legacy Interrupt Service Routine ************************************************************************/ int ixgbe_intr(void *arg) { struct ixgbe_softc *sc = arg; struct ix_rx_queue *que = sc->rx_queues; struct ixgbe_hw *hw = &sc->hw; if_ctx_t ctx = sc->ctx; u32 eicr, eicr_mask; eicr = IXGBE_READ_REG(hw, IXGBE_EICR); ++que->irqs; if (eicr == 0) { ixgbe_if_enable_intr(ctx); return (FILTER_HANDLED); } /* Check for fan failure */ if ((hw->device_id == IXGBE_DEV_ID_82598AT) && (eicr & IXGBE_EICR_GPI_SDP1)) { device_printf(sc->dev, "\nCRITICAL: FAN FAILURE!! REPLACE IMMEDIATELY!!\n"); IXGBE_WRITE_REG(hw, IXGBE_EIMS, IXGBE_EICR_GPI_SDP1_BY_MAC(hw)); } /* Link status change */ if (eicr & IXGBE_EICR_LSC) { IXGBE_WRITE_REG(hw, IXGBE_EIMC, IXGBE_EIMC_LSC); iflib_admin_intr_deferred(ctx); } if (ixgbe_is_sfp(hw)) { /* Pluggable optics-related interrupt */ if (hw->mac.type >= ixgbe_mac_X540) eicr_mask = IXGBE_EICR_GPI_SDP0_X540; else eicr_mask = IXGBE_EICR_GPI_SDP2_BY_MAC(hw); if (eicr & eicr_mask) { IXGBE_WRITE_REG(hw, IXGBE_EICR, eicr_mask); sc->task_requests |= IXGBE_REQUEST_TASK_MOD; } if ((hw->mac.type == ixgbe_mac_82599EB) && (eicr & IXGBE_EICR_GPI_SDP1_BY_MAC(hw))) { IXGBE_WRITE_REG(hw, IXGBE_EICR, IXGBE_EICR_GPI_SDP1_BY_MAC(hw)); sc->task_requests |= IXGBE_REQUEST_TASK_MSF; } } /* External PHY interrupt */ if ((hw->phy.type == ixgbe_phy_x550em_ext_t) && (eicr & IXGBE_EICR_GPI_SDP0_X540)) sc->task_requests |= IXGBE_REQUEST_TASK_PHY; return (FILTER_SCHEDULE_THREAD); } /* ixgbe_intr */ /************************************************************************ * ixgbe_free_pci_resources ************************************************************************/ static void ixgbe_free_pci_resources(if_ctx_t ctx) { struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ix_rx_queue *que = sc->rx_queues; device_t dev = iflib_get_dev(ctx); /* Release all MSI-X queue resources */ if (sc->intr_type == IFLIB_INTR_MSIX) iflib_irq_free(ctx, &sc->irq); if (que != NULL) { for (int i = 0; i < sc->num_rx_queues; i++, que++) { iflib_irq_free(ctx, &que->que_irq); } } if (sc->pci_mem != NULL) bus_release_resource(dev, SYS_RES_MEMORY, rman_get_rid(sc->pci_mem), sc->pci_mem); } /* ixgbe_free_pci_resources */ /************************************************************************ * ixgbe_sysctl_flowcntl * * SYSCTL wrapper around setting Flow Control ************************************************************************/ static int ixgbe_sysctl_flowcntl(SYSCTL_HANDLER_ARGS) { struct ixgbe_softc *sc; int error, fc; sc = (struct ixgbe_softc *)arg1; fc = sc->hw.fc.current_mode; error = sysctl_handle_int(oidp, &fc, 0, req); if ((error) || (req->newptr == NULL)) return (error); /* Don't bother if it's not changed */ if (fc == sc->hw.fc.current_mode) return (0); return ixgbe_set_flowcntl(sc, fc); } /* ixgbe_sysctl_flowcntl */ /************************************************************************ * ixgbe_set_flowcntl - Set flow control * * Flow control values: * 0 - off * 1 - rx pause * 2 - tx pause * 3 - full ************************************************************************/ static int ixgbe_set_flowcntl(struct ixgbe_softc *sc, int fc) { switch (fc) { case ixgbe_fc_rx_pause: case ixgbe_fc_tx_pause: case ixgbe_fc_full: sc->hw.fc.requested_mode = fc; if (sc->num_rx_queues > 1) ixgbe_disable_rx_drop(sc); break; case ixgbe_fc_none: sc->hw.fc.requested_mode = ixgbe_fc_none; if (sc->num_rx_queues > 1) ixgbe_enable_rx_drop(sc); break; default: return (EINVAL); } /* Don't autoneg if forcing a value */ sc->hw.fc.disable_fc_autoneg = true; ixgbe_fc_enable(&sc->hw); return (0); } /* ixgbe_set_flowcntl */ /************************************************************************ * ixgbe_enable_rx_drop * * Enable the hardware to drop packets when the buffer is * full. This is useful with multiqueue, so that no single * queue being full stalls the entire RX engine. We only * enable this when Multiqueue is enabled AND Flow Control * is disabled. ************************************************************************/ static void ixgbe_enable_rx_drop(struct ixgbe_softc *sc) { struct ixgbe_hw *hw = &sc->hw; struct rx_ring *rxr; u32 srrctl; for (int i = 0; i < sc->num_rx_queues; i++) { rxr = &sc->rx_queues[i].rxr; srrctl = IXGBE_READ_REG(hw, IXGBE_SRRCTL(rxr->me)); srrctl |= IXGBE_SRRCTL_DROP_EN; IXGBE_WRITE_REG(hw, IXGBE_SRRCTL(rxr->me), srrctl); } /* enable drop for each vf */ for (int i = 0; i < sc->num_vfs; i++) { IXGBE_WRITE_REG(hw, IXGBE_QDE, (IXGBE_QDE_WRITE | (i << IXGBE_QDE_IDX_SHIFT) | IXGBE_QDE_ENABLE)); } } /* ixgbe_enable_rx_drop */ /************************************************************************ * ixgbe_disable_rx_drop ************************************************************************/ static void ixgbe_disable_rx_drop(struct ixgbe_softc *sc) { struct ixgbe_hw *hw = &sc->hw; struct rx_ring *rxr; u32 srrctl; for (int i = 0; i < sc->num_rx_queues; i++) { rxr = &sc->rx_queues[i].rxr; srrctl = IXGBE_READ_REG(hw, IXGBE_SRRCTL(rxr->me)); srrctl &= ~IXGBE_SRRCTL_DROP_EN; IXGBE_WRITE_REG(hw, IXGBE_SRRCTL(rxr->me), srrctl); } /* disable drop for each vf */ for (int i = 0; i < sc->num_vfs; i++) { IXGBE_WRITE_REG(hw, IXGBE_QDE, (IXGBE_QDE_WRITE | (i << IXGBE_QDE_IDX_SHIFT))); } } /* ixgbe_disable_rx_drop */ /************************************************************************ * ixgbe_sysctl_advertise * * SYSCTL wrapper around setting advertised speed ************************************************************************/ static int ixgbe_sysctl_advertise(SYSCTL_HANDLER_ARGS) { struct ixgbe_softc *sc; int error, advertise; sc = (struct ixgbe_softc *)arg1; advertise = sc->advertise; error = sysctl_handle_int(oidp, &advertise, 0, req); if ((error) || (req->newptr == NULL)) return (error); return ixgbe_set_advertise(sc, advertise); } /* ixgbe_sysctl_advertise */ /************************************************************************ * ixgbe_set_advertise - Control advertised link speed * * Flags: * 0x1 - advertise 100 Mb * 0x2 - advertise 1G * 0x4 - advertise 10G * 0x8 - advertise 10 Mb (yes, Mb) * 0x10 - advertise 2.5G (disabled by default) * 0x20 - advertise 5G (disabled by default) * ************************************************************************/ static int ixgbe_set_advertise(struct ixgbe_softc *sc, int advertise) { device_t dev = iflib_get_dev(sc->ctx); struct ixgbe_hw *hw; ixgbe_link_speed speed = 0; ixgbe_link_speed link_caps = 0; s32 err = IXGBE_NOT_IMPLEMENTED; bool negotiate = false; /* Checks to validate new value */ if (sc->advertise == advertise) /* no change */ return (0); hw = &sc->hw; /* No speed changes for backplane media */ if (hw->phy.media_type == ixgbe_media_type_backplane) return (ENODEV); if (!((hw->phy.media_type == ixgbe_media_type_copper) || (hw->phy.multispeed_fiber))) { device_printf(dev, "Advertised speed can only be set on copper or multispeed fiber media types.\n"); return (EINVAL); } if (advertise < 0x1 || advertise > 0x3F) { device_printf(dev, "Invalid advertised speed; valid modes are 0x1 through 0x3F\n"); return (EINVAL); } if (hw->mac.ops.get_link_capabilities) { err = hw->mac.ops.get_link_capabilities(hw, &link_caps, &negotiate); if (err != IXGBE_SUCCESS) { device_printf(dev, "Unable to determine supported advertise speeds\n"); return (ENODEV); } } /* Set new value and report new advertised mode */ if (advertise & 0x1) { if (!(link_caps & IXGBE_LINK_SPEED_100_FULL)) { device_printf(dev, "Interface does not support 100Mb advertised speed\n"); return (EINVAL); } speed |= IXGBE_LINK_SPEED_100_FULL; } if (advertise & 0x2) { if (!(link_caps & IXGBE_LINK_SPEED_1GB_FULL)) { device_printf(dev, "Interface does not support 1Gb advertised speed\n"); return (EINVAL); } speed |= IXGBE_LINK_SPEED_1GB_FULL; } if (advertise & 0x4) { if (!(link_caps & IXGBE_LINK_SPEED_10GB_FULL)) { device_printf(dev, "Interface does not support 10Gb advertised speed\n"); return (EINVAL); } speed |= IXGBE_LINK_SPEED_10GB_FULL; } if (advertise & 0x8) { if (!(link_caps & IXGBE_LINK_SPEED_10_FULL)) { device_printf(dev, "Interface does not support 10Mb advertised speed\n"); return (EINVAL); } speed |= IXGBE_LINK_SPEED_10_FULL; } if (advertise & 0x10) { if (!(link_caps & IXGBE_LINK_SPEED_2_5GB_FULL)) { device_printf(dev, "Interface does not support 2.5G advertised speed\n"); return (EINVAL); } speed |= IXGBE_LINK_SPEED_2_5GB_FULL; } if (advertise & 0x20) { if (!(link_caps & IXGBE_LINK_SPEED_5GB_FULL)) { device_printf(dev, "Interface does not support 5G advertised speed\n"); return (EINVAL); } speed |= IXGBE_LINK_SPEED_5GB_FULL; } hw->mac.autotry_restart = true; hw->mac.ops.setup_link(hw, speed, true); sc->advertise = advertise; return (0); } /* ixgbe_set_advertise */ /************************************************************************ * ixgbe_get_default_advertise - Get default advertised speed settings * * Formatted for sysctl usage. * Flags: * 0x1 - advertise 100 Mb * 0x2 - advertise 1G * 0x4 - advertise 10G * 0x8 - advertise 10 Mb (yes, Mb) * 0x10 - advertise 2.5G (disabled by default) * 0x20 - advertise 5G (disabled by default) ************************************************************************/ static int ixgbe_get_default_advertise(struct ixgbe_softc *sc) { struct ixgbe_hw *hw = &sc->hw; int speed; ixgbe_link_speed link_caps = 0; s32 err; bool negotiate = false; /* * Advertised speed means nothing unless it's copper or * multi-speed fiber */ if (!(hw->phy.media_type == ixgbe_media_type_copper) && !(hw->phy.multispeed_fiber)) return (0); err = hw->mac.ops.get_link_capabilities(hw, &link_caps, &negotiate); if (err != IXGBE_SUCCESS) return (0); if (hw->mac.type == ixgbe_mac_X550) { /* * 2.5G and 5G autonegotiation speeds on X550 * are disabled by default due to reported * interoperability issues with some switches. */ link_caps &= ~(IXGBE_LINK_SPEED_2_5GB_FULL | IXGBE_LINK_SPEED_5GB_FULL); } speed = ((link_caps & IXGBE_LINK_SPEED_10GB_FULL) ? 0x4 : 0) | ((link_caps & IXGBE_LINK_SPEED_5GB_FULL) ? 0x20 : 0) | ((link_caps & IXGBE_LINK_SPEED_2_5GB_FULL) ? 0x10 : 0) | ((link_caps & IXGBE_LINK_SPEED_1GB_FULL) ? 0x2 : 0) | ((link_caps & IXGBE_LINK_SPEED_100_FULL) ? 0x1 : 0) | ((link_caps & IXGBE_LINK_SPEED_10_FULL) ? 0x8 : 0); return speed; } /* ixgbe_get_default_advertise */ /************************************************************************ * ixgbe_sysctl_dmac - Manage DMA Coalescing * * Control values: * 0/1 - off / on (use default value of 1000) * * Legal timer values are: * 50,100,250,500,1000,2000,5000,10000 * * Turning off interrupt moderation will also turn this off. ************************************************************************/ static int ixgbe_sysctl_dmac(SYSCTL_HANDLER_ARGS) { struct ixgbe_softc *sc = (struct ixgbe_softc *)arg1; struct ifnet *ifp = iflib_get_ifp(sc->ctx); int error; u16 newval; newval = sc->dmac; error = sysctl_handle_16(oidp, &newval, 0, req); if ((error) || (req->newptr == NULL)) return (error); switch (newval) { case 0: /* Disabled */ sc->dmac = 0; break; case 1: /* Enable and use default */ sc->dmac = 1000; break; case 50: case 100: case 250: case 500: case 1000: case 2000: case 5000: case 10000: /* Legal values - allow */ sc->dmac = newval; break; default: /* Do nothing, illegal value */ return (EINVAL); } /* Re-initialize hardware if it's already running */ if (ifp->if_drv_flags & IFF_DRV_RUNNING) ifp->if_init(ifp); return (0); } /* ixgbe_sysctl_dmac */ #ifdef IXGBE_DEBUG /************************************************************************ * ixgbe_sysctl_power_state * * Sysctl to test power states * Values: * 0 - set device to D0 * 3 - set device to D3 * (none) - get current device power state ************************************************************************/ static int ixgbe_sysctl_power_state(SYSCTL_HANDLER_ARGS) { struct ixgbe_softc *sc = (struct ixgbe_softc *)arg1; device_t dev = sc->dev; int curr_ps, new_ps, error = 0; curr_ps = new_ps = pci_get_powerstate(dev); error = sysctl_handle_int(oidp, &new_ps, 0, req); if ((error) || (req->newptr == NULL)) return (error); if (new_ps == curr_ps) return (0); if (new_ps == 3 && curr_ps == 0) error = DEVICE_SUSPEND(dev); else if (new_ps == 0 && curr_ps == 3) error = DEVICE_RESUME(dev); else return (EINVAL); device_printf(dev, "New state: %d\n", pci_get_powerstate(dev)); return (error); } /* ixgbe_sysctl_power_state */ #endif /************************************************************************ * ixgbe_sysctl_wol_enable * * Sysctl to enable/disable the WoL capability, * if supported by the adapter. * * Values: * 0 - disabled * 1 - enabled ************************************************************************/ static int ixgbe_sysctl_wol_enable(SYSCTL_HANDLER_ARGS) { struct ixgbe_softc *sc = (struct ixgbe_softc *)arg1; struct ixgbe_hw *hw = &sc->hw; int new_wol_enabled; int error = 0; new_wol_enabled = hw->wol_enabled; error = sysctl_handle_int(oidp, &new_wol_enabled, 0, req); if ((error) || (req->newptr == NULL)) return (error); new_wol_enabled = !!(new_wol_enabled); if (new_wol_enabled == hw->wol_enabled) return (0); if (new_wol_enabled > 0 && !sc->wol_support) return (ENODEV); else hw->wol_enabled = new_wol_enabled; return (0); } /* ixgbe_sysctl_wol_enable */ /************************************************************************ * ixgbe_sysctl_wufc - Wake Up Filter Control * * Sysctl to enable/disable the types of packets that the * adapter will wake up on upon receipt. * Flags: * 0x1 - Link Status Change * 0x2 - Magic Packet * 0x4 - Direct Exact * 0x8 - Directed Multicast * 0x10 - Broadcast * 0x20 - ARP/IPv4 Request Packet * 0x40 - Direct IPv4 Packet * 0x80 - Direct IPv6 Packet * * Settings not listed above will cause the sysctl to return an error. ************************************************************************/ static int ixgbe_sysctl_wufc(SYSCTL_HANDLER_ARGS) { struct ixgbe_softc *sc = (struct ixgbe_softc *)arg1; int error = 0; u32 new_wufc; new_wufc = sc->wufc; error = sysctl_handle_32(oidp, &new_wufc, 0, req); if ((error) || (req->newptr == NULL)) return (error); if (new_wufc == sc->wufc) return (0); if (new_wufc & 0xffffff00) return (EINVAL); new_wufc &= 0xff; new_wufc |= (0xffffff & sc->wufc); sc->wufc = new_wufc; return (0); } /* ixgbe_sysctl_wufc */ #ifdef IXGBE_DEBUG /************************************************************************ * ixgbe_sysctl_print_rss_config ************************************************************************/ static int ixgbe_sysctl_print_rss_config(SYSCTL_HANDLER_ARGS) { struct ixgbe_softc *sc = (struct ixgbe_softc *)arg1; struct ixgbe_hw *hw = &sc->hw; device_t dev = sc->dev; struct sbuf *buf; int error = 0, reta_size; u32 reg; buf = sbuf_new_for_sysctl(NULL, NULL, 128, req); if (!buf) { device_printf(dev, "Could not allocate sbuf for output.\n"); return (ENOMEM); } // TODO: use sbufs to make a string to print out /* Set multiplier for RETA setup and table size based on MAC */ switch (sc->hw.mac.type) { case ixgbe_mac_X550: case ixgbe_mac_X550EM_x: case ixgbe_mac_X550EM_a: reta_size = 128; break; default: reta_size = 32; break; } /* Print out the redirection table */ sbuf_cat(buf, "\n"); for (int i = 0; i < reta_size; i++) { if (i < 32) { reg = IXGBE_READ_REG(hw, IXGBE_RETA(i)); sbuf_printf(buf, "RETA(%2d): 0x%08x\n", i, reg); } else { reg = IXGBE_READ_REG(hw, IXGBE_ERETA(i - 32)); sbuf_printf(buf, "ERETA(%2d): 0x%08x\n", i - 32, reg); } } // TODO: print more config error = sbuf_finish(buf); if (error) device_printf(dev, "Error finishing sbuf: %d\n", error); sbuf_delete(buf); return (0); } /* ixgbe_sysctl_print_rss_config */ #endif /* IXGBE_DEBUG */ /************************************************************************ * ixgbe_sysctl_phy_temp - Retrieve temperature of PHY * * For X552/X557-AT devices using an external PHY ************************************************************************/ static int ixgbe_sysctl_phy_temp(SYSCTL_HANDLER_ARGS) { struct ixgbe_softc *sc = (struct ixgbe_softc *)arg1; struct ixgbe_hw *hw = &sc->hw; u16 reg; if (hw->device_id != IXGBE_DEV_ID_X550EM_X_10G_T) { device_printf(iflib_get_dev(sc->ctx), "Device has no supported external thermal sensor.\n"); return (ENODEV); } if (hw->phy.ops.read_reg(hw, IXGBE_PHY_CURRENT_TEMP, IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, ®)) { device_printf(iflib_get_dev(sc->ctx), "Error reading from PHY's current temperature register\n"); return (EAGAIN); } /* Shift temp for output */ reg = reg >> 8; return (sysctl_handle_16(oidp, NULL, reg, req)); } /* ixgbe_sysctl_phy_temp */ /************************************************************************ * ixgbe_sysctl_phy_overtemp_occurred * * Reports (directly from the PHY) whether the current PHY * temperature is over the overtemp threshold. ************************************************************************/ static int ixgbe_sysctl_phy_overtemp_occurred(SYSCTL_HANDLER_ARGS) { struct ixgbe_softc *sc = (struct ixgbe_softc *)arg1; struct ixgbe_hw *hw = &sc->hw; u16 reg; if (hw->device_id != IXGBE_DEV_ID_X550EM_X_10G_T) { device_printf(iflib_get_dev(sc->ctx), "Device has no supported external thermal sensor.\n"); return (ENODEV); } if (hw->phy.ops.read_reg(hw, IXGBE_PHY_OVERTEMP_STATUS, IXGBE_MDIO_VENDOR_SPECIFIC_1_DEV_TYPE, ®)) { device_printf(iflib_get_dev(sc->ctx), "Error reading from PHY's temperature status register\n"); return (EAGAIN); } /* Get occurrence bit */ reg = !!(reg & 0x4000); return (sysctl_handle_16(oidp, 0, reg, req)); } /* ixgbe_sysctl_phy_overtemp_occurred */ /************************************************************************ * ixgbe_sysctl_eee_state * * Sysctl to set EEE power saving feature * Values: * 0 - disable EEE * 1 - enable EEE * (none) - get current device EEE state ************************************************************************/ static int ixgbe_sysctl_eee_state(SYSCTL_HANDLER_ARGS) { struct ixgbe_softc *sc = (struct ixgbe_softc *)arg1; device_t dev = sc->dev; struct ifnet *ifp = iflib_get_ifp(sc->ctx); int curr_eee, new_eee, error = 0; s32 retval; curr_eee = new_eee = !!(sc->feat_en & IXGBE_FEATURE_EEE); error = sysctl_handle_int(oidp, &new_eee, 0, req); if ((error) || (req->newptr == NULL)) return (error); /* Nothing to do */ if (new_eee == curr_eee) return (0); /* Not supported */ if (!(sc->feat_cap & IXGBE_FEATURE_EEE)) return (EINVAL); /* Bounds checking */ if ((new_eee < 0) || (new_eee > 1)) return (EINVAL); retval = ixgbe_setup_eee(&sc->hw, new_eee); if (retval) { device_printf(dev, "Error in EEE setup: 0x%08X\n", retval); return (EINVAL); } /* Restart auto-neg */ ifp->if_init(ifp); device_printf(dev, "New EEE state: %d\n", new_eee); /* Cache new value */ if (new_eee) sc->feat_en |= IXGBE_FEATURE_EEE; else sc->feat_en &= ~IXGBE_FEATURE_EEE; return (error); } /* ixgbe_sysctl_eee_state */ /************************************************************************ * ixgbe_init_device_features ************************************************************************/ static void ixgbe_init_device_features(struct ixgbe_softc *sc) { sc->feat_cap = IXGBE_FEATURE_NETMAP | IXGBE_FEATURE_RSS | IXGBE_FEATURE_MSI | IXGBE_FEATURE_MSIX | IXGBE_FEATURE_LEGACY_IRQ; /* Set capabilities first... */ switch (sc->hw.mac.type) { case ixgbe_mac_82598EB: if (sc->hw.device_id == IXGBE_DEV_ID_82598AT) sc->feat_cap |= IXGBE_FEATURE_FAN_FAIL; break; case ixgbe_mac_X540: sc->feat_cap |= IXGBE_FEATURE_SRIOV; sc->feat_cap |= IXGBE_FEATURE_FDIR; if ((sc->hw.device_id == IXGBE_DEV_ID_X540_BYPASS) && (sc->hw.bus.func == 0)) sc->feat_cap |= IXGBE_FEATURE_BYPASS; break; case ixgbe_mac_X550: sc->feat_cap |= IXGBE_FEATURE_TEMP_SENSOR; sc->feat_cap |= IXGBE_FEATURE_SRIOV; sc->feat_cap |= IXGBE_FEATURE_FDIR; break; case ixgbe_mac_X550EM_x: sc->feat_cap |= IXGBE_FEATURE_SRIOV; sc->feat_cap |= IXGBE_FEATURE_FDIR; break; case ixgbe_mac_X550EM_a: sc->feat_cap |= IXGBE_FEATURE_SRIOV; sc->feat_cap |= IXGBE_FEATURE_FDIR; sc->feat_cap &= ~IXGBE_FEATURE_LEGACY_IRQ; if ((sc->hw.device_id == IXGBE_DEV_ID_X550EM_A_1G_T) || (sc->hw.device_id == IXGBE_DEV_ID_X550EM_A_1G_T_L)) { sc->feat_cap |= IXGBE_FEATURE_TEMP_SENSOR; sc->feat_cap |= IXGBE_FEATURE_EEE; } break; case ixgbe_mac_82599EB: sc->feat_cap |= IXGBE_FEATURE_SRIOV; sc->feat_cap |= IXGBE_FEATURE_FDIR; if ((sc->hw.device_id == IXGBE_DEV_ID_82599_BYPASS) && (sc->hw.bus.func == 0)) sc->feat_cap |= IXGBE_FEATURE_BYPASS; if (sc->hw.device_id == IXGBE_DEV_ID_82599_QSFP_SF_QP) sc->feat_cap &= ~IXGBE_FEATURE_LEGACY_IRQ; break; default: break; } /* Enabled by default... */ /* Fan failure detection */ if (sc->feat_cap & IXGBE_FEATURE_FAN_FAIL) sc->feat_en |= IXGBE_FEATURE_FAN_FAIL; /* Netmap */ if (sc->feat_cap & IXGBE_FEATURE_NETMAP) sc->feat_en |= IXGBE_FEATURE_NETMAP; /* EEE */ if (sc->feat_cap & IXGBE_FEATURE_EEE) sc->feat_en |= IXGBE_FEATURE_EEE; /* Thermal Sensor */ if (sc->feat_cap & IXGBE_FEATURE_TEMP_SENSOR) sc->feat_en |= IXGBE_FEATURE_TEMP_SENSOR; /* Enabled via global sysctl... */ /* Flow Director */ if (ixgbe_enable_fdir) { if (sc->feat_cap & IXGBE_FEATURE_FDIR) sc->feat_en |= IXGBE_FEATURE_FDIR; else device_printf(sc->dev, "Device does not support Flow Director. Leaving disabled."); } /* * Message Signal Interrupts - Extended (MSI-X) * Normal MSI is only enabled if MSI-X calls fail. */ if (!ixgbe_enable_msix) sc->feat_cap &= ~IXGBE_FEATURE_MSIX; /* Receive-Side Scaling (RSS) */ if ((sc->feat_cap & IXGBE_FEATURE_RSS) && ixgbe_enable_rss) sc->feat_en |= IXGBE_FEATURE_RSS; /* Disable features with unmet dependencies... */ /* No MSI-X */ if (!(sc->feat_cap & IXGBE_FEATURE_MSIX)) { sc->feat_cap &= ~IXGBE_FEATURE_RSS; sc->feat_cap &= ~IXGBE_FEATURE_SRIOV; sc->feat_en &= ~IXGBE_FEATURE_RSS; sc->feat_en &= ~IXGBE_FEATURE_SRIOV; } } /* ixgbe_init_device_features */ /************************************************************************ * ixgbe_check_fan_failure ************************************************************************/ static void ixgbe_check_fan_failure(struct ixgbe_softc *sc, u32 reg, bool in_interrupt) { u32 mask; mask = (in_interrupt) ? IXGBE_EICR_GPI_SDP1_BY_MAC(&sc->hw) : IXGBE_ESDP_SDP1; if (reg & mask) device_printf(sc->dev, "\nCRITICAL: FAN FAILURE!! REPLACE IMMEDIATELY!!\n"); } /* ixgbe_check_fan_failure */ /************************************************************************ * ixgbe_sbuf_fw_version ************************************************************************/ static void ixgbe_sbuf_fw_version(struct ixgbe_hw *hw, struct sbuf *buf) { struct ixgbe_nvm_version nvm_ver = {0}; uint16_t phyfw = 0; int status; const char *space = ""; ixgbe_get_oem_prod_version(hw, &nvm_ver); /* OEM's NVM version */ ixgbe_get_orom_version(hw, &nvm_ver); /* Option ROM */ ixgbe_get_etk_id(hw, &nvm_ver); /* eTrack identifies a build in Intel's SCM */ status = ixgbe_get_phy_firmware_version(hw, &phyfw); if (nvm_ver.oem_valid) { sbuf_printf(buf, "NVM OEM V%d.%d R%d", nvm_ver.oem_major, nvm_ver.oem_minor, nvm_ver.oem_release); space = " "; } if (nvm_ver.or_valid) { sbuf_printf(buf, "%sOption ROM V%d-b%d-p%d", space, nvm_ver.or_major, nvm_ver.or_build, nvm_ver.or_patch); space = " "; } if (nvm_ver.etk_id != ((NVM_VER_INVALID << NVM_ETK_SHIFT) | NVM_VER_INVALID)) { sbuf_printf(buf, "%seTrack 0x%08x", space, nvm_ver.etk_id); space = " "; } if (phyfw != 0 && status == IXGBE_SUCCESS) sbuf_printf(buf, "%sPHY FW V%d", space, phyfw); } /* ixgbe_sbuf_fw_version */ /************************************************************************ * ixgbe_print_fw_version ************************************************************************/ static void ixgbe_print_fw_version(if_ctx_t ctx) { struct ixgbe_softc *sc = iflib_get_softc(ctx); struct ixgbe_hw *hw = &sc->hw; device_t dev = sc->dev; struct sbuf *buf; int error = 0; buf = sbuf_new_auto(); if (!buf) { device_printf(dev, "Could not allocate sbuf for output.\n"); return; } ixgbe_sbuf_fw_version(hw, buf); error = sbuf_finish(buf); if (error) device_printf(dev, "Error finishing sbuf: %d\n", error); else if (sbuf_len(buf)) device_printf(dev, "%s\n", sbuf_data(buf)); sbuf_delete(buf); } /* ixgbe_print_fw_version */ /************************************************************************ * ixgbe_sysctl_print_fw_version ************************************************************************/ static int ixgbe_sysctl_print_fw_version(SYSCTL_HANDLER_ARGS) { struct ixgbe_softc *sc = (struct ixgbe_softc *)arg1; struct ixgbe_hw *hw = &sc->hw; device_t dev = sc->dev; struct sbuf *buf; int error = 0; buf = sbuf_new_for_sysctl(NULL, NULL, 128, req); if (!buf) { device_printf(dev, "Could not allocate sbuf for output.\n"); return (ENOMEM); } ixgbe_sbuf_fw_version(hw, buf); error = sbuf_finish(buf); if (error) device_printf(dev, "Error finishing sbuf: %d\n", error); sbuf_delete(buf); return (0); } /* ixgbe_sysctl_print_fw_version */ diff --git a/sys/dev/mgb/if_mgb.c b/sys/dev/mgb/if_mgb.c index b2962d5ff3d8..e3806300ed9a 100644 --- a/sys/dev/mgb/if_mgb.c +++ b/sys/dev/mgb/if_mgb.c @@ -1,1616 +1,1617 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * * Copyright (c) 2019 The FreeBSD Foundation, Inc. * * This driver was written by Gerald ND Aryeetey * under sponsorship from the FreeBSD Foundation. * * 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 /* * Microchip LAN7430/LAN7431 PCIe to Gigabit Ethernet Controller driver. * * Product information: * LAN7430 https://www.microchip.com/en-us/product/LAN7430 * - Integrated IEEE 802.3 compliant PHY * LAN7431 https://www.microchip.com/en-us/product/LAN7431 * - RGMII Interface * * This driver uses the iflib interface and the default 'ukphy' PHY driver. * * UNIMPLEMENTED FEATURES * ---------------------- * A number of features supported by LAN743X device are not yet implemented in * this driver: * * - Multiple (up to 4) RX queues support * - Just needs to remove asserts and malloc multiple `rx_ring_data` * structs based on ncpus. * - RX/TX Checksum Offloading support * - VLAN support * - Receive Packet Filtering (Multicast Perfect/Hash Address) support * - Wake on LAN (WoL) support * - TX LSO support * - Receive Side Scaling (RSS) support * - Debugging Capabilities: * - Could include MAC statistics and * error status registers in sysctl. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ifdi_if.h" #include "miibus_if.h" static pci_vendor_info_t mgb_vendor_info_array[] = { PVID(MGB_MICROCHIP_VENDOR_ID, MGB_LAN7430_DEVICE_ID, "Microchip LAN7430 PCIe Gigabit Ethernet Controller"), PVID(MGB_MICROCHIP_VENDOR_ID, MGB_LAN7431_DEVICE_ID, "Microchip LAN7431 PCIe Gigabit Ethernet Controller"), PVID_END }; /* Device methods */ static device_register_t mgb_register; /* IFLIB methods */ static ifdi_attach_pre_t mgb_attach_pre; static ifdi_attach_post_t mgb_attach_post; static ifdi_detach_t mgb_detach; static ifdi_tx_queues_alloc_t mgb_tx_queues_alloc; static ifdi_rx_queues_alloc_t mgb_rx_queues_alloc; static ifdi_queues_free_t mgb_queues_free; static ifdi_init_t mgb_init; static ifdi_stop_t mgb_stop; static ifdi_msix_intr_assign_t mgb_msix_intr_assign; static ifdi_tx_queue_intr_enable_t mgb_tx_queue_intr_enable; static ifdi_rx_queue_intr_enable_t mgb_rx_queue_intr_enable; static ifdi_intr_enable_t mgb_intr_enable_all; static ifdi_intr_disable_t mgb_intr_disable_all; /* IFLIB_TXRX methods */ static int mgb_isc_txd_encap(void *, if_pkt_info_t); static void mgb_isc_txd_flush(void *, uint16_t, qidx_t); static int mgb_isc_txd_credits_update(void *, uint16_t, bool); static int mgb_isc_rxd_available(void *, uint16_t, qidx_t, qidx_t); static int mgb_isc_rxd_pkt_get(void *, if_rxd_info_t); static void mgb_isc_rxd_refill(void *, if_rxd_update_t); static void mgb_isc_rxd_flush(void *, uint16_t, uint8_t, qidx_t); /* Interrupts */ static driver_filter_t mgb_legacy_intr; static driver_filter_t mgb_admin_intr; static driver_filter_t mgb_rxq_intr; static bool mgb_intr_test(struct mgb_softc *); /* MII methods */ static miibus_readreg_t mgb_miibus_readreg; static miibus_writereg_t mgb_miibus_writereg; static miibus_linkchg_t mgb_miibus_linkchg; static miibus_statchg_t mgb_miibus_statchg; static int mgb_media_change(if_t); static void mgb_media_status(if_t, struct ifmediareq *); /* Helper/Test functions */ static int mgb_test_bar(struct mgb_softc *); static int mgb_alloc_regs(struct mgb_softc *); static int mgb_release_regs(struct mgb_softc *); static void mgb_get_ethaddr(struct mgb_softc *, struct ether_addr *); static int mgb_wait_for_bits(struct mgb_softc *, int, int, int); /* H/W init, reset and teardown helpers */ static int mgb_hw_init(struct mgb_softc *); static int mgb_hw_teardown(struct mgb_softc *); static int mgb_hw_reset(struct mgb_softc *); static int mgb_mac_init(struct mgb_softc *); static int mgb_dmac_reset(struct mgb_softc *); static int mgb_phy_reset(struct mgb_softc *); static int mgb_dma_init(struct mgb_softc *); static int mgb_dma_tx_ring_init(struct mgb_softc *, int); static int mgb_dma_rx_ring_init(struct mgb_softc *, int); static int mgb_dmac_control(struct mgb_softc *, int, int, enum mgb_dmac_cmd); static int mgb_fct_control(struct mgb_softc *, int, int, enum mgb_fct_cmd); /********************************************************************* * FreeBSD Device Interface Entry Points *********************************************************************/ static device_method_t mgb_methods[] = { /* Device interface */ DEVMETHOD(device_register, mgb_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), /* MII Interface */ DEVMETHOD(miibus_readreg, mgb_miibus_readreg), DEVMETHOD(miibus_writereg, mgb_miibus_writereg), DEVMETHOD(miibus_linkchg, mgb_miibus_linkchg), DEVMETHOD(miibus_statchg, mgb_miibus_statchg), DEVMETHOD_END }; static driver_t mgb_driver = { "mgb", mgb_methods, sizeof(struct mgb_softc) }; static devclass_t mgb_devclass; DRIVER_MODULE(mgb, pci, mgb_driver, mgb_devclass, NULL, NULL); IFLIB_PNP_INFO(pci, mgb, mgb_vendor_info_array); MODULE_VERSION(mgb, 1); #if 0 /* MIIBUS_DEBUG */ /* If MIIBUS debug stuff is in attach then order matters. Use below instead. */ DRIVER_MODULE_ORDERED(miibus, mgb, miibus_driver, miibus_devclass, NULL, NULL, SI_ORDER_ANY); #endif /* MIIBUS_DEBUG */ DRIVER_MODULE(miibus, mgb, miibus_driver, miibus_devclass, NULL, NULL); MODULE_DEPEND(mgb, pci, 1, 1, 1); MODULE_DEPEND(mgb, ether, 1, 1, 1); MODULE_DEPEND(mgb, miibus, 1, 1, 1); MODULE_DEPEND(mgb, iflib, 1, 1, 1); static device_method_t mgb_iflib_methods[] = { DEVMETHOD(ifdi_attach_pre, mgb_attach_pre), DEVMETHOD(ifdi_attach_post, mgb_attach_post), DEVMETHOD(ifdi_detach, mgb_detach), DEVMETHOD(ifdi_init, mgb_init), DEVMETHOD(ifdi_stop, mgb_stop), DEVMETHOD(ifdi_tx_queues_alloc, mgb_tx_queues_alloc), DEVMETHOD(ifdi_rx_queues_alloc, mgb_rx_queues_alloc), DEVMETHOD(ifdi_queues_free, mgb_queues_free), DEVMETHOD(ifdi_msix_intr_assign, mgb_msix_intr_assign), DEVMETHOD(ifdi_tx_queue_intr_enable, mgb_tx_queue_intr_enable), DEVMETHOD(ifdi_rx_queue_intr_enable, mgb_rx_queue_intr_enable), DEVMETHOD(ifdi_intr_enable, mgb_intr_enable_all), DEVMETHOD(ifdi_intr_disable, mgb_intr_disable_all), #if 0 /* Not yet implemented IFLIB methods */ /* * Set multicast addresses, mtu and promiscuous mode */ DEVMETHOD(ifdi_multi_set, mgb_multi_set), DEVMETHOD(ifdi_mtu_set, mgb_mtu_set), DEVMETHOD(ifdi_promisc_set, mgb_promisc_set), /* * Needed for VLAN support */ DEVMETHOD(ifdi_vlan_register, mgb_vlan_register), DEVMETHOD(ifdi_vlan_unregister, mgb_vlan_unregister), + DEVMETHOD(ifdi_needs_restart, mgb_if_needs_restart), /* * Needed for WOL support * at the very least. */ DEVMETHOD(ifdi_shutdown, mgb_shutdown), DEVMETHOD(ifdi_suspend, mgb_suspend), DEVMETHOD(ifdi_resume, mgb_resume), #endif /* UNUSED_IFLIB_METHODS */ DEVMETHOD_END }; static driver_t mgb_iflib_driver = { "mgb", mgb_iflib_methods, sizeof(struct mgb_softc) }; static struct if_txrx mgb_txrx = { .ift_txd_encap = mgb_isc_txd_encap, .ift_txd_flush = mgb_isc_txd_flush, .ift_txd_credits_update = mgb_isc_txd_credits_update, .ift_rxd_available = mgb_isc_rxd_available, .ift_rxd_pkt_get = mgb_isc_rxd_pkt_get, .ift_rxd_refill = mgb_isc_rxd_refill, .ift_rxd_flush = mgb_isc_rxd_flush, .ift_legacy_intr = mgb_legacy_intr }; static struct if_shared_ctx mgb_sctx_init = { .isc_magic = IFLIB_MAGIC, .isc_q_align = PAGE_SIZE, .isc_admin_intrcnt = 1, .isc_flags = IFLIB_DRIVER_MEDIA /* | IFLIB_HAS_RXCQ | IFLIB_HAS_TXCQ*/, .isc_vendor_info = mgb_vendor_info_array, .isc_driver_version = "1", .isc_driver = &mgb_iflib_driver, /* 2 queues per set for TX and RX (ring queue, head writeback queue) */ .isc_ntxqs = 2, .isc_tx_maxsize = MGB_DMA_MAXSEGS * MCLBYTES, /* .isc_tx_nsegments = MGB_DMA_MAXSEGS, */ .isc_tx_maxsegsize = MCLBYTES, .isc_ntxd_min = {1, 1}, /* Will want to make this bigger */ .isc_ntxd_max = {MGB_DMA_RING_SIZE, 1}, .isc_ntxd_default = {MGB_DMA_RING_SIZE, 1}, .isc_nrxqs = 2, .isc_rx_maxsize = MCLBYTES, .isc_rx_nsegments = 1, .isc_rx_maxsegsize = MCLBYTES, .isc_nrxd_min = {1, 1}, /* Will want to make this bigger */ .isc_nrxd_max = {MGB_DMA_RING_SIZE, 1}, .isc_nrxd_default = {MGB_DMA_RING_SIZE, 1}, .isc_nfl = 1, /*one free list since there is only one queue */ #if 0 /* UNUSED_CTX */ .isc_tso_maxsize = MGB_TSO_MAXSIZE + sizeof(struct ether_vlan_header), .isc_tso_maxsegsize = MGB_TX_MAXSEGSIZE, #endif /* UNUSED_CTX */ }; /*********************************************************************/ static void * mgb_register(device_t dev) { return (&mgb_sctx_init); } static int mgb_attach_pre(if_ctx_t ctx) { struct mgb_softc *sc; if_softc_ctx_t scctx; int error, phyaddr, rid; struct ether_addr hwaddr; struct mii_data *miid; sc = iflib_get_softc(ctx); sc->ctx = ctx; sc->dev = iflib_get_dev(ctx); scctx = iflib_get_softc_ctx(ctx); /* IFLIB required setup */ scctx->isc_txrx = &mgb_txrx; scctx->isc_tx_nsegments = MGB_DMA_MAXSEGS; /* Ring desc queues */ scctx->isc_txqsizes[0] = sizeof(struct mgb_ring_desc) * scctx->isc_ntxd[0]; scctx->isc_rxqsizes[0] = sizeof(struct mgb_ring_desc) * scctx->isc_nrxd[0]; /* Head WB queues */ scctx->isc_txqsizes[1] = sizeof(uint32_t) * scctx->isc_ntxd[1]; scctx->isc_rxqsizes[1] = sizeof(uint32_t) * scctx->isc_nrxd[1]; /* XXX: Must have 1 txqset, but can have up to 4 rxqsets */ scctx->isc_nrxqsets = 1; scctx->isc_ntxqsets = 1; /* scctx->isc_tx_csum_flags = (CSUM_TCP | CSUM_UDP) | (CSUM_TCP_IPV6 | CSUM_UDP_IPV6) | CSUM_TSO */ scctx->isc_tx_csum_flags = 0; scctx->isc_capabilities = scctx->isc_capenable = 0; #if 0 /* * CSUM, TSO and VLAN support are TBD */ IFCAP_TXCSUM | IFCAP_TXCSUM_IPV6 | IFCAP_TSO4 | IFCAP_TSO6 | IFCAP_RXCSUM | IFCAP_RXCSUM_IPV6 | IFCAP_VLAN_MTU | IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_HWCSUM | IFCAP_VLAN_HWTSO | IFCAP_JUMBO_MTU; scctx->isc_capabilities |= IFCAP_LRO | IFCAP_VLAN_HWFILTER; #endif /* get the BAR */ error = mgb_alloc_regs(sc); if (error != 0) { device_printf(sc->dev, "Unable to allocate bus resource: registers.\n"); goto fail; } error = mgb_test_bar(sc); if (error != 0) goto fail; error = mgb_hw_init(sc); if (error != 0) { device_printf(sc->dev, "MGB device init failed. (err: %d)\n", error); goto fail; } switch (pci_get_device(sc->dev)) { case MGB_LAN7430_DEVICE_ID: phyaddr = 1; break; case MGB_LAN7431_DEVICE_ID: default: phyaddr = MII_PHY_ANY; break; } /* XXX: Would be nice(r) if locked methods were here */ error = mii_attach(sc->dev, &sc->miibus, iflib_get_ifp(ctx), mgb_media_change, mgb_media_status, BMSR_DEFCAPMASK, phyaddr, MII_OFFSET_ANY, MIIF_DOPAUSE); if (error != 0) { device_printf(sc->dev, "Failed to attach MII interface\n"); goto fail; } miid = device_get_softc(sc->miibus); scctx->isc_media = &miid->mii_media; scctx->isc_msix_bar = pci_msix_table_bar(sc->dev); /** Setup PBA BAR **/ rid = pci_msix_pba_bar(sc->dev); if (rid != scctx->isc_msix_bar) { sc->pba = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->pba == NULL) { error = ENXIO; device_printf(sc->dev, "Failed to setup PBA BAR\n"); goto fail; } } mgb_get_ethaddr(sc, &hwaddr); if (ETHER_IS_BROADCAST(hwaddr.octet) || ETHER_IS_MULTICAST(hwaddr.octet) || ETHER_IS_ZERO(hwaddr.octet)) ether_gen_addr(iflib_get_ifp(ctx), &hwaddr); /* * XXX: if the MAC address was generated the linux driver * writes it back to the device. */ iflib_set_mac(ctx, hwaddr.octet); /* Map all vectors to vector 0 (admin interrupts) by default. */ CSR_WRITE_REG(sc, MGB_INTR_VEC_RX_MAP, 0); CSR_WRITE_REG(sc, MGB_INTR_VEC_TX_MAP, 0); CSR_WRITE_REG(sc, MGB_INTR_VEC_OTHER_MAP, 0); return (0); fail: mgb_detach(ctx); return (error); } static int mgb_attach_post(if_ctx_t ctx) { struct mgb_softc *sc; sc = iflib_get_softc(ctx); device_printf(sc->dev, "Interrupt test: %s\n", (mgb_intr_test(sc) ? "PASS" : "FAIL")); return (0); } static int mgb_detach(if_ctx_t ctx) { struct mgb_softc *sc; int error; sc = iflib_get_softc(ctx); /* XXX: Should report errors but still detach everything. */ error = mgb_hw_teardown(sc); /* Release IRQs */ iflib_irq_free(ctx, &sc->rx_irq); iflib_irq_free(ctx, &sc->admin_irq); if (sc->miibus != NULL) device_delete_child(sc->dev, sc->miibus); if (sc->pba != NULL) error = bus_release_resource(sc->dev, SYS_RES_MEMORY, rman_get_rid(sc->pba), sc->pba); sc->pba = NULL; error = mgb_release_regs(sc); return (error); } static int mgb_media_change(if_t ifp) { struct mii_data *miid; struct mii_softc *miisc; struct mgb_softc *sc; if_ctx_t ctx; int needs_reset; ctx = if_getsoftc(ifp); sc = iflib_get_softc(ctx); miid = device_get_softc(sc->miibus); LIST_FOREACH(miisc, &miid->mii_phys, mii_list) PHY_RESET(miisc); needs_reset = mii_mediachg(miid); if (needs_reset != 0) ifp->if_init(ctx); return (needs_reset); } static void mgb_media_status(if_t ifp, struct ifmediareq *ifmr) { struct mgb_softc *sc; struct mii_data *miid; sc = iflib_get_softc(if_getsoftc(ifp)); miid = device_get_softc(sc->miibus); if ((if_getflags(ifp) & IFF_UP) == 0) return; mii_pollstat(miid); ifmr->ifm_active = miid->mii_media_active; ifmr->ifm_status = miid->mii_media_status; } static int mgb_tx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs, int ntxqs, int ntxqsets) { struct mgb_softc *sc; struct mgb_ring_data *rdata; int q; sc = iflib_get_softc(ctx); KASSERT(ntxqsets == 1, ("ntxqsets = %d", ntxqsets)); rdata = &sc->tx_ring_data; for (q = 0; q < ntxqsets; q++) { KASSERT(ntxqs == 2, ("ntxqs = %d", ntxqs)); /* Ring */ rdata->ring = (struct mgb_ring_desc *) vaddrs[q * ntxqs + 0]; rdata->ring_bus_addr = paddrs[q * ntxqs + 0]; /* Head WB */ rdata->head_wb = (uint32_t *) vaddrs[q * ntxqs + 1]; rdata->head_wb_bus_addr = paddrs[q * ntxqs + 1]; } return (0); } static int mgb_rx_queues_alloc(if_ctx_t ctx, caddr_t *vaddrs, uint64_t *paddrs, int nrxqs, int nrxqsets) { struct mgb_softc *sc; struct mgb_ring_data *rdata; int q; sc = iflib_get_softc(ctx); KASSERT(nrxqsets == 1, ("nrxqsets = %d", nrxqsets)); rdata = &sc->rx_ring_data; for (q = 0; q < nrxqsets; q++) { KASSERT(nrxqs == 2, ("nrxqs = %d", nrxqs)); /* Ring */ rdata->ring = (struct mgb_ring_desc *) vaddrs[q * nrxqs + 0]; rdata->ring_bus_addr = paddrs[q * nrxqs + 0]; /* Head WB */ rdata->head_wb = (uint32_t *) vaddrs[q * nrxqs + 1]; rdata->head_wb_bus_addr = paddrs[q * nrxqs + 1]; } return (0); } static void mgb_queues_free(if_ctx_t ctx) { struct mgb_softc *sc; sc = iflib_get_softc(ctx); memset(&sc->rx_ring_data, 0, sizeof(struct mgb_ring_data)); memset(&sc->tx_ring_data, 0, sizeof(struct mgb_ring_data)); } static void mgb_init(if_ctx_t ctx) { struct mgb_softc *sc; struct mii_data *miid; int error; sc = iflib_get_softc(ctx); miid = device_get_softc(sc->miibus); device_printf(sc->dev, "running init ...\n"); mgb_dma_init(sc); /* XXX: Turn off perfect filtering, turn on (broad|multi|uni)cast rx */ CSR_CLEAR_REG(sc, MGB_RFE_CTL, MGB_RFE_ALLOW_PERFECT_FILTER); CSR_UPDATE_REG(sc, MGB_RFE_CTL, MGB_RFE_ALLOW_BROADCAST | MGB_RFE_ALLOW_MULTICAST | MGB_RFE_ALLOW_UNICAST); error = mii_mediachg(miid); /* Not much we can do if this fails. */ if (error) device_printf(sc->dev, "%s: mii_mediachg returned %d", __func__, error); } #ifdef DEBUG static void mgb_dump_some_stats(struct mgb_softc *sc) { int i; int first_stat = 0x1200; int last_stat = 0x12FC; for (i = first_stat; i <= last_stat; i += 4) if (CSR_READ_REG(sc, i) != 0) device_printf(sc->dev, "0x%04x: 0x%08x\n", i, CSR_READ_REG(sc, i)); char *stat_names[] = { "MAC_ERR_STS ", "FCT_INT_STS ", "DMAC_CFG ", "DMAC_CMD ", "DMAC_INT_STS ", "DMAC_INT_EN ", "DMAC_RX_ERR_STS0 ", "DMAC_RX_ERR_STS1 ", "DMAC_RX_ERR_STS2 ", "DMAC_RX_ERR_STS3 ", "INT_STS ", "INT_EN ", "INT_VEC_EN ", "INT_VEC_MAP0 ", "INT_VEC_MAP1 ", "INT_VEC_MAP2 ", "TX_HEAD0", "TX_TAIL0", "DMAC_TX_ERR_STS0 ", NULL }; int stats[] = { 0x114, 0xA0, 0xC00, 0xC0C, 0xC10, 0xC14, 0xC60, 0xCA0, 0xCE0, 0xD20, 0x780, 0x788, 0x794, 0x7A0, 0x7A4, 0x780, 0xD58, 0xD5C, 0xD60, 0x0 }; i = 0; printf("==============================\n"); while (stats[i++]) device_printf(sc->dev, "%s at offset 0x%04x = 0x%08x\n", stat_names[i - 1], stats[i - 1], CSR_READ_REG(sc, stats[i - 1])); printf("==== TX RING DESCS ====\n"); for (i = 0; i < MGB_DMA_RING_SIZE; i++) device_printf(sc->dev, "ring[%d].data0=0x%08x\n" "ring[%d].data1=0x%08x\n" "ring[%d].data2=0x%08x\n" "ring[%d].data3=0x%08x\n", i, sc->tx_ring_data.ring[i].ctl, i, sc->tx_ring_data.ring[i].addr.low, i, sc->tx_ring_data.ring[i].addr.high, i, sc->tx_ring_data.ring[i].sts); device_printf(sc->dev, "==== DUMP_TX_DMA_RAM ====\n"); CSR_WRITE_REG(sc, 0x24, 0xF); // DP_SEL & TX_RAM_0 for (i = 0; i < 128; i++) { CSR_WRITE_REG(sc, 0x2C, i); // DP_ADDR CSR_WRITE_REG(sc, 0x28, 0); // DP_CMD while ((CSR_READ_REG(sc, 0x24) & 0x80000000) == 0) // DP_SEL & READY DELAY(1000); device_printf(sc->dev, "DMAC_TX_RAM_0[%u]=%08x\n", i, CSR_READ_REG(sc, 0x30)); // DP_DATA } } #endif static void mgb_stop(if_ctx_t ctx) { struct mgb_softc *sc ; if_softc_ctx_t scctx; int i; sc = iflib_get_softc(ctx); scctx = iflib_get_softc_ctx(ctx); /* XXX: Could potentially timeout */ for (i = 0; i < scctx->isc_nrxqsets; i++) { mgb_dmac_control(sc, MGB_DMAC_RX_START, 0, DMAC_STOP); mgb_fct_control(sc, MGB_FCT_RX_CTL, 0, FCT_DISABLE); } for (i = 0; i < scctx->isc_ntxqsets; i++) { mgb_dmac_control(sc, MGB_DMAC_TX_START, 0, DMAC_STOP); mgb_fct_control(sc, MGB_FCT_TX_CTL, 0, FCT_DISABLE); } } static int mgb_legacy_intr(void *xsc) { struct mgb_softc *sc; sc = xsc; iflib_admin_intr_deferred(sc->ctx); return (FILTER_HANDLED); } static int mgb_rxq_intr(void *xsc) { struct mgb_softc *sc; if_softc_ctx_t scctx; uint32_t intr_sts, intr_en; int qidx; sc = xsc; scctx = iflib_get_softc_ctx(sc->ctx); intr_sts = CSR_READ_REG(sc, MGB_INTR_STS); intr_en = CSR_READ_REG(sc, MGB_INTR_ENBL_SET); intr_sts &= intr_en; for (qidx = 0; qidx < scctx->isc_nrxqsets; qidx++) { if ((intr_sts & MGB_INTR_STS_RX(qidx))){ CSR_WRITE_REG(sc, MGB_INTR_ENBL_CLR, MGB_INTR_STS_RX(qidx)); CSR_WRITE_REG(sc, MGB_INTR_STS, MGB_INTR_STS_RX(qidx)); } } return (FILTER_SCHEDULE_THREAD); } static int mgb_admin_intr(void *xsc) { struct mgb_softc *sc; if_softc_ctx_t scctx; uint32_t intr_sts, intr_en; int qidx; sc = xsc; scctx = iflib_get_softc_ctx(sc->ctx); intr_sts = CSR_READ_REG(sc, MGB_INTR_STS); intr_en = CSR_READ_REG(sc, MGB_INTR_ENBL_SET); intr_sts &= intr_en; /* TODO: shouldn't continue if suspended */ if ((intr_sts & MGB_INTR_STS_ANY) == 0) return (FILTER_STRAY); if ((intr_sts & MGB_INTR_STS_TEST) != 0) { sc->isr_test_flag = true; CSR_WRITE_REG(sc, MGB_INTR_STS, MGB_INTR_STS_TEST); return (FILTER_HANDLED); } if ((intr_sts & MGB_INTR_STS_RX_ANY) != 0) { for (qidx = 0; qidx < scctx->isc_nrxqsets; qidx++) { if ((intr_sts & MGB_INTR_STS_RX(qidx))){ iflib_rx_intr_deferred(sc->ctx, qidx); } } return (FILTER_HANDLED); } /* XXX: TX interrupts should not occur */ if ((intr_sts & MGB_INTR_STS_TX_ANY) != 0) { for (qidx = 0; qidx < scctx->isc_ntxqsets; qidx++) { if ((intr_sts & MGB_INTR_STS_RX(qidx))) { /* clear the interrupt sts and run handler */ CSR_WRITE_REG(sc, MGB_INTR_ENBL_CLR, MGB_INTR_STS_TX(qidx)); CSR_WRITE_REG(sc, MGB_INTR_STS, MGB_INTR_STS_TX(qidx)); iflib_tx_intr_deferred(sc->ctx, qidx); } } return (FILTER_HANDLED); } return (FILTER_SCHEDULE_THREAD); } static int mgb_msix_intr_assign(if_ctx_t ctx, int msix) { struct mgb_softc *sc; if_softc_ctx_t scctx; int error, i, vectorid; char irq_name[16]; sc = iflib_get_softc(ctx); scctx = iflib_get_softc_ctx(ctx); KASSERT(scctx->isc_nrxqsets == 1 && scctx->isc_ntxqsets == 1, ("num rxqsets/txqsets != 1 ")); /* * First vector should be admin interrupts, others vectors are TX/RX * * RIDs start at 1, and vector ids start at 0. */ vectorid = 0; error = iflib_irq_alloc_generic(ctx, &sc->admin_irq, vectorid + 1, IFLIB_INTR_ADMIN, mgb_admin_intr, sc, 0, "admin"); if (error) { device_printf(sc->dev, "Failed to register admin interrupt handler\n"); return (error); } for (i = 0; i < scctx->isc_nrxqsets; i++) { vectorid++; snprintf(irq_name, sizeof(irq_name), "rxq%d", i); error = iflib_irq_alloc_generic(ctx, &sc->rx_irq, vectorid + 1, IFLIB_INTR_RXTX, mgb_rxq_intr, sc, i, irq_name); if (error) { device_printf(sc->dev, "Failed to register rxq %d interrupt handler\n", i); return (error); } CSR_UPDATE_REG(sc, MGB_INTR_VEC_RX_MAP, MGB_INTR_VEC_MAP(vectorid, i)); } /* Not actually mapping hw TX interrupts ... */ for (i = 0; i < scctx->isc_ntxqsets; i++) { snprintf(irq_name, sizeof(irq_name), "txq%d", i); iflib_softirq_alloc_generic(ctx, NULL, IFLIB_INTR_TX, NULL, i, irq_name); } return (0); } static void mgb_intr_enable_all(if_ctx_t ctx) { struct mgb_softc *sc; if_softc_ctx_t scctx; int i, dmac_enable = 0, intr_sts = 0, vec_en = 0; sc = iflib_get_softc(ctx); scctx = iflib_get_softc_ctx(ctx); intr_sts |= MGB_INTR_STS_ANY; vec_en |= MGB_INTR_STS_ANY; for (i = 0; i < scctx->isc_nrxqsets; i++) { intr_sts |= MGB_INTR_STS_RX(i); dmac_enable |= MGB_DMAC_RX_INTR_ENBL(i); vec_en |= MGB_INTR_RX_VEC_STS(i); } /* TX interrupts aren't needed ... */ CSR_WRITE_REG(sc, MGB_INTR_ENBL_SET, intr_sts); CSR_WRITE_REG(sc, MGB_INTR_VEC_ENBL_SET, vec_en); CSR_WRITE_REG(sc, MGB_DMAC_INTR_STS, dmac_enable); CSR_WRITE_REG(sc, MGB_DMAC_INTR_ENBL_SET, dmac_enable); } static void mgb_intr_disable_all(if_ctx_t ctx) { struct mgb_softc *sc; sc = iflib_get_softc(ctx); CSR_WRITE_REG(sc, MGB_INTR_ENBL_CLR, UINT32_MAX); CSR_WRITE_REG(sc, MGB_INTR_VEC_ENBL_CLR, UINT32_MAX); CSR_WRITE_REG(sc, MGB_INTR_STS, UINT32_MAX); CSR_WRITE_REG(sc, MGB_DMAC_INTR_ENBL_CLR, UINT32_MAX); CSR_WRITE_REG(sc, MGB_DMAC_INTR_STS, UINT32_MAX); } static int mgb_rx_queue_intr_enable(if_ctx_t ctx, uint16_t qid) { /* called after successful rx isr */ struct mgb_softc *sc; sc = iflib_get_softc(ctx); CSR_WRITE_REG(sc, MGB_INTR_VEC_ENBL_SET, MGB_INTR_RX_VEC_STS(qid)); CSR_WRITE_REG(sc, MGB_INTR_ENBL_SET, MGB_INTR_STS_RX(qid)); CSR_WRITE_REG(sc, MGB_DMAC_INTR_STS, MGB_DMAC_RX_INTR_ENBL(qid)); CSR_WRITE_REG(sc, MGB_DMAC_INTR_ENBL_SET, MGB_DMAC_RX_INTR_ENBL(qid)); return (0); } static int mgb_tx_queue_intr_enable(if_ctx_t ctx, uint16_t qid) { /* XXX: not called (since tx interrupts not used) */ struct mgb_softc *sc; sc = iflib_get_softc(ctx); CSR_WRITE_REG(sc, MGB_INTR_ENBL_SET, MGB_INTR_STS_TX(qid)); CSR_WRITE_REG(sc, MGB_DMAC_INTR_STS, MGB_DMAC_TX_INTR_ENBL(qid)); CSR_WRITE_REG(sc, MGB_DMAC_INTR_ENBL_SET, MGB_DMAC_TX_INTR_ENBL(qid)); return (0); } static bool mgb_intr_test(struct mgb_softc *sc) { int i; sc->isr_test_flag = false; CSR_WRITE_REG(sc, MGB_INTR_STS, MGB_INTR_STS_TEST); CSR_WRITE_REG(sc, MGB_INTR_VEC_ENBL_SET, MGB_INTR_STS_ANY); CSR_WRITE_REG(sc, MGB_INTR_ENBL_SET, MGB_INTR_STS_ANY | MGB_INTR_STS_TEST); CSR_WRITE_REG(sc, MGB_INTR_SET, MGB_INTR_STS_TEST); if (sc->isr_test_flag) return (true); for (i = 0; i < MGB_TIMEOUT; i++) { DELAY(10); if (sc->isr_test_flag) break; } CSR_WRITE_REG(sc, MGB_INTR_ENBL_CLR, MGB_INTR_STS_TEST); CSR_WRITE_REG(sc, MGB_INTR_STS, MGB_INTR_STS_TEST); return (sc->isr_test_flag); } static int mgb_isc_txd_encap(void *xsc , if_pkt_info_t ipi) { struct mgb_softc *sc; struct mgb_ring_data *rdata; struct mgb_ring_desc *txd; bus_dma_segment_t *segs; qidx_t pidx, nsegs; int i; KASSERT(ipi->ipi_qsidx == 0, ("tried to refill TX Channel %d.\n", ipi->ipi_qsidx)); sc = xsc; rdata = &sc->tx_ring_data; pidx = ipi->ipi_pidx; segs = ipi->ipi_segs; nsegs = ipi->ipi_nsegs; /* For each seg, create a descriptor */ for (i = 0; i < nsegs; ++i) { KASSERT(nsegs == 1, ("Multisegment packet !!!!!\n")); txd = &rdata->ring[pidx]; txd->ctl = htole32( (segs[i].ds_len & MGB_DESC_CTL_BUFLEN_MASK ) | /* * XXX: This will be wrong in the multipacket case * I suspect FS should be for the first packet and * LS should be for the last packet */ MGB_TX_DESC_CTL_FS | MGB_TX_DESC_CTL_LS | MGB_DESC_CTL_FCS); txd->addr.low = htole32(CSR_TRANSLATE_ADDR_LOW32( segs[i].ds_addr)); txd->addr.high = htole32(CSR_TRANSLATE_ADDR_HIGH32( segs[i].ds_addr)); txd->sts = htole32( (segs[i].ds_len << 16) & MGB_DESC_FRAME_LEN_MASK); pidx = MGB_NEXT_RING_IDX(pidx); } ipi->ipi_new_pidx = pidx; return (0); } static void mgb_isc_txd_flush(void *xsc, uint16_t txqid, qidx_t pidx) { struct mgb_softc *sc; struct mgb_ring_data *rdata; KASSERT(txqid == 0, ("tried to flush TX Channel %d.\n", txqid)); sc = xsc; rdata = &sc->tx_ring_data; if (rdata->last_tail != pidx) { rdata->last_tail = pidx; CSR_WRITE_REG(sc, MGB_DMA_TX_TAIL(txqid), rdata->last_tail); } } static int mgb_isc_txd_credits_update(void *xsc, uint16_t txqid, bool clear) { struct mgb_softc *sc; struct mgb_ring_desc *txd; struct mgb_ring_data *rdata; int processed = 0; /* * > If clear is true, we need to report the number of TX command ring * > descriptors that have been processed by the device. If clear is * > false, we just need to report whether or not at least one TX * > command ring descriptor has been processed by the device. * - vmx driver */ KASSERT(txqid == 0, ("tried to credits_update TX Channel %d.\n", txqid)); sc = xsc; rdata = &sc->tx_ring_data; while (*(rdata->head_wb) != rdata->last_head) { if (!clear) return (1); txd = &rdata->ring[rdata->last_head]; memset(txd, 0, sizeof(struct mgb_ring_desc)); rdata->last_head = MGB_NEXT_RING_IDX(rdata->last_head); processed++; } return (processed); } static int mgb_isc_rxd_available(void *xsc, uint16_t rxqid, qidx_t idx, qidx_t budget) { struct mgb_softc *sc; struct mgb_ring_data *rdata; int avail = 0; sc = xsc; KASSERT(rxqid == 0, ("tried to check availability in RX Channel %d.\n", rxqid)); rdata = &sc->rx_ring_data; for (; idx != *(rdata->head_wb); idx = MGB_NEXT_RING_IDX(idx)) { avail++; /* XXX: Could verify desc is device owned here */ if (avail == budget) break; } return (avail); } static int mgb_isc_rxd_pkt_get(void *xsc, if_rxd_info_t ri) { struct mgb_softc *sc; struct mgb_ring_data *rdata; struct mgb_ring_desc rxd; int total_len; KASSERT(ri->iri_qsidx == 0, ("tried to check availability in RX Channel %d\n", ri->iri_qsidx)); sc = xsc; total_len = 0; rdata = &sc->rx_ring_data; while (*(rdata->head_wb) != rdata->last_head) { /* copy ring desc and do swapping */ rxd = rdata->ring[rdata->last_head]; rxd.ctl = le32toh(rxd.ctl); rxd.addr.low = le32toh(rxd.ctl); rxd.addr.high = le32toh(rxd.ctl); rxd.sts = le32toh(rxd.ctl); if ((rxd.ctl & MGB_DESC_CTL_OWN) != 0) { device_printf(sc->dev, "Tried to read descriptor ... " "found that it's owned by the driver\n"); return (EINVAL); } if ((rxd.ctl & MGB_RX_DESC_CTL_FS) == 0) { device_printf(sc->dev, "Tried to read descriptor ... " "found that FS is not set.\n"); device_printf(sc->dev, "Tried to read descriptor ... that it FS is not set.\n"); return (EINVAL); } /* XXX: Multi-packet support */ if ((rxd.ctl & MGB_RX_DESC_CTL_LS) == 0) { device_printf(sc->dev, "Tried to read descriptor ... " "found that LS is not set. (Multi-buffer packets not yet supported)\n"); return (EINVAL); } ri->iri_frags[0].irf_flid = 0; ri->iri_frags[0].irf_idx = rdata->last_head; ri->iri_frags[0].irf_len = MGB_DESC_GET_FRAME_LEN(&rxd); total_len += ri->iri_frags[0].irf_len; rdata->last_head = MGB_NEXT_RING_IDX(rdata->last_head); break; } ri->iri_nfrags = 1; ri->iri_len = total_len; return (0); } static void mgb_isc_rxd_refill(void *xsc, if_rxd_update_t iru) { struct mgb_softc *sc; struct mgb_ring_data *rdata; struct mgb_ring_desc *rxd; uint64_t *paddrs; qidx_t *idxs; qidx_t idx; int count, len; count = iru->iru_count; len = iru->iru_buf_size; idxs = iru->iru_idxs; paddrs = iru->iru_paddrs; KASSERT(iru->iru_qsidx == 0, ("tried to refill RX Channel %d.\n", iru->iru_qsidx)); sc = xsc; rdata = &sc->rx_ring_data; while (count > 0) { idx = idxs[--count]; rxd = &rdata->ring[idx]; rxd->sts = 0; rxd->addr.low = htole32(CSR_TRANSLATE_ADDR_LOW32(paddrs[count])); rxd->addr.high = htole32(CSR_TRANSLATE_ADDR_HIGH32(paddrs[count])); rxd->ctl = htole32(MGB_DESC_CTL_OWN | (len & MGB_DESC_CTL_BUFLEN_MASK)); } return; } static void mgb_isc_rxd_flush(void *xsc, uint16_t rxqid, uint8_t flid, qidx_t pidx) { struct mgb_softc *sc; sc = xsc; KASSERT(rxqid == 0, ("tried to flush RX Channel %d.\n", rxqid)); /* * According to the programming guide, last_tail must be set to * the last valid RX descriptor, rather than to the one past that. * Note that this is not true for the TX ring! */ sc->rx_ring_data.last_tail = MGB_PREV_RING_IDX(pidx); CSR_WRITE_REG(sc, MGB_DMA_RX_TAIL(rxqid), sc->rx_ring_data.last_tail); return; } static int mgb_test_bar(struct mgb_softc *sc) { uint32_t id_rev, dev_id; id_rev = CSR_READ_REG(sc, 0); dev_id = id_rev >> 16; if (dev_id == MGB_LAN7430_DEVICE_ID || dev_id == MGB_LAN7431_DEVICE_ID) { return (0); } else { device_printf(sc->dev, "ID check failed.\n"); return (ENXIO); } } static int mgb_alloc_regs(struct mgb_softc *sc) { int rid; rid = PCIR_BAR(MGB_BAR); pci_enable_busmaster(sc->dev); sc->regs = bus_alloc_resource_any(sc->dev, SYS_RES_MEMORY, &rid, RF_ACTIVE); if (sc->regs == NULL) return (ENXIO); return (0); } static int mgb_release_regs(struct mgb_softc *sc) { int error = 0; if (sc->regs != NULL) error = bus_release_resource(sc->dev, SYS_RES_MEMORY, rman_get_rid(sc->regs), sc->regs); sc->regs = NULL; pci_disable_busmaster(sc->dev); return (error); } static int mgb_dma_init(struct mgb_softc *sc) { if_softc_ctx_t scctx; int ch, error = 0; scctx = iflib_get_softc_ctx(sc->ctx); for (ch = 0; ch < scctx->isc_nrxqsets; ch++) if ((error = mgb_dma_rx_ring_init(sc, ch))) goto fail; for (ch = 0; ch < scctx->isc_nrxqsets; ch++) if ((error = mgb_dma_tx_ring_init(sc, ch))) goto fail; fail: return (error); } static int mgb_dma_rx_ring_init(struct mgb_softc *sc, int channel) { struct mgb_ring_data *rdata; int ring_config, error = 0; rdata = &sc->rx_ring_data; mgb_dmac_control(sc, MGB_DMAC_RX_START, 0, DMAC_RESET); KASSERT(MGB_DMAC_STATE_IS_INITIAL(sc, MGB_DMAC_RX_START, channel), ("Trying to init channels when not in init state\n")); /* write ring address */ if (rdata->ring_bus_addr == 0) { device_printf(sc->dev, "Invalid ring bus addr.\n"); goto fail; } CSR_WRITE_REG(sc, MGB_DMA_RX_BASE_H(channel), CSR_TRANSLATE_ADDR_HIGH32(rdata->ring_bus_addr)); CSR_WRITE_REG(sc, MGB_DMA_RX_BASE_L(channel), CSR_TRANSLATE_ADDR_LOW32(rdata->ring_bus_addr)); /* write head pointer writeback address */ if (rdata->head_wb_bus_addr == 0) { device_printf(sc->dev, "Invalid head wb bus addr.\n"); goto fail; } CSR_WRITE_REG(sc, MGB_DMA_RX_HEAD_WB_H(channel), CSR_TRANSLATE_ADDR_HIGH32(rdata->head_wb_bus_addr)); CSR_WRITE_REG(sc, MGB_DMA_RX_HEAD_WB_L(channel), CSR_TRANSLATE_ADDR_LOW32(rdata->head_wb_bus_addr)); /* Enable head pointer writeback */ CSR_WRITE_REG(sc, MGB_DMA_RX_CONFIG0(channel), MGB_DMA_HEAD_WB_ENBL); ring_config = CSR_READ_REG(sc, MGB_DMA_RX_CONFIG1(channel)); /* ring size */ ring_config &= ~MGB_DMA_RING_LEN_MASK; ring_config |= (MGB_DMA_RING_SIZE & MGB_DMA_RING_LEN_MASK); /* packet padding (PAD_2 is better for IP header alignment ...) */ ring_config &= ~MGB_DMA_RING_PAD_MASK; ring_config |= (MGB_DMA_RING_PAD_0 & MGB_DMA_RING_PAD_MASK); CSR_WRITE_REG(sc, MGB_DMA_RX_CONFIG1(channel), ring_config); rdata->last_head = CSR_READ_REG(sc, MGB_DMA_RX_HEAD(channel)); mgb_fct_control(sc, MGB_FCT_RX_CTL, channel, FCT_RESET); if (error != 0) { device_printf(sc->dev, "Failed to reset RX FCT.\n"); goto fail; } mgb_fct_control(sc, MGB_FCT_RX_CTL, channel, FCT_ENABLE); if (error != 0) { device_printf(sc->dev, "Failed to enable RX FCT.\n"); goto fail; } mgb_dmac_control(sc, MGB_DMAC_RX_START, channel, DMAC_START); if (error != 0) device_printf(sc->dev, "Failed to start RX DMAC.\n"); fail: return (error); } static int mgb_dma_tx_ring_init(struct mgb_softc *sc, int channel) { struct mgb_ring_data *rdata; int ring_config, error = 0; rdata = &sc->tx_ring_data; if ((error = mgb_fct_control(sc, MGB_FCT_TX_CTL, channel, FCT_RESET))) { device_printf(sc->dev, "Failed to reset TX FCT.\n"); goto fail; } if ((error = mgb_fct_control(sc, MGB_FCT_TX_CTL, channel, FCT_ENABLE))) { device_printf(sc->dev, "Failed to enable TX FCT.\n"); goto fail; } if ((error = mgb_dmac_control(sc, MGB_DMAC_TX_START, channel, DMAC_RESET))) { device_printf(sc->dev, "Failed to reset TX DMAC.\n"); goto fail; } KASSERT(MGB_DMAC_STATE_IS_INITIAL(sc, MGB_DMAC_TX_START, channel), ("Trying to init channels in not init state\n")); /* write ring address */ if (rdata->ring_bus_addr == 0) { device_printf(sc->dev, "Invalid ring bus addr.\n"); goto fail; } CSR_WRITE_REG(sc, MGB_DMA_TX_BASE_H(channel), CSR_TRANSLATE_ADDR_HIGH32(rdata->ring_bus_addr)); CSR_WRITE_REG(sc, MGB_DMA_TX_BASE_L(channel), CSR_TRANSLATE_ADDR_LOW32(rdata->ring_bus_addr)); /* write ring size */ ring_config = CSR_READ_REG(sc, MGB_DMA_TX_CONFIG1(channel)); ring_config &= ~MGB_DMA_RING_LEN_MASK; ring_config |= (MGB_DMA_RING_SIZE & MGB_DMA_RING_LEN_MASK); CSR_WRITE_REG(sc, MGB_DMA_TX_CONFIG1(channel), ring_config); /* Enable interrupt on completion and head pointer writeback */ ring_config = (MGB_DMA_HEAD_WB_LS_ENBL | MGB_DMA_HEAD_WB_ENBL); CSR_WRITE_REG(sc, MGB_DMA_TX_CONFIG0(channel), ring_config); /* write head pointer writeback address */ if (rdata->head_wb_bus_addr == 0) { device_printf(sc->dev, "Invalid head wb bus addr.\n"); goto fail; } CSR_WRITE_REG(sc, MGB_DMA_TX_HEAD_WB_H(channel), CSR_TRANSLATE_ADDR_HIGH32(rdata->head_wb_bus_addr)); CSR_WRITE_REG(sc, MGB_DMA_TX_HEAD_WB_L(channel), CSR_TRANSLATE_ADDR_LOW32(rdata->head_wb_bus_addr)); rdata->last_head = CSR_READ_REG(sc, MGB_DMA_TX_HEAD(channel)); KASSERT(rdata->last_head == 0, ("MGB_DMA_TX_HEAD was not reset.\n")); rdata->last_tail = 0; CSR_WRITE_REG(sc, MGB_DMA_TX_TAIL(channel), rdata->last_tail); if ((error = mgb_dmac_control(sc, MGB_DMAC_TX_START, channel, DMAC_START))) device_printf(sc->dev, "Failed to start TX DMAC.\n"); fail: return (error); } static int mgb_dmac_control(struct mgb_softc *sc, int start, int channel, enum mgb_dmac_cmd cmd) { int error = 0; switch (cmd) { case DMAC_RESET: CSR_WRITE_REG(sc, MGB_DMAC_CMD, MGB_DMAC_CMD_RESET(start, channel)); error = mgb_wait_for_bits(sc, MGB_DMAC_CMD, 0, MGB_DMAC_CMD_RESET(start, channel)); break; case DMAC_START: /* * NOTE: this simplifies the logic, since it will never * try to start in STOP_PENDING, but it also increases work. */ error = mgb_dmac_control(sc, start, channel, DMAC_STOP); if (error != 0) return (error); CSR_WRITE_REG(sc, MGB_DMAC_CMD, MGB_DMAC_CMD_START(start, channel)); break; case DMAC_STOP: CSR_WRITE_REG(sc, MGB_DMAC_CMD, MGB_DMAC_CMD_STOP(start, channel)); error = mgb_wait_for_bits(sc, MGB_DMAC_CMD, MGB_DMAC_CMD_STOP(start, channel), MGB_DMAC_CMD_START(start, channel)); break; } return (error); } static int mgb_fct_control(struct mgb_softc *sc, int reg, int channel, enum mgb_fct_cmd cmd) { switch (cmd) { case FCT_RESET: CSR_WRITE_REG(sc, reg, MGB_FCT_RESET(channel)); return (mgb_wait_for_bits(sc, reg, 0, MGB_FCT_RESET(channel))); case FCT_ENABLE: CSR_WRITE_REG(sc, reg, MGB_FCT_ENBL(channel)); return (0); case FCT_DISABLE: CSR_WRITE_REG(sc, reg, MGB_FCT_DSBL(channel)); return (mgb_wait_for_bits(sc, reg, 0, MGB_FCT_ENBL(channel))); } } static int mgb_hw_teardown(struct mgb_softc *sc) { int err = 0; /* Stop MAC */ CSR_CLEAR_REG(sc, MGB_MAC_RX, MGB_MAC_ENBL); CSR_WRITE_REG(sc, MGB_MAC_TX, MGB_MAC_ENBL); if ((err = mgb_wait_for_bits(sc, MGB_MAC_RX, MGB_MAC_DSBL, 0))) return (err); if ((err = mgb_wait_for_bits(sc, MGB_MAC_TX, MGB_MAC_DSBL, 0))) return (err); return (err); } static int mgb_hw_init(struct mgb_softc *sc) { int error = 0; error = mgb_hw_reset(sc); if (error != 0) goto fail; mgb_mac_init(sc); error = mgb_phy_reset(sc); if (error != 0) goto fail; error = mgb_dmac_reset(sc); if (error != 0) goto fail; fail: return (error); } static int mgb_hw_reset(struct mgb_softc *sc) { CSR_UPDATE_REG(sc, MGB_HW_CFG, MGB_LITE_RESET); return (mgb_wait_for_bits(sc, MGB_HW_CFG, 0, MGB_LITE_RESET)); } static int mgb_mac_init(struct mgb_softc *sc) { /** * enable automatic duplex detection and * automatic speed detection */ CSR_UPDATE_REG(sc, MGB_MAC_CR, MGB_MAC_ADD_ENBL | MGB_MAC_ASD_ENBL); CSR_UPDATE_REG(sc, MGB_MAC_TX, MGB_MAC_ENBL); CSR_UPDATE_REG(sc, MGB_MAC_RX, MGB_MAC_ENBL); return (MGB_STS_OK); } static int mgb_phy_reset(struct mgb_softc *sc) { CSR_UPDATE_BYTE(sc, MGB_PMT_CTL, MGB_PHY_RESET); if (mgb_wait_for_bits(sc, MGB_PMT_CTL, 0, MGB_PHY_RESET) == MGB_STS_TIMEOUT) return (MGB_STS_TIMEOUT); return (mgb_wait_for_bits(sc, MGB_PMT_CTL, MGB_PHY_READY, 0)); } static int mgb_dmac_reset(struct mgb_softc *sc) { CSR_WRITE_REG(sc, MGB_DMAC_CMD, MGB_DMAC_RESET); return (mgb_wait_for_bits(sc, MGB_DMAC_CMD, 0, MGB_DMAC_RESET)); } static int mgb_wait_for_bits(struct mgb_softc *sc, int reg, int set_bits, int clear_bits) { int i, val; i = 0; do { /* * XXX: Datasheets states delay should be > 5 microseconds * for device reset. */ DELAY(100); val = CSR_READ_REG(sc, reg); if ((val & set_bits) == set_bits && (val & clear_bits) == 0) return (MGB_STS_OK); } while (i++ < MGB_TIMEOUT); return (MGB_STS_TIMEOUT); } static void mgb_get_ethaddr(struct mgb_softc *sc, struct ether_addr *dest) { CSR_READ_REG_BYTES(sc, MGB_MAC_ADDR_BASE_L, &dest->octet[0], 4); CSR_READ_REG_BYTES(sc, MGB_MAC_ADDR_BASE_H, &dest->octet[4], 2); } static int mgb_miibus_readreg(device_t dev, int phy, int reg) { struct mgb_softc *sc; int mii_access; sc = iflib_get_softc(device_get_softc(dev)); if (mgb_wait_for_bits(sc, MGB_MII_ACCESS, 0, MGB_MII_BUSY) == MGB_STS_TIMEOUT) return (EIO); mii_access = (phy & MGB_MII_PHY_ADDR_MASK) << MGB_MII_PHY_ADDR_SHIFT; mii_access |= (reg & MGB_MII_REG_ADDR_MASK) << MGB_MII_REG_ADDR_SHIFT; mii_access |= MGB_MII_BUSY | MGB_MII_READ; CSR_WRITE_REG(sc, MGB_MII_ACCESS, mii_access); if (mgb_wait_for_bits(sc, MGB_MII_ACCESS, 0, MGB_MII_BUSY) == MGB_STS_TIMEOUT) return (EIO); return (CSR_READ_2_BYTES(sc, MGB_MII_DATA)); } static int mgb_miibus_writereg(device_t dev, int phy, int reg, int data) { struct mgb_softc *sc; int mii_access; sc = iflib_get_softc(device_get_softc(dev)); if (mgb_wait_for_bits(sc, MGB_MII_ACCESS, 0, MGB_MII_BUSY) == MGB_STS_TIMEOUT) return (EIO); mii_access = (phy & MGB_MII_PHY_ADDR_MASK) << MGB_MII_PHY_ADDR_SHIFT; mii_access |= (reg & MGB_MII_REG_ADDR_MASK) << MGB_MII_REG_ADDR_SHIFT; mii_access |= MGB_MII_BUSY | MGB_MII_WRITE; CSR_WRITE_REG(sc, MGB_MII_DATA, data); CSR_WRITE_REG(sc, MGB_MII_ACCESS, mii_access); if (mgb_wait_for_bits(sc, MGB_MII_ACCESS, 0, MGB_MII_BUSY) == MGB_STS_TIMEOUT) return (EIO); return (0); } /* XXX: May need to lock these up */ static void mgb_miibus_statchg(device_t dev) { struct mgb_softc *sc; struct mii_data *miid; sc = iflib_get_softc(device_get_softc(dev)); miid = device_get_softc(sc->miibus); /* Update baudrate in iflib */ sc->baudrate = ifmedia_baudrate(miid->mii_media_active); iflib_link_state_change(sc->ctx, sc->link_state, sc->baudrate); } static void mgb_miibus_linkchg(device_t dev) { struct mgb_softc *sc; struct mii_data *miid; int link_state; sc = iflib_get_softc(device_get_softc(dev)); miid = device_get_softc(sc->miibus); /* XXX: copied from miibus_linkchg **/ if (miid->mii_media_status & IFM_AVALID) { if (miid->mii_media_status & IFM_ACTIVE) link_state = LINK_STATE_UP; else link_state = LINK_STATE_DOWN; } else link_state = LINK_STATE_UNKNOWN; sc->link_state = link_state; iflib_link_state_change(sc->ctx, sc->link_state, sc->baudrate); } diff --git a/sys/net/ifdi_if.m b/sys/net/ifdi_if.m index 6e8661920394..fb36b898c1d0 100644 --- a/sys/net/ifdi_if.m +++ b/sys/net/ifdi_if.m @@ -1,468 +1,468 @@ # # Copyright (c) 2014-2018, Matthew Macy (mmacy@mattmacy.io) # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # 2. Neither the name of Matthew Macy nor the names of its # contributors may be used to endorse or promote products derived from # this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # #include #include #include #include #include #include #include #include #include #include #include #include #include INTERFACE ifdi; CODE { static void null_void_op(if_ctx_t _ctx __unused) { } static int null_knlist_add(if_ctx_t _ctx __unused, struct knote *_kn) { return (0); } static int null_knote_event(if_ctx_t _ctx __unused, struct knote *_kn, int _hint) { return (0); } static void null_timer_op(if_ctx_t _ctx __unused, uint16_t _qsidx __unused) { } static int null_int_op(if_ctx_t _ctx __unused) { return (0); } static int null_int_int_op(if_ctx_t _ctx __unused, int arg0 __unused) { return (ENOTSUP); } static int null_queue_intr_enable(if_ctx_t _ctx __unused, uint16_t _qid __unused) { return (ENOTSUP); } static void null_led_func(if_ctx_t _ctx __unused, int _onoff __unused) { } static void null_vlan_register_op(if_ctx_t _ctx __unused, uint16_t vtag __unused) { } static int null_q_setup(if_ctx_t _ctx __unused, uint32_t _qid __unused) { return (0); } static int null_i2c_req(if_ctx_t _sctx __unused, struct ifi2creq *_i2c __unused) { return (ENOTSUP); } static int null_sysctl_int_delay(if_ctx_t _sctx __unused, if_int_delay_info_t _iidi __unused) { return (0); } static int null_iov_init(if_ctx_t _ctx __unused, uint16_t num_vfs __unused, const nvlist_t *params __unused) { return (ENOTSUP); } static int null_vf_add(if_ctx_t _ctx __unused, uint16_t num_vfs __unused, const nvlist_t *params __unused) { return (ENOTSUP); } static int null_priv_ioctl(if_ctx_t _ctx __unused, u_long command, caddr_t *data __unused) { return (ENOTSUP); } static void null_media_status(if_ctx_t ctx __unused, struct ifmediareq *ifmr) { ifmr->ifm_status = IFM_AVALID | IFM_ACTIVE; ifmr->ifm_active = IFM_ETHER | IFM_25G_ACC | IFM_FDX; } static int null_cloneattach(if_ctx_t ctx __unused, struct if_clone *ifc __unused, const char *name __unused, caddr_t params __unused) { return (0); } static void null_rx_clset(if_ctx_t _ctx __unused, uint16_t _flid __unused, uint16_t _qid __unused, caddr_t *_sdcl __unused) { } static void null_object_info_get(if_ctx_t ctx __unused, void *data __unused, int size __unused) { } static int default_mac_set(if_ctx_t ctx, const uint8_t *mac) { struct ifnet *ifp = iflib_get_ifp(ctx); struct sockaddr_dl *sdl; if (ifp && ifp->if_addr) { sdl = (struct sockaddr_dl *)ifp->if_addr->ifa_addr; MPASS(sdl->sdl_type == IFT_ETHER); memcpy(LLADDR(sdl), mac, ETHER_ADDR_LEN); } return (0); } static bool null_needs_restart(if_ctx_t _ctx __unused, enum iflib_restart_event _event __unused) { - return (true); + return (false); } }; # # kevent interfaces # METHOD int knlist_add { if_ctx_t _ctx; struct knote *_kn; } DEFAULT null_knlist_add; METHOD int knote_event { if_ctx_t _ctx; struct knote *_kn; int hint; } DEFAULT null_knote_event; # # query # METHOD int object_info_get { if_ctx_t _ctx; void *data; int size; } DEFAULT null_object_info_get; # # bus interfaces # METHOD int attach_pre { if_ctx_t _ctx; } DEFAULT null_int_op; METHOD int attach_post { if_ctx_t _ctx; } DEFAULT null_int_op; METHOD int reinit_pre { if_ctx_t _ctx; } DEFAULT null_int_op; METHOD int reinit_post { if_ctx_t _ctx; } DEFAULT null_int_op; METHOD int cloneattach { if_ctx_t _ctx; struct if_clone *_ifc; const char *_name; caddr_t params; } DEFAULT null_cloneattach; METHOD int detach { if_ctx_t _ctx; }; METHOD int suspend { if_ctx_t _ctx; } DEFAULT null_int_op; METHOD int shutdown { if_ctx_t _ctx; } DEFAULT null_int_op; METHOD int resume { if_ctx_t _ctx; } DEFAULT null_int_op; # # downcall to driver to allocate its # own queue state and tie it to the parent # METHOD int tx_queues_alloc { if_ctx_t _ctx; caddr_t *_vaddrs; uint64_t *_paddrs; int ntxqs; int ntxqsets; }; METHOD int rx_queues_alloc { if_ctx_t _ctx; caddr_t *_vaddrs; uint64_t *_paddrs; int nrxqs; int nrxqsets; }; METHOD void queues_free { if_ctx_t _ctx; } DEFAULT null_void_op; METHOD void rx_clset { if_ctx_t _ctx; uint16_t _fl; uint16_t _qsetid; caddr_t *_sdcl; } DEFAULT null_rx_clset; # # interface reset / stop # METHOD void init { if_ctx_t _ctx; }; METHOD void stop { if_ctx_t _ctx; }; # # interrupt setup and manipulation # METHOD int msix_intr_assign { if_ctx_t _sctx; int msix; } DEFAULT null_int_int_op; METHOD void intr_enable { if_ctx_t _ctx; }; METHOD void intr_disable { if_ctx_t _ctx; }; METHOD int rx_queue_intr_enable { if_ctx_t _ctx; uint16_t _qid; } DEFAULT null_queue_intr_enable; METHOD int tx_queue_intr_enable { if_ctx_t _ctx; uint16_t _qid; } DEFAULT null_queue_intr_enable; METHOD void link_intr_enable { if_ctx_t _ctx; } DEFAULT null_void_op; # # interface configuration # METHOD void multi_set { if_ctx_t _ctx; }; METHOD int mtu_set { if_ctx_t _ctx; uint32_t _mtu; }; METHOD int mac_set { if_ctx_t _ctx; const uint8_t *_mac; } DEFAULT default_mac_set; METHOD void media_set{ if_ctx_t _ctx; } DEFAULT null_void_op; METHOD int promisc_set { if_ctx_t _ctx; int _flags; }; METHOD void crcstrip_set { if_ctx_t _ctx; int _onoff; int _strip; }; # # IOV handling # METHOD void vflr_handle { if_ctx_t _ctx; } DEFAULT null_void_op; METHOD int iov_init { if_ctx_t _ctx; uint16_t num_vfs; const nvlist_t * params; } DEFAULT null_iov_init; METHOD void iov_uninit { if_ctx_t _ctx; } DEFAULT null_void_op; METHOD int iov_vf_add { if_ctx_t _ctx; uint16_t num_vfs; const nvlist_t * params; } DEFAULT null_vf_add; # # Device status # METHOD void update_admin_status { if_ctx_t _ctx; }; METHOD void media_status { if_ctx_t _ctx; struct ifmediareq *_ifm; } DEFAULT null_media_status; METHOD int media_change { if_ctx_t _ctx; } DEFAULT null_int_op; METHOD uint64_t get_counter { if_ctx_t _ctx; ift_counter cnt; }; METHOD int priv_ioctl { if_ctx_t _ctx; u_long _cmd; caddr_t _data; } DEFAULT null_priv_ioctl; # # optional methods # METHOD int i2c_req { if_ctx_t _ctx; struct ifi2creq *_req; } DEFAULT null_i2c_req; METHOD int txq_setup { if_ctx_t _ctx; uint32_t _txqid; } DEFAULT null_q_setup; METHOD int rxq_setup { if_ctx_t _ctx; uint32_t _txqid; } DEFAULT null_q_setup; METHOD void timer { if_ctx_t _ctx; uint16_t _txqid; } DEFAULT null_timer_op; METHOD void watchdog_reset { if_ctx_t _ctx; } DEFAULT null_void_op; METHOD void watchdog_reset_queue { if_ctx_t _ctx; uint16_t _q; } DEFAULT null_timer_op; METHOD void led_func { if_ctx_t _ctx; int _onoff; } DEFAULT null_led_func; METHOD void vlan_register { if_ctx_t _ctx; uint16_t _vtag; } DEFAULT null_vlan_register_op; METHOD void vlan_unregister { if_ctx_t _ctx; uint16_t _vtag; } DEFAULT null_vlan_register_op; METHOD int sysctl_int_delay { if_ctx_t _sctx; if_int_delay_info_t _iidi; } DEFAULT null_sysctl_int_delay; METHOD void debug { if_ctx_t _ctx; } DEFAULT null_void_op; METHOD bool needs_restart { if_ctx_t _ctx; enum iflib_restart_event _event; } DEFAULT null_needs_restart;