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