xref: /freebsd/sys/dev/ath/ath_hal/ar9002/ar9280_olc.c (revision 95ee2897e98f5d444f26ed2334cc7c439f9c16c6)
1718cf2ccSPedro F. Giffuni /*-
2*4d846d26SWarner Losh  * SPDX-License-Identifier: BSD-2-Clause
3718cf2ccSPedro F. Giffuni  *
448c1d364SAdrian Chadd  * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.
548c1d364SAdrian Chadd  *
648c1d364SAdrian Chadd  * Redistribution and use in source and binary forms, with or without
748c1d364SAdrian Chadd  * modification, are permitted provided that the following conditions
848c1d364SAdrian Chadd  * are met:
948c1d364SAdrian Chadd  * 1. Redistributions of source code must retain the above copyright
1048c1d364SAdrian Chadd  *    notice, this list of conditions and the following disclaimer.
1148c1d364SAdrian Chadd  * 2. Redistributions in binary form must reproduce the above copyright
1248c1d364SAdrian Chadd  *    notice, this list of conditions and the following disclaimer in the
1348c1d364SAdrian Chadd  *    documentation and/or other materials provided with the distribution.
1448c1d364SAdrian Chadd  *
1548c1d364SAdrian Chadd  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
1648c1d364SAdrian Chadd  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
1748c1d364SAdrian Chadd  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
1848c1d364SAdrian Chadd  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
1948c1d364SAdrian Chadd  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
2048c1d364SAdrian Chadd  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
2148c1d364SAdrian Chadd  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
2248c1d364SAdrian Chadd  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
2348c1d364SAdrian Chadd  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
2448c1d364SAdrian Chadd  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
2548c1d364SAdrian Chadd  * SUCH DAMAGE.
2648c1d364SAdrian Chadd  */
2748c1d364SAdrian Chadd #include "opt_ah.h"
2848c1d364SAdrian Chadd 
2948c1d364SAdrian Chadd #include "ah.h"
3048c1d364SAdrian Chadd #include "ah_internal.h"
3148c1d364SAdrian Chadd 
3248c1d364SAdrian Chadd #include "ah_eeprom_v14.h"
3348c1d364SAdrian Chadd 
3448c1d364SAdrian Chadd #include "ar9002/ar9280.h"
3548c1d364SAdrian Chadd #include "ar5416/ar5416reg.h"
3648c1d364SAdrian Chadd #include "ar5416/ar5416phy.h"
3748c1d364SAdrian Chadd #include "ar9002/ar9002phy.h"
3848c1d364SAdrian Chadd 
3948c1d364SAdrian Chadd #include "ar9002/ar9280_olc.h"
4048c1d364SAdrian Chadd 
4148c1d364SAdrian Chadd void
ar9280olcInit(struct ath_hal * ah)4248c1d364SAdrian Chadd ar9280olcInit(struct ath_hal *ah)
4348c1d364SAdrian Chadd {
4448c1d364SAdrian Chadd 	uint32_t i;
4548c1d364SAdrian Chadd 
464342b610SAdrian Chadd 	/* Only do OLC if it's enabled for this chipset */
474342b610SAdrian Chadd 	if (! ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
484342b610SAdrian Chadd 		return;
494342b610SAdrian Chadd 
504342b610SAdrian Chadd 	HALDEBUG(ah, HAL_DEBUG_RESET, "%s: Setting up TX gain tables.\n", __func__);
514342b610SAdrian Chadd 
5248c1d364SAdrian Chadd 	for (i = 0; i < AR9280_TX_GAIN_TABLE_SIZE; i++)
5348c1d364SAdrian Chadd 		AH9280(ah)->originalGain[i] = MS(OS_REG_READ(ah,
5448c1d364SAdrian Chadd 		    AR_PHY_TX_GAIN_TBL1 + i * 4), AR_PHY_TX_GAIN);
5548c1d364SAdrian Chadd 
5648c1d364SAdrian Chadd 	AH9280(ah)->PDADCdelta = 0;
5748c1d364SAdrian Chadd }
5848c1d364SAdrian Chadd 
5948c1d364SAdrian Chadd 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)6048c1d364SAdrian Chadd ar9280olcGetTxGainIndex(struct ath_hal *ah,
6148c1d364SAdrian Chadd     const struct ieee80211_channel *chan,
6248c1d364SAdrian Chadd     struct calDataPerFreqOpLoop *rawDatasetOpLoop,
6348c1d364SAdrian Chadd     uint8_t *calChans, uint16_t availPiers, uint8_t *pwr, uint8_t *pcdacIdx)
6448c1d364SAdrian Chadd {
6548c1d364SAdrian Chadd 	uint8_t pcdac, i = 0;
6648c1d364SAdrian Chadd 	uint16_t idxL = 0, idxR = 0, numPiers;
6748c1d364SAdrian Chadd 	HAL_BOOL match;
6848c1d364SAdrian Chadd 	CHAN_CENTERS centers;
6948c1d364SAdrian Chadd 
7048c1d364SAdrian Chadd 	ar5416GetChannelCenters(ah, chan, &centers);
7148c1d364SAdrian Chadd 
7248c1d364SAdrian Chadd 	for (numPiers = 0; numPiers < availPiers; numPiers++)
7348c1d364SAdrian Chadd 		if (calChans[numPiers] == AR5416_BCHAN_UNUSED)
7448c1d364SAdrian Chadd 			break;
7548c1d364SAdrian Chadd 
766ff1b2bdSAdrian Chadd 	match = ath_ee_getLowerUpperIndex((uint8_t)FREQ2FBIN(centers.synth_center,
7748c1d364SAdrian Chadd 		    IEEE80211_IS_CHAN_2GHZ(chan)), calChans, numPiers,
7848c1d364SAdrian Chadd 		    &idxL, &idxR);
7948c1d364SAdrian Chadd 	if (match) {
8048c1d364SAdrian Chadd 		pcdac = rawDatasetOpLoop[idxL].pcdac[0][0];
8148c1d364SAdrian Chadd 		*pwr = rawDatasetOpLoop[idxL].pwrPdg[0][0];
8248c1d364SAdrian Chadd 	} else {
8348c1d364SAdrian Chadd 		pcdac = rawDatasetOpLoop[idxR].pcdac[0][0];
8448c1d364SAdrian Chadd 		*pwr = (rawDatasetOpLoop[idxL].pwrPdg[0][0] +
8548c1d364SAdrian Chadd 				rawDatasetOpLoop[idxR].pwrPdg[0][0])/2;
8648c1d364SAdrian Chadd 	}
8748c1d364SAdrian Chadd 	while (pcdac > AH9280(ah)->originalGain[i] &&
8848c1d364SAdrian Chadd 			i < (AR9280_TX_GAIN_TABLE_SIZE - 1))
8948c1d364SAdrian Chadd 		i++;
9048c1d364SAdrian Chadd 
9148c1d364SAdrian Chadd 	*pcdacIdx = i;
9248c1d364SAdrian Chadd }
9348c1d364SAdrian Chadd 
9448c1d364SAdrian Chadd /*
9548c1d364SAdrian Chadd  * XXX txPower here is likely not the target txPower in the traditional
9648c1d364SAdrian Chadd  * XXX sense, but is set by a call to ar9280olcGetTxGainIndex().
9748c1d364SAdrian Chadd  * XXX Thus, be careful if you're trying to use this routine yourself.
9848c1d364SAdrian Chadd  */
9948c1d364SAdrian Chadd void
ar9280olcGetPDADCs(struct ath_hal * ah,uint32_t initTxGain,int txPower,uint8_t * pPDADCValues)10048c1d364SAdrian Chadd ar9280olcGetPDADCs(struct ath_hal *ah, uint32_t initTxGain, int txPower,
10148c1d364SAdrian Chadd     uint8_t *pPDADCValues)
10248c1d364SAdrian Chadd {
10348c1d364SAdrian Chadd 	uint32_t i;
10448c1d364SAdrian Chadd 	uint32_t offset;
10548c1d364SAdrian Chadd 
10648c1d364SAdrian Chadd 	OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_0, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3);
10748c1d364SAdrian Chadd 	OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_1, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3);
10848c1d364SAdrian Chadd 
10948c1d364SAdrian Chadd 	OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL7, AR_PHY_TX_PWRCTRL_INIT_TX_GAIN, initTxGain);
11048c1d364SAdrian Chadd 
11148c1d364SAdrian Chadd 	offset = txPower;
11248c1d364SAdrian Chadd 	for (i = 0; i < AR5416_NUM_PDADC_VALUES; i++)
11348c1d364SAdrian Chadd 		if (i < offset)
11448c1d364SAdrian Chadd 			pPDADCValues[i] = 0x0;
11548c1d364SAdrian Chadd 		else
11648c1d364SAdrian Chadd 			pPDADCValues[i] = 0xFF;
11748c1d364SAdrian Chadd }
11848c1d364SAdrian Chadd 
11948c1d364SAdrian Chadd /*
12048c1d364SAdrian Chadd  * Run temperature compensation calibration.
12148c1d364SAdrian Chadd  *
12248c1d364SAdrian Chadd  * The TX gain table is adjusted depending upon the difference
12348c1d364SAdrian Chadd  * between the initial PDADC value and the currently read
12448c1d364SAdrian Chadd  * average TX power sample value. This value is only valid if
12548c1d364SAdrian Chadd  * frames have been transmitted, so currPDADC will be 0 if
12648c1d364SAdrian Chadd  * no frames have yet been transmitted.
12748c1d364SAdrian Chadd  */
12848c1d364SAdrian Chadd void
ar9280olcTemperatureCompensation(struct ath_hal * ah)12948c1d364SAdrian Chadd ar9280olcTemperatureCompensation(struct ath_hal *ah)
13048c1d364SAdrian Chadd {
13148c1d364SAdrian Chadd 	uint32_t rddata, i;
13248c1d364SAdrian Chadd 	int delta, currPDADC, regval;
13348c1d364SAdrian Chadd 	uint8_t hpwr_5g = 0;
13448c1d364SAdrian Chadd 
1354342b610SAdrian Chadd 	if (! ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
1364342b610SAdrian Chadd 		return;
1374342b610SAdrian Chadd 
13848c1d364SAdrian Chadd 	rddata = OS_REG_READ(ah, AR_PHY_TX_PWRCTRL4);
13948c1d364SAdrian Chadd 	currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT);
14048c1d364SAdrian Chadd 
14148c1d364SAdrian Chadd 	HALDEBUG(ah, HAL_DEBUG_PERCAL,
14248c1d364SAdrian Chadd 	    "%s: called: initPDADC=%d, currPDADC=%d\n",
14348c1d364SAdrian Chadd 	    __func__, AH5416(ah)->initPDADC, currPDADC);
14448c1d364SAdrian Chadd 
14548c1d364SAdrian Chadd 	if (AH5416(ah)->initPDADC == 0 || currPDADC == 0)
14648c1d364SAdrian Chadd 		return;
14748c1d364SAdrian Chadd 
14848c1d364SAdrian Chadd 	(void) (ath_hal_eepromGet(ah, AR_EEP_DAC_HPWR_5G, &hpwr_5g));
14948c1d364SAdrian Chadd 
15048c1d364SAdrian Chadd 	if (hpwr_5g)
15148c1d364SAdrian Chadd 		delta = (currPDADC - AH5416(ah)->initPDADC + 4) / 8;
15248c1d364SAdrian Chadd 	else
15348c1d364SAdrian Chadd 		delta = (currPDADC - AH5416(ah)->initPDADC + 5) / 10;
15448c1d364SAdrian Chadd 
15548c1d364SAdrian Chadd 	HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: delta=%d, PDADCdelta=%d\n",
15648c1d364SAdrian Chadd 	    __func__, delta, AH9280(ah)->PDADCdelta);
15748c1d364SAdrian Chadd 
15848c1d364SAdrian Chadd 	if (delta != AH9280(ah)->PDADCdelta) {
15948c1d364SAdrian Chadd 		AH9280(ah)->PDADCdelta = delta;
16048c1d364SAdrian Chadd 		for (i = 1; i < AR9280_TX_GAIN_TABLE_SIZE; i++) {
16148c1d364SAdrian Chadd 			regval = AH9280(ah)->originalGain[i] - delta;
16248c1d364SAdrian Chadd 			if (regval < 0)
16348c1d364SAdrian Chadd 				regval = 0;
16448c1d364SAdrian Chadd 
16548c1d364SAdrian Chadd 			OS_REG_RMW_FIELD(ah,
16648c1d364SAdrian Chadd 				      AR_PHY_TX_GAIN_TBL1 + i * 4,
16748c1d364SAdrian Chadd 				      AR_PHY_TX_GAIN, regval);
16848c1d364SAdrian Chadd 		}
16948c1d364SAdrian Chadd 	}
17048c1d364SAdrian Chadd }
17148c1d364SAdrian Chadd 
172cc5c884dSAdrian Chadd 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)173cc5c884dSAdrian Chadd ar9280ChangeGainBoundarySettings(struct ath_hal *ah, uint16_t *gb,
174cc5c884dSAdrian Chadd     uint16_t numXpdGain, uint16_t pdGainOverlap_t2, int8_t pwr_table_offset,
175cc5c884dSAdrian Chadd     int16_t *diff)
176cc5c884dSAdrian Chadd {
177cc5c884dSAdrian Chadd 	uint16_t k;
178cc5c884dSAdrian Chadd 
179cc5c884dSAdrian Chadd 	/* Prior to writing the boundaries or the pdadc vs. power table
180cc5c884dSAdrian Chadd 	 * into the chip registers the default starting point on the pdadc
181cc5c884dSAdrian Chadd 	 * vs. power table needs to be checked and the curve boundaries
182cc5c884dSAdrian Chadd 	 * adjusted accordingly
183cc5c884dSAdrian Chadd 	 */
184cc5c884dSAdrian Chadd 	if (AR_SREV_MERLIN_20_OR_LATER(ah)) {
185cc5c884dSAdrian Chadd 		uint16_t gb_limit;
186cc5c884dSAdrian Chadd 
187cc5c884dSAdrian Chadd 		if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) {
188cc5c884dSAdrian Chadd 			/* get the difference in dB */
189cc5c884dSAdrian Chadd 			*diff = (uint16_t)(pwr_table_offset - AR5416_PWR_TABLE_OFFSET_DB);
190cc5c884dSAdrian Chadd 			/* get the number of half dB steps */
191cc5c884dSAdrian Chadd 			*diff *= 2;
192cc5c884dSAdrian Chadd 			/* change the original gain boundary settings
193cc5c884dSAdrian Chadd 			 * by the number of half dB steps
194cc5c884dSAdrian Chadd 			 */
195cc5c884dSAdrian Chadd 			for (k = 0; k < numXpdGain; k++)
196cc5c884dSAdrian Chadd 				gb[k] = (uint16_t)(gb[k] - *diff);
197cc5c884dSAdrian Chadd 		}
198cc5c884dSAdrian Chadd 		/* Because of a hardware limitation, ensure the gain boundary
199cc5c884dSAdrian Chadd 		 * is not larger than (63 - overlap)
200cc5c884dSAdrian Chadd 		 */
201cc5c884dSAdrian Chadd 		gb_limit = (uint16_t)(AR5416_MAX_RATE_POWER - pdGainOverlap_t2);
202cc5c884dSAdrian Chadd 
203cc5c884dSAdrian Chadd 		for (k = 0; k < numXpdGain; k++)
204cc5c884dSAdrian Chadd 			gb[k] = (uint16_t)min(gb_limit, gb[k]);
205cc5c884dSAdrian Chadd 	}
206cc5c884dSAdrian Chadd 
207cc5c884dSAdrian Chadd 	return *diff;
208cc5c884dSAdrian Chadd }
209cc5c884dSAdrian Chadd 
210cc5c884dSAdrian Chadd static void
ar9280AdjustPDADCValues(struct ath_hal * ah,int8_t pwr_table_offset,int16_t diff,uint8_t * pdadcValues)211cc5c884dSAdrian Chadd ar9280AdjustPDADCValues(struct ath_hal *ah, int8_t pwr_table_offset,
212cc5c884dSAdrian Chadd     int16_t diff, uint8_t *pdadcValues)
213cc5c884dSAdrian Chadd {
214cc5c884dSAdrian Chadd #define NUM_PDADC(diff) (AR5416_NUM_PDADC_VALUES - diff)
215cc5c884dSAdrian Chadd 	uint16_t k;
216cc5c884dSAdrian Chadd 
217cc5c884dSAdrian Chadd 	/* If this is a board that has a pwrTableOffset that differs from
218cc5c884dSAdrian Chadd 	 * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the
219cc5c884dSAdrian Chadd 	 * pdadc vs pwr table needs to be adjusted prior to writing to the
220cc5c884dSAdrian Chadd 	 * chip.
221cc5c884dSAdrian Chadd 	 */
222cc5c884dSAdrian Chadd 	if (AR_SREV_MERLIN_20_OR_LATER(ah)) {
223cc5c884dSAdrian Chadd 		if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) {
224cc5c884dSAdrian Chadd 			/* shift the table to start at the new offset */
225cc5c884dSAdrian Chadd 			for (k = 0; k < (uint16_t)NUM_PDADC(diff); k++ ) {
226cc5c884dSAdrian Chadd 				pdadcValues[k] = pdadcValues[k + diff];
227cc5c884dSAdrian Chadd 			}
228cc5c884dSAdrian Chadd 
229cc5c884dSAdrian Chadd 			/* fill the back of the table */
230cc5c884dSAdrian Chadd 			for (k = (uint16_t)NUM_PDADC(diff); k < NUM_PDADC(0); k++) {
231cc5c884dSAdrian Chadd 				pdadcValues[k] = pdadcValues[NUM_PDADC(diff)];
232cc5c884dSAdrian Chadd 			}
233cc5c884dSAdrian Chadd 		}
234cc5c884dSAdrian Chadd 	}
235cc5c884dSAdrian Chadd #undef NUM_PDADC
236cc5c884dSAdrian Chadd }
23748c1d364SAdrian Chadd /*
23848c1d364SAdrian Chadd  * This effectively disables the gain boundaries leaving it
23948c1d364SAdrian Chadd  * to the open-loop TX power control.
24048c1d364SAdrian Chadd  */
24148c1d364SAdrian Chadd static void
ar9280SetGainBoundariesOpenLoop(struct ath_hal * ah,int i,uint16_t pdGainOverlap_t2,uint16_t gainBoundaries[])242b90b8dd2SAdrian Chadd ar9280SetGainBoundariesOpenLoop(struct ath_hal *ah, int i,
24348c1d364SAdrian Chadd     uint16_t pdGainOverlap_t2, uint16_t gainBoundaries[])
24448c1d364SAdrian Chadd {
245b90b8dd2SAdrian Chadd 	int regChainOffset;
246b90b8dd2SAdrian Chadd 
247b90b8dd2SAdrian Chadd 	regChainOffset = ar5416GetRegChainOffset(ah, i);
248b90b8dd2SAdrian Chadd 
24948c1d364SAdrian Chadd 	/* These are unused for OLC */
25048c1d364SAdrian Chadd 	(void) pdGainOverlap_t2;
25148c1d364SAdrian Chadd 	(void) gainBoundaries;
25248c1d364SAdrian Chadd 
253b90b8dd2SAdrian Chadd 	HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: chain %d: writing closed loop values\n",
254b90b8dd2SAdrian Chadd 	    __func__, i);
255b90b8dd2SAdrian Chadd 
25648c1d364SAdrian Chadd 	OS_REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset,
25748c1d364SAdrian Chadd 	    SM(0x6, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) |
25848c1d364SAdrian Chadd 	    SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1)  |
25948c1d364SAdrian Chadd 	    SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2)  |
26048c1d364SAdrian Chadd 	    SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3)  |
26148c1d364SAdrian Chadd 	    SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4));
26248c1d364SAdrian Chadd }
26348c1d364SAdrian Chadd 
26448c1d364SAdrian Chadd /* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */
26548c1d364SAdrian Chadd /* XXX shouldn't be here! */
26648c1d364SAdrian Chadd #define EEP_MINOR(_ah) \
26748c1d364SAdrian Chadd         (AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK)
26848c1d364SAdrian Chadd #define IS_EEP_MINOR_V2(_ah)    (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2)
26948c1d364SAdrian Chadd #define IS_EEP_MINOR_V3(_ah)    (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_3)
27048c1d364SAdrian Chadd 
27148c1d364SAdrian Chadd /**************************************************************
27248c1d364SAdrian Chadd  * ar9280SetPowerCalTable
27348c1d364SAdrian Chadd  *
27448c1d364SAdrian Chadd  * Pull the PDADC piers from cal data and interpolate them across the given
27548c1d364SAdrian Chadd  * points as well as from the nearest pier(s) to get a power detector
27648c1d364SAdrian Chadd  * linear voltage to power level table.
27748c1d364SAdrian Chadd  *
27848c1d364SAdrian Chadd  * Handle OLC for Merlin where required.
27948c1d364SAdrian Chadd  */
28048c1d364SAdrian Chadd HAL_BOOL
ar9280SetPowerCalTable(struct ath_hal * ah,struct ar5416eeprom * pEepData,const struct ieee80211_channel * chan,int16_t * pTxPowerIndexOffset)28148c1d364SAdrian Chadd ar9280SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData,
28248c1d364SAdrian Chadd 	const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset)
28348c1d364SAdrian Chadd {
28448c1d364SAdrian Chadd 	CAL_DATA_PER_FREQ *pRawDataset;
28548c1d364SAdrian Chadd 	uint8_t  *pCalBChans = AH_NULL;
28648c1d364SAdrian Chadd 	uint16_t pdGainOverlap_t2;
28748c1d364SAdrian Chadd 	static uint8_t  pdadcValues[AR5416_NUM_PDADC_VALUES];
28848c1d364SAdrian Chadd 	uint16_t gainBoundaries[AR5416_PD_GAINS_IN_MASK];
28948c1d364SAdrian Chadd 	uint16_t numPiers, i;
29048c1d364SAdrian Chadd 	int16_t  tMinCalPower;
29148c1d364SAdrian Chadd 	uint16_t numXpdGain, xpdMask;
29248c1d364SAdrian Chadd 	uint16_t xpdGainValues[AR5416_NUM_PD_GAINS];
29348c1d364SAdrian Chadd 	uint32_t regChainOffset;
294cc5c884dSAdrian Chadd 	int8_t pwr_table_offset;
29548c1d364SAdrian Chadd 
29648c1d364SAdrian Chadd 	OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues));
29748c1d364SAdrian Chadd 
29848c1d364SAdrian Chadd 	xpdMask = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].xpdGain;
29948c1d364SAdrian Chadd 
300cc5c884dSAdrian Chadd 	(void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET, &pwr_table_offset);
301cc5c884dSAdrian Chadd 
30248c1d364SAdrian Chadd 	if (IS_EEP_MINOR_V2(ah)) {
30348c1d364SAdrian Chadd 		pdGainOverlap_t2 = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pdGainOverlap;
30448c1d364SAdrian Chadd 	} else {
30548c1d364SAdrian Chadd 		pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP));
30648c1d364SAdrian Chadd 	}
30748c1d364SAdrian Chadd 
30848c1d364SAdrian Chadd 	if (IEEE80211_IS_CHAN_2GHZ(chan)) {
30948c1d364SAdrian Chadd 		pCalBChans = pEepData->calFreqPier2G;
31048c1d364SAdrian Chadd 		numPiers = AR5416_NUM_2G_CAL_PIERS;
31148c1d364SAdrian Chadd 	} else {
31248c1d364SAdrian Chadd 		pCalBChans = pEepData->calFreqPier5G;
31348c1d364SAdrian Chadd 		numPiers = AR5416_NUM_5G_CAL_PIERS;
31448c1d364SAdrian Chadd 	}
31548c1d364SAdrian Chadd 
31648c1d364SAdrian Chadd 	/* If OLC is being done, set the init PDADC value appropriately */
31748c1d364SAdrian Chadd 	if (IEEE80211_IS_CHAN_2GHZ(chan) && AR_SREV_MERLIN_20_OR_LATER(ah) &&
31848c1d364SAdrian Chadd 	    ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) {
31948c1d364SAdrian Chadd 		struct calDataPerFreq *pRawDataset = pEepData->calPierData2G[0];
32048c1d364SAdrian Chadd 		AH5416(ah)->initPDADC = ((struct calDataPerFreqOpLoop *) pRawDataset)->vpdPdg[0][0];
32148c1d364SAdrian Chadd 	} else {
32248c1d364SAdrian Chadd 		/*
32348c1d364SAdrian Chadd 		 * XXX ath9k doesn't clear this for 5ghz mode if
32448c1d364SAdrian Chadd 		 * it were set in 2ghz mode before!
32548c1d364SAdrian Chadd 		 * The Merlin OLC temperature compensation code
32648c1d364SAdrian Chadd 		 * uses this to calculate the PDADC delta during
32748c1d364SAdrian Chadd 		 * calibration ; 0 here effectively stops the
32848c1d364SAdrian Chadd 		 * temperature compensation calibration from
329f6b6084bSPedro F. Giffuni 		 * occurring.
33048c1d364SAdrian Chadd 		 */
33148c1d364SAdrian Chadd 		AH5416(ah)->initPDADC = 0;
33248c1d364SAdrian Chadd 	}
33348c1d364SAdrian Chadd 
33448c1d364SAdrian Chadd 	/* Calculate the value of xpdgains from the xpdGain Mask */
33548c1d364SAdrian Chadd 	numXpdGain = ar5416GetXpdGainValues(ah, xpdMask, xpdGainValues);
33648c1d364SAdrian Chadd 
33748c1d364SAdrian Chadd 	/* Write the detector gain biases and their number */
33848c1d364SAdrian Chadd 	ar5416WriteDetectorGainBiases(ah, numXpdGain, xpdGainValues);
33948c1d364SAdrian Chadd 
34048c1d364SAdrian Chadd 	for (i = 0; i < AR5416_MAX_CHAINS; i++) {
34148c1d364SAdrian Chadd 		regChainOffset = ar5416GetRegChainOffset(ah, i);
34248c1d364SAdrian Chadd 		if (pEepData->baseEepHeader.txMask & (1 << i)) {
343cc5c884dSAdrian Chadd 			uint16_t diff;
344cc5c884dSAdrian Chadd 
34548c1d364SAdrian Chadd 			if (IEEE80211_IS_CHAN_2GHZ(chan)) {
34648c1d364SAdrian Chadd 				pRawDataset = pEepData->calPierData2G[i];
34748c1d364SAdrian Chadd 			} else {
34848c1d364SAdrian Chadd 				pRawDataset = pEepData->calPierData5G[i];
34948c1d364SAdrian Chadd 			}
35048c1d364SAdrian Chadd 
35148c1d364SAdrian Chadd 			/* Fetch the gain boundaries and the PDADC values */
35248c1d364SAdrian Chadd 			if (AR_SREV_MERLIN_20_OR_LATER(ah) &&
35348c1d364SAdrian Chadd 			    ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) {
35448c1d364SAdrian Chadd 				uint8_t pcdacIdx;
35548c1d364SAdrian Chadd 				uint8_t txPower;
35648c1d364SAdrian Chadd 
35748c1d364SAdrian Chadd 				ar9280olcGetTxGainIndex(ah, chan,
35848c1d364SAdrian Chadd 				    (struct calDataPerFreqOpLoop *) pRawDataset,
35948c1d364SAdrian Chadd 				    pCalBChans, numPiers, &txPower, &pcdacIdx);
36048c1d364SAdrian Chadd 				ar9280olcGetPDADCs(ah, pcdacIdx, txPower / 2, pdadcValues);
36148c1d364SAdrian Chadd 			} else {
36248c1d364SAdrian Chadd 				ar5416GetGainBoundariesAndPdadcs(ah,  chan,
36348c1d364SAdrian Chadd 				    pRawDataset, pCalBChans, numPiers,
36448c1d364SAdrian Chadd 				    pdGainOverlap_t2, &tMinCalPower,
36548c1d364SAdrian Chadd 				    gainBoundaries, pdadcValues, numXpdGain);
36648c1d364SAdrian Chadd 			}
36748c1d364SAdrian Chadd 
36848c1d364SAdrian Chadd 			/*
36948c1d364SAdrian Chadd 			 * Prior to writing the boundaries or the pdadc vs. power table
37048c1d364SAdrian Chadd 			 * into the chip registers the default starting point on the pdadc
37148c1d364SAdrian Chadd 			 * vs. power table needs to be checked and the curve boundaries
37248c1d364SAdrian Chadd 			 * adjusted accordingly
37348c1d364SAdrian Chadd 			 */
374cc5c884dSAdrian Chadd 			diff = ar9280ChangeGainBoundarySettings(ah,
375cc5c884dSAdrian Chadd 			    gainBoundaries, numXpdGain, pdGainOverlap_t2,
376cc5c884dSAdrian Chadd 			    pwr_table_offset, &diff);
37748c1d364SAdrian Chadd 
378ef1901a3SAdrian Chadd 			if ((i == 0) || AR_SREV_5416_V20_OR_LATER(ah)) {
37948c1d364SAdrian Chadd 				/* Set gain boundaries for either open- or closed-loop TPC */
38048c1d364SAdrian Chadd 				if (AR_SREV_MERLIN_20_OR_LATER(ah) &&
38148c1d364SAdrian Chadd 				    ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
38248c1d364SAdrian Chadd 					ar9280SetGainBoundariesOpenLoop(ah,
383b90b8dd2SAdrian Chadd 					    i, pdGainOverlap_t2,
38448c1d364SAdrian Chadd 					    gainBoundaries);
38548c1d364SAdrian Chadd 				else
38648c1d364SAdrian Chadd 					ar5416SetGainBoundariesClosedLoop(ah,
387b90b8dd2SAdrian Chadd 					    i, pdGainOverlap_t2,
38848c1d364SAdrian Chadd 					    gainBoundaries);
38948c1d364SAdrian Chadd 			}
39048c1d364SAdrian Chadd 
39148c1d364SAdrian Chadd 			/*
39248c1d364SAdrian Chadd 			 * If this is a board that has a pwrTableOffset that differs from
39348c1d364SAdrian Chadd 			 * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the
39448c1d364SAdrian Chadd 			 * pdadc vs pwr table needs to be adjusted prior to writing to the
39548c1d364SAdrian Chadd 			 * chip.
39648c1d364SAdrian Chadd 			 */
397cc5c884dSAdrian Chadd 			ar9280AdjustPDADCValues(ah, pwr_table_offset, diff, pdadcValues);
39848c1d364SAdrian Chadd 
39948c1d364SAdrian Chadd 			/* Write the power values into the baseband power table */
400b90b8dd2SAdrian Chadd 			ar5416WritePdadcValues(ah, i, pdadcValues);
40148c1d364SAdrian Chadd 		}
40248c1d364SAdrian Chadd 	}
40348c1d364SAdrian Chadd 	*pTxPowerIndexOffset = 0;
40448c1d364SAdrian Chadd 
40548c1d364SAdrian Chadd 	return AH_TRUE;
40648c1d364SAdrian Chadd }
407