Module Name: src Committed By: msaitoh Date: Sat Jun 13 15:47:58 UTC 2015
Modified Files: src/sys/dev/pci: if_wm.c if_wmreg.h Log Message: Add MSI/MSI-X support written by Kengo Nakahara. Some old devices' support is written by me. It's disabled by default. If you'd like to use, define WM_MSI_MSIX. Tested with: 8254[3405617] (INTx even if it has MSI CAP because of a errata) 8257[12], 82583 ICH8, ICH10, PCH2, PCH_LPT(I21[78]) (MSI) 8257[456], 82580, I35[04], I21[01] (MSI-X) Not tested: 82542, 82573, 80003, ICH9, PCH, To generate a diff of this commit: cvs rdiff -u -r1.334 -r1.335 src/sys/dev/pci/if_wm.c cvs rdiff -u -r1.77 -r1.78 src/sys/dev/pci/if_wmreg.h Please note that diffs are not public domain; they are subject to the copyright notices on the relevant files.
Modified files: Index: src/sys/dev/pci/if_wm.c diff -u src/sys/dev/pci/if_wm.c:1.334 src/sys/dev/pci/if_wm.c:1.335 --- src/sys/dev/pci/if_wm.c:1.334 Fri Jun 12 04:40:28 2015 +++ src/sys/dev/pci/if_wm.c Sat Jun 13 15:47:58 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: if_wm.c,v 1.334 2015/06/12 04:40:28 msaitoh Exp $ */ +/* $NetBSD: if_wm.c,v 1.335 2015/06/13 15:47:58 msaitoh Exp $ */ /* * Copyright (c) 2001, 2002, 2003, 2004 Wasabi Systems, Inc. @@ -81,7 +81,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.334 2015/06/12 04:40:28 msaitoh Exp $"); +__KERNEL_RCSID(0, "$NetBSD: if_wm.c,v 1.335 2015/06/13 15:47:58 msaitoh Exp $"); #ifdef _KERNEL_OPT #include "opt_net_mpsafe.h" @@ -154,6 +154,31 @@ int wm_debug = WM_DEBUG_TX | WM_DEBUG_RX #define WM_MPSAFE 1 #endif +#ifdef __HAVE_PCI_MSI_MSIX +#if 0 /* off by default */ +#define WM_MSI_MSIX 1 +#endif +#endif + +/* + * This device driver divides interrupt to TX, RX and link state. + * Each MSI-X vector indexes are below. + */ +#define WM_NINTR 3 +#define WM_TX_INTR_INDEX 0 +#define WM_RX_INTR_INDEX 1 +#define WM_LINK_INTR_INDEX 2 +#define WM_MAX_NINTR WM_NINTR + +/* + * This device driver set affinity to each interrupts like below (round-robin). + * If the number CPUs is less than the number of interrupts, this driver usase + * the same CPU for multiple interrupts. + */ +#define WM_TX_INTR_CPUID 0 +#define WM_RX_INTR_CPUID 1 +#define WM_LINK_INTR_CPUID 2 + /* * Transmit descriptor list size. Due to errata, we can only have * 256 hardware descriptors in the ring on < 82544, but we use 4096 @@ -295,7 +320,13 @@ struct wm_softc { int sc_flowflags; /* 802.3x flow control flags */ int sc_align_tweak; - void *sc_ih; /* interrupt cookie */ + void *sc_ihs[WM_MAX_NINTR]; /* + * interrupt cookie. + * legacy and msi use sc_ihs[0]. + */ + pci_intr_handle_t *sc_intrs; /* legacy and msi use sc_intrs[0] */ + int sc_nintrs; /* number of interrupts */ + callout_t sc_tick_ch; /* tick callout */ bool sc_stopping; @@ -593,13 +624,18 @@ static int wm_nq_tx_offload(struct wm_so static void wm_nq_start(struct ifnet *); static void wm_nq_start_locked(struct ifnet *); /* Interrupt */ -static void wm_txintr(struct wm_softc *); -static void wm_rxintr(struct wm_softc *); +static int wm_txeof(struct wm_softc *); +static void wm_rxeof(struct wm_softc *); static void wm_linkintr_gmii(struct wm_softc *, uint32_t); static void wm_linkintr_tbi(struct wm_softc *, uint32_t); static void wm_linkintr_serdes(struct wm_softc *, uint32_t); static void wm_linkintr(struct wm_softc *, uint32_t); -static int wm_intr(void *); +static int wm_intr_legacy(void *); +#ifdef WM_MSI_MSIX +static int wm_txintr_msix(void *); +static int wm_rxintr_msix(void *); +static int wm_linkintr_msix(void *); +#endif /* * Media related. @@ -1368,7 +1404,11 @@ wm_attach(device_t parent, device_t self prop_dictionary_t dict; struct ifnet *ifp = &sc->sc_ethercom.ec_if; pci_chipset_tag_t pc = pa->pa_pc; +#ifndef WM_MSI_MSIX pci_intr_handle_t ih; +#else + bool intr_established = false; +#endif const char *intrstr = NULL; const char *eetype, *xname; bus_space_tag_t memt; @@ -1424,6 +1464,19 @@ wm_attach(device_t parent, device_t self sc->sc_type = WM_T_82542_2_0; } + /* + * Disable MSI for Errata: + * "Message Signaled Interrupt Feature May Corrupt Write Transactions" + * + * 82544: Errata 25 + * 82540: Errata 6 (easy to reproduce device timeout) + * 82545: Errata 4 (easy to reproduce device timeout) + * 82546: Errata 26 (easy to reproduce device timeout) + * 82541: Errata 7 (easy to reproduce device timeout) + */ + if (sc->sc_type <= WM_T_82541_2) + pa->pa_flags &= ~PCI_FLAGS_MSI_OKAY; + if ((sc->sc_type == WM_T_82575) || (sc->sc_type == WM_T_82576) || (sc->sc_type == WM_T_82580) || (sc->sc_type == WM_T_I350) || (sc->sc_type == WM_T_I354) @@ -1517,6 +1570,7 @@ wm_attach(device_t parent, device_t self return; } +#ifndef WM_MSI_MSIX /* * Map and establish our interrupt. */ @@ -1528,8 +1582,8 @@ wm_attach(device_t parent, device_t self #ifdef WM_MPSAFE pci_intr_setattr(pc, &ih, PCI_INTR_MPSAFE, true); #endif - sc->sc_ih = pci_intr_establish(pc, ih, IPL_NET, wm_intr, sc); - if (sc->sc_ih == NULL) { + sc->sc_ihs[0] = pci_intr_establish(pc, ih, IPL_NET, wm_intr_legacy,sc); + if (sc->sc_ihs[0] == NULL) { aprint_error_dev(sc->sc_dev, "unable to establish interrupt"); if (intrstr != NULL) aprint_error(" at %s", intrstr); @@ -1537,6 +1591,171 @@ wm_attach(device_t parent, device_t self return; } aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", intrstr); + sc->sc_nintrs = 1; +#else /* WM_MSI_MSIX */ + if (pci_msix_alloc_exact(pa, &sc->sc_intrs, WM_NINTR) == 0) { + /* 1st, try to use MSI-X */ + void *vih; + kcpuset_t *affinity; + + kcpuset_create(&affinity, false); + + /* + * for TX + */ + intrstr = pci_intr_string(pc, sc->sc_intrs[WM_TX_INTR_INDEX], + intrbuf, sizeof(intrbuf)); +#ifdef WM_MPSAFE + pci_intr_setattr(pc, &sc->sc_intrs[WM_TX_INTR_INDEX], + PCI_INTR_MPSAFE, true); +#endif + vih = pci_intr_establish(pc, sc->sc_intrs[WM_TX_INTR_INDEX], + IPL_NET, wm_txintr_msix, sc); + if (vih == NULL) { + aprint_error_dev(sc->sc_dev, + "unable to establish MSI-X(for TX)%s%s\n", + intrstr ? " at " : "", intrstr ? intrstr : ""); + pci_intr_release(sc->sc_pc, sc->sc_intrs, + WM_NINTR); + goto msi; + } + kcpuset_zero(affinity); + /* Round-robin affinity */ + kcpuset_set(affinity, WM_TX_INTR_CPUID % ncpu); + error = pci_intr_distribute(vih, affinity, NULL); + if (error == 0) { + aprint_normal_dev(sc->sc_dev, + "for TX interrupting at %s affinity to %u\n", + intrstr, WM_TX_INTR_CPUID % ncpu); + } else { + aprint_normal_dev(sc->sc_dev, + "for TX interrupting at %s\n", + intrstr); + } + sc->sc_ihs[WM_TX_INTR_INDEX] = vih; + + /* + * for RX + */ + intrstr = pci_intr_string(pc, sc->sc_intrs[WM_RX_INTR_INDEX], + intrbuf, sizeof(intrbuf)); +#ifdef WM_MPSAFE + pci_intr_setattr(pc, &sc->sc_intrs[WM_RX_INTR_INDEX], + PCI_INTR_MPSAFE, true); +#endif + vih = pci_intr_establish(pc, sc->sc_intrs[WM_RX_INTR_INDEX], + IPL_NET, wm_rxintr_msix, sc); + if (vih == NULL) { + aprint_error_dev(sc->sc_dev, + "unable to establish MSI-X(for RX)%s%s\n", + intrstr ? " at " : "", intrstr ? intrstr : ""); + pci_intr_release(sc->sc_pc, sc->sc_intrs, + WM_NINTR); + goto msi; + } + kcpuset_zero(affinity); + kcpuset_set(affinity, WM_RX_INTR_CPUID % ncpu); + error = pci_intr_distribute(vih, affinity, NULL); + if (error == 0) { + aprint_normal_dev(sc->sc_dev, + "for RX interrupting at %s affinity to %u\n", + intrstr, WM_RX_INTR_CPUID % ncpu); + } else { + aprint_normal_dev(sc->sc_dev, + "for RX interrupting at %s\n", + intrstr); + } + sc->sc_ihs[WM_RX_INTR_INDEX] = vih; + + /* + * for link state changing + */ + intrstr = pci_intr_string(pc, sc->sc_intrs[WM_LINK_INTR_INDEX], + intrbuf, sizeof(intrbuf)); +#ifdef WM_MPSAFE + pci_intr_setattr(pc, &sc->sc_intrs[WM_LINK_INTR_INDEX], + PCI_INTR_MPSAFE, true); +#endif + vih = pci_intr_establish(pc, sc->sc_intrs[WM_LINK_INTR_INDEX], + IPL_NET, wm_linkintr_msix, sc); + if (vih == NULL) { + aprint_error_dev(sc->sc_dev, + "unable to establish MSI-X(for LINK)%s%s\n", + intrstr ? " at " : "", intrstr ? intrstr : ""); + pci_intr_release(sc->sc_pc, sc->sc_intrs, + WM_NINTR); + goto msi; + } + kcpuset_zero(affinity); + kcpuset_set(affinity, WM_LINK_INTR_CPUID % ncpu); + error = pci_intr_distribute(vih, affinity, NULL); + if (error == 0) { + aprint_normal_dev(sc->sc_dev, + "for LINK interrupting at %s affinity to %u\n", + intrstr, WM_LINK_INTR_CPUID % ncpu); + } else { + aprint_normal_dev(sc->sc_dev, + "for LINK interrupting at %s\n", + intrstr); + } + sc->sc_ihs[WM_LINK_INTR_INDEX] = vih; + + sc->sc_nintrs = WM_NINTR; + kcpuset_destroy(affinity); + intr_established = true; + } + +msi: + if ((intr_established == false) + && (pci_msi_alloc_exact(pa, &sc->sc_intrs, 1) == 0)) { + /* 2nd, try to use MSI */ + intrstr = pci_intr_string(pc, sc->sc_intrs[0], intrbuf, + sizeof(intrbuf)); +#ifdef WM_MPSAFE + pci_intr_setattr(pc, &sc->sc_intrs[0], PCI_INTR_MPSAFE, true); +#endif + sc->sc_ihs[0] = pci_intr_establish(pc, sc->sc_intrs[0], + IPL_NET, wm_intr_legacy, sc); + if (sc->sc_ihs[0] == NULL) { + aprint_error_dev(sc->sc_dev, "unable to establish MSI\n"); + pci_intr_release(sc->sc_pc, sc->sc_intrs, + 1); + goto intx; + } + aprint_normal_dev(sc->sc_dev, "MSI at %s\n", intrstr); + + sc->sc_nintrs = 1; + intr_established = true; + } + +intx: + if ((intr_established == false) + && (pci_intx_alloc(pa, &sc->sc_intrs) == 0)) { + /* Last, try to use INTx */ + intrstr = pci_intr_string(pc, sc->sc_intrs[0], intrbuf, + sizeof(intrbuf)); +#ifdef WM_MPSAFE + pci_intr_setattr(pc, &sc->sc_intrs[0], PCI_INTR_MPSAFE, true); +#endif + sc->sc_ihs[0] = pci_intr_establish(pc, sc->sc_intrs[0], + IPL_NET, wm_intr_legacy, sc); + if (sc->sc_ihs[0] == NULL) { + aprint_error_dev(sc->sc_dev, "unable to establish MSI\n"); + pci_intr_release(sc->sc_pc, sc->sc_intrs, 1); + goto int_failed; + } + aprint_normal_dev(sc->sc_dev, "interrupting at %s\n", intrstr); + + sc->sc_nintrs = 1; + intr_established = true; + } + +int_failed: + if (intr_established == false) { + aprint_error_dev(sc->sc_dev, "failed to allocate interrput\n"); + return; + } +#endif /* WM_MSI_MSIX */ /* * Check the function ID (unit number of the chip). @@ -2573,10 +2792,15 @@ wm_detach(device_t self, int flags __unu bus_dmamem_free(sc->sc_dmat, &sc->sc_cd_seg, sc->sc_cd_rseg); /* Disestablish the interrupt handler */ - if (sc->sc_ih != NULL) { - pci_intr_disestablish(sc->sc_pc, sc->sc_ih); - sc->sc_ih = NULL; + for (i = 0; i < sc->sc_nintrs; i++) { + if (sc->sc_ihs[i] != NULL) { + pci_intr_disestablish(sc->sc_pc, sc->sc_ihs[i]); + sc->sc_ihs[i] = NULL; + } } +#ifdef WM_MSI_MSIX + pci_intr_release(sc->sc_pc, sc->sc_intrs, sc->sc_nintrs); +#endif /* WM_MSI_MSIX */ /* Unmap the registers */ if (sc->sc_ss) { @@ -2636,7 +2860,7 @@ wm_watchdog(struct ifnet *ifp) * before we report an error. */ WM_TX_LOCK(sc); - wm_txintr(sc); + wm_txeof(sc); WM_TX_UNLOCK(sc); if (sc->sc_txfree != WM_NTXDESC(sc)) { @@ -3622,6 +3846,14 @@ wm_reset(struct wm_softc *sc) /* Clear interrupt */ CSR_WRITE(sc, WMREG_IMC, 0xffffffffU); + if (sc->sc_nintrs > 1) { + if (sc->sc_type != WM_T_82574) { + CSR_WRITE(sc, WMREG_EIMC, 0xffffffffU); + CSR_WRITE(sc, WMREG_EIAC, 0); + } else { + CSR_WRITE(sc, WMREG_EIAC_82574, 0); + } + } /* Stop the transmit and receive processes. */ CSR_WRITE(sc, WMREG_RCTL, 0); @@ -3860,6 +4092,13 @@ wm_reset(struct wm_softc *sc) /* Clear any pending interrupt events. */ CSR_WRITE(sc, WMREG_IMC, 0xffffffffU); reg = CSR_READ(sc, WMREG_ICR); + if (sc->sc_nintrs > 1) { + if (sc->sc_type != WM_T_82574) { + CSR_WRITE(sc, WMREG_EIMC, 0xffffffffU); + CSR_WRITE(sc, WMREG_EIAC, 0); + } else + CSR_WRITE(sc, WMREG_EIAC_82574, 0); + } /* reload sc_ctrl */ sc->sc_ctrl = CSR_READ(sc, WMREG_CTRL); @@ -4289,11 +4528,124 @@ wm_init_locked(struct ifnet *ifp) reg |= RXCSUM_IPV6OFL | RXCSUM_TUOFL; CSR_WRITE(sc, WMREG_RXCSUM, reg); + /* Set up MSI-X */ + if (sc->sc_nintrs > 1) { + uint32_t ivar; + + if (sc->sc_type == WM_T_82575) { + /* Interrupt control */ + reg = CSR_READ(sc, WMREG_CTRL_EXT); + reg |= CTRL_EXT_PBA | CTRL_EXT_EIAME | CTRL_EXT_NSICR; + CSR_WRITE(sc, WMREG_CTRL_EXT, reg); + + /* TX */ + CSR_WRITE(sc, WMREG_MSIXBM(WM_TX_INTR_INDEX), + EITR_TX_QUEUE0); + /* RX */ + CSR_WRITE(sc, WMREG_MSIXBM(WM_RX_INTR_INDEX), + EITR_RX_QUEUE0); + /* Link status */ + CSR_WRITE(sc, WMREG_MSIXBM(WM_LINK_INTR_INDEX), + EITR_OTHER); + } else if (sc->sc_type == WM_T_82574) { + /* Interrupt control */ + reg = CSR_READ(sc, WMREG_CTRL_EXT); + reg |= CTRL_EXT_PBA | CTRL_EXT_EIAME; + CSR_WRITE(sc, WMREG_CTRL_EXT, reg); + + /* TX, RX and Link status */ + ivar = __SHIFTIN((IVAR_VALID_82574 | WM_TX_INTR_INDEX), + IVAR_TX_MASK_Q_82574(0)); + ivar |= __SHIFTIN((IVAR_VALID_82574 |WM_RX_INTR_INDEX), + IVAR_RX_MASK_Q_82574(0)); + ivar |=__SHIFTIN((IVAR_VALID_82574|WM_LINK_INTR_INDEX), + IVAR_OTHER_MASK); + CSR_WRITE(sc, WMREG_IVAR, ivar | IVAR_INT_ON_ALL_WB); + } else { + /* Interrupt control */ + CSR_WRITE(sc, WMREG_GPIE, GPIE_NSICR + | GPIE_MULTI_MSIX | GPIE_EIAME + | GPIE_PBA); + + switch (sc->sc_type) { + case WM_T_82580: + case WM_T_I350: + case WM_T_I354: + case WM_T_I210: + case WM_T_I211: + /* TX */ + ivar = CSR_READ(sc, WMREG_IVAR_Q(0)); + ivar &= ~IVAR_TX_MASK_Q(0); + ivar |= __SHIFTIN( + (WM_TX_INTR_INDEX | IVAR_VALID), + IVAR_TX_MASK_Q(0)); + CSR_WRITE(sc, WMREG_IVAR_Q(0), ivar); + + /* RX */ + ivar = CSR_READ(sc, WMREG_IVAR_Q(0)); + ivar &= ~IVAR_RX_MASK_Q(0); + ivar |= __SHIFTIN( + (WM_RX_INTR_INDEX | IVAR_VALID), + IVAR_RX_MASK_Q(0)); + CSR_WRITE(sc, WMREG_IVAR_Q(0), ivar); + break; + case WM_T_82576: + /* TX */ + ivar = CSR_READ(sc, WMREG_IVAR_Q_82576(0)); + ivar &= ~IVAR_TX_MASK_Q_82576(0); + ivar |= __SHIFTIN( + (WM_TX_INTR_INDEX | IVAR_VALID), + IVAR_TX_MASK_Q_82576(0)); + CSR_WRITE(sc, WMREG_IVAR_Q_82576(0), ivar); + + /* RX */ + ivar = CSR_READ(sc, WMREG_IVAR_Q_82576(0)); + ivar &= ~IVAR_RX_MASK_Q_82576(0); + ivar |= __SHIFTIN( + (WM_RX_INTR_INDEX | IVAR_VALID), + IVAR_RX_MASK_Q_82576(0)); + CSR_WRITE(sc, WMREG_IVAR_Q_82576(0), ivar); + break; + default: + break; + } + + /* Link status */ + ivar = __SHIFTIN((WM_LINK_INTR_INDEX | IVAR_VALID), + IVAR_MISC_OTHER); + CSR_WRITE(sc, WMREG_IVAR_MISC, ivar); + } + } + /* Set up the interrupt registers. */ CSR_WRITE(sc, WMREG_IMC, 0xffffffffU); sc->sc_icr = ICR_TXDW | ICR_LSC | ICR_RXSEQ | ICR_RXDMT0 | ICR_RXO | ICR_RXT0; - CSR_WRITE(sc, WMREG_IMS, sc->sc_icr); + if (sc->sc_nintrs > 1) { + uint32_t mask; + switch (sc->sc_type) { + case WM_T_82574: + CSR_WRITE(sc, WMREG_EIAC_82574, + WMREG_EIAC_82574_MSIX_MASK); + sc->sc_icr |= WMREG_EIAC_82574_MSIX_MASK; + CSR_WRITE(sc, WMREG_IMS, sc->sc_icr); + break; + default: + if (sc->sc_type == WM_T_82575) + mask = EITR_RX_QUEUE0 |EITR_TX_QUEUE0 + | EITR_OTHER; + else + mask = (1 << WM_RX_INTR_INDEX) + | (1 << WM_TX_INTR_INDEX) + | (1 << WM_LINK_INTR_INDEX); + CSR_WRITE(sc, WMREG_EIAC, mask); + CSR_WRITE(sc, WMREG_EIAM, mask); + CSR_WRITE(sc, WMREG_EIMS, mask); + CSR_WRITE(sc, WMREG_IMS, ICR_LSC); + break; + } + } else + CSR_WRITE(sc, WMREG_IMS, sc->sc_icr); if ((sc->sc_type == WM_T_ICH8) || (sc->sc_type == WM_T_ICH9) || (sc->sc_type == WM_T_ICH10) || (sc->sc_type == WM_T_PCH) @@ -4496,11 +4848,18 @@ wm_stop_locked(struct ifnet *ifp, int di /* * Clear the interrupt mask to ensure the device cannot assert its * interrupt line. - * Clear sc->sc_icr to ensure wm_intr() makes no attempt to service - * any currently pending or shared interrupt. + * Clear sc->sc_icr to ensure wm_intr_legacy() makes no attempt to + * service any currently pending or shared interrupt. */ CSR_WRITE(sc, WMREG_IMC, 0xffffffffU); sc->sc_icr = 0; + if (sc->sc_nintrs > 1) { + if (sc->sc_type != WM_T_82574) { + CSR_WRITE(sc, WMREG_EIMC, 0xffffffffU); + CSR_WRITE(sc, WMREG_EIAC, 0); + } else + CSR_WRITE(sc, WMREG_EIAC_82574, 0); + } /* Release any queued transmit buffers. */ for (i = 0; i < WM_TXQUEUELEN(sc); i++) { @@ -4884,7 +5243,7 @@ wm_start_locked(struct ifnet *ifp) /* Get a work queue entry. */ if (sc->sc_txsfree < WM_TXQUEUE_GC(sc)) { - wm_txintr(sc); + wm_txeof(sc); if (sc->sc_txsfree == 0) { DPRINTF(WM_DEBUG_TX, ("%s: TX: no free job descriptors\n", @@ -5396,7 +5755,7 @@ wm_nq_start_locked(struct ifnet *ifp) /* Get a work queue entry. */ if (sc->sc_txsfree < WM_TXQUEUE_GC(sc)) { - wm_txintr(sc); + wm_txeof(sc); if (sc->sc_txsfree == 0) { DPRINTF(WM_DEBUG_TX, ("%s: TX: no free job descriptors\n", @@ -5533,7 +5892,7 @@ wm_nq_start_locked(struct ifnet *ifp) sc->sc_txdescs[nexttx].wtx_fields.wtxu_vlan = htole16(VLAN_TAG_VALUE(mtag) & 0xffff); } else { - sc->sc_txdescs[nexttx].wtx_fields.wtxu_vlan = 0; + sc->sc_txdescs[nexttx].wtx_fields.wtxu_vlan =0; } dcmdlen = 0; } else { @@ -5645,20 +6004,22 @@ wm_nq_start_locked(struct ifnet *ifp) /* Interrupt */ /* - * wm_txintr: + * wm_txeof: * * Helper; handle transmit interrupts. */ -static void -wm_txintr(struct wm_softc *sc) +static int +wm_txeof(struct wm_softc *sc) { struct ifnet *ifp = &sc->sc_ethercom.ec_if; struct wm_txsoft *txs; - uint8_t status; + bool processed = false; + int count = 0; int i; + uint8_t status; if (sc->sc_stopping) - return; + return 0; ifp->if_flags &= ~IFF_OACTIVE; @@ -5684,6 +6045,8 @@ wm_txintr(struct wm_softc *sc) break; } + processed = true; + count++; DPRINTF(WM_DEBUG_TX, ("%s: TX: job %d done: descs %d..%d\n", device_xname(sc->sc_dev), i, txs->txs_firstdesc, @@ -5726,26 +6089,32 @@ wm_txintr(struct wm_softc *sc) DPRINTF(WM_DEBUG_TX, ("%s: TX: txsdirty -> %d\n", device_xname(sc->sc_dev), i)); + if (count != 0) + rnd_add_uint32(&sc->rnd_source, count); + /* * If there are no more pending transmissions, cancel the watchdog * timer. */ if (sc->sc_txsfree == WM_TXQUEUELEN(sc)) ifp->if_timer = 0; + + return processed; } /* - * wm_rxintr: + * wm_rxeof: * * Helper; handle receive interrupts. */ static void -wm_rxintr(struct wm_softc *sc) +wm_rxeof(struct wm_softc *sc) { struct ifnet *ifp = &sc->sc_ethercom.ec_if; struct wm_rxsoft *rxs; struct mbuf *m; int i, len; + int count = 0; uint8_t status, errors; uint16_t vlantag; @@ -5769,6 +6138,7 @@ wm_rxintr(struct wm_softc *sc) break; } + count++; if (__predict_false(sc->sc_rxdiscard)) { DPRINTF(WM_DEBUG_RX, ("%s: RX: discarding contents of descriptor %d\n", @@ -5938,6 +6308,8 @@ wm_rxintr(struct wm_softc *sc) /* Update the receive pointer. */ sc->sc_rxptr = i; + if (count != 0) + rnd_add_uint32(&sc->rnd_source, count); DPRINTF(WM_DEBUG_RX, ("%s: RX: rxptr -> %d\n", device_xname(sc->sc_dev), i)); @@ -6173,23 +6545,26 @@ wm_linkintr(struct wm_softc *sc, uint32_ } /* - * wm_intr: + * wm_intr_legacy: * - * Interrupt service routine. + * Interrupt service routine for INTx and MSI. */ static int -wm_intr(void *arg) +wm_intr_legacy(void *arg) { struct wm_softc *sc = arg; struct ifnet *ifp = &sc->sc_ethercom.ec_if; - uint32_t icr; + uint32_t icr, rndval = 0; int handled = 0; + DPRINTF(WM_DEBUG_TX, + ("%s: INTx: got intr\n", device_xname(sc->sc_dev))); while (1 /* CONSTCOND */) { icr = CSR_READ(sc, WMREG_ICR); if ((icr & sc->sc_icr) == 0) break; - rnd_add_uint32(&sc->rnd_source, icr); + if (rndval == 0) + rndval = icr; WM_RX_LOCK(sc); @@ -6209,7 +6584,7 @@ wm_intr(void *arg) WM_EVCNT_INCR(&sc->sc_ev_rxintr); } #endif - wm_rxintr(sc); + wm_rxeof(sc); WM_RX_UNLOCK(sc); WM_TX_LOCK(sc); @@ -6222,7 +6597,7 @@ wm_intr(void *arg) WM_EVCNT_INCR(&sc->sc_ev_txdw); } #endif - wm_txintr(sc); + wm_txeof(sc); if (icr & (ICR_LSC|ICR_RXSEQ)) { WM_EVCNT_INCR(&sc->sc_ev_linkintr); @@ -6239,6 +6614,57 @@ wm_intr(void *arg) } } + rnd_add_uint32(&sc->rnd_source, rndval); + + if (handled) { + /* Try to get more packets going. */ + ifp->if_start(ifp); + } + + return handled; +} + +#ifdef WM_MSI_MSIX +/* + * wm_txintr_msix: + * + * Interrupt service routine for TX complete interrupt for MSI-X. + */ +static int +wm_txintr_msix(void *arg) +{ + struct wm_softc *sc = arg; + struct ifnet *ifp = &sc->sc_ethercom.ec_if; + int handled = 0; + + DPRINTF(WM_DEBUG_TX, + ("%s: TX: got Tx intr\n", device_xname(sc->sc_dev))); + + if (sc->sc_type == WM_T_82574) + CSR_WRITE(sc, WMREG_IMC, ICR_TXQ0); /* 82574 only */ + else if (sc->sc_type == WM_T_82575) + CSR_WRITE(sc, WMREG_EIMC, EITR_TX_QUEUE0); + else + CSR_WRITE(sc, WMREG_EIMC, 1 << WM_TX_INTR_INDEX); + + WM_TX_LOCK(sc); + + if (sc->sc_stopping) + goto out; + + WM_EVCNT_INCR(&sc->sc_ev_txdw); + handled = wm_txeof(sc); + +out: + WM_TX_UNLOCK(sc); + + if (sc->sc_type == WM_T_82574) + CSR_WRITE(sc, WMREG_IMS, ICR_TXQ0); /* 82574 only */ + else if (sc->sc_type == WM_T_82575) + CSR_WRITE(sc, WMREG_EIMS, EITR_TX_QUEUE0); + else + CSR_WRITE(sc, WMREG_EIMS, 1 << WM_TX_INTR_INDEX); + if (handled) { /* Try to get more packets going. */ ifp->if_start(ifp); @@ -6248,6 +6674,87 @@ wm_intr(void *arg) } /* + * wm_rxintr_msix: + * + * Interrupt service routine for RX interrupt for MSI-X. + */ +static int +wm_rxintr_msix(void *arg) +{ + struct wm_softc *sc = arg; + + DPRINTF(WM_DEBUG_TX, + ("%s: RX: got Rx intr\n", device_xname(sc->sc_dev))); + + if (sc->sc_type == WM_T_82574) + CSR_WRITE(sc, WMREG_IMC, ICR_RXQ0); /* 82574 only */ + else if (sc->sc_type == WM_T_82575) + CSR_WRITE(sc, WMREG_EIMC, EITR_RX_QUEUE0); + else + CSR_WRITE(sc, WMREG_EIMC, 1 << WM_RX_INTR_INDEX); + + WM_RX_LOCK(sc); + + if (sc->sc_stopping) + goto out; + + WM_EVCNT_INCR(&sc->sc_ev_rxintr); + wm_rxeof(sc); + +out: + WM_RX_UNLOCK(sc); + + if (sc->sc_type == WM_T_82574) + CSR_WRITE(sc, WMREG_IMS, ICR_RXQ0); + else if (sc->sc_type == WM_T_82575) + CSR_WRITE(sc, WMREG_EIMS, EITR_RX_QUEUE0); + else + CSR_WRITE(sc, WMREG_EIMS, 1 << WM_RX_INTR_INDEX); + + return 1; +} + +/* + * wm_linkintr_msix: + * + * Interrupt service routine for link status change for MSI-X. + */ +static int +wm_linkintr_msix(void *arg) +{ + struct wm_softc *sc = arg; + + DPRINTF(WM_DEBUG_TX, + ("%s: LINK: got link intr\n", device_xname(sc->sc_dev))); + + if (sc->sc_type == WM_T_82574) + CSR_WRITE(sc, WMREG_IMC, ICR_OTHER); /* 82574 only */ + else if (sc->sc_type == WM_T_82575) + CSR_WRITE(sc, WMREG_EIMC, EITR_OTHER); + else + CSR_WRITE(sc, WMREG_EIMC, 1 << WM_LINK_INTR_INDEX); + WM_TX_LOCK(sc); + if (sc->sc_stopping) + goto out; + + WM_EVCNT_INCR(&sc->sc_ev_linkintr); + wm_linkintr(sc, ICR_LSC); + +out: + WM_TX_UNLOCK(sc); + + if (sc->sc_type == WM_T_82574) + CSR_WRITE(sc, WMREG_IMS, ICR_OTHER | ICR_LSC); /* 82574 only */ + else if (sc->sc_type == WM_T_82575) + CSR_WRITE(sc, WMREG_EIMS, EITR_OTHER); + else + CSR_WRITE(sc, WMREG_EIMS, 1 << WM_LINK_INTR_INDEX); + + return 1; +} +#endif /* WM_MSI_MSIX */ + +/* * Media related. * GMII, SGMII, TBI (and SERDES) */ Index: src/sys/dev/pci/if_wmreg.h diff -u src/sys/dev/pci/if_wmreg.h:1.77 src/sys/dev/pci/if_wmreg.h:1.78 --- src/sys/dev/pci/if_wmreg.h:1.77 Sat Jun 6 04:39:12 2015 +++ src/sys/dev/pci/if_wmreg.h Sat Jun 13 15:47:58 2015 @@ -1,4 +1,4 @@ -/* $NetBSD: if_wmreg.h,v 1.77 2015/06/06 04:39:12 msaitoh Exp $ */ +/* $NetBSD: if_wmreg.h,v 1.78 2015/06/13 15:47:58 msaitoh Exp $ */ /* * Copyright (c) 2001 Wasabi Systems, Inc. @@ -299,6 +299,7 @@ struct livengood_tcpip_ctxdesc { #define EERD_DATA_SHIFT 16 /* Offset to data in EEPROM read/write registers */ #define WMREG_CTRL_EXT 0x0018 /* Extended Device Control Register */ +#define CTRL_EXT_NSICR __BIT(0) /* Non Interrupt clear on read */ #define CTRL_EXT_GPI_EN(x) (1U << (x)) /* gpin interrupt enable */ #define CTRL_EXT_SWDPINS_SHIFT 4 #define CTRL_EXT_SWDPINS_MASK 0x0d @@ -326,8 +327,10 @@ struct livengood_tcpip_ctxdesc { #define CTRL_EXT_LINK_MODE_TBI 0x00C00000 #define CTRL_EXT_LINK_MODE_PCIE_SERDES 0x00C00000 #define CTRL_EXT_PHYPDEN 0x00100000 +#define CTRL_EXT_EIAME __BIT(24) /* Extended Interrupt Auto Mask En */ #define CTRL_EXT_I2C_ENA 0x02000000 /* I2C enable */ #define CTRL_EXT_DRV_LOAD 0x10000000 +#define CTRL_EXT_PBA __BIT(31) /* PBA Support */ #define WMREG_MDIC 0x0020 /* MDI Control Register */ #define MDIC_DATA(x) ((x) & 0xffff) @@ -430,6 +433,11 @@ struct livengood_tcpip_ctxdesc { #define ICR_MDAC (1U << 9) /* MDIO access complete */ #define ICR_RXCFG (1U << 10) /* Receiving /C/ */ #define ICR_GPI(x) (1U << (x)) /* general purpose interrupts */ +#define ICR_RXQ0 __BIT(20) /* 82574: Rx queue 0 interrupt */ +#define ICR_RXQ1 __BIT(21) /* 82574: Rx queue 1 interrupt */ +#define ICR_TXQ0 __BIT(22) /* 82574: Tx queue 0 interrupt */ +#define ICR_TXQ1 __BIT(23) /* 82574: Tx queue 1 interrupt */ +#define ICR_OTHER __BIT(24) /* 82574: Other interrupt */ #define ICR_INT (1U << 31) /* device generated an interrupt */ #define WMREG_ITR 0x00c4 /* Interrupt Throttling Register */ @@ -439,12 +447,40 @@ struct livengood_tcpip_ctxdesc { #define WMREG_ICS 0x00c8 /* Interrupt Cause Set Register */ /* See ICR bits. */ +#define WMREG_IVAR 0x00e4 /* Interrupt Vector Allocation Register */ +#define WMREG_IVAR0 0x01700 /* Interrupt Vector Allocation */ +#define IVAR_ALLOC_MASK __BITS(0, 6) /* Bit 5 and 6 are reserved */ +#define IVAR_VALID __BIT(7) +/* IVAR definitions for 82580 and newer */ +#define WMREG_IVAR_Q(x) (WMREG_IVAR0 + ((x) % 2) * 4) +#define IVAR_TX_MASK_Q(x) (0x000000ff << (((x) % 2) == 0 ? 8 : 24)) +#define IVAR_RX_MASK_Q(x) (0x000000ff << (((x) % 2) == 0 ? 0 : 16)) +/* IVAR definitions for 82576 */ +#define WMREG_IVAR_Q_82576(x) (WMREG_IVAR0 + ((x) & 0x7) * 4) +#define IVAR_TX_MASK_Q_82576(x) (0x000000ff << (((x) / 8) == 0 ? 8 : 24)) +#define IVAR_RX_MASK_Q_82576(x) (0x000000ff << (((x) / 8) == 0 ? 0 : 16)) +/* IVAR definitions for 82574 */ +#define IVAR_ALLOC_MASK_82574 __BITS(0, 2) +#define IVAR_VALID_82574 __BIT(3) +#define IVAR_TX_MASK_Q_82574(x) (0x0000000f << ((x) == 0 ? 8 : 12)) +#define IVAR_RX_MASK_Q_82574(x) (0x0000000f << ((x) == 0 ? 0 : 4)) +#define IVAR_OTHER_MASK __BITS(16, 19) +#define IVAR_INT_ON_ALL_WB __BIT(31) + +#define WMREG_IVAR_MISC 0x01740 /* IVAR for other causes */ +#define IVAR_MISC_TCPTIMER __BITS(0, 7) +#define IVAR_MISC_OTHER __BITS(8, 15) + #define WMREG_IMS 0x00d0 /* Interrupt Mask Set Register */ /* See ICR bits. */ #define WMREG_IMC 0x00d8 /* Interrupt Mask Clear Register */ /* See ICR bits. */ +#define WMREG_EIAC_82574 0x00dc /* Interrupt Auto Clear Register */ +#define WMREG_EIAC_82574_MSIX_MASK (ICR_RXQ0 | ICR_RXQ1 \ + | ICR_TXQ0 | ICR_TXQ1 | ICR_OTHER) + #define WMREG_RCTL 0x0100 /* Receive Control */ #define RCTL_EN (1U << 1) /* receiver enable */ #define RCTL_SBP (1U << 2) /* store bad packets */ @@ -716,6 +752,12 @@ struct livengood_tcpip_ctxdesc { #define PBA_ECC_STAT_CLR 0x00000002 /* Clear ECC error counter */ #define PBA_ECC_INT_EN 0x00000004 /* Enable ICR bit 5 on ECC error */ +#define WMREG_GPIE 0x01514 /* General Purpose Interrupt Enable */ +#define GPIE_NSICR __BIT(0) /* Non Selective Interrupt Clear */ +#define GPIE_MULTI_MSIX __BIT(4) /* Multiple MSIX */ +#define GPIE_EIAME __BIT(30) /* Extended Interrupt Auto Mask Ena. */ +#define GPIE_PBA __BIT(31) /* PBA support */ + #define WMREG_EICS 0x01520 /* Ext. Interrupt Cause Set - WO */ #define WMREG_EIMS 0x01524 /* Ext. Interrupt Mask Set/Read - RW */ #define WMREG_EIMC 0x01528 /* Ext. Interrupt Mask Clear - WO */ @@ -724,6 +766,8 @@ struct livengood_tcpip_ctxdesc { #define WMREG_EICR 0x01580 /* Ext. Interrupt Cause Read - R/clr */ +#define WMREG_MSIXBM(x) (0x1600 + (x) * 4) /* MSI-X Allocation */ + #define EITR_RX_QUEUE0 0x00000001 /* Rx Queue 0 Interrupt */ #define EITR_RX_QUEUE1 0x00000002 /* Rx Queue 1 Interrupt */ #define EITR_RX_QUEUE2 0x00000004 /* Rx Queue 2 Interrupt */