Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/sys/dev/ath/ath_hal/ar9002/ar9280_olc.c
39566 views
1
/*-
2
* SPDX-License-Identifier: BSD-2-Clause
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
#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
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
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 (pcdac > AH9280(ah)->originalGain[i] &&
88
i < (AR9280_TX_GAIN_TABLE_SIZE - 1))
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
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
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
static int16_t
173
ar9280ChangeGainBoundarySettings(struct ath_hal *ah, uint16_t *gb,
174
uint16_t numXpdGain, uint16_t pdGainOverlap_t2, int8_t pwr_table_offset,
175
int16_t *diff)
176
{
177
uint16_t k;
178
179
/* Prior to writing the boundaries or the pdadc vs. power table
180
* into the chip registers the default starting point on the pdadc
181
* vs. power table needs to be checked and the curve boundaries
182
* adjusted accordingly
183
*/
184
if (AR_SREV_MERLIN_20_OR_LATER(ah)) {
185
uint16_t gb_limit;
186
187
if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) {
188
/* get the difference in dB */
189
*diff = (uint16_t)(pwr_table_offset - AR5416_PWR_TABLE_OFFSET_DB);
190
/* get the number of half dB steps */
191
*diff *= 2;
192
/* change the original gain boundary settings
193
* by the number of half dB steps
194
*/
195
for (k = 0; k < numXpdGain; k++)
196
gb[k] = (uint16_t)(gb[k] - *diff);
197
}
198
/* Because of a hardware limitation, ensure the gain boundary
199
* is not larger than (63 - overlap)
200
*/
201
gb_limit = (uint16_t)(AR5416_MAX_RATE_POWER - pdGainOverlap_t2);
202
203
for (k = 0; k < numXpdGain; k++)
204
gb[k] = (uint16_t)min(gb_limit, gb[k]);
205
}
206
207
return *diff;
208
}
209
210
static void
211
ar9280AdjustPDADCValues(struct ath_hal *ah, int8_t pwr_table_offset,
212
int16_t diff, uint8_t *pdadcValues)
213
{
214
#define NUM_PDADC(diff) (AR5416_NUM_PDADC_VALUES - diff)
215
uint16_t k;
216
217
/* If this is a board that has a pwrTableOffset that differs from
218
* the default AR5416_PWR_TABLE_OFFSET_DB then the start of the
219
* pdadc vs pwr table needs to be adjusted prior to writing to the
220
* chip.
221
*/
222
if (AR_SREV_MERLIN_20_OR_LATER(ah)) {
223
if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) {
224
/* shift the table to start at the new offset */
225
for (k = 0; k < (uint16_t)NUM_PDADC(diff); k++ ) {
226
pdadcValues[k] = pdadcValues[k + diff];
227
}
228
229
/* fill the back of the table */
230
for (k = (uint16_t)NUM_PDADC(diff); k < NUM_PDADC(0); k++) {
231
pdadcValues[k] = pdadcValues[NUM_PDADC(diff)];
232
}
233
}
234
}
235
#undef NUM_PDADC
236
}
237
/*
238
* This effectively disables the gain boundaries leaving it
239
* to the open-loop TX power control.
240
*/
241
static void
242
ar9280SetGainBoundariesOpenLoop(struct ath_hal *ah, int i,
243
uint16_t pdGainOverlap_t2, uint16_t gainBoundaries[])
244
{
245
int regChainOffset;
246
247
regChainOffset = ar5416GetRegChainOffset(ah, i);
248
249
/* These are unused for OLC */
250
(void) pdGainOverlap_t2;
251
(void) gainBoundaries;
252
253
HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: chain %d: writing closed loop values\n",
254
__func__, i);
255
256
OS_REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset,
257
SM(0x6, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) |
258
SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) |
259
SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) |
260
SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) |
261
SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4));
262
}
263
264
/* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */
265
/* XXX shouldn't be here! */
266
#define EEP_MINOR(_ah) \
267
(AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK)
268
#define IS_EEP_MINOR_V2(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2)
269
#define IS_EEP_MINOR_V3(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_3)
270
271
/**************************************************************
272
* ar9280SetPowerCalTable
273
*
274
* Pull the PDADC piers from cal data and interpolate them across the given
275
* points as well as from the nearest pier(s) to get a power detector
276
* linear voltage to power level table.
277
*
278
* Handle OLC for Merlin where required.
279
*/
280
HAL_BOOL
281
ar9280SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData,
282
const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset)
283
{
284
CAL_DATA_PER_FREQ *pRawDataset;
285
uint8_t *pCalBChans = AH_NULL;
286
uint16_t pdGainOverlap_t2;
287
static uint8_t pdadcValues[AR5416_NUM_PDADC_VALUES];
288
uint16_t gainBoundaries[AR5416_PD_GAINS_IN_MASK];
289
uint16_t numPiers, i;
290
int16_t tMinCalPower;
291
uint16_t numXpdGain, xpdMask;
292
uint16_t xpdGainValues[AR5416_NUM_PD_GAINS];
293
uint32_t regChainOffset;
294
int8_t pwr_table_offset;
295
296
OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues));
297
298
xpdMask = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].xpdGain;
299
300
(void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET, &pwr_table_offset);
301
302
if (IS_EEP_MINOR_V2(ah)) {
303
pdGainOverlap_t2 = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pdGainOverlap;
304
} else {
305
pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP));
306
}
307
308
if (IEEE80211_IS_CHAN_2GHZ(chan)) {
309
pCalBChans = pEepData->calFreqPier2G;
310
numPiers = AR5416_NUM_2G_CAL_PIERS;
311
} else {
312
pCalBChans = pEepData->calFreqPier5G;
313
numPiers = AR5416_NUM_5G_CAL_PIERS;
314
}
315
316
/* If OLC is being done, set the init PDADC value appropriately */
317
if (IEEE80211_IS_CHAN_2GHZ(chan) && AR_SREV_MERLIN_20_OR_LATER(ah) &&
318
ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) {
319
struct calDataPerFreq *pRawDataset = pEepData->calPierData2G[0];
320
AH5416(ah)->initPDADC = ((struct calDataPerFreqOpLoop *) pRawDataset)->vpdPdg[0][0];
321
} else {
322
/*
323
* XXX ath9k doesn't clear this for 5ghz mode if
324
* it were set in 2ghz mode before!
325
* The Merlin OLC temperature compensation code
326
* uses this to calculate the PDADC delta during
327
* calibration ; 0 here effectively stops the
328
* temperature compensation calibration from
329
* occurring.
330
*/
331
AH5416(ah)->initPDADC = 0;
332
}
333
334
/* Calculate the value of xpdgains from the xpdGain Mask */
335
numXpdGain = ar5416GetXpdGainValues(ah, xpdMask, xpdGainValues);
336
337
/* Write the detector gain biases and their number */
338
ar5416WriteDetectorGainBiases(ah, numXpdGain, xpdGainValues);
339
340
for (i = 0; i < AR5416_MAX_CHAINS; i++) {
341
regChainOffset = ar5416GetRegChainOffset(ah, i);
342
if (pEepData->baseEepHeader.txMask & (1 << i)) {
343
uint16_t diff;
344
345
if (IEEE80211_IS_CHAN_2GHZ(chan)) {
346
pRawDataset = pEepData->calPierData2G[i];
347
} else {
348
pRawDataset = pEepData->calPierData5G[i];
349
}
350
351
/* Fetch the gain boundaries and the PDADC values */
352
if (AR_SREV_MERLIN_20_OR_LATER(ah) &&
353
ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) {
354
uint8_t pcdacIdx;
355
uint8_t txPower;
356
357
ar9280olcGetTxGainIndex(ah, chan,
358
(struct calDataPerFreqOpLoop *) pRawDataset,
359
pCalBChans, numPiers, &txPower, &pcdacIdx);
360
ar9280olcGetPDADCs(ah, pcdacIdx, txPower / 2, pdadcValues);
361
} else {
362
ar5416GetGainBoundariesAndPdadcs(ah, chan,
363
pRawDataset, pCalBChans, numPiers,
364
pdGainOverlap_t2, &tMinCalPower,
365
gainBoundaries, pdadcValues, numXpdGain);
366
}
367
368
/*
369
* Prior to writing the boundaries or the pdadc vs. power table
370
* into the chip registers the default starting point on the pdadc
371
* vs. power table needs to be checked and the curve boundaries
372
* adjusted accordingly
373
*/
374
diff = ar9280ChangeGainBoundarySettings(ah,
375
gainBoundaries, numXpdGain, pdGainOverlap_t2,
376
pwr_table_offset, &diff);
377
378
if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) {
379
/* Set gain boundaries for either open- or closed-loop TPC */
380
if (AR_SREV_MERLIN_20_OR_LATER(ah) &&
381
ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
382
ar9280SetGainBoundariesOpenLoop(ah,
383
i, pdGainOverlap_t2,
384
gainBoundaries);
385
else
386
ar5416SetGainBoundariesClosedLoop(ah,
387
i, pdGainOverlap_t2,
388
gainBoundaries);
389
}
390
391
/*
392
* If this is a board that has a pwrTableOffset that differs from
393
* the default AR5416_PWR_TABLE_OFFSET_DB then the start of the
394
* pdadc vs pwr table needs to be adjusted prior to writing to the
395
* chip.
396
*/
397
ar9280AdjustPDADCValues(ah, pwr_table_offset, diff, pdadcValues);
398
399
/* Write the power values into the baseband power table */
400
ar5416WritePdadcValues(ah, i, pdadcValues);
401
}
402
}
403
*pTxPowerIndexOffset = 0;
404
405
return AH_TRUE;
406
}
407
408