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>

authored by Felix Fietkau and committed by John W. Linville 5882da02 4a39e781

+26 -16
-9
drivers/net/wireless/ath/ath9k/hw.c
··· 1254 ah->txchainmask = common->tx_chainmask; 1255 ah->rxchainmask = common->rx_chainmask; 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)) 1267 return -EIO; 1268
··· 1254 ah->txchainmask = common->tx_chainmask; 1255 ah->rxchainmask = common->rx_chainmask; 1256 1257 if (!ath9k_hw_setpower(ah, ATH9K_PM_AWAKE)) 1258 return -EIO; 1259
+22 -3
drivers/net/wireless/ath/ath9k/mac.c
··· 751 } 752 EXPORT_SYMBOL(ath9k_hw_abortpcurecv); 753 754 - bool ath9k_hw_stopdmarecv(struct ath_hw *ah) 755 { 756 #define AH_RX_STOP_DMA_TIMEOUT 10000 /* usec */ 757 #define AH_RX_TIME_QUANTUM 100 /* usec */ 758 struct ath_common *common = ath9k_hw_common(ah); 759 int i; 760 761 REG_WRITE(ah, AR_CR, AR_CR_RXD); 762 ··· 771 for (i = AH_RX_STOP_DMA_TIMEOUT / AH_TIME_QUANTUM; i != 0; i--) { 772 if ((REG_READ(ah, AR_CR) & AR_CR_RXE) == 0) 773 break; 774 udelay(AH_TIME_QUANTUM); 775 } 776 777 if (i == 0) { 778 ath_err(common, 779 - "DMA failed to stop in %d ms AR_CR=0x%08x AR_DIAG_SW=0x%08x\n", 780 AH_RX_STOP_DMA_TIMEOUT / 1000, 781 REG_READ(ah, AR_CR), 782 - REG_READ(ah, AR_DIAG_SW)); 783 return false; 784 } else { 785 return true;
··· 751 } 752 EXPORT_SYMBOL(ath9k_hw_abortpcurecv); 753 754 + bool ath9k_hw_stopdmarecv(struct ath_hw *ah, bool *reset) 755 { 756 #define AH_RX_STOP_DMA_TIMEOUT 10000 /* usec */ 757 #define AH_RX_TIME_QUANTUM 100 /* usec */ 758 struct ath_common *common = ath9k_hw_common(ah); 759 + u32 mac_status, last_mac_status = 0; 760 int i; 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 768 REG_WRITE(ah, AR_CR, AR_CR_RXD); 769 ··· 764 for (i = AH_RX_STOP_DMA_TIMEOUT / AH_TIME_QUANTUM; i != 0; i--) { 765 if ((REG_READ(ah, AR_CR) & AR_CR_RXE) == 0) 766 break; 767 + 768 + if (!AR_SREV_9300_20_OR_LATER(ah)) { 769 + mac_status = REG_READ(ah, AR_DMADBG_7) & 0x7f0; 770 + if (mac_status == 0x1c0 && mac_status == last_mac_status) { 771 + *reset = true; 772 + break; 773 + } 774 + 775 + last_mac_status = mac_status; 776 + } 777 + 778 udelay(AH_TIME_QUANTUM); 779 } 780 781 if (i == 0) { 782 ath_err(common, 783 + "DMA failed to stop in %d ms AR_CR=0x%08x AR_DIAG_SW=0x%08x DMADBG_7=0x%08x\n", 784 AH_RX_STOP_DMA_TIMEOUT / 1000, 785 REG_READ(ah, AR_CR), 786 + REG_READ(ah, AR_DIAG_SW), 787 + REG_READ(ah, AR_DMADBG_7)); 788 return false; 789 } else { 790 return true;
+1 -1
drivers/net/wireless/ath/ath9k/mac.h
··· 695 void ath9k_hw_putrxbuf(struct ath_hw *ah, u32 rxdp); 696 void ath9k_hw_startpcureceive(struct ath_hw *ah, bool is_scanning); 697 void ath9k_hw_abortpcurecv(struct ath_hw *ah); 698 - bool ath9k_hw_stopdmarecv(struct ath_hw *ah); 699 int ath9k_hw_beaconq_setup(struct ath_hw *ah); 700 701 /* Interrupt Handling */
··· 695 void ath9k_hw_putrxbuf(struct ath_hw *ah, u32 rxdp); 696 void ath9k_hw_startpcureceive(struct ath_hw *ah, bool is_scanning); 697 void ath9k_hw_abortpcurecv(struct ath_hw *ah); 698 + bool ath9k_hw_stopdmarecv(struct ath_hw *ah, bool *reset); 699 int ath9k_hw_beaconq_setup(struct ath_hw *ah); 700 701 /* Interrupt Handling */
+3 -3
drivers/net/wireless/ath/ath9k/recv.c
··· 486 bool ath_stoprecv(struct ath_softc *sc) 487 { 488 struct ath_hw *ah = sc->sc_ah; 489 - bool stopped; 490 491 spin_lock_bh(&sc->rx.rxbuflock); 492 ath9k_hw_abortpcurecv(ah); 493 ath9k_hw_setrxfilter(ah, 0); 494 - stopped = ath9k_hw_stopdmarecv(ah); 495 496 if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) 497 ath_edma_stop_recv(sc); ··· 506 "confusing the DMA engine when we start RX up\n"); 507 ATH_DBG_WARN_ON_ONCE(!stopped); 508 } 509 - return stopped; 510 } 511 512 void ath_flushrecv(struct ath_softc *sc)
··· 486 bool ath_stoprecv(struct ath_softc *sc) 487 { 488 struct ath_hw *ah = sc->sc_ah; 489 + bool stopped, reset = false; 490 491 spin_lock_bh(&sc->rx.rxbuflock); 492 ath9k_hw_abortpcurecv(ah); 493 ath9k_hw_setrxfilter(ah, 0); 494 + stopped = ath9k_hw_stopdmarecv(ah, &reset); 495 496 if (sc->sc_ah->caps.hw_caps & ATH9K_HW_CAP_EDMA) 497 ath_edma_stop_recv(sc); ··· 506 "confusing the DMA engine when we start RX up\n"); 507 ATH_DBG_WARN_ON_ONCE(!stopped); 508 } 509 + return stopped || reset; 510 } 511 512 void ath_flushrecv(struct ath_softc *sc)