aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorFelix Fietkau <nbd@openwrt.org>2011-04-08 14:13:18 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-04-12 16:41:33 -0400
commit5882da02e9d9089b7e8c739f3e774aaeeff8b7ba (patch)
treea260e6094888a14fc85d687b24606886fc158891 /drivers
parent4a39e781682828c1b81a839e7d343fd91945a8d5 (diff)
ath9k_hw: fix stopping rx DMA during resets
During PHY errors, the MAC can sometimes fail to enter an idle state on older hardware (before AR9380) after an rx stop has been requested. This typically shows up in the kernel log with messages like these: ath: Could not stop RX, we could be confusing the DMA engine when we start RX up ------------[ cut here ]------------ WARNING: at drivers/net/wireless/ath/ath9k/recv.c:504 ath_stoprecv+0xcc/0xf0 [ath9k]() Call Trace: [<8023f0e8>] dump_stack+0x8/0x34 [<80075050>] warn_slowpath_common+0x78/0xa4 [<80075094>] warn_slowpath_null+0x18/0x24 [<80d66d60>] ath_stoprecv+0xcc/0xf0 [ath9k] [<80d642cc>] ath_set_channel+0xbc/0x270 [ath9k] [<80d65254>] ath_radio_disable+0x4a4/0x7fc [ath9k] When this happens, the state that the MAC enters is easy to identify and does not result in bogus DMA traffic, however to ensure a working state after a channel change, the hardware should still be reset. This patch adds detection for this specific MAC state, after which the above warnings completely disappear in my tests. Signed-off-by: Felix Fietkau <nbd@openwrt.org> Cc: stable@kernel.org Cc: Kyungwan Nam <Kyungwan.Nam@Atheros.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/net/wireless/ath/ath9k/hw.c9
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.c25
-rw-r--r--drivers/net/wireless/ath/ath9k/mac.h2
-rw-r--r--drivers/net/wireless/ath/ath9k/recv.c6
4 files changed, 26 insertions, 16 deletions
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 1ec9bcd6b281..c95bc5cc1a1f 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -1254,15 +1254,6 @@ int ath9k_hw_reset(struct ath_hw *ah, struct ath9k_channel *chan,
1254 ah->txchainmask = common->tx_chainmask; 1254 ah->txchainmask = common->tx_chainmask;
1255 ah->rxchainmask = common->rx_chainmask; 1255 ah->rxchainmask = common->rx_chainmask;
1256 1256
1257 if ((common->bus_ops->ath_bus_type != ATH_USB) && !ah->chip_fullsleep) {
1258 ath9k_hw_abortpcurecv(ah);
1259 if (!ath9k_hw_stopdmarecv(ah)) {
1260 ath_dbg(common, ATH_DBG_XMIT,
1261 "Failed to stop receive dma\n");
1262 bChannelChange = false;
1263 }
1264 }
1265
1266 if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) 1257 if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE))
1267 return -EIO; 1258 return -EIO;
1268 1259
diff --git a/drivers/net/wireless/ath/ath9k/mac.c b/drivers/net/wireless/ath/ath9k/mac.c
index 562257ac52cf..edc1cbbfecaf 100644
--- a/drivers/net/wireless/ath/ath9k/mac.c
+++ b/drivers/net/wireless/ath/ath9k/mac.c
@@ -751,28 +751,47 @@ void ath9k_hw_abortpcurecv(struct ath_hw *ah)
751} 751}
752EXPORT_SYMBOL(ath9k_hw_abortpcurecv); 752EXPORT_SYMBOL(ath9k_hw_abortpcurecv);
753 753
754bool ath9k_hw_stopdmarecv(struct ath_hw *ah) 754bool ath9k_hw_stopdmarecv(struct ath_hw *ah, bool *reset)
755{ 755{
756#define AH_RX_STOP_DMA_TIMEOUT 10000 /* usec */ 756#define AH_RX_STOP_DMA_TIMEOUT 10000 /* usec */
757#define AH_RX_TIME_QUANTUM 100 /* usec */ 757#define AH_RX_TIME_QUANTUM 100 /* usec */
758 struct ath_common *common = ath9k_hw_common(ah); 758 struct ath_common *common = ath9k_hw_common(ah);
759 u32 mac_status, last_mac_status = 0;
759 int i; 760 int i;
760 761
762 /* Enable access to the DMA observation bus */
763 REG_WRITE(ah, AR_MACMISC,
764 ((AR_MACMISC_DMA_OBS_LINE_8 << AR_MACMISC_DMA_OBS_S) |
765 (AR_MACMISC_MISC_OBS_BUS_1 <<
766 AR_MACMISC_MISC_OBS_BUS_MSB_S)));
767
761 REG_WRITE(ah, AR_CR, AR_CR_RXD); 768 REG_WRITE(ah, AR_CR, AR_CR_RXD);
762 769
763 /* Wait for rx enable bit to go low */ 770 /* Wait for rx enable bit to go low */
764 for (i = AH_RX_STOP_DMA_TIMEOUT / AH_TIME_QUANTUM; i != 0; i--) { 771 for (i = AH_RX_STOP_DMA_TIMEOUT / AH_TIME_QUANTUM; i != 0; i--) {
765 if ((REG_READ(ah, AR_CR) & AR_CR_RXE) == 0) 772 if ((REG_READ(ah, AR_CR) & AR_CR_RXE) == 0)
766 break; 773 break;
774
775 if (!AR_SREV_9300_20_OR_LATER(ah)) {
776 mac_status = REG_READ(ah, AR_DMADBG_7) & 0x7f0;
777 if (mac_status == 0x1c0 && mac_status == last_mac_status) {
778 *reset = true;
779 break;
780 }
781
782 last_mac_status = mac_status;
783 }
784
767 udelay(AH_TIME_QUANTUM); 785 udelay(AH_TIME_QUANTUM);
768 } 786 }
769 787
770 if (i == 0) { 788 if (i == 0) {
771 ath_err(common, 789 ath_err(common,
772 "DMA failed to stop in %d ms AR_CR=0x%08x AR_DIAG_SW=0x%08x\n", 790 "DMA failed to stop in %d ms AR_CR=0x%08x AR_DIAG_SW=0x%08x DMADBG_7=0x%08x\n",
773 AH_RX_STOP_DMA_TIMEOUT / 1000, 791 AH_RX_STOP_DMA_TIMEOUT / 1000,
774 REG_READ(ah, AR_CR), 792 REG_READ(ah, AR_CR),
775 REG_READ(ah, AR_DIAG_SW)); 793 REG_READ(ah, AR_DIAG_SW),
794 REG_READ(ah, AR_DMADBG_7));
776 return false; 795 return false;
777 } else { 796 } else {
778 return true; 797 return true;
diff --git a/drivers/net/wireless/ath/ath9k/mac.h b/drivers/net/wireless/ath/ath9k/mac.h
index b2b2ff852c32..c2a59386fb9c 100644
--- a/drivers/net/wireless/ath/ath9k/mac.h
+++ b/drivers/net/wireless/ath/ath9k/mac.h
@@ -695,7 +695,7 @@ bool ath9k_hw_setrxabort(struct ath_hw *ah, bool set);
695void ath9k_hw_putrxbuf(struct ath_hw *ah, u32 rxdp); 695void ath9k_hw_putrxbuf(struct ath_hw *ah, u32 rxdp);
696void ath9k_hw_startpcureceive(struct ath_hw *ah, bool is_scanning); 696void ath9k_hw_startpcureceive(struct ath_hw *ah, bool is_scanning);
697void ath9k_hw_abortpcurecv(struct ath_hw *ah); 697void ath9k_hw_abortpcurecv(struct ath_hw *ah);
698bool ath9k_hw_stopdmarecv(struct ath_hw *ah); 698bool ath9k_hw_stopdmarecv(struct ath_hw *ah, bool *reset);
699int ath9k_hw_beaconq_setup(struct ath_hw *ah); 699int ath9k_hw_beaconq_setup(struct ath_hw *ah);
700 700
701/* Interrupt Handling */ 701/* Interrupt Handling */
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index a9c3f4672aa0..dcd19bc337d1 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -486,12 +486,12 @@ start_recv:
486bool ath_stoprecv(struct ath_softc *sc) 486bool ath_stoprecv(struct ath_softc *sc)
487{ 487{
488 struct ath_hw *ah = sc->sc_ah; 488 struct ath_hw *ah = sc->sc_ah;
489 bool stopped; 489 bool stopped, reset = false;
490 490
491 spin_lock_bh(&sc->rx.rxbuflock); 491 spin_lock_bh(&sc->rx.rxbuflock);
492 ath9k_hw_abortpcurecv(ah); 492 ath9k_hw_abortpcurecv(ah);
493 ath9k_hw_setrxfilter(ah, 0); 493 ath9k_hw_setrxfilter(ah, 0);
494 stopped = ath9k_hw_stopdmarecv(ah); 494 stopped = ath9k_hw_stopdmarecv(ah, &reset);
495 495
496 if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) 496 if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA)
497 ath_edma_stop_recv(sc); 497 ath_edma_stop_recv(sc);
@@ -506,7 +506,7 @@ bool ath_stoprecv(struct ath_softc *sc)
506 "confusing the DMA engine when we start RX up\n"); 506 "confusing the DMA engine when we start RX up\n");
507 ATH_DBG_WARN_ON_ONCE(!stopped); 507 ATH_DBG_WARN_ON_ONCE(!stopped);
508 } 508 }
509 return stopped; 509 return stopped || reset;
510} 510}
511 511
512void ath_flushrecv(struct ath_softc *sc) 512void ath_flushrecv(struct ath_softc *sc)