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.1.1.1 2008/12/11 04:46:43 alc 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 | |
31 | static 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 | |
46 | static 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 | */ |
63 | void |
64 | ar5212InitializeGainValues(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 | */ |
91 | static uint32_t |
92 | ar5212GetRfField(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 | |
122 | static HAL_BOOL |
123 | ar5212InvalidGainReadback(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 | */ |
159 | void |
160 | ar5212RequestRfgain(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 | */ |
178 | static HAL_BOOL |
179 | ar5212IsGainAdjustNeeded(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 | */ |
187 | static int32_t |
188 | ar5212AdjustGain(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 | */ |
242 | static void |
243 | ar5212GetGainFCorrection(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 | */ |
276 | HAL_RFGAIN |
277 | ar5212GetRfgain(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 | |