| 1 | /* |
| 2 | * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting |
| 3 | * Copyright (c) 2002-2008 Atheros Communications, Inc. |
| 4 | * |
| 5 | * Permission to use, copy, modify, and/or distribute this software for any |
| 6 | * purpose with or without fee is hereby granted, provided that the above |
| 7 | * copyright notice and this permission notice appear in all copies. |
| 8 | * |
| 9 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
| 10 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
| 11 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
| 12 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
| 13 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
| 14 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
| 15 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
| 16 | * |
| 17 | * $Id: ar5112.c,v 1.1.1.1 2008/12/11 04:46:37 alc Exp $ |
| 18 | */ |
| 19 | #include "opt_ah.h" |
| 20 | |
| 21 | #include "ah.h" |
| 22 | #include "ah_internal.h" |
| 23 | |
| 24 | #include "ah_eeprom_v3.h" |
| 25 | |
| 26 | #include "ar5212/ar5212.h" |
| 27 | #include "ar5212/ar5212reg.h" |
| 28 | #include "ar5212/ar5212phy.h" |
| 29 | |
| 30 | #define AH_5212_5112 |
| 31 | #include "ar5212/ar5212.ini" |
| 32 | |
| 33 | #define N(a) (sizeof(a)/sizeof(a[0])) |
| 34 | |
| 35 | struct ar5112State { |
| 36 | RF_HAL_FUNCS base; /* public state, must be first */ |
| 37 | uint16_t pcdacTable[PWR_TABLE_SIZE]; |
| 38 | |
| 39 | uint32_t Bank1Data[N(ar5212Bank1_5112)]; |
| 40 | uint32_t Bank2Data[N(ar5212Bank2_5112)]; |
| 41 | uint32_t Bank3Data[N(ar5212Bank3_5112)]; |
| 42 | uint32_t Bank6Data[N(ar5212Bank6_5112)]; |
| 43 | uint32_t Bank7Data[N(ar5212Bank7_5112)]; |
| 44 | }; |
| 45 | #define AR5112(ah) ((struct ar5112State *) AH5212(ah)->ah_rfHal) |
| 46 | |
| 47 | static void ar5212GetLowerUpperIndex(uint16_t v, |
| 48 | uint16_t *lp, uint16_t listSize, |
| 49 | uint32_t *vlo, uint32_t *vhi); |
| 50 | static HAL_BOOL getFullPwrTable(uint16_t numPcdacs, uint16_t *pcdacs, |
| 51 | int16_t *power, int16_t maxPower, int16_t *retVals); |
| 52 | static int16_t getPminAndPcdacTableFromPowerTable(int16_t *pwrTableT4, |
| 53 | uint16_t retVals[]); |
| 54 | static int16_t getPminAndPcdacTableFromTwoPowerTables(int16_t *pwrTableLXpdT4, |
| 55 | int16_t *pwrTableHXpdT4, uint16_t retVals[], int16_t *pMid); |
| 56 | static int16_t interpolate_signed(uint16_t target, |
| 57 | uint16_t srcLeft, uint16_t srcRight, |
| 58 | int16_t targetLeft, int16_t targetRight); |
| 59 | |
| 60 | extern void ar5212ModifyRfBuffer(uint32_t *rfBuf, uint32_t reg32, |
| 61 | uint32_t numBits, uint32_t firstBit, uint32_t column); |
| 62 | |
| 63 | static void |
| 64 | ar5112WriteRegs(struct ath_hal *ah, u_int modesIndex, u_int freqIndex, |
| 65 | int writes) |
| 66 | { |
| 67 | HAL_INI_WRITE_ARRAY(ah, ar5212Modes_5112, modesIndex, writes); |
| 68 | HAL_INI_WRITE_ARRAY(ah, ar5212Common_5112, 1, writes); |
| 69 | HAL_INI_WRITE_ARRAY(ah, ar5212BB_RfGain_5112, freqIndex, writes); |
| 70 | } |
| 71 | |
| 72 | /* |
| 73 | * Take the MHz channel value and set the Channel value |
| 74 | * |
| 75 | * ASSUMES: Writes enabled to analog bus |
| 76 | */ |
| 77 | static HAL_BOOL |
| 78 | ar5112SetChannel(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan) |
| 79 | { |
| 80 | uint32_t channelSel = 0; |
| 81 | uint32_t bModeSynth = 0; |
| 82 | uint32_t aModeRefSel = 0; |
| 83 | uint32_t reg32 = 0; |
| 84 | uint16_t freq; |
| 85 | |
| 86 | OS_MARK(ah, AH_MARK_SETCHANNEL, chan->channel); |
| 87 | |
| 88 | if (chan->channel < 4800) { |
| 89 | uint32_t txctl; |
| 90 | |
| 91 | if (((chan->channel - 2192) % 5) == 0) { |
| 92 | channelSel = ((chan->channel - 672) * 2 - 3040)/10; |
| 93 | bModeSynth = 0; |
| 94 | } else if (((chan->channel - 2224) % 5) == 0) { |
| 95 | channelSel = ((chan->channel - 704) * 2 - 3040) / 10; |
| 96 | bModeSynth = 1; |
| 97 | } else { |
| 98 | HALDEBUG(ah, HAL_DEBUG_ANY, |
| 99 | "%s: invalid channel %u MHz\n" , |
| 100 | __func__, chan->channel); |
| 101 | return AH_FALSE; |
| 102 | } |
| 103 | |
| 104 | channelSel = (channelSel << 2) & 0xff; |
| 105 | channelSel = ath_hal_reverseBits(channelSel, 8); |
| 106 | |
| 107 | txctl = OS_REG_READ(ah, AR_PHY_CCK_TX_CTRL); |
| 108 | if (chan->channel == 2484) { |
| 109 | /* Enable channel spreading for channel 14 */ |
| 110 | OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, |
| 111 | txctl | AR_PHY_CCK_TX_CTRL_JAPAN); |
| 112 | } else { |
| 113 | OS_REG_WRITE(ah, AR_PHY_CCK_TX_CTRL, |
| 114 | txctl &~ AR_PHY_CCK_TX_CTRL_JAPAN); |
| 115 | } |
| 116 | } else if (((chan->channel % 5) == 2) && (chan->channel <= 5435)) { |
| 117 | freq = chan->channel - 2; /* Align to even 5MHz raster */ |
| 118 | channelSel = ath_hal_reverseBits( |
| 119 | (uint32_t)(((freq - 4800)*10)/25 + 1), 8); |
| 120 | aModeRefSel = ath_hal_reverseBits(0, 2); |
| 121 | } else if ((chan->channel % 20) == 0 && chan->channel >= 5120) { |
| 122 | channelSel = ath_hal_reverseBits( |
| 123 | ((chan->channel - 4800) / 20 << 2), 8); |
| 124 | aModeRefSel = ath_hal_reverseBits(3, 2); |
| 125 | } else if ((chan->channel % 10) == 0) { |
| 126 | channelSel = ath_hal_reverseBits( |
| 127 | ((chan->channel - 4800) / 10 << 1), 8); |
| 128 | aModeRefSel = ath_hal_reverseBits(2, 2); |
| 129 | } else if ((chan->channel % 5) == 0) { |
| 130 | channelSel = ath_hal_reverseBits( |
| 131 | (chan->channel - 4800) / 5, 8); |
| 132 | aModeRefSel = ath_hal_reverseBits(1, 2); |
| 133 | } else { |
| 134 | HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel %u MHz\n" , |
| 135 | __func__, chan->channel); |
| 136 | return AH_FALSE; |
| 137 | } |
| 138 | |
| 139 | reg32 = (channelSel << 4) | (aModeRefSel << 2) | (bModeSynth << 1) | |
| 140 | (1 << 12) | 0x1; |
| 141 | OS_REG_WRITE(ah, AR_PHY(0x27), reg32 & 0xff); |
| 142 | |
| 143 | reg32 >>= 8; |
| 144 | OS_REG_WRITE(ah, AR_PHY(0x36), reg32 & 0x7f); |
| 145 | |
| 146 | AH_PRIVATE(ah)->ah_curchan = chan; |
| 147 | return AH_TRUE; |
| 148 | } |
| 149 | |
| 150 | /* |
| 151 | * Return a reference to the requested RF Bank. |
| 152 | */ |
| 153 | static uint32_t * |
| 154 | ar5112GetRfBank(struct ath_hal *ah, int bank) |
| 155 | { |
| 156 | struct ar5112State *priv = AR5112(ah); |
| 157 | |
| 158 | HALASSERT(priv != AH_NULL); |
| 159 | switch (bank) { |
| 160 | case 1: return priv->Bank1Data; |
| 161 | case 2: return priv->Bank2Data; |
| 162 | case 3: return priv->Bank3Data; |
| 163 | case 6: return priv->Bank6Data; |
| 164 | case 7: return priv->Bank7Data; |
| 165 | } |
| 166 | HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown RF Bank %d requested\n" , |
| 167 | __func__, bank); |
| 168 | return AH_NULL; |
| 169 | } |
| 170 | |
| 171 | /* |
| 172 | * Reads EEPROM header info from device structure and programs |
| 173 | * all rf registers |
| 174 | * |
| 175 | * REQUIRES: Access to the analog rf device |
| 176 | */ |
| 177 | static HAL_BOOL |
| 178 | ar5112SetRfRegs(struct ath_hal *ah, HAL_CHANNEL_INTERNAL *chan, |
| 179 | uint16_t modesIndex, uint16_t *rfXpdGain) |
| 180 | { |
| 181 | #define RF_BANK_SETUP(_priv, _ix, _col) do { \ |
| 182 | int i; \ |
| 183 | for (i = 0; i < N(ar5212Bank##_ix##_5112); i++) \ |
| 184 | (_priv)->Bank##_ix##Data[i] = ar5212Bank##_ix##_5112[i][_col];\ |
| 185 | } while (0) |
| 186 | struct ath_hal_5212 *ahp = AH5212(ah); |
| 187 | const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; |
| 188 | uint16_t rfXpdSel, gainI; |
| 189 | uint16_t ob5GHz = 0, db5GHz = 0; |
| 190 | uint16_t ob2GHz = 0, db2GHz = 0; |
| 191 | struct ar5112State *priv = AR5112(ah); |
| 192 | GAIN_VALUES *gv = &ahp->ah_gainValues; |
| 193 | int regWrites = 0; |
| 194 | |
| 195 | HALASSERT(priv); |
| 196 | |
| 197 | /* Setup rf parameters */ |
| 198 | switch (chan->channelFlags & CHANNEL_ALL) { |
| 199 | case CHANNEL_A: |
| 200 | case CHANNEL_T: |
| 201 | if (chan->channel > 4000 && chan->channel < 5260) { |
| 202 | ob5GHz = ee->ee_ob1; |
| 203 | db5GHz = ee->ee_db1; |
| 204 | } else if (chan->channel >= 5260 && chan->channel < 5500) { |
| 205 | ob5GHz = ee->ee_ob2; |
| 206 | db5GHz = ee->ee_db2; |
| 207 | } else if (chan->channel >= 5500 && chan->channel < 5725) { |
| 208 | ob5GHz = ee->ee_ob3; |
| 209 | db5GHz = ee->ee_db3; |
| 210 | } else if (chan->channel >= 5725) { |
| 211 | ob5GHz = ee->ee_ob4; |
| 212 | db5GHz = ee->ee_db4; |
| 213 | } else { |
| 214 | /* XXX else */ |
| 215 | } |
| 216 | rfXpdSel = ee->ee_xpd[headerInfo11A]; |
| 217 | gainI = ee->ee_gainI[headerInfo11A]; |
| 218 | break; |
| 219 | case CHANNEL_B: |
| 220 | ob2GHz = ee->ee_ob2GHz[0]; |
| 221 | db2GHz = ee->ee_db2GHz[0]; |
| 222 | rfXpdSel = ee->ee_xpd[headerInfo11B]; |
| 223 | gainI = ee->ee_gainI[headerInfo11B]; |
| 224 | break; |
| 225 | case CHANNEL_G: |
| 226 | case CHANNEL_108G: |
| 227 | ob2GHz = ee->ee_ob2GHz[1]; |
| 228 | db2GHz = ee->ee_ob2GHz[1]; |
| 229 | rfXpdSel = ee->ee_xpd[headerInfo11G]; |
| 230 | gainI = ee->ee_gainI[headerInfo11G]; |
| 231 | break; |
| 232 | default: |
| 233 | HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n" , |
| 234 | __func__, chan->channelFlags); |
| 235 | return AH_FALSE; |
| 236 | } |
| 237 | |
| 238 | /* Setup Bank 1 Write */ |
| 239 | RF_BANK_SETUP(priv, 1, 1); |
| 240 | |
| 241 | /* Setup Bank 2 Write */ |
| 242 | RF_BANK_SETUP(priv, 2, modesIndex); |
| 243 | |
| 244 | /* Setup Bank 3 Write */ |
| 245 | RF_BANK_SETUP(priv, 3, modesIndex); |
| 246 | |
| 247 | /* Setup Bank 6 Write */ |
| 248 | RF_BANK_SETUP(priv, 6, modesIndex); |
| 249 | |
| 250 | ar5212ModifyRfBuffer(priv->Bank6Data, rfXpdSel, 1, 302, 0); |
| 251 | |
| 252 | ar5212ModifyRfBuffer(priv->Bank6Data, rfXpdGain[0], 2, 270, 0); |
| 253 | ar5212ModifyRfBuffer(priv->Bank6Data, rfXpdGain[1], 2, 257, 0); |
| 254 | |
| 255 | if (IS_CHAN_OFDM(chan)) { |
| 256 | ar5212ModifyRfBuffer(priv->Bank6Data, |
| 257 | gv->currStep->paramVal[GP_PWD_138], 1, 168, 3); |
| 258 | ar5212ModifyRfBuffer(priv->Bank6Data, |
| 259 | gv->currStep->paramVal[GP_PWD_137], 1, 169, 3); |
| 260 | ar5212ModifyRfBuffer(priv->Bank6Data, |
| 261 | gv->currStep->paramVal[GP_PWD_136], 1, 170, 3); |
| 262 | ar5212ModifyRfBuffer(priv->Bank6Data, |
| 263 | gv->currStep->paramVal[GP_PWD_132], 1, 174, 3); |
| 264 | ar5212ModifyRfBuffer(priv->Bank6Data, |
| 265 | gv->currStep->paramVal[GP_PWD_131], 1, 175, 3); |
| 266 | ar5212ModifyRfBuffer(priv->Bank6Data, |
| 267 | gv->currStep->paramVal[GP_PWD_130], 1, 176, 3); |
| 268 | } |
| 269 | |
| 270 | /* Only the 5 or 2 GHz OB/DB need to be set for a mode */ |
| 271 | if (IS_CHAN_2GHZ(chan)) { |
| 272 | ar5212ModifyRfBuffer(priv->Bank6Data, ob2GHz, 3, 287, 0); |
| 273 | ar5212ModifyRfBuffer(priv->Bank6Data, db2GHz, 3, 290, 0); |
| 274 | } else { |
| 275 | ar5212ModifyRfBuffer(priv->Bank6Data, ob5GHz, 3, 279, 0); |
| 276 | ar5212ModifyRfBuffer(priv->Bank6Data, db5GHz, 3, 282, 0); |
| 277 | } |
| 278 | |
| 279 | /* Lower synth voltage for X112 Rev 2.0 only */ |
| 280 | if (IS_RADX112_REV2(ah)) { |
| 281 | /* Non-Reversed analyg registers - so values are pre-reversed */ |
| 282 | ar5212ModifyRfBuffer(priv->Bank6Data, 2, 2, 90, 2); |
| 283 | ar5212ModifyRfBuffer(priv->Bank6Data, 2, 2, 92, 2); |
| 284 | ar5212ModifyRfBuffer(priv->Bank6Data, 2, 2, 94, 2); |
| 285 | ar5212ModifyRfBuffer(priv->Bank6Data, 2, 1, 254, 2); |
| 286 | } |
| 287 | |
| 288 | /* Decrease Power Consumption for 5312/5213 and up */ |
| 289 | if (AH_PRIVATE(ah)->ah_phyRev >= AR_PHY_CHIP_ID_REV_2) { |
| 290 | ar5212ModifyRfBuffer(priv->Bank6Data, 1, 1, 281, 1); |
| 291 | ar5212ModifyRfBuffer(priv->Bank6Data, 1, 2, 1, 3); |
| 292 | ar5212ModifyRfBuffer(priv->Bank6Data, 1, 2, 3, 3); |
| 293 | ar5212ModifyRfBuffer(priv->Bank6Data, 1, 1, 139, 3); |
| 294 | ar5212ModifyRfBuffer(priv->Bank6Data, 1, 1, 140, 3); |
| 295 | } |
| 296 | |
| 297 | /* Setup Bank 7 Setup */ |
| 298 | RF_BANK_SETUP(priv, 7, modesIndex); |
| 299 | if (IS_CHAN_OFDM(chan)) |
| 300 | ar5212ModifyRfBuffer(priv->Bank7Data, |
| 301 | gv->currStep->paramVal[GP_MIXGAIN_OVR], 2, 37, 0); |
| 302 | |
| 303 | ar5212ModifyRfBuffer(priv->Bank7Data, gainI, 6, 14, 0); |
| 304 | |
| 305 | /* Adjust params for Derby TX power control */ |
| 306 | if (IS_CHAN_HALF_RATE(chan) || IS_CHAN_QUARTER_RATE(chan)) { |
| 307 | uint32_t rfDelay, rfPeriod; |
| 308 | |
| 309 | rfDelay = 0xf; |
| 310 | rfPeriod = (IS_CHAN_HALF_RATE(chan)) ? 0x8 : 0xf; |
| 311 | ar5212ModifyRfBuffer(priv->Bank7Data, rfDelay, 4, 58, 0); |
| 312 | ar5212ModifyRfBuffer(priv->Bank7Data, rfPeriod, 4, 70, 0); |
| 313 | } |
| 314 | |
| 315 | #ifdef notyet |
| 316 | /* Analog registers are setup - EAR can modify */ |
| 317 | if (ar5212IsEarEngaged(pDev, chan)) |
| 318 | uint32_t modifier; |
| 319 | ar5212EarModify(pDev, EAR_LC_RF_WRITE, chan, &modifier); |
| 320 | #endif |
| 321 | /* Write Analog registers */ |
| 322 | HAL_INI_WRITE_BANK(ah, ar5212Bank1_5112, priv->Bank1Data, regWrites); |
| 323 | HAL_INI_WRITE_BANK(ah, ar5212Bank2_5112, priv->Bank2Data, regWrites); |
| 324 | HAL_INI_WRITE_BANK(ah, ar5212Bank3_5112, priv->Bank3Data, regWrites); |
| 325 | HAL_INI_WRITE_BANK(ah, ar5212Bank6_5112, priv->Bank6Data, regWrites); |
| 326 | HAL_INI_WRITE_BANK(ah, ar5212Bank7_5112, priv->Bank7Data, regWrites); |
| 327 | |
| 328 | /* Now that we have reprogrammed rfgain value, clear the flag. */ |
| 329 | ahp->ah_rfgainState = HAL_RFGAIN_INACTIVE; |
| 330 | return AH_TRUE; |
| 331 | #undef RF_BANK_SETUP |
| 332 | } |
| 333 | |
| 334 | /* |
| 335 | * Read the transmit power levels from the structures taken from EEPROM |
| 336 | * Interpolate read transmit power values for this channel |
| 337 | * Organize the transmit power values into a table for writing into the hardware |
| 338 | */ |
| 339 | static HAL_BOOL |
| 340 | ar5112SetPowerTable(struct ath_hal *ah, |
| 341 | int16_t *pPowerMin, int16_t *pPowerMax, HAL_CHANNEL_INTERNAL *chan, |
| 342 | uint16_t *rfXpdGain) |
| 343 | { |
| 344 | struct ath_hal_5212 *ahp = AH5212(ah); |
| 345 | const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; |
| 346 | uint32_t numXpdGain = IS_RADX112_REV2(ah) ? 2 : 1; |
| 347 | uint32_t xpdGainMask = 0; |
| 348 | int16_t powerMid, *pPowerMid = &powerMid; |
| 349 | |
| 350 | const EXPN_DATA_PER_CHANNEL_5112 *pRawCh; |
| 351 | const EEPROM_POWER_EXPN_5112 *pPowerExpn = AH_NULL; |
| 352 | |
| 353 | uint32_t ii, jj, kk; |
| 354 | int16_t minPwr_t4, maxPwr_t4, Pmin, Pmid; |
| 355 | |
| 356 | uint32_t chan_idx_L = 0, chan_idx_R = 0; |
| 357 | uint16_t chan_L, chan_R; |
| 358 | |
| 359 | int16_t pwr_table0[64]; |
| 360 | int16_t pwr_table1[64]; |
| 361 | uint16_t pcdacs[10]; |
| 362 | int16_t powers[10]; |
| 363 | uint16_t numPcd; |
| 364 | int16_t powTableLXPD[2][64]; |
| 365 | int16_t powTableHXPD[2][64]; |
| 366 | int16_t tmpPowerTable[64]; |
| 367 | uint16_t xgainList[2]; |
| 368 | uint16_t xpdMask; |
| 369 | |
| 370 | switch (chan->channelFlags & CHANNEL_ALL) { |
| 371 | case CHANNEL_A: |
| 372 | case CHANNEL_T: |
| 373 | pPowerExpn = &ee->ee_modePowerArray5112[headerInfo11A]; |
| 374 | xpdGainMask = ee->ee_xgain[headerInfo11A]; |
| 375 | break; |
| 376 | case CHANNEL_B: |
| 377 | pPowerExpn = &ee->ee_modePowerArray5112[headerInfo11B]; |
| 378 | xpdGainMask = ee->ee_xgain[headerInfo11B]; |
| 379 | break; |
| 380 | case CHANNEL_G: |
| 381 | case CHANNEL_108G: |
| 382 | pPowerExpn = &ee->ee_modePowerArray5112[headerInfo11G]; |
| 383 | xpdGainMask = ee->ee_xgain[headerInfo11G]; |
| 384 | break; |
| 385 | default: |
| 386 | HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unknown channel flags 0x%x\n" , |
| 387 | __func__, chan->channelFlags & CHANNEL_ALL); |
| 388 | return AH_FALSE; |
| 389 | } |
| 390 | |
| 391 | if ((xpdGainMask & pPowerExpn->xpdMask) < 1) { |
| 392 | HALDEBUG(ah, HAL_DEBUG_ANY, |
| 393 | "%s: desired xpdGainMask 0x%x not supported by " |
| 394 | "calibrated xpdMask 0x%x\n" , __func__, |
| 395 | xpdGainMask, pPowerExpn->xpdMask); |
| 396 | return AH_FALSE; |
| 397 | } |
| 398 | |
| 399 | maxPwr_t4 = (int16_t)(2*(*pPowerMax)); /* pwr_t2 -> pwr_t4 */ |
| 400 | minPwr_t4 = (int16_t)(2*(*pPowerMin)); /* pwr_t2 -> pwr_t4 */ |
| 401 | |
| 402 | xgainList[0] = 0xDEAD; |
| 403 | xgainList[1] = 0xDEAD; |
| 404 | |
| 405 | kk = 0; |
| 406 | xpdMask = pPowerExpn->xpdMask; |
| 407 | for (jj = 0; jj < NUM_XPD_PER_CHANNEL; jj++) { |
| 408 | if (((xpdMask >> jj) & 1) > 0) { |
| 409 | if (kk > 1) { |
| 410 | HALDEBUG(ah, HAL_DEBUG_ANY, |
| 411 | "A maximum of 2 xpdGains supported" |
| 412 | "in pExpnPower data\n" ); |
| 413 | return AH_FALSE; |
| 414 | } |
| 415 | xgainList[kk++] = (uint16_t)jj; |
| 416 | } |
| 417 | } |
| 418 | |
| 419 | ar5212GetLowerUpperIndex(chan->channel, &pPowerExpn->pChannels[0], |
| 420 | pPowerExpn->numChannels, &chan_idx_L, &chan_idx_R); |
| 421 | |
| 422 | kk = 0; |
| 423 | for (ii = chan_idx_L; ii <= chan_idx_R; ii++) { |
| 424 | pRawCh = &(pPowerExpn->pDataPerChannel[ii]); |
| 425 | if (xgainList[1] == 0xDEAD) { |
| 426 | jj = xgainList[0]; |
| 427 | numPcd = pRawCh->pDataPerXPD[jj].numPcdacs; |
| 428 | OS_MEMCPY(&pcdacs[0], &pRawCh->pDataPerXPD[jj].pcdac[0], |
| 429 | numPcd * sizeof(uint16_t)); |
| 430 | OS_MEMCPY(&powers[0], &pRawCh->pDataPerXPD[jj].pwr_t4[0], |
| 431 | numPcd * sizeof(int16_t)); |
| 432 | if (!getFullPwrTable(numPcd, &pcdacs[0], &powers[0], |
| 433 | pRawCh->maxPower_t4, &tmpPowerTable[0])) { |
| 434 | return AH_FALSE; |
| 435 | } |
| 436 | OS_MEMCPY(&powTableLXPD[kk][0], &tmpPowerTable[0], |
| 437 | 64*sizeof(int16_t)); |
| 438 | } else { |
| 439 | jj = xgainList[0]; |
| 440 | numPcd = pRawCh->pDataPerXPD[jj].numPcdacs; |
| 441 | OS_MEMCPY(&pcdacs[0], &pRawCh->pDataPerXPD[jj].pcdac[0], |
| 442 | numPcd*sizeof(uint16_t)); |
| 443 | OS_MEMCPY(&powers[0], |
| 444 | &pRawCh->pDataPerXPD[jj].pwr_t4[0], |
| 445 | numPcd*sizeof(int16_t)); |
| 446 | if (!getFullPwrTable(numPcd, &pcdacs[0], &powers[0], |
| 447 | pRawCh->maxPower_t4, &tmpPowerTable[0])) { |
| 448 | return AH_FALSE; |
| 449 | } |
| 450 | OS_MEMCPY(&powTableLXPD[kk][0], &tmpPowerTable[0], |
| 451 | 64 * sizeof(int16_t)); |
| 452 | |
| 453 | jj = xgainList[1]; |
| 454 | numPcd = pRawCh->pDataPerXPD[jj].numPcdacs; |
| 455 | OS_MEMCPY(&pcdacs[0], &pRawCh->pDataPerXPD[jj].pcdac[0], |
| 456 | numPcd * sizeof(uint16_t)); |
| 457 | OS_MEMCPY(&powers[0], |
| 458 | &pRawCh->pDataPerXPD[jj].pwr_t4[0], |
| 459 | numPcd * sizeof(int16_t)); |
| 460 | if (!getFullPwrTable(numPcd, &pcdacs[0], &powers[0], |
| 461 | pRawCh->maxPower_t4, &tmpPowerTable[0])) { |
| 462 | return AH_FALSE; |
| 463 | } |
| 464 | OS_MEMCPY(&powTableHXPD[kk][0], &tmpPowerTable[0], |
| 465 | 64 * sizeof(int16_t)); |
| 466 | } |
| 467 | kk++; |
| 468 | } |
| 469 | |
| 470 | chan_L = pPowerExpn->pChannels[chan_idx_L]; |
| 471 | chan_R = pPowerExpn->pChannels[chan_idx_R]; |
| 472 | kk = chan_idx_R - chan_idx_L; |
| 473 | |
| 474 | if (xgainList[1] == 0xDEAD) { |
| 475 | for (jj = 0; jj < 64; jj++) { |
| 476 | pwr_table0[jj] = interpolate_signed( |
| 477 | chan->channel, chan_L, chan_R, |
| 478 | powTableLXPD[0][jj], powTableLXPD[kk][jj]); |
| 479 | } |
| 480 | Pmin = getPminAndPcdacTableFromPowerTable(&pwr_table0[0], |
| 481 | ahp->ah_pcdacTable); |
| 482 | *pPowerMin = (int16_t) (Pmin / 2); |
| 483 | *pPowerMid = (int16_t) (pwr_table0[63] / 2); |
| 484 | *pPowerMax = (int16_t) (pwr_table0[63] / 2); |
| 485 | rfXpdGain[0] = xgainList[0]; |
| 486 | rfXpdGain[1] = rfXpdGain[0]; |
| 487 | } else { |
| 488 | for (jj = 0; jj < 64; jj++) { |
| 489 | pwr_table0[jj] = interpolate_signed( |
| 490 | chan->channel, chan_L, chan_R, |
| 491 | powTableLXPD[0][jj], powTableLXPD[kk][jj]); |
| 492 | pwr_table1[jj] = interpolate_signed( |
| 493 | chan->channel, chan_L, chan_R, |
| 494 | powTableHXPD[0][jj], powTableHXPD[kk][jj]); |
| 495 | } |
| 496 | if (numXpdGain == 2) { |
| 497 | Pmin = getPminAndPcdacTableFromTwoPowerTables( |
| 498 | &pwr_table0[0], &pwr_table1[0], |
| 499 | ahp->ah_pcdacTable, &Pmid); |
| 500 | *pPowerMin = (int16_t) (Pmin / 2); |
| 501 | *pPowerMid = (int16_t) (Pmid / 2); |
| 502 | *pPowerMax = (int16_t) (pwr_table0[63] / 2); |
| 503 | rfXpdGain[0] = xgainList[0]; |
| 504 | rfXpdGain[1] = xgainList[1]; |
| 505 | } else if (minPwr_t4 <= pwr_table1[63] && |
| 506 | maxPwr_t4 <= pwr_table1[63]) { |
| 507 | Pmin = getPminAndPcdacTableFromPowerTable( |
| 508 | &pwr_table1[0], ahp->ah_pcdacTable); |
| 509 | rfXpdGain[0] = xgainList[1]; |
| 510 | rfXpdGain[1] = rfXpdGain[0]; |
| 511 | *pPowerMin = (int16_t) (Pmin / 2); |
| 512 | *pPowerMid = (int16_t) (pwr_table1[63] / 2); |
| 513 | *pPowerMax = (int16_t) (pwr_table1[63] / 2); |
| 514 | } else { |
| 515 | Pmin = getPminAndPcdacTableFromPowerTable( |
| 516 | &pwr_table0[0], ahp->ah_pcdacTable); |
| 517 | rfXpdGain[0] = xgainList[0]; |
| 518 | rfXpdGain[1] = rfXpdGain[0]; |
| 519 | *pPowerMin = (int16_t) (Pmin/2); |
| 520 | *pPowerMid = (int16_t) (pwr_table0[63] / 2); |
| 521 | *pPowerMax = (int16_t) (pwr_table0[63] / 2); |
| 522 | } |
| 523 | } |
| 524 | |
| 525 | /* |
| 526 | * Move 5112 rates to match power tables where the max |
| 527 | * power table entry corresponds with maxPower. |
| 528 | */ |
| 529 | HALASSERT(*pPowerMax <= PCDAC_STOP); |
| 530 | ahp->ah_txPowerIndexOffset = PCDAC_STOP - *pPowerMax; |
| 531 | |
| 532 | return AH_TRUE; |
| 533 | } |
| 534 | |
| 535 | /* |
| 536 | * Returns interpolated or the scaled up interpolated value |
| 537 | */ |
| 538 | static int16_t |
| 539 | interpolate_signed(uint16_t target, uint16_t srcLeft, uint16_t srcRight, |
| 540 | int16_t targetLeft, int16_t targetRight) |
| 541 | { |
| 542 | int16_t rv; |
| 543 | |
| 544 | if (srcRight != srcLeft) { |
| 545 | rv = ((target - srcLeft)*targetRight + |
| 546 | (srcRight - target)*targetLeft) / (srcRight - srcLeft); |
| 547 | } else { |
| 548 | rv = targetLeft; |
| 549 | } |
| 550 | return rv; |
| 551 | } |
| 552 | |
| 553 | /* |
| 554 | * Return indices surrounding the value in sorted integer lists. |
| 555 | * |
| 556 | * NB: the input list is assumed to be sorted in ascending order |
| 557 | */ |
| 558 | static void |
| 559 | ar5212GetLowerUpperIndex(uint16_t v, uint16_t *lp, uint16_t listSize, |
| 560 | uint32_t *vlo, uint32_t *vhi) |
| 561 | { |
| 562 | uint32_t target = v; |
| 563 | uint16_t *ep = lp+listSize; |
| 564 | uint16_t *tp; |
| 565 | |
| 566 | /* |
| 567 | * Check first and last elements for out-of-bounds conditions. |
| 568 | */ |
| 569 | if (target < lp[0]) { |
| 570 | *vlo = *vhi = 0; |
| 571 | return; |
| 572 | } |
| 573 | if (target >= ep[-1]) { |
| 574 | *vlo = *vhi = listSize - 1; |
| 575 | return; |
| 576 | } |
| 577 | |
| 578 | /* look for value being near or between 2 values in list */ |
| 579 | for (tp = lp; tp < ep; tp++) { |
| 580 | /* |
| 581 | * If value is close to the current value of the list |
| 582 | * then target is not between values, it is one of the values |
| 583 | */ |
| 584 | if (*tp == target) { |
| 585 | *vlo = *vhi = tp - lp; |
| 586 | return; |
| 587 | } |
| 588 | /* |
| 589 | * Look for value being between current value and next value |
| 590 | * if so return these 2 values |
| 591 | */ |
| 592 | if (target < tp[1]) { |
| 593 | *vlo = tp - lp; |
| 594 | *vhi = *vlo + 1; |
| 595 | return; |
| 596 | } |
| 597 | } |
| 598 | } |
| 599 | |
| 600 | static HAL_BOOL |
| 601 | getFullPwrTable(uint16_t numPcdacs, uint16_t *pcdacs, int16_t *power, int16_t maxPower, int16_t *retVals) |
| 602 | { |
| 603 | uint16_t ii; |
| 604 | uint16_t idxL = 0; |
| 605 | uint16_t idxR = 1; |
| 606 | |
| 607 | if (numPcdacs < 2) { |
| 608 | HALDEBUG(AH_NULL, HAL_DEBUG_ANY, |
| 609 | "%s: at least 2 pcdac values needed [%d]\n" , |
| 610 | __func__, numPcdacs); |
| 611 | return AH_FALSE; |
| 612 | } |
| 613 | for (ii = 0; ii < 64; ii++) { |
| 614 | if (ii>pcdacs[idxR] && idxR < numPcdacs-1) { |
| 615 | idxL++; |
| 616 | idxR++; |
| 617 | } |
| 618 | retVals[ii] = interpolate_signed(ii, |
| 619 | pcdacs[idxL], pcdacs[idxR], power[idxL], power[idxR]); |
| 620 | if (retVals[ii] >= maxPower) { |
| 621 | while (ii < 64) |
| 622 | retVals[ii++] = maxPower; |
| 623 | } |
| 624 | } |
| 625 | return AH_TRUE; |
| 626 | } |
| 627 | |
| 628 | /* |
| 629 | * Takes a single calibration curve and creates a power table. |
| 630 | * Adjusts the new power table so the max power is relative |
| 631 | * to the maximum index in the power table. |
| 632 | * |
| 633 | * WARNING: rates must be adjusted for this relative power table |
| 634 | */ |
| 635 | static int16_t |
| 636 | getPminAndPcdacTableFromPowerTable(int16_t *pwrTableT4, uint16_t retVals[]) |
| 637 | { |
| 638 | int16_t ii, jj, jjMax; |
| 639 | int16_t pMin, currPower, pMax; |
| 640 | |
| 641 | /* If the spread is > 31.5dB, keep the upper 31.5dB range */ |
| 642 | if ((pwrTableT4[63] - pwrTableT4[0]) > 126) { |
| 643 | pMin = pwrTableT4[63] - 126; |
| 644 | } else { |
| 645 | pMin = pwrTableT4[0]; |
| 646 | } |
| 647 | |
| 648 | pMax = pwrTableT4[63]; |
| 649 | jjMax = 63; |
| 650 | |
| 651 | /* Search for highest pcdac 0.25dB below maxPower */ |
| 652 | while ((pwrTableT4[jjMax] > (pMax - 1) ) && (jjMax >= 0)) { |
| 653 | jjMax--; |
| 654 | } |
| 655 | |
| 656 | jj = jjMax; |
| 657 | currPower = pMax; |
| 658 | for (ii = 63; ii >= 0; ii--) { |
| 659 | while ((jj < 64) && (jj > 0) && (pwrTableT4[jj] >= currPower)) { |
| 660 | jj--; |
| 661 | } |
| 662 | if (jj == 0) { |
| 663 | while (ii >= 0) { |
| 664 | retVals[ii] = retVals[ii + 1]; |
| 665 | ii--; |
| 666 | } |
| 667 | break; |
| 668 | } |
| 669 | retVals[ii] = jj; |
| 670 | currPower -= 2; // corresponds to a 0.5dB step |
| 671 | } |
| 672 | return pMin; |
| 673 | } |
| 674 | |
| 675 | /* |
| 676 | * Combines the XPD curves from two calibration sets into a single |
| 677 | * power table and adjusts the power table so the max power is relative |
| 678 | * to the maximum index in the power table |
| 679 | * |
| 680 | * WARNING: rates must be adjusted for this relative power table |
| 681 | */ |
| 682 | static int16_t |
| 683 | getPminAndPcdacTableFromTwoPowerTables(int16_t *pwrTableLXpdT4, |
| 684 | int16_t *pwrTableHXpdT4, uint16_t retVals[], int16_t *pMid) |
| 685 | { |
| 686 | int16_t ii, jj, jjMax; |
| 687 | int16_t pMin, pMax, currPower; |
| 688 | int16_t *pwrTableT4; |
| 689 | uint16_t msbFlag = 0x40; // turns on the 7th bit of the pcdac |
| 690 | |
| 691 | /* If the spread is > 31.5dB, keep the upper 31.5dB range */ |
| 692 | if ((pwrTableLXpdT4[63] - pwrTableHXpdT4[0]) > 126) { |
| 693 | pMin = pwrTableLXpdT4[63] - 126; |
| 694 | } else { |
| 695 | pMin = pwrTableHXpdT4[0]; |
| 696 | } |
| 697 | |
| 698 | pMax = pwrTableLXpdT4[63]; |
| 699 | jjMax = 63; |
| 700 | /* Search for highest pcdac 0.25dB below maxPower */ |
| 701 | while ((pwrTableLXpdT4[jjMax] > (pMax - 1) ) && (jjMax >= 0)){ |
| 702 | jjMax--; |
| 703 | } |
| 704 | |
| 705 | *pMid = pwrTableHXpdT4[63]; |
| 706 | jj = jjMax; |
| 707 | ii = 63; |
| 708 | currPower = pMax; |
| 709 | pwrTableT4 = &(pwrTableLXpdT4[0]); |
| 710 | while (ii >= 0) { |
| 711 | if ((currPower <= *pMid) || ( (jj == 0) && (msbFlag == 0x40))){ |
| 712 | msbFlag = 0x00; |
| 713 | pwrTableT4 = &(pwrTableHXpdT4[0]); |
| 714 | jj = 63; |
| 715 | } |
| 716 | while ((jj > 0) && (pwrTableT4[jj] >= currPower)) { |
| 717 | jj--; |
| 718 | } |
| 719 | if ((jj == 0) && (msbFlag == 0x00)) { |
| 720 | while (ii >= 0) { |
| 721 | retVals[ii] = retVals[ii+1]; |
| 722 | ii--; |
| 723 | } |
| 724 | break; |
| 725 | } |
| 726 | retVals[ii] = jj | msbFlag; |
| 727 | currPower -= 2; // corresponds to a 0.5dB step |
| 728 | ii--; |
| 729 | } |
| 730 | return pMin; |
| 731 | } |
| 732 | |
| 733 | static int16_t |
| 734 | ar5112GetMinPower(struct ath_hal *ah, const EXPN_DATA_PER_CHANNEL_5112 *data) |
| 735 | { |
| 736 | int i, minIndex; |
| 737 | int16_t minGain,minPwr,minPcdac,retVal; |
| 738 | |
| 739 | /* Assume NUM_POINTS_XPD0 > 0 */ |
| 740 | minGain = data->pDataPerXPD[0].xpd_gain; |
| 741 | for (minIndex=0,i=1; i<NUM_XPD_PER_CHANNEL; i++) { |
| 742 | if (data->pDataPerXPD[i].xpd_gain < minGain) { |
| 743 | minIndex = i; |
| 744 | minGain = data->pDataPerXPD[i].xpd_gain; |
| 745 | } |
| 746 | } |
| 747 | minPwr = data->pDataPerXPD[minIndex].pwr_t4[0]; |
| 748 | minPcdac = data->pDataPerXPD[minIndex].pcdac[0]; |
| 749 | for (i=1; i<NUM_POINTS_XPD0; i++) { |
| 750 | if (data->pDataPerXPD[minIndex].pwr_t4[i] < minPwr) { |
| 751 | minPwr = data->pDataPerXPD[minIndex].pwr_t4[i]; |
| 752 | minPcdac = data->pDataPerXPD[minIndex].pcdac[i]; |
| 753 | } |
| 754 | } |
| 755 | retVal = minPwr - (minPcdac*2); |
| 756 | return(retVal); |
| 757 | } |
| 758 | |
| 759 | static HAL_BOOL |
| 760 | ar5112GetChannelMaxMinPower(struct ath_hal *ah, HAL_CHANNEL *chan, |
| 761 | int16_t *maxPow, int16_t *minPow) |
| 762 | { |
| 763 | const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; |
| 764 | int numChannels=0,i,last; |
| 765 | int totalD, totalF,totalMin; |
| 766 | const EXPN_DATA_PER_CHANNEL_5112 *data=AH_NULL; |
| 767 | const EEPROM_POWER_EXPN_5112 *powerArray=AH_NULL; |
| 768 | |
| 769 | *maxPow = 0; |
| 770 | if (IS_CHAN_A(chan)) { |
| 771 | powerArray = ee->ee_modePowerArray5112; |
| 772 | data = powerArray[headerInfo11A].pDataPerChannel; |
| 773 | numChannels = powerArray[headerInfo11A].numChannels; |
| 774 | } else if (IS_CHAN_G(chan) || IS_CHAN_108G(chan)) { |
| 775 | /* XXX - is this correct? Should we also use the same power for turbo G? */ |
| 776 | powerArray = ee->ee_modePowerArray5112; |
| 777 | data = powerArray[headerInfo11G].pDataPerChannel; |
| 778 | numChannels = powerArray[headerInfo11G].numChannels; |
| 779 | } else if (IS_CHAN_B(chan)) { |
| 780 | powerArray = ee->ee_modePowerArray5112; |
| 781 | data = powerArray[headerInfo11B].pDataPerChannel; |
| 782 | numChannels = powerArray[headerInfo11B].numChannels; |
| 783 | } else { |
| 784 | return (AH_TRUE); |
| 785 | } |
| 786 | /* Make sure the channel is in the range of the TP values |
| 787 | * (freq piers) |
| 788 | */ |
| 789 | if (numChannels < 1) |
| 790 | return(AH_FALSE); |
| 791 | |
| 792 | if ((chan->channel < data[0].channelValue) || |
| 793 | (chan->channel > data[numChannels-1].channelValue)) { |
| 794 | if (chan->channel < data[0].channelValue) { |
| 795 | *maxPow = data[0].maxPower_t4; |
| 796 | *minPow = ar5112GetMinPower(ah, &data[0]); |
| 797 | return(AH_TRUE); |
| 798 | } else { |
| 799 | *maxPow = data[numChannels - 1].maxPower_t4; |
| 800 | *minPow = ar5112GetMinPower(ah, &data[numChannels - 1]); |
| 801 | return(AH_TRUE); |
| 802 | } |
| 803 | } |
| 804 | |
| 805 | /* Linearly interpolate the power value now */ |
| 806 | for (last=0,i=0; |
| 807 | (i<numChannels) && (chan->channel > data[i].channelValue); |
| 808 | last=i++); |
| 809 | totalD = data[i].channelValue - data[last].channelValue; |
| 810 | if (totalD > 0) { |
| 811 | totalF = data[i].maxPower_t4 - data[last].maxPower_t4; |
| 812 | *maxPow = (int8_t) ((totalF*(chan->channel-data[last].channelValue) + data[last].maxPower_t4*totalD)/totalD); |
| 813 | |
| 814 | totalMin = ar5112GetMinPower(ah,&data[i]) - ar5112GetMinPower(ah, &data[last]); |
| 815 | *minPow = (int8_t) ((totalMin*(chan->channel-data[last].channelValue) + ar5112GetMinPower(ah, &data[last])*totalD)/totalD); |
| 816 | return (AH_TRUE); |
| 817 | } else { |
| 818 | if (chan->channel == data[i].channelValue) { |
| 819 | *maxPow = data[i].maxPower_t4; |
| 820 | *minPow = ar5112GetMinPower(ah, &data[i]); |
| 821 | return(AH_TRUE); |
| 822 | } else |
| 823 | return(AH_FALSE); |
| 824 | } |
| 825 | } |
| 826 | |
| 827 | /* |
| 828 | * Free memory for analog bank scratch buffers |
| 829 | */ |
| 830 | static void |
| 831 | ar5112RfDetach(struct ath_hal *ah) |
| 832 | { |
| 833 | struct ath_hal_5212 *ahp = AH5212(ah); |
| 834 | |
| 835 | HALASSERT(ahp->ah_rfHal != AH_NULL); |
| 836 | ath_hal_free(ahp->ah_rfHal); |
| 837 | ahp->ah_rfHal = AH_NULL; |
| 838 | } |
| 839 | |
| 840 | /* |
| 841 | * Allocate memory for analog bank scratch buffers |
| 842 | * Scratch Buffer will be reinitialized every reset so no need to zero now |
| 843 | */ |
| 844 | static HAL_BOOL |
| 845 | ar5112RfAttach(struct ath_hal *ah, HAL_STATUS *status) |
| 846 | { |
| 847 | struct ath_hal_5212 *ahp = AH5212(ah); |
| 848 | struct ar5112State *priv; |
| 849 | |
| 850 | HALASSERT(ah->ah_magic == AR5212_MAGIC); |
| 851 | |
| 852 | HALASSERT(ahp->ah_rfHal == AH_NULL); |
| 853 | priv = ath_hal_malloc(sizeof(struct ar5112State)); |
| 854 | if (priv == AH_NULL) { |
| 855 | HALDEBUG(ah, HAL_DEBUG_ANY, |
| 856 | "%s: cannot allocate private state\n" , __func__); |
| 857 | *status = HAL_ENOMEM; /* XXX */ |
| 858 | return AH_FALSE; |
| 859 | } |
| 860 | priv->base.rfDetach = ar5112RfDetach; |
| 861 | priv->base.writeRegs = ar5112WriteRegs; |
| 862 | priv->base.getRfBank = ar5112GetRfBank; |
| 863 | priv->base.setChannel = ar5112SetChannel; |
| 864 | priv->base.setRfRegs = ar5112SetRfRegs; |
| 865 | priv->base.setPowerTable = ar5112SetPowerTable; |
| 866 | priv->base.getChannelMaxMinPower = ar5112GetChannelMaxMinPower; |
| 867 | priv->base.getNfAdjust = ar5212GetNfAdjust; |
| 868 | |
| 869 | ahp->ah_pcdacTable = priv->pcdacTable; |
| 870 | ahp->ah_pcdacTableSize = sizeof(priv->pcdacTable); |
| 871 | ahp->ah_rfHal = &priv->base; |
| 872 | |
| 873 | return AH_TRUE; |
| 874 | } |
| 875 | |
| 876 | static HAL_BOOL |
| 877 | ar5112Probe(struct ath_hal *ah) |
| 878 | { |
| 879 | return IS_RAD5112(ah); |
| 880 | } |
| 881 | AH_RF(RF5112, ar5112Probe, ar5112RfAttach); |
| 882 | |