1185380Ssam/*
2185380Ssam * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
3185380Ssam * Copyright (c) 2002-2008 Atheros Communications, Inc.
4185380Ssam *
5185380Ssam * Permission to use, copy, modify, and/or distribute this software for any
6185380Ssam * purpose with or without fee is hereby granted, provided that the above
7185380Ssam * copyright notice and this permission notice appear in all copies.
8185380Ssam *
9185380Ssam * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10185380Ssam * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11185380Ssam * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12185380Ssam * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13185380Ssam * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14185380Ssam * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15185380Ssam * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16185380Ssam *
17187129Ssam * $FreeBSD$
18185380Ssam */
19185380Ssam#include "opt_ah.h"
20185380Ssam
21185380Ssam#include "ah.h"
22185380Ssam#include "ah_internal.h"
23185380Ssam#include "ah_devid.h"
24185380Ssam
25185380Ssam#include "ar5212/ar5212.h"
26185380Ssam#include "ar5212/ar5212reg.h"
27185380Ssam#include "ar5212/ar5212phy.h"
28185380Ssam
29185380Ssam#include "ah_eeprom_v3.h"
30185380Ssam
31185380Ssamstatic const GAIN_OPTIMIZATION_LADDER gainLadder = {
32185380Ssam	9,					/* numStepsInLadder */
33185380Ssam	4,					/* defaultStepNum */
34185380Ssam	{ { {4, 1, 1, 1},  6, "FG8"},
35185380Ssam	  { {4, 0, 1, 1},  4, "FG7"},
36185380Ssam	  { {3, 1, 1, 1},  3, "FG6"},
37185380Ssam	  { {4, 0, 0, 1},  1, "FG5"},
38185380Ssam	  { {4, 1, 1, 0},  0, "FG4"},	/* noJack */
39185380Ssam	  { {4, 0, 1, 0}, -2, "FG3"},	/* halfJack */
40185380Ssam	  { {3, 1, 1, 0}, -3, "FG2"},	/* clip3 */
41185380Ssam	  { {4, 0, 0, 0}, -4, "FG1"},	/* noJack */
42185380Ssam	  { {2, 1, 1, 0}, -6, "FG0"} 	/* clip2 */
43185380Ssam	}
44185380Ssam};
45185380Ssam
46185380Ssamstatic const GAIN_OPTIMIZATION_LADDER gainLadder5112 = {
47185380Ssam	8,					/* numStepsInLadder */
48185380Ssam	1,					/* defaultStepNum */
49185380Ssam	{ { {3, 0,0,0, 0,0,0},   6, "FG7"},	/* most fixed gain */
50185380Ssam	  { {2, 0,0,0, 0,0,0},   0, "FG6"},
51185380Ssam	  { {1, 0,0,0, 0,0,0},  -3, "FG5"},
52185380Ssam	  { {0, 0,0,0, 0,0,0},  -6, "FG4"},
53185380Ssam	  { {0, 1,1,0, 0,0,0},  -8, "FG3"},
54185380Ssam	  { {0, 1,1,0, 1,1,0}, -10, "FG2"},
55185380Ssam	  { {0, 1,0,1, 1,1,0}, -13, "FG1"},
56185380Ssam	  { {0, 1,0,1, 1,0,1}, -16, "FG0"},	/* least fixed gain */
57185380Ssam	}
58185380Ssam};
59185380Ssam
60185380Ssam/*
61185380Ssam * Initialize the gain structure to good values
62185380Ssam */
63185380Ssamvoid
64185380Ssamar5212InitializeGainValues(struct ath_hal *ah)
65185380Ssam{
66185380Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
67185380Ssam	GAIN_VALUES *gv = &ahp->ah_gainValues;
68185380Ssam
69185380Ssam	/* initialize gain optimization values */
70185380Ssam	if (IS_RAD5112_ANY(ah)) {
71185380Ssam		gv->currStepNum = gainLadder5112.defaultStepNum;
72185380Ssam		gv->currStep =
73185380Ssam			&gainLadder5112.optStep[gainLadder5112.defaultStepNum];
74185380Ssam		gv->active = AH_TRUE;
75185380Ssam		gv->loTrig = 20;
76185380Ssam		gv->hiTrig = 85;
77185380Ssam	} else {
78185380Ssam		gv->currStepNum = gainLadder.defaultStepNum;
79185380Ssam		gv->currStep = &gainLadder.optStep[gainLadder.defaultStepNum];
80185380Ssam		gv->active = AH_TRUE;
81185380Ssam		gv->loTrig = 20;
82185380Ssam		gv->hiTrig = 35;
83185380Ssam	}
84185380Ssam}
85185380Ssam
86185380Ssam#define	MAX_ANALOG_START	319		/* XXX */
87185380Ssam
88185380Ssam/*
89185380Ssam * Find analog bits of given parameter data and return a reversed value
90185380Ssam */
91185380Ssamstatic uint32_t
92185380Ssamar5212GetRfField(uint32_t *rfBuf, uint32_t numBits, uint32_t firstBit, uint32_t column)
93185380Ssam{
94185380Ssam	uint32_t reg32 = 0, mask, arrayEntry, lastBit;
95185380Ssam	uint32_t bitPosition, bitsShifted;
96185380Ssam	int32_t bitsLeft;
97185380Ssam
98185380Ssam	HALASSERT(column <= 3);
99185380Ssam	HALASSERT(numBits <= 32);
100185380Ssam	HALASSERT(firstBit + numBits <= MAX_ANALOG_START);
101185380Ssam
102185380Ssam	arrayEntry = (firstBit - 1) / 8;
103185380Ssam	bitPosition = (firstBit - 1) % 8;
104185380Ssam	bitsLeft = numBits;
105185380Ssam	bitsShifted = 0;
106185380Ssam	while (bitsLeft > 0) {
107185380Ssam		lastBit = (bitPosition + bitsLeft > 8) ?
108185380Ssam			(8) : (bitPosition + bitsLeft);
109185380Ssam		mask = (((1 << lastBit) - 1) ^ ((1 << bitPosition) - 1)) <<
110185380Ssam			(column * 8);
111185380Ssam		reg32 |= (((rfBuf[arrayEntry] & mask) >> (column * 8)) >>
112185380Ssam			bitPosition) << bitsShifted;
113185380Ssam		bitsShifted += lastBit - bitPosition;
114185380Ssam		bitsLeft -= (8 - bitPosition);
115185380Ssam		bitPosition = 0;
116185380Ssam		arrayEntry++;
117185380Ssam	}
118185380Ssam	reg32 = ath_hal_reverseBits(reg32, numBits);
119185380Ssam	return reg32;
120185380Ssam}
121185380Ssam
122185380Ssamstatic HAL_BOOL
123185380Ssamar5212InvalidGainReadback(struct ath_hal *ah, GAIN_VALUES *gv)
124185380Ssam{
125185380Ssam	uint32_t gStep, g, mixOvr;
126185380Ssam	uint32_t L1, L2, L3, L4;
127185380Ssam
128185380Ssam	if (IS_RAD5112_ANY(ah)) {
129185380Ssam		mixOvr = ar5212GetRfField(ar5212GetRfBank(ah, 7), 1, 36, 0);
130185380Ssam		L1 = 0;
131185380Ssam		L2 = 107;
132185380Ssam		L3 = 0;
133185380Ssam		L4 = 107;
134185380Ssam		if (mixOvr == 1) {
135185380Ssam			L2 = 83;
136185380Ssam			L4 = 83;
137185380Ssam			gv->hiTrig = 55;
138185380Ssam		}
139185380Ssam	} else {
140185380Ssam		gStep = ar5212GetRfField(ar5212GetRfBank(ah, 7), 6, 37, 0);
141185380Ssam
142185380Ssam		L1 = 0;
143185380Ssam		L2 = (gStep == 0x3f) ? 50 : gStep + 4;
144185380Ssam		L3 = (gStep != 0x3f) ? 0x40 : L1;
145185380Ssam		L4 = L3 + 50;
146185380Ssam
147185380Ssam		gv->loTrig = L1 + (gStep == 0x3f ? DYN_ADJ_LO_MARGIN : 0);
148185380Ssam		/* never adjust if != 0x3f */
149185380Ssam		gv->hiTrig = L4 - (gStep == 0x3f ? DYN_ADJ_UP_MARGIN : -5);
150185380Ssam	}
151185380Ssam	g = gv->currGain;
152185380Ssam
153185380Ssam	return !((g >= L1 && g<= L2) || (g >= L3 && g <= L4));
154185380Ssam}
155185380Ssam
156185380Ssam/*
157185380Ssam * Enable the probe gain check on the next packet
158185380Ssam */
159185380Ssamvoid
160185380Ssamar5212RequestRfgain(struct ath_hal *ah)
161185380Ssam{
162185380Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
163185380Ssam	uint32_t probePowerIndex;
164185380Ssam
165185380Ssam	/* Enable the gain readback probe */
166185380Ssam	probePowerIndex = ahp->ah_ofdmTxPower + ahp->ah_txPowerIndexOffset;
167185380Ssam	OS_REG_WRITE(ah, AR_PHY_PAPD_PROBE,
168185380Ssam		  SM(probePowerIndex, AR_PHY_PAPD_PROBE_POWERTX)
169185380Ssam		| AR_PHY_PAPD_PROBE_NEXT_TX);
170185380Ssam
171185380Ssam	ahp->ah_rfgainState = HAL_RFGAIN_READ_REQUESTED;
172185380Ssam}
173185380Ssam
174185380Ssam/*
175185380Ssam * Check to see if our readback gain level sits within the linear
176185380Ssam * region of our current variable attenuation window
177185380Ssam */
178185380Ssamstatic HAL_BOOL
179185380Ssamar5212IsGainAdjustNeeded(struct ath_hal *ah, const GAIN_VALUES *gv)
180185380Ssam{
181185380Ssam	return (gv->currGain <= gv->loTrig || gv->currGain >= gv->hiTrig);
182185380Ssam}
183185380Ssam
184185380Ssam/*
185185380Ssam * Move the rabbit ears in the correct direction.
186185380Ssam */
187185380Ssamstatic int32_t
188185380Ssamar5212AdjustGain(struct ath_hal *ah, GAIN_VALUES *gv)
189185380Ssam{
190185380Ssam	const GAIN_OPTIMIZATION_LADDER *gl;
191185380Ssam
192185380Ssam	if (IS_RAD5112_ANY(ah))
193185380Ssam		gl = &gainLadder5112;
194185380Ssam	else
195185380Ssam		gl = &gainLadder;
196185380Ssam	gv->currStep = &gl->optStep[gv->currStepNum];
197185380Ssam	if (gv->currGain >= gv->hiTrig) {
198185380Ssam		if (gv->currStepNum == 0) {
199185380Ssam			HALDEBUG(ah, HAL_DEBUG_ANY, "%s: Max gain limit.\n",
200185380Ssam			    __func__);
201185380Ssam			return -1;
202185380Ssam		}
203185380Ssam		HALDEBUG(ah, HAL_DEBUG_RFPARAM,
204185380Ssam		    "%s: Adding gain: currG=%d [%s] --> ",
205185380Ssam		    __func__, gv->currGain, gv->currStep->stepName);
206185380Ssam		gv->targetGain = gv->currGain;
207185380Ssam		while (gv->targetGain >= gv->hiTrig && gv->currStepNum > 0) {
208185380Ssam			gv->targetGain -= 2 * (gl->optStep[--(gv->currStepNum)].stepGain -
209185380Ssam				gv->currStep->stepGain);
210185380Ssam			gv->currStep = &gl->optStep[gv->currStepNum];
211185380Ssam		}
212185380Ssam		HALDEBUG(ah, HAL_DEBUG_RFPARAM, "targG=%d [%s]\n",
213185380Ssam		    gv->targetGain, gv->currStep->stepName);
214185380Ssam		return 1;
215185380Ssam	}
216185380Ssam	if (gv->currGain <= gv->loTrig) {
217185380Ssam		if (gv->currStepNum == gl->numStepsInLadder-1) {
218185380Ssam			HALDEBUG(ah, HAL_DEBUG_RFPARAM,
219185380Ssam			    "%s: Min gain limit.\n", __func__);
220185380Ssam			return -2;
221185380Ssam		}
222185380Ssam		HALDEBUG(ah, HAL_DEBUG_RFPARAM,
223185380Ssam		    "%s: Deducting gain: currG=%d [%s] --> ",
224185380Ssam		    __func__, gv->currGain, gv->currStep->stepName);
225185380Ssam		gv->targetGain = gv->currGain;
226185380Ssam		while (gv->targetGain <= gv->loTrig &&
227185380Ssam		      gv->currStepNum < (gl->numStepsInLadder - 1)) {
228185380Ssam			gv->targetGain -= 2 *
229185380Ssam				(gl->optStep[++(gv->currStepNum)].stepGain - gv->currStep->stepGain);
230185380Ssam			gv->currStep = &gl->optStep[gv->currStepNum];
231185380Ssam		}
232185380Ssam		HALDEBUG(ah, HAL_DEBUG_RFPARAM, "targG=%d [%s]\n",
233185380Ssam		    gv->targetGain, gv->currStep->stepName);
234185380Ssam		return 2;
235185380Ssam	}
236185380Ssam	return 0;		/* caller didn't call needAdjGain first */
237185380Ssam}
238185380Ssam
239185380Ssam/*
240185380Ssam * Read rf register to determine if gainF needs correction
241185380Ssam */
242188197Ssamstatic uint32_t
243185380Ssamar5212GetGainFCorrection(struct ath_hal *ah)
244185380Ssam{
245185380Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
246188197Ssam	uint32_t correction;
247185380Ssam
248185380Ssam	HALASSERT(IS_RADX112_REV2(ah));
249185380Ssam
250188197Ssam	correction = 0;
251185380Ssam	if (ar5212GetRfField(ar5212GetRfBank(ah, 7), 1, 36, 0) == 1) {
252188197Ssam		const GAIN_VALUES *gv = &ahp->ah_gainValues;
253185380Ssam		uint32_t mixGain = gv->currStep->paramVal[0];
254185380Ssam		uint32_t gainStep =
255185380Ssam			ar5212GetRfField(ar5212GetRfBank(ah, 7), 4, 32, 0);
256185380Ssam		switch (mixGain) {
257185380Ssam		case 0 :
258188197Ssam			correction = 0;
259185380Ssam			break;
260185380Ssam		case 1 :
261188197Ssam			correction = gainStep;
262185380Ssam			break;
263185380Ssam		case 2 :
264188197Ssam			correction = 2 * gainStep - 5;
265185380Ssam			break;
266185380Ssam		case 3 :
267188197Ssam			correction = 2 * gainStep;
268185380Ssam			break;
269185380Ssam		}
270185380Ssam	}
271188197Ssam	return correction;
272185380Ssam}
273185380Ssam
274185380Ssam/*
275185380Ssam * Exported call to check for a recent gain reading and return
276185380Ssam * the current state of the thermal calibration gain engine.
277185380Ssam */
278185380SsamHAL_RFGAIN
279185380Ssamar5212GetRfgain(struct ath_hal *ah)
280185380Ssam{
281185380Ssam	struct ath_hal_5212 *ahp = AH5212(ah);
282185380Ssam	GAIN_VALUES *gv = &ahp->ah_gainValues;
283185380Ssam	uint32_t rddata, probeType;
284185380Ssam
285187129Ssam	/* NB: beware of touching the BB when PHY is powered down */
286187129Ssam	if (!gv->active || !ahp->ah_phyPowerOn)
287185380Ssam		return HAL_RFGAIN_INACTIVE;
288185380Ssam
289185380Ssam	if (ahp->ah_rfgainState == HAL_RFGAIN_READ_REQUESTED) {
290185380Ssam		/* Caller had asked to setup a new reading. Check it. */
291185380Ssam		rddata = OS_REG_READ(ah, AR_PHY_PAPD_PROBE);
292185380Ssam
293185380Ssam		if ((rddata & AR_PHY_PAPD_PROBE_NEXT_TX) == 0) {
294185380Ssam			/* bit got cleared, we have a new reading. */
295185380Ssam			gv->currGain = rddata >> AR_PHY_PAPD_PROBE_GAINF_S;
296185380Ssam			probeType = MS(rddata, AR_PHY_PAPD_PROBE_TYPE);
297185380Ssam			if (probeType == AR_PHY_PAPD_PROBE_TYPE_CCK) {
298185380Ssam				const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;
299185380Ssam
300185380Ssam				HALASSERT(IS_RAD5112_ANY(ah));
301185380Ssam				HALASSERT(ah->ah_magic == AR5212_MAGIC);
302185380Ssam				if (AH_PRIVATE(ah)->ah_phyRev >= AR_PHY_CHIP_ID_REV_2)
303185380Ssam					gv->currGain += ee->ee_cckOfdmGainDelta;
304185380Ssam				else
305185380Ssam					gv->currGain += PHY_PROBE_CCK_CORRECTION;
306185380Ssam			}
307185380Ssam			if (IS_RADX112_REV2(ah)) {
308188197Ssam				uint32_t correct = ar5212GetGainFCorrection(ah);
309188197Ssam				if (gv->currGain >= correct)
310188197Ssam					gv->currGain -= correct;
311185380Ssam				else
312185380Ssam					gv->currGain = 0;
313185380Ssam			}
314185380Ssam			/* inactive by default */
315185380Ssam			ahp->ah_rfgainState = HAL_RFGAIN_INACTIVE;
316185380Ssam
317185380Ssam			if (!ar5212InvalidGainReadback(ah, gv) &&
318185380Ssam			    ar5212IsGainAdjustNeeded(ah, gv) &&
319185380Ssam			    ar5212AdjustGain(ah, gv) > 0) {
320185380Ssam				/*
321185380Ssam				 * Change needed. Copy ladder info
322185380Ssam				 * into eeprom info.
323185380Ssam				 */
324185380Ssam				ahp->ah_rfgainState = HAL_RFGAIN_NEED_CHANGE;
325185380Ssam				/* for ap51 */
326185380Ssam				ahp->ah_cwCalRequire = AH_TRUE;
327185380Ssam				/* Request IQ recalibration for temperature chang */
328185380Ssam				ahp->ah_bIQCalibration = IQ_CAL_INACTIVE;
329185380Ssam			}
330185380Ssam		}
331185380Ssam	}
332185380Ssam	return ahp->ah_rfgainState;
333185380Ssam}
334