4d97934ead
o remove HAL_CHANNEL; convert the hal to use net80211 channels; this mostly involves mechanical changes to variable names and channel attribute macros o gut HAL_CHANNEL_PRIVATE as most of the contents are now redundant with the net80211 channel available o change api for ath_hal_init_channels: no more reglass id's, no more outdoor indication (was a noop), anM contents o add ath_hal_getchannels to have the hal construct a channel list without altering runtime state; this is used to retrieve the calibration list for the device in ath_getradiocaps o add ath_hal_set_channels to take a channel list and regulatory data from above and construct internal state to match (maps frequencies for 900MHz cards, setup for CTL lookups, etc) o compact the private channel table: we keep one private channel per frequency instead of one per HAL_CHANNEL; this gives a big space savings and potentially improves ani and calibration by sharing state (to be seen; didn't see anything in testing); a new config option AH_MAXCHAN controls the table size (default to 96 which was chosen to be ~3x the largest expected size) o shrink ani state and change to mirror private channel table (one entry per frequency indexed by ic_devdata) o move ani state flags to private channel state o remove country codes; use net80211 definitions instead o remove GSM regulatory support; it's no longer needed now that we pass in channel lists from above o consolidate ADHOC_NO_11A attribute with DISALLOW_ADHOC_11A o simplify initial channel list construction based on the EEPROM contents; we preserve country code support for now but may want to just fallback to a WWR sku and dispatch the discovered country code up to user space so the channel list can be constructed using the master regdomain tables o defer to net80211 for max antenna gain o eliminate sorting of internal channel table; now that we use ic_devdata as an index, table lookups are O(1) o remove internal copy of the country code; the public one is sufficient o remove AH_SUPPORT_11D conditional compilation; we always support 11d o remove ath_hal_ispublicsafetysku; not needed any more o remove ath_hal_isgsmsku; no more GSM stuff o move Conformance Test Limit (CTL) state from private channel to a lookup using per-band pointers cached in the private state block o remove regulatory class id support; was unused and belongs in net80211 o fix channel list construction to set IEEE80211_CHAN_NOADHOC, IEEE80211_CHAN_NOHOSTAP, and IEEE80211_CHAN_4MSXMIT o remove private channel flags CHANNEL_DFS and CHANNEL_4MS_LIMIT; these are now set in the constructed net80211 channel o store CHANNEL_NFCREQUIRED (Noise Floor Required) channel attribute in one of the driver-private flag bits of the net80211 channel o move 900MHz frequency mapping into the hal; the mapped frequency is stored in the private channel and used throughout the hal (no more mapping in the driver and/or net80211) o remove ath_hal_mhz2ieee; it's no longer needed as net80211 does the calculation and available in the net80211 channel o change noise floor calibration logic to work with compacted private channel table setup; this may require revisiting as we no longer can distinguish channel attributes (e.g. 11b vs 11g vs turbo) but since the data is used only to calculate status data we can live with it for now o change ah_getChipPowerLimits internal method to operate on a single channel instead of all channels in the private channel table o add ath_hal_gethwchannel to map a net80211 channel to a h/w frequency (always the same except for 900MHz channels) o add HAL_EEBADREG and HAL_EEBADCC status codes to better identify regulatory problems o remove CTRY_DEBUG and CTRY_DEFAULT enum's; these come from net80211 now o change ath_hal_getwirelessmodes to really return wireless modes supported by the hardware (was previously applying regulatory constraints) o return channel interference status with IEEE80211_CHANSTATE_CWINT (should change to a callback so hal api's can take const pointers) o remove some #define's no longer needed with the inclusion of <net80211/_ieee80211.h> Sponsored by: Carlson Wireless
888 lines
27 KiB
C
888 lines
27 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"
|
|
|
|
#ifdef AH_SUPPORT_AR5312
|
|
|
|
#include "ah.h"
|
|
#include "ah_internal.h"
|
|
#include "ah_devid.h"
|
|
|
|
#include "ar5312/ar5312.h"
|
|
#include "ar5312/ar5312reg.h"
|
|
#include "ar5312/ar5312phy.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 */
|
|
|
|
extern int16_t ar5212GetNf(struct ath_hal *, const struct ieee80211_channel *);
|
|
extern void ar5212SetRateDurationTable(struct ath_hal *,
|
|
const struct ieee80211_channel *);
|
|
extern HAL_BOOL ar5212SetTransmitPower(struct ath_hal *ah,
|
|
const struct ieee80211_channel *chan, uint16_t *rfXpdGain);
|
|
extern void ar5212SetDeltaSlope(struct ath_hal *,
|
|
const struct ieee80211_channel *);
|
|
extern HAL_BOOL ar5212SetBoardValues(struct ath_hal *,
|
|
const struct ieee80211_channel *);
|
|
extern void ar5212SetIFSTiming(struct ath_hal *,
|
|
const struct ieee80211_channel *);
|
|
extern HAL_BOOL ar5212IsSpurChannel(struct ath_hal *,
|
|
const struct ieee80211_channel *);
|
|
extern HAL_BOOL ar5212ChannelChange(struct ath_hal *,
|
|
const struct ieee80211_channel *);
|
|
|
|
static HAL_BOOL ar5312SetResetReg(struct ath_hal *, uint32_t resetMask);
|
|
|
|
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 i;
|
|
|
|
/* Write Common Array Parameters */
|
|
for (i = 0; i < ia->rows; i++) {
|
|
uint32_t reg = V(i, 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(i, 1));
|
|
DMA_YIELD(writes);
|
|
}
|
|
}
|
|
return writes;
|
|
#undef IS_NO_RESET_TIMER_ADDR
|
|
#undef V
|
|
}
|
|
|
|
/*
|
|
* 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
|
|
ar5312Reset(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;
|
|
const HAL_EEPROM *ee;
|
|
uint32_t saveFrameSeqCount, saveDefAntenna;
|
|
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 = 0;
|
|
uint32_t testReg;
|
|
uint32_t saveLedState = 0;
|
|
|
|
HALASSERT(ah->ah_magic == AR5212_MAGIC);
|
|
ee = AH_PRIVATE(ah)->ah_eeprom;
|
|
|
|
OS_MARK(ah, AH_MARK_RESET, bChannelChange);
|
|
/*
|
|
* Map public channel to private.
|
|
*/
|
|
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);
|
|
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(ahp->ah_eeversion >= AR_EEPROM_VER3);
|
|
|
|
/* Preserve certain DMA hardware registers on a channel change */
|
|
if (bChannelChange) {
|
|
/*
|
|
* On Venice, the TSF is almost preserved across a reset;
|
|
* it requires the 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 the channel change is across the same mode - perform a fast channel change */
|
|
if ((IS_2413(ah) || IS_5413(ah))) {
|
|
/*
|
|
* 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 - some ugly code for XR
|
|
*/
|
|
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 */
|
|
return AH_TRUE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* 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 */
|
|
if (!IS_5315(ah))
|
|
saveLedState = OS_REG_READ(ah, AR5312_PCICFG) &
|
|
(AR_PCICFG_LEDCTL | AR_PCICFG_LEDMODE | AR_PCICFG_LEDBLINK |
|
|
AR_PCICFG_LEDSLOW);
|
|
|
|
ar5312RestoreClock(ah, opmode); /* move to refclk operation */
|
|
|
|
/*
|
|
* Adjust gain parameters before reset if
|
|
* there's an outstanding gain updated.
|
|
*/
|
|
(void) ar5212GetRfgain(ah);
|
|
|
|
if (!ar5312ChipReset(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;
|
|
modesIndex = IEEE80211_IS_CHAN_108G(chan) ? 5 :
|
|
IEEE80211_IS_CHAN_G(chan) ? 4 : 3;
|
|
} else {
|
|
freqIndex = 1;
|
|
modesIndex = IEEE80211_IS_CHAN_ST(chan) ? 2 : 1;
|
|
}
|
|
|
|
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);
|
|
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);
|
|
|
|
/* 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 (chan->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
|
|
}
|
|
|
|
if (IS_5312_2_X(ah)) {
|
|
/* ADC_CTRL */
|
|
OS_REG_WRITE(ah, AR_PHY_SIGMA_DELTA,
|
|
SM(2, AR_PHY_SIGMA_DELTA_ADC_SEL) |
|
|
SM(4, AR_PHY_SIGMA_DELTA_FILT2) |
|
|
SM(0x16, AR_PHY_SIGMA_DELTA_FILT1) |
|
|
SM(0, AR_PHY_SIGMA_DELTA_ADC_CLIP));
|
|
|
|
if (IEEE80211_IS_CHAN_2GHZ(chan))
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_RXGAIN, AR_PHY_RXGAIN_TXRX_RF_MAX, 0x0F);
|
|
|
|
/* CCK Short parameter adjustment in 11B mode */
|
|
if (IEEE80211_IS_CHAN_B(chan))
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_CCK_RXCTRL4, AR_PHY_CCK_RXCTRL4_FREQ_EST_SHORT, 12);
|
|
|
|
/* Set ADC/DAC select values */
|
|
OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL, 0x04);
|
|
|
|
/* Increase 11A AGC Settling */
|
|
if (IEEE80211_IS_CHAN_A(chan))
|
|
OS_REG_RMW_FIELD(ah, AR_PHY_SETTLING, AR_PHY_SETTLING_AGC, 32);
|
|
} else {
|
|
/* Set ADC/DAC select values */
|
|
OS_REG_WRITE(ah, AR_PHY_SLEEP_SCAL, 0x0e);
|
|
}
|
|
|
|
/* 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 */
|
|
if (!IS_5315(ah))
|
|
OS_REG_WRITE(ah, AR5312_PCICFG, OS_REG_READ(ah, AR_PCICFG) | saveLedState);
|
|
|
|
/* 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 */
|
|
|
|
/* flush SCAL reg */
|
|
if (IS_5312_2_X(ah)) {
|
|
(void) OS_REG_READ(ah, AR_PHY_SLEEP_SCAL);
|
|
}
|
|
|
|
/*
|
|
* 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.
|
|
*/
|
|
ar5312SetupClock(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));
|
|
|
|
/* Restore user-specified settings */
|
|
if (ahp->ah_miscMode != 0)
|
|
OS_REG_WRITE(ah, AR_MISC_MODE, ahp->ah_miscMode);
|
|
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 (ahp->ah_sifstime != (u_int) -1)
|
|
ar5212SetSifsTime(ah, ahp->ah_sifstime);
|
|
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 (bChannelChange && !IEEE80211_IS_CHAN_DFS(chan))
|
|
chan->ic_state &= ~IEEE80211_CHANSTATE_CWINT;
|
|
|
|
HALDEBUG(ah, HAL_DEBUG_RESET, "%s: done\n", __func__);
|
|
|
|
OS_MARK(ah, AH_MARK_RESET_DONE, 0);
|
|
|
|
return AH_TRUE;
|
|
bad:
|
|
OS_MARK(ah, AH_MARK_RESET_DONE, ecode);
|
|
if (status != AH_NULL)
|
|
*status = ecode;
|
|
return AH_FALSE;
|
|
#undef FAIL
|
|
#undef N
|
|
}
|
|
|
|
/*
|
|
* 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
|
|
ar5312PhyDisable(struct ath_hal *ah)
|
|
{
|
|
return ar5312SetResetReg(ah, AR_RC_BB);
|
|
}
|
|
|
|
/*
|
|
* Places all of hardware into reset
|
|
*/
|
|
HAL_BOOL
|
|
ar5312Disable(struct ath_hal *ah)
|
|
{
|
|
if (!ar5312SetPowerMode(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 ar5312SetResetReg(ah, AR_RC_MAC | AR_RC_BB);
|
|
}
|
|
|
|
/*
|
|
* 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
|
|
ar5312ChipReset(struct ath_hal *ah, const struct ieee80211_channel *chan)
|
|
{
|
|
|
|
OS_MARK(ah, AH_MARK_CHIPRESET, chan ? chan->ic_freq : 0);
|
|
|
|
/*
|
|
* Reset the HW
|
|
*/
|
|
if (!ar5312SetResetReg(ah, AR_RC_MAC | AR_RC_BB)) {
|
|
HALDEBUG(ah, HAL_DEBUG_ANY, "%s: ar5312SetResetReg failed\n",
|
|
__func__);
|
|
return AH_FALSE;
|
|
}
|
|
|
|
/* Bring out of sleep mode (AGAIN) */
|
|
if (!ar5312SetPowerMode(ah, HAL_PM_AWAKE, AH_TRUE)) {
|
|
HALDEBUG(ah, HAL_DEBUG_ANY, "%s: ar5312SetPowerMode failed\n",
|
|
__func__);
|
|
return AH_FALSE;
|
|
}
|
|
|
|
/* Clear warm reset register */
|
|
if (!ar5312SetResetReg(ah, 0)) {
|
|
HALDEBUG(ah, HAL_DEBUG_ANY, "%s: ar5312SetResetReg failed\n",
|
|
__func__);
|
|
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_RAD5112_ANY(ah)) {
|
|
rfMode = AR_PHY_MODE_AR5112;
|
|
if (!IS_5315(ah)) {
|
|
if (IEEE80211_IS_CHAN_CCK(chan)) {
|
|
phyPLL = AR_PHY_PLL_CTL_44_5312;
|
|
} else {
|
|
if (IEEE80211_IS_CHAN_HALF(chan)) {
|
|
phyPLL = AR_PHY_PLL_CTL_40_5312_HALF;
|
|
} else if (IEEE80211_IS_CHAN_QUARTER(chan)) {
|
|
phyPLL = AR_PHY_PLL_CTL_40_5312_QUARTER;
|
|
} else {
|
|
phyPLL = AR_PHY_PLL_CTL_40_5312;
|
|
}
|
|
}
|
|
} else {
|
|
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;
|
|
}
|
|
} else {
|
|
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;
|
|
}
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* Write the given reset bit mask into the reset register
|
|
*/
|
|
static HAL_BOOL
|
|
ar5312SetResetReg(struct ath_hal *ah, uint32_t resetMask)
|
|
{
|
|
uint32_t mask = resetMask ? resetMask : ~0;
|
|
HAL_BOOL rt;
|
|
|
|
if ((rt = ar5312MacReset(ah, mask)) == AH_FALSE) {
|
|
return rt;
|
|
}
|
|
if ((resetMask & AR_RC_MAC) == 0) {
|
|
if (isBigEndian()) {
|
|
/*
|
|
* Set CFG, little-endian for register
|
|
* and descriptor accesses.
|
|
*/
|
|
#ifdef AH_NEED_DESC_SWAP
|
|
mask = INIT_CONFIG_STATUS | AR_CFG_SWRD;
|
|
#else
|
|
mask = INIT_CONFIG_STATUS |
|
|
AR_CFG_SWTD | AR_CFG_SWRD;
|
|
#endif
|
|
OS_REG_WRITE(ah, AR_CFG, mask);
|
|
} else
|
|
OS_REG_WRITE(ah, AR_CFG, INIT_CONFIG_STATUS);
|
|
}
|
|
return rt;
|
|
}
|
|
|
|
/*
|
|
* ar5312MacReset resets (and then un-resets) the specified
|
|
* wireless components.
|
|
* Note: The RCMask cannot be zero on entering from ar5312SetResetReg.
|
|
*/
|
|
|
|
HAL_BOOL
|
|
ar5312MacReset(struct ath_hal *ah, unsigned int RCMask)
|
|
{
|
|
int wlanNum = AR5312_UNIT(ah);
|
|
uint32_t resetBB, resetBits, regMask;
|
|
uint32_t reg;
|
|
|
|
if (RCMask == 0)
|
|
return(AH_FALSE);
|
|
#if ( AH_SUPPORT_2316 || AH_SUPPORT_2317 )
|
|
if (IS_5315(ah)) {
|
|
switch(wlanNum) {
|
|
case 0:
|
|
resetBB = AR5315_RC_BB0_CRES | AR5315_RC_WBB0_RES;
|
|
/* Warm and cold reset bits for wbb */
|
|
resetBits = AR5315_RC_WMAC0_RES;
|
|
break;
|
|
case 1:
|
|
resetBB = AR5315_RC_BB1_CRES | AR5315_RC_WBB1_RES;
|
|
/* Warm and cold reset bits for wbb */
|
|
resetBits = AR5315_RC_WMAC1_RES;
|
|
break;
|
|
default:
|
|
return(AH_FALSE);
|
|
}
|
|
regMask = ~(resetBB | resetBits);
|
|
|
|
/* read before */
|
|
reg = OS_REG_READ(ah,
|
|
(AR5315_RSTIMER_BASE - ((uint32_t) ah->ah_sh) + AR5315_RESET));
|
|
|
|
if (RCMask == AR_RC_BB) {
|
|
/* Put baseband in reset */
|
|
reg |= resetBB; /* Cold and warm reset the baseband bits */
|
|
} else {
|
|
/*
|
|
* Reset the MAC and baseband. This is a bit different than
|
|
* the PCI version, but holding in reset causes problems.
|
|
*/
|
|
reg &= regMask;
|
|
reg |= (resetBits | resetBB) ;
|
|
}
|
|
OS_REG_WRITE(ah,
|
|
(AR5315_RSTIMER_BASE - ((uint32_t) ah->ah_sh)+AR5315_RESET),
|
|
reg);
|
|
/* read after */
|
|
OS_REG_READ(ah,
|
|
(AR5315_RSTIMER_BASE - ((uint32_t) ah->ah_sh) +AR5315_RESET));
|
|
OS_DELAY(100);
|
|
|
|
/* Bring MAC and baseband out of reset */
|
|
reg &= regMask;
|
|
/* read before */
|
|
OS_REG_READ(ah,
|
|
(AR5315_RSTIMER_BASE- ((uint32_t) ah->ah_sh) +AR5315_RESET));
|
|
OS_REG_WRITE(ah,
|
|
(AR5315_RSTIMER_BASE - ((uint32_t) ah->ah_sh)+AR5315_RESET),
|
|
reg);
|
|
/* read after */
|
|
OS_REG_READ(ah,
|
|
(AR5315_RSTIMER_BASE- ((uint32_t) ah->ah_sh) +AR5315_RESET));
|
|
|
|
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
|
|
switch(wlanNum) {
|
|
case 0:
|
|
resetBB = AR5312_RC_BB0_CRES | AR5312_RC_WBB0_RES;
|
|
/* Warm and cold reset bits for wbb */
|
|
resetBits = AR5312_RC_WMAC0_RES;
|
|
break;
|
|
case 1:
|
|
resetBB = AR5312_RC_BB1_CRES | AR5312_RC_WBB1_RES;
|
|
/* Warm and cold reset bits for wbb */
|
|
resetBits = AR5312_RC_WMAC1_RES;
|
|
break;
|
|
default:
|
|
return(AH_FALSE);
|
|
}
|
|
regMask = ~(resetBB | resetBits);
|
|
|
|
/* read before */
|
|
reg = OS_REG_READ(ah,
|
|
(AR5312_RSTIMER_BASE - ((uint32_t) ah->ah_sh) + AR5312_RESET));
|
|
|
|
if (RCMask == AR_RC_BB) {
|
|
/* Put baseband in reset */
|
|
reg |= resetBB; /* Cold and warm reset the baseband bits */
|
|
} else {
|
|
/*
|
|
* Reset the MAC and baseband. This is a bit different than
|
|
* the PCI version, but holding in reset causes problems.
|
|
*/
|
|
reg &= regMask;
|
|
reg |= (resetBits | resetBB) ;
|
|
}
|
|
OS_REG_WRITE(ah,
|
|
(AR5312_RSTIMER_BASE - ((uint32_t) ah->ah_sh)+AR5312_RESET),
|
|
reg);
|
|
/* read after */
|
|
OS_REG_READ(ah,
|
|
(AR5312_RSTIMER_BASE - ((uint32_t) ah->ah_sh) +AR5312_RESET));
|
|
OS_DELAY(100);
|
|
|
|
/* Bring MAC and baseband out of reset */
|
|
reg &= regMask;
|
|
/* read before */
|
|
OS_REG_READ(ah,
|
|
(AR5312_RSTIMER_BASE- ((uint32_t) ah->ah_sh) +AR5312_RESET));
|
|
OS_REG_WRITE(ah,
|
|
(AR5312_RSTIMER_BASE - ((uint32_t) ah->ah_sh)+AR5312_RESET),
|
|
reg);
|
|
/* read after */
|
|
OS_REG_READ(ah,
|
|
(AR5312_RSTIMER_BASE- ((uint32_t) ah->ah_sh) +AR5312_RESET));
|
|
}
|
|
return(AH_TRUE);
|
|
}
|
|
|
|
#endif /* AH_SUPPORT_AR5312 */
|