xref: /openbmc/linux/drivers/net/wireless/ath/ath9k/ani.c (revision cbecf716ca618fd44feda6bd9a64a8179d031fc5)
1203c4805SLuis R. Rodriguez /*
25b68138eSSujith Manoharan  * Copyright (c) 2008-2011 Atheros Communications Inc.
3203c4805SLuis R. Rodriguez  *
4203c4805SLuis R. Rodriguez  * Permission to use, copy, modify, and/or distribute this software for any
5203c4805SLuis R. Rodriguez  * purpose with or without fee is hereby granted, provided that the above
6203c4805SLuis R. Rodriguez  * copyright notice and this permission notice appear in all copies.
7203c4805SLuis R. Rodriguez  *
8203c4805SLuis R. Rodriguez  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9203c4805SLuis R. Rodriguez  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10203c4805SLuis R. Rodriguez  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11203c4805SLuis R. Rodriguez  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12203c4805SLuis R. Rodriguez  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13203c4805SLuis R. Rodriguez  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14203c4805SLuis R. Rodriguez  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15203c4805SLuis R. Rodriguez  */
16203c4805SLuis R. Rodriguez 
17bbce80e1SNikitas Angelinas #include <linux/kernel.h>
18ee40fa06SPaul Gortmaker #include <linux/export.h>
19cfe8cba9SLuis R. Rodriguez #include "hw.h"
20c16fcb49SFelix Fietkau #include "hw-ops.h"
21203c4805SLuis R. Rodriguez 
22e36b27afSLuis R. Rodriguez struct ani_ofdm_level_entry {
23e36b27afSLuis R. Rodriguez 	int spur_immunity_level;
24e36b27afSLuis R. Rodriguez 	int fir_step_level;
25e36b27afSLuis R. Rodriguez 	int ofdm_weak_signal_on;
26e36b27afSLuis R. Rodriguez };
27e36b27afSLuis R. Rodriguez 
28e36b27afSLuis R. Rodriguez /* values here are relative to the INI */
29e36b27afSLuis R. Rodriguez 
30e36b27afSLuis R. Rodriguez /*
31e36b27afSLuis R. Rodriguez  * Legend:
32e36b27afSLuis R. Rodriguez  *
33e36b27afSLuis R. Rodriguez  * SI: Spur immunity
34e36b27afSLuis R. Rodriguez  * FS: FIR Step
35e36b27afSLuis R. Rodriguez  * WS: OFDM / CCK Weak Signal detection
36e36b27afSLuis R. Rodriguez  * MRC-CCK: Maximal Ratio Combining for CCK
37e36b27afSLuis R. Rodriguez  */
38e36b27afSLuis R. Rodriguez 
39e36b27afSLuis R. Rodriguez static const struct ani_ofdm_level_entry ofdm_level_table[] = {
40e36b27afSLuis R. Rodriguez 	/* SI  FS  WS */
41e36b27afSLuis R. Rodriguez 	{  0,  0,  1  }, /* lvl 0 */
42e36b27afSLuis R. Rodriguez 	{  1,  1,  1  }, /* lvl 1 */
43e36b27afSLuis R. Rodriguez 	{  2,  2,  1  }, /* lvl 2 */
44e36b27afSLuis R. Rodriguez 	{  3,  2,  1  }, /* lvl 3  (default) */
45e36b27afSLuis R. Rodriguez 	{  4,  3,  1  }, /* lvl 4 */
46e36b27afSLuis R. Rodriguez 	{  5,  4,  1  }, /* lvl 5 */
47e36b27afSLuis R. Rodriguez 	{  6,  5,  1  }, /* lvl 6 */
48e36b27afSLuis R. Rodriguez 	{  7,  6,  1  }, /* lvl 7 */
49b99553fbSSujith Manoharan 	{  7,  7,  1  }, /* lvl 8 */
50b99553fbSSujith Manoharan 	{  7,  8,  0  }  /* lvl 9 */
51e36b27afSLuis R. Rodriguez };
52e36b27afSLuis R. Rodriguez #define ATH9K_ANI_OFDM_NUM_LEVEL \
53bbce80e1SNikitas Angelinas 	ARRAY_SIZE(ofdm_level_table)
54e36b27afSLuis R. Rodriguez #define ATH9K_ANI_OFDM_MAX_LEVEL \
55e36b27afSLuis R. Rodriguez 	(ATH9K_ANI_OFDM_NUM_LEVEL-1)
56e36b27afSLuis R. Rodriguez #define ATH9K_ANI_OFDM_DEF_LEVEL \
57e36b27afSLuis R. Rodriguez 	3 /* default level - matches the INI settings */
58e36b27afSLuis R. Rodriguez 
59e36b27afSLuis R. Rodriguez /*
60e36b27afSLuis R. Rodriguez  * MRC (Maximal Ratio Combining) has always been used with multi-antenna ofdm.
61e36b27afSLuis R. Rodriguez  * With OFDM for single stream you just add up all antenna inputs, you're
62550116d2SMasahiro Yamada  * only interested in what you get after FFT. Signal alignment is also not
63e36b27afSLuis R. Rodriguez  * required for OFDM because any phase difference adds up in the frequency
64e36b27afSLuis R. Rodriguez  * domain.
65e36b27afSLuis R. Rodriguez  *
66e36b27afSLuis R. Rodriguez  * MRC requires extra work for use with CCK. You need to align the antenna
67e36b27afSLuis R. Rodriguez  * signals from the different antenna before you can add the signals together.
68550116d2SMasahiro Yamada  * You need alignment of signals as CCK is in time domain, so addition can cancel
69e36b27afSLuis R. Rodriguez  * your signal completely if phase is 180 degrees (think of adding sine waves).
70e36b27afSLuis R. Rodriguez  * You also need to remove noise before the addition and this is where ANI
71e36b27afSLuis R. Rodriguez  * MRC CCK comes into play. One of the antenna inputs may be stronger but
72e36b27afSLuis R. Rodriguez  * lower SNR, so just adding after alignment can be dangerous.
73e36b27afSLuis R. Rodriguez  *
74e36b27afSLuis R. Rodriguez  * Regardless of alignment in time, the antenna signals add constructively after
75e36b27afSLuis R. Rodriguez  * FFT and improve your reception. For more information:
76e36b27afSLuis R. Rodriguez  *
77*06020202SAlexander A. Klimov  * https://en.wikipedia.org/wiki/Maximal-ratio_combining
78e36b27afSLuis R. Rodriguez  */
79e36b27afSLuis R. Rodriguez 
80e36b27afSLuis R. Rodriguez struct ani_cck_level_entry {
81e36b27afSLuis R. Rodriguez 	int fir_step_level;
82e36b27afSLuis R. Rodriguez 	int mrc_cck_on;
83e36b27afSLuis R. Rodriguez };
84e36b27afSLuis R. Rodriguez 
85e36b27afSLuis R. Rodriguez static const struct ani_cck_level_entry cck_level_table[] = {
86e36b27afSLuis R. Rodriguez 	/* FS  MRC-CCK  */
87e36b27afSLuis R. Rodriguez 	{  0,  1  }, /* lvl 0 */
88e36b27afSLuis R. Rodriguez 	{  1,  1  }, /* lvl 1 */
89e36b27afSLuis R. Rodriguez 	{  2,  1  }, /* lvl 2  (default) */
90e36b27afSLuis R. Rodriguez 	{  3,  1  }, /* lvl 3 */
91e36b27afSLuis R. Rodriguez 	{  4,  0  }, /* lvl 4 */
92e36b27afSLuis R. Rodriguez 	{  5,  0  }, /* lvl 5 */
93e36b27afSLuis R. Rodriguez 	{  6,  0  }, /* lvl 6 */
94b99553fbSSujith Manoharan 	{  7,  0  }, /* lvl 7 (only for high rssi) */
95b99553fbSSujith Manoharan 	{  8,  0  }  /* lvl 8 (only for high rssi) */
96e36b27afSLuis R. Rodriguez };
97e36b27afSLuis R. Rodriguez 
98e36b27afSLuis R. Rodriguez #define ATH9K_ANI_CCK_NUM_LEVEL \
99bbce80e1SNikitas Angelinas 	ARRAY_SIZE(cck_level_table)
100e36b27afSLuis R. Rodriguez #define ATH9K_ANI_CCK_MAX_LEVEL \
101e36b27afSLuis R. Rodriguez 	(ATH9K_ANI_CCK_NUM_LEVEL-1)
102e36b27afSLuis R. Rodriguez #define ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI \
103e36b27afSLuis R. Rodriguez 	(ATH9K_ANI_CCK_NUM_LEVEL-3)
104e36b27afSLuis R. Rodriguez #define ATH9K_ANI_CCK_DEF_LEVEL \
105e36b27afSLuis R. Rodriguez 	2 /* default level - matches the INI settings */
106e36b27afSLuis R. Rodriguez 
ath9k_hw_update_mibstats(struct ath_hw * ah,struct ath9k_mib_stats * stats)107203c4805SLuis R. Rodriguez static void ath9k_hw_update_mibstats(struct ath_hw *ah,
108203c4805SLuis R. Rodriguez 				     struct ath9k_mib_stats *stats)
109203c4805SLuis R. Rodriguez {
1109c05babdSOleksij Rempel 	u32 addr[5] = {AR_RTS_OK, AR_RTS_FAIL, AR_ACK_FAIL,
1119c05babdSOleksij Rempel 		       AR_FCS_FAIL, AR_BEACON_CNT};
1129c05babdSOleksij Rempel 	u32 data[5];
1139c05babdSOleksij Rempel 
1149c05babdSOleksij Rempel 	REG_READ_MULTI(ah, &addr[0], &data[0], 5);
1159c05babdSOleksij Rempel 	/* AR_RTS_OK */
1169c05babdSOleksij Rempel 	stats->rts_good += data[0];
1179c05babdSOleksij Rempel 	/* AR_RTS_FAIL */
1189c05babdSOleksij Rempel 	stats->rts_bad += data[1];
1199c05babdSOleksij Rempel 	/* AR_ACK_FAIL */
1209c05babdSOleksij Rempel 	stats->ackrcv_bad += data[2];
1219c05babdSOleksij Rempel 	/* AR_FCS_FAIL */
1229c05babdSOleksij Rempel 	stats->fcs_bad += data[3];
1239c05babdSOleksij Rempel 	/* AR_BEACON_CNT */
1249c05babdSOleksij Rempel 	stats->beacons += data[4];
125203c4805SLuis R. Rodriguez }
126203c4805SLuis R. Rodriguez 
ath9k_ani_restart(struct ath_hw * ah)127093115b7SFelix Fietkau static void ath9k_ani_restart(struct ath_hw *ah)
128203c4805SLuis R. Rodriguez {
12966b533d5SMiaoqing Pan 	struct ar5416AniState *aniState = &ah->ani;
130203c4805SLuis R. Rodriguez 
131203c4805SLuis R. Rodriguez 	aniState->listenTime = 0;
1321aa8e847SSujith 
1337d0d0df0SSujith 	ENABLE_REGWRITE_BUFFER(ah);
1347d0d0df0SSujith 
135465dce62SFelix Fietkau 	REG_WRITE(ah, AR_PHY_ERR_1, 0);
136465dce62SFelix Fietkau 	REG_WRITE(ah, AR_PHY_ERR_2, 0);
137e36b27afSLuis R. Rodriguez 	REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
138e36b27afSLuis R. Rodriguez 	REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
139e36b27afSLuis R. Rodriguez 
140e36b27afSLuis R. Rodriguez 	REGWRITE_BUFFER_FLUSH(ah);
141e36b27afSLuis R. Rodriguez 
142e36b27afSLuis R. Rodriguez 	ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
143e36b27afSLuis R. Rodriguez 
144e36b27afSLuis R. Rodriguez 	aniState->ofdmPhyErrCount = 0;
145e36b27afSLuis R. Rodriguez 	aniState->cckPhyErrCount = 0;
146e36b27afSLuis R. Rodriguez }
147e36b27afSLuis R. Rodriguez 
148e36b27afSLuis R. Rodriguez /* Adjust the OFDM Noise Immunity Level */
ath9k_hw_set_ofdm_nil(struct ath_hw * ah,u8 immunityLevel,bool scan)14973dc3eb8SFelix Fietkau static void ath9k_hw_set_ofdm_nil(struct ath_hw *ah, u8 immunityLevel,
15073dc3eb8SFelix Fietkau 				  bool scan)
151e36b27afSLuis R. Rodriguez {
152c24bd362SSujith Manoharan 	struct ar5416AniState *aniState = &ah->ani;
153e36b27afSLuis R. Rodriguez 	struct ath_common *common = ath9k_hw_common(ah);
154e36b27afSLuis R. Rodriguez 	const struct ani_ofdm_level_entry *entry_ofdm;
155e36b27afSLuis R. Rodriguez 	const struct ani_cck_level_entry *entry_cck;
1560b81cc39SFelix Fietkau 	bool weak_sig;
157e36b27afSLuis R. Rodriguez 
158d2182b69SJoe Perches 	ath_dbg(common, ANI, "**** ofdmlevel %d=>%d, rssi=%d[lo=%d hi=%d]\n",
159e36b27afSLuis R. Rodriguez 		aniState->ofdmNoiseImmunityLevel,
16035e808b7SFelix Fietkau 		immunityLevel, BEACON_RSSI(ah),
1619dbac67dSFelix Fietkau 		ATH9K_ANI_RSSI_THR_LOW,
1629dbac67dSFelix Fietkau 		ATH9K_ANI_RSSI_THR_HIGH);
163e36b27afSLuis R. Rodriguez 
164ae9c25a1SFelix Fietkau 	if (AR_SREV_9100(ah) && immunityLevel < ATH9K_ANI_OFDM_DEF_LEVEL)
165ae9c25a1SFelix Fietkau 		immunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL;
166ae9c25a1SFelix Fietkau 
16773dc3eb8SFelix Fietkau 	if (!scan)
1681e8f0a31SFelix Fietkau 		aniState->ofdmNoiseImmunityLevel = immunityLevel;
169e36b27afSLuis R. Rodriguez 
170e36b27afSLuis R. Rodriguez 	entry_ofdm = &ofdm_level_table[aniState->ofdmNoiseImmunityLevel];
171e36b27afSLuis R. Rodriguez 	entry_cck = &cck_level_table[aniState->cckNoiseImmunityLevel];
172e36b27afSLuis R. Rodriguez 
173e36b27afSLuis R. Rodriguez 	if (aniState->spurImmunityLevel != entry_ofdm->spur_immunity_level)
174e36b27afSLuis R. Rodriguez 		ath9k_hw_ani_control(ah,
175e36b27afSLuis R. Rodriguez 				     ATH9K_ANI_SPUR_IMMUNITY_LEVEL,
176e36b27afSLuis R. Rodriguez 				     entry_ofdm->spur_immunity_level);
177e36b27afSLuis R. Rodriguez 
178e36b27afSLuis R. Rodriguez 	if (aniState->firstepLevel != entry_ofdm->fir_step_level &&
179e36b27afSLuis R. Rodriguez 	    entry_ofdm->fir_step_level >= entry_cck->fir_step_level)
180e36b27afSLuis R. Rodriguez 		ath9k_hw_ani_control(ah,
181e36b27afSLuis R. Rodriguez 				     ATH9K_ANI_FIRSTEP_LEVEL,
182e36b27afSLuis R. Rodriguez 				     entry_ofdm->fir_step_level);
183e36b27afSLuis R. Rodriguez 
1840b81cc39SFelix Fietkau 	weak_sig = entry_ofdm->ofdm_weak_signal_on;
1850b81cc39SFelix Fietkau 	if (ah->opmode == NL80211_IFTYPE_STATION &&
1869dbac67dSFelix Fietkau 	    BEACON_RSSI(ah) <= ATH9K_ANI_RSSI_THR_HIGH)
1870b81cc39SFelix Fietkau 		weak_sig = true;
18880b4205bSSujith Manoharan 	/*
1896241226fSFelix Fietkau 	 * Newer chipsets are better at dealing with high PHY error counts -
1906241226fSFelix Fietkau 	 * keep weak signal detection enabled when no RSSI threshold is
1916241226fSFelix Fietkau 	 * available to determine if it is needed (mode != STA)
19280b4205bSSujith Manoharan 	 */
1936241226fSFelix Fietkau 	else if (AR_SREV_9300_20_OR_LATER(ah) &&
1946241226fSFelix Fietkau 		 ah->opmode != NL80211_IFTYPE_STATION)
1956241226fSFelix Fietkau 		weak_sig = true;
1966241226fSFelix Fietkau 
197f5547245SFelix Fietkau 	/* Older chipsets are more sensitive to high PHY error counts */
198f5547245SFelix Fietkau 	else if (!AR_SREV_9300_20_OR_LATER(ah) &&
199f5547245SFelix Fietkau 		 aniState->ofdmNoiseImmunityLevel >= 8)
200f5547245SFelix Fietkau 		weak_sig = false;
201f5547245SFelix Fietkau 
2026241226fSFelix Fietkau 	if (aniState->ofdmWeakSigDetect != weak_sig)
2036241226fSFelix Fietkau 		ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION,
2046241226fSFelix Fietkau 				     weak_sig);
20555fee98aSSujith Manoharan 
206f5547245SFelix Fietkau 	if (!AR_SREV_9300_20_OR_LATER(ah))
207f5547245SFelix Fietkau 		return;
208f5547245SFelix Fietkau 
20955fee98aSSujith Manoharan 	if (aniState->ofdmNoiseImmunityLevel >= ATH9K_ANI_OFDM_DEF_LEVEL) {
21055fee98aSSujith Manoharan 		ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH;
21155fee98aSSujith Manoharan 		ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW_ABOVE_INI;
21255fee98aSSujith Manoharan 	} else {
21355fee98aSSujith Manoharan 		ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_BELOW_INI;
21455fee98aSSujith Manoharan 		ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW;
21555fee98aSSujith Manoharan 	}
216e36b27afSLuis R. Rodriguez }
217e36b27afSLuis R. Rodriguez 
ath9k_hw_ani_ofdm_err_trigger(struct ath_hw * ah)2188eb4980cSFelix Fietkau static void ath9k_hw_ani_ofdm_err_trigger(struct ath_hw *ah)
219e36b27afSLuis R. Rodriguez {
22066b533d5SMiaoqing Pan 	struct ar5416AniState *aniState = &ah->ani;
221e36b27afSLuis R. Rodriguez 
222e36b27afSLuis R. Rodriguez 	if (aniState->ofdmNoiseImmunityLevel < ATH9K_ANI_OFDM_MAX_LEVEL)
22373dc3eb8SFelix Fietkau 		ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel + 1, false);
224e36b27afSLuis R. Rodriguez }
225e36b27afSLuis R. Rodriguez 
226e36b27afSLuis R. Rodriguez /*
227e36b27afSLuis R. Rodriguez  * Set the ANI settings to match an CCK level.
228e36b27afSLuis R. Rodriguez  */
ath9k_hw_set_cck_nil(struct ath_hw * ah,u_int8_t immunityLevel,bool scan)22973dc3eb8SFelix Fietkau static void ath9k_hw_set_cck_nil(struct ath_hw *ah, u_int8_t immunityLevel,
23073dc3eb8SFelix Fietkau 				 bool scan)
231e36b27afSLuis R. Rodriguez {
232c24bd362SSujith Manoharan 	struct ar5416AniState *aniState = &ah->ani;
233e36b27afSLuis R. Rodriguez 	struct ath_common *common = ath9k_hw_common(ah);
234e36b27afSLuis R. Rodriguez 	const struct ani_ofdm_level_entry *entry_ofdm;
235e36b27afSLuis R. Rodriguez 	const struct ani_cck_level_entry *entry_cck;
236e36b27afSLuis R. Rodriguez 
237d2182b69SJoe Perches 	ath_dbg(common, ANI, "**** ccklevel %d=>%d, rssi=%d[lo=%d hi=%d]\n",
238e36b27afSLuis R. Rodriguez 		aniState->cckNoiseImmunityLevel, immunityLevel,
2399dbac67dSFelix Fietkau 		BEACON_RSSI(ah), ATH9K_ANI_RSSI_THR_LOW,
2409dbac67dSFelix Fietkau 		ATH9K_ANI_RSSI_THR_HIGH);
241e36b27afSLuis R. Rodriguez 
242ae9c25a1SFelix Fietkau 	if (AR_SREV_9100(ah) && immunityLevel < ATH9K_ANI_CCK_DEF_LEVEL)
243ae9c25a1SFelix Fietkau 		immunityLevel = ATH9K_ANI_CCK_DEF_LEVEL;
244ae9c25a1SFelix Fietkau 
2455330df7bSFelix Fietkau 	if (ah->opmode == NL80211_IFTYPE_STATION &&
2469dbac67dSFelix Fietkau 	    BEACON_RSSI(ah) <= ATH9K_ANI_RSSI_THR_LOW &&
247e36b27afSLuis R. Rodriguez 	    immunityLevel > ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI)
248e36b27afSLuis R. Rodriguez 		immunityLevel = ATH9K_ANI_CCK_MAX_LEVEL_LOW_RSSI;
249e36b27afSLuis R. Rodriguez 
25073dc3eb8SFelix Fietkau 	if (!scan)
2511e8f0a31SFelix Fietkau 		aniState->cckNoiseImmunityLevel = immunityLevel;
252e36b27afSLuis R. Rodriguez 
253e36b27afSLuis R. Rodriguez 	entry_ofdm = &ofdm_level_table[aniState->ofdmNoiseImmunityLevel];
254e36b27afSLuis R. Rodriguez 	entry_cck = &cck_level_table[aniState->cckNoiseImmunityLevel];
255e36b27afSLuis R. Rodriguez 
256e36b27afSLuis R. Rodriguez 	if (aniState->firstepLevel != entry_cck->fir_step_level &&
257e36b27afSLuis R. Rodriguez 	    entry_cck->fir_step_level >= entry_ofdm->fir_step_level)
258e36b27afSLuis R. Rodriguez 		ath9k_hw_ani_control(ah,
259e36b27afSLuis R. Rodriguez 				     ATH9K_ANI_FIRSTEP_LEVEL,
260e36b27afSLuis R. Rodriguez 				     entry_cck->fir_step_level);
261e36b27afSLuis R. Rodriguez 
262e36b27afSLuis R. Rodriguez 	/* Skip MRC CCK for pre AR9003 families */
263ede6a5e7SMiaoqing Pan 	if (!AR_SREV_9300_20_OR_LATER(ah) || AR_SREV_9485(ah) ||
264ede6a5e7SMiaoqing Pan 	    AR_SREV_9565(ah) || AR_SREV_9561(ah))
265e36b27afSLuis R. Rodriguez 		return;
266e36b27afSLuis R. Rodriguez 
26781b67fd6SRajkumar Manoharan 	if (aniState->mrcCCK != entry_cck->mrc_cck_on)
268e36b27afSLuis R. Rodriguez 		ath9k_hw_ani_control(ah,
269e36b27afSLuis R. Rodriguez 				     ATH9K_ANI_MRC_CCK,
270e36b27afSLuis R. Rodriguez 				     entry_cck->mrc_cck_on);
271e36b27afSLuis R. Rodriguez }
272e36b27afSLuis R. Rodriguez 
ath9k_hw_ani_cck_err_trigger(struct ath_hw * ah)2738eb4980cSFelix Fietkau static void ath9k_hw_ani_cck_err_trigger(struct ath_hw *ah)
274e36b27afSLuis R. Rodriguez {
27566b533d5SMiaoqing Pan 	struct ar5416AniState *aniState = &ah->ani;
276e36b27afSLuis R. Rodriguez 
277e36b27afSLuis R. Rodriguez 	if (aniState->cckNoiseImmunityLevel < ATH9K_ANI_CCK_MAX_LEVEL)
27873dc3eb8SFelix Fietkau 		ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel + 1,
27973dc3eb8SFelix Fietkau 				     false);
280e36b27afSLuis R. Rodriguez }
281e36b27afSLuis R. Rodriguez 
282e36b27afSLuis R. Rodriguez /*
283e36b27afSLuis R. Rodriguez  * only lower either OFDM or CCK errors per turn
284e36b27afSLuis R. Rodriguez  * we lower the other one next time
285e36b27afSLuis R. Rodriguez  */
ath9k_hw_ani_lower_immunity(struct ath_hw * ah)2868eb4980cSFelix Fietkau static void ath9k_hw_ani_lower_immunity(struct ath_hw *ah)
287e36b27afSLuis R. Rodriguez {
28866b533d5SMiaoqing Pan 	struct ar5416AniState *aniState = &ah->ani;
289e36b27afSLuis R. Rodriguez 
290e36b27afSLuis R. Rodriguez 	/* lower OFDM noise immunity */
291e36b27afSLuis R. Rodriguez 	if (aniState->ofdmNoiseImmunityLevel > 0 &&
292e36b27afSLuis R. Rodriguez 	    (aniState->ofdmsTurn || aniState->cckNoiseImmunityLevel == 0)) {
29373dc3eb8SFelix Fietkau 		ath9k_hw_set_ofdm_nil(ah, aniState->ofdmNoiseImmunityLevel - 1,
29473dc3eb8SFelix Fietkau 				      false);
295e36b27afSLuis R. Rodriguez 		return;
296e36b27afSLuis R. Rodriguez 	}
297e36b27afSLuis R. Rodriguez 
298e36b27afSLuis R. Rodriguez 	/* lower CCK noise immunity */
299e36b27afSLuis R. Rodriguez 	if (aniState->cckNoiseImmunityLevel > 0)
30073dc3eb8SFelix Fietkau 		ath9k_hw_set_cck_nil(ah, aniState->cckNoiseImmunityLevel - 1,
30173dc3eb8SFelix Fietkau 				     false);
302e36b27afSLuis R. Rodriguez }
303e36b27afSLuis R. Rodriguez 
304e36b27afSLuis R. Rodriguez /*
305e36b27afSLuis R. Rodriguez  * Restore the ANI parameters in the HAL and reset the statistics.
306e36b27afSLuis R. Rodriguez  * This routine should be called for every hardware reset and for
307e36b27afSLuis R. Rodriguez  * every channel change.
308e36b27afSLuis R. Rodriguez  */
ath9k_ani_reset(struct ath_hw * ah,bool is_scanning)3098eb4980cSFelix Fietkau void ath9k_ani_reset(struct ath_hw *ah, bool is_scanning)
310e36b27afSLuis R. Rodriguez {
311c24bd362SSujith Manoharan 	struct ar5416AniState *aniState = &ah->ani;
312e36b27afSLuis R. Rodriguez 	struct ath9k_channel *chan = ah->curchan;
313e36b27afSLuis R. Rodriguez 	struct ath_common *common = ath9k_hw_common(ah);
3141e8f0a31SFelix Fietkau 	int ofdm_nil, cck_nil;
315e36b27afSLuis R. Rodriguez 
31666b533d5SMiaoqing Pan 	if (!chan)
317e36b27afSLuis R. Rodriguez 		return;
318e36b27afSLuis R. Rodriguez 
319e36b27afSLuis R. Rodriguez 	BUG_ON(aniState == NULL);
320e36b27afSLuis R. Rodriguez 	ah->stats.ast_ani_reset++;
321e36b27afSLuis R. Rodriguez 
3221e8f0a31SFelix Fietkau 	ofdm_nil = max_t(int, ATH9K_ANI_OFDM_DEF_LEVEL,
3231e8f0a31SFelix Fietkau 			 aniState->ofdmNoiseImmunityLevel);
3241e8f0a31SFelix Fietkau 	cck_nil = max_t(int, ATH9K_ANI_CCK_DEF_LEVEL,
3251e8f0a31SFelix Fietkau 			 aniState->cckNoiseImmunityLevel);
3261e8f0a31SFelix Fietkau 
327e36b27afSLuis R. Rodriguez 	if (is_scanning ||
328e36b27afSLuis R. Rodriguez 	    (ah->opmode != NL80211_IFTYPE_STATION &&
329e36b27afSLuis R. Rodriguez 	     ah->opmode != NL80211_IFTYPE_ADHOC)) {
330e36b27afSLuis R. Rodriguez 		/*
331e36b27afSLuis R. Rodriguez 		 * If we're scanning or in AP mode, the defaults (ini)
332e36b27afSLuis R. Rodriguez 		 * should be in place. For an AP we assume the historical
333e36b27afSLuis R. Rodriguez 		 * levels for this channel are probably outdated so start
334e36b27afSLuis R. Rodriguez 		 * from defaults instead.
335e36b27afSLuis R. Rodriguez 		 */
336e36b27afSLuis R. Rodriguez 		if (aniState->ofdmNoiseImmunityLevel !=
337e36b27afSLuis R. Rodriguez 		    ATH9K_ANI_OFDM_DEF_LEVEL ||
338e36b27afSLuis R. Rodriguez 		    aniState->cckNoiseImmunityLevel !=
339e36b27afSLuis R. Rodriguez 		    ATH9K_ANI_CCK_DEF_LEVEL) {
340d2182b69SJoe Perches 			ath_dbg(common, ANI,
3418896934cSFelix Fietkau 				"Restore defaults: opmode %u chan %d Mhz is_scanning=%d ofdm:%d cck:%d\n",
342e36b27afSLuis R. Rodriguez 				ah->opmode,
343e36b27afSLuis R. Rodriguez 				chan->channel,
344e36b27afSLuis R. Rodriguez 				is_scanning,
345e36b27afSLuis R. Rodriguez 				aniState->ofdmNoiseImmunityLevel,
346e36b27afSLuis R. Rodriguez 				aniState->cckNoiseImmunityLevel);
347e36b27afSLuis R. Rodriguez 
3481e8f0a31SFelix Fietkau 			ofdm_nil = ATH9K_ANI_OFDM_DEF_LEVEL;
3491e8f0a31SFelix Fietkau 			cck_nil = ATH9K_ANI_CCK_DEF_LEVEL;
350e36b27afSLuis R. Rodriguez 		}
351e36b27afSLuis R. Rodriguez 	} else {
352e36b27afSLuis R. Rodriguez 		/*
353e36b27afSLuis R. Rodriguez 		 * restore historical levels for this channel
354e36b27afSLuis R. Rodriguez 		 */
355d2182b69SJoe Perches 		ath_dbg(common, ANI,
3568896934cSFelix Fietkau 			"Restore history: opmode %u chan %d Mhz is_scanning=%d ofdm:%d cck:%d\n",
357e36b27afSLuis R. Rodriguez 			ah->opmode,
358e36b27afSLuis R. Rodriguez 			chan->channel,
359e36b27afSLuis R. Rodriguez 			is_scanning,
360e36b27afSLuis R. Rodriguez 			aniState->ofdmNoiseImmunityLevel,
361e36b27afSLuis R. Rodriguez 			aniState->cckNoiseImmunityLevel);
362e36b27afSLuis R. Rodriguez 	}
36373dc3eb8SFelix Fietkau 	ath9k_hw_set_ofdm_nil(ah, ofdm_nil, is_scanning);
36473dc3eb8SFelix Fietkau 	ath9k_hw_set_cck_nil(ah, cck_nil, is_scanning);
365e36b27afSLuis R. Rodriguez 
366093115b7SFelix Fietkau 	ath9k_ani_restart(ah);
367203c4805SLuis R. Rodriguez }
368203c4805SLuis R. Rodriguez 
ath9k_hw_ani_read_counters(struct ath_hw * ah)369e49f9137SFelix Fietkau static bool ath9k_hw_ani_read_counters(struct ath_hw *ah)
370203c4805SLuis R. Rodriguez {
371c46917bbSLuis R. Rodriguez 	struct ath_common *common = ath9k_hw_common(ah);
372c24bd362SSujith Manoharan 	struct ar5416AniState *aniState = &ah->ani;
373bfc472bbSFelix Fietkau 	u32 phyCnt1, phyCnt2;
374bfc472bbSFelix Fietkau 	int32_t listenTime;
375203c4805SLuis R. Rodriguez 
376b5bfc568SFelix Fietkau 	ath_hw_cycle_counters_update(common);
377b5bfc568SFelix Fietkau 	listenTime = ath_hw_get_listen_time(common);
378b5bfc568SFelix Fietkau 
379e49f9137SFelix Fietkau 	if (listenTime <= 0) {
380107021c4SMohammed Shafi Shajakhan 		ah->stats.ast_ani_lneg_or_lzero++;
381093115b7SFelix Fietkau 		ath9k_ani_restart(ah);
382e49f9137SFelix Fietkau 		return false;
383203c4805SLuis R. Rodriguez 	}
384203c4805SLuis R. Rodriguez 
385203c4805SLuis R. Rodriguez 	aniState->listenTime += listenTime;
386203c4805SLuis R. Rodriguez 
387203c4805SLuis R. Rodriguez 	ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
388203c4805SLuis R. Rodriguez 
389203c4805SLuis R. Rodriguez 	phyCnt1 = REG_READ(ah, AR_PHY_ERR_1);
390203c4805SLuis R. Rodriguez 	phyCnt2 = REG_READ(ah, AR_PHY_ERR_2);
391203c4805SLuis R. Rodriguez 
392465dce62SFelix Fietkau 	ah->stats.ast_ani_ofdmerrs += phyCnt1 - aniState->ofdmPhyErrCount;
393465dce62SFelix Fietkau 	aniState->ofdmPhyErrCount = phyCnt1;
394203c4805SLuis R. Rodriguez 
395465dce62SFelix Fietkau 	ah->stats.ast_ani_cckerrs += phyCnt2 - aniState->cckPhyErrCount;
396465dce62SFelix Fietkau 	aniState->cckPhyErrCount = phyCnt2;
397465dce62SFelix Fietkau 
398e49f9137SFelix Fietkau 	return true;
399bfc472bbSFelix Fietkau }
400bfc472bbSFelix Fietkau 
ath9k_hw_ani_monitor(struct ath_hw * ah,struct ath9k_channel * chan)40195792178SFelix Fietkau void ath9k_hw_ani_monitor(struct ath_hw *ah, struct ath9k_channel *chan)
402e36b27afSLuis R. Rodriguez {
40366b533d5SMiaoqing Pan 	struct ar5416AniState *aniState = &ah->ani;
404e36b27afSLuis R. Rodriguez 	struct ath_common *common = ath9k_hw_common(ah);
405e36b27afSLuis R. Rodriguez 	u32 ofdmPhyErrRate, cckPhyErrRate;
406e36b27afSLuis R. Rodriguez 
407e49f9137SFelix Fietkau 	if (!ath9k_hw_ani_read_counters(ah))
408e49f9137SFelix Fietkau 		return;
409e36b27afSLuis R. Rodriguez 
410e36b27afSLuis R. Rodriguez 	ofdmPhyErrRate = aniState->ofdmPhyErrCount * 1000 /
411e36b27afSLuis R. Rodriguez 			 aniState->listenTime;
412e36b27afSLuis R. Rodriguez 	cckPhyErrRate =  aniState->cckPhyErrCount * 1000 /
413e36b27afSLuis R. Rodriguez 			 aniState->listenTime;
414e36b27afSLuis R. Rodriguez 
415d2182b69SJoe Perches 	ath_dbg(common, ANI,
416226afe68SJoe Perches 		"listenTime=%d OFDM:%d errs=%d/s CCK:%d errs=%d/s ofdm_turn=%d\n",
417bfc472bbSFelix Fietkau 		aniState->listenTime,
418bfc472bbSFelix Fietkau 		aniState->ofdmNoiseImmunityLevel,
419e36b27afSLuis R. Rodriguez 		ofdmPhyErrRate, aniState->cckNoiseImmunityLevel,
420e36b27afSLuis R. Rodriguez 		cckPhyErrRate, aniState->ofdmsTurn);
421e36b27afSLuis R. Rodriguez 
42255fee98aSSujith Manoharan 	if (aniState->listenTime > ah->aniperiod) {
42355fee98aSSujith Manoharan 		if (cckPhyErrRate < ah->config.cck_trig_low &&
42455fee98aSSujith Manoharan 		    ofdmPhyErrRate < ah->config.ofdm_trig_low) {
425e36b27afSLuis R. Rodriguez 			ath9k_hw_ani_lower_immunity(ah);
426e36b27afSLuis R. Rodriguez 			aniState->ofdmsTurn = !aniState->ofdmsTurn;
42755fee98aSSujith Manoharan 		} else if (ofdmPhyErrRate > ah->config.ofdm_trig_high) {
4288eb4980cSFelix Fietkau 			ath9k_hw_ani_ofdm_err_trigger(ah);
429e36b27afSLuis R. Rodriguez 			aniState->ofdmsTurn = false;
430093115b7SFelix Fietkau 		} else if (cckPhyErrRate > ah->config.cck_trig_high) {
4318eb4980cSFelix Fietkau 			ath9k_hw_ani_cck_err_trigger(ah);
432e36b27afSLuis R. Rodriguez 			aniState->ofdmsTurn = true;
433c1cab1dfSMiaoqing Pan 		} else
434c1cab1dfSMiaoqing Pan 			return;
435c1cab1dfSMiaoqing Pan 
43654da20d8SRajkumar Manoharan 		ath9k_ani_restart(ah);
437203c4805SLuis R. Rodriguez 	}
438203c4805SLuis R. Rodriguez }
43995792178SFelix Fietkau EXPORT_SYMBOL(ath9k_hw_ani_monitor);
440203c4805SLuis R. Rodriguez 
ath9k_enable_mib_counters(struct ath_hw * ah)441203c4805SLuis R. Rodriguez void ath9k_enable_mib_counters(struct ath_hw *ah)
442203c4805SLuis R. Rodriguez {
443c46917bbSLuis R. Rodriguez 	struct ath_common *common = ath9k_hw_common(ah);
444c46917bbSLuis R. Rodriguez 
445d2182b69SJoe Perches 	ath_dbg(common, ANI, "Enable MIB counters\n");
446203c4805SLuis R. Rodriguez 
447203c4805SLuis R. Rodriguez 	ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
448203c4805SLuis R. Rodriguez 
4497d0d0df0SSujith 	ENABLE_REGWRITE_BUFFER(ah);
4507d0d0df0SSujith 
451203c4805SLuis R. Rodriguez 	REG_WRITE(ah, AR_FILT_OFDM, 0);
452203c4805SLuis R. Rodriguez 	REG_WRITE(ah, AR_FILT_CCK, 0);
453203c4805SLuis R. Rodriguez 	REG_WRITE(ah, AR_MIBC,
454203c4805SLuis R. Rodriguez 		  ~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS)
455203c4805SLuis R. Rodriguez 		  & 0x0f);
456203c4805SLuis R. Rodriguez 	REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
457203c4805SLuis R. Rodriguez 	REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
4587d0d0df0SSujith 
4597d0d0df0SSujith 	REGWRITE_BUFFER_FLUSH(ah);
460203c4805SLuis R. Rodriguez }
461203c4805SLuis R. Rodriguez 
462203c4805SLuis R. Rodriguez /* Freeze the MIB counters, get the stats and then clear them */
ath9k_hw_disable_mib_counters(struct ath_hw * ah)463203c4805SLuis R. Rodriguez void ath9k_hw_disable_mib_counters(struct ath_hw *ah)
464203c4805SLuis R. Rodriguez {
465c46917bbSLuis R. Rodriguez 	struct ath_common *common = ath9k_hw_common(ah);
466c46917bbSLuis R. Rodriguez 
467d2182b69SJoe Perches 	ath_dbg(common, ANI, "Disable MIB counters\n");
468c46917bbSLuis R. Rodriguez 
469203c4805SLuis R. Rodriguez 	REG_WRITE(ah, AR_MIBC, AR_MIBC_FMC);
470203c4805SLuis R. Rodriguez 	ath9k_hw_update_mibstats(ah, &ah->ah_mibStats);
471203c4805SLuis R. Rodriguez 	REG_WRITE(ah, AR_MIBC, AR_MIBC_CMC);
472203c4805SLuis R. Rodriguez 	REG_WRITE(ah, AR_FILT_OFDM, 0);
473203c4805SLuis R. Rodriguez 	REG_WRITE(ah, AR_FILT_CCK, 0);
474203c4805SLuis R. Rodriguez }
47521d5130bSSujith EXPORT_SYMBOL(ath9k_hw_disable_mib_counters);
476203c4805SLuis R. Rodriguez 
ath9k_hw_ani_init(struct ath_hw * ah)477f637cfd6SLuis R. Rodriguez void ath9k_hw_ani_init(struct ath_hw *ah)
478203c4805SLuis R. Rodriguez {
479c46917bbSLuis R. Rodriguez 	struct ath_common *common = ath9k_hw_common(ah);
480c24bd362SSujith Manoharan 	struct ar5416AniState *ani = &ah->ani;
481203c4805SLuis R. Rodriguez 
482d2182b69SJoe Perches 	ath_dbg(common, ANI, "Initialize ANI\n");
483203c4805SLuis R. Rodriguez 
484f5547245SFelix Fietkau 	if (AR_SREV_9300_20_OR_LATER(ah)) {
485465dce62SFelix Fietkau 		ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH;
486465dce62SFelix Fietkau 		ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW;
487465dce62SFelix Fietkau 		ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH;
488465dce62SFelix Fietkau 		ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW;
489f5547245SFelix Fietkau 	} else {
490f5547245SFelix Fietkau 		ah->config.ofdm_trig_high = ATH9K_ANI_OFDM_TRIG_HIGH_OLD;
491f5547245SFelix Fietkau 		ah->config.ofdm_trig_low = ATH9K_ANI_OFDM_TRIG_LOW_OLD;
492f5547245SFelix Fietkau 		ah->config.cck_trig_high = ATH9K_ANI_CCK_TRIG_HIGH_OLD;
493f5547245SFelix Fietkau 		ah->config.cck_trig_low = ATH9K_ANI_CCK_TRIG_LOW_OLD;
494f5547245SFelix Fietkau 	}
495093115b7SFelix Fietkau 
496465dce62SFelix Fietkau 	ani->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL;
497465dce62SFelix Fietkau 	ani->firstepLevel = ATH9K_ANI_FIRSTEP_LVL;
49881b67fd6SRajkumar Manoharan 	ani->mrcCCK = AR_SREV_9300_20_OR_LATER(ah) ? true : false;
499093115b7SFelix Fietkau 	ani->ofdmsTurn = true;
5004f4395c6SSujith Manoharan 	ani->ofdmWeakSigDetect = true;
501093115b7SFelix Fietkau 	ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL;
502f36369afSRajkumar Manoharan 	ani->ofdmNoiseImmunityLevel = ATH9K_ANI_OFDM_DEF_LEVEL;
503e36b27afSLuis R. Rodriguez 
504e36b27afSLuis R. Rodriguez 	/*
505e36b27afSLuis R. Rodriguez 	 * since we expect some ongoing maintenance on the tables, let's sanity
506e36b27afSLuis R. Rodriguez 	 * check here default level should not modify INI setting.
507e36b27afSLuis R. Rodriguez 	 */
508465dce62SFelix Fietkau 	ah->aniperiod = ATH9K_ANI_PERIOD;
509465dce62SFelix Fietkau 	ah->config.ani_poll_interval = ATH9K_ANI_POLLINTERVAL;
5101aa8e847SSujith 
511093115b7SFelix Fietkau 	ath9k_ani_restart(ah);
512093115b7SFelix Fietkau 	ath9k_enable_mib_counters(ah);
513203c4805SLuis R. Rodriguez }
514