xref: /dragonfly/sys/dev/netif/ath/ath_hal/ar9002/ar9280_olc.c (revision 93d7266760cd54ddd6c33317d44791ba6f7c9930)
1 /*
2  * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23  * SUCH DAMAGE.
24  *
25  * $FreeBSD$
26  */
27 #include "opt_ah.h"
28 
29 #include "ah.h"
30 #include "ah_internal.h"
31 
32 #include "ah_eeprom_v14.h"
33 
34 #include "ar9002/ar9280.h"
35 #include "ar5416/ar5416reg.h"
36 #include "ar5416/ar5416phy.h"
37 #include "ar9002/ar9002phy.h"
38 
39 #include "ar9002/ar9280_olc.h"
40 
41 void
ar9280olcInit(struct ath_hal * ah)42 ar9280olcInit(struct ath_hal *ah)
43 {
44           uint32_t i;
45 
46           /* Only do OLC if it's enabled for this chipset */
47           if (! ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
48                     return;
49 
50           HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Setting up TX gain tables.\n", __func__);
51 
52           for (i = 0; i < AR9280_TX_GAIN_TABLE_SIZE; i++)
53                     AH9280(ah)->originalGain[i] = MS(OS_REG_READ(ah,
54                         AR_PHY_TX_GAIN_TBL1 + i * 4), AR_PHY_TX_GAIN);
55 
56           AH9280(ah)->PDADCdelta = 0;
57 }
58 
59 void
ar9280olcGetTxGainIndex(struct ath_hal * ah,const struct ieee80211_channel * chan,struct calDataPerFreqOpLoop * rawDatasetOpLoop,uint8_t * calChans,uint16_t availPiers,uint8_t * pwr,uint8_t * pcdacIdx)60 ar9280olcGetTxGainIndex(struct ath_hal *ah,
61     const struct ieee80211_channel *chan,
62     struct calDataPerFreqOpLoop *rawDatasetOpLoop,
63     uint8_t *calChans, uint16_t availPiers, uint8_t *pwr, uint8_t *pcdacIdx)
64 {
65           uint8_t pcdac, i = 0;
66           uint16_t idxL = 0, idxR = 0, numPiers;
67           HAL_BOOL match;
68           CHAN_CENTERS centers;
69 
70           ar5416GetChannelCenters(ah, chan, &centers);
71 
72           for (numPiers = 0; numPiers < availPiers; numPiers++)
73                     if (calChans[numPiers] == AR5416_BCHAN_UNUSED)
74                               break;
75 
76           match = ath_ee_getLowerUpperIndex((uint8_t)FREQ2FBIN(centers.synth_center,
77                         IEEE80211_IS_CHAN_2GHZ(chan)), calChans, numPiers,
78                         &idxL, &idxR);
79           if (match) {
80                     pcdac = rawDatasetOpLoop[idxL].pcdac[0][0];
81                     *pwr = rawDatasetOpLoop[idxL].pwrPdg[0][0];
82           } else {
83                     pcdac = rawDatasetOpLoop[idxR].pcdac[0][0];
84                     *pwr = (rawDatasetOpLoop[idxL].pwrPdg[0][0] +
85                                         rawDatasetOpLoop[idxR].pwrPdg[0][0])/2;
86           }
87           while (i < (AR9280_TX_GAIN_TABLE_SIZE - 1) &&
88                               pcdac > AH9280(ah)->originalGain[i])
89                     i++;
90 
91           *pcdacIdx = i;
92 }
93 
94 /*
95  * XXX txPower here is likely not the target txPower in the traditional
96  * XXX sense, but is set by a call to ar9280olcGetTxGainIndex().
97  * XXX Thus, be careful if you're trying to use this routine yourself.
98  */
99 void
ar9280olcGetPDADCs(struct ath_hal * ah,uint32_t initTxGain,int txPower,uint8_t * pPDADCValues)100 ar9280olcGetPDADCs(struct ath_hal *ah, uint32_t initTxGain, int txPower,
101     uint8_t *pPDADCValues)
102 {
103           uint32_t i;
104           uint32_t offset;
105 
106           OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_0, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3);
107           OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_1, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3);
108 
109           OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL7, AR_PHY_TX_PWRCTRL_INIT_TX_GAIN, initTxGain);
110 
111           offset = txPower;
112           for (i = 0; i < AR5416_NUM_PDADC_VALUES; i++)
113                     if (i < offset)
114                               pPDADCValues[i] = 0x0;
115                     else
116                               pPDADCValues[i] = 0xFF;
117 }
118 
119 /*
120  * Run temperature compensation calibration.
121  *
122  * The TX gain table is adjusted depending upon the difference
123  * between the initial PDADC value and the currently read
124  * average TX power sample value. This value is only valid if
125  * frames have been transmitted, so currPDADC will be 0 if
126  * no frames have yet been transmitted.
127  */
128 void
ar9280olcTemperatureCompensation(struct ath_hal * ah)129 ar9280olcTemperatureCompensation(struct ath_hal *ah)
130 {
131           uint32_t rddata, i;
132           int delta, currPDADC, regval;
133           uint8_t hpwr_5g = 0;
134 
135           if (! ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
136                     return;
137 
138           rddata = OS_REG_READ(ah, AR_PHY_TX_PWRCTRL4);
139           currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT);
140 
141           HALDEBUG(ah, HAL_DEBUG_PERCAL,
142               "%s: called: initPDADC=%d, currPDADC=%d\n",
143               __func__, AH5416(ah)->initPDADC, currPDADC);
144 
145           if (AH5416(ah)->initPDADC == 0 || currPDADC == 0)
146                     return;
147 
148           (void) (ath_hal_eepromGet(ah, AR_EEP_DAC_HPWR_5G, &hpwr_5g));
149 
150           if (hpwr_5g)
151                     delta = (currPDADC - AH5416(ah)->initPDADC + 4) / 8;
152           else
153                     delta = (currPDADC - AH5416(ah)->initPDADC + 5) / 10;
154 
155           HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: delta=%d, PDADCdelta=%d\n",
156               __func__, delta, AH9280(ah)->PDADCdelta);
157 
158           if (delta != AH9280(ah)->PDADCdelta) {
159                     AH9280(ah)->PDADCdelta = delta;
160                     for (i = 1; i < AR9280_TX_GAIN_TABLE_SIZE; i++) {
161                               regval = AH9280(ah)->originalGain[i] - delta;
162                               if (regval < 0)
163                                         regval = 0;
164 
165                               OS_REG_RMW_FIELD(ah,
166                                               AR_PHY_TX_GAIN_TBL1 + i * 4,
167                                               AR_PHY_TX_GAIN, regval);
168                     }
169           }
170 }
171 
172 
173 static int16_t
ar9280ChangeGainBoundarySettings(struct ath_hal * ah,uint16_t * gb,uint16_t numXpdGain,uint16_t pdGainOverlap_t2,int8_t pwr_table_offset,int16_t * diff)174 ar9280ChangeGainBoundarySettings(struct ath_hal *ah, uint16_t *gb,
175     uint16_t numXpdGain, uint16_t pdGainOverlap_t2, int8_t pwr_table_offset,
176     int16_t *diff)
177 {
178           uint16_t k;
179 
180           /* Prior to writing the boundaries or the pdadc vs. power table
181            * into the chip registers the default starting point on the pdadc
182            * vs. power table needs to be checked and the curve boundaries
183            * adjusted accordingly
184            */
185           if (AR_SREV_MERLIN_20_OR_LATER(ah)) {
186                     uint16_t gb_limit;
187 
188                     if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) {
189                               /* get the difference in dB */
190                               *diff = (uint16_t)(pwr_table_offset - AR5416_PWR_TABLE_OFFSET_DB);
191                               /* get the number of half dB steps */
192                               *diff *= 2;
193                               /* change the original gain boundary settings
194                                * by the number of half dB steps
195                                */
196                               for (k = 0; k < numXpdGain; k++)
197                                         gb[k] = (uint16_t)(gb[k] - *diff);
198                     }
199                     /* Because of a hardware limitation, ensure the gain boundary
200                      * is not larger than (63 - overlap)
201                      */
202                     gb_limit = (uint16_t)(AR5416_MAX_RATE_POWER - pdGainOverlap_t2);
203 
204                     for (k = 0; k < numXpdGain; k++)
205                               gb[k] = (uint16_t)min(gb_limit, gb[k]);
206           }
207 
208           return *diff;
209 }
210 
211 static void
ar9280AdjustPDADCValues(struct ath_hal * ah,int8_t pwr_table_offset,int16_t diff,uint8_t * pdadcValues)212 ar9280AdjustPDADCValues(struct ath_hal *ah, int8_t pwr_table_offset,
213     int16_t diff, uint8_t *pdadcValues)
214 {
215 #define NUM_PDADC(diff) (AR5416_NUM_PDADC_VALUES - diff)
216           uint16_t k;
217 
218           /* If this is a board that has a pwrTableOffset that differs from
219            * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the
220            * pdadc vs pwr table needs to be adjusted prior to writing to the
221            * chip.
222            */
223           if (AR_SREV_MERLIN_20_OR_LATER(ah)) {
224                     if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) {
225                               /* shift the table to start at the new offset */
226                               for (k = 0; k < (uint16_t)NUM_PDADC(diff); k++ ) {
227                                         pdadcValues[k] = pdadcValues[k + diff];
228                               }
229 
230                               /* fill the back of the table */
231                               for (k = (uint16_t)NUM_PDADC(diff); k < NUM_PDADC(0); k++) {
232                                         pdadcValues[k] = pdadcValues[NUM_PDADC(diff)];
233                               }
234                     }
235           }
236 #undef NUM_PDADC
237 }
238 /*
239  * This effectively disables the gain boundaries leaving it
240  * to the open-loop TX power control.
241  */
242 static void
ar9280SetGainBoundariesOpenLoop(struct ath_hal * ah,int i,uint16_t pdGainOverlap_t2,uint16_t gainBoundaries[])243 ar9280SetGainBoundariesOpenLoop(struct ath_hal *ah, int i,
244     uint16_t pdGainOverlap_t2, uint16_t gainBoundaries[])
245 {
246           int regChainOffset;
247 
248           regChainOffset = ar5416GetRegChainOffset(ah, i);
249 
250           /* These are unused for OLC */
251           (void) pdGainOverlap_t2;
252           (void) gainBoundaries;
253 
254           HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: chain %d: writing closed loop values\n",
255               __func__, i);
256 
257           OS_REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset,
258               SM(0x6, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) |
259               SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1)  |
260               SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2)  |
261               SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3)  |
262               SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4));
263 }
264 
265 /* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */
266 /* XXX shouldn't be here! */
267 #define EEP_MINOR(_ah) \
268         (AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK)
269 #define IS_EEP_MINOR_V2(_ah)    (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2)
270 #define IS_EEP_MINOR_V3(_ah)    (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_3)
271 
272 /**************************************************************
273  * ar9280SetPowerCalTable
274  *
275  * Pull the PDADC piers from cal data and interpolate them across the given
276  * points as well as from the nearest pier(s) to get a power detector
277  * linear voltage to power level table.
278  *
279  * Handle OLC for Merlin where required.
280  */
281 HAL_BOOL
ar9280SetPowerCalTable(struct ath_hal * ah,struct ar5416eeprom * pEepData,const struct ieee80211_channel * chan,int16_t * pTxPowerIndexOffset)282 ar9280SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData,
283           const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset)
284 {
285           CAL_DATA_PER_FREQ *pRawDataset;
286           uint8_t  *pCalBChans = AH_NULL;
287           uint16_t pdGainOverlap_t2;
288           static uint8_t  pdadcValues[AR5416_NUM_PDADC_VALUES];
289           uint16_t gainBoundaries[AR5416_PD_GAINS_IN_MASK];
290           uint16_t numPiers, i;
291           int16_t  tMinCalPower;
292           uint16_t numXpdGain, xpdMask;
293           uint16_t xpdGainValues[AR5416_NUM_PD_GAINS];
294           uint32_t regChainOffset;
295           int8_t pwr_table_offset;
296 
297           OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues));
298 
299           xpdMask = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].xpdGain;
300 
301           (void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET, &pwr_table_offset);
302 
303 
304           if (IS_EEP_MINOR_V2(ah)) {
305                     pdGainOverlap_t2 = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pdGainOverlap;
306           } else {
307                     pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP));
308           }
309 
310           if (IEEE80211_IS_CHAN_2GHZ(chan)) {
311                     pCalBChans = pEepData->calFreqPier2G;
312                     numPiers = AR5416_NUM_2G_CAL_PIERS;
313           } else {
314                     pCalBChans = pEepData->calFreqPier5G;
315                     numPiers = AR5416_NUM_5G_CAL_PIERS;
316           }
317 
318           /* If OLC is being done, set the init PDADC value appropriately */
319           if (IEEE80211_IS_CHAN_2GHZ(chan) && AR_SREV_MERLIN_20_OR_LATER(ah) &&
320               ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) {
321                     struct calDataPerFreq *pRawDataset = pEepData->calPierData2G[0];
322                     AH5416(ah)->initPDADC = ((struct calDataPerFreqOpLoop *) pRawDataset)->vpdPdg[0][0];
323           } else {
324                     /*
325                      * XXX ath9k doesn't clear this for 5ghz mode if
326                      * it were set in 2ghz mode before!
327                      * The Merlin OLC temperature compensation code
328                      * uses this to calculate the PDADC delta during
329                      * calibration ; 0 here effectively stops the
330                      * temperature compensation calibration from
331                      * occurring.
332                      */
333                     AH5416(ah)->initPDADC = 0;
334           }
335 
336           /* Calculate the value of xpdgains from the xpdGain Mask */
337           numXpdGain = ar5416GetXpdGainValues(ah, xpdMask, xpdGainValues);
338 
339           /* Write the detector gain biases and their number */
340           ar5416WriteDetectorGainBiases(ah, numXpdGain, xpdGainValues);
341 
342           for (i = 0; i < AR5416_MAX_CHAINS; i++) {
343                     regChainOffset = ar5416GetRegChainOffset(ah, i);
344                     if (pEepData->baseEepHeader.txMask & (1 << i)) {
345                               uint16_t diff = 0;  /* quiet gcc error w/ass */
346 
347                               if (IEEE80211_IS_CHAN_2GHZ(chan)) {
348                                         pRawDataset = pEepData->calPierData2G[i];
349                               } else {
350                                         pRawDataset = pEepData->calPierData5G[i];
351                               }
352 
353                               /* Fetch the gain boundaries and the PDADC values */
354                               if (AR_SREV_MERLIN_20_OR_LATER(ah) &&
355                                   ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) {
356                                         uint8_t pcdacIdx;
357                                         uint8_t txPower;
358 
359                                         ar9280olcGetTxGainIndex(ah, chan,
360                                             (struct calDataPerFreqOpLoop *) pRawDataset,
361                                             pCalBChans, numPiers, &txPower, &pcdacIdx);
362                                         ar9280olcGetPDADCs(ah, pcdacIdx, txPower / 2, pdadcValues);
363                               } else {
364                                         ar5416GetGainBoundariesAndPdadcs(ah,  chan,
365                                             pRawDataset, pCalBChans, numPiers,
366                                             pdGainOverlap_t2, &tMinCalPower,
367                                             gainBoundaries, pdadcValues, numXpdGain);
368                               }
369 
370                               /*
371                                * Prior to writing the boundaries or the pdadc vs. power table
372                                * into the chip registers the default starting point on the pdadc
373                                * vs. power table needs to be checked and the curve boundaries
374                                * adjusted accordingly
375                                */
376                               diff = ar9280ChangeGainBoundarySettings(ah,
377                                   gainBoundaries, numXpdGain, pdGainOverlap_t2,
378                                   pwr_table_offset, &diff);
379 
380                               if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) {
381                                         /* Set gain boundaries for either open- or closed-loop TPC */
382                                         if (AR_SREV_MERLIN_20_OR_LATER(ah) &&
383                                             ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
384                                                   ar9280SetGainBoundariesOpenLoop(ah,
385                                                       i, pdGainOverlap_t2,
386                                                       gainBoundaries);
387                                         else
388                                                   ar5416SetGainBoundariesClosedLoop(ah,
389                                                       i, pdGainOverlap_t2,
390                                                       gainBoundaries);
391                               }
392 
393                               /*
394                                * If this is a board that has a pwrTableOffset that differs from
395                                * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the
396                                * pdadc vs pwr table needs to be adjusted prior to writing to the
397                                * chip.
398                                */
399                               ar9280AdjustPDADCValues(ah, pwr_table_offset, diff, pdadcValues);
400 
401                               /* Write the power values into the baseband power table */
402                               ar5416WritePdadcValues(ah, i, pdadcValues);
403                     }
404           }
405           *pTxPowerIndexOffset = 0;
406 
407           return AH_TRUE;
408 }
409