Home | History | Annotate | Line # | Download | only in ar5212
      1 /*
      2  * Copyright (c) 2002-2008 Sam Leffler, Errno Consulting
      3  * Copyright (c) 2002-2008 Atheros Communications, Inc.
      4  *
      5  * Permission to use, copy, modify, and/or distribute this software for any
      6  * purpose with or without fee is hereby granted, provided that the above
      7  * copyright notice and this permission notice appear in all copies.
      8  *
      9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     10  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     11  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     12  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     13  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     14  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     15  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     16  *
     17  * $Id: ar5212_rfgain.c,v 1.1.1.1 2008/12/11 04:46:43 alc Exp $
     18  */
     19 #include "opt_ah.h"
     20 
     21 #include "ah.h"
     22 #include "ah_internal.h"
     23 #include "ah_devid.h"
     24 
     25 #include "ar5212/ar5212.h"
     26 #include "ar5212/ar5212reg.h"
     27 #include "ar5212/ar5212phy.h"
     28 
     29 #include "ah_eeprom_v3.h"
     30 
     31 static const GAIN_OPTIMIZATION_LADDER gainLadder = {
     32 	9,					/* numStepsInLadder */
     33 	4,					/* defaultStepNum */
     34 	{ { {4, 1, 1, 1},  6, "FG8"},
     35 	  { {4, 0, 1, 1},  4, "FG7"},
     36 	  { {3, 1, 1, 1},  3, "FG6"},
     37 	  { {4, 0, 0, 1},  1, "FG5"},
     38 	  { {4, 1, 1, 0},  0, "FG4"},	/* noJack */
     39 	  { {4, 0, 1, 0}, -2, "FG3"},	/* halfJack */
     40 	  { {3, 1, 1, 0}, -3, "FG2"},	/* clip3 */
     41 	  { {4, 0, 0, 0}, -4, "FG1"},	/* noJack */
     42 	  { {2, 1, 1, 0}, -6, "FG0"} 	/* clip2 */
     43 	}
     44 };
     45 
     46 static const GAIN_OPTIMIZATION_LADDER gainLadder5112 = {
     47 	8,					/* numStepsInLadder */
     48 	1,					/* defaultStepNum */
     49 	{ { {3, 0,0,0, 0,0,0},   6, "FG7"},	/* most fixed gain */
     50 	  { {2, 0,0,0, 0,0,0},   0, "FG6"},
     51 	  { {1, 0,0,0, 0,0,0},  -3, "FG5"},
     52 	  { {0, 0,0,0, 0,0,0},  -6, "FG4"},
     53 	  { {0, 1,1,0, 0,0,0},  -8, "FG3"},
     54 	  { {0, 1,1,0, 1,1,0}, -10, "FG2"},
     55 	  { {0, 1,0,1, 1,1,0}, -13, "FG1"},
     56 	  { {0, 1,0,1, 1,0,1}, -16, "FG0"},	/* least fixed gain */
     57 	}
     58 };
     59 
     60 /*
     61  * Initialize the gain structure to good values
     62  */
     63 void
     64 ar5212InitializeGainValues(struct ath_hal *ah)
     65 {
     66 	struct ath_hal_5212 *ahp = AH5212(ah);
     67 	GAIN_VALUES *gv = &ahp->ah_gainValues;
     68 
     69 	/* initialize gain optimization values */
     70 	if (IS_RAD5112_ANY(ah)) {
     71 		gv->currStepNum = gainLadder5112.defaultStepNum;
     72 		gv->currStep =
     73 			&gainLadder5112.optStep[gainLadder5112.defaultStepNum];
     74 		gv->active = AH_TRUE;
     75 		gv->loTrig = 20;
     76 		gv->hiTrig = 85;
     77 	} else {
     78 		gv->currStepNum = gainLadder.defaultStepNum;
     79 		gv->currStep = &gainLadder.optStep[gainLadder.defaultStepNum];
     80 		gv->active = AH_TRUE;
     81 		gv->loTrig = 20;
     82 		gv->hiTrig = 35;
     83 	}
     84 }
     85 
     86 #define	MAX_ANALOG_START	319		/* XXX */
     87 
     88 /*
     89  * Find analog bits of given parameter data and return a reversed value
     90  */
     91 static uint32_t
     92 ar5212GetRfField(uint32_t *rfBuf, uint32_t numBits, uint32_t firstBit, uint32_t column)
     93 {
     94 	uint32_t reg32 = 0, mask, arrayEntry, lastBit;
     95 	uint32_t bitPosition, bitsShifted;
     96 	int32_t bitsLeft;
     97 
     98 	HALASSERT(column <= 3);
     99 	HALASSERT(numBits <= 32);
    100 	HALASSERT(firstBit + numBits <= MAX_ANALOG_START);
    101 
    102 	arrayEntry = (firstBit - 1) / 8;
    103 	bitPosition = (firstBit - 1) % 8;
    104 	bitsLeft = numBits;
    105 	bitsShifted = 0;
    106 	while (bitsLeft > 0) {
    107 		lastBit = (bitPosition + bitsLeft > 8) ?
    108 			(8) : (bitPosition + bitsLeft);
    109 		mask = (((1 << lastBit) - 1) ^ ((1 << bitPosition) - 1)) <<
    110 			(column * 8);
    111 		reg32 |= (((rfBuf[arrayEntry] & mask) >> (column * 8)) >>
    112 			bitPosition) << bitsShifted;
    113 		bitsShifted += lastBit - bitPosition;
    114 		bitsLeft -= (8 - bitPosition);
    115 		bitPosition = 0;
    116 		arrayEntry++;
    117 	}
    118 	reg32 = ath_hal_reverseBits(reg32, numBits);
    119 	return reg32;
    120 }
    121 
    122 static HAL_BOOL
    123 ar5212InvalidGainReadback(struct ath_hal *ah, GAIN_VALUES *gv)
    124 {
    125 	uint32_t gStep, g, mixOvr;
    126 	uint32_t L1, L2, L3, L4;
    127 
    128 	if (IS_RAD5112_ANY(ah)) {
    129 		mixOvr = ar5212GetRfField(ar5212GetRfBank(ah, 7), 1, 36, 0);
    130 		L1 = 0;
    131 		L2 = 107;
    132 		L3 = 0;
    133 		L4 = 107;
    134 		if (mixOvr == 1) {
    135 			L2 = 83;
    136 			L4 = 83;
    137 			gv->hiTrig = 55;
    138 		}
    139 	} else {
    140 		gStep = ar5212GetRfField(ar5212GetRfBank(ah, 7), 6, 37, 0);
    141 
    142 		L1 = 0;
    143 		L2 = (gStep == 0x3f) ? 50 : gStep + 4;
    144 		L3 = (gStep != 0x3f) ? 0x40 : L1;
    145 		L4 = L3 + 50;
    146 
    147 		gv->loTrig = L1 + (gStep == 0x3f ? DYN_ADJ_LO_MARGIN : 0);
    148 		/* never adjust if != 0x3f */
    149 		gv->hiTrig = L4 - (gStep == 0x3f ? DYN_ADJ_UP_MARGIN : -5);
    150 	}
    151 	g = gv->currGain;
    152 
    153 	return !((g >= L1 && g<= L2) || (g >= L3 && g <= L4));
    154 }
    155 
    156 /*
    157  * Enable the probe gain check on the next packet
    158  */
    159 void
    160 ar5212RequestRfgain(struct ath_hal *ah)
    161 {
    162 	struct ath_hal_5212 *ahp = AH5212(ah);
    163 	uint32_t probePowerIndex;
    164 
    165 	/* Enable the gain readback probe */
    166 	probePowerIndex = ahp->ah_ofdmTxPower + ahp->ah_txPowerIndexOffset;
    167 	OS_REG_WRITE(ah, AR_PHY_PAPD_PROBE,
    168 		  SM(probePowerIndex, AR_PHY_PAPD_PROBE_POWERTX)
    169 		| AR_PHY_PAPD_PROBE_NEXT_TX);
    170 
    171 	ahp->ah_rfgainState = HAL_RFGAIN_READ_REQUESTED;
    172 }
    173 
    174 /*
    175  * Check to see if our readback gain level sits within the linear
    176  * region of our current variable attenuation window
    177  */
    178 static HAL_BOOL
    179 ar5212IsGainAdjustNeeded(struct ath_hal *ah, const GAIN_VALUES *gv)
    180 {
    181 	return (gv->currGain <= gv->loTrig || gv->currGain >= gv->hiTrig);
    182 }
    183 
    184 /*
    185  * Move the rabbit ears in the correct direction.
    186  */
    187 static int32_t
    188 ar5212AdjustGain(struct ath_hal *ah, GAIN_VALUES *gv)
    189 {
    190 	const GAIN_OPTIMIZATION_LADDER *gl;
    191 
    192 	if (IS_RAD5112_ANY(ah))
    193 		gl = &gainLadder5112;
    194 	else
    195 		gl = &gainLadder;
    196 	gv->currStep = &gl->optStep[gv->currStepNum];
    197 	if (gv->currGain >= gv->hiTrig) {
    198 		if (gv->currStepNum == 0) {
    199 			HALDEBUG(ah, HAL_DEBUG_ANY, "%s: Max gain limit.\n",
    200 			    __func__);
    201 			return -1;
    202 		}
    203 		HALDEBUG(ah, HAL_DEBUG_RFPARAM,
    204 		    "%s: Adding gain: currG=%d [%s] --> ",
    205 		    __func__, gv->currGain, gv->currStep->stepName);
    206 		gv->targetGain = gv->currGain;
    207 		while (gv->targetGain >= gv->hiTrig && gv->currStepNum > 0) {
    208 			gv->targetGain -= 2 * (gl->optStep[--(gv->currStepNum)].stepGain -
    209 				gv->currStep->stepGain);
    210 			gv->currStep = &gl->optStep[gv->currStepNum];
    211 		}
    212 		HALDEBUG(ah, HAL_DEBUG_RFPARAM, "targG=%d [%s]\n",
    213 		    gv->targetGain, gv->currStep->stepName);
    214 		return 1;
    215 	}
    216 	if (gv->currGain <= gv->loTrig) {
    217 		if (gv->currStepNum == gl->numStepsInLadder-1) {
    218 			HALDEBUG(ah, HAL_DEBUG_RFPARAM,
    219 			    "%s: Min gain limit.\n", __func__);
    220 			return -2;
    221 		}
    222 		HALDEBUG(ah, HAL_DEBUG_RFPARAM,
    223 		    "%s: Deducting gain: currG=%d [%s] --> ",
    224 		    __func__, gv->currGain, gv->currStep->stepName);
    225 		gv->targetGain = gv->currGain;
    226 		while (gv->targetGain <= gv->loTrig &&
    227 		      gv->currStepNum < (gl->numStepsInLadder - 1)) {
    228 			gv->targetGain -= 2 *
    229 				(gl->optStep[++(gv->currStepNum)].stepGain - gv->currStep->stepGain);
    230 			gv->currStep = &gl->optStep[gv->currStepNum];
    231 		}
    232 		HALDEBUG(ah, HAL_DEBUG_RFPARAM, "targG=%d [%s]\n",
    233 		    gv->targetGain, gv->currStep->stepName);
    234 		return 2;
    235 	}
    236 	return 0;		/* caller didn't call needAdjGain first */
    237 }
    238 
    239 /*
    240  * Read rf register to determine if gainF needs correction
    241  */
    242 static void
    243 ar5212GetGainFCorrection(struct ath_hal *ah)
    244 {
    245 	struct ath_hal_5212 *ahp = AH5212(ah);
    246 	GAIN_VALUES *gv = &ahp->ah_gainValues;
    247 
    248 	HALASSERT(IS_RADX112_REV2(ah));
    249 
    250 	gv->gainFCorrection = 0;
    251 	if (ar5212GetRfField(ar5212GetRfBank(ah, 7), 1, 36, 0) == 1) {
    252 		uint32_t mixGain = gv->currStep->paramVal[0];
    253 		uint32_t gainStep =
    254 			ar5212GetRfField(ar5212GetRfBank(ah, 7), 4, 32, 0);
    255 		switch (mixGain) {
    256 		case 0 :
    257 			gv->gainFCorrection = 0;
    258 			break;
    259 		case 1 :
    260 			gv->gainFCorrection = gainStep;
    261 			break;
    262 		case 2 :
    263 			gv->gainFCorrection = 2 * gainStep - 5;
    264 			break;
    265 		case 3 :
    266 			gv->gainFCorrection = 2 * gainStep;
    267 			break;
    268 		}
    269 	}
    270 }
    271 
    272 /*
    273  * Exported call to check for a recent gain reading and return
    274  * the current state of the thermal calibration gain engine.
    275  */
    276 HAL_RFGAIN
    277 ar5212GetRfgain(struct ath_hal *ah)
    278 {
    279 	struct ath_hal_5212 *ahp = AH5212(ah);
    280 	GAIN_VALUES *gv = &ahp->ah_gainValues;
    281 	uint32_t rddata, probeType;
    282 
    283 	if (!gv->active)
    284 		return HAL_RFGAIN_INACTIVE;
    285 
    286 	if (ahp->ah_rfgainState == HAL_RFGAIN_READ_REQUESTED) {
    287 		/* Caller had asked to setup a new reading. Check it. */
    288 		rddata = OS_REG_READ(ah, AR_PHY_PAPD_PROBE);
    289 
    290 		if ((rddata & AR_PHY_PAPD_PROBE_NEXT_TX) == 0) {
    291 			/* bit got cleared, we have a new reading. */
    292 			gv->currGain = rddata >> AR_PHY_PAPD_PROBE_GAINF_S;
    293 			probeType = MS(rddata, AR_PHY_PAPD_PROBE_TYPE);
    294 			if (probeType == AR_PHY_PAPD_PROBE_TYPE_CCK) {
    295 				const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;
    296 
    297 				HALASSERT(IS_RAD5112_ANY(ah));
    298 				HALASSERT(ah->ah_magic == AR5212_MAGIC);
    299 				if (AH_PRIVATE(ah)->ah_phyRev >= AR_PHY_CHIP_ID_REV_2)
    300 					gv->currGain += ee->ee_cckOfdmGainDelta;
    301 				else
    302 					gv->currGain += PHY_PROBE_CCK_CORRECTION;
    303 			}
    304 			if (IS_RADX112_REV2(ah)) {
    305 				ar5212GetGainFCorrection(ah);
    306 				if (gv->currGain >= gv->gainFCorrection)
    307 					gv->currGain -= gv->gainFCorrection;
    308 				else
    309 					gv->currGain = 0;
    310 			}
    311 			/* inactive by default */
    312 			ahp->ah_rfgainState = HAL_RFGAIN_INACTIVE;
    313 
    314 			if (!ar5212InvalidGainReadback(ah, gv) &&
    315 			    ar5212IsGainAdjustNeeded(ah, gv) &&
    316 			    ar5212AdjustGain(ah, gv) > 0) {
    317 				/*
    318 				 * Change needed. Copy ladder info
    319 				 * into eeprom info.
    320 				 */
    321 				ahp->ah_rfgainState = HAL_RFGAIN_NEED_CHANGE;
    322 				/* for ap51 */
    323 				ahp->ah_cwCalRequire = AH_TRUE;
    324 				/* Request IQ recalibration for temperature chang */
    325 				ahp->ah_bIQCalibration = IQ_CAL_INACTIVE;
    326 			}
    327 		}
    328 	}
    329 	return ahp->ah_rfgainState;
    330 }
    331