ar9280_olc.c revision 219393
1219393Sadrian/* 2219393Sadrian * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd. 3219393Sadrian * 4219393Sadrian * Redistribution and use in source and binary forms, with or without 5219393Sadrian * modification, are permitted provided that the following conditions 6219393Sadrian * are met: 7219393Sadrian * 1. Redistributions of source code must retain the above copyright 8219393Sadrian * notice, this list of conditions and the following disclaimer. 9219393Sadrian * 2. Redistributions in binary form must reproduce the above copyright 10219393Sadrian * notice, this list of conditions and the following disclaimer in the 11219393Sadrian * documentation and/or other materials provided with the distribution. 12219393Sadrian * 13219393Sadrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 14219393Sadrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15219393Sadrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16219393Sadrian * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 17219393Sadrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 18219393Sadrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 19219393Sadrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 20219393Sadrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 21219393Sadrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 22219393Sadrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 23219393Sadrian * SUCH DAMAGE. 24219393Sadrian * 25219393Sadrian * $FreeBSD: head/sys/dev/ath/ath_hal/ar9002/ar9280_olc.c 219393 2011-03-08 06:59:59Z adrian $ 26219393Sadrian */ 27219393Sadrian#include "opt_ah.h" 28219393Sadrian 29219393Sadrian#include "ah.h" 30219393Sadrian#include "ah_internal.h" 31219393Sadrian 32219393Sadrian#include "ah_eeprom_v14.h" 33219393Sadrian 34219393Sadrian#include "ar9002/ar9280.h" 35219393Sadrian#include "ar5416/ar5416reg.h" 36219393Sadrian#include "ar5416/ar5416phy.h" 37219393Sadrian#include "ar9002/ar9002phy.h" 38219393Sadrian 39219393Sadrian#include "ar9002/ar9280_olc.h" 40219393Sadrian 41219393Sadrianvoid 42219393Sadrianar9280olcInit(struct ath_hal *ah) 43219393Sadrian{ 44219393Sadrian uint32_t i; 45219393Sadrian 46219393Sadrian for (i = 0; i < AR9280_TX_GAIN_TABLE_SIZE; i++) 47219393Sadrian AH9280(ah)->originalGain[i] = MS(OS_REG_READ(ah, 48219393Sadrian AR_PHY_TX_GAIN_TBL1 + i * 4), AR_PHY_TX_GAIN); 49219393Sadrian 50219393Sadrian AH9280(ah)->PDADCdelta = 0; 51219393Sadrian} 52219393Sadrian 53219393Sadrianvoid 54219393Sadrianar9280olcGetTxGainIndex(struct ath_hal *ah, 55219393Sadrian const struct ieee80211_channel *chan, 56219393Sadrian struct calDataPerFreqOpLoop *rawDatasetOpLoop, 57219393Sadrian uint8_t *calChans, uint16_t availPiers, uint8_t *pwr, uint8_t *pcdacIdx) 58219393Sadrian{ 59219393Sadrian uint8_t pcdac, i = 0; 60219393Sadrian uint16_t idxL = 0, idxR = 0, numPiers; 61219393Sadrian HAL_BOOL match; 62219393Sadrian CHAN_CENTERS centers; 63219393Sadrian 64219393Sadrian ar5416GetChannelCenters(ah, chan, ¢ers); 65219393Sadrian 66219393Sadrian for (numPiers = 0; numPiers < availPiers; numPiers++) 67219393Sadrian if (calChans[numPiers] == AR5416_BCHAN_UNUSED) 68219393Sadrian break; 69219393Sadrian 70219393Sadrian match = getLowerUpperIndex((uint8_t)FREQ2FBIN(centers.synth_center, 71219393Sadrian IEEE80211_IS_CHAN_2GHZ(chan)), calChans, numPiers, 72219393Sadrian &idxL, &idxR); 73219393Sadrian if (match) { 74219393Sadrian pcdac = rawDatasetOpLoop[idxL].pcdac[0][0]; 75219393Sadrian *pwr = rawDatasetOpLoop[idxL].pwrPdg[0][0]; 76219393Sadrian } else { 77219393Sadrian pcdac = rawDatasetOpLoop[idxR].pcdac[0][0]; 78219393Sadrian *pwr = (rawDatasetOpLoop[idxL].pwrPdg[0][0] + 79219393Sadrian rawDatasetOpLoop[idxR].pwrPdg[0][0])/2; 80219393Sadrian } 81219393Sadrian while (pcdac > AH9280(ah)->originalGain[i] && 82219393Sadrian i < (AR9280_TX_GAIN_TABLE_SIZE - 1)) 83219393Sadrian i++; 84219393Sadrian 85219393Sadrian *pcdacIdx = i; 86219393Sadrian} 87219393Sadrian 88219393Sadrian/* 89219393Sadrian * XXX txPower here is likely not the target txPower in the traditional 90219393Sadrian * XXX sense, but is set by a call to ar9280olcGetTxGainIndex(). 91219393Sadrian * XXX Thus, be careful if you're trying to use this routine yourself. 92219393Sadrian */ 93219393Sadrianvoid 94219393Sadrianar9280olcGetPDADCs(struct ath_hal *ah, uint32_t initTxGain, int txPower, 95219393Sadrian uint8_t *pPDADCValues) 96219393Sadrian{ 97219393Sadrian uint32_t i; 98219393Sadrian uint32_t offset; 99219393Sadrian 100219393Sadrian OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_0, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3); 101219393Sadrian OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_1, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3); 102219393Sadrian 103219393Sadrian OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL7, AR_PHY_TX_PWRCTRL_INIT_TX_GAIN, initTxGain); 104219393Sadrian 105219393Sadrian offset = txPower; 106219393Sadrian for (i = 0; i < AR5416_NUM_PDADC_VALUES; i++) 107219393Sadrian if (i < offset) 108219393Sadrian pPDADCValues[i] = 0x0; 109219393Sadrian else 110219393Sadrian pPDADCValues[i] = 0xFF; 111219393Sadrian} 112219393Sadrian 113219393Sadrian/* 114219393Sadrian * Run temperature compensation calibration. 115219393Sadrian * 116219393Sadrian * The TX gain table is adjusted depending upon the difference 117219393Sadrian * between the initial PDADC value and the currently read 118219393Sadrian * average TX power sample value. This value is only valid if 119219393Sadrian * frames have been transmitted, so currPDADC will be 0 if 120219393Sadrian * no frames have yet been transmitted. 121219393Sadrian */ 122219393Sadrianvoid 123219393Sadrianar9280olcTemperatureCompensation(struct ath_hal *ah) 124219393Sadrian{ 125219393Sadrian uint32_t rddata, i; 126219393Sadrian int delta, currPDADC, regval; 127219393Sadrian uint8_t hpwr_5g = 0; 128219393Sadrian 129219393Sadrian rddata = OS_REG_READ(ah, AR_PHY_TX_PWRCTRL4); 130219393Sadrian currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT); 131219393Sadrian 132219393Sadrian HALDEBUG(ah, HAL_DEBUG_PERCAL, 133219393Sadrian "%s: called: initPDADC=%d, currPDADC=%d\n", 134219393Sadrian __func__, AH5416(ah)->initPDADC, currPDADC); 135219393Sadrian 136219393Sadrian if (AH5416(ah)->initPDADC == 0 || currPDADC == 0) 137219393Sadrian return; 138219393Sadrian 139219393Sadrian (void) (ath_hal_eepromGet(ah, AR_EEP_DAC_HPWR_5G, &hpwr_5g)); 140219393Sadrian 141219393Sadrian if (hpwr_5g) 142219393Sadrian delta = (currPDADC - AH5416(ah)->initPDADC + 4) / 8; 143219393Sadrian else 144219393Sadrian delta = (currPDADC - AH5416(ah)->initPDADC + 5) / 10; 145219393Sadrian 146219393Sadrian HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: delta=%d, PDADCdelta=%d\n", 147219393Sadrian __func__, delta, AH9280(ah)->PDADCdelta); 148219393Sadrian 149219393Sadrian if (delta != AH9280(ah)->PDADCdelta) { 150219393Sadrian AH9280(ah)->PDADCdelta = delta; 151219393Sadrian for (i = 1; i < AR9280_TX_GAIN_TABLE_SIZE; i++) { 152219393Sadrian regval = AH9280(ah)->originalGain[i] - delta; 153219393Sadrian if (regval < 0) 154219393Sadrian regval = 0; 155219393Sadrian 156219393Sadrian OS_REG_RMW_FIELD(ah, 157219393Sadrian AR_PHY_TX_GAIN_TBL1 + i * 4, 158219393Sadrian AR_PHY_TX_GAIN, regval); 159219393Sadrian } 160219393Sadrian } 161219393Sadrian} 162219393Sadrian 163219393Sadrian/* 164219393Sadrian * This effectively disables the gain boundaries leaving it 165219393Sadrian * to the open-loop TX power control. 166219393Sadrian */ 167219393Sadrianstatic void 168219393Sadrianar9280SetGainBoundariesOpenLoop(struct ath_hal *ah, int regChainOffset, 169219393Sadrian uint16_t pdGainOverlap_t2, uint16_t gainBoundaries[]) 170219393Sadrian{ 171219393Sadrian /* These are unused for OLC */ 172219393Sadrian (void) pdGainOverlap_t2; 173219393Sadrian (void) gainBoundaries; 174219393Sadrian 175219393Sadrian OS_REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset, 176219393Sadrian SM(0x6, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | 177219393Sadrian SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) | 178219393Sadrian SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) | 179219393Sadrian SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) | 180219393Sadrian SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4)); 181219393Sadrian} 182219393Sadrian 183219393Sadrian/* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */ 184219393Sadrian/* XXX shouldn't be here! */ 185219393Sadrian#define EEP_MINOR(_ah) \ 186219393Sadrian (AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK) 187219393Sadrian#define IS_EEP_MINOR_V2(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2) 188219393Sadrian#define IS_EEP_MINOR_V3(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_3) 189219393Sadrian 190219393Sadrian/************************************************************** 191219393Sadrian * ar9280SetPowerCalTable 192219393Sadrian * 193219393Sadrian * Pull the PDADC piers from cal data and interpolate them across the given 194219393Sadrian * points as well as from the nearest pier(s) to get a power detector 195219393Sadrian * linear voltage to power level table. 196219393Sadrian * 197219393Sadrian * Handle OLC for Merlin where required. 198219393Sadrian */ 199219393SadrianHAL_BOOL 200219393Sadrianar9280SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, 201219393Sadrian const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset) 202219393Sadrian{ 203219393Sadrian CAL_DATA_PER_FREQ *pRawDataset; 204219393Sadrian uint8_t *pCalBChans = AH_NULL; 205219393Sadrian uint16_t pdGainOverlap_t2; 206219393Sadrian static uint8_t pdadcValues[AR5416_NUM_PDADC_VALUES]; 207219393Sadrian uint16_t gainBoundaries[AR5416_PD_GAINS_IN_MASK]; 208219393Sadrian uint16_t numPiers, i; 209219393Sadrian int16_t tMinCalPower; 210219393Sadrian uint16_t numXpdGain, xpdMask; 211219393Sadrian uint16_t xpdGainValues[AR5416_NUM_PD_GAINS]; 212219393Sadrian uint32_t regChainOffset; 213219393Sadrian 214219393Sadrian OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues)); 215219393Sadrian 216219393Sadrian xpdMask = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].xpdGain; 217219393Sadrian 218219393Sadrian if (IS_EEP_MINOR_V2(ah)) { 219219393Sadrian pdGainOverlap_t2 = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pdGainOverlap; 220219393Sadrian } else { 221219393Sadrian pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP)); 222219393Sadrian } 223219393Sadrian 224219393Sadrian if (IEEE80211_IS_CHAN_2GHZ(chan)) { 225219393Sadrian pCalBChans = pEepData->calFreqPier2G; 226219393Sadrian numPiers = AR5416_NUM_2G_CAL_PIERS; 227219393Sadrian } else { 228219393Sadrian pCalBChans = pEepData->calFreqPier5G; 229219393Sadrian numPiers = AR5416_NUM_5G_CAL_PIERS; 230219393Sadrian } 231219393Sadrian 232219393Sadrian /* If OLC is being done, set the init PDADC value appropriately */ 233219393Sadrian if (IEEE80211_IS_CHAN_2GHZ(chan) && AR_SREV_MERLIN_20_OR_LATER(ah) && 234219393Sadrian ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) { 235219393Sadrian struct calDataPerFreq *pRawDataset = pEepData->calPierData2G[0]; 236219393Sadrian AH5416(ah)->initPDADC = ((struct calDataPerFreqOpLoop *) pRawDataset)->vpdPdg[0][0]; 237219393Sadrian } else { 238219393Sadrian /* 239219393Sadrian * XXX ath9k doesn't clear this for 5ghz mode if 240219393Sadrian * it were set in 2ghz mode before! 241219393Sadrian * The Merlin OLC temperature compensation code 242219393Sadrian * uses this to calculate the PDADC delta during 243219393Sadrian * calibration ; 0 here effectively stops the 244219393Sadrian * temperature compensation calibration from 245219393Sadrian * occuring. 246219393Sadrian */ 247219393Sadrian AH5416(ah)->initPDADC = 0; 248219393Sadrian } 249219393Sadrian 250219393Sadrian /* Calculate the value of xpdgains from the xpdGain Mask */ 251219393Sadrian numXpdGain = ar5416GetXpdGainValues(ah, xpdMask, xpdGainValues); 252219393Sadrian 253219393Sadrian /* Write the detector gain biases and their number */ 254219393Sadrian ar5416WriteDetectorGainBiases(ah, numXpdGain, xpdGainValues); 255219393Sadrian 256219393Sadrian for (i = 0; i < AR5416_MAX_CHAINS; i++) { 257219393Sadrian regChainOffset = ar5416GetRegChainOffset(ah, i); 258219393Sadrian if (pEepData->baseEepHeader.txMask & (1 << i)) { 259219393Sadrian if (IEEE80211_IS_CHAN_2GHZ(chan)) { 260219393Sadrian pRawDataset = pEepData->calPierData2G[i]; 261219393Sadrian } else { 262219393Sadrian pRawDataset = pEepData->calPierData5G[i]; 263219393Sadrian } 264219393Sadrian 265219393Sadrian /* Fetch the gain boundaries and the PDADC values */ 266219393Sadrian if (AR_SREV_MERLIN_20_OR_LATER(ah) && 267219393Sadrian ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) { 268219393Sadrian uint8_t pcdacIdx; 269219393Sadrian uint8_t txPower; 270219393Sadrian 271219393Sadrian ar9280olcGetTxGainIndex(ah, chan, 272219393Sadrian (struct calDataPerFreqOpLoop *) pRawDataset, 273219393Sadrian pCalBChans, numPiers, &txPower, &pcdacIdx); 274219393Sadrian ar9280olcGetPDADCs(ah, pcdacIdx, txPower / 2, pdadcValues); 275219393Sadrian } else { 276219393Sadrian ar5416GetGainBoundariesAndPdadcs(ah, chan, 277219393Sadrian pRawDataset, pCalBChans, numPiers, 278219393Sadrian pdGainOverlap_t2, &tMinCalPower, 279219393Sadrian gainBoundaries, pdadcValues, numXpdGain); 280219393Sadrian } 281219393Sadrian 282219393Sadrian /* 283219393Sadrian * Prior to writing the boundaries or the pdadc vs. power table 284219393Sadrian * into the chip registers the default starting point on the pdadc 285219393Sadrian * vs. power table needs to be checked and the curve boundaries 286219393Sadrian * adjusted accordingly 287219393Sadrian */ 288219393Sadrian // XXX ath9k_change_gain_boundary_setting(); 289219393Sadrian 290219393Sadrian if ((i == 0) || AR_SREV_OWL_20_OR_LATER(ah)) { 291219393Sadrian /* Set gain boundaries for either open- or closed-loop TPC */ 292219393Sadrian if (AR_SREV_MERLIN_20_OR_LATER(ah) && 293219393Sadrian ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) 294219393Sadrian ar9280SetGainBoundariesOpenLoop(ah, 295219393Sadrian regChainOffset, pdGainOverlap_t2, 296219393Sadrian gainBoundaries); 297219393Sadrian else 298219393Sadrian ar5416SetGainBoundariesClosedLoop(ah, 299219393Sadrian regChainOffset, pdGainOverlap_t2, 300219393Sadrian gainBoundaries); 301219393Sadrian } 302219393Sadrian 303219393Sadrian /* 304219393Sadrian * If this is a board that has a pwrTableOffset that differs from 305219393Sadrian * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the 306219393Sadrian * pdadc vs pwr table needs to be adjusted prior to writing to the 307219393Sadrian * chip. 308219393Sadrian */ 309219393Sadrian /* XXX ath9k_adjust_pdadc_values() */ 310219393Sadrian 311219393Sadrian /* Write the power values into the baseband power table */ 312219393Sadrian ar5416WritePdadcValues(ah, regChainOffset, pdadcValues); 313219393Sadrian } 314219393Sadrian } 315219393Sadrian *pTxPowerIndexOffset = 0; 316219393Sadrian 317219393Sadrian return AH_TRUE; 318219393Sadrian} 319