Merge branch 'master' into net80211-update
[dragonfly.git] / sys / dev / netif / ath / hal / ath_hal / ar5212 / ar5212_rfgain.c
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  * $FreeBSD: head/sys/dev/ath/ath_hal/ar5212/ar5212_rfgain.c 188197 2009-02-05 21:13:31Z sam $
18  * $DragonFly$
19  */
20 #include "opt_ah.h"
21
22 #include "ah.h"
23 #include "ah_internal.h"
24 #include "ah_devid.h"
25
26 #include "ar5212/ar5212.h"
27 #include "ar5212/ar5212reg.h"
28 #include "ar5212/ar5212phy.h"
29
30 #include "ah_eeprom_v3.h"
31
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 */
44         }
45 };
46
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 */
58         }
59 };
60
61 /*
62  * Initialize the gain structure to good values
63  */
64 void
65 ar5212InitializeGainValues(struct ath_hal *ah)
66 {
67         struct ath_hal_5212 *ahp = AH5212(ah);
68         GAIN_VALUES *gv = &ahp->ah_gainValues;
69
70         /* initialize gain optimization values */
71         if (IS_RAD5112_ANY(ah)) {
72                 gv->currStepNum = gainLadder5112.defaultStepNum;
73                 gv->currStep =
74                         &gainLadder5112.optStep[gainLadder5112.defaultStepNum];
75                 gv->active = AH_TRUE;
76                 gv->loTrig = 20;
77                 gv->hiTrig = 85;
78         } else {
79                 gv->currStepNum = gainLadder.defaultStepNum;
80                 gv->currStep = &gainLadder.optStep[gainLadder.defaultStepNum];
81                 gv->active = AH_TRUE;
82                 gv->loTrig = 20;
83                 gv->hiTrig = 35;
84         }
85 }
86
87 #define MAX_ANALOG_START        319             /* XXX */
88
89 /*
90  * Find analog bits of given parameter data and return a reversed value
91  */
92 static uint32_t
93 ar5212GetRfField(uint32_t *rfBuf, uint32_t numBits, uint32_t firstBit, uint32_t column)
94 {
95         uint32_t reg32 = 0, mask, arrayEntry, lastBit;
96         uint32_t bitPosition, bitsShifted;
97         int32_t bitsLeft;
98
99         HALASSERT(column <= 3);
100         HALASSERT(numBits <= 32);
101         HALASSERT(firstBit + numBits <= MAX_ANALOG_START);
102
103         arrayEntry = (firstBit - 1) / 8;
104         bitPosition = (firstBit - 1) % 8;
105         bitsLeft = numBits;
106         bitsShifted = 0;
107         while (bitsLeft > 0) {
108                 lastBit = (bitPosition + bitsLeft > 8) ?
109                         (8) : (bitPosition + bitsLeft);
110                 mask = (((1 << lastBit) - 1) ^ ((1 << bitPosition) - 1)) <<
111                         (column * 8);
112                 reg32 |= (((rfBuf[arrayEntry] & mask) >> (column * 8)) >>
113                         bitPosition) << bitsShifted;
114                 bitsShifted += lastBit - bitPosition;
115                 bitsLeft -= (8 - bitPosition);
116                 bitPosition = 0;
117                 arrayEntry++;
118         }
119         reg32 = ath_hal_reverseBits(reg32, numBits);
120         return reg32;
121 }
122
123 static HAL_BOOL
124 ar5212InvalidGainReadback(struct ath_hal *ah, GAIN_VALUES *gv)
125 {
126         uint32_t gStep, g, mixOvr;
127         uint32_t L1, L2, L3, L4;
128
129         if (IS_RAD5112_ANY(ah)) {
130                 mixOvr = ar5212GetRfField(ar5212GetRfBank(ah, 7), 1, 36, 0);
131                 L1 = 0;
132                 L2 = 107;
133                 L3 = 0;
134                 L4 = 107;
135                 if (mixOvr == 1) {
136                         L2 = 83;
137                         L4 = 83;
138                         gv->hiTrig = 55;
139                 }
140         } else {
141                 gStep = ar5212GetRfField(ar5212GetRfBank(ah, 7), 6, 37, 0);
142
143                 L1 = 0;
144                 L2 = (gStep == 0x3f) ? 50 : gStep + 4;
145                 L3 = (gStep != 0x3f) ? 0x40 : L1;
146                 L4 = L3 + 50;
147
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);
151         }
152         g = gv->currGain;
153
154         return !((g >= L1 && g<= L2) || (g >= L3 && g <= L4));
155 }
156
157 /*
158  * Enable the probe gain check on the next packet
159  */
160 void
161 ar5212RequestRfgain(struct ath_hal *ah)
162 {
163         struct ath_hal_5212 *ahp = AH5212(ah);
164         uint32_t probePowerIndex;
165
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);
171
172         ahp->ah_rfgainState = HAL_RFGAIN_READ_REQUESTED;
173 }
174
175 /*
176  * Check to see if our readback gain level sits within the linear
177  * region of our current variable attenuation window
178  */
179 static HAL_BOOL
180 ar5212IsGainAdjustNeeded(struct ath_hal *ah, const GAIN_VALUES *gv)
181 {
182         return (gv->currGain <= gv->loTrig || gv->currGain >= gv->hiTrig);
183 }
184
185 /*
186  * Move the rabbit ears in the correct direction.
187  */
188 static int32_t 
189 ar5212AdjustGain(struct ath_hal *ah, GAIN_VALUES *gv)
190 {
191         const GAIN_OPTIMIZATION_LADDER *gl;
192
193         if (IS_RAD5112_ANY(ah))
194                 gl = &gainLadder5112;
195         else
196                 gl = &gainLadder;
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",
201                             __func__);
202                         return -1;
203                 }
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];
212                 }
213                 HALDEBUG(ah, HAL_DEBUG_RFPARAM, "targG=%d [%s]\n",
214                     gv->targetGain, gv->currStep->stepName);
215                 return 1;
216         }
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__);
221                         return -2;
222                 }
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];
232                 }
233                 HALDEBUG(ah, HAL_DEBUG_RFPARAM, "targG=%d [%s]\n",
234                     gv->targetGain, gv->currStep->stepName);
235                 return 2;
236         }
237         return 0;               /* caller didn't call needAdjGain first */
238 }
239
240 /*
241  * Read rf register to determine if gainF needs correction
242  */
243 static uint32_t
244 ar5212GetGainFCorrection(struct ath_hal *ah)
245 {
246         struct ath_hal_5212 *ahp = AH5212(ah);
247         uint32_t correction;
248
249         HALASSERT(IS_RADX112_REV2(ah));
250
251         correction = 0;
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];
255                 uint32_t gainStep =
256                         ar5212GetRfField(ar5212GetRfBank(ah, 7), 4, 32, 0);
257                 switch (mixGain) {
258                 case 0 :
259                         correction = 0;
260                         break;
261                 case 1 :
262                         correction = gainStep;
263                         break;
264                 case 2 :
265                         correction = 2 * gainStep - 5;
266                         break;
267                 case 3 :
268                         correction = 2 * gainStep;
269                         break;
270                 }
271         }
272         return correction;
273 }
274
275 /*
276  * Exported call to check for a recent gain reading and return
277  * the current state of the thermal calibration gain engine.
278  */
279 HAL_RFGAIN
280 ar5212GetRfgain(struct ath_hal *ah)
281 {
282         struct ath_hal_5212 *ahp = AH5212(ah);
283         GAIN_VALUES *gv = &ahp->ah_gainValues;
284         uint32_t rddata, probeType;
285
286         /* NB: beware of touching the BB when PHY is powered down */
287         if (!gv->active || !ahp->ah_phyPowerOn)
288                 return HAL_RFGAIN_INACTIVE;
289
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);
293
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;
300
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;
305                                 else
306                                         gv->currGain += PHY_PROBE_CCK_CORRECTION;
307                         }
308                         if (IS_RADX112_REV2(ah)) {
309                                 uint32_t correct = ar5212GetGainFCorrection(ah);
310                                 if (gv->currGain >= correct)
311                                         gv->currGain -= correct;
312                                 else
313                                         gv->currGain = 0;
314                         }
315                         /* inactive by default */
316                         ahp->ah_rfgainState = HAL_RFGAIN_INACTIVE;
317
318                         if (!ar5212InvalidGainReadback(ah, gv) &&
319                             ar5212IsGainAdjustNeeded(ah, gv) &&
320                             ar5212AdjustGain(ah, gv) > 0) {
321                                 /*
322                                  * Change needed. Copy ladder info
323                                  * into eeprom info.
324                                  */
325                                 ahp->ah_rfgainState = HAL_RFGAIN_NEED_CHANGE;
326                                 /* for ap51 */
327                                 ahp->ah_cwCalRequire = AH_TRUE;
328                                 /* Request IQ recalibration for temperature chang */
329                                 ahp->ah_bIQCalibration = IQ_CAL_INACTIVE;
330                         }
331                 }
332         }
333         return ahp->ah_rfgainState;
334 }