101 lines
3 KiB
Diff
101 lines
3 KiB
Diff
|
From: Felix Fietkau <nbd@nbd.name>
|
||
|
Date: Wed, 25 Jan 2017 13:00:58 +0100
|
||
|
Subject: [PATCH] ath9k: check for deaf rx path state
|
||
|
|
||
|
Various chips occasionally run into a state where the tx path still
|
||
|
appears to be working normally, but the rx path is deaf.
|
||
|
|
||
|
There is no known register signature to check for this state explicitly,
|
||
|
so use the lack of rx interrupts as an indicator.
|
||
|
|
||
|
This detection is prone to false positives, since a device could also
|
||
|
simply be in an environment where there are no frames on the air.
|
||
|
However, in this case doing a reset should be harmless since it's
|
||
|
obviously not interrupting any real activity. To avoid confusion, call
|
||
|
the reset counters in this case "Rx path inactive" instead of something
|
||
|
like "Rx path deaf", since it may not be an indication of a real
|
||
|
hardware failure.
|
||
|
|
||
|
Signed-off-by: Felix Fietkau <nbd@nbd.name>
|
||
|
---
|
||
|
|
||
|
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
|
||
|
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
|
||
|
@@ -1027,6 +1027,7 @@ struct ath_softc {
|
||
|
|
||
|
u8 gtt_cnt;
|
||
|
u32 intrstatus;
|
||
|
+ u32 rx_active;
|
||
|
u16 ps_flags; /* PS_* */
|
||
|
bool ps_enabled;
|
||
|
bool ps_idle;
|
||
|
--- a/drivers/net/wireless/ath/ath9k/debug.c
|
||
|
+++ b/drivers/net/wireless/ath/ath9k/debug.c
|
||
|
@@ -763,6 +763,7 @@ static int read_file_reset(struct seq_fi
|
||
|
[RESET_TYPE_BEACON_STUCK] = "Stuck Beacon",
|
||
|
[RESET_TYPE_MCI] = "MCI Reset",
|
||
|
[RESET_TYPE_CALIBRATION] = "Calibration error",
|
||
|
+ [RESET_TYPE_RX_INACTIVE] = "Rx path inactive",
|
||
|
[RESET_TX_DMA_ERROR] = "Tx DMA stop error",
|
||
|
[RESET_RX_DMA_ERROR] = "Rx DMA stop error",
|
||
|
};
|
||
|
--- a/drivers/net/wireless/ath/ath9k/debug.h
|
||
|
+++ b/drivers/net/wireless/ath/ath9k/debug.h
|
||
|
@@ -50,6 +50,7 @@ enum ath_reset_type {
|
||
|
RESET_TYPE_BEACON_STUCK,
|
||
|
RESET_TYPE_MCI,
|
||
|
RESET_TYPE_CALIBRATION,
|
||
|
+ RESET_TYPE_RX_INACTIVE,
|
||
|
RESET_TX_DMA_ERROR,
|
||
|
RESET_RX_DMA_ERROR,
|
||
|
__RESET_TYPE_MAX
|
||
|
--- a/drivers/net/wireless/ath/ath9k/link.c
|
||
|
+++ b/drivers/net/wireless/ath/ath9k/link.c
|
||
|
@@ -53,13 +53,27 @@ reset:
|
||
|
|
||
|
}
|
||
|
|
||
|
+static bool ath_rx_active_check(struct ath_softc *sc)
|
||
|
+{
|
||
|
+ if (sc->rx_active) {
|
||
|
+ sc->rx_active = 0;
|
||
|
+ return true;
|
||
|
+ }
|
||
|
+
|
||
|
+ ath_dbg(ath9k_hw_common(sc->sc_ah), RESET,
|
||
|
+ "rx path inactive, resetting the chip\n");
|
||
|
+ ath9k_queue_reset(sc, RESET_TYPE_RX_INACTIVE);
|
||
|
+ return false;
|
||
|
+}
|
||
|
+
|
||
|
void ath_hw_check_work(struct work_struct *work)
|
||
|
{
|
||
|
struct ath_softc *sc = container_of(work, struct ath_softc,
|
||
|
hw_check_work.work);
|
||
|
|
||
|
if (!ath_hw_check(sc) ||
|
||
|
- !ath_tx_complete_check(sc))
|
||
|
+ !ath_tx_complete_check(sc) ||
|
||
|
+ !ath_rx_active_check(sc))
|
||
|
return;
|
||
|
|
||
|
ieee80211_queue_delayed_work(sc->hw, &sc->hw_check_work,
|
||
|
--- a/drivers/net/wireless/ath/ath9k/main.c
|
||
|
+++ b/drivers/net/wireless/ath/ath9k/main.c
|
||
|
@@ -269,6 +269,7 @@ static bool ath_complete_reset(struct at
|
||
|
}
|
||
|
|
||
|
sc->gtt_cnt = 0;
|
||
|
+ sc->rx_active = 1;
|
||
|
|
||
|
ath9k_hw_set_interrupts(ah);
|
||
|
ath9k_hw_enable_interrupts(ah);
|
||
|
@@ -452,6 +453,7 @@ void ath9k_tasklet(unsigned long data)
|
||
|
ath_rx_tasklet(sc, 0, true);
|
||
|
|
||
|
ath_rx_tasklet(sc, 0, false);
|
||
|
+ sc->rx_active = 1;
|
||
|
}
|
||
|
|
||
|
if (status & ATH9K_INT_TX) {
|