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