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 #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
ar5212InitializeGainValues(struct ath_hal * ah)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
ar5212GetRfField(uint32_t * rfBuf,uint32_t numBits,uint32_t firstBit,uint32_t column)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
ar5212InvalidGainReadback(struct ath_hal * ah,GAIN_VALUES * gv)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
ar5212RequestRfgain(struct ath_hal * ah)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
ar5212IsGainAdjustNeeded(struct ath_hal * ah,const GAIN_VALUES * gv)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
ar5212AdjustGain(struct ath_hal * ah,GAIN_VALUES * gv)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 uint32_t
ar5212GetGainFCorrection(struct ath_hal * ah)243 ar5212GetGainFCorrection(struct ath_hal *ah)
244 {
245 struct ath_hal_5212 *ahp = AH5212(ah);
246 uint32_t correction;
247
248 HALASSERT(IS_RADX112_REV2(ah));
249
250 correction = 0;
251 if (ar5212GetRfField(ar5212GetRfBank(ah, 7), 1, 36, 0) == 1) {
252 const GAIN_VALUES *gv = &ahp->ah_gainValues;
253 uint32_t mixGain = gv->currStep->paramVal[0];
254 uint32_t gainStep =
255 ar5212GetRfField(ar5212GetRfBank(ah, 7), 4, 32, 0);
256 switch (mixGain) {
257 case 0 :
258 correction = 0;
259 break;
260 case 1 :
261 correction = gainStep;
262 break;
263 case 2 :
264 correction = 2 * gainStep - 5;
265 break;
266 case 3 :
267 correction = 2 * gainStep;
268 break;
269 }
270 }
271 return correction;
272 }
273
274 /*
275 * Exported call to check for a recent gain reading and return
276 * the current state of the thermal calibration gain engine.
277 */
278 HAL_RFGAIN
ar5212GetRfgain(struct ath_hal * ah)279 ar5212GetRfgain(struct ath_hal *ah)
280 {
281 struct ath_hal_5212 *ahp = AH5212(ah);
282 GAIN_VALUES *gv = &ahp->ah_gainValues;
283 uint32_t rddata, probeType;
284
285 /* NB: beware of touching the BB when PHY is powered down */
286 if (!gv->active || !ahp->ah_phyPowerOn)
287 return HAL_RFGAIN_INACTIVE;
288
289 if (ahp->ah_rfgainState == HAL_RFGAIN_READ_REQUESTED) {
290 /* Caller had asked to setup a new reading. Check it. */
291 rddata = OS_REG_READ(ah, AR_PHY_PAPD_PROBE);
292
293 if ((rddata & AR_PHY_PAPD_PROBE_NEXT_TX) == 0) {
294 /* bit got cleared, we have a new reading. */
295 gv->currGain = rddata >> AR_PHY_PAPD_PROBE_GAINF_S;
296 probeType = MS(rddata, AR_PHY_PAPD_PROBE_TYPE);
297 if (probeType == AR_PHY_PAPD_PROBE_TYPE_CCK) {
298 const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;
299
300 HALASSERT(IS_RAD5112_ANY(ah));
301 HALASSERT(ah->ah_magic == AR5212_MAGIC);
302 if (AH_PRIVATE(ah)->ah_phyRev >= AR_PHY_CHIP_ID_REV_2)
303 gv->currGain += ee->ee_cckOfdmGainDelta;
304 else
305 gv->currGain += PHY_PROBE_CCK_CORRECTION;
306 }
307 if (IS_RADX112_REV2(ah)) {
308 uint32_t correct = ar5212GetGainFCorrection(ah);
309 if (gv->currGain >= correct)
310 gv->currGain -= correct;
311 else
312 gv->currGain = 0;
313 }
314 /* inactive by default */
315 ahp->ah_rfgainState = HAL_RFGAIN_INACTIVE;
316
317 if (!ar5212InvalidGainReadback(ah, gv) &&
318 ar5212IsGainAdjustNeeded(ah, gv) &&
319 ar5212AdjustGain(ah, gv) > 0) {
320 /*
321 * Change needed. Copy ladder info
322 * into eeprom info.
323 */
324 ahp->ah_rfgainState = HAL_RFGAIN_NEED_CHANGE;
325 /* for ap51 */
326 ahp->ah_cwCalRequire = AH_TRUE;
327 /* Request IQ recalibration for temperature chang */
328 ahp->ah_bIQCalibration = IQ_CAL_INACTIVE;
329 }
330 }
331 }
332 return ahp->ah_rfgainState;
333 }
334