xref: /dragonfly/sys/dev/netif/ath/ath_hal/ar5212/ar5212_rfgain.c (revision 572ff6f6e8b95055988f178b6ba12ce77bb5b3c2)
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$
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