Path: blob/main/sys/dev/ath/ath_hal/ar5212/ar2317.c
39566 views
/*-1* SPDX-License-Identifier: ISC2*3* Copyright (c) 2002-2009 Sam Leffler, Errno Consulting4* Copyright (c) 2002-2008 Atheros Communications, Inc.5*6* Permission to use, copy, modify, and/or distribute this software for any7* purpose with or without fee is hereby granted, provided that the above8* copyright notice and this permission notice appear in all copies.9*10* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES11* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF12* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR13* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES14* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN15* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF16* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.17*/18#include "opt_ah.h"1920#include "ah.h"21#include "ah_internal.h"2223#include "ar5212/ar5212.h"24#include "ar5212/ar5212reg.h"25#include "ar5212/ar5212phy.h"2627#include "ah_eeprom_v3.h"2829#define AH_5212_231730#include "ar5212/ar5212.ini"3132#define N(a) (sizeof(a)/sizeof(a[0]))3334typedef RAW_DATA_STRUCT_2413 RAW_DATA_STRUCT_2317;35typedef RAW_DATA_PER_CHANNEL_2413 RAW_DATA_PER_CHANNEL_2317;36#define PWR_TABLE_SIZE_2317 PWR_TABLE_SIZE_24133738struct ar2317State {39RF_HAL_FUNCS base; /* public state, must be first */40uint16_t pcdacTable[PWR_TABLE_SIZE_2317];4142uint32_t Bank1Data[N(ar5212Bank1_2317)];43uint32_t Bank2Data[N(ar5212Bank2_2317)];44uint32_t Bank3Data[N(ar5212Bank3_2317)];45uint32_t Bank6Data[N(ar5212Bank6_2317)];46uint32_t Bank7Data[N(ar5212Bank7_2317)];4748/*49* Private state for reduced stack usage.50*/51/* filled out Vpd table for all pdGains (chanL) */52uint16_t vpdTable_L[MAX_NUM_PDGAINS_PER_CHANNEL]53[MAX_PWR_RANGE_IN_HALF_DB];54/* filled out Vpd table for all pdGains (chanR) */55uint16_t vpdTable_R[MAX_NUM_PDGAINS_PER_CHANNEL]56[MAX_PWR_RANGE_IN_HALF_DB];57/* filled out Vpd table for all pdGains (interpolated) */58uint16_t vpdTable_I[MAX_NUM_PDGAINS_PER_CHANNEL]59[MAX_PWR_RANGE_IN_HALF_DB];60};61#define AR2317(ah) ((struct ar2317State *) AH5212(ah)->ah_rfHal)6263extern void ar5212ModifyRfBuffer(uint32_t *rfBuf, uint32_t reg32,64uint32_t numBits, uint32_t firstBit, uint32_t column);6566static void67ar2317WriteRegs(struct ath_hal *ah, u_int modesIndex, u_int freqIndex,68int writes)69{70HAL_INI_WRITE_ARRAY(ah, ar5212Modes_2317, modesIndex, writes);71HAL_INI_WRITE_ARRAY(ah, ar5212Common_2317, 1, writes);72HAL_INI_WRITE_ARRAY(ah, ar5212BB_RfGain_2317, freqIndex, writes);73}7475/*76* Take the MHz channel value and set the Channel value77*78* ASSUMES: Writes enabled to analog bus79*/80static HAL_BOOL81ar2317SetChannel(struct ath_hal *ah, const struct ieee80211_channel *chan)82{83uint16_t freq = ath_hal_gethwchannel(ah, chan);84uint32_t channelSel = 0;85uint32_t bModeSynth = 0;86uint32_t aModeRefSel = 0;87uint32_t reg32 = 0;8889OS_MARK(ah, AH_MARK_SETCHANNEL, freq);9091if (freq < 4800) {92uint32_t txctl;93channelSel = freq - 2272 ;94channelSel = ath_hal_reverseBits(channelSel, 8);9596txctl = OS_REG_READ(ah, AR_PHY_CCK_TX_CTRL);97if (freq == 2484) {98/* Enable channel spreading for channel 14 */99OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL,100txctl | AR_PHY_CCK_TX_CTRL_JAPAN);101} else {102OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL,103txctl &~ AR_PHY_CCK_TX_CTRL_JAPAN);104}105} else if ((freq % 20) == 0 && freq >= 5120) {106channelSel = ath_hal_reverseBits(107((freq - 4800) / 20 << 2), 8);108aModeRefSel = ath_hal_reverseBits(3, 2);109} else if ((freq % 10) == 0) {110channelSel = ath_hal_reverseBits(111((freq - 4800) / 10 << 1), 8);112aModeRefSel = ath_hal_reverseBits(2, 2);113} else if ((freq % 5) == 0) {114channelSel = ath_hal_reverseBits(115(freq - 4800) / 5, 8);116aModeRefSel = ath_hal_reverseBits(1, 2);117} else {118HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u MHz\n",119__func__, freq);120return AH_FALSE;121}122123reg32 = (channelSel << 4) | (aModeRefSel << 2) | (bModeSynth << 1) |124(1 << 12) | 0x1;125OS_REG_WRITE(ah, AR_PHY(0x27), reg32 & 0xff);126127reg32 >>= 8;128OS_REG_WRITE(ah, AR_PHY(0x36), reg32 & 0x7f);129130AH_PRIVATE(ah)->ah_curchan = chan;131return AH_TRUE;132}133134/*135* Reads EEPROM header info from device structure and programs136* all rf registers137*138* REQUIRES: Access to the analog rf device139*/140static HAL_BOOL141ar2317SetRfRegs(struct ath_hal *ah,142const struct ieee80211_channel *chan,143uint16_t modesIndex, uint16_t *rfXpdGain)144{145#define RF_BANK_SETUP(_priv, _ix, _col) do { \146int i; \147for (i = 0; i < N(ar5212Bank##_ix##_2317); i++) \148(_priv)->Bank##_ix##Data[i] = ar5212Bank##_ix##_2317[i][_col];\149} while (0)150struct ath_hal_5212 *ahp = AH5212(ah);151const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;152uint16_t ob2GHz = 0, db2GHz = 0;153struct ar2317State *priv = AR2317(ah);154int regWrites = 0;155156HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: chan %u/0x%x modesIndex %u\n",157__func__, chan->ic_freq, chan->ic_flags, modesIndex);158159HALASSERT(priv);160161/* Setup rf parameters */162if (IEEE80211_IS_CHAN_B(chan)) {163ob2GHz = ee->ee_obFor24;164db2GHz = ee->ee_dbFor24;165} else {166ob2GHz = ee->ee_obFor24g;167db2GHz = ee->ee_dbFor24g;168}169170/* Bank 1 Write */171RF_BANK_SETUP(priv, 1, 1);172173/* Bank 2 Write */174RF_BANK_SETUP(priv, 2, modesIndex);175176/* Bank 3 Write */177RF_BANK_SETUP(priv, 3, modesIndex);178179/* Bank 6 Write */180RF_BANK_SETUP(priv, 6, modesIndex);181182ar5212ModifyRfBuffer(priv->Bank6Data, ob2GHz, 3, 193, 0);183ar5212ModifyRfBuffer(priv->Bank6Data, db2GHz, 3, 190, 0);184185/* Bank 7 Setup */186RF_BANK_SETUP(priv, 7, modesIndex);187188/* Write Analog registers */189HAL_INI_WRITE_BANK(ah, ar5212Bank1_2317, priv->Bank1Data, regWrites);190HAL_INI_WRITE_BANK(ah, ar5212Bank2_2317, priv->Bank2Data, regWrites);191HAL_INI_WRITE_BANK(ah, ar5212Bank3_2317, priv->Bank3Data, regWrites);192HAL_INI_WRITE_BANK(ah, ar5212Bank6_2317, priv->Bank6Data, regWrites);193HAL_INI_WRITE_BANK(ah, ar5212Bank7_2317, priv->Bank7Data, regWrites);194/* Now that we have reprogrammed rfgain value, clear the flag. */195ahp->ah_rfgainState = HAL_RFGAIN_INACTIVE;196197return AH_TRUE;198#undef RF_BANK_SETUP199}200201/*202* Return a reference to the requested RF Bank.203*/204static uint32_t *205ar2317GetRfBank(struct ath_hal *ah, int bank)206{207struct ar2317State *priv = AR2317(ah);208209HALASSERT(priv != AH_NULL);210switch (bank) {211case 1: return priv->Bank1Data;212case 2: return priv->Bank2Data;213case 3: return priv->Bank3Data;214case 6: return priv->Bank6Data;215case 7: return priv->Bank7Data;216}217HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown RF Bank %d requested\n",218__func__, bank);219return AH_NULL;220}221222/*223* Return indices surrounding the value in sorted integer lists.224*225* NB: the input list is assumed to be sorted in ascending order226*/227static void228GetLowerUpperIndex(int16_t v, const uint16_t *lp, uint16_t listSize,229uint32_t *vlo, uint32_t *vhi)230{231int16_t target = v;232const int16_t *ep = lp+listSize;233const int16_t *tp;234235/*236* Check first and last elements for out-of-bounds conditions.237*/238if (target < lp[0]) {239*vlo = *vhi = 0;240return;241}242if (target >= ep[-1]) {243*vlo = *vhi = listSize - 1;244return;245}246247/* look for value being near or between 2 values in list */248for (tp = lp; tp < ep; tp++) {249/*250* If value is close to the current value of the list251* then target is not between values, it is one of the values252*/253if (*tp == target) {254*vlo = *vhi = tp - (const int16_t *) lp;255return;256}257/*258* Look for value being between current value and next value259* if so return these 2 values260*/261if (target < tp[1]) {262*vlo = tp - (const int16_t *) lp;263*vhi = *vlo + 1;264return;265}266}267}268269/*270* Fill the Vpdlist for indices Pmax-Pmin271*/272static HAL_BOOL273ar2317FillVpdTable(uint32_t pdGainIdx, int16_t Pmin, int16_t Pmax,274const int16_t *pwrList, const int16_t *VpdList,275uint16_t numIntercepts, uint16_t retVpdList[][64])276{277uint16_t ii, jj, kk;278int16_t currPwr = (int16_t)(2*Pmin);279/* since Pmin is pwr*2 and pwrList is 4*pwr */280uint32_t idxL, idxR;281282ii = 0;283jj = 0;284285if (numIntercepts < 2)286return AH_FALSE;287288while (ii <= (uint16_t)(Pmax - Pmin)) {289GetLowerUpperIndex(currPwr, pwrList, numIntercepts,290&(idxL), &(idxR));291if (idxR < 1)292idxR = 1; /* extrapolate below */293if (idxL == (uint32_t)(numIntercepts - 1))294idxL = numIntercepts - 2; /* extrapolate above */295if (pwrList[idxL] == pwrList[idxR])296kk = VpdList[idxL];297else298kk = (uint16_t)299(((currPwr - pwrList[idxL])*VpdList[idxR]+300(pwrList[idxR] - currPwr)*VpdList[idxL])/301(pwrList[idxR] - pwrList[idxL]));302retVpdList[pdGainIdx][ii] = kk;303ii++;304currPwr += 2; /* half dB steps */305}306307return AH_TRUE;308}309310/*311* Returns interpolated or the scaled up interpolated value312*/313static int16_t314interpolate_signed(uint16_t target, uint16_t srcLeft, uint16_t srcRight,315int16_t targetLeft, int16_t targetRight)316{317int16_t rv;318319if (srcRight != srcLeft) {320rv = ((target - srcLeft)*targetRight +321(srcRight - target)*targetLeft) / (srcRight - srcLeft);322} else {323rv = targetLeft;324}325return rv;326}327328/*329* Uses the data points read from EEPROM to reconstruct the pdadc power table330* Called by ar2317SetPowerTable()331*/332static int333ar2317getGainBoundariesAndPdadcsForPowers(struct ath_hal *ah, uint16_t channel,334const RAW_DATA_STRUCT_2317 *pRawDataset,335uint16_t pdGainOverlap_t2,336int16_t *pMinCalPower, uint16_t pPdGainBoundaries[],337uint16_t pPdGainValues[], uint16_t pPDADCValues[])338{339struct ar2317State *priv = AR2317(ah);340#define VpdTable_L priv->vpdTable_L341#define VpdTable_R priv->vpdTable_R342#define VpdTable_I priv->vpdTable_I343/* XXX excessive stack usage? */344uint32_t ii, jj, kk;345int32_t ss;/* potentially -ve index for taking care of pdGainOverlap */346uint32_t idxL, idxR;347uint32_t numPdGainsUsed = 0;348/*349* If desired to support -ve power levels in future, just350* change pwr_I_0 to signed 5-bits.351*/352int16_t Pmin_t2[MAX_NUM_PDGAINS_PER_CHANNEL];353/* to accommodate -ve power levels later on. */354int16_t Pmax_t2[MAX_NUM_PDGAINS_PER_CHANNEL];355/* to accommodate -ve power levels later on */356uint16_t numVpd = 0;357uint16_t Vpd_step;358int16_t tmpVal ;359uint32_t sizeCurrVpdTable, maxIndex, tgtIndex;360361/* Get upper lower index */362GetLowerUpperIndex(channel, pRawDataset->pChannels,363pRawDataset->numChannels, &(idxL), &(idxR));364365for (ii = 0; ii < MAX_NUM_PDGAINS_PER_CHANNEL; ii++) {366jj = MAX_NUM_PDGAINS_PER_CHANNEL - ii - 1;367/* work backwards 'cause highest pdGain for lowest power */368numVpd = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].numVpd;369if (numVpd > 0) {370pPdGainValues[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pd_gain;371Pmin_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[0];372if (Pmin_t2[numPdGainsUsed] >pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]) {373Pmin_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0];374}375Pmin_t2[numPdGainsUsed] = (int16_t)376(Pmin_t2[numPdGainsUsed] / 2);377Pmax_t2[numPdGainsUsed] = pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[numVpd-1];378if (Pmax_t2[numPdGainsUsed] > pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[numVpd-1])379Pmax_t2[numPdGainsUsed] =380pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[numVpd-1];381Pmax_t2[numPdGainsUsed] = (int16_t)(Pmax_t2[numPdGainsUsed] / 2);382ar2317FillVpdTable(383numPdGainsUsed, Pmin_t2[numPdGainsUsed], Pmax_t2[numPdGainsUsed],384&(pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].pwr_t4[0]),385&(pRawDataset->pDataPerChannel[idxL].pDataPerPDGain[jj].Vpd[0]), numVpd, VpdTable_L386);387ar2317FillVpdTable(388numPdGainsUsed, Pmin_t2[numPdGainsUsed], Pmax_t2[numPdGainsUsed],389&(pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].pwr_t4[0]),390&(pRawDataset->pDataPerChannel[idxR].pDataPerPDGain[jj].Vpd[0]), numVpd, VpdTable_R391);392for (kk = 0; kk < (uint16_t)(Pmax_t2[numPdGainsUsed] - Pmin_t2[numPdGainsUsed]); kk++) {393VpdTable_I[numPdGainsUsed][kk] =394interpolate_signed(395channel, pRawDataset->pChannels[idxL], pRawDataset->pChannels[idxR],396(int16_t)VpdTable_L[numPdGainsUsed][kk], (int16_t)VpdTable_R[numPdGainsUsed][kk]);397}398/* fill VpdTable_I for this pdGain */399numPdGainsUsed++;400}401/* if this pdGain is used */402}403404*pMinCalPower = Pmin_t2[0];405kk = 0; /* index for the final table */406for (ii = 0; ii < numPdGainsUsed; ii++) {407if (ii == (numPdGainsUsed - 1))408pPdGainBoundaries[ii] = Pmax_t2[ii] +409PD_GAIN_BOUNDARY_STRETCH_IN_HALF_DB;410else411pPdGainBoundaries[ii] = (uint16_t)412((Pmax_t2[ii] + Pmin_t2[ii+1]) / 2 );413if (pPdGainBoundaries[ii] > 63) {414HALDEBUG(ah, HAL_DEBUG_ANY,415"%s: clamp pPdGainBoundaries[%d] %d\n",416__func__, ii, pPdGainBoundaries[ii]);/*XXX*/417pPdGainBoundaries[ii] = 63;418}419420/* Find starting index for this pdGain */421if (ii == 0)422ss = 0; /* for the first pdGain, start from index 0 */423else424ss = (pPdGainBoundaries[ii-1] - Pmin_t2[ii]) -425pdGainOverlap_t2;426Vpd_step = (uint16_t)(VpdTable_I[ii][1] - VpdTable_I[ii][0]);427Vpd_step = (uint16_t)((Vpd_step < 1) ? 1 : Vpd_step);428/*429*-ve ss indicates need to extrapolate data below for this pdGain430*/431while (ss < 0) {432tmpVal = (int16_t)(VpdTable_I[ii][0] + ss*Vpd_step);433pPDADCValues[kk++] = (uint16_t)((tmpVal < 0) ? 0 : tmpVal);434ss++;435}436437sizeCurrVpdTable = Pmax_t2[ii] - Pmin_t2[ii];438tgtIndex = pPdGainBoundaries[ii] + pdGainOverlap_t2 - Pmin_t2[ii];439maxIndex = (tgtIndex < sizeCurrVpdTable) ? tgtIndex : sizeCurrVpdTable;440441while (ss < (int16_t)maxIndex)442pPDADCValues[kk++] = VpdTable_I[ii][ss++];443444Vpd_step = (uint16_t)(VpdTable_I[ii][sizeCurrVpdTable-1] -445VpdTable_I[ii][sizeCurrVpdTable-2]);446Vpd_step = (uint16_t)((Vpd_step < 1) ? 1 : Vpd_step);447/*448* for last gain, pdGainBoundary == Pmax_t2, so will449* have to extrapolate450*/451if (tgtIndex > maxIndex) { /* need to extrapolate above */452while(ss < (int16_t)tgtIndex) {453tmpVal = (uint16_t)454(VpdTable_I[ii][sizeCurrVpdTable-1] +455(ss-maxIndex)*Vpd_step);456pPDADCValues[kk++] = (tmpVal > 127) ?457127 : tmpVal;458ss++;459}460} /* extrapolated above */461} /* for all pdGainUsed */462463while (ii < MAX_NUM_PDGAINS_PER_CHANNEL) {464pPdGainBoundaries[ii] = pPdGainBoundaries[ii-1];465ii++;466}467while (kk < 128) {468pPDADCValues[kk] = pPDADCValues[kk-1];469kk++;470}471472return numPdGainsUsed;473#undef VpdTable_L474#undef VpdTable_R475#undef VpdTable_I476}477478static HAL_BOOL479ar2317SetPowerTable(struct ath_hal *ah,480int16_t *minPower, int16_t *maxPower,481const struct ieee80211_channel *chan,482uint16_t *rfXpdGain)483{484struct ath_hal_5212 *ahp = AH5212(ah);485const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;486const RAW_DATA_STRUCT_2317 *pRawDataset = AH_NULL;487uint16_t pdGainOverlap_t2;488int16_t minCalPower2317_t2;489uint16_t *pdadcValues = ahp->ah_pcdacTable;490uint16_t gainBoundaries[4];491uint32_t reg32, regoffset;492int i, numPdGainsUsed;493#ifndef AH_USE_INIPDGAIN494uint32_t tpcrg1;495#endif496497HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: chan 0x%x flag 0x%x\n",498__func__, chan->ic_freq, chan->ic_flags);499500if (IEEE80211_IS_CHAN_G(chan) || IEEE80211_IS_CHAN_108G(chan))501pRawDataset = &ee->ee_rawDataset2413[headerInfo11G];502else if (IEEE80211_IS_CHAN_B(chan))503pRawDataset = &ee->ee_rawDataset2413[headerInfo11B];504else {505HALDEBUG(ah, HAL_DEBUG_ANY, "%s: illegal mode\n", __func__);506return AH_FALSE;507}508509pdGainOverlap_t2 = (uint16_t) SM(OS_REG_READ(ah, AR_PHY_TPCRG5),510AR_PHY_TPCRG5_PD_GAIN_OVERLAP);511512numPdGainsUsed = ar2317getGainBoundariesAndPdadcsForPowers(ah,513chan->channel, pRawDataset, pdGainOverlap_t2,514&minCalPower2317_t2,gainBoundaries, rfXpdGain, pdadcValues);515HALASSERT(1 <= numPdGainsUsed && numPdGainsUsed <= 3);516517#ifdef AH_USE_INIPDGAIN518/*519* Use pd_gains curve from eeprom; Atheros always uses520* the default curve from the ini file but some vendors521* (e.g. Zcomax) want to override this curve and not522* honoring their settings results in tx power 5dBm low.523*/524OS_REG_RMW_FIELD(ah, AR_PHY_TPCRG1, AR_PHY_TPCRG1_NUM_PD_GAIN,525(pRawDataset->pDataPerChannel[0].numPdGains - 1));526#else527tpcrg1 = OS_REG_READ(ah, AR_PHY_TPCRG1);528tpcrg1 = (tpcrg1 &~ AR_PHY_TPCRG1_NUM_PD_GAIN)529| SM(numPdGainsUsed-1, AR_PHY_TPCRG1_NUM_PD_GAIN);530switch (numPdGainsUsed) {531case 3:532tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING3;533tpcrg1 |= SM(rfXpdGain[2], AR_PHY_TPCRG1_PDGAIN_SETTING3);534/* fall thru... */535case 2:536tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING2;537tpcrg1 |= SM(rfXpdGain[1], AR_PHY_TPCRG1_PDGAIN_SETTING2);538/* fall thru... */539case 1:540tpcrg1 &= ~AR_PHY_TPCRG1_PDGAIN_SETTING1;541tpcrg1 |= SM(rfXpdGain[0], AR_PHY_TPCRG1_PDGAIN_SETTING1);542break;543}544#ifdef AH_DEBUG545if (tpcrg1 != OS_REG_READ(ah, AR_PHY_TPCRG1))546HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: using non-default "547"pd_gains (default 0x%x, calculated 0x%x)\n",548__func__, OS_REG_READ(ah, AR_PHY_TPCRG1), tpcrg1);549#endif550OS_REG_WRITE(ah, AR_PHY_TPCRG1, tpcrg1);551#endif552553/*554* Note the pdadc table may not start at 0 dBm power, could be555* negative or greater than 0. Need to offset the power556* values by the amount of minPower for griffin557*/558if (minCalPower2317_t2 != 0)559ahp->ah_txPowerIndexOffset = (int16_t)(0 - minCalPower2317_t2);560else561ahp->ah_txPowerIndexOffset = 0;562563/* Finally, write the power values into the baseband power table */564regoffset = 0x9800 + (672 <<2); /* beginning of pdadc table in griffin */565for (i = 0; i < 32; i++) {566reg32 = ((pdadcValues[4*i + 0] & 0xFF) << 0) |567((pdadcValues[4*i + 1] & 0xFF) << 8) |568((pdadcValues[4*i + 2] & 0xFF) << 16) |569((pdadcValues[4*i + 3] & 0xFF) << 24) ;570OS_REG_WRITE(ah, regoffset, reg32);571regoffset += 4;572}573574OS_REG_WRITE(ah, AR_PHY_TPCRG5,575SM(pdGainOverlap_t2, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) |576SM(gainBoundaries[0], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) |577SM(gainBoundaries[1], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) |578SM(gainBoundaries[2], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) |579SM(gainBoundaries[3], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4));580581return AH_TRUE;582}583584static int16_t585ar2317GetMinPower(struct ath_hal *ah, const RAW_DATA_PER_CHANNEL_2317 *data)586{587uint32_t ii,jj;588uint16_t Pmin=0,numVpd;589590for (ii = 0; ii < MAX_NUM_PDGAINS_PER_CHANNEL; ii++) {591jj = MAX_NUM_PDGAINS_PER_CHANNEL - ii - 1;592/* work backwards 'cause highest pdGain for lowest power */593numVpd = data->pDataPerPDGain[jj].numVpd;594if (numVpd > 0) {595Pmin = data->pDataPerPDGain[jj].pwr_t4[0];596return(Pmin);597}598}599return(Pmin);600}601602static int16_t603ar2317GetMaxPower(struct ath_hal *ah, const RAW_DATA_PER_CHANNEL_2317 *data)604{605uint32_t ii;606uint16_t Pmax=0,numVpd;607uint16_t vpdmax;608609for (ii=0; ii< MAX_NUM_PDGAINS_PER_CHANNEL; ii++) {610/* work forwards cuase lowest pdGain for highest power */611numVpd = data->pDataPerPDGain[ii].numVpd;612if (numVpd > 0) {613Pmax = data->pDataPerPDGain[ii].pwr_t4[numVpd-1];614vpdmax = data->pDataPerPDGain[ii].Vpd[numVpd-1];615return(Pmax);616}617}618return(Pmax);619}620621static HAL_BOOL622ar2317GetChannelMaxMinPower(struct ath_hal *ah,623const struct ieee80211_channel *chan,624int16_t *maxPow, int16_t *minPow)625{626uint16_t freq = chan->ic_freq; /* NB: never mapped */627const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;628const RAW_DATA_STRUCT_2317 *pRawDataset = AH_NULL;629const RAW_DATA_PER_CHANNEL_2317 *data=AH_NULL;630uint16_t numChannels;631int totalD,totalF, totalMin,last, i;632633*maxPow = 0;634635if (IEEE80211_IS_CHAN_G(chan) || IEEE80211_IS_CHAN_108G(chan))636pRawDataset = &ee->ee_rawDataset2413[headerInfo11G];637else if (IEEE80211_IS_CHAN_B(chan))638pRawDataset = &ee->ee_rawDataset2413[headerInfo11B];639else640return(AH_FALSE);641642numChannels = pRawDataset->numChannels;643data = pRawDataset->pDataPerChannel;644645/* Make sure the channel is in the range of the TP values646* (freq piers)647*/648if (numChannels < 1)649return(AH_FALSE);650651if ((freq < data[0].channelValue) ||652(freq > data[numChannels-1].channelValue)) {653if (freq < data[0].channelValue) {654*maxPow = ar2317GetMaxPower(ah, &data[0]);655*minPow = ar2317GetMinPower(ah, &data[0]);656return(AH_TRUE);657} else {658*maxPow = ar2317GetMaxPower(ah, &data[numChannels - 1]);659*minPow = ar2317GetMinPower(ah, &data[numChannels - 1]);660return(AH_TRUE);661}662}663664/* Linearly interpolate the power value now */665for (last=0,i=0; (i<numChannels) && (freq > data[i].channelValue);666last = i++);667totalD = data[i].channelValue - data[last].channelValue;668if (totalD > 0) {669totalF = ar2317GetMaxPower(ah, &data[i]) - ar2317GetMaxPower(ah, &data[last]);670*maxPow = (int8_t) ((totalF*(freq-data[last].channelValue) +671ar2317GetMaxPower(ah, &data[last])*totalD)/totalD);672totalMin = ar2317GetMinPower(ah, &data[i]) - ar2317GetMinPower(ah, &data[last]);673*minPow = (int8_t) ((totalMin*(freq-data[last].channelValue) +674ar2317GetMinPower(ah, &data[last])*totalD)/totalD);675return(AH_TRUE);676} else {677if (freq == data[i].channelValue) {678*maxPow = ar2317GetMaxPower(ah, &data[i]);679*minPow = ar2317GetMinPower(ah, &data[i]);680return(AH_TRUE);681} else682return(AH_FALSE);683}684}685686/*687* Free memory for analog bank scratch buffers688*/689static void690ar2317RfDetach(struct ath_hal *ah)691{692struct ath_hal_5212 *ahp = AH5212(ah);693694HALASSERT(ahp->ah_rfHal != AH_NULL);695ath_hal_free(ahp->ah_rfHal);696ahp->ah_rfHal = AH_NULL;697}698699/*700* Allocate memory for analog bank scratch buffers701* Scratch Buffer will be reinitialized every reset so no need to zero now702*/703static HAL_BOOL704ar2317RfAttach(struct ath_hal *ah, HAL_STATUS *status)705{706struct ath_hal_5212 *ahp = AH5212(ah);707struct ar2317State *priv;708709HALASSERT(ah->ah_magic == AR5212_MAGIC);710711HALASSERT(ahp->ah_rfHal == AH_NULL);712priv = ath_hal_malloc(sizeof(struct ar2317State));713if (priv == AH_NULL) {714HALDEBUG(ah, HAL_DEBUG_ANY,715"%s: cannot allocate private state\n", __func__);716*status = HAL_ENOMEM; /* XXX */717return AH_FALSE;718}719priv->base.rfDetach = ar2317RfDetach;720priv->base.writeRegs = ar2317WriteRegs;721priv->base.getRfBank = ar2317GetRfBank;722priv->base.setChannel = ar2317SetChannel;723priv->base.setRfRegs = ar2317SetRfRegs;724priv->base.setPowerTable = ar2317SetPowerTable;725priv->base.getChannelMaxMinPower = ar2317GetChannelMaxMinPower;726priv->base.getNfAdjust = ar5212GetNfAdjust;727728ahp->ah_pcdacTable = priv->pcdacTable;729ahp->ah_pcdacTableSize = sizeof(priv->pcdacTable);730ahp->ah_rfHal = &priv->base;731732return AH_TRUE;733}734735static HAL_BOOL736ar2317Probe(struct ath_hal *ah)737{738return IS_2317(ah);739}740AH_RF(RF2317, ar2317Probe, ar2317RfAttach);741742743