ar9280_olc.c revision 219444
1/* 2 * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23 * SUCH DAMAGE. 24 * 25 * $FreeBSD: head/sys/dev/ath/ath_hal/ar9002/ar9280_olc.c 219444 2011-03-10 06:08:24Z adrian $ 26 */ 27#include "opt_ah.h" 28 29#include "ah.h" 30#include "ah_internal.h" 31 32#include "ah_eeprom_v14.h" 33 34#include "ar9002/ar9280.h" 35#include "ar5416/ar5416reg.h" 36#include "ar5416/ar5416phy.h" 37#include "ar9002/ar9002phy.h" 38 39#include "ar9002/ar9280_olc.h" 40 41void 42ar9280olcInit(struct ath_hal *ah) 43{ 44 uint32_t i; 45 46 for (i = 0; i < AR9280_TX_GAIN_TABLE_SIZE; i++) 47 AH9280(ah)->originalGain[i] = MS(OS_REG_READ(ah, 48 AR_PHY_TX_GAIN_TBL1 + i * 4), AR_PHY_TX_GAIN); 49 50 AH9280(ah)->PDADCdelta = 0; 51} 52 53void 54ar9280olcGetTxGainIndex(struct ath_hal *ah, 55 const struct ieee80211_channel *chan, 56 struct calDataPerFreqOpLoop *rawDatasetOpLoop, 57 uint8_t *calChans, uint16_t availPiers, uint8_t *pwr, uint8_t *pcdacIdx) 58{ 59 uint8_t pcdac, i = 0; 60 uint16_t idxL = 0, idxR = 0, numPiers; 61 HAL_BOOL match; 62 CHAN_CENTERS centers; 63 64 ar5416GetChannelCenters(ah, chan, ¢ers); 65 66 for (numPiers = 0; numPiers < availPiers; numPiers++) 67 if (calChans[numPiers] == AR5416_BCHAN_UNUSED) 68 break; 69 70 match = getLowerUpperIndex((uint8_t)FREQ2FBIN(centers.synth_center, 71 IEEE80211_IS_CHAN_2GHZ(chan)), calChans, numPiers, 72 &idxL, &idxR); 73 if (match) { 74 pcdac = rawDatasetOpLoop[idxL].pcdac[0][0]; 75 *pwr = rawDatasetOpLoop[idxL].pwrPdg[0][0]; 76 } else { 77 pcdac = rawDatasetOpLoop[idxR].pcdac[0][0]; 78 *pwr = (rawDatasetOpLoop[idxL].pwrPdg[0][0] + 79 rawDatasetOpLoop[idxR].pwrPdg[0][0])/2; 80 } 81 while (pcdac > AH9280(ah)->originalGain[i] && 82 i < (AR9280_TX_GAIN_TABLE_SIZE - 1)) 83 i++; 84 85 *pcdacIdx = i; 86} 87 88/* 89 * XXX txPower here is likely not the target txPower in the traditional 90 * XXX sense, but is set by a call to ar9280olcGetTxGainIndex(). 91 * XXX Thus, be careful if you're trying to use this routine yourself. 92 */ 93void 94ar9280olcGetPDADCs(struct ath_hal *ah, uint32_t initTxGain, int txPower, 95 uint8_t *pPDADCValues) 96{ 97 uint32_t i; 98 uint32_t offset; 99 100 OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_0, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3); 101 OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_1, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3); 102 103 OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL7, AR_PHY_TX_PWRCTRL_INIT_TX_GAIN, initTxGain); 104 105 offset = txPower; 106 for (i = 0; i < AR5416_NUM_PDADC_VALUES; i++) 107 if (i < offset) 108 pPDADCValues[i] = 0x0; 109 else 110 pPDADCValues[i] = 0xFF; 111} 112 113/* 114 * Run temperature compensation calibration. 115 * 116 * The TX gain table is adjusted depending upon the difference 117 * between the initial PDADC value and the currently read 118 * average TX power sample value. This value is only valid if 119 * frames have been transmitted, so currPDADC will be 0 if 120 * no frames have yet been transmitted. 121 */ 122void 123ar9280olcTemperatureCompensation(struct ath_hal *ah) 124{ 125 uint32_t rddata, i; 126 int delta, currPDADC, regval; 127 uint8_t hpwr_5g = 0; 128 129 rddata = OS_REG_READ(ah, AR_PHY_TX_PWRCTRL4); 130 currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT); 131 132 HALDEBUG(ah, HAL_DEBUG_PERCAL, 133 "%s: called: initPDADC=%d, currPDADC=%d\n", 134 __func__, AH5416(ah)->initPDADC, currPDADC); 135 136 if (AH5416(ah)->initPDADC == 0 || currPDADC == 0) 137 return; 138 139 (void) (ath_hal_eepromGet(ah, AR_EEP_DAC_HPWR_5G, &hpwr_5g)); 140 141 if (hpwr_5g) 142 delta = (currPDADC - AH5416(ah)->initPDADC + 4) / 8; 143 else 144 delta = (currPDADC - AH5416(ah)->initPDADC + 5) / 10; 145 146 HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: delta=%d, PDADCdelta=%d\n", 147 __func__, delta, AH9280(ah)->PDADCdelta); 148 149 if (delta != AH9280(ah)->PDADCdelta) { 150 AH9280(ah)->PDADCdelta = delta; 151 for (i = 1; i < AR9280_TX_GAIN_TABLE_SIZE; i++) { 152 regval = AH9280(ah)->originalGain[i] - delta; 153 if (regval < 0) 154 regval = 0; 155 156 OS_REG_RMW_FIELD(ah, 157 AR_PHY_TX_GAIN_TBL1 + i * 4, 158 AR_PHY_TX_GAIN, regval); 159 } 160 } 161} 162 163 164static int16_t 165ar9280ChangeGainBoundarySettings(struct ath_hal *ah, uint16_t *gb, 166 uint16_t numXpdGain, uint16_t pdGainOverlap_t2, int8_t pwr_table_offset, 167 int16_t *diff) 168{ 169 uint16_t k; 170 171 /* Prior to writing the boundaries or the pdadc vs. power table 172 * into the chip registers the default starting point on the pdadc 173 * vs. power table needs to be checked and the curve boundaries 174 * adjusted accordingly 175 */ 176 if (AR_SREV_MERLIN_20_OR_LATER(ah)) { 177 uint16_t gb_limit; 178 179 if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) { 180 /* get the difference in dB */ 181 *diff = (uint16_t)(pwr_table_offset - AR5416_PWR_TABLE_OFFSET_DB); 182 /* get the number of half dB steps */ 183 *diff *= 2; 184 /* change the original gain boundary settings 185 * by the number of half dB steps 186 */ 187 for (k = 0; k < numXpdGain; k++) 188 gb[k] = (uint16_t)(gb[k] - *diff); 189 } 190 /* Because of a hardware limitation, ensure the gain boundary 191 * is not larger than (63 - overlap) 192 */ 193 gb_limit = (uint16_t)(AR5416_MAX_RATE_POWER - pdGainOverlap_t2); 194 195 for (k = 0; k < numXpdGain; k++) 196 gb[k] = (uint16_t)min(gb_limit, gb[k]); 197 } 198 199 return *diff; 200} 201 202static void 203ar9280AdjustPDADCValues(struct ath_hal *ah, int8_t pwr_table_offset, 204 int16_t diff, uint8_t *pdadcValues) 205{ 206#define NUM_PDADC(diff) (AR5416_NUM_PDADC_VALUES - diff) 207 uint16_t k; 208 209 /* If this is a board that has a pwrTableOffset that differs from 210 * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the 211 * pdadc vs pwr table needs to be adjusted prior to writing to the 212 * chip. 213 */ 214 if (AR_SREV_MERLIN_20_OR_LATER(ah)) { 215 if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) { 216 /* shift the table to start at the new offset */ 217 for (k = 0; k < (uint16_t)NUM_PDADC(diff); k++ ) { 218 pdadcValues[k] = pdadcValues[k + diff]; 219 } 220 221 /* fill the back of the table */ 222 for (k = (uint16_t)NUM_PDADC(diff); k < NUM_PDADC(0); k++) { 223 pdadcValues[k] = pdadcValues[NUM_PDADC(diff)]; 224 } 225 } 226 } 227#undef NUM_PDADC 228} 229/* 230 * This effectively disables the gain boundaries leaving it 231 * to the open-loop TX power control. 232 */ 233static void 234ar9280SetGainBoundariesOpenLoop(struct ath_hal *ah, int regChainOffset, 235 uint16_t pdGainOverlap_t2, uint16_t gainBoundaries[]) 236{ 237 /* These are unused for OLC */ 238 (void) pdGainOverlap_t2; 239 (void) gainBoundaries; 240 241 OS_REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset, 242 SM(0x6, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | 243 SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) | 244 SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) | 245 SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) | 246 SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4)); 247} 248 249/* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */ 250/* XXX shouldn't be here! */ 251#define EEP_MINOR(_ah) \ 252 (AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK) 253#define IS_EEP_MINOR_V2(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2) 254#define IS_EEP_MINOR_V3(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_3) 255 256/************************************************************** 257 * ar9280SetPowerCalTable 258 * 259 * Pull the PDADC piers from cal data and interpolate them across the given 260 * points as well as from the nearest pier(s) to get a power detector 261 * linear voltage to power level table. 262 * 263 * Handle OLC for Merlin where required. 264 */ 265HAL_BOOL 266ar9280SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, 267 const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset) 268{ 269 CAL_DATA_PER_FREQ *pRawDataset; 270 uint8_t *pCalBChans = AH_NULL; 271 uint16_t pdGainOverlap_t2; 272 static uint8_t pdadcValues[AR5416_NUM_PDADC_VALUES]; 273 uint16_t gainBoundaries[AR5416_PD_GAINS_IN_MASK]; 274 uint16_t numPiers, i; 275 int16_t tMinCalPower; 276 uint16_t numXpdGain, xpdMask; 277 uint16_t xpdGainValues[AR5416_NUM_PD_GAINS]; 278 uint32_t regChainOffset; 279 int8_t pwr_table_offset; 280 281 OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues)); 282 283 xpdMask = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].xpdGain; 284 285 (void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET, &pwr_table_offset); 286 287 288 if (IS_EEP_MINOR_V2(ah)) { 289 pdGainOverlap_t2 = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pdGainOverlap; 290 } else { 291 pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP)); 292 } 293 294 if (IEEE80211_IS_CHAN_2GHZ(chan)) { 295 pCalBChans = pEepData->calFreqPier2G; 296 numPiers = AR5416_NUM_2G_CAL_PIERS; 297 } else { 298 pCalBChans = pEepData->calFreqPier5G; 299 numPiers = AR5416_NUM_5G_CAL_PIERS; 300 } 301 302 /* If OLC is being done, set the init PDADC value appropriately */ 303 if (IEEE80211_IS_CHAN_2GHZ(chan) && AR_SREV_MERLIN_20_OR_LATER(ah) && 304 ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) { 305 struct calDataPerFreq *pRawDataset = pEepData->calPierData2G[0]; 306 AH5416(ah)->initPDADC = ((struct calDataPerFreqOpLoop *) pRawDataset)->vpdPdg[0][0]; 307 } else { 308 /* 309 * XXX ath9k doesn't clear this for 5ghz mode if 310 * it were set in 2ghz mode before! 311 * The Merlin OLC temperature compensation code 312 * uses this to calculate the PDADC delta during 313 * calibration ; 0 here effectively stops the 314 * temperature compensation calibration from 315 * occuring. 316 */ 317 AH5416(ah)->initPDADC = 0; 318 } 319 320 /* Calculate the value of xpdgains from the xpdGain Mask */ 321 numXpdGain = ar5416GetXpdGainValues(ah, xpdMask, xpdGainValues); 322 323 /* Write the detector gain biases and their number */ 324 ar5416WriteDetectorGainBiases(ah, numXpdGain, xpdGainValues); 325 326 for (i = 0; i < AR5416_MAX_CHAINS; i++) { 327 regChainOffset = ar5416GetRegChainOffset(ah, i); 328 if (pEepData->baseEepHeader.txMask & (1 << i)) { 329 uint16_t diff; 330 331 if (IEEE80211_IS_CHAN_2GHZ(chan)) { 332 pRawDataset = pEepData->calPierData2G[i]; 333 } else { 334 pRawDataset = pEepData->calPierData5G[i]; 335 } 336 337 /* Fetch the gain boundaries and the PDADC values */ 338 if (AR_SREV_MERLIN_20_OR_LATER(ah) && 339 ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) { 340 uint8_t pcdacIdx; 341 uint8_t txPower; 342 343 ar9280olcGetTxGainIndex(ah, chan, 344 (struct calDataPerFreqOpLoop *) pRawDataset, 345 pCalBChans, numPiers, &txPower, &pcdacIdx); 346 ar9280olcGetPDADCs(ah, pcdacIdx, txPower / 2, pdadcValues); 347 } else { 348 ar5416GetGainBoundariesAndPdadcs(ah, chan, 349 pRawDataset, pCalBChans, numPiers, 350 pdGainOverlap_t2, &tMinCalPower, 351 gainBoundaries, pdadcValues, numXpdGain); 352 } 353 354 /* 355 * Prior to writing the boundaries or the pdadc vs. power table 356 * into the chip registers the default starting point on the pdadc 357 * vs. power table needs to be checked and the curve boundaries 358 * adjusted accordingly 359 */ 360 diff = ar9280ChangeGainBoundarySettings(ah, 361 gainBoundaries, numXpdGain, pdGainOverlap_t2, 362 pwr_table_offset, &diff); 363 364 if ((i == 0) || AR_SREV_OWL_20_OR_LATER(ah)) { 365 /* Set gain boundaries for either open- or closed-loop TPC */ 366 if (AR_SREV_MERLIN_20_OR_LATER(ah) && 367 ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) 368 ar9280SetGainBoundariesOpenLoop(ah, 369 regChainOffset, pdGainOverlap_t2, 370 gainBoundaries); 371 else 372 ar5416SetGainBoundariesClosedLoop(ah, 373 regChainOffset, pdGainOverlap_t2, 374 gainBoundaries); 375 } 376 377 /* 378 * If this is a board that has a pwrTableOffset that differs from 379 * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the 380 * pdadc vs pwr table needs to be adjusted prior to writing to the 381 * chip. 382 */ 383 ar9280AdjustPDADCValues(ah, pwr_table_offset, diff, pdadcValues); 384 385 /* Write the power values into the baseband power table */ 386 ar5416WritePdadcValues(ah, regChainOffset, pdadcValues); 387 } 388 } 389 *pTxPowerIndexOffset = 0; 390 391 return AH_TRUE; 392} 393