2 * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
3 * Copyright (c) 2002-2008 Atheros Communications, Inc.
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.
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.
17 * $FreeBSD: head/sys/dev/ath/ath_hal/ar5212/ar5212_rfgain.c 188197 2009-02-05 21:13:31Z sam $
23 #include "ah_internal.h"
26 #include "ar5212/ar5212.h"
27 #include "ar5212/ar5212reg.h"
28 #include "ar5212/ar5212phy.h"
30 #include "ah_eeprom_v3.h"
32 static const GAIN_OPTIMIZATION_LADDER gainLadder = {
33 9, /* numStepsInLadder */
34 4, /* defaultStepNum */
35 { { {4, 1, 1, 1}, 6, "FG8"},
36 { {4, 0, 1, 1}, 4, "FG7"},
37 { {3, 1, 1, 1}, 3, "FG6"},
38 { {4, 0, 0, 1}, 1, "FG5"},
39 { {4, 1, 1, 0}, 0, "FG4"}, /* noJack */
40 { {4, 0, 1, 0}, -2, "FG3"}, /* halfJack */
41 { {3, 1, 1, 0}, -3, "FG2"}, /* clip3 */
42 { {4, 0, 0, 0}, -4, "FG1"}, /* noJack */
43 { {2, 1, 1, 0}, -6, "FG0"} /* clip2 */
47 static const GAIN_OPTIMIZATION_LADDER gainLadder5112 = {
48 8, /* numStepsInLadder */
49 1, /* defaultStepNum */
50 { { {3, 0,0,0, 0,0,0}, 6, "FG7"}, /* most fixed gain */
51 { {2, 0,0,0, 0,0,0}, 0, "FG6"},
52 { {1, 0,0,0, 0,0,0}, -3, "FG5"},
53 { {0, 0,0,0, 0,0,0}, -6, "FG4"},
54 { {0, 1,1,0, 0,0,0}, -8, "FG3"},
55 { {0, 1,1,0, 1,1,0}, -10, "FG2"},
56 { {0, 1,0,1, 1,1,0}, -13, "FG1"},
57 { {0, 1,0,1, 1,0,1}, -16, "FG0"}, /* least fixed gain */
62 * Initialize the gain structure to good values
65 ar5212InitializeGainValues(struct ath_hal *ah)
67 struct ath_hal_5212 *ahp = AH5212(ah);
68 GAIN_VALUES *gv = &ahp->ah_gainValues;
70 /* initialize gain optimization values */
71 if (IS_RAD5112_ANY(ah)) {
72 gv->currStepNum = gainLadder5112.defaultStepNum;
74 &gainLadder5112.optStep[gainLadder5112.defaultStepNum];
79 gv->currStepNum = gainLadder.defaultStepNum;
80 gv->currStep = &gainLadder.optStep[gainLadder.defaultStepNum];
87 #define MAX_ANALOG_START 319 /* XXX */
90 * Find analog bits of given parameter data and return a reversed value
93 ar5212GetRfField(uint32_t *rfBuf, uint32_t numBits, uint32_t firstBit, uint32_t column)
95 uint32_t reg32 = 0, mask, arrayEntry, lastBit;
96 uint32_t bitPosition, bitsShifted;
99 HALASSERT(column <= 3);
100 HALASSERT(numBits <= 32);
101 HALASSERT(firstBit + numBits <= MAX_ANALOG_START);
103 arrayEntry = (firstBit - 1) / 8;
104 bitPosition = (firstBit - 1) % 8;
107 while (bitsLeft > 0) {
108 lastBit = (bitPosition + bitsLeft > 8) ?
109 (8) : (bitPosition + bitsLeft);
110 mask = (((1 << lastBit) - 1) ^ ((1 << bitPosition) - 1)) <<
112 reg32 |= (((rfBuf[arrayEntry] & mask) >> (column * 8)) >>
113 bitPosition) << bitsShifted;
114 bitsShifted += lastBit - bitPosition;
115 bitsLeft -= (8 - bitPosition);
119 reg32 = ath_hal_reverseBits(reg32, numBits);
124 ar5212InvalidGainReadback(struct ath_hal *ah, GAIN_VALUES *gv)
126 uint32_t gStep, g, mixOvr;
127 uint32_t L1, L2, L3, L4;
129 if (IS_RAD5112_ANY(ah)) {
130 mixOvr = ar5212GetRfField(ar5212GetRfBank(ah, 7), 1, 36, 0);
141 gStep = ar5212GetRfField(ar5212GetRfBank(ah, 7), 6, 37, 0);
144 L2 = (gStep == 0x3f) ? 50 : gStep + 4;
145 L3 = (gStep != 0x3f) ? 0x40 : L1;
148 gv->loTrig = L1 + (gStep == 0x3f ? DYN_ADJ_LO_MARGIN : 0);
149 /* never adjust if != 0x3f */
150 gv->hiTrig = L4 - (gStep == 0x3f ? DYN_ADJ_UP_MARGIN : -5);
154 return !((g >= L1 && g<= L2) || (g >= L3 && g <= L4));
158 * Enable the probe gain check on the next packet
161 ar5212RequestRfgain(struct ath_hal *ah)
163 struct ath_hal_5212 *ahp = AH5212(ah);
164 uint32_t probePowerIndex;
166 /* Enable the gain readback probe */
167 probePowerIndex = ahp->ah_ofdmTxPower + ahp->ah_txPowerIndexOffset;
168 OS_REG_WRITE(ah, AR_PHY_PAPD_PROBE,
169 SM(probePowerIndex, AR_PHY_PAPD_PROBE_POWERTX)
170 | AR_PHY_PAPD_PROBE_NEXT_TX);
172 ahp->ah_rfgainState = HAL_RFGAIN_READ_REQUESTED;
176 * Check to see if our readback gain level sits within the linear
177 * region of our current variable attenuation window
180 ar5212IsGainAdjustNeeded(struct ath_hal *ah, const GAIN_VALUES *gv)
182 return (gv->currGain <= gv->loTrig || gv->currGain >= gv->hiTrig);
186 * Move the rabbit ears in the correct direction.
189 ar5212AdjustGain(struct ath_hal *ah, GAIN_VALUES *gv)
191 const GAIN_OPTIMIZATION_LADDER *gl;
193 if (IS_RAD5112_ANY(ah))
194 gl = &gainLadder5112;
197 gv->currStep = &gl->optStep[gv->currStepNum];
198 if (gv->currGain >= gv->hiTrig) {
199 if (gv->currStepNum == 0) {
200 HALDEBUG(ah, HAL_DEBUG_ANY, "%s: Max gain limit.\n",
204 HALDEBUG(ah, HAL_DEBUG_RFPARAM,
205 "%s: Adding gain: currG=%d [%s] --> ",
206 __func__, gv->currGain, gv->currStep->stepName);
207 gv->targetGain = gv->currGain;
208 while (gv->targetGain >= gv->hiTrig && gv->currStepNum > 0) {
209 gv->targetGain -= 2 * (gl->optStep[--(gv->currStepNum)].stepGain -
210 gv->currStep->stepGain);
211 gv->currStep = &gl->optStep[gv->currStepNum];
213 HALDEBUG(ah, HAL_DEBUG_RFPARAM, "targG=%d [%s]\n",
214 gv->targetGain, gv->currStep->stepName);
217 if (gv->currGain <= gv->loTrig) {
218 if (gv->currStepNum == gl->numStepsInLadder-1) {
219 HALDEBUG(ah, HAL_DEBUG_RFPARAM,
220 "%s: Min gain limit.\n", __func__);
223 HALDEBUG(ah, HAL_DEBUG_RFPARAM,
224 "%s: Deducting gain: currG=%d [%s] --> ",
225 __func__, gv->currGain, gv->currStep->stepName);
226 gv->targetGain = gv->currGain;
227 while (gv->targetGain <= gv->loTrig &&
228 gv->currStepNum < (gl->numStepsInLadder - 1)) {
229 gv->targetGain -= 2 *
230 (gl->optStep[++(gv->currStepNum)].stepGain - gv->currStep->stepGain);
231 gv->currStep = &gl->optStep[gv->currStepNum];
233 HALDEBUG(ah, HAL_DEBUG_RFPARAM, "targG=%d [%s]\n",
234 gv->targetGain, gv->currStep->stepName);
237 return 0; /* caller didn't call needAdjGain first */
241 * Read rf register to determine if gainF needs correction
244 ar5212GetGainFCorrection(struct ath_hal *ah)
246 struct ath_hal_5212 *ahp = AH5212(ah);
249 HALASSERT(IS_RADX112_REV2(ah));
252 if (ar5212GetRfField(ar5212GetRfBank(ah, 7), 1, 36, 0) == 1) {
253 const GAIN_VALUES *gv = &ahp->ah_gainValues;
254 uint32_t mixGain = gv->currStep->paramVal[0];
256 ar5212GetRfField(ar5212GetRfBank(ah, 7), 4, 32, 0);
262 correction = gainStep;
265 correction = 2 * gainStep - 5;
268 correction = 2 * gainStep;
276 * Exported call to check for a recent gain reading and return
277 * the current state of the thermal calibration gain engine.
280 ar5212GetRfgain(struct ath_hal *ah)
282 struct ath_hal_5212 *ahp = AH5212(ah);
283 GAIN_VALUES *gv = &ahp->ah_gainValues;
284 uint32_t rddata, probeType;
286 /* NB: beware of touching the BB when PHY is powered down */
287 if (!gv->active || !ahp->ah_phyPowerOn)
288 return HAL_RFGAIN_INACTIVE;
290 if (ahp->ah_rfgainState == HAL_RFGAIN_READ_REQUESTED) {
291 /* Caller had asked to setup a new reading. Check it. */
292 rddata = OS_REG_READ(ah, AR_PHY_PAPD_PROBE);
294 if ((rddata & AR_PHY_PAPD_PROBE_NEXT_TX) == 0) {
295 /* bit got cleared, we have a new reading. */
296 gv->currGain = rddata >> AR_PHY_PAPD_PROBE_GAINF_S;
297 probeType = MS(rddata, AR_PHY_PAPD_PROBE_TYPE);
298 if (probeType == AR_PHY_PAPD_PROBE_TYPE_CCK) {
299 const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;
301 HALASSERT(IS_RAD5112_ANY(ah));
302 HALASSERT(ah->ah_magic == AR5212_MAGIC);
303 if (AH_PRIVATE(ah)->ah_phyRev >= AR_PHY_CHIP_ID_REV_2)
304 gv->currGain += ee->ee_cckOfdmGainDelta;
306 gv->currGain += PHY_PROBE_CCK_CORRECTION;
308 if (IS_RADX112_REV2(ah)) {
309 uint32_t correct = ar5212GetGainFCorrection(ah);
310 if (gv->currGain >= correct)
311 gv->currGain -= correct;
315 /* inactive by default */
316 ahp->ah_rfgainState = HAL_RFGAIN_INACTIVE;
318 if (!ar5212InvalidGainReadback(ah, gv) &&
319 ar5212IsGainAdjustNeeded(ah, gv) &&
320 ar5212AdjustGain(ah, gv) > 0) {
322 * Change needed. Copy ladder info
325 ahp->ah_rfgainState = HAL_RFGAIN_NEED_CHANGE;
327 ahp->ah_cwCalRequire = AH_TRUE;
328 /* Request IQ recalibration for temperature chang */
329 ahp->ah_bIQCalibration = IQ_CAL_INACTIVE;
333 return ahp->ah_rfgainState;