FreeBSD kernel ATH device code
ar9280_olc.c
Go to the documentation of this file.
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
43void
45{
46 uint32_t i;
47
48 /* Only do OLC if it's enabled for this chipset */
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,
57
58 AH9280(ah)->PDADCdelta = 0;
59}
60
61void
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] &&
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 */
101void
102ar9280olcGetPDADCs(struct ath_hal *ah, uint32_t initTxGain, int txPower,
103 uint8_t *pPDADCValues)
104{
105 uint32_t i;
106 uint32_t offset;
107
110
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 */
130void
132{
133 uint32_t rddata, i;
134 int delta, currPDADC, regval;
135 uint8_t hpwr_5g = 0;
136
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
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
168 AR_PHY_TX_GAIN_TBL1 + i * 4,
169 AR_PHY_TX_GAIN, regval);
170 }
171 }
172}
173
174static int16_t
176 uint16_t numXpdGain, uint16_t pdGainOverlap_t2, int8_t pwr_table_offset,
177 int16_t *diff)
178{
179 uint16_t k;
180
181 /* Prior to writing the boundaries or the pdadc vs. power table
182 * into the chip registers the default starting point on the pdadc
183 * vs. power table needs to be checked and the curve boundaries
184 * adjusted accordingly
185 */
187 uint16_t gb_limit;
188
189 if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) {
190 /* get the difference in dB */
191 *diff = (uint16_t)(pwr_table_offset - AR5416_PWR_TABLE_OFFSET_DB);
192 /* get the number of half dB steps */
193 *diff *= 2;
194 /* change the original gain boundary settings
195 * by the number of half dB steps
196 */
197 for (k = 0; k < numXpdGain; k++)
198 gb[k] = (uint16_t)(gb[k] - *diff);
199 }
200 /* Because of a hardware limitation, ensure the gain boundary
201 * is not larger than (63 - overlap)
202 */
203 gb_limit = (uint16_t)(AR5416_MAX_RATE_POWER - pdGainOverlap_t2);
204
205 for (k = 0; k < numXpdGain; k++)
206 gb[k] = (uint16_t)min(gb_limit, gb[k]);
207 }
208
209 return *diff;
210}
211
212static void
213ar9280AdjustPDADCValues(struct ath_hal *ah, int8_t pwr_table_offset,
214 int16_t diff, uint8_t *pdadcValues)
215{
216#define NUM_PDADC(diff) (AR5416_NUM_PDADC_VALUES - diff)
217 uint16_t k;
218
219 /* If this is a board that has a pwrTableOffset that differs from
220 * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the
221 * pdadc vs pwr table needs to be adjusted prior to writing to the
222 * chip.
223 */
225 if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) {
226 /* shift the table to start at the new offset */
227 for (k = 0; k < (uint16_t)NUM_PDADC(diff); k++ ) {
228 pdadcValues[k] = pdadcValues[k + diff];
229 }
230
231 /* fill the back of the table */
232 for (k = (uint16_t)NUM_PDADC(diff); k < NUM_PDADC(0); k++) {
233 pdadcValues[k] = pdadcValues[NUM_PDADC(diff)];
234 }
235 }
236 }
237#undef NUM_PDADC
238}
239/*
240 * This effectively disables the gain boundaries leaving it
241 * to the open-loop TX power control.
242 */
243static void
245 uint16_t pdGainOverlap_t2, uint16_t gainBoundaries[])
246{
247 int regChainOffset;
248
249 regChainOffset = ar5416GetRegChainOffset(ah, i);
250
251 /* These are unused for OLC */
252 (void) pdGainOverlap_t2;
253 (void) gainBoundaries;
254
255 HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: chain %d: writing closed loop values\n",
256 __func__, i);
257
258 OS_REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset,
264}
265
266/* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */
267/* XXX shouldn't be here! */
268#define EEP_MINOR(_ah) \
269 (AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK)
270#define IS_EEP_MINOR_V2(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2)
271#define IS_EEP_MINOR_V3(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_3)
272
273/**************************************************************
274 * ar9280SetPowerCalTable
275 *
276 * Pull the PDADC piers from cal data and interpolate them across the given
277 * points as well as from the nearest pier(s) to get a power detector
278 * linear voltage to power level table.
279 *
280 * Handle OLC for Merlin where required.
281 */
283ar9280SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData,
284 const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset)
285{
286 CAL_DATA_PER_FREQ *pRawDataset;
287 uint8_t *pCalBChans = AH_NULL;
288 uint16_t pdGainOverlap_t2;
289 static uint8_t pdadcValues[AR5416_NUM_PDADC_VALUES];
290 uint16_t gainBoundaries[AR5416_PD_GAINS_IN_MASK];
291 uint16_t numPiers, i;
292 int16_t tMinCalPower;
293 uint16_t numXpdGain, xpdMask;
294 uint16_t xpdGainValues[AR5416_NUM_PD_GAINS];
295 uint32_t regChainOffset;
296 int8_t pwr_table_offset;
297
298 OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues));
299
300 xpdMask = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].xpdGain;
301
302 (void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET, &pwr_table_offset);
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) &&
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;
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 */
356 uint8_t pcdacIdx;
357 uint8_t txPower;
358
360 (struct calDataPerFreqOpLoop *) pRawDataset,
361 pCalBChans, numPiers, &txPower, &pcdacIdx);
362 ar9280olcGetPDADCs(ah, pcdacIdx, txPower / 2, pdadcValues);
363 } else {
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 */
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 */
385 i, pdGainOverlap_t2,
386 gainBoundaries);
387 else
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}
HAL_BOOL ath_ee_getLowerUpperIndex(uint8_t target, uint8_t *pList, uint16_t listSize, uint16_t *indexL, uint16_t *indexR)
Definition: ah.c:1369
HAL_BOOL
Definition: ah.h:93
@ AH_TRUE
Definition: ah.h:95
@ HAL_DEBUG_RESET
Definition: ah_debug.h:31
@ HAL_DEBUG_PERCAL
Definition: ah_debug.h:33
@ HAL_DEBUG_EEPROM
Definition: ah_debug.h:44
@ AR_EEP_PWR_TABLE_OFFSET
Definition: ah_eeprom.h:105
@ AR_EEP_DAC_HPWR_5G
Definition: ah_eeprom.h:99
@ AR_EEP_OL_PWRCTRL
Definition: ah_eeprom.h:100
#define AR5416_NUM_2G_CAL_PIERS
Definition: ah_eeprom_v14.h:65
#define AR5416_NUM_5G_CAL_PIERS
Definition: ah_eeprom_v14.h:64
#define AR5416_NUM_PDADC_VALUES
Definition: ah_eeprom_v14.h:78
#define FREQ2FBIN(x, y)
Definition: ah_eeprom_v14.h:83
#define AR5416_MAX_RATE_POWER
Definition: ah_eeprom_v14.h:77
#define AR5416_NUM_PD_GAINS
Definition: ah_eeprom_v14.h:73
#define AR5416_PWR_TABLE_OFFSET_DB
Definition: ah_eeprom_v14.h:85
#define AR5416_MAX_CHAINS
Definition: ah_eeprom_v14.h:84
#define AR5416_BCHAN_UNUSED
Definition: ah_eeprom_v14.h:80
#define AR5416_PD_GAINS_IN_MASK
Definition: ah_eeprom_v14.h:74
#define SM(_v, _f)
Definition: ah_internal.h:587
#define MS(_v, _f)
Definition: ah_internal.h:588
#define ath_hal_eepromGet(_ah, _param, _val)
Definition: ah_internal.h:486
#define OS_REG_RMW_FIELD(_a, _r, _f, _v)
Definition: ah_internal.h:591
#define AH_NULL
Definition: ah_internal.h:28
#define HALDEBUG(_ah, __m,...)
Definition: ah_internal.h:658
#define ath_hal_eepromGetFlag(_ah, _param)
Definition: ah_internal.h:490
#define OS_MEMZERO(_a, _n)
Definition: ah_osdep.h:72
#define OS_REG_WRITE(_ah, _reg, _val)
Definition: ah_osdep.h:139
#define OS_REG_READ(_ah, _reg)
Definition: ah_osdep.h:140
#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4
Definition: ar5212phy.h:358
#define AR_PHY_TPCRG5
Definition: ar5212phy.h:349
#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2
Definition: ar5212phy.h:354
#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1
Definition: ar5212phy.h:352
#define AR_PHY_TPCRG5_PD_GAIN_OVERLAP
Definition: ar5212phy.h:350
#define AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3
Definition: ar5212phy.h:356
void ar5416SetGainBoundariesClosedLoop(struct ath_hal *ah, int i, uint16_t pdGainOverlap_t2, uint16_t gainBoundaries[])
void ar5416GetChannelCenters(struct ath_hal *, const struct ieee80211_channel *chan, CHAN_CENTERS *centers)
void ar5416WriteDetectorGainBiases(struct ath_hal *ah, uint16_t numXpdGain, uint16_t xpdGainValues[])
#define AH5416(_ah)
Definition: ar5416.h:162
void ar5416WritePdadcValues(struct ath_hal *ah, int i, uint8_t pdadcValues[])
uint16_t ar5416GetXpdGainValues(struct ath_hal *ah, uint16_t xpdMask, uint16_t xpdGainValues[])
void ar5416GetGainBoundariesAndPdadcs(struct ath_hal *ah, const struct ieee80211_channel *chan, CAL_DATA_PER_FREQ *pRawDataSet, uint8_t *bChans, uint16_t availPiers, uint16_t tPdGainOverlap, int16_t *pMinCalPower, uint16_t *pPdGainBoundaries, uint8_t *pPDADCValues, uint16_t numXpdGains)
int ar5416GetRegChainOffset(struct ath_hal *ah, int i)
#define AR_SREV_5416_V20_OR_LATER(_ah)
Definition: ar5416reg.h:653
#define AR_SREV_MERLIN_20_OR_LATER(_ah)
Definition: ar5416reg.h:747
#define AR_PHY_TX_PWRCTRL4
Definition: ar9002phy.h:32
#define AR_PHY_TX_PWRCTRL6_1
Definition: ar9002phy.h:39
#define AR_PHY_TX_PWRCTRL7
Definition: ar9002phy.h:43
#define AR_PHY_TX_PWRCTRL_ERR_EST_MODE
Definition: ar9002phy.h:40
#define AR_PHY_TX_PWRCTRL6_0
Definition: ar9002phy.h:38
#define AR_PHY_TX_GAIN_TBL1
Definition: ar9002phy.h:50
#define AR_PHY_TX_GAIN
Definition: ar9002phy.h:51
#define AR_PHY_TX_PWRCTRL_PD_AVG_OUT
Definition: ar9002phy.h:35
#define AR_PHY_TX_PWRCTRL_INIT_TX_GAIN
Definition: ar9002phy.h:44
#define AR9280_TX_GAIN_TABLE_SIZE
Definition: ar9280.h:30
#define AH9280(_ah)
Definition: ar9280.h:43
static void ar9280AdjustPDADCValues(struct ath_hal *ah, int8_t pwr_table_offset, int16_t diff, uint8_t *pdadcValues)
Definition: ar9280_olc.c:213
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)
Definition: ar9280_olc.c:175
static void ar9280SetGainBoundariesOpenLoop(struct ath_hal *ah, int i, uint16_t pdGainOverlap_t2, uint16_t gainBoundaries[])
Definition: ar9280_olc.c:244
HAL_BOOL ar9280SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset)
Definition: ar9280_olc.c:283
void ar9280olcTemperatureCompensation(struct ath_hal *ah)
Definition: ar9280_olc.c:131
void ar9280olcInit(struct ath_hal *ah)
Definition: ar9280_olc.c:44
#define NUM_PDADC(diff)
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)
Definition: ar9280_olc.c:62
void ar9280olcGetPDADCs(struct ath_hal *ah, uint32_t initTxGain, int txPower, uint8_t *pPDADCValues)
Definition: ar9280_olc.c:102
#define IS_EEP_MINOR_V2(_ah)
Definition: ar9280_olc.c:270
uint16_t synth_center
Definition: ar5416.h:31
uint8_t pdGainOverlap
BASE_EEP_HEADER baseEepHeader
MODAL_EEP_HEADER modalHeader[2]
CAL_DATA_PER_FREQ calPierData2G[AR5416_MAX_CHAINS][AR5416_NUM_2G_CAL_PIERS]
uint8_t calFreqPier2G[AR5416_NUM_2G_CAL_PIERS]
CAL_DATA_PER_FREQ calPierData5G[AR5416_MAX_CHAINS][AR5416_NUM_5G_CAL_PIERS]
uint8_t calFreqPier5G[AR5416_NUM_5G_CAL_PIERS]
Definition: ah.h:1219
uint8_t pcdac[2][5]
uint8_t pwrPdg[2][5]