From owner-svn-src-all@FreeBSD.ORG Tue Mar 8 07:00:00 2011 Return-Path: Delivered-To: svn-src-all@freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:4f8:fff6::34]) by hub.freebsd.org (Postfix) with ESMTP id 13954106564A; Tue, 8 Mar 2011 07:00:00 +0000 (UTC) (envelope-from adrian@FreeBSD.org) Received: from svn.freebsd.org (svn.freebsd.org [IPv6:2001:4f8:fff6::2c]) by mx1.freebsd.org (Postfix) with ESMTP id F380B8FC08; Tue, 8 Mar 2011 06:59:59 +0000 (UTC) Received: from svn.freebsd.org (localhost [127.0.0.1]) by svn.freebsd.org (8.14.3/8.14.3) with ESMTP id p286xxno060454; Tue, 8 Mar 2011 06:59:59 GMT (envelope-from adrian@svn.freebsd.org) Received: (from adrian@localhost) by svn.freebsd.org (8.14.3/8.14.3/Submit) id p286xxV2060442; Tue, 8 Mar 2011 06:59:59 GMT (envelope-from adrian@svn.freebsd.org) Message-Id: <201103080659.p286xxV2060442@svn.freebsd.org> From: Adrian Chadd Date: Tue, 8 Mar 2011 06:59:59 +0000 (UTC) To: src-committers@freebsd.org, svn-src-all@freebsd.org, svn-src-head@freebsd.org X-SVN-Group: head MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cc: Subject: svn commit: r219393 - in head/sys: conf dev/ath/ath_hal/ar5416 dev/ath/ath_hal/ar9002 modules/ath X-BeenThere: svn-src-all@freebsd.org X-Mailman-Version: 2.1.5 Precedence: list List-Id: "SVN commit messages for the entire src tree \(except for " user" and " projects" \)" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 08 Mar 2011 07:00:00 -0000 Author: adrian Date: Tue Mar 8 06:59:59 2011 New Revision: 219393 URL: http://svn.freebsd.org/changeset/base/219393 Log: Implement open-loop TX power control (OLC) for Merlin (AR9280) and generally tidy up the TX power programming code. Enforce that the TX power offset for Merlin is -5 dBm, rather than any other value programmable in the EEPROM. This requires some further code to be ported over from ath9k, so until that is done and tested, fail to attach NICs whose TX power offset isn't -5 dBm. This improves both legacy and HT transmission on my merlin board. It allows for stable MCS TX up to MCS15. Specifics: * Refactor out a bunch of the TX power calibration code - setting/obtaining the power detector / gain boundaries, programming the PDADC * Take the -5 dBm TX power offset into account on Merlin - "0" in the per-rate TX power register means -5 dBm, not 0 dBm * When doing OLC * Enforce min (0) and max (AR5416_MAX_RATE_POWER) when fiddling with the TX power, to avoid the TX power values from wrapping when low. * Implement the 1 dBm cck power offset when doing OLC * Implement temperature compensation for 2.4ghz mode when doing OLC * Implement an AR9280 specific TX power calibration routine which includes the OLC twiddles, leaving the earlier chipset path (AR5416, AR9160) alone Whilst here, use these refactored routines for the AR9285 TX power calibration/programming code and enforce correct overflow/underflow handling when fiddling with TX power values. Obtained from: linux ath9k Added: head/sys/dev/ath/ath_hal/ar9002/ar9002phy.h (contents, props changed) head/sys/dev/ath/ath_hal/ar9002/ar9280_olc.c (contents, props changed) head/sys/dev/ath/ath_hal/ar9002/ar9280_olc.h (contents, props changed) Modified: head/sys/conf/files head/sys/dev/ath/ath_hal/ar5416/ar5416.h head/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c head/sys/dev/ath/ath_hal/ar5416/ar5416_cal.c head/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c head/sys/dev/ath/ath_hal/ar9002/ar9280.h head/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c head/sys/dev/ath/ath_hal/ar9002/ar9285_reset.c head/sys/modules/ath/Makefile Modified: head/sys/conf/files ============================================================================== --- head/sys/conf/files Tue Mar 8 03:04:07 2011 (r219392) +++ head/sys/conf/files Tue Mar 8 06:59:59 2011 (r219393) @@ -759,6 +759,9 @@ dev/ath/ath_hal/ar9001/ar9160_attach.c o dev/ath/ath_hal/ar9002/ar9280_attach.c optional ath_hal | ath_ar9280 | \ ath_ar9285 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" +dev/ath/ath_hal/ar9002/ar9280_olc.c optional ath_hal | ath_ar9280 | \ + ath_ar9285 \ + compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" # ar9285 (depends on ar5416 and ar9280) dev/ath/ath_hal/ar9002/ar9285_attach.c optional ath_hal | ath_ar9285 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" Modified: head/sys/dev/ath/ath_hal/ar5416/ar5416.h ============================================================================== --- head/sys/dev/ath/ath_hal/ar5416/ar5416.h Tue Mar 8 03:04:07 2011 (r219392) +++ head/sys/dev/ath/ath_hal/ar5416/ar5416.h Tue Mar 8 06:59:59 2011 (r219393) @@ -74,6 +74,16 @@ struct ath_hal_5416 { void (*ah_spurMitigate)(struct ath_hal *, const struct ieee80211_channel *); + /* optional open-loop tx power control related methods */ + void (*ah_olcInit)(struct ath_hal *); + void (*ah_olcTempCompensation)(struct ath_hal *); + + /* tx power control */ + HAL_BOOL (*ah_setPowerCalTable) (struct ath_hal *ah, + struct ar5416eeprom *pEepData, + const struct ieee80211_channel *chan, + int16_t *pTxPowerIndexOffset); + u_int ah_globaltxtimeout; /* global tx timeout */ u_int ah_gpioMask; int ah_hangs; /* h/w hangs state */ @@ -93,6 +103,8 @@ struct ath_hal_5416 { struct ar5416NfLimits nf_2g; struct ar5416NfLimits nf_5g; + + int initPDADC; }; #define AH5416(_ah) ((struct ath_hal_5416 *)(_ah)) @@ -192,6 +204,7 @@ extern HAL_RFGAIN ar5416GetRfgain(struct extern HAL_BOOL ar5416Disable(struct ath_hal *ah); extern HAL_BOOL ar5416ChipReset(struct ath_hal *ah, const struct ieee80211_channel *); +extern int ar5416GetRegChainOffset(struct ath_hal *ah, int i); extern HAL_BOOL ar5416SetBoardValues(struct ath_hal *, const struct ieee80211_channel *); extern HAL_BOOL ar5416SetResetReg(struct ath_hal *, uint32_t type); @@ -215,6 +228,28 @@ extern void ar5416GetTargetPowersLeg(str extern void ar5416InitChainMasks(struct ath_hal *ah); extern void ar5416RestoreChainMask(struct ath_hal *ah); +/* TX power setup related routines in ar5416_reset.c */ +extern HAL_BOOL getLowerUpperIndex(uint8_t target, uint8_t *pList, + uint16_t listSize, uint16_t *indexL, uint16_t *indexR); +extern void ar5416GetGainBoundariesAndPdadcs(struct ath_hal *ah, + const struct ieee80211_channel *chan, CAL_DATA_PER_FREQ *pRawDataSet, + uint8_t * bChans, uint16_t availPiers, + uint16_t tPdGainOverlap, int16_t *pMinCalPower, + uint16_t * pPdGainBoundaries, uint8_t * pPDADCValues, + uint16_t numXpdGains); +extern void ar5416SetGainBoundariesClosedLoop(struct ath_hal *ah, + int regChainOffset, uint16_t pdGainOverlap_t2, + uint16_t gainBoundaries[]); +extern uint16_t ar5416GetXpdGainValues(struct ath_hal *ah, uint16_t xpdMask, + uint16_t xpdGainValues[]); +extern void ar5416WriteDetectorGainBiases(struct ath_hal *ah, + uint16_t numXpdGain, uint16_t xpdGainValues[]); +extern void ar5416WritePdadcValues(struct ath_hal *ah, int regChainOffset, + uint8_t pdadcValues[]); +extern HAL_BOOL ar5416SetPowerCalTable(struct ath_hal *ah, + struct ar5416eeprom *pEepData, const struct ieee80211_channel *chan, + int16_t *pTxPowerIndexOffset); + extern HAL_BOOL ar5416StopTxDma(struct ath_hal *ah, u_int q); extern HAL_BOOL ar5416SetupTxDesc(struct ath_hal *ah, struct ath_desc *ds, u_int pktLen, u_int hdrLen, HAL_PKT_TYPE type, u_int txPower, Modified: head/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c ============================================================================== --- head/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c Tue Mar 8 03:04:07 2011 (r219392) +++ head/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c Tue Mar 8 06:59:59 2011 (r219393) @@ -62,6 +62,19 @@ ar5416AniSetup(struct ath_hal *ah) } /* + * AR5416 doesn't do OLC or temperature compensation. + */ +static void +ar5416olcInit(struct ath_hal *ah) +{ +} + +static void +ar5416olcTempCompensation(struct ath_hal *ah) +{ +} + +/* * Attach for an AR5416 part. */ void @@ -161,8 +174,15 @@ ar5416InitState(struct ath_hal_5416 *ahp #endif ahp->ah_priv.ah_getChipPowerLimits = ar5416GetChipPowerLimits; + /* Internal ops */ AH5416(ah)->ah_writeIni = ar5416WriteIni; AH5416(ah)->ah_spurMitigate = ar5416SpurMitigate; + + /* Internal TX power control related operations */ + AH5416(ah)->ah_olcInit = ar5416olcInit; + AH5416(ah)->ah_olcTempCompensation = ar5416olcTempCompensation; + AH5416(ah)->ah_setPowerCalTable = ar5416SetPowerCalTable; + /* * Start by setting all Owl devices to 2x2 */ Modified: head/sys/dev/ath/ath_hal/ar5416/ar5416_cal.c ============================================================================== --- head/sys/dev/ath/ath_hal/ar5416/ar5416_cal.c Tue Mar 8 03:04:07 2011 (r219392) +++ head/sys/dev/ath/ath_hal/ar5416/ar5416_cal.c Tue Mar 8 06:59:59 2011 (r219393) @@ -468,6 +468,9 @@ ar5416PerCalibrationN(struct ath_hal *ah /* Do NF cal only at longer intervals */ if (longcal) { + /* Do temperature compensation if the chipset needs it */ + AH5416(ah)->ah_olcTempCompensation(ah); + /* * Get the value from the previous NF cal * and update the history buffer. Modified: head/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c ============================================================================== --- head/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c Tue Mar 8 03:04:07 2011 (r219392) +++ head/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c Tue Mar 8 06:59:59 2011 (r219393) @@ -61,24 +61,12 @@ static HAL_BOOL ar5416SetPowerPerRateTab uint16_t cfgCtl, uint16_t AntennaReduction, uint16_t twiceMaxRegulatoryPower, uint16_t powerLimit); -static HAL_BOOL ar5416SetPowerCalTable(struct ath_hal *ah, - struct ar5416eeprom *pEepData, - const struct ieee80211_channel *chan, - int16_t *pTxPowerIndexOffset); static uint16_t ar5416GetMaxEdgePower(uint16_t freq, CAL_CTL_EDGES *pRdEdgesPower, HAL_BOOL is2GHz); static int16_t interpolate(uint16_t target, uint16_t srcLeft, uint16_t srcRight, int16_t targetLeft, int16_t targetRight); static void ar5416Set11nRegs(struct ath_hal *ah, const struct ieee80211_channel *chan); -static void ar5416GetGainBoundariesAndPdadcs(struct ath_hal *ah, - const struct ieee80211_channel *chan, CAL_DATA_PER_FREQ *pRawDataSet, - uint8_t * bChans, uint16_t availPiers, - uint16_t tPdGainOverlap, int16_t *pMinCalPower, - uint16_t * pPdGainBoundaries, uint8_t * pPDADCValues, - uint16_t numXpdGains); -static HAL_BOOL getLowerUpperIndex(uint8_t target, uint8_t *pList, - uint16_t listSize, uint16_t *indexL, uint16_t *indexR); static HAL_BOOL ar5416FillVpdTable(uint8_t pwrMin, uint8_t pwrMax, uint8_t *pPwrList, uint8_t *pVpdList, uint16_t numIntercepts, uint8_t *pRetVpdList); @@ -224,6 +212,7 @@ ar5416Reset(struct ath_hal *ah, HAL_OPMO * before any radio register twiddling is done. */ ar5416InitChainMasks(ah); + AH5416(ah)->ah_olcInit(ah); /* Setup the transmit power values. */ if (!ah->ah_setTxPower(ah, chan, rfXpdGain)) { @@ -848,7 +837,7 @@ ar5416SetTransmitPower(struct ath_hal *a return AH_FALSE; } - if (!ar5416SetPowerCalTable(ah, pEepData, chan, &txPowerIndexOffset)) { + if (!AH5416(ah)->ah_setPowerCalTable(ah, pEepData, chan, &txPowerIndexOffset)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unable to set power table\n", __func__); return AH_FALSE; @@ -879,9 +868,71 @@ ar5416SetTransmitPower(struct ath_hal *a } #ifdef AH_EEPROM_DUMP + /* + * Dump the rate array whilst it represents the intended dBm*2 + * values versus what's being adjusted before being programmed + * in. Keep this in mind if you code up this function and enable + * this debugging; the values won't necessarily be what's being + * programmed into the hardware. + */ ar5416PrintPowerPerRate(ah, ratesArray); #endif + /* + * Merlin and later have a power offset, so subtract + * pwr_table_offset * 2 from each value. The default + * power offset is -5 dBm - ie, a register value of 0 + * equates to a TX power of -5 dBm. + */ + if (AR_SREV_MERLIN_20_OR_LATER(ah)) { + int8_t pwr_table_offset; + + (void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET, + &pwr_table_offset); + /* Underflow power gets clamped at raw value 0 */ + /* Overflow power gets camped at AR5416_MAX_RATE_POWER */ + for (i = 0; i < N(ratesArray); i++) { + /* + * + pwr_table_offset is in dBm + * + ratesArray is in 1/2 dBm + */ + ratesArray[i] -= (pwr_table_offset * 2); + if (ratesArray[i] < 0) + ratesArray[i] = 0; + else if (ratesArray[i] > AR5416_MAX_RATE_POWER) + ratesArray[i] = AR5416_MAX_RATE_POWER; + } + } + + /* + * Adjust rates for OLC where needed + * + * The following CCK rates need adjusting when doing 2.4ghz + * CCK transmission. + * + * + rate2s, rate2l, rate1l, rate11s, rate11l, rate5_5s, rate5_5l + * + rateExtCck, rateDupCck + * + * They're adjusted here regardless. The hardware then gets + * programmed as needed. 5GHz operation doesn't program in CCK + * rates for legacy mode but they seem to be initialised for + * HT40 regardless of channel type. + */ + if (AR_SREV_MERLIN_20_OR_LATER(ah) && + ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) { + int adj[] = { + rate2s, rate2l, rate1l, rate11s, rate11l, + rate5_5s, rate5_5l, rateExtCck, rateDupCck + }; + int cck_ofdm_delta = 2; + int i; + for (i = 0; i < N(adj); i++) { + ratesArray[i] -= cck_ofdm_delta; + if (ratesArray[i] < 0) + ratesArray[i] = 0; + } + } + /* Write the OFDM power per rate set */ OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE1, POW_SM(ratesArray[rate18mb], 24) @@ -1297,7 +1348,32 @@ ar5416SetDefGainValues(struct ath_hal *a } } +/* + * Get the register chain offset for the given chain. + * + * Take into account the register chain swapping with AR5416 v2.0. + * + * XXX make sure that the reg chain swapping is only done for + * XXX AR5416 v2.0 or greater, and not later chips? + */ +int +ar5416GetRegChainOffset(struct ath_hal *ah, int i) +{ + int regChainOffset; + + if (AR_SREV_OWL_20_OR_LATER(ah) && + (AH5416(ah)->ah_rx_chainmask == 0x5 || + AH5416(ah)->ah_tx_chainmask == 0x5) && (i != 0)) { + /* Regs are swapped from chain 2 to 1 for 5416 2_0 with + * only chains 0 and 2 populated + */ + regChainOffset = (i == 1) ? 0x2000 : 0x1000; + } else { + regChainOffset = i * 0x1000; + } + return regChainOffset; +} /* * Read EEPROM header info and program the device for correct operation @@ -1323,16 +1399,7 @@ ar5416SetBoardValues(struct ath_hal *ah, if (AR_SREV_MERLIN(ah)) { if (i >= 2) break; } - if (AR_SREV_OWL_20_OR_LATER(ah) && - (AH5416(ah)->ah_rx_chainmask == 0x5 || - AH5416(ah)->ah_tx_chainmask == 0x5) && i != 0) { - /* Regs are swapped from chain 2 to 1 for 5416 2_0 with - * only chains 0 and 2 populated - */ - regChainOffset = (i == 1) ? 0x2000 : 0x1000; - } else { - regChainOffset = i * 0x1000; - } + regChainOffset = ar5416GetRegChainOffset(ah, i); OS_REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0 + regChainOffset, pModal->antCtrlChain[i]); @@ -1857,6 +1924,120 @@ ar5416GetTargetPowersLeg(struct ath_hal } } +/* + * Set the gain boundaries for the given radio chain. + * + * The gain boundaries tell the hardware at what point in the + * PDADC array to "switch over" from one PD gain setting + * to another. There's also a gain overlap between two + * PDADC array gain curves where there's valid PD values + * for 2 gain settings. + * + * The hardware uses the gain overlap and gain boundaries + * to determine which gain curve to use for the given + * target TX power. + */ +void +ar5416SetGainBoundariesClosedLoop(struct ath_hal *ah, int regChainOffset, + uint16_t pdGainOverlap_t2, uint16_t gainBoundaries[]) +{ + OS_REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset, + SM(pdGainOverlap_t2, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | + SM(gainBoundaries[0], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) | + SM(gainBoundaries[1], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) | + SM(gainBoundaries[2], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) | + SM(gainBoundaries[3], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4)); +} + +/* + * Get the gain values and the number of gain levels given + * in xpdMask. + * + * The EEPROM xpdMask determines which power detector gain + * levels were used during calibration. Each of these mask + * bits maps to a fixed gain level in hardware. + */ +uint16_t +ar5416GetXpdGainValues(struct ath_hal *ah, uint16_t xpdMask, + uint16_t xpdGainValues[]) +{ + int i; + uint16_t numXpdGain = 0; + + for (i = 1; i <= AR5416_PD_GAINS_IN_MASK; i++) { + if ((xpdMask >> (AR5416_PD_GAINS_IN_MASK - i)) & 1) { + if (numXpdGain >= AR5416_NUM_PD_GAINS) { + HALASSERT(0); + break; + } + xpdGainValues[numXpdGain] = (uint16_t)(AR5416_PD_GAINS_IN_MASK - i); + numXpdGain++; + } + } + return numXpdGain; +} + +/* + * Write the detector gain and biases. + * + * There are four power detector gain levels. The xpdMask in the EEPROM + * determines which power detector gain levels have TX power calibration + * data associated with them. This function writes the number of + * PD gain levels and their values into the hardware. + * + * This is valid for all TX chains - the calibration data itself however + * will likely differ per-chain. + */ +void +ar5416WriteDetectorGainBiases(struct ath_hal *ah, uint16_t numXpdGain, + uint16_t xpdGainValues[]) +{ + OS_REG_WRITE(ah, AR_PHY_TPCRG1, (OS_REG_READ(ah, AR_PHY_TPCRG1) & + ~(AR_PHY_TPCRG1_NUM_PD_GAIN | AR_PHY_TPCRG1_PD_GAIN_1 | + AR_PHY_TPCRG1_PD_GAIN_2 | AR_PHY_TPCRG1_PD_GAIN_3)) | + SM(numXpdGain - 1, AR_PHY_TPCRG1_NUM_PD_GAIN) | + SM(xpdGainValues[0], AR_PHY_TPCRG1_PD_GAIN_1 ) | + SM(xpdGainValues[1], AR_PHY_TPCRG1_PD_GAIN_2) | + SM(xpdGainValues[2], AR_PHY_TPCRG1_PD_GAIN_3)); +} + +/* + * Write the PDADC array to the given chain offset. + * + * The 32 PDADC registers are written without any care about + * their contents - so if various chips treat values as "special", + * this routine will not care. + */ +void +ar5416WritePdadcValues(struct ath_hal *ah, int regChainOffset, + uint8_t pdadcValues[]) +{ + int regOffset; + int j; + int reg32; + + regOffset = AR_PHY_BASE + (672 << 2) + regChainOffset; + + for (j = 0; j < 32; j++) { + reg32 = ((pdadcValues[4*j + 0] & 0xFF) << 0) | + ((pdadcValues[4*j + 1] & 0xFF) << 8) | + ((pdadcValues[4*j + 2] & 0xFF) << 16) | + ((pdadcValues[4*j + 3] & 0xFF) << 24) ; + OS_REG_WRITE(ah, regOffset, reg32); +#ifdef PDADC_DUMP + ath_hal_printf(ah, "PDADC: Chain %d | PDADC %3d Value %3d |" + " PDADC %3d Value %3d | PDADC %3d Value %3d | PDADC %3d" + " Value %3d |\n", + i, + 4*j, pdadcValues[4*j], + 4*j+1, pdadcValues[4*j + 1], + 4*j+2, pdadcValues[4*j + 2], + 4*j+3, pdadcValues[4*j + 3]); +#endif + regOffset += 4; + } +} + /************************************************************** * ar5416SetPowerCalTable * @@ -1864,7 +2045,7 @@ ar5416GetTargetPowersLeg(struct ath_hal * points as well as from the nearest pier(s) to get a power detector * linear voltage to power level table. */ -static HAL_BOOL +HAL_BOOL ar5416SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset) { @@ -1873,11 +2054,11 @@ ar5416SetPowerCalTable(struct ath_hal *a uint16_t pdGainOverlap_t2; static uint8_t pdadcValues[AR5416_NUM_PDADC_VALUES]; uint16_t gainBoundaries[AR5416_PD_GAINS_IN_MASK]; - uint16_t numPiers, i, j; + uint16_t numPiers, i; int16_t tMinCalPower; uint16_t numXpdGain, xpdMask; uint16_t xpdGainValues[AR5416_NUM_PD_GAINS]; - uint32_t reg32, regOffset, regChainOffset; + uint32_t regChainOffset; OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues)); @@ -1897,36 +2078,14 @@ ar5416SetPowerCalTable(struct ath_hal *a numPiers = AR5416_NUM_5G_CAL_PIERS; } - numXpdGain = 0; /* Calculate the value of xpdgains from the xpdGain Mask */ - for (i = 1; i <= AR5416_PD_GAINS_IN_MASK; i++) { - if ((xpdMask >> (AR5416_PD_GAINS_IN_MASK - i)) & 1) { - if (numXpdGain >= AR5416_NUM_PD_GAINS) { - HALASSERT(0); - break; - } - xpdGainValues[numXpdGain] = (uint16_t)(AR5416_PD_GAINS_IN_MASK - i); - numXpdGain++; - } - } + numXpdGain = ar5416GetXpdGainValues(ah, xpdMask, xpdGainValues); /* Write the detector gain biases and their number */ - OS_REG_WRITE(ah, AR_PHY_TPCRG1, (OS_REG_READ(ah, AR_PHY_TPCRG1) & - ~(AR_PHY_TPCRG1_NUM_PD_GAIN | AR_PHY_TPCRG1_PD_GAIN_1 | AR_PHY_TPCRG1_PD_GAIN_2 | AR_PHY_TPCRG1_PD_GAIN_3)) | - SM(numXpdGain - 1, AR_PHY_TPCRG1_NUM_PD_GAIN) | SM(xpdGainValues[0], AR_PHY_TPCRG1_PD_GAIN_1 ) | - SM(xpdGainValues[1], AR_PHY_TPCRG1_PD_GAIN_2) | SM(xpdGainValues[2], AR_PHY_TPCRG1_PD_GAIN_3)); + ar5416WriteDetectorGainBiases(ah, numXpdGain, xpdGainValues); for (i = 0; i < AR5416_MAX_CHAINS; i++) { - - if (AR_SREV_OWL_20_OR_LATER(ah) && - ( AH5416(ah)->ah_rx_chainmask == 0x5 || AH5416(ah)->ah_tx_chainmask == 0x5) && (i != 0)) { - /* Regs are swapped from chain 2 to 1 for 5416 2_0 with - * only chains 0 and 2 populated - */ - regChainOffset = (i == 1) ? 0x2000 : 0x1000; - } else { - regChainOffset = i * 0x1000; - } + regChainOffset = ar5416GetRegChainOffset(ah, i); if (pEepData->baseEepHeader.txMask & (1 << i)) { if (IEEE80211_IS_CHAN_2GHZ(chan)) { @@ -1935,47 +2094,20 @@ ar5416SetPowerCalTable(struct ath_hal *a pRawDataset = pEepData->calPierData5G[i]; } - ar5416GetGainBoundariesAndPdadcs(ah, chan, pRawDataset, + /* Fetch the gain boundaries and the PDADC values */ + ar5416GetGainBoundariesAndPdadcs(ah, chan, pRawDataset, pCalBChans, numPiers, pdGainOverlap_t2, &tMinCalPower, gainBoundaries, pdadcValues, numXpdGain); if ((i == 0) || AR_SREV_OWL_20_OR_LATER(ah)) { - /* - * Note the pdadc table may not start at 0 dBm power, could be - * negative or greater than 0. Need to offset the power - * values by the amount of minPower for griffin - */ - - OS_REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset, - SM(pdGainOverlap_t2, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | - SM(gainBoundaries[0], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) | - SM(gainBoundaries[1], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) | - SM(gainBoundaries[2], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) | - SM(gainBoundaries[3], AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4)); + ar5416SetGainBoundariesClosedLoop(ah, regChainOffset, + pdGainOverlap_t2, gainBoundaries); } /* Write the power values into the baseband power table */ - regOffset = AR_PHY_BASE + (672 << 2) + regChainOffset; - - for (j = 0; j < 32; j++) { - reg32 = ((pdadcValues[4*j + 0] & 0xFF) << 0) | - ((pdadcValues[4*j + 1] & 0xFF) << 8) | - ((pdadcValues[4*j + 2] & 0xFF) << 16) | - ((pdadcValues[4*j + 3] & 0xFF) << 24) ; - OS_REG_WRITE(ah, regOffset, reg32); - -#ifdef PDADC_DUMP - ath_hal_printf(ah, "PDADC: Chain %d | PDADC %3d Value %3d | PDADC %3d Value %3d | PDADC %3d Value %3d | PDADC %3d Value %3d |\n", - i, - 4*j, pdadcValues[4*j], - 4*j+1, pdadcValues[4*j + 1], - 4*j+2, pdadcValues[4*j + 2], - 4*j+3, pdadcValues[4*j + 3]); -#endif - regOffset += 4; - } + ar5416WritePdadcValues(ah, regChainOffset, pdadcValues); } } *pTxPowerIndexOffset = 0; @@ -1989,7 +2121,7 @@ ar5416SetPowerCalTable(struct ath_hal *a * Uses the data points read from EEPROM to reconstruct the pdadc power table * Called by ar5416SetPowerCalTable only. */ -static void +void ar5416GetGainBoundariesAndPdadcs(struct ath_hal *ah, const struct ieee80211_channel *chan, CAL_DATA_PER_FREQ *pRawDataSet, Added: head/sys/dev/ath/ath_hal/ar9002/ar9002phy.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/ath/ath_hal/ar9002/ar9002phy.h Tue Mar 8 06:59:59 2011 (r219393) @@ -0,0 +1,50 @@ +/* + * Copyright (c) 2010-2011 Adrian Chadd, Xenion Pty Ltd. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#ifndef __ATH_AR9002PHY_H__ +#define __ATH_AR9002PHY_H__ + +#define AR_PHY_TX_PWRCTRL4 0xa264 +#define AR_PHY_TX_PWRCTRL_PD_AVG_VALID 0x00000001 +#define AR_PHY_TX_PWRCTRL_PD_AVG_VALID_S 0 +#define AR_PHY_TX_PWRCTRL_PD_AVG_OUT 0x000001FE +#define AR_PHY_TX_PWRCTRL_PD_AVG_OUT_S 1 + +#define AR_PHY_TX_PWRCTRL6_0 0xa270 +#define AR_PHY_TX_PWRCTRL6_1 0xb270 +#define AR_PHY_TX_PWRCTRL_ERR_EST_MODE 0x03000000 +#define AR_PHY_TX_PWRCTRL_ERR_EST_MODE_S 24 + +#define AR_PHY_TX_PWRCTRL7 0xa274 +#define AR_PHY_TX_PWRCTRL_INIT_TX_GAIN 0x01F80000 +#define AR_PHY_TX_PWRCTRL_INIT_TX_GAIN_S 19 + + +#define AR_PHY_TX_GAIN_TBL1 0xa300 +#define AR_PHY_TX_GAIN 0x0007F000 +#define AR_PHY_TX_GAIN_S 12 + +#endif Modified: head/sys/dev/ath/ath_hal/ar9002/ar9280.h ============================================================================== --- head/sys/dev/ath/ath_hal/ar9002/ar9280.h Tue Mar 8 03:04:07 2011 (r219392) +++ head/sys/dev/ath/ath_hal/ar9002/ar9280.h Tue Mar 8 06:59:59 2011 (r219393) @@ -20,12 +20,23 @@ #include "ar5416/ar5416.h" +/* + * This is a chip thing, but it's used here as part of the + * ath_hal_9280 struct; so it's convienent to locate the + * define here. + */ +#define AR9280_TX_GAIN_TABLE_SIZE 22 + struct ath_hal_9280 { struct ath_hal_5416 ah_5416; HAL_INI_ARRAY ah_ini_xmodes; HAL_INI_ARRAY ah_ini_rxgain; HAL_INI_ARRAY ah_ini_txgain; + + int PDADCdelta; + + uint32_t originalGain[AR9280_TX_GAIN_TABLE_SIZE]; }; #define AH9280(_ah) ((struct ath_hal_9280 *)(_ah)) Modified: head/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c ============================================================================== --- head/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c Tue Mar 8 03:04:07 2011 (r219392) +++ head/sys/dev/ath/ath_hal/ar9002/ar9280_attach.c Tue Mar 8 06:59:59 2011 (r219393) @@ -30,6 +30,7 @@ #include "ar9002/ar9280v1.ini" #include "ar9002/ar9280v2.ini" +#include "ar9002/ar9280_olc.h" static const HAL_PERCAL_DATA ar9280_iq_cal = { /* single sample */ .calName = "IQ", .calType = IQ_MISMATCH_CAL, @@ -112,6 +113,7 @@ ar9280Attach(uint16_t devid, HAL_SOFTC s uint32_t val; HAL_STATUS ecode; HAL_BOOL rfStatus; + int8_t pwr_table_offset; HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", __func__, sc, (void*) st, (void*) sh); @@ -142,6 +144,10 @@ ar9280Attach(uint16_t devid, HAL_SOFTC s AH5416(ah)->ah_spurMitigate = ar9280SpurMitigate; AH5416(ah)->ah_writeIni = ar9280WriteIni; + AH5416(ah)->ah_olcInit = ar9280olcInit; + AH5416(ah)->ah_olcTempCompensation = ar9280olcTemperatureCompensation; + AH5416(ah)->ah_setPowerCalTable = ar9280SetPowerCalTable; + AH5416(ah)->ah_rx_chainmask = AR9280_DEFAULT_RXCHAINMASK; AH5416(ah)->ah_tx_chainmask = AR9280_DEFAULT_TXCHAINMASK; @@ -240,6 +246,18 @@ ar9280Attach(uint16_t devid, HAL_SOFTC s goto bad; } + /* + * Check whether the power table offset isn't the default. + * This can occur with eeprom minor V21 or greater on Merlin. + */ + (void) ath_hal_eepromGet(ah, AR_EEP_PWR_TABLE_OFFSET, &pwr_table_offset); + if (pwr_table_offset != AR5416_PWR_TABLE_OFFSET_DB) { + ath_hal_printf(ah, "ERROR: default pwr offset: %d dBm != EEPROM pwr offset: %d dBm\n", + AR5416_PWR_TABLE_OFFSET_DB, (int) pwr_table_offset); + ecode = HAL_ENOTSUPP; + goto bad; + } + if (AR_SREV_MERLIN_20_OR_LATER(ah)) { /* setup rxgain table */ switch (ath_hal_eepromGet(ah, AR_EEP_RXGAIN_TYPE, AH_NULL)) { Added: head/sys/dev/ath/ath_hal/ar9002/ar9280_olc.c ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/ath/ath_hal/ar9002/ar9280_olc.c Tue Mar 8 06:59:59 2011 (r219393) @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +#include "opt_ah.h" + +#include "ah.h" +#include "ah_internal.h" + +#include "ah_eeprom_v14.h" + +#include "ar9002/ar9280.h" +#include "ar5416/ar5416reg.h" +#include "ar5416/ar5416phy.h" +#include "ar9002/ar9002phy.h" + +#include "ar9002/ar9280_olc.h" + +void +ar9280olcInit(struct ath_hal *ah) +{ + uint32_t i; + + for (i = 0; i < AR9280_TX_GAIN_TABLE_SIZE; i++) + AH9280(ah)->originalGain[i] = MS(OS_REG_READ(ah, + AR_PHY_TX_GAIN_TBL1 + i * 4), AR_PHY_TX_GAIN); + + AH9280(ah)->PDADCdelta = 0; +} + +void +ar9280olcGetTxGainIndex(struct ath_hal *ah, + const struct ieee80211_channel *chan, + struct calDataPerFreqOpLoop *rawDatasetOpLoop, + uint8_t *calChans, uint16_t availPiers, uint8_t *pwr, uint8_t *pcdacIdx) +{ + uint8_t pcdac, i = 0; + uint16_t idxL = 0, idxR = 0, numPiers; + HAL_BOOL match; + CHAN_CENTERS centers; + + ar5416GetChannelCenters(ah, chan, ¢ers); + + for (numPiers = 0; numPiers < availPiers; numPiers++) + if (calChans[numPiers] == AR5416_BCHAN_UNUSED) + break; + + match = getLowerUpperIndex((uint8_t)FREQ2FBIN(centers.synth_center, + IEEE80211_IS_CHAN_2GHZ(chan)), calChans, numPiers, + &idxL, &idxR); + if (match) { + pcdac = rawDatasetOpLoop[idxL].pcdac[0][0]; + *pwr = rawDatasetOpLoop[idxL].pwrPdg[0][0]; + } else { + pcdac = rawDatasetOpLoop[idxR].pcdac[0][0]; + *pwr = (rawDatasetOpLoop[idxL].pwrPdg[0][0] + + rawDatasetOpLoop[idxR].pwrPdg[0][0])/2; + } + while (pcdac > AH9280(ah)->originalGain[i] && + i < (AR9280_TX_GAIN_TABLE_SIZE - 1)) + i++; + + *pcdacIdx = i; +} + +/* + * XXX txPower here is likely not the target txPower in the traditional + * XXX sense, but is set by a call to ar9280olcGetTxGainIndex(). + * XXX Thus, be careful if you're trying to use this routine yourself. + */ +void +ar9280olcGetPDADCs(struct ath_hal *ah, uint32_t initTxGain, int txPower, + uint8_t *pPDADCValues) +{ + uint32_t i; + uint32_t offset; + + OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_0, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3); + OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL6_1, AR_PHY_TX_PWRCTRL_ERR_EST_MODE, 3); + + OS_REG_RMW_FIELD(ah, AR_PHY_TX_PWRCTRL7, AR_PHY_TX_PWRCTRL_INIT_TX_GAIN, initTxGain); + + offset = txPower; + for (i = 0; i < AR5416_NUM_PDADC_VALUES; i++) + if (i < offset) + pPDADCValues[i] = 0x0; + else + pPDADCValues[i] = 0xFF; +} + +/* + * Run temperature compensation calibration. + * + * The TX gain table is adjusted depending upon the difference + * between the initial PDADC value and the currently read + * average TX power sample value. This value is only valid if + * frames have been transmitted, so currPDADC will be 0 if + * no frames have yet been transmitted. + */ +void +ar9280olcTemperatureCompensation(struct ath_hal *ah) +{ + uint32_t rddata, i; + int delta, currPDADC, regval; + uint8_t hpwr_5g = 0; + + rddata = OS_REG_READ(ah, AR_PHY_TX_PWRCTRL4); + currPDADC = MS(rddata, AR_PHY_TX_PWRCTRL_PD_AVG_OUT); + + HALDEBUG(ah, HAL_DEBUG_PERCAL, + "%s: called: initPDADC=%d, currPDADC=%d\n", + __func__, AH5416(ah)->initPDADC, currPDADC); + + if (AH5416(ah)->initPDADC == 0 || currPDADC == 0) + return; + + (void) (ath_hal_eepromGet(ah, AR_EEP_DAC_HPWR_5G, &hpwr_5g)); + + if (hpwr_5g) + delta = (currPDADC - AH5416(ah)->initPDADC + 4) / 8; + else + delta = (currPDADC - AH5416(ah)->initPDADC + 5) / 10; + + HALDEBUG(ah, HAL_DEBUG_PERCAL, "%s: delta=%d, PDADCdelta=%d\n", + __func__, delta, AH9280(ah)->PDADCdelta); + + if (delta != AH9280(ah)->PDADCdelta) { + AH9280(ah)->PDADCdelta = delta; + for (i = 1; i < AR9280_TX_GAIN_TABLE_SIZE; i++) { + regval = AH9280(ah)->originalGain[i] - delta; + if (regval < 0) + regval = 0; + + OS_REG_RMW_FIELD(ah, + AR_PHY_TX_GAIN_TBL1 + i * 4, + AR_PHY_TX_GAIN, regval); + } + } +} + +/* + * This effectively disables the gain boundaries leaving it + * to the open-loop TX power control. + */ +static void +ar9280SetGainBoundariesOpenLoop(struct ath_hal *ah, int regChainOffset, + uint16_t pdGainOverlap_t2, uint16_t gainBoundaries[]) +{ + /* These are unused for OLC */ + (void) pdGainOverlap_t2; + (void) gainBoundaries; + + OS_REG_WRITE(ah, AR_PHY_TPCRG5 + regChainOffset, + SM(0x6, AR_PHY_TPCRG5_PD_GAIN_OVERLAP) | + SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_1) | + SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_2) | + SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_3) | + SM(0x38, AR_PHY_TPCRG5_PD_GAIN_BOUNDARY_4)); +} + +/* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */ +/* XXX shouldn't be here! */ +#define EEP_MINOR(_ah) \ + (AH_PRIVATE(_ah)->ah_eeversion & AR5416_EEP_VER_MINOR_MASK) +#define IS_EEP_MINOR_V2(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_2) +#define IS_EEP_MINOR_V3(_ah) (EEP_MINOR(_ah) >= AR5416_EEP_MINOR_VER_3) + +/************************************************************** + * ar9280SetPowerCalTable + * + * Pull the PDADC piers from cal data and interpolate them across the given + * points as well as from the nearest pier(s) to get a power detector + * linear voltage to power level table. + * + * Handle OLC for Merlin where required. + */ +HAL_BOOL +ar9280SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, + const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset) +{ + CAL_DATA_PER_FREQ *pRawDataset; + uint8_t *pCalBChans = AH_NULL; + uint16_t pdGainOverlap_t2; + static uint8_t pdadcValues[AR5416_NUM_PDADC_VALUES]; + uint16_t gainBoundaries[AR5416_PD_GAINS_IN_MASK]; + uint16_t numPiers, i; + int16_t tMinCalPower; + uint16_t numXpdGain, xpdMask; + uint16_t xpdGainValues[AR5416_NUM_PD_GAINS]; + uint32_t regChainOffset; + + OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues)); + + xpdMask = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].xpdGain; + + if (IS_EEP_MINOR_V2(ah)) { + pdGainOverlap_t2 = pEepData->modalHeader[IEEE80211_IS_CHAN_2GHZ(chan)].pdGainOverlap; + } else { + pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP)); + } + + if (IEEE80211_IS_CHAN_2GHZ(chan)) { + pCalBChans = pEepData->calFreqPier2G; + numPiers = AR5416_NUM_2G_CAL_PIERS; + } else { + pCalBChans = pEepData->calFreqPier5G; + numPiers = AR5416_NUM_5G_CAL_PIERS; + } + + /* If OLC is being done, set the init PDADC value appropriately */ + if (IEEE80211_IS_CHAN_2GHZ(chan) && AR_SREV_MERLIN_20_OR_LATER(ah) && + ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) { + struct calDataPerFreq *pRawDataset = pEepData->calPierData2G[0]; + AH5416(ah)->initPDADC = ((struct calDataPerFreqOpLoop *) pRawDataset)->vpdPdg[0][0]; + } else { + /* + * XXX ath9k doesn't clear this for 5ghz mode if + * it were set in 2ghz mode before! + * The Merlin OLC temperature compensation code + * uses this to calculate the PDADC delta during + * calibration ; 0 here effectively stops the + * temperature compensation calibration from + * occuring. + */ + AH5416(ah)->initPDADC = 0; + } + + /* Calculate the value of xpdgains from the xpdGain Mask */ + numXpdGain = ar5416GetXpdGainValues(ah, xpdMask, xpdGainValues); + + /* Write the detector gain biases and their number */ + ar5416WriteDetectorGainBiases(ah, numXpdGain, xpdGainValues); + + for (i = 0; i < AR5416_MAX_CHAINS; i++) { + regChainOffset = ar5416GetRegChainOffset(ah, i); + if (pEepData->baseEepHeader.txMask & (1 << i)) { + if (IEEE80211_IS_CHAN_2GHZ(chan)) { + pRawDataset = pEepData->calPierData2G[i]; + } else { + pRawDataset = pEepData->calPierData5G[i]; + } + + /* Fetch the gain boundaries and the PDADC values */ + if (AR_SREV_MERLIN_20_OR_LATER(ah) && + ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) { + uint8_t pcdacIdx; + uint8_t txPower; + + ar9280olcGetTxGainIndex(ah, chan, + (struct calDataPerFreqOpLoop *) pRawDataset, + pCalBChans, numPiers, &txPower, &pcdacIdx); + ar9280olcGetPDADCs(ah, pcdacIdx, txPower / 2, pdadcValues); + } else { + ar5416GetGainBoundariesAndPdadcs(ah, chan, + pRawDataset, pCalBChans, numPiers, + pdGainOverlap_t2, &tMinCalPower, + gainBoundaries, pdadcValues, numXpdGain); + } + + /* + * Prior to writing the boundaries or the pdadc vs. power table + * into the chip registers the default starting point on the pdadc + * vs. power table needs to be checked and the curve boundaries + * adjusted accordingly + */ + // XXX ath9k_change_gain_boundary_setting(); + + if ((i == 0) || AR_SREV_OWL_20_OR_LATER(ah)) { + /* Set gain boundaries for either open- or closed-loop TPC */ + if (AR_SREV_MERLIN_20_OR_LATER(ah) && + ath_hal_eepromGetFlag(ah, AR_EEP_OL_PWRCTRL)) + ar9280SetGainBoundariesOpenLoop(ah, + regChainOffset, pdGainOverlap_t2, + gainBoundaries); + else + ar5416SetGainBoundariesClosedLoop(ah, + regChainOffset, pdGainOverlap_t2, + gainBoundaries); + } + + /* + * If this is a board that has a pwrTableOffset that differs from + * the default AR5416_PWR_TABLE_OFFSET_DB then the start of the + * pdadc vs pwr table needs to be adjusted prior to writing to the + * chip. + */ + /* XXX ath9k_adjust_pdadc_values() */ + + /* Write the power values into the baseband power table */ + ar5416WritePdadcValues(ah, regChainOffset, pdadcValues); + } + } + *pTxPowerIndexOffset = 0; + + return AH_TRUE; +} Added: head/sys/dev/ath/ath_hal/ar9002/ar9280_olc.h ============================================================================== --- /dev/null 00:00:00 1970 (empty, because file is newly added) +++ head/sys/dev/ath/ath_hal/ar9002/ar9280_olc.h Tue Mar 8 06:59:59 2011 (r219393) @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2010-2011 Adrian Chadd, Xenion Pty Ltd. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***