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