| 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 | |