xref: /dflybsd-src/sys/contrib/dev/ath/ath_hal/ar9300/ar9300_ani.c (revision f12e211682553026f6ac76f76a296cc6d457f933)
1b7d5e03cSMatthew Dillon /*
2b7d5e03cSMatthew Dillon  * Copyright (c) 2013 Qualcomm Atheros, Inc.
3b7d5e03cSMatthew Dillon  *
4b7d5e03cSMatthew Dillon  * Permission to use, copy, modify, and/or distribute this software for any
5b7d5e03cSMatthew Dillon  * purpose with or without fee is hereby granted, provided that the above
6b7d5e03cSMatthew Dillon  * copyright notice and this permission notice appear in all copies.
7b7d5e03cSMatthew Dillon  *
8b7d5e03cSMatthew Dillon  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9b7d5e03cSMatthew Dillon  * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10b7d5e03cSMatthew Dillon  * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11b7d5e03cSMatthew Dillon  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12b7d5e03cSMatthew Dillon  * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13b7d5e03cSMatthew Dillon  * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14b7d5e03cSMatthew Dillon  * PERFORMANCE OF THIS SOFTWARE.
15b7d5e03cSMatthew Dillon  */
16b7d5e03cSMatthew Dillon 
17b7d5e03cSMatthew Dillon #include "opt_ah.h"
18b7d5e03cSMatthew Dillon 
19b7d5e03cSMatthew Dillon #include "ah.h"
20b7d5e03cSMatthew Dillon #include "ah_internal.h"
21b7d5e03cSMatthew Dillon #include "ah_desc.h"
22b7d5e03cSMatthew Dillon //#include "ah_pktlog.h"
23b7d5e03cSMatthew Dillon 
24b7d5e03cSMatthew Dillon #include "ar9300/ar9300.h"
25b7d5e03cSMatthew Dillon #include "ar9300/ar9300reg.h"
26b7d5e03cSMatthew Dillon #include "ar9300/ar9300phy.h"
27b7d5e03cSMatthew Dillon 
28b7d5e03cSMatthew Dillon extern  void ar9300_set_rx_filter(struct ath_hal *ah, u_int32_t bits);
29b7d5e03cSMatthew Dillon extern  u_int32_t ar9300_get_rx_filter(struct ath_hal *ah);
30b7d5e03cSMatthew Dillon 
31b7d5e03cSMatthew Dillon #define HAL_ANI_DEBUG 1
32b7d5e03cSMatthew Dillon 
33b7d5e03cSMatthew Dillon /*
34b7d5e03cSMatthew Dillon  * Anti noise immunity support.  We track phy errors and react
35b7d5e03cSMatthew Dillon  * to excessive errors by adjusting the noise immunity parameters.
36b7d5e03cSMatthew Dillon  */
37b7d5e03cSMatthew Dillon 
38b7d5e03cSMatthew Dillon /******************************************************************************
39b7d5e03cSMatthew Dillon  *
40b7d5e03cSMatthew Dillon  * New Ani Algorithm for Station side only
41b7d5e03cSMatthew Dillon  *
42b7d5e03cSMatthew Dillon  *****************************************************************************/
43b7d5e03cSMatthew Dillon 
44b7d5e03cSMatthew Dillon #define HAL_ANI_OFDM_TRIG_HIGH     1000 /* units are errors per second */
45b7d5e03cSMatthew Dillon #define HAL_ANI_OFDM_TRIG_LOW       400 /* units are errors per second */
46b7d5e03cSMatthew Dillon #define HAL_ANI_CCK_TRIG_HIGH       600 /* units are errors per second */
47b7d5e03cSMatthew Dillon #define HAL_ANI_CCK_TRIG_LOW        300 /* units are errors per second */
48b7d5e03cSMatthew Dillon #define HAL_ANI_USE_OFDM_WEAK_SIG  AH_TRUE
49b7d5e03cSMatthew Dillon #define HAL_ANI_ENABLE_MRC_CCK     AH_TRUE /* default is enabled */
50b7d5e03cSMatthew Dillon #define HAL_ANI_DEF_SPUR_IMMUNE_LVL   3
51b7d5e03cSMatthew Dillon #define HAL_ANI_DEF_FIRSTEP_LVL       2
52b7d5e03cSMatthew Dillon #define HAL_ANI_RSSI_THR_HIGH        40
53b7d5e03cSMatthew Dillon #define HAL_ANI_RSSI_THR_LOW          7
54b7d5e03cSMatthew Dillon #define HAL_ANI_PERIOD             1000
55b7d5e03cSMatthew Dillon 
56b7d5e03cSMatthew Dillon #define HAL_NOISE_DETECT_PERIOD     100
57b7d5e03cSMatthew Dillon #define HAL_NOISE_RECOVER_PERIOD    5000
58b7d5e03cSMatthew Dillon 
59b7d5e03cSMatthew Dillon #define HAL_SIG_FIRSTEP_SETTING_MIN   0
60b7d5e03cSMatthew Dillon #define HAL_SIG_FIRSTEP_SETTING_MAX  20
61b7d5e03cSMatthew Dillon #define HAL_SIG_SPUR_IMM_SETTING_MIN  0
62b7d5e03cSMatthew Dillon #define HAL_SIG_SPUR_IMM_SETTING_MAX 22
63b7d5e03cSMatthew Dillon 
64b7d5e03cSMatthew Dillon #define HAL_EP_RND(x, mul) \
65b7d5e03cSMatthew Dillon     ((((x) % (mul)) >= ((mul) / 2)) ? ((x) + ((mul) - 1)) / (mul) : (x) / (mul))
66b7d5e03cSMatthew Dillon #define BEACON_RSSI(ahp) \
67b7d5e03cSMatthew Dillon     HAL_EP_RND(ahp->ah_stats.ast_nodestats.ns_avgbrssi, \
68b7d5e03cSMatthew Dillon         HAL_RSSI_EP_MULTIPLIER)
69b7d5e03cSMatthew Dillon 
70b7d5e03cSMatthew Dillon typedef int TABLE[];
71b7d5e03cSMatthew Dillon /*
72b7d5e03cSMatthew Dillon  *                            level:    0   1   2   3   4   5   6   7   8
73b7d5e03cSMatthew Dillon  * firstep_table:    lvl 0-8, default 2
74b7d5e03cSMatthew Dillon  */
75b7d5e03cSMatthew Dillon static const TABLE firstep_table    = { -4, -2,  0,  2,  4,  6,  8, 10, 12};
76b7d5e03cSMatthew Dillon /* cycpwr_thr1_table: lvl 0-7, default 3 */
77b7d5e03cSMatthew Dillon static const TABLE cycpwr_thr1_table = { -6, -4, -2,  0,  2,  4,  6,  8 };
78b7d5e03cSMatthew Dillon /* values here are relative to the INI */
79b7d5e03cSMatthew Dillon 
80b7d5e03cSMatthew Dillon typedef struct _HAL_ANI_OFDM_LEVEL_ENTRY {
81b7d5e03cSMatthew Dillon     int spur_immunity_level;
82b7d5e03cSMatthew Dillon     int fir_step_level;
83b7d5e03cSMatthew Dillon     int ofdm_weak_signal_on;
84b7d5e03cSMatthew Dillon } HAL_ANI_OFDM_LEVEL_ENTRY;
85b7d5e03cSMatthew Dillon static const HAL_ANI_OFDM_LEVEL_ENTRY ofdm_level_table[] = {
86b7d5e03cSMatthew Dillon /*     SI  FS  WS */
87b7d5e03cSMatthew Dillon      {  0,  0,  1  }, /* lvl 0 */
88b7d5e03cSMatthew Dillon      {  1,  1,  1  }, /* lvl 1 */
89b7d5e03cSMatthew Dillon      {  2,  2,  1  }, /* lvl 2 */
90b7d5e03cSMatthew Dillon      {  3,  2,  1  }, /* lvl 3  (default) */
91b7d5e03cSMatthew Dillon      {  4,  3,  1  }, /* lvl 4 */
92b7d5e03cSMatthew Dillon      {  5,  4,  1  }, /* lvl 5 */
93b7d5e03cSMatthew Dillon      {  6,  5,  1  }, /* lvl 6 */
94b7d5e03cSMatthew Dillon      {  7,  6,  1  }, /* lvl 7 */
95b7d5e03cSMatthew Dillon      {  7,  7,  1  }, /* lvl 8 */
96b7d5e03cSMatthew Dillon      {  7,  8,  0  }  /* lvl 9 */
97b7d5e03cSMatthew Dillon };
98b7d5e03cSMatthew Dillon #define HAL_ANI_OFDM_NUM_LEVEL \
99b7d5e03cSMatthew Dillon     (sizeof(ofdm_level_table) / sizeof(ofdm_level_table[0]))
100b7d5e03cSMatthew Dillon #define HAL_ANI_OFDM_MAX_LEVEL (HAL_ANI_OFDM_NUM_LEVEL - 1)
101b7d5e03cSMatthew Dillon #define HAL_ANI_OFDM_DEF_LEVEL 3 /* default level - matches the INI settings */
102b7d5e03cSMatthew Dillon 
103b7d5e03cSMatthew Dillon typedef struct _HAL_ANI_CCK_LEVEL_ENTRY {
104b7d5e03cSMatthew Dillon     int fir_step_level;
105b7d5e03cSMatthew Dillon     int mrc_cck_on;
106b7d5e03cSMatthew Dillon } HAL_ANI_CCK_LEVEL_ENTRY;
107b7d5e03cSMatthew Dillon 
108b7d5e03cSMatthew Dillon static const HAL_ANI_CCK_LEVEL_ENTRY cck_level_table[] = {
109b7d5e03cSMatthew Dillon /*     FS  MRC-CCK */
110b7d5e03cSMatthew Dillon      {  0,  1  },  /* lvl 0 */
111b7d5e03cSMatthew Dillon      {  1,  1  },  /* lvl 1 */
112b7d5e03cSMatthew Dillon      {  2,  1  },  /* lvl 2  (default) */
113b7d5e03cSMatthew Dillon      {  3,  1  },  /* lvl 3 */
114b7d5e03cSMatthew Dillon      {  4,  0  },  /* lvl 4 */
115b7d5e03cSMatthew Dillon      {  5,  0  },  /* lvl 5 */
116b7d5e03cSMatthew Dillon      {  6,  0  },  /* lvl 6 */
117b7d5e03cSMatthew Dillon      {  7,  0  },  /* lvl 7 (only for high rssi) */
118b7d5e03cSMatthew Dillon      {  8,  0  }   /* lvl 8 (only for high rssi) */
119b7d5e03cSMatthew Dillon };
120b7d5e03cSMatthew Dillon #define HAL_ANI_CCK_NUM_LEVEL \
121b7d5e03cSMatthew Dillon     (sizeof(cck_level_table) / sizeof(cck_level_table[0]))
122b7d5e03cSMatthew Dillon #define HAL_ANI_CCK_MAX_LEVEL           (HAL_ANI_CCK_NUM_LEVEL - 1)
123b7d5e03cSMatthew Dillon #define HAL_ANI_CCK_MAX_LEVEL_LOW_RSSI  (HAL_ANI_CCK_NUM_LEVEL - 3)
124b7d5e03cSMatthew Dillon #define HAL_ANI_CCK_DEF_LEVEL 2 /* default level - matches the INI settings */
125b7d5e03cSMatthew Dillon 
126b7d5e03cSMatthew Dillon /*
127b7d5e03cSMatthew Dillon  * register values to turn OFDM weak signal detection OFF
128b7d5e03cSMatthew Dillon  */
129b7d5e03cSMatthew Dillon static const int m1_thresh_low_off     = 127;
130b7d5e03cSMatthew Dillon static const int m2_thresh_low_off     = 127;
131b7d5e03cSMatthew Dillon static const int m1_thresh_off         = 127;
132b7d5e03cSMatthew Dillon static const int m2_thresh_off         = 127;
133b7d5e03cSMatthew Dillon static const int m2_count_thr_off      =  31;
134b7d5e03cSMatthew Dillon static const int m2_count_thr_low_off  =  63;
135b7d5e03cSMatthew Dillon static const int m1_thresh_low_ext_off = 127;
136b7d5e03cSMatthew Dillon static const int m2_thresh_low_ext_off = 127;
137b7d5e03cSMatthew Dillon static const int m1_thresh_ext_off     = 127;
138b7d5e03cSMatthew Dillon static const int m2_thresh_ext_off     = 127;
139b7d5e03cSMatthew Dillon 
140b7d5e03cSMatthew Dillon void
ar9300_enable_mib_counters(struct ath_hal * ah)141b7d5e03cSMatthew Dillon ar9300_enable_mib_counters(struct ath_hal *ah)
142b7d5e03cSMatthew Dillon {
143b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Enable MIB counters\n", __func__);
144b7d5e03cSMatthew Dillon     /* Clear the mib counters and save them in the stats */
145b7d5e03cSMatthew Dillon     ar9300_update_mib_mac_stats(ah);
146b7d5e03cSMatthew Dillon 
147b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_FILT_OFDM, 0);
148b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_FILT_CCK, 0);
149b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_MIBC,
150b7d5e03cSMatthew Dillon         ~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS) & 0x0f);
151b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
152b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
153b7d5e03cSMatthew Dillon 
154b7d5e03cSMatthew Dillon }
155b7d5e03cSMatthew Dillon 
156b7d5e03cSMatthew Dillon void
ar9300_disable_mib_counters(struct ath_hal * ah)157b7d5e03cSMatthew Dillon ar9300_disable_mib_counters(struct ath_hal *ah)
158b7d5e03cSMatthew Dillon {
159b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Disabling MIB counters\n", __func__);
160b7d5e03cSMatthew Dillon 
161b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_MIBC,  AR_MIBC_FMC | AR_MIBC_CMC);
162b7d5e03cSMatthew Dillon 
163b7d5e03cSMatthew Dillon     /* Clear the mib counters and save them in the stats */
164b7d5e03cSMatthew Dillon     ar9300_update_mib_mac_stats(ah);
165b7d5e03cSMatthew Dillon 
166b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_FILT_OFDM, 0);
167b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_FILT_CCK, 0);
168b7d5e03cSMatthew Dillon }
169b7d5e03cSMatthew Dillon 
170b7d5e03cSMatthew Dillon /*
171b7d5e03cSMatthew Dillon  * This routine returns the index into the ani_state array that
172b7d5e03cSMatthew Dillon  * corresponds to the channel in *chan.  If no match is found and the
173b7d5e03cSMatthew Dillon  * array is still not fully utilized, a new entry is created for the
174b7d5e03cSMatthew Dillon  * channel.  We assume the attach function has already initialized the
175b7d5e03cSMatthew Dillon  * ah_ani values and only the channel field needs to be set.
176b7d5e03cSMatthew Dillon  */
177b7d5e03cSMatthew Dillon static int
ar9300_get_ani_channel_index(struct ath_hal * ah,const struct ieee80211_channel * chan)178b7d5e03cSMatthew Dillon ar9300_get_ani_channel_index(struct ath_hal *ah,
179b7d5e03cSMatthew Dillon   const struct ieee80211_channel *chan)
180b7d5e03cSMatthew Dillon {
181b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
182b7d5e03cSMatthew Dillon     int i;
183b7d5e03cSMatthew Dillon 
184b7d5e03cSMatthew Dillon     for (i = 0; i < ARRAY_LENGTH(ahp->ah_ani); i++) {
185b7d5e03cSMatthew Dillon         /* XXX this doesn't distinguish between 20/40 channels */
186b7d5e03cSMatthew Dillon         if (ahp->ah_ani[i].c.ic_freq == chan->ic_freq) {
187b7d5e03cSMatthew Dillon             return i;
188b7d5e03cSMatthew Dillon         }
189b7d5e03cSMatthew Dillon         if (ahp->ah_ani[i].c.ic_freq == 0) {
190b7d5e03cSMatthew Dillon             ahp->ah_ani[i].c.ic_freq = chan->ic_freq;
191b7d5e03cSMatthew Dillon             ahp->ah_ani[i].c.ic_flags = chan->ic_flags;
192b7d5e03cSMatthew Dillon             return i;
193b7d5e03cSMatthew Dillon         }
194b7d5e03cSMatthew Dillon     }
195b7d5e03cSMatthew Dillon     /* XXX statistic */
196b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
197b7d5e03cSMatthew Dillon         "%s: No more channel states left. Using channel 0\n", __func__);
198b7d5e03cSMatthew Dillon     return 0;        /* XXX gotta return something valid */
199b7d5e03cSMatthew Dillon }
200b7d5e03cSMatthew Dillon 
201b7d5e03cSMatthew Dillon /*
202b7d5e03cSMatthew Dillon  * Return the current ANI state of the channel we're on
203b7d5e03cSMatthew Dillon  */
204b7d5e03cSMatthew Dillon struct ar9300_ani_state *
ar9300_ani_get_current_state(struct ath_hal * ah)205b7d5e03cSMatthew Dillon ar9300_ani_get_current_state(struct ath_hal *ah)
206b7d5e03cSMatthew Dillon {
207b7d5e03cSMatthew Dillon     return AH9300(ah)->ah_curani;
208b7d5e03cSMatthew Dillon }
209b7d5e03cSMatthew Dillon 
210b7d5e03cSMatthew Dillon /*
211b7d5e03cSMatthew Dillon  * Return the current statistics.
212b7d5e03cSMatthew Dillon  */
213a20e5e51SMatthew Dillon HAL_ANI_STATS *
ar9300_ani_get_current_stats(struct ath_hal * ah)214b7d5e03cSMatthew Dillon ar9300_ani_get_current_stats(struct ath_hal *ah)
215b7d5e03cSMatthew Dillon {
216b7d5e03cSMatthew Dillon     return &AH9300(ah)->ah_stats;
217b7d5e03cSMatthew Dillon }
218b7d5e03cSMatthew Dillon 
219b7d5e03cSMatthew Dillon /*
220b7d5e03cSMatthew Dillon  * Setup ANI handling.  Sets all thresholds and levels to default level AND
221b7d5e03cSMatthew Dillon  * resets the channel statistics
222b7d5e03cSMatthew Dillon  */
223b7d5e03cSMatthew Dillon 
224b7d5e03cSMatthew Dillon void
ar9300_ani_attach(struct ath_hal * ah)225b7d5e03cSMatthew Dillon ar9300_ani_attach(struct ath_hal *ah)
226b7d5e03cSMatthew Dillon {
227b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
228b7d5e03cSMatthew Dillon     int i;
229b7d5e03cSMatthew Dillon 
230b7d5e03cSMatthew Dillon     OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani));
231b7d5e03cSMatthew Dillon     for (i = 0; i < ARRAY_LENGTH(ahp->ah_ani); i++) {
232b7d5e03cSMatthew Dillon         ahp->ah_ani[i].ofdm_trig_high = HAL_ANI_OFDM_TRIG_HIGH;
233b7d5e03cSMatthew Dillon         ahp->ah_ani[i].ofdm_trig_low = HAL_ANI_OFDM_TRIG_LOW;
234b7d5e03cSMatthew Dillon         ahp->ah_ani[i].cck_trig_high = HAL_ANI_CCK_TRIG_HIGH;
235b7d5e03cSMatthew Dillon         ahp->ah_ani[i].cck_trig_low = HAL_ANI_CCK_TRIG_LOW;
236b7d5e03cSMatthew Dillon         ahp->ah_ani[i].rssi_thr_high = HAL_ANI_RSSI_THR_HIGH;
237b7d5e03cSMatthew Dillon         ahp->ah_ani[i].rssi_thr_low = HAL_ANI_RSSI_THR_LOW;
238b7d5e03cSMatthew Dillon         ahp->ah_ani[i].ofdm_noise_immunity_level = HAL_ANI_OFDM_DEF_LEVEL;
239b7d5e03cSMatthew Dillon         ahp->ah_ani[i].cck_noise_immunity_level = HAL_ANI_CCK_DEF_LEVEL;
240b7d5e03cSMatthew Dillon         ahp->ah_ani[i].ofdm_weak_sig_detect_off = !HAL_ANI_USE_OFDM_WEAK_SIG;
241b7d5e03cSMatthew Dillon         ahp->ah_ani[i].spur_immunity_level = HAL_ANI_DEF_SPUR_IMMUNE_LVL;
242b7d5e03cSMatthew Dillon         ahp->ah_ani[i].firstep_level = HAL_ANI_DEF_FIRSTEP_LVL;
243b7d5e03cSMatthew Dillon         ahp->ah_ani[i].mrc_cck_off = !HAL_ANI_ENABLE_MRC_CCK;
244b7d5e03cSMatthew Dillon         ahp->ah_ani[i].ofdms_turn = AH_TRUE;
245b7d5e03cSMatthew Dillon         ahp->ah_ani[i].must_restore = AH_FALSE;
246b7d5e03cSMatthew Dillon     }
247b7d5e03cSMatthew Dillon 
248b7d5e03cSMatthew Dillon     /*
249b7d5e03cSMatthew Dillon      * Since we expect some ongoing maintenance on the tables,
250b7d5e03cSMatthew Dillon      * let's sanity check here.
251b7d5e03cSMatthew Dillon      * The default level should not modify INI setting.
252b7d5e03cSMatthew Dillon      */
253b7d5e03cSMatthew Dillon     HALASSERT(firstep_table[HAL_ANI_DEF_FIRSTEP_LVL] == 0);
254b7d5e03cSMatthew Dillon     HALASSERT(cycpwr_thr1_table[HAL_ANI_DEF_SPUR_IMMUNE_LVL] == 0);
255b7d5e03cSMatthew Dillon     HALASSERT(
256b7d5e03cSMatthew Dillon         ofdm_level_table[HAL_ANI_OFDM_DEF_LEVEL].fir_step_level ==
257b7d5e03cSMatthew Dillon         HAL_ANI_DEF_FIRSTEP_LVL);
258b7d5e03cSMatthew Dillon     HALASSERT(
259b7d5e03cSMatthew Dillon         ofdm_level_table[HAL_ANI_OFDM_DEF_LEVEL].spur_immunity_level ==
260b7d5e03cSMatthew Dillon         HAL_ANI_DEF_SPUR_IMMUNE_LVL);
261b7d5e03cSMatthew Dillon     HALASSERT(
262b7d5e03cSMatthew Dillon         cck_level_table[HAL_ANI_CCK_DEF_LEVEL].fir_step_level ==
263b7d5e03cSMatthew Dillon         HAL_ANI_DEF_FIRSTEP_LVL);
264b7d5e03cSMatthew Dillon 
265b7d5e03cSMatthew Dillon     /* Initialize and enable MIB Counters */
266b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_PHY_ERR_1, 0);
267b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_PHY_ERR_2, 0);
268b7d5e03cSMatthew Dillon     ar9300_enable_mib_counters(ah);
269b7d5e03cSMatthew Dillon 
270b7d5e03cSMatthew Dillon     ahp->ah_ani_period = HAL_ANI_PERIOD;
271b7d5e03cSMatthew Dillon     if (ah->ah_config.ath_hal_enable_ani) {
272b7d5e03cSMatthew Dillon         ahp->ah_proc_phy_err |= HAL_PROCESS_ANI;
273b7d5e03cSMatthew Dillon     }
274b7d5e03cSMatthew Dillon }
275b7d5e03cSMatthew Dillon 
276b7d5e03cSMatthew Dillon /*
277b7d5e03cSMatthew Dillon  * Cleanup any ANI state setup.
278b7d5e03cSMatthew Dillon  */
279b7d5e03cSMatthew Dillon void
ar9300_ani_detach(struct ath_hal * ah)280b7d5e03cSMatthew Dillon ar9300_ani_detach(struct ath_hal *ah)
281b7d5e03cSMatthew Dillon {
282b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Detaching Ani\n", __func__);
283b7d5e03cSMatthew Dillon     ar9300_disable_mib_counters(ah);
284b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_PHY_ERR_1, 0);
285b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_PHY_ERR_2, 0);
286b7d5e03cSMatthew Dillon }
287b7d5e03cSMatthew Dillon 
288b7d5e03cSMatthew Dillon /*
289b7d5e03cSMatthew Dillon  * Initialize the ANI register values with default (ini) values.
290b7d5e03cSMatthew Dillon  * This routine is called during a (full) hardware reset after
291b7d5e03cSMatthew Dillon  * all the registers are initialised from the INI.
292b7d5e03cSMatthew Dillon  */
293b7d5e03cSMatthew Dillon void
ar9300_ani_init_defaults(struct ath_hal * ah,HAL_HT_MACMODE macmode)294b7d5e03cSMatthew Dillon ar9300_ani_init_defaults(struct ath_hal *ah, HAL_HT_MACMODE macmode)
295b7d5e03cSMatthew Dillon {
296b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
297b7d5e03cSMatthew Dillon     struct ar9300_ani_state *ani_state;
298b7d5e03cSMatthew Dillon     const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
299b7d5e03cSMatthew Dillon     int index;
300b7d5e03cSMatthew Dillon     u_int32_t val;
301b7d5e03cSMatthew Dillon 
302b7d5e03cSMatthew Dillon     HALASSERT(chan != AH_NULL);
303b7d5e03cSMatthew Dillon     index = ar9300_get_ani_channel_index(ah, chan);
304b7d5e03cSMatthew Dillon     ani_state = &ahp->ah_ani[index];
305b7d5e03cSMatthew Dillon     ahp->ah_curani = ani_state;
306b7d5e03cSMatthew Dillon 
307b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_ANI,
308b7d5e03cSMatthew Dillon         "%s: ver %d.%d opmode %u chan %d Mhz/0x%x macmode %d\n",
309b7d5e03cSMatthew Dillon         __func__, AH_PRIVATE(ah)->ah_macVersion, AH_PRIVATE(ah)->ah_macRev,
310b7d5e03cSMatthew Dillon         AH_PRIVATE(ah)->ah_opmode, chan->ic_freq, chan->ic_flags, macmode);
311b7d5e03cSMatthew Dillon 
312b7d5e03cSMatthew Dillon     val = OS_REG_READ(ah, AR_PHY_SFCORR);
313b7d5e03cSMatthew Dillon     ani_state->ini_def.m1_thresh = MS(val, AR_PHY_SFCORR_M1_THRESH);
314b7d5e03cSMatthew Dillon     ani_state->ini_def.m2_thresh = MS(val, AR_PHY_SFCORR_M2_THRESH);
315b7d5e03cSMatthew Dillon     ani_state->ini_def.m2_count_thr = MS(val, AR_PHY_SFCORR_M2COUNT_THR);
316b7d5e03cSMatthew Dillon 
317b7d5e03cSMatthew Dillon     val = OS_REG_READ(ah, AR_PHY_SFCORR_LOW);
318b7d5e03cSMatthew Dillon     ani_state->ini_def.m1_thresh_low =
319b7d5e03cSMatthew Dillon         MS(val, AR_PHY_SFCORR_LOW_M1_THRESH_LOW);
320b7d5e03cSMatthew Dillon     ani_state->ini_def.m2_thresh_low =
321b7d5e03cSMatthew Dillon         MS(val, AR_PHY_SFCORR_LOW_M2_THRESH_LOW);
322b7d5e03cSMatthew Dillon     ani_state->ini_def.m2_count_thr_low =
323b7d5e03cSMatthew Dillon         MS(val, AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW);
324b7d5e03cSMatthew Dillon 
325b7d5e03cSMatthew Dillon     val = OS_REG_READ(ah, AR_PHY_SFCORR_EXT);
326b7d5e03cSMatthew Dillon     ani_state->ini_def.m1_thresh_ext = MS(val, AR_PHY_SFCORR_EXT_M1_THRESH);
327b7d5e03cSMatthew Dillon     ani_state->ini_def.m2_thresh_ext = MS(val, AR_PHY_SFCORR_EXT_M2_THRESH);
328b7d5e03cSMatthew Dillon     ani_state->ini_def.m1_thresh_low_ext =
329b7d5e03cSMatthew Dillon         MS(val, AR_PHY_SFCORR_EXT_M1_THRESH_LOW);
330b7d5e03cSMatthew Dillon     ani_state->ini_def.m2_thresh_low_ext =
331b7d5e03cSMatthew Dillon         MS(val, AR_PHY_SFCORR_EXT_M2_THRESH_LOW);
332b7d5e03cSMatthew Dillon 
333b7d5e03cSMatthew Dillon     ani_state->ini_def.firstep =
334b7d5e03cSMatthew Dillon         OS_REG_READ_FIELD(ah, AR_PHY_FIND_SIG, AR_PHY_FIND_SIG_FIRSTEP);
335b7d5e03cSMatthew Dillon     ani_state->ini_def.firstep_low =
336b7d5e03cSMatthew Dillon         OS_REG_READ_FIELD(
337b7d5e03cSMatthew Dillon             ah, AR_PHY_FIND_SIG_LOW, AR_PHY_FIND_SIG_LOW_FIRSTEP_LOW);
338b7d5e03cSMatthew Dillon     ani_state->ini_def.cycpwr_thr1 =
339b7d5e03cSMatthew Dillon         OS_REG_READ_FIELD(ah, AR_PHY_TIMING5, AR_PHY_TIMING5_CYCPWR_THR1);
340b7d5e03cSMatthew Dillon     ani_state->ini_def.cycpwr_thr1_ext =
341b7d5e03cSMatthew Dillon         OS_REG_READ_FIELD(ah, AR_PHY_EXT_CCA, AR_PHY_EXT_CYCPWR_THR1);
342b7d5e03cSMatthew Dillon 
343b7d5e03cSMatthew Dillon     /* these levels just got reset to defaults by the INI */
344b7d5e03cSMatthew Dillon     ani_state->spur_immunity_level = HAL_ANI_DEF_SPUR_IMMUNE_LVL;
345b7d5e03cSMatthew Dillon     ani_state->firstep_level = HAL_ANI_DEF_FIRSTEP_LVL;
346b7d5e03cSMatthew Dillon     ani_state->ofdm_weak_sig_detect_off = !HAL_ANI_USE_OFDM_WEAK_SIG;
347b7d5e03cSMatthew Dillon     ani_state->mrc_cck_off = !HAL_ANI_ENABLE_MRC_CCK;
348b7d5e03cSMatthew Dillon 
349b7d5e03cSMatthew Dillon     ani_state->cycle_count = 0;
350b7d5e03cSMatthew Dillon }
351b7d5e03cSMatthew Dillon 
352b7d5e03cSMatthew Dillon /*
353b7d5e03cSMatthew Dillon  * Set the ANI settings to match an OFDM level.
354b7d5e03cSMatthew Dillon  */
355b7d5e03cSMatthew Dillon static void
ar9300_ani_set_odfm_noise_immunity_level(struct ath_hal * ah,u_int8_t ofdm_noise_immunity_level)356b7d5e03cSMatthew Dillon ar9300_ani_set_odfm_noise_immunity_level(struct ath_hal *ah,
357b7d5e03cSMatthew Dillon                                    u_int8_t ofdm_noise_immunity_level)
358b7d5e03cSMatthew Dillon {
359b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
360b7d5e03cSMatthew Dillon     struct ar9300_ani_state *ani_state = ahp->ah_curani;
361b7d5e03cSMatthew Dillon 
362b7d5e03cSMatthew Dillon     ani_state->rssi = BEACON_RSSI(ahp);
363b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_ANI,
364b7d5e03cSMatthew Dillon         "**** %s: ofdmlevel %d=>%d, rssi=%d[lo=%d hi=%d]\n", __func__,
365b7d5e03cSMatthew Dillon         ani_state->ofdm_noise_immunity_level, ofdm_noise_immunity_level,
366b7d5e03cSMatthew Dillon         ani_state->rssi, ani_state->rssi_thr_low, ani_state->rssi_thr_high);
367b7d5e03cSMatthew Dillon 
368b7d5e03cSMatthew Dillon     ani_state->ofdm_noise_immunity_level = ofdm_noise_immunity_level;
369b7d5e03cSMatthew Dillon 
370b7d5e03cSMatthew Dillon     if (ani_state->spur_immunity_level !=
371b7d5e03cSMatthew Dillon         ofdm_level_table[ofdm_noise_immunity_level].spur_immunity_level)
372b7d5e03cSMatthew Dillon     {
373b7d5e03cSMatthew Dillon         ar9300_ani_control(
374b7d5e03cSMatthew Dillon             ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
375b7d5e03cSMatthew Dillon             ofdm_level_table[ofdm_noise_immunity_level].spur_immunity_level);
376b7d5e03cSMatthew Dillon     }
377b7d5e03cSMatthew Dillon 
378b7d5e03cSMatthew Dillon     if (ani_state->firstep_level !=
379b7d5e03cSMatthew Dillon             ofdm_level_table[ofdm_noise_immunity_level].fir_step_level &&
380b7d5e03cSMatthew Dillon         ofdm_level_table[ofdm_noise_immunity_level].fir_step_level >=
381b7d5e03cSMatthew Dillon             cck_level_table[ani_state->cck_noise_immunity_level].fir_step_level)
382b7d5e03cSMatthew Dillon     {
383b7d5e03cSMatthew Dillon         ar9300_ani_control(
384b7d5e03cSMatthew Dillon             ah, HAL_ANI_FIRSTEP_LEVEL,
385b7d5e03cSMatthew Dillon             ofdm_level_table[ofdm_noise_immunity_level].fir_step_level);
386b7d5e03cSMatthew Dillon     }
387b7d5e03cSMatthew Dillon 
388b7d5e03cSMatthew Dillon     if ((AH_PRIVATE(ah)->ah_opmode != HAL_M_STA ||
389b7d5e03cSMatthew Dillon         ani_state->rssi <= ani_state->rssi_thr_high))
390b7d5e03cSMatthew Dillon     {
391b7d5e03cSMatthew Dillon         if (ani_state->ofdm_weak_sig_detect_off) {
392b7d5e03cSMatthew Dillon             /*
393b7d5e03cSMatthew Dillon              * force on ofdm weak sig detect.
394b7d5e03cSMatthew Dillon              */
395b7d5e03cSMatthew Dillon             ar9300_ani_control(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_TRUE);
396b7d5e03cSMatthew Dillon         }
397b7d5e03cSMatthew Dillon     } else if (ani_state->ofdm_weak_sig_detect_off ==
398b7d5e03cSMatthew Dillon                ofdm_level_table[ofdm_noise_immunity_level].ofdm_weak_signal_on)
399b7d5e03cSMatthew Dillon     {
400b7d5e03cSMatthew Dillon         ar9300_ani_control(
401b7d5e03cSMatthew Dillon             ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
402b7d5e03cSMatthew Dillon             ofdm_level_table[ofdm_noise_immunity_level].ofdm_weak_signal_on);
403b7d5e03cSMatthew Dillon     }
404b7d5e03cSMatthew Dillon }
405b7d5e03cSMatthew Dillon 
406b7d5e03cSMatthew Dillon /*
407b7d5e03cSMatthew Dillon  * Set the ANI settings to match a CCK level.
408b7d5e03cSMatthew Dillon  */
409b7d5e03cSMatthew Dillon static void
ar9300_ani_set_cck_noise_immunity_level(struct ath_hal * ah,u_int8_t cck_noise_immunity_level)410b7d5e03cSMatthew Dillon ar9300_ani_set_cck_noise_immunity_level(struct ath_hal *ah,
411b7d5e03cSMatthew Dillon                                   u_int8_t cck_noise_immunity_level)
412b7d5e03cSMatthew Dillon {
413b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
414b7d5e03cSMatthew Dillon     struct ar9300_ani_state *ani_state = ahp->ah_curani;
415b7d5e03cSMatthew Dillon     int level;
416b7d5e03cSMatthew Dillon 
417b7d5e03cSMatthew Dillon     ani_state->rssi = BEACON_RSSI(ahp);
418b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_ANI,
419b7d5e03cSMatthew Dillon         "**** %s: ccklevel %d=>%d, rssi=%d[lo=%d hi=%d]\n",
420b7d5e03cSMatthew Dillon         __func__, ani_state->cck_noise_immunity_level, cck_noise_immunity_level,
421b7d5e03cSMatthew Dillon         ani_state->rssi, ani_state->rssi_thr_low, ani_state->rssi_thr_high);
422b7d5e03cSMatthew Dillon 
423b7d5e03cSMatthew Dillon     if (AH_PRIVATE(ah)->ah_opmode == HAL_M_STA &&
424b7d5e03cSMatthew Dillon         ani_state->rssi <= ani_state->rssi_thr_low &&
425b7d5e03cSMatthew Dillon         cck_noise_immunity_level > HAL_ANI_CCK_MAX_LEVEL_LOW_RSSI)
426b7d5e03cSMatthew Dillon     {
427b7d5e03cSMatthew Dillon         cck_noise_immunity_level = HAL_ANI_CCK_MAX_LEVEL_LOW_RSSI;
428b7d5e03cSMatthew Dillon     }
429b7d5e03cSMatthew Dillon 
430b7d5e03cSMatthew Dillon     ani_state->cck_noise_immunity_level = cck_noise_immunity_level;
431b7d5e03cSMatthew Dillon 
432b7d5e03cSMatthew Dillon     level = ani_state->ofdm_noise_immunity_level;
433b7d5e03cSMatthew Dillon     if (ani_state->firstep_level !=
434b7d5e03cSMatthew Dillon             cck_level_table[cck_noise_immunity_level].fir_step_level &&
435b7d5e03cSMatthew Dillon         cck_level_table[cck_noise_immunity_level].fir_step_level >=
436b7d5e03cSMatthew Dillon             ofdm_level_table[level].fir_step_level)
437b7d5e03cSMatthew Dillon     {
438b7d5e03cSMatthew Dillon         ar9300_ani_control(
439b7d5e03cSMatthew Dillon             ah, HAL_ANI_FIRSTEP_LEVEL,
440b7d5e03cSMatthew Dillon             cck_level_table[cck_noise_immunity_level].fir_step_level);
441b7d5e03cSMatthew Dillon     }
442b7d5e03cSMatthew Dillon 
443b7d5e03cSMatthew Dillon     if (ani_state->mrc_cck_off ==
444b7d5e03cSMatthew Dillon         cck_level_table[cck_noise_immunity_level].mrc_cck_on)
445b7d5e03cSMatthew Dillon     {
446b7d5e03cSMatthew Dillon         ar9300_ani_control(
447b7d5e03cSMatthew Dillon             ah, HAL_ANI_MRC_CCK,
448b7d5e03cSMatthew Dillon             cck_level_table[cck_noise_immunity_level].mrc_cck_on);
449b7d5e03cSMatthew Dillon     }
450b7d5e03cSMatthew Dillon }
451b7d5e03cSMatthew Dillon 
452b7d5e03cSMatthew Dillon /*
453b7d5e03cSMatthew Dillon  * Control Adaptive Noise Immunity Parameters
454b7d5e03cSMatthew Dillon  */
455b7d5e03cSMatthew Dillon HAL_BOOL
ar9300_ani_control(struct ath_hal * ah,HAL_ANI_CMD cmd,int param)456b7d5e03cSMatthew Dillon ar9300_ani_control(struct ath_hal *ah, HAL_ANI_CMD cmd, int param)
457b7d5e03cSMatthew Dillon {
458b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
459b7d5e03cSMatthew Dillon     struct ar9300_ani_state *ani_state = ahp->ah_curani;
460b7d5e03cSMatthew Dillon     const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
461b7d5e03cSMatthew Dillon     int32_t value, value2;
462b7d5e03cSMatthew Dillon     u_int level = param;
463b7d5e03cSMatthew Dillon     u_int is_on;
464b7d5e03cSMatthew Dillon 
465b7d5e03cSMatthew Dillon     if (chan == NULL && cmd != HAL_ANI_MODE) {
466b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
467b7d5e03cSMatthew Dillon             "%s: ignoring cmd 0x%02x - no channel\n", __func__, cmd);
468b7d5e03cSMatthew Dillon         return AH_FALSE;
469b7d5e03cSMatthew Dillon     }
470b7d5e03cSMatthew Dillon 
471b7d5e03cSMatthew Dillon     switch (cmd & ahp->ah_ani_function) {
472b7d5e03cSMatthew Dillon     case HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION:
473b7d5e03cSMatthew Dillon         {
474b7d5e03cSMatthew Dillon             int m1_thresh_low, m2_thresh_low;
475b7d5e03cSMatthew Dillon             int m1_thresh, m2_thresh;
476b7d5e03cSMatthew Dillon             int m2_count_thr, m2_count_thr_low;
477b7d5e03cSMatthew Dillon             int m1_thresh_low_ext, m2_thresh_low_ext;
478b7d5e03cSMatthew Dillon             int m1_thresh_ext, m2_thresh_ext;
479b7d5e03cSMatthew Dillon             /*
480b7d5e03cSMatthew Dillon              * is_on == 1 means ofdm weak signal detection is ON
481b7d5e03cSMatthew Dillon              * (default, less noise imm)
482b7d5e03cSMatthew Dillon              * is_on == 0 means ofdm weak signal detection is OFF
483b7d5e03cSMatthew Dillon              * (more noise imm)
484b7d5e03cSMatthew Dillon              */
485b7d5e03cSMatthew Dillon             is_on = param ? 1 : 0;
486b7d5e03cSMatthew Dillon 
487848b370cSMatthew Dillon             if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah))
488848b370cSMatthew Dillon                 goto skip_ws_det;
489848b370cSMatthew Dillon 
490b7d5e03cSMatthew Dillon             /*
491b7d5e03cSMatthew Dillon              * make register setting for default (weak sig detect ON)
492b7d5e03cSMatthew Dillon              * come from INI file
493b7d5e03cSMatthew Dillon              */
494b7d5e03cSMatthew Dillon             m1_thresh_low    = is_on ?
495b7d5e03cSMatthew Dillon                 ani_state->ini_def.m1_thresh_low    : m1_thresh_low_off;
496b7d5e03cSMatthew Dillon             m2_thresh_low    = is_on ?
497b7d5e03cSMatthew Dillon                 ani_state->ini_def.m2_thresh_low    : m2_thresh_low_off;
498b7d5e03cSMatthew Dillon             m1_thresh       = is_on ?
499b7d5e03cSMatthew Dillon                 ani_state->ini_def.m1_thresh       : m1_thresh_off;
500b7d5e03cSMatthew Dillon             m2_thresh       = is_on ?
501b7d5e03cSMatthew Dillon                 ani_state->ini_def.m2_thresh       : m2_thresh_off;
502b7d5e03cSMatthew Dillon             m2_count_thr     = is_on ?
503b7d5e03cSMatthew Dillon                 ani_state->ini_def.m2_count_thr     : m2_count_thr_off;
504b7d5e03cSMatthew Dillon             m2_count_thr_low  = is_on ?
505b7d5e03cSMatthew Dillon                 ani_state->ini_def.m2_count_thr_low  : m2_count_thr_low_off;
506b7d5e03cSMatthew Dillon             m1_thresh_low_ext = is_on ?
507b7d5e03cSMatthew Dillon                 ani_state->ini_def.m1_thresh_low_ext : m1_thresh_low_ext_off;
508b7d5e03cSMatthew Dillon             m2_thresh_low_ext = is_on ?
509b7d5e03cSMatthew Dillon                 ani_state->ini_def.m2_thresh_low_ext : m2_thresh_low_ext_off;
510b7d5e03cSMatthew Dillon             m1_thresh_ext    = is_on ?
511b7d5e03cSMatthew Dillon                 ani_state->ini_def.m1_thresh_ext    : m1_thresh_ext_off;
512b7d5e03cSMatthew Dillon             m2_thresh_ext    = is_on ?
513b7d5e03cSMatthew Dillon                 ani_state->ini_def.m2_thresh_ext    : m2_thresh_ext_off;
514b7d5e03cSMatthew Dillon             OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
515b7d5e03cSMatthew Dillon                 AR_PHY_SFCORR_LOW_M1_THRESH_LOW, m1_thresh_low);
516b7d5e03cSMatthew Dillon             OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
517b7d5e03cSMatthew Dillon                 AR_PHY_SFCORR_LOW_M2_THRESH_LOW, m2_thresh_low);
518b7d5e03cSMatthew Dillon             OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M1_THRESH,
519b7d5e03cSMatthew Dillon                 m1_thresh);
520b7d5e03cSMatthew Dillon             OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M2_THRESH,
521b7d5e03cSMatthew Dillon                 m2_thresh);
522b7d5e03cSMatthew Dillon             OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M2COUNT_THR,
523b7d5e03cSMatthew Dillon                 m2_count_thr);
524b7d5e03cSMatthew Dillon             OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
525b7d5e03cSMatthew Dillon                 AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, m2_count_thr_low);
526b7d5e03cSMatthew Dillon             OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
527b7d5e03cSMatthew Dillon                 AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1_thresh_low_ext);
528b7d5e03cSMatthew Dillon             OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
529b7d5e03cSMatthew Dillon                 AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2_thresh_low_ext);
530b7d5e03cSMatthew Dillon             OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_M1_THRESH,
531b7d5e03cSMatthew Dillon                 m1_thresh_ext);
532b7d5e03cSMatthew Dillon             OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_M2_THRESH,
533b7d5e03cSMatthew Dillon                 m2_thresh_ext);
534848b370cSMatthew Dillon skip_ws_det:
535b7d5e03cSMatthew Dillon             if (is_on) {
536b7d5e03cSMatthew Dillon                 OS_REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
537b7d5e03cSMatthew Dillon                     AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
538b7d5e03cSMatthew Dillon             } else {
539b7d5e03cSMatthew Dillon                 OS_REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW,
540b7d5e03cSMatthew Dillon                     AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
541b7d5e03cSMatthew Dillon             }
542a20e5e51SMatthew Dillon             if ((!is_on) != ani_state->ofdm_weak_sig_detect_off) {
543b7d5e03cSMatthew Dillon                 HALDEBUG(ah, HAL_DEBUG_ANI,
544b7d5e03cSMatthew Dillon                     "%s: ** ch %d: ofdm weak signal: %s=>%s\n",
545b7d5e03cSMatthew Dillon                     __func__, chan->ic_freq,
546b7d5e03cSMatthew Dillon                     !ani_state->ofdm_weak_sig_detect_off ? "on" : "off",
547b7d5e03cSMatthew Dillon                     is_on ? "on" : "off");
548b7d5e03cSMatthew Dillon                 if (is_on) {
549b7d5e03cSMatthew Dillon                     ahp->ah_stats.ast_ani_ofdmon++;
550b7d5e03cSMatthew Dillon                 } else {
551b7d5e03cSMatthew Dillon                     ahp->ah_stats.ast_ani_ofdmoff++;
552b7d5e03cSMatthew Dillon                 }
553b7d5e03cSMatthew Dillon                 ani_state->ofdm_weak_sig_detect_off = !is_on;
554b7d5e03cSMatthew Dillon             }
555b7d5e03cSMatthew Dillon             break;
556b7d5e03cSMatthew Dillon         }
557b7d5e03cSMatthew Dillon     case HAL_ANI_FIRSTEP_LEVEL:
558b7d5e03cSMatthew Dillon         if (level >= ARRAY_LENGTH(firstep_table)) {
559b7d5e03cSMatthew Dillon             HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
560b7d5e03cSMatthew Dillon                 "%s: HAL_ANI_FIRSTEP_LEVEL level out of range (%u > %u)\n",
561b7d5e03cSMatthew Dillon                 __func__, level, (unsigned) ARRAY_LENGTH(firstep_table));
562b7d5e03cSMatthew Dillon             return AH_FALSE;
563b7d5e03cSMatthew Dillon         }
564b7d5e03cSMatthew Dillon         /*
565b7d5e03cSMatthew Dillon          * make register setting relative to default
566b7d5e03cSMatthew Dillon          * from INI file & cap value
567b7d5e03cSMatthew Dillon          */
568b7d5e03cSMatthew Dillon         value =
569b7d5e03cSMatthew Dillon             firstep_table[level] -
570b7d5e03cSMatthew Dillon             firstep_table[HAL_ANI_DEF_FIRSTEP_LVL] +
571b7d5e03cSMatthew Dillon             ani_state->ini_def.firstep;
572b7d5e03cSMatthew Dillon         if (value < HAL_SIG_FIRSTEP_SETTING_MIN) {
573b7d5e03cSMatthew Dillon             value = HAL_SIG_FIRSTEP_SETTING_MIN;
574b7d5e03cSMatthew Dillon         }
575b7d5e03cSMatthew Dillon         if (value > HAL_SIG_FIRSTEP_SETTING_MAX) {
576b7d5e03cSMatthew Dillon             value = HAL_SIG_FIRSTEP_SETTING_MAX;
577b7d5e03cSMatthew Dillon         }
578b7d5e03cSMatthew Dillon         OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, AR_PHY_FIND_SIG_FIRSTEP, value);
579b7d5e03cSMatthew Dillon         /*
580b7d5e03cSMatthew Dillon          * we need to set first step low register too
581b7d5e03cSMatthew Dillon          * make register setting relative to default from INI file & cap value
582b7d5e03cSMatthew Dillon          */
583b7d5e03cSMatthew Dillon         value2 =
584b7d5e03cSMatthew Dillon             firstep_table[level] -
585b7d5e03cSMatthew Dillon             firstep_table[HAL_ANI_DEF_FIRSTEP_LVL] +
586b7d5e03cSMatthew Dillon             ani_state->ini_def.firstep_low;
587b7d5e03cSMatthew Dillon         if (value2 < HAL_SIG_FIRSTEP_SETTING_MIN) {
588b7d5e03cSMatthew Dillon             value2 = HAL_SIG_FIRSTEP_SETTING_MIN;
589b7d5e03cSMatthew Dillon         }
590b7d5e03cSMatthew Dillon         if (value2 > HAL_SIG_FIRSTEP_SETTING_MAX) {
591b7d5e03cSMatthew Dillon             value2 = HAL_SIG_FIRSTEP_SETTING_MAX;
592b7d5e03cSMatthew Dillon         }
593b7d5e03cSMatthew Dillon         OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG_LOW,
594b7d5e03cSMatthew Dillon             AR_PHY_FIND_SIG_LOW_FIRSTEP_LOW, value2);
595b7d5e03cSMatthew Dillon 
596b7d5e03cSMatthew Dillon         if (level != ani_state->firstep_level) {
597b7d5e03cSMatthew Dillon             HALDEBUG(ah, HAL_DEBUG_ANI,
598b7d5e03cSMatthew Dillon                 "%s: ** ch %d: level %d=>%d[def:%d] firstep[level]=%d ini=%d\n",
599b7d5e03cSMatthew Dillon                 __func__, chan->ic_freq, ani_state->firstep_level, level,
600b7d5e03cSMatthew Dillon                 HAL_ANI_DEF_FIRSTEP_LVL, value, ani_state->ini_def.firstep);
601b7d5e03cSMatthew Dillon             HALDEBUG(ah, HAL_DEBUG_ANI,
602b7d5e03cSMatthew Dillon                 "%s: ** ch %d: level %d=>%d[def:%d] "
603b7d5e03cSMatthew Dillon                 "firstep_low[level]=%d ini=%d\n",
604b7d5e03cSMatthew Dillon                 __func__, chan->ic_freq, ani_state->firstep_level, level,
605b7d5e03cSMatthew Dillon                 HAL_ANI_DEF_FIRSTEP_LVL, value2,
606b7d5e03cSMatthew Dillon                 ani_state->ini_def.firstep_low);
607b7d5e03cSMatthew Dillon             if (level > ani_state->firstep_level) {
608b7d5e03cSMatthew Dillon                 ahp->ah_stats.ast_ani_stepup++;
609b7d5e03cSMatthew Dillon             } else if (level < ani_state->firstep_level) {
610b7d5e03cSMatthew Dillon                 ahp->ah_stats.ast_ani_stepdown++;
611b7d5e03cSMatthew Dillon             }
612b7d5e03cSMatthew Dillon             ani_state->firstep_level = level;
613b7d5e03cSMatthew Dillon         }
614b7d5e03cSMatthew Dillon         break;
615b7d5e03cSMatthew Dillon     case HAL_ANI_SPUR_IMMUNITY_LEVEL:
616b7d5e03cSMatthew Dillon         if (level >= ARRAY_LENGTH(cycpwr_thr1_table)) {
617b7d5e03cSMatthew Dillon             HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
618b7d5e03cSMatthew Dillon                 "%s: HAL_ANI_SPUR_IMMUNITY_LEVEL level "
619b7d5e03cSMatthew Dillon                 "out of range (%u > %u)\n",
620b7d5e03cSMatthew Dillon                 __func__, level, (unsigned) ARRAY_LENGTH(cycpwr_thr1_table));
621b7d5e03cSMatthew Dillon             return AH_FALSE;
622b7d5e03cSMatthew Dillon         }
623b7d5e03cSMatthew Dillon         /*
624b7d5e03cSMatthew Dillon          * make register setting relative to default from INI file & cap value
625b7d5e03cSMatthew Dillon          */
626b7d5e03cSMatthew Dillon         value =
627b7d5e03cSMatthew Dillon             cycpwr_thr1_table[level] -
628b7d5e03cSMatthew Dillon             cycpwr_thr1_table[HAL_ANI_DEF_SPUR_IMMUNE_LVL] +
629b7d5e03cSMatthew Dillon             ani_state->ini_def.cycpwr_thr1;
630b7d5e03cSMatthew Dillon         if (value < HAL_SIG_SPUR_IMM_SETTING_MIN) {
631b7d5e03cSMatthew Dillon             value = HAL_SIG_SPUR_IMM_SETTING_MIN;
632b7d5e03cSMatthew Dillon         }
633b7d5e03cSMatthew Dillon         if (value > HAL_SIG_SPUR_IMM_SETTING_MAX) {
634b7d5e03cSMatthew Dillon             value = HAL_SIG_SPUR_IMM_SETTING_MAX;
635b7d5e03cSMatthew Dillon         }
636b7d5e03cSMatthew Dillon         OS_REG_RMW_FIELD(ah, AR_PHY_TIMING5, AR_PHY_TIMING5_CYCPWR_THR1, value);
637b7d5e03cSMatthew Dillon 
638b7d5e03cSMatthew Dillon         /*
639b7d5e03cSMatthew Dillon          * set AR_PHY_EXT_CCA for extension channel
640b7d5e03cSMatthew Dillon          * make register setting relative to default from INI file & cap value
641b7d5e03cSMatthew Dillon          */
642b7d5e03cSMatthew Dillon         value2 =
643b7d5e03cSMatthew Dillon             cycpwr_thr1_table[level] -
644b7d5e03cSMatthew Dillon             cycpwr_thr1_table[HAL_ANI_DEF_SPUR_IMMUNE_LVL] +
645b7d5e03cSMatthew Dillon             ani_state->ini_def.cycpwr_thr1_ext;
646b7d5e03cSMatthew Dillon         if (value2 < HAL_SIG_SPUR_IMM_SETTING_MIN) {
647b7d5e03cSMatthew Dillon             value2 = HAL_SIG_SPUR_IMM_SETTING_MIN;
648b7d5e03cSMatthew Dillon         }
649b7d5e03cSMatthew Dillon         if (value2 > HAL_SIG_SPUR_IMM_SETTING_MAX) {
650b7d5e03cSMatthew Dillon             value2 = HAL_SIG_SPUR_IMM_SETTING_MAX;
651b7d5e03cSMatthew Dillon         }
652b7d5e03cSMatthew Dillon         OS_REG_RMW_FIELD(ah, AR_PHY_EXT_CCA, AR_PHY_EXT_CYCPWR_THR1, value2);
653b7d5e03cSMatthew Dillon 
654b7d5e03cSMatthew Dillon         if (level != ani_state->spur_immunity_level) {
655b7d5e03cSMatthew Dillon             HALDEBUG(ah, HAL_DEBUG_ANI,
656b7d5e03cSMatthew Dillon                 "%s: ** ch %d: level %d=>%d[def:%d] "
657b7d5e03cSMatthew Dillon                 "cycpwr_thr1[level]=%d ini=%d\n",
658b7d5e03cSMatthew Dillon                 __func__, chan->ic_freq, ani_state->spur_immunity_level, level,
659b7d5e03cSMatthew Dillon                 HAL_ANI_DEF_SPUR_IMMUNE_LVL, value,
660b7d5e03cSMatthew Dillon                 ani_state->ini_def.cycpwr_thr1);
661b7d5e03cSMatthew Dillon             HALDEBUG(ah, HAL_DEBUG_ANI,
662b7d5e03cSMatthew Dillon                 "%s: ** ch %d: level %d=>%d[def:%d] "
663b7d5e03cSMatthew Dillon                 "cycpwr_thr1_ext[level]=%d ini=%d\n",
664b7d5e03cSMatthew Dillon                 __func__, chan->ic_freq, ani_state->spur_immunity_level, level,
665b7d5e03cSMatthew Dillon                 HAL_ANI_DEF_SPUR_IMMUNE_LVL, value2,
666b7d5e03cSMatthew Dillon                 ani_state->ini_def.cycpwr_thr1_ext);
667b7d5e03cSMatthew Dillon             if (level > ani_state->spur_immunity_level) {
668b7d5e03cSMatthew Dillon                 ahp->ah_stats.ast_ani_spurup++;
669b7d5e03cSMatthew Dillon             } else if (level < ani_state->spur_immunity_level) {
670b7d5e03cSMatthew Dillon                 ahp->ah_stats.ast_ani_spurdown++;
671b7d5e03cSMatthew Dillon             }
672b7d5e03cSMatthew Dillon             ani_state->spur_immunity_level = level;
673b7d5e03cSMatthew Dillon         }
674b7d5e03cSMatthew Dillon         break;
675b7d5e03cSMatthew Dillon     case HAL_ANI_MRC_CCK:
676b7d5e03cSMatthew Dillon         /*
677b7d5e03cSMatthew Dillon          * is_on == 1 means MRC CCK ON (default, less noise imm)
678b7d5e03cSMatthew Dillon          * is_on == 0 means MRC CCK is OFF (more noise imm)
679b7d5e03cSMatthew Dillon          */
680b7d5e03cSMatthew Dillon         is_on = param ? 1 : 0;
681b7d5e03cSMatthew Dillon         if (!AR_SREV_POSEIDON(ah)) {
682b7d5e03cSMatthew Dillon             OS_REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL,
683b7d5e03cSMatthew Dillon                 AR_PHY_MRC_CCK_ENABLE, is_on);
684b7d5e03cSMatthew Dillon             OS_REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL,
685b7d5e03cSMatthew Dillon                 AR_PHY_MRC_CCK_MUX_REG, is_on);
686b7d5e03cSMatthew Dillon         }
687a20e5e51SMatthew Dillon         if ((!is_on) != ani_state->mrc_cck_off) {
688b7d5e03cSMatthew Dillon             HALDEBUG(ah, HAL_DEBUG_ANI,
689b7d5e03cSMatthew Dillon                 "%s: ** ch %d: MRC CCK: %s=>%s\n", __func__, chan->ic_freq,
690b7d5e03cSMatthew Dillon                 !ani_state->mrc_cck_off ? "on" : "off", is_on ? "on" : "off");
691b7d5e03cSMatthew Dillon             if (is_on) {
692b7d5e03cSMatthew Dillon                 ahp->ah_stats.ast_ani_ccklow++;
693b7d5e03cSMatthew Dillon             } else {
694b7d5e03cSMatthew Dillon                 ahp->ah_stats.ast_ani_cckhigh++;
695b7d5e03cSMatthew Dillon             }
696b7d5e03cSMatthew Dillon             ani_state->mrc_cck_off = !is_on;
697b7d5e03cSMatthew Dillon         }
698b7d5e03cSMatthew Dillon         break;
699b7d5e03cSMatthew Dillon     case HAL_ANI_PRESENT:
700b7d5e03cSMatthew Dillon         break;
701b7d5e03cSMatthew Dillon #ifdef AH_PRIVATE_DIAG
702b7d5e03cSMatthew Dillon     case HAL_ANI_MODE:
703b7d5e03cSMatthew Dillon         if (param == 0) {
704b7d5e03cSMatthew Dillon             ahp->ah_proc_phy_err &= ~HAL_PROCESS_ANI;
705b7d5e03cSMatthew Dillon             /* Turn off HW counters if we have them */
706b7d5e03cSMatthew Dillon             ar9300_ani_detach(ah);
707b7d5e03cSMatthew Dillon             if (AH_PRIVATE(ah)->ah_curchan == NULL) {
708b7d5e03cSMatthew Dillon                 return AH_TRUE;
709b7d5e03cSMatthew Dillon             }
710b7d5e03cSMatthew Dillon             /* if we're turning off ANI, reset regs back to INI settings */
711b7d5e03cSMatthew Dillon             if (ah->ah_config.ath_hal_enable_ani) {
712b7d5e03cSMatthew Dillon                 HAL_ANI_CMD savefunc = ahp->ah_ani_function;
713b7d5e03cSMatthew Dillon                 /* temporarly allow all functions so we can reset */
714b7d5e03cSMatthew Dillon                 ahp->ah_ani_function = HAL_ANI_ALL;
715b7d5e03cSMatthew Dillon                 HALDEBUG(ah, HAL_DEBUG_ANI,
716b7d5e03cSMatthew Dillon                     "%s: disable all ANI functions\n", __func__);
717b7d5e03cSMatthew Dillon                 ar9300_ani_set_odfm_noise_immunity_level(
718b7d5e03cSMatthew Dillon                     ah, HAL_ANI_OFDM_DEF_LEVEL);
719b7d5e03cSMatthew Dillon                 ar9300_ani_set_cck_noise_immunity_level(
720b7d5e03cSMatthew Dillon                     ah, HAL_ANI_CCK_DEF_LEVEL);
721b7d5e03cSMatthew Dillon                 ahp->ah_ani_function = savefunc;
722b7d5e03cSMatthew Dillon             }
723b7d5e03cSMatthew Dillon         } else {            /* normal/auto mode */
724b7d5e03cSMatthew Dillon             HALDEBUG(ah, HAL_DEBUG_ANI, "%s: enabled\n", __func__);
725b7d5e03cSMatthew Dillon             ahp->ah_proc_phy_err |= HAL_PROCESS_ANI;
726b7d5e03cSMatthew Dillon             if (AH_PRIVATE(ah)->ah_curchan == NULL) {
727b7d5e03cSMatthew Dillon                 return AH_TRUE;
728b7d5e03cSMatthew Dillon             }
729b7d5e03cSMatthew Dillon             ar9300_enable_mib_counters(ah);
730b7d5e03cSMatthew Dillon             ar9300_ani_reset(ah, AH_FALSE);
731b7d5e03cSMatthew Dillon             ani_state = ahp->ah_curani;
732b7d5e03cSMatthew Dillon         }
733b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_ANI, "5 ANC: ahp->ah_proc_phy_err %x \n",
734b7d5e03cSMatthew Dillon                  ahp->ah_proc_phy_err);
735b7d5e03cSMatthew Dillon         break;
736b7d5e03cSMatthew Dillon     case HAL_ANI_PHYERR_RESET:
737b7d5e03cSMatthew Dillon         ahp->ah_stats.ast_ani_ofdmerrs = 0;
738b7d5e03cSMatthew Dillon         ahp->ah_stats.ast_ani_cckerrs = 0;
739b7d5e03cSMatthew Dillon         break;
740b7d5e03cSMatthew Dillon #endif /* AH_PRIVATE_DIAG */
741b7d5e03cSMatthew Dillon     default:
742b7d5e03cSMatthew Dillon #if HAL_ANI_DEBUG
743b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_ANI,
744b7d5e03cSMatthew Dillon             "%s: invalid cmd 0x%02x (allowed=0x%02x)\n",
745b7d5e03cSMatthew Dillon             __func__, cmd, ahp->ah_ani_function);
746b7d5e03cSMatthew Dillon #endif
747b7d5e03cSMatthew Dillon         return AH_FALSE;
748b7d5e03cSMatthew Dillon     }
749b7d5e03cSMatthew Dillon 
750b7d5e03cSMatthew Dillon #if HAL_ANI_DEBUG
751b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_ANI,
752b7d5e03cSMatthew Dillon         "%s: ANI parameters: SI=%d, ofdm_ws=%s FS=%d MRCcck=%s listen_time=%d "
753b7d5e03cSMatthew Dillon         "CC=%d listen=%d ofdm_errs=%d cck_errs=%d\n",
754b7d5e03cSMatthew Dillon         __func__, ani_state->spur_immunity_level,
755b7d5e03cSMatthew Dillon         !ani_state->ofdm_weak_sig_detect_off ? "on" : "off",
756b7d5e03cSMatthew Dillon         ani_state->firstep_level, !ani_state->mrc_cck_off ? "on" : "off",
757b7d5e03cSMatthew Dillon         ani_state->listen_time, ani_state->cycle_count,
758b7d5e03cSMatthew Dillon         ani_state->listen_time, ani_state->ofdm_phy_err_count,
759b7d5e03cSMatthew Dillon         ani_state->cck_phy_err_count);
760b7d5e03cSMatthew Dillon #endif
761b7d5e03cSMatthew Dillon 
762b7d5e03cSMatthew Dillon #ifndef REMOVE_PKT_LOG
763b7d5e03cSMatthew Dillon     /* do pktlog */
764b7d5e03cSMatthew Dillon     {
765b7d5e03cSMatthew Dillon         struct log_ani log_data;
766b7d5e03cSMatthew Dillon 
767b7d5e03cSMatthew Dillon         /* Populate the ani log record */
768b7d5e03cSMatthew Dillon         log_data.phy_stats_disable = DO_ANI(ah);
769b7d5e03cSMatthew Dillon         log_data.noise_immun_lvl = ani_state->ofdm_noise_immunity_level;
770b7d5e03cSMatthew Dillon         log_data.spur_immun_lvl = ani_state->spur_immunity_level;
771b7d5e03cSMatthew Dillon         log_data.ofdm_weak_det = ani_state->ofdm_weak_sig_detect_off;
772b7d5e03cSMatthew Dillon         log_data.cck_weak_thr = ani_state->cck_noise_immunity_level;
773b7d5e03cSMatthew Dillon         log_data.fir_lvl = ani_state->firstep_level;
774b7d5e03cSMatthew Dillon         log_data.listen_time = ani_state->listen_time;
775b7d5e03cSMatthew Dillon         log_data.cycle_count = ani_state->cycle_count;
776b7d5e03cSMatthew Dillon         /* express ofdm_phy_err_count as errors/second */
777b7d5e03cSMatthew Dillon         log_data.ofdm_phy_err_count = ani_state->listen_time ?
778b7d5e03cSMatthew Dillon             ani_state->ofdm_phy_err_count * 1000 / ani_state->listen_time : 0;
779b7d5e03cSMatthew Dillon         /* express cck_phy_err_count as errors/second */
780b7d5e03cSMatthew Dillon         log_data.cck_phy_err_count =  ani_state->listen_time ?
781b7d5e03cSMatthew Dillon             ani_state->cck_phy_err_count * 1000 / ani_state->listen_time  : 0;
782b7d5e03cSMatthew Dillon         log_data.rssi = ani_state->rssi;
783b7d5e03cSMatthew Dillon 
784b7d5e03cSMatthew Dillon         /* clear interrupt context flag */
785b7d5e03cSMatthew Dillon         ath_hal_log_ani(AH_PRIVATE(ah)->ah_sc, &log_data, 0);
786b7d5e03cSMatthew Dillon     }
787b7d5e03cSMatthew Dillon #endif
788b7d5e03cSMatthew Dillon 
789b7d5e03cSMatthew Dillon     return AH_TRUE;
790b7d5e03cSMatthew Dillon }
791b7d5e03cSMatthew Dillon 
792b7d5e03cSMatthew Dillon static void
ar9300_ani_restart(struct ath_hal * ah)793b7d5e03cSMatthew Dillon ar9300_ani_restart(struct ath_hal *ah)
794b7d5e03cSMatthew Dillon {
795b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
796b7d5e03cSMatthew Dillon     struct ar9300_ani_state *ani_state;
797b7d5e03cSMatthew Dillon 
798b7d5e03cSMatthew Dillon     if (!DO_ANI(ah)) {
799b7d5e03cSMatthew Dillon         return;
800b7d5e03cSMatthew Dillon     }
801b7d5e03cSMatthew Dillon 
802b7d5e03cSMatthew Dillon     ani_state = ahp->ah_curani;
803b7d5e03cSMatthew Dillon 
804b7d5e03cSMatthew Dillon     ani_state->listen_time = 0;
805b7d5e03cSMatthew Dillon 
806b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_PHY_ERR_1, 0);
807b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_PHY_ERR_2, 0);
808b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
809b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
810b7d5e03cSMatthew Dillon 
811b7d5e03cSMatthew Dillon     /* Clear the mib counters and save them in the stats */
812b7d5e03cSMatthew Dillon     ar9300_update_mib_mac_stats(ah);
813b7d5e03cSMatthew Dillon 
814b7d5e03cSMatthew Dillon     ani_state->ofdm_phy_err_count = 0;
815b7d5e03cSMatthew Dillon     ani_state->cck_phy_err_count = 0;
816b7d5e03cSMatthew Dillon }
817b7d5e03cSMatthew Dillon 
818b7d5e03cSMatthew Dillon static void
ar9300_ani_ofdm_err_trigger(struct ath_hal * ah)819b7d5e03cSMatthew Dillon ar9300_ani_ofdm_err_trigger(struct ath_hal *ah)
820b7d5e03cSMatthew Dillon {
821b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
822b7d5e03cSMatthew Dillon     struct ar9300_ani_state *ani_state;
823b7d5e03cSMatthew Dillon 
824b7d5e03cSMatthew Dillon     if (!DO_ANI(ah)) {
825b7d5e03cSMatthew Dillon         return;
826b7d5e03cSMatthew Dillon     }
827b7d5e03cSMatthew Dillon 
828b7d5e03cSMatthew Dillon     ani_state = ahp->ah_curani;
829b7d5e03cSMatthew Dillon 
830b7d5e03cSMatthew Dillon     if (ani_state->ofdm_noise_immunity_level < HAL_ANI_OFDM_MAX_LEVEL) {
831b7d5e03cSMatthew Dillon         ar9300_ani_set_odfm_noise_immunity_level(
832b7d5e03cSMatthew Dillon             ah, ani_state->ofdm_noise_immunity_level + 1);
833b7d5e03cSMatthew Dillon     }
834b7d5e03cSMatthew Dillon }
835b7d5e03cSMatthew Dillon 
836b7d5e03cSMatthew Dillon static void
ar9300_ani_cck_err_trigger(struct ath_hal * ah)837b7d5e03cSMatthew Dillon ar9300_ani_cck_err_trigger(struct ath_hal *ah)
838b7d5e03cSMatthew Dillon {
839b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
840b7d5e03cSMatthew Dillon     struct ar9300_ani_state *ani_state;
841b7d5e03cSMatthew Dillon 
842b7d5e03cSMatthew Dillon     if (!DO_ANI(ah)) {
843b7d5e03cSMatthew Dillon         return;
844b7d5e03cSMatthew Dillon     }
845b7d5e03cSMatthew Dillon 
846b7d5e03cSMatthew Dillon     ani_state = ahp->ah_curani;
847b7d5e03cSMatthew Dillon 
848b7d5e03cSMatthew Dillon     if (ani_state->cck_noise_immunity_level < HAL_ANI_CCK_MAX_LEVEL) {
849b7d5e03cSMatthew Dillon         ar9300_ani_set_cck_noise_immunity_level(
850b7d5e03cSMatthew Dillon             ah, ani_state->cck_noise_immunity_level + 1);
851b7d5e03cSMatthew Dillon     }
852b7d5e03cSMatthew Dillon }
853b7d5e03cSMatthew Dillon 
854b7d5e03cSMatthew Dillon /*
855b7d5e03cSMatthew Dillon  * Restore the ANI parameters in the HAL and reset the statistics.
856b7d5e03cSMatthew Dillon  * This routine should be called for every hardware reset and for
857b7d5e03cSMatthew Dillon  * every channel change.
858b7d5e03cSMatthew Dillon  */
859b7d5e03cSMatthew Dillon void
ar9300_ani_reset(struct ath_hal * ah,HAL_BOOL is_scanning)860b7d5e03cSMatthew Dillon ar9300_ani_reset(struct ath_hal *ah, HAL_BOOL is_scanning)
861b7d5e03cSMatthew Dillon {
862b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
863b7d5e03cSMatthew Dillon     struct ar9300_ani_state *ani_state;
864b7d5e03cSMatthew Dillon     const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
865b7d5e03cSMatthew Dillon     HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);
866b7d5e03cSMatthew Dillon     int index;
867b7d5e03cSMatthew Dillon 
868b7d5e03cSMatthew Dillon     HALASSERT(chan != AH_NULL);
869b7d5e03cSMatthew Dillon 
870b7d5e03cSMatthew Dillon     if (!DO_ANI(ah)) {
871b7d5e03cSMatthew Dillon         return;
872b7d5e03cSMatthew Dillon     }
873b7d5e03cSMatthew Dillon 
874b7d5e03cSMatthew Dillon     /*
875b7d5e03cSMatthew Dillon      * we need to re-point to the correct ANI state since the channel
876b7d5e03cSMatthew Dillon      * may have changed due to a fast channel change
877b7d5e03cSMatthew Dillon     */
878b7d5e03cSMatthew Dillon     index = ar9300_get_ani_channel_index(ah, chan);
879b7d5e03cSMatthew Dillon     ani_state = &ahp->ah_ani[index];
880b7d5e03cSMatthew Dillon     HALASSERT(ani_state != AH_NULL);
881b7d5e03cSMatthew Dillon     ahp->ah_curani = ani_state;
882b7d5e03cSMatthew Dillon 
883b7d5e03cSMatthew Dillon     ahp->ah_stats.ast_ani_reset++;
884b7d5e03cSMatthew Dillon 
885b7d5e03cSMatthew Dillon     ani_state->phy_noise_spur = 0;
886b7d5e03cSMatthew Dillon 
887b7d5e03cSMatthew Dillon     /* only allow a subset of functions in AP mode */
888b7d5e03cSMatthew Dillon     if (AH_PRIVATE(ah)->ah_opmode == HAL_M_HOSTAP) {
889b7d5e03cSMatthew Dillon         if (IS_CHAN_2GHZ(ichan)) {
890b7d5e03cSMatthew Dillon             ahp->ah_ani_function = (HAL_ANI_SPUR_IMMUNITY_LEVEL |
891b7d5e03cSMatthew Dillon                                     HAL_ANI_FIRSTEP_LEVEL |
892b7d5e03cSMatthew Dillon                                     HAL_ANI_MRC_CCK);
893b7d5e03cSMatthew Dillon         } else {
894b7d5e03cSMatthew Dillon             ahp->ah_ani_function = 0;
895b7d5e03cSMatthew Dillon         }
896b7d5e03cSMatthew Dillon     }
897b7d5e03cSMatthew Dillon     /* always allow mode (on/off) to be controlled */
898b7d5e03cSMatthew Dillon     ahp->ah_ani_function |= HAL_ANI_MODE;
899b7d5e03cSMatthew Dillon 
900b7d5e03cSMatthew Dillon     if (is_scanning ||
901b7d5e03cSMatthew Dillon         (AH_PRIVATE(ah)->ah_opmode != HAL_M_STA &&
902b7d5e03cSMatthew Dillon          AH_PRIVATE(ah)->ah_opmode != HAL_M_IBSS))
903b7d5e03cSMatthew Dillon     {
904b7d5e03cSMatthew Dillon         /*
905b7d5e03cSMatthew Dillon          * If we're scanning or in AP mode, the defaults (ini) should be
906b7d5e03cSMatthew Dillon          * in place.
907b7d5e03cSMatthew Dillon          * For an AP we assume the historical levels for this channel are
908b7d5e03cSMatthew Dillon          * probably outdated so start from defaults instead.
909b7d5e03cSMatthew Dillon          */
910b7d5e03cSMatthew Dillon         if (ani_state->ofdm_noise_immunity_level != HAL_ANI_OFDM_DEF_LEVEL ||
911b7d5e03cSMatthew Dillon             ani_state->cck_noise_immunity_level != HAL_ANI_CCK_DEF_LEVEL)
912b7d5e03cSMatthew Dillon         {
913b7d5e03cSMatthew Dillon             HALDEBUG(ah, HAL_DEBUG_ANI,
914b7d5e03cSMatthew Dillon                 "%s: Restore defaults: opmode %u chan %d Mhz/0x%x "
915b7d5e03cSMatthew Dillon                 "is_scanning=%d restore=%d ofdm:%d cck:%d\n",
916b7d5e03cSMatthew Dillon                 __func__, AH_PRIVATE(ah)->ah_opmode, chan->ic_freq,
917b7d5e03cSMatthew Dillon                 chan->ic_flags, is_scanning, ani_state->must_restore,
918b7d5e03cSMatthew Dillon                 ani_state->ofdm_noise_immunity_level,
919b7d5e03cSMatthew Dillon                 ani_state->cck_noise_immunity_level);
920b7d5e03cSMatthew Dillon             /*
921b7d5e03cSMatthew Dillon              * for STA/IBSS, we want to restore the historical values later
922b7d5e03cSMatthew Dillon              * (when we're not scanning)
923b7d5e03cSMatthew Dillon              */
924b7d5e03cSMatthew Dillon             if (AH_PRIVATE(ah)->ah_opmode == HAL_M_STA ||
925b7d5e03cSMatthew Dillon                 AH_PRIVATE(ah)->ah_opmode == HAL_M_IBSS)
926b7d5e03cSMatthew Dillon             {
927b7d5e03cSMatthew Dillon                 ar9300_ani_control(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
928b7d5e03cSMatthew Dillon                     HAL_ANI_DEF_SPUR_IMMUNE_LVL);
929b7d5e03cSMatthew Dillon                 ar9300_ani_control(
930b7d5e03cSMatthew Dillon                     ah, HAL_ANI_FIRSTEP_LEVEL, HAL_ANI_DEF_FIRSTEP_LVL);
931b7d5e03cSMatthew Dillon                 ar9300_ani_control(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
932b7d5e03cSMatthew Dillon                     HAL_ANI_USE_OFDM_WEAK_SIG);
933b7d5e03cSMatthew Dillon                 ar9300_ani_control(ah, HAL_ANI_MRC_CCK, HAL_ANI_ENABLE_MRC_CCK);
934b7d5e03cSMatthew Dillon                 ani_state->must_restore = AH_TRUE;
935b7d5e03cSMatthew Dillon             } else {
936b7d5e03cSMatthew Dillon                 ar9300_ani_set_odfm_noise_immunity_level(
937b7d5e03cSMatthew Dillon                     ah, HAL_ANI_OFDM_DEF_LEVEL);
938b7d5e03cSMatthew Dillon                 ar9300_ani_set_cck_noise_immunity_level(
939b7d5e03cSMatthew Dillon                     ah, HAL_ANI_CCK_DEF_LEVEL);
940b7d5e03cSMatthew Dillon             }
941b7d5e03cSMatthew Dillon         }
942b7d5e03cSMatthew Dillon     } else {
943b7d5e03cSMatthew Dillon         /*
944b7d5e03cSMatthew Dillon          * restore historical levels for this channel
945b7d5e03cSMatthew Dillon          */
946b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_ANI,
947b7d5e03cSMatthew Dillon             "%s: Restore history: opmode %u chan %d Mhz/0x%x is_scanning=%d "
948b7d5e03cSMatthew Dillon             "restore=%d ofdm:%d cck:%d\n",
949b7d5e03cSMatthew Dillon             __func__, AH_PRIVATE(ah)->ah_opmode, chan->ic_freq,
950b7d5e03cSMatthew Dillon             chan->ic_flags, is_scanning, ani_state->must_restore,
951b7d5e03cSMatthew Dillon             ani_state->ofdm_noise_immunity_level,
952b7d5e03cSMatthew Dillon             ani_state->cck_noise_immunity_level);
953b7d5e03cSMatthew Dillon         ar9300_ani_set_odfm_noise_immunity_level(
954b7d5e03cSMatthew Dillon             ah, ani_state->ofdm_noise_immunity_level);
955b7d5e03cSMatthew Dillon         ar9300_ani_set_cck_noise_immunity_level(
956b7d5e03cSMatthew Dillon             ah, ani_state->cck_noise_immunity_level);
957b7d5e03cSMatthew Dillon         ani_state->must_restore = AH_FALSE;
958b7d5e03cSMatthew Dillon     }
959b7d5e03cSMatthew Dillon 
960b7d5e03cSMatthew Dillon     /* enable phy counters */
961b7d5e03cSMatthew Dillon     ar9300_ani_restart(ah);
962b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
963b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
964b7d5e03cSMatthew Dillon }
965b7d5e03cSMatthew Dillon 
966b7d5e03cSMatthew Dillon /*
967b7d5e03cSMatthew Dillon  * Process a MIB interrupt.  We may potentially be invoked because
968b7d5e03cSMatthew Dillon  * any of the MIB counters overflow/trigger so don't assume we're
969b7d5e03cSMatthew Dillon  * here because a PHY error counter triggered.
970b7d5e03cSMatthew Dillon  */
971b7d5e03cSMatthew Dillon void
ar9300_process_mib_intr(struct ath_hal * ah,const HAL_NODE_STATS * stats)972b7d5e03cSMatthew Dillon ar9300_process_mib_intr(struct ath_hal *ah, const HAL_NODE_STATS *stats)
973b7d5e03cSMatthew Dillon {
974b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
975b7d5e03cSMatthew Dillon     u_int32_t phy_cnt1, phy_cnt2;
976b7d5e03cSMatthew Dillon 
977b7d5e03cSMatthew Dillon #if 0
978b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Processing Mib Intr\n", __func__);
979b7d5e03cSMatthew Dillon #endif
980b7d5e03cSMatthew Dillon 
981b7d5e03cSMatthew Dillon     /* Reset these counters regardless */
982b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_FILT_OFDM, 0);
983b7d5e03cSMatthew Dillon     OS_REG_WRITE(ah, AR_FILT_CCK, 0);
984b7d5e03cSMatthew Dillon     if (!(OS_REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING)) {
985b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR);
986b7d5e03cSMatthew Dillon     }
987b7d5e03cSMatthew Dillon 
988b7d5e03cSMatthew Dillon     /* Clear the mib counters and save them in the stats */
989b7d5e03cSMatthew Dillon     ar9300_update_mib_mac_stats(ah);
990b7d5e03cSMatthew Dillon     ahp->ah_stats.ast_nodestats = *stats;
991b7d5e03cSMatthew Dillon 
992b7d5e03cSMatthew Dillon     if (!DO_ANI(ah)) {
993b7d5e03cSMatthew Dillon         /*
994b7d5e03cSMatthew Dillon          * We must always clear the interrupt cause by resetting
995b7d5e03cSMatthew Dillon          * the phy error regs.
996b7d5e03cSMatthew Dillon          */
997b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_PHY_ERR_1, 0);
998b7d5e03cSMatthew Dillon         OS_REG_WRITE(ah, AR_PHY_ERR_2, 0);
999b7d5e03cSMatthew Dillon         return;
1000b7d5e03cSMatthew Dillon     }
1001b7d5e03cSMatthew Dillon 
1002b7d5e03cSMatthew Dillon     /* NB: these are not reset-on-read */
1003b7d5e03cSMatthew Dillon     phy_cnt1 = OS_REG_READ(ah, AR_PHY_ERR_1);
1004b7d5e03cSMatthew Dillon     phy_cnt2 = OS_REG_READ(ah, AR_PHY_ERR_2);
1005b7d5e03cSMatthew Dillon #if HAL_ANI_DEBUG
1006b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_ANI,
1007b7d5e03cSMatthew Dillon         "%s: Errors: OFDM=0x%08x-0x0=%d   CCK=0x%08x-0x0=%d\n",
1008b7d5e03cSMatthew Dillon         __func__, phy_cnt1, phy_cnt1, phy_cnt2, phy_cnt2);
1009b7d5e03cSMatthew Dillon #endif
1010b7d5e03cSMatthew Dillon     if (((phy_cnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) ||
1011b7d5e03cSMatthew Dillon         ((phy_cnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) {
1012b7d5e03cSMatthew Dillon         /* NB: always restart to insure the h/w counters are reset */
1013b7d5e03cSMatthew Dillon         ar9300_ani_restart(ah);
1014b7d5e03cSMatthew Dillon     }
1015b7d5e03cSMatthew Dillon }
1016b7d5e03cSMatthew Dillon 
1017b7d5e03cSMatthew Dillon 
1018b7d5e03cSMatthew Dillon static void
ar9300_ani_lower_immunity(struct ath_hal * ah)1019b7d5e03cSMatthew Dillon ar9300_ani_lower_immunity(struct ath_hal *ah)
1020b7d5e03cSMatthew Dillon {
1021b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
1022b7d5e03cSMatthew Dillon     struct ar9300_ani_state *ani_state = ahp->ah_curani;
1023b7d5e03cSMatthew Dillon 
1024b7d5e03cSMatthew Dillon     if (ani_state->ofdm_noise_immunity_level > 0 &&
1025b7d5e03cSMatthew Dillon         (ani_state->ofdms_turn || ani_state->cck_noise_immunity_level == 0)) {
1026b7d5e03cSMatthew Dillon         /*
1027b7d5e03cSMatthew Dillon          * lower OFDM noise immunity
1028b7d5e03cSMatthew Dillon          */
1029b7d5e03cSMatthew Dillon         ar9300_ani_set_odfm_noise_immunity_level(
1030b7d5e03cSMatthew Dillon             ah, ani_state->ofdm_noise_immunity_level - 1);
1031b7d5e03cSMatthew Dillon 
1032b7d5e03cSMatthew Dillon         /*
1033b7d5e03cSMatthew Dillon          * only lower either OFDM or CCK errors per turn
1034b7d5e03cSMatthew Dillon          * we lower the other one next time
1035b7d5e03cSMatthew Dillon          */
1036b7d5e03cSMatthew Dillon         return;
1037b7d5e03cSMatthew Dillon     }
1038b7d5e03cSMatthew Dillon 
1039b7d5e03cSMatthew Dillon     if (ani_state->cck_noise_immunity_level > 0) {
1040b7d5e03cSMatthew Dillon         /*
1041b7d5e03cSMatthew Dillon          * lower CCK noise immunity
1042b7d5e03cSMatthew Dillon          */
1043b7d5e03cSMatthew Dillon         ar9300_ani_set_cck_noise_immunity_level(
1044b7d5e03cSMatthew Dillon             ah, ani_state->cck_noise_immunity_level - 1);
1045b7d5e03cSMatthew Dillon     }
1046b7d5e03cSMatthew Dillon }
1047b7d5e03cSMatthew Dillon 
1048b7d5e03cSMatthew Dillon /* convert HW counter values to ms using mode specifix clock rate */
1049b7d5e03cSMatthew Dillon //#define CLOCK_RATE(_ah)  (ath_hal_chan_2_clock_rate_mhz(_ah) * 1000)
1050b7d5e03cSMatthew Dillon #define CLOCK_RATE(_ah)  (ath_hal_mac_clks(ah, 1000))
1051b7d5e03cSMatthew Dillon 
1052b7d5e03cSMatthew Dillon /*
1053b7d5e03cSMatthew Dillon  * Return an approximation of the time spent ``listening'' by
1054b7d5e03cSMatthew Dillon  * deducting the cycles spent tx'ing and rx'ing from the total
1055b7d5e03cSMatthew Dillon  * cycle count since our last call.  A return value <0 indicates
1056b7d5e03cSMatthew Dillon  * an invalid/inconsistent time.
1057b7d5e03cSMatthew Dillon  */
1058b7d5e03cSMatthew Dillon static int32_t
ar9300_ani_get_listen_time(struct ath_hal * ah,HAL_ANISTATS * ani_stats)1059b7d5e03cSMatthew Dillon ar9300_ani_get_listen_time(struct ath_hal *ah, HAL_ANISTATS *ani_stats)
1060b7d5e03cSMatthew Dillon {
1061b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
1062b7d5e03cSMatthew Dillon     struct ar9300_ani_state *ani_state;
1063b7d5e03cSMatthew Dillon     u_int32_t tx_frame_count, rx_frame_count, cycle_count;
1064a20e5e51SMatthew Dillon     u_int32_t rx_busy_count, rx_ext_busy_count;
1065b7d5e03cSMatthew Dillon     int32_t listen_time;
1066b7d5e03cSMatthew Dillon 
1067b7d5e03cSMatthew Dillon     tx_frame_count = OS_REG_READ(ah, AR_TFCNT);
1068b7d5e03cSMatthew Dillon     rx_frame_count = OS_REG_READ(ah, AR_RFCNT);
1069a20e5e51SMatthew Dillon     rx_busy_count = OS_REG_READ(ah, AR_RCCNT);
1070a20e5e51SMatthew Dillon     rx_ext_busy_count = OS_REG_READ(ah, AR_EXTRCCNT);
1071b7d5e03cSMatthew Dillon     cycle_count = OS_REG_READ(ah, AR_CCCNT);
1072b7d5e03cSMatthew Dillon 
1073b7d5e03cSMatthew Dillon     ani_state = ahp->ah_curani;
1074b7d5e03cSMatthew Dillon     if (ani_state->cycle_count == 0 || ani_state->cycle_count > cycle_count) {
1075b7d5e03cSMatthew Dillon         /*
1076b7d5e03cSMatthew Dillon          * Cycle counter wrap (or initial call); it's not possible
1077b7d5e03cSMatthew Dillon          * to accurately calculate a value because the registers
1078b7d5e03cSMatthew Dillon          * right shift rather than wrap--so punt and return 0.
1079b7d5e03cSMatthew Dillon          */
1080b7d5e03cSMatthew Dillon         listen_time = 0;
1081b7d5e03cSMatthew Dillon         ahp->ah_stats.ast_ani_lzero++;
1082b7d5e03cSMatthew Dillon #if HAL_ANI_DEBUG
1083b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_ANI,
1084b7d5e03cSMatthew Dillon             "%s: 1st call: ani_state->cycle_count=%d\n",
1085b7d5e03cSMatthew Dillon             __func__, ani_state->cycle_count);
1086b7d5e03cSMatthew Dillon #endif
1087b7d5e03cSMatthew Dillon     } else {
1088b7d5e03cSMatthew Dillon         int32_t ccdelta = cycle_count - ani_state->cycle_count;
1089b7d5e03cSMatthew Dillon         int32_t rfdelta = rx_frame_count - ani_state->rx_frame_count;
1090b7d5e03cSMatthew Dillon         int32_t tfdelta = tx_frame_count - ani_state->tx_frame_count;
1091a20e5e51SMatthew Dillon         int32_t rcdelta = rx_busy_count - ani_state->rx_busy_count;
1092a20e5e51SMatthew Dillon         int32_t extrcdelta = rx_ext_busy_count - ani_state->rx_ext_busy_count;
1093b7d5e03cSMatthew Dillon         listen_time = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE(ah);
1094a20e5e51SMatthew Dillon //#if HAL_ANI_DEBUG
1095b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_ANI,
1096a20e5e51SMatthew Dillon             "%s: cyclecount=%d, rfcount=%d, tfcount=%d, rcdelta=%d, extrcdelta=%d, listen_time=%d "
1097b7d5e03cSMatthew Dillon             "CLOCK_RATE=%d\n",
1098a20e5e51SMatthew Dillon             __func__, ccdelta, rfdelta, tfdelta, rcdelta, extrcdelta,
1099a20e5e51SMatthew Dillon             listen_time, CLOCK_RATE(ah));
1100a20e5e51SMatthew Dillon //#endif
1101a20e5e51SMatthew Dillon             /* Populate as appropriate */
1102a20e5e51SMatthew Dillon             ani_stats->cyclecnt_diff = ccdelta;
1103a20e5e51SMatthew Dillon             ani_stats->rxclr_cnt = rcdelta;
1104a20e5e51SMatthew Dillon             ani_stats->txframecnt_diff = tfdelta;
1105a20e5e51SMatthew Dillon             ani_stats->rxframecnt_diff = rfdelta;
1106a20e5e51SMatthew Dillon             ani_stats->extrxclr_cnt = extrcdelta;
1107a20e5e51SMatthew Dillon             ani_stats->listen_time = listen_time;
1108a20e5e51SMatthew Dillon             ani_stats->valid = AH_TRUE;
1109b7d5e03cSMatthew Dillon     }
1110b7d5e03cSMatthew Dillon     ani_state->cycle_count = cycle_count;
1111b7d5e03cSMatthew Dillon     ani_state->tx_frame_count = tx_frame_count;
1112b7d5e03cSMatthew Dillon     ani_state->rx_frame_count = rx_frame_count;
1113a20e5e51SMatthew Dillon     ani_state->rx_busy_count = rx_busy_count;
1114a20e5e51SMatthew Dillon     ani_state->rx_ext_busy_count = rx_ext_busy_count;
1115b7d5e03cSMatthew Dillon     return listen_time;
1116b7d5e03cSMatthew Dillon }
1117b7d5e03cSMatthew Dillon 
1118b7d5e03cSMatthew Dillon /*
1119b7d5e03cSMatthew Dillon  * Do periodic processing.  This routine is called from a timer
1120b7d5e03cSMatthew Dillon  */
1121b7d5e03cSMatthew Dillon void
ar9300_ani_ar_poll(struct ath_hal * ah,const HAL_NODE_STATS * stats,const struct ieee80211_channel * chan,HAL_ANISTATS * ani_stats)1122b7d5e03cSMatthew Dillon ar9300_ani_ar_poll(struct ath_hal *ah, const HAL_NODE_STATS *stats,
1123b7d5e03cSMatthew Dillon                 const struct ieee80211_channel *chan, HAL_ANISTATS *ani_stats)
1124b7d5e03cSMatthew Dillon {
1125b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
1126b7d5e03cSMatthew Dillon     struct ar9300_ani_state *ani_state;
1127b7d5e03cSMatthew Dillon     int32_t listen_time;
1128b7d5e03cSMatthew Dillon     u_int32_t ofdm_phy_err_rate, cck_phy_err_rate;
1129b7d5e03cSMatthew Dillon     u_int32_t ofdm_phy_err_cnt, cck_phy_err_cnt;
1130b7d5e03cSMatthew Dillon     HAL_BOOL old_phy_noise_spur;
1131b7d5e03cSMatthew Dillon 
1132b7d5e03cSMatthew Dillon     ani_state = ahp->ah_curani;
1133b7d5e03cSMatthew Dillon     ahp->ah_stats.ast_nodestats = *stats;        /* XXX optimize? */
1134b7d5e03cSMatthew Dillon 
1135b7d5e03cSMatthew Dillon     if (ani_state == NULL) {
1136b7d5e03cSMatthew Dillon         /* should not happen */
1137b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
1138b7d5e03cSMatthew Dillon             "%s: can't poll - no ANI not initialized for this channel\n",
1139b7d5e03cSMatthew Dillon             __func__);
1140b7d5e03cSMatthew Dillon         return;
1141b7d5e03cSMatthew Dillon     }
1142b7d5e03cSMatthew Dillon 
1143b7d5e03cSMatthew Dillon     /*
1144b7d5e03cSMatthew Dillon      * ar9300_ani_ar_poll is never called while scanning but we may have been
1145b7d5e03cSMatthew Dillon      * scanning and now just restarted polling.  In this case we need to
1146b7d5e03cSMatthew Dillon      * restore historical values.
1147b7d5e03cSMatthew Dillon      */
1148b7d5e03cSMatthew Dillon     if (ani_state->must_restore) {
1149b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_ANI,
1150b7d5e03cSMatthew Dillon             "%s: must restore - calling ar9300_ani_restart\n", __func__);
1151b7d5e03cSMatthew Dillon         ar9300_ani_reset(ah, AH_FALSE);
1152b7d5e03cSMatthew Dillon         return;
1153b7d5e03cSMatthew Dillon     }
1154b7d5e03cSMatthew Dillon 
1155b7d5e03cSMatthew Dillon     listen_time = ar9300_ani_get_listen_time(ah, ani_stats);
1156b7d5e03cSMatthew Dillon     if (listen_time <= 0) {
1157b7d5e03cSMatthew Dillon         ahp->ah_stats.ast_ani_lneg++;
1158b7d5e03cSMatthew Dillon         /* restart ANI period if listen_time is invalid */
1159b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_ANI,
1160b7d5e03cSMatthew Dillon             "%s: listen_time=%d - calling ar9300_ani_restart\n",
1161b7d5e03cSMatthew Dillon             __func__, listen_time);
1162b7d5e03cSMatthew Dillon         ar9300_ani_restart(ah);
1163b7d5e03cSMatthew Dillon         return;
1164b7d5e03cSMatthew Dillon     }
1165b7d5e03cSMatthew Dillon     /* XXX beware of overflow? */
1166b7d5e03cSMatthew Dillon     ani_state->listen_time += listen_time;
1167b7d5e03cSMatthew Dillon 
1168b7d5e03cSMatthew Dillon     /* Clear the mib counters and save them in the stats */
1169b7d5e03cSMatthew Dillon     ar9300_update_mib_mac_stats(ah);
1170b7d5e03cSMatthew Dillon     /* NB: these are not reset-on-read */
1171b7d5e03cSMatthew Dillon     ofdm_phy_err_cnt = OS_REG_READ(ah, AR_PHY_ERR_1);
1172b7d5e03cSMatthew Dillon     cck_phy_err_cnt = OS_REG_READ(ah, AR_PHY_ERR_2);
1173b7d5e03cSMatthew Dillon 
1174a20e5e51SMatthew Dillon     /* Populate HAL_ANISTATS */
1175a20e5e51SMatthew Dillon     if (ani_stats) {
1176a20e5e51SMatthew Dillon             ani_stats->cckphyerr_cnt =
1177a20e5e51SMatthew Dillon                cck_phy_err_cnt - ani_state->cck_phy_err_count;
1178a20e5e51SMatthew Dillon             ani_stats->ofdmphyerrcnt_diff =
1179a20e5e51SMatthew Dillon               ofdm_phy_err_cnt - ani_state->ofdm_phy_err_count;
1180a20e5e51SMatthew Dillon     }
1181b7d5e03cSMatthew Dillon 
1182b7d5e03cSMatthew Dillon     /* NB: only use ast_ani_*errs with AH_PRIVATE_DIAG */
1183b7d5e03cSMatthew Dillon     ahp->ah_stats.ast_ani_ofdmerrs +=
1184b7d5e03cSMatthew Dillon         ofdm_phy_err_cnt - ani_state->ofdm_phy_err_count;
1185b7d5e03cSMatthew Dillon     ani_state->ofdm_phy_err_count = ofdm_phy_err_cnt;
1186b7d5e03cSMatthew Dillon 
1187b7d5e03cSMatthew Dillon     ahp->ah_stats.ast_ani_cckerrs +=
1188b7d5e03cSMatthew Dillon         cck_phy_err_cnt - ani_state->cck_phy_err_count;
1189b7d5e03cSMatthew Dillon     ani_state->cck_phy_err_count = cck_phy_err_cnt;
1190b7d5e03cSMatthew Dillon 
1191b7d5e03cSMatthew Dillon #if HAL_ANI_DEBUG
1192b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_ANI,
1193b7d5e03cSMatthew Dillon         "%s: Errors: OFDM=0x%08x-0x0=%d   CCK=0x%08x-0x0=%d\n",
1194b7d5e03cSMatthew Dillon         __func__, ofdm_phy_err_cnt, ofdm_phy_err_cnt,
1195b7d5e03cSMatthew Dillon         cck_phy_err_cnt, cck_phy_err_cnt);
1196b7d5e03cSMatthew Dillon #endif
1197b7d5e03cSMatthew Dillon 
1198b7d5e03cSMatthew Dillon     /*
1199b7d5e03cSMatthew Dillon      * If ani is not enabled, return after we've collected
1200b7d5e03cSMatthew Dillon      * statistics
1201b7d5e03cSMatthew Dillon      */
1202b7d5e03cSMatthew Dillon     if (!DO_ANI(ah)) {
1203b7d5e03cSMatthew Dillon         return;
1204b7d5e03cSMatthew Dillon     }
1205b7d5e03cSMatthew Dillon 
1206b7d5e03cSMatthew Dillon     ofdm_phy_err_rate =
1207b7d5e03cSMatthew Dillon         ani_state->ofdm_phy_err_count * 1000 / ani_state->listen_time;
1208b7d5e03cSMatthew Dillon     cck_phy_err_rate =
1209b7d5e03cSMatthew Dillon         ani_state->cck_phy_err_count * 1000 / ani_state->listen_time;
1210b7d5e03cSMatthew Dillon 
1211b7d5e03cSMatthew Dillon     HALDEBUG(ah, HAL_DEBUG_ANI,
1212b7d5e03cSMatthew Dillon         "%s: listen_time=%d OFDM:%d errs=%d/s CCK:%d errs=%d/s ofdm_turn=%d\n",
1213b7d5e03cSMatthew Dillon         __func__, listen_time,
1214b7d5e03cSMatthew Dillon         ani_state->ofdm_noise_immunity_level, ofdm_phy_err_rate,
1215b7d5e03cSMatthew Dillon         ani_state->cck_noise_immunity_level, cck_phy_err_rate,
1216b7d5e03cSMatthew Dillon         ani_state->ofdms_turn);
1217b7d5e03cSMatthew Dillon 
1218b7d5e03cSMatthew Dillon     if (ani_state->listen_time >= HAL_NOISE_DETECT_PERIOD) {
1219b7d5e03cSMatthew Dillon         old_phy_noise_spur = ani_state->phy_noise_spur;
1220b7d5e03cSMatthew Dillon         if (ofdm_phy_err_rate <= ani_state->ofdm_trig_low &&
1221b7d5e03cSMatthew Dillon             cck_phy_err_rate <= ani_state->cck_trig_low) {
1222b7d5e03cSMatthew Dillon             if (ani_state->listen_time >= HAL_NOISE_RECOVER_PERIOD) {
1223b7d5e03cSMatthew Dillon                 ani_state->phy_noise_spur = 0;
1224b7d5e03cSMatthew Dillon             }
1225b7d5e03cSMatthew Dillon         } else {
1226b7d5e03cSMatthew Dillon             ani_state->phy_noise_spur = 1;
1227b7d5e03cSMatthew Dillon         }
1228b7d5e03cSMatthew Dillon         if (old_phy_noise_spur != ani_state->phy_noise_spur) {
1229b7d5e03cSMatthew Dillon             HALDEBUG(ah, HAL_DEBUG_ANI,
1230*f12e2116SSascha Wildner                      "%s: environment change from %d to %d\n",
1231b7d5e03cSMatthew Dillon                      __func__, old_phy_noise_spur, ani_state->phy_noise_spur);
1232b7d5e03cSMatthew Dillon         }
1233b7d5e03cSMatthew Dillon     }
1234b7d5e03cSMatthew Dillon 
1235b7d5e03cSMatthew Dillon     if (ani_state->listen_time > 5 * ahp->ah_ani_period) {
1236b7d5e03cSMatthew Dillon         /*
1237b7d5e03cSMatthew Dillon          * Check to see if need to lower immunity if
1238b7d5e03cSMatthew Dillon          * 5 ani_periods have passed
1239b7d5e03cSMatthew Dillon          */
1240b7d5e03cSMatthew Dillon         if (ofdm_phy_err_rate <= ani_state->ofdm_trig_low &&
1241b7d5e03cSMatthew Dillon             cck_phy_err_rate <= ani_state->cck_trig_low)
1242b7d5e03cSMatthew Dillon         {
1243b7d5e03cSMatthew Dillon             HALDEBUG(ah, HAL_DEBUG_ANI,
1244b7d5e03cSMatthew Dillon                 "%s: 1. listen_time=%d OFDM:%d errs=%d/s(<%d)  "
1245b7d5e03cSMatthew Dillon                 "CCK:%d errs=%d/s(<%d) -> ar9300_ani_lower_immunity\n",
1246b7d5e03cSMatthew Dillon                 __func__, ani_state->listen_time,
1247b7d5e03cSMatthew Dillon                 ani_state->ofdm_noise_immunity_level, ofdm_phy_err_rate,
1248b7d5e03cSMatthew Dillon                 ani_state->ofdm_trig_low, ani_state->cck_noise_immunity_level,
1249b7d5e03cSMatthew Dillon                 cck_phy_err_rate, ani_state->cck_trig_low);
1250b7d5e03cSMatthew Dillon             ar9300_ani_lower_immunity(ah);
1251b7d5e03cSMatthew Dillon             ani_state->ofdms_turn = !ani_state->ofdms_turn;
1252b7d5e03cSMatthew Dillon         }
1253b7d5e03cSMatthew Dillon         HALDEBUG(ah, HAL_DEBUG_ANI,
1254b7d5e03cSMatthew Dillon             "%s: 1 listen_time=%d ofdm=%d/s cck=%d/s - "
1255b7d5e03cSMatthew Dillon             "calling ar9300_ani_restart\n",
1256b7d5e03cSMatthew Dillon             __func__, ani_state->listen_time,
1257b7d5e03cSMatthew Dillon             ofdm_phy_err_rate, cck_phy_err_rate);
1258b7d5e03cSMatthew Dillon         ar9300_ani_restart(ah);
1259b7d5e03cSMatthew Dillon      } else if (ani_state->listen_time > ahp->ah_ani_period) {
1260b7d5e03cSMatthew Dillon         /* check to see if need to raise immunity */
1261b7d5e03cSMatthew Dillon         if (ofdm_phy_err_rate > ani_state->ofdm_trig_high &&
1262b7d5e03cSMatthew Dillon             (cck_phy_err_rate <= ani_state->cck_trig_high ||
1263b7d5e03cSMatthew Dillon              ani_state->ofdms_turn))
1264b7d5e03cSMatthew Dillon         {
1265b7d5e03cSMatthew Dillon             HALDEBUG(ah, HAL_DEBUG_ANI,
1266b7d5e03cSMatthew Dillon                 "%s: 2 listen_time=%d OFDM:%d errs=%d/s(>%d) -> "
1267b7d5e03cSMatthew Dillon                 "ar9300_ani_ofdm_err_trigger\n",
1268b7d5e03cSMatthew Dillon                 __func__, ani_state->listen_time,
1269b7d5e03cSMatthew Dillon                 ani_state->ofdm_noise_immunity_level, ofdm_phy_err_rate,
1270b7d5e03cSMatthew Dillon                 ani_state->ofdm_trig_high);
1271b7d5e03cSMatthew Dillon             ar9300_ani_ofdm_err_trigger(ah);
1272b7d5e03cSMatthew Dillon             ar9300_ani_restart(ah);
1273b7d5e03cSMatthew Dillon             ani_state->ofdms_turn = AH_FALSE;
1274b7d5e03cSMatthew Dillon         } else if (cck_phy_err_rate > ani_state->cck_trig_high) {
1275b7d5e03cSMatthew Dillon             HALDEBUG(ah, HAL_DEBUG_ANI,
1276b7d5e03cSMatthew Dillon                 "%s: 3 listen_time=%d CCK:%d errs=%d/s(>%d) -> "
1277b7d5e03cSMatthew Dillon                 "ar9300_ani_cck_err_trigger\n",
1278b7d5e03cSMatthew Dillon                 __func__, ani_state->listen_time,
1279b7d5e03cSMatthew Dillon                 ani_state->cck_noise_immunity_level, cck_phy_err_rate,
1280b7d5e03cSMatthew Dillon                 ani_state->cck_trig_high);
1281b7d5e03cSMatthew Dillon             ar9300_ani_cck_err_trigger(ah);
1282b7d5e03cSMatthew Dillon             ar9300_ani_restart(ah);
1283b7d5e03cSMatthew Dillon             ani_state->ofdms_turn = AH_TRUE;
1284b7d5e03cSMatthew Dillon         }
1285b7d5e03cSMatthew Dillon     }
1286b7d5e03cSMatthew Dillon }
1287b7d5e03cSMatthew Dillon 
1288b7d5e03cSMatthew Dillon /*
1289b7d5e03cSMatthew Dillon  * The poll function above calculates short noise spurs, caused by non-80211
1290b7d5e03cSMatthew Dillon  * devices, based on OFDM/CCK Phy errs.
1291b7d5e03cSMatthew Dillon  * If the noise is short enough, we don't want our ratectrl Algo to stop probing
1292b7d5e03cSMatthew Dillon  * higher rates, due to bad PER.
1293b7d5e03cSMatthew Dillon  */
1294b7d5e03cSMatthew Dillon HAL_BOOL
ar9300_is_ani_noise_spur(struct ath_hal * ah)1295b7d5e03cSMatthew Dillon ar9300_is_ani_noise_spur(struct ath_hal *ah)
1296b7d5e03cSMatthew Dillon {
1297b7d5e03cSMatthew Dillon     struct ath_hal_9300 *ahp = AH9300(ah);
1298b7d5e03cSMatthew Dillon     struct ar9300_ani_state *ani_state;
1299b7d5e03cSMatthew Dillon 
1300b7d5e03cSMatthew Dillon     ani_state = ahp->ah_curani;
1301b7d5e03cSMatthew Dillon 
1302b7d5e03cSMatthew Dillon     return ani_state->phy_noise_spur;
1303b7d5e03cSMatthew Dillon }
1304b7d5e03cSMatthew Dillon 
1305