ar5212_rfgain.c revision 185406
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: ar5212_rfgain.c,v 1.2 2008/11/19 21:23:01 sam Exp $ 18 */ 19#include "opt_ah.h" 20 21#include "ah.h" 22#include "ah_internal.h" 23#include "ah_devid.h" 24 25#include "ar5212/ar5212.h" 26#include "ar5212/ar5212reg.h" 27#include "ar5212/ar5212phy.h" 28 29#include "ah_eeprom_v3.h" 30 31static const GAIN_OPTIMIZATION_LADDER gainLadder = { 32 9, /* numStepsInLadder */ 33 4, /* defaultStepNum */ 34 { { {4, 1, 1, 1}, 6, "FG8"}, 35 { {4, 0, 1, 1}, 4, "FG7"}, 36 { {3, 1, 1, 1}, 3, "FG6"}, 37 { {4, 0, 0, 1}, 1, "FG5"}, 38 { {4, 1, 1, 0}, 0, "FG4"}, /* noJack */ 39 { {4, 0, 1, 0}, -2, "FG3"}, /* halfJack */ 40 { {3, 1, 1, 0}, -3, "FG2"}, /* clip3 */ 41 { {4, 0, 0, 0}, -4, "FG1"}, /* noJack */ 42 { {2, 1, 1, 0}, -6, "FG0"} /* clip2 */ 43 } 44}; 45 46static const GAIN_OPTIMIZATION_LADDER gainLadder5112 = { 47 8, /* numStepsInLadder */ 48 1, /* defaultStepNum */ 49 { { {3, 0,0,0, 0,0,0}, 6, "FG7"}, /* most fixed gain */ 50 { {2, 0,0,0, 0,0,0}, 0, "FG6"}, 51 { {1, 0,0,0, 0,0,0}, -3, "FG5"}, 52 { {0, 0,0,0, 0,0,0}, -6, "FG4"}, 53 { {0, 1,1,0, 0,0,0}, -8, "FG3"}, 54 { {0, 1,1,0, 1,1,0}, -10, "FG2"}, 55 { {0, 1,0,1, 1,1,0}, -13, "FG1"}, 56 { {0, 1,0,1, 1,0,1}, -16, "FG0"}, /* least fixed gain */ 57 } 58}; 59 60/* 61 * Initialize the gain structure to good values 62 */ 63void 64ar5212InitializeGainValues(struct ath_hal *ah) 65{ 66 struct ath_hal_5212 *ahp = AH5212(ah); 67 GAIN_VALUES *gv = &ahp->ah_gainValues; 68 69 /* initialize gain optimization values */ 70 if (IS_RAD5112_ANY(ah)) { 71 gv->currStepNum = gainLadder5112.defaultStepNum; 72 gv->currStep = 73 &gainLadder5112.optStep[gainLadder5112.defaultStepNum]; 74 gv->active = AH_TRUE; 75 gv->loTrig = 20; 76 gv->hiTrig = 85; 77 } else { 78 gv->currStepNum = gainLadder.defaultStepNum; 79 gv->currStep = &gainLadder.optStep[gainLadder.defaultStepNum]; 80 gv->active = AH_TRUE; 81 gv->loTrig = 20; 82 gv->hiTrig = 35; 83 } 84} 85 86#define MAX_ANALOG_START 319 /* XXX */ 87 88/* 89 * Find analog bits of given parameter data and return a reversed value 90 */ 91static uint32_t 92ar5212GetRfField(uint32_t *rfBuf, uint32_t numBits, uint32_t firstBit, uint32_t column) 93{ 94 uint32_t reg32 = 0, mask, arrayEntry, lastBit; 95 uint32_t bitPosition, bitsShifted; 96 int32_t bitsLeft; 97 98 HALASSERT(column <= 3); 99 HALASSERT(numBits <= 32); 100 HALASSERT(firstBit + numBits <= MAX_ANALOG_START); 101 102 arrayEntry = (firstBit - 1) / 8; 103 bitPosition = (firstBit - 1) % 8; 104 bitsLeft = numBits; 105 bitsShifted = 0; 106 while (bitsLeft > 0) { 107 lastBit = (bitPosition + bitsLeft > 8) ? 108 (8) : (bitPosition + bitsLeft); 109 mask = (((1 << lastBit) - 1) ^ ((1 << bitPosition) - 1)) << 110 (column * 8); 111 reg32 |= (((rfBuf[arrayEntry] & mask) >> (column * 8)) >> 112 bitPosition) << bitsShifted; 113 bitsShifted += lastBit - bitPosition; 114 bitsLeft -= (8 - bitPosition); 115 bitPosition = 0; 116 arrayEntry++; 117 } 118 reg32 = ath_hal_reverseBits(reg32, numBits); 119 return reg32; 120} 121 122static HAL_BOOL 123ar5212InvalidGainReadback(struct ath_hal *ah, GAIN_VALUES *gv) 124{ 125 uint32_t gStep, g, mixOvr; 126 uint32_t L1, L2, L3, L4; 127 128 if (IS_RAD5112_ANY(ah)) { 129 mixOvr = ar5212GetRfField(ar5212GetRfBank(ah, 7), 1, 36, 0); 130 L1 = 0; 131 L2 = 107; 132 L3 = 0; 133 L4 = 107; 134 if (mixOvr == 1) { 135 L2 = 83; 136 L4 = 83; 137 gv->hiTrig = 55; 138 } 139 } else { 140 gStep = ar5212GetRfField(ar5212GetRfBank(ah, 7), 6, 37, 0); 141 142 L1 = 0; 143 L2 = (gStep == 0x3f) ? 50 : gStep + 4; 144 L3 = (gStep != 0x3f) ? 0x40 : L1; 145 L4 = L3 + 50; 146 147 gv->loTrig = L1 + (gStep == 0x3f ? DYN_ADJ_LO_MARGIN : 0); 148 /* never adjust if != 0x3f */ 149 gv->hiTrig = L4 - (gStep == 0x3f ? DYN_ADJ_UP_MARGIN : -5); 150 } 151 g = gv->currGain; 152 153 return !((g >= L1 && g<= L2) || (g >= L3 && g <= L4)); 154} 155 156/* 157 * Enable the probe gain check on the next packet 158 */ 159void 160ar5212RequestRfgain(struct ath_hal *ah) 161{ 162 struct ath_hal_5212 *ahp = AH5212(ah); 163 uint32_t probePowerIndex; 164 165 /* Enable the gain readback probe */ 166 probePowerIndex = ahp->ah_ofdmTxPower + ahp->ah_txPowerIndexOffset; 167 OS_REG_WRITE(ah, AR_PHY_PAPD_PROBE, 168 SM(probePowerIndex, AR_PHY_PAPD_PROBE_POWERTX) 169 | AR_PHY_PAPD_PROBE_NEXT_TX); 170 171 ahp->ah_rfgainState = HAL_RFGAIN_READ_REQUESTED; 172} 173 174/* 175 * Check to see if our readback gain level sits within the linear 176 * region of our current variable attenuation window 177 */ 178static HAL_BOOL 179ar5212IsGainAdjustNeeded(struct ath_hal *ah, const GAIN_VALUES *gv) 180{ 181 return (gv->currGain <= gv->loTrig || gv->currGain >= gv->hiTrig); 182} 183 184/* 185 * Move the rabbit ears in the correct direction. 186 */ 187static int32_t 188ar5212AdjustGain(struct ath_hal *ah, GAIN_VALUES *gv) 189{ 190 const GAIN_OPTIMIZATION_LADDER *gl; 191 192 if (IS_RAD5112_ANY(ah)) 193 gl = &gainLadder5112; 194 else 195 gl = &gainLadder; 196 gv->currStep = &gl->optStep[gv->currStepNum]; 197 if (gv->currGain >= gv->hiTrig) { 198 if (gv->currStepNum == 0) { 199 HALDEBUG(ah, HAL_DEBUG_ANY, "%s: Max gain limit.\n", 200 __func__); 201 return -1; 202 } 203 HALDEBUG(ah, HAL_DEBUG_RFPARAM, 204 "%s: Adding gain: currG=%d [%s] --> ", 205 __func__, gv->currGain, gv->currStep->stepName); 206 gv->targetGain = gv->currGain; 207 while (gv->targetGain >= gv->hiTrig && gv->currStepNum > 0) { 208 gv->targetGain -= 2 * (gl->optStep[--(gv->currStepNum)].stepGain - 209 gv->currStep->stepGain); 210 gv->currStep = &gl->optStep[gv->currStepNum]; 211 } 212 HALDEBUG(ah, HAL_DEBUG_RFPARAM, "targG=%d [%s]\n", 213 gv->targetGain, gv->currStep->stepName); 214 return 1; 215 } 216 if (gv->currGain <= gv->loTrig) { 217 if (gv->currStepNum == gl->numStepsInLadder-1) { 218 HALDEBUG(ah, HAL_DEBUG_RFPARAM, 219 "%s: Min gain limit.\n", __func__); 220 return -2; 221 } 222 HALDEBUG(ah, HAL_DEBUG_RFPARAM, 223 "%s: Deducting gain: currG=%d [%s] --> ", 224 __func__, gv->currGain, gv->currStep->stepName); 225 gv->targetGain = gv->currGain; 226 while (gv->targetGain <= gv->loTrig && 227 gv->currStepNum < (gl->numStepsInLadder - 1)) { 228 gv->targetGain -= 2 * 229 (gl->optStep[++(gv->currStepNum)].stepGain - gv->currStep->stepGain); 230 gv->currStep = &gl->optStep[gv->currStepNum]; 231 } 232 HALDEBUG(ah, HAL_DEBUG_RFPARAM, "targG=%d [%s]\n", 233 gv->targetGain, gv->currStep->stepName); 234 return 2; 235 } 236 return 0; /* caller didn't call needAdjGain first */ 237} 238 239/* 240 * Read rf register to determine if gainF needs correction 241 */ 242static void 243ar5212GetGainFCorrection(struct ath_hal *ah) 244{ 245 struct ath_hal_5212 *ahp = AH5212(ah); 246 GAIN_VALUES *gv = &ahp->ah_gainValues; 247 248 HALASSERT(IS_RADX112_REV2(ah)); 249 250 gv->gainFCorrection = 0; 251 if (ar5212GetRfField(ar5212GetRfBank(ah, 7), 1, 36, 0) == 1) { 252 uint32_t mixGain = gv->currStep->paramVal[0]; 253 uint32_t gainStep = 254 ar5212GetRfField(ar5212GetRfBank(ah, 7), 4, 32, 0); 255 switch (mixGain) { 256 case 0 : 257 gv->gainFCorrection = 0; 258 break; 259 case 1 : 260 gv->gainFCorrection = gainStep; 261 break; 262 case 2 : 263 gv->gainFCorrection = 2 * gainStep - 5; 264 break; 265 case 3 : 266 gv->gainFCorrection = 2 * gainStep; 267 break; 268 } 269 } 270} 271 272/* 273 * Exported call to check for a recent gain reading and return 274 * the current state of the thermal calibration gain engine. 275 */ 276HAL_RFGAIN 277ar5212GetRfgain(struct ath_hal *ah) 278{ 279 struct ath_hal_5212 *ahp = AH5212(ah); 280 GAIN_VALUES *gv = &ahp->ah_gainValues; 281 uint32_t rddata, probeType; 282 283 if (!gv->active) 284 return HAL_RFGAIN_INACTIVE; 285 286 if (ahp->ah_rfgainState == HAL_RFGAIN_READ_REQUESTED) { 287 /* Caller had asked to setup a new reading. Check it. */ 288 rddata = OS_REG_READ(ah, AR_PHY_PAPD_PROBE); 289 290 if ((rddata & AR_PHY_PAPD_PROBE_NEXT_TX) == 0) { 291 /* bit got cleared, we have a new reading. */ 292 gv->currGain = rddata >> AR_PHY_PAPD_PROBE_GAINF_S; 293 probeType = MS(rddata, AR_PHY_PAPD_PROBE_TYPE); 294 if (probeType == AR_PHY_PAPD_PROBE_TYPE_CCK) { 295 const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; 296 297 HALASSERT(IS_RAD5112_ANY(ah)); 298 HALASSERT(ah->ah_magic == AR5212_MAGIC); 299 if (AH_PRIVATE(ah)->ah_phyRev >= AR_PHY_CHIP_ID_REV_2) 300 gv->currGain += ee->ee_cckOfdmGainDelta; 301 else 302 gv->currGain += PHY_PROBE_CCK_CORRECTION; 303 } 304 if (IS_RADX112_REV2(ah)) { 305 ar5212GetGainFCorrection(ah); 306 if (gv->currGain >= gv->gainFCorrection) 307 gv->currGain -= gv->gainFCorrection; 308 else 309 gv->currGain = 0; 310 } 311 /* inactive by default */ 312 ahp->ah_rfgainState = HAL_RFGAIN_INACTIVE; 313 314 if (!ar5212InvalidGainReadback(ah, gv) && 315 ar5212IsGainAdjustNeeded(ah, gv) && 316 ar5212AdjustGain(ah, gv) > 0) { 317 /* 318 * Change needed. Copy ladder info 319 * into eeprom info. 320 */ 321 ahp->ah_rfgainState = HAL_RFGAIN_NEED_CHANGE; 322 /* for ap51 */ 323 ahp->ah_cwCalRequire = AH_TRUE; 324 /* Request IQ recalibration for temperature chang */ 325 ahp->ah_bIQCalibration = IQ_CAL_INACTIVE; 326 } 327 } 328 } 329 return ahp->ah_rfgainState; 330} 331