2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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.
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
32 #include "ah_internal.h"
34 #include "ah_eeprom_v14.h"
36 #include "ar9002/ar9280.h"
37 #include "ar5416/ar5416reg.h"
38 #include "ar5416/ar5416phy.h"
39 #include "ar9002/ar9002phy.h"
41 #include "ar9002/ar9280_olc.h"
44 ar9280olcInit(struct ath_hal *ah)
48 /* Only do OLC if it's enabled for this chipset */
49 if (! ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
52 HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Setting up TX gain tables.\n", __func__);
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);
58 AH9280(ah)->PDADCdelta = 0;
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)
68 uint16_t idxL = 0, idxR = 0, numPiers;
72 ar5416GetChannelCenters(ah, chan, ¢ers);
74 for (numPiers = 0; numPiers < availPiers; numPiers++)
75 if (calChans[numPiers] == AR5416_BCHAN_UNUSED)
78 match = ath_ee_getLowerUpperIndex((uint8_t)FREQ2FBIN(centers.synth_center,
79 IEEE80211_IS_CHAN_2GHZ(chan)), calChans, numPiers,
82 pcdac = rawDatasetOpLoop[idxL].pcdac[0][0];
83 *pwr = rawDatasetOpLoop[idxL].pwrPdg[0][0];
85 pcdac = rawDatasetOpLoop[idxR].pcdac[0][0];
86 *pwr = (rawDatasetOpLoop[idxL].pwrPdg[0][0] +
87 rawDatasetOpLoop[idxR].pwrPdg[0][0])/2;
89 while (pcdac > AH9280(ah)->originalGain[i] &&
90 i < (AR9280_TX_GAIN_TABLE_SIZE - 1))
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.
102 ar9280olcGetPDADCs(struct ath_hal *ah, uint32_t initTxGain, int txPower,
103 uint8_t *pPDADCValues)
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);
111 OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL7, AR_PHY_TX_PWRCTRL_INIT_TX_GAIN, initTxGain);
114 for (i = 0; i < AR5416_NUM_PDADC_VALUES; i++)
116 pPDADCValues[i] = 0x0;
118 pPDADCValues[i] = 0xFF;
122 * Run temperature compensation calibration.
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.
131 ar9280olcTemperatureCompensation(struct ath_hal *ah)
134 int delta, currPDADC, regval;
137 if (! ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
140 rddata = OS_REG_READ(ah, AR_PHY_TX_PWRCTRL4);
141 currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT);
143 HALDEBUG(ah, HAL_DEBUG_PERCAL,
144 "%s: called: initPDADC=%d, currPDADC=%d\n",
145 __func__, AH5416(ah)->initPDADC, currPDADC);
147 if (AH5416(ah)->initPDADC == 0 || currPDADC == 0)
150 (void) (ath_hal_eepromGet(ah, AR_EEP_DAC_HPWR_5G, &hpwr_5g));
153 delta = (currPDADC - AH5416(ah)->initPDADC + 4) / 8;
155 delta = (currPDADC - AH5416(ah)->initPDADC + 5) / 10;
157 HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: delta=%d, PDADCdelta=%d\n",
158 __func__, delta, AH9280(ah)->PDADCdelta);
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;
168 AR_PHY_TX_GAIN_TBL1 + i * 4,
169 AR_PHY_TX_GAIN, regval);
176 ar9280ChangeGainBoundarySettings(struct ath_hal *ah, uint16_t *gb,
177 uint16_t numXpdGain, uint16_t pdGainOverlap_t2, int8_t pwr_table_offset,
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
187 if (AR_SREV_MERLIN_20_OR_LATER(ah)) {
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 */
195 /* change the original gain boundary settings
196 * by the number of half dB steps
198 for (k = 0; k < numXpdGain; k++)
199 gb[k] = (uint16_t)(gb[k] - *diff);
201 /* Because of a hardware limitation, ensure the gain boundary
202 * is not larger than (63 - overlap)
204 gb_limit = (uint16_t)(AR5416_MAX_RATE_POWER - pdGainOverlap_t2);
206 for (k = 0; k < numXpdGain; k++)
207 gb[k] = (uint16_t)min(gb_limit, gb[k]);
214 ar9280AdjustPDADCValues(struct ath_hal *ah, int8_t pwr_table_offset,
215 int16_t diff, uint8_t *pdadcValues)
217 #define NUM_PDADC(diff) (AR5416_NUM_PDADC_VALUES - diff)
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
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];
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)];
241 * This effectively disables the gain boundaries leaving it
242 * to the open-loop TX power control.
245 ar9280SetGainBoundariesOpenLoop(struct ath_hal *ah, int i,
246 uint16_t pdGainOverlap_t2, uint16_t gainBoundaries[])
250 regChainOffset = ar5416GetRegChainOffset(ah, i);
252 /* These are unused for OLC */
253 (void) pdGainOverlap_t2;
254 (void) gainBoundaries;
256 HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: chain %d: writing closed loop values\n",
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));
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)
274 /**************************************************************
275 * ar9280SetPowerCalTable
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.
281 * Handle OLC for Merlin where required.
284 ar9280SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData,
285 const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset)
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;
299 OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues));
301 xpdMask = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].xpdGain;
303 (void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET, &pwr_table_offset);
306 if (IS_EEP_MINOR_V2(ah)) {
307 pdGainOverlap_t2 = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pdGainOverlap;
309 pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP));
312 if (IEEE80211_IS_CHAN_2GHZ(chan)) {
313 pCalBChans = pEepData->calFreqPier2G;
314 numPiers = AR5416_NUM_2G_CAL_PIERS;
316 pCalBChans = pEepData->calFreqPier5G;
317 numPiers = AR5416_NUM_5G_CAL_PIERS;
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];
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
335 AH5416(ah)->initPDADC = 0;
338 /* Calculate the value of xpdgains from the xpdGain Mask */
339 numXpdGain = ar5416GetXpdGainValues(ah, xpdMask, xpdGainValues);
341 /* Write the detector gain biases and their number */
342 ar5416WriteDetectorGainBiases(ah, numXpdGain, xpdGainValues);
344 for (i = 0; i < AR5416_MAX_CHAINS; i++) {
345 regChainOffset = ar5416GetRegChainOffset(ah, i);
346 if (pEepData->baseEepHeader.txMask & (1 << i)) {
349 if (IEEE80211_IS_CHAN_2GHZ(chan)) {
350 pRawDataset = pEepData->calPierData2G[i];
352 pRawDataset = pEepData->calPierData5G[i];
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)) {
361 ar9280olcGetTxGainIndex(ah, chan,
362 (struct calDataPerFreqOpLoop *) pRawDataset,
363 pCalBChans, numPiers, &txPower, &pcdacIdx);
364 ar9280olcGetPDADCs(ah, pcdacIdx, txPower / 2, pdadcValues);
366 ar5416GetGainBoundariesAndPdadcs(ah, chan,
367 pRawDataset, pCalBChans, numPiers,
368 pdGainOverlap_t2, &tMinCalPower,
369 gainBoundaries, pdadcValues, numXpdGain);
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
378 diff = ar9280ChangeGainBoundarySettings(ah,
379 gainBoundaries, numXpdGain, pdGainOverlap_t2,
380 pwr_table_offset, &diff);
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,
390 ar5416SetGainBoundariesClosedLoop(ah,
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
401 ar9280AdjustPDADCValues(ah, pwr_table_offset, diff, pdadcValues);
403 /* Write the power values into the baseband power table */
404 ar5416WritePdadcValues(ah, i, pdadcValues);
407 *pTxPowerIndexOffset = 0;