From 7d4f72b39dc0973f57cb310b464d4e08dbd13335 Mon Sep 17 00:00:00 2001 From: Rui Paulo Date: Mon, 15 Feb 2010 17:49:49 +0000 Subject: [PATCH] Bring back AR9285 support. This fixes most of the issues and should be pretty usable. MFC after: 1 month --- sys/conf/files | 13 +- sys/dev/ath/ath_hal/ah.h | 4 + sys/dev/ath/ath_hal/ar5416/ar5416.h | 16 + sys/dev/ath/ath_hal/ar5416/ar5416_attach.c | 2 + sys/dev/ath/ath_hal/ar5416/ar5416_reset.c | 24 +- sys/dev/ath/ath_hal/ar5416/ar9280.h | 3 + sys/dev/ath/ath_hal/ar5416/ar9280_attach.c | 4 +- sys/dev/ath/ath_hal/ar5416/ar9285.c | 64 ++ sys/dev/ath/ath_hal/ar5416/ar9285.h | 43 + sys/dev/ath/ath_hal/ar5416/ar9285_attach.c | 395 +++++++++ sys/dev/ath/ath_hal/ar5416/ar9285_reset.c | 951 +++++++++++++++++++++ 11 files changed, 1495 insertions(+), 24 deletions(-) create mode 100644 sys/dev/ath/ath_hal/ar5416/ar9285.c create mode 100644 sys/dev/ath/ath_hal/ar5416/ar9285.h create mode 100644 sys/dev/ath/ath_hal/ar5416/ar9285_attach.c create mode 100644 sys/dev/ath/ath_hal/ar5416/ar9285_reset.c diff --git a/sys/conf/files b/sys/conf/files index 886765c8442c..7793ec7610b1 100644 --- a/sys/conf/files +++ b/sys/conf/files @@ -555,7 +555,7 @@ dev/ath/ath_hal/ah_eeprom_v14.c \ optional ath_hal | ath_ar5416 | ath_ar9160 | ath_ar9280 \ compile-with "${NORMAL_C} -I$S/dev/ath" dev/ath/ath_hal/ah_eeprom_v4k.c \ - optional ath_hal | ath_ar9280 | ath_ar9285 \ + optional ath_hal | ath_ar9285 \ compile-with "${NORMAL_C} -I$S/dev/ath" dev/ath/ath_hal/ah_regdomain.c optional ath \ compile-with "${NORMAL_C} -I$S/dev/ath" @@ -713,9 +713,14 @@ dev/ath/ath_hal/ar5416/ar5416_xmit.c \ # ar9160 (depends on ar5416) dev/ath/ath_hal/ar5416/ar9160_attach.c optional ath_hal | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" -# ar9280/ar9285 (depends on ar5416) +# ar9280 (depends on ar5416) dev/ath/ath_hal/ar5416/ar9280_attach.c optional ath_hal | ath_ar9280 | \ - ath_ar9285 \ + 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/ar5416/ar9285_attach.c optional ath_hal | ath_ar9285 \ + compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" +dev/ath/ath_hal/ar5416/ar9285_reset.c optional ath_hal | ath_ar9285 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" # rf backends dev/ath/ath_hal/ar5212/ar2316.c optional ath_rf2316 \ @@ -736,6 +741,8 @@ dev/ath/ath_hal/ar5416/ar2133.c optional ath_hal | ath_ar5416 | ath_ar9160 \ compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" dev/ath/ath_hal/ar5416/ar9280.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/ar5416/ar9285.c optional ath_hal | ath_ar9285 \ + compile-with "${NORMAL_C} -I$S/dev/ath -I$S/dev/ath/ath_hal" # ath rate control algorithms dev/ath/ath_rate/amrr/amrr.c optional ath_rate_amrr \ compile-with "${NORMAL_C} -I$S/dev/ath" diff --git a/sys/dev/ath/ath_hal/ah.h b/sys/dev/ath/ath_hal/ah.h index f7434f87eef5..26288489a8cf 100644 --- a/sys/dev/ath/ath_hal/ah.h +++ b/sys/dev/ath/ath_hal/ah.h @@ -638,7 +638,11 @@ struct ath_hal { HAL_BOOL longCal, HAL_BOOL *isCalDone); HAL_BOOL __ahdecl(*ah_resetCalValid)(struct ath_hal *, const struct ieee80211_channel *); + HAL_BOOL __ahdecl(*ah_setTxPower)(struct ath_hal *, + const struct ieee80211_channel *, uint16_t *); HAL_BOOL __ahdecl(*ah_setTxPowerLimit)(struct ath_hal *, uint32_t); + HAL_BOOL __ahdecl(*ah_setBoardValues)(struct ath_hal *, + const struct ieee80211_channel *); /* Transmit functions */ HAL_BOOL __ahdecl(*ah_updateTxTrigLevel)(struct ath_hal*, diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416.h b/sys/dev/ath/ath_hal/ar5416/ar5416.h index 3007229c9a9b..8d1484b60aa7 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar5416.h +++ b/sys/dev/ath/ath_hal/ar5416/ar5416.h @@ -21,6 +21,7 @@ #include "ar5212/ar5212.h" #include "ar5416_cal.h" +#include "ah_eeprom_v14.h" /* for CAL_TARGET_POWER_* */ #define AR5416_MAGIC 0x20065416 @@ -179,12 +180,27 @@ extern HAL_RFGAIN ar5416GetRfgain(struct ath_hal *ah); extern HAL_BOOL ar5416Disable(struct ath_hal *ah); extern HAL_BOOL ar5416ChipReset(struct ath_hal *ah, const struct ieee80211_channel *); +extern HAL_BOOL ar5416SetBoardValues(struct ath_hal *, + const struct ieee80211_channel *); extern HAL_BOOL ar5416SetResetReg(struct ath_hal *, uint32_t type); extern HAL_BOOL ar5416SetTxPowerLimit(struct ath_hal *ah, uint32_t limit); +extern HAL_BOOL ar5416SetTransmitPower(struct ath_hal *, + const struct ieee80211_channel *, uint16_t *); extern HAL_BOOL ar5416GetChipPowerLimits(struct ath_hal *ah, struct ieee80211_channel *chan); extern void ar5416GetChannelCenters(struct ath_hal *, const struct ieee80211_channel *chan, CHAN_CENTERS *centers); +extern void ar5416GetTargetPowers(struct ath_hal *ah, + const struct ieee80211_channel *chan, + CAL_TARGET_POWER_HT *powInfo, + uint16_t numChannels, CAL_TARGET_POWER_HT *pNewPower, + uint16_t numRates, HAL_BOOL isHt40Target); +extern void ar5416GetTargetPowersLeg(struct ath_hal *ah, + const struct ieee80211_channel *chan, + CAL_TARGET_POWER_LEG *powInfo, + uint16_t numChannels, CAL_TARGET_POWER_LEG *pNewPower, + uint16_t numRates, HAL_BOOL isExtTarget); + extern HAL_BOOL ar5416StopTxDma(struct ath_hal *ah, u_int q); extern HAL_BOOL ar5416SetupTxDesc(struct ath_hal *ah, struct ath_desc *ds, diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c b/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c index e59a09962be6..a0bf7bdd627e 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c +++ b/sys/dev/ath/ath_hal/ar5416/ar5416_attach.c @@ -89,6 +89,8 @@ ar5416InitState(struct ath_hal_5416 *ahp5416, uint16_t devid, HAL_SOFTC sc, ah->ah_perCalibrationN = ar5416PerCalibrationN, ah->ah_resetCalValid = ar5416ResetCalValid, ah->ah_setTxPowerLimit = ar5416SetTxPowerLimit; + ah->ah_setTxPower = ar5416SetTransmitPower; + ah->ah_setBoardValues = ar5416SetBoardValues; /* Transmit functions */ ah->ah_stopTxDma = ar5416StopTxDma; diff --git a/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c b/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c index 1b75715f1dfa..22ad3b77303a 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c +++ b/sys/dev/ath/ath_hal/ar5416/ar5416_reset.c @@ -45,9 +45,6 @@ static void ar5416InitIMR(struct ath_hal *ah, HAL_OPMODE opmode); static void ar5416InitQoS(struct ath_hal *ah); static void ar5416InitUserSettings(struct ath_hal *ah); -static HAL_BOOL ar5416SetTransmitPower(struct ath_hal *ah, - const struct ieee80211_channel *chan, uint16_t *rfXpdGain); - #if 0 static HAL_BOOL ar5416ChannelChange(struct ath_hal *, const struct ieee80211_channel *); #endif @@ -56,7 +53,6 @@ static void ar5416SetDeltaSlope(struct ath_hal *, const struct ieee80211_channel static HAL_BOOL ar5416SetResetPowerOn(struct ath_hal *ah); static HAL_BOOL ar5416SetReset(struct ath_hal *ah, int type); static void ar5416InitPLL(struct ath_hal *ah, const struct ieee80211_channel *chan); -static HAL_BOOL ar5416SetBoardValues(struct ath_hal *, const struct ieee80211_channel *); static HAL_BOOL ar5416SetPowerPerRateTable(struct ath_hal *ah, struct ar5416eeprom *pEepData, const struct ieee80211_channel *chan, int16_t *ratesArray, @@ -69,14 +65,6 @@ static HAL_BOOL ar5416SetPowerCalTable(struct ath_hal *ah, int16_t *pTxPowerIndexOffset); static uint16_t ar5416GetMaxEdgePower(uint16_t freq, CAL_CTL_EDGES *pRdEdgesPower, HAL_BOOL is2GHz); -static void ar5416GetTargetPowers(struct ath_hal *ah, - const struct ieee80211_channel *chan, CAL_TARGET_POWER_HT *powInfo, - uint16_t numChannels, CAL_TARGET_POWER_HT *pNewPower, - uint16_t numRates, HAL_BOOL isHt40Target); -static void ar5416GetTargetPowersLeg(struct ath_hal *ah, - const struct ieee80211_channel *chan, CAL_TARGET_POWER_LEG *powInfo, - uint16_t numChannels, CAL_TARGET_POWER_LEG *pNewPower, - uint16_t numRates, HAL_BOOL isExtTarget); static int16_t interpolate(uint16_t target, uint16_t srcLeft, uint16_t srcRight, int16_t targetLeft, int16_t targetRight); @@ -224,7 +212,7 @@ ar5416Reset(struct ath_hal *ah, HAL_OPMODE opmode, OS_REG_WRITE(ah, AR_SELFGEN_MASK, AH5416(ah)->ah_tx_chainmask); /* Setup the transmit power values. */ - if (!ar5416SetTransmitPower(ah, chan, rfXpdGain)) { + if (!ah->ah_setTxPower(ah, chan, rfXpdGain)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error init'ing transmit power\n", __func__); FAIL(HAL_EIO); @@ -245,7 +233,7 @@ ar5416Reset(struct ath_hal *ah, HAL_OPMODE opmode, AH5416(ah)->ah_spurMitigate(ah, chan); /* Setup board specific options for EEPROM version 3 */ - if (!ar5416SetBoardValues(ah, chan)) { + if (!ah->ah_setBoardValues(ah, chan)) { HALDEBUG(ah, HAL_DEBUG_ANY, "%s: error setting board options\n", __func__); FAIL(HAL_EIO); @@ -793,7 +781,7 @@ typedef enum Ar5416_Rates { * Set the transmit power in the baseband for the given * operating channel and mode. */ -static HAL_BOOL +HAL_BOOL ar5416SetTransmitPower(struct ath_hal *ah, const struct ieee80211_channel *chan, uint16_t *rfXpdGain) { @@ -1185,7 +1173,7 @@ ar5416InitPLL(struct ath_hal *ah, const struct ieee80211_channel *chan) * Read EEPROM header info and program the device for correct operation * given the channel value. */ -static HAL_BOOL +HAL_BOOL ar5416SetBoardValues(struct ath_hal *ah, const struct ieee80211_channel *chan) { const HAL_EEPROM_v14 *ee = AH_PRIVATE(ah)->ah_eeprom; @@ -1622,7 +1610,7 @@ ar5416GetMaxEdgePower(uint16_t freq, CAL_CTL_EDGES *pRdEdgesPower, HAL_BOOL is2G * Return the rates of target power for the given target power table * channel, and number of channels */ -static void +void ar5416GetTargetPowers(struct ath_hal *ah, const struct ieee80211_channel *chan, CAL_TARGET_POWER_HT *powInfo, uint16_t numChannels, CAL_TARGET_POWER_HT *pNewPower, uint16_t numRates, @@ -1681,7 +1669,7 @@ ar5416GetTargetPowers(struct ath_hal *ah, const struct ieee80211_channel *chan, * Return the four rates of target power for the given target power table * channel, and number of channels */ -static void +void ar5416GetTargetPowersLeg(struct ath_hal *ah, const struct ieee80211_channel *chan, CAL_TARGET_POWER_LEG *powInfo, uint16_t numChannels, diff --git a/sys/dev/ath/ath_hal/ar5416/ar9280.h b/sys/dev/ath/ath_hal/ar5416/ar9280.h index 2bc7f82b42eb..59039f0157ee 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar9280.h +++ b/sys/dev/ath/ath_hal/ar5416/ar9280.h @@ -39,4 +39,7 @@ HAL_BOOL ar9280RfAttach(struct ath_hal *, HAL_STATUS *); struct ath_hal; HAL_BOOL ar9280SetAntennaSwitch(struct ath_hal *, HAL_ANT_SETTING); +void ar9280SpurMitigate(struct ath_hal *, + const struct ieee80211_channel *); + #endif /* _ATH_AR9280_H_ */ diff --git a/sys/dev/ath/ath_hal/ar5416/ar9280_attach.c b/sys/dev/ath/ath_hal/ar5416/ar9280_attach.c index d998af05943b..8436482b11c8 100644 --- a/sys/dev/ath/ath_hal/ar5416/ar9280_attach.c +++ b/sys/dev/ath/ath_hal/ar5416/ar9280_attach.c @@ -64,8 +64,6 @@ static void ar9280ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore); static HAL_BOOL ar9280FillCapabilityInfo(struct ath_hal *ah); static void ar9280WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan); -static void ar9280SpurMitigate(struct ath_hal *ah, - const struct ieee80211_channel *chan); static void ar9280AniSetup(struct ath_hal *ah) @@ -360,7 +358,7 @@ ar9280WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan) #define AR_SPUR_FEEQ_BOUND_HT40 19 #define AR_SPUR_FEEQ_BOUND_HT20 10 -static void +void ar9280SpurMitigate(struct ath_hal *ah, const struct ieee80211_channel *chan) { static const int pilot_mask_reg[4] = { AR_PHY_TIMING7, AR_PHY_TIMING8, diff --git a/sys/dev/ath/ath_hal/ar5416/ar9285.c b/sys/dev/ath/ath_hal/ar5416/ar9285.c new file mode 100644 index 000000000000..800c92fa1b68 --- /dev/null +++ b/sys/dev/ath/ath_hal/ar5416/ar9285.c @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2008-2009 Sam Leffler, Errno Consulting + * Copyright (c) 2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $FreeBSD$ + */ +#include "opt_ah.h" + +#include "ah.h" +#include "ah_internal.h" + +#include "ah_eeprom_v14.h" + +#include "ar5416/ar9280.h" +#include "ar5416/ar9285.h" +#include "ar5416/ar5416reg.h" +#include "ar5416/ar5416phy.h" + +static void +ar9285GetNoiseFloor(struct ath_hal *ah, int16_t nfarray[]) +{ + int16_t nf; + + nf = MS(OS_REG_READ(ah, AR_PHY_CCA), AR9280_PHY_MINCCA_PWR); + if (nf & 0x100) + nf = 0 - ((nf ^ 0x1ff) + 1); + HALDEBUG(ah, HAL_DEBUG_NFCAL, + "NF calibrated [ctl] [chain 0] is %d\n", nf); + nfarray[0] = nf; + + nfarray[1] = 0; + + nf = MS(OS_REG_READ(ah, AR_PHY_EXT_CCA), AR9280_PHY_EXT_MINCCA_PWR); + if (nf & 0x100) + nf = 0 - ((nf ^ 0x1ff) + 1); + HALDEBUG(ah, HAL_DEBUG_NFCAL, + "NF calibrated [ext] [chain 0] is %d\n", nf); + nfarray[3] = nf; + + nfarray[4] = 0; +} + +HAL_BOOL +ar9285RfAttach(struct ath_hal *ah, HAL_STATUS *status) +{ + if (ar9280RfAttach(ah, status) == AH_FALSE) + return AH_FALSE; + + AH_PRIVATE(ah)->ah_getNoiseFloor = ar9285GetNoiseFloor; + + return AH_TRUE; +} diff --git a/sys/dev/ath/ath_hal/ar5416/ar9285.h b/sys/dev/ath/ath_hal/ar5416/ar9285.h new file mode 100644 index 000000000000..1ee058bf3120 --- /dev/null +++ b/sys/dev/ath/ath_hal/ar5416/ar9285.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2008-2009 Sam Leffler, Errno Consulting + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $FreeBSD$ + */ +#ifndef _ATH_AR9285_H_ +#define _ATH_AR9285_H_ + +#include "ar5416/ar5416.h" + +struct ath_hal_9285 { + struct ath_hal_5416 ah_5416; + + HAL_INI_ARRAY ah_ini_txgain; + HAL_INI_ARRAY ah_ini_rxgain; +}; +#define AH9285(_ah) ((struct ath_hal_9285 *)(_ah)) + +#define AR9285_DEFAULT_RXCHAINMASK 1 +#define AR9285_DEFAULT_TXCHAINMASK 1 + + +HAL_BOOL ar9285SetAntennaSwitch(struct ath_hal *, HAL_ANT_SETTING); +HAL_BOOL ar9285RfAttach(struct ath_hal *, HAL_STATUS *); + +extern HAL_BOOL ar9285SetTransmitPower(struct ath_hal *, + const struct ieee80211_channel *, uint16_t *); +extern HAL_BOOL ar9285SetBoardValues(struct ath_hal *, + const struct ieee80211_channel *); + +#endif /* _ATH_AR9285_H_ */ diff --git a/sys/dev/ath/ath_hal/ar5416/ar9285_attach.c b/sys/dev/ath/ath_hal/ar5416/ar9285_attach.c new file mode 100644 index 000000000000..a63965202185 --- /dev/null +++ b/sys/dev/ath/ath_hal/ar5416/ar9285_attach.c @@ -0,0 +1,395 @@ +/* + * Copyright (c) 2008-2009 Sam Leffler, Errno Consulting + * Copyright (c) 2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $FreeBSD$ + */ +#include "opt_ah.h" + +#include "ah.h" +#include "ah_internal.h" +#include "ah_devid.h" + +#include "ah_eeprom_v4k.h" /* XXX for tx/rx gain */ + +#include "ar5416/ar9280.h" +#include "ar5416/ar9285.h" +#include "ar5416/ar5416reg.h" +#include "ar5416/ar5416phy.h" + +#include "ar5416/ar9285.ini" +#include "ar5416/ar9285v2.ini" +#include "ar5416/ar9280v2.ini" /* XXX ini for tx/rx gain */ + +static const HAL_PERCAL_DATA ar9280_iq_cal = { /* single sample */ + .calName = "IQ", .calType = IQ_MISMATCH_CAL, + .calNumSamples = MIN_CAL_SAMPLES, + .calCountMax = PER_MAX_LOG_COUNT, + .calCollect = ar5416IQCalCollect, + .calPostProc = ar5416IQCalibration +}; +static const HAL_PERCAL_DATA ar9280_adc_gain_cal = { /* single sample */ + .calName = "ADC Gain", .calType = ADC_GAIN_CAL, + .calNumSamples = MIN_CAL_SAMPLES, + .calCountMax = PER_MIN_LOG_COUNT, + .calCollect = ar5416AdcGainCalCollect, + .calPostProc = ar5416AdcGainCalibration +}; +static const HAL_PERCAL_DATA ar9280_adc_dc_cal = { /* single sample */ + .calName = "ADC DC", .calType = ADC_DC_CAL, + .calNumSamples = MIN_CAL_SAMPLES, + .calCountMax = PER_MIN_LOG_COUNT, + .calCollect = ar5416AdcDcCalCollect, + .calPostProc = ar5416AdcDcCalibration +}; +static const HAL_PERCAL_DATA ar9280_adc_init_dc_cal = { + .calName = "ADC Init DC", .calType = ADC_DC_INIT_CAL, + .calNumSamples = MIN_CAL_SAMPLES, + .calCountMax = INIT_LOG_COUNT, + .calCollect = ar5416AdcDcCalCollect, + .calPostProc = ar5416AdcDcCalibration +}; + +static void ar9285ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore); +static HAL_BOOL ar9285FillCapabilityInfo(struct ath_hal *ah); +static void ar9285WriteIni(struct ath_hal *ah, + const struct ieee80211_channel *chan); + +static void +ar9285AniSetup(struct ath_hal *ah) +{ + /* NB: disable ANI for reliable RIFS rx */ + ar5212AniAttach(ah, AH_NULL, AH_NULL, AH_FALSE); +} + +/* + * Attach for an AR9285 part. + */ +static struct ath_hal * +ar9285Attach(uint16_t devid, HAL_SOFTC sc, + HAL_BUS_TAG st, HAL_BUS_HANDLE sh, HAL_STATUS *status) +{ + struct ath_hal_9285 *ahp9285; + struct ath_hal_5212 *ahp; + struct ath_hal *ah; + uint32_t val; + HAL_STATUS ecode; + HAL_BOOL rfStatus; + + HALDEBUG(AH_NULL, HAL_DEBUG_ATTACH, "%s: sc %p st %p sh %p\n", + __func__, sc, (void*) st, (void*) sh); + + /* NB: memory is returned zero'd */ + ahp9285 = ath_hal_malloc(sizeof (struct ath_hal_9285)); + if (ahp9285 == AH_NULL) { + HALDEBUG(AH_NULL, HAL_DEBUG_ANY, + "%s: cannot allocate memory for state block\n", __func__); + *status = HAL_ENOMEM; + return AH_NULL; + } + ahp = AH5212(ahp9285); + ah = &ahp->ah_priv.h; + + ar5416InitState(AH5416(ah), devid, sc, st, sh, status); + + /* XXX override with 9285 specific state */ + /* override 5416 methods for our needs */ + ah->ah_setAntennaSwitch = ar9285SetAntennaSwitch; + ah->ah_configPCIE = ar9285ConfigPCIE; + ah->ah_setTxPower = ar9285SetTransmitPower; + ah->ah_setBoardValues = ar9285SetBoardValues; + + AH5416(ah)->ah_cal.iqCalData.calData = &ar9280_iq_cal; + AH5416(ah)->ah_cal.adcGainCalData.calData = &ar9280_adc_gain_cal; + AH5416(ah)->ah_cal.adcDcCalData.calData = &ar9280_adc_dc_cal; + AH5416(ah)->ah_cal.adcDcCalInitData.calData = &ar9280_adc_init_dc_cal; + AH5416(ah)->ah_cal.suppCals = ADC_GAIN_CAL | ADC_DC_CAL | IQ_MISMATCH_CAL; + + AH5416(ah)->ah_spurMitigate = ar9280SpurMitigate; + AH5416(ah)->ah_writeIni = ar9285WriteIni; + AH5416(ah)->ah_rx_chainmask = AR9285_DEFAULT_RXCHAINMASK; + AH5416(ah)->ah_tx_chainmask = AR9285_DEFAULT_TXCHAINMASK; + + if (!ar5416SetResetReg(ah, HAL_RESET_POWER_ON)) { + /* reset chip */ + HALDEBUG(ah, HAL_DEBUG_ANY, "%s: couldn't reset chip\n", + __func__); + ecode = HAL_EIO; + goto bad; + } + + if (!ar5416SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) { + HALDEBUG(ah, HAL_DEBUG_ANY, "%s: couldn't wakeup chip\n", + __func__); + ecode = HAL_EIO; + goto bad; + } + /* Read Revisions from Chips before taking out of reset */ + val = OS_REG_READ(ah, AR_SREV); + HALDEBUG(ah, HAL_DEBUG_ATTACH, + "%s: ID 0x%x VERSION 0x%x TYPE 0x%x REVISION 0x%x\n", + __func__, MS(val, AR_XSREV_ID), MS(val, AR_XSREV_VERSION), + MS(val, AR_XSREV_TYPE), MS(val, AR_XSREV_REVISION)); + /* NB: include chip type to differentiate from pre-Sowl versions */ + AH_PRIVATE(ah)->ah_macVersion = + (val & AR_XSREV_VERSION) >> AR_XSREV_TYPE_S; + AH_PRIVATE(ah)->ah_macRev = MS(val, AR_XSREV_REVISION); + AH_PRIVATE(ah)->ah_ispcie = (val & AR_XSREV_TYPE_HOST_MODE) == 0; + + /* setup common ini data; rf backends handle remainder */ + if (AR_SREV_KITE_12_OR_LATER(ah)) { + HAL_INI_INIT(&ahp->ah_ini_modes, ar9285Modes_v2, 6); + HAL_INI_INIT(&ahp->ah_ini_common, ar9285Common_v2, 2); + HAL_INI_INIT(&AH5416(ah)->ah_ini_pcieserdes, + ar9285PciePhy_clkreq_always_on_L1_v2, 2); + } else { + HAL_INI_INIT(&ahp->ah_ini_modes, ar9285Modes, 6); + HAL_INI_INIT(&ahp->ah_ini_common, ar9285Common, 2); + HAL_INI_INIT(&AH5416(ah)->ah_ini_pcieserdes, + ar9285PciePhy_clkreq_always_on_L1, 2); + } + ar5416AttachPCIE(ah); + + ecode = ath_hal_v4kEepromAttach(ah); + if (ecode != HAL_OK) + goto bad; + + if (!ar5416ChipReset(ah, AH_NULL)) { /* reset chip */ + HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip reset failed\n", + __func__); + ecode = HAL_EIO; + goto bad; + } + + AH_PRIVATE(ah)->ah_phyRev = OS_REG_READ(ah, AR_PHY_CHIP_ID); + + if (!ar5212ChipTest(ah)) { + HALDEBUG(ah, HAL_DEBUG_ANY, "%s: hardware self-test failed\n", + __func__); + ecode = HAL_ESELFTEST; + goto bad; + } + + /* + * Set correct Baseband to analog shift + * setting to access analog chips. + */ + OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); + + /* Read Radio Chip Rev Extract */ + AH_PRIVATE(ah)->ah_analog5GhzRev = ar5416GetRadioRev(ah); + switch (AH_PRIVATE(ah)->ah_analog5GhzRev & AR_RADIO_SREV_MAJOR) { + case AR_RAD2133_SREV_MAJOR: /* Sowl: 2G/3x3 */ + case AR_RAD5133_SREV_MAJOR: /* Sowl: 2+5G/3x3 */ + break; + default: + if (AH_PRIVATE(ah)->ah_analog5GhzRev == 0) { + AH_PRIVATE(ah)->ah_analog5GhzRev = + AR_RAD5133_SREV_MAJOR; + break; + } +#ifdef AH_DEBUG + HALDEBUG(ah, HAL_DEBUG_ANY, + "%s: 5G Radio Chip Rev 0x%02X is not supported by " + "this driver\n", __func__, + AH_PRIVATE(ah)->ah_analog5GhzRev); + ecode = HAL_ENOTSUPP; + goto bad; +#endif + } + rfStatus = ar9285RfAttach(ah, &ecode); + if (!rfStatus) { + HALDEBUG(ah, HAL_DEBUG_ANY, "%s: RF setup failed, status %u\n", + __func__, ecode); + goto bad; + } + + HAL_INI_INIT(&ahp9285->ah_ini_rxgain, ar9280Modes_original_rxgain_v2, + 6); + /* setup txgain table */ + switch (ath_hal_eepromGet(ah, AR_EEP_TXGAIN_TYPE, AH_NULL)) { + case AR5416_EEP_TXGAIN_HIGH_POWER: + HAL_INI_INIT(&ahp9285->ah_ini_txgain, + ar9285Modes_high_power_tx_gain_v2, 6); + break; + case AR5416_EEP_TXGAIN_ORIG: + HAL_INI_INIT(&ahp9285->ah_ini_txgain, + ar9285Modes_original_tx_gain_v2, 6); + break; + default: + HALASSERT(AH_FALSE); + goto bad; /* XXX ? try to continue */ + } + + /* + * Got everything we need now to setup the capabilities. + */ + if (!ar9285FillCapabilityInfo(ah)) { + ecode = HAL_EEREAD; + goto bad; + } + + ecode = ath_hal_eepromGet(ah, AR_EEP_MACADDR, ahp->ah_macaddr); + if (ecode != HAL_OK) { + HALDEBUG(ah, HAL_DEBUG_ANY, + "%s: error getting mac address from EEPROM\n", __func__); + goto bad; + } + /* XXX How about the serial number ? */ + /* Read Reg Domain */ + AH_PRIVATE(ah)->ah_currentRD = + ath_hal_eepromGet(ah, AR_EEP_REGDMN_0, AH_NULL); + + /* + * ah_miscMode is populated by ar5416FillCapabilityInfo() + * starting from griffin. Set here to make sure that + * AR_MISC_MODE_MIC_NEW_LOC_ENABLE is set before a GTK is + * placed into hardware. + */ + if (ahp->ah_miscMode != 0) + OS_REG_WRITE(ah, AR_MISC_MODE, ahp->ah_miscMode); + + ar9285AniSetup(ah); /* Anti Noise Immunity */ + ar5416InitNfHistBuff(AH5416(ah)->ah_cal.nfCalHist); + + HALDEBUG(ah, HAL_DEBUG_ATTACH, "%s: return\n", __func__); + + return ah; +bad: + if (ah != AH_NULL) + ah->ah_detach(ah); + if (status) + *status = ecode; + return AH_NULL; +} + +static void +ar9285ConfigPCIE(struct ath_hal *ah, HAL_BOOL restore) +{ + if (AH_PRIVATE(ah)->ah_ispcie && !restore) { + ath_hal_ini_write(ah, &AH5416(ah)->ah_ini_pcieserdes, 1, 0); + OS_DELAY(1000); + OS_REG_SET_BIT(ah, AR_PCIE_PM_CTRL, AR_PCIE_PM_CTRL_ENA); + OS_REG_WRITE(ah, AR_WA, AR9285_WA_DEFAULT); + } +} + +static void +ar9285WriteIni(struct ath_hal *ah, const struct ieee80211_channel *chan) +{ + u_int modesIndex, freqIndex; + int regWrites = 0; + + /* Setup the indices for the next set of register array writes */ + /* XXX Ignore 11n dynamic mode on the AR5416 for the moment */ + freqIndex = 2; + if (IEEE80211_IS_CHAN_HT40(chan)) + modesIndex = 3; + else if (IEEE80211_IS_CHAN_108G(chan)) + modesIndex = 5; + else + modesIndex = 4; + + /* Set correct Baseband to analog shift setting to access analog chips. */ + OS_REG_WRITE(ah, AR_PHY(0), 0x00000007); + OS_REG_WRITE(ah, AR_PHY_ADC_SERIAL_CTL, AR_PHY_SEL_INTERNAL_ADDAC); + regWrites = ath_hal_ini_write(ah, &AH5212(ah)->ah_ini_modes, + modesIndex, regWrites); + if (AR_SREV_KITE_12_OR_LATER(ah)) { + regWrites = ath_hal_ini_write(ah, &AH9285(ah)->ah_ini_txgain, + modesIndex, regWrites); + } + regWrites = ath_hal_ini_write(ah, &AH5212(ah)->ah_ini_common, + 1, regWrites); + +} + +/* + * Fill all software cached or static hardware state information. + * Return failure if capabilities are to come from EEPROM and + * cannot be read. + */ +static HAL_BOOL +ar9285FillCapabilityInfo(struct ath_hal *ah) +{ + HAL_CAPABILITIES *pCap = &AH_PRIVATE(ah)->ah_caps; + + if (!ar5416FillCapabilityInfo(ah)) + return AH_FALSE; + pCap->halNumGpioPins = 12; + pCap->halWowSupport = AH_TRUE; + pCap->halWowMatchPatternExact = AH_TRUE; +#if 0 + pCap->halWowMatchPatternDword = AH_TRUE; +#endif + pCap->halCSTSupport = AH_TRUE; + pCap->halRifsRxSupport = AH_TRUE; + pCap->halRifsTxSupport = AH_TRUE; + pCap->halRtsAggrLimit = 64*1024; /* 802.11n max */ + pCap->halExtChanDfsSupport = AH_TRUE; +#if 0 + /* XXX bluetooth */ + pCap->halBtCoexSupport = AH_TRUE; +#endif + pCap->halAutoSleepSupport = AH_FALSE; /* XXX? */ +#if 0 + pCap->hal4kbSplitTransSupport = AH_FALSE; +#endif + pCap->halRxStbcSupport = 1; + pCap->halTxStbcSupport = 1; + + return AH_TRUE; +} + +HAL_BOOL +ar9285SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING settings) +{ +#define ANTENNA0_CHAINMASK 0x1 +#define ANTENNA1_CHAINMASK 0x2 + struct ath_hal_5416 *ahp = AH5416(ah); + + /* Antenna selection is done by setting the tx/rx chainmasks approp. */ + switch (settings) { + case HAL_ANT_FIXED_A: + /* Enable first antenna only */ + ahp->ah_tx_chainmask = ANTENNA0_CHAINMASK; + ahp->ah_rx_chainmask = ANTENNA0_CHAINMASK; + break; + case HAL_ANT_FIXED_B: + /* Enable second antenna only, after checking capability */ + if (AH_PRIVATE(ah)->ah_caps.halTxChainMask > ANTENNA1_CHAINMASK) + ahp->ah_tx_chainmask = ANTENNA1_CHAINMASK; + ahp->ah_rx_chainmask = ANTENNA1_CHAINMASK; + break; + case HAL_ANT_VARIABLE: + /* Restore original chainmask settings */ + /* XXX */ + ahp->ah_tx_chainmask = AR5416_DEFAULT_TXCHAINMASK; + ahp->ah_rx_chainmask = AR5416_DEFAULT_RXCHAINMASK; + break; + } + return AH_TRUE; +#undef ANTENNA0_CHAINMASK +#undef ANTENNA1_CHAINMASK +} + +static const char* +ar9285Probe(uint16_t vendorid, uint16_t devid) +{ + if (vendorid == ATHEROS_VENDOR_ID && devid == AR9285_DEVID_PCIE) + return "Atheros 9285"; + return AH_NULL; +} +AH_CHIP(AR9285, ar9285Probe, ar9285Attach); diff --git a/sys/dev/ath/ath_hal/ar5416/ar9285_reset.c b/sys/dev/ath/ath_hal/ar5416/ar9285_reset.c new file mode 100644 index 000000000000..8c2de10c3c5a --- /dev/null +++ b/sys/dev/ath/ath_hal/ar5416/ar9285_reset.c @@ -0,0 +1,951 @@ +/* + * Copyright (c) 2002-2009 Sam Leffler, Errno Consulting + * Copyright (c) 2002-2008 Atheros Communications, Inc. + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * $FreeBSD$ + */ + +/* + * This is almost the same as ar5416_reset.c but uses the v4k EEPROM and + * supports only 2Ghz operation. + */ + +#include "opt_ah.h" + +#include "ah.h" +#include "ah_internal.h" +#include "ah_devid.h" + +#include "ah_eeprom_v14.h" +#include "ah_eeprom_v4k.h" + +#include "ar5416/ar9285.h" +#include "ar5416/ar5416.h" +#include "ar5416/ar5416reg.h" +#include "ar5416/ar5416phy.h" + +/* Eeprom versioning macros. Returns true if the version is equal or newer than the ver specified */ +#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) + +/* Additional Time delay to wait after activiting the Base band */ +#define BASE_ACTIVATE_DELAY 100 /* 100 usec */ +#define PLL_SETTLE_DELAY 300 /* 300 usec */ +#define RTC_PLL_SETTLE_DELAY 1000 /* 1 ms */ + +static HAL_BOOL ar9285SetPowerPerRateTable(struct ath_hal *ah, + struct ar5416eeprom_4k *pEepData, + const struct ieee80211_channel *chan, int16_t *ratesArray, + uint16_t cfgCtl, uint16_t AntennaReduction, + uint16_t twiceMaxRegulatoryPower, + uint16_t powerLimit); +static HAL_BOOL ar9285SetPowerCalTable(struct ath_hal *ah, + struct ar5416eeprom_4k *pEepData, + const struct ieee80211_channel *chan, + int16_t *pTxPowerIndexOffset); +static int16_t interpolate(uint16_t target, uint16_t srcLeft, + uint16_t srcRight, int16_t targetLeft, int16_t targetRight); +static HAL_BOOL ar9285FillVpdTable(uint8_t, uint8_t, uint8_t *, uint8_t *, + uint16_t, uint8_t *); +static void ar9285GetGainBoundariesAndPdadcs(struct ath_hal *ah, + const struct ieee80211_channel *chan, CAL_DATA_PER_FREQ_4K *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 uint16_t ar9285GetMaxEdgePower(uint16_t, CAL_CTL_EDGES *); + +/* XXX gag, this is sick */ +typedef enum Ar5416_Rates { + rate6mb, rate9mb, rate12mb, rate18mb, + rate24mb, rate36mb, rate48mb, rate54mb, + rate1l, rate2l, rate2s, rate5_5l, + rate5_5s, rate11l, rate11s, rateXr, + rateHt20_0, rateHt20_1, rateHt20_2, rateHt20_3, + rateHt20_4, rateHt20_5, rateHt20_6, rateHt20_7, + rateHt40_0, rateHt40_1, rateHt40_2, rateHt40_3, + rateHt40_4, rateHt40_5, rateHt40_6, rateHt40_7, + rateDupCck, rateDupOfdm, rateExtCck, rateExtOfdm, + Ar5416RateSize +} AR5416_RATES; + +HAL_BOOL +ar9285SetTransmitPower(struct ath_hal *ah, + const struct ieee80211_channel *chan, uint16_t *rfXpdGain) +{ +#define POW_SM(_r, _s) (((_r) & 0x3f) << (_s)) +#define N(a) (sizeof (a) / sizeof (a[0])) + + MODAL_EEP4K_HEADER *pModal; + struct ath_hal_5212 *ahp = AH5212(ah); + int16_t ratesArray[Ar5416RateSize]; + int16_t txPowerIndexOffset = 0; + uint8_t ht40PowerIncForPdadc = 2; + int i; + + uint16_t cfgCtl; + uint16_t powerLimit; + uint16_t twiceAntennaReduction; + uint16_t twiceMaxRegulatoryPower; + int16_t maxPower; + HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom; + struct ar5416eeprom_4k *pEepData = &ee->ee_base; + + HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER14_1); + + /* Setup info for the actual eeprom */ + OS_MEMZERO(ratesArray, sizeof(ratesArray)); + cfgCtl = ath_hal_getctl(ah, chan); + powerLimit = chan->ic_maxregpower * 2; + twiceAntennaReduction = chan->ic_maxantgain; + twiceMaxRegulatoryPower = AH_MIN(MAX_RATE_POWER, AH_PRIVATE(ah)->ah_powerLimit); + pModal = &pEepData->modalHeader; + HALDEBUG(ah, HAL_DEBUG_RESET, "%s Channel=%u CfgCtl=%u\n", + __func__,chan->ic_freq, cfgCtl ); + + if (IS_EEP_MINOR_V2(ah)) { + ht40PowerIncForPdadc = pModal->ht40PowerIncForPdadc; + } + + if (!ar9285SetPowerPerRateTable(ah, pEepData, chan, + &ratesArray[0],cfgCtl, + twiceAntennaReduction, + twiceMaxRegulatoryPower, powerLimit)) { + HALDEBUG(ah, HAL_DEBUG_ANY, + "%s: unable to set tx power per rate table\n", __func__); + return AH_FALSE; + } + + if (!ar9285SetPowerCalTable(ah, pEepData, chan, &txPowerIndexOffset)) { + HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unable to set power table\n", + __func__); + return AH_FALSE; + } + + maxPower = AH_MAX(ratesArray[rate6mb], ratesArray[rateHt20_0]); + maxPower = AH_MAX(maxPower, ratesArray[rate1l]); + + if (IEEE80211_IS_CHAN_HT40(chan)) { + maxPower = AH_MAX(maxPower, ratesArray[rateHt40_0]); + } + + ahp->ah_tx6PowerInHalfDbm = maxPower; + AH_PRIVATE(ah)->ah_maxPowerLevel = maxPower; + ahp->ah_txPowerIndexOffset = txPowerIndexOffset; + + /* + * txPowerIndexOffset is set by the SetPowerTable() call - + * adjust the rate table (0 offset if rates EEPROM not loaded) + */ + for (i = 0; i < N(ratesArray); i++) { + ratesArray[i] = (int16_t)(txPowerIndexOffset + ratesArray[i]); + if (ratesArray[i] > AR5416_MAX_RATE_POWER) + ratesArray[i] = AR5416_MAX_RATE_POWER; + ratesArray[i] -= AR5416_PWR_TABLE_OFFSET_DB * 2; + } + +#ifdef AH_EEPROM_DUMP + ar5416PrintPowerPerRate(ah, ratesArray); +#endif + + /* Write the OFDM power per rate set */ + OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE1, + POW_SM(ratesArray[rate18mb], 24) + | POW_SM(ratesArray[rate12mb], 16) + | POW_SM(ratesArray[rate9mb], 8) + | POW_SM(ratesArray[rate6mb], 0) + ); + OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE2, + POW_SM(ratesArray[rate54mb], 24) + | POW_SM(ratesArray[rate48mb], 16) + | POW_SM(ratesArray[rate36mb], 8) + | POW_SM(ratesArray[rate24mb], 0) + ); + + /* Write the CCK power per rate set */ + OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE3, + POW_SM(ratesArray[rate2s], 24) + | POW_SM(ratesArray[rate2l], 16) + | POW_SM(ratesArray[rateXr], 8) /* XR target power */ + | POW_SM(ratesArray[rate1l], 0) + ); + OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE4, + POW_SM(ratesArray[rate11s], 24) + | POW_SM(ratesArray[rate11l], 16) + | POW_SM(ratesArray[rate5_5s], 8) + | POW_SM(ratesArray[rate5_5l], 0) + ); + HALDEBUG(ah, HAL_DEBUG_RESET, + "%s AR_PHY_POWER_TX_RATE3=0x%x AR_PHY_POWER_TX_RATE4=0x%x\n", + __func__, OS_REG_READ(ah,AR_PHY_POWER_TX_RATE3), + OS_REG_READ(ah,AR_PHY_POWER_TX_RATE4)); + + /* Write the HT20 power per rate set */ + OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE5, + POW_SM(ratesArray[rateHt20_3], 24) + | POW_SM(ratesArray[rateHt20_2], 16) + | POW_SM(ratesArray[rateHt20_1], 8) + | POW_SM(ratesArray[rateHt20_0], 0) + ); + OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE6, + POW_SM(ratesArray[rateHt20_7], 24) + | POW_SM(ratesArray[rateHt20_6], 16) + | POW_SM(ratesArray[rateHt20_5], 8) + | POW_SM(ratesArray[rateHt20_4], 0) + ); + + if (IEEE80211_IS_CHAN_HT40(chan)) { + /* Write the HT40 power per rate set */ + /* Correct PAR difference between HT40 and HT20/LEGACY */ + OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE7, + POW_SM(ratesArray[rateHt40_3] + ht40PowerIncForPdadc, 24) + | POW_SM(ratesArray[rateHt40_2] + ht40PowerIncForPdadc, 16) + | POW_SM(ratesArray[rateHt40_1] + ht40PowerIncForPdadc, 8) + | POW_SM(ratesArray[rateHt40_0] + ht40PowerIncForPdadc, 0) + ); + OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE8, + POW_SM(ratesArray[rateHt40_7] + ht40PowerIncForPdadc, 24) + | POW_SM(ratesArray[rateHt40_6] + ht40PowerIncForPdadc, 16) + | POW_SM(ratesArray[rateHt40_5] + ht40PowerIncForPdadc, 8) + | POW_SM(ratesArray[rateHt40_4] + ht40PowerIncForPdadc, 0) + ); + /* Write the Dup/Ext 40 power per rate set */ + OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE9, + POW_SM(ratesArray[rateExtOfdm], 24) + | POW_SM(ratesArray[rateExtCck], 16) + | POW_SM(ratesArray[rateDupOfdm], 8) + | POW_SM(ratesArray[rateDupCck], 0) + ); + } + + return AH_TRUE; +#undef POW_SM +#undef N +} + +HAL_BOOL +ar9285SetBoardValues(struct ath_hal *ah, const struct ieee80211_channel *chan) +{ + const HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom; + const struct ar5416eeprom_4k *eep = &ee->ee_base; + const MODAL_EEP4K_HEADER *pModal; + int i, regChainOffset; + uint8_t txRxAttenLocal; /* workaround for eeprom versions <= 14.2 */ + + HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER14_1); + pModal = &eep->modalHeader; + + /* NB: workaround for eeprom versions <= 14.2 */ + txRxAttenLocal = 23; + + OS_REG_WRITE(ah, AR_PHY_SWITCH_COM, pModal->antCtrlCommon); + for (i = 0; i < AR5416_4K_MAX_CHAINS; i++) { + 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; + } + + OS_REG_WRITE(ah, AR_PHY_SWITCH_CHAIN_0 + regChainOffset, pModal->antCtrlChain[i]); + OS_REG_WRITE(ah, AR_PHY_TIMING_CTRL4 + regChainOffset, + (OS_REG_READ(ah, AR_PHY_TIMING_CTRL4 + regChainOffset) & + ~(AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF | AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF)) | + SM(pModal->iqCalICh[i], AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF) | + SM(pModal->iqCalQCh[i], AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF)); + + /* + * Large signal upgrade. + * XXX update + */ + + if ((i == 0) || AR_SREV_OWL_20_OR_LATER(ah)) { + OS_REG_WRITE(ah, AR_PHY_RXGAIN + regChainOffset, + (OS_REG_READ(ah, AR_PHY_RXGAIN + regChainOffset) & ~AR_PHY_RXGAIN_TXRX_ATTEN) | + SM(IS_EEP_MINOR_V3(ah) ? pModal->txRxAttenCh[i] : txRxAttenLocal, + AR_PHY_RXGAIN_TXRX_ATTEN)); + + OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + regChainOffset, + (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + regChainOffset) & ~AR_PHY_GAIN_2GHZ_RXTX_MARGIN) | + SM(pModal->rxTxMarginCh[i], AR_PHY_GAIN_2GHZ_RXTX_MARGIN)); + } + } + + OS_REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH, pModal->switchSettling); + OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_ADC, pModal->adcDesiredSize); + OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ, AR_PHY_DESIRED_SZ_PGA, pModal->pgaDesiredSize); + OS_REG_WRITE(ah, AR_PHY_RF_CTL4, + SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAA_OFF) + | SM(pModal->txEndToXpaOff, AR_PHY_RF_CTL4_TX_END_XPAB_OFF) + | SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAA_ON) + | SM(pModal->txFrameToXpaOn, AR_PHY_RF_CTL4_FRAME_XPAB_ON)); + + OS_REG_RMW_FIELD(ah, AR_PHY_RF_CTL3, AR_PHY_TX_END_TO_A2_RX_ON, pModal->txEndToRxOn); + + OS_REG_RMW_FIELD(ah, AR_PHY_CCA, AR9280_PHY_CCA_THRESH62, + pModal->thresh62); + OS_REG_RMW_FIELD(ah, AR_PHY_EXT_CCA0, AR_PHY_EXT_CCA0_THRESH62, + pModal->thresh62); + + /* Minor Version Specific application */ + if (IS_EEP_MINOR_V2(ah)) { + OS_REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_FRAME_TO_DATA_START, pModal->txFrameToDataStart); + OS_REG_RMW_FIELD(ah, AR_PHY_RF_CTL2, AR_PHY_TX_FRAME_TO_PA_ON, pModal->txFrameToPaOn); + } + + if (IS_EEP_MINOR_V3(ah)) { + if (IEEE80211_IS_CHAN_HT40(chan)) { + /* Overwrite switch settling with HT40 value */ + OS_REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_SWITCH, pModal->swSettleHt40); + } + + if ((AR_SREV_OWL_20_OR_LATER(ah)) && + ( AH5416(ah)->ah_rx_chainmask == 0x5 || AH5416(ah)->ah_tx_chainmask == 0x5)){ + /* Reg Offsets are swapped for logical mapping */ + OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + 0x1000, (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + 0x1000) & ~AR_PHY_GAIN_2GHZ_BSW_MARGIN) | + SM(pModal->bswMargin[2], AR_PHY_GAIN_2GHZ_BSW_MARGIN)); + OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + 0x1000, (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + 0x1000) & ~AR_PHY_GAIN_2GHZ_BSW_ATTEN) | + SM(pModal->bswAtten[2], AR_PHY_GAIN_2GHZ_BSW_ATTEN)); + OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + 0x2000, (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + 0x2000) & ~AR_PHY_GAIN_2GHZ_BSW_MARGIN) | + SM(pModal->bswMargin[1], AR_PHY_GAIN_2GHZ_BSW_MARGIN)); + OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + 0x2000, (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + 0x2000) & ~AR_PHY_GAIN_2GHZ_BSW_ATTEN) | + SM(pModal->bswAtten[1], AR_PHY_GAIN_2GHZ_BSW_ATTEN)); + } else { + OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + 0x1000, (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + 0x1000) & ~AR_PHY_GAIN_2GHZ_BSW_MARGIN) | + SM(pModal->bswMargin[1], AR_PHY_GAIN_2GHZ_BSW_MARGIN)); + OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + 0x1000, (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + 0x1000) & ~AR_PHY_GAIN_2GHZ_BSW_ATTEN) | + SM(pModal->bswAtten[1], AR_PHY_GAIN_2GHZ_BSW_ATTEN)); + OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + 0x2000, (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + 0x2000) & ~AR_PHY_GAIN_2GHZ_BSW_MARGIN) | + SM(pModal->bswMargin[2],AR_PHY_GAIN_2GHZ_BSW_MARGIN)); + OS_REG_WRITE(ah, AR_PHY_GAIN_2GHZ + 0x2000, (OS_REG_READ(ah, AR_PHY_GAIN_2GHZ + 0x2000) & ~AR_PHY_GAIN_2GHZ_BSW_ATTEN) | + SM(pModal->bswAtten[2], AR_PHY_GAIN_2GHZ_BSW_ATTEN)); + } + OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ, AR_PHY_GAIN_2GHZ_BSW_MARGIN, pModal->bswMargin[0]); + OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ, AR_PHY_GAIN_2GHZ_BSW_ATTEN, pModal->bswAtten[0]); + } + return AH_TRUE; +} + +/* + * Helper functions common for AP/CB/XB + */ + +static HAL_BOOL +ar9285SetPowerPerRateTable(struct ath_hal *ah, struct ar5416eeprom_4k *pEepData, + const struct ieee80211_channel *chan, + int16_t *ratesArray, uint16_t cfgCtl, + uint16_t AntennaReduction, + uint16_t twiceMaxRegulatoryPower, + uint16_t powerLimit) +{ +#define N(a) (sizeof(a)/sizeof(a[0])) +/* Local defines to distinguish between extension and control CTL's */ +#define EXT_ADDITIVE (0x8000) +#define CTL_11G_EXT (CTL_11G | EXT_ADDITIVE) +#define CTL_11B_EXT (CTL_11B | EXT_ADDITIVE) + + uint16_t twiceMaxEdgePower = AR5416_MAX_RATE_POWER; + int i; + int16_t twiceLargestAntenna; + CAL_CTL_DATA_4K *rep; + CAL_TARGET_POWER_LEG targetPowerOfdm, targetPowerCck = {0, {0, 0, 0, 0}}; + CAL_TARGET_POWER_LEG targetPowerOfdmExt = {0, {0, 0, 0, 0}}, targetPowerCckExt = {0, {0, 0, 0, 0}}; + CAL_TARGET_POWER_HT targetPowerHt20, targetPowerHt40 = {0, {0, 0, 0, 0}}; + int16_t scaledPower, minCtlPower; + +#define SUB_NUM_CTL_MODES_AT_2G_40 3 /* excluding HT40, EXT-OFDM, EXT-CCK */ + static const uint16_t ctlModesFor11g[] = { + CTL_11B, CTL_11G, CTL_2GHT20, CTL_11B_EXT, CTL_11G_EXT, CTL_2GHT40 + }; + const uint16_t *pCtlMode; + uint16_t numCtlModes, ctlMode, freq; + CHAN_CENTERS centers; + + ar5416GetChannelCenters(ah, chan, ¢ers); + + /* Compute TxPower reduction due to Antenna Gain */ + + twiceLargestAntenna = pEepData->modalHeader.antennaGainCh[0]; + twiceLargestAntenna = (int16_t)AH_MIN((AntennaReduction) - twiceLargestAntenna, 0); + + /* XXX setup for 5212 use (really used?) */ + ath_hal_eepromSet(ah, AR_EEP_ANTGAINMAX_2, twiceLargestAntenna); + + /* + * scaledPower is the minimum of the user input power level and + * the regulatory allowed power level + */ + scaledPower = AH_MIN(powerLimit, twiceMaxRegulatoryPower + twiceLargestAntenna); + + /* Get target powers from EEPROM - our baseline for TX Power */ + /* Setup for CTL modes */ + numCtlModes = N(ctlModesFor11g) - SUB_NUM_CTL_MODES_AT_2G_40; /* CTL_11B, CTL_11G, CTL_2GHT20 */ + pCtlMode = ctlModesFor11g; + + ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPowerCck, + AR5416_4K_NUM_2G_CCK_TARGET_POWERS, &targetPowerCck, 4, AH_FALSE); + ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPower2G, + AR5416_4K_NUM_2G_20_TARGET_POWERS, &targetPowerOfdm, 4, AH_FALSE); + ar5416GetTargetPowers(ah, chan, pEepData->calTargetPower2GHT20, + AR5416_4K_NUM_2G_20_TARGET_POWERS, &targetPowerHt20, 8, AH_FALSE); + + if (IEEE80211_IS_CHAN_HT40(chan)) { + numCtlModes = N(ctlModesFor11g); /* All 2G CTL's */ + + ar5416GetTargetPowers(ah, chan, pEepData->calTargetPower2GHT40, + AR5416_4K_NUM_2G_40_TARGET_POWERS, &targetPowerHt40, 8, AH_TRUE); + /* Get target powers for extension channels */ + ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPowerCck, + AR5416_4K_NUM_2G_CCK_TARGET_POWERS, &targetPowerCckExt, 4, AH_TRUE); + ar5416GetTargetPowersLeg(ah, chan, pEepData->calTargetPower2G, + AR5416_4K_NUM_2G_20_TARGET_POWERS, &targetPowerOfdmExt, 4, AH_TRUE); + } + + /* + * For MIMO, need to apply regulatory caps individually across dynamically + * running modes: CCK, OFDM, HT20, HT40 + * + * The outer loop walks through each possible applicable runtime mode. + * The inner loop walks through each ctlIndex entry in EEPROM. + * The ctl value is encoded as [7:4] == test group, [3:0] == test mode. + * + */ + for (ctlMode = 0; ctlMode < numCtlModes; ctlMode++) { + HAL_BOOL isHt40CtlMode = (pCtlMode[ctlMode] == CTL_5GHT40) || + (pCtlMode[ctlMode] == CTL_2GHT40); + if (isHt40CtlMode) { + freq = centers.ctl_center; + } else if (pCtlMode[ctlMode] & EXT_ADDITIVE) { + freq = centers.ext_center; + } else { + freq = centers.ctl_center; + } + + /* walk through each CTL index stored in EEPROM */ + for (i = 0; (i < AR5416_4K_NUM_CTLS) && pEepData->ctlIndex[i]; i++) { + uint16_t twiceMinEdgePower; + + /* compare test group from regulatory channel list with test mode from pCtlMode list */ + if ((((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == pEepData->ctlIndex[i]) || + (((cfgCtl & ~CTL_MODE_M) | (pCtlMode[ctlMode] & CTL_MODE_M)) == + ((pEepData->ctlIndex[i] & CTL_MODE_M) | SD_NO_CTL))) { + rep = &(pEepData->ctlData[i]); + twiceMinEdgePower = ar9285GetMaxEdgePower(freq, + rep->ctlEdges[ + owl_get_ntxchains(AH5416(ah)->ah_tx_chainmask) - 1]); + if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) { + /* Find the minimum of all CTL edge powers that apply to this channel */ + twiceMaxEdgePower = AH_MIN(twiceMaxEdgePower, twiceMinEdgePower); + } else { + /* specific */ + twiceMaxEdgePower = twiceMinEdgePower; + break; + } + } + } + minCtlPower = (uint8_t)AH_MIN(twiceMaxEdgePower, scaledPower); + /* Apply ctl mode to correct target power set */ + switch(pCtlMode[ctlMode]) { + case CTL_11B: + for (i = 0; i < N(targetPowerCck.tPow2x); i++) { + targetPowerCck.tPow2x[i] = (uint8_t)AH_MIN(targetPowerCck.tPow2x[i], minCtlPower); + } + break; + case CTL_11A: + case CTL_11G: + for (i = 0; i < N(targetPowerOfdm.tPow2x); i++) { + targetPowerOfdm.tPow2x[i] = (uint8_t)AH_MIN(targetPowerOfdm.tPow2x[i], minCtlPower); + } + break; + case CTL_5GHT20: + case CTL_2GHT20: + for (i = 0; i < N(targetPowerHt20.tPow2x); i++) { + targetPowerHt20.tPow2x[i] = (uint8_t)AH_MIN(targetPowerHt20.tPow2x[i], minCtlPower); + } + break; + case CTL_11B_EXT: + targetPowerCckExt.tPow2x[0] = (uint8_t)AH_MIN(targetPowerCckExt.tPow2x[0], minCtlPower); + break; + case CTL_11G_EXT: + targetPowerOfdmExt.tPow2x[0] = (uint8_t)AH_MIN(targetPowerOfdmExt.tPow2x[0], minCtlPower); + break; + case CTL_5GHT40: + case CTL_2GHT40: + for (i = 0; i < N(targetPowerHt40.tPow2x); i++) { + targetPowerHt40.tPow2x[i] = (uint8_t)AH_MIN(targetPowerHt40.tPow2x[i], minCtlPower); + } + break; + default: + return AH_FALSE; + break; + } + } /* end ctl mode checking */ + + /* Set rates Array from collected data */ + ratesArray[rate6mb] = ratesArray[rate9mb] = ratesArray[rate12mb] = ratesArray[rate18mb] = ratesArray[rate24mb] = targetPowerOfdm.tPow2x[0]; + ratesArray[rate36mb] = targetPowerOfdm.tPow2x[1]; + ratesArray[rate48mb] = targetPowerOfdm.tPow2x[2]; + ratesArray[rate54mb] = targetPowerOfdm.tPow2x[3]; + ratesArray[rateXr] = targetPowerOfdm.tPow2x[0]; + + for (i = 0; i < N(targetPowerHt20.tPow2x); i++) { + ratesArray[rateHt20_0 + i] = targetPowerHt20.tPow2x[i]; + } + + ratesArray[rate1l] = targetPowerCck.tPow2x[0]; + ratesArray[rate2s] = ratesArray[rate2l] = targetPowerCck.tPow2x[1]; + ratesArray[rate5_5s] = ratesArray[rate5_5l] = targetPowerCck.tPow2x[2]; + ratesArray[rate11s] = ratesArray[rate11l] = targetPowerCck.tPow2x[3]; + if (IEEE80211_IS_CHAN_HT40(chan)) { + for (i = 0; i < N(targetPowerHt40.tPow2x); i++) { + ratesArray[rateHt40_0 + i] = targetPowerHt40.tPow2x[i]; + } + ratesArray[rateDupOfdm] = targetPowerHt40.tPow2x[0]; + ratesArray[rateDupCck] = targetPowerHt40.tPow2x[0]; + ratesArray[rateExtOfdm] = targetPowerOfdmExt.tPow2x[0]; + if (IEEE80211_IS_CHAN_2GHZ(chan)) { + ratesArray[rateExtCck] = targetPowerCckExt.tPow2x[0]; + } + } + return AH_TRUE; +#undef EXT_ADDITIVE +#undef CTL_11G_EXT +#undef CTL_11B_EXT +#undef SUB_NUM_CTL_MODES_AT_2G_40 +#undef N +} + +/************************************************************************** + * fbin2freq + * + * Get channel value from binary representation held in eeprom + * RETURNS: the frequency in MHz + */ +static uint16_t +fbin2freq(uint8_t fbin) +{ + /* + * Reserved value 0xFF provides an empty definition both as + * an fbin and as a frequency - do not convert + */ + if (fbin == AR5416_BCHAN_UNUSED) { + return fbin; + } + + return (uint16_t)(2300 + fbin); +} + +/* + * XXX almost the same as ar5416GetMaxEdgePower. + */ +static uint16_t +ar9285GetMaxEdgePower(uint16_t freq, CAL_CTL_EDGES *pRdEdgesPower) +{ + uint16_t twiceMaxEdgePower = AR5416_MAX_RATE_POWER; + int i; + + /* Get the edge power */ + for (i = 0; (i < AR5416_NUM_BAND_EDGES) && (pRdEdgesPower[i].bChannel != AR5416_BCHAN_UNUSED) ; i++) { + /* + * If there's an exact channel match or an inband flag set + * on the lower channel use the given rdEdgePower + */ + if (freq == fbin2freq(pRdEdgesPower[i].bChannel)) { + twiceMaxEdgePower = MS(pRdEdgesPower[i].tPowerFlag, CAL_CTL_EDGES_POWER); + break; + } else if ((i > 0) && (freq < fbin2freq(pRdEdgesPower[i].bChannel))) { + if (fbin2freq(pRdEdgesPower[i - 1].bChannel) < freq && (pRdEdgesPower[i - 1].tPowerFlag & CAL_CTL_EDGES_FLAG) != 0) { + twiceMaxEdgePower = MS(pRdEdgesPower[i - 1].tPowerFlag, CAL_CTL_EDGES_POWER); + } + /* Leave loop - no more affecting edges possible in this monotonic increasing list */ + break; + } + } + HALASSERT(twiceMaxEdgePower > 0); + return twiceMaxEdgePower; +} + + + +static HAL_BOOL +ar9285SetPowerCalTable(struct ath_hal *ah, struct ar5416eeprom_4k *pEepData, + const struct ieee80211_channel *chan, int16_t *pTxPowerIndexOffset) +{ + CAL_DATA_PER_FREQ_4K *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, j; + int16_t tMinCalPower; + uint16_t numXpdGain, xpdMask; + uint16_t xpdGainValues[AR5416_4K_NUM_PD_GAINS]; + uint32_t reg32, regOffset, regChainOffset; + + OS_MEMZERO(xpdGainValues, sizeof(xpdGainValues)); + + xpdMask = pEepData->modalHeader.xpdGain; + + if (IS_EEP_MINOR_V2(ah)) { + pdGainOverlap_t2 = pEepData->modalHeader.pdGainOverlap; + } else { + pdGainOverlap_t2 = (uint16_t)(MS(OS_REG_READ(ah, AR_PHY_TPCRG5), AR_PHY_TPCRG5_PD_GAIN_OVERLAP)); + } + + pCalBChans = pEepData->calFreqPier2G; + numPiers = AR5416_4K_NUM_2G_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_4K_NUM_PD_GAINS) { + HALASSERT(0); + break; + } + xpdGainValues[numXpdGain] = (uint16_t)(AR5416_PD_GAINS_IN_MASK - i); + numXpdGain++; + } + } + + /* 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)); + + 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; + } + + if (pEepData->baseEepHeader.txMask & (1 << i)) { + pRawDataset = pEepData->calPierData2G[i]; + + ar9285GetGainBoundariesAndPdadcs(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)); + } + + /* 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; + } + } + } + *pTxPowerIndexOffset = 0; + + return AH_TRUE; +} + +static void +ar9285GetGainBoundariesAndPdadcs(struct ath_hal *ah, + const struct ieee80211_channel *chan, + CAL_DATA_PER_FREQ_4K *pRawDataSet, + uint8_t * bChans, uint16_t availPiers, + uint16_t tPdGainOverlap, int16_t *pMinCalPower, uint16_t * pPdGainBoundaries, + uint8_t * pPDADCValues, uint16_t numXpdGains) +{ + + int i, j, k; + int16_t ss; /* potentially -ve index for taking care of pdGainOverlap */ + uint16_t idxL, idxR, numPiers; /* Pier indexes */ + + /* filled out Vpd table for all pdGains (chanL) */ + static uint8_t vpdTableL[AR5416_4K_NUM_PD_GAINS][AR5416_MAX_PWR_RANGE_IN_HALF_DB]; + + /* filled out Vpd table for all pdGains (chanR) */ + static uint8_t vpdTableR[AR5416_4K_NUM_PD_GAINS][AR5416_MAX_PWR_RANGE_IN_HALF_DB]; + + /* filled out Vpd table for all pdGains (interpolated) */ + static uint8_t vpdTableI[AR5416_4K_NUM_PD_GAINS][AR5416_MAX_PWR_RANGE_IN_HALF_DB]; + + uint8_t *pVpdL, *pVpdR, *pPwrL, *pPwrR; + uint8_t minPwrT4[AR5416_4K_NUM_PD_GAINS]; + uint8_t maxPwrT4[AR5416_4K_NUM_PD_GAINS]; + int16_t vpdStep; + int16_t tmpVal; + uint16_t sizeCurrVpdTable, maxIndex, tgtIndex; + HAL_BOOL match; + int16_t minDelta = 0; + CHAN_CENTERS centers; + + ar5416GetChannelCenters(ah, chan, ¢ers); + + /* Trim numPiers for the number of populated channel Piers */ + for (numPiers = 0; numPiers < availPiers; numPiers++) { + if (bChans[numPiers] == AR5416_BCHAN_UNUSED) { + break; + } + } + + /* Find pier indexes around the current channel */ + match = getLowerUpperIndex((uint8_t)FREQ2FBIN(centers.synth_center, IEEE80211_IS_CHAN_2GHZ(chan)), + bChans, numPiers, &idxL, &idxR); + + if (match) { + /* Directly fill both vpd tables from the matching index */ + for (i = 0; i < numXpdGains; i++) { + minPwrT4[i] = pRawDataSet[idxL].pwrPdg[i][0]; + maxPwrT4[i] = pRawDataSet[idxL].pwrPdg[i][4]; + ar9285FillVpdTable(minPwrT4[i], maxPwrT4[i], + pRawDataSet[idxL].pwrPdg[i], + pRawDataSet[idxL].vpdPdg[i], + AR5416_PD_GAIN_ICEPTS, vpdTableI[i]); + } + } else { + for (i = 0; i < numXpdGains; i++) { + pVpdL = pRawDataSet[idxL].vpdPdg[i]; + pPwrL = pRawDataSet[idxL].pwrPdg[i]; + pVpdR = pRawDataSet[idxR].vpdPdg[i]; + pPwrR = pRawDataSet[idxR].pwrPdg[i]; + + /* Start Vpd interpolation from the max of the minimum powers */ + minPwrT4[i] = AH_MAX(pPwrL[0], pPwrR[0]); + + /* End Vpd interpolation from the min of the max powers */ + maxPwrT4[i] = AH_MIN(pPwrL[AR5416_PD_GAIN_ICEPTS - 1], pPwrR[AR5416_PD_GAIN_ICEPTS - 1]); + HALASSERT(maxPwrT4[i] > minPwrT4[i]); + + /* Fill pier Vpds */ + ar9285FillVpdTable(minPwrT4[i], maxPwrT4[i], pPwrL, pVpdL, + AR5416_PD_GAIN_ICEPTS, vpdTableL[i]); + ar9285FillVpdTable(minPwrT4[i], maxPwrT4[i], pPwrR, pVpdR, + AR5416_PD_GAIN_ICEPTS, vpdTableR[i]); + + /* Interpolate the final vpd */ + for (j = 0; j <= (maxPwrT4[i] - minPwrT4[i]) / 2; j++) { + vpdTableI[i][j] = (uint8_t)(interpolate((uint16_t)FREQ2FBIN(centers.synth_center, IEEE80211_IS_CHAN_2GHZ(chan)), + bChans[idxL], bChans[idxR], vpdTableL[i][j], vpdTableR[i][j])); + } + } + } + *pMinCalPower = (int16_t)(minPwrT4[0] / 2); + + k = 0; /* index for the final table */ + for (i = 0; i < numXpdGains; i++) { + if (i == (numXpdGains - 1)) { + pPdGainBoundaries[i] = (uint16_t)(maxPwrT4[i] / 2); + } else { + pPdGainBoundaries[i] = (uint16_t)((maxPwrT4[i] + minPwrT4[i+1]) / 4); + } + + pPdGainBoundaries[i] = (uint16_t)AH_MIN(AR5416_MAX_RATE_POWER, pPdGainBoundaries[i]); + + /* NB: only applies to owl 1.0 */ + if ((i == 0) && !AR_SREV_OWL_20_OR_LATER(ah) ) { + /* + * fix the gain delta, but get a delta that can be applied to min to + * keep the upper power values accurate, don't think max needs to + * be adjusted because should not be at that area of the table? + */ + minDelta = pPdGainBoundaries[0] - 23; + pPdGainBoundaries[0] = 23; + } + else { + minDelta = 0; + } + + /* Find starting index for this pdGain */ + if (i == 0) { + ss = 0; /* for the first pdGain, start from index 0 */ + } else { + /* need overlap entries extrapolated below. */ + ss = (int16_t)((pPdGainBoundaries[i-1] - (minPwrT4[i] / 2)) - tPdGainOverlap + 1 + minDelta); + } + vpdStep = (int16_t)(vpdTableI[i][1] - vpdTableI[i][0]); + vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep); + /* + *-ve ss indicates need to extrapolate data below for this pdGain + */ + while ((ss < 0) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { + tmpVal = (int16_t)(vpdTableI[i][0] + ss * vpdStep); + pPDADCValues[k++] = (uint8_t)((tmpVal < 0) ? 0 : tmpVal); + ss++; + } + + sizeCurrVpdTable = (uint8_t)((maxPwrT4[i] - minPwrT4[i]) / 2 +1); + tgtIndex = (uint8_t)(pPdGainBoundaries[i] + tPdGainOverlap - (minPwrT4[i] / 2)); + maxIndex = (tgtIndex < sizeCurrVpdTable) ? tgtIndex : sizeCurrVpdTable; + + while ((ss < maxIndex) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { + pPDADCValues[k++] = vpdTableI[i][ss++]; + } + + vpdStep = (int16_t)(vpdTableI[i][sizeCurrVpdTable - 1] - vpdTableI[i][sizeCurrVpdTable - 2]); + vpdStep = (int16_t)((vpdStep < 1) ? 1 : vpdStep); + /* + * for last gain, pdGainBoundary == Pmax_t2, so will + * have to extrapolate + */ + if (tgtIndex > maxIndex) { /* need to extrapolate above */ + while ((ss <= tgtIndex) && (k < (AR5416_NUM_PDADC_VALUES - 1))) { + tmpVal = (int16_t)((vpdTableI[i][sizeCurrVpdTable - 1] + + (ss - maxIndex +1) * vpdStep)); + pPDADCValues[k++] = (uint8_t)((tmpVal > 255) ? 255 : tmpVal); + ss++; + } + } /* extrapolated above */ + } /* for all pdGainUsed */ + + /* Fill out pdGainBoundaries - only up to 2 allowed here, but hardware allows up to 4 */ + while (i < AR5416_PD_GAINS_IN_MASK) { + pPdGainBoundaries[i] = pPdGainBoundaries[i-1]; + i++; + } + + while (k < AR5416_NUM_PDADC_VALUES) { + pPDADCValues[k] = pPDADCValues[k-1]; + k++; + } + return; +} +/* + * XXX same as ar5416FillVpdTable + */ +static HAL_BOOL +ar9285FillVpdTable(uint8_t pwrMin, uint8_t pwrMax, uint8_t *pPwrList, + uint8_t *pVpdList, uint16_t numIntercepts, uint8_t *pRetVpdList) +{ + uint16_t i, k; + uint8_t currPwr = pwrMin; + uint16_t idxL, idxR; + + HALASSERT(pwrMax > pwrMin); + for (i = 0; i <= (pwrMax - pwrMin) / 2; i++) { + getLowerUpperIndex(currPwr, pPwrList, numIntercepts, + &(idxL), &(idxR)); + if (idxR < 1) + idxR = 1; /* extrapolate below */ + if (idxL == numIntercepts - 1) + idxL = (uint16_t)(numIntercepts - 2); /* extrapolate above */ + if (pPwrList[idxL] == pPwrList[idxR]) + k = pVpdList[idxL]; + else + k = (uint16_t)( ((currPwr - pPwrList[idxL]) * pVpdList[idxR] + (pPwrList[idxR] - currPwr) * pVpdList[idxL]) / + (pPwrList[idxR] - pPwrList[idxL]) ); + HALASSERT(k < 256); + pRetVpdList[i] = (uint8_t)k; + currPwr += 2; /* half dB steps */ + } + + return AH_TRUE; +} +static int16_t +interpolate(uint16_t target, uint16_t srcLeft, uint16_t srcRight, + int16_t targetLeft, int16_t targetRight) +{ + int16_t rv; + + if (srcRight == srcLeft) { + rv = targetLeft; + } else { + rv = (int16_t)( ((target - srcLeft) * targetRight + + (srcRight - target) * targetLeft) / (srcRight - srcLeft) ); + } + return rv; +} + +HAL_BOOL +getLowerUpperIndex(uint8_t target, uint8_t *pList, uint16_t listSize, + uint16_t *indexL, uint16_t *indexR) +{ + uint16_t i; + + /* + * Check first and last elements for beyond ordered array cases. + */ + if (target <= pList[0]) { + *indexL = *indexR = 0; + return AH_TRUE; + } + if (target >= pList[listSize-1]) { + *indexL = *indexR = (uint16_t)(listSize - 1); + return AH_TRUE; + } + + /* look for value being near or between 2 values in list */ + for (i = 0; i < listSize - 1; i++) { + /* + * If value is close to the current value of the list + * then target is not between values, it is one of the values + */ + if (pList[i] == target) { + *indexL = *indexR = i; + return AH_TRUE; + } + /* + * Look for value being between current value and next value + * if so return these 2 values + */ + if (target < pList[i + 1]) { + *indexL = i; + *indexR = (uint16_t)(i + 1); + return AH_FALSE; + } + } + HALASSERT(0); + *indexL = *indexR = 0; + return AH_FALSE; +}