]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/dev/ath/ath_hal/ar5416/ar5416_cal.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / dev / ath / ath_hal / ar5416 / ar5416_cal.c
1 /*
2  * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting
3  * Copyright (c) 2002-2008 Atheros Communications, Inc.
4  *
5  * Permission to use, copy, modify, and/or distribute this software for any
6  * purpose with or without fee is hereby granted, provided that the above
7  * copyright notice and this permission notice appear in all copies.
8  *
9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16  *
17  * $FreeBSD$
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 void 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 /*
199  * AGC calibration for the AR5416, AR9130, AR9160, AR9280.
200  */
201 HAL_BOOL
202 ar5416InitCalHardware(struct ath_hal *ah, const struct ieee80211_channel *chan)
203 {
204
205         if (AR_SREV_MERLIN_10_OR_LATER(ah)) {
206                 /* Disable ADC */
207                 OS_REG_CLR_BIT(ah, AR_PHY_ADC_CTL,
208                     AR_PHY_ADC_CTL_OFF_PWDADC);
209
210                 /* Enable Rx Filter Cal */
211                 OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
212                     AR_PHY_AGC_CONTROL_FLTR_CAL);
213         }       
214
215         /* Calibrate the AGC */
216         OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL);
217
218         /* Poll for offset calibration complete */
219         if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) {
220                 HALDEBUG(ah, HAL_DEBUG_ANY,
221                     "%s: offset calibration did not complete in 1ms; "
222                     "noisy environment?\n", __func__);
223                 return AH_FALSE;
224         }
225
226         if (AR_SREV_MERLIN_10_OR_LATER(ah)) {
227                 /* Enable ADC */
228                 OS_REG_SET_BIT(ah, AR_PHY_ADC_CTL,
229                     AR_PHY_ADC_CTL_OFF_PWDADC);
230
231                 /* Disable Rx Filter Cal */
232                 OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL,
233                     AR_PHY_AGC_CONTROL_FLTR_CAL);
234         }
235
236         return AH_TRUE;
237 }
238
239 /*
240  * Initialize Calibration infrastructure.
241  */
242 #define MAX_CAL_CHECK           32
243 HAL_BOOL
244 ar5416InitCal(struct ath_hal *ah, const struct ieee80211_channel *chan)
245 {
246         struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
247         HAL_CHANNEL_INTERNAL *ichan;
248
249         ichan = ath_hal_checkchannel(ah, chan);
250         HALASSERT(ichan != AH_NULL);
251
252         /* Do initial chipset-specific calibration */
253         if (! AH5416(ah)->ah_cal_initcal(ah, chan)) {
254                 HALDEBUG(ah, HAL_DEBUG_ANY,
255                     "%s: initial chipset calibration did "
256                     "not complete in time; noisy environment?\n", __func__);
257                 return AH_FALSE;
258         }
259
260         /* If there's PA Cal, do it */
261         if (AH5416(ah)->ah_cal_pacal)
262                 AH5416(ah)->ah_cal_pacal(ah, AH_TRUE);
263
264         /* 
265          * Do NF calibration after DC offset and other CALs.
266          * Per system engineers, noise floor value can sometimes be 20 dB
267          * higher than normal value if DC offset and noise floor cal are
268          * triggered at the same time.
269          */
270         OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
271
272         /*
273          * This may take a while to run; make sure subsequent
274          * calibration routines check that this has completed
275          * before reading the value and triggering a subsequent
276          * calibration.
277          */
278
279         /* Initialize list pointers */
280         cal->cal_list = cal->cal_last = cal->cal_curr = AH_NULL;
281
282         /*
283          * Enable IQ, ADC Gain, ADC DC Offset Cals
284          */
285         if (AR_SREV_HOWL(ah) || AR_SREV_SOWL_10_OR_LATER(ah)) {
286                 /* Setup all non-periodic, init time only calibrations */
287                 /* XXX: Init DC Offset not working yet */
288 #if 0
289                 if (ar5416IsCalSupp(ah, chan, ADC_DC_INIT_CAL)) {
290                         INIT_CAL(&cal->adcDcCalInitData);
291                         INSERT_CAL(cal, &cal->adcDcCalInitData);
292                 }
293                 /* Initialize current pointer to first element in list */
294                 cal->cal_curr = cal->cal_list;
295
296                 if (cal->ah_cal_curr != AH_NULL && !ar5416RunInitCals(ah, 0))
297                         return AH_FALSE;
298 #endif
299         }
300
301         /* If Cals are supported, add them to list via INIT/INSERT_CAL */
302         if (ar5416IsCalSupp(ah, chan, ADC_GAIN_CAL)) {
303                 INIT_CAL(&cal->adcGainCalData);
304                 INSERT_CAL(cal, &cal->adcGainCalData);
305                 HALDEBUG(ah, HAL_DEBUG_PERCAL,
306                     "%s: enable ADC Gain Calibration.\n", __func__);
307         }
308         if (ar5416IsCalSupp(ah, chan, ADC_DC_CAL)) {
309                 INIT_CAL(&cal->adcDcCalData);
310                 INSERT_CAL(cal, &cal->adcDcCalData);
311                 HALDEBUG(ah, HAL_DEBUG_PERCAL,
312                     "%s: enable ADC DC Calibration.\n", __func__);
313         }
314         if (ar5416IsCalSupp(ah, chan, IQ_MISMATCH_CAL)) {
315                 INIT_CAL(&cal->iqCalData);
316                 INSERT_CAL(cal, &cal->iqCalData);
317                 HALDEBUG(ah, HAL_DEBUG_PERCAL,
318                     "%s: enable IQ Calibration.\n", __func__);
319         }
320         /* Initialize current pointer to first element in list */
321         cal->cal_curr = cal->cal_list;
322
323         /* Kick off measurements for the first cal */
324         if (cal->cal_curr != AH_NULL)
325                 ar5416ResetMeasurement(ah, cal->cal_curr);
326
327         /* Mark all calibrations on this channel as being invalid */
328         ichan->calValid = 0;
329
330         return AH_TRUE;
331 #undef  MAX_CAL_CHECK
332 }
333
334 /*
335  * Entry point for upper layers to restart current cal.
336  * Reset the calibration valid bit in channel.
337  */
338 HAL_BOOL
339 ar5416ResetCalValid(struct ath_hal *ah, const struct ieee80211_channel *chan)
340 {
341         struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
342         HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);
343         HAL_CAL_LIST *currCal = cal->cal_curr;
344
345         if (!AR_SREV_SOWL_10_OR_LATER(ah))
346                 return AH_FALSE;
347         if (currCal == AH_NULL)
348                 return AH_FALSE;
349         if (ichan == AH_NULL) {
350                 HALDEBUG(ah, HAL_DEBUG_ANY,
351                     "%s: invalid channel %u/0x%x; no mapping\n",
352                     __func__, chan->ic_freq, chan->ic_flags);
353                 return AH_FALSE;
354         }
355         /*
356          * Expected that this calibration has run before, post-reset.
357          * Current state should be done
358          */
359         if (currCal->calState != CAL_DONE) {
360                 HALDEBUG(ah, HAL_DEBUG_ANY,
361                     "%s: Calibration state incorrect, %d\n",
362                     __func__, currCal->calState);
363                 return AH_FALSE;
364         }
365
366         /* Verify Cal is supported on this channel */
367         if (!ar5416IsCalSupp(ah, chan, currCal->calData->calType))
368                 return AH_FALSE;
369
370         HALDEBUG(ah, HAL_DEBUG_PERCAL,
371             "%s: Resetting Cal %d state for channel %u/0x%x\n",
372             __func__, currCal->calData->calType, chan->ic_freq,
373             chan->ic_flags);
374
375         /* Disable cal validity in channel */
376         ichan->calValid &= ~currCal->calData->calType;
377         currCal->calState = CAL_WAITING;
378
379         return AH_TRUE;
380 }
381
382 /*
383  * Recalibrate the lower PHY chips to account for temperature/environment
384  * changes.
385  */
386 static void
387 ar5416DoCalibration(struct ath_hal *ah,  HAL_CHANNEL_INTERNAL *ichan,
388         uint8_t rxchainmask, HAL_CAL_LIST *currCal, HAL_BOOL *isCalDone)
389 {
390         struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
391
392         /* Cal is assumed not done until explicitly set below */
393         *isCalDone = AH_FALSE;
394
395         HALDEBUG(ah, HAL_DEBUG_PERCAL,
396             "%s: %s Calibration, state %d, calValid 0x%x\n",
397             __func__, currCal->calData->calName, currCal->calState,
398             ichan->calValid);
399
400         /* Calibration in progress. */
401         if (currCal->calState == CAL_RUNNING) {
402                 /* Check to see if it has finished. */
403                 if (!(OS_REG_READ(ah, AR_PHY_TIMING_CTRL4) & AR_PHY_TIMING_CTRL4_DO_CAL)) {
404                         HALDEBUG(ah, HAL_DEBUG_PERCAL,
405                             "%s: sample %d of %d finished\n",
406                             __func__, cal->calSamples,
407                             currCal->calData->calNumSamples);
408                         /* 
409                          * Collect measurements for active chains.
410                          */
411                         currCal->calData->calCollect(ah);
412                         if (++cal->calSamples >= currCal->calData->calNumSamples) {
413                                 int i, numChains = 0;
414                                 for (i = 0; i < AR5416_MAX_CHAINS; i++) {
415                                         if (rxchainmask & (1 << i))
416                                                 numChains++;
417                                 }
418                                 /* 
419                                  * Process accumulated data
420                                  */
421                                 currCal->calData->calPostProc(ah, numChains);
422
423                                 /* Calibration has finished. */
424                                 ichan->calValid |= currCal->calData->calType;
425                                 currCal->calState = CAL_DONE;
426                                 *isCalDone = AH_TRUE;
427                         } else {
428                                 /*
429                                  * Set-up to collect of another sub-sample.
430                                  */
431                                 ar5416SetupMeasurement(ah, currCal);
432                         }
433                 }
434         } else if (!(ichan->calValid & currCal->calData->calType)) {
435                 /* If current cal is marked invalid in channel, kick it off */
436                 ar5416ResetMeasurement(ah, currCal);
437         }
438 }
439
440 /*
441  * Internal interface to schedule periodic calibration work.
442  */
443 HAL_BOOL
444 ar5416PerCalibrationN(struct ath_hal *ah, struct ieee80211_channel *chan,
445         u_int rxchainmask, HAL_BOOL longcal, HAL_BOOL *isCalDone)
446 {
447         struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
448         HAL_CAL_LIST *currCal = cal->cal_curr;
449         HAL_CHANNEL_INTERNAL *ichan;
450         int r;
451
452         OS_MARK(ah, AH_MARK_PERCAL, chan->ic_freq);
453
454         *isCalDone = AH_TRUE;
455
456         /*
457          * Since ath_hal calls the PerCal method with rxchainmask=0x1;
458          * override it with the current chainmask. The upper levels currently
459          * doesn't know about the chainmask.
460          */
461         rxchainmask = AH5416(ah)->ah_rx_chainmask;
462
463         /* Invalid channel check */
464         ichan = ath_hal_checkchannel(ah, chan);
465         if (ichan == AH_NULL) {
466                 HALDEBUG(ah, HAL_DEBUG_ANY,
467                     "%s: invalid channel %u/0x%x; no mapping\n",
468                     __func__, chan->ic_freq, chan->ic_flags);
469                 return AH_FALSE;
470         }
471
472         /*
473          * For given calibration:
474          * 1. Call generic cal routine
475          * 2. When this cal is done (isCalDone) if we have more cals waiting
476          *    (eg after reset), mask this to upper layers by not propagating
477          *    isCalDone if it is set to TRUE.
478          *    Instead, change isCalDone to FALSE and setup the waiting cal(s)
479          *    to be run.
480          */
481         if (currCal != AH_NULL &&
482             (currCal->calState == CAL_RUNNING ||
483              currCal->calState == CAL_WAITING)) {
484                 ar5416DoCalibration(ah, ichan, rxchainmask, currCal, isCalDone);
485                 if (*isCalDone == AH_TRUE) {
486                         cal->cal_curr = currCal = currCal->calNext;
487                         if (currCal->calState == CAL_WAITING) {
488                                 *isCalDone = AH_FALSE;
489                                 ar5416ResetMeasurement(ah, currCal);
490                         }
491                 }
492         }
493
494         /* Do NF cal only at longer intervals */
495         if (longcal) {
496                 /* Do PA calibration if the chipset supports */
497                 if (AH5416(ah)->ah_cal_pacal)
498                         AH5416(ah)->ah_cal_pacal(ah, AH_FALSE);
499
500                 /* Do open-loop temperature compensation if the chipset needs it */
501                 if (ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
502                         AH5416(ah)->ah_olcTempCompensation(ah);
503
504                 /*
505                  * Get the value from the previous NF cal
506                  * and update the history buffer.
507                  */
508                 r = ar5416GetNf(ah, chan);
509                 if (r == 0 || r == -1) {
510                         /* NF calibration result isn't valid */
511                         HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "%s: NF calibration"
512                             " didn't finish; delaying CCA\n", __func__);
513                 } else {
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                         ar5416LoadNF(ah, AH_PRIVATE(ah)->ah_curchan);
522
523                         /* start NF calibration, without updating BB NF register*/
524                         ar5416StartNFCal(ah);
525                 }
526         }
527         return AH_TRUE;
528 }
529
530 /*
531  * Recalibrate the lower PHY chips to account for temperature/environment
532  * changes.
533  */
534 HAL_BOOL
535 ar5416PerCalibration(struct ath_hal *ah, struct ieee80211_channel *chan,
536         HAL_BOOL *isIQdone)
537 {
538         struct ath_hal_5416 *ahp = AH5416(ah);
539         struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
540         HAL_CAL_LIST *curCal = cal->cal_curr;
541
542         if (curCal != AH_NULL && curCal->calData->calType == IQ_MISMATCH_CAL) {
543                 return ar5416PerCalibrationN(ah, chan, ahp->ah_rx_chainmask,
544                     AH_TRUE, isIQdone);
545         } else {
546                 HAL_BOOL isCalDone;
547
548                 *isIQdone = AH_FALSE;
549                 return ar5416PerCalibrationN(ah, chan, ahp->ah_rx_chainmask,
550                     AH_TRUE, &isCalDone);
551         }
552 }
553
554 static HAL_BOOL
555 ar5416GetEepromNoiseFloorThresh(struct ath_hal *ah,
556         const struct ieee80211_channel *chan, int16_t *nft)
557 {
558         if (IEEE80211_IS_CHAN_5GHZ(chan)) {
559                 ath_hal_eepromGet(ah, AR_EEP_NFTHRESH_5, nft);
560                 return AH_TRUE;
561         }
562         if (IEEE80211_IS_CHAN_2GHZ(chan)) {
563                 ath_hal_eepromGet(ah, AR_EEP_NFTHRESH_2, nft);
564                 return AH_TRUE;
565         }
566         HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n",
567             __func__, chan->ic_flags);
568         return AH_FALSE;
569 }
570
571 static void
572 ar5416StartNFCal(struct ath_hal *ah)
573 {
574         OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF);
575         OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
576         OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
577 }
578
579 static void
580 ar5416LoadNF(struct ath_hal *ah, const struct ieee80211_channel *chan)
581 {
582         static const uint32_t ar5416_cca_regs[] = {
583                 AR_PHY_CCA,
584                 AR_PHY_CH1_CCA,
585                 AR_PHY_CH2_CCA,
586                 AR_PHY_EXT_CCA,
587                 AR_PHY_CH1_EXT_CCA,
588                 AR_PHY_CH2_EXT_CCA
589         };
590         struct ar5212NfCalHist *h;
591         int i;
592         int32_t val;
593         uint8_t chainmask;
594         int16_t default_nf = ar5416GetDefaultNF(ah, chan);
595
596         /*
597          * Force NF calibration for all chains.
598          */
599         if (AR_SREV_KITE(ah)) {
600                 /* Kite has only one chain */
601                 chainmask = 0x9;
602         } else if (AR_SREV_MERLIN(ah) || AR_SREV_KIWI(ah)) {
603                 /* Merlin/Kiwi has only two chains */
604                 chainmask = 0x1B;
605         } else {
606                 chainmask = 0x3F;
607         }
608
609         /*
610          * Write filtered NF values into maxCCApwr register parameter
611          * so we can load below.
612          */
613         h = AH5416(ah)->ah_cal.nfCalHist;
614         HALDEBUG(ah, HAL_DEBUG_NFCAL, "CCA: ");
615         for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {
616
617                 /* Don't write to EXT radio CCA registers unless in HT/40 mode */
618                 /* XXX this check should really be cleaner! */
619                 if (i > 2 && !IEEE80211_IS_CHAN_HT40(chan))
620                         continue;
621
622                 if (chainmask & (1 << i)) { 
623                         int16_t nf_val;
624
625                         if (h)
626                                 nf_val = h[i].privNF;
627                         else
628                                 nf_val = default_nf;
629
630                         val = OS_REG_READ(ah, ar5416_cca_regs[i]);
631                         val &= 0xFFFFFE00;
632                         val |= (((uint32_t) nf_val << 1) & 0x1ff);
633                         HALDEBUG(ah, HAL_DEBUG_NFCAL, "[%d: %d]", i, nf_val);
634                         OS_REG_WRITE(ah, ar5416_cca_regs[i], val);
635                 }
636         }
637         HALDEBUG(ah, HAL_DEBUG_NFCAL, "\n");
638
639         /* Load software filtered NF value into baseband internal minCCApwr variable. */
640         OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF);
641         OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
642         OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
643
644         /* Wait for load to complete, should be fast, a few 10s of us. */
645         if (! ar5212WaitNFCalComplete(ah, 1000)) {
646                 /*
647                  * We timed out waiting for the noisefloor to load, probably due to an
648                  * in-progress rx. Simply return here and allow the load plenty of time
649                  * to complete before the next calibration interval.  We need to avoid
650                  * trying to load -50 (which happens below) while the previous load is
651                  * still in progress as this can cause rx deafness. Instead by returning
652                  * here, the baseband nf cal will just be capped by our present
653                  * noisefloor until the next calibration timer.
654                  */
655                 HALDEBUG(ah, HAL_DEBUG_UNMASKABLE, "Timeout while waiting for "
656                     "nf to load: AR_PHY_AGC_CONTROL=0x%x\n",
657                     OS_REG_READ(ah, AR_PHY_AGC_CONTROL));
658                 return;
659         }
660
661         /*
662          * Restore maxCCAPower register parameter again so that we're not capped
663          * by the median we just loaded.  This will be initial (and max) value
664          * of next noise floor calibration the baseband does.  
665          */
666         for (i = 0; i < AR5416_NUM_NF_READINGS; i ++)
667
668                 /* Don't write to EXT radio CCA registers unless in HT/40 mode */
669                 /* XXX this check should really be cleaner! */
670                 if (i > 2 && !IEEE80211_IS_CHAN_HT40(chan))
671                         continue;
672
673                 if (chainmask & (1 << i)) {     
674                         val = OS_REG_READ(ah, ar5416_cca_regs[i]);
675                         val &= 0xFFFFFE00;
676                         val |= (((uint32_t)(-50) << 1) & 0x1ff);
677                         OS_REG_WRITE(ah, ar5416_cca_regs[i], val);
678                 }
679 }
680
681 /*
682  * This just initialises the "good" values for AR5416 which
683  * may not be right; it'lll be overridden by ar5416SanitizeNF()
684  * to nominal values.
685  */
686 void
687 ar5416InitNfHistBuff(struct ar5212NfCalHist *h)
688 {
689         int i, j;
690
691         for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {
692                 h[i].currIndex = 0;
693                 h[i].privNF = AR5416_CCA_MAX_GOOD_VALUE;
694                 h[i].invalidNFcount = AR512_NF_CAL_HIST_MAX;
695                 for (j = 0; j < AR512_NF_CAL_HIST_MAX; j ++)
696                         h[i].nfCalBuffer[j] = AR5416_CCA_MAX_GOOD_VALUE;
697         }
698 }
699
700 /*
701  * Update the noise floor buffer as a ring buffer
702  */
703 static void
704 ar5416UpdateNFHistBuff(struct ath_hal *ah, struct ar5212NfCalHist *h,
705     int16_t *nfarray)
706 {
707         int i;
708
709         /* XXX TODO: don't record nfarray[] entries for inactive chains */
710         for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {
711                 h[i].nfCalBuffer[h[i].currIndex] = nfarray[i];
712
713                 if (++h[i].currIndex >= AR512_NF_CAL_HIST_MAX)
714                         h[i].currIndex = 0;
715                 if (h[i].invalidNFcount > 0) {
716                         if (nfarray[i] < AR5416_CCA_MIN_BAD_VALUE ||
717                             nfarray[i] > AR5416_CCA_MAX_HIGH_VALUE) {
718                                 h[i].invalidNFcount = AR512_NF_CAL_HIST_MAX;
719                         } else {
720                                 h[i].invalidNFcount--;
721                                 h[i].privNF = nfarray[i];
722                         }
723                 } else {
724                         h[i].privNF = ar5212GetNfHistMid(h[i].nfCalBuffer);
725                 }
726         }
727 }   
728
729 static uint16_t
730 ar5416GetDefaultNF(struct ath_hal *ah, const struct ieee80211_channel *chan)
731 {
732         struct ar5416NfLimits *limit;
733
734         if (!chan || IEEE80211_IS_CHAN_2GHZ(chan))
735                 limit = &AH5416(ah)->nf_2g;
736         else
737                 limit = &AH5416(ah)->nf_5g;
738
739         return limit->nominal;
740 }
741
742 static void
743 ar5416SanitizeNF(struct ath_hal *ah, int16_t *nf)
744 {
745
746         struct ar5416NfLimits *limit;
747         int i;
748
749         if (IEEE80211_IS_CHAN_2GHZ(AH_PRIVATE(ah)->ah_curchan))
750                 limit = &AH5416(ah)->nf_2g;
751         else
752                 limit = &AH5416(ah)->nf_5g;
753
754         for (i = 0; i < AR5416_NUM_NF_READINGS; i++) {
755                 if (!nf[i])
756                         continue;
757
758                 if (nf[i] > limit->max) {
759                         HALDEBUG(ah, HAL_DEBUG_NFCAL,
760                                   "NF[%d] (%d) > MAX (%d), correcting to MAX\n",
761                                   i, nf[i], limit->max);
762                         nf[i] = limit->max;
763                 } else if (nf[i] < limit->min) {
764                         HALDEBUG(ah, HAL_DEBUG_NFCAL,
765                                   "NF[%d] (%d) < MIN (%d), correcting to NOM\n",
766                                   i, nf[i], limit->min);
767                         nf[i] = limit->nominal;
768                 }
769         }
770 }
771
772
773 /*
774  * Read the NF and check it against the noise floor threshhold
775  *
776  * Return 0 if the NF calibration hadn't finished, 0 if it was
777  * invalid, or > 0 for a valid NF reading.
778  */
779 static int16_t
780 ar5416GetNf(struct ath_hal *ah, struct ieee80211_channel *chan)
781 {
782         int16_t nf, nfThresh;
783         int i;
784         int retval = 0;
785
786         if (ar5212IsNFCalInProgress(ah)) {
787                 HALDEBUG(ah, HAL_DEBUG_ANY,
788                     "%s: NF didn't complete in calibration window\n", __func__);
789                 nf = 0;
790                 retval = -1;    /* NF didn't finish */
791         } else {
792                 /* Finished NF cal, check against threshold */
793                 int16_t nfarray[NUM_NOISEFLOOR_READINGS] = { 0 };
794                 HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);
795                         
796                 /* TODO - enhance for multiple chains and ext ch */
797                 ath_hal_getNoiseFloor(ah, nfarray);
798                 nf = nfarray[0];
799                 ar5416SanitizeNF(ah, nfarray);
800                 if (ar5416GetEepromNoiseFloorThresh(ah, chan, &nfThresh)) {
801                         if (nf > nfThresh) {
802                                 HALDEBUG(ah, HAL_DEBUG_UNMASKABLE,
803                                     "%s: noise floor failed detected; "
804                                     "detected %d, threshold %d\n", __func__,
805                                     nf, nfThresh);
806                                 /*
807                                  * NB: Don't discriminate 2.4 vs 5Ghz, if this
808                                  *     happens it indicates a problem regardless
809                                  *     of the band.
810                                  */
811                                 chan->ic_state |= IEEE80211_CHANSTATE_CWINT;
812                                 nf = 0;
813                                 retval = 0;
814                         }
815                 } else {
816                         nf = 0;
817                         retval = 0;
818                 }
819                 /* Update MIMO channel statistics, regardless of validity or not (for now) */
820                 for (i = 0; i < 3; i++) {
821                         ichan->noiseFloorCtl[i] = nfarray[i];
822                         ichan->noiseFloorExt[i] = nfarray[i + 3];
823                 }
824                 ichan->privFlags |= CHANNEL_MIMO_NF_VALID;
825
826                 ar5416UpdateNFHistBuff(ah, AH5416(ah)->ah_cal.nfCalHist, nfarray);
827                 ichan->rawNoiseFloor = nf;
828                 retval = nf;
829         }
830         return retval;
831 }