Open source ath(4) HAL code.
[dragonfly.git] / sys / dev / netif / ath / hal / ath_hal / ar5416 / ar5416_cal.c
1 /*
2  * Copyright (c) 2002-2008 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  * $Id: ar5416_cal.c,v 1.7 2008/11/11 17:43:23 sam Exp $
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 "ar5416/ar5416.h"
28 #include "ar5416/ar5416reg.h"
29 #include "ar5416/ar5416phy.h"
30
31 /* Owl specific stuff */
32 #define NUM_NOISEFLOOR_READINGS 6       /* 3 chains * (ctl + ext) */
33
34 static void ar5416StartNFCal(struct ath_hal *ah);
35 static void ar5416LoadNF(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *);
36 static int16_t ar5416GetNf(struct ath_hal *, HAL_CHANNEL_INTERNAL *);
37
38 /*
39  * Determine if calibration is supported by device and channel flags
40  */
41 static OS_INLINE HAL_BOOL
42 ar5416IsCalSupp(struct ath_hal *ah, HAL_CHANNEL *chan, HAL_CAL_TYPE calType) 
43 {
44         struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
45
46         switch (calType & cal->suppCals) {
47         case IQ_MISMATCH_CAL:
48                 /* Run IQ Mismatch for non-CCK only */
49                 return !IS_CHAN_B(chan);
50         case ADC_GAIN_CAL:
51         case ADC_DC_CAL:
52                 /* Run ADC Gain Cal for non-CCK & non 2GHz-HT20 only */
53                 return !IS_CHAN_B(chan) &&
54                     !(IS_CHAN_2GHZ(chan) && IS_CHAN_HT20(chan));
55         }
56         return AH_FALSE;
57 }
58
59 /*
60  * Setup HW to collect samples used for current cal
61  */
62 static void
63 ar5416SetupMeasurement(struct ath_hal *ah, HAL_CAL_LIST *currCal)
64 {
65         /* Start calibration w/ 2^(INIT_IQCAL_LOG_COUNT_MAX+1) samples */
66         OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4,
67             AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX,
68             currCal->calData->calCountMax);
69
70         /* Select calibration to run */
71         switch (currCal->calData->calType) {
72         case IQ_MISMATCH_CAL:
73                 OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_IQ);
74                 HALDEBUG(ah, HAL_DEBUG_PERCAL,
75                     "%s: start IQ Mismatch calibration\n", __func__);
76                 break;
77         case ADC_GAIN_CAL:
78                 OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_GAIN);
79                 HALDEBUG(ah, HAL_DEBUG_PERCAL,
80                     "%s: start ADC Gain calibration\n", __func__);
81                 break;
82         case ADC_DC_CAL:
83                 OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_PER);
84                 HALDEBUG(ah, HAL_DEBUG_PERCAL,
85                     "%s: start ADC DC calibration\n", __func__);
86                 break;
87         case ADC_DC_INIT_CAL:
88                 OS_REG_WRITE(ah, AR_PHY_CALMODE, AR_PHY_CALMODE_ADC_DC_INIT);
89                 HALDEBUG(ah, HAL_DEBUG_PERCAL,
90                     "%s: start Init ADC DC calibration\n", __func__);
91                 break;
92         }
93         /* Kick-off cal */
94         OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_DO_CAL);
95 }
96
97 /*
98  * Initialize shared data structures and prepare a cal to be run.
99  */
100 static void
101 ar5416ResetMeasurement(struct ath_hal *ah, HAL_CAL_LIST *currCal)
102 {
103         struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
104
105         /* Reset data structures shared between different calibrations */
106         OS_MEMZERO(cal->caldata, sizeof(cal->caldata));
107         cal->calSamples = 0;
108
109         /* Setup HW for new calibration */
110         ar5416SetupMeasurement(ah, currCal);
111
112         /* Change SW state to RUNNING for this calibration */
113         currCal->calState = CAL_RUNNING;
114 }
115
116 #if 0
117 /*
118  * Run non-periodic calibrations.
119  */
120 static HAL_BOOL
121 ar5416RunInitCals(struct ath_hal *ah, int init_cal_count)
122 {
123         struct ath_hal_5416 *ahp = AH5416(ah);
124         struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
125         HAL_CHANNEL_INTERNAL ichan;     /* XXX bogus */
126         HAL_CAL_LIST *curCal = ahp->ah_cal_curr;
127         HAL_BOOL isCalDone;
128         int i;
129
130         if (curCal == AH_NULL)
131                 return AH_FALSE;
132
133         ichan.calValid = 0;
134         for (i = 0; i < init_cal_count; i++) {
135                 /* Reset this Cal */
136                 ar5416ResetMeasurement(ah, curCal);
137                 /* Poll for offset calibration complete */
138                 if (!ath_hal_wait(ah, AR_PHY_TIMING_CTRL4, AR_PHY_TIMING_CTRL4_DO_CAL, 0)) {
139                         HALDEBUG(ah, HAL_DEBUG_ANY,
140                             "%s: Cal %d failed to finish in 100ms.\n",
141                             __func__, curCal->calData->calType);
142                         /* Re-initialize list pointers for periodic cals */
143                         cal->cal_list = cal->cal_last = cal->cal_curr = AH_NULL;
144                         return AH_FALSE;
145                 }
146                 /* Run this cal */
147                 ar5416DoCalibration(ah, &ichan, ahp->ah_rxchainmask,
148                     curCal, &isCalDone);
149                 if (!isCalDone)
150                         HALDEBUG(ah, HAL_DEBUG_ANY,
151                             "%s: init cal %d did not complete.\n",
152                             __func__, curCal->calData->calType);
153                 if (curCal->calNext != AH_NULL)
154                         curCal = curCal->calNext;
155         }
156
157         /* Re-initialize list pointers for periodic cals */
158         cal->cal_list = cal->cal_last = cal->cal_curr = AH_NULL;
159         return AH_TRUE;
160 }
161 #endif
162
163 /*
164  * Initialize Calibration infrastructure.
165  */
166 HAL_BOOL
167 ar5416InitCal(struct ath_hal *ah, HAL_CHANNEL *chan)
168 {
169         struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
170         HAL_CHANNEL_INTERNAL *ichan;
171
172         ichan = ath_hal_checkchannel(ah, chan);
173         HALASSERT(ichan != AH_NULL);
174
175         if (AR_SREV_MERLIN_10_OR_LATER(ah)) {
176                 /* Enable Rx Filter Cal */
177                 OS_REG_CLR_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC);
178                 OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
179                     AR_PHY_AGC_CONTROL_FLTR_CAL);
180
181                 /* Clear the carrier leak cal bit */
182                 OS_REG_CLR_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE);
183
184                 /* kick off the cal */
185                 OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL);
186
187                 /* Poll for offset calibration complete */
188                 if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) {
189                         HALDEBUG(ah, HAL_DEBUG_ANY,
190                             "%s: offset calibration failed to complete in 1ms; "
191                             "noisy environment?\n", __func__);
192                         return AH_FALSE;
193                 }
194
195                 /* Set the cl cal bit and rerun the cal a 2nd time */
196                 /* Enable Rx Filter Cal */
197                 OS_REG_CLR_BIT(ah, AR_PHY_ADC_CTL, AR_PHY_ADC_CTL_OFF_PWDADC);
198                 OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL,
199                     AR_PHY_AGC_CONTROL_FLTR_CAL);
200
201                 OS_REG_SET_BIT(ah, AR_PHY_CL_CAL_CTL, AR_PHY_CL_CAL_ENABLE);
202         }       
203
204         /* Calibrate the AGC */
205         OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL);
206
207         /* Poll for offset calibration complete */
208         if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) {
209                 HALDEBUG(ah, HAL_DEBUG_ANY,
210                     "%s: offset calibration did not complete in 1ms; "
211                     "noisy environment?\n", __func__);
212                 return AH_FALSE;
213         }
214
215         /* 
216          * Do NF calibration after DC offset and other CALs.
217          * Per system engineers, noise floor value can sometimes be 20 dB
218          * higher than normal value if DC offset and noise floor cal are
219          * triggered at the same time.
220          */
221         OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
222
223         /* Initialize list pointers */
224         cal->cal_list = cal->cal_last = cal->cal_curr = AH_NULL;
225
226         /*
227          * Enable IQ, ADC Gain, ADC DC Offset Cals
228          */
229         if (AR_SREV_SOWL_10_OR_LATER(ah)) {
230                 /* Setup all non-periodic, init time only calibrations */
231                 /* XXX: Init DC Offset not working yet */
232 #if 0
233                 if (ar5416IsCalSupp(ah, chan, ADC_DC_INIT_CAL)) {
234                         INIT_CAL(&cal->adcDcCalInitData);
235                         INSERT_CAL(cal, &cal->adcDcCalInitData);
236                 }
237                 /* Initialize current pointer to first element in list */
238                 cal->cal_curr = cal->cal_list;
239
240                 if (cal->ah_cal_curr != AH_NULL && !ar5416RunInitCals(ah, 0))
241                         return AH_FALSE;
242 #endif
243         }
244
245         /* If Cals are supported, add them to list via INIT/INSERT_CAL */
246         if (ar5416IsCalSupp(ah, chan, ADC_GAIN_CAL)) {
247                 INIT_CAL(&cal->adcGainCalData);
248                 INSERT_CAL(cal, &cal->adcGainCalData);
249                 HALDEBUG(ah, HAL_DEBUG_PERCAL,
250                     "%s: enable ADC Gain Calibration.\n", __func__);
251         }
252         if (ar5416IsCalSupp(ah, chan, ADC_DC_CAL)) {
253                 INIT_CAL(&cal->adcDcCalData);
254                 INSERT_CAL(cal, &cal->adcDcCalData);
255                 HALDEBUG(ah, HAL_DEBUG_PERCAL,
256                     "%s: enable ADC DC Calibration.\n", __func__);
257         }
258         if (ar5416IsCalSupp(ah, chan, IQ_MISMATCH_CAL)) {
259                 INIT_CAL(&cal->iqCalData);
260                 INSERT_CAL(cal, &cal->iqCalData);
261                 HALDEBUG(ah, HAL_DEBUG_PERCAL,
262                     "%s: enable IQ Calibration.\n", __func__);
263         }
264         /* Initialize current pointer to first element in list */
265         cal->cal_curr = cal->cal_list;
266
267         /* Kick off measurements for the first cal */
268         if (cal->cal_curr != AH_NULL)
269                 ar5416ResetMeasurement(ah, cal->cal_curr);
270
271         /* Mark all calibrations on this channel as being invalid */
272         ichan->calValid = 0;
273
274         return AH_TRUE;
275 }
276
277 /*
278  * Entry point for upper layers to restart current cal.
279  * Reset the calibration valid bit in channel.
280  */
281 HAL_BOOL
282 ar5416ResetCalValid(struct ath_hal *ah, HAL_CHANNEL *chan)
283 {
284         struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
285         HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);
286         HAL_CAL_LIST *currCal = cal->cal_curr;
287
288         if (!AR_SREV_SOWL_10_OR_LATER(ah))
289                 return AH_FALSE;
290         if (currCal == AH_NULL)
291                 return AH_FALSE;
292         if (ichan == AH_NULL) {
293                 HALDEBUG(ah, HAL_DEBUG_ANY,
294                     "%s: invalid channel %u/0x%x; no mapping\n",
295                     __func__, chan->channel, chan->channelFlags);
296                 return AH_FALSE;
297         }
298         /*
299          * Expected that this calibration has run before, post-reset.
300          * Current state should be done
301          */
302         if (currCal->calState != CAL_DONE) {
303                 HALDEBUG(ah, HAL_DEBUG_ANY,
304                     "%s: Calibration state incorrect, %d\n",
305                     __func__, currCal->calState);
306                 return AH_FALSE;
307         }
308
309         /* Verify Cal is supported on this channel */
310         if (!ar5416IsCalSupp(ah, chan, currCal->calData->calType))
311                 return AH_FALSE;
312
313         HALDEBUG(ah, HAL_DEBUG_PERCAL,
314             "%s: Resetting Cal %d state for channel %u/0x%x\n",
315             __func__, currCal->calData->calType, chan->channel,
316             chan->channelFlags);
317
318         /* Disable cal validity in channel */
319         ichan->calValid &= ~currCal->calData->calType;
320         currCal->calState = CAL_WAITING;
321
322         return AH_TRUE;
323 }
324
325 /*
326  * Recalibrate the lower PHY chips to account for temperature/environment
327  * changes.
328  */
329 static void
330 ar5416DoCalibration(struct ath_hal *ah,  HAL_CHANNEL_INTERNAL *ichan,
331         uint8_t rxchainmask, HAL_CAL_LIST *currCal, HAL_BOOL *isCalDone)
332 {
333         struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
334
335         /* Cal is assumed not done until explicitly set below */
336         *isCalDone = AH_FALSE;
337
338         HALDEBUG(ah, HAL_DEBUG_PERCAL,
339             "%s: %s Calibration, state %d, calValid 0x%x\n",
340             __func__, currCal->calData->calName, currCal->calState,
341             ichan->calValid);
342
343         /* Calibration in progress. */
344         if (currCal->calState == CAL_RUNNING) {
345                 /* Check to see if it has finished. */
346                 if (!(OS_REG_READ(ah, AR_PHY_TIMING_CTRL4) & AR_PHY_TIMING_CTRL4_DO_CAL)) {
347                         HALDEBUG(ah, HAL_DEBUG_PERCAL,
348                             "%s: sample %d of %d finished\n",
349                             __func__, cal->calSamples,
350                             currCal->calData->calNumSamples);
351                         /* 
352                          * Collect measurements for active chains.
353                          */
354                         currCal->calData->calCollect(ah);
355                         if (++cal->calSamples >= currCal->calData->calNumSamples) {
356                                 int i, numChains = 0;
357                                 for (i = 0; i < AR5416_MAX_CHAINS; i++) {
358                                         if (rxchainmask & (1 << i))
359                                                 numChains++;
360                                 }
361                                 /* 
362                                  * Process accumulated data
363                                  */
364                                 currCal->calData->calPostProc(ah, numChains);
365
366                                 /* Calibration has finished. */
367                                 ichan->calValid |= currCal->calData->calType;
368                                 currCal->calState = CAL_DONE;
369                                 *isCalDone = AH_TRUE;
370                         } else {
371                                 /*
372                                  * Set-up to collect of another sub-sample.
373                                  */
374                                 ar5416SetupMeasurement(ah, currCal);
375                         }
376                 }
377         } else if (!(ichan->calValid & currCal->calData->calType)) {
378                 /* If current cal is marked invalid in channel, kick it off */
379                 ar5416ResetMeasurement(ah, currCal);
380         }
381 }
382
383 /*
384  * Internal interface to schedule periodic calibration work.
385  */
386 HAL_BOOL
387 ar5416PerCalibrationN(struct ath_hal *ah,  HAL_CHANNEL *chan,
388         u_int rxchainmask, HAL_BOOL longcal, HAL_BOOL *isCalDone)
389 {
390         struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
391         HAL_CAL_LIST *currCal = cal->cal_curr;
392         HAL_CHANNEL_INTERNAL *ichan;
393
394         OS_MARK(ah, AH_MARK_PERCAL, chan->channel);
395
396         *isCalDone = AH_TRUE;
397
398         /* Invalid channel check */
399         ichan = ath_hal_checkchannel(ah, chan);
400         if (ichan == AH_NULL) {
401                 HALDEBUG(ah, HAL_DEBUG_ANY,
402                     "%s: invalid channel %u/0x%x; no mapping\n",
403                     __func__, chan->channel, chan->channelFlags);
404                 return AH_FALSE;
405         }
406
407         /*
408          * For given calibration:
409          * 1. Call generic cal routine
410          * 2. When this cal is done (isCalDone) if we have more cals waiting
411          *    (eg after reset), mask this to upper layers by not propagating
412          *    isCalDone if it is set to TRUE.
413          *    Instead, change isCalDone to FALSE and setup the waiting cal(s)
414          *    to be run.
415          */
416         if (currCal != AH_NULL &&
417             (currCal->calState == CAL_RUNNING ||
418              currCal->calState == CAL_WAITING)) {
419                 ar5416DoCalibration(ah, ichan, rxchainmask, currCal, isCalDone);
420                 if (*isCalDone == AH_TRUE) {
421                         cal->cal_curr = currCal = currCal->calNext;
422                         if (currCal->calState == CAL_WAITING) {
423                                 *isCalDone = AH_FALSE;
424                                 ar5416ResetMeasurement(ah, currCal);
425                         }
426                 }
427         }
428
429         /* Do NF cal only at longer intervals */
430         if (longcal) {
431                 /*
432                  * Get the value from the previous NF cal
433                  * and update the history buffer.
434                  */
435                 ar5416GetNf(ah, ichan);
436
437                 /* 
438                  * Load the NF from history buffer of the current channel.
439                  * NF is slow time-variant, so it is OK to use a
440                  * historical value.
441                  */
442                 ar5416LoadNF(ah, AH_PRIVATE(ah)->ah_curchan);
443
444                 /* start NF calibration, without updating BB NF register*/
445                 ar5416StartNFCal(ah);
446
447                 if ((ichan->channelFlags & CHANNEL_CW_INT) != 0) {
448                         /* report up and clear internal state */
449                         chan->channelFlags |= CHANNEL_CW_INT;
450                         ichan->channelFlags &= ~CHANNEL_CW_INT;
451                 }
452         }
453         return AH_TRUE;
454 }
455
456 /*
457  * Recalibrate the lower PHY chips to account for temperature/environment
458  * changes.
459  */
460 HAL_BOOL
461 ar5416PerCalibration(struct ath_hal *ah,  HAL_CHANNEL *chan, HAL_BOOL *isIQdone)
462 {
463         struct ath_hal_5416 *ahp = AH5416(ah);
464         struct ar5416PerCal *cal = &AH5416(ah)->ah_cal;
465         HAL_CAL_LIST *curCal = cal->cal_curr;
466
467         if (curCal != AH_NULL && curCal->calData->calType == IQ_MISMATCH_CAL) {
468                 return ar5416PerCalibrationN(ah, chan, ahp->ah_rx_chainmask,
469                     AH_TRUE, isIQdone);
470         } else {
471                 HAL_BOOL isCalDone;
472
473                 *isIQdone = AH_FALSE;
474                 return ar5416PerCalibrationN(ah, chan, ahp->ah_rx_chainmask,
475                     AH_TRUE, &isCalDone);
476         }
477 }
478
479 static HAL_BOOL
480 ar5416GetEepromNoiseFloorThresh(struct ath_hal *ah,
481         const HAL_CHANNEL_INTERNAL *chan, int16_t *nft)
482 {
483         switch (chan->channelFlags & CHANNEL_ALL_NOTURBO) {
484         case CHANNEL_A:
485         case CHANNEL_A_HT20:
486         case CHANNEL_A_HT40PLUS:
487         case CHANNEL_A_HT40MINUS:
488                 ath_hal_eepromGet(ah, AR_EEP_NFTHRESH_5, nft);
489                 break;
490         case CHANNEL_B:
491         case CHANNEL_G:
492         case CHANNEL_G_HT20:
493         case CHANNEL_G_HT40PLUS:
494         case CHANNEL_G_HT40MINUS:
495                 ath_hal_eepromGet(ah, AR_EEP_NFTHRESH_2, nft);
496                 break;
497         default:
498                 HALDEBUG(ah, HAL_DEBUG_ANY,
499                     "%s: invalid channel flags 0x%x\n",
500                     __func__, chan->channelFlags);
501                 return AH_FALSE;
502         }
503         return AH_TRUE;
504 }
505
506 static void
507 ar5416StartNFCal(struct ath_hal *ah)
508 {
509         OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF);
510         OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
511         OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
512 }
513
514 static void
515 ar5416LoadNF(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan)
516 {
517         static const uint32_t ar5416_cca_regs[] = {
518                 AR_PHY_CCA,
519                 AR_PHY_CH1_CCA,
520                 AR_PHY_CH2_CCA,
521                 AR_PHY_EXT_CCA,
522                 AR_PHY_CH1_EXT_CCA,
523                 AR_PHY_CH2_EXT_CCA
524         };
525         struct ar5212NfCalHist *h;
526         int i, j;
527         int32_t val;
528         uint8_t chainmask;
529
530         /*
531          * Force NF calibration for all chains.
532          */
533         if (AR_SREV_KITE(ah)) {
534                 /* Kite has only one chain */
535                 chainmask = 0x9;
536         } else if (AR_SREV_MERLIN(ah)) {
537                 /* Merlin has only two chains */
538                 chainmask = 0x1B;
539         } else {
540                 chainmask = 0x3F;
541         }
542
543         /*
544          * Write filtered NF values into maxCCApwr register parameter
545          * so we can load below.
546          */
547         h = AH5416(ah)->ah_cal.nfCalHist;
548         for (i = 0; i < AR5416_NUM_NF_READINGS; i ++)
549                 if (chainmask & (1 << i)) { 
550                         val = OS_REG_READ(ah, ar5416_cca_regs[i]);
551                         val &= 0xFFFFFE00;
552                         val |= (((uint32_t)(h[i].privNF) << 1) & 0x1ff);
553                         OS_REG_WRITE(ah, ar5416_cca_regs[i], val);
554                 }
555
556         /* Load software filtered NF value into baseband internal minCCApwr variable. */
557         OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF);
558         OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
559         OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
560
561         /* Wait for load to complete, should be fast, a few 10s of us. */
562         for (j = 0; j < 1000; j++) {
563                 if ((OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) == 0)
564                         break;
565                 OS_DELAY(10);
566         }
567
568         /*
569          * Restore maxCCAPower register parameter again so that we're not capped
570          * by the median we just loaded.  This will be initial (and max) value
571          * of next noise floor calibration the baseband does.  
572          */
573         for (i = 0; i < AR5416_NUM_NF_READINGS; i ++)
574                 if (chainmask & (1 << i)) {     
575                         val = OS_REG_READ(ah, ar5416_cca_regs[i]);
576                         val &= 0xFFFFFE00;
577                         val |= (((uint32_t)(-50) << 1) & 0x1ff);
578                         OS_REG_WRITE(ah, ar5416_cca_regs[i], val);
579                 }
580 }
581
582 void
583 ar5416InitNfHistBuff(struct ar5212NfCalHist *h)
584 {
585         int i, j;
586
587         for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {
588                 h[i].currIndex = 0;
589                 h[i].privNF = AR5416_CCA_MAX_GOOD_VALUE;
590                 h[i].invalidNFcount = AR512_NF_CAL_HIST_MAX;
591                 for (j = 0; j < AR512_NF_CAL_HIST_MAX; j ++)
592                         h[i].nfCalBuffer[j] = AR5416_CCA_MAX_GOOD_VALUE;
593         }
594 }
595
596 /*
597  * Update the noise floor buffer as a ring buffer
598  */
599 static void
600 ar5416UpdateNFHistBuff(struct ar5212NfCalHist *h, int16_t *nfarray)
601 {
602         int i;
603
604         for (i = 0; i < AR5416_NUM_NF_READINGS; i ++) {
605                 h[i].nfCalBuffer[h[i].currIndex] = nfarray[i];
606
607                 if (++h[i].currIndex >= AR512_NF_CAL_HIST_MAX)
608                         h[i].currIndex = 0;
609                 if (h[i].invalidNFcount > 0) {
610                         if (nfarray[i] < AR5416_CCA_MIN_BAD_VALUE ||
611                             nfarray[i] > AR5416_CCA_MAX_HIGH_VALUE) {
612                                 h[i].invalidNFcount = AR512_NF_CAL_HIST_MAX;
613                         } else {
614                                 h[i].invalidNFcount--;
615                                 h[i].privNF = nfarray[i];
616                         }
617                 } else {
618                         h[i].privNF = ar5212GetNfHistMid(h[i].nfCalBuffer);
619                 }
620         }
621 }   
622
623 /*
624  * Read the NF and check it against the noise floor threshhold
625  */
626 static int16_t
627 ar5416GetNf(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan)
628 {
629         int16_t nf, nfThresh;
630
631         if (OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) {
632                 HALDEBUG(ah, HAL_DEBUG_ANY,
633                     "%s: NF didn't complete in calibration window\n", __func__);
634                 nf = 0;
635         } else {
636                 /* Finished NF cal, check against threshold */
637                 int16_t nfarray[NUM_NOISEFLOOR_READINGS] = { 0 };
638                         
639                 /* TODO - enhance for multiple chains and ext ch */
640                 ath_hal_getNoiseFloor(ah, nfarray);
641                 nf = nfarray[0];
642                 if (ar5416GetEepromNoiseFloorThresh(ah, chan, &nfThresh)) {
643                         if (nf > nfThresh) {
644                                 HALDEBUG(ah, HAL_DEBUG_ANY,
645                                     "%s: noise floor failed detected; "
646                                     "detected %d, threshold %d\n", __func__,
647                                     nf, nfThresh);
648                                 /*
649                                  * NB: Don't discriminate 2.4 vs 5Ghz, if this
650                                  *     happens it indicates a problem regardless
651                                  *     of the band.
652                                  */
653                                 chan->channelFlags |= CHANNEL_CW_INT;
654                                 nf = 0;
655                         }
656                 } else {
657                         nf = 0;
658                 }
659                 ar5416UpdateNFHistBuff(AH5416(ah)->ah_cal.nfCalHist, nfarray);
660                 chan->rawNoiseFloor = nf;
661         }
662         return nf;
663 }