1185380Ssam/* 2185380Ssam * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting 3185380Ssam * Copyright (c) 2002-2008 Atheros Communications, Inc. 4185380Ssam * 5185380Ssam * Permission to use, copy, modify, and/or distribute this software for any 6185380Ssam * purpose with or without fee is hereby granted, provided that the above 7185380Ssam * copyright notice and this permission notice appear in all copies. 8185380Ssam * 9185380Ssam * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10185380Ssam * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11185380Ssam * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12185380Ssam * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13185380Ssam * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14185380Ssam * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15185380Ssam * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16185380Ssam * 17187129Ssam * $FreeBSD$ 18185380Ssam */ 19185380Ssam#include "opt_ah.h" 20185380Ssam 21185380Ssam#include "ah.h" 22185380Ssam#include "ah_internal.h" 23185380Ssam#include "ah_devid.h" 24185380Ssam 25185380Ssam#include "ar5212/ar5212.h" 26185380Ssam#include "ar5212/ar5212reg.h" 27185380Ssam#include "ar5212/ar5212phy.h" 28185380Ssam 29185380Ssam#include "ah_eeprom_v3.h" 30185380Ssam 31185380Ssamstatic const GAIN_OPTIMIZATION_LADDER gainLadder = { 32185380Ssam 9, /* numStepsInLadder */ 33185380Ssam 4, /* defaultStepNum */ 34185380Ssam { { {4, 1, 1, 1}, 6, "FG8"}, 35185380Ssam { {4, 0, 1, 1}, 4, "FG7"}, 36185380Ssam { {3, 1, 1, 1}, 3, "FG6"}, 37185380Ssam { {4, 0, 0, 1}, 1, "FG5"}, 38185380Ssam { {4, 1, 1, 0}, 0, "FG4"}, /* noJack */ 39185380Ssam { {4, 0, 1, 0}, -2, "FG3"}, /* halfJack */ 40185380Ssam { {3, 1, 1, 0}, -3, "FG2"}, /* clip3 */ 41185380Ssam { {4, 0, 0, 0}, -4, "FG1"}, /* noJack */ 42185380Ssam { {2, 1, 1, 0}, -6, "FG0"} /* clip2 */ 43185380Ssam } 44185380Ssam}; 45185380Ssam 46185380Ssamstatic const GAIN_OPTIMIZATION_LADDER gainLadder5112 = { 47185380Ssam 8, /* numStepsInLadder */ 48185380Ssam 1, /* defaultStepNum */ 49185380Ssam { { {3, 0,0,0, 0,0,0}, 6, "FG7"}, /* most fixed gain */ 50185380Ssam { {2, 0,0,0, 0,0,0}, 0, "FG6"}, 51185380Ssam { {1, 0,0,0, 0,0,0}, -3, "FG5"}, 52185380Ssam { {0, 0,0,0, 0,0,0}, -6, "FG4"}, 53185380Ssam { {0, 1,1,0, 0,0,0}, -8, "FG3"}, 54185380Ssam { {0, 1,1,0, 1,1,0}, -10, "FG2"}, 55185380Ssam { {0, 1,0,1, 1,1,0}, -13, "FG1"}, 56185380Ssam { {0, 1,0,1, 1,0,1}, -16, "FG0"}, /* least fixed gain */ 57185380Ssam } 58185380Ssam}; 59185380Ssam 60185380Ssam/* 61185380Ssam * Initialize the gain structure to good values 62185380Ssam */ 63185380Ssamvoid 64185380Ssamar5212InitializeGainValues(struct ath_hal *ah) 65185380Ssam{ 66185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 67185380Ssam GAIN_VALUES *gv = &ahp->ah_gainValues; 68185380Ssam 69185380Ssam /* initialize gain optimization values */ 70185380Ssam if (IS_RAD5112_ANY(ah)) { 71185380Ssam gv->currStepNum = gainLadder5112.defaultStepNum; 72185380Ssam gv->currStep = 73185380Ssam &gainLadder5112.optStep[gainLadder5112.defaultStepNum]; 74185380Ssam gv->active = AH_TRUE; 75185380Ssam gv->loTrig = 20; 76185380Ssam gv->hiTrig = 85; 77185380Ssam } else { 78185380Ssam gv->currStepNum = gainLadder.defaultStepNum; 79185380Ssam gv->currStep = &gainLadder.optStep[gainLadder.defaultStepNum]; 80185380Ssam gv->active = AH_TRUE; 81185380Ssam gv->loTrig = 20; 82185380Ssam gv->hiTrig = 35; 83185380Ssam } 84185380Ssam} 85185380Ssam 86185380Ssam#define MAX_ANALOG_START 319 /* XXX */ 87185380Ssam 88185380Ssam/* 89185380Ssam * Find analog bits of given parameter data and return a reversed value 90185380Ssam */ 91185380Ssamstatic uint32_t 92185380Ssamar5212GetRfField(uint32_t *rfBuf, uint32_t numBits, uint32_t firstBit, uint32_t column) 93185380Ssam{ 94185380Ssam uint32_t reg32 = 0, mask, arrayEntry, lastBit; 95185380Ssam uint32_t bitPosition, bitsShifted; 96185380Ssam int32_t bitsLeft; 97185380Ssam 98185380Ssam HALASSERT(column <= 3); 99185380Ssam HALASSERT(numBits <= 32); 100185380Ssam HALASSERT(firstBit + numBits <= MAX_ANALOG_START); 101185380Ssam 102185380Ssam arrayEntry = (firstBit - 1) / 8; 103185380Ssam bitPosition = (firstBit - 1) % 8; 104185380Ssam bitsLeft = numBits; 105185380Ssam bitsShifted = 0; 106185380Ssam while (bitsLeft > 0) { 107185380Ssam lastBit = (bitPosition + bitsLeft > 8) ? 108185380Ssam (8) : (bitPosition + bitsLeft); 109185380Ssam mask = (((1 << lastBit) - 1) ^ ((1 << bitPosition) - 1)) << 110185380Ssam (column * 8); 111185380Ssam reg32 |= (((rfBuf[arrayEntry] & mask) >> (column * 8)) >> 112185380Ssam bitPosition) << bitsShifted; 113185380Ssam bitsShifted += lastBit - bitPosition; 114185380Ssam bitsLeft -= (8 - bitPosition); 115185380Ssam bitPosition = 0; 116185380Ssam arrayEntry++; 117185380Ssam } 118185380Ssam reg32 = ath_hal_reverseBits(reg32, numBits); 119185380Ssam return reg32; 120185380Ssam} 121185380Ssam 122185380Ssamstatic HAL_BOOL 123185380Ssamar5212InvalidGainReadback(struct ath_hal *ah, GAIN_VALUES *gv) 124185380Ssam{ 125185380Ssam uint32_t gStep, g, mixOvr; 126185380Ssam uint32_t L1, L2, L3, L4; 127185380Ssam 128185380Ssam if (IS_RAD5112_ANY(ah)) { 129185380Ssam mixOvr = ar5212GetRfField(ar5212GetRfBank(ah, 7), 1, 36, 0); 130185380Ssam L1 = 0; 131185380Ssam L2 = 107; 132185380Ssam L3 = 0; 133185380Ssam L4 = 107; 134185380Ssam if (mixOvr == 1) { 135185380Ssam L2 = 83; 136185380Ssam L4 = 83; 137185380Ssam gv->hiTrig = 55; 138185380Ssam } 139185380Ssam } else { 140185380Ssam gStep = ar5212GetRfField(ar5212GetRfBank(ah, 7), 6, 37, 0); 141185380Ssam 142185380Ssam L1 = 0; 143185380Ssam L2 = (gStep == 0x3f) ? 50 : gStep + 4; 144185380Ssam L3 = (gStep != 0x3f) ? 0x40 : L1; 145185380Ssam L4 = L3 + 50; 146185380Ssam 147185380Ssam gv->loTrig = L1 + (gStep == 0x3f ? DYN_ADJ_LO_MARGIN : 0); 148185380Ssam /* never adjust if != 0x3f */ 149185380Ssam gv->hiTrig = L4 - (gStep == 0x3f ? DYN_ADJ_UP_MARGIN : -5); 150185380Ssam } 151185380Ssam g = gv->currGain; 152185380Ssam 153185380Ssam return !((g >= L1 && g<= L2) || (g >= L3 && g <= L4)); 154185380Ssam} 155185380Ssam 156185380Ssam/* 157185380Ssam * Enable the probe gain check on the next packet 158185380Ssam */ 159185380Ssamvoid 160185380Ssamar5212RequestRfgain(struct ath_hal *ah) 161185380Ssam{ 162185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 163185380Ssam uint32_t probePowerIndex; 164185380Ssam 165185380Ssam /* Enable the gain readback probe */ 166185380Ssam probePowerIndex = ahp->ah_ofdmTxPower + ahp->ah_txPowerIndexOffset; 167185380Ssam OS_REG_WRITE(ah, AR_PHY_PAPD_PROBE, 168185380Ssam SM(probePowerIndex, AR_PHY_PAPD_PROBE_POWERTX) 169185380Ssam | AR_PHY_PAPD_PROBE_NEXT_TX); 170185380Ssam 171185380Ssam ahp->ah_rfgainState = HAL_RFGAIN_READ_REQUESTED; 172185380Ssam} 173185380Ssam 174185380Ssam/* 175185380Ssam * Check to see if our readback gain level sits within the linear 176185380Ssam * region of our current variable attenuation window 177185380Ssam */ 178185380Ssamstatic HAL_BOOL 179185380Ssamar5212IsGainAdjustNeeded(struct ath_hal *ah, const GAIN_VALUES *gv) 180185380Ssam{ 181185380Ssam return (gv->currGain <= gv->loTrig || gv->currGain >= gv->hiTrig); 182185380Ssam} 183185380Ssam 184185380Ssam/* 185185380Ssam * Move the rabbit ears in the correct direction. 186185380Ssam */ 187185380Ssamstatic int32_t 188185380Ssamar5212AdjustGain(struct ath_hal *ah, GAIN_VALUES *gv) 189185380Ssam{ 190185380Ssam const GAIN_OPTIMIZATION_LADDER *gl; 191185380Ssam 192185380Ssam if (IS_RAD5112_ANY(ah)) 193185380Ssam gl = &gainLadder5112; 194185380Ssam else 195185380Ssam gl = &gainLadder; 196185380Ssam gv->currStep = &gl->optStep[gv->currStepNum]; 197185380Ssam if (gv->currGain >= gv->hiTrig) { 198185380Ssam if (gv->currStepNum == 0) { 199185380Ssam HALDEBUG(ah, HAL_DEBUG_ANY, "%s: Max gain limit.\n", 200185380Ssam __func__); 201185380Ssam return -1; 202185380Ssam } 203185380Ssam HALDEBUG(ah, HAL_DEBUG_RFPARAM, 204185380Ssam "%s: Adding gain: currG=%d [%s] --> ", 205185380Ssam __func__, gv->currGain, gv->currStep->stepName); 206185380Ssam gv->targetGain = gv->currGain; 207185380Ssam while (gv->targetGain >= gv->hiTrig && gv->currStepNum > 0) { 208185380Ssam gv->targetGain -= 2 * (gl->optStep[--(gv->currStepNum)].stepGain - 209185380Ssam gv->currStep->stepGain); 210185380Ssam gv->currStep = &gl->optStep[gv->currStepNum]; 211185380Ssam } 212185380Ssam HALDEBUG(ah, HAL_DEBUG_RFPARAM, "targG=%d [%s]\n", 213185380Ssam gv->targetGain, gv->currStep->stepName); 214185380Ssam return 1; 215185380Ssam } 216185380Ssam if (gv->currGain <= gv->loTrig) { 217185380Ssam if (gv->currStepNum == gl->numStepsInLadder-1) { 218185380Ssam HALDEBUG(ah, HAL_DEBUG_RFPARAM, 219185380Ssam "%s: Min gain limit.\n", __func__); 220185380Ssam return -2; 221185380Ssam } 222185380Ssam HALDEBUG(ah, HAL_DEBUG_RFPARAM, 223185380Ssam "%s: Deducting gain: currG=%d [%s] --> ", 224185380Ssam __func__, gv->currGain, gv->currStep->stepName); 225185380Ssam gv->targetGain = gv->currGain; 226185380Ssam while (gv->targetGain <= gv->loTrig && 227185380Ssam gv->currStepNum < (gl->numStepsInLadder - 1)) { 228185380Ssam gv->targetGain -= 2 * 229185380Ssam (gl->optStep[++(gv->currStepNum)].stepGain - gv->currStep->stepGain); 230185380Ssam gv->currStep = &gl->optStep[gv->currStepNum]; 231185380Ssam } 232185380Ssam HALDEBUG(ah, HAL_DEBUG_RFPARAM, "targG=%d [%s]\n", 233185380Ssam gv->targetGain, gv->currStep->stepName); 234185380Ssam return 2; 235185380Ssam } 236185380Ssam return 0; /* caller didn't call needAdjGain first */ 237185380Ssam} 238185380Ssam 239185380Ssam/* 240185380Ssam * Read rf register to determine if gainF needs correction 241185380Ssam */ 242188197Ssamstatic uint32_t 243185380Ssamar5212GetGainFCorrection(struct ath_hal *ah) 244185380Ssam{ 245185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 246188197Ssam uint32_t correction; 247185380Ssam 248185380Ssam HALASSERT(IS_RADX112_REV2(ah)); 249185380Ssam 250188197Ssam correction = 0; 251185380Ssam if (ar5212GetRfField(ar5212GetRfBank(ah, 7), 1, 36, 0) == 1) { 252188197Ssam const GAIN_VALUES *gv = &ahp->ah_gainValues; 253185380Ssam uint32_t mixGain = gv->currStep->paramVal[0]; 254185380Ssam uint32_t gainStep = 255185380Ssam ar5212GetRfField(ar5212GetRfBank(ah, 7), 4, 32, 0); 256185380Ssam switch (mixGain) { 257185380Ssam case 0 : 258188197Ssam correction = 0; 259185380Ssam break; 260185380Ssam case 1 : 261188197Ssam correction = gainStep; 262185380Ssam break; 263185380Ssam case 2 : 264188197Ssam correction = 2 * gainStep - 5; 265185380Ssam break; 266185380Ssam case 3 : 267188197Ssam correction = 2 * gainStep; 268185380Ssam break; 269185380Ssam } 270185380Ssam } 271188197Ssam return correction; 272185380Ssam} 273185380Ssam 274185380Ssam/* 275185380Ssam * Exported call to check for a recent gain reading and return 276185380Ssam * the current state of the thermal calibration gain engine. 277185380Ssam */ 278185380SsamHAL_RFGAIN 279185380Ssamar5212GetRfgain(struct ath_hal *ah) 280185380Ssam{ 281185380Ssam struct ath_hal_5212 *ahp = AH5212(ah); 282185380Ssam GAIN_VALUES *gv = &ahp->ah_gainValues; 283185380Ssam uint32_t rddata, probeType; 284185380Ssam 285187129Ssam /* NB: beware of touching the BB when PHY is powered down */ 286187129Ssam if (!gv->active || !ahp->ah_phyPowerOn) 287185380Ssam return HAL_RFGAIN_INACTIVE; 288185380Ssam 289185380Ssam if (ahp->ah_rfgainState == HAL_RFGAIN_READ_REQUESTED) { 290185380Ssam /* Caller had asked to setup a new reading. Check it. */ 291185380Ssam rddata = OS_REG_READ(ah, AR_PHY_PAPD_PROBE); 292185380Ssam 293185380Ssam if ((rddata & AR_PHY_PAPD_PROBE_NEXT_TX) == 0) { 294185380Ssam /* bit got cleared, we have a new reading. */ 295185380Ssam gv->currGain = rddata >> AR_PHY_PAPD_PROBE_GAINF_S; 296185380Ssam probeType = MS(rddata, AR_PHY_PAPD_PROBE_TYPE); 297185380Ssam if (probeType == AR_PHY_PAPD_PROBE_TYPE_CCK) { 298185380Ssam const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom; 299185380Ssam 300185380Ssam HALASSERT(IS_RAD5112_ANY(ah)); 301185380Ssam HALASSERT(ah->ah_magic == AR5212_MAGIC); 302185380Ssam if (AH_PRIVATE(ah)->ah_phyRev >= AR_PHY_CHIP_ID_REV_2) 303185380Ssam gv->currGain += ee->ee_cckOfdmGainDelta; 304185380Ssam else 305185380Ssam gv->currGain += PHY_PROBE_CCK_CORRECTION; 306185380Ssam } 307185380Ssam if (IS_RADX112_REV2(ah)) { 308188197Ssam uint32_t correct = ar5212GetGainFCorrection(ah); 309188197Ssam if (gv->currGain >= correct) 310188197Ssam gv->currGain -= correct; 311185380Ssam else 312185380Ssam gv->currGain = 0; 313185380Ssam } 314185380Ssam /* inactive by default */ 315185380Ssam ahp->ah_rfgainState = HAL_RFGAIN_INACTIVE; 316185380Ssam 317185380Ssam if (!ar5212InvalidGainReadback(ah, gv) && 318185380Ssam ar5212IsGainAdjustNeeded(ah, gv) && 319185380Ssam ar5212AdjustGain(ah, gv) > 0) { 320185380Ssam /* 321185380Ssam * Change needed. Copy ladder info 322185380Ssam * into eeprom info. 323185380Ssam */ 324185380Ssam ahp->ah_rfgainState = HAL_RFGAIN_NEED_CHANGE; 325185380Ssam /* for ap51 */ 326185380Ssam ahp->ah_cwCalRequire = AH_TRUE; 327185380Ssam /* Request IQ recalibration for temperature chang */ 328185380Ssam ahp->ah_bIQCalibration = IQ_CAL_INACTIVE; 329185380Ssam } 330185380Ssam } 331185380Ssam } 332185380Ssam return ahp->ah_rfgainState; 333185380Ssam} 334