114779705SSam Leffler /* 259efa8b5SSam Leffler * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting 314779705SSam Leffler * Copyright (c) 2002-2008 Atheros Communications, Inc. 414779705SSam Leffler * 514779705SSam Leffler * Permission to use, copy, modify, and/or distribute this software for any 614779705SSam Leffler * purpose with or without fee is hereby granted, provided that the above 714779705SSam Leffler * copyright notice and this permission notice appear in all copies. 814779705SSam Leffler * 914779705SSam Leffler * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1014779705SSam Leffler * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1114779705SSam Leffler * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 1214779705SSam Leffler * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1314779705SSam Leffler * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 1414779705SSam Leffler * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 1514779705SSam Leffler * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1614779705SSam Leffler * 1759efa8b5SSam Leffler * $FreeBSD$ 1814779705SSam Leffler */ 1914779705SSam Leffler #include "opt_ah.h" 2014779705SSam Leffler 2114779705SSam Leffler #include "ah.h" 2214779705SSam Leffler #include "ah_internal.h" 2314779705SSam Leffler 2414779705SSam Leffler #include "ah_eeprom_v3.h" 2514779705SSam Leffler 2614779705SSam Leffler #include "ar5212/ar5212.h" 2714779705SSam Leffler #include "ar5212/ar5212reg.h" 2814779705SSam Leffler #include "ar5212/ar5212phy.h" 2914779705SSam Leffler 3014779705SSam Leffler #define AH_5212_5112 3114779705SSam Leffler #include "ar5212/ar5212.ini" 3214779705SSam Leffler 3314779705SSam Leffler #define N(a) (sizeof(a)/sizeof(a[0])) 3414779705SSam Leffler 3514779705SSam Leffler struct ar5112State { 3614779705SSam Leffler RF_HAL_FUNCS base; /* public state, must be first */ 3714779705SSam Leffler uint16_t pcdacTable[PWR_TABLE_SIZE]; 3814779705SSam Leffler 3914779705SSam Leffler uint32_t Bank1Data[N(ar5212Bank1_5112)]; 4014779705SSam Leffler uint32_t Bank2Data[N(ar5212Bank2_5112)]; 4114779705SSam Leffler uint32_t Bank3Data[N(ar5212Bank3_5112)]; 4214779705SSam Leffler uint32_t Bank6Data[N(ar5212Bank6_5112)]; 4314779705SSam Leffler uint32_t Bank7Data[N(ar5212Bank7_5112)]; 4414779705SSam Leffler }; 4514779705SSam Leffler #define AR5112(ah) ((struct ar5112State *) AH5212(ah)->ah_rfHal) 4614779705SSam Leffler 4714779705SSam Leffler static void ar5212GetLowerUpperIndex(uint16_t v, 4814779705SSam Leffler uint16_t *lp, uint16_t listSize, 4914779705SSam Leffler uint32_t *vlo, uint32_t *vhi); 5014779705SSam Leffler static HAL_BOOL getFullPwrTable(uint16_t numPcdacs, uint16_t *pcdacs, 5114779705SSam Leffler int16_t *power, int16_t maxPower, int16_t *retVals); 5214779705SSam Leffler static int16_t getPminAndPcdacTableFromPowerTable(int16_t *pwrTableT4, 5314779705SSam Leffler uint16_t retVals[]); 5414779705SSam Leffler static int16_t getPminAndPcdacTableFromTwoPowerTables(int16_t *pwrTableLXpdT4, 5514779705SSam Leffler int16_t *pwrTableHXpdT4, uint16_t retVals[], int16_t *pMid); 5614779705SSam Leffler static int16_t interpolate_signed(uint16_t target, 5714779705SSam Leffler uint16_t srcLeft, uint16_t srcRight, 5814779705SSam Leffler int16_t targetLeft, int16_t targetRight); 5914779705SSam Leffler 6014779705SSam Leffler extern void ar5212ModifyRfBuffer(uint32_t *rfBuf, uint32_t reg32, 6114779705SSam Leffler uint32_t numBits, uint32_t firstBit, uint32_t column); 6214779705SSam Leffler 6314779705SSam Leffler static void 6414779705SSam Leffler ar5112WriteRegs(struct ath_hal *ah, u_int modesIndex, u_int freqIndex, 6514779705SSam Leffler int writes) 6614779705SSam Leffler { 6714779705SSam Leffler HAL_INI_WRITE_ARRAY(ah, ar5212Modes_5112, modesIndex, writes); 6814779705SSam Leffler HAL_INI_WRITE_ARRAY(ah, ar5212Common_5112, 1, writes); 6914779705SSam Leffler HAL_INI_WRITE_ARRAY(ah, ar5212BB_RfGain_5112, freqIndex, writes); 7014779705SSam Leffler } 7114779705SSam Leffler 7214779705SSam Leffler /* 7314779705SSam Leffler * Take the MHz channel value and set the Channel value 7414779705SSam Leffler * 7514779705SSam Leffler * ASSUMES: Writes enabled to analog bus 7614779705SSam Leffler */ 7714779705SSam Leffler static HAL_BOOL 7859efa8b5SSam Leffler ar5112SetChannel(struct ath_hal *ah, const struct ieee80211_channel *chan) 7914779705SSam Leffler { 8059efa8b5SSam Leffler uint16_t freq = ath_hal_gethwchannel(ah, chan); 8114779705SSam Leffler uint32_t channelSel = 0; 8214779705SSam Leffler uint32_t bModeSynth = 0; 8314779705SSam Leffler uint32_t aModeRefSel = 0; 8414779705SSam Leffler uint32_t reg32 = 0; 8514779705SSam Leffler 8659efa8b5SSam Leffler OS_MARK(ah, AH_MARK_SETCHANNEL, freq); 8714779705SSam Leffler 8859efa8b5SSam Leffler if (freq < 4800) { 8914779705SSam Leffler uint32_t txctl; 9014779705SSam Leffler 9159efa8b5SSam Leffler if (((freq - 2192) % 5) == 0) { 9259efa8b5SSam Leffler channelSel = ((freq - 672) * 2 - 3040)/10; 9314779705SSam Leffler bModeSynth = 0; 9459efa8b5SSam Leffler } else if (((freq - 2224) % 5) == 0) { 9559efa8b5SSam Leffler channelSel = ((freq - 704) * 2 - 3040) / 10; 9614779705SSam Leffler bModeSynth = 1; 9714779705SSam Leffler } else { 9814779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, 9914779705SSam Leffler "%s: invalid channel %u MHz\n", 10059efa8b5SSam Leffler __func__, freq); 10114779705SSam Leffler return AH_FALSE; 10214779705SSam Leffler } 10314779705SSam Leffler 10414779705SSam Leffler channelSel = (channelSel << 2) & 0xff; 10514779705SSam Leffler channelSel = ath_hal_reverseBits(channelSel, 8); 10614779705SSam Leffler 10714779705SSam Leffler txctl = OS_REG_READ(ah, AR_PHY_CCK_TX_CTRL); 10859efa8b5SSam Leffler if (freq == 2484) { 10914779705SSam Leffler /* Enable channel spreading for channel 14 */ 11014779705SSam Leffler OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, 11114779705SSam Leffler txctl | AR_PHY_CCK_TX_CTRL_JAPAN); 11214779705SSam Leffler } else { 11314779705SSam Leffler OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, 11414779705SSam Leffler txctl &~ AR_PHY_CCK_TX_CTRL_JAPAN); 11514779705SSam Leffler } 11659efa8b5SSam Leffler } else if (((freq % 5) == 2) && (freq <= 5435)) { 11759efa8b5SSam Leffler freq = freq - 2; /* Align to even 5MHz raster */ 11814779705SSam Leffler channelSel = ath_hal_reverseBits( 11914779705SSam Leffler (uint32_t)(((freq - 4800)*10)/25 + 1), 8); 12014779705SSam Leffler aModeRefSel = ath_hal_reverseBits(0, 2); 12159efa8b5SSam Leffler } else if ((freq % 20) == 0 && freq >= 5120) { 12214779705SSam Leffler channelSel = ath_hal_reverseBits( 12359efa8b5SSam Leffler ((freq - 4800) / 20 << 2), 8); 12414779705SSam Leffler aModeRefSel = ath_hal_reverseBits(3, 2); 12559efa8b5SSam Leffler } else if ((freq % 10) == 0) { 12614779705SSam Leffler channelSel = ath_hal_reverseBits( 12759efa8b5SSam Leffler ((freq - 4800) / 10 << 1), 8); 12814779705SSam Leffler aModeRefSel = ath_hal_reverseBits(2, 2); 12959efa8b5SSam Leffler } else if ((freq % 5) == 0) { 13014779705SSam Leffler channelSel = ath_hal_reverseBits( 13159efa8b5SSam Leffler (freq - 4800) / 5, 8); 13214779705SSam Leffler aModeRefSel = ath_hal_reverseBits(1, 2); 13314779705SSam Leffler } else { 13414779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u MHz\n", 13559efa8b5SSam Leffler __func__, freq); 13614779705SSam Leffler return AH_FALSE; 13714779705SSam Leffler } 13814779705SSam Leffler 13914779705SSam Leffler reg32 = (channelSel << 4) | (aModeRefSel << 2) | (bModeSynth << 1) | 14014779705SSam Leffler (1 << 12) | 0x1; 14114779705SSam Leffler OS_REG_WRITE(ah, AR_PHY(0x27), reg32 & 0xff); 14214779705SSam Leffler 14314779705SSam Leffler reg32 >>= 8; 14414779705SSam Leffler OS_REG_WRITE(ah, AR_PHY(0x36), reg32 & 0x7f); 14514779705SSam Leffler 14614779705SSam Leffler AH_PRIVATE(ah)->ah_curchan = chan; 14714779705SSam Leffler return AH_TRUE; 14814779705SSam Leffler } 14914779705SSam Leffler 15014779705SSam Leffler /* 15114779705SSam Leffler * Return a reference to the requested RF Bank. 15214779705SSam Leffler */ 15314779705SSam Leffler static uint32_t * 15414779705SSam Leffler ar5112GetRfBank(struct ath_hal *ah, int bank) 15514779705SSam Leffler { 15614779705SSam Leffler struct ar5112State *priv = AR5112(ah); 15714779705SSam Leffler 15814779705SSam Leffler HALASSERT(priv != AH_NULL); 15914779705SSam Leffler switch (bank) { 16014779705SSam Leffler case 1: return priv->Bank1Data; 16114779705SSam Leffler case 2: return priv->Bank2Data; 16214779705SSam Leffler case 3: return priv->Bank3Data; 16314779705SSam Leffler case 6: return priv->Bank6Data; 16414779705SSam Leffler case 7: return priv->Bank7Data; 16514779705SSam Leffler } 16614779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown RF Bank %d requested\n", 16714779705SSam Leffler __func__, bank); 16814779705SSam Leffler return AH_NULL; 16914779705SSam Leffler } 17014779705SSam Leffler 17114779705SSam Leffler /* 17214779705SSam Leffler * Reads EEPROM header info from device structure and programs 17314779705SSam Leffler * all rf registers 17414779705SSam Leffler * 17514779705SSam Leffler * REQUIRES: Access to the analog rf device 17614779705SSam Leffler */ 17714779705SSam Leffler static HAL_BOOL 17859efa8b5SSam Leffler ar5112SetRfRegs(struct ath_hal *ah, 17959efa8b5SSam Leffler const struct ieee80211_channel *chan, 18014779705SSam Leffler uint16_t modesIndex, uint16_t *rfXpdGain) 18114779705SSam Leffler { 18214779705SSam Leffler #define RF_BANK_SETUP(_priv, _ix, _col) do { \ 18314779705SSam Leffler int i; \ 18414779705SSam Leffler for (i = 0; i < N(ar5212Bank##_ix##_5112); i++) \ 18514779705SSam Leffler (_priv)->Bank##_ix##Data[i] = ar5212Bank##_ix##_5112[i][_col];\ 18614779705SSam Leffler } while (0) 18759efa8b5SSam Leffler uint16_t freq = ath_hal_gethwchannel(ah, chan); 18814779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah); 18914779705SSam Leffler const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; 19014779705SSam Leffler uint16_t rfXpdSel, gainI; 19114779705SSam Leffler uint16_t ob5GHz = 0, db5GHz = 0; 19214779705SSam Leffler uint16_t ob2GHz = 0, db2GHz = 0; 19314779705SSam Leffler struct ar5112State *priv = AR5112(ah); 19414779705SSam Leffler GAIN_VALUES *gv = &ahp->ah_gainValues; 19514779705SSam Leffler int regWrites = 0; 19614779705SSam Leffler 19714779705SSam Leffler HALASSERT(priv); 19814779705SSam Leffler 19959efa8b5SSam Leffler HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: chan %u/0x%x modesIndex %u\n", 20059efa8b5SSam Leffler __func__, chan->ic_freq, chan->ic_flags, modesIndex); 20159efa8b5SSam Leffler 20214779705SSam Leffler /* Setup rf parameters */ 20359efa8b5SSam Leffler switch (chan->ic_flags & IEEE80211_CHAN_ALLFULL) { 20459efa8b5SSam Leffler case IEEE80211_CHAN_A: 20559efa8b5SSam Leffler if (freq > 4000 && freq < 5260) { 20614779705SSam Leffler ob5GHz = ee->ee_ob1; 20714779705SSam Leffler db5GHz = ee->ee_db1; 20859efa8b5SSam Leffler } else if (freq >= 5260 && freq < 5500) { 20914779705SSam Leffler ob5GHz = ee->ee_ob2; 21014779705SSam Leffler db5GHz = ee->ee_db2; 21159efa8b5SSam Leffler } else if (freq >= 5500 && freq < 5725) { 21214779705SSam Leffler ob5GHz = ee->ee_ob3; 21314779705SSam Leffler db5GHz = ee->ee_db3; 21459efa8b5SSam Leffler } else if (freq >= 5725) { 21514779705SSam Leffler ob5GHz = ee->ee_ob4; 21614779705SSam Leffler db5GHz = ee->ee_db4; 21714779705SSam Leffler } else { 21814779705SSam Leffler /* XXX else */ 21914779705SSam Leffler } 22014779705SSam Leffler rfXpdSel = ee->ee_xpd[headerInfo11A]; 22114779705SSam Leffler gainI = ee->ee_gainI[headerInfo11A]; 22214779705SSam Leffler break; 22359efa8b5SSam Leffler case IEEE80211_CHAN_B: 22414779705SSam Leffler ob2GHz = ee->ee_ob2GHz[0]; 22514779705SSam Leffler db2GHz = ee->ee_db2GHz[0]; 22614779705SSam Leffler rfXpdSel = ee->ee_xpd[headerInfo11B]; 22714779705SSam Leffler gainI = ee->ee_gainI[headerInfo11B]; 22814779705SSam Leffler break; 22959efa8b5SSam Leffler case IEEE80211_CHAN_G: 23059efa8b5SSam Leffler case IEEE80211_CHAN_PUREG: /* NB: really 108G */ 23114779705SSam Leffler ob2GHz = ee->ee_ob2GHz[1]; 23214779705SSam Leffler db2GHz = ee->ee_ob2GHz[1]; 23314779705SSam Leffler rfXpdSel = ee->ee_xpd[headerInfo11G]; 23414779705SSam Leffler gainI = ee->ee_gainI[headerInfo11G]; 23514779705SSam Leffler break; 23614779705SSam Leffler default: 23714779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n", 23859efa8b5SSam Leffler __func__, chan->ic_flags); 23914779705SSam Leffler return AH_FALSE; 24014779705SSam Leffler } 24114779705SSam Leffler 24214779705SSam Leffler /* Setup Bank 1 Write */ 24314779705SSam Leffler RF_BANK_SETUP(priv, 1, 1); 24414779705SSam Leffler 24514779705SSam Leffler /* Setup Bank 2 Write */ 24614779705SSam Leffler RF_BANK_SETUP(priv, 2, modesIndex); 24714779705SSam Leffler 24814779705SSam Leffler /* Setup Bank 3 Write */ 24914779705SSam Leffler RF_BANK_SETUP(priv, 3, modesIndex); 25014779705SSam Leffler 25114779705SSam Leffler /* Setup Bank 6 Write */ 25214779705SSam Leffler RF_BANK_SETUP(priv, 6, modesIndex); 25314779705SSam Leffler 25414779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank6Data, rfXpdSel, 1, 302, 0); 25514779705SSam Leffler 25614779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank6Data, rfXpdGain[0], 2, 270, 0); 25714779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank6Data, rfXpdGain[1], 2, 257, 0); 25814779705SSam Leffler 25959efa8b5SSam Leffler if (IEEE80211_IS_CHAN_OFDM(chan)) { 26014779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank6Data, 26114779705SSam Leffler gv->currStep->paramVal[GP_PWD_138], 1, 168, 3); 26214779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank6Data, 26314779705SSam Leffler gv->currStep->paramVal[GP_PWD_137], 1, 169, 3); 26414779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank6Data, 26514779705SSam Leffler gv->currStep->paramVal[GP_PWD_136], 1, 170, 3); 26614779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank6Data, 26714779705SSam Leffler gv->currStep->paramVal[GP_PWD_132], 1, 174, 3); 26814779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank6Data, 26914779705SSam Leffler gv->currStep->paramVal[GP_PWD_131], 1, 175, 3); 27014779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank6Data, 27114779705SSam Leffler gv->currStep->paramVal[GP_PWD_130], 1, 176, 3); 27214779705SSam Leffler } 27314779705SSam Leffler 27414779705SSam Leffler /* Only the 5 or 2 GHz OB/DB need to be set for a mode */ 27559efa8b5SSam Leffler if (IEEE80211_IS_CHAN_2GHZ(chan)) { 27614779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank6Data, ob2GHz, 3, 287, 0); 27714779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank6Data, db2GHz, 3, 290, 0); 27814779705SSam Leffler } else { 27914779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank6Data, ob5GHz, 3, 279, 0); 28014779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank6Data, db5GHz, 3, 282, 0); 28114779705SSam Leffler } 28214779705SSam Leffler 28314779705SSam Leffler /* Lower synth voltage for X112 Rev 2.0 only */ 28414779705SSam Leffler if (IS_RADX112_REV2(ah)) { 28514779705SSam Leffler /* Non-Reversed analyg registers - so values are pre-reversed */ 28614779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank6Data, 2, 2, 90, 2); 28714779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank6Data, 2, 2, 92, 2); 28814779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank6Data, 2, 2, 94, 2); 28914779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank6Data, 2, 1, 254, 2); 29014779705SSam Leffler } 29114779705SSam Leffler 29214779705SSam Leffler /* Decrease Power Consumption for 5312/5213 and up */ 29314779705SSam Leffler if (AH_PRIVATE(ah)->ah_phyRev >= AR_PHY_CHIP_ID_REV_2) { 29414779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank6Data, 1, 1, 281, 1); 29514779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank6Data, 1, 2, 1, 3); 29614779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank6Data, 1, 2, 3, 3); 29714779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank6Data, 1, 1, 139, 3); 29814779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank6Data, 1, 1, 140, 3); 29914779705SSam Leffler } 30014779705SSam Leffler 30114779705SSam Leffler /* Setup Bank 7 Setup */ 30214779705SSam Leffler RF_BANK_SETUP(priv, 7, modesIndex); 30359efa8b5SSam Leffler if (IEEE80211_IS_CHAN_OFDM(chan)) 30414779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank7Data, 30514779705SSam Leffler gv->currStep->paramVal[GP_MIXGAIN_OVR], 2, 37, 0); 30614779705SSam Leffler 30714779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank7Data, gainI, 6, 14, 0); 30814779705SSam Leffler 30914779705SSam Leffler /* Adjust params for Derby TX power control */ 31059efa8b5SSam Leffler if (IEEE80211_IS_CHAN_HALF(chan) || IEEE80211_IS_CHAN_QUARTER(chan)) { 31114779705SSam Leffler uint32_t rfDelay, rfPeriod; 31214779705SSam Leffler 31314779705SSam Leffler rfDelay = 0xf; 31459efa8b5SSam Leffler rfPeriod = (IEEE80211_IS_CHAN_HALF(chan)) ? 0x8 : 0xf; 31514779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank7Data, rfDelay, 4, 58, 0); 31614779705SSam Leffler ar5212ModifyRfBuffer(priv->Bank7Data, rfPeriod, 4, 70, 0); 31714779705SSam Leffler } 31814779705SSam Leffler 31914779705SSam Leffler #ifdef notyet 32014779705SSam Leffler /* Analog registers are setup - EAR can modify */ 32114779705SSam Leffler if (ar5212IsEarEngaged(pDev, chan)) 32214779705SSam Leffler uint32_t modifier; 32314779705SSam Leffler ar5212EarModify(pDev, EAR_LC_RF_WRITE, chan, &modifier); 32414779705SSam Leffler #endif 32514779705SSam Leffler /* Write Analog registers */ 32614779705SSam Leffler HAL_INI_WRITE_BANK(ah, ar5212Bank1_5112, priv->Bank1Data, regWrites); 32714779705SSam Leffler HAL_INI_WRITE_BANK(ah, ar5212Bank2_5112, priv->Bank2Data, regWrites); 32814779705SSam Leffler HAL_INI_WRITE_BANK(ah, ar5212Bank3_5112, priv->Bank3Data, regWrites); 32914779705SSam Leffler HAL_INI_WRITE_BANK(ah, ar5212Bank6_5112, priv->Bank6Data, regWrites); 33014779705SSam Leffler HAL_INI_WRITE_BANK(ah, ar5212Bank7_5112, priv->Bank7Data, regWrites); 33114779705SSam Leffler 33214779705SSam Leffler /* Now that we have reprogrammed rfgain value, clear the flag. */ 33314779705SSam Leffler ahp->ah_rfgainState = HAL_RFGAIN_INACTIVE; 33414779705SSam Leffler return AH_TRUE; 33514779705SSam Leffler #undef RF_BANK_SETUP 33614779705SSam Leffler } 33714779705SSam Leffler 33814779705SSam Leffler /* 33914779705SSam Leffler * Read the transmit power levels from the structures taken from EEPROM 34014779705SSam Leffler * Interpolate read transmit power values for this channel 34114779705SSam Leffler * Organize the transmit power values into a table for writing into the hardware 34214779705SSam Leffler */ 34314779705SSam Leffler static HAL_BOOL 34414779705SSam Leffler ar5112SetPowerTable(struct ath_hal *ah, 34559efa8b5SSam Leffler int16_t *pPowerMin, int16_t *pPowerMax, 34659efa8b5SSam Leffler const struct ieee80211_channel *chan, 34714779705SSam Leffler uint16_t *rfXpdGain) 34814779705SSam Leffler { 34959efa8b5SSam Leffler uint16_t freq = ath_hal_gethwchannel(ah, chan); 35014779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah); 35114779705SSam Leffler const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; 35214779705SSam Leffler uint32_t numXpdGain = IS_RADX112_REV2(ah) ? 2 : 1; 35314779705SSam Leffler uint32_t xpdGainMask = 0; 35414779705SSam Leffler int16_t powerMid, *pPowerMid = &powerMid; 35514779705SSam Leffler 35614779705SSam Leffler const EXPN_DATA_PER_CHANNEL_5112 *pRawCh; 35714779705SSam Leffler const EEPROM_POWER_EXPN_5112 *pPowerExpn = AH_NULL; 35814779705SSam Leffler 35914779705SSam Leffler uint32_t ii, jj, kk; 36014779705SSam Leffler int16_t minPwr_t4, maxPwr_t4, Pmin, Pmid; 36114779705SSam Leffler 36214779705SSam Leffler uint32_t chan_idx_L = 0, chan_idx_R = 0; 36314779705SSam Leffler uint16_t chan_L, chan_R; 36414779705SSam Leffler 36514779705SSam Leffler int16_t pwr_table0[64]; 36614779705SSam Leffler int16_t pwr_table1[64]; 36714779705SSam Leffler uint16_t pcdacs[10]; 36814779705SSam Leffler int16_t powers[10]; 36914779705SSam Leffler uint16_t numPcd; 37014779705SSam Leffler int16_t powTableLXPD[2][64]; 37114779705SSam Leffler int16_t powTableHXPD[2][64]; 37214779705SSam Leffler int16_t tmpPowerTable[64]; 37314779705SSam Leffler uint16_t xgainList[2]; 37414779705SSam Leffler uint16_t xpdMask; 37514779705SSam Leffler 37659efa8b5SSam Leffler switch (chan->ic_flags & IEEE80211_CHAN_ALLTURBOFULL) { 37759efa8b5SSam Leffler case IEEE80211_CHAN_A: 37859efa8b5SSam Leffler case IEEE80211_CHAN_ST: 37914779705SSam Leffler pPowerExpn = &ee->ee_modePowerArray5112[headerInfo11A]; 38014779705SSam Leffler xpdGainMask = ee->ee_xgain[headerInfo11A]; 38114779705SSam Leffler break; 38259efa8b5SSam Leffler case IEEE80211_CHAN_B: 38314779705SSam Leffler pPowerExpn = &ee->ee_modePowerArray5112[headerInfo11B]; 38414779705SSam Leffler xpdGainMask = ee->ee_xgain[headerInfo11B]; 38514779705SSam Leffler break; 38659efa8b5SSam Leffler case IEEE80211_CHAN_G: 38759efa8b5SSam Leffler case IEEE80211_CHAN_108G: 38814779705SSam Leffler pPowerExpn = &ee->ee_modePowerArray5112[headerInfo11G]; 38914779705SSam Leffler xpdGainMask = ee->ee_xgain[headerInfo11G]; 39014779705SSam Leffler break; 39114779705SSam Leffler default: 39214779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown channel flags 0x%x\n", 39359efa8b5SSam Leffler __func__, chan->ic_flags); 39414779705SSam Leffler return AH_FALSE; 39514779705SSam Leffler } 39614779705SSam Leffler 39714779705SSam Leffler if ((xpdGainMask & pPowerExpn->xpdMask) < 1) { 39814779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, 39914779705SSam Leffler "%s: desired xpdGainMask 0x%x not supported by " 40014779705SSam Leffler "calibrated xpdMask 0x%x\n", __func__, 40114779705SSam Leffler xpdGainMask, pPowerExpn->xpdMask); 40214779705SSam Leffler return AH_FALSE; 40314779705SSam Leffler } 40414779705SSam Leffler 40514779705SSam Leffler maxPwr_t4 = (int16_t)(2*(*pPowerMax)); /* pwr_t2 -> pwr_t4 */ 40614779705SSam Leffler minPwr_t4 = (int16_t)(2*(*pPowerMin)); /* pwr_t2 -> pwr_t4 */ 40714779705SSam Leffler 40814779705SSam Leffler xgainList[0] = 0xDEAD; 40914779705SSam Leffler xgainList[1] = 0xDEAD; 41014779705SSam Leffler 41114779705SSam Leffler kk = 0; 41214779705SSam Leffler xpdMask = pPowerExpn->xpdMask; 41314779705SSam Leffler for (jj = 0; jj < NUM_XPD_PER_CHANNEL; jj++) { 41414779705SSam Leffler if (((xpdMask >> jj) & 1) > 0) { 41514779705SSam Leffler if (kk > 1) { 41614779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, 41714779705SSam Leffler "A maximum of 2 xpdGains supported" 41814779705SSam Leffler "in pExpnPower data\n"); 41914779705SSam Leffler return AH_FALSE; 42014779705SSam Leffler } 42114779705SSam Leffler xgainList[kk++] = (uint16_t)jj; 42214779705SSam Leffler } 42314779705SSam Leffler } 42414779705SSam Leffler 42559efa8b5SSam Leffler ar5212GetLowerUpperIndex(freq, &pPowerExpn->pChannels[0], 42614779705SSam Leffler pPowerExpn->numChannels, &chan_idx_L, &chan_idx_R); 42714779705SSam Leffler 42814779705SSam Leffler kk = 0; 42914779705SSam Leffler for (ii = chan_idx_L; ii <= chan_idx_R; ii++) { 43014779705SSam Leffler pRawCh = &(pPowerExpn->pDataPerChannel[ii]); 43114779705SSam Leffler if (xgainList[1] == 0xDEAD) { 43214779705SSam Leffler jj = xgainList[0]; 43314779705SSam Leffler numPcd = pRawCh->pDataPerXPD[jj].numPcdacs; 43414779705SSam Leffler OS_MEMCPY(&pcdacs[0], &pRawCh->pDataPerXPD[jj].pcdac[0], 43514779705SSam Leffler numPcd * sizeof(uint16_t)); 43614779705SSam Leffler OS_MEMCPY(&powers[0], &pRawCh->pDataPerXPD[jj].pwr_t4[0], 43714779705SSam Leffler numPcd * sizeof(int16_t)); 43814779705SSam Leffler if (!getFullPwrTable(numPcd, &pcdacs[0], &powers[0], 43914779705SSam Leffler pRawCh->maxPower_t4, &tmpPowerTable[0])) { 44014779705SSam Leffler return AH_FALSE; 44114779705SSam Leffler } 44214779705SSam Leffler OS_MEMCPY(&powTableLXPD[kk][0], &tmpPowerTable[0], 44314779705SSam Leffler 64*sizeof(int16_t)); 44414779705SSam Leffler } else { 44514779705SSam Leffler jj = xgainList[0]; 44614779705SSam Leffler numPcd = pRawCh->pDataPerXPD[jj].numPcdacs; 44714779705SSam Leffler OS_MEMCPY(&pcdacs[0], &pRawCh->pDataPerXPD[jj].pcdac[0], 44814779705SSam Leffler numPcd*sizeof(uint16_t)); 44914779705SSam Leffler OS_MEMCPY(&powers[0], 45014779705SSam Leffler &pRawCh->pDataPerXPD[jj].pwr_t4[0], 45114779705SSam Leffler numPcd*sizeof(int16_t)); 45214779705SSam Leffler if (!getFullPwrTable(numPcd, &pcdacs[0], &powers[0], 45314779705SSam Leffler pRawCh->maxPower_t4, &tmpPowerTable[0])) { 45414779705SSam Leffler return AH_FALSE; 45514779705SSam Leffler } 45614779705SSam Leffler OS_MEMCPY(&powTableLXPD[kk][0], &tmpPowerTable[0], 45714779705SSam Leffler 64 * sizeof(int16_t)); 45814779705SSam Leffler 45914779705SSam Leffler jj = xgainList[1]; 46014779705SSam Leffler numPcd = pRawCh->pDataPerXPD[jj].numPcdacs; 46114779705SSam Leffler OS_MEMCPY(&pcdacs[0], &pRawCh->pDataPerXPD[jj].pcdac[0], 46214779705SSam Leffler numPcd * sizeof(uint16_t)); 46314779705SSam Leffler OS_MEMCPY(&powers[0], 46414779705SSam Leffler &pRawCh->pDataPerXPD[jj].pwr_t4[0], 46514779705SSam Leffler numPcd * sizeof(int16_t)); 46614779705SSam Leffler if (!getFullPwrTable(numPcd, &pcdacs[0], &powers[0], 46714779705SSam Leffler pRawCh->maxPower_t4, &tmpPowerTable[0])) { 46814779705SSam Leffler return AH_FALSE; 46914779705SSam Leffler } 47014779705SSam Leffler OS_MEMCPY(&powTableHXPD[kk][0], &tmpPowerTable[0], 47114779705SSam Leffler 64 * sizeof(int16_t)); 47214779705SSam Leffler } 47314779705SSam Leffler kk++; 47414779705SSam Leffler } 47514779705SSam Leffler 47614779705SSam Leffler chan_L = pPowerExpn->pChannels[chan_idx_L]; 47714779705SSam Leffler chan_R = pPowerExpn->pChannels[chan_idx_R]; 47814779705SSam Leffler kk = chan_idx_R - chan_idx_L; 47914779705SSam Leffler 48014779705SSam Leffler if (xgainList[1] == 0xDEAD) { 48114779705SSam Leffler for (jj = 0; jj < 64; jj++) { 48214779705SSam Leffler pwr_table0[jj] = interpolate_signed( 48359efa8b5SSam Leffler freq, chan_L, chan_R, 48414779705SSam Leffler powTableLXPD[0][jj], powTableLXPD[kk][jj]); 48514779705SSam Leffler } 48614779705SSam Leffler Pmin = getPminAndPcdacTableFromPowerTable(&pwr_table0[0], 48714779705SSam Leffler ahp->ah_pcdacTable); 48814779705SSam Leffler *pPowerMin = (int16_t) (Pmin / 2); 48914779705SSam Leffler *pPowerMid = (int16_t) (pwr_table0[63] / 2); 49014779705SSam Leffler *pPowerMax = (int16_t) (pwr_table0[63] / 2); 49114779705SSam Leffler rfXpdGain[0] = xgainList[0]; 49214779705SSam Leffler rfXpdGain[1] = rfXpdGain[0]; 49314779705SSam Leffler } else { 49414779705SSam Leffler for (jj = 0; jj < 64; jj++) { 49514779705SSam Leffler pwr_table0[jj] = interpolate_signed( 49659efa8b5SSam Leffler freq, chan_L, chan_R, 49714779705SSam Leffler powTableLXPD[0][jj], powTableLXPD[kk][jj]); 49814779705SSam Leffler pwr_table1[jj] = interpolate_signed( 49959efa8b5SSam Leffler freq, chan_L, chan_R, 50014779705SSam Leffler powTableHXPD[0][jj], powTableHXPD[kk][jj]); 50114779705SSam Leffler } 50214779705SSam Leffler if (numXpdGain == 2) { 50314779705SSam Leffler Pmin = getPminAndPcdacTableFromTwoPowerTables( 50414779705SSam Leffler &pwr_table0[0], &pwr_table1[0], 50514779705SSam Leffler ahp->ah_pcdacTable, &Pmid); 50614779705SSam Leffler *pPowerMin = (int16_t) (Pmin / 2); 50714779705SSam Leffler *pPowerMid = (int16_t) (Pmid / 2); 50814779705SSam Leffler *pPowerMax = (int16_t) (pwr_table0[63] / 2); 50914779705SSam Leffler rfXpdGain[0] = xgainList[0]; 51014779705SSam Leffler rfXpdGain[1] = xgainList[1]; 51114779705SSam Leffler } else if (minPwr_t4 <= pwr_table1[63] && 51214779705SSam Leffler maxPwr_t4 <= pwr_table1[63]) { 51314779705SSam Leffler Pmin = getPminAndPcdacTableFromPowerTable( 51414779705SSam Leffler &pwr_table1[0], ahp->ah_pcdacTable); 51514779705SSam Leffler rfXpdGain[0] = xgainList[1]; 51614779705SSam Leffler rfXpdGain[1] = rfXpdGain[0]; 51714779705SSam Leffler *pPowerMin = (int16_t) (Pmin / 2); 51814779705SSam Leffler *pPowerMid = (int16_t) (pwr_table1[63] / 2); 51914779705SSam Leffler *pPowerMax = (int16_t) (pwr_table1[63] / 2); 52014779705SSam Leffler } else { 52114779705SSam Leffler Pmin = getPminAndPcdacTableFromPowerTable( 52214779705SSam Leffler &pwr_table0[0], ahp->ah_pcdacTable); 52314779705SSam Leffler rfXpdGain[0] = xgainList[0]; 52414779705SSam Leffler rfXpdGain[1] = rfXpdGain[0]; 52514779705SSam Leffler *pPowerMin = (int16_t) (Pmin/2); 52614779705SSam Leffler *pPowerMid = (int16_t) (pwr_table0[63] / 2); 52714779705SSam Leffler *pPowerMax = (int16_t) (pwr_table0[63] / 2); 52814779705SSam Leffler } 52914779705SSam Leffler } 53014779705SSam Leffler 53114779705SSam Leffler /* 53214779705SSam Leffler * Move 5112 rates to match power tables where the max 53314779705SSam Leffler * power table entry corresponds with maxPower. 53414779705SSam Leffler */ 53514779705SSam Leffler HALASSERT(*pPowerMax <= PCDAC_STOP); 53614779705SSam Leffler ahp->ah_txPowerIndexOffset = PCDAC_STOP - *pPowerMax; 53714779705SSam Leffler 53814779705SSam Leffler return AH_TRUE; 53914779705SSam Leffler } 54014779705SSam Leffler 54114779705SSam Leffler /* 54214779705SSam Leffler * Returns interpolated or the scaled up interpolated value 54314779705SSam Leffler */ 54414779705SSam Leffler static int16_t 54514779705SSam Leffler interpolate_signed(uint16_t target, uint16_t srcLeft, uint16_t srcRight, 54614779705SSam Leffler int16_t targetLeft, int16_t targetRight) 54714779705SSam Leffler { 54814779705SSam Leffler int16_t rv; 54914779705SSam Leffler 55014779705SSam Leffler if (srcRight != srcLeft) { 55114779705SSam Leffler rv = ((target - srcLeft)*targetRight + 55214779705SSam Leffler (srcRight - target)*targetLeft) / (srcRight - srcLeft); 55314779705SSam Leffler } else { 55414779705SSam Leffler rv = targetLeft; 55514779705SSam Leffler } 55614779705SSam Leffler return rv; 55714779705SSam Leffler } 55814779705SSam Leffler 55914779705SSam Leffler /* 56014779705SSam Leffler * Return indices surrounding the value in sorted integer lists. 56114779705SSam Leffler * 56214779705SSam Leffler * NB: the input list is assumed to be sorted in ascending order 56314779705SSam Leffler */ 56414779705SSam Leffler static void 56514779705SSam Leffler ar5212GetLowerUpperIndex(uint16_t v, uint16_t *lp, uint16_t listSize, 56614779705SSam Leffler uint32_t *vlo, uint32_t *vhi) 56714779705SSam Leffler { 56814779705SSam Leffler uint32_t target = v; 56914779705SSam Leffler uint16_t *ep = lp+listSize; 57014779705SSam Leffler uint16_t *tp; 57114779705SSam Leffler 57214779705SSam Leffler /* 57314779705SSam Leffler * Check first and last elements for out-of-bounds conditions. 57414779705SSam Leffler */ 57514779705SSam Leffler if (target < lp[0]) { 57614779705SSam Leffler *vlo = *vhi = 0; 57714779705SSam Leffler return; 57814779705SSam Leffler } 57914779705SSam Leffler if (target >= ep[-1]) { 58014779705SSam Leffler *vlo = *vhi = listSize - 1; 58114779705SSam Leffler return; 58214779705SSam Leffler } 58314779705SSam Leffler 58414779705SSam Leffler /* look for value being near or between 2 values in list */ 58514779705SSam Leffler for (tp = lp; tp < ep; tp++) { 58614779705SSam Leffler /* 58714779705SSam Leffler * If value is close to the current value of the list 58814779705SSam Leffler * then target is not between values, it is one of the values 58914779705SSam Leffler */ 59014779705SSam Leffler if (*tp == target) { 59114779705SSam Leffler *vlo = *vhi = tp - lp; 59214779705SSam Leffler return; 59314779705SSam Leffler } 59414779705SSam Leffler /* 59514779705SSam Leffler * Look for value being between current value and next value 59614779705SSam Leffler * if so return these 2 values 59714779705SSam Leffler */ 59814779705SSam Leffler if (target < tp[1]) { 59914779705SSam Leffler *vlo = tp - lp; 60014779705SSam Leffler *vhi = *vlo + 1; 60114779705SSam Leffler return; 60214779705SSam Leffler } 60314779705SSam Leffler } 60414779705SSam Leffler } 60514779705SSam Leffler 60614779705SSam Leffler static HAL_BOOL 60714779705SSam Leffler getFullPwrTable(uint16_t numPcdacs, uint16_t *pcdacs, int16_t *power, int16_t maxPower, int16_t *retVals) 60814779705SSam Leffler { 60914779705SSam Leffler uint16_t ii; 61014779705SSam Leffler uint16_t idxL = 0; 61114779705SSam Leffler uint16_t idxR = 1; 61214779705SSam Leffler 61314779705SSam Leffler if (numPcdacs < 2) { 614*0e56140aSAdrian Chadd HALDEBUG(AH_NULL, HAL_DEBUG_ANY, 61514779705SSam Leffler "%s: at least 2 pcdac values needed [%d]\n", 61614779705SSam Leffler __func__, numPcdacs); 61714779705SSam Leffler return AH_FALSE; 61814779705SSam Leffler } 61914779705SSam Leffler for (ii = 0; ii < 64; ii++) { 62014779705SSam Leffler if (ii>pcdacs[idxR] && idxR < numPcdacs-1) { 62114779705SSam Leffler idxL++; 62214779705SSam Leffler idxR++; 62314779705SSam Leffler } 62414779705SSam Leffler retVals[ii] = interpolate_signed(ii, 62514779705SSam Leffler pcdacs[idxL], pcdacs[idxR], power[idxL], power[idxR]); 62614779705SSam Leffler if (retVals[ii] >= maxPower) { 62714779705SSam Leffler while (ii < 64) 62814779705SSam Leffler retVals[ii++] = maxPower; 62914779705SSam Leffler } 63014779705SSam Leffler } 63114779705SSam Leffler return AH_TRUE; 63214779705SSam Leffler } 63314779705SSam Leffler 63414779705SSam Leffler /* 63514779705SSam Leffler * Takes a single calibration curve and creates a power table. 63614779705SSam Leffler * Adjusts the new power table so the max power is relative 63714779705SSam Leffler * to the maximum index in the power table. 63814779705SSam Leffler * 63914779705SSam Leffler * WARNING: rates must be adjusted for this relative power table 64014779705SSam Leffler */ 64114779705SSam Leffler static int16_t 64214779705SSam Leffler getPminAndPcdacTableFromPowerTable(int16_t *pwrTableT4, uint16_t retVals[]) 64314779705SSam Leffler { 64414779705SSam Leffler int16_t ii, jj, jjMax; 64514779705SSam Leffler int16_t pMin, currPower, pMax; 64614779705SSam Leffler 64714779705SSam Leffler /* If the spread is > 31.5dB, keep the upper 31.5dB range */ 64814779705SSam Leffler if ((pwrTableT4[63] - pwrTableT4[0]) > 126) { 64914779705SSam Leffler pMin = pwrTableT4[63] - 126; 65014779705SSam Leffler } else { 65114779705SSam Leffler pMin = pwrTableT4[0]; 65214779705SSam Leffler } 65314779705SSam Leffler 65414779705SSam Leffler pMax = pwrTableT4[63]; 65514779705SSam Leffler jjMax = 63; 65614779705SSam Leffler 65714779705SSam Leffler /* Search for highest pcdac 0.25dB below maxPower */ 65814779705SSam Leffler while ((pwrTableT4[jjMax] > (pMax - 1) ) && (jjMax >= 0)) { 65914779705SSam Leffler jjMax--; 66014779705SSam Leffler } 66114779705SSam Leffler 66214779705SSam Leffler jj = jjMax; 66314779705SSam Leffler currPower = pMax; 66414779705SSam Leffler for (ii = 63; ii >= 0; ii--) { 66514779705SSam Leffler while ((jj < 64) && (jj > 0) && (pwrTableT4[jj] >= currPower)) { 66614779705SSam Leffler jj--; 66714779705SSam Leffler } 66814779705SSam Leffler if (jj == 0) { 66914779705SSam Leffler while (ii >= 0) { 67014779705SSam Leffler retVals[ii] = retVals[ii + 1]; 67114779705SSam Leffler ii--; 67214779705SSam Leffler } 67314779705SSam Leffler break; 67414779705SSam Leffler } 67514779705SSam Leffler retVals[ii] = jj; 67614779705SSam Leffler currPower -= 2; // corresponds to a 0.5dB step 67714779705SSam Leffler } 67814779705SSam Leffler return pMin; 67914779705SSam Leffler } 68014779705SSam Leffler 68114779705SSam Leffler /* 68214779705SSam Leffler * Combines the XPD curves from two calibration sets into a single 68314779705SSam Leffler * power table and adjusts the power table so the max power is relative 68414779705SSam Leffler * to the maximum index in the power table 68514779705SSam Leffler * 68614779705SSam Leffler * WARNING: rates must be adjusted for this relative power table 68714779705SSam Leffler */ 68814779705SSam Leffler static int16_t 68914779705SSam Leffler getPminAndPcdacTableFromTwoPowerTables(int16_t *pwrTableLXpdT4, 69014779705SSam Leffler int16_t *pwrTableHXpdT4, uint16_t retVals[], int16_t *pMid) 69114779705SSam Leffler { 69214779705SSam Leffler int16_t ii, jj, jjMax; 69314779705SSam Leffler int16_t pMin, pMax, currPower; 69414779705SSam Leffler int16_t *pwrTableT4; 69514779705SSam Leffler uint16_t msbFlag = 0x40; // turns on the 7th bit of the pcdac 69614779705SSam Leffler 69714779705SSam Leffler /* If the spread is > 31.5dB, keep the upper 31.5dB range */ 69814779705SSam Leffler if ((pwrTableLXpdT4[63] - pwrTableHXpdT4[0]) > 126) { 69914779705SSam Leffler pMin = pwrTableLXpdT4[63] - 126; 70014779705SSam Leffler } else { 70114779705SSam Leffler pMin = pwrTableHXpdT4[0]; 70214779705SSam Leffler } 70314779705SSam Leffler 70414779705SSam Leffler pMax = pwrTableLXpdT4[63]; 70514779705SSam Leffler jjMax = 63; 70614779705SSam Leffler /* Search for highest pcdac 0.25dB below maxPower */ 70714779705SSam Leffler while ((pwrTableLXpdT4[jjMax] > (pMax - 1) ) && (jjMax >= 0)){ 70814779705SSam Leffler jjMax--; 70914779705SSam Leffler } 71014779705SSam Leffler 71114779705SSam Leffler *pMid = pwrTableHXpdT4[63]; 71214779705SSam Leffler jj = jjMax; 71314779705SSam Leffler ii = 63; 71414779705SSam Leffler currPower = pMax; 71514779705SSam Leffler pwrTableT4 = &(pwrTableLXpdT4[0]); 71614779705SSam Leffler while (ii >= 0) { 71714779705SSam Leffler if ((currPower <= *pMid) || ( (jj == 0) && (msbFlag == 0x40))){ 71814779705SSam Leffler msbFlag = 0x00; 71914779705SSam Leffler pwrTableT4 = &(pwrTableHXpdT4[0]); 72014779705SSam Leffler jj = 63; 72114779705SSam Leffler } 72214779705SSam Leffler while ((jj > 0) && (pwrTableT4[jj] >= currPower)) { 72314779705SSam Leffler jj--; 72414779705SSam Leffler } 72514779705SSam Leffler if ((jj == 0) && (msbFlag == 0x00)) { 72614779705SSam Leffler while (ii >= 0) { 72714779705SSam Leffler retVals[ii] = retVals[ii+1]; 72814779705SSam Leffler ii--; 72914779705SSam Leffler } 73014779705SSam Leffler break; 73114779705SSam Leffler } 73214779705SSam Leffler retVals[ii] = jj | msbFlag; 73314779705SSam Leffler currPower -= 2; // corresponds to a 0.5dB step 73414779705SSam Leffler ii--; 73514779705SSam Leffler } 73614779705SSam Leffler return pMin; 73714779705SSam Leffler } 73814779705SSam Leffler 73914779705SSam Leffler static int16_t 74014779705SSam Leffler ar5112GetMinPower(struct ath_hal *ah, const EXPN_DATA_PER_CHANNEL_5112 *data) 74114779705SSam Leffler { 74214779705SSam Leffler int i, minIndex; 74314779705SSam Leffler int16_t minGain,minPwr,minPcdac,retVal; 74414779705SSam Leffler 74514779705SSam Leffler /* Assume NUM_POINTS_XPD0 > 0 */ 74614779705SSam Leffler minGain = data->pDataPerXPD[0].xpd_gain; 74714779705SSam Leffler for (minIndex=0,i=1; i<NUM_XPD_PER_CHANNEL; i++) { 74814779705SSam Leffler if (data->pDataPerXPD[i].xpd_gain < minGain) { 74914779705SSam Leffler minIndex = i; 75014779705SSam Leffler minGain = data->pDataPerXPD[i].xpd_gain; 75114779705SSam Leffler } 75214779705SSam Leffler } 75314779705SSam Leffler minPwr = data->pDataPerXPD[minIndex].pwr_t4[0]; 75414779705SSam Leffler minPcdac = data->pDataPerXPD[minIndex].pcdac[0]; 75514779705SSam Leffler for (i=1; i<NUM_POINTS_XPD0; i++) { 75614779705SSam Leffler if (data->pDataPerXPD[minIndex].pwr_t4[i] < minPwr) { 75714779705SSam Leffler minPwr = data->pDataPerXPD[minIndex].pwr_t4[i]; 75814779705SSam Leffler minPcdac = data->pDataPerXPD[minIndex].pcdac[i]; 75914779705SSam Leffler } 76014779705SSam Leffler } 76114779705SSam Leffler retVal = minPwr - (minPcdac*2); 76214779705SSam Leffler return(retVal); 76314779705SSam Leffler } 76414779705SSam Leffler 76514779705SSam Leffler static HAL_BOOL 76659efa8b5SSam Leffler ar5112GetChannelMaxMinPower(struct ath_hal *ah, 76759efa8b5SSam Leffler const struct ieee80211_channel *chan, 76814779705SSam Leffler int16_t *maxPow, int16_t *minPow) 76914779705SSam Leffler { 77059efa8b5SSam Leffler uint16_t freq = chan->ic_freq; /* NB: never mapped */ 77114779705SSam Leffler const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; 77214779705SSam Leffler int numChannels=0,i,last; 77314779705SSam Leffler int totalD, totalF,totalMin; 77414779705SSam Leffler const EXPN_DATA_PER_CHANNEL_5112 *data=AH_NULL; 77514779705SSam Leffler const EEPROM_POWER_EXPN_5112 *powerArray=AH_NULL; 77614779705SSam Leffler 77714779705SSam Leffler *maxPow = 0; 77859efa8b5SSam Leffler if (IEEE80211_IS_CHAN_A(chan)) { 77914779705SSam Leffler powerArray = ee->ee_modePowerArray5112; 78014779705SSam Leffler data = powerArray[headerInfo11A].pDataPerChannel; 78114779705SSam Leffler numChannels = powerArray[headerInfo11A].numChannels; 78259efa8b5SSam Leffler } else if (IEEE80211_IS_CHAN_G(chan) || IEEE80211_IS_CHAN_108G(chan)) { 78314779705SSam Leffler /* XXX - is this correct? Should we also use the same power for turbo G? */ 78414779705SSam Leffler powerArray = ee->ee_modePowerArray5112; 78514779705SSam Leffler data = powerArray[headerInfo11G].pDataPerChannel; 78614779705SSam Leffler numChannels = powerArray[headerInfo11G].numChannels; 78759efa8b5SSam Leffler } else if (IEEE80211_IS_CHAN_B(chan)) { 78814779705SSam Leffler powerArray = ee->ee_modePowerArray5112; 78914779705SSam Leffler data = powerArray[headerInfo11B].pDataPerChannel; 79014779705SSam Leffler numChannels = powerArray[headerInfo11B].numChannels; 79114779705SSam Leffler } else { 79214779705SSam Leffler return (AH_TRUE); 79314779705SSam Leffler } 79414779705SSam Leffler /* Make sure the channel is in the range of the TP values 79514779705SSam Leffler * (freq piers) 79614779705SSam Leffler */ 79714779705SSam Leffler if (numChannels < 1) 79814779705SSam Leffler return(AH_FALSE); 79914779705SSam Leffler 80059efa8b5SSam Leffler if ((freq < data[0].channelValue) || 80159efa8b5SSam Leffler (freq > data[numChannels-1].channelValue)) { 80259efa8b5SSam Leffler if (freq < data[0].channelValue) { 80314779705SSam Leffler *maxPow = data[0].maxPower_t4; 80414779705SSam Leffler *minPow = ar5112GetMinPower(ah, &data[0]); 80514779705SSam Leffler return(AH_TRUE); 80614779705SSam Leffler } else { 80714779705SSam Leffler *maxPow = data[numChannels - 1].maxPower_t4; 80814779705SSam Leffler *minPow = ar5112GetMinPower(ah, &data[numChannels - 1]); 80914779705SSam Leffler return(AH_TRUE); 81014779705SSam Leffler } 81114779705SSam Leffler } 81214779705SSam Leffler 81314779705SSam Leffler /* Linearly interpolate the power value now */ 81414779705SSam Leffler for (last=0,i=0; 81559efa8b5SSam Leffler (i<numChannels) && (freq > data[i].channelValue); 81614779705SSam Leffler last=i++); 81714779705SSam Leffler totalD = data[i].channelValue - data[last].channelValue; 81814779705SSam Leffler if (totalD > 0) { 81914779705SSam Leffler totalF = data[i].maxPower_t4 - data[last].maxPower_t4; 82059efa8b5SSam Leffler *maxPow = (int8_t) ((totalF*(freq-data[last].channelValue) + data[last].maxPower_t4*totalD)/totalD); 82114779705SSam Leffler 82214779705SSam Leffler totalMin = ar5112GetMinPower(ah,&data[i]) - ar5112GetMinPower(ah, &data[last]); 82359efa8b5SSam Leffler *minPow = (int8_t) ((totalMin*(freq-data[last].channelValue) + ar5112GetMinPower(ah, &data[last])*totalD)/totalD); 82414779705SSam Leffler return (AH_TRUE); 82514779705SSam Leffler } else { 82659efa8b5SSam Leffler if (freq == data[i].channelValue) { 82714779705SSam Leffler *maxPow = data[i].maxPower_t4; 82814779705SSam Leffler *minPow = ar5112GetMinPower(ah, &data[i]); 82914779705SSam Leffler return(AH_TRUE); 83014779705SSam Leffler } else 83114779705SSam Leffler return(AH_FALSE); 83214779705SSam Leffler } 83314779705SSam Leffler } 83414779705SSam Leffler 83514779705SSam Leffler /* 83614779705SSam Leffler * Free memory for analog bank scratch buffers 83714779705SSam Leffler */ 83814779705SSam Leffler static void 83914779705SSam Leffler ar5112RfDetach(struct ath_hal *ah) 84014779705SSam Leffler { 84114779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah); 84214779705SSam Leffler 84314779705SSam Leffler HALASSERT(ahp->ah_rfHal != AH_NULL); 84414779705SSam Leffler ath_hal_free(ahp->ah_rfHal); 84514779705SSam Leffler ahp->ah_rfHal = AH_NULL; 84614779705SSam Leffler } 84714779705SSam Leffler 84814779705SSam Leffler /* 84914779705SSam Leffler * Allocate memory for analog bank scratch buffers 85014779705SSam Leffler * Scratch Buffer will be reinitialized every reset so no need to zero now 85114779705SSam Leffler */ 85214779705SSam Leffler static HAL_BOOL 85314779705SSam Leffler ar5112RfAttach(struct ath_hal *ah, HAL_STATUS *status) 85414779705SSam Leffler { 85514779705SSam Leffler struct ath_hal_5212 *ahp = AH5212(ah); 85614779705SSam Leffler struct ar5112State *priv; 85714779705SSam Leffler 85814779705SSam Leffler HALASSERT(ah->ah_magic == AR5212_MAGIC); 85914779705SSam Leffler 86014779705SSam Leffler HALASSERT(ahp->ah_rfHal == AH_NULL); 86114779705SSam Leffler priv = ath_hal_malloc(sizeof(struct ar5112State)); 86214779705SSam Leffler if (priv == AH_NULL) { 86314779705SSam Leffler HALDEBUG(ah, HAL_DEBUG_ANY, 86414779705SSam Leffler "%s: cannot allocate private state\n", __func__); 86514779705SSam Leffler *status = HAL_ENOMEM; /* XXX */ 86614779705SSam Leffler return AH_FALSE; 86714779705SSam Leffler } 86814779705SSam Leffler priv->base.rfDetach = ar5112RfDetach; 86914779705SSam Leffler priv->base.writeRegs = ar5112WriteRegs; 87014779705SSam Leffler priv->base.getRfBank = ar5112GetRfBank; 87114779705SSam Leffler priv->base.setChannel = ar5112SetChannel; 87214779705SSam Leffler priv->base.setRfRegs = ar5112SetRfRegs; 87314779705SSam Leffler priv->base.setPowerTable = ar5112SetPowerTable; 87414779705SSam Leffler priv->base.getChannelMaxMinPower = ar5112GetChannelMaxMinPower; 87514779705SSam Leffler priv->base.getNfAdjust = ar5212GetNfAdjust; 87614779705SSam Leffler 87714779705SSam Leffler ahp->ah_pcdacTable = priv->pcdacTable; 87814779705SSam Leffler ahp->ah_pcdacTableSize = sizeof(priv->pcdacTable); 87914779705SSam Leffler ahp->ah_rfHal = &priv->base; 88014779705SSam Leffler 88114779705SSam Leffler return AH_TRUE; 88214779705SSam Leffler } 88314779705SSam Leffler 88414779705SSam Leffler static HAL_BOOL 88514779705SSam Leffler ar5112Probe(struct ath_hal *ah) 88614779705SSam Leffler { 88714779705SSam Leffler return IS_RAD5112(ah); 88814779705SSam Leffler } 88914779705SSam Leffler AH_RF(RF5112, ar5112Probe, ar5112RfAttach); 890