Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/ath/ath_hal/ar5416/ar5416_cal.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_devid.h"
24
25
#include "ah_eeprom_v14.h"
26
27
#include "ar5212/ar5212.h" /* for NF cal related declarations */
28
29
#include "ar5416/ar5416.h"
30
#include "ar5416/ar5416reg.h"
31
#include "ar5416/ar5416phy.h"
32
33
/* Owl specific stuff */
34
#define NUM_NOISEFLOOR_READINGS 6 /* 3 chains * (ctl + ext) */
35
36
static void ar5416StartNFCal(struct ath_hal *ah);
37
static HAL_BOOL ar5416LoadNF(struct ath_hal *ah, const struct ieee80211_channel *);
38
static int16_t ar5416GetNf(struct ath_hal *, struct ieee80211_channel *);
39
40
static uint16_t ar5416GetDefaultNF(struct ath_hal *ah, const struct ieee80211_channel *chan);
41
static void ar5416SanitizeNF(struct ath_hal *ah, int16_t *nf);
42
43
/*
44
* Determine if calibration is supported by device and channel flags
45
*/
46
47
/*
48
* ADC GAIN/DC offset calibration is for calibrating two ADCs that
49
* are acting as one by interleaving incoming symbols. This isn't
50
* relevant for 2.4GHz 20MHz wide modes because, as far as I can tell,
51
* the secondary ADC is never enabled. It is enabled however for
52
* 5GHz modes.
53
*
54
* It hasn't been confirmed whether doing this calibration is needed
55
* at all in the above modes and/or whether it's actually harmful.
56
* So for now, let's leave it enabled and just remember to get
57
* confirmation that it needs to be clarified.
58
*
59
* See US Patent No: US 7,541,952 B1:
60
* " Method and Apparatus for Offset and Gain Compensation for
61
* Analog-to-Digital Converters."
62
*/
63
static OS_INLINE HAL_BOOL
64
ar5416IsCalSupp(struct ath_hal *ah, const struct ieee80211_channel *chan,
65
HAL_CAL_TYPE calType)
66
{
67
struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
68
69
switch (calType & cal->suppCals) {
70
case IQ_MISMATCH_CAL:
71
/* Run IQ Mismatch for non-CCK only */
72
return !IEEE80211_IS_CHAN_B(chan);
73
case ADC_GAIN_CAL:
74
case ADC_DC_CAL:
75
/*
76
* Run ADC Gain Cal for either 5ghz any or 2ghz HT40.
77
*
78
* Don't run ADC calibrations for 5ghz fast clock mode
79
* in HT20 - only one ADC is used.
80
*/
81
if (IEEE80211_IS_CHAN_HT20(chan) &&
82
(IS_5GHZ_FAST_CLOCK_EN(ah, chan)))
83
return AH_FALSE;
84
if (IEEE80211_IS_CHAN_5GHZ(chan))
85
return AH_TRUE;
86
if (IEEE80211_IS_CHAN_HT40(chan))
87
return AH_TRUE;
88
return AH_FALSE;
89
}
90
return AH_FALSE;
91
}
92
93
/*
94
* Setup HW to collect samples used for current cal
95
*/
96
static void
97
ar5416SetupMeasurement(struct ath_hal *ah, HAL_CAL_LIST *currCal)
98
{
99
/* Start calibration w/ 2^(INIT_IQCAL_LOG_COUNT_MAX+1) samples */
100
OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4,
101
AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX,
102
currCal->calData->calCountMax);
103
104
/* Select calibration to run */
105
switch (currCal->calData->calType) {
106
case IQ_MISMATCH_CAL:
107
OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ);
108
HALDEBUG(ah, HAL_DEBUG_PERCAL,
109
"%s: start IQ Mismatch calibration\n", __func__);
110
break;
111
case ADC_GAIN_CAL:
112
OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_GAIN);
113
HALDEBUG(ah, HAL_DEBUG_PERCAL,
114
"%s: start ADC Gain calibration\n", __func__);
115
break;
116
case ADC_DC_CAL:
117
OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_PER);
118
HALDEBUG(ah, HAL_DEBUG_PERCAL,
119
"%s: start ADC DC calibration\n", __func__);
120
break;
121
case ADC_DC_INIT_CAL:
122
OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_INIT);
123
HALDEBUG(ah, HAL_DEBUG_PERCAL,
124
"%s: start Init ADC DC calibration\n", __func__);
125
break;
126
}
127
/* Kick-off cal */
128
OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_DO_CAL);
129
}
130
131
/*
132
* Initialize shared data structures and prepare a cal to be run.
133
*/
134
static void
135
ar5416ResetMeasurement(struct ath_hal *ah, HAL_CAL_LIST *currCal)
136
{
137
struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
138
139
/* Reset data structures shared between different calibrations */
140
OS_MEMZERO(cal->caldata, sizeof(cal->caldata));
141
cal->calSamples = 0;
142
143
/* Setup HW for new calibration */
144
ar5416SetupMeasurement(ah, currCal);
145
146
/* Change SW state to RUNNING for this calibration */
147
currCal->calState = CAL_RUNNING;
148
}
149
150
#if 0
151
/*
152
* Run non-periodic calibrations.
153
*/
154
static HAL_BOOL
155
ar5416RunInitCals(struct ath_hal *ah, int init_cal_count)
156
{
157
struct ath_hal_5416 *ahp = AH5416(ah);
158
struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
159
HAL_CHANNEL_INTERNAL ichan; /* XXX bogus */
160
HAL_CAL_LIST *curCal = ahp->ah_cal_curr;
161
HAL_BOOL isCalDone;
162
int i;
163
164
if (curCal == AH_NULL)
165
return AH_FALSE;
166
167
ichan.calValid = 0;
168
for (i = 0; i < init_cal_count; i++) {
169
/* Reset this Cal */
170
ar5416ResetMeasurement(ah, curCal);
171
/* Poll for offset calibration complete */
172
if (!ath_hal_wait(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_DO_CAL, 0)) {
173
HALDEBUG(ah, HAL_DEBUG_ANY,
174
"%s: Cal %d failed to finish in 100ms.\n",
175
__func__, curCal->calData->calType);
176
/* Re-initialize list pointers for periodic cals */
177
cal->cal_list = cal->cal_last = cal->cal_curr = AH_NULL;
178
return AH_FALSE;
179
}
180
/* Run this cal */
181
ar5416DoCalibration(ah, &ichan, ahp->ah_rxchainmask,
182
curCal, &isCalDone);
183
if (!isCalDone)
184
HALDEBUG(ah, HAL_DEBUG_ANY,
185
"%s: init cal %d did not complete.\n",
186
__func__, curCal->calData->calType);
187
if (curCal->calNext != AH_NULL)
188
curCal = curCal->calNext;
189
}
190
191
/* Re-initialize list pointers for periodic cals */
192
cal->cal_list = cal->cal_last = cal->cal_curr = AH_NULL;
193
return AH_TRUE;
194
}
195
#endif
196
197
/*
198
* AGC calibration for the AR5416, AR9130, AR9160, AR9280.
199
*/
200
HAL_BOOL
201
ar5416InitCalHardware(struct ath_hal *ah, const struct ieee80211_channel *chan)
202
{
203
204
if (AR_SREV_MERLIN_10_OR_LATER(ah)) {
205
/* Disable ADC */
206
OS_REG_CLR_BIT(ah, AR_PHY_ADC_CTL,
207
AR_PHY_ADC_CTL_OFF_PWDADC);
208
209
/* Enable Rx Filter Cal */
210
OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
211
AR_PHY_AGC_CONTROL_FLTR_CAL);
212
}
213
214
/* Calibrate the AGC */
215
OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL);
216
217
/* Poll for offset calibration complete */
218
if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) {
219
HALDEBUG(ah, HAL_DEBUG_ANY,
220
"%s: offset calibration did not complete in 1ms; "
221
"noisy environment?\n", __func__);
222
return AH_FALSE;
223
}
224
225
if (AR_SREV_MERLIN_10_OR_LATER(ah)) {
226
/* Enable ADC */
227
OS_REG_SET_BIT(ah, AR_PHY_ADC_CTL,
228
AR_PHY_ADC_CTL_OFF_PWDADC);
229
230
/* Disable Rx Filter Cal */
231
OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
232
AR_PHY_AGC_CONTROL_FLTR_CAL);
233
}
234
235
return AH_TRUE;
236
}
237
238
/*
239
* Initialize Calibration infrastructure.
240
*/
241
#define MAX_CAL_CHECK 32
242
HAL_BOOL
243
ar5416InitCal(struct ath_hal *ah, const struct ieee80211_channel *chan)
244
{
245
struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
246
HAL_CHANNEL_INTERNAL *ichan;
247
248
ichan = ath_hal_checkchannel(ah, chan);
249
HALASSERT(ichan != AH_NULL);
250
251
/* Do initial chipset-specific calibration */
252
if (! AH5416(ah)->ah_cal_initcal(ah, chan)) {
253
HALDEBUG(ah, HAL_DEBUG_ANY,
254
"%s: initial chipset calibration did "
255
"not complete in time; noisy environment?\n", __func__);
256
return AH_FALSE;
257
}
258
259
/* If there's PA Cal, do it */
260
if (AH5416(ah)->ah_cal_pacal)
261
AH5416(ah)->ah_cal_pacal(ah, AH_TRUE);
262
263
/*
264
* Do NF calibration after DC offset and other CALs.
265
* Per system engineers, noise floor value can sometimes be 20 dB
266
* higher than normal value if DC offset and noise floor cal are
267
* triggered at the same time.
268
*/
269
OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
270
271
/*
272
* This may take a while to run; make sure subsequent
273
* calibration routines check that this has completed
274
* before reading the value and triggering a subsequent
275
* calibration.
276
*/
277
278
/* Initialize list pointers */
279
cal->cal_list = cal->cal_last = cal->cal_curr = AH_NULL;
280
281
/*
282
* Enable IQ, ADC Gain, ADC DC Offset Cals
283
*/
284
if (AR_SREV_HOWL(ah) || AR_SREV_SOWL_10_OR_LATER(ah)) {
285
/* Setup all non-periodic, init time only calibrations */
286
/* XXX: Init DC Offset not working yet */
287
#if 0
288
if (ar5416IsCalSupp(ah, chan, ADC_DC_INIT_CAL)) {
289
INIT_CAL(&cal->adcDcCalInitData);
290
INSERT_CAL(cal, &cal->adcDcCalInitData);
291
}
292
/* Initialize current pointer to first element in list */
293
cal->cal_curr = cal->cal_list;
294
295
if (cal->ah_cal_curr != AH_NULL && !ar5416RunInitCals(ah, 0))
296
return AH_FALSE;
297
#endif
298
}
299
300
/* If Cals are supported, add them to list via INIT/INSERT_CAL */
301
if (ar5416IsCalSupp(ah, chan, ADC_GAIN_CAL)) {
302
INIT_CAL(&cal->adcGainCalData);
303
INSERT_CAL(cal, &cal->adcGainCalData);
304
HALDEBUG(ah, HAL_DEBUG_PERCAL,
305
"%s: enable ADC Gain Calibration.\n", __func__);
306
}
307
if (ar5416IsCalSupp(ah, chan, ADC_DC_CAL)) {
308
INIT_CAL(&cal->adcDcCalData);
309
INSERT_CAL(cal, &cal->adcDcCalData);
310
HALDEBUG(ah, HAL_DEBUG_PERCAL,
311
"%s: enable ADC DC Calibration.\n", __func__);
312
}
313
if (ar5416IsCalSupp(ah, chan, IQ_MISMATCH_CAL)) {
314
INIT_CAL(&cal->iqCalData);
315
INSERT_CAL(cal, &cal->iqCalData);
316
HALDEBUG(ah, HAL_DEBUG_PERCAL,
317
"%s: enable IQ Calibration.\n", __func__);
318
}
319
/* Initialize current pointer to first element in list */
320
cal->cal_curr = cal->cal_list;
321
322
/* Kick off measurements for the first cal */
323
if (cal->cal_curr != AH_NULL)
324
ar5416ResetMeasurement(ah, cal->cal_curr);
325
326
/* Mark all calibrations on this channel as being invalid */
327
ichan->calValid = 0;
328
329
return AH_TRUE;
330
#undef MAX_CAL_CHECK
331
}
332
333
/*
334
* Entry point for upper layers to restart current cal.
335
* Reset the calibration valid bit in channel.
336
*/
337
HAL_BOOL
338
ar5416ResetCalValid(struct ath_hal *ah, const struct ieee80211_channel *chan)
339
{
340
struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
341
HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);
342
HAL_CAL_LIST *currCal = cal->cal_curr;
343
344
if (!AR_SREV_SOWL_10_OR_LATER(ah))
345
return AH_FALSE;
346
if (currCal == AH_NULL)
347
return AH_FALSE;
348
if (ichan == AH_NULL) {
349
HALDEBUG(ah, HAL_DEBUG_ANY,
350
"%s: invalid channel %u/0x%x; no mapping\n",
351
__func__, chan->ic_freq, chan->ic_flags);
352
return AH_FALSE;
353
}
354
/*
355
* Expected that this calibration has run before, post-reset.
356
* Current state should be done
357
*/
358
if (currCal->calState != CAL_DONE) {
359
HALDEBUG(ah, HAL_DEBUG_ANY,
360
"%s: Calibration state incorrect, %d\n",
361
__func__, currCal->calState);
362
return AH_FALSE;
363
}
364
365
/* Verify Cal is supported on this channel */
366
if (!ar5416IsCalSupp(ah, chan, currCal->calData->calType))
367
return AH_FALSE;
368
369
HALDEBUG(ah, HAL_DEBUG_PERCAL,
370
"%s: Resetting Cal %d state for channel %u/0x%x\n",
371
__func__, currCal->calData->calType, chan->ic_freq,
372
chan->ic_flags);
373
374
/* Disable cal validity in channel */
375
ichan->calValid &= ~currCal->calData->calType;
376
currCal->calState = CAL_WAITING;
377
378
return AH_TRUE;
379
}
380
381
/*
382
* Recalibrate the lower PHY chips to account for temperature/environment
383
* changes.
384
*/
385
static void
386
ar5416DoCalibration(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *ichan,
387
uint8_t rxchainmask, HAL_CAL_LIST *currCal, HAL_BOOL *isCalDone)
388
{
389
struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
390
391
/* Cal is assumed not done until explicitly set below */
392
*isCalDone = AH_FALSE;
393
394
HALDEBUG(ah, HAL_DEBUG_PERCAL,
395
"%s: %s Calibration, state %d, calValid 0x%x\n",
396
__func__, currCal->calData->calName, currCal->calState,
397
ichan->calValid);
398
399
/* Calibration in progress. */
400
if (currCal->calState == CAL_RUNNING) {
401
/* Check to see if it has finished. */
402
if (!(OS_REG_READ(ah, AR_PHY_TIMING_CTRL4) & AR_PHY_TIMING_CTRL4_DO_CAL)) {
403
HALDEBUG(ah, HAL_DEBUG_PERCAL,
404
"%s: sample %d of %d finished\n",
405
__func__, cal->calSamples,
406
currCal->calData->calNumSamples);
407
/*
408
* Collect measurements for active chains.
409
*/
410
currCal->calData->calCollect(ah);
411
if (++cal->calSamples >= currCal->calData->calNumSamples) {
412
int i, numChains = 0;
413
for (i = 0; i < AR5416_MAX_CHAINS; i++) {
414
if (rxchainmask & (1 << i))
415
numChains++;
416
}
417
/*
418
* Process accumulated data
419
*/
420
currCal->calData->calPostProc(ah, numChains);
421
422
/* Calibration has finished. */
423
ichan->calValid |= currCal->calData->calType;
424
currCal->calState = CAL_DONE;
425
*isCalDone = AH_TRUE;
426
} else {
427
/*
428
* Set-up to collect of another sub-sample.
429
*/
430
ar5416SetupMeasurement(ah, currCal);
431
}
432
}
433
} else if (!(ichan->calValid & currCal->calData->calType)) {
434
/* If current cal is marked invalid in channel, kick it off */
435
ar5416ResetMeasurement(ah, currCal);
436
}
437
}
438
439
/*
440
* Internal interface to schedule periodic calibration work.
441
*/
442
HAL_BOOL
443
ar5416PerCalibrationN(struct ath_hal *ah, struct ieee80211_channel *chan,
444
u_int rxchainmask, HAL_BOOL longcal, HAL_BOOL *isCalDone)
445
{
446
struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
447
HAL_CAL_LIST *currCal = cal->cal_curr;
448
HAL_CHANNEL_INTERNAL *ichan;
449
int r;
450
451
OS_MARK(ah, AH_MARK_PERCAL, chan->ic_freq);
452
453
*isCalDone = AH_TRUE;
454
455
/*
456
* Since ath_hal calls the PerCal method with rxchainmask=0x1;
457
* override it with the current chainmask. The upper levels currently
458
* doesn't know about the chainmask.
459
*/
460
rxchainmask = AH5416(ah)->ah_rx_chainmask;
461
462
/* Invalid channel check */
463
ichan = ath_hal_checkchannel(ah, chan);
464
if (ichan == AH_NULL) {
465
HALDEBUG(ah, HAL_DEBUG_ANY,
466
"%s: invalid channel %u/0x%x; no mapping\n",
467
__func__, chan->ic_freq, chan->ic_flags);
468
return AH_FALSE;
469
}
470
471
/*
472
* For given calibration:
473
* 1. Call generic cal routine
474
* 2. When this cal is done (isCalDone) if we have more cals waiting
475
* (eg after reset), mask this to upper layers by not propagating
476
* isCalDone if it is set to TRUE.
477
* Instead, change isCalDone to FALSE and setup the waiting cal(s)
478
* to be run.
479
*/
480
if (currCal != AH_NULL &&
481
(currCal->calState == CAL_RUNNING ||
482
currCal->calState == CAL_WAITING)) {
483
ar5416DoCalibration(ah, ichan, rxchainmask, currCal, isCalDone);
484
if (*isCalDone == AH_TRUE) {
485
cal->cal_curr = currCal = currCal->calNext;
486
if (currCal->calState == CAL_WAITING) {
487
*isCalDone = AH_FALSE;
488
ar5416ResetMeasurement(ah, currCal);
489
}
490
}
491
}
492
493
/* Do NF cal only at longer intervals */
494
if (longcal) {
495
/* Do PA calibration if the chipset supports */
496
if (AH5416(ah)->ah_cal_pacal)
497
AH5416(ah)->ah_cal_pacal(ah, AH_FALSE);
498
499
/* Do open-loop temperature compensation if the chipset needs it */
500
if (ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
501
AH5416(ah)->ah_olcTempCompensation(ah);
502
503
/*
504
* Get the value from the previous NF cal
505
* and update the history buffer.
506
*/
507
r = ar5416GetNf(ah, chan);
508
if (r == 0 || r == -1) {
509
/* NF calibration result isn't valid */
510
HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: NF calibration"
511
" didn't finish; delaying CCA\n", __func__);
512
} else {
513
int ret;
514
/*
515
* NF calibration result is valid.
516
*
517
* Load the NF from history buffer of the current channel.
518
* NF is slow time-variant, so it is OK to use a
519
* historical value.
520
*/
521
ret = ar5416LoadNF(ah, AH_PRIVATE(ah)->ah_curchan);
522
523
/* start NF calibration, without updating BB NF register*/
524
ar5416StartNFCal(ah);
525
526
/*
527
* If we failed calibration then tell the driver
528
* we failed and it should do a full chip reset
529
*/
530
if (! ret)
531
return AH_FALSE;
532
}
533
}
534
return AH_TRUE;
535
}
536
537
/*
538
* Recalibrate the lower PHY chips to account for temperature/environment
539
* changes.
540
*/
541
HAL_BOOL
542
ar5416PerCalibration(struct ath_hal *ah, struct ieee80211_channel *chan,
543
HAL_BOOL *isIQdone)
544
{
545
struct ath_hal_5416 *ahp = AH5416(ah);
546
struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
547
HAL_CAL_LIST *curCal = cal->cal_curr;
548
549
if (curCal != AH_NULL && curCal->calData->calType == IQ_MISMATCH_CAL) {
550
return ar5416PerCalibrationN(ah, chan, ahp->ah_rx_chainmask,
551
AH_TRUE, isIQdone);
552
} else {
553
HAL_BOOL isCalDone;
554
555
*isIQdone = AH_FALSE;
556
return ar5416PerCalibrationN(ah, chan, ahp->ah_rx_chainmask,
557
AH_TRUE, &isCalDone);
558
}
559
}
560
561
static HAL_BOOL
562
ar5416GetEepromNoiseFloorThresh(struct ath_hal *ah,
563
const struct ieee80211_channel *chan, int16_t *nft)
564
{
565
if (IEEE80211_IS_CHAN_5GHZ(chan)) {
566
ath_hal_eepromGet(ah, AR_EEP_NFTHRESH_5, nft);
567
return AH_TRUE;
568
}
569
if (IEEE80211_IS_CHAN_2GHZ(chan)) {
570
ath_hal_eepromGet(ah, AR_EEP_NFTHRESH_2, nft);
571
return AH_TRUE;
572
}
573
HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n",
574
__func__, chan->ic_flags);
575
return AH_FALSE;
576
}
577
578
static void
579
ar5416StartNFCal(struct ath_hal *ah)
580
{
581
OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF);
582
OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
583
OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
584
}
585
586
static HAL_BOOL
587
ar5416LoadNF(struct ath_hal *ah, const struct ieee80211_channel *chan)
588
{
589
static const uint32_t ar5416_cca_regs[] = {
590
AR_PHY_CCA,
591
AR_PHY_CH1_CCA,
592
AR_PHY_CH2_CCA,
593
AR_PHY_EXT_CCA,
594
AR_PHY_CH1_EXT_CCA,
595
AR_PHY_CH2_EXT_CCA
596
};
597
struct ar5212NfCalHist *h;
598
int i;
599
int32_t val;
600
uint8_t chainmask;
601
int16_t default_nf = ar5416GetDefaultNF(ah, chan);
602
603
/*
604
* Force NF calibration for all chains.
605
*/
606
if (AR_SREV_KITE(ah)) {
607
/* Kite has only one chain */
608
chainmask = 0x9;
609
} else if (AR_SREV_MERLIN(ah) || AR_SREV_KIWI(ah)) {
610
/* Merlin/Kiwi has only two chains */
611
chainmask = 0x1B;
612
} else {
613
chainmask = 0x3F;
614
}
615
616
/*
617
* Write filtered NF values into maxCCApwr register parameter
618
* so we can load below.
619
*/
620
h = AH5416(ah)->ah_cal.nfCalHist;
621
HALDEBUG(ah, HAL_DEBUG_NFCAL, "CCA: ");
622
for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {
623
/* Don't write to EXT radio CCA registers unless in HT/40 mode */
624
/* XXX this check should really be cleaner! */
625
if (i > 2 && !IEEE80211_IS_CHAN_HT40(chan))
626
continue;
627
628
if (chainmask & (1 << i)) {
629
int16_t nf_val;
630
631
if (h)
632
nf_val = h[i].privNF;
633
else
634
nf_val = default_nf;
635
636
val = OS_REG_READ(ah, ar5416_cca_regs[i]);
637
val &= 0xFFFFFE00;
638
val |= (((uint32_t) nf_val << 1) & 0x1ff);
639
HALDEBUG(ah, HAL_DEBUG_NFCAL, "[%d: %d]", i, nf_val);
640
OS_REG_WRITE(ah, ar5416_cca_regs[i], val);
641
}
642
}
643
HALDEBUG(ah, HAL_DEBUG_NFCAL, "\n");
644
645
/* Load software filtered NF value into baseband internal minCCApwr variable. */
646
OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF);
647
OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
648
OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
649
650
/* Wait for load to complete, should be fast, a few 10s of us. */
651
if (! ar5212WaitNFCalComplete(ah, 1000)) {
652
/*
653
* We timed out waiting for the noisefloor to load, probably due to an
654
* in-progress rx. Simply return here and allow the load plenty of time
655
* to complete before the next calibration interval. We need to avoid
656
* trying to load -50 (which happens below) while the previous load is
657
* still in progress as this can cause rx deafness. Instead by returning
658
* here, the baseband nf cal will just be capped by our present
659
* noisefloor until the next calibration timer.
660
*/
661
HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "Timeout while waiting for "
662
"nf to load: AR_PHY_AGC_CONTROL=0x%x\n",
663
OS_REG_READ(ah, AR_PHY_AGC_CONTROL));
664
return AH_FALSE;
665
}
666
667
/*
668
* Restore maxCCAPower register parameter again so that we're not capped
669
* by the median we just loaded. This will be initial (and max) value
670
* of next noise floor calibration the baseband does.
671
*/
672
for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {
673
/* Don't write to EXT radio CCA registers unless in HT/40 mode */
674
/* XXX this check should really be cleaner! */
675
if (i > 2 && !IEEE80211_IS_CHAN_HT40(chan))
676
continue;
677
678
if (chainmask & (1 << i)) {
679
val = OS_REG_READ(ah, ar5416_cca_regs[i]);
680
val &= 0xFFFFFE00;
681
val |= (((uint32_t)(-50) << 1) & 0x1ff);
682
OS_REG_WRITE(ah, ar5416_cca_regs[i], val);
683
}
684
}
685
return AH_TRUE;
686
}
687
688
/*
689
* This just initialises the "good" values for AR5416 which
690
* may not be right; it'lll be overridden by ar5416SanitizeNF()
691
* to nominal values.
692
*/
693
void
694
ar5416InitNfHistBuff(struct ar5212NfCalHist *h)
695
{
696
int i, j;
697
698
for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {
699
h[i].currIndex = 0;
700
h[i].privNF = AR5416_CCA_MAX_GOOD_VALUE;
701
h[i].invalidNFcount = AR512_NF_CAL_HIST_MAX;
702
for (j = 0; j < AR512_NF_CAL_HIST_MAX; j ++)
703
h[i].nfCalBuffer[j] = AR5416_CCA_MAX_GOOD_VALUE;
704
}
705
}
706
707
/*
708
* Update the noise floor buffer as a ring buffer
709
*/
710
static void
711
ar5416UpdateNFHistBuff(struct ath_hal *ah, struct ar5212NfCalHist *h,
712
int16_t *nfarray)
713
{
714
int i;
715
716
/* XXX TODO: don't record nfarray[] entries for inactive chains */
717
for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {
718
h[i].nfCalBuffer[h[i].currIndex] = nfarray[i];
719
720
if (++h[i].currIndex >= AR512_NF_CAL_HIST_MAX)
721
h[i].currIndex = 0;
722
if (h[i].invalidNFcount > 0) {
723
if (nfarray[i] < AR5416_CCA_MIN_BAD_VALUE ||
724
nfarray[i] > AR5416_CCA_MAX_HIGH_VALUE) {
725
h[i].invalidNFcount = AR512_NF_CAL_HIST_MAX;
726
} else {
727
h[i].invalidNFcount--;
728
h[i].privNF = nfarray[i];
729
}
730
} else {
731
h[i].privNF = ar5212GetNfHistMid(h[i].nfCalBuffer);
732
}
733
}
734
}
735
736
static uint16_t
737
ar5416GetDefaultNF(struct ath_hal *ah, const struct ieee80211_channel *chan)
738
{
739
struct ar5416NfLimits *limit;
740
741
if (!chan || IEEE80211_IS_CHAN_2GHZ(chan))
742
limit = &AH5416(ah)->nf_2g;
743
else
744
limit = &AH5416(ah)->nf_5g;
745
746
return limit->nominal;
747
}
748
749
static void
750
ar5416SanitizeNF(struct ath_hal *ah, int16_t *nf)
751
{
752
753
struct ar5416NfLimits *limit;
754
int i;
755
756
if (IEEE80211_IS_CHAN_2GHZ(AH_PRIVATE(ah)->ah_curchan))
757
limit = &AH5416(ah)->nf_2g;
758
else
759
limit = &AH5416(ah)->nf_5g;
760
761
for (i = 0; i < AR5416_NUM_NF_READINGS; i++) {
762
if (!nf[i])
763
continue;
764
765
if (nf[i] > limit->max) {
766
HALDEBUG(ah, HAL_DEBUG_NFCAL,
767
"NF[%d] (%d) > MAX (%d), correcting to MAX\n",
768
i, nf[i], limit->max);
769
nf[i] = limit->max;
770
} else if (nf[i] < limit->min) {
771
HALDEBUG(ah, HAL_DEBUG_NFCAL,
772
"NF[%d] (%d) < MIN (%d), correcting to NOM\n",
773
i, nf[i], limit->min);
774
nf[i] = limit->nominal;
775
}
776
}
777
}
778
779
/*
780
* Read the NF and check it against the noise floor threshold
781
*
782
* Return 0 if the NF calibration hadn't finished, 0 if it was
783
* invalid, or > 0 for a valid NF reading.
784
*/
785
static int16_t
786
ar5416GetNf(struct ath_hal *ah, struct ieee80211_channel *chan)
787
{
788
int16_t nf, nfThresh;
789
int i;
790
int retval = 0;
791
792
if (ar5212IsNFCalInProgress(ah)) {
793
HALDEBUG(ah, HAL_DEBUG_ANY,
794
"%s: NF didn't complete in calibration window\n", __func__);
795
nf = 0;
796
retval = -1; /* NF didn't finish */
797
} else {
798
/* Finished NF cal, check against threshold */
799
int16_t nfarray[NUM_NOISEFLOOR_READINGS] = { 0 };
800
HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);
801
802
/* TODO - enhance for multiple chains and ext ch */
803
ath_hal_getNoiseFloor(ah, nfarray);
804
nf = nfarray[0];
805
ar5416SanitizeNF(ah, nfarray);
806
if (ar5416GetEepromNoiseFloorThresh(ah, chan, &nfThresh)) {
807
if (nf > nfThresh) {
808
HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
809
"%s: noise floor failed detected; "
810
"detected %d, threshold %d\n", __func__,
811
nf, nfThresh);
812
/*
813
* NB: Don't discriminate 2.4 vs 5Ghz, if this
814
* happens it indicates a problem regardless
815
* of the band.
816
*/
817
chan->ic_state |= IEEE80211_CHANSTATE_CWINT;
818
nf = 0;
819
retval = 0;
820
}
821
} else {
822
nf = 0;
823
retval = 0;
824
}
825
/* Update MIMO channel statistics, regardless of validity or not (for now) */
826
for (i = 0; i < 3; i++) {
827
ichan->noiseFloorCtl[i] = nfarray[i];
828
ichan->noiseFloorExt[i] = nfarray[i + 3];
829
}
830
ichan->privFlags |= CHANNEL_MIMO_NF_VALID;
831
832
ar5416UpdateNFHistBuff(ah, AH5416(ah)->ah_cal.nfCalHist, nfarray);
833
ichan->rawNoiseFloor = nf;
834
retval = nf;
835
}
836
return retval;
837
}
838
839