Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/ath/ath_hal/ar5212/ar5212_ani.c
39566 views
1
/*-
2
* SPDX-License-Identifier: ISC
3
*
4
* Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
5
* Copyright (c) 2002-2008 Atheros Communications, Inc.
6
*
7
* Permission to use, copy, modify, and/or distribute this software for any
8
* purpose with or without fee is hereby granted, provided that the above
9
* copyright notice and this permission notice appear in all copies.
10
*
11
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18
*/
19
#include "opt_ah.h"
20
21
#include "ah.h"
22
#include "ah_internal.h"
23
#include "ah_desc.h"
24
25
#include "ar5212/ar5212.h"
26
#include "ar5212/ar5212reg.h"
27
#include "ar5212/ar5212phy.h"
28
29
/*
30
* Anti noise immunity support. We track phy errors and react
31
* to excessive errors by adjusting the noise immunity parameters.
32
*/
33
34
#define HAL_EP_RND(x, mul) \
35
((((x)%(mul)) >= ((mul)/2)) ? ((x) + ((mul) - 1)) / (mul) : (x)/(mul))
36
#define BEACON_RSSI(ahp) \
37
HAL_EP_RND(ahp->ah_stats.ast_nodestats.ns_avgbrssi, \
38
HAL_RSSI_EP_MULTIPLIER)
39
40
/*
41
* ANI processing tunes radio parameters according to PHY errors
42
* and related information. This is done for noise and spur
43
* immunity in all operating modes if the device indicates it's
44
* capable at attach time. In addition, when there is a reference
45
* rssi value (e.g. beacon frames from an ap in station mode)
46
* further tuning is done.
47
*
48
* ANI_ENA indicates whether any ANI processing should be done;
49
* this is specified at attach time.
50
*
51
* ANI_ENA_RSSI indicates whether rssi-based processing should
52
* done, this is enabled based on operating mode and is meaningful
53
* only if ANI_ENA is true.
54
*
55
* ANI parameters are typically controlled only by the hal. The
56
* AniControl interface however permits manual tuning through the
57
* diagnostic api.
58
*/
59
#define ANI_ENA(ah) \
60
(AH5212(ah)->ah_procPhyErr & HAL_ANI_ENA)
61
#define ANI_ENA_RSSI(ah) \
62
(AH5212(ah)->ah_procPhyErr & HAL_RSSI_ANI_ENA)
63
64
#define ah_mibStats ah_stats.ast_mibstats
65
66
static void
67
enableAniMIBCounters(struct ath_hal *ah, const struct ar5212AniParams *params)
68
{
69
struct ath_hal_5212 *ahp = AH5212(ah);
70
71
HALDEBUG(ah, HAL_DEBUG_ANI, "%s: Enable mib counters: "
72
"OfdmPhyErrBase 0x%x cckPhyErrBase 0x%x\n",
73
__func__, params->ofdmPhyErrBase, params->cckPhyErrBase);
74
75
OS_REG_WRITE(ah, AR_FILTOFDM, 0);
76
OS_REG_WRITE(ah, AR_FILTCCK, 0);
77
78
OS_REG_WRITE(ah, AR_PHYCNT1, params->ofdmPhyErrBase);
79
OS_REG_WRITE(ah, AR_PHYCNT2, params->cckPhyErrBase);
80
OS_REG_WRITE(ah, AR_PHYCNTMASK1, AR_PHY_ERR_OFDM_TIMING);
81
OS_REG_WRITE(ah, AR_PHYCNTMASK2, AR_PHY_ERR_CCK_TIMING);
82
83
ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save+clear counters*/
84
ar5212EnableMibCounters(ah); /* enable everything */
85
}
86
87
static void
88
disableAniMIBCounters(struct ath_hal *ah)
89
{
90
struct ath_hal_5212 *ahp = AH5212(ah);
91
92
HALDEBUG(ah, HAL_DEBUG_ANI, "Disable MIB counters\n");
93
94
ar5212UpdateMibCounters(ah, &ahp->ah_mibStats); /* save stats */
95
ar5212DisableMibCounters(ah); /* disable everything */
96
97
OS_REG_WRITE(ah, AR_PHYCNTMASK1, 0);
98
OS_REG_WRITE(ah, AR_PHYCNTMASK2, 0);
99
}
100
101
/*
102
* Return the current ANI state of the channel we're on
103
*/
104
struct ar5212AniState *
105
ar5212AniGetCurrentState(struct ath_hal *ah)
106
{
107
return AH5212(ah)->ah_curani;
108
}
109
110
/*
111
* Return the current statistics.
112
*/
113
HAL_ANI_STATS *
114
ar5212AniGetCurrentStats(struct ath_hal *ah)
115
{
116
struct ath_hal_5212 *ahp = AH5212(ah);
117
118
/* update mib stats so we return current data */
119
/* XXX? side-effects to doing this here? */
120
ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);
121
return &ahp->ah_stats;
122
}
123
124
static void
125
setPhyErrBase(struct ath_hal *ah, struct ar5212AniParams *params)
126
{
127
if (params->ofdmTrigHigh >= AR_PHY_COUNTMAX) {
128
HALDEBUG(ah, HAL_DEBUG_ANY,
129
"OFDM Trigger %d is too high for hw counters, using max\n",
130
params->ofdmTrigHigh);
131
params->ofdmPhyErrBase = 0;
132
} else
133
params->ofdmPhyErrBase = AR_PHY_COUNTMAX - params->ofdmTrigHigh;
134
if (params->cckTrigHigh >= AR_PHY_COUNTMAX) {
135
HALDEBUG(ah, HAL_DEBUG_ANY,
136
"CCK Trigger %d is too high for hw counters, using max\n",
137
params->cckTrigHigh);
138
params->cckPhyErrBase = 0;
139
} else
140
params->cckPhyErrBase = AR_PHY_COUNTMAX - params->cckTrigHigh;
141
}
142
143
/*
144
* Setup ANI handling. Sets all thresholds and reset the
145
* channel statistics. Note that ar5212AniReset should be
146
* called by ar5212Reset before anything else happens and
147
* that's where we force initial settings.
148
*/
149
void
150
ar5212AniAttach(struct ath_hal *ah, const struct ar5212AniParams *params24,
151
const struct ar5212AniParams *params5, HAL_BOOL enable)
152
{
153
struct ath_hal_5212 *ahp = AH5212(ah);
154
155
ahp->ah_hasHwPhyCounters =
156
AH_PRIVATE(ah)->ah_caps.halHwPhyCounterSupport;
157
158
if (params24 != AH_NULL) {
159
OS_MEMCPY(&ahp->ah_aniParams24, params24, sizeof(*params24));
160
setPhyErrBase(ah, &ahp->ah_aniParams24);
161
}
162
if (params5 != AH_NULL) {
163
OS_MEMCPY(&ahp->ah_aniParams5, params5, sizeof(*params5));
164
setPhyErrBase(ah, &ahp->ah_aniParams5);
165
}
166
167
OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani));
168
if (ahp->ah_hasHwPhyCounters) {
169
/* Enable MIB Counters */
170
enableAniMIBCounters(ah, &ahp->ah_aniParams24 /*XXX*/);
171
}
172
if (enable) { /* Enable ani now */
173
HALASSERT(params24 != AH_NULL && params5 != AH_NULL);
174
ahp->ah_procPhyErr |= HAL_ANI_ENA;
175
} else {
176
ahp->ah_procPhyErr &= ~HAL_ANI_ENA;
177
}
178
}
179
180
HAL_BOOL
181
ar5212AniSetParams(struct ath_hal *ah, const struct ar5212AniParams *params24,
182
const struct ar5212AniParams *params5)
183
{
184
struct ath_hal_5212 *ahp = AH5212(ah);
185
HAL_BOOL ena = (ahp->ah_procPhyErr & HAL_ANI_ENA) != 0;
186
187
ar5212AniControl(ah, HAL_ANI_MODE, AH_FALSE);
188
189
OS_MEMCPY(&ahp->ah_aniParams24, params24, sizeof(*params24));
190
setPhyErrBase(ah, &ahp->ah_aniParams24);
191
OS_MEMCPY(&ahp->ah_aniParams5, params5, sizeof(*params5));
192
setPhyErrBase(ah, &ahp->ah_aniParams5);
193
194
OS_MEMZERO(ahp->ah_ani, sizeof(ahp->ah_ani));
195
ar5212AniReset(ah, AH_PRIVATE(ah)->ah_curchan,
196
AH_PRIVATE(ah)->ah_opmode, AH_FALSE);
197
198
ar5212AniControl(ah, HAL_ANI_MODE, ena);
199
200
return AH_TRUE;
201
}
202
203
/*
204
* Cleanup any ANI state setup.
205
*/
206
void
207
ar5212AniDetach(struct ath_hal *ah)
208
{
209
struct ath_hal_5212 *ahp = AH5212(ah);
210
211
HALDEBUG(ah, HAL_DEBUG_ANI, "Detaching Ani\n");
212
if (ahp->ah_hasHwPhyCounters)
213
disableAniMIBCounters(ah);
214
}
215
216
/*
217
* Control Adaptive Noise Immunity Parameters
218
*/
219
HAL_BOOL
220
ar5212AniControl(struct ath_hal *ah, HAL_ANI_CMD cmd, int param)
221
{
222
typedef int TABLE[];
223
struct ath_hal_5212 *ahp = AH5212(ah);
224
struct ar5212AniState *aniState = ahp->ah_curani;
225
const struct ar5212AniParams *params = AH_NULL;
226
227
/*
228
* This function may be called before there's a current
229
* channel (eg to disable ANI.)
230
*/
231
if (aniState != AH_NULL)
232
params = aniState->params;
233
234
OS_MARK(ah, AH_MARK_ANI_CONTROL, cmd);
235
236
switch (cmd) {
237
case HAL_ANI_NOISE_IMMUNITY_LEVEL: {
238
u_int level = param;
239
240
if (level > params->maxNoiseImmunityLevel) {
241
HALDEBUG(ah, HAL_DEBUG_ANY,
242
"%s: level out of range (%u > %u)\n",
243
__func__, level, params->maxNoiseImmunityLevel);
244
return AH_FALSE;
245
}
246
247
OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ,
248
AR_PHY_DESIRED_SZ_TOT_DES, params->totalSizeDesired[level]);
249
OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1,
250
AR_PHY_AGC_CTL1_COARSE_LOW, params->coarseLow[level]);
251
OS_REG_RMW_FIELD(ah, AR_PHY_AGC_CTL1,
252
AR_PHY_AGC_CTL1_COARSE_HIGH, params->coarseHigh[level]);
253
OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
254
AR_PHY_FIND_SIG_FIRPWR, params->firpwr[level]);
255
256
if (level > aniState->noiseImmunityLevel)
257
ahp->ah_stats.ast_ani_niup++;
258
else if (level < aniState->noiseImmunityLevel)
259
ahp->ah_stats.ast_ani_nidown++;
260
aniState->noiseImmunityLevel = level;
261
break;
262
}
263
case HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION: {
264
static const TABLE m1ThreshLow = { 127, 50 };
265
static const TABLE m2ThreshLow = { 127, 40 };
266
static const TABLE m1Thresh = { 127, 0x4d };
267
static const TABLE m2Thresh = { 127, 0x40 };
268
static const TABLE m2CountThr = { 31, 16 };
269
static const TABLE m2CountThrLow = { 63, 48 };
270
u_int on = param ? 1 : 0;
271
272
OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
273
AR_PHY_SFCORR_LOW_M1_THRESH_LOW, m1ThreshLow[on]);
274
OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
275
AR_PHY_SFCORR_LOW_M2_THRESH_LOW, m2ThreshLow[on]);
276
OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR,
277
AR_PHY_SFCORR_M1_THRESH, m1Thresh[on]);
278
OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR,
279
AR_PHY_SFCORR_M2_THRESH, m2Thresh[on]);
280
OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR,
281
AR_PHY_SFCORR_M2COUNT_THR, m2CountThr[on]);
282
OS_REG_RMW_FIELD(ah, AR_PHY_SFCORR_LOW,
283
AR_PHY_SFCORR_LOW_M2COUNT_THR_LOW, m2CountThrLow[on]);
284
285
if (on) {
286
OS_REG_SET_BIT(ah, AR_PHY_SFCORR_LOW,
287
AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
288
ahp->ah_stats.ast_ani_ofdmon++;
289
} else {
290
OS_REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW,
291
AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW);
292
ahp->ah_stats.ast_ani_ofdmoff++;
293
}
294
aniState->ofdmWeakSigDetectOff = !on;
295
break;
296
}
297
case HAL_ANI_CCK_WEAK_SIGNAL_THR: {
298
static const TABLE weakSigThrCck = { 8, 6 };
299
u_int high = param ? 1 : 0;
300
301
OS_REG_RMW_FIELD(ah, AR_PHY_CCK_DETECT,
302
AR_PHY_CCK_DETECT_WEAK_SIG_THR_CCK, weakSigThrCck[high]);
303
if (high)
304
ahp->ah_stats.ast_ani_cckhigh++;
305
else
306
ahp->ah_stats.ast_ani_ccklow++;
307
aniState->cckWeakSigThreshold = high;
308
break;
309
}
310
case HAL_ANI_FIRSTEP_LEVEL: {
311
u_int level = param;
312
313
if (level > params->maxFirstepLevel) {
314
HALDEBUG(ah, HAL_DEBUG_ANY,
315
"%s: level out of range (%u > %u)\n",
316
__func__, level, params->maxFirstepLevel);
317
return AH_FALSE;
318
}
319
OS_REG_RMW_FIELD(ah, AR_PHY_FIND_SIG,
320
AR_PHY_FIND_SIG_FIRSTEP, params->firstep[level]);
321
if (level > aniState->firstepLevel)
322
ahp->ah_stats.ast_ani_stepup++;
323
else if (level < aniState->firstepLevel)
324
ahp->ah_stats.ast_ani_stepdown++;
325
aniState->firstepLevel = level;
326
break;
327
}
328
case HAL_ANI_SPUR_IMMUNITY_LEVEL: {
329
u_int level = param;
330
331
if (level > params->maxSpurImmunityLevel) {
332
HALDEBUG(ah, HAL_DEBUG_ANY,
333
"%s: level out of range (%u > %u)\n",
334
__func__, level, params->maxSpurImmunityLevel);
335
return AH_FALSE;
336
}
337
OS_REG_RMW_FIELD(ah, AR_PHY_TIMING5,
338
AR_PHY_TIMING5_CYCPWR_THR1, params->cycPwrThr1[level]);
339
if (level > aniState->spurImmunityLevel)
340
ahp->ah_stats.ast_ani_spurup++;
341
else if (level < aniState->spurImmunityLevel)
342
ahp->ah_stats.ast_ani_spurdown++;
343
aniState->spurImmunityLevel = level;
344
break;
345
}
346
case HAL_ANI_PRESENT:
347
break;
348
case HAL_ANI_MODE:
349
if (param == 0) {
350
ahp->ah_procPhyErr &= ~HAL_ANI_ENA;
351
/* Turn off HW counters if we have them */
352
ar5212AniDetach(ah);
353
ah->ah_setRxFilter(ah,
354
ah->ah_getRxFilter(ah) &~ HAL_RX_FILTER_PHYERR);
355
} else { /* normal/auto mode */
356
/* don't mess with state if already enabled */
357
if (ahp->ah_procPhyErr & HAL_ANI_ENA)
358
break;
359
if (ahp->ah_hasHwPhyCounters) {
360
ar5212SetRxFilter(ah,
361
ar5212GetRxFilter(ah) &~ HAL_RX_FILTER_PHYERR);
362
/* Enable MIB Counters */
363
enableAniMIBCounters(ah,
364
ahp->ah_curani != AH_NULL ?
365
ahp->ah_curani->params:
366
&ahp->ah_aniParams24 /*XXX*/);
367
} else {
368
ah->ah_setRxFilter(ah,
369
ah->ah_getRxFilter(ah) | HAL_RX_FILTER_PHYERR);
370
}
371
ahp->ah_procPhyErr |= HAL_ANI_ENA;
372
}
373
break;
374
#ifdef AH_PRIVATE_DIAG
375
case HAL_ANI_PHYERR_RESET:
376
ahp->ah_stats.ast_ani_ofdmerrs = 0;
377
ahp->ah_stats.ast_ani_cckerrs = 0;
378
break;
379
#endif /* AH_PRIVATE_DIAG */
380
default:
381
HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid cmd %u\n",
382
__func__, cmd);
383
return AH_FALSE;
384
}
385
return AH_TRUE;
386
}
387
388
static void
389
ar5212AniOfdmErrTrigger(struct ath_hal *ah)
390
{
391
struct ath_hal_5212 *ahp = AH5212(ah);
392
const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
393
struct ar5212AniState *aniState;
394
const struct ar5212AniParams *params;
395
396
HALASSERT(chan != AH_NULL);
397
398
if (!ANI_ENA(ah))
399
return;
400
401
aniState = ahp->ah_curani;
402
params = aniState->params;
403
/* First, raise noise immunity level, up to max */
404
if (aniState->noiseImmunityLevel+1 <= params->maxNoiseImmunityLevel) {
405
HALDEBUG(ah, HAL_DEBUG_ANI, "%s: raise NI to %u\n", __func__,
406
aniState->noiseImmunityLevel + 1);
407
ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,
408
aniState->noiseImmunityLevel + 1);
409
return;
410
}
411
/* then, raise spur immunity level, up to max */
412
if (aniState->spurImmunityLevel+1 <= params->maxSpurImmunityLevel) {
413
HALDEBUG(ah, HAL_DEBUG_ANI, "%s: raise SI to %u\n", __func__,
414
aniState->spurImmunityLevel + 1);
415
ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
416
aniState->spurImmunityLevel + 1);
417
return;
418
}
419
420
if (ANI_ENA_RSSI(ah)) {
421
int32_t rssi = BEACON_RSSI(ahp);
422
if (rssi > params->rssiThrHigh) {
423
/*
424
* Beacon rssi is high, can turn off ofdm
425
* weak sig detect.
426
*/
427
if (!aniState->ofdmWeakSigDetectOff) {
428
HALDEBUG(ah, HAL_DEBUG_ANI,
429
"%s: rssi %d OWSD off\n", __func__, rssi);
430
ar5212AniControl(ah,
431
HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
432
AH_FALSE);
433
ar5212AniControl(ah,
434
HAL_ANI_SPUR_IMMUNITY_LEVEL, 0);
435
return;
436
}
437
/*
438
* If weak sig detect is already off, as last resort,
439
* raise firstep level
440
*/
441
if (aniState->firstepLevel+1 <= params->maxFirstepLevel) {
442
HALDEBUG(ah, HAL_DEBUG_ANI,
443
"%s: rssi %d raise ST %u\n", __func__, rssi,
444
aniState->firstepLevel+1);
445
ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
446
aniState->firstepLevel + 1);
447
return;
448
}
449
} else if (rssi > params->rssiThrLow) {
450
/*
451
* Beacon rssi in mid range, need ofdm weak signal
452
* detect, but we can raise firststepLevel.
453
*/
454
if (aniState->ofdmWeakSigDetectOff) {
455
HALDEBUG(ah, HAL_DEBUG_ANI,
456
"%s: rssi %d OWSD on\n", __func__, rssi);
457
ar5212AniControl(ah,
458
HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
459
AH_TRUE);
460
}
461
if (aniState->firstepLevel+1 <= params->maxFirstepLevel) {
462
HALDEBUG(ah, HAL_DEBUG_ANI,
463
"%s: rssi %d raise ST %u\n", __func__, rssi,
464
aniState->firstepLevel+1);
465
ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
466
aniState->firstepLevel + 1);
467
}
468
return;
469
} else {
470
/*
471
* Beacon rssi is low, if in 11b/g mode, turn off ofdm
472
* weak signal detection and zero firstepLevel to
473
* maximize CCK sensitivity
474
*/
475
if (IEEE80211_IS_CHAN_CCK(chan)) {
476
if (!aniState->ofdmWeakSigDetectOff) {
477
HALDEBUG(ah, HAL_DEBUG_ANI,
478
"%s: rssi %d OWSD off\n",
479
__func__, rssi);
480
ar5212AniControl(ah,
481
HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
482
AH_FALSE);
483
}
484
if (aniState->firstepLevel > 0) {
485
HALDEBUG(ah, HAL_DEBUG_ANI,
486
"%s: rssi %d zero ST (was %u)\n",
487
__func__, rssi,
488
aniState->firstepLevel);
489
ar5212AniControl(ah,
490
HAL_ANI_FIRSTEP_LEVEL, 0);
491
}
492
return;
493
}
494
}
495
}
496
}
497
498
static void
499
ar5212AniCckErrTrigger(struct ath_hal *ah)
500
{
501
struct ath_hal_5212 *ahp = AH5212(ah);
502
const struct ieee80211_channel *chan = AH_PRIVATE(ah)->ah_curchan;
503
struct ar5212AniState *aniState;
504
const struct ar5212AniParams *params;
505
506
HALASSERT(chan != AH_NULL);
507
508
if (!ANI_ENA(ah))
509
return;
510
511
/* first, raise noise immunity level, up to max */
512
aniState = ahp->ah_curani;
513
params = aniState->params;
514
if (aniState->noiseImmunityLevel+1 <= params->maxNoiseImmunityLevel) {
515
HALDEBUG(ah, HAL_DEBUG_ANI, "%s: raise NI to %u\n", __func__,
516
aniState->noiseImmunityLevel + 1);
517
ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,
518
aniState->noiseImmunityLevel + 1);
519
return;
520
}
521
522
if (ANI_ENA_RSSI(ah)) {
523
int32_t rssi = BEACON_RSSI(ahp);
524
if (rssi > params->rssiThrLow) {
525
/*
526
* Beacon signal in mid and high range,
527
* raise firstep level.
528
*/
529
if (aniState->firstepLevel+1 <= params->maxFirstepLevel) {
530
HALDEBUG(ah, HAL_DEBUG_ANI,
531
"%s: rssi %d raise ST %u\n", __func__, rssi,
532
aniState->firstepLevel+1);
533
ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
534
aniState->firstepLevel + 1);
535
}
536
} else {
537
/*
538
* Beacon rssi is low, zero firstep level to maximize
539
* CCK sensitivity in 11b/g mode.
540
*/
541
/* XXX can optimize */
542
if (IEEE80211_IS_CHAN_B(chan) ||
543
IEEE80211_IS_CHAN_G(chan)) {
544
if (aniState->firstepLevel > 0) {
545
HALDEBUG(ah, HAL_DEBUG_ANI,
546
"%s: rssi %d zero ST (was %u)\n",
547
__func__, rssi,
548
aniState->firstepLevel);
549
ar5212AniControl(ah,
550
HAL_ANI_FIRSTEP_LEVEL, 0);
551
}
552
}
553
}
554
}
555
}
556
557
static void
558
ar5212AniRestart(struct ath_hal *ah, struct ar5212AniState *aniState)
559
{
560
struct ath_hal_5212 *ahp = AH5212(ah);
561
562
aniState->listenTime = 0;
563
if (ahp->ah_hasHwPhyCounters) {
564
const struct ar5212AniParams *params = aniState->params;
565
/*
566
* NB: these are written on reset based on the
567
* ini so we must re-write them!
568
*/
569
OS_REG_WRITE(ah, AR_PHYCNT1, params->ofdmPhyErrBase);
570
OS_REG_WRITE(ah, AR_PHYCNT2, params->cckPhyErrBase);
571
OS_REG_WRITE(ah, AR_PHYCNTMASK1, AR_PHY_ERR_OFDM_TIMING);
572
OS_REG_WRITE(ah, AR_PHYCNTMASK2, AR_PHY_ERR_CCK_TIMING);
573
574
/* Clear the mib counters and save them in the stats */
575
ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);
576
}
577
aniState->ofdmPhyErrCount = 0;
578
aniState->cckPhyErrCount = 0;
579
}
580
581
/*
582
* Restore/reset the ANI parameters and reset the statistics.
583
* This routine must be called for every channel change.
584
*/
585
void
586
ar5212AniReset(struct ath_hal *ah, const struct ieee80211_channel *chan,
587
HAL_OPMODE opmode, int restore)
588
{
589
struct ath_hal_5212 *ahp = AH5212(ah);
590
HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);
591
/* XXX bounds check ic_devdata */
592
struct ar5212AniState *aniState = &ahp->ah_ani[chan->ic_devdata];
593
uint32_t rxfilter;
594
595
if ((ichan->privFlags & CHANNEL_ANI_INIT) == 0) {
596
OS_MEMZERO(aniState, sizeof(*aniState));
597
if (IEEE80211_IS_CHAN_2GHZ(chan))
598
aniState->params = &ahp->ah_aniParams24;
599
else
600
aniState->params = &ahp->ah_aniParams5;
601
ichan->privFlags |= CHANNEL_ANI_INIT;
602
HALASSERT((ichan->privFlags & CHANNEL_ANI_SETUP) == 0);
603
}
604
ahp->ah_curani = aniState;
605
#if 0
606
ath_hal_printf(ah,"%s: chan %u/0x%x restore %d opmode %u%s\n",
607
__func__, chan->ic_freq, chan->ic_flags, restore, opmode,
608
ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : "");
609
#else
610
HALDEBUG(ah, HAL_DEBUG_ANI, "%s: chan %u/0x%x restore %d opmode %u%s\n",
611
__func__, chan->ic_freq, chan->ic_flags, restore, opmode,
612
ichan->privFlags & CHANNEL_ANI_SETUP ? " setup" : "");
613
#endif
614
OS_MARK(ah, AH_MARK_ANI_RESET, opmode);
615
616
/*
617
* Turn off PHY error frame delivery while we futz with settings.
618
*/
619
rxfilter = ah->ah_getRxFilter(ah);
620
ah->ah_setRxFilter(ah, rxfilter &~ HAL_RX_FILTER_PHYERR);
621
622
/*
623
* If ANI is disabled at this point, don't set the default
624
* ANI parameter settings - leave the HAL settings there.
625
* This is (currently) needed for reliable radar detection.
626
*/
627
if (! ANI_ENA(ah)) {
628
HALDEBUG(ah, HAL_DEBUG_ANI, "%s: ANI disabled\n",
629
__func__);
630
goto finish;
631
}
632
633
/*
634
* Automatic processing is done only in station mode right now.
635
*/
636
if (opmode == HAL_M_STA)
637
ahp->ah_procPhyErr |= HAL_RSSI_ANI_ENA;
638
else
639
ahp->ah_procPhyErr &= ~HAL_RSSI_ANI_ENA;
640
/*
641
* Set all ani parameters. We either set them to initial
642
* values or restore the previous ones for the channel.
643
* XXX if ANI follows hardware, we don't care what mode we're
644
* XXX in, we should keep the ani parameters
645
*/
646
if (restore && (ichan->privFlags & CHANNEL_ANI_SETUP)) {
647
ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,
648
aniState->noiseImmunityLevel);
649
ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
650
aniState->spurImmunityLevel);
651
ar5212AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
652
!aniState->ofdmWeakSigDetectOff);
653
ar5212AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR,
654
aniState->cckWeakSigThreshold);
655
ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
656
aniState->firstepLevel);
657
} else {
658
ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL, 0);
659
ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL, 0);
660
ar5212AniControl(ah, HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
661
AH_TRUE);
662
ar5212AniControl(ah, HAL_ANI_CCK_WEAK_SIGNAL_THR, AH_FALSE);
663
ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL, 0);
664
ichan->privFlags |= CHANNEL_ANI_SETUP;
665
}
666
/*
667
* In case the counters haven't yet been setup; set them up.
668
*/
669
enableAniMIBCounters(ah, ahp->ah_curani->params);
670
ar5212AniRestart(ah, aniState);
671
672
finish:
673
/* restore RX filter mask */
674
ah->ah_setRxFilter(ah, rxfilter);
675
}
676
677
/*
678
* Process a MIB interrupt. We may potentially be invoked because
679
* any of the MIB counters overflow/trigger so don't assume we're
680
* here because a PHY error counter triggered.
681
*/
682
void
683
ar5212ProcessMibIntr(struct ath_hal *ah, const HAL_NODE_STATS *stats)
684
{
685
struct ath_hal_5212 *ahp = AH5212(ah);
686
uint32_t phyCnt1, phyCnt2;
687
688
HALDEBUG(ah, HAL_DEBUG_ANI, "%s: mibc 0x%x phyCnt1 0x%x phyCnt2 0x%x "
689
"filtofdm 0x%x filtcck 0x%x\n",
690
__func__, OS_REG_READ(ah, AR_MIBC),
691
OS_REG_READ(ah, AR_PHYCNT1), OS_REG_READ(ah, AR_PHYCNT2),
692
OS_REG_READ(ah, AR_FILTOFDM), OS_REG_READ(ah, AR_FILTCCK));
693
694
/*
695
* First order of business is to clear whatever caused
696
* the interrupt so we don't keep getting interrupted.
697
* We have the usual mib counters that are reset-on-read
698
* and the additional counters that appeared starting in
699
* Hainan. We collect the mib counters and explicitly
700
* zero additional counters we are not using. Anything
701
* else is reset only if it caused the interrupt.
702
*/
703
/* NB: these are not reset-on-read */
704
phyCnt1 = OS_REG_READ(ah, AR_PHYCNT1);
705
phyCnt2 = OS_REG_READ(ah, AR_PHYCNT2);
706
/* not used, always reset them in case they are the cause */
707
OS_REG_WRITE(ah, AR_FILTOFDM, 0);
708
OS_REG_WRITE(ah, AR_FILTCCK, 0);
709
710
/* Clear the mib counters and save them in the stats */
711
ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);
712
ahp->ah_stats.ast_nodestats = *stats;
713
714
/*
715
* Check for an ani stat hitting the trigger threshold.
716
* When this happens we get a MIB interrupt and the top
717
* 2 bits of the counter register will be 0b11, hence
718
* the mask check of phyCnt?.
719
*/
720
if (((phyCnt1 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK) ||
721
((phyCnt2 & AR_MIBCNT_INTRMASK) == AR_MIBCNT_INTRMASK)) {
722
struct ar5212AniState *aniState = ahp->ah_curani;
723
const struct ar5212AniParams *params = aniState->params;
724
uint32_t ofdmPhyErrCnt, cckPhyErrCnt;
725
726
ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase;
727
ahp->ah_stats.ast_ani_ofdmerrs +=
728
ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
729
aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
730
731
cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase;
732
ahp->ah_stats.ast_ani_cckerrs +=
733
cckPhyErrCnt - aniState->cckPhyErrCount;
734
aniState->cckPhyErrCount = cckPhyErrCnt;
735
736
/*
737
* NB: figure out which counter triggered. If both
738
* trigger we'll only deal with one as the processing
739
* clobbers the error counter so the trigger threshold
740
* check will never be true.
741
*/
742
if (aniState->ofdmPhyErrCount > params->ofdmTrigHigh)
743
ar5212AniOfdmErrTrigger(ah);
744
if (aniState->cckPhyErrCount > params->cckTrigHigh)
745
ar5212AniCckErrTrigger(ah);
746
/* NB: always restart to insure the h/w counters are reset */
747
ar5212AniRestart(ah, aniState);
748
}
749
}
750
751
void
752
ar5212AniPhyErrReport(struct ath_hal *ah, const struct ath_rx_status *rs)
753
{
754
struct ath_hal_5212 *ahp = AH5212(ah);
755
struct ar5212AniState *aniState;
756
const struct ar5212AniParams *params;
757
758
HALASSERT(!ahp->ah_hasHwPhyCounters && rs != AH_NULL);
759
760
aniState = ahp->ah_curani;
761
params = aniState->params;
762
if (rs->rs_phyerr == HAL_PHYERR_OFDM_TIMING) {
763
aniState->ofdmPhyErrCount++;
764
ahp->ah_stats.ast_ani_ofdmerrs++;
765
if (aniState->ofdmPhyErrCount > params->ofdmTrigHigh) {
766
ar5212AniOfdmErrTrigger(ah);
767
ar5212AniRestart(ah, aniState);
768
}
769
} else if (rs->rs_phyerr == HAL_PHYERR_CCK_TIMING) {
770
aniState->cckPhyErrCount++;
771
ahp->ah_stats.ast_ani_cckerrs++;
772
if (aniState->cckPhyErrCount > params->cckTrigHigh) {
773
ar5212AniCckErrTrigger(ah);
774
ar5212AniRestart(ah, aniState);
775
}
776
}
777
}
778
779
static void
780
ar5212AniLowerImmunity(struct ath_hal *ah)
781
{
782
struct ath_hal_5212 *ahp = AH5212(ah);
783
struct ar5212AniState *aniState;
784
const struct ar5212AniParams *params;
785
786
HALASSERT(ANI_ENA(ah));
787
788
aniState = ahp->ah_curani;
789
params = aniState->params;
790
if (ANI_ENA_RSSI(ah)) {
791
int32_t rssi = BEACON_RSSI(ahp);
792
if (rssi > params->rssiThrHigh) {
793
/*
794
* Beacon signal is high, leave ofdm weak signal
795
* detection off or it may oscillate. Let it fall
796
* through.
797
*/
798
} else if (rssi > params->rssiThrLow) {
799
/*
800
* Beacon rssi in mid range, turn on ofdm weak signal
801
* detection or lower firstep level.
802
*/
803
if (aniState->ofdmWeakSigDetectOff) {
804
HALDEBUG(ah, HAL_DEBUG_ANI,
805
"%s: rssi %d OWSD on\n", __func__, rssi);
806
ar5212AniControl(ah,
807
HAL_ANI_OFDM_WEAK_SIGNAL_DETECTION,
808
AH_TRUE);
809
return;
810
}
811
if (aniState->firstepLevel > 0) {
812
HALDEBUG(ah, HAL_DEBUG_ANI,
813
"%s: rssi %d lower ST %u\n", __func__, rssi,
814
aniState->firstepLevel-1);
815
ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
816
aniState->firstepLevel - 1);
817
return;
818
}
819
} else {
820
/*
821
* Beacon rssi is low, reduce firstep level.
822
*/
823
if (aniState->firstepLevel > 0) {
824
HALDEBUG(ah, HAL_DEBUG_ANI,
825
"%s: rssi %d lower ST %u\n", __func__, rssi,
826
aniState->firstepLevel-1);
827
ar5212AniControl(ah, HAL_ANI_FIRSTEP_LEVEL,
828
aniState->firstepLevel - 1);
829
return;
830
}
831
}
832
}
833
/* then lower spur immunity level, down to zero */
834
if (aniState->spurImmunityLevel > 0) {
835
HALDEBUG(ah, HAL_DEBUG_ANI, "%s: lower SI %u\n",
836
__func__, aniState->spurImmunityLevel-1);
837
ar5212AniControl(ah, HAL_ANI_SPUR_IMMUNITY_LEVEL,
838
aniState->spurImmunityLevel - 1);
839
return;
840
}
841
/*
842
* if all else fails, lower noise immunity level down to a min value
843
* zero for now
844
*/
845
if (aniState->noiseImmunityLevel > 0) {
846
HALDEBUG(ah, HAL_DEBUG_ANI, "%s: lower NI %u\n",
847
__func__, aniState->noiseImmunityLevel-1);
848
ar5212AniControl(ah, HAL_ANI_NOISE_IMMUNITY_LEVEL,
849
aniState->noiseImmunityLevel - 1);
850
return;
851
}
852
}
853
854
#define CLOCK_RATE 44000 /* XXX use mac_usec or similar */
855
/* convert HW counter values to ms using 11g clock rate, goo9d enough
856
for 11a and Turbo */
857
858
/*
859
* Return an approximation of the time spent ``listening'' by
860
* deducting the cycles spent tx'ing and rx'ing from the total
861
* cycle count since our last call. A return value <0 indicates
862
* an invalid/inconsistent time.
863
*/
864
static int32_t
865
ar5212AniGetListenTime(struct ath_hal *ah)
866
{
867
struct ath_hal_5212 *ahp = AH5212(ah);
868
struct ar5212AniState *aniState = NULL;
869
int32_t listenTime = 0;
870
int good;
871
HAL_SURVEY_SAMPLE hs;
872
873
/*
874
* We shouldn't see ah_curchan be NULL, but just in case..
875
*/
876
if (AH_PRIVATE(ah)->ah_curchan == AH_NULL) {
877
ath_hal_printf(ah, "%s: ah_curchan = NULL?\n", __func__);
878
return (0);
879
}
880
881
/*
882
* Fetch the current statistics, squirrel away the current
883
* sample, bump the sequence/sample counter.
884
*/
885
OS_MEMZERO(&hs, sizeof(hs));
886
good = ar5212GetMibCycleCounts(ah, &hs);
887
ath_hal_survey_add_sample(ah, &hs);
888
889
if (ANI_ENA(ah))
890
aniState = ahp->ah_curani;
891
892
if (good == AH_FALSE) {
893
/*
894
* Cycle counter wrap (or initial call); it's not possible
895
* to accurately calculate a value because the registers
896
* right shift rather than wrap--so punt and return 0.
897
*/
898
listenTime = 0;
899
ahp->ah_stats.ast_ani_lzero++;
900
} else if (ANI_ENA(ah)) {
901
/*
902
* Only calculate and update the cycle count if we have
903
* an ANI state.
904
*/
905
int32_t ccdelta =
906
AH5212(ah)->ah_cycleCount - aniState->cycleCount;
907
int32_t rfdelta =
908
AH5212(ah)->ah_rxBusy - aniState->rxFrameCount;
909
int32_t tfdelta =
910
AH5212(ah)->ah_txBusy - aniState->txFrameCount;
911
listenTime = (ccdelta - rfdelta - tfdelta) / CLOCK_RATE;
912
}
913
914
/*
915
* Again, only update ANI state if we have it.
916
*/
917
if (ANI_ENA(ah)) {
918
aniState->cycleCount = AH5212(ah)->ah_cycleCount;
919
aniState->rxFrameCount = AH5212(ah)->ah_rxBusy;
920
aniState->txFrameCount = AH5212(ah)->ah_txBusy;
921
}
922
923
return listenTime;
924
}
925
926
/*
927
* Update ani stats in preparation for listen time processing.
928
*/
929
static void
930
updateMIBStats(struct ath_hal *ah, struct ar5212AniState *aniState)
931
{
932
struct ath_hal_5212 *ahp = AH5212(ah);
933
const struct ar5212AniParams *params = aniState->params;
934
uint32_t phyCnt1, phyCnt2;
935
int32_t ofdmPhyErrCnt, cckPhyErrCnt;
936
937
HALASSERT(ahp->ah_hasHwPhyCounters);
938
939
/* Clear the mib counters and save them in the stats */
940
ar5212UpdateMibCounters(ah, &ahp->ah_mibStats);
941
942
/* NB: these are not reset-on-read */
943
phyCnt1 = OS_REG_READ(ah, AR_PHYCNT1);
944
phyCnt2 = OS_REG_READ(ah, AR_PHYCNT2);
945
946
/* NB: these are spec'd to never roll-over */
947
ofdmPhyErrCnt = phyCnt1 - params->ofdmPhyErrBase;
948
if (ofdmPhyErrCnt < 0) {
949
HALDEBUG(ah, HAL_DEBUG_ANI, "OFDM phyErrCnt %d phyCnt1 0x%x\n",
950
ofdmPhyErrCnt, phyCnt1);
951
ofdmPhyErrCnt = AR_PHY_COUNTMAX;
952
}
953
ahp->ah_stats.ast_ani_ofdmerrs +=
954
ofdmPhyErrCnt - aniState->ofdmPhyErrCount;
955
aniState->ofdmPhyErrCount = ofdmPhyErrCnt;
956
957
cckPhyErrCnt = phyCnt2 - params->cckPhyErrBase;
958
if (cckPhyErrCnt < 0) {
959
HALDEBUG(ah, HAL_DEBUG_ANI, "CCK phyErrCnt %d phyCnt2 0x%x\n",
960
cckPhyErrCnt, phyCnt2);
961
cckPhyErrCnt = AR_PHY_COUNTMAX;
962
}
963
ahp->ah_stats.ast_ani_cckerrs +=
964
cckPhyErrCnt - aniState->cckPhyErrCount;
965
aniState->cckPhyErrCount = cckPhyErrCnt;
966
}
967
968
void
969
ar5212RxMonitor(struct ath_hal *ah, const HAL_NODE_STATS *stats,
970
const struct ieee80211_channel *chan)
971
{
972
struct ath_hal_5212 *ahp = AH5212(ah);
973
ahp->ah_stats.ast_nodestats.ns_avgbrssi = stats->ns_avgbrssi;
974
}
975
976
/*
977
* Do periodic processing. This routine is called from the
978
* driver's rx interrupt handler after processing frames.
979
*/
980
void
981
ar5212AniPoll(struct ath_hal *ah, const struct ieee80211_channel *chan)
982
{
983
struct ath_hal_5212 *ahp = AH5212(ah);
984
struct ar5212AniState *aniState = ahp->ah_curani;
985
const struct ar5212AniParams *params;
986
int32_t listenTime;
987
988
/* Always update from the MIB, for statistics gathering */
989
listenTime = ar5212AniGetListenTime(ah);
990
991
/* XXX can aniState be null? */
992
if (aniState == AH_NULL)
993
return;
994
if (!ANI_ENA(ah))
995
return;
996
997
if (listenTime < 0) {
998
ahp->ah_stats.ast_ani_lneg++;
999
/* restart ANI period if listenTime is invalid */
1000
ar5212AniRestart(ah, aniState);
1001
1002
/* Don't do any further ANI processing here */
1003
return;
1004
}
1005
/* XXX beware of overflow? */
1006
aniState->listenTime += listenTime;
1007
1008
OS_MARK(ah, AH_MARK_ANI_POLL, aniState->listenTime);
1009
1010
params = aniState->params;
1011
if (aniState->listenTime > 5*params->period) {
1012
/*
1013
* Check to see if need to lower immunity if
1014
* 5 aniPeriods have passed
1015
*/
1016
if (ahp->ah_hasHwPhyCounters)
1017
updateMIBStats(ah, aniState);
1018
if (aniState->ofdmPhyErrCount <= aniState->listenTime *
1019
params->ofdmTrigLow/1000 &&
1020
aniState->cckPhyErrCount <= aniState->listenTime *
1021
params->cckTrigLow/1000)
1022
ar5212AniLowerImmunity(ah);
1023
ar5212AniRestart(ah, aniState);
1024
} else if (aniState->listenTime > params->period) {
1025
if (ahp->ah_hasHwPhyCounters)
1026
updateMIBStats(ah, aniState);
1027
/* check to see if need to raise immunity */
1028
if (aniState->ofdmPhyErrCount > aniState->listenTime *
1029
params->ofdmTrigHigh / 1000) {
1030
HALDEBUG(ah, HAL_DEBUG_ANI,
1031
"%s: OFDM err %u listenTime %u\n", __func__,
1032
aniState->ofdmPhyErrCount, aniState->listenTime);
1033
ar5212AniOfdmErrTrigger(ah);
1034
ar5212AniRestart(ah, aniState);
1035
} else if (aniState->cckPhyErrCount > aniState->listenTime *
1036
params->cckTrigHigh / 1000) {
1037
HALDEBUG(ah, HAL_DEBUG_ANI,
1038
"%s: CCK err %u listenTime %u\n", __func__,
1039
aniState->cckPhyErrCount, aniState->listenTime);
1040
ar5212AniCckErrTrigger(ah);
1041
ar5212AniRestart(ah, aniState);
1042
}
1043
}
1044
}
1045
1046