ar9280_olc.c revision 219393
1219393Sadrian/*
2219393Sadrian * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.
3219393Sadrian *
4219393Sadrian * Redistribution and use in source and binary forms, with or without
5219393Sadrian * modification, are permitted provided that the following conditions
6219393Sadrian * are met:
7219393Sadrian * 1. Redistributions of source code must retain the above copyright
8219393Sadrian *    notice, this list of conditions and the following disclaimer.
9219393Sadrian * 2. Redistributions in binary form must reproduce the above copyright
10219393Sadrian *    notice, this list of conditions and the following disclaimer in the
11219393Sadrian *    documentation and/or other materials provided with the distribution.
12219393Sadrian *
13219393Sadrian * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14219393Sadrian * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15219393Sadrian * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16219393Sadrian * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17219393Sadrian * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18219393Sadrian * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19219393Sadrian * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20219393Sadrian * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21219393Sadrian * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22219393Sadrian * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23219393Sadrian * SUCH DAMAGE.
24219393Sadrian *
25219393Sadrian * $FreeBSD: head/sys/dev/ath/ath_hal/ar9002/ar9280_olc.c 219393 2011-03-08 06:59:59Z adrian $
26219393Sadrian */
27219393Sadrian#include "opt_ah.h"
28219393Sadrian
29219393Sadrian#include "ah.h"
30219393Sadrian#include "ah_internal.h"
31219393Sadrian
32219393Sadrian#include "ah_eeprom_v14.h"
33219393Sadrian
34219393Sadrian#include "ar9002/ar9280.h"
35219393Sadrian#include "ar5416/ar5416reg.h"
36219393Sadrian#include "ar5416/ar5416phy.h"
37219393Sadrian#include "ar9002/ar9002phy.h"
38219393Sadrian
39219393Sadrian#include "ar9002/ar9280_olc.h"
40219393Sadrian
41219393Sadrianvoid
42219393Sadrianar9280olcInit(struct ath_hal *ah)
43219393Sadrian{
44219393Sadrian	uint32_t i;
45219393Sadrian
46219393Sadrian	for (i = 0; i < AR9280_TX_GAIN_TABLE_SIZE; i++)
47219393Sadrian		AH9280(ah)->originalGain[i] = MS(OS_REG_READ(ah,
48219393Sadrian		    AR_PHY_TX_GAIN_TBL1 + i * 4), AR_PHY_TX_GAIN);
49219393Sadrian
50219393Sadrian	AH9280(ah)->PDADCdelta = 0;
51219393Sadrian}
52219393Sadrian
53219393Sadrianvoid
54219393Sadrianar9280olcGetTxGainIndex(struct ath_hal *ah,
55219393Sadrian    const struct ieee80211_channel *chan,
56219393Sadrian    struct calDataPerFreqOpLoop *rawDatasetOpLoop,
57219393Sadrian    uint8_t *calChans, uint16_t availPiers, uint8_t *pwr, uint8_t *pcdacIdx)
58219393Sadrian{
59219393Sadrian	uint8_t pcdac, i = 0;
60219393Sadrian	uint16_t idxL = 0, idxR = 0, numPiers;
61219393Sadrian	HAL_BOOL match;
62219393Sadrian	CHAN_CENTERS centers;
63219393Sadrian
64219393Sadrian	ar5416GetChannelCenters(ah, chan, &centers);
65219393Sadrian
66219393Sadrian	for (numPiers = 0; numPiers < availPiers; numPiers++)
67219393Sadrian		if (calChans[numPiers] == AR5416_BCHAN_UNUSED)
68219393Sadrian			break;
69219393Sadrian
70219393Sadrian	match = getLowerUpperIndex((uint8_t)FREQ2FBIN(centers.synth_center,
71219393Sadrian		    IEEE80211_IS_CHAN_2GHZ(chan)), calChans, numPiers,
72219393Sadrian		    &idxL, &idxR);
73219393Sadrian	if (match) {
74219393Sadrian		pcdac = rawDatasetOpLoop[idxL].pcdac[0][0];
75219393Sadrian		*pwr = rawDatasetOpLoop[idxL].pwrPdg[0][0];
76219393Sadrian	} else {
77219393Sadrian		pcdac = rawDatasetOpLoop[idxR].pcdac[0][0];
78219393Sadrian		*pwr = (rawDatasetOpLoop[idxL].pwrPdg[0][0] +
79219393Sadrian				rawDatasetOpLoop[idxR].pwrPdg[0][0])/2;
80219393Sadrian	}
81219393Sadrian	while (pcdac > AH9280(ah)->originalGain[i] &&
82219393Sadrian			i < (AR9280_TX_GAIN_TABLE_SIZE - 1))
83219393Sadrian		i++;
84219393Sadrian
85219393Sadrian	*pcdacIdx = i;
86219393Sadrian}
87219393Sadrian
88219393Sadrian/*
89219393Sadrian * XXX txPower here is likely not the target txPower in the traditional
90219393Sadrian * XXX sense, but is set by a call to ar9280olcGetTxGainIndex().
91219393Sadrian * XXX Thus, be careful if you're trying to use this routine yourself.
92219393Sadrian */
93219393Sadrianvoid
94219393Sadrianar9280olcGetPDADCs(struct ath_hal *ah, uint32_t initTxGain, int txPower,
95219393Sadrian    uint8_t *pPDADCValues)
96219393Sadrian{
97219393Sadrian	uint32_t i;
98219393Sadrian	uint32_t offset;
99219393Sadrian
100219393Sadrian	OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_0, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3);
101219393Sadrian	OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_1, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3);
102219393Sadrian
103219393Sadrian	OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL7, AR_PHY_TX_PWRCTRL_INIT_TX_GAIN, initTxGain);
104219393Sadrian
105219393Sadrian	offset = txPower;
106219393Sadrian	for (i = 0; i < AR5416_NUM_PDADC_VALUES; i++)
107219393Sadrian		if (i < offset)
108219393Sadrian			pPDADCValues[i] = 0x0;
109219393Sadrian		else
110219393Sadrian			pPDADCValues[i] = 0xFF;
111219393Sadrian}
112219393Sadrian
113219393Sadrian/*
114219393Sadrian * Run temperature compensation calibration.
115219393Sadrian *
116219393Sadrian * The TX gain table is adjusted depending upon the difference
117219393Sadrian * between the initial PDADC value and the currently read
118219393Sadrian * average TX power sample value. This value is only valid if
119219393Sadrian * frames have been transmitted, so currPDADC will be 0 if
120219393Sadrian * no frames have yet been transmitted.
121219393Sadrian */
122219393Sadrianvoid
123219393Sadrianar9280olcTemperatureCompensation(struct ath_hal *ah)
124219393Sadrian{
125219393Sadrian	uint32_t rddata, i;
126219393Sadrian	int delta, currPDADC, regval;
127219393Sadrian	uint8_t hpwr_5g = 0;
128219393Sadrian
129219393Sadrian	rddata = OS_REG_READ(ah, AR_PHY_TX_PWRCTRL4);
130219393Sadrian	currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT);
131219393Sadrian
132219393Sadrian	HALDEBUG(ah, HAL_DEBUG_PERCAL,
133219393Sadrian	    "%s: called: initPDADC=%d, currPDADC=%d\n",
134219393Sadrian	    __func__, AH5416(ah)->initPDADC, currPDADC);
135219393Sadrian
136219393Sadrian	if (AH5416(ah)->initPDADC == 0 || currPDADC == 0)
137219393Sadrian		return;
138219393Sadrian
139219393Sadrian	(void) (ath_hal_eepromGet(ah, AR_EEP_DAC_HPWR_5G, &hpwr_5g));
140219393Sadrian
141219393Sadrian	if (hpwr_5g)
142219393Sadrian		delta = (currPDADC - AH5416(ah)->initPDADC + 4) / 8;
143219393Sadrian	else
144219393Sadrian		delta = (currPDADC - AH5416(ah)->initPDADC + 5) / 10;
145219393Sadrian
146219393Sadrian	HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: delta=%d, PDADCdelta=%d\n",
147219393Sadrian	    __func__, delta, AH9280(ah)->PDADCdelta);
148219393Sadrian
149219393Sadrian	if (delta != AH9280(ah)->PDADCdelta) {
150219393Sadrian		AH9280(ah)->PDADCdelta = delta;
151219393Sadrian		for (i = 1; i < AR9280_TX_GAIN_TABLE_SIZE; i++) {
152219393Sadrian			regval = AH9280(ah)->originalGain[i] - delta;
153219393Sadrian			if (regval < 0)
154219393Sadrian				regval = 0;
155219393Sadrian
156219393Sadrian			OS_REG_RMW_FIELD(ah,
157219393Sadrian				      AR_PHY_TX_GAIN_TBL1 + i * 4,
158219393Sadrian				      AR_PHY_TX_GAIN, regval);
159219393Sadrian		}
160219393Sadrian	}
161219393Sadrian}
162219393Sadrian
163219393Sadrian/*
164219393Sadrian * This effectively disables the gain boundaries leaving it
165219393Sadrian * to the open-loop TX power control.
166219393Sadrian */
167219393Sadrianstatic void
168219393Sadrianar9280SetGainBoundariesOpenLoop(struct ath_hal *ah, int regChainOffset,
169219393Sadrian    uint16_t pdGainOverlap_t2, uint16_t gainBoundaries[])
170219393Sadrian{
171219393Sadrian	/* These are unused for OLC */
172219393Sadrian	(void) pdGainOverlap_t2;
173219393Sadrian	(void) gainBoundaries;
174219393Sadrian
175219393Sadrian	OS_REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset,
176219393Sadrian	    SM(0x6, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) |
177219393Sadrian	    SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1)  |
178219393Sadrian	    SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2)  |
179219393Sadrian	    SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3)  |
180219393Sadrian	    SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4));
181219393Sadrian}
182219393Sadrian
183219393Sadrian/* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */
184219393Sadrian/* XXX shouldn't be here! */
185219393Sadrian#define EEP_MINOR(_ah) \
186219393Sadrian        (AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK)
187219393Sadrian#define IS_EEP_MINOR_V2(_ah)    (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2)
188219393Sadrian#define IS_EEP_MINOR_V3(_ah)    (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_3)
189219393Sadrian
190219393Sadrian/**************************************************************
191219393Sadrian * ar9280SetPowerCalTable
192219393Sadrian *
193219393Sadrian * Pull the PDADC piers from cal data and interpolate them across the given
194219393Sadrian * points as well as from the nearest pier(s) to get a power detector
195219393Sadrian * linear voltage to power level table.
196219393Sadrian *
197219393Sadrian * Handle OLC for Merlin where required.
198219393Sadrian */
199219393SadrianHAL_BOOL
200219393Sadrianar9280SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData,
201219393Sadrian	const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset)
202219393Sadrian{
203219393Sadrian	CAL_DATA_PER_FREQ *pRawDataset;
204219393Sadrian	uint8_t  *pCalBChans = AH_NULL;
205219393Sadrian	uint16_t pdGainOverlap_t2;
206219393Sadrian	static uint8_t  pdadcValues[AR5416_NUM_PDADC_VALUES];
207219393Sadrian	uint16_t gainBoundaries[AR5416_PD_GAINS_IN_MASK];
208219393Sadrian	uint16_t numPiers, i;
209219393Sadrian	int16_t  tMinCalPower;
210219393Sadrian	uint16_t numXpdGain, xpdMask;
211219393Sadrian	uint16_t xpdGainValues[AR5416_NUM_PD_GAINS];
212219393Sadrian	uint32_t regChainOffset;
213219393Sadrian
214219393Sadrian	OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues));
215219393Sadrian
216219393Sadrian	xpdMask = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].xpdGain;
217219393Sadrian
218219393Sadrian	if (IS_EEP_MINOR_V2(ah)) {
219219393Sadrian		pdGainOverlap_t2 = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pdGainOverlap;
220219393Sadrian	} else {
221219393Sadrian		pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP));
222219393Sadrian	}
223219393Sadrian
224219393Sadrian	if (IEEE80211_IS_CHAN_2GHZ(chan)) {
225219393Sadrian		pCalBChans = pEepData->calFreqPier2G;
226219393Sadrian		numPiers = AR5416_NUM_2G_CAL_PIERS;
227219393Sadrian	} else {
228219393Sadrian		pCalBChans = pEepData->calFreqPier5G;
229219393Sadrian		numPiers = AR5416_NUM_5G_CAL_PIERS;
230219393Sadrian	}
231219393Sadrian
232219393Sadrian	/* If OLC is being done, set the init PDADC value appropriately */
233219393Sadrian	if (IEEE80211_IS_CHAN_2GHZ(chan) && AR_SREV_MERLIN_20_OR_LATER(ah) &&
234219393Sadrian	    ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) {
235219393Sadrian		struct calDataPerFreq *pRawDataset = pEepData->calPierData2G[0];
236219393Sadrian		AH5416(ah)->initPDADC = ((struct calDataPerFreqOpLoop *) pRawDataset)->vpdPdg[0][0];
237219393Sadrian	} else {
238219393Sadrian		/*
239219393Sadrian		 * XXX ath9k doesn't clear this for 5ghz mode if
240219393Sadrian		 * it were set in 2ghz mode before!
241219393Sadrian		 * The Merlin OLC temperature compensation code
242219393Sadrian		 * uses this to calculate the PDADC delta during
243219393Sadrian		 * calibration ; 0 here effectively stops the
244219393Sadrian		 * temperature compensation calibration from
245219393Sadrian		 * occuring.
246219393Sadrian		 */
247219393Sadrian		AH5416(ah)->initPDADC = 0;
248219393Sadrian	}
249219393Sadrian
250219393Sadrian	/* Calculate the value of xpdgains from the xpdGain Mask */
251219393Sadrian	numXpdGain = ar5416GetXpdGainValues(ah, xpdMask, xpdGainValues);
252219393Sadrian
253219393Sadrian	/* Write the detector gain biases and their number */
254219393Sadrian	ar5416WriteDetectorGainBiases(ah, numXpdGain, xpdGainValues);
255219393Sadrian
256219393Sadrian	for (i = 0; i < AR5416_MAX_CHAINS; i++) {
257219393Sadrian		regChainOffset = ar5416GetRegChainOffset(ah, i);
258219393Sadrian		if (pEepData->baseEepHeader.txMask & (1 << i)) {
259219393Sadrian			if (IEEE80211_IS_CHAN_2GHZ(chan)) {
260219393Sadrian				pRawDataset = pEepData->calPierData2G[i];
261219393Sadrian			} else {
262219393Sadrian				pRawDataset = pEepData->calPierData5G[i];
263219393Sadrian			}
264219393Sadrian
265219393Sadrian			/* Fetch the gain boundaries and the PDADC values */
266219393Sadrian			if (AR_SREV_MERLIN_20_OR_LATER(ah) &&
267219393Sadrian			    ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) {
268219393Sadrian				uint8_t pcdacIdx;
269219393Sadrian				uint8_t txPower;
270219393Sadrian
271219393Sadrian				ar9280olcGetTxGainIndex(ah, chan,
272219393Sadrian				    (struct calDataPerFreqOpLoop *) pRawDataset,
273219393Sadrian				    pCalBChans, numPiers, &txPower, &pcdacIdx);
274219393Sadrian				ar9280olcGetPDADCs(ah, pcdacIdx, txPower / 2, pdadcValues);
275219393Sadrian			} else {
276219393Sadrian				ar5416GetGainBoundariesAndPdadcs(ah,  chan,
277219393Sadrian				    pRawDataset, pCalBChans, numPiers,
278219393Sadrian				    pdGainOverlap_t2, &tMinCalPower,
279219393Sadrian				    gainBoundaries, pdadcValues, numXpdGain);
280219393Sadrian			}
281219393Sadrian
282219393Sadrian			/*
283219393Sadrian			 * Prior to writing the boundaries or the pdadc vs. power table
284219393Sadrian			 * into the chip registers the default starting point on the pdadc
285219393Sadrian			 * vs. power table needs to be checked and the curve boundaries
286219393Sadrian			 * adjusted accordingly
287219393Sadrian			 */
288219393Sadrian			// XXX ath9k_change_gain_boundary_setting();
289219393Sadrian
290219393Sadrian			if ((i == 0) || AR_SREV_OWL_20_OR_LATER(ah)) {
291219393Sadrian				/* Set gain boundaries for either open- or closed-loop TPC */
292219393Sadrian				if (AR_SREV_MERLIN_20_OR_LATER(ah) &&
293219393Sadrian				    ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
294219393Sadrian					ar9280SetGainBoundariesOpenLoop(ah,
295219393Sadrian					    regChainOffset, pdGainOverlap_t2,
296219393Sadrian					    gainBoundaries);
297219393Sadrian				else
298219393Sadrian					ar5416SetGainBoundariesClosedLoop(ah,
299219393Sadrian					    regChainOffset, pdGainOverlap_t2,
300219393Sadrian					    gainBoundaries);
301219393Sadrian			}
302219393Sadrian
303219393Sadrian			/*
304219393Sadrian			 * If this is a board that has a pwrTableOffset that differs from
305219393Sadrian			 * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the
306219393Sadrian			 * pdadc vs pwr table needs to be adjusted prior to writing to the
307219393Sadrian			 * chip.
308219393Sadrian			 */
309219393Sadrian			/* XXX ath9k_adjust_pdadc_values() */
310219393Sadrian
311219393Sadrian			/* Write the power values into the baseband power table */
312219393Sadrian			ar5416WritePdadcValues(ah, regChainOffset, pdadcValues);
313219393Sadrian		}
314219393Sadrian	}
315219393Sadrian	*pTxPowerIndexOffset = 0;
316219393Sadrian
317219393Sadrian	return AH_TRUE;
318219393Sadrian}
319