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