16e778a7eSPedro F. Giffuni /*-
26e778a7eSPedro F. Giffuni  * SPDX-License-Identifier: ISC
36e778a7eSPedro F. Giffuni  *
414779705SSam Leffler  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
514779705SSam Leffler  * Copyright (c) 2002-2008 Atheros Communications, Inc.
614779705SSam Leffler  *
714779705SSam Leffler  * Permission to use, copy, modify, and/or distribute this software for any
814779705SSam Leffler  * purpose with or without fee is hereby granted, provided that the above
914779705SSam Leffler  * copyright notice and this permission notice appear in all copies.
1014779705SSam Leffler  *
1114779705SSam Leffler  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
1214779705SSam Leffler  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
1314779705SSam Leffler  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
1414779705SSam Leffler  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
1514779705SSam Leffler  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
1614779705SSam Leffler  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
1714779705SSam Leffler  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
1814779705SSam Leffler  */
1914779705SSam Leffler #include "opt_ah.h"
2014779705SSam Leffler 
2114779705SSam Leffler #include "ah.h"
2214779705SSam Leffler #include "ah_internal.h"
2314779705SSam Leffler #include "ah_devid.h"
2414779705SSam Leffler 
2514779705SSam Leffler #include "ar5212/ar5212.h"
2614779705SSam Leffler #include "ar5212/ar5212reg.h"
2714779705SSam Leffler #include "ar5212/ar5212phy.h"
2814779705SSam Leffler 
2914779705SSam Leffler #include "ah_eeprom_v3.h"
3014779705SSam Leffler 
3114779705SSam Leffler static const GAIN_OPTIMIZATION_LADDER gainLadder = {
3214779705SSam Leffler 	9,					/* numStepsInLadder */
3314779705SSam Leffler 	4,					/* defaultStepNum */
3414779705SSam Leffler 	{ { {4, 1, 1, 1},  6, "FG8"},
3514779705SSam Leffler 	  { {4, 0, 1, 1},  4, "FG7"},
3614779705SSam Leffler 	  { {3, 1, 1, 1},  3, "FG6"},
3714779705SSam Leffler 	  { {4, 0, 0, 1},  1, "FG5"},
3814779705SSam Leffler 	  { {4, 1, 1, 0},  0, "FG4"},	/* noJack */
3914779705SSam Leffler 	  { {4, 0, 1, 0}, -2, "FG3"},	/* halfJack */
4014779705SSam Leffler 	  { {3, 1, 1, 0}, -3, "FG2"},	/* clip3 */
4114779705SSam Leffler 	  { {4, 0, 0, 0}, -4, "FG1"},	/* noJack */
4214779705SSam Leffler 	  { {2, 1, 1, 0}, -6, "FG0"} 	/* clip2 */
4314779705SSam Leffler 	}
4414779705SSam Leffler };
4514779705SSam Leffler 
4614779705SSam Leffler static const GAIN_OPTIMIZATION_LADDER gainLadder5112 = {
4714779705SSam Leffler 	8,					/* numStepsInLadder */
4814779705SSam Leffler 	1,					/* defaultStepNum */
4914779705SSam Leffler 	{ { {3, 0,0,0, 0,0,0},   6, "FG7"},	/* most fixed gain */
5014779705SSam Leffler 	  { {2, 0,0,0, 0,0,0},   0, "FG6"},
5114779705SSam Leffler 	  { {1, 0,0,0, 0,0,0},  -3, "FG5"},
5214779705SSam Leffler 	  { {0, 0,0,0, 0,0,0},  -6, "FG4"},
5314779705SSam Leffler 	  { {0, 1,1,0, 0,0,0},  -8, "FG3"},
5414779705SSam Leffler 	  { {0, 1,1,0, 1,1,0}, -10, "FG2"},
5514779705SSam Leffler 	  { {0, 1,0,1, 1,1,0}, -13, "FG1"},
5614779705SSam Leffler 	  { {0, 1,0,1, 1,0,1}, -16, "FG0"},	/* least fixed gain */
5714779705SSam Leffler 	}
5814779705SSam Leffler };
5914779705SSam Leffler 
6014779705SSam Leffler /*
6114779705SSam Leffler  * Initialize the gain structure to good values
6214779705SSam Leffler  */
6314779705SSam Leffler void
ar5212InitializeGainValues(struct ath_hal * ah)6414779705SSam Leffler ar5212InitializeGainValues(struct ath_hal *ah)
6514779705SSam Leffler {
6614779705SSam Leffler 	struct ath_hal_5212 *ahp = AH5212(ah);
6714779705SSam Leffler 	GAIN_VALUES *gv = &ahp->ah_gainValues;
6814779705SSam Leffler 
6914779705SSam Leffler 	/* initialize gain optimization values */
7014779705SSam Leffler 	if (IS_RAD5112_ANY(ah)) {
7114779705SSam Leffler 		gv->currStepNum = gainLadder5112.defaultStepNum;
7214779705SSam Leffler 		gv->currStep =
7314779705SSam Leffler 			&gainLadder5112.optStep[gainLadder5112.defaultStepNum];
7414779705SSam Leffler 		gv->active = AH_TRUE;
7514779705SSam Leffler 		gv->loTrig = 20;
7614779705SSam Leffler 		gv->hiTrig = 85;
7714779705SSam Leffler 	} else {
7814779705SSam Leffler 		gv->currStepNum = gainLadder.defaultStepNum;
7914779705SSam Leffler 		gv->currStep = &gainLadder.optStep[gainLadder.defaultStepNum];
8014779705SSam Leffler 		gv->active = AH_TRUE;
8114779705SSam Leffler 		gv->loTrig = 20;
8214779705SSam Leffler 		gv->hiTrig = 35;
8314779705SSam Leffler 	}
8414779705SSam Leffler }
8514779705SSam Leffler 
8614779705SSam Leffler #define	MAX_ANALOG_START	319		/* XXX */
8714779705SSam Leffler 
8814779705SSam Leffler /*
8914779705SSam Leffler  * Find analog bits of given parameter data and return a reversed value
9014779705SSam Leffler  */
9114779705SSam Leffler static uint32_t
ar5212GetRfField(uint32_t * rfBuf,uint32_t numBits,uint32_t firstBit,uint32_t column)9214779705SSam Leffler ar5212GetRfField(uint32_t *rfBuf, uint32_t numBits, uint32_t firstBit, uint32_t column)
9314779705SSam Leffler {
9414779705SSam Leffler 	uint32_t reg32 = 0, mask, arrayEntry, lastBit;
9514779705SSam Leffler 	uint32_t bitPosition, bitsShifted;
9614779705SSam Leffler 	int32_t bitsLeft;
9714779705SSam Leffler 
9814779705SSam Leffler 	HALASSERT(column <= 3);
9914779705SSam Leffler 	HALASSERT(numBits <= 32);
10014779705SSam Leffler 	HALASSERT(firstBit + numBits <= MAX_ANALOG_START);
10114779705SSam Leffler 
10214779705SSam Leffler 	arrayEntry = (firstBit - 1) / 8;
10314779705SSam Leffler 	bitPosition = (firstBit - 1) % 8;
10414779705SSam Leffler 	bitsLeft = numBits;
10514779705SSam Leffler 	bitsShifted = 0;
10614779705SSam Leffler 	while (bitsLeft > 0) {
10714779705SSam Leffler 		lastBit = (bitPosition + bitsLeft > 8) ?
10814779705SSam Leffler 			(8) : (bitPosition + bitsLeft);
10914779705SSam Leffler 		mask = (((1 << lastBit) - 1) ^ ((1 << bitPosition) - 1)) <<
11014779705SSam Leffler 			(column * 8);
11114779705SSam Leffler 		reg32 |= (((rfBuf[arrayEntry] & mask) >> (column * 8)) >>
11214779705SSam Leffler 			bitPosition) << bitsShifted;
11314779705SSam Leffler 		bitsShifted += lastBit - bitPosition;
11414779705SSam Leffler 		bitsLeft -= (8 - bitPosition);
11514779705SSam Leffler 		bitPosition = 0;
11614779705SSam Leffler 		arrayEntry++;
11714779705SSam Leffler 	}
11814779705SSam Leffler 	reg32 = ath_hal_reverseBits(reg32, numBits);
11914779705SSam Leffler 	return reg32;
12014779705SSam Leffler }
12114779705SSam Leffler 
12214779705SSam Leffler static HAL_BOOL
ar5212InvalidGainReadback(struct ath_hal * ah,GAIN_VALUES * gv)12314779705SSam Leffler ar5212InvalidGainReadback(struct ath_hal *ah, GAIN_VALUES *gv)
12414779705SSam Leffler {
12514779705SSam Leffler 	uint32_t gStep, g, mixOvr;
12614779705SSam Leffler 	uint32_t L1, L2, L3, L4;
12714779705SSam Leffler 
12814779705SSam Leffler 	if (IS_RAD5112_ANY(ah)) {
12914779705SSam Leffler 		mixOvr = ar5212GetRfField(ar5212GetRfBank(ah, 7), 1, 36, 0);
13014779705SSam Leffler 		L1 = 0;
13114779705SSam Leffler 		L2 = 107;
13214779705SSam Leffler 		L3 = 0;
13314779705SSam Leffler 		L4 = 107;
13414779705SSam Leffler 		if (mixOvr == 1) {
13514779705SSam Leffler 			L2 = 83;
13614779705SSam Leffler 			L4 = 83;
13714779705SSam Leffler 			gv->hiTrig = 55;
13814779705SSam Leffler 		}
13914779705SSam Leffler 	} else {
14014779705SSam Leffler 		gStep = ar5212GetRfField(ar5212GetRfBank(ah, 7), 6, 37, 0);
14114779705SSam Leffler 
14214779705SSam Leffler 		L1 = 0;
14314779705SSam Leffler 		L2 = (gStep == 0x3f) ? 50 : gStep + 4;
14414779705SSam Leffler 		L3 = (gStep != 0x3f) ? 0x40 : L1;
14514779705SSam Leffler 		L4 = L3 + 50;
14614779705SSam Leffler 
14714779705SSam Leffler 		gv->loTrig = L1 + (gStep == 0x3f ? DYN_ADJ_LO_MARGIN : 0);
14814779705SSam Leffler 		/* never adjust if != 0x3f */
14914779705SSam Leffler 		gv->hiTrig = L4 - (gStep == 0x3f ? DYN_ADJ_UP_MARGIN : -5);
15014779705SSam Leffler 	}
15114779705SSam Leffler 	g = gv->currGain;
15214779705SSam Leffler 
15314779705SSam Leffler 	return !((g >= L1 && g<= L2) || (g >= L3 && g <= L4));
15414779705SSam Leffler }
15514779705SSam Leffler 
15614779705SSam Leffler /*
15714779705SSam Leffler  * Enable the probe gain check on the next packet
15814779705SSam Leffler  */
15914779705SSam Leffler void
ar5212RequestRfgain(struct ath_hal * ah)16014779705SSam Leffler ar5212RequestRfgain(struct ath_hal *ah)
16114779705SSam Leffler {
16214779705SSam Leffler 	struct ath_hal_5212 *ahp = AH5212(ah);
16314779705SSam Leffler 	uint32_t probePowerIndex;
16414779705SSam Leffler 
16514779705SSam Leffler 	/* Enable the gain readback probe */
16614779705SSam Leffler 	probePowerIndex = ahp->ah_ofdmTxPower + ahp->ah_txPowerIndexOffset;
16714779705SSam Leffler 	OS_REG_WRITE(ah, AR_PHY_PAPD_PROBE,
16814779705SSam Leffler 		  SM(probePowerIndex, AR_PHY_PAPD_PROBE_POWERTX)
16914779705SSam Leffler 		| AR_PHY_PAPD_PROBE_NEXT_TX);
17014779705SSam Leffler 
17114779705SSam Leffler 	ahp->ah_rfgainState = HAL_RFGAIN_READ_REQUESTED;
17214779705SSam Leffler }
17314779705SSam Leffler 
17414779705SSam Leffler /*
17514779705SSam Leffler  * Check to see if our readback gain level sits within the linear
17614779705SSam Leffler  * region of our current variable attenuation window
17714779705SSam Leffler  */
17814779705SSam Leffler static HAL_BOOL
ar5212IsGainAdjustNeeded(struct ath_hal * ah,const GAIN_VALUES * gv)17914779705SSam Leffler ar5212IsGainAdjustNeeded(struct ath_hal *ah, const GAIN_VALUES *gv)
18014779705SSam Leffler {
18114779705SSam Leffler 	return (gv->currGain <= gv->loTrig || gv->currGain >= gv->hiTrig);
18214779705SSam Leffler }
18314779705SSam Leffler 
18414779705SSam Leffler /*
18514779705SSam Leffler  * Move the rabbit ears in the correct direction.
18614779705SSam Leffler  */
18714779705SSam Leffler static int32_t
ar5212AdjustGain(struct ath_hal * ah,GAIN_VALUES * gv)18814779705SSam Leffler ar5212AdjustGain(struct ath_hal *ah, GAIN_VALUES *gv)
18914779705SSam Leffler {
19014779705SSam Leffler 	const GAIN_OPTIMIZATION_LADDER *gl;
19114779705SSam Leffler 
19214779705SSam Leffler 	if (IS_RAD5112_ANY(ah))
19314779705SSam Leffler 		gl = &gainLadder5112;
19414779705SSam Leffler 	else
19514779705SSam Leffler 		gl = &gainLadder;
19614779705SSam Leffler 	gv->currStep = &gl->optStep[gv->currStepNum];
19714779705SSam Leffler 	if (gv->currGain >= gv->hiTrig) {
19814779705SSam Leffler 		if (gv->currStepNum == 0) {
19914779705SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_ANY, "%s: Max gain limit.\n",
20014779705SSam Leffler 			    __func__);
20114779705SSam Leffler 			return -1;
20214779705SSam Leffler 		}
20314779705SSam Leffler 		HALDEBUG(ah, HAL_DEBUG_RFPARAM,
20414779705SSam Leffler 		    "%s: Adding gain: currG=%d [%s] --> ",
20514779705SSam Leffler 		    __func__, gv->currGain, gv->currStep->stepName);
20614779705SSam Leffler 		gv->targetGain = gv->currGain;
20714779705SSam Leffler 		while (gv->targetGain >= gv->hiTrig && gv->currStepNum > 0) {
20814779705SSam Leffler 			gv->targetGain -= 2 * (gl->optStep[--(gv->currStepNum)].stepGain -
20914779705SSam Leffler 				gv->currStep->stepGain);
21014779705SSam Leffler 			gv->currStep = &gl->optStep[gv->currStepNum];
21114779705SSam Leffler 		}
21214779705SSam Leffler 		HALDEBUG(ah, HAL_DEBUG_RFPARAM, "targG=%d [%s]\n",
21314779705SSam Leffler 		    gv->targetGain, gv->currStep->stepName);
21414779705SSam Leffler 		return 1;
21514779705SSam Leffler 	}
21614779705SSam Leffler 	if (gv->currGain <= gv->loTrig) {
21714779705SSam Leffler 		if (gv->currStepNum == gl->numStepsInLadder-1) {
21814779705SSam Leffler 			HALDEBUG(ah, HAL_DEBUG_RFPARAM,
21914779705SSam Leffler 			    "%s: Min gain limit.\n", __func__);
22014779705SSam Leffler 			return -2;
22114779705SSam Leffler 		}
22214779705SSam Leffler 		HALDEBUG(ah, HAL_DEBUG_RFPARAM,
22314779705SSam Leffler 		    "%s: Deducting gain: currG=%d [%s] --> ",
22414779705SSam Leffler 		    __func__, gv->currGain, gv->currStep->stepName);
22514779705SSam Leffler 		gv->targetGain = gv->currGain;
22614779705SSam Leffler 		while (gv->targetGain <= gv->loTrig &&
22714779705SSam Leffler 		      gv->currStepNum < (gl->numStepsInLadder - 1)) {
22814779705SSam Leffler 			gv->targetGain -= 2 *
22914779705SSam Leffler 				(gl->optStep[++(gv->currStepNum)].stepGain - gv->currStep->stepGain);
23014779705SSam Leffler 			gv->currStep = &gl->optStep[gv->currStepNum];
23114779705SSam Leffler 		}
23214779705SSam Leffler 		HALDEBUG(ah, HAL_DEBUG_RFPARAM, "targG=%d [%s]\n",
23314779705SSam Leffler 		    gv->targetGain, gv->currStep->stepName);
23414779705SSam Leffler 		return 2;
23514779705SSam Leffler 	}
23614779705SSam Leffler 	return 0;		/* caller didn't call needAdjGain first */
23714779705SSam Leffler }
23814779705SSam Leffler 
23914779705SSam Leffler /*
24014779705SSam Leffler  * Read rf register to determine if gainF needs correction
24114779705SSam Leffler  */
242e25b0c15SSam Leffler static uint32_t
ar5212GetGainFCorrection(struct ath_hal * ah)24314779705SSam Leffler ar5212GetGainFCorrection(struct ath_hal *ah)
24414779705SSam Leffler {
24514779705SSam Leffler 	struct ath_hal_5212 *ahp = AH5212(ah);
246e25b0c15SSam Leffler 	uint32_t correction;
24714779705SSam Leffler 
24814779705SSam Leffler 	HALASSERT(IS_RADX112_REV2(ah));
24914779705SSam Leffler 
250e25b0c15SSam Leffler 	correction = 0;
25114779705SSam Leffler 	if (ar5212GetRfField(ar5212GetRfBank(ah, 7), 1, 36, 0) == 1) {
252e25b0c15SSam Leffler 		const GAIN_VALUES *gv = &ahp->ah_gainValues;
25314779705SSam Leffler 		uint32_t mixGain = gv->currStep->paramVal[0];
25414779705SSam Leffler 		uint32_t gainStep =
25514779705SSam Leffler 			ar5212GetRfField(ar5212GetRfBank(ah, 7), 4, 32, 0);
25614779705SSam Leffler 		switch (mixGain) {
25714779705SSam Leffler 		case 0 :
258e25b0c15SSam Leffler 			correction = 0;
25914779705SSam Leffler 			break;
26014779705SSam Leffler 		case 1 :
261e25b0c15SSam Leffler 			correction = gainStep;
26214779705SSam Leffler 			break;
26314779705SSam Leffler 		case 2 :
264e25b0c15SSam Leffler 			correction = 2 * gainStep - 5;
26514779705SSam Leffler 			break;
26614779705SSam Leffler 		case 3 :
267e25b0c15SSam Leffler 			correction = 2 * gainStep;
26814779705SSam Leffler 			break;
26914779705SSam Leffler 		}
27014779705SSam Leffler 	}
271e25b0c15SSam Leffler 	return correction;
27214779705SSam Leffler }
27314779705SSam Leffler 
27414779705SSam Leffler /*
27514779705SSam Leffler  * Exported call to check for a recent gain reading and return
27614779705SSam Leffler  * the current state of the thermal calibration gain engine.
27714779705SSam Leffler  */
27814779705SSam Leffler HAL_RFGAIN
ar5212GetRfgain(struct ath_hal * ah)27914779705SSam Leffler ar5212GetRfgain(struct ath_hal *ah)
28014779705SSam Leffler {
28114779705SSam Leffler 	struct ath_hal_5212 *ahp = AH5212(ah);
28214779705SSam Leffler 	GAIN_VALUES *gv = &ahp->ah_gainValues;
28314779705SSam Leffler 	uint32_t rddata, probeType;
28414779705SSam Leffler 
285694be3e5SSam Leffler 	/* NB: beware of touching the BB when PHY is powered down */
286694be3e5SSam Leffler 	if (!gv->active || !ahp->ah_phyPowerOn)
28714779705SSam Leffler 		return HAL_RFGAIN_INACTIVE;
28814779705SSam Leffler 
28914779705SSam Leffler 	if (ahp->ah_rfgainState == HAL_RFGAIN_READ_REQUESTED) {
29014779705SSam Leffler 		/* Caller had asked to setup a new reading. Check it. */
29114779705SSam Leffler 		rddata = OS_REG_READ(ah, AR_PHY_PAPD_PROBE);
29214779705SSam Leffler 
29314779705SSam Leffler 		if ((rddata & AR_PHY_PAPD_PROBE_NEXT_TX) == 0) {
29414779705SSam Leffler 			/* bit got cleared, we have a new reading. */
29514779705SSam Leffler 			gv->currGain = rddata >> AR_PHY_PAPD_PROBE_GAINF_S;
29614779705SSam Leffler 			probeType = MS(rddata, AR_PHY_PAPD_PROBE_TYPE);
29714779705SSam Leffler 			if (probeType == AR_PHY_PAPD_PROBE_TYPE_CCK) {
29814779705SSam Leffler 				const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;
29914779705SSam Leffler 
30014779705SSam Leffler 				HALASSERT(IS_RAD5112_ANY(ah));
30114779705SSam Leffler 				HALASSERT(ah->ah_magic == AR5212_MAGIC);
30214779705SSam Leffler 				if (AH_PRIVATE(ah)->ah_phyRev >= AR_PHY_CHIP_ID_REV_2)
30314779705SSam Leffler 					gv->currGain += ee->ee_cckOfdmGainDelta;
30414779705SSam Leffler 				else
30514779705SSam Leffler 					gv->currGain += PHY_PROBE_CCK_CORRECTION;
30614779705SSam Leffler 			}
30714779705SSam Leffler 			if (IS_RADX112_REV2(ah)) {
308e25b0c15SSam Leffler 				uint32_t correct = ar5212GetGainFCorrection(ah);
309e25b0c15SSam Leffler 				if (gv->currGain >= correct)
310e25b0c15SSam Leffler 					gv->currGain -= correct;
31114779705SSam Leffler 				else
31214779705SSam Leffler 					gv->currGain = 0;
31314779705SSam Leffler 			}
31414779705SSam Leffler 			/* inactive by default */
31514779705SSam Leffler 			ahp->ah_rfgainState = HAL_RFGAIN_INACTIVE;
31614779705SSam Leffler 
31714779705SSam Leffler 			if (!ar5212InvalidGainReadback(ah, gv) &&
31814779705SSam Leffler 			    ar5212IsGainAdjustNeeded(ah, gv) &&
31914779705SSam Leffler 			    ar5212AdjustGain(ah, gv) > 0) {
32014779705SSam Leffler 				/*
32114779705SSam Leffler 				 * Change needed. Copy ladder info
32214779705SSam Leffler 				 * into eeprom info.
32314779705SSam Leffler 				 */
32414779705SSam Leffler 				ahp->ah_rfgainState = HAL_RFGAIN_NEED_CHANGE;
32514779705SSam Leffler 				/* for ap51 */
32614779705SSam Leffler 				ahp->ah_cwCalRequire = AH_TRUE;
32714779705SSam Leffler 				/* Request IQ recalibration for temperature chang */
32814779705SSam Leffler 				ahp->ah_bIQCalibration = IQ_CAL_INACTIVE;
32914779705SSam Leffler 			}
33014779705SSam Leffler 		}
33114779705SSam Leffler 	}
33214779705SSam Leffler 	return ahp->ah_rfgainState;
33314779705SSam Leffler }
334