aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@openwrt.org>2011-03-11 15:38:18 -0500
committerJohn W. Linville <linville@tuxdriver.com>2011-03-14 14:46:58 -0400
commit0d51cccc2436fa4d978efc3764552779e163d840 (patch)
tree28470ba80a7b29e2ccedd2fa56e0a99f55663027
parent997941d7efe4d165a558ed5b6029a8b3c2c85cf7 (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.c28
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.h1
-rw-r--r--drivers/net/wireless/ath/ath9k/xmit.c14
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}
144EXPORT_SYMBOL(ath9k_hw_updatetxtriglevel); 144EXPORT_SYMBOL(ath9k_hw_updatetxtriglevel);
145 145
146void 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}
172EXPORT_SYMBOL(ath9k_hw_abort_tx_dma);
173
146bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q) 174bool 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);
677u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q); 677u32 ath9k_hw_numtxpending(struct ath_hw *ah, u32 q);
678bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel); 678bool ath9k_hw_updatetxtriglevel(struct ath_hw *ah, bool bIncTrigLevel);
679bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q); 679bool ath9k_hw_stoptxdma(struct ath_hw *ah, u32 q);
680void ath9k_hw_abort_tx_dma(struct ath_hw *ah);
680void ath9k_hw_gettxintrtxqs(struct ath_hw *ah, u32 *txqs); 681void ath9k_hw_gettxintrtxqs(struct ath_hw *ah, u32 *txqs);
681bool ath9k_hw_set_txq_props(struct ath_hw *ah, int q, 682bool 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)