1250003Sadrian/*
2250003Sadrian * Copyright (c) 2013 Qualcomm Atheros, Inc.
3250003Sadrian *
4250003Sadrian * Permission to use, copy, modify, and/or distribute this software for any
5250003Sadrian * purpose with or without fee is hereby granted, provided that the above
6250003Sadrian * copyright notice and this permission notice appear in all copies.
7250003Sadrian *
8250003Sadrian * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
9250003Sadrian * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
10250003Sadrian * AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
11250003Sadrian * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
12250003Sadrian * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
13250003Sadrian * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
14250003Sadrian * PERFORMANCE OF THIS SOFTWARE.
15250003Sadrian */
16250003Sadrian
17250003Sadrian#include "opt_ah.h"
18250003Sadrian
19250003Sadrian#include "ah.h"
20250003Sadrian#include "ah_internal.h"
21250003Sadrian#include "ah_desc.h"
22250008Sadrian//#include "ah_pktlog.h"
23250003Sadrian
24250003Sadrian#include "ar9300/ar9300.h"
25250003Sadrian#include "ar9300/ar9300reg.h"
26250003Sadrian#include "ar9300/ar9300phy.h"
27250003Sadrian
28250003Sadrianextern  void ar9300_set_rx_filter(struct ath_hal *ah, u_int32_t bits);
29250003Sadrianextern  u_int32_t ar9300_get_rx_filter(struct ath_hal *ah);
30250003Sadrian
31250008Sadrian#define HAL_ANI_DEBUG 1
32250003Sadrian
33250003Sadrian/*
34250003Sadrian * Anti noise immunity support.  We track phy errors and react
35250003Sadrian * to excessive errors by adjusting the noise immunity parameters.
36250003Sadrian */
37250003Sadrian
38250003Sadrian/******************************************************************************
39250003Sadrian *
40250003Sadrian * New Ani Algorithm for Station side only
41250003Sadrian *
42250003Sadrian *****************************************************************************/
43250003Sadrian
44250003Sadrian#define HAL_ANI_OFDM_TRIG_HIGH     1000 /* units are errors per second */
45250003Sadrian#define HAL_ANI_OFDM_TRIG_LOW       400 /* units are errors per second */
46250003Sadrian#define HAL_ANI_CCK_TRIG_HIGH       600 /* units are errors per second */
47250003Sadrian#define HAL_ANI_CCK_TRIG_LOW        300 /* units are errors per second */
48250008Sadrian#define HAL_ANI_USE_OFDM_WEAK_SIG  AH_TRUE
49250003Sadrian#define HAL_ANI_ENABLE_MRC_CCK     AH_TRUE /* default is enabled */
50250003Sadrian#define HAL_ANI_DEF_SPUR_IMMUNE_LVL   3
51250003Sadrian#define HAL_ANI_DEF_FIRSTEP_LVL       2
52250003Sadrian#define HAL_ANI_RSSI_THR_HIGH        40
53250003Sadrian#define HAL_ANI_RSSI_THR_LOW          7
54250003Sadrian#define HAL_ANI_PERIOD             1000
55250003Sadrian
56250003Sadrian#define HAL_NOISE_DETECT_PERIOD     100
57250003Sadrian#define HAL_NOISE_RECOVER_PERIOD    5000
58250003Sadrian
59250003Sadrian#define HAL_SIG_FIRSTEP_SETTING_MIN   0
60250003Sadrian#define HAL_SIG_FIRSTEP_SETTING_MAX  20
61250003Sadrian#define HAL_SIG_SPUR_IMM_SETTING_MIN  0
62250003Sadrian#define HAL_SIG_SPUR_IMM_SETTING_MAX 22
63250003Sadrian
64250003Sadrian#define HAL_EP_RND(x, mul) \
65250003Sadrian    ((((x) % (mul)) >= ((mul) / 2)) ? ((x) + ((mul) - 1)) / (mul) : (x) / (mul))
66250003Sadrian#define BEACON_RSSI(ahp) \
67250003Sadrian    HAL_EP_RND(ahp->ah_stats.ast_nodestats.ns_avgbrssi, \
68250003Sadrian        HAL_RSSI_EP_MULTIPLIER)
69250003Sadrian
70250003Sadriantypedef int TABLE[];
71250003Sadrian/*
72250003Sadrian *                            level:    0   1   2   3   4   5   6   7   8
73250003Sadrian * firstep_table:    lvl 0-8, default 2
74250003Sadrian */
75250003Sadrianstatic const TABLE firstep_table    = { -4, -2,  0,  2,  4,  6,  8, 10, 12};
76250003Sadrian/* cycpwr_thr1_table: lvl 0-7, default 3 */
77250003Sadrianstatic const TABLE cycpwr_thr1_table = { -6, -4, -2,  0,  2,  4,  6,  8 };
78250003Sadrian/* values here are relative to the INI */
79250003Sadrian
80250003Sadriantypedef struct _HAL_ANI_OFDM_LEVEL_ENTRY {
81250003Sadrian    int spur_immunity_level;
82250003Sadrian    int fir_step_level;
83250003Sadrian    int ofdm_weak_signal_on;
84250003Sadrian} HAL_ANI_OFDM_LEVEL_ENTRY;
85250003Sadrianstatic const HAL_ANI_OFDM_LEVEL_ENTRY ofdm_level_table[] = {
86250003Sadrian/*     SI  FS  WS */
87250003Sadrian     {  0,  0,  1  }, /* lvl 0 */
88250003Sadrian     {  1,  1,  1  }, /* lvl 1 */
89250003Sadrian     {  2,  2,  1  }, /* lvl 2 */
90250003Sadrian     {  3,  2,  1  }, /* lvl 3  (default) */
91250003Sadrian     {  4,  3,  1  }, /* lvl 4 */
92250003Sadrian     {  5,  4,  1  }, /* lvl 5 */
93250003Sadrian     {  6,  5,  1  }, /* lvl 6 */
94250003Sadrian     {  7,  6,  1  }, /* lvl 7 */
95250003Sadrian     {  7,  7,  1  }, /* lvl 8 */
96250003Sadrian     {  7,  8,  0  }  /* lvl 9 */
97250003Sadrian};
98250003Sadrian#define HAL_ANI_OFDM_NUM_LEVEL \
99250003Sadrian    (sizeof(ofdm_level_table) / sizeof(ofdm_level_table[0]))
100250003Sadrian#define HAL_ANI_OFDM_MAX_LEVEL (HAL_ANI_OFDM_NUM_LEVEL - 1)
101250003Sadrian#define HAL_ANI_OFDM_DEF_LEVEL 3 /* default level - matches the INI settings */
102250003Sadrian
103250003Sadriantypedef struct _HAL_ANI_CCK_LEVEL_ENTRY {
104250003Sadrian    int fir_step_level;
105250003Sadrian    int mrc_cck_on;
106250003Sadrian} HAL_ANI_CCK_LEVEL_ENTRY;
107250003Sadrian
108250003Sadrianstatic const HAL_ANI_CCK_LEVEL_ENTRY cck_level_table[] = {
109250003Sadrian/*     FS  MRC-CCK */
110250003Sadrian     {  0,  1  },  /* lvl 0 */
111250003Sadrian     {  1,  1  },  /* lvl 1 */
112250003Sadrian     {  2,  1  },  /* lvl 2  (default) */
113250003Sadrian     {  3,  1  },  /* lvl 3 */
114250003Sadrian     {  4,  0  },  /* lvl 4 */
115250003Sadrian     {  5,  0  },  /* lvl 5 */
116250003Sadrian     {  6,  0  },  /* lvl 6 */
117250003Sadrian     {  7,  0  },  /* lvl 7 (only for high rssi) */
118250003Sadrian     {  8,  0  }   /* lvl 8 (only for high rssi) */
119250003Sadrian};
120250003Sadrian#define HAL_ANI_CCK_NUM_LEVEL \
121250003Sadrian    (sizeof(cck_level_table) / sizeof(cck_level_table[0]))
122250003Sadrian#define HAL_ANI_CCK_MAX_LEVEL           (HAL_ANI_CCK_NUM_LEVEL - 1)
123250003Sadrian#define HAL_ANI_CCK_MAX_LEVEL_LOW_RSSI  (HAL_ANI_CCK_NUM_LEVEL - 3)
124250003Sadrian#define HAL_ANI_CCK_DEF_LEVEL 2 /* default level - matches the INI settings */
125250003Sadrian
126250003Sadrian/*
127250003Sadrian * register values to turn OFDM weak signal detection OFF
128250003Sadrian */
129250003Sadrianstatic const int m1_thresh_low_off     = 127;
130250003Sadrianstatic const int m2_thresh_low_off     = 127;
131250003Sadrianstatic const int m1_thresh_off         = 127;
132250003Sadrianstatic const int m2_thresh_off         = 127;
133250003Sadrianstatic const int m2_count_thr_off      =  31;
134250003Sadrianstatic const int m2_count_thr_low_off  =  63;
135250003Sadrianstatic const int m1_thresh_low_ext_off = 127;
136250003Sadrianstatic const int m2_thresh_low_ext_off = 127;
137250003Sadrianstatic const int m1_thresh_ext_off     = 127;
138250003Sadrianstatic const int m2_thresh_ext_off     = 127;
139250003Sadrian
140250003Sadrianvoid
141250003Sadrianar9300_enable_mib_counters(struct ath_hal *ah)
142250003Sadrian{
143250003Sadrian    HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Enable MIB counters\n", __func__);
144250003Sadrian    /* Clear the mib counters and save them in the stats */
145250003Sadrian    ar9300_update_mib_mac_stats(ah);
146250003Sadrian
147250003Sadrian    OS_REG_WRITE(ah, AR_FILT_OFDM, 0);
148250003Sadrian    OS_REG_WRITE(ah, AR_FILT_CCK, 0);
149250003Sadrian    OS_REG_WRITE(ah, AR_MIBC,
150250003Sadrian        ~(AR_MIBC_COW | AR_MIBC_FMC | AR_MIBC_CMC | AR_MIBC_MCS) & 0x0f);
151250003Sadrian    OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
152250003Sadrian    OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
153250003Sadrian
154250003Sadrian}
155250003Sadrian
156250003Sadrianvoid
157250003Sadrianar9300_disable_mib_counters(struct ath_hal *ah)
158250003Sadrian{
159250003Sadrian    HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Disabling MIB counters\n", __func__);
160250003Sadrian
161250003Sadrian    OS_REG_WRITE(ah, AR_MIBC,  AR_MIBC_FMC | AR_MIBC_CMC);
162250003Sadrian
163250003Sadrian    /* Clear the mib counters and save them in the stats */
164250003Sadrian    ar9300_update_mib_mac_stats(ah);
165250003Sadrian
166250003Sadrian    OS_REG_WRITE(ah, AR_FILT_OFDM, 0);
167250003Sadrian    OS_REG_WRITE(ah, AR_FILT_CCK, 0);
168250003Sadrian}
169250003Sadrian
170250003Sadrian/*
171250003Sadrian * This routine returns the index into the ani_state array that
172250003Sadrian * corresponds to the channel in *chan.  If no match is found and the
173250003Sadrian * array is still not fully utilized, a new entry is created for the
174250003Sadrian * channel.  We assume the attach function has already initialized the
175250003Sadrian * ah_ani values and only the channel field needs to be set.
176250003Sadrian */
177250003Sadrianstatic int
178250008Sadrianar9300_get_ani_channel_index(struct ath_hal *ah,
179250008Sadrian  const struct ieee80211_channel *chan)
180250003Sadrian{
181250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
182250003Sadrian    int i;
183250003Sadrian
184250003Sadrian    for (i = 0; i < ARRAY_LENGTH(ahp->ah_ani); i++) {
185250008Sadrian        /* XXX this doesn't distinguish between 20/40 channels */
186250008Sadrian        if (ahp->ah_ani[i].c.ic_freq == chan->ic_freq) {
187250003Sadrian            return i;
188250003Sadrian        }
189250008Sadrian        if (ahp->ah_ani[i].c.ic_freq == 0) {
190250008Sadrian            ahp->ah_ani[i].c.ic_freq = chan->ic_freq;
191250008Sadrian            ahp->ah_ani[i].c.ic_flags = chan->ic_flags;
192250003Sadrian            return i;
193250003Sadrian        }
194250003Sadrian    }
195250003Sadrian    /* XXX statistic */
196250003Sadrian    HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
197250003Sadrian        "%s: No more channel states left. Using channel 0\n", __func__);
198250003Sadrian    return 0;        /* XXX gotta return something valid */
199250003Sadrian}
200250003Sadrian
201250003Sadrian/*
202250003Sadrian * Return the current ANI state of the channel we're on
203250003Sadrian */
204250003Sadrianstruct ar9300_ani_state *
205250003Sadrianar9300_ani_get_current_state(struct ath_hal *ah)
206250003Sadrian{
207250003Sadrian    return AH9300(ah)->ah_curani;
208250003Sadrian}
209250003Sadrian
210250003Sadrian/*
211250003Sadrian * Return the current statistics.
212250003Sadrian */
213280941SadrianHAL_ANI_STATS *
214250003Sadrianar9300_ani_get_current_stats(struct ath_hal *ah)
215250003Sadrian{
216250003Sadrian    return &AH9300(ah)->ah_stats;
217250003Sadrian}
218250003Sadrian
219250003Sadrian/*
220250003Sadrian * Setup ANI handling.  Sets all thresholds and levels to default level AND
221250003Sadrian * resets the channel statistics
222250003Sadrian */
223250003Sadrian
224250003Sadrianvoid
225250003Sadrianar9300_ani_attach(struct ath_hal *ah)
226250003Sadrian{
227250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
228250003Sadrian    int i;
229250003Sadrian
230250003Sadrian    OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani));
231250003Sadrian    for (i = 0; i < ARRAY_LENGTH(ahp->ah_ani); i++) {
232250003Sadrian        ahp->ah_ani[i].ofdm_trig_high = HAL_ANI_OFDM_TRIG_HIGH;
233250003Sadrian        ahp->ah_ani[i].ofdm_trig_low = HAL_ANI_OFDM_TRIG_LOW;
234250003Sadrian        ahp->ah_ani[i].cck_trig_high = HAL_ANI_CCK_TRIG_HIGH;
235250003Sadrian        ahp->ah_ani[i].cck_trig_low = HAL_ANI_CCK_TRIG_LOW;
236250003Sadrian        ahp->ah_ani[i].rssi_thr_high = HAL_ANI_RSSI_THR_HIGH;
237250003Sadrian        ahp->ah_ani[i].rssi_thr_low = HAL_ANI_RSSI_THR_LOW;
238250003Sadrian        ahp->ah_ani[i].ofdm_noise_immunity_level = HAL_ANI_OFDM_DEF_LEVEL;
239250003Sadrian        ahp->ah_ani[i].cck_noise_immunity_level = HAL_ANI_CCK_DEF_LEVEL;
240250003Sadrian        ahp->ah_ani[i].ofdm_weak_sig_detect_off = !HAL_ANI_USE_OFDM_WEAK_SIG;
241250003Sadrian        ahp->ah_ani[i].spur_immunity_level = HAL_ANI_DEF_SPUR_IMMUNE_LVL;
242250003Sadrian        ahp->ah_ani[i].firstep_level = HAL_ANI_DEF_FIRSTEP_LVL;
243250003Sadrian        ahp->ah_ani[i].mrc_cck_off = !HAL_ANI_ENABLE_MRC_CCK;
244250003Sadrian        ahp->ah_ani[i].ofdms_turn = AH_TRUE;
245250003Sadrian        ahp->ah_ani[i].must_restore = AH_FALSE;
246250003Sadrian    }
247250003Sadrian
248250003Sadrian    /*
249250003Sadrian     * Since we expect some ongoing maintenance on the tables,
250250003Sadrian     * let's sanity check here.
251250003Sadrian     * The default level should not modify INI setting.
252250003Sadrian     */
253250003Sadrian    HALASSERT(firstep_table[HAL_ANI_DEF_FIRSTEP_LVL] == 0);
254250003Sadrian    HALASSERT(cycpwr_thr1_table[HAL_ANI_DEF_SPUR_IMMUNE_LVL] == 0);
255250003Sadrian    HALASSERT(
256250003Sadrian        ofdm_level_table[HAL_ANI_OFDM_DEF_LEVEL].fir_step_level ==
257250003Sadrian        HAL_ANI_DEF_FIRSTEP_LVL);
258250003Sadrian    HALASSERT(
259250003Sadrian        ofdm_level_table[HAL_ANI_OFDM_DEF_LEVEL].spur_immunity_level ==
260250003Sadrian        HAL_ANI_DEF_SPUR_IMMUNE_LVL);
261250003Sadrian    HALASSERT(
262250003Sadrian        cck_level_table[HAL_ANI_CCK_DEF_LEVEL].fir_step_level ==
263250003Sadrian        HAL_ANI_DEF_FIRSTEP_LVL);
264250003Sadrian
265250003Sadrian    /* Initialize and enable MIB Counters */
266250003Sadrian    OS_REG_WRITE(ah, AR_PHY_ERR_1, 0);
267250003Sadrian    OS_REG_WRITE(ah, AR_PHY_ERR_2, 0);
268250003Sadrian    ar9300_enable_mib_counters(ah);
269250003Sadrian
270250003Sadrian    ahp->ah_ani_period = HAL_ANI_PERIOD;
271250008Sadrian    if (ah->ah_config.ath_hal_enable_ani) {
272250003Sadrian        ahp->ah_proc_phy_err |= HAL_PROCESS_ANI;
273250003Sadrian    }
274250003Sadrian}
275250003Sadrian
276250003Sadrian/*
277250003Sadrian * Cleanup any ANI state setup.
278250003Sadrian */
279250003Sadrianvoid
280250003Sadrianar9300_ani_detach(struct ath_hal *ah)
281250003Sadrian{
282250003Sadrian    HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Detaching Ani\n", __func__);
283250003Sadrian    ar9300_disable_mib_counters(ah);
284250003Sadrian    OS_REG_WRITE(ah, AR_PHY_ERR_1, 0);
285250003Sadrian    OS_REG_WRITE(ah, AR_PHY_ERR_2, 0);
286250003Sadrian}
287250003Sadrian
288250003Sadrian/*
289250003Sadrian * Initialize the ANI register values with default (ini) values.
290250003Sadrian * This routine is called during a (full) hardware reset after
291250003Sadrian * all the registers are initialised from the INI.
292250003Sadrian */
293250003Sadrianvoid
294250003Sadrianar9300_ani_init_defaults(struct ath_hal *ah, HAL_HT_MACMODE macmode)
295250003Sadrian{
296250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
297250003Sadrian    struct ar9300_ani_state *ani_state;
298250008Sadrian    const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
299250003Sadrian    int index;
300250003Sadrian    u_int32_t val;
301250003Sadrian
302250003Sadrian    HALASSERT(chan != AH_NULL);
303250003Sadrian    index = ar9300_get_ani_channel_index(ah, chan);
304250003Sadrian    ani_state = &ahp->ah_ani[index];
305250003Sadrian    ahp->ah_curani = ani_state;
306250003Sadrian
307250003Sadrian    HALDEBUG(ah, HAL_DEBUG_ANI,
308250003Sadrian        "%s: ver %d.%d opmode %u chan %d Mhz/0x%x macmode %d\n",
309250003Sadrian        __func__, AH_PRIVATE(ah)->ah_macVersion, AH_PRIVATE(ah)->ah_macRev,
310250008Sadrian        AH_PRIVATE(ah)->ah_opmode, chan->ic_freq, chan->ic_flags, macmode);
311250003Sadrian
312250003Sadrian    val = OS_REG_READ(ah, AR_PHY_SFCORR);
313250003Sadrian    ani_state->ini_def.m1_thresh = MS(val, AR_PHY_SFCORR_M1_THRESH);
314250003Sadrian    ani_state->ini_def.m2_thresh = MS(val, AR_PHY_SFCORR_M2_THRESH);
315250003Sadrian    ani_state->ini_def.m2_count_thr = MS(val, AR_PHY_SFCORR_M2COUNT_THR);
316250003Sadrian
317250003Sadrian    val = OS_REG_READ(ah, AR_PHY_SFCORR_LOW);
318250003Sadrian    ani_state->ini_def.m1_thresh_low =
319250003Sadrian        MS(val, AR_PHY_SFCORR_LOW_M1_THRESH_LOW);
320250003Sadrian    ani_state->ini_def.m2_thresh_low =
321250003Sadrian        MS(val, AR_PHY_SFCORR_LOW_M2_THRESH_LOW);
322250003Sadrian    ani_state->ini_def.m2_count_thr_low =
323250003Sadrian        MS(val, AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW);
324250003Sadrian
325250003Sadrian    val = OS_REG_READ(ah, AR_PHY_SFCORR_EXT);
326250003Sadrian    ani_state->ini_def.m1_thresh_ext = MS(val, AR_PHY_SFCORR_EXT_M1_THRESH);
327250003Sadrian    ani_state->ini_def.m2_thresh_ext = MS(val, AR_PHY_SFCORR_EXT_M2_THRESH);
328250003Sadrian    ani_state->ini_def.m1_thresh_low_ext =
329250003Sadrian        MS(val, AR_PHY_SFCORR_EXT_M1_THRESH_LOW);
330250003Sadrian    ani_state->ini_def.m2_thresh_low_ext =
331250003Sadrian        MS(val, AR_PHY_SFCORR_EXT_M2_THRESH_LOW);
332250003Sadrian
333250003Sadrian    ani_state->ini_def.firstep =
334250003Sadrian        OS_REG_READ_FIELD(ah, AR_PHY_FIND_SIG, AR_PHY_FIND_SIG_FIRSTEP);
335250003Sadrian    ani_state->ini_def.firstep_low =
336250003Sadrian        OS_REG_READ_FIELD(
337250003Sadrian            ah, AR_PHY_FIND_SIG_LOW, AR_PHY_FIND_SIG_LOW_FIRSTEP_LOW);
338250003Sadrian    ani_state->ini_def.cycpwr_thr1 =
339250003Sadrian        OS_REG_READ_FIELD(ah, AR_PHY_TIMING5, AR_PHY_TIMING5_CYCPWR_THR1);
340250003Sadrian    ani_state->ini_def.cycpwr_thr1_ext =
341250003Sadrian        OS_REG_READ_FIELD(ah, AR_PHY_EXT_CCA, AR_PHY_EXT_CYCPWR_THR1);
342250003Sadrian
343250003Sadrian    /* these levels just got reset to defaults by the INI */
344250003Sadrian    ani_state->spur_immunity_level = HAL_ANI_DEF_SPUR_IMMUNE_LVL;
345250003Sadrian    ani_state->firstep_level = HAL_ANI_DEF_FIRSTEP_LVL;
346250003Sadrian    ani_state->ofdm_weak_sig_detect_off = !HAL_ANI_USE_OFDM_WEAK_SIG;
347250003Sadrian    ani_state->mrc_cck_off = !HAL_ANI_ENABLE_MRC_CCK;
348250003Sadrian
349250003Sadrian    ani_state->cycle_count = 0;
350250003Sadrian}
351250003Sadrian
352250003Sadrian/*
353250003Sadrian * Set the ANI settings to match an OFDM level.
354250003Sadrian */
355250003Sadrianstatic void
356250003Sadrianar9300_ani_set_odfm_noise_immunity_level(struct ath_hal *ah,
357250003Sadrian                                   u_int8_t ofdm_noise_immunity_level)
358250003Sadrian{
359250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
360250003Sadrian    struct ar9300_ani_state *ani_state = ahp->ah_curani;
361250003Sadrian
362250003Sadrian    ani_state->rssi = BEACON_RSSI(ahp);
363250003Sadrian    HALDEBUG(ah, HAL_DEBUG_ANI,
364250003Sadrian        "**** %s: ofdmlevel %d=>%d, rssi=%d[lo=%d hi=%d]\n", __func__,
365250003Sadrian        ani_state->ofdm_noise_immunity_level, ofdm_noise_immunity_level,
366250003Sadrian        ani_state->rssi, ani_state->rssi_thr_low, ani_state->rssi_thr_high);
367250003Sadrian
368250003Sadrian    ani_state->ofdm_noise_immunity_level = ofdm_noise_immunity_level;
369250003Sadrian
370250003Sadrian    if (ani_state->spur_immunity_level !=
371250003Sadrian        ofdm_level_table[ofdm_noise_immunity_level].spur_immunity_level)
372250003Sadrian    {
373250003Sadrian        ar9300_ani_control(
374250003Sadrian            ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
375250003Sadrian            ofdm_level_table[ofdm_noise_immunity_level].spur_immunity_level);
376250003Sadrian    }
377250003Sadrian
378250003Sadrian    if (ani_state->firstep_level !=
379250003Sadrian            ofdm_level_table[ofdm_noise_immunity_level].fir_step_level &&
380250003Sadrian        ofdm_level_table[ofdm_noise_immunity_level].fir_step_level >=
381250003Sadrian            cck_level_table[ani_state->cck_noise_immunity_level].fir_step_level)
382250003Sadrian    {
383250003Sadrian        ar9300_ani_control(
384250003Sadrian            ah, HAL_ANI_FIRSTEP_LEVEL,
385250003Sadrian            ofdm_level_table[ofdm_noise_immunity_level].fir_step_level);
386250003Sadrian    }
387250003Sadrian
388250003Sadrian    if ((AH_PRIVATE(ah)->ah_opmode != HAL_M_STA ||
389250003Sadrian        ani_state->rssi <= ani_state->rssi_thr_high))
390250003Sadrian    {
391250003Sadrian        if (ani_state->ofdm_weak_sig_detect_off) {
392250003Sadrian            /*
393250003Sadrian             * force on ofdm weak sig detect.
394250003Sadrian             */
395250003Sadrian            ar9300_ani_control(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION, AH_TRUE);
396250003Sadrian        }
397250003Sadrian    } else if (ani_state->ofdm_weak_sig_detect_off ==
398250003Sadrian               ofdm_level_table[ofdm_noise_immunity_level].ofdm_weak_signal_on)
399250003Sadrian    {
400250003Sadrian        ar9300_ani_control(
401250003Sadrian            ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
402250003Sadrian            ofdm_level_table[ofdm_noise_immunity_level].ofdm_weak_signal_on);
403250003Sadrian    }
404250003Sadrian}
405250003Sadrian
406250003Sadrian/*
407250003Sadrian * Set the ANI settings to match a CCK level.
408250003Sadrian */
409250003Sadrianstatic void
410250003Sadrianar9300_ani_set_cck_noise_immunity_level(struct ath_hal *ah,
411250003Sadrian                                  u_int8_t cck_noise_immunity_level)
412250003Sadrian{
413250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
414250003Sadrian    struct ar9300_ani_state *ani_state = ahp->ah_curani;
415250003Sadrian    int level;
416250003Sadrian
417250003Sadrian    ani_state->rssi = BEACON_RSSI(ahp);
418250003Sadrian    HALDEBUG(ah, HAL_DEBUG_ANI,
419250003Sadrian        "**** %s: ccklevel %d=>%d, rssi=%d[lo=%d hi=%d]\n",
420250003Sadrian        __func__, ani_state->cck_noise_immunity_level, cck_noise_immunity_level,
421250003Sadrian        ani_state->rssi, ani_state->rssi_thr_low, ani_state->rssi_thr_high);
422250003Sadrian
423250003Sadrian    if (AH_PRIVATE(ah)->ah_opmode == HAL_M_STA &&
424250003Sadrian        ani_state->rssi <= ani_state->rssi_thr_low &&
425250003Sadrian        cck_noise_immunity_level > HAL_ANI_CCK_MAX_LEVEL_LOW_RSSI)
426250003Sadrian    {
427250003Sadrian        cck_noise_immunity_level = HAL_ANI_CCK_MAX_LEVEL_LOW_RSSI;
428250003Sadrian    }
429250003Sadrian
430250003Sadrian    ani_state->cck_noise_immunity_level = cck_noise_immunity_level;
431250003Sadrian
432250003Sadrian    level = ani_state->ofdm_noise_immunity_level;
433250003Sadrian    if (ani_state->firstep_level !=
434250003Sadrian            cck_level_table[cck_noise_immunity_level].fir_step_level &&
435250003Sadrian        cck_level_table[cck_noise_immunity_level].fir_step_level >=
436250003Sadrian            ofdm_level_table[level].fir_step_level)
437250003Sadrian    {
438250003Sadrian        ar9300_ani_control(
439250003Sadrian            ah, HAL_ANI_FIRSTEP_LEVEL,
440250003Sadrian            cck_level_table[cck_noise_immunity_level].fir_step_level);
441250003Sadrian    }
442250003Sadrian
443250003Sadrian    if (ani_state->mrc_cck_off ==
444250003Sadrian        cck_level_table[cck_noise_immunity_level].mrc_cck_on)
445250003Sadrian    {
446250003Sadrian        ar9300_ani_control(
447250003Sadrian            ah, HAL_ANI_MRC_CCK,
448250003Sadrian            cck_level_table[cck_noise_immunity_level].mrc_cck_on);
449250003Sadrian    }
450250003Sadrian}
451250003Sadrian
452250003Sadrian/*
453250003Sadrian * Control Adaptive Noise Immunity Parameters
454250003Sadrian */
455250003SadrianHAL_BOOL
456250003Sadrianar9300_ani_control(struct ath_hal *ah, HAL_ANI_CMD cmd, int param)
457250003Sadrian{
458250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
459250003Sadrian    struct ar9300_ani_state *ani_state = ahp->ah_curani;
460250008Sadrian    const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
461250003Sadrian    int32_t value, value2;
462250003Sadrian    u_int level = param;
463250003Sadrian    u_int is_on;
464250003Sadrian
465250008Sadrian    if (chan == NULL && cmd != HAL_ANI_MODE) {
466250008Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
467250003Sadrian            "%s: ignoring cmd 0x%02x - no channel\n", __func__, cmd);
468250008Sadrian        return AH_FALSE;
469250008Sadrian    }
470250003Sadrian
471250003Sadrian    switch (cmd & ahp->ah_ani_function) {
472250003Sadrian    case HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION:
473250003Sadrian        {
474250003Sadrian            int m1_thresh_low, m2_thresh_low;
475250003Sadrian            int m1_thresh, m2_thresh;
476250003Sadrian            int m2_count_thr, m2_count_thr_low;
477250003Sadrian            int m1_thresh_low_ext, m2_thresh_low_ext;
478250003Sadrian            int m1_thresh_ext, m2_thresh_ext;
479250003Sadrian            /*
480250003Sadrian             * is_on == 1 means ofdm weak signal detection is ON
481250003Sadrian             * (default, less noise imm)
482250003Sadrian             * is_on == 0 means ofdm weak signal detection is OFF
483250003Sadrian             * (more noise imm)
484250003Sadrian             */
485250003Sadrian            is_on = param ? 1 : 0;
486250003Sadrian
487277303Sadrian            if (AR_SREV_JUPITER(ah) || AR_SREV_APHRODITE(ah))
488277303Sadrian                goto skip_ws_det;
489277303Sadrian
490250003Sadrian            /*
491250003Sadrian             * make register setting for default (weak sig detect ON)
492250003Sadrian             * come from INI file
493250003Sadrian             */
494250003Sadrian            m1_thresh_low    = is_on ?
495250003Sadrian                ani_state->ini_def.m1_thresh_low    : m1_thresh_low_off;
496250003Sadrian            m2_thresh_low    = is_on ?
497250003Sadrian                ani_state->ini_def.m2_thresh_low    : m2_thresh_low_off;
498250003Sadrian            m1_thresh       = is_on ?
499250003Sadrian                ani_state->ini_def.m1_thresh       : m1_thresh_off;
500250003Sadrian            m2_thresh       = is_on ?
501250003Sadrian                ani_state->ini_def.m2_thresh       : m2_thresh_off;
502250003Sadrian            m2_count_thr     = is_on ?
503250003Sadrian                ani_state->ini_def.m2_count_thr     : m2_count_thr_off;
504250003Sadrian            m2_count_thr_low  = is_on ?
505250003Sadrian                ani_state->ini_def.m2_count_thr_low  : m2_count_thr_low_off;
506250003Sadrian            m1_thresh_low_ext = is_on ?
507250003Sadrian                ani_state->ini_def.m1_thresh_low_ext : m1_thresh_low_ext_off;
508250003Sadrian            m2_thresh_low_ext = is_on ?
509250003Sadrian                ani_state->ini_def.m2_thresh_low_ext : m2_thresh_low_ext_off;
510250003Sadrian            m1_thresh_ext    = is_on ?
511250003Sadrian                ani_state->ini_def.m1_thresh_ext    : m1_thresh_ext_off;
512250003Sadrian            m2_thresh_ext    = is_on ?
513250003Sadrian                ani_state->ini_def.m2_thresh_ext    : m2_thresh_ext_off;
514250003Sadrian            OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
515250003Sadrian                AR_PHY_SFCORR_LOW_M1_THRESH_LOW, m1_thresh_low);
516250003Sadrian            OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
517250003Sadrian                AR_PHY_SFCORR_LOW_M2_THRESH_LOW, m2_thresh_low);
518250003Sadrian            OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M1_THRESH,
519250003Sadrian                m1_thresh);
520250003Sadrian            OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M2_THRESH,
521250003Sadrian                m2_thresh);
522250003Sadrian            OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR, AR_PHY_SFCORR_M2COUNT_THR,
523250003Sadrian                m2_count_thr);
524250003Sadrian            OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
525250003Sadrian                AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, m2_count_thr_low);
526250003Sadrian            OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
527250003Sadrian                AR_PHY_SFCORR_EXT_M1_THRESH_LOW, m1_thresh_low_ext);
528250003Sadrian            OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT,
529250003Sadrian                AR_PHY_SFCORR_EXT_M2_THRESH_LOW, m2_thresh_low_ext);
530250003Sadrian            OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_M1_THRESH,
531250003Sadrian                m1_thresh_ext);
532250003Sadrian            OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_EXT, AR_PHY_SFCORR_EXT_M2_THRESH,
533250003Sadrian                m2_thresh_ext);
534277303Sadrianskip_ws_det:
535250003Sadrian            if (is_on) {
536250003Sadrian                OS_REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
537250003Sadrian                    AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
538250003Sadrian            } else {
539250003Sadrian                OS_REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW,
540250003Sadrian                    AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
541250003Sadrian            }
542287302Sadrian            if ((!is_on) != ani_state->ofdm_weak_sig_detect_off) {
543250003Sadrian                HALDEBUG(ah, HAL_DEBUG_ANI,
544250003Sadrian                    "%s: ** ch %d: ofdm weak signal: %s=>%s\n",
545250008Sadrian                    __func__, chan->ic_freq,
546250003Sadrian                    !ani_state->ofdm_weak_sig_detect_off ? "on" : "off",
547250003Sadrian                    is_on ? "on" : "off");
548250003Sadrian                if (is_on) {
549250003Sadrian                    ahp->ah_stats.ast_ani_ofdmon++;
550250003Sadrian                } else {
551250003Sadrian                    ahp->ah_stats.ast_ani_ofdmoff++;
552250003Sadrian                }
553250003Sadrian                ani_state->ofdm_weak_sig_detect_off = !is_on;
554250003Sadrian            }
555250003Sadrian            break;
556250003Sadrian        }
557250003Sadrian    case HAL_ANI_FIRSTEP_LEVEL:
558250003Sadrian        if (level >= ARRAY_LENGTH(firstep_table)) {
559250003Sadrian            HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
560250003Sadrian                "%s: HAL_ANI_FIRSTEP_LEVEL level out of range (%u > %u)\n",
561250003Sadrian                __func__, level, (unsigned) ARRAY_LENGTH(firstep_table));
562250003Sadrian            return AH_FALSE;
563250003Sadrian        }
564250003Sadrian        /*
565250003Sadrian         * make register setting relative to default
566250003Sadrian         * from INI file & cap value
567250003Sadrian         */
568250003Sadrian        value =
569250003Sadrian            firstep_table[level] -
570250003Sadrian            firstep_table[HAL_ANI_DEF_FIRSTEP_LVL] +
571250003Sadrian            ani_state->ini_def.firstep;
572250003Sadrian        if (value < HAL_SIG_FIRSTEP_SETTING_MIN) {
573250003Sadrian            value = HAL_SIG_FIRSTEP_SETTING_MIN;
574250003Sadrian        }
575250003Sadrian        if (value > HAL_SIG_FIRSTEP_SETTING_MAX) {
576250003Sadrian            value = HAL_SIG_FIRSTEP_SETTING_MAX;
577250003Sadrian        }
578250003Sadrian        OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG, AR_PHY_FIND_SIG_FIRSTEP, value);
579250003Sadrian        /*
580250003Sadrian         * we need to set first step low register too
581250003Sadrian         * make register setting relative to default from INI file & cap value
582250003Sadrian         */
583250003Sadrian        value2 =
584250003Sadrian            firstep_table[level] -
585250003Sadrian            firstep_table[HAL_ANI_DEF_FIRSTEP_LVL] +
586250003Sadrian            ani_state->ini_def.firstep_low;
587250003Sadrian        if (value2 < HAL_SIG_FIRSTEP_SETTING_MIN) {
588250003Sadrian            value2 = HAL_SIG_FIRSTEP_SETTING_MIN;
589250003Sadrian        }
590250003Sadrian        if (value2 > HAL_SIG_FIRSTEP_SETTING_MAX) {
591250003Sadrian            value2 = HAL_SIG_FIRSTEP_SETTING_MAX;
592250003Sadrian        }
593250003Sadrian        OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG_LOW,
594250003Sadrian            AR_PHY_FIND_SIG_LOW_FIRSTEP_LOW, value2);
595250003Sadrian
596250003Sadrian        if (level != ani_state->firstep_level) {
597250003Sadrian            HALDEBUG(ah, HAL_DEBUG_ANI,
598250003Sadrian                "%s: ** ch %d: level %d=>%d[def:%d] firstep[level]=%d ini=%d\n",
599250008Sadrian                __func__, chan->ic_freq, ani_state->firstep_level, level,
600250003Sadrian                HAL_ANI_DEF_FIRSTEP_LVL, value, ani_state->ini_def.firstep);
601250003Sadrian            HALDEBUG(ah, HAL_DEBUG_ANI,
602250003Sadrian                "%s: ** ch %d: level %d=>%d[def:%d] "
603250003Sadrian                "firstep_low[level]=%d ini=%d\n",
604250008Sadrian                __func__, chan->ic_freq, ani_state->firstep_level, level,
605250003Sadrian                HAL_ANI_DEF_FIRSTEP_LVL, value2,
606250003Sadrian                ani_state->ini_def.firstep_low);
607250003Sadrian            if (level > ani_state->firstep_level) {
608250003Sadrian                ahp->ah_stats.ast_ani_stepup++;
609250003Sadrian            } else if (level < ani_state->firstep_level) {
610250003Sadrian                ahp->ah_stats.ast_ani_stepdown++;
611250003Sadrian            }
612250003Sadrian            ani_state->firstep_level = level;
613250003Sadrian        }
614250003Sadrian        break;
615250003Sadrian    case HAL_ANI_SPUR_IMMUNITY_LEVEL:
616250003Sadrian        if (level >= ARRAY_LENGTH(cycpwr_thr1_table)) {
617250003Sadrian            HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
618250003Sadrian                "%s: HAL_ANI_SPUR_IMMUNITY_LEVEL level "
619250003Sadrian                "out of range (%u > %u)\n",
620250003Sadrian                __func__, level, (unsigned) ARRAY_LENGTH(cycpwr_thr1_table));
621250003Sadrian            return AH_FALSE;
622250003Sadrian        }
623250003Sadrian        /*
624250003Sadrian         * make register setting relative to default from INI file & cap value
625250003Sadrian         */
626250003Sadrian        value =
627250003Sadrian            cycpwr_thr1_table[level] -
628250003Sadrian            cycpwr_thr1_table[HAL_ANI_DEF_SPUR_IMMUNE_LVL] +
629250003Sadrian            ani_state->ini_def.cycpwr_thr1;
630250003Sadrian        if (value < HAL_SIG_SPUR_IMM_SETTING_MIN) {
631250003Sadrian            value = HAL_SIG_SPUR_IMM_SETTING_MIN;
632250003Sadrian        }
633250003Sadrian        if (value > HAL_SIG_SPUR_IMM_SETTING_MAX) {
634250003Sadrian            value = HAL_SIG_SPUR_IMM_SETTING_MAX;
635250003Sadrian        }
636250003Sadrian        OS_REG_RMW_FIELD(ah, AR_PHY_TIMING5, AR_PHY_TIMING5_CYCPWR_THR1, value);
637250003Sadrian
638250003Sadrian        /*
639250003Sadrian         * set AR_PHY_EXT_CCA for extension channel
640250003Sadrian         * make register setting relative to default from INI file & cap value
641250003Sadrian         */
642250003Sadrian        value2 =
643250003Sadrian            cycpwr_thr1_table[level] -
644250003Sadrian            cycpwr_thr1_table[HAL_ANI_DEF_SPUR_IMMUNE_LVL] +
645250003Sadrian            ani_state->ini_def.cycpwr_thr1_ext;
646250003Sadrian        if (value2 < HAL_SIG_SPUR_IMM_SETTING_MIN) {
647250003Sadrian            value2 = HAL_SIG_SPUR_IMM_SETTING_MIN;
648250003Sadrian        }
649250003Sadrian        if (value2 > HAL_SIG_SPUR_IMM_SETTING_MAX) {
650250003Sadrian            value2 = HAL_SIG_SPUR_IMM_SETTING_MAX;
651250003Sadrian        }
652250003Sadrian        OS_REG_RMW_FIELD(ah, AR_PHY_EXT_CCA, AR_PHY_EXT_CYCPWR_THR1, value2);
653250003Sadrian
654250003Sadrian        if (level != ani_state->spur_immunity_level) {
655250003Sadrian            HALDEBUG(ah, HAL_DEBUG_ANI,
656250003Sadrian                "%s: ** ch %d: level %d=>%d[def:%d] "
657250003Sadrian                "cycpwr_thr1[level]=%d ini=%d\n",
658250008Sadrian                __func__, chan->ic_freq, ani_state->spur_immunity_level, level,
659250003Sadrian                HAL_ANI_DEF_SPUR_IMMUNE_LVL, value,
660250003Sadrian                ani_state->ini_def.cycpwr_thr1);
661250003Sadrian            HALDEBUG(ah, HAL_DEBUG_ANI,
662250003Sadrian                "%s: ** ch %d: level %d=>%d[def:%d] "
663250003Sadrian                "cycpwr_thr1_ext[level]=%d ini=%d\n",
664250008Sadrian                __func__, chan->ic_freq, ani_state->spur_immunity_level, level,
665250003Sadrian                HAL_ANI_DEF_SPUR_IMMUNE_LVL, value2,
666250003Sadrian                ani_state->ini_def.cycpwr_thr1_ext);
667250003Sadrian            if (level > ani_state->spur_immunity_level) {
668250003Sadrian                ahp->ah_stats.ast_ani_spurup++;
669250003Sadrian            } else if (level < ani_state->spur_immunity_level) {
670250003Sadrian                ahp->ah_stats.ast_ani_spurdown++;
671250003Sadrian            }
672250003Sadrian            ani_state->spur_immunity_level = level;
673250003Sadrian        }
674250003Sadrian        break;
675250003Sadrian    case HAL_ANI_MRC_CCK:
676250003Sadrian        /*
677250003Sadrian         * is_on == 1 means MRC CCK ON (default, less noise imm)
678250003Sadrian         * is_on == 0 means MRC CCK is OFF (more noise imm)
679250003Sadrian         */
680250003Sadrian        is_on = param ? 1 : 0;
681250003Sadrian        if (!AR_SREV_POSEIDON(ah)) {
682250003Sadrian            OS_REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL,
683250003Sadrian                AR_PHY_MRC_CCK_ENABLE, is_on);
684250003Sadrian            OS_REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL,
685250003Sadrian                AR_PHY_MRC_CCK_MUX_REG, is_on);
686250003Sadrian        }
687287302Sadrian        if ((!is_on) != ani_state->mrc_cck_off) {
688250003Sadrian            HALDEBUG(ah, HAL_DEBUG_ANI,
689250008Sadrian                "%s: ** ch %d: MRC CCK: %s=>%s\n", __func__, chan->ic_freq,
690250003Sadrian                !ani_state->mrc_cck_off ? "on" : "off", is_on ? "on" : "off");
691250003Sadrian            if (is_on) {
692250003Sadrian                ahp->ah_stats.ast_ani_ccklow++;
693250003Sadrian            } else {
694250003Sadrian                ahp->ah_stats.ast_ani_cckhigh++;
695250003Sadrian            }
696250003Sadrian            ani_state->mrc_cck_off = !is_on;
697250003Sadrian        }
698250003Sadrian        break;
699250003Sadrian    case HAL_ANI_PRESENT:
700250003Sadrian        break;
701250003Sadrian#ifdef AH_PRIVATE_DIAG
702250003Sadrian    case HAL_ANI_MODE:
703250003Sadrian        if (param == 0) {
704250003Sadrian            ahp->ah_proc_phy_err &= ~HAL_PROCESS_ANI;
705250003Sadrian            /* Turn off HW counters if we have them */
706250003Sadrian            ar9300_ani_detach(ah);
707250003Sadrian            if (AH_PRIVATE(ah)->ah_curchan == NULL) {
708250003Sadrian                return AH_TRUE;
709250003Sadrian            }
710250003Sadrian            /* if we're turning off ANI, reset regs back to INI settings */
711250082Sadrian            if (ah->ah_config.ath_hal_enable_ani) {
712250003Sadrian                HAL_ANI_CMD savefunc = ahp->ah_ani_function;
713250003Sadrian                /* temporarly allow all functions so we can reset */
714250003Sadrian                ahp->ah_ani_function = HAL_ANI_ALL;
715250003Sadrian                HALDEBUG(ah, HAL_DEBUG_ANI,
716250003Sadrian                    "%s: disable all ANI functions\n", __func__);
717250003Sadrian                ar9300_ani_set_odfm_noise_immunity_level(
718250003Sadrian                    ah, HAL_ANI_OFDM_DEF_LEVEL);
719250003Sadrian                ar9300_ani_set_cck_noise_immunity_level(
720250003Sadrian                    ah, HAL_ANI_CCK_DEF_LEVEL);
721250003Sadrian                ahp->ah_ani_function = savefunc;
722250003Sadrian            }
723250003Sadrian        } else {            /* normal/auto mode */
724250003Sadrian            HALDEBUG(ah, HAL_DEBUG_ANI, "%s: enabled\n", __func__);
725250003Sadrian            ahp->ah_proc_phy_err |= HAL_PROCESS_ANI;
726250003Sadrian            if (AH_PRIVATE(ah)->ah_curchan == NULL) {
727250003Sadrian                return AH_TRUE;
728250003Sadrian            }
729250003Sadrian            ar9300_enable_mib_counters(ah);
730250003Sadrian            ar9300_ani_reset(ah, AH_FALSE);
731250003Sadrian            ani_state = ahp->ah_curani;
732250003Sadrian        }
733250003Sadrian        HALDEBUG(ah, HAL_DEBUG_ANI, "5 ANC: ahp->ah_proc_phy_err %x \n",
734250003Sadrian                 ahp->ah_proc_phy_err);
735250003Sadrian        break;
736250003Sadrian    case HAL_ANI_PHYERR_RESET:
737250003Sadrian        ahp->ah_stats.ast_ani_ofdmerrs = 0;
738250003Sadrian        ahp->ah_stats.ast_ani_cckerrs = 0;
739250003Sadrian        break;
740250003Sadrian#endif /* AH_PRIVATE_DIAG */
741250003Sadrian    default:
742250003Sadrian#if HAL_ANI_DEBUG
743250003Sadrian        HALDEBUG(ah, HAL_DEBUG_ANI,
744250003Sadrian            "%s: invalid cmd 0x%02x (allowed=0x%02x)\n",
745250003Sadrian            __func__, cmd, ahp->ah_ani_function);
746250003Sadrian#endif
747250003Sadrian        return AH_FALSE;
748250003Sadrian    }
749250003Sadrian
750250003Sadrian#if HAL_ANI_DEBUG
751250003Sadrian    HALDEBUG(ah, HAL_DEBUG_ANI,
752250003Sadrian        "%s: ANI parameters: SI=%d, ofdm_ws=%s FS=%d MRCcck=%s listen_time=%d "
753250003Sadrian        "CC=%d listen=%d ofdm_errs=%d cck_errs=%d\n",
754250003Sadrian        __func__, ani_state->spur_immunity_level,
755250003Sadrian        !ani_state->ofdm_weak_sig_detect_off ? "on" : "off",
756250003Sadrian        ani_state->firstep_level, !ani_state->mrc_cck_off ? "on" : "off",
757250003Sadrian        ani_state->listen_time, ani_state->cycle_count,
758250003Sadrian        ani_state->listen_time, ani_state->ofdm_phy_err_count,
759250003Sadrian        ani_state->cck_phy_err_count);
760250003Sadrian#endif
761250003Sadrian
762250003Sadrian#ifndef REMOVE_PKT_LOG
763250003Sadrian    /* do pktlog */
764250003Sadrian    {
765250003Sadrian        struct log_ani log_data;
766250003Sadrian
767250003Sadrian        /* Populate the ani log record */
768250003Sadrian        log_data.phy_stats_disable = DO_ANI(ah);
769250003Sadrian        log_data.noise_immun_lvl = ani_state->ofdm_noise_immunity_level;
770250003Sadrian        log_data.spur_immun_lvl = ani_state->spur_immunity_level;
771250003Sadrian        log_data.ofdm_weak_det = ani_state->ofdm_weak_sig_detect_off;
772250003Sadrian        log_data.cck_weak_thr = ani_state->cck_noise_immunity_level;
773250003Sadrian        log_data.fir_lvl = ani_state->firstep_level;
774250003Sadrian        log_data.listen_time = ani_state->listen_time;
775250003Sadrian        log_data.cycle_count = ani_state->cycle_count;
776250003Sadrian        /* express ofdm_phy_err_count as errors/second */
777250003Sadrian        log_data.ofdm_phy_err_count = ani_state->listen_time ?
778250003Sadrian            ani_state->ofdm_phy_err_count * 1000 / ani_state->listen_time : 0;
779250003Sadrian        /* express cck_phy_err_count as errors/second */
780250003Sadrian        log_data.cck_phy_err_count =  ani_state->listen_time ?
781250003Sadrian            ani_state->cck_phy_err_count * 1000 / ani_state->listen_time  : 0;
782250003Sadrian        log_data.rssi = ani_state->rssi;
783250003Sadrian
784250003Sadrian        /* clear interrupt context flag */
785250003Sadrian        ath_hal_log_ani(AH_PRIVATE(ah)->ah_sc, &log_data, 0);
786250003Sadrian    }
787250003Sadrian#endif
788250003Sadrian
789250003Sadrian    return AH_TRUE;
790250003Sadrian}
791250003Sadrian
792250003Sadrianstatic void
793250003Sadrianar9300_ani_restart(struct ath_hal *ah)
794250003Sadrian{
795250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
796250003Sadrian    struct ar9300_ani_state *ani_state;
797250003Sadrian
798250003Sadrian    if (!DO_ANI(ah)) {
799250003Sadrian        return;
800250003Sadrian    }
801250003Sadrian
802250003Sadrian    ani_state = ahp->ah_curani;
803250003Sadrian
804250003Sadrian    ani_state->listen_time = 0;
805250003Sadrian
806250003Sadrian    OS_REG_WRITE(ah, AR_PHY_ERR_1, 0);
807250003Sadrian    OS_REG_WRITE(ah, AR_PHY_ERR_2, 0);
808250003Sadrian    OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
809250003Sadrian    OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
810250003Sadrian
811250003Sadrian    /* Clear the mib counters and save them in the stats */
812250003Sadrian    ar9300_update_mib_mac_stats(ah);
813250003Sadrian
814250003Sadrian    ani_state->ofdm_phy_err_count = 0;
815250003Sadrian    ani_state->cck_phy_err_count = 0;
816250003Sadrian}
817250003Sadrian
818250003Sadrianstatic void
819250003Sadrianar9300_ani_ofdm_err_trigger(struct ath_hal *ah)
820250003Sadrian{
821250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
822250003Sadrian    struct ar9300_ani_state *ani_state;
823250003Sadrian
824250003Sadrian    if (!DO_ANI(ah)) {
825250003Sadrian        return;
826250003Sadrian    }
827250003Sadrian
828250003Sadrian    ani_state = ahp->ah_curani;
829250003Sadrian
830250003Sadrian    if (ani_state->ofdm_noise_immunity_level < HAL_ANI_OFDM_MAX_LEVEL) {
831250003Sadrian        ar9300_ani_set_odfm_noise_immunity_level(
832250003Sadrian            ah, ani_state->ofdm_noise_immunity_level + 1);
833250003Sadrian    }
834250003Sadrian}
835250003Sadrian
836250003Sadrianstatic void
837250003Sadrianar9300_ani_cck_err_trigger(struct ath_hal *ah)
838250003Sadrian{
839250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
840250003Sadrian    struct ar9300_ani_state *ani_state;
841250003Sadrian
842250003Sadrian    if (!DO_ANI(ah)) {
843250003Sadrian        return;
844250003Sadrian    }
845250003Sadrian
846250003Sadrian    ani_state = ahp->ah_curani;
847250003Sadrian
848250003Sadrian    if (ani_state->cck_noise_immunity_level < HAL_ANI_CCK_MAX_LEVEL) {
849250003Sadrian        ar9300_ani_set_cck_noise_immunity_level(
850250003Sadrian            ah, ani_state->cck_noise_immunity_level + 1);
851250003Sadrian    }
852250003Sadrian}
853250003Sadrian
854250003Sadrian/*
855250003Sadrian * Restore the ANI parameters in the HAL and reset the statistics.
856250003Sadrian * This routine should be called for every hardware reset and for
857250003Sadrian * every channel change.
858250003Sadrian */
859250003Sadrianvoid
860250003Sadrianar9300_ani_reset(struct ath_hal *ah, HAL_BOOL is_scanning)
861250003Sadrian{
862250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
863250003Sadrian    struct ar9300_ani_state *ani_state;
864250008Sadrian    const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
865250008Sadrian    HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);
866250003Sadrian    int index;
867250003Sadrian
868250003Sadrian    HALASSERT(chan != AH_NULL);
869250003Sadrian
870250003Sadrian    if (!DO_ANI(ah)) {
871250003Sadrian        return;
872250003Sadrian    }
873250003Sadrian
874250003Sadrian    /*
875250003Sadrian     * we need to re-point to the correct ANI state since the channel
876250003Sadrian     * may have changed due to a fast channel change
877250003Sadrian    */
878250003Sadrian    index = ar9300_get_ani_channel_index(ah, chan);
879250003Sadrian    ani_state = &ahp->ah_ani[index];
880250003Sadrian    HALASSERT(ani_state != AH_NULL);
881250003Sadrian    ahp->ah_curani = ani_state;
882250003Sadrian
883250003Sadrian    ahp->ah_stats.ast_ani_reset++;
884250003Sadrian
885250003Sadrian    ani_state->phy_noise_spur = 0;
886250003Sadrian
887250003Sadrian    /* only allow a subset of functions in AP mode */
888250003Sadrian    if (AH_PRIVATE(ah)->ah_opmode == HAL_M_HOSTAP) {
889250008Sadrian        if (IS_CHAN_2GHZ(ichan)) {
890250003Sadrian            ahp->ah_ani_function = (HAL_ANI_SPUR_IMMUNITY_LEVEL |
891250003Sadrian                                    HAL_ANI_FIRSTEP_LEVEL |
892250003Sadrian                                    HAL_ANI_MRC_CCK);
893250003Sadrian        } else {
894250003Sadrian            ahp->ah_ani_function = 0;
895250003Sadrian        }
896250003Sadrian    }
897250003Sadrian    /* always allow mode (on/off) to be controlled */
898250003Sadrian    ahp->ah_ani_function |= HAL_ANI_MODE;
899250003Sadrian
900250003Sadrian    if (is_scanning ||
901250003Sadrian        (AH_PRIVATE(ah)->ah_opmode != HAL_M_STA &&
902250003Sadrian         AH_PRIVATE(ah)->ah_opmode != HAL_M_IBSS))
903250003Sadrian    {
904250003Sadrian        /*
905250003Sadrian         * If we're scanning or in AP mode, the defaults (ini) should be
906250003Sadrian         * in place.
907250003Sadrian         * For an AP we assume the historical levels for this channel are
908250003Sadrian         * probably outdated so start from defaults instead.
909250003Sadrian         */
910250003Sadrian        if (ani_state->ofdm_noise_immunity_level != HAL_ANI_OFDM_DEF_LEVEL ||
911250003Sadrian            ani_state->cck_noise_immunity_level != HAL_ANI_CCK_DEF_LEVEL)
912250003Sadrian        {
913250003Sadrian            HALDEBUG(ah, HAL_DEBUG_ANI,
914250003Sadrian                "%s: Restore defaults: opmode %u chan %d Mhz/0x%x "
915250003Sadrian                "is_scanning=%d restore=%d ofdm:%d cck:%d\n",
916250008Sadrian                __func__, AH_PRIVATE(ah)->ah_opmode, chan->ic_freq,
917250008Sadrian                chan->ic_flags, is_scanning, ani_state->must_restore,
918250003Sadrian                ani_state->ofdm_noise_immunity_level,
919250003Sadrian                ani_state->cck_noise_immunity_level);
920250003Sadrian            /*
921250003Sadrian             * for STA/IBSS, we want to restore the historical values later
922250003Sadrian             * (when we're not scanning)
923250003Sadrian             */
924250003Sadrian            if (AH_PRIVATE(ah)->ah_opmode == HAL_M_STA ||
925250003Sadrian                AH_PRIVATE(ah)->ah_opmode == HAL_M_IBSS)
926250003Sadrian            {
927250003Sadrian                ar9300_ani_control(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
928250003Sadrian                    HAL_ANI_DEF_SPUR_IMMUNE_LVL);
929250003Sadrian                ar9300_ani_control(
930250003Sadrian                    ah, HAL_ANI_FIRSTEP_LEVEL, HAL_ANI_DEF_FIRSTEP_LVL);
931250003Sadrian                ar9300_ani_control(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
932250003Sadrian                    HAL_ANI_USE_OFDM_WEAK_SIG);
933250003Sadrian                ar9300_ani_control(ah, HAL_ANI_MRC_CCK, HAL_ANI_ENABLE_MRC_CCK);
934250003Sadrian                ani_state->must_restore = AH_TRUE;
935250003Sadrian            } else {
936250003Sadrian                ar9300_ani_set_odfm_noise_immunity_level(
937250003Sadrian                    ah, HAL_ANI_OFDM_DEF_LEVEL);
938250003Sadrian                ar9300_ani_set_cck_noise_immunity_level(
939250003Sadrian                    ah, HAL_ANI_CCK_DEF_LEVEL);
940250003Sadrian            }
941250003Sadrian        }
942250003Sadrian    } else {
943250003Sadrian        /*
944250003Sadrian         * restore historical levels for this channel
945250003Sadrian         */
946250003Sadrian        HALDEBUG(ah, HAL_DEBUG_ANI,
947250003Sadrian            "%s: Restore history: opmode %u chan %d Mhz/0x%x is_scanning=%d "
948250003Sadrian            "restore=%d ofdm:%d cck:%d\n",
949250008Sadrian            __func__, AH_PRIVATE(ah)->ah_opmode, chan->ic_freq,
950250008Sadrian            chan->ic_flags, is_scanning, ani_state->must_restore,
951250003Sadrian            ani_state->ofdm_noise_immunity_level,
952250003Sadrian            ani_state->cck_noise_immunity_level);
953250003Sadrian        ar9300_ani_set_odfm_noise_immunity_level(
954250003Sadrian            ah, ani_state->ofdm_noise_immunity_level);
955250003Sadrian        ar9300_ani_set_cck_noise_immunity_level(
956250003Sadrian            ah, ani_state->cck_noise_immunity_level);
957250003Sadrian        ani_state->must_restore = AH_FALSE;
958250003Sadrian    }
959250003Sadrian
960250003Sadrian    /* enable phy counters */
961250003Sadrian    ar9300_ani_restart(ah);
962250003Sadrian    OS_REG_WRITE(ah, AR_PHY_ERR_MASK_1, AR_PHY_ERR_OFDM_TIMING);
963250003Sadrian    OS_REG_WRITE(ah, AR_PHY_ERR_MASK_2, AR_PHY_ERR_CCK_TIMING);
964250003Sadrian}
965250003Sadrian
966250003Sadrian/*
967250003Sadrian * Process a MIB interrupt.  We may potentially be invoked because
968250003Sadrian * any of the MIB counters overflow/trigger so don't assume we're
969250003Sadrian * here because a PHY error counter triggered.
970250003Sadrian */
971250003Sadrianvoid
972250003Sadrianar9300_process_mib_intr(struct ath_hal *ah, const HAL_NODE_STATS *stats)
973250003Sadrian{
974250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
975250003Sadrian    u_int32_t phy_cnt1, phy_cnt2;
976250003Sadrian
977250003Sadrian#if 0
978250003Sadrian    HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Processing Mib Intr\n", __func__);
979250003Sadrian#endif
980250003Sadrian
981250003Sadrian    /* Reset these counters regardless */
982250003Sadrian    OS_REG_WRITE(ah, AR_FILT_OFDM, 0);
983250003Sadrian    OS_REG_WRITE(ah, AR_FILT_CCK, 0);
984250003Sadrian    if (!(OS_REG_READ(ah, AR_SLP_MIB_CTRL) & AR_SLP_MIB_PENDING)) {
985250003Sadrian        OS_REG_WRITE(ah, AR_SLP_MIB_CTRL, AR_SLP_MIB_CLEAR);
986250003Sadrian    }
987250003Sadrian
988250003Sadrian    /* Clear the mib counters and save them in the stats */
989250003Sadrian    ar9300_update_mib_mac_stats(ah);
990250003Sadrian    ahp->ah_stats.ast_nodestats = *stats;
991250003Sadrian
992250003Sadrian    if (!DO_ANI(ah)) {
993250003Sadrian        /*
994250003Sadrian         * We must always clear the interrupt cause by resetting
995250003Sadrian         * the phy error regs.
996250003Sadrian         */
997250003Sadrian        OS_REG_WRITE(ah, AR_PHY_ERR_1, 0);
998250003Sadrian        OS_REG_WRITE(ah, AR_PHY_ERR_2, 0);
999250003Sadrian        return;
1000250003Sadrian    }
1001250003Sadrian
1002250003Sadrian    /* NB: these are not reset-on-read */
1003250003Sadrian    phy_cnt1 = OS_REG_READ(ah, AR_PHY_ERR_1);
1004250003Sadrian    phy_cnt2 = OS_REG_READ(ah, AR_PHY_ERR_2);
1005250003Sadrian#if HAL_ANI_DEBUG
1006250003Sadrian    HALDEBUG(ah, HAL_DEBUG_ANI,
1007250003Sadrian        "%s: Errors: OFDM=0x%08x-0x0=%d   CCK=0x%08x-0x0=%d\n",
1008250003Sadrian        __func__, phy_cnt1, phy_cnt1, phy_cnt2, phy_cnt2);
1009250003Sadrian#endif
1010250003Sadrian    if (((phy_cnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) ||
1011250003Sadrian        ((phy_cnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) {
1012250003Sadrian        /* NB: always restart to insure the h/w counters are reset */
1013250003Sadrian        ar9300_ani_restart(ah);
1014250003Sadrian    }
1015250003Sadrian}
1016250003Sadrian
1017250003Sadrian
1018250003Sadrianstatic void
1019250003Sadrianar9300_ani_lower_immunity(struct ath_hal *ah)
1020250003Sadrian{
1021250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
1022250003Sadrian    struct ar9300_ani_state *ani_state = ahp->ah_curani;
1023250003Sadrian
1024250003Sadrian    if (ani_state->ofdm_noise_immunity_level > 0 &&
1025250003Sadrian        (ani_state->ofdms_turn || ani_state->cck_noise_immunity_level == 0)) {
1026250003Sadrian        /*
1027250003Sadrian         * lower OFDM noise immunity
1028250003Sadrian         */
1029250003Sadrian        ar9300_ani_set_odfm_noise_immunity_level(
1030250003Sadrian            ah, ani_state->ofdm_noise_immunity_level - 1);
1031250003Sadrian
1032250003Sadrian        /*
1033250003Sadrian         * only lower either OFDM or CCK errors per turn
1034250003Sadrian         * we lower the other one next time
1035250003Sadrian         */
1036250003Sadrian        return;
1037250003Sadrian    }
1038250003Sadrian
1039250003Sadrian    if (ani_state->cck_noise_immunity_level > 0) {
1040250003Sadrian        /*
1041250003Sadrian         * lower CCK noise immunity
1042250003Sadrian         */
1043250003Sadrian        ar9300_ani_set_cck_noise_immunity_level(
1044250003Sadrian            ah, ani_state->cck_noise_immunity_level - 1);
1045250003Sadrian    }
1046250003Sadrian}
1047250003Sadrian
1048250003Sadrian/* convert HW counter values to ms using mode specifix clock rate */
1049250008Sadrian//#define CLOCK_RATE(_ah)  (ath_hal_chan_2_clock_rate_mhz(_ah) * 1000)
1050250008Sadrian#define CLOCK_RATE(_ah)  (ath_hal_mac_clks(ah, 1000))
1051250003Sadrian
1052250003Sadrian/*
1053250003Sadrian * Return an approximation of the time spent ``listening'' by
1054250003Sadrian * deducting the cycles spent tx'ing and rx'ing from the total
1055250003Sadrian * cycle count since our last call.  A return value <0 indicates
1056250003Sadrian * an invalid/inconsistent time.
1057250003Sadrian */
1058250003Sadrianstatic int32_t
1059250003Sadrianar9300_ani_get_listen_time(struct ath_hal *ah, HAL_ANISTATS *ani_stats)
1060250003Sadrian{
1061250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
1062250003Sadrian    struct ar9300_ani_state *ani_state;
1063250003Sadrian    u_int32_t tx_frame_count, rx_frame_count, cycle_count;
1064280829Sadrian    u_int32_t rx_busy_count, rx_ext_busy_count;
1065250003Sadrian    int32_t listen_time;
1066250003Sadrian
1067250003Sadrian    tx_frame_count = OS_REG_READ(ah, AR_TFCNT);
1068250003Sadrian    rx_frame_count = OS_REG_READ(ah, AR_RFCNT);
1069280829Sadrian    rx_busy_count = OS_REG_READ(ah, AR_RCCNT);
1070280829Sadrian    rx_ext_busy_count = OS_REG_READ(ah, AR_EXTRCCNT);
1071250003Sadrian    cycle_count = OS_REG_READ(ah, AR_CCCNT);
1072250003Sadrian
1073250003Sadrian    ani_state = ahp->ah_curani;
1074250003Sadrian    if (ani_state->cycle_count == 0 || ani_state->cycle_count > cycle_count) {
1075250003Sadrian        /*
1076250003Sadrian         * Cycle counter wrap (or initial call); it's not possible
1077250003Sadrian         * to accurately calculate a value because the registers
1078250003Sadrian         * right shift rather than wrap--so punt and return 0.
1079250003Sadrian         */
1080250003Sadrian        listen_time = 0;
1081250003Sadrian        ahp->ah_stats.ast_ani_lzero++;
1082250003Sadrian#if HAL_ANI_DEBUG
1083250003Sadrian        HALDEBUG(ah, HAL_DEBUG_ANI,
1084250003Sadrian            "%s: 1st call: ani_state->cycle_count=%d\n",
1085250003Sadrian            __func__, ani_state->cycle_count);
1086250003Sadrian#endif
1087250003Sadrian    } else {
1088250003Sadrian        int32_t ccdelta = cycle_count - ani_state->cycle_count;
1089250003Sadrian        int32_t rfdelta = rx_frame_count - ani_state->rx_frame_count;
1090250003Sadrian        int32_t tfdelta = tx_frame_count - ani_state->tx_frame_count;
1091280829Sadrian        int32_t rcdelta = rx_busy_count - ani_state->rx_busy_count;
1092280829Sadrian        int32_t extrcdelta = rx_ext_busy_count - ani_state->rx_ext_busy_count;
1093250003Sadrian        listen_time = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE(ah);
1094280829Sadrian//#if HAL_ANI_DEBUG
1095250003Sadrian        HALDEBUG(ah, HAL_DEBUG_ANI,
1096280829Sadrian            "%s: cyclecount=%d, rfcount=%d, tfcount=%d, rcdelta=%d, extrcdelta=%d, listen_time=%d "
1097250003Sadrian            "CLOCK_RATE=%d\n",
1098280829Sadrian            __func__, ccdelta, rfdelta, tfdelta, rcdelta, extrcdelta,
1099280829Sadrian            listen_time, CLOCK_RATE(ah));
1100280829Sadrian//#endif
1101280829Sadrian            /* Populate as appropriate */
1102280829Sadrian            ani_stats->cyclecnt_diff = ccdelta;
1103280829Sadrian            ani_stats->rxclr_cnt = rcdelta;
1104280829Sadrian            ani_stats->txframecnt_diff = tfdelta;
1105280829Sadrian            ani_stats->rxframecnt_diff = rfdelta;
1106280829Sadrian            ani_stats->extrxclr_cnt = extrcdelta;
1107280829Sadrian            ani_stats->listen_time = listen_time;
1108280829Sadrian            ani_stats->valid = AH_TRUE;
1109250003Sadrian    }
1110250003Sadrian    ani_state->cycle_count = cycle_count;
1111250003Sadrian    ani_state->tx_frame_count = tx_frame_count;
1112250003Sadrian    ani_state->rx_frame_count = rx_frame_count;
1113280829Sadrian    ani_state->rx_busy_count = rx_busy_count;
1114280829Sadrian    ani_state->rx_ext_busy_count = rx_ext_busy_count;
1115250003Sadrian    return listen_time;
1116250003Sadrian}
1117250003Sadrian
1118250003Sadrian/*
1119250003Sadrian * Do periodic processing.  This routine is called from a timer
1120250003Sadrian */
1121250003Sadrianvoid
1122250003Sadrianar9300_ani_ar_poll(struct ath_hal *ah, const HAL_NODE_STATS *stats,
1123250008Sadrian                const struct ieee80211_channel *chan, HAL_ANISTATS *ani_stats)
1124250003Sadrian{
1125250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
1126250003Sadrian    struct ar9300_ani_state *ani_state;
1127250003Sadrian    int32_t listen_time;
1128250003Sadrian    u_int32_t ofdm_phy_err_rate, cck_phy_err_rate;
1129250003Sadrian    u_int32_t ofdm_phy_err_cnt, cck_phy_err_cnt;
1130250003Sadrian    HAL_BOOL old_phy_noise_spur;
1131250003Sadrian
1132250003Sadrian    ani_state = ahp->ah_curani;
1133250003Sadrian    ahp->ah_stats.ast_nodestats = *stats;        /* XXX optimize? */
1134250003Sadrian
1135250003Sadrian    if (ani_state == NULL) {
1136250003Sadrian        /* should not happen */
1137250003Sadrian        HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
1138250003Sadrian            "%s: can't poll - no ANI not initialized for this channel\n",
1139250003Sadrian            __func__);
1140250003Sadrian        return;
1141250003Sadrian    }
1142250003Sadrian
1143250003Sadrian    /*
1144250003Sadrian     * ar9300_ani_ar_poll is never called while scanning but we may have been
1145250003Sadrian     * scanning and now just restarted polling.  In this case we need to
1146250003Sadrian     * restore historical values.
1147250003Sadrian     */
1148250003Sadrian    if (ani_state->must_restore) {
1149250003Sadrian        HALDEBUG(ah, HAL_DEBUG_ANI,
1150250003Sadrian            "%s: must restore - calling ar9300_ani_restart\n", __func__);
1151250003Sadrian        ar9300_ani_reset(ah, AH_FALSE);
1152250003Sadrian        return;
1153250003Sadrian    }
1154250003Sadrian
1155250003Sadrian    listen_time = ar9300_ani_get_listen_time(ah, ani_stats);
1156250003Sadrian    if (listen_time <= 0) {
1157250003Sadrian        ahp->ah_stats.ast_ani_lneg++;
1158250003Sadrian        /* restart ANI period if listen_time is invalid */
1159250003Sadrian        HALDEBUG(ah, HAL_DEBUG_ANI,
1160250003Sadrian            "%s: listen_time=%d - calling ar9300_ani_restart\n",
1161250003Sadrian            __func__, listen_time);
1162250003Sadrian        ar9300_ani_restart(ah);
1163250003Sadrian        return;
1164250003Sadrian    }
1165250003Sadrian    /* XXX beware of overflow? */
1166250003Sadrian    ani_state->listen_time += listen_time;
1167250003Sadrian
1168250003Sadrian    /* Clear the mib counters and save them in the stats */
1169250003Sadrian    ar9300_update_mib_mac_stats(ah);
1170250003Sadrian    /* NB: these are not reset-on-read */
1171250003Sadrian    ofdm_phy_err_cnt = OS_REG_READ(ah, AR_PHY_ERR_1);
1172250003Sadrian    cck_phy_err_cnt = OS_REG_READ(ah, AR_PHY_ERR_2);
1173250003Sadrian
1174280829Sadrian    /* Populate HAL_ANISTATS */
1175280829Sadrian    if (ani_stats) {
1176280829Sadrian            ani_stats->cckphyerr_cnt =
1177280829Sadrian               cck_phy_err_cnt - ani_state->cck_phy_err_count;
1178280829Sadrian            ani_stats->ofdmphyerrcnt_diff =
1179280829Sadrian              ofdm_phy_err_cnt - ani_state->ofdm_phy_err_count;
1180280829Sadrian    }
1181250003Sadrian
1182250003Sadrian    /* NB: only use ast_ani_*errs with AH_PRIVATE_DIAG */
1183250003Sadrian    ahp->ah_stats.ast_ani_ofdmerrs +=
1184250003Sadrian        ofdm_phy_err_cnt - ani_state->ofdm_phy_err_count;
1185250003Sadrian    ani_state->ofdm_phy_err_count = ofdm_phy_err_cnt;
1186250003Sadrian
1187250003Sadrian    ahp->ah_stats.ast_ani_cckerrs +=
1188250003Sadrian        cck_phy_err_cnt - ani_state->cck_phy_err_count;
1189250003Sadrian    ani_state->cck_phy_err_count = cck_phy_err_cnt;
1190250003Sadrian
1191250003Sadrian#if HAL_ANI_DEBUG
1192250003Sadrian    HALDEBUG(ah, HAL_DEBUG_ANI,
1193250003Sadrian        "%s: Errors: OFDM=0x%08x-0x0=%d   CCK=0x%08x-0x0=%d\n",
1194250003Sadrian        __func__, ofdm_phy_err_cnt, ofdm_phy_err_cnt,
1195250003Sadrian        cck_phy_err_cnt, cck_phy_err_cnt);
1196250003Sadrian#endif
1197250003Sadrian
1198250003Sadrian    /*
1199250003Sadrian     * If ani is not enabled, return after we've collected
1200250003Sadrian     * statistics
1201250003Sadrian     */
1202250003Sadrian    if (!DO_ANI(ah)) {
1203250003Sadrian        return;
1204250003Sadrian    }
1205250003Sadrian
1206250003Sadrian    ofdm_phy_err_rate =
1207250003Sadrian        ani_state->ofdm_phy_err_count * 1000 / ani_state->listen_time;
1208250003Sadrian    cck_phy_err_rate =
1209250003Sadrian        ani_state->cck_phy_err_count * 1000 / ani_state->listen_time;
1210250003Sadrian
1211250003Sadrian    HALDEBUG(ah, HAL_DEBUG_ANI,
1212250003Sadrian        "%s: listen_time=%d OFDM:%d errs=%d/s CCK:%d errs=%d/s ofdm_turn=%d\n",
1213250003Sadrian        __func__, listen_time,
1214250003Sadrian        ani_state->ofdm_noise_immunity_level, ofdm_phy_err_rate,
1215250003Sadrian        ani_state->cck_noise_immunity_level, cck_phy_err_rate,
1216250003Sadrian        ani_state->ofdms_turn);
1217250003Sadrian
1218250003Sadrian    if (ani_state->listen_time >= HAL_NOISE_DETECT_PERIOD) {
1219250003Sadrian        old_phy_noise_spur = ani_state->phy_noise_spur;
1220250003Sadrian        if (ofdm_phy_err_rate <= ani_state->ofdm_trig_low &&
1221250003Sadrian            cck_phy_err_rate <= ani_state->cck_trig_low) {
1222250003Sadrian            if (ani_state->listen_time >= HAL_NOISE_RECOVER_PERIOD) {
1223250003Sadrian                ani_state->phy_noise_spur = 0;
1224250003Sadrian            }
1225250003Sadrian        } else {
1226250003Sadrian            ani_state->phy_noise_spur = 1;
1227250003Sadrian        }
1228250003Sadrian        if (old_phy_noise_spur != ani_state->phy_noise_spur) {
1229250003Sadrian            HALDEBUG(ah, HAL_DEBUG_ANI,
1230250003Sadrian                     "%s: enviroment change from %d to %d\n",
1231250003Sadrian                     __func__, old_phy_noise_spur, ani_state->phy_noise_spur);
1232250003Sadrian        }
1233250003Sadrian    }
1234250003Sadrian
1235250003Sadrian    if (ani_state->listen_time > 5 * ahp->ah_ani_period) {
1236250003Sadrian        /*
1237250003Sadrian         * Check to see if need to lower immunity if
1238250003Sadrian         * 5 ani_periods have passed
1239250003Sadrian         */
1240250003Sadrian        if (ofdm_phy_err_rate <= ani_state->ofdm_trig_low &&
1241250003Sadrian            cck_phy_err_rate <= ani_state->cck_trig_low)
1242250003Sadrian        {
1243250003Sadrian            HALDEBUG(ah, HAL_DEBUG_ANI,
1244250003Sadrian                "%s: 1. listen_time=%d OFDM:%d errs=%d/s(<%d)  "
1245250003Sadrian                "CCK:%d errs=%d/s(<%d) -> ar9300_ani_lower_immunity\n",
1246250003Sadrian                __func__, ani_state->listen_time,
1247250003Sadrian                ani_state->ofdm_noise_immunity_level, ofdm_phy_err_rate,
1248250003Sadrian                ani_state->ofdm_trig_low, ani_state->cck_noise_immunity_level,
1249250003Sadrian                cck_phy_err_rate, ani_state->cck_trig_low);
1250250003Sadrian            ar9300_ani_lower_immunity(ah);
1251250003Sadrian            ani_state->ofdms_turn = !ani_state->ofdms_turn;
1252250003Sadrian        }
1253250003Sadrian        HALDEBUG(ah, HAL_DEBUG_ANI,
1254250003Sadrian            "%s: 1 listen_time=%d ofdm=%d/s cck=%d/s - "
1255250003Sadrian            "calling ar9300_ani_restart\n",
1256250003Sadrian            __func__, ani_state->listen_time,
1257250003Sadrian            ofdm_phy_err_rate, cck_phy_err_rate);
1258250003Sadrian        ar9300_ani_restart(ah);
1259250003Sadrian     } else if (ani_state->listen_time > ahp->ah_ani_period) {
1260250003Sadrian        /* check to see if need to raise immunity */
1261250003Sadrian        if (ofdm_phy_err_rate > ani_state->ofdm_trig_high &&
1262250003Sadrian            (cck_phy_err_rate <= ani_state->cck_trig_high ||
1263250003Sadrian             ani_state->ofdms_turn))
1264250003Sadrian        {
1265250003Sadrian            HALDEBUG(ah, HAL_DEBUG_ANI,
1266250003Sadrian                "%s: 2 listen_time=%d OFDM:%d errs=%d/s(>%d) -> "
1267250003Sadrian                "ar9300_ani_ofdm_err_trigger\n",
1268250003Sadrian                __func__, ani_state->listen_time,
1269250003Sadrian                ani_state->ofdm_noise_immunity_level, ofdm_phy_err_rate,
1270250003Sadrian                ani_state->ofdm_trig_high);
1271250003Sadrian            ar9300_ani_ofdm_err_trigger(ah);
1272250003Sadrian            ar9300_ani_restart(ah);
1273250003Sadrian            ani_state->ofdms_turn = AH_FALSE;
1274250003Sadrian        } else if (cck_phy_err_rate > ani_state->cck_trig_high) {
1275250003Sadrian            HALDEBUG(ah, HAL_DEBUG_ANI,
1276250003Sadrian                "%s: 3 listen_time=%d CCK:%d errs=%d/s(>%d) -> "
1277250003Sadrian                "ar9300_ani_cck_err_trigger\n",
1278250003Sadrian                __func__, ani_state->listen_time,
1279250003Sadrian                ani_state->cck_noise_immunity_level, cck_phy_err_rate,
1280250003Sadrian                ani_state->cck_trig_high);
1281250003Sadrian            ar9300_ani_cck_err_trigger(ah);
1282250003Sadrian            ar9300_ani_restart(ah);
1283250003Sadrian            ani_state->ofdms_turn = AH_TRUE;
1284250003Sadrian        }
1285250003Sadrian    }
1286250003Sadrian}
1287250003Sadrian
1288250003Sadrian/*
1289250003Sadrian * The poll function above calculates short noise spurs, caused by non-80211
1290250003Sadrian * devices, based on OFDM/CCK Phy errs.
1291250003Sadrian * If the noise is short enough, we don't want our ratectrl Algo to stop probing
1292250003Sadrian * higher rates, due to bad PER.
1293250003Sadrian */
1294250003SadrianHAL_BOOL
1295250003Sadrianar9300_is_ani_noise_spur(struct ath_hal *ah)
1296250003Sadrian{
1297250003Sadrian    struct ath_hal_9300 *ahp = AH9300(ah);
1298250003Sadrian    struct ar9300_ani_state *ani_state;
1299250003Sadrian
1300250003Sadrian    ani_state = ahp->ah_curani;
1301250003Sadrian
1302250003Sadrian    return ani_state->phy_noise_spur;
1303250003Sadrian}
1304250003Sadrian
1305