12111ac0dSBruno Randolf /*
22111ac0dSBruno Randolf * Copyright (C) 2010 Bruno Randolf <br1@einfach.org>
32111ac0dSBruno Randolf *
42111ac0dSBruno Randolf * Permission to use, copy, modify, and/or distribute this software for any
52111ac0dSBruno Randolf * purpose with or without fee is hereby granted, provided that the above
62111ac0dSBruno Randolf * copyright notice and this permission notice appear in all copies.
72111ac0dSBruno Randolf *
82111ac0dSBruno Randolf * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
92111ac0dSBruno Randolf * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
102111ac0dSBruno Randolf * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
112111ac0dSBruno Randolf * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
122111ac0dSBruno Randolf * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
132111ac0dSBruno Randolf * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
142111ac0dSBruno Randolf * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
152111ac0dSBruno Randolf */
162111ac0dSBruno Randolf
17516304b0SJoe Perches #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
18516304b0SJoe Perches
192111ac0dSBruno Randolf #include "ath5k.h"
202111ac0dSBruno Randolf #include "reg.h"
212111ac0dSBruno Randolf #include "debug.h"
222111ac0dSBruno Randolf #include "ani.h"
232111ac0dSBruno Randolf
242111ac0dSBruno Randolf /**
252111ac0dSBruno Randolf * DOC: Basic ANI Operation
262111ac0dSBruno Randolf *
272111ac0dSBruno Randolf * Adaptive Noise Immunity (ANI) controls five noise immunity parameters
282111ac0dSBruno Randolf * depending on the amount of interference in the environment, increasing
292111ac0dSBruno Randolf * or reducing sensitivity as necessary.
302111ac0dSBruno Randolf *
312111ac0dSBruno Randolf * The parameters are:
32c47faa36SNick Kossifidis *
332111ac0dSBruno Randolf * - "noise immunity"
34c47faa36SNick Kossifidis *
352111ac0dSBruno Randolf * - "spur immunity"
36c47faa36SNick Kossifidis *
372111ac0dSBruno Randolf * - "firstep level"
38c47faa36SNick Kossifidis *
392111ac0dSBruno Randolf * - "OFDM weak signal detection"
40c47faa36SNick Kossifidis *
412111ac0dSBruno Randolf * - "CCK weak signal detection"
422111ac0dSBruno Randolf *
432111ac0dSBruno Randolf * Basically we look at the amount of ODFM and CCK timing errors we get and then
442111ac0dSBruno Randolf * raise or lower immunity accordingly by setting one or more of these
452111ac0dSBruno Randolf * parameters.
46c47faa36SNick Kossifidis *
472111ac0dSBruno Randolf * Newer chipsets have PHY error counters in hardware which will generate a MIB
482111ac0dSBruno Randolf * interrupt when they overflow. Older hardware has too enable PHY error frames
492111ac0dSBruno Randolf * by setting a RX flag and then count every single PHY error. When a specified
502111ac0dSBruno Randolf * threshold of errors has been reached we will raise immunity.
512111ac0dSBruno Randolf * Also we regularly check the amount of errors and lower or raise immunity as
522111ac0dSBruno Randolf * necessary.
532111ac0dSBruno Randolf */
542111ac0dSBruno Randolf
552111ac0dSBruno Randolf
56c47faa36SNick Kossifidis /***********************\
57c47faa36SNick Kossifidis * ANI parameter control *
58c47faa36SNick Kossifidis \***********************/
592111ac0dSBruno Randolf
602111ac0dSBruno Randolf /**
612111ac0dSBruno Randolf * ath5k_ani_set_noise_immunity_level() - Set noise immunity level
62c47faa36SNick Kossifidis * @ah: The &struct ath5k_hw
632111ac0dSBruno Randolf * @level: level between 0 and @ATH5K_ANI_MAX_NOISE_IMM_LVL
642111ac0dSBruno Randolf */
652111ac0dSBruno Randolf void
ath5k_ani_set_noise_immunity_level(struct ath5k_hw * ah,int level)662111ac0dSBruno Randolf ath5k_ani_set_noise_immunity_level(struct ath5k_hw *ah, int level)
672111ac0dSBruno Randolf {
682111ac0dSBruno Randolf /* TODO:
692111ac0dSBruno Randolf * ANI documents suggest the following five levels to use, but the HAL
709320b5c4SNick Kossifidis * and ath9k use only the last two levels, making this
712111ac0dSBruno Randolf * essentially an on/off option. There *may* be a reason for this (???),
722111ac0dSBruno Randolf * so i stick with the HAL version for now...
732111ac0dSBruno Randolf */
742111ac0dSBruno Randolf #if 0
758b22523bSJoe Perches static const s8 lo[] = { -52, -56, -60, -64, -70 };
769320b5c4SNick Kossifidis static const s8 hi[] = { -18, -18, -16, -14, -12 };
778b22523bSJoe Perches static const s8 sz[] = { -34, -41, -48, -55, -62 };
788b22523bSJoe Perches static const s8 fr[] = { -70, -72, -75, -78, -80 };
792111ac0dSBruno Randolf #else
808b22523bSJoe Perches static const s8 lo[] = { -64, -70 };
818b22523bSJoe Perches static const s8 hi[] = { -14, -12 };
829320b5c4SNick Kossifidis static const s8 sz[] = { -55, -62 };
838b22523bSJoe Perches static const s8 fr[] = { -78, -80 };
842111ac0dSBruno Randolf #endif
8505e8594dSDan Carpenter if (level < 0 || level >= ARRAY_SIZE(sz)) {
86e0d687bdSPavel Roskin ATH5K_ERR(ah, "noise immunity level %d out of range",
874f424867SBruno Randolf level);
882111ac0dSBruno Randolf return;
892111ac0dSBruno Randolf }
902111ac0dSBruno Randolf
912111ac0dSBruno Randolf AR5K_REG_WRITE_BITS(ah, AR5K_PHY_DESIRED_SIZE,
922111ac0dSBruno Randolf AR5K_PHY_DESIRED_SIZE_TOT, sz[level]);
932111ac0dSBruno Randolf AR5K_REG_WRITE_BITS(ah, AR5K_PHY_AGCCOARSE,
942111ac0dSBruno Randolf AR5K_PHY_AGCCOARSE_LO, lo[level]);
952111ac0dSBruno Randolf AR5K_REG_WRITE_BITS(ah, AR5K_PHY_AGCCOARSE,
962111ac0dSBruno Randolf AR5K_PHY_AGCCOARSE_HI, hi[level]);
972111ac0dSBruno Randolf AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SIG,
982111ac0dSBruno Randolf AR5K_PHY_SIG_FIRPWR, fr[level]);
992111ac0dSBruno Randolf
100e0d687bdSPavel Roskin ah->ani_state.noise_imm_level = level;
101e0d687bdSPavel Roskin ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level);
1022111ac0dSBruno Randolf }
1032111ac0dSBruno Randolf
1042111ac0dSBruno Randolf /**
1052111ac0dSBruno Randolf * ath5k_ani_set_spur_immunity_level() - Set spur immunity level
106c47faa36SNick Kossifidis * @ah: The &struct ath5k_hw
1072111ac0dSBruno Randolf * @level: level between 0 and @max_spur_level (the maximum level is dependent
1082111ac0dSBruno Randolf * on the chip revision).
1092111ac0dSBruno Randolf */
1102111ac0dSBruno Randolf void
ath5k_ani_set_spur_immunity_level(struct ath5k_hw * ah,int level)1112111ac0dSBruno Randolf ath5k_ani_set_spur_immunity_level(struct ath5k_hw *ah, int level)
1122111ac0dSBruno Randolf {
1138b22523bSJoe Perches static const int val[] = { 2, 4, 6, 8, 10, 12, 14, 16 };
1142111ac0dSBruno Randolf
11505e8594dSDan Carpenter if (level < 0 || level >= ARRAY_SIZE(val) ||
116e0d687bdSPavel Roskin level > ah->ani_state.max_spur_level) {
117e0d687bdSPavel Roskin ATH5K_ERR(ah, "spur immunity level %d out of range",
1184f424867SBruno Randolf level);
1192111ac0dSBruno Randolf return;
1202111ac0dSBruno Randolf }
1212111ac0dSBruno Randolf
1222111ac0dSBruno Randolf AR5K_REG_WRITE_BITS(ah, AR5K_PHY_OFDM_SELFCORR,
1232111ac0dSBruno Randolf AR5K_PHY_OFDM_SELFCORR_CYPWR_THR1, val[level]);
1242111ac0dSBruno Randolf
125e0d687bdSPavel Roskin ah->ani_state.spur_level = level;
126e0d687bdSPavel Roskin ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level);
1272111ac0dSBruno Randolf }
1282111ac0dSBruno Randolf
1292111ac0dSBruno Randolf /**
1302111ac0dSBruno Randolf * ath5k_ani_set_firstep_level() - Set "firstep" level
131c47faa36SNick Kossifidis * @ah: The &struct ath5k_hw
1322111ac0dSBruno Randolf * @level: level between 0 and @ATH5K_ANI_MAX_FIRSTEP_LVL
1332111ac0dSBruno Randolf */
1342111ac0dSBruno Randolf void
ath5k_ani_set_firstep_level(struct ath5k_hw * ah,int level)1352111ac0dSBruno Randolf ath5k_ani_set_firstep_level(struct ath5k_hw *ah, int level)
1362111ac0dSBruno Randolf {
1378b22523bSJoe Perches static const int val[] = { 0, 4, 8 };
1382111ac0dSBruno Randolf
13905e8594dSDan Carpenter if (level < 0 || level >= ARRAY_SIZE(val)) {
140e0d687bdSPavel Roskin ATH5K_ERR(ah, "firstep level %d out of range", level);
1412111ac0dSBruno Randolf return;
1422111ac0dSBruno Randolf }
1432111ac0dSBruno Randolf
1442111ac0dSBruno Randolf AR5K_REG_WRITE_BITS(ah, AR5K_PHY_SIG,
1452111ac0dSBruno Randolf AR5K_PHY_SIG_FIRSTEP, val[level]);
1462111ac0dSBruno Randolf
147e0d687bdSPavel Roskin ah->ani_state.firstep_level = level;
148e0d687bdSPavel Roskin ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "new level %d", level);
1492111ac0dSBruno Randolf }
1502111ac0dSBruno Randolf
1512111ac0dSBruno Randolf /**
152c47faa36SNick Kossifidis * ath5k_ani_set_ofdm_weak_signal_detection() - Set OFDM weak signal detection
153c47faa36SNick Kossifidis * @ah: The &struct ath5k_hw
1542111ac0dSBruno Randolf * @on: turn on or off
1552111ac0dSBruno Randolf */
1562111ac0dSBruno Randolf void
ath5k_ani_set_ofdm_weak_signal_detection(struct ath5k_hw * ah,bool on)1572111ac0dSBruno Randolf ath5k_ani_set_ofdm_weak_signal_detection(struct ath5k_hw *ah, bool on)
1582111ac0dSBruno Randolf {
1598b22523bSJoe Perches static const int m1l[] = { 127, 50 };
1608b22523bSJoe Perches static const int m2l[] = { 127, 40 };
1618b22523bSJoe Perches static const int m1[] = { 127, 0x4d };
1628b22523bSJoe Perches static const int m2[] = { 127, 0x40 };
1638b22523bSJoe Perches static const int m2cnt[] = { 31, 16 };
1648b22523bSJoe Perches static const int m2lcnt[] = { 63, 48 };
1652111ac0dSBruno Randolf
1662111ac0dSBruno Randolf AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR,
1672111ac0dSBruno Randolf AR5K_PHY_WEAK_OFDM_LOW_THR_M1, m1l[on]);
1682111ac0dSBruno Randolf AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR,
1692111ac0dSBruno Randolf AR5K_PHY_WEAK_OFDM_LOW_THR_M2, m2l[on]);
1702111ac0dSBruno Randolf AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR,
1712111ac0dSBruno Randolf AR5K_PHY_WEAK_OFDM_HIGH_THR_M1, m1[on]);
1722111ac0dSBruno Randolf AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR,
1732111ac0dSBruno Randolf AR5K_PHY_WEAK_OFDM_HIGH_THR_M2, m2[on]);
1742111ac0dSBruno Randolf AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_HIGH_THR,
1752111ac0dSBruno Randolf AR5K_PHY_WEAK_OFDM_HIGH_THR_M2_COUNT, m2cnt[on]);
1762111ac0dSBruno Randolf AR5K_REG_WRITE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR,
1772111ac0dSBruno Randolf AR5K_PHY_WEAK_OFDM_LOW_THR_M2_COUNT, m2lcnt[on]);
1782111ac0dSBruno Randolf
1792111ac0dSBruno Randolf if (on)
1802111ac0dSBruno Randolf AR5K_REG_ENABLE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR,
1812111ac0dSBruno Randolf AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN);
1822111ac0dSBruno Randolf else
1832111ac0dSBruno Randolf AR5K_REG_DISABLE_BITS(ah, AR5K_PHY_WEAK_OFDM_LOW_THR,
1842111ac0dSBruno Randolf AR5K_PHY_WEAK_OFDM_LOW_THR_SELFCOR_EN);
1852111ac0dSBruno Randolf
186e0d687bdSPavel Roskin ah->ani_state.ofdm_weak_sig = on;
187e0d687bdSPavel Roskin ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "turned %s",
1882111ac0dSBruno Randolf on ? "on" : "off");
1892111ac0dSBruno Randolf }
1902111ac0dSBruno Randolf
1912111ac0dSBruno Randolf /**
192c47faa36SNick Kossifidis * ath5k_ani_set_cck_weak_signal_detection() - Set CCK weak signal detection
193c47faa36SNick Kossifidis * @ah: The &struct ath5k_hw
1942111ac0dSBruno Randolf * @on: turn on or off
1952111ac0dSBruno Randolf */
1962111ac0dSBruno Randolf void
ath5k_ani_set_cck_weak_signal_detection(struct ath5k_hw * ah,bool on)1972111ac0dSBruno Randolf ath5k_ani_set_cck_weak_signal_detection(struct ath5k_hw *ah, bool on)
1982111ac0dSBruno Randolf {
1998b22523bSJoe Perches static const int val[] = { 8, 6 };
2002111ac0dSBruno Randolf AR5K_REG_WRITE_BITS(ah, AR5K_PHY_CCK_CROSSCORR,
2012111ac0dSBruno Randolf AR5K_PHY_CCK_CROSSCORR_WEAK_SIG_THR, val[on]);
202e0d687bdSPavel Roskin ah->ani_state.cck_weak_sig = on;
203e0d687bdSPavel Roskin ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "turned %s",
2042111ac0dSBruno Randolf on ? "on" : "off");
2052111ac0dSBruno Randolf }
2062111ac0dSBruno Randolf
2072111ac0dSBruno Randolf
208c47faa36SNick Kossifidis /***************\
209c47faa36SNick Kossifidis * ANI algorithm *
210c47faa36SNick Kossifidis \***************/
2112111ac0dSBruno Randolf
2122111ac0dSBruno Randolf /**
2132111ac0dSBruno Randolf * ath5k_ani_raise_immunity() - Increase noise immunity
214c47faa36SNick Kossifidis * @ah: The &struct ath5k_hw
215c47faa36SNick Kossifidis * @as: The &struct ath5k_ani_state
2162111ac0dSBruno Randolf * @ofdm_trigger: If this is true we are called because of too many OFDM errors,
2172111ac0dSBruno Randolf * the algorithm will tune more parameters then.
2182111ac0dSBruno Randolf *
2192111ac0dSBruno Randolf * Try to raise noise immunity (=decrease sensitivity) in several steps
2202111ac0dSBruno Randolf * depending on the average RSSI of the beacons we received.
2212111ac0dSBruno Randolf */
2222111ac0dSBruno Randolf static void
ath5k_ani_raise_immunity(struct ath5k_hw * ah,struct ath5k_ani_state * as,bool ofdm_trigger)2232111ac0dSBruno Randolf ath5k_ani_raise_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as,
2242111ac0dSBruno Randolf bool ofdm_trigger)
2252111ac0dSBruno Randolf {
22646f26ddfSJohannes Berg int rssi = ewma_beacon_rssi_read(&ah->ah_beacon_rssi_avg);
2272111ac0dSBruno Randolf
228e0d687bdSPavel Roskin ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "raise immunity (%s)",
2292111ac0dSBruno Randolf ofdm_trigger ? "ODFM" : "CCK");
2302111ac0dSBruno Randolf
2312111ac0dSBruno Randolf /* first: raise noise immunity */
2322111ac0dSBruno Randolf if (as->noise_imm_level < ATH5K_ANI_MAX_NOISE_IMM_LVL) {
2332111ac0dSBruno Randolf ath5k_ani_set_noise_immunity_level(ah, as->noise_imm_level + 1);
2342111ac0dSBruno Randolf return;
2352111ac0dSBruno Randolf }
2362111ac0dSBruno Randolf
2372111ac0dSBruno Randolf /* only OFDM: raise spur immunity level */
2382111ac0dSBruno Randolf if (ofdm_trigger &&
239e0d687bdSPavel Roskin as->spur_level < ah->ani_state.max_spur_level) {
2402111ac0dSBruno Randolf ath5k_ani_set_spur_immunity_level(ah, as->spur_level + 1);
2412111ac0dSBruno Randolf return;
2422111ac0dSBruno Randolf }
2432111ac0dSBruno Randolf
2442111ac0dSBruno Randolf /* AP mode */
245e0d687bdSPavel Roskin if (ah->opmode == NL80211_IFTYPE_AP) {
2462111ac0dSBruno Randolf if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL)
2472111ac0dSBruno Randolf ath5k_ani_set_firstep_level(ah, as->firstep_level + 1);
2482111ac0dSBruno Randolf return;
2492111ac0dSBruno Randolf }
2502111ac0dSBruno Randolf
2512111ac0dSBruno Randolf /* STA and IBSS mode */
2522111ac0dSBruno Randolf
2532111ac0dSBruno Randolf /* TODO: for IBSS mode it would be better to keep a beacon RSSI average
2542111ac0dSBruno Randolf * per each neighbour node and use the minimum of these, to make sure we
2552111ac0dSBruno Randolf * don't shut out a remote node by raising immunity too high. */
2562111ac0dSBruno Randolf
2572111ac0dSBruno Randolf if (rssi > ATH5K_ANI_RSSI_THR_HIGH) {
258e0d687bdSPavel Roskin ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
2592111ac0dSBruno Randolf "beacon RSSI high");
2602111ac0dSBruno Randolf /* only OFDM: beacon RSSI is high, we can disable ODFM weak
2612111ac0dSBruno Randolf * signal detection */
26223677ce3SJoe Perches if (ofdm_trigger && as->ofdm_weak_sig) {
2632111ac0dSBruno Randolf ath5k_ani_set_ofdm_weak_signal_detection(ah, false);
2642111ac0dSBruno Randolf ath5k_ani_set_spur_immunity_level(ah, 0);
2652111ac0dSBruno Randolf return;
2662111ac0dSBruno Randolf }
2672111ac0dSBruno Randolf /* as a last resort or CCK: raise firstep level */
2682111ac0dSBruno Randolf if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL) {
2692111ac0dSBruno Randolf ath5k_ani_set_firstep_level(ah, as->firstep_level + 1);
2702111ac0dSBruno Randolf return;
2712111ac0dSBruno Randolf }
2722111ac0dSBruno Randolf } else if (rssi > ATH5K_ANI_RSSI_THR_LOW) {
2732111ac0dSBruno Randolf /* beacon RSSI in mid range, we need OFDM weak signal detect,
2742111ac0dSBruno Randolf * but can raise firstep level */
275e0d687bdSPavel Roskin ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
2762111ac0dSBruno Randolf "beacon RSSI mid");
27723677ce3SJoe Perches if (ofdm_trigger && !as->ofdm_weak_sig)
2782111ac0dSBruno Randolf ath5k_ani_set_ofdm_weak_signal_detection(ah, true);
2792111ac0dSBruno Randolf if (as->firstep_level < ATH5K_ANI_MAX_FIRSTEP_LVL)
2802111ac0dSBruno Randolf ath5k_ani_set_firstep_level(ah, as->firstep_level + 1);
2812111ac0dSBruno Randolf return;
28257fbcce3SJohannes Berg } else if (ah->ah_current_channel->band == NL80211_BAND_2GHZ) {
2832111ac0dSBruno Randolf /* beacon RSSI is low. in B/G mode turn of OFDM weak signal
2842111ac0dSBruno Randolf * detect and zero firstep level to maximize CCK sensitivity */
285e0d687bdSPavel Roskin ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
2862111ac0dSBruno Randolf "beacon RSSI low, 2GHz");
28723677ce3SJoe Perches if (ofdm_trigger && as->ofdm_weak_sig)
2882111ac0dSBruno Randolf ath5k_ani_set_ofdm_weak_signal_detection(ah, false);
2892111ac0dSBruno Randolf if (as->firstep_level > 0)
2902111ac0dSBruno Randolf ath5k_ani_set_firstep_level(ah, 0);
2912111ac0dSBruno Randolf return;
2922111ac0dSBruno Randolf }
2932111ac0dSBruno Randolf
2942111ac0dSBruno Randolf /* TODO: why not?:
2952111ac0dSBruno Randolf if (as->cck_weak_sig == true) {
2962111ac0dSBruno Randolf ath5k_ani_set_cck_weak_signal_detection(ah, false);
2972111ac0dSBruno Randolf }
2982111ac0dSBruno Randolf */
2992111ac0dSBruno Randolf }
3002111ac0dSBruno Randolf
3012111ac0dSBruno Randolf /**
3022111ac0dSBruno Randolf * ath5k_ani_lower_immunity() - Decrease noise immunity
303c47faa36SNick Kossifidis * @ah: The &struct ath5k_hw
304c47faa36SNick Kossifidis * @as: The &struct ath5k_ani_state
3052111ac0dSBruno Randolf *
3062111ac0dSBruno Randolf * Try to lower noise immunity (=increase sensitivity) in several steps
3072111ac0dSBruno Randolf * depending on the average RSSI of the beacons we received.
3082111ac0dSBruno Randolf */
3092111ac0dSBruno Randolf static void
ath5k_ani_lower_immunity(struct ath5k_hw * ah,struct ath5k_ani_state * as)3102111ac0dSBruno Randolf ath5k_ani_lower_immunity(struct ath5k_hw *ah, struct ath5k_ani_state *as)
3112111ac0dSBruno Randolf {
31246f26ddfSJohannes Berg int rssi = ewma_beacon_rssi_read(&ah->ah_beacon_rssi_avg);
3132111ac0dSBruno Randolf
314e0d687bdSPavel Roskin ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "lower immunity");
3152111ac0dSBruno Randolf
316e0d687bdSPavel Roskin if (ah->opmode == NL80211_IFTYPE_AP) {
3172111ac0dSBruno Randolf /* AP mode */
3182111ac0dSBruno Randolf if (as->firstep_level > 0) {
3192111ac0dSBruno Randolf ath5k_ani_set_firstep_level(ah, as->firstep_level - 1);
3202111ac0dSBruno Randolf return;
3212111ac0dSBruno Randolf }
3222111ac0dSBruno Randolf } else {
3232111ac0dSBruno Randolf /* STA and IBSS mode (see TODO above) */
3242111ac0dSBruno Randolf if (rssi > ATH5K_ANI_RSSI_THR_HIGH) {
3252111ac0dSBruno Randolf /* beacon signal is high, leave OFDM weak signal
3262111ac0dSBruno Randolf * detection off or it may oscillate
3272111ac0dSBruno Randolf * TODO: who said it's off??? */
3282111ac0dSBruno Randolf } else if (rssi > ATH5K_ANI_RSSI_THR_LOW) {
3292111ac0dSBruno Randolf /* beacon RSSI is mid-range: turn on ODFM weak signal
3302111ac0dSBruno Randolf * detection and next, lower firstep level */
33123677ce3SJoe Perches if (!as->ofdm_weak_sig) {
3322111ac0dSBruno Randolf ath5k_ani_set_ofdm_weak_signal_detection(ah,
3332111ac0dSBruno Randolf true);
3342111ac0dSBruno Randolf return;
3352111ac0dSBruno Randolf }
3362111ac0dSBruno Randolf if (as->firstep_level > 0) {
3372111ac0dSBruno Randolf ath5k_ani_set_firstep_level(ah,
3382111ac0dSBruno Randolf as->firstep_level - 1);
3392111ac0dSBruno Randolf return;
3402111ac0dSBruno Randolf }
3412111ac0dSBruno Randolf } else {
3422111ac0dSBruno Randolf /* beacon signal is low: only reduce firstep level */
3432111ac0dSBruno Randolf if (as->firstep_level > 0) {
3442111ac0dSBruno Randolf ath5k_ani_set_firstep_level(ah,
3452111ac0dSBruno Randolf as->firstep_level - 1);
3462111ac0dSBruno Randolf return;
3472111ac0dSBruno Randolf }
3482111ac0dSBruno Randolf }
3492111ac0dSBruno Randolf }
3502111ac0dSBruno Randolf
3512111ac0dSBruno Randolf /* all modes */
3522111ac0dSBruno Randolf if (as->spur_level > 0) {
3532111ac0dSBruno Randolf ath5k_ani_set_spur_immunity_level(ah, as->spur_level - 1);
3542111ac0dSBruno Randolf return;
3552111ac0dSBruno Randolf }
3562111ac0dSBruno Randolf
3572111ac0dSBruno Randolf /* finally, reduce noise immunity */
3582111ac0dSBruno Randolf if (as->noise_imm_level > 0) {
3592111ac0dSBruno Randolf ath5k_ani_set_noise_immunity_level(ah, as->noise_imm_level - 1);
3602111ac0dSBruno Randolf return;
3612111ac0dSBruno Randolf }
3622111ac0dSBruno Randolf }
3632111ac0dSBruno Randolf
3642111ac0dSBruno Randolf /**
3657109ca5cSFelix Fietkau * ath5k_hw_ani_get_listen_time() - Update counters and return listening time
366c47faa36SNick Kossifidis * @ah: The &struct ath5k_hw
367c47faa36SNick Kossifidis * @as: The &struct ath5k_ani_state
3682111ac0dSBruno Randolf *
3692111ac0dSBruno Randolf * Return an approximation of the time spent "listening" in milliseconds (ms)
3707109ca5cSFelix Fietkau * since the last call of this function.
3717109ca5cSFelix Fietkau * Save a snapshot of the counter values for debugging/statistics.
3722111ac0dSBruno Randolf */
3732111ac0dSBruno Randolf static int
ath5k_hw_ani_get_listen_time(struct ath5k_hw * ah,struct ath5k_ani_state * as)3742111ac0dSBruno Randolf ath5k_hw_ani_get_listen_time(struct ath5k_hw *ah, struct ath5k_ani_state *as)
3752111ac0dSBruno Randolf {
3767109ca5cSFelix Fietkau struct ath_common *common = ath5k_hw_common(ah);
3772111ac0dSBruno Randolf int listen;
3782111ac0dSBruno Randolf
3797109ca5cSFelix Fietkau spin_lock_bh(&common->cc_lock);
3802111ac0dSBruno Randolf
3817109ca5cSFelix Fietkau ath_hw_cycle_counters_update(common);
3827109ca5cSFelix Fietkau memcpy(&as->last_cc, &common->cc_ani, sizeof(as->last_cc));
3832111ac0dSBruno Randolf
3847109ca5cSFelix Fietkau /* clears common->cc_ani */
3857109ca5cSFelix Fietkau listen = ath_hw_get_listen_time(common);
3867109ca5cSFelix Fietkau
3877109ca5cSFelix Fietkau spin_unlock_bh(&common->cc_lock);
3887109ca5cSFelix Fietkau
3892111ac0dSBruno Randolf return listen;
3902111ac0dSBruno Randolf }
3912111ac0dSBruno Randolf
3922111ac0dSBruno Randolf /**
3932111ac0dSBruno Randolf * ath5k_ani_save_and_clear_phy_errors() - Clear and save PHY error counters
394c47faa36SNick Kossifidis * @ah: The &struct ath5k_hw
395c47faa36SNick Kossifidis * @as: The &struct ath5k_ani_state
3962111ac0dSBruno Randolf *
3972111ac0dSBruno Randolf * Clear the PHY error counters as soon as possible, since this might be called
3982111ac0dSBruno Randolf * from a MIB interrupt and we want to make sure we don't get interrupted again.
3992111ac0dSBruno Randolf * Add the count of CCK and OFDM errors to our internal state, so it can be used
4002111ac0dSBruno Randolf * by the algorithm later.
4012111ac0dSBruno Randolf *
4022111ac0dSBruno Randolf * Will be called from interrupt and tasklet context.
4032111ac0dSBruno Randolf * Returns 0 if both counters are zero.
4042111ac0dSBruno Randolf */
4052111ac0dSBruno Randolf static int
ath5k_ani_save_and_clear_phy_errors(struct ath5k_hw * ah,struct ath5k_ani_state * as)4062111ac0dSBruno Randolf ath5k_ani_save_and_clear_phy_errors(struct ath5k_hw *ah,
4072111ac0dSBruno Randolf struct ath5k_ani_state *as)
4082111ac0dSBruno Randolf {
4092111ac0dSBruno Randolf unsigned int ofdm_err, cck_err;
4102111ac0dSBruno Randolf
4112111ac0dSBruno Randolf if (!ah->ah_capabilities.cap_has_phyerr_counters)
4122111ac0dSBruno Randolf return 0;
4132111ac0dSBruno Randolf
4142111ac0dSBruno Randolf ofdm_err = ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1);
4152111ac0dSBruno Randolf cck_err = ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2);
4162111ac0dSBruno Randolf
4172111ac0dSBruno Randolf /* reset counters first, we might be in a hurry (interrupt) */
4182111ac0dSBruno Randolf ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_OFDM_TRIG_HIGH,
4192111ac0dSBruno Randolf AR5K_PHYERR_CNT1);
4202111ac0dSBruno Randolf ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_CCK_TRIG_HIGH,
4212111ac0dSBruno Randolf AR5K_PHYERR_CNT2);
4222111ac0dSBruno Randolf
4232111ac0dSBruno Randolf ofdm_err = ATH5K_ANI_OFDM_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX - ofdm_err);
4242111ac0dSBruno Randolf cck_err = ATH5K_ANI_CCK_TRIG_HIGH - (ATH5K_PHYERR_CNT_MAX - cck_err);
4252111ac0dSBruno Randolf
4262111ac0dSBruno Randolf /* sometimes both can be zero, especially when there is a superfluous
4272111ac0dSBruno Randolf * second interrupt. detect that here and return an error. */
4282111ac0dSBruno Randolf if (ofdm_err <= 0 && cck_err <= 0)
4292111ac0dSBruno Randolf return 0;
4302111ac0dSBruno Randolf
4312111ac0dSBruno Randolf /* avoid negative values should one of the registers overflow */
4322111ac0dSBruno Randolf if (ofdm_err > 0) {
4332111ac0dSBruno Randolf as->ofdm_errors += ofdm_err;
4342111ac0dSBruno Randolf as->sum_ofdm_errors += ofdm_err;
4352111ac0dSBruno Randolf }
4362111ac0dSBruno Randolf if (cck_err > 0) {
4372111ac0dSBruno Randolf as->cck_errors += cck_err;
4382111ac0dSBruno Randolf as->sum_cck_errors += cck_err;
4392111ac0dSBruno Randolf }
4402111ac0dSBruno Randolf return 1;
4412111ac0dSBruno Randolf }
4422111ac0dSBruno Randolf
4432111ac0dSBruno Randolf /**
4442111ac0dSBruno Randolf * ath5k_ani_period_restart() - Restart ANI period
445c47faa36SNick Kossifidis * @as: The &struct ath5k_ani_state
4462111ac0dSBruno Randolf *
4472111ac0dSBruno Randolf * Just reset counters, so they are clear for the next "ani period".
4482111ac0dSBruno Randolf */
4492111ac0dSBruno Randolf static void
ath5k_ani_period_restart(struct ath5k_ani_state * as)45029355a48SNick Kossifidis ath5k_ani_period_restart(struct ath5k_ani_state *as)
4512111ac0dSBruno Randolf {
4522111ac0dSBruno Randolf /* keep last values for debugging */
4532111ac0dSBruno Randolf as->last_ofdm_errors = as->ofdm_errors;
4542111ac0dSBruno Randolf as->last_cck_errors = as->cck_errors;
4552111ac0dSBruno Randolf as->last_listen = as->listen_time;
4562111ac0dSBruno Randolf
4572111ac0dSBruno Randolf as->ofdm_errors = 0;
4582111ac0dSBruno Randolf as->cck_errors = 0;
4592111ac0dSBruno Randolf as->listen_time = 0;
4602111ac0dSBruno Randolf }
4612111ac0dSBruno Randolf
4622111ac0dSBruno Randolf /**
4632111ac0dSBruno Randolf * ath5k_ani_calibration() - The main ANI calibration function
464c47faa36SNick Kossifidis * @ah: The &struct ath5k_hw
4652111ac0dSBruno Randolf *
4662111ac0dSBruno Randolf * We count OFDM and CCK errors relative to the time where we did not send or
4672111ac0dSBruno Randolf * receive ("listen" time) and raise or lower immunity accordingly.
4682111ac0dSBruno Randolf * This is called regularly (every second) from the calibration timer, but also
4692111ac0dSBruno Randolf * when an error threshold has been reached.
4702111ac0dSBruno Randolf *
4712111ac0dSBruno Randolf * In order to synchronize access from different contexts, this should be
4722111ac0dSBruno Randolf * called only indirectly by scheduling the ANI tasklet!
4732111ac0dSBruno Randolf */
4742111ac0dSBruno Randolf void
ath5k_ani_calibration(struct ath5k_hw * ah)4752111ac0dSBruno Randolf ath5k_ani_calibration(struct ath5k_hw *ah)
4762111ac0dSBruno Randolf {
477e0d687bdSPavel Roskin struct ath5k_ani_state *as = &ah->ani_state;
4782111ac0dSBruno Randolf int listen, ofdm_high, ofdm_low, cck_high, cck_low;
4792111ac0dSBruno Randolf
4802111ac0dSBruno Randolf /* get listen time since last call and add it to the counter because we
4819537a162SBruno Randolf * might not have restarted the "ani period" last time.
4829537a162SBruno Randolf * always do this to calculate the busy time also in manual mode */
4832111ac0dSBruno Randolf listen = ath5k_hw_ani_get_listen_time(ah, as);
4842111ac0dSBruno Randolf as->listen_time += listen;
4852111ac0dSBruno Randolf
4869537a162SBruno Randolf if (as->ani_mode != ATH5K_ANI_MODE_AUTO)
4879537a162SBruno Randolf return;
4889537a162SBruno Randolf
4892111ac0dSBruno Randolf ath5k_ani_save_and_clear_phy_errors(ah, as);
4902111ac0dSBruno Randolf
4912111ac0dSBruno Randolf ofdm_high = as->listen_time * ATH5K_ANI_OFDM_TRIG_HIGH / 1000;
4922111ac0dSBruno Randolf cck_high = as->listen_time * ATH5K_ANI_CCK_TRIG_HIGH / 1000;
4932111ac0dSBruno Randolf ofdm_low = as->listen_time * ATH5K_ANI_OFDM_TRIG_LOW / 1000;
4942111ac0dSBruno Randolf cck_low = as->listen_time * ATH5K_ANI_CCK_TRIG_LOW / 1000;
4952111ac0dSBruno Randolf
496e0d687bdSPavel Roskin ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
4972111ac0dSBruno Randolf "listen %d (now %d)", as->listen_time, listen);
498e0d687bdSPavel Roskin ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
4992111ac0dSBruno Randolf "check high ofdm %d/%d cck %d/%d",
5002111ac0dSBruno Randolf as->ofdm_errors, ofdm_high, as->cck_errors, cck_high);
5012111ac0dSBruno Randolf
5022111ac0dSBruno Randolf if (as->ofdm_errors > ofdm_high || as->cck_errors > cck_high) {
5032111ac0dSBruno Randolf /* too many PHY errors - we have to raise immunity */
504*c26b01d5SJason Yan bool ofdm_flag = as->ofdm_errors > ofdm_high;
5052111ac0dSBruno Randolf ath5k_ani_raise_immunity(ah, as, ofdm_flag);
50629355a48SNick Kossifidis ath5k_ani_period_restart(as);
5072111ac0dSBruno Randolf
5082111ac0dSBruno Randolf } else if (as->listen_time > 5 * ATH5K_ANI_LISTEN_PERIOD) {
5092111ac0dSBruno Randolf /* If more than 5 (TODO: why 5?) periods have passed and we got
5102111ac0dSBruno Randolf * relatively little errors we can try to lower immunity */
511e0d687bdSPavel Roskin ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
5122111ac0dSBruno Randolf "check low ofdm %d/%d cck %d/%d",
5132111ac0dSBruno Randolf as->ofdm_errors, ofdm_low, as->cck_errors, cck_low);
5142111ac0dSBruno Randolf
5152111ac0dSBruno Randolf if (as->ofdm_errors <= ofdm_low && as->cck_errors <= cck_low)
5162111ac0dSBruno Randolf ath5k_ani_lower_immunity(ah, as);
5172111ac0dSBruno Randolf
51829355a48SNick Kossifidis ath5k_ani_period_restart(as);
5192111ac0dSBruno Randolf }
5202111ac0dSBruno Randolf }
5212111ac0dSBruno Randolf
5222111ac0dSBruno Randolf
523c47faa36SNick Kossifidis /*******************\
524c47faa36SNick Kossifidis * Interrupt handler *
525c47faa36SNick Kossifidis \*******************/
5262111ac0dSBruno Randolf
5272111ac0dSBruno Randolf /**
5282111ac0dSBruno Randolf * ath5k_ani_mib_intr() - Interrupt handler for ANI MIB counters
529c47faa36SNick Kossifidis * @ah: The &struct ath5k_hw
5302111ac0dSBruno Randolf *
5312111ac0dSBruno Randolf * Just read & reset the registers quickly, so they don't generate more
5322111ac0dSBruno Randolf * interrupts, save the counters and schedule the tasklet to decide whether
5332111ac0dSBruno Randolf * to raise immunity or not.
5342111ac0dSBruno Randolf *
5352111ac0dSBruno Randolf * We just need to handle PHY error counters, ath5k_hw_update_mib_counters()
5362111ac0dSBruno Randolf * should take care of all "normal" MIB interrupts.
5372111ac0dSBruno Randolf */
5382111ac0dSBruno Randolf void
ath5k_ani_mib_intr(struct ath5k_hw * ah)5392111ac0dSBruno Randolf ath5k_ani_mib_intr(struct ath5k_hw *ah)
5402111ac0dSBruno Randolf {
541e0d687bdSPavel Roskin struct ath5k_ani_state *as = &ah->ani_state;
5422111ac0dSBruno Randolf
5432111ac0dSBruno Randolf /* nothing to do here if HW does not have PHY error counters - they
5442111ac0dSBruno Randolf * can't be the reason for the MIB interrupt then */
5452111ac0dSBruno Randolf if (!ah->ah_capabilities.cap_has_phyerr_counters)
5462111ac0dSBruno Randolf return;
5472111ac0dSBruno Randolf
5482111ac0dSBruno Randolf /* not in use but clear anyways */
5492111ac0dSBruno Randolf ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT);
5502111ac0dSBruno Randolf ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT);
5512111ac0dSBruno Randolf
552e0d687bdSPavel Roskin if (ah->ani_state.ani_mode != ATH5K_ANI_MODE_AUTO)
5532111ac0dSBruno Randolf return;
5542111ac0dSBruno Randolf
555a180a130SBob Copeland /* If one of the errors triggered, we can get a superfluous second
556a180a130SBob Copeland * interrupt, even though we have already reset the register. The
557a180a130SBob Copeland * function detects that so we can return early. */
5582111ac0dSBruno Randolf if (ath5k_ani_save_and_clear_phy_errors(ah, as) == 0)
5592111ac0dSBruno Randolf return;
5602111ac0dSBruno Randolf
5612111ac0dSBruno Randolf if (as->ofdm_errors > ATH5K_ANI_OFDM_TRIG_HIGH ||
5622111ac0dSBruno Randolf as->cck_errors > ATH5K_ANI_CCK_TRIG_HIGH)
563e0d687bdSPavel Roskin tasklet_schedule(&ah->ani_tasklet);
5642111ac0dSBruno Randolf }
5652111ac0dSBruno Randolf
5662111ac0dSBruno Randolf /**
567c47faa36SNick Kossifidis * ath5k_ani_phy_error_report - Used by older HW to report PHY errors
568c47faa36SNick Kossifidis *
569c47faa36SNick Kossifidis * @ah: The &struct ath5k_hw
570c47faa36SNick Kossifidis * @phyerr: One of enum ath5k_phy_error_code
5712111ac0dSBruno Randolf *
5722111ac0dSBruno Randolf * This is used by hardware without PHY error counters to report PHY errors
5732111ac0dSBruno Randolf * on a frame-by-frame basis, instead of the interrupt.
5742111ac0dSBruno Randolf */
5752111ac0dSBruno Randolf void
ath5k_ani_phy_error_report(struct ath5k_hw * ah,enum ath5k_phy_error_code phyerr)5762111ac0dSBruno Randolf ath5k_ani_phy_error_report(struct ath5k_hw *ah,
5772111ac0dSBruno Randolf enum ath5k_phy_error_code phyerr)
5782111ac0dSBruno Randolf {
579e0d687bdSPavel Roskin struct ath5k_ani_state *as = &ah->ani_state;
5802111ac0dSBruno Randolf
5812111ac0dSBruno Randolf if (phyerr == AR5K_RX_PHY_ERROR_OFDM_TIMING) {
5822111ac0dSBruno Randolf as->ofdm_errors++;
5832111ac0dSBruno Randolf if (as->ofdm_errors > ATH5K_ANI_OFDM_TRIG_HIGH)
584e0d687bdSPavel Roskin tasklet_schedule(&ah->ani_tasklet);
5852111ac0dSBruno Randolf } else if (phyerr == AR5K_RX_PHY_ERROR_CCK_TIMING) {
5862111ac0dSBruno Randolf as->cck_errors++;
5872111ac0dSBruno Randolf if (as->cck_errors > ATH5K_ANI_CCK_TRIG_HIGH)
588e0d687bdSPavel Roskin tasklet_schedule(&ah->ani_tasklet);
5892111ac0dSBruno Randolf }
5902111ac0dSBruno Randolf }
5912111ac0dSBruno Randolf
5922111ac0dSBruno Randolf
593c47faa36SNick Kossifidis /****************\
594c47faa36SNick Kossifidis * Initialization *
595c47faa36SNick Kossifidis \****************/
5962111ac0dSBruno Randolf
5972111ac0dSBruno Randolf /**
5982111ac0dSBruno Randolf * ath5k_enable_phy_err_counters() - Enable PHY error counters
599c47faa36SNick Kossifidis * @ah: The &struct ath5k_hw
6002111ac0dSBruno Randolf *
6012111ac0dSBruno Randolf * Enable PHY error counters for OFDM and CCK timing errors.
6022111ac0dSBruno Randolf */
6032111ac0dSBruno Randolf static void
ath5k_enable_phy_err_counters(struct ath5k_hw * ah)6042111ac0dSBruno Randolf ath5k_enable_phy_err_counters(struct ath5k_hw *ah)
6052111ac0dSBruno Randolf {
6062111ac0dSBruno Randolf ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_OFDM_TRIG_HIGH,
6072111ac0dSBruno Randolf AR5K_PHYERR_CNT1);
6082111ac0dSBruno Randolf ath5k_hw_reg_write(ah, ATH5K_PHYERR_CNT_MAX - ATH5K_ANI_CCK_TRIG_HIGH,
6092111ac0dSBruno Randolf AR5K_PHYERR_CNT2);
6102111ac0dSBruno Randolf ath5k_hw_reg_write(ah, AR5K_PHY_ERR_FIL_OFDM, AR5K_PHYERR_CNT1_MASK);
6112111ac0dSBruno Randolf ath5k_hw_reg_write(ah, AR5K_PHY_ERR_FIL_CCK, AR5K_PHYERR_CNT2_MASK);
6122111ac0dSBruno Randolf
6132111ac0dSBruno Randolf /* not in use */
6142111ac0dSBruno Randolf ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT);
6152111ac0dSBruno Randolf ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT);
6162111ac0dSBruno Randolf }
6172111ac0dSBruno Randolf
6182111ac0dSBruno Randolf /**
6192111ac0dSBruno Randolf * ath5k_disable_phy_err_counters() - Disable PHY error counters
620c47faa36SNick Kossifidis * @ah: The &struct ath5k_hw
6212111ac0dSBruno Randolf *
6222111ac0dSBruno Randolf * Disable PHY error counters for OFDM and CCK timing errors.
6232111ac0dSBruno Randolf */
6242111ac0dSBruno Randolf static void
ath5k_disable_phy_err_counters(struct ath5k_hw * ah)6252111ac0dSBruno Randolf ath5k_disable_phy_err_counters(struct ath5k_hw *ah)
6262111ac0dSBruno Randolf {
6272111ac0dSBruno Randolf ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT1);
6282111ac0dSBruno Randolf ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT2);
6292111ac0dSBruno Randolf ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT1_MASK);
6302111ac0dSBruno Randolf ath5k_hw_reg_write(ah, 0, AR5K_PHYERR_CNT2_MASK);
6312111ac0dSBruno Randolf
6322111ac0dSBruno Randolf /* not in use */
6332111ac0dSBruno Randolf ath5k_hw_reg_write(ah, 0, AR5K_OFDM_FIL_CNT);
6342111ac0dSBruno Randolf ath5k_hw_reg_write(ah, 0, AR5K_CCK_FIL_CNT);
6352111ac0dSBruno Randolf }
6362111ac0dSBruno Randolf
6372111ac0dSBruno Randolf /**
6382111ac0dSBruno Randolf * ath5k_ani_init() - Initialize ANI
639c47faa36SNick Kossifidis * @ah: The &struct ath5k_hw
640c47faa36SNick Kossifidis * @mode: One of enum ath5k_ani_mode
6412111ac0dSBruno Randolf *
6422111ac0dSBruno Randolf * Initialize ANI according to mode.
6432111ac0dSBruno Randolf */
6442111ac0dSBruno Randolf void
ath5k_ani_init(struct ath5k_hw * ah,enum ath5k_ani_mode mode)6452111ac0dSBruno Randolf ath5k_ani_init(struct ath5k_hw *ah, enum ath5k_ani_mode mode)
6462111ac0dSBruno Randolf {
6472111ac0dSBruno Randolf /* ANI is only possible on 5212 and newer */
6482111ac0dSBruno Randolf if (ah->ah_version < AR5K_AR5212)
6492111ac0dSBruno Randolf return;
6502111ac0dSBruno Randolf
65186fbe17dSPavel Roskin if (mode < ATH5K_ANI_MODE_OFF || mode > ATH5K_ANI_MODE_AUTO) {
652e0d687bdSPavel Roskin ATH5K_ERR(ah, "ANI mode %d out of range", mode);
65386fbe17dSPavel Roskin return;
65486fbe17dSPavel Roskin }
65586fbe17dSPavel Roskin
6562111ac0dSBruno Randolf /* clear old state information */
657e0d687bdSPavel Roskin memset(&ah->ani_state, 0, sizeof(ah->ani_state));
6582111ac0dSBruno Randolf
6592111ac0dSBruno Randolf /* older hardware has more spur levels than newer */
6602111ac0dSBruno Randolf if (ah->ah_mac_srev < AR5K_SREV_AR2414)
661e0d687bdSPavel Roskin ah->ani_state.max_spur_level = 7;
6622111ac0dSBruno Randolf else
663e0d687bdSPavel Roskin ah->ani_state.max_spur_level = 2;
6642111ac0dSBruno Randolf
6652111ac0dSBruno Randolf /* initial values for our ani parameters */
6662111ac0dSBruno Randolf if (mode == ATH5K_ANI_MODE_OFF) {
667e0d687bdSPavel Roskin ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "ANI off\n");
6682111ac0dSBruno Randolf } else if (mode == ATH5K_ANI_MODE_MANUAL_LOW) {
669e0d687bdSPavel Roskin ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
6702111ac0dSBruno Randolf "ANI manual low -> high sensitivity\n");
6712111ac0dSBruno Randolf ath5k_ani_set_noise_immunity_level(ah, 0);
6722111ac0dSBruno Randolf ath5k_ani_set_spur_immunity_level(ah, 0);
6732111ac0dSBruno Randolf ath5k_ani_set_firstep_level(ah, 0);
6742111ac0dSBruno Randolf ath5k_ani_set_ofdm_weak_signal_detection(ah, true);
6752111ac0dSBruno Randolf ath5k_ani_set_cck_weak_signal_detection(ah, true);
6762111ac0dSBruno Randolf } else if (mode == ATH5K_ANI_MODE_MANUAL_HIGH) {
677e0d687bdSPavel Roskin ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI,
6782111ac0dSBruno Randolf "ANI manual high -> low sensitivity\n");
6792111ac0dSBruno Randolf ath5k_ani_set_noise_immunity_level(ah,
6802111ac0dSBruno Randolf ATH5K_ANI_MAX_NOISE_IMM_LVL);
6812111ac0dSBruno Randolf ath5k_ani_set_spur_immunity_level(ah,
682e0d687bdSPavel Roskin ah->ani_state.max_spur_level);
6832111ac0dSBruno Randolf ath5k_ani_set_firstep_level(ah, ATH5K_ANI_MAX_FIRSTEP_LVL);
6842111ac0dSBruno Randolf ath5k_ani_set_ofdm_weak_signal_detection(ah, false);
6852111ac0dSBruno Randolf ath5k_ani_set_cck_weak_signal_detection(ah, false);
6862111ac0dSBruno Randolf } else if (mode == ATH5K_ANI_MODE_AUTO) {
687e0d687bdSPavel Roskin ATH5K_DBG_UNLIMIT(ah, ATH5K_DEBUG_ANI, "ANI auto\n");
6882111ac0dSBruno Randolf ath5k_ani_set_noise_immunity_level(ah, 0);
6892111ac0dSBruno Randolf ath5k_ani_set_spur_immunity_level(ah, 0);
6902111ac0dSBruno Randolf ath5k_ani_set_firstep_level(ah, 0);
6912111ac0dSBruno Randolf ath5k_ani_set_ofdm_weak_signal_detection(ah, true);
6922111ac0dSBruno Randolf ath5k_ani_set_cck_weak_signal_detection(ah, false);
6932111ac0dSBruno Randolf }
6942111ac0dSBruno Randolf
6952111ac0dSBruno Randolf /* newer hardware has PHY error counter registers which we can use to
6962111ac0dSBruno Randolf * get OFDM and CCK error counts. older hardware has to set rxfilter and
6972111ac0dSBruno Randolf * report every single PHY error by calling ath5k_ani_phy_error_report()
6982111ac0dSBruno Randolf */
6992111ac0dSBruno Randolf if (mode == ATH5K_ANI_MODE_AUTO) {
7002111ac0dSBruno Randolf if (ah->ah_capabilities.cap_has_phyerr_counters)
7012111ac0dSBruno Randolf ath5k_enable_phy_err_counters(ah);
7022111ac0dSBruno Randolf else
7032111ac0dSBruno Randolf ath5k_hw_set_rx_filter(ah, ath5k_hw_get_rx_filter(ah) |
7042111ac0dSBruno Randolf AR5K_RX_FILTER_PHYERR);
7052111ac0dSBruno Randolf } else {
7062111ac0dSBruno Randolf if (ah->ah_capabilities.cap_has_phyerr_counters)
7072111ac0dSBruno Randolf ath5k_disable_phy_err_counters(ah);
7082111ac0dSBruno Randolf else
7092111ac0dSBruno Randolf ath5k_hw_set_rx_filter(ah, ath5k_hw_get_rx_filter(ah) &
7102111ac0dSBruno Randolf ~AR5K_RX_FILTER_PHYERR);
7112111ac0dSBruno Randolf }
7122111ac0dSBruno Randolf
713e0d687bdSPavel Roskin ah->ani_state.ani_mode = mode;
7142111ac0dSBruno Randolf }
7152111ac0dSBruno Randolf
7162111ac0dSBruno Randolf
717c47faa36SNick Kossifidis /**************\
718c47faa36SNick Kossifidis * Debug output *
719c47faa36SNick Kossifidis \**************/
7202111ac0dSBruno Randolf
7212111ac0dSBruno Randolf #ifdef CONFIG_ATH5K_DEBUG
7222111ac0dSBruno Randolf
723c47faa36SNick Kossifidis /**
724c47faa36SNick Kossifidis * ath5k_ani_print_counters() - Print ANI counters
725c47faa36SNick Kossifidis * @ah: The &struct ath5k_hw
726c47faa36SNick Kossifidis *
727c47faa36SNick Kossifidis * Used for debugging ANI
728c47faa36SNick Kossifidis */
7292111ac0dSBruno Randolf void
ath5k_ani_print_counters(struct ath5k_hw * ah)7302111ac0dSBruno Randolf ath5k_ani_print_counters(struct ath5k_hw *ah)
7312111ac0dSBruno Randolf {
7322111ac0dSBruno Randolf /* clears too */
733516304b0SJoe Perches pr_notice("ACK fail\t%d\n", ath5k_hw_reg_read(ah, AR5K_ACK_FAIL));
734516304b0SJoe Perches pr_notice("RTS fail\t%d\n", ath5k_hw_reg_read(ah, AR5K_RTS_FAIL));
735516304b0SJoe Perches pr_notice("RTS success\t%d\n", ath5k_hw_reg_read(ah, AR5K_RTS_OK));
736516304b0SJoe Perches pr_notice("FCS error\t%d\n", ath5k_hw_reg_read(ah, AR5K_FCS_FAIL));
7372111ac0dSBruno Randolf
7382111ac0dSBruno Randolf /* no clear */
739516304b0SJoe Perches pr_notice("tx\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_TX));
740516304b0SJoe Perches pr_notice("rx\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_RX));
741516304b0SJoe Perches pr_notice("busy\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_RXCLR));
742516304b0SJoe Perches pr_notice("cycles\t%d\n", ath5k_hw_reg_read(ah, AR5K_PROFCNT_CYCLE));
7432111ac0dSBruno Randolf
744516304b0SJoe Perches pr_notice("AR5K_PHYERR_CNT1\t%d\n",
7452111ac0dSBruno Randolf ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT1));
746516304b0SJoe Perches pr_notice("AR5K_PHYERR_CNT2\t%d\n",
7472111ac0dSBruno Randolf ath5k_hw_reg_read(ah, AR5K_PHYERR_CNT2));
748516304b0SJoe Perches pr_notice("AR5K_OFDM_FIL_CNT\t%d\n",
7492111ac0dSBruno Randolf ath5k_hw_reg_read(ah, AR5K_OFDM_FIL_CNT));
750516304b0SJoe Perches pr_notice("AR5K_CCK_FIL_CNT\t%d\n",
7512111ac0dSBruno Randolf ath5k_hw_reg_read(ah, AR5K_CCK_FIL_CNT));
7522111ac0dSBruno Randolf }
7532111ac0dSBruno Randolf
7542111ac0dSBruno Randolf #endif
755