Index: if_ath.c =================================================================== --- if_ath.c (revision 270430) +++ if_ath.c (working copy) @@ -1811,6 +1811,10 @@ AH_FALSE, &status); ath_reset_keycache(sc); + ATH_RX_LOCK(sc); + sc->sc_rx_stopped = 1; + ATH_RX_UNLOCK(sc); + /* Let DFS at it in case it's a DFS channel */ ath_dfs_radar_enable(sc, ic->ic_curchan); @@ -2015,44 +2019,46 @@ if (status & HAL_INT_RXEOL) { int imask; ATH_KTR(sc, ATH_KTR_ERROR, 0, "ath_intr: RXEOL"); - ATH_PCU_LOCK(sc); + if (! sc->sc_isedma) { + ATH_PCU_LOCK(sc); + /* + * NB: the hardware should re-read the link when + * RXE bit is written, but it doesn't work at + * least on older hardware revs. + */ + sc->sc_stats.ast_rxeol++; + /* + * Disable RXEOL/RXORN - prevent an interrupt + * storm until the PCU logic can be reset. + * In case the interface is reset some other + * way before "sc_kickpcu" is called, don't + * modify sc_imask - that way if it is reset + * by a call to ath_reset() somehow, the + * interrupt mask will be correctly reprogrammed. + */ + imask = sc->sc_imask; + imask &= ~(HAL_INT_RXEOL | HAL_INT_RXORN); + ath_hal_intrset(ah, imask); + /* + * Only blank sc_rxlink if we've not yet kicked + * the PCU. + * + * This isn't entirely correct - the correct solution + * would be to have a PCU lock and engage that for + * the duration of the PCU fiddling; which would include + * running the RX process. Otherwise we could end up + * messing up the RX descriptor chain and making the + * RX desc list much shorter. + */ + if (! sc->sc_kickpcu) + sc->sc_rxlink = NULL; + sc->sc_kickpcu = 1; + ATH_PCU_UNLOCK(sc); + } /* - * NB: the hardware should re-read the link when - * RXE bit is written, but it doesn't work at - * least on older hardware revs. - */ - sc->sc_stats.ast_rxeol++; - /* - * Disable RXEOL/RXORN - prevent an interrupt - * storm until the PCU logic can be reset. - * In case the interface is reset some other - * way before "sc_kickpcu" is called, don't - * modify sc_imask - that way if it is reset - * by a call to ath_reset() somehow, the - * interrupt mask will be correctly reprogrammed. - */ - imask = sc->sc_imask; - imask &= ~(HAL_INT_RXEOL | HAL_INT_RXORN); - ath_hal_intrset(ah, imask); - /* - * Only blank sc_rxlink if we've not yet kicked - * the PCU. - * - * This isn't entirely correct - the correct solution - * would be to have a PCU lock and engage that for - * the duration of the PCU fiddling; which would include - * running the RX process. Otherwise we could end up - * messing up the RX descriptor chain and making the - * RX desc list much shorter. - */ - if (! sc->sc_kickpcu) - sc->sc_rxlink = NULL; - sc->sc_kickpcu = 1; - ATH_PCU_UNLOCK(sc); - /* - * Enqueue an RX proc, to handled whatever + * Enqueue an RX proc to handle whatever * is in the RX queue. - * This will then kick the PCU. + * This will then kick the PCU if required. */ sc->sc_rx.recv_sched(sc, 1); } @@ -2348,6 +2354,11 @@ ATH_UNLOCK(sc); return; } + + ATH_RX_LOCK(sc); + sc->sc_rx_stopped = 1; + ATH_RX_UNLOCK(sc); + ath_chan_change(sc, ic->ic_curchan); /* Let DFS at it in case it's a DFS channel */ @@ -2406,8 +2417,7 @@ * Enable interrupts. */ sc->sc_imask = HAL_INT_RX | HAL_INT_TX - | HAL_INT_RXEOL | HAL_INT_RXORN - | HAL_INT_TXURN + | HAL_INT_RXORN | HAL_INT_TXURN | HAL_INT_FATAL | HAL_INT_GLOBAL; /* @@ -2418,6 +2428,14 @@ sc->sc_imask |= (HAL_INT_RXHP | HAL_INT_RXLP); /* + * If we're an EDMA NIC, we don't care about RXEOL. + * Writing a new descriptor in will simply restart + * RX DMA. + */ + if (! sc->sc_isedma) + sc->sc_imask |= HAL_INT_RXEOL; + + /* * Enable MIB interrupts when there are hardware phy counters. * Note we only do this (at the moment) for station mode. */ @@ -2735,6 +2753,10 @@ __func__, status); sc->sc_diversity = ath_hal_getdiversity(ah); + ATH_RX_LOCK(sc); + sc->sc_rx_stopped = 1; + ATH_RX_UNLOCK(sc); + /* Let DFS at it in case it's a DFS channel */ ath_dfs_radar_enable(sc, ic->ic_curchan); @@ -5384,6 +5406,10 @@ } sc->sc_diversity = ath_hal_getdiversity(ah); + ATH_RX_LOCK(sc); + sc->sc_rx_stopped = 1; + ATH_RX_UNLOCK(sc); + /* Let DFS at it in case it's a DFS channel */ ath_dfs_radar_enable(sc, chan); Index: if_ath_rx_edma.c =================================================================== --- if_ath_rx_edma.c (revision 270068) +++ if_ath_rx_edma.c (working copy) @@ -160,10 +160,20 @@ struct ath_hal *ah = sc->sc_ah; ATH_RX_LOCK(sc); + ath_hal_stoppcurecv(ah); ath_hal_setrxfilter(ah, 0); - ath_hal_stopdmarecv(ah); + /* + * + */ + if (ath_hal_stopdmarecv(ah) == AH_TRUE) + sc->sc_rx_stopped = 1; + + /* + * Give the various bus FIFOs (not EDMA descriptor FIFO) + * time to finish flushing out data. + */ DELAY(3000); /* Flush RX pending for each queue */ @@ -218,10 +228,6 @@ /* * Start receive. - * - * XXX TODO: this needs to reallocate the FIFO entries when a reset - * occurs, in case the FIFO is filled up and no new descriptors get - * thrown into the FIFO. */ static int ath_edma_startrecv(struct ath_softc *sc) @@ -230,35 +236,30 @@ ATH_RX_LOCK(sc); + /* + * Sanity check - are we being called whilst RX + * isn't stopped? If so, we may end up pushing + * too many entries into the RX FIFO and + * badness occurs. + */ + /* Enable RX FIFO */ ath_hal_rxena(ah); /* - * Entries should only be written out if the - * FIFO is empty. - * - * XXX This isn't correct. I should be looking - * at the value of AR_RXDP_SIZE (0x0070) to determine - * how many entries are in here. - * - * A warm reset will clear the registers but not the FIFO. - * - * And I believe this is actually the address of the last - * handled buffer rather than the current FIFO pointer. - * So if no frames have been (yet) seen, we'll reinit the - * FIFO. - * - * I'll chase that up at some point. + * In theory the hardware has been initialised, right? */ - if (ath_hal_getrxbuf(sc->sc_ah, HAL_RX_QUEUE_HP) == 0) { + if (sc->sc_rx_stopped == 1) { DPRINTF(sc, ATH_DEBUG_EDMA_RX, "%s: Re-initing HP FIFO\n", __func__); ath_edma_reinit_fifo(sc, HAL_RX_QUEUE_HP); - } - if (ath_hal_getrxbuf(sc->sc_ah, HAL_RX_QUEUE_LP) == 0) { DPRINTF(sc, ATH_DEBUG_EDMA_RX, "%s: Re-initing LP FIFO\n", __func__); ath_edma_reinit_fifo(sc, HAL_RX_QUEUE_LP); + } else { + device_printf(sc->sc_dev, + "%s: called without stopped RX?\n", + __func__); } /* Add up to m_fifolen entries in each queue */ @@ -266,6 +267,9 @@ * These must occur after the above write so the FIFO buffers * are pushed/tracked in the same order as the hardware will * process them. + * + * XXX TODO: is this really necessary? We should've stopped + * the hardware already and reinitialised it, so it's a no-op. */ ath_edma_rxfifo_alloc(sc, HAL_RX_QUEUE_HP, sc->sc_rxedma[HAL_RX_QUEUE_HP].m_fifolen); @@ -276,6 +280,11 @@ ath_mode_init(sc); ath_hal_startpcurecv(ah); + /* + * We're now doing RX DMA! + */ + sc->sc_rx_stopped = 0; + ATH_RX_UNLOCK(sc); return (0); @@ -451,24 +460,6 @@ "ath edma rx proc: npkts=%d\n", npkts); - /* Handle resched and kickpcu appropriately */ - ATH_PCU_LOCK(sc); - if (dosched && sc->sc_kickpcu) { - ATH_KTR(sc, ATH_KTR_ERROR, 0, - "ath_edma_recv_proc_queue(): kickpcu"); - if (npkts > 0) - device_printf(sc->sc_dev, - "%s: handled npkts %d\n", - __func__, npkts); - - /* - * XXX TODO: what should occur here? Just re-poke and - * re-enable the RX FIFO? - */ - sc->sc_kickpcu = 0; - } - ATH_PCU_UNLOCK(sc); - return; } Index: if_athvar.h =================================================================== --- if_athvar.h (revision 270068) +++ if_athvar.h (working copy) @@ -566,6 +566,7 @@ int sc_tx_statuslen; int sc_tx_nmaps; /* Number of TX maps */ int sc_edma_bufsize; + int sc_rx_stopped; /* XXX only for EDMA */ void (*sc_node_cleanup)(struct ieee80211_node *); void (*sc_node_free)(struct ieee80211_node *);