This patch improves ANI operations by switching among the immunity
levels based on PHY errors and beacon rssi which will adjust receiver
desensitizing parameters. The changes are
* Configure the Weak Signal Detection based on current immunity value.
* At highest OFDM immunity level poor performance was observed with
strong interference. By tuning the FIR step and spur immunity levels
and not changing any weak signal detection thresholds at any level
helped to improve the performance.
* ANI took long time to recover back to lower immunity levels on heavy
data load. As the listen time got reset to zero before reaching to
the 5x of aniperiod, the immunity level is not lowering back even
without any interference. This patch fix that.
Cc: Paul Stewart <[email protected]>
Cc: Susinder Gulasekaran <[email protected]>
Signed-off-by: Suresh Chandrasekaran <[email protected]>
Signed-off-by: Rajkumar Manoharan <[email protected]>
---
drivers/net/wireless/ath/ath9k/ani.c | 49 +++++++++++++--------------
drivers/net/wireless/ath/ath9k/ani.h | 6 ++--
drivers/net/wireless/ath/ath9k/ar5008_phy.c | 38 ---------------------
drivers/net/wireless/ath/ath9k/ar9003_phy.c | 49 ---------------------------
4 files changed, 27 insertions(+), 115 deletions(-)
diff --git a/drivers/net/wireless/ath/ath9k/ani.c b/drivers/net/wireless/ath/ath9k/ani.c
index bc56f57..6c771f5 100644
--- a/drivers/net/wireless/ath/ath9k/ani.c
+++ b/drivers/net/wireless/ath/ath9k/ani.c
@@ -46,8 +46,8 @@ static const struct ani_ofdm_level_entry ofdm_level_table[] = {
{ 5, 4, 1 }, /* lvl 5 */
{ 6, 5, 1 }, /* lvl 6 */
{ 7, 6, 1 }, /* lvl 7 */
- { 7, 7, 1 }, /* lvl 8 */
- { 7, 8, 0 } /* lvl 9 */
+ { 7, 6, 0 }, /* lvl 8 */
+ { 7, 7, 0 } /* lvl 9 */
};
#define ATH9K_ANI_OFDM_NUM_LEVEL \
ARRAY_SIZE(ofdm_level_table)
@@ -91,8 +91,8 @@ static const struct ani_cck_level_entry cck_level_table[] = {
{ 4, 0 }, /* lvl 4 */
{ 5, 0 }, /* lvl 5 */
{ 6, 0 }, /* lvl 6 */
- { 7, 0 }, /* lvl 7 (only for high rssi) */
- { 8, 0 } /* lvl 8 (only for high rssi) */
+ { 6, 0 }, /* lvl 7 (only for high rssi) */
+ { 7, 0 } /* lvl 8 (only for high rssi) */
};
#define ATH9K_ANI_CCK_NUM_LEVEL \
@@ -290,16 +290,9 @@ static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel)
ATH9K_ANI_FIRSTEP_LEVEL,
entry_ofdm->fir_step_level);
- if ((ah->opmode != NL80211_IFTYPE_STATION &&
- ah->opmode != NL80211_IFTYPE_ADHOC) ||
- aniState->noiseFloor <= aniState->rssiThrHigh) {
- if (aniState->ofdmWeakSigDetectOff)
- /* force on ofdm weak sig detect */
- ath9k_hw_ani_control(ah,
- ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
- true);
- else if (aniState->ofdmWeakSigDetectOff ==
- entry_ofdm->ofdm_weak_signal_on)
+ if ((aniState->noiseFloor >= aniState->rssiThrHigh) &&
+ (!aniState->ofdmWeakSigDetectOff !=
+ entry_ofdm->ofdm_weak_signal_on)) {
ath9k_hw_ani_control(ah,
ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
entry_ofdm->ofdm_weak_signal_on);
@@ -717,26 +710,30 @@ void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan)
ofdmPhyErrRate, aniState->cckNoiseImmunityLevel,
cckPhyErrRate, aniState->ofdmsTurn);
- if (aniState->listenTime > 5 * ah->aniperiod) {
- if (ofdmPhyErrRate <= ah->config.ofdm_trig_low &&
- cckPhyErrRate <= ah->config.cck_trig_low) {
+ if (aniState->listenTime > ah->aniperiod) {
+ if (cckPhyErrRate < ah->config.cck_trig_low &&
+ ((ofdmPhyErrRate < ah->config.ofdm_trig_low &&
+ aniState->ofdmNoiseImmunityLevel <
+ ATH9K_ANI_OFDM_DEF_LEVEL) ||
+ (ofdmPhyErrRate < ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI &&
+ aniState->ofdmNoiseImmunityLevel >=
+ ATH9K_ANI_OFDM_DEF_LEVEL))) {
ath9k_hw_ani_lower_immunity(ah);
aniState->ofdmsTurn = !aniState->ofdmsTurn;
- }
- ath9k_ani_restart(ah);
- } else if (aniState->listenTime > ah->aniperiod) {
- /* check to see if need to raise immunity */
- if (ofdmPhyErrRate > ah->config.ofdm_trig_high &&
- (cckPhyErrRate <= ah->config.cck_trig_high ||
- aniState->ofdmsTurn)) {
+ } else if ((ofdmPhyErrRate > ah->config.ofdm_trig_high &&
+ aniState->ofdmNoiseImmunityLevel >=
+ ATH9K_ANI_OFDM_DEF_LEVEL) ||
+ (ofdmPhyErrRate >
+ ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI &&
+ aniState->ofdmNoiseImmunityLevel <
+ ATH9K_ANI_OFDM_DEF_LEVEL)) {
ath9k_hw_ani_ofdm_err_trigger(ah);
- ath9k_ani_restart(ah);
aniState->ofdmsTurn = false;
} else if (cckPhyErrRate > ah->config.cck_trig_high) {
ath9k_hw_ani_cck_err_trigger(ah);
- ath9k_ani_restart(ah);
aniState->ofdmsTurn = true;
}
+ ath9k_ani_restart(ah);
}
}
EXPORT_SYMBOL(ath9k_hw_ani_monitor);
diff --git a/drivers/net/wireless/ath/ath9k/ani.h b/drivers/net/wireless/ath/ath9k/ani.h
index 83029d6..72e2b87 100644
--- a/drivers/net/wireless/ath/ath9k/ani.h
+++ b/drivers/net/wireless/ath/ath9k/ani.h
@@ -25,11 +25,13 @@
/* units are errors per second */
#define ATH9K_ANI_OFDM_TRIG_HIGH_OLD 500
-#define ATH9K_ANI_OFDM_TRIG_HIGH_NEW 1000
+#define ATH9K_ANI_OFDM_TRIG_HIGH_NEW 3500
+#define ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI 1000
/* units are errors per second */
#define ATH9K_ANI_OFDM_TRIG_LOW_OLD 200
#define ATH9K_ANI_OFDM_TRIG_LOW_NEW 400
+#define ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI 900
/* units are errors per second */
#define ATH9K_ANI_CCK_TRIG_HIGH_OLD 200
@@ -53,7 +55,7 @@
#define ATH9K_ANI_RSSI_THR_LOW 7
#define ATH9K_ANI_PERIOD_OLD 100
-#define ATH9K_ANI_PERIOD_NEW 1000
+#define ATH9K_ANI_PERIOD_NEW 300
/* in ms */
#define ATH9K_ANI_POLLINTERVAL_OLD 100
diff --git a/drivers/net/wireless/ath/ath9k/ar5008_phy.c b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
index 86a891f..96527a4 100644
--- a/drivers/net/wireless/ath/ath9k/ar5008_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar5008_phy.c
@@ -1056,46 +1056,8 @@ static bool ar5008_hw_ani_control_old(struct ath_hw *ah,
break;
}
case ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION:{
- static const int m1ThreshLow[] = { 127, 50 };
- static const int m2ThreshLow[] = { 127, 40 };
- static const int m1Thresh[] = { 127, 0x4d };
- static const int m2Thresh[] = { 127, 0x40 };
- static const int m2CountThr[] = { 31, 16 };
- static const int m2CountThrLow[] = { 63, 48 };
u32 on = param ? 1 : 0;
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
- AR_PHY_SFCORR_LOW_M1_THRESH_LOW,
- m1ThreshLow[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
- AR_PHY_SFCORR_LOW_M2_THRESH_LOW,
- m2ThreshLow[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR,
- AR_PHY_SFCORR_M1_THRESH,
- m1Thresh[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR,
- AR_PHY_SFCORR_M2_THRESH,
- m2Thresh[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR,
- AR_PHY_SFCORR_M2COUNT_THR,
- m2CountThr[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
- AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW,
- m2CountThrLow[on]);
-
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
- AR_PHY_SFCORR_EXT_M1_THRESH_LOW,
- m1ThreshLow[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
- AR_PHY_SFCORR_EXT_M2_THRESH_LOW,
- m2ThreshLow[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
- AR_PHY_SFCORR_EXT_M1_THRESH,
- m1Thresh[on]);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
- AR_PHY_SFCORR_EXT_M2_THRESH,
- m2Thresh[on]);
-
if (on)
REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
diff --git a/drivers/net/wireless/ath/ath9k/ar9003_phy.c b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
index 70e27d2..54797e3 100644
--- a/drivers/net/wireless/ath/ath9k/ar9003_phy.c
+++ b/drivers/net/wireless/ath/ath9k/ar9003_phy.c
@@ -824,55 +824,6 @@ static bool ar9003_hw_ani_control(struct ath_hw *ah,
* on == 0 means more noise imm
*/
u32 on = param ? 1 : 0;
- /*
- * make register setting for default
- * (weak sig detect ON) come from INI file
- */
- int m1ThreshLow = on ?
- aniState->iniDef.m1ThreshLow : m1ThreshLow_off;
- int m2ThreshLow = on ?
- aniState->iniDef.m2ThreshLow : m2ThreshLow_off;
- int m1Thresh = on ?
- aniState->iniDef.m1Thresh : m1Thresh_off;
- int m2Thresh = on ?
- aniState->iniDef.m2Thresh : m2Thresh_off;
- int m2CountThr = on ?
- aniState->iniDef.m2CountThr : m2CountThr_off;
- int m2CountThrLow = on ?
- aniState->iniDef.m2CountThrLow : m2CountThrLow_off;
- int m1ThreshLowExt = on ?
- aniState->iniDef.m1ThreshLowExt : m1ThreshLowExt_off;
- int m2ThreshLowExt = on ?
- aniState->iniDef.m2ThreshLowExt : m2ThreshLowExt_off;
- int m1ThreshExt = on ?
- aniState->iniDef.m1ThreshExt : m1ThreshExt_off;
- int m2ThreshExt = on ?
- aniState->iniDef.m2ThreshExt : m2ThreshExt_off;
-
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
- AR_PHY_SFCORR_LOW_M1_THRESH_LOW,
- m1ThreshLow);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
- AR_PHY_SFCORR_LOW_M2_THRESH_LOW,
- m2ThreshLow);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR,
- AR_PHY_SFCORR_M1_THRESH, m1Thresh);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR,
- AR_PHY_SFCORR_M2_THRESH, m2Thresh);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR,
- AR_PHY_SFCORR_M2COUNT_THR, m2CountThr);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
- AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW,
- m2CountThrLow);
-
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
- AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1ThreshLowExt);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
- AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2ThreshLowExt);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
- AR_PHY_SFCORR_EXT_M1_THRESH, m1ThreshExt);
- REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
- AR_PHY_SFCORR_EXT_M2_THRESH, m2ThreshExt);
if (on)
REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
--
1.7.9.4
On 2012-03-13 10:38 PM, Rajkumar Manoharan wrote:
> In the experiment with Azimuth ADEPT-n testbed where the APs transmit
> power was reduced to 25% and the signal strength was futher attenuated
> by 20dB and induced a path loss of ~7dB, the station was reporting
> beacon losses and the following issue were observed.
>
> * rx clear is stuck at low for more than 100ms
> * dcu chain and complete state is stuck at one of the hang signature
>
> This patch triggers the hang detection logic that recovers the chip
> from any of the above conditions. As the issue was originally reported
> in ChromeOs with AR9382 chips, this detection logic is enabled only for
> AR9380/2 chips.
>
> Cc: Paul Stewart <[email protected]>
> Reported-by: Gary Morain <[email protected]>
> Signed-off-by: Rajkumar Manoharan <[email protected]>
> ---
> drivers/net/wireless/ath/ath.h | 1 +
> drivers/net/wireless/ath/ath9k/ath9k.h | 3 ++
> drivers/net/wireless/ath/ath9k/hw.c | 71 ++++++++++++++++++++++++++++++++
> drivers/net/wireless/ath/ath9k/hw.h | 1 +
> drivers/net/wireless/ath/ath9k/init.c | 1 +
> drivers/net/wireless/ath/ath9k/main.c | 66 +++++++++++++++++++++++++++++
> drivers/net/wireless/ath/ath9k/recv.c | 3 ++
> drivers/net/wireless/ath/hw.c | 5 +++
> 8 files changed, 151 insertions(+)
>
> --- a/drivers/net/wireless/ath/ath9k/hw.c
> +++ b/drivers/net/wireless/ath/ath9k/hw.c
> @@ -3045,3 +3045,74 @@ void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len)
> hw_name[used] = '\0';
> }
> EXPORT_SYMBOL(ath9k_hw_name);
> +
> +static bool ath9k_hw_check_dcs(u32 dma_dbg, u32 num_dcu_states,
> + int *hang_state, int *hang_pos)
> +{
> + static u32 dcu_chain_state[] = {5, 6, 9}; /* DCU chain stuck states */
> + u32 chain_state, dcs_pos, i;
> +
> + for (dcs_pos = 0; dcs_pos < num_dcu_states; dcs_pos++) {
> + chain_state = (dma_dbg >> (5 * dcs_pos)) & 0x1f;
> + for (i = 0; i < 3; i++) {
> + if (chain_state == dcu_chain_state[i]) {
> + *hang_state = chain_state;
> + *hang_pos = dcs_pos;
> + return true;
> + }
> + }
> + }
> + return false;
> +}
> +
> +#define DCU_COMPLETE_STATE 1
> +#define DCU_COMPLETE_STATE_MASK 0x3
> +#define NUM_STATUS_READS 50
> +bool ath9k_hw_detect_mac_hang(struct ath_hw *ah)
> +{
> + u32 chain_state, comp_state, dcs_reg = AR_DMADBG_4;
> + u32 i, hang_pos, hang_state, num_state = 6;
> +
> + comp_state = REG_READ(ah, AR_DMADBG_6);
> +
> + if ((comp_state & DCU_COMPLETE_STATE_MASK) != DCU_COMPLETE_STATE) {
> + ath_dbg(ath9k_hw_common(ah), RESET,
> + "MAC Hang signature not found at DCU complete\n");
> + return false;
> + }
> +
> + chain_state = REG_READ(ah, dcs_reg);
> + if (ath9k_hw_check_dcs(chain_state, num_state, &hang_state, &hang_pos))
> + goto hang_check_iter;
> +
> + dcs_reg = AR_DMADBG_5;
> + num_state = 4;
> + chain_state = REG_READ(ah, dcs_reg);
> + if (ath9k_hw_check_dcs(chain_state, num_state, &hang_state, &hang_pos))
> + goto hang_check_iter;
> +
> + ath_dbg(ath9k_hw_common(ah), RESET,
> + "MAC Hang signature 1 not found\n");
> + return false;
> +
> +hang_check_iter:
> + ath_dbg(ath9k_hw_common(ah), RESET,
> + "DCU registers: chain %08x complete %08x Hang: state %d pos %d\n",
> + chain_state, comp_state, hang_state, hang_pos);
> +
> + for (i = 0; i < NUM_STATUS_READS; i++) {
> + chain_state = REG_READ(ah, dcs_reg);
> + chain_state = (chain_state >> (5 * hang_pos)) & 0x1f;
> + comp_state = REG_READ(ah, AR_DMADBG_6);
> +
> + if (((comp_state & DCU_COMPLETE_STATE_MASK) !=
> + DCU_COMPLETE_STATE) ||
> + (chain_state != hang_state))
> + return false;
> + }
> +
> + ath_dbg(ath9k_hw_common(ah), RESET, "MAC Hang signature 1 found\n");
> +
> + return true;
> +}
> +EXPORT_SYMBOL(ath9k_hw_detect_mac_hang);
This could be merged into ath9k_hw_check_alive()
> --- a/drivers/net/wireless/ath/ath9k/main.c
> +++ b/drivers/net/wireless/ath/ath9k/main.c
> @@ -1396,6 +1399,65 @@ static void ath9k_do_vif_add_setup(struct ieee80211_hw *hw,
> }
> }
>
> +void ath_start_rx_poll(struct ath_softc *sc, u32 nmsec)
> +{
> + if (!AR_SREV_9300(sc->sc_ah))
> + return;
> +
> + if (!(sc->sc_flags & SC_OP_PRIM_STA_VIF))
> + return;
> +
> + mod_timer(&sc->rx_poll_timer, jiffies + msecs_to_jiffies(nmsec));
> +}
> +
> +void ath_rx_poll_work(unsigned long data)
> +{
> + struct ath_softc *sc = (struct ath_softc *)data;
> + struct ath_hw *ah = sc->sc_ah;
> + struct ath_common *common = ath9k_hw_common(ah);
> + static u32 iteration, match_count;
> + static u64 last_run;
> + unsigned long flags;
> + u32 rx_clear, rx, tx, nmsec = 10;
> +
> + if (jiffies_to_msecs(jiffies - last_run) > 120)
> + iteration = match_count = 0;
> + else
> + iteration += 1;
> +
> + ath9k_ps_wakeup(sc);
> +
> + spin_lock_irqsave(&common->cc_lock, flags);
> + ath_hw_cycle_counters_update(common);
> +
> + rx_clear = common->cc_rxpoll.rx_busy * 100 / common->cc_rxpoll.cycles;
> + rx = common->cc_rxpoll.rx_frame * 100 / common->cc_rxpoll.cycles;
> + tx = common->cc_rxpoll.tx_frame * 100 / common->cc_rxpoll.cycles;
> + memset(&common->cc_rxpoll, 0, sizeof(common->cc_rxpoll));
> + spin_unlock_irqrestore(&common->cc_lock, flags);
> +
> + last_run = jiffies;
> + if (rx_clear > 98) {
> + ath_dbg(common, RESET,
> + "rx clear %d match count %d iteration %d\n",
> + rx_clear, match_count, iteration);
> + if (match_count++ > 9)
> + goto queue_reset_work;
> + } else if (ath9k_hw_detect_mac_hang(ah))
> + goto queue_reset_work;
> + else if (iteration >= 15) {
> + iteration = match_count = 0;
> + nmsec = 200;
> + }
> + ath9k_ps_restore(sc);
> + ath_start_rx_poll(sc, nmsec);
> + return;
> +
> +queue_reset_work:
> + ath9k_ps_restore(sc);
> + ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
> + iteration = match_count = 0;
> +}
>
> static int ath9k_add_interface(struct ieee80211_hw *hw,
> struct ieee80211_vif *vif)
How about merging this with ath_hw_check() which does similar things?
That way AP mode would get coverage as well.
> diff --git a/drivers/net/wireless/ath/hw.c b/drivers/net/wireless/ath/hw.c
> index 19befb3..f1821ea 100644
> --- a/drivers/net/wireless/ath/hw.c
> +++ b/drivers/net/wireless/ath/hw.c
> @@ -166,6 +166,11 @@ void ath_hw_cycle_counters_update(struct ath_common *common)
> common->cc_survey.rx_busy += busy;
> common->cc_survey.rx_frame += rx;
> common->cc_survey.tx_frame += tx;
> +
> + common->cc_rxpoll.cycles += cycles;
> + common->cc_rxpoll.rx_busy += busy;
> + common->cc_rxpoll.rx_frame += rx;
> + common->cc_rxpoll.tx_frame += tx;
> }
> EXPORT_SYMBOL(ath_hw_cycle_counters_update);
Do we really need yet another cycle counter state here? How about
reusing the survey counters like ath_hw_check() - or even reusing that
chunk of code entirely.
- Felix
In the experiment with Azimuth ADEPT-n testbed where the APs transmit
power was reduced to 25% and the signal strength was futher attenuated
by 20dB and induced a path loss of ~7dB, the station was reporting
beacon losses and the following issue were observed.
* rx clear is stuck at low for more than 100ms
* dcu chain and complete state is stuck at one of the hang signature
This patch triggers the hang detection logic that recovers the chip
from any of the above conditions. As the issue was originally reported
in ChromeOs with AR9382 chips, this detection logic is enabled only for
AR9380/2 chips.
Cc: Paul Stewart <[email protected]>
Reported-by: Gary Morain <[email protected]>
Signed-off-by: Rajkumar Manoharan <[email protected]>
---
drivers/net/wireless/ath/ath.h | 1 +
drivers/net/wireless/ath/ath9k/ath9k.h | 3 ++
drivers/net/wireless/ath/ath9k/hw.c | 71 ++++++++++++++++++++++++++++++++
drivers/net/wireless/ath/ath9k/hw.h | 1 +
drivers/net/wireless/ath/ath9k/init.c | 1 +
drivers/net/wireless/ath/ath9k/main.c | 66 +++++++++++++++++++++++++++++
drivers/net/wireless/ath/ath9k/recv.c | 3 ++
drivers/net/wireless/ath/hw.c | 5 +++
8 files changed, 151 insertions(+)
diff --git a/drivers/net/wireless/ath/ath.h b/drivers/net/wireless/ath/ath.h
index c54b7d37..962b52f 100644
--- a/drivers/net/wireless/ath/ath.h
+++ b/drivers/net/wireless/ath/ath.h
@@ -150,6 +150,7 @@ struct ath_common {
spinlock_t cc_lock;
struct ath_cycle_counters cc_ani;
struct ath_cycle_counters cc_survey;
+ struct ath_cycle_counters cc_rxpoll;
struct ath_regulatory regulatory;
struct ath_regulatory reg_world_copy;
diff --git a/drivers/net/wireless/ath/ath9k/ath9k.h b/drivers/net/wireless/ath/ath9k/ath9k.h
index 3d8e51c..f039593 100644
--- a/drivers/net/wireless/ath/ath9k/ath9k.h
+++ b/drivers/net/wireless/ath/ath9k/ath9k.h
@@ -430,6 +430,8 @@ void ath9k_set_beaconing_status(struct ath_softc *sc, bool status);
void ath_reset_work(struct work_struct *work);
void ath_hw_check(struct work_struct *work);
void ath_hw_pll_work(struct work_struct *work);
+void ath_rx_poll_work(unsigned long data);
+void ath_start_rx_poll(struct ath_softc *sc, u32 nmsec);
void ath_paprd_calibrate(struct work_struct *work);
void ath_ani_calibrate(unsigned long data);
void ath_start_ani(struct ath_common *common);
@@ -679,6 +681,7 @@ struct ath_softc {
struct ath_beacon_config cur_beacon_conf;
struct delayed_work tx_complete_work;
struct delayed_work hw_pll_work;
+ struct timer_list rx_poll_timer;
#ifdef CONFIG_ATH9K_BTCOEX_SUPPORT
struct ath_btcoex btcoex;
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 02cc1ce..51ebd7f 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -3045,3 +3045,74 @@ void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len)
hw_name[used] = '\0';
}
EXPORT_SYMBOL(ath9k_hw_name);
+
+static bool ath9k_hw_check_dcs(u32 dma_dbg, u32 num_dcu_states,
+ int *hang_state, int *hang_pos)
+{
+ static u32 dcu_chain_state[] = {5, 6, 9}; /* DCU chain stuck states */
+ u32 chain_state, dcs_pos, i;
+
+ for (dcs_pos = 0; dcs_pos < num_dcu_states; dcs_pos++) {
+ chain_state = (dma_dbg >> (5 * dcs_pos)) & 0x1f;
+ for (i = 0; i < 3; i++) {
+ if (chain_state == dcu_chain_state[i]) {
+ *hang_state = chain_state;
+ *hang_pos = dcs_pos;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+#define DCU_COMPLETE_STATE 1
+#define DCU_COMPLETE_STATE_MASK 0x3
+#define NUM_STATUS_READS 50
+bool ath9k_hw_detect_mac_hang(struct ath_hw *ah)
+{
+ u32 chain_state, comp_state, dcs_reg = AR_DMADBG_4;
+ u32 i, hang_pos, hang_state, num_state = 6;
+
+ comp_state = REG_READ(ah, AR_DMADBG_6);
+
+ if ((comp_state & DCU_COMPLETE_STATE_MASK) != DCU_COMPLETE_STATE) {
+ ath_dbg(ath9k_hw_common(ah), RESET,
+ "MAC Hang signature not found at DCU complete\n");
+ return false;
+ }
+
+ chain_state = REG_READ(ah, dcs_reg);
+ if (ath9k_hw_check_dcs(chain_state, num_state, &hang_state, &hang_pos))
+ goto hang_check_iter;
+
+ dcs_reg = AR_DMADBG_5;
+ num_state = 4;
+ chain_state = REG_READ(ah, dcs_reg);
+ if (ath9k_hw_check_dcs(chain_state, num_state, &hang_state, &hang_pos))
+ goto hang_check_iter;
+
+ ath_dbg(ath9k_hw_common(ah), RESET,
+ "MAC Hang signature 1 not found\n");
+ return false;
+
+hang_check_iter:
+ ath_dbg(ath9k_hw_common(ah), RESET,
+ "DCU registers: chain %08x complete %08x Hang: state %d pos %d\n",
+ chain_state, comp_state, hang_state, hang_pos);
+
+ for (i = 0; i < NUM_STATUS_READS; i++) {
+ chain_state = REG_READ(ah, dcs_reg);
+ chain_state = (chain_state >> (5 * hang_pos)) & 0x1f;
+ comp_state = REG_READ(ah, AR_DMADBG_6);
+
+ if (((comp_state & DCU_COMPLETE_STATE_MASK) !=
+ DCU_COMPLETE_STATE) ||
+ (chain_state != hang_state))
+ return false;
+ }
+
+ ath_dbg(ath9k_hw_common(ah), RESET, "MAC Hang signature 1 found\n");
+
+ return true;
+}
+EXPORT_SYMBOL(ath9k_hw_detect_mac_hang);
diff --git a/drivers/net/wireless/ath/ath9k/hw.h b/drivers/net/wireless/ath/ath9k/hw.h
index 1707137..c9031da 100644
--- a/drivers/net/wireless/ath/ath9k/hw.h
+++ b/drivers/net/wireless/ath/ath9k/hw.h
@@ -987,6 +987,7 @@ void ath_gen_timer_free(struct ath_hw *ah, struct ath_gen_timer *timer);
void ath_gen_timer_isr(struct ath_hw *hw);
void ath9k_hw_name(struct ath_hw *ah, char *hw_name, size_t len);
+bool ath9k_hw_detect_mac_hang(struct ath_hw *ah);
/* HTC */
void ath9k_hw_htc_resetinit(struct ath_hw *ah);
diff --git a/drivers/net/wireless/ath/ath9k/init.c b/drivers/net/wireless/ath/ath9k/init.c
index 944e9b5..73de7e3 100644
--- a/drivers/net/wireless/ath/ath9k/init.c
+++ b/drivers/net/wireless/ath/ath9k/init.c
@@ -787,6 +787,7 @@ int ath9k_init_device(u16 devid, struct ath_softc *sc,
goto error_world;
}
+ setup_timer(&sc->rx_poll_timer, ath_rx_poll_work, (unsigned long)sc);
sc->last_rssi = ATH_RSSI_DUMMY_MARKER;
ath_init_leds(sc);
diff --git a/drivers/net/wireless/ath/ath9k/main.c b/drivers/net/wireless/ath/ath9k/main.c
index 4a99010..d1c9998 100644
--- a/drivers/net/wireless/ath/ath9k/main.c
+++ b/drivers/net/wireless/ath/ath9k/main.c
@@ -243,6 +243,7 @@ static bool ath_prepare_reset(struct ath_softc *sc, bool retry_tx, bool flush)
sc->hw_busy_count = 0;
del_timer_sync(&common->ani.timer);
+ del_timer_sync(&sc->rx_poll_timer);
ath9k_debug_samp_bb_mac(sc);
ath9k_hw_disable_interrupts(ah);
@@ -284,6 +285,7 @@ static bool ath_complete_reset(struct ath_softc *sc, bool start)
ieee80211_queue_delayed_work(sc->hw, &sc->tx_complete_work, 0);
ieee80211_queue_delayed_work(sc->hw, &sc->hw_pll_work, HZ/2);
+ ath_start_rx_poll(sc, sc->cur_beacon_conf.beacon_interval);
if (!common->disable_ani)
ath_start_ani(common);
}
@@ -1163,6 +1165,7 @@ static void ath9k_stop(struct ieee80211_hw *hw)
mutex_lock(&sc->mutex);
ath_cancel_work(sc);
+ del_timer_sync(&sc->rx_poll_timer);
if (sc->sc_flags & SC_OP_INVALID) {
ath_dbg(common, ANY, "Device not present\n");
@@ -1396,6 +1399,65 @@ static void ath9k_do_vif_add_setup(struct ieee80211_hw *hw,
}
}
+void ath_start_rx_poll(struct ath_softc *sc, u32 nmsec)
+{
+ if (!AR_SREV_9300(sc->sc_ah))
+ return;
+
+ if (!(sc->sc_flags & SC_OP_PRIM_STA_VIF))
+ return;
+
+ mod_timer(&sc->rx_poll_timer, jiffies + msecs_to_jiffies(nmsec));
+}
+
+void ath_rx_poll_work(unsigned long data)
+{
+ struct ath_softc *sc = (struct ath_softc *)data;
+ struct ath_hw *ah = sc->sc_ah;
+ struct ath_common *common = ath9k_hw_common(ah);
+ static u32 iteration, match_count;
+ static u64 last_run;
+ unsigned long flags;
+ u32 rx_clear, rx, tx, nmsec = 10;
+
+ if (jiffies_to_msecs(jiffies - last_run) > 120)
+ iteration = match_count = 0;
+ else
+ iteration += 1;
+
+ ath9k_ps_wakeup(sc);
+
+ spin_lock_irqsave(&common->cc_lock, flags);
+ ath_hw_cycle_counters_update(common);
+
+ rx_clear = common->cc_rxpoll.rx_busy * 100 / common->cc_rxpoll.cycles;
+ rx = common->cc_rxpoll.rx_frame * 100 / common->cc_rxpoll.cycles;
+ tx = common->cc_rxpoll.tx_frame * 100 / common->cc_rxpoll.cycles;
+ memset(&common->cc_rxpoll, 0, sizeof(common->cc_rxpoll));
+ spin_unlock_irqrestore(&common->cc_lock, flags);
+
+ last_run = jiffies;
+ if (rx_clear > 98) {
+ ath_dbg(common, RESET,
+ "rx clear %d match count %d iteration %d\n",
+ rx_clear, match_count, iteration);
+ if (match_count++ > 9)
+ goto queue_reset_work;
+ } else if (ath9k_hw_detect_mac_hang(ah))
+ goto queue_reset_work;
+ else if (iteration >= 15) {
+ iteration = match_count = 0;
+ nmsec = 200;
+ }
+ ath9k_ps_restore(sc);
+ ath_start_rx_poll(sc, nmsec);
+ return;
+
+queue_reset_work:
+ ath9k_ps_restore(sc);
+ ieee80211_queue_work(sc->hw, &sc->hw_reset_work);
+ iteration = match_count = 0;
+}
static int ath9k_add_interface(struct ieee80211_hw *hw,
struct ieee80211_vif *vif)
@@ -1917,6 +1979,8 @@ static void ath9k_bss_iter(void *data, u8 *mac, struct ieee80211_vif *vif)
sc->last_rssi = ATH_RSSI_DUMMY_MARKER;
sc->sc_ah->stats.avgbrssi = ATH_RSSI_DUMMY_MARKER;
+ ath_start_rx_poll(sc, 3 * bss_conf->beacon_int);
+
if (!common->disable_ani) {
sc->sc_flags |= SC_OP_ANI_RUN;
ath_start_ani(common);
@@ -1956,6 +2020,7 @@ static void ath9k_config_bss(struct ath_softc *sc, struct ieee80211_vif *vif)
/* Stop ANI */
sc->sc_flags &= ~SC_OP_ANI_RUN;
del_timer_sync(&common->ani.timer);
+ del_timer_sync(&sc->rx_poll_timer);
memset(&sc->caldata, 0, sizeof(sc->caldata));
}
}
@@ -1999,6 +2064,7 @@ static void ath9k_bss_info_changed(struct ieee80211_hw *hw,
} else {
sc->sc_flags &= ~SC_OP_ANI_RUN;
del_timer_sync(&common->ani.timer);
+ del_timer_sync(&sc->rx_poll_timer);
}
}
diff --git a/drivers/net/wireless/ath/ath9k/recv.c b/drivers/net/wireless/ath/ath9k/recv.c
index f4ae3ba..1a139d4 100644
--- a/drivers/net/wireless/ath/ath9k/recv.c
+++ b/drivers/net/wireless/ath/ath9k/recv.c
@@ -1855,6 +1855,9 @@ int ath_rx_tasklet(struct ath_softc *sc, int flush, bool hp)
if (retval)
goto requeue_drop_frag;
+ if (rs.is_mybeacon)
+ ath_start_rx_poll(sc, 3 * sc->cur_beacon_conf.
+ beacon_interval);
/* Ensure we always have an skb to requeue once we are done
* processing the current buffer's skb */
requeue_skb = ath_rxbuf_alloc(common, common->rx_bufsize, GFP_ATOMIC);
diff --git a/drivers/net/wireless/ath/hw.c b/drivers/net/wireless/ath/hw.c
index 19befb3..f1821ea 100644
--- a/drivers/net/wireless/ath/hw.c
+++ b/drivers/net/wireless/ath/hw.c
@@ -166,6 +166,11 @@ void ath_hw_cycle_counters_update(struct ath_common *common)
common->cc_survey.rx_busy += busy;
common->cc_survey.rx_frame += rx;
common->cc_survey.tx_frame += tx;
+
+ common->cc_rxpoll.cycles += cycles;
+ common->cc_rxpoll.rx_busy += busy;
+ common->cc_rxpoll.rx_frame += rx;
+ common->cc_rxpoll.tx_frame += tx;
}
EXPORT_SYMBOL(ath_hw_cycle_counters_update);
--
1.7.9.4