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$ 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 41 void 42 ar9280olcInit(struct ath_hal *ah) 43 { 44 uint32_t i; 45 46 /* Only do OLC if it's enabled for this chipset */ 47 if (! ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) 48 return; 49 50 HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Setting up TX gain tables.\n", __func__); 51 52 for (i = 0; i < AR9280_TX_GAIN_TABLE_SIZE; i++) 53 AH9280(ah)->originalGain[i] = MS(OS_REG_READ(ah, 54 AR_PHY_TX_GAIN_TBL1 + i * 4), AR_PHY_TX_GAIN); 55 56 AH9280(ah)->PDADCdelta = 0; 57 } 58 59 void 60 ar9280olcGetTxGainIndex(struct ath_hal *ah, 61 const struct ieee80211_channel *chan, 62 struct calDataPerFreqOpLoop *rawDatasetOpLoop, 63 uint8_t *calChans, uint16_t availPiers, uint8_t *pwr, uint8_t *pcdacIdx) 64 { 65 uint8_t pcdac, i = 0; 66 uint16_t idxL = 0, idxR = 0, numPiers; 67 HAL_BOOL match; 68 CHAN_CENTERS centers; 69 70 ar5416GetChannelCenters(ah, chan, ¢ers); 71 72 for (numPiers = 0; numPiers < availPiers; numPiers++) 73 if (calChans[numPiers] == AR5416_BCHAN_UNUSED) 74 break; 75 76 match = ath_ee_getLowerUpperIndex((uint8_t)FREQ2FBIN(centers.synth_center, 77 IEEE80211_IS_CHAN_2GHZ(chan)), calChans, numPiers, 78 &idxL, &idxR); 79 if (match) { 80 pcdac = rawDatasetOpLoop[idxL].pcdac[0][0]; 81 *pwr = rawDatasetOpLoop[idxL].pwrPdg[0][0]; 82 } else { 83 pcdac = rawDatasetOpLoop[idxR].pcdac[0][0]; 84 *pwr = (rawDatasetOpLoop[idxL].pwrPdg[0][0] + 85 rawDatasetOpLoop[idxR].pwrPdg[0][0])/2; 86 } 87 while (pcdac > AH9280(ah)->originalGain[i] && 88 i < (AR9280_TX_GAIN_TABLE_SIZE - 1)) 89 i++; 90 91 *pcdacIdx = i; 92 } 93 94 /* 95 * XXX txPower here is likely not the target txPower in the traditional 96 * XXX sense, but is set by a call to ar9280olcGetTxGainIndex(). 97 * XXX Thus, be careful if you're trying to use this routine yourself. 98 */ 99 void 100 ar9280olcGetPDADCs(struct ath_hal *ah, uint32_t initTxGain, int txPower, 101 uint8_t *pPDADCValues) 102 { 103 uint32_t i; 104 uint32_t offset; 105 106 OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_0, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3); 107 OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_1, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3); 108 109 OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL7, AR_PHY_TX_PWRCTRL_INIT_TX_GAIN, initTxGain); 110 111 offset = txPower; 112 for (i = 0; i < AR5416_NUM_PDADC_VALUES; i++) 113 if (i < offset) 114 pPDADCValues[i] = 0x0; 115 else 116 pPDADCValues[i] = 0xFF; 117 } 118 119 /* 120 * Run temperature compensation calibration. 121 * 122 * The TX gain table is adjusted depending upon the difference 123 * between the initial PDADC value and the currently read 124 * average TX power sample value. This value is only valid if 125 * frames have been transmitted, so currPDADC will be 0 if 126 * no frames have yet been transmitted. 127 */ 128 void 129 ar9280olcTemperatureCompensation(struct ath_hal *ah) 130 { 131 uint32_t rddata, i; 132 int delta, currPDADC, regval; 133 uint8_t hpwr_5g = 0; 134 135 if (! ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) 136 return; 137 138 rddata = OS_REG_READ(ah, AR_PHY_TX_PWRCTRL4); 139 currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT); 140 141 HALDEBUG(ah, HAL_DEBUG_PERCAL, 142 "%s: called: initPDADC=%d, currPDADC=%d\n", 143 __func__, AH5416(ah)->initPDADC, currPDADC); 144 145 if (AH5416(ah)->initPDADC == 0 || currPDADC == 0) 146 return; 147 148 (void) (ath_hal_eepromGet(ah, AR_EEP_DAC_HPWR_5G, &hpwr_5g)); 149 150 if (hpwr_5g) 151 delta = (currPDADC - AH5416(ah)->initPDADC + 4) / 8; 152 else 153 delta = (currPDADC - AH5416(ah)->initPDADC + 5) / 10; 154 155 HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: delta=%d, PDADCdelta=%d\n", 156 __func__, delta, AH9280(ah)->PDADCdelta); 157 158 if (delta != AH9280(ah)->PDADCdelta) { 159 AH9280(ah)->PDADCdelta = delta; 160 for (i = 1; i < AR9280_TX_GAIN_TABLE_SIZE; i++) { 161 regval = AH9280(ah)->originalGain[i] - delta; 162 if (regval < 0) 163 regval = 0; 164 165 OS_REG_RMW_FIELD(ah, 166 AR_PHY_TX_GAIN_TBL1 + i * 4, 167 AR_PHY_TX_GAIN, regval); 168 } 169 } 170 } 171 172 173 static int16_t 174 ar9280ChangeGainBoundarySettings(struct ath_hal *ah, uint16_t *gb, 175 uint16_t numXpdGain, uint16_t pdGainOverlap_t2, int8_t pwr_table_offset, 176 int16_t *diff) 177 { 178 uint16_t k; 179 180 /* Prior to writing the boundaries or the pdadc vs. power table 181 * into the chip registers the default starting point on the pdadc 182 * vs. power table needs to be checked and the curve boundaries 183 * adjusted accordingly 184 */ 185 if (AR_SREV_MERLIN_20_OR_LATER(ah)) { 186 uint16_t gb_limit; 187 188 if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) { 189 /* get the difference in dB */ 190 *diff = (uint16_t)(pwr_table_offset - AR5416_PWR_TABLE_OFFSET_DB); 191 /* get the number of half dB steps */ 192 *diff *= 2; 193 /* change the original gain boundary settings 194 * by the number of half dB steps 195 */ 196 for (k = 0; k < numXpdGain; k++) 197 gb[k] = (uint16_t)(gb[k] - *diff); 198 } 199 /* Because of a hardware limitation, ensure the gain boundary 200 * is not larger than (63 - overlap) 201 */ 202 gb_limit = (uint16_t)(AR5416_MAX_RATE_POWER - pdGainOverlap_t2); 203 204 for (k = 0; k < numXpdGain; k++) 205 gb[k] = (uint16_t)min(gb_limit, gb[k]); 206 } 207 208 return *diff; 209 } 210 211 static void 212 ar9280AdjustPDADCValues(struct ath_hal *ah, int8_t pwr_table_offset, 213 int16_t diff, uint8_t *pdadcValues) 214 { 215 #define NUM_PDADC(diff) (AR5416_NUM_PDADC_VALUES - diff) 216 uint16_t k; 217 218 /* If this is a board that has a pwrTableOffset that differs from 219 * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the 220 * pdadc vs pwr table needs to be adjusted prior to writing to the 221 * chip. 222 */ 223 if (AR_SREV_MERLIN_20_OR_LATER(ah)) { 224 if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) { 225 /* shift the table to start at the new offset */ 226 for (k = 0; k < (uint16_t)NUM_PDADC(diff); k++ ) { 227 pdadcValues[k] = pdadcValues[k + diff]; 228 } 229 230 /* fill the back of the table */ 231 for (k = (uint16_t)NUM_PDADC(diff); k < NUM_PDADC(0); k++) { 232 pdadcValues[k] = pdadcValues[NUM_PDADC(diff)]; 233 } 234 } 235 } 236 #undef NUM_PDADC 237 } 238 /* 239 * This effectively disables the gain boundaries leaving it 240 * to the open-loop TX power control. 241 */ 242 static void 243 ar9280SetGainBoundariesOpenLoop(struct ath_hal *ah, int i, 244 uint16_t pdGainOverlap_t2, uint16_t gainBoundaries[]) 245 { 246 int regChainOffset; 247 248 regChainOffset = ar5416GetRegChainOffset(ah, i); 249 250 /* These are unused for OLC */ 251 (void) pdGainOverlap_t2; 252 (void) gainBoundaries; 253 254 HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: chain %d: writing closed loop values\n", 255 __func__, i); 256 257 OS_REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset, 258 SM(0x6, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | 259 SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) | 260 SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) | 261 SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) | 262 SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4)); 263 } 264 265 /* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */ 266 /* XXX shouldn't be here! */ 267 #define EEP_MINOR(_ah) \ 268 (AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK) 269 #define IS_EEP_MINOR_V2(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2) 270 #define IS_EEP_MINOR_V3(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_3) 271 272 /************************************************************** 273 * ar9280SetPowerCalTable 274 * 275 * Pull the PDADC piers from cal data and interpolate them across the given 276 * points as well as from the nearest pier(s) to get a power detector 277 * linear voltage to power level table. 278 * 279 * Handle OLC for Merlin where required. 280 */ 281 HAL_BOOL 282 ar9280SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, 283 const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset) 284 { 285 CAL_DATA_PER_FREQ *pRawDataset; 286 uint8_t *pCalBChans = AH_NULL; 287 uint16_t pdGainOverlap_t2; 288 static uint8_t pdadcValues[AR5416_NUM_PDADC_VALUES]; 289 uint16_t gainBoundaries[AR5416_PD_GAINS_IN_MASK]; 290 uint16_t numPiers, i; 291 int16_t tMinCalPower; 292 uint16_t numXpdGain, xpdMask; 293 uint16_t xpdGainValues[AR5416_NUM_PD_GAINS]; 294 uint32_t regChainOffset; 295 int8_t pwr_table_offset; 296 297 OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues)); 298 299 xpdMask = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].xpdGain; 300 301 (void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET, &pwr_table_offset); 302 303 304 if (IS_EEP_MINOR_V2(ah)) { 305 pdGainOverlap_t2 = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pdGainOverlap; 306 } else { 307 pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP)); 308 } 309 310 if (IEEE80211_IS_CHAN_2GHZ(chan)) { 311 pCalBChans = pEepData->calFreqPier2G; 312 numPiers = AR5416_NUM_2G_CAL_PIERS; 313 } else { 314 pCalBChans = pEepData->calFreqPier5G; 315 numPiers = AR5416_NUM_5G_CAL_PIERS; 316 } 317 318 /* If OLC is being done, set the init PDADC value appropriately */ 319 if (IEEE80211_IS_CHAN_2GHZ(chan) && AR_SREV_MERLIN_20_OR_LATER(ah) && 320 ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) { 321 struct calDataPerFreq *pRawDataset = pEepData->calPierData2G[0]; 322 AH5416(ah)->initPDADC = ((struct calDataPerFreqOpLoop *) pRawDataset)->vpdPdg[0][0]; 323 } else { 324 /* 325 * XXX ath9k doesn't clear this for 5ghz mode if 326 * it were set in 2ghz mode before! 327 * The Merlin OLC temperature compensation code 328 * uses this to calculate the PDADC delta during 329 * calibration ; 0 here effectively stops the 330 * temperature compensation calibration from 331 * occurring. 332 */ 333 AH5416(ah)->initPDADC = 0; 334 } 335 336 /* Calculate the value of xpdgains from the xpdGain Mask */ 337 numXpdGain = ar5416GetXpdGainValues(ah, xpdMask, xpdGainValues); 338 339 /* Write the detector gain biases and their number */ 340 ar5416WriteDetectorGainBiases(ah, numXpdGain, xpdGainValues); 341 342 for (i = 0; i < AR5416_MAX_CHAINS; i++) { 343 regChainOffset = ar5416GetRegChainOffset(ah, i); 344 if (pEepData->baseEepHeader.txMask & (1 << i)) { 345 uint16_t diff; 346 347 if (IEEE80211_IS_CHAN_2GHZ(chan)) { 348 pRawDataset = pEepData->calPierData2G[i]; 349 } else { 350 pRawDataset = pEepData->calPierData5G[i]; 351 } 352 353 /* Fetch the gain boundaries and the PDADC values */ 354 if (AR_SREV_MERLIN_20_OR_LATER(ah) && 355 ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) { 356 uint8_t pcdacIdx; 357 uint8_t txPower; 358 359 ar9280olcGetTxGainIndex(ah, chan, 360 (struct calDataPerFreqOpLoop *) pRawDataset, 361 pCalBChans, numPiers, &txPower, &pcdacIdx); 362 ar9280olcGetPDADCs(ah, pcdacIdx, txPower / 2, pdadcValues); 363 } else { 364 ar5416GetGainBoundariesAndPdadcs(ah, chan, 365 pRawDataset, pCalBChans, numPiers, 366 pdGainOverlap_t2, &tMinCalPower, 367 gainBoundaries, pdadcValues, numXpdGain); 368 } 369 370 /* 371 * Prior to writing the boundaries or the pdadc vs. power table 372 * into the chip registers the default starting point on the pdadc 373 * vs. power table needs to be checked and the curve boundaries 374 * adjusted accordingly 375 */ 376 diff = ar9280ChangeGainBoundarySettings(ah, 377 gainBoundaries, numXpdGain, pdGainOverlap_t2, 378 pwr_table_offset, &diff); 379 380 if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) { 381 /* Set gain boundaries for either open- or closed-loop TPC */ 382 if (AR_SREV_MERLIN_20_OR_LATER(ah) && 383 ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) 384 ar9280SetGainBoundariesOpenLoop(ah, 385 i, pdGainOverlap_t2, 386 gainBoundaries); 387 else 388 ar5416SetGainBoundariesClosedLoop(ah, 389 i, pdGainOverlap_t2, 390 gainBoundaries); 391 } 392 393 /* 394 * If this is a board that has a pwrTableOffset that differs from 395 * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the 396 * pdadc vs pwr table needs to be adjusted prior to writing to the 397 * chip. 398 */ 399 ar9280AdjustPDADCValues(ah, pwr_table_offset, diff, pdadcValues); 400 401 /* Write the power values into the baseband power table */ 402 ar5416WritePdadcValues(ah, i, pdadcValues); 403 } 404 } 405 *pTxPowerIndexOffset = 0; 406 407 return AH_TRUE; 408 } 409