diff options
author | Felix Fietkau <nbd@openwrt.org> | 2011-03-11 15:38:18 -0500 |
---|---|---|
committer | John W. Linville <linville@tuxdriver.com> | 2011-03-14 14:46:58 -0400 |
commit | 0d51cccc2436fa4d978efc3764552779e163d840 (patch) | |
tree | 28470ba80a7b29e2ccedd2fa56e0a99f55663027 | |
parent | 997941d7efe4d165a558ed5b6029a8b3c2c85cf7 (diff) |
ath9k: fix stopping tx dma on reset
In some situations, stopping Tx DMA frequently fails, leading to messages
like this:
ath: Failed to stop TX DMA in 100 msec after killing last frame
ath: Failed to stop TX DMA!
This patch uses a few MAC features to abort DMA globally instead of iterating
over all hardware queues and attempting to stop them individually.
Not only is that faster and works with a shorter timeout, it also makes the
process much more reliable.
With this change, I can no longer trigger these messages on AR9380,
and on AR9280 they become much more rare.
Signed-off-by: Felix Fietkau <nbd@openwrt.org>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
-rw-r--r-- | drivers/net/wireless/ath/ath9k/mac.c | 28 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/mac.h | 1 | ||||
-rw-r--r-- | drivers/net/wireless/ath/ath9k/xmit.c | 14 |
3 files changed, 35 insertions, 8 deletions
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c index c75d40fb86f1..58aaf9ab0920 100644 --- a/drivers/net/wireless/ath/ath9k/mac.c +++ b/drivers/net/wireless/ath/ath9k/mac.c | |||
@@ -143,6 +143,34 @@ bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel) | |||
143 | } | 143 | } |
144 | EXPORT_SYMBOL(ath9k_hw_updatetxtriglevel); | 144 | EXPORT_SYMBOL(ath9k_hw_updatetxtriglevel); |
145 | 145 | ||
146 | void ath9k_hw_abort_tx_dma(struct ath_hw *ah) | ||
147 | { | ||
148 | int i, q; | ||
149 | |||
150 | REG_WRITE(ah, AR_Q_TXD, AR_Q_TXD_M); | ||
151 | |||
152 | REG_SET_BIT(ah, AR_PCU_MISC, AR_PCU_FORCE_QUIET_COLL | AR_PCU_CLEAR_VMF); | ||
153 | REG_SET_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH); | ||
154 | REG_SET_BIT(ah, AR_D_GBL_IFS_MISC, AR_D_GBL_IFS_MISC_IGNORE_BACKOFF); | ||
155 | |||
156 | for (q = 0; q < AR_NUM_QCU; q++) { | ||
157 | for (i = 0; i < 1000; i++) { | ||
158 | if (i) | ||
159 | udelay(5); | ||
160 | |||
161 | if (!ath9k_hw_numtxpending(ah, q)) | ||
162 | break; | ||
163 | } | ||
164 | } | ||
165 | |||
166 | REG_CLR_BIT(ah, AR_PCU_MISC, AR_PCU_FORCE_QUIET_COLL | AR_PCU_CLEAR_VMF); | ||
167 | REG_CLR_BIT(ah, AR_DIAG_SW, AR_DIAG_FORCE_CH_IDLE_HIGH); | ||
168 | REG_CLR_BIT(ah, AR_D_GBL_IFS_MISC, AR_D_GBL_IFS_MISC_IGNORE_BACKOFF); | ||
169 | |||
170 | REG_WRITE(ah, AR_Q_TXD, 0); | ||
171 | } | ||
172 | EXPORT_SYMBOL(ath9k_hw_abort_tx_dma); | ||
173 | |||
146 | bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q) | 174 | bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q) |
147 | { | 175 | { |
148 | #define ATH9K_TX_STOP_DMA_TIMEOUT 4000 /* usec */ | 176 | #define ATH9K_TX_STOP_DMA_TIMEOUT 4000 /* usec */ |
diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h index 04d58ae923bb..d37c6d413be1 100644 --- a/drivers/net/wireless/ath/ath9k/mac.h +++ b/drivers/net/wireless/ath/ath9k/mac.h | |||
@@ -677,6 +677,7 @@ void ath9k_hw_cleartxdesc(struct ath_hw *ah, void *ds); | |||
677 | u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q); | 677 | u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q); |
678 | bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel); | 678 | bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel); |
679 | bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q); | 679 | bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q); |
680 | void ath9k_hw_abort_tx_dma(struct ath_hw *ah); | ||
680 | void ath9k_hw_gettxintrtxqs(struct ath_hw *ah, u32 *txqs); | 681 | void ath9k_hw_gettxintrtxqs(struct ath_hw *ah, u32 *txqs); |
681 | bool ath9k_hw_set_txq_props(struct ath_hw *ah, int q, | 682 | bool ath9k_hw_set_txq_props(struct ath_hw *ah, int q, |
682 | const struct ath9k_tx_queue_info *qinfo); | 683 | const struct ath9k_tx_queue_info *qinfo); |
diff --git a/drivers/net/wireless/ath/ath9k/xmit.c b/drivers/net/wireless/ath/ath9k/xmit.c index e16136d61799..bb1d29e90eb1 100644 --- a/drivers/net/wireless/ath/ath9k/xmit.c +++ b/drivers/net/wireless/ath/ath9k/xmit.c | |||
@@ -1194,16 +1194,14 @@ bool ath_drain_all_txq(struct ath_softc *sc, bool retry_tx) | |||
1194 | if (sc->sc_flags & SC_OP_INVALID) | 1194 | if (sc->sc_flags & SC_OP_INVALID) |
1195 | return true; | 1195 | return true; |
1196 | 1196 | ||
1197 | /* Stop beacon queue */ | 1197 | ath9k_hw_abort_tx_dma(ah); |
1198 | ath9k_hw_stoptxdma(sc->sc_ah, sc->beacon.beaconq); | ||
1199 | 1198 | ||
1200 | /* Stop data queues */ | 1199 | /* Check if any queue remains active */ |
1201 | for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { | 1200 | for (i = 0; i < ATH9K_NUM_TX_QUEUES; i++) { |
1202 | if (ATH_TXQ_SETUP(sc, i)) { | 1201 | if (!ATH_TXQ_SETUP(sc, i)) |
1203 | txq = &sc->tx.txq[i]; | 1202 | continue; |
1204 | ath9k_hw_stoptxdma(ah, txq->axq_qnum); | 1203 | |
1205 | npend += ath9k_hw_numtxpending(ah, txq->axq_qnum); | 1204 | npend += ath9k_hw_numtxpending(ah, sc->tx.txq[i].axq_qnum); |
1206 | } | ||
1207 | } | 1205 | } |
1208 | 1206 | ||
1209 | if (npend) | 1207 | if (npend) |