bc391cb2ce
that generates a fatal bus trap. Normally, the chips are setup to do 128 byte DMA bursts, but when on this CPU, they can only safely due 4-byte DMA bursts due to this bug. Details of the exact nature of the bug are sketchy, but some can be found at https://forum.openwrt.org/viewtopic.php?pid=70060 on pages 4, 5 and 6. There's a small performance penalty associated with this workaround, so it is only enabled when needed on the Atheros AR71xx platforms. Unfortunately, this condition is impossible to detect at runtime without MIPS specific ifdefs. Rather than cast an overly-broad net like Linux/OpenWRT dues (which enables this workaround all the time on MIPS32 platforms), we put this option in the kernel for just the affected machines. Sam didn't like this aspect of the patch when he reviewed it, and I'd love to hear sane proposals on how to fix it :) Reviewed by: sam@
2645 lines
80 KiB
C
2645 lines
80 KiB
C
/*
|
|
* 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$
|
|
*/
|
|
#include "opt_ah.h"
|
|
|
|
#include "ah.h"
|
|
#include "ah_internal.h"
|
|
#include "ah_devid.h"
|
|
|
|
#include "ar5212/ar5212.h"
|
|
#include "ar5212/ar5212reg.h"
|
|
#include "ar5212/ar5212phy.h"
|
|
|
|
#include "ah_eeprom_v3.h"
|
|
|
|
/* Additional Time delay to wait after activiting the Base band */
|
|
#define BASE_ACTIVATE_DELAY 100 /* 100 usec */
|
|
#define PLL_SETTLE_DELAY 300 /* 300 usec */
|
|
|
|
static HAL_BOOL ar5212SetResetReg(struct ath_hal *, uint32_t resetMask);
|
|
/* NB: public for 5312 use */
|
|
HAL_BOOL ar5212IsSpurChannel(struct ath_hal *,
|
|
const struct ieee80211_channel *);
|
|
HAL_BOOL ar5212ChannelChange(struct ath_hal *,
|
|
const struct ieee80211_channel *);
|
|
int16_t ar5212GetNf(struct ath_hal *, struct ieee80211_channel *);
|
|
HAL_BOOL ar5212SetBoardValues(struct ath_hal *,
|
|
const struct ieee80211_channel *);
|
|
void ar5212SetDeltaSlope(struct ath_hal *,
|
|
const struct ieee80211_channel *);
|
|
HAL_BOOL ar5212SetTransmitPower(struct ath_hal *ah,
|
|
const struct ieee80211_channel *chan, uint16_t *rfXpdGain);
|
|
static HAL_BOOL ar5212SetRateTable(struct ath_hal *,
|
|
const struct ieee80211_channel *, int16_t tpcScaleReduction,
|
|
int16_t powerLimit,
|
|
HAL_BOOL commit, int16_t *minPower, int16_t *maxPower);
|
|
static void ar5212CorrectGainDelta(struct ath_hal *, int twiceOfdmCckDelta);
|
|
static void ar5212GetTargetPowers(struct ath_hal *,
|
|
const struct ieee80211_channel *,
|
|
const TRGT_POWER_INFO *pPowerInfo, uint16_t numChannels,
|
|
TRGT_POWER_INFO *pNewPower);
|
|
static uint16_t ar5212GetMaxEdgePower(uint16_t channel,
|
|
const RD_EDGES_POWER *pRdEdgesPower);
|
|
void ar5212SetRateDurationTable(struct ath_hal *,
|
|
const struct ieee80211_channel *);
|
|
void ar5212SetIFSTiming(struct ath_hal *,
|
|
const struct ieee80211_channel *);
|
|
|
|
/* NB: public for RF backend use */
|
|
void ar5212GetLowerUpperValues(uint16_t value,
|
|
uint16_t *pList, uint16_t listSize,
|
|
uint16_t *pLowerValue, uint16_t *pUpperValue);
|
|
void ar5212ModifyRfBuffer(uint32_t *rfBuf, uint32_t reg32,
|
|
uint32_t numBits, uint32_t firstBit, uint32_t column);
|
|
|
|
static int
|
|
write_common(struct ath_hal *ah, const HAL_INI_ARRAY *ia,
|
|
HAL_BOOL bChannelChange, int writes)
|
|
{
|
|
#define IS_NO_RESET_TIMER_ADDR(x) \
|
|
( (((x) >= AR_BEACON) && ((x) <= AR_CFP_DUR)) || \
|
|
(((x) >= AR_SLEEP1) && ((x) <= AR_SLEEP3)))
|
|
#define V(r, c) (ia)->data[((r)*(ia)->cols) + (c)]
|
|
int r;
|
|
|
|
/* Write Common Array Parameters */
|
|
for (r = 0; r < ia->rows; r++) {
|
|
uint32_t reg = V(r, 0);
|
|
/* XXX timer/beacon setup registers? */
|
|
/* On channel change, don't reset the PCU registers */
|
|
if (!(bChannelChange && IS_NO_RESET_TIMER_ADDR(reg))) {
|
|
OS_REG_WRITE(ah, reg, V(r, 1));
|
|
DMA_YIELD(writes);
|
|
}
|
|
}
|
|
return writes;
|
|
#undef IS_NO_RESET_TIMER_ADDR
|
|
#undef V
|
|
}
|
|
|
|
#define IS_DISABLE_FAST_ADC_CHAN(x) (((x) == 2462) || ((x) == 2467))
|
|
|
|
/*
|
|
* Places the device in and out of reset and then places sane
|
|
* values in the registers based on EEPROM config, initialization
|
|
* vectors (as determined by the mode), and station configuration
|
|
*
|
|
* bChannelChange is used to preserve DMA/PCU registers across
|
|
* a HW Reset during channel change.
|
|
*/
|
|
HAL_BOOL
|
|
ar5212Reset(struct ath_hal *ah, HAL_OPMODE opmode,
|
|
struct ieee80211_channel *chan,
|
|
HAL_BOOL bChannelChange, HAL_STATUS *status)
|
|
{
|
|
#define N(a) (sizeof (a) / sizeof (a[0]))
|
|
#define FAIL(_code) do { ecode = _code; goto bad; } while (0)
|
|
struct ath_hal_5212 *ahp = AH5212(ah);
|
|
HAL_CHANNEL_INTERNAL *ichan = AH_NULL;
|
|
const HAL_EEPROM *ee;
|
|
uint32_t softLedCfg, softLedState;
|
|
uint32_t saveFrameSeqCount, saveDefAntenna, saveLedState;
|
|
uint32_t macStaId1, synthDelay, txFrm2TxDStart;
|
|
uint16_t rfXpdGain[MAX_NUM_PDGAINS_PER_CHANNEL];
|
|
int16_t cckOfdmPwrDelta = 0;
|
|
u_int modesIndex, freqIndex;
|
|
HAL_STATUS ecode;
|
|
int i, regWrites;
|
|
uint32_t testReg, powerVal;
|
|
int8_t twiceAntennaGain, twiceAntennaReduction;
|
|
uint32_t ackTpcPow, ctsTpcPow, chirpTpcPow;
|
|
HAL_BOOL isBmode = AH_FALSE;
|
|
|
|
HALASSERT(ah->ah_magic == AR5212_MAGIC);
|
|
ee = AH_PRIVATE(ah)->ah_eeprom;
|
|
|
|
OS_MARK(ah, AH_MARK_RESET, bChannelChange);
|
|
|
|
/* Bring out of sleep mode */
|
|
if (!ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) {
|
|
HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip did not wakeup\n",
|
|
__func__);
|
|
FAIL(HAL_EIO);
|
|
}
|
|
|
|
/*
|
|
* Map public channel to private.
|
|
*/
|
|
ichan = ath_hal_checkchannel(ah, chan);
|
|
if (ichan == AH_NULL)
|
|
FAIL(HAL_EINVAL);
|
|
switch (opmode) {
|
|
case HAL_M_STA:
|
|
case HAL_M_IBSS:
|
|
case HAL_M_HOSTAP:
|
|
case HAL_M_MONITOR:
|
|
break;
|
|
default:
|
|
HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid operating mode %u\n",
|
|
__func__, opmode);
|
|
FAIL(HAL_EINVAL);
|
|
break;
|
|
}
|
|
HALASSERT(AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER3);
|
|
|
|
SAVE_CCK(ah, chan, isBmode);
|
|
|
|
/* Preserve certain DMA hardware registers on a channel change */
|
|
if (bChannelChange) {
|
|
/*
|
|
* On Venice, the TSF is almost preserved across a reset;
|
|
* it requires doubling writes to the RESET_TSF
|
|
* bit in the AR_BEACON register; it also has the quirk
|
|
* of the TSF going back in time on the station (station
|
|
* latches onto the last beacon's tsf during a reset 50%
|
|
* of the times); the latter is not a problem for adhoc
|
|
* stations since as long as the TSF is behind, it will
|
|
* get resynchronized on receiving the next beacon; the
|
|
* TSF going backwards in time could be a problem for the
|
|
* sleep operation (supported on infrastructure stations
|
|
* only) - the best and most general fix for this situation
|
|
* is to resynchronize the various sleep/beacon timers on
|
|
* the receipt of the next beacon i.e. when the TSF itself
|
|
* gets resynchronized to the AP's TSF - power save is
|
|
* needed to be temporarily disabled until that time
|
|
*
|
|
* Need to save the sequence number to restore it after
|
|
* the reset!
|
|
*/
|
|
saveFrameSeqCount = OS_REG_READ(ah, AR_D_SEQNUM);
|
|
} else
|
|
saveFrameSeqCount = 0; /* NB: silence compiler */
|
|
#if 0
|
|
/*
|
|
* XXX disable for now; this appears to sometimes cause OFDM
|
|
* XXX timing error floods when ani is enabled and bg scanning
|
|
* XXX kicks in
|
|
*/
|
|
/* If the channel change is across the same mode - perform a fast channel change */
|
|
if (IS_2413(ah) || IS_5413(ah)) {
|
|
/*
|
|
* Fast channel change can only be used when:
|
|
* -channel change requested - so it's not the initial reset.
|
|
* -it's not a change to the current channel -
|
|
* often called when switching modes on a channel
|
|
* -the modes of the previous and requested channel are the
|
|
* same
|
|
* XXX opmode shouldn't change either?
|
|
*/
|
|
if (bChannelChange &&
|
|
(AH_PRIVATE(ah)->ah_curchan != AH_NULL) &&
|
|
(chan->ic_freq != AH_PRIVATE(ah)->ah_curchan->ic_freq) &&
|
|
((chan->ic_flags & IEEE80211_CHAN_ALLTURBO) ==
|
|
(AH_PRIVATE(ah)->ah_curchan->ic_flags & IEEE80211_CHAN_ALLTURBO))) {
|
|
if (ar5212ChannelChange(ah, chan)) {
|
|
/* If ChannelChange completed - skip the rest of reset */
|
|
/* XXX ani? */
|
|
goto done;
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
/*
|
|
* Preserve the antenna on a channel change
|
|
*/
|
|
saveDefAntenna = OS_REG_READ(ah, AR_DEF_ANTENNA);
|
|
if (saveDefAntenna == 0) /* XXX magic constants */
|
|
saveDefAntenna = 1;
|
|
|
|
/* Save hardware flag before chip reset clears the register */
|
|
macStaId1 = OS_REG_READ(ah, AR_STA_ID1) &
|
|
(AR_STA_ID1_BASE_RATE_11B | AR_STA_ID1_USE_DEFANT);
|
|
|
|
/* Save led state from pci config register */
|
|
saveLedState = OS_REG_READ(ah, AR_PCICFG) &
|
|
(AR_PCICFG_LEDCTL | AR_PCICFG_LEDMODE | AR_PCICFG_LEDBLINK |
|
|
AR_PCICFG_LEDSLOW);
|
|
softLedCfg = OS_REG_READ(ah, AR_GPIOCR);
|
|
softLedState = OS_REG_READ(ah, AR_GPIODO);
|
|
|
|
ar5212RestoreClock(ah, opmode); /* move to refclk operation */
|
|
|
|
/*
|
|
* Adjust gain parameters before reset if
|
|
* there's an outstanding gain updated.
|
|
*/
|
|
(void) ar5212GetRfgain(ah);
|
|
|
|
if (!ar5212ChipReset(ah, chan)) {
|
|
HALDEBUG(ah, HAL_DEBUG_ANY, "%s: chip reset failed\n", __func__);
|
|
FAIL(HAL_EIO);
|
|
}
|
|
|
|
/* Setup the indices for the next set of register array writes */
|
|
if (IEEE80211_IS_CHAN_2GHZ(chan)) {
|
|
freqIndex = 2;
|
|
if (IEEE80211_IS_CHAN_108G(chan))
|
|
modesIndex = 5;
|
|
else if (IEEE80211_IS_CHAN_G(chan))
|
|
modesIndex = 4;
|
|
else if (IEEE80211_IS_CHAN_B(chan))
|
|
modesIndex = 3;
|
|
else {
|
|
HALDEBUG(ah, HAL_DEBUG_ANY,
|
|
"%s: invalid channel %u/0x%x\n",
|
|
__func__, chan->ic_freq, chan->ic_flags);
|
|
FAIL(HAL_EINVAL);
|
|
}
|
|
} else {
|
|
freqIndex = 1;
|
|
if (IEEE80211_IS_CHAN_TURBO(chan))
|
|
modesIndex = 2;
|
|
else if (IEEE80211_IS_CHAN_A(chan))
|
|
modesIndex = 1;
|
|
else {
|
|
HALDEBUG(ah, HAL_DEBUG_ANY,
|
|
"%s: invalid channel %u/0x%x\n",
|
|
__func__, chan->ic_freq, chan->ic_flags);
|
|
FAIL(HAL_EINVAL);
|
|
}
|
|
}
|
|
|
|
OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__);
|
|
|
|
/* Set correct Baseband to analog shift setting to access analog chips. */
|
|
OS_REG_WRITE(ah, AR_PHY(0), 0x00000007);
|
|
|
|
regWrites = ath_hal_ini_write(ah, &ahp->ah_ini_modes, modesIndex, 0);
|
|
regWrites = write_common(ah, &ahp->ah_ini_common, bChannelChange,
|
|
regWrites);
|
|
#ifdef AH_RXCFG_SDMAMW_4BYTES
|
|
/*
|
|
* Nala doesn't work with 128 byte bursts on pb42(hydra) (ar71xx),
|
|
* use 4 instead. Enabling it on all platforms would hurt performance,
|
|
* so we only enable it on the ones that are affected by it.
|
|
*/
|
|
OS_REG_WRITE(ah, AR_RXCFG, 0);
|
|
#endif
|
|
ahp->ah_rfHal->writeRegs(ah, modesIndex, freqIndex, regWrites);
|
|
|
|
OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__);
|
|
|
|
if (IEEE80211_IS_CHAN_HALF(chan) || IEEE80211_IS_CHAN_QUARTER(chan)) {
|
|
ar5212SetIFSTiming(ah, chan);
|
|
if (IS_5413(ah)) {
|
|
/*
|
|
* Force window_length for 1/2 and 1/4 rate channels,
|
|
* the ini file sets this to zero otherwise.
|
|
*/
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL,
|
|
AR_PHY_FRAME_CTL_WINLEN, 3);
|
|
}
|
|
}
|
|
|
|
/* Overwrite INI values for revised chipsets */
|
|
if (AH_PRIVATE(ah)->ah_phyRev >= AR_PHY_CHIP_ID_REV_2) {
|
|
/* ADC_CTL */
|
|
OS_REG_WRITE(ah, AR_PHY_ADC_CTL,
|
|
SM(2, AR_PHY_ADC_CTL_OFF_INBUFGAIN) |
|
|
SM(2, AR_PHY_ADC_CTL_ON_INBUFGAIN) |
|
|
AR_PHY_ADC_CTL_OFF_PWDDAC |
|
|
AR_PHY_ADC_CTL_OFF_PWDADC);
|
|
|
|
/* TX_PWR_ADJ */
|
|
if (ichan->channel == 2484) {
|
|
cckOfdmPwrDelta = SCALE_OC_DELTA(
|
|
ee->ee_cckOfdmPwrDelta -
|
|
ee->ee_scaledCh14FilterCckDelta);
|
|
} else {
|
|
cckOfdmPwrDelta = SCALE_OC_DELTA(
|
|
ee->ee_cckOfdmPwrDelta);
|
|
}
|
|
|
|
if (IEEE80211_IS_CHAN_G(chan)) {
|
|
OS_REG_WRITE(ah, AR_PHY_TXPWRADJ,
|
|
SM((ee->ee_cckOfdmPwrDelta*-1),
|
|
AR_PHY_TXPWRADJ_CCK_GAIN_DELTA) |
|
|
SM((cckOfdmPwrDelta*-1),
|
|
AR_PHY_TXPWRADJ_CCK_PCDAC_INDEX));
|
|
} else {
|
|
OS_REG_WRITE(ah, AR_PHY_TXPWRADJ, 0);
|
|
}
|
|
|
|
/* Add barker RSSI thresh enable as disabled */
|
|
OS_REG_CLR_BIT(ah, AR_PHY_DAG_CTRLCCK,
|
|
AR_PHY_DAG_CTRLCCK_EN_RSSI_THR);
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_DAG_CTRLCCK,
|
|
AR_PHY_DAG_CTRLCCK_RSSI_THR, 2);
|
|
|
|
/* Set the mute mask to the correct default */
|
|
OS_REG_WRITE(ah, AR_SEQ_MASK, 0x0000000F);
|
|
}
|
|
|
|
if (AH_PRIVATE(ah)->ah_phyRev >= AR_PHY_CHIP_ID_REV_3) {
|
|
/* Clear reg to alllow RX_CLEAR line debug */
|
|
OS_REG_WRITE(ah, AR_PHY_BLUETOOTH, 0);
|
|
}
|
|
if (AH_PRIVATE(ah)->ah_phyRev >= AR_PHY_CHIP_ID_REV_4) {
|
|
#ifdef notyet
|
|
/* Enable burst prefetch for the data queues */
|
|
OS_REG_RMW_FIELD(ah, AR_D_FPCTL, ... );
|
|
/* Enable double-buffering */
|
|
OS_REG_CLR_BIT(ah, AR_TXCFG, AR_TXCFG_DBL_BUF_DIS);
|
|
#endif
|
|
}
|
|
|
|
/* Set ADC/DAC select values */
|
|
OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL, 0x0e);
|
|
|
|
if (IS_5413(ah) || IS_2417(ah)) {
|
|
uint32_t newReg = 1;
|
|
if (IS_DISABLE_FAST_ADC_CHAN(ichan->channel))
|
|
newReg = 0;
|
|
/* As it's a clock changing register, only write when the value needs to be changed */
|
|
if (OS_REG_READ(ah, AR_PHY_FAST_ADC) != newReg)
|
|
OS_REG_WRITE(ah, AR_PHY_FAST_ADC, newReg);
|
|
}
|
|
|
|
/* Setup the transmit power values. */
|
|
if (!ar5212SetTransmitPower(ah, chan, rfXpdGain)) {
|
|
HALDEBUG(ah, HAL_DEBUG_ANY,
|
|
"%s: error init'ing transmit power\n", __func__);
|
|
FAIL(HAL_EIO);
|
|
}
|
|
|
|
/* Write the analog registers */
|
|
if (!ahp->ah_rfHal->setRfRegs(ah, chan, modesIndex, rfXpdGain)) {
|
|
HALDEBUG(ah, HAL_DEBUG_ANY, "%s: ar5212SetRfRegs failed\n",
|
|
__func__);
|
|
FAIL(HAL_EIO);
|
|
}
|
|
|
|
/* Write delta slope for OFDM enabled modes (A, G, Turbo) */
|
|
if (IEEE80211_IS_CHAN_OFDM(chan)) {
|
|
if (IS_5413(ah) ||
|
|
AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER5_3)
|
|
ar5212SetSpurMitigation(ah, chan);
|
|
ar5212SetDeltaSlope(ah, chan);
|
|
}
|
|
|
|
/* Setup board specific options for EEPROM version 3 */
|
|
if (!ar5212SetBoardValues(ah, chan)) {
|
|
HALDEBUG(ah, HAL_DEBUG_ANY,
|
|
"%s: error setting board options\n", __func__);
|
|
FAIL(HAL_EIO);
|
|
}
|
|
|
|
/* Restore certain DMA hardware registers on a channel change */
|
|
if (bChannelChange)
|
|
OS_REG_WRITE(ah, AR_D_SEQNUM, saveFrameSeqCount);
|
|
|
|
OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__);
|
|
|
|
OS_REG_WRITE(ah, AR_STA_ID0, LE_READ_4(ahp->ah_macaddr));
|
|
OS_REG_WRITE(ah, AR_STA_ID1, LE_READ_2(ahp->ah_macaddr + 4)
|
|
| macStaId1
|
|
| AR_STA_ID1_RTS_USE_DEF
|
|
| ahp->ah_staId1Defaults
|
|
);
|
|
ar5212SetOperatingMode(ah, opmode);
|
|
|
|
/* Set Venice BSSID mask according to current state */
|
|
OS_REG_WRITE(ah, AR_BSSMSKL, LE_READ_4(ahp->ah_bssidmask));
|
|
OS_REG_WRITE(ah, AR_BSSMSKU, LE_READ_2(ahp->ah_bssidmask + 4));
|
|
|
|
/* Restore previous led state */
|
|
OS_REG_WRITE(ah, AR_PCICFG, OS_REG_READ(ah, AR_PCICFG) | saveLedState);
|
|
|
|
/* Restore soft Led state to GPIO */
|
|
OS_REG_WRITE(ah, AR_GPIOCR, softLedCfg);
|
|
OS_REG_WRITE(ah, AR_GPIODO, softLedState);
|
|
|
|
/* Restore previous antenna */
|
|
OS_REG_WRITE(ah, AR_DEF_ANTENNA, saveDefAntenna);
|
|
|
|
/* then our BSSID */
|
|
OS_REG_WRITE(ah, AR_BSS_ID0, LE_READ_4(ahp->ah_bssid));
|
|
OS_REG_WRITE(ah, AR_BSS_ID1, LE_READ_2(ahp->ah_bssid + 4));
|
|
|
|
/* Restore bmiss rssi & count thresholds */
|
|
OS_REG_WRITE(ah, AR_RSSI_THR, ahp->ah_rssiThr);
|
|
|
|
OS_REG_WRITE(ah, AR_ISR, ~0); /* cleared on write */
|
|
|
|
if (!ar5212SetChannel(ah, chan))
|
|
FAIL(HAL_EIO);
|
|
|
|
OS_MARK(ah, AH_MARK_RESET_LINE, __LINE__);
|
|
|
|
ar5212SetCoverageClass(ah, AH_PRIVATE(ah)->ah_coverageClass, 1);
|
|
|
|
ar5212SetRateDurationTable(ah, chan);
|
|
|
|
/* Set Tx frame start to tx data start delay */
|
|
if (IS_RAD5112_ANY(ah) &&
|
|
(IEEE80211_IS_CHAN_HALF(chan) || IEEE80211_IS_CHAN_QUARTER(chan))) {
|
|
txFrm2TxDStart =
|
|
IEEE80211_IS_CHAN_HALF(chan) ?
|
|
TX_FRAME_D_START_HALF_RATE:
|
|
TX_FRAME_D_START_QUARTER_RATE;
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_TX_CTL,
|
|
AR_PHY_TX_FRAME_TO_TX_DATA_START, txFrm2TxDStart);
|
|
}
|
|
|
|
/*
|
|
* Setup fast diversity.
|
|
* Fast diversity can be enabled or disabled via regadd.txt.
|
|
* Default is enabled.
|
|
* For reference,
|
|
* Disable: reg val
|
|
* 0x00009860 0x00009d18 (if 11a / 11g, else no change)
|
|
* 0x00009970 0x192bb514
|
|
* 0x0000a208 0xd03e4648
|
|
*
|
|
* Enable: 0x00009860 0x00009d10 (if 11a / 11g, else no change)
|
|
* 0x00009970 0x192fb514
|
|
* 0x0000a208 0xd03e6788
|
|
*/
|
|
|
|
/* XXX Setup pre PHY ENABLE EAR additions */
|
|
/*
|
|
* Wait for the frequency synth to settle (synth goes on
|
|
* via AR_PHY_ACTIVE_EN). Read the phy active delay register.
|
|
* Value is in 100ns increments.
|
|
*/
|
|
synthDelay = OS_REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY;
|
|
if (IEEE80211_IS_CHAN_B(chan)) {
|
|
synthDelay = (4 * synthDelay) / 22;
|
|
} else {
|
|
synthDelay /= 10;
|
|
}
|
|
|
|
/* Activate the PHY (includes baseband activate and synthesizer on) */
|
|
OS_REG_WRITE(ah, AR_PHY_ACTIVE, AR_PHY_ACTIVE_EN);
|
|
|
|
/*
|
|
* There is an issue if the AP starts the calibration before
|
|
* the base band timeout completes. This could result in the
|
|
* rx_clear false triggering. As a workaround we add delay an
|
|
* extra BASE_ACTIVATE_DELAY usecs to ensure this condition
|
|
* does not happen.
|
|
*/
|
|
if (IEEE80211_IS_CHAN_HALF(chan)) {
|
|
OS_DELAY((synthDelay << 1) + BASE_ACTIVATE_DELAY);
|
|
} else if (IEEE80211_IS_CHAN_QUARTER(chan)) {
|
|
OS_DELAY((synthDelay << 2) + BASE_ACTIVATE_DELAY);
|
|
} else {
|
|
OS_DELAY(synthDelay + BASE_ACTIVATE_DELAY);
|
|
}
|
|
|
|
/*
|
|
* The udelay method is not reliable with notebooks.
|
|
* Need to check to see if the baseband is ready
|
|
*/
|
|
testReg = OS_REG_READ(ah, AR_PHY_TESTCTRL);
|
|
/* Selects the Tx hold */
|
|
OS_REG_WRITE(ah, AR_PHY_TESTCTRL, AR_PHY_TESTCTRL_TXHOLD);
|
|
i = 0;
|
|
while ((i++ < 20) &&
|
|
(OS_REG_READ(ah, 0x9c24) & 0x10)) /* test if baseband not ready */ OS_DELAY(200);
|
|
OS_REG_WRITE(ah, AR_PHY_TESTCTRL, testReg);
|
|
|
|
/* Calibrate the AGC and start a NF calculation */
|
|
OS_REG_WRITE(ah, AR_PHY_AGC_CONTROL,
|
|
OS_REG_READ(ah, AR_PHY_AGC_CONTROL)
|
|
| AR_PHY_AGC_CONTROL_CAL
|
|
| AR_PHY_AGC_CONTROL_NF);
|
|
|
|
if (!IEEE80211_IS_CHAN_B(chan) && ahp->ah_bIQCalibration != IQ_CAL_DONE) {
|
|
/* Start IQ calibration w/ 2^(INIT_IQCAL_LOG_COUNT_MAX+1) samples */
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4,
|
|
AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX,
|
|
INIT_IQCAL_LOG_COUNT_MAX);
|
|
OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4,
|
|
AR_PHY_TIMING_CTRL4_DO_IQCAL);
|
|
ahp->ah_bIQCalibration = IQ_CAL_RUNNING;
|
|
} else
|
|
ahp->ah_bIQCalibration = IQ_CAL_INACTIVE;
|
|
|
|
/* Setup compression registers */
|
|
ar5212SetCompRegs(ah);
|
|
|
|
/* Set 1:1 QCU to DCU mapping for all queues */
|
|
for (i = 0; i < AR_NUM_DCU; i++)
|
|
OS_REG_WRITE(ah, AR_DQCUMASK(i), 1 << i);
|
|
|
|
ahp->ah_intrTxqs = 0;
|
|
for (i = 0; i < AH_PRIVATE(ah)->ah_caps.halTotalQueues; i++)
|
|
ar5212ResetTxQueue(ah, i);
|
|
|
|
/*
|
|
* Setup interrupt handling. Note that ar5212ResetTxQueue
|
|
* manipulates the secondary IMR's as queues are enabled
|
|
* and disabled. This is done with RMW ops to insure the
|
|
* settings we make here are preserved.
|
|
*/
|
|
ahp->ah_maskReg = AR_IMR_TXOK | AR_IMR_TXERR | AR_IMR_TXURN
|
|
| AR_IMR_RXOK | AR_IMR_RXERR | AR_IMR_RXORN
|
|
| AR_IMR_HIUERR
|
|
;
|
|
if (opmode == HAL_M_HOSTAP)
|
|
ahp->ah_maskReg |= AR_IMR_MIB;
|
|
OS_REG_WRITE(ah, AR_IMR, ahp->ah_maskReg);
|
|
/* Enable bus errors that are OR'd to set the HIUERR bit */
|
|
OS_REG_WRITE(ah, AR_IMR_S2,
|
|
OS_REG_READ(ah, AR_IMR_S2)
|
|
| AR_IMR_S2_MCABT | AR_IMR_S2_SSERR | AR_IMR_S2_DPERR);
|
|
|
|
if (AH_PRIVATE(ah)->ah_rfkillEnabled)
|
|
ar5212EnableRfKill(ah);
|
|
|
|
if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_CAL, 0)) {
|
|
HALDEBUG(ah, HAL_DEBUG_ANY,
|
|
"%s: offset calibration failed to complete in 1ms;"
|
|
" noisy environment?\n", __func__);
|
|
}
|
|
|
|
/*
|
|
* Set clocks back to 32kHz if they had been using refClk, then
|
|
* use an external 32kHz crystal when sleeping, if one exists.
|
|
*/
|
|
ar5212SetupClock(ah, opmode);
|
|
|
|
/*
|
|
* Writing to AR_BEACON will start timers. Hence it should
|
|
* be the last register to be written. Do not reset tsf, do
|
|
* not enable beacons at this point, but preserve other values
|
|
* like beaconInterval.
|
|
*/
|
|
OS_REG_WRITE(ah, AR_BEACON,
|
|
(OS_REG_READ(ah, AR_BEACON) &~ (AR_BEACON_EN | AR_BEACON_RESET_TSF)));
|
|
|
|
/* XXX Setup post reset EAR additions */
|
|
|
|
/* QoS support */
|
|
if (AH_PRIVATE(ah)->ah_macVersion > AR_SREV_VERSION_VENICE ||
|
|
(AH_PRIVATE(ah)->ah_macVersion == AR_SREV_VERSION_VENICE &&
|
|
AH_PRIVATE(ah)->ah_macRev >= AR_SREV_GRIFFIN_LITE)) {
|
|
OS_REG_WRITE(ah, AR_QOS_CONTROL, 0x100aa); /* XXX magic */
|
|
OS_REG_WRITE(ah, AR_QOS_SELECT, 0x3210); /* XXX magic */
|
|
}
|
|
|
|
/* Turn on NOACK Support for QoS packets */
|
|
OS_REG_WRITE(ah, AR_NOACK,
|
|
SM(2, AR_NOACK_2BIT_VALUE) |
|
|
SM(5, AR_NOACK_BIT_OFFSET) |
|
|
SM(0, AR_NOACK_BYTE_OFFSET));
|
|
|
|
/* Get Antenna Gain reduction */
|
|
if (IEEE80211_IS_CHAN_5GHZ(chan)) {
|
|
ath_hal_eepromGet(ah, AR_EEP_ANTGAINMAX_5, &twiceAntennaGain);
|
|
} else {
|
|
ath_hal_eepromGet(ah, AR_EEP_ANTGAINMAX_2, &twiceAntennaGain);
|
|
}
|
|
twiceAntennaReduction =
|
|
ath_hal_getantennareduction(ah, chan, twiceAntennaGain);
|
|
|
|
/* TPC for self-generated frames */
|
|
|
|
ackTpcPow = MS(ahp->ah_macTPC, AR_TPC_ACK);
|
|
if ((ackTpcPow-ahp->ah_txPowerIndexOffset) > chan->ic_maxpower)
|
|
ackTpcPow = chan->ic_maxpower+ahp->ah_txPowerIndexOffset;
|
|
|
|
if (ackTpcPow > (2*chan->ic_maxregpower - twiceAntennaReduction))
|
|
ackTpcPow = (2*chan->ic_maxregpower - twiceAntennaReduction)
|
|
+ ahp->ah_txPowerIndexOffset;
|
|
|
|
ctsTpcPow = MS(ahp->ah_macTPC, AR_TPC_CTS);
|
|
if ((ctsTpcPow-ahp->ah_txPowerIndexOffset) > chan->ic_maxpower)
|
|
ctsTpcPow = chan->ic_maxpower+ahp->ah_txPowerIndexOffset;
|
|
|
|
if (ctsTpcPow > (2*chan->ic_maxregpower - twiceAntennaReduction))
|
|
ctsTpcPow = (2*chan->ic_maxregpower - twiceAntennaReduction)
|
|
+ ahp->ah_txPowerIndexOffset;
|
|
|
|
chirpTpcPow = MS(ahp->ah_macTPC, AR_TPC_CHIRP);
|
|
if ((chirpTpcPow-ahp->ah_txPowerIndexOffset) > chan->ic_maxpower)
|
|
chirpTpcPow = chan->ic_maxpower+ahp->ah_txPowerIndexOffset;
|
|
|
|
if (chirpTpcPow > (2*chan->ic_maxregpower - twiceAntennaReduction))
|
|
chirpTpcPow = (2*chan->ic_maxregpower - twiceAntennaReduction)
|
|
+ ahp->ah_txPowerIndexOffset;
|
|
|
|
if (ackTpcPow > 63)
|
|
ackTpcPow = 63;
|
|
if (ctsTpcPow > 63)
|
|
ctsTpcPow = 63;
|
|
if (chirpTpcPow > 63)
|
|
chirpTpcPow = 63;
|
|
|
|
powerVal = SM(ackTpcPow, AR_TPC_ACK) |
|
|
SM(ctsTpcPow, AR_TPC_CTS) |
|
|
SM(chirpTpcPow, AR_TPC_CHIRP);
|
|
|
|
OS_REG_WRITE(ah, AR_TPC, powerVal);
|
|
|
|
/* Restore user-specified settings */
|
|
if (ahp->ah_miscMode != 0)
|
|
OS_REG_WRITE(ah, AR_MISC_MODE, ahp->ah_miscMode);
|
|
if (ahp->ah_sifstime != (u_int) -1)
|
|
ar5212SetSifsTime(ah, ahp->ah_sifstime);
|
|
if (ahp->ah_slottime != (u_int) -1)
|
|
ar5212SetSlotTime(ah, ahp->ah_slottime);
|
|
if (ahp->ah_acktimeout != (u_int) -1)
|
|
ar5212SetAckTimeout(ah, ahp->ah_acktimeout);
|
|
if (ahp->ah_ctstimeout != (u_int) -1)
|
|
ar5212SetCTSTimeout(ah, ahp->ah_ctstimeout);
|
|
if (AH_PRIVATE(ah)->ah_diagreg != 0)
|
|
OS_REG_WRITE(ah, AR_DIAG_SW, AH_PRIVATE(ah)->ah_diagreg);
|
|
|
|
AH_PRIVATE(ah)->ah_opmode = opmode; /* record operating mode */
|
|
#if 0
|
|
done:
|
|
#endif
|
|
if (bChannelChange && !IEEE80211_IS_CHAN_DFS(chan))
|
|
chan->ic_state &= ~IEEE80211_CHANSTATE_CWINT;
|
|
|
|
HALDEBUG(ah, HAL_DEBUG_RESET, "%s: done\n", __func__);
|
|
|
|
RESTORE_CCK(ah, chan, isBmode);
|
|
|
|
OS_MARK(ah, AH_MARK_RESET_DONE, 0);
|
|
|
|
return AH_TRUE;
|
|
bad:
|
|
RESTORE_CCK(ah, chan, isBmode);
|
|
|
|
OS_MARK(ah, AH_MARK_RESET_DONE, ecode);
|
|
if (status != AH_NULL)
|
|
*status = ecode;
|
|
return AH_FALSE;
|
|
#undef FAIL
|
|
#undef N
|
|
}
|
|
|
|
/*
|
|
* Call the rf backend to change the channel.
|
|
*/
|
|
HAL_BOOL
|
|
ar5212SetChannel(struct ath_hal *ah, const struct ieee80211_channel *chan)
|
|
{
|
|
struct ath_hal_5212 *ahp = AH5212(ah);
|
|
|
|
/* Change the synth */
|
|
if (!ahp->ah_rfHal->setChannel(ah, chan))
|
|
return AH_FALSE;
|
|
return AH_TRUE;
|
|
}
|
|
|
|
/*
|
|
* This channel change evaluates whether the selected hardware can
|
|
* perform a synthesizer-only channel change (no reset). If the
|
|
* TX is not stopped, or the RFBus cannot be granted in the given
|
|
* time, the function returns false as a reset is necessary
|
|
*/
|
|
HAL_BOOL
|
|
ar5212ChannelChange(struct ath_hal *ah, const struct ieee80211_channel *chan)
|
|
{
|
|
uint32_t ulCount;
|
|
uint32_t data, synthDelay, qnum;
|
|
uint16_t rfXpdGain[MAX_NUM_PDGAINS_PER_CHANNEL];
|
|
HAL_BOOL txStopped = AH_TRUE;
|
|
HAL_CHANNEL_INTERNAL *ichan;
|
|
|
|
/*
|
|
* Map public channel to private.
|
|
*/
|
|
ichan = ath_hal_checkchannel(ah, chan);
|
|
|
|
/* TX must be stopped or RF Bus grant will not work */
|
|
for (qnum = 0; qnum < AH_PRIVATE(ah)->ah_caps.halTotalQueues; qnum++) {
|
|
if (ar5212NumTxPending(ah, qnum)) {
|
|
txStopped = AH_FALSE;
|
|
break;
|
|
}
|
|
}
|
|
if (!txStopped)
|
|
return AH_FALSE;
|
|
|
|
/* Kill last Baseband Rx Frame */
|
|
OS_REG_WRITE(ah, AR_PHY_RFBUS_REQ, AR_PHY_RFBUS_REQ_REQUEST); /* Request analog bus grant */
|
|
for (ulCount = 0; ulCount < 100; ulCount++) {
|
|
if (OS_REG_READ(ah, AR_PHY_RFBUS_GNT))
|
|
break;
|
|
OS_DELAY(5);
|
|
}
|
|
if (ulCount >= 100)
|
|
return AH_FALSE;
|
|
|
|
/* Change the synth */
|
|
if (!ar5212SetChannel(ah, chan))
|
|
return AH_FALSE;
|
|
|
|
/*
|
|
* Wait for the frequency synth to settle (synth goes on via PHY_ACTIVE_EN).
|
|
* Read the phy active delay register. Value is in 100ns increments.
|
|
*/
|
|
data = OS_REG_READ(ah, AR_PHY_RX_DELAY) & AR_PHY_RX_DELAY_DELAY;
|
|
if (IEEE80211_IS_CHAN_B(chan)) {
|
|
synthDelay = (4 * data) / 22;
|
|
} else {
|
|
synthDelay = data / 10;
|
|
}
|
|
OS_DELAY(synthDelay + BASE_ACTIVATE_DELAY);
|
|
|
|
/* Setup the transmit power values. */
|
|
if (!ar5212SetTransmitPower(ah, chan, rfXpdGain)) {
|
|
HALDEBUG(ah, HAL_DEBUG_ANY,
|
|
"%s: error init'ing transmit power\n", __func__);
|
|
return AH_FALSE;
|
|
}
|
|
|
|
/* Write delta slope for OFDM enabled modes (A, G, Turbo) */
|
|
if (IEEE80211_IS_CHAN_OFDM(chan)) {
|
|
if (IS_5413(ah) ||
|
|
AH_PRIVATE(ah)->ah_eeversion >= AR_EEPROM_VER5_3)
|
|
ar5212SetSpurMitigation(ah, chan);
|
|
ar5212SetDeltaSlope(ah, chan);
|
|
}
|
|
|
|
/* Release the RFBus Grant */
|
|
OS_REG_WRITE(ah, AR_PHY_RFBUS_REQ, 0);
|
|
|
|
/* Start Noise Floor Cal */
|
|
OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
|
|
return AH_TRUE;
|
|
}
|
|
|
|
void
|
|
ar5212SetOperatingMode(struct ath_hal *ah, int opmode)
|
|
{
|
|
uint32_t val;
|
|
|
|
val = OS_REG_READ(ah, AR_STA_ID1);
|
|
val &= ~(AR_STA_ID1_STA_AP | AR_STA_ID1_ADHOC);
|
|
switch (opmode) {
|
|
case HAL_M_HOSTAP:
|
|
OS_REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_STA_AP
|
|
| AR_STA_ID1_KSRCH_MODE);
|
|
OS_REG_CLR_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION);
|
|
break;
|
|
case HAL_M_IBSS:
|
|
OS_REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_ADHOC
|
|
| AR_STA_ID1_KSRCH_MODE);
|
|
OS_REG_SET_BIT(ah, AR_CFG, AR_CFG_AP_ADHOC_INDICATION);
|
|
break;
|
|
case HAL_M_STA:
|
|
case HAL_M_MONITOR:
|
|
OS_REG_WRITE(ah, AR_STA_ID1, val | AR_STA_ID1_KSRCH_MODE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Places the PHY and Radio chips into reset. A full reset
|
|
* must be called to leave this state. The PCI/MAC/PCU are
|
|
* not placed into reset as we must receive interrupt to
|
|
* re-enable the hardware.
|
|
*/
|
|
HAL_BOOL
|
|
ar5212PhyDisable(struct ath_hal *ah)
|
|
{
|
|
return ar5212SetResetReg(ah, AR_RC_BB);
|
|
}
|
|
|
|
/*
|
|
* Places all of hardware into reset
|
|
*/
|
|
HAL_BOOL
|
|
ar5212Disable(struct ath_hal *ah)
|
|
{
|
|
if (!ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE))
|
|
return AH_FALSE;
|
|
/*
|
|
* Reset the HW - PCI must be reset after the rest of the
|
|
* device has been reset.
|
|
*/
|
|
return ar5212SetResetReg(ah, AR_RC_MAC | AR_RC_BB | AR_RC_PCI);
|
|
}
|
|
|
|
/*
|
|
* Places the hardware into reset and then pulls it out of reset
|
|
*
|
|
* TODO: Only write the PLL if we're changing to or from CCK mode
|
|
*
|
|
* WARNING: The order of the PLL and mode registers must be correct.
|
|
*/
|
|
HAL_BOOL
|
|
ar5212ChipReset(struct ath_hal *ah, const struct ieee80211_channel *chan)
|
|
{
|
|
|
|
OS_MARK(ah, AH_MARK_CHIPRESET, chan ? chan->ic_freq : 0);
|
|
|
|
/*
|
|
* Reset the HW - PCI must be reset after the rest of the
|
|
* device has been reset
|
|
*/
|
|
if (!ar5212SetResetReg(ah, AR_RC_MAC | AR_RC_BB | AR_RC_PCI))
|
|
return AH_FALSE;
|
|
|
|
/* Bring out of sleep mode (AGAIN) */
|
|
if (!ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE))
|
|
return AH_FALSE;
|
|
|
|
/* Clear warm reset register */
|
|
if (!ar5212SetResetReg(ah, 0))
|
|
return AH_FALSE;
|
|
|
|
/*
|
|
* Perform warm reset before the mode/PLL/turbo registers
|
|
* are changed in order to deactivate the radio. Mode changes
|
|
* with an active radio can result in corrupted shifts to the
|
|
* radio device.
|
|
*/
|
|
|
|
/*
|
|
* Set CCK and Turbo modes correctly.
|
|
*/
|
|
if (chan != AH_NULL) { /* NB: can be null during attach */
|
|
uint32_t rfMode, phyPLL = 0, curPhyPLL, turbo;
|
|
|
|
if (IS_5413(ah)) { /* NB: =>'s 5424 also */
|
|
rfMode = AR_PHY_MODE_AR5112;
|
|
if (IEEE80211_IS_CHAN_HALF(chan))
|
|
rfMode |= AR_PHY_MODE_HALF;
|
|
else if (IEEE80211_IS_CHAN_QUARTER(chan))
|
|
rfMode |= AR_PHY_MODE_QUARTER;
|
|
|
|
if (IEEE80211_IS_CHAN_CCK(chan))
|
|
phyPLL = AR_PHY_PLL_CTL_44_5112;
|
|
else
|
|
phyPLL = AR_PHY_PLL_CTL_40_5413;
|
|
} else if (IS_RAD5111(ah)) {
|
|
rfMode = AR_PHY_MODE_AR5111;
|
|
if (IEEE80211_IS_CHAN_CCK(chan))
|
|
phyPLL = AR_PHY_PLL_CTL_44;
|
|
else
|
|
phyPLL = AR_PHY_PLL_CTL_40;
|
|
if (IEEE80211_IS_CHAN_HALF(chan))
|
|
phyPLL = AR_PHY_PLL_CTL_HALF;
|
|
else if (IEEE80211_IS_CHAN_QUARTER(chan))
|
|
phyPLL = AR_PHY_PLL_CTL_QUARTER;
|
|
} else { /* 5112, 2413, 2316, 2317 */
|
|
rfMode = AR_PHY_MODE_AR5112;
|
|
if (IEEE80211_IS_CHAN_CCK(chan))
|
|
phyPLL = AR_PHY_PLL_CTL_44_5112;
|
|
else
|
|
phyPLL = AR_PHY_PLL_CTL_40_5112;
|
|
if (IEEE80211_IS_CHAN_HALF(chan))
|
|
phyPLL |= AR_PHY_PLL_CTL_HALF;
|
|
else if (IEEE80211_IS_CHAN_QUARTER(chan))
|
|
phyPLL |= AR_PHY_PLL_CTL_QUARTER;
|
|
}
|
|
if (IEEE80211_IS_CHAN_G(chan))
|
|
rfMode |= AR_PHY_MODE_DYNAMIC;
|
|
else if (IEEE80211_IS_CHAN_OFDM(chan))
|
|
rfMode |= AR_PHY_MODE_OFDM;
|
|
else
|
|
rfMode |= AR_PHY_MODE_CCK;
|
|
if (IEEE80211_IS_CHAN_5GHZ(chan))
|
|
rfMode |= AR_PHY_MODE_RF5GHZ;
|
|
else
|
|
rfMode |= AR_PHY_MODE_RF2GHZ;
|
|
turbo = IEEE80211_IS_CHAN_TURBO(chan) ?
|
|
(AR_PHY_FC_TURBO_MODE | AR_PHY_FC_TURBO_SHORT) : 0;
|
|
curPhyPLL = OS_REG_READ(ah, AR_PHY_PLL_CTL);
|
|
/*
|
|
* PLL, Mode, and Turbo values must be written in the correct
|
|
* order to ensure:
|
|
* - The PLL cannot be set to 44 unless the CCK or DYNAMIC
|
|
* mode bit is set
|
|
* - Turbo cannot be set at the same time as CCK or DYNAMIC
|
|
*/
|
|
if (IEEE80211_IS_CHAN_CCK(chan)) {
|
|
OS_REG_WRITE(ah, AR_PHY_TURBO, turbo);
|
|
OS_REG_WRITE(ah, AR_PHY_MODE, rfMode);
|
|
if (curPhyPLL != phyPLL) {
|
|
OS_REG_WRITE(ah, AR_PHY_PLL_CTL, phyPLL);
|
|
/* Wait for the PLL to settle */
|
|
OS_DELAY(PLL_SETTLE_DELAY);
|
|
}
|
|
} else {
|
|
if (curPhyPLL != phyPLL) {
|
|
OS_REG_WRITE(ah, AR_PHY_PLL_CTL, phyPLL);
|
|
/* Wait for the PLL to settle */
|
|
OS_DELAY(PLL_SETTLE_DELAY);
|
|
}
|
|
OS_REG_WRITE(ah, AR_PHY_TURBO, turbo);
|
|
OS_REG_WRITE(ah, AR_PHY_MODE, rfMode);
|
|
}
|
|
}
|
|
return AH_TRUE;
|
|
}
|
|
|
|
/*
|
|
* Recalibrate the lower PHY chips to account for temperature/environment
|
|
* changes.
|
|
*/
|
|
HAL_BOOL
|
|
ar5212PerCalibrationN(struct ath_hal *ah,
|
|
struct ieee80211_channel *chan,
|
|
u_int chainMask, HAL_BOOL longCal, HAL_BOOL *isCalDone)
|
|
{
|
|
#define IQ_CAL_TRIES 10
|
|
struct ath_hal_5212 *ahp = AH5212(ah);
|
|
HAL_CHANNEL_INTERNAL *ichan;
|
|
int32_t qCoff, qCoffDenom;
|
|
int32_t iqCorrMeas, iCoff, iCoffDenom;
|
|
uint32_t powerMeasQ, powerMeasI;
|
|
HAL_BOOL isBmode = AH_FALSE;
|
|
|
|
OS_MARK(ah, AH_MARK_PERCAL, chan->ic_freq);
|
|
*isCalDone = AH_FALSE;
|
|
ichan = ath_hal_checkchannel(ah, chan);
|
|
if (ichan == AH_NULL) {
|
|
HALDEBUG(ah, HAL_DEBUG_ANY,
|
|
"%s: invalid channel %u/0x%x; no mapping\n",
|
|
__func__, chan->ic_freq, chan->ic_flags);
|
|
return AH_FALSE;
|
|
}
|
|
SAVE_CCK(ah, chan, isBmode);
|
|
|
|
if (ahp->ah_bIQCalibration == IQ_CAL_DONE ||
|
|
ahp->ah_bIQCalibration == IQ_CAL_INACTIVE)
|
|
*isCalDone = AH_TRUE;
|
|
|
|
/* IQ calibration in progress. Check to see if it has finished. */
|
|
if (ahp->ah_bIQCalibration == IQ_CAL_RUNNING &&
|
|
!(OS_REG_READ(ah, AR_PHY_TIMING_CTRL4) & AR_PHY_TIMING_CTRL4_DO_IQCAL)) {
|
|
int i;
|
|
|
|
/* IQ Calibration has finished. */
|
|
ahp->ah_bIQCalibration = IQ_CAL_INACTIVE;
|
|
*isCalDone = AH_TRUE;
|
|
|
|
/* workaround for misgated IQ Cal results */
|
|
i = 0;
|
|
do {
|
|
/* Read calibration results. */
|
|
powerMeasI = OS_REG_READ(ah, AR_PHY_IQCAL_RES_PWR_MEAS_I);
|
|
powerMeasQ = OS_REG_READ(ah, AR_PHY_IQCAL_RES_PWR_MEAS_Q);
|
|
iqCorrMeas = OS_REG_READ(ah, AR_PHY_IQCAL_RES_IQ_CORR_MEAS);
|
|
if (powerMeasI && powerMeasQ)
|
|
break;
|
|
/* Do we really need this??? */
|
|
OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4,
|
|
AR_PHY_TIMING_CTRL4_DO_IQCAL);
|
|
} while (++i < IQ_CAL_TRIES);
|
|
|
|
HALDEBUG(ah, HAL_DEBUG_PERCAL,
|
|
"%s: IQ cal finished: %d tries\n", __func__, i);
|
|
HALDEBUG(ah, HAL_DEBUG_PERCAL,
|
|
"%s: powerMeasI %u powerMeasQ %u iqCorrMeas %d\n",
|
|
__func__, powerMeasI, powerMeasQ, iqCorrMeas);
|
|
|
|
/*
|
|
* Prescale these values to remove 64-bit operation
|
|
* requirement at the loss of a little precision.
|
|
*/
|
|
iCoffDenom = (powerMeasI / 2 + powerMeasQ / 2) / 128;
|
|
qCoffDenom = powerMeasQ / 128;
|
|
|
|
/* Protect against divide-by-0 and loss of sign bits. */
|
|
if (iCoffDenom != 0 && qCoffDenom >= 2) {
|
|
iCoff = (int8_t)(-iqCorrMeas) / iCoffDenom;
|
|
/* IQCORR_Q_I_COFF is a signed 6 bit number */
|
|
if (iCoff < -32) {
|
|
iCoff = -32;
|
|
} else if (iCoff > 31) {
|
|
iCoff = 31;
|
|
}
|
|
|
|
/* IQCORR_Q_Q_COFF is a signed 5 bit number */
|
|
qCoff = (powerMeasI / qCoffDenom) - 128;
|
|
if (qCoff < -16) {
|
|
qCoff = -16;
|
|
} else if (qCoff > 15) {
|
|
qCoff = 15;
|
|
}
|
|
|
|
HALDEBUG(ah, HAL_DEBUG_PERCAL,
|
|
"%s: iCoff %d qCoff %d\n", __func__, iCoff, qCoff);
|
|
|
|
/* Write values and enable correction */
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4,
|
|
AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF, iCoff);
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4,
|
|
AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF, qCoff);
|
|
OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4,
|
|
AR_PHY_TIMING_CTRL4_IQCORR_ENABLE);
|
|
|
|
ahp->ah_bIQCalibration = IQ_CAL_DONE;
|
|
ichan->privFlags |= CHANNEL_IQVALID;
|
|
ichan->iCoff = iCoff;
|
|
ichan->qCoff = qCoff;
|
|
}
|
|
} else if (!IEEE80211_IS_CHAN_B(chan) &&
|
|
ahp->ah_bIQCalibration == IQ_CAL_DONE &&
|
|
(ichan->privFlags & CHANNEL_IQVALID) == 0) {
|
|
/*
|
|
* Start IQ calibration if configured channel has changed.
|
|
* Use a magic number of 15 based on default value.
|
|
*/
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4,
|
|
AR_PHY_TIMING_CTRL4_IQCAL_LOG_COUNT_MAX,
|
|
INIT_IQCAL_LOG_COUNT_MAX);
|
|
OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4,
|
|
AR_PHY_TIMING_CTRL4_DO_IQCAL);
|
|
ahp->ah_bIQCalibration = IQ_CAL_RUNNING;
|
|
}
|
|
/* XXX EAR */
|
|
|
|
if (longCal) {
|
|
/* Check noise floor results */
|
|
ar5212GetNf(ah, chan);
|
|
if (!IEEE80211_IS_CHAN_CWINT(chan)) {
|
|
/* Perform cal for 5Ghz channels and any OFDM on 5112 */
|
|
if (IEEE80211_IS_CHAN_5GHZ(chan) ||
|
|
(IS_RAD5112(ah) && IEEE80211_IS_CHAN_OFDM(chan)))
|
|
ar5212RequestRfgain(ah);
|
|
}
|
|
}
|
|
RESTORE_CCK(ah, chan, isBmode);
|
|
|
|
return AH_TRUE;
|
|
#undef IQ_CAL_TRIES
|
|
}
|
|
|
|
HAL_BOOL
|
|
ar5212PerCalibration(struct ath_hal *ah, struct ieee80211_channel *chan,
|
|
HAL_BOOL *isIQdone)
|
|
{
|
|
return ar5212PerCalibrationN(ah, chan, 0x1, AH_TRUE, isIQdone);
|
|
}
|
|
|
|
HAL_BOOL
|
|
ar5212ResetCalValid(struct ath_hal *ah, const struct ieee80211_channel *chan)
|
|
{
|
|
HAL_CHANNEL_INTERNAL *ichan;
|
|
|
|
ichan = ath_hal_checkchannel(ah, chan);
|
|
if (ichan == AH_NULL) {
|
|
HALDEBUG(ah, HAL_DEBUG_ANY,
|
|
"%s: invalid channel %u/0x%x; no mapping\n",
|
|
__func__, chan->ic_freq, chan->ic_flags);
|
|
return AH_FALSE;
|
|
}
|
|
ichan->privFlags &= ~CHANNEL_IQVALID;
|
|
return AH_TRUE;
|
|
}
|
|
|
|
/*
|
|
* Write the given reset bit mask into the reset register
|
|
*/
|
|
static HAL_BOOL
|
|
ar5212SetResetReg(struct ath_hal *ah, uint32_t resetMask)
|
|
{
|
|
uint32_t mask = resetMask ? resetMask : ~0;
|
|
HAL_BOOL rt;
|
|
|
|
/* XXX ar5212MacStop & co. */
|
|
|
|
if (AH_PRIVATE(ah)->ah_ispcie) {
|
|
resetMask &= ~AR_RC_PCI;
|
|
}
|
|
|
|
(void) OS_REG_READ(ah, AR_RXDP);/* flush any pending MMR writes */
|
|
OS_REG_WRITE(ah, AR_RC, resetMask);
|
|
OS_DELAY(15); /* need to wait at least 128 clocks
|
|
when reseting PCI before read */
|
|
mask &= (AR_RC_MAC | AR_RC_BB);
|
|
resetMask &= (AR_RC_MAC | AR_RC_BB);
|
|
rt = ath_hal_wait(ah, AR_RC, mask, resetMask);
|
|
if ((resetMask & AR_RC_MAC) == 0) {
|
|
if (isBigEndian()) {
|
|
/*
|
|
* Set CFG, little-endian for register
|
|
* and descriptor accesses.
|
|
*/
|
|
mask = INIT_CONFIG_STATUS | AR_CFG_SWRD | AR_CFG_SWRG;
|
|
#ifndef AH_NEED_DESC_SWAP
|
|
mask |= AR_CFG_SWTD;
|
|
#endif
|
|
OS_REG_WRITE(ah, AR_CFG, LE_READ_4(&mask));
|
|
} else
|
|
OS_REG_WRITE(ah, AR_CFG, INIT_CONFIG_STATUS);
|
|
if (ar5212SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE))
|
|
(void) OS_REG_READ(ah, AR_ISR_RAC);
|
|
}
|
|
|
|
/* track PHY power state so we don't try to r/w BB registers */
|
|
AH5212(ah)->ah_phyPowerOn = ((resetMask & AR_RC_BB) == 0);
|
|
return rt;
|
|
}
|
|
|
|
int16_t
|
|
ar5212GetNoiseFloor(struct ath_hal *ah)
|
|
{
|
|
int16_t nf = (OS_REG_READ(ah, AR_PHY(25)) >> 19) & 0x1ff;
|
|
if (nf & 0x100)
|
|
nf = 0 - ((nf ^ 0x1ff) + 1);
|
|
return nf;
|
|
}
|
|
|
|
static HAL_BOOL
|
|
getNoiseFloorThresh(struct ath_hal *ah, const struct ieee80211_channel *chan,
|
|
int16_t *nft)
|
|
{
|
|
const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;
|
|
|
|
HALASSERT(ah->ah_magic == AR5212_MAGIC);
|
|
|
|
switch (chan->ic_flags & IEEE80211_CHAN_ALLFULL) {
|
|
case IEEE80211_CHAN_A:
|
|
*nft = ee->ee_noiseFloorThresh[headerInfo11A];
|
|
break;
|
|
case IEEE80211_CHAN_B:
|
|
*nft = ee->ee_noiseFloorThresh[headerInfo11B];
|
|
break;
|
|
case IEEE80211_CHAN_G:
|
|
case IEEE80211_CHAN_PUREG: /* NB: really 108G */
|
|
*nft = ee->ee_noiseFloorThresh[headerInfo11G];
|
|
break;
|
|
default:
|
|
HALDEBUG(ah, HAL_DEBUG_ANY,
|
|
"%s: invalid channel flags %u/0x%x\n",
|
|
__func__, chan->ic_freq, chan->ic_flags);
|
|
return AH_FALSE;
|
|
}
|
|
return AH_TRUE;
|
|
}
|
|
|
|
/*
|
|
* Setup the noise floor cal history buffer.
|
|
*/
|
|
void
|
|
ar5212InitNfCalHistBuffer(struct ath_hal *ah)
|
|
{
|
|
struct ath_hal_5212 *ahp = AH5212(ah);
|
|
int i;
|
|
|
|
ahp->ah_nfCalHist.first_run = 1;
|
|
ahp->ah_nfCalHist.currIndex = 0;
|
|
ahp->ah_nfCalHist.privNF = AR5212_CCA_MAX_GOOD_VALUE;
|
|
ahp->ah_nfCalHist.invalidNFcount = AR512_NF_CAL_HIST_MAX;
|
|
for (i = 0; i < AR512_NF_CAL_HIST_MAX; i ++)
|
|
ahp->ah_nfCalHist.nfCalBuffer[i] = AR5212_CCA_MAX_GOOD_VALUE;
|
|
}
|
|
|
|
/*
|
|
* Add a noise floor value to the ring buffer.
|
|
*/
|
|
static __inline void
|
|
updateNFHistBuff(struct ar5212NfCalHist *h, int16_t nf)
|
|
{
|
|
h->nfCalBuffer[h->currIndex] = nf;
|
|
if (++h->currIndex >= AR512_NF_CAL_HIST_MAX)
|
|
h->currIndex = 0;
|
|
}
|
|
|
|
/*
|
|
* Return the median noise floor value in the ring buffer.
|
|
*/
|
|
int16_t
|
|
ar5212GetNfHistMid(const int16_t calData[AR512_NF_CAL_HIST_MAX])
|
|
{
|
|
int16_t sort[AR512_NF_CAL_HIST_MAX];
|
|
int i, j;
|
|
|
|
OS_MEMCPY(sort, calData, AR512_NF_CAL_HIST_MAX*sizeof(int16_t));
|
|
for (i = 0; i < AR512_NF_CAL_HIST_MAX-1; i ++) {
|
|
for (j = 1; j < AR512_NF_CAL_HIST_MAX-i; j ++) {
|
|
if (sort[j] > sort[j-1]) {
|
|
int16_t nf = sort[j];
|
|
sort[j] = sort[j-1];
|
|
sort[j-1] = nf;
|
|
}
|
|
}
|
|
}
|
|
return sort[(AR512_NF_CAL_HIST_MAX-1)>>1];
|
|
}
|
|
|
|
/*
|
|
* Read the NF and check it against the noise floor threshhold
|
|
*/
|
|
int16_t
|
|
ar5212GetNf(struct ath_hal *ah, struct ieee80211_channel *chan)
|
|
{
|
|
struct ath_hal_5212 *ahp = AH5212(ah);
|
|
struct ar5212NfCalHist *h = &ahp->ah_nfCalHist;
|
|
HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);
|
|
int16_t nf, nfThresh;
|
|
int32_t val;
|
|
|
|
if (OS_REG_READ(ah, AR_PHY_AGC_CONTROL) & AR_PHY_AGC_CONTROL_NF) {
|
|
HALDEBUG(ah, HAL_DEBUG_ANY,
|
|
"%s: NF did not complete in calibration window\n", __func__);
|
|
ichan->rawNoiseFloor = h->privNF; /* most recent value */
|
|
return ichan->rawNoiseFloor;
|
|
}
|
|
|
|
/*
|
|
* Finished NF cal, check against threshold.
|
|
*/
|
|
nf = ar5212GetNoiseFloor(ah);
|
|
if (getNoiseFloorThresh(ah, chan, &nfThresh)) {
|
|
if (nf > nfThresh) {
|
|
HALDEBUG(ah, HAL_DEBUG_ANY,
|
|
"%s: noise floor failed detected; detected %u, "
|
|
"threshold %u\n", __func__, nf, nfThresh);
|
|
/*
|
|
* NB: Don't discriminate 2.4 vs 5Ghz, if this
|
|
* happens it indicates a problem regardless
|
|
* of the band.
|
|
*/
|
|
chan->ic_state |= IEEE80211_CHANSTATE_CWINT;
|
|
nf = 0;
|
|
}
|
|
} else
|
|
nf = 0;
|
|
|
|
/*
|
|
* Pass through histogram and write median value as
|
|
* calculated from the accrued window. We require a
|
|
* full window of in-range values to be seen before we
|
|
* start using the history.
|
|
*/
|
|
updateNFHistBuff(h, nf);
|
|
if (h->first_run) {
|
|
if (nf < AR5212_CCA_MIN_BAD_VALUE ||
|
|
nf > AR5212_CCA_MAX_HIGH_VALUE) {
|
|
nf = AR5212_CCA_MAX_GOOD_VALUE;
|
|
h->invalidNFcount = AR512_NF_CAL_HIST_MAX;
|
|
} else if (--(h->invalidNFcount) == 0) {
|
|
h->first_run = 0;
|
|
h->privNF = nf = ar5212GetNfHistMid(h->nfCalBuffer);
|
|
} else {
|
|
nf = AR5212_CCA_MAX_GOOD_VALUE;
|
|
}
|
|
} else {
|
|
h->privNF = nf = ar5212GetNfHistMid(h->nfCalBuffer);
|
|
}
|
|
|
|
val = OS_REG_READ(ah, AR_PHY(25));
|
|
val &= 0xFFFFFE00;
|
|
val |= (((uint32_t)nf << 1) & 0x1FF);
|
|
OS_REG_WRITE(ah, AR_PHY(25), val);
|
|
OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF);
|
|
OS_REG_CLR_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
|
|
OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
|
|
|
|
if (!ath_hal_wait(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF, 0)) {
|
|
#ifdef AH_DEBUG
|
|
ath_hal_printf(ah, "%s: AGC not ready AGC_CONTROL 0x%x\n",
|
|
__func__, OS_REG_READ(ah, AR_PHY_AGC_CONTROL));
|
|
#endif
|
|
}
|
|
|
|
/*
|
|
* Now load a high maxCCAPower value again so that we're
|
|
* not capped by the median we just loaded
|
|
*/
|
|
val &= 0xFFFFFE00;
|
|
val |= (((uint32_t)(-50) << 1) & 0x1FF);
|
|
OS_REG_WRITE(ah, AR_PHY(25), val);
|
|
OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_ENABLE_NF);
|
|
OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NO_UPDATE_NF);
|
|
OS_REG_SET_BIT(ah, AR_PHY_AGC_CONTROL, AR_PHY_AGC_CONTROL_NF);
|
|
|
|
return (ichan->rawNoiseFloor = nf);
|
|
}
|
|
|
|
/*
|
|
* Set up compression configuration registers
|
|
*/
|
|
void
|
|
ar5212SetCompRegs(struct ath_hal *ah)
|
|
{
|
|
struct ath_hal_5212 *ahp = AH5212(ah);
|
|
int i;
|
|
|
|
/* Check if h/w supports compression */
|
|
if (!AH_PRIVATE(ah)->ah_caps.halCompressSupport)
|
|
return;
|
|
|
|
OS_REG_WRITE(ah, AR_DCCFG, 1);
|
|
|
|
OS_REG_WRITE(ah, AR_CCFG,
|
|
(AR_COMPRESSION_WINDOW_SIZE >> 8) & AR_CCFG_WIN_M);
|
|
|
|
OS_REG_WRITE(ah, AR_CCFG,
|
|
OS_REG_READ(ah, AR_CCFG) | AR_CCFG_MIB_INT_EN);
|
|
OS_REG_WRITE(ah, AR_CCUCFG,
|
|
AR_CCUCFG_RESET_VAL | AR_CCUCFG_CATCHUP_EN);
|
|
|
|
OS_REG_WRITE(ah, AR_CPCOVF, 0);
|
|
|
|
/* reset decompression mask */
|
|
for (i = 0; i < HAL_DECOMP_MASK_SIZE; i++) {
|
|
OS_REG_WRITE(ah, AR_DCM_A, i);
|
|
OS_REG_WRITE(ah, AR_DCM_D, ahp->ah_decompMask[i]);
|
|
}
|
|
}
|
|
|
|
HAL_BOOL
|
|
ar5212SetAntennaSwitchInternal(struct ath_hal *ah, HAL_ANT_SETTING settings,
|
|
const struct ieee80211_channel *chan)
|
|
{
|
|
#define ANT_SWITCH_TABLE1 AR_PHY(88)
|
|
#define ANT_SWITCH_TABLE2 AR_PHY(89)
|
|
struct ath_hal_5212 *ahp = AH5212(ah);
|
|
const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;
|
|
uint32_t antSwitchA, antSwitchB;
|
|
int ix;
|
|
|
|
HALASSERT(ah->ah_magic == AR5212_MAGIC);
|
|
HALASSERT(ahp->ah_phyPowerOn);
|
|
|
|
switch (chan->ic_flags & IEEE80211_CHAN_ALLFULL) {
|
|
case IEEE80211_CHAN_A:
|
|
ix = 0;
|
|
break;
|
|
case IEEE80211_CHAN_G:
|
|
case IEEE80211_CHAN_PUREG: /* NB: 108G */
|
|
ix = 2;
|
|
break;
|
|
case IEEE80211_CHAN_B:
|
|
if (IS_2425(ah) || IS_2417(ah)) {
|
|
/* NB: Nala/Swan: 11b is handled using 11g */
|
|
ix = 2;
|
|
} else
|
|
ix = 1;
|
|
break;
|
|
default:
|
|
HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n",
|
|
__func__, chan->ic_flags);
|
|
return AH_FALSE;
|
|
}
|
|
|
|
antSwitchA = ee->ee_antennaControl[1][ix]
|
|
| (ee->ee_antennaControl[2][ix] << 6)
|
|
| (ee->ee_antennaControl[3][ix] << 12)
|
|
| (ee->ee_antennaControl[4][ix] << 18)
|
|
| (ee->ee_antennaControl[5][ix] << 24)
|
|
;
|
|
antSwitchB = ee->ee_antennaControl[6][ix]
|
|
| (ee->ee_antennaControl[7][ix] << 6)
|
|
| (ee->ee_antennaControl[8][ix] << 12)
|
|
| (ee->ee_antennaControl[9][ix] << 18)
|
|
| (ee->ee_antennaControl[10][ix] << 24)
|
|
;
|
|
/*
|
|
* For fixed antenna, give the same setting for both switch banks
|
|
*/
|
|
switch (settings) {
|
|
case HAL_ANT_FIXED_A:
|
|
antSwitchB = antSwitchA;
|
|
break;
|
|
case HAL_ANT_FIXED_B:
|
|
antSwitchA = antSwitchB;
|
|
break;
|
|
case HAL_ANT_VARIABLE:
|
|
break;
|
|
default:
|
|
HALDEBUG(ah, HAL_DEBUG_ANY, "%s: bad antenna setting %u\n",
|
|
__func__, settings);
|
|
return AH_FALSE;
|
|
}
|
|
if (antSwitchB == antSwitchA) {
|
|
HALDEBUG(ah, HAL_DEBUG_RFPARAM,
|
|
"%s: Setting fast diversity off.\n", __func__);
|
|
OS_REG_CLR_BIT(ah,AR_PHY_CCK_DETECT,
|
|
AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
|
|
ahp->ah_diversity = AH_FALSE;
|
|
} else {
|
|
HALDEBUG(ah, HAL_DEBUG_RFPARAM,
|
|
"%s: Setting fast diversity on.\n", __func__);
|
|
OS_REG_SET_BIT(ah,AR_PHY_CCK_DETECT,
|
|
AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
|
|
ahp->ah_diversity = AH_TRUE;
|
|
}
|
|
ahp->ah_antControl = settings;
|
|
|
|
OS_REG_WRITE(ah, ANT_SWITCH_TABLE1, antSwitchA);
|
|
OS_REG_WRITE(ah, ANT_SWITCH_TABLE2, antSwitchB);
|
|
|
|
return AH_TRUE;
|
|
#undef ANT_SWITCH_TABLE2
|
|
#undef ANT_SWITCH_TABLE1
|
|
}
|
|
|
|
HAL_BOOL
|
|
ar5212IsSpurChannel(struct ath_hal *ah, const struct ieee80211_channel *chan)
|
|
{
|
|
uint16_t freq = ath_hal_gethwchannel(ah, chan);
|
|
uint32_t clockFreq =
|
|
((IS_5413(ah) || IS_RAD5112_ANY(ah) || IS_2417(ah)) ? 40 : 32);
|
|
return ( ((freq % clockFreq) != 0)
|
|
&& (((freq % clockFreq) < 10)
|
|
|| (((freq) % clockFreq) > 22)) );
|
|
}
|
|
|
|
/*
|
|
* Read EEPROM header info and program the device for correct operation
|
|
* given the channel value.
|
|
*/
|
|
HAL_BOOL
|
|
ar5212SetBoardValues(struct ath_hal *ah, const struct ieee80211_channel *chan)
|
|
{
|
|
#define NO_FALSE_DETECT_BACKOFF 2
|
|
#define CB22_FALSE_DETECT_BACKOFF 6
|
|
#define AR_PHY_BIS(_ah, _reg, _mask, _val) \
|
|
OS_REG_WRITE(_ah, AR_PHY(_reg), \
|
|
(OS_REG_READ(_ah, AR_PHY(_reg)) & _mask) | (_val));
|
|
struct ath_hal_5212 *ahp = AH5212(ah);
|
|
const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;
|
|
int arrayMode, falseDectectBackoff;
|
|
int is2GHz = IEEE80211_IS_CHAN_2GHZ(chan);
|
|
HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);
|
|
int8_t adcDesiredSize, pgaDesiredSize;
|
|
uint16_t switchSettling, txrxAtten, rxtxMargin;
|
|
int iCoff, qCoff;
|
|
|
|
HALASSERT(ah->ah_magic == AR5212_MAGIC);
|
|
|
|
switch (chan->ic_flags & IEEE80211_CHAN_ALLTURBOFULL) {
|
|
case IEEE80211_CHAN_A:
|
|
case IEEE80211_CHAN_ST:
|
|
arrayMode = headerInfo11A;
|
|
if (!IS_RAD5112_ANY(ah) && !IS_2413(ah) && !IS_5413(ah))
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_FRAME_CTL,
|
|
AR_PHY_FRAME_CTL_TX_CLIP,
|
|
ahp->ah_gainValues.currStep->paramVal[GP_TXCLIP]);
|
|
break;
|
|
case IEEE80211_CHAN_B:
|
|
arrayMode = headerInfo11B;
|
|
break;
|
|
case IEEE80211_CHAN_G:
|
|
case IEEE80211_CHAN_108G:
|
|
arrayMode = headerInfo11G;
|
|
break;
|
|
default:
|
|
HALDEBUG(ah, HAL_DEBUG_ANY, "%s: invalid channel flags 0x%x\n",
|
|
__func__, chan->ic_flags);
|
|
return AH_FALSE;
|
|
}
|
|
|
|
/* Set the antenna register(s) correctly for the chip revision */
|
|
AR_PHY_BIS(ah, 68, 0xFFFFFC06,
|
|
(ee->ee_antennaControl[0][arrayMode] << 4) | 0x1);
|
|
|
|
ar5212SetAntennaSwitchInternal(ah, ahp->ah_antControl, chan);
|
|
|
|
/* Set the Noise Floor Thresh on ar5211 devices */
|
|
OS_REG_WRITE(ah, AR_PHY(90),
|
|
(ee->ee_noiseFloorThresh[arrayMode] & 0x1FF)
|
|
| (1 << 9));
|
|
|
|
if (ee->ee_version >= AR_EEPROM_VER5_0 && IEEE80211_IS_CHAN_TURBO(chan)) {
|
|
switchSettling = ee->ee_switchSettlingTurbo[is2GHz];
|
|
adcDesiredSize = ee->ee_adcDesiredSizeTurbo[is2GHz];
|
|
pgaDesiredSize = ee->ee_pgaDesiredSizeTurbo[is2GHz];
|
|
txrxAtten = ee->ee_txrxAttenTurbo[is2GHz];
|
|
rxtxMargin = ee->ee_rxtxMarginTurbo[is2GHz];
|
|
} else {
|
|
switchSettling = ee->ee_switchSettling[arrayMode];
|
|
adcDesiredSize = ee->ee_adcDesiredSize[arrayMode];
|
|
pgaDesiredSize = ee->ee_pgaDesiredSize[is2GHz];
|
|
txrxAtten = ee->ee_txrxAtten[is2GHz];
|
|
rxtxMargin = ee->ee_rxtxMargin[is2GHz];
|
|
}
|
|
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_SETTLING,
|
|
AR_PHY_SETTLING_SWITCH, switchSettling);
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ,
|
|
AR_PHY_DESIRED_SZ_ADC, adcDesiredSize);
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_DESIRED_SZ,
|
|
AR_PHY_DESIRED_SZ_PGA, pgaDesiredSize);
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_RXGAIN,
|
|
AR_PHY_RXGAIN_TXRX_ATTEN, txrxAtten);
|
|
OS_REG_WRITE(ah, AR_PHY(13),
|
|
(ee->ee_txEndToXPAOff[arrayMode] << 24)
|
|
| (ee->ee_txEndToXPAOff[arrayMode] << 16)
|
|
| (ee->ee_txFrameToXPAOn[arrayMode] << 8)
|
|
| ee->ee_txFrameToXPAOn[arrayMode]);
|
|
AR_PHY_BIS(ah, 10, 0xFFFF00FF,
|
|
ee->ee_txEndToXLNAOn[arrayMode] << 8);
|
|
AR_PHY_BIS(ah, 25, 0xFFF80FFF,
|
|
(ee->ee_thresh62[arrayMode] << 12) & 0x7F000);
|
|
|
|
/*
|
|
* False detect backoff - suspected 32 MHz spur causes false
|
|
* detects in OFDM, causing Tx Hangs. Decrease weak signal
|
|
* sensitivity for this card.
|
|
*/
|
|
falseDectectBackoff = NO_FALSE_DETECT_BACKOFF;
|
|
if (ee->ee_version < AR_EEPROM_VER3_3) {
|
|
/* XXX magic number */
|
|
if (AH_PRIVATE(ah)->ah_subvendorid == 0x1022 &&
|
|
IEEE80211_IS_CHAN_OFDM(chan))
|
|
falseDectectBackoff += CB22_FALSE_DETECT_BACKOFF;
|
|
} else {
|
|
if (ar5212IsSpurChannel(ah, chan))
|
|
falseDectectBackoff += ee->ee_falseDetectBackoff[arrayMode];
|
|
}
|
|
AR_PHY_BIS(ah, 73, 0xFFFFFF01, (falseDectectBackoff << 1) & 0xFE);
|
|
|
|
if (ichan->privFlags & CHANNEL_IQVALID) {
|
|
iCoff = ichan->iCoff;
|
|
qCoff = ichan->qCoff;
|
|
} else {
|
|
iCoff = ee->ee_iqCalI[is2GHz];
|
|
qCoff = ee->ee_iqCalQ[is2GHz];
|
|
}
|
|
|
|
/* write previous IQ results */
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4,
|
|
AR_PHY_TIMING_CTRL4_IQCORR_Q_I_COFF, iCoff);
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_TIMING_CTRL4,
|
|
AR_PHY_TIMING_CTRL4_IQCORR_Q_Q_COFF, qCoff);
|
|
OS_REG_SET_BIT(ah, AR_PHY_TIMING_CTRL4,
|
|
AR_PHY_TIMING_CTRL4_IQCORR_ENABLE);
|
|
|
|
if (ee->ee_version >= AR_EEPROM_VER4_1) {
|
|
if (!IEEE80211_IS_CHAN_108G(chan) || ee->ee_version >= AR_EEPROM_VER5_0)
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_GAIN_2GHZ,
|
|
AR_PHY_GAIN_2GHZ_RXTX_MARGIN, rxtxMargin);
|
|
}
|
|
if (ee->ee_version >= AR_EEPROM_VER5_1) {
|
|
/* for now always disabled */
|
|
OS_REG_WRITE(ah, AR_PHY_HEAVY_CLIP_ENABLE, 0);
|
|
}
|
|
|
|
return AH_TRUE;
|
|
#undef AR_PHY_BIS
|
|
#undef NO_FALSE_DETECT_BACKOFF
|
|
#undef CB22_FALSE_DETECT_BACKOFF
|
|
}
|
|
|
|
/*
|
|
* Apply Spur Immunity to Boards that require it.
|
|
* Applies only to OFDM RX operation.
|
|
*/
|
|
|
|
void
|
|
ar5212SetSpurMitigation(struct ath_hal *ah,
|
|
const struct ieee80211_channel *chan)
|
|
{
|
|
uint32_t pilotMask[2] = {0, 0}, binMagMask[4] = {0, 0, 0 , 0};
|
|
uint16_t i, finalSpur, curChanAsSpur, binWidth = 0, spurDetectWidth, spurChan;
|
|
int32_t spurDeltaPhase = 0, spurFreqSd = 0, spurOffset, binOffsetNumT16, curBinOffset;
|
|
int16_t numBinOffsets;
|
|
static const uint16_t magMapFor4[4] = {1, 2, 2, 1};
|
|
static const uint16_t magMapFor3[3] = {1, 2, 1};
|
|
const uint16_t *pMagMap;
|
|
HAL_BOOL is2GHz = IEEE80211_IS_CHAN_2GHZ(chan);
|
|
HAL_CHANNEL_INTERNAL *ichan = ath_hal_checkchannel(ah, chan);
|
|
uint32_t val;
|
|
|
|
#define CHAN_TO_SPUR(_f, _freq) ( ((_freq) - ((_f) ? 2300 : 4900)) * 10 )
|
|
if (IS_2417(ah)) {
|
|
HALDEBUG(ah, HAL_DEBUG_RFPARAM, "%s: no spur mitigation\n",
|
|
__func__);
|
|
return;
|
|
}
|
|
|
|
curChanAsSpur = CHAN_TO_SPUR(is2GHz, ichan->channel);
|
|
|
|
if (ichan->mainSpur) {
|
|
/* Pull out the saved spur value */
|
|
finalSpur = ichan->mainSpur;
|
|
} else {
|
|
/*
|
|
* Check if spur immunity should be performed for this channel
|
|
* Should only be performed once per channel and then saved
|
|
*/
|
|
finalSpur = AR_NO_SPUR;
|
|
spurDetectWidth = HAL_SPUR_CHAN_WIDTH;
|
|
if (IEEE80211_IS_CHAN_TURBO(chan))
|
|
spurDetectWidth *= 2;
|
|
|
|
/* Decide if any spur affects the current channel */
|
|
for (i = 0; i < AR_EEPROM_MODAL_SPURS; i++) {
|
|
spurChan = ath_hal_getSpurChan(ah, i, is2GHz);
|
|
if (spurChan == AR_NO_SPUR) {
|
|
break;
|
|
}
|
|
if ((curChanAsSpur - spurDetectWidth <= (spurChan & HAL_SPUR_VAL_MASK)) &&
|
|
(curChanAsSpur + spurDetectWidth >= (spurChan & HAL_SPUR_VAL_MASK))) {
|
|
finalSpur = spurChan & HAL_SPUR_VAL_MASK;
|
|
break;
|
|
}
|
|
}
|
|
/* Save detected spur (or no spur) for this channel */
|
|
ichan->mainSpur = finalSpur;
|
|
}
|
|
|
|
/* Write spur immunity data */
|
|
if (finalSpur == AR_NO_SPUR) {
|
|
/* Disable Spur Immunity Regs if they appear set */
|
|
if (OS_REG_READ(ah, AR_PHY_TIMING_CTRL4) & AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER) {
|
|
/* Clear Spur Delta Phase, Spur Freq, and enable bits */
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_MASK_CTL, AR_PHY_MASK_CTL_RATE, 0);
|
|
val = OS_REG_READ(ah, AR_PHY_TIMING_CTRL4);
|
|
val &= ~(AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER |
|
|
AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK |
|
|
AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK);
|
|
OS_REG_WRITE(ah, AR_PHY_MASK_CTL, val);
|
|
OS_REG_WRITE(ah, AR_PHY_TIMING11, 0);
|
|
|
|
/* Clear pilot masks */
|
|
OS_REG_WRITE(ah, AR_PHY_TIMING7, 0);
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_TIMING8, AR_PHY_TIMING8_PILOT_MASK_2, 0);
|
|
OS_REG_WRITE(ah, AR_PHY_TIMING9, 0);
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_TIMING10, AR_PHY_TIMING10_PILOT_MASK_2, 0);
|
|
|
|
/* Clear magnitude masks */
|
|
OS_REG_WRITE(ah, AR_PHY_BIN_MASK_1, 0);
|
|
OS_REG_WRITE(ah, AR_PHY_BIN_MASK_2, 0);
|
|
OS_REG_WRITE(ah, AR_PHY_BIN_MASK_3, 0);
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_MASK_CTL, AR_PHY_MASK_CTL_MASK_4, 0);
|
|
OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_1, 0);
|
|
OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_2, 0);
|
|
OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_3, 0);
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_BIN_MASK2_4, AR_PHY_BIN_MASK2_4_MASK_4, 0);
|
|
}
|
|
} else {
|
|
spurOffset = finalSpur - curChanAsSpur;
|
|
/*
|
|
* Spur calculations:
|
|
* spurDeltaPhase is (spurOffsetIn100KHz / chipFrequencyIn100KHz) << 21
|
|
* spurFreqSd is (spurOffsetIn100KHz / sampleFrequencyIn100KHz) << 11
|
|
*/
|
|
if (IEEE80211_IS_CHAN_TURBO(chan)) {
|
|
/* Chip Frequency & sampleFrequency are 80 MHz */
|
|
spurDeltaPhase = (spurOffset << 16) / 25;
|
|
spurFreqSd = spurDeltaPhase >> 10;
|
|
binWidth = HAL_BIN_WIDTH_TURBO_100HZ;
|
|
} else if (IEEE80211_IS_CHAN_G(chan)) {
|
|
/* Chip Frequency is 44MHz, sampleFrequency is 40 MHz */
|
|
spurFreqSd = (spurOffset << 8) / 55;
|
|
spurDeltaPhase = (spurOffset << 17) / 25;
|
|
binWidth = HAL_BIN_WIDTH_BASE_100HZ;
|
|
} else {
|
|
HALASSERT(!IEEE80211_IS_CHAN_B(chan));
|
|
/* Chip Frequency & sampleFrequency are 40 MHz */
|
|
spurDeltaPhase = (spurOffset << 17) / 25;
|
|
spurFreqSd = spurDeltaPhase >> 10;
|
|
binWidth = HAL_BIN_WIDTH_BASE_100HZ;
|
|
}
|
|
|
|
/* Compute Pilot Mask */
|
|
binOffsetNumT16 = ((spurOffset * 1000) << 4) / binWidth;
|
|
/* The spur is on a bin if it's remainder at times 16 is 0 */
|
|
if (binOffsetNumT16 & 0xF) {
|
|
numBinOffsets = 4;
|
|
pMagMap = magMapFor4;
|
|
} else {
|
|
numBinOffsets = 3;
|
|
pMagMap = magMapFor3;
|
|
}
|
|
for (i = 0; i < numBinOffsets; i++) {
|
|
if ((binOffsetNumT16 >> 4) > HAL_MAX_BINS_ALLOWED) {
|
|
HALDEBUG(ah, HAL_DEBUG_ANY,
|
|
"Too man bins in spur mitigation\n");
|
|
return;
|
|
}
|
|
|
|
/* Get Pilot Mask values */
|
|
curBinOffset = (binOffsetNumT16 >> 4) + i + 25;
|
|
if ((curBinOffset >= 0) && (curBinOffset <= 32)) {
|
|
if (curBinOffset <= 25)
|
|
pilotMask[0] |= 1 << curBinOffset;
|
|
else if (curBinOffset >= 27)
|
|
pilotMask[0] |= 1 << (curBinOffset - 1);
|
|
} else if ((curBinOffset >= 33) && (curBinOffset <= 52))
|
|
pilotMask[1] |= 1 << (curBinOffset - 33);
|
|
|
|
/* Get viterbi values */
|
|
if ((curBinOffset >= -1) && (curBinOffset <= 14))
|
|
binMagMask[0] |= pMagMap[i] << (curBinOffset + 1) * 2;
|
|
else if ((curBinOffset >= 15) && (curBinOffset <= 30))
|
|
binMagMask[1] |= pMagMap[i] << (curBinOffset - 15) * 2;
|
|
else if ((curBinOffset >= 31) && (curBinOffset <= 46))
|
|
binMagMask[2] |= pMagMap[i] << (curBinOffset -31) * 2;
|
|
else if((curBinOffset >= 47) && (curBinOffset <= 53))
|
|
binMagMask[3] |= pMagMap[i] << (curBinOffset -47) * 2;
|
|
}
|
|
|
|
/* Write Spur Delta Phase, Spur Freq, and enable bits */
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_MASK_CTL, AR_PHY_MASK_CTL_RATE, 0xFF);
|
|
val = OS_REG_READ(ah, AR_PHY_TIMING_CTRL4);
|
|
val |= (AR_PHY_TIMING_CTRL4_ENABLE_SPUR_FILTER |
|
|
AR_PHY_TIMING_CTRL4_ENABLE_CHAN_MASK |
|
|
AR_PHY_TIMING_CTRL4_ENABLE_PILOT_MASK);
|
|
OS_REG_WRITE(ah, AR_PHY_TIMING_CTRL4, val);
|
|
OS_REG_WRITE(ah, AR_PHY_TIMING11, AR_PHY_TIMING11_USE_SPUR_IN_AGC |
|
|
SM(spurFreqSd, AR_PHY_TIMING11_SPUR_FREQ_SD) |
|
|
SM(spurDeltaPhase, AR_PHY_TIMING11_SPUR_DELTA_PHASE));
|
|
|
|
/* Write pilot masks */
|
|
OS_REG_WRITE(ah, AR_PHY_TIMING7, pilotMask[0]);
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_TIMING8, AR_PHY_TIMING8_PILOT_MASK_2, pilotMask[1]);
|
|
OS_REG_WRITE(ah, AR_PHY_TIMING9, pilotMask[0]);
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_TIMING10, AR_PHY_TIMING10_PILOT_MASK_2, pilotMask[1]);
|
|
|
|
/* Write magnitude masks */
|
|
OS_REG_WRITE(ah, AR_PHY_BIN_MASK_1, binMagMask[0]);
|
|
OS_REG_WRITE(ah, AR_PHY_BIN_MASK_2, binMagMask[1]);
|
|
OS_REG_WRITE(ah, AR_PHY_BIN_MASK_3, binMagMask[2]);
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_MASK_CTL, AR_PHY_MASK_CTL_MASK_4, binMagMask[3]);
|
|
OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_1, binMagMask[0]);
|
|
OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_2, binMagMask[1]);
|
|
OS_REG_WRITE(ah, AR_PHY_BIN_MASK2_3, binMagMask[2]);
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_BIN_MASK2_4, AR_PHY_BIN_MASK2_4_MASK_4, binMagMask[3]);
|
|
}
|
|
#undef CHAN_TO_SPUR
|
|
}
|
|
|
|
|
|
/*
|
|
* Delta slope coefficient computation.
|
|
* Required for OFDM operation.
|
|
*/
|
|
void
|
|
ar5212SetDeltaSlope(struct ath_hal *ah, const struct ieee80211_channel *chan)
|
|
{
|
|
#define COEF_SCALE_S 24
|
|
#define INIT_CLOCKMHZSCALED 0x64000000
|
|
uint16_t freq = ath_hal_gethwchannel(ah, chan);
|
|
unsigned long coef_scaled, coef_exp, coef_man, ds_coef_exp, ds_coef_man;
|
|
unsigned long clockMhzScaled = INIT_CLOCKMHZSCALED;
|
|
|
|
if (IEEE80211_IS_CHAN_TURBO(chan))
|
|
clockMhzScaled *= 2;
|
|
/* half and quarter rate can divide the scaled clock by 2 or 4 respectively */
|
|
/* scale for selected channel bandwidth */
|
|
if (IEEE80211_IS_CHAN_HALF(chan)) {
|
|
clockMhzScaled = clockMhzScaled >> 1;
|
|
} else if (IEEE80211_IS_CHAN_QUARTER(chan)) {
|
|
clockMhzScaled = clockMhzScaled >> 2;
|
|
}
|
|
|
|
/*
|
|
* ALGO -> coef = 1e8/fcarrier*fclock/40;
|
|
* scaled coef to provide precision for this floating calculation
|
|
*/
|
|
coef_scaled = clockMhzScaled / freq;
|
|
|
|
/*
|
|
* ALGO -> coef_exp = 14-floor(log2(coef));
|
|
* floor(log2(x)) is the highest set bit position
|
|
*/
|
|
for (coef_exp = 31; coef_exp > 0; coef_exp--)
|
|
if ((coef_scaled >> coef_exp) & 0x1)
|
|
break;
|
|
/* A coef_exp of 0 is a legal bit position but an unexpected coef_exp */
|
|
HALASSERT(coef_exp);
|
|
coef_exp = 14 - (coef_exp - COEF_SCALE_S);
|
|
|
|
/*
|
|
* ALGO -> coef_man = floor(coef* 2^coef_exp+0.5);
|
|
* The coefficient is already shifted up for scaling
|
|
*/
|
|
coef_man = coef_scaled + (1 << (COEF_SCALE_S - coef_exp - 1));
|
|
ds_coef_man = coef_man >> (COEF_SCALE_S - coef_exp);
|
|
ds_coef_exp = coef_exp - 16;
|
|
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_TIMING3,
|
|
AR_PHY_TIMING3_DSC_MAN, ds_coef_man);
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_TIMING3,
|
|
AR_PHY_TIMING3_DSC_EXP, ds_coef_exp);
|
|
#undef INIT_CLOCKMHZSCALED
|
|
#undef COEF_SCALE_S
|
|
}
|
|
|
|
/*
|
|
* Set a limit on the overall output power. Used for dynamic
|
|
* transmit power control and the like.
|
|
*
|
|
* NB: limit is in units of 0.5 dbM.
|
|
*/
|
|
HAL_BOOL
|
|
ar5212SetTxPowerLimit(struct ath_hal *ah, uint32_t limit)
|
|
{
|
|
/* XXX blech, construct local writable copy */
|
|
struct ieee80211_channel dummy = *AH_PRIVATE(ah)->ah_curchan;
|
|
uint16_t dummyXpdGains[2];
|
|
HAL_BOOL isBmode;
|
|
|
|
SAVE_CCK(ah, &dummy, isBmode);
|
|
AH_PRIVATE(ah)->ah_powerLimit = AH_MIN(limit, MAX_RATE_POWER);
|
|
return ar5212SetTransmitPower(ah, &dummy, dummyXpdGains);
|
|
}
|
|
|
|
/*
|
|
* Set the transmit power in the baseband for the given
|
|
* operating channel and mode.
|
|
*/
|
|
HAL_BOOL
|
|
ar5212SetTransmitPower(struct ath_hal *ah,
|
|
const struct ieee80211_channel *chan, uint16_t *rfXpdGain)
|
|
{
|
|
#define POW_OFDM(_r, _s) (((0 & 1)<< ((_s)+6)) | (((_r) & 0x3f) << (_s)))
|
|
#define POW_CCK(_r, _s) (((_r) & 0x3f) << (_s))
|
|
#define N(a) (sizeof (a) / sizeof (a[0]))
|
|
static const uint16_t tpcScaleReductionTable[5] =
|
|
{ 0, 3, 6, 9, MAX_RATE_POWER };
|
|
struct ath_hal_5212 *ahp = AH5212(ah);
|
|
uint16_t freq = ath_hal_gethwchannel(ah, chan);
|
|
const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;
|
|
int16_t minPower, maxPower, tpcInDb, powerLimit;
|
|
int i;
|
|
|
|
HALASSERT(ah->ah_magic == AR5212_MAGIC);
|
|
|
|
OS_MEMZERO(ahp->ah_pcdacTable, ahp->ah_pcdacTableSize);
|
|
OS_MEMZERO(ahp->ah_ratesArray, sizeof(ahp->ah_ratesArray));
|
|
|
|
powerLimit = AH_MIN(MAX_RATE_POWER, AH_PRIVATE(ah)->ah_powerLimit);
|
|
if (powerLimit >= MAX_RATE_POWER || powerLimit == 0)
|
|
tpcInDb = tpcScaleReductionTable[AH_PRIVATE(ah)->ah_tpScale];
|
|
else
|
|
tpcInDb = 0;
|
|
if (!ar5212SetRateTable(ah, chan, tpcInDb, powerLimit,
|
|
AH_TRUE, &minPower, &maxPower)) {
|
|
HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unable to set rate table\n",
|
|
__func__);
|
|
return AH_FALSE;
|
|
}
|
|
if (!ahp->ah_rfHal->setPowerTable(ah,
|
|
&minPower, &maxPower, chan, rfXpdGain)) {
|
|
HALDEBUG(ah, HAL_DEBUG_ANY, "%s: unable to set power table\n",
|
|
__func__);
|
|
return AH_FALSE;
|
|
}
|
|
|
|
/*
|
|
* Adjust XR power/rate up by 2 dB to account for greater peak
|
|
* to avg ratio - except in newer avg power designs
|
|
*/
|
|
if (!IS_2413(ah) && !IS_5413(ah))
|
|
ahp->ah_ratesArray[15] += 4;
|
|
/*
|
|
* txPowerIndexOffset is set by the SetPowerTable() call -
|
|
* adjust the rate table
|
|
*/
|
|
for (i = 0; i < N(ahp->ah_ratesArray); i++) {
|
|
ahp->ah_ratesArray[i] += ahp->ah_txPowerIndexOffset;
|
|
if (ahp->ah_ratesArray[i] > 63)
|
|
ahp->ah_ratesArray[i] = 63;
|
|
}
|
|
|
|
if (ee->ee_eepMap < 2) {
|
|
/*
|
|
* Correct gain deltas for 5212 G operation -
|
|
* Removed with revised chipset
|
|
*/
|
|
if (AH_PRIVATE(ah)->ah_phyRev < AR_PHY_CHIP_ID_REV_2 &&
|
|
IEEE80211_IS_CHAN_G(chan)) {
|
|
uint16_t cckOfdmPwrDelta;
|
|
|
|
if (freq == 2484)
|
|
cckOfdmPwrDelta = SCALE_OC_DELTA(
|
|
ee->ee_cckOfdmPwrDelta -
|
|
ee->ee_scaledCh14FilterCckDelta);
|
|
else
|
|
cckOfdmPwrDelta = SCALE_OC_DELTA(
|
|
ee->ee_cckOfdmPwrDelta);
|
|
ar5212CorrectGainDelta(ah, cckOfdmPwrDelta);
|
|
}
|
|
/*
|
|
* Finally, write the power values into the
|
|
* baseband power table
|
|
*/
|
|
for (i = 0; i < (PWR_TABLE_SIZE/2); i++) {
|
|
OS_REG_WRITE(ah, AR_PHY_PCDAC_TX_POWER(i),
|
|
((((ahp->ah_pcdacTable[2*i + 1] << 8) | 0xff) & 0xffff) << 16)
|
|
| (((ahp->ah_pcdacTable[2*i] << 8) | 0xff) & 0xffff)
|
|
);
|
|
}
|
|
}
|
|
|
|
/* Write the OFDM power per rate set */
|
|
OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE1,
|
|
POW_OFDM(ahp->ah_ratesArray[3], 24)
|
|
| POW_OFDM(ahp->ah_ratesArray[2], 16)
|
|
| POW_OFDM(ahp->ah_ratesArray[1], 8)
|
|
| POW_OFDM(ahp->ah_ratesArray[0], 0)
|
|
);
|
|
OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE2,
|
|
POW_OFDM(ahp->ah_ratesArray[7], 24)
|
|
| POW_OFDM(ahp->ah_ratesArray[6], 16)
|
|
| POW_OFDM(ahp->ah_ratesArray[5], 8)
|
|
| POW_OFDM(ahp->ah_ratesArray[4], 0)
|
|
);
|
|
|
|
/* Write the CCK power per rate set */
|
|
OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE3,
|
|
POW_CCK(ahp->ah_ratesArray[10], 24)
|
|
| POW_CCK(ahp->ah_ratesArray[9], 16)
|
|
| POW_CCK(ahp->ah_ratesArray[15], 8) /* XR target power */
|
|
| POW_CCK(ahp->ah_ratesArray[8], 0)
|
|
);
|
|
OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE4,
|
|
POW_CCK(ahp->ah_ratesArray[14], 24)
|
|
| POW_CCK(ahp->ah_ratesArray[13], 16)
|
|
| POW_CCK(ahp->ah_ratesArray[12], 8)
|
|
| POW_CCK(ahp->ah_ratesArray[11], 0)
|
|
);
|
|
|
|
/*
|
|
* Set max power to 30 dBm and, optionally,
|
|
* enable TPC in tx descriptors.
|
|
*/
|
|
OS_REG_WRITE(ah, AR_PHY_POWER_TX_RATE_MAX, MAX_RATE_POWER |
|
|
(ahp->ah_tpcEnabled ? AR_PHY_POWER_TX_RATE_MAX_TPC_ENABLE : 0));
|
|
|
|
return AH_TRUE;
|
|
#undef N
|
|
#undef POW_CCK
|
|
#undef POW_OFDM
|
|
}
|
|
|
|
/*
|
|
* Sets the transmit power in the baseband for the given
|
|
* operating channel and mode.
|
|
*/
|
|
static HAL_BOOL
|
|
ar5212SetRateTable(struct ath_hal *ah, const struct ieee80211_channel *chan,
|
|
int16_t tpcScaleReduction, int16_t powerLimit, HAL_BOOL commit,
|
|
int16_t *pMinPower, int16_t *pMaxPower)
|
|
{
|
|
struct ath_hal_5212 *ahp = AH5212(ah);
|
|
uint16_t freq = ath_hal_gethwchannel(ah, chan);
|
|
const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;
|
|
uint16_t *rpow = ahp->ah_ratesArray;
|
|
uint16_t twiceMaxEdgePower = MAX_RATE_POWER;
|
|
uint16_t twiceMaxEdgePowerCck = MAX_RATE_POWER;
|
|
uint16_t twiceMaxRDPower = MAX_RATE_POWER;
|
|
int i;
|
|
uint8_t cfgCtl;
|
|
int8_t twiceAntennaGain, twiceAntennaReduction;
|
|
const RD_EDGES_POWER *rep;
|
|
TRGT_POWER_INFO targetPowerOfdm, targetPowerCck;
|
|
int16_t scaledPower, maxAvailPower = 0;
|
|
int16_t r13, r9, r7, r0;
|
|
|
|
HALASSERT(ah->ah_magic == AR5212_MAGIC);
|
|
|
|
twiceMaxRDPower = chan->ic_maxregpower * 2;
|
|
*pMaxPower = -MAX_RATE_POWER;
|
|
*pMinPower = MAX_RATE_POWER;
|
|
|
|
/* Get conformance test limit maximum for this channel */
|
|
cfgCtl = ath_hal_getctl(ah, chan);
|
|
for (i = 0; i < ee->ee_numCtls; i++) {
|
|
uint16_t twiceMinEdgePower;
|
|
|
|
if (ee->ee_ctl[i] == 0)
|
|
continue;
|
|
if (ee->ee_ctl[i] == cfgCtl ||
|
|
cfgCtl == ((ee->ee_ctl[i] & CTL_MODE_M) | SD_NO_CTL)) {
|
|
rep = &ee->ee_rdEdgesPower[i * NUM_EDGES];
|
|
twiceMinEdgePower = ar5212GetMaxEdgePower(freq, rep);
|
|
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 {
|
|
twiceMaxEdgePower = twiceMinEdgePower;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (IEEE80211_IS_CHAN_G(chan)) {
|
|
/* Check for a CCK CTL for 11G CCK powers */
|
|
cfgCtl = (cfgCtl & ~CTL_MODE_M) | CTL_11B;
|
|
for (i = 0; i < ee->ee_numCtls; i++) {
|
|
uint16_t twiceMinEdgePowerCck;
|
|
|
|
if (ee->ee_ctl[i] == 0)
|
|
continue;
|
|
if (ee->ee_ctl[i] == cfgCtl ||
|
|
cfgCtl == ((ee->ee_ctl[i] & CTL_MODE_M) | SD_NO_CTL)) {
|
|
rep = &ee->ee_rdEdgesPower[i * NUM_EDGES];
|
|
twiceMinEdgePowerCck = ar5212GetMaxEdgePower(freq, rep);
|
|
if ((cfgCtl & ~CTL_MODE_M) == SD_NO_CTL) {
|
|
/* Find the minimum of all CTL edge powers that apply to this channel */
|
|
twiceMaxEdgePowerCck = AH_MIN(twiceMaxEdgePowerCck, twiceMinEdgePowerCck);
|
|
} else {
|
|
twiceMaxEdgePowerCck = twiceMinEdgePowerCck;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
/* Set the 11B cck edge power to the one found before */
|
|
twiceMaxEdgePowerCck = twiceMaxEdgePower;
|
|
}
|
|
|
|
/* Get Antenna Gain reduction */
|
|
if (IEEE80211_IS_CHAN_5GHZ(chan)) {
|
|
ath_hal_eepromGet(ah, AR_EEP_ANTGAINMAX_5, &twiceAntennaGain);
|
|
} else {
|
|
ath_hal_eepromGet(ah, AR_EEP_ANTGAINMAX_2, &twiceAntennaGain);
|
|
}
|
|
twiceAntennaReduction =
|
|
ath_hal_getantennareduction(ah, chan, twiceAntennaGain);
|
|
|
|
if (IEEE80211_IS_CHAN_OFDM(chan)) {
|
|
/* Get final OFDM target powers */
|
|
if (IEEE80211_IS_CHAN_2GHZ(chan)) {
|
|
ar5212GetTargetPowers(ah, chan, ee->ee_trgtPwr_11g,
|
|
ee->ee_numTargetPwr_11g, &targetPowerOfdm);
|
|
} else {
|
|
ar5212GetTargetPowers(ah, chan, ee->ee_trgtPwr_11a,
|
|
ee->ee_numTargetPwr_11a, &targetPowerOfdm);
|
|
}
|
|
|
|
/* Get Maximum OFDM power */
|
|
/* Minimum of target and edge powers */
|
|
scaledPower = AH_MIN(twiceMaxEdgePower,
|
|
twiceMaxRDPower - twiceAntennaReduction);
|
|
|
|
/*
|
|
* If turbo is set, reduce power to keep power
|
|
* consumption under 2 Watts. Note that we always do
|
|
* this unless specially configured. Then we limit
|
|
* power only for non-AP operation.
|
|
*/
|
|
if (IEEE80211_IS_CHAN_TURBO(chan)
|
|
#ifdef AH_ENABLE_AP_SUPPORT
|
|
&& AH_PRIVATE(ah)->ah_opmode != HAL_M_HOSTAP
|
|
#endif
|
|
) {
|
|
/*
|
|
* If turbo is set, reduce power to keep power
|
|
* consumption under 2 Watts
|
|
*/
|
|
if (ee->ee_version >= AR_EEPROM_VER3_1)
|
|
scaledPower = AH_MIN(scaledPower,
|
|
ee->ee_turbo2WMaxPower5);
|
|
/*
|
|
* EEPROM version 4.0 added an additional
|
|
* constraint on 2.4GHz channels.
|
|
*/
|
|
if (ee->ee_version >= AR_EEPROM_VER4_0 &&
|
|
IEEE80211_IS_CHAN_2GHZ(chan))
|
|
scaledPower = AH_MIN(scaledPower,
|
|
ee->ee_turbo2WMaxPower2);
|
|
}
|
|
|
|
maxAvailPower = AH_MIN(scaledPower,
|
|
targetPowerOfdm.twicePwr6_24);
|
|
|
|
/* Reduce power by max regulatory domain allowed restrictions */
|
|
scaledPower = maxAvailPower - (tpcScaleReduction * 2);
|
|
scaledPower = (scaledPower < 0) ? 0 : scaledPower;
|
|
scaledPower = AH_MIN(scaledPower, powerLimit);
|
|
|
|
if (commit) {
|
|
/* Set OFDM rates 9, 12, 18, 24 */
|
|
r0 = rpow[0] = rpow[1] = rpow[2] = rpow[3] = rpow[4] = scaledPower;
|
|
|
|
/* Set OFDM rates 36, 48, 54, XR */
|
|
rpow[5] = AH_MIN(rpow[0], targetPowerOfdm.twicePwr36);
|
|
rpow[6] = AH_MIN(rpow[0], targetPowerOfdm.twicePwr48);
|
|
r7 = rpow[7] = AH_MIN(rpow[0], targetPowerOfdm.twicePwr54);
|
|
|
|
if (ee->ee_version >= AR_EEPROM_VER4_0) {
|
|
/* Setup XR target power from EEPROM */
|
|
rpow[15] = AH_MIN(scaledPower, IEEE80211_IS_CHAN_2GHZ(chan) ?
|
|
ee->ee_xrTargetPower2 : ee->ee_xrTargetPower5);
|
|
} else {
|
|
/* XR uses 6mb power */
|
|
rpow[15] = rpow[0];
|
|
}
|
|
ahp->ah_ofdmTxPower = *pMaxPower;
|
|
|
|
} else {
|
|
r0 = scaledPower;
|
|
r7 = AH_MIN(r0, targetPowerOfdm.twicePwr54);
|
|
}
|
|
*pMinPower = r7;
|
|
*pMaxPower = r0;
|
|
|
|
HALDEBUG(ah, HAL_DEBUG_RFPARAM,
|
|
"%s: MaxRD: %d TurboMax: %d MaxCTL: %d "
|
|
"TPC_Reduction %d chan=%d (0x%x) maxAvailPower=%d pwr6_24=%d, maxPower=%d\n",
|
|
__func__, twiceMaxRDPower, ee->ee_turbo2WMaxPower5,
|
|
twiceMaxEdgePower, tpcScaleReduction * 2,
|
|
chan->ic_freq, chan->ic_flags,
|
|
maxAvailPower, targetPowerOfdm.twicePwr6_24, *pMaxPower);
|
|
}
|
|
|
|
if (IEEE80211_IS_CHAN_CCK(chan)) {
|
|
/* Get final CCK target powers */
|
|
ar5212GetTargetPowers(ah, chan, ee->ee_trgtPwr_11b,
|
|
ee->ee_numTargetPwr_11b, &targetPowerCck);
|
|
|
|
/* Reduce power by max regulatory domain allowed restrictions */
|
|
scaledPower = AH_MIN(twiceMaxEdgePowerCck,
|
|
twiceMaxRDPower - twiceAntennaReduction);
|
|
if (maxAvailPower < AH_MIN(scaledPower, targetPowerCck.twicePwr6_24))
|
|
maxAvailPower = AH_MIN(scaledPower, targetPowerCck.twicePwr6_24);
|
|
|
|
/* Reduce power by user selection */
|
|
scaledPower = AH_MIN(scaledPower, targetPowerCck.twicePwr6_24) - (tpcScaleReduction * 2);
|
|
scaledPower = (scaledPower < 0) ? 0 : scaledPower;
|
|
scaledPower = AH_MIN(scaledPower, powerLimit);
|
|
|
|
if (commit) {
|
|
/* Set CCK rates 2L, 2S, 5.5L, 5.5S, 11L, 11S */
|
|
rpow[8] = AH_MIN(scaledPower, targetPowerCck.twicePwr6_24);
|
|
r9 = rpow[9] = AH_MIN(scaledPower, targetPowerCck.twicePwr36);
|
|
rpow[10] = rpow[9];
|
|
rpow[11] = AH_MIN(scaledPower, targetPowerCck.twicePwr48);
|
|
rpow[12] = rpow[11];
|
|
r13 = rpow[13] = AH_MIN(scaledPower, targetPowerCck.twicePwr54);
|
|
rpow[14] = rpow[13];
|
|
} else {
|
|
r9 = AH_MIN(scaledPower, targetPowerCck.twicePwr36);
|
|
r13 = AH_MIN(scaledPower, targetPowerCck.twicePwr54);
|
|
}
|
|
|
|
/* Set min/max power based off OFDM values or initialization */
|
|
if (r13 < *pMinPower)
|
|
*pMinPower = r13;
|
|
if (r9 > *pMaxPower)
|
|
*pMaxPower = r9;
|
|
|
|
HALDEBUG(ah, HAL_DEBUG_RFPARAM,
|
|
"%s: cck: MaxRD: %d MaxCTL: %d "
|
|
"TPC_Reduction %d chan=%d (0x%x) maxAvailPower=%d pwr6_24=%d, maxPower=%d\n",
|
|
__func__, twiceMaxRDPower, twiceMaxEdgePowerCck,
|
|
tpcScaleReduction * 2, chan->ic_freq, chan->ic_flags,
|
|
maxAvailPower, targetPowerCck.twicePwr6_24, *pMaxPower);
|
|
}
|
|
if (commit) {
|
|
ahp->ah_tx6PowerInHalfDbm = *pMaxPower;
|
|
AH_PRIVATE(ah)->ah_maxPowerLevel = ahp->ah_tx6PowerInHalfDbm;
|
|
}
|
|
return AH_TRUE;
|
|
}
|
|
|
|
HAL_BOOL
|
|
ar5212GetChipPowerLimits(struct ath_hal *ah, struct ieee80211_channel *chan)
|
|
{
|
|
struct ath_hal_5212 *ahp = AH5212(ah);
|
|
#if 0
|
|
static const uint16_t tpcScaleReductionTable[5] =
|
|
{ 0, 3, 6, 9, MAX_RATE_POWER };
|
|
int16_t tpcInDb, powerLimit;
|
|
#endif
|
|
int16_t minPower, maxPower;
|
|
|
|
/*
|
|
* Get Pier table max and min powers.
|
|
*/
|
|
if (ahp->ah_rfHal->getChannelMaxMinPower(ah, chan, &maxPower, &minPower)) {
|
|
/* NB: rf code returns 1/4 dBm units, convert */
|
|
chan->ic_maxpower = maxPower / 2;
|
|
chan->ic_minpower = minPower / 2;
|
|
} else {
|
|
HALDEBUG(ah, HAL_DEBUG_ANY,
|
|
"%s: no min/max power for %u/0x%x\n",
|
|
__func__, chan->ic_freq, chan->ic_flags);
|
|
chan->ic_maxpower = MAX_RATE_POWER;
|
|
chan->ic_minpower = 0;
|
|
}
|
|
#if 0
|
|
/*
|
|
* Now adjust to reflect any global scale and/or CTL's.
|
|
* (XXX is that correct?)
|
|
*/
|
|
powerLimit = AH_MIN(MAX_RATE_POWER, AH_PRIVATE(ah)->ah_powerLimit);
|
|
if (powerLimit >= MAX_RATE_POWER || powerLimit == 0)
|
|
tpcInDb = tpcScaleReductionTable[AH_PRIVATE(ah)->ah_tpScale];
|
|
else
|
|
tpcInDb = 0;
|
|
if (!ar5212SetRateTable(ah, chan, tpcInDb, powerLimit,
|
|
AH_FALSE, &minPower, &maxPower)) {
|
|
HALDEBUG(ah, HAL_DEBUG_ANY,
|
|
"%s: unable to find max/min power\n",__func__);
|
|
return AH_FALSE;
|
|
}
|
|
if (maxPower < chan->ic_maxpower)
|
|
chan->ic_maxpower = maxPower;
|
|
if (minPower < chan->ic_minpower)
|
|
chan->ic_minpower = minPower;
|
|
HALDEBUG(ah, HAL_DEBUG_RESET,
|
|
"Chan %d: MaxPow = %d MinPow = %d\n",
|
|
chan->ic_freq, chan->ic_maxpower, chans->ic_minpower);
|
|
#endif
|
|
return AH_TRUE;
|
|
}
|
|
|
|
/*
|
|
* Correct for the gain-delta between ofdm and cck mode target
|
|
* powers. Write the results to the rate table and the power table.
|
|
*
|
|
* Conventions :
|
|
* 1. rpow[ii] is the integer value of 2*(desired power
|
|
* for the rate ii in dBm) to provide 0.5dB resolution. rate
|
|
* mapping is as following :
|
|
* [0..7] --> ofdm 6, 9, .. 48, 54
|
|
* [8..14] --> cck 1L, 2L, 2S, .. 11L, 11S
|
|
* [15] --> XR (all rates get the same power)
|
|
* 2. powv[ii] is the pcdac corresponding to ii/2 dBm.
|
|
*/
|
|
static void
|
|
ar5212CorrectGainDelta(struct ath_hal *ah, int twiceOfdmCckDelta)
|
|
{
|
|
#define N(_a) (sizeof(_a) / sizeof(_a[0]))
|
|
struct ath_hal_5212 *ahp = AH5212(ah);
|
|
const HAL_EEPROM *ee = AH_PRIVATE(ah)->ah_eeprom;
|
|
int16_t ratesIndex[N(ahp->ah_ratesArray)];
|
|
uint16_t ii, jj, iter;
|
|
int32_t cckIndex;
|
|
int16_t gainDeltaAdjust;
|
|
|
|
HALASSERT(ah->ah_magic == AR5212_MAGIC);
|
|
|
|
gainDeltaAdjust = ee->ee_cckOfdmGainDelta;
|
|
|
|
/* make a local copy of desired powers as initial indices */
|
|
OS_MEMCPY(ratesIndex, ahp->ah_ratesArray, sizeof(ratesIndex));
|
|
|
|
/* fix only the CCK indices */
|
|
for (ii = 8; ii < 15; ii++) {
|
|
/* apply a gain_delta correction of -15 for CCK */
|
|
ratesIndex[ii] -= gainDeltaAdjust;
|
|
|
|
/* Now check for contention with all ofdm target powers */
|
|
jj = 0;
|
|
iter = 0;
|
|
/* indicates not all ofdm rates checked forcontention yet */
|
|
while (jj < 16) {
|
|
if (ratesIndex[ii] < 0)
|
|
ratesIndex[ii] = 0;
|
|
if (jj == 8) { /* skip CCK rates */
|
|
jj = 15;
|
|
continue;
|
|
}
|
|
if (ratesIndex[ii] == ahp->ah_ratesArray[jj]) {
|
|
if (ahp->ah_ratesArray[jj] == 0)
|
|
ratesIndex[ii]++;
|
|
else if (iter > 50) {
|
|
/*
|
|
* To avoid pathological case of of
|
|
* dm target powers 0 and 0.5dBm
|
|
*/
|
|
ratesIndex[ii]++;
|
|
} else
|
|
ratesIndex[ii]--;
|
|
/* check with all rates again */
|
|
jj = 0;
|
|
iter++;
|
|
} else
|
|
jj++;
|
|
}
|
|
if (ratesIndex[ii] >= PWR_TABLE_SIZE)
|
|
ratesIndex[ii] = PWR_TABLE_SIZE -1;
|
|
cckIndex = ahp->ah_ratesArray[ii] - twiceOfdmCckDelta;
|
|
if (cckIndex < 0)
|
|
cckIndex = 0;
|
|
|
|
/*
|
|
* Validate that the indexes for the powv are not
|
|
* out of bounds.
|
|
*/
|
|
HALASSERT(cckIndex < PWR_TABLE_SIZE);
|
|
HALASSERT(ratesIndex[ii] < PWR_TABLE_SIZE);
|
|
ahp->ah_pcdacTable[ratesIndex[ii]] =
|
|
ahp->ah_pcdacTable[cckIndex];
|
|
}
|
|
/* Override rate per power table with new values */
|
|
for (ii = 8; ii < 15; ii++)
|
|
ahp->ah_ratesArray[ii] = ratesIndex[ii];
|
|
#undef N
|
|
}
|
|
|
|
/*
|
|
* Find the maximum conformance test limit for the given channel and CTL info
|
|
*/
|
|
static uint16_t
|
|
ar5212GetMaxEdgePower(uint16_t channel, const RD_EDGES_POWER *pRdEdgesPower)
|
|
{
|
|
/* temp array for holding edge channels */
|
|
uint16_t tempChannelList[NUM_EDGES];
|
|
uint16_t clo, chi, twiceMaxEdgePower;
|
|
int i, numEdges;
|
|
|
|
/* Get the edge power */
|
|
for (i = 0; i < NUM_EDGES; i++) {
|
|
if (pRdEdgesPower[i].rdEdge == 0)
|
|
break;
|
|
tempChannelList[i] = pRdEdgesPower[i].rdEdge;
|
|
}
|
|
numEdges = i;
|
|
|
|
ar5212GetLowerUpperValues(channel, tempChannelList,
|
|
numEdges, &clo, &chi);
|
|
/* Get the index for the lower channel */
|
|
for (i = 0; i < numEdges && clo != tempChannelList[i]; i++)
|
|
;
|
|
/* Is lower channel ever outside the rdEdge? */
|
|
HALASSERT(i != numEdges);
|
|
|
|
if ((clo == chi && clo == channel) || (pRdEdgesPower[i].flag)) {
|
|
/*
|
|
* If there's an exact channel match or an inband flag set
|
|
* on the lower channel use the given rdEdgePower
|
|
*/
|
|
twiceMaxEdgePower = pRdEdgesPower[i].twice_rdEdgePower;
|
|
HALASSERT(twiceMaxEdgePower > 0);
|
|
} else
|
|
twiceMaxEdgePower = MAX_RATE_POWER;
|
|
return twiceMaxEdgePower;
|
|
}
|
|
|
|
/*
|
|
* Returns interpolated or the scaled up interpolated value
|
|
*/
|
|
static uint16_t
|
|
interpolate(uint16_t target, uint16_t srcLeft, uint16_t srcRight,
|
|
uint16_t targetLeft, uint16_t targetRight)
|
|
{
|
|
uint16_t rv;
|
|
int16_t lRatio;
|
|
|
|
/* to get an accurate ratio, always scale, if want to scale, then don't scale back down */
|
|
if ((targetLeft * targetRight) == 0)
|
|
return 0;
|
|
|
|
if (srcRight != srcLeft) {
|
|
/*
|
|
* Note the ratio always need to be scaled,
|
|
* since it will be a fraction.
|
|
*/
|
|
lRatio = (target - srcLeft) * EEP_SCALE / (srcRight - srcLeft);
|
|
if (lRatio < 0) {
|
|
/* Return as Left target if value would be negative */
|
|
rv = targetLeft;
|
|
} else if (lRatio > EEP_SCALE) {
|
|
/* Return as Right target if Ratio is greater than 100% (SCALE) */
|
|
rv = targetRight;
|
|
} else {
|
|
rv = (lRatio * targetRight + (EEP_SCALE - lRatio) *
|
|
targetLeft) / EEP_SCALE;
|
|
}
|
|
} else {
|
|
rv = targetLeft;
|
|
}
|
|
return rv;
|
|
}
|
|
|
|
/*
|
|
* Return the four rates of target power for the given target power table
|
|
* channel, and number of channels
|
|
*/
|
|
static void
|
|
ar5212GetTargetPowers(struct ath_hal *ah, const struct ieee80211_channel *chan,
|
|
const TRGT_POWER_INFO *powInfo,
|
|
uint16_t numChannels, TRGT_POWER_INFO *pNewPower)
|
|
{
|
|
uint16_t freq = ath_hal_gethwchannel(ah, chan);
|
|
/* temp array for holding target power channels */
|
|
uint16_t tempChannelList[NUM_TEST_FREQUENCIES];
|
|
uint16_t clo, chi, ixlo, ixhi;
|
|
int i;
|
|
|
|
/* Copy the target powers into the temp channel list */
|
|
for (i = 0; i < numChannels; i++)
|
|
tempChannelList[i] = powInfo[i].testChannel;
|
|
|
|
ar5212GetLowerUpperValues(freq, tempChannelList,
|
|
numChannels, &clo, &chi);
|
|
|
|
/* Get the indices for the channel */
|
|
ixlo = ixhi = 0;
|
|
for (i = 0; i < numChannels; i++) {
|
|
if (clo == tempChannelList[i]) {
|
|
ixlo = i;
|
|
}
|
|
if (chi == tempChannelList[i]) {
|
|
ixhi = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Get the lower and upper channels, target powers,
|
|
* and interpolate between them.
|
|
*/
|
|
pNewPower->twicePwr6_24 = interpolate(freq, clo, chi,
|
|
powInfo[ixlo].twicePwr6_24, powInfo[ixhi].twicePwr6_24);
|
|
pNewPower->twicePwr36 = interpolate(freq, clo, chi,
|
|
powInfo[ixlo].twicePwr36, powInfo[ixhi].twicePwr36);
|
|
pNewPower->twicePwr48 = interpolate(freq, clo, chi,
|
|
powInfo[ixlo].twicePwr48, powInfo[ixhi].twicePwr48);
|
|
pNewPower->twicePwr54 = interpolate(freq, clo, chi,
|
|
powInfo[ixlo].twicePwr54, powInfo[ixhi].twicePwr54);
|
|
}
|
|
|
|
/*
|
|
* Search a list for a specified value v that is within
|
|
* EEP_DELTA of the search values. Return the closest
|
|
* values in the list above and below the desired value.
|
|
* EEP_DELTA is a factional value; everything is scaled
|
|
* so only integer arithmetic is used.
|
|
*
|
|
* NB: the input list is assumed to be sorted in ascending order
|
|
*/
|
|
void
|
|
ar5212GetLowerUpperValues(uint16_t v, uint16_t *lp, uint16_t listSize,
|
|
uint16_t *vlo, uint16_t *vhi)
|
|
{
|
|
uint32_t target = v * EEP_SCALE;
|
|
uint16_t *ep = lp+listSize;
|
|
|
|
/*
|
|
* Check first and last elements for out-of-bounds conditions.
|
|
*/
|
|
if (target < (uint32_t)(lp[0] * EEP_SCALE - EEP_DELTA)) {
|
|
*vlo = *vhi = lp[0];
|
|
return;
|
|
}
|
|
if (target > (uint32_t)(ep[-1] * EEP_SCALE + EEP_DELTA)) {
|
|
*vlo = *vhi = ep[-1];
|
|
return;
|
|
}
|
|
|
|
/* look for value being near or between 2 values in list */
|
|
for (; lp < ep; lp++) {
|
|
/*
|
|
* If value is close to the current value of the list
|
|
* then target is not between values, it is one of the values
|
|
*/
|
|
if (abs(lp[0] * EEP_SCALE - target) < EEP_DELTA) {
|
|
*vlo = *vhi = lp[0];
|
|
return;
|
|
}
|
|
/*
|
|
* Look for value being between current value and next value
|
|
* if so return these 2 values
|
|
*/
|
|
if (target < (uint32_t)(lp[1] * EEP_SCALE - EEP_DELTA)) {
|
|
*vlo = lp[0];
|
|
*vhi = lp[1];
|
|
return;
|
|
}
|
|
}
|
|
HALASSERT(AH_FALSE); /* should not reach here */
|
|
}
|
|
|
|
/*
|
|
* Perform analog "swizzling" of parameters into their location
|
|
*
|
|
* NB: used by RF backends
|
|
*/
|
|
void
|
|
ar5212ModifyRfBuffer(uint32_t *rfBuf, uint32_t reg32, uint32_t numBits,
|
|
uint32_t firstBit, uint32_t column)
|
|
{
|
|
#define MAX_ANALOG_START 319 /* XXX */
|
|
uint32_t tmp32, mask, arrayEntry, lastBit;
|
|
int32_t bitPosition, bitsLeft;
|
|
|
|
HALASSERT(column <= 3);
|
|
HALASSERT(numBits <= 32);
|
|
HALASSERT(firstBit + numBits <= MAX_ANALOG_START);
|
|
|
|
tmp32 = ath_hal_reverseBits(reg32, numBits);
|
|
arrayEntry = (firstBit - 1) / 8;
|
|
bitPosition = (firstBit - 1) % 8;
|
|
bitsLeft = numBits;
|
|
while (bitsLeft > 0) {
|
|
lastBit = (bitPosition + bitsLeft > 8) ?
|
|
8 : bitPosition + bitsLeft;
|
|
mask = (((1 << lastBit) - 1) ^ ((1 << bitPosition) - 1)) <<
|
|
(column * 8);
|
|
rfBuf[arrayEntry] &= ~mask;
|
|
rfBuf[arrayEntry] |= ((tmp32 << bitPosition) <<
|
|
(column * 8)) & mask;
|
|
bitsLeft -= 8 - bitPosition;
|
|
tmp32 = tmp32 >> (8 - bitPosition);
|
|
bitPosition = 0;
|
|
arrayEntry++;
|
|
}
|
|
#undef MAX_ANALOG_START
|
|
}
|
|
|
|
/*
|
|
* Sets the rate to duration values in MAC - used for multi-
|
|
* rate retry.
|
|
* The rate duration table needs to cover all valid rate codes;
|
|
* the 11g table covers all ofdm rates, while the 11b table
|
|
* covers all cck rates => all valid rates get covered between
|
|
* these two mode's ratetables!
|
|
* But if we're turbo, the ofdm phy is replaced by the turbo phy
|
|
* and cck is not valid with turbo => all rates get covered
|
|
* by the turbo ratetable only
|
|
*/
|
|
void
|
|
ar5212SetRateDurationTable(struct ath_hal *ah,
|
|
const struct ieee80211_channel *chan)
|
|
{
|
|
const HAL_RATE_TABLE *rt;
|
|
int i;
|
|
|
|
/* NB: band doesn't matter for 1/2 and 1/4 rate */
|
|
if (IEEE80211_IS_CHAN_HALF(chan)) {
|
|
rt = ar5212GetRateTable(ah, HAL_MODE_11A_HALF_RATE);
|
|
} else if (IEEE80211_IS_CHAN_QUARTER(chan)) {
|
|
rt = ar5212GetRateTable(ah, HAL_MODE_11A_QUARTER_RATE);
|
|
} else {
|
|
rt = ar5212GetRateTable(ah,
|
|
IEEE80211_IS_CHAN_TURBO(chan) ? HAL_MODE_TURBO : HAL_MODE_11G);
|
|
}
|
|
|
|
for (i = 0; i < rt->rateCount; ++i)
|
|
OS_REG_WRITE(ah,
|
|
AR_RATE_DURATION(rt->info[i].rateCode),
|
|
ath_hal_computetxtime(ah, rt,
|
|
WLAN_CTRL_FRAME_SIZE,
|
|
rt->info[i].controlRate, AH_FALSE));
|
|
if (!IEEE80211_IS_CHAN_TURBO(chan)) {
|
|
/* 11g Table is used to cover the CCK rates. */
|
|
rt = ar5212GetRateTable(ah, HAL_MODE_11G);
|
|
for (i = 0; i < rt->rateCount; ++i) {
|
|
uint32_t reg = AR_RATE_DURATION(rt->info[i].rateCode);
|
|
|
|
if (rt->info[i].phy != IEEE80211_T_CCK)
|
|
continue;
|
|
|
|
OS_REG_WRITE(ah, reg,
|
|
ath_hal_computetxtime(ah, rt,
|
|
WLAN_CTRL_FRAME_SIZE,
|
|
rt->info[i].controlRate, AH_FALSE));
|
|
/* cck rates have short preamble option also */
|
|
if (rt->info[i].shortPreamble) {
|
|
reg += rt->info[i].shortPreamble << 2;
|
|
OS_REG_WRITE(ah, reg,
|
|
ath_hal_computetxtime(ah, rt,
|
|
WLAN_CTRL_FRAME_SIZE,
|
|
rt->info[i].controlRate,
|
|
AH_TRUE));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Adjust various register settings based on half/quarter rate clock setting.
|
|
* This includes: +USEC, TX/RX latency,
|
|
* + IFS params: slot, eifs, misc etc.
|
|
*/
|
|
void
|
|
ar5212SetIFSTiming(struct ath_hal *ah, const struct ieee80211_channel *chan)
|
|
{
|
|
uint32_t txLat, rxLat, usec, slot, refClock, eifs, init_usec;
|
|
|
|
HALASSERT(IEEE80211_IS_CHAN_HALF(chan) ||
|
|
IEEE80211_IS_CHAN_QUARTER(chan));
|
|
|
|
refClock = OS_REG_READ(ah, AR_USEC) & AR_USEC_USEC32;
|
|
if (IEEE80211_IS_CHAN_HALF(chan)) {
|
|
slot = IFS_SLOT_HALF_RATE;
|
|
rxLat = RX_NON_FULL_RATE_LATENCY << AR5212_USEC_RX_LAT_S;
|
|
txLat = TX_HALF_RATE_LATENCY << AR5212_USEC_TX_LAT_S;
|
|
usec = HALF_RATE_USEC;
|
|
eifs = IFS_EIFS_HALF_RATE;
|
|
init_usec = INIT_USEC >> 1;
|
|
} else { /* quarter rate */
|
|
slot = IFS_SLOT_QUARTER_RATE;
|
|
rxLat = RX_NON_FULL_RATE_LATENCY << AR5212_USEC_RX_LAT_S;
|
|
txLat = TX_QUARTER_RATE_LATENCY << AR5212_USEC_TX_LAT_S;
|
|
usec = QUARTER_RATE_USEC;
|
|
eifs = IFS_EIFS_QUARTER_RATE;
|
|
init_usec = INIT_USEC >> 2;
|
|
}
|
|
|
|
OS_REG_WRITE(ah, AR_USEC, (usec | refClock | txLat | rxLat));
|
|
OS_REG_WRITE(ah, AR_D_GBL_IFS_SLOT, slot);
|
|
OS_REG_WRITE(ah, AR_D_GBL_IFS_EIFS, eifs);
|
|
OS_REG_RMW_FIELD(ah, AR_D_GBL_IFS_MISC,
|
|
AR_D_GBL_IFS_MISC_USEC_DURATION, init_usec);
|
|
}
|