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