ar9280_olc.c revision 219586
1/*
2 * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 *
25 * $FreeBSD: head/sys/dev/ath/ath_hal/ar9002/ar9280_olc.c 219586 2011-03-13 05:54:05Z adrian $
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
41void
42ar9280olcInit(struct ath_hal *ah)
43{
44	uint32_t i;
45
46	for (i = 0; i < AR9280_TX_GAIN_TABLE_SIZE; i++)
47		AH9280(ah)->originalGain[i] = MS(OS_REG_READ(ah,
48		    AR_PHY_TX_GAIN_TBL1 + i * 4), AR_PHY_TX_GAIN);
49
50	AH9280(ah)->PDADCdelta = 0;
51}
52
53void
54ar9280olcGetTxGainIndex(struct ath_hal *ah,
55    const struct ieee80211_channel *chan,
56    struct calDataPerFreqOpLoop *rawDatasetOpLoop,
57    uint8_t *calChans, uint16_t availPiers, uint8_t *pwr, uint8_t *pcdacIdx)
58{
59	uint8_t pcdac, i = 0;
60	uint16_t idxL = 0, idxR = 0, numPiers;
61	HAL_BOOL match;
62	CHAN_CENTERS centers;
63
64	ar5416GetChannelCenters(ah, chan, &centers);
65
66	for (numPiers = 0; numPiers < availPiers; numPiers++)
67		if (calChans[numPiers] == AR5416_BCHAN_UNUSED)
68			break;
69
70	match = ath_ee_getLowerUpperIndex((uint8_t)FREQ2FBIN(centers.synth_center,
71		    IEEE80211_IS_CHAN_2GHZ(chan)), calChans, numPiers,
72		    &idxL, &idxR);
73	if (match) {
74		pcdac = rawDatasetOpLoop[idxL].pcdac[0][0];
75		*pwr = rawDatasetOpLoop[idxL].pwrPdg[0][0];
76	} else {
77		pcdac = rawDatasetOpLoop[idxR].pcdac[0][0];
78		*pwr = (rawDatasetOpLoop[idxL].pwrPdg[0][0] +
79				rawDatasetOpLoop[idxR].pwrPdg[0][0])/2;
80	}
81	while (pcdac > AH9280(ah)->originalGain[i] &&
82			i < (AR9280_TX_GAIN_TABLE_SIZE - 1))
83		i++;
84
85	*pcdacIdx = i;
86}
87
88/*
89 * XXX txPower here is likely not the target txPower in the traditional
90 * XXX sense, but is set by a call to ar9280olcGetTxGainIndex().
91 * XXX Thus, be careful if you're trying to use this routine yourself.
92 */
93void
94ar9280olcGetPDADCs(struct ath_hal *ah, uint32_t initTxGain, int txPower,
95    uint8_t *pPDADCValues)
96{
97	uint32_t i;
98	uint32_t offset;
99
100	OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_0, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3);
101	OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_1, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3);
102
103	OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL7, AR_PHY_TX_PWRCTRL_INIT_TX_GAIN, initTxGain);
104
105	offset = txPower;
106	for (i = 0; i < AR5416_NUM_PDADC_VALUES; i++)
107		if (i < offset)
108			pPDADCValues[i] = 0x0;
109		else
110			pPDADCValues[i] = 0xFF;
111}
112
113/*
114 * Run temperature compensation calibration.
115 *
116 * The TX gain table is adjusted depending upon the difference
117 * between the initial PDADC value and the currently read
118 * average TX power sample value. This value is only valid if
119 * frames have been transmitted, so currPDADC will be 0 if
120 * no frames have yet been transmitted.
121 */
122void
123ar9280olcTemperatureCompensation(struct ath_hal *ah)
124{
125	uint32_t rddata, i;
126	int delta, currPDADC, regval;
127	uint8_t hpwr_5g = 0;
128
129	rddata = OS_REG_READ(ah, AR_PHY_TX_PWRCTRL4);
130	currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT);
131
132	HALDEBUG(ah, HAL_DEBUG_PERCAL,
133	    "%s: called: initPDADC=%d, currPDADC=%d\n",
134	    __func__, AH5416(ah)->initPDADC, currPDADC);
135
136	if (AH5416(ah)->initPDADC == 0 || currPDADC == 0)
137		return;
138
139	(void) (ath_hal_eepromGet(ah, AR_EEP_DAC_HPWR_5G, &hpwr_5g));
140
141	if (hpwr_5g)
142		delta = (currPDADC - AH5416(ah)->initPDADC + 4) / 8;
143	else
144		delta = (currPDADC - AH5416(ah)->initPDADC + 5) / 10;
145
146	HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: delta=%d, PDADCdelta=%d\n",
147	    __func__, delta, AH9280(ah)->PDADCdelta);
148
149	if (delta != AH9280(ah)->PDADCdelta) {
150		AH9280(ah)->PDADCdelta = delta;
151		for (i = 1; i < AR9280_TX_GAIN_TABLE_SIZE; i++) {
152			regval = AH9280(ah)->originalGain[i] - delta;
153			if (regval < 0)
154				regval = 0;
155
156			OS_REG_RMW_FIELD(ah,
157				      AR_PHY_TX_GAIN_TBL1 + i * 4,
158				      AR_PHY_TX_GAIN, regval);
159		}
160	}
161}
162
163
164static int16_t
165ar9280ChangeGainBoundarySettings(struct ath_hal *ah, uint16_t *gb,
166    uint16_t numXpdGain, uint16_t pdGainOverlap_t2, int8_t pwr_table_offset,
167    int16_t *diff)
168{
169	uint16_t k;
170
171	/* Prior to writing the boundaries or the pdadc vs. power table
172	 * into the chip registers the default starting point on the pdadc
173	 * vs. power table needs to be checked and the curve boundaries
174	 * adjusted accordingly
175	 */
176	if (AR_SREV_MERLIN_20_OR_LATER(ah)) {
177		uint16_t gb_limit;
178
179		if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) {
180			/* get the difference in dB */
181			*diff = (uint16_t)(pwr_table_offset - AR5416_PWR_TABLE_OFFSET_DB);
182			/* get the number of half dB steps */
183			*diff *= 2;
184			/* change the original gain boundary settings
185			 * by the number of half dB steps
186			 */
187			for (k = 0; k < numXpdGain; k++)
188				gb[k] = (uint16_t)(gb[k] - *diff);
189		}
190		/* Because of a hardware limitation, ensure the gain boundary
191		 * is not larger than (63 - overlap)
192		 */
193		gb_limit = (uint16_t)(AR5416_MAX_RATE_POWER - pdGainOverlap_t2);
194
195		for (k = 0; k < numXpdGain; k++)
196			gb[k] = (uint16_t)min(gb_limit, gb[k]);
197	}
198
199	return *diff;
200}
201
202static void
203ar9280AdjustPDADCValues(struct ath_hal *ah, int8_t pwr_table_offset,
204    int16_t diff, uint8_t *pdadcValues)
205{
206#define NUM_PDADC(diff) (AR5416_NUM_PDADC_VALUES - diff)
207	uint16_t k;
208
209	/* If this is a board that has a pwrTableOffset that differs from
210	 * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the
211	 * pdadc vs pwr table needs to be adjusted prior to writing to the
212	 * chip.
213	 */
214	if (AR_SREV_MERLIN_20_OR_LATER(ah)) {
215		if (AR5416_PWR_TABLE_OFFSET_DB != pwr_table_offset) {
216			/* shift the table to start at the new offset */
217			for (k = 0; k < (uint16_t)NUM_PDADC(diff); k++ ) {
218				pdadcValues[k] = pdadcValues[k + diff];
219			}
220
221			/* fill the back of the table */
222			for (k = (uint16_t)NUM_PDADC(diff); k < NUM_PDADC(0); k++) {
223				pdadcValues[k] = pdadcValues[NUM_PDADC(diff)];
224			}
225		}
226	}
227#undef NUM_PDADC
228}
229/*
230 * This effectively disables the gain boundaries leaving it
231 * to the open-loop TX power control.
232 */
233static void
234ar9280SetGainBoundariesOpenLoop(struct ath_hal *ah, int i,
235    uint16_t pdGainOverlap_t2, uint16_t gainBoundaries[])
236{
237	int regChainOffset;
238
239	regChainOffset = ar5416GetRegChainOffset(ah, i);
240
241	/* These are unused for OLC */
242	(void) pdGainOverlap_t2;
243	(void) gainBoundaries;
244
245	HALDEBUG(ah, HAL_DEBUG_EEPROM, "%s: chain %d: writing closed loop values\n",
246	    __func__, i);
247
248	OS_REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset,
249	    SM(0x6, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) |
250	    SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1)  |
251	    SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2)  |
252	    SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3)  |
253	    SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4));
254}
255
256/* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */
257/* XXX shouldn't be here! */
258#define EEP_MINOR(_ah) \
259        (AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK)
260#define IS_EEP_MINOR_V2(_ah)    (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2)
261#define IS_EEP_MINOR_V3(_ah)    (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_3)
262
263/**************************************************************
264 * ar9280SetPowerCalTable
265 *
266 * Pull the PDADC piers from cal data and interpolate them across the given
267 * points as well as from the nearest pier(s) to get a power detector
268 * linear voltage to power level table.
269 *
270 * Handle OLC for Merlin where required.
271 */
272HAL_BOOL
273ar9280SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData,
274	const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset)
275{
276	CAL_DATA_PER_FREQ *pRawDataset;
277	uint8_t  *pCalBChans = AH_NULL;
278	uint16_t pdGainOverlap_t2;
279	static uint8_t  pdadcValues[AR5416_NUM_PDADC_VALUES];
280	uint16_t gainBoundaries[AR5416_PD_GAINS_IN_MASK];
281	uint16_t numPiers, i;
282	int16_t  tMinCalPower;
283	uint16_t numXpdGain, xpdMask;
284	uint16_t xpdGainValues[AR5416_NUM_PD_GAINS];
285	uint32_t regChainOffset;
286	int8_t pwr_table_offset;
287
288	OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues));
289
290	xpdMask = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].xpdGain;
291
292	(void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET, &pwr_table_offset);
293
294
295	if (IS_EEP_MINOR_V2(ah)) {
296		pdGainOverlap_t2 = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pdGainOverlap;
297	} else {
298		pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP));
299	}
300
301	if (IEEE80211_IS_CHAN_2GHZ(chan)) {
302		pCalBChans = pEepData->calFreqPier2G;
303		numPiers = AR5416_NUM_2G_CAL_PIERS;
304	} else {
305		pCalBChans = pEepData->calFreqPier5G;
306		numPiers = AR5416_NUM_5G_CAL_PIERS;
307	}
308
309	/* If OLC is being done, set the init PDADC value appropriately */
310	if (IEEE80211_IS_CHAN_2GHZ(chan) && AR_SREV_MERLIN_20_OR_LATER(ah) &&
311	    ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) {
312		struct calDataPerFreq *pRawDataset = pEepData->calPierData2G[0];
313		AH5416(ah)->initPDADC = ((struct calDataPerFreqOpLoop *) pRawDataset)->vpdPdg[0][0];
314	} else {
315		/*
316		 * XXX ath9k doesn't clear this for 5ghz mode if
317		 * it were set in 2ghz mode before!
318		 * The Merlin OLC temperature compensation code
319		 * uses this to calculate the PDADC delta during
320		 * calibration ; 0 here effectively stops the
321		 * temperature compensation calibration from
322		 * occuring.
323		 */
324		AH5416(ah)->initPDADC = 0;
325	}
326
327	/* Calculate the value of xpdgains from the xpdGain Mask */
328	numXpdGain = ar5416GetXpdGainValues(ah, xpdMask, xpdGainValues);
329
330	/* Write the detector gain biases and their number */
331	ar5416WriteDetectorGainBiases(ah, numXpdGain, xpdGainValues);
332
333	for (i = 0; i < AR5416_MAX_CHAINS; i++) {
334		regChainOffset = ar5416GetRegChainOffset(ah, i);
335		if (pEepData->baseEepHeader.txMask & (1 << i)) {
336			uint16_t diff;
337
338			if (IEEE80211_IS_CHAN_2GHZ(chan)) {
339				pRawDataset = pEepData->calPierData2G[i];
340			} else {
341				pRawDataset = pEepData->calPierData5G[i];
342			}
343
344			/* Fetch the gain boundaries and the PDADC values */
345			if (AR_SREV_MERLIN_20_OR_LATER(ah) &&
346			    ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) {
347				uint8_t pcdacIdx;
348				uint8_t txPower;
349
350				ar9280olcGetTxGainIndex(ah, chan,
351				    (struct calDataPerFreqOpLoop *) pRawDataset,
352				    pCalBChans, numPiers, &txPower, &pcdacIdx);
353				ar9280olcGetPDADCs(ah, pcdacIdx, txPower / 2, pdadcValues);
354			} else {
355				ar5416GetGainBoundariesAndPdadcs(ah,  chan,
356				    pRawDataset, pCalBChans, numPiers,
357				    pdGainOverlap_t2, &tMinCalPower,
358				    gainBoundaries, pdadcValues, numXpdGain);
359			}
360
361			/*
362			 * Prior to writing the boundaries or the pdadc vs. power table
363			 * into the chip registers the default starting point on the pdadc
364			 * vs. power table needs to be checked and the curve boundaries
365			 * adjusted accordingly
366			 */
367			diff = ar9280ChangeGainBoundarySettings(ah,
368			    gainBoundaries, numXpdGain, pdGainOverlap_t2,
369			    pwr_table_offset, &diff);
370
371			if ((i == 0) || AR_SREV_OWL_20_OR_LATER(ah)) {
372				/* Set gain boundaries for either open- or closed-loop TPC */
373				if (AR_SREV_MERLIN_20_OR_LATER(ah) &&
374				    ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL))
375					ar9280SetGainBoundariesOpenLoop(ah,
376					    i, pdGainOverlap_t2,
377					    gainBoundaries);
378				else
379					ar5416SetGainBoundariesClosedLoop(ah,
380					    i, pdGainOverlap_t2,
381					    gainBoundaries);
382			}
383
384			/*
385			 * If this is a board that has a pwrTableOffset that differs from
386			 * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the
387			 * pdadc vs pwr table needs to be adjusted prior to writing to the
388			 * chip.
389			 */
390			ar9280AdjustPDADCValues(ah, pwr_table_offset, diff, pdadcValues);
391
392			/* Write the power values into the baseband power table */
393			ar5416WritePdadcValues(ah, i, pdadcValues);
394		}
395	}
396	*pTxPowerIndexOffset = 0;
397
398	return AH_TRUE;
399}
400